[rlplot] 18/23: Imported Upstream version 1.5

Andreas Tille tille at debian.org
Wed Jun 29 09:50:58 UTC 2016


This is an automated email from the git hooks/post-receive script.

tille pushed a commit to branch master
in repository rlplot.

commit 2b3b5e36c7bb7c21c334e92a26b71c69939ec67e
Author: Andreas Tille <tille at debian.org>
Date:   Wed Jun 29 11:43:58 2016 +0200

    Imported Upstream version 1.5
---
 .kdbgrc.rlplot  |    18 +
 Axes.cpp        |  4130 ++++++-----
 Export.cpp      |  2170 +++---
 Fileio.cpp      |  7985 ++++++++++-----------
 Makefile        |    42 +-
 ODbuttons.cpp   |  3084 ++++----
 Output.cpp      |  3919 +++++------
 PlotObs.cpp     | 11865 ++++++++++++++++---------------
 PropertyDlg.cpp | 19991 ++++++++++++++++++++++++++--------------------------
 QT3_Spec.h      |    17 +-
 QT_Spec.cpp     |   411 +-
 QT_Spec.h       |    16 +-
 RLPLOT.RC       |   307 +
 TheDialog.cpp   |  9974 +++++++++++++-------------
 TheDialog.h     |  1120 +--
 UtilObj.cpp     |  8115 ++++++++++-----------
 Utils.cpp       |  5022 ++++++-------
 Version.h       |     4 +-
 WinSpec.cpp     |   378 +-
 WinSpec.h       |     9 +-
 exprlp.cpp      |   488 +-
 menu.h          |   213 +-
 mfcalc.cpp      |  1096 +--
 mfcalc.y        |   241 +-
 no_gui.cpp      |  1175 ++--
 reports.cpp     |  6323 +++++++++--------
 rlp_math.cpp    |  5235 +++++++-------
 rlplot.cpp      | 20483 +++++++++++++++++++++++++++---------------------------
 rlplot.h        |  6018 ++++++++--------
 rlplot.spec     |     5 +-
 rlplot.spec~    |    70 -
 spreadwi.cpp    |  5293 +++++++-------
 use_gui.cpp     |  3870 ++++++-----
 33 files changed, 65888 insertions(+), 63199 deletions(-)

diff --git a/.kdbgrc.rlplot b/.kdbgrc.rlplot
new file mode 100644
index 0000000..284dc15
--- /dev/null
+++ b/.kdbgrc.rlplot
@@ -0,0 +1,18 @@
+[Breakpoint 0]
+Enabled=true
+File=/home/c71960/rlplot/Axes.cpp
+Line=684
+Temporary=false
+
+[General]
+DebuggerCmdStr=
+DriverName=GDB
+FileVersion=1
+OptionsSelected=
+ProgramArgs=
+TTYLevel=7
+WorkingDirectory=
+
+[Memory]
+ColumnWidths=80,0
+NumExprs=0
diff --git a/Axes.cpp b/Axes.cpp
index 7d81b99..90255a7 100755
--- a/Axes.cpp
+++ b/Axes.cpp
@@ -1,1888 +1,2242 @@
-//Axes.cpp, Copyright 2000-2007 R.Lackner
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-// This module contains most of the axis-object and related stuff 
-//   like ticks and grid lines.
-#include "rlplot.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <string.h>
-
-extern char TmpTxt[];
-extern Default defs;
-extern GraphObj *CurrGO, *TrackGO;			//Selected Graphic Objects
-extern Axis **CurrAxes;
-extern UndoObj Undo;
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// define an object for each grid line
-GridLine::GridLine(GraphObj *par, DataObj *d, int which, DWORD df):
-	GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	type = which;
-	flags = df;
-	if(flags & AXIS_RADIAL) type |= DL_CIRCULAR;	//default to circular grid
-	Id = GO_GRIDLINE;
-	if(parent) parent->Command(CMD_GET_GRIDLINE, &LineDef, 0L);
-	bModified = false;
-}
-
-GridLine::GridLine(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	bModified = false;
-}
-
-GridLine::~GridLine()
-{
-	int i;
-
-	if(bModified) Undo.InvalidGO(this);
-	if(cpts) free(cpts);		cpts = 0L;
-	if(gl1) free(gl1);			gl1 = 0L;
-	if(gl2) free(gl2);			gl2 = 0L;
-	if(gl3) free(gl3);			gl3 = 0L;
-	if(ls) {
-		for(i = 0; i < 3; i++) if(ls[i]) delete(ls[i]);
-		free(ls);				ls = 0L;
-		}
-	if(mo) DelBitmapClass(mo);	mo = 0L;
-}
-
-void
-GridLine::DoPlot(anyOutput *o)
-{
-	int tmp, ix, iy, ir;
-	AxisDEF *axdef;
-
-	if(!parent || !o) return;
-	o->SetLine(&LineDef);
-	if(mo) DelBitmapClass(mo);	mo = 0L;
-	if(!type) type = DL_LEFT | DL_BOTTOM;
-	if(type & DL_CIRCULAR) {
-		axdef = (AxisDEF*)((Axis*)(parent->parent))->GetAxis();
-		ix = o->co2ix(axdef->Center.fx + parent->GetSize(SIZE_GRECT_LEFT));
-		iy = o->co2iy(axdef->Center.fy + parent->GetSize(SIZE_GRECT_TOP));
-		ir = abs((int)(parent->GetSize(SIZE_YBASE))-iy);
-		ncpts = 0;
-		cpts = MakeArc(ix, iy, ir, 0x0f, &ncpts);
-		SetMinMaxRect(&rDims, ix-ir, iy-ir, ix+ir, iy+ir);
-		IncrementMinMaxRect(&rDims, 3);
-		o->oPolyline(cpts, (int)ncpts);
-		return;
-		}
-	if(parent && parent->Id == GO_TICK) {
-		pts[0].x = pts[1].x = pts[2].x = pts[3].x = (long)parent->GetSize(SIZE_XBASE);
-		pts[0].y = pts[1].y = pts[2].y = pts[3].y = (long)parent->GetSize(SIZE_YBASE);
-		if(type & DL_LEFT) {
-			tmp = o->fx2ix(parent->GetSize(SIZE_BOUNDS_LEFT));
-			if(tmp < pts[0].x) pts[0].x = tmp;
-			if(tmp > pts[1].x) pts[1].x = tmp;
-			}
-		if(type & DL_RIGHT) {
-			tmp = o->fx2ix(parent->GetSize(SIZE_BOUNDS_RIGHT));
-			if(tmp < pts[0].x) pts[0].x = tmp;
-			if(tmp > pts[1].x) pts[1].x = tmp;
-			}
-		if(type & DL_YAXIS) {
-			tmp = iround(parent->GetSize(SIZE_YAXISX));
-			if(tmp < pts[0].x) pts[0].x = tmp;
-			if(tmp > pts[1].x) pts[1].x = tmp;
-			}
-		if(type & DL_TOP) {
-			tmp = o->fy2iy(parent->GetSize(SIZE_BOUNDS_TOP));
-			if(tmp < pts[2].y) pts[2].y = tmp;
-			if(tmp > pts[3].y) pts[3].y = tmp;
-			}
-		if(type & DL_BOTTOM) {
-			tmp = o->fy2iy(parent->GetSize(SIZE_BOUNDS_BOTTOM));
-			if(tmp < pts[2].y) pts[2].y = tmp;
-			if(tmp > pts[3].y) pts[3].y = tmp;
-			}
-		if(type & DL_XAXIS) {
-			tmp = iround(parent->GetSize(SIZE_XAXISY));
-			if(tmp < pts[2].y) pts[2].y = tmp;
-			if(tmp > pts[3].y) pts[3].y = tmp;
-			}
-		if(pts[0].x != pts[1].x || pts[0].y != pts[1].y){
-			o->oPolyline(pts, 2);
-			SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
-			}
-		if(pts[2].x != pts[3].x || pts[2].y != pts[3].y){
-			o->oPolyline(pts+2, 2);
-			SetMinMaxRect(&rDims, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
-			}
-		IncrementMinMaxRect(&rDims, 3);
-		}
-}
-
-void
-GridLine::DoMark(anyOutput *o, bool mark)
-{
-	if(mark){
-		if(mo) DelBitmapClass(mo);					mo = 0L;
-		memcpy(&mrc, &rDims, sizeof(RECT));
-		IncrementMinMaxRect(&mrc, 6 + o->un2ix(LineDef.width));
-		mo = GetRectBitmap(&mrc, o);
-		if(type & DL_CIRCULAR) {
-			InvertLine(cpts, ncpts, &LineDef, &rDims, o, mark);
-			}
-		else {
-			if(pts[0].x != pts[1].x || pts[0].y != pts[1].y)InvertLine(pts, 2, &LineDef, &rDims, o, mark);
-			if(pts[2].x != pts[3].x || pts[2].y != pts[3].y)InvertLine(pts+2, 2, &LineDef, &rDims, o, mark);
-			}
-		}
-	else RestoreRectBitmap(&mo, &mrc, o);
-}
-
-bool
-GridLine::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	POINT p1;
-
-	switch(cmd){
-	case CMD_SET_DATAOBJ:
-		Id = GO_GRIDLINE;
-		return true;
-	case CMD_SCALE:
-		LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;
-		return true;
-	case CMD_SETSCROLL:
-	case CMD_SET_GO3D:
-	case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		return false;
-	case CMD_SET_GRIDLINE:
-		if(tmpl) memcpy(&LineDef, tmpl, sizeof(LineDEF));
-		return true;
-	case CMD_SET_GRIDTYPE:
-		if(tmpl)type = *((int*)tmpl);
-		return true;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, p1.x = mev->x, p1.y = mev->y)) {
-				if(cpts && (type & DL_CIRCULAR) && IsCloseToPL(p1, cpts, ncpts)) {
-					o->ShowMark(CurrGO = this, MRK_GODRAW);
-					return true;
-					}
-				else if(!(type & DL_CIRCULAR)){
-					o->ShowMark(CurrGO = this, MRK_GODRAW);
-					return true;
-					}
-				}
-			break;
-			}
-		return false;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Gridline for axes and plots in 3D space
-GridLine3D::GridLine3D(GraphObj *par, DataObj *d, int which, DWORD df):
-	GridLine(par, d, which, df)
-{
-	Id = GO_GRIDLINE3D;
-}
-
-GridLine3D::GridLine3D(int src):GridLine(src)
-{
-}
-
-GridLine3D::~GridLine3D()
-{
-	int i;
-
-	if(cpts) free(cpts);		cpts = 0L;
-	if(gl1) free(gl1);			gl1 = 0L;
-	if(gl2) free(gl2);			gl2 = 0L;
-	if(gl3) free(gl3);			gl3 = 0L;
-	if(ls) {
-		for(i = 0; i < 6; i++) if(ls[i]) delete(ls[i]);
-		free(ls);				ls = 0L;
-		}
-	if(mo) DelBitmapClass(mo);	mo = 0L;
-}
-
-void
-GridLine3D::DoMark(anyOutput *o, bool mark)
-{
-	int i;
-	POINT3D *gl;
-
-	if(mark){
-		if(mo) DelBitmapClass(mo);					mo = 0L;
-		if(ls){
-			memcpy(&mrc, &rDims, sizeof(RECT));
-			IncrementMinMaxRect(&mrc, 6 + o->un2ix(LineDef.width));
-			mo = GetRectBitmap(&mrc, o);
-			for(i = 0; i < 6; i++) if(ls[i]) { 
-				switch(i) {
-				case 0:				case 1:
-					gl = gl1;		break;
-				case 2:		case 3:
-					gl = gl2;		break;
-				case 4:		case 5:
-					gl = gl3;		break;
-					}
-				if(gl) {
-					if(gl[0].x && gl[0].y && gl[1].x && gl[1].y) {
-						pts[0].x = gl[0].x;		pts[1].x = gl[1].x;
-						pts[0].y = gl[0].y;		pts[1].y = gl[1].y;
-						InvertLine(pts, 2, &LineDef, &mrc, o, mark);
-						}
-					if(gl[2].x && gl[2].y && gl[3].x && gl[3].y) {
-						pts[0].x = gl[2].x;		pts[1].x = gl[3].x;
-						pts[0].y = gl[2].y;		pts[1].y = gl[3].y;
-						InvertLine(pts, 2, &LineDef, &mrc, o, mark);
-						}
-					}
-				}
-			}
-		}
-	else if(mo)	RestoreRectBitmap(&mo, &mrc, o);
-}
-
-
-void
-GridLine3D:: DoPlot(anyOutput *o)
-{
-	fPOINT3D p1, p2, pn;
-	int i;
-
-	if(!parent || !o) return;
-	if(mo) DelBitmapClass(mo);	mo = 0L;
-	if(!gl1) gl1 = (POINT3D*)calloc(4, sizeof(POINT3D));
-	if(!gl2) gl2 = (POINT3D*)calloc(4, sizeof(POINT3D));
-	if(!gl3) gl3 = (POINT3D*)calloc(4, sizeof(POINT3D));
-	if(!ls) ls = (line_segment**)calloc(6, sizeof(line_segment*));
-	if(!(o->ActualSize(&rDims)))return;
-	Swap(rDims.left, rDims.right);	Swap(rDims.top, rDims.bottom);
-	if(gl1 && gl2 && gl3 && ls) {
-		for(i = 0; i < 6; i++) if(ls[i]) {
-			delete(ls[i]);		ls[i] = 0L;
-			}
-		if(type & 0x01) {
-			pn.fx = parent->GetSize(SIZE_BOUNDS_XMIN);	pn.fy = parent->GetSize(SIZE_BOUNDS_YMIN);
-			pn.fz = parent->GetSize(SIZE_MINE);			o->fvec2ivec(&pn, &p1);
-			pn.fx = parent->GetSize(SIZE_BOUNDS_XMAX);	o->fvec2ivec(&pn, &p2);
-			gl1[0].x = iround(p1.fx);					gl1[0].y = iround(p1.fy);
-			gl1[1].x = iround(p2.fx);					gl1[1].y = iround(p2.fy);
-			gl1[0].z = iround(p1.fz);					gl1[1].z = iround(p2.fz);
-			if(ls[0] = new line_segment(this, data, &LineDef, &gl1[0], &gl1[1]))
-				ls[0]->DoPlot(o);
-			UpdateMinMaxRect(&rDims, gl1[0].x, gl1[0].y);
-			UpdateMinMaxRect(&rDims, gl1[1].x, gl1[1].y);
-			}
-		if(type & 0x02) {
-			pn.fx = parent->GetSize(SIZE_BOUNDS_XMIN);	pn.fy = parent->GetSize(SIZE_BOUNDS_YMIN);
-			pn.fz = parent->GetSize(SIZE_MINE);			o->fvec2ivec(&pn, &p1);
-			pn.fy = parent->GetSize(SIZE_BOUNDS_YMAX);	o->fvec2ivec(&pn, &p2);
-			gl1[2].x = iround(p1.fx);					gl1[2].y = iround(p1.fy);
-			gl1[3].x = iround(p2.fx);					gl1[3].y = iround(p2.fy);
-			gl1[2].z = iround(p1.fz);					gl1[3].z = iround(p2.fz);
-			if(ls[1] = new line_segment(this, data, &LineDef, &gl1[2], &gl1[3]))
-				ls[1]->DoPlot(o);
-			UpdateMinMaxRect(&rDims, gl1[2].x, gl1[2].y);
-			UpdateMinMaxRect(&rDims, gl1[3].x, gl1[3].y);
-			}
-		if(type & 0x04) {
-			pn.fx = parent->GetSize(SIZE_BOUNDS_XMIN);	pn.fy = parent->GetSize(SIZE_MINE);
-			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMIN);	o->fvec2ivec(&pn, &p1);
-			pn.fx = parent->GetSize(SIZE_BOUNDS_XMAX);	o->fvec2ivec(&pn, &p2);
-			gl2[0].x = iround(p1.fx);					gl2[0].y = iround(p1.fy);
-			gl2[1].x = iround(p2.fx);					gl2[1].y = iround(p2.fy);
-			gl2[0].z = iround(p1.fz);					gl2[1].z = iround(p2.fz);
-			if(ls[2] = new line_segment(this, data, &LineDef, &gl2[0], &gl2[1]))
-				ls[2]->DoPlot(o);
-			UpdateMinMaxRect(&rDims, gl2[0].x, gl2[0].y);
-			UpdateMinMaxRect(&rDims, gl2[1].x, gl2[1].y);
-			}
-		if(type & 0x08) {
-			pn.fx = parent->GetSize(SIZE_BOUNDS_XMIN);	pn.fy = parent->GetSize(SIZE_MINE);
-			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMIN);	o->fvec2ivec(&pn, &p1);
-			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMAX);	o->fvec2ivec(&pn, &p2);
-			gl2[2].x = iround(p1.fx);					gl2[2].y = iround(p1.fy);
-			gl2[3].x = iround(p2.fx);					gl2[3].y = iround(p2.fy);
-			gl2[2].z = iround(p1.fz);					gl2[3].z = iround(p2.fz);
-			if(ls[3] = new line_segment(this, data, &LineDef, &gl2[2], &gl2[3]))
-				ls[3]->DoPlot(o);
-			UpdateMinMaxRect(&rDims, gl2[2].x, gl2[2].y);
-			UpdateMinMaxRect(&rDims, gl2[3].x, gl2[3].y);
-			}
-		if(type & 0x10) {
-			pn.fx = parent->GetSize(SIZE_MINE);			pn.fy = parent->GetSize(SIZE_BOUNDS_YMIN);
-			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMIN);	o->fvec2ivec(&pn, &p1);
-			pn.fy = parent->GetSize(SIZE_BOUNDS_YMAX);	o->fvec2ivec(&pn, &p2);
-			gl3[0].x = iround(p1.fx);					gl3[0].y = iround(p1.fy);
-			gl3[1].x = iround(p2.fx);					gl3[1].y = iround(p2.fy);
-			gl3[0].z = iround(p1.fz);					gl3[1].z = iround(p2.fz);
-			if(ls[4] = new line_segment(this, data, &LineDef, &gl3[0], &gl3[1]))
-				ls[4]->DoPlot(o);
-			UpdateMinMaxRect(&rDims, gl3[0].x, gl3[0].y);
-			UpdateMinMaxRect(&rDims, gl3[1].x, gl3[1].y);
-			}
-		if(type & 0x20) {
-			pn.fx = parent->GetSize(SIZE_MINE);			pn.fy = parent->GetSize(SIZE_BOUNDS_YMIN);
-			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMIN);	o->fvec2ivec(&pn, &p1);
-			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMAX);	o->fvec2ivec(&pn, &p2);
-			gl3[2].x = iround(p1.fx);					gl3[2].y = iround(p1.fy);
-			gl3[3].x = iround(p2.fx);					gl3[3].y = iround(p2.fy);
-			gl3[2].z = iround(p1.fz);					gl3[3].z = iround(p2.fz);
-			if(ls[5] = new line_segment(this, data, &LineDef, &gl3[2], &gl3[3]))
-				ls[5]->DoPlot(o);
-			UpdateMinMaxRect(&rDims, gl3[2].x, gl3[2].y);
-			UpdateMinMaxRect(&rDims, gl3[3].x, gl3[3].y);
-			}
-		}
-	IncrementMinMaxRect(&rDims, 4);
-}
-
-bool
-GridLine3D::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-
-	switch(cmd) {
-	case CMD_SET_DATAOBJ:
-		Id = GO_GRIDLINE3D;
-		return true;
-	case CMD_MOUSE_EVENT:
-		if(tmpl && ls) switch (((MouseEvent *)tmpl)->Action) {
-		case MOUSE_LBUP:
-			for(i = 0; i < 6; i++) if(ls[i] && 
-				ls[i]->ObjThere(((MouseEvent *)tmpl)->x, ((MouseEvent *)tmpl)->y)) {
-				o->ShowMark(this, MRK_GODRAW);
-				return true;
-				}
-			}
-		break;
-	default:
-		return GridLine::Command(cmd, tmpl, o);
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Radial Gridline for polar plots: spokes for the plot
-GridRadial::GridRadial(GraphObj *par, DataObj *d, int which, DWORD df):
-	GridLine(par, d, which, df)
-{
-	Id = GO_GRIDRADIAL;
-}
-
-GridRadial::GridRadial(int src):GridLine(src)
-{
-}
-
-GridRadial::~GridRadial()
-{
-	if(mo) DelBitmapClass(mo);	mo = 0L;
-}
-
-void
-GridRadial::DoPlot(anyOutput *o)
-{
-	AxisDEF *axdef;
-
-	if(!parent || !o) return;
-	if(mo) DelBitmapClass(mo);	mo = 0L;
-	o->SetLine(&LineDef);
-	if(parent->Id == GO_TICK && parent->parent && parent->parent->Id == GO_AXIS) {
-		axdef = (AxisDEF*)((Axis*)(parent->parent))->GetAxis();
-		pts[0].x = iround(parent->GetSize(SIZE_XBASE));
-		pts[0].y = iround(parent->GetSize(SIZE_YBASE));
-		pts[1].x = o->co2ix(axdef->Center.fx + parent->GetSize(SIZE_GRECT_LEFT));
-		pts[1].y = o->co2iy(axdef->Center.fy + parent->GetSize(SIZE_GRECT_TOP));
-		SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
-		IncrementMinMaxRect(&rDims, 3);
-		o->oPolyline(pts, 2);
-		}
-}
-
-void
-GridRadial::DoMark(anyOutput *o, bool mark)
-{
-	if(mark) {
-		if(mo) DelBitmapClass(mo);					mo = 0L;
-		memcpy(&mrc, &rDims, sizeof(RECT));
-		IncrementMinMaxRect(&mrc, 6 + o->un2ix(LineDef.width));
-		mo = GetRectBitmap(&mrc, o);
-		InvertLine(pts, 2, &LineDef, &rDims, o, mark);
-		}
-	else if(mo)	RestoreRectBitmap(&mo, &mrc, o);
-}
-
-bool
-GridRadial::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	POINT p1;
-
-	switch(cmd) {
-	case CMD_SET_DATAOBJ:
-		Id = GO_GRIDRADIAL;
-		return true;
-	case CMD_MOUSE_EVENT:
-		if(tmpl) switch (((MouseEvent *)tmpl)->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, p1.x=((MouseEvent *)tmpl)->x, p1.y=((MouseEvent *)tmpl)->y)){
-				if(IsCloseToPL(p1, pts, 2)) {
-					o->ShowMark(this, MRK_GODRAW);
-					return true;
-					}
-				}
-			}
-		break;
-	default:
-		return GridLine::Command(cmd, tmpl, o);
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Each axis tick is a graphic object managing tick labels and grid lines
-Tick::Tick(GraphObj *par, DataObj *d, double val, DWORD Flags):GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	value = val;			flags = Flags;
-	Id = GO_TICK;			bModified = false;
-}
-
-Tick::Tick(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	bModified = false;
-}
-
-Tick::~Tick()
-{
-	Command(CMD_FLUSH, 0L, 0L);
-	if(mo) DelBitmapClass(mo);	mo = 0L;
-	if(bModified) Undo.InvalidGO(this);
-}
-	
-double
-Tick::GetSize(int select)
-{
-	switch(select){
-	case SIZE_LB_XPOS:	return lbx;
-	case SIZE_XBASE:	return fix;
-	case SIZE_LB_YPOS:	return lby;
-	case SIZE_YBASE:	return fiy;
-	case SIZE_ZBASE:	return fiz;
-	case SIZE_LB_XDIST:
-		if(parent && parent->Id == GO_AXIS) return parent->GetSize(SIZE_TLB_XDIST);
-		return 0.0f;
-	case SIZE_LB_YDIST:
-		if(parent && parent->Id == GO_AXIS) return parent->GetSize(SIZE_TLB_YDIST);
-		return 0.0f;
-	case SIZE_MINE:		return value;
-	default:
-		if(parent) return parent->GetSize(select);
-		}
-	return 0.0;
-}
-
-bool
-Tick::SetSize(int select, double value)
-{
-	switch(select & 0xfff) {
-	case SIZE_AXIS_TICKS:
-		size = value;
-		break;
-	case SIZE_LB_XDIST:		case SIZE_LB_YDIST:
-		if(label)return label->SetSize(select, value);
-		break;
-	case SIZE_TICK_ANGLE:
-		angle = value;
-		}
-	return false;
-}
-
-bool
-Tick::SetColor(int select, DWORD col)
-{
-	switch(select & 0xfff) {
-	case COL_AXIS:
-		if(label)label->SetColor((select & UNDO_STORESET) ? 
-			COL_TEXT | UNDO_STORESET : COL_TEXT, col);
-		return true;
-	case COL_BG:
-		if(label) return label->SetColor(select, col);
-		return false;
-		}
-	return false;
-}
-
-void
-Tick::DoMark(anyOutput *o, bool mark)
-{
-	if(mark){
-		if(mo) DelBitmapClass(mo);					mo = 0L;
-		memcpy(&mrc, &rDims, sizeof(RECT));
-		IncrementMinMaxRect(&mrc, 6 + (parent && parent->Id == GO_AXIS) ? o->un2ix(((Axis*)parent)->axline.width):o->iLine);
-		mo = GetRectBitmap(&mrc, o);
-		InvertLine(pts, 2, defs.GetOutLine(), &rDims, o, mark);
-		}
-	else RestoreRectBitmap(&mo, &mrc, o);
-}
-
-bool
-Tick::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	TextDEF *LabelDef;
-	GraphObj **tmpPlots;
-	AxisDEF *axis;
-
-	switch(cmd){
-	case CMD_SCALE:
-		if(label) label->Command(cmd, tmpl, o);
-		if(Grid) Grid->Command(cmd, tmpl, o);
-		size *= ((scaleINFO*)tmpl)->sy.fy;
-		break;
-	case CMD_SET_AXDEF:
-		if(axis = (AxisDEF*)tmpl) {
-			flags = (flags & AXIS_MINORTICK) | axis->flags;
-			}
-		break;
-	case CMD_FLUSH:
-		if(Grid) DeleteGO(Grid);		Grid = 0L;
-		if(label) DeleteGO(label);		label = 0L;
-		if(name) free(name);			name = 0L;
-		if(ls) delete(ls);				ls = 0L;
-		return true;
-	case CMD_SET_TICKSTYLE:
-		flags &= ~0x07;
-		flags |= (0x07 & *((DWORD*)tmpl));
-		return true;
-	case CMD_TICK_TYPE:
-		if(tmpl) type = *((int*)tmpl);
-		return true;
-	case CMD_SETSCROLL:
-	case CMD_REDRAW:
-		//this commands are usually issued from a child or from Undo
-		bModified = true;
-		return parent ? parent->Command(CMD_REDRAW, 0L, o) : false;
-	case CMD_MUTATE:
-		bModified = true;
-		if(!parent || !(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
-		if(label == tmpPlots[0]) {
-			Undo.MutateGO((GraphObj**)&label, tmpPlots[1], 0L, o);
-			return true;
-			}
-		break;
-	case CMD_DELOBJ:
-		bModified = true;
-		if(parent && tmpl && o) {
-			if(tmpl == Grid) {
-				Undo.DeleteGO((GraphObj**)(&Grid), 0L, o);
-				flags &= ~AXIS_GRIDLINE;
-				return parent->Command(CMD_REDRAW, 0L, o);
-				}
-			if(tmpl == label) {
-				Undo.DeleteGO((GraphObj**)(&label), 0L, o);
-				label = 0L;
-				return parent->Command(CMD_REDRAW, 0L, o);
-				}
-			}
-		return false;
-	case CMD_SET_GO3D:
-	case CMD_GET_GRIDLINE:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		return false;
-	case CMD_SET_GRIDTYPE:
-		if(tmpl && *((int*)tmpl)) gl_type = *((int*)tmpl);
-	case CMD_SET_GRIDLINE:
-		if(Grid && tmpl) return Grid->Command(cmd, tmpl, o);
-		return false;
-	case CMD_SET_DATAOBJ:
-		Id = GO_TICK;
-		if(Grid) Grid->Command(cmd, tmpl, o);
-		if(label) label->Command(cmd, tmpl, o);
-		return true;
-	case CMD_TEXTTHERE:
-		if(label && label->Command(cmd, tmpl, o)) return true;
-		return false;
-	case CMD_MOUSE_EVENT:
-		if((flags & AXIS_GRIDLINE) && Grid && Grid->Command(cmd, tmpl, o)) return true;
-		if(label && label->Command(cmd, tmpl, o)) return true;
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO && 
-				IsCloseToLine(&pts[0], &pts[1], mev->x, mev->y)) {
-				o->ShowMark(this, MRK_GODRAW);
-				return true;
-				}
-			break;
-			}
-		return false;
-	case CMD_TLB_TXTDEF:
-		if(label) return label->Command(CMD_SETTEXTDEF, tmpl, o);
-		else return false;
-	case CMD_SETTEXT:
-		if(label) return label->Command(cmd, tmpl, o);
-		if(!(LabelDef = (TextDEF *)calloc(1, sizeof(TextDEF))))return false;
-		LabelDef->ColTxt = parent ? parent->GetColor(COL_AXIS) : defs.Color(COL_AXIS);
-		LabelDef->ColBg = parent ? parent->GetColor(COL_BG) : defs.Color(COL_AXIS);
-		LabelDef->RotBL = LabelDef->RotCHAR = 0.0f;
-		LabelDef->fSize = parent ? parent->GetSize(SIZE_TICK_LABELS) : DefSize(SIZE_TICK_LABELS);
-		switch(flags & 0x70) {
-		case AXIS_USER:		LabelDef->Align = TXA_VCENTER | TXA_HCENTER;	break;
-		case AXIS_LEFT:		LabelDef->Align = TXA_VCENTER | TXA_HRIGHT;		break;
-		case AXIS_RIGHT:	LabelDef->Align = TXA_VCENTER | TXA_HLEFT;		break;
-		case AXIS_TOP:		LabelDef->Align = TXA_VBOTTOM | TXA_HCENTER;	break;
-		case AXIS_BOTTOM:	LabelDef->Align = TXA_VTOP | TXA_HCENTER;		break;
-		default:			LabelDef->Align = TXA_VTOP | TXA_HRIGHT;		break;
-			}
-		LabelDef->Style = TXS_NORMAL;
-		LabelDef->Mode = TXM_TRANSPARENT;
-		LabelDef->Font = FONT_HELVETICA;
-		LabelDef->text = tmpl && *((char*)tmpl) ? (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0) : 0L;
-		label = new Label(this, 0L, fix, fiy, LabelDef, LB_X_PARENT | LB_Y_PARENT);
-		if(LabelDef->text) free(LabelDef->text);
-		delete (LabelDef);
-		return true;
-		}
-	return false;
-}
-
-void
-Tick::DoPlot(double six, double csx, anyOutput *o)
-{
-	fPOINT3D dp1, dp2;
-	POINT3D ip2, p31, p32;
-
-	if(!parent || parent->Id != GO_AXIS) return;
-	if(mo) DelBitmapClass(mo);		mo = 0L;
-	if(ls) delete(ls);				ls = 0L;
-	ip2.x = ip2.y = ip2.z = 0;
-	if(!((Axis*)parent)->GetValuePos(value, &fix, &fiy, &fiz, o))return;
-	lbx = fix;		lby = fiy;
-	if(flags & AXIS_ANGULAR) {
-		dp1.fx = o->co2fix(parent->GetSize(SIZE_XCENT)+parent->GetSize(SIZE_GRECT_LEFT));
-		dp1.fy = o->co2fiy(parent->GetSize(SIZE_YCENT)+parent->GetSize(SIZE_GRECT_TOP));
-		dp1.fz = o->un2fix(parent->GetSize(SIZE_DRADIUS));
-		six = (fix - dp1.fx)/dp1.fz;		csx = (dp1.fy - fiy)/dp1.fz;
-		lbx += (o->un2fix(DefSize(SIZE_AXIS_TICKS)*3.0*six));
-		lby -= (o->un2fiy(DefSize(SIZE_AXIS_TICKS)*3.0*csx));
-		}
-	switch(type & 0x0f) {
-	case 1:		lsi = sin(angle/57.29577951);	lcsi = cos(angle/57.29577951);	break;
-	default:	lsi = -csx;		lcsi = six;		break;
-		}
-	if(flags & AXIS_MINORTICK) {
-		ip2.x = o->un2ix(0.5 * size * lcsi);		ip2.y = o->un2iy(0.5 * size * lsi);
-		}
-	else {
-		ip2.x = o->un2ix(size * lcsi);				ip2.y = o->un2iy(size * lsi);
-		}
-	if(flags & AXIS_3D){
-		dp1.fx = dp1.fy = dp1.fz = 0.0;
-		switch(type & 0x0f){
-		case 2:		dp1.fx = size;	dp1.fy = dp1.fz = 0.0;		break;
-		case 3:		dp1.fy = -size;	dp1.fx = dp1.fz = 0.0;		break;
-		case 4:		dp1.fz = size;	dp1.fx = dp1.fy = 0.0;		break;
-			}
-		if(dp1.fx != dp1.fy || dp1.fx != dp1.fz) {
-			if(flags & AXIS_MINORTICK) {
-				dp1.fx *= 0.5;		dp1.fy *= 0.5;	dp1.fz *= 0.5;
-				}
-			o->uvec2ivec(&dp1, &dp2);
-			ip2.x = iround(dp2.fx);		ip2.y = iround(dp2.fy);
-			ip2.z = iround(dp2.fz);
-			}
-		}
-	switch (flags &0x03) {
-	case AXIS_NOTICKS:
-		return;							//no ticks
-	case AXIS_POSTICKS:					//ticks are positive
-		break;
-	case AXIS_NEGTICKS:					//ticks are negative
-		ip2.x *= -1;			ip2.y *= -1;		break;
-	case AXIS_SYMTICKS:					//symmetrical ticks around axis: process later
-		break;
-		}
-	pts[1].x = iround(fix);	pts[1].y = iround(fiy);
-	p31.z = p32.z = iround(fiz);
-	if((flags &0x03) == AXIS_SYMTICKS) {	//tick is symetrical !
-		pts[1].x -= (ip2.x >>1);		pts[1].y -= (ip2.y >>1);
-		p31.z -= (ip2.z >>1);			p32.z = p31.z;
-		}
-	p31.x = p32.x = pts[0].x = pts[1].x;	p31.y = p32.y = pts[0].y = pts[1].y;
-	pts[1].x += ip2.x;			pts[1].y += ip2.y;			p32.z += ip2.z;
-	p32.x += ip2.x;				p32.y += ip2.y;
-	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
-	IncrementMinMaxRect(&rDims, 6);
-	if(parent && parent->Id == GO_AXIS && (flags & AXIS_3D)){
-		if(ls = new line_segment(this, data, &((Axis*)parent)->axline, &p31, &p32)){
-			ls->DoPlot(o);
-			}
-		}
-	else o->oSolidLine(pts);
-	if(flags & AXIS_MINORTICK) return;
-	if(flags & AXIS_GRIDLINE) {
-		if(!Grid){
-			if(flags & AXIS_3D) {
-				Grid = new GridLine3D(this, data, gl_type, flags);
-				}
-			else if((flags & AXIS_ANGULAR) == AXIS_ANGULAR){
-				Grid = new GridRadial(this, data, gl_type, flags);
-				}
-			else {
-				Grid = new GridLine(this, data, gl_type, flags);
-				}
-			}
-		if(Grid) Grid->DoPlot(o);
-		// we lost the line definition from the parent axis
-		if(parent) parent->Command(CMD_RESET_LINE, 0L, o);
-		}
-	if(label){
-		if(flags & AXIS_3D) label->SetSize(SIZE_ZPOS, fiz); 
-		label->DoPlot(o);
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Axes are graphic objects containing ticks
-Axis::Axis(GraphObj *par, DataObj *d, AxisDEF *ax, DWORD flags):
-	GraphObj(par, d)
-{
-	if(!(axis = (AxisDEF*)malloc(sizeof(AxisDEF))))return;
-	FileIO(INIT_VARS);
-	if(flags & AXIS_3D) GridLine.pattern = 0L;
-	if(ax->owner){
-		if(axis) free(axis);
-		axis = ax;
-		}
-	else {
-		if(axis) {
-			memcpy((void*)axis, (void*)ax, sizeof(AxisDEF));
-			axis->owner = (void*)this;
-			}
-		}
-	axis->flags = flags;
-	if ((flags & AXIS_ANGULAR) || (flags & AXIS_RADIAL)) {
-		GridLine.color = colAxis;
-		GridLine.pattern = 0x0;
-		}
-	Id = GO_AXIS;
-}
-
-Axis::Axis(int src):GraphObj(0L, 0L)
-{
-	if(!(axis = (AxisDEF*)malloc(sizeof(AxisDEF))))return;
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-Axis::~Axis()
-{
-	Undo.InvalidGO(this);
-	if(axis && axis->owner == (void*)this){
-		if(axis->breaks) free(axis->breaks);
-		free(axis);
-		}
-	Command(CMD_FLUSH, 0L, 0L);
-	if(ssMATval) free(ssMATval);	if(ssMATlbl) free(ssMATlbl);
-	if(ssMITval) free(ssMITval);	ssMATval = ssMATlbl = ssMITval = 0L;
-	if(axisLabel) DeleteGO(axisLabel);	axisLabel = 0L;
-	if(mo) DelBitmapClass(mo);			mo = 0L;
-	if(atv) delete atv;					atv = 0L;
-}
-
-double
-Axis::GetSize(int select)
-{
-	switch(select) {
-	case SIZE_LB_XDIST:			return lbdist.fx;
-	case SIZE_LB_YDIST:			return lbdist.fy;
-	case SIZE_TLB_XDIST:		return tlbdist.fx;
-	case SIZE_TLB_YDIST:		return tlbdist.fy;
-	case SIZE_LB_XPOS:			return(flim[0].fx + flim[1].fx)/2.0f;
-	case SIZE_LB_YPOS:			return(flim[0].fy + flim[1].fy)/2.0f;
-	case SIZE_TICK_LABELS:		return sizAxTickLabel;
-	case SIZE_AXIS_TICKS:		return sizAxTick;
-	case SIZE_AXIS_LINE:		return sizAxLine;
-	case SIZE_XPOS:				return axis->loc[0].fx;
-	case SIZE_XPOS+1:			return axis->loc[1].fx;
-	case SIZE_YPOS:				return axis->loc[0].fy;
-	case SIZE_YPOS+1:			return axis->loc[1].fy;
-	case SIZE_ZPOS:				return axis->loc[0].fz;
-	case SIZE_ZPOS+1:			return axis->loc[1].fz;
-	case SIZE_XCENT:			return axis->Center.fx;
-	case SIZE_YCENT:			return axis->Center.fy;
-	case SIZE_RADIUS1:	case SIZE_RADIUS2:	case SIZE_DRADIUS:
-		return axis->Radius;
-	case SIZE_BOUNDS_XMIN:		case SIZE_BOUNDS_XMAX:		case SIZE_BOUNDS_YMIN:
-	case SIZE_BOUNDS_YMAX:		case SIZE_BOUNDS_ZMIN:		case SIZE_BOUNDS_ZMAX:
-		if(parent) return parent->GetSize(select);
-		break;
-		}
-	return DefSize(select);
-}
-
-DWORD
-Axis::GetColor(int select)
-{
-	switch(select){
-	case COL_AXIS:
-		return colAxis;
-		}
-	if(parent) return parent->GetColor(select);
-	else return defs.Color(select);
-}
-
-bool
-Axis::SetSize(int select, double value)
-{
-	int i;
-
-	switch(select & 0xfff) {
-	case SIZE_AXIS_LINE:
-		 sizAxLine = value;
-		 break;
-	case SIZE_LB_XDIST:
-		lbdist.fx = value;
-		if(axisLabel)axisLabel->SetSize(select,value);
-		break;
-	case SIZE_LB_YDIST:
-		lbdist.fy = value;
-		if(axisLabel)axisLabel->SetSize(select,value);
-		break;
-	case SIZE_TLB_XDIST:		case SIZE_TLB_YDIST:
-	case SIZE_AXIS_TICKS:		case SIZE_TICK_ANGLE:
-		switch (select){
-		case SIZE_TLB_XDIST:
-			tlbdist.fx = value;			select = SIZE_LB_XDIST;
-			break;
-		case SIZE_TLB_YDIST:
-			tlbdist.fy = value;			select = SIZE_LB_YDIST;
-			break;
-		case SIZE_AXIS_TICKS:
-			sizAxTick = value;
-			break;
-		case SIZE_TICK_ANGLE:
-			tick_angle = value;
-			break;
-			}
-		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++) 
-			if(Ticks[i]) Ticks[i]->SetSize(select, value);
-		break;
-	default:
-		return false;
-		}
-	return true;
-}
-
-bool
-Axis::SetColor(int select, DWORD col)
-{
-	int i;
-
-	switch(select & 0xfff) {
-	case COL_AXIS:
-		if(colAxis == col) return true;
-		if(select & UNDO_STORESET){
-			Undo.ValDword(this, &colAxis, UNDO_CONTINUE);
-			Undo.ValDword(this, &tlbdef.ColTxt, UNDO_CONTINUE);
-			}
-		colAxis = col;
-		if (axis && (axis->flags & AXIS_ANGULAR) || (axis->flags & AXIS_RADIAL)) {
-			GridLine.color = colAxis;
-			}
-		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++)
-			if(Ticks[i]) Ticks[i]->SetColor(select, colAxis);
-		if(axisLabel) axisLabel->SetColor((select & UNDO_STORESET) ? 
-			COL_TEXT | UNDO_STORESET : COL_TEXT, col);
-		tlbdef.ColTxt = col;
-		return true;
-	case COL_BG:
-		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++)
-			if(Ticks[i]) Ticks[i]->SetColor(select, col);
-		if(axisLabel) axisLabel->SetColor(select, col);
-		return true;
-		}
-	return false;
-}
-
-void
-Axis::DoPlot(anyOutput *o)
-{
-	lfPOINT fp1, fp2;
-	fPOINT3D loc, cu1, cu2, rc;
-	double tmp, dx, dy;
-	int i, j, ix, iy;
-	fRECT scaleRC;
-	AxisDEF tmp_axis;
-
-	if(!o || !parent) return;
-	if(mo)DelBitmapClass(mo);		mo = 0L;
-	if(l_segs){
-		for (i = 0; i < nl_segs; i++) if(l_segs[i]) delete(l_segs[i]);
-		free(l_segs);		l_segs = 0L;		nl_segs = 0;
-		}
-	dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
-	if(!type) {
-		if(fabs(axis->loc[1].fx - axis->loc[0].fx) > 
-			fabs(axis->loc[1].fy - axis->loc[0].fy)) {
-			if((axis->flags) & AXIS_3D){
-				if(fabs(axis->loc[1].fz - axis->loc[0].fz) > 
-					fabs(axis->loc[1].fx - axis->loc[0].fx)) type = 3;
-				else type = 1;
-				}
-			else type = 1;
-			}
-		else {
-			if((axis->flags) & AXIS_3D){
-				if(fabs(axis->loc[1].fz - axis->loc[0].fz) > 
-					fabs(axis->loc[1].fy - axis->loc[0].fy)) type = 3;
-				else type = 2;
-				}
-			else type = 2;
-			}
-		}
-	//find default type for grid lines
-	if(!gl_type){
-		if(axis->flags & AXIS_3D) {
-			switch(type) {
-			case 1:		gl_type = 0x30;			break;
-			case 2:		gl_type = 0x0c;			break;
-			case 3:		gl_type = 0x03;			break;
-			default:	gl_type = 0x26;			break;
-				}
-			}
-		else {
-			gl_type = type == 2 ? DL_LEFT | DL_RIGHT : DL_TOP | DL_BOTTOM;
-			}
-		}
-	if(!Ticks && (axis->flags & 0x03) != AXIS_NOTICKS)CreateTicks();
-	if(axis->owner == this && !scaleOut) scaleOut = new anyOutput();
-	if(scaleOut) {
-		// set scaling information in scaleOut
-		scaleOut->hres = o->hres;			scaleOut->vres = o->vres;
-		scaleOut->VPscale = o->VPscale;
-		scaleOut->VPorg.fx = o->VPorg.fx;	scaleOut->VPorg.fy = o->VPorg.fy;
-		memcpy(&tmp_axis, axis, sizeof(AxisDEF));
-		tmp_axis.loc[0].fx += dx;		tmp_axis.loc[1].fx += dx;
-		tmp_axis.loc[0].fy += dy;		tmp_axis.loc[1].fy += dy;
-		if(IsPlot3D(parent) && (axis->flags & AXIS_3D)) {
-			//set 3D information in scaleOut
-			cu1.fx = axis->loc[0].fx +dx;	cu1.fy = axis->loc[0].fy +dy;
-			cu1.fz = axis->loc[0].fz;		cu2.fx = axis->loc[1].fx +dx;
-			cu2.fy = axis->loc[1].fy +dy;	cu2.fz = axis->loc[1].fz;
-			rc.fx = parent->GetSize(SIZE_XCENTER) +dx;
-			rc.fy = parent->GetSize(SIZE_YCENTER) +dy;
-			rc.fz = parent->GetSize(SIZE_ZCENTER);
-			scaleOut->SetSpace(&cu1, &cu2, defs.cUnits, ((Plot3D*)parent)->RotDef, &rc, 
-				&tmp_axis, &tmp_axis, &tmp_axis);
-			}
-		else {
-			// set axis and rectangle in scaleOut
-			scaleRC.Xmin = scaleRC.Xmax = dx;
-			scaleRC.Ymin = scaleRC.Ymax = dy;
-			scaleRC.Xmin += axis->loc[0].fx;		scaleRC.Ymin += axis->loc[1].fy;
-			scaleRC.Xmax += axis->loc[1].fx;		scaleRC.Ymax += axis->loc[0].fy;
-			scaleOut->SetRect(scaleRC, o->units, &tmp_axis, &tmp_axis);
-			}
-		}
-	axline.width = sizAxLine;
-	axline.patlength = 1.0;
-	axline.color = colAxis;
-	axline.pattern = 0L;		//solid line, no pattern
-	o->SetLine(&axline);
-	if(axis->flags & AXIS_ANGULAR) {
-		pts[1].x = i = o->un2ix(axis->Radius);		pts[1].y = j = o->un2iy(axis->Radius);
-		pts[0].x = ix = o->co2ix(axis->Center.fx + dx);
-		pts[0].y = iy = o->co2iy(axis->Center.fy + dy);
-		o->oCircle(ix-i, iy-j, ix+i, iy+j);
-		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++) 
-			if(Ticks[i]) Ticks[i]->DoPlot(0.0, 1.0, o);
-		rDims.left = ix-i;	rDims.right = ix +i;
-		rDims.top = iy -i;	rDims.bottom = iy+i;
-		IncrementMinMaxRect(&rDims, o->un2ix(axline.width*4.0));
-		return;
-		}
-	if(axis->flags & AXIS_3D){
-		loc.fx= axis->loc[0].fx;	loc.fy= axis->loc[0].fy;	loc.fz = axis->loc[0].fz;	
-		o->cvec2ivec(&loc, &flim[0]);
-		loc.fx= axis->loc[1].fx;	loc.fy= axis->loc[1].fy;	loc.fz = axis->loc[1].fz;	
-		o->cvec2ivec(&loc, &flim[1]);
-		}
-	else {
-		flim[0].fz = flim[1].fz = 0.0;
-		if(parent) switch(axis->flags & 0x70) {
-		case AXIS_USER:				//leave unchanged
-			fp1.fx = axis->loc[0].fx;		fp1.fy = axis->loc[0].fy;
-			fp2.fx = axis->loc[1].fx;		fp2.fy = axis->loc[1].fy;
-			break;
-		case AXIS_LEFT:
-			if(axis->flags & AXIS_X_DATA) {
-				axis->loc[0].fx = axis->loc[1].fx =	fp1.fx = fp2.fx = 
-					parent->GetSize(SIZE_BOUNDS_LEFT);
-				}
-			else {
-				axis->loc[0].fx = axis->loc[1].fx =	fp1.fx = fp2.fx = 
-					parent->GetSize(SIZE_DRECT_LEFT);
-				}
-			if(axis->flags & AXIS_Y_DATA) {
-				fp1.fy = parent->GetSize(SIZE_BOUNDS_BOTTOM);
-				fp2.fy = parent->GetSize(SIZE_BOUNDS_TOP);
-				}
-			else {
-				fp1.fy = parent->GetSize(SIZE_DRECT_TOP);
-				fp2.fy = parent->GetSize(SIZE_DRECT_BOTTOM);
-				}
-			break;
-		case AXIS_RIGHT:
-			if(axis->flags & AXIS_X_DATA) {
-				axis->loc[0].fx = axis->loc[1].fx = fp1.fx = fp2.fx = 
-					parent->GetSize(SIZE_BOUNDS_RIGHT);
-				}
-			else {
-				axis->loc[0].fx = axis->loc[1].fx =	fp1.fx = fp2.fx = 
-					parent->GetSize(SIZE_DRECT_RIGHT);
-				}
-			if(axis->flags & AXIS_Y_DATA) {
-				fp1.fy = parent->GetSize(SIZE_BOUNDS_BOTTOM);
-				fp2.fy = parent->GetSize(SIZE_BOUNDS_TOP);
-				}
-			else {
-				fp1.fy = parent->GetSize(SIZE_DRECT_TOP);
-				fp2.fy = parent->GetSize(SIZE_DRECT_BOTTOM);
-				}
-			break;
-		case AXIS_TOP:
-			if(axis->flags & AXIS_Y_DATA) {
-				axis->loc[0].fy = axis->loc[1].fy = fp1.fy = fp2.fy = 
-					parent->GetSize(SIZE_BOUNDS_TOP);
-				}
-			else {
-				axis->loc[0].fy = axis->loc[1].fy = fp1.fy = fp2.fy = 
-					parent->GetSize(SIZE_DRECT_TOP);
-				}
-			if(axis->flags & AXIS_X_DATA) {
-				fp1.fx = parent->GetSize(SIZE_BOUNDS_LEFT);
-				fp2.fx = parent->GetSize(SIZE_BOUNDS_RIGHT);
-				}
-			else {
-				fp1.fx = parent->GetSize(SIZE_DRECT_LEFT);
-				fp2.fx = parent->GetSize(SIZE_DRECT_RIGHT);
-				}
-			break;
-		case AXIS_BOTTOM:
-			if(axis->flags & AXIS_Y_DATA) {
-				axis->loc[0].fy = axis->loc[1].fy = fp1.fy = fp2.fy =
-					parent->GetSize(SIZE_BOUNDS_BOTTOM);
-				}
-			else {
-				axis->loc[0].fy = axis->loc[1].fy = fp1.fy = fp2.fy = 
-					parent->GetSize(SIZE_DRECT_BOTTOM);
-				}
-			if(axis->flags & AXIS_X_DATA) {
-				fp1.fx = parent->GetSize(SIZE_BOUNDS_LEFT);
-				fp2.fx = parent->GetSize(SIZE_BOUNDS_RIGHT);
-				}
-			else {
-				fp1.fx = parent->GetSize(SIZE_DRECT_LEFT);
-				fp2.fx = parent->GetSize(SIZE_DRECT_RIGHT);
-				}
-			break;
-			}
-		if(axis->flags & AXIS_X_DATA) {
-			flim[0].fx = o->fx2fix(fp1.fx);		flim[1].fx = o->fx2fix(fp2.fx);
-			}
-		else {
-			flim[0].fx = o->co2fix(fp1.fx + dx);	flim[1].fx = o->co2fix(fp2.fx + dx);
-			axis->loc[0].fx = fp1.fx;	axis->loc[1].fx = fp2.fx;
-			}
-		if(axis->flags & AXIS_Y_DATA) {
-			flim[0].fy = o->fy2fiy(fp1.fy);		flim[1].fy = o->fy2fiy(fp2.fy);
-			}
-		else {
-			flim[0].fy = o->co2fiy(fp1.fy + dy);	flim[1].fy = o->co2fiy(fp2.fy + dy);
-			axis->loc[0].fy = fp1.fy;	axis->loc[1].fy = fp2.fy;
-			}
-		}
-	pts[0].x = iround(flim[0].fx);		pts[1].x = iround(flim[1].fx);
-	pts[0].y = iround(flim[0].fy);		pts[1].y = iround(flim[1].fy);
-	pts3D[0].x = pts[0].x;				pts3D[1].x = pts[1].x;
-	pts3D[0].y = pts[0].y;				pts3D[1].y = pts[1].y;
-	pts3D[0].z = iround(flim[0].fz);	pts3D[1].z = iround(flim[1].fz);
-	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
-	IncrementMinMaxRect(&rDims, 3);
-	//calculate sine and cosine for ticks in any direction of axis
-	si = flim[1].fy - flim[0].fy;
-	tmp = (flim[1].fx - flim[0].fx);
-	si = fabs(si) > 0.0001 ? si/sqrt(si*si + tmp*tmp) : 0.0;
-	csi = flim[1].fx - flim[0].fx;
-	tmp = (flim[1].fy - flim[0].fy);
-	csi = fabs(csi) > 0.0001 ? csi/sqrt(csi*csi + tmp*tmp) : 0.0;
-	//draw axis line
-	if(axis->breaks && axis->nBreaks)DrawBreaks(o);
-	else if((axis->flags) & AXIS_3D){
-		if(!(l_segs = (line_segment**)calloc(1, sizeof(line_segment*))))return;
-		l_segs[0] = new line_segment(this, data, &axline, &pts3D[0], &pts3D[1]);
-		nl_segs = 1;
-		if(l_segs[0])l_segs[0]->DoPlot(o);
-		}
-	else o->oSolidLine(pts);
-	//now execute the draw routine of label and every tick
-	if(axisLabel){
-		if(axis->flags & AXIS_3D) axisLabel->SetSize(SIZE_ZPOS, (pts3D[0].z + pts3D[1].z)>>1); 
-		axisLabel->DoPlot(o);
-		}
-	if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++) 
-		if(Ticks[i]) Ticks[i]->DoPlot(si, csi, o);
-}
-
-bool
-Axis::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	GraphObj **tmpPlots;
-	void *sv_ptr;
-	scaleINFO *scale;
-	int i;
-
-	switch (cmd) {
-	case CMD_SCALE:
-		if(!Ticks && (axis->flags & 0x03) != AXIS_NOTICKS)CreateTicks();
-		scale = (scaleINFO*)tmpl;
-		lbdist.fx *= scale->sx.fy;			lbdist.fy *= scale->sy.fy;
-		tlbdist.fx *= scale->sx.fy;			tlbdist.fy *= scale->sy.fy;
-		sizAxTickLabel *= scale->sy.fy;		sizAxTick *= scale->sy.fy;
-		sizAxLine *= scale->sy.fy;			brksymsize *= scale->sy.fy;
-		brkgap *= scale->sy.fy;				GridLine.patlength *= scale->sy.fy;
-		GridLine.width *= scale->sy.fy;		tlbdef.fSize *= scale->sy.fy;
-		axis->loc[0].fx *= scale->sx.fy;	axis->loc[1].fx *= scale->sx.fy;
-		axis->loc[0].fy *= scale->sy.fy;	axis->loc[1].fy *= scale->sy.fy;
-		axis->loc[0].fz *= scale->sz.fy;	axis->loc[1].fz *= scale->sz.fy;
-		axis->Center.fx *= scale->sx.fy;	axis->Center.fy *= scale->sy.fy;
-		axis->Radius *= scale->sy.fy;		tlbdef.iSize = 0;
-		if(axisLabel) axisLabel->Command(cmd, tmpl, o);
-		if(Ticks) for(i = 0; i < NumTicks; i++) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = GO_AXIS;
-		data = (DataObj*)tmpl;
-		if(Ticks) for(i = 0; i< NumTicks; i++) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
-		if(axisLabel) axisLabel->Command(cmd, tmpl, o);
-	case CMD_TICK_TYPE:
-		if(cmd == CMD_TICK_TYPE){
-			if(tmpl) tick_type = *((int*)tmpl);
-			else return false;
-			}
-	case CMD_SET_TICKSTYLE:
-		if(cmd == CMD_SET_TICKSTYLE){
-			axis->flags &= ~0x07;		axis->flags |= (*((DWORD*)tmpl) & 0x07);
-			}
-	case CMD_SET_GRIDTYPE:
-		if(cmd == CMD_SET_GRIDTYPE && tmpl && *((int*)tmpl)) {
-			gl_type = *((int*)tmpl);
-			}
-	case CMD_TLB_TXTDEF:			//do all ticks
-		if(Ticks) for(i = 0; i< NumTicks; i++) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
-		if(cmd == CMD_TLB_TXTDEF && tmpl) {
-			memcpy(&tlbdef, tmpl, sizeof(TextDEF));
-			tlbdef.text = 0L;
-			}
-		return true;
-	case CMD_SET_GRIDLINE:
-		if(tmpl) memcpy(&GridLine, tmpl, sizeof(LineDEF));
-		if(Ticks) for(i = 0; i< NumTicks; i++) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_GET_GRIDLINE:
-		if(tmpl) memcpy(tmpl, &GridLine, sizeof(LineDEF));
-		return true;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(axis->flags & AXIS_ANGULAR) {
-				i = (mev->x - pts[0].x) * (mev->x - pts[0].x) + 
-					(mev->y - pts[0].y) * (mev->y - pts[0].y);
-				i = isqr(i) - pts[1].x;
-				if(i < 4 && i > -4){
-					o->ShowMark(this, MRK_GODRAW);
-					return true;
-					}
-				}
-			else if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO && 
-				IsCloseToLine(&pts[0], &pts[1], mev->x, mev->y)) {
-				o->ShowMark(this, MRK_GODRAW);
-				return true;
-				}
-			break;
-			}
-		if(axisLabel && axisLabel->Command(cmd, tmpl, o)) return true;
-		if(Ticks) for(i = 0; i < NumTicks; i++)
-			if(Ticks[i] && Ticks[i]->Command(cmd, tmpl, o)) return true;
-		return false;
-	case CMD_TEXTTHERE:
-		if(axisLabel && axisLabel->Command(cmd, tmpl, o)) return true;
-		if(Ticks) for(i = 0; i < NumTicks; i++) {
-			if(Ticks[i] && Ticks[i]->Command(cmd, tmpl, o)) return true;
-			}
-		return false;
-	case CMD_SET_AXDEF:
-		if(axis = (AxisDEF*)tmpl) {
-			if(axis && axis->owner == (void*)this) {
-				if(axis->breaks) free(axis->breaks);
-				free(axis);
-				}
-			if(Ticks) for(i = 0; i < NumTicks; i++)
-				if(Ticks[i]) {
-					Ticks[i]->Command(cmd, tmpl, o);
-					Ticks[i]->Command(CMD_SET_GRIDTYPE, (void*) &gl_type, 0L);
-					Ticks[i]->Command(CMD_TLB_TXTDEF, &tlbdef, 0L);
-					if(axis->flags & AXIS_GRIDLINE) Ticks[i]->Command(CMD_SET_GRIDLINE, &GridLine, 0L);
-					Ticks[i]->SetSize(SIZE_TICK_ANGLE, tick_angle);
-					Ticks[i]->SetSize(SIZE_AXIS_TICKS, sizAxTick);
-					Ticks[i]->SetSize(SIZE_LB_XDIST, tlbdist.fx);
-					Ticks[i]->SetSize(SIZE_LB_YDIST, tlbdist.fy);
-					Ticks[i]->Command(CMD_TICK_TYPE, &tick_type, 0L);
-					}
-			return true;
-			}
-		return false;
-	case CMD_CAN_CLOSE:
-		if(axis->owner == (void*)this) return true;
-		return false;
-	case CMD_DROP_LABEL:
-		if(axisLabel)DeleteGO(axisLabel);
-		if(axisLabel = (Label*)tmpl) {
-			axisLabel->parent = this;
-			}
-		return true;
-	case CMD_DELOBJ:
-		o->HideMark();
-		if(!tmpl || !parent) return false;
-		if(DeleteGOL((GraphObj***) &Ticks, NumTicks, (GraphObj *)tmpl, o)) 
-			return parent->Command(CMD_REDRAW, 0L, o);
-		if(tmpl == (void*)axisLabel) {
-			Undo.DeleteGO((GraphObj**)(&axisLabel), 0L, o);
-			return parent->Command(CMD_REDRAW, 0L, o);
-			}
-		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++){ 
-			if(Ticks[i] && Ticks[i]->Command(cmd, tmpl, o))
-				return parent->Command(CMD_REDRAW, 0L, o);
-			}
-		break;
-	case CMD_MUTATE:
-		if(!parent || !(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
-		if(axisLabel == tmpPlots[0]) {
-			Undo.MutateGO(&axisLabel, tmpPlots[1], 0L, o);
-			return true;
-			}
-		break;
-	case CMD_REPL_GO:
-		if(!(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
-		if(Ticks) for(i = 0; i < NumTicks; i++) if(Ticks[i] && Ticks[i] == tmpPlots[0]){
-			return bModified = ReplaceGO((GraphObj**)&Ticks[i], tmpPlots);
-			}
-		if(axisLabel && axisLabel == tmpPlots[0])
-			return bModified = ReplaceGO((GraphObj**)&axisLabel, tmpPlots);
-		return false;
-	case CMD_UPDATE:
-		UpdateTicks();
-	case CMD_SETSCROLL:
-		cmd = CMD_REDRAW;
-	case CMD_REDRAW:
-		bModified = true;
-	case CMD_SET_GO3D:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		return false;
-	case CMD_RESET_LINE:
-		return o->SetLine(&axline);
-	case CMD_FLUSH:
-		if(Ticks) {
-			for(i = 0; i < NumTicks; i++) if(Ticks[i]) DeleteGO(Ticks[i]);
-			free(Ticks);	NumTicks = 0;	Ticks = 0L;
-			}
-		if(l_segs){
-			for (i = 0; i < nl_segs; i++) if(l_segs[i]) delete(l_segs[i]);
-			free(l_segs);		l_segs = 0L;	nl_segs = 0;
-			}
-		if(scaleOut) delete(scaleOut);
-		scaleOut = drawOut = 0L;
-		return true;
-	case CMD_AUTOSCALE:		//we receive this command to update ticks after rescaling
-		if(axis && (AxisDEF*)tmpl == axis && (axis->flags & AXIS_AUTOSCALE) && 
-			(axis->flags & AXIS_AUTOTICK)) return Command(CMD_FLUSH, tmpl, o);
-		break;
-	case CMD_SAVE_TICKS:
-		SavVarInit(200 * NumTicks);
-		if(Ticks) for(i = 0; i < NumTicks; i++) {
-			if(Ticks[i]) Ticks[i]->FileIO(SAVE_VARS);
-			}
-		sv_ptr = SavVarFetch();
-		Undo.SavVarBlock(this, &sv_ptr, 0);
-		if(axis->flags & AXIS_AUTOTICK){
-			Undo.ValDword(this, &axis->flags, UNDO_CONTINUE);
-			axis->flags &= ~(AXIS_AUTOTICK | AXIS_AUTOSCALE);
-			}
-		bModified = true;
-		return true;
-		}
-	return false;
-}
-
-void
-Axis::SetTick(long idx, double val, DWORD flags, char *txt)
-{
-	char *l;
-
-	if((flags & AXIS_ANGULAR) && !(flags & 0x03)) flags |= AXIS_POSTICKS;
-	Ticks[idx] = new Tick(this, data, val, flags);
-	if(!txt) {
-		WriteNatFloatToBuff(TmpTxt, val);
-		l = (char*)memdup(TmpTxt+1, (int)strlen(TmpTxt+1)+1, 0);
-		}
-	else l = (char*)memdup(txt, (int)strlen(txt)+1, 0);
-	if(!gl_type) {
-		}
-	if(Ticks[idx]) {
-		Ticks[idx]->Command(CMD_SET_GRIDTYPE, (void*) &gl_type, 0L);
-		if(l) Ticks[idx]->Command(CMD_SETTEXT, l, 0L);
-		Ticks[idx]->Command(CMD_TLB_TXTDEF, &tlbdef, 0L);
-		if(flags & AXIS_GRIDLINE) Ticks[idx]->Command(CMD_SET_GRIDLINE, &GridLine, 0L);
-		Ticks[idx]->SetSize(SIZE_TICK_ANGLE, tick_angle);
-		Ticks[idx]->SetSize(SIZE_AXIS_TICKS, sizAxTick);
-		Ticks[idx]->Command(CMD_TICK_TYPE, &tick_type, 0L);
-		}
-	if(l) free(l);
-}
-
-void
-Axis::mkTimeAxis()
-{
-	int nstep, mode;
-	double span, val;
-	rlp_datetime start, step;
-	static char *tick_formats[] = {"y", "Y", "W", "x", "Z.V.", "d", "H:M", "d"};
-
-	span = axis->max - axis->min;
-	memset(&step, 0, sizeof(rlp_datetime));		parse_datevalue(&start, axis->Start);
-	start.hours=start.minutes=start.doy = 0;	start.seconds = val = 0.0;
-	if(span > 60.0) start.dow = 1;
-	if(span > 24000.0) {
-		step.year = start.month = start.dom = 1;
-		nstep = 2 + (int)(span / 364.0);		mode = 0;
-		}
-	else if(span > 700.0) {
-		step.month = start.month = start.dom = 1;
-		nstep = 2 + (int)(span / 28.0);			mode = 1;
-		}
-	else if(span > 300.0) {
-		step.month = start.dom = 1;
-		nstep = 2 + (int)(span / 28.0);			mode = 2;
-		}
-	else if(span > 150.0) {
-		step.month = start.dom = 1;
-		nstep = 2 + (int)(span / 28.0);			mode = 3;
-		}
-	else if(span > 60.0) {
-		step.dom = 1;
-		nstep = 6 + (int)(span/7.0);			mode = 4;
-		}
-	else if(span > 8.0) {
-		step.dom = 1;
-		nstep = 2+(int)(span);					mode = 5;
-		}
-	else if(span > 2.0) {
-		step.hours = 6;
-		nstep = 4+(int)(span*4.0);				mode = 6;
-		}
-	else if(span > 0.5) {
-		step.hours = (span > 0.9 ? 2 : 1);
-		nstep = 4+(int)(span*24.0);				mode = 7;
-		}
-	else if(span > 0.05) {
-		step.minutes = (span > 0.3 ? (span > 0.4 ? 30 : 15) : (span <= 0.1 ? 5 : 10));
-		nstep = 100;							mode = 8;
-		}
-	else if(span > 0.005) {
-		step.minutes = 1;
-		nstep = 100;							mode = 8;
-		}
-	else return;
-	if(nstep < 50) nstep = 50;					add_date(&start, 0L);
-	if(!(Ticks = (Tick**)calloc(nstep, sizeof(Tick*))))return;
-	for(NumTicks = 0; NumTicks < nstep && val <= axis->max;	NumTicks++) {
-		val = date2value(&start);
-		switch(mode) {
-		case 0:
-			if((start.year%10) == 0) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[0]));
-			else NumTicks--;					break;
-		case 1:
-			if(start.month == 1){
-				if(span <= 6000.0 || (span <= 12000.0 && (start.year%5) == 0) || (span <= 24000.0 && (start.year%10) == 0))
-					SetTick(NumTicks, val, axis->flags, date2text(&start, 
-					(span > 3000.0 && span < 12000.0)?tick_formats[0] : tick_formats[1]));
-				else NumTicks--;
-				}
-			else if(span <= 1500.0) SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, 
-				date2text(&start, tick_formats[4]));
-			else NumTicks--;					break;
-		case 2:
-			SetTick(NumTicks, val, axis->flags, date2text(&start, start.month==1 ? tick_formats[0]:tick_formats[2]));
-			break;
-		case 3:
-			SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[3]));
-			break;
-		case 4:
-			if(start.dom == 1)SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[3]));
-			else if(start.dow == 1) SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[4]));
-			else NumTicks--;					break;
-		case 5:
-			if(start.dow == 1) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[4]));
-			else if(span < 30.0)SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[7]));
-			else NumTicks--;					break;
-		case 6:
-			if(start.hours == 0) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[4]));
-			else SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[6]));
-			break;
-		case 7:
-			if(start.hours == 0) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[5]));
-			else if((start.hours % 6) == 0) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[6]));
-			else SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[6]));
-			break;
-		case 8:
-			if(start.minutes == 0){
-				if(span <= 0.3 || (start.hours %2) == 0) SetTick(NumTicks, val, axis->flags, 
-					date2text(&start, tick_formats[6]));
-				else SetTick(NumTicks, val, axis->flags, "");
-				}
-			else SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[6]));
-			break;
-			}
-		add_date(&start, &step);
-		}
-}
-
-void
-Axis::CreateTicks()
-{
-	int i, n, nstep;
-	char *format, *tick_label;
-	double fVal, tmp;
-	DWORD flags;
-
-	if(axis->min == -HUGE_VAL || axis->min == HUGE_VAL) return;
-	if(axis->max == -HUGE_VAL || axis->max == HUGE_VAL) return;
-	if(axis->min >= axis->max) return;
-	Command(CMD_FLUSH, 0L, 0L);
-	if((axis->flags & 0xf000) == AXIS_LOG) {	//log-axis
-		if(axis->Start > defs.min4log) tmp = log10(axis->Start);
-		else switch (type){
-			case 1:								//x axis
-			case 2:								//y axis
-			case 3:								//z axis
-				axis->Start = tmp = log10(base4log(axis, type-1));
-				if(axis->Start <= 0.0) axis->Start = 1.0;
-				break;
-			default: return;
-			}
-		n = (int)(log10(axis->max) - tmp);
-		Ticks = (Tick**)calloc(100, sizeof(Tick*));
-		for(NumTicks=0, i=(int)floor(tmp); NumTicks <90; i++){
-			SetTick(NumTicks++, pow(10.0, i), axis->flags, 0L);
-			if(n < 5) {
-				flags = n ? axis->flags | AXIS_MINORTICK : axis->flags;
-				SetTick(NumTicks++, 5.0*pow(10.0, i), axis->flags, 0L);
-				SetTick(NumTicks++, 3.0*pow(10.0, i), flags, 0L);
-				SetTick(NumTicks++, 4.0*pow(10.0, i), flags, 0L);
-				SetTick(NumTicks++, 6.0*pow(10.0, i), flags, 0L);
-				SetTick(NumTicks++, 7.0*pow(10.0, i), flags, 0L);
-				SetTick(NumTicks++, 8.0*pow(10.0, i), flags, 0L);
-				SetTick(NumTicks++, 9.0*pow(10.0, i), flags, 0L);
-				if(n < 3) SetTick(NumTicks++, 2.0*pow(10.0, i), axis->flags, 0L);
-				else SetTick(NumTicks++, 2.0*pow(10.0, i), axis->flags | AXIS_MINORTICK, 0L);
-				}
-			else {
-				SetTick(NumTicks++, 5.0*pow(10.0, i), axis->flags | AXIS_MINORTICK, 0L);
-				}
-			}
-		}
-	else {										//linear axis
-		if((axis->flags & 0xf000) == AXIS_DATETIME) {
-			mkTimeAxis();		return;
-			}
-		if(atv && (nstep = atv->Count()) && (Ticks = (Tick**)calloc(nstep+1, sizeof(Tick*)))) {
-			for(NumTicks = i = n = 0; NumTicks < nstep && atv->GetItem(i, &tick_label, &fVal);	i++) {
-				SetTick(NumTicks, fVal, axis->flags, tick_label);
-				NumTicks++;		n += (int)strlen(tick_label);
-				}
-			type = type;
-#ifdef _WINDOWS
-			if(type == 1 && n > 40) {
-				tlbdef.RotBL = n >100 ? 90.0 : 45.0;		tlbdef.Align = TXA_HRIGHT | TXA_VCENTER;
-				tlbdist.fy = sizAxTick;						SetSize(SIZE_TLB_YDIST, tlbdist.fy);
-				Command(CMD_TLB_TXTDEF, &tlbdef, 0L);
-				}
-#else
-			if(type == 1 && n > 30) {
-				tlbdef.RotBL = n >70 ? 90.0 : 45.0;			tlbdef.Align = TXA_HRIGHT | TXA_VCENTER;
-				tlbdist.fy = sizAxTick;						SetSize(SIZE_TLB_YDIST, tlbdist.fy);
-				Command(CMD_TLB_TXTDEF, &tlbdef, 0L);
-				}
-#endif
-			return;
-			}
-		if((axis->flags & 0xf000) == AXIS_RECI) {
-			NiceStep(axis, 8);
-			format = GetNumFormat(floor(log10(axis->Step)));
-			fVal = axis->Start;
-			}
-		else {
-			NiceStep(axis, 4);
-			format = GetNumFormat(floor(log10(fabs(axis->max - axis->min))));
-			fVal = axis->Start;
-			}
-		nstep = 1+(int)((axis->max-axis->min)/axis->Step);
-		Ticks = (Tick**)calloc(nstep+2, sizeof(Tick*));
-		if(!Ticks) return;
-		for(NumTicks = 0; NumTicks < nstep && fVal <= axis->max;
-			NumTicks++, fVal += axis->Step) {
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, format, fVal);
-#else
-			sprintf(TmpTxt, format, fVal);
-#endif
-			SetTick(NumTicks, fVal, axis->flags, TmpTxt);
-			}
-		}
-}
-
-void
-Axis::ManuTicks(double sa, double st, int n, DWORD flags)
-{
-	int j, m;
-	char *format;
-	double fVal, mival, mist;
-
-	Command(CMD_FLUSH, 0L, 0L);
-	mist = st/(double)(n+1);
-	m = (int)(((axis->max-axis->min) / st)*(double)(n+1))+n+2;
-	format = GetNumFormat(floor(log10(st)));
-	Ticks = (Tick**)calloc(m, sizeof(Tick*));
-	for(NumTicks = 0, fVal = sa; NumTicks < m && fVal <= axis->max; NumTicks++) {
-#ifdef USE_WIN_SECURE
-		sprintf_s(TmpTxt, TMP_TXT_SIZE, format, fVal);
-#else
-		sprintf(TmpTxt, format, fVal);
-#endif
-		SetTick(NumTicks, fVal, flags, TmpTxt);
-		for(j = 0; j < n; j++) {
-			mival = fVal+mist*(double)j +mist;
-			if(mival < axis->max) {
-#ifdef USE_WIN_SECURE
-				sprintf_s(TmpTxt, TMP_TXT_SIZE, format, mival);
-#else
-				sprintf(TmpTxt, format, mival);
-#endif
-				NumTicks++;
-				SetTick(NumTicks, mival, flags | AXIS_MINORTICK, TmpTxt);
-				}
-			}
-		fVal += st;
-		}
-}
-
-bool
-Axis::GetValuePos(double val, double *fix, double *fiy, double *fiz, anyOutput *op)
-{
-	double temp, tmp1 = 1.0;
-	int i;
-	bool bRet = true;
-	anyOutput *o;
-	fPOINT3D p1, p2;
-	lfPOINT fdp, fip;
-	AxisDEF caxis;
-
-	*fix = *fiy = *fiz = 0.0;
-	if(!op || !parent || (val < axis->min && val < axis->max) || 
-		(val > axis->min && val > axis->max)) return false;
-	for(i = 0; i < axis->nBreaks && bRet; i++) 
-		if((val >= axis->breaks[i].fx && val <= axis->breaks[i].fy) ||
-			(val <= axis->breaks[i].fx && val >= axis->breaks[i].fy)) bRet = false;
-	if(axis->owner == this && scaleOut)	o = scaleOut;
-	else o = op;
-	if(axis->flags & AXIS_3D) {
-		//Get a copy of the axis because GetAxisFac() modifies its contents
-		memcpy(&caxis, axis, sizeof(AxisDEF));
-		p1.fx = op->un2fix(axis->loc[1].fx - caxis.loc[0].fx);
-		p1.fy = op->un2fiy(axis->loc[1].fy - caxis.loc[0].fy);
-		p1.fz = op->un2fiz(axis->loc[1].fz - caxis.loc[0].fz);
-		tmp1 = sqrt(p1.fx*p1.fx + p1.fy*p1.fy + p1.fz*p1.fz);
-		p1.fx /= tmp1;		p1.fy /= tmp1;		p1.fz /= tmp1;
-		tmp1 = GetAxisFac(&caxis, tmp1, (type&0xf)-1);
-		temp = TransformValue(&caxis, val, true);
-		temp = (temp - caxis.min)*tmp1;
-		if(axis->flags & AXIS_INVERT) {
-			p1.fx = op->fix2un(op->un2fix(axis->loc[1].fx) - p1.fx*temp);
-			p1.fy = op->fiy2un(op->un2fiy(axis->loc[1].fy) - p1.fy*temp);
-			p1.fz = op->fix2un(op->un2fiz(axis->loc[1].fz) - p1.fz*temp);
-			}
-		else {
-			p1.fx = op->fix2un(p1.fx*temp+op->un2fix(axis->loc[0].fx));
-			p1.fy = op->fiy2un(p1.fy*temp+op->un2fiy(axis->loc[0].fy));
-			p1.fz = op->fix2un(p1.fz*temp+op->un2fiz(axis->loc[0].fz));
-			}
-		op->cvec2ivec(&p1, &p2);
-		*fix = p2.fx;	*fiy = p2.fy;	*fiz = p2.fz;
-		if((p2.fx < (flim[0].fx-1) && p2.fx < (flim[1].fx-1)) ||
-			(p2.fx > (flim[0].fx+1) && p2.fx > (flim[1].fx+1)) ||
-			(p2.fy < (flim[0].fy-1) && p2.fy < (flim[1].fy-1)) ||
-			(p2.fy > (flim[0].fy+1) && p2.fy > (flim[1].fy+1)) ||
-			(p2.fz < (flim[0].fz-1) && p2.fz < (flim[1].fz-1)) ||
-			(p2.fz > (flim[0].fz+1) && p2.fz > (flim[1].fz+1))) bRet = false;
-		return bRet;
-		}
-	else if(axis->flags & AXIS_ANGULAR) {
-		fdp.fx = val;				fdp.fy = parent->GetSize(SIZE_BOUNDS_TOP);
-		op->fp2fip(&fdp, &fip);		*fix = fip.fx;			*fiy = fip.fy;
-		return bRet;
-		}
-	else if(type == 1 && fabs(flim[1].fx-flim[0].fx)>10.0) {	//x dominant
-		tmp1 = (o->fx2fix(val)-flim[0].fx)/(flim[1].fx-flim[0].fx);
-		*fix = flim[0].fx - tmp1*(flim[0].fx - flim[1].fx);
-		*fiy = flim[0].fy - tmp1*(flim[0].fy - flim[1].fy);
-		}
-	else if(type == 2 && fabs(flim[1].fy-flim[0].fy)>10.0){		//y dominant
-		tmp1 = (o->fy2fiy(val)-flim[1].fy)/(flim[0].fy-flim[1].fy);
-		*fix = flim[1].fx + tmp1*(flim[0].fx - flim[1].fx);
-		*fiy = flim[1].fy + tmp1*(flim[0].fy - flim[1].fy);
-		}
-	if(tmp1 < -.005 || tmp1 > 1.005) return false;
-	return bRet;
-}
-
-void 
-Axis::BreakSymbol(POINT3D *p1, double dsi, double dcsi, bool connect, anyOutput *o)
-{
-	POINT *sym = 0L, *csym = 0L;
-	static POINT lp;
-	int j, n = 0;
-	double tmp;
-
-	switch (brksym){
-	case 2:
-		n = o->un2ix(brksymsize);
-		if(!(sym = (POINT*)calloc(2, sizeof(POINT))))return;
-		if(!(csym = (POINT*)calloc(2, sizeof(POINT)))){
-			free(sym);
-			return;
-			}
-		sym[0].x = (int)((-(n>>1))*dsi) + (int)((n>>2)*dcsi); 
-		sym[0].y = (int)((-(n>>1))*dcsi) + (int)((n>>2)*dsi);
-		sym[1].x = (int)((n>>1)*dsi) - (int)((n>>2)*dcsi); 
-		sym[1].y = (int)((n>>1)*dcsi) - (int)((n>>2)*dsi);
-		n = 2;
-		break;
-	case 3:
-		n = o->un2ix(brksymsize);
-		if(!(sym = (POINT*)calloc(2, sizeof(POINT))))return;
-		if(!(csym = (POINT*)calloc(2, sizeof(POINT)))){
-			free(sym);
-			return;
-			}
-		sym[0].x = (int)((-(n>>1))*dsi); 
-		sym[0].y = (int)((-(n>>1))*dcsi);
-		sym[1].x = (int)((n>>1)*dsi); 
-		sym[1].y = (int)((n>>1)*dcsi);
-		n = 2;
-		break;
-	case 4:
-		n = o->un2ix(brksymsize);
-		if(!(sym = (POINT*)calloc(n, sizeof(POINT))))return;
-		if(!(csym = (POINT*)calloc(n, sizeof(POINT)))){
-			free(sym);
-			return;
-			}
-		for(j = 0; j< n; j++) {
-			tmp = (double)j*6.283185307/(double)n;
-			tmp = sin(tmp)*(double)n/6.0;
-			sym[j].x = (int)((j-(n>>1))*dsi) + (int)(tmp*dcsi); 
-			sym[j].y = (int)((j-(n>>1))*dcsi) + (int)(tmp*dsi);
-			}
-		break;
-		}
-	if(sym && csym && n) {
-		if(brksym == 3 && connect) {
-			csym[0].x = lp.x;				csym[0].y = lp.y;
-			csym[1].x = sym[0].x + p1->x;	csym[1].y = sym[0].y + p1->y;
-			o->oPolyline(csym, 2);
-			}
-		for(j = 0; j < n; j++) {
-			csym[j].x = sym[j].x + p1->x;
-			csym[j].y = sym[j].y + p1->y;
-			}
-		o->oPolyline(csym, n);
-		lp.x = csym[n-1].x;				lp.y = csym[n-1].y;
-		}
-	if(sym) free(sym);			if(csym) free(csym);
-}
-
-void
-Axis::DrawBreaks(anyOutput *o)
-{
-	fPOINT3D *pts, np, tmp_p;
-	double dx, dy, dz, d, dn, da, lsi, lcsi;
-	POINT pbs[2];
-	int i, j;
-
-	dx = flim[0].fx > flim[1].fx ? flim[1].fx : flim[0].fx;
-	dy = flim[0].fy > flim[1].fy ? flim[1].fy : flim[0].fy;
-	dz = flim[0].fz > flim[1].fz ? flim[0].fz + (flim[0].fz - flim[1].fz)*50.0 : 
-		flim[1].fz + (flim[1].fz - flim[0].fz)*50.0;
-	if(axis->flags & AXIS_3D){
-		if(!(l_segs = (line_segment**)calloc(2+axis->nBreaks, sizeof(line_segment*))))return;
-		nl_segs = 0;
-		}
-	if(!(pts = (fPOINT3D*)calloc(2+axis->nBreaks, sizeof(fPOINT3D))))return;
-	memcpy(pts, &flim[0], sizeof(fPOINT3D));
-	for (i = 1; i < (2+axis->nBreaks); i++) {
-		switch (i) {
-		case 1:
-			memcpy(&np, &flim[1], sizeof(fPOINT3D));
-			break;
-		default:
-			GetValuePos(axis->breaks[i-2].fx, &np.fx, &np.fy, &np.fz, o);
-			break;
-			}
-		for(j = 0; j < i; j++) {
-			dn = (d = np.fx-dx) * d;		dn += ((d = np.fy-dy) * d);
-			dn += ((d = np.fz-dz) * d);		da = (d = pts[j].fx-dx) * d;		
-			da += ((d = pts[j].fy-dy)*d);	da += ((d = pts[j].fz-dz)*d);
-			if(dn < da) {
-				memcpy(&tmp_p, &pts[j], sizeof(fPOINT3D));
-				memcpy(&pts[j], &np, sizeof(fPOINT3D));
-				memcpy(&np, &tmp_p, sizeof(fPOINT3D));
-				}
-			}
-		memcpy(&pts[i], &np, sizeof(fPOINT3D));
-		}
-	for(i = 1; i < (2+axis->nBreaks); i++) {
-		dn = (d = pts[i].fx-pts[i-1].fx) * d;		dn += ((d = pts[i].fy-pts[i-1].fy) * d);
-		dn += ((d = pts[i].fz-pts[i-1].fz) * d);	dn = sqrt(dn);
-		da = o->un2fix(brkgap/2.0);
-		if(dn > 0.01) {
-			np.fx = da * (pts[i].fx-pts[i-1].fx)/dn;
-			np.fy = da * (pts[i].fy-pts[i-1].fy)/dn;
-			np.fz = da * (pts[i].fz-pts[i-1].fz)/dn;
-			d = (pts[i].fx - pts[i-1].fx) * (pts[i].fx - pts[i-1].fx);
-			d += ((pts[i].fy - pts[i-1].fy) * (pts[i].fy - pts[i-1].fy));
-			d = sqrt(d);			if(d < 1.0) d = 1.0;
-			lsi = (pts[i].fy - pts[i-1].fy)/d;
-			lcsi = (pts[i].fx -pts[i-1].fx)/d;
-			if(i == 1) {
-				pts3D[0].x = pbs[0].x = iround(pts[i-1].fx);
-				pts3D[0].y = pbs[0].y = iround(pts[i-1].fy);
-				pts3D[0].z = iround(pts[i-1].fz);
-				}
-			else {
-				pts3D[0].x = pbs[0].x = iround(pts[i-1].fx + np.fx);
-				pts3D[0].y = pbs[0].y = iround(pts[i-1].fy + np.fy);
-				pts3D[0].z = iround(pts[i-1].fz + np.fz);
-				BreakSymbol(&pts3D[0], lsi, -lcsi, true, o);
-				}
-			if(i == (1+axis->nBreaks)) {
-				pts3D[1].x = pbs[1].x = iround(pts[i].fx);
-				pts3D[1].y = pbs[1].y = iround(pts[i].fy);
-				pts3D[1].z = iround(pts[i].fz);
-				}
-			else {
-				pts3D[1].x = pbs[1].x = iround(pts[i].fx - np.fx);
-				pts3D[1].y = pbs[1].y = iround(pts[i].fy - np.fy);
-				pts3D[1].z = iround(pts[i].fz - np.fz);
-				BreakSymbol(&pts3D[1], lsi, -lcsi, false, o);
-				}
-			if(axis->flags & AXIS_3D) {
-				if(l_segs[nl_segs] = new line_segment(this, data, &axline, &pts3D[0], &pts3D[1])){
-					l_segs[nl_segs]->DoPlot(o);			nl_segs++;
-					}
-				}
-			else o->oSolidLine(pbs);
-			}
-		}
-	free(pts);
-}
-
-void 
-Axis::UpdateTicks()
-{
-	int i, j, k, l;
-	double tmpval;
-	AccRange *rT=0L, *rL=0L, *rMT=0L;
-
-	if(!ssMATval || !(rT = new AccRange(ssMATval))) return;
-	if(Ticks) {
-		Undo.DropListGO(this, (GraphObj***)&Ticks, &NumTicks, 0L);
-		}
-	if(ssMATlbl) rL = new AccRange(ssMATlbl);
-	if(ssMITval) rMT = new AccRange(ssMITval);
-	if(!(Ticks = ((Tick**)calloc(rT->CountItems()+ (( rMT != 0L) ? rMT->CountItems() : 0) +4,
-		sizeof(Tick*))))) return;
-	rT->GetFirst(&i, &j);		if(!(rT->GetNext(&i, &j)))return;
-	if(rL) rL->GetFirst(&k, &l);
-	NumTicks =0;
-	do{
-		if(rL) rL->GetNext(&k, &l);
-		if(data->GetValue(j, i, &tmpval)) {
-			if(!(Ticks[NumTicks] = new Tick(this, data, tmpval, axis->flags)))return;
-			if(rL) {
-				if(Ticks[NumTicks] && data->GetText(l, k, TmpTxt, TMP_TXT_SIZE)) 
-					Ticks[NumTicks]->Command(CMD_SETTEXT, TmpTxt, 0L);
-				}
-			Ticks[NumTicks]->SetSize(SIZE_TICK_ANGLE, tick_angle);
-			Ticks[NumTicks]->Command(CMD_TICK_TYPE, &tick_type, 0L);
-			}
-		NumTicks++;
-		}while(rT->GetNext(&i, &j));
-	if(rMT) {
-		if(rMT->GetFirst(&i, &j) && rMT->GetNext(&i, &j)) do {
-			if(data->GetValue(j, i, &tmpval) &&	0L!=(Ticks[NumTicks] = new Tick(this, data, 
-				tmpval, axis->flags | AXIS_MINORTICK))) {
-				Ticks[NumTicks]->SetSize(SIZE_TICK_ANGLE, tick_angle);
-				Ticks[NumTicks]->Command(CMD_TICK_TYPE, &tick_type, 0L);
-				NumTicks++;
-				}
-			}while(rMT->GetNext(&i, &j));
-		}
-	Command(CMD_TLB_TXTDEF, &tlbdef, 0L);	SetSize(SIZE_TLB_XDIST, tlbdist.fx);
-	SetSize(SIZE_TLB_YDIST, tlbdist.fy);
-	if(rT) delete(rT);	if(rL) delete(rL);	if(rMT) delete(rMT);
-}
+//Axes.cpp, Copyright 2000-2008 R.Lackner
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// This module contains most of the axis-object and related stuff 
+//   like ticks and grid lines.
+#include "rlplot.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+extern char TmpTxt[];
+extern Default defs;
+extern GraphObj *CurrGO, *TrackGO;			//Selected Graphic Objects
+extern Axis **CurrAxes;
+extern UndoObj Undo;
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// define an object for each grid line
+GridLine::GridLine(GraphObj *par, DataObj *d, int which, DWORD df):
+	GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	type = which;
+	flags = df;
+	if(flags & AXIS_RADIAL) type |= DL_CIRCULAR;	//default to circular grid
+	Id = GO_GRIDLINE;
+	if(parent) parent->Command(CMD_GET_GRIDLINE, &LineDef, 0L);
+	bModified = false;
+}
+
+GridLine::GridLine(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	bModified = false;
+}
+
+GridLine::~GridLine()
+{
+	int i;
+
+	if(bModified) Undo.InvalidGO(this);
+	if(cpts) free(cpts);		cpts = 0L;
+	if(gl1) free(gl1);			gl1 = 0L;
+	if(gl2) free(gl2);			gl2 = 0L;
+	if(gl3) free(gl3);			gl3 = 0L;
+	if(ls) {
+		for(i = 0; i < 3; i++) if(ls[i]) delete(ls[i]);
+		free(ls);				ls = 0L;
+		}
+	if(mo) DelBitmapClass(mo);	mo = 0L;
+}
+
+void
+GridLine::DoPlot(anyOutput *o)
+{
+	int tmp, ix, iy, ir;
+	AxisDEF *axdef;
+
+	if(!parent || !o) return;
+	o->SetLine(&LineDef);
+	if(mo) DelBitmapClass(mo);	mo = 0L;
+	if(!type) type = DL_LEFT | DL_BOTTOM;
+	if(type & DL_CIRCULAR) {
+		axdef = (AxisDEF*)((Axis*)(parent->parent))->GetAxis();
+		ix = o->co2ix(axdef->Center.fx + parent->GetSize(SIZE_GRECT_LEFT));
+		iy = o->co2iy(axdef->Center.fy + parent->GetSize(SIZE_GRECT_TOP));
+		ir = abs((int)(parent->GetSize(SIZE_YBASE))-iy);
+		ncpts = 0;
+		cpts = MakeArc(ix, iy, ir, 0x0f, &ncpts);
+		SetMinMaxRect(&rDims, ix-ir, iy-ir, ix+ir, iy+ir);
+		IncrementMinMaxRect(&rDims, 6 + o->un2ix(LineDef.width));
+		o->oPolyline(cpts, (int)ncpts);
+		return;
+		}
+	if(parent && parent->Id == GO_TICK) {
+		pts[0].x = pts[1].x = pts[2].x = pts[3].x = (long)parent->GetSize(SIZE_XBASE);
+		pts[0].y = pts[1].y = pts[2].y = pts[3].y = (long)parent->GetSize(SIZE_YBASE);
+		if(type & DL_LEFT) {
+			tmp = o->fx2ix(parent->GetSize(SIZE_BOUNDS_LEFT));
+			if(tmp < pts[0].x) pts[0].x = tmp;
+			if(tmp > pts[1].x) pts[1].x = tmp;
+			}
+		if(type & DL_RIGHT) {
+			tmp = o->fx2ix(parent->GetSize(SIZE_BOUNDS_RIGHT));
+			if(tmp < pts[0].x) pts[0].x = tmp;
+			if(tmp > pts[1].x) pts[1].x = tmp;
+			}
+		if(type & DL_YAXIS) {
+			tmp = iround(parent->GetSize(SIZE_YAXISX));
+			if(tmp < pts[0].x) pts[0].x = tmp;
+			if(tmp > pts[1].x) pts[1].x = tmp;
+			}
+		if(type & DL_TOP) {
+			tmp = o->fy2iy(parent->GetSize(SIZE_BOUNDS_TOP));
+			if(tmp < pts[2].y) pts[2].y = tmp;
+			if(tmp > pts[3].y) pts[3].y = tmp;
+			}
+		if(type & DL_BOTTOM) {
+			tmp = o->fy2iy(parent->GetSize(SIZE_BOUNDS_BOTTOM));
+			if(tmp < pts[2].y) pts[2].y = tmp;
+			if(tmp > pts[3].y) pts[3].y = tmp;
+			}
+		if(type & DL_XAXIS) {
+			tmp = iround(parent->GetSize(SIZE_XAXISY));
+			if(tmp < pts[2].y) pts[2].y = tmp;
+			if(tmp > pts[3].y) pts[3].y = tmp;
+			}
+		if(pts[0].x != pts[1].x || pts[0].y != pts[1].y){
+			o->oPolyline(pts, 2);
+			SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+			}
+		if(pts[2].x != pts[3].x || pts[2].y != pts[3].y){
+			o->oPolyline(pts+2, 2);
+			SetMinMaxRect(&rDims, pts[2].x, pts[2].y, pts[3].x, pts[3].y);
+			}
+		IncrementMinMaxRect(&rDims, 6 + o->un2ix(LineDef.width));
+		}
+}
+
+void
+GridLine::DoMark(anyOutput *o, bool mark)
+{
+	if(mark){
+		if(mo) DelBitmapClass(mo);					mo = 0L;
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		IncrementMinMaxRect(&mrc, 6 + o->un2ix(LineDef.width));
+		mo = GetRectBitmap(&mrc, o);
+		if(type & DL_CIRCULAR) {
+			InvertLine(cpts, ncpts, &LineDef, &rDims, o, mark);
+			}
+		else {
+			if(pts[0].x != pts[1].x || pts[0].y != pts[1].y)InvertLine(pts, 2, &LineDef, &rDims, o, mark);
+			if(pts[2].x != pts[3].x || pts[2].y != pts[3].y)InvertLine(pts+2, 2, &LineDef, &rDims, o, mark);
+			}
+		}
+	else RestoreRectBitmap(&mo, &mrc, o);
+}
+
+bool
+GridLine::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	POINT p1;
+
+	switch(cmd){
+	case CMD_SET_DATAOBJ:
+		Id = GO_GRIDLINE;
+		return true;
+	case CMD_SCALE:
+		LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;
+		return true;
+	case CMD_SETSCROLL:
+	case CMD_SET_GO3D:
+	case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		return false;
+	case CMD_SET_GRIDLINE:
+		if(tmpl) memcpy(&LineDef, tmpl, sizeof(LineDEF));
+		return true;
+	case CMD_SET_GRIDTYPE:
+		if(tmpl)type = *((int*)tmpl);
+		return true;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, p1.x = mev->x, p1.y = mev->y)) {
+				if(cpts && (type & DL_CIRCULAR) && IsCloseToPL(p1, cpts, ncpts)) {
+					o->ShowMark(CurrGO = this, MRK_GODRAW);
+					return true;
+					}
+				else if(!(type & DL_CIRCULAR)){
+					o->ShowMark(CurrGO = this, MRK_GODRAW);
+					return true;
+					}
+				}
+			break;
+			}
+		return false;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Gridline for axes and plots in 3D space
+GridLine3D::GridLine3D(GraphObj *par, DataObj *d, int which, DWORD df):
+	GridLine(par, d, which, df)
+{
+	Id = GO_GRIDLINE3D;
+}
+
+GridLine3D::GridLine3D(int src):GridLine(src)
+{
+}
+
+GridLine3D::~GridLine3D()
+{
+	int i;
+
+	if(cpts) free(cpts);		cpts = 0L;
+	if(gl1) free(gl1);			gl1 = 0L;
+	if(gl2) free(gl2);			gl2 = 0L;
+	if(gl3) free(gl3);			gl3 = 0L;
+	if(ls) {
+		for(i = 0; i < 6; i++) if(ls[i]) delete(ls[i]);
+		free(ls);				ls = 0L;
+		}
+	if(mo) DelBitmapClass(mo);	mo = 0L;
+}
+
+void
+GridLine3D::DoMark(anyOutput *o, bool mark)
+{
+	int i;
+	POINT3D *gl;
+
+	if(mark){
+		if(mo) DelBitmapClass(mo);					mo = 0L;
+		if(ls){
+			memcpy(&mrc, &rDims, sizeof(RECT));
+			IncrementMinMaxRect(&mrc, 6 + o->un2ix(LineDef.width));
+			mo = GetRectBitmap(&mrc, o);
+			for(i = 0; i < 6; i++) if(ls[i]) { 
+				switch(i) {
+				case 0:				case 1:
+					gl = gl1;		break;
+				case 2:		case 3:
+					gl = gl2;		break;
+				case 4:		case 5:
+					gl = gl3;		break;
+					}
+				if(gl) {
+					if(gl[0].x && gl[0].y && gl[1].x && gl[1].y) {
+						pts[0].x = gl[0].x;		pts[1].x = gl[1].x;
+						pts[0].y = gl[0].y;		pts[1].y = gl[1].y;
+						InvertLine(pts, 2, &LineDef, &mrc, o, mark);
+						}
+					if(gl[2].x && gl[2].y && gl[3].x && gl[3].y) {
+						pts[0].x = gl[2].x;		pts[1].x = gl[3].x;
+						pts[0].y = gl[2].y;		pts[1].y = gl[3].y;
+						InvertLine(pts, 2, &LineDef, &mrc, o, mark);
+						}
+					}
+				}
+			}
+		}
+	else if(mo)	RestoreRectBitmap(&mo, &mrc, o);
+}
+
+
+void
+GridLine3D:: DoPlot(anyOutput *o)
+{
+	fPOINT3D p1, p2, pn;
+	int i;
+
+	if(!parent || !o) return;
+	if(mo) DelBitmapClass(mo);	mo = 0L;
+	if(!gl1) gl1 = (POINT3D*)calloc(4, sizeof(POINT3D));
+	if(!gl2) gl2 = (POINT3D*)calloc(4, sizeof(POINT3D));
+	if(!gl3) gl3 = (POINT3D*)calloc(4, sizeof(POINT3D));
+	if(!ls) ls = (line_segment**)calloc(6, sizeof(line_segment*));
+	if(!(o->ActualSize(&rDims)))return;
+	Swap(rDims.left, rDims.right);	Swap(rDims.top, rDims.bottom);
+	if(gl1 && gl2 && gl3 && ls) {
+		for(i = 0; i < 6; i++) if(ls[i]) {
+			delete(ls[i]);		ls[i] = 0L;
+			}
+		if(type & 0x01) {
+			pn.fx = parent->GetSize(SIZE_BOUNDS_XMIN);	pn.fy = parent->GetSize(SIZE_BOUNDS_YMIN);
+			pn.fz = parent->GetSize(SIZE_MINE);			o->fvec2ivec(&pn, &p1);
+			pn.fx = parent->GetSize(SIZE_BOUNDS_XMAX);	o->fvec2ivec(&pn, &p2);
+			gl1[0].x = iround(p1.fx);					gl1[0].y = iround(p1.fy);
+			gl1[1].x = iround(p2.fx);					gl1[1].y = iround(p2.fy);
+			gl1[0].z = iround(p1.fz);					gl1[1].z = iround(p2.fz);
+			if(ls[0] = new line_segment(this, data, &LineDef, &gl1[0], &gl1[1]))
+				ls[0]->DoPlot(o);
+			UpdateMinMaxRect(&rDims, gl1[0].x, gl1[0].y);
+			UpdateMinMaxRect(&rDims, gl1[1].x, gl1[1].y);
+			}
+		if(type & 0x02) {
+			pn.fx = parent->GetSize(SIZE_BOUNDS_XMIN);	pn.fy = parent->GetSize(SIZE_BOUNDS_YMIN);
+			pn.fz = parent->GetSize(SIZE_MINE);			o->fvec2ivec(&pn, &p1);
+			pn.fy = parent->GetSize(SIZE_BOUNDS_YMAX);	o->fvec2ivec(&pn, &p2);
+			gl1[2].x = iround(p1.fx);					gl1[2].y = iround(p1.fy);
+			gl1[3].x = iround(p2.fx);					gl1[3].y = iround(p2.fy);
+			gl1[2].z = iround(p1.fz);					gl1[3].z = iround(p2.fz);
+			if(ls[1] = new line_segment(this, data, &LineDef, &gl1[2], &gl1[3]))
+				ls[1]->DoPlot(o);
+			UpdateMinMaxRect(&rDims, gl1[2].x, gl1[2].y);
+			UpdateMinMaxRect(&rDims, gl1[3].x, gl1[3].y);
+			}
+		if(type & 0x04) {
+			pn.fx = parent->GetSize(SIZE_BOUNDS_XMIN);	pn.fy = parent->GetSize(SIZE_MINE);
+			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMIN);	o->fvec2ivec(&pn, &p1);
+			pn.fx = parent->GetSize(SIZE_BOUNDS_XMAX);	o->fvec2ivec(&pn, &p2);
+			gl2[0].x = iround(p1.fx);					gl2[0].y = iround(p1.fy);
+			gl2[1].x = iround(p2.fx);					gl2[1].y = iround(p2.fy);
+			gl2[0].z = iround(p1.fz);					gl2[1].z = iround(p2.fz);
+			if(ls[2] = new line_segment(this, data, &LineDef, &gl2[0], &gl2[1]))
+				ls[2]->DoPlot(o);
+			UpdateMinMaxRect(&rDims, gl2[0].x, gl2[0].y);
+			UpdateMinMaxRect(&rDims, gl2[1].x, gl2[1].y);
+			}
+		if(type & 0x08) {
+			pn.fx = parent->GetSize(SIZE_BOUNDS_XMIN);	pn.fy = parent->GetSize(SIZE_MINE);
+			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMIN);	o->fvec2ivec(&pn, &p1);
+			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMAX);	o->fvec2ivec(&pn, &p2);
+			gl2[2].x = iround(p1.fx);					gl2[2].y = iround(p1.fy);
+			gl2[3].x = iround(p2.fx);					gl2[3].y = iround(p2.fy);
+			gl2[2].z = iround(p1.fz);					gl2[3].z = iround(p2.fz);
+			if(ls[3] = new line_segment(this, data, &LineDef, &gl2[2], &gl2[3]))
+				ls[3]->DoPlot(o);
+			UpdateMinMaxRect(&rDims, gl2[2].x, gl2[2].y);
+			UpdateMinMaxRect(&rDims, gl2[3].x, gl2[3].y);
+			}
+		if(type & 0x10) {
+			pn.fx = parent->GetSize(SIZE_MINE);			pn.fy = parent->GetSize(SIZE_BOUNDS_YMIN);
+			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMIN);	o->fvec2ivec(&pn, &p1);
+			pn.fy = parent->GetSize(SIZE_BOUNDS_YMAX);	o->fvec2ivec(&pn, &p2);
+			gl3[0].x = iround(p1.fx);					gl3[0].y = iround(p1.fy);
+			gl3[1].x = iround(p2.fx);					gl3[1].y = iround(p2.fy);
+			gl3[0].z = iround(p1.fz);					gl3[1].z = iround(p2.fz);
+			if(ls[4] = new line_segment(this, data, &LineDef, &gl3[0], &gl3[1]))
+				ls[4]->DoPlot(o);
+			UpdateMinMaxRect(&rDims, gl3[0].x, gl3[0].y);
+			UpdateMinMaxRect(&rDims, gl3[1].x, gl3[1].y);
+			}
+		if(type & 0x20) {
+			pn.fx = parent->GetSize(SIZE_MINE);			pn.fy = parent->GetSize(SIZE_BOUNDS_YMIN);
+			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMIN);	o->fvec2ivec(&pn, &p1);
+			pn.fz = parent->GetSize(SIZE_BOUNDS_ZMAX);	o->fvec2ivec(&pn, &p2);
+			gl3[2].x = iround(p1.fx);					gl3[2].y = iround(p1.fy);
+			gl3[3].x = iround(p2.fx);					gl3[3].y = iround(p2.fy);
+			gl3[2].z = iround(p1.fz);					gl3[3].z = iround(p2.fz);
+			if(ls[5] = new line_segment(this, data, &LineDef, &gl3[2], &gl3[3]))
+				ls[5]->DoPlot(o);
+			UpdateMinMaxRect(&rDims, gl3[2].x, gl3[2].y);
+			UpdateMinMaxRect(&rDims, gl3[3].x, gl3[3].y);
+			}
+		}
+	IncrementMinMaxRect(&rDims, 6 + o->un2ix(LineDef.width));
+}
+
+bool
+GridLine3D::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+
+	switch(cmd) {
+	case CMD_SET_DATAOBJ:
+		Id = GO_GRIDLINE3D;
+		return true;
+	case CMD_MOUSE_EVENT:
+		if(tmpl && ls) switch (((MouseEvent *)tmpl)->Action) {
+		case MOUSE_LBUP:
+			for(i = 0; i < 6; i++) if(ls[i] && 
+				ls[i]->ObjThere(((MouseEvent *)tmpl)->x, ((MouseEvent *)tmpl)->y)) {
+				o->ShowMark(this, MRK_GODRAW);
+				return true;
+				}
+			}
+		break;
+	default:
+		return GridLine::Command(cmd, tmpl, o);
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Radial Gridline for polar plots: spokes for the plot
+GridRadial::GridRadial(GraphObj *par, DataObj *d, int which, DWORD df):
+	GridLine(par, d, which, df)
+{
+	Id = GO_GRIDRADIAL;
+}
+
+GridRadial::GridRadial(int src):GridLine(src)
+{
+}
+
+GridRadial::~GridRadial()
+{
+	if(mo) DelBitmapClass(mo);	mo = 0L;
+}
+
+void
+GridRadial::DoPlot(anyOutput *o)
+{
+	AxisDEF *axdef;
+
+	if(!parent || !o) return;
+	if(mo) DelBitmapClass(mo);	mo = 0L;
+	o->SetLine(&LineDef);
+	if(parent->Id == GO_TICK && parent->parent && parent->parent->Id == GO_AXIS) {
+		axdef = (AxisDEF*)((Axis*)(parent->parent))->GetAxis();
+		pts[0].x = iround(parent->GetSize(SIZE_XBASE));
+		pts[0].y = iround(parent->GetSize(SIZE_YBASE));
+		pts[1].x = o->co2ix(axdef->Center.fx + parent->GetSize(SIZE_GRECT_LEFT));
+		pts[1].y = o->co2iy(axdef->Center.fy + parent->GetSize(SIZE_GRECT_TOP));
+		SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+		IncrementMinMaxRect(&rDims, 6 + o->un2ix(LineDef.width));
+		o->oPolyline(pts, 2);
+		}
+}
+
+void
+GridRadial::DoMark(anyOutput *o, bool mark)
+{
+	if(mark) {
+		if(mo) DelBitmapClass(mo);					mo = 0L;
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		IncrementMinMaxRect(&mrc, 6 + o->un2ix(LineDef.width));
+		mo = GetRectBitmap(&mrc, o);
+		InvertLine(pts, 2, &LineDef, &rDims, o, mark);
+		}
+	else if(mo)	RestoreRectBitmap(&mo, &mrc, o);
+}
+
+bool
+GridRadial::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	POINT p1;
+
+	switch(cmd) {
+	case CMD_SET_DATAOBJ:
+		Id = GO_GRIDRADIAL;
+		return true;
+	case CMD_MOUSE_EVENT:
+		if(tmpl) switch (((MouseEvent *)tmpl)->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, p1.x=((MouseEvent *)tmpl)->x, p1.y=((MouseEvent *)tmpl)->y)){
+				if(IsCloseToPL(p1, pts, 2)) {
+					o->ShowMark(this, MRK_GODRAW);
+					return true;
+					}
+				}
+			}
+		break;
+	default:
+		return GridLine::Command(cmd, tmpl, o);
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Each axis tick is a graphic object managing tick labels and grid lines
+Tick::Tick(GraphObj *par, DataObj *d, double val, DWORD Flags):GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	value = val;			flags = Flags;
+	Id = GO_TICK;			bModified = false;
+}
+
+Tick::Tick(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	bModified = false;
+}
+
+Tick::~Tick()
+{
+	int i;
+
+	if(Polygons) {
+		for(i = 0; i < numPG; i++) if(Polygons[i]) DeleteGO(Polygons[i]);
+		free(Polygons);			Polygons = 0L;	numPG = 0;
+		}
+	Command(CMD_FLUSH, 0L, 0L);
+	if(mo) DelBitmapClass(mo);		mo = 0L;
+	if(bModified) Undo.InvalidGO(this);
+	if(seg) free(seg);			seg = 0L;
+}
+	
+double
+Tick::GetSize(int select)
+{
+	switch(select){
+	case SIZE_LB_XPOS:	return lbx;
+	case SIZE_XBASE:	return fix;
+	case SIZE_LB_YPOS:	return lby;
+	case SIZE_YBASE:	return fiy;
+	case SIZE_ZBASE:	return fiz;
+	case SIZE_LB_XDIST:
+		if(parent && parent->Id == GO_AXIS) return parent->GetSize(SIZE_TLB_XDIST);
+		return 0.0f;
+	case SIZE_LB_YDIST:
+		if(parent && parent->Id == GO_AXIS) return parent->GetSize(SIZE_TLB_YDIST);
+		return 0.0f;
+	case SIZE_MINE:		return value;
+	default:
+		if(parent) return parent->GetSize(select);
+		}
+	return 0.0;
+}
+
+bool
+Tick::SetSize(int select, double value)
+{
+	switch(select & 0xfff) {
+	case SIZE_AXIS_TICKS:
+		size = value;
+		break;
+	case SIZE_LB_XDIST:		case SIZE_LB_YDIST:
+		if(label)return label->SetSize(select, value);
+		break;
+	case SIZE_TICK_ANGLE:
+		angle = value;
+		}
+	return false;
+}
+
+bool
+Tick::SetColor(int select, DWORD col)
+{
+	switch(select & 0xfff) {
+	case COL_AXIS:
+		if(label)label->SetColor((select & UNDO_STORESET) ? 
+			COL_TEXT | UNDO_STORESET : COL_TEXT, col);
+		return true;
+	case COL_BG:
+		if(label) return label->SetColor(select, col);
+		return false;
+		}
+	return false;
+}
+
+void
+Tick::DoMark(anyOutput *o, bool mark)
+{
+	if(!bValidTick) return;
+	if(mark){
+		if(mo) DelBitmapClass(mo);					mo = 0L;
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		IncrementMinMaxRect(&mrc, 6 + (parent && parent->Id == GO_AXIS) ? o->un2ix(((Axis*)parent)->axline.width):o->iLine);
+		mo = GetRectBitmap(&mrc, o);
+		InvertLine(pts, 2, defs.GetOutLine(), &rDims, o, mark);
+		}
+	else RestoreRectBitmap(&mo, &mrc, o);
+}
+
+bool
+Tick::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	TextDEF *LabelDef;
+	GraphObj **tmpPlots;
+	AxisDEF *axis;
+	DWORD pg_color;
+	int i;
+
+	switch(cmd){
+	case CMD_SCALE:
+		if(label) label->Command(cmd, tmpl, o);
+		if(Grid) Grid->Command(cmd, tmpl, o);
+		size *= ((scaleINFO*)tmpl)->sy.fy;
+		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) (Polygons[i])->Command(cmd, tmpl, o);;
+		break;
+	case CMD_ADDTOLINE:
+		return StoreSeg((lfPOINT*)tmpl);
+	case CMD_RECALC:
+		if(Polygons) Undo.DropListGO(this, (GraphObj***)&Polygons, &numPG, UNDO_CONTINUE);
+		n_seg = 0;		bModified = true;
+		return true;
+	case CMD_SET_AXDEF:
+		if(axis = (AxisDEF*)tmpl) {
+			flags = (flags & AXIS_MINORTICK) | axis->flags;
+			}
+		break;
+	case CMD_FLUSH:
+		if(Grid) DeleteGO(Grid);		Grid = 0L;
+		if(label) DeleteGO(label);		label = 0L;
+		if(name) free(name);			name = 0L;
+		if(ls) delete(ls);				ls = 0L;
+		return true;
+	case CMD_SET_TICKSTYLE:
+		flags &= ~0x07;
+		flags |= (0x07 & *((DWORD*)tmpl));
+		return true;
+	case CMD_UPDPG:
+		if(parent && parent->Id == GO_AXIS) pg_color = ((Axis*)parent)->GradColor(value);
+		else pg_color = 0x00ffffff;
+		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) {
+			Polygons[i]->SetColor(COL_POLYGON, pg_color);
+			}
+		return true;
+	case CMD_DRAWPG:
+		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) {
+			Polygons[i]->DoPlot(o);
+			}
+		// we lost the line definition from the parent axis
+		if(parent) parent->Command(CMD_RESET_LINE, 0L, o);
+		return true;
+	case CMD_TICK_TYPE:
+		if(tmpl) type = *((int*)tmpl);
+		return true;
+	case CMD_SETSCROLL:		case CMD_REDRAW:
+		//this commands are usually issued from a child or from Undo
+		bModified = true;
+		return parent ? parent->Command(CMD_REDRAW, 0L, o) : false;
+	case CMD_MUTATE:
+		bModified = true;
+		if(!parent || !(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
+		if(label == tmpPlots[0]) {
+			Undo.MutateGO((GraphObj**)&label, tmpPlots[1], 0L, o);
+			return true;
+			}
+		break;
+	case CMD_DELOBJ:
+		bModified = true;
+		if(parent && tmpl && o) {
+			if(tmpl == Grid) {
+				Undo.DeleteGO((GraphObj**)(&Grid), 0L, o);
+				flags &= ~AXIS_GRIDLINE;
+				return parent->Command(CMD_REDRAW, 0L, o);
+				}
+			if(tmpl == label) {
+				Undo.DeleteGO((GraphObj**)(&label), 0L, o);
+				label = 0L;
+				return parent->Command(CMD_REDRAW, 0L, o);
+				}
+			if(DeleteGOL((GraphObj***) &Polygons, numPG, (GraphObj *)tmpl, o)) 
+				return parent->Command(CMD_REDRAW, 0L, o);
+			}
+		return false;
+	case CMD_SET_GO3D:		case CMD_GET_GRIDLINE:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		return false;
+	case CMD_SET_GRIDTYPE:
+		if(tmpl && *((int*)tmpl)) gl_type = *((int*)tmpl);
+	case CMD_SET_GRIDLINE:
+		if(Grid && tmpl) return Grid->Command(cmd, tmpl, o);
+		return false;
+	case CMD_SET_DATAOBJ:
+		Id = GO_TICK;
+		if(Grid) Grid->Command(cmd, tmpl, o);
+		if(label) label->Command(cmd, tmpl, o);
+		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) (Polygons[i])->Command(cmd, tmpl, o);;
+		return true;
+	case CMD_TEXTTHERE:
+		if(label && label->Command(cmd, tmpl, o)) return true;
+		return false;
+	case CMD_MOUSE_EVENT:
+		if((flags & AXIS_GRIDLINE) && Grid && Grid->Command(cmd, tmpl, o)) return true;
+		if(label && label->Command(cmd, tmpl, o)) return true;
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO && 
+				IsCloseToLine(&pts[0], &pts[1], mev->x, mev->y)) {
+				o->ShowMark(this, MRK_GODRAW);
+				return true;
+				}
+			if(Polygons && !CurrGO) for(i = numPG-1; i >= 0; i--)
+				if(Polygons[i]) if(Polygons[i]->Command(cmd, tmpl, o))return true;
+			break;
+			}
+		return false;
+	case CMD_TLB_TXTDEF:
+		if(label) return label->Command(CMD_SETTEXTDEF, tmpl, o);
+		return false;
+	case CMD_GETTEXT:
+		if(label) return label->Command(cmd, tmpl, o);
+		return false;
+	case CMD_SETTEXT:
+		if(label) return label->Command(cmd, tmpl, o);
+		if(!(LabelDef = (TextDEF *)calloc(1, sizeof(TextDEF))))return false;
+		LabelDef->ColTxt = parent ? parent->GetColor(COL_AXIS) : defs.Color(COL_AXIS);
+		LabelDef->ColBg = parent ? parent->GetColor(COL_BG) : defs.Color(COL_AXIS);
+		LabelDef->RotBL = LabelDef->RotCHAR = 0.0f;
+		LabelDef->fSize = parent ? parent->GetSize(SIZE_TICK_LABELS) : DefSize(SIZE_TICK_LABELS);
+		switch(flags & 0x70) {
+		case AXIS_USER:		LabelDef->Align = TXA_VCENTER | TXA_HCENTER;	break;
+		case AXIS_LEFT:		LabelDef->Align = TXA_VCENTER | TXA_HRIGHT;		break;
+		case AXIS_RIGHT:	LabelDef->Align = TXA_VCENTER | TXA_HLEFT;		break;
+		case AXIS_TOP:		LabelDef->Align = TXA_VBOTTOM | TXA_HCENTER;	break;
+		case AXIS_BOTTOM:	LabelDef->Align = TXA_VTOP | TXA_HCENTER;		break;
+		default:			LabelDef->Align = TXA_VTOP | TXA_HRIGHT;		break;
+			}
+		LabelDef->Style = TXS_NORMAL;
+		LabelDef->Mode = TXM_TRANSPARENT;
+		LabelDef->Font = FONT_HELVETICA;
+		LabelDef->text = tmpl && *((char*)tmpl) ? (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0) : 0L;
+		label = new Label(this, 0L, fix, fiy, LabelDef, LB_X_PARENT | LB_Y_PARENT);
+		if(LabelDef->text) free(LabelDef->text);
+		delete (LabelDef);
+		return true;
+		}
+	return false;
+}
+
+void
+Tick::Track(POINT *p, anyOutput *o)
+{
+	POINT tpts[2];
+	int iw;
+
+	if(!bValidTick || !parent || !o) return;
+	iw = 6 + o->un2ix(o->LineWidth);
+	defs.UpdRect(o, rDims.left, rDims.top, rDims.right, rDims.bottom);
+	tpts[0].x = pts[0].x+p->x;	tpts[0].y = pts[0].y+p->y;
+	tpts[1].x = pts[1].x+p->x;	tpts[1].y = pts[1].y+p->y;
+	defs.UpdRect(o, tpts[0].x-iw, tpts[0].y-iw, tpts[1].x-iw, tpts[1].y-iw);
+	defs.UpdRect(o, tpts[0].x+iw, tpts[0].y+iw, tpts[1].x+iw, tpts[1].y+iw);
+	o->ShowLine(tpts, 2, parent->GetColor(COL_AXIS));
+	if((flags & AXIS_MINORTICK) == 0 && label) label->Track(p, o);
+}
+
+void
+Tick::DoPlot(double six, double csx, anyOutput *o)
+{
+	fPOINT3D dp1, dp2;
+	POINT3D ip2, p31, p32;
+
+	if(!parent || parent->Id != GO_AXIS) return;
+	if(mo) DelBitmapClass(mo);		mo = 0L;
+	if(ls) delete(ls);				ls = 0L;
+	ip2.x = ip2.y = ip2.z = 0;
+	if(!(bValidTick = ((Axis*)parent)->GetValuePos(value, &fix, &fiy, &fiz, o))) return;
+	lbx = fix;		lby = fiy;
+	if(flags & AXIS_ANGULAR) {
+		dp1.fx = o->co2fix(parent->GetSize(SIZE_XCENT)+parent->GetSize(SIZE_GRECT_LEFT));
+		dp1.fy = o->co2fiy(parent->GetSize(SIZE_YCENT)+parent->GetSize(SIZE_GRECT_TOP));
+		dp1.fz = o->un2fix(parent->GetSize(SIZE_DRADIUS));
+		six = (fix - dp1.fx)/dp1.fz;		csx = (dp1.fy - fiy)/dp1.fz;
+		lbx += (o->un2fix(DefSize(SIZE_AXIS_TICKS)*3.0*six));
+		lby -= (o->un2fiy(DefSize(SIZE_AXIS_TICKS)*3.0*csx));
+		}
+	switch(type & 0x0f) {
+	case 1:		lsi = sin(angle/57.29577951);	lcsi = cos(angle/57.29577951);	break;
+	default:	lsi = -csx;		lcsi = six;		break;
+		}
+	if(flags & AXIS_MINORTICK) {
+		ip2.x = o->un2ix(0.5 * size * lcsi);		ip2.y = o->un2iy(0.5 * size * lsi);
+		}
+	else {
+		ip2.x = o->un2ix(size * lcsi);				ip2.y = o->un2iy(size * lsi);
+		}
+	if(flags & AXIS_3D){
+		dp1.fx = dp1.fy = dp1.fz = 0.0;
+		switch(type & 0x0f){
+		case 2:		dp1.fx = size;	dp1.fy = dp1.fz = 0.0;		break;
+		case 3:		dp1.fy = -size;	dp1.fx = dp1.fz = 0.0;		break;
+		case 4:		dp1.fz = size;	dp1.fx = dp1.fy = 0.0;		break;
+			}
+		if(dp1.fx != dp1.fy || dp1.fx != dp1.fz) {
+			if(flags & AXIS_MINORTICK) {
+				dp1.fx *= 0.5;		dp1.fy *= 0.5;	dp1.fz *= 0.5;
+				}
+			o->uvec2ivec(&dp1, &dp2);
+			ip2.x = iround(dp2.fx);		ip2.y = iround(dp2.fy);
+			ip2.z = iround(dp2.fz);
+			}
+		}
+	switch (flags &0x03) {
+	case AXIS_NOTICKS:
+		return;							//no ticks
+	case AXIS_POSTICKS:					//ticks are positive
+		break;
+	case AXIS_NEGTICKS:					//ticks are negative
+		ip2.x *= -1;			ip2.y *= -1;		break;
+	case AXIS_SYMTICKS:					//symmetrical ticks around axis: process later
+		break;
+		}
+	pts[1].x = iround(fix);	pts[1].y = iround(fiy);
+	p31.z = p32.z = iround(fiz);
+	if((flags &0x03) == AXIS_SYMTICKS) {	//tick is symetrical !
+		pts[1].x -= (ip2.x >>1);		pts[1].y -= (ip2.y >>1);
+		p31.z -= (ip2.z >>1);			p32.z = p31.z;
+		}
+	p31.x = p32.x = pts[0].x = pts[1].x;	p31.y = p32.y = pts[0].y = pts[1].y;
+	pts[1].x += ip2.x;			pts[1].y += ip2.y;			p32.z += ip2.z;
+	p32.x += ip2.x;				p32.y += ip2.y;
+	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+	IncrementMinMaxRect(&rDims, 6 + o->un2ix(o->LineWidth));
+	if(parent && parent->Id == GO_AXIS && (flags & AXIS_3D)){
+		if(ls = new line_segment(this, data, &((Axis*)parent)->axline, &p31, &p32)){
+			ls->DoPlot(o);
+			}
+		}
+	else o->oSolidLine(pts);
+	if(!(flags & AXIS_MINORTICK)){
+		if(flags & AXIS_GRIDLINE) {
+			if(!Grid){
+				if(flags & AXIS_3D) {
+					Grid = new GridLine3D(this, data, gl_type, flags);
+					}
+				else if((flags & AXIS_ANGULAR) == AXIS_ANGULAR){
+					Grid = new GridRadial(this, data, gl_type, flags);
+					}
+				else {
+					Grid = new GridLine(this, data, gl_type, flags);
+					}
+				}
+			if(Grid) Grid->DoPlot(o);
+			// we lost the line definition from the parent axis
+			if(parent) parent->Command(CMD_RESET_LINE, 0L, o);
+			}
+		if(label){
+			if(flags & AXIS_3D) label->SetSize(SIZE_ZPOS, fiz); 
+			label->DoPlot(o);
+			}
+		}
+}
+
+//return true if two points are close together
+#define X_TOL 1.0E-12
+#define Y_TOL 1.0E-12
+bool
+Tick::CmpPoints(double x1, double y1, double x2, double y2)
+{
+	if(fabs(x2 - x1) > X_TOL) return false;
+	if(fabs(y2 - y1) > Y_TOL) return false;
+	return true;
+}
+#undef X_TOL
+#undef Y_TOL
+
+bool
+Tick::StoreSeg(lfPOINT *line)
+{
+	int i;
+
+	if(n_seg >= s_seg) {
+		if(!(seg = (double*)realloc(seg, ((s_seg += 50)<<2)*sizeof(double))))return false;
+		}
+	i = n_seg << 2;
+	seg[i++] = line[0].fx;		seg[i++] = line[0].fy;
+	seg[i++] = line[1].fx;		seg[i++] = line[1].fy;
+	n_seg++;			return false;
+}
+
+//collect line segments to polygons
+bool
+Tick::ProcSeg()
+{
+	lfPOINT *pg;
+	int i, j, l, n, n0, n1, n_pg, level;
+	bool cont1, cont2;
+	FillDEF pg_fill = {0, 0x0, 1.0, 0L, 0x0L};
+	LineDEF pg_line = {defs.GetSize(SIZE_HAIRLINE), 1.0, 0x0L, 0x0L};
+
+	if(n_seg < 3) {
+		n_seg = 0;	return false;
+		}
+	if(!(pg = (lfPOINT*)malloc((n_seg+2)*sizeof(lfPOINT)))) return false;
+	if(!parent || parent->Id != GO_AXIS) return false;
+	pg_fill.color = pg_fill.color2 = ((Axis*)parent)->GradColor(value);
+	pg_line.color = ((pg_fill.color & 0x00fefefeL)>>1);
+	while(n_seg > 2) {
+		pg[0].fx = seg[0];	pg[0].fy = seg[1];
+		pg[1].fx = seg[2];	pg[1].fy = seg[3];
+		n0 = level = 0; n = 1;	n_pg = 2;
+		do {
+			do {
+				cont2 = cont1 = false;		n1 = (n<<2);
+				if(CmpPoints(pg[n_pg-1].fx, pg[n_pg-1].fy, seg[n1], seg[n1+1])) {
+					pg[n_pg].fx = seg[n1+2];	pg[n_pg].fy = seg[n1+3];
+					n++;	n_pg++;		cont2=true;
+					}
+				else if(CmpPoints(pg[n_pg-1].fx, pg[n_pg-1].fy, seg[n1+2], seg[n1+3] )) {
+					pg[n_pg].fx = seg[n1];		pg[n_pg].fy = seg[n1+1];
+					n++;	n_pg++;		cont2=true;
+					}
+				else if(CmpPoints(pg[0].fx, pg[0].fy, seg[n1], seg[n1+1])) {
+					for(i = n_pg; i > 0; i--) {
+						pg[i].fx = pg[i-1].fx;	pg[i].fy = pg[i-1].fy;
+						}
+					pg[0].fx = seg[n1+2];		pg[0].fy = seg[n1+3];
+					n++;	n_pg++;		cont2=true;
+					}
+				else if(CmpPoints(pg[0].fx, pg[0].fy, seg[n1+2], seg[n1+3])) {
+					for(i = n_pg; i > 0; i--) {
+						pg[i].fx = pg[i-1].fx;	pg[i].fy = pg[i-1].fy;
+						}
+					pg[0].fx = seg[n1];			pg[0].fy = seg[n1+1];
+					n++;	n_pg++;		cont2=true;
+					}
+				if(cont2) level = 0;
+				}while(cont2 && n_pg < s_seg && n < n_seg);
+			if(n > n0 && n < n_seg) {
+				for(i = n0<<2, j = n<<2, l = (n_seg<<2); j < l; i++, j++) {
+					seg[i] = seg[j];
+					}
+				n_seg -= (n-n0);	n0 = n = 0;
+				}
+			else if(n == n_seg){
+				n_seg = n0;			n0 = n = 0;
+				}
+			else {
+				n0++;				n++;
+				}
+			if(CmpPoints(pg[0].fx, pg[0].fy, pg[n_pg-1].fx, pg[n_pg-1].fy)) cont1 = false;
+			else if(n < n_seg && n_seg) cont1 = true;
+			else {
+				if(n_seg && level < 3) {
+					level++;	n = 0;	cont1 = true;
+					}
+				else cont1 = false;
+				}
+			}while(cont1);
+		if(n_pg > 2) {
+			if(CmpPoints(pg[0].fx, pg[0].fy, pg[n_pg-1].fx, pg[n_pg-1].fy)) n_pg--;
+			if(n_pg > 2) {
+				Polygons = (DataPolygon**)realloc(Polygons, (numPG+2)*sizeof(DataPolygon*));
+				if(Polygons[numPG] = new DataPolygon(this, data, (lfPOINT*)memdup(pg, n_pg*sizeof(lfPOINT), 0), n_pg, 0L)){
+					Polygons[numPG]->type = 12;
+					Polygons[numPG]->Command(CMD_SET_LINE, &pg_line, 0L);
+					Polygons[numPG++]->Command(CMD_PG_FILL, &pg_fill, 0L);
+					}
+				}
+			}
+		}
+	free(pg);	n_seg = 0;	return true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Axes are graphic objects containing ticks
+Axis::Axis(GraphObj *par, DataObj *d, AxisDEF *ax, DWORD flags):
+	GraphObj(par, d)
+{
+	if(!(axis = (AxisDEF*)malloc(sizeof(AxisDEF))))return;
+	FileIO(INIT_VARS);
+	if(flags & AXIS_3D) GridLine.pattern = 0L;
+	if(ax->owner){
+		if(axis) free(axis);
+		axis = ax;
+		}
+	else {
+		if(axis) {
+			memcpy((void*)axis, (void*)ax, sizeof(AxisDEF));
+			axis->owner = (void*)this;
+			}
+		}
+	axis->flags = flags;
+	if ((flags & AXIS_ANGULAR) || (flags & AXIS_RADIAL)) {
+		GridLine.color = colAxis;
+		GridLine.pattern = 0x0;
+		}
+	Id = GO_AXIS;
+}
+
+Axis::Axis(int src):GraphObj(0L, 0L)
+{
+	if(!(axis = (AxisDEF*)malloc(sizeof(AxisDEF))))return;
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+Axis::~Axis()
+{
+	Undo.InvalidGO(this);
+	if(axis && axis->owner == (void*)this){
+		if(axis->breaks) free(axis->breaks);
+		free(axis);
+		}
+	Command(CMD_FLUSH, 0L, 0L);
+	if(ssMATval) free(ssMATval);	if(ssMATlbl) free(ssMATlbl);
+	if(ssMITval) free(ssMITval);	ssMATval = ssMATlbl = ssMITval = 0L;
+	if(axisLabel) DeleteGO(axisLabel);	axisLabel = 0L;
+	if(mo) DelBitmapClass(mo);			mo = 0L;
+	if(atv) delete atv;					atv = 0L;
+}
+
+double
+Axis::GetSize(int select)
+{
+	switch(select) {
+	case SIZE_LB_XDIST:			return lbdist.fx;
+	case SIZE_LB_YDIST:			return lbdist.fy;
+	case SIZE_TLB_XDIST:		return tlbdist.fx;
+	case SIZE_TLB_YDIST:		return tlbdist.fy;
+	case SIZE_LB_XPOS:			return(flim[0].fx + flim[1].fx)/2.0f;
+	case SIZE_LB_YPOS:			return(flim[0].fy + flim[1].fy)/2.0f;
+	case SIZE_TICK_LABELS:		return sizAxTickLabel;
+	case SIZE_AXIS_TICKS:		return sizAxTick;
+	case SIZE_AXIS_LINE:		return sizAxLine;
+	case SIZE_XPOS:				return axis->loc[0].fx;
+	case SIZE_XPOS+1:			return axis->loc[1].fx;
+	case SIZE_YPOS:				return axis->loc[0].fy;
+	case SIZE_YPOS+1:			return axis->loc[1].fy;
+	case SIZE_ZPOS:				return axis->loc[0].fz;
+	case SIZE_ZPOS+1:			return axis->loc[1].fz;
+	case SIZE_XCENT:			return axis->Center.fx;
+	case SIZE_YCENT:			return axis->Center.fy;
+	case SIZE_RADIUS1:	case SIZE_RADIUS2:	case SIZE_DRADIUS:
+		return axis->Radius;
+	case SIZE_BOUNDS_XMIN:		case SIZE_BOUNDS_XMAX:		case SIZE_BOUNDS_YMIN:
+	case SIZE_BOUNDS_YMAX:		case SIZE_BOUNDS_ZMIN:		case SIZE_BOUNDS_ZMAX:
+		if(parent) return parent->GetSize(select);
+		break;
+		}
+	return DefSize(select);
+}
+
+DWORD
+Axis::GetColor(int select)
+{
+	switch(select){
+	case COL_AXIS:
+		return colAxis;
+		}
+	if(parent) return parent->GetColor(select);
+	else return defs.Color(select);
+}
+
+bool
+Axis::SetSize(int select, double value)
+{
+	int i;
+
+	switch(select & 0xfff) {
+	case SIZE_AXIS_LINE:
+		 sizAxLine = value;
+		 break;
+	case SIZE_LB_XDIST:
+		lbdist.fx = value;
+		if(axisLabel)axisLabel->SetSize(select,value);
+		break;
+	case SIZE_LB_YDIST:
+		lbdist.fy = value;
+		if(axisLabel)axisLabel->SetSize(select,value);
+		break;
+	case SIZE_TLB_XDIST:		case SIZE_TLB_YDIST:
+	case SIZE_AXIS_TICKS:		case SIZE_TICK_ANGLE:
+		switch (select){
+		case SIZE_TLB_XDIST:
+			tlbdist.fx = value;			select = SIZE_LB_XDIST;
+			break;
+		case SIZE_TLB_YDIST:
+			tlbdist.fy = value;			select = SIZE_LB_YDIST;
+			break;
+		case SIZE_AXIS_TICKS:
+			sizAxTick = value;
+			break;
+		case SIZE_TICK_ANGLE:
+			tick_angle = value;
+			break;
+			}
+		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++) 
+			if(Ticks[i]) Ticks[i]->SetSize(select, value);
+		break;
+	default:
+		return false;
+		}
+	return true;
+}
+
+bool
+Axis::SetColor(int select, DWORD col)
+{
+	int i;
+
+	switch(select & 0xfff) {
+	case COL_AXIS:
+		if(colAxis == col) return true;
+		if(select & UNDO_STORESET){
+			Undo.ValDword(this, &colAxis, UNDO_CONTINUE);
+			Undo.ValDword(this, &tlbdef.ColTxt, UNDO_CONTINUE);
+			}
+		colAxis = col;
+		if (axis && (axis->flags & AXIS_ANGULAR) || (axis->flags & AXIS_RADIAL)) {
+			GridLine.color = colAxis;
+			}
+		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++)
+			if(Ticks[i]) Ticks[i]->SetColor(select, colAxis);
+		if(axisLabel) axisLabel->SetColor((select & UNDO_STORESET) ? 
+			COL_TEXT | UNDO_STORESET : COL_TEXT, col);
+		tlbdef.ColTxt = col;
+		return true;
+	case COL_BG:
+		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++)
+			if(Ticks[i]) Ticks[i]->SetColor(select, col);
+		if(axisLabel) axisLabel->SetColor(select, col);
+		return true;
+		}
+	return false;
+}
+
+void
+Axis::DoPlot(anyOutput *o)
+{
+	lfPOINT fp1, fp2;
+	fPOINT3D loc, cu1, cu2, rc;
+	double tmp, dx, dy;
+	int i, j, ix, iy;
+	fRECT scaleRC;
+	AxisDEF tmp_axis;
+
+	if(!o || !parent) return;
+	if(mo)DelBitmapClass(mo);		mo = 0L;
+	if(l_segs){
+		for (i = 0; i < nl_segs; i++) if(l_segs[i]) delete(l_segs[i]);
+		free(l_segs);		l_segs = 0L;		nl_segs = 0;
+		}
+	dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
+	if(!type) {
+		if(fabs(axis->loc[1].fx - axis->loc[0].fx) > 
+			fabs(axis->loc[1].fy - axis->loc[0].fy)) {
+			if((axis->flags) & AXIS_3D){
+				if(fabs(axis->loc[1].fz - axis->loc[0].fz) > 
+					fabs(axis->loc[1].fx - axis->loc[0].fx)) type = 3;
+				else type = 1;
+				}
+			else type = 1;
+			}
+		else {
+			if((axis->flags) & AXIS_3D){
+				if(fabs(axis->loc[1].fz - axis->loc[0].fz) > 
+					fabs(axis->loc[1].fy - axis->loc[0].fy)) type = 3;
+				else type = 2;
+				}
+			else type = 2;
+			}
+		}
+	//find default type for grid lines
+	if(!gl_type){
+		if(axis->flags & AXIS_3D) {
+			switch(type) {
+			case 1:		gl_type = 0x30;			break;
+			case 2:		gl_type = 0x0c;			break;
+			case 3:		gl_type = 0x03;			break;
+			default:	gl_type = 0x26;			break;
+				}
+			}
+		else {
+			gl_type = type == 2 ? DL_LEFT | DL_RIGHT : DL_TOP | DL_BOTTOM;
+			}
+		}
+	if(!Ticks && (axis->flags & 0x03) != AXIS_NOTICKS)CreateTicks();
+	if(axis->owner == this && !scaleOut) scaleOut = new anyOutput();
+	if(scaleOut) {
+		// set scaling information in scaleOut
+		scaleOut->hres = o->hres;			scaleOut->vres = o->vres;
+		scaleOut->VPscale = o->VPscale;
+		scaleOut->VPorg.fx = o->VPorg.fx;	scaleOut->VPorg.fy = o->VPorg.fy;
+		memcpy(&tmp_axis, axis, sizeof(AxisDEF));
+		tmp_axis.loc[0].fx += dx;		tmp_axis.loc[1].fx += dx;
+		tmp_axis.loc[0].fy += dy;		tmp_axis.loc[1].fy += dy;
+		if(IsPlot3D(parent) && (axis->flags & AXIS_3D)) {
+			//set 3D information in scaleOut
+			cu1.fx = axis->loc[0].fx +dx;	cu1.fy = axis->loc[0].fy +dy;
+			cu1.fz = axis->loc[0].fz;		cu2.fx = axis->loc[1].fx +dx;
+			cu2.fy = axis->loc[1].fy +dy;	cu2.fz = axis->loc[1].fz;
+			rc.fx = parent->GetSize(SIZE_XCENTER) +dx;
+			rc.fy = parent->GetSize(SIZE_YCENTER) +dy;
+			rc.fz = parent->GetSize(SIZE_ZCENTER);
+			scaleOut->SetSpace(&cu1, &cu2, defs.cUnits, ((Plot3D*)parent)->RotDef, &rc, 
+				&tmp_axis, &tmp_axis, &tmp_axis);
+			}
+		else {
+			// set axis and rectangle in scaleOut
+			scaleRC.Xmin = scaleRC.Xmax = dx;
+			scaleRC.Ymin = scaleRC.Ymax = dy;
+			scaleRC.Xmin += axis->loc[0].fx;		scaleRC.Ymin += axis->loc[1].fy;
+			scaleRC.Xmax += axis->loc[1].fx;		scaleRC.Ymax += axis->loc[0].fy;
+			scaleOut->SetRect(scaleRC, o->units, &tmp_axis, &tmp_axis);
+			}
+		}
+	axline.width = sizAxLine;
+	axline.patlength = 1.0;
+	axline.color = colAxis;
+	axline.pattern = 0L;		//solid line, no pattern
+	o->SetLine(&axline);
+	if(axis->flags & AXIS_ANGULAR) {
+		pts[1].x = i = o->un2ix(axis->Radius);		pts[1].y = j = o->un2iy(axis->Radius);
+		pts[0].x = ix = o->co2ix(axis->Center.fx + dx);
+		pts[0].y = iy = o->co2iy(axis->Center.fy + dy);
+		o->oCircle(ix-i, iy-j, ix+i, iy+j);
+		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++) 
+			if(Ticks[i]) Ticks[i]->DoPlot(0.0, 1.0, o);
+		rDims.left = ix-i;	rDims.right = ix +i;
+		rDims.top = iy -i;	rDims.bottom = iy+i;
+		IncrementMinMaxRect(&rDims, 6 + o->un2ix(sizAxLine));
+		return;
+		}
+	if(axis->flags & AXIS_3D){
+		loc.fx= axis->loc[0].fx;	loc.fy= axis->loc[0].fy;	loc.fz = axis->loc[0].fz;	
+		o->cvec2ivec(&loc, &flim[0]);
+		loc.fx= axis->loc[1].fx;	loc.fy= axis->loc[1].fy;	loc.fz = axis->loc[1].fz;	
+		o->cvec2ivec(&loc, &flim[1]);
+		}
+	else {
+		flim[0].fz = flim[1].fz = 0.0;
+		if(parent) switch(axis->flags & 0x70) {
+		case AXIS_USER:				//leave unchanged
+			fp1.fx = axis->loc[0].fx;		fp1.fy = axis->loc[0].fy;
+			fp2.fx = axis->loc[1].fx;		fp2.fy = axis->loc[1].fy;
+			break;
+		case AXIS_LEFT:
+			if(axis->flags & AXIS_X_DATA) {
+				axis->loc[0].fx = axis->loc[1].fx =	fp1.fx = fp2.fx = 
+					parent->GetSize(SIZE_BOUNDS_LEFT);
+				}
+			else {
+				axis->loc[0].fx = axis->loc[1].fx =	fp1.fx = fp2.fx = 
+					parent->GetSize(SIZE_DRECT_LEFT);
+				}
+			if(axis->flags & AXIS_Y_DATA) {
+				fp1.fy = parent->GetSize(SIZE_BOUNDS_BOTTOM);
+				fp2.fy = parent->GetSize(SIZE_BOUNDS_TOP);
+				}
+			else {
+				fp1.fy = parent->GetSize(SIZE_DRECT_TOP);
+				fp2.fy = parent->GetSize(SIZE_DRECT_BOTTOM);
+				}
+			break;
+		case AXIS_RIGHT:
+			if(axis->flags & AXIS_X_DATA) {
+				axis->loc[0].fx = axis->loc[1].fx = fp1.fx = fp2.fx = 
+					parent->GetSize(SIZE_BOUNDS_RIGHT);
+				}
+			else {
+				axis->loc[0].fx = axis->loc[1].fx =	fp1.fx = fp2.fx = 
+					parent->GetSize(SIZE_DRECT_RIGHT);
+				}
+			if(axis->flags & AXIS_Y_DATA) {
+				fp1.fy = parent->GetSize(SIZE_BOUNDS_BOTTOM);
+				fp2.fy = parent->GetSize(SIZE_BOUNDS_TOP);
+				}
+			else {
+				fp1.fy = parent->GetSize(SIZE_DRECT_TOP);
+				fp2.fy = parent->GetSize(SIZE_DRECT_BOTTOM);
+				}
+			break;
+		case AXIS_TOP:
+			if(axis->flags & AXIS_Y_DATA) {
+				axis->loc[0].fy = axis->loc[1].fy = fp1.fy = fp2.fy = 
+					parent->GetSize(SIZE_BOUNDS_TOP);
+				}
+			else {
+				axis->loc[0].fy = axis->loc[1].fy = fp1.fy = fp2.fy = 
+					parent->GetSize(SIZE_DRECT_TOP);
+				}
+			if(axis->flags & AXIS_X_DATA) {
+				fp1.fx = parent->GetSize(SIZE_BOUNDS_LEFT);
+				fp2.fx = parent->GetSize(SIZE_BOUNDS_RIGHT);
+				}
+			else {
+				fp1.fx = parent->GetSize(SIZE_DRECT_LEFT);
+				fp2.fx = parent->GetSize(SIZE_DRECT_RIGHT);
+				}
+			break;
+		case AXIS_BOTTOM:
+			if(axis->flags & AXIS_Y_DATA) {
+				axis->loc[0].fy = axis->loc[1].fy = fp1.fy = fp2.fy =
+					parent->GetSize(SIZE_BOUNDS_BOTTOM);
+				}
+			else {
+				axis->loc[0].fy = axis->loc[1].fy = fp1.fy = fp2.fy = 
+					parent->GetSize(SIZE_DRECT_BOTTOM);
+				}
+			if(axis->flags & AXIS_X_DATA) {
+				fp1.fx = parent->GetSize(SIZE_BOUNDS_LEFT);
+				fp2.fx = parent->GetSize(SIZE_BOUNDS_RIGHT);
+				}
+			else {
+				fp1.fx = parent->GetSize(SIZE_DRECT_LEFT);
+				fp2.fx = parent->GetSize(SIZE_DRECT_RIGHT);
+				}
+			break;
+			}
+		if(axis->flags & AXIS_X_DATA) {
+			flim[0].fx = o->fx2fix(fp1.fx);		flim[1].fx = o->fx2fix(fp2.fx);
+			}
+		else {
+			flim[0].fx = o->co2fix(fp1.fx + dx);	flim[1].fx = o->co2fix(fp2.fx + dx);
+			axis->loc[0].fx = fp1.fx;	axis->loc[1].fx = fp2.fx;
+			}
+		if(axis->flags & AXIS_Y_DATA) {
+			flim[0].fy = o->fy2fiy(fp1.fy);		flim[1].fy = o->fy2fiy(fp2.fy);
+			}
+		else {
+			flim[0].fy = o->co2fiy(fp1.fy + dy);	flim[1].fy = o->co2fiy(fp2.fy + dy);
+			axis->loc[0].fy = fp1.fy;	axis->loc[1].fy = fp2.fy;
+			}
+		}
+	pts[0].x = iround(flim[0].fx);		pts[1].x = iround(flim[1].fx);
+	pts[0].y = iround(flim[0].fy);		pts[1].y = iround(flim[1].fy);
+	pts3D[0].x = pts[0].x;				pts3D[1].x = pts[1].x;
+	pts3D[0].y = pts[0].y;				pts3D[1].y = pts[1].y;
+	pts3D[0].z = iround(flim[0].fz);	pts3D[1].z = iround(flim[1].fz);
+	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+	IncrementMinMaxRect(&rDims, 6 + o->un2ix(sizAxLine));
+	//calculate sine and cosine for ticks in any direction of axis
+	si = flim[1].fy - flim[0].fy;
+	tmp = (flim[1].fx - flim[0].fx);
+	si = fabs(si) > 0.0001 ? si/sqrt(si*si + tmp*tmp) : 0.0;
+	csi = flim[1].fx - flim[0].fx;
+	tmp = (flim[1].fy - flim[0].fy);
+	csi = fabs(csi) > 0.0001 ? csi/sqrt(csi*csi + tmp*tmp) : 0.0;
+	//draw gradient bar, breaks and axis line
+	if(type == 4) GradientBar(o);
+	else if(axis->breaks && axis->nBreaks)DrawBreaks(o);
+	else if((axis->flags) & AXIS_3D){
+		if(!(l_segs = (line_segment**)calloc(1, sizeof(line_segment*))))return;
+		l_segs[0] = new line_segment(this, data, &axline, &pts3D[0], &pts3D[1]);
+		nl_segs = 1;
+		if(l_segs[0])l_segs[0]->DoPlot(o);
+		}
+	else o->oSolidLine(pts);
+	//now execute the draw routine of label and every tick
+	if(axisLabel){
+		if(axis->flags & AXIS_3D) axisLabel->SetSize(SIZE_ZPOS, (pts3D[0].z + pts3D[1].z)>>1); 
+		axisLabel->DoPlot(o);
+		}
+	if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++) 
+		if(Ticks[i] && Ticks[i]->GetSize(SIZE_MINE) >= (axis->min - 1.0e-16)
+		&& Ticks[i]->GetSize(SIZE_MINE) <= (axis->max + 1.0e-16)) Ticks[i]->DoPlot(si, csi, o);
+}
+
+bool
+Axis::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	GraphObj **tmpPlots;
+	void *sv_ptr;
+	scaleINFO *scale;
+	int i;
+
+	switch (cmd) {
+	case CMD_SCALE:
+		if(!Ticks && (axis->flags & 0x03) != AXIS_NOTICKS)CreateTicks();
+		scale = (scaleINFO*)tmpl;
+		lbdist.fx *= scale->sx.fy;			lbdist.fy *= scale->sy.fy;
+		tlbdist.fx *= scale->sx.fy;			tlbdist.fy *= scale->sy.fy;
+		sizAxTickLabel *= scale->sy.fy;		sizAxTick *= scale->sy.fy;
+		sizAxLine *= scale->sy.fy;			brksymsize *= scale->sy.fy;
+		brkgap *= scale->sy.fy;				GridLine.patlength *= scale->sy.fy;
+		GridLine.width *= scale->sy.fy;		tlbdef.fSize *= scale->sy.fy;
+		axis->loc[0].fx *= scale->sx.fy;	axis->loc[1].fx *= scale->sx.fy;
+		axis->loc[0].fy *= scale->sy.fy;	axis->loc[1].fy *= scale->sy.fy;
+		axis->loc[0].fz *= scale->sz.fy;	axis->loc[1].fz *= scale->sz.fy;
+		axis->Center.fx *= scale->sx.fy;	axis->Center.fy *= scale->sy.fy;
+		axis->Radius *= scale->sy.fy;		tlbdef.iSize = 0;
+		if(axisLabel) axisLabel->Command(cmd, tmpl, o);
+		if(Ticks) for(i = 0; i < NumTicks; i++) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_AXIS;
+		data = (DataObj*)tmpl;
+		if(Ticks) for(i = 0; i< NumTicks; i++) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
+		if(axisLabel) axisLabel->Command(cmd, tmpl, o);
+	case CMD_TICK_TYPE:
+		if(cmd == CMD_TICK_TYPE){
+			if(tmpl) tick_type = *((int*)tmpl);
+			else return false;
+			}
+	case CMD_SET_TICKSTYLE:
+		if(cmd == CMD_SET_TICKSTYLE){
+			axis->flags &= ~0x07;		axis->flags |= (*((DWORD*)tmpl) & 0x07);
+			}
+	case CMD_SET_GRIDTYPE:
+		if(cmd == CMD_SET_GRIDTYPE && tmpl && *((int*)tmpl)) {
+			gl_type = *((int*)tmpl);
+			}
+	case CMD_TLB_TXTDEF:			//do all ticks
+		if(Ticks) for(i = 0; i< NumTicks; i++) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
+		if(cmd == CMD_TLB_TXTDEF && tmpl) {
+			memcpy(&tlbdef, tmpl, sizeof(TextDEF));
+			tlbdef.text = 0L;
+			}
+		return true;
+	case CMD_SET_GRIDLINE:
+		if(tmpl) memcpy(&GridLine, tmpl, sizeof(LineDEF));
+		return true;
+	case CMD_MRK_DIRTY:
+		if((type & 0xf) == 4 && !o) cmd = CMD_UPDPG;
+		else return false;
+	case CMD_DRAWPG:	case CMD_UPDPG:
+		if((type & 0xf) == 4 && Ticks) {
+			if(axis->flags & AXIS_INVERT){
+				for(i = NumTicks-1; i >= 0; i--) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
+				}
+			else {
+				for(i = 0; i< NumTicks; i++) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
+				}
+			}
+		return true;
+	case CMD_GET_GRIDLINE:
+		if(tmpl) memcpy(tmpl, &GridLine, sizeof(LineDEF));
+		return true;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(!CurrGO && ObjThere(mev->x, mev->y)) {
+				o->ShowMark(this, MRK_GODRAW);
+				return true;
+				}
+			break;
+			}
+		if(axisLabel && axisLabel->Command(cmd, tmpl, o)) return true;
+		if(Ticks) for(i = (NumTicks-1); i >= 0; i--)
+			if(Ticks[i] && Ticks[i]->Command(cmd, tmpl, o)) return true;
+		return false;
+	case CMD_TEXTTHERE:
+		if(axisLabel && axisLabel->Command(cmd, tmpl, o)) return true;
+		if(Ticks) for(i = 0; i < NumTicks; i++) {
+			if(Ticks[i] && Ticks[i]->Command(cmd, tmpl, o)) return true;
+			}
+		return false;
+	case CMD_SET_AXDEF:
+		if(axis = (AxisDEF*)tmpl) {
+			if(axis && axis->owner == (void*)this) {
+				if(axis->breaks) free(axis->breaks);
+				free(axis);
+				}
+			if(Ticks) for(i = 0; i < NumTicks; i++)
+				if(Ticks[i]) {
+					Ticks[i]->Command(cmd, tmpl, o);
+					Ticks[i]->Command(CMD_SET_GRIDTYPE, (void*) &gl_type, 0L);
+					Ticks[i]->Command(CMD_TLB_TXTDEF, &tlbdef, 0L);
+					if(axis->flags & AXIS_GRIDLINE) Ticks[i]->Command(CMD_SET_GRIDLINE, &GridLine, 0L);
+					Ticks[i]->SetSize(SIZE_TICK_ANGLE, tick_angle);
+					Ticks[i]->SetSize(SIZE_AXIS_TICKS, sizAxTick);
+					Ticks[i]->SetSize(SIZE_LB_XDIST, tlbdist.fx);
+					Ticks[i]->SetSize(SIZE_LB_YDIST, tlbdist.fy);
+					Ticks[i]->Command(CMD_TICK_TYPE, &tick_type, 0L);
+					}
+			return true;
+			}
+		return false;
+	case CMD_CAN_CLOSE:
+		if(axis->owner == (void*)this) return true;
+		return false;
+	case CMD_DROP_LABEL:
+		if(axisLabel)DeleteGO(axisLabel);
+		if(axisLabel = (Label*)tmpl) {
+			axisLabel->parent = this;
+			}
+		return true;
+	case CMD_DELOBJ:
+		o->HideMark();
+		if(!tmpl || !parent) return false;
+		if(DeleteGOL((GraphObj***) &Ticks, NumTicks, (GraphObj *)tmpl, o)) 
+			return parent->Command(CMD_REDRAW, 0L, o);
+		if(tmpl == (void*)axisLabel) {
+			Undo.DeleteGO((GraphObj**)(&axisLabel), 0L, o);
+			return parent->Command(CMD_REDRAW, 0L, o);
+			}
+		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++){ 
+			if(Ticks[i] && Ticks[i]->Command(cmd, tmpl, o))
+				return parent->Command(CMD_REDRAW, 0L, o);
+			}
+		break;
+	case CMD_MUTATE:
+		if(!parent || !(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
+		if(axisLabel == tmpPlots[0]) {
+			Undo.MutateGO(&axisLabel, tmpPlots[1], 0L, o);
+			return true;
+			}
+		break;
+	case CMD_REPL_GO:
+		if(!(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
+		if(Ticks) for(i = 0; i < NumTicks; i++) if(Ticks[i] && Ticks[i] == tmpPlots[0]){
+			return bModified = ReplaceGO((GraphObj**)&Ticks[i], tmpPlots);
+			}
+		if(axisLabel && axisLabel == tmpPlots[0])
+			return bModified = ReplaceGO((GraphObj**)&axisLabel, tmpPlots);
+		return false;
+	case CMD_MOVE:
+		if(moveable) {
+			bModified = true;
+			Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
+			}
+	case CMD_UNDO_MOVE:
+		if(moveable) {
+			axis->loc[0].fx += ((lfPOINT*)tmpl)[0].fx;	axis->loc[0].fy +=  ((lfPOINT*)tmpl)[0].fy;
+			axis->loc[1].fx += ((lfPOINT*)tmpl)[0].fx;	axis->loc[1].fy +=  ((lfPOINT*)tmpl)[0].fy;
+			}
+		CurrGO = this;
+		return parent->Command(CMD_REDRAW, 0, o);
+	case CMD_UPDATE:
+		UpdateTicks();
+	case CMD_SETSCROLL:
+		cmd = CMD_REDRAW;
+	case CMD_REDRAW:
+		bModified = true;
+	case CMD_SET_GO3D:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		return false;
+	case CMD_RESET_LINE:
+		return o->SetLine(&axline);
+	case CMD_FLUSH:
+		if(Ticks) {
+			for(i = 0; i < NumTicks; i++) if(Ticks[i]) DeleteGO(Ticks[i]);
+			free(Ticks);	NumTicks = 0;	Ticks = 0L;
+			}
+		if(l_segs){
+			for (i = 0; i < nl_segs; i++) if(l_segs[i]) delete(l_segs[i]);
+			free(l_segs);		l_segs = 0L;	nl_segs = 0;
+			}
+		if(scaleOut) delete(scaleOut);
+		scaleOut = drawOut = 0L;
+		return true;
+	case CMD_RECALC:
+		if(Ticks) {
+			for(i = 0; i < NumTicks; i++) if(Ticks[i]) Ticks[i]->Command(cmd, tmpl, o);
+			return true;
+			}
+		break;
+	case CMD_AUTOSCALE:		//we receive this command to update ticks after rescaling
+		if(axis && (AxisDEF*)tmpl == axis && (axis->flags & AXIS_AUTOSCALE) && 
+			(axis->flags & AXIS_AUTOTICK)) return Command(CMD_FLUSH, tmpl, o);
+		break;
+	case CMD_SAVE_TICKS:
+		SavVarInit(200 * NumTicks);
+		if(Ticks) for(i = 0; i < NumTicks; i++) {
+			if(Ticks[i]) Ticks[i]->FileIO(SAVE_VARS);
+			}
+		sv_ptr = SavVarFetch();
+		Undo.SavVarBlock(this, &sv_ptr, 0);
+		if(axis->flags & AXIS_AUTOTICK){
+			Undo.ValDword(this, &axis->flags, UNDO_CONTINUE);
+			axis->flags &= ~(AXIS_AUTOTICK | AXIS_AUTOSCALE);
+			}
+		bModified = true;
+		return true;
+		}
+	return false;
+}
+
+void * 
+Axis::ObjThere(int x, int y)
+{
+	int i;
+	POINT p1 = {x, y};
+
+	if(axis->flags & AXIS_ANGULAR) {
+		i = (x - pts[0].x) * (x - pts[0].x) + (y - pts[0].y) * (y - pts[0].y);
+		i = isqr(i) - pts[1].x;
+		if(i < 4 && i > -4) return this;
+		}
+	else if((type & 0x04) == 4 && IsInPolygon(&p1, gradient_box, 5)) return this;
+	else if(IsInRect(&rDims, x, y) && IsCloseToLine(&pts[0], &pts[1], x, y)) {
+		return this;
+		}
+	return 0L;
+}
+
+void
+Axis::Track(POINT *p, anyOutput *o)
+{
+	POINT tpts[5];
+	int i, iw;
+
+	if(!parent || !o) return;
+	iw = 6 + o->un2ix(sizAxLine);
+	defs.UpdRect(o, rDims.left, rDims.top, rDims.right, rDims.bottom);
+	tpts[0].x = pts[0].x+p->x;	tpts[0].y = pts[0].y+p->y;
+	tpts[1].x = pts[1].x+p->x;	tpts[1].y = pts[1].y+p->y;
+	defs.UpdRect(o, tpts[0].x-iw, tpts[0].y-iw, tpts[1].x-iw, tpts[1].y-iw);
+	defs.UpdRect(o, tpts[0].x+iw, tpts[0].y+iw, tpts[1].x+iw, tpts[1].y+iw);
+	if(type & 0x04) {
+		for(i = 0; i < 5; i++) {
+			tpts[i].x = gradient_box[i].x + p->x;
+			tpts[i].y = gradient_box[i].y + p->y;
+			defs.UpdRect(o, tpts[i].x-iw, tpts[i].y-iw, tpts[i].x+iw, tpts[i].y+iw);
+			}
+		o->ShowLine(tpts, 5, colAxis);
+		}
+	else o->ShowLine(tpts, 2, colAxis);
+	if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++) {
+		if(Ticks[i] && Ticks[i]->GetSize(SIZE_MINE) >= (axis->min - 1.0e-16)
+			&& Ticks[i]->GetSize(SIZE_MINE) <= (axis->max + 1.0e-16)) Ticks[i]->Track(p, o);
+		}
+}
+
+DWORD
+Axis::GradColor(double val)
+{
+	DWORD retcol = 0x00cbcbcbL;
+	double y, f;
+
+	y = (val-axis->min)/(axis->max-axis->min);
+	if(axis->flags & AXIS_INVERT) y = 1.0-y;
+	if(grad_type & 0x10) y = 1.0-y;
+	switch(grad_type & 0x0f) {
+	case 0:
+		retcol = gCol_0;		break;
+	case 1:		case 4:
+		retcol = 0x00000000L;
+		if(y >= 1.0) retcol = 0x000000ffL;
+		else if(y >= 0.75) {
+			f = (y - 0.75)*1024.0;		retcol |= 0x000000ffL;
+			retcol |= (((f < 255.0)?((int)(255.0-f)):0)<<8);
+			}
+		else if(y >= 0.5) {
+			f = (y - 0.5)*1024.0;		retcol |= 0x0000ff00L;
+			retcol |= ((f < 255.0)?((int)f):0);
+			}
+		else if(y >= 0.0 && (grad_type &0x0f) == 4) {
+			f = y*512.0;
+			retcol |= (((f < 255.0)?((int)f):0)<<8);
+			retcol |= (((f < 255.0)?((int)(255.0-f)):255)<<16);
+			}
+		else if(y >= 0.25) {
+			f = (y - 0.25)*1024.0;		retcol |= 0x0000ff00L;
+			retcol |= (((f < 255.0)?((int)(255.0-f)):255)<<16);
+			}
+		else if(y >= 0.0) {
+			f = y*1024.0;			retcol |= 0x00ff0000L;
+			retcol |= (((f < 255.0)?((int)f):0)<<8);
+			}
+		else retcol = 0x00ff0000L;
+		break;
+	case 2:
+		retcol = 0x00000000L;
+		if(y >= 1.0) retcol = 0x00ffffffL;
+		else if(y >= (5.0/6.0)) {
+			f = (y - (5.0/6.0))*1536.0;	retcol |= 0x000000ffL;
+			retcol |= (((f < 255.0)?((int)f):0xff)<<8);
+			retcol |= (((f < 255.0)?((int)f):0xff)<<16);
+			}
+		else if(y >= (4.0/6.0)) {
+			f = (y - (4.0/6.0))*1536.0;	retcol |= 0x000000ffL;
+			retcol |= (((f < 255.0)?((int)(255.0-f)):0)<<8);
+			}
+		else if(y >= (3.0/6.0)) {
+			f = (y - (3.0/6.0))*1536.0;	retcol |= 0x0000ff00L;
+			retcol |= ((f < 255.0)?((int)f):0xff);
+			}
+		else if(y >= (2.0/6.0)) {
+			f = (y - (2.0/6.0))*1536.0;	retcol |= 0x0000ff00L;
+			retcol |= (((f < 255.0)?((int)(255.0-f)):0xff)<<16);
+			}
+		else if(y >= (1.0/6.0)) {
+			f = (y - (1.0/6.0))*1536.0;	retcol |= 0x00ff0000L;
+			retcol |= (((f < 255.0)?((int)f):0xff)<<8);
+			}
+		else if(y >= 0.0) {
+			f = y*1536.0;
+			retcol |= (((f < 255.0)?((int)f):0xff)<<16);
+			}
+		else retcol = 0x0L;
+		break;
+	case 3:
+		retcol = IpolCol(gCol_2, gCol_1, y);
+		}
+	if(gTrans & 0xff000000) {
+		retcol = (retcol & 0x00ffffff) | (gTrans & 0xff000000);
+		}
+	return retcol;
+}
+
+void
+Axis::SetTick(long idx, double val, DWORD flags, char *txt)
+{
+	char *l;
+
+	if((flags & AXIS_ANGULAR) && !(flags & 0x03)) flags |= AXIS_POSTICKS;
+	Ticks[idx] = new Tick(this, data, val, flags);
+	if(!txt) {
+		WriteNatFloatToBuff(TmpTxt, val);
+		l = (char*)memdup(TmpTxt+1, (int)strlen(TmpTxt+1)+1, 0);
+		}
+	else l = (char*)memdup(txt, (int)strlen(txt)+1, 0);
+	if(!gl_type) {
+		}
+	if(Ticks[idx]) {
+		Ticks[idx]->Command(CMD_SET_GRIDTYPE, (void*) &gl_type, 0L);
+		if(l) Ticks[idx]->Command(CMD_SETTEXT, l, 0L);
+		Ticks[idx]->Command(CMD_TLB_TXTDEF, &tlbdef, 0L);
+		if(flags & AXIS_GRIDLINE) Ticks[idx]->Command(CMD_SET_GRIDLINE, &GridLine, 0L);
+		Ticks[idx]->SetSize(SIZE_TICK_ANGLE, tick_angle);
+		Ticks[idx]->SetSize(SIZE_AXIS_TICKS, sizAxTick);
+		Ticks[idx]->Command(CMD_TICK_TYPE, &tick_type, 0L);
+		}
+	if(l) free(l);
+}
+
+void
+Axis::mkTimeAxis()
+{
+	int nstep, mode;
+	double span, val;
+	rlp_datetime start, step;
+	static char *tick_formats[] = {"y", "Y", "W", "x", "Z.V.", "d", "H:M", "d"};
+
+	span = axis->max - axis->min;
+	memset(&step, 0, sizeof(rlp_datetime));		parse_datevalue(&start, axis->Start);
+	start.hours=start.minutes=start.doy = 0;	start.seconds = val = 0.0;
+	if(span > 60.0) start.dow = 1;
+	if(span > 24000.0) {
+		step.year = start.month = start.dom = 1;
+		nstep = 2 + (int)(span / 364.0);		mode = 0;
+		}
+	else if(span > 700.0) {
+		step.month = start.month = start.dom = 1;
+		nstep = 2 + (int)(span / 28.0);			mode = 1;
+		}
+	else if(span > 300.0) {
+		step.month = start.dom = 1;
+		nstep = 2 + (int)(span / 28.0);			mode = 2;
+		}
+	else if(span > 150.0) {
+		step.month = start.dom = 1;
+		nstep = 2 + (int)(span / 28.0);			mode = 3;
+		}
+	else if(span > 60.0) {
+		step.dom = 1;
+		nstep = 6 + (int)(span/7.0);			mode = 4;
+		}
+	else if(span > 8.0) {
+		step.dom = 1;
+		nstep = 2+(int)(span);					mode = 5;
+		}
+	else if(span > 2.0) {
+		step.hours = 6;
+		nstep = 4+(int)(span*4.0);				mode = 6;
+		}
+	else if(span > 0.5) {
+		step.hours = (span > 0.9 ? 2 : 1);
+		nstep = 4+(int)(span*24.0);				mode = 7;
+		}
+	else if(span > 0.05) {
+		step.minutes = (span > 0.3 ? (span > 0.4 ? 30 : 15) : (span <= 0.1 ? 5 : 10));
+		nstep = 100;							mode = 8;
+		}
+	else if(span > 0.005) {
+		step.minutes = 1;
+		nstep = 100;							mode = 8;
+		}
+	else return;
+	if(nstep < 50) nstep = 50;					add_date(&start, 0L);
+	if(!(Ticks = (Tick**)calloc(nstep, sizeof(Tick*))))return;
+	for(NumTicks = 0; NumTicks < nstep && val <= axis->max;	NumTicks++) {
+		val = date2value(&start);
+		switch(mode) {
+		case 0:
+			if((start.year%10) == 0) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[0]));
+			else NumTicks--;					break;
+		case 1:
+			if(start.month == 1){
+				if(span <= 6000.0 || (span <= 12000.0 && (start.year%5) == 0) || (span <= 24000.0 && (start.year%10) == 0))
+					SetTick(NumTicks, val, axis->flags, date2text(&start, 
+					(span > 3000.0 && span < 12000.0)?tick_formats[0] : tick_formats[1]));
+				else NumTicks--;
+				}
+			else if(span <= 1500.0) SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, 
+				date2text(&start, tick_formats[4]));
+			else NumTicks--;					break;
+		case 2:
+			SetTick(NumTicks, val, axis->flags, date2text(&start, start.month==1 ? tick_formats[0]:tick_formats[2]));
+			break;
+		case 3:
+			SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[3]));
+			break;
+		case 4:
+			if(start.dom == 1)SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[3]));
+			else if(start.dow == 1) SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[4]));
+			else NumTicks--;					break;
+		case 5:
+			if(start.dow == 1) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[4]));
+			else if(span < 30.0)SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[7]));
+			else NumTicks--;					break;
+		case 6:
+			if(start.hours == 0) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[4]));
+			else SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[6]));
+			break;
+		case 7:
+			if(start.hours == 0) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[5]));
+			else if((start.hours % 6) == 0) SetTick(NumTicks, val, axis->flags, date2text(&start, tick_formats[6]));
+			else SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[6]));
+			break;
+		case 8:
+			if(start.minutes == 0){
+				if(span <= 0.3 || (start.hours %2) == 0) SetTick(NumTicks, val, axis->flags, 
+					date2text(&start, tick_formats[6]));
+				else SetTick(NumTicks, val, axis->flags, "");
+				}
+			else SetTick(NumTicks, val, axis->flags | AXIS_MINORTICK, date2text(&start, tick_formats[6]));
+			break;
+			}
+		add_date(&start, &step);
+		}
+}
+
+void
+Axis::CreateTicks()
+{
+	int i, n, nstep;
+	char *format, *tick_label;
+	double fVal, tmp;
+	DWORD flags;
+
+	if(axis->min == -HUGE_VAL || axis->min == HUGE_VAL) return;
+	if(axis->max == -HUGE_VAL || axis->max == HUGE_VAL) return;
+	if(axis->min >= axis->max) return;
+	if(Ticks) {
+		Undo.DropListGO(this, (GraphObj***)&Ticks, &NumTicks, 0L);
+		}
+	Command(CMD_FLUSH, 0L, 0L);
+	if((axis->flags & 0xf000) == AXIS_LOG) {	//log-axis
+		if(axis->Start > defs.min4log) tmp = log10(axis->Start);
+		else switch (type){
+			case 1:								//x axis
+			case 2:								//y axis
+			case 3:								//z axis
+				axis->Start = tmp = log10(base4log(axis, type-1));
+				if(axis->Start <= 0.0) axis->Start = 1.0;
+				break;
+			default: return;
+			}
+		n = (int)(log10(axis->max) - tmp);
+		Ticks = (Tick**)calloc(100, sizeof(Tick*));
+		for(NumTicks=0, i=(int)floor(tmp); NumTicks <90; i++){
+			SetTick(NumTicks++, pow(10.0, i), axis->flags, 0L);
+			if(n < 5) {
+				flags = n ? axis->flags | AXIS_MINORTICK : axis->flags;
+				SetTick(NumTicks++, 5.0*pow(10.0, i), axis->flags, 0L);
+				SetTick(NumTicks++, 3.0*pow(10.0, i), flags, 0L);
+				SetTick(NumTicks++, 4.0*pow(10.0, i), flags, 0L);
+				SetTick(NumTicks++, 6.0*pow(10.0, i), flags, 0L);
+				SetTick(NumTicks++, 7.0*pow(10.0, i), flags, 0L);
+				SetTick(NumTicks++, 8.0*pow(10.0, i), flags, 0L);
+				SetTick(NumTicks++, 9.0*pow(10.0, i), flags, 0L);
+				if(n < 3) SetTick(NumTicks++, 2.0*pow(10.0, i), axis->flags, 0L);
+				else SetTick(NumTicks++, 2.0*pow(10.0, i), axis->flags | AXIS_MINORTICK, 0L);
+				}
+			else {
+				SetTick(NumTicks++, 5.0*pow(10.0, i), axis->flags | AXIS_MINORTICK, 0L);
+				}
+			}
+		}
+	else {										//linear axis
+		if((axis->flags & 0xf000) == AXIS_DATETIME) {
+			mkTimeAxis();		return;
+			}
+		if(atv && (nstep = atv->Count()) && (Ticks = (Tick**)calloc(nstep+1, sizeof(Tick*)))) {
+			for(NumTicks = i = n = 0; NumTicks < nstep && atv->GetItem(i, &tick_label, &fVal);	i++) {
+				SetTick(NumTicks, fVal, axis->flags, tick_label);
+				NumTicks++;		n += (int)strlen(tick_label);
+				}
+			type = type;
+#ifdef _WINDOWS
+			if(type == 1 && n > 40) {
+				tlbdef.RotBL = n >100 ? 90.0 : 45.0;		tlbdef.Align = TXA_HRIGHT | TXA_VCENTER;
+				tlbdist.fy = sizAxTick;						SetSize(SIZE_TLB_YDIST, tlbdist.fy);
+				Command(CMD_TLB_TXTDEF, &tlbdef, 0L);
+				}
+#else
+			if(type == 1 && n > 30) {
+				tlbdef.RotBL = n >70 ? 90.0 : 45.0;			tlbdef.Align = TXA_HRIGHT | TXA_VCENTER;
+				tlbdist.fy = sizAxTick;						SetSize(SIZE_TLB_YDIST, tlbdist.fy);
+				Command(CMD_TLB_TXTDEF, &tlbdef, 0L);
+				}
+#endif
+			return;
+			}
+		if((axis->flags & 0xf000) == AXIS_RECI) {
+			NiceStep(axis, 8);
+			format = GetNumFormat(floor(log10(axis->Step)));
+			fVal = axis->Start;
+			}
+		else {
+			NiceStep(axis, 4);
+			format = GetNumFormat(floor(log10(fabs(axis->max - axis->min))));
+			fVal = axis->Start;
+			}
+		nstep = 1+(int)((axis->max-axis->min)/axis->Step);
+		Ticks = (Tick**)calloc(nstep+2, sizeof(Tick*));
+		if(!Ticks) return;
+		for(NumTicks = 0; NumTicks < nstep && fVal <= axis->max;
+			NumTicks++, fVal += axis->Step) {
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, format, fVal);
+#else
+			sprintf(TmpTxt, format, fVal);
+#endif
+			SetTick(NumTicks, fVal, axis->flags, TmpTxt);
+			}
+		}
+}
+
+void
+Axis::ManuTicks(double sa, double st, int n, DWORD flags)
+{
+	int j, m;
+	char *format;
+	double fVal, mival, mist;
+
+	Command(CMD_FLUSH, 0L, 0L);
+	mist = st/(double)(n+1);
+	m = (int)(((axis->max-axis->min) / st)*(double)(n+1))+n+2;
+	format = GetNumFormat(floor(log10(st)));
+	Ticks = (Tick**)calloc(m, sizeof(Tick*));
+	for(NumTicks = 0, fVal = sa; NumTicks < m && fVal <= axis->max; NumTicks++) {
+#ifdef USE_WIN_SECURE
+		sprintf_s(TmpTxt, TMP_TXT_SIZE, format, fVal);
+#else
+		sprintf(TmpTxt, format, fVal);
+#endif
+		SetTick(NumTicks, fVal, flags, TmpTxt);
+		for(j = 0; j < n; j++) {
+			mival = fVal+mist*(double)j +mist;
+			if(mival < axis->max) {
+#ifdef USE_WIN_SECURE
+				sprintf_s(TmpTxt, TMP_TXT_SIZE, format, mival);
+#else
+				sprintf(TmpTxt, format, mival);
+#endif
+				NumTicks++;
+				SetTick(NumTicks, mival, flags | AXIS_MINORTICK, TmpTxt);
+				}
+			}
+		fVal += st;
+		}
+}
+
+bool
+Axis::GetValuePos(double val, double *fix, double *fiy, double *fiz, anyOutput *op)
+{
+	double temp, tmp1 = 1.0;
+	int i;
+	bool bRet = true;
+	anyOutput *o;
+	fPOINT3D p1, p2;
+	lfPOINT fdp, fip;
+	AxisDEF caxis;
+
+	*fix = *fiy = *fiz = 0.0;
+	if(!op || !parent || (val < axis->min && val < axis->max) || 
+		(val > axis->min && val > axis->max)) return false;
+	for(i = 0; i < axis->nBreaks && bRet; i++) 
+		if((val >= axis->breaks[i].fx && val <= axis->breaks[i].fy) ||
+			(val <= axis->breaks[i].fx && val >= axis->breaks[i].fy)) bRet = false;
+	if(axis->owner == this && scaleOut)	o = scaleOut;
+	else o = op;
+	if(axis->flags & AXIS_3D) {
+		//Get a copy of the axis because GetAxisFac() modifies its contents
+		memcpy(&caxis, axis, sizeof(AxisDEF));
+		p1.fx = op->un2fix(axis->loc[1].fx - caxis.loc[0].fx);
+		p1.fy = op->un2fiy(axis->loc[1].fy - caxis.loc[0].fy);
+		p1.fz = op->un2fiz(axis->loc[1].fz - caxis.loc[0].fz);
+		tmp1 = sqrt(p1.fx*p1.fx + p1.fy*p1.fy + p1.fz*p1.fz);
+		p1.fx /= tmp1;		p1.fy /= tmp1;		p1.fz /= tmp1;
+		tmp1 = GetAxisFac(&caxis, tmp1, (type&0xf)-1);
+		temp = TransformValue(&caxis, val, true);
+		temp = (temp - caxis.min)*tmp1;
+		if(axis->flags & AXIS_INVERT) {
+			p1.fx = op->fix2un(op->un2fix(axis->loc[1].fx) - p1.fx*temp);
+			p1.fy = op->fiy2un(op->un2fiy(axis->loc[1].fy) - p1.fy*temp);
+			p1.fz = op->fix2un(op->un2fiz(axis->loc[1].fz) - p1.fz*temp);
+			}
+		else {
+			p1.fx = op->fix2un(p1.fx*temp+op->un2fix(axis->loc[0].fx));
+			p1.fy = op->fiy2un(p1.fy*temp+op->un2fiy(axis->loc[0].fy));
+			p1.fz = op->fix2un(p1.fz*temp+op->un2fiz(axis->loc[0].fz));
+			}
+		op->cvec2ivec(&p1, &p2);
+		*fix = p2.fx;	*fiy = p2.fy;	*fiz = p2.fz;
+		if((p2.fx < (flim[0].fx-1) && p2.fx < (flim[1].fx-1)) ||
+			(p2.fx > (flim[0].fx+1) && p2.fx > (flim[1].fx+1)) ||
+			(p2.fy < (flim[0].fy-1) && p2.fy < (flim[1].fy-1)) ||
+			(p2.fy > (flim[0].fy+1) && p2.fy > (flim[1].fy+1)) ||
+			(p2.fz < (flim[0].fz-1) && p2.fz < (flim[1].fz-1)) ||
+			(p2.fz > (flim[0].fz+1) && p2.fz > (flim[1].fz+1))) bRet = false;
+		return bRet;
+		}
+	else if(axis->flags & AXIS_ANGULAR) {
+		fdp.fx = val;				fdp.fy = parent->GetSize(SIZE_BOUNDS_TOP);
+		op->fp2fip(&fdp, &fip);		*fix = fip.fx;			*fiy = fip.fy;
+		return bRet;
+		}
+	else if((type & 0x0f) == 1 && fabs(flim[1].fx-flim[0].fx)>10.0) {	//x dominant
+		tmp1 = (o->fx2fix(val)-flim[0].fx)/(flim[1].fx-flim[0].fx);
+		*fix = flim[0].fx - tmp1*(flim[0].fx - flim[1].fx);
+		*fiy = flim[0].fy - tmp1*(flim[0].fy - flim[1].fy);
+		}
+	else if(((type & 0x0f) == 2  || (type &0x0f) == 4) && fabs(flim[1].fy-flim[0].fy)>10.0){	//y dominant
+		tmp1 = (o->fy2fiy(val)-flim[1].fy)/(flim[0].fy-flim[1].fy);
+		*fix = flim[1].fx + tmp1*(flim[0].fx - flim[1].fx);
+		*fiy = flim[1].fy + tmp1*(flim[0].fy - flim[1].fy);
+		}
+	else return false;
+	if(tmp1 > -1.0e-15 && tmp1 < 1.0 + 1.0e-15) return bRet;
+	return false;
+}
+
+void 
+Axis::BreakSymbol(POINT3D *p1, double dsi, double dcsi, bool connect, anyOutput *o)
+{
+	POINT *sym = 0L, *csym = 0L;
+	static POINT lp;
+	int j, n = 0;
+	double tmp;
+
+	switch (brksym){
+	case 2:
+		n = o->un2ix(brksymsize);
+		if(!(sym = (POINT*)calloc(2, sizeof(POINT))))return;
+		if(!(csym = (POINT*)calloc(2, sizeof(POINT)))){
+			free(sym);
+			return;
+			}
+		sym[0].x = (int)((-(n>>1))*dsi) + (int)((n>>2)*dcsi); 
+		sym[0].y = (int)((-(n>>1))*dcsi) + (int)((n>>2)*dsi);
+		sym[1].x = (int)((n>>1)*dsi) - (int)((n>>2)*dcsi); 
+		sym[1].y = (int)((n>>1)*dcsi) - (int)((n>>2)*dsi);
+		n = 2;
+		break;
+	case 3:
+		n = o->un2ix(brksymsize);
+		if(!(sym = (POINT*)calloc(2, sizeof(POINT))))return;
+		if(!(csym = (POINT*)calloc(2, sizeof(POINT)))){
+			free(sym);
+			return;
+			}
+		sym[0].x = (int)((-(n>>1))*dsi); 
+		sym[0].y = (int)((-(n>>1))*dcsi);
+		sym[1].x = (int)((n>>1)*dsi); 
+		sym[1].y = (int)((n>>1)*dcsi);
+		n = 2;
+		break;
+	case 4:
+		n = o->un2ix(brksymsize);
+		if(!(sym = (POINT*)calloc(n, sizeof(POINT))))return;
+		if(!(csym = (POINT*)calloc(n, sizeof(POINT)))){
+			free(sym);
+			return;
+			}
+		for(j = 0; j< n; j++) {
+			tmp = (double)j*6.283185307/(double)n;
+			tmp = sin(tmp)*(double)n/6.0;
+			sym[j].x = (int)((j-(n>>1))*dsi) + (int)(tmp*dcsi); 
+			sym[j].y = (int)((j-(n>>1))*dcsi) + (int)(tmp*dsi);
+			}
+		break;
+		}
+	if(sym && csym && n) {
+		if(brksym == 3 && connect) {
+			csym[0].x = lp.x;				csym[0].y = lp.y;
+			csym[1].x = sym[0].x + p1->x;	csym[1].y = sym[0].y + p1->y;
+			o->oPolyline(csym, 2);
+			}
+		for(j = 0; j < n; j++) {
+			csym[j].x = sym[j].x + p1->x;
+			csym[j].y = sym[j].y + p1->y;
+			}
+		o->oPolyline(csym, n);
+		lp.x = csym[n-1].x;				lp.y = csym[n-1].y;
+		}
+	if(sym) free(sym);			if(csym) free(csym);
+}
+
+void
+Axis::DrawBreaks(anyOutput *o)
+{
+	fPOINT3D *pts, np, tmp_p;
+	double dx, dy, dz, d, dn, da, lsi, lcsi;
+	POINT pbs[2];
+	int i, j;
+
+	dx = flim[0].fx > flim[1].fx ? flim[1].fx : flim[0].fx;
+	dy = flim[0].fy > flim[1].fy ? flim[1].fy : flim[0].fy;
+	dz = flim[0].fz > flim[1].fz ? flim[0].fz + (flim[0].fz - flim[1].fz)*50.0 : 
+		flim[1].fz + (flim[1].fz - flim[0].fz)*50.0;
+	if(axis->flags & AXIS_3D){
+		if(!(l_segs = (line_segment**)calloc(2+axis->nBreaks, sizeof(line_segment*))))return;
+		nl_segs = 0;
+		}
+	if(!(pts = (fPOINT3D*)calloc(2+axis->nBreaks, sizeof(fPOINT3D))))return;
+	memcpy(pts, &flim[0], sizeof(fPOINT3D));
+	for (i = 1; i < (2+axis->nBreaks); i++) {
+		switch (i) {
+		case 1:
+			memcpy(&np, &flim[1], sizeof(fPOINT3D));
+			break;
+		default:
+			GetValuePos(axis->breaks[i-2].fx, &np.fx, &np.fy, &np.fz, o);
+			break;
+			}
+		for(j = 0; j < i; j++) {
+			dn = (d = np.fx-dx) * d;		dn += ((d = np.fy-dy) * d);
+			dn += ((d = np.fz-dz) * d);		da = (d = pts[j].fx-dx) * d;		
+			da += ((d = pts[j].fy-dy)*d);	da += ((d = pts[j].fz-dz)*d);
+			if(dn < da) {
+				memcpy(&tmp_p, &pts[j], sizeof(fPOINT3D));
+				memcpy(&pts[j], &np, sizeof(fPOINT3D));
+				memcpy(&np, &tmp_p, sizeof(fPOINT3D));
+				}
+			}
+		memcpy(&pts[i], &np, sizeof(fPOINT3D));
+		}
+	for(i = 1; i < (2+axis->nBreaks); i++) {
+		dn = (d = pts[i].fx-pts[i-1].fx) * d;		dn += ((d = pts[i].fy-pts[i-1].fy) * d);
+		dn += ((d = pts[i].fz-pts[i-1].fz) * d);	dn = sqrt(dn);
+		da = o->un2fix(brkgap/2.0);
+		if(dn > 0.01) {
+			np.fx = da * (pts[i].fx-pts[i-1].fx)/dn;
+			np.fy = da * (pts[i].fy-pts[i-1].fy)/dn;
+			np.fz = da * (pts[i].fz-pts[i-1].fz)/dn;
+			d = (pts[i].fx - pts[i-1].fx) * (pts[i].fx - pts[i-1].fx);
+			d += ((pts[i].fy - pts[i-1].fy) * (pts[i].fy - pts[i-1].fy));
+			d = sqrt(d);			if(d < 1.0) d = 1.0;
+			lsi = (pts[i].fy - pts[i-1].fy)/d;
+			lcsi = (pts[i].fx -pts[i-1].fx)/d;
+			if(i == 1) {
+				pts3D[0].x = pbs[0].x = iround(pts[i-1].fx);
+				pts3D[0].y = pbs[0].y = iround(pts[i-1].fy);
+				pts3D[0].z = iround(pts[i-1].fz);
+				}
+			else {
+				pts3D[0].x = pbs[0].x = iround(pts[i-1].fx + np.fx);
+				pts3D[0].y = pbs[0].y = iround(pts[i-1].fy + np.fy);
+				pts3D[0].z = iround(pts[i-1].fz + np.fz);
+				BreakSymbol(&pts3D[0], lsi, -lcsi, true, o);
+				}
+			if(i == (1+axis->nBreaks)) {
+				pts3D[1].x = pbs[1].x = iround(pts[i].fx);
+				pts3D[1].y = pbs[1].y = iround(pts[i].fy);
+				pts3D[1].z = iround(pts[i].fz);
+				}
+			else {
+				pts3D[1].x = pbs[1].x = iround(pts[i].fx - np.fx);
+				pts3D[1].y = pbs[1].y = iround(pts[i].fy - np.fy);
+				pts3D[1].z = iround(pts[i].fz - np.fz);
+				BreakSymbol(&pts3D[1], lsi, -lcsi, false, o);
+				}
+			if(axis->flags & AXIS_3D) {
+				if(l_segs[nl_segs] = new line_segment(this, data, &axline, &pts3D[0], &pts3D[1])){
+					l_segs[nl_segs]->DoPlot(o);			nl_segs++;
+					}
+				}
+			else o->oSolidLine(pbs);
+			}
+		}
+	free(pts);
+}
+
+void 
+Axis::UpdateTicks()
+{
+	int i, j, k, l;
+	double tmpval;
+	AccRange *rT=0L, *rL=0L, *rMT=0L;
+
+	if(!ssMATval || !(rT = new AccRange(ssMATval))) return;
+	if(Ticks) {
+		Undo.DropListGO(this, (GraphObj***)&Ticks, &NumTicks, 0L);
+		}
+	if(ssMATlbl) rL = new AccRange(ssMATlbl);
+	if(ssMITval) rMT = new AccRange(ssMITval);
+	if(!(Ticks = ((Tick**)calloc(rT->CountItems()+ (( rMT != 0L) ? rMT->CountItems() : 0) +4,
+		sizeof(Tick*))))) return;
+	rT->GetFirst(&i, &j);		if(!(rT->GetNext(&i, &j)))return;
+	if(rL) rL->GetFirst(&k, &l);
+	NumTicks =0;
+	do{
+		if(rL) rL->GetNext(&k, &l);
+		if(data->GetValue(j, i, &tmpval)) {
+			if(!(Ticks[NumTicks] = new Tick(this, data, tmpval, axis->flags)))return;
+			if(rL) {
+				if(Ticks[NumTicks] && data->GetText(l, k, TmpTxt, TMP_TXT_SIZE)) 
+					Ticks[NumTicks]->Command(CMD_SETTEXT, TmpTxt, 0L);
+				}
+			Ticks[NumTicks]->SetSize(SIZE_TICK_ANGLE, tick_angle);
+			Ticks[NumTicks]->Command(CMD_TICK_TYPE, &tick_type, 0L);
+			}
+		NumTicks++;
+		}while(rT->GetNext(&i, &j));
+	if(rMT) {
+		if(rMT->GetFirst(&i, &j) && rMT->GetNext(&i, &j)) do {
+			if(data->GetValue(j, i, &tmpval) &&	0L!=(Ticks[NumTicks] = new Tick(this, data, 
+				tmpval, axis->flags | AXIS_MINORTICK))) {
+				Ticks[NumTicks]->SetSize(SIZE_TICK_ANGLE, tick_angle);
+				Ticks[NumTicks]->Command(CMD_TICK_TYPE, &tick_type, 0L);
+				NumTicks++;
+				}
+			}while(rMT->GetNext(&i, &j));
+		}
+	Command(CMD_TLB_TXTDEF, &tlbdef, 0L);	SetSize(SIZE_TLB_XDIST, tlbdist.fx);
+	SetSize(SIZE_TLB_YDIST, tlbdist.fy);
+	if(rT) delete(rT);	if(rL) delete(rL);	if(rMT) delete(rMT);
+}
+
+void
+Axis::GradientBar(anyOutput *o)
+{
+	FillDEF gf = {0, 0x0, 1.0, 0L, 0x0};
+	LineDEF gl = {0.0, 1.0, 0x0, 0x0};
+	POINT gpt[5];
+	double v_val, fix, fiy, fiz;
+	int i, iw;
+
+	if(!o || !Ticks || NumTicks < 2) return;
+	iw = o->un2ix(defs.GetSize(SIZE_AXIS_TICKS)*2);
+	GetValuePos(axis->min, &fix, &fiy, &fiz, o);
+	gpt[0].x = gpt[3].x = gpt[4].x = (iround(fix)-iw);
+	gpt[0].y = gpt[1].y = gpt[4].y = iround(fiy);
+	memcpy(&gradient_box, &gpt, sizeof(POINT)*5);		
+	gf.color = gf.color2 = gl.color = GradColor(axis->min);
+	for(i = 0; i < NumTicks; i++) if(Ticks[i]) {
+		if(GetValuePos(v_val = Ticks[i]->GetSize(SIZE_MINE), &fix, &fiy, &fiz, o)) {
+			gpt[1].x = gpt[2].x = iround(fix);		gpt[2].y = gpt[3].y = iround(fiy);
+			o->SetLine(&gl);	o->SetFill(&gf);	
+			if(gpt[1].y != gpt[2].y) o->oPolygon(gpt, 5, 0L);
+			gpt[0].x= gpt[3].x= gpt[4].x= (pts[1].x-iw);	gpt[0].y = gpt[1].y = gpt[4].y = gpt[2].y;
+			gf.color = gf.color2 = gl.color = GradColor(v_val);
+			}
+		}
+	if(GetValuePos(axis->max, &fix, &fiy, &fiz, o)) {
+		gpt[1].x = gpt[2].x = gradient_box[1].x = gradient_box[2].x = iround(fix);
+		gpt[2].y = gpt[3].y = gradient_box[2].y = gradient_box[3].y = iround(fiy);
+		o->SetLine(&gl);	o->SetFill(&gf);	
+		if(gpt[1].y != gpt[2].y) {
+			o->oPolygon(gpt, 5, 0L);
+			}
+		UpdateMinMaxRect(&rDims, gpt[0].x, gpt[1].y);
+		o->SetLine(&axline);
+		o->oPolyline(gradient_box, 5);
+		}
+	else o->SetLine(&axline);
+}
diff --git a/Export.cpp b/Export.cpp
index 461ee15..7fabea0 100755
--- a/Export.cpp
+++ b/Export.cpp
@@ -1,1062 +1,1108 @@
-//Export.cpp, Copyright (c) 2002-2007 R.Lackner
-//export graph files
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-#include "rlplot.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <math.h>
-#include <fcntl.h>				//file open flags
-#include <sys/stat.h>			//I/O flags
-#ifdef USE_WIN_SECURE
-	#include <share.h>			//I/O flags
-#endif
-
-#ifdef _WINDOWS
-	#include <io.h>					//for read/write
-#else
-	#define O_BINARY 0x0
-	#include <unistd.h>
-#endif
-
-extern char TmpTxt[];
-extern Default defs;
-static char *str_ind = "                              ";
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Entry point to export graph to Windows Meta File
-void DoExportWmf(GraphObj *g, char *FileName, float res, DWORD flags)
-{
-	InfoBox("The export of Windos metafile (*.wmf)\n has been dicontinued.\n\n");
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// export to *.svg file (scalable vector graphic)
-// this code is based on information from the following books
-// H. Spona, 'Das Einsteigerseminar SVG-Webgrafiken mit XML',
-//     vmi, ISBN 3-8266-7181-3
-// M. Salathe, 'SVG Scalabe Vector Graphics ...f�r professionelle Einsteiger',
-//     M&T, ISBN 3-8272-6188-0
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class ExportSVG:public anyOutput {
-public:
-	HatchOut *hgo;
-
-	ExportSVG(GraphObj *g, char *FileName, DWORD flags);
-	~ExportSVG();
-	bool SetLine(LineDEF *lDef);
-	bool SetFill(FillDEF *fill);
-	bool SetTextSpec(TextDEF *set);
-	bool StartPage();
-	bool EndPage();
-	bool oCircle(int x1, int y1, int x2, int y2, char* nam = 0L);
-	bool oPolyline(POINT * pts, int cp, char *nam = 0L);
-	bool oRectangle(int x1, int y1, int x2, int y2, char *nam = 0L);
-	bool oSolidLine(POINT *p);
-	bool oTextOut(int x, int y, char *txt, int cb);
-	bool oTextOutW(int x, int y, w_char *txt, int cb);
-	bool oPolygon(POINT *pts, int cp, char * nam = 0L);
-
-private:
-	int iLineWidth, cb_out, out_pos, out_size, cb_ind;
-	char *out_buff;
-	bool bUseGroupLine, bOutputPending;
-	GraphObj *go;
-	char *name, output[120], tHatchStyle[80];
-	DWORD flags;
-
-	bool com_TextOut(int x, int y, char *txt, int cb);
-	void Indent(bool ind);
-	void AddToOutput(char *txt, int len);
-	char *ColName(DWORD col);
-};
-
-ExportSVG::ExportSVG(GraphObj *g, char *FileName, DWORD flg)
-{
-	hgo =0L;
-	DeskRect.left = DeskRect.top = 0;
-	DeskRect.right = DeskRect.bottom = 0x4fffffff;
-	dFillCol = 0xffffffffL;
-	hres = vres = 1000.0f;
-	go = g;
-	if(FileName)name = (char*)memdup(FileName, (int)strlen(FileName)+1, 0);
-	else name = 0L;
-	out_buff = 0L;		out_pos = out_size = 0;
-	flags = flg;		cb_ind = 3;
-	rlp_strcpy(tHatchStyle, 80, "style=\"stroke:black; stroke-width:1\"");
-	bUseGroupLine = false;
-}
-
-ExportSVG::~ExportSVG()
-{
-	if(hgo) delete hgo;
-	if(name) free(name);
-}
-
-bool
-ExportSVG::SetLine(LineDEF *lDef)
-{
-	LineWidth = lDef->width;
-	if(1 >(iLineWidth  = iround(un2fix(lDef->width)))) iLineWidth = 1;
-	dPattern = lDef->pattern;
-	dLineCol = lDef->color;
-	RLP.finc = (float)(256.0/un2fix(lDef->patlength*8.0));
-	RLP.fp = 0.0;
-	return true;
-}
-
-bool
-ExportSVG::SetFill(FillDEF *fill)
-{
-	int iL; 
-
-	if(!fill) return false;
-	if((fill->type & 0xff) != FILL_NONE) {
-		if(!hgo) hgo = new HatchOut(this);
-		if(hgo) hgo->SetFill(fill);
-		if(fill->hatch) {
-			if(1 >(iL  = iround(un2fix(fill->hatch->width)))) iL = 1;
-#ifdef USE_WIN_SECURE
-			sprintf_s(tHatchStyle, 80, "style=\"fill:none; stroke:%s; stroke-width:%d\"",
-				ColName(fill->hatch->color), iL);
-#else
-			sprintf(tHatchStyle, "style=\"fill:none; stroke:%s; stroke-width:%d\"",
-				ColName(fill->hatch->color), iL);
-#endif
-			}
-		}
-	else {
-		if(hgo) delete hgo;
-		hgo = 0L;
-		}
-	dFillCol = fill->color;
-	dFillCol2 = fill->color2;
-	return true;
-}
-
-bool
-ExportSVG::SetTextSpec(TextDEF *set)
-{
-	if(set->fSize > 0.0) {
-		if((set->Style & TXS_SUPER) || (set->Style & TXS_SUB))
-			set->iSize = un2iy(set->fSize * 0.71);
-		else set->iSize = un2iy(set->fSize);
-		}
-	if(!set->iSize) return false;
-	return anyOutput::SetTextSpec(set);
-}
-
-bool 
-ExportSVG::StartPage()
-{
-	int w, h;
-
-	if(!go) return false;
-	w = un2ix(go->GetSize(SIZE_GRECT_RIGHT) - go->GetSize(SIZE_GRECT_LEFT))/10;
-	h = un2iy(go->GetSize(SIZE_GRECT_BOTTOM) - go->GetSize(SIZE_GRECT_TOP))/10;
-	w++; h++;
-	if(flags & 0x01) add_to_buff(&out_buff, &out_pos, &out_size, "Content-Type: image/svg+xml\n\n", 0);
-	VPorg.fy = -un2fiy(go->GetSize(SIZE_GRECT_TOP));
-	VPorg.fx = -un2fix(go->GetSize(SIZE_GRECT_LEFT));
-	add_to_buff(&out_buff, &out_pos, &out_size, "<?xml version=\"1.0\"?>\n"
-		"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20001102//EN\"\n"
-		"   \"http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd\">\n<svg ", 0);
-	if(defs.svgAttr) add_to_buff(&out_buff, &out_pos, &out_size, defs.svgAttr, 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, " width=\"", 0);
-	add_int_to_buff(&out_buff, &out_pos, &out_size, w, false, 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "\" height=\"", 0);
-	add_int_to_buff(&out_buff, &out_pos, &out_size, h, false, 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "\" style=\"stroke-linecap:round\">\n", 0);
-	if(defs.svgScript) {
-		add_to_buff(&out_buff, &out_pos, &out_size, "<defs>\n<script type=\"text/ecmascript\"><![CDATA[\n\n", 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, defs.svgScript, 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, "\n\n]]></script>\n</defs>\n\n", 0);
-		}
-	add_to_buff(&out_buff, &out_pos, &out_size, "<g transform=\"scale(0.1)\" style=\"font-family:Helvetica\">\n", 0);
-	return true;
-}
-
-bool
-ExportSVG::EndPage()
-{
-	FILE *oFile;
-
-	add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n</svg>\n", 0);
-	if(name && out_buff && out_pos > 20) {
-#ifdef USE_WIN_SECURE
-		fopen_s(&oFile, name, "w");
-#else
-		oFile = fopen(name, "w");
-#endif
-		if(!oFile) {
-			ErrorBox("Could not open\noutput file!");
-			return false;
-			}
-		fprintf(oFile, "%s", out_buff);
-		free(out_buff);		fclose (oFile);
-		}
-	return true;
-}
-
-bool
-ExportSVG::oCircle(int x1, int y1, int x2, int y2, char* nam)
-{
-	if(x1 > x2) Swap(x1, x2);
-	if(y1 > y2) Swap(y1, y2);
-
-	if(hgo){
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, "<g>  <!-- ", 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, (x2-x1) == (y2-y1) ? (char*)"circle" : (char*)"ellipse", 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, " with pattern -->\n", 0);
-		Indent(true);
-		}
-	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-	add_to_buff(&out_buff, &out_pos, &out_size, (x2-x1) == (y2-y1) ? (char*)"<circle" : (char*)"<ellipse", 0);
-	if(nam && nam[0]) {
-		add_to_buff(&out_buff, &out_pos, &out_size, " name=\"", 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, nam, 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, "\"", 0);
-		}
-	add_to_buff(&out_buff, &out_pos, &out_size, " cx=\"", 0);
-	add_int_to_buff(&out_buff, &out_pos, &out_size, (x1+x2)>>1, false, 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "\" cy=\"", 0);
-	add_int_to_buff(&out_buff, &out_pos, &out_size, (y1+y2)>>1, false, 0);
-	if((x2-x1)==(y2-y1)) {
-		add_to_buff(&out_buff, &out_pos, &out_size, "\" r=\"", 0);
-		add_int_to_buff(&out_buff, &out_pos, &out_size, (x2-x1)>>1, false, 0);
-		}
-	else {
-		add_to_buff(&out_buff, &out_pos, &out_size, "\" rx=\"", 0);
-		add_int_to_buff(&out_buff, &out_pos, &out_size, (x2-x1)>>1, false, 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, "\" ry=\"", 0);
-		add_int_to_buff(&out_buff, &out_pos, &out_size, (y2-y1)>>1, false, 0);
-		}
-	add_to_buff(&out_buff, &out_pos, &out_size, "\" style=\"fill:", 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, ColName(dFillCol), 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "; stroke:", 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, ColName(dLineCol), 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-width:", 0);
-	add_int_to_buff(&out_buff, &out_pos, &out_size, iLineWidth, false, 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "\"/>\n", 0);
-	if(hgo) {
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, "<g ", 3);
-		add_to_buff(&out_buff, &out_pos, &out_size, tHatchStyle, 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, ">  <!-- hatch -->\n", 0);
-		Indent(true);		bUseGroupLine = true;
-		hgo->oCircle(x1, y1, x2, y2);
-		Indent(false);		bUseGroupLine = false;
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 0);
-		Indent(false);
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 0);
-		}
-	return true;
-}
-
-bool
-ExportSVG::oPolyline(POINT *pts, int cp, char *nam)
-{
-	int i, cb;
-	char tmptxt[120];
-
-	if(cp < 2) return false;
-	if (dPattern){
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, "<g style=\"stroke:", 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, ColName(dLineCol), 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-width:", 0);
-		add_int_to_buff(&out_buff, &out_pos, &out_size, iLineWidth, false, 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-linecap:round\"><!-- pattern line -->\n", 0);
-		Indent(true);			bUseGroupLine = true;
-		for (i = 1; i < cp; i++) PatLine(pts[i-1], pts[i]);
-		Indent(false);
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 0);
-		bUseGroupLine = false;
-		}
-	else {
-		if(cp == 2) return oSolidLine(pts);
-		bOutputPending = false;
-		cb_out = rlp_strcpy(output, 20,"<polyline points=\""); 
-		for(i = 0; i < cp; i++) {
-#ifdef USE_WIN_SECURE
-			cb = sprintf_s(tmptxt, 120, "%d %d ", pts[i].x, pts[i].y);
-#else
-			cb = sprintf(tmptxt, "%d %d ", pts[i].x, pts[i].y);
-#endif
-			AddToOutput(tmptxt, cb);
-			}
-		if(cb_out) output[cb_out-1] = '"';
-		if(!bUseGroupLine) {
-			cb = rlp_strcpy(tmptxt, 120, " style = \"fill:none; ");
-			AddToOutput(tmptxt, cb);
-			cb = rlp_strcpy(tmptxt, 120, "stroke:");	//bug fixed by vefremov
-			cb += rlp_strcpy(tmptxt+cb, 120-cb, ColName(dLineCol));
-			cb += rlp_strcpy(tmptxt+cb, 120-cb, "; ");
-			AddToOutput(tmptxt, cb);
-#ifdef USE_WIN_SECURE
-			cb = sprintf_s(tmptxt, 120, "stroke-width:%d\"/>",iLineWidth);
-#else
-			cb = sprintf(tmptxt, "stroke-width:%d\"/>",iLineWidth);
-#endif
-			AddToOutput(tmptxt, cb);
-			}
-		else AddToOutput("/>", 2);
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, "\n", 1);
-		if(bOutputPending)Indent(false);
-		}
-	return true;
-}
-
-bool
-ExportSVG::oRectangle(int x1, int y1, int x2, int y2, char *nam)
-{
-	if(x1 > x2) Swap(x1, x2);
-	if(y1 > y2) Swap(y1, y2);
-	if(hgo){
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, "<g>  <!-- rectangle with pattern -->\n", 0);
-		Indent(true);
-		}
-	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-	add_to_buff(&out_buff, &out_pos, &out_size, "<rect", 5);
-	if(nam && nam[0]) {
-		add_to_buff(&out_buff, &out_pos, &out_size, " name=\"", 7);
-		add_to_buff(&out_buff, &out_pos, &out_size, nam, 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, "\"", 1);
-		}
-	add_to_buff(&out_buff, &out_pos, &out_size, " x=\"", 4);
-	add_int_to_buff(&out_buff, &out_pos, &out_size, x1, false, 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "\" y=\"", 5);
-	add_int_to_buff(&out_buff, &out_pos, &out_size, y1, false, 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "\" width=\"", 9);
-	add_int_to_buff(&out_buff, &out_pos, &out_size, x2-x1-1, false, 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "\" height=\"", 10);
-	add_int_to_buff(&out_buff, &out_pos, &out_size, y2-y1-1, false, 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "\" style=\"fill:", 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, ColName(dFillCol), 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "; stroke:", 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, ColName(dLineCol), 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-width:", 0);
-	add_int_to_buff(&out_buff, &out_pos, &out_size, iLineWidth, false, 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "\"/>\n", 0);
-	if(hgo) {
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, "<g ", 3);
-		add_to_buff(&out_buff, &out_pos, &out_size, tHatchStyle, 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, ">  <!-- hatch -->\n", 0);
-		Indent(true);				bUseGroupLine = true;
-		hgo->oRectangle(x1, y1, x2, y2, 0L);
-		Indent(false);				bUseGroupLine = false;
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 5);
-		Indent(false);
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 5);
-		}
-	return true;
-}
-
-bool
-ExportSVG::oSolidLine(POINT *p)
-{
-	if(!p) return false;
-	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-	add_to_buff(&out_buff, &out_pos, &out_size, "<line x1=\"", 0);
-	add_int_to_buff(&out_buff, &out_pos, &out_size, p[0].x, false, 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "\" y1=\"", 6);
-	add_int_to_buff(&out_buff, &out_pos, &out_size, p[0].y, false, 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "\" x2=\"", 6);
-	add_int_to_buff(&out_buff, &out_pos, &out_size, p[1].x, false, 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "\" y2=\"", 6);
-	add_int_to_buff(&out_buff, &out_pos, &out_size, p[1].y, false, 0);
-	if(!bUseGroupLine) {
-		add_to_buff(&out_buff, &out_pos, &out_size, "\" style=\"stroke:", 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, ColName(dLineCol), 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-width:", 0);
-		add_int_to_buff(&out_buff, &out_pos, &out_size, iLineWidth, false, 0);
-		}
-	add_to_buff(&out_buff, &out_pos, &out_size, "\"/>\n", 4);
-	return true;
-}
-
-bool
-ExportSVG::com_TextOut(int x, int y, char *txt, int cb)
-{
-	int c, h, ix, iy, dy;
-	char tmptxt[120];
-
-	if(!txt || !txt[0]) return false;
-	else h = TxtSet.iSize;
-	if(TxtSet.Align & TXA_VCENTER) iy = y + h/3;
-	else if(TxtSet.Align & TXA_VBOTTOM) iy = y;
-	else iy = y + iround(h * 0.8);
-	ix = x;		dy = 0;
-	if(TxtSet.Style & TXS_SUB) {
-		if((TxtSet.Align & TXA_VCENTER) == TXA_VCENTER) dy = un2iy(TxtSet.fSize*0.4);
-		else if((TxtSet.Align & TXA_VBOTTOM) == TXA_VBOTTOM) dy = un2iy(TxtSet.fSize*0.2);
-		else if((TxtSet.Align & TXA_VTOP) == TXA_VTOP) dy = un2iy(TxtSet.fSize*.6);
-		}
-	else if(TxtSet.Style & TXS_SUPER) {
-		if((TxtSet.Align & TXA_VCENTER) == TXA_VCENTER) dy = -un2iy(TxtSet.fSize*0.4);
-		else if((TxtSet.Align & TXA_VBOTTOM) == TXA_VBOTTOM) dy = -un2iy(TxtSet.fSize*0.6);
-		else if((TxtSet.Align & TXA_VTOP) == TXA_VTOP) dy = -un2iy(TxtSet.fSize*0.2);
-		}
-#ifdef USE_WIN_SECURE
-	cb_out = sprintf_s(output, 120, "<text x=\"%d\" y=\"%d\" dy=\"%d\" ", ix, iy, dy);
-#else
-	cb_out = sprintf(output, "<text x=\"%d\" y=\"%d\" dy=\"%d\" ", ix, iy, dy);
-#endif
-	if(fabs(TxtSet.RotBL) >.01 || fabs(TxtSet.RotCHAR) >.01) {
-#ifdef USE_WIN_SECURE
-		cb_out += sprintf_s(output+cb_out, 120-cb_out, "transform=\"rotate(%.0f,%d,%d)\" ", -TxtSet.RotBL, ix, iy);
-#else
-		cb_out += sprintf(output+cb_out,"transform=\"rotate(%.0f,%d,%d)\" ", -TxtSet.RotBL, ix, iy);
-#endif
-		}
-	c = rlp_strcpy(tmptxt, 140, "style=\"font-family:");
-	switch(TxtSet.Font) {
-	case FONT_TIMES:	c += rlp_strcpy(tmptxt+c, 120-c, "Times;");		break;
-	case FONT_COURIER:	c += rlp_strcpy(tmptxt+c, 120-c, "Courier;");	break;
-	default:			c += rlp_strcpy(tmptxt+c, 120-c, "Helvetica;");	break;
-		}
-	if(TxtSet.Style & TXS_ITALIC) c += rlp_strcpy(tmptxt+c, 120-c, " font-style:italic;");
-	if(TxtSet.Style & TXS_BOLD) c += rlp_strcpy(tmptxt+c, 120-c, " font-weight:bold;");
-	if(TxtSet.Style & TXS_UNDERLINE) c += rlp_strcpy(tmptxt+c, 120-c, " text-decoration:underline;");
-	AddToOutput(tmptxt, c);
-#ifdef USE_WIN_SECURE
-	c = sprintf_s(tmptxt, 120, " fill:%s; stroke:%s; ", ColName(TxtSet.ColTxt), ColName(TxtSet.ColTxt));
-#else
-	c = sprintf(tmptxt, " fill:%s; stroke:%s; ", ColName(TxtSet.ColTxt), ColName(TxtSet.ColTxt));
-#endif
-	AddToOutput(tmptxt, c);
-#ifdef USE_WIN_SECURE
-	c = sprintf_s(tmptxt, 120, "font-size:%d; text-anchor:%s \">", h, 
-		(TxtSet.Align & TXA_HRIGHT) ? "end" : (TxtSet.Align & TXA_HCENTER) ? "middle":"start");
-#else
-	c = sprintf(tmptxt, "font-size:%d; text-anchor:%s \">", h, 
-		(TxtSet.Align & TXA_HRIGHT) ? "end" : (TxtSet.Align & TXA_HCENTER) ? "middle":"start");
-#endif
-	AddToOutput(tmptxt, c);
-	if((cb_ind+strlen(txt)+cb_out) <110) cb_out += rlp_strcpy(output+cb_out, 120-cb_out, txt);
-	else {
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, "\n", 1);
-		cb_out=rlp_strcpy(output, 120, txt);
-		}
-	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-	add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
-	if((cb_ind + cb_out) <104) {
-		add_to_buff(&out_buff, &out_pos, &out_size, "</text>\n", 0);
-		}
-	else add_to_buff(&out_buff, &out_pos, &out_size, "\n</text>\n", 0);
-	return true;
-}
-
-bool
-ExportSVG::oTextOut(int x, int y, char *txt, int cb)
-{
-	char *nt;
-
-	if(!txt || !txt[0]) return false;
-	nt = str2xml(txt, TxtSet.Font == FONT_GREEK);
-	return com_TextOut(x, y, nt, cb);
-}
-
-bool
-ExportSVG::oTextOutW(int x, int y, w_char *txt, int cb)
-{
-	int i, j;
-	wchar_t wc;
-	char c;
-
-	for(i = j = 0; txt[i]; i++) {
-		switch(txt[i]) {
-			case '"':
-				j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, """);
-				break;
-			case '&':
-				j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, "&");
-				break;
-			case '<':
-				j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, "<");
-				break;
-			case '>':
-				j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, ">");
-				break;
-			default:
-				if(txt[i] > 255) {
-#ifdef USE_WIN_SECURE
-					j += sprintf_s(TmpTxt+j, TMP_TXT_SIZE-j, "&#%d;", txt[i]);
-#else
-					j += sprintf(TmpTxt+j, "&#%d;", txt[i]);
-#endif
-					}
-				else if(txt[i] > 127) {
-					c = (char)txt[i];
-					if(mbtowc(&wc, &c, 1) >0) 
-#ifdef USE_WIN_SECURE
-						j += sprintf_s(TmpTxt+j, TMP_TXT_SIZE-j, "&#%d;", ((unsigned short)wc));
-#else
-						j += sprintf(TmpTxt+j, "&#%d;", ((unsigned short)wc));
-#endif
-					}
-				else TmpTxt[j++] = (char)txt[i];
-				break;
-			}
-		}
-	TmpTxt[j++] = 0;
-	return com_TextOut(x, y, TmpTxt, j-1);
-}
-
-bool
-ExportSVG::oPolygon(POINT *pts, int cp, char *nam)
-{
-	int i, cb;
-	char tmptxt[40];
-
-	if(cp <3) return false;
-	if(hgo){
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, "<g>  <!-- polygon with pattern -->\n", 0);
-		Indent(true);
-		}
-	bOutputPending = false;
-#ifdef USE_WIN_SECURE
-	cb_out = sprintf_s(output, 120, "<polygon%s%s%s points=\"",
-		nam? " name=\"" : "", nam ? nam : "", nam ? "\"" : ""); 
-#else
-	cb_out = sprintf(output, "<polygon%s%s%s points=\"",
-		nam? " name=\"" : "", nam ? nam : "", nam ? "\"" : ""); 
-#endif
-	for(i = 0; i < cp; i++) {
-#ifdef USE_WIN_SECURE
-		cb = sprintf_s(tmptxt, 40, "%d %d ", pts[i].x, pts[i].y);
-#else
-		cb = sprintf(tmptxt, "%d %d ", pts[i].x, pts[i].y);
-#endif
-		AddToOutput(tmptxt, cb);
-		}
-	if(cb_out) output[cb_out-1] = '"';
-#ifdef USE_WIN_SECURE
-	cb = sprintf_s(tmptxt, 40, " style=\"fill:%s; ", ColName(dFillCol));
-	AddToOutput(tmptxt, cb);
-	cb = sprintf_s(tmptxt, 40, "stroke:%s; ", ColName(dLineCol));
-	AddToOutput(tmptxt, cb);
-	cb = sprintf_s(tmptxt, 40, "stroke-width:%d\"/>",iLineWidth);
-	AddToOutput(tmptxt, cb);
-#else
-	cb = sprintf(tmptxt, " style=\"fill:%s; ", ColName(dFillCol));
-	AddToOutput(tmptxt, cb);
-	cb = sprintf(tmptxt, "stroke:%s; ", ColName(dLineCol));
-	AddToOutput(tmptxt, cb);
-	cb = sprintf(tmptxt, "stroke-width:%d\"/>",iLineWidth);
-	AddToOutput(tmptxt, cb);
-#endif
-	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-	add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
-	add_to_buff(&out_buff, &out_pos, &out_size, "\n", 1);
-	if(bOutputPending)Indent(false);
-	if(hgo) {
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, "<g ", 3);
-		add_to_buff(&out_buff, &out_pos, &out_size, tHatchStyle, 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, ">  <!-- hatch -->\n", 0);
-		Indent(true);				bUseGroupLine = true;
-		hgo->oPolygon(pts, cp);		Indent(false);
-		bUseGroupLine = false;
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 5);
-		Indent(false);
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 5);
-		}
-	return true;
-}
-
-void
-ExportSVG::Indent(bool ind)
-{
-	if(ind) {
-		if(cb_ind < 20) cb_ind += 3;
-		}
-	else {
-		if(cb_ind > 5) cb_ind -= 3;
-		}
-}
-
-void
-ExportSVG::AddToOutput(char *txt, int len)
-{
-	if(!txt || !txt[0]) return;
-	if(!len) len = (int)strlen(txt);
-	if((len + cb_out + cb_ind) < 110){
-		cb_out += rlp_strcpy(output+cb_out, 120, txt);
-		}
-	else {
-		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
-		add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
-		add_to_buff(&out_buff, &out_pos, &out_size, "\n", 1);
-		if(!bOutputPending) Indent(true);
-		bOutputPending = true;
-		cb_out = rlp_strcpy(output, 120, txt);
-		}
-}
-
-char *
-ExportSVG::ColName(DWORD col)
-{
-	static char txt1[20], txt2[20];
-	static int sw;
-	char *ret;
-
-	switch(col) {
-	case 0x00000000:			return "black";
-	case 0x000000ff:			return "red";
-	case 0x0000ff00:			return "lime";
-	case 0x0000ffff:			return "yellow";
-	case 0x00ff0000:			return "blue";
-	case 0x00ff00ff:			return "magenta";
-	case 0x00ffff00:			return "cyan";
-	case 0x00ffffff:			return "white";
-		}
-	sw++;
-#ifdef USE_WIN_SECURE
-	sprintf_s(ret = (sw & 0x01 ? txt1 : txt2), 20, "rgb(%d,%d,%d)", col & 0xff, (col>>8) & 0xff, (col>>16) &0xff);
-#else
-	sprintf(ret = (sw & 0x01 ? txt1 : txt2), "rgb(%d,%d,%d)", col & 0xff, (col>>8) & 0xff, (col>>16) &0xff);
-#endif
-	return ret;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Entry point to export graph to *.svg
-void DoExportSvg(GraphObj *g, char *FileName, DWORD flags)
-{
-	ExportSVG *ex;
-	
-	ex = new ExportSVG(g, FileName, flags);
-	if(ex->StartPage()) {
-		g->DoPlot(ex);
-		ex->EndPage();
-		}
-	HideTextCursor();	
-	delete(ex);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// export to *.eps file (encapsulated post script).
-// This code is based on information from the following book
-// G. Born, 'Referenzhandbuch Dateiformate', 
-//     Addison-Wesley ISBN 3-89319-815-6
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class ExportEPS:public anyOutput {
-public:
-	HatchOut *hgo;
-
-	ExportEPS(GraphObj *g, char *FileName, DWORD flags);
-	~ExportEPS();
-	bool SetLine(LineDEF *lDef);
-	bool SetFill(FillDEF *fill);
-	bool SetTextSpec(TextDEF *set);
-	bool StartPage();
-	bool EndPage();
-	bool oCircle(int x1, int y1, int x2, int y2, char* nam = 0L);
-	bool oPolyline(POINT * pts, int cp, char *nam = 0L);
-	bool oRectangle(int x1, int y1, int x2, int y2, char *nam = 0L);
-	bool oSolidLine(POINT *p);
-	bool oTextOut(int x, int y, char *txt, int cb);
-	bool oPolygon(POINT *pts, int cp, char *nam = 0L);
-
-private:
-	RECT BoundBox;
-	fRECT SolLines[2000];
-	int nSolLines;
-	GraphObj *go;
-	char *name, FontName[30];
-	FILE *oFile;
-	DWORD CurrCol;
-	bool bFontChange;
-
-	float ix2eps(int x);
-	float iy2eps(int y);
-	char *col2eps(DWORD color);
-	void FlushSolLines();
-	void AddSolLine(float x1, float y1, float x2, float y2);
-};
-
-ExportEPS::ExportEPS(GraphObj *g, char *FileName, DWORD flags)
-{
-	hgo =0L;		nSolLines = 0;			DeskRect.left = DeskRect.top = 0;
-	DeskRect.right = DeskRect.bottom = 0x4fffffff;
-	dFillCol = 0xffffffffL;					hres = vres = 720.0f;
-	go = g;			bFontChange = false;	oFile = 0L;
-	if(FileName)name = (char*)memdup(FileName, (int)strlen(FileName)+1,0);
-	else name = 0L;
-}
-
-ExportEPS::~ExportEPS()
-{
-	if(hgo) delete hgo;
-	if(name) free(name);
-}
-
-bool
-ExportEPS::SetLine(LineDEF *lDef)
-{
-	if(LineWidth != lDef->width || dLineCol != lDef->color) {
-		FlushSolLines();
-		LineWidth = lDef->width;
-		CurrCol = dLineCol = lDef->color;
-		fprintf(oFile, "\nnewpath %.1f setlinewidth %s ",
-			un2fix(LineWidth)/10.0f, col2eps(dLineCol));
-		}
-	dPattern = lDef->pattern;
-	RLP.finc = (float)(256.0/un2fix(lDef->patlength*8.0));
-	RLP.fp = 0.0;
-	return true;
-}
-
-bool
-ExportEPS::SetFill(FillDEF *fill)
-{
-	if(!fill) return false;
-	if((fill->type & 0xff) != FILL_NONE) {
-		if(!hgo) hgo = new HatchOut(this);		if(hgo) hgo->SetFill(fill);
-		}
-	else {
-		if(hgo) delete hgo;						hgo = 0L;
-		}
-	dFillCol = fill->color;						dFillCol2 = fill->color2;
-	return true;
-}
-
-bool
-ExportEPS::SetTextSpec(TextDEF *set)
-{
-	int cb;
-
-	if(set->fSize > 0.0) {
-		if((set->Style & TXS_SUPER) || (set->Style & TXS_SUB)) set->iSize = un2iy(set->fSize * 0.71);
-		else set->iSize = un2iy(set->fSize);
-		}
-	if(!set->iSize) return false;
-	anyOutput::SetTextSpec(set);
-	switch(TxtSet.Font) {
-	case FONT_TIMES:	cb = rlp_strcpy(FontName, 30, "(Times");		break;		//Serif
-	case FONT_COURIER:	cb = rlp_strcpy(FontName, 30, "(Courier");		break;		//fixed spaced
-	default:			cb = rlp_strcpy(FontName, 30, "(Helvetica");	break;		//Sans Serif	
-		}
-	if(TxtSet.Style & TXS_BOLD) cb += rlp_strcpy(FontName+cb, 30-cb, "-Bold");
-	if(TxtSet.Style & TXS_ITALIC) cb += rlp_strcpy(FontName+cb, 30-cb, "-Italic");
-	cb += rlp_strcpy(FontName+cb, 30-cb, ")");		bFontChange = true;
-	return true;
-}
-
-bool 
-ExportEPS::StartPage()
-{
-	time_t ti;
-	
-	if(!go) return false;
-	ti = time(0L);
-	BoundBox.top = BoundBox.left = 0;
-	BoundBox.right = un2ix(go->GetSize(SIZE_GRECT_RIGHT) - go->GetSize(SIZE_GRECT_LEFT))/10;
-	BoundBox.bottom = un2iy(go->GetSize(SIZE_GRECT_BOTTOM) - go->GetSize(SIZE_GRECT_TOP))/10;
-	BoundBox.right++;	BoundBox.bottom++;
-	if(name) {
-#ifdef USE_WIN_SECURE
-		fopen_s(&oFile, name, "w");
-#else
-		oFile = fopen(name, "w");
-#endif
-		if(!oFile) {
-			ErrorBox("Could not open\noutput file!");
-			return false;
-			}
-		}
-	else oFile = stdout;
-	VPorg.fy = -un2fiy(go->GetSize(SIZE_GRECT_TOP));
-	VPorg.fx = -un2fix(go->GetSize(SIZE_GRECT_LEFT));
-	fprintf(oFile, "%%!PS-Adobe-1.0 EPSF-3.0\n"
-		"%%%%BoundingBox: %d %d %d %d\n", BoundBox.left, BoundBox.top,
-		BoundBox.right, BoundBox.bottom);
-	fprintf(oFile, "%%%%Title: %s\n", name);
-	fprintf(oFile,"%%%%Creator: RLPlot version "SZ_VERSION"\n");
-#ifdef USE_WIN_SECURE
-	ctime_s(TmpTxt, 50, &ti);	TmpTxt[24] = 0;
-	fprintf(oFile,"%%%%CreationDate: %s", TmpTxt);
-#else
-	fprintf(oFile,"%%%%CreationDate: %s", ctime(&ti));
-#endif
-	fprintf(oFile, "%%%%Pages: 1\n%%%%DocumentFonts: (atend)\n");
-	fprintf(oFile, "%%%%EndComments\n"
-		"%%%%BeginProlog\n"
-		"%%%%EndProlog\n"
-		"%%%%Page: 1 1");
-	return true;
-}
-
-bool
-ExportEPS::EndPage()
-{
-	fprintf(oFile, "\nshowpage\n%%%%Trailer\n");
-	fprintf(oFile, "%%%%DocumentFonts: Helvetica\n");
-	fprintf(oFile, "%%%%EOF\n");
-	fclose (oFile);
-	return true;
-}
-
-bool
-ExportEPS::oCircle(int x1, int y1, int x2, int y2, char* nam)
-{
-	FlushSolLines();
-	if((x2 - x1) == (y2 -y1)) {
-		fprintf(oFile, "\nnewpath %.1f %.1f %.1f 0 360 arc ", ix2eps((x1+x2)/2), 
-			iy2eps((y1+y2)/2), (float)(x2-x1)/20.0f);
-		fprintf(oFile, "%s fill", col2eps(CurrCol = dFillCol));
-		if(hgo){
-			hgo->oCircle(x1, y1, x2, y2);
-			FlushSolLines();
-			}
-		fprintf(oFile, "\nnewpath %.1f %.1f %.1f 0 360 arc ", ix2eps((x1+x2)/2), 
-			iy2eps((y1+y2)/2), (float)(x2-x1)/20.0f);
-		fprintf(oFile, "%s stroke", col2eps(CurrCol = dLineCol));
-		}
-	else if(x1 != x2 && y1 != y2){
-		fprintf(oFile, "\ngsave %.1f %.1f translate", (ix2eps((x1+x2)>>1)),
-			(iy2eps((y1+y2)>>1)));
-		if(abs(x2-x1) > abs(y2-y1)) {
-			fprintf(oFile, " 1 %lf scale", fabs(((double)(y2-y1))/((double)(x2-x1))));
-			fprintf(oFile, " 0 0 %.1lf 0 360 arc ", fabs(((double)(x2-x1))/20.0));
-			fprintf(oFile, "%s fill", col2eps(CurrCol = dFillCol));
-			fprintf(oFile, "\n 0 0 %.1lf 0 360 arc ", fabs(((double)(x2-x1))/20.0));
-			}
-		else {
-			fprintf(oFile, " %lf 1 scale", fabs(((double)(x2-x1))/((double)(y2-y1))));
-			fprintf(oFile, " 0 0 %.1lf 0 360 arc ", fabs(((double)(y2-y1))/20.0));
-			fprintf(oFile, "%s fill", col2eps(CurrCol = dFillCol));
-			fprintf(oFile, "\n 0 0 %.1lf 0 360 arc ", fabs(((double)(y2-y1))/20.0));
-			}
-		fprintf(oFile, "%s stroke grestore", col2eps(CurrCol = dLineCol));
-		if(hgo){
-			hgo->oCircle(x1, y1, x2, y2);
-			FlushSolLines();
-			}
-		}
-	return true;
-}
-
-bool
-ExportEPS::oPolyline(POINT * pts, int cp, char *nam)
-{
-	int i, j;
-
-	if(cp <1) return false;
-	if (dPattern){
-		for (i = 1; i < cp; i++) PatLine(pts[i-1], pts[i]);
-		return true;
-		}
-	else if(cp == 2) return oSolidLine(pts);
-	FlushSolLines();
-	if(CurrCol != dLineCol) {
-		fprintf(oFile, "\n%s ", col2eps(CurrCol = dLineCol));
-		}
-	for(i = cp-1, j = 0; i >= 0; i--, j++) {
-		if(!(j & 0x07)) fprintf(oFile, "\n");
-		fprintf(oFile, " %.1f %.1f", ix2eps(pts[i].x), iy2eps(pts[i].y));
-		}
-	fprintf(oFile, " moveto %d {lineto} repeat stroke ", cp-1);
-	return true;
-}
-
-
-bool
-ExportEPS::oRectangle(int x1, int y1, int x2, int y2, char *nam)
-{
-	FlushSolLines();
-	fprintf(oFile, "\n%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f moveto 4 {lineto}"
-		" repeat %s fill", ix2eps(x1), iy2eps(y1), ix2eps(x1), iy2eps(y2), ix2eps(x2), iy2eps(y2), 
-		ix2eps(x2), iy2eps(y1), ix2eps(x1), iy2eps(y1), col2eps(CurrCol = dFillCol));
-	if(hgo) hgo->oRectangle(x1, y1, x2, y2, 0L);
-	fprintf(oFile, "\n%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f moveto 4 {lineto}"
-		" repeat %s stroke", ix2eps(x1), iy2eps(y1), ix2eps(x1), iy2eps(y2), ix2eps(x2), iy2eps(y2), 
-		ix2eps(x2), iy2eps(y1), ix2eps(x1), iy2eps(y1), col2eps(CurrCol = dLineCol));
-	return true;
-}
-
-bool
-ExportEPS::oSolidLine(POINT *p)
-{
-	if(CurrCol != dLineCol) {
-		FlushSolLines();
-		fprintf(oFile, "\n%s ", col2eps(CurrCol = dLineCol));
-		}
-	AddSolLine(ix2eps(p[0].x), iy2eps(p[0].y), ix2eps(p[1].x), iy2eps(p[1].y));
-	return true;
-}
-
-bool
-ExportEPS::oTextOut(int x, int y, char *txt, int cb)
-{
-	int h, ix, iy, w;
-	float fx, fy, lw;
-
-	FlushSolLines();	if(!txt || !txt[0]) return true;
-	oGetTextExtent(txt, cb, &w, &h);
-	if(bFontChange)	{
-		fprintf(oFile, "\n%s findfont %d scalefont setfont ", FontName, TxtSet.iSize/10);
-		bFontChange = false;
-		}
-	if(TxtSet.Align & TXA_VCENTER) iy = y + h/3;
-	else if(TxtSet.Align & TXA_VBOTTOM) iy = y;
-	else iy = y + iround(h*.8);
-	if(TxtSet.Align & TXA_HRIGHT) ix = x-w;
-	else if(TxtSet.Align & TXA_HCENTER) ix = x-w/2;
-	else ix = x;			lw = (float)(iy-y)/150.0f;
-	fprintf(oFile,"\n");
-	if(fabs(TxtSet.RotBL) >.01 || fabs(TxtSet.RotCHAR) >.01) {
-		if(TxtSet.Style & TXS_SUB) iy += un2iy(TxtSet.fSize*0.6);
-		else if(TxtSet.Style & TXS_SUPER) iy -= un2iy(TxtSet.fSize*.2);
-		fprintf(oFile, "gsave %.1f %.1f translate %f rotate %.1f %.1f moveto\n",
-			ix2eps(x), iy2eps(y), TxtSet.RotBL, (float)(ix-x)/10.0f, (float)(iy-y)/-10.0f);
-		fprintf(oFile, "%s ", col2eps(CurrCol = TxtSet.ColTxt));
-		fprintf(oFile, "(%s) show ", txt);
-		if(TxtSet.Style & TXS_UNDERLINE) {
-			fprintf(oFile, "\ncurrentpoint %.1f exch pop moveto", (float)(iy-y)/-10.0f - lw*1.2);
-			fprintf(oFile, " 0 %.1f lineto %s %.1f setlinewidth stroke ", (float)(iy-y)/-10.0f -lw*1.2,
-				col2eps(TxtSet.ColTxt), lw);
-			}
-		fprintf(oFile, "grestore\n");
-		}
-	else {
-		if(TxtSet.Style & TXS_SUB) iy += un2iy(TxtSet.fSize*0.6);
-		else if(TxtSet.Style & TXS_SUPER) iy -= un2iy(TxtSet.fSize*.2);
-		fx = ix2eps(ix);			fy = iy2eps(iy);
-		fprintf(oFile, "%s ", col2eps(CurrCol = TxtSet.ColTxt));
-		fprintf(oFile,"%.1f %.1f moveto (%s) show ", fx, fy, txt);
-		if(TxtSet.Style & TXS_UNDERLINE) {
-			fprintf(oFile, "\ncurrentpoint %.1f exch pop moveto", fy - lw*1.2);
-			fprintf(oFile, " %.1f %.1f lineto %s %.1f setlinewidth stroke\n", fx, fy - lw*1.2, 
-				col2eps(TxtSet.ColTxt), lw);
-			}
-		}
-	return true;
-}
-
-bool
-ExportEPS::oPolygon(POINT *pts, int cp, char *nam)
-{
-	int i, j;
-
-	if(cp <1) return false;
-	if(cp == 2) return oSolidLine(pts);
-	FlushSolLines();
-	for(i = cp-1, j = 0; i >= 0; i--, j++) {
-		if(!(j & 0x07)) fprintf(oFile, "\n");
-		fprintf(oFile, " %.1f %.1f", ix2eps(pts[i].x), iy2eps(pts[i].y));
-		}
-	fprintf(oFile, " moveto %d {lineto} repeat %s fill ", cp-1, col2eps(CurrCol = dFillCol));
-	if(hgo) hgo->oPolygon(pts, cp);
-	return oPolyline(pts, cp);
-}
-
-float
-ExportEPS::ix2eps(int x)
-{
-	return (float)x/10.0f;
-}
-
-float
-ExportEPS::iy2eps(int y)
-{
-	return (float)y/-10.0f + (float)BoundBox.bottom;
-}
-
-char *
-ExportEPS::col2eps(DWORD color)
-{
-	static char txt[50];
-	float r, g, b;
-
-	r = (float)(color & 0xff)/255.0f;
-	g = (float)((color>>8)&0xff)/255.0f;
-	b = (float)((color>>16)&0xff)/255.0f;
-#ifdef USE_WIN_SECURE
-	sprintf_s(txt, 50, "%g %g %g setrgbcolor", r, g, b);
-#else
-	sprintf(txt, "%g %g %g setrgbcolor", r, g, b);
-#endif
-	return txt;
-}
-
-void
-ExportEPS::FlushSolLines()
-{
-	int i, j;
-	
-	if(nSolLines <1) {
-		nSolLines = 0;
-		return;
-		}
-	if(nSolLines == 1) {
-		fprintf(oFile, "\n%.1f %.1f moveto %.1f %.1f lineto stroke ", 
-			SolLines[0].Ymin, SolLines[0].Ymax, SolLines[0].Xmin, SolLines[0].Xmax);
-		nSolLines = 0;
-		return;
-		}
-	for(i = nSolLines-1, j = 0; i >=0; i--, j++) {
-		if(!(j & 0x03)) fprintf(oFile, "\n");
-		fprintf(oFile, " %.1f %.1f %.1f %.1f", SolLines[i].Xmin, SolLines[i].Xmax,
-			SolLines[i].Ymin, SolLines[i].Ymax);
-		}
-	if(j > 8 && ((j & 0x3) >=2 || (j & 0x03) == 0)) fprintf(oFile, "\n");
-	fprintf(oFile, " %d {moveto lineto} repeat stroke ", nSolLines);
-	nSolLines = 0;
-}
-
-void
-ExportEPS::AddSolLine(float x1, float y1, float x2, float y2)
-{
-	if(nSolLines >= 2000) FlushSolLines();
-	SolLines[nSolLines].Ymin = x1;	SolLines[nSolLines].Ymax = y1;
-	SolLines[nSolLines].Xmin = x2;	SolLines[nSolLines].Xmax = y2;
-	nSolLines++;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Entry point to export graph to *.eps
-void DoExportEps(GraphObj *g, char *FileName, DWORD flags)
-{
-	ExportEPS *ex;
-
-	ex = new ExportEPS(g, FileName, flags);
-	if(ex->StartPage()) {
-		g->DoPlot(ex);
-		ex->EndPage();
-		}
-	delete(ex);
-}
+//Export.cpp, Copyright (c) 2002-2008 R.Lackner
+//export graph files
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+#include "rlplot.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#include <fcntl.h>				//file open flags
+#include <sys/stat.h>			//I/O flags
+#ifdef USE_WIN_SECURE
+	#include <share.h>			//I/O flags
+#endif
+
+#ifdef _WINDOWS
+	#include <io.h>					//for read/write
+#else
+	#define O_BINARY 0x0
+	#include <unistd.h>
+#endif
+
+extern char TmpTxt[];
+extern Default defs;
+static char *str_ind = "                              ";
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Entry point to export graph to Windows Meta File
+void DoExportWmf(GraphObj *g, char *FileName, float res, DWORD flags)
+{
+	InfoBox("The export of Windos metafile (*.wmf)\n has been dicontinued.\n\n");
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// export to *.svg file (scalable vector graphic)
+// this code is based on information from the following books
+// H. Spona, 'Das Einsteigerseminar SVG-Webgrafiken mit XML',
+//     vmi, ISBN 3-8266-7181-3
+// M. Salathe, 'SVG Scalabe Vector Graphics ...f�r professionelle Einsteiger',
+//     M&T, ISBN 3-8272-6188-0
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class ExportSVG:public anyOutput {
+public:
+	HatchOut *hgo;
+
+	ExportSVG(GraphObj *g, char *FileName, DWORD flags);
+	~ExportSVG();
+	bool SetLine(LineDEF *lDef);
+	bool SetFill(FillDEF *fill);
+	bool SetTextSpec(TextDEF *set);
+	bool StartPage();
+	bool EndPage();
+	bool oCircle(int x1, int y1, int x2, int y2, char* nam = 0L);
+	bool oPolyline(POINT * pts, int cp, char *nam = 0L);
+	bool oRectangle(int x1, int y1, int x2, int y2, char *nam = 0L);
+	bool oSolidLine(POINT *p);
+	bool oTextOut(int x, int y, char *txt, int cb);
+	bool oTextOutW(int x, int y, w_char *txt, int cb);
+	bool oPolygon(POINT *pts, int cp, char * nam = 0L);
+
+private:
+	int iLineWidth, cb_out, out_pos, out_size, cb_ind;
+	char *out_buff;
+	bool bUseGroupLine, bOutputPending;
+	GraphObj *go;
+	char *name, output[120], tHatchStyle[80];
+	DWORD flags;
+
+	bool com_TextOut(int x, int y, char *txt, int cb);
+	void Indent(bool ind);
+	void AddToOutput(char *txt, int len);
+	char *ColName(DWORD col);
+	char *Transparency(DWORD col, int type);
+};
+
+ExportSVG::ExportSVG(GraphObj *g, char *FileName, DWORD flg)
+{
+	hgo =0L;
+	DeskRect.left = DeskRect.top = 0;
+	DeskRect.right = DeskRect.bottom = 0x4fffffff;
+	dFillCol = 0xffffffffL;
+	hres = vres = 1000.0f;
+	go = g;
+	if(FileName)name = (char*)memdup(FileName, (int)strlen(FileName)+1, 0);
+	else name = 0L;
+	out_buff = 0L;		out_pos = out_size = 0;
+	flags = flg;		cb_ind = 3;
+	rlp_strcpy(tHatchStyle, 80, "style=\"stroke:black; stroke-width:1\"");
+	bUseGroupLine = false;
+}
+
+ExportSVG::~ExportSVG()
+{
+	if(hgo) delete hgo;
+	if(name) free(name);
+}
+
+bool
+ExportSVG::SetLine(LineDEF *lDef)
+{
+	LineWidth = lDef->width;
+	if(1 >(iLineWidth  = iround(un2fix(lDef->width)))) iLineWidth = 1;
+	dPattern = lDef->pattern;
+	dLineCol = lDef->color;
+	RLP.finc = (float)(256.0/un2fix(lDef->patlength*8.0));
+	RLP.fp = 0.0;
+	return true;
+}
+
+bool
+ExportSVG::SetFill(FillDEF *fill)
+{
+	int iL; 
+
+	if(!fill) return false;
+	if((fill->type & 0xff) != FILL_NONE) {
+		if(!hgo) hgo = new HatchOut(this);
+		if(hgo) hgo->SetFill(fill);
+		if(fill->hatch) {
+			if(1 >(iL  = iround(un2fix(fill->hatch->width)))) iL = 1;
+#ifdef USE_WIN_SECURE
+			sprintf_s(tHatchStyle, 80, "style=\"fill:none; stroke:%s; stroke-width:%d\"",
+				ColName(fill->hatch->color), iL);
+#else
+			sprintf(tHatchStyle, "style=\"fill:none; stroke:%s; stroke-width:%d\"",
+				ColName(fill->hatch->color), iL);
+#endif
+			}
+		}
+	else {
+		if(hgo) delete hgo;
+		hgo = 0L;
+		}
+	dFillCol = fill->color;
+	dFillCol2 = fill->color2;
+	return true;
+}
+
+bool
+ExportSVG::SetTextSpec(TextDEF *set)
+{
+	if(set->fSize > 0.0) {
+		if((set->Style & TXS_SUPER) || (set->Style & TXS_SUB))
+			set->iSize = un2iy(set->fSize * 0.71);
+		else set->iSize = un2iy(set->fSize);
+		}
+	if(!set->iSize) return false;
+	return anyOutput::SetTextSpec(set);
+}
+
+bool 
+ExportSVG::StartPage()
+{
+	int w, h;
+
+	if(!go) return false;
+	w = un2ix(go->GetSize(SIZE_GRECT_RIGHT) - go->GetSize(SIZE_GRECT_LEFT))/10;
+	h = un2iy(go->GetSize(SIZE_GRECT_BOTTOM) - go->GetSize(SIZE_GRECT_TOP))/10;
+	w++; h++;
+	if(flags & 0x01) add_to_buff(&out_buff, &out_pos, &out_size, "Content-Type: image/svg+xml\n\n", 0);
+	VPorg.fy = -un2fiy(go->GetSize(SIZE_GRECT_TOP));
+	VPorg.fx = -un2fix(go->GetSize(SIZE_GRECT_LEFT));
+	add_to_buff(&out_buff, &out_pos, &out_size, "<?xml version=\"1.0\"?>\n"
+		"<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 20001102//EN\"\n"
+		"   \"http://www.w3.org/TR/2000/CR-SVG-20001102/DTD/svg-20001102.dtd\">\n<svg ", 0);
+	if(defs.svgAttr) add_to_buff(&out_buff, &out_pos, &out_size, defs.svgAttr, 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, " width=\"", 0);
+	add_int_to_buff(&out_buff, &out_pos, &out_size, w, false, 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "\" height=\"", 0);
+	add_int_to_buff(&out_buff, &out_pos, &out_size, h, false, 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "\" style=\"stroke-linecap:round; stroke-linejoin:round\">\n", 0);
+	if(defs.svgScript) {
+		add_to_buff(&out_buff, &out_pos, &out_size, "<defs>\n<script type=\"text/ecmascript\"><![CDATA[\n\n", 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, defs.svgScript, 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, "\n\n]]></script>\n</defs>\n\n", 0);
+		}
+	add_to_buff(&out_buff, &out_pos, &out_size, "<g transform=\"scale(0.1)\" style=\"font-family:Helvetica\">\n", 0);
+	return true;
+}
+
+bool
+ExportSVG::EndPage()
+{
+	FILE *oFile;
+
+	add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n</svg>\n", 0);
+	if(name && out_buff && out_pos > 20) {
+#ifdef USE_WIN_SECURE
+		fopen_s(&oFile, name, "w");
+#else
+		oFile = fopen(name, "w");
+#endif
+		if(!oFile) {
+			ErrorBox("Could not open\noutput file!");
+			return false;
+			}
+		fprintf(oFile, "%s", out_buff);
+		free(out_buff);		fclose (oFile);
+		}
+	return true;
+}
+
+bool
+ExportSVG::oCircle(int x1, int y1, int x2, int y2, char* nam)
+{
+	if(x1 > x2) Swap(x1, x2);
+	if(y1 > y2) Swap(y1, y2);
+
+	if(hgo){
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, "<g>  <!-- ", 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, (x2-x1) == (y2-y1) ? (char*)"circle" : (char*)"ellipse", 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, " with pattern -->\n", 0);
+		Indent(true);
+		}
+	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+	add_to_buff(&out_buff, &out_pos, &out_size, (x2-x1) == (y2-y1) ? (char*)"<circle" : (char*)"<ellipse", 0);
+	if(nam && nam[0]) {
+		add_to_buff(&out_buff, &out_pos, &out_size, " name=\"", 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, nam, 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, "\"", 0);
+		}
+	add_to_buff(&out_buff, &out_pos, &out_size, " cx=\"", 0);
+	add_int_to_buff(&out_buff, &out_pos, &out_size, (x1+x2)>>1, false, 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "\" cy=\"", 0);
+	add_int_to_buff(&out_buff, &out_pos, &out_size, (y1+y2)>>1, false, 0);
+	if((x2-x1)==(y2-y1)) {
+		add_to_buff(&out_buff, &out_pos, &out_size, "\" r=\"", 0);
+		add_int_to_buff(&out_buff, &out_pos, &out_size, (x2-x1)>>1, false, 0);
+		}
+	else {
+		add_to_buff(&out_buff, &out_pos, &out_size, "\" rx=\"", 0);
+		add_int_to_buff(&out_buff, &out_pos, &out_size, (x2-x1)>>1, false, 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, "\" ry=\"", 0);
+		add_int_to_buff(&out_buff, &out_pos, &out_size, (y2-y1)>>1, false, 0);
+		}
+	add_to_buff(&out_buff, &out_pos, &out_size, "\" style=\"fill:", 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, ColName(dFillCol), 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "; stroke:", 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, ColName(dLineCol), 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-width:", 0);
+	add_int_to_buff(&out_buff, &out_pos, &out_size, iLineWidth, false, 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, ";", 1);
+	add_to_buff(&out_buff, &out_pos, &out_size, Transparency(dFillCol, 2), 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, Transparency(dLineCol, 1), 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "\"/>\n", 0);
+	if(hgo) {
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, "<g ", 3);
+		add_to_buff(&out_buff, &out_pos, &out_size, tHatchStyle, 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, ">  <!-- hatch -->\n", 0);
+		Indent(true);		bUseGroupLine = true;
+		hgo->oCircle(x1, y1, x2, y2);
+		Indent(false);		bUseGroupLine = false;
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 0);
+		Indent(false);
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 0);
+		}
+	return true;
+}
+
+bool
+ExportSVG::oPolyline(POINT *pts, int cp, char *nam)
+{
+	int i, cb;
+	char tmptxt[120];
+
+	if(cp < 2) return false;
+	if (dPattern){
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, "<g style=\"stroke:", 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, ColName(dLineCol), 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-width:", 0);
+		add_int_to_buff(&out_buff, &out_pos, &out_size, iLineWidth, false, 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-linecap:round;", 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, Transparency(dLineCol, 1), 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, "\"><!-- pattern line -->\n", 0);
+		Indent(true);			bUseGroupLine = true;
+		for (i = 1; i < cp; i++) PatLine(pts[i-1], pts[i]);
+		Indent(false);
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 0);
+		bUseGroupLine = false;
+		}
+	else {
+		if(cp == 2) return oSolidLine(pts);
+		bOutputPending = false;
+		cb_out = rlp_strcpy(output, 20,"<polyline points=\""); 
+		for(i = 0; i < cp; i++) {
+#ifdef USE_WIN_SECURE
+			cb = sprintf_s(tmptxt, 120, "%d %d ", pts[i].x, pts[i].y);
+#else
+			cb = sprintf(tmptxt, "%d %d ", pts[i].x, pts[i].y);
+#endif
+			AddToOutput(tmptxt, cb);
+			}
+		if(cb_out) output[cb_out-1] = '"';
+		if(!bUseGroupLine) {
+			cb = rlp_strcpy(tmptxt, 120, " style = \"fill:none; ");
+			AddToOutput(tmptxt, cb);
+			cb = rlp_strcpy(tmptxt, 120, "stroke:");	//bug fixed by vefremov
+			cb += rlp_strcpy(tmptxt+cb, 120-cb, ColName(dLineCol));
+			cb += rlp_strcpy(tmptxt+cb, 120-cb, "; ");
+			AddToOutput(tmptxt, cb);
+#ifdef USE_WIN_SECURE
+			cb = sprintf_s(tmptxt, 120, "stroke-width:%d;",iLineWidth);
+#else
+			cb = sprintf(tmptxt, "stroke-width:%d;",iLineWidth);
+#endif
+			AddToOutput(tmptxt, cb);
+			if(cb = rlp_strcpy(tmptxt, 40, Transparency(dLineCol, 1))) AddToOutput(tmptxt, cb);
+			AddToOutput("\"/>", 3); 
+			}
+		else AddToOutput("/>", 2);
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, "\n", 1);
+		if(bOutputPending)Indent(false);
+		}
+	return true;
+}
+
+bool
+ExportSVG::oRectangle(int x1, int y1, int x2, int y2, char *nam)
+{
+	if(x1 > x2) Swap(x1, x2);
+	if(y1 > y2) Swap(y1, y2);
+	if(hgo){
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, "<g>  <!-- rectangle with pattern -->\n", 0);
+		Indent(true);
+		}
+	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+	add_to_buff(&out_buff, &out_pos, &out_size, "<rect", 5);
+	if(nam && nam[0]) {
+		add_to_buff(&out_buff, &out_pos, &out_size, " name=\"", 7);
+		add_to_buff(&out_buff, &out_pos, &out_size, nam, 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, "\"", 1);
+		}
+	add_to_buff(&out_buff, &out_pos, &out_size, " x=\"", 4);
+	add_int_to_buff(&out_buff, &out_pos, &out_size, x1, false, 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "\" y=\"", 5);
+	add_int_to_buff(&out_buff, &out_pos, &out_size, y1, false, 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "\" width=\"", 9);
+	add_int_to_buff(&out_buff, &out_pos, &out_size, x2-x1-1, false, 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "\" height=\"", 10);
+	add_int_to_buff(&out_buff, &out_pos, &out_size, y2-y1-1, false, 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "\" style=\"fill:", 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, ColName(dFillCol), 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "; stroke:", 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, ColName(dLineCol), 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-width:", 0);
+	add_int_to_buff(&out_buff, &out_pos, &out_size, iLineWidth, false, 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, ";", 1);
+	add_to_buff(&out_buff, &out_pos, &out_size, Transparency(dFillCol, 2), 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, Transparency(dLineCol, 1), 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "\"/>\n", 0);
+	if(hgo) {
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, "<g ", 3);
+		add_to_buff(&out_buff, &out_pos, &out_size, tHatchStyle, 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, ">  <!-- hatch -->\n", 0);
+		Indent(true);				bUseGroupLine = true;
+		hgo->oRectangle(x1, y1, x2, y2, 0L);
+		Indent(false);				bUseGroupLine = false;
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 5);
+		Indent(false);
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 5);
+		}
+	return true;
+}
+
+bool
+ExportSVG::oSolidLine(POINT *p)
+{
+	if(!p) return false;
+	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+	add_to_buff(&out_buff, &out_pos, &out_size, "<line x1=\"", 0);
+	add_int_to_buff(&out_buff, &out_pos, &out_size, p[0].x, false, 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "\" y1=\"", 6);
+	add_int_to_buff(&out_buff, &out_pos, &out_size, p[0].y, false, 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "\" x2=\"", 6);
+	add_int_to_buff(&out_buff, &out_pos, &out_size, p[1].x, false, 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "\" y2=\"", 6);
+	add_int_to_buff(&out_buff, &out_pos, &out_size, p[1].y, false, 0);
+	if(!bUseGroupLine) {
+		add_to_buff(&out_buff, &out_pos, &out_size, "\" style=\"stroke:", 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, ColName(dLineCol), 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, "; stroke-width:", 0);
+		add_int_to_buff(&out_buff, &out_pos, &out_size, iLineWidth, false, 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, ";", 1);
+		add_to_buff(&out_buff, &out_pos, &out_size, Transparency(dLineCol, 1), 0);
+		}
+	add_to_buff(&out_buff, &out_pos, &out_size, "\"/>\n", 4);
+	return true;
+}
+
+bool
+ExportSVG::com_TextOut(int x, int y, char *txt, int cb)
+{
+	int c, h, ix, iy, dy;
+	char tmptxt[120];
+
+	if(!txt || !txt[0]) return false;
+	else h = TxtSet.iSize;
+	if(TxtSet.Align & TXA_VCENTER) iy = y + h/3;
+	else if(TxtSet.Align & TXA_VBOTTOM) iy = y;
+	else iy = y + iround(h * 0.8);
+	ix = x;		dy = 0;
+	if(TxtSet.Style & TXS_SUB) {
+		if((TxtSet.Align & TXA_VCENTER) == TXA_VCENTER) dy = un2iy(TxtSet.fSize*0.4);
+		else if((TxtSet.Align & TXA_VBOTTOM) == TXA_VBOTTOM) dy = un2iy(TxtSet.fSize*0.2);
+		else if((TxtSet.Align & TXA_VTOP) == TXA_VTOP) dy = un2iy(TxtSet.fSize*.6);
+		}
+	else if(TxtSet.Style & TXS_SUPER) {
+		if((TxtSet.Align & TXA_VCENTER) == TXA_VCENTER) dy = -un2iy(TxtSet.fSize*0.4);
+		else if((TxtSet.Align & TXA_VBOTTOM) == TXA_VBOTTOM) dy = -un2iy(TxtSet.fSize*0.6);
+		else if((TxtSet.Align & TXA_VTOP) == TXA_VTOP) dy = -un2iy(TxtSet.fSize*0.2);
+		}
+#ifdef USE_WIN_SECURE
+	cb_out = sprintf_s(output, 120, "<text x=\"%d\" y=\"%d\" dy=\"%d\" ", ix, iy, dy);
+#else
+	cb_out = sprintf(output, "<text x=\"%d\" y=\"%d\" dy=\"%d\" ", ix, iy, dy);
+#endif
+	if(fabs(TxtSet.RotBL) >.01 || fabs(TxtSet.RotCHAR) >.01) {
+#ifdef USE_WIN_SECURE
+		cb_out += sprintf_s(output+cb_out, 120-cb_out, "transform=\"rotate(%.0f,%d,%d)\" ", -TxtSet.RotBL, ix, iy);
+#else
+		cb_out += sprintf(output+cb_out,"transform=\"rotate(%.0f,%d,%d)\" ", -TxtSet.RotBL, ix, iy);
+#endif
+		}
+	c = rlp_strcpy(tmptxt, 140, "style=\"font-family:");
+	switch(TxtSet.Font) {
+	case FONT_TIMES:	c += rlp_strcpy(tmptxt+c, 120-c, "Times;");		break;
+	case FONT_COURIER:	c += rlp_strcpy(tmptxt+c, 120-c, "Courier;");	break;
+	default:			c += rlp_strcpy(tmptxt+c, 120-c, "Helvetica;");	break;
+		}
+	if(TxtSet.Style & TXS_ITALIC) c += rlp_strcpy(tmptxt+c, 120-c, " font-style:italic;");
+	if(TxtSet.Style & TXS_BOLD) c += rlp_strcpy(tmptxt+c, 120-c, " font-weight:bold;");
+	if(TxtSet.Style & TXS_UNDERLINE) c += rlp_strcpy(tmptxt+c, 120-c, " text-decoration:underline;");
+	AddToOutput(tmptxt, c);
+#ifdef USE_WIN_SECURE
+	c = sprintf_s(tmptxt, 120, " fill:%s; stroke:%s;%s ", ColName(TxtSet.ColTxt), ColName(TxtSet.ColTxt),Transparency(TxtSet.ColTxt, 0));
+#else
+	c = sprintf(tmptxt, " fill:%s; stroke:%s;%s ", ColName(TxtSet.ColTxt), ColName(TxtSet.ColTxt),Transparency(TxtSet.ColTxt, 0));
+#endif
+	AddToOutput(tmptxt, c);
+#ifdef USE_WIN_SECURE
+	c = sprintf_s(tmptxt, 120, "font-size:%d; text-anchor:%s \">", h, 
+		(TxtSet.Align & TXA_HRIGHT) ? "end" : (TxtSet.Align & TXA_HCENTER) ? "middle":"start");
+#else
+	c = sprintf(tmptxt, "font-size:%d; text-anchor:%s \">", h, 
+		(TxtSet.Align & TXA_HRIGHT) ? "end" : (TxtSet.Align & TXA_HCENTER) ? "middle":"start");
+#endif
+	AddToOutput(tmptxt, c);
+	if((cb_ind+strlen(txt)+cb_out) <110) cb_out += rlp_strcpy(output+cb_out, 120-cb_out, txt);
+	else {
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, "\n", 1);
+		cb_out=rlp_strcpy(output, 120, txt);
+		}
+	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+	add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
+	if((cb_ind + cb_out) <104) {
+		add_to_buff(&out_buff, &out_pos, &out_size, "</text>\n", 0);
+		}
+	else add_to_buff(&out_buff, &out_pos, &out_size, "\n</text>\n", 0);
+	return true;
+}
+
+bool
+ExportSVG::oTextOut(int x, int y, char *txt, int cb)
+{
+	char *nt;
+
+	if(!txt || !txt[0]) return false;
+	nt = str2xml(txt, TxtSet.Font == FONT_GREEK);
+	return com_TextOut(x, y, nt, cb);
+}
+
+bool
+ExportSVG::oTextOutW(int x, int y, w_char *txt, int cb)
+{
+	int i, j;
+	wchar_t wc;
+	char c;
+
+	for(i = j = 0; txt[i]; i++) {
+		switch(txt[i]) {
+			case '"':
+				j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, """);
+				break;
+			case '&':
+				j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, "&");
+				break;
+			case '<':
+				j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, "<");
+				break;
+			case '>':
+				j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, ">");
+				break;
+			default:
+				if(txt[i] > 255) {
+#ifdef USE_WIN_SECURE
+					j += sprintf_s(TmpTxt+j, TMP_TXT_SIZE-j, "&#%d;", txt[i]);
+#else
+					j += sprintf(TmpTxt+j, "&#%d;", txt[i]);
+#endif
+					}
+				else if(txt[i] > 127) {
+					c = (char)txt[i];
+					if(mbtowc(&wc, &c, 1) >0) 
+#ifdef USE_WIN_SECURE
+						j += sprintf_s(TmpTxt+j, TMP_TXT_SIZE-j, "&#%d;", ((unsigned short)wc));
+#else
+						j += sprintf(TmpTxt+j, "&#%d;", ((unsigned short)wc));
+#endif
+					}
+				else TmpTxt[j++] = (char)txt[i];
+				break;
+			}
+		}
+	TmpTxt[j++] = 0;
+	return com_TextOut(x, y, TmpTxt, j-1);
+}
+
+bool
+ExportSVG::oPolygon(POINT *pts, int cp, char *nam)
+{
+	int i, cb;
+	char tmptxt[40];
+
+	if(cp <3) return false;
+	if(hgo){
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, "<g>  <!-- polygon with pattern -->\n", 0);
+		Indent(true);
+		}
+	bOutputPending = false;
+#ifdef USE_WIN_SECURE
+	cb_out = sprintf_s(output, 120, "<polygon%s%s%s points=\"",
+		nam? " name=\"" : "", nam ? nam : "", nam ? "\"" : ""); 
+#else
+	cb_out = sprintf(output, "<polygon%s%s%s points=\"",
+		nam? " name=\"" : "", nam ? nam : "", nam ? "\"" : ""); 
+#endif
+	for(i = 0; i < cp; i++) {
+#ifdef USE_WIN_SECURE
+		cb = sprintf_s(tmptxt, 40, "%d %d ", pts[i].x, pts[i].y);
+#else
+		cb = sprintf(tmptxt, "%d %d ", pts[i].x, pts[i].y);
+#endif
+		AddToOutput(tmptxt, cb);
+		}
+	if(cb_out) output[cb_out-1] = '"';
+#ifdef USE_WIN_SECURE
+	cb = sprintf_s(tmptxt, 40, " style=\"fill:%s; ", ColName(dFillCol));
+	AddToOutput(tmptxt, cb);
+	cb = sprintf_s(tmptxt, 40, "stroke:%s; ", ColName(dLineCol));
+	AddToOutput(tmptxt, cb);
+	cb = sprintf_s(tmptxt, 40, "stroke-width:%d;",iLineWidth);
+	AddToOutput(tmptxt, cb);
+#else
+	cb = sprintf(tmptxt, " style=\"fill:%s; ", ColName(dFillCol));
+	AddToOutput(tmptxt, cb);
+	cb = sprintf(tmptxt, "stroke:%s; ", ColName(dLineCol));
+	AddToOutput(tmptxt, cb);
+	cb = sprintf(tmptxt, "stroke-width:%d;", iLineWidth);
+	AddToOutput(tmptxt, cb);
+#endif
+	if(cb = rlp_strcpy(tmptxt, 40, Transparency(dFillCol, 2))) AddToOutput(tmptxt, cb);
+	if(cb = rlp_strcpy(tmptxt, 40, Transparency(dLineCol, 1))) AddToOutput(tmptxt, cb);
+	AddToOutput("\"/>", 3); 
+	add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+	add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
+	add_to_buff(&out_buff, &out_pos, &out_size, "\n", 1);
+	if(bOutputPending)Indent(false);
+	if(hgo) {
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, "<g ", 3);
+		add_to_buff(&out_buff, &out_pos, &out_size, tHatchStyle, 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, ">  <!-- hatch -->\n", 0);
+		Indent(true);				bUseGroupLine = true;
+		hgo->oPolygon(pts, cp);		Indent(false);
+		bUseGroupLine = false;
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 5);
+		Indent(false);
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, "</g>\n", 5);
+		}
+	return true;
+}
+
+void
+ExportSVG::Indent(bool ind)
+{
+	if(ind) {
+		if(cb_ind < 20) cb_ind += 3;
+		}
+	else {
+		if(cb_ind > 5) cb_ind -= 3;
+		}
+}
+
+void
+ExportSVG::AddToOutput(char *txt, int len)
+{
+	if(!txt || !txt[0]) return;
+	if(!len) len = (int)strlen(txt);
+	if((len + cb_out + cb_ind) < 110){
+		cb_out += rlp_strcpy(output+cb_out, 120, txt);
+		}
+	else {
+		add_to_buff(&out_buff, &out_pos, &out_size, str_ind, cb_ind);
+		add_to_buff(&out_buff, &out_pos, &out_size, output, 0);
+		add_to_buff(&out_buff, &out_pos, &out_size, "\n", 1);
+		if(!bOutputPending) Indent(true);
+		bOutputPending = true;
+		cb_out = rlp_strcpy(output, 120, txt);
+		}
+}
+
+char *
+ExportSVG::ColName(DWORD col)
+{
+	static char txt1[20], txt2[20];
+	static int sw;
+	char *ret;
+
+	switch(col) {
+	case 0x00000000:			return "black";
+	case 0x000000ff:			return "red";
+	case 0x0000ff00:			return "lime";
+	case 0x0000ffff:			return "yellow";
+	case 0x00ff0000:			return "blue";
+	case 0x00ff00ff:			return "magenta";
+	case 0x00ffff00:			return "cyan";
+	case 0x00ffffff:			return "white";
+		}
+	sw = (sw+1) & 0x0f;
+#ifdef USE_WIN_SECURE
+	sprintf_s(ret = (sw & 0x01 ? txt1 : txt2), 20, "rgb(%d,%d,%d)", col & 0xff, (col>>8) & 0xff, (col>>16) &0xff);
+#else
+	sprintf(ret = (sw & 0x01 ? txt1 : txt2), "rgb(%d,%d,%d)", col & 0xff, (col>>8) & 0xff, (col>>16) &0xff);
+#endif
+	return ret;
+}
+
+char *
+ExportSVG::Transparency(DWORD col, int type)
+{
+	static char txt1[30], txt2[30];
+	static int sw;
+	int cb;
+	char *ret;
+	double a;
+
+	if((col & 0xfe000000) == 0) return "";
+	a = ((double)(255-(col>>24)))/256.0;
+	if(a > 0.99) return "";
+	sw = (sw+1) & 0x0f;
+	ret = sw & 0x01 ? txt1 : txt2;
+	switch(type) {
+	case 1:
+		cb = rlp_strcpy(ret, 30, " stroke-opacity:");	break;
+	case 2:	
+		cb = rlp_strcpy(ret, 30, " fill-opacity:");	break;
+	default:
+		cb = rlp_strcpy(ret, 30, " opacity:");		break;
+		}
+#ifdef USE_WIN_SECURE
+	sprintf_s(ret+cb, 30-cb, "%0.3lf;", a);
+#else
+	sprintf(ret+cb, "%0.3lf;", a);
+#endif
+	return ret;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Entry point to export graph to *.svg
+void DoExportSvg(GraphObj *g, char *FileName, DWORD flags)
+{
+	ExportSVG *ex;
+	
+	ex = new ExportSVG(g, FileName, flags);
+	if(ex->StartPage()) {
+		g->DoPlot(ex);
+		ex->EndPage();
+		}
+	HideTextCursor();	
+	delete(ex);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// export to *.eps file (encapsulated post script).
+// This code is based on information from the following book
+// G. Born, 'Referenzhandbuch Dateiformate', 
+//     Addison-Wesley ISBN 3-89319-815-6
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class ExportEPS:public anyOutput {
+public:
+	HatchOut *hgo;
+
+	ExportEPS(GraphObj *g, char *FileName, DWORD flags);
+	~ExportEPS();
+	bool SetLine(LineDEF *lDef);
+	bool SetFill(FillDEF *fill);
+	bool SetTextSpec(TextDEF *set);
+	bool StartPage();
+	bool EndPage();
+	bool oCircle(int x1, int y1, int x2, int y2, char* nam = 0L);
+	bool oPolyline(POINT * pts, int cp, char *nam = 0L);
+	bool oRectangle(int x1, int y1, int x2, int y2, char *nam = 0L);
+	bool oSolidLine(POINT *p);
+	bool oTextOut(int x, int y, char *txt, int cb);
+	bool oPolygon(POINT *pts, int cp, char *nam = 0L);
+
+private:
+	RECT BoundBox;
+	fRECT SolLines[2000];
+	int nSolLines;
+	GraphObj *go;
+	char *name, FontName[30];
+	FILE *oFile;
+	DWORD CurrCol;
+	bool bFontChange;
+
+	float ix2eps(int x);
+	float iy2eps(int y);
+	char *col2eps(DWORD color);
+	void FlushSolLines();
+	void AddSolLine(float x1, float y1, float x2, float y2);
+};
+
+ExportEPS::ExportEPS(GraphObj *g, char *FileName, DWORD flags)
+{
+	hgo =0L;		nSolLines = 0;			DeskRect.left = DeskRect.top = 0;
+	DeskRect.right = DeskRect.bottom = 0x4fffffff;
+	dFillCol = 0xffffffffL;					hres = vres = 720.0f;
+	go = g;			bFontChange = false;	oFile = 0L;
+	if(FileName)name = (char*)memdup(FileName, (int)strlen(FileName)+1,0);
+	else name = 0L;
+}
+
+ExportEPS::~ExportEPS()
+{
+	if(hgo) delete hgo;
+	if(name) free(name);
+}
+
+bool
+ExportEPS::SetLine(LineDEF *lDef)
+{
+	if(LineWidth != lDef->width || dLineCol != lDef->color) {
+		FlushSolLines();
+		LineWidth = lDef->width;
+		CurrCol = dLineCol = lDef->color;
+		fprintf(oFile, "\nnewpath %.1f setlinewidth %s ",
+			un2fix(LineWidth)/10.0f, col2eps(dLineCol));
+		}
+	dPattern = lDef->pattern;
+	RLP.finc = (float)(256.0/un2fix(lDef->patlength*8.0));
+	RLP.fp = 0.0;
+	return true;
+}
+
+bool
+ExportEPS::SetFill(FillDEF *fill)
+{
+	if(!fill) return false;
+	if((fill->type & 0xff) != FILL_NONE) {
+		if(!hgo) hgo = new HatchOut(this);		if(hgo) hgo->SetFill(fill);
+		}
+	else {
+		if(hgo) delete hgo;						hgo = 0L;
+		}
+	dFillCol = fill->color;						dFillCol2 = fill->color2;
+	return true;
+}
+
+bool
+ExportEPS::SetTextSpec(TextDEF *set)
+{
+	int cb;
+
+	if(set->fSize > 0.0) {
+		if((set->Style & TXS_SUPER) || (set->Style & TXS_SUB)) set->iSize = un2iy(set->fSize * 0.71);
+		else set->iSize = un2iy(set->fSize);
+		}
+	if(!set->iSize) return false;
+	anyOutput::SetTextSpec(set);
+	switch(TxtSet.Font) {
+	case FONT_TIMES:	cb = rlp_strcpy(FontName, 30, "(Times");		break;		//Serif
+	case FONT_COURIER:	cb = rlp_strcpy(FontName, 30, "(Courier");		break;		//fixed spaced
+	default:			cb = rlp_strcpy(FontName, 30, "(Helvetica");	break;		//Sans Serif	
+		}
+	if(TxtSet.Style & TXS_BOLD) cb += rlp_strcpy(FontName+cb, 30-cb, "-Bold");
+	if(TxtSet.Style & TXS_ITALIC) cb += rlp_strcpy(FontName+cb, 30-cb, "-Italic");
+	cb += rlp_strcpy(FontName+cb, 30-cb, ")");		bFontChange = true;
+	return true;
+}
+
+bool 
+ExportEPS::StartPage()
+{
+	time_t ti;
+	
+	if(!go) return false;
+	ti = time(0L);
+	BoundBox.top = BoundBox.left = 0;
+	BoundBox.right = un2ix(go->GetSize(SIZE_GRECT_RIGHT) - go->GetSize(SIZE_GRECT_LEFT))/10;
+	BoundBox.bottom = un2iy(go->GetSize(SIZE_GRECT_BOTTOM) - go->GetSize(SIZE_GRECT_TOP))/10;
+	BoundBox.right++;	BoundBox.bottom++;
+	if(name) {
+#ifdef USE_WIN_SECURE
+		fopen_s(&oFile, name, "w");
+#else
+		oFile = fopen(name, "w");
+#endif
+		if(!oFile) {
+			ErrorBox("Could not open\noutput file!");
+			return false;
+			}
+		}
+	else oFile = stdout;
+	VPorg.fy = -un2fiy(go->GetSize(SIZE_GRECT_TOP));
+	VPorg.fx = -un2fix(go->GetSize(SIZE_GRECT_LEFT));
+	fprintf(oFile, "%%!PS-Adobe-1.0 EPSF-3.0\n"
+		"%%%%BoundingBox: %d %d %d %d\n", BoundBox.left, BoundBox.top,
+		BoundBox.right, BoundBox.bottom);
+	fprintf(oFile, "%%%%Title: %s\n", name);
+	fprintf(oFile,"%%%%Creator: RLPlot version "SZ_VERSION"\n");
+#ifdef USE_WIN_SECURE
+	ctime_s(TmpTxt, 50, &ti);	TmpTxt[24] = 0;
+	fprintf(oFile,"%%%%CreationDate: %s", TmpTxt);
+#else
+	fprintf(oFile,"%%%%CreationDate: %s", ctime(&ti));
+#endif
+	fprintf(oFile, "%%%%Pages: 1\n%%%%DocumentFonts: (atend)\n");
+	fprintf(oFile, "%%%%EndComments\n"
+		"%%%%BeginProlog\n"
+		"%%%%EndProlog\n"
+		"%%%%Page: 1 1");
+	return true;
+}
+
+bool
+ExportEPS::EndPage()
+{
+	fprintf(oFile, "\nshowpage\n%%%%Trailer\n");
+	fprintf(oFile, "%%%%DocumentFonts: Helvetica\n");
+	fprintf(oFile, "%%%%EOF\n");
+	fclose (oFile);
+	return true;
+}
+
+bool
+ExportEPS::oCircle(int x1, int y1, int x2, int y2, char* nam)
+{
+	FlushSolLines();
+	if((x2 - x1) == (y2 -y1)) {
+		fprintf(oFile, "\nnewpath %.1f %.1f %.1f 0 360 arc ", ix2eps((x1+x2)/2), 
+			iy2eps((y1+y2)/2), (float)(x2-x1)/20.0f);
+		fprintf(oFile, "%s fill", col2eps(CurrCol = dFillCol));
+		if(hgo){
+			hgo->oCircle(x1, y1, x2, y2);
+			FlushSolLines();
+			}
+		fprintf(oFile, "\nnewpath %.1f %.1f %.1f 0 360 arc ", ix2eps((x1+x2)/2), 
+			iy2eps((y1+y2)/2), (float)(x2-x1)/20.0f);
+		fprintf(oFile, "%s stroke", col2eps(CurrCol = dLineCol));
+		}
+	else if(x1 != x2 && y1 != y2){
+		fprintf(oFile, "\ngsave %.1f %.1f translate", (ix2eps((x1+x2)>>1)),
+			(iy2eps((y1+y2)>>1)));
+		if(abs(x2-x1) > abs(y2-y1)) {
+			fprintf(oFile, " 1 %lf scale", fabs(((double)(y2-y1))/((double)(x2-x1))));
+			fprintf(oFile, " 0 0 %.1lf 0 360 arc ", fabs(((double)(x2-x1))/20.0));
+			fprintf(oFile, "%s fill", col2eps(CurrCol = dFillCol));
+			fprintf(oFile, "\n 0 0 %.1lf 0 360 arc ", fabs(((double)(x2-x1))/20.0));
+			}
+		else {
+			fprintf(oFile, " %lf 1 scale", fabs(((double)(x2-x1))/((double)(y2-y1))));
+			fprintf(oFile, " 0 0 %.1lf 0 360 arc ", fabs(((double)(y2-y1))/20.0));
+			fprintf(oFile, "%s fill", col2eps(CurrCol = dFillCol));
+			fprintf(oFile, "\n 0 0 %.1lf 0 360 arc ", fabs(((double)(y2-y1))/20.0));
+			}
+		fprintf(oFile, "%s stroke grestore", col2eps(CurrCol = dLineCol));
+		if(hgo){
+			hgo->oCircle(x1, y1, x2, y2);
+			FlushSolLines();
+			}
+		}
+	return true;
+}
+
+bool
+ExportEPS::oPolyline(POINT * pts, int cp, char *nam)
+{
+	int i, j;
+
+	if(cp <1) return false;
+	if (dPattern){
+		for (i = 1; i < cp; i++) PatLine(pts[i-1], pts[i]);
+		return true;
+		}
+	else if(cp == 2) return oSolidLine(pts);
+	FlushSolLines();
+	if(CurrCol != dLineCol) {
+		fprintf(oFile, "\n%s ", col2eps(CurrCol = dLineCol));
+		}
+	for(i = cp-1, j = 0; i >= 0; i--, j++) {
+		if(!(j & 0x07)) fprintf(oFile, "\n");
+		fprintf(oFile, " %.1f %.1f", ix2eps(pts[i].x), iy2eps(pts[i].y));
+		}
+	fprintf(oFile, " moveto %d {lineto} repeat stroke ", cp-1);
+	return true;
+}
+
+
+bool
+ExportEPS::oRectangle(int x1, int y1, int x2, int y2, char *nam)
+{
+	FlushSolLines();
+	fprintf(oFile, "\n%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f moveto 4 {lineto}"
+		" repeat %s fill", ix2eps(x1), iy2eps(y1), ix2eps(x1), iy2eps(y2), ix2eps(x2), iy2eps(y2), 
+		ix2eps(x2), iy2eps(y1), ix2eps(x1), iy2eps(y1), col2eps(CurrCol = dFillCol));
+	if(hgo) hgo->oRectangle(x1, y1, x2, y2, 0L);
+	fprintf(oFile, "\n%.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f %.1f moveto 4 {lineto}"
+		" repeat %s stroke", ix2eps(x1), iy2eps(y1), ix2eps(x1), iy2eps(y2), ix2eps(x2), iy2eps(y2), 
+		ix2eps(x2), iy2eps(y1), ix2eps(x1), iy2eps(y1), col2eps(CurrCol = dLineCol));
+	return true;
+}
+
+bool
+ExportEPS::oSolidLine(POINT *p)
+{
+	if(CurrCol != dLineCol) {
+		FlushSolLines();
+		fprintf(oFile, "\n%s ", col2eps(CurrCol = dLineCol));
+		}
+	AddSolLine(ix2eps(p[0].x), iy2eps(p[0].y), ix2eps(p[1].x), iy2eps(p[1].y));
+	return true;
+}
+
+bool
+ExportEPS::oTextOut(int x, int y, char *txt, int cb)
+{
+	int h, ix, iy, w;
+	float fx, fy, lw;
+
+	FlushSolLines();	if(!txt || !txt[0]) return true;
+	oGetTextExtent(txt, cb, &w, &h);
+	if(bFontChange)	{
+		fprintf(oFile, "\n%s findfont %d scalefont setfont ", FontName, TxtSet.iSize/10);
+		bFontChange = false;
+		}
+	if(TxtSet.Align & TXA_VCENTER) iy = y + h/3;
+	else if(TxtSet.Align & TXA_VBOTTOM) iy = y;
+	else iy = y + iround(h*.8);
+	if(TxtSet.Align & TXA_HRIGHT) ix = x-w;
+	else if(TxtSet.Align & TXA_HCENTER) ix = x-w/2;
+	else ix = x;			lw = (float)(iy-y)/150.0f;
+	fprintf(oFile,"\n");
+	if(fabs(TxtSet.RotBL) >.01 || fabs(TxtSet.RotCHAR) >.01) {
+		if(TxtSet.Style & TXS_SUB) iy += un2iy(TxtSet.fSize*0.6);
+		else if(TxtSet.Style & TXS_SUPER) iy -= un2iy(TxtSet.fSize*.2);
+		fprintf(oFile, "gsave %.1f %.1f translate %f rotate %.1f %.1f moveto\n",
+			ix2eps(x), iy2eps(y), TxtSet.RotBL, (float)(ix-x)/10.0f, (float)(iy-y)/-10.0f);
+		fprintf(oFile, "%s ", col2eps(CurrCol = TxtSet.ColTxt));
+		fprintf(oFile, "(%s) show ", txt);
+		if(TxtSet.Style & TXS_UNDERLINE) {
+			fprintf(oFile, "\ncurrentpoint %.1f exch pop moveto", (float)(iy-y)/-10.0f - lw*1.2);
+			fprintf(oFile, " 0 %.1f lineto %s %.1f setlinewidth stroke ", (float)(iy-y)/-10.0f -lw*1.2,
+				col2eps(TxtSet.ColTxt), lw);
+			}
+		fprintf(oFile, "grestore\n");
+		}
+	else {
+		if(TxtSet.Style & TXS_SUB) iy += un2iy(TxtSet.fSize*0.6);
+		else if(TxtSet.Style & TXS_SUPER) iy -= un2iy(TxtSet.fSize*.2);
+		fx = ix2eps(ix);			fy = iy2eps(iy);
+		fprintf(oFile, "%s ", col2eps(CurrCol = TxtSet.ColTxt));
+		fprintf(oFile,"%.1f %.1f moveto (%s) show ", fx, fy, txt);
+		if(TxtSet.Style & TXS_UNDERLINE) {
+			fprintf(oFile, "\ncurrentpoint %.1f exch pop moveto", fy - lw*1.2);
+			fprintf(oFile, " %.1f %.1f lineto %s %.1f setlinewidth stroke\n", fx, fy - lw*1.2, 
+				col2eps(TxtSet.ColTxt), lw);
+			}
+		}
+	return true;
+}
+
+bool
+ExportEPS::oPolygon(POINT *pts, int cp, char *nam)
+{
+	int i, j;
+
+	if(cp <1) return false;
+	if(cp == 2) return oSolidLine(pts);
+	FlushSolLines();
+	for(i = cp-1, j = 0; i >= 0; i--, j++) {
+		if(!(j & 0x07)) fprintf(oFile, "\n");
+		fprintf(oFile, " %.1f %.1f", ix2eps(pts[i].x), iy2eps(pts[i].y));
+		}
+	fprintf(oFile, " moveto %d {lineto} repeat %s fill ", cp-1, col2eps(CurrCol = dFillCol));
+	if(hgo) hgo->oPolygon(pts, cp);
+	return oPolyline(pts, cp);
+}
+
+float
+ExportEPS::ix2eps(int x)
+{
+	return (float)x/10.0f;
+}
+
+float
+ExportEPS::iy2eps(int y)
+{
+	return (float)y/-10.0f + (float)BoundBox.bottom;
+}
+
+char *
+ExportEPS::col2eps(DWORD color)
+{
+	static char txt[50];
+	float r, g, b;
+
+	r = (float)(color & 0xff)/255.0f;
+	g = (float)((color>>8)&0xff)/255.0f;
+	b = (float)((color>>16)&0xff)/255.0f;
+#ifdef USE_WIN_SECURE
+	sprintf_s(txt, 50, "%g %g %g setrgbcolor", r, g, b);
+#else
+	sprintf(txt, "%g %g %g setrgbcolor", r, g, b);
+#endif
+	return txt;
+}
+
+void
+ExportEPS::FlushSolLines()
+{
+	int i, j;
+	
+	if(nSolLines <1) {
+		nSolLines = 0;
+		return;
+		}
+	if(nSolLines == 1) {
+		fprintf(oFile, "\n%.1f %.1f moveto %.1f %.1f lineto stroke ", 
+			SolLines[0].Ymin, SolLines[0].Ymax, SolLines[0].Xmin, SolLines[0].Xmax);
+		nSolLines = 0;
+		return;
+		}
+	for(i = nSolLines-1, j = 0; i >=0; i--, j++) {
+		if(!(j & 0x03)) fprintf(oFile, "\n");
+		fprintf(oFile, " %.1f %.1f %.1f %.1f", SolLines[i].Xmin, SolLines[i].Xmax,
+			SolLines[i].Ymin, SolLines[i].Ymax);
+		}
+	if(j > 8 && ((j & 0x3) >=2 || (j & 0x03) == 0)) fprintf(oFile, "\n");
+	fprintf(oFile, " %d {moveto lineto} repeat stroke ", nSolLines);
+	nSolLines = 0;
+}
+
+void
+ExportEPS::AddSolLine(float x1, float y1, float x2, float y2)
+{
+	if(nSolLines >= 2000) FlushSolLines();
+	SolLines[nSolLines].Ymin = x1;	SolLines[nSolLines].Ymax = y1;
+	SolLines[nSolLines].Xmin = x2;	SolLines[nSolLines].Xmax = y2;
+	nSolLines++;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Entry point to export graph to *.eps
+void DoExportEps(GraphObj *g, char *FileName, DWORD flags)
+{
+	ExportEPS *ex;
+
+	ex = new ExportEPS(g, FileName, flags);
+	if(ex->StartPage()) {
+		g->DoPlot(ex);
+		ex->EndPage();
+		}
+	delete(ex);
+}
diff --git a/Fileio.cpp b/Fileio.cpp
index c224279..13a88b3 100755
--- a/Fileio.cpp
+++ b/Fileio.cpp
@@ -1,3951 +1,4034 @@
-//FileIO.cpp, Copyright (c) 2001-2007 R.Lackner
-//read/write graphic objects
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-#include "rlplot.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <math.h>
-#include <ctype.h>
-#include <fcntl.h>				//file open flags
-#include <sys/stat.h>			//I/O flags
-
-#ifdef _WINDOWS
-	#include <io.h>					//for read/write
-#else
-	#define O_BINARY 0x0
-	#include <unistd.h>
-#endif
-
-extern GraphObj *CurrGO;			//Selected Graphic Objects
-extern Default defs;
-extern int dlgtxtheight;
-extern char TmpTxt[];
-extern int cPlots;
-GraphObj *LastOpenGO;
-
-static notary *Notary = 0L;
-static ReadCache *Cache = 0L;
-
-unsigned long cObsW;				//count objects written
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// graphic input/output is driven by tables based on the descIO template
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-typedef struct {
-	char *label;
-	unsigned short type;
-	void *ptr;
-	long *count;
-	}descIO;
-
-//the type member of descIO describes the following data types pointed to by ptr
-enum {typNONE, typNZINT, typINT, typLFLOAT, typNZLFLOAT, 
-	typDWORD, typFRECT, typNZLFPOINT, typLFPOINT, typPOINT3D,
-	typAXDEF, typPTRAXDEF, typLINEDEF, typFILLDEF, typGOBJ,	typOBJLST,
-	typFPLST, typFPLST3D, typIPLST, typTEXT, typTXTDEF, typPTRTXTDEF, 
-	typLAST = 0x100};
-
-static char *ptr =0L;
-static int cbOut, sizeOut = 0, iFile = -1;
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// output graph to file, elementary functions
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static int OpenOutputFile(char *file)
-{
-	time_t ti;
-
-	if(ptr) free(ptr);
-	ptr = 0L;		cbOut = 0;		ti = time(0L);
-	if(file && BackupFile(file)) {
-#ifdef USE_WIN_SECURE
-		if(_sopen_s(&iFile, file, O_RDWR | O_BINARY | O_CREAT | O_TRUNC, 
-			0x40, S_IWRITE) || iFile < 0){
-			ErrorBox("Open failed for output file");
-			return -1;
-			}
-#else
-		if(-1 ==(iFile = open(file, O_RDWR | O_BINARY | O_CREAT | O_TRUNC,
-			S_IWRITE | S_IREAD))){
-			ErrorBox("Open failed for output file");
-			return -1;
-			}
-#endif
-		ptr = (char *)malloc(sizeOut = 2000);
-		if(!ptr) goto openerr;
-		cbOut = rlp_strcpy(ptr, 20, ";RLP 1.0\n;File \"");
-		add_to_buff(&ptr, &cbOut, &sizeOut, file, 0);
-		add_to_buff(&ptr, &cbOut, &sizeOut, "\" created by RLPlot version "SZ_VERSION" for ", 0);
-#ifdef _WINDOWS
-		add_to_buff(&ptr, &cbOut, &sizeOut, "Windows", 7);
-#else
-		add_to_buff(&ptr, &cbOut, &sizeOut, "Qt", 2);
-#endif
-		add_to_buff(&ptr, &cbOut, &sizeOut, "\n;Date/Time: ", 13);
-#ifdef USE_WIN_SECURE
-		ctime_s(ptr+cbOut, 30, &ti);	cbOut += 25;
-#else
-		add_to_buff(&ptr, &cbOut, &sizeOut, ctime(&ti), 25);
-#endif
-		}
-	return iFile;
-openerr:
-	ptr = 0L;	cbOut = sizeOut = 0;
-#ifdef USE_WIN_SECURE
-	if(iFile >=0) _close(iFile);
-#else
-	if(iFile >=0) close(iFile);
-#endif
-	return iFile = -1;
-}
-
-static void CloseOutputFile()
-{
-	if(iFile >= 0){
-#ifdef USE_WIN_SECURE
-		if(cbOut) _write(iFile, ptr, cbOut);
-		_close(iFile);
-#else
-		if(cbOut) write(iFile, ptr, cbOut);
-		close(iFile);
-#endif
-		}
-	if(ptr) free(ptr);
-	cbOut = sizeOut = 0;
-	ptr = 0L;		iFile = -1;
-}
-
-static void WriteTypObjLst(GraphObj **obs, int c1)
-{
-	int i, j, no, n, *idx;
-	
-	if(!obs || !(idx=(int*)malloc(c1*sizeof(int)))) return;
-	for(i = no = 0; i < c1; i++) {
-		if(j = Notary->RegisterGO(obs[i])) idx[no++] = j;
-		}
-	add_to_buff(&ptr, &cbOut, &sizeOut, "(", 1);
-	add_int_to_buff(&ptr, &cbOut, &sizeOut, no, false, 0);
-	add_to_buff(&ptr, &cbOut, &sizeOut, "){", 2);
-	for(i = 0; i < no; i += 16) {
-		if(i) add_to_buff(&ptr, &cbOut, &sizeOut, "   ", 3);
-		for(j = 0; (n=i+j) < no && j < 16; j++) {
-			add_int_to_buff(&ptr, &cbOut, &sizeOut, idx[n], j != 0, 0);
-			}
-		if(n >= no) add_to_buff(&ptr, &cbOut, &sizeOut, "}", 1);
-		else add_to_buff(&ptr, &cbOut, &sizeOut, "\n", 1);
-		}
-}
-
-static void WriteTypIpLst(POINT *ppt, int count)
-{
-	long i, j, c, n;
-
-	if(!ppt) return;
-	add_to_buff(&ptr, &cbOut, &sizeOut, "(", 1);
-	add_int_to_buff(&ptr, &cbOut, &sizeOut, count, false, 0);
-	add_to_buff(&ptr, &cbOut, &sizeOut, "){", 2);
-	for(i = 0; i < count; i += 8) {
-		for(j = c = 0; (n = i+j) <count && j < 8; j++) {
-			add_int_to_buff(&ptr, &cbOut, &sizeOut, ppt[n].x, (j != 0), 0);
-			add_int_to_buff(&ptr, &cbOut, &sizeOut, ppt[n].y, true, 0);
-			}
-		if(n >= count) add_to_buff(&ptr, &cbOut, &sizeOut, "}", 1);
-		else add_to_buff(&ptr, &cbOut, &sizeOut, "\n", 1);
-		}
-}
-
-static void WriteTypFpLst(lfPOINT *ppt, long count, bool bPar)
-{
-	int i, j, n;
-
-	if (bPar){
-		if(!ppt) return;
-		add_to_buff(&ptr, &cbOut, &sizeOut, "(", 1);
-		add_int_to_buff(&ptr, &cbOut, &sizeOut, count, false, 0);
-		add_to_buff(&ptr, &cbOut, &sizeOut, "){", 2);
-		}
-	else {
-		if(!ppt) count = 0;
-		add_int_to_buff(&ptr, &cbOut, &sizeOut, count, true, 0);
-		if(!count) {
-			add_to_buff(&ptr, &cbOut, &sizeOut, "\n", 1);
-			return;
-			}
-		add_to_buff(&ptr, &cbOut, &sizeOut, " {", 2);
-		}
-	for(i = 0; i < count; i += 8) {
-		for(j = 0; (n = i+j) <count && j < 8; j++) {
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ppt[n].fx, (j != 0));
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ppt[n].fy, true);
-			}
-		if(n >= count) add_to_buff(&ptr, &cbOut, &sizeOut, "}", 1);
-		else add_to_buff(&ptr, &cbOut, &sizeOut, "\n", 1);
-		}
-}
-
-static void WriteTypFpLst3D(fPOINT3D *ppt, int count)
-{
-	long i, j, c, n;
-
-	if(!ppt) return;
-	add_to_buff(&ptr, &cbOut, &sizeOut, "(", 1);
-	add_int_to_buff(&ptr, &cbOut, &sizeOut, count, false, 0);
-	add_to_buff(&ptr, &cbOut, &sizeOut, "){", 2);
-	for(i = 0; i < count; i +=5) {
-		for(j = c = 0; (n =i+j) <count && j < 5; j++) {
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ppt[n].fx, (j != 0));
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ppt[n].fy, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ppt[n].fz, true);
-			}
-		if(n >= count) add_to_buff(&ptr, &cbOut, &sizeOut, "}", 1);
-		else add_to_buff(&ptr, &cbOut, &sizeOut, "\n", 1);
-		}
-}
-
-static char * esc_str = 0L;
-static int esc_str_size = 0;
-static void WriteEscString(char *txt)
-{
-	int i, j, l, lim = 60;
-
-	if(!txt || !txt[0]) return;
-	l = (int)strlen(txt);
-	if((l+10) > esc_str_size) if(!(esc_str = (char*)realloc(esc_str, esc_str_size = (l+100))))return;
-	j = 0;	esc_str[j++] = '"';
-	for(i = 0; txt[i]; i++) {
-		switch(txt[i]) {
-		case '\\':
-			esc_str[j++] = '\\';	esc_str[j++] = '\\';
-			break;
-		case '\n':
-			esc_str[j++] = '\\';	esc_str[j++] = 'n';
-			break;
-		default:	
-			if(((unsigned char*)txt)[i] >= ' ') esc_str[j++] = txt[i];
-			}
-		if(j > (esc_str_size -10)) esc_str = (char*)realloc(esc_str, (esc_str_size += 100));
-		if(j > lim && (l-i) > 3) {
-			esc_str[j++] = '"';		esc_str[j++] = '\\';
-			esc_str[j++] = '\n';	esc_str[j++] = ' ';
-			esc_str[j++] = ' ';		esc_str[j++] = ' ';
-			esc_str[j++] = '"';
-			lim += 60;
-			}
-		}
-	esc_str[j++] = '"';
-	add_to_buff(&ptr, &cbOut, &sizeOut, esc_str, j);
-}
-
-bool ExecOutput(int id, char *Class, descIO *Desc)
-{
-	int i, last;
-	fRECT *fr;
-	AxisDEF *ax;
-	LineDEF *ld;
-	FillDEF *fd;
-	TextDEF *tx;
-
-	add_to_buff(&ptr, &cbOut, &sizeOut, "\n[", 2);
-	add_int_to_buff(&ptr, &cbOut, &sizeOut, id, false, 0);
-	add_to_buff(&ptr, &cbOut, &sizeOut, "=", 1);
-	add_to_buff(&ptr, &cbOut, &sizeOut, Class, 0);
-	add_to_buff(&ptr, &cbOut, &sizeOut, "]\n", 2);
-	cObsW++;
-	for(i = 0; Desc[i].label; i++) {
-		if(ptr[cbOut-1] != '\n') add_to_buff(&ptr, &cbOut, &sizeOut, "\n", 1);
-		last = cbOut;
-		add_to_buff(&ptr, &cbOut, &sizeOut, Desc[i].label, 0);
-		add_to_buff(&ptr, &cbOut, &sizeOut, "=", 1);
-		switch(Desc[i].type & 0xff){
-		case typNZINT:
-			if(!(*(int*)Desc[i].ptr)) {
-				cbOut = last;			break;
-				}
-			//if not zero value continue as if int
-		case typINT:
-			add_int_to_buff(&ptr, &cbOut, &sizeOut, *(int*)Desc[i].ptr, true, 0);
-			break;
-		case typNZLFLOAT:
-			if(*((double*)Desc[i].ptr) == 0.0) {
-				cbOut = last;			break;
-				}
-			//if not zero or negative continue as if float
-		case typLFLOAT:
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, *(double*)Desc[i].ptr, true);
-			break;
-		case typDWORD:
-			add_hex_to_buff(&ptr, &cbOut, &sizeOut, *(DWORD *)Desc[i].ptr, true);
-			break;
-		case typFRECT:
-			fr = (fRECT*)Desc[i].ptr;
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, fr->Xmin, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, fr->Ymax, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, fr->Xmax, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, fr->Ymin, true);
-			break;
-		case typNZLFPOINT:
-			if(((lfPOINT *)Desc[i].ptr)->fx == ((lfPOINT *)Desc[i].ptr)->fy &&
-				((lfPOINT *)Desc[i].ptr)->fx == 0.0f){
-				cbOut = last;			break;
-				}
-			//if not zero continue as if fPOINT
-		case typLFPOINT:
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ((lfPOINT *)Desc[i].ptr)->fx, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ((lfPOINT *)Desc[i].ptr)->fy, true);
-			break;
-		case typPOINT3D:
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ((fPOINT3D *)Desc[i].ptr)->fx, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ((fPOINT3D *)Desc[i].ptr)->fy, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ((fPOINT3D *)Desc[i].ptr)->fz, true);
-			break;
-		case typAXDEF:		case typPTRAXDEF:
-			ax = (Desc[i].type & 0xff) == typAXDEF ? (AxisDEF *)Desc[i].ptr : *(AxisDEF **)Desc[i].ptr;
-			//we do not set ownership: reconstruct after read
-			add_hex_to_buff(&ptr, &cbOut, &sizeOut, ax->flags, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->min, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->max, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->loc[0].fx, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->loc[0].fy, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->loc[0].fz, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->loc[1].fx, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->loc[1].fy, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->loc[1].fz, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->Start, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->Step, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->Center.fx, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->Center.fy, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->Radius, true);
-			WriteTypFpLst(ax->breaks, ax->nBreaks, false);
-			break;
-		case typLINEDEF:
-			ld = (LineDEF *)Desc[i].ptr;
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ld->width, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ld->patlength, true);
-			add_hex_to_buff(&ptr, &cbOut, &sizeOut, ld->color, true);
-			add_hex_to_buff(&ptr, &cbOut, &sizeOut, ld->pattern, true);
-			break;
-		case typFILLDEF:
-			fd = (FillDEF *)Desc[i].ptr;
-			//we set the 'hatch' member to zero: reconstruct after read
-			add_int_to_buff(&ptr, &cbOut, &sizeOut, fd->type, true, 0);
-			add_hex_to_buff(&ptr, &cbOut, &sizeOut, fd->color, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, fd->scale, true);
-			add_to_buff(&ptr, &cbOut, &sizeOut, " 0x0", 4);
-			add_hex_to_buff(&ptr, &cbOut, &sizeOut, fd->color2, true);
-			break;
-		case typGOBJ:
-			if(*(GraphObj **)(Desc[i].ptr)) add_int_to_buff(&ptr, &cbOut, &sizeOut, 
-				Notary->RegisterGO(*(GraphObj **)(Desc[i].ptr)), true, 0);
-			else cbOut = last;
-			break;
-		case typOBJLST:
-			if(!*(GraphObj ***)(Desc[i].ptr)){
-				cbOut = last;				break;
-				}
-			WriteTypObjLst(*(GraphObj ***)Desc[i].ptr, Desc[i].count ? *Desc[i].count : 0);
-			break;
-		case typIPLST:
-			if(!*(POINT**)(Desc[i].ptr)){
-				cbOut = last;				break;
-				}
-			WriteTypIpLst(*(POINT**)Desc[i].ptr, Desc[i].count ? *Desc[i].count : 0L);
-			break;
-		case typFPLST:
-			if(!*(lfPOINT**)(Desc[i].ptr)){
-				cbOut = last;				break;
-				}
-			WriteTypFpLst(*(lfPOINT**)Desc[i].ptr, Desc[i].count ? *Desc[i].count : 0L, true);
-			break;
-		case typFPLST3D:
-			if(!*(fPOINT3D**)(Desc[i].ptr)){
-				cbOut = last;				break;
-				}
-			WriteTypFpLst3D(*(fPOINT3D**)Desc[i].ptr, Desc[i].count ? *Desc[i].count : 0);
-			break;
-		case typTEXT:
-			if(!*(char**)(Desc[i].ptr)) cbOut = last;
-			else WriteEscString(*((char**)Desc[i].ptr));
-			break;
-		case typTXTDEF:		case typPTRTXTDEF:
-			tx = (Desc[i].type &0xff) == typTXTDEF ? (TextDEF *)Desc[i].ptr : *(TextDEF **)Desc[i].ptr;
-			if(!tx) {
-				cbOut = last;				break;
-				}
-			add_hex_to_buff(&ptr, &cbOut, &sizeOut, tx->ColTxt, true);
-			add_hex_to_buff(&ptr, &cbOut, &sizeOut, tx->ColBg, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, tx->fSize, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, tx->RotBL, true);
-			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, tx->RotCHAR, true);
-			add_int_to_buff(&ptr, &cbOut, &sizeOut, tx->Align, true, 0);
-			add_int_to_buff(&ptr, &cbOut, &sizeOut, tx->Mode, true, 0);
-			add_int_to_buff(&ptr, &cbOut, &sizeOut, tx->Style, true, 0);
-			add_int_to_buff(&ptr, &cbOut, &sizeOut, tx->Font, true, 0);
-			if(tx->text && tx->text[0]) {
-				add_to_buff(&ptr, &cbOut, &sizeOut, " \"", 2);
-				add_to_buff(&ptr, &cbOut, &sizeOut, tx->text, 0);
-				add_to_buff(&ptr, &cbOut, &sizeOut, "\"\n", 2);
-				}
-			break;
-			}
-		if(Desc[i].type & typLAST) break;
-		}
-	if(ptr[cbOut-1] != '\n') add_to_buff(&ptr, &cbOut, &sizeOut, "\n", 1);
-	return true;
-}
-
-void ReadTypIpLst(POINT *ptr, long count, unsigned char *first)
-{
-	int i, j, k, f[20];
-
-	if(!ptr || !first) return;
-#ifdef USE_WIN_SECURE
-	k = sscanf_s((char*)first, "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d", 
-		&f[0], &f[1], &f[2], &f[3], &f[4], &f[5], &f[6], &f[7], &f[8], &f[9],
-		&f[10], &f[11], &f[12], &f[13], &f[14], &f[15], &f[16], &f[17], &f[18], &f[19]);
-#else
-	k = sscanf((char*)first, "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d", 
-		&f[0], &f[1], &f[2], &f[3], &f[4], &f[5], &f[6], &f[7], &f[8], &f[9],
-		&f[10], &f[11], &f[12], &f[13], &f[14], &f[15], &f[16], &f[17], &f[18], &f[19]);
-#endif
-	for(i = 0,  j = 0; j < k && i < count; i++, j += 2) {
-		ptr[i].x = f[j];	ptr[i].y = f[j+1];
-		}
-	while (i < count) {
-		if(!Cache->GetInt(&ptr[i].x) || !Cache->GetInt(&ptr[i].y)) return;
-		i++;
-		}
-}
-
-void ReadTypFpLst(lfPOINT *ptr, long count, unsigned char *first)
-{
-	double f[20];
-	int j, k;
-	long i;
-
-	if(!ptr || !first) return;
-#ifdef USE_WIN_SECURE
-	k = sscanf_s((char*)first, "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf", 
-		&f[0], &f[1], &f[2], &f[3], &f[4], &f[5], &f[6], &f[7], &f[8], &f[9],
-		&f[10], &f[11], &f[12], &f[13], &f[14], &f[15], &f[16], &f[17], &f[18], &f[19]);
-#else
-	k = sscanf((char*)first, "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf", 
-		&f[0], &f[1], &f[2], &f[3], &f[4], &f[5], &f[6], &f[7], &f[8], &f[9],
-		&f[10], &f[11], &f[12], &f[13], &f[14], &f[15], &f[16], &f[17], &f[18], &f[19]);
-#endif
-	for(i = 0,  j = 0; j < k && i < count; i++, j += 2) {
-		ptr[i].fx = f[j];	ptr[i].fy = f[j+1];
-		}
-	while (i < count) {
-		if(!Cache->GetFloat(&ptr[i].fx) || !Cache->GetFloat(&ptr[i].fy)) return;
-		i++;
-		}
-}
-
-void ReadTypFpLst3D(fPOINT3D *ptr, long count, unsigned char *first)
-{
-	double f[21];
-	int j, k;
-	long i;
-
-	if(!ptr || !first) return;
-#ifdef USE_WIN_SECURE
-	k = sscanf_s((char*)first, "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
-		&f[0], &f[1], &f[2], &f[3], &f[4], &f[5], &f[6], &f[7], &f[8], &f[9],
-		&f[10], &f[11], &f[12], &f[13], &f[14], &f[15], &f[16], &f[17], &f[18], &f[19], &f[20]);
-#else
-	k = sscanf((char*)first, "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
-		&f[0], &f[1], &f[2], &f[3], &f[4], &f[5], &f[6], &f[7], &f[8], &f[9],
-		&f[10], &f[11], &f[12], &f[13], &f[14], &f[15], &f[16], &f[17], &f[18], &f[19], &f[20]);
-#endif
-	for(i = 0,  j = 0; j < k && i < count; i++, j += 3) {
-		ptr[i].fx = f[j];	ptr[i].fy = f[j+1];		ptr[i].fz = f[j+2];
-		}
-	while (i < count) {
-		if(!Cache->GetFloat(&ptr[i].fx) || !Cache->GetFloat(&ptr[i].fy) ||
-			!Cache->GetFloat(&ptr[i].fz)) return;
-		i++;
-		}
-}
-
-void TranslateEscChar(char *txt)
-{
-	int i, j;
-
-	if(txt && txt[0]) {
-		for(i = j = 0; txt[i]; i++) {
-			if(txt[i] == '\\') {
-				switch(txt[i+1]) {
-				case 'n':
-					txt[j++] = 0x0a;	i++;	break;
-				case 'r':
-					txt[j++] = 0x0d;	i++;	break;
-				case '"':	case 0x27:			case '\\':
-					txt[j++] = txt[++i];		break;
-				default:
-					txt[j++] = txt[i];	break;
-					}
-				}
-			else txt[j++] = txt[i];
-			}
-		txt[j] = 0;
-		}
-}
-
-void AddLines(char **txt)
-{
-	char tmp[1000], *ntxt;
-	bool mlines;
-	int i, j, cb = (int)strlen(*txt);
-
-	do {
-		mlines = false;
-		Cache->ReadLine(tmp, sizeof(tmp));
-		for(i = (int)strlen(tmp); i > 0 &&(tmp[i-1] < 33 || (tmp[i-1] == 
-			'"' && tmp[i-2] != '\\') ||	(tmp[i-1] == '\\' && 
-			(mlines = true))); tmp[--i] = 0);
-		for(i = 0; tmp[i] && (tmp[i] < 33 || tmp[i] == '"'); i++);
-		TranslateEscChar(tmp);
-		if(tmp[0] && (j = (int)strlen(tmp+i)) && (ntxt = (char*)realloc(*txt, cb + j + 1))) {
-			rlp_strcpy(ntxt+cb, j+1, tmp+i);
-			cb += j;	*(txt) = ntxt;
-			}
-		} while (mlines);
-}
-
-bool ExecInput(descIO *Desc)
-{
-	unsigned char c, tmp[1000], tmp2[20];
-	int i, j, k, l;
-	bool match, mlines;
-	int il, jl;
-	AxisDEF *ax;
-	POINT *lp;
-	lfPOINT *lfp;
-	fPOINT3D *lfp3d;
-	LineDEF *ld;
-	FillDEF *fd;
-	fRECT *fr;
-	GraphObj **gobs;
-	TextDEF *tx;
-
-	if(!Desc || !Desc[0].label) return false;
-	for(j = k = 0; ; ) {
-		do{
-			c = Cache->Getc();
-			switch (c) {
-			case '[':					//next object
-			case 0:						//probably eof
-				return true;
-			case '}':					//a lists hang over
-				c = Cache->Getc();
-				break;
-				}
-		} while(c <33);
-		for(i = 1, tmp[0] = c; i < sizeof(tmp) && '=' != (tmp[i] = Cache->Getc()); i++){
-			if(tmp[i] < 32 && tmp[i]) i = -1;			//some error conditions
-			else if(!tmp[i] && Cache->eof) return true;
-			else if(tmp[i] == '[') return true;
-			}
-		tmp[i] = 0;
-		match = mlines = false;
-		do {
-			if(0 == strcmp((char*)tmp, Desc[j].label)) {
-				Cache->ReadLine((char*)tmp, sizeof(tmp));
-				switch(Desc[j].type & 0xff){
-				case typNZINT:
-				case typINT:
-#ifdef USE_WIN_SECURE
-					sscanf_s((char*)tmp, "%d", (int*)Desc[j].ptr);
-#else
-					sscanf((char*)tmp, "%d", (int*)Desc[j].ptr);
-#endif
-					break;
-				case typNZLFLOAT:		case typLFLOAT:
-#ifdef USE_WIN_SECURE
-					sscanf_s((char*)tmp, "%lf", (double*)Desc[j].ptr);
-#else
-					sscanf((char*)tmp, "%lf", (double*)Desc[j].ptr);
-#endif
-					break;
-				case typDWORD:
-#ifdef USE_WIN_SECURE
-					sscanf_s((char*)tmp, "%x", (DWORD*)Desc[j].ptr);
-#else
-					sscanf((char*)tmp, "%x", (DWORD*)Desc[j].ptr);
-#endif
-					break;
-				case typFRECT:
-					fr = (fRECT*) Desc[j].ptr;
-#ifdef USE_WIN_SECURE
-					sscanf_s((char*)tmp, "%lf%lf%lf%lf", &fr->Xmin, &fr->Ymax, &fr->Xmax, &fr->Ymin);
-#else
-					sscanf((char*)tmp, "%lf%lf%lf%lf", &fr->Xmin, &fr->Ymax, &fr->Xmax, &fr->Ymin);
-#endif
-					break;
-				case typNZLFPOINT:		case typLFPOINT:
-					lfp = (lfPOINT*) Desc[j].ptr;
-#ifdef USE_WIN_SECURE
-					sscanf_s((char*)tmp, "%lf%lf", &lfp->fx, &lfp->fy);
-#else
-					sscanf((char*)tmp, "%lf%lf", &lfp->fx, &lfp->fy);
-#endif
-					break;
-				case typPOINT3D:
-					lfp3d = (fPOINT3D*) Desc[j].ptr;
-#ifdef USE_WIN_SECURE
-					sscanf_s((char*)tmp, "%lf%lf%lf", &lfp3d->fx, &lfp3d->fy, &lfp3d->fz);
-#else
-					sscanf((char*)tmp, "%lf%lf%lf", &lfp3d->fx, &lfp3d->fy, &lfp3d->fz);
-#endif
-					break;
-				case typPTRAXDEF:
-				case typAXDEF:
-					ax = (Desc[j].type & 0xff) == typAXDEF ? (AxisDEF *)Desc[j].ptr : *(AxisDEF **)Desc[j].ptr;
-					//pointer for typPTRAXDEF and memory allocated by the Axis module!
-					if(!ax) break;
-#ifdef USE_WIN_SECURE
-					sscanf_s((char*)tmp, "%x%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%d", &ax->flags, &ax->min, &ax->max,
-						&ax->loc[0].fx,	&ax->loc[0].fy, &ax->loc[0].fz, &ax->loc[1].fx,
-						&ax->loc[1].fy, &ax->loc[1].fz, &ax->Start, &ax->Step, &ax->Center.fx, 
-						&ax->Center.fy, &ax->Radius, &ax->nBreaks);
-#else
-					sscanf((char*)tmp, "%x%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%d", &ax->flags, &ax->min, &ax->max,
-						&ax->loc[0].fx,	&ax->loc[0].fy, &ax->loc[0].fz, &ax->loc[1].fx,
-						&ax->loc[1].fy, &ax->loc[1].fz, &ax->Start, &ax->Step, &ax->Center.fx, 
-						&ax->Center.fy, &ax->Radius, &ax->nBreaks);
-#endif
-					if(ax->nBreaks) {
-						ax->breaks = (lfPOINT*)calloc(ax->nBreaks, sizeof(lfPOINT));
-						for(i = 0; tmp[i] && tmp[i-1] != '{'; i++);
-						if(tmp[i]) {
-							ReadTypFpLst(ax->breaks, ax->nBreaks, (unsigned char*)tmp+i);
-							SortAxisBreaks(ax);
-							}
-						}
-					break;
-				case typLINEDEF:
-					ld = (LineDEF*) Desc[j].ptr;
-#ifdef USE_WIN_SECURE
-					sscanf_s((char*)tmp,"%lf%lf%x%x", &ld->width, &ld->patlength, &ld->color, &ld->pattern);
-#else
-					sscanf((char*)tmp,"%lf%lf%x%x", &ld->width, &ld->patlength, &ld->color, &ld->pattern);
-#endif
-					break;
-				case typFILLDEF:
-					fd = (FillDEF*) Desc[j].ptr;
-#ifdef USE_WIN_SECURE
-					sscanf_s((char*)tmp, "%d%x%lf%x%x", &fd->type, &fd->color, &fd->scale, &fd->hatch, &fd->color2);
-#else
-					sscanf((char*)tmp, "%d%x%lf%x%x", &fd->type, &fd->color, &fd->scale, &fd->hatch, &fd->color2);
-#endif
-					fd->hatch = 0L;
-					break;
-				case typGOBJ:
-					*(GraphObj**)(Desc[j].ptr) = Notary->PopGO(atol((char*)tmp));
-					break;
-				case typOBJLST:
-					for(i = 0; i < 10 && tmp[i] != '(' && tmp[i]; i++);
-					if(!tmp[i]) break;
-#ifdef USE_WIN_SECURE
-					if(sscanf_s((char*)tmp+i+1, "%ld", &il) && il) {
-#else
-					if(sscanf((char*)tmp+i+1, "%ld", &il) && il) {
-#endif
-						*Desc[j].count = il;
-						if(!*(GraphObj***)(Desc[j].ptr)){
-							*(GraphObj***)(Desc[j].ptr) = (GraphObj**)calloc(il, sizeof(GraphObj*));
-							}
-						if((gobs = *(GraphObj***)(Desc[j].ptr))){
-							i += 4;
-							while(tmp[i-1] != '{') i++; while(tmp[i-1] < 33) i++;
-#ifdef USE_WIN_SECURE
-							strcat_s((char*)tmp, 1000, " ");
-#else
-							strcat((char*)tmp, " ");
-#endif
-							for( ;il >0; il--) {
-								for(l = 0; l < sizeof(tmp2); ){
-									if(tmp[i]) c = tmp[i++];
-									else if(!(c = Cache->Getc())) break;
-									if(c >='0' && c <='9') tmp2[l++] = c;
-									else {
-										tmp2[l] = 0;
-										if(l)break;
-										}
-									}
-#ifdef USE_WIN_SECURE
-								sscanf_s((char*)tmp2, "%d", &jl);
-#else
-								sscanf((char*)tmp2, "%d", &jl);
-#endif
-								*gobs++ = Notary->PopGO(jl);
-								if(c == '}') break;
-								}
-							}
-						}	
-					break;
-				case typIPLST:
-					for(i = 0; i < 10 && tmp[i] != '(' && tmp[i]; i++);
-					if(!tmp[i]) break;
-#ifdef USE_WIN_SECURE
-					if(sscanf_s((char*)tmp+i+1, "%d", &il) && il) {
-#else
-					if(sscanf((char*)tmp+i+1, "%d", &il) && il) {
-#endif
-						*Desc[j].count = il;
-						if(!*(POINT**)(Desc[j].ptr)){
-							*(POINT**)(Desc[j].ptr) = (POINT*)calloc(il, sizeof(POINT));
-							}
-						if(!(lp = *(POINT**)(Desc[j].ptr)))return false;
-						while(tmp[i-1] != '{') i++; while(tmp[i-1] < 33) i++;
-						ReadTypIpLst(lp, il, (unsigned char*)tmp+i);
-						}
-					break;
-				case typFPLST:
-					for(i = 0; i < 10 && tmp[i] != '(' && tmp[i]; i++);
-					if(!tmp[i]) break;
-#ifdef USE_WIN_SECURE
-					if(sscanf_s((char*)tmp+i+1, "%d", &il) && il) {
-#else
-					if(sscanf((char*)tmp+i+1, "%d", &il) && il) {
-#endif
-						*Desc[j].count = il;
-						if(!*(lfPOINT**)(Desc[j].ptr)){
-							*(lfPOINT**)(Desc[j].ptr) = (lfPOINT*)calloc(il, sizeof(lfPOINT));
-							}
-						if(!(lfp = *(lfPOINT**)(Desc[j].ptr)))return false;
-						while(tmp[i-1] != '{') i++; while(tmp[i-1] < 33) i++;
-						ReadTypFpLst(lfp, il, (unsigned char*)tmp+i);
-						}
-					break;
-				case typFPLST3D:
-					for(i = 0; i < 10 && tmp[i] != '(' && tmp[i]; i++);
-					if(!tmp[i]) break;
-#ifdef USE_WIN_SECURE
-					if(sscanf_s((char*)tmp+i+1, "%d", &il) && il) {
-#else
-					if(sscanf((char*)tmp+i+1, "%d", &il) && il) {
-#endif
-						*Desc[j].count = il;
-						if(!*(fPOINT3D**)(Desc[j].ptr)){
-							*(fPOINT3D**)(Desc[j].ptr) = (fPOINT3D*)calloc(il, sizeof(fPOINT3D));
-							}
-						if(!Desc[j].ptr)return false;
-						while(tmp[i-1] != '{') i++; while(tmp[i-1] < 33) i++;
-						ReadTypFpLst3D(*(fPOINT3D**)(Desc[j].ptr), il, (unsigned char*)tmp+i);
-						}
-					break;
-				case typTEXT:
-					for(i = (int)strlen((char*)tmp); i > 0 &&(tmp[i-1] < 32 || (tmp[i-1] == 
-						'"' && tmp[i-2] != '\\') ||	(tmp[i-1] == '\\' && 
-						(mlines = true))); tmp[--i] = 0);
-					for(i = 0; tmp[i] && (tmp[i] < 33 || tmp[i] == '"'); i++);
-					TranslateEscChar((char*)tmp);
-					if(tmp[0]){
-						*(char**)(Desc[j].ptr) = (char*)memdup((char*)tmp+i, (int)strlen((char*)tmp+i)+1, 0);
-						if(mlines) AddLines((char**)(Desc[j].ptr));
-						}
-					break;
-				case typPTRTXTDEF:
-				case typTXTDEF:
-					tx = (Desc[j].type & 0xff) == typTXTDEF ? (TextDEF *)Desc[j].ptr : *(TextDEF **)Desc[j].ptr;
-					if(!tx) {
-						if((Desc[j].type & 0xff) == typTXTDEF) break;	//prabably wrong usage of typTXTDEF instad of
-																//    typPTRTXTDEF
-						tx = *(TextDEF **)(Desc[j].ptr) = (TextDEF*)calloc(1, sizeof(TextDEF));
-						if(!tx) return false;					//memory allocation error
-						}
-#ifdef USE_WIN_SECURE
-					sscanf_s((char*)tmp, "%x%x%lf%lf%lf%d%d%d%d", &tx->ColTxt, &tx->ColBg, 
-						&tx->fSize, &tx->RotBL, &tx->RotCHAR,
-						&tx->Align, &tx->Mode, &tx->Style, &tx->Font);
-#else
-					sscanf((char*)tmp, "%x%x%lf%lf%lf%d%d%d%d", &tx->ColTxt, &tx->ColBg, 
-						&tx->fSize, &tx->RotBL, &tx->RotCHAR,
-						&tx->Align, &tx->Mode, &tx->Style, &tx->Font);
-#endif
-					tx->iSize = 0;
-					for(i = (int)strlen((char*)tmp); i >0 && tmp[i] != '"'; i--);
-					if(i) {
-						tmp[i] = 0;
-						for(l = 0; l <i && tmp[l] != '"'; l++);
-						if(i && tmp[l+1]) tx->text = (char*)memdup((char*)tmp+l+1, (int)strlen((char*)tmp+l+1)+1, 0);
-						}
-					break;
-					}
-				match = true;			j++;	k++;
-				if(!Desc[j].label || (Desc[j-1].type & typLAST)) 
-					j = k = 0;	//rewind: items in file not sorted
-				}
-			else {
-				j++;
-				k++;
-				if(!Desc[j].label || (Desc[j-1].type & typLAST)) {				//Error:
-					if(k > j){						//  item not defined in Desc
-						match = true;				//  read parameters,
-						Cache->ReadLine((char*)tmp, sizeof(tmp));	//   then continue
-						}
-					j= 0;
-					}
-				}
-			}while(!match);
-		}
-}
-
-bool SaveGraphAs(GraphObj *g)
-{
-	char *name = 0L;
-	int i;
-	bool bRet = true;
-
-	if(Notary || !g) {
-		ErrorBox("Output pending or\nno graph.");
-		return false;
-		}
-	cObsW = 0;
-	Notary = new notary();
-	if(g->Id == GO_GRAPH || g->Id == GO_PAGE) {
-		if(((Graph*)g)->filename) name = ((Graph*)g)->filename;
-		}
-	name = SaveGraphAsName(name);
-	if (name && Notary) {
-		iFile = OpenOutputFile(name);
-		if(iFile >=0) {
-			if(g && g->FileIO(FILE_WRITE)){
-				if(g->Id == GO_GRAPH || g->Id == GO_PAGE) {
-					g->Command(CMD_FILENAME, name, 0L);
-					}
-				for(i = (int)strlen(name); i >=0 && name[i] != '/' && name[i] != '\\'; i--);
-				if(name[i]) i++;
-				g->Command(CMD_SETNAME, name+i, 0L);
-				g->Command(CMD_UPDHISTORY, 0L, 0L);
-				}
-			else ErrorBox("Could not write\ndata to file.");
-			}
-		else ErrorBox("Open failed for\noutput file.");
-		CloseOutputFile();
-		}
-	else bRet = false;
-	if(Notary) delete Notary;	Notary = 0L;
-	return bRet;
-}
-
-char *GraphToMem(GraphObj *g, long *size)
-{
-	static char *ret;
-
-	if(Notary || !g) {
-		ErrorBox("Output pending or\nno graph.");
-		return false;
-		}
-	cObsW = 0;				iFile = -1;
-	cbOut = sizeOut = 0;	ptr = 0L;
-	if (Notary = new notary()) {
-		if(g && g->FileIO(FILE_WRITE)){
-			//all done
-			}
-		delete Notary;					Notary = 0L;
-		if(ptr) ptr[cbOut] = 0;
-		ret = ptr;						if(size) *size = cbOut;
-		iFile = -1;
-		cbOut = sizeOut = 0;	ptr = 0L;
-		return ret;
-		}
-	return 0L;
-}
-
-void UpdGOfromMem(GraphObj *go, unsigned char *buff)
-{
-	int i=0;
-
-	if(!go || !buff) return;
-	iFile = -1;							cbOut = sizeOut = 0;	ptr = 0L;
-	for(i = 0; buff[i] && buff[i] != ']'; i++);
-	if(!buff[i])return;
-	for(; buff[i] && buff[i] <33; i++);
-	if(!buff[i] || i < 4) return;
-	if(!(Cache = new MemCache(buff+i-1))) return;
-	if ((Notary = new notary()) && go->Id > GO_UNKNOWN && go->Id < GO_DEFRW) {
-		//notary not needed but saver if tree exists
-		go->Command(CMD_FLUSH, 0L, 0L);
-		go->FileIO(INIT_VARS);			go->FileIO(FILE_READ);
-		delete Notary;					Notary = 0L;
-		}
-	delete Cache;		Cache = 0L;
-}
-
-bool OpenGraph(GraphObj *root, char *name, unsigned char *mem, bool bPaste)
-{
-	unsigned char c, tmp[80];
-	char debug[80];
-	int i, id, lid;
-	unsigned int hv;
-	GraphObj *go;
-	scaleINFO sc_info;
-
-	LastOpenGO = 0L;
-	if(Notary || Cache) {
-		ErrorBox("Output pending:\nRead Error.");
-		return false;
-		}
-	if(!(Notary = new notary()))return false;
-	if(mem) {
-		if(!(Cache = new MemCache(mem))) return false;
-		}
-	else if(Cache = new ReadCache()){
-		if(!Cache->Open(name)) {
-			delete Notary;		delete Cache;
-			Notary = 0L;		Cache = 0L;
-			ErrorBox("Error open file");
-			return false;
-			}
-		}
-	else return false;
-	//DEBUG: skipping header
-	do {
-		c = Cache->Getc();
-		} while(c && c != '[');
-	if(!c) goto ReadErr;
-	do {
-		for(i = 0; i < sizeof(tmp) && c != '=' && c; i++){
-			tmp[i] = c = Cache->Getc();
-			if(c == '[') i = -1;
-			}
-		if(!c) goto ReadErr;
-		tmp[i] = tmp[i-1] = 0;			id=0;
-#ifdef USE_WIN_SECURE
-		sscanf_s((char*)tmp, "%d", &id);
-#else
-		sscanf((char*)tmp, "%d", &id);
-#endif
-		if(!id) goto ReadErr;
-		//go to class name
-		while((tmp[0] = Cache->Getc())<31 && tmp[0]);
-		if(!tmp[0]) goto ReadErr;
-		for(i = 1; i < sizeof(tmp) && c!= ']' && c; i++)
-			tmp[i] = c = Cache->Getc();
-		if(!c) goto ReadErr;
-		tmp[i-1] = 0;
-		go = 0L;
-		hv = HashValue(tmp);
-		switch(hv) {
-		case 3895:		go = new Axis(FILE_READ);			break;
-		case 886:		go = new Bar(FILE_READ);			break;
-		case 81384:		go = new Symbol(FILE_READ);			break;
-		case 62229:		go = new Bubble(FILE_READ);			break;
-		case 948:		go = new Box(FILE_READ);			break;
-		case 15411:		go = new Arrow(FILE_READ);			break;
-		case 1052406:	go = new ErrorBar(FILE_READ);		break;
-		case 324566:	go = new Whisker(FILE_READ);		break;
-		case 1031437:	go = new DropLine(FILE_READ);		break;
-		case 4839:		go = new Tick(FILE_READ);			break;
-		case 16832:		go = new Label(FILE_READ);			break;
-		case 1071373:	go = new GridLine(FILE_READ);		break;
-		case 963085:	go = new DataLine(FILE_READ);		break;
-		case 61662266:	go = new DataPolygon(FILE_READ);	break;
-		case 435228:	go = new segment(FILE_READ);		break;
-		case 1741325:	go = new polyline(FILE_READ);		break;
-		case 435258:	go = new polygon(FILE_READ);		break;
-		case 92534:		go = new Bezier(FILE_READ);			break;
-		case 6888037:	go = new rectangle(FILE_READ);		break;
-		case 1780087:	go = new roundrec(FILE_READ);		break;
-		case 78813:		go = new Sphere(FILE_READ);			break;
-		case 15463:		go = new Brick(FILE_READ);			break;
-		case 69952:		go = new Line3D(FILE_READ);			break;
-		case 386257:	go = new ellipse(FILE_READ);		break;
-		case 95680:		go = new mLabel(FILE_READ);			break;
-		case 4819316:	go = new PlotScatt(FILE_READ);		break;
-		case 117848:	go = new xyStat(FILE_READ);			break;
-		case 15935312:	go = new BubblePlot(FILE_READ);		break;
-		case 247376:	go = new BoxPlot(FILE_READ);		break;
-		case 317384:	go = new StackBar(FILE_READ);		break;
-		case 1205932:	go = new PieChart(FILE_READ);		break;
-		case 16664:		go = new Graph(FILE_READ);			break;
-		case 25108:		go = new GoGroup(FILE_READ);		break;
-		case 300976:	go = new Scatt3D(FILE_READ);		break;
-		case 297280:	go = new Plane3D(FILE_READ);		break;
-		case 19227098:	go = new Regression(FILE_READ);		break;
-		case 297997:	go = new RegLine(FILE_READ);		break;
-		case 4318417:	go = new SDellipse(FILE_READ);		break;
-		case 4843600:	go = new PolarPlot(FILE_READ);		break;
-		case 977452:	go = new DensDisp(FILE_READ);		break;
-		case 4465:		go = new Page(FILE_READ);			break;
-		case 75120:		go = new Plot3D(FILE_READ);			break;
-		case 17142080:	go = new GridLine3D(FILE_READ);		break;
-		case 246688:	go = new Arrow3D(FILE_READ);		break;
-		case 75562:		go = new Ribbon(FILE_READ);			break;
-		case 16503104:	go = new DropLine3D(FILE_READ);		break;
-		case 28859579:	go = new svgOptions(FILE_READ);		break;
-		case 70259:		go = new Limits(FILE_READ);			break;
-		case 17145824:	go = new GridRadial(FILE_READ);		break;
-		case 1074714:	go = new Function(FILE_READ);		break;
-		case 256075:	go = new FitFunc(FILE_READ);		break;
-		case 273377:	go = new LegItem(FILE_READ);		break;
-		case 1053744:	go = new FreqDist(FILE_READ);		break;
-		case 68748:		go = new Legend(FILE_READ);			break;
-		case 66800:		go = new Grid3D(FILE_READ);			break;
-		case 967843:	go = new DefsRW(FILE_READ);			break;
-		case 66848:		go = new Func3D(FILE_READ);			break;
-		case 5001225:	go = new TextFrame(FILE_READ);		break;
-		case 4743132:	go = new NormQuant(FILE_READ);		break;
-		default:
-#ifdef USE_WIN_SECURE
-			sprintf_s(debug, 80, "Object %ld in file\n(Class = \"%s\")\nhash #%d\nis unknown.", id, tmp, hv);
-#else
-			sprintf(debug, "Object %ld in file\n(Class = \"%s\")\nhash #%d\nis unknown.", id, tmp, hv);
-#endif
-			InfoBox(debug);
-			}
-		if(go) {
-			if(((int)id) < 0) DeleteGO(go);		//temporary objects have id < 0
-			else if(!Notary->PushGO(lid = id, go)) DeleteGO(go);
-			}
-		if('[' != Cache->Lastc()) do {			//search next object
-			c = Cache->Getc();
-			} while(c && c != '[');
-		tmp[0] = 0;
-		}while (c);
-	Cache->Close();
-	if((LastOpenGO = go = Notary->PopGO(lid))) {
-		go->Command(CMD_SET_DATAOBJ, 0L, 0L);
-		delete Notary;		Notary = 0L;
-		if(bPaste && go->Id == GO_GRAPH) {
-			sc_info.sx.fx = -((Graph*)go)->GRect.Xmin;				sc_info.sy.fx = -((Graph*)go)->GRect.Ymin;
-			sc_info.sx.fy = sc_info.sy.fy = sc_info.sz.fy = 1.0;	sc_info.sz.fx = 0.0;
-			go->Command(CMD_SCALE, &sc_info, 0L);
-			sc_info.sx.fy = sc_info.sy.fy = sc_info.sz.fy = 1.0/go->GetSize(SIZE_SCALE);
-			sc_info.sx.fx = sc_info.sy.fx = sc_info.sz.fx = 0.0;
-			go->Command(CMD_SCALE, &sc_info, 0L);
-			}
-		if(bPaste && root->Command(CMD_PASTE_OBJ, (void *)go, 0L)) {
-			// object accepted
-			}
-		else if(go->Id < GO_GRAPH 
-			&& !(root->Command(CMD_DROP_PLOT, (void *)go, 0L))){
-			DeleteGO(go);	go = 0L;
-			}
-		else if(go->Id >= GO_GRAPH 
-			&& !(root->Command(CMD_DROP_GRAPH, (void *)go, 0L))){
-			DeleteGO(go);	go = 0L;
-			}
-		if(go) go->Command(CMD_FILENAME, name, 0L);
-		}
-	if(Notary) delete Notary;		Notary = 0L;
-	delete Cache;					Cache = 0L;
-	return true;
-
-ReadErr:
-	Cache->Close();	
-#ifdef USE_WIN_SECURE
-	if(iFile >= 0) _close(iFile);
-#else
-	if(iFile >= 0) close(iFile);
-#endif
-	iFile = -1;
-	delete Notary;		Notary = 0L;
-	delete Cache;		Cache = 0L;
-	if(!name || !defs.IniFile || strcmp(name, defs.IniFile))
-		ErrorBox("An error occured during read.");
-	return false;
-}
-
-bool InitVarsGO(descIO *Desc)
-{
-	int i;
-	AxisDEF *ax;
-	TextDEF *tx;
-
-	for(i = 0; Desc[i].label; i++) {
-		switch(Desc[i].type & 0xff) {
-		case typNZINT:
-		case typINT:
-			*(int*)Desc[i].ptr = 0;			
-			break;
-		case typNZLFLOAT:
-		case typLFLOAT:
-			*(double*)Desc[i].ptr = 0.0;
-			break;
-		case typDWORD:
-			*(DWORD*)Desc[i].ptr = 0x0L;
-			break;
-		case typFRECT:
-			((fRECT*)Desc[i].ptr)->Xmin = ((fRECT*)Desc[i].ptr)->Xmax =
-				((fRECT*)Desc[i].ptr)->Ymin = ((fRECT*)Desc[i].ptr)->Ymax = 0.0;
-			break;
-		case typNZLFPOINT:
-		case typLFPOINT:
-			((lfPOINT*)Desc[i].ptr)->fx = ((lfPOINT*)Desc[i].ptr)->fy = 0.0;
-			break;
-		case typPOINT3D:
-			((fPOINT3D*)Desc[i].ptr)->fx = ((fPOINT3D*)Desc[i].ptr)->fy =
-				((fPOINT3D*)Desc[i].ptr)->fz = 0.0;
-			break;
-		case typPTRAXDEF:
-		case typAXDEF:
-			ax = (Desc[i].type & 0xff) == typAXDEF ? (AxisDEF *)Desc[i].ptr : *(AxisDEF **)Desc[i].ptr;
-			if(!ax) break;
-			ax->owner = 0L;		ax->flags = 0L;		ax->breaks = 0L;	ax->nBreaks = 0;
-			ax->min = ax->max = ax->Start = ax->Step = 0.0f;
-			ax->loc[0].fx = ax->loc[0].fy = ax->loc[0].fz = 0.0f;
-			ax->loc[1].fx = ax->loc[1].fy = ax->loc[1].fz = 0.0f;
-			ax->Center.fx = ax->Center.fy = ax->Radius = 0.0f;
-			break;
-		case typLINEDEF:
-			((LineDEF*)Desc[i].ptr)->width = defs.GetSize(SIZE_HAIRLINE);
-			((LineDEF*)Desc[i].ptr)->patlength = defs.GetSize(SIZE_PATLENGTH);
-			((LineDEF*)Desc[i].ptr)->color = ((LineDEF*)Desc[i].ptr)->pattern = 0x0L;
-			break;
-		case typFILLDEF:
-			((FillDEF*)Desc[i].ptr)->type = FILL_NONE;
-			((FillDEF*)Desc[i].ptr)->color = 0x00ffffffL;
-			((FillDEF*)Desc[i].ptr)->scale = 1.0f;
-			((FillDEF*)Desc[i].ptr)->hatch = (LineDEF*)0L;
-			((FillDEF*)Desc[i].ptr)->color2 = 0x00ffffffL;
-			break;
-		case typGOBJ:
-			*(GraphObj **)Desc[i].ptr = (GraphObj*)0L;
-			break;
-		case typOBJLST:
-			*Desc[i].count = 0L;
-			*(GraphObj ***)Desc[i].ptr = (GraphObj**)0L;
-			break;
-		case typIPLST:
-			*Desc[i].count = 0L;
-			*(POINT **)Desc[i].ptr = (POINT*)0L;
-			break;
-		case typFPLST:
-			*Desc[i].count = 0L;
-			*(lfPOINT **)Desc[i].ptr = (lfPOINT*)0L;
-			break;
-		case typFPLST3D:
-			*Desc[i].count = 0L;
-			*(fPOINT3D **)Desc[i].ptr = (fPOINT3D*)0L;
-			break;
-		case typTEXT:
-			*(char **)Desc[i].ptr = (char*)0L;
-			break;
-		case typTXTDEF:
-			tx = (TextDEF *)Desc[i].ptr;
-			tx->ColTxt = 0x0L,			tx->ColBg = 0x00ffffffL;
-			tx->fSize = defs.GetSize(SIZE_TEXT);
-			tx->RotBL = tx->RotCHAR = 0.0;
-			tx->Align = tx->Mode = tx->Style = tx->Font = 0L;
-			tx->text = 0L;
-			break;
-		case typPTRTXTDEF:
-			*(TextDEF**)Desc[i].ptr = (TextDEF*)0L;
-			break;
-			}
-		if(Desc[i].type & typLAST) break;
-		}
-	return true;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Save object data to memory block
-static unsigned char *SavVarBuf = 0L;
-static long SavVarSize = 0,	SavVarPos = 0;
-void SavVarInit(long len)
-{
-	if(SavVarBuf) free(SavVarBuf);
-	SavVarBuf = (unsigned char *)malloc(SavVarSize = len+4096);
-	SavVarPos = 0;
-}
-
-bool SavVarAdd(void *ptr, int size)
-{
-	int len;
-
-	if(SavVarBuf) {
-		len = sizeof(unsigned char *) + sizeof(int) + size;
-		while (SavVarSize <= SavVarPos+len) {
-			if(!(SavVarBuf = (unsigned char *)realloc(SavVarBuf, SavVarSize + 4096))) return false;
-			SavVarSize += 4096;
-			}
-		memcpy(SavVarBuf+SavVarPos, &ptr, sizeof(unsigned char *));
-		SavVarPos += sizeof(unsigned char *);
-		memcpy(SavVarBuf+SavVarPos, &size, sizeof(int));	SavVarPos += sizeof(int);
-		memcpy(SavVarBuf+SavVarPos, ptr, size);				SavVarPos += size;
-		return true;
-		}
-	return false;
-}
-
-void *SavVarFetch()
-{
-	void *tmp;
-
-	SavVarAdd(0L, 0);				
-	tmp = SavVarBuf = (unsigned char *)realloc(SavVarBuf, SavVarPos);
-	SavVarSize = SavVarPos = 0;		SavVarBuf = 0L;
-	return tmp;
-}
-
-bool SaveVarGO(descIO *Desc)
-{
-	int i;
-	AxisDEF *ax;
-	TextDEF *tx;
-
-	for(i = 0; Desc[i].label; i++) {
-		switch(Desc[i].type & 0xff){
-		case typNZINT:
-		case typINT:
-			SavVarAdd(Desc[i].ptr, sizeof(int));
-			break;
-		case typNZLFLOAT:
-		case typLFLOAT:
-			SavVarAdd(Desc[i].ptr, sizeof(double));
-			break;
-		case typDWORD:
-			SavVarAdd(Desc[i].ptr, sizeof(DWORD));
-			break;
-		case typFRECT:
-			SavVarAdd(Desc[i].ptr, sizeof(fRECT));
-			break;
-		case typNZLFPOINT:
-		case typLFPOINT:
-			SavVarAdd(Desc[i].ptr, sizeof(lfPOINT));
-			break;
-		case typPOINT3D:
-			SavVarAdd(Desc[i].ptr, sizeof(fPOINT3D));
-			break;
-		case typAXDEF:
-		case typPTRAXDEF:
-			ax = (Desc[i].type & 0xff) == typAXDEF ? (AxisDEF *)Desc[i].ptr : *(AxisDEF **)Desc[i].ptr;
-			if(ax) {
-				SavVarAdd(ax, sizeof(AxisDEF));
-				if(ax->breaks && ax->nBreaks) {
-					SavVarAdd(ax->breaks, ax->nBreaks * sizeof(lfPOINT));
-					}
-				}
-			break;
-		case typLINEDEF:
-			SavVarAdd(Desc[i].ptr, sizeof(LineDEF));
-			break;
-		case typFILLDEF:
-			SavVarAdd(Desc[i].ptr, sizeof(FillDEF));
-			break;
-		case typGOBJ:	case typOBJLST:		//not supported
-			break;
-		case typIPLST:						//probably in the future
-			break;
-		case typFPLST:
-			break;
-		case typFPLST3D:
-			SavVarAdd(*((void **)Desc[i].ptr), sizeof(fPOINT3D) * (*Desc[i].count));
-			break;
-		case typTEXT:
-			if(*(char**)(Desc[i].ptr)) SavVarAdd(Desc[i].ptr, (int)strlen(*(char**)(Desc[i].ptr))+1);
-			break;
-		case typTXTDEF:
-		case typPTRTXTDEF:
-			tx = (Desc[i].type &0xff) == typTXTDEF ? (TextDEF *)Desc[i].ptr : *(TextDEF **)Desc[i].ptr;
-			if(tx) SavVarAdd(tx, sizeof(TextDEF) - sizeof(char*));
-			break;
-			}
-		if(Desc[i].type & typLAST) break;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Graphic object member funtions for IO
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-svgOptions::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"tagAttr", typTEXT, &svgattr, 0L},
-		{"Script", typLAST | typTEXT, &script, 0L}};
-
-	switch(rw) {
-	case INIT_VARS:
-		return InitVarsGO(Desc);
-	case FILE_READ:
-		return ExecInput(Desc);
-		}
-	return false;
-}
-
-bool
-Symbol::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"ssRef", typIPLST, &ssRef, &cssRef},
-		{"Idx", typINT, &idx, 0L},
-		{"Pos", typLFPOINT, &fPos, 0L},
-		{"Size", typLFLOAT, &size, 0L},
-		{"Line", typLINEDEF, &SymLine, 0L},
-		{"FillCol", typDWORD, &SymFill.color, 0L},
-		{"Text", typPTRTXTDEF, &SymTxt, 0L},
-		{"Name", typLAST | typTEXT, &name, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		size = DefSize(SIZE_SYMBOL);
-		SymLine.color = parent ? parent->GetColor(COL_SYM_LINE) : defs.Color(COL_SYM_LINE);
-		SymLine.width = parent ? parent->GetSize(SIZE_SYM_LINE) : DefSize(SIZE_SYM_LINE);
-		SymFill.type = FILL_NONE;
-		SymFill.color = parent ? parent->GetColor(COL_SYM_FILL) : defs.Color(COL_SYM_FILL);
-		SymFill.scale = 1.0f;
-		SymFill.hatch = (LineDEF *) 0L;
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		SymFill.hatch = (LineDEF *) 0L;
-		return true;
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "Symbol", Desc);
-		}
-	return false;
-}
-
-
-bool
-Bubble::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"ssRef", typIPLST, &ssRef, &cssRef},
-		{"Pos", typLFPOINT, &fPos, 0L},
-		{"Size", typLFLOAT, &fs, 0L},
-		{"Line", typLINEDEF, &BubbleLine, 0L},
-		{"FillLine", typLINEDEF, &BubbleFillLine, 0L},
-		{"Fill", typFILLDEF, &BubbleFill, 0L},
-		{"Name", typLAST | typTEXT, &name, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		BubbleLine.width = DefSize(SIZE_BUBBLE_LINE);
-		BubbleFillLine.width = DefSize(SIZE_BUBBLE_HATCH_LINE);
-		BubbleLine.color = BubbleFillLine.color = defs.Color(COL_BUBBLE_LINE);
-		BubbleFill.color = defs.Color(COL_BUBBLE_FILL);
-		ssRef = 0L;
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		BubbleFill.hatch = &BubbleFillLine;
-		return true;
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "Bubble", Desc);
-		}
-	return false;
-}
-
-bool
-Bar::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typINT, &type, 0L},
-		{"ssRef", typIPLST, &ssRef, &cssRef},
-		{"Pos", typLFPOINT, &fPos, 0L},
-		{"Size", typLFLOAT, &size, 0L},
-		{"Org", typLFPOINT, &BarBase, 0L},
-		{"Line", typLINEDEF, &BarLine, 0L},
-		{"Fill", typFILLDEF, &BarFill, 0L},
-		{"FillLine", typLINEDEF, &HatchLine, 0L},
-		{"Name", typLAST | typTEXT, &name, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		type = BAR_VERTB;
-		memcpy(&BarFill, defs.GetFill(), sizeof(FillDEF));
-		if(BarFill.hatch) memcpy(&HatchLine, BarFill.hatch, sizeof(LineDEF));
-		BarFill.hatch = &HatchLine;
-		memcpy(&BarLine, defs.GetOutLine(), sizeof(LineDEF));
-		size = DefSize(SIZE_BAR);
-		mo = 0L;
-		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		BarFill.hatch = &HatchLine;
-		return true;
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "Bar", Desc);
-		}
-	return false;
-}
-
-void
-DataLine::FileValues(char *name, int type, double start, double step)
-{
-	FILE *file;
-	int i, c;
-	double fx;
-	lfPOINT *tfp;
-
-#ifdef USE_WIN_SECURE
-	if(fopen_s(&file, name, "r")) {
-		sprintf_s(TmpTxt, TMP_TXT_SIZE, "DataLine: open failed for file \"%s\"", name);
-#else
-	if(!(file = fopen(name, "r"))) {
-		sprintf(TmpTxt, "DataLine: open failed for file \"%s\"", name);
-#endif
-		ErrorBox(TmpTxt);
-		return;
-		}
-	if(Values) free(Values);
-	if(!(Values = (lfPOINT*)calloc( nPnt = 1000, sizeof(lfPOINT)))){
-		fclose(file);
-		return;
-		}
-	switch(type) {
-	case 1:				//x and y values
-		i = 0;
-		do {
-#ifdef USE_WIN_SECURE
-			c = fscanf_s(file, "%lf%lf", &Values[i].fx, &Values[i].fy);
-#else
-			c = fscanf(file, "%lf%lf", &Values[i].fx, &Values[i].fy);
-#endif
-			i++;
-			if(i >= nPnt &&(tfp = (lfPOINT*)realloc(Values, (nPnt+1000)*sizeof(lfPOINT)))){
-				Values = tfp;			nPnt += 1000;
-				}
-			else if(i >= nPnt) break;
-			}while(c == 2);
-		i--;
-		break;
-	case 2:				//only y values
-		i = 0;	fx = start;
-		do {
-#ifdef USE_WIN_SECURE
-			c = fscanf_s(file, "%lf", &Values[i].fy);
-#else
-			c = fscanf(file, "%lf", &Values[i].fy);
-#endif
-			Values[i].fx = fx;
-			i++;	fx += step;
-			if(i >= nPnt &&(tfp = (lfPOINT*)realloc(Values, (nPnt+1000)*sizeof(lfPOINT)))){
-				Values = tfp;			nPnt += 1000;
-				}
-			}while(c == 1);
-		i--;
-		break;
-		}
-	nPntSet = i-1;
-	fclose(file);
-}
-
-bool
-DataLine::FileIO(int rw)
-{
-	long i;
-	char *file1 = 0L;
-	char *file2 = 0L;
-	double Start = 0.0f, Step = 0.0f;
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"ssXref", typTEXT, &ssXref, 0L},
-		{"ssYref", typTEXT, &ssYref, 0L},
-		{"BgCol", typDWORD, &BgColor, 0L},
-		{"Line", typLINEDEF, &LineDef, 0L},
-		{"Data", typFPLST, &Values, &i},
-		{"file_xy", typTEXT, &file2, 0L},
-		{"start_x", typNZLFLOAT, &Start, 0L},
-		{"step_x", typNZLFLOAT, &Step, 0L},
-		{"file_y", typTEXT, &file1, 0L},
-		{"Desc", typLAST | typTEXT, &name, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		isPolygon = false;
-		nPnt = nPntSet = cp = 0;
-		memcpy(&LineDef, defs.GetLine(), sizeof(LineDEF));
-		BgColor = defs.Color(COL_BG);
-		pts = 0L;		dirty = true;	mo = 0L;
-		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
-		min.fx = min.fy = max.fx = max.fy = 0.0;
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		nPnt = i;
-		nPntSet = i-1;
-		if(file2)FileValues(file2, 1, 0.0, 1.0);
-		else if(file1)FileValues(file1, 2, Start, fabs(Step) > defs.min4log ? Step : 1.0);
-		if(file1) free(file1);
-		if(file2) free(file2);
-		if(i && Values)return true;
-		break;
-	case FILE_WRITE:
-		i = nPntSet+1;
-		return ExecOutput(Notary->RegisterGO(this), "DataLine", Desc);
-		}
-	return false;
-}
-
-bool
-DataPolygon::FileIO(int rw)
-{
-	long i;
-	char *file1 = 0L;
-	char *file2 = 0L;
-	double Start = 0.0f, Step = 0.0f;
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"ssXref", typTEXT, &ssXref, 0L},
-		{"ssYref", typTEXT, &ssYref, 0L},
-		{"BgCol", typDWORD, &BgColor, 0L},
-		{"Line", typLINEDEF, &LineDef, 0L},
-		{"FillLine", typLINEDEF, &pgFillLine, 0L},
-		{"Fill", typFILLDEF, &pgFill, 0L},
-		{"Data", typFPLST, &Values, &i},
-		{"file_xy", typTEXT, &file2, 0L},
-		{"start_x", typNZLFLOAT, &Start, 0L},
-		{"step_x", typNZLFLOAT, &Step, 0L},
-		{"file_y", typLAST | typTEXT, &file1, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		isPolygon = true;
-		memcpy(&LineDef, defs.GetLine(), sizeof(LineDEF));
-		LineDef.pattern = 0L;
-		BgColor = defs.Color(COL_BG);
-		memcpy(&pgFill, defs.GetFill(), sizeof(FillDEF));
-		if(pgFill.hatch) memcpy(&pgFillLine, pgFill.hatch, sizeof(LineDEF));
-		pgFill.hatch = &pgFillLine;		dirty = true;	mo = 0L;
-		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
-		min.fx = min.fy = max.fx = max.fy = 0.0f;
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		nPnt = i;
-		nPntSet = i-1;
-		if(file2)FileValues(file2, 1, 0.0f, 1.0f);
-		else if(file1)FileValues(file1, 2, Start, fabs(Step) > defs.min4log ? Step : 1.0);
-		if(file1) free(file1);
-		if(file2) free(file2);
-		pgFill.hatch = &pgFillLine;
-		if(i && Values)return true;
-		break;
-	case FILE_WRITE:
-		i = nPntSet+1;
-		return ExecOutput(Notary->RegisterGO(this), "DataPolygon", Desc);
-		}
-	return false;
-}
-
-bool
-RegLine::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"type", typNZINT, &type, 0L},
-		{"nPoints", typINT, &nPoints, 0L},
-		{"BgCol", typDWORD, &BgColor, 0L},
-		{"Line", typLINEDEF, &LineDef, 0L},
-		{"Range", typFRECT, &lim, 0L},
-		{"uClip", typFRECT, &uclip, 0L},
-		{"mx", typNZLFLOAT, &mx, 0L},
-		{"my", typNZLFLOAT, &my, 0L},
-		{"li1", typLFPOINT, &l1, 0L},
-		{"li2", typLFPOINT, &l2, 0L},
-		{"li3", typLFPOINT, &l3, 0L},
-		{"li4", typLFPOINT, &l4, 0L},
-		{"li5", typLAST | typLFPOINT, &l5, 0L}};
-
-	switch(rw) {
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		cp = 0;
-		memcpy(&LineDef, defs.GetLine(), sizeof(LineDEF));
-		BgColor = defs.Color(COL_BG);
-		pts = 0L;
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "RegLine", Desc);
-		}
-	return false;
-}
-
-void
-SDellipse::RegGO(void *n)
-{
-	if(n) {
-		if(rl)rl->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-	}
-}
-
-bool
-SDellipse::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"type", typNZINT, &type, 0L},
-		{"Line", typLINEDEF, &LineDef, 0L},
-		{"Range", typFRECT, &lim, 0L},
-		{"Regr", typGOBJ, &rl, 0L},
-		{"Data", typLAST | typFPLST, &val, &nPoints}};
-
-	switch(rw) {
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		memcpy(&LineDef, defs.GetOutLine(), sizeof(LineDEF));
-		pts = 0L;
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		if(rl) rl->parent = this;
-		return true;
-	case FILE_WRITE:
-		if(rl) rl->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "SDellipse", Desc);
-		}
-	return false;
-}
-
-bool
-ErrorBar::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"ssRef", typIPLST, &ssRef, &cssRef},
-		{"Pos", typLFPOINT, &fPos, 0L},
-		{"Err", typLFLOAT, &ferr, 0L},
-		{"Size", typLFLOAT, &SizeBar, 0L},
-		{"Line", typLINEDEF, &ErrLine, 0L},
-		{"Desc", typLAST | typTEXT, &name, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		SizeBar = DefSize(SIZE_ERRBAR);
-		ErrLine.width = DefSize(SIZE_ERRBAR_LINE);
-		ErrLine.color = defs.Color(COL_SYM_LINE);
-		mo = 0L;
-		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "ErrorBar", Desc);
-		}
-	return false;
-}
-
-bool
-Arrow::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"ssRef", typIPLST, &ssRef, &cssRef},
-		{"moveable", typNZINT, &moveable, 0L},
-		{"p1", typLFPOINT, &pos1, 0L},
-		{"p2", typLFPOINT, &pos2, 0L},
-		{"CapW", typLFLOAT, &cw, 0L},
-		{"CapL", typLFLOAT, &cl, 0L},
-		{"Line", typLAST | typLINEDEF, &LineDef, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		cw = DefSize(SIZE_ARROW_CAPWIDTH);
-		cl = DefSize(SIZE_ARROW_CAPLENGTH);
-		LineDef.color = parent ? parent->GetColor(COL_DATA_LINE) : defs.Color(COL_DATA_LINE);
-		LineDef.width = parent ? parent->GetSize(SIZE_ARROW_LINE) : DefSize(SIZE_ARROW_LINE);
-		type = ARROW_LINE;		dh1 = dh2 = 0L;		mo = 0L;
-		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "Arrow", Desc);
-		}
-	return false;
-}
-
-bool
-Box::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"ssRef", typIPLST, &ssRef, &cssRef},
-		{"High", typLFPOINT, &pos1, 0L},
-		{"Low", typLFPOINT, &pos2, 0L},
-		{"Size", typLFLOAT, &size, 0L},
-		{"Line", typLINEDEF, &Outline, 0L},
-		{"FillLine", typLINEDEF, &Hatchline, 0L},
-		{"Fill", typFILLDEF, &Fill, 0L},
-		{"Name", typLAST | typTEXT, &name, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		memcpy(&Outline, defs.GetOutLine(), sizeof(LineDEF));
-		memcpy(&Fill, defs.GetFill(), sizeof(FillDEF));
-		if(Fill.hatch)memcpy(&Hatchline, Fill.hatch, sizeof(LineDEF));
-		Fill.hatch = &Hatchline;
-		size = DefSize(SIZE_BAR);
-		ssRef = 0L;
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		Fill.hatch = &Hatchline;
-		return true;
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "Box", Desc);
-		}
-	return false;
-}
-
-bool
-Whisker::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"ssRef", typIPLST, &ssRef, &cssRef},
-		{"High", typLFPOINT, &pos1, 0L},
-		{"Low", typLFPOINT, &pos2, 0L},
-		{"Size", typLFLOAT, &size, 0L},
-		{"Line", typLINEDEF, &LineDef, 0L},
-		{"Desc", typLAST | typTEXT, &name, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		size = DefSize(SIZE_WHISKER);
-		LineDef.width = DefSize(SIZE_WHISKER_LINE);
-		LineDef.color = defs.Color(COL_WHISKER);
-		mo = 0L;
-		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "Whisker", Desc);
-		}
-	return false;
-}
-
-bool
-DropLine::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typINT, &type, 0L},
-		{"ssRef", typIPLST, &ssRef, &cssRef},
-		{"Pos", typLFPOINT, &fPos, 0L},
-		{"Line", typLAST | typLINEDEF, &LineDef, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		LineDef.color = defs.Color(COL_SYM_LINE);
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "DropLine", Desc);
-		}
-	return false;
-}
-
-bool
-Sphere::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"Line", typLINEDEF, &Line, 0L},
-		{"Fill", typFILLDEF, &Fill, 0L},
-		{"Pos", typPOINT3D, &fPos, 0L},
-		{"Size", typLFLOAT, &size, 0L},
-		{"ssRef", typLAST | typIPLST, &ssRef, &cssRef}};
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		size = DefSize(SIZE_SYMBOL);
-		Line.color = defs.Color(COL_SYM_LINE);
-		Line.width = DefSize(SIZE_SYM_LINE);
-		Fill.color = defs.Color(COL_SYM_FILL);
-		scl = 0L;		nscl = 0;
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "Sphere", Desc);
-		}
-	return false;
-}
-
-bool
-Plane3D::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Line", typLINEDEF, &Line, 0L},
-		{"Fill", typFILLDEF, &Fill, 0L},
-		{"values", typLAST | typFPLST3D, &dt, &ndt}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		ipl = 0L;	pts = 0L;
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "Plane3D", Desc);
-		}
-	return false;
-}
-
-bool
-Brick::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Line", typLINEDEF, &Line, 0L},
-		{"Fill", typFILLDEF, &Fill, 0L},
-		{"Pos", typPOINT3D, &fPos, 0L},
-		{"depth", typLFLOAT, &depth, 0L},
-		{"width", typLFLOAT, &width, 0L},
-		{"height", typLFLOAT, &height, 0L},
-		{"flags", typDWORD, &flags, 0L},
-		{"ssRef", typLAST | typIPLST, &ssRef, &cssRef}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		Line.color = defs.Color(COL_SYM_LINE);
-		Line.width = DefSize(SIZE_SYM_LINE);
-		Fill.color = defs.Color(COL_SYM_FILL);
-		faces = (plane**)calloc(6, sizeof(plane*));
-		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
-		mo = 0L;
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "Brick", Desc);
-		}
-	return false;
-}
-
-bool
-DropLine3D::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typINT, &type, 0L},
-		{"Line", typLINEDEF, &Line, 0L},
-		{"Pos", typPOINT3D, &fPos, 0L},
-		{"ssRef", typLAST | typIPLST, &ssRef, &cssRef}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		Line.color = defs.Color(COL_SYM_LINE);
-		Line.width = DefSize(SIZE_HAIRLINE);	mo = 0L;
-		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
-		ls[0] = ls[1] = ls[2] = ls[3] = ls[4] = ls[5] = 0L;
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "DropLine3D", Desc);
-		}
-	return false;
-}
-
-bool
-Arrow3D::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typINT, &type, 0L},
-		{"Line", typLINEDEF, &Line, 0L},
-		{"Org", typPOINT3D, &fPos1, 0L},
-		{"Pos", typPOINT3D, &fPos2, 0L},
-		{"CapW", typLFLOAT, &cw, 0L},
-		{"CapL", typLFLOAT, &cl, 0L},
-		{"ssRef", typLAST | typIPLST, &ssRef, &cssRef}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		cw = DefSize(SIZE_ARROW_CAPWIDTH);
-		cl = DefSize(SIZE_ARROW_CAPLENGTH);
-		Line.color = defs.Color(COL_ARROW);
-		Line.width = DefSize(SIZE_ARROW_LINE);
-		ls[0] = ls[1] = ls[2] = 0L;
-		cap = 0L;
-		type = ARROW_LINE;
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "Arrow3D", Desc);
-		}
-	return false;
-}
-
-bool
-Line3D::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Line", typLINEDEF, &Line, 0L},
-		{"ssRefX", typTEXT, &x_range, 0L},
-		{"ssRefY", typTEXT, &y_range, 0L},
-		{"ssRefZ", typTEXT, &z_range, 0L},
-		{"ssRef", typIPLST, &ssRef, &cssRef},
-		{"values", typLAST | typFPLST3D, &values, &nPts}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		Line.color = defs.Color(COL_DATA_LINE);
-		Line.width = DefSize(SIZE_DATA_LINE);
-		ls = 0L;	pts = 0L;	npts = 0L;	mo=0L;
-		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
-		min.fx = min.fy = min.fz = max.fx = max.fy = max.fz = 0.0;
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		if(nPts > 1) ls = (line_segment **)calloc(nPts-1, sizeof(line_segment*));
-		return true;
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "Line3D", Desc);
-		}
-	return false;
-}
-
-bool
-Label::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"ssRef", typIPLST, &ssRef, &cssRef},
-		{"moveable", typNZINT, &moveable, 0L},
-		{"Pos", typNZLFPOINT, &fPos, 0L},
-		{"Dist", typNZLFPOINT, &fDist, 0L},
-		{"Flags", typDWORD, &flags, 0L},
-		{"TxtDef", typLAST | typTXTDEF, &TextDef, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		TextDef.ColTxt = 0x0L;
-		TextDef.ColBg = 0x00ffffffL;
-		TextDef.fSize = DefSize(SIZE_TEXT);
-		TextDef.RotBL = TextDef.RotCHAR = 0.0;
-		TextDef.iSize = 0;
-		TextDef.Align = TXA_VTOP | TXA_HLEFT;
-		TextDef.Mode = TXM_TRANSPARENT;
-		TextDef.Style = TXS_NORMAL;
-		TextDef.Font = FONT_HELVETICA;
-		TextDef.text = 0L;	bgcolor = 0x00ffffffL;
-		bgLine.width = 0.0;		bgLine.patlength = 6.0;
-		bgLine.color = bgcolor;	bgLine.pattern = 0L;
-		CursorPos = 0;	defDisp = 0L;	bBGvalid = bModified = false;
-		curr_z = 0.0;		is3D = false;
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		if(parent && parent->Id != GO_MLABEL) {
-			if(!TextDef.text || !TextDef.text[0]) return false;
-			}
-		return ExecOutput(Notary->RegisterGO(this), "Label", Desc);
-		}
-	return false;
-}
-
-void
-mLabel::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(Lines) for(i = 0; i < nLines; i++) if(Lines[i]) Lines[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-	}
-}
-
-bool
-mLabel::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"moveable", typNZINT, &moveable, 0L},
-		{"Pos", typNZLFPOINT, &fPos, 0L},
-		{"Dist", typNZLFPOINT, &fDist, 0L},
-		{"lspc", typLFLOAT, &lspc, 0L},
-		{"Flags", typDWORD, &flags, 0L},
-		{"TxtDef", typTXTDEF, &TextDef, 0L},
-		{"Lines", typLAST | typOBJLST, &Lines, &nLines}};
-	int i;
-
-	switch(rw) {
-	case SAVE_VARS:
-		//The lines inherit settings from this object.
-		//We need not save them in this context
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		TextDef.ColTxt = 0x0L;
-		TextDef.ColBg = 0x00ffffffL;
-		TextDef.fSize = DefSize(SIZE_TEXT);
-		TextDef.RotBL = TextDef.RotCHAR = 0.0;
-		TextDef.iSize = 0;
-		TextDef.Align = TXA_VTOP | TXA_HLEFT;
-		TextDef.Mode = TXM_TRANSPARENT;
-		TextDef.Style = TXS_NORMAL;
-		TextDef.Font = FONT_HELVETICA;
-		TextDef.text = 0L;
-		undo_flags = 0L;	lspc = 1.0;
-		curr_z = 0.0;		is3D = false;
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		if(Lines) for ( i = 0; i < nLines; i++)
-			if(Lines[i]) Lines[i]->parent = this;
-		return true;
-	case FILE_WRITE:
-		if(Lines) for ( i = 0; i < nLines; i++)
-			if(Lines[i]) Lines[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "mLabel", Desc);
-		}
-	return false;
-}
-
-bool
-TextFrame::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"moveable", typNZINT, &moveable, 0L},
-		{"Pos1", typNZLFPOINT, &pos1, 0L},
-		{"Pos2", typNZLFPOINT, &pos2, 0L},
-		{"lspc", typLFLOAT, &lspc, 0L},
-		{"Pad", typFRECT, &pad, 0L},
-		{"TxtDef", typTXTDEF, &TextDef, 0L},
-		{"Line", typLINEDEF, &Line, 0L},
-		{"FillLine", typLINEDEF, &FillLine, 0L},
-		{"Fill", typFILLDEF, &Fill, 0L},
-		{"Text", typLAST | typTEXT, &text, 0L}};
-	switch(rw) {
-	case SAVE_VARS:
-		return false;
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		TextDef.ColTxt = 0x0L;
-		TextDef.ColBg = 0x00ffffffL;
-		TextDef.fSize = DefSize(SIZE_TEXT);
-		TextDef.RotBL = TextDef.RotCHAR = 0.0;
-		TextDef.iSize = 0;
-		TextDef.Align = TXA_VBOTTOM | TXA_HLEFT;
-		TextDef.Mode = TXM_TRANSPARENT;
-		TextDef.Style = TXS_NORMAL;
-		TextDef.Font = FONT_HELVETICA;
-		TextDef.text = 0L;
-		lines = 0L;		nlines = 0;			drc = 0L;
-		cur_pos.x = cur_pos.y = tm_c = 0;	tm_rec = 0L;
-		bModified = bResize = has_m1 = has_m2 = false;
-		if(Fill.hatch) memcpy(&FillLine, Fill.hatch, sizeof(LineDEF));
-		Fill.hatch = &FillLine;			c_char = m1_char = m2_char = '?';
-		pad.Xmin = pad.Xmax = pad.Ymin = pad.Ymax = DefSize(SIZE_SYMBOL)/2.0;
-		Cursor.left = Cursor.right = Cursor.top = Cursor.bottom = 0;
-		pad.Xmax *= 2.0;
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);				Fill.hatch = &FillLine;
-		return true;
-	case FILE_WRITE:
-		if(lines)lines2text();
-		if(!text || !text[0]) return false;
-		return ExecOutput(Notary->RegisterGO(this), "TextFrame", Desc);
-		}
-	return false;
-}
-
-bool
-segment::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"moveable", typNZINT, &moveable, 0L},
-		{"cent", typLFPOINT, &fCent, 0L},
-		{"ri", typNZLFLOAT, &radius1, 0L},
-		{"ra", typLFLOAT, &radius2, 0L},
-		{"start", typLFLOAT, &angle1, 0L},
-		{"end", typLFLOAT, &angle2, 0L},
-		{"shout", typNZLFLOAT, &shift, 0L},
-		{"Line", typLINEDEF, &segLine, 0L},
-		{"FillLine", typLINEDEF, &segFillLine, 0L},
-		{"Fill", typLAST | typFILLDEF, &segFill, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		segLine.width = DefSize(SIZE_SEGLINE);
-		pts = 0L;	nPts = 0;
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		segFill.hatch = &segFillLine;
-		return true;
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "segment", Desc);
-		}
-	return false;
-}
-
-bool
-polyline::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"moveable", typNZINT, &moveable, 0L},
-		{"Data", typFPLST, &Values, &nPoints},
-		{"Line", typLINEDEF, &pgLine, 0L},
-		{"FillLine", typLINEDEF, &pgFillLine, 0L},
-		{"Fill", typLAST | typFILLDEF, &pgFill, 0L}};
-	
-	switch(rw) {
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		memcpy(&pgLine, defs.plLineDEF(0L), sizeof(LineDEF));
-		memcpy(&pgFill, defs.pgFillDEF(0L), sizeof(FillDEF));
-		if(pgFill.hatch) memcpy(&pgFillLine, pgFill.hatch, sizeof(LineDEF));
-		pgFill.hatch = &pgFillLine;
-		pts = 0L;		nPts = 0;
-		pHandles = 0L;
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		pgFill.hatch = &pgFillLine;
-		return true;
-	case FILE_WRITE:
-		if(type != 1) Desc[3].type |= typLAST;	//skip fill for polyline
-		return ExecOutput(Notary->RegisterGO(this), 
-			type == 1 ? (char*)"polygon" : (char*)"polyline", Desc);
-		}
-	return false;
-}
-
-bool
-Bezier::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"moveable", typNZINT, &moveable, 0L},
-		{"Data", typFPLST, &Values, &nPoints},
-		{"Line", typLINEDEF, &pgLine, 0L},
-		{"FillLine", typLINEDEF, &pgFillLine, 0L},
-		{"Fill", typLAST | typFILLDEF, &pgFill, 0L}};
-	
-	switch(rw) {
-	case INIT_VARS:
-		//assume that all initialization is done by polyline::FileIO(int rw)
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		pgFill.hatch = &pgFillLine;
-		return true;
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "bezier", Desc);
-		}
-	return false;
-}
-
-bool
-rectangle::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"moveable", typNZINT, &moveable, 0L},
-		{"p1", typLFPOINT, &fp1, 0L},
-		{"p2", typLFPOINT, &fp2, 0L},
-		{"Line", typLINEDEF, &Line, 0L},
-		{"FillLine", typLINEDEF, &FillLine, 0L},
-		{"Fill", typFILLDEF, &Fill, 0L},
-		{"Rad", typNZLFLOAT, &rad, 0L},
-		{"Name", typLAST | typTEXT, &name, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		memcpy(&Line, defs.pgLineDEF(0L), sizeof(LineDEF));
-		memcpy(&Fill, defs.pgFillDEF(0L), sizeof(FillDEF));
-		if(Fill.hatch) memcpy(&FillLine, Fill.hatch, sizeof(LineDEF));
-		Fill.hatch = &FillLine;
-		pts = 0L;	nPts = 0L;
-		rad = DefSize(SIZE_RRECT_RAD);
-		drc = 0L;
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		Fill.hatch = &FillLine;
-		return true;
-	case FILE_WRITE:
-		if(type != 2) rad = 0.0;
-		ExecOutput(Notary->RegisterGO(this), 
-			type == 1? (char*)"ellipse" : type == 2? (char*)"roundrec" :
-			(char*)"rectangle", Desc);
-		return true;
-		}
-	return false;
-}
-
-void
-LegItem::RegGO(void *n)
-{
-	if(n) {
-		if(Sym) Sym->RegGO(n);
-		if(Desc) Desc->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-LegItem::FileIO(int rw)
-{
-	descIO Des[] = {
-		{"D_Line", typLINEDEF, &DataLine, 0L},
-		{"O_Line", typLINEDEF, &OutLine, 0L},
-		{"H_Line", typLINEDEF, &HatchLine, 0L},
-		{"Fill", typFILLDEF, &Fill, 0L},
-		{"Sym", typGOBJ, &Sym, 0L},
-		{"Text", typGOBJ, &Desc, 0L},
-		{"flags", typLAST | typDWORD, &flags, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Des);
-	case INIT_VARS:
-		InitVarsGO(Des);
-		Fill.hatch = &HatchLine;
-		return true;
-	case FILE_READ:
-		ExecInput(Des);
-		Fill.hatch = &HatchLine;
-		if(Sym) Sym->parent=this;
-		if(Desc) Desc->parent=this;
-		return true;
-	case FILE_WRITE:
-		if(Sym) Sym->FileIO(rw);
-		if(Desc) Desc->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "LegItem", Des);
-		}
-	return false;
-}
-
-void
-Legend::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(Items) for(i = 0; i < nItems; i++) if(Items[i]) Items[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-Legend::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"pos", typLFPOINT, &pos, 0L},
-		{"rec1", typFRECT, &B_Rect, 0L},
-		{"rec2", typFRECT, &D_Rect, 0L},
-		{"rec3", typFRECT, &F_Rect, 0L},
-		{"Items", typLAST | typOBJLST, &Items, &nItems}};
-	int i;
-	double d;
-
-	switch(rw) {
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		B_Rect.Ymin = DefSize(SIZE_DRECT_TOP);			B_Rect.Xmin = DefSize(SIZE_DRECT_LEFT);
-		B_Rect.Xmax = B_Rect.Xmin + 1.5*(d = DefSize(SIZE_BAR));
-		B_Rect.Ymin += d*0.2;
-		B_Rect.Ymax = B_Rect.Ymin + d/2.0;
-		D_Rect.Ymin = 0.0;			D_Rect.Xmin = d*0.7;
-		D_Rect.Xmax = d*1.3;		D_Rect.Ymax = d*0.4;
-		F_Rect.Ymin = 0.0;			F_Rect.Xmin = d*0.2;
-		F_Rect.Xmax = d*1.3;		F_Rect.Ymax = d*0.4;
-		to = 0L;					hasLine = false;
-		trc.left = trc.right = trc.top = trc.bottom = 0;
-		if(!name) {
-			name = (char*)malloc(20 * sizeof(char));
-			rlp_strcpy(name, 20, "Legend");
-			}
-		return true;
-	case FILE_READ:
-		nItems = 0L;
-		ExecInput(Desc);
-		if(Items) for(i = 0; i < nItems; i++) if(Items[i]) Items[i]->parent=this;
-		return true;
-	case FILE_WRITE:
-		if(Items) for(i = 0; i < nItems; i++) if(Items[i]) Items[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "Legend", Desc);
-		}
-	return false;
-}
-
-void
-PlotScatt::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(TheLine) TheLine->RegGO(n);
-		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->RegGO(n);
-		if(Errors) for(i = 0; i < nPoints; i++) if(Errors[i]) Errors[i]->RegGO(n);
-		if(Arrows) for(i = 0; i < nPoints; i++) if(Arrows[i]) Arrows[i]->RegGO(n);
-		if(DropLines) for(i = 0; i < nPoints; i++) if(DropLines[i]) DropLines[i]->RegGO(n);
-		if(Labels) for(i = 0; i < nPoints; i++) if(Labels[i]) Labels[i]->RegGO(n);
-		if(Bars) for(i = 0; i < nPoints; i++) if(Bars[i]) Bars[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-PlotScatt::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"hide", typNZINT, &hidden, 0L},
-		{"Bounds", typFRECT, &Bounds, 0L},
-		{"DefSym", typNZINT, &DefSym, 0L},
-		{"baDist", typLFPOINT, &BarDist, 0L},
-		{"xRange", typTEXT, &xRange, 0L},
-		{"yRange", typTEXT, &yRange, 0L},
-		{"eRange", typTEXT, &ErrRange, 0L},
-		{"lRange", typTEXT, &LbRange, 0L},
-		{"x_axis", typNZINT, &use_xaxis, 0L},
-		{"y_axis", typNZINT, &use_yaxis, 0L},
-		{"Bars", typOBJLST, &Bars, &nPoints},
-		{"Symbols", typOBJLST, &Symbols, &nPoints},
-		{"PL", typGOBJ, &TheLine, 0L},
-		{"ErrBars", typOBJLST, &Errors, &nPoints},
-		{"Arrows", typOBJLST, &Arrows, &nPoints},
-		{"dLines", typOBJLST, &DropLines, &nPoints},
-		{"Labels", typOBJLST, &Labels, &nPoints},
-		{"x_info", typTEXT, &x_info, 0L},
-		{"y_info", typTEXT, &y_info, 0L},
-		{"DataDesc", typLAST | typTEXT, &data_desc, 0L}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		x_info = y_info = z_info = 0L;
-		InitVarsGO(Desc);
-		DefSym = SYM_CIRCLE;
-		DefSel = 0x01;
-		dirty = true;
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "xy-plot (%s)", name);
-#else
-			i = sprintf(TmpTxt, "xy-plot (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		nPoints = 0L;
-		ExecInput(Desc);
-		ForEach(FE_PARENT, 0L, 0L);
-		return true;
-	case FILE_WRITE:
-		if(TheLine) TheLine->FileIO(rw);
-		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->FileIO(rw);
-		if(Errors) for(i = 0; i < nPoints; i++) if(Errors[i]) Errors[i]->FileIO(rw);
-		if(Arrows) for(i = 0; i < nPoints; i++) if(Arrows[i]) Arrows[i]->FileIO(rw);
-		if(DropLines) for(i = 0; i < nPoints; i++) if(DropLines[i]) DropLines[i]->FileIO(rw);
-		if(Labels) for(i = 0; i < nPoints; i++) if(Labels[i]) Labels[i]->FileIO(rw);
-		if(Bars) for(i = 0; i < nPoints; i++) if(Bars[i]) Bars[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "PlotScatt", Desc);
-		}
-	return false;
-}
-
-bool
-xyStat::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"hide", typNZINT, &hidden, 0L},
-		{"Bounds", typFRECT, &Bounds, 0L},
-		{"DefSym", typNZINT, &DefSym, 0L},
-		{"baDist", typLFPOINT, &BarDist, 0L},
-		{"confi", typLFLOAT, &ci, 0L},
-		{"xRange", typTEXT, &xRange, 0L},
-		{"yRange", typTEXT, &yRange, 0L},
-		{"prefix", typTEXT, &case_prefix, 0L},
-		{"x_axis", typNZINT, &use_xaxis, 0L},
-		{"y_axis", typNZINT, &use_yaxis, 0L},
-		{"Bars", typOBJLST, &Bars, &nPoints},
-		{"Symbols", typOBJLST, &Symbols, &nPoints},
-		{"PL", typGOBJ, &TheLine, 0L},
-		{"ErrBars", typOBJLST, &Errors, &nPoints},
-		{"Labels", typOBJLST, &Labels, &nPoints},
-		{"x_info", typTEXT, &x_info, 0L},
-		{"y_info", typLAST | typTEXT, &y_info, 0L}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		//most initialistion is done by PlotScatt::FileIO
-		curr_data = 0L;
-		case_prefix = 0L;
-		ci = 95.0;
-		return true;
-	case FILE_READ:
-		nPoints = 0L;
-		ExecInput(Desc);
-		ForEach(FE_PARENT, 0L, 0L);
-		return true;
-	case FILE_WRITE:
-		if(TheLine) TheLine->FileIO(rw);
-		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->FileIO(rw);
-		if(Errors) for(i = 0; i < nPoints; i++) if(Errors[i]) Errors[i]->FileIO(rw);
-		if(Labels) for(i = 0; i < nPoints; i++) if(Labels[i]) Labels[i]->FileIO(rw);
-		if(Bars) for(i = 0; i < nPoints; i++) if(Bars[i]) Bars[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "xyStat", Desc);
-		}
-	return false;
-}
-
-void
-FreqDist::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(plots) for(i = 0; i < nPlots; i++) if(plots[i]) plots[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-FreqDist::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"hide", typNZINT, &hidden, 0L},
-		{"ssRef", typTEXT, &ssRef, 0L},
-		{"x_axis", typNZINT, &use_xaxis, 0L},
-		{"y_axis", typNZINT, &use_yaxis, 0L},
-		{"cl_start", typNZLFLOAT, &start, 0L},
-		{"cl_size", typLFLOAT, &step, 0L},
-		{"BarLine", typLINEDEF, &BarLine, 0L},
-		{"BarFill", typFILLDEF, &BarFill, 0L},
-		{"BarFillLine", typLINEDEF, &HatchLine, 0L},
-		{"plots", typLAST | typOBJLST, &plots, &nPlots}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		memcpy(&BarFill, defs.GetFill(), sizeof(FillDEF));
-		BarFill.color = 0x00c0ffffL;
-		if(BarFill.hatch) memcpy(&HatchLine, BarFill.hatch, sizeof(LineDEF));
-		BarFill.hatch = &HatchLine;
-		memcpy(&BarLine, defs.GetOutLine(), sizeof(LineDEF));
-		curr_data=0L;		dirty = true;
-		dmin = HUGE_VAL, dmax = -HUGE_VAL;
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "freq. dist. (%s)", name);
-#else
-			i = sprintf(TmpTxt, "freq. dist. (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		if(plots) for(i = 0; i < nPlots; i++) if(plots[i]) plots[i]->parent=this;
-		return true;
-	case FILE_WRITE:
-		if(plots) for(i = 0; i < nPlots; i++) if(plots[i]) plots[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "FreqDist", Desc);
-		}
-	return false;
-}
-
-void
-Regression::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(rLine) rLine->RegGO(n);
-		if(sde) sde->RegGO(n);
-		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-Regression::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"hide", typNZINT, &hidden, 0L},
-		{"Bounds", typFRECT, &Bounds, 0L},
-		{"xRange", typTEXT, &xRange, 0L},
-		{"yRange", typTEXT, &yRange, 0L},
-		{"x_axis", typNZINT, &use_xaxis, 0L},
-		{"y_axis", typNZINT, &use_yaxis, 0L},
-		{"Line", typGOBJ, &rLine, 0L},
-		{"Ellipse", typGOBJ, &sde, 0L},
-		{"Symbols", typLAST | typOBJLST, &Symbols, &nPoints}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		dirty = true;
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "regression (%s)", name);
-#else
-			i = sprintf(TmpTxt, "regression (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		nPoints = 0L;
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		if(rLine) rLine->FileIO(rw);
-		if(sde) sde->FileIO(rw);
-		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "Regression", Desc);
-		}
-	return false;
-}
-
-void
-BubblePlot::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(Bubbles) for(i = 0; i < nPoints; i++) if(Bubbles[i]) Bubbles[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-BubblePlot::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"hide", typNZINT, &hidden, 0L},
-		{"Bounds", typFRECT, &Bounds, 0L},
-		{"x_axis", typNZINT, &use_xaxis, 0L},
-		{"y_axis", typNZINT, &use_yaxis, 0L},
-		{"Line", typLINEDEF, &BubbleLine, 0L},
-		{"FillLine", typLINEDEF, &BubbleFillLine, 0L},
-		{"Fill", typFILLDEF, &BubbleFill, 0L},
-		{"Bubbles", typLAST | typOBJLST, &Bubbles, &nPoints}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		BubbleFill.color = defs.Color(COL_BUBBLE_FILL);
-		BubbleLine.color = defs.Color(COL_BUBBLE_LINE);
-		BubbleLine.width = DefSize(SIZE_BUBBLE_LINE);
-		BubbleFillLine.color = defs.Color(COL_BUBBLE_FILLLINE);
-		BubbleFillLine.width = DefSize(SIZE_BUBBLE_HATCH_LINE);
-		BubbleFill.hatch = &BubbleFillLine;
-		dirty = true;
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "bubbles (%s)", name);
-#else
-			i = sprintf(TmpTxt, "bubbles (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		if(Bubbles) for(i = 0; i < nPoints; i++) if(Bubbles[i]) Bubbles[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "BubblePlot", Desc);
-		}
-	return false;
-}
-
-void
-PolarPlot::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(Plots) for(i = 0; i < nPlots; i++) if(Plots[i]) Plots[i]->RegGO(n);
-		if(Axes) for(i = 0; i < nAxes; i++) if(Axes[i]) Axes[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-PolarPlot::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"hide", typNZINT, &hidden, 0L},
-		{"Bounds", typFRECT, &Bounds, 0L},
-		{"ang_offs", typLFLOAT, &offs, 0L},
-		{"Plots", typOBJLST, &Plots, (long*)&nPlots},
-		{"Axes", typOBJLST, &Axes, (long*)&nAxes},
-		{"FillLine", typLINEDEF, &FillLine, 0L},
-		{"Fill", typLAST | typFILLDEF, &Fill, 0L}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		CurrDisp = 0L;
-		InitVarsGO(Desc);
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "polar root (%s)", name);
-#else
-			i = sprintf(TmpTxt, "polar root (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		if(Plots) for(i = 0; i < nPlots; i++) if(Plots[i]) Plots[i]->FileIO(rw);
-		if(Axes) for(i = 0; i < nAxes; i++) if(Axes[i]) Axes[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "PolarPlot", Desc);
-		}
-	return false;
-}
-
-void
-BoxPlot::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(Boxes) for(i = 0; i < nPoints; i++) if(Boxes[i]) Boxes[i]->RegGO(n);
-		if(Whiskers) for(i = 0; i < nPoints; i++) if(Whiskers[i]) Whiskers[i]->RegGO(n);
-		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->RegGO(n);
-		if(Labels) for(i = 0; i < nPoints; i++) if(Labels[i]) Labels[i]->RegGO(n);
-		if(TheLine) TheLine->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-BoxPlot::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"hide", typNZINT, &hidden, 0L},
-		{"Bounds", typFRECT, &Bounds, 0L},
-		{"xRange", typTEXT, &xRange, 0L},
-		{"yRange", typTEXT, &yRange, 0L},
-		{"prefix", typTEXT, &case_prefix, 0L},
-		{"boDist", typLFPOINT, &BoxDist, 0L},
-		{"ci_box", typNZLFLOAT, &ci_box, 0L},
-		{"ci_err", typNZLFLOAT, &ci_err, 0L},
-		{"x_axis", typNZINT, &use_xaxis, 0L},
-		{"y_axis", typNZINT, &use_yaxis, 0L},
-		{"Boxes", typOBJLST, &Boxes, &nPoints},
-		{"Whiskers", typOBJLST, &Whiskers, &nPoints},
-		{"Symbols", typOBJLST, &Symbols, &nPoints},
-		{"Labels", typOBJLST, &Labels, &nPoints},
-		{"Line", typLAST | typGOBJ, &TheLine, 0L}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		dirty = true;		InitVarsGO(Desc);
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "boxes (%s)", name);
-#else
-			i = sprintf(TmpTxt, "boxes (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		curr_data = 0L;
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		if(Boxes) for(i = 0; i < nPoints; i++) 
-			if(Boxes[i]) Boxes[i]->FileIO(rw);
-		if(Whiskers) for(i = 0; i < nPoints; i++) 
-			if(Whiskers[i]) Whiskers[i]->FileIO(rw);
-		if(Symbols) for(i = 0; i < nPoints; i++) 
-			if(Symbols[i]) Symbols[i]->FileIO(rw);
-		if(Labels) for(i = 0; i < nPoints; i++) 
-			if(Labels[i]) Labels[i]->FileIO(rw);
-		if(TheLine) TheLine->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "BoxPlot", Desc);
-		}
-	return false;
-}
-
-void
-DensDisp::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(Boxes) for(i = 0; i < nPoints; i++) if(Boxes[i]) Boxes[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-DensDisp::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"hide", typNZINT, &hidden, 0L},
-		{"Bounds", typFRECT, &Bounds, 0L},
-		{"xRange", typTEXT, &xRange, 0L},
-		{"yRange", typTEXT, &yRange, 0L},
-		{"x_axis", typNZINT, &use_xaxis, 0L},
-		{"y_axis", typNZINT, &use_yaxis, 0L},
-		{"Line", typLINEDEF, &DefLine, 0L},
-		{"FillLine", typLINEDEF, &DefFillLine, 0L},
-		{"Fill", typFILLDEF, &DefFill, 0L},
-		{"Boxes", typLAST | typOBJLST, &Boxes, &nPoints}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		return InitVarsGO(Desc);
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		if(Boxes) for(i = 0; i < nPoints; i++) if(Boxes[i]) Boxes[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "DensDisp", Desc);
-		}
-	return false;
-}
-
-void
-StackBar::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(Boxes) for(i = 0; i < numPlots; i++) if(Boxes[i]) Boxes[i]->RegGO(n);
-		if(xyPlots) for(i = 0; i < numXY; i++) if(xyPlots[i]) xyPlots[i]->RegGO(n);
-		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) Polygons[i]->RegGO(n);
-		if(Lines) for(i = 0; i < numPL; i++) if(Lines[i]) Lines[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-StackBar::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Bounds", typFRECT, &Bounds, 0L},
-		{"hide", typNZINT, &hidden, 0L},
-		{"x_axis", typNZINT, &use_xaxis, 0L},
-		{"y_axis", typNZINT, &use_yaxis, 0L},
-		{"cumData", typNZINT, &cum_data_mode, 0L},
-		{"StartVal", typNZLFLOAT, &StartVal, 0L},
-		{"Dspm", typNZLFPOINT, &dspm, 0L},
-		{"ssXrange", typTEXT, &ssXrange, 0L},
-		{"ssYrange", typTEXT, &ssYrange, 0L},
-		{"BoxBars", typOBJLST, &Boxes, (long*)&numPlots},
-		{"Plots", typOBJLST, &xyPlots, (long*)&numXY},
-		{"Polygons", typOBJLST, &Polygons, (long*)&numPG},
-		{"Lines", typLAST | typOBJLST, &Lines, (long*)&numPL}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		dirty = true;	CumData = 0L;
-		InitVarsGO(Desc);
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "stack (%s)", name);
-#else
-			i = sprintf(TmpTxt, "stack (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		return true;
-	case FILE_WRITE:
-		if(Boxes) for(i = 0; i < numPlots; i++) if(Boxes[i]) Boxes[i]->FileIO(rw);
-		if(xyPlots) for(i = 0; i < numXY; i++) if(xyPlots[i]) xyPlots[i]->FileIO(rw);
-		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) Polygons[i]->FileIO(rw);
-		if(Lines) for(i = 0; i < numPL; i++) if(Lines[i]) Lines[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "Stacked", Desc);
-		}
-	return false;
-}
-
-void
-PieChart::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(Segments) for(i = 0; i < nPts; i++) if(Segments[i]) Segments[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-PieChart::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"ssRefA", typTEXT, &ssRefA, 0L},
-		{"ssRefR", typTEXT, &ssRefR, 0L},
-		{"hide", typNZINT, &hidden, 0L},
-		{"CtDef", typLFPOINT, &CtDef, 0L},
-		{"FacRad", typLFLOAT, &FacRad, 0L},
-		{"Segs", typLAST | typOBJLST, &Segments, (long*)&nPts}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		Bounds.Xmax = Bounds.Ymax = 100.0f;
-		Bounds.Xmin = Bounds.Ymin = -100.0f;
-		InitVarsGO(Desc);
-		CtDef.fx = 90.0;	CtDef.fy = 360.0;
-		FacRad = 1.0;
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "pie chart (%s)", name);
-#else
-			i = sprintf(TmpTxt, "pie chart (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		if(Segments) for(i = 0; i < nPts; i++) if(Segments[i]) Segments[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "SegChart", Desc);
-		}
-	return false;
-}
-
-void
-GoGroup::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(Objects) for(i = 0; i < nObs; i++) if(Objects[i]) Objects[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-GoGroup::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Pos", typNZLFPOINT, &fPos, 0L},
-		{"hide", typNZINT, &hidden, 0L},
-		{"Items", typLAST | typOBJLST, &Objects, (long*)&nObs}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		Bounds.Xmax = Bounds.Ymax = 100.0f;
-		Bounds.Xmin = Bounds.Ymin = -100.0f;
-		return InitVarsGO(Desc);
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		if(Objects) for(i = 0; i < nObs; i++) if(Objects[i]) Objects[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "group", Desc);
-		}
-	return false;
-}
-
-void
-Scatt3D::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(Line) Line->RegGO(n);
-		if(rib) rib->RegGO(n);
-		if(Balls) for(i = 0; i < nBalls; i++) if(Balls[i]) Balls[i]->RegGO(n);
-		if(Columns) for(i = 0; i < nColumns; i++) if(Columns[i]) Columns[i]->RegGO(n);
-		if(DropLines) for(i = 0; i < nDropLines; i++) if(DropLines[i]) DropLines[i]->RegGO(n);
-		if(Arrows) for(i = 0; i < nArrows; i++) if(Arrows[i]) Arrows[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-Scatt3D::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"ssRefX", typTEXT, &ssRefX, 0L},
-		{"ssRefY", typTEXT, &ssRefY, 0L},
-		{"ssRefZ", typTEXT, &ssRefZ, 0L},
-		{"DataDesc", typTEXT, &data_desc, 0L},
-		{"hide", typNZINT, &hidden, 0L},
-		{"x_axis", typNZINT, &use_xaxis, 0L},
-		{"y_axis", typNZINT, &use_yaxis, 0L},
-		{"z_axis", typNZINT, &use_zaxis, 0L},
-		{"Line", typGOBJ, &Line, 0L},
-		{"Balls", typOBJLST, &Balls, &nBalls},
-		{"Columns", typOBJLST, &Columns, &nColumns},
-		{"DropLines", typOBJLST, &DropLines, &nDropLines},
-		{"ParaV", typGOBJ, &rib, 0L},
-		{"Arrows", typLAST | typOBJLST, &Arrows, &nArrows}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		c_flags = 0L;
-		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-		xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
-		xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
-		dirty = true;
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "xyz-plot (%s)", name);
-#else
-			i = sprintf(TmpTxt, "xyz-plot (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		//now set parent in all children
-		if(Balls) for(i = 0; i < nBalls; i++) if(Balls[i]) Balls[i]->parent = this;
-		if(Columns) for(i = 0; i < nColumns; i++) if(Columns[i]) Columns[i]->parent = this;
-		if(DropLines) for(i = 0; i < nDropLines; i++) if(DropLines[i]) DropLines[i]->parent = this;
-		if(Arrows) for(i = 0; i < nArrows; i++) if(Arrows[i]) Arrows[i]->parent = this;
-		if(Line) Line->parent = this;
-		if(rib) rib->parent = this;
-		return true;
-	case FILE_WRITE:
-		if(Line) Line->FileIO(rw);
-		if(rib) rib->FileIO(rw);
-		if(Balls) for(i = 0; i < nBalls; i++) if(Balls[i]) Balls[i]->FileIO(rw);
-		if(Columns) for(i = 0; i < nColumns; i++) if(Columns[i]) Columns[i]->FileIO(rw);
-		if(DropLines) for(i = 0; i < nDropLines; i++) if(DropLines[i]) DropLines[i]->FileIO(rw);
-		if(Arrows) for(i = 0; i < nArrows; i++) if(Arrows[i]) Arrows[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "Scatt3D", Desc);
-		}
-	return false;
-}
-
-void
-Ribbon::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-Ribbon::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"hide", typNZINT, &hidden, 0L},
-		{"z-pos", typNZLFLOAT, &z_value},
-		{"z-width", typNZLFLOAT, &z_width},
-		{"relwidth", typNZLFLOAT, &relwidth},
-		{"ssRefX", typTEXT, &ssRefX, 0L},
-		{"ssRefY", typTEXT, &ssRefY, 0L},
-		{"ssRefZ", typTEXT, &ssRefZ, 0L},
-		{"DataDesc", typTEXT, &data_desc, 0L},
-		{"Line", typLINEDEF, &Line, 0L},
-		{"Fill", typFILLDEF, &Fill, 0L},
-		{"values", typFPLST3D, &values, &nVal},
-		{"Planes", typLAST | typOBJLST, &planes, &nPlanes}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-		xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
-		xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
-		relwidth = 0.6;				dirty = true;
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "ribbon (%s)", name);
-#else
-			i = sprintf(TmpTxt, "ribbon (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		//now set parent in all children
-		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->parent = this;
-		return true;
-	case FILE_WRITE:
-		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "Ribbon", Desc);
-		}
-	return false;
-}
-
-void
-Grid3D::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->RegGO(n);
-		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-Grid3D::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"hide", typNZINT, &hidden, 0L},
-		{"Start", typPOINT3D, &start, 0L},
-		{"Step", typPOINT3D, &step, 0L},
-		{"Line", typLINEDEF, &Line, 0L},
-		{"Fill", typFILLDEF, &Fill, 0L},
-		{"lines", typOBJLST, &lines, &nLines},
-		{"planes", typLAST | typOBJLST, &planes, &nPlanes}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-		xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
-		xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
-		step.fx = step.fz = 1.0;
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "grid (%s)", name);
-#else
-			i = sprintf(TmpTxt, "grid (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		//now set parent in all children
-		if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->parent = this;
-		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->parent = this;
-		return true;
-	case FILE_WRITE:
-		if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->FileIO(rw);
-		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "Grid3D", Desc);
-		}
-	return false;
-}
-
-bool
-Limits::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Bounds", typLAST | typFRECT, &Bounds, 0L}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "limits (%s)", name);
-#else
-			i = sprintf(TmpTxt, "limits (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), "Limits", Desc);
-		}
-	return false;
-}
-
-void
-Function::RegGO(void *n)
-{
-	if(n) {
-		if(dl)dl->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-Function::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"hide", typNZINT, &hidden, 0L},
-		{"x1", typNZLFLOAT, &x1, 0L},
-		{"x2", typNZLFLOAT, &x2, 0L},
-		{"xstep", typNZLFLOAT, &xstep, 0L},
-		{"Line", typLINEDEF, &Line, 0L},
-		{"f_xy", typTEXT, &cmdxy, 0L},
-		{"param", typTEXT, &param, 0L},
-		{"DataLine", typGOBJ, &dl, 0L},
-		{"Desc", typLAST | typTEXT, &name, 0L}};
-	int i;
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		cmdxy = param = 0L;
-		memcpy(&Line, defs.GetLine(), sizeof(LineDEF));
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "function (%s)", name);
-#else
-			i = sprintf(TmpTxt, "function (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		if(dl) dl->parent = this;
-		return true;
-	case FILE_WRITE:
-		if(dl) dl->FileIO(rw);
-		ExecOutput(Notary->RegisterGO(this), "Function", Desc);
-		}
-	return false;
-}
-
-void
-FitFunc::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-FitFunc::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"hide", typNZINT, &hidden, 0L},
-		{"ssXref", typTEXT, &ssXref, 0L},
-		{"ssYref", typTEXT, &ssYref, 0L},
-		{"x1", typNZLFLOAT, &x1, 0L},
-		{"x2", typNZLFLOAT, &x2, 0L},
-		{"xstep", typNZLFLOAT, &xstep, 0L},
-		{"conv", typNZLFLOAT, &conv, 0L},
-		{"chi2", typNZLFLOAT, &chi2, 0L},
-		{"maxiter", typNZINT, &maxiter, 0L},
-		{"Line", typLINEDEF, &Line, 0L},
-		{"f_xy", typTEXT, &cmdxy, 0L},
-		{"p_xy", typTEXT, &parxy, 0L},
-		{"Symbols", typLAST | typOBJLST, &Symbols, &nPoints}};
-	int i;
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		cmdxy = parxy = 0L;
-		memcpy(&Line, defs.GetLine(), sizeof(LineDEF));
-		conv = 1.0e-15;	maxiter = 100;
-		dl = 0L;
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "fit function (%s)", name);
-#else
-			i = sprintf(TmpTxt, "fit function (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		if(Symbols) for(i = 0; i < nPoints; i++)
-			if(Symbols[i]) Symbols[i]->parent = this;
-		return true;
-	case FILE_WRITE:
-		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "FitFunc", Desc);
-		}
-	return false;
-}
-
-bool
-NormQuant::FileIO(int rw)
-{
-	lfPOINT *dt;
-	long cnt;
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"nData", typINT, &nData, 0L},
-		{"Data", typFPLST, &dt, &cnt},
-		{"ssRef", typTEXT, &ssRef, 0L},
-		{"x_info", typTEXT, &x_info, 0L},
-		{"y_info", typLAST | typTEXT, &y_info, 0L}};
-	int i, j, l;
-
-	if(rw == FILE_WRITE) {
-		if(nData < 4) return false;
-		l = (nData >>1) +1;				cnt = l;
-		if(!(dt = (lfPOINT *)calloc(l, sizeof(lfPOINT))))return false;
-		for(i = j = 0; i < nData; i += 2, j++) {
-			dt[j].fx = src_data[i];		dt[j].fy = src_data[i+1];
-			}
-		}
-	else {
-		dt = 0L;		cnt = 0;
-		}
-	switch(rw) {
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		sy = new Symbol(this, data, 0.0, 0.0, SYM_CIRCLE);
-		x_vals = y_vals = src_data = 0L;
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "normal quantiles (%s)", name);
-#else
-			i = sprintf(TmpTxt, "normal quantiles (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		if(dt && cnt > 1 && (src_data = (double*)malloc(nData*sizeof(double)))) {
-			for(i = j = 0, l = nData-1; i < nData; i += 2, j++) {
-				src_data[i] = dt[j].fx;		if(i < l) src_data[i+1] = dt[j].fy;
-				}
-			free(dt);
-			}
-		return true;
-	case FILE_WRITE:
-		ExecOutput(Notary->RegisterGO(this), "NormQuant", Desc);
-		free(dt);
-		return true;
-		}
-	return false;
-}
-
-bool
-GridLine::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"Line", typLINEDEF, &LineDef, 0L},
-		{"flags", typLAST | typDWORD, &flags, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		ncpts = 0;		cpts = 0L;	gl1 = gl2 = gl3 = 0L;	ls = 0L;
-		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;	mo = 0L;
-		return true;
-	case FILE_READ:
-		return ExecInput(Desc);
-	case FILE_WRITE:
-		return ExecOutput(Notary->RegisterGO(this), 
-			Id == GO_GRIDLINE ?(char*)"GridLine" : 
-			Id == GO_GRIDRADIAL? (char*)"GridRadial" : (char*)"GridLine3D", Desc);
-		}
-	return false;
-}
-
-void
-Tick::RegGO(void *n)
-{
-	if(n) {
-		if(Grid && (flags & AXIS_GRIDLINE)) Grid->RegGO(n);
-		if(label) label->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-Tick::FileIO(int rw)
-{
-	GraphObj *gl = Grid;
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"Val", typLFLOAT, &value, 0L},
-		{"Flags", typDWORD, &flags, 0L},
-		{"Rot", typNZLFLOAT, &angle, 0L},
-		{"GridType", typINT, &gl_type, 0L},
-		{"Grid", typGOBJ, &gl, 0L},
-		{"Label", typGOBJ, &label, 0L},
-		{"Size", typLAST | typLFLOAT, &size, 0L}};
-
-	switch(rw) {
-	case SAVE_VARS:
-		if(Grid && (flags & AXIS_GRIDLINE)) Grid->FileIO(rw);
-		if(label) label->FileIO(rw);
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
-		fix = fiy = 0.0f;	ls = 0L;	Grid = 0L;	mo = 0L;
-		size = DefSize(SIZE_AXIS_TICKS);
-		return true;
-	case FILE_READ:
-		ExecInput(Desc);
-		Grid = (GridLine*) gl;
-		if(Grid)Grid->parent = this;
-		if(label)label->parent = this;
-		return true;
-	case FILE_WRITE:
-		if(Grid && (flags & AXIS_GRIDLINE)) Grid->FileIO(rw);
-		else gl = 0L;
-		if(label) label->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "Tick", Desc);
-		}
-	return false;
-}
-
-void
-Axis::TickFile(char *name)
-{
-	ReadCache *ca;
-	int i, j, k, nt;
-	char line[500], item[20];
-	Tick **ttck;
-
-	if(!name) return;
-	if(!(ca = new ReadCache())) return;
-	if(! ca->Open(name)) {
-		delete ca;
-#ifdef USE_WIN_SECURE
-		sprintf_s(TmpTxt, TMP_TXT_SIZE, "Error open file \"%s\"\nfor axis ticks", name);
-#else
-		sprintf(TmpTxt, "Error open file \"%s\"\nfor axis ticks", name);
-#endif
-		ErrorBox(TmpTxt);
-		return;
-		}
-	Command(CMD_FLUSH, 0L, 0L);
-	if(!(Ticks = ((Tick**)calloc(nt = 100, sizeof(Tick*))))) return;
-	for(i = 0; ; i++) {
-		j = k = 0;
-		ca->ReadLine(line, sizeof(line));
-		if(!line[0]) break;
-		while(line[j] && line[j] < 33) j++;
-		do{ item[k] = line[j++]; }
-			while(item[k] >32 && item[k++] != '=' && k <sizeof(item) && j <sizeof(line));
-		item[k--] = 0;		if(!line[j-1])j--;
-		while(k && !(isdigit(item[k])))item[k--]=0;
-		while(line[j] && (line[j]<33 || line[j] == '"'))j++;
-		k = (int)strlen(line);
-		while(k >=j && (line[k] < 33 || line[k] == '"')) line[k--] = 0;
-		//realloc table if necessary
-		if(NumTicks >= nt) {
-			if((ttck= (Tick**)realloc(Ticks, (nt += 1000)*sizeof(Tick*))))Ticks= ttck;
-			else NumTicks--;
-			}
-		//now add tick to table
-		if(!(Ticks[NumTicks] = new Tick(this, data, atof(item),
-			line[j] ? axis->flags : axis->flags | AXIS_MINORTICK)))break;
-		Ticks[NumTicks]->Command(CMD_SETTEXT, line+j, 0L);
-		NumTicks++;
-		}
-	ca->Close();
-	if(!NumTicks && Ticks) {
-		free(Ticks);
-		NumTicks = 0;
-		}
-	delete ca;
-}
-
-void
-Axis::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		for(i = 0; Ticks && i< NumTicks; i++) if(Ticks[i]) Ticks[i]->RegGO(n);
-		if(axisLabel) axisLabel->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-Axis::FileIO(int rw)
-{
-	char *tickfile = 0L;
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"sAxLine", typLFLOAT, &sizAxLine, 0L},
-		{"sAxTick", typLFLOAT, &sizAxTick, 0L},
-		{"BrkGap", typLFLOAT, &brkgap, 0L},
-		{"BrkSymSize", typLFLOAT, &brksymsize, 0L},
-		{"BrkSym", typINT, &brksym, 0L},
-		{"sTickLabel", typLFLOAT, &sizAxTickLabel, 0L},
-		{"tick_type", typNZINT, &tick_type, 0L},
-		{"tick_angle", typNZLFLOAT, &tick_angle, 0L},
-		{"LbDist", typNZLFPOINT, &lbdist, 0L},
-		{"TickLbDist", typNZLFPOINT, &tlbdist, 0L}, 
-		{"Color", typDWORD, &colAxis, 0L},
-		{"AxisDef", typPTRAXDEF, &axis},
-		{"GridLine", typLINEDEF, &GridLine, 0L},
-		{"GridType", typINT, &gl_type, 0L},
-		{"Ticks", typOBJLST, &Ticks, (long*)&NumTicks},
-		{"Label", typGOBJ, &axisLabel, 0L},
-		{"TickFile", typTEXT, &tickfile, 0L},
-		{"ssRefTV", typTEXT, &ssMATval, 0L},
-		{"ssRefTL", typTEXT, &ssMATlbl, 0L},
-		{"ssRefMT", typTEXT, &ssMITval, 0L},
-		{"tlbDef", typLAST | typTXTDEF, &tlbdef, 0L}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		sizAxLine = DefSize(SIZE_AXIS_LINE);
-		sizAxTick = DefSize(SIZE_AXIS_TICKS);
-		sizAxTickLabel = DefSize(SIZE_TICK_LABELS);
-		colAxis = parent ? parent->GetColor(COL_AXIS) : defs.Color(COL_AXIS);
-		GridLine.color = 0x00808080L;
-		GridLine.pattern = 0xf8f8f8f8L;
-		brksymsize = DefSize(SIZE_TICK_LABELS);
-		brkgap = DefSize(SIZE_AXIS_TICKS);
-		brksym = 2;
-		tlbdef.ColTxt = parent ? parent->GetColor(COL_AXIS) : defs.Color(COL_AXIS);
-		tlbdef.ColBg = parent ? parent->GetColor(COL_BG) : defs.Color(COL_AXIS);
-		tlbdef.RotBL = tlbdef.RotCHAR = 0.0f;
-		tlbdef.fSize = DefSize(SIZE_TICK_LABELS);	tlbdef.Align = TXA_VCENTER | TXA_HCENTER;
-		tlbdef.Style = TXS_NORMAL;					tlbdef.Mode = TXM_TRANSPARENT;
-		tlbdef.Font = FONT_HELVETICA;				atv = 0L;
-		tlbdef.text = 0L;		l_segs = 0L;		nl_segs = 0;
-		drawOut = scaleOut = 0L;					bModified = false;
-		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
-		mo = 0L;
-		return true;
-	case FILE_READ:
-		if(axisLabel)DeleteGO(axisLabel);
-		if(tickfile) free(tickfile);		if(ssMATval) free(ssMATval);
-		if(ssMATlbl) free(ssMATlbl);		if(ssMITval) free(ssMITval);
-		tickfile = 0L;
-		if(ExecInput(Desc) && tickfile && tickfile[0]){
-			TickFile(tickfile);
-			free(tickfile);					tickfile = 0L;
-			}
-		if(axis) axis->owner = this;
-		if(axisLabel)axisLabel->parent = this;
-		if(Ticks) for(i = 0; i < NumTicks; i++) if(Ticks[i]) Ticks[i]->parent = this;
-		return true;
-	case FILE_WRITE:
-		//do all ticks
-		for(i = 0; Ticks && i< NumTicks; i++) if(Ticks[i]) Ticks[i]->FileIO(rw);
-		if(axisLabel) axisLabel->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "Axis", Desc);
-		}
-	return false;
-}
-
-void
-Plot3D::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(plots) for(i = 0; plots && i< nPlots; i++) if(plots[i]) plots[i]->RegGO(n);
-		if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-Plot3D::FileIO(int rw)
-{
-	fPOINT3D rot_vec, rot_ang;
-	descIO Desc[] = {
-		{"hide", typNZINT, &hidden, 0L},
-		{"xBounds", typLFPOINT, &xBounds, 0L},
-		{"yBounds", typLFPOINT, &yBounds, 0L},
-		{"zBounds", typLFPOINT, &zBounds, 0L},
-		{"Corner1", typPOINT3D, &cub1, 0L},
-		{"Corner2", typPOINT3D, &cub2, 0L},
-		{"Center", typPOINT3D, &rotC, 0L},
-		{"rot_vec", typPOINT3D, &rot_vec, 0L},
-		{"rot_ang", typPOINT3D, &rot_ang, 0L},
-		{"x_axis", typNZINT, &use_xaxis, 0L},
-		{"y_axis", typNZINT, &use_yaxis, 0L},
-		{"z_axis", typNZINT, &use_zaxis, 0L},
-		{"Axes", typOBJLST, &Axes, (long*)&nAxes},
-		{"Plots", typLAST | typOBJLST, &plots, (long*)&nPlots}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		InitVarsGO(Desc);
-		drag = 0L;		moveable = 1;
-		//set up RotDef
-		RotDef[0] = 0.919384;		RotDef[1] = 0.389104;		RotDef[2] = -0.057709;
-		RotDef[3] = 0.327146;		RotDef[4] = 0.944974;		RotDef[5] = 1.0-RotDef[4];
-		cub1.fx = DefSize(SIZE_GRECT_LEFT) + DefSize(SIZE_DRECT_LEFT);
-		cub2.fx = DefSize(SIZE_GRECT_LEFT) + DefSize(SIZE_DRECT_RIGHT);
-		cub1.fy = DefSize(SIZE_GRECT_TOP) + DefSize(SIZE_DRECT_BOTTOM);
-		cub2.fy = DefSize(SIZE_GRECT_TOP) + DefSize(SIZE_DRECT_TOP);
-		cub1.fy += DefSize(SIZE_DRECT_TOP);	cub2.fy += DefSize(SIZE_DRECT_TOP);
-		cub1.fz = 0.0;
-		cub2.fz = DefSize(SIZE_DRECT_BOTTOM) - DefSize(SIZE_DRECT_TOP);
-		rotC.fx = (cub1.fx + cub2.fx)/2.0;		rotC.fy = (cub1.fy + cub2.fy)/2.0;
-		rotC.fz = (cub1.fz + cub2.fz)/2.0;
-		dispObs = 0L;		nmaxObs = 0;	crea_flags = 0L;		dirty = true;
-		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-		xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
-		xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
-		Sc_Plots = 0L;		nscp = 0;
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "3D-root (%s)", name);
-#else
-			i = sprintf(TmpTxt, "3D-root (%s)", name);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		rot_vec.fx = 0.919384;	rot_vec.fy = 0.389104;	rot_vec.fz = -0.057709;
-		rot_ang.fx = 0.327146;	rot_ang.fy = 0.944974;	rot_ang.fz = 0.055026;
-		ExecInput(Desc);
-		RotDef[0] = rot_vec.fx;	RotDef[1] = rot_vec.fy;	RotDef[2] = rot_vec.fz;
-		RotDef[3] = rot_ang.fx;	RotDef[4] = rot_ang.fy;	RotDef[5] = rot_ang.fz;
-		return true;
-	case FILE_WRITE:
-		rot_vec.fx = RotDef[0];	rot_vec.fy = RotDef[1];	rot_vec.fz = RotDef[2];
-		rot_ang.fx = RotDef[3];	rot_ang.fy = RotDef[4];	rot_ang.fz = RotDef[5];
-		//do all plots
-		for(i = 0; plots && i< nPlots; i++) if(plots[i]) plots[i]->FileIO(rw);
-		//do all axes
-		if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "Plot3D", Desc);
-		}
-	return false;
-}
-
-void
-Func3D::RegGO(void *n)
-{
-}
-
-bool
-Func3D::FileIO(int rw)
-{
-	fPOINT3D rot_vec, rot_ang;
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"hide", typNZINT, &hidden, 0L},
-		{"xBounds", typLFPOINT, &xBounds, 0L},
-		{"yBounds", typLFPOINT, &yBounds, 0L},
-		{"zBounds", typLFPOINT, &zBounds, 0L},
-		{"Corner1", typPOINT3D, &cub1, 0L},
-		{"Corner2", typPOINT3D, &cub2, 0L},
-		{"Center", typPOINT3D, &rotC, 0L},
-		{"rot_vec", typPOINT3D, &rot_vec, 0L},
-		{"rot_ang", typPOINT3D, &rot_ang, 0L},
-		{"x_axis", typNZINT, &use_xaxis, 0L},
-		{"y_axis", typNZINT, &use_yaxis, 0L},
-		{"z_axis", typNZINT, &use_zaxis, 0L},
-		{"Axes", typOBJLST, &Axes, (long*)&nAxes},
-		{"Plots", typOBJLST, &plots, (long*)&nPlots},
-		{"x1", typNZLFLOAT, &x1, 0L},
-		{"x2", typNZLFLOAT, &x2, 0L},
-		{"xstep", typNZLFLOAT, &xstep, 0L},
-		{"z1", typNZLFLOAT, &x1, 0L},
-		{"z2", typNZLFLOAT, &x2, 0L},
-		{"zstep", typNZLFLOAT, &xstep, 0L},
-		{"Line", typLINEDEF, &Line, 0L},
-		{"Fill", typFILLDEF, &Fill, 0L},
-		{"f_xz", typTEXT, &cmdxy, 0L},
-		{"param", typLAST | typTEXT, &param, 0L}};
-	int i;
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		x1 = -20.0;		x2 = 20.0;	xstep = 2.0;
-		z1 = -20.0;		z2 = 20.0;	zstep = 2.0;
-		gda = 0L;		gob = 0L;
-		param = cmdxy = 0L;
-		Line.width = DefSize(SIZE_HAIRLINE);
-		Line.patlength = DefSize(SIZE_PATLENGTH);
-		Line.color = Line.pattern = 0x0L;
-		Fill.color = 0x00c0c0c0;
-		Fill.color2 = 0x00ffffff;
-		Fill.hatch = 0L;
-		Fill.type = FILL_LIGHT3D;
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "3D function (Plot %d)", cPlots);
-#else
-			i = sprintf(TmpTxt, "3D function (Plot %d)", cPlots);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		rot_vec.fx = 0.919384;	rot_vec.fy = 0.389104;	rot_vec.fz = -0.057709;
-		rot_ang.fx = 0.327146;	rot_ang.fy = 0.944974;	rot_ang.fz = 0.055026;
-		ExecInput(Desc);
-		RotDef[0] = rot_vec.fx;	RotDef[1] = rot_vec.fy;	RotDef[2] = rot_vec.fz;
-		RotDef[3] = rot_ang.fx;	RotDef[4] = rot_ang.fy;	RotDef[5] = rot_ang.fz;
-		return true;
-	case FILE_WRITE:
-		rot_vec.fx = RotDef[0];	rot_vec.fy = RotDef[1];	rot_vec.fz = RotDef[2];
-		rot_ang.fx = RotDef[3];	rot_ang.fy = RotDef[4];	rot_ang.fz = RotDef[5];
-		//do all plots
-		for(i = 0; plots && i< nPlots; i++) if(plots[i]) plots[i]->FileIO(rw);
-		//do all axes
-		if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->FileIO(rw);
-		ExecOutput(Notary->RegisterGO(this), "Func3D", Desc);
-		}
-	return false;
-}
-
-void
-FitFunc3D::RegGO(void *n)
-{
-}
-
-bool
-FitFunc3D::FileIO(int rw)
-{
-	fPOINT3D rot_vec, rot_ang;
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"hide", typNZINT, &hidden, 0L},
-		{"ssXref", typTEXT, &ssXref, 0L},
-		{"ssYref", typTEXT, &ssYref, 0L},
-		{"ssZref", typTEXT, &ssZref, 0L},
-		{"xBounds", typLFPOINT, &xBounds, 0L},
-		{"yBounds", typLFPOINT, &yBounds, 0L},
-		{"zBounds", typLFPOINT, &zBounds, 0L},
-		{"Corner1", typPOINT3D, &cub1, 0L},
-		{"Corner2", typPOINT3D, &cub2, 0L},
-		{"Center", typPOINT3D, &rotC, 0L},
-		{"rot_vec", typPOINT3D, &rot_vec, 0L},
-		{"rot_ang", typPOINT3D, &rot_ang, 0L},
-		{"x_axis", typNZINT, &use_xaxis, 0L},
-		{"y_axis", typNZINT, &use_yaxis, 0L},
-		{"z_axis", typNZINT, &use_zaxis, 0L},
-		{"Axes", typOBJLST, &Axes, (long*)&nAxes},
-		{"Plots", typOBJLST, &plots, (long*)&nPlots},
-		{"x1", typNZLFLOAT, &x1, 0L},
-		{"x2", typNZLFLOAT, &x2, 0L},
-		{"xstep", typNZLFLOAT, &xstep, 0L},
-		{"z1", typNZLFLOAT, &x1, 0L},
-		{"z2", typNZLFLOAT, &x2, 0L},
-		{"zstep", typNZLFLOAT, &xstep, 0L},
-		{"maxiter", typNZINT, &maxiter, 0L},
-		{"Line", typLINEDEF, &Line, 0L},
-		{"Fill", typFILLDEF, &Fill, 0L},
-		{"f_xz", typTEXT, &cmdxy, 0L},
-		{"param", typLAST | typTEXT, &param, 0L}};
-	int i;
-
-	switch(rw) {
-	case SAVE_VARS:
-		return SaveVarGO(Desc);
-	case INIT_VARS:
-		x1 = -20.0;		x2 = 20.0;	xstep = 2.0;
-		z1 = -20.0;		z2 = 20.0;	zstep = 2.0;
-		gda = 0L;		gob = 0L;
-		conv = 1.0e-15;	maxiter = 100;
-		param = cmdxy = ssXref = ssYref = ssZref = 0L;
-		Line.width = DefSize(SIZE_HAIRLINE);
-		Line.patlength = DefSize(SIZE_PATLENGTH);
-		Line.color = Line.pattern = 0x0L;
-		Fill.color = 0x00c0c0c0;
-		Fill.color2 = 0x00ffffff;
-		Fill.hatch = 0L;
-		Fill.type = FILL_LIGHT3D;
-		if(name) {
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "FitFunc3D (Plot %d)", cPlots);
-#else
-			i = sprintf(TmpTxt, "FitFunc3D (Plot %d)", cPlots);
-#endif
-			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
-			}
-		return true;
-	case FILE_READ:
-		rot_vec.fx = 0.919384;	rot_vec.fy = 0.389104;	rot_vec.fz = -0.057709;
-		rot_ang.fx = 0.327146;	rot_ang.fy = 0.944974;	rot_ang.fz = 0.055026;
-		ExecInput(Desc);
-		RotDef[0] = rot_vec.fx;	RotDef[1] = rot_vec.fy;	RotDef[2] = rot_vec.fz;
-		RotDef[3] = rot_ang.fx;	RotDef[4] = rot_ang.fy;	RotDef[5] = rot_ang.fz;
-		return true;
-	case FILE_WRITE:
-		rot_vec.fx = RotDef[0];	rot_vec.fy = RotDef[1];	rot_vec.fz = RotDef[2];
-		rot_ang.fx = RotDef[3];	rot_ang.fy = RotDef[4];	rot_ang.fz = RotDef[5];
-		//do all plots
-		for(i = 0; plots && i< nPlots; i++) if(plots[i]) plots[i]->FileIO(rw);
-		//do all axes
-		if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->FileIO(rw);
-		ExecOutput(Notary->RegisterGO(this), "FitFunc3D", Desc);
-		}
-	return false;
-}
-
-void
-Graph::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(Plots) for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->RegGO(n);
-		if(Axes) for(i = 0; i< NumAxes; i++) if(Axes[i]) Axes[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-Graph::FileIO(int rw)
-{
-	int ixax, iyax;
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"Units", typNZINT, &units, 0L},
-		{"Scale", typNZLFLOAT, &scale, 0L},
-		{"GRect", typFRECT, &GRect, 0L},
-		{"DRect", typFRECT, &DRect, 0L},
-		{"Bounds", typFRECT, &Bounds, 0L},
-		{"ColFrame", typDWORD, &ColGR, 0L},
-		{"ColFrameL", typDWORD, &ColGRL, 0L},
-		{"ColRec", typDWORD, &ColDR, 0L},
-		{"ColAxis", typDWORD, &ColAX, 0L},
-		{"Xaxis", typAXDEF, &x_axis, 0L},
-		{"Yaxis", typAXDEF, &y_axis, 0L},
-		{"DefXAxis", typINT, &ixax, 0L},
-		{"DefYAxis", typINT, &iyax, 0L},
-		{"Axes", typOBJLST, &Axes, (long*)&NumAxes},
-		{"Plots", typLAST | typOBJLST, &Plots, (long*)&NumPlots}};
-	int i;
-	bool bConvert = false;
-
-	ixax = iyax = -1;
-	switch(rw) {
-	case INIT_VARS:
-		InitVarsGO(Desc);						units = defs.cUnits = defs.dUnits;
-		OwnDisp = bDialogOpen = false;			dirty = true;
-		GRect.Ymin = defs.GetSize(SIZE_GRECT_TOP);		GRect.Ymax = defs.GetSize(SIZE_GRECT_BOTTOM);
-		GRect.Xmin = defs.GetSize(SIZE_GRECT_LEFT);		GRect.Xmax = defs.GetSize(SIZE_GRECT_RIGHT);
-		DRect.Ymin = defs.GetSize(SIZE_DRECT_TOP);		DRect.Ymax = defs.GetSize(SIZE_DRECT_BOTTOM);
-		DRect.Xmin = defs.GetSize(SIZE_DRECT_LEFT);		DRect.Xmax = defs.GetSize(SIZE_DRECT_RIGHT);
-		ColGR = defs.Color(COL_GRECT);					ColGRL = defs.Color(COL_GRECTLINE);
-		ColDR = defs.Color(COL_DRECT);					ColBG = defs.Color(COL_BG);
-		ColAX = defs.Color(COL_AXIS);
-		x_axis.max = y_axis.max = 1.0;					x_axis.owner = y_axis.owner = (void *)this;
-		rcDim.left = rcDim.right = rcDim.top = rcDim.bottom = 0;
-		rcUpd.left = rcUpd.right = rcUpd.top = rcUpd.bottom = 0;
-		CurrGO = 0L;		Disp = 0L;			Sc_Plots = 0L;
-		AxisTempl = 0;		nscp = 0;			CurrDisp = 0L;
-		ToolMode = TM_STANDARD;					zoom_def = 0L;
-		tl_pts = 0L;		tl_nPts = 0;		tickstyle = zoom_level = 0;
-		frm_g = frm_d = 0L;	PasteObj = 0L;		filename = 0L;
-		rc_mrk.left = rc_mrk.right = rc_mrk.top = rc_mrk.bottom = -1;
-		return true;
-	case FILE_READ:
-		units = 0;			//default to mm if statement mising in file
-		if((bConvert =ExecInput(Desc)) && ixax>=0 && iyax >=0 && Axes && 
-			NumAxes >= ixax+1 && NumAxes >= iyax) {
-			if(Axes[ixax]) Axes[ixax]->Command(CMD_SET_AXDEF, &x_axis, 0L);
-			if(Axes[iyax]) Axes[iyax]->Command(CMD_SET_AXDEF, &y_axis, 0L);
-			return true;
-			}
-		return bConvert;
-	case FILE_WRITE:
-		bModified = false;		if(scale == 1.0) scale = 0.0;
-		//find default axes
-		if(Axes) for(i = 0; Axes && i < NumAxes; i++) {
-			if(Axes[i] && Axes[i]->GetAxis() == &x_axis) ixax = i;
-			else if(Axes[i] && Axes[i]->GetAxis() == &y_axis) iyax = i;
-			}
-		if(Id == GO_GRAPH)RegGO(Notary);
-		//do all plots
-		if(Plots) for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->FileIO(rw);
-		//do all axes
-		if(Axes) for(i = 0; i< NumAxes; i++) if(Axes[i]) Axes[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "Graph", Desc);
-		}
-	return false;
-}
-
-void
-Page::RegGO(void *n)
-{
-	int i;
-
-	if(n) {
-		if(Plots) for(i = 0; i< NumPlots; i++) 
-			if(Plots[i] && Plots[i]->Id != GO_GRAPH) Plots[i]->RegGO(n);
-		((notary*)n)->AddRegGO(this);
-		}
-}
-
-bool
-Page::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"Type", typNZINT, &type, 0L},
-		{"Units", typNZINT, &units, 0L},
-		{"GRect", typFRECT, &GRect, 0L},
-		{"Plots", typLAST | typOBJLST, &Plots, (long*)&NumPlots}};
-	int i;
-
-	switch(rw) {
-	case INIT_VARS:
-		//assume that Graph::FileIO(INIT_VARS) has been executed
-		GRect.Xmin = GRect.Ymin = 0.0;
-		GetPaper(&GRect.Xmax, &GRect.Ymax);
-		ColBG = 0x00e8e8e8L;
-		LineDef.width = 0.0;	LineDef.patlength = 1.0;
-		LineDef.color = LineDef.pattern = 0x0L;
-		FillDef.type = FILL_NONE;
-		FillDef.color = 0x00ffffffL;	//use white paper
-		FillDef.scale = 1.0;
-		FillDef.hatch = 0L;		filename =0L;
-		return true;
-	case FILE_READ:
-		Graph::FileIO(rw);
-		return true;
-	case FILE_WRITE:
-		//do all plots
-		bModified = false;
-		if(Id == GO_PAGE)RegGO(Notary);
-		if(Plots) for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->FileIO(rw);
-		return ExecOutput(Notary->RegisterGO(this), "Page", Desc);
-		}
-	return false;
-}
-
-bool
-DefsRW::FileIO(int rw)
-{
-	descIO Desc[] = {
-		{"dUnits", typINT, &defs.dUnits, 0L},
-		{"cUnits", typINT, &defs.cUnits, 0L},
-		{"dtHeight", typINT, &dlgtxtheight, 0L},
-		{"ss_txt", typLFLOAT, &defs.ss_txt, 0L},
-		{"fmt_date", typTEXT, &defs.fmt_date, 0L},
-		{"fmt_datetime", typTEXT, &defs.fmt_datetime, 0L},
-		{"fmt_time", typTEXT, &defs.fmt_time, 0L},
-		{"curr_path", typTEXT, &defs.currPath, 0L},
-		{"File1", typTEXT, &defs.File1, 0L},
-		{"File2", typTEXT, &defs.File2, 0L},
-		{"File3", typTEXT, &defs.File3, 0L},
-		{"File4", typTEXT, &defs.File4, 0L},
-		{"File5", typTEXT, &defs.File5, 0L},
-		{"File6", typLAST | typTEXT, &defs.File6, 0L}};
-
-	switch(rw) {
-	case FILE_READ:
-		ExecInput(Desc);
-		//check for plausibility
-		if(defs.ss_txt < 0.3 || defs.ss_txt > 3.0) defs.ss_txt = 0.9;
-		if(defs.dUnits < 0 || defs.dUnits > 2) defs.dUnits = 0;
-		defs.cUnits = defs.dUnits;
-#ifdef _WINDOWS
-		if(dlgtxtheight < 5 || dlgtxtheight > 60) dlgtxtheight = 16;
-#else
-		if(dlgtxtheight < 5 || dlgtxtheight > 60) dlgtxtheight = 10;
-#endif
-		return true;
-	case FILE_WRITE:
-		Notary = new notary();
-#ifdef USE_WIN_SECURE
-		_unlink(defs.IniFile);
-#else
-		unlink(defs.IniFile);
-#endif
-		iFile = OpenOutputFile(defs.IniFile);
-		if(iFile >=0) {
-			ExecOutput(-1, "Defaults", Desc);
-			}
-		CloseOutputFile();	if(Notary) delete Notary;	Notary = 0L;
-		return true;
-		}
-	return false;
-}
+//FileIO.cpp, Copyright (c) 2001-2008 R.Lackner
+//read/write graphic objects
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+#include "rlplot.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#include <ctype.h>
+#include <fcntl.h>				//file open flags
+#include <sys/stat.h>			//I/O flags
+
+#ifdef _WINDOWS
+	#include <io.h>					//for read/write
+#else
+	#define O_BINARY 0x0
+	#include <unistd.h>
+#endif
+
+extern GraphObj *CurrGO;			//Selected Graphic Objects
+extern Default defs;
+extern int dlgtxtheight;
+extern char TmpTxt[];
+extern int cPlots;
+GraphObj *LastOpenGO;
+
+static notary *Notary = 0L;
+static ReadCache *Cache = 0L;
+
+unsigned long cObsW;				//count objects written
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// graphic input/output is driven by tables based on the descIO template
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+typedef struct {
+	char *label;
+	unsigned short type;
+	void *ptr;
+	long *count;
+	}descIO;
+
+//the type member of descIO describes the following data types pointed to by ptr
+enum {typNONE, typNZINT, typINT, typLFLOAT, typNZLFLOAT, 
+	typDWORD, typFRECT, typNZLFPOINT, typLFPOINT, typPOINT3D,
+	typAXDEF, typPTRAXDEF, typLINEDEF, typFILLDEF, typGOBJ,	typOBJLST,
+	typFPLST, typFPLST3D, typIPLST, typTEXT, typTXTDEF, typPTRTXTDEF, 
+	typLAST = 0x100};
+
+static char *ptr =0L;
+static int cbOut, sizeOut = 0, iFile = -1;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// output graph to file, elementary functions
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static int OpenOutputFile(char *file)
+{
+	time_t ti;
+
+	if(ptr) free(ptr);
+	ptr = 0L;		cbOut = 0;		ti = time(0L);
+	if(file && BackupFile(file)) {
+#ifdef USE_WIN_SECURE
+		if(_sopen_s(&iFile, file, O_RDWR | O_BINARY | O_CREAT | O_TRUNC, 
+			0x40, S_IWRITE) || iFile < 0){
+			ErrorBox("Open failed for output file");
+			return -1;
+			}
+#else
+		if(-1 ==(iFile = open(file, O_RDWR | O_BINARY | O_CREAT | O_TRUNC,
+			S_IWRITE | S_IREAD))){
+			ErrorBox("Open failed for output file");
+			return -1;
+			}
+#endif
+		ptr = (char *)malloc(sizeOut = 2000);
+		if(!ptr) goto openerr;
+		cbOut = rlp_strcpy(ptr, 20, ";RLP 1.0\n;File \"");
+		add_to_buff(&ptr, &cbOut, &sizeOut, file, 0);
+		add_to_buff(&ptr, &cbOut, &sizeOut, "\" created by RLPlot version "SZ_VERSION" for ", 0);
+#ifdef _WINDOWS
+		add_to_buff(&ptr, &cbOut, &sizeOut, "Windows", 7);
+#else
+		add_to_buff(&ptr, &cbOut, &sizeOut, "Qt", 2);
+#endif
+		add_to_buff(&ptr, &cbOut, &sizeOut, "\n;Date/Time: ", 13);
+#ifdef USE_WIN_SECURE
+		ctime_s(ptr+cbOut, 30, &ti);	cbOut += 25;
+#else
+		add_to_buff(&ptr, &cbOut, &sizeOut, ctime(&ti), 25);
+#endif
+		}
+	return iFile;
+openerr:
+	ptr = 0L;	cbOut = sizeOut = 0;
+#ifdef USE_WIN_SECURE
+	if(iFile >=0) _close(iFile);
+#else
+	if(iFile >=0) close(iFile);
+#endif
+	return iFile = -1;
+}
+
+static void CloseOutputFile()
+{
+	if(iFile >= 0){
+#ifdef USE_WIN_SECURE
+		if(cbOut) _write(iFile, ptr, cbOut);
+		_close(iFile);
+#else
+		if(cbOut) write(iFile, ptr, cbOut);
+		close(iFile);
+#endif
+		}
+	if(ptr) free(ptr);
+	cbOut = sizeOut = 0;
+	ptr = 0L;		iFile = -1;
+}
+
+static void WriteTypObjLst(GraphObj **obs, int c1)
+{
+	int i, j, no, n, *idx;
+	
+	if(!obs || !(idx=(int*)malloc(c1*sizeof(int)))) return;
+	for(i = no = 0; i < c1; i++) {
+		if(j = Notary->RegisterGO(obs[i])) idx[no++] = j;
+		}
+	add_to_buff(&ptr, &cbOut, &sizeOut, "(", 1);
+	add_int_to_buff(&ptr, &cbOut, &sizeOut, no, false, 0);
+	add_to_buff(&ptr, &cbOut, &sizeOut, "){", 2);
+	for(i = 0; i < no; i += 16) {
+		if(i) add_to_buff(&ptr, &cbOut, &sizeOut, "   ", 3);
+		for(j = 0; (n=i+j) < no && j < 16; j++) {
+			add_int_to_buff(&ptr, &cbOut, &sizeOut, idx[n], j != 0, 0);
+			}
+		if(n >= no) add_to_buff(&ptr, &cbOut, &sizeOut, "}", 1);
+		else add_to_buff(&ptr, &cbOut, &sizeOut, "\n", 1);
+		}
+}
+
+static void WriteTypIpLst(POINT *ppt, int count)
+{
+	long i, j, c, n;
+
+	if(!ppt) return;
+	add_to_buff(&ptr, &cbOut, &sizeOut, "(", 1);
+	add_int_to_buff(&ptr, &cbOut, &sizeOut, count, false, 0);
+	add_to_buff(&ptr, &cbOut, &sizeOut, "){", 2);
+	for(i = 0; i < count; i += 8) {
+		for(j = c = 0; (n = i+j) <count && j < 8; j++) {
+			add_int_to_buff(&ptr, &cbOut, &sizeOut, ppt[n].x, (j != 0), 0);
+			add_int_to_buff(&ptr, &cbOut, &sizeOut, ppt[n].y, true, 0);
+			}
+		if(n >= count) add_to_buff(&ptr, &cbOut, &sizeOut, "}", 1);
+		else add_to_buff(&ptr, &cbOut, &sizeOut, "\n", 1);
+		}
+}
+
+static void WriteTypFpLst(lfPOINT *ppt, long count, bool bPar)
+{
+	int i, j, n;
+
+	if (bPar){
+		if(!ppt) return;
+		add_to_buff(&ptr, &cbOut, &sizeOut, "(", 1);
+		add_int_to_buff(&ptr, &cbOut, &sizeOut, count, false, 0);
+		add_to_buff(&ptr, &cbOut, &sizeOut, "){", 2);
+		}
+	else {
+		if(!ppt) count = 0;
+		add_int_to_buff(&ptr, &cbOut, &sizeOut, count, true, 0);
+		if(!count) {
+			add_to_buff(&ptr, &cbOut, &sizeOut, "\n", 1);
+			return;
+			}
+		add_to_buff(&ptr, &cbOut, &sizeOut, " {", 2);
+		}
+	for(i = 0; i < count; i += 8) {
+		for(j = 0; (n = i+j) <count && j < 8; j++) {
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ppt[n].fx, (j != 0));
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ppt[n].fy, true);
+			}
+		if(n >= count) add_to_buff(&ptr, &cbOut, &sizeOut, "}", 1);
+		else add_to_buff(&ptr, &cbOut, &sizeOut, "\n", 1);
+		}
+}
+
+static void WriteTypFpLst3D(fPOINT3D *ppt, int count)
+{
+	long i, j, c, n;
+
+	if(!ppt) return;
+	add_to_buff(&ptr, &cbOut, &sizeOut, "(", 1);
+	add_int_to_buff(&ptr, &cbOut, &sizeOut, count, false, 0);
+	add_to_buff(&ptr, &cbOut, &sizeOut, "){", 2);
+	for(i = 0; i < count; i +=5) {
+		for(j = c = 0; (n =i+j) <count && j < 5; j++) {
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ppt[n].fx, (j != 0));
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ppt[n].fy, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ppt[n].fz, true);
+			}
+		if(n >= count) add_to_buff(&ptr, &cbOut, &sizeOut, "}", 1);
+		else add_to_buff(&ptr, &cbOut, &sizeOut, "\n", 1);
+		}
+}
+
+static char * esc_str = 0L;
+static int esc_str_size = 0;
+static void WriteEscString(char *txt)
+{
+	int i, j, l, lim = 60;
+
+	if(!txt || !txt[0]) return;
+	l = (int)strlen(txt);
+	if((l+10) > esc_str_size) if(!(esc_str = (char*)realloc(esc_str, esc_str_size = (l+100))))return;
+	j = 0;	esc_str[j++] = '"';
+	for(i = 0; txt[i]; i++) {
+		switch(txt[i]) {
+		case '\\':
+			esc_str[j++] = '\\';	esc_str[j++] = '\\';
+			break;
+		case '\n':
+			esc_str[j++] = '\\';	esc_str[j++] = 'n';
+			break;
+		default:	
+			if(((unsigned char*)txt)[i] >= ' ') esc_str[j++] = txt[i];
+			}
+		if(j > (esc_str_size -10)) esc_str = (char*)realloc(esc_str, (esc_str_size += 100));
+		if(j > lim && (l-i) > 3) {
+			esc_str[j++] = '"';		esc_str[j++] = '\\';
+			esc_str[j++] = '\n';	esc_str[j++] = ' ';
+			esc_str[j++] = ' ';		esc_str[j++] = ' ';
+			esc_str[j++] = '"';
+			lim += 60;
+			}
+		}
+	esc_str[j++] = '"';
+	add_to_buff(&ptr, &cbOut, &sizeOut, esc_str, j);
+}
+
+bool ExecOutput(int id, char *Class, descIO *Desc)
+{
+	int i, last;
+	fRECT *fr;
+	AxisDEF *ax;
+	LineDEF *ld;
+	FillDEF *fd;
+	TextDEF *tx;
+
+	add_to_buff(&ptr, &cbOut, &sizeOut, "\n[", 2);
+	add_int_to_buff(&ptr, &cbOut, &sizeOut, id, false, 0);
+	add_to_buff(&ptr, &cbOut, &sizeOut, "=", 1);
+	add_to_buff(&ptr, &cbOut, &sizeOut, Class, 0);
+	add_to_buff(&ptr, &cbOut, &sizeOut, "]\n", 2);
+	cObsW++;
+	for(i = 0; Desc[i].label; i++) {
+		if(ptr[cbOut-1] != '\n') add_to_buff(&ptr, &cbOut, &sizeOut, "\n", 1);
+		last = cbOut;
+		add_to_buff(&ptr, &cbOut, &sizeOut, Desc[i].label, 0);
+		add_to_buff(&ptr, &cbOut, &sizeOut, "=", 1);
+		switch(Desc[i].type & 0xff){
+		case typNZINT:
+			if(!(*(int*)Desc[i].ptr)) {
+				cbOut = last;			break;
+				}
+			//if not zero value continue as if int
+		case typINT:
+			add_int_to_buff(&ptr, &cbOut, &sizeOut, *(int*)Desc[i].ptr, true, 0);
+			break;
+		case typNZLFLOAT:
+			if(*((double*)Desc[i].ptr) == 0.0) {
+				cbOut = last;			break;
+				}
+			//if not zero or negative continue as if float
+		case typLFLOAT:
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, *(double*)Desc[i].ptr, true);
+			break;
+		case typDWORD:
+			add_hex_to_buff(&ptr, &cbOut, &sizeOut, *(DWORD *)Desc[i].ptr, true);
+			break;
+		case typFRECT:
+			fr = (fRECT*)Desc[i].ptr;
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, fr->Xmin, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, fr->Ymax, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, fr->Xmax, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, fr->Ymin, true);
+			break;
+		case typNZLFPOINT:
+			if(((lfPOINT *)Desc[i].ptr)->fx == ((lfPOINT *)Desc[i].ptr)->fy &&
+				((lfPOINT *)Desc[i].ptr)->fx == 0.0f){
+				cbOut = last;			break;
+				}
+			//if not zero continue as if fPOINT
+		case typLFPOINT:
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ((lfPOINT *)Desc[i].ptr)->fx, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ((lfPOINT *)Desc[i].ptr)->fy, true);
+			break;
+		case typPOINT3D:
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ((fPOINT3D *)Desc[i].ptr)->fx, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ((fPOINT3D *)Desc[i].ptr)->fy, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ((fPOINT3D *)Desc[i].ptr)->fz, true);
+			break;
+		case typAXDEF:		case typPTRAXDEF:
+			ax = (Desc[i].type & 0xff) == typAXDEF ? (AxisDEF *)Desc[i].ptr : *(AxisDEF **)Desc[i].ptr;
+			//we do not set ownership: reconstruct after read
+			add_hex_to_buff(&ptr, &cbOut, &sizeOut, ax->flags, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->min, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->max, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->loc[0].fx, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->loc[0].fy, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->loc[0].fz, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->loc[1].fx, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->loc[1].fy, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->loc[1].fz, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->Start, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->Step, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->Center.fx, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->Center.fy, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ax->Radius, true);
+			WriteTypFpLst(ax->breaks, ax->nBreaks, false);
+			break;
+		case typLINEDEF:
+			ld = (LineDEF *)Desc[i].ptr;
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ld->width, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, ld->patlength, true);
+			add_hex_to_buff(&ptr, &cbOut, &sizeOut, ld->color, true);
+			add_hex_to_buff(&ptr, &cbOut, &sizeOut, ld->pattern, true);
+			break;
+		case typFILLDEF:
+			fd = (FillDEF *)Desc[i].ptr;
+			//we set the 'hatch' member to zero: reconstruct after read
+			add_int_to_buff(&ptr, &cbOut, &sizeOut, fd->type, true, 0);
+			add_hex_to_buff(&ptr, &cbOut, &sizeOut, fd->color, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, fd->scale, true);
+			add_to_buff(&ptr, &cbOut, &sizeOut, " 0x0", 4);
+			add_hex_to_buff(&ptr, &cbOut, &sizeOut, fd->color2, true);
+			break;
+		case typGOBJ:
+			if(*(GraphObj **)(Desc[i].ptr)) add_int_to_buff(&ptr, &cbOut, &sizeOut, 
+				Notary->RegisterGO(*(GraphObj **)(Desc[i].ptr)), true, 0);
+			else cbOut = last;
+			break;
+		case typOBJLST:
+			if(!(*(GraphObj ***)(Desc[i].ptr)) || !(*Desc[i].count)){
+				cbOut = last;				break;
+				}
+			WriteTypObjLst(*(GraphObj ***)Desc[i].ptr, Desc[i].count ? *Desc[i].count : 0);
+			break;
+		case typIPLST:
+			if(!(*(POINT**)(Desc[i].ptr)) || !(*Desc[i].count)){
+				cbOut = last;				break;
+				}
+			WriteTypIpLst(*(POINT**)Desc[i].ptr, Desc[i].count ? *Desc[i].count : 0L);
+			break;
+		case typFPLST:
+			if(!(*(lfPOINT**)(Desc[i].ptr)) || !(*Desc[i].count)){
+				cbOut = last;				break;
+				}
+			WriteTypFpLst(*(lfPOINT**)Desc[i].ptr, Desc[i].count ? *Desc[i].count : 0L, true);
+			break;
+		case typFPLST3D:
+			if(!(*(fPOINT3D**)(Desc[i].ptr)) || !(*Desc[i].count)){
+				cbOut = last;				break;
+				}
+			WriteTypFpLst3D(*(fPOINT3D**)Desc[i].ptr, Desc[i].count ? *Desc[i].count : 0);
+			break;
+		case typTEXT:
+			if(!*(char**)(Desc[i].ptr)) cbOut = last;
+			else WriteEscString(*((char**)Desc[i].ptr));
+			break;
+		case typTXTDEF:		case typPTRTXTDEF:
+			tx = (Desc[i].type &0xff) == typTXTDEF ? (TextDEF *)Desc[i].ptr : *(TextDEF **)Desc[i].ptr;
+			if(!tx) {
+				cbOut = last;				break;
+				}
+			add_hex_to_buff(&ptr, &cbOut, &sizeOut, tx->ColTxt, true);
+			add_hex_to_buff(&ptr, &cbOut, &sizeOut, tx->ColBg, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, tx->fSize, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, tx->RotBL, true);
+			add_dbl_to_buff(&ptr, &cbOut, &sizeOut, tx->RotCHAR, true);
+			add_int_to_buff(&ptr, &cbOut, &sizeOut, tx->Align, true, 0);
+			add_int_to_buff(&ptr, &cbOut, &sizeOut, tx->Mode, true, 0);
+			add_int_to_buff(&ptr, &cbOut, &sizeOut, tx->Style, true, 0);
+			add_int_to_buff(&ptr, &cbOut, &sizeOut, tx->Font, true, 0);
+			if(tx->text && tx->text[0]) {
+				add_to_buff(&ptr, &cbOut, &sizeOut, " \"", 2);
+				add_to_buff(&ptr, &cbOut, &sizeOut, tx->text, 0);
+				add_to_buff(&ptr, &cbOut, &sizeOut, "\"\n", 2);
+				}
+			break;
+			}
+		if(Desc[i].type & typLAST) break;
+		}
+	if(ptr[cbOut-1] != '\n') add_to_buff(&ptr, &cbOut, &sizeOut, "\n", 1);
+	return true;
+}
+
+void ReadTypIpLst(POINT *ptr, long count, unsigned char *first)
+{
+	int i, j, k, f[20];
+
+	if(!ptr || !first) return;
+#ifdef USE_WIN_SECURE
+	k = sscanf_s((char*)first, "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d", 
+		&f[0], &f[1], &f[2], &f[3], &f[4], &f[5], &f[6], &f[7], &f[8], &f[9],
+		&f[10], &f[11], &f[12], &f[13], &f[14], &f[15], &f[16], &f[17], &f[18], &f[19]);
+#else
+	k = sscanf((char*)first, "%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d%d", 
+		&f[0], &f[1], &f[2], &f[3], &f[4], &f[5], &f[6], &f[7], &f[8], &f[9],
+		&f[10], &f[11], &f[12], &f[13], &f[14], &f[15], &f[16], &f[17], &f[18], &f[19]);
+#endif
+	for(i = 0,  j = 0; j < k && i < count; i++, j += 2) {
+		ptr[i].x = f[j];	ptr[i].y = f[j+1];
+		}
+	while (i < count) {
+		if(!Cache->GetInt(&ptr[i].x) || !Cache->GetInt(&ptr[i].y)) return;
+		i++;
+		}
+}
+
+void ReadTypFpLst(lfPOINT *ptr, long count, unsigned char *first)
+{
+	double f[20];
+	int j, k;
+	long i;
+
+	if(!ptr || !first) return;
+#ifdef USE_WIN_SECURE
+	k = sscanf_s((char*)first, "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf", 
+		&f[0], &f[1], &f[2], &f[3], &f[4], &f[5], &f[6], &f[7], &f[8], &f[9],
+		&f[10], &f[11], &f[12], &f[13], &f[14], &f[15], &f[16], &f[17], &f[18], &f[19]);
+#else
+	k = sscanf((char*)first, "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf", 
+		&f[0], &f[1], &f[2], &f[3], &f[4], &f[5], &f[6], &f[7], &f[8], &f[9],
+		&f[10], &f[11], &f[12], &f[13], &f[14], &f[15], &f[16], &f[17], &f[18], &f[19]);
+#endif
+	for(i = 0,  j = 0; j < k && i < count; i++, j += 2) {
+		ptr[i].fx = f[j];	ptr[i].fy = f[j+1];
+		}
+	while (i < count) {
+		if(!Cache->GetFloat(&ptr[i].fx) || !Cache->GetFloat(&ptr[i].fy)) return;
+		i++;
+		}
+}
+
+void ReadTypFpLst3D(fPOINT3D *ptr, long count, unsigned char *first)
+{
+	double f[21];
+	int j, k;
+	long i;
+
+	if(!ptr || !first) return;
+#ifdef USE_WIN_SECURE
+	k = sscanf_s((char*)first, "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
+		&f[0], &f[1], &f[2], &f[3], &f[4], &f[5], &f[6], &f[7], &f[8], &f[9],
+		&f[10], &f[11], &f[12], &f[13], &f[14], &f[15], &f[16], &f[17], &f[18], &f[19], &f[20]);
+#else
+	k = sscanf((char*)first, "%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf",
+		&f[0], &f[1], &f[2], &f[3], &f[4], &f[5], &f[6], &f[7], &f[8], &f[9],
+		&f[10], &f[11], &f[12], &f[13], &f[14], &f[15], &f[16], &f[17], &f[18], &f[19], &f[20]);
+#endif
+	for(i = 0,  j = 0; j < k && i < count; i++, j += 3) {
+		ptr[i].fx = f[j];	ptr[i].fy = f[j+1];		ptr[i].fz = f[j+2];
+		}
+	while (i < count) {
+		if(!Cache->GetFloat(&ptr[i].fx) || !Cache->GetFloat(&ptr[i].fy) ||
+			!Cache->GetFloat(&ptr[i].fz)) return;
+		i++;
+		}
+}
+
+void TranslateEscChar(char *txt)
+{
+	int i, j;
+
+	if(txt && txt[0]) {
+		for(i = j = 0; txt[i]; i++) {
+			if(txt[i] == '\\') {
+				switch(txt[i+1]) {
+				case 'n':
+					txt[j++] = 0x0a;	i++;	break;
+				case 'r':
+					txt[j++] = 0x0d;	i++;	break;
+				case '"':	case 0x27:			case '\\':
+					txt[j++] = txt[++i];		break;
+				default:
+					txt[j++] = txt[i];	break;
+					}
+				}
+			else txt[j++] = txt[i];
+			}
+		txt[j] = 0;
+		}
+}
+
+void AddLines(char **txt)
+{
+	char tmp[1000], *ntxt;
+	bool mlines;
+	int i, j, cb = (int)strlen(*txt);
+
+	do {
+		mlines = false;
+		Cache->ReadLine(tmp, sizeof(tmp));
+		for(i = (int)strlen(tmp); i > 0 &&(tmp[i-1] < 33 || (tmp[i-1] == 
+			'"' && tmp[i-2] != '\\') ||	(tmp[i-1] == '\\' && 
+			(mlines = true))); tmp[--i] = 0);
+		for(i = 0; tmp[i] && (tmp[i] < 33 || tmp[i] == '"'); i++);
+		TranslateEscChar(tmp);
+		if(tmp[0] && (j = (int)strlen(tmp+i)) && (ntxt = (char*)realloc(*txt, cb + j + 1))) {
+			rlp_strcpy(ntxt+cb, j+1, tmp+i);
+			cb += j;	*(txt) = ntxt;
+			}
+		} while (mlines);
+}
+
+bool ExecInput(descIO *Desc)
+{
+	unsigned char c, tmp[1000], tmp2[20];
+	int i, j, k, l;
+	bool match, mlines;
+	int il, jl;
+	AxisDEF *ax;
+	POINT *lp;
+	lfPOINT *lfp;
+	fPOINT3D *lfp3d;
+	LineDEF *ld;
+	FillDEF *fd;
+	fRECT *fr;
+	GraphObj **gobs;
+	TextDEF *tx;
+
+	if(!Desc || !Desc[0].label) return false;
+	for(j = k = 0; ; ) {
+		do{
+			c = Cache->Getc();
+			switch (c) {
+			case '[':					//next object
+			case 0:						//probably eof
+				return true;
+			case '}':					//a lists hang over
+				c = Cache->Getc();
+				break;
+				}
+		} while(c <33);
+		for(i = 1, tmp[0] = c; i < sizeof(tmp) && '=' != (tmp[i] = Cache->Getc()); i++){
+			if(tmp[i] < 32 && tmp[i]) i = -1;			//some error conditions
+			else if(!tmp[i] && Cache->eof) return true;
+			else if(tmp[i] == '[') return true;
+			}
+		tmp[i] = 0;
+		match = mlines = false;
+		do {
+			if(0 == strcmp((char*)tmp, Desc[j].label)) {
+				Cache->ReadLine((char*)tmp, sizeof(tmp));
+				switch(Desc[j].type & 0xff){
+				case typNZINT:
+				case typINT:
+#ifdef USE_WIN_SECURE
+					sscanf_s((char*)tmp, "%d", (int*)Desc[j].ptr);
+#else
+					sscanf((char*)tmp, "%d", (int*)Desc[j].ptr);
+#endif
+					break;
+				case typNZLFLOAT:		case typLFLOAT:
+#ifdef USE_WIN_SECURE
+					sscanf_s((char*)tmp, "%lf", (double*)Desc[j].ptr);
+#else
+					sscanf((char*)tmp, "%lf", (double*)Desc[j].ptr);
+#endif
+					break;
+				case typDWORD:
+#ifdef USE_WIN_SECURE
+					sscanf_s((char*)tmp, "%x", (DWORD*)Desc[j].ptr);
+#else
+					sscanf((char*)tmp, "%x", (DWORD*)Desc[j].ptr);
+#endif
+					break;
+				case typFRECT:
+					fr = (fRECT*) Desc[j].ptr;
+#ifdef USE_WIN_SECURE
+					sscanf_s((char*)tmp, "%lf%lf%lf%lf", &fr->Xmin, &fr->Ymax, &fr->Xmax, &fr->Ymin);
+#else
+					sscanf((char*)tmp, "%lf%lf%lf%lf", &fr->Xmin, &fr->Ymax, &fr->Xmax, &fr->Ymin);
+#endif
+					break;
+				case typNZLFPOINT:		case typLFPOINT:
+					lfp = (lfPOINT*) Desc[j].ptr;
+#ifdef USE_WIN_SECURE
+					sscanf_s((char*)tmp, "%lf%lf", &lfp->fx, &lfp->fy);
+#else
+					sscanf((char*)tmp, "%lf%lf", &lfp->fx, &lfp->fy);
+#endif
+					break;
+				case typPOINT3D:
+					lfp3d = (fPOINT3D*) Desc[j].ptr;
+#ifdef USE_WIN_SECURE
+					sscanf_s((char*)tmp, "%lf%lf%lf", &lfp3d->fx, &lfp3d->fy, &lfp3d->fz);
+#else
+					sscanf((char*)tmp, "%lf%lf%lf", &lfp3d->fx, &lfp3d->fy, &lfp3d->fz);
+#endif
+					break;
+				case typPTRAXDEF:
+				case typAXDEF:
+					ax = (Desc[j].type & 0xff) == typAXDEF ? (AxisDEF *)Desc[j].ptr : *(AxisDEF **)Desc[j].ptr;
+					//pointer for typPTRAXDEF and memory allocated by the Axis module!
+					if(!ax) break;
+#ifdef USE_WIN_SECURE
+					sscanf_s((char*)tmp, "%x%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%d", &ax->flags, &ax->min, &ax->max,
+						&ax->loc[0].fx,	&ax->loc[0].fy, &ax->loc[0].fz, &ax->loc[1].fx,
+						&ax->loc[1].fy, &ax->loc[1].fz, &ax->Start, &ax->Step, &ax->Center.fx, 
+						&ax->Center.fy, &ax->Radius, &ax->nBreaks);
+#else
+					sscanf((char*)tmp, "%x%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%lf%d", &ax->flags, &ax->min, &ax->max,
+						&ax->loc[0].fx,	&ax->loc[0].fy, &ax->loc[0].fz, &ax->loc[1].fx,
+						&ax->loc[1].fy, &ax->loc[1].fz, &ax->Start, &ax->Step, &ax->Center.fx, 
+						&ax->Center.fy, &ax->Radius, &ax->nBreaks);
+#endif
+					if(ax->nBreaks) {
+						ax->breaks = (lfPOINT*)calloc(ax->nBreaks, sizeof(lfPOINT));
+						for(i = 0; tmp[i] && tmp[i-1] != '{'; i++);
+						if(tmp[i]) {
+							ReadTypFpLst(ax->breaks, ax->nBreaks, (unsigned char*)tmp+i);
+							SortAxisBreaks(ax);
+							}
+						}
+					break;
+				case typLINEDEF:
+					ld = (LineDEF*) Desc[j].ptr;
+#ifdef USE_WIN_SECURE
+					sscanf_s((char*)tmp,"%lf%lf%x%x", &ld->width, &ld->patlength, &ld->color, &ld->pattern);
+#else
+					sscanf((char*)tmp,"%lf%lf%x%x", &ld->width, &ld->patlength, &ld->color, &ld->pattern);
+#endif
+					break;
+				case typFILLDEF:
+					fd = (FillDEF*) Desc[j].ptr;
+#ifdef USE_WIN_SECURE
+					sscanf_s((char*)tmp, "%d%x%lf%x%x", &fd->type, &fd->color, &fd->scale, &fd->hatch, &fd->color2);
+#else
+					sscanf((char*)tmp, "%d%x%lf%x%x", &fd->type, &fd->color, &fd->scale, &fd->hatch, &fd->color2);
+#endif
+					fd->hatch = 0L;
+					break;
+				case typGOBJ:
+					*(GraphObj**)(Desc[j].ptr) = Notary->PopGO(atol((char*)tmp));
+					break;
+				case typOBJLST:
+					for(i = 0; i < 10 && tmp[i] != '(' && tmp[i]; i++);
+					if(!tmp[i]) break;
+#ifdef USE_WIN_SECURE
+					if(sscanf_s((char*)tmp+i+1, "%ld", &il) && il) {
+#else
+					if(sscanf((char*)tmp+i+1, "%ld", &il) && il) {
+#endif
+						*Desc[j].count = il;
+						if(!*(GraphObj***)(Desc[j].ptr)){
+							*(GraphObj***)(Desc[j].ptr) = (GraphObj**)calloc(il, sizeof(GraphObj*));
+							}
+						if((gobs = *(GraphObj***)(Desc[j].ptr))){
+							i += 4;
+							while(tmp[i-1] != '{') i++; while(tmp[i-1] < 33) i++;
+#ifdef USE_WIN_SECURE
+							strcat_s((char*)tmp, 1000, " ");
+#else
+							strcat((char*)tmp, " ");
+#endif
+							for( ;il >0; il--) {
+								for(l = 0; l < sizeof(tmp2); ){
+									if(tmp[i]) c = tmp[i++];
+									else if(!(c = Cache->Getc())) break;
+									if(c >='0' && c <='9') tmp2[l++] = c;
+									else {
+										tmp2[l] = 0;
+										if(l)break;
+										}
+									}
+#ifdef USE_WIN_SECURE
+								sscanf_s((char*)tmp2, "%d", &jl);
+#else
+								sscanf((char*)tmp2, "%d", &jl);
+#endif
+								*gobs++ = Notary->PopGO(jl);
+								if(c == '}') break;
+								}
+							}
+						}	
+					break;
+				case typIPLST:
+					for(i = 0; i < 10 && tmp[i] != '(' && tmp[i]; i++);
+					if(!tmp[i]) break;
+#ifdef USE_WIN_SECURE
+					if(sscanf_s((char*)tmp+i+1, "%d", &il) && il) {
+#else
+					if(sscanf((char*)tmp+i+1, "%d", &il) && il) {
+#endif
+						*Desc[j].count = il;
+						if(!*(POINT**)(Desc[j].ptr)){
+							*(POINT**)(Desc[j].ptr) = (POINT*)calloc(il, sizeof(POINT));
+							}
+						if(!(lp = *(POINT**)(Desc[j].ptr)))return false;
+						while(tmp[i-1] != '{') i++; while(tmp[i-1] < 33) i++;
+						ReadTypIpLst(lp, il, (unsigned char*)tmp+i);
+						}
+					break;
+				case typFPLST:
+					for(i = 0; i < 10 && tmp[i] != '(' && tmp[i]; i++);
+					if(!tmp[i]) break;
+#ifdef USE_WIN_SECURE
+					if(sscanf_s((char*)tmp+i+1, "%d", &il) && il) {
+#else
+					if(sscanf((char*)tmp+i+1, "%d", &il) && il) {
+#endif
+						*Desc[j].count = il;
+						if(!*(lfPOINT**)(Desc[j].ptr)){
+							*(lfPOINT**)(Desc[j].ptr) = (lfPOINT*)calloc(il, sizeof(lfPOINT));
+							}
+						if(!(lfp = *(lfPOINT**)(Desc[j].ptr)))return false;
+						while(tmp[i-1] != '{') i++; while(tmp[i-1] < 33) i++;
+						ReadTypFpLst(lfp, il, (unsigned char*)tmp+i);
+						}
+					break;
+				case typFPLST3D:
+					for(i = 0; i < 10 && tmp[i] != '(' && tmp[i]; i++);
+					if(!tmp[i]) break;
+#ifdef USE_WIN_SECURE
+					if(sscanf_s((char*)tmp+i+1, "%d", &il) && il) {
+#else
+					if(sscanf((char*)tmp+i+1, "%d", &il) && il) {
+#endif
+						*Desc[j].count = il;
+						if(!*(fPOINT3D**)(Desc[j].ptr)){
+							*(fPOINT3D**)(Desc[j].ptr) = (fPOINT3D*)calloc(il, sizeof(fPOINT3D));
+							}
+						if(!Desc[j].ptr)return false;
+						while(tmp[i-1] != '{') i++; while(tmp[i-1] < 33) i++;
+						ReadTypFpLst3D(*(fPOINT3D**)(Desc[j].ptr), il, (unsigned char*)tmp+i);
+						}
+					break;
+				case typTEXT:
+					for(i = (int)strlen((char*)tmp); i > 0 &&(tmp[i-1] < 32 || (tmp[i-1] == 
+						'"' && tmp[i-2] != '\\') ||	(tmp[i-1] == '\\' && 
+						(mlines = true))); tmp[--i] = 0);
+					for(i = 0; tmp[i] && (tmp[i] < 33 || tmp[i] == '"'); i++);
+					TranslateEscChar((char*)tmp);
+					if(tmp[0]){
+						*(char**)(Desc[j].ptr) = (char*)memdup((char*)tmp+i, (int)strlen((char*)tmp+i)+1, 0);
+						if(mlines) AddLines((char**)(Desc[j].ptr));
+						}
+					break;
+				case typPTRTXTDEF:
+				case typTXTDEF:
+					tx = (Desc[j].type & 0xff) == typTXTDEF ? (TextDEF *)Desc[j].ptr : *(TextDEF **)Desc[j].ptr;
+					if(!tx) {
+						if((Desc[j].type & 0xff) == typTXTDEF) break;	//prabably wrong usage of typTXTDEF instad of
+																//    typPTRTXTDEF
+						tx = *(TextDEF **)(Desc[j].ptr) = (TextDEF*)calloc(1, sizeof(TextDEF));
+						if(!tx) return false;					//memory allocation error
+						}
+#ifdef USE_WIN_SECURE
+					sscanf_s((char*)tmp, "%x%x%lf%lf%lf%d%d%d%d", &tx->ColTxt, &tx->ColBg, 
+						&tx->fSize, &tx->RotBL, &tx->RotCHAR,
+						&tx->Align, &tx->Mode, &tx->Style, &tx->Font);
+#else
+					sscanf((char*)tmp, "%x%x%lf%lf%lf%d%d%d%d", &tx->ColTxt, &tx->ColBg, 
+						&tx->fSize, &tx->RotBL, &tx->RotCHAR,
+						&tx->Align, &tx->Mode, &tx->Style, &tx->Font);
+#endif
+					tx->iSize = 0;
+					for(i = (int)strlen((char*)tmp); i >0 && tmp[i] != '"'; i--);
+					if(i) {
+						tmp[i] = 0;
+						for(l = 0; l <i && tmp[l] != '"'; l++);
+						if(i && tmp[l+1]) tx->text = (char*)memdup((char*)tmp+l+1, (int)strlen((char*)tmp+l+1)+1, 0);
+						}
+					break;
+					}
+				match = true;			j++;	k++;
+				if(!Desc[j].label || (Desc[j-1].type & typLAST)) 
+					j = k = 0;	//rewind: items in file not sorted
+				}
+			else {
+				j++;
+				k++;
+				if(!Desc[j].label || (Desc[j-1].type & typLAST)) {				//Error:
+					if(k > j){						//  item not defined in Desc
+						match = true;				//  read parameters,
+						Cache->ReadLine((char*)tmp, sizeof(tmp));	//   then continue
+						}
+					j= 0;
+					}
+				}
+			}while(!match);
+		}
+}
+
+bool SaveGraphAs(GraphObj *g)
+{
+	char *name = 0L;
+	int i;
+	bool bRet = true;
+
+	if(Notary || !g) {
+		ErrorBox("Output pending or\nno graph.");
+		return false;
+		}
+	cObsW = 0;
+	Notary = new notary();
+	if(g->Id == GO_GRAPH || g->Id == GO_PAGE) {
+		if(((Graph*)g)->filename) name = ((Graph*)g)->filename;
+		}
+	name = SaveGraphAsName(name);
+	if (name && Notary) {
+		iFile = OpenOutputFile(name);
+		if(iFile >=0) {
+			if(g && g->FileIO(FILE_WRITE)){
+				if(g->Id == GO_GRAPH || g->Id == GO_PAGE) {
+					g->Command(CMD_FILENAME, name, 0L);
+					}
+				for(i = (int)strlen(name); i >=0 && name[i] != '/' && name[i] != '\\'; i--);
+				if(name[i]) i++;
+				g->Command(CMD_SETNAME, name+i, 0L);
+				g->Command(CMD_UPDHISTORY, 0L, 0L);
+				}
+			else ErrorBox("Could not write\ndata to file.");
+			}
+		else ErrorBox("Open failed for\noutput file.");
+		CloseOutputFile();
+		}
+	else bRet = false;
+	if(Notary) delete Notary;	Notary = 0L;
+	return bRet;
+}
+
+char *GraphToMem(GraphObj *g, long *size)
+{
+	static char *ret;
+
+	if(Notary || !g) {
+		ErrorBox("Output pending or\nno graph.");
+		return false;
+		}
+	cObsW = 0;				iFile = -1;
+	cbOut = sizeOut = 0;	ptr = 0L;
+	if (Notary = new notary()) {
+		if(g && g->FileIO(FILE_WRITE)){
+			//all done
+			}
+		delete Notary;					Notary = 0L;
+		if(ptr) ptr[cbOut] = 0;
+		ret = ptr;						if(size) *size = cbOut;
+		iFile = -1;
+		cbOut = sizeOut = 0;	ptr = 0L;
+		return ret;
+		}
+	return 0L;
+}
+
+void UpdGOfromMem(GraphObj *go, unsigned char *buff)
+{
+	int i=0;
+
+	if(!go || !buff) return;
+	iFile = -1;							cbOut = sizeOut = 0;	ptr = 0L;
+	for(i = 0; buff[i] && buff[i] != ']'; i++);
+	if(!buff[i])return;
+	for(; buff[i] && buff[i] <33; i++);
+	if(!buff[i] || i < 4) return;
+	if(!(Cache = new MemCache(buff+i-1))) return;
+	if ((Notary = new notary()) && go->Id > GO_UNKNOWN && go->Id < GO_DEFRW) {
+		//notary not needed but saver if tree exists
+		go->Command(CMD_FLUSH, 0L, 0L);
+		go->FileIO(INIT_VARS);			go->FileIO(FILE_READ);
+		delete Notary;					Notary = 0L;
+		}
+	delete Cache;		Cache = 0L;
+}
+
+bool OpenGraph(GraphObj *root, char *name, unsigned char *mem, bool bPaste)
+{
+	unsigned char c, tmp[80];
+	char debug[80];
+	int i, id, lid;
+	unsigned int hv;
+	GraphObj *go;
+	scaleINFO sc_info;
+
+	LastOpenGO = 0L;
+	if(Notary || Cache) {
+		ErrorBox("Output pending:\nRead Error.");
+		return false;
+		}
+	if(!(Notary = new notary()))return false;
+	if(mem) {
+		if(!(Cache = new MemCache(mem))) return false;
+		}
+	else if(Cache = new ReadCache()){
+		if(!Cache->Open(name)) {
+			delete Notary;		delete Cache;
+			Notary = 0L;		Cache = 0L;
+			ErrorBox("Error open file");
+			return false;
+			}
+		}
+	else return false;
+	//DEBUG: skipping header
+	do {
+		c = Cache->Getc();
+		} while(c && c != '[');
+	if(!c) goto ReadErr;
+	do {
+		for(i = 0; i < sizeof(tmp) && c != '=' && c; i++){
+			tmp[i] = c = Cache->Getc();
+			if(c == '[') i = -1;
+			}
+		if(!c) goto ReadErr;
+		tmp[i] = tmp[i-1] = 0;			id=0;
+#ifdef USE_WIN_SECURE
+		sscanf_s((char*)tmp, "%d", &id);
+#else
+		sscanf((char*)tmp, "%d", &id);
+#endif
+		if(!id) goto ReadErr;
+		//go to class name
+		while((tmp[0] = Cache->Getc())<31 && tmp[0]);
+		if(!tmp[0]) goto ReadErr;
+		for(i = 1; i < sizeof(tmp) && c!= ']' && c; i++)
+			tmp[i] = c = Cache->Getc();
+		if(!c) goto ReadErr;
+		tmp[i-1] = 0;
+		go = 0L;
+		hv = HashValue(tmp);
+		switch(hv) {
+		case 3895:	go = new Axis(FILE_READ);		break;
+		case 886:	go = new Bar(FILE_READ);		break;
+		case 81384:	go = new Symbol(FILE_READ);		break;
+		case 62229:	go = new Bubble(FILE_READ);		break;
+		case 948:	go = new Box(FILE_READ);		break;
+		case 15411:	go = new Arrow(FILE_READ);		break;
+		case 1052406:	go = new ErrorBar(FILE_READ);		break;
+		case 324566:	go = new Whisker(FILE_READ);		break;
+		case 1031437:	go = new DropLine(FILE_READ);		break;
+		case 4839:	go = new Tick(FILE_READ);		break;
+		case 16832:	go = new Label(FILE_READ);		break;
+		case 1071373:	go = new GridLine(FILE_READ);		break;
+		case 963085:	go = new DataLine(FILE_READ);		break;
+		case 61662266:	go = new DataPolygon(FILE_READ);	break;
+		case 435228:	go = new segment(FILE_READ);		break;
+		case 1741325:	go = new polyline(FILE_READ);		break;
+		case 435258:	go = new polygon(FILE_READ);		break;
+		case 92534:	go = new Bezier(FILE_READ);		break;
+		case 6888037:	go = new rectangle(FILE_READ);		break;
+		case 1780087:	go = new roundrec(FILE_READ);		break;
+		case 78813:	go = new Sphere(FILE_READ);		break;
+		case 15463:	go = new Brick(FILE_READ);		break;
+		case 69952:	go = new Line3D(FILE_READ);		break;
+		case 386257:	go = new ellipse(FILE_READ);		break;
+		case 95680:	go = new mLabel(FILE_READ);		break;
+		case 4819316:	go = new PlotScatt(FILE_READ);		break;
+		case 117848:	go = new xyStat(FILE_READ);		break;
+		case 15935312:	go = new BubblePlot(FILE_READ);		break;
+		case 247376:	go = new BoxPlot(FILE_READ);		break;
+		case 317384:	go = new StackBar(FILE_READ);		break;
+		case 1205932:	go = new PieChart(FILE_READ);		break;
+		case 16664:	go = new Graph(FILE_READ);		break;
+		case 25108:	go = new GoGroup(FILE_READ);		break;
+		case 300976:	go = new Scatt3D(FILE_READ);		break;
+		case 297280:	go = new Plane3D(FILE_READ);		break;
+		case 19227098:	go = new Regression(FILE_READ);		break;
+		case 297997:	go = new RegLine(FILE_READ);		break;
+		case 4318417:	go = new SDellipse(FILE_READ);		break;
+		case 4843600:	go = new PolarPlot(FILE_READ);		break;
+		case 977452:	go = new DensDisp(FILE_READ);		break;
+		case 4465:	go = new Page(FILE_READ);		break;
+		case 75120:	go = new Plot3D(FILE_READ);		break;
+		case 17142080:	go = new GridLine3D(FILE_READ);		break;
+		case 246688:	go = new Arrow3D(FILE_READ);		break;
+		case 75562:	go = new Ribbon(FILE_READ);		break;
+		case 16503104:	go = new DropLine3D(FILE_READ);		break;
+		case 28859579:	go = new svgOptions(FILE_READ);		break;
+		case 70259:	go = new Limits(FILE_READ);		break;
+		case 17145824:	go = new GridRadial(FILE_READ);		break;
+		case 1074714:	go = new Function(FILE_READ);		break;
+		case 256075:	go = new FitFunc(FILE_READ);		break;
+		case 273377:	go = new LegItem(FILE_READ);		break;
+		case 1053744:	go = new FreqDist(FILE_READ);		break;
+		case 68748:	go = new Legend(FILE_READ);		break;
+		case 66800:	go = new Grid3D(FILE_READ);		break;
+		case 967843:	go = new DefsRW(FILE_READ);		break;
+		case 66848:	go = new Func3D(FILE_READ);		break;
+		case 5001225:	go = new TextFrame(FILE_READ);		break;
+		case 4743132:	go = new NormQuant(FILE_READ);		break;
+		case 64333904:	go = new ContourPlot(FILE_READ);	break;
+		default:
+#ifdef USE_WIN_SECURE
+			sprintf_s(debug, 80, "Object %ld in file\n(Class = \"%s\")\nhash #%d\nis unknown.", id, tmp, hv);
+#else
+			sprintf(debug, "Object %ld in file\n(Class = \"%s\")\nhash #%d\nis unknown.", id, tmp, hv);
+#endif
+			InfoBox(debug);
+			}
+		if(go) {
+			if(((int)id) < 0) DeleteGO(go);		//temporary objects have id < 0
+			else if(!Notary->PushGO(lid = id, go)) DeleteGO(go);
+			}
+		if('[' != Cache->Lastc()) do {			//search next object
+			c = Cache->Getc();
+			} while(c && c != '[');
+		tmp[0] = 0;
+		}while (c);
+	Cache->Close();
+	if((LastOpenGO = go = Notary->PopGO(lid))) {
+		go->Command(CMD_SET_DATAOBJ, 0L, 0L);
+		if (name && name[0]) go->Command(CMD_FILENAME, name, 0L);
+		delete Notary;		Notary = 0L;
+		if(bPaste && go->Id == GO_GRAPH) {
+			sc_info.sx.fx = -((Graph*)go)->GRect.Xmin;				sc_info.sy.fx = -((Graph*)go)->GRect.Ymin;
+			sc_info.sx.fy = sc_info.sy.fy = sc_info.sz.fy = 1.0;	sc_info.sz.fx = 0.0;
+			go->Command(CMD_SCALE, &sc_info, 0L);
+			sc_info.sx.fy = sc_info.sy.fy = sc_info.sz.fy = 1.0/go->GetSize(SIZE_SCALE);
+			sc_info.sx.fx = sc_info.sy.fx = sc_info.sz.fx = 0.0;
+			go->Command(CMD_SCALE, &sc_info, 0L);
+			}
+		if(bPaste && root->Command(CMD_PASTE_OBJ, (void *)go, 0L)) {
+			// object accepted
+			}
+		else if(go->Id < GO_GRAPH 
+			&& !(root->Command(CMD_DROP_PLOT, (void *)go, 0L))){
+			DeleteGO(go);	go = 0L;
+			}
+		else if(go->Id >= GO_GRAPH 
+			&& !(root->Command(CMD_DROP_GRAPH, (void *)go, 0L))){
+			DeleteGO(go);	go = 0L;
+			}
+		//go may not be valid any more at this stage
+		}
+	if(Notary) delete Notary;		Notary = 0L;
+	delete Cache;					Cache = 0L;
+	return true;
+
+ReadErr:
+	Cache->Close();	
+#ifdef USE_WIN_SECURE
+	if(iFile >= 0) _close(iFile);
+#else
+	if(iFile >= 0) close(iFile);
+#endif
+	iFile = -1;
+	delete Notary;		Notary = 0L;
+	delete Cache;		Cache = 0L;
+	if(!name || !defs.IniFile || strcmp(name, defs.IniFile))
+		ErrorBox("An error occured during read.");
+	return false;
+}
+
+bool InitVarsGO(descIO *Desc)
+{
+	int i;
+	AxisDEF *ax;
+	TextDEF *tx;
+
+	for(i = 0; Desc[i].label; i++) {
+		switch(Desc[i].type & 0xff) {
+		case typNZINT:
+		case typINT:
+			*(int*)Desc[i].ptr = 0;			
+			break;
+		case typNZLFLOAT:
+		case typLFLOAT:
+			*(double*)Desc[i].ptr = 0.0;
+			break;
+		case typDWORD:
+			*(DWORD*)Desc[i].ptr = 0x0L;
+			break;
+		case typFRECT:
+			((fRECT*)Desc[i].ptr)->Xmin = ((fRECT*)Desc[i].ptr)->Xmax =
+				((fRECT*)Desc[i].ptr)->Ymin = ((fRECT*)Desc[i].ptr)->Ymax = 0.0;
+			break;
+		case typNZLFPOINT:
+		case typLFPOINT:
+			((lfPOINT*)Desc[i].ptr)->fx = ((lfPOINT*)Desc[i].ptr)->fy = 0.0;
+			break;
+		case typPOINT3D:
+			((fPOINT3D*)Desc[i].ptr)->fx = ((fPOINT3D*)Desc[i].ptr)->fy =
+				((fPOINT3D*)Desc[i].ptr)->fz = 0.0;
+			break;
+		case typPTRAXDEF:
+		case typAXDEF:
+			ax = (Desc[i].type & 0xff) == typAXDEF ? (AxisDEF *)Desc[i].ptr : *(AxisDEF **)Desc[i].ptr;
+			if(!ax) break;
+			ax->owner = 0L;		ax->flags = 0L;		ax->breaks = 0L;	ax->nBreaks = 0;
+			ax->min = ax->max = ax->Start = ax->Step = 0.0f;
+			ax->loc[0].fx = ax->loc[0].fy = ax->loc[0].fz = 0.0f;
+			ax->loc[1].fx = ax->loc[1].fy = ax->loc[1].fz = 0.0f;
+			ax->Center.fx = ax->Center.fy = ax->Radius = 0.0f;
+			break;
+		case typLINEDEF:
+			((LineDEF*)Desc[i].ptr)->width = defs.GetSize(SIZE_HAIRLINE);
+			((LineDEF*)Desc[i].ptr)->patlength = defs.GetSize(SIZE_PATLENGTH);
+			((LineDEF*)Desc[i].ptr)->color = ((LineDEF*)Desc[i].ptr)->pattern = 0x0L;
+			break;
+		case typFILLDEF:
+			((FillDEF*)Desc[i].ptr)->type = FILL_NONE;
+			((FillDEF*)Desc[i].ptr)->color = 0x00ffffffL;
+			((FillDEF*)Desc[i].ptr)->scale = 1.0f;
+			((FillDEF*)Desc[i].ptr)->hatch = (LineDEF*)0L;
+			((FillDEF*)Desc[i].ptr)->color2 = 0x00ffffffL;
+			break;
+		case typGOBJ:
+			*(GraphObj **)Desc[i].ptr = (GraphObj*)0L;
+			break;
+		case typOBJLST:
+			*Desc[i].count = 0L;
+			*(GraphObj ***)Desc[i].ptr = (GraphObj**)0L;
+			break;
+		case typIPLST:
+			*Desc[i].count = 0L;
+			*(POINT **)Desc[i].ptr = (POINT*)0L;
+			break;
+		case typFPLST:
+			*Desc[i].count = 0L;
+			*(lfPOINT **)Desc[i].ptr = (lfPOINT*)0L;
+			break;
+		case typFPLST3D:
+			*Desc[i].count = 0L;
+			*(fPOINT3D **)Desc[i].ptr = (fPOINT3D*)0L;
+			break;
+		case typTEXT:
+			*(char **)Desc[i].ptr = (char*)0L;
+			break;
+		case typTXTDEF:
+			tx = (TextDEF *)Desc[i].ptr;
+			tx->ColTxt = 0x0L,			tx->ColBg = 0x00ffffffL;
+			tx->fSize = defs.GetSize(SIZE_TEXT);
+			tx->RotBL = tx->RotCHAR = 0.0;
+			tx->Align = tx->Mode = tx->Style = tx->Font = 0L;
+			tx->text = 0L;
+			break;
+		case typPTRTXTDEF:
+			*(TextDEF**)Desc[i].ptr = (TextDEF*)0L;
+			break;
+			}
+		if(Desc[i].type & typLAST) break;
+		}
+	return true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Save object data to memory block
+static unsigned char *SavVarBuf = 0L;
+static long SavVarSize = 0,	SavVarPos = 0;
+void SavVarInit(long len)
+{
+	if(SavVarBuf) free(SavVarBuf);
+	SavVarBuf = (unsigned char *)malloc(SavVarSize = len+4096);
+	SavVarPos = 0;
+}
+
+bool SavVarAdd(void *ptr, int size)
+{
+	int len;
+
+	if(SavVarBuf && size>0) {
+		len = sizeof(unsigned char *) + sizeof(int) + size;
+		while (SavVarSize <= SavVarPos+len) {
+			if(!(SavVarBuf = (unsigned char *)realloc(SavVarBuf, SavVarSize + 4096))) return false;
+			SavVarSize += 4096;
+			}
+		memcpy(SavVarBuf+SavVarPos, &ptr, sizeof(unsigned char *));	SavVarPos += sizeof(unsigned char *);
+		memcpy(SavVarBuf+SavVarPos, &size, sizeof(int));		SavVarPos += sizeof(int);
+		if(ptr) {
+			memcpy(SavVarBuf+SavVarPos, ptr, size);			SavVarPos += size;
+			}
+		return true;
+		}
+	return false;
+}
+
+void *SavVarFetch()
+{
+	void *tmp;
+
+	SavVarAdd((void*)0L, sizeof(unsigned char*));				
+	tmp = SavVarBuf;	SavVarSize = SavVarPos = 0;		SavVarBuf = 0L;
+	return tmp;
+}
+
+bool SaveVarGO(descIO *Desc)
+{
+	int i;
+	AxisDEF *ax;
+	TextDEF *tx;
+
+	for(i = 0; Desc[i].label; i++) {
+		switch(Desc[i].type & 0xff){
+		case typNZINT:
+		case typINT:
+			SavVarAdd(Desc[i].ptr, sizeof(int));
+			break;
+		case typNZLFLOAT:
+		case typLFLOAT:
+			SavVarAdd(Desc[i].ptr, sizeof(double));
+			break;
+		case typDWORD:
+			SavVarAdd(Desc[i].ptr, sizeof(DWORD));
+			break;
+		case typFRECT:
+			SavVarAdd(Desc[i].ptr, sizeof(fRECT));
+			break;
+		case typNZLFPOINT:
+		case typLFPOINT:
+			SavVarAdd(Desc[i].ptr, sizeof(lfPOINT));
+			break;
+		case typPOINT3D:
+			SavVarAdd(Desc[i].ptr, sizeof(fPOINT3D));
+			break;
+		case typAXDEF:
+		case typPTRAXDEF:
+			ax = (Desc[i].type & 0xff) == typAXDEF ? (AxisDEF *)Desc[i].ptr : *(AxisDEF **)Desc[i].ptr;
+			if(ax) {
+				SavVarAdd(ax, sizeof(AxisDEF));
+				if(ax->breaks && ax->nBreaks) {
+					SavVarAdd(ax->breaks, ax->nBreaks * sizeof(lfPOINT));
+					}
+				}
+			break;
+		case typLINEDEF:
+			SavVarAdd(Desc[i].ptr, sizeof(LineDEF));
+			break;
+		case typFILLDEF:
+			SavVarAdd(Desc[i].ptr, sizeof(FillDEF));
+			break;
+		case typGOBJ:	case typOBJLST:		//not supported
+			break;
+		case typIPLST:						//probably in the future
+			break;
+		case typFPLST:
+			SavVarAdd(Desc[i].count, sizeof(long));
+			SavVarAdd(*((void **)Desc[i].ptr), sizeof(lfPOINT) * (*Desc[i].count));
+			break;
+		case typFPLST3D:
+			SavVarAdd(Desc[i].count, sizeof(long));
+			SavVarAdd(*((void **)Desc[i].ptr), sizeof(fPOINT3D) * (*Desc[i].count));
+			break;
+		case typTEXT:
+			if(*(char**)(Desc[i].ptr)) SavVarAdd(Desc[i].ptr, (int)strlen(*(char**)(Desc[i].ptr))+1);
+			break;
+		case typTXTDEF:
+		case typPTRTXTDEF:
+			tx = (Desc[i].type &0xff) == typTXTDEF ? (TextDEF *)Desc[i].ptr : *(TextDEF **)Desc[i].ptr;
+			if(tx) SavVarAdd(tx, sizeof(TextDEF) - sizeof(char*));
+			break;
+			}
+		if(Desc[i].type & typLAST) break;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Graphic object member funtions for IO
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+svgOptions::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"tagAttr", typTEXT, &svgattr, 0L},
+		{"Script", typLAST | typTEXT, &script, 0L}};
+
+	switch(rw) {
+	case INIT_VARS:
+		return InitVarsGO(Desc);
+	case FILE_READ:
+		return ExecInput(Desc);
+		}
+	return false;
+}
+
+bool
+Symbol::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"ssRef", typIPLST, &ssRef, &cssRef},
+		{"Idx", typINT, &idx, 0L},
+		{"Pos", typLFPOINT, &fPos, 0L},
+		{"Size", typLFLOAT, &size, 0L},
+		{"Line", typLINEDEF, &SymLine, 0L},
+		{"FillCol", typDWORD, &SymFill.color, 0L},
+		{"Text", typPTRTXTDEF, &SymTxt, 0L},
+		{"Name", typLAST | typTEXT, &name, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		size = DefSize(SIZE_SYMBOL);
+		SymLine.color = parent ? parent->GetColor(COL_SYM_LINE) : defs.Color(COL_SYM_LINE);
+		SymLine.width = parent ? parent->GetSize(SIZE_SYM_LINE) : DefSize(SIZE_SYM_LINE);
+		SymFill.type = FILL_NONE;
+		SymFill.color = parent ? parent->GetColor(COL_SYM_FILL) : defs.Color(COL_SYM_FILL);
+		SymFill.scale = 1.0f;
+		SymFill.hatch = (LineDEF *) 0L;
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		SymFill.hatch = (LineDEF *) 0L;
+		return true;
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "Symbol", Desc);
+		}
+	return false;
+}
+
+
+bool
+Bubble::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"ssRef", typIPLST, &ssRef, &cssRef},
+		{"Pos", typLFPOINT, &fPos, 0L},
+		{"Size", typLFLOAT, &fs, 0L},
+		{"Line", typLINEDEF, &BubbleLine, 0L},
+		{"FillLine", typLINEDEF, &BubbleFillLine, 0L},
+		{"Fill", typFILLDEF, &BubbleFill, 0L},
+		{"Name", typLAST | typTEXT, &name, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		BubbleLine.width = DefSize(SIZE_BUBBLE_LINE);
+		BubbleFillLine.width = DefSize(SIZE_BUBBLE_HATCH_LINE);
+		BubbleLine.color = BubbleFillLine.color = defs.Color(COL_BUBBLE_LINE);
+		BubbleFill.color = defs.Color(COL_BUBBLE_FILL);
+		ssRef = 0L;
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		BubbleFill.hatch = &BubbleFillLine;
+		return true;
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "Bubble", Desc);
+		}
+	return false;
+}
+
+bool
+Bar::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typINT, &type, 0L},
+		{"ssRef", typIPLST, &ssRef, &cssRef},
+		{"Pos", typLFPOINT, &fPos, 0L},
+		{"Size", typLFLOAT, &size, 0L},
+		{"Org", typLFPOINT, &BarBase, 0L},
+		{"Line", typLINEDEF, &BarLine, 0L},
+		{"Fill", typFILLDEF, &BarFill, 0L},
+		{"FillLine", typLINEDEF, &HatchLine, 0L},
+		{"Name", typLAST | typTEXT, &name, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		type = BAR_VERTB;
+		memcpy(&BarFill, defs.GetFill(), sizeof(FillDEF));
+		if(BarFill.hatch) memcpy(&HatchLine, BarFill.hatch, sizeof(LineDEF));
+		BarFill.hatch = &HatchLine;
+		memcpy(&BarLine, defs.GetOutLine(), sizeof(LineDEF));
+		size = DefSize(SIZE_BAR);
+		mo = 0L;
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		BarFill.hatch = &HatchLine;
+		return true;
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "Bar", Desc);
+		}
+	return false;
+}
+
+void
+DataLine::FileValues(char *name, int type, double start, double step)
+{
+	FILE *file;
+	int i, c;
+	double fx;
+	lfPOINT *tfp;
+
+#ifdef USE_WIN_SECURE
+	if(fopen_s(&file, name, "r")) {
+		sprintf_s(TmpTxt, TMP_TXT_SIZE, "DataLine: open failed for file \"%s\"", name);
+#else
+	if(!(file = fopen(name, "r"))) {
+		sprintf(TmpTxt, "DataLine: open failed for file \"%s\"", name);
+#endif
+		ErrorBox(TmpTxt);
+		return;
+		}
+	if(Values) free(Values);
+	if(!(Values = (lfPOINT*)calloc( nPnt = 1000, sizeof(lfPOINT)))){
+		fclose(file);
+		return;
+		}
+	switch(type) {
+	case 1:				//x and y values
+		i = 0;
+		do {
+#ifdef USE_WIN_SECURE
+			c = fscanf_s(file, "%lf%lf", &Values[i].fx, &Values[i].fy);
+#else
+			c = fscanf(file, "%lf%lf", &Values[i].fx, &Values[i].fy);
+#endif
+			i++;
+			if(i >= nPnt &&(tfp = (lfPOINT*)realloc(Values, (nPnt+1000)*sizeof(lfPOINT)))){
+				Values = tfp;			nPnt += 1000;
+				}
+			else if(i >= nPnt) break;
+			}while(c == 2);
+		i--;
+		break;
+	case 2:				//only y values
+		i = 0;	fx = start;
+		do {
+#ifdef USE_WIN_SECURE
+			c = fscanf_s(file, "%lf", &Values[i].fy);
+#else
+			c = fscanf(file, "%lf", &Values[i].fy);
+#endif
+			Values[i].fx = fx;
+			i++;	fx += step;
+			if(i >= nPnt &&(tfp = (lfPOINT*)realloc(Values, (nPnt+1000)*sizeof(lfPOINT)))){
+				Values = tfp;			nPnt += 1000;
+				}
+			}while(c == 1);
+		i--;
+		break;
+		}
+	nPntSet = i-1;
+	fclose(file);
+}
+
+bool
+DataLine::FileIO(int rw)
+{
+	char *file1 = 0L;
+	char *file2 = 0L;
+	double Start = 0.0f, Step = 0.0f;
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"ssXref", typTEXT, &ssXref, 0L},
+		{"ssYref", typTEXT, &ssYref, 0L},
+		{"BgCol", typDWORD, &BgColor, 0L},
+		{"Line", typLINEDEF, &LineDef, 0L},
+		{"Data", typFPLST, &Values, &nPnt},
+		{"file_xy", typTEXT, &file2, 0L},
+		{"start_x", typNZLFLOAT, &Start, 0L},
+		{"step_x", typNZLFLOAT, &Step, 0L},
+		{"file_y", typTEXT, &file1, 0L},
+		{"Desc", typLAST | typTEXT, &name, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		isPolygon = false;
+		nPnt = nPntSet = cp = 0;
+		memcpy(&LineDef, defs.GetLine(), sizeof(LineDEF));
+		BgColor = defs.Color(COL_BG);
+		pts = 0L;		dirty = true;	mo = 0L;
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
+		min.fx = min.fy = max.fx = max.fy = 0.0;
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		nPntSet = nPnt-1;
+		if(file2)FileValues(file2, 1, 0.0, 1.0);
+		else if(file1)FileValues(file1, 2, Start, fabs(Step) > defs.min4log ? Step : 1.0);
+		if(file1) free(file1);
+		if(file2) free(file2);
+		if(nPnt && Values)return true;
+		break;
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "DataLine", Desc);
+		}
+	return false;
+}
+
+bool
+DataPolygon::FileIO(int rw)
+{
+	char *file1 = 0L;
+	char *file2 = 0L;
+	double Start = 0.0f, Step = 0.0f;
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"ssXref", typTEXT, &ssXref, 0L},
+		{"ssYref", typTEXT, &ssYref, 0L},
+		{"BgCol", typDWORD, &BgColor, 0L},
+		{"Line", typLINEDEF, &LineDef, 0L},
+		{"FillLine", typLINEDEF, &pgFillLine, 0L},
+		{"Fill", typFILLDEF, &pgFill, 0L},
+		{"Data", typFPLST, &Values, &nPnt},
+		{"file_xy", typTEXT, &file2, 0L},
+		{"start_x", typNZLFLOAT, &Start, 0L},
+		{"step_x", typNZLFLOAT, &Step, 0L},
+		{"file_y", typLAST | typTEXT, &file1, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		isPolygon = true;
+		memcpy(&LineDef, defs.GetLine(), sizeof(LineDEF));
+		LineDef.pattern = 0L;
+		BgColor = defs.Color(COL_BG);
+		memcpy(&pgFill, defs.GetFill(), sizeof(FillDEF));
+		if(pgFill.hatch) memcpy(&pgFillLine, pgFill.hatch, sizeof(LineDEF));
+		pgFill.hatch = &pgFillLine;		dirty = true;	mo = 0L;
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
+		min.fx = min.fy = max.fx = max.fy = 0.0f;
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		nPntSet = nPnt-1;
+		if(file2)FileValues(file2, 1, 0.0f, 1.0f);
+		else if(file1)FileValues(file1, 2, Start, fabs(Step) > defs.min4log ? Step : 1.0);
+		if(file1) free(file1);
+		if(file2) free(file2);
+		pgFill.hatch = &pgFillLine;
+		if(nPnt && Values)return true;
+		break;
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "DataPolygon", Desc);
+		}
+	return false;
+}
+
+bool
+RegLine::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"type", typNZINT, &type, 0L},
+		{"nPoints", typINT, &nPoints, 0L},
+		{"BgCol", typDWORD, &BgColor, 0L},
+		{"Line", typLINEDEF, &LineDef, 0L},
+		{"Range", typFRECT, &lim, 0L},
+		{"uClip", typFRECT, &uclip, 0L},
+		{"mx", typNZLFLOAT, &mx, 0L},
+		{"my", typNZLFLOAT, &my, 0L},
+		{"li1", typLFPOINT, &l1, 0L},
+		{"li2", typLFPOINT, &l2, 0L},
+		{"li3", typLFPOINT, &l3, 0L},
+		{"li4", typLFPOINT, &l4, 0L},
+		{"li5", typLAST | typLFPOINT, &l5, 0L}};
+
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		cp = 0;
+		memcpy(&LineDef, defs.GetLine(), sizeof(LineDEF));
+		BgColor = defs.Color(COL_BG);
+		pts = 0L;
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "RegLine", Desc);
+		}
+	return false;
+}
+
+void
+SDellipse::RegGO(void *n)
+{
+	if(n) {
+		if(rl)rl->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+	}
+}
+
+bool
+SDellipse::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"type", typNZINT, &type, 0L},
+		{"Line", typLINEDEF, &LineDef, 0L},
+		{"Range", typFRECT, &lim, 0L},
+		{"Regr", typGOBJ, &rl, 0L},
+		{"Data", typLAST | typFPLST, &val, &nPoints}};
+
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		memcpy(&LineDef, defs.GetOutLine(), sizeof(LineDEF));
+		pts = 0L;
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		if(rl) rl->parent = this;
+		return true;
+	case FILE_WRITE:
+		if(rl) rl->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "SDellipse", Desc);
+		}
+	return false;
+}
+
+bool
+ErrorBar::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"ssRef", typIPLST, &ssRef, &cssRef},
+		{"Pos", typLFPOINT, &fPos, 0L},
+		{"Err", typLFLOAT, &ferr, 0L},
+		{"Size", typLFLOAT, &SizeBar, 0L},
+		{"Line", typLINEDEF, &ErrLine, 0L},
+		{"Desc", typLAST | typTEXT, &name, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		SizeBar = DefSize(SIZE_ERRBAR);
+		ErrLine.width = DefSize(SIZE_ERRBAR_LINE);
+		ErrLine.color = defs.Color(COL_SYM_LINE);
+		mo = 0L;
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "ErrorBar", Desc);
+		}
+	return false;
+}
+
+bool
+Arrow::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"ssRef", typIPLST, &ssRef, &cssRef},
+		{"moveable", typNZINT, &moveable, 0L},
+		{"p1", typLFPOINT, &pos1, 0L},
+		{"p2", typLFPOINT, &pos2, 0L},
+		{"CapW", typLFLOAT, &cw, 0L},
+		{"CapL", typLFLOAT, &cl, 0L},
+		{"Line", typLAST | typLINEDEF, &LineDef, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		cw = DefSize(SIZE_ARROW_CAPWIDTH);
+		cl = DefSize(SIZE_ARROW_CAPLENGTH);
+		LineDef.color = parent ? parent->GetColor(COL_DATA_LINE) : defs.Color(COL_DATA_LINE);
+		LineDef.width = parent ? parent->GetSize(SIZE_ARROW_LINE) : DefSize(SIZE_ARROW_LINE);
+		type = ARROW_LINE;		dh1 = dh2 = 0L;		mo = 0L;
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "Arrow", Desc);
+		}
+	return false;
+}
+
+bool
+Box::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"ssRef", typIPLST, &ssRef, &cssRef},
+		{"High", typLFPOINT, &pos1, 0L},
+		{"Low", typLFPOINT, &pos2, 0L},
+		{"Size", typLFLOAT, &size, 0L},
+		{"Line", typLINEDEF, &Outline, 0L},
+		{"FillLine", typLINEDEF, &Hatchline, 0L},
+		{"Fill", typFILLDEF, &Fill, 0L},
+		{"Name", typLAST | typTEXT, &name, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		memcpy(&Outline, defs.GetOutLine(), sizeof(LineDEF));
+		memcpy(&Fill, defs.GetFill(), sizeof(FillDEF));
+		if(Fill.hatch)memcpy(&Hatchline, Fill.hatch, sizeof(LineDEF));
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
+		Fill.hatch = &Hatchline;		size = DefSize(SIZE_BAR);
+		ssRef = 0L;				mo = 0L;
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		Fill.hatch = &Hatchline;
+		return true;
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "Box", Desc);
+		}
+	return false;
+}
+
+bool
+Whisker::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"ssRef", typIPLST, &ssRef, &cssRef},
+		{"High", typLFPOINT, &pos1, 0L},
+		{"Low", typLFPOINT, &pos2, 0L},
+		{"Size", typLFLOAT, &size, 0L},
+		{"Line", typLINEDEF, &LineDef, 0L},
+		{"Desc", typLAST | typTEXT, &name, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		size = DefSize(SIZE_WHISKER);
+		LineDef.width = DefSize(SIZE_WHISKER_LINE);
+		LineDef.color = defs.Color(COL_WHISKER);
+		mo = 0L;
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "Whisker", Desc);
+		}
+	return false;
+}
+
+bool
+DropLine::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typINT, &type, 0L},
+		{"ssRef", typIPLST, &ssRef, &cssRef},
+		{"Pos", typLFPOINT, &fPos, 0L},
+		{"Line", typLAST | typLINEDEF, &LineDef, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		LineDef.color = defs.Color(COL_SYM_LINE);
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "DropLine", Desc);
+		}
+	return false;
+}
+
+bool
+Sphere::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"Line", typLINEDEF, &Line, 0L},
+		{"Fill", typFILLDEF, &Fill, 0L},
+		{"Pos", typPOINT3D, &fPos, 0L},
+		{"Size", typLFLOAT, &size, 0L},
+		{"ssRef", typLAST | typIPLST, &ssRef, &cssRef}};
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		size = DefSize(SIZE_SYMBOL);
+		Line.color = defs.Color(COL_SYM_LINE);
+		Line.width = DefSize(SIZE_SYM_LINE);
+		Fill.color = defs.Color(COL_SYM_FILL);
+		scl = 0L;		nscl = 0;
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "Sphere", Desc);
+		}
+	return false;
+}
+
+bool
+Plane3D::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Line", typLINEDEF, &Line, 0L},
+		{"Fill", typFILLDEF, &Fill, 0L},
+		{"values", typLAST | typFPLST3D, &dt, &ndt}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		ipl = 0L;	pts = 0L;
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "Plane3D", Desc);
+		}
+	return false;
+}
+
+bool
+Brick::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Line", typLINEDEF, &Line, 0L},
+		{"Fill", typFILLDEF, &Fill, 0L},
+		{"Pos", typPOINT3D, &fPos, 0L},
+		{"depth", typLFLOAT, &depth, 0L},
+		{"width", typLFLOAT, &width, 0L},
+		{"height", typLFLOAT, &height, 0L},
+		{"flags", typDWORD, &flags, 0L},
+		{"ssRef", typLAST | typIPLST, &ssRef, &cssRef}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		Line.color = defs.Color(COL_SYM_LINE);
+		Line.width = DefSize(SIZE_SYM_LINE);
+		Fill.color = defs.Color(COL_SYM_FILL);
+		faces = (plane**)calloc(6, sizeof(plane*));
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
+		mo = 0L;
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "Brick", Desc);
+		}
+	return false;
+}
+
+bool
+DropLine3D::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typINT, &type, 0L},
+		{"Line", typLINEDEF, &Line, 0L},
+		{"Pos", typPOINT3D, &fPos, 0L},
+		{"ssRef", typLAST | typIPLST, &ssRef, &cssRef}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		Line.color = defs.Color(COL_SYM_LINE);
+		Line.width = DefSize(SIZE_HAIRLINE);	mo = 0L;
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
+		ls[0] = ls[1] = ls[2] = ls[3] = ls[4] = ls[5] = 0L;
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "DropLine3D", Desc);
+		}
+	return false;
+}
+
+bool
+Arrow3D::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typINT, &type, 0L},
+		{"Line", typLINEDEF, &Line, 0L},
+		{"Org", typPOINT3D, &fPos1, 0L},
+		{"Pos", typPOINT3D, &fPos2, 0L},
+		{"CapW", typLFLOAT, &cw, 0L},
+		{"CapL", typLFLOAT, &cl, 0L},
+		{"ssRef", typLAST | typIPLST, &ssRef, &cssRef}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		cw = DefSize(SIZE_ARROW_CAPWIDTH);
+		cl = DefSize(SIZE_ARROW_CAPLENGTH);
+		Line.color = defs.Color(COL_ARROW);
+		Line.width = DefSize(SIZE_ARROW_LINE);
+		ls[0] = ls[1] = ls[2] = 0L;
+		cap = 0L;
+		type = ARROW_LINE;
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "Arrow3D", Desc);
+		}
+	return false;
+}
+
+bool
+Line3D::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Line", typLINEDEF, &Line, 0L},
+		{"ssRefX", typTEXT, &x_range, 0L},
+		{"ssRefY", typTEXT, &y_range, 0L},
+		{"ssRefZ", typTEXT, &z_range, 0L},
+		{"ssRef", typIPLST, &ssRef, &cssRef},
+		{"values", typLAST | typFPLST3D, &values, &nPts}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		Line.color = defs.Color(COL_DATA_LINE);
+		Line.width = DefSize(SIZE_DATA_LINE);
+		ls = 0L;	pts = 0L;	npts = 0L;	mo=0L;
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
+		min.fx = min.fy = min.fz = max.fx = max.fy = max.fz = 0.0;
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		if(nPts > 1) ls = (line_segment **)calloc(nPts-1, sizeof(line_segment*));
+		return true;
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "Line3D", Desc);
+		}
+	return false;
+}
+
+bool
+Label::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"ssRef", typIPLST, &ssRef, &cssRef},
+		{"moveable", typNZINT, &moveable, 0L},
+		{"Pos", typNZLFPOINT, &fPos, 0L},
+		{"Dist", typNZLFPOINT, &fDist, 0L},
+		{"Flags", typDWORD, &flags, 0L},
+		{"TxtDef", typLAST | typTXTDEF, &TextDef, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		TextDef.ColTxt = 0x0L;
+		TextDef.ColBg = 0x00ffffffL;
+		TextDef.fSize = DefSize(SIZE_TEXT);
+		TextDef.RotBL = TextDef.RotCHAR = 0.0;
+		TextDef.iSize = 0;
+		TextDef.Align = TXA_VTOP | TXA_HLEFT;
+		TextDef.Mode = TXM_TRANSPARENT;
+		TextDef.Style = TXS_NORMAL;
+		TextDef.Font = FONT_HELVETICA;
+		TextDef.text = 0L;	bgcolor = 0x00ffffffL;
+		bgLine.width = 0.0;		bgLine.patlength = 6.0;
+		bgLine.color = bgcolor;	bgLine.pattern = 0L;
+		CursorPos = 0;	defDisp = 0L;	bBGvalid = bModified = false;
+		curr_z = 0.0;		is3D = false;
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		if(parent && parent->Id != GO_MLABEL) {
+			if(!TextDef.text || !TextDef.text[0]) return false;
+			}
+		return ExecOutput(Notary->RegisterGO(this), "Label", Desc);
+		}
+	return false;
+}
+
+void
+mLabel::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Lines) for(i = 0; i < nLines; i++) if(Lines[i]) Lines[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+	}
+}
+
+bool
+mLabel::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"moveable", typNZINT, &moveable, 0L},
+		{"Pos", typNZLFPOINT, &fPos, 0L},
+		{"Dist", typNZLFPOINT, &fDist, 0L},
+		{"lspc", typLFLOAT, &lspc, 0L},
+		{"Flags", typDWORD, &flags, 0L},
+		{"TxtDef", typTXTDEF, &TextDef, 0L},
+		{"Lines", typLAST | typOBJLST, &Lines, &nLines}};
+	int i;
+
+	switch(rw) {
+	case SAVE_VARS:
+		//The lines inherit settings from this object.
+		//We need not save them in this context
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		TextDef.ColTxt = 0x0L;
+		TextDef.ColBg = 0x00ffffffL;
+		TextDef.fSize = DefSize(SIZE_TEXT);
+		TextDef.RotBL = TextDef.RotCHAR = 0.0;
+		TextDef.iSize = 0;
+		TextDef.Align = TXA_VTOP | TXA_HLEFT;
+		TextDef.Mode = TXM_TRANSPARENT;
+		TextDef.Style = TXS_NORMAL;
+		TextDef.Font = FONT_HELVETICA;
+		TextDef.text = 0L;
+		undo_flags = 0L;	lspc = 1.0;
+		curr_z = 0.0;		is3D = false;
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		if(Lines) for ( i = 0; i < nLines; i++)
+			if(Lines[i]) Lines[i]->parent = this;
+		return true;
+	case FILE_WRITE:
+		if(Lines) for ( i = 0; i < nLines; i++)
+			if(Lines[i]) Lines[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "mLabel", Desc);
+		}
+	return false;
+}
+
+bool
+TextFrame::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"moveable", typNZINT, &moveable, 0L},
+		{"Pos1", typNZLFPOINT, &pos1, 0L},
+		{"Pos2", typNZLFPOINT, &pos2, 0L},
+		{"lspc", typLFLOAT, &lspc, 0L},
+		{"Pad", typFRECT, &pad, 0L},
+		{"TxtDef", typTXTDEF, &TextDef, 0L},
+		{"Line", typLINEDEF, &Line, 0L},
+		{"FillLine", typLINEDEF, &FillLine, 0L},
+		{"Fill", typFILLDEF, &Fill, 0L},
+		{"Text", typLAST | typTEXT, &text, 0L}};
+	switch(rw) {
+	case SAVE_VARS:
+		return false;
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		TextDef.ColTxt = 0x0L;
+		TextDef.ColBg = 0x00ffffffL;
+		TextDef.fSize = DefSize(SIZE_TEXT);
+		TextDef.RotBL = TextDef.RotCHAR = 0.0;
+		TextDef.iSize = 0;
+		TextDef.Align = TXA_VBOTTOM | TXA_HLEFT;
+		TextDef.Mode = TXM_TRANSPARENT;
+		TextDef.Style = TXS_NORMAL;
+		TextDef.Font = FONT_HELVETICA;
+		TextDef.text = 0L;
+		lines = 0L;		nlines = 0;			drc = 0L;
+		cur_pos.x = cur_pos.y = tm_c = 0;	tm_rec = 0L;
+		bModified = bResize = has_m1 = has_m2 = false;
+		if(Fill.hatch) memcpy(&FillLine, Fill.hatch, sizeof(LineDEF));
+		Fill.hatch = &FillLine;			c_char = m1_char = m2_char = '?';
+		pad.Xmin = pad.Xmax = pad.Ymin = pad.Ymax = DefSize(SIZE_SYMBOL)/2.0;
+		Cursor.left = Cursor.right = Cursor.top = Cursor.bottom = 0;
+		pad.Xmax *= 2.0;
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);				Fill.hatch = &FillLine;
+		return true;
+	case FILE_WRITE:
+		if(lines)lines2text();
+		if(!text || !text[0]) return false;
+		return ExecOutput(Notary->RegisterGO(this), "TextFrame", Desc);
+		}
+	return false;
+}
+
+bool
+segment::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"moveable", typNZINT, &moveable, 0L},
+		{"cent", typLFPOINT, &fCent, 0L},
+		{"ri", typNZLFLOAT, &radius1, 0L},
+		{"ra", typLFLOAT, &radius2, 0L},
+		{"start", typLFLOAT, &angle1, 0L},
+		{"end", typLFLOAT, &angle2, 0L},
+		{"shout", typNZLFLOAT, &shift, 0L},
+		{"Line", typLINEDEF, &segLine, 0L},
+		{"FillLine", typLINEDEF, &segFillLine, 0L},
+		{"Fill", typLAST | typFILLDEF, &segFill, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		segLine.width = DefSize(SIZE_SEGLINE);
+		pts = 0L;	nPts = 0;	mo = 0L;
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		segFill.hatch = &segFillLine;
+		return true;
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "segment", Desc);
+		}
+	return false;
+}
+
+bool
+polyline::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"moveable", typNZINT, &moveable, 0L},
+		{"Data", typFPLST, &Values, &nPoints},
+		{"Line", typLINEDEF, &pgLine, 0L},
+		{"FillLine", typLINEDEF, &pgFillLine, 0L},
+		{"Fill", typLAST | typFILLDEF, &pgFill, 0L}};
+	
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		memcpy(&pgLine, defs.plLineDEF(0L), sizeof(LineDEF));
+		memcpy(&pgFill, defs.pgFillDEF(0L), sizeof(FillDEF));
+		if(pgFill.hatch) memcpy(&pgFillLine, pgFill.hatch, sizeof(LineDEF));
+		pgFill.hatch = &pgFillLine;
+		pts = 0L;		nPts = 0;
+		pHandles = 0L;
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		pgFill.hatch = &pgFillLine;
+		return true;
+	case FILE_WRITE:
+		if(type != 1) Desc[3].type |= typLAST;	//skip fill for polyline
+		return ExecOutput(Notary->RegisterGO(this), 
+			type == 1 ? (char*)"polygon" : (char*)"polyline", Desc);
+		}
+	return false;
+}
+
+bool
+Bezier::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"moveable", typNZINT, &moveable, 0L},
+		{"Data", typFPLST, &Values, &nPoints},
+		{"Line", typLINEDEF, &pgLine, 0L},
+		{"FillLine", typLINEDEF, &pgFillLine, 0L},
+		{"Fill", typLAST | typFILLDEF, &pgFill, 0L}};
+	
+	switch(rw) {
+	case INIT_VARS:
+		//assume that all initialization is done by polyline::FileIO(int rw)
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		pgFill.hatch = &pgFillLine;
+		return true;
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "bezier", Desc);
+		}
+	return false;
+}
+
+bool
+rectangle::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"moveable", typNZINT, &moveable, 0L},
+		{"p1", typLFPOINT, &fp1, 0L},
+		{"p2", typLFPOINT, &fp2, 0L},
+		{"Line", typLINEDEF, &Line, 0L},
+		{"FillLine", typLINEDEF, &FillLine, 0L},
+		{"Fill", typFILLDEF, &Fill, 0L},
+		{"Rad", typNZLFLOAT, &rad, 0L},
+		{"Name", typLAST | typTEXT, &name, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		memcpy(&Line, defs.pgLineDEF(0L), sizeof(LineDEF));
+		memcpy(&Fill, defs.pgFillDEF(0L), sizeof(FillDEF));
+		if(Fill.hatch) memcpy(&FillLine, Fill.hatch, sizeof(LineDEF));
+		Fill.hatch = &FillLine;
+		pts = 0L;	nPts = 0L;
+		rad = DefSize(SIZE_RRECT_RAD);
+		drc = 0L;
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		Fill.hatch = &FillLine;
+		return true;
+	case FILE_WRITE:
+		if(type != 2) rad = 0.0;
+		ExecOutput(Notary->RegisterGO(this), 
+			type == 1? (char*)"ellipse" : type == 2? (char*)"roundrec" :
+			(char*)"rectangle", Desc);
+		return true;
+		}
+	return false;
+}
+
+void
+LegItem::RegGO(void *n)
+{
+	if(n) {
+		if(Sym) Sym->RegGO(n);
+		if(Desc) Desc->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+LegItem::FileIO(int rw)
+{
+	descIO Des[] = {
+		{"D_Line", typLINEDEF, &DataLine, 0L},
+		{"O_Line", typLINEDEF, &OutLine, 0L},
+		{"H_Line", typLINEDEF, &HatchLine, 0L},
+		{"Fill", typFILLDEF, &Fill, 0L},
+		{"Sym", typGOBJ, &Sym, 0L},
+		{"Text", typGOBJ, &Desc, 0L},
+		{"flags", typLAST | typDWORD, &flags, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Des);
+	case INIT_VARS:
+		InitVarsGO(Des);
+		Fill.hatch = &HatchLine;
+		return true;
+	case FILE_READ:
+		ExecInput(Des);
+		Fill.hatch = &HatchLine;
+		if(Sym) Sym->parent=this;
+		if(Desc) Desc->parent=this;
+		return true;
+	case FILE_WRITE:
+		if(Sym) Sym->FileIO(rw);
+		if(Desc) Desc->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "LegItem", Des);
+		}
+	return false;
+}
+
+void
+Legend::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Items) for(i = 0; i < nItems; i++) if(Items[i]) Items[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+Legend::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"pos", typLFPOINT, &pos, 0L},
+		{"rec1", typFRECT, &B_Rect, 0L},
+		{"rec2", typFRECT, &D_Rect, 0L},
+		{"rec3", typFRECT, &F_Rect, 0L},
+		{"Items", typLAST | typOBJLST, &Items, &nItems}};
+	int i;
+	double d;
+
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		B_Rect.Ymin = DefSize(SIZE_DRECT_TOP);			B_Rect.Xmin = DefSize(SIZE_DRECT_LEFT);
+		B_Rect.Xmax = B_Rect.Xmin + 1.5*(d = DefSize(SIZE_BAR));
+		B_Rect.Ymin += d*0.2;
+		B_Rect.Ymax = B_Rect.Ymin + d/2.0;
+		D_Rect.Ymin = 0.0;			D_Rect.Xmin = d*0.7;
+		D_Rect.Xmax = d*1.3;		D_Rect.Ymax = d*0.4;
+		F_Rect.Ymin = 0.0;			F_Rect.Xmin = d*0.2;
+		F_Rect.Xmax = d*1.3;		F_Rect.Ymax = d*0.4;
+		to = 0L;					hasLine = false;
+		trc.left = trc.right = trc.top = trc.bottom = 0;
+		if(!name) {
+			name = (char*)malloc(20 * sizeof(char));
+			rlp_strcpy(name, 20, "Legend");
+			}
+		return true;
+	case FILE_READ:
+		nItems = 0L;
+		ExecInput(Desc);
+		if(Items) for(i = 0; i < nItems; i++) if(Items[i]) Items[i]->parent=this;
+		return true;
+	case FILE_WRITE:
+		if(Items) for(i = 0; i < nItems; i++) if(Items[i]) Items[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "Legend", Desc);
+		}
+	return false;
+}
+
+void
+PlotScatt::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(TheLine) TheLine->RegGO(n);
+		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->RegGO(n);
+		if(Errors) for(i = 0; i < nPoints; i++) if(Errors[i]) Errors[i]->RegGO(n);
+		if(Arrows) for(i = 0; i < nPoints; i++) if(Arrows[i]) Arrows[i]->RegGO(n);
+		if(DropLines) for(i = 0; i < nPoints; i++) if(DropLines[i]) DropLines[i]->RegGO(n);
+		if(Labels) for(i = 0; i < nPoints; i++) if(Labels[i]) Labels[i]->RegGO(n);
+		if(Bars) for(i = 0; i < nPoints; i++) if(Bars[i]) Bars[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+PlotScatt::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"hide", typNZINT, &hidden, 0L},
+		{"Bounds", typFRECT, &Bounds, 0L},
+		{"DefSym", typNZINT, &DefSym, 0L},
+		{"baDist", typLFPOINT, &BarDist, 0L},
+		{"xRange", typTEXT, &xRange, 0L},
+		{"yRange", typTEXT, &yRange, 0L},
+		{"eRange", typTEXT, &ErrRange, 0L},
+		{"lRange", typTEXT, &LbRange, 0L},
+		{"x_axis", typNZINT, &use_xaxis, 0L},
+		{"y_axis", typNZINT, &use_yaxis, 0L},
+		{"Bars", typOBJLST, &Bars, &nPoints},
+		{"Symbols", typOBJLST, &Symbols, &nPoints},
+		{"PL", typGOBJ, &TheLine, 0L},
+		{"ErrBars", typOBJLST, &Errors, &nPoints},
+		{"Arrows", typOBJLST, &Arrows, &nPoints},
+		{"dLines", typOBJLST, &DropLines, &nPoints},
+		{"Labels", typOBJLST, &Labels, &nPoints},
+		{"x_info", typTEXT, &x_info, 0L},
+		{"y_info", typTEXT, &y_info, 0L},
+		{"DataDesc", typLAST | typTEXT, &data_desc, 0L}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		x_info = y_info = z_info = 0L;
+		InitVarsGO(Desc);
+		DefSym = SYM_CIRCLE;
+		DefSel = 0x01;
+		dirty = true;
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "xy-plot (%s)", name);
+#else
+			i = sprintf(TmpTxt, "xy-plot (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		nPoints = 0L;
+		ExecInput(Desc);
+		ForEach(FE_PARENT, 0L, 0L);
+		return true;
+	case FILE_WRITE:
+		if(TheLine) TheLine->FileIO(rw);
+		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->FileIO(rw);
+		if(Errors) for(i = 0; i < nPoints; i++) if(Errors[i]) Errors[i]->FileIO(rw);
+		if(Arrows) for(i = 0; i < nPoints; i++) if(Arrows[i]) Arrows[i]->FileIO(rw);
+		if(DropLines) for(i = 0; i < nPoints; i++) if(DropLines[i]) DropLines[i]->FileIO(rw);
+		if(Labels) for(i = 0; i < nPoints; i++) if(Labels[i]) Labels[i]->FileIO(rw);
+		if(Bars) for(i = 0; i < nPoints; i++) if(Bars[i]) Bars[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "PlotScatt", Desc);
+		}
+	return false;
+}
+
+bool
+xyStat::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"Bounds", typFRECT, &Bounds, 0L},
+		{"DefSym", typNZINT, &DefSym, 0L},
+		{"baDist", typLFPOINT, &BarDist, 0L},
+		{"confi", typLFLOAT, &ci, 0L},
+		{"xRange", typTEXT, &xRange, 0L},
+		{"yRange", typTEXT, &yRange, 0L},
+		{"prefix", typTEXT, &case_prefix, 0L},
+		{"x_axis", typNZINT, &use_xaxis, 0L},
+		{"y_axis", typNZINT, &use_yaxis, 0L},
+		{"Bars", typOBJLST, &Bars, &nPoints},
+		{"Symbols", typOBJLST, &Symbols, &nPoints},
+		{"PL", typGOBJ, &TheLine, 0L},
+		{"ErrBars", typOBJLST, &Errors, &nPoints},
+		{"Labels", typOBJLST, &Labels, &nPoints},
+		{"x_info", typTEXT, &x_info, 0L},
+		{"y_info", typLAST | typTEXT, &y_info, 0L}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		//most initialistion is done by PlotScatt::FileIO
+		curr_data = 0L;
+		case_prefix = 0L;
+		ci = 95.0;
+		return true;
+	case FILE_READ:
+		nPoints = 0L;
+		ExecInput(Desc);
+		ForEach(FE_PARENT, 0L, 0L);
+		return true;
+	case FILE_WRITE:
+		if(TheLine) TheLine->FileIO(rw);
+		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->FileIO(rw);
+		if(Errors) for(i = 0; i < nPoints; i++) if(Errors[i]) Errors[i]->FileIO(rw);
+		if(Labels) for(i = 0; i < nPoints; i++) if(Labels[i]) Labels[i]->FileIO(rw);
+		if(Bars) for(i = 0; i < nPoints; i++) if(Bars[i]) Bars[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "xyStat", Desc);
+		}
+	return false;
+}
+
+void
+FreqDist::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(plots) for(i = 0; i < nPlots; i++) if(plots[i]) plots[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+FreqDist::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"ssRef", typTEXT, &ssRef, 0L},
+		{"x_axis", typNZINT, &use_xaxis, 0L},
+		{"y_axis", typNZINT, &use_yaxis, 0L},
+		{"cl_start", typNZLFLOAT, &start, 0L},
+		{"cl_size", typLFLOAT, &step, 0L},
+		{"BarLine", typLINEDEF, &BarLine, 0L},
+		{"BarFill", typFILLDEF, &BarFill, 0L},
+		{"BarFillLine", typLINEDEF, &HatchLine, 0L},
+		{"plots", typLAST | typOBJLST, &plots, &nPlots}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		memcpy(&BarFill, defs.GetFill(), sizeof(FillDEF));
+		BarFill.color = 0x00c0ffffL;
+		if(BarFill.hatch) memcpy(&HatchLine, BarFill.hatch, sizeof(LineDEF));
+		BarFill.hatch = &HatchLine;
+		memcpy(&BarLine, defs.GetOutLine(), sizeof(LineDEF));
+		curr_data=0L;		dirty = true;
+		dmin = HUGE_VAL, dmax = -HUGE_VAL;
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "freq. dist. (%s)", name);
+#else
+			i = sprintf(TmpTxt, "freq. dist. (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		if(plots) for(i = 0; i < nPlots; i++) if(plots[i]) plots[i]->parent=this;
+		return true;
+	case FILE_WRITE:
+		if(plots) for(i = 0; i < nPlots; i++) if(plots[i]) plots[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "FreqDist", Desc);
+		}
+	return false;
+}
+
+void
+Regression::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(rLine) rLine->RegGO(n);
+		if(sde) sde->RegGO(n);
+		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+Regression::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"Bounds", typFRECT, &Bounds, 0L},
+		{"xRange", typTEXT, &xRange, 0L},
+		{"yRange", typTEXT, &yRange, 0L},
+		{"x_axis", typNZINT, &use_xaxis, 0L},
+		{"y_axis", typNZINT, &use_yaxis, 0L},
+		{"Line", typGOBJ, &rLine, 0L},
+		{"Ellipse", typGOBJ, &sde, 0L},
+		{"Symbols", typLAST | typOBJLST, &Symbols, &nPoints}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		dirty = true;
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "regression (%s)", name);
+#else
+			i = sprintf(TmpTxt, "regression (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		nPoints = 0L;
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		if(rLine) rLine->FileIO(rw);
+		if(sde) sde->FileIO(rw);
+		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "Regression", Desc);
+		}
+	return false;
+}
+
+void
+BubblePlot::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Bubbles) for(i = 0; i < nPoints; i++) if(Bubbles[i]) Bubbles[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+BubblePlot::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"hide", typNZINT, &hidden, 0L},
+		{"Bounds", typFRECT, &Bounds, 0L},
+		{"x_axis", typNZINT, &use_xaxis, 0L},
+		{"y_axis", typNZINT, &use_yaxis, 0L},
+		{"Line", typLINEDEF, &BubbleLine, 0L},
+		{"FillLine", typLINEDEF, &BubbleFillLine, 0L},
+		{"Fill", typFILLDEF, &BubbleFill, 0L},
+		{"Bubbles", typLAST | typOBJLST, &Bubbles, &nPoints}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		BubbleFill.color = defs.Color(COL_BUBBLE_FILL);
+		BubbleLine.color = defs.Color(COL_BUBBLE_LINE);
+		BubbleLine.width = DefSize(SIZE_BUBBLE_LINE);
+		BubbleFillLine.color = defs.Color(COL_BUBBLE_FILLLINE);
+		BubbleFillLine.width = DefSize(SIZE_BUBBLE_HATCH_LINE);
+		BubbleFill.hatch = &BubbleFillLine;
+		dirty = true;
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "Bubble Plot (%s)", name);
+#else
+			i = sprintf(TmpTxt, "Bubble Plot (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		if(Bubbles) for(i = 0; i < nPoints; i++) if(Bubbles[i]) Bubbles[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "BubblePlot", Desc);
+		}
+	return false;
+}
+
+void
+PolarPlot::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Plots) for(i = 0; i < nPlots; i++) if(Plots[i]) Plots[i]->RegGO(n);
+		if(Axes) for(i = 0; i < nAxes; i++) if(Axes[i]) Axes[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+PolarPlot::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"Bounds", typFRECT, &Bounds, 0L},
+		{"ang_offs", typLFLOAT, &offs, 0L},
+		{"Plots", typOBJLST, &Plots, (long*)&nPlots},
+		{"Axes", typOBJLST, &Axes, (long*)&nAxes},
+		{"FillLine", typLINEDEF, &FillLine, 0L},
+		{"Fill", typLAST | typFILLDEF, &Fill, 0L}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		CurrDisp = 0L;
+		InitVarsGO(Desc);
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "polar root (%s)", name);
+#else
+			i = sprintf(TmpTxt, "polar root (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		if(Plots) for(i = 0; i < nPlots; i++) if(Plots[i]) Plots[i]->FileIO(rw);
+		if(Axes) for(i = 0; i < nAxes; i++) if(Axes[i]) Axes[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "PolarPlot", Desc);
+		}
+	return false;
+}
+
+void
+BoxPlot::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Boxes) for(i = 0; i < nPoints; i++) if(Boxes[i]) Boxes[i]->RegGO(n);
+		if(Whiskers) for(i = 0; i < nPoints; i++) if(Whiskers[i]) Whiskers[i]->RegGO(n);
+		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->RegGO(n);
+		if(Labels) for(i = 0; i < nPoints; i++) if(Labels[i]) Labels[i]->RegGO(n);
+		if(TheLine) TheLine->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+BoxPlot::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"Bounds", typFRECT, &Bounds, 0L},
+		{"xRange", typTEXT, &xRange, 0L},
+		{"yRange", typTEXT, &yRange, 0L},
+		{"prefix", typTEXT, &case_prefix, 0L},
+		{"boDist", typLFPOINT, &BoxDist, 0L},
+		{"ci_box", typNZLFLOAT, &ci_box, 0L},
+		{"ci_err", typNZLFLOAT, &ci_err, 0L},
+		{"x_axis", typNZINT, &use_xaxis, 0L},
+		{"y_axis", typNZINT, &use_yaxis, 0L},
+		{"Boxes", typOBJLST, &Boxes, &nPoints},
+		{"Whiskers", typOBJLST, &Whiskers, &nPoints},
+		{"Symbols", typOBJLST, &Symbols, &nPoints},
+		{"Labels", typOBJLST, &Labels, &nPoints},
+		{"Line", typLAST | typGOBJ, &TheLine, 0L}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		dirty = true;		InitVarsGO(Desc);
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "boxes (%s)", name);
+#else
+			i = sprintf(TmpTxt, "boxes (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		curr_data = 0L;
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		if(Boxes) for(i = 0; i < nPoints; i++) 
+			if(Boxes[i]) Boxes[i]->FileIO(rw);
+		if(Whiskers) for(i = 0; i < nPoints; i++) 
+			if(Whiskers[i]) Whiskers[i]->FileIO(rw);
+		if(Symbols) for(i = 0; i < nPoints; i++) 
+			if(Symbols[i]) Symbols[i]->FileIO(rw);
+		if(Labels) for(i = 0; i < nPoints; i++) 
+			if(Labels[i]) Labels[i]->FileIO(rw);
+		if(TheLine) TheLine->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "BoxPlot", Desc);
+		}
+	return false;
+}
+
+void
+DensDisp::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Boxes) for(i = 0; i < nPoints; i++) if(Boxes[i]) Boxes[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+DensDisp::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"Bounds", typFRECT, &Bounds, 0L},
+		{"xRange", typTEXT, &xRange, 0L},
+		{"yRange", typTEXT, &yRange, 0L},
+		{"x_axis", typNZINT, &use_xaxis, 0L},
+		{"y_axis", typNZINT, &use_yaxis, 0L},
+		{"Line", typLINEDEF, &DefLine, 0L},
+		{"FillLine", typLINEDEF, &DefFillLine, 0L},
+		{"Fill", typFILLDEF, &DefFill, 0L},
+		{"Boxes", typLAST | typOBJLST, &Boxes, &nPoints}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		return InitVarsGO(Desc);
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		if(Boxes) for(i = 0; i < nPoints; i++) if(Boxes[i]) Boxes[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "DensDisp", Desc);
+		}
+	return false;
+}
+
+void
+StackBar::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Boxes) for(i = 0; i < numPlots; i++) if(Boxes[i]) Boxes[i]->RegGO(n);
+		if(xyPlots) for(i = 0; i < numXY; i++) if(xyPlots[i]) xyPlots[i]->RegGO(n);
+		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) Polygons[i]->RegGO(n);
+		if(Lines) for(i = 0; i < numPL; i++) if(Lines[i]) Lines[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+StackBar::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Bounds", typFRECT, &Bounds, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"x_axis", typNZINT, &use_xaxis, 0L},
+		{"y_axis", typNZINT, &use_yaxis, 0L},
+		{"cumData", typNZINT, &cum_data_mode, 0L},
+		{"StartVal", typNZLFLOAT, &StartVal, 0L},
+		{"Dspm", typNZLFPOINT, &dspm, 0L},
+		{"ssXrange", typTEXT, &ssXrange, 0L},
+		{"ssYrange", typTEXT, &ssYrange, 0L},
+		{"BoxBars", typOBJLST, &Boxes, (long*)&numPlots},
+		{"Plots", typOBJLST, &xyPlots, (long*)&numXY},
+		{"Polygons", typOBJLST, &Polygons, (long*)&numPG},
+		{"Lines", typLAST | typOBJLST, &Lines, (long*)&numPL}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		dirty = true;	CumData = 0L;
+		InitVarsGO(Desc);
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "stack (%s)", name);
+#else
+			i = sprintf(TmpTxt, "stack (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		return true;
+	case FILE_WRITE:
+		if(Boxes) for(i = 0; i < numPlots; i++) if(Boxes[i]) Boxes[i]->FileIO(rw);
+		if(xyPlots) for(i = 0; i < numXY; i++) if(xyPlots[i]) xyPlots[i]->FileIO(rw);
+		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) Polygons[i]->FileIO(rw);
+		if(Lines) for(i = 0; i < numPL; i++) if(Lines[i]) Lines[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "Stacked", Desc);
+		}
+	return false;
+}
+
+void
+PieChart::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Segments) for(i = 0; i < nPts; i++) if(Segments[i]) Segments[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+PieChart::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"ssRefA", typTEXT, &ssRefA, 0L},
+		{"ssRefR", typTEXT, &ssRefR, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"CtDef", typLFPOINT, &CtDef, 0L},
+		{"FacRad", typLFLOAT, &FacRad, 0L},
+		{"Segs", typLAST | typOBJLST, &Segments, (long*)&nPts}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		Bounds.Xmax = Bounds.Ymax = 100.0f;
+		Bounds.Xmin = Bounds.Ymin = -100.0f;
+		InitVarsGO(Desc);
+		CtDef.fx = 90.0;	CtDef.fy = 360.0;
+		FacRad = 1.0;
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "pie chart (%s)", name);
+#else
+			i = sprintf(TmpTxt, "pie chart (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		if(Segments) for(i = 0; i < nPts; i++) if(Segments[i]) Segments[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "SegChart", Desc);
+		}
+	return false;
+}
+
+void
+GoGroup::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Objects) for(i = 0; i < nObs; i++) if(Objects[i]) Objects[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+GoGroup::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Pos", typNZLFPOINT, &fPos, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"Items", typLAST | typOBJLST, &Objects, (long*)&nObs}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		Bounds.Xmax = Bounds.Ymax = 100.0f;
+		Bounds.Xmin = Bounds.Ymin = -100.0f;
+		return InitVarsGO(Desc);
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		if(Objects) for(i = 0; i < nObs; i++) if(Objects[i]) Objects[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "group", Desc);
+		}
+	return false;
+}
+
+void
+Scatt3D::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Line) Line->RegGO(n);
+		if(rib) rib->RegGO(n);
+		if(Balls) for(i = 0; i < nBalls; i++) if(Balls[i]) Balls[i]->RegGO(n);
+		if(Columns) for(i = 0; i < nColumns; i++) if(Columns[i]) Columns[i]->RegGO(n);
+		if(DropLines) for(i = 0; i < nDropLines; i++) if(DropLines[i]) DropLines[i]->RegGO(n);
+		if(Arrows) for(i = 0; i < nArrows; i++) if(Arrows[i]) Arrows[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+Scatt3D::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"ssRefX", typTEXT, &ssRefX, 0L},
+		{"ssRefY", typTEXT, &ssRefY, 0L},
+		{"ssRefZ", typTEXT, &ssRefZ, 0L},
+		{"DataDesc", typTEXT, &data_desc, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"x_axis", typNZINT, &use_xaxis, 0L},
+		{"y_axis", typNZINT, &use_yaxis, 0L},
+		{"z_axis", typNZINT, &use_zaxis, 0L},
+		{"Line", typGOBJ, &Line, 0L},
+		{"Balls", typOBJLST, &Balls, &nBalls},
+		{"Columns", typOBJLST, &Columns, &nColumns},
+		{"DropLines", typOBJLST, &DropLines, &nDropLines},
+		{"ParaV", typGOBJ, &rib, 0L},
+		{"Arrows", typLAST | typOBJLST, &Arrows, &nArrows}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		c_flags = 0L;
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
+		xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
+		dirty = true;
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "xyz-plot (%s)", name);
+#else
+			i = sprintf(TmpTxt, "xyz-plot (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		//now set parent in all children
+		if(Balls) for(i = 0; i < nBalls; i++) if(Balls[i]) Balls[i]->parent = this;
+		if(Columns) for(i = 0; i < nColumns; i++) if(Columns[i]) Columns[i]->parent = this;
+		if(DropLines) for(i = 0; i < nDropLines; i++) if(DropLines[i]) DropLines[i]->parent = this;
+		if(Arrows) for(i = 0; i < nArrows; i++) if(Arrows[i]) Arrows[i]->parent = this;
+		if(Line) Line->parent = this;
+		if(rib) rib->parent = this;
+		return true;
+	case FILE_WRITE:
+		if(Line) Line->FileIO(rw);
+		if(rib) rib->FileIO(rw);
+		if(Balls) for(i = 0; i < nBalls; i++) if(Balls[i]) Balls[i]->FileIO(rw);
+		if(Columns) for(i = 0; i < nColumns; i++) if(Columns[i]) Columns[i]->FileIO(rw);
+		if(DropLines) for(i = 0; i < nDropLines; i++) if(DropLines[i]) DropLines[i]->FileIO(rw);
+		if(Arrows) for(i = 0; i < nArrows; i++) if(Arrows[i]) Arrows[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "Scatt3D", Desc);
+		}
+	return false;
+}
+
+void
+Ribbon::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+Ribbon::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"z-pos", typNZLFLOAT, &z_value},
+		{"z-width", typNZLFLOAT, &z_width},
+		{"relwidth", typNZLFLOAT, &relwidth},
+		{"ssRefX", typTEXT, &ssRefX, 0L},
+		{"ssRefY", typTEXT, &ssRefY, 0L},
+		{"ssRefZ", typTEXT, &ssRefZ, 0L},
+		{"DataDesc", typTEXT, &data_desc, 0L},
+		{"Line", typLINEDEF, &Line, 0L},
+		{"Fill", typFILLDEF, &Fill, 0L},
+		{"values", typFPLST3D, &values, &nVal},
+		{"Planes", typLAST | typOBJLST, &planes, &nPlanes}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
+		xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
+		relwidth = 0.6;				dirty = true;
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "ribbon (%s)", name);
+#else
+			i = sprintf(TmpTxt, "ribbon (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		//now set parent in all children
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->parent = this;
+		return true;
+	case FILE_WRITE:
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "Ribbon", Desc);
+		}
+	return false;
+}
+
+void
+Grid3D::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->RegGO(n);
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+Grid3D::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"Start", typPOINT3D, &start, 0L},
+		{"Step", typPOINT3D, &step, 0L},
+		{"Line", typLINEDEF, &Line, 0L},
+		{"Fill", typFILLDEF, &Fill, 0L},
+		{"lines", typOBJLST, &lines, &nLines},
+		{"planes", typLAST | typOBJLST, &planes, &nPlanes}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
+		xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
+		step.fx = step.fz = 1.0;
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "grid (%s)", name);
+#else
+			i = sprintf(TmpTxt, "grid (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		//now set parent in all children
+		if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->parent = this;
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->parent = this;
+		return true;
+	case FILE_WRITE:
+		if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->FileIO(rw);
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "Grid3D", Desc);
+		}
+	return false;
+}
+
+bool
+Limits::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Bounds", typLAST | typFRECT, &Bounds, 0L}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "limits (%s)", name);
+#else
+			i = sprintf(TmpTxt, "limits (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), "Limits", Desc);
+		}
+	return false;
+}
+
+void
+Function::RegGO(void *n)
+{
+	if(n) {
+		if(dl)dl->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+Function::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"hide", typNZINT, &hidden, 0L},
+		{"x1", typNZLFLOAT, &x1, 0L},
+		{"x2", typNZLFLOAT, &x2, 0L},
+		{"xstep", typNZLFLOAT, &xstep, 0L},
+		{"Line", typLINEDEF, &Line, 0L},
+		{"f_xy", typTEXT, &cmdxy, 0L},
+		{"param", typTEXT, &param, 0L},
+		{"DataLine", typGOBJ, &dl, 0L},
+		{"Desc", typLAST | typTEXT, &name, 0L}};
+	int i;
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		cmdxy = param = 0L;
+		memcpy(&Line, defs.GetLine(), sizeof(LineDEF));
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "function (%s)", name);
+#else
+			i = sprintf(TmpTxt, "function (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		if(dl) dl->parent = this;
+		return true;
+	case FILE_WRITE:
+		if(dl) dl->FileIO(rw);
+		ExecOutput(Notary->RegisterGO(this), "Function", Desc);
+		}
+	return false;
+}
+
+void
+FitFunc::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+FitFunc::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"hide", typNZINT, &hidden, 0L},
+		{"ssXref", typTEXT, &ssXref, 0L},
+		{"ssYref", typTEXT, &ssYref, 0L},
+		{"x1", typNZLFLOAT, &x1, 0L},
+		{"x2", typNZLFLOAT, &x2, 0L},
+		{"xstep", typNZLFLOAT, &xstep, 0L},
+		{"conv", typNZLFLOAT, &conv, 0L},
+		{"chi2", typNZLFLOAT, &chi2, 0L},
+		{"maxiter", typNZINT, &maxiter, 0L},
+		{"Line", typLINEDEF, &Line, 0L},
+		{"f_xy", typTEXT, &cmdxy, 0L},
+		{"p_xy", typTEXT, &parxy, 0L},
+		{"Symbols", typLAST | typOBJLST, &Symbols, &nPoints}};
+	int i;
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		cmdxy = parxy = 0L;
+		memcpy(&Line, defs.GetLine(), sizeof(LineDEF));
+		conv = 1.0e-15;	maxiter = 100;
+		dl = 0L;
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "fit function (%s)", name);
+#else
+			i = sprintf(TmpTxt, "fit function (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		if(Symbols) for(i = 0; i < nPoints; i++)
+			if(Symbols[i]) Symbols[i]->parent = this;
+		return true;
+	case FILE_WRITE:
+		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "FitFunc", Desc);
+		}
+	return false;
+}
+
+bool
+NormQuant::FileIO(int rw)
+{
+	lfPOINT *dt;
+	long cnt;
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"nData", typINT, &nData, 0L},
+		{"Data", typFPLST, &dt, &cnt},
+		{"ssRef", typTEXT, &ssRef, 0L},
+		{"x_info", typTEXT, &x_info, 0L},
+		{"y_info", typLAST | typTEXT, &y_info, 0L}};
+	int i, j, l;
+
+	if(rw == FILE_WRITE) {
+		if(nData < 4) return false;
+		l = (nData >>1) +1;				cnt = l;
+		if(!(dt = (lfPOINT *)calloc(l, sizeof(lfPOINT))))return false;
+		for(i = j = 0; i < nData; i += 2, j++) {
+			dt[j].fx = src_data[i];		dt[j].fy = src_data[i+1];
+			}
+		}
+	else {
+		dt = 0L;		cnt = 0;
+		}
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		sy = new Symbol(this, data, 0.0, 0.0, SYM_CIRCLE);
+		x_vals = y_vals = src_data = 0L;
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "normal quantiles (%s)", name);
+#else
+			i = sprintf(TmpTxt, "normal quantiles (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		if(dt && cnt > 1 && (src_data = (double*)malloc(nData*sizeof(double)))) {
+			for(i = j = 0, l = nData-1; i < nData; i += 2, j++) {
+				src_data[i] = dt[j].fx;		if(i < l) src_data[i+1] = dt[j].fy;
+				}
+			free(dt);
+			}
+		return true;
+	case FILE_WRITE:
+		ExecOutput(Notary->RegisterGO(this), "NormQuant", Desc);
+		free(dt);
+		return true;
+		}
+	return false;
+}
+
+bool
+GridLine::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"Line", typLINEDEF, &LineDef, 0L},
+		{"flags", typLAST | typDWORD, &flags, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		ncpts = 0;		cpts = 0L;	gl1 = gl2 = gl3 = 0L;	ls = 0L;
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;	mo = 0L;
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		return ExecOutput(Notary->RegisterGO(this), 
+			Id == GO_GRIDLINE ?(char*)"GridLine" : 
+			Id == GO_GRIDRADIAL? (char*)"GridRadial" : (char*)"GridLine3D", Desc);
+		}
+	return false;
+}
+
+void
+Tick::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Grid && (flags & AXIS_GRIDLINE)) Grid->RegGO(n);
+		if(label) label->RegGO(n);
+		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) Polygons[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+Tick::FileIO(int rw)
+{
+	GraphObj *gl = Grid;
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"Val", typLFLOAT, &value, 0L},
+		{"Flags", typDWORD, &flags, 0L},
+		{"Rot", typNZLFLOAT, &angle, 0L},
+		{"GridType", typINT, &gl_type, 0L},
+		{"Grid", typGOBJ, &gl, 0L},
+		{"Label", typGOBJ, &label, 0L},
+		{"Polygons", typOBJLST, &Polygons, &numPG},
+		{"Size", typLAST | typLFLOAT, &size, 0L}};
+	int i;
+
+	switch(rw) {
+	case SAVE_VARS:
+		if(Grid && (flags & AXIS_GRIDLINE)) Grid->FileIO(rw);
+		if(label) label->FileIO(rw);
+//		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) Polygons[i]->FileIO(rw);
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
+		fix = fiy = 0.0f;	ls = 0L;	Grid = 0L;	mo = 0L;
+		n_seg = s_seg = 0;	seg = 0L;	bValidTick = false;
+		size = DefSize(SIZE_AXIS_TICKS);
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		Grid = (GridLine*) gl;
+		if(Grid)Grid->parent = this;
+		if(label)label->parent = this;
+		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) Polygons[i]->parent = this;
+		return true;
+	case FILE_WRITE:
+		if(Grid && (flags & AXIS_GRIDLINE)) Grid->FileIO(rw);
+		else gl = 0L;
+		if(label) label->FileIO(rw);
+		if(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i]) Polygons[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "Tick", Desc);
+		}
+	return false;
+}
+
+void
+Axis::TickFile(char *name)
+{
+	ReadCache *ca;
+	int i, j, k, nt;
+	char line[500], item[20];
+	Tick **ttck;
+
+	if(!name) return;
+	if(!(ca = new ReadCache())) return;
+	if(! ca->Open(name)) {
+		delete ca;
+#ifdef USE_WIN_SECURE
+		sprintf_s(TmpTxt, TMP_TXT_SIZE, "Error open file \"%s\"\nfor axis ticks", name);
+#else
+		sprintf(TmpTxt, "Error open file \"%s\"\nfor axis ticks", name);
+#endif
+		ErrorBox(TmpTxt);
+		return;
+		}
+	Command(CMD_FLUSH, 0L, 0L);
+	if(!(Ticks = ((Tick**)calloc(nt = 100, sizeof(Tick*))))) return;
+	for(i = 0; ; i++) {
+		j = k = 0;
+		ca->ReadLine(line, sizeof(line));
+		if(!line[0]) break;
+		while(line[j] && line[j] < 33) j++;
+		do{ item[k] = line[j++]; }
+			while(item[k] >32 && item[k++] != '=' && k <sizeof(item) && j <sizeof(line));
+		item[k--] = 0;		if(!line[j-1])j--;
+		while(k && !(isdigit(item[k])))item[k--]=0;
+		while(line[j] && (line[j]<33 || line[j] == '"'))j++;
+		k = (int)strlen(line);
+		while(k >=j && (line[k] < 33 || line[k] == '"')) line[k--] = 0;
+		//realloc table if necessary
+		if(NumTicks >= nt) {
+			if((ttck= (Tick**)realloc(Ticks, (nt += 1000)*sizeof(Tick*))))Ticks= ttck;
+			else NumTicks--;
+			}
+		//now add tick to table
+		if(!(Ticks[NumTicks] = new Tick(this, data, atof(item),
+			line[j] ? axis->flags : axis->flags | AXIS_MINORTICK)))break;
+		Ticks[NumTicks]->Command(CMD_SETTEXT, line+j, 0L);
+		NumTicks++;
+		}
+	ca->Close();
+	if(!NumTicks && Ticks) {
+		free(Ticks);
+		NumTicks = 0;
+		}
+	delete ca;
+}
+
+void
+Axis::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		for(i = 0; Ticks && i< NumTicks; i++) if(Ticks[i]) Ticks[i]->RegGO(n);
+		if(axisLabel) axisLabel->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+Axis::FileIO(int rw)
+{
+	char *tickfile = 0L;
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"moveable", typNZINT, &moveable, 0L},
+		{"sAxLine", typLFLOAT, &sizAxLine, 0L},
+		{"sAxTick", typLFLOAT, &sizAxTick, 0L},
+		{"BrkGap", typLFLOAT, &brkgap, 0L},
+		{"BrkSymSize", typLFLOAT, &brksymsize, 0L},
+		{"BrkSym", typINT, &brksym, 0L},
+		{"sTickLabel", typLFLOAT, &sizAxTickLabel, 0L},
+		{"tick_angle", typNZLFLOAT, &tick_angle, 0L},
+		{"LbDist", typNZLFPOINT, &lbdist, 0L},
+		{"TickLbDist", typNZLFPOINT, &tlbdist, 0L}, 
+		{"tlbDef", typTXTDEF, &tlbdef, 0L},
+		{"Color", typDWORD, &colAxis, 0L},
+		{"AxisDef", typPTRAXDEF, &axis},
+		{"GridLine", typLINEDEF, &GridLine, 0L},
+		{"GridType", typINT, &gl_type, 0L},
+		{"Ticks", typOBJLST, &Ticks, (long*)&NumTicks},
+		{"Label", typGOBJ, &axisLabel, 0L},
+		{"TickFile", typTEXT, &tickfile, 0L},
+		{"ssRefTV", typTEXT, &ssMATval, 0L},
+		{"ssRefTL", typTEXT, &ssMATlbl, 0L},
+		{"ssRefMT", typTEXT, &ssMITval, 0L},
+		{"g_type", typINT, &grad_type, 0L},
+		{"g_col_0", typDWORD, &gCol_0, 0L},
+		{"g_col_1", typDWORD, &gCol_1, 0L},
+		{"g_col_2", typDWORD, &gCol_2, 0L},
+		{"gTrans", typLAST | typDWORD, &gTrans, 0L}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		sizAxLine = DefSize(SIZE_AXIS_LINE);
+		sizAxTick = DefSize(SIZE_AXIS_TICKS);
+		sizAxTickLabel = DefSize(SIZE_TICK_LABELS);
+		colAxis = parent ? parent->GetColor(COL_AXIS) : defs.Color(COL_AXIS);
+		GridLine.color = 0x00808080L;
+		GridLine.pattern = 0xf8f8f8f8L;
+		brksymsize = DefSize(SIZE_TICK_LABELS);
+		brkgap = DefSize(SIZE_AXIS_TICKS);
+		brksym = 2;
+		tlbdef.ColTxt = parent ? parent->GetColor(COL_AXIS) : defs.Color(COL_AXIS);
+		tlbdef.ColBg = parent ? parent->GetColor(COL_BG) : defs.Color(COL_AXIS);
+		tlbdef.RotBL = tlbdef.RotCHAR = 0.0f;
+		tlbdef.fSize = DefSize(SIZE_TICK_LABELS);	tlbdef.Align = TXA_VCENTER | TXA_HCENTER;
+		tlbdef.Style = TXS_NORMAL;					tlbdef.Mode = TXM_TRANSPARENT;
+		tlbdef.Font = FONT_HELVETICA;				atv = 0L;
+		tlbdef.text = 0L;		l_segs = 0L;		nl_segs = 0;
+		drawOut = scaleOut = 0L;					bModified = false;
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
+		mo = 0L;		grad_type = 1;		gTrans = 0x00000000L;
+		gCol_0 = 0x00ffffffL;	gCol_1 = 0x00ff0000L;	gCol_2 = 0x000000ffL;
+		return true;
+	case FILE_READ:
+		if(axisLabel)DeleteGO(axisLabel);
+		if(tickfile) free(tickfile);		if(ssMATval) free(ssMATval);
+		if(ssMATlbl) free(ssMATlbl);		if(ssMITval) free(ssMITval);
+		tickfile = 0L;
+		if(ExecInput(Desc) && tickfile && tickfile[0]){
+			TickFile(tickfile);
+			free(tickfile);					tickfile = 0L;
+			}
+		if(axis) axis->owner = this;
+		if(axisLabel)axisLabel->parent = this;
+		if(Ticks) for(i = 0; i < NumTicks; i++) if(Ticks[i]) Ticks[i]->parent = this;
+		return true;
+	case FILE_WRITE:
+		//do all ticks
+		for(i = 0; Ticks && i< NumTicks; i++) if(Ticks[i]) Ticks[i]->FileIO(rw);
+		if(axisLabel) axisLabel->FileIO(rw);
+		if((type & 0x04)!=4) Desc[17].type |= typLAST;
+		return ExecOutput(Notary->RegisterGO(this), "Axis", Desc);
+		}
+	return false;
+}
+
+void
+ContourPlot::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Symbols) for(i = 0; i < nSym; i++) if(Symbols[i]) Symbols[i]->RegGO(n);
+		if(Labels) for(i = 0; i < nLab; i++) if(Labels[i]) Labels[i]->RegGO(n);
+		if(zAxis) zAxis->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+ContourPlot::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"Bounds", typFRECT, &Bounds, 0L},
+		{"xBounds", typLFPOINT, &xBounds, 0L},
+		{"yBounds", typLFPOINT, &yBounds, 0L},
+		{"zBounds", typLFPOINT, &zBounds, 0L},
+		{"srz", typNZLFLOAT, &sr_zval, 0L},
+		{"flags", typDWORD, &flags, 0L},
+		{"zDef", typAXDEF, &z_axis, 0L},
+		{"zAxis", typGOBJ, &zAxis, 0L},
+		{"Values", typFPLST3D, &val, &nval},
+		{"Symbols", typOBJLST, &Symbols, &nSym},
+		{"Labels", typOBJLST, &Labels, &nLab},
+		{"ssRefX", typTEXT, &ssRefX, 0L},
+		{"ssRefY", typTEXT, &ssRefY, 0L},
+		{"ssRefZ", typLAST | typTEXT, &ssRefZ, 0L}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
+		xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
+		sr_zval = 0.0;		flags = 0L;
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "Contour Plot (%s)", name);
+#else
+			i = sprintf(TmpTxt, "Contour Plot (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		if(Symbols) for(i = 0; i < nSym; i++) if(Symbols[i]) Symbols[i]->parent=this;
+		if(Labels) for(i = 0; i < nLab; i++) if(Labels[i]) Labels[i]->parent=this;
+		if(zAxis) zAxis->parent = this;
+		return true;
+	case FILE_WRITE:
+		if(Symbols) for(i = 0; i < nSym; i++) if(Symbols[i]) Symbols[i]->FileIO(rw);
+		if(Labels) for(i = 0; i < nLab; i++) if(Labels[i]) Labels[i]->FileIO(rw);
+		if(zAxis) zAxis->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "ContourPlot", Desc);
+		}
+	return false;
+}
+
+void
+Plot3D::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(plots) for(i = 0; plots && i< nPlots; i++) if(plots[i]) plots[i]->RegGO(n);
+		if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+Plot3D::FileIO(int rw)
+{
+	fPOINT3D rot_vec, rot_ang;
+	descIO Desc[] = {
+		{"hide", typNZINT, &hidden, 0L},
+		{"xBounds", typLFPOINT, &xBounds, 0L},
+		{"yBounds", typLFPOINT, &yBounds, 0L},
+		{"zBounds", typLFPOINT, &zBounds, 0L},
+		{"Corner1", typPOINT3D, &cub1, 0L},
+		{"Corner2", typPOINT3D, &cub2, 0L},
+		{"Center", typPOINT3D, &rotC, 0L},
+		{"rot_vec", typPOINT3D, &rot_vec, 0L},
+		{"rot_ang", typPOINT3D, &rot_ang, 0L},
+		{"x_axis", typNZINT, &use_xaxis, 0L},
+		{"y_axis", typNZINT, &use_yaxis, 0L},
+		{"z_axis", typNZINT, &use_zaxis, 0L},
+		{"Axes", typOBJLST, &Axes, (long*)&nAxes},
+		{"Plots", typLAST | typOBJLST, &plots, (long*)&nPlots}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		drag = 0L;		moveable = 1;
+		//set up RotDef
+		RotDef[0] = 0.919384;		RotDef[1] = 0.389104;		RotDef[2] = -0.057709;
+		RotDef[3] = 0.327146;		RotDef[4] = 0.944974;		RotDef[5] = 1.0-RotDef[4];
+		cub1.fx = DefSize(SIZE_GRECT_LEFT) + DefSize(SIZE_DRECT_LEFT);
+		cub2.fx = DefSize(SIZE_GRECT_LEFT) + DefSize(SIZE_DRECT_RIGHT);
+		cub1.fy = DefSize(SIZE_GRECT_TOP) + DefSize(SIZE_DRECT_BOTTOM);
+		cub2.fy = DefSize(SIZE_GRECT_TOP) + DefSize(SIZE_DRECT_TOP);
+		cub1.fy += DefSize(SIZE_DRECT_TOP);	cub2.fy += DefSize(SIZE_DRECT_TOP);
+		cub1.fz = 0.0;
+		cub2.fz = DefSize(SIZE_DRECT_BOTTOM) - DefSize(SIZE_DRECT_TOP);
+		rotC.fx = (cub1.fx + cub2.fx)/2.0;		rotC.fy = (cub1.fy + cub2.fy)/2.0;
+		rotC.fz = (cub1.fz + cub2.fz)/2.0;
+		dispObs = 0L;		nmaxObs = 0;	crea_flags = 0L;		dirty = true;
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
+		xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
+		Sc_Plots = 0L;		nscp = 0;
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "3D-root (%s)", name);
+#else
+			i = sprintf(TmpTxt, "3D-root (%s)", name);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		rot_vec.fx = 0.919384;	rot_vec.fy = 0.389104;	rot_vec.fz = -0.057709;
+		rot_ang.fx = 0.327146;	rot_ang.fy = 0.944974;	rot_ang.fz = 0.055026;
+		ExecInput(Desc);
+		RotDef[0] = rot_vec.fx;	RotDef[1] = rot_vec.fy;	RotDef[2] = rot_vec.fz;
+		RotDef[3] = rot_ang.fx;	RotDef[4] = rot_ang.fy;	RotDef[5] = rot_ang.fz;
+		return true;
+	case FILE_WRITE:
+		rot_vec.fx = RotDef[0];	rot_vec.fy = RotDef[1];	rot_vec.fz = RotDef[2];
+		rot_ang.fx = RotDef[3];	rot_ang.fy = RotDef[4];	rot_ang.fz = RotDef[5];
+		//do all plots
+		for(i = 0; plots && i< nPlots; i++) if(plots[i]) plots[i]->FileIO(rw);
+		//do all axes
+		if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "Plot3D", Desc);
+		}
+	return false;
+}
+
+void
+Func3D::RegGO(void *n)
+{
+}
+
+bool
+Func3D::FileIO(int rw)
+{
+	fPOINT3D rot_vec, rot_ang;
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"xBounds", typLFPOINT, &xBounds, 0L},
+		{"yBounds", typLFPOINT, &yBounds, 0L},
+		{"zBounds", typLFPOINT, &zBounds, 0L},
+		{"Corner1", typPOINT3D, &cub1, 0L},
+		{"Corner2", typPOINT3D, &cub2, 0L},
+		{"Center", typPOINT3D, &rotC, 0L},
+		{"rot_vec", typPOINT3D, &rot_vec, 0L},
+		{"rot_ang", typPOINT3D, &rot_ang, 0L},
+		{"x_axis", typNZINT, &use_xaxis, 0L},
+		{"y_axis", typNZINT, &use_yaxis, 0L},
+		{"z_axis", typNZINT, &use_zaxis, 0L},
+		{"Axes", typOBJLST, &Axes, (long*)&nAxes},
+		{"Plots", typOBJLST, &plots, (long*)&nPlots},
+		{"x1", typNZLFLOAT, &x1, 0L},
+		{"x2", typNZLFLOAT, &x2, 0L},
+		{"xstep", typNZLFLOAT, &xstep, 0L},
+		{"z1", typNZLFLOAT, &x1, 0L},
+		{"z2", typNZLFLOAT, &x2, 0L},
+		{"zstep", typNZLFLOAT, &xstep, 0L},
+		{"Line", typLINEDEF, &Line, 0L},
+		{"Fill", typFILLDEF, &Fill, 0L},
+		{"f_xz", typTEXT, &cmdxy, 0L},
+		{"param", typLAST | typTEXT, &param, 0L}};
+	int i;
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		x1 = -20.0;		x2 = 20.0;	xstep = 2.0;
+		z1 = -20.0;		z2 = 20.0;	zstep = 2.0;
+		gda = 0L;		gob = 0L;
+		param = cmdxy = 0L;
+		Line.width = DefSize(SIZE_HAIRLINE);
+		Line.patlength = DefSize(SIZE_PATLENGTH);
+		Line.color = Line.pattern = 0x0L;
+		Fill.color = 0x00c0c0c0;
+		Fill.color2 = 0x00ffffff;
+		Fill.hatch = 0L;
+		Fill.type = FILL_LIGHT3D;
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "3D function (Plot %d)", cPlots);
+#else
+			i = sprintf(TmpTxt, "3D function (Plot %d)", cPlots);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		rot_vec.fx = 0.919384;	rot_vec.fy = 0.389104;	rot_vec.fz = -0.057709;
+		rot_ang.fx = 0.327146;	rot_ang.fy = 0.944974;	rot_ang.fz = 0.055026;
+		ExecInput(Desc);
+		RotDef[0] = rot_vec.fx;	RotDef[1] = rot_vec.fy;	RotDef[2] = rot_vec.fz;
+		RotDef[3] = rot_ang.fx;	RotDef[4] = rot_ang.fy;	RotDef[5] = rot_ang.fz;
+		return true;
+	case FILE_WRITE:
+		rot_vec.fx = RotDef[0];	rot_vec.fy = RotDef[1];	rot_vec.fz = RotDef[2];
+		rot_ang.fx = RotDef[3];	rot_ang.fy = RotDef[4];	rot_ang.fz = RotDef[5];
+		//do all plots
+		for(i = 0; plots && i< nPlots; i++) if(plots[i]) plots[i]->FileIO(rw);
+		//do all axes
+		if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->FileIO(rw);
+		ExecOutput(Notary->RegisterGO(this), "Func3D", Desc);
+		}
+	return false;
+}
+
+void
+FitFunc3D::RegGO(void *n)
+{
+}
+
+bool
+FitFunc3D::FileIO(int rw)
+{
+	fPOINT3D rot_vec, rot_ang;
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"ssXref", typTEXT, &ssXref, 0L},
+		{"ssYref", typTEXT, &ssYref, 0L},
+		{"ssZref", typTEXT, &ssZref, 0L},
+		{"xBounds", typLFPOINT, &xBounds, 0L},
+		{"yBounds", typLFPOINT, &yBounds, 0L},
+		{"zBounds", typLFPOINT, &zBounds, 0L},
+		{"Corner1", typPOINT3D, &cub1, 0L},
+		{"Corner2", typPOINT3D, &cub2, 0L},
+		{"Center", typPOINT3D, &rotC, 0L},
+		{"rot_vec", typPOINT3D, &rot_vec, 0L},
+		{"rot_ang", typPOINT3D, &rot_ang, 0L},
+		{"x_axis", typNZINT, &use_xaxis, 0L},
+		{"y_axis", typNZINT, &use_yaxis, 0L},
+		{"z_axis", typNZINT, &use_zaxis, 0L},
+		{"Axes", typOBJLST, &Axes, (long*)&nAxes},
+		{"Plots", typOBJLST, &plots, (long*)&nPlots},
+		{"x1", typNZLFLOAT, &x1, 0L},
+		{"x2", typNZLFLOAT, &x2, 0L},
+		{"xstep", typNZLFLOAT, &xstep, 0L},
+		{"z1", typNZLFLOAT, &x1, 0L},
+		{"z2", typNZLFLOAT, &x2, 0L},
+		{"zstep", typNZLFLOAT, &xstep, 0L},
+		{"maxiter", typNZINT, &maxiter, 0L},
+		{"Line", typLINEDEF, &Line, 0L},
+		{"Fill", typFILLDEF, &Fill, 0L},
+		{"f_xz", typTEXT, &cmdxy, 0L},
+		{"param", typLAST | typTEXT, &param, 0L}};
+	int i;
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		x1 = -20.0;		x2 = 20.0;	xstep = 2.0;
+		z1 = -20.0;		z2 = 20.0;	zstep = 2.0;
+		gda = 0L;		gob = 0L;
+		conv = 1.0e-15;	maxiter = 100;
+		param = cmdxy = ssXref = ssYref = ssZref = 0L;
+		Line.width = DefSize(SIZE_HAIRLINE);
+		Line.patlength = DefSize(SIZE_PATLENGTH);
+		Line.color = Line.pattern = 0x0L;
+		Fill.color = 0x00c0c0c0;
+		Fill.color2 = 0x00ffffff;
+		Fill.hatch = 0L;
+		Fill.type = FILL_LIGHT3D;
+		if(name) {
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "FitFunc3D (Plot %d)", cPlots);
+#else
+			i = sprintf(TmpTxt, "FitFunc3D (Plot %d)", cPlots);
+#endif
+			free(name);		name = (char*)memdup(TmpTxt, i+1, 0);
+			}
+		return true;
+	case FILE_READ:
+		rot_vec.fx = 0.919384;	rot_vec.fy = 0.389104;	rot_vec.fz = -0.057709;
+		rot_ang.fx = 0.327146;	rot_ang.fy = 0.944974;	rot_ang.fz = 0.055026;
+		ExecInput(Desc);
+		RotDef[0] = rot_vec.fx;	RotDef[1] = rot_vec.fy;	RotDef[2] = rot_vec.fz;
+		RotDef[3] = rot_ang.fx;	RotDef[4] = rot_ang.fy;	RotDef[5] = rot_ang.fz;
+		return true;
+	case FILE_WRITE:
+		rot_vec.fx = RotDef[0];	rot_vec.fy = RotDef[1];	rot_vec.fz = RotDef[2];
+		rot_ang.fx = RotDef[3];	rot_ang.fy = RotDef[4];	rot_ang.fz = RotDef[5];
+		//do all plots
+		for(i = 0; plots && i< nPlots; i++) if(plots[i]) plots[i]->FileIO(rw);
+		//do all axes
+		if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->FileIO(rw);
+		ExecOutput(Notary->RegisterGO(this), "FitFunc3D", Desc);
+		}
+	return false;
+}
+
+void
+Graph::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Plots) for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->RegGO(n);
+		if(Axes) for(i = 0; i< NumAxes; i++) if(Axes[i]) Axes[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+Graph::FileIO(int rw)
+{
+	int ixax, iyax;
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"Units", typNZINT, &units, 0L},
+		{"Scale", typNZLFLOAT, &scale, 0L},
+		{"GRect", typFRECT, &GRect, 0L},
+		{"DRect", typFRECT, &DRect, 0L},
+		{"Bounds", typFRECT, &Bounds, 0L},
+		{"ColFrame", typDWORD, &ColGR, 0L},
+		{"ColFrameL", typDWORD, &ColGRL, 0L},
+		{"ColRec", typDWORD, &ColDR, 0L},
+		{"ColAxis", typDWORD, &ColAX, 0L},
+		{"Xaxis", typAXDEF, &x_axis, 0L},
+		{"Yaxis", typAXDEF, &y_axis, 0L},
+		{"DefXAxis", typINT, &ixax, 0L},
+		{"DefYAxis", typINT, &iyax, 0L},
+		{"Axes", typOBJLST, &Axes, (long*)&NumAxes},
+		{"Plots", typLAST | typOBJLST, &Plots, (long*)&NumPlots}};
+	int i;
+	bool bConvert = false;
+
+	ixax = iyax = -1;
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);						units = defs.cUnits = defs.dUnits;
+		OwnDisp = bDialogOpen = false;			dirty = true;
+		GRect.Ymin = defs.GetSize(SIZE_GRECT_TOP);		GRect.Ymax = defs.GetSize(SIZE_GRECT_BOTTOM);
+		GRect.Xmin = defs.GetSize(SIZE_GRECT_LEFT);		GRect.Xmax = defs.GetSize(SIZE_GRECT_RIGHT);
+		DRect.Ymin = defs.GetSize(SIZE_DRECT_TOP);		DRect.Ymax = defs.GetSize(SIZE_DRECT_BOTTOM);
+		DRect.Xmin = defs.GetSize(SIZE_DRECT_LEFT);		DRect.Xmax = defs.GetSize(SIZE_DRECT_RIGHT);
+		ColGR = defs.Color(COL_GRECT);					ColGRL = defs.Color(COL_GRECTLINE);
+		ColDR = defs.Color(COL_DRECT);					ColBG = defs.Color(COL_BG);
+		ColAX = defs.Color(COL_AXIS);
+		x_axis.max = y_axis.max = 1.0;					x_axis.owner = y_axis.owner = (void *)this;
+		rcDim.left = rcDim.right = rcDim.top = rcDim.bottom = 0;
+		rcUpd.left = rcUpd.right = rcUpd.top = rcUpd.bottom = 0;
+		CurrGO = 0L;		Disp = 0L;			Sc_Plots = 0L;
+		AxisTempl = 0;		nscp = 0;			CurrDisp = 0L;
+		ToolMode = TM_STANDARD;	bModified = false;		zoom_def = 0L;
+		tl_pts = 0L;		tl_nPts = 0;		tickstyle = zoom_level = 0;
+		frm_g = frm_d = 0L;	PasteObj = 0L;		filename = 0L;
+		rc_mrk.left = rc_mrk.right = rc_mrk.top = rc_mrk.bottom = -1;
+		return true;
+	case FILE_READ:
+		units = 0;			//default to mm if statement mising in file
+		if((bConvert =ExecInput(Desc)) && ixax>=0 && iyax >=0 && Axes && 
+			NumAxes >= ixax+1 && NumAxes >= iyax) {
+			if(Axes[ixax]) Axes[ixax]->Command(CMD_SET_AXDEF, &x_axis, 0L);
+			if(Axes[iyax]) Axes[iyax]->Command(CMD_SET_AXDEF, &y_axis, 0L);
+			return true;
+			}
+		return bConvert;
+	case FILE_WRITE:
+		bModified = false;		if(scale == 1.0) scale = 0.0;
+		//find default axes
+		if(Axes) for(i = 0; Axes && i < NumAxes; i++) {
+			if(Axes[i] && Axes[i]->GetAxis() == &x_axis) ixax = i;
+			else if(Axes[i] && Axes[i]->GetAxis() == &y_axis) iyax = i;
+			}
+		if(Id == GO_GRAPH)RegGO(Notary);
+		//do all plots
+		if(Plots) for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->FileIO(rw);
+		//do all axes
+		if(Axes) for(i = 0; i< NumAxes; i++) if(Axes[i]) Axes[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "Graph", Desc);
+		}
+	return false;
+}
+
+void
+Page::RegGO(void *n)
+{
+	int i;
+
+	if(n) {
+		if(Plots) for(i = 0; i< NumPlots; i++) 
+			if(Plots[i] && Plots[i]->Id != GO_GRAPH) Plots[i]->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+Page::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"Units", typNZINT, &units, 0L},
+		{"GRect", typFRECT, &GRect, 0L},
+		{"Plots", typLAST | typOBJLST, &Plots, (long*)&NumPlots}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		//assume that Graph::FileIO(INIT_VARS) has been executed
+		GRect.Xmin = GRect.Ymin = 0.0;
+		GetPaper(&GRect.Xmax, &GRect.Ymax);
+		ColBG = 0x00e8e8e8L;
+		LineDef.width = 0.0;	LineDef.patlength = 1.0;
+		LineDef.color = LineDef.pattern = 0x0L;
+		FillDef.type = FILL_NONE;
+		FillDef.color = 0x00ffffffL;	//use white paper
+		FillDef.scale = 1.0;
+		FillDef.hatch = 0L;		filename =0L;
+		return true;
+	case FILE_READ:
+		Graph::FileIO(rw);
+		return true;
+	case FILE_WRITE:
+		//do all plots
+		bModified = false;
+		if(Id == GO_PAGE)RegGO(Notary);
+		if(Plots) for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "Page", Desc);
+		}
+	return false;
+}
+
+bool
+DefsRW::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"dUnits", typINT, &defs.dUnits, 0L},
+		{"cUnits", typINT, &defs.cUnits, 0L},
+		{"dtHeight", typINT, &dlgtxtheight, 0L},
+		{"ss_txt", typLFLOAT, &defs.ss_txt, 0L},
+		{"fmt_date", typTEXT, &defs.fmt_date, 0L},
+		{"fmt_datetime", typTEXT, &defs.fmt_datetime, 0L},
+		{"fmt_time", typTEXT, &defs.fmt_time, 0L},
+		{"curr_path", typTEXT, &defs.currPath, 0L},
+		{"menu_height", typINT, &defs.iMenuHeight, 0L},
+		{"File1", typTEXT, &defs.File1, 0L},
+		{"File2", typTEXT, &defs.File2, 0L},
+		{"File3", typTEXT, &defs.File3, 0L},
+		{"File4", typTEXT, &defs.File4, 0L},
+		{"File5", typTEXT, &defs.File5, 0L},
+		{"File6", typLAST | typTEXT, &defs.File6, 0L}};
+
+	switch(rw) {
+	case FILE_READ:
+		ExecInput(Desc);
+		//check for plausibility
+		if(defs.ss_txt < 0.3 || defs.ss_txt > 3.0) defs.ss_txt = 0.9;
+		if(defs.dUnits < 0 || defs.dUnits > 2) defs.dUnits = 0;
+		defs.cUnits = defs.dUnits;
+#ifdef _WINDOWS
+		if(dlgtxtheight < 5 || dlgtxtheight > 60) dlgtxtheight = 16;
+#else
+		if(dlgtxtheight < 5 || dlgtxtheight > 60) dlgtxtheight = 10;
+#endif
+		return true;
+	case FILE_WRITE:
+		Notary = new notary();
+#ifdef USE_WIN_SECURE
+		_unlink(defs.IniFile);
+#else
+		unlink(defs.IniFile);
+#endif
+		iFile = OpenOutputFile(defs.IniFile);
+		if(iFile >=0) {
+			ExecOutput(-1, "Defaults", Desc);
+			}
+		CloseOutputFile();	if(Notary) delete Notary;	Notary = 0L;
+		return true;
+		}
+	return false;
+}
diff --git a/Makefile b/Makefile
index aacf7b2..b42c857 100755
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# Makefile, Copyright 2002-2007 R.Lackner
+# Makefile, Copyright 2002-2008 R.Lackner
 # 
 #
 #    This file is part of RLPlot.
@@ -17,28 +17,43 @@
 #    along with RLPlot; if not, write to the Free Software
 #    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 #
-# This Makefile assumes that you installed Trolltechs QT in /usr/local/qt
-#   visit: http://www.trolltech.com for download and licence information
 #
 CC = g++
 X11LIBS = -lX11 -lm
 SRCDIR = ./
 
+#######
+#######   Declarations for Qt3
 QT3DIR = /usr/lib/qt-3.3
 QT3MOC = /usr/lib/qt-3.3/bin/moc
 MOC3FLAGS =
 QT3CFLAGS = "-I$(QT3DIR)/include -pipe -O2"
 QT3H = QT3_Spec.h
 QT3LIBS = "-L$(QT3DIR)/lib -L/usr/X11R6/lib -lqt-mt"
-
+#
+#   If above declarations don't work for Qt3 try the following ...
+#QT3DIR = /usr/share/qt-3
+#QT3DIR = /usr/share/qt3
+#QT3MOC = /usr/share/qt-3/bin/moc
+#QT3MOC = /usr/share/qt3/bin/moc
+
+#######
+#######   Declarations for Qt4
 QT4DIR = /usr/lib/qt4
 QT4MOC = /usr/lib/qt4/bin/moc-qt4
 MOC4FLAGS = "MOCFLAGS=-DQT_VERSION=0x040000"
 QT4CFLAGS = "-I/usr/include/Qt -pipe -O2"
 QT4H = QT_Spec.h
-QT4LIBS = "-L$(QT4DIR)/lib -L/usr/X11R6/lib -lQtCore -lQtGui -lQtNetwork"
-
-
+QT4LIBS = "-L$(QT4DIR)/lib -L/usr/X11R6/lib -lQtCore -lQtGui"
+#
+#   If above declarations don't work for Qt4 try the following ...
+#QT4DIR = /usr/share/qt4
+#QT4MOC = /usr/share/qt4/bin/moc
+#QT4MOC = /usr/lib/qt4/bin/moc
+#QT4CFLAGS = "-I/usr/include/qt4/Qt -I/usr/include/qt4 -pipe -O2"
+#QT4CFLAGS = "-I/usr/lib/qt4/include -I/usr/lib/qt4/include/Qt -pipe -O2"
+
+#######
 GENOBJ = exprlp.o rlplot.o Output.o Utils.o UtilObj.o\
  Fileio.o Export.o PlotObs.o Axes.o mfcalc.o rlp_math.o no_gui.o
 
@@ -47,6 +62,7 @@ OBJECTS = moc_QT_Spec.o QT_Spec.o Output.o Utils.o UtilObj.o\
  Export.o PlotObs.o Axes.o ODbuttons.o mfcalc.o rlp_math.o use_gui.o\
  reports.o
 
+#######
 all:
 	make Qt4
 	make exprlp
@@ -65,7 +81,9 @@ exprlp: $(GENOBJ)
 
 clean:
 	rm -f *.o *~
-	rm moc_QT_Spec.cpp
+	rm -f moc_QT_Spec.cpp
+	rm -f rlplot
+	rm -f exprlp
 
 ####### Compile
 
@@ -73,7 +91,6 @@ moc_QT_Spec.o: moc_QT_Spec.cpp $(SRCDIR)QT_Spec.h
 	$(CC) $(CFLAGS) -o $@ -c $<
 
 moc_QT_Spec.cpp: $(SRCDIR)QT_Spec.h
-#	$(QTDIR)/bin/moc-qt4 $(SRCDIR)QT_Spec.h -o moc_QT_Spec.cpp -DQT_VERSION=0x040000
 	$(MOC) $(SRCDIR)$(QTH) -o moc_QT_Spec.cpp $(MOCFLAGS)
 
 mfcalc.o: $(SRCDIR)mfcalc.cpp
@@ -88,7 +105,7 @@ rlp_math.o: $(SRCDIR)rlp_math.cpp
 exprlp.o: $(SRCDIR)exprlp.cpp
 	$(CC) $(CFLAGS) -o $@ -c $<
 
-QT_Spec.o: $(SRCDIR)QT_Spec.cpp $(SRCDIR)QT_Spec.h $(SRCDIR)rlplot.h
+QT_Spec.o: $(SRCDIR)QT_Spec.cpp $(SRCDIR)QT_Spec.h $(SRCDIR)rlplot.h $(SRCDIR)menu.h
 	$(CC) $(CFLAGS) -o $@ -c $<
 
 Output.o: $(SRCDIR)Output.cpp $(SRCDIR)rlplot.h
@@ -133,9 +150,6 @@ no_gui.o: $(SRCDIR)no_gui.cpp $(SRCDIR)rlplot.h
 use_gui.o: $(SRCDIR)use_gui.cpp $(SRCDIR)rlplot.h
 	$(CC) $(CFLAGS) -o $@ -c $<
 
-reports.o: $(SRCDIR)reports.cpp $(SRCDIR)rlplot.h
+reports.o: $(SRCDIR)reports.cpp  $(SRCDIR)TheDialog.h $(SRCDIR)rlplot.h
 	$(CC) $(CFLAGS) -o $@ -c $<
 
-
-
-
diff --git a/ODbuttons.cpp b/ODbuttons.cpp
index 5d0f3d6..5146121 100755
--- a/ODbuttons.cpp
+++ b/ODbuttons.cpp
@@ -1,1508 +1,1576 @@
-//ODbuttons.cpp, Copyright (c) 2001-2007 R.Lackner
-//Property dialogs for graphic objects
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-// Tis module contains the different graphic buttons for dialogs
-#include "rlplot.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "TheDialog.h"
-
-extern int ODtickstyle;
-extern int AxisTempl3D;
-extern Default defs;
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Common code to modify drawing order in any dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Exceute drawing order buttons as owner drwn buttons
-void OD_DrawOrder(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	LineDEF Line = {.0f, 1.0f, 0x0L, 0x0L};
-	FillDEF Fill = {FILL_NONE, 0x0080ffffL, 1.0, 0L};
-	POINT pts[5];
-	RECT hrc;
-	int i, j, x, y;
-
-	Fill.color = 0x0080ffffL;
-	switch(cmd) {
-	case OD_MBTRACK:
-		if(!data) return;
-		x = ((MouseEvent*)data)->x;		y = ((MouseEvent*)data)->y;
-		memcpy(&hrc, rec, sizeof(RECT));
-		IncrementMinMaxRect(&hrc, -6);
-		if(IsInRect(&hrc, x, y)) Fill.color = 0x00e0ffffL;
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		pts[0].x = rec->left+10;	pts[0].y = rec->bottom-3;
-		pts[1].x = rec->right-9;	pts[1].y = rec->bottom-3;
-		pts[2].x = rec->right-3;	pts[2].y = rec->bottom-9;
-		pts[3].x = rec->left+16;	pts[3].y = rec->bottom-9;
-		pts[4].x = pts[0].x;		pts[4].y = pts[0].y;
-		o->SetLine(&Line);			o->SetFill(&Fill);
-		for(i = 0; i < 5; i++){
-			o->oPolygon(pts, 5);
-			for(j = 0; j < 5; j++) {
-				pts[j].y -=4;
-				}
-			}
-		pts[0].x = pts[1].x = pts[3].x = rec->left+4;
-		pts[2].x = rec->left+1;		pts[4].x = rec->left+7;
-		switch (id) {
-		case 600:
-			pts[0].y = pts[3].y = rec->top+6;			pts[1].y = rec->bottom-3;
-			pts[2].y = pts[4].y = rec->top+9;
-			break;
-		case 601:
-			pts[0].y = pts[3].y = rec->top+12;			pts[1].y = rec->bottom-9;
-			pts[2].y = pts[4].y = rec->top+15;
-			break;
-		case 602:
-			pts[0].y = pts[3].y = rec->bottom-9;		pts[1].y = rec->top+12;
-			pts[2].y = pts[4].y = rec->bottom-12;
-			break;
-		case 603:
-			pts[0].y = pts[3].y = rec->bottom-3;		pts[1].y = rec->top+6;
-			pts[2].y = pts[4].y = rec->bottom-6;
-			break;
-			}
-		Fill.color = 0x0fL;								o->SetFill(&Fill);
-		o->oPolyline(pts, 2);							o->oPolygon(pts+2, 3);
-		o->UpdateRect(rec, false);
-		break;
-		}
-}
-
-int ExecDrawOrderButt(GraphObj *parent, GraphObj *obj, int id)
-{
-	switch(id){
-	case 600:
-		parent->Command(CMD_MOVE_TOP, obj, 0L);
-		return -1;
-	case 601:
-		parent->Command(CMD_MOVE_UP, obj, 0L);
-		return -1;
-	case 602:
-		parent->Command(CMD_MOVE_DOWN, obj, 0L);
-		return -1;
-	case 603:
-		parent->Command(CMD_MOVE_BOTTOM, obj, 0L);
-		return -1;
-		}
-	return id;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Execute line style as owner drawn buttons
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void OD_LineStyleTempl(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	LineDEF Line = {0.0, 1.0, 0x0L, 0x0L};
-	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
-	POINT *pts;
-	int i, ix, iy, np;
-
-	if(!(pts=(POINT*)malloc(sizeof(POINT)*(rec->right-rec->left)))) return;
-	switch(cmd) {
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
-		ix = (rec->left + rec->right)/2;
-		iy = (rec->top +rec->bottom)/2;
-		o->SetLine(&Line);
-		pts[0].x = pts[3].x = pts[4].x = rec->left;
-		pts[0].y = pts[1].y = pts[4].y = rec->top;
-		pts[1].x = pts[2].x = rec->right-1;
-		pts[2].y = pts[3].y = rec->bottom-1;
-		o->oPolyline(pts, 5);				Line.color = 0x00000000L;
-		o->SetLine(&Line);					o->SetFill(&Fill);
-		o->oRectangle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
-		if(cmd == OD_DRAWSELECTED){
-			Fill.color = 0x000000ffL;		o->SetFill(&Fill);
-			}
-		np = 0;
-		switch(id) {
-		case 201:
-			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-15;
-			pts[np].x = rec->right-15;		pts[np++].y = rec->top+15;
-			break;
-		case 206:
-			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-10;
-		case 202:
-			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-15;
-			pts[np].x = ix;					pts[np++].y = pts[np-1].y;
-			pts[np].x = ix;					pts[np++].y = iy;
-			pts[np].x = rec->right-15;		pts[np++].y = iy;
-			pts[np].x = pts[np-1].x;		pts[np++].y = rec->top+15;
-			if(id == 206){
-				pts[np].x = rec->right-8;	pts[np++].y = rec->top+15;
-				}
-			break;
-		case 207:
-			pts[np].x = rec->left+8;		pts[np++].y = rec->bottom-15;
-		case 203:
-			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-15;
-			pts[np].x = pts[np-1].x;		pts[np++].y = iy;
-			pts[np].x = ix;					pts[np++].y = iy;
-			pts[np].x = ix;					pts[np++].y = rec->top+15;
-			pts[np].x = rec->right-15;		pts[np++].y = pts[np-1].y;
-			if(id == 207){
-				pts[np].x = rec->right-15;	pts[np++].y = rec->top+7;
-				}
-			break;
-		case 208:
-			pts[np].x = rec->left+8;		pts[np++].y = rec->bottom-15;
-		case 204:
-			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-15;
-			pts[np].x = (pts[np-1].x + ix)>>1;	pts[np++].y = pts[np-1].y;
-			pts[np].x = pts[np-1].x;		pts[np++].y = iy;
-			pts[np].x = (rec->right-15 + ix)>>1;	pts[np++].y = iy;
-			pts[np].x = pts[np-1].x;		pts[np++].y = rec->top+15;
-			pts[np].x = rec->right-15;		pts[np++].y = pts[np-1].y;
-			if(id == 208) pts[np-1].x += 6;
-			break;
-		case 209:
-			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-10;
-		case 205:
-			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-15;
-			pts[np].x = pts[0].x;			pts[np++].y = (pts[np-1].y +iy)>>1;
-			pts[np].x = ix;					pts[np++].y = pts[np-1].y;
-			pts[np].x = ix;					pts[np++].y = (iy + rec->top+15)>>1;
-			pts[np].x = rec->right-15;		pts[np++].y = pts[np-1].y;
-			pts[np].x = pts[np-1].x;		pts[np++].y = rec->top+15;
-			if(id == 209) pts[np-1].y -= 7;
-			break;
-		case 210:
-			pts[0].x = rec->left +9;	pts[0].y = iy+4;	pts[1].x = pts[0].x+1;
-			for(i = 0; i < (rec->right - rec->left - 18); i++) {
-				pts[1].y = 4 + iy + iround(pow(20.0, 1.0+((double)-i)/30.0) * -sin(((double)i)/4.0));
-				o->oSolidLine(pts);
-				pts[0].x++;		pts[1].x++;		pts[0].y = pts[1].y;	
-				}
-			o->oCircle(rec->left+7, iy+4, rec->left+12, iy +9);
-			o->oCircle(rec->left+12, iy-10, rec->left+17, iy -5);
-			o->oCircle(rec->right-19, iy+5, rec->right-24, iy +10);
-			o->oCircle(rec->right-9, iy, rec->right-14, iy+5);
-			break;
-		case 211:
-			pts[0].y = rec->top +9;	pts[0].x = ix;	pts[1].y = pts[0].y+1;
-			for(i = 0; i < (rec->bottom - rec->top - 18); i++) {
-				pts[1].x = ix + iround(pow(20.0, 1.0+((double)-i)/50.0) * -sin(((double)i)/4.0));
-				o->oSolidLine(pts);
-				pts[0].y++;		pts[1].y++;		pts[0].x = pts[1].x;	
-				}
-			o->oCircle(ix-3, rec->top + 9, ix+2, rec->top + 14);
-			o->oCircle(rec->left+11, iy-10, rec->left+16, iy -5);
-			o->oCircle(ix+3, rec->top + 27, ix+8, rec->top + 32);
-			o->oCircle(ix-5, iy+12, ix, iy + 17);
-			break;
-		case 212:
-			for(i = 2; i < (rec->bottom - rec->top - 18); i++) {
-				pts[1].x = ix + iround(pow(20.0, 1.0+((double)-i)/50.0) * -sin(((double)i)/4.0));
-				pts[1].y = iy + iround(pow(20.0, 1.0+((double)-i)/50.0) * -cos(((double)i)/4.0));
-				if(i>2)o->oSolidLine(pts);
-				pts[0].y = pts[1].y;		pts[0].x = pts[1].x;	
-				}
-			o->oCircle(ix-5, iy-4, ix, iy+1);			o->oCircle(ix-10, iy-17, ix-5, iy-12);
-#ifdef _WINDOWS
-			o->oCircle(ix-12, iy+9, ix-7, iy+4);		o->oCircle(ix+9, iy+7, ix+4, iy+2);
-#else
-			o->oCircle(ix-12, iy+9, ix-7, iy+4);		o->oCircle(ix+8, iy+6, ix+4, iy+2);
-#endif
-			break;
-			}
-		if(np) o->oPolyline(pts, np);
-		switch(id) {
-		case 201:	case 202:	case 203:	case 204:	case 205:
-		case 206:	case 207:	case 208:	case 209:
-			o->oCircle(ix-2, iy-2, ix+3, iy+3);
-#ifdef _WINDOWS
-			o->oCircle(rec->left+13, rec->bottom-13, rec->left+18, rec->bottom-18);
-			o->oCircle(rec->right-13, rec->top+13, rec->right-18, rec->top+18);
-#else
-			o->oCircle(rec->left+13, rec->bottom-14, rec->left+18, rec->bottom-18);
-			o->oCircle(rec->right-14, rec->top+13, rec->right-18, rec->top+18);
-#endif
-			break;
-		case 210:	case 211:	case 212:
-			break;
-			}
-		o->UpdateRect(rec, false);
-		free(pts);
-		break;
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Execute error bar style as owner drawn buttons for the error bar dialog
-// and in the scatterplot dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void OD_ErrBarTempl(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	LineDEF Line = {0.0, 1.0, 0x0L, 0x0L};
-	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
-	POINT pts[6];
-	int ix, iy;
-
-	switch(cmd) {
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
-		ix = (rec->left + rec->right)/2;
-		iy = (rec->top +rec->bottom)/2;
-		o->SetLine(&Line);
-		pts[0].x = pts[3].x = pts[4].x = rec->left;
-		pts[0].y = pts[1].y = pts[4].y = rec->top;
-		pts[1].x = pts[2].x = rec->right-1;
-		pts[2].y = pts[3].y = rec->bottom-1;
-		o->oPolyline(pts, 5);
-		Line.color = 0x00000000L;
-		o->SetLine(&Line);
-		o->SetFill(&Fill);
-		o->oRectangle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
-		switch(id) {
-		case 500:
-			pts[2].x = pts[3].x = ix;
-			pts[0].x = pts[4].x = ix-5;			pts[1].x = pts[5].x = ix+5;
-			pts[0].y = pts[1].y = pts[2].y = rec->top +8;
-			pts[3].y = pts[4].y = pts[5].y = rec->bottom -8;
-			o->oSolidLine(pts);		o->oSolidLine(pts+2);		o->oSolidLine(pts+4);
-			break;
-		case 501:
-		case 502:
-			pts[2].x = pts[3].x = ix;	pts[0].x = ix-5;	pts[1].x = ix+5;
-			pts[0].y = pts[1].y = pts[2].y = (id == 502 ? rec->bottom -8 : rec->top +8);
-			pts[3].y = iy;
-			o->oSolidLine(pts);			o->oSolidLine(pts+2);
-			break;
-		case 503:
-			pts[2].y = pts[3].y = iy;
-			pts[0].y = pts[4].y = iy-5;			pts[1].y = pts[5].y = iy+5;
-			pts[0].x = pts[1].x = pts[2].x = rec->left +8;
-			pts[3].x = pts[4].x = pts[5].x = rec->right -8;
-			o->oSolidLine(pts);		o->oSolidLine(pts+2);		o->oSolidLine(pts+4);
-			break;
-		case 504:
-		case 505:
-			pts[2].y = pts[3].y = iy;	pts[0].y = iy-5;	pts[1].y = iy+5;
-			pts[0].x = pts[1].x = pts[2].x = (id == 505 ? rec->right -8 : rec->left +8);
-			pts[3].x = ix;
-			o->oSolidLine(pts);			o->oSolidLine(pts+2);
-			break;
-			}
-		o->oCircle(ix-4, iy-4, ix+4, iy+4);
-		o->UpdateRect(rec, false);
-		break;
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Execute whisker style as owner drawn buttons 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void OD_WhiskerTempl(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	LineDEF Line = {0.0, 1.0, 0x0L, 0x0L};
-	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
-	POINT pts[6];
-	int ix, iy;
-
-	switch(cmd) {
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
-		ix = (rec->left + rec->right)/2;
-		iy = (rec->top +rec->bottom)/2;
-		o->SetLine(&Line);
-		pts[0].x = pts[3].x = pts[4].x = rec->left;
-		pts[0].y = pts[1].y = pts[4].y = rec->top;
-		pts[1].x = pts[2].x = rec->right-1;
-		pts[2].y = pts[3].y = rec->bottom-1;
-		o->oPolyline(pts, 5);
-		Line.color = 0x00000000L;
-		o->SetLine(&Line);
-		o->SetFill(&Fill);
-		o->oRectangle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
-		switch(id) {
-		case 500:
-			pts[2].x = pts[3].x = ix;
-			pts[0].x = pts[4].x = ix-5;			pts[1].x = pts[5].x = ix+5;
-			pts[0].y = pts[1].y = pts[2].y = rec->top +8;
-			pts[3].y = pts[4].y = pts[5].y = rec->bottom -8;
-			o->oSolidLine(pts);		o->oSolidLine(pts+2);		o->oSolidLine(pts+4);
-			break;
-		case 501:
-			pts[0].x = pts[1].x = ix;			pts[0].y =  rec->bottom -8;
-			pts[1].y =  rec->top +8;			o->oSolidLine(pts);
-			break;
-		case 502:
-			pts[0].x = ix-5;	pts[1].x = pts[2].x = ix;	pts[3].x = ix +5;
-			pts[0].y = pts[1].y = rec->bottom-8;	pts[2].y = pts[3].y = rec->top+8;
-			o->oPolyline(pts, 4);
-			break;
-		case 503:
-			pts[0].x = ix+5;	pts[1].x = pts[2].x = ix;	pts[3].x = ix -5;
-			pts[0].y = pts[1].y = rec->bottom-8;	pts[2].y = pts[3].y = rec->top+8;
-			o->oPolyline(pts, 4);
-			break;
-			}
-		o->UpdateRect(rec, false);
-		break;
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Execute polar plot templates as owner drawn buttons
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void OD_PolarTempl(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	LineDEF Line = {.0f, 1.0f, 0x0L, 0x0L};
-	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
-	FillDEF FillR = {FILL_NONE, 0x000000ffL, 1.0, 0L};
-	FillDEF FillG = {FILL_NONE, 0x0000ff00L, 1.0, 0L};
-	FillDEF FillB = {FILL_NONE, 0x00ff0000L, 1.0, 0L};
-	FillDEF FillY = {FILL_NONE, 0x0000ffffL, 1.0, 0L};
-	TextDEF td, otd;
-	POINT pts[12];
-	int ix, iy;
-
-	switch(cmd) {
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
-		ix = (rec->left + rec->right)/2;
-		iy = (rec->top +rec->bottom)/2;
-		o->SetLine(&Line);
-		pts[0].x = pts[3].x = pts[4].x = rec->left;
-		pts[0].y = pts[1].y = pts[4].y = rec->top;
-		pts[1].x = pts[2].x = rec->right-1;
-		pts[2].y = pts[3].y = rec->bottom-1;
-		o->oPolyline(pts, 5);
-		Line.color = 0x00000000L;
-		o->SetLine(&Line);
-		o->SetFill(&Fill);
-		o->oCircle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
-		switch(id) {
-		case 200:
-		case 201:
-		case 202:
-			if(id == 201 || id == 202) {
-				pts[0].x = rec->left+13;	pts[0].y = rec->top+10;
-				pts[1].x = rec->left+15;	pts[1].y = rec->top+25;
-				pts[2].x = rec->right-19;	pts[2].y = rec->top+33;
-				pts[3].x = rec->right-11;	pts[3].y = rec->top+13;
-				o->oPolyline(pts, 4);
-				o->SetFill(&FillG);
-				}
-			else o->SetFill(&FillR);
-			if(id == 200 || id == 201) {
-				o->oCircle(rec->left+10, rec->top+7, rec->left+16, rec->top+13);
-				o->oCircle(rec->left+12, rec->top+22, rec->left+18, rec->top+28);
-				o->oCircle(rec->right-22, rec->top+30, rec->right-16, rec->top+36);
-				o->oCircle(rec->right-14, rec->top+10, rec->right-8, rec->top+16);
-				}
-			break;
-		case 203:
-			pts[0].x = rec->left+7;		pts[0].y = rec->top+13;
-			pts[1].x = rec->left+10;	pts[1].y = rec->top+30;
-			pts[2].x = rec->right-19;	pts[2].y = rec->top+33;
-			pts[3].x = rec->right-9;	pts[3].y = rec->top+11;
-			pts[4].x = ix-4;			pts[4].y =iy +3;
-			o->SetFill(&FillY);
-			o->oPolygon(pts, 5);
-			break;
-		case 204:
-			if(cmd == OD_DRAWNORMAL) FillG.color = 0x00e8e8e8L;
-			o->SetFill(&FillG);
-			o->oCircle(ix-6, rec->top+5, ix+6, iy+6);
-			memcpy(&td, &o->TxtSet, sizeof(TextDEF));
-			memcpy(&otd, &o->TxtSet, sizeof(TextDEF));
-			td.Align = TXA_HCENTER | TXA_VTOP;
-			td.Style = TXS_NORMAL;
-			td.Mode = TXM_TRANSPARENT;
-			td.fSize *= 0.8;	td.iSize = 0;
-			td.ColTxt = 0x00ff0000L;
-			o->SetTextSpec(&td);
-			o->oTextOut(ix, iy+3, "y=f(x)", 0);
-			o->SetTextSpec(&otd);
-			}
-		o->UpdateRect(rec, false);
-		break;
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Execute templates for pie-charts as owner drawn buttons
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void OD_PieTempl(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	LineDEF Line = {.0, 1.0, 0x0L, 0x0L};
-	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
-	FillDEF FillR = {FILL_NONE, 0x000000ffL, 1.0, 0L};
-	FillDEF FillG = {FILL_NONE, 0x0000ff00L, 1.0, 0L};
-	FillDEF FillB = {FILL_NONE, 0x00ff0000L, 1.0, 0L};
-	double angels1[]={90.0, 45.0, -45.0, 90.0};
-	double angels2[]={180, 157.5, 112.5, 0.0};
-	POINT pts[12];
-	int ix, iy;
-	double r, *ang = angels1;
-	segment *seg = 0L;
-	lfPOINT fc;
-
-	switch(cmd) {
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
-		ix = (rec->left + rec->right)/2;
-		iy = (rec->top +rec->bottom)/2;
-		o->SetLine(&Line);
-		pts[0].x = pts[3].x = pts[4].x = rec->left;
-		pts[0].y = pts[1].y = pts[4].y = rec->top;
-		pts[1].x = pts[2].x = rec->right-1;
-		pts[2].y = pts[3].y = rec->bottom-1;
-		o->oPolyline(pts, 5);
-		Line.color = 0x00000000L;
-		o->SetLine(&Line);
-		o->SetFill(&Fill);
-		o->oRectangle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
-		switch(id) {
-		case 401:		case 411:
-			ang = angels2;
-		case 400:		case 410:
-			fc.fx = o->fix2un((double)ix-1);		fc.fy = o->fiy2un((double)iy);
-			r = o->fix2un((double)(rec->right -rec->left))/3;
-			seg = new segment(0L, 0L, &fc, 0.0, r, ang[0], ang[1]);
-			if(seg) {
-				if(id == 410 || id == 411) seg->SetSize(SIZE_RADIUS1, r*.7);
-				seg->Command(CMD_SEG_LINE, &Line, 0L);
-				seg->Command(CMD_SEG_FILL, &FillR, 0L);
-				seg->DoPlot(o);
-				seg->SetSize(SIZE_ANGLE1, ang[1]);
-				seg->SetSize(SIZE_ANGLE2, ang[2]);
-				seg->Command(CMD_SEG_FILL, &FillG, 0L);
-				seg->DoPlot(o);
-				seg->SetSize(SIZE_ANGLE1, ang[2]);
-				seg->SetSize(SIZE_ANGLE2, ang[3]);
-				seg->Command(CMD_SEG_FILL, &FillB, 0L);
-				seg->DoPlot(o);
-				delete seg;
-				}
-			break;
-			}
-		o->UpdateRect(rec, false);
-		break;
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Show a simple graph how 3D axes are organized as owner drawn button
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void OD_AxisDesc3D(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	POINT pts[5];
-
-	switch(cmd) {
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		pts[0].x = ((rec->left + rec->right)>>1)-15;
-		pts[0].y = ((rec->bottom + rec->top)>>1)+10;
-		pts[1].x = rec->left + 15;	pts[1].y = rec->bottom-20;
-		o->oSolidLine(pts);
-		pts[2].x = pts[1].x +2;		pts[2].y = pts[1].y -7;
-		o->oSolidLine(pts + 1);
-		pts[2].x = pts[1].x +6;		pts[2].y = pts[1].y -2;
-		o->oSolidLine(pts + 1);
-		o->oTextOut(pts[1].x -2, pts[1].y -5, "z", 1);
-		pts[1].x = pts[0].x;		pts[1].y = rec->top+20;
-		o->oSolidLine(pts);
-		pts[2].x = pts[1].x -4;		pts[2].y = pts[1].y +6;
-		o->oSolidLine(pts + 1);
-		pts[2].x = pts[1].x +4;
-		o->oSolidLine(pts + 1);
-		o->oTextOut(pts[1].x + 4, pts[1].y - 18, "y", 1);
-		pts[1].x = rec->right-15;	pts[1].y = rec->bottom -22;
-		o->oSolidLine(pts);
-		pts[2].x = pts[1].x -6;		pts[2].y = pts[1].y +2;
-		o->oSolidLine(pts + 1);
-		pts[2].x = pts[1].x -4;		pts[2].y = pts[1].y -5;
-		o->oSolidLine(pts + 1);
-		o->oTextOut(pts[1].x +9, pts[1].y -4, "x", 1);
-		o->UpdateRect(rec, false);
-		break;
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Execute axis breaks symbols as owner drawn button
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void OD_BreakTempl(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	LineDEF Line = {0.0, 1.0, 0x0L, 0x0L};
-	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
-	POINT pts[15];
-	int i, ix, iy;
-
-	switch(cmd) {
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
-		ix = (rec->left + rec->right)>>1;
-		iy = (rec->top +rec->bottom)>>1;
-		o->SetLine(&Line);
-		pts[0].x = pts[3].x = pts[4].x = rec->left;
-		pts[0].y = pts[1].y = pts[4].y = rec->top;
-		pts[1].x = pts[2].x = rec->right-1;
-		pts[2].y = pts[3].y = rec->bottom-1;
-		o->oPolyline(pts, 5);
-		Line.color = 0x00000000L;
-		o->SetLine(&Line);
-		o->SetFill(&Fill);
-		o->oRectangle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
-		pts[0].x = pts[1].x = ix;
-		pts[0].y = rec->top +5;		pts[1].y = iy-3;
-		o->oSolidLine(pts);
-		pts[0].y = rec->bottom -7;		pts[1].y = iy+3;
-		o->oSolidLine(pts);
-		switch(id) {
-		case 402:
-			pts[0].x = ix-7;		pts[1].x = ix+7;
-			pts[0].y = iy;			pts[1].y = iy-6;
-			o->oSolidLine(pts);
-			pts[0].y += 6;			pts[1].y += 6;
-			o->oSolidLine(pts);
-			break;
-		case 403:
-			pts[0].x = ix-7;		pts[1].x = ix+7;
-			pts[0].y = iy-3;		pts[1].y = iy-3;
-			o->oSolidLine(pts);
-			pts[1].y += 6;			o->oSolidLine(pts);
-			pts[0].y += 6;			o->oSolidLine(pts);
-			break;
-		case 404:
-			for(i = 0; i < 15; i++) {
-				pts[i].x = ix +i -7;
-				pts[i].y = iy - 3 + (int)(sin((double)i*0.41887902)*2.5);
-				}
-			o->oPolyline(pts, 15);
-			for(i = 0; i < 15; i++) pts[i].y += 6;
-			o->oPolyline(pts, 15);
-			break;
-			}
-		o->UpdateRect(rec, false);
-		break;
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Execute plot selection templates as owner drawn button
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void UtilBarDraw(POINT *pts, int x, int y1, int y2, anyOutput *o)
-{
-	pts[1].x = pts[0].x = pts[4].x = x;
-	pts[0].y = y1;				pts[1].y = y2;
-	pts[2].y = pts[1].y-1;		pts[2].x = pts[3].x = pts[0].x-3;	
-	pts[3].y = pts[0].y-1;		pts[4].y = pts[0].y;
-	o->oPolygon(pts, 5);
-	pts[2].x += 5;				pts[3].x += 5;
-	o->oPolygon(pts, 5);
-	pts[1].x -= 3;				pts[1].y = pts[0].y-1;
-	pts[2].x = pts[0].x-2;		pts[2].y = pts[0].y-2;
-	o->oPolygon(pts, 5);
-}
-
-void OD_PlotTempl(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	LineDEF Line = {.0, 1.0, 0x0L, 0x0L};
-	LineDEF rLine = {.0, 1.0, 0x00000080L, 0x0L};
-	LineDEF bLine = {.0, 1.0, 0x00e00000L, 0x0L};
-	LineDEF gLine = {.0, 1.0, 0x0000e000L, 0x0L};
-	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
-	FillDEF FillR = {FILL_NONE, 0x000000ffL, 1.0, 0L};
-	FillDEF FillG = {FILL_NONE, 0x0000ff00L, 1.0, 0L};
-	FillDEF FillB = {FILL_NONE, 0x00ff0000L, 1.0, 0L};
-	FillDEF FillY = {FILL_NONE, 0x0000ffffL, 1.0, 0L};
-	TextDEF td, otd;
-	POINT pts[12];
-	int i, j, ix, iy;
-	double r;
-	segment *seg = 0L;
-	lfPOINT fc;
-
-	switch(cmd) {
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		ix = (rec->left + rec->right)>>1;
-		iy = (rec->top +rec->bottom)>>1;
-		switch(id) {
-		case 560:	case 561:	case 562:	case 563:	case 564:		//3D axes
-		case 565:	case 566:	case 567:
-			OD_AxisTempl3D(cmd, par, rec, o, data, 410+AxisTempl3D);
-			break;
-		default:
-			Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
-			Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
-			o->SetLine(&Line);
-			pts[0].x = pts[3].x = pts[4].x = rec->left;
-			pts[0].y = pts[1].y = pts[4].y = rec->top;
-			pts[1].x = pts[2].x = rec->right-1;
-			pts[2].y = pts[3].y = rec->bottom-1;
-			o->oPolyline(pts, 5);
-			Line.color = 0x00000000L;
-			o->SetLine(&Line);
-			o->SetFill(&Fill);
-			o->oRectangle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
-			break;
-			}
-		switch(id) {
-		case 500:
-		case 501:
-			fc.fx = o->fix2un((double)ix-1);		fc.fy = o->fiy2un((double)iy);
-			r = o->fix2un((double)(rec->right -rec->left))/3;
-			seg = new segment(0L, 0L, &fc, 0.0, r, 90.0, 45.0);
-			if(seg) {
-				if(id == 501) seg->SetSize(SIZE_RADIUS1, r*.7f);
-				seg->Command(CMD_SEG_LINE, &Line, 0L);
-				seg->Command(CMD_SEG_FILL, &FillR, 0L);
-				seg->DoPlot(o);
-				seg->SetSize(SIZE_ANGLE1, 45.0f);
-				seg->SetSize(SIZE_ANGLE2, -45.0f);
-				seg->Command(CMD_SEG_FILL, &FillG, 0L);
-				seg->DoPlot(o);
-				seg->SetSize(SIZE_ANGLE1, -45.0f);
-				seg->SetSize(SIZE_ANGLE2, 90.0f);
-				seg->Command(CMD_SEG_FILL, &FillB, 0L);
-				seg->DoPlot(o);
-				delete seg;
-				}
-			break;
-		case 502:
-			pts[0].x = rec->right-8;	pts[0].y = rec->top+8;
-			pts[1].x = ix+4;			pts[1].y = iy+2;
-			pts[2].x = ix+8;			pts[2].y = rec->bottom-11;
-			pts[3].x = ix;				pts[3].y = iy+7;
-			pts[4].x = ix-6;			pts[4].y = iy+13;
-			pts[5].x = ix-5;			pts[5].y = iy+5;
-			pts[6].x = rec->left+10;	pts[6].y = iy+2;
-			pts[7].x = ix-3;			pts[7].y = iy-1;
-			pts[8].x = ix-3;			pts[8].y = iy-10;
-			pts[9].x = ix+2;			pts[9].y = iy-4;
-			pts[10].x = pts[0].x;		pts[10].y = pts[0].y;
-			o->SetFill(&FillY);
-			o->oPolygon(pts, 11);
-			break;
-		case 503:
-			o->SetFill(&FillR);
-			o->oRectangle(rec->left+8, rec->top+30, rec->left+12, rec->bottom-3);
-			o->oRectangle(rec->left+15, rec->bottom-10, rec->left+19, rec->bottom-3);
-			o->oRectangle(rec->left+22, rec->top+10, rec->left+26, rec->bottom-3);
-			o->oRectangle(rec->left+29, rec->bottom-35, rec->left+33, rec->bottom-3);
-			o->oRectangle(rec->left+36, rec->top+30, rec->left+40, rec->bottom-3);
-			break;
-		case 504:	case 507:
-			o->SetFill(&FillR);
-			o->oRectangle(rec->left+9, rec->top+30, rec->left+13, rec->bottom-3);
-			o->oRectangle(rec->right-20, id == 507 ? rec->top + 25 : rec->top+10, rec->right-16, rec->bottom-3);
-			o->SetFill(&FillG);
-			o->oRectangle(rec->left+13, rec->top+25, rec->left+17, rec->bottom-3);
-			o->oRectangle(rec->right-16, rec->top+15, rec->right-12, rec->bottom-3);
-			o->SetFill(&FillB);
-			o->oRectangle(rec->left+17, rec->top+35, rec->left+21, rec->bottom-3);
-			o->oRectangle(rec->right-12, rec->top+20, rec->right-8, rec->bottom-3);
-			if(id == 507) {
-				o->SetLine(&Line);
-				pts[0].x = pts[1].x = rec->left+11;			pts[0].y = rec->top+20;
-				pts[1].y = rec->top+40;						o->oSolidLine(pts);
-				pts[0].x = pts[1].x = rec->left+15;			pts[0].y = rec->top+15;
-				pts[1].y = rec->top+35;						o->oSolidLine(pts);
-				pts[0].x = pts[1].x = rec->left+19;			pts[0].y = rec->top+30;
-				pts[1].y = rec->top+40;						o->oSolidLine(pts);
-				pts[0].x = pts[1].x = rec->right-18;		pts[0].y = rec->top+15;
-				pts[1].y = rec->top+35;						o->oSolidLine(pts);
-				pts[0].x = pts[1].x = rec->right-14;		pts[0].y = rec->top+10;
-				pts[1].y = rec->top+20;						o->oSolidLine(pts);
-				pts[0].x = pts[1].x = rec->right-10;		pts[0].y = rec->top+10;
-				pts[1].y = rec->top+30;						o->oSolidLine(pts);
-				}
-			break;
-		case 505:
-			o->SetFill(&FillY);
-			o->oRectangle(rec->left+15, rec->bottom-10, rec->left+19, rec->bottom-3);
-			o->oRectangle(rec->left+22, rec->top+15, rec->left+26, rec->bottom-3);
-			o->oRectangle(rec->left+29, rec->bottom-30, rec->left+33, rec->bottom-3);
-			o->oRectangle(rec->left+36, rec->bottom-12, rec->left+40, rec->bottom-3);
-			o->SetLine(&bLine);
-			pts[0].x = rec->left +9;	pts[0].y = rec->bottom-5;	pts[1].x = pts[0].x+1;
-			for(i = 0; i < (rec->right - rec->left - 18); i++) {
-				r = ((double)(i+rec->left-ix+7))/8.0;
-				pts[1].y = rec->bottom - iround(exp(-r*r)*35.0+5.0);
-				o->oSolidLine(pts);
-				pts[0].x++;		pts[1].x++;		pts[0].y = pts[1].y;	
-				}
-			break;
-		case 506:
-			o->SetLine(&bLine);
-			pts[0].x = rec->left +9;	pts[0].y = rec->bottom-5;	pts[1].x = pts[0].x+1;
-			for(i = 0; i < (rec->right - rec->left - 18); i++) {
-				r = ((double)(i+rec->left-ix+7))/8.0;
-				pts[1].y = pts[0].y - iround(exp(-r*r)*1.0+5.0);
-				pts[1].y = rec->bottom - iround(errf(r)*16.0+26.0);
-				if (i) o->oSolidLine(pts);
-				pts[0].x++;		pts[1].x++;		pts[0].y = pts[1].y;	
-				}
-			o->SetLine(&rLine);
-			pts[0].x = ix-2;	pts[1].x = pts[0].x+1;
-			for(i = 0; i < (rec->right - ix-5); i++) {
-				r = ((double)(i-9))/4.0;
-				pts[1].y = rec->bottom - iround(exp(-r*r)*17.0+7.0);
-				if (i) o->oSolidLine(pts);
-				pts[0].x++;		pts[1].x++;		pts[0].y = pts[1].y;	
-				}
-			o->SetFill(&FillG);			o->SetLine(&Line);
-			o->oCircle(ix, iy, ix+5, iy+5);
-			o->oCircle(ix-5, iy+8, ix, iy+13);
-			o->oCircle(ix+5, iy-10, ix+10, iy-5);
-			break;
-		case 520:		case 521:
-			if(id == 521) {
-				pts[0].x = rec->left+13;	pts[0].y = rec->bottom-12;
-				pts[1].x = rec->left+20;	pts[1].y = rec->top+18;
-				pts[2].x = rec->right-19;	pts[2].y = rec->top+33;
-				pts[3].x = rec->right-11;	pts[3].y = rec->top+13;
-				o->oPolyline(pts, 4);
-				o->SetFill(&FillG);
-				}
-			else o->SetFill(&FillR);
-			o->oCircle(rec->left+10, rec->bottom-15, rec->left+16, rec->bottom-9);
-			o->oCircle(rec->left+17, rec->top+15, rec->left+23, rec->top+21);
-			o->oCircle(rec->right-22, rec->top+30, rec->right-16, rec->top+36);
-			o->oCircle(rec->right-14, rec->top+10, rec->right-8, rec->top+16);
-			break;
-		case 522:
-			o->SetFill(&FillR);
-			o->oRectangle(rec->left+3, rec->top+8, rec->left+16, rec->top+16);
-			o->SetFill(&FillG);
-			o->oRectangle(rec->left+3, iy-4, rec->right-16, iy+4);
-			o->SetFill(&FillB);
-			o->oRectangle(rec->left+3, rec->bottom-8, rec->left+26, rec->bottom-16);
-			break;
-		case 523:
-			o->SetFill(&FillR);
-			o->oRectangle(rec->left+8, rec->top+30, rec->left+16, rec->bottom-3);
-			o->SetFill(&FillG);
-			o->oRectangle(ix-4, rec->top+10, ix+4, rec->bottom-3);
-			o->SetFill(&FillB);
-			o->oRectangle(rec->right-8, rec->top+20, rec->right-16, rec->bottom-3);
-			break;
-		case 524:
-			o->SetFill(&FillG);
-			o->oCircle(rec->left+10, rec->bottom-15, rec->left+16, rec->bottom-9);
-			o->oCircle(ix-9, iy-18, ix+9, iy);
-			o->oCircle(rec->right-7, rec->top+30, rec->right-17, rec->top+40);
-			break;
-		case 525:
-			pts[0].x = pts[1].x = rec->left +12;
-			pts[0].y = rec->top+20;		pts[1].y = rec->top+40;
-			o->oPolyline(pts,2);
-			pts[0].x = pts[1].x = rec->right-12;
-			o->oPolyline(pts,2);
-			pts[0].x = pts[1].x = ix;
-			pts[0].y = rec->top+10;		pts[1].y = rec->top+35;
-			o->oPolyline(pts,2);
-			o->SetFill(&FillY);
-			o->oRectangle(rec->left+8, rec->top+25, rec->left+16, rec->top+35);
-			o->oRectangle(ix-4, rec->top+13, ix+4, rec->top+28);
-			o->oRectangle(rec->right-8, rec->top+30, rec->right-16, rec->top+35);
-			break;
-		case 526:
-			pts[0].x = rec->left+13;	pts[0].y = rec->bottom-12;
-			pts[1].x = rec->right-11;	pts[1].y = rec->top+8;
-			o->oSolidLine(pts);
-			o->SetFill(&FillB);
-			o->oCircle(rec->left+10, rec->bottom-21, rec->left+16, rec->bottom-15);
-			o->oCircle(rec->left+17, rec->top+20, rec->left+23, rec->top+26);
-			o->oCircle(rec->right-22, rec->top+25, rec->right-16, rec->top+31);
-			o->oCircle(rec->right-14, rec->top+15, rec->right-8, rec->top+21);
-			o->oCircle(rec->right-22, rec->top+9, rec->right-16, rec->top+15);
-			break;
-		case 527:
-			o->oCircle(rec->left+8, rec->top+8, rec->right-8, rec->bottom-8);
-			o->oCircle(rec->left+16, rec->top+16, rec->right-16, rec->bottom-16);
-			pts[0].x = rec->left+6;		pts[0].y = iy;
-			pts[1].x = rec->right-6;	pts[1].y = iy;
-			o->oSolidLine(pts);
-			pts[0].x = ix;				pts[0].y = rec->bottom-6;
-			pts[1].x = ix;				pts[1].y = rec->top+6;
-			o->oSolidLine(pts);			o->SetFill(&FillR);
-			o->oCircle(rec->left+13, rec->top+13, rec->left+19, rec->top+19);
-			o->oCircle(ix-7, iy+1, ix-1, iy+7);
-			o->oCircle(rec->right-19, rec->bottom-19, rec->right-13, rec->bottom-13);
-			o->oCircle(rec->right-19, rec->top+13, rec->right-13, rec->top+19);
-			break;
-		case 528:
-			o->SetFill(&FillY);
-			o->oRectangle(rec->left+8, iy-2, ix-2, iy+2);
-			o->oRectangle(ix-2, iy-15, ix+2, iy+15);
-			o->oRectangle(ix+2, iy-11, ix+6, iy+11);
-			o->oRectangle(ix+6, iy-5, rec->right-8, iy+5);
-			break;
-		case 529:
-			o->SetLine(&rLine);
-			pts[0].x = rec->left +9;	pts[0].y = iy;	pts[1].x = pts[0].x+1;
-			for(i = 0; i < (rec->right - rec->left - 18); i++) {
-				pts[1].y = iy-4 + iround(pow(20.0, 1.0+((double)-i)/30.0) * -sin(((double)i)));
-				o->oSolidLine(pts);
-				pts[0].x++;		pts[1].x++;		pts[0].y = pts[1].y;	
-				}
-			memcpy(&td, &o->TxtSet, sizeof(TextDEF));
-			memcpy(&otd, &o->TxtSet, sizeof(TextDEF));
-			td.Align = TXA_HCENTER | TXA_VTOP;
-			td.Style = TXS_NORMAL;
-			td.Mode = TXM_TRANSPARENT;
-			td.ColTxt = 0x00c00000L;
-			o->SetTextSpec(&td);
-			o->oTextOut(ix, iy+4, "y=f(x)", 0);
-			o->SetTextSpec(&otd);
-			break;
-		case 530:
-			o->SetLine(&rLine);
-			pts[0].x = rec->left +9;	pts[0].y = iy+13;	pts[1].x = pts[0].x+1;
-			for(i = 0; i < (rec->right - rec->left - 18); i++) {
-				pts[1].y = iy+12 + iround(-log10(((double)i)/.4 + 1.0)*15.0);
-				o->oSolidLine(pts);
-				pts[0].x++;		pts[1].x++;		pts[0].y = pts[1].y;	
-				}
-			o->SetLine(&Line);			o->SetFill(&FillG);
-			o->oCircle(rec->left+8, rec->bottom-15, rec->left+14, rec->bottom-9);
-			o->oCircle(rec->left+11, iy-1, rec->left+17, iy+5);
-			o->oCircle(rec->left+17, rec->top+12, rec->left+23, rec->top+18);
-			o->oCircle(rec->right-22, rec->top+8, rec->right-16, rec->top+14);
-			o->oCircle(rec->right-14, rec->top+10, rec->right-8, rec->top+16);
-			memcpy(&td, &o->TxtSet, sizeof(TextDEF));
-			memcpy(&otd, &o->TxtSet, sizeof(TextDEF));
-			td.Align = TXA_HLEFT | TXA_VCENTER;
-			td.Style = TXS_BOLD;	td.Mode = TXM_TRANSPARENT;
-			td.fSize = defs.GetSize(SIZE_TEXT)*1.75;			td.iSize = 0;
-			td.ColTxt = cmd == OD_DRAWSELECTED ? 0x0000f0f0L : 0x00c00000;
-			o->SetTextSpec(&td);
-			o->oTextOut(ix-2, iy+3, "?", 0);
-			o->SetTextSpec(&otd);
-			break;
-		case 531:
-			o->SetLine(&rLine);
-			pts[0].x = rec->left +9;	pts[0].y = iy;	
-			pts[1].x = ix;				pts[1].y = rec->top + 9;
-			pts[2].x = rec->right -9;	pts[2].y = iy;
-			o->oPolyline(pts, 3, 0L);
-			o->SetLine(&gLine);
-			pts[0].y -= 15;	pts[1].y = iy;	pts[2].y -=7;
-			o->oPolyline(pts, 3, 0L);
-			o->SetLine(&bLine);
-			pts[0].y += 9;	pts[1].y += 10;	pts[2].y = pts[1].y;
-			o->oPolyline(pts, 3, 0L);
-			break;
-		case 532:
-			pts[0].x = rec->left +13;	pts[0].y = rec->top+8;	
-			pts[1].x = rec->left +13;	pts[1].y = iy;
-			o->oSolidLine(pts);
-			pts[0].x -= 3;		pts[1].x += 3;
-			pts[0].y = pts[1].y = rec->top+8;
-			o->oSolidLine(pts);
-			pts[0].y = pts[1].y = iy;
-			o->oSolidLine(pts);
-			pts[0].x = ix;	pts[0].y = iy-8;	
-			pts[1].x = ix;	pts[1].y = rec->bottom-13;
-			o->oSolidLine(pts);
-			pts[0].x -= 3;		pts[1].x += 3;
-			pts[0].y = pts[1].y = iy-8;
-			o->oSolidLine(pts);
-			pts[0].y = pts[1].y = rec->bottom-13;
-			o->oSolidLine(pts);
-			pts[0].x = rec->right -13;	pts[0].y = rec->top+10;	
-			pts[1].x = rec->right -13;	pts[1].y = iy-6;
-			o->oSolidLine(pts);
-			pts[0].x -= 3;		pts[1].x += 3;
-			pts[0].y = pts[1].y = rec->top+10;
-			o->oSolidLine(pts);
-			pts[0].y = pts[1].y = iy-6;
-			o->oSolidLine(pts);
-			pts[0].x = rec->left+13;	pts[1].x = ix;		pts[2].x = rec->right-13;
-			pts[0].y = (rec->top+8+iy)>>1;
-			pts[1].y = (rec->bottom-13 + iy -8)>>1;
-			pts[2].y = (rec->top+10+iy-6)>>1;
-			o->oPolyline(pts, 3, 0L);
-			o->SetFill(&FillY);
-			o->oCircle(pts[0].x-3, pts[0].y-3, pts[0].x+3, pts[0].y+3);
-			o->oCircle(pts[1].x-3, pts[1].y-3, pts[1].x+3, pts[1].y+3);
-			o->oCircle(pts[2].x-3, pts[2].y-3, pts[2].x+3, pts[2].y+3);
-			break;
-		case 540:
-			o->SetFill(&FillR);
-			o->oRectangle(rec->left+8, rec->bottom-8, rec->left+16, rec->bottom-3);
-			o->oRectangle(ix-4, rec->bottom-18, ix+4, rec->bottom-3);
-			o->oRectangle(rec->right-8, rec->bottom-12, rec->right-16, rec->bottom-3);
-			o->SetFill(&FillG);
-			o->oRectangle(rec->left+8, rec->bottom-13, rec->left+16, rec->bottom-8);
-			o->oRectangle(ix-4, rec->bottom-28, ix+4, rec->bottom-18);
-			o->oRectangle(rec->right-8, rec->bottom-22, rec->right-16, rec->bottom-12);
-			o->SetFill(&FillB);
-			o->oRectangle(rec->left+8, rec->bottom-18, rec->left+16, rec->bottom-13);
-			o->oRectangle(ix-4, rec->bottom-38, ix+4, rec->bottom-28);
-			o->oRectangle(rec->right-8, rec->bottom-27, rec->right-16, rec->bottom-22);
-			break;
-		case 541:
-			o->SetFill(&FillR);
-			pts[0].x = pts[1].x = pts[5].x = rec->left+8;
-			pts[0].y = pts[4].y =pts[5].y = rec->bottom-4;
-			pts[1].y = iy+5;	pts[2].x = ix;	pts[2].y = rec->bottom-5;
-			pts[3].x = pts[4].x = rec->right-8;
-			pts[3].y = rec->bottom-12;
-			o->oPolygon(pts, 6);
-			o->SetFill(&FillY);
-			for(i = 1; i < 6; i++) {
-				pts[i-1].x = pts[i].x;	pts[i-1].y = pts[i].y;
-				}
-			pts[5].x = pts[0].x;	pts[5].y = pts[0].y;
-			pts[4].x = pts[1].x;	pts[4].y = pts[1].y-8;
-			pts[3].y = rec->bottom-20;
-			o->oPolygon(pts, 6);
-			o->SetFill(&FillG);
-			pts[1].y = pts[4].y;	pts[2].y = pts[3].y;
-			pts[4].y -= 12;	pts[3].y -= 3;
-			o->oPolygon(pts, 6);
-			break;
-		case 542:
-			Line.color = 0x00ff0000L;
-			o->SetLine(&Line);
-			pts[0].x = rec->left+6;		pts[0].y = rec->bottom-6;
-			pts[1].x = rec->left+10;	pts[1].y = rec->bottom-6;
-			pts[2].x = rec->left+12;	pts[2].y = iy + 8;
-			pts[3].x = rec->left+14;	pts[3].y = rec->bottom-6;
-			pts[4].x = rec->right-24;	pts[4].y = rec->bottom-6;
-			pts[5].x = rec->right-22;	pts[5].y = iy + 4;
-			pts[6].x = rec->right-20;	pts[6].y = rec->bottom-6;
-			pts[7].x = rec->right-16;	pts[7].y = rec->bottom-6;
-			for(i = 0; i < 4; i++){
-				o->oPolyline(pts, 8);
-				for(j = 0; j < 8; j++) {
-					pts[j].x += 4;	pts[j].y -= 4;
-					}
-				pts[2].y -= 4;	pts[5].y++;
-				}
-			break;
-		case 543:
-			OD_AxisTempl3D(cmd, par, rec, o, data, 411);
-			o->SetFill(&FillR);
-			UtilBarDraw(pts, ix-5, iy-2, rec->bottom-12, o);
-			UtilBarDraw(pts, ix, iy+3, rec->bottom-11, o);
-			UtilBarDraw(pts, ix+5, iy-5, rec->bottom-10, o);
-			UtilBarDraw(pts, ix+10, iy-4, rec->bottom-9, o);
-			o->SetFill(&FillG);
-			UtilBarDraw(pts, ix-10, iy+2, rec->bottom-9, o);
-			UtilBarDraw(pts, ix-5, iy+10, rec->bottom-8, o);
-			UtilBarDraw(pts, ix, iy+8, rec->bottom-7, o);
-			UtilBarDraw(pts, ix+5, iy, rec->bottom-6, o);
-			break;
-		case 544:
-			OD_AxisTempl3D(cmd, par, rec, o, data, 411);
-			for(i = 0; i < 6; i++){
-				switch(i) {
-				case 0:
-					o->SetFill(&FillY);
-					pts[0].x = ix-5;		pts[0].y = iy+1;
-					pts[1].x = ix;			pts[1].y = iy-10;
-					break;
-				case 1:
-					pts[0].x = pts[1].x;	pts[0].y = pts[1].y;
-					pts[1].x = ix+5;			pts[1].y = iy -6;
-					break;
-				case 2:
-					pts[0].x = pts[1].x;	pts[0].y = pts[1].y;
-					pts[1].x = ix+10;		pts[1].y = iy +4;
-					break;
-				case 3:
-					o->SetFill(&FillR);
-					pts[0].x = ix-10;		pts[0].y = iy-10;
-					pts[1].x = ix-5;		pts[1].y = iy+4;
-					break;
-				case 4:
-					pts[0].x = pts[1].x;	pts[0].y = pts[1].y;
-					pts[1].x = ix;			pts[1].y = iy +9;
-					break;
-				case 5:
-					pts[0].x = pts[1].x;	pts[0].y = pts[1].y;
-					pts[1].x = ix+10;		pts[1].y = iy +12;
-					break;
-					}
-				pts[2].x = pts[1].x -3;	pts[2].y = pts[1].y + 2;
-				pts[3].x = pts[0].x -3;	pts[3].y = pts[0].y + 2;
-				pts[4].x = pts[0].x;	pts[4].y = pts[0].y;
-				o->oPolygon(pts, 5);
-				}
-			break;
-		case 560:
-			o->SetFill(&FillY);
-#ifdef _WINDOWS
-			o->oCircle(rec->right-13, rec->top+7, rec->right-19, rec->top+13);
-			o->oCircle(rec->right-11, iy-3, rec->right-17, iy+3);
-			o->oCircle(ix, iy+3, ix+6, iy+9);
-			o->oCircle(rec->left+12, iy+3, rec->left+18, iy+9);
-			o->oCircle(ix, rec->bottom-6, ix+6, rec->bottom-12);
-#else
-			o->oCircle(rec->right-15, rec->top+7, rec->right-19, rec->top+13);
-			o->oCircle(rec->right-13, iy-3, rec->right-17, iy+3);
-			o->oCircle(ix, iy+3, ix+6, iy+9);
-			o->oCircle(rec->left+12, iy+3, rec->left+18, iy+9);
-			o->oCircle(ix, rec->bottom-8, ix+6, rec->bottom-12);
-#endif
-			break;
-		case 561:
-			o->SetFill(&FillG);
-			UtilBarDraw(pts, ix+1, rec->top +12, rec->bottom-10, o);
-			UtilBarDraw(pts, rec->left+16, iy+8, rec->bottom-8, o);
-			UtilBarDraw(pts, rec->right-12, iy+12, rec->bottom-8, o);
-			break;
-		case 562:
-			o->SetLine(&bLine);
-			pts[0].x = rec->left+20;	pts[0].y = rec->bottom-10;
-			pts[1].x = rec->right-10;	pts[1].y = rec->bottom-16;
-			o->oSolidLine(pts);
-			pts[0].x = pts[1].x;		pts[0].y = pts[1].y;
-			pts[1].x -= 8;				pts[1].y -= 12;
-			o->oSolidLine(pts);
-			pts[0].x = pts[1].x;		pts[0].y = pts[1].y;
-			pts[1].x -= 18;				pts[1].y += 3;
-			o->oSolidLine(pts);
-			pts[0].x = pts[1].x;		pts[0].y = pts[1].y;
-			pts[1].x += 15;				pts[1].y += 4;
-			o->oSolidLine(pts);
-			break;
-		case 563:
-			o->SetFill(&FillG);
-			o->oCircle(rec->left+12, rec->bottom-19, rec->left+18, rec->bottom-13);
-			o->oCircle(ix, iy-10, ix+14, iy+4);
-			o->oCircle(ix, rec->top+34, ix+10, rec->top+44);
-			break;
-		case 564:
-			o->SetFill(&FillY);
-			pts[0].x = rec->left+10;	pts[0].y = rec->bottom-14;
-			pts[1].x = pts[0].x;		pts[1].y = rec->top +16;
-			pts[2].x = ix-6;			pts[2].y = iy+4;
-			pts[3].x = pts[2].x;		pts[3].y = rec->bottom -10;
-			pts[4].x = pts[0].x;		pts[4].y = pts[0].y;
-			o->oPolygon(pts, 5, 0L);
-			pts[0].x = pts[2].x;		pts[0].y = pts[2].y;
-			pts[1].x = pts[3].x;		pts[1].y = pts[3].y;
-			pts[2].x = ix + 3;			pts[2].y = pts[1].y -2;
-			pts[3].x = pts[2].x;		pts[3].y = pts[0].y +1;
-			pts[4].x = pts[0].x;		pts[4].y = pts[0].y;
-			o->oPolygon(pts, 5, 0L);
-			pts[0].x = pts[2].x;		pts[0].y = pts[2].y;
-			pts[1].x = pts[3].x;		pts[1].y = pts[3].y;
-			pts[2].x = ix + 10;			pts[2].y = pts[1].y +9;
-			pts[3].x = pts[2].x;		pts[3].y = pts[0].y +3;
-			pts[4].x = pts[0].x;		pts[4].y = pts[0].y;
-			o->oPolygon(pts, 5, 0L);
-			break;
-		case 565:	case 566:
-			o->SetLine(&rLine);
-			pts[0].x = ix-16;			pts[0].y = iy-2;
-			pts[1].x = ix+4;			pts[1].y = iy+6;
-			for(i = 0; i < 4; i++) {
-				o->oSolidLine(pts);
-				pts[0].x += 4;			pts[1].x += 4;
-				pts[0].y -= 4;			pts[1].y -= 4;
-				}
-			pts[0].x = ix+4;			pts[0].y = iy+6;
-			pts[1].x -= 2;				pts[1].y += 4;
-			for(i = 0; i < 5; i++) {
-				o->oSolidLine(pts);
-				pts[0].x -= 5;			pts[1].x -= 5;
-				pts[0].y -= 2;			pts[1].y -= 2;
-				}
-			memcpy(&td, &o->TxtSet, sizeof(TextDEF));
-			memcpy(&otd, &o->TxtSet, sizeof(TextDEF));
-			td.Align = TXA_HCENTER | TXA_VTOP;
-			td.Mode = TXM_TRANSPARENT;
-			if(id == 565) {
-				td.Style = TXS_NORMAL;			td.ColTxt = 0x00c00000L;
-				o->SetTextSpec(&td);			o->oTextOut(ix, iy+4, "f(x,z)", 0);
-				}
-			else {
-				td.Style = TXS_BOLD;
-				td.fSize = defs.GetSize(SIZE_TEXT)*1.75;			td.iSize = 0;
-				td.ColTxt = cmd == OD_DRAWSELECTED ? 0x0000cb00L : 0x00cb00c0L;
-				o->SetTextSpec(&td);
-				o->oTextOut(ix-10, iy-6, "?", 0);
-				}
-			o->SetTextSpec(&otd);
-			break;
-		case 567:
-			o->SetLine(&bLine);			if(cmd == OD_DRAWSELECTED) o->SetFill(&FillG);
-			pts[0].x = ix-10;			pts[0].y = iy+4;
-			pts[1].x = ix-6;			pts[1].y = iy+10;
-			pts[2].x = ix-16;			pts[2].y = iy+8;
-			o->oPolygon(pts, 3, 0L);
-			pts[2].x = ix+2;		pts[2].y = iy-10;		o->oPolygon(pts, 3, 0L);
-			pts[0].x = ix+10;		pts[0].y = iy-12;		o->oPolygon(pts, 3, 0L);
-			pts[2].x = ix+2;		pts[2].y = iy+14;		o->oPolygon(pts, 3, 0L);
-			pts[1].x = ix+12;		pts[1].y = iy+4;		o->oPolygon(pts, 3, 0L);
-			pts[0].x = ix+16;		pts[0].y = iy+12;		o->oPolygon(pts, 3, 0L);
-			break;
-			}
-		o->UpdateRect(rec, false);
-		break;
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Execute axis templates as owner drawn buttons
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void OD_AxisTempl(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	LineDEF Line = {0.0, 1.0, 0x0L, 0x0L};
-	LineDEF Grid = {0.0, 1.0, 0x00c0c0c0, 0x0L};
-	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
-	POINT pts[5];
-	int i, ty, tx, sx;
-
-	switch(cmd) {
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
-		o->SetLine(&Line);
-		pts[0].x = pts[3].x = pts[4].x = rec->left;
-		pts[0].y = pts[1].y = pts[4].y = rec->top;
-		pts[1].x = pts[2].x = rec->right-1;
-		pts[2].y = pts[3].y = rec->bottom-1;
-		o->oPolyline(pts, 5);
-		Line.color = 0x00000000L;
-		o->SetLine(&Line);
-		o->SetFill(&Fill);
-		o->oRectangle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
-		tx = ty = 0;
-		switch(id) {
-		case 310:
-			o->oRectangle(rec->left+10, rec->top+6, rec->right-6, rec->bottom-10);
-			ty = rec->bottom -10;		tx = rec->left+8;
-			break;
-		case 311:
-			o->oRectangle(rec->left+10, rec->top+6, rec->right-6, rec->bottom-10);
-			pts[0].x = rec->left+10;		pts[1].x = rec->right-6;
-			pts[0].y = pts[1].y = rec->bottom-32;
-			o->oSolidLine(pts);
-			pts[0].y = rec->top+6;		pts[1].y = rec->bottom-10;
-			pts[0].x = pts[1].x = rec->left + 32;
-			o->oSolidLine(pts);
-			ty = rec->bottom -31;		tx = rec->left+30;
-			break;
-		case 312:
-			pts[0].x = rec->left+10;		pts[1].x = rec->right-6;
-			pts[0].y = pts[1].y = rec->bottom-11;
-			o->oSolidLine(pts);
-			pts[0].y = rec->top+6;		pts[1].y = rec->bottom-10;
-			pts[0].x = pts[1].x = rec->left + 10;
-			o->oSolidLine(pts);
-			ty = rec->bottom -10;		tx = rec->left+8;
-			break;
-		case 313:
-			pts[0].x = rec->left+10;		pts[1].x = rec->right-6;
-			pts[0].y = pts[1].y = rec->top + 9;
-			o->oSolidLine(pts);
-			pts[0].y = rec->top+10;			pts[1].y = rec->bottom-10;
-			pts[0].x = pts[1].x = rec->left + 10;
-			o->oSolidLine(pts);
-			ty = rec->top+7;			tx = rec->left+8;
-			break;
-		case 314:
-			pts[0].x = rec->left+10;		pts[1].x = rec->right-6;
-			pts[0].y = pts[1].y = rec->bottom-11;
-			o->oSolidLine(pts);
-			pts[0].y = rec->top+6;		pts[1].y = rec->bottom-10;
-			pts[0].x = pts[1].x = rec->left + 27;
-			o->oSolidLine(pts);
-			ty = rec->bottom -10;		tx = rec->left+25;
-			break;
-			}
-		if(ODtickstyle & 0x300) {
-			o->SetLine(&Grid);
-			pts[0].y = rec->top+7;		pts[1].y = rec->bottom-11;
-			if(id == 313) pts[0].y +=3;
-			if(ODtickstyle & 0x100) for(i = rec->left+16; i < rec->right-6; i+=12) {
-				pts[0].x = pts[1].x = i;
-				o->oSolidLine(pts);
-				}
-			pts[0].x = rec->left+11;	pts[1].x = rec->right-7;
-			if(ODtickstyle & 0x200) 
-				for(i = rec->bottom- (id == 313 ? 11 : 17); i > rec->top+6; i -=12) {
-				pts[0].y = pts[1].y = i;
-				o->oSolidLine(pts);
-				}
-			o->SetLine(&Line);
-			}
-		if(tx != ty) {
-			sx = 2;
-			switch(ODtickstyle & 0x03){
-			case 1:
-				if(id == 313) ty += 3;
-				else ty -= 3;
-				tx += 3;	
-				break;
-			case 2:
-				if(id == 313) ty += 1;
-				else ty -= 2;
-				tx += 1;
-#ifdef _WINDOWS
-				sx = 3;
-#endif
-				break;
-			default:
-				break;
-				}
-			pts[0].y = ty;	pts[1].y = ty+sx;
-			for(i = rec->left+10; i < rec->right-6; i+=6) {
-				pts[0].x = pts[1].x = i;
-				o->oSolidLine(pts);
-				}
-			pts[0].x = tx;	pts[1].x = tx+sx;
-			for(i = rec->bottom-11; i > rec->top+6; i -=6) {
-				pts[0].y = pts[1].y = i;
-				o->oSolidLine(pts);
-				}
-			}
-		o->UpdateRect(rec, false);
-		break;
-		}
-
-}
-
-void OD_AxisTempl3D(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	LineDEF Line = {0.0, 1.0, 0x0L, 0x0L};
-	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
-	POINT pts[5];
-	int x, y;
-
-	switch(cmd) {
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
-		o->SetLine(&Line);
-		pts[0].x = pts[3].x = pts[4].x = rec->left;
-		pts[0].y = pts[1].y = pts[4].y = rec->top;
-		pts[1].x = pts[2].x = rec->right-1;
-		pts[2].y = pts[3].y = rec->bottom-1;
-		o->oPolyline(pts, 5);
-		Line.color = 0x00000000L;
-		o->SetLine(&Line);						o->SetFill(&Fill);
-		o->oRectangle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00c0c0c0L : 0x0L;
-		o->SetLine(&Line);						o->SetFill(&Fill);
-		switch(id) {
-		case 410:	case 411:
-			pts[0].x = rec->left+20;			pts[0].y = rec->bottom-14;
-			pts[1].x = rec->left+10;			pts[1].y = rec->bottom-10;
-			o->oSolidLine(pts);
-			pts[1].x = rec->right-10;			pts[1].y = pts[0].y + 3;
-			o->oSolidLine(pts);
-			pts[1].x = pts[0].x;				pts[1].y = rec->top+8;
-			o->oSolidLine(pts);
-			if(id == 411) {
-				pts[0].x = rec->left+10;		pts[0].y = rec->top+12;
-				o->oSolidLine(pts);
-				pts[0].x = rec->right-10;		pts[0].y = pts[1].y + 3;
-				o->oSolidLine(pts);
-				pts[1].x = pts[0].x;			pts[1].y = rec->bottom-11;
-				o->oSolidLine(pts);
-				pts[0].x = rec->right-20;		pts[0].y = rec->bottom-7;
-				o->oSolidLine(pts);
-				pts[1].x = rec->left+10;		pts[1].y = rec->bottom-10;
-				o->oSolidLine(pts);
-				pts[0].x = rec->left+10;		pts[0].y = rec->top+12;
-				o->oSolidLine(pts);
-				}
-			break;
-		case 412:
-			x = (rec->right+rec->left)>>1;		y = (rec->top+rec->bottom)>>1;
-			pts[0].x = rec->left+14;			pts[0].y = y+4;
-			pts[1].x = rec->right-14;			pts[1].y = y-2;
-			o->oSolidLine(pts);
-			pts[1].y += 6;	pts[0].y -= 6;	pts[1].x +=4;	pts[0].x -=4;
-			o->oSolidLine(pts);
-			pts[0].x = pts[1].x = x;	pts[0].y = y-15;	pts[1].y = y+15;
-			o->oSolidLine(pts);
-			}
-		o->UpdateRect(rec, false);
-		Line.color = 0x00000000L;		o->SetLine(&Line);
-		break;
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// 2D Plot: Execute axis templates for new axis as owner drawn buttons
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void OD_NewAxisTempl(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	LineDEF Line = {0.0, 1.0, 0x0L, 0x0L};
-	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
-	POINT pts[5];
-	int i, ix, iy, step, d1, d2;
-
-	switch(cmd) {
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
-		o->SetLine(&Line);
-		ix = (rec->right + rec->left)>>1;
-		iy = (rec->bottom + rec->top)>>1;
-		pts[0].x = pts[3].x = pts[4].x = rec->left;
-		pts[0].y = pts[1].y = pts[4].y = rec->top;
-		pts[1].x = pts[2].x = rec->right-1;
-		pts[2].y = pts[3].y = rec->bottom-1;
-		o->oPolyline(pts, 5);
-		Line.color = 0x00000000L;
-		o->SetLine(&Line);
-		o->SetFill(&Fill);
-		o->oRectangle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
-		d1 = d2 = 0;
-		switch(id) {
-		case 201:			d1 = -6;				d2 = -2;
-		case 202:			d2 -= 14;
-		case 203:			d1 += 6;				d2 += 3;
-		case 204:
-			d1 = d1 + ix -3;		d2 = d2 + ix +4;
-			pts[0].x = pts[1].x = ix;
-			pts[0].y = rec->top +9;	pts[1].y = rec->bottom -9;
-			step = ((pts[1].y - pts[0].y)/5)+1;
-			o->oSolidLine(pts);
-			pts[0].x = d1;		pts[1].x = ix;
-			for(i = rec->top +11; i <= rec->bottom-9; i += step) {
-				pts[0].y = pts[1].y = i;
-				o->oSolidLine(pts);
-				o->oRectangle(d2, i-1, d2+4, i+1);
-				}
-			break;
-		case 205:			d1 = 6;					d2 = 3;
-		case 206:			d2 += 10;
-		case 207:			d1 -= 6;				d2 -= 3;
-		case 208:
-			d1 = d1 + iy +3;		d2 = d2 + iy - 4;
-			pts[0].y = pts[1].y = iy;
-			pts[0].x = rec->left +9;	pts[1].x = rec->right -9;
-			step = ((pts[1].x - pts[0].x)/4)+1;
-			o->oSolidLine(pts);
-			pts[0].y = d1;		pts[1].y = iy;
-			for(i = rec->left +11; i <= rec->right-9; i += step) {
-				pts[0].x = pts[1].x = i;
-				o->oSolidLine(pts);
-				o->oRectangle(i-1, d2, i+2, d2+2);
-				}
-			break;
-			}
-		o->UpdateRect(rec, false);
-		break;
-		}
-}
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// 3D Plot: Execute axis templates for new axis as owner drawn buttons
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void OD_NewAxisTempl3D(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	LineDEF Line = {0.0, 1.0, 0x0L, 0x0L};
-	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
-	POINT spts[24];
-	int i, ix, iy, edge;
-	DWORD col1, col2;
-
-	switch(cmd) {
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
-		col1 = cmd == OD_DRAWSELECTED ? 0x008080c0L : 0x00c8c8c8L;
-		col2 = cmd == OD_DRAWSELECTED ? 0x0000ff00L : 0x0000c000L;
-		o->SetLine(&Line);
-		ix = (rec->right + rec->left)>>1;		iy = (rec->bottom + rec->top)>>1;
-		spts[0].x = spts[3].x = spts[4].x = rec->left;
-		spts[0].y = spts[1].y = spts[4].y = rec->top;
-		spts[1].x = spts[2].x = rec->right-1;	spts[2].y = spts[3].y = rec->bottom-1;
-		o->oPolyline(spts, 5);					Line.color = 0x00000000L;
-		o->SetLine(&Line);						o->SetFill(&Fill);
-		o->oRectangle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
-		spts[0].x = spts[6].x = spts[8].x = spts[13].x = spts[18].x = spts[19].x = rec->left+20;		
-		spts[0].y = spts[13].y = spts[18].y = rec->bottom-14;
-		spts[1].x = spts[2].x = spts[4].x = spts[5].x = spts[7].x = spts[21].x = rec->left+10;
-		spts[1].y = spts[2].y = spts[4].y = rec->bottom-10;
-		spts[3].x = spts[15].x = spts[16].x = spts[17].x = spts[20].x = spts[22].x = rec->right-20;				
-		spts[3].y = spts[15].y = spts[16].y = spts[2].y + 3;
-		spts[5].y = spts[7].y = spts[21].y = rec->top+12;
-		spts[6].y = spts[8].y = spts[19].y = rec->top+8;
-		spts[9].x = spts[10].x = spts[11].x = spts[12].x = spts[14].x = spts[23].x = rec->right-10;
-		spts[9].y = spts[10].y = spts[23].y = spts[8].y+3;
-		spts[11].y = spts[12].y = spts[14].y = spts[0].y+3;
-		spts[17].y = spts[20].y = spts[22].y = spts[7].y+3;
-		switch(id) {
-			case 201:		edge = 4;			break;
-			case 202:		edge = 16;			break;
-			case 203:		edge = 10;			break;
-			case 204:		edge = 18;			break;
-			case 205:		edge = 2;			break;
-			case 206:		edge = 14;			break;
-			case 207:		edge = 12;			break;
-			case 208:		edge = 0;			break;
-			case 209:		edge = 20;			break;
-			case 210:		edge = 22;			break;
-			case 211:		edge = 8;			break;
-			case 212:		edge = 6;			break;
-			default:		edge = -1;			break;
-			}
-		Line.color = col1;				o->SetLine(&Line);
-		if(true)for(i = 0; i < 24; i+= 2) {
-			if(i == edge){
-				Line.color = col2;		o->SetLine(&Line);
-				o->oSolidLine(spts+i);
-				Line.color = col1;		o->SetLine(&Line);
-				}
-			else o->oSolidLine(spts+i);
-			}
-		o->UpdateRect(rec, false);
-		break;
-		}
-}
+//ODbuttons.cpp, Copyright (c) 2001-2008 R.Lackner
+//Property dialogs for graphic objects
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// Tis module contains the different graphic buttons for dialogs
+#include "rlplot.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "TheDialog.h"
+
+extern int ODtickstyle;
+extern int AxisTempl3D;
+extern Default defs;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// utility draw base rectangle for OD-button
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static void OD_BaseRect(anyOutput *o, int cmd, RECT *rec)
+{
+	LineDEF Line = {0.0, 1.0, 0x0L, 0x0L};
+	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
+	POINT pts[5];
+
+	Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
+	Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
+	o->SetLine(&Line);
+	pts[0].x = pts[3].x = pts[4].x = rec->left;
+	pts[0].y = pts[1].y = pts[4].y = rec->top;
+	pts[1].x = pts[2].x = rec->right-1;
+	pts[2].y = pts[3].y = rec->bottom-1;
+	o->oPolyline(pts, 5);				Line.color = 0x00000000L;
+	o->SetLine(&Line);				o->SetFill(&Fill);
+	o->oRectangle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Common code to modify drawing order in any dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Exceute drawing order buttons as owner drwn buttons
+void OD_DrawOrder(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	LineDEF Line = {.0f, 1.0f, 0x0L, 0x0L};
+	FillDEF Fill = {FILL_NONE, 0x0080ffffL, 1.0, 0L};
+	POINT pts[5];
+	RECT hrc;
+	int i, j, x, y;
+
+	Fill.color = 0x0080ffffL;
+	switch(cmd) {
+	case OD_MBTRACK:
+		if(!data) return;
+		x = ((MouseEvent*)data)->x;		y = ((MouseEvent*)data)->y;
+		memcpy(&hrc, rec, sizeof(RECT));
+		IncrementMinMaxRect(&hrc, -6);
+		if(IsInRect(&hrc, x, y)) Fill.color = 0x00e0ffffL;
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		pts[0].x = rec->left+10;	pts[0].y = rec->bottom-3;
+		pts[1].x = rec->right-9;	pts[1].y = rec->bottom-3;
+		pts[2].x = rec->right-3;	pts[2].y = rec->bottom-9;
+		pts[3].x = rec->left+16;	pts[3].y = rec->bottom-9;
+		pts[4].x = pts[0].x;		pts[4].y = pts[0].y;
+		o->SetLine(&Line);			o->SetFill(&Fill);
+		for(i = 0; i < 5; i++){
+			o->oPolygon(pts, 5);
+			for(j = 0; j < 5; j++) {
+				pts[j].y -=4;
+				}
+			}
+		pts[0].x = pts[1].x = pts[3].x = rec->left+4;
+		pts[2].x = rec->left+1;		pts[4].x = rec->left+7;
+		switch (id) {
+		case 600:
+			pts[0].y = pts[3].y = rec->top+6;			pts[1].y = rec->bottom-3;
+			pts[2].y = pts[4].y = rec->top+9;
+			break;
+		case 601:
+			pts[0].y = pts[3].y = rec->top+12;			pts[1].y = rec->bottom-9;
+			pts[2].y = pts[4].y = rec->top+15;
+			break;
+		case 602:
+			pts[0].y = pts[3].y = rec->bottom-9;		pts[1].y = rec->top+12;
+			pts[2].y = pts[4].y = rec->bottom-12;
+			break;
+		case 603:
+			pts[0].y = pts[3].y = rec->bottom-3;		pts[1].y = rec->top+6;
+			pts[2].y = pts[4].y = rec->bottom-6;
+			break;
+			}
+		Fill.color = 0x0fL;								o->SetFill(&Fill);
+		o->oPolyline(pts, 2);							o->oPolygon(pts+2, 3);
+		o->UpdateRect(rec, false);
+		break;
+		}
+}
+
+int ExecDrawOrderButt(GraphObj *parent, GraphObj *obj, int id)
+{
+	switch(id){
+	case 600:
+		parent->Command(CMD_MOVE_TOP, obj, 0L);
+		return -1;
+	case 601:
+		parent->Command(CMD_MOVE_UP, obj, 0L);
+		return -1;
+	case 602:
+		parent->Command(CMD_MOVE_DOWN, obj, 0L);
+		return -1;
+	case 603:
+		parent->Command(CMD_MOVE_BOTTOM, obj, 0L);
+		return -1;
+		}
+	return id;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Execute polygon style as owner drawn buttons
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void OD_PolygonStyleTempl(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
+	POINT *pts;
+	int ix= (rec->left + rec->right)>>1, iy = (rec->top +rec->bottom)>>1, np;
+	long cp;
+	POINT tmppts[] = {{rec->left+15, iy}, {rec->left+15, iy-5}, {ix-5, rec->top+14},
+		{ix, rec->top+15}, {ix+10, rec->top+17}, {rec->right-7, rec->bottom-22},
+		{rec->right-15, rec->bottom-15}, {rec->right-23, rec->bottom-8},
+		{rec->left+15, iy+5}, {rec->left+15, iy}};
+	
+
+	if(!(pts=(POINT*)malloc(2*sizeof(POINT)*(rec->right-rec->left)))) return;
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		OD_BaseRect(o, cmd, rec);
+		if(cmd == OD_DRAWSELECTED){
+			Fill.color = 0x0000ffffL;		o->SetFill(&Fill);
+			}
+
+		np = 0;
+		switch(id) {
+		case 201:
+			pts[np].x = rec->left+15;		pts[np++].y = iy;
+			pts[np].x = ix;				pts[np++].y = rec->top+15;
+			pts[np].x = rec->right-15;		pts[np++].y = rec->bottom-15;
+			break;
+		case 202:
+			pts[np].x = rec->left+15;		pts[np++].y = iy;
+			pts[np].x = ix;				pts[np++].y = iy;
+			pts[np].x = ix;				pts[np++].y = rec->top+15;
+			pts[np].x = rec->right-15;		pts[np++].y = rec->top+15;
+			pts[np].x = rec->right-15;		pts[np++].y = rec->bottom-15;
+			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-15;
+			break;
+		case 203:
+			pts[np].x = rec->left+15;		pts[np++].y = iy;
+			pts[np].x = rec->left+15;		pts[np++].y = rec->top+15;
+			pts[np].x = ix;				pts[np++].y = rec->top+15;
+			pts[np].x = ix;				pts[np++].y = rec->bottom-15;
+			pts[np].x = rec->right-15;		pts[np++].y = rec->bottom-15;
+			pts[np].x = rec->right-15;		pts[np++].y = iy;
+			break;
+		case 213:
+			cp = 0;
+			DrawBezier(&cp, pts, tmppts[0], tmppts[1], tmppts[2], tmppts[3], 0);
+			DrawBezier(&cp, pts, tmppts[3], tmppts[4], tmppts[5], tmppts[6], 0);
+			DrawBezier(&cp, pts, tmppts[6], tmppts[7], tmppts[8], tmppts[9], 0);
+			np = (int)cp;
+			break;
+			}
+		if(np) o->oPolygon(pts, np);
+		switch(id) {
+		case 201:	case 202:	case 203:	case 213:
+			Fill.color = (cmd == OD_DRAWSELECTED) ? 0x000000ffL : 0x00ffffffL;
+			o->SetFill(&Fill);
+			o->oCircle(ix-2, rec->top+13, ix+3, rec->top+18);
+			o->oCircle(rec->left+13, iy-2, rec->left+18, iy+3);
+			o->oCircle(rec->right-14, rec->bottom-14, rec->right-17, rec->bottom-17);
+			break;
+			}
+		o->UpdateRect(rec, false);
+		free(pts);
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Execute line style as owner drawn buttons
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void OD_LineStyleTempl(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	LineDEF Line = {0.0, 1.0, 0x0L, 0x0L};
+	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
+	POINT *pts;
+	int i, ix, iy, np;
+
+	if(!(pts=(POINT*)malloc(2*sizeof(POINT)*(rec->right-rec->left)))) return;
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
+		ix = (rec->left + rec->right)/2;
+		iy = (rec->top +rec->bottom)/2;
+		o->SetLine(&Line);
+		pts[0].x = pts[3].x = pts[4].x = rec->left;
+		pts[0].y = pts[1].y = pts[4].y = rec->top;
+		pts[1].x = pts[2].x = rec->right-1;
+		pts[2].y = pts[3].y = rec->bottom-1;
+		o->oPolyline(pts, 5);				Line.color = 0x00000000L;
+		o->SetLine(&Line);					o->SetFill(&Fill);
+		o->oRectangle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
+		if(cmd == OD_DRAWSELECTED){
+			Fill.color = 0x000000ffL;		o->SetFill(&Fill);
+			}
+		np = 0;
+		switch(id) {
+		case 201:
+			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-15;
+			pts[np].x = rec->right-15;		pts[np++].y = rec->top+15;
+			break;
+		case 206:
+			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-10;
+		case 202:
+			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-15;
+			pts[np].x = ix;					pts[np++].y = pts[np-1].y;
+			pts[np].x = ix;					pts[np++].y = iy;
+			pts[np].x = rec->right-15;		pts[np++].y = iy;
+			pts[np].x = pts[np-1].x;		pts[np++].y = rec->top+15;
+			if(id == 206){
+				pts[np].x = rec->right-8;	pts[np++].y = rec->top+15;
+				}
+			break;
+		case 207:
+			pts[np].x = rec->left+8;		pts[np++].y = rec->bottom-15;
+		case 203:
+			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-15;
+			pts[np].x = pts[np-1].x;		pts[np++].y = iy;
+			pts[np].x = ix;					pts[np++].y = iy;
+			pts[np].x = ix;					pts[np++].y = rec->top+15;
+			pts[np].x = rec->right-15;		pts[np++].y = pts[np-1].y;
+			if(id == 207){
+				pts[np].x = rec->right-15;	pts[np++].y = rec->top+7;
+				}
+			break;
+		case 208:
+			pts[np].x = rec->left+8;		pts[np++].y = rec->bottom-15;
+		case 204:
+			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-15;
+			pts[np].x = (pts[np-1].x + ix)>>1;	pts[np++].y = pts[np-1].y;
+			pts[np].x = pts[np-1].x;		pts[np++].y = iy;
+			pts[np].x = (rec->right-15 + ix)>>1;	pts[np++].y = iy;
+			pts[np].x = pts[np-1].x;		pts[np++].y = rec->top+15;
+			pts[np].x = rec->right-15;		pts[np++].y = pts[np-1].y;
+			if(id == 208) pts[np-1].x += 6;
+			break;
+		case 209:
+			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-10;
+		case 205:
+			pts[np].x = rec->left+15;		pts[np++].y = rec->bottom-15;
+			pts[np].x = pts[0].x;			pts[np++].y = (pts[np-1].y +iy)>>1;
+			pts[np].x = ix;					pts[np++].y = pts[np-1].y;
+			pts[np].x = ix;					pts[np++].y = (iy + rec->top+15)>>1;
+			pts[np].x = rec->right-15;		pts[np++].y = pts[np-1].y;
+			pts[np].x = pts[np-1].x;		pts[np++].y = rec->top+15;
+			if(id == 209) pts[np-1].y -= 7;
+			break;
+		case 210:
+			pts[0].x = rec->left +9;	pts[0].y = iy+4;	pts[1].x = pts[0].x+1;
+			for(i = 0; i < (rec->right - rec->left - 18); i++) {
+				pts[1].y = 4 + iy + iround(pow(20.0, 1.0+((double)-i)/30.0) * -sin(((double)i)/4.0));
+				o->oSolidLine(pts);
+				pts[0].x++;		pts[1].x++;		pts[0].y = pts[1].y;	
+				}
+			o->oCircle(rec->left+7, iy+4, rec->left+12, iy +9);
+			o->oCircle(rec->left+12, iy-10, rec->left+17, iy -5);
+			o->oCircle(rec->right-19, iy+5, rec->right-24, iy +10);
+			o->oCircle(rec->right-9, iy, rec->right-14, iy+5);
+			break;
+		case 211:
+			pts[0].y = rec->top +9;	pts[0].x = ix;	pts[1].y = pts[0].y+1;
+			for(i = 0; i < (rec->bottom - rec->top - 18); i++) {
+				pts[1].x = ix + iround(pow(20.0, 1.0+((double)-i)/50.0) * -sin(((double)i)/4.0));
+				o->oSolidLine(pts);
+				pts[0].y++;		pts[1].y++;		pts[0].x = pts[1].x;	
+				}
+			o->oCircle(ix-3, rec->top + 9, ix+2, rec->top + 14);
+			o->oCircle(rec->left+11, iy-10, rec->left+16, iy -5);
+			o->oCircle(ix+3, rec->top + 27, ix+8, rec->top + 32);
+			o->oCircle(ix-5, iy+12, ix, iy + 17);
+			break;
+		case 212:
+			for(i = 2; i < (rec->bottom - rec->top - 18); i++) {
+				pts[1].x = ix + iround(pow(20.0, 1.0+((double)-i)/50.0) * -sin(((double)i)/4.0));
+				pts[1].y = iy + iround(pow(20.0, 1.0+((double)-i)/50.0) * -cos(((double)i)/4.0));
+				if(i>2)o->oSolidLine(pts);
+				pts[0].y = pts[1].y;		pts[0].x = pts[1].x;	
+				}
+			o->oCircle(ix-5, iy-4, ix, iy+1);			o->oCircle(ix-10, iy-17, ix-5, iy-12);
+#ifdef _WINDOWS
+			o->oCircle(ix-12, iy+9, ix-7, iy+4);		o->oCircle(ix+9, iy+7, ix+4, iy+2);
+#else
+			o->oCircle(ix-12, iy+9, ix-7, iy+4);		o->oCircle(ix+8, iy+6, ix+4, iy+2);
+#endif
+			break;
+			}
+		if(np) o->oPolyline(pts, np);
+		switch(id) {
+		case 201:	case 202:	case 203:	case 204:	case 205:
+		case 206:	case 207:	case 208:	case 209:
+			o->oCircle(ix-2, iy-2, ix+3, iy+3);
+#ifdef _WINDOWS
+			o->oCircle(rec->left+13, rec->bottom-13, rec->left+18, rec->bottom-18);
+			o->oCircle(rec->right-13, rec->top+13, rec->right-18, rec->top+18);
+#else
+			o->oCircle(rec->left+13, rec->bottom-14, rec->left+18, rec->bottom-18);
+			o->oCircle(rec->right-14, rec->top+13, rec->right-18, rec->top+18);
+#endif
+			break;
+		case 210:	case 211:	case 212:
+			break;
+			}
+		o->UpdateRect(rec, false);
+		free(pts);
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Execute bubble style as owner drawn buttons
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void OD_BubbleTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id)
+{
+	FillDEF Fill = {FILL_NONE, 0x00c0ffffL, 1.0, 0L};
+	POINT pts[3];
+	int ix, iy;
+
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		ix = (rec->left + rec->right)/2;
+		iy = (rec->top +rec->bottom)/2;
+		pts[0].x = ix-10;	pts[2].x = ix+10;	pts[1].x = ix;
+		OD_BaseRect(o, cmd, rec);
+		if(cmd == OD_DRAWSELECTED) o->SetFill(&Fill);
+		switch(id) {
+		case 109:	case 201:
+			o->oCircle(ix-10, iy-10, ix+10, iy+10);
+			break;
+		case 110:	case 202:
+			o->oRectangle(ix-10, iy-10, ix+10, iy+10);
+			break;
+		case 111:	case 203:
+			pts[0].y = pts[2].y = iy + 9;		pts[1].y = iy - 11;
+			o->oPolygon(pts, 3);
+			break;
+		case 112:	case 204:
+			pts[0].y = pts[2].y = iy - 9;		pts[1].y = iy + 11;
+			o->oPolygon(pts, 3);
+			break;
+			}
+		o->UpdateRect(rec, false);
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Execute error bar style as owner drawn buttons for the error bar dialog
+// and in the scatterplot dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void OD_ErrBarTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id)
+{
+	POINT pts[6];
+	int ix, iy;
+
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		ix = (rec->left + rec->right)/2;
+		iy = (rec->top +rec->bottom)/2;
+		OD_BaseRect(o, cmd, rec);
+		switch(id) {
+		case 500:
+			pts[2].x = pts[3].x = ix;
+			pts[0].x = pts[4].x = ix-5;			pts[1].x = pts[5].x = ix+5;
+			pts[0].y = pts[1].y = pts[2].y = rec->top +8;
+			pts[3].y = pts[4].y = pts[5].y = rec->bottom -8;
+			o->oSolidLine(pts);		o->oSolidLine(pts+2);		o->oSolidLine(pts+4);
+			break;
+		case 501:
+		case 502:
+			pts[2].x = pts[3].x = ix;	pts[0].x = ix-5;	pts[1].x = ix+5;
+			pts[0].y = pts[1].y = pts[2].y = (id == 502 ? rec->bottom -8 : rec->top +8);
+			pts[3].y = iy;
+			o->oSolidLine(pts);			o->oSolidLine(pts+2);
+			break;
+		case 503:
+			pts[2].y = pts[3].y = iy;
+			pts[0].y = pts[4].y = iy-5;			pts[1].y = pts[5].y = iy+5;
+			pts[0].x = pts[1].x = pts[2].x = rec->left +8;
+			pts[3].x = pts[4].x = pts[5].x = rec->right -8;
+			o->oSolidLine(pts);		o->oSolidLine(pts+2);		o->oSolidLine(pts+4);
+			break;
+		case 504:
+		case 505:
+			pts[2].y = pts[3].y = iy;	pts[0].y = iy-5;	pts[1].y = iy+5;
+			pts[0].x = pts[1].x = pts[2].x = (id == 505 ? rec->right -8 : rec->left +8);
+			pts[3].x = ix;
+			o->oSolidLine(pts);			o->oSolidLine(pts+2);
+			break;
+			}
+		o->oCircle(ix-4, iy-4, ix+4, iy+4);
+		o->UpdateRect(rec, false);
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Execute whisker style as owner drawn buttons 
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void OD_WhiskerTempl(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	POINT pts[6];
+	int ix, iy;
+
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		ix = (rec->left + rec->right)/2;
+		iy = (rec->top +rec->bottom)/2;
+		OD_BaseRect(o, cmd, rec);
+		switch(id) {
+		case 500:
+			pts[2].x = pts[3].x = ix;
+			pts[0].x = pts[4].x = ix-5;			pts[1].x = pts[5].x = ix+5;
+			pts[0].y = pts[1].y = pts[2].y = rec->top +8;
+			pts[3].y = pts[4].y = pts[5].y = rec->bottom -8;
+			o->oSolidLine(pts);		o->oSolidLine(pts+2);		o->oSolidLine(pts+4);
+			break;
+		case 501:
+			pts[0].x = pts[1].x = ix;			pts[0].y =  rec->bottom -8;
+			pts[1].y =  rec->top +8;			o->oSolidLine(pts);
+			break;
+		case 502:
+			pts[0].x = ix-5;	pts[1].x = pts[2].x = ix;	pts[3].x = ix +5;
+			pts[0].y = pts[1].y = rec->bottom-8;	pts[2].y = pts[3].y = rec->top+8;
+			o->oPolyline(pts, 4);
+			break;
+		case 503:
+			pts[0].x = ix+5;	pts[1].x = pts[2].x = ix;	pts[3].x = ix -5;
+			pts[0].y = pts[1].y = rec->bottom-8;	pts[2].y = pts[3].y = rec->top+8;
+			o->oPolyline(pts, 4);
+			break;
+			}
+		o->UpdateRect(rec, false);
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Execute polar plot templates as owner drawn buttons
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void OD_PolarTempl(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	LineDEF Line = {.0f, 1.0f, 0x0L, 0x0L};
+	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
+	FillDEF FillR = {FILL_NONE, 0x000000ffL, 1.0, 0L};
+	FillDEF FillG = {FILL_NONE, 0x0000ff00L, 1.0, 0L};
+	FillDEF FillB = {FILL_NONE, 0x00ff0000L, 1.0, 0L};
+	FillDEF FillY = {FILL_NONE, 0x0000ffffL, 1.0, 0L};
+	TextDEF td, otd;
+	POINT pts[12];
+	int ix, iy;
+
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
+		ix = (rec->left + rec->right)/2;
+		iy = (rec->top +rec->bottom)/2;
+		o->SetLine(&Line);
+		pts[0].x = pts[3].x = pts[4].x = rec->left;
+		pts[0].y = pts[1].y = pts[4].y = rec->top;
+		pts[1].x = pts[2].x = rec->right-1;
+		pts[2].y = pts[3].y = rec->bottom-1;
+		o->oPolyline(pts, 5);
+		Line.color = 0x00000000L;
+		o->SetLine(&Line);
+		o->SetFill(&Fill);
+		o->oCircle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
+		switch(id) {
+		case 200:
+		case 201:
+		case 202:
+			if(id == 201 || id == 202) {
+				pts[0].x = rec->left+13;	pts[0].y = rec->top+10;
+				pts[1].x = rec->left+15;	pts[1].y = rec->top+25;
+				pts[2].x = rec->right-19;	pts[2].y = rec->top+33;
+				pts[3].x = rec->right-11;	pts[3].y = rec->top+13;
+				o->oPolyline(pts, 4);
+				o->SetFill(&FillG);
+				}
+			else o->SetFill(&FillR);
+			if(id == 200 || id == 201) {
+				o->oCircle(rec->left+10, rec->top+7, rec->left+16, rec->top+13);
+				o->oCircle(rec->left+12, rec->top+22, rec->left+18, rec->top+28);
+				o->oCircle(rec->right-22, rec->top+30, rec->right-16, rec->top+36);
+				o->oCircle(rec->right-14, rec->top+10, rec->right-8, rec->top+16);
+				}
+			break;
+		case 203:
+			pts[0].x = rec->left+7;		pts[0].y = rec->top+13;
+			pts[1].x = rec->left+10;	pts[1].y = rec->top+30;
+			pts[2].x = rec->right-19;	pts[2].y = rec->top+33;
+			pts[3].x = rec->right-9;	pts[3].y = rec->top+11;
+			pts[4].x = ix-4;			pts[4].y =iy +3;
+			o->SetFill(&FillY);
+			o->oPolygon(pts, 5);
+			break;
+		case 204:
+			if(cmd == OD_DRAWNORMAL) FillG.color = 0x00e8e8e8L;
+			o->SetFill(&FillG);
+			o->oCircle(ix-6, rec->top+5, ix+6, iy+6);
+			memcpy(&td, &o->TxtSet, sizeof(TextDEF));
+			memcpy(&otd, &o->TxtSet, sizeof(TextDEF));
+			td.Align = TXA_HCENTER | TXA_VTOP;
+			td.Style = TXS_NORMAL;
+			td.Mode = TXM_TRANSPARENT;
+			td.fSize *= 0.8;	td.iSize = 0;
+			td.ColTxt = 0x00ff0000L;
+			o->SetTextSpec(&td);
+			o->oTextOut(ix, iy+3, "y=f(x)", 0);
+			o->SetTextSpec(&otd);
+			}
+		o->UpdateRect(rec, false);
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Execute templates for pie-charts as owner drawn buttons
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void OD_PieTempl(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	LineDEF Line = {.0, 1.0, 0x0L, 0x0L};
+	FillDEF FillR = {FILL_NONE, 0x000000ffL, 1.0, 0L};
+	FillDEF FillG = {FILL_NONE, 0x0000ff00L, 1.0, 0L};
+	FillDEF FillB = {FILL_NONE, 0x00ff0000L, 1.0, 0L};
+	double angels1[]={90.0, 45.0, -45.0, 90.0};
+	double angels2[]={180, 157.5, 112.5, 0.0};
+	int ix, iy;
+	double r, *ang = angels1;
+	segment *seg = 0L;
+	lfPOINT fc;
+
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		ix = (rec->left + rec->right)/2;
+		iy = (rec->top +rec->bottom)/2;
+		OD_BaseRect(o, cmd, rec);
+		switch(id) {
+		case 401:		case 411:
+			ang = angels2;
+		case 400:		case 410:
+			fc.fx = o->fix2un((double)ix-1);		fc.fy = o->fiy2un((double)iy);
+			r = o->fix2un((double)(rec->right -rec->left))/3;
+			seg = new segment(0L, 0L, &fc, 0.0, r, ang[0], ang[1]);
+			if(seg) {
+				if(id == 410 || id == 411) seg->SetSize(SIZE_RADIUS1, r*.7);
+				seg->Command(CMD_SEG_LINE, &Line, 0L);
+				seg->Command(CMD_SEG_FILL, &FillR, 0L);
+				seg->DoPlot(o);
+				seg->SetSize(SIZE_ANGLE1, ang[1]);
+				seg->SetSize(SIZE_ANGLE2, ang[2]);
+				seg->Command(CMD_SEG_FILL, &FillG, 0L);
+				seg->DoPlot(o);
+				seg->SetSize(SIZE_ANGLE1, ang[2]);
+				seg->SetSize(SIZE_ANGLE2, ang[3]);
+				seg->Command(CMD_SEG_FILL, &FillB, 0L);
+				seg->DoPlot(o);
+				delete seg;
+				}
+			break;
+			}
+		o->UpdateRect(rec, false);
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Show a simple graph how 3D axes are organized as owner drawn button
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void OD_AxisDesc3D(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	POINT pts[5];
+
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		pts[0].x = ((rec->left + rec->right)>>1)-15;
+		pts[0].y = ((rec->bottom + rec->top)>>1)+10;
+		pts[1].x = rec->left + 15;	pts[1].y = rec->bottom-20;
+		o->oSolidLine(pts);
+		pts[2].x = pts[1].x +2;		pts[2].y = pts[1].y -7;
+		o->oSolidLine(pts + 1);
+		pts[2].x = pts[1].x +6;		pts[2].y = pts[1].y -2;
+		o->oSolidLine(pts + 1);
+		o->oTextOut(pts[1].x -2, pts[1].y -5, "z", 1);
+		pts[1].x = pts[0].x;		pts[1].y = rec->top+20;
+		o->oSolidLine(pts);
+		pts[2].x = pts[1].x -4;		pts[2].y = pts[1].y +6;
+		o->oSolidLine(pts + 1);
+		pts[2].x = pts[1].x +4;
+		o->oSolidLine(pts + 1);
+		o->oTextOut(pts[1].x + 4, pts[1].y - 18, "y", 1);
+		pts[1].x = rec->right-15;	pts[1].y = rec->bottom -22;
+		o->oSolidLine(pts);
+		pts[2].x = pts[1].x -6;		pts[2].y = pts[1].y +2;
+		o->oSolidLine(pts + 1);
+		pts[2].x = pts[1].x -4;		pts[2].y = pts[1].y -5;
+		o->oSolidLine(pts + 1);
+		o->oTextOut(pts[1].x +9, pts[1].y -4, "x", 1);
+		o->UpdateRect(rec, false);
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Execute axis breaks symbols as owner drawn button
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void OD_BreakTempl(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	LineDEF Line = {0.0, 1.0, 0x0L, 0x0L};
+	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
+	POINT pts[15];
+	int i, ix, iy;
+
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		ix = (rec->left + rec->right)>>1;
+		iy = (rec->top +rec->bottom)>>1;
+		OD_BaseRect(o, cmd, rec);
+		pts[0].x = pts[1].x = ix;
+		pts[0].y = rec->top +5;		pts[1].y = iy-3;
+		o->oSolidLine(pts);
+		pts[0].y = rec->bottom -7;		pts[1].y = iy+3;
+		o->oSolidLine(pts);
+		switch(id) {
+		case 402:
+			pts[0].x = ix-7;		pts[1].x = ix+7;
+			pts[0].y = iy;			pts[1].y = iy-6;
+			o->oSolidLine(pts);
+			pts[0].y += 6;			pts[1].y += 6;
+			o->oSolidLine(pts);
+			break;
+		case 403:
+			pts[0].x = ix-7;		pts[1].x = ix+7;
+			pts[0].y = iy-3;		pts[1].y = iy-3;
+			o->oSolidLine(pts);
+			pts[1].y += 6;			o->oSolidLine(pts);
+			pts[0].y += 6;			o->oSolidLine(pts);
+			break;
+		case 404:
+			for(i = 0; i < 15; i++) {
+				pts[i].x = ix +i -7;
+				pts[i].y = iy - 3 + (int)(sin((double)i*0.41887902)*2.5);
+				}
+			o->oPolyline(pts, 15);
+			for(i = 0; i < 15; i++) pts[i].y += 6;
+			o->oPolyline(pts, 15);
+			break;
+			}
+		o->UpdateRect(rec, false);
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Execute plot selection templates as owner drawn button
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static void UtilBarDraw(POINT *pts, int x, int y1, int y2, anyOutput *o)
+{
+	pts[1].x = pts[0].x = pts[4].x = x;
+	pts[0].y = y1;			pts[1].y = y2;
+	pts[2].y = pts[1].y-1;		pts[2].x = pts[3].x = pts[0].x-3;	
+	pts[3].y = pts[0].y-1;		pts[4].y = pts[0].y;
+	o->oPolygon(pts, 5);
+	pts[2].x += 5;			pts[3].x += 5;
+	o->oPolygon(pts, 5);
+	pts[1].x -= 3;			pts[1].y = pts[0].y-1;
+	pts[2].x = pts[0].x-2;		pts[2].y = pts[0].y-2;
+	o->oPolygon(pts, 5);
+}
+
+void OD_PlotTempl(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	LineDEF Line = {.0, 1.0, 0x0L, 0x0L};
+	LineDEF rLine = {.0, 1.0, 0x00000080L, 0x0L};
+	LineDEF bLine = {.0, 1.0, 0x00e00000L, 0x0L};
+	LineDEF gLine = {.0, 1.0, 0x0000e000L, 0x0L};
+	FillDEF FillR = {FILL_NONE, 0x000000ffL, 1.0, 0L};
+	FillDEF FillG = {FILL_NONE, 0x0000ff00L, 1.0, 0L};
+	FillDEF FillB = {FILL_NONE, 0x00ff0000L, 1.0, 0L};
+	FillDEF FillY = {FILL_NONE, 0x0000ffffL, 1.0, 0L};
+	TextDEF td, otd;
+	POINT pts[12];
+	int i, j, ix, iy;
+	double r;
+	segment *seg = 0L;
+	lfPOINT fc;
+
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		ix = (rec->left + rec->right)>>1;
+		iy = (rec->top +rec->bottom)>>1;
+		switch(id) {
+		case 560:	case 561:	case 562:	case 563:	case 564:		//3D axes
+		case 565:	case 566:	case 567:
+			OD_AxisTempl3D(cmd, par, rec, o, data, 410+AxisTempl3D);
+			break;
+		default:
+			OD_BaseRect(o, cmd, rec);
+			break;
+			}
+		if(cmd != OD_DRAWSELECTED) {
+			FillR.color |= 0x00808080L;		FillG.color |= 0x00808080L;
+			FillB.color |= 0x00808080L;		FillY.color |= 0x00808080L;
+			}
+		switch(id) {
+		case 500:
+		case 501:
+			fc.fx = o->fix2un((double)ix-1);		fc.fy = o->fiy2un((double)iy);
+			r = o->fix2un((double)(rec->right -rec->left))/3;
+			seg = new segment(0L, 0L, &fc, 0.0, r, 90.0, 45.0);
+			if(seg) {
+				if(id == 501) seg->SetSize(SIZE_RADIUS1, r*.7f);
+				seg->Command(CMD_SEG_LINE, &Line, 0L);
+				seg->Command(CMD_SEG_FILL, &FillR, 0L);
+				seg->DoPlot(o);
+				seg->SetSize(SIZE_ANGLE1, 45.0f);
+				seg->SetSize(SIZE_ANGLE2, -45.0f);
+				seg->Command(CMD_SEG_FILL, &FillG, 0L);
+				seg->DoPlot(o);
+				seg->SetSize(SIZE_ANGLE1, -45.0f);
+				seg->SetSize(SIZE_ANGLE2, 90.0f);
+				seg->Command(CMD_SEG_FILL, &FillB, 0L);
+				seg->DoPlot(o);
+				delete seg;
+				}
+			break;
+		case 502:
+			pts[0].x = rec->right-8;	pts[0].y = rec->top+8;
+			pts[1].x = ix+4;			pts[1].y = iy+2;
+			pts[2].x = ix+8;			pts[2].y = rec->bottom-11;
+			pts[3].x = ix;				pts[3].y = iy+7;
+			pts[4].x = ix-6;			pts[4].y = iy+13;
+			pts[5].x = ix-5;			pts[5].y = iy+5;
+			pts[6].x = rec->left+10;	pts[6].y = iy+2;
+			pts[7].x = ix-3;			pts[7].y = iy-1;
+			pts[8].x = ix-3;			pts[8].y = iy-10;
+			pts[9].x = ix+2;			pts[9].y = iy-4;
+			pts[10].x = pts[0].x;		pts[10].y = pts[0].y;
+			o->SetFill(&FillY);
+			o->oPolygon(pts, 11);
+			break;
+		case 503:
+			o->SetFill(&FillR);
+			o->oRectangle(rec->left+8, rec->top+30, rec->left+12, rec->bottom-3);
+			o->oRectangle(rec->left+15, rec->bottom-10, rec->left+19, rec->bottom-3);
+			o->oRectangle(rec->left+22, rec->top+10, rec->left+26, rec->bottom-3);
+			o->oRectangle(rec->left+29, rec->bottom-35, rec->left+33, rec->bottom-3);
+			o->oRectangle(rec->left+36, rec->top+30, rec->left+40, rec->bottom-3);
+			break;
+		case 504:	case 507:
+			o->SetFill(&FillR);
+			o->oRectangle(rec->left+9, rec->top+30, rec->left+13, rec->bottom-3);
+			o->oRectangle(rec->right-20, id == 507 ? rec->top + 25 : rec->top+10, rec->right-16, rec->bottom-3);
+			o->SetFill(&FillG);
+			o->oRectangle(rec->left+13, rec->top+25, rec->left+17, rec->bottom-3);
+			o->oRectangle(rec->right-16, rec->top+15, rec->right-12, rec->bottom-3);
+			o->SetFill(&FillB);
+			o->oRectangle(rec->left+17, rec->top+35, rec->left+21, rec->bottom-3);
+			o->oRectangle(rec->right-12, rec->top+20, rec->right-8, rec->bottom-3);
+			if(id == 507) {
+				o->SetLine(&Line);
+				pts[0].x = pts[1].x = rec->left+11;			pts[0].y = rec->top+20;
+				pts[1].y = rec->top+40;						o->oSolidLine(pts);
+				pts[0].x = pts[1].x = rec->left+15;			pts[0].y = rec->top+15;
+				pts[1].y = rec->top+35;						o->oSolidLine(pts);
+				pts[0].x = pts[1].x = rec->left+19;			pts[0].y = rec->top+30;
+				pts[1].y = rec->top+40;						o->oSolidLine(pts);
+				pts[0].x = pts[1].x = rec->right-18;		pts[0].y = rec->top+15;
+				pts[1].y = rec->top+35;						o->oSolidLine(pts);
+				pts[0].x = pts[1].x = rec->right-14;		pts[0].y = rec->top+10;
+				pts[1].y = rec->top+20;						o->oSolidLine(pts);
+				pts[0].x = pts[1].x = rec->right-10;		pts[0].y = rec->top+10;
+				pts[1].y = rec->top+30;						o->oSolidLine(pts);
+				}
+			break;
+		case 505:
+			o->SetFill(&FillY);
+			o->oRectangle(rec->left+15, rec->bottom-10, rec->left+19, rec->bottom-3);
+			o->oRectangle(rec->left+22, rec->top+15, rec->left+26, rec->bottom-3);
+			o->oRectangle(rec->left+29, rec->bottom-30, rec->left+33, rec->bottom-3);
+			o->oRectangle(rec->left+36, rec->bottom-12, rec->left+40, rec->bottom-3);
+			o->SetLine(&bLine);
+			pts[0].x = rec->left +9;	pts[0].y = rec->bottom-5;	pts[1].x = pts[0].x+1;
+			for(i = 0; i < (rec->right - rec->left - 18); i++) {
+				r = ((double)(i+rec->left-ix+7))/8.0;
+				pts[1].y = rec->bottom - iround(exp(-r*r)*35.0+5.0);
+				o->oSolidLine(pts);
+				pts[0].x++;		pts[1].x++;		pts[0].y = pts[1].y;	
+				}
+			break;
+		case 506:
+			o->SetLine(&bLine);
+			pts[0].x = rec->left +9;	pts[0].y = rec->bottom-5;	pts[1].x = pts[0].x+1;
+			for(i = 0; i < (rec->right - rec->left - 18); i++) {
+				r = ((double)(i+rec->left-ix+7))/8.0;
+				pts[1].y = pts[0].y - iround(exp(-r*r)*1.0+5.0);
+				pts[1].y = rec->bottom - iround(errf(r)*16.0+26.0);
+				if (i) o->oSolidLine(pts);
+				pts[0].x++;		pts[1].x++;		pts[0].y = pts[1].y;	
+				}
+			o->SetLine(&rLine);
+			pts[0].x = ix-2;	pts[1].x = pts[0].x+1;
+			for(i = 0; i < (rec->right - ix-5); i++) {
+				r = ((double)(i-9))/4.0;
+				pts[1].y = rec->bottom - iround(exp(-r*r)*17.0+7.0);
+				if (i) o->oSolidLine(pts);
+				pts[0].x++;		pts[1].x++;		pts[0].y = pts[1].y;	
+				}
+			o->SetFill(&FillG);			o->SetLine(&Line);
+			o->oCircle(ix, iy, ix+5, iy+5);
+			o->oCircle(ix-5, iy+8, ix, iy+13);
+			o->oCircle(ix+5, iy-10, ix+10, iy-5);
+			break;
+		case 520:		case 521:
+			if(id == 521) {
+				pts[0].x = rec->left+13;	pts[0].y = rec->bottom-12;
+				pts[1].x = rec->left+20;	pts[1].y = rec->top+18;
+				pts[2].x = rec->right-19;	pts[2].y = rec->top+33;
+				pts[3].x = rec->right-11;	pts[3].y = rec->top+13;
+				o->oPolyline(pts, 4);
+				o->SetFill(&FillG);
+				}
+			else o->SetFill(&FillR);
+			o->oCircle(rec->left+10, rec->bottom-15, rec->left+16, rec->bottom-9);
+			o->oCircle(rec->left+17, rec->top+15, rec->left+23, rec->top+21);
+			o->oCircle(rec->right-22, rec->top+30, rec->right-16, rec->top+36);
+			o->oCircle(rec->right-14, rec->top+10, rec->right-8, rec->top+16);
+			break;
+		case 522:
+			o->SetFill(&FillR);
+			o->oRectangle(rec->left+3, rec->top+8, rec->left+16, rec->top+16);
+			o->SetFill(&FillG);
+			o->oRectangle(rec->left+3, iy-4, rec->right-16, iy+4);
+			o->SetFill(&FillB);
+			o->oRectangle(rec->left+3, rec->bottom-8, rec->left+26, rec->bottom-16);
+			break;
+		case 523:
+			o->SetFill(&FillR);
+			o->oRectangle(rec->left+8, rec->top+30, rec->left+16, rec->bottom-3);
+			o->SetFill(&FillG);
+			o->oRectangle(ix-4, rec->top+10, ix+4, rec->bottom-3);
+			o->SetFill(&FillB);
+			o->oRectangle(rec->right-8, rec->top+20, rec->right-16, rec->bottom-3);
+			break;
+		case 524:
+			o->SetFill(&FillG);
+			o->oCircle(rec->left+10, rec->bottom-15, rec->left+16, rec->bottom-9);
+			o->oCircle(ix-9, iy-18, ix+9, iy);
+			o->oCircle(rec->right-7, rec->top+30, rec->right-17, rec->top+40);
+			break;
+		case 525:
+			pts[0].x = pts[1].x = rec->left +12;
+			pts[0].y = rec->top+20;		pts[1].y = rec->top+40;
+			o->oPolyline(pts,2);
+			pts[0].x = pts[1].x = rec->right-12;
+			o->oPolyline(pts,2);
+			pts[0].x = pts[1].x = ix;
+			pts[0].y = rec->top+10;		pts[1].y = rec->top+35;
+			o->oPolyline(pts,2);
+			o->SetFill(&FillY);
+			o->oRectangle(rec->left+8, rec->top+25, rec->left+16, rec->top+35);
+			o->oRectangle(ix-4, rec->top+13, ix+4, rec->top+28);
+			o->oRectangle(rec->right-8, rec->top+30, rec->right-16, rec->top+35);
+			break;
+		case 526:
+			pts[0].x = rec->left+13;	pts[0].y = rec->bottom-12;
+			pts[1].x = rec->right-11;	pts[1].y = rec->top+8;
+			o->oSolidLine(pts);
+			o->SetFill(&FillB);
+			o->oCircle(rec->left+10, rec->bottom-21, rec->left+16, rec->bottom-15);
+			o->oCircle(rec->left+17, rec->top+20, rec->left+23, rec->top+26);
+			o->oCircle(rec->right-22, rec->top+25, rec->right-16, rec->top+31);
+			o->oCircle(rec->right-14, rec->top+15, rec->right-8, rec->top+21);
+			o->oCircle(rec->right-22, rec->top+9, rec->right-16, rec->top+15);
+			break;
+		case 527:
+			o->oCircle(rec->left+8, rec->top+8, rec->right-8, rec->bottom-8);
+			o->oCircle(rec->left+16, rec->top+16, rec->right-16, rec->bottom-16);
+			pts[0].x = rec->left+6;		pts[0].y = iy;
+			pts[1].x = rec->right-6;	pts[1].y = iy;
+			o->oSolidLine(pts);
+			pts[0].x = ix;				pts[0].y = rec->bottom-6;
+			pts[1].x = ix;				pts[1].y = rec->top+6;
+			o->oSolidLine(pts);			o->SetFill(&FillR);
+			o->oCircle(rec->left+13, rec->top+13, rec->left+19, rec->top+19);
+			o->oCircle(ix-7, iy+1, ix-1, iy+7);
+			o->oCircle(rec->right-19, rec->bottom-19, rec->right-13, rec->bottom-13);
+			o->oCircle(rec->right-19, rec->top+13, rec->right-13, rec->top+19);
+			break;
+		case 528:
+			o->SetFill(&FillY);
+			o->oRectangle(rec->left+8, iy-2, ix-2, iy+2);
+			o->oRectangle(ix-2, iy-15, ix+2, iy+15);
+			o->oRectangle(ix+2, iy-11, ix+6, iy+11);
+			o->oRectangle(ix+6, iy-5, rec->right-8, iy+5);
+			break;
+		case 529:
+			o->SetLine(&rLine);
+			pts[0].x = rec->left +9;	pts[0].y = iy;	pts[1].x = pts[0].x+1;
+			for(i = 0; i < (rec->right - rec->left - 18); i++) {
+				pts[1].y = iy-4 + iround(pow(20.0, 1.0+((double)-i)/30.0) * -sin(((double)i)));
+				o->oSolidLine(pts);
+				pts[0].x++;		pts[1].x++;		pts[0].y = pts[1].y;	
+				}
+			memcpy(&td, &o->TxtSet, sizeof(TextDEF));
+			memcpy(&otd, &o->TxtSet, sizeof(TextDEF));
+			td.Align = TXA_HCENTER | TXA_VTOP;
+			td.Style = TXS_NORMAL;
+			td.Mode = TXM_TRANSPARENT;
+			td.ColTxt = 0x00c00000L;
+			o->SetTextSpec(&td);
+			o->oTextOut(ix, iy+4, "y=f(x)", 0);
+			o->SetTextSpec(&otd);
+			break;
+		case 530:
+			o->SetLine(&rLine);
+			pts[0].x = rec->left +9;	pts[0].y = iy+13;	pts[1].x = pts[0].x+1;
+			for(i = 0; i < (rec->right - rec->left - 18); i++) {
+				pts[1].y = iy+12 + iround(-log10(((double)i)/.4 + 1.0)*15.0);
+				o->oSolidLine(pts);
+				pts[0].x++;		pts[1].x++;		pts[0].y = pts[1].y;	
+				}
+			o->SetLine(&Line);			o->SetFill(&FillG);
+			o->oCircle(rec->left+8, rec->bottom-15, rec->left+14, rec->bottom-9);
+			o->oCircle(rec->left+11, iy-1, rec->left+17, iy+5);
+			o->oCircle(rec->left+17, rec->top+12, rec->left+23, rec->top+18);
+			o->oCircle(rec->right-22, rec->top+8, rec->right-16, rec->top+14);
+			o->oCircle(rec->right-14, rec->top+10, rec->right-8, rec->top+16);
+			memcpy(&td, &o->TxtSet, sizeof(TextDEF));
+			memcpy(&otd, &o->TxtSet, sizeof(TextDEF));
+			td.Align = TXA_HLEFT | TXA_VCENTER;
+			td.Style = TXS_BOLD;	td.Mode = TXM_TRANSPARENT;
+			td.fSize = defs.GetSize(SIZE_TEXT)*1.75;			td.iSize = 0;
+			td.ColTxt = cmd == OD_DRAWSELECTED ? 0x0000f0f0L : 0x00c00000;
+			o->SetTextSpec(&td);
+			o->oTextOut(ix-2, iy+3, "?", 0);
+			o->SetTextSpec(&otd);
+			break;
+		case 531:
+			o->SetLine(&rLine);
+			pts[0].x = rec->left +9;	pts[0].y = iy;	
+			pts[1].x = ix;				pts[1].y = rec->top + 9;
+			pts[2].x = rec->right -9;	pts[2].y = iy;
+			o->oPolyline(pts, 3, 0L);
+			o->SetLine(&gLine);
+			pts[0].y -= 15;	pts[1].y = iy;	pts[2].y -=7;
+			o->oPolyline(pts, 3, 0L);
+			o->SetLine(&bLine);
+			pts[0].y += 9;	pts[1].y += 10;	pts[2].y = pts[1].y;
+			o->oPolyline(pts, 3, 0L);
+			break;
+		case 532:
+			pts[0].x = rec->left +13;	pts[0].y = rec->top+8;	
+			pts[1].x = rec->left +13;	pts[1].y = iy;
+			o->oSolidLine(pts);
+			pts[0].x -= 3;		pts[1].x += 3;
+			pts[0].y = pts[1].y = rec->top+8;
+			o->oSolidLine(pts);
+			pts[0].y = pts[1].y = iy;
+			o->oSolidLine(pts);
+			pts[0].x = ix;	pts[0].y = iy-8;	
+			pts[1].x = ix;	pts[1].y = rec->bottom-13;
+			o->oSolidLine(pts);
+			pts[0].x -= 3;		pts[1].x += 3;
+			pts[0].y = pts[1].y = iy-8;
+			o->oSolidLine(pts);
+			pts[0].y = pts[1].y = rec->bottom-13;
+			o->oSolidLine(pts);
+			pts[0].x = rec->right -13;	pts[0].y = rec->top+10;	
+			pts[1].x = rec->right -13;	pts[1].y = iy-6;
+			o->oSolidLine(pts);
+			pts[0].x -= 3;		pts[1].x += 3;
+			pts[0].y = pts[1].y = rec->top+10;
+			o->oSolidLine(pts);
+			pts[0].y = pts[1].y = iy-6;
+			o->oSolidLine(pts);
+			pts[0].x = rec->left+13;	pts[1].x = ix;		pts[2].x = rec->right-13;
+			pts[0].y = (rec->top+8+iy)>>1;
+			pts[1].y = (rec->bottom-13 + iy -8)>>1;
+			pts[2].y = (rec->top+10+iy-6)>>1;
+			o->oPolyline(pts, 3, 0L);
+			o->SetFill(&FillY);
+			o->oCircle(pts[0].x-3, pts[0].y-3, pts[0].x+3, pts[0].y+3);
+			o->oCircle(pts[1].x-3, pts[1].y-3, pts[1].x+3, pts[1].y+3);
+			o->oCircle(pts[2].x-3, pts[2].y-3, pts[2].x+3, pts[2].y+3);
+			break;
+		case 540:
+			o->SetFill(&FillR);
+			o->oRectangle(rec->left+8, rec->bottom-8, rec->left+16, rec->bottom-3);
+			o->oRectangle(ix-4, rec->bottom-18, ix+4, rec->bottom-3);
+			o->oRectangle(rec->right-8, rec->bottom-12, rec->right-16, rec->bottom-3);
+			o->SetFill(&FillG);
+			o->oRectangle(rec->left+8, rec->bottom-13, rec->left+16, rec->bottom-8);
+			o->oRectangle(ix-4, rec->bottom-28, ix+4, rec->bottom-18);
+			o->oRectangle(rec->right-8, rec->bottom-22, rec->right-16, rec->bottom-12);
+			o->SetFill(&FillB);
+			o->oRectangle(rec->left+8, rec->bottom-18, rec->left+16, rec->bottom-13);
+			o->oRectangle(ix-4, rec->bottom-38, ix+4, rec->bottom-28);
+			o->oRectangle(rec->right-8, rec->bottom-27, rec->right-16, rec->bottom-22);
+			break;
+		case 541:
+			o->SetFill(&FillR);
+			pts[0].x = pts[1].x = pts[5].x = rec->left+8;
+			pts[0].y = pts[4].y =pts[5].y = rec->bottom-4;
+			pts[1].y = iy+5;	pts[2].x = ix;	pts[2].y = rec->bottom-5;
+			pts[3].x = pts[4].x = rec->right-8;
+			pts[3].y = rec->bottom-12;
+			o->oPolygon(pts, 6);
+			o->SetFill(&FillY);
+			for(i = 1; i < 6; i++) {
+				pts[i-1].x = pts[i].x;	pts[i-1].y = pts[i].y;
+				}
+			pts[5].x = pts[0].x;	pts[5].y = pts[0].y;
+			pts[4].x = pts[1].x;	pts[4].y = pts[1].y-8;
+			pts[3].y = rec->bottom-20;
+			o->oPolygon(pts, 6);
+			o->SetFill(&FillG);
+			pts[1].y = pts[4].y;	pts[2].y = pts[3].y;
+			pts[4].y -= 12;	pts[3].y -= 3;
+			o->oPolygon(pts, 6);
+			break;
+		case 542:
+			Line.color = 0x00ff0000L;
+			o->SetLine(&Line);
+			pts[0].x = rec->left+6;		pts[0].y = rec->bottom-6;
+			pts[1].x = rec->left+10;	pts[1].y = rec->bottom-6;
+			pts[2].x = rec->left+12;	pts[2].y = iy + 8;
+			pts[3].x = rec->left+14;	pts[3].y = rec->bottom-6;
+			pts[4].x = rec->right-24;	pts[4].y = rec->bottom-6;
+			pts[5].x = rec->right-22;	pts[5].y = iy + 4;
+			pts[6].x = rec->right-20;	pts[6].y = rec->bottom-6;
+			pts[7].x = rec->right-16;	pts[7].y = rec->bottom-6;
+			for(i = 0; i < 4; i++){
+				o->oPolyline(pts, 8);
+				for(j = 0; j < 8; j++) {
+					pts[j].x += 4;	pts[j].y -= 4;
+					}
+				pts[2].y -= 4;	pts[5].y++;
+				}
+			break;
+		case 543:
+			OD_AxisTempl3D(cmd, par, rec, o, data, 411);
+			o->SetFill(&FillR);
+			UtilBarDraw(pts, ix-5, iy-2, rec->bottom-12, o);
+			UtilBarDraw(pts, ix, iy+3, rec->bottom-11, o);
+			UtilBarDraw(pts, ix+5, iy-5, rec->bottom-10, o);
+			UtilBarDraw(pts, ix+10, iy-4, rec->bottom-9, o);
+			o->SetFill(&FillG);
+			UtilBarDraw(pts, ix-10, iy+2, rec->bottom-9, o);
+			UtilBarDraw(pts, ix-5, iy+10, rec->bottom-8, o);
+			UtilBarDraw(pts, ix, iy+8, rec->bottom-7, o);
+			UtilBarDraw(pts, ix+5, iy, rec->bottom-6, o);
+			break;
+		case 544:
+			OD_AxisTempl3D(cmd, par, rec, o, data, 411);
+			for(i = 0; i < 6; i++){
+				switch(i) {
+				case 0:
+					o->SetFill(&FillY);
+					pts[0].x = ix-5;		pts[0].y = iy+1;
+					pts[1].x = ix;			pts[1].y = iy-10;
+					break;
+				case 1:
+					pts[0].x = pts[1].x;	pts[0].y = pts[1].y;
+					pts[1].x = ix+5;			pts[1].y = iy -6;
+					break;
+				case 2:
+					pts[0].x = pts[1].x;	pts[0].y = pts[1].y;
+					pts[1].x = ix+10;		pts[1].y = iy +4;
+					break;
+				case 3:
+					o->SetFill(&FillR);
+					pts[0].x = ix-10;		pts[0].y = iy-10;
+					pts[1].x = ix-5;		pts[1].y = iy+4;
+					break;
+				case 4:
+					pts[0].x = pts[1].x;	pts[0].y = pts[1].y;
+					pts[1].x = ix;			pts[1].y = iy +9;
+					break;
+				case 5:
+					pts[0].x = pts[1].x;	pts[0].y = pts[1].y;
+					pts[1].x = ix+10;		pts[1].y = iy +12;
+					break;
+					}
+				pts[2].x = pts[1].x -3;	pts[2].y = pts[1].y + 2;
+				pts[3].x = pts[0].x -3;	pts[3].y = pts[0].y + 2;
+				pts[4].x = pts[0].x;	pts[4].y = pts[0].y;
+				o->oPolygon(pts, 5);
+				}
+			break;
+		case 560:
+			o->SetFill(&FillY);
+#ifdef _WINDOWS
+			o->oCircle(rec->right-13, rec->top+7, rec->right-19, rec->top+13);
+			o->oCircle(rec->right-11, iy-3, rec->right-17, iy+3);
+			o->oCircle(ix, iy+3, ix+6, iy+9);
+			o->oCircle(rec->left+12, iy+3, rec->left+18, iy+9);
+			o->oCircle(ix, rec->bottom-6, ix+6, rec->bottom-12);
+#else
+			o->oCircle(rec->right-15, rec->top+7, rec->right-19, rec->top+13);
+			o->oCircle(rec->right-13, iy-3, rec->right-17, iy+3);
+			o->oCircle(ix, iy+3, ix+6, iy+9);
+			o->oCircle(rec->left+12, iy+3, rec->left+18, iy+9);
+			o->oCircle(ix, rec->bottom-8, ix+6, rec->bottom-12);
+#endif
+			break;
+		case 561:
+			o->SetFill(&FillG);
+			UtilBarDraw(pts, ix+1, rec->top +12, rec->bottom-10, o);
+			UtilBarDraw(pts, rec->left+16, iy+8, rec->bottom-8, o);
+			UtilBarDraw(pts, rec->right-12, iy+12, rec->bottom-8, o);
+			break;
+		case 562:
+			o->SetLine(&bLine);
+			pts[0].x = rec->left+20;	pts[0].y = rec->bottom-10;
+			pts[1].x = rec->right-10;	pts[1].y = rec->bottom-16;
+			o->oSolidLine(pts);
+			pts[0].x = pts[1].x;		pts[0].y = pts[1].y;
+			pts[1].x -= 8;				pts[1].y -= 12;
+			o->oSolidLine(pts);
+			pts[0].x = pts[1].x;		pts[0].y = pts[1].y;
+			pts[1].x -= 18;				pts[1].y += 3;
+			o->oSolidLine(pts);
+			pts[0].x = pts[1].x;		pts[0].y = pts[1].y;
+			pts[1].x += 15;				pts[1].y += 4;
+			o->oSolidLine(pts);
+			break;
+		case 563:
+			o->SetFill(&FillG);
+			o->oCircle(rec->left+12, rec->bottom-19, rec->left+18, rec->bottom-13);
+			o->oCircle(ix, iy-10, ix+14, iy+4);
+			o->oCircle(ix, rec->top+34, ix+10, rec->top+44);
+			break;
+		case 564:
+			o->SetFill(&FillY);
+			pts[0].x = rec->left+10;	pts[0].y = rec->bottom-14;
+			pts[1].x = pts[0].x;		pts[1].y = rec->top +16;
+			pts[2].x = ix-6;			pts[2].y = iy+4;
+			pts[3].x = pts[2].x;		pts[3].y = rec->bottom -10;
+			pts[4].x = pts[0].x;		pts[4].y = pts[0].y;
+			o->oPolygon(pts, 5, 0L);
+			pts[0].x = pts[2].x;		pts[0].y = pts[2].y;
+			pts[1].x = pts[3].x;		pts[1].y = pts[3].y;
+			pts[2].x = ix + 3;			pts[2].y = pts[1].y -2;
+			pts[3].x = pts[2].x;		pts[3].y = pts[0].y +1;
+			pts[4].x = pts[0].x;		pts[4].y = pts[0].y;
+			o->oPolygon(pts, 5, 0L);
+			pts[0].x = pts[2].x;		pts[0].y = pts[2].y;
+			pts[1].x = pts[3].x;		pts[1].y = pts[3].y;
+			pts[2].x = ix + 10;			pts[2].y = pts[1].y +9;
+			pts[3].x = pts[2].x;		pts[3].y = pts[0].y +3;
+			pts[4].x = pts[0].x;		pts[4].y = pts[0].y;
+			o->oPolygon(pts, 5, 0L);
+			break;
+		case 565:	case 566:
+			o->SetLine(&rLine);
+			pts[0].x = ix-16;			pts[0].y = iy-2;
+			pts[1].x = ix+4;			pts[1].y = iy+6;
+			for(i = 0; i < 4; i++) {
+				o->oSolidLine(pts);
+				pts[0].x += 4;			pts[1].x += 4;
+				pts[0].y -= 4;			pts[1].y -= 4;
+				}
+			pts[0].x = ix+4;			pts[0].y = iy+6;
+			pts[1].x -= 2;				pts[1].y += 4;
+			for(i = 0; i < 5; i++) {
+				o->oSolidLine(pts);
+				pts[0].x -= 5;			pts[1].x -= 5;
+				pts[0].y -= 2;			pts[1].y -= 2;
+				}
+			memcpy(&td, &o->TxtSet, sizeof(TextDEF));
+			memcpy(&otd, &o->TxtSet, sizeof(TextDEF));
+			td.Align = TXA_HCENTER | TXA_VTOP;
+			td.Mode = TXM_TRANSPARENT;
+			if(id == 565) {
+				td.Style = TXS_NORMAL;			td.ColTxt = 0x00c00000L;
+				o->SetTextSpec(&td);			o->oTextOut(ix, iy+4, "f(x,z)", 0);
+				}
+			else {
+				td.Style = TXS_BOLD;
+				td.fSize = defs.GetSize(SIZE_TEXT)*1.75;			td.iSize = 0;
+				td.ColTxt = cmd == OD_DRAWSELECTED ? 0x0000cb00L : 0x00cb00c0L;
+				o->SetTextSpec(&td);
+				o->oTextOut(ix-10, iy-6, "?", 0);
+				}
+			o->SetTextSpec(&otd);
+			break;
+		case 567:
+			o->SetLine(&bLine);			if(cmd == OD_DRAWSELECTED) o->SetFill(&FillG);
+			pts[0].x = ix-10;			pts[0].y = iy+4;
+			pts[1].x = ix-6;			pts[1].y = iy+10;
+			pts[2].x = ix-16;			pts[2].y = iy+8;
+			o->oPolygon(pts, 3, 0L);
+			pts[2].x = ix+2;		pts[2].y = iy-10;		o->oPolygon(pts, 3, 0L);
+			pts[0].x = ix+10;		pts[0].y = iy-12;		o->oPolygon(pts, 3, 0L);
+			pts[2].x = ix+2;		pts[2].y = iy+14;		o->oPolygon(pts, 3, 0L);
+			pts[1].x = ix+12;		pts[1].y = iy+4;		o->oPolygon(pts, 3, 0L);
+			pts[0].x = ix+16;		pts[0].y = iy+12;		o->oPolygon(pts, 3, 0L);
+			break;
+		case 568:
+			if(cmd == OD_DRAWSELECTED) {
+				FillY.color |= 0x00808080L;			FillG.color |= 0x00808080L;
+				FillR.color |= 0x00808080L;			FillB.color |= 0x00808080L;
+				o->SetFill(&FillY);
+				pts[0].x = pts[3].x = rec->left+3;		pts[0].y = pts[1].y = rec->top+3;
+				pts[1].x = pts[2].x = rec->right-3;		pts[2].y = pts[3].y = rec->bottom-3;
+				o->oPolygon(pts, 4);
+				}
+			o->SetLine(cmd == OD_DRAWSELECTED ? &bLine : &Line);
+			if(cmd == OD_DRAWSELECTED) o->SetFill(&FillG);
+			pts[0].x = pts[1].x = rec->left+15;				pts[0].y = rec->top+3;
+			pts[1].y = rec->top+5;		pts[2].x = rec->left+12;	pts[2].y = rec->top+12;
+			pts[3].x = rec->left+7;		pts[3].y = pts[4].y = rec->top+15;
+			pts[4].x = rec->left+3;		pts[5].x = rec->left+3;		pts[5].y = rec->top+3;
+			o->oPolygon(pts, 6);
+			pts[0].x = pts[1].x = ix;	pts[2].x = ix+6;		pts[3].x = rec->right-7;
+			pts[4].x = rec->right-3;	pts[5].x = rec->right-3;	o->oPolygon(pts, 6);
+			pts[0].y = rec->bottom-3;	pts[1].y = rec->bottom-9;	pts[2].y = iy+3;
+			pts[3].x = rec->right-9;	pts[3].y = pts[4].y = iy-3;	pts[5].y = rec->bottom-3;
+			o->oPolygon(pts, 6);
+			pts[0].x = pts[1].x = rec->right-15;				pts[0].y = rec->bottom-3;
+			pts[1].y = rec->bottom-5;	pts[2].x = rec->right-12;	pts[2].y = rec->bottom-10;
+			pts[3].x = rec->right-7;	pts[3].y = pts[4].y = rec->bottom-15;
+			pts[4].x = rec->right-3;	pts[5].x = rec->right-3;	pts[5].y = rec->bottom-3;
+			if(cmd == OD_DRAWSELECTED) o->SetFill(&FillB);			o->oPolygon(pts, 6);
+			pts[0].x = pts[1].x = rec->left+15;				pts[0].y = rec->bottom-3;
+			pts[1].y = rec->bottom-5;	pts[2].x = rec->left+12;	pts[2].y = iy+5;
+			pts[3].x = rec->left+7;		pts[3].y = pts[4].y = iy-3;	pts[4].x = rec->left+3;
+			pts[5].x = rec->left+3;		if(cmd == OD_DRAWSELECTED) o->SetFill(&FillG);
+			o->oPolygon(pts, 6);
+			if(cmd == OD_DRAWSELECTED) o->SetFill(&FillR);	o->oCircle(ix-9, iy-9, ix+1, iy+3);
+			break;
+			}
+		o->UpdateRect(rec, false);
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Execute axis templates as owner drawn buttons
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void OD_AxisTempl(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	LineDEF Line = {0.0, 1.0, 0x0L, 0x0L};
+	LineDEF Grid = {0.0, 1.0, 0x00c0c0c0, 0x0L};
+	POINT pts[5];
+	int i, ty, tx, sx;
+
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		OD_BaseRect(o, cmd, rec);
+		tx = ty = 0;
+		switch(id) {
+		case 310:
+			o->oRectangle(rec->left+10, rec->top+6, rec->right-6, rec->bottom-10);
+			ty = rec->bottom -10;		tx = rec->left+8;
+			break;
+		case 311:
+			o->oRectangle(rec->left+10, rec->top+6, rec->right-6, rec->bottom-10);
+			pts[0].x = rec->left+10;		pts[1].x = rec->right-6;
+			pts[0].y = pts[1].y = rec->bottom-32;
+			o->oSolidLine(pts);
+			pts[0].y = rec->top+6;		pts[1].y = rec->bottom-10;
+			pts[0].x = pts[1].x = rec->left + 32;
+			o->oSolidLine(pts);
+			ty = rec->bottom -31;		tx = rec->left+30;
+			break;
+		case 312:
+			pts[0].x = rec->left+10;		pts[1].x = rec->right-6;
+			pts[0].y = pts[1].y = rec->bottom-11;
+			o->oSolidLine(pts);
+			pts[0].y = rec->top+6;		pts[1].y = rec->bottom-10;
+			pts[0].x = pts[1].x = rec->left + 10;
+			o->oSolidLine(pts);
+			ty = rec->bottom -10;		tx = rec->left+8;
+			break;
+		case 313:
+			pts[0].x = rec->left+10;		pts[1].x = rec->right-6;
+			pts[0].y = pts[1].y = rec->top + 9;
+			o->oSolidLine(pts);
+			pts[0].y = rec->top+10;			pts[1].y = rec->bottom-10;
+			pts[0].x = pts[1].x = rec->left + 10;
+			o->oSolidLine(pts);
+			ty = rec->top+7;			tx = rec->left+8;
+			break;
+		case 314:
+			pts[0].x = rec->left+10;		pts[1].x = rec->right-6;
+			pts[0].y = pts[1].y = rec->bottom-11;
+			o->oSolidLine(pts);
+			pts[0].y = rec->top+6;		pts[1].y = rec->bottom-10;
+			pts[0].x = pts[1].x = rec->left + 27;
+			o->oSolidLine(pts);
+			ty = rec->bottom -10;		tx = rec->left+25;
+			break;
+			}
+		if(ODtickstyle & 0x300) {
+			o->SetLine(&Grid);
+			pts[0].y = rec->top+7;		pts[1].y = rec->bottom-11;
+			if(id == 313) pts[0].y +=3;
+			if(ODtickstyle & 0x100) for(i = rec->left+16; i < rec->right-6; i+=12) {
+				pts[0].x = pts[1].x = i;
+				o->oSolidLine(pts);
+				}
+			pts[0].x = rec->left+11;	pts[1].x = rec->right-7;
+			if(ODtickstyle & 0x200) 
+				for(i = rec->bottom- (id == 313 ? 11 : 17); i > rec->top+6; i -=12) {
+				pts[0].y = pts[1].y = i;
+				o->oSolidLine(pts);
+				}
+			o->SetLine(&Line);
+			}
+		if(tx != ty) {
+			sx = 2;
+			switch(ODtickstyle & 0x03){
+			case 1:
+				if(id == 313) ty += 3;
+				else ty -= 3;
+				tx += 3;	
+				break;
+			case 2:
+				if(id == 313) ty += 1;
+				else ty -= 2;
+				tx += 1;
+#ifdef _WINDOWS
+				sx = 3;
+#endif
+				break;
+			default:
+				break;
+				}
+			pts[0].y = ty;	pts[1].y = ty+sx;
+			for(i = rec->left+10; i < rec->right-6; i+=6) {
+				pts[0].x = pts[1].x = i;
+				o->oSolidLine(pts);
+				}
+			pts[0].x = tx;	pts[1].x = tx+sx;
+			for(i = rec->bottom-11; i > rec->top+6; i -=6) {
+				pts[0].y = pts[1].y = i;
+				o->oSolidLine(pts);
+				}
+			}
+		o->UpdateRect(rec, false);
+		break;
+		}
+
+}
+
+void OD_AxisTempl3D(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	LineDEF Line = {0.0, 1.0, 0x0L, 0x0L};
+	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
+	POINT pts[5];
+	int x, y;
+
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		OD_BaseRect(o, cmd, rec);
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00c0c0c0L : 0x0L;
+		o->SetLine(&Line);						o->SetFill(&Fill);
+		switch(id) {
+		case 410:	case 411:
+			pts[0].x = rec->left+20;			pts[0].y = rec->bottom-14;
+			pts[1].x = rec->left+10;			pts[1].y = rec->bottom-10;
+			o->oSolidLine(pts);
+			pts[1].x = rec->right-10;			pts[1].y = pts[0].y + 3;
+			o->oSolidLine(pts);
+			pts[1].x = pts[0].x;				pts[1].y = rec->top+8;
+			o->oSolidLine(pts);
+			if(id == 411) {
+				pts[0].x = rec->left+10;		pts[0].y = rec->top+12;
+				o->oSolidLine(pts);
+				pts[0].x = rec->right-10;		pts[0].y = pts[1].y + 3;
+				o->oSolidLine(pts);
+				pts[1].x = pts[0].x;			pts[1].y = rec->bottom-11;
+				o->oSolidLine(pts);
+				pts[0].x = rec->right-20;		pts[0].y = rec->bottom-7;
+				o->oSolidLine(pts);
+				pts[1].x = rec->left+10;		pts[1].y = rec->bottom-10;
+				o->oSolidLine(pts);
+				pts[0].x = rec->left+10;		pts[0].y = rec->top+12;
+				o->oSolidLine(pts);
+				}
+			break;
+		case 412:
+			x = (rec->right+rec->left)>>1;		y = (rec->top+rec->bottom)>>1;
+			pts[0].x = rec->left+14;			pts[0].y = y+4;
+			pts[1].x = rec->right-14;			pts[1].y = y-2;
+			o->oSolidLine(pts);
+			pts[1].y += 6;	pts[0].y -= 6;	pts[1].x +=4;	pts[0].x -=4;
+			o->oSolidLine(pts);
+			pts[0].x = pts[1].x = x;	pts[0].y = y-15;	pts[1].y = y+15;
+			o->oSolidLine(pts);
+			}
+		o->UpdateRect(rec, false);
+		Line.color = 0x00000000L;		o->SetLine(&Line);
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 2D Plot: Execute axis templates for new axis as owner drawn buttons
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void OD_NewAxisTempl(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	POINT pts[5];
+	int i, ix, iy, step, d1, d2;
+
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		ix = (rec->right + rec->left)>>1;
+		iy = (rec->bottom + rec->top)>>1;
+		OD_BaseRect(o, cmd, rec);
+		d1 = d2 = 0;
+		switch(id) {
+		case 201:			d1 = -6;				d2 = -2;
+		case 202:			d2 -= 14;
+		case 203:			d1 += 6;				d2 += 3;
+		case 204:
+			d1 = d1 + ix -3;		d2 = d2 + ix +4;
+			pts[0].x = pts[1].x = ix;
+			pts[0].y = rec->top +9;	pts[1].y = rec->bottom -9;
+			step = ((pts[1].y - pts[0].y)/5)+1;
+			o->oSolidLine(pts);
+			pts[0].x = d1;		pts[1].x = ix;
+			for(i = rec->top +11; i <= rec->bottom-9; i += step) {
+				pts[0].y = pts[1].y = i;
+				o->oSolidLine(pts);
+				o->oRectangle(d2, i-1, d2+4, i+1);
+				}
+			break;
+		case 205:			d1 = 6;					d2 = 3;
+		case 206:			d2 += 10;
+		case 207:			d1 -= 6;				d2 -= 3;
+		case 208:
+			d1 = d1 + iy +3;		d2 = d2 + iy - 4;
+			pts[0].y = pts[1].y = iy;
+			pts[0].x = rec->left +9;	pts[1].x = rec->right -9;
+			step = ((pts[1].x - pts[0].x)/4)+1;
+			o->oSolidLine(pts);
+			pts[0].y = d1;		pts[1].y = iy;
+			for(i = rec->left +11; i <= rec->right-9; i += step) {
+				pts[0].x = pts[1].x = i;
+				o->oSolidLine(pts);
+				o->oRectangle(i-1, d2, i+2, d2+2);
+				}
+			break;
+			}
+		o->UpdateRect(rec, false);
+		break;
+		}
+}
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 3D Plot: Execute axis templates for new axis as owner drawn buttons
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void OD_NewAxisTempl3D(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id)
+{
+	LineDEF Line = {0.0, 1.0, 0x0L, 0x0L};
+	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
+	POINT spts[24];
+	int i, ix, iy, edge;
+	DWORD col1, col2;
+
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
+		col1 = cmd == OD_DRAWSELECTED ? 0x008080c0L : 0x00c8c8c8L;
+		col2 = cmd == OD_DRAWSELECTED ? 0x0000ff00L : 0x0000c000L;
+		o->SetLine(&Line);
+		ix = (rec->right + rec->left)>>1;		iy = (rec->bottom + rec->top)>>1;
+		spts[0].x = spts[3].x = spts[4].x = rec->left;
+		spts[0].y = spts[1].y = spts[4].y = rec->top;
+		spts[1].x = spts[2].x = rec->right-1;	spts[2].y = spts[3].y = rec->bottom-1;
+		o->oPolyline(spts, 5);					Line.color = 0x00000000L;
+		o->SetLine(&Line);					o->SetFill(&Fill);
+		o->oRectangle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
+		spts[0].x = spts[6].x = spts[8].x = spts[13].x = spts[18].x = spts[19].x = rec->left+20;		
+		spts[0].y = spts[13].y = spts[18].y = rec->bottom-14;
+		spts[1].x = spts[2].x = spts[4].x = spts[5].x = spts[7].x = spts[21].x = rec->left+10;
+		spts[1].y = spts[2].y = spts[4].y = rec->bottom-10;
+		spts[3].x = spts[15].x = spts[16].x = spts[17].x = spts[20].x = spts[22].x = rec->right-20;				
+		spts[3].y = spts[15].y = spts[16].y = spts[2].y + 3;
+		spts[5].y = spts[7].y = spts[21].y = rec->top+12;
+		spts[6].y = spts[8].y = spts[19].y = rec->top+8;
+		spts[9].x = spts[10].x = spts[11].x = spts[12].x = spts[14].x = spts[23].x = rec->right-10;
+		spts[9].y = spts[10].y = spts[23].y = spts[8].y+3;
+		spts[11].y = spts[12].y = spts[14].y = spts[0].y+3;
+		spts[17].y = spts[20].y = spts[22].y = spts[7].y+3;
+		switch(id) {
+			case 201:		edge = 4;			break;
+			case 202:		edge = 16;			break;
+			case 203:		edge = 10;			break;
+			case 204:		edge = 18;			break;
+			case 205:		edge = 2;			break;
+			case 206:		edge = 14;			break;
+			case 207:		edge = 12;			break;
+			case 208:		edge = 0;			break;
+			case 209:		edge = 20;			break;
+			case 210:		edge = 22;			break;
+			case 211:		edge = 8;			break;
+			case 212:		edge = 6;			break;
+			default:		edge = -1;			break;
+			}
+		Line.color = col1;				o->SetLine(&Line);
+		if(true)for(i = 0; i < 24; i+= 2) {
+			if(i == edge){
+				Line.color = col2;		o->SetLine(&Line);
+				o->oSolidLine(spts+i);
+				Line.color = col1;		o->SetLine(&Line);
+				}
+			else o->oSolidLine(spts+i);
+			}
+		o->UpdateRect(rec, false);
+		break;
+		}
+}
diff --git a/Output.cpp b/Output.cpp
index 9749dad..a8bb23d 100755
--- a/Output.cpp
+++ b/Output.cpp
@@ -1,1959 +1,1960 @@
-//Output.cpp, Copyright (c) 2000-2007 R.Lackner
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-#include <math.h>
-#include <stdlib.h>
-#include <string.h>
-#include <stdio.h>
-#include <fcntl.h>				//file open flags
-#include <sys/stat.h>			//I/O flags
-#ifdef _WINDOWS
-	#include <io.h>					//for read/write
-#else
-	#define O_BINARY 0x0
-	#include <unistd.h>
-#endif
-#include "rlplot.h"
-
-tag_Units Units[] = {{0, "mm", 1.0f}, {1, "cm", 10.0f}, {2, "inch", 25.4f},
-	};
-
-extern Default defs;
-extern GraphObj *CurrGO, *TrackGO;		//Selected Graphic Objects
-extern Label *CurrLabel;
-extern Graph *CurrGraph;
-extern dragHandle *CurrHandle;
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Output base class
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-anyOutput::anyOutput()
-{
-	units = defs.cUnits;
-	dBgCol = dFillCol = defs.Color(COL_BG);
-	xAxis.owner = yAxis.owner = zAxis.owner = (void *)this;
-	xAxis.flags = yAxis.flags = zAxis.flags = 0L;
-	xAxis.min = yAxis.min = zAxis.min = 0.0;
-	xAxis.max = yAxis.max = zAxis.max = 1.0;
-	xAxis.nBreaks = yAxis.nBreaks = zAxis.nBreaks = 0;
-	xAxis.breaks = yAxis.breaks = zAxis.breaks = 0L;
-	ddx = ddy = ddz = 1.0;
-	RLP.finc = 1.0f;
-	RLP.fp = 0.0f;
-	dPattern = 0xffffffffL;			//impossible (invisible)line pattern to start with
-	dBgCol = defs.Color(COL_BG);	OC_type = OC_UNKNOWN;
-	MrkMode = MRK_NONE;				MrkRect = 0L;
-	VPorg.fx = VPorg.fy = 0.0;		VPscale = 1.0;
-	MenuHeight = 0;					cCursor = MC_ARROW;
-	rotM[0][0] = rotM[1][1] = rotM[2][2] = 1.0;
-	rotM[0][1] = rotM[0][2] = rotM[1][0] = rotM[1][2] = rotM[2][0] = rotM[2][1] = 0.0;
-	hasHistMenu = false;			HistMenuSize = 0;
-	light_source.fx = light_source.fy = 0.0;
-}
-
-void
-anyOutput::SetRect(fRECT rec, int u, AxisDEF *x_ax, AxisDEF *y_ax)
-{
-	double spx, spy;
-
-	if (u >= 0 && u < NUM_UNITS) defs.cUnits = units = u;
-	else units = defs.cUnits;
-	spx = rec.Xmax - rec.Xmin;	spy = rec.Ymin -rec.Ymax;
-	MrkMode = MRK_NONE;
-	Box1.Xmin = co2fix(rec.Xmin);	Box1.Ymin = co2fiy(rec.Ymax);
-	Box1.Xmax = co2fix(rec.Xmax);	Box1.Ymax = co2fiy(rec.Ymin);
-	if(!x_ax || !y_ax) return;
-	if(x_ax->flags & AXIS_DEFRECT) {
-		Box1.Xmin = co2fix(x_ax->loc[0].fx);
-		Box1.Xmax = co2fix(x_ax->loc[1].fx);
-		spx = x_ax->loc[1].fx - x_ax->loc[0].fx;
-		}
-	if(y_ax->flags & AXIS_DEFRECT) {
-		Box1.Ymin = co2fiy(y_ax->loc[0].fy);
-		Box1.Ymax = co2fiy(y_ax->loc[1].fy);
-		spy = y_ax->loc[1].fy - y_ax->loc[0].fy;
-		}
-	memcpy(&xAxis, x_ax, sizeof(AxisDEF));
-	memcpy(&yAxis, y_ax, sizeof(AxisDEF));
-	ddy = GetAxisFac(&yAxis, un2fiy(spy), 1);
-	ddx = GetAxisFac(&xAxis, un2fix(spx), 0);
-	xAxis.owner = yAxis.owner = this;
-}
-
-void
-anyOutput::UseAxis(AxisDEF *ax, int type)
-{
-	AxisDEF *cax;
-
-	MrkMode = MRK_NONE;
-	if(!ax) return;
-	switch (type) {
-		case 1:			//x-axis
-			memcpy(cax = &xAxis, ax, sizeof(AxisDEF));
-			Box1.Xmin = co2fix(ax->loc[0].fx);
-			Box1.Xmax = co2fix(ax->loc[1].fx);
-			ddx = GetAxisFac(&xAxis, Box1.Xmax - Box1.Xmin, 0);
-			break;
-		case 2:			//y-axis
-			memcpy(cax = &yAxis, ax, sizeof(AxisDEF));
-			if(ax->flags & AXIS_3D) {
-				Box1.Ymax = co2fiy(ax->loc[0].fy);
-				Box1.Ymin = co2fiy(ax->loc[1].fy);
-				}
-			else {
-				Box1.Ymin = co2fiy(ax->loc[0].fy);
-				Box1.Ymax = co2fiy(ax->loc[1].fy);
-				}
-			ddy = GetAxisFac(&yAxis, Box1.Ymax - Box1.Ymin, 1);
-			break;
-		case 3:			//z-axis
-			memcpy(cax = &zAxis, ax, sizeof(AxisDEF));
-			Box1z.fx = un2fiz(ax->loc[0].fz);
-			Box1z.fy = un2fiz(ax->loc[1].fz);
-			ddz = GetAxisFac(&zAxis, Box1z.fy - Box1z.fx, 2);
-			break;
-		default:		//unnknown direction
-			return;
-		}
-	cax->owner = this;
-}
-
-void
-anyOutput::SetSpace(fPOINT3D *cub1, fPOINT3D *cub2, int u, double *rot, 
-	fPOINT3D *cent, AxisDEF *x_ax, AxisDEF *y_ax, AxisDEF *z_ax)
-{
-	double rotQ[6];		//rotation definition:
-						//  unit vector x
-						//              y
-						//              z
-						//  sin(phi)
-						//  cos(phi)
-						//  1.0 -cos(phi)
-	double dp;
-
-	if (u >= 0 && u < NUM_UNITS) defs.cUnits = units = u;
-	else units = defs.cUnits;
-	MrkMode = MRK_NONE;
-	HideTextCursor();
-	memcpy(&xAxis, x_ax, sizeof(AxisDEF));
-	memcpy(&yAxis, y_ax, sizeof(AxisDEF));
-	memcpy(&zAxis, z_ax, sizeof(AxisDEF));
-	xAxis.owner = yAxis.owner = zAxis.owner = this;
-	//assume resolution equal in all directions: use un2fix() for
-	//   all coordinates
-	Box1.Xmin = co2fix(cub1->fx);		Box1.Ymin = co2fiy(cub2->fy);
-	Box1.Xmax = co2fix(cub2->fx);		Box1.Ymax = co2fiy(cub1->fy);
-	Box1z.fx = un2fiz(cub1->fz);		Box1z.fy = un2fiz(cub2->fz);
-	if(x_ax->flags & AXIS_DEFRECT) {
-		Box1.Xmin = co2fix(x_ax->loc[0].fx);	Box1.Xmax = co2fix(x_ax->loc[1].fx);
-		}
-	if(y_ax->flags & AXIS_DEFRECT) {
-		Box1.Ymax = co2fiy(y_ax->loc[0].fy);	Box1.Ymin = co2fiy(y_ax->loc[1].fy);
-		}
-	if(z_ax->flags & AXIS_DEFRECT) {
-		Box1z.fx = un2fiz(z_ax->loc[0].fz);		Box1z.fy = un2fiz(z_ax->loc[1].fz);
-		}
-	ddx = GetAxisFac(&xAxis, Box1.Xmax-Box1.Xmin, 0);
-	ddy = GetAxisFac(&yAxis, Box1.Ymax-Box1.Ymin, 1);
-	ddz = GetAxisFac(&zAxis, Box1z.fy - Box1z.fx, 2);
-	rotC.fx = un2fix(cent->fx)+ VPorg.fx;
-	rotC.fy = un2fiy(cent->fy)+ VPorg.fy;	
-	rotC.fz = un2fiz(cent->fz);
-	memcpy(rotQ, rot, sizeof(rotQ));
-	//normalize vector part of rotQ
-	dp = sqrt(rotQ[0]*rotQ[0] + rotQ[1]*rotQ[1] + rotQ[2]*rotQ[2]);
-	rotQ[0] /= dp;		rotQ[1] /= dp;		rotQ[2] /= dp;
-	dp = sqrt(rotQ[0]*rotQ[0] + rotQ[1]*rotQ[1] + rotQ[2]*rotQ[2]);
-	//set up rotation matrix from quaternion
-	//see: Graphic Gems, A.S. Glassner ed.; Academic Press Inc.
-	//M.E. Pique: Rotation Tools
-	// ISBN 0-12-286165-5, p. 466
-	rotM[0][0] = rotQ[5]*rotQ[0]*rotQ[0] + rotQ[4];
-	rotM[0][1] = rotQ[5]*rotQ[0]*rotQ[1] + rotQ[3]*rotQ[2];
-	rotM[0][2] = rotQ[5]*rotQ[0]*rotQ[2] - rotQ[3]*rotQ[1];
-	rotM[1][0] = rotQ[5]*rotQ[0]*rotQ[1] - rotQ[3]*rotQ[2];
-	rotM[1][1] = rotQ[5]*rotQ[1]*rotQ[1] + rotQ[4];
-	rotM[1][2] = rotQ[5]*rotQ[1]*rotQ[2] + rotQ[3]*rotQ[0];
-	rotM[2][0] = rotQ[5]*rotQ[0]*rotQ[2] + rotQ[3]*rotQ[1];
-	rotM[2][1] = rotQ[5]*rotQ[1]*rotQ[2] - rotQ[3]*rotQ[0];
-	rotM[2][2] = rotQ[5]*rotQ[2]*rotQ[2] + rotQ[4];
-}
-
-void
-anyOutput::LightSource(double x, double y)
-{
-	int i, j, m;
-	double angx, angy;
-	double a[3][3], b[3][3];
-
-	if(light_source.fx == 0.0 || light_source.fy == 0.0 ||
-		x != light_source.fx || y != light_source.fy) {
-		light_source.fx = x;		light_source.fy = y;
-		angx = x * 0.017453292;		angy = y * 0.017453292;
-		for (i = 0; i < 3; i++)	for(j = 0; j < 3; j++) {
-			a[i][j] = b[i][j] = 0.0;
-			}
-		//first axis
-		a[0][0] = 1.0;			a[1][1] = cos(angx);		a[1][2] = -sin(angx);
-		a[2][1] = -a[1][2];		a[2][2] = a[1][1];
-		//second axis
-		b[0][0] = cos(angy);	b[0][1] = -sin(angy);		b[1][0] = -b[0][1];
-		b[1][1] = b[0][0];		b[2][2] = 1.0;
-		//combine the two rotations
-		for (i = 0; i < 3; i++) for(j = 0; j < 3; j++){
-			light_vec[i][j] = 0.0;
-			for(m = 0; m < 3; m++) light_vec[i][j] += (a[i][m] * b[m][j]);
-			}
-		}
-}
-
-DWORD
-anyOutput::VecColor(double *plane_vec, DWORD color1, DWORD color2)
-{
-	double v[3], vec[3], vlength;
-	int i, j;
-
-	//rotate vector towards the light source
-	if(!plane_vec) return color1;
-	v[0] = plane_vec[0];		v[1] = plane_vec[2];	v[2] = plane_vec[1];
-	for (i = 0; i < 3; i++) for(j = 0, vec[i] = 0.0; j < 3; j++)
-		vec[i] += (light_vec[i][j] * v[j]);
-	//normalize vec: both vector should have unit length but make sure
-	vlength = sqrt(vec[0]*vec[0]+vec[1]*vec[1]+vec[2]*vec[2]);
-	if(vlength < 0.9) return color1;
-	vec[0] /= vlength;	vec[1] /= vlength;	vec[2] /= vlength;
-	//calc color
-	return IpolCol(color1, color2, fabs(vec[1]));
-}
-
-bool
-anyOutput::GetSize(RECT *rc)
-{
-	memcpy(rc, &DeskRect, sizeof(RECT));
-	return true;
-}
-
-double
-anyOutput::fx2fix(double x)
-{
-	double temp;
-
-	x = TransformValue(&xAxis, x, true);
-	temp = (x - xAxis.min)*ddx;
-	if(0 == (xAxis.flags & AXIS_INVERT)) return temp + Box1.Xmin;
-	return Box1.Xmax-temp;
-}
-
-double
-anyOutput::fy2fiy(double y)
-{
-	double temp;
-
-	y = TransformValue(&yAxis, y, true);
-	temp = (y - yAxis.min)*ddy;
-	if(AXIS_INVERT == (yAxis.flags & AXIS_INVERT)) return temp + Box1.Ymin;
-	return Box1.Ymax-temp;
-}
-
-double
-anyOutput::fz2fiz(double z)
-{
-	double temp;
-
-	z = TransformValue(&zAxis, z, true);
-	temp = (z - zAxis.min)*ddz;
-	if(0 == (zAxis.flags & AXIS_INVERT)) return temp + Box1z.fx;
-	return Box1z.fy-temp;
-}
-
-bool 
-anyOutput::fp2fip(lfPOINT *fdp, lfPOINT *fip)
-{
-	double x, y, si, csi, temp;
-
-	if((xAxis.flags & AXIS_ANGULAR) && (yAxis.flags & AXIS_RADIAL)) {
-		x = 6.283185307 * TransformValue(&xAxis, fdp->fx + xAxis.Start, true)/(xAxis.max-xAxis.min);
-		si = sin(x);					csi = cos(x);
-		y = TransformValue(&yAxis, fdp->fy, true);
-		temp = (y - yAxis.min)*ddy;
-		if(yAxis.flags & AXIS_INVERT) temp = Box1.Ymax - Box1.Ymin - temp;
-		fip->fx = ((Box1.Xmin + Box1.Xmax)/2.0) + csi * temp;
-		if(xAxis.flags & AXIS_INVERT) fip->fy = Box1.Ymax + si * temp;
-		else fip->fy = Box1.Ymax - si * temp;
-		fip->fy += disp_y;
-		return true;
-		}
-	else {
-		fip->fx = fx2fix(fdp->fx);		fip->fy = fy2fiy(fdp->fy);
-		return true;
-		}
-	return false;
-}
-
-bool 
-anyOutput::fvec2ivec(fPOINT3D *v, fPOINT3D *iv)
-{
-	double x, y, z;
-	
-	if(!v || !iv) return false;
-	x = fx2fix(v->fx)-rotC.fx;
-	y = fy2fiy(v->fy)-rotC.fy;
-	z = fz2fiz(v->fz)-rotC.fz;
-	iv->fx = x * rotM[0][0] + y * rotM[0][1] + z * rotM[0][2] + rotC.fx;
-	iv->fy = x * rotM[1][0] + y * rotM[1][1] + z * rotM[1][2] + rotC.fy;
-	iv->fz = x * rotM[2][0] + y * rotM[2][1] + z * rotM[2][2] + rotC.fz;
-	iv->fx += disp_x;			iv->fy += disp_y;
-	return true;
-}
-
-bool
-anyOutput::cvec2ivec(fPOINT3D *v, fPOINT3D *iv)
-{
-	double x, y, z;
-	
-	if(!v || !iv) return false;
-	x = co2fix(v->fx)-rotC.fx;
-	y = co2fiy(v->fy)-rotC.fy;
-	z = un2fiz(v->fz)-rotC.fz;
-	iv->fx = x * rotM[0][0] + y * rotM[0][1] + z * rotM[0][2] + rotC.fx;
-	iv->fy = x * rotM[1][0] + y * rotM[1][1] + z * rotM[1][2] + rotC.fy;
-	iv->fz = x * rotM[2][0] + y * rotM[2][1] + z * rotM[2][2] + rotC.fz;
-	iv->fx += disp_x;			iv->fy += disp_y;
-	return true;
-}
-
-bool
-anyOutput::uvec2ivec(fPOINT3D *v, fPOINT3D *iv)
-{
-	double x, y, z;
-	
-	if(!v || !iv) return false;
-	x = un2fix(v->fx);
-	y = un2fiy(v->fy);
-	z = un2fiz(v->fz);
-	iv->fx = x * rotM[0][0] + y * rotM[0][1] + z * rotM[0][2];
-	iv->fy = x * rotM[1][0] + y * rotM[1][1] + z * rotM[1][2];
-	iv->fz = x * rotM[2][0] + y * rotM[2][1] + z * rotM[2][2];
-	return true;
-}
-
-double
-anyOutput::un2fix(double x)
-{
-	return (x * VPscale * hres*Units[units].convert/25.4);
-}
-
-double
-anyOutput::un2fiy(double y)
-{
-	return (y * VPscale * vres*Units[units].convert/25.4);
-}
-
-double
-anyOutput::un2fiz(double z)
-{
-	return (z * VPscale * hres*Units[units].convert/25.4);
-}
-
-double
-anyOutput::fix2un(double fix)
-{
-	return (fix/Units[units].convert*25.4/hres)/VPscale;
-}
-
-double
-anyOutput::fiy2un(double fiy)
-{
-	return (fiy/Units[units].convert*25.4/vres)/VPscale;
-}
-
-bool
-anyOutput::GetLine(LineDEF *lDef)
-{
-	if(lDef) {
-		lDef->width = LineWidth;
-		lDef->color = dLineCol;
-		lDef->pattern = dPattern;
-		return true;
-		}
-	return false;
-}
-
-bool
-anyOutput::SetTextSpec(TextDEF *set)
-{
-	memcpy(&TxtSet, set, sizeof(TextDEF));
-	TxtSet.text = 0L;
-	return true;
-}
-
-bool
-anyOutput::ShowMark(void *src, int Mode)
-{
-	GraphObj *go;
-
-	if(MrkMode != MRK_NONE) HideMark();
-	MrkMode = Mode;			MrkRect = src;		HideCopyMark();
-	switch (Mode) {
-		case MRK_INVERT:
-			return UpdateRect((RECT*)src, true);
-		case MRK_GODRAW:	case MRK_SSB_DRAW:
-			go = (GraphObj *) src;				go->DoMark(this, true);
-			CurrGO = go;
-			if(CurrLabel && CurrLabel != CurrGO) {
-				HideTextCursor();				CurrLabel = 0L;
-				}
-			return true;
-		}
-	return false;
-}
-
-bool
-anyOutput::HideMark()
-{
-	switch(MrkMode) {
-		case MRK_NONE:
-			return true;
-		case MRK_INVERT:
-			MrkMode = MRK_NONE;
-			return UpdateRect((RECT*)MrkRect, false);
-		case MRK_GODRAW:
-			MrkMode = MRK_NONE;					//inhibit reentrance
-			if(CurrLabel && CurrLabel->Command(CMD_HIDEMARK, 0L, this))
-				CurrGraph->Command(CMD_REDRAW, 0L, this);
-			else if(MrkRect)((GraphObj*)MrkRect)->DoMark(this, false);
-			else if(CurrGraph) CurrGraph->Command(CMD_REDRAW, 0L, this);
-			return true;
-		case MRK_SSB_DRAW:
-			MrkMode = MRK_NONE;
-			if (MrkRect) ((GraphObj*)MrkRect)->DoMark(this, false);
-			return true;
-		}
-	return false;
-}
-
-int
-anyOutput::CalcCursorPos(char *txt, POINT p, POINT *fit)
-{
-	int i, d, w, h, CurrPos;
-
-	d = TxtSet.iSize >>2;
-	if(!txt || !fit) return 0;
-	if (!(i = (int)strlen(txt)))return 0;
-	//right justified text
-	if(TXA_HRIGHT == (TxtSet.Align & TXA_HRIGHT)){
-		if((p.x - fit->x) < d) return i;
-		for (CurrPos = i-1; CurrPos >= 0; CurrPos--) {
-			if(!oGetTextExtent(txt+CurrPos, i-CurrPos, &w, &h)) return 0;
-			if((w = p.x - w - d) <= fit->x) return CurrPos;
-			}
-		return 0;
-		}
-	//left justified text
-	else {
-		if((fit->x - p.x) < d) return 0;
-		for (CurrPos = i; CurrPos >= 0; CurrPos--) {
-			if(!oGetTextExtent(txt, CurrPos, &w, &h)) return 0;
-			if((w = p.x + w - d) <= fit->x) return CurrPos;
-			}
-		}
-	return 0;
-}
-
-bool
-anyOutput::TextCursor(char *txt, POINT p, POINT *fit, int *pos, int dx)
-{
-	int i, w, h, CurrPos;
-	RECT disp;
-
-	if(fit) CurrPos = CalcCursorPos(txt, p, fit);
-	//recalculate caret position
-	if(txt && pos && !fit){
-		if(TxtSet.Align & TXA_HRIGHT) {		//right justfied text
-			if((i = (int)strlen(txt)-(*pos))){
-				if(!oGetTextExtent(txt+(*pos), i, &w, &h)) return false;
-				w = p.x - w;
-				}
-			else w = p.x-1;
-			}
-		else {								//left justified text
-			if(!(*pos)) w = 0;
-			else if(!oGetTextExtent(txt, *pos, &w, &h))return false;
-			w += p.x;
-			}
-		}
-	else if(!fit)return false;
-	//right justified text: search caret and cursor position
-	else if(txt && (TxtSet.Align & TXA_HRIGHT)){
-		i = (int)strlen(txt);
-		if(i == CurrPos) w = 1;
-		else if(!oGetTextExtent(txt+CurrPos, i-CurrPos, &w, &h)) return false;
-		w = p.x - w;
-		}
-	//left justified text: search caret and cursor position
-	else if(txt && fit) {
-		if (!CurrPos) w = 0;
-		else if(!oGetTextExtent(txt, CurrPos, & w, &h)) return false;
-		w += p.x;
-		}
-	if(fit && pos) *pos = CurrPos;
-	disp.left = disp.right = w+dx;
-	disp.top = p.y;
-	if(TxtSet.Align & TXA_VCENTER) {
-		disp.top -= (TxtSet.iSize>>1);
-		}
-#ifdef _WINDOWS
-	disp.bottom = disp.top + TxtSet.iSize-1;
-#else
-	disp.top -= 1;
-	disp.bottom = disp.top + iround(TxtSet.iSize*1.25)-2;
-#endif
-	ShowTextCursor(this, &disp, 0x0L);
-	return true;
-}
-
-//we need our own implementation of Bresenham's line drawing algorithm to draw
-//   a line with variable pattern sizes.
-//Ref: P.S. Heckbert (1990) "Digital Line Drawing", in: Graphic Gems
-//   (A.S. Glassner, ed.); Academic Press, Inc.,
-//   ISBN 0-12-286165-5
-bool
-anyOutput::PatLine(POINT p1, POINT p2)
-{
-	int d, ax, ay, sx, sy, dx, dy;
-	double fInc2;
-	bool bPen;
-	POINT tr[2];
-
-	dx = p2.x - p1.x;
-	fInc2 = RLP.finc * 0.414213562;			//increment by sqrt(2) if 45� slope
-	if ( p2.x < p1.x) { 	ax = (-dx)<<1;		sx = -1;		}
-	else {					ax = dx <<1;		sx = 1;		}
-	dy = p2.y - p1.y;
-	if (p2.y < p1.y) {	ay = (-dy)<<1;		sy = -1;		}
-	else {					ay = dy<<1;			sy = 1;		}
-	tr[0].x = tr[1].x = p1.x;							tr[0].y = tr[1].y = p1.y;
-	if(dPattern &(1 << ((int)RLP.fp))) bPen = false;
-	else bPen = true;
-	if (ax > ay) {													// x dominant
-		d = ay - (ax >>1);
-		for ( ; ; ) {
-			RLP.fp += RLP.finc;
-			if(RLP.fp >= 32.0f) RLP.fp -= 32.0f;
-			if(bPen) {
-				if(tr[1].x == p2.x) return oSolidLine(tr);
-				if (d >= 0) {tr[1].y += sy;	d -= ax; RLP.fp += fInc2;}
-				tr[1].x += sx;
-				if(dPattern &(1 << ((int)RLP.fp))) {
-					bPen = false;
-					oSolidLine(tr);
-					tr[0].x = tr[1].x; tr[0].y = tr[1].y;
-					}
-				}
-			else {
-				if(tr[0].x == p2.x) return true;
-				if (d >= 0) {tr[0].y += sy;	d -= ax; RLP.fp += fInc2;}
-				tr[0].x += sx;
-				if(!(dPattern &(1 << ((int)RLP.fp)))) {
-					bPen = true;
-					tr[1].x = tr[0].x; tr[1].y = tr[0].y;
-					}
-				}
-			d += ay;
-			}
-		}
-	else {															// y dominant
-		d = ax - (ay >>1);
-		for ( ; ; ) {
-			RLP.fp += RLP.finc;
-			if(RLP.fp >= 32.0f) RLP.fp -= 32.0f;
-			if (bPen){
-				if (tr[1].y == p2.y) return oSolidLine(tr);
-				if (d >= 0) {tr[1].x += sx; d -= ay; RLP.fp += fInc2;}
-				tr[1].y += sy;
-				if(dPattern &(1 << ((int)RLP.fp))) {
-					bPen = false;
-					oSolidLine(tr);
-					tr[0].x = tr[1].x; tr[0].y = tr[1].y;
-					}
-				}
-			else {
-				if (tr[0].y == p2.y) return true;
-				if (d >= 0) {tr[0].x += sx; d -= ay; RLP.fp += fInc2;}
-				tr[0].y += sy;
-				if(!(dPattern &(1 << ((int)RLP.fp)))) {
-					bPen = true;
-					tr[1].x = tr[0].x; tr[1].y = tr[0].y;
-					}
-				}
-			d += ax;
-			}
-		}
-}
-
-static int Helv_Char_Width [] = {
- 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
- 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
- 15, 17, 20, 31, 31, 49, 37, 11, 18, 18, 21, 32, 15, 18, 15, 15,
- 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 15, 15, 32, 32, 32, 31,
- 56, 37, 37, 40, 40, 37, 34, 43, 40, 15, 28, 37, 31, 45, 40, 43,
- 37, 43, 40, 37, 33, 40, 37, 54, 35, 35, 34, 15, 15, 15, 24, 31,
- 18, 31, 31, 28, 31, 31, 15, 31, 31, 11, 13, 28, 11, 47, 31, 31,
- 31, 31, 18, 28, 15, 31, 29, 39, 27, 27, 27, 18, 14, 18, 32, 41,
- 31, 41, 12, 31, 18, 55, 31, 31, 18, 56, 37, 18, 55, 41, 34, 41,
- 41, 12, 12, 18, 18, 19, 31, 55, 16, 55, 28, 18, 52, 41, 27, 37,
- 15, 17, 31, 31, 31, 31, 14, 31, 18, 41, 20, 31, 32, 18, 41, 30,
- 22, 30, 18, 18, 18, 32, 30, 15, 18, 18, 20, 31, 46, 46, 46, 34,
- 37, 37, 37, 37, 37, 37, 55, 40, 37, 37, 37, 37, 15, 15, 15, 15,
- 40, 40, 43, 43, 43, 43, 43, 32, 43, 40, 40, 40, 40, 37, 37, 34,
- 31, 31, 31, 31, 31, 31, 49, 28, 31, 31, 31, 31, 15, 15, 15, 15,
- 31, 31, 31, 31, 31, 31, 31, 30, 34, 31, 31, 31, 31, 28, 31, 28};
-
-/*
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 18, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 18, 18, 24, 42, 39, 66, 51, 18, 24, 24, 30, 42, 18, 45, 18, 21,
- 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 18, 18, 45, 45, 45, 36,
- 75, 51, 51, 54, 54, 48, 42, 57, 54, 24, 39, 54, 42, 63, 54, 54,
- 48, 54, 51, 48, 48, 54, 51, 66, 51, 48, 45, 21, 21, 21, 36, 42,
- 18, 39, 42, 36, 42, 39, 24, 42, 39, 18, 18, 36, 18, 60, 42, 39,
- 42, 42, 27, 36, 24, 42, 39, 54, 36, 39, 36, 24, 18, 24, 42, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
- 18, 18, 39, 42, 39, 42, 18, 39, 24, 57, 27, 42, 45, 24, 57, 24,
- 27, 42, 21, 21, 21, 42, 36, 18, 21, 21, 27, 42, 57, 57, 57, 36,
- 51, 51, 51, 51, 51, 51, 69, 54, 48, 48, 48, 48, 24, 24, 24, 24,
- 54, 54, 54, 54, 54, 54, 54, 42, 54, 54, 54, 54, 54, 48, 48, 45,
- 39, 39, 39, 39, 39, 39, 63, 36, 39, 39, 39, 39, 18, 18, 18, 18,
- 39, 42, 39, 39, 39, 39, 39, 42, 39, 42, 42, 42, 42, 39, 42, 39};
-*/
-static int Cour_Char_Width [] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 35, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
- 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35};
-
-
-
-bool
-anyOutput::oGetTextExtent(char *text, int cb, int *width, int *height)
-{
-	int *CharWidth;
-	int i, w;
-
-	switch (TxtSet.Font) {
-	case FONT_COURIER:		CharWidth = Cour_Char_Width;	break;
-	default:				CharWidth = Helv_Char_Width;	break;
-		}
-	if(!cb && text) cb = (int)strlen(text);
-	for(i = w = 0; i < cb; i++) w += ((unsigned)text[i] < 256 ? CharWidth[(unsigned)text[i]] : 35);
-	*width = iround(((double)w * (double)TxtSet.iSize)/52.0);
-	*height = TxtSet.iSize;
-	return true;
-}
-
-bool
-anyOutput::oGetTextExtentW(w_char *text, int cb, int *width, int *height)
-{
-	if(cb < 1) for(cb = 0; text[cb]; cb++);
-	*width = (TxtSet.iSize * cb)>>1;
-	*height = TxtSet.iSize;
-	return true;
-}
-
-bool 
-anyOutput::oSphere(int cx, int cy, int r, POINT *pts, int cp, char *nam)
-{
-	FillDEF fd;
-	HatchOut *ho;
-	int i, j;
-
-	if(pts && cp) oPolygon(pts, cp, nam);
-	else oCircle(cx - r, cy - r, cx + r + 1, cy + r + 1, nam);
-	fd.color = dFillCol;		fd.color2 = dFillCol2;
-	fd.hatch = 0L;				fd.scale = 1.0;
-	fd.type = FILL_NONE;
-	if(ho = new HatchOut(this)) {
-		ho->SetFill(&fd);
-		ho->light_source.fx = light_source.fx;
-		ho->light_source.fy = light_source.fy;
-		for(i = 0; i < 3; i++) for (j = 0; j < 3; j++) {
-			ho->light_vec[i][j] = light_vec[i][j];
-			}
-		ho->oSphere(cx, cy, r, pts, cp, 0L);
-		delete(ho);
-		return true;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Process hatch patterns in an output class
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-enum {HO_NONE, HO_RECT, HO_CIRCLE, HO_ELLIPSE, HO_BIGELLYPSE,
-	HO_POLYGON};
-struct _HatchDef{
-	union {
-		struct {
-			RECT rec;
-			}rec;
-		struct {
-			POINT centre;
-			long sr;
-			}cir;
-		struct {
-			POINT centre;
-			int ix, iy;
-			unsigned long sab;
-			}ell;
-		struct {
-			POINT fo[2];
-			unsigned int rsab;
-			int a, b;
-			}bell;
-		struct {
-			POINT *pts;
-			int cp;
-			}plg;
-		};
-}HatchDef;
-
-HatchOut::HatchOut(anyOutput *Parent):anyOutput()
-{
-	ParInit = false;
-	ho = HO_NONE;
-	ht = FILL_COMBS;
-	out = Parent;
-	xbase = ybase = 1.5;
-	units = 0;					//use mm for defaults
-	if(Parent) out->GetSize(&DeskRect);
-	else DeskRect.left = DeskRect.right = DeskRect.top = DeskRect.bottom = 0;
-	MyLineDef.width = 0.0f;
-	MyLineDef.patlength = 10.0f;
-	MyLineDef.color = 0x00ff0000L;
-	MyLineDef.pattern = 0x00000000L;
-}
-
-
-HatchOut::~HatchOut()
-{
-}
-
-bool
-HatchOut::SetFill(FillDEF *fill)
-{
-	if(!fill) return false;
-	ht = (fill->type & 0xff);
-	if(fill->hatch) memcpy(&MyLineDef, fill->hatch, sizeof(LineDEF));
-	//we assume that all operations are at a 1:1 pixel relation to the parent,
-	//  but we use un2fix and un2fiy from the parent: correct for zoom ....
-	switch(out->units) {
-	case 0:						//parent uses mm
-		xbase = out->un2fix(1.5);		ybase = out->un2fiy(1.5);
-		break;
-	case 1:						//parent uses cm
-		xbase = out->un2fix(0.15);		ybase = out->un2fiy(0.15);
-		break;
-	case 2:						//parent uses inches
-		xbase = out->un2fix(0.059);		ybase = out->un2fiy(0.059);
-		break;
-		}
-	if(fill->scale >0.05f && fill->scale < 20.0) {
-		xbase *= fill->scale;		ybase *= fill->scale;
-		}
-	dFillCol = fill->color;
-	dFillCol2 = fill->color2;
-	return true;
-}
-
-bool
-HatchOut::StartPage()
-{
-	if(out) out->GetSize(&DeskRect);
-	return true;
-}
-
-bool
-HatchOut::oCircle(int x1, int y1, int x2, int y2, char *nam)
-{
-	long tmp;
-
-	if(x1 < x2) {		UseRect.left = x1;		UseRect.right = x2;		}
-	else {				UseRect.left = x2;		UseRect.right = x1;		}
-	if(y1 < y2) {		UseRect.top = y1;			UseRect.bottom = y2;		}
-	else {				UseRect.top = y2;			UseRect.bottom = y1;		}
-	if((UseRect.right -UseRect.left)==(UseRect.bottom-UseRect.top)) {
-		HatchDef.cir.centre.x = (UseRect.right + UseRect.left)/2;
-		HatchDef.cir.centre.y = (UseRect.bottom + UseRect.top)/2;
-		tmp = (UseRect.right - UseRect.left)/2;
-		HatchDef.cir.sr = (long)tmp * (long)tmp-1;
-		if(HatchDef.cir.sr >9) HatchDef.cir.sr -= tmp;	//stay inside circle
-		ho = HO_CIRCLE;
-		PrepareParent(false);
-		return DoHatch();
-		}
-	//for small ellipses use the centered equation
-	if((UseRect.right -UseRect.left) <512 && (UseRect.bottom-UseRect.top)<512) {
-		ho = HO_ELLIPSE;
-		HatchDef.ell.centre.x = (UseRect.right + UseRect.left)/2;
-		HatchDef.ell.centre.y = (UseRect.bottom + UseRect.top)/2;
-		HatchDef.ell.ix = tmp = (UseRect.right - UseRect.left)/2;
-		HatchDef.ell.sab = tmp * tmp;
-		HatchDef.ell.iy = tmp = (UseRect.bottom - UseRect.top)/2;
-		HatchDef.ell.sab *= (tmp * tmp);
-		PrepareParent(false);
-		return DoHatch();
-		}
-	//for bigger ellipses we use the focuses to describe the ellipse
-	//  this reduces numerical problems
-	ho = HO_BIGELLYPSE;
-	HatchDef.bell.fo[0].x= HatchDef.bell.fo[1].x= (UseRect.right+UseRect.left)/2;
-	HatchDef.bell.fo[0].y= HatchDef.bell.fo[1].y= (UseRect.bottom+UseRect.top)/2;
-	if((UseRect.right -UseRect.left) >(UseRect.bottom-UseRect.top)){
-		HatchDef.bell.rsab =	UseRect.right - UseRect.left;
-		HatchDef.bell.a = (UseRect.right - UseRect.left)/2;
-		HatchDef.bell.b = (UseRect.bottom - UseRect.top)/2;
-		tmp = isqr(HatchDef.bell.a*HatchDef.bell.a -
-			HatchDef.bell.b*HatchDef.bell.b);
-		HatchDef.bell.fo[0].x -= tmp;
-		HatchDef.bell.fo[1].x += tmp;
-		}
-	else {
-		HatchDef.bell.rsab =	UseRect.bottom - UseRect.top;
-		HatchDef.bell.b = (UseRect.right - UseRect.left)/2;
-		HatchDef.bell.a = (UseRect.bottom - UseRect.top)/2;
-		tmp = isqr(HatchDef.bell.a*HatchDef.bell.a -
-			HatchDef.bell.b*HatchDef.bell.b);
-		HatchDef.bell.fo[0].y -= tmp;
-		HatchDef.bell.fo[1].y += tmp;
-		}
-	PrepareParent(false);
-	return DoHatch();
-}
-
-bool
-HatchOut::oSphere(int cx, int cy, int r, POINT *pts, int cp, char *nam)
-{
-	double v[3], vec[3];
-	int i, j;
-
-	ht = FILL_LIGHT3D;
-
-	v[0] = 0.0;		v[1] = 1.0;		v[2] = 0.0;
-	for (i = 0; i < 3; i++) for(j = 0, vec[i] = 0.0; j < 3; j++)
-		vec[i] += (light_vec[i][j] * v[j]);
-	circ_grad.cx = cx + iround(vec[0]*((double)r));
-	circ_grad.cy = cy - iround(vec[2]*((double)r));
-	circ_grad.r = r;
-	if(pts && cp) return oPolygon(pts, cp, nam);
-	return oCircle(cx - r, cy - r, cx + r + 1, cy + r + 1, nam);
-}
-
-bool
-HatchOut::oRectangle(int x1, int y1, int x2, int y2, char *nam)
-{
-	if(x1 < x2) {
-		HatchDef.rec.rec.left = UseRect.left = x1;
-		HatchDef.rec.rec.right = UseRect.right = x2-1;
-		}
-	else {
-		HatchDef.rec.rec.left = UseRect.left = x2;
-		HatchDef.rec.rec.right = UseRect.right = x1-1;
-		}
-	if(y1 < y2) {
-		HatchDef.rec.rec.top = UseRect.top = y1;
-		HatchDef.rec.rec.bottom = UseRect.bottom = y2-1;
-		}
-	else {
-		HatchDef.rec.rec.top = UseRect.top = y2;
-		HatchDef.rec.rec.bottom = UseRect.bottom = y1-1;
-		}
-	ho = HO_RECT;
-	PrepareParent(false);
-	return DoHatch();
-}
-
-bool
-HatchOut::oPolygon(POINT *pts, int cp, char *nam)
-{
-	int i;
-	POINT *p;
-
-	p = (POINT*)malloc((cp+2)*(sizeof(POINT)));
-	HatchDef.plg.pts = p;
-	if(!p || cp < 3)return false;
-	HatchDef.plg.pts[0].x = UseRect.left = UseRect.right = pts[0].x;
-	HatchDef.plg.pts[0].y = UseRect.top = UseRect.bottom = pts[0].y;
-	for(i = 1; i < cp; i++){
-		UseRect.left = UseRect.left < pts[i].x ? UseRect.left : pts[i].x;
-		UseRect.right = UseRect.right > pts[i].x ? UseRect.right : pts[i].x;
-		UseRect.top = UseRect.top < pts[i].y ? UseRect.top : pts[i].y;
-		UseRect.bottom = UseRect.bottom > pts[i].y ? UseRect.bottom : pts[i].y;
-		p[i].x = pts[i].x;		p[i].y = pts[i].y;
-		}
-	i--;
-	if(p[i].x != pts[0].x || p[i].y != pts[0].y) {
-		i++;
-		p[i].x = pts[0].x;		p[i].y = pts[0].y;
-		}
-	HatchDef.plg.cp = i+1;
-	ho= HO_POLYGON;
-	PrepareParent(false);
-	return DoHatch();
-}
-
-bool
-HatchOut::PrepareParent(bool Restore)
-{
-	if(Restore){
-		if(out && ParInit)out->SetLine(&ParLineDef);
-      }
-	else if(out) {
-		out->GetLine(&ParLineDef);
- 		ParInit = out->SetLine(&MyLineDef);
-		}
-	return true;
-}
-
-bool
-HatchOut::DoHatch()
-{
-	MkPolyLine(NULL, NULL);
-	switch(ht){
-	case FILL_NONE:											break;
-	case FILL_HLINES:		Lines000();						break;
-	case FILL_VLINES:		Lines090();						break;
-	case FILL_HVCROSS:		Lines000();		Lines090();		break;
-	case FILL_DLINEU:		Lines045();						break;
-	case FILL_DLINED:		Lines315();						break;
-	case FILL_DCROSS:		Lines045();		Lines315();		break;
-	case FILL_STIPPLE1:		case FILL_STIPPLE2:		
-	case FILL_STIPPLE3:		case FILL_STIPPLE4:
-	case FILL_STIPPLE5:		Stipple(ht);					break;
-	case FILL_ZIGZAG:		Zigzag();						break;
-	case FILL_COMBS:		Combs();						break;
-	case FILL_BRICKH:		BricksH();						break;
-	case FILL_BRICKV:		BricksV();						break;
-	case FILL_BRICKDU:		Bricks045();					break;
-	case FILL_BRICKDD:		Bricks315();					break;
-	case FILL_TEXTURE1:		Texture1();						break;
-	case FILL_TEXTURE2:		Texture2();						break;
-	case FILL_WAVES1:		Arcs(FILL_WAVES1);				break;
-	case FILL_SCALES:		Arcs(FILL_SCALES);				break;
-	case FILL_SHINGLES:		Arcs(FILL_SHINGLES);			break;
-	case FILL_WAVES2:		Waves2(0);						break;
-	case FILL_HERRING:		Herringbone();					break;
-	case FILL_CIRCLES:		Circles();						break;
-	case FILL_GRASS:		Grass();						break;
-	case FILL_FOAM:			Foam();							break;
-	case FILL_RECS:			Recs();							break;
-	case FILL_HASH:			Hash();							break;
-	case FILL_WATER:		Waves2(1);						break;
-	case FILL_LIGHT3D:		CircGrad();						break;
-		}
-
-	//clean up
-	if(ho == HO_POLYGON) {
-		if(HatchDef.plg.pts) free(HatchDef.plg.pts);
-		HatchDef.plg.pts = NULL;
-		}
-	ho = HO_NONE;
-	MkPolyLine(NULL, out);
-	return PrepareParent(true);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// collect line segments to a polyline command
-#define MK_PL_MAX 1000
-bool
-HatchOut::MkPolyLine(POINT *p, anyOutput *o)
-{
-	static POINT pl[MK_PL_MAX];
-	static long npt = 0;
-
-	if(p && o) {
-		if(!npt) {
-			memcpy(pl, p, 2*sizeof(POINT));
-			npt = 2;
-			return true;
-			}
-		if(p[0].x != pl[npt-1].x || p[0].y != pl[npt-1].y) {
-			o->oPolyline(pl, npt);
-			memcpy(pl, p, 2*sizeof(POINT));
-			npt = 2;
-			return true;
-			}
-		AddToPolygon(&npt, pl, p+1);
-		if(npt > (MK_PL_MAX-1)) {
-			npt = 0;
-			return o->oPolyline(pl, MK_PL_MAX);
-			}
-		return true;
-		}
-	if(!p && !o) {
-		npt = 0;
-		return true;
-		}
-	if(!p && o && npt) {
-		o->oPolyline(pl, npt);
-		npt = 0;
-		return true;
-      }
-	return false;
-}
-
-//use Bresenham's algorithm to draw lines
-//Ref: P.S. Heckbert (1990) "Digital Line Drawing", in: Graphic Gems
-//   (A.S. Glassner, ed.); Academic Press, Inc.,
-//   ISBN 0-12-286165-5
-bool
-HatchOut::HatchLine(POINT p1, POINT p2)
-{
-	int d, ax, ay, sx, sy, dx, dy;
-	bool bPen;
-	POINT tr[2];
-
-	dx = p2.x - p1.x;
-	if ( p2.x < p1.x) { 	ax = (-dx)<<1;		sx = -1;		}
-	else {					ax = dx <<1;		sx = 1;		}
-	dy = p2.y - p1.y;
-	if (p2.y < p1.y) {	ay = (-dy)<<1;		sy = -1;		}
-	else {					ay = dy<<1;			sy = 1;		}
-	tr[0].x = tr[1].x = p1.x;					tr[0].y = tr[1].y = p1.y;
-	if(IsInside(p1)) bPen = true;
-	else bPen = false;
-	if (ax > ay) {													// x dominant
-		d = ay - (ax >>1);
-		for ( ; ; ) {
-			if(bPen) {
-				if(tr[1].x == p2.x) return MkPolyLine(tr, out);
-				if (d >= 0) {tr[1].y += sy;	d -= ax;}
-				tr[1].x += sx;
-				if(!IsInside(tr[1])) {
-					bPen = false;
-					MkPolyLine(tr, out);
-					tr[0].x = tr[1].x;	tr[0].y = tr[1].y;
-					}
-				}
-			else {
-				if(tr[0].x == p2.x) return true;
-				if (d >= 0) {tr[0].y += sy;	d -= ax;}
-				tr[0].x += sx;
-				if(IsInside(tr[0])){
-					bPen = true;
-					tr[1].x = tr[0].x;	tr[1].y = tr[0].y;
-					}
-				}
-			d += ay;
-			}
-		}
-	else {															// y dominant
-		d = ax - (ay >>1);
-		for ( ; ; ) {
-			if (bPen){
-				if (tr[1].y == p2.y) return MkPolyLine(tr, out);
-				if (d >= 0) {tr[1].x += sx; d -= ay;}
-				tr[1].y += sy;
-				if(!IsInside(tr[1])) {
-					bPen = false;
-					MkPolyLine(tr, out);
-					tr[0].x = tr[1].x;	tr[0].y = tr[1].y;
-					}
-				}
-			else {
-				if (tr[0].y == p2.y) return true;
-				if (d >= 0) {tr[0].x += sx; d -= ay;}
-				tr[0].y += sy;
-				if(IsInside(tr[0])) {
-					bPen = true;
-					tr[1].x = tr[0].x;	tr[1].y = tr[0].y;
-					}
-				}
-			d += ax;
-			}
-		}
-}
-
-//use circular Bresenham's algorithm to draw arcs
-//Ref: C. Montani, R. Scopigno (1990) "Speres-To-Voxel Conversion", in:
-//   Graphic Gems (A.S. Glassner ed.) Academic Press, Inc.; 
-//   ISBN 0-12-288165-5 
-bool
-HatchOut::HatchArc(int ix, int iy, int r, int qad, bool start)
-{
-	int x, y, q, di, de, lim;
-	bool bInside;
-	static POINT tr[2];
-
-	if(r < 1) r = 1;
-	if(start) {
-		tr[1].x = ix-r;		tr[1].y = iy;
-		}
-	for(q = 0; q < qad; q++) {
-		x = lim = 0;	y = r;	di = 2*(1-r);
-		bInside = IsInside(tr[1]);
-		while (y >= lim){
-			if(di < 0) {
-				de = 2*di + 2*y -1;
-				if(de > 0) {
-					x++;	y--;	di += (2*x -2*y +2);
-					}
-				else {
-					x++;	di += (2*x +1);
-					}
-				}
-			else {
-				de = 2*di -2*x -1;
-				if(de > 0) {
-					y--;	di += (-2*y +1);
-					}
-				else {
-					x++;	y--;	di += (2*x -2*y +2);
-					}
-				}
-			tr[0].x = tr[1].x;		tr[0].y = tr[1].y;
-			switch(q) {
-			case 0:	tr[1].x = ix-y;		tr[1].y = iy+x;	break;
-			case 1: tr[1].x = ix+x;		tr[1].y = iy+y;	break;
-			case 2: tr[1].x = ix+y;		tr[1].y = iy-x;	break;
-			case 3:	tr[1].x = ix-x;		tr[1].y = iy-y;	break;
-				}
-			if(IsInside(tr[1])){
-				if(bInside)MkPolyLine(tr, out);
-				bInside = true;
-				}
-			else {
-				if(bInside) MkPolyLine(0L, out);
-				bInside = false;
-				}
-			}
-		}
-	return true;
-}
-
-bool
-HatchOut::IsInside(POINT p)
-{
-	long tmp1, tmp2, tmp3, tmp4;
-
-	if(out->OC_type != OC_HIMETRIC && (p.x < DeskRect.left || p.x > DeskRect.right || p.y < DeskRect.top
-		|| p.y >DeskRect.bottom)) return false;
-	switch(ho){
-	case HO_RECT:
-		if(p.x > HatchDef.rec.rec.left && p.x < HatchDef.rec.rec.right &&
-			p.y > HatchDef.rec.rec.top && p.y < HatchDef.rec.rec.bottom)
-			return true;
-		return false;
-	case HO_CIRCLE:
-		tmp1 = p.x-HatchDef.cir.centre.x;
-		tmp2 = p.y-HatchDef.cir.centre.y;
-		if((tmp1 * tmp1 + tmp2 * tmp2) < HatchDef.cir.sr)
-			return true;
-		return false;
-	case HO_ELLIPSE:
-		tmp1 = p.x-HatchDef.ell.centre.x;
-		tmp2 = p.y-HatchDef.ell.centre.y;
-		tmp3 = HatchDef.ell.iy;
-		tmp4 = HatchDef.ell.ix;
-		if((unsigned long)(tmp1 * tmp1 * tmp3 * tmp3 + tmp2 * tmp2 * tmp4 * tmp4) < HatchDef.ell.sab)
-			return true;
-		return false;
-	case HO_BIGELLYPSE:
-		tmp1 = HatchDef.bell.fo[0].x - p.x;			tmp1 *= tmp1;
-		tmp2 = HatchDef.bell.fo[0].y - p.y;			tmp2 *= tmp2;
-		tmp3 = HatchDef.bell.fo[1].x - p.x;			tmp3 *= tmp3;
-		tmp4 = HatchDef.bell.fo[1].y - p.y;			tmp4 *= tmp4;
-		return (isqr(tmp1+tmp2)+isqr(tmp3+tmp4)) < HatchDef.bell.rsab;
-	case HO_POLYGON:
-		return IsInPolygon(&p, HatchDef.plg.pts, HatchDef.plg.cp);
-		}
-	return false;
-}
-
-void
-HatchOut::Lines000()
-{
-	int y, yinc;
-	POINT Line[2];
-
-	if(2>(yinc = iround(ybase*.8)))yinc = 2;
-	Line[0].x = UseRect.left;
-	Line[1].x = UseRect.right;
-	for(y = UseRect.top; y < UseRect.bottom; y += yinc) {
-		Line[0].y = Line[1].y = y;
-		HatchLine(Line[0], Line[1]);
-		}
-}
-
-void
-HatchOut::Lines090()
-{
-	int x, xinc;
-	POINT Line[2];
-
-	if(2>(xinc = iround(xbase*.8)))xinc = 2;
-	Line[0].y = UseRect.top;
-	Line[1].y = UseRect.bottom;
-	for(x = UseRect.left; x < UseRect.right; x += xinc) {
-		Line[0].x = Line[1].x = x;
-		HatchLine(Line[0], Line[1]);
-		}
-}
-
-void
-HatchOut::Lines045()
-{
-	int x, y, y1, xinc, yinc;
-	POINT Line[2];
-
-	if(3>(xinc=iround(xbase*1.2)))xinc=3;		if(3>(yinc=iround(ybase*1.2)))yinc=3;
-	Line[1].x = x = UseRect.right;
-	Line[0].y = Line[1].y = y = UseRect.bottom;
-	while(x > UseRect.left) {
-		Line[0].x = x = x-xinc;
-		if(y > UseRect.top) Line[1].y = y = y-yinc;
-		else Line[1].x -= xinc;
-		HatchLine(Line[0], Line[1]);
-		}
-	y1 = Line[0].y;
-	while(y1 > UseRect.top) {
-		Line[0].y = y1 = y1-yinc;
-		if(y > UseRect.top) Line[1].y = y = y-yinc;
-		else Line[1].x -= xinc;
-		HatchLine(Line[0], Line[1]);
-		}
-}
-
-void
-HatchOut::Lines315()
-{
-	int x, y, y1, xinc, yinc;
-	POINT Line[2];
-
-	if(3>(xinc=iround(xbase*1.2)))xinc= 3;	if(3>(yinc=iround(ybase*1.2)))yinc= 3;
-	Line[1].x = x = UseRect.right;
-	Line[0].y = Line[1].y = y = UseRect.top;
-	while (x > UseRect.left) {
-		Line[0].x = x = x-xinc;
-		if(y < UseRect.bottom) Line[1].y = y = y+yinc;
-		else Line[1].x -= xinc;
-		HatchLine(Line[0], Line[1]);
-		}
-	y1 = Line[0].y;
-	while(y1 < UseRect.bottom) {
-		Line[0].y = y1 = y1+yinc;
-		if(y < UseRect.bottom) Line[1].y = y = y+yinc;
-		else Line[1].x -= xinc;
-		HatchLine(Line[0], Line[1]);
-		}
-}
-
-void
-HatchOut::Stipple(int type)
-{
-	int x, y, xinc, yinc, level, xspac, yspac;
-	POINT Line[2];
-
-	if(!(xinc = iround(xbase*0.48)))xinc = 1;	if(!(yinc = iround(ybase*0.48)))yinc = 1;
-	if(!(xspac = iround(xbase*0.56)))xspac = 1;	if(!(yspac = iround(ybase*0.56)))yspac = 1;
-	level = 0;
-	for(x = UseRect.left; x < UseRect.right; x += xspac*2) {
-		level &= 0x1;
-		for(y = UseRect.top; y < UseRect.bottom; y += yspac*4) {
-			if(type < FILL_STIPPLE3) {
-				Line[0].x = x;
-				Line[0].y = level? y+yinc+yspac*2 : y+yinc;
-				Line[1].x = Line[0].x+xinc;		Line[1].y = Line[0].y-yinc;
-				if(type == FILL_STIPPLE1)HatchLine(Line[0], Line[1]);
-				Line[0].x = Line[1].x;	Line[0].y = Line[1].y;
-				Line[1].x += xinc;		Line[1].y += yinc;
-				if(type == FILL_STIPPLE1)HatchLine(Line[0], Line[1]);
-				Line[0].x = Line[1].x;	Line[0].y = Line[1].y;
-				Line[1].x -= xinc;		Line[1].y += yinc;
-				HatchLine(Line[0], Line[1]);
-				Line[0].x = Line[1].x;	Line[0].y = Line[1].y;
-				Line[1].x -= xinc;				Line[1].y -= yinc;
-				HatchLine(Line[0], Line[1]);
-				}
-			else if(type <= FILL_STIPPLE5) {
-				Line[0].x = x;
-				Line[0].y =Line[1].y = level? y+yinc+yspac*2 : y+yinc;
-				Line[1].x = Line[0].x+xinc*2;
-				if(type == FILL_STIPPLE3 || type == FILL_STIPPLE4)HatchLine(Line[0], Line[1]);
-				Line[0].x = Line[1].x = x+xinc;	
-				Line[0].y = Line[1].y -yinc;
-				Line[1].y += yinc;
-				if(type == FILL_STIPPLE3 || type == FILL_STIPPLE5)HatchLine(Line[0], Line[1]);
-				}
-			}
-		level++;
-		}
-}
-
-void
-HatchOut::Zigzag()
-{
-	int yinc, ix, iy;
-	POINT Line[2];
-
-	if(3>(yinc = iround(ybase)))yinc=3;
-	if(2>(iy = iround(ybase*.8)))iy=2;		if(2>(ix = iround(xbase*.8)))ix=2;
-	Line[0].x = Line[1].x = UseRect.left;
-	Line[0].y = Line[1].y = UseRect.top;
-	while(Line[0].y < UseRect.bottom +iy) {
-		while(Line[1].x < UseRect.right) {
-			Line[1].y -= iy;					Line[1].x += ix;
-			HatchLine(Line[0], Line[1]);
-			Line[0].x = Line[1].x;			Line[1].x += ix;
-			Line[0].y = Line[1].y;			Line[1].y += iy;
-			HatchLine(Line[0], Line[1]);
-			Line[0].x = Line[1].x;			Line[0].y = Line[1].y;
-			}
-		Line[0].x = Line[1].x = UseRect.left;
-		Line[0].y = Line[1].y += yinc;
-		}
-}
-
-void
-HatchOut::Combs()
-{
-	int x, y, xinc, yinc;
-	POINT Line[2], Next;
-
-	if(!(yinc = iround(ybase*.4)))yinc = 1;
-	if(2 >(xinc = iround(xbase*.69282))) xinc = 2;		//exact yinc *sin(60�)*2
-	y = UseRect.top + yinc;
-	while(y < UseRect.bottom + yinc*2) {
-		Line[0].y = Line[1].y = y;
-		Line[0].x = Line[1].x = UseRect.left-xinc;
-		while(Line[1].x < UseRect.right && Line[1].y >= UseRect.top){
-			Line[1].y -= (yinc*2);
-			HatchLine(Line[0], Line[1]);
-			Line[0].x = Line[1].x;			Line[1].x += xinc;
-			Line[0].y = Line[1].y;			Line[1].y -= yinc;
-			HatchLine(Line[0], Line[1]);
-			Line[0].x = Line[1].x;			Line[0].y = Line[1].y;
-			Line[1].x += xinc;				Line[1].y += yinc;
-			HatchLine(Line[0], Line[1]);
-			Line[1].x -= xinc;				Line[1].y -= yinc;
-			}
-		y += yinc*6;
-		}
-	Next.x = x = UseRect.left-xinc;					Next.y = y;
-	while(x < UseRect.right) {
-		Line[0].y = Line[1].y = Next.y;
-		Line[0].x = Line[1].x = x;
-		while(Line[1].x < UseRect.right&& Line[1].y >= UseRect.top){
-			Line[1].y -= (yinc*2);
-			HatchLine(Line[0], Line[1]);
-			Line[0].x = Line[1].x;			Line[1].x += xinc;
-			Line[0].y = Line[1].y;			Line[1].y -= yinc;
-			HatchLine(Line[0], Line[1]);
-			Line[0].x = Line[1].x;			Line[0].y = Line[1].y;
-			Line[1].x += xinc;				Line[1].y += yinc;
-			HatchLine(Line[0], Line[1]);
-			Line[1].x -= xinc;				Line[1].y -= yinc;
-			}
-		x += xinc*2;
-		}
-}
-
-void
-HatchOut::BricksH()
-{
-	int i, j, y, yinc, xinc;
-	POINT Line[2];
-
-	if(4>(xinc=iround(xbase*1.6)))xinc=4;	if(2>(yinc=iround(ybase*0.8)))yinc=2;
-	for(y = UseRect.top, j = 0; y < UseRect.bottom; y += yinc, j++) {
-		Line[0].x = UseRect.left;
-		Line[1].x = UseRect.right;
-		Line[0].y = Line[1].y = y;
- 		HatchLine(Line[0], Line[1]);
-		Line[0].y ++;								Line[1].y += yinc;
-		for (i = (j&1)?UseRect.left:UseRect.left+xinc/2;i<UseRect.right;i+=xinc){
-			Line[0].x = Line[1].x = i;
-			HatchLine(Line[0], Line[1]);
-			}
-		}
-}
-
-void
-HatchOut::BricksV()
-{
-	int i, j, x, yinc, xinc;
-	POINT Line[2];
-
-	if(2>(xinc=iround(xbase*0.8)))xinc=2;	if(4>(yinc=iround(ybase*1.6)))yinc=4;
-	for(x = UseRect.left, j= 0; x < UseRect.right; x += xinc, j++) {
-		Line[0].y = UseRect.top;
-		Line[1].y = UseRect.bottom;
-		Line[0].x = Line[1].x = x;
-		HatchLine(Line[0], Line[1]);
-		Line[0].x ++;								Line[1].x += xinc;
-		for (i = (j&1)?UseRect.top:UseRect.top+yinc/2;i<UseRect.bottom;i+=yinc){
-			Line[0].y = Line[1].y = i;
-			HatchLine(Line[0], Line[1]);
-			}
-		}
-}
-
-void
-HatchOut::Bricks045()
-{
-	int xinc, yinc, bwx, bwy;
-	POINT Line[2];
-
-	if(2>(xinc=iround(xbase*.7)))xinc = 2;	if(2>(yinc=iround(ybase*.7)))yinc = 2;
-	bwx = xinc *2;						bwy = yinc *2;
-	Line[0].x = UseRect.left - xinc;
-	Line[0].y = UseRect.top;
-	while((Line[0].y < UseRect.bottom + bwy)) {
-		while(Line[0].x < UseRect.right) {
-			Line[1].x = Line[0].x + bwx;
-			Line[1].y = Line[0].y - bwy;
-			HatchLine(Line[0], Line[1]);
-			Line[0].x = Line[1].x;
-			Line[0].y = Line[1].y;
-			Line[1].x = Line[0].x + xinc;
-			Line[1].y = Line[0].y + yinc;
-			HatchLine(Line[0], Line[1]);
-			Line[0].y += bwy;
-			}
-		Line[0].y += bwy;
-		Line[0].x = UseRect.left - xinc;
-		}
-}
-
-void
-HatchOut::Bricks315()
-{
-	int xinc, yinc, bwx, bwy;
-	POINT Line[2];
-
-	if(2>(xinc=iround(xbase*.7)))xinc = 2;	if(2>(yinc=iround(ybase*.7)))yinc = 2;
-	bwx = xinc *2;							bwy = yinc *2;
-	Line[0].x = UseRect.left - xinc;
-	Line[0].y = UseRect.top -bwy;
-	while((Line[0].y < UseRect.bottom)) {
-		while(Line[0].x < UseRect.right) {
-			Line[1].x = Line[0].x + bwx;
-			Line[1].y = Line[0].y + bwy;
-			HatchLine(Line[0], Line[1]);
-			Line[0].x = Line[1].x;
-			Line[0].y = Line[1].y;
-			Line[1].x = Line[0].x + xinc;
-			Line[1].y = Line[0].y - yinc;
-			HatchLine(Line[0], Line[1]);
-			Line[0].y -= bwy;
-			}
-		Line[0].y += bwy;
-		Line[0].x = UseRect.left - xinc;
-		}
-}
-
-void
-HatchOut::Texture1()
-{
-	int j, xinc, yinc;
-	POINT Line[2];
-
-	if(!(xinc = iround(xbase*0.4)))xinc = 1;	if(!(yinc = iround(ybase*0.4)))yinc = 1;
-	Line[0].x = UseRect.left - xinc;
-	Line[0].y = UseRect.top - yinc;
-	j = 0;
-	while((Line[0].y < UseRect.bottom)) {
-		while(Line[0].x < UseRect.right) {
-			Line[1].x = Line[0].x;
-			Line[1].y = Line[0].y + (yinc *2);
-			HatchLine(Line[0], Line[1]);
-			Line[0].x += xinc;
-			Line[0].y += yinc;
-			Line[1].x = Line[0].x + (xinc *2);
-			Line[1].y = Line[0].y;
-			HatchLine(Line[0], Line[1]);
-			Line[0].x += xinc*3;
-			Line[0].y -= yinc;
-			}
-		j++;
-		Line[0].y += yinc *2;
-		Line[0].x = UseRect.left - xinc;
-		if(j &0x01) Line[0].x += (xinc * 2);
-		}
-}
-
-
-void
-HatchOut::Texture2()
-{
-	int j, xinc, yinc;
-	POINT Line[2];
-
-	if(2>(xinc=iround(xbase*.6)))xinc= 2;	if(2>(yinc=iround(ybase*.6)))yinc= 2;
-	Line[0].x = UseRect.left - xinc*2;		Line[0].y = UseRect.top - yinc*2;
-	j = 0;
-	while((Line[0].y < UseRect.bottom)) {
-		while(Line[0].x < UseRect.right) {
-			Line[1].x = Line[0].x;			Line[1].y = Line[0].y + (yinc *3);
-			HatchLine(Line[0], Line[1]);
-			Line[0].x += xinc;				Line[1].x = Line[0].x;
-			HatchLine(Line[0], Line[1]);
-			Line[0].y += yinc;				Line[1].x = Line[0].x + (xinc *3);
-			Line[1].y = Line[0].y;
-			HatchLine(Line[0], Line[1]);
-			Line[0].y += yinc;				Line[1].y = Line[0].y;
-			HatchLine(Line[0], Line[1]);
-			Line[0].x += xinc*3;			Line[0].y -= yinc*2;
-			}
-		j++;
-		Line[0].y += yinc *2;		Line[0].x = UseRect.left - xinc*2;
-		if(j &0x01) Line[0].x += (xinc *2);
-		}
-}
-
-void
-HatchOut::Arcs(int type)
-{
-	int i, j, level, ix, iy;
-
-	if(type == FILL_SHINGLES) {
-		iy = iround(ybase*1.6);			ix = iround(xbase*.8);
-		}
-	else {
-		iy = iround(ybase*.8);			ix = iround(xbase*.8);
-		}
-	if(iy < 2) iy = 2;				if(ix < 2) ix = 2;
-	UseRect.right += ix;		UseRect.top -= (iy<<1);	UseRect.bottom += (iy<<1);
-	for(i = UseRect.top, level = 0; i < UseRect.bottom; i+= iy, level++) {
-		if(type == FILL_WAVES1) {
-			HatchArc(UseRect.left, i, ix, 0, true);
-			for(j = UseRect.left; j < UseRect.right; j += ix*2) HatchArc(j, i, ix, 2, false);
-			i += iy/3;
-			}
-		else if(type == FILL_SCALES) {
-			HatchArc(UseRect.left, i, ix, 0, true);
-			for(j = UseRect.left; j < UseRect.right; j += ix*2)
-				HatchArc((level &1) ? j+ix:j, i, ix, 2, false);
-			i++;
-			}
-		else {
-			for(j = UseRect.left; j < UseRect.right; j += ix*2){
-				HatchArc((level &1) ? j+ix:j, i-1, ix, 0, true);
-				HatchArc((level &1) ? j+ix:j, i + iy/2, ix, 2, false);
-				}
-			i++;
-			}
-		}
-}
-
-void
-HatchOut::Waves2(int type)					//hatch using sine waves
-{
-	int i, j, level, y, ix, yinc, *pts;
-	POINT Line[2];
-	double dtmp;
-
-	if(3>(yinc = iround(type?ybase*.8 : ybase*1.2)))yinc = 3;	
-	if(type == 0 && 14>(ix = iround(xbase*2.5)))ix = 14;
-	else if(type == 1 && 7>(ix = iround(xbase*1.2)))ix = 7;
-	if(!(pts = (int *)malloc(ix * sizeof(int))))return;
-	for(i = 0; i < ix; i++) {
-		dtmp = sin(6.283185307/((double)ix/(double)i));
-		if(type == 1) dtmp /= 2.0;
-		pts[i] = dtmp > 0.0 ? iround(0.3*ybase*dtmp) : iround(0.3*ybase*dtmp);
-		}
-	UseRect.bottom += yinc;					UseRect.right++;
-	for(y = UseRect.top, level = 0; y <= UseRect.bottom; y += yinc, level++){
-		Line[0].x = UseRect.left;			Line[1].x = UseRect.left+1;
-		Line[0].y = y;						Line[1].y = y+pts[1];
-		if(type == 0) for(j = 2; Line[0].x < UseRect.right; j++) {
-			HatchLine(Line[0], Line[1]);
-			Line[0].x = Line[1].x;			Line[1].x++;
-			Line[0].y = Line[1].y;			Line[1].y = y + pts[j%ix];
-			}
-		else if(type == 1) {
-			if(level & 1) {
-				Line[0].x += (ix + (ix>>1));		Line[1].x = Line[0].x +1;
-				}
-			for(j = 2; Line[0].x < UseRect.right; j++){
-				HatchLine(Line[0], Line[1]);
-				Line[0].x = Line[1].x;
-				Line[0].y = Line[1].y;				Line[1].y = y + pts[j%ix];
-				if((j-1) % ix) Line[1].x++;
-				else {
-					HatchLine(Line[0], Line[1]);
-					Line[1].x += (ix << 1);			Line[0].x = Line[1].x -1;
-					}
-				}
-			}
-		}
-	free(pts);
-}
-
-void
-HatchOut::Herringbone()
-{
-	int ix1, ix2, iy1, iy2, y;
-	POINT Line[2];
-
-	if(2>(ix1 = iround(xbase*.6)))ix1 = 2;		ix2 = ix1*4;
-	if(2>(iy1 = iround(ybase*.6)))iy1 = 2;		iy2 = iy1*4;
-	for(y = UseRect.top; y <= UseRect.bottom + iy2; y += iy1*2) {
-		Line[0].x = UseRect.left-ix2;		Line[1].x = Line[0].x + ix2;
-		Line[0].y = y;						Line[1].y = Line[0].y - iy2;
-		do {
-			HatchLine(Line[0], Line[1]);
-			Line[0].x = Line[1].x - ix1;	Line[1].x = Line[0].x + ix2;
-			Line[0].y = Line[1].y - iy1;	Line[1].y = Line[0].y + iy2;
-			HatchLine(Line[0], Line[1]);
-			Line[0].x = Line[1].x - ix1;	Line[1].x = Line[0].x + ix2;
-			Line[0].y = Line[1].y + iy1;	Line[1].y = Line[0].y - iy2;
-			} while(Line[0].x <= UseRect.right);
-		}
-}
-
-void
-HatchOut::Circles()
-{
-	int x, y, r, level, xspac, yspac;
-
-	if(2 > (xspac = iround(+xbase*.8))) xspac = 2;
-	if(4 > (yspac = iround(ybase*1.38564))) yspac = 4;
-	if(1 > (r = iround(xbase*.4)))r = 1;
-	level = 0;
-	UseRect.bottom += yspac;		UseRect.right += r;
-	for(x = UseRect.left; x < UseRect.right; x += xspac) {
-		level &= 0x1;
-		for(y = UseRect.top; y < UseRect.bottom; y += yspac*2) {
-			HatchArc(x, level? y+yspac : y, r, 4, true);
-			}
-		level++;
-		}
-}
-
-void
-HatchOut::Grass()
-{
-	int i, count, dh, dw;
-	double xsize, ysize;
-	long idum = -1;
-	POINT pts[2];
-
-	xsize = xbase *2.0;		ysize = ybase*2.0;
-	if(xsize < 4.0) xsize = 4.0;	if(ysize < 4.0) ysize = 4.0;
-	IncrementMinMaxRect(&UseRect, (int)xsize);
-	count = (UseRect.right -UseRect.left)*(UseRect.bottom-UseRect.top);
-	i = (int)(xsize*ysize*0.15);		if(i) count /= i;
-	dh = UseRect.bottom-UseRect.top;	dw = UseRect.right-UseRect.left;
-	for(i = 0; i < count; i++) {
-		pts[0].x = UseRect.left+(int)(dw*ran2(&idum));
-		pts[0].y = UseRect.top+(int)(dh*ran2(&idum));
-		pts[1].x = pts[0].x + (int)(ran2(&idum)*xsize);
-		pts[1].y = pts[0].y + (int)(ran2(&idum)*ysize);
-		if(pts[0].x != pts[1].x || pts[0].y != pts[1].y) HatchLine(pts[0], pts[1]);
-		}
-}
-
-void
-HatchOut::Foam()
-{
-	int i, count, dh, dw;
-	double xsize;
-	long idum = -1;
-
-	xsize = xbase *0.9;
-	if(xsize < 2.0) xsize = 2.0;
-	IncrementMinMaxRect(&UseRect, (int)xsize);
-	count = (UseRect.right -UseRect.left)*(UseRect.bottom-UseRect.top);
-	count /= (int)(xsize*xsize);
-	dh = UseRect.bottom-UseRect.top;	dw = UseRect.right-UseRect.left;
-	for(i = 0; i < count; i++) {
-		HatchArc(UseRect.left+(int)(dw*ran2(&idum)), UseRect.top+(int)(dh*ran2(&idum)),
-			(int)(ran2(&idum)*ran2(&idum)*xsize +xsize*.2), 4, true);
-		}
-}
-
-void
-HatchOut::Recs()
-{
-	int i, count, dh, dw;
-	double xsize, ysize;
-	long idum = -1;
-	POINT Line[5];
-
-	xsize = xbase *2.8;	ysize = ybase *2.8;
-	if(xsize < 4.0) xsize = 4.0;		if(ysize < 4.0) ysize = 4.0;
-	IncrementMinMaxRect(&UseRect, (int)xsize);
-	count = (UseRect.right -UseRect.left)*(UseRect.bottom-UseRect.top);
-	i = (int)(floor(xsize*ysize*.4));	if(i) count /= i;
-	dh = UseRect.bottom-UseRect.top;	dw = UseRect.right-UseRect.left;
-	for(i = 0; i < count; i++) {
-		Line[0].x = Line[3].x = Line[4].x = UseRect.left+(int)(dw*ran2(&idum));
-		Line[0].y = Line[1].y = Line[4].y = UseRect.top+(int)(dh*ran2(&idum));
-		Line[1].x = Line[2].x = Line[0].x + (int)(ran2(&idum)*xsize +xsize*.2);
-		Line[2].y = Line[3].y = Line[0].y + (int)(ran2(&idum)*ysize +ysize*.2);
-		HatchLine(Line[0], Line[1]);			HatchLine(Line[1], Line[2]);
-		HatchLine(Line[2], Line[3]);			HatchLine(Line[3], Line[4]);
-		}
-}
-
-void
-HatchOut::Hash()
-{
-	int i, dh, dw, cx, cy, xinc, yinc;
-	double xsize, ysize, mix, miy;
-	long idum = -1;
-	POINT Line[5];
-
-	xsize = xbase * 0.9;						ysize = ybase * 0.9;
-	xinc = iround(xsize * 3.3);					yinc = iround(ysize * 2.2);
-	mix = xbase *.5;							miy = ybase *.5;
-	dw = iround(xbase > 5 ? xbase/2.0 : 2.0);	dh = iround(ybase > 5 ? xbase/2.0 : 2.0);
-	if(xsize < 2.0) xsize = 2.0;		if(ysize < 2.0) ysize = 2.0;
-	IncrementMinMaxRect(&UseRect, (int)xsize);
-	for(i = 0, cy = UseRect.top; cy < UseRect.bottom; i++, cy += yinc) {
-		for(cx = (i & 1) ? UseRect.left:UseRect.left+xinc/2; cx < UseRect.right; cx += xinc) { 
-			Line[0].x = Line[1].x = cx;
-			Line[0].y = cy - iround(ran2(&idum) * ysize + miy);
-			Line[1].y = cy + iround(ran2(&idum) * ysize + miy);
-			HatchLine(Line[0], Line[1]);
-			Line[0].x = Line[1].x = cx + dw;
-			Line[0].y = cy - iround(ran2(&idum) * ysize + miy);
-			Line[1].y = cy + iround(ran2(&idum) * ysize + miy);
-			HatchLine(Line[0], Line[1]);
-			Line[0].y = Line[1].y = cy;
-			Line[0].x = cx - iround(ran2(&idum) * xsize + mix);
-			Line[1].x = cx + iround(ran2(&idum) * xsize + mix);
-			HatchLine(Line[0], Line[1]);
-			Line[0].y = Line[1].y = cy + dh;
-			Line[0].x = cx - iround(ran2(&idum) * xsize + mix);
-			Line[1].x = cx + iround(ran2(&idum) * xsize + mix);
-			HatchLine(Line[0], Line[1]);
-			}
-		}
-}
-
-void
-HatchOut::CircGrad()
-{
-	int i;
-	double f;
-	LineDEF ld = {0.0, 1.0, 0x0, 0x0};
-
-	for(i = 1; i < circ_grad.r; i++) {
-		f = (((double)i)/((double)circ_grad.r));
-		f = f*f;
-		ld.color = IpolCol(dFillCol, dFillCol2, f);
-		out->SetLine(&ld);
-		HatchArc(circ_grad.cx, circ_grad.cy, i, 4, true);
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// export to *.tif file (tag image file)
-// This code is based on information from the following book
-// G. Born, 'Referenzhandbuch Dateiformate', 
-//     Addison-Wesley ISBN 3-89319-815-6
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class ExportTif:public anyOutput {
-public:
-	ExportTif(GraphObj *g, char *FileName, DWORD flags, double res, double wi, double he);
-	~ExportTif();
-	bool StartPage();
-	bool EndPage();
-
-private:
-	int w, h;
-	anyOutput *bmo;
-	char *name;
-	int oFile;
-};
-
-ExportTif::ExportTif(GraphObj *g, char *FileName, DWORD flags, double res, double wi, double he)
-{
-	hres = vres = res;
-	name = 0L;							bmo = 0L;
-	DeskRect.left = DeskRect.top = 0;
-	DeskRect.right = DeskRect.bottom = 0x4fffffff;
-	dFillCol = 0xffffffffL;
-	if(g && FileName) {
-		w = un2ix(wi);		h = un2iy(he);
-		if(bmo = NewBitmapClass(w, h, hres, vres)){
-			bmo->VPorg.fy = -co2fiy(g->GetSize(SIZE_GRECT_TOP));
-			bmo->VPorg.fx = -co2fix(g->GetSize(SIZE_GRECT_LEFT));
-			bmo->Erase(0x00ffffffL);
-			g->DoPlot(bmo);
-			}
-		name = (char*)memdup(FileName, (int)strlen(FileName)+1, 0);
-		}
-	oFile = 0;
-}
-
-ExportTif::~ExportTif()
-{
-	if(name) free(name);
-	if(bmo) DelBitmapClass(bmo);
-}
-
-bool
-ExportTif::StartPage()
-{
-	unsigned char header[] = {
-		0x49, 0x49,							//intel byte order
-		0x2a, 0x00,							//version 4.2
-		0x08, 0x00, 0x00, 0x00,				//the image file directory just follows
-		0x0f, 0x00,							//number of tags in IFD
-		0xfe, 0x00, 0x04, 0x00, 0x01, 0x00, //new subfile tag
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x00, 0x01, 0x04, 0x00, 0x01, 0x00,	//image width tag (pixels)
-		0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
-		0x01, 0x01, 0x04, 0x00, 0x01, 0x00,	//image height tag (pixels)
-		0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
-		0x02, 0x01, 0x03, 0x00, 0x01, 0x00,	//BitsPerSample tag
-		0x00, 0x00, 0x08, 0x00, 0x00, 0x00, //   ...8 bits per sample
-		0x03, 0x01, 0x03, 0x00, 0x01, 0x00, //compression tag
-		0x00, 0x00, 0x01, 0x00, 0x00, 0x00,	//   ...no compression
-		0x06, 0x01, 0x03, 0x00, 0x01, 0x00, //photometric interpretation
-		0x00, 0x00, 0x02, 0x00, 0x00, 0x00,	//   ...its RGB
-		0x11, 0x01, 0x04, 0x00, 0x01, 0x00,	//strip (image) offset
-		0x00, 0x00, 0x82, 0x00, 0x00, 0x00,	//   ... number of strips and offset to data
-		0x12, 0x01, 0x03, 0x00, 0x01, 0x00,	//orientation tag
-		0x00, 0x00, 0x01, 0x00, 0x00, 0x00,	//   ...its top/left
-		0x15, 0x01, 0x03, 0x00, 0x01, 0x00,	//samples per pixel
-		0x00, 0x00, 0x03, 0x00, 0x00, 0x00,	//   ... 3 samples (colors) for RGB
-		0x17, 0x01, 0x04, 0x00, 0x01, 0x00,	//strip byte count
-		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-		0x1a, 0x01, 0x05, 0x00, 0x01, 0x00,	//horizontal resolution
-		0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
-		0x1b, 0x01, 0x05, 0x00, 0x01, 0x00,	//vertical resolution
-		0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
-		0x1c, 0x01, 0x03, 0x00, 0x01, 0x00,	//planar configuration
-		0x00, 0x00, 0x01, 0x00, 0x01, 0x00,	//   ... one layer
-		0x28, 0x01, 0x03, 0x00, 0x01, 0x00,	//resolution units
-		0x00, 0x00, 0x02, 0x00, 0x01, 0x00,	//   ... dots per inch
-		0x31, 0x01, 0x02, 0x00, 0x01, 0x00,	//Software
-		0x00, 0x00, 0x00, 0x00, 0x01, 0x00	//   ... RLPlot ...
-		};
-	DWORD res_info[4] = {iround(hres), 0x01, iround(vres), 0x01};
-	char prog_name[20];
-	int cb;
-
-	if(name && bmo) {
-#ifdef USE_WIN_SECURE
-		if(_sopen_s(&oFile, name, O_RDWR | O_BINARY | O_CREAT | O_TRUNC, 
-			0x40, S_IWRITE) || oFile < 0){
-			ErrorBox("Could not open output file");
-			return false;
-			}
-#else
-		if(-1 ==(oFile = open(name, O_RDWR | O_BINARY | O_CREAT | O_TRUNC,
-			S_IWRITE | S_IREAD))){
-			ErrorBox("Could not open output file");
-			return false;
-			}
-#endif
-		*((int*)(header+30))= w;		*((int*)(header+42))= h;
-		*((DWORD*)(header+126)) = (DWORD)(w * h * 3);
-		*((int*)(header+90)) = sizeof(header) + sizeof(res_info) + 20;
-		*((int*)(header+138)) = sizeof(header);
-		*((int*)(header+150)) = sizeof(header)+8;
-		*((int*)(header+186)) = sizeof(header)+16;
-		cb = rlp_strcpy(prog_name, 20, "RLPlot ");
-		rlp_strcpy(prog_name+cb, 20-cb, SZ_VERSION);
-#ifdef USE_WIN_SECURE
-		_write(oFile, &header, sizeof(header));
-		_write(oFile, &res_info, sizeof(res_info));
-		_write(oFile, &prog_name, 20);
-
-#else
-		write(oFile, &header, sizeof(header));
-		write(oFile, &res_info, sizeof(res_info));
-		write(oFile, &prog_name, 20);
-#endif
-		return true;
-		}
-	return false;
-}
-
-bool
-ExportTif::EndPage()
-{
-	int i, j, c;
-	DWORD pix;
-	unsigned char *pix_data, *cpix = (unsigned char*)&pix;
-
-	if(bmo && (pix_data = (unsigned char*)malloc(3072))){
-		for(i = c = 0; i < h; i++) {
-			for(j = 0; j < w; j++) {
-				bmo->oGetPix(j, i, &pix);			pix_data[c++] = cpix[0];
-				pix_data[c++] = cpix[1];			pix_data[c++] = cpix[2];
-				if(c >= 3072) {
-#ifdef USE_WIN_SECURE
-					_write(oFile, pix_data, 3072);
-#else
-					write(oFile, pix_data, 3072);
-#endif
-					c = 0;
-					}
-				}
-			}
-#ifdef USE_WIN_SECURE
-		_write(oFile, pix_data, c);
-#else
-		write(oFile, pix_data, c);
-#endif
-		free(pix_data);
-		}
-#ifdef USE_WIN_SECURE
-	_close(oFile);
-#else
-	close(oFile);
-#endif
-	oFile = -1;
-	return true;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Entry point to export graph to tag image file
-void DoExportTif(GraphObj *g, char *FileName, DWORD flags)
-{
-	ExportTif *ex;
-	double res, width, height;
-
-	if(!g || !FileName) return;
-	res = 98.0;
-	width = g->GetSize(SIZE_GRECT_RIGHT) - g->GetSize(SIZE_GRECT_LEFT);
-	height = g->GetSize(SIZE_GRECT_BOTTOM) - g->GetSize(SIZE_GRECT_TOP);
-	if(GetBitmapRes(&res, &width, &height, "Export Tag Image File")){
-		ex = new ExportTif(g, FileName, flags, res, width, height);
-		if(ex->StartPage())	ex->EndPage();
-		delete(ex);
-		}
-}	
+//Output.cpp, Copyright (c) 2000-2008 R.Lackner
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>				//file open flags
+#include <sys/stat.h>			//I/O flags
+#ifdef _WINDOWS
+	#include <io.h>					//for read/write
+#else
+	#define O_BINARY 0x0
+	#include <unistd.h>
+#endif
+#include "rlplot.h"
+
+tag_Units Units[] = {{0, "mm", 1.0f}, {1, "cm", 10.0f}, {2, "inch", 25.4f},
+	};
+
+extern Default defs;
+extern GraphObj *CurrGO, *TrackGO;		//Selected Graphic Objects
+extern Label *CurrLabel;
+extern Graph *CurrGraph;
+extern dragHandle *CurrHandle;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Output base class
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+anyOutput::anyOutput()
+{
+	units = defs.cUnits;		minLW = 1;
+	dBgCol = dFillCol = defs.Color(COL_BG);
+	xAxis.owner = yAxis.owner = zAxis.owner = (void *)this;
+	xAxis.flags = yAxis.flags = zAxis.flags = 0L;
+	xAxis.min = yAxis.min = zAxis.min = 0.0;
+	xAxis.max = yAxis.max = zAxis.max = 1.0;
+	xAxis.nBreaks = yAxis.nBreaks = zAxis.nBreaks = 0;
+	xAxis.breaks = yAxis.breaks = zAxis.breaks = 0L;
+	ddx = ddy = ddz = 1.0;
+	RLP.finc = 1.0f;
+	RLP.fp = 0.0f;
+	dPattern = 0xffffffffL;			//impossible (invisible)line pattern to start with
+	dBgCol = defs.Color(COL_BG);	OC_type = OC_UNKNOWN;
+	MrkMode = MRK_NONE;				MrkRect = 0L;
+	VPorg.fx = VPorg.fy = 0.0;		VPscale = 1.0;
+	MenuHeight = 0;					cCursor = MC_ARROW;
+	rotM[0][0] = rotM[1][1] = rotM[2][2] = 1.0;
+	rotM[0][1] = rotM[0][2] = rotM[1][0] = rotM[1][2] = rotM[2][0] = rotM[2][1] = 0.0;
+	hasHistMenu = false;			HistMenuSize = 0;
+	light_source.fx = light_source.fy = 0.0;
+}
+
+void
+anyOutput::SetRect(fRECT rec, int u, AxisDEF *x_ax, AxisDEF *y_ax)
+{
+	double spx, spy;
+
+	if (u >= 0 && u < NUM_UNITS) defs.cUnits = units = u;
+	else units = defs.cUnits;
+	spx = rec.Xmax - rec.Xmin;	spy = rec.Ymin -rec.Ymax;
+	MrkMode = MRK_NONE;
+	Box1.Xmin = co2fix(rec.Xmin);	Box1.Ymin = co2fiy(rec.Ymax);
+	Box1.Xmax = co2fix(rec.Xmax);	Box1.Ymax = co2fiy(rec.Ymin);
+	if(!x_ax || !y_ax) return;
+	if(x_ax->flags & AXIS_DEFRECT) {
+		Box1.Xmin = co2fix(x_ax->loc[0].fx);
+		Box1.Xmax = co2fix(x_ax->loc[1].fx);
+		spx = x_ax->loc[1].fx - x_ax->loc[0].fx;
+		}
+	if(y_ax->flags & AXIS_DEFRECT) {
+		Box1.Ymin = co2fiy(y_ax->loc[0].fy);
+		Box1.Ymax = co2fiy(y_ax->loc[1].fy);
+		spy = y_ax->loc[1].fy - y_ax->loc[0].fy;
+		}
+	memcpy(&xAxis, x_ax, sizeof(AxisDEF));
+	memcpy(&yAxis, y_ax, sizeof(AxisDEF));
+	ddy = GetAxisFac(&yAxis, un2fiy(spy), 1);
+	ddx = GetAxisFac(&xAxis, un2fix(spx), 0);
+	xAxis.owner = yAxis.owner = this;
+}
+
+void
+anyOutput::UseAxis(AxisDEF *ax, int type)
+{
+	AxisDEF *cax;
+
+	MrkMode = MRK_NONE;
+	if(!ax) return;
+	switch (type) {
+		case 1:			//x-axis
+			memcpy(cax = &xAxis, ax, sizeof(AxisDEF));
+			Box1.Xmin = co2fix(ax->loc[0].fx);
+			Box1.Xmax = co2fix(ax->loc[1].fx);
+			ddx = GetAxisFac(&xAxis, Box1.Xmax - Box1.Xmin, 0);
+			break;
+		case 2:			//y-axis
+			memcpy(cax = &yAxis, ax, sizeof(AxisDEF));
+			if(ax->flags & AXIS_3D) {
+				Box1.Ymax = co2fiy(ax->loc[0].fy);
+				Box1.Ymin = co2fiy(ax->loc[1].fy);
+				}
+			else {
+				Box1.Ymin = co2fiy(ax->loc[0].fy);
+				Box1.Ymax = co2fiy(ax->loc[1].fy);
+				}
+			ddy = GetAxisFac(&yAxis, Box1.Ymax - Box1.Ymin, 1);
+			break;
+		case 3:			//z-axis
+			memcpy(cax = &zAxis, ax, sizeof(AxisDEF));
+			Box1z.fx = un2fiz(ax->loc[0].fz);
+			Box1z.fy = un2fiz(ax->loc[1].fz);
+			ddz = GetAxisFac(&zAxis, Box1z.fy - Box1z.fx, 2);
+			break;
+		default:		//unnknown direction
+			return;
+		}
+	cax->owner = this;
+}
+
+void
+anyOutput::SetSpace(fPOINT3D *cub1, fPOINT3D *cub2, int u, double *rot, 
+	fPOINT3D *cent, AxisDEF *x_ax, AxisDEF *y_ax, AxisDEF *z_ax)
+{
+	double rotQ[6];		//rotation definition:
+						//  unit vector x
+						//              y
+						//              z
+						//  sin(phi)
+						//  cos(phi)
+						//  1.0 -cos(phi)
+	double dp;
+
+	if (u >= 0 && u < NUM_UNITS) defs.cUnits = units = u;
+	else units = defs.cUnits;
+	MrkMode = MRK_NONE;
+	HideTextCursor();
+	memcpy(&xAxis, x_ax, sizeof(AxisDEF));
+	memcpy(&yAxis, y_ax, sizeof(AxisDEF));
+	memcpy(&zAxis, z_ax, sizeof(AxisDEF));
+	xAxis.owner = yAxis.owner = zAxis.owner = this;
+	//assume resolution equal in all directions: use un2fix() for
+	//   all coordinates
+	Box1.Xmin = co2fix(cub1->fx);		Box1.Ymin = co2fiy(cub2->fy);
+	Box1.Xmax = co2fix(cub2->fx);		Box1.Ymax = co2fiy(cub1->fy);
+	Box1z.fx = un2fiz(cub1->fz);		Box1z.fy = un2fiz(cub2->fz);
+	if(x_ax->flags & AXIS_DEFRECT) {
+		Box1.Xmin = co2fix(x_ax->loc[0].fx);	Box1.Xmax = co2fix(x_ax->loc[1].fx);
+		}
+	if(y_ax->flags & AXIS_DEFRECT) {
+		Box1.Ymax = co2fiy(y_ax->loc[0].fy);	Box1.Ymin = co2fiy(y_ax->loc[1].fy);
+		}
+	if(z_ax->flags & AXIS_DEFRECT) {
+		Box1z.fx = un2fiz(z_ax->loc[0].fz);		Box1z.fy = un2fiz(z_ax->loc[1].fz);
+		}
+	ddx = GetAxisFac(&xAxis, Box1.Xmax-Box1.Xmin, 0);
+	ddy = GetAxisFac(&yAxis, Box1.Ymax-Box1.Ymin, 1);
+	ddz = GetAxisFac(&zAxis, Box1z.fy - Box1z.fx, 2);
+	rotC.fx = un2fix(cent->fx)+ VPorg.fx;
+	rotC.fy = un2fiy(cent->fy)+ VPorg.fy;	
+	rotC.fz = un2fiz(cent->fz);
+	memcpy(rotQ, rot, sizeof(rotQ));
+	//normalize vector part of rotQ
+	dp = sqrt(rotQ[0]*rotQ[0] + rotQ[1]*rotQ[1] + rotQ[2]*rotQ[2]);
+	rotQ[0] /= dp;		rotQ[1] /= dp;		rotQ[2] /= dp;
+	dp = sqrt(rotQ[0]*rotQ[0] + rotQ[1]*rotQ[1] + rotQ[2]*rotQ[2]);
+	//set up rotation matrix from quaternion
+	//see: Graphic Gems, A.S. Glassner ed.; Academic Press Inc.
+	//M.E. Pique: Rotation Tools
+	// ISBN 0-12-286165-5, p. 466
+	rotM[0][0] = rotQ[5]*rotQ[0]*rotQ[0] + rotQ[4];
+	rotM[0][1] = rotQ[5]*rotQ[0]*rotQ[1] + rotQ[3]*rotQ[2];
+	rotM[0][2] = rotQ[5]*rotQ[0]*rotQ[2] - rotQ[3]*rotQ[1];
+	rotM[1][0] = rotQ[5]*rotQ[0]*rotQ[1] - rotQ[3]*rotQ[2];
+	rotM[1][1] = rotQ[5]*rotQ[1]*rotQ[1] + rotQ[4];
+	rotM[1][2] = rotQ[5]*rotQ[1]*rotQ[2] + rotQ[3]*rotQ[0];
+	rotM[2][0] = rotQ[5]*rotQ[0]*rotQ[2] + rotQ[3]*rotQ[1];
+	rotM[2][1] = rotQ[5]*rotQ[1]*rotQ[2] - rotQ[3]*rotQ[0];
+	rotM[2][2] = rotQ[5]*rotQ[2]*rotQ[2] + rotQ[4];
+}
+
+void
+anyOutput::LightSource(double x, double y)
+{
+	int i, j, m;
+	double angx, angy;
+	double a[3][3], b[3][3];
+
+	if(light_source.fx == 0.0 || light_source.fy == 0.0 ||
+		x != light_source.fx || y != light_source.fy) {
+		light_source.fx = x;		light_source.fy = y;
+		angx = x * 0.017453292;		angy = y * 0.017453292;
+		for (i = 0; i < 3; i++)	for(j = 0; j < 3; j++) {
+			a[i][j] = b[i][j] = 0.0;
+			}
+		//first axis
+		a[0][0] = 1.0;			a[1][1] = cos(angx);		a[1][2] = -sin(angx);
+		a[2][1] = -a[1][2];		a[2][2] = a[1][1];
+		//second axis
+		b[0][0] = cos(angy);	b[0][1] = -sin(angy);		b[1][0] = -b[0][1];
+		b[1][1] = b[0][0];		b[2][2] = 1.0;
+		//combine the two rotations
+		for (i = 0; i < 3; i++) for(j = 0; j < 3; j++){
+			light_vec[i][j] = 0.0;
+			for(m = 0; m < 3; m++) light_vec[i][j] += (a[i][m] * b[m][j]);
+			}
+		}
+}
+
+DWORD
+anyOutput::VecColor(double *plane_vec, DWORD color1, DWORD color2)
+{
+	double v[3], vec[3], vlength;
+	int i, j;
+
+	//rotate vector towards the light source
+	if(!plane_vec) return color1;
+	v[0] = plane_vec[0];		v[1] = plane_vec[2];	v[2] = plane_vec[1];
+	for (i = 0; i < 3; i++) for(j = 0, vec[i] = 0.0; j < 3; j++)
+		vec[i] += (light_vec[i][j] * v[j]);
+	//normalize vec: both vector should have unit length but make sure
+	vlength = sqrt(vec[0]*vec[0]+vec[1]*vec[1]+vec[2]*vec[2]);
+	if(vlength < 0.9) return color1;
+	vec[0] /= vlength;	vec[1] /= vlength;	vec[2] /= vlength;
+	//calc color
+	return IpolCol(color1, color2, fabs(vec[1]));
+}
+
+bool
+anyOutput::GetSize(RECT *rc)
+{
+	memcpy(rc, &DeskRect, sizeof(RECT));
+	return true;
+}
+
+double
+anyOutput::fx2fix(double x)
+{
+	double temp;
+
+	x = TransformValue(&xAxis, x, true);
+	temp = (x - xAxis.min)*ddx;
+	if(0 == (xAxis.flags & AXIS_INVERT)) return temp + Box1.Xmin;
+	return Box1.Xmax-temp;
+}
+
+double
+anyOutput::fy2fiy(double y)
+{
+	double temp;
+
+	y = TransformValue(&yAxis, y, true);
+	temp = (y - yAxis.min)*ddy;
+	if(AXIS_INVERT == (yAxis.flags & AXIS_INVERT)) return temp + Box1.Ymin;
+	return Box1.Ymax-temp;
+}
+
+double
+anyOutput::fz2fiz(double z)
+{
+	double temp;
+
+	z = TransformValue(&zAxis, z, true);
+	temp = (z - zAxis.min)*ddz;
+	if(0 == (zAxis.flags & AXIS_INVERT)) return temp + Box1z.fx;
+	return Box1z.fy-temp;
+}
+
+bool 
+anyOutput::fp2fip(lfPOINT *fdp, lfPOINT *fip)
+{
+	double x, y, si, csi, temp;
+
+	if((xAxis.flags & AXIS_ANGULAR) && (yAxis.flags & AXIS_RADIAL)) {
+		x = 6.283185307 * TransformValue(&xAxis, fdp->fx + xAxis.Start, true)/(xAxis.max-xAxis.min);
+		si = sin(x);					csi = cos(x);
+		y = TransformValue(&yAxis, fdp->fy, true);
+		temp = (y - yAxis.min)*ddy;
+		if(yAxis.flags & AXIS_INVERT) temp = Box1.Ymax - Box1.Ymin - temp;
+		fip->fx = ((Box1.Xmin + Box1.Xmax)/2.0) + csi * temp;
+		if(xAxis.flags & AXIS_INVERT) fip->fy = Box1.Ymax + si * temp;
+		else fip->fy = Box1.Ymax - si * temp;
+		fip->fy += disp_y;
+		return true;
+		}
+	else {
+		fip->fx = fx2fix(fdp->fx);		fip->fy = fy2fiy(fdp->fy);
+		return true;
+		}
+	return false;
+}
+
+bool 
+anyOutput::fvec2ivec(fPOINT3D *v, fPOINT3D *iv)
+{
+	double x, y, z;
+	
+	if(!v || !iv) return false;
+	x = fx2fix(v->fx)-rotC.fx;
+	y = fy2fiy(v->fy)-rotC.fy;
+	z = fz2fiz(v->fz)-rotC.fz;
+	iv->fx = x * rotM[0][0] + y * rotM[0][1] + z * rotM[0][2] + rotC.fx;
+	iv->fy = x * rotM[1][0] + y * rotM[1][1] + z * rotM[1][2] + rotC.fy;
+	iv->fz = x * rotM[2][0] + y * rotM[2][1] + z * rotM[2][2] + rotC.fz;
+	iv->fx += disp_x;			iv->fy += disp_y;
+	return true;
+}
+
+bool
+anyOutput::cvec2ivec(fPOINT3D *v, fPOINT3D *iv)
+{
+	double x, y, z;
+	
+	if(!v || !iv) return false;
+	x = co2fix(v->fx)-rotC.fx;
+	y = co2fiy(v->fy)-rotC.fy;
+	z = un2fiz(v->fz)-rotC.fz;
+	iv->fx = x * rotM[0][0] + y * rotM[0][1] + z * rotM[0][2] + rotC.fx;
+	iv->fy = x * rotM[1][0] + y * rotM[1][1] + z * rotM[1][2] + rotC.fy;
+	iv->fz = x * rotM[2][0] + y * rotM[2][1] + z * rotM[2][2] + rotC.fz;
+	iv->fx += disp_x;			iv->fy += disp_y;
+	return true;
+}
+
+bool
+anyOutput::uvec2ivec(fPOINT3D *v, fPOINT3D *iv)
+{
+	double x, y, z;
+	
+	if(!v || !iv) return false;
+	x = un2fix(v->fx);
+	y = un2fiy(v->fy);
+	z = un2fiz(v->fz);
+	iv->fx = x * rotM[0][0] + y * rotM[0][1] + z * rotM[0][2];
+	iv->fy = x * rotM[1][0] + y * rotM[1][1] + z * rotM[1][2];
+	iv->fz = x * rotM[2][0] + y * rotM[2][1] + z * rotM[2][2];
+	return true;
+}
+
+double
+anyOutput::un2fix(double x)
+{
+	return (x * VPscale * hres*Units[units].convert/25.4);
+}
+
+double
+anyOutput::un2fiy(double y)
+{
+	return (y * VPscale * vres*Units[units].convert/25.4);
+}
+
+double
+anyOutput::un2fiz(double z)
+{
+	return (z * VPscale * hres*Units[units].convert/25.4);
+}
+
+double
+anyOutput::fix2un(double fix)
+{
+	return (fix/Units[units].convert*25.4/hres)/VPscale;
+}
+
+double
+anyOutput::fiy2un(double fiy)
+{
+	return (fiy/Units[units].convert*25.4/vres)/VPscale;
+}
+
+bool
+anyOutput::GetLine(LineDEF *lDef)
+{
+	if(lDef) {
+		lDef->width = LineWidth;
+		lDef->color = dLineCol;
+		lDef->pattern = dPattern;
+		return true;
+		}
+	return false;
+}
+
+bool
+anyOutput::SetTextSpec(TextDEF *set)
+{
+	memcpy(&TxtSet, set, sizeof(TextDEF));
+	TxtSet.text = 0L;
+	return true;
+}
+
+bool
+anyOutput::ShowMark(void *src, int Mode)
+{
+	GraphObj *go;
+
+	if(MrkMode != MRK_NONE) HideMark();
+	MrkMode = Mode;			MrkRect = src;		HideCopyMark();
+	switch (Mode) {
+		case MRK_INVERT:
+			return UpdateRect((RECT*)src, true);
+		case MRK_GODRAW:	case MRK_SSB_DRAW:
+			go = (GraphObj *) src;				go->DoMark(this, true);
+			CurrGO = go;
+			if(CurrLabel && CurrLabel != CurrGO) {
+				HideTextCursor();				CurrLabel = 0L;
+				}
+			return true;
+		}
+	return false;
+}
+
+bool
+anyOutput::HideMark()
+{
+	switch(MrkMode) {
+		case MRK_NONE:
+			return true;
+		case MRK_INVERT:
+			MrkMode = MRK_NONE;
+			return UpdateRect((RECT*)MrkRect, false);
+		case MRK_GODRAW:
+			MrkMode = MRK_NONE;					//inhibit reentrance
+			if(CurrLabel && CurrLabel->Command(CMD_HIDEMARK, 0L, this))
+				CurrGraph->Command(CMD_REDRAW, 0L, this);
+			else if(MrkRect)((GraphObj*)MrkRect)->DoMark(this, false);
+			else if(CurrGraph) CurrGraph->Command(CMD_REDRAW, 0L, this);
+			return true;
+		case MRK_SSB_DRAW:
+			MrkMode = MRK_NONE;
+			if (MrkRect) ((GraphObj*)MrkRect)->DoMark(this, false);
+			return true;
+		}
+	return false;
+}
+
+int
+anyOutput::CalcCursorPos(char *txt, POINT p, POINT *fit)
+{
+	int i, d, w, h, CurrPos;
+
+	d = TxtSet.iSize >>2;
+	if(!txt || !fit) return 0;
+	if (!(i = (int)strlen(txt)))return 0;
+	//right justified text
+	if(TXA_HRIGHT == (TxtSet.Align & TXA_HRIGHT)){
+		if((p.x - fit->x) < d) return i;
+		for (CurrPos = i-1; CurrPos >= 0; CurrPos--) {
+			if(!oGetTextExtent(txt+CurrPos, i-CurrPos, &w, &h)) return 0;
+			if((w = p.x - w - d) <= fit->x) return CurrPos;
+			}
+		return 0;
+		}
+	//left justified text
+	else {
+		if((fit->x - p.x) < d) return 0;
+		for (CurrPos = i; CurrPos >= 0; CurrPos--) {
+			if(!oGetTextExtent(txt, CurrPos, &w, &h)) return 0;
+			if((w = p.x + w - d) <= fit->x) return CurrPos;
+			}
+		}
+	return 0;
+}
+
+bool
+anyOutput::TextCursor(char *txt, POINT p, POINT *fit, int *pos, int dx)
+{
+	int i, w, h, CurrPos;
+	RECT disp;
+
+	if(fit) CurrPos = CalcCursorPos(txt, p, fit);
+	//recalculate caret position
+	if(txt && pos && !fit){
+		if(TxtSet.Align & TXA_HRIGHT) {		//right justfied text
+			if((i = (int)strlen(txt)-(*pos))){
+				if(!oGetTextExtent(txt+(*pos), i, &w, &h)) return false;
+				w = p.x - w;
+				}
+			else w = p.x-1;
+			}
+		else {								//left justified text
+			if(!(*pos)) w = 0;
+			else if(!oGetTextExtent(txt, *pos, &w, &h))return false;
+			w += p.x;
+			}
+		}
+	else if(!fit)return false;
+	//right justified text: search caret and cursor position
+	else if(txt && (TxtSet.Align & TXA_HRIGHT)){
+		i = (int)strlen(txt);
+		if(i == CurrPos) w = 1;
+		else if(!oGetTextExtent(txt+CurrPos, i-CurrPos, &w, &h)) return false;
+		w = p.x - w;
+		}
+	//left justified text: search caret and cursor position
+	else if(txt && fit) {
+		if (!CurrPos) w = 0;
+		else if(!oGetTextExtent(txt, CurrPos, & w, &h)) return false;
+		w += p.x;
+		}
+	if(fit && pos) *pos = CurrPos;
+	disp.left = disp.right = w+dx;
+	disp.top = p.y;
+	if(TxtSet.Align & TXA_VCENTER) {
+		disp.top -= (TxtSet.iSize>>1);
+		}
+#ifdef _WINDOWS
+	disp.bottom = disp.top + TxtSet.iSize-1;
+#else
+	disp.top -= 1;
+	disp.bottom = disp.top + iround(TxtSet.iSize*1.25)-2;
+#endif
+	ShowTextCursor(this, &disp, 0x0L);
+	return true;
+}
+
+//we need our own implementation of Bresenham's line drawing algorithm to draw
+//   a line with variable pattern sizes.
+//Ref: P.S. Heckbert (1990) "Digital Line Drawing", in: Graphic Gems
+//   (A.S. Glassner, ed.); Academic Press, Inc.,
+//   ISBN 0-12-286165-5
+bool
+anyOutput::PatLine(POINT p1, POINT p2)
+{
+	int d, ax, ay, sx, sy, dx, dy;
+	double fInc2;
+	bool bPen;
+	POINT tr[2];
+
+	dx = p2.x - p1.x;
+	fInc2 = RLP.finc * 0.414213562;			//increment by sqrt(2) if 45� slope
+	if ( p2.x < p1.x) { 	ax = (-dx)<<1;		sx = -1;		}
+	else {					ax = dx <<1;		sx = 1;		}
+	dy = p2.y - p1.y;
+	if (p2.y < p1.y) {	ay = (-dy)<<1;		sy = -1;		}
+	else {					ay = dy<<1;			sy = 1;		}
+	tr[0].x = tr[1].x = p1.x;							tr[0].y = tr[1].y = p1.y;
+	if(dPattern &(1 << ((int)RLP.fp))) bPen = false;
+	else bPen = true;
+	if (ax > ay) {													// x dominant
+		d = ay - (ax >>1);
+		for ( ; ; ) {
+			RLP.fp += RLP.finc;
+			if(RLP.fp >= 32.0f) RLP.fp -= 32.0f;
+			if(bPen) {
+				if(tr[1].x == p2.x) return oSolidLine(tr);
+				if (d >= 0) {tr[1].y += sy;	d -= ax; RLP.fp += fInc2;}
+				tr[1].x += sx;
+				if(dPattern &(1 << ((int)RLP.fp))) {
+					bPen = false;
+					oSolidLine(tr);
+					tr[0].x = tr[1].x; tr[0].y = tr[1].y;
+					}
+				}
+			else {
+				if(tr[0].x == p2.x) return true;
+				if (d >= 0) {tr[0].y += sy;	d -= ax; RLP.fp += fInc2;}
+				tr[0].x += sx;
+				if(!(dPattern &(1 << ((int)RLP.fp)))) {
+					bPen = true;
+					tr[1].x = tr[0].x; tr[1].y = tr[0].y;
+					}
+				}
+			d += ay;
+			}
+		}
+	else {															// y dominant
+		d = ax - (ay >>1);
+		for ( ; ; ) {
+			RLP.fp += RLP.finc;
+			if(RLP.fp >= 32.0f) RLP.fp -= 32.0f;
+			if (bPen){
+				if (tr[1].y == p2.y) return oSolidLine(tr);
+				if (d >= 0) {tr[1].x += sx; d -= ay; RLP.fp += fInc2;}
+				tr[1].y += sy;
+				if(dPattern &(1 << ((int)RLP.fp))) {
+					bPen = false;
+					oSolidLine(tr);
+					tr[0].x = tr[1].x; tr[0].y = tr[1].y;
+					}
+				}
+			else {
+				if (tr[0].y == p2.y) return true;
+				if (d >= 0) {tr[0].x += sx; d -= ay; RLP.fp += fInc2;}
+				tr[0].y += sy;
+				if(!(dPattern &(1 << ((int)RLP.fp)))) {
+					bPen = true;
+					tr[1].x = tr[0].x; tr[1].y = tr[0].y;
+					}
+				}
+			d += ax;
+			}
+		}
+}
+
+static int Helv_Char_Width [] = {
+ 0, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
+ 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
+ 15, 17, 20, 31, 31, 49, 37, 11, 18, 18, 21, 32, 15, 18, 15, 15,
+ 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 15, 15, 32, 32, 32, 31,
+ 56, 37, 37, 40, 40, 37, 34, 43, 40, 15, 28, 37, 31, 45, 40, 43,
+ 37, 43, 40, 37, 33, 40, 37, 54, 35, 35, 34, 15, 15, 15, 24, 31,
+ 18, 31, 31, 28, 31, 31, 15, 31, 31, 11, 13, 28, 11, 47, 31, 31,
+ 31, 31, 18, 28, 15, 31, 29, 39, 27, 27, 27, 18, 14, 18, 32, 41,
+ 31, 41, 12, 31, 18, 55, 31, 31, 18, 56, 37, 18, 55, 41, 34, 41,
+ 41, 12, 12, 18, 18, 19, 31, 55, 16, 55, 28, 18, 52, 41, 27, 37,
+ 15, 17, 31, 31, 31, 31, 14, 31, 18, 41, 20, 31, 32, 18, 41, 30,
+ 22, 30, 18, 18, 18, 32, 30, 15, 18, 18, 20, 31, 46, 46, 46, 34,
+ 37, 37, 37, 37, 37, 37, 55, 40, 37, 37, 37, 37, 15, 15, 15, 15,
+ 40, 40, 43, 43, 43, 43, 43, 32, 43, 40, 40, 40, 40, 37, 37, 34,
+ 31, 31, 31, 31, 31, 31, 49, 28, 31, 31, 31, 31, 15, 15, 15, 15,
+ 31, 31, 31, 31, 31, 31, 31, 30, 34, 31, 31, 31, 31, 28, 31, 28};
+
+/*
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 18, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 18, 18, 24, 42, 39, 66, 51, 18, 24, 24, 30, 42, 18, 45, 18, 21,
+ 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 18, 18, 45, 45, 45, 36,
+ 75, 51, 51, 54, 54, 48, 42, 57, 54, 24, 39, 54, 42, 63, 54, 54,
+ 48, 54, 51, 48, 48, 54, 51, 66, 51, 48, 45, 21, 21, 21, 36, 42,
+ 18, 39, 42, 36, 42, 39, 24, 42, 39, 18, 18, 36, 18, 60, 42, 39,
+ 42, 42, 27, 36, 24, 42, 39, 54, 36, 39, 36, 24, 18, 24, 42, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 18,
+ 18, 18, 39, 42, 39, 42, 18, 39, 24, 57, 27, 42, 45, 24, 57, 24,
+ 27, 42, 21, 21, 21, 42, 36, 18, 21, 21, 27, 42, 57, 57, 57, 36,
+ 51, 51, 51, 51, 51, 51, 69, 54, 48, 48, 48, 48, 24, 24, 24, 24,
+ 54, 54, 54, 54, 54, 54, 54, 42, 54, 54, 54, 54, 54, 48, 48, 45,
+ 39, 39, 39, 39, 39, 39, 63, 36, 39, 39, 39, 39, 18, 18, 18, 18,
+ 39, 42, 39, 39, 39, 39, 39, 42, 39, 42, 42, 42, 42, 39, 42, 39};
+*/
+static int Cour_Char_Width [] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 35, 0, 0, 0, 35, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35,
+ 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35};
+
+
+
+bool
+anyOutput::oGetTextExtent(char *text, int cb, int *width, int *height)
+{
+	int *CharWidth;
+	int i, w;
+
+	switch (TxtSet.Font) {
+	case FONT_COURIER:		CharWidth = Cour_Char_Width;	break;
+	default:				CharWidth = Helv_Char_Width;	break;
+		}
+	if(!cb && text) cb = (int)strlen(text);
+	for(i = w = 0; i < cb; i++) w += ((unsigned)text[i] < 256 ? CharWidth[(unsigned)text[i]] : 35);
+	*width = iround(((double)w * (double)TxtSet.iSize)/52.0);
+	*height = TxtSet.iSize;
+	return true;
+}
+
+bool
+anyOutput::oGetTextExtentW(w_char *text, int cb, int *width, int *height)
+{
+	if(cb < 1) for(cb = 0; text[cb]; cb++);
+	*width = (TxtSet.iSize * cb)>>1;
+	*height = TxtSet.iSize;
+	return true;
+}
+
+bool 
+anyOutput::oSphere(int cx, int cy, int r, POINT *pts, int cp, char *nam)
+{
+	FillDEF fd;
+	HatchOut *ho;
+	int i, j, mlw;
+
+	if(pts && cp) oPolygon(pts, cp, nam);
+	else oCircle(cx - r, cy - r, cx + r + 1, cy + r + 1, nam);
+	fd.color = dFillCol;		fd.color2 = dFillCol2;
+	fd.hatch = 0L;			fd.scale = 1.0;
+	fd.type = FILL_NONE;
+	if(ho = new HatchOut(this)) {
+		ho->SetFill(&fd);
+		mlw = minLW;		minLW = ho->minLW = 2;
+		ho->light_source.fx = light_source.fx;
+		ho->light_source.fy = light_source.fy;
+		for(i = 0; i < 3; i++) for (j = 0; j < 3; j++) {
+			ho->light_vec[i][j] = light_vec[i][j];
+			}
+		ho->oSphere(cx, cy, r-iLine, pts, cp, 0L);
+		delete(ho);		minLW = mlw;
+		return true;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Process hatch patterns in an output class
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+enum {HO_NONE, HO_RECT, HO_CIRCLE, HO_ELLIPSE, HO_BIGELLYPSE,
+	HO_POLYGON};
+struct _HatchDef{
+	union {
+		struct {
+			RECT rec;
+			}rec;
+		struct {
+			POINT centre;
+			long sr;
+			}cir;
+		struct {
+			POINT centre;
+			int ix, iy;
+			unsigned long sab;
+			}ell;
+		struct {
+			POINT fo[2];
+			unsigned int rsab;
+			int a, b;
+			}bell;
+		struct {
+			POINT *pts;
+			int cp;
+			}plg;
+		};
+}HatchDef;
+
+HatchOut::HatchOut(anyOutput *Parent):anyOutput()
+{
+	ParInit = false;
+	ho = HO_NONE;
+	ht = FILL_COMBS;
+	out = Parent;
+	xbase = ybase = 1.5;
+	units = 0;					//use mm for defaults
+	if(Parent) out->GetSize(&DeskRect);
+	else DeskRect.left = DeskRect.right = DeskRect.top = DeskRect.bottom = 0;
+	MyLineDef.width = 0.0f;
+	MyLineDef.patlength = 10.0f;
+	MyLineDef.color = 0x00ff0000L;
+	MyLineDef.pattern = 0x00000000L;
+}
+
+
+HatchOut::~HatchOut()
+{
+}
+
+bool
+HatchOut::SetFill(FillDEF *fill)
+{
+	if(!fill) return false;
+	ht = (fill->type & 0xff);
+	if(fill->hatch) memcpy(&MyLineDef, fill->hatch, sizeof(LineDEF));
+	//we assume that all operations are at a 1:1 pixel relation to the parent,
+	//  but we use un2fix and un2fiy from the parent: correct for zoom ....
+	switch(out->units) {
+	case 0:						//parent uses mm
+		xbase = out->un2fix(1.5);		ybase = out->un2fiy(1.5);
+		break;
+	case 1:						//parent uses cm
+		xbase = out->un2fix(0.15);		ybase = out->un2fiy(0.15);
+		break;
+	case 2:						//parent uses inches
+		xbase = out->un2fix(0.059);		ybase = out->un2fiy(0.059);
+		break;
+		}
+	if(fill->scale >0.05f && fill->scale < 20.0) {
+		xbase *= fill->scale;		ybase *= fill->scale;
+		}
+	dFillCol = fill->color;
+	dFillCol2 = fill->color2;
+	return true;
+}
+
+bool
+HatchOut::StartPage()
+{
+	if(out) out->GetSize(&DeskRect);
+	return true;
+}
+
+bool
+HatchOut::oCircle(int x1, int y1, int x2, int y2, char *nam)
+{
+	long tmp;
+
+	if(x1 < x2) {		UseRect.left = x1;		UseRect.right = x2;		}
+	else {				UseRect.left = x2;		UseRect.right = x1;		}
+	if(y1 < y2) {		UseRect.top = y1;			UseRect.bottom = y2;		}
+	else {				UseRect.top = y2;			UseRect.bottom = y1;		}
+	if((UseRect.right -UseRect.left)==(UseRect.bottom-UseRect.top)) {
+		HatchDef.cir.centre.x = (UseRect.right + UseRect.left)/2;
+		HatchDef.cir.centre.y = (UseRect.bottom + UseRect.top)/2;
+		tmp = (UseRect.right - UseRect.left)/2;
+		HatchDef.cir.sr = (long)tmp * (long)tmp-1;
+		if(HatchDef.cir.sr >9) HatchDef.cir.sr -= tmp;	//stay inside circle
+		ho = HO_CIRCLE;
+		PrepareParent(false);
+		return DoHatch();
+		}
+	//for small ellipses use the centered equation
+	if((UseRect.right -UseRect.left) <512 && (UseRect.bottom-UseRect.top)<512) {
+		ho = HO_ELLIPSE;
+		HatchDef.ell.centre.x = (UseRect.right + UseRect.left)/2;
+		HatchDef.ell.centre.y = (UseRect.bottom + UseRect.top)/2;
+		HatchDef.ell.ix = tmp = (UseRect.right - UseRect.left)/2;
+		HatchDef.ell.sab = tmp * tmp;
+		HatchDef.ell.iy = tmp = (UseRect.bottom - UseRect.top)/2;
+		HatchDef.ell.sab *= (tmp * tmp);
+		PrepareParent(false);
+		return DoHatch();
+		}
+	//for bigger ellipses we use the focuses to describe the ellipse
+	//  this reduces numerical problems
+	ho = HO_BIGELLYPSE;
+	HatchDef.bell.fo[0].x= HatchDef.bell.fo[1].x= (UseRect.right+UseRect.left)/2;
+	HatchDef.bell.fo[0].y= HatchDef.bell.fo[1].y= (UseRect.bottom+UseRect.top)/2;
+	if((UseRect.right -UseRect.left) >(UseRect.bottom-UseRect.top)){
+		HatchDef.bell.rsab =	UseRect.right - UseRect.left;
+		HatchDef.bell.a = (UseRect.right - UseRect.left)/2;
+		HatchDef.bell.b = (UseRect.bottom - UseRect.top)/2;
+		tmp = isqr(HatchDef.bell.a*HatchDef.bell.a -
+			HatchDef.bell.b*HatchDef.bell.b);
+		HatchDef.bell.fo[0].x -= tmp;
+		HatchDef.bell.fo[1].x += tmp;
+		}
+	else {
+		HatchDef.bell.rsab =	UseRect.bottom - UseRect.top;
+		HatchDef.bell.b = (UseRect.right - UseRect.left)/2;
+		HatchDef.bell.a = (UseRect.bottom - UseRect.top)/2;
+		tmp = isqr(HatchDef.bell.a*HatchDef.bell.a -
+			HatchDef.bell.b*HatchDef.bell.b);
+		HatchDef.bell.fo[0].y -= tmp;
+		HatchDef.bell.fo[1].y += tmp;
+		}
+	PrepareParent(false);
+	return DoHatch();
+}
+
+bool
+HatchOut::oSphere(int cx, int cy, int r, POINT *pts, int cp, char *nam)
+{
+	double v[3], vec[3];
+	int i, j;
+
+	ht = FILL_LIGHT3D;
+
+	v[0] = 0.0;		v[1] = 1.0;		v[2] = 0.0;
+	for (i = 0; i < 3; i++) for(j = 0, vec[i] = 0.0; j < 3; j++)
+		vec[i] += (light_vec[i][j] * v[j]);
+	circ_grad.cx = cx + iround(vec[0]*((double)r));
+	circ_grad.cy = cy - iround(vec[2]*((double)r));
+	circ_grad.r = r;
+	if(pts && cp) return oPolygon(pts, cp, nam);
+	return oCircle(cx - r, cy - r, cx + r + 1, cy + r + 1, nam);
+}
+
+bool
+HatchOut::oRectangle(int x1, int y1, int x2, int y2, char *nam)
+{
+	if(x1 < x2) {
+		HatchDef.rec.rec.left = UseRect.left = x1;
+		HatchDef.rec.rec.right = UseRect.right = x2-1;
+		}
+	else {
+		HatchDef.rec.rec.left = UseRect.left = x2;
+		HatchDef.rec.rec.right = UseRect.right = x1-1;
+		}
+	if(y1 < y2) {
+		HatchDef.rec.rec.top = UseRect.top = y1;
+		HatchDef.rec.rec.bottom = UseRect.bottom = y2-1;
+		}
+	else {
+		HatchDef.rec.rec.top = UseRect.top = y2;
+		HatchDef.rec.rec.bottom = UseRect.bottom = y1-1;
+		}
+	ho = HO_RECT;
+	PrepareParent(false);
+	return DoHatch();
+}
+
+bool
+HatchOut::oPolygon(POINT *pts, int cp, char *nam)
+{
+	int i;
+	POINT *p;
+
+	p = (POINT*)malloc((cp+2)*(sizeof(POINT)));
+	HatchDef.plg.pts = p;
+	if(!p || cp < 3)return false;
+	HatchDef.plg.pts[0].x = UseRect.left = UseRect.right = pts[0].x;
+	HatchDef.plg.pts[0].y = UseRect.top = UseRect.bottom = pts[0].y;
+	for(i = 1; i < cp; i++){
+		UseRect.left = UseRect.left < pts[i].x ? UseRect.left : pts[i].x;
+		UseRect.right = UseRect.right > pts[i].x ? UseRect.right : pts[i].x;
+		UseRect.top = UseRect.top < pts[i].y ? UseRect.top : pts[i].y;
+		UseRect.bottom = UseRect.bottom > pts[i].y ? UseRect.bottom : pts[i].y;
+		p[i].x = pts[i].x;		p[i].y = pts[i].y;
+		}
+	i--;
+	if(p[i].x != pts[0].x || p[i].y != pts[0].y) {
+		i++;
+		p[i].x = pts[0].x;		p[i].y = pts[0].y;
+		}
+	HatchDef.plg.cp = i+1;
+	ho= HO_POLYGON;
+	PrepareParent(false);
+	return DoHatch();
+}
+
+bool
+HatchOut::PrepareParent(bool Restore)
+{
+	if(Restore){
+		if(out && ParInit)out->SetLine(&ParLineDef);
+      }
+	else if(out) {
+		out->GetLine(&ParLineDef);
+ 		ParInit = out->SetLine(&MyLineDef);
+		}
+	return true;
+}
+
+bool
+HatchOut::DoHatch()
+{
+	MkPolyLine(NULL, NULL);
+	switch(ht){
+	case FILL_NONE:											break;
+	case FILL_HLINES:		Lines000();						break;
+	case FILL_VLINES:		Lines090();						break;
+	case FILL_HVCROSS:		Lines000();		Lines090();		break;
+	case FILL_DLINEU:		Lines045();						break;
+	case FILL_DLINED:		Lines315();						break;
+	case FILL_DCROSS:		Lines045();		Lines315();		break;
+	case FILL_STIPPLE1:		case FILL_STIPPLE2:		
+	case FILL_STIPPLE3:		case FILL_STIPPLE4:
+	case FILL_STIPPLE5:		Stipple(ht);					break;
+	case FILL_ZIGZAG:		Zigzag();						break;
+	case FILL_COMBS:		Combs();						break;
+	case FILL_BRICKH:		BricksH();						break;
+	case FILL_BRICKV:		BricksV();						break;
+	case FILL_BRICKDU:		Bricks045();					break;
+	case FILL_BRICKDD:		Bricks315();					break;
+	case FILL_TEXTURE1:		Texture1();						break;
+	case FILL_TEXTURE2:		Texture2();						break;
+	case FILL_WAVES1:		Arcs(FILL_WAVES1);				break;
+	case FILL_SCALES:		Arcs(FILL_SCALES);				break;
+	case FILL_SHINGLES:		Arcs(FILL_SHINGLES);			break;
+	case FILL_WAVES2:		Waves2(0);						break;
+	case FILL_HERRING:		Herringbone();					break;
+	case FILL_CIRCLES:		Circles();						break;
+	case FILL_GRASS:		Grass();						break;
+	case FILL_FOAM:			Foam();							break;
+	case FILL_RECS:			Recs();							break;
+	case FILL_HASH:			Hash();							break;
+	case FILL_WATER:		Waves2(1);						break;
+	case FILL_LIGHT3D:		CircGrad();						break;
+		}
+
+	//clean up
+	if(ho == HO_POLYGON) {
+		if(HatchDef.plg.pts) free(HatchDef.plg.pts);
+		HatchDef.plg.pts = NULL;
+		}
+	ho = HO_NONE;
+	MkPolyLine(NULL, out);
+	return PrepareParent(true);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// collect line segments to a polyline command
+#define MK_PL_MAX 1000
+bool
+HatchOut::MkPolyLine(POINT *p, anyOutput *o)
+{
+	static POINT pl[MK_PL_MAX];
+	static long npt = 0;
+
+	if(p && o) {
+		if(!npt) {
+			memcpy(pl, p, 2*sizeof(POINT));
+			npt = 2;
+			return true;
+			}
+		if(p[0].x != pl[npt-1].x || p[0].y != pl[npt-1].y) {
+			o->oPolyline(pl, npt);
+			memcpy(pl, p, 2*sizeof(POINT));
+			npt = 2;
+			return true;
+			}
+		AddToPolygon(&npt, pl, p+1);
+		if(npt > (MK_PL_MAX-1)) {
+			npt = 0;
+			return o->oPolyline(pl, MK_PL_MAX);
+			}
+		return true;
+		}
+	if(!p && !o) {
+		npt = 0;
+		return true;
+		}
+	if(!p && o && npt) {
+		o->oPolyline(pl, npt);
+		npt = 0;
+		return true;
+      }
+	return false;
+}
+
+//use Bresenham's algorithm to draw lines
+//Ref: P.S. Heckbert (1990) "Digital Line Drawing", in: Graphic Gems
+//   (A.S. Glassner, ed.); Academic Press, Inc.,
+//   ISBN 0-12-286165-5
+bool
+HatchOut::HatchLine(POINT p1, POINT p2)
+{
+	int d, ax, ay, sx, sy, dx, dy;
+	bool bPen;
+	POINT tr[2];
+
+	dx = p2.x - p1.x;
+	if ( p2.x < p1.x) { 	ax = (-dx)<<1;		sx = -1;		}
+	else {					ax = dx <<1;		sx = 1;		}
+	dy = p2.y - p1.y;
+	if (p2.y < p1.y) {	ay = (-dy)<<1;		sy = -1;		}
+	else {					ay = dy<<1;			sy = 1;		}
+	tr[0].x = tr[1].x = p1.x;					tr[0].y = tr[1].y = p1.y;
+	if(IsInside(p1)) bPen = true;
+	else bPen = false;
+	if (ax > ay) {													// x dominant
+		d = ay - (ax >>1);
+		for ( ; ; ) {
+			if(bPen) {
+				if(tr[1].x == p2.x) return MkPolyLine(tr, out);
+				if (d >= 0) {tr[1].y += sy;	d -= ax;}
+				tr[1].x += sx;
+				if(!IsInside(tr[1])) {
+					bPen = false;
+					MkPolyLine(tr, out);
+					tr[0].x = tr[1].x;	tr[0].y = tr[1].y;
+					}
+				}
+			else {
+				if(tr[0].x == p2.x) return true;
+				if (d >= 0) {tr[0].y += sy;	d -= ax;}
+				tr[0].x += sx;
+				if(IsInside(tr[0])){
+					bPen = true;
+					tr[1].x = tr[0].x;	tr[1].y = tr[0].y;
+					}
+				}
+			d += ay;
+			}
+		}
+	else {															// y dominant
+		d = ax - (ay >>1);
+		for ( ; ; ) {
+			if (bPen){
+				if (tr[1].y == p2.y) return MkPolyLine(tr, out);
+				if (d >= 0) {tr[1].x += sx; d -= ay;}
+				tr[1].y += sy;
+				if(!IsInside(tr[1])) {
+					bPen = false;
+					MkPolyLine(tr, out);
+					tr[0].x = tr[1].x;	tr[0].y = tr[1].y;
+					}
+				}
+			else {
+				if (tr[0].y == p2.y) return true;
+				if (d >= 0) {tr[0].x += sx; d -= ay;}
+				tr[0].y += sy;
+				if(IsInside(tr[0])) {
+					bPen = true;
+					tr[1].x = tr[0].x;	tr[1].y = tr[0].y;
+					}
+				}
+			d += ax;
+			}
+		}
+}
+
+//use circular Bresenham's algorithm to draw arcs
+//Ref: C. Montani, R. Scopigno (1990) "Speres-To-Voxel Conversion", in:
+//   Graphic Gems (A.S. Glassner ed.) Academic Press, Inc.; 
+//   ISBN 0-12-288165-5 
+bool
+HatchOut::HatchArc(int ix, int iy, int r, int qad, bool start)
+{
+	int x, y, q, di, de, lim;
+	bool bInside;
+	static POINT tr[2];
+
+	if(r < 1) r = 1;
+	if(start) {
+		tr[1].x = ix-r;		tr[1].y = iy;
+		}
+	for(q = 0; q < qad; q++) {
+		x = lim = 0;	y = r;	di = 2*(1-r);
+		bInside = IsInside(tr[1]);
+		while (y >= lim){
+			if(di < 0) {
+				de = 2*di + 2*y -1;
+				if(de > 0) {
+					x++;	y--;	di += (2*x -2*y +2);
+					}
+				else {
+					x++;	di += (2*x +1);
+					}
+				}
+			else {
+				de = 2*di -2*x -1;
+				if(de > 0) {
+					y--;	di += (-2*y +1);
+					}
+				else {
+					x++;	y--;	di += (2*x -2*y +2);
+					}
+				}
+			tr[0].x = tr[1].x;		tr[0].y = tr[1].y;
+			switch(q) {
+			case 0:	tr[1].x = ix-y;		tr[1].y = iy+x;	break;
+			case 1: tr[1].x = ix+x;		tr[1].y = iy+y;	break;
+			case 2: tr[1].x = ix+y;		tr[1].y = iy-x;	break;
+			case 3:	tr[1].x = ix-x;		tr[1].y = iy-y;	break;
+				}
+			if(IsInside(tr[1])){
+				if(bInside)MkPolyLine(tr, out);
+				bInside = true;
+				}
+			else {
+				if(bInside) MkPolyLine(0L, out);
+				bInside = false;
+				}
+			}
+		}
+	return true;
+}
+
+bool
+HatchOut::IsInside(POINT p)
+{
+	long tmp1, tmp2, tmp3, tmp4;
+
+	if(out->OC_type != OC_HIMETRIC && (p.x < DeskRect.left || p.x > DeskRect.right || p.y < DeskRect.top
+		|| p.y >DeskRect.bottom)) return false;
+	switch(ho){
+	case HO_RECT:
+		if(p.x > HatchDef.rec.rec.left && p.x < HatchDef.rec.rec.right &&
+			p.y > HatchDef.rec.rec.top && p.y < HatchDef.rec.rec.bottom)
+			return true;
+		return false;
+	case HO_CIRCLE:
+		tmp1 = p.x-HatchDef.cir.centre.x;
+		tmp2 = p.y-HatchDef.cir.centre.y;
+		if((tmp1 * tmp1 + tmp2 * tmp2) < HatchDef.cir.sr)
+			return true;
+		return false;
+	case HO_ELLIPSE:
+		tmp1 = p.x-HatchDef.ell.centre.x;
+		tmp2 = p.y-HatchDef.ell.centre.y;
+		tmp3 = HatchDef.ell.iy;
+		tmp4 = HatchDef.ell.ix;
+		if((unsigned long)(tmp1 * tmp1 * tmp3 * tmp3 + tmp2 * tmp2 * tmp4 * tmp4) < HatchDef.ell.sab)
+			return true;
+		return false;
+	case HO_BIGELLYPSE:
+		tmp1 = HatchDef.bell.fo[0].x - p.x;			tmp1 *= tmp1;
+		tmp2 = HatchDef.bell.fo[0].y - p.y;			tmp2 *= tmp2;
+		tmp3 = HatchDef.bell.fo[1].x - p.x;			tmp3 *= tmp3;
+		tmp4 = HatchDef.bell.fo[1].y - p.y;			tmp4 *= tmp4;
+		return (isqr(tmp1+tmp2)+isqr(tmp3+tmp4)) < HatchDef.bell.rsab;
+	case HO_POLYGON:
+		return IsInPolygon(&p, HatchDef.plg.pts, HatchDef.plg.cp);
+		}
+	return false;
+}
+
+void
+HatchOut::Lines000()
+{
+	int y, yinc;
+	POINT Line[2];
+
+	if(2>(yinc = iround(ybase*.8)))yinc = 2;
+	Line[0].x = UseRect.left;
+	Line[1].x = UseRect.right;
+	for(y = UseRect.top; y < UseRect.bottom; y += yinc) {
+		Line[0].y = Line[1].y = y;
+		HatchLine(Line[0], Line[1]);
+		}
+}
+
+void
+HatchOut::Lines090()
+{
+	int x, xinc;
+	POINT Line[2];
+
+	if(2>(xinc = iround(xbase*.8)))xinc = 2;
+	Line[0].y = UseRect.top;
+	Line[1].y = UseRect.bottom;
+	for(x = UseRect.left; x < UseRect.right; x += xinc) {
+		Line[0].x = Line[1].x = x;
+		HatchLine(Line[0], Line[1]);
+		}
+}
+
+void
+HatchOut::Lines045()
+{
+	int x, y, y1, xinc, yinc;
+	POINT Line[2];
+
+	if(3>(xinc=iround(xbase*1.2)))xinc=3;		if(3>(yinc=iround(ybase*1.2)))yinc=3;
+	Line[1].x = x = UseRect.right;
+	Line[0].y = Line[1].y = y = UseRect.bottom;
+	while(x > UseRect.left) {
+		Line[0].x = x = x-xinc;
+		if(y > UseRect.top) Line[1].y = y = y-yinc;
+		else Line[1].x -= xinc;
+		HatchLine(Line[0], Line[1]);
+		}
+	y1 = Line[0].y;
+	while(y1 > UseRect.top) {
+		Line[0].y = y1 = y1-yinc;
+		if(y > UseRect.top) Line[1].y = y = y-yinc;
+		else Line[1].x -= xinc;
+		HatchLine(Line[0], Line[1]);
+		}
+}
+
+void
+HatchOut::Lines315()
+{
+	int x, y, y1, xinc, yinc;
+	POINT Line[2];
+
+	if(3>(xinc=iround(xbase*1.2)))xinc= 3;	if(3>(yinc=iround(ybase*1.2)))yinc= 3;
+	Line[1].x = x = UseRect.right;
+	Line[0].y = Line[1].y = y = UseRect.top;
+	while (x > UseRect.left) {
+		Line[0].x = x = x-xinc;
+		if(y < UseRect.bottom) Line[1].y = y = y+yinc;
+		else Line[1].x -= xinc;
+		HatchLine(Line[0], Line[1]);
+		}
+	y1 = Line[0].y;
+	while(y1 < UseRect.bottom) {
+		Line[0].y = y1 = y1+yinc;
+		if(y < UseRect.bottom) Line[1].y = y = y+yinc;
+		else Line[1].x -= xinc;
+		HatchLine(Line[0], Line[1]);
+		}
+}
+
+void
+HatchOut::Stipple(int type)
+{
+	int x, y, xinc, yinc, level, xspac, yspac;
+	POINT Line[2];
+
+	if(!(xinc = iround(xbase*0.48)))xinc = 1;	if(!(yinc = iround(ybase*0.48)))yinc = 1;
+	if(!(xspac = iround(xbase*0.56)))xspac = 1;	if(!(yspac = iround(ybase*0.56)))yspac = 1;
+	level = 0;
+	for(x = UseRect.left; x < UseRect.right; x += xspac*2) {
+		level &= 0x1;
+		for(y = UseRect.top; y < UseRect.bottom; y += yspac*4) {
+			if(type < FILL_STIPPLE3) {
+				Line[0].x = x;
+				Line[0].y = level? y+yinc+yspac*2 : y+yinc;
+				Line[1].x = Line[0].x+xinc;		Line[1].y = Line[0].y-yinc;
+				if(type == FILL_STIPPLE1)HatchLine(Line[0], Line[1]);
+				Line[0].x = Line[1].x;	Line[0].y = Line[1].y;
+				Line[1].x += xinc;		Line[1].y += yinc;
+				if(type == FILL_STIPPLE1)HatchLine(Line[0], Line[1]);
+				Line[0].x = Line[1].x;	Line[0].y = Line[1].y;
+				Line[1].x -= xinc;		Line[1].y += yinc;
+				HatchLine(Line[0], Line[1]);
+				Line[0].x = Line[1].x;	Line[0].y = Line[1].y;
+				Line[1].x -= xinc;				Line[1].y -= yinc;
+				HatchLine(Line[0], Line[1]);
+				}
+			else if(type <= FILL_STIPPLE5) {
+				Line[0].x = x;
+				Line[0].y =Line[1].y = level? y+yinc+yspac*2 : y+yinc;
+				Line[1].x = Line[0].x+xinc*2;
+				if(type == FILL_STIPPLE3 || type == FILL_STIPPLE4)HatchLine(Line[0], Line[1]);
+				Line[0].x = Line[1].x = x+xinc;	
+				Line[0].y = Line[1].y -yinc;
+				Line[1].y += yinc;
+				if(type == FILL_STIPPLE3 || type == FILL_STIPPLE5)HatchLine(Line[0], Line[1]);
+				}
+			}
+		level++;
+		}
+}
+
+void
+HatchOut::Zigzag()
+{
+	int yinc, ix, iy;
+	POINT Line[2];
+
+	if(3>(yinc = iround(ybase)))yinc=3;
+	if(2>(iy = iround(ybase*.8)))iy=2;		if(2>(ix = iround(xbase*.8)))ix=2;
+	Line[0].x = Line[1].x = UseRect.left;
+	Line[0].y = Line[1].y = UseRect.top;
+	while(Line[0].y < UseRect.bottom +iy) {
+		while(Line[1].x < UseRect.right) {
+			Line[1].y -= iy;					Line[1].x += ix;
+			HatchLine(Line[0], Line[1]);
+			Line[0].x = Line[1].x;			Line[1].x += ix;
+			Line[0].y = Line[1].y;			Line[1].y += iy;
+			HatchLine(Line[0], Line[1]);
+			Line[0].x = Line[1].x;			Line[0].y = Line[1].y;
+			}
+		Line[0].x = Line[1].x = UseRect.left;
+		Line[0].y = Line[1].y += yinc;
+		}
+}
+
+void
+HatchOut::Combs()
+{
+	int x, y, xinc, yinc;
+	POINT Line[2], Next;
+
+	if(!(yinc = iround(ybase*.4)))yinc = 1;
+	if(2 >(xinc = iround(xbase*.69282))) xinc = 2;		//exact yinc *sin(60�)*2
+	y = UseRect.top + yinc;
+	while(y < UseRect.bottom + yinc*2) {
+		Line[0].y = Line[1].y = y;
+		Line[0].x = Line[1].x = UseRect.left-xinc;
+		while(Line[1].x < UseRect.right && Line[1].y >= UseRect.top){
+			Line[1].y -= (yinc*2);
+			HatchLine(Line[0], Line[1]);
+			Line[0].x = Line[1].x;			Line[1].x += xinc;
+			Line[0].y = Line[1].y;			Line[1].y -= yinc;
+			HatchLine(Line[0], Line[1]);
+			Line[0].x = Line[1].x;			Line[0].y = Line[1].y;
+			Line[1].x += xinc;				Line[1].y += yinc;
+			HatchLine(Line[0], Line[1]);
+			Line[1].x -= xinc;				Line[1].y -= yinc;
+			}
+		y += yinc*6;
+		}
+	Next.x = x = UseRect.left-xinc;					Next.y = y;
+	while(x < UseRect.right) {
+		Line[0].y = Line[1].y = Next.y;
+		Line[0].x = Line[1].x = x;
+		while(Line[1].x < UseRect.right&& Line[1].y >= UseRect.top){
+			Line[1].y -= (yinc*2);
+			HatchLine(Line[0], Line[1]);
+			Line[0].x = Line[1].x;			Line[1].x += xinc;
+			Line[0].y = Line[1].y;			Line[1].y -= yinc;
+			HatchLine(Line[0], Line[1]);
+			Line[0].x = Line[1].x;			Line[0].y = Line[1].y;
+			Line[1].x += xinc;				Line[1].y += yinc;
+			HatchLine(Line[0], Line[1]);
+			Line[1].x -= xinc;				Line[1].y -= yinc;
+			}
+		x += xinc*2;
+		}
+}
+
+void
+HatchOut::BricksH()
+{
+	int i, j, y, yinc, xinc;
+	POINT Line[2];
+
+	if(4>(xinc=iround(xbase*1.6)))xinc=4;	if(2>(yinc=iround(ybase*0.8)))yinc=2;
+	for(y = UseRect.top, j = 0; y < UseRect.bottom; y += yinc, j++) {
+		Line[0].x = UseRect.left;
+		Line[1].x = UseRect.right;
+		Line[0].y = Line[1].y = y;
+ 		HatchLine(Line[0], Line[1]);
+		Line[0].y ++;								Line[1].y += yinc;
+		for (i = (j&1)?UseRect.left:UseRect.left+xinc/2;i<UseRect.right;i+=xinc){
+			Line[0].x = Line[1].x = i;
+			HatchLine(Line[0], Line[1]);
+			}
+		}
+}
+
+void
+HatchOut::BricksV()
+{
+	int i, j, x, yinc, xinc;
+	POINT Line[2];
+
+	if(2>(xinc=iround(xbase*0.8)))xinc=2;	if(4>(yinc=iround(ybase*1.6)))yinc=4;
+	for(x = UseRect.left, j= 0; x < UseRect.right; x += xinc, j++) {
+		Line[0].y = UseRect.top;
+		Line[1].y = UseRect.bottom;
+		Line[0].x = Line[1].x = x;
+		HatchLine(Line[0], Line[1]);
+		Line[0].x ++;								Line[1].x += xinc;
+		for (i = (j&1)?UseRect.top:UseRect.top+yinc/2;i<UseRect.bottom;i+=yinc){
+			Line[0].y = Line[1].y = i;
+			HatchLine(Line[0], Line[1]);
+			}
+		}
+}
+
+void
+HatchOut::Bricks045()
+{
+	int xinc, yinc, bwx, bwy;
+	POINT Line[2];
+
+	if(2>(xinc=iround(xbase*.7)))xinc = 2;	if(2>(yinc=iround(ybase*.7)))yinc = 2;
+	bwx = xinc *2;						bwy = yinc *2;
+	Line[0].x = UseRect.left - xinc;
+	Line[0].y = UseRect.top;
+	while((Line[0].y < UseRect.bottom + bwy)) {
+		while(Line[0].x < UseRect.right) {
+			Line[1].x = Line[0].x + bwx;
+			Line[1].y = Line[0].y - bwy;
+			HatchLine(Line[0], Line[1]);
+			Line[0].x = Line[1].x;
+			Line[0].y = Line[1].y;
+			Line[1].x = Line[0].x + xinc;
+			Line[1].y = Line[0].y + yinc;
+			HatchLine(Line[0], Line[1]);
+			Line[0].y += bwy;
+			}
+		Line[0].y += bwy;
+		Line[0].x = UseRect.left - xinc;
+		}
+}
+
+void
+HatchOut::Bricks315()
+{
+	int xinc, yinc, bwx, bwy;
+	POINT Line[2];
+
+	if(2>(xinc=iround(xbase*.7)))xinc = 2;	if(2>(yinc=iround(ybase*.7)))yinc = 2;
+	bwx = xinc *2;							bwy = yinc *2;
+	Line[0].x = UseRect.left - xinc;
+	Line[0].y = UseRect.top -bwy;
+	while((Line[0].y < UseRect.bottom)) {
+		while(Line[0].x < UseRect.right) {
+			Line[1].x = Line[0].x + bwx;
+			Line[1].y = Line[0].y + bwy;
+			HatchLine(Line[0], Line[1]);
+			Line[0].x = Line[1].x;
+			Line[0].y = Line[1].y;
+			Line[1].x = Line[0].x + xinc;
+			Line[1].y = Line[0].y - yinc;
+			HatchLine(Line[0], Line[1]);
+			Line[0].y -= bwy;
+			}
+		Line[0].y += bwy;
+		Line[0].x = UseRect.left - xinc;
+		}
+}
+
+void
+HatchOut::Texture1()
+{
+	int j, xinc, yinc;
+	POINT Line[2];
+
+	if(!(xinc = iround(xbase*0.4)))xinc = 1;	if(!(yinc = iround(ybase*0.4)))yinc = 1;
+	Line[0].x = UseRect.left - xinc;
+	Line[0].y = UseRect.top - yinc;
+	j = 0;
+	while((Line[0].y < UseRect.bottom)) {
+		while(Line[0].x < UseRect.right) {
+			Line[1].x = Line[0].x;
+			Line[1].y = Line[0].y + (yinc *2);
+			HatchLine(Line[0], Line[1]);
+			Line[0].x += xinc;
+			Line[0].y += yinc;
+			Line[1].x = Line[0].x + (xinc *2);
+			Line[1].y = Line[0].y;
+			HatchLine(Line[0], Line[1]);
+			Line[0].x += xinc*3;
+			Line[0].y -= yinc;
+			}
+		j++;
+		Line[0].y += yinc *2;
+		Line[0].x = UseRect.left - xinc;
+		if(j &0x01) Line[0].x += (xinc * 2);
+		}
+}
+
+
+void
+HatchOut::Texture2()
+{
+	int j, xinc, yinc;
+	POINT Line[2];
+
+	if(2>(xinc=iround(xbase*.6)))xinc= 2;	if(2>(yinc=iround(ybase*.6)))yinc= 2;
+	Line[0].x = UseRect.left - xinc*2;		Line[0].y = UseRect.top - yinc*2;
+	j = 0;
+	while((Line[0].y < UseRect.bottom)) {
+		while(Line[0].x < UseRect.right) {
+			Line[1].x = Line[0].x;			Line[1].y = Line[0].y + (yinc *3);
+			HatchLine(Line[0], Line[1]);
+			Line[0].x += xinc;				Line[1].x = Line[0].x;
+			HatchLine(Line[0], Line[1]);
+			Line[0].y += yinc;				Line[1].x = Line[0].x + (xinc *3);
+			Line[1].y = Line[0].y;
+			HatchLine(Line[0], Line[1]);
+			Line[0].y += yinc;				Line[1].y = Line[0].y;
+			HatchLine(Line[0], Line[1]);
+			Line[0].x += xinc*3;			Line[0].y -= yinc*2;
+			}
+		j++;
+		Line[0].y += yinc *2;		Line[0].x = UseRect.left - xinc*2;
+		if(j &0x01) Line[0].x += (xinc *2);
+		}
+}
+
+void
+HatchOut::Arcs(int type)
+{
+	int i, j, level, ix, iy;
+
+	if(type == FILL_SHINGLES) {
+		iy = iround(ybase*1.6);			ix = iround(xbase*.8);
+		}
+	else {
+		iy = iround(ybase*.8);			ix = iround(xbase*.8);
+		}
+	if(iy < 2) iy = 2;				if(ix < 2) ix = 2;
+	UseRect.right += ix;		UseRect.top -= (iy<<1);	UseRect.bottom += (iy<<1);
+	for(i = UseRect.top, level = 0; i < UseRect.bottom; i+= iy, level++) {
+		if(type == FILL_WAVES1) {
+			HatchArc(UseRect.left, i, ix, 0, true);
+			for(j = UseRect.left; j < UseRect.right; j += ix*2) HatchArc(j, i, ix, 2, false);
+			i += iy/3;
+			}
+		else if(type == FILL_SCALES) {
+			HatchArc(UseRect.left, i, ix, 0, true);
+			for(j = UseRect.left; j < UseRect.right; j += ix*2)
+				HatchArc((level &1) ? j+ix:j, i, ix, 2, false);
+			i++;
+			}
+		else {
+			for(j = UseRect.left; j < UseRect.right; j += ix*2){
+				HatchArc((level &1) ? j+ix:j, i-1, ix, 0, true);
+				HatchArc((level &1) ? j+ix:j, i + iy/2, ix, 2, false);
+				}
+			i++;
+			}
+		}
+}
+
+void
+HatchOut::Waves2(int type)					//hatch using sine waves
+{
+	int i, j, level, y, ix, yinc, *pts;
+	POINT Line[2];
+	double dtmp;
+
+	if(3>(yinc = iround(type?ybase*.8 : ybase*1.2)))yinc = 3;	
+	if(type == 0 && 14>(ix = iround(xbase*2.5)))ix = 14;
+	else if(type == 1 && 7>(ix = iround(xbase*1.2)))ix = 7;
+	if(!(pts = (int *)malloc(ix * sizeof(int))))return;
+	for(i = 0; i < ix; i++) {
+		dtmp = sin(6.283185307/((double)ix/(double)i));
+		if(type == 1) dtmp /= 2.0;
+		pts[i] = dtmp > 0.0 ? iround(0.3*ybase*dtmp) : iround(0.3*ybase*dtmp);
+		}
+	UseRect.bottom += yinc;					UseRect.right++;
+	for(y = UseRect.top, level = 0; y <= UseRect.bottom; y += yinc, level++){
+		Line[0].x = UseRect.left;			Line[1].x = UseRect.left+1;
+		Line[0].y = y;						Line[1].y = y+pts[1];
+		if(type == 0) for(j = 2; Line[0].x < UseRect.right; j++) {
+			HatchLine(Line[0], Line[1]);
+			Line[0].x = Line[1].x;			Line[1].x++;
+			Line[0].y = Line[1].y;			Line[1].y = y + pts[j%ix];
+			}
+		else if(type == 1) {
+			if(level & 1) {
+				Line[0].x += (ix + (ix>>1));		Line[1].x = Line[0].x +1;
+				}
+			for(j = 2; Line[0].x < UseRect.right; j++){
+				HatchLine(Line[0], Line[1]);
+				Line[0].x = Line[1].x;
+				Line[0].y = Line[1].y;				Line[1].y = y + pts[j%ix];
+				if((j-1) % ix) Line[1].x++;
+				else {
+					HatchLine(Line[0], Line[1]);
+					Line[1].x += (ix << 1);			Line[0].x = Line[1].x -1;
+					}
+				}
+			}
+		}
+	free(pts);
+}
+
+void
+HatchOut::Herringbone()
+{
+	int ix1, ix2, iy1, iy2, y;
+	POINT Line[2];
+
+	if(2>(ix1 = iround(xbase*.6)))ix1 = 2;		ix2 = ix1*4;
+	if(2>(iy1 = iround(ybase*.6)))iy1 = 2;		iy2 = iy1*4;
+	for(y = UseRect.top; y <= UseRect.bottom + iy2; y += iy1*2) {
+		Line[0].x = UseRect.left-ix2;		Line[1].x = Line[0].x + ix2;
+		Line[0].y = y;						Line[1].y = Line[0].y - iy2;
+		do {
+			HatchLine(Line[0], Line[1]);
+			Line[0].x = Line[1].x - ix1;	Line[1].x = Line[0].x + ix2;
+			Line[0].y = Line[1].y - iy1;	Line[1].y = Line[0].y + iy2;
+			HatchLine(Line[0], Line[1]);
+			Line[0].x = Line[1].x - ix1;	Line[1].x = Line[0].x + ix2;
+			Line[0].y = Line[1].y + iy1;	Line[1].y = Line[0].y - iy2;
+			} while(Line[0].x <= UseRect.right);
+		}
+}
+
+void
+HatchOut::Circles()
+{
+	int x, y, r, level, xspac, yspac;
+
+	if(2 > (xspac = iround(+xbase*.8))) xspac = 2;
+	if(4 > (yspac = iround(ybase*1.38564))) yspac = 4;
+	if(1 > (r = iround(xbase*.4)))r = 1;
+	level = 0;
+	UseRect.bottom += yspac;		UseRect.right += r;
+	for(x = UseRect.left; x < UseRect.right; x += xspac) {
+		level &= 0x1;
+		for(y = UseRect.top; y < UseRect.bottom; y += yspac*2) {
+			HatchArc(x, level? y+yspac : y, r, 4, true);
+			}
+		level++;
+		}
+}
+
+void
+HatchOut::Grass()
+{
+	int i, count, dh, dw;
+	double xsize, ysize;
+	long idum = -1;
+	POINT pts[2];
+
+	xsize = xbase *2.0;		ysize = ybase*2.0;
+	if(xsize < 4.0) xsize = 4.0;	if(ysize < 4.0) ysize = 4.0;
+	IncrementMinMaxRect(&UseRect, (int)xsize);
+	count = (UseRect.right -UseRect.left)*(UseRect.bottom-UseRect.top);
+	i = (int)(xsize*ysize*0.15);		if(i) count /= i;
+	dh = UseRect.bottom-UseRect.top;	dw = UseRect.right-UseRect.left;
+	for(i = 0; i < count; i++) {
+		pts[0].x = UseRect.left+(int)(dw*ran2(&idum));
+		pts[0].y = UseRect.top+(int)(dh*ran2(&idum));
+		pts[1].x = pts[0].x + (int)(ran2(&idum)*xsize);
+		pts[1].y = pts[0].y + (int)(ran2(&idum)*ysize);
+		if(pts[0].x != pts[1].x || pts[0].y != pts[1].y) HatchLine(pts[0], pts[1]);
+		}
+}
+
+void
+HatchOut::Foam()
+{
+	int i, count, dh, dw;
+	double xsize;
+	long idum = -1;
+
+	xsize = xbase *0.9;
+	if(xsize < 2.0) xsize = 2.0;
+	IncrementMinMaxRect(&UseRect, (int)xsize);
+	count = (UseRect.right -UseRect.left)*(UseRect.bottom-UseRect.top);
+	count /= (int)(xsize*xsize);
+	dh = UseRect.bottom-UseRect.top;	dw = UseRect.right-UseRect.left;
+	for(i = 0; i < count; i++) {
+		HatchArc(UseRect.left+(int)(dw*ran2(&idum)), UseRect.top+(int)(dh*ran2(&idum)),
+			(int)(ran2(&idum)*ran2(&idum)*xsize +xsize*.2), 4, true);
+		}
+}
+
+void
+HatchOut::Recs()
+{
+	int i, count, dh, dw;
+	double xsize, ysize;
+	long idum = -1;
+	POINT Line[5];
+
+	xsize = xbase *2.8;	ysize = ybase *2.8;
+	if(xsize < 4.0) xsize = 4.0;		if(ysize < 4.0) ysize = 4.0;
+	IncrementMinMaxRect(&UseRect, (int)xsize);
+	count = (UseRect.right -UseRect.left)*(UseRect.bottom-UseRect.top);
+	i = (int)(floor(xsize*ysize*.4));	if(i) count /= i;
+	dh = UseRect.bottom-UseRect.top;	dw = UseRect.right-UseRect.left;
+	for(i = 0; i < count; i++) {
+		Line[0].x = Line[3].x = Line[4].x = UseRect.left+(int)(dw*ran2(&idum));
+		Line[0].y = Line[1].y = Line[4].y = UseRect.top+(int)(dh*ran2(&idum));
+		Line[1].x = Line[2].x = Line[0].x + (int)(ran2(&idum)*xsize +xsize*.2);
+		Line[2].y = Line[3].y = Line[0].y + (int)(ran2(&idum)*ysize +ysize*.2);
+		HatchLine(Line[0], Line[1]);			HatchLine(Line[1], Line[2]);
+		HatchLine(Line[2], Line[3]);			HatchLine(Line[3], Line[4]);
+		}
+}
+
+void
+HatchOut::Hash()
+{
+	int i, dh, dw, cx, cy, xinc, yinc;
+	double xsize, ysize, mix, miy;
+	long idum = -1;
+	POINT Line[5];
+
+	xsize = xbase * 0.9;						ysize = ybase * 0.9;
+	xinc = iround(xsize * 3.3);					yinc = iround(ysize * 2.2);
+	mix = xbase *.5;							miy = ybase *.5;
+	dw = iround(xbase > 5 ? xbase/2.0 : 2.0);	dh = iround(ybase > 5 ? xbase/2.0 : 2.0);
+	if(xsize < 2.0) xsize = 2.0;		if(ysize < 2.0) ysize = 2.0;
+	IncrementMinMaxRect(&UseRect, (int)xsize);
+	for(i = 0, cy = UseRect.top; cy < UseRect.bottom; i++, cy += yinc) {
+		for(cx = (i & 1) ? UseRect.left:UseRect.left+xinc/2; cx < UseRect.right; cx += xinc) { 
+			Line[0].x = Line[1].x = cx;
+			Line[0].y = cy - iround(ran2(&idum) * ysize + miy);
+			Line[1].y = cy + iround(ran2(&idum) * ysize + miy);
+			HatchLine(Line[0], Line[1]);
+			Line[0].x = Line[1].x = cx + dw;
+			Line[0].y = cy - iround(ran2(&idum) * ysize + miy);
+			Line[1].y = cy + iround(ran2(&idum) * ysize + miy);
+			HatchLine(Line[0], Line[1]);
+			Line[0].y = Line[1].y = cy;
+			Line[0].x = cx - iround(ran2(&idum) * xsize + mix);
+			Line[1].x = cx + iround(ran2(&idum) * xsize + mix);
+			HatchLine(Line[0], Line[1]);
+			Line[0].y = Line[1].y = cy + dh;
+			Line[0].x = cx - iround(ran2(&idum) * xsize + mix);
+			Line[1].x = cx + iround(ran2(&idum) * xsize + mix);
+			HatchLine(Line[0], Line[1]);
+			}
+		}
+}
+
+void
+HatchOut::CircGrad()
+{
+	int i;
+	double f;
+	LineDEF ld = {0.0, 1.0, 0x0, 0x0};
+
+	for(i = 1; i < circ_grad.r; i++) {
+		f = (((double)i)/((double)circ_grad.r));
+		f = f*f;
+		ld.color = IpolCol(dFillCol, dFillCol2, f);
+		out->SetLine(&ld);
+		HatchArc(circ_grad.cx, circ_grad.cy, i, 4, true);
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// export to *.tif file (tag image file)
+// This code is based on information from the following book
+// G. Born, 'Referenzhandbuch Dateiformate', 
+//     Addison-Wesley ISBN 3-89319-815-6
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class ExportTif:public anyOutput {
+public:
+	ExportTif(GraphObj *g, char *FileName, DWORD flags, double res, double wi, double he);
+	~ExportTif();
+	bool StartPage();
+	bool EndPage();
+
+private:
+	int w, h;
+	anyOutput *bmo;
+	char *name;
+	int oFile;
+};
+
+ExportTif::ExportTif(GraphObj *g, char *FileName, DWORD flags, double res, double wi, double he)
+{
+	hres = vres = res;
+	name = 0L;							bmo = 0L;
+	DeskRect.left = DeskRect.top = 0;
+	DeskRect.right = DeskRect.bottom = 0x4fffffff;
+	dFillCol = 0xffffffffL;
+	if(g && FileName) {
+		w = un2ix(wi);		h = un2iy(he);
+		if(bmo = NewBitmapClass(w, h, hres, vres)){
+			bmo->VPorg.fy = -co2fiy(g->GetSize(SIZE_GRECT_TOP));
+			bmo->VPorg.fx = -co2fix(g->GetSize(SIZE_GRECT_LEFT));
+			bmo->Erase(0x00ffffffL);
+			g->DoPlot(bmo);
+			}
+		name = (char*)memdup(FileName, (int)strlen(FileName)+1, 0);
+		}
+	oFile = 0;
+}
+
+ExportTif::~ExportTif()
+{
+	if(name) free(name);
+	if(bmo) DelBitmapClass(bmo);
+}
+
+bool
+ExportTif::StartPage()
+{
+	unsigned char header[] = {
+		0x49, 0x49,							//intel byte order
+		0x2a, 0x00,							//version 4.2
+		0x08, 0x00, 0x00, 0x00,				//the image file directory just follows
+		0x0f, 0x00,							//number of tags in IFD
+		0xfe, 0x00, 0x04, 0x00, 0x01, 0x00, //new subfile tag
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x00, 0x01, 0x04, 0x00, 0x01, 0x00,	//image width tag (pixels)
+		0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
+		0x01, 0x01, 0x04, 0x00, 0x01, 0x00,	//image height tag (pixels)
+		0x00, 0x00, 0x64, 0x00, 0x00, 0x00,
+		0x02, 0x01, 0x03, 0x00, 0x01, 0x00,	//BitsPerSample tag
+		0x00, 0x00, 0x08, 0x00, 0x00, 0x00, //   ...8 bits per sample
+		0x03, 0x01, 0x03, 0x00, 0x01, 0x00, //compression tag
+		0x00, 0x00, 0x01, 0x00, 0x00, 0x00,	//   ...no compression
+		0x06, 0x01, 0x03, 0x00, 0x01, 0x00, //photometric interpretation
+		0x00, 0x00, 0x02, 0x00, 0x00, 0x00,	//   ...its RGB
+		0x11, 0x01, 0x04, 0x00, 0x01, 0x00,	//strip (image) offset
+		0x00, 0x00, 0x82, 0x00, 0x00, 0x00,	//   ... number of strips and offset to data
+		0x12, 0x01, 0x03, 0x00, 0x01, 0x00,	//orientation tag
+		0x00, 0x00, 0x01, 0x00, 0x00, 0x00,	//   ...its top/left
+		0x15, 0x01, 0x03, 0x00, 0x01, 0x00,	//samples per pixel
+		0x00, 0x00, 0x03, 0x00, 0x00, 0x00,	//   ... 3 samples (colors) for RGB
+		0x17, 0x01, 0x04, 0x00, 0x01, 0x00,	//strip byte count
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+		0x1a, 0x01, 0x05, 0x00, 0x01, 0x00,	//horizontal resolution
+		0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+		0x1b, 0x01, 0x05, 0x00, 0x01, 0x00,	//vertical resolution
+		0x00, 0x00, 0x00, 0x00, 0x01, 0x00,
+		0x1c, 0x01, 0x03, 0x00, 0x01, 0x00,	//planar configuration
+		0x00, 0x00, 0x01, 0x00, 0x01, 0x00,	//   ... one layer
+		0x28, 0x01, 0x03, 0x00, 0x01, 0x00,	//resolution units
+		0x00, 0x00, 0x02, 0x00, 0x01, 0x00,	//   ... dots per inch
+		0x31, 0x01, 0x02, 0x00, 0x01, 0x00,	//Software
+		0x00, 0x00, 0x00, 0x00, 0x01, 0x00	//   ... RLPlot ...
+		};
+	DWORD res_info[4] = {iround(hres), 0x01, iround(vres), 0x01};
+	char prog_name[20];
+	int cb;
+
+	if(name && bmo) {
+#ifdef USE_WIN_SECURE
+		if(_sopen_s(&oFile, name, O_RDWR | O_BINARY | O_CREAT | O_TRUNC, 
+			0x40, S_IWRITE) || oFile < 0){
+			ErrorBox("Could not open output file");
+			return false;
+			}
+#else
+		if(-1 ==(oFile = open(name, O_RDWR | O_BINARY | O_CREAT | O_TRUNC,
+			S_IWRITE | S_IREAD))){
+			ErrorBox("Could not open output file");
+			return false;
+			}
+#endif
+		*((int*)(header+30))= w;		*((int*)(header+42))= h;
+		*((DWORD*)(header+126)) = (DWORD)(w * h * 3);
+		*((int*)(header+90)) = sizeof(header) + sizeof(res_info) + 20;
+		*((int*)(header+138)) = sizeof(header);
+		*((int*)(header+150)) = sizeof(header)+8;
+		*((int*)(header+186)) = sizeof(header)+16;
+		cb = rlp_strcpy(prog_name, 20, "RLPlot ");
+		rlp_strcpy(prog_name+cb, 20-cb, SZ_VERSION);
+#ifdef USE_WIN_SECURE
+		_write(oFile, &header, sizeof(header));
+		_write(oFile, &res_info, sizeof(res_info));
+		_write(oFile, &prog_name, 20);
+
+#else
+		write(oFile, &header, sizeof(header));
+		write(oFile, &res_info, sizeof(res_info));
+		write(oFile, &prog_name, 20);
+#endif
+		return true;
+		}
+	return false;
+}
+
+bool
+ExportTif::EndPage()
+{
+	int i, j, c;
+	DWORD pix;
+	unsigned char *pix_data, *cpix = (unsigned char*)&pix;
+
+	if(bmo && (pix_data = (unsigned char*)malloc(3072))){
+		for(i = c = 0; i < h; i++) {
+			for(j = 0; j < w; j++) {
+				bmo->oGetPix(j, i, &pix);			pix_data[c++] = cpix[0];
+				pix_data[c++] = cpix[1];			pix_data[c++] = cpix[2];
+				if(c >= 3072) {
+#ifdef USE_WIN_SECURE
+					_write(oFile, pix_data, 3072);
+#else
+					write(oFile, pix_data, 3072);
+#endif
+					c = 0;
+					}
+				}
+			}
+#ifdef USE_WIN_SECURE
+		_write(oFile, pix_data, c);
+#else
+		write(oFile, pix_data, c);
+#endif
+		free(pix_data);
+		}
+#ifdef USE_WIN_SECURE
+	_close(oFile);
+#else
+	close(oFile);
+#endif
+	oFile = -1;
+	return true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Entry point to export graph to tag image file
+void DoExportTif(GraphObj *g, char *FileName, DWORD flags)
+{
+	ExportTif *ex;
+	double res, width, height;
+
+	if(!g || !FileName) return;
+	res = 98.0;
+	width = g->GetSize(SIZE_GRECT_RIGHT) - g->GetSize(SIZE_GRECT_LEFT);
+	height = g->GetSize(SIZE_GRECT_BOTTOM) - g->GetSize(SIZE_GRECT_TOP);
+	if(GetBitmapRes(&res, &width, &height, "Export Tag Image File")){
+		ex = new ExportTif(g, FileName, flags, res, width, height);
+		if(ex->StartPage())	ex->EndPage();
+		delete(ex);
+		}
+}	
diff --git a/PlotObs.cpp b/PlotObs.cpp
index fb2f240..b3ce97a 100755
--- a/PlotObs.cpp
+++ b/PlotObs.cpp
@@ -1,5713 +1,6152 @@
-//PlotObs.cpp, Copyright (c) 2001-2007 R.Lackner
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// This modules contains code for the differnt Plot objects. Plots are
-// graphic objects containing more objects, which represent the data.
-// Several Plots may be contained in a Graph: Plots are the different layers
-// of a Graph.
-// Most part of this module has been moved here from rlplot.cpp of
-// earlier versions. 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-#include "rlplot.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <string.h>
-
-extern char TmpTxt[];
-extern Default defs;
-extern int cPlots;
-extern GraphObj *CurrGO, *TrackGO;			//Selected Graphic Objects
-extern Axis **CurrAxes;						//axes of current graph
-extern UndoObj Undo;
-
-int AxisTempl3D = 0;
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Plot::Plot(GraphObj *par, DataObj *d):GraphObj(par, d)
-{
-	int pos, nsize;
-
-	Id = GO_PLOT;
-	Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-	if(name = (char*)malloc((nsize = 20)*sizeof(char))){
-		pos = rlp_strcpy(name, nsize, (char*)"Plot");
-		add_int_to_buff(&name, &pos, &nsize, ++cPlots, true, 0);
-		}
-	use_xaxis = use_yaxis = use_zaxis = 0;	hidden = 0;
-	x_info = y_info = z_info = data_desc = 0L;
-	x_tv = y_tv = 0L;	x_dtype = y_dtype = z_dtype = 0;
-}
-
-double
-Plot::GetSize(int select)
-{
-	switch(select){
-	case SIZE_MINE:				return 0.0;
-	//The Bounds values must be returned by every plot:
-	//   they are necessary for scaling !
-	case SIZE_BOUNDS_XMIN:		
-		return parent ? parent->GetSize(SIZE_BOUNDS_XMIN) : Bounds.Xmin;
-	case SIZE_BOUNDS_XMAX:		
-		return parent ? parent->GetSize(SIZE_BOUNDS_XMAX) : Bounds.Xmax;
-	case SIZE_BOUNDS_YMIN:		
-		return parent ? parent->GetSize(SIZE_BOUNDS_YMIN) : Bounds.Ymin;	
-	case SIZE_BOUNDS_YMAX:		
-		return parent ? parent->GetSize(SIZE_BOUNDS_YMAX) : Bounds.Ymax;
-	case SIZE_BARMINX:
-	case SIZE_BARMINY:
-		return 1.0f;
-	default:
-		return DefSize(select);
-		}
-}
-
-DWORD
-Plot::GetColor(int select)
-{
-	if(parent) return parent->GetColor(select);
-	else return defs.Color(select);
-}
-
-void
-Plot::CheckBounds(double x, double y)
-{
-	if(x < Bounds.Xmin) Bounds.Xmin = x;	if(x > Bounds.Xmax) Bounds.Xmax = x;
-	if(y < Bounds.Ymin) Bounds.Ymin = y;	if(y > Bounds.Ymax) Bounds.Ymax = y;
-}
-
-bool
-Plot::UseAxis(int idx)
-{
-	if(CurrAxes && CurrAxes[idx]) {
-		switch(CurrAxes[idx]->type & 0xf) {
-		case 1:									// x-axis
-			Undo.ValInt(parent, &use_xaxis, 0L);
-			use_xaxis = idx;			return true;
-		case 2:									// y-axis
-			Undo.ValInt(parent, &use_yaxis, 0L);
-			use_yaxis = idx;			return true;
-		case 3:									// z-axis
-			Undo.ValInt(parent, &use_zaxis, 0L);
-			use_zaxis = idx;			return true;
-			}
-		}
-	return false;
-}
-
-void
-Plot::ApplyAxes(anyOutput *o)
-{
-	if(!o || !CurrAxes || !parent) return;
-	if(use_xaxis && CurrAxes[use_xaxis]) {
-		o->UseAxis(CurrAxes[use_xaxis]->axis, CurrAxes[use_xaxis]->type & 0xf);
-		}
-	else use_xaxis = 0;
-	if(use_yaxis && CurrAxes[use_yaxis]) {
-		o->UseAxis(CurrAxes[use_yaxis]->axis, CurrAxes[use_yaxis]->type & 0xf);
-		}
-	else use_yaxis = 0;
-	if(use_zaxis && CurrAxes[use_zaxis]) {
-		o->UseAxis(CurrAxes[use_zaxis]->axis, CurrAxes[use_zaxis]->type & 0xf);
-		}
-	else use_zaxis = 0;
-	return;
-}
-
-void
-Plot::CheckBounds3D(double x, double y, double z)
-{
-	if(x < xBounds.fx) xBounds.fx = x;	if(x > xBounds.fy) xBounds.fy = x;
-	if(y < yBounds.fx) yBounds.fx = y;	if(y > yBounds.fy) yBounds.fy = y;
-	if(z < zBounds.fx) zBounds.fx = z;	if(z > zBounds.fy) zBounds.fy = z;
-	CheckBounds(x, y);
-}
-
-bool
-Plot::SavVarObs(GraphObj **gol, long ngo, DWORD flags)
-{
-	int i;
-	void *ptr;
-
-	if(!gol || !ngo) return false;
-	SavVarInit(150 * ngo);
-	for(i = 0; i < ngo; i++) 
-		if(gol[i]) gol[i]->FileIO(SAVE_VARS);
-	ptr = SavVarFetch();
-	Undo.SavVarBlock(this, &ptr, flags);
-	return true;
-}
-
-DataObj *
-Plot::CreaCumData(char *xr, char *yr, int mode, double base)
-{
-	char **yranges;
-	int i, j, nc, nr, ir, ic, n, c_num, c_txt, c_datetime;
-	double value, old_val;
-	DataObj *CumData = 0L;
-	anyResult ares;
-	AccRange *ax = 0L, **ayy = 0L;
-	TextValue *tv = 0L;
-	bool *validRows;
-
-	if(!xr || !yr || !mode || !data) return 0L;
-	if(!(CumData = new DataObj()))return 0L;
-	//count valid data lines
-	if(!(ax = new AccRange(xr))) {
-		delete CumData;		CumData = 0L;	return 0L;
-		}
-	ax->DataTypes(data, &c_num, &c_txt, &c_datetime);
-	nr = ax->CountItems();
-	if(!(yranges = split(yr, '&', &nc))){
-		delete CumData;		delete ax;		return 0L;
-		}
-	if(x_tv) x_tv->Reset();		if(y_tv) y_tv->Reset();
-	j = mode == 1 || mode == 2 ? nr : nr * 2;
-	if(CumData->Init(j , nc+2) && (validRows = (bool*)calloc(j, sizeof(bool)))){
-		if(!c_num && (c_txt + c_datetime) > 0 ) {
-			if(x_tv) tv = x_tv;
-			else if(y_tv) tv = y_tv;
-			else tv = x_tv = new TextValue();
-			}
-		//setup all ranges
-		if(!(ayy = (AccRange**)calloc(nc, sizeof(AccRange*))))return 0L;
-		for(i = 0; i < nc; i++) {
-			if(yranges[i] && *yranges[i] && (ayy[i] = new AccRange(yranges[i]))) {
-				if(!ayy[i]->GetFirst(&ic, &ir)) return 0L;
-				}
-			}
-		// set x values as first column
-		for(i = n = 0, ax->GetFirst(&ic, &ir); ax->GetNext(&ic, &ir); i++, n++) {
-			if(data->GetResult(&ares, ir, ic, false)) {
-				if(tv) {
-					switch(ares.type) {
-					case ET_TEXT:
-						value = tv->GetValue(ares.text);			break;
-					default:
-						TranslateResult(&ares);
-						value = tv->GetValue(ares.text);			break;
-						}
-					CumData->SetValue(n, 0, value);		CumData->SetValue(n, 1, base);
-					}
-				else if(ares.type == ET_VALUE && ares.value > -HUGE_VAL && ares.value < HUGE_VAL) {
-					CumData->SetValue(n, 0, value = ares.value);	CumData->SetValue(n, 1, base);
-					}
-				else {
-					CumData->SetValue(n, 0, value = 0.0);	CumData->SetValue(n, 1, base);
-					}
-				if(mode == 3 || mode == 4){				//complete polygon data
-					CumData->SetValue((nr<<1)-i-1, 0, value);
-					}
-				for(j = 0; j < nc; j++) {
-					if(CumData->GetValue(n, j+1, &value)) CumData->SetValue(n, j+2, value);
-					if(ayy[j]->GetNext(&ic, &ir) && data->GetResult(&ares, ir, ic, false)){
-						if(ares.type == ET_VALUE && ares.value > -HUGE_VAL && ares.value < HUGE_VAL){
-							value = ares.value;		validRows[i] = true;
-							}
-						else value = 0.0;			old_val = 0.0;
-						CumData->GetValue(n, j+2, &old_val);
-						switch (mode) {
-						case 1:	case 3:	value += old_val;			break;
-						case 2:	case 4: value = old_val -value;		break;
-							}
-						CumData->SetValue(n, j+2, value);
-						}
-					if(mode == 3 || mode == 4)			//complete polygon data
-						if(CumData->GetValue(n, j+1, &value)){
-							if(validRows[n]) validRows[(nr<<1)-i-1] = true;
-							CumData->SetValue((nr<<1)-i-1, j+2, value);
-							}
-					}
-				}
-			else {
-				for(j = 0; j < nc; j++) ayy[j]->GetNext(&ic, &ir);
-				}
-			}
-		for(i = 0; i < nc; i++) delete ayy[i];		free(ayy);
-		for(i = 0; i < CumData->cRows; i++) {
-			if(!validRows[i]) {
-				CumData->cRows--;
-				for(j = 0; j < CumData->cCols; j++) {
-					if(CumData->etRows[i][j]) delete CumData->etRows[i][j];
-					}
-				free(CumData->etRows[i]);
-				for(j = i; j < CumData->cRows; j++) {
-					CumData->etRows[j] = CumData->etRows[j+1];
-					validRows[j] = validRows[j+1];
-					}
-				if(!validRows[i] && i < CumData->cRows) i--;
-				}
-			}
-		free(validRows);
-		}
-	for(i = 0; i < nc; i++) if(yranges[i]) free(yranges[i]);
-	if(ax) delete ax;		free(yranges);
-	return CumData;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// PlotScatt handles most XY-Plots: its a Plot-Class
-PlotScatt::PlotScatt(GraphObj *par, DataObj *d, DWORD presel):Plot(par, d)
-{
-	FileIO(INIT_VARS);
-	DefSel = presel;
-	Id = GO_PLOTSCATT;
-	if (!d) {
-		if(parent && parent->Command(CMD_DELOBJ, this, NULL)) return;
-		ErrorBox("Attempt to create plot\nwithout any data.");
-		return;
-		}
-}
-
-PlotScatt::PlotScatt(GraphObj *par, DataObj *d, int cBars, Bar **bars, ErrorBar **errs):Plot(par, d)
-{
-	int i;
-
-	FileIO(INIT_VARS);
-	if(cBars && bars) {
-		if((Bars = (Bar**)calloc(cBars, sizeof(Bar*)))) {
-			nPoints = cBars;
-			for(i = 0; i < cBars; i++) {
-				if((Bars[i] = bars[i])) Bars[i]->parent = this;
-				bars[i] = 0L;
-				}
-			}
-		}
-	if(cBars && errs) {
-		if((Errors = (ErrorBar**)calloc(cBars, sizeof(Bar*)))) {
-			nPoints = cBars;
-			for(i = 0; i < cBars; i++) {
-				if((Errors[i] = errs[i])) Errors[i]->parent = this;
-				errs[i] = 0L;
-				}
-			}
-		}
-	Id = GO_PLOTSCATT;
-}
-
-PlotScatt::PlotScatt(GraphObj *par, DataObj *d, int nPts, Symbol **sym, DataLine *lin):
-	Plot(par, d)
-{
-	int i;
-
-	FileIO(INIT_VARS);
-	nPoints = nPts;
-	if(Symbols = sym) for(i = 0; i < nPts; i++) if(Symbols[i]) Symbols[i]->parent = this;
-	if(TheLine = lin) TheLine->parent = this;
-	Id = GO_PLOTSCATT;
-}
-
-PlotScatt::PlotScatt(int src):Plot(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-PlotScatt::~PlotScatt()
-{
-	ForEach(FE_FLUSH, 0L, 0L);
-	if(name) free(name);			name=0L;
-	if(x_info) free(x_info);		x_info = 0L;
-	if(y_info) free(x_info);		y_info = 0L;
-	if(data_desc) free(data_desc);	data_desc = 0L;
-	if(x_tv) delete(x_tv);			x_tv = 0L;
-	if(y_tv) delete(y_tv);			y_tv = 0L;
-	Undo.InvalidGO(this);
-}
-
-double
-PlotScatt::GetSize(int select)
-{
-	int i;
-	double ft1, ft2, d;
-
-	switch(select){
-	case SIZE_BARMINX:
-		if(BarDist.fx >= 0.0001) return BarDist.fx;
-		if((!Bars) || (nPoints < 2)) return BarDist.fx = 1.0;
-		ft1 = -HUGE_VAL;	ft2 = HUGE_VAL;		BarDist.fx= HUGE_VAL;
-		for(i = 0; i < nPoints; i++) {
-			if(Bars[i]) {
-				ft2 = Bars[i]->GetSize(SIZE_XPOS);
-				d = fabs(ft2-ft1);
-				if(d != 0.0 && d < BarDist.fx) BarDist.fx = d;
-				}
-			ft1 = ft2;
-			}
-		return BarDist.fx = BarDist.fx > 0.0001 && BarDist.fx != HUGE_VAL  ? BarDist.fx : 1.0;
-	case SIZE_BARMINY:
-		if(BarDist.fy >= 0.0001) return BarDist.fy;
-		if((!Bars) || (nPoints < 2)) return BarDist.fy = 1.0;
-		ft1 = -HUGE_VAL;	ft2 = HUGE_VAL;		BarDist.fy= HUGE_VAL;
-		for(i = 0; i < nPoints; i++) {
-			if(Bars[i]) {
-				ft2 = Bars[i]->GetSize(SIZE_YPOS);
-				d = fabs(ft2-ft1);
-				if(d != 0.0 && d < BarDist.fy) BarDist.fy = d;
-				}
-			ft1 = ft2;
-			}
-		return BarDist.fy = BarDist.fy > 0.0001 && BarDist.fy != HUGE_VAL  ? BarDist.fy : 1.0;
-	default:
-		return Plot::GetSize(select);
-		}
-}
-
-bool
-PlotScatt::SetSize(int select, double value)
-{
-	int i;
-
-	switch(select & 0xfff){
-	case SIZE_BARMINX:
-		BarDist.fx = value;
-		return true;
-	case SIZE_BARMINY:
-		BarDist.fy = value;
-		return true;
-	case SIZE_SYMBOL:		case SIZE_SYM_LINE:
-		if(Symbols)	for(i = 0; i < nPoints; i++) 
-			if(Symbols[i]) Symbols[i]->SetSize(select, value);
-		return true;
-	case SIZE_WHISKER:		case SIZE_WHISKER_LINE:
-	case SIZE_ERRBAR:		case SIZE_ERRBAR_LINE:
-		if(Errors)	for(i = 0; i < nPoints; i++) 
-			if(Errors[i]) Errors[i]->SetSize(select, value);
-		return true;
-	case SIZE_BAR_LINE:		case SIZE_BAR:		case SIZE_XBASE:		case SIZE_YBASE:
-		if(Bars) for(i = 0; i < nPoints; i++) 
-			if(Bars[i]) Bars[i]->SetSize(select, value);
-		return true;
-	case SIZE_LB_XDIST:		case SIZE_LB_YDIST:
-		if(Labels) for(i = 0; i < nPoints; i++)
-			if(Labels[i]) Labels[i]->SetSize(select, value);
-		return true;
-	case SIZE_ARROW_LINE:	case SIZE_ARROW_CAPWIDTH:	case SIZE_ARROW_CAPLENGTH:
-		if(Arrows) for(i = 0; i < nPoints; i++)
-			if(Arrows[i]) Arrows[i]->SetSize(select, value);
-		return true;
-	}
-	return false;
-}
-
-bool
-PlotScatt::SetColor(int select, DWORD col)
-{
-	int i;
-	GraphObj **go = 0L;
-
-	switch(select) {
-	case COL_SYM_LINE:
-	case COL_SYM_FILL:		go = (GraphObj**)Symbols;		break;
-	case COL_WHISKER:
-	case COL_ERROR_LINE:	go = (GraphObj**)Errors;		break;
-	case COL_BAR_LINE:
-	case COL_BAR_FILL:		go = (GraphObj**)Bars;			break;
-	case COL_ARROW:			go = (GraphObj**)Arrows;		break;
-	default:				return false;
-		}
-	if(go) for(i = 0; i < nPoints; i++)
-		if(go[i]) go[i]->SetColor(select, col);
-	return true;
-}
-
-void
-PlotScatt::DoPlot(anyOutput *o)
-{
-	if(!parent) return;
-	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
-	if(use_xaxis || use_yaxis) {
-		ApplyAxes(o);
-		ForEach(FE_PLOT, 0L, o);
-		parent->Command(CMD_AXIS, 0L, o);
-		}
-	else {
-		ForEach(FE_PLOT, 0L, o);
-		}
-	dirty = false;
-}
-
-bool
-PlotScatt::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		if(hidden) return false;
-		if(!CurrGO && ((MouseEvent*)tmpl)->Action == MOUSE_LBUP)
-			return ForEach(cmd, tmpl, o);
-		return false;
-	case CMD_LEGEND:
-		if(((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
-		if(Bars) for (i = 0; i < nPoints; i++)
-			if(Bars[i]) Bars[i]->Command(cmd, tmpl, o);
-		if(Symbols) {
-			if(TheLine && TheLine->Id == GO_DATALINE) {
-				for (i = 0; i < nPoints && i < 100; i++)
-					if(Symbols[i]) ((Legend*)tmpl)->HasSym(&TheLine->LineDef, Symbols[i], 0L);
-				}
-			else {
-				for (i = 0; i < nPoints && i < 100; i++)
-					if(Symbols[i]) ((Legend*)tmpl)->HasSym(0L, Symbols[i], 0L);
-				}
-			if(TheLine && TheLine->Id == GO_DATAPOLYGON) TheLine->Command(cmd, tmpl, o);
-			}
-		else if(TheLine) TheLine->Command(cmd, tmpl, o);
-		if(Errors) for (i = 0; i < nPoints; i++)
-			if(Errors[i]) Errors[i]->Command(cmd, tmpl, o);
-		break;
-	case CMD_MRK_DIRTY:
-		dirty = true;
-	case CMD_SETSCROLL:		case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		return false;
-	case CMD_USEAXIS:
-		return UseAxis(*((int*)tmpl));
-	case CMD_FLUSH:
-		return ForEach(FE_FLUSH, 0L, 0L);
-	case CMD_TEXTTHERE:
-		if(Labels) for(i = 0; i < nPoints; i++)	if(Labels[i] &&  Labels[i]->Command(cmd, tmpl, o))	return true;
-		return false;
-	case CMD_AUTOSCALE:
-		if(hidden) return false;
-		if(dirty){
-			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
-			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-			}
-		else{
-			if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH && 
-				Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin) {
-				((Plot*)parent)->CheckBounds(Bounds.Xmin, Bounds.Ymin);
-				((Plot*)parent)->CheckBounds(Bounds.Xmax, Bounds.Ymax);
-				return true;
-				}
-			}
-		dirty = false;
-	case CMD_UPDATE:
-		if(cmd == CMD_UPDATE){
-			if (Symbols) SavVarObs((GraphObj **)Symbols, nPoints, UNDO_CONTINUE);
-			if (Bars) SavVarObs((GraphObj **)Bars, nPoints, UNDO_CONTINUE);
-			if (Errors) SavVarObs((GraphObj **)Errors, nPoints, UNDO_CONTINUE);
-			if (Arrows) SavVarObs((GraphObj **)Arrows, nPoints, UNDO_CONTINUE);
-			if (DropLines) SavVarObs((GraphObj **)DropLines, nPoints, UNDO_CONTINUE);
-			if (Labels) SavVarObs((GraphObj **)Labels, nPoints, UNDO_CONTINUE);
-			dirty = true;
-			}
-	case CMD_SET_DATAOBJ:
-		if(cmd == CMD_SET_DATAOBJ) {
-			Id = GO_PLOTSCATT;
-			if(data && data == (DataObj *) tmpl) return true;
-			data = (DataObj *)tmpl;	
-			}
-		ForEach(cmd, tmpl, o);
-		if(cmd == CMD_AUTOSCALE) {
-			if(x_tv) {
-				Bounds.Xmin = 0.5;		Bounds.Xmax = ((double)x_tv->Count())+0.5;
-				}
-			if(y_tv) {
-				Bounds.Ymin = 0.5;		Bounds.Xmax = ((double)y_tv->Count())+0.5;
-				}
-			}
-		if(cmd == CMD_AUTOSCALE && parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH
-			&& Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin) {
-			((Plot*)parent)->CheckBounds(Bounds.Xmin, Bounds.Ymin);
-			((Plot*)parent)->CheckBounds(Bounds.Xmax, Bounds.Ymax);
-			}
-		return true;
-	case CMD_SCALE:
-		return ForEach(cmd, tmpl, o);
-	case CMD_MUTATE:		case CMD_REPL_GO:
-		dirty = true;
-		return ForEach(cmd == CMD_REPL_GO ? FE_REPLGO : FE_MUTATE, tmpl, o);
-	case CMD_SYMTEXT:		case CMD_SYMTEXT_UNDO:	case CMD_SYM_RANGETEXT:
-	case CMD_SYMTEXTDEF:	case CMD_SYM_TYPE:
-		if(Symbols) for(i = 0; i < nPoints; i++)
-			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_SETTEXTDEF:
-		if(Labels) for(i = 0; i < nPoints; i++)
-			if(Labels[i]) Labels[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_DL_LINE:		case CMD_DL_TYPE:
-		if(DropLines) for(i = 0; i < nPoints; i++)
-			if(DropLines[i]) DropLines[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_ERR_TYPE:		case CMD_WHISKER_STYLE:		case CMD_ERRDESC:
-		if(Errors) for(i = 0; i < nPoints; i++) {
-			if(Errors[i]) Errors[i]->Command(cmd, tmpl, o);
-			}
-	case CMD_BAR_TYPE:		case CMD_BAR_FILL:
-		if(Bars) for(i = 0; i < nPoints; i++) {
-			if(Bars[i]) Bars[i]->Command(cmd, tmpl, o);
-			}
-		return true;
-	case CMD_ARROW_TYPE:	case CMD_ARROW_ORG:
-		if(Arrows) for(i = 0; i < nPoints; i++) {
-			if(Arrows[i]) Arrows[i]->Command(cmd, tmpl, o);
-			}
-		return true;
-	case CMD_DELOBJ:
-		dirty = true;
-		if(parent && tmpl && o) return ForEach(FE_DELOBJ, tmpl, o);
-		break;
-	case CMD_SAVE_SYMBOLS:
-		return SavVarObs((GraphObj **)Symbols, nPoints, 0L);
-	case CMD_SAVE_BARS:
-		return SavVarObs((GraphObj **)Bars, nPoints, 0L);
-	case CMD_SAVE_ERRS:
-		return SavVarObs((GraphObj **)Errors, nPoints, 0L);
-	case CMD_SAVE_ARROWS:
-		return SavVarObs((GraphObj **)Arrows, nPoints, 0L);
-	case CMD_SAVE_DROPLINES:
-		return SavVarObs((GraphObj **)DropLines, nPoints, 0L);
-	case CMD_SAVE_LABELS:
-		return SavVarObs((GraphObj **)Labels, nPoints, 0L);
-		}
-	return false;
-}
-
-bool
-PlotScatt::ForEach(int cmd, void *tmp, anyOutput *o)
-{
-	int i, j;
-	GraphObj **obs[] = {(GraphObj**)Symbols, (GraphObj**)Errors, (GraphObj**)Arrows,
-		(GraphObj**)DropLines, (GraphObj**)Labels, (GraphObj**)Bars};
-	GraphObj ***go = 0L;
-	GraphObj **tmpPlots;
-	bool bRedraw;
-
-	switch(cmd) {
-	case FE_MUTATE:
-	case FE_REPLGO:
-		if((tmpPlots = (GraphObj **)tmp) && tmpPlots[0] && tmpPlots[1]) {
-			for(j = 0; j < 6; j++){
-				if(obs[j]) for(i = 0; i < nPoints; i++){
-					if(obs[j][i] && obs[j][i] == tmpPlots[0]) {
-						if(cmd == FE_REPLGO) return ReplaceGO(&obs[j][i], tmpPlots);
-						else {
-							Undo.MutateGO(&obs[j][i], tmpPlots[1], 0L, o);
-							return true;
-							}
-						}
-					}
-				}
-			if(TheLine == tmpPlots[0]){
-				if(cmd == FE_REPLGO) return ReplaceGO((GraphObj**)&TheLine, tmpPlots);
-				else {
-					Undo.MutateGO((GraphObj**)&TheLine, tmpPlots[1], 0L, o);
-					return true;
-					}
-				}
-			}
-		return false;
-	case FE_PARENT:
-		for(j = 0; j < 6; j++){
-			if(obs[j]) for(i = 0; i < nPoints; i++){
-				if(obs[j][i]) obs[j][i]->parent = this;
-				}
-			}
-		if(TheLine) TheLine->parent = this;
-		return true;
-	case CMD_UPDATE:	case CMD_SET_DATAOBJ:	case CMD_AUTOSCALE:		case CMD_SCALE:
-		for(j = 0; j < 6; j++){
-			if(obs[j]) for(i = 0; i < nPoints; i++){
-				if(obs[j][i]) obs[j][i]->Command(cmd, tmp, o);
-				}
-			}
-		if(TheLine) TheLine->Command(cmd, tmp, o);
-		return true;
-	case FE_PLOT:
-		if(TheLine) TheLine->DoPlot(o);
-		for(j = 5; j >= 0; j--){
-			if(obs[j]) for(i = 0; i < nPoints; i++){
-				if(obs[j][i]) obs[j][i]->DoPlot(o);
-				}
-			}
-		return true;
-	case FE_FLUSH:
-		for(j = 0; j < 6; j++){
-			if(obs[j]) {
-				for(i = 0; i < nPoints; i++) if(obs[j][i]) DeleteGO(obs[j][i]);
-				free(obs[j]);	obs[j] = 0L;
-				}
-			}
-		if(ErrRange) free(ErrRange);	if(yRange) free(yRange);
-		if(xRange) free(xRange);		if(LbRange) free(LbRange);
-		ErrRange = yRange = xRange = LbRange = 0L;
-		if(TheLine) DeleteGO(TheLine);
-		Bars = 0L;		Symbols = 0L;		Errors = 0L; 
-		Arrows = 0L;	DropLines = 0L;		Labels = 0L;	TheLine = 0L;
-		return true;
-	case FE_DELOBJ:
-		if(!o) return false;
-		for(j = 0, bRedraw = false, go = 0L; j < 6 && !bRedraw; j++) {
-			if(obs[j]) for(i = 0; i < nPoints; i++){
-				if(obs[j][i]){
-					if(tmp == (void*)obs[j][i]) {
-						o->MrkMode = MRK_NONE;
-						o->MouseCursor(MC_WAIT, true);
-						Undo.DeleteGO(&obs[j][i], 0L, o);
-						switch(j) {
-						case 0: go = (GraphObj***)&Symbols;		break;
-						case 1: go = (GraphObj***)&Errors;		break;
-						case 2: go = (GraphObj***)&Arrows;		break;
-						case 3: go = (GraphObj***)&DropLines;	break;
-						case 4: go = (GraphObj***)&Labels;		break;
-						case 5: go = (GraphObj***)&Bars;		break;
-							}
-						bRedraw = true;
-						break;
-						}
-					}
-				}
-			}
-		if(!bRedraw && TheLine && tmp == (void *) TheLine) {
-			o->MrkMode = MRK_NONE;
-			Undo.DeleteGO((GraphObj**)(&TheLine), 0L, o);
-			bRedraw = true;
-			}
-		if(bRedraw && go) for(i = j = 0; i < nPoints; i++) if(go[0][i]) j++;
-		if(!j) Undo.DropMemory(this, (void**)go, UNDO_CONTINUE);
-		if(bRedraw && dirty) Command(CMD_AUTOSCALE, 0L, o); 
-		if(!Bars && !Symbols && !Errors && !Arrows && !TheLine && !DropLines
-			&& !Labels) parent->Command(CMD_DELOBJ_CONT, this, o);
-		else if(bRedraw) parent->Command(CMD_REDRAW, NULL, o);
-		return bRedraw;
-	default:							//pass command to all objects
-		for(j = 0; j < 6; j++){
-			if(obs[j]) for(i = 0; i < nPoints; i++){
-				if(obs[j][i]) if(obs[j][i]->Command(cmd, tmp, o)) return true;
-				}
-			}
-		if(TheLine) return (TheLine->Command(cmd, tmp, o));
-		return false;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// xyStat is based on scatterplot
-xyStat::xyStat(GraphObj *par, DataObj *d):PlotScatt(par, d, 0L)
-{
-	FileIO(INIT_VARS);
-	Id = GO_XYSTAT;
-}
-
-xyStat::xyStat(int src):PlotScatt(0)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-xyStat::~xyStat()
-{
-	ForEach(FE_FLUSH, 0L, 0L);
-	if(curr_data) delete curr_data;			curr_data = 0L;
-	if(case_prefix) free(case_prefix);		case_prefix = 0L;
-	if(yRange) free(yRange);				yRange = 0L;
-	if(xRange) free(xRange);				xRange = 0L;
-	if(name) free(name);					name=0L;
-	if(x_info) free(x_info);				x_info = 0L;
-	if(y_info) free(x_info);				y_info = 0L;
-	if(x_tv) delete(x_tv);					x_tv = 0;
-	Undo.InvalidGO(this);
-}
-
-bool
-xyStat::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch (cmd) {
-	case CMD_UPDATE:
-		if (Symbols) SavVarObs((GraphObj **)Symbols, nPoints, UNDO_CONTINUE);
-		if (Bars) SavVarObs((GraphObj **)Bars, nPoints, UNDO_CONTINUE);
-		if (Errors) SavVarObs((GraphObj **)Errors, nPoints, UNDO_CONTINUE);
-		if (Labels) SavVarObs((GraphObj **)Labels, nPoints, UNDO_CONTINUE);
-		CreateData();
-		ForEach(CMD_SET_DATAOBJ, curr_data, o);
-		ForEach(CMD_UPDATE, tmpl, o);
-		return dirty = true;
-	case CMD_SET_DATAOBJ:
-		if(cmd == CMD_SET_DATAOBJ) {
-			Id = GO_XYSTAT;
-			if(data && data == (DataObj *) tmpl) return true;
-			if(curr_data) delete curr_data;		curr_data = 0L;
-			data = (DataObj *)tmpl;
-			if(data && !curr_data) CreateData();
-			tmpl = curr_data;
-			}
-		ForEach(cmd, tmpl, o);
-		return true;
-	default:
-		return PlotScatt::Command(cmd, tmpl, o);
-		}
-	return false;
-}
-
-void
-xyStat::CreateData()
-{
-	int i, j, k, l, m, n, *ny, c_num, c_txt, c_dattim;
-	double y, ss, d, lo, hi, **ay, *ax, *tay, *q1, *q2, *q3;
-	lfPOINT *xy;
-	AccRange *rX, *rY;
-	anyResult x_res, y_res;
-
-	if(!data || !xRange || !yRange || !xRange[0] || !yRange[0]) return;
-	if(!(rX = new AccRange(xRange)) || !(rY = new AccRange(yRange))) return;
-	if(!x_info) x_info = rX->RangeDesc(data, 0);	if(!y_info) y_info = rY->RangeDesc(data, 0);
-	m = rX->CountItems();	n = 0;
-	if(m < 2 || !(xy = (lfPOINT*) malloc(m * sizeof(lfPOINT)))) {
-		delete rX;	delete rY;
-		return;
-		}
-	if(x_tv) delete x_tv;					x_tv = 0L;
-	ny = (int*) calloc(m, sizeof(int));
-	ay = (double**) calloc(m, sizeof(double*));
-	ax = (double*) calloc(m, sizeof(double));
-	tay = (double*)malloc(m * sizeof(double));
-	if(!ny || !ay || !ax || !tay) {
-		if(ny) free(ny);	if(ay) free(ay);
-		if(ax) free(ax);	if(tay) free(tay);
-		delete rX;	delete rY;
-		return;
-		}
-	rX->DataTypes(data, &c_num, &c_txt, &c_dattim);
-	if(c_num < 5 && (c_txt + c_dattim) > 5) {
-		x_tv = new TextValue();	
-		}
-	rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);	dirty = true;
-	rX->GetNext(&i, &j);	rY->GetNext(&k, &l);	n=0;
-	do {
-		if(data->GetResult(&x_res, j, i, false) && data->GetResult(&y_res, l, k, false) && y_res.type == ET_VALUE) {
-			xy[n].fy = y_res.value;
-			if(x_tv){ 
-				switch(x_res.type) {
-				case ET_TEXT:
-					xy[n++].fx = x_tv->GetValue(x_res.text);
-					break;
-				case ET_VALUE:	case ET_BOOL:	case ET_DATE:	case ET_TIME:	case ET_DATETIME:
-					TranslateResult(&x_res);
-					xy[n++].fx = x_tv->GetValue(x_res.text);
-					break;
-					}
-				}
-			else if(x_res.type == ET_VALUE) xy[n++].fx = x_res.value;
-			}
-		}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
-	delete rX;			delete rY;
-	if(!n) {
-		if(ny) free(ny);	if(ay) free(ay);
-		if(ax) free(ax);	if(tay) free(tay);
-		return;
-		}
-	SortFpArray(n, xy);
-	for(i = j = 0; i < (n-1); i++, j++) {
-		ax[j] = xy[i].fx;		tay[0] = xy[i].fy;
-		ny[j] = 1;
-		for(k = 1; xy[i+1].fx == xy[i].fx; k++) {
-			tay[k] = xy[i+1].fy;
-			i++;		ny[j]++;
-			}
-		ay[j] = (double*)memdup(tay, k * sizeof(double), 0);
-		}
-	if(xy[i].fx > xy[i-1].fx) {
-		ax[j] = xy[i].fx;		tay[0] = xy[i].fy;
-		ny[j] = 1;
-		ay[j++] = (double*)memdup(tay, sizeof(double), 0);
-		}
-	if(type & 0x0480) {		//medians and/or percentiles required
-		q1 = (double *)malloc(j * sizeof(double));
-		q2 = (double *)malloc(j * sizeof(double));
-		q3 = (double *)malloc(j * sizeof(double));
-		if(q1 && q2 && q3) {
-			for(i = 0; i < j; i++) {
-				if(ny[i] > 1) d_quartile(ny[i], ay[i], q1+i, q2+i, q3+i);
-				else q1[i] = q2[i] = q3[i] = *ay[i];
-				}
-			}
-		else type &= (~0x0480);
-		}
-	else q1 = q2 = q3 = 0L;
-	if((curr_data = curr_data ? curr_data : new DataObj()) && curr_data->Init(j, 6)) {
-		for(i = 0; i < j; i++) curr_data->SetValue(i,0,ax[i]);	// set x-values
-		for(i = 0; i < j; i++) {								// set y-values
-			if(ny[i] > 1) switch(type & 0x00f0) {
-				case 0x0010:	default:
-					curr_data->SetValue(i, 1, y=d_amean(ny[i], ay[i]));
-					break;
-				case 0x0020:
-					curr_data->SetValue(i, 1, y=d_gmean(ny[i], ay[i]));
-					break;
-				case 0x0040:
-					curr_data->SetValue(i, 1, y=d_hmean(ny[i], ay[i]));
-					break;
-				case 0x0080:
-					curr_data->SetValue(i, 1, y=q2[i]);
-					break;
-				}
-			else curr_data->SetValue(i, 1, y= *ay[i]);
-			curr_data->SetValue(i, 4, y);
-			}
-		for(i = 0; i < j; i++) {								// set errors
-			switch(type & 0x1f00) {
-			case 0x0100:	case 0x0200:	case 0x1000:	//SD, SEM, conf. int.
-				if(ny[i] > 1) {
-					ss = d_variance(ny[i], ay[i], &y);
-					switch(type & 0x1f00) {
-					case 0x0100:
-						curr_data->SetValue(i, 2, sqrt(ss));
-						break;
-					case 0x0200:
-						curr_data->SetValue(i, 2, sqrt(ss)/sqrt((double)ny[i]));
-						break;
-					case 0x1000:
-						d = distinv(t_dist, ny[i]-1, 1, 1.0-(ci/100.0), 2.0);
-						curr_data->SetValue(i, 2, d * sqrt(ss)/sqrt((double)ny[i]));
-						break;
-						}
-					}
-				else curr_data->SetValue(i, 2, 0.0);
-				if(curr_data->GetValue(i, 1, &y) && curr_data->GetValue(i, 2, &hi))
-					curr_data->SetValue(i, 4, hi+y);
-				break;
-			case 0x0400:								//percentiles
-				curr_data->SetValue(i, 2, q1[i]);	curr_data->SetValue(i, 3, q3[i]);
-				curr_data->SetValue(i, 4, q3[i]);
-				break;
-			case 0x0800:								//min-max
-				lo = hi = *ay[i];
-				for(k = 1; k < ny[i]; k++) {
-					if(ay[i][k] < lo) lo = ay[i][k];
-					if(ay[i][k] > hi) hi = ay[i][k];
-					}
-				curr_data->SetValue(i, 2, lo);		curr_data->SetValue(i, 3, hi);
-				curr_data->SetValue(i, 4, hi);
-				break;
-				}
-			}
-		if(type & 0x6000) for(i = 0; i < j; i++) {				// number of cases
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "%s%d", case_prefix ? case_prefix : "", ny[i]);
-#else
-			sprintf(TmpTxt, "%s%d", case_prefix ? case_prefix : "", ny[i]);
-#endif
-			curr_data->SetText(i, 5, TmpTxt);
-			}
-		}
-	if(q1) free(q1);	if(q2) free(q2);	if(q3) free(q3);
-	for(i = 0; i < m; i++) if(ay[i]) free(ay[i]);
-	free(tay);	free(ay);	free(ax);	free(ny);	free(xy);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// BarChart is based on scatterplot
-BarChart::BarChart(GraphObj *par, DataObj *d):PlotScatt(par, d, 0L)
-{
-	Id = GO_BARCHART;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Frequenc distribution: bar chart with function
-FreqDist::FreqDist(GraphObj *par, DataObj *d):Plot(par, d)
-{
-	FileIO(INIT_VARS);
-	Id = GO_FREQDIST;
-}
-
-FreqDist::FreqDist(GraphObj *par, DataObj *d, char* range, bool bOnce):Plot(par, d)
-{
-	FileIO(INIT_VARS);
-	if(range && range[0]) {
-		ssRef = (char*)memdup(range, (int)strlen(range)+1, 0);
-		plots = (GraphObj**)calloc(nPlots=3, sizeof(GraphObj*));
-		ProcData(-1);
-		if(bOnce && ssRef) {
-			free(ssRef);		ssRef = 0L;
-			}
-		}
-	Id = GO_FREQDIST;
-}
-
-FreqDist::FreqDist(GraphObj *par, DataObj *d, double *vals, int nvals, int nclasses):Plot(par, d)
-{
-	int i, j;
-	int *cl_data;
-	Bar **bars;
-
-	FileIO(INIT_VARS);
-	ssRef = 0L;
-	plots = (GraphObj**)calloc(nPlots=3, sizeof(GraphObj*));
-	for(i = 0, dmin = HUGE_VAL, dmax = -HUGE_VAL; i < nvals; i++) {
-		if(vals[i] < dmin) dmin = vals[i];
-		if(vals[i] > dmax) dmax = vals[i];
-		}
-	start = dmin;		step = 1.00001*(dmax-dmin)/((double)nclasses);
-	if(!(cl_data = (int*)calloc(nclasses+1, sizeof(int)))) return;
-	for(i = 0; i < nvals; i++) {
-		j = (int)(floor((vals[i] - start)/step));
-		if(j >= 0 && j <= nclasses) cl_data[j]++;
-		}
-	if(cl_data[nclasses]) nclasses++;
-	if(bars = (Bar**)calloc(nclasses, sizeof(Bar*))) for(i = 0; i < nclasses; i++) {
-		if(bars[i] = new Bar(this, 0L, i*step+start, (double)cl_data[i], BAR_VERTB | BAR_RELWIDTH,
-			-1, -1, -1, -1, "Count")) bars[i]->SetSize(SIZE_BAR, 100.0);
-		}
-	//create bar chart
-	if(bars && (plots[0] = new PlotScatt(this, data, nclasses, bars, 0L))){
-		plots[0]->Command(CMD_BAR_FILL, &BarFill, 0L);
-		plots[0]->SetColor(COL_BAR_LINE, BarLine.color);
-		plots[0]->SetSize(SIZE_BAR_LINE, BarLine.width);
-		}
-	if(plots[0]){
-		Bounds.Xmin = dmin;		Bounds.Xmax = dmax;
-		plots[0]->Command(CMD_AUTOSCALE, 0L, 0L);
-		}
-	free(cl_data);
-	Id = GO_FREQDIST;
-}
-
-FreqDist::FreqDist(int src):Plot(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-FreqDist::~FreqDist()
-{
-	int i;
-
-	if(curr_data) delete(curr_data);		curr_data = 0L;
-	if(ssRef) free(ssRef);					ssRef = 0L;
-	if(plots) {
-		for(i = 0; i < nPlots; i++) if(plots[i]) DeleteGO(plots[i]);
-		free(plots);						plots=0L;
-		}
-	if(name) free(name);					name=0L;
-	if(x_info) free(x_info);				x_info = 0L;
-	if(y_info) free(y_info);				y_info = 0L;
-	if(x_tv) delete x_tv;					x_tv = 0L;
-}
-
-void
-FreqDist::DoPlot(anyOutput *o)
-{
-	int i;
-
-	if(!plots || !o || !data || !parent) return;
-	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
-	if(use_xaxis || use_yaxis) ApplyAxes(o);
-	for(i = 0; i < nPlots; i++) {
-		if(plots[i]) {
-			if(plots[i]->Id >= GO_PLOT && plots[i]->Id < GO_GRAPH){
-				if(((Plot*)plots[i])->hidden == 0) plots[i]->DoPlot(o);
-				}
-			else plots[i]->DoPlot(o);
-			}
-		}
-	if(use_xaxis || use_yaxis) parent->Command(CMD_AXIS, 0L, o);
-}
-
-bool
-FreqDist::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		if(hidden || ((MouseEvent*)tmpl)->Action != MOUSE_LBUP || CurrGO) return false;
-	case CMD_LEGEND:
-		if(plots) for(i = 0; i < nPlots; i++) {
-			if(plots[i]){
-				if(plots[i]->Id >= GO_PLOT && plots[i]->Id < GO_GRAPH){
-					if(((Plot*)plots[i])->hidden == 0) plots[i]->Command(cmd, tmpl, o);
-					}
-				else plots[i]->Command(cmd, tmpl, o);
-				}
-			}
-		return false;
-	case CMD_SCALE:
-		if(plots) for(i = 0; i < nPlots; i++) if(plots[i]) plots[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_DELOBJ:
-		if(plots && tmpl && parent) for(i = 0; i < nPlots; i++) if(plots[i]){
-			if(tmpl == (void*)plots[i]) {
-				DeleteGO(plots[i]);	plots[i]=0L;
-				if(i == 1) type=0;
-				parent->Command(CMD_REDRAW, 0L, o);
-				return true;
-				}
-			}
-		return false;
-	case CMD_UPDATE:
-		if(data && parent && plots) {
-			ProcData(0);
-			if(!curr_data) return false;
-			for(i = 0; i < nPlots; i++) if(plots[i]) {
-				plots[i]->Command(CMD_SET_DATAOBJ, curr_data, o);
-				plots[i]->Command(CMD_UPDATE, 0L, o);
-				}
-			}
-		return false;
-	case CMD_SET_DATAOBJ:
-		Id = GO_FREQDIST;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_MRK_DIRTY:
-		dirty = true;
-	case CMD_SETSCROLL:		case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		return false;
-	case CMD_USEAXIS:
-		return UseAxis(*((int*)tmpl));
-	case CMD_AUTOSCALE:
-		if(hidden) return false;
-		if(dirty){
-			Bounds.Xmin = HUGE_VAL;		Bounds.Ymin = 0;
-			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-			if(dmax > dmin) {
-				Bounds.Xmin = dmin;		Bounds.Xmax = dmax;
-				}
-			}
-		else{
-			if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH && 
-				Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin) {
-				((Plot*)parent)->CheckBounds(Bounds.Xmin, Bounds.Ymin);
-				((Plot*)parent)->CheckBounds(Bounds.Xmax, Bounds.Ymax);
-				return true;
-				}
-			}
-		dirty = false;
-		if(plots) for(i = 0; i < nPlots; i++) {
-			if(plots[i]){
-				if(plots[i]->Id >= GO_PLOT && plots[i]->Id < GO_GRAPH){
-					if(((Plot*)plots[i])->hidden == 0) plots[i]->Command(cmd, tmpl, o);
-					}
-				else plots[i]->Command(cmd, tmpl, o);
-				}
-			}
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH && 
-			Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin) {
-			((Plot*)parent)->CheckBounds(Bounds.Xmin, Bounds.Ymin);
-			((Plot*)parent)->CheckBounds(Bounds.Xmax, Bounds.Ymax);
-			return true;
-			}
-	case CMD_DROP_PLOT:		case CMD_DROP_GRAPH:
-		if(tmpl && plots && nPlots >1) {
-			if(plots[1]) DeleteGO(plots[1]);
-			plots[1] = (GraphObj*)tmpl;		dirty = true;		plots[1]->parent = this;
-			plots[1]->Command(CMD_SET_DATAOBJ, curr_data, o);
-			Command(CMD_AUTOSCALE, 0L, o);
-			return true;
-			}
-		return false;
-		}
-	return false;
-}
-
-void
-FreqDist::ProcData(int sel)
-{
-	AccRange *ar;
-	int nv, i, j, r, c, ncl, *f_data, cb_f, size_fo, pos_fo, c_num, c_txt, c_dattim;
-	double min, max, sum, mean, sd, tmp, *s_data, *t_data, lstep;
-	double chi2, df, x, y;
-	anyResult *result;
-	bool bValid = false;
-	Bar **bars = 0L;
-	anyResult res;
-	TextDEF td;
-	char *fo, *fdesc = 0L, formula[500];
-
-	if(!parent || !data || !ssRef || !plots) return;
-	if(curr_data) delete(curr_data);		curr_data = 0L;
-	if(x_tv) delete x_tv;					x_tv = 0L;
-	if((curr_data = new DataObj()) && (ar = new AccRange(ssRef))) {
-		if(!y_info && (y_info = (char*)malloc(25*sizeof(char)))) rlp_strcpy(y_info, 25, "No. of observations");
-		dmin = HUGE_VAL, dmax = -HUGE_VAL;
-		ar->DataTypes(data, &c_num, &c_txt, &c_dattim);
-		if(c_num < 5 && (c_txt + c_dattim) > 5) {
-			x_tv = new TextValue();		dmin = 0.0;
-			}
-		//copy spreadsheet data into array
-		nv = ar->CountItems();			ar->GetFirst(&c, &r);
-		if(!(s_data = (double*)malloc(nv * sizeof(double))) 
-			|| !(t_data = (double*)malloc(nv * sizeof(double)))) {
-			delete(ar);					return;
-			}
-		for(sum = 0.0, nv = 0; ar->GetNext(&c, &r); ) if(data->GetResult(&res, r, c, false)) {
-			if(x_tv){
-				switch(res.type) {
-				case ET_TEXT:
-					if((tmp = x_tv->GetValue(res.text))> 0.0)bValid = true;
-					else bValid = false;				break;
-				case ET_VALUE:	case ET_DATE:	case ET_TIME:	case ET_DATETIME:	case ET_BOOL:
-					TranslateResult(&res);
-					if((tmp = x_tv->GetValue(res.text))> 0.0)bValid = true;
-					else bValid = false;				break;
-				default: 
-					bValid = false;						break;
-					}
-				}
-			else {
-				if(res.type == ET_VALUE) {
-					tmp = res.value;		bValid = true;
-					}
-				else bValid = false;
-				}
-			if(bValid) {
-				if(tmp > dmax) dmax = tmp;	if(tmp < dmin) dmin = tmp;
-				s_data[nv] = tmp;
-				switch (type & 0xff){
-				case 2:
-					if(tmp > 0.0) t_data[nv] = log(tmp);
-					else nv--;
-					break;
-				default:	t_data[nv] = tmp;		break;
-					}
-				nv++;
-				}
-			}
-		delete(ar);
-		if(!nv || dmin >= dmax) {
-			free(s_data);		s_data = 0L;		free(t_data);		t_data = 0L;
-			return;
-			}
-		min = dmin;		max = dmax;
-		lstep = (max-min)/100.0;
-		d_variance(nv, t_data, &mean, &sd);
-		sd = sqrt(sd/((double)(nv-1)));
-		step = fabs(step);
-		if(x_tv) {
-			start = 0.5;	step = 1.0;		max+= 0.5;
-			}
-		else if(sel == -1) {
-			start = min;	step = (max - min)/(step != 0.0 ? step : 7.0);
-			}
-		else if(sel == -2) {
-			min = start;
-			}
-		ncl = (int)(floor((max-start)/step))+1;
-		if(plots[0] &&	(max > (Bounds.Xmax+step/2.0) || min < (Bounds.Xmin-step/2.0))) {
-			DeleteGO(plots[0]);		plots[0] = 0L;
-			}
-		if(!plots[0])bars = (Bar**)calloc(ncl, sizeof(Bar*));
-		f_data = (int*)calloc(ncl+1, sizeof(int));
-		for(i = 0; i < nv; i++) {
-			j = (int)(floor((s_data[i] - start)/step));
-			if(j >= 0 && j < ncl) f_data[j]++;
-			else if(s_data[i] == max) f_data[j-1]++;
-			}
-		if(f_data[ncl]) ncl++;
-		curr_data->Init(ncl, 2);
-		//create data object containg the counts / bin and bars
-		for(i = 0; i< ncl; i++) {
-			curr_data->SetValue(i, 0, tmp = start + i * step + step * .5);
-			curr_data->SetValue(i, 1, (double)f_data[i]);
-			if(bars) {
-				if(bars[i] = new Bar(this, 0L, tmp, (double)f_data[i], BAR_VERTB | BAR_RELWIDTH,
-					0, i, 1, i, "Count")) bars[i]->SetSize(SIZE_BAR, 100.0);
-				}
-			}
-		free(s_data);		free(t_data);		free(f_data);
-		//create bar chart
-		if(bars && (plots[0] = new PlotScatt(this, data, ncl, bars, 0L))){
-			plots[0]->Command(CMD_BAR_FILL, &BarFill, 0L);
-			plots[0]->SetColor(COL_BAR_LINE, BarLine.color);
-			plots[0]->SetSize(SIZE_BAR_LINE, BarLine.width);
-			}
-		if(plots[0]){
-			Bounds.Xmin = dmin;		Bounds.Xmax = dmax;
-			plots[0]->Command(CMD_SET_DATAOBJ, curr_data, 0L);
-			plots[0]->Command(CMD_AUTOSCALE, 0L, 0L);
-			}
-		//create function
-		if((type & 0xff) && (fo = (char*)malloc(size_fo = 1000))) {
-			pos_fo = rlp_strcpy(fo, 1000, (char*) "[1=Function]\nx1=");
-			add_dbl_to_buff(&fo, &pos_fo, &size_fo, min, true);
-			add_to_buff(&fo, &pos_fo, &size_fo,(char*)"\nx2=", 4);
-			add_dbl_to_buff(&fo, &pos_fo, &size_fo, max, true);
-			add_to_buff(&fo, &pos_fo, &size_fo,(char*)"\nxstep=", 7);
-			add_dbl_to_buff(&fo, &pos_fo, &size_fo, lstep, true);
-			add_to_buff(&fo, &pos_fo, &size_fo,(char*)"\nLine=", 6);
-			add_dbl_to_buff(&fo, &pos_fo, &size_fo, DefSize(SIZE_DATA_LINE), true);
-			add_to_buff(&fo, &pos_fo, &size_fo,(char*)" 6 0x000000ff 0x0\n", 18);
-			cb_f = 0;
-			switch (type & 0xff){
-			case 2:				//lognormal
-#ifdef USE_WIN_SECURE
-				cb_f = sprintf_s(formula, 500, "%g*lognormfreq(x,%g,%g)",nv*step, mean, sd);
-#else
-				cb_f = sprintf(formula,"%g*lognormfreq(x,%g,%g)",nv*step, mean, sd);
-#endif
-				fdesc = (char*)"Desc=\"Lognormal Dist.\"\n";
-				break;
-			case 3:				//exponential
-#ifdef USE_WIN_SECURE
-				cb_f = sprintf_s(formula, 500, "%g*expfreq(x,%g)",nv*step, 1.0/mean);
-#else
-				cb_f = sprintf(formula,"%g*expfreq(x,%g)",nv*step, 1.0/mean);
-#endif
-				fdesc = (char*)"Desc=\"Exponential Dist.\"\n";
-				break;
-			case 4:				//rectangular
-#ifdef USE_WIN_SECURE
-				cb_f = sprintf_s(formula, 500, "%g*%g*(x>=%g&&x<=%g)",nv*step, 1.0/(dmax-dmin), dmin, dmax);
-#else
-				cb_f = sprintf(formula,"%g*%g*(x>=%g&&x<=%g)",nv*step, 1.0/(dmax-dmin), dmin, dmax);
-#endif
-				fdesc = (char*)"Desc=\"Rectangular Dist.\"\n";
-				break;
-			case 5:				//chi-square
-#ifdef USE_WIN_SECURE
-				cb_f = sprintf_s(formula, 500, "%g*chifreq(x,%g)",nv*step, mean);
-#else
-				cb_f = sprintf(formula,"%g*chifreq(x,%g)",nv*step, mean);
-#endif
-				fdesc = (char*)"Desc=\"Chi<sup>2</sup> Dist.\"\n";
-				break;
-			case 10:			//binomial
-#ifdef USE_WIN_SECURE
-				cb_f = sprintf_s(formula, 500, "%g*binomfreq(x,%g,%g)*(x>0)",nv*step, dmax, mean/dmax);
-#else
-				cb_f = sprintf(formula,"%g*binomfreq(x,%g,%g)*(x>0)",nv*step, dmax, mean/dmax);
-#endif
-				fdesc = (char*)"Desc=\"Binomial Dist.\"\n";
-				break;
-			case 11:			//poisson
-#ifdef USE_WIN_SECURE
-				cb_f = sprintf_s(formula, 500, "%g*poisfreq(x,%g)*(x>0)",nv*step, mean);
-#else
-				cb_f = sprintf(formula,"%g*poisfreq(x,%g)*(x>0)",nv*step, mean);
-#endif
-				fdesc = (char*)"Desc=\"Poisson Dist.\"\n";
-				break;
-			default:			//normal
-#ifdef USE_WIN_SECURE
-				cb_f = sprintf_s(formula, 500, "%g*normfreq(x,%g,%g)",nv*step, mean, sd);
-#else
-				cb_f = sprintf(formula,"%g*normfreq(x,%g,%g)",nv*step, mean, sd);
-#endif
-				fdesc = (char*)"Desc=\"Normal Dist.\"\n";
-				break;
-				}
-			if(cb_f) {
-				add_to_buff(&fo, &pos_fo, &size_fo, "f_xy=\"y=" , 8);
-				add_to_buff(&fo, &pos_fo, &size_fo, formula , cb_f);
-				add_to_buff(&fo, &pos_fo, &size_fo, "\\n\"\n" , 4);
-				}
-			if(fdesc)add_to_buff(&fo, &pos_fo, &size_fo, fdesc, 0);
-			OpenGraph(this, 0L, (unsigned char *)fo, false);
-			free(fo);							chi2 = df = 0.0;
-			//calculate chi-square test of fit
-			if(curr_data) for(i = 0; i< ncl; i++) {
-				if(curr_data->GetValue(i,0, &x) && curr_data->GetValue(i,1, &y)){
-#ifdef USE_WIN_SECURE
-					sprintf_s(TmpTxt, TMP_TXT_SIZE, "x=%g;%s", x, formula);
-#else
-					sprintf(TmpTxt, "x=%g;%s", x, formula);
-#endif
-					result = do_formula(curr_data, TmpTxt);
-					if(result->type == ET_VALUE && fabs(result->value) > 0.0) {
-						tmp = y-result->value;	
-						tmp = (tmp*tmp)/result->value;
-						chi2 += tmp;			df += 1.0;
-						}
-					}
-				}
-			//report result of the chi-square test
-			if(chi2 > 0.0 && parent && (fo = (char*)malloc(size_fo = 1000))) {
-				tmp = chi_dist(chi2, df-1.0, 1.0);
-				pos_fo = rlp_strcpy(fo, 1000, (char*)"chi<sup> 2</sup> =");
-				add_dbl_to_buff(&fo, &pos_fo, &size_fo, chi2, true);
-				add_to_buff(&fo, &pos_fo, &size_fo, (char*)", n =", 5);
-				add_dbl_to_buff(&fo, &pos_fo, &size_fo, df, true);
-				add_to_buff(&fo, &pos_fo, &size_fo, (char*)", df =", 6);
-				add_dbl_to_buff(&fo, &pos_fo, &size_fo, df-1.0, true);
-				add_to_buff(&fo, &pos_fo, &size_fo, (char*)", p =", 5);
-				if(tmp < 0.0001) {
-					pos_fo--;	add_to_buff(&fo, &pos_fo, &size_fo, (char*)"< 0.0001", 8);
-					}
-				else add_dbl_to_buff(&fo, &pos_fo, &size_fo, tmp, true);
-				if(!plots[2]) {
-					x = (parent->GetSize(SIZE_GRECT_RIGHT) - parent->GetSize(SIZE_GRECT_LEFT))/2.0;
-					y = parent->GetSize(SIZE_GRECT_BOTTOM) - DefSize(SIZE_TEXT)*5;
-					y -= parent->GetSize(SIZE_DRECT_TOP);
-					td.Align = TXA_VTOP | TXA_HCENTER;			td.ColBg = 0x00ffffffL;
-					td.ColTxt = 0x00ff0000L;					td.Font = FONT_HELVETICA;
-					td.fSize = DefSize(SIZE_TEXT);			td.iSize = 0;
-					td.Mode = TXM_TRANSPARENT;					td.RotBL = td.RotCHAR = 0.0;
-					td.Style = TXS_NORMAL;						td.text = 0L;
-					plots[2] = new Label(this, data, x, y, &td, 0x0L);
-					plots[2]->moveable = 1;
-					}
-				plots[2]->Command(CMD_SETTEXT, fo, 0L);			free(fo);
-				}
-			}
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Regression line and symbols
-Regression::Regression(GraphObj *par, DataObj *d):Plot(par, d)
-{
-	FileIO(INIT_VARS);
-	Id = GO_REGRESSION;
-}
-
-Regression::Regression(int src):Plot(0L, 0L)
-{
-	int i;
-
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		//now set parent in all children
-		if(rLine) rLine->parent = this;
-		if(sde) sde->parent = this;
-		if(Symbols)
-			for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->parent = this;
-		}
-}
-
-Regression::~Regression()
-{
-	Command(CMD_FLUSH, 0L, 0L);
-	if(name) free(name);		name=0L;
-	Undo.InvalidGO(this);
-}
-
-double
-Regression::GetSize(int select)
-{
-	return Plot::GetSize(select);
-}
-
-bool
-Regression::SetSize(int select, double value)
-{
-	int i;
-
-	switch(select & 0xfff){
-	case SIZE_SYMBOL:
-	case SIZE_SYM_LINE:
-		if(Symbols)	for(i = 0; i < nPoints; i++) 
-			if(Symbols[i]) Symbols[i]->SetSize(select, value);
-		return true;
-	}
-	return false;
-}
-
-bool
-Regression::SetColor(int select, DWORD col)
-{
-	int i;
-	switch(select) {
-	case COL_SYM_LINE:
-	case COL_SYM_FILL:
-		if(Symbols) for(i = 0; i < nPoints; i++) 
-			if(Symbols[i]) Symbols[i]->SetColor(select, col);
-		return true;
-	}
-	return false;
-}
-
-void
-Regression::DoPlot(anyOutput *o)
-{
-	int i;
-	
-
-	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
-	if(use_xaxis || use_yaxis) ApplyAxes(o);
-	if(sde){
-		if(rLine && sde->Command(CMD_DROP_OBJECT, rLine, o)) rLine = 0L;
-		sde->DoPlot(o);
-		}
-	if(rLine) rLine->DoPlot(o);
-	if(Symbols)	for(i = 0; i < nPoints; i++) 
-		if(Symbols[i]) Symbols[i]->DoPlot(o);
-	if(use_xaxis || use_yaxis) parent->Command(CMD_AXIS, 0L, o);
-	dirty = false;
-}
-
-bool
-Regression::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i, j;
-	static MouseEvent *mev;
-	bool bEmpty,bRedraw = false;
-	LineDEF *ld;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		if(hidden) return false;
-		mev = (MouseEvent *) tmpl;
-		switch(mev->Action) {
-		case MOUSE_LBUP:
-			if(Symbols) for (i = nPoints-1; i >= 0; i--)
-				if(Symbols[i] && Symbols[i]->Command(cmd, tmpl, o))return true;
-			if(rLine && rLine->Command(cmd, tmpl, o)) return true;
-			if(sde && sde->Command(cmd, tmpl, o)) return true;
-			break;
-			}
-		break;
-	case CMD_LEGEND:
-		if(tmpl && ((GraphObj*)tmpl)->Id == GO_LEGEND && rLine && rLine->Id == GO_REGLINE) {
-			ld = rLine->GetLine();
-			if(Symbols) {
-				for (i = 0; i < nPoints && i < 100; i++)
-					if(Symbols[i]) ((Legend*)tmpl)->HasSym(ld, Symbols[i], "Regression");
-				}
-			else ((Legend*)tmpl)->HasFill(ld, 0L, "Regression");
-			return true;
-			}
-		return false;
-	case CMD_MRK_DIRTY:
-		if(rLine || sde) Recalc();
-		dirty = true;
-	case CMD_SETSCROLL:
-	case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		return false;
-	case CMD_USEAXIS:
-		UseAxis(*((int*)tmpl));
-		return true;
-	case CMD_DROP_OBJECT:
-		if(tmpl && ((GraphObj*)tmpl)->Id == GO_REGLINE && !rLine) {
-			rLine = (RegLine *)tmpl;
-			rLine->parent = this;
-			return true;
-			}
-		break;
-	case CMD_FLUSH:
-		if(yRange) free(yRange);		if(xRange) free(xRange);
-		yRange = xRange = 0L;
-		if(rLine) DeleteGO(rLine);
-		if(sde) DeleteGO(sde);
-		rLine = 0L;		sde = 0L;
-		if(Symbols) for (i = nPoints-1; i >= 0; i--)
-			if(Symbols[i]) DeleteGO(Symbols[i]);
-		if(Symbols) free(Symbols);
-		Symbols = 0L;
-		return true;
-	case CMD_AUTOSCALE:
-		if(dirty){
-			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
-			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-			}
-		else return true;
-		dirty = false;
-	case CMD_SET_DATAOBJ:
-		if(cmd == CMD_SET_DATAOBJ) {
-			Id = GO_REGRESSION;
-			data = (DataObj *)tmpl;	
-			}
-		if(rLine) rLine->Command(cmd, tmpl, o);
-		if(Symbols) for (i = 0; i < nPoints; i++)
-			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_SCALE:
-		if(rLine) rLine->Command(cmd, tmpl, o);
-		if(sde) sde->Command(cmd, tmpl, o);
-	case CMD_SYMTEXT:		case CMD_SYM_RANGETEXT:
-	case CMD_SYMTEXTDEF:	case CMD_SYM_TYPE:
-		if(Symbols) for(i = 0; i < nPoints; i++)
-			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_SAVE_SYMBOLS:
-		return SavVarObs((GraphObj **)Symbols, nPoints, 0L);
-	case CMD_UPDATE:
-		if(Symbols) {
-			SavVarObs((GraphObj**)Symbols, nPoints, UNDO_CONTINUE);
-			for(i = 0; i < nPoints; i++)
-				if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
-			if(rLine || sde) Recalc();
-			return true;
-			}
-		return false;
-	case CMD_DELOBJ:
-		if(!parent || !o) return false;
-		dirty = bEmpty = bRedraw = false;
-		if(Symbols) for(i = 0; i < nPoints; i++)
-			if(Symbols[i] && (void*)Symbols[i] == tmpl) {
-				bRedraw = true;
-				o->HideMark();
-				Undo.DeleteGO((GraphObj**)(&Symbols[i]), 0L, o);
-				for(j = 0, bEmpty = true; j < nPoints; j++) {
-					if(Symbols[j]) {
-						bEmpty = false;
-						break;
-						}
-					}
-				if(!bEmpty && dirty) Command(CMD_AUTOSCALE, 0L, o);
-				break;
-				}
-		if(rLine && (void*)rLine == tmpl) {
-			Undo.DeleteGO((GraphObj**)(&rLine), 0L, o);
-			if(!Symbols && !sde) parent->Command(CMD_DELOBJ_CONT, this, o);
-			else bRedraw = true;
-			}
-		if(sde && (void*)sde == tmpl) {
-			sde->Command(CMD_RMU, 0L, 0L);
-			Undo.DeleteGO((GraphObj**)(&sde), 0L, o);
-			if(!Symbols && !rLine) parent->Command(CMD_DELOBJ_CONT, this, o);
-			else bRedraw = true;
-			}
-		if(bEmpty && Symbols) {
-			Undo.DropMemory(this, (void**)(&Symbols), UNDO_CONTINUE);
-			bRedraw = false;
-			if(!rLine && !sde) parent->Command(CMD_DELOBJ_CONT, this, o);
-			else bRedraw = true;
-			}
-		if(bRedraw)parent->Command(CMD_REDRAW, 0L, o);
-		return bRedraw;
-		}
-	return false;
-}
-
-void
-Regression::Recalc()
-{
-	int i, j;
-	long n;
-	bool dValid;
-	lfPOINT *val;
-
-	if(nPoints <2 || !Symbols || 
-		!(val = (lfPOINT*)calloc(nPoints, sizeof(lfPOINT)))) return;
-	for(i = 0, n = 0; i < nPoints; i++){
-		if(Symbols[i] && Symbols[i]->Id == GO_SYMBOL) {
-			val[j = (int)n].fx = Symbols[i]->GetSize(SIZE_XPOS);
-			val[j].fy = Symbols[i]->GetSize(SIZE_YPOS);
-			dValid = true;
-			switch(type & 0x700) {
-			case 0x100:					//logarithmic x
-				if(dValid = val[j].fx > defs.min4log) val[j].fx = log10(val[j].fx);
-				break;
-			case 0x200:					//reciprocal x
-				if(dValid = fabs(val[j].fx) >defs.min4log) val[j].fx = 1.0/val[j].fx;
-				break;
-			case 0x300:					//square root x
-				if(dValid = fabs(val[j].fx) >defs.min4log) val[j].fx = sqrt(val[j].fx);
-				break;
-				}
-			if(dValid) switch(type & 0x7000) {
-			case 0x1000:				//logarithmic y
-				if(dValid = val[j].fy > defs.min4log) val[j].fy = log10(val[j].fy);
-				break;
-			case 0x2000:				//reciprocal y
-				if(dValid = fabs(val[j].fy) >defs.min4log) val[j].fy = 1.0/val[j].fy;
-				break;
-			case 0x3000:				//square root y
-				if(dValid = fabs(val[j].fy) >defs.min4log) val[j].fy = sqrt(val[j].fy);
-				break;
-				}
-			if(dValid) n++;
-			}
-		}
-	if(sde && sde->Id == GO_SDELLIPSE) sde->Recalc(val, n);
-	if(rLine && rLine->Id == GO_REGLINE) rLine->Recalc(val, n);
-	free(val);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// BubblePlot is a Plot-Class
-BubblePlot::BubblePlot(GraphObj *par, DataObj *d):Plot(par, d)
-{
-	FileIO(INIT_VARS);
-	Id = GO_BUBBLEPLOT;
-	if (!d && parent) parent->Command(CMD_DELOBJ, this, NULL);
-}
-
-BubblePlot::BubblePlot(int src):Plot(0L, 0L)
-{
-	long i;
-
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		BubbleFill.hatch = &BubbleFillLine;
-		if(Bubbles)for(i = 0; i< nPoints; i++) {
-			if(Bubbles[i])Bubbles[i]->parent = this;
-			}
-		}
-}
-
-BubblePlot::~BubblePlot()
-{
-	int i;
-
-	if(Bubbles) {
-		for(i = 0; i < nPoints; i++) if(Bubbles[i]) DeleteGO(Bubbles[i]);
-		free (Bubbles);
-		}
-	if(name) free(name);		name=0L;
-	Undo.InvalidGO(this);
-}
-
-DWORD
-BubblePlot::GetColor(int select)
-{
-	switch(select) {
-	case COL_BUBBLE_FILL:			return BubbleFill.color;
-	case COL_BUBBLE_LINE:			return BubbleLine.color;
-	case COL_BUBBLE_FILLLINE:	   	return BubbleFillLine.color;
-	default:
-		return Plot::GetColor(select);
-	}
-}
-
-void
-BubblePlot::DoPlot(anyOutput *o)
-{
-	int i;
-
-	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
-	if(use_xaxis || use_yaxis) {
-		ApplyAxes(o);
-		if(Bubbles) for(i = 0; i < nPoints; i++) 
-			if(Bubbles[i]) Bubbles[i]->DoPlot(o);
-		parent->Command(CMD_AXIS, 0L, o);
-		}
-	else {
-		if(Bubbles) for(i = 0; i < nPoints; i++) 
-			if(Bubbles[i]) Bubbles[i]->DoPlot(o);
-		}
-	dirty = false;
-}
-
-bool
-BubblePlot::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-	static MouseEvent *mev;
-	GraphObj **tmpPlots;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		if(hidden) return false;
-		mev = (MouseEvent *) tmpl;
-		switch(mev->Action) {
-		case MOUSE_LBUP:
-			//select objects invers to plot order
-			if(Bubbles && !CurrGO) for(i = nPoints-1; i >=0; i--)
-				if(Bubbles[i]) if(Bubbles[i]->Command(cmd, tmpl, o))break;
-			break;
-			}
-		break;
-	case CMD_REPL_GO:
-		if((tmpPlots = (GraphObj **)tmpl) && tmpPlots[0] && tmpPlots[1] && Bubbles) {
-			for(i = 0; i < nPoints; i++) if(Bubbles[i] && Bubbles[i] == tmpPlots[0]) { 
-				return ReplaceGO((GraphObj**)&Bubbles[i], tmpPlots);
-				}
-			}
-		return false;
-	case CMD_LEGEND:
-		if(((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
-		if(Bubbles) for (i = 0; i < nPoints; i++)
-			if(Bubbles[i]) Bubbles[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_SCALE:
-		if(!tmpl) return false;
-		BubbleLine.width *= ((scaleINFO*)tmpl)->sy.fy;
-		BubbleLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		BubbleFillLine.width *= ((scaleINFO*)tmpl)->sy.fy;
-		BubbleFillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		BubbleFill.scale *= ((scaleINFO*)tmpl)->sy.fy;
-		if(Bubbles) for(i = 0; i < nPoints; i++)
-			if(Bubbles[i]) Bubbles[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_MRK_DIRTY:
-		dirty = true;
-	case CMD_SETSCROLL:		case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		return false;
-	case CMD_USEAXIS:
-		UseAxis(*((int*)tmpl));
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = GO_BUBBLEPLOT;
-		data = (DataObj *)tmpl;
-	case CMD_UPDATE:
-		if(cmd == CMD_UPDATE && Bubbles) SavVarObs((GraphObj **)Bubbles, nPoints, UNDO_CONTINUE);
-	case CMD_AUTOSCALE:
-		if(cmd == CMD_AUTOSCALE && Bubbles) {
-			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
-			}
-	case CMD_BUBBLE_ATTRIB:		case CMD_BUBBLE_TYPE:		case CMD_BUBBLE_FILL:
-	case CMD_BUBBLE_LINE:
-		if(Bubbles) for(i = 0; i < nPoints; i++)
-			if(Bubbles[i]) Bubbles[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_DELOBJ:
-		if(Bubbles && parent) for(i = 0; i < nPoints; i++) {
-			o->HideMark();
-			if(Bubbles[i] && tmpl == (void *)Bubbles[i]) {
-				Undo.DeleteGO((GraphObj**)(&Bubbles[i]), 0L, o);
-				parent->Command(CMD_REDRAW, 0L, o);
-				return true;
-				}
-			}
-		break;
-	case CMD_SAVE_SYMBOLS:
-		return SavVarObs((GraphObj **)Bubbles, nPoints, 0L);
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// PolarPlot is a Plot-Class
-PolarPlot::PolarPlot(GraphObj *par, DataObj *d):Plot(par, d)
-{
-	FileIO(INIT_VARS);
-	Id = GO_POLARPLOT;
-	if (!d && parent) parent->Command(CMD_DELOBJ, this, NULL);
-}
-
-PolarPlot::PolarPlot(int src):Plot(0L, 0L)
-{
-	int i;
-
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		Fill.hatch = &FillLine;
-		//now set parent in all children
-		if(Plots) 
-			for(i = 0; i < nPlots; i++) if(Plots[i]) Plots[i]->parent = this;
-		if(Axes) 
-			for(i = 0; i < nAxes; i++) if(Axes[i]) Axes[i]->parent = this;
-		}
-}
-
-PolarPlot::~PolarPlot()
-{
-	int i;
-
-	if(Plots){
-		for(i = 0; i < nPlots; i++) if(Plots[i]) DeleteGO(Plots[i]);
-		free(Plots);		Plots = 0L;
-		}
-	if(Axes){
-		for(i = 0; i < nAxes; i++) if(Axes[i]) DeleteGO(Axes[i]);
-		free(Axes);			Axes = 0L;
-		}
-	if(name) free(name);	name=0L;
-	Undo.InvalidGO(this);
-}
-
-double
-PolarPlot::GetSize(int select)
-{
-	switch(select) {
-	case SIZE_BOUNDS_XMIN:		return Bounds.Xmin;
-	case SIZE_BOUNDS_XMAX:		return Bounds.Xmax;
-	case SIZE_BOUNDS_YMIN:		return Bounds.Ymin;
-	case SIZE_BOUNDS_YMAX:		return Bounds.Ymax;
-	case SIZE_BOUNDS_LEFT:		return (Axes && Axes[0])?(((Axis*)Axes[0])->GetAxis())->min:0.0;
-	case SIZE_BOUNDS_RIGHT:		return (Axes && Axes[0])?(((Axis*)Axes[0])->GetAxis())->max:0.0;
-	case SIZE_BOUNDS_TOP:		return (Axes && Axes[1])?(((Axis*)Axes[1])->GetAxis())->max:0.0;
-	case SIZE_BOUNDS_BOTTOM:	return (Axes && Axes[1])?(((Axis*)Axes[1])->GetAxis())->min:0.0;
-	case SIZE_YAXISX:
-		if(!CurrDisp) return 0.0;
-		if((((Axis*)Axes[1])->GetAxis())->flags & AXIS_X_DATA) 
-			return CurrDisp->fx2fix((((Axis*)Axes[1])->GetAxis())->loc[0].fx);
-		else return CurrDisp->co2fix((((Axis*)Axes[1])->GetAxis())->loc[0].fx);
-	case SIZE_XAXISY:
-		if(!CurrDisp) return 0.0;
-		if((((Axis*)Axes[0])->GetAxis())->flags & AXIS_Y_DATA) 
-			return CurrDisp->fy2fiy((((Axis*)Axes[0])->GetAxis())->loc[0].fy);
-		else return CurrDisp->co2fiy((((Axis*)Axes[0])->GetAxis())->loc[0].fy);
-	case SIZE_XCENTER:			return (((Axis*)Axes[0])->GetAxis())->Center.fx;
-	case SIZE_YCENTER:			return (((Axis*)Axes[0])->GetAxis())->Center.fy;
-	default:
-		if(parent) return parent->GetSize(select);
-		}
-	return DefSize(select);
-}
-
-void
-PolarPlot::DoPlot(anyOutput *o)
-{
-	int i;
-
-	if(o) CurrDisp = o;
-	else return;
-	if(!parent) return;
-	CurrRect.Xmin = CurrRect.Xmax = (((Axis*)Axes[1])->GetAxis())->Center.fx + 
-		parent->GetSize(SIZE_GRECT_LEFT);
-	CurrRect.Xmin -= (((Axis*)Axes[0])->GetAxis())->Radius;	
-	CurrRect.Xmax += (((Axis*)Axes[0])->GetAxis())->Radius;
-	CurrRect.Ymin = CurrRect.Ymax = (((Axis*)Axes[0])->GetAxis())->Center.fy +
-		parent->GetSize(SIZE_GRECT_TOP);
-	CurrRect.Ymin -= (((Axis*)Axes[0])->GetAxis())->Radius;
-	CurrRect.Ymax += (((Axis*)Axes[0])->GetAxis())->Radius;
-	(((Axis*)Axes[0])->GetAxis())->Start = ((((Axis*)Axes[0])->GetAxis())->flags & AXIS_INVERT) ? -offs : offs;
-	o->SetRect(CurrRect, defs.cUnits, ((Axis*)Axes[0])->GetAxis(), ((Axis*)Axes[1])->GetAxis());
-	o->SetFill(&Fill);
-	if(Axes) for(i = 0; i < nAxes; i++) {
-		if(i == 1) {
-			if(!(type & 0x01) && Axes[i]) Axes[i]->DoPlot(o);
-			}
-		else if(Axes[i]) Axes[i]->DoPlot(o);
-		}
-	if(Plots) for(i = 0; i < nPlots; i++) if(Plots[i]) {
-		if(Plots[i]->Id >= GO_PLOT && Plots[i]->Id < GO_GRAPH) {
-			if(((Plot*)Plots[i])->hidden == 0) Plots[i]->DoPlot(o);
-			}
-		else Plots[i]->DoPlot(o);
-		}
-	rDims.left = o->co2ix(CurrRect.Xmin);	rDims.right = o->co2ix(CurrRect.Xmax);
-	rDims.top = o->co2iy(CurrRect.Ymin);	rDims.bottom = o->co2iy(CurrRect.Ymax);
-	if(parent) parent->Command(CMD_AXIS, 0L, o);
-}
-
-bool
-PolarPlot::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	GraphObj **tmpPlots;
-	int i;
-	AxisDEF *ad0, *ad1;
-	double tmp;
-
-	switch (cmd) {
-	case CMD_CONFIG:
-		Config();
-		return true;
-	case CMD_SCALE:
-		FillLine.width *= ((scaleINFO*)tmpl)->sy.fy;
-		FillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;
-		if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->Command(cmd, tmpl, o);
-		if(Plots) for(i = 0; i < nPlots; i++) if(Plots[i]) Plots[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = GO_POLARPLOT;
-		data = (DataObj *)tmpl;	
-	case CMD_UPDATE:
-		if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->Command(cmd, tmpl, o);
-	case CMD_LEGEND:
-		if(Plots) for(i = 0; i < nPlots; i++) if(Plots[i]) Plots[i]->Command(cmd, tmpl, o);
-		return false;
-	case CMD_OBJTREE:
-		if(!tmpl) return false;
-		if(Plots) for(i = 0; i < nPlots; i++) if(Plots[i]) 
-			((ObjTree*)tmpl)->Command(CMD_UPDATE, Plots[i], 0L);
-		return true;
-	case CMD_MOUSE_EVENT:
-		if(hidden) return false;
-		if(o) switch(((MouseEvent*)tmpl)->Action) {
-		case MOUSE_LBUP:
-			if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i])
-				if(Axes[i]->Command(cmd, tmpl, o))return true;
-			if(Plots) for(i = 0; i < nPlots; i++) if(Plots[i]) 
-				Plots[i]->Command(cmd, tmpl, o);
-			if(!CurrGO && IsInRect(&rDims, ((MouseEvent*)tmpl)->x, ((MouseEvent*)tmpl)->y)){
-				CurrGO = this;
-				o->ShowMark(&rDims, MRK_INVERT);
-				return true;
-				}
-			break;
-			}
-		return false;
-	case CMD_REPL_GO:
-		if(!(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
-		if(Axes) for(i = 0; i < nAxes; i++) if(Axes[i] && Axes[i] == tmpPlots[0]){
-			return ReplaceGO(&Axes[i], tmpPlots);
-			}
-		break;
-	case CMD_SETSCROLL:		case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		return false;
-	case CMD_DELOBJ_CONT:	case CMD_DELOBJ:
-		if(Plots && nPlots) for(i = 0; i < nPlots; i++) if(tmpl == (void*)Plots[i]) {
-			Undo.DeleteGO((GraphObj**)(&Plots[i]), cmd == CMD_DELOBJ_CONT ? UNDO_CONTINUE : 0L, o);
-			if(parent)parent->Command(CMD_REDRAW, NULL, o);
-			return true;
-			}
-		if(Axes && nAxes > 1 && (tmpl == (void*)Axes[0] || tmpl == (void*)Axes[1]))
-			InfoBox("Axes required for scaling\ncannot be deleted.");
-		break;
-	case CMD_AXIS:		//axis changed: reconstruct corresponding axis
-		if(Axes && nAxes >1 && tmpl && Axes[0] && Axes[1]) {
-			ad0 = ((Axis*)Axes[0])->GetAxis();		ad1 = ((Axis*)Axes[1])->GetAxis();
-			if(tmpl == Axes[0]) {
-				CheckNewFloat(&ad1->loc[1].fy, ad1->loc[1].fy, ad0->Center.fy, this, UNDO_CONTINUE);
-				tmp = ad1->loc[1].fy - ad0->Radius;
-				CheckNewFloat(&ad1->loc[0].fy, ad1->loc[0].fy, tmp, this, UNDO_CONTINUE);
-				}
-			if(tmpl == Axes[1]) {
-				CheckNewFloat(&ad0->Center.fy, ad0->Center.fy, ad1->loc[1].fy, this, UNDO_CONTINUE);
-				tmp = fabs(ad1->loc[1].fy - ad1->loc[0].fy);
-				CheckNewFloat(&ad0->Radius, ad0->Radius, tmp, this, UNDO_CONTINUE);
-				}
-			}
-		break;	
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// BoxPlot is a Plot-Class
-BoxPlot::BoxPlot(GraphObj *par, DataObj *d):Plot(par, d)
-{
-	FileIO(INIT_VARS);
-	Id = GO_BOXPLOT;
-}
-
-BoxPlot::BoxPlot(int src):Plot(0L, 0L)
-{
-	int i;
-
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		//now set parent in all children
-		if(Boxes) 
-			for(i = 0; i < nPoints; i++) if(Boxes[i]) Boxes[i]->parent = this;
-		if(Whiskers) 
-			for(i = 0; i < nPoints; i++) if(Whiskers[i]) Whiskers[i]->parent = this;
-		if(Symbols) 
-			for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->parent = this;
-		if(Labels) 
-			for(i = 0; i < nPoints; i++) if(Labels[i]) Labels[i]->parent = this;
-		if(TheLine) TheLine->parent = this;
-		}
-}
-
-BoxPlot::BoxPlot(GraphObj *par, DataObj *dt, int mode, int c1, int c2, int c3, char *box_name):Plot(par, dt)
-{
-	int i, nr, cb;
-	lfPOINT fp;
-
-	FileIO(INIT_VARS);		Id = GO_BOXPLOT;	fp.fx = fp.fy = 0.0;	cb = 0;
-	if(data && data->GetSize(&i, &nr)) {
-		nPoints = nr;		if(box_name) cb = (int)strlen(box_name);
-		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
-		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-		if(Boxes = (Box**)calloc(nr, sizeof(Box*))) for(i = 0; i < nr; i++) {
-			if(mode == 1) Boxes[i] = new Box(this, data, fp, fp, BAR_RELWIDTH, c1, i, c2, i, c1, i, c3, i);
-			else Boxes[i] = new Box(this, data, fp, fp, BAR_RELWIDTH, c2, i, c1, i, c3, i, c1, i);
-			if(box_name && box_name[0] && Boxes[i]) Boxes[i]->name = (char*)memdup(box_name, cb+1, 0);
-			}
-		}
-}
-
-BoxPlot::~BoxPlot()
-{
-	int i;
-
-	if(Whiskers) {
-		for(i = 0; i < nPoints; i++) if(Whiskers[i]) DeleteGO(Whiskers[i]);
-		free (Whiskers);
-		}
-	if(Boxes) {
-		for(i = 0; i < nPoints; i++) if(Boxes[i]) DeleteGO(Boxes[i]);
-		free (Boxes);
-		}
-	if(Symbols) {
-		for(i = 0; i < nPoints; i++) if(Symbols[i]) DeleteGO(Symbols[i]);
-		free (Symbols);
-		}
-	if(Labels) {
-		for(i = 0; i < nPoints; i++) if(Labels[i]) DeleteGO(Labels[i]);
-		free (Labels);
-		}
-	if(TheLine) DeleteGO(TheLine);
-	if(curr_data) delete curr_data;		curr_data = 0L;
-	if(xRange) free(xRange);			xRange = 0L;
-	if(yRange) free(yRange);			yRange = 0L;
-	if(x_info) free(x_info);			x_info = 0L;
-	if(y_info) free(y_info);			y_info = 0L;
-	if(case_prefix) free(case_prefix);	case_prefix = 0L;
-	if(name) free(name);				name = 0L;
-	if(x_tv) delete(x_tv);				x_tv = 0;
-	if(y_tv) delete(y_tv);				y_tv = 0;
-	Undo.InvalidGO(this);
-}
-
-double
-BoxPlot::GetSize(int select)
-{
-	int i;
-	double ft1, ft2, d;
-
-	switch(select){
-	case SIZE_BOXMINX:
-		if(BoxDist.fx >= 0.0001) return BoxDist.fx;
-		if((!Boxes) || (nPoints < 2)) return BoxDist.fx = 1.0;
-		ft1 = -HUGE_VAL;	ft2 = HUGE_VAL;		BoxDist.fx= HUGE_VAL;
-		for(i = 0; i < nPoints; i++) {
-			if(Boxes[i]) {
-				ft2 = Boxes[i]->GetSize(SIZE_XPOS);
-				d = fabs(ft2-ft1);
-				if(d != 0.0 && d < BoxDist.fx) BoxDist.fx = d;
-				}
-			ft1 = ft2;
-			}
-		return BoxDist.fx = BoxDist.fx > 0.0001 && BoxDist.fx != HUGE_VAL  ? BoxDist.fx : 1.0;
-	case SIZE_BOXMINY:
-		if(BoxDist.fy >= 0.0001) return BoxDist.fy;
-		if((!Boxes) || (nPoints < 2)) return BoxDist.fy = 1.0;
-		ft1 = -HUGE_VAL;	ft2 = HUGE_VAL;		BoxDist.fy= HUGE_VAL;
-		for(i = 0; i < nPoints; i++) {
-			if(Boxes[i]) {
-				ft2 = Boxes[i]->GetSize(SIZE_YPOS);
-				d = fabs(ft2-ft1);
-				if(d != 0.0 && d < BoxDist.fy) BoxDist.fy = d;
-				}
-			ft1 = ft2;
-			}
-		return BoxDist.fy = BoxDist.fy > 0.0001 && BoxDist.fy != HUGE_VAL  ? BoxDist.fy : 1.0;
-	default:
-		return Plot::GetSize(select);
-		}
-}
-
-bool
-BoxPlot::SetSize(int select, double value)
-{
-	int i;
-
-	switch(select & 0xfff){
-	case SIZE_SYMBOL:		case SIZE_SYM_LINE:
-		if(Symbols) for(i = 0; i < nPoints; i++) 
-			if(Symbols[i]) Symbols[i]->SetSize(select, value);
-		return true;
-	case SIZE_WHISKER:		case SIZE_WHISKER_LINE:
-		if(Whiskers) for(i = 0; i < nPoints; i++) 
-			if(Whiskers[i]) Whiskers[i]->SetSize(select, value);
-		return true;
-	case SIZE_BOX:			case SIZE_BOX_LINE:
-		if(Boxes) for(i = 0; i < nPoints; i++) 
-			if(Boxes[i]) Boxes[i]->SetSize(select, value);
-		return true;
-	case SIZE_LB_XDIST:		case SIZE_LB_YDIST:
-		if(Labels) for(i = 0; i < nPoints; i++)
-			if(Labels[i]) Labels[i]->SetSize(select, value);
-		return true;
-	}
-	return false;
-}
-
-bool
-BoxPlot::SetColor(int select, DWORD col)
-{
-	int i;
-
-	switch(select) {
-	case COL_SYM_LINE:		case COL_SYM_FILL:
-		if(Symbols)	for(i = 0; i < nPoints; i++) 
-			if(Symbols[i]) Symbols[i]->SetColor(select, col);
-		return true;
-	case COL_WHISKER:
-		if(Whiskers) for(i = 0; i < nPoints; i++)
-			if(Whiskers[i]) Whiskers[i]->SetColor(select, col);
-		return true;
-	case COL_BOX_LINE:
-		if(Boxes) for(i = 0; i < nPoints; i++)
-			if(Boxes[i]) Boxes[i]->SetColor(select, col);
-		return true;
-	default:
-		return false;
-		}
-}
-
-void
-BoxPlot::DoPlot(anyOutput *o)
-{
-	if(!parent || !o) return;
-	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
-	if(use_xaxis || use_yaxis) ApplyAxes(o);
-	ForEach(FE_PLOT, 0L, o);
-	dirty = false;
-	if(use_xaxis || use_yaxis)parent->Command(CMD_AXIS, 0L, o);
-}
-
-bool
-BoxPlot::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		if(hidden) return false;
-		if(!CurrGO && ((MouseEvent*)tmpl)->Action == MOUSE_LBUP) return ForEach(cmd, tmpl, o);
-		return false;
-	case CMD_TEXTTHERE:
-		if(Labels) for(i = 0; i < nPoints; i++)	if(Labels[i] &&  Labels[i]->Command(cmd, tmpl, o))	return true;
-		return false;
-	case CMD_LEGEND:
-		if(((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
-		if(Symbols) {
-			if(TheLine && TheLine->Id == GO_DATALINE) {
-				for (i = 0; i < nPoints && i < 100; i++)
-					if(Symbols[i]) ((Legend*)tmpl)->HasSym(&TheLine->LineDef, Symbols[i], 0L);
-				}
-			else {
-				for (i = 0; i < nPoints && i < 100; i++)
-					if(Symbols[i]) ((Legend*)tmpl)->HasSym(0L, Symbols[i], 0L);
-				}
-			if(TheLine && TheLine->Id == GO_DATAPOLYGON) TheLine->Command(cmd, tmpl, o);
-			}
-		else if(TheLine) TheLine->Command(cmd, tmpl, o);
-		if(Boxes) for (i = 0; i < nPoints; i++)	if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
-		if(Whiskers) for (i = 0; i < nPoints; i++)	if(Whiskers[i]) Whiskers[i]->Command(cmd, tmpl, o);
-		break;
-	case CMD_SET_DATAOBJ:
-		Id = GO_BOXPLOT;		data = (DataObj *)tmpl;		dirty = true;
-		if(type && xRange && yRange && data) {		//Stat. - Plot ?
-			CreateData();
-			return ForEach(CMD_SET_DATAOBJ, curr_data, o);
-			}
-	case CMD_SCALE:
-		return ForEach(cmd, tmpl, o);
-	case CMD_AUTOSCALE:
-		if(hidden) return false;
-		if(dirty) {
-			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
-			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-			}
-		else{
-			if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH && 
-				Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin) {
-				((Plot*)parent)->CheckBounds(Bounds.Xmin, Bounds.Ymin);
-				((Plot*)parent)->CheckBounds(Bounds.Xmax, Bounds.Ymax);
-				return true;
-				}
-			}
-		dirty = false;
-		ForEach(cmd, tmpl, o);
-		if(x_tv) {
-			Bounds.Xmin = 0.5;		Bounds.Xmax = ((double)x_tv->Count())+0.5;
-			}
-		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH
-			&& Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin){
-			((Plot*)parent)->CheckBounds(Bounds.Xmin, Bounds.Ymin);
-			((Plot*)parent)->CheckBounds(Bounds.Xmax, Bounds.Ymax);
-			}
-		return true;
-	case CMD_UPDATE:
-		if(parent) {
-			if(Boxes) SavVarObs((GraphObj **)Boxes, nPoints, UNDO_CONTINUE);
-			if(Whiskers) SavVarObs((GraphObj **)Whiskers, nPoints, UNDO_CONTINUE);
-			if(Symbols) SavVarObs((GraphObj **)Symbols, nPoints, UNDO_CONTINUE);
-			if(Labels) SavVarObs((GraphObj **)Labels, nPoints, UNDO_CONTINUE); 
-			}
-		if(type && xRange && yRange) {		//Stat. - Plot ?
-			CreateData();
-			ForEach(CMD_SET_DATAOBJ, curr_data, o);
-			}
-		ForEach(cmd, tmpl, o);
-		return dirty = true;
-	case CMD_USEAXIS:
-		UseAxis(*((int*)tmpl));
-		return true;
-	case CMD_MRK_DIRTY:
-		dirty = true;
-	case CMD_SETSCROLL:		case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		return false;
-	case CMD_SETTEXTDEF:
-		if(Labels) for(i = 0; i < nPoints; i++)
-			if(Labels[i]) Labels[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_DELOBJ:
-		if(ForEach(FE_DELOBJ, tmpl, o)) {
-			parent->Command(CMD_REDRAW, 0L, o);
-			return true;
-			}
-		return false;
-	case CMD_SYMTEXT:		case CMD_SYM_RANGETEXT:
-	case CMD_SYMTEXTDEF:	case CMD_SYM_TYPE:
-		if(Symbols) for(i = 0; i < nPoints; i++)
-			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_SAVE_SYMBOLS:
-		return SavVarObs((GraphObj **)Symbols, nPoints, 0L);
-	case CMD_SAVE_BARS:
-		return SavVarObs((GraphObj **)Boxes, nPoints, 0L);
-	case CMD_SAVE_BARS_CONT:
-		return SavVarObs((GraphObj **)Boxes, nPoints, UNDO_CONTINUE);
-	case CMD_SAVE_ERRS:
-		return SavVarObs((GraphObj **)Whiskers, nPoints, 0L);
-	case CMD_SAVE_LABELS:
-		return SavVarObs((GraphObj **)Labels, nPoints, 0L);
-	case CMD_BOX_TYPE:
-		BoxDist.fy = BoxDist.fx = 0.0;
-	case CMD_BOX_FILL:
-		if(Boxes) for (i = 0; i < nPoints; i++)
-			if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_WHISKER_STYLE:		case CMD_ERRDESC:
-		if(Whiskers) for (i = 0; i < nPoints; i++)
-			if(Whiskers[i]) Whiskers[i]->Command(cmd, tmpl, o);
-		return true;
-		}
-	return false;
-}
-
-bool
-BoxPlot::ForEach(int cmd, void *tmpl, anyOutput *o)
-{
-	GraphObj ***pobs[] = {(GraphObj***)&Boxes, (GraphObj***)&Whiskers, 
-		(GraphObj***)&Symbols, (GraphObj***)&Labels};
-	GraphObj **p;
-	int i, j;
-	bool bRet;
-
-	switch(cmd) {
-	case FE_DELOBJ:
-		if(!o || !parent || !tmpl) return false;
-		for(i = 0; i < 4; i++) {
-			if(DeleteGOL(pobs[i], nPoints, (GraphObj*) tmpl, o)) return true;
-			}
-		if(TheLine && tmpl == (void *) TheLine) {
-			Undo.DeleteGO((GraphObj**)(&TheLine), 0L, o);
-			return true;
-			}
-		break;
-	case FE_PLOT:
-		if(TheLine) TheLine->DoPlot(o);
-		for(i = 0; i < 4; i++){
-			if(p= *pobs[i]) for(j = 0; j < nPoints; j++) {
-				if(p[j]) p[j]->DoPlot(o);
-				}
-			}
-		return true;
-	case CMD_MOUSE_EVENT:				//invers to plot order
-		for(i = 3; i >= 0; i--){
-			if(p= *pobs[i]) for(j = nPoints-1; j >= 0; j--) {
-				if(p[j]) {
-					bRet = p[j]->Command(cmd, tmpl, o);
-					if(bRet && cmd == CMD_MOUSE_EVENT) return true;
-					}
-				}
-			}
-		if(TheLine) return TheLine->Command(cmd, tmpl, o);
-		return false;
-	default:							//pass command to all objects
-		for(i = 0; i < 4; i++){
-			if(p= *pobs[i]) for(j = 0; j < nPoints; j++) {
-				if(p[j]) {
-					bRet = p[j]->Command(cmd, tmpl, o);
-					}
-				}
-			}
-		if(TheLine) return TheLine->Command(cmd, tmpl, o);
-		return false;
-		}
-	return false;
-}
-
-void
-BoxPlot::CreateData()
-{
-	int i, j, k, l, m, n, *ny, c_num, c_txt, c_dattim;
-	double y, ss, d, lo, hi, **ay, *ax, *tay, *q1, *q2, *q3;
-	lfPOINT *xy;
-	AccRange *rX, *rY;
-	anyResult x_res, y_res;
-
-	if(curr_data) delete curr_data;			curr_data = 0L;
-	if(!data || !xRange || !yRange || !xRange[0] || !yRange[0]) return;
-	if(!(rX = new AccRange(xRange)) || !(rY = new AccRange(yRange))) return;
-	m = rX->CountItems();	n = 0;
-	if(m < 2 || !(xy = (lfPOINT*) malloc(m * sizeof(lfPOINT)))) {
-		delete rX;	delete rY;
-		return;
-		}
-	if(x_tv) delete x_tv;					x_tv = 0L;
-	ny = (int*) calloc(m, sizeof(int));
-	ay = (double**) calloc(m, sizeof(double*));
-	ax = (double*) calloc(m, sizeof(double));
-	tay = (double*)malloc(m * sizeof(double));
-	if(!ny || !ay || !ax || !tay) {
-		if(ny) free(ny);	if(ay) free(ay);
-		if(ax) free(ax);	if(tay) free(tay);
-		delete rX;	delete rY;
-		return;
-		}
-	rX->DataTypes(data, &c_num, &c_txt, &c_dattim);
-	if(c_num < 5 && (c_txt + c_dattim) > 5) {
-		x_tv = new TextValue();	
-		}
-	rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
-	rX->GetNext(&i, &j);	rY->GetNext(&k, &l);	n=0;
-	do {
-		if(data->GetResult(&x_res, j, i, false) && data->GetResult(&y_res, l, k, false) && y_res.type == ET_VALUE) {
-			xy[n].fy = y_res.value;
-			if(x_tv){ 
-				switch(x_res.type) {
-				case ET_TEXT:
-					xy[n++].fx = x_tv->GetValue(x_res.text);
-					break;
-				case ET_VALUE:	case ET_BOOL:	case ET_DATE:	case ET_TIME:	case ET_DATETIME:
-					TranslateResult(&x_res);
-					xy[n++].fx = x_tv->GetValue(x_res.text);
-					break;
-					}
-				}
-			else if(x_res.type == ET_VALUE) xy[n++].fx = x_res.value;
-			}
-		}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
-	delete rX;			delete rY;
-	if(!n) {
-		if(ny) free(ny);	if(ay) free(ay);
-		if(ax) free(ax);	if(tay) free(tay);
-		return;
-		}
-	SortFpArray(n, xy);
-	for(i = j = 0; i < (n-1); i++, j++) {
-		ax[j] = xy[i].fx;		tay[0] = xy[i].fy;
-		ny[j] = 1;
-		for(k = 1; xy[i+1].fx == xy[i].fx; k++) {
-			tay[k] = xy[i+1].fy;
-			i++;		ny[j]++;
-			}
-		ay[j] = (double*)memdup(tay, k * sizeof(double), 0);
-		}
-	if(xy[i].fx > xy[i-1].fx) {
-		ax[j] = xy[i].fx;		tay[0] = xy[i].fy;
-		ny[j] = 1;
-		ay[j++] = (double*)memdup(tay, sizeof(double), 0);
-		}
-	if((type & 0x0004) == 0x0004 || (type & 0x0030) == 0x0030 || (type & 0x0300) == 0x0300) {
-		//medians and/or percentiles required
-		q1 = (double *)malloc(j * sizeof(double));
-		q2 = (double *)malloc(j * sizeof(double));
-		q3 = (double *)malloc(j * sizeof(double));
-		if(q1 && q2 && q3) {
-			for(i = 0; i < j; i++) {
-				if(ny[i] > 1) d_quartile(ny[i], ay[i], q1+i, q2+i, q3+i);
-				else q1[i] = q2[i] = q3[i] = *ay[i];
-				}
-			}
-		else type = 0;
-		}
-	else q1 = q2 = q3 = 0L;
-	if(type && (curr_data = new DataObj()) && curr_data->Init(j, 8)) {
-		for(i = 0; i < j; i++) curr_data->SetValue(i,0,ax[i]);	// set x-values
-		for(i = 0; i < j; i++) {								// set means
-			if(ny[i] > 1) switch(type & 0x000f) {
-				case 0x0001:	default:
-					curr_data->SetValue(i, 1, y=d_amean(ny[i], ay[i]));
-					break;
-				case 0x0002:
-					curr_data->SetValue(i, 1, y=d_gmean(ny[i], ay[i]));
-					break;
-				case 0x0003:
-					curr_data->SetValue(i, 1, y=d_hmean(ny[i], ay[i]));
-					break;
-				case 0x0004:
-					curr_data->SetValue(i, 1, y=q2[i]);
-					break;
-				}
-			else curr_data->SetValue(i, 1, y= *ay[i]);
-			curr_data->SetValue(i, 6, y);						//label's y
-			}
-		if((type & 0x00f0) == 0x0010 || (type & 0x00f0) == 0x0020 || (type & 0x00f0) == 0x0050
-			|| (type & 0x0f0f) == 0x0201 || (type & 0x0f0f) == 0x0501) for(i = 0; i < j; i++) {
-			// set SD, SE, Conf. Intervall
-			if(ny[i] > 1) {
-				ss = sqrt(d_variance(ny[i], ay[i], &y));
-				}
-			else {
-				y = *ay[i];		ss = 0.0;
-				}
-			//Box info is in cols 2 & 3
-			if((type & 0x00f0) == 0x0010) {
-				curr_data->SetValue(i, 2, y - ss);	curr_data->SetValue(i, 3, y + ss);
-				}
-			else if((type & 0x00f0) == 0x0020) {
-				curr_data->SetValue(i, 2, y - ss/sqrt((double)ny[i]));	
-				curr_data->SetValue(i, 3, y + ss/sqrt((double)ny[i]));
-				}
-			else if((type & 0x00f0) == 0x0050) {
-				d = ny[i] > 1 ? distinv(t_dist, ny[i]-1, 1, 1.0-(ci_box/100.0), 2.0) : 0;
-				curr_data->SetValue(i, 2, y - d*ss/(double)sqrt((double)ny[i]));	
-				curr_data->SetValue(i, 3, y + d*ss/(double)sqrt((double)ny[i]));
-				}
-			//Whisker info is in cols 4 & 5
-			if((type & 0x0f0f) == 0x0101) {
-				curr_data->SetValue(i, 4, y - ss);	curr_data->SetValue(i, 5, y + ss);
-				}
-			else if((type & 0x0f0f) == 0x0201) {
-				curr_data->SetValue(i, 4, y - ss/sqrt((double)ny[i]));
-				curr_data->SetValue(i, 5, y + ss/sqrt((double)ny[i]));
-				}
-			else if((type & 0x0f0f) == 0x0501) {
-				d = ny[i] > 1 ? distinv(t_dist, ny[i]-1, 1, 1.0-(ci_err/100.0), 2.0) : 0;
-				curr_data->SetValue(i, 4, y - d*ss/sqrt((double)ny[i]));
-				curr_data->SetValue(i, 5, y + d*ss/sqrt((double)ny[i]));
-				}
-			}
-		if((type & 0x00f0) == 0x0040 || (type & 0x0f00) == 0x0400) for(i = 0; i < j; i++) {
-			// set min and max
-			lo = hi = *ay[i];
- 			if(ny[i] > 1) {
-				for(k = 1; k < ny[i]; k++) {
-					if(ay[i][k] < lo) lo = ay[i][k];
-					if(ay[i][k] > hi) hi = ay[i][k];
-					}
-				}
-			if((type & 0x00f0) == 0x0040) {
-				curr_data->SetValue(i, 2, lo);	curr_data->SetValue(i, 3, hi);
-				}
-			if((type & 0x0f00) == 0x0400) {
-				curr_data->SetValue(i, 4, lo);	curr_data->SetValue(i, 5, hi);
-				}
-			}
-		if(q1 && q3 && ((type & 0x00f0) == 0x0030 || (type & 0x0f00) == 0x0300)) for(i = 0; i < j; i++) {
-			// percentiles ....
-			if((type & 0x00f0) == 0x0030) {
-				curr_data->SetValue(i, 2, q1[i]);	curr_data->SetValue(i, 3, q3[i]);
-				}
-			if((type & 0x0f00) == 0x0300) {
-				curr_data->SetValue(i, 4, q1[i]);	curr_data->SetValue(i, 5, q3[i]);
-				}
-			}
-		if(type & 0xc000) for(i = 0; i < j; i++) {
-			//labels ...
-			if((type & 0x4000) && curr_data->GetValue(i, 5, &y)) curr_data->SetValue(i, 6, y);
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "%s%d", case_prefix ? case_prefix : "", ny[i]);
-#else
-			sprintf(TmpTxt, "%s%d", case_prefix ? case_prefix : "", ny[i]);
-#endif
-			curr_data->SetText(i, 7, TmpTxt);
-			}
-		}
-	else {
-		if(curr_data) delete curr_data;
-		curr_data = 0L;
-		}
-	if(q1) free(q1);	if(q2) free(q2);	if(q3) free(q3);
-	for(i = 0; i < m; i++) if(ay[i]) free(ay[i]);
-	free(tay);	free(ay);	free(ax);	free(ny);	free(xy);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Density distribution plot
-DensDisp::DensDisp(GraphObj *par, DataObj *d):Plot(par, d)
-{
-	FileIO(INIT_VARS);
-	Id = GO_DENSDISP;
-	if (!d && parent) parent->Command(CMD_DELOBJ, this, NULL);
-}
-
-DensDisp::DensDisp(int src):Plot(0L, 0L)
-{
-	int i;
-
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		//now set parent in all children
-		if(Boxes) 
-			for(i = 0; i < nPoints; i++) if(Boxes[i]) Boxes[i]->parent = this;
-		}
-}
-
-DensDisp::~DensDisp()
-{
-	int i;
-
-	if(Boxes) {
-		for(i = 0; i < nPoints; i++) if(Boxes[i]) DeleteGO(Boxes[i]);
-		free (Boxes);
-		}
-	if(yRange) free(yRange);		if(xRange) free(xRange);
-	yRange = xRange = 0L;
-	if(name) free(name);			name=0L;
-	if(x_info) free(x_info);		x_info = 0L;
-	if(y_info) free(y_info);		y_info = 0L;
-	Undo.InvalidGO(this);
-}
-
-bool
-DensDisp::SetSize(int select, double value)
-{
-	int i;
-
-	switch(select & 0xfff){
-	case SIZE_BOX_LINE:
-		if(Boxes) for(i = 0; i < nPoints; i++) 
-			if(Boxes[i]) Boxes[i]->SetSize(select, value);
-		return true;
-	}
-	return false;
-}
-
-bool
-DensDisp::SetColor(int select, DWORD col)
-{
-	int i;
-
-	switch(select) {
-	case COL_BOX_LINE:
-		if(Boxes) for(i = 0; i < nPoints; i++)
-			if(Boxes[i]) Boxes[i]->SetColor(select, col);
-		return true;
-	default:
-		return false;
-		}
-}
-
-void
-DensDisp::DoPlot(anyOutput *o)
-{
-	int i;
-
-	if(!parent) return;
-	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
-	if(use_xaxis || use_yaxis) {
-		ApplyAxes(o);
-		if(Boxes) for(i = 0; i < nPoints; i++) 
-			if(Boxes[i]) Boxes[i]->DoPlot(o);
-		parent->Command(CMD_AXIS, 0L, o);
-		}
-	else {
-		if(Boxes) for(i = 0; i < nPoints; i++) 
-			if(Boxes[i]) Boxes[i]->DoPlot(o);
-		}
-	dirty = false;
-}
-
-bool
-DensDisp::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-	MouseEvent *mev;
-
-	switch (cmd) {
-	case CMD_MRK_DIRTY:
-		dirty = true;
-	case CMD_SETSCROLL:		case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		return false;
-	case CMD_SCALE:
-		DefLine.width *= ((scaleINFO*)tmpl)->sy.fy;		DefLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		DefFillLine.width *= ((scaleINFO*)tmpl)->sy.fy;	DefFillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		DefFill.scale *= ((scaleINFO*)tmpl)->sy.fy;
-		for(i = 0; i < nPoints; i++){
-			if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
-			}
-		return true;
-	case CMD_USEAXIS:
-		UseAxis(*((int*)tmpl));
-		return true;
-	case CMD_MOUSE_EVENT:
-		if(hidden) return false;
-		mev = (MouseEvent *) tmpl;
-		switch(mev->Action) {
-		case MOUSE_LBUP:
-			for(i = nPoints-1; i >= 0 && !CurrGO; i--) {
-				if(Boxes[i] && Boxes[i]->Command(cmd, tmpl, o))return true;
-				}
-			break;
-			}
-		break;
-	case CMD_SET_DATAOBJ:
-		for(i = 0; i < nPoints; i++) if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
-		Id = GO_DENSDISP;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_DELOBJ:
-		if(!parent || !o) return false;
-		if(DeleteGOL((GraphObj***)&Boxes, nPoints, (GraphObj*)tmpl, o)) 
-			return parent->Command(CMD_REDRAW, 0L, o);
-		break;
-	case CMD_AUTOSCALE:
-		if(dirty){
-			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
-			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-			}
-		else return true;
-		dirty = false;
-	case CMD_BOX_TYPE:		case CMD_BOX_FILL:
-		if(Boxes) for (i = 0; i < nPoints; i++)
-			if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_UPDATE:
-		if(Boxes) SavVarObs((GraphObj **)Boxes, nPoints, UNDO_CONTINUE);
-		DoUpdate();
-		return true;
-		}
-	return false;
-}
-
-void
-DensDisp::DoUpdate()
-{
-	AccRange *rX, *rY;
-	int i, j, k, l, ic, n;
-	double v, w;
-	lfPOINT fp1, fp2;
-	Box **op = Boxes;
-
-	if(xRange && yRange && (rX = new AccRange(xRange)) && (rY = new AccRange(yRange))) {
-		if((n=rX->CountItems()) == rY->CountItems()) {
-			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-			if(!(Boxes = (Box**)realloc(Boxes, n * sizeof(Box*)))) return;
-			if(op && op != Boxes) Undo.InvalidGO(this);
-			for(i = nPoints; i < n; i++) {
-				Boxes[i] = 0L;
-				}
-			nPoints = n;
-			rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
-			rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
-			for(ic = 0; ic < n && !data->GetValue(j, i, &v); ic++) {
-				rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
-				}
-			rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
-			if(type & 0x10){			//vertical ?
-				fp2.fx = 0;		fp2.fy = v;
-				}
-			else {
-				fp2.fx = v;		fp2.fy = 0.0;
-				}
-			ic = 0;
-			do {
-				if(data->GetValue(j, i, &v) && data->GetValue(l, k, &w)){
-					fp1.fx = fp2.fx;	fp1.fy = fp2.fy;
-					if(type & 0x10) {
-						CheckBounds(w, fp1.fy);			CheckBounds(-w, v);
-						fp2.fy = v;		
-						switch(type & 0x3) {
-						case 1:		fp1.fx = fp2.fx = w/2.0;		break;
-						case 2:		fp1.fx = fp2.fx = -w/2.0;		break;
-						default:	fp2.fx = 0.0;					break;
-							}
-						}
-					else {
-						CheckBounds(fp1.fx, w);			CheckBounds(v, -w);
-						fp2.fx = v;
-						switch(type & 0x3) {
-						case 1:		fp1.fy = fp2.fy = w/2.0;		break;
-						case 2:		fp1.fy = fp2.fy = -w/2.0;		break;
-						default:	fp2.fy = 0.0;					break;
-							}
-						}
-					if(op && Boxes[ic]) {
-						Boxes[ic]->SetSize(SIZE_XPOS, fp1.fx);	Boxes[ic]->SetSize(SIZE_XPOS+1, fp2.fx);
-						Boxes[ic]->SetSize(SIZE_YPOS, fp1.fy);	Boxes[ic]->SetSize(SIZE_YPOS+1, fp2.fy);
-						Boxes[ic]->SetSize(SIZE_BOX, (type &0x03) ? w/2.0 : w);
-						}
-					else if(!op && (Boxes[ic] = new Box(this, data, fp1, fp2, BAR_WIDTHDATA)))
-						Boxes[ic]->SetSize(SIZE_BOX, (type &0x03) ? w/2.0 : w);
-					}
-				ic++;
-				}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
-			if(!op) {
-				SetSize(SIZE_BOX_LINE, DefLine.width);
-				SetColor(COL_BOX_LINE, DefLine.color);
-				Command(CMD_BOX_FILL, (void*)&DefFill, 0L);
-				}
-			}
-		delete(rX);		delete(rY);
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Stacked bars consist of several box-plots
-StackBar::StackBar(GraphObj *par, DataObj *d):Plot(par, d)
-{
-	FileIO(INIT_VARS);
-	Id = GO_STACKBAR;
-	if (!d && parent) parent->Command(CMD_DELOBJ, this, NULL);
-}
-
-StackBar::StackBar(int src):Plot(0L, 0L)
-{
-	int i;
-
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		//now set parent in all children
-		if(Boxes) for(i = 0; i < numPlots; i++)
-			if(Boxes[i]) Boxes[i]->parent = this;
-		if(xyPlots) for(i = 0; i < numXY; i++)
-			if(xyPlots[i]) xyPlots[i]->parent = this;
-		if(Polygons) for(i = 0; i < numPG; i++)
-			if(Polygons[i]) Polygons[i]->parent = this;
-		if(Lines) for(i = 0; i < numPL; i++)
-			if(Lines[i]) Lines[i]->parent = this;
-		}
-}
-
-StackBar::~StackBar()
-{
-	int i;
-
-	if(Boxes){
-		for(i = 0; i < numPlots; i++) if(Boxes[i]) DeleteGO(Boxes[i]);
-		free(Boxes);
-		}
-	if(xyPlots){
-		for(i = 0; i < numXY; i++) if(xyPlots[i]) DeleteGO(xyPlots[i]);
-		free(xyPlots);
-		}
-	if(Polygons) {
-		for(i = 0; i < numPG; i++) if(Polygons[i]) DeleteGO(Polygons[i]);
-		free(Polygons);
-		}
-	if(Lines) {
-		for(i = 0; i < numPL; i++) if(Lines[i]) DeleteGO(Lines[i]);
-		free(Lines);
-		}
-	if(ssXrange) free(ssXrange);	if(ssYrange) free(ssYrange);
-	if(CumData) delete CumData;		CumData = 0L;
-	if(name) free(name);			name=0L;
-	if(x_tv) delete x_tv;			x_tv = 0L;
-	if(y_tv) delete y_tv;			y_tv = 0L;
-	Undo.InvalidGO(this);
-}
-
-bool
-StackBar::SetSize(int select, double value)
-{
-	int i;
-
-	switch(select & 0xfff){
-	case SIZE_BAR:
-		if(xyPlots) for(i = 0; i < numXY; i++)
-			if(xyPlots[i]) xyPlots[i]->SetSize(select, value);
-		return true;
-	case SIZE_BOX:		case SIZE_BOX_LINE:
-		if(Boxes) for(i = 0; i < numPlots; i++) 
-			if(Boxes[i]) Boxes[i]->SetSize(select, value);
-		return true;
-	}
-	return false;
-}
-
-void
-StackBar::DoPlot(anyOutput *o)
-{
-	int i;
-	double dx, dy;
-	fRECT oldREC;
-
-	if(!o || !parent) return;
-	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
-	if(use_xaxis || use_yaxis) ApplyAxes(o);
-	dx = o->un2fix(dspm.fx);		dy = o->un2fiy(dspm.fy);
-	memcpy(&oldREC, &o->Box1, sizeof(fRECT));
-	if(Boxes) for(i = 0; i < numPlots; i++) if(Boxes[i]) {
-		if(Boxes[i]->Id >= GO_PLOT && Boxes[i]->Id < GO_GRAPH) {
-			if(((Plot*)Boxes[i])->hidden == 0) Boxes[i]->DoPlot(o);
-			}
-		else Boxes[i]->DoPlot(o);
-		}
-	if(xyPlots) for(i = 0; i < numXY; i++) if(xyPlots[i]) {
-		if(xyPlots[i]->Id >= GO_PLOT && xyPlots[i]->Id < GO_GRAPH) {
-			if(((Plot*)xyPlots[i])->hidden == 0) xyPlots[i]->DoPlot(o);
-			}
-		else xyPlots[i]->DoPlot(o);
-		}
-	if(Polygons) for(i = 0; i < numPG; i++)
-		if(Polygons[i]) Polygons[i]->DoPlot(o);
-	if(Lines) for(i = numPL-1; i >= 0; i--){
-		o->Box1.Xmin = oldREC.Xmin + dx*i;
-		o->Box1.Ymin = oldREC.Ymin + dy*i;
-		o->Box1.Xmax = oldREC.Xmax + dx*i;
-		o->Box1.Ymax = oldREC.Ymax + dy*i;
-		if(Lines[i]) Lines[i]->DoPlot(o);
-		}
-	dirty = false;
-	memcpy(&o->Box1, &oldREC, sizeof(fRECT));
-	if(use_xaxis || use_yaxis) parent->Command(CMD_AXIS, 0L, o);
-}
-
-bool
-StackBar::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-	static MouseEvent *mev;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		if(hidden) return false;
-		mev = (MouseEvent *) tmpl;
-		switch(mev->Action) {
-		case MOUSE_LBUP:
-			if(Boxes && !CurrGO) for(i = 0; i < numPlots; i++)
-				if(Boxes[i] && Boxes[i]->Command(cmd, tmpl, o))return true;
-			if(xyPlots && !CurrGO) for(i = 0; i < numXY; i++)
-				if(xyPlots[i] && xyPlots[i]->Command(cmd, tmpl, o))return true;
-			if(Polygons && !CurrGO) for(i = 0; i < numPG; i++)
-				if(Polygons[i] && Polygons[i]->Command(cmd, tmpl, o))return true;
-			if(Lines && !CurrGO) for(i = 0; i < numPL; i++)
-				if(Lines[i] && Lines[i]->Command(cmd, tmpl, o))return true;
-			break;
-			}
-		break;
-	case CMD_OBJTREE:
-		if(!tmpl) return false;
-		if(Boxes) for(i = 0; i < numPlots; i++) if(Boxes[i]) 
-			((ObjTree*)tmpl)->Command(CMD_UPDATE, Boxes[i], 0L);
-		if(xyPlots) for(i = 0; i < numXY; i++) if(xyPlots[i]) 
-			((ObjTree*)tmpl)->Command(CMD_UPDATE, xyPlots[i], 0L);
-		return true;
-	case CMD_SCALE:
-		dspm.fx *= ((scaleINFO*)tmpl)->sx.fy;
-		dspm.fy *= ((scaleINFO*)tmpl)->sy.fy;
-	case CMD_LEGEND:
-		if(Boxes) for (i = 0; i < numPlots; i++)
-			if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
-		if(Polygons) for (i = numPG-1; i >= 0; i--)
-			if(Polygons[i]) Polygons[i]->Command(cmd, tmpl, o);
-		if(Lines) for (i = numPL-1; i >= 0; i--)
-			if(Lines[i]) Lines[i]->Command(cmd, tmpl, o);
-		if(xyPlots) for (i = 0; i < numXY; i++)
-			if(xyPlots[i]) xyPlots[i]->Command(cmd, tmpl, o);
-		break;
-	case CMD_USEAXIS:
-		UseAxis(*((int*)tmpl));
-		return true;
-	case CMD_MRK_DIRTY:
-		dirty = true;
-	case CMD_SETSCROLL:		case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		return false;
-	case CMD_BAR_TYPE:
-		if(xyPlots) for(i = 0; i < numXY; i++)
-			if(xyPlots[i]) xyPlots[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_SAVE_BARS:		case CMD_SAVE_BARS_CONT:
-		if(Boxes) for(i = 0; i < numPlots; i++)
-			if(Boxes[i])Boxes[i]->Command(CMD_SAVE_BARS_CONT, tmpl, o);
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = GO_STACKBAR;
-		if(data == tmpl) return true;
-		data = (DataObj *)tmpl;
-	case CMD_AUTOSCALE:		case CMD_UPDATE:
-		if(cmd == CMD_AUTOSCALE) {
-			if(hidden) return false;
-			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
-			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-			dirty = false;
-			}
-		if(cum_data_mode){
-			if(CumData) delete CumData;
-			if(CumData = CreaCumData(ssXrange, ssYrange, cum_data_mode, StartVal)) {
-				if(Polygons) for(i = 0; i < numPG; i++)
-					if(Polygons[i]) Polygons[i]->Command(CMD_SET_DATAOBJ, CumData, o);
-				if(Lines) for(i = 0; i < numPL; i++)
-					if(Lines[i]) Lines[i]->Command(CMD_SET_DATAOBJ, CumData, o);
-				if(xyPlots) for(i = 0; i < numXY; i++)
-					if(xyPlots[i]) xyPlots[i]->Command(CMD_SET_DATAOBJ, CumData, o);
-				if(Boxes) for (i = 0; i < numPlots; i++) 
-					if(Boxes[i]) Boxes[i]->Command(CMD_SET_DATAOBJ, CumData, o);
-				}
-			}
-		if(cmd == CMD_SET_DATAOBJ) tmpl = (void*) CumData;
-		if(Polygons) for(i = 0; i < numPG; i++)
-			if(Polygons[i]) Polygons[i]->Command(cmd, tmpl, o);
-		if(Lines) for(i = 0; i < numPL; i++)
-			if(Lines[i]) Lines[i]->Command(cmd, tmpl, o);
-		if(xyPlots) for(i = 0; i < numXY; i++)
-			if(xyPlots[i]) {
-				if(cmd == CMD_AUTOSCALE && xyPlots[i]->Id >= GO_PLOT && xyPlots[i]->Id < GO_GRAPH) {
-					if(((Plot*)xyPlots[i])->hidden == 0) xyPlots[i]->Command(cmd, tmpl, o);
-					}
-				else xyPlots[i]->Command(cmd, tmpl, o);
-				}
-	case CMD_BOX_TYPE:
-		if(Boxes) for (i = 0; i < numPlots; i++) 
-			if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_DELOBJ:
-		if(o) o->HideMark();
-		if(!tmpl || !parent) return false;
-		if(DeleteGOL((GraphObj***)&Polygons, numPG, (GraphObj*)tmpl, o)) 
-			return parent->Command(CMD_REDRAW, 0L, o);
-		if(DeleteGOL((GraphObj***)&Lines, numPL, (GraphObj*)tmpl, o)) 
-			return parent->Command(CMD_REDRAW, 0L, o);
-		if(DeleteGOL((GraphObj***)&xyPlots, numXY, (GraphObj*)tmpl, o)) 
-			return parent->Command(CMD_REDRAW, 0L, o);
-		if(DeleteGOL((GraphObj***)&Boxes, numPlots, (GraphObj*)tmpl, o)) 
-			return parent->Command(CMD_REDRAW, 0L, o);
-		if(xyPlots) for(i = 0; i < numXY; i++)
-			if(xyPlots[i] && xyPlots[i]->Command(cmd, tmpl, o)) return true;
-		if(Boxes) for(i = 0; i < numPlots; i++)
-			if(Boxes[i] && Boxes[i]->Command(cmd, tmpl, o)) return true;
-		return false;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Stacked polygons is based on stacked bar
-StackPG::StackPG(GraphObj *par, DataObj *d):StackBar(par, d)
-{
-	Id = GO_STACKPG;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// waterfall plot is based on stacked bar
-Waterfall::Waterfall(GraphObj *par, DataObj *d):StackBar(par, d)
-{
-	Id = GO_WATERFALL;
-	dspm.fx = dspm.fy = 0.0;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// multi data line plot is based on stacked bar
-MultiLines::MultiLines(GraphObj *par, DataObj *d):StackBar(par, d)
-{
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// simple pie chart
-PieChart::PieChart(GraphObj *par, DataObj *d):Plot(par, d)
-{
-	FileIO(INIT_VARS);
-	Id = GO_PIECHART;
-	if (!d && parent) parent->Command(CMD_DELOBJ, this, NULL);
-}
-
-PieChart::PieChart(int src):Plot(0L, 0L)
-{
-	int i;
-	
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		//now set parent in  children
-		if(Segments) 
-			for(i = 0; i < nPts; i++) if(Segments[i]) Segments[i]->parent = this;
-		}
-}
-
-PieChart::~PieChart()
-{
-	int i;
-
-	if(Segments) {
-		for(i = 0; i < nPts; i++) if(Segments[i]) DeleteGO(Segments[i]);
-		free(Segments);		Segments = 0L;
-		}
-	if(ssRefA) free(ssRefA);	if(ssRefR) free(ssRefR);
-	ssRefA = ssRefR = 0L;
-	if(name) free(name);		name=0L;
-	Undo.InvalidGO(this);
-}
-
-bool 
-PieChart::SetSize(int select, double value)
-{
-	int i;
-
-	switch(select & 0xfff) {
-	case SIZE_XPOS:
-	case SIZE_YPOS:
-	case SIZE_RADIUS1:
-	case SIZE_RADIUS2:
-		if(Segments) for(i = 0; i < nPts; i++) {
-			if(Segments[i]) Segments[i]->SetSize(select, value);
-			}
-		return true;
-	default:
-		return false;
-		}
-	return true;
-}
-
-void
-PieChart::DoPlot(anyOutput *o)
-{
-	int i;
-
-	if(Segments) for(i = 0; i < nPts; i++) if(Segments[i]) Segments[i]->DoPlot(o);
-}
-
-bool
-PieChart::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-	MouseEvent *mev;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		if(hidden) return false;
-		mev = (MouseEvent *) tmpl;
-		switch(mev->Action) {
-		case MOUSE_LBUP:
-			//select objects invers to plot order
-			if(Segments && !CurrGO) for(i = nPts-1; i>=0; i--)
-				if(Segments[i]) if(Segments[i]->Command(cmd, tmpl, o))break;
-			break;
-			}
-		break;
-	case CMD_SETSCROLL:		case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		return false;
-	case CMD_UPDATE:
-		DoUpdate();
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = GO_PIECHART;
-		data = (DataObj *)tmpl;
-	case CMD_SCALE:
-		if(cmd == CMD_SCALE) {
-			CtDef.fx *= ((scaleINFO*)tmpl)->sx.fy;	CtDef.fy *= ((scaleINFO*)tmpl)->sx.fy;
-			}
-	case CMD_SHIFT_OUT:		case CMD_SEG_FILL:		case CMD_SEG_LINE:
-	case CMD_SEG_MOVEABLE:	case CMD_LEGEND:
-		if(Segments) for(i = 0; i < nPts; i++)
-			if(Segments[i]) Segments[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_DELOBJ:
-		o->HideMark();
-		if(Segments && parent) for(i = 0; i < nPts; i++) {
-			if(Segments[i] && tmpl == (void *)Segments[i]) {
-				Undo.DeleteGO((GraphObj**)(&Segments[i]), 0L, o);
-				parent->Command(CMD_REDRAW, NULL, o);
-				return true;
-				}
-			}
-		break;
-	case CMD_SAVE_SYMBOLS:
-		return SavVarObs((GraphObj **)Segments, nPts, 0L);
-		}
-	return false;
-}
-
-void
-PieChart::DoUpdate()
-{
-	AccRange *rY = 0L, *rR = 0L;
-	double sum, fv, dang1, dang2;
-	int i, ix, iy, rix, riy;
-
-	if(ssRefA && (rY = new AccRange(ssRefA))) {
-		SavVarObs((GraphObj **)Segments, nPts, UNDO_CONTINUE);
-		if(ssRefR) rR = new AccRange(ssRefR);
-		rY->GetFirst(&ix, &iy);				rY->GetNext(&ix, &iy);
-		for(i = 0, sum = 0.0; i < nPts; i++){
-			if(data->GetValue(iy, ix, &fv)) sum += fv;
-			rY->GetNext(&ix, &iy);
-			}
-		sum /= CtDef.fy;
-		dang1 = dang2 = CtDef.fx;
-		rY->GetFirst(&ix, &iy);				rY->GetNext(&ix, &iy);
-		if(rR) {
-			rR->GetFirst(&rix, &riy);		rR->GetNext(&rix, &riy);
-			}
-		for(i = 0; i < nPts; i++){
-			if(data->GetValue(iy, ix, &fv)) {
-				dang2 -= (double)fv / sum;
-				if(dang2 < 0.0) dang2 += 360.0;
-				if(Segments[i]) {
-					Segments[i]->SetSize(SIZE_ANGLE1, dang1);
-					Segments[i]->SetSize(SIZE_ANGLE2, dang2);
-					if(rR && data->GetValue(riy, rix, &fv)){
-						fv *= FacRad;
-						Segments[i]->SetSize(SIZE_RADIUS2, fv);
-						}
-					}
-				dang1 = dang2;
-				}
-			rY->GetNext(&ix, &iy);
-			if(rR) rR->GetNext(&rix, &riy);
-			}
-		}
-	if(rY) delete rY;		if(rR) delete rR;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// ring chart is based on piechart
-RingChart::RingChart(GraphObj *par, DataObj *d):PieChart(par, d)
-{
-	Id = GO_RINGCHART;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// a GoGroup contains objects NOT referring to data (e.g. drawing objects)
-GoGroup::GoGroup(GraphObj *par, DataObj *d):Plot(par, d)
-{
-	Objects = 0L;
-	nObs = 0;
-	fPos.fx = fPos.fy = 0.0;
-	Id = GO_GROUP;
-}
-
-GoGroup::GoGroup(int src):Plot(0L, 0L)
-{
-	int i;
-	
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		//now set parent in  children
-		if(Objects) 
-			for(i = 0; i < nObs; i++) if(Objects[i]) Objects[i]->parent = this;
-		}
-}
-
-GoGroup::~GoGroup()
-{
-	int i;
-
-	if(Objects && nObs) {
-		for(i = 0; i < nObs; i++) if(Objects[i]) DeleteGO(Objects[i]);
-		free(Objects);
-		}
-	if(name) free(name);		name=0L;
-	Undo.InvalidGO(this);
-}
-
-double
-GoGroup::GetSize(int select)
-{
-	if(parent) switch(select){
-	case SIZE_GRECT_TOP:
-	case SIZE_GRECT_BOTTOM:
-		return parent->GetSize(select)-fPos.fy;
-	case SIZE_GRECT_LEFT:
-	case SIZE_GRECT_RIGHT:
-		return parent->GetSize(select)-fPos.fx;
-	case SIZE_XPOS:
-		return fPos.fx;
-	case SIZE_YPOS:
-		return fPos.fy;
-		}
-	return 0.0f;
-}
-
-void 
-GoGroup::DoPlot(anyOutput *o)
-{
-	int i;
-	double dx, dy;
-	
-	dx = o->un2fix(fPos.fx + (parent ? parent->GetSize(SIZE_GRECT_LEFT) : 0.0));
-	dy = o->un2fiy(fPos.fy + (parent ? parent->GetSize(SIZE_GRECT_TOP) : 0.0));
-	o->VPorg.fx += dx;				o->VPorg.fy += dy;
-	for(i = 0; i < nObs; i++) if(Objects[i]) Objects[i]->DoPlot(o);
-	o->VPorg.fx -= dx;				o->VPorg.fy -= dy;
-}
-
-bool
-GoGroup::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	GraphObj **tmp_go;
-	MouseEvent *mev;
-	int i;
-
-	switch(cmd) {
-	case CMD_MOUSE_EVENT:
-		if(hidden) return false;
-		mev = (MouseEvent *) tmpl;
-		switch(mev->Action) {
-		case MOUSE_LBUP:
-			//select objects invers to plot order
-			if(Objects && !CurrGO) for(i = nObs-1; i>=0; i--)
-				if(Objects[i]) if(Objects[i]->Command(cmd, tmpl, o))break;
-			break;
-			}
-		break;
-	case CMD_SET_DATAOBJ:
-		Id = GO_GROUP;
-		if(Objects) for(i = 0; i < nObs; i++)
-			if(Objects[i]) Objects[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_DROP_OBJECT:
-		if(!Objects || nObs<1) {
-			if((Objects = (GraphObj **)calloc(1, sizeof(GraphObj*)))){
-				Objects[0] = (GraphObj *)tmpl;
-				nObs = 1;
-				return true;
-				}
-			}
-		else if((tmp_go = (GraphObj **)realloc(Objects, (nObs+1)*sizeof(GraphObj*)))) {
-			Objects = tmp_go;
-			Objects[nObs++] = (GraphObj *)tmpl;
-			return true;
-			}
-		break;
-	case CMD_SETSCROLL:
-	case CMD_REDRAW:
-		if(parent) return parent->Command(CMD_REDRAW, tmpl, o);
-		return false;
-	case CMD_DELOBJ:
-		if(Objects && parent) for(i = 0; i < nObs; i++) {
-			o->HideMark();
-			if(Objects[i] && tmpl == (void *)Objects[i]) {
-				Undo.DeleteGO((GraphObj**)(&Objects[i]), 0L, o);
-				parent->Command(CMD_REDRAW, NULL, o);
-				return true;
-				}
-			}
-		break;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// star chart
-StarChart::StarChart(GraphObj *par, DataObj *d):GoGroup(par, d)
-{
-	Id = GO_STARCHART;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// three dimensional scatterplot
-Scatt3D::Scatt3D(GraphObj *par, DataObj *d, DWORD flags):Plot(par, d)
-{
-	FileIO(INIT_VARS);
-	c_flags = flags;
-	Id = GO_SCATT3D;
-}
-
-Scatt3D::Scatt3D(GraphObj *par, DataObj *d, Brick **cols, long nob):Plot(par, d)
-{
-	int i;
-
-	FileIO(INIT_VARS);
-	c_flags = 0L;		Id = GO_SCATT3D;
-	Columns = cols;		nColumns = nob;
-	if(Columns) for(i = 0; i < nColumns; i++) if(Columns[i]) Columns[i]->parent=this;
-}
-
-Scatt3D::Scatt3D(GraphObj *par, DataObj *d, Sphere **ba, long nob):Plot(par, d)
-{
-	int i;
-
-	FileIO(INIT_VARS);
-	c_flags = 0L;		Id = GO_SCATT3D;
-	Balls = ba;			nBalls = nob;
-	if(Balls) for(i = 0; i < nBalls; i++) if(Balls[i]) Balls[i]->parent=this;
-}
-
-Scatt3D::Scatt3D(int src):Plot(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-Scatt3D::~Scatt3D()
-{
-	long i;
-
-	if(ssRefX) free(ssRefX);	if(ssRefY) free(ssRefY);
-	if(ssRefZ) free(ssRefZ);	ssRefX = ssRefY = ssRefZ = 0L;
-	Undo.InvalidGO(this);
-	if(Balls) {
-		for(i = 0; i < nBalls; i++) if(Balls[i]) DeleteGO(Balls[i]);
-		free(Balls);					Balls = 0L;
-		}
-	if(Columns) {
-		for(i = 0; i < nColumns; i++) if(Columns[i]) DeleteGO(Columns[i]);
-		free(Columns);					Columns = 0L;
-		}
-	if(DropLines) {
-		for(i = 0; i < nDropLines; i++) if(DropLines[i]) DeleteGO(DropLines[i]);
-		free(DropLines);				DropLines = 0L;
-		}
-	if(Arrows) {
-		for(i = 0; i < nArrows; i++) if(Arrows[i]) DeleteGO(Arrows[i]);
-		free(Arrows);					Arrows = 0L;
-		}
-	if(Line) {
-		DeleteGO(Line);					Line = 0L;
-		}
-	if(rib) {
-		DeleteGO(rib);					 rib = 0L;
-		}
-	if(name) free(name);				name=0L;
-	if(data_desc) free(data_desc);		data_desc = 0L;
-}
-
-double
-Scatt3D::GetSize(int select)
-{
-	if(parent) return parent->GetSize(select);
-	return 0.0;
-}
-
-bool
-Scatt3D::SetSize(int select, double value)
-{
-	int i;
-
-	switch(select & 0xfff) {
-	case SIZE_SYM_LINE:
-	case SIZE_SYMBOL:
-		if(Balls) for (i=0; i < nBalls; i++)
-			if(Balls[i]) Balls[i]->SetSize(select, value);
-		return true;
-	case SIZE_BAR_BASE:		case SIZE_BAR_LINE:		case SIZE_BAR:
-	case SIZE_BAR_DEPTH:
-		if(Columns) for (i=0; i < nColumns; i++)
-			if(Columns[i]) Columns[i]->SetSize(select, value);
-		return true;
-	case SIZE_ARROW_LINE:	case SIZE_ARROW_CAPWIDTH:
-	case SIZE_ARROW_CAPLENGTH:
-		if(Arrows) for (i=0; i < nArrows; i++)
-			if(Arrows[i]) Arrows[i]->SetSize(select, value);
-		return true;
-		}
-	return false;
-}
-
-bool
-Scatt3D::SetColor(int select, DWORD col)
-{
-	int i;
-
-	switch(select) {
-	case COL_SYM_LINE:			case COL_SYM_FILL:
-		if(Balls) for (i=0; i < nBalls; i++)
-			if(Balls[i]) Balls[i]->SetColor(select, col);
-		return true;
-	case COL_BAR_LINE:			case COL_BAR_FILL:
-		if(Columns) for (i=0; i < nColumns; i++)
-			if(Columns[i]) Columns[i]->SetColor(select, col);
-		return true;
-	case COL_ARROW:
-		if(Arrows) for (i=0; i < nArrows; i++)
-			if(Arrows[i]) Arrows[i]->SetColor(select, col);
-		return true;
-		}
-	return false;
-}
-
-void
-Scatt3D::DoPlot(anyOutput *o)
-{
-	long i;
-	RECT rc;
-
-	if(!o || !parent) return;
-	if(use_xaxis || use_yaxis || use_zaxis) ApplyAxes(o);
-	o->GetSize(&rc);
-	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
-	rDims.left = rc.right;			rDims.right = rc.left;
-	rDims.top = rc.bottom;			rDims.bottom = rc.top;
-	if(Balls) {
-		for(i = 0; i < nBalls; i++){
-			if(Balls[i]){
-				Balls[i]->DoPlot(o);
-				UpdateMinMaxRect(&rDims, Balls[i]->rDims.right, Balls[i]->rDims.top);
-				UpdateMinMaxRect(&rDims, Balls[i]->rDims.left, Balls[i]->rDims.bottom);
-				}
-			}
-		}
-	if(Columns) {
-		for(i = 0; i < nColumns; i++){
-			if(Columns[i]){
-				Columns[i]->DoPlot(o);
-				UpdateMinMaxRect(&rDims, Columns[i]->rDims.right, Columns[i]->rDims.top);
-				UpdateMinMaxRect(&rDims, Columns[i]->rDims.left, Columns[i]->rDims.bottom);
-				}
-			}
-		}
-	if(DropLines) {
-		for(i = 0; i < nDropLines; i++){
-			if(DropLines[i]){
-				DropLines[i]->DoPlot(o);
-				UpdateMinMaxRect(&rDims, DropLines[i]->rDims.right, DropLines[i]->rDims.top);
-				UpdateMinMaxRect(&rDims, DropLines[i]->rDims.left, DropLines[i]->rDims.bottom);
-				}
-			}
-		}
-	if(Arrows) {
-		for(i = 0; i < nArrows; i++){
-			if(Arrows[i]){
-				Arrows[i]->DoPlot(o);
-				UpdateMinMaxRect(&rDims, Arrows[i]->rDims.right, Arrows[i]->rDims.top);
-				UpdateMinMaxRect(&rDims, Arrows[i]->rDims.left, Arrows[i]->rDims.bottom);
-				}
-			}
-		}
-	if(Line) {
-		Line->DoPlot(o);
-		UpdateMinMaxRect(&rDims, Line->rDims.right, Line->rDims.top);
-		UpdateMinMaxRect(&rDims, Line->rDims.left, Line->rDims.bottom);
-		}
-	if(rib) {
-		rib->DoPlot(o);
-		UpdateMinMaxRect(&rDims, rib->rDims.right, rib->rDims.top);
-		UpdateMinMaxRect(&rDims, rib->rDims.left, rib->rDims.bottom);
-		}
-	if(use_xaxis || use_yaxis || use_zaxis)parent->Command(CMD_AXIS, 0L, o);
-	dirty = false;
-}
-
-bool
-Scatt3D::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-	MouseEvent *mev;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		if(hidden) return false;
-		mev = (MouseEvent *) tmpl;
-		switch(mev->Action) {
-		case MOUSE_LBUP:
-			if(Balls && !CurrGO) for(i = 0; i < nBalls; i++)
-				if(Balls[i]) if(Balls[i]->Command(cmd, tmpl, o))return true;
-			if(Columns && !CurrGO) for(i = 0; i < nColumns; i++)
-				if(Columns[i]) if(Columns[i]->Command(cmd, tmpl, o))return true;
-			if(DropLines && !CurrGO) for(i = 0; i < nDropLines; i++)
-				if(DropLines[i]) if(DropLines[i]->Command(cmd, tmpl, o))return true;
-			if(Arrows && !CurrGO) for(i = 0; i < nArrows; i++)
-				if(Arrows[i]) if(Arrows[i]->Command(cmd, tmpl, o))return true;
-			if(Line && !CurrGO) if(Line->Command(cmd, tmpl, o)) return true;
-			if(rib && !CurrGO) if(rib->Command(cmd, tmpl, o)) return true;
-			break;
-			}
-		break;
-	case CMD_MRK_DIRTY:
-		dirty = true;
-	case CMD_SET_GO3D:		case CMD_SETSCROLL:		case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_DL_LINE:		case CMD_DL_TYPE:
-		if(DropLines) for(i = 0; i < nDropLines; i++)
-			if(DropLines[i]) DropLines[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_ARROW_TYPE:	case CMD_ARROW_ORG3D:
-		if(Arrows) for(i = 0; i < nArrows; i++)
-			if(Arrows[i]) Arrows[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_USEAXIS:
-		return UseAxis(*((int*)tmpl));
-	case CMD_LEGEND:
-		if(!tmpl) return false;
-		if(Balls) {
-			if(Line && Line->Id == GO_LINE3D) {
-				for (i = 0; i < nBalls && i < 100; i++)
-					if(Balls[i]) ((Legend*)tmpl)->HasSym(&Line->Line, Balls[i], 0L);
-				}
-			else {
-				for (i = 0; i < nBalls && i < 100; i++) {
-					if(Balls[i]) {
-						if(Balls[i]->type) Balls[i]->Command(cmd, tmpl, o);
-						else ((Legend*)tmpl)->HasSym(0L, Balls[i], 0L);
-						}
-					}
-				}
-			}
-		else if(Line) Line->Command(cmd, tmpl, o);
-		if(Columns) for(i = 0; i < nColumns; i++)
-			if(Columns[i]) Columns[i]->Command(cmd, tmpl, o);
-		if(rib) rib->Command(cmd, tmpl, o);
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = GO_SCATT3D;
-		data = (DataObj *)tmpl;
-	case CMD_UPDATE:	case CMD_SCALE:
-		if(Balls) {
-			SavVarObs((GraphObj**)Balls, nBalls, UNDO_CONTINUE);
-			for(i = 0; i < nBalls; i++)	if(Balls[i]) Balls[i]->Command(cmd, tmpl, o);
-			}
-		if(Columns) {
-			SavVarObs((GraphObj**)Columns, nColumns, UNDO_CONTINUE);
-			for(i = 0; i < nColumns; i++) if(Columns[i]) Columns[i]->Command(cmd, tmpl, o);
-			}
-		if(DropLines) {
-			SavVarObs((GraphObj**)DropLines, nDropLines, UNDO_CONTINUE);
-			for(i = 0; i < nDropLines; i++) if(DropLines[i]) DropLines[i]->Command(cmd, tmpl, o);
-			}
-		if(Arrows) {
-			SavVarObs((GraphObj**)Arrows, nArrows, UNDO_CONTINUE);
-			for(i = 0; i < nArrows; i++) if(Arrows[i]) Arrows[i]->Command(cmd, tmpl, o);
-			}
-		if(Line) Line->Command(cmd, tmpl, o);
-		if(rib) rib->Command(cmd, tmpl, o);
-		return true;
-	case CMD_AUTOSCALE:
-		if(dirty) {
-			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-			xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
-			xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
-			if(Balls) for(i = 0; i < nBalls; i++)
-				if(Balls[i]) Balls[i]->Command(cmd, tmpl, o);
-			if(Columns) for(i = 0; i < nColumns; i++)
-				if(Columns[i]) Columns[i]->Command(cmd, tmpl, o);
-			if(DropLines) for(i = 0; i < nDropLines; i++)
-				if(DropLines[i]) DropLines[i]->Command(cmd, tmpl, o);
-			if(Arrows) for(i = 0; i < nArrows; i++)
-				if(Arrows[i]) Arrows[i]->Command(cmd, tmpl, o);
-			if(Line) Line->Command(cmd, tmpl, o);
-			if(rib) rib->Command(cmd, tmpl, o);
-			}
-		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH &&
-			xBounds.fx <= xBounds.fy && yBounds.fx <= yBounds.fy && zBounds.fx <= zBounds.fy){
-			((Plot*)parent)->CheckBounds3D(xBounds.fx, yBounds.fx, zBounds.fx);
-			((Plot*)parent)->CheckBounds3D(xBounds.fy, yBounds.fy, zBounds.fy);
-			}
-		dirty = false;
-		return true;
-	case CMD_DELOBJ:
-		if(o) o->HideMark();
-		if(!tmpl || !parent) return false;
-		if(rib && rib->Command(cmd, tmpl, o)) return true;
-		if(Balls) for(i = 0; i < nBalls; i++) if(Balls[i] == tmpl) {
-			Undo.DeleteGO((GraphObj**)(&Balls[i]), 0L, o);
-			return parent->Command(CMD_REDRAW, 0L, o);
-			}
-		if(Columns) for(i = 0; i < nColumns; i++) if(Columns[i] == tmpl) {
-			Undo.DeleteGO((GraphObj**)(&Columns[i]), 0L, o);
-			return parent->Command(CMD_REDRAW, 0L, o);
-			}
-		if(DropLines) for(i = 0; i < nDropLines; i++) if(DropLines[i] == tmpl) {
-			Undo.DeleteGO((GraphObj**)(&DropLines[i]), 0L, o);
-			return parent->Command(CMD_REDRAW, 0L, o);
-			}
-		if(Arrows) for(i = 0; i < nArrows; i++) if(Arrows[i] == tmpl) {
-			Undo.DeleteGO((GraphObj**)(&Arrows[i]), 0L, o);
-			return parent->Command(CMD_REDRAW, 0L, o);
-			}
-		if(Line && Line == tmpl) {
-			Undo.DeleteGO((GraphObj**)(&Line), 0L, o);
-			return parent->Command(CMD_REDRAW, 0L, o);
-			}
-		if(rib && rib == tmpl) {
-			Undo.DeleteGO((GraphObj**)(&rib), 0L, o);
-			return parent->Command(CMD_REDRAW, 0L, o);
-			}
-	case CMD_SYM_FILL:
-		if(Balls) for(i= 0; i < nBalls; i++) if(Balls[i])Balls[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_BAR_FILL:
-		if(Columns) for(i= 0; i < nColumns; i++) if(Columns[i])Columns[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_SAVE_SYMBOLS:
-		return SavVarObs((GraphObj **)Balls, nBalls, 0L);
-	case CMD_SAVE_BARS:
-		return SavVarObs((GraphObj **)Columns, nColumns, 0L);
-	case CMD_SAVE_ARROWS:
-		return SavVarObs((GraphObj **)Arrows, nArrows, 0L);
-	case CMD_SAVE_DROPLINES:
-		return SavVarObs((GraphObj **)DropLines, nDropLines, 0L);
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// three dimensional ribbon based on list of Plane3D objects
-Ribbon::Ribbon(GraphObj *par, DataObj *d, double z, double width, char *xr, char *yr)
-	:Plot(par, d)
-{
-	FileIO(INIT_VARS);		Id = GO_RIBBON;		type = 1;
-	if(xr && xr[0]) ssRefX = (char*)memdup(xr, (int)strlen(xr)+1, 0L);
-	if(yr && yr[0]) ssRefY = (char*)memdup(yr, (int)strlen(yr)+1, 0L);
-	z_value = z;	z_width = width;
-}
-
-Ribbon::Ribbon(GraphObj *par, DataObj *d, int which, char *xr, char *yr, char *zr)
-	:Plot(par, d)
-{
-	FileIO(INIT_VARS);		Id = GO_RIBBON;			type = which;
-	if(xr && xr[0]) ssRefX = (char*)memdup(xr, (int)strlen(xr)+1, 0L);
-	if(yr && yr[0]) ssRefY = (char*)memdup(yr, (int)strlen(yr)+1, 0L);
-	if(zr && zr[0]) ssRefZ = (char*)memdup(zr, (int)strlen(zr)+1, 0L);
-	CreateObs();
-}
-
-Ribbon::Ribbon(GraphObj *par, DataObj *d, GraphObj **go, int ngo)
-	:Plot(par, d)
-{
-	int i;
-
-	FileIO(INIT_VARS);		Id = GO_RIBBON;		type = 3;
-	planes = (Plane3D**)go;						nPlanes = ngo;
-	for(i = 0; i < ngo; i++) planes[i]->parent = this;
-}
-
-
-Ribbon::Ribbon(int src):Plot(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-Ribbon::~Ribbon()
-{
-	int i;
-
-	if(ssRefX) free(ssRefX);	if(ssRefY) free(ssRefY);
-	if(ssRefZ) free(ssRefZ);	ssRefX = ssRefY = ssRefZ = 0L;
-	Undo.InvalidGO(this);
-	if(planes) {
-		for(i = 0; i < nPlanes; i++) if(planes[i]) DeleteGO(planes[i]);
-		free(planes);		planes = 0L;
-		}
-	if(values) free(values);		values = 0L;	nVal = 0;
-	if(name) free(name);			name=0L;
-	if(data_desc) free(data_desc);	data_desc = 0L;
-}
-
-double
-Ribbon::GetSize(int select)
-{
-	switch(select) {
-	case SIZE_CELLWIDTH:	return relwidth;
-	case SIZE_ZPOS:			return z_value;
-		}
-	return 0.0;
-}
-
-bool
-Ribbon::SetSize(int select, double value)
-{
-	int i;
-
-	switch(select) {
-	case SIZE_SYM_LINE:
-		if(planes) for (i=0; i < nPlanes; i++)
-			if(planes[i]) planes[i]->SetSize(select, value);
-		return true;
-	case SIZE_CELLWIDTH:
-		if(value != relwidth) {
-			//assume planes saved already by CMD_SAVE_SYMBOLS
-			Undo.ValFloat(this, &relwidth, UNDO_CONTINUE);
-			relwidth = value;
-			if(planes) UpdateObs(false);
-			}
-		return true;
-	case SIZE_ZPOS:
-		if(value != z_value) {
-			//assume planes saved already by CMD_SAVE_SYMBOLS
-			Undo.ValFloat(this, &z_value, UNDO_CONTINUE);
-			z_value = value;
-			if(planes) UpdateObs(false);
-			}
-		return true;
-		}
-	return false;
-}
-
-bool
-Ribbon::SetColor(int select, DWORD col)
-{
-	int i;
-
-	switch(select) {
-	case COL_POLYLINE:
-		Line.color = col;
-	case COL_POLYGON:
-		if(select == COL_POLYGON) Fill.color = col;
-		if(planes) for (i=0; i < nPlanes; i++)
-			if(planes[i]) planes[i]->SetColor(select, col);
-		return true;
-		}
-	return false;
-}
-
-void
-Ribbon::DoPlot(anyOutput *o)
-{
-	int i;
-
-	if(!planes) CreateObs();
-	if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->DoPlot(o);
-	dirty = false;
-}
-
-bool
-Ribbon::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-	MouseEvent *mev;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		if(hidden) return false;
-		mev = (MouseEvent *) tmpl;
-		switch(mev->Action) {
-		case MOUSE_LBUP:
-			if(planes && !CurrGO) for(i = 0; i < nPlanes; i++)
-				if(planes[i]) if(planes[i]->Command(cmd, tmpl, o)) return true;
-			break;
-			}
-		break;
-	case CMD_SCALE:
-		z_value *= ((scaleINFO*)tmpl)->sz.fy;
-		z_width *= ((scaleINFO*)tmpl)->sz.fy;
-		for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->Command(cmd, tmpl, o);
-		break;
-	case CMD_MRK_DIRTY:
-		dirty = true;
-	case CMD_SET_GO3D:		case CMD_SETSCROLL:		case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_SET_DATAOBJ:
-		Id = GO_RIBBON;
-		data = (DataObj *)tmpl;
-		if(planes) for(i = 0; i < nPlanes; i++)
-			if(planes[i]) planes[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_UPDATE:
-		SavVarObs((GraphObj **)planes, nPlanes, UNDO_CONTINUE);
-		Undo.DataMem(this, (void**)&values, nVal * sizeof(fPOINT3D), &nVal, UNDO_CONTINUE);
-		UpdateObs(dirty = true);
-		if(parent) parent->Command(CMD_MRK_DIRTY, tmpl, o);
-		return true;
-	case CMD_AUTOSCALE:
-		if(!planes) CreateObs();
-		if(dirty) {
-			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-			xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
-			xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
-			if(planes) for(i = 0; i < nPlanes; i++)
-				if(planes[i]) planes[i]->Command(cmd, tmpl, o);
-			}
-		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH &&
-			xBounds.fx <= xBounds.fy && yBounds.fx <= yBounds.fy && zBounds.fx <= zBounds.fy){
-			((Plot*)parent)->CheckBounds3D(xBounds.fx, yBounds.fx, zBounds.fx);
-			((Plot*)parent)->CheckBounds3D(xBounds.fy, yBounds.fy, zBounds.fy);
-			}
-		return true;
-	case CMD_DELOBJ:
-		if(!tmpl || !parent) return false;
-		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i] == tmpl) {
-			Undo.DeleteGO((GraphObj**)(&planes[i]), 0L, o);
-			return parent->Command(CMD_REDRAW, 0L, o);
-			}
-		return false;
-	case CMD_SYM_FILL:		case CMD_LEGEND:
-		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->Command(cmd, tmpl, o); 
-		return true;
-	case CMD_SAVE_SYMBOLS:
-		return SavVarObs((GraphObj **)planes, nPlanes, 0L);
-		}
-	return false;
-}
-
-void
-Ribbon::CreateObs()
-{
-	int i, j, n, rx, cx, ry, cy, rz, cz;
-	double fx, fy, fz, tmp;
-	fPOINT3D pg[5];
-	AccRange *rX, *rY, *rZ;
-	Triangle *trl, *trc, *trn;
-
-	if(planes || !data) return;
-	rX = rY = rZ = 0L;
-	switch(type) {
-	case 1:
-		if(!ssRefX || !ssRefY) return;
-		if(z_width == 0.0) z_width = 1.0;	if(relwidth == 0.0) relwidth = 0.6;
-		if((rX = new AccRange(ssRefX)) && (rY = new AccRange(ssRefY))) {
-			if(!data_desc) data_desc = rY->RangeDesc(data, 1);
-			tmp = relwidth*z_width/2.0;
-			if(!(values = (fPOINT3D*)calloc(i = rX->CountItems(), sizeof(fPOINT3D)))){
-				delete rX;	delete rY;	return;
-				}
-			if(!(planes = (Plane3D**)calloc(i-1, sizeof(Plane3D*)))){
-				free(values);	values = 0L;	delete rX;	delete rY;	return;
-				}
-			for(i = j = 0, rX->GetFirst(&cx, &rx), rY->GetFirst(&cy, &ry);
-				rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry); i++) {
-				if(data->GetValue(rx, cx, &fx) && data->GetValue(ry, cy, &fy)) {
-					values[i].fx = fx;	values[i].fy = fy;	values[i].fz = z_value;
-					pg[3].fx = pg[2].fx = fx;	pg[3].fy = pg[2].fy = fy;
-					pg[2].fz = z_value - tmp;	pg[3].fz = z_value +tmp;
-					if(j) {
-						pg[4].fx = pg[0].fx;	pg[4].fy = pg[0].fy; pg[4].fz = pg[0].fz;
-						planes[i-1] = new Plane3D(this, data, pg, 5);
-						if(planes[i-1]) planes[i-1]->Command(CMD_PG_FILL, &Fill, 0L);
-						}
-					j++;
-					pg[0].fx = pg[3].fx;	pg[0].fy = pg[3].fy; pg[0].fz = pg[3].fz;
-					pg[1].fx = pg[2].fx;	pg[1].fy = pg[2].fy; pg[1].fz = pg[2].fz;
-					}
-				}
-			nPlanes = i-1;		nVal = i;
-			}
-		break;
-	case 2:
-		if(!ssRefX || !ssRefY || !ssRefZ) return;
-		if((rX = new AccRange(ssRefX)) && (rY = new AccRange(ssRefY)) && (rZ = new AccRange(ssRefZ))) {
-			if(!(values = (fPOINT3D*)calloc(i = rX->CountItems(), sizeof(fPOINT3D)))){
-				delete rX;	delete rY;	delete rZ;	return;
-				}
-			if(!(planes = (Plane3D**)calloc(i-1, sizeof(Plane3D*)))){
-				free(values);	values = 0L;	delete rX;	delete rY;	delete rZ;	return;
-				}
-			if(!data_desc) data_desc = rY->RangeDesc(data, 1);
-			for(i = 0, rX->GetFirst(&cx, &rx), rY->GetFirst(&cy, &ry), rZ->GetFirst(&cz, &rz);
-				rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry) && rZ->GetNext(&cz, &rz); i++) {
-				if(data->GetValue(rx, cx, &fx) && data->GetValue(ry, cy, &fy) &&
-					data->GetValue(rz, cz, &fz)) {
-					values[i].fx = fx;	values[i].fy = fy;	values[i].fz = fz;
-					pg[3].fx = pg[2].fx = fx;	pg[2].fz = pg[3].fz = fz;		
-					pg[3].fy = 0.0;				pg[2].fy = fy;
-					if(i) {
-						pg[4].fx = pg[0].fx;	pg[4].fy = pg[0].fy; pg[4].fz = pg[0].fz;
-						planes[i-1] = new Plane3D(this, data, pg, 5);
-						if(planes[i-1]) planes[i-1]->Command(CMD_PG_FILL, &Fill, 0L);
-						}
-					pg[0].fx = pg[3].fx;		pg[0].fy = pg[3].fy;	pg[0].fz = pg[3].fz;
-					pg[1].fx = pg[2].fx;		pg[1].fy = pg[2].fy;	pg[1].fz = pg[2].fz;
-					}
-				}
-			nPlanes = i-1;		nVal = i;
-			}
-		break;
-	case 3:
-		if(!ssRefX || !ssRefY || !ssRefZ) break;
-		Undo.InvalidGO(this);
-		trl = Triangulate1(ssRefX, ssRefZ, ssRefY, data);
-		for(i = 0, trc = trl; trc; i++) trc = trc->next;
-		if((n = i) && (planes = (Plane3D**)malloc(n*sizeof(Plane3D*)))) 
-			for(i = nPlanes = 0, trc = trl; trc && i < n; i++) {
-			for(j = 0; j < 4; j++) {	//swap y and z values;
-				tmp = trc->pt[j].fz;	trc->pt[j].fz = trc->pt[j].fy;	trc->pt[j].fy = tmp;
-				}
-			planes[nPlanes++] = new Plane3D(this, data, trc->pt, 4);
-			trn = trc->next;	delete trc;		trc = trn;
-			}
-		dirty = true;			Command(CMD_AUTOSCALE, 0L, 0L);
-		break;
-		}
-	if(rX) delete rX;	if(rY) delete rY;	if(rZ) delete rZ;
-}
-
-void
-Ribbon::UpdateObs(bool bNewData)
-{
-	int i, j, k, rx, cx, ry, cy, rz, cz;
-	double fx, fy, fz, tmp, da1, da2;
-	AccRange *rX, *rY, *rZ;
-	int sel_id[] = {SIZE_XPOS, SIZE_YPOS, SIZE_ZPOS};
-
-	if(!planes || (!values && type < 3)) return;
-	rX = rY = rZ = 0L;
-	switch(type) {
-	case 1:
-		if(!ssRefX || !ssRefY || !data) return;
-		if(z_width == 0.0) z_width = 1.0;	if(relwidth == 0.0) relwidth = 0.6;
-		if((rX = new AccRange(ssRefX)) && (rY = new AccRange(ssRefY))) {
-			tmp = relwidth*z_width/2.0;
-			for(i = 0, rX->GetFirst(&cx, &rx), rY->GetFirst(&cy, &ry);
-				rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry) && i < nVal; i++) {
-				if(data->GetValue(rx, cx, &fx) && data->GetValue(ry, cy, &fy)) {
-					if(bNewData) {
-						values[i].fx = fx;	values[i].fy = fy;	values[i].fz = z_value;
-						}
-					else {
-						fx = values[i].fx;	fy = values[i].fy;	values[i].fz = z_value;
-						}
-					if(i && planes[i-1]) {
-						for(j = 0; j < 3; j++){
-							for(k = 0; k <5; k++) {
-								switch (j) {
-								case 0:	
-									da1 = values[i-1].fx;		da2 = values[i].fx;
-									break;
-								case 1:
-									da1 = values[i-1].fy;		da2 = values[i].fy;
-									break;
-								case 2:
-									if(k != 1 && k != 2) da1 = da2 = (values[i].fz + tmp);
-									else da1 = da2 = (values[i].fz - tmp);
-									break;
-									}
-								planes[i-1]->SetSize(sel_id[j]+k, (k != 2 && k != 3) ? da1 : da2);
-								}
-							}
-						}
-					}
-				}
-			}
-		break;
-	case 2:
-		if(!ssRefX || !ssRefY || !ssRefZ || !data) return;
-		if((rX = new AccRange(ssRefX)) && (rY = new AccRange(ssRefY)) && (rZ = new AccRange(ssRefZ))) {
-			for(i = 0, rX->GetFirst(&cx, &rx), rY->GetFirst(&cy, &ry), rZ->GetFirst(&cz, &rz);
-				rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry) && rZ->GetNext(&cz, &rz) && i < nVal; i++) {
-				if(data->GetValue(rx, cx, &fx) && data->GetValue(ry, cy, &fy) && data->GetValue(rz, cz, &fz)) {
-					values[i].fx = fx;	values[i].fy = fy;	values[i].fz = fz;
-					if(i && planes[i-1]) {
-						planes[i-1]->SetSize(SIZE_XPOS, values[i-1].fx);	planes[i-1]->SetSize(SIZE_XPOS+3, fx);
-						planes[i-1]->SetSize(SIZE_XPOS+1, values[i-1].fx);	planes[i-1]->SetSize(SIZE_XPOS+2, fx);
-						planes[i-1]->SetSize(SIZE_XPOS+4, values[i-1].fx);
-						planes[i-1]->SetSize(SIZE_YPOS+1, values[i-1].fy);	planes[i-1]->SetSize(SIZE_YPOS+2, fy);
-						planes[i-1]->SetSize(SIZE_ZPOS, values[i-1].fz);	planes[i-1]->SetSize(SIZE_ZPOS+3, fz);
-						planes[i-1]->SetSize(SIZE_ZPOS+1, values[i-1].fz);	planes[i-1]->SetSize(SIZE_ZPOS+2, fz);
-						planes[i-1]->SetSize(SIZE_ZPOS+4, values[i-1].fz);
-						}
-					}
-				}
-			}
-		break;
-	case 3:
-		if(planes) {
-			for(i = 0; i < nPlanes; i++) if(planes[i]) DeleteGO(planes[i]);
-			free(planes);		planes = 0L;	nPlanes = 0;
-			}
-		CreateObs();
-		}
-	if(rX) delete rX;	if(rY) delete rY;	if(rZ) delete rZ;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// draw a 3dimensional grid
-Grid3D::Grid3D(GraphObj *par, DataObj *d, int sel, double x1, double xstep, double z1, double zstep)
-	:Plot(par, d)
-{
-	FileIO(INIT_VARS);		Id = GO_GRID3D;
-	start.fx = x1;			step.fx = xstep;
-	start.fz = z1;			step.fz = zstep;
-	type = sel;
-}
-
-Grid3D::Grid3D(int src):Plot(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-Grid3D::~Grid3D()
-{
-	int i;
-
-	Undo.InvalidGO(this);
-	if(lines) {
-		for(i = 0; i < nLines; i++) if(lines[i]) DeleteGO(lines[i]);
-		free(lines);		lines = 0L;
-		}
-	if(planes) {
-		for(i = 0; i < nPlanes; i++) if(planes[i]) DeleteGO(planes[i]);
-		free(planes);		planes = 0L;
-		}
-	nLines = nPlanes = 0;
-	if(name) free(name);	name=0L;
-}
-
-bool 
-Grid3D::SetSize(int select, double value)
-{
-	int i;
-
-	switch (select) {
-	case SIZE_SYM_LINE:
-		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->SetSize(select, value); 
-		return true;
-		}
-	return false;
-}
-
-bool
-Grid3D::SetColor(int select, DWORD col)
-{
-	int i;
-
-	switch (select) {
-	case COL_POLYLINE:
-		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->SetColor(select, col); 
-		return true;
-		}
-	return false;
-}
-
-void
-Grid3D::DoPlot(anyOutput *o)
-{
-	int i;
-
-	if(!lines && !planes) CreateObs(false);
-	if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->DoPlot(o);
-	if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->DoPlot(o);
-	dirty = false;
-}
-
-bool
-Grid3D::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-	MouseEvent *mev;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		if(hidden) return false;
-		mev = (MouseEvent *) tmpl;
-		switch(mev->Action) {
-		case MOUSE_LBUP:
-			if(lines && !CurrGO) for(i = 0; i < nLines; i++)
-				if(lines[i]) if(lines[i]->Command(cmd, tmpl, o)) return true;
-			if(planes && !CurrGO) for(i = 0; i < nPlanes; i++)
-				if(planes[i]) if(planes[i]->Command(cmd, tmpl, o)) return true;
-			break;
-			}
-		break;
-	case CMD_SET_LINE:
-		if(tmpl) {
-			memcpy(&Line, tmpl, sizeof(LineDEF));
-			if(lines) {
-				SavVarObs((GraphObj**)lines, nLines, 0L);
-				for(i = 0; i < nLines; i++)
-					if(lines[i]) lines[i]->Command(cmd, tmpl, o);
-				}
-			if(planes) {
-				SavVarObs((GraphObj**)planes, nPlanes, 0L);
-				for(i = 0; i < nPlanes; i++)
-					if(planes[i]) planes[i]->Command(cmd, tmpl, o);
-				}
-			}
-		break;
-	case CMD_LEGEND:
-		if(!hidden) ((Legend*)tmpl)->HasFill(&Line, planes ? &Fill : 0L, 0L);
-		break;
-	case CMD_CONFIG:
-		return Configure();
-	case CMD_MRK_DIRTY:
-		dirty = true;
-	case CMD_SET_GO3D:		case CMD_SETSCROLL:		case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_SET_DATAOBJ:
-		Id = GO_GRID3D;
-		if(tmpl == data) return true;
-		data = (DataObj *)tmpl;
-	case CMD_UPDATE:
-		if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->Command(cmd, tmpl, o);
-		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->Command(cmd, tmpl, o);
-		return dirty = true;
-	case CMD_AUTOSCALE:
-		if(!lines && !planes) CreateObs(false);
-		if(dirty) {
-			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-			xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
-			xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
-			if(lines) for(i = 0; i < nLines; i++)
-				if(lines[i]) lines[i]->Command(cmd, tmpl, o);
-			if(planes) for(i = 0; i < nPlanes; i++)
-				if(planes[i]) planes[i]->Command(cmd, tmpl, o);
-			}
-		if(zBounds.fx > zBounds.fy) zBounds.fx = zBounds.fy = 0.0;
-		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH &&
-			xBounds.fx <= xBounds.fy && yBounds.fx <= yBounds.fy){
-			((Plot*)parent)->CheckBounds3D(xBounds.fx, yBounds.fx, zBounds.fx);
-			((Plot*)parent)->CheckBounds3D(xBounds.fy, yBounds.fy, zBounds.fy);
-			}
-		return true;
-	case CMD_SYM_FILL:
-		if(!tmpl) return false;
-		memcpy(&Fill, tmpl, sizeof(FillDEF));
-		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->Command(cmd, tmpl, o); 
-		return true;
-	case CMD_SAVE_SYMBOLS:
-		return SavVarObs((GraphObj **)planes, nPlanes, 0L);
-	case CMD_DELOBJ:
-		if(!parent) return false;
-		if(DeleteGOL((GraphObj***)&lines,nLines,(GraphObj*)tmpl,o)) return parent->Command(CMD_REDRAW, 0L, o);
-		if(DeleteGOL((GraphObj***)&planes,nPlanes,(GraphObj*)tmpl,o)) return parent->Command(CMD_REDRAW, 0L, o);
-		break;
-		}
-	return false;
-}
-
-void
-Grid3D::CreateObs(bool set_undo)
-{
-	int i, ir, ic, idx, w, h;
-	fPOINT3D *vec;
-
-	if(!parent || !data || lines || planes) return;
-	dirty = true;
-	if(type == 0) {
-		if(!(vec = (fPOINT3D*)malloc(sizeof(fPOINT3D) * 2))) return;
-		data->GetSize(&w, &h);
-		if(0 >= (nLines = 2 * w * h - w - h)) return;
-		if(!(lines =(Line3D**)calloc(nLines, sizeof(Line3D*)))) return;
-		vec[0].fz = start.fz;			data->GetValue(0, 0, &vec[0].fy);
-		for(ic = 1, idx = 0; ic <= w; ic++) {
-			vec[0].fx = start.fx;
-			data->GetValue(0, ic-1, &vec[0].fy);
-			for(ir = 1; ir <= h; ir++){
-				if(ic < w && data->GetValue(ir-1, ic, &vec[1].fy)) {
-					vec[1].fz = vec[0].fz + step.fz;	vec[1].fx = vec[0].fx;
-					lines[idx++] = new Line3D(this, data, vec, 2, 
-						-1, -1, ic-1, ir-1, -1, -1, -1, -1, ic, ir-1, -1, -1);
-					}
-				if(ir < h && data->GetValue(ir, ic-1, &vec[1].fy)) {
-					vec[1].fz = vec[0].fz;	vec[1].fx = vec[0].fx + step.fx;
-					lines[idx++] = new Line3D(this, data, vec, 2,
-						-1, -1, ic-1, ir-1, -1, -1, -1, -1, ic-1, ir, -1, -1);
-					}
-				vec[0].fx += step.fx;	vec[0].fy = vec[1].fy;
-				}
-			vec[0].fz += step.fz;
-			}
-		for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->Command(CMD_SET_LINE, &Line, 0L);
-		free(vec);
-		}
-	else if(type == 1) {
-		if(!(vec = (fPOINT3D*)malloc(sizeof(fPOINT3D) * 5))) return;
-		vec[0].fz = vec[4].fz = start.fz;
-		vec[3].fz = (start.fz +step.fz);
-		data->GetSize(&w, &h);
-		if(0 >= (nPlanes = w * h)) return;
-		if(!(planes =(Plane3D**)calloc(nPlanes, sizeof(Plane3D*)))) return;
-		for(ic = 1, idx = 0; ic <= w; ic++) {
-			vec[0].fx = vec[3].fx = vec[4].fx = (start.fx+step.fx);
-			vec[1].fx = vec[2].fx = start.fx;
-			vec[1].fz = vec[4].fz;	vec[2].fz = vec[3].fz;
-			data->GetValue(0, ic-1, &vec[1].fy);	data->GetValue(0, ic, &vec[2].fy);
-			for(ir = 1; ir <= h; ir++){
-				if(ic < w && ir < h && data->GetValue(ir, ic, &vec[3].fy) 
-					&& data->GetValue(ir, ic-1, &vec[4].fy)) {
-					vec[0].fz = vec[4].fz;	vec[0].fy = vec[4].fy;	vec[0].fx = vec[4].fx;
-					planes[idx++] = new Plane3D(this, 0L, vec, 5);
-					}
-				vec[1].fz = vec[4].fz;		vec[1].fy = vec[4].fy;		vec[1].fx = vec[4].fx;
-				vec[2].fz = vec[3].fz;		vec[2].fy = vec[3].fy;		vec[2].fx = vec[3].fx;
-				vec[3].fx += step.fx;		vec[4].fx += step.fx;
-				}
-			vec[3].fz += step.fz;			vec[4].fz += step.fz;
-			}
-		nPlanes = idx;
-		for(i = 0; i < nPlanes; i++) if(planes[i]){
-			planes[i]->Command(CMD_SET_LINE, &Line, 0L);
-			planes[i]->Command(CMD_SYM_FILL, &Fill, 0L);
-			}
-		SetSize(SIZE_SYM_LINE, Line.width);		SetColor(COL_POLYLINE, Line.color);
-		free(vec);
-		}
-	if(set_undo) {
-		if(planes && nPlanes)Undo.StoreListGO(parent, (GraphObj***)&planes, &nPlanes, UNDO_CONTINUE);
-		if(lines && nLines)Undo.StoreListGO(parent, (GraphObj***)&lines, &nLines, UNDO_CONTINUE);
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// define minima and maxima rectangle to be used by graph
-Limits::Limits(int src):Plot(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-Limits::~Limits()
-{
-	if(name) free(name);		name=0L;
-}
-
-double
-Limits::GetSize(int select)
-{
-	return 0.0;
-}
-
-bool
-Limits::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch (cmd) {
-	case CMD_SET_DATAOBJ:
-		Id = GO_LIMITS;
-		data = (DataObj *)tmpl;	
-		return true;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Calculate and display a user defined function
-Function::Function(GraphObj *par, DataObj *d, char *desc):Plot(par, d)
-{
-	FileIO(INIT_VARS);		cmdxy = (char*)malloc(20*sizeof(char));
-	if(parent && parent->Id == GO_POLARPLOT) {
-		x1 = 0.0;			x2 = 360.0;			xstep = 0.5;
-		if(cmdxy)rlp_strcpy(cmdxy, 20, (char*)"sin(pi*x/30)+1.1");
-		}
-	else {
-		x1 = 0.0;			x2 = 100.0;			xstep = 0.5;
-		if(cmdxy)rlp_strcpy(cmdxy, 20, (char*)"sin(x)/x");
-		}
-	if(desc) name = (char*)memdup(desc, (int)strlen(desc)+1, 0);
-	Id = GO_FUNCTION;
-}
-
-Function::Function(int src):Plot(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-Function::~Function()
-{
-	if(cmdxy) free(cmdxy);		cmdxy = 0L;
-	if(param) free(param);		param = 0L;
-	if(dl) DeleteGO(dl);		dl = 0L;
-	if(name) free(name);		name=0L;
-}
-
-bool
-Function::SetSize(int select, double value)
-{
-	switch(select & 0xfff){
-	case SIZE_MIN_X:	x1 = value;		return true;
-	case SIZE_MAX_X:	x2 = value;		return true;
-	case SIZE_XSTEP:	xstep=value;	return true;
-		}
-	return false;
-}
-
-void
-Function::DoPlot(anyOutput *o)
-{
-	if((!dl || dirty) && cmdxy && cmdxy[0]) Update(o, 0);
-	dirty = false;
-	if(dl && o) {
-		dl->Command(CMD_SET_LINE, &Line, o);
-		dl->DoPlot(o);
-		}
-}
-
-bool
-Function::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch (cmd) {
-	case CMD_LEGEND:	case CMD_MOUSE_EVENT:
-		if(hidden) return false;
-		if(dl) return dl->Command(cmd, tmpl, o);
-		break;
-	case CMD_SCALE:
-		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
-		return true;
-	case CMD_DELOBJ:
-		if(parent && tmpl && tmpl == dl) return parent->Command(CMD_DELOBJ, this, o);
-		break;
-	case CMD_MRK_DIRTY:
-		if(parent) parent->Command(cmd, tmpl, o);
-		return dirty = true;
-	case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_SET_LINE:
-		if(tmpl) memcpy(&Line, tmpl, sizeof(LineDEF));
-		break;
-	case CMD_SET_DATAOBJ:
-		if(dl) dl->Command(cmd, tmpl, o);
-		Id = GO_FUNCTION;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_SETPARAM:
-		if(tmpl) {
-			if(param) free(param);			param = 0L;
-			if(*((char*)tmpl))param = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0);
-			}
-		dirty = true;
-		return true;
-	case CMD_SETFUNC:
-		if(tmpl) {
-			if(cmdxy) free(cmdxy);			cmdxy = 0L;
-			if(*((char*)tmpl))cmdxy = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0);
-			}
-		dirty = true;
-		return true;
-	case CMD_UPDATE:
-		return Update(o, UNDO_CONTINUE);
-	case CMD_AUTOSCALE:
-		if(!dl) return Update(o, 0L);
-		if(dirty) {
-			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
-			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-			xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
-			xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
-			if(dl) dl->Command(cmd, tmpl, o);
-			dirty = false;
-			}
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH
-			&& Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin) {
-			((Plot*)parent)->CheckBounds(Bounds.Xmin, Bounds.Ymin);
-			((Plot*)parent)->CheckBounds(Bounds.Xmax, Bounds.Ymax);
-			}
-		return true;
-		}
-	return false;
-}
-
-bool
-Function::Update(anyOutput *o, DWORD flags)
-{
-	lfPOINT *xydata;
-	long ndata;
-
-	if(!parent || !cmdxy) return false;
-	LockData(false, false);
-	do_xyfunc(data, x1, x2, xstep, cmdxy, &xydata, &ndata, param);
-	LockData(false, false);
-	if(xydata && ndata >1) {
-		if(!dl) dl = new DataLine(this, data, xydata, ndata, name);
-		else dl->LineData(xydata, ndata);
-		dirty = true;
-		Command(CMD_AUTOSCALE, 0L, 0L);
-		return true;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Calculate and display a user defined function
-static char *lastFunc2D = 0L, *lastParam2D=0L;
-FitFunc::FitFunc(GraphObj *par, DataObj *d):Plot(par, d)
-{
-	FileIO(INIT_VARS);
-	x1 = 0.0;			x2 = 100.0;					xstep = 0.5;		dl = 0L;
-	if(lastFunc2D && lastFunc2D[0] && lastParam2D && lastParam2D[0]) {
-		cmdxy = (char*)memdup(lastFunc2D, (int)strlen(lastFunc2D)+1, 0);
-		parxy = (char*)memdup(lastParam2D, (int)strlen(lastParam2D)+1, 0);
-		}
-	if(!cmdxy || !parxy) {
-		cmdxy = (char*)malloc(20*sizeof(char));		parxy = (char*)malloc(20*sizeof(char));
-		if(cmdxy) rlp_strcpy(cmdxy, 20, "a+b*x^c");
-		if(parxy) rlp_strcpy(parxy, 20, "a=1; b=1; c=0.1;");
-		}
-	Id = GO_FITFUNC;
-}
-
-
-FitFunc::FitFunc(int src):Plot(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-FitFunc::~FitFunc()
-{
-	int i;
-
-	if(Symbols) {
-		for(i = 0; i< nPoints; i++) if(Symbols[i]) DeleteGO(Symbols[i]);
-		free(Symbols);
-		}
-	if(cmdxy) free(cmdxy);		cmdxy = 0L;
-	if(parxy) free(parxy);		parxy = 0L;
-	if(ssXref) free(ssXref);	ssXref = 0L;
-	if(ssYref) free(ssYref);	ssYref = 0L;
-	if(dl) DeleteGO(dl);		dl = 0L;
-	if(name) free(name);		name=0L;
-	Undo.InvalidGO(this);
-}
-
-bool
-FitFunc::SetSize(int select, double value)
-{
-	int i;
-
-	switch(select & 0xfff){
-	case SIZE_SYMBOL:
-	case SIZE_SYM_LINE:
-		if(Symbols)	for(i = 0; i < nPoints; i++) 
-			if(Symbols[i]) Symbols[i]->SetSize(select, value);
-		return true;
-		}
-	return false;
-}
-
-bool
-FitFunc::SetColor(int select, DWORD col)
-{
-	int i;
-
-	switch(select) {
-	case COL_SYM_LINE:
-	case COL_SYM_FILL:
-		if(Symbols) for(i = 0; i < nPoints; i++)
-			if(Symbols[i]) Symbols[i]->SetColor(select, col);
-		return true;
-		}
-	return false;
-}
-
-void
-FitFunc::DoPlot(anyOutput *o)
-{
-	int i;
-
-	if(!data || x1 >= x2) return;
-	dirty = false;
-	if(!dl && (dl = new Function(this, data, "Fitted function"))) {
-		dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
-		dl->SetSize(SIZE_XSTEP, xstep);			dl->Command(CMD_SETFUNC, cmdxy, 0L);
-		dl->Command(CMD_SETPARAM, parxy, 0L);	dl->Command(CMD_SET_LINE, &Line, 0L);
-		dl->Update(o, UNDO_CONTINUE);
-		}
-	if(dl && o) {
-		dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
-		dl->SetSize(SIZE_XSTEP, xstep);			dl->Command(CMD_SETFUNC, cmdxy, 0L);
-		dl->Command(CMD_SETPARAM, parxy, 0L);	dl->Command(CMD_SET_LINE, &Line, 0L);
-		dl->DoPlot(o);
-		}
-	if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->DoPlot(o);
-}
-
-bool 
-FitFunc::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-	MouseEvent *mev;
-	LineDEF *ld;
-
-	switch(cmd) {
-	case CMD_LEGEND:
-		if(tmpl && ((GraphObj*)tmpl)->Id == GO_LEGEND && dl && dl->Id == GO_FUNCTION) {
-			ld = dl->GetLine();
-			if(Symbols) {
-				for (i = 0; i < nPoints && i < 100; i++)
-					if(Symbols[i]) ((Legend*)tmpl)->HasSym(ld, Symbols[i], "Fitted function");
-				}
-			else ((Legend*)tmpl)->HasFill(ld, 0L, dl->name);
-			return true;
-			}
-		return false;
-	case CMD_ENDDIALOG:
-		if(!cmdxy || !parxy) return false;
-		if(i = (int)strlen(cmdxy)) {
-			if(lastFunc2D = (char*)realloc(lastFunc2D, i+2))
-				rlp_strcpy(lastFunc2D, i+1, cmdxy);
-			}
-		if(i = (int)strlen(parxy)) {
-			if(lastParam2D = (char*)realloc(lastParam2D, i+2))
-				rlp_strcpy(lastParam2D, i+1, parxy);
-			}
-		return true;
-	case CMD_SCALE:
-		if(dl) return dl->Command(cmd, tmpl, o);
-		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
-		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
-		return true;
-	case CMD_MOUSE_EVENT:
-		if(hidden) return false;
-		mev = (MouseEvent *) tmpl;
-		switch(mev->Action) {
-		case MOUSE_LBUP:
-			//select objects invers to plot order
-			if(Symbols && !CurrGO) for(i = nPoints-1; i >=0; i--)
-				if(Symbols[i] && Symbols[i]->Command(cmd, tmpl, o))return true;
-			break;
-			}
-		if(dl) return dl->Command(cmd, tmpl, o);
-		return false;
-	case CMD_AUTOSCALE:
-		if(dirty) {
-			if(!dl && (dl = new Function(this, data, "Fitted function"))) {
-				dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
-				dl->SetSize(SIZE_XSTEP, xstep);			dl->Command(CMD_SETFUNC, cmdxy, 0L);
-				dl->Command(CMD_SETPARAM, parxy, 0L);	dl->Command(CMD_SET_LINE, &Line, 0L);
-				dl->Update(o, UNDO_CONTINUE);
-				}
-			if(dl) {
-				dl->Command(cmd, tmpl, o);
-				memcpy(&Bounds, &dl->Bounds, sizeof(fRECT));
-				}
-			if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i])Symbols[i]->Command(cmd, tmpl, o);
-			dirty = false;
-			}
-		return true;
-	case CMD_UPDATE:
-		if(Symbols) {
-			SavVarObs((GraphObj**)Symbols, nPoints, UNDO_CONTINUE);
-			for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
-			}
-		Undo.String(this, &parxy, UNDO_CONTINUE);
-		do_fitfunc(data, ssXref, ssYref, 0L, &parxy, cmdxy, conv, maxiter, &chi2);
-		if(!dl) dl = new Function(this, data, "Fitted function");
-		if(dl){
-			dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
-			dl->SetSize(SIZE_XSTEP, xstep);			dl->Command(CMD_SETFUNC, cmdxy, 0L);
-			dl->Command(CMD_SETPARAM, parxy, 0L);	dl->Command(CMD_SET_LINE, &Line, 0L);
-			dl->Update(o, UNDO_CONTINUE);
-			}
-		dirty = true;
-		if(parent) parent->Command(CMD_MRK_DIRTY, 0L, o);
-		return true;
-	case CMD_DELOBJ:
-		if(!parent) return false;
-		if(tmpl && tmpl == dl) return parent->Command(CMD_DELOBJ, this, o);
-		else if(DeleteGOL((GraphObj***)&Symbols,nPoints,(GraphObj*)tmpl,o)) return parent->Command(CMD_REDRAW,0L,o);
-		else if(dl) return dl->Command(cmd, tmpl, o);
-		return false;
-	case CMD_MRK_DIRTY:
-		dirty = true;
-		if(dl){
-			dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
-			dl->SetSize(SIZE_XSTEP, xstep);
-			}
-	case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_SET_DATAOBJ:
-		if(dl) dl->Command(cmd, tmpl, o);
-		if(Symbols) for(i = 0; i < nPoints; i++)
-			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
-		Id = GO_FITFUNC;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_SYMTEXT:		case CMD_SYMTEXT_UNDO:	case CMD_SYM_RANGETEXT:
-	case CMD_SYMTEXTDEF:	case CMD_SYM_TYPE:
-		if(Symbols) for(i = 0; i < nPoints; i++)
-			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_SAVE_SYMBOLS:
-		return SavVarObs((GraphObj **)Symbols, nPoints, 0L);
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// normal quantile plot and derivates
-NormQuant::NormQuant(GraphObj *par, DataObj *d, char* range)
-	:Plot(par, d)
-{
-	FileIO(INIT_VARS);
-	if(range && range[0]) ssRef = (char*)memdup(range, (int)strlen(range)+1, 0);
-	else ssRef = 0L;
-	Id = GO_NORMQUANT;
-}
-
-NormQuant::NormQuant(GraphObj *par, DataObj *d, double *val, int nval)
-	:Plot(par, d)
-{
-	FileIO(INIT_VARS);		ssRef = 0L;
-	if(val && nval) {
-		src_data = (double*)memdup(val, nval*sizeof(double), 0);
-		SortArray(nData = nval, src_data);		ProcessData();
-		}
-	Id = GO_NORMQUANT;
-}
-
-NormQuant::NormQuant(int src):Plot(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		if(nData)SortArray(nData, src_data);	ProcessData();
-		}
-}
-
-NormQuant::~NormQuant()
-{
-	if(ssRef) free(ssRef);		ssRef = 0L;
-	if(x_info) free(x_info);	x_info = 0L;
-	if(y_info) free(y_info);	y_info = 0L;
-	if(x_vals) free(x_vals);	x_vals = 0L;
-	if(y_vals) free(y_vals);	y_vals = 0L;
-	if(src_data)free(src_data);	src_data = 0L;
-	if(sy)delete(sy);
-}
-
-void
-NormQuant::DoPlot(anyOutput *o)
-{
-	int i;
-
-	//draw symbols
-	if(sy && y_vals && src_data && y_vals && nValidData) {
-		sy->SetSize(SIZE_SYMBOL, defs.GetSize(SIZE_SYMBOL)/10.0);
-		for(i = 0; i < nValidData; i++) {
-			sy->SetSize(SIZE_XPOS, x_vals[i]);
-			sy->SetSize(SIZE_YPOS, y_vals[i]);
-			sy->DoPlot(o);
-			}
-		}
-}
-
-bool
-NormQuant::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch(cmd) {
-	case CMD_LEGEND:
-		if(sy) ((Legend*)tmpl)->HasSym(0L, sy, x_info ? x_info : (char*)"Data");
-		return true;
-	case CMD_SCALE:
-		return true;
-	case CMD_UPDATE:
-		return true;
-	case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_SET_DATAOBJ:
-		Id = GO_NORMQUANT;
-		if(sy) sy->Command(cmd, tmpl, o);
-		data = (DataObj *)tmpl;
-		return true;
-		}
-	return false;
-}
-
-bool
-NormQuant::ProcessData()
-{
-	int i, r, c, n;
-	AccRange *rD;
-	double y, dtmp, sum;
-
-	if(data && ssRef && ssRef[0] && (rD = new AccRange(ssRef))) {
-		if((n = rD->CountItems()) && (src_data = (double*)realloc(src_data, n * sizeof(double)))){
-			for(nData = 0, rD->GetFirst(&c, &r); rD->GetNext(&c, &r); ) {
-				if(data->GetValue(r, c, &dtmp)) src_data[nData++] = dtmp;
-				}
-			if(nData)SortArray(nData, src_data);
-			}
-		if(y_info = (char*)malloc(20)){
-			rlp_strcpy(y_info, 20, "Normal quantiles");
-			}
-		x_info = rD->RangeDesc(data, 2);
-		delete rD;
-		}
-	if(src_data && nData) {
-		Bounds.Ymin = HUGE_VAL;			Bounds.Ymax = -HUGE_VAL;
-		x_vals = (double*)realloc(x_vals, nData * sizeof(double));
-		y_vals = (double*)realloc(y_vals, nData * sizeof(double));
-		for(n = i = 0, sum = dtmp = 1.0/((double)nData); i < (nData-1); i++ ) {
-			y = distinv(norm_dist, 0.0, 1.0, sum, 0.5);
-			if(y > -HUGE_VAL && y < HUGE_VAL) {
-				y_vals[n] = y;			x_vals[n] = src_data[i];
-				if(y < Bounds.Ymin) Bounds.Ymin = y;
-				if(y > Bounds.Ymax) Bounds.Ymax = y;
-				n++;
-				}
-			sum += dtmp;
-			}
-		Bounds.Xmax = src_data[nData-1];	Bounds.Xmin = src_data[0];
-		if(Bounds.Ymax <= Bounds.Ymin) {
-			Bounds.Ymin = -5.0;		Bounds.Ymax = 5.0;
-			}
-		nValidData = n;
-		return (n > 3);
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// three dimensional graph
-Plot3D::Plot3D(GraphObj *par, DataObj *d, DWORD flags):Plot(par, d)
-{
-	RotDef = (double*)malloc(6 *sizeof(double));
-	FileIO(INIT_VARS);
-	Id = GO_PLOT3D;
-	crea_flags = flags;
-	xBounds.fx = yBounds.fx = zBounds.fx = Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
-	xBounds.fy = yBounds.fy = zBounds.fy = Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-}
-
-Plot3D::Plot3D(int src):Plot(0L, 0L)
-{
-	int i;
-
-	RotDef = (double*)malloc(6 *sizeof(double));
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		//now set parent in all children
-		if(Axes) for(i = 0; i < nAxes; i++)
-			if(Axes[i]) Axes[i]->parent = this;
-		if(plots) for(i = 0; i < nPlots; i++)
-			if(plots[i]) plots[i]->parent = this;
-		}
-}
-
-Plot3D::~Plot3D()
-{
-	int i;
-
-	if(plots) {
-		for(i = 0; i < nPlots; i++) if(plots[i]) DeleteGO(plots[i]);
-		free(plots);
-		}
-	if(Axes) {
-		for(i = 0; i < nAxes; i++) if(Axes[i]) DeleteGO(Axes[i]);
-		free(Axes);
-		}
-	plots = 0L;		nPlots = nAxes = 0;
-	if(nscp > 0 && nscp <= nPlots && Sc_Plots) free(Sc_Plots);
-	nscp = 0;			Sc_Plots = 0L;
-	if(drag) DeleteGO(drag);	drag = 0L;
-	if(dispObs) free(dispObs);	dispObs = 0L;
-	free(RotDef);
-	if(name) free(name);		name=0L;
-	Undo.InvalidGO(this);
-}
-
-double
-Plot3D::GetSize(int select)
-{
-	AxisDEF *ax;
-
-	switch(select){
-	//The Bounds values must be returned by every plot:
-	//   they are necessary for scaling !
-	case SIZE_BOUNDS_XMIN:
-		if(Axes && nAxes >2 && Axes[0] && (ax = Axes[0]->GetAxis()))
-			return (ax->flags & AXIS_INVERT) ? ax->max : ax->min;
-		return 0.0;
-	case SIZE_BOUNDS_XMAX:
-		if(Axes && nAxes >2 && Axes[0] && (ax = Axes[0]->GetAxis()))
-			return (ax->flags & AXIS_INVERT) ? ax->min : ax->max;
-		return 0.0;
-	case SIZE_BOUNDS_YMIN:
-		if(Axes && nAxes >2 && Axes[1] && (ax = Axes[1]->GetAxis())) 
-			return (ax->flags & AXIS_INVERT) ? ax->max : ax->min;
-		return 0.0;
-	case SIZE_BOUNDS_YMAX:
-		if(Axes && nAxes >2 && Axes[1] && (ax = Axes[1]->GetAxis())) 
-			return (ax->flags & AXIS_INVERT) ? ax->min : ax->max;
-		return 0.0;
-	case SIZE_BOUNDS_ZMIN:
-		if(Axes && nAxes >2 && Axes[2] && (ax = Axes[2]->GetAxis())) 
-			return (ax->flags & AXIS_INVERT) ? ax->max : ax->min;
-		return 0.0;
-	case SIZE_BOUNDS_ZMAX:
-		if(Axes && nAxes >2 && Axes[2] && (ax = Axes[2]->GetAxis())) 
-			return (ax->flags & AXIS_INVERT) ? ax->min : ax->max;
-		return 0.0;
-	case SIZE_XPOS:		case SIZE_XPOS+4:		return cu1.fx;
-	case SIZE_XPOS+1:	case SIZE_XPOS+5:		return cu2.fx;
-	case SIZE_XPOS+2:	case SIZE_XPOS+6:		return cu2.fx;
-	case SIZE_XPOS+3:	case SIZE_XPOS+7:		return cu1.fx;
-	case SIZE_YPOS:		case SIZE_YPOS+1:		case SIZE_YPOS+2:
-	case SIZE_YPOS+3:			return cu1.fy;
-	case SIZE_YPOS+4:	case SIZE_YPOS+5:		case SIZE_YPOS+6:
-	case SIZE_YPOS+7:			return cu2.fy;
-	case SIZE_ZPOS:		case SIZE_ZPOS+1:		case SIZE_ZPOS+4:
-	case SIZE_ZPOS+5:			return cu1.fz;
-	case SIZE_ZPOS+2:	case SIZE_ZPOS+3:		case SIZE_ZPOS+6:
-	case SIZE_ZPOS+7:			return cu2.fz;
-	case SIZE_XCENTER:	return rotC.fx;
-	case SIZE_YCENTER:	return rotC.fy;
-	case SIZE_ZCENTER:	return rotC.fz;
-	default:
-		return DefSize(select);
-		}
-}
-
-bool
-Plot3D::SetColor(int select, DWORD col)
-{
-	int i;
-
-	switch(select & 0xfff) {
-	case COL_AXIS:
-		if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->SetColor(select, col);
-		return true;
-		}
-	return false;
-}
-
-void
-Plot3D::DoPlot(anyOutput *o)
-{
-	long i, j, fo;
-
-	nObs = 0;
-	if(!parent || !o) return;
-	if(nscp > 0 && nscp <= nPlots && Sc_Plots) free(Sc_Plots);
-	nscp = 0;			Sc_Plots = 0L;		o->MouseCursor(MC_WAIT, true);
-	if(dirty) DoAutoscale();
-	if(Axes && nAxes >2) {		//if no axes then parent is another Plot3D ...
-		o->LightSource(32.0, 16.0);						CurrAxes = Axes;
-		cu1.fx = cub1.fx;		cu1.fy = cub1.fy;		cu1.fz = cub1.fz;
-		cu2.fx = cub2.fx;		cu2.fy = cub2.fy;		cu2.fz = cub2.fz;
-		rc.fx = rotC.fx;		rc.fy = rotC.fy;		rc.fz = rotC.fz;
-		o->SetSpace(&cu1, &cu2, defs.cUnits, RotDef, &rc, Axes[0]->GetAxis(),
-			Axes[1]->GetAxis(), Axes[2]->GetAxis());
-		if(nAxes >3) {									//DEBUG
-			nAxes = nAxes;
-			}
-		for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->DoPlot(o);
-		}
-	else if(IsPlot3D(parent)) {
-		if (use_xaxis || use_yaxis || use_zaxis)ApplyAxes(o);
-		parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
-		}
-	else {
-		CurrAxes = 0L;
-		}
-	if(plots) for(i = 0; i < nPlots; i++) if(plots[i]){
-		if(plots[i]->Id >= GO_PLOT && plots[i]->Id < GO_GRAPH) {
-			if(((Plot*)plots[i])->hidden == 0) plots[i]->DoPlot(o);
-			}
-		else plots[i]->DoPlot(o);
-		if(i) {
-			UpdateMinMaxRect(&rDims, plots[i]->rDims.right, plots[i]->rDims.top);
-			UpdateMinMaxRect(&rDims, plots[i]->rDims.left, plots[i]->rDims.bottom);
-			}
-		else memcpy(&rDims, &plots[i]->rDims, sizeof(RECT));
-			}
-	for(i = 0; i< nAxes; i++) if(Axes[i]){
-		UpdateMinMaxRect(&rDims, Axes[i]->rDims.right, Axes[i]->rDims.top);
-		UpdateMinMaxRect(&rDims, Axes[i]->rDims.left, Axes[i]->rDims.bottom);
-		}
-	for(i = j = 1; i < nObs; i++) if(dispObs[i] && dispObs[i]->go) 
-		dispObs[j++] = dispObs[i];
-	nObs = j;
-	if(nObs >1  && dispObs){
-		SortObj();
-		for (i = fo = 1; i <= nObs; i++){
-			while(dispObs[fo]->Zmax < dispObs[i]->Zmin && fo < (nObs-1)) fo++;
-			for(j = fo; j <= nObs; j++) {
-				if(dispObs[j]->go->Id != GO_LINESEG && i != j) switch(dispObs[i]->go->Id) {
-				case GO_LINESEG:
-				case GO_SPHERE:
-				case GO_PLANE:
-					dispObs[i]->go->Command(CMD_CLIP, dispObs[j]->go, o);
-					break;
-					}
-				//the following line, if included, reduces time but clipping 
-				//   is not complete because of CMD_DRAW_LATER
-//				if(dispObs[j]->Zmin > dispObs[i]->Zmax) break;
-				}
-			}
-		for (i = 1; i <= nObs; i++)	dispObs[i]->go->Command(CMD_REDRAW, 0L, o);
-		}
-	if(IsPlot3D(parent) && (use_xaxis || use_yaxis || use_zaxis))parent->Command(CMD_AXIS, 0L, o);
-	dirty = false;
-	o->MouseCursor(MC_ARROW, true);
-}
-
-void
-Plot3D::DoMark(anyOutput *o, bool mark)
-{
-	RECT upd;
-
-	if(!drag) drag = new Drag3D(this);
-	if(mark && drag) drag->DoPlot(o);
-	else {
-		memcpy(&upd, &rDims, sizeof(RECT));
-		IncrementMinMaxRect(&upd, 6);
-		o->UpdateRect(&upd, false);
-		}
-}
-
-bool
-Plot3D::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-	GraphObj **tmpPlots;
-
-	switch (cmd) {
-	case CMD_SCALE:
-		cub1.fx *= ((scaleINFO*)tmpl)->sx.fy;		cub1.fy *= ((scaleINFO*)tmpl)->sy.fy;
-		cub1.fz *= ((scaleINFO*)tmpl)->sz.fy;		cub2.fx *= ((scaleINFO*)tmpl)->sx.fy;
-		cub2.fy *= ((scaleINFO*)tmpl)->sy.fy;		cub2.fz *= ((scaleINFO*)tmpl)->sz.fy;
-		rotC.fx *= ((scaleINFO*)tmpl)->sx.fy;		rotC.fy *= ((scaleINFO*)tmpl)->sy.fy;
-		rotC.fz *= ((scaleINFO*)tmpl)->sz.fy;
-		if(plots) for(i = 0; i < nPlots; i++)
-			if(plots[i]) plots[i]->Command(cmd, tmpl, o);
-		if(Axes) for(i = 0; i < nAxes; i++)
-			if(Axes[i]) Axes[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_MOUSE_EVENT:
-		if(hidden || ((MouseEvent*)tmpl)->Action != MOUSE_LBUP || CurrGO) return false;
-		if(dispObs) for (i = nObs; i > 0; i--)	{
-			if(dispObs[i]) dispObs[i]->go->Command(cmd, tmpl, o);
-			if(CurrGO) return true;
-			}
-		if(IsInRect(&rDims, ((MouseEvent*)tmpl)->x, ((MouseEvent*)tmpl)->y)) {
-			o->ShowMark(CurrGO = this, MRK_GODRAW);
-			return true;
-			}
-		break;
-	case CMD_USEAXIS:
-		if(IsPlot3D(parent)) return UseAxis(*((int*)tmpl));
-		break;
-	case CMD_REG_AXISPLOT:	//notification: plot can handle its own axes
-		if(nscp > 0 && nscp <= nPlots && Sc_Plots)  {
-			for(i = 0; i < nscp; i++)
-				if(Sc_Plots[i] == (GraphObj*)tmpl) return true;
-			if(tmpPlots = (GraphObj**)realloc(Sc_Plots, (nscp+1)*sizeof(GraphObj*))){
-				tmpPlots[nscp++] = (GraphObj *)tmpl;
-				Sc_Plots = tmpPlots;
-				}
-			else {		//memory allocation error
-				nscp = 0;
-				Sc_Plots = 0L;
-				}
-			}
-		else {
-			if(Sc_Plots = (GraphObj **)calloc(1, sizeof(GraphObj*))){
-				Sc_Plots[0] = (GraphObj *)tmpl;
-				nscp = 1;
-				}
-			else nscp = 0;
-			}
-		return true;
-	case CMD_AXIS:			//one of the plots has changed scaling: reset
-		CurrAxes = Axes;
-		if(o) o->SetSpace(&cu1, &cu2, defs.cUnits, RotDef, &rc, Axes[0]->GetAxis(),
-			Axes[1]->GetAxis(), Axes[2]->GetAxis());
-		return true;
-	case CMD_OBJTREE:
-		if(!tmpl || !plots) return false;
-		for(i = 0; i < nPlots; i++) if(plots[i]) {
-			((ObjTree*)tmpl)->Command(CMD_UPDATE, plots[i], 0L);
-			if(plots[i]->Id > GO_PLOT && plots[i]->Id < GO_GRAPH) plots[i]->Command(cmd, tmpl, o);
-			}
-		return true;
-	case CMD_REPL_GO:
-		if(!(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
-		if(plots) for(i = 0; i < nPlots; i++) if(plots[i] && plots[i] == tmpPlots[0]){
-			return dirty = ReplaceGO((GraphObj**)&plots[i], tmpPlots);
-			}
-		return false;
-	case CMD_MRK_DIRTY:
-		if(IsPlot3D(parent)) return parent->Command(cmd, tmpl, o);
-		return dirty = true;
-	case CMD_ADDAXIS:
-		if(AddAxis()){
-			if(parent) return parent->Command(CMD_REDRAW, tmpl, o);
-			}
-		return false;
-	case CMD_SET_GO3D:
-		if(IsPlot3D(parent)) return parent->Command(CMD_REDRAW, 0L, o);
-		return AcceptObj((GraphObj *)tmpl);
-	case CMD_SETSCROLL:				case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_SHIFTLEFT:
-		return Rotate(-0.017452406, 0.0, 0.0, o, true);
-	case CMD_SHIFTRIGHT:
-		return Rotate(0.017452406, 0.0, 0.0, o, true);
-	case CMD_SHIFTUP:
-		return Rotate(0.0, 0.017452406, 0.0, o, true);
-	case CMD_SHIFTDOWN:
-		return Rotate(0.0, -0.017452406, 0.0, o, true);
-	case CMD_CURRIGHT:
-		return Rotate(0.087155742, 0.0, 0.0, o, true);
-	case CMD_CURRLEFT:
-		return Rotate(-0.087155742, 0.0, 0.0, o, true);
-	case CMD_CURRUP:
-		return Rotate(0.0, 0.087155742, 0.0, o, true);
-	case CMD_CURRDOWN:
-		return Rotate(0.0, -0.087155742, 0.0, o, true);
-	case CMD_ADDCHAR:
-		if(tmpl && *((int*)tmpl) == 'r') return Rotate(0.0, 0.0, 0.087155742, o, true);
-		if(tmpl && *((int*)tmpl) == 'l') return Rotate(0.0, 0.0, -0.087155742, o, true);
-		if(tmpl && *((int*)tmpl) == 'R') return Rotate(0.0, 0.0, 0.017452406, o, true);
-		if(tmpl && *((int*)tmpl) == 'L') return Rotate(0.0, 0.0, -0.017452406, o, true);
-		return false;
-	case CMD_LEGEND:
-		if(plots) for(i = 0; i < nPlots; i++)
-			if(plots[i]) plots[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = GO_PLOT3D;
-		data = (DataObj *)tmpl;
-	case CMD_UPDATE:
-		if(plots) for(i = 0; i < nPlots; i++)
-			if(plots[i]) plots[i]->Command(cmd, tmpl, o);
-		if(Axes) for(i = 0; i < nAxes; i++)
-			if(Axes[i]) Axes[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_DELOBJ:
-		if(DeleteGOL((GraphObj***)&plots,nPlots,(GraphObj*)tmpl,o)) return parent->Command(CMD_REDRAW,0L,o);
-		return false;
-	case CMD_MOVE:
-		if(CurrGO && CurrGO->Id == GO_DRAGHANDLE) {
-			CalcRotation(((lfPOINT*)tmpl)[0].fx, ((lfPOINT*)tmpl)[0].fy, o, true);
-			if(parent) return parent->Command(CMD_REDRAW, 0L, 0L);
-			}
-		return true;
-	case CMD_AUTOSCALE:
-		if(dirty) {
-			DoAutoscale();
-			dirty = false;
-			}
-		return true;
-	case CMD_DROP_PLOT:
-		if(!parent || !tmpl || ((GraphObj*)tmpl)->Id < GO_PLOT) return false;
-		if(IsPlot3D(parent)) return parent->Command(cmd, tmpl, o);
-		if(!nPlots) {
-			plots = (GraphObj**)calloc(2, sizeof(GraphObj*));
-			if(plots) {
-				nPlots = 1;					plots[0] = (Plot *)tmpl;
-				plots[0]->parent = this;	CreateAxes();
-				return dirty = parent->Command(CMD_REDRAW, 0L, 0L);
-				}
-			}
-		else {
-			((Plot *)tmpl)->parent = this;
-			tmpPlots = (GraphObj**)memdup(plots, sizeof(GraphObj*) * (nPlots+2), 0);
-			Undo.ListGOmoved(plots, tmpPlots, nPlots);
-			Undo.SetGO(this, &tmpPlots[nPlots++], (Plot *)tmpl, 0L);
-			free(plots);			plots = tmpPlots;
-			return dirty = parent->Command(CMD_REDRAW, 0L, 0L);
-			}
-		return false;
-	case CMD_ADDPLOT:
-		return AddPlot(0x0);
-		}
-	return false;
-}
-
-void *
-Plot3D::ObjThere(int x, int y)
-{
-	if(drag) return drag->ObjThere(x, y);
-	return 0L;
-}
-
-void
-Plot3D::Track(POINT *p, anyOutput *o)
-{
-	fPOINT3D v, iv;
-	POINT pts[5];
-	RECT upd_rc;
-
-	CalcRotation(((lfPOINT*)p)->fx, ((lfPOINT*)p)->fy, o, false);
-	memcpy(&upd_rc, &rDims, sizeof(RECT));
-	IncrementMinMaxRect(&rDims, 3);
-	o->UpdateRect(&upd_rc, false);
-	memcpy(&v, &cu2, sizeof(fPOINT3D));
-	o->cvec2ivec(&v, &iv);
-	pts[0].x = iround(iv.fx);		pts[0].y = iround(iv.fy);
-	UpdateMinMaxRect(&rDims, pts[0].x, pts[0].y);
-	v.fx = cu1.fx;					o->cvec2ivec(&v, &iv); 
-	pts[1].x = iround(iv.fx);		pts[1].y = iround(iv.fy);
-	UpdateMinMaxRect(&rDims, pts[1].x, pts[1].y);
-	v.fy = cu1.fy;					o->cvec2ivec(&v, &iv); 
-	pts[2].x = iround(iv.fx);		pts[2].y = iround(iv.fy);
-	UpdateMinMaxRect(&rDims, pts[2].x, pts[2].y);
-	v.fz = cu1.fz;					o->cvec2ivec(&v, &iv); 
-	pts[3].x = iround(iv.fx);		pts[3].y = iround(iv.fy);
-	UpdateMinMaxRect(&rDims, pts[3].x, pts[3].y);
-	v.fy = cu2.fy;					o->cvec2ivec(&v, &iv); 
-	pts[4].x = iround(iv.fx);		pts[4].y = iround(iv.fy);
-	UpdateMinMaxRect(&rDims, pts[4].x, pts[4].y);
-	o->ShowLine(pts, 5, 0x000000ff);
-	v.fz = cu2.fz;					o->cvec2ivec(&v, &iv);
-	pts[0].x = iround(iv.fx);		pts[0].y = iround(iv.fy);
-	v.fz = cu1.fz;					o->cvec2ivec(&v, &iv); 
-	pts[1].x = iround(iv.fx);		pts[1].y = iround(iv.fy);
-	v.fx = cu2.fx;					o->cvec2ivec(&v, &iv);
-	pts[2].x = iround(iv.fx);		pts[2].y = iround(iv.fy);
-	v.fz = cu2.fz;					o->cvec2ivec(&v, &iv);
-	pts[3].x = iround(iv.fx);		pts[3].y = iround(iv.fy);
-	v.fy = cu1.fy;					o->cvec2ivec(&v, &iv);
-	pts[4].x = iround(iv.fx);		pts[4].y = iround(iv.fy);
-	o->ShowLine(pts, 5, 0x000000ff);
-	v.fy = cu2.fy;	v.fz = cu1.fz;		o->cvec2ivec(&v, &iv);
-	pts[0].x = iround(iv.fx);		pts[0].y = iround(iv.fy);
-	v.fy = cu1.fy;					o->cvec2ivec(&v, &iv);
-	pts[1].x = iround(iv.fx);		pts[1].y = iround(iv.fy);
-	v.fx = cu1.fx;					o->cvec2ivec(&v, &iv);
-	pts[2].x = iround(iv.fx);		pts[2].y = iround(iv.fy);
-	o->ShowLine(pts, 3, 0x000000ff);
-	v.fz = cu2.fz;					o->cvec2ivec(&v, &iv);
-	pts[0].x = iround(iv.fx);		pts[0].y = iround(iv.fy);
-	v.fx = cu2.fx;					o->cvec2ivec(&v, &iv);
-	pts[1].x = iround(iv.fx);		pts[1].y = iround(iv.fy);
-	v.fz = cu1.fz;					o->cvec2ivec(&v, &iv);
-	pts[2].x = iround(iv.fx);		pts[2].y = iround(iv.fy);
-	o->ShowLine(pts, 3, 0x000000ff);
-}
-
-void
-Plot3D::CreateAxes()
-{
-	typedef struct {
-		double x1, y1, z1, x2, y2, z2;
-		DWORD flags;
-		int a_type, t_type;
-		double lb_x, lb_y, tlb_x, tlb_y;
-		int txa;
-		}Axis3Ddef;
-	AxisDEF tmp_axis;
-	double ts = DefSize(SIZE_AXIS_TICKS);
-	int i;
-	if(Axes || !parent)return;
-	TextDEF tlbdef = {parent->GetColor(COL_AXIS), 0x00ffffffL, DefSize(SIZE_TICK_LABELS),
-		0.0, 0.0, 0, TXA_HLEFT | TXA_VCENTER, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, 0L};
-	Axis3Ddef *at = 0L;
-	Axis3Ddef at1[] = {
-		{cub1.fx, cub1.fy, 0.0, cub2.fx, cub1.fy, 0.0,
-			AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_NEGTICKS, 1, 3,
-			0.0, NiceValue((ts+DefSize(SIZE_AXIS_TICKS))*2.0), 0.0,
-			NiceValue(ts * 2.0), TXA_HCENTER | TXA_VTOP},
-		{cub1.fx, cub1.fy, 0.0, cub1.fx, cub2.fy, 0.0,
-			AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_NEGTICKS, 2, 2,
-			-NiceValue((ts+DefSize(SIZE_AXIS_TICKS))*3.0), 0.0,
-			-NiceValue(ts * 2.0), 0.0, TXA_HRIGHT | TXA_VCENTER},
-		{cub1.fx, cub1.fy, 0.0, cub1.fx, cub1.fy, cub2.fz,
-			AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_NEGTICKS, 3, 2,
-			-NiceValue((ts+DefSize(SIZE_AXIS_TICKS))*3.0), 0.0,
-			-NiceValue(ts * 2.0), 0.0, TXA_HRIGHT | TXA_VCENTER}};
-	Axis3Ddef at2[] = {
-		{at1[0].x1, at1[0].y1, at1[2].z2, at1[0].x2, at1[0].y2, at1[2].z2,
-			AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_NEGTICKS, 1, 3,
-			0.0, at1[0].lb_y, 0.0,	at1[0].tlb_y, TXA_HCENTER | TXA_VTOP},
-		{at1[0].x2, at1[1].y1, 0.0, at1[0].x2, at1[1].y2, 0.0,
-			AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_POSTICKS, 2, 2,
-			-at1[1].lb_x, 0.0, -at1[1].tlb_x, 0.0, TXA_HLEFT | TXA_VCENTER},
-		{at1[0].x2, at1[0].y1, 0.0, at1[0].x2, at1[0].y1, at1[2].z2,
-			AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_POSTICKS, 3, 2,
-			-at1[2].lb_x, 0.0, -at1[2].tlb_x, 0.0, TXA_HLEFT | TXA_VCENTER},
-		{at1[0].x1, at1[0].y1, 0.0, at1[0].x2, at1[0].y2, 0.0,
-			AXIS_3D, 1, 3, 0.0, at1[0].lb_y, 0.0, at1[0].tlb_y, TXA_HCENTER | TXA_VCENTER},
-		{at1[0].x1, at1[1].y2, 0.0, at1[0].x2, at1[1].y2, 0.0,
-			AXIS_3D, 1, 3, 0.0, at1[0].lb_y, 0.0, at1[0].tlb_y, TXA_HCENTER | TXA_VCENTER},
-		{at1[0].x1, at1[1].y1, 0.0, at1[0].x1, at1[1].y2, 0.0,
-			AXIS_3D, 2, 2, at1[1].lb_x, 0.0, at1[1].tlb_x, 0.0, TXA_HCENTER | TXA_VCENTER},
-		{at1[0].x1, at1[1].y1, at1[2].z2, at1[0].x1, at1[1].y2, at1[2].z2,
-			AXIS_3D, 2, 2, at1[1].lb_x, 0.0, at1[1].tlb_x, 0.0, TXA_HCENTER | TXA_VCENTER},
-		{at1[0].x1, at1[0].y1, 0.0, at1[0].x1, at1[0].y1, at1[2].z2,
-			AXIS_3D, 3, 2, at1[2].lb_x, 0.0, at1[2].tlb_x, 0.0, TXA_HCENTER | TXA_VCENTER},
-		{at1[0].x1, at1[1].y2, 0.0, at1[0].x1, at1[1].y2, at1[2].z2,
-			AXIS_3D, 3, 2, at1[2].lb_x, 0.0, at1[2].tlb_x, 0.0}, TXA_HCENTER | TXA_VCENTER};
-	Axis3Ddef at3[] = {
-		{at1[0].x1, (at1[1].y1+at1[1].y2)/2.0, at1[2].z2/2.0, at1[0].x2, 
-			(at1[1].y1+at1[1].y2)/2.0, at1[2].z2/2.0,
-			AXIS_3D | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_NEGTICKS, 1, 3,
-			0.0, at1[0].lb_y, 0.0,	at1[0].tlb_y, TXA_HCENTER | TXA_VTOP},
-		{(at1[0].x1 + at1[0].x2)/2.0, at1[1].y1, at1[2].z2/2.0, 
-			(at1[0].x1 + at1[0].x2)/2.0, at1[1].y2, at1[2].z2/2.0,
-			AXIS_3D | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_POSTICKS, 2, 2,
-			-at1[1].lb_x, 0.0, -at1[1].tlb_x, 0.0, TXA_HLEFT | TXA_VCENTER},
-		{(at1[0].x1 + at1[0].x2)/2.0, (at1[1].y1+at1[1].y2)/2.0, 0.0,
-			(at1[0].x1 + at1[0].x2)/2.0, (at1[1].y1+at1[1].y2)/2.0, at1[2].z2,
-			AXIS_3D | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_POSTICKS, 3, 2,
-			-at1[2].lb_x, 0.0, -at1[2].tlb_x, 0.0, TXA_HLEFT | TXA_VCENTER}};
-
-	tmp_axis.min = 0.0;			tmp_axis.max = 100.0;
-	tmp_axis.Start = 0.0;		tmp_axis.Step = 20.0;
-	tmp_axis.Center.fx = tmp_axis.Center.fy = 0.0;
-	tmp_axis.Radius = 0.0;		tmp_axis.nBreaks = 0;
-	tmp_axis.breaks = 0L;		tmp_axis.owner = 0L;
-	switch(AxisTempl3D){
-	case 0:		at = at1;		nAxes = 3;		break;
-	case 1:		at = at2;		nAxes = 9;		break;
-	case 2:		at = at3;		nAxes = 3;		break;
-		}
-	if(!(Axes = (Axis**)calloc(nAxes, sizeof(Axis *))))return;
-	if(at && nAxes) for(i = 0; i < nAxes; i++) {
-		tmp_axis.loc[0].fx = at[i].x1;		tmp_axis.loc[0].fy = at[i].y1;
-		tmp_axis.loc[0].fz = at[i].z1;		tmp_axis.loc[1].fx = at[i].x2;
-		tmp_axis.loc[1].fy = at[i].y2;		tmp_axis.loc[1].fz = at[i].z2;
-		tlbdef.Align = at[i].txa;
-		if((Axes[i] = new Axis(this, data, &tmp_axis, at[i].flags))){
-			Axes[i]->type = at[i].a_type;
-			Axes[i]->SetSize(SIZE_LB_YDIST, at[i].lb_y);
-			Axes[i]->SetSize(SIZE_LB_XDIST, at[i].lb_x);
-			Axes[i]->SetSize(SIZE_TLB_YDIST, at[i].tlb_y);
-			Axes[i]->SetSize(SIZE_TLB_XDIST, at[i].tlb_x);
-			Axes[i]->Command(CMD_TICK_TYPE, &at[i].t_type, 0L);
-			Axes[i]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		}
-}
-
-void
-Plot3D::DoAutoscale()
-{
-	int i;
-	AxisDEF *ad;
-
-	if(!plots) return;
-	Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-	xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
-	xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
-	for(i = 0; i < nPlots; i++) {
-		if(plots[i]) {
-			if(plots[i]->Id >= GO_PLOT && plots[i]->Id < GO_GRAPH) {
-				if(!((Plot*)plots[i])->hidden) plots[i]->Command(CMD_AUTOSCALE, 0L, 0L);
-				}
-			else plots[i]->Command(CMD_AUTOSCALE, 0L, 0L);
-			}
-		}
-	if(xBounds.fx <= xBounds.fy && yBounds.fx <= yBounds.fy && zBounds.fx <= zBounds.fy){
-		if(Axes)for(i = 0; i < 3; i++) if(Axes[i]){
-			ad = Axes[i]->axis;
-			if(ad->flags & AXIS_AUTOSCALE) {
-				switch(i) {
-				case 0:
-					if(xBounds.fx == xBounds.fy) {
-						xBounds.fx -= 1.0;	xBounds.fy += 1.0;
-						}
-					ad->min = xBounds.fx;	ad->max = xBounds.fy;	break;
-				case 1:
-					if(yBounds.fx == yBounds.fy) {
-						yBounds.fx -= 1.0;	yBounds.fy += 1.0;
-						}
-					ad->min = yBounds.fx;	ad->max = yBounds.fy;	break;
-				case 2:
-					if(zBounds.fx == zBounds.fy) {
-						zBounds.fx -= 1.0;	zBounds.fy += 1.0;
-						}
-					ad->min = zBounds.fx;	ad->max = zBounds.fy;	break;
-					}
-				NiceAxis(ad, 4);
-				if(ad->min <= 0.0 && ((ad->flags & 0xf000) == AXIS_LOG ||
-					(ad->flags & 0xf000) == AXIS_RECI)) {
-					ad->min = base4log(ad, i);
-					}
-				Axes[i]->Command(CMD_AUTOSCALE, ad, 0L);
-				}
-			}
-		else if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			((Plot*)parent)->CheckBounds3D(xBounds.fx, yBounds.fx, zBounds.fx);
-			((Plot*)parent)->CheckBounds3D(xBounds.fy, yBounds.fy, zBounds.fy);
-			}
-		}
-}
-
-//Implement some kind of virtual trackball
-//see: J. Hultquist: A Virtual Trackball
-//Graphic Gems, A.S. Glassner ed.; Academic Press Inc.
-//ISBN 0-12-286165-5, pp. 462-463
-void
-Plot3D::CalcRotation(double dx, double dy, anyOutput *o, bool accept)
-{
-	fPOINT3D V0, V1, A;
-	double R, R2, si, dp, NewRot[6];
-	double rotM[3][3], newM[3][3];			//rotation matrices
-
-	if(!CurrGO || CurrGO->Id != GO_DRAGHANDLE || CurrGO->type < DH_18 ||
-		CurrGO->type > DH_88) return;
-	//get coordinates for last accepted rotation
-	V0.fx = GetSize(SIZE_XPOS + CurrGO->type - DH_18);
-	V0.fy = GetSize(SIZE_YPOS + CurrGO->type - DH_18);
-	V0.fz = GetSize(SIZE_ZPOS + CurrGO->type - DH_18);
-	//revert to last matrix	
-	o->SetSpace(&cu1, &cu2, defs.cUnits, RotDef, &rc, Axes[0]->GetAxis(),
-		Axes[1]->GetAxis(), Axes[2]->GetAxis());
-	o->cvec2ivec(&V0, &V1);
-	memcpy(&V0, &V1, sizeof(fPOINT3D));
-	V1.fx += o->un2fix(dx);		V1.fy += o->un2fiy(dy);
-	V0.fx -= o->rotC.fx;		V0.fy -= o->rotC.fy;		V0.fz -= o->rotC.fz;
-	V1.fx -= o->rotC.fx;		V1.fy -= o->rotC.fy;		V1.fz -= o->rotC.fz;
-	R = sqrt(R2 = V0.fx * V0.fx + V0.fy * V0.fy + V0.fz * V0.fz);
-	R2 -= (V1.fx * V1.fx + V1.fy * V1.fy);
-	if (R2 <= 1.0) return;
-	V1.fz = V1.fz > 0.0 ? sqrt(R2) : -sqrt(R2);
-	V0.fx /= R;			V0.fy /= R;			V0.fz /= R;
-	V1.fx /= R;			V1.fy /= R;			V1.fz /= R;
-	A.fx = (V1.fy * V0.fz) - (V1.fz * V0.fy);
-	A.fy = (V1.fz * V0.fx) - (V1.fx * V0.fz);
-	A.fz = (V1.fx * V0.fy) - (V1.fy * V0.fx);
-
-	si = sqrt(A.fx * A.fx + A.fy * A.fy + A.fz * A.fz);
-	if(si > 0.001) {
-		NewRot[0] = A.fx;	NewRot[1] = A.fy;	NewRot[2] = A.fz;
-		NewRot[3] = si;		NewRot[4] = sqrt(1.0-si*si);	NewRot[5] = 1.0-NewRot[4];
-		//normalize vector part of NewRot
-		dp = sqrt(NewRot[0]*NewRot[0] + NewRot[1]*NewRot[1] + NewRot[2]*NewRot[2]);
-		NewRot[0] /= dp;	NewRot[1] /= dp;	NewRot[2] /= dp;
-		//set up rotation matrix from quaternion
-		//see: Graphic Gems, A.S. Glassner ed.; Academic Press Inc.
-		//M.E. Pique: Rotation Tools
-		// ISBN 0-12-286165-5, p.466
-		rotM[0][0] = NewRot[5]*NewRot[0]*NewRot[0] + NewRot[4];
-		rotM[0][1] = NewRot[5]*NewRot[0]*NewRot[1] + NewRot[3]*NewRot[2];
-		rotM[0][2] = NewRot[5]*NewRot[0]*NewRot[2] - NewRot[3]*NewRot[1];
-		rotM[1][0] = NewRot[5]*NewRot[0]*NewRot[1] - NewRot[3]*NewRot[2];
-		rotM[1][1] = NewRot[5]*NewRot[1]*NewRot[1] + NewRot[4];
-		rotM[1][2] = NewRot[5]*NewRot[1]*NewRot[2] + NewRot[3]*NewRot[0];
-		rotM[2][0] = NewRot[5]*NewRot[0]*NewRot[2] + NewRot[3]*NewRot[1];
-		rotM[2][1] = NewRot[5]*NewRot[1]*NewRot[2] - NewRot[3]*NewRot[0];
-		rotM[2][2] = NewRot[5]*NewRot[2]*NewRot[2] + NewRot[4];
-		//rotate rotation matrix
-		if(MatMul(o->rotM, rotM, newM)) memcpy(&o->rotM, &newM, sizeof(newM));
-		}
-	if(accept) {
-		//create new quaternion in RotDef from rotation matrix of output class
-		Undo.RotDef(this, &RotDef, 0L);
-		RotDef[4] = (o->rotM[0][0] + o->rotM[1][1] + o->rotM[2][2] -1)/2.0;
-		RotDef[3] = sqrt(1.0-RotDef[4]*RotDef[4]);
-		RotDef[0] = (o->rotM[1][2] - o->rotM[2][1])/(2.0 * RotDef[3]);
-		RotDef[1] = (o->rotM[2][0] - o->rotM[0][2])/(2.0 * RotDef[3]);
-		RotDef[2] = (o->rotM[0][1] - o->rotM[1][0])/(2.0 * RotDef[3]);
-		RotDef[5] = 1.0-RotDef[4];
-		}
-}
-
-bool
-Plot3D::AcceptObj(GraphObj *go)
-{
-	if(!dispObs && !(dispObs = (obj_desc**)
-		calloc(nmaxObs = 1024, sizeof(obj_desc*))))return false;
-	else if((nObs+1) >= nmaxObs) dispObs = (obj_desc**)
-		realloc(dispObs, (nmaxObs = nmaxObs +1024) * sizeof(obj_desc*));
-	if(dispObs[++nObs] = (obj_desc*)calloc(1, sizeof(obj_desc))){
-		dispObs[nObs]->Zmin = go->GetSize(SIZE_MIN_Z);
-		dispObs[nObs]->Zmax = go->GetSize(SIZE_MAX_Z);
-		dispObs[nObs]->go = go;
-		}
-	return true;
-}
-
-//Execute a heap sort before drawing the objects
-//W.H. pres, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1988/1989)
-//Numerical Recipes in C, Cambridge University Press, ISBN 0-521-35465-X
-// p. 245
-void
-Plot3D::SortObj()
-{
-	int l, j, ir, i;
-	obj_desc *rra;
-
-	if(nObs < 2) return;
-	l=(nObs >> 1)+1;			ir = nObs;
-	for( ; ; ){
-		if(l > 1) rra = dispObs[--l];
-		else {
-			rra = dispObs[ir];
-			dispObs[ir] = dispObs[1];
-			if(--ir == 1) {
-				dispObs[1] = rra;
-				return;
-				}
-			}
-		i = l;					j = l << 1;
-		while(j <= ir) {
-			if(j < ir && (dispObs[j]->Zmin < dispObs[j+1]->Zmin
-				|| (dispObs[j]->Zmin == dispObs[j+1]->Zmin && dispObs[j]->Zmax < dispObs[j+1]->Zmax))) ++j;
-			if(rra->Zmin < dispObs[j]->Zmin || (rra->Zmin == dispObs[j]->Zmin && rra->Zmax < dispObs[j]->Zmax)) {
-				dispObs[i] = dispObs[j];
-				j += (i = j);
-				}
-			else j = ir + 1;
-			}
-		dispObs[i] = rra;
-		}
-}
-
-bool
-Plot3D::Rotate(double dx, double dy, double dz, anyOutput *o, bool accept)
-{
-	int i;
-	double si, NewRot[6];
-	double rotM[3][3], newM[3][3];			//rotation matrices
-	bool bRet = true;
-
-	o->SetSpace(&cu1, &cu2, defs.cUnits, RotDef, &rc, Axes[0]->GetAxis(),
-		Axes[1]->GetAxis(), Axes[2]->GetAxis());
-	for(i = 0; i < 3; i++) {
-		switch (i){
-		case 0:
-			if(dx > 0.0) {
-				NewRot[0] = -o->rotM[1][0];			NewRot[1] = -o->rotM[1][1];
-				NewRot[2] = -o->rotM[1][2];
-				NewRot[3] = si = dx;
-				}
-			else {
-				NewRot[0] = o->rotM[1][0];			NewRot[1] = o->rotM[1][1];
-				NewRot[2] = o->rotM[1][2];
-				NewRot[3] = si = -dx;
-				}
-			break;
-		case 1:
-			if(dy > 0.0) {
-				NewRot[0] = -o->rotM[0][0];			NewRot[1] = -o->rotM[0][1];
-				NewRot[2] = -o->rotM[0][2];
-				NewRot[3] = si = dy;
-				}
-			else {
-				NewRot[0] = o->rotM[0][0];			NewRot[1] = o->rotM[0][1];
-				NewRot[2] = o->rotM[0][2];
-				NewRot[3] = si = -dy;
-				}
-			break;
-		case 2:
-			if(dz > 0.0) {
-				NewRot[0] = -o->rotM[2][0];			NewRot[1] = -o->rotM[2][1];
-				NewRot[2] = -o->rotM[2][2];
-				NewRot[3] = si = dz;
-				}
-			else {
-				NewRot[0] = o->rotM[2][0];			NewRot[1] = o->rotM[2][1];
-				NewRot[2] = o->rotM[2][2];
-				NewRot[3] = si = -dz;
-				}
-			break;
-			}
-		if(si > 0.0) {
-			NewRot[4] = sqrt(1.0-si*si);	NewRot[5] = 1.0-NewRot[4];
-			//set up rotation matrix from quaternion
-			//see: Graphic Gems, A.S. Glassner ed.; Academic Press Inc.
-			//M.E. Pique: Rotation Tools
-			// ISBN 0-12-286165-5, p.466
-			rotM[0][0] = NewRot[5]*NewRot[0]*NewRot[0] + NewRot[4];
-			rotM[0][1] = NewRot[5]*NewRot[0]*NewRot[1] + NewRot[3]*NewRot[2];
-			rotM[0][2] = NewRot[5]*NewRot[0]*NewRot[2] - NewRot[3]*NewRot[1];
-			rotM[1][0] = NewRot[5]*NewRot[0]*NewRot[1] - NewRot[3]*NewRot[2];
-			rotM[1][1] = NewRot[5]*NewRot[1]*NewRot[1] + NewRot[4];
-			rotM[1][2] = NewRot[5]*NewRot[1]*NewRot[2] + NewRot[3]*NewRot[0];
-			rotM[2][0] = NewRot[5]*NewRot[0]*NewRot[2] + NewRot[3]*NewRot[1];
-			rotM[2][1] = NewRot[5]*NewRot[1]*NewRot[2] - NewRot[3]*NewRot[0];
-			rotM[2][2] = NewRot[5]*NewRot[2]*NewRot[2] + NewRot[4];
-			if(MatMul(o->rotM, rotM, newM))	memcpy(&o->rotM, &newM, sizeof(newM));
-			else accept = bRet = false;
-			}
-		}
-	if(accept && bRet) {
-		//create new quaternion in RotDef from rotation matrix of output class
-		Undo.RotDef(this, &RotDef, 0L);
-		RotDef[4] = (o->rotM[0][0] + o->rotM[1][1] + o->rotM[2][2] -1)/2.0;
-		RotDef[3] = sqrt(1.0-RotDef[4]*RotDef[4]);
-		RotDef[0] = (o->rotM[1][2] - o->rotM[2][1])/(2.0 * RotDef[3]);
-		RotDef[1] = (o->rotM[2][0] - o->rotM[0][2])/(2.0 * RotDef[3]);
-		RotDef[2] = (o->rotM[0][1] - o->rotM[1][0])/(2.0 * RotDef[3]);
-		RotDef[5] = 1.0-RotDef[4];
-		if(parent)return parent->Command(CMD_REDRAW, 0L, o);
-		}
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// use Plot3D to create a 2.5 dimensional chart
-Chart25D::Chart25D(GraphObj *par, DataObj *d, DWORD flags)
-	:Plot3D(par, d, flags)
-{
-	dspm.fx = dspm.fy = dspm.fz = 1.0;
-}
-
-Chart25D::~Chart25D()
-{
-	if(name) free(name);		name=0L;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// use Plot3D to create a 2.5 dimensional ribbon chart
-Ribbon25D::Ribbon25D(GraphObj *par, DataObj *d, DWORD flags)
-	:Plot3D(par, d, flags)
-{
-	dspm.fx = dspm.fy = dspm.fz = 1.0;
-}
-
-Ribbon25D::~Ribbon25D()
-{
-	if(name) free(name);		name=0L;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// use Plot3D to create a 3 dimensional bubble plot
-BubblePlot3D::BubblePlot3D(GraphObj *par, DataObj *d)
-	:Plot3D(par, d, 0x0L)
-{
-}
-
-BubblePlot3D::~BubblePlot3D()
-{
-	if(name) free(name);		name=0L;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// 3D function plotter
-Func3D::Func3D(GraphObj *par, DataObj *d)
-	:Plot3D(par, d, 0x0L)
-{
-	FileIO(INIT_VARS);
-	if(cmdxy = (char*)malloc(40*sizeof(char)))
-		rlp_strcpy(cmdxy, 40, (char*)"r=sqrt(x*x+z*z)\ny=1-exp(-8/(r+1))");
-	Id = GO_FUNC3D;
-}
-
-Func3D::Func3D(int src):Plot3D(0)
-{
-	int i;
-
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		//now set parent in all children
-		if(Axes) for(i = 0; i < nAxes; i++)
-			if(Axes[i]) Axes[i]->parent = this;
-		if(plots) for(i = 0; i < nPlots; i++)
-			if(plots[i]) plots[i]->parent = this;
-		}
-}
-
-Func3D::~Func3D()
-{
-	if(param) free(param);		param = 0L;
-	if(cmdxy) free(cmdxy);		cmdxy = 0L;
-	if(gda) delete(gda);		gda = 0L;
-	if(name) free(name);		name=0L;
-}
-
-bool
-Func3D::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch(cmd) {
-	case CMD_SET_DATAOBJ:
-		Plot3D::Command(cmd, tmpl, o);
-		if(gob && gda) gob->Command(cmd, gda, o);
-		Id = GO_FUNC3D;
-		return true;
-	case CMD_UPDATE:
-		return Update();
-		break;
-		}
-	return Plot3D::Command(cmd, tmpl, o);
-}
-
-bool
-Func3D::Update()
-{
-	if(cmdxy) {
-		dirty = true;
-		if(xstep == 0.0) xstep = 1.0;	if(zstep == 0.0) zstep = 1.0;
-		if(!gda) gda = new DataObj();
-		if(gda && do_func3D(gda, x1, x2, xstep, z1, z2, zstep, cmdxy, param)) {
-			if(gob = new Grid3D(this, gda, type, x1, xstep, z1, zstep)) {
-				gob->Command(CMD_SET_LINE, &Line, 0L);
-				gob->Command(CMD_SYM_FILL, &Fill, 0L);
-				if(!plots && (plots = (GraphObj**)calloc(2, sizeof(GraphObj*)))) {
-					nPlots = 1;				plots[0] = (Plot *)gob;
-					if(parent->Id == GO_GRAPH) CreateAxes();
-					return dirty = parent->Command(CMD_REDRAW, 0L, 0L);
-					}
-				else if(plots && nPlots && plots[0]->Id == GO_GRID3D) {
-					Undo.DeleteGO(&plots[0], UNDO_CONTINUE, 0L);
-					Undo.SetGO(this, &plots[0], gob, UNDO_CONTINUE);
-					return true;
-					}
-				else {
-					DeleteGO(gob);		gob=0L;
-					}
-				}
-			}
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// fit 3D function to data
-static char *lastFunc3D = 0L, *lastParam3D=0L;
-FitFunc3D::FitFunc3D(GraphObj *par, DataObj *d)
-	:Plot3D(par, d, 0x0L)
-{
-	FileIO(INIT_VARS);
-	if(lastFunc3D && lastFunc3D[0] && lastParam3D && lastParam3D[0]) {
-		cmdxy = (char*)memdup(lastFunc3D, (int)strlen(lastFunc3D)+1, 0);
-		param = (char*)memdup(lastParam3D, (int)strlen(lastParam3D)+1, 0);
-		}
-	if(!cmdxy || !param) {
-		cmdxy = (char*)malloc(20*sizeof(char));		param = (char*)malloc(20*sizeof(char));
-		if(cmdxy) rlp_strcpy(cmdxy, 20, (char*)"a+b*x^c");
-		if(param) rlp_strcpy(param, 20, (char*)"a=1; b=1; c=0.1;");
-		}
-	Id = GO_FITFUNC3D;
-}
-
-FitFunc3D::FitFunc3D(int src):Plot3D(0)
-{
-	int i;
-
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		//now set parent in all children
-		if(Axes) for(i = 0; i < nAxes; i++)
-			if(Axes[i]) Axes[i]->parent = this;
-		if(plots) for(i = 0; i < nPlots; i++)
-			if(plots[i]) plots[i]->parent = this;
-		}
-}
-
-FitFunc3D::~FitFunc3D()
-{
-	if(param) free(param);		param = 0L;
-	if(cmdxy) free(cmdxy);		cmdxy = 0L;
-	if(gda) delete(gda);		gda = 0L;
-	if(name) free(name);		name=0L;
-}
-
-bool
-FitFunc3D::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-
-	switch(cmd) {
-	case CMD_ENDDIALOG:
-		if(!cmdxy || !param) return false;
-		if(i = (int)strlen(cmdxy)) {
-			if(lastFunc3D = (char*)realloc(lastFunc3D, i+2))
-				rlp_strcpy(lastFunc3D, i+1, cmdxy);
-			}
-		if(i = (int)strlen(param)) {
-			if(lastParam3D = (char*)realloc(lastParam3D, i+2))
-				rlp_strcpy(lastParam3D, i+1, param);
-			}
-		return true;
-	case CMD_SET_DATAOBJ:
-		Plot3D::Command(cmd, tmpl, o);
-		if(gob && gda) gob->Command(cmd, gda, o);
-		Id = GO_FITFUNC3D;
-		return true;
-	case CMD_UPDATE:
-		return Update();
-		break;
-		}
-	return Plot3D::Command(cmd, tmpl, o);
-}
-
-bool
-FitFunc3D::Update()
-{
-	if(cmdxy) {
-		dirty = true;
-		if(xstep == 0.0) xstep = 1.0;	if(zstep == 0.0) zstep = 1.0;
-		if(!gda) gda = new DataObj();
-		if(gda && do_func3D(gda, x1, x2, xstep, z1, z2, zstep, cmdxy, param)) {
-			if(gob = new Grid3D(this, gda, type, x1, xstep, z1, zstep)) {
-				gob->Command(CMD_SET_LINE, &Line, 0L);
-				gob->Command(CMD_SYM_FILL, &Fill, 0L);
-				if(!plots && (plots = (GraphObj**)calloc(3, sizeof(GraphObj*)))) {
-					nPlots = 1;				plots[0] = (Plot *)gob;
-					if(parent->Id == GO_GRAPH) CreateAxes();
-					return dirty = parent->Command(CMD_REDRAW, 0L, 0L);
-					}
-				else if(plots && nPlots && plots[0]->Id == GO_GRID3D) {
-					Undo.DeleteGO(&plots[0], UNDO_CONTINUE, 0L);
-					Undo.SetGO(this, &plots[0], gob, UNDO_CONTINUE);
-					return true;
-					}
-				else {
-					DeleteGO(gob);		gob=0L;
-					}
-				}
-			}
-		}
-	return false;
-}
+//PlotObs.cpp, Copyright (c) 2001-2008 R.Lackner
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// This modules contains code for the differnt Plot objects. Plots are
+// graphic objects containing more objects, which represent the data.
+// Several Plots may be contained in a Graph: Plots are the different layers
+// of a Graph.
+// Most part of this module has been moved here from rlplot.cpp of
+// earlier versions. 
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#include "rlplot.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+extern char TmpTxt[];
+extern Default defs;
+extern int cPlots;
+extern GraphObj *CurrGO, *TrackGO;			//Selected Graphic Objects
+extern Axis **CurrAxes;						//axes of current graph
+extern UndoObj Undo;
+
+int AxisTempl3D = 0;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Plot::Plot(GraphObj *par, DataObj *d):GraphObj(par, d)
+{
+	int pos, nsize;
+
+	Id = GO_PLOT;
+	Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+	if(name = (char*)malloc((nsize = 20)*sizeof(char))){
+		pos = rlp_strcpy(name, nsize, (char*)"Plot");
+		add_int_to_buff(&name, &pos, &nsize, ++cPlots, true, 0);
+		}
+	use_xaxis = use_yaxis = use_zaxis = 0;	hidden = 0;
+	x_info = y_info = z_info = data_desc = 0L;
+	x_tv = y_tv = 0L;	x_dtype = y_dtype = z_dtype = 0;
+}
+
+double
+Plot::GetSize(int select)
+{
+	switch(select){
+	case SIZE_MINE:				return 0.0;
+	//The Bounds values must be returned by every plot:
+	//   they are necessary for scaling !
+	case SIZE_BOUNDS_XMIN:		
+		return parent ? parent->GetSize(SIZE_BOUNDS_XMIN) : Bounds.Xmin;
+	case SIZE_BOUNDS_XMAX:		
+		return parent ? parent->GetSize(SIZE_BOUNDS_XMAX) : Bounds.Xmax;
+	case SIZE_BOUNDS_YMIN:		
+		return parent ? parent->GetSize(SIZE_BOUNDS_YMIN) : Bounds.Ymin;	
+	case SIZE_BOUNDS_YMAX:		
+		return parent ? parent->GetSize(SIZE_BOUNDS_YMAX) : Bounds.Ymax;
+	case SIZE_BARMINX:
+	case SIZE_BARMINY:
+		return 1.0f;
+	default:
+		return DefSize(select);
+		}
+}
+
+DWORD
+Plot::GetColor(int select)
+{
+	if(parent) return parent->GetColor(select);
+	else return defs.Color(select);
+}
+
+void
+Plot::CheckBounds(double x, double y)
+{
+	if(x < Bounds.Xmin) Bounds.Xmin = x;	if(x > Bounds.Xmax) Bounds.Xmax = x;
+	if(y < Bounds.Ymin) Bounds.Ymin = y;	if(y > Bounds.Ymax) Bounds.Ymax = y;
+}
+
+bool
+Plot::UseAxis(int idx)
+{
+	if(CurrAxes && CurrAxes[idx]) {
+		switch(CurrAxes[idx]->type & 0xf) {
+		case 1:									// x-axis
+			Undo.ValInt(parent, &use_xaxis, 0L);
+			use_xaxis = idx;			return true;
+		case 2:									// y-axis
+			Undo.ValInt(parent, &use_yaxis, 0L);
+			use_yaxis = idx;			return true;
+		case 3:									// z-axis
+			Undo.ValInt(parent, &use_zaxis, 0L);
+			use_zaxis = idx;			return true;
+			}
+		}
+	return false;
+}
+
+void
+Plot::ApplyAxes(anyOutput *o)
+{
+	if(!o || !CurrAxes || !parent) return;
+	if(use_xaxis && CurrAxes[use_xaxis]) {
+		o->UseAxis(CurrAxes[use_xaxis]->axis, CurrAxes[use_xaxis]->type & 0xf);
+		}
+	else use_xaxis = 0;
+	if(use_yaxis && CurrAxes[use_yaxis]) {
+		o->UseAxis(CurrAxes[use_yaxis]->axis, CurrAxes[use_yaxis]->type & 0xf);
+		}
+	else use_yaxis = 0;
+	if(use_zaxis && CurrAxes[use_zaxis]) {
+		o->UseAxis(CurrAxes[use_zaxis]->axis, CurrAxes[use_zaxis]->type & 0xf);
+		}
+	else use_zaxis = 0;
+	return;
+}
+
+void
+Plot::CheckBounds3D(double x, double y, double z)
+{
+	if(x < xBounds.fx) xBounds.fx = x;	if(x > xBounds.fy) xBounds.fy = x;
+	if(y < yBounds.fx) yBounds.fx = y;	if(y > yBounds.fy) yBounds.fy = y;
+	if(z < zBounds.fx) zBounds.fx = z;	if(z > zBounds.fy) zBounds.fy = z;
+	CheckBounds(x, y);
+}
+
+bool
+Plot::SavVarObs(GraphObj **gol, long ngo, DWORD flags)
+{
+	int i;
+	void *ptr;
+
+	if(!gol || !ngo) return false;
+	SavVarInit(150 * ngo);
+	for(i = 0; i < ngo; i++) 
+		if(gol[i]) gol[i]->FileIO(SAVE_VARS);
+	ptr = SavVarFetch();
+	Undo.SavVarBlock(this, &ptr, flags);
+	return true;
+}
+
+DataObj *
+Plot::CreaCumData(char *xr, char *yr, int mode, double base)
+{
+	char **yranges;
+	int i, j, nc, nr, ir, ic, n, c_num, c_txt, c_datetime;
+	double value, old_val;
+	DataObj *CumData = 0L;
+	anyResult ares;
+	AccRange *ax = 0L, **ayy = 0L;
+	TextValue *tv = 0L;
+	bool *validRows;
+
+	if(!xr || !yr || !mode || !data) return 0L;
+	if(!(CumData = new DataObj()))return 0L;
+	//count valid data lines
+	if(!(ax = new AccRange(xr))) {
+		delete CumData;		CumData = 0L;	return 0L;
+		}
+	ax->DataTypes(data, &c_num, &c_txt, &c_datetime);
+	nr = ax->CountItems();
+	if(!(yranges = split(yr, '&', &nc))){
+		delete CumData;		delete ax;		return 0L;
+		}
+	if(x_tv) x_tv->Reset();		if(y_tv) y_tv->Reset();
+	j = mode == 1 || mode == 2 ? nr : nr * 2;
+	if(CumData->Init(j , nc+2) && (validRows = (bool*)calloc(j, sizeof(bool)))){
+		if(!c_num && (c_txt + c_datetime) > 0 ) {
+			if(x_tv) tv = x_tv;
+			else if(y_tv) tv = y_tv;
+			else tv = x_tv = new TextValue();
+			}
+		//setup all ranges
+		if(!(ayy = (AccRange**)calloc(nc, sizeof(AccRange*))))return 0L;
+		for(i = 0; i < nc; i++) {
+			if(yranges[i] && *yranges[i] && (ayy[i] = new AccRange(yranges[i]))) {
+				if(!ayy[i]->GetFirst(&ic, &ir)) return 0L;
+				}
+			}
+		// set x values as first column
+		for(i = n = 0, ax->GetFirst(&ic, &ir); ax->GetNext(&ic, &ir); i++, n++) {
+			if(data->GetResult(&ares, ir, ic, false)) {
+				if(tv) {
+					switch(ares.type) {
+					case ET_TEXT:
+						value = tv->GetValue(ares.text);			break;
+					default:
+						TranslateResult(&ares);
+						value = tv->GetValue(ares.text);			break;
+						}
+					CumData->SetValue(n, 0, value);		CumData->SetValue(n, 1, base);
+					}
+				else if(ares.type == ET_VALUE && ares.value > -HUGE_VAL && ares.value < HUGE_VAL) {
+					CumData->SetValue(n, 0, value = ares.value);	CumData->SetValue(n, 1, base);
+					}
+				else {
+					CumData->SetValue(n, 0, value = 0.0);	CumData->SetValue(n, 1, base);
+					}
+				if(mode == 3 || mode == 4){				//complete polygon data
+					CumData->SetValue((nr<<1)-i-1, 0, value);
+					}
+				for(j = 0; j < nc; j++) {
+					if(CumData->GetValue(n, j+1, &value)) CumData->SetValue(n, j+2, value);
+					if(ayy[j]->GetNext(&ic, &ir) && data->GetResult(&ares, ir, ic, false)){
+						if(ares.type == ET_VALUE && ares.value > -HUGE_VAL && ares.value < HUGE_VAL){
+							value = ares.value;		validRows[i] = true;
+							}
+						else value = 0.0;			old_val = 0.0;
+						CumData->GetValue(n, j+2, &old_val);
+						switch (mode) {
+						case 1:	case 3:	value += old_val;			break;
+						case 2:	case 4: value = old_val -value;		break;
+							}
+						CumData->SetValue(n, j+2, value);
+						}
+					if(mode == 3 || mode == 4)			//complete polygon data
+						if(CumData->GetValue(n, j+1, &value)){
+							if(validRows[n]) validRows[(nr<<1)-i-1] = true;
+							CumData->SetValue((nr<<1)-i-1, j+2, value);
+							}
+					}
+				}
+			else {
+				for(j = 0; j < nc; j++) ayy[j]->GetNext(&ic, &ir);
+				}
+			}
+		for(i = 0; i < nc; i++) delete ayy[i];		free(ayy);
+		for(i = 0; i < CumData->cRows; i++) {
+			if(!validRows[i]) {
+				CumData->cRows--;
+				for(j = 0; j < CumData->cCols; j++) {
+					if(CumData->etRows[i][j]) delete CumData->etRows[i][j];
+					}
+				free(CumData->etRows[i]);
+				for(j = i; j < CumData->cRows; j++) {
+					CumData->etRows[j] = CumData->etRows[j+1];
+					validRows[j] = validRows[j+1];
+					}
+				if(!validRows[i] && i < CumData->cRows) i--;
+				}
+			}
+		free(validRows);
+		}
+	for(i = 0; i < nc; i++) if(yranges[i]) free(yranges[i]);
+	if(ax) delete ax;		free(yranges);
+	return CumData;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// PlotScatt handles most XY-Plots: its a Plot-Class
+PlotScatt::PlotScatt(GraphObj *par, DataObj *d, DWORD presel):Plot(par, d)
+{
+	FileIO(INIT_VARS);
+	DefSel = presel;
+	Id = GO_PLOTSCATT;
+	if (!d) {
+		if(parent && parent->Command(CMD_DELOBJ, this, NULL)) return;
+		ErrorBox("Attempt to create plot\nwithout any data.");
+		return;
+		}
+}
+
+PlotScatt::PlotScatt(GraphObj *par, DataObj *d, int cBars, Bar **bars, ErrorBar **errs):Plot(par, d)
+{
+	int i;
+
+	FileIO(INIT_VARS);
+	if(cBars && bars) {
+		if((Bars = (Bar**)calloc(cBars, sizeof(Bar*)))) {
+			nPoints = cBars;
+			for(i = 0; i < cBars; i++) {
+				if((Bars[i] = bars[i])) Bars[i]->parent = this;
+				bars[i] = 0L;
+				}
+			}
+		}
+	if(cBars && errs) {
+		if((Errors = (ErrorBar**)calloc(cBars, sizeof(Bar*)))) {
+			nPoints = cBars;
+			for(i = 0; i < cBars; i++) {
+				if((Errors[i] = errs[i])) Errors[i]->parent = this;
+				errs[i] = 0L;
+				}
+			}
+		}
+	Id = GO_PLOTSCATT;
+}
+
+PlotScatt::PlotScatt(GraphObj *par, DataObj *d, int nPts, Symbol **sym, DataLine *lin):
+	Plot(par, d)
+{
+	int i;
+
+	FileIO(INIT_VARS);
+	nPoints = nPts;
+	if(Symbols = sym) for(i = 0; i < nPts; i++) if(Symbols[i]) Symbols[i]->parent = this;
+	if(TheLine = lin) TheLine->parent = this;
+	Id = GO_PLOTSCATT;
+}
+
+PlotScatt::PlotScatt(int src):Plot(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+PlotScatt::~PlotScatt()
+{
+	ForEach(FE_FLUSH, 0L, 0L);
+	if(name) free(name);			name=0L;
+	if(x_info) free(x_info);		x_info = 0L;
+	if(y_info) free(x_info);		y_info = 0L;
+	if(data_desc) free(data_desc);	data_desc = 0L;
+	if(x_tv) delete(x_tv);			x_tv = 0L;
+	if(y_tv) delete(y_tv);			y_tv = 0L;
+	Undo.InvalidGO(this);
+}
+
+double
+PlotScatt::GetSize(int select)
+{
+	int i;
+	double ft1, ft2, d;
+
+	switch(select){
+	case SIZE_BARMINX:
+		if(BarDist.fx >= 0.0001) return BarDist.fx;
+		if((!Bars) || (nPoints < 2)) return BarDist.fx = 1.0;
+		ft1 = -HUGE_VAL;	ft2 = HUGE_VAL;		BarDist.fx= HUGE_VAL;
+		for(i = 0; i < nPoints; i++) {
+			if(Bars[i]) {
+				ft2 = Bars[i]->GetSize(SIZE_XPOS);
+				d = fabs(ft2-ft1);
+				if(d != 0.0 && d < BarDist.fx) BarDist.fx = d;
+				}
+			ft1 = ft2;
+			}
+		return BarDist.fx = BarDist.fx > 0.0001 && BarDist.fx != HUGE_VAL  ? BarDist.fx : 1.0;
+	case SIZE_BARMINY:
+		if(BarDist.fy >= 0.0001) return BarDist.fy;
+		if((!Bars) || (nPoints < 2)) return BarDist.fy = 1.0;
+		ft1 = -HUGE_VAL;	ft2 = HUGE_VAL;		BarDist.fy= HUGE_VAL;
+		for(i = 0; i < nPoints; i++) {
+			if(Bars[i]) {
+				ft2 = Bars[i]->GetSize(SIZE_YPOS);
+				d = fabs(ft2-ft1);
+				if(d != 0.0 && d < BarDist.fy) BarDist.fy = d;
+				}
+			ft1 = ft2;
+			}
+		return BarDist.fy = BarDist.fy > 0.0001 && BarDist.fy != HUGE_VAL  ? BarDist.fy : 1.0;
+	default:
+		return Plot::GetSize(select);
+		}
+}
+
+bool
+PlotScatt::SetSize(int select, double value)
+{
+	int i;
+
+	switch(select & 0xfff){
+	case SIZE_BARMINX:
+		BarDist.fx = value;
+		return true;
+	case SIZE_BARMINY:
+		BarDist.fy = value;
+		return true;
+	case SIZE_SYMBOL:		case SIZE_SYM_LINE:
+		if(Symbols)	for(i = 0; i < nPoints; i++) 
+			if(Symbols[i]) Symbols[i]->SetSize(select, value);
+		return true;
+	case SIZE_WHISKER:		case SIZE_WHISKER_LINE:
+	case SIZE_ERRBAR:		case SIZE_ERRBAR_LINE:
+		if(Errors)	for(i = 0; i < nPoints; i++) 
+			if(Errors[i]) Errors[i]->SetSize(select, value);
+		return true;
+	case SIZE_BAR_LINE:		case SIZE_BAR:		case SIZE_XBASE:		case SIZE_YBASE:
+		if(Bars) for(i = 0; i < nPoints; i++) 
+			if(Bars[i]) Bars[i]->SetSize(select, value);
+		return true;
+	case SIZE_LB_XDIST:		case SIZE_LB_YDIST:
+		if(Labels) for(i = 0; i < nPoints; i++)
+			if(Labels[i]) Labels[i]->SetSize(select, value);
+		return true;
+	case SIZE_ARROW_LINE:	case SIZE_ARROW_CAPWIDTH:	case SIZE_ARROW_CAPLENGTH:
+		if(Arrows) for(i = 0; i < nPoints; i++)
+			if(Arrows[i]) Arrows[i]->SetSize(select, value);
+		return true;
+	}
+	return false;
+}
+
+bool
+PlotScatt::SetColor(int select, DWORD col)
+{
+	int i;
+	GraphObj **go = 0L;
+
+	switch(select) {
+	case COL_SYM_LINE:
+	case COL_SYM_FILL:		go = (GraphObj**)Symbols;		break;
+	case COL_WHISKER:
+	case COL_ERROR_LINE:	go = (GraphObj**)Errors;		break;
+	case COL_BAR_LINE:
+	case COL_BAR_FILL:		go = (GraphObj**)Bars;			break;
+	case COL_ARROW:			go = (GraphObj**)Arrows;		break;
+	default:				return false;
+		}
+	if(go) for(i = 0; i < nPoints; i++)
+		if(go[i]) go[i]->SetColor(select, col);
+	return true;
+}
+
+void
+PlotScatt::DoPlot(anyOutput *o)
+{
+	if(!parent) return;
+	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
+	if(use_xaxis || use_yaxis) {
+		ApplyAxes(o);
+		ForEach(FE_PLOT, 0L, o);
+		parent->Command(CMD_AXIS, 0L, o);
+		}
+	else {
+		ForEach(FE_PLOT, 0L, o);
+		}
+	dirty = false;
+}
+
+bool
+PlotScatt::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		if(!CurrGO && ((MouseEvent*)tmpl)->Action == MOUSE_LBUP)
+			return ForEach(cmd, tmpl, o);
+		return false;
+	case CMD_LEGEND:
+		if(((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		if(Bars) for (i = 0; i < nPoints; i++)
+			if(Bars[i]) Bars[i]->Command(cmd, tmpl, o);
+		if(Symbols) {
+			if(TheLine && TheLine->Id == GO_DATALINE) {
+				for (i = 0; i < nPoints && i < 100; i++)
+					if(Symbols[i]) ((Legend*)tmpl)->HasSym(&TheLine->LineDef, Symbols[i], 0L);
+				}
+			else {
+				for (i = 0; i < nPoints && i < 100; i++)
+					if(Symbols[i]) ((Legend*)tmpl)->HasSym(0L, Symbols[i], 0L);
+				}
+			if(TheLine && TheLine->Id == GO_DATAPOLYGON) TheLine->Command(cmd, tmpl, o);
+			}
+		else if(TheLine) TheLine->Command(cmd, tmpl, o);
+		if(Errors) for (i = 0; i < nPoints; i++)
+			if(Errors[i]) Errors[i]->Command(cmd, tmpl, o);
+		break;
+	case CMD_MRK_DIRTY:
+		dirty = true;
+	case CMD_SETSCROLL:		case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		return false;
+	case CMD_USEAXIS:
+		return UseAxis(*((int*)tmpl));
+	case CMD_FLUSH:
+		return ForEach(FE_FLUSH, 0L, 0L);
+	case CMD_TEXTTHERE:
+		if(Labels) for(i = 0; i < nPoints; i++)	if(Labels[i] &&  Labels[i]->Command(cmd, tmpl, o))	return true;
+		return false;
+	case CMD_AUTOSCALE:
+		if(hidden) return false;
+		if(dirty){
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
+			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			}
+		else{
+			if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH && 
+				Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin) {
+				((Plot*)parent)->CheckBounds(Bounds.Xmin, Bounds.Ymin);
+				((Plot*)parent)->CheckBounds(Bounds.Xmax, Bounds.Ymax);
+				return true;
+				}
+			}
+		dirty = false;
+	case CMD_UPDATE:
+		if(cmd == CMD_UPDATE){
+			if (Symbols) SavVarObs((GraphObj **)Symbols, nPoints, UNDO_CONTINUE);
+			if (Bars) SavVarObs((GraphObj **)Bars, nPoints, UNDO_CONTINUE);
+			if (Errors) SavVarObs((GraphObj **)Errors, nPoints, UNDO_CONTINUE);
+			if (Arrows) SavVarObs((GraphObj **)Arrows, nPoints, UNDO_CONTINUE);
+			if (DropLines) SavVarObs((GraphObj **)DropLines, nPoints, UNDO_CONTINUE);
+			if (Labels) SavVarObs((GraphObj **)Labels, nPoints, UNDO_CONTINUE);
+			dirty = true;
+			}
+	case CMD_SET_DATAOBJ:
+		if(cmd == CMD_SET_DATAOBJ) {
+			Id = GO_PLOTSCATT;
+			if(data && data == (DataObj *) tmpl) return true;
+			data = (DataObj *)tmpl;	
+			}
+		ForEach(cmd, tmpl, o);
+		if(cmd == CMD_AUTOSCALE) {
+			if(x_tv) {
+				Bounds.Xmin = 0.5;		Bounds.Xmax = ((double)x_tv->Count())+0.5;
+				}
+			if(y_tv) {
+				Bounds.Ymin = 0.5;		Bounds.Xmax = ((double)y_tv->Count())+0.5;
+				}
+			}
+		if(cmd == CMD_AUTOSCALE && parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH
+			&& Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin) {
+			((Plot*)parent)->CheckBounds(Bounds.Xmin, Bounds.Ymin);
+			((Plot*)parent)->CheckBounds(Bounds.Xmax, Bounds.Ymax);
+			}
+		return true;
+	case CMD_SCALE:
+		return ForEach(cmd, tmpl, o);
+	case CMD_MUTATE:		case CMD_REPL_GO:
+		dirty = true;
+		return ForEach(cmd == CMD_REPL_GO ? FE_REPLGO : FE_MUTATE, tmpl, o);
+	case CMD_SYMTEXT:		case CMD_SYMTEXT_UNDO:	case CMD_SYM_RANGETEXT:
+	case CMD_SYMTEXTDEF:	case CMD_SYM_TYPE:
+		if(Symbols) for(i = 0; i < nPoints; i++)
+			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_SETTEXTDEF:
+		if(Labels) for(i = 0; i < nPoints; i++)
+			if(Labels[i]) Labels[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_DL_LINE:		case CMD_DL_TYPE:
+		if(DropLines) for(i = 0; i < nPoints; i++)
+			if(DropLines[i]) DropLines[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_ERR_TYPE:		case CMD_WHISKER_STYLE:		case CMD_ERRDESC:
+		if(Errors) for(i = 0; i < nPoints; i++) {
+			if(Errors[i]) Errors[i]->Command(cmd, tmpl, o);
+			}
+	case CMD_BAR_TYPE:		case CMD_BAR_FILL:
+		if(Bars) for(i = 0; i < nPoints; i++) {
+			if(Bars[i]) Bars[i]->Command(cmd, tmpl, o);
+			}
+		return true;
+	case CMD_ARROW_TYPE:	case CMD_ARROW_ORG:
+		if(Arrows) for(i = 0; i < nPoints; i++) {
+			if(Arrows[i]) Arrows[i]->Command(cmd, tmpl, o);
+			}
+		return true;
+	case CMD_DELOBJ:
+		dirty = true;
+		if(parent && tmpl && o) return ForEach(FE_DELOBJ, tmpl, o);
+		break;
+	case CMD_SAVE_SYMBOLS:
+		return SavVarObs((GraphObj **)Symbols, nPoints, 0L);
+	case CMD_SAVE_BARS:
+		return SavVarObs((GraphObj **)Bars, nPoints, 0L);
+	case CMD_SAVE_ERRS:
+		return SavVarObs((GraphObj **)Errors, nPoints, 0L);
+	case CMD_SAVE_ARROWS:
+		return SavVarObs((GraphObj **)Arrows, nPoints, 0L);
+	case CMD_SAVE_DROPLINES:
+		return SavVarObs((GraphObj **)DropLines, nPoints, 0L);
+	case CMD_SAVE_LABELS:
+		return SavVarObs((GraphObj **)Labels, nPoints, 0L);
+		}
+	return false;
+}
+
+bool
+PlotScatt::ForEach(int cmd, void *tmp, anyOutput *o)
+{
+	int i, j;
+	GraphObj **obs[] = {(GraphObj**)Symbols, (GraphObj**)Errors, (GraphObj**)Arrows,
+		(GraphObj**)DropLines, (GraphObj**)Labels, (GraphObj**)Bars};
+	GraphObj ***go = 0L;
+	GraphObj **tmpPlots;
+	bool bRedraw;
+
+	switch(cmd) {
+	case FE_MUTATE:
+	case FE_REPLGO:
+		if((tmpPlots = (GraphObj **)tmp) && tmpPlots[0] && tmpPlots[1]) {
+			for(j = 0; j < 6; j++){
+				if(obs[j]) for(i = 0; i < nPoints; i++){
+					if(obs[j][i] && obs[j][i] == tmpPlots[0]) {
+						if(cmd == FE_REPLGO) return ReplaceGO(&obs[j][i], tmpPlots);
+						else {
+							Undo.MutateGO(&obs[j][i], tmpPlots[1], 0L, o);
+							return true;
+							}
+						}
+					}
+				}
+			if(TheLine == tmpPlots[0]){
+				if(cmd == FE_REPLGO) return ReplaceGO((GraphObj**)&TheLine, tmpPlots);
+				else {
+					Undo.MutateGO((GraphObj**)&TheLine, tmpPlots[1], 0L, o);
+					return true;
+					}
+				}
+			}
+		return false;
+	case FE_PARENT:
+		for(j = 0; j < 6; j++){
+			if(obs[j]) for(i = 0; i < nPoints; i++){
+				if(obs[j][i]) obs[j][i]->parent = this;
+				}
+			}
+		if(TheLine) TheLine->parent = this;
+		return true;
+	case CMD_UPDATE:	case CMD_SET_DATAOBJ:	case CMD_AUTOSCALE:		case CMD_SCALE:
+		for(j = 0; j < 6; j++){
+			if(obs[j]) for(i = 0; i < nPoints; i++){
+				if(obs[j][i]) obs[j][i]->Command(cmd, tmp, o);
+				}
+			}
+		if(TheLine) TheLine->Command(cmd, tmp, o);
+		return true;
+	case FE_PLOT:
+		if(TheLine) TheLine->DoPlot(o);
+		for(j = 5; j >= 0; j--){
+			if(obs[j]) for(i = 0; i < nPoints; i++){
+				if(obs[j][i]) obs[j][i]->DoPlot(o);
+				}
+			}
+		return true;
+	case FE_FLUSH:
+		for(j = 0; j < 6; j++){
+			if(obs[j]) {
+				for(i = 0; i < nPoints; i++) if(obs[j][i]) DeleteGO(obs[j][i]);
+				free(obs[j]);	obs[j] = 0L;
+				}
+			}
+		if(ErrRange) free(ErrRange);	if(yRange) free(yRange);
+		if(xRange) free(xRange);		if(LbRange) free(LbRange);
+		ErrRange = yRange = xRange = LbRange = 0L;
+		if(TheLine) DeleteGO(TheLine);
+		Bars = 0L;		Symbols = 0L;		Errors = 0L; 
+		Arrows = 0L;	DropLines = 0L;		Labels = 0L;	TheLine = 0L;
+		return true;
+	case FE_DELOBJ:
+		if(!o) return false;
+		for(j = 0, bRedraw = false, go = 0L; j < 6 && !bRedraw; j++) {
+			if(obs[j]) for(i = 0; i < nPoints; i++){
+				if(obs[j][i]){
+					if(tmp == (void*)obs[j][i]) {
+						o->MrkMode = MRK_NONE;
+						o->MouseCursor(MC_WAIT, true);
+						Undo.DeleteGO(&obs[j][i], 0L, o);
+						switch(j) {
+						case 0: go = (GraphObj***)&Symbols;		break;
+						case 1: go = (GraphObj***)&Errors;		break;
+						case 2: go = (GraphObj***)&Arrows;		break;
+						case 3: go = (GraphObj***)&DropLines;	break;
+						case 4: go = (GraphObj***)&Labels;		break;
+						case 5: go = (GraphObj***)&Bars;		break;
+							}
+						bRedraw = true;
+						break;
+						}
+					}
+				}
+			}
+		if(!bRedraw && TheLine && tmp == (void *) TheLine) {
+			o->MrkMode = MRK_NONE;
+			Undo.DeleteGO((GraphObj**)(&TheLine), 0L, o);
+			bRedraw = true;
+			}
+		if(bRedraw && go) for(i = j = 0; i < nPoints; i++) if(go[0][i]) j++;
+		if(!j) Undo.DropMemory(this, (void**)go, UNDO_CONTINUE);
+		if(bRedraw && dirty) Command(CMD_AUTOSCALE, 0L, o); 
+		if(!Bars && !Symbols && !Errors && !Arrows && !TheLine && !DropLines
+			&& !Labels) parent->Command(CMD_DELOBJ_CONT, this, o);
+		else if(bRedraw) parent->Command(CMD_REDRAW, NULL, o);
+		return bRedraw;
+	default:							//pass command to all objects
+		for(j = 0; j < 6; j++){
+			if(obs[j]) for(i = 0; i < nPoints; i++){
+				if(obs[j][i]) if(obs[j][i]->Command(cmd, tmp, o)) return true;
+				}
+			}
+		if(TheLine) return (TheLine->Command(cmd, tmp, o));
+		return false;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// xyStat is based on scatterplot
+xyStat::xyStat(GraphObj *par, DataObj *d):PlotScatt(par, d, 0L)
+{
+	FileIO(INIT_VARS);
+	Id = GO_XYSTAT;
+}
+
+xyStat::xyStat(int src):PlotScatt(0)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+xyStat::~xyStat()
+{
+	ForEach(FE_FLUSH, 0L, 0L);
+	if(curr_data) delete curr_data;			curr_data = 0L;
+	if(case_prefix) free(case_prefix);		case_prefix = 0L;
+	if(yRange) free(yRange);				yRange = 0L;
+	if(xRange) free(xRange);				xRange = 0L;
+	if(name) free(name);					name=0L;
+	if(x_info) free(x_info);				x_info = 0L;
+	if(y_info) free(x_info);				y_info = 0L;
+	if(x_tv) delete(x_tv);					x_tv = 0;
+	Undo.InvalidGO(this);
+}
+
+bool
+xyStat::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch (cmd) {
+	case CMD_UPDATE:
+		if (Symbols) SavVarObs((GraphObj **)Symbols, nPoints, UNDO_CONTINUE);
+		if (Bars) SavVarObs((GraphObj **)Bars, nPoints, UNDO_CONTINUE);
+		if (Errors) SavVarObs((GraphObj **)Errors, nPoints, UNDO_CONTINUE);
+		if (Labels) SavVarObs((GraphObj **)Labels, nPoints, UNDO_CONTINUE);
+		CreateData();
+		ForEach(CMD_SET_DATAOBJ, curr_data, o);
+		ForEach(CMD_UPDATE, tmpl, o);
+		return dirty = true;
+	case CMD_SET_DATAOBJ:
+		if(cmd == CMD_SET_DATAOBJ) {
+			Id = GO_XYSTAT;
+			if(data && data == (DataObj *) tmpl) return true;
+			if(curr_data) delete curr_data;		curr_data = 0L;
+			data = (DataObj *)tmpl;
+			if(data && !curr_data) CreateData();
+			tmpl = curr_data;
+			}
+		ForEach(cmd, tmpl, o);
+		return true;
+	default:
+		return PlotScatt::Command(cmd, tmpl, o);
+		}
+	return false;
+}
+
+void
+xyStat::CreateData()
+{
+	int i, j, k, l, m, n, *ny, c_num, c_txt, c_dattim;
+	double y, ss, d, lo, hi, **ay, *ax, *tay, *q1, *q2, *q3;
+	lfPOINT *xy;
+	AccRange *rX, *rY;
+	anyResult x_res, y_res;
+
+	if(!data || !xRange || !yRange || !xRange[0] || !yRange[0]) return;
+	if(!(rX = new AccRange(xRange)) || !(rY = new AccRange(yRange))) return;
+	if(!x_info) x_info = rX->RangeDesc(data, 0);	if(!y_info) y_info = rY->RangeDesc(data, 0);
+	m = rX->CountItems();	n = 0;
+	if(m < 2 || !(xy = (lfPOINT*) malloc(m * sizeof(lfPOINT)))) {
+		delete rX;	delete rY;
+		return;
+		}
+	if(x_tv) delete x_tv;					x_tv = 0L;
+	ny = (int*) calloc(m, sizeof(int));
+	ay = (double**) calloc(m, sizeof(double*));
+	ax = (double*) calloc(m, sizeof(double));
+	tay = (double*)malloc(m * sizeof(double));
+	if(!ny || !ay || !ax || !tay) {
+		if(ny) free(ny);	if(ay) free(ay);
+		if(ax) free(ax);	if(tay) free(tay);
+		delete rX;	delete rY;
+		return;
+		}
+	rX->DataTypes(data, &c_num, &c_txt, &c_dattim);
+	if(c_num < 5 && (c_txt + c_dattim) > 5) {
+		x_tv = new TextValue();	
+		}
+	rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);	dirty = true;
+	rX->GetNext(&i, &j);	rY->GetNext(&k, &l);	n=0;
+	do {
+		if(data->GetResult(&x_res, j, i, false) && data->GetResult(&y_res, l, k, false) && y_res.type == ET_VALUE) {
+			xy[n].fy = y_res.value;
+			if(x_tv){ 
+				switch(x_res.type) {
+				case ET_TEXT:
+					xy[n++].fx = x_tv->GetValue(x_res.text);
+					break;
+				case ET_VALUE:	case ET_BOOL:	case ET_DATE:	case ET_TIME:	case ET_DATETIME:
+					TranslateResult(&x_res);
+					xy[n++].fx = x_tv->GetValue(x_res.text);
+					break;
+					}
+				}
+			else if(x_res.type == ET_VALUE) xy[n++].fx = x_res.value;
+			}
+		}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
+	delete rX;			delete rY;
+	if(!n) {
+		if(ny) free(ny);	if(ay) free(ay);
+		if(ax) free(ax);	if(tay) free(tay);
+		return;
+		}
+	SortFpArray(n, xy);
+	for(i = j = 0; i < (n-1); i++, j++) {
+		ax[j] = xy[i].fx;		tay[0] = xy[i].fy;
+		ny[j] = 1;
+		for(k = 1; xy[i+1].fx == xy[i].fx; k++) {
+			tay[k] = xy[i+1].fy;
+			i++;		ny[j]++;
+			}
+		ay[j] = (double*)memdup(tay, k * sizeof(double), 0);
+		}
+	if(xy[i].fx > xy[i-1].fx) {
+		ax[j] = xy[i].fx;		tay[0] = xy[i].fy;
+		ny[j] = 1;
+		ay[j++] = (double*)memdup(tay, sizeof(double), 0);
+		}
+	if(type & 0x0480) {		//medians and/or percentiles required
+		q1 = (double *)malloc(j * sizeof(double));
+		q2 = (double *)malloc(j * sizeof(double));
+		q3 = (double *)malloc(j * sizeof(double));
+		if(q1 && q2 && q3) {
+			for(i = 0; i < j; i++) {
+				if(ny[i] > 1) d_quartile(ny[i], ay[i], q1+i, q2+i, q3+i);
+				else q1[i] = q2[i] = q3[i] = *ay[i];
+				}
+			}
+		else type &= (~0x0480);
+		}
+	else q1 = q2 = q3 = 0L;
+	if((curr_data = curr_data ? curr_data : new DataObj()) && curr_data->Init(j, 6)) {
+		for(i = 0; i < j; i++) curr_data->SetValue(i,0,ax[i]);	// set x-values
+		for(i = 0; i < j; i++) {								// set y-values
+			if(ny[i] > 1) switch(type & 0x00f0) {
+				case 0x0010:	default:
+					curr_data->SetValue(i, 1, y=d_amean(ny[i], ay[i]));
+					break;
+				case 0x0020:
+					curr_data->SetValue(i, 1, y=d_gmean(ny[i], ay[i]));
+					break;
+				case 0x0040:
+					curr_data->SetValue(i, 1, y=d_hmean(ny[i], ay[i]));
+					break;
+				case 0x0080:
+					curr_data->SetValue(i, 1, y=q2[i]);
+					break;
+				}
+			else curr_data->SetValue(i, 1, y= *ay[i]);
+			curr_data->SetValue(i, 4, y);
+			}
+		for(i = 0; i < j; i++) {								// set errors
+			switch(type & 0x1f00) {
+			case 0x0100:	case 0x0200:	case 0x1000:	//SD, SEM, conf. int.
+				if(ny[i] > 1) {
+					ss = d_variance(ny[i], ay[i], &y);
+					switch(type & 0x1f00) {
+					case 0x0100:
+						curr_data->SetValue(i, 2, sqrt(ss));
+						break;
+					case 0x0200:
+						curr_data->SetValue(i, 2, sqrt(ss)/sqrt((double)ny[i]));
+						break;
+					case 0x1000:
+						d = distinv(t_dist, ny[i]-1, 1, 1.0-(ci/100.0), 2.0);
+						curr_data->SetValue(i, 2, d * sqrt(ss)/sqrt((double)ny[i]));
+						break;
+						}
+					}
+				else curr_data->SetValue(i, 2, 0.0);
+				if(curr_data->GetValue(i, 1, &y) && curr_data->GetValue(i, 2, &hi))
+					curr_data->SetValue(i, 4, hi+y);
+				break;
+			case 0x0400:								//percentiles
+				curr_data->SetValue(i, 2, q1[i]);	curr_data->SetValue(i, 3, q3[i]);
+				curr_data->SetValue(i, 4, q3[i]);
+				break;
+			case 0x0800:								//min-max
+				lo = hi = *ay[i];
+				for(k = 1; k < ny[i]; k++) {
+					if(ay[i][k] < lo) lo = ay[i][k];
+					if(ay[i][k] > hi) hi = ay[i][k];
+					}
+				curr_data->SetValue(i, 2, lo);		curr_data->SetValue(i, 3, hi);
+				curr_data->SetValue(i, 4, hi);
+				break;
+				}
+			}
+		if(type & 0x6000) for(i = 0; i < j; i++) {				// number of cases
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "%s%d", case_prefix ? case_prefix : "", ny[i]);
+#else
+			sprintf(TmpTxt, "%s%d", case_prefix ? case_prefix : "", ny[i]);
+#endif
+			curr_data->SetText(i, 5, TmpTxt);
+			}
+		}
+	if(q1) free(q1);	if(q2) free(q2);	if(q3) free(q3);
+	for(i = 0; i < m; i++) if(ay[i]) free(ay[i]);
+	free(tay);	free(ay);	free(ax);	free(ny);	free(xy);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// BarChart is based on scatterplot
+BarChart::BarChart(GraphObj *par, DataObj *d):PlotScatt(par, d, 0L)
+{
+	Id = GO_BARCHART;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Frequenc distribution: bar chart with function
+FreqDist::FreqDist(GraphObj *par, DataObj *d):Plot(par, d)
+{
+	FileIO(INIT_VARS);
+	Id = GO_FREQDIST;
+}
+
+FreqDist::FreqDist(GraphObj *par, DataObj *d, char* range, bool bOnce):Plot(par, d)
+{
+	FileIO(INIT_VARS);
+	if(range && range[0]) {
+		ssRef = (char*)memdup(range, (int)strlen(range)+1, 0);
+		plots = (GraphObj**)calloc(nPlots=3, sizeof(GraphObj*));
+		ProcData(-1);
+		if(bOnce && ssRef) {
+			free(ssRef);		ssRef = 0L;
+			}
+		}
+	Id = GO_FREQDIST;
+}
+
+FreqDist::FreqDist(GraphObj *par, DataObj *d, double *vals, int nvals, int nclasses):Plot(par, d)
+{
+	int i, j;
+	int *cl_data;
+	Bar **bars;
+
+	FileIO(INIT_VARS);
+	ssRef = 0L;
+	plots = (GraphObj**)calloc(nPlots=3, sizeof(GraphObj*));
+	for(i = 0, dmin = HUGE_VAL, dmax = -HUGE_VAL; i < nvals; i++) {
+		if(vals[i] < dmin) dmin = vals[i];
+		if(vals[i] > dmax) dmax = vals[i];
+		}
+	start = dmin;		step = 1.00001*(dmax-dmin)/((double)nclasses);
+	if(!(cl_data = (int*)calloc(nclasses+1, sizeof(int)))) return;
+	for(i = 0; i < nvals; i++) {
+		j = (int)(floor((vals[i] - start)/step));
+		if(j >= 0 && j <= nclasses) cl_data[j]++;
+		}
+	if(cl_data[nclasses]) nclasses++;
+	if(bars = (Bar**)calloc(nclasses, sizeof(Bar*))) for(i = 0; i < nclasses; i++) {
+		if(bars[i] = new Bar(this, 0L, i*step+start, (double)cl_data[i], BAR_VERTB | BAR_RELWIDTH,
+			-1, -1, -1, -1, "Count")) bars[i]->SetSize(SIZE_BAR, 100.0);
+		}
+	//create bar chart
+	if(bars && (plots[0] = new PlotScatt(this, data, nclasses, bars, 0L))){
+		plots[0]->Command(CMD_BAR_FILL, &BarFill, 0L);
+		plots[0]->SetColor(COL_BAR_LINE, BarLine.color);
+		plots[0]->SetSize(SIZE_BAR_LINE, BarLine.width);
+		}
+	if(plots[0]){
+		Bounds.Xmin = dmin;		Bounds.Xmax = dmax;
+		plots[0]->Command(CMD_AUTOSCALE, 0L, 0L);
+		}
+	free(cl_data);
+	Id = GO_FREQDIST;
+}
+
+FreqDist::FreqDist(int src):Plot(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+FreqDist::~FreqDist()
+{
+	int i;
+
+	if(curr_data) delete(curr_data);		curr_data = 0L;
+	if(ssRef) free(ssRef);					ssRef = 0L;
+	if(plots) {
+		for(i = 0; i < nPlots; i++) if(plots[i]) DeleteGO(plots[i]);
+		free(plots);						plots=0L;
+		}
+	if(name) free(name);					name=0L;
+	if(x_info) free(x_info);				x_info = 0L;
+	if(y_info) free(y_info);				y_info = 0L;
+	if(x_tv) delete x_tv;					x_tv = 0L;
+}
+
+void
+FreqDist::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(!plots || !o || !data || !parent) return;
+	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
+	if(use_xaxis || use_yaxis) ApplyAxes(o);
+	for(i = 0; i < nPlots; i++) {
+		if(plots[i]) {
+			if(plots[i]->Id >= GO_PLOT && plots[i]->Id < GO_GRAPH){
+				if(((Plot*)plots[i])->hidden == 0) plots[i]->DoPlot(o);
+				}
+			else plots[i]->DoPlot(o);
+			}
+		}
+	if(use_xaxis || use_yaxis) parent->Command(CMD_AXIS, 0L, o);
+}
+
+bool
+FreqDist::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		if(hidden || ((MouseEvent*)tmpl)->Action != MOUSE_LBUP || CurrGO) return false;
+	case CMD_LEGEND:
+		if(plots) for(i = 0; i < nPlots; i++) {
+			if(plots[i]){
+				if(plots[i]->Id >= GO_PLOT && plots[i]->Id < GO_GRAPH){
+					if(((Plot*)plots[i])->hidden == 0) plots[i]->Command(cmd, tmpl, o);
+					}
+				else plots[i]->Command(cmd, tmpl, o);
+				}
+			}
+		return false;
+	case CMD_SCALE:
+		if(plots) for(i = 0; i < nPlots; i++) if(plots[i]) plots[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_DELOBJ:
+		if(plots && tmpl && parent) for(i = 0; i < nPlots; i++) if(plots[i]){
+			if(tmpl == (void*)plots[i]) {
+				DeleteGO(plots[i]);	plots[i]=0L;
+				if(i == 1) type=0;
+				parent->Command(CMD_REDRAW, 0L, o);
+				return true;
+				}
+			}
+		return false;
+	case CMD_UPDATE:
+		if(data && parent && plots) {
+			ProcData(0);
+			if(!curr_data) return false;
+			for(i = 0; i < nPlots; i++) if(plots[i]) {
+				plots[i]->Command(CMD_SET_DATAOBJ, curr_data, o);
+				plots[i]->Command(CMD_UPDATE, 0L, o);
+				}
+			}
+		return false;
+	case CMD_SET_DATAOBJ:
+		Id = GO_FREQDIST;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_MRK_DIRTY:
+		dirty = true;
+	case CMD_SETSCROLL:		case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		return false;
+	case CMD_USEAXIS:
+		return UseAxis(*((int*)tmpl));
+	case CMD_AUTOSCALE:
+		if(hidden) return false;
+		if(dirty){
+			Bounds.Xmin = HUGE_VAL;		Bounds.Ymin = 0;
+			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			if(dmax > dmin) {
+				Bounds.Xmin = dmin;		Bounds.Xmax = dmax;
+				}
+			}
+		else{
+			if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH && 
+				Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin) {
+				((Plot*)parent)->CheckBounds(Bounds.Xmin, Bounds.Ymin);
+				((Plot*)parent)->CheckBounds(Bounds.Xmax, Bounds.Ymax);
+				return true;
+				}
+			}
+		dirty = false;
+		if(plots) for(i = 0; i < nPlots; i++) {
+			if(plots[i]){
+				if(plots[i]->Id >= GO_PLOT && plots[i]->Id < GO_GRAPH){
+					if(((Plot*)plots[i])->hidden == 0) plots[i]->Command(cmd, tmpl, o);
+					}
+				else plots[i]->Command(cmd, tmpl, o);
+				}
+			}
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH && 
+			Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin) {
+			((Plot*)parent)->CheckBounds(Bounds.Xmin, Bounds.Ymin);
+			((Plot*)parent)->CheckBounds(Bounds.Xmax, Bounds.Ymax);
+			return true;
+			}
+	case CMD_DROP_PLOT:		case CMD_DROP_GRAPH:
+		if(tmpl && plots && nPlots >1) {
+			if(plots[1]) DeleteGO(plots[1]);
+			plots[1] = (GraphObj*)tmpl;		dirty = true;		plots[1]->parent = this;
+			plots[1]->Command(CMD_SET_DATAOBJ, curr_data, o);
+			Command(CMD_AUTOSCALE, 0L, o);
+			return true;
+			}
+		return false;
+		}
+	return false;
+}
+
+void
+FreqDist::ProcData(int sel)
+{
+	AccRange *ar;
+	int nv, i, j, r, c, ncl, *f_data, cb_f, size_fo, pos_fo, c_num, c_txt, c_dattim;
+	double min, max, sum, mean, sd, tmp, *s_data, *t_data, lstep;
+	double chi2, df, x, y;
+	anyResult *result;
+	bool bValid = false;
+	Bar **bars = 0L;
+	anyResult res;
+	TextDEF td;
+	char *fo, *fdesc = 0L, formula[500];
+
+	if(!parent || !data || !ssRef || !plots) return;
+	if(curr_data) delete(curr_data);		curr_data = 0L;
+	if(x_tv) delete x_tv;					x_tv = 0L;
+	if((curr_data = new DataObj()) && (ar = new AccRange(ssRef))) {
+		if(!y_info && (y_info = (char*)malloc(25*sizeof(char)))) rlp_strcpy(y_info, 25, "No. of observations");
+		dmin = HUGE_VAL, dmax = -HUGE_VAL;
+		ar->DataTypes(data, &c_num, &c_txt, &c_dattim);
+		if(c_num < 5 && (c_txt + c_dattim) > 5) {
+			x_tv = new TextValue();		dmin = 0.0;
+			}
+		//copy spreadsheet data into array
+		nv = ar->CountItems();			ar->GetFirst(&c, &r);
+		if(!(s_data = (double*)malloc(nv * sizeof(double))) 
+			|| !(t_data = (double*)malloc(nv * sizeof(double)))) {
+			delete(ar);					return;
+			}
+		for(sum = 0.0, nv = 0; ar->GetNext(&c, &r); ) if(data->GetResult(&res, r, c, false)) {
+			if(x_tv){
+				switch(res.type) {
+				case ET_TEXT:
+					if((tmp = x_tv->GetValue(res.text))> 0.0)bValid = true;
+					else bValid = false;				break;
+				case ET_VALUE:	case ET_DATE:	case ET_TIME:	case ET_DATETIME:	case ET_BOOL:
+					TranslateResult(&res);
+					if((tmp = x_tv->GetValue(res.text))> 0.0)bValid = true;
+					else bValid = false;				break;
+				default: 
+					bValid = false;						break;
+					}
+				}
+			else {
+				if(res.type == ET_VALUE) {
+					tmp = res.value;		bValid = true;
+					}
+				else bValid = false;
+				}
+			if(bValid) {
+				if(tmp > dmax) dmax = tmp;	if(tmp < dmin) dmin = tmp;
+				s_data[nv] = tmp;
+				switch (type & 0xff){
+				case 2:
+					if(tmp > 0.0) t_data[nv] = log(tmp);
+					else nv--;
+					break;
+				default:	t_data[nv] = tmp;		break;
+					}
+				nv++;
+				}
+			}
+		delete(ar);
+		if(!nv || dmin >= dmax) {
+			free(s_data);		s_data = 0L;		free(t_data);		t_data = 0L;
+			return;
+			}
+		min = dmin;		max = dmax;
+		lstep = (max-min)/100.0;
+		d_variance(nv, t_data, &mean, &sd);
+		sd = sqrt(sd/((double)(nv-1)));
+		step = fabs(step);
+		if(x_tv) {
+			start = 0.5;	step = 1.0;		max+= 0.5;
+			}
+		else if(sel == -1) {
+			start = min;	step = (max - min)/(step != 0.0 ? step : 7.0);
+			}
+		else if(sel == -2) {
+			min = start;
+			}
+		ncl = (int)(floor((max-start)/step))+1;
+		if(plots[0] &&	(max > (Bounds.Xmax+step/2.0) || min < (Bounds.Xmin-step/2.0))) {
+			DeleteGO(plots[0]);		plots[0] = 0L;
+			}
+		if(!plots[0])bars = (Bar**)calloc(ncl, sizeof(Bar*));
+		f_data = (int*)calloc(ncl+1, sizeof(int));
+		for(i = 0; i < nv; i++) {
+			j = (int)(floor((s_data[i] - start)/step));
+			if(j >= 0 && j < ncl) f_data[j]++;
+			else if(s_data[i] == max) f_data[j-1]++;
+			}
+		if(f_data[ncl]) ncl++;
+		curr_data->Init(ncl, 2);
+		//create data object containg the counts / bin and bars
+		for(i = 0; i< ncl; i++) {
+			curr_data->SetValue(i, 0, tmp = start + i * step + step * .5);
+			curr_data->SetValue(i, 1, (double)f_data[i]);
+			if(bars) {
+				if(bars[i] = new Bar(this, 0L, tmp, (double)f_data[i], BAR_VERTB | BAR_RELWIDTH,
+					0, i, 1, i, "Count")) bars[i]->SetSize(SIZE_BAR, 100.0);
+				}
+			}
+		free(s_data);		free(t_data);		free(f_data);
+		//create bar chart
+		if(bars && (plots[0] = new PlotScatt(this, data, ncl, bars, 0L))){
+			plots[0]->Command(CMD_BAR_FILL, &BarFill, 0L);
+			plots[0]->SetColor(COL_BAR_LINE, BarLine.color);
+			plots[0]->SetSize(SIZE_BAR_LINE, BarLine.width);
+			}
+		if(plots[0]){
+			Bounds.Xmin = dmin;		Bounds.Xmax = dmax;
+			plots[0]->Command(CMD_SET_DATAOBJ, curr_data, 0L);
+			plots[0]->Command(CMD_AUTOSCALE, 0L, 0L);
+			}
+		//create function
+		if((type & 0xff) && (fo = (char*)malloc(size_fo = 1000))) {
+			pos_fo = rlp_strcpy(fo, 1000, (char*) "[1=Function]\nx1=");
+			add_dbl_to_buff(&fo, &pos_fo, &size_fo, min, true);
+			add_to_buff(&fo, &pos_fo, &size_fo,(char*)"\nx2=", 4);
+			add_dbl_to_buff(&fo, &pos_fo, &size_fo, max, true);
+			add_to_buff(&fo, &pos_fo, &size_fo,(char*)"\nxstep=", 7);
+			add_dbl_to_buff(&fo, &pos_fo, &size_fo, lstep, true);
+			add_to_buff(&fo, &pos_fo, &size_fo,(char*)"\nLine=", 6);
+			add_dbl_to_buff(&fo, &pos_fo, &size_fo, DefSize(SIZE_DATA_LINE), true);
+			add_to_buff(&fo, &pos_fo, &size_fo,(char*)" 6 0x000000ff 0x0\n", 18);
+			cb_f = 0;
+			switch (type & 0xff){
+			case 2:				//lognormal
+#ifdef USE_WIN_SECURE
+				cb_f = sprintf_s(formula, 500, "%g*lognormfreq(x,%g,%g)",nv*step, mean, sd);
+#else
+				cb_f = sprintf(formula,"%g*lognormfreq(x,%g,%g)",nv*step, mean, sd);
+#endif
+				fdesc = (char*)"Desc=\"Lognormal Dist.\"\n";
+				break;
+			case 3:				//exponential
+#ifdef USE_WIN_SECURE
+				cb_f = sprintf_s(formula, 500, "%g*expfreq(x,%g)",nv*step, 1.0/mean);
+#else
+				cb_f = sprintf(formula,"%g*expfreq(x,%g)",nv*step, 1.0/mean);
+#endif
+				fdesc = (char*)"Desc=\"Exponential Dist.\"\n";
+				break;
+			case 4:				//rectangular
+#ifdef USE_WIN_SECURE
+				cb_f = sprintf_s(formula, 500, "%g*%g*(x>=%g&&x<=%g)",nv*step, 1.0/(dmax-dmin), dmin, dmax);
+#else
+				cb_f = sprintf(formula,"%g*%g*(x>=%g&&x<=%g)",nv*step, 1.0/(dmax-dmin), dmin, dmax);
+#endif
+				fdesc = (char*)"Desc=\"Rectangular Dist.\"\n";
+				break;
+			case 5:				//chi-square
+#ifdef USE_WIN_SECURE
+				cb_f = sprintf_s(formula, 500, "%g*chifreq(x,%g)",nv*step, mean);
+#else
+				cb_f = sprintf(formula,"%g*chifreq(x,%g)",nv*step, mean);
+#endif
+				fdesc = (char*)"Desc=\"Chi<sup>2</sup> Dist.\"\n";
+				break;
+			case 10:			//binomial
+#ifdef USE_WIN_SECURE
+				cb_f = sprintf_s(formula, 500, "%g*binomfreq(x,%g,%g)*(x>0)",nv*step, dmax, mean/dmax);
+#else
+				cb_f = sprintf(formula,"%g*binomfreq(x,%g,%g)*(x>0)",nv*step, dmax, mean/dmax);
+#endif
+				fdesc = (char*)"Desc=\"Binomial Dist.\"\n";
+				break;
+			case 11:			//poisson
+#ifdef USE_WIN_SECURE
+				cb_f = sprintf_s(formula, 500, "%g*poisfreq(x,%g)*(x>0)",nv*step, mean);
+#else
+				cb_f = sprintf(formula,"%g*poisfreq(x,%g)*(x>0)",nv*step, mean);
+#endif
+				fdesc = (char*)"Desc=\"Poisson Dist.\"\n";
+				break;
+			default:			//normal
+#ifdef USE_WIN_SECURE
+				cb_f = sprintf_s(formula, 500, "%g*normfreq(x,%g,%g)",nv*step, mean, sd);
+#else
+				cb_f = sprintf(formula,"%g*normfreq(x,%g,%g)",nv*step, mean, sd);
+#endif
+				fdesc = (char*)"Desc=\"Normal Dist.\"\n";
+				break;
+				}
+			if(cb_f) {
+				add_to_buff(&fo, &pos_fo, &size_fo, "f_xy=\"y=" , 8);
+				add_to_buff(&fo, &pos_fo, &size_fo, formula , cb_f);
+				add_to_buff(&fo, &pos_fo, &size_fo, "\\n\"\n" , 4);
+				}
+			if(fdesc)add_to_buff(&fo, &pos_fo, &size_fo, fdesc, 0);
+			OpenGraph(this, 0L, (unsigned char *)fo, false);
+			free(fo);							chi2 = df = 0.0;
+			//calculate chi-square test of fit
+			if(curr_data) for(i = 0; i< ncl; i++) {
+				if(curr_data->GetValue(i,0, &x) && curr_data->GetValue(i,1, &y)){
+#ifdef USE_WIN_SECURE
+					sprintf_s(TmpTxt, TMP_TXT_SIZE, "x=%g;%s", x, formula);
+#else
+					sprintf(TmpTxt, "x=%g;%s", x, formula);
+#endif
+					result = do_formula(curr_data, TmpTxt);
+					if(result->type == ET_VALUE && fabs(result->value) > 0.0) {
+						tmp = y-result->value;	
+						tmp = (tmp*tmp)/result->value;
+						chi2 += tmp;			df += 1.0;
+						}
+					}
+				}
+			//report result of the chi-square test
+			if(chi2 > 0.0 && parent && (fo = (char*)malloc(size_fo = 1000))) {
+				tmp = chi_dist(chi2, df-1.0, 1.0);
+				pos_fo = rlp_strcpy(fo, 1000, (char*)"chi<sup> 2</sup> =");
+				add_dbl_to_buff(&fo, &pos_fo, &size_fo, chi2, true);
+				add_to_buff(&fo, &pos_fo, &size_fo, (char*)", n =", 5);
+				add_dbl_to_buff(&fo, &pos_fo, &size_fo, df, true);
+				add_to_buff(&fo, &pos_fo, &size_fo, (char*)", df =", 6);
+				add_dbl_to_buff(&fo, &pos_fo, &size_fo, df-1.0, true);
+				add_to_buff(&fo, &pos_fo, &size_fo, (char*)", p =", 5);
+				if(tmp < 0.0001) {
+					pos_fo--;	add_to_buff(&fo, &pos_fo, &size_fo, (char*)"< 0.0001", 8);
+					}
+				else add_dbl_to_buff(&fo, &pos_fo, &size_fo, tmp, true);
+				if(!plots[2]) {
+					x = (parent->GetSize(SIZE_GRECT_RIGHT) - parent->GetSize(SIZE_GRECT_LEFT))/2.0;
+					y = parent->GetSize(SIZE_GRECT_BOTTOM) - DefSize(SIZE_TEXT)*5;
+					y -= parent->GetSize(SIZE_DRECT_TOP);
+					td.Align = TXA_VTOP | TXA_HCENTER;			td.ColBg = 0x00ffffffL;
+					td.ColTxt = 0x00ff0000L;					td.Font = FONT_HELVETICA;
+					td.fSize = DefSize(SIZE_TEXT);			td.iSize = 0;
+					td.Mode = TXM_TRANSPARENT;					td.RotBL = td.RotCHAR = 0.0;
+					td.Style = TXS_NORMAL;						td.text = 0L;
+					plots[2] = new Label(this, data, x, y, &td, 0x0L);
+					plots[2]->moveable = 1;
+					}
+				plots[2]->Command(CMD_SETTEXT, fo, 0L);			free(fo);
+				}
+			}
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Regression line and symbols
+Regression::Regression(GraphObj *par, DataObj *d):Plot(par, d)
+{
+	FileIO(INIT_VARS);
+	Id = GO_REGRESSION;
+}
+
+Regression::Regression(int src):Plot(0L, 0L)
+{
+	int i;
+
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		//now set parent in all children
+		if(rLine) rLine->parent = this;
+		if(sde) sde->parent = this;
+		if(Symbols)
+			for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->parent = this;
+		}
+}
+
+Regression::~Regression()
+{
+	Command(CMD_FLUSH, 0L, 0L);
+	if(name) free(name);		name=0L;
+	Undo.InvalidGO(this);
+}
+
+double
+Regression::GetSize(int select)
+{
+	return Plot::GetSize(select);
+}
+
+bool
+Regression::SetSize(int select, double value)
+{
+	int i;
+
+	switch(select & 0xfff){
+	case SIZE_SYMBOL:
+	case SIZE_SYM_LINE:
+		if(Symbols)	for(i = 0; i < nPoints; i++) 
+			if(Symbols[i]) Symbols[i]->SetSize(select, value);
+		return true;
+	}
+	return false;
+}
+
+bool
+Regression::SetColor(int select, DWORD col)
+{
+	int i;
+	switch(select) {
+	case COL_SYM_LINE:
+	case COL_SYM_FILL:
+		if(Symbols) for(i = 0; i < nPoints; i++) 
+			if(Symbols[i]) Symbols[i]->SetColor(select, col);
+		return true;
+	}
+	return false;
+}
+
+void
+Regression::DoPlot(anyOutput *o)
+{
+	int i;
+	
+
+	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
+	if(use_xaxis || use_yaxis) ApplyAxes(o);
+	if(sde){
+		if(rLine && sde->Command(CMD_DROP_OBJECT, rLine, o)) rLine = 0L;
+		sde->DoPlot(o);
+		}
+	if(rLine) rLine->DoPlot(o);
+	if(Symbols)	for(i = 0; i < nPoints; i++) 
+		if(Symbols[i]) Symbols[i]->DoPlot(o);
+	if(use_xaxis || use_yaxis) parent->Command(CMD_AXIS, 0L, o);
+	dirty = false;
+}
+
+bool
+Regression::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i, j;
+	static MouseEvent *mev;
+	bool bEmpty,bRedraw = false;
+	LineDEF *ld;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		mev = (MouseEvent *) tmpl;
+		switch(mev->Action) {
+		case MOUSE_LBUP:
+			if(Symbols) for (i = nPoints-1; i >= 0; i--)
+				if(Symbols[i] && Symbols[i]->Command(cmd, tmpl, o))return true;
+			if(rLine && rLine->Command(cmd, tmpl, o)) return true;
+			if(sde && sde->Command(cmd, tmpl, o)) return true;
+			break;
+			}
+		break;
+	case CMD_LEGEND:
+		if(tmpl && ((GraphObj*)tmpl)->Id == GO_LEGEND && rLine && rLine->Id == GO_REGLINE) {
+			ld = rLine->GetLine();
+			if(Symbols) {
+				for (i = 0; i < nPoints && i < 100; i++)
+					if(Symbols[i]) ((Legend*)tmpl)->HasSym(ld, Symbols[i], "Regression");
+				}
+			else ((Legend*)tmpl)->HasFill(ld, 0L, "Regression");
+			return true;
+			}
+		return false;
+	case CMD_MRK_DIRTY:
+		if(rLine || sde) Recalc();
+		dirty = true;
+	case CMD_SETSCROLL:
+	case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		return false;
+	case CMD_USEAXIS:
+		UseAxis(*((int*)tmpl));
+		return true;
+	case CMD_DROP_OBJECT:
+		if(tmpl && ((GraphObj*)tmpl)->Id == GO_REGLINE && !rLine) {
+			rLine = (RegLine *)tmpl;
+			rLine->parent = this;
+			return true;
+			}
+		break;
+	case CMD_FLUSH:
+		if(yRange) free(yRange);		if(xRange) free(xRange);
+		yRange = xRange = 0L;
+		if(rLine) DeleteGO(rLine);
+		if(sde) DeleteGO(sde);
+		rLine = 0L;		sde = 0L;
+		if(Symbols) for (i = nPoints-1; i >= 0; i--)
+			if(Symbols[i]) DeleteGO(Symbols[i]);
+		if(Symbols) free(Symbols);
+		Symbols = 0L;
+		return true;
+	case CMD_AUTOSCALE:
+		if(dirty){
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
+			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			}
+		else return true;
+		dirty = false;
+	case CMD_SET_DATAOBJ:
+		if(cmd == CMD_SET_DATAOBJ) {
+			Id = GO_REGRESSION;
+			data = (DataObj *)tmpl;	
+			}
+		if(rLine) rLine->Command(cmd, tmpl, o);
+		if(Symbols) for (i = 0; i < nPoints; i++)
+			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_SCALE:
+		if(rLine) rLine->Command(cmd, tmpl, o);
+		if(sde) sde->Command(cmd, tmpl, o);
+	case CMD_SYMTEXT:		case CMD_SYM_RANGETEXT:
+	case CMD_SYMTEXTDEF:	case CMD_SYM_TYPE:
+		if(Symbols) for(i = 0; i < nPoints; i++)
+			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_SAVE_SYMBOLS:
+		return SavVarObs((GraphObj **)Symbols, nPoints, 0L);
+	case CMD_UPDATE:
+		if(Symbols) {
+			SavVarObs((GraphObj**)Symbols, nPoints, UNDO_CONTINUE);
+			for(i = 0; i < nPoints; i++)
+				if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
+			if(rLine || sde) Recalc();
+			return true;
+			}
+		return false;
+	case CMD_DELOBJ:
+		if(!parent || !o) return false;
+		dirty = bEmpty = bRedraw = false;
+		if(Symbols) for(i = 0; i < nPoints; i++)
+			if(Symbols[i] && (void*)Symbols[i] == tmpl) {
+				bRedraw = true;
+				o->HideMark();
+				Undo.DeleteGO((GraphObj**)(&Symbols[i]), 0L, o);
+				for(j = 0, bEmpty = true; j < nPoints; j++) {
+					if(Symbols[j]) {
+						bEmpty = false;
+						break;
+						}
+					}
+				if(!bEmpty && dirty) Command(CMD_AUTOSCALE, 0L, o);
+				break;
+				}
+		if(rLine && (void*)rLine == tmpl) {
+			Undo.DeleteGO((GraphObj**)(&rLine), 0L, o);
+			if(!Symbols && !sde) parent->Command(CMD_DELOBJ_CONT, this, o);
+			else bRedraw = true;
+			}
+		if(sde && (void*)sde == tmpl) {
+			sde->Command(CMD_RMU, 0L, 0L);
+			Undo.DeleteGO((GraphObj**)(&sde), 0L, o);
+			if(!Symbols && !rLine) parent->Command(CMD_DELOBJ_CONT, this, o);
+			else bRedraw = true;
+			}
+		if(bEmpty && Symbols) {
+			Undo.DropMemory(this, (void**)(&Symbols), UNDO_CONTINUE);
+			bRedraw = false;
+			if(!rLine && !sde) parent->Command(CMD_DELOBJ_CONT, this, o);
+			else bRedraw = true;
+			}
+		if(bRedraw)parent->Command(CMD_REDRAW, 0L, o);
+		return bRedraw;
+		}
+	return false;
+}
+
+void
+Regression::Recalc()
+{
+	int i, j;
+	long n;
+	bool dValid;
+	lfPOINT *val;
+
+	if(nPoints <2 || !Symbols || 
+		!(val = (lfPOINT*)calloc(nPoints, sizeof(lfPOINT)))) return;
+	for(i = 0, n = 0; i < nPoints; i++){
+		if(Symbols[i] && Symbols[i]->Id == GO_SYMBOL) {
+			val[j = (int)n].fx = Symbols[i]->GetSize(SIZE_XPOS);
+			val[j].fy = Symbols[i]->GetSize(SIZE_YPOS);
+			dValid = true;
+			switch(type & 0x700) {
+			case 0x100:					//logarithmic x
+				if(dValid = val[j].fx > defs.min4log) val[j].fx = log10(val[j].fx);
+				break;
+			case 0x200:					//reciprocal x
+				if(dValid = fabs(val[j].fx) >defs.min4log) val[j].fx = 1.0/val[j].fx;
+				break;
+			case 0x300:					//square root x
+				if(dValid = fabs(val[j].fx) >defs.min4log) val[j].fx = sqrt(val[j].fx);
+				break;
+				}
+			if(dValid) switch(type & 0x7000) {
+			case 0x1000:				//logarithmic y
+				if(dValid = val[j].fy > defs.min4log) val[j].fy = log10(val[j].fy);
+				break;
+			case 0x2000:				//reciprocal y
+				if(dValid = fabs(val[j].fy) >defs.min4log) val[j].fy = 1.0/val[j].fy;
+				break;
+			case 0x3000:				//square root y
+				if(dValid = fabs(val[j].fy) >defs.min4log) val[j].fy = sqrt(val[j].fy);
+				break;
+				}
+			if(dValid) n++;
+			}
+		}
+	if(sde && sde->Id == GO_SDELLIPSE) sde->Recalc(val, n);
+	if(rLine && rLine->Id == GO_REGLINE) rLine->Recalc(val, n);
+	free(val);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// BubblePlot is a Plot-Class
+BubblePlot::BubblePlot(GraphObj *par, DataObj *d):Plot(par, d)
+{
+	FileIO(INIT_VARS);
+	Id = GO_BUBBLEPLOT;
+	if (!d && parent) parent->Command(CMD_DELOBJ, this, NULL);
+}
+
+BubblePlot::BubblePlot(int src):Plot(0L, 0L)
+{
+	long i;
+
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		BubbleFill.hatch = &BubbleFillLine;
+		if(Bubbles)for(i = 0; i< nPoints; i++) {
+			if(Bubbles[i])Bubbles[i]->parent = this;
+			}
+		}
+}
+
+BubblePlot::~BubblePlot()
+{
+	int i;
+
+	if(Bubbles) {
+		for(i = 0; i < nPoints; i++) if(Bubbles[i]) DeleteGO(Bubbles[i]);
+		free (Bubbles);
+		}
+	if(name) free(name);		name=0L;
+	Undo.InvalidGO(this);
+}
+
+DWORD
+BubblePlot::GetColor(int select)
+{
+	switch(select) {
+	case COL_BUBBLE_FILL:			return BubbleFill.color;
+	case COL_BUBBLE_LINE:			return BubbleLine.color;
+	case COL_BUBBLE_FILLLINE:	   	return BubbleFillLine.color;
+	default:
+		return Plot::GetColor(select);
+	}
+}
+
+void
+BubblePlot::DoPlot(anyOutput *o)
+{
+	int i;
+
+	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
+	if(use_xaxis || use_yaxis) {
+		ApplyAxes(o);
+		if(Bubbles) for(i = 0; i < nPoints; i++) 
+			if(Bubbles[i]) Bubbles[i]->DoPlot(o);
+		parent->Command(CMD_AXIS, 0L, o);
+		}
+	else {
+		if(Bubbles) for(i = 0; i < nPoints; i++) 
+			if(Bubbles[i]) Bubbles[i]->DoPlot(o);
+		}
+	dirty = false;
+}
+
+bool
+BubblePlot::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	static MouseEvent *mev;
+	GraphObj **tmpPlots;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		mev = (MouseEvent *) tmpl;
+		switch(mev->Action) {
+		case MOUSE_LBUP:
+			//select objects invers to plot order
+			if(Bubbles && !CurrGO) for(i = nPoints-1; i >=0; i--)
+				if(Bubbles[i]) if(Bubbles[i]->Command(cmd, tmpl, o))break;
+			break;
+			}
+		break;
+	case CMD_REPL_GO:
+		if((tmpPlots = (GraphObj **)tmpl) && tmpPlots[0] && tmpPlots[1] && Bubbles) {
+			for(i = 0; i < nPoints; i++) if(Bubbles[i] && Bubbles[i] == tmpPlots[0]) { 
+				return ReplaceGO((GraphObj**)&Bubbles[i], tmpPlots);
+				}
+			}
+		return false;
+	case CMD_LEGEND:
+		if(((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		if(Bubbles) for (i = 0; i < nPoints; i++)
+			if(Bubbles[i]) Bubbles[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_SCALE:
+		if(!tmpl) return false;
+		BubbleLine.width *= ((scaleINFO*)tmpl)->sy.fy;
+		BubbleLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		BubbleFillLine.width *= ((scaleINFO*)tmpl)->sy.fy;
+		BubbleFillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		BubbleFill.scale *= ((scaleINFO*)tmpl)->sy.fy;
+		if(Bubbles) for(i = 0; i < nPoints; i++)
+			if(Bubbles[i]) Bubbles[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_MRK_DIRTY:
+		dirty = true;
+	case CMD_SETSCROLL:		case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		return false;
+	case CMD_USEAXIS:
+		UseAxis(*((int*)tmpl));
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_BUBBLEPLOT;
+		data = (DataObj *)tmpl;
+	case CMD_UPDATE:
+		if(cmd == CMD_UPDATE && Bubbles) SavVarObs((GraphObj **)Bubbles, nPoints, UNDO_CONTINUE);
+	case CMD_AUTOSCALE:
+		if(cmd == CMD_AUTOSCALE && Bubbles) {
+			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
+			}
+	case CMD_BUBBLE_ATTRIB:		case CMD_BUBBLE_TYPE:		case CMD_BUBBLE_FILL:
+	case CMD_BUBBLE_LINE:
+		if(Bubbles) for(i = 0; i < nPoints; i++)
+			if(Bubbles[i]) Bubbles[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_DELOBJ:
+		if(Bubbles && parent) for(i = 0; i < nPoints; i++) {
+			o->HideMark();
+			if(Bubbles[i] && tmpl == (void *)Bubbles[i]) {
+				Undo.DeleteGO((GraphObj**)(&Bubbles[i]), 0L, o);
+				parent->Command(CMD_REDRAW, 0L, o);
+				return true;
+				}
+			}
+		break;
+	case CMD_SAVE_SYMBOLS:
+		return SavVarObs((GraphObj **)Bubbles, nPoints, 0L);
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// PolarPlot is a Plot-Class
+PolarPlot::PolarPlot(GraphObj *par, DataObj *d):Plot(par, d)
+{
+	FileIO(INIT_VARS);
+	Id = GO_POLARPLOT;
+	if (!d && parent) parent->Command(CMD_DELOBJ, this, NULL);
+}
+
+PolarPlot::PolarPlot(int src):Plot(0L, 0L)
+{
+	int i;
+
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		Fill.hatch = &FillLine;
+		//now set parent in all children
+		if(Plots) 
+			for(i = 0; i < nPlots; i++) if(Plots[i]) Plots[i]->parent = this;
+		if(Axes) 
+			for(i = 0; i < nAxes; i++) if(Axes[i]) Axes[i]->parent = this;
+		}
+}
+
+PolarPlot::~PolarPlot()
+{
+	int i;
+
+	if(Plots){
+		for(i = 0; i < nPlots; i++) if(Plots[i]) DeleteGO(Plots[i]);
+		free(Plots);		Plots = 0L;
+		}
+	if(Axes){
+		for(i = 0; i < nAxes; i++) if(Axes[i]) DeleteGO(Axes[i]);
+		free(Axes);			Axes = 0L;
+		}
+	if(name) free(name);	name=0L;
+	Undo.InvalidGO(this);
+}
+
+double
+PolarPlot::GetSize(int select)
+{
+	switch(select) {
+	case SIZE_BOUNDS_XMIN:		return Bounds.Xmin;
+	case SIZE_BOUNDS_XMAX:		return Bounds.Xmax;
+	case SIZE_BOUNDS_YMIN:		return Bounds.Ymin;
+	case SIZE_BOUNDS_YMAX:		return Bounds.Ymax;
+	case SIZE_BOUNDS_LEFT:		return (Axes && Axes[0])?(((Axis*)Axes[0])->GetAxis())->min:0.0;
+	case SIZE_BOUNDS_RIGHT:		return (Axes && Axes[0])?(((Axis*)Axes[0])->GetAxis())->max:0.0;
+	case SIZE_BOUNDS_TOP:		return (Axes && Axes[1])?(((Axis*)Axes[1])->GetAxis())->max:0.0;
+	case SIZE_BOUNDS_BOTTOM:	return (Axes && Axes[1])?(((Axis*)Axes[1])->GetAxis())->min:0.0;
+	case SIZE_YAXISX:
+		if(!CurrDisp) return 0.0;
+		if((((Axis*)Axes[1])->GetAxis())->flags & AXIS_X_DATA) 
+			return CurrDisp->fx2fix((((Axis*)Axes[1])->GetAxis())->loc[0].fx);
+		else return CurrDisp->co2fix((((Axis*)Axes[1])->GetAxis())->loc[0].fx);
+	case SIZE_XAXISY:
+		if(!CurrDisp) return 0.0;
+		if((((Axis*)Axes[0])->GetAxis())->flags & AXIS_Y_DATA) 
+			return CurrDisp->fy2fiy((((Axis*)Axes[0])->GetAxis())->loc[0].fy);
+		else return CurrDisp->co2fiy((((Axis*)Axes[0])->GetAxis())->loc[0].fy);
+	case SIZE_XCENTER:			return (((Axis*)Axes[0])->GetAxis())->Center.fx;
+	case SIZE_YCENTER:			return (((Axis*)Axes[0])->GetAxis())->Center.fy;
+	default:
+		if(parent) return parent->GetSize(select);
+		}
+	return DefSize(select);
+}
+
+void
+PolarPlot::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(o) CurrDisp = o;
+	else return;
+	if(!parent) return;
+	CurrRect.Xmin = CurrRect.Xmax = (((Axis*)Axes[1])->GetAxis())->Center.fx + 
+		parent->GetSize(SIZE_GRECT_LEFT);
+	CurrRect.Xmin -= (((Axis*)Axes[0])->GetAxis())->Radius;	
+	CurrRect.Xmax += (((Axis*)Axes[0])->GetAxis())->Radius;
+	CurrRect.Ymin = CurrRect.Ymax = (((Axis*)Axes[0])->GetAxis())->Center.fy +
+		parent->GetSize(SIZE_GRECT_TOP);
+	CurrRect.Ymin -= (((Axis*)Axes[0])->GetAxis())->Radius;
+	CurrRect.Ymax += (((Axis*)Axes[0])->GetAxis())->Radius;
+	(((Axis*)Axes[0])->GetAxis())->Start = ((((Axis*)Axes[0])->GetAxis())->flags & AXIS_INVERT) ? -offs : offs;
+	o->SetRect(CurrRect, defs.cUnits, ((Axis*)Axes[0])->GetAxis(), ((Axis*)Axes[1])->GetAxis());
+	o->SetFill(&Fill);
+	if(Axes) for(i = 0; i < nAxes; i++) {
+		if(i == 1) {
+			if(!(type & 0x01) && Axes[i]) Axes[i]->DoPlot(o);
+			}
+		else if(Axes[i]) Axes[i]->DoPlot(o);
+		}
+	if(Plots) for(i = 0; i < nPlots; i++) if(Plots[i]) {
+		if(Plots[i]->Id >= GO_PLOT && Plots[i]->Id < GO_GRAPH) {
+			if(((Plot*)Plots[i])->hidden == 0) Plots[i]->DoPlot(o);
+			}
+		else Plots[i]->DoPlot(o);
+		}
+	rDims.left = o->co2ix(CurrRect.Xmin);	rDims.right = o->co2ix(CurrRect.Xmax);
+	rDims.top = o->co2iy(CurrRect.Ymin);	rDims.bottom = o->co2iy(CurrRect.Ymax);
+	if(parent) parent->Command(CMD_AXIS, 0L, o);
+}
+
+bool
+PolarPlot::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	GraphObj **tmpPlots;
+	int i;
+	AxisDEF *ad0, *ad1;
+	double tmp;
+
+	switch (cmd) {
+	case CMD_CONFIG:
+		Config();
+		return true;
+	case CMD_SCALE:
+		FillLine.width *= ((scaleINFO*)tmpl)->sy.fy;
+		FillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;
+		if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->Command(cmd, tmpl, o);
+		if(Plots) for(i = 0; i < nPlots; i++) if(Plots[i]) Plots[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_POLARPLOT;
+		data = (DataObj *)tmpl;	
+	case CMD_UPDATE:
+		if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->Command(cmd, tmpl, o);
+	case CMD_LEGEND:
+		if(Plots) for(i = 0; i < nPlots; i++) if(Plots[i]) Plots[i]->Command(cmd, tmpl, o);
+		return false;
+	case CMD_OBJTREE:
+		if(!tmpl) return false;
+		if(Plots) for(i = 0; i < nPlots; i++) if(Plots[i]) 
+			((ObjTree*)tmpl)->Command(CMD_UPDATE, Plots[i], 0L);
+		return true;
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		if(o) switch(((MouseEvent*)tmpl)->Action) {
+		case MOUSE_LBUP:
+			if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i])
+				if(Axes[i]->Command(cmd, tmpl, o))return true;
+			if(Plots) for(i = 0; i < nPlots; i++) if(Plots[i]) 
+				Plots[i]->Command(cmd, tmpl, o);
+			if(!CurrGO && IsInRect(&rDims, ((MouseEvent*)tmpl)->x, ((MouseEvent*)tmpl)->y)){
+				CurrGO = this;
+				o->ShowMark(&rDims, MRK_INVERT);
+				return true;
+				}
+			break;
+			}
+		return false;
+	case CMD_REPL_GO:
+		if(!(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
+		if(Axes) for(i = 0; i < nAxes; i++) if(Axes[i] && Axes[i] == tmpPlots[0]){
+			return ReplaceGO(&Axes[i], tmpPlots);
+			}
+		break;
+	case CMD_SETSCROLL:		case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		return false;
+	case CMD_DELOBJ_CONT:	case CMD_DELOBJ:
+		if(Plots && nPlots) for(i = 0; i < nPlots; i++) if(tmpl == (void*)Plots[i]) {
+			Undo.DeleteGO((GraphObj**)(&Plots[i]), cmd == CMD_DELOBJ_CONT ? UNDO_CONTINUE : 0L, o);
+			if(parent)parent->Command(CMD_REDRAW, NULL, o);
+			return true;
+			}
+		if(Axes && nAxes > 1 && (tmpl == (void*)Axes[0] || tmpl == (void*)Axes[1]))
+			InfoBox("Axes required for scaling\ncannot be deleted.");
+		break;
+	case CMD_AXIS:		//axis changed: reconstruct corresponding axis
+		if(Axes && nAxes >1 && tmpl && Axes[0] && Axes[1]) {
+			ad0 = ((Axis*)Axes[0])->GetAxis();		ad1 = ((Axis*)Axes[1])->GetAxis();
+			if(tmpl == Axes[0]) {
+				CheckNewFloat(&ad1->loc[1].fy, ad1->loc[1].fy, ad0->Center.fy, this, UNDO_CONTINUE);
+				tmp = ad1->loc[1].fy - ad0->Radius;
+				CheckNewFloat(&ad1->loc[0].fy, ad1->loc[0].fy, tmp, this, UNDO_CONTINUE);
+				}
+			if(tmpl == Axes[1]) {
+				CheckNewFloat(&ad0->Center.fy, ad0->Center.fy, ad1->loc[1].fy, this, UNDO_CONTINUE);
+				tmp = fabs(ad1->loc[1].fy - ad1->loc[0].fy);
+				CheckNewFloat(&ad0->Radius, ad0->Radius, tmp, this, UNDO_CONTINUE);
+				}
+			}
+		break;	
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// BoxPlot is a Plot-Class
+BoxPlot::BoxPlot(GraphObj *par, DataObj *d):Plot(par, d)
+{
+	FileIO(INIT_VARS);
+	Id = GO_BOXPLOT;
+}
+
+BoxPlot::BoxPlot(int src):Plot(0L, 0L)
+{
+	int i;
+
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		//now set parent in all children
+		if(Boxes) 
+			for(i = 0; i < nPoints; i++) if(Boxes[i]) Boxes[i]->parent = this;
+		if(Whiskers) 
+			for(i = 0; i < nPoints; i++) if(Whiskers[i]) Whiskers[i]->parent = this;
+		if(Symbols) 
+			for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->parent = this;
+		if(Labels) 
+			for(i = 0; i < nPoints; i++) if(Labels[i]) Labels[i]->parent = this;
+		if(TheLine) TheLine->parent = this;
+		}
+}
+
+BoxPlot::BoxPlot(GraphObj *par, DataObj *dt, int mode, int c1, int c2, int c3, char *box_name):Plot(par, dt)
+{
+	int i, nr, cb;
+	lfPOINT fp;
+
+	FileIO(INIT_VARS);		Id = GO_BOXPLOT;	fp.fx = fp.fy = 0.0;	cb = 0;
+	if(data && data->GetSize(&i, &nr)) {
+		nPoints = nr;		if(box_name) cb = (int)strlen(box_name);
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
+		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		if(Boxes = (Box**)calloc(nr, sizeof(Box*))) for(i = 0; i < nr; i++) {
+			if(mode == 1) Boxes[i] = new Box(this, data, fp, fp, BAR_RELWIDTH, c1, i, c2, i, c1, i, c3, i);
+			else Boxes[i] = new Box(this, data, fp, fp, BAR_RELWIDTH, c2, i, c1, i, c3, i, c1, i);
+			if(box_name && box_name[0] && Boxes[i]) Boxes[i]->name = (char*)memdup(box_name, cb+1, 0);
+			}
+		}
+}
+
+BoxPlot::~BoxPlot()
+{
+	int i;
+
+	if(Whiskers) {
+		for(i = 0; i < nPoints; i++) if(Whiskers[i]) DeleteGO(Whiskers[i]);
+		free (Whiskers);
+		}
+	if(Boxes) {
+		for(i = 0; i < nPoints; i++) if(Boxes[i]) DeleteGO(Boxes[i]);
+		free (Boxes);
+		}
+	if(Symbols) {
+		for(i = 0; i < nPoints; i++) if(Symbols[i]) DeleteGO(Symbols[i]);
+		free (Symbols);
+		}
+	if(Labels) {
+		for(i = 0; i < nPoints; i++) if(Labels[i]) DeleteGO(Labels[i]);
+		free (Labels);
+		}
+	if(TheLine) DeleteGO(TheLine);
+	if(curr_data) delete curr_data;		curr_data = 0L;
+	if(xRange) free(xRange);			xRange = 0L;
+	if(yRange) free(yRange);			yRange = 0L;
+	if(x_info) free(x_info);			x_info = 0L;
+	if(y_info) free(y_info);			y_info = 0L;
+	if(case_prefix) free(case_prefix);	case_prefix = 0L;
+	if(name) free(name);				name = 0L;
+	if(x_tv) delete(x_tv);				x_tv = 0;
+	if(y_tv) delete(y_tv);				y_tv = 0;
+	Undo.InvalidGO(this);
+}
+
+double
+BoxPlot::GetSize(int select)
+{
+	int i;
+	double ft1, ft2, d;
+
+	switch(select){
+	case SIZE_BOXMINX:
+		if(BoxDist.fx >= 0.0001) return BoxDist.fx;
+		if((!Boxes) || (nPoints < 2)) return BoxDist.fx = 1.0;
+		ft1 = -HUGE_VAL;	ft2 = HUGE_VAL;		BoxDist.fx= HUGE_VAL;
+		for(i = 0; i < nPoints; i++) {
+			if(Boxes[i]) {
+				ft2 = Boxes[i]->GetSize(SIZE_XPOS);
+				d = fabs(ft2-ft1);
+				if(d != 0.0 && d < BoxDist.fx) BoxDist.fx = d;
+				}
+			ft1 = ft2;
+			}
+		return BoxDist.fx = BoxDist.fx > 0.0001 && BoxDist.fx != HUGE_VAL  ? BoxDist.fx : 1.0;
+	case SIZE_BOXMINY:
+		if(BoxDist.fy >= 0.0001) return BoxDist.fy;
+		if((!Boxes) || (nPoints < 2)) return BoxDist.fy = 1.0;
+		ft1 = -HUGE_VAL;	ft2 = HUGE_VAL;		BoxDist.fy= HUGE_VAL;
+		for(i = 0; i < nPoints; i++) {
+			if(Boxes[i]) {
+				ft2 = Boxes[i]->GetSize(SIZE_YPOS);
+				d = fabs(ft2-ft1);
+				if(d != 0.0 && d < BoxDist.fy) BoxDist.fy = d;
+				}
+			ft1 = ft2;
+			}
+		return BoxDist.fy = BoxDist.fy > 0.0001 && BoxDist.fy != HUGE_VAL  ? BoxDist.fy : 1.0;
+	default:
+		return Plot::GetSize(select);
+		}
+}
+
+bool
+BoxPlot::SetSize(int select, double value)
+{
+	int i;
+
+	switch(select & 0xfff){
+	case SIZE_SYMBOL:		case SIZE_SYM_LINE:
+		if(Symbols) for(i = 0; i < nPoints; i++) 
+			if(Symbols[i]) Symbols[i]->SetSize(select, value);
+		return true;
+	case SIZE_WHISKER:		case SIZE_WHISKER_LINE:
+		if(Whiskers) for(i = 0; i < nPoints; i++) 
+			if(Whiskers[i]) Whiskers[i]->SetSize(select, value);
+		return true;
+	case SIZE_BOX:			case SIZE_BOX_LINE:
+		if(Boxes) for(i = 0; i < nPoints; i++) 
+			if(Boxes[i]) Boxes[i]->SetSize(select, value);
+		return true;
+	case SIZE_LB_XDIST:		case SIZE_LB_YDIST:
+		if(Labels) for(i = 0; i < nPoints; i++)
+			if(Labels[i]) Labels[i]->SetSize(select, value);
+		return true;
+	}
+	return false;
+}
+
+bool
+BoxPlot::SetColor(int select, DWORD col)
+{
+	int i;
+
+	switch(select) {
+	case COL_SYM_LINE:		case COL_SYM_FILL:
+		if(Symbols)	for(i = 0; i < nPoints; i++) 
+			if(Symbols[i]) Symbols[i]->SetColor(select, col);
+		return true;
+	case COL_WHISKER:
+		if(Whiskers) for(i = 0; i < nPoints; i++)
+			if(Whiskers[i]) Whiskers[i]->SetColor(select, col);
+		return true;
+	case COL_BOX_LINE:
+		if(Boxes) for(i = 0; i < nPoints; i++)
+			if(Boxes[i]) Boxes[i]->SetColor(select, col);
+		return true;
+	default:
+		return false;
+		}
+}
+
+void
+BoxPlot::DoPlot(anyOutput *o)
+{
+	if(!parent || !o) return;
+	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
+	if(use_xaxis || use_yaxis) ApplyAxes(o);
+	ForEach(FE_PLOT, 0L, o);
+	dirty = false;
+	if(use_xaxis || use_yaxis)parent->Command(CMD_AXIS, 0L, o);
+}
+
+bool
+BoxPlot::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		if(!CurrGO && ((MouseEvent*)tmpl)->Action == MOUSE_LBUP) return ForEach(cmd, tmpl, o);
+		return false;
+	case CMD_TEXTTHERE:
+		if(Labels) for(i = 0; i < nPoints; i++)	if(Labels[i] &&  Labels[i]->Command(cmd, tmpl, o))	return true;
+		return false;
+	case CMD_LEGEND:
+		if(((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		if(Symbols) {
+			if(TheLine && TheLine->Id == GO_DATALINE) {
+				for (i = 0; i < nPoints && i < 100; i++)
+					if(Symbols[i]) ((Legend*)tmpl)->HasSym(&TheLine->LineDef, Symbols[i], 0L);
+				}
+			else {
+				for (i = 0; i < nPoints && i < 100; i++)
+					if(Symbols[i]) ((Legend*)tmpl)->HasSym(0L, Symbols[i], 0L);
+				}
+			if(TheLine && TheLine->Id == GO_DATAPOLYGON) TheLine->Command(cmd, tmpl, o);
+			}
+		else if(TheLine) TheLine->Command(cmd, tmpl, o);
+		if(Boxes) for (i = 0; i < nPoints; i++)	if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
+		if(Whiskers) for (i = 0; i < nPoints; i++)	if(Whiskers[i]) Whiskers[i]->Command(cmd, tmpl, o);
+		break;
+	case CMD_SET_DATAOBJ:
+		Id = GO_BOXPLOT;		data = (DataObj *)tmpl;		dirty = true;
+		if(type && xRange && yRange && data) {		//Stat. - Plot ?
+			CreateData();
+			return ForEach(CMD_SET_DATAOBJ, curr_data, o);
+			}
+	case CMD_SCALE:
+		return ForEach(cmd, tmpl, o);
+	case CMD_AUTOSCALE:
+		if(hidden) return false;
+		if(dirty) {
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
+			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			}
+		else{
+			if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH && 
+				Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin) {
+				((Plot*)parent)->CheckBounds(Bounds.Xmin, Bounds.Ymin);
+				((Plot*)parent)->CheckBounds(Bounds.Xmax, Bounds.Ymax);
+				return true;
+				}
+			}
+		dirty = false;
+		ForEach(cmd, tmpl, o);
+		if(x_tv) {
+			Bounds.Xmin = 0.5;		Bounds.Xmax = ((double)x_tv->Count())+0.5;
+			}
+		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH
+			&& Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin){
+			((Plot*)parent)->CheckBounds(Bounds.Xmin, Bounds.Ymin);
+			((Plot*)parent)->CheckBounds(Bounds.Xmax, Bounds.Ymax);
+			}
+		return true;
+	case CMD_UPDATE:
+		if(parent) {
+			if(Boxes) SavVarObs((GraphObj **)Boxes, nPoints, UNDO_CONTINUE);
+			if(Whiskers) SavVarObs((GraphObj **)Whiskers, nPoints, UNDO_CONTINUE);
+			if(Symbols) SavVarObs((GraphObj **)Symbols, nPoints, UNDO_CONTINUE);
+			if(Labels) SavVarObs((GraphObj **)Labels, nPoints, UNDO_CONTINUE); 
+			}
+		if(type && xRange && yRange) {		//Stat. - Plot ?
+			CreateData();
+			ForEach(CMD_SET_DATAOBJ, curr_data, o);
+			}
+		ForEach(cmd, tmpl, o);
+		return dirty = true;
+	case CMD_USEAXIS:
+		UseAxis(*((int*)tmpl));
+		return true;
+	case CMD_MRK_DIRTY:
+		dirty = true;
+	case CMD_SETSCROLL:		case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		return false;
+	case CMD_SETTEXTDEF:
+		if(Labels) for(i = 0; i < nPoints; i++)
+			if(Labels[i]) Labels[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_DELOBJ:
+		if(ForEach(FE_DELOBJ, tmpl, o)) {
+			parent->Command(CMD_REDRAW, 0L, o);
+			return true;
+			}
+		return false;
+	case CMD_SYMTEXT:		case CMD_SYM_RANGETEXT:
+	case CMD_SYMTEXTDEF:	case CMD_SYM_TYPE:
+		if(Symbols) for(i = 0; i < nPoints; i++)
+			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_SAVE_SYMBOLS:
+		return SavVarObs((GraphObj **)Symbols, nPoints, 0L);
+	case CMD_SAVE_BARS:
+		return SavVarObs((GraphObj **)Boxes, nPoints, 0L);
+	case CMD_SAVE_BARS_CONT:
+		return SavVarObs((GraphObj **)Boxes, nPoints, UNDO_CONTINUE);
+	case CMD_SAVE_ERRS:
+		return SavVarObs((GraphObj **)Whiskers, nPoints, 0L);
+	case CMD_SAVE_LABELS:
+		return SavVarObs((GraphObj **)Labels, nPoints, 0L);
+	case CMD_BOX_TYPE:
+		BoxDist.fy = BoxDist.fx = 0.0;
+	case CMD_BOX_FILL:
+		if(Boxes) for (i = 0; i < nPoints; i++)
+			if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_WHISKER_STYLE:		case CMD_ERRDESC:
+		if(Whiskers) for (i = 0; i < nPoints; i++)
+			if(Whiskers[i]) Whiskers[i]->Command(cmd, tmpl, o);
+		return true;
+		}
+	return false;
+}
+
+bool
+BoxPlot::ForEach(int cmd, void *tmpl, anyOutput *o)
+{
+	GraphObj ***pobs[] = {(GraphObj***)&Boxes, (GraphObj***)&Whiskers, 
+		(GraphObj***)&Symbols, (GraphObj***)&Labels};
+	GraphObj **p;
+	int i, j;
+	bool bRet;
+
+	switch(cmd) {
+	case FE_DELOBJ:
+		if(!o || !parent || !tmpl) return false;
+		for(i = 0; i < 4; i++) {
+			if(DeleteGOL(pobs[i], nPoints, (GraphObj*) tmpl, o)) return true;
+			}
+		if(TheLine && tmpl == (void *) TheLine) {
+			Undo.DeleteGO((GraphObj**)(&TheLine), 0L, o);
+			return true;
+			}
+		break;
+	case FE_PLOT:
+		if(TheLine) TheLine->DoPlot(o);
+		for(i = 0; i < 4; i++){
+			if(p= *pobs[i]) for(j = 0; j < nPoints; j++) {
+				if(p[j]) p[j]->DoPlot(o);
+				}
+			}
+		return true;
+	case CMD_MOUSE_EVENT:				//invers to plot order
+		for(i = 3; i >= 0; i--){
+			if(p= *pobs[i]) for(j = nPoints-1; j >= 0; j--) {
+				if(p[j]) {
+					bRet = p[j]->Command(cmd, tmpl, o);
+					if(bRet && cmd == CMD_MOUSE_EVENT) return true;
+					}
+				}
+			}
+		if(TheLine) return TheLine->Command(cmd, tmpl, o);
+		return false;
+	default:							//pass command to all objects
+		for(i = 0; i < 4; i++){
+			if(p= *pobs[i]) for(j = 0; j < nPoints; j++) {
+				if(p[j]) {
+					bRet = p[j]->Command(cmd, tmpl, o);
+					}
+				}
+			}
+		if(TheLine) return TheLine->Command(cmd, tmpl, o);
+		return false;
+		}
+	return false;
+}
+
+void
+BoxPlot::CreateData()
+{
+	int i, j, k, l, m, n, *ny, c_num, c_txt, c_dattim;
+	double y, ss, d, lo, hi, **ay, *ax, *tay, *q1, *q2, *q3;
+	lfPOINT *xy;
+	AccRange *rX, *rY;
+	anyResult x_res, y_res;
+
+	if(curr_data) delete curr_data;			curr_data = 0L;
+	if(!data || !xRange || !yRange || !xRange[0] || !yRange[0]) return;
+	if(!(rX = new AccRange(xRange)) || !(rY = new AccRange(yRange))) return;
+	m = rX->CountItems();	n = 0;
+	if(m < 2 || !(xy = (lfPOINT*) malloc(m * sizeof(lfPOINT)))) {
+		delete rX;	delete rY;
+		return;
+		}
+	if(x_tv) delete x_tv;					x_tv = 0L;
+	ny = (int*) calloc(m, sizeof(int));
+	ay = (double**) calloc(m, sizeof(double*));
+	ax = (double*) calloc(m, sizeof(double));
+	tay = (double*)malloc(m * sizeof(double));
+	if(!ny || !ay || !ax || !tay) {
+		if(ny) free(ny);	if(ay) free(ay);
+		if(ax) free(ax);	if(tay) free(tay);
+		delete rX;	delete rY;
+		return;
+		}
+	rX->DataTypes(data, &c_num, &c_txt, &c_dattim);
+	if(c_num < 5 && (c_txt + c_dattim) > 5) {
+		x_tv = new TextValue();	
+		}
+	rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
+	rX->GetNext(&i, &j);	rY->GetNext(&k, &l);	n=0;
+	do {
+		if(data->GetResult(&x_res, j, i, false) && data->GetResult(&y_res, l, k, false) && y_res.type == ET_VALUE) {
+			xy[n].fy = y_res.value;
+			if(x_tv){ 
+				switch(x_res.type) {
+				case ET_TEXT:
+					xy[n++].fx = x_tv->GetValue(x_res.text);
+					break;
+				case ET_VALUE:	case ET_BOOL:	case ET_DATE:	case ET_TIME:	case ET_DATETIME:
+					TranslateResult(&x_res);
+					xy[n++].fx = x_tv->GetValue(x_res.text);
+					break;
+					}
+				}
+			else if(x_res.type == ET_VALUE) xy[n++].fx = x_res.value;
+			}
+		}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
+	delete rX;			delete rY;
+	if(!n) {
+		if(ny) free(ny);	if(ay) free(ay);
+		if(ax) free(ax);	if(tay) free(tay);
+		return;
+		}
+	SortFpArray(n, xy);
+	for(i = j = 0; i < (n-1); i++, j++) {
+		ax[j] = xy[i].fx;		tay[0] = xy[i].fy;
+		ny[j] = 1;
+		for(k = 1; xy[i+1].fx == xy[i].fx; k++) {
+			tay[k] = xy[i+1].fy;
+			i++;		ny[j]++;
+			}
+		ay[j] = (double*)memdup(tay, k * sizeof(double), 0);
+		}
+	if(xy[i].fx > xy[i-1].fx) {
+		ax[j] = xy[i].fx;		tay[0] = xy[i].fy;
+		ny[j] = 1;
+		ay[j++] = (double*)memdup(tay, sizeof(double), 0);
+		}
+	if((type & 0x0004) == 0x0004 || (type & 0x0030) == 0x0030 || (type & 0x0300) == 0x0300) {
+		//medians and/or percentiles required
+		q1 = (double *)malloc(j * sizeof(double));
+		q2 = (double *)malloc(j * sizeof(double));
+		q3 = (double *)malloc(j * sizeof(double));
+		if(q1 && q2 && q3) {
+			for(i = 0; i < j; i++) {
+				if(ny[i] > 1) d_quartile(ny[i], ay[i], q1+i, q2+i, q3+i);
+				else q1[i] = q2[i] = q3[i] = *ay[i];
+				}
+			}
+		else type = 0;
+		}
+	else q1 = q2 = q3 = 0L;
+	if(type && (curr_data = new DataObj()) && curr_data->Init(j, 8)) {
+		for(i = 0; i < j; i++) curr_data->SetValue(i,0,ax[i]);	// set x-values
+		for(i = 0; i < j; i++) {								// set means
+			if(ny[i] > 1) switch(type & 0x000f) {
+				case 0x0001:	default:
+					curr_data->SetValue(i, 1, y=d_amean(ny[i], ay[i]));
+					break;
+				case 0x0002:
+					curr_data->SetValue(i, 1, y=d_gmean(ny[i], ay[i]));
+					break;
+				case 0x0003:
+					curr_data->SetValue(i, 1, y=d_hmean(ny[i], ay[i]));
+					break;
+				case 0x0004:
+					curr_data->SetValue(i, 1, y=q2[i]);
+					break;
+				}
+			else curr_data->SetValue(i, 1, y= *ay[i]);
+			curr_data->SetValue(i, 6, y);						//label's y
+			}
+		if((type & 0x00f0) == 0x0010 || (type & 0x00f0) == 0x0020 || (type & 0x00f0) == 0x0050
+			|| (type & 0x0f0f) == 0x0201 || (type & 0x0f0f) == 0x0501) for(i = 0; i < j; i++) {
+			// set SD, SE, Conf. Intervall
+			if(ny[i] > 1) {
+				ss = sqrt(d_variance(ny[i], ay[i], &y));
+				}
+			else {
+				y = *ay[i];		ss = 0.0;
+				}
+			//Box info is in cols 2 & 3
+			if((type & 0x00f0) == 0x0010) {
+				curr_data->SetValue(i, 2, y - ss);	curr_data->SetValue(i, 3, y + ss);
+				}
+			else if((type & 0x00f0) == 0x0020) {
+				curr_data->SetValue(i, 2, y - ss/sqrt((double)ny[i]));	
+				curr_data->SetValue(i, 3, y + ss/sqrt((double)ny[i]));
+				}
+			else if((type & 0x00f0) == 0x0050) {
+				d = ny[i] > 1 ? distinv(t_dist, ny[i]-1, 1, 1.0-(ci_box/100.0), 2.0) : 0;
+				curr_data->SetValue(i, 2, y - d*ss/(double)sqrt((double)ny[i]));	
+				curr_data->SetValue(i, 3, y + d*ss/(double)sqrt((double)ny[i]));
+				}
+			//Whisker info is in cols 4 & 5
+			if((type & 0x0f0f) == 0x0101) {
+				curr_data->SetValue(i, 4, y - ss);	curr_data->SetValue(i, 5, y + ss);
+				}
+			else if((type & 0x0f0f) == 0x0201) {
+				curr_data->SetValue(i, 4, y - ss/sqrt((double)ny[i]));
+				curr_data->SetValue(i, 5, y + ss/sqrt((double)ny[i]));
+				}
+			else if((type & 0x0f0f) == 0x0501) {
+				d = ny[i] > 1 ? distinv(t_dist, ny[i]-1, 1, 1.0-(ci_err/100.0), 2.0) : 0;
+				curr_data->SetValue(i, 4, y - d*ss/sqrt((double)ny[i]));
+				curr_data->SetValue(i, 5, y + d*ss/sqrt((double)ny[i]));
+				}
+			}
+		if((type & 0x00f0) == 0x0040 || (type & 0x0f00) == 0x0400) for(i = 0; i < j; i++) {
+			// set min and max
+			lo = hi = *ay[i];
+ 			if(ny[i] > 1) {
+				for(k = 1; k < ny[i]; k++) {
+					if(ay[i][k] < lo) lo = ay[i][k];
+					if(ay[i][k] > hi) hi = ay[i][k];
+					}
+				}
+			if((type & 0x00f0) == 0x0040) {
+				curr_data->SetValue(i, 2, lo);	curr_data->SetValue(i, 3, hi);
+				}
+			if((type & 0x0f00) == 0x0400) {
+				curr_data->SetValue(i, 4, lo);	curr_data->SetValue(i, 5, hi);
+				}
+			}
+		if(q1 && q3 && ((type & 0x00f0) == 0x0030 || (type & 0x0f00) == 0x0300)) for(i = 0; i < j; i++) {
+			// percentiles ....
+			if((type & 0x00f0) == 0x0030) {
+				curr_data->SetValue(i, 2, q1[i]);	curr_data->SetValue(i, 3, q3[i]);
+				}
+			if((type & 0x0f00) == 0x0300) {
+				curr_data->SetValue(i, 4, q1[i]);	curr_data->SetValue(i, 5, q3[i]);
+				}
+			}
+		if(type & 0xc000) for(i = 0; i < j; i++) {
+			//labels ...
+			if((type & 0x4000) && curr_data->GetValue(i, 5, &y)) curr_data->SetValue(i, 6, y);
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "%s%d", case_prefix ? case_prefix : "", ny[i]);
+#else
+			sprintf(TmpTxt, "%s%d", case_prefix ? case_prefix : "", ny[i]);
+#endif
+			curr_data->SetText(i, 7, TmpTxt);
+			}
+		}
+	else {
+		if(curr_data) delete curr_data;
+		curr_data = 0L;
+		}
+	if(q1) free(q1);	if(q2) free(q2);	if(q3) free(q3);
+	for(i = 0; i < m; i++) if(ay[i]) free(ay[i]);
+	free(tay);	free(ay);	free(ax);	free(ny);	free(xy);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Density distribution plot
+DensDisp::DensDisp(GraphObj *par, DataObj *d):Plot(par, d)
+{
+	FileIO(INIT_VARS);
+	Id = GO_DENSDISP;
+	if (!d && parent) parent->Command(CMD_DELOBJ, this, NULL);
+}
+
+DensDisp::DensDisp(int src):Plot(0L, 0L)
+{
+	int i;
+
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		//now set parent in all children
+		if(Boxes) 
+			for(i = 0; i < nPoints; i++) if(Boxes[i]) Boxes[i]->parent = this;
+		}
+}
+
+DensDisp::~DensDisp()
+{
+	int i;
+
+	if(Boxes) {
+		for(i = 0; i < nPoints; i++) if(Boxes[i]) DeleteGO(Boxes[i]);
+		free (Boxes);
+		}
+	if(yRange) free(yRange);		if(xRange) free(xRange);
+	yRange = xRange = 0L;
+	if(name) free(name);			name=0L;
+	if(x_info) free(x_info);		x_info = 0L;
+	if(y_info) free(y_info);		y_info = 0L;
+	Undo.InvalidGO(this);
+}
+
+bool
+DensDisp::SetSize(int select, double value)
+{
+	int i;
+
+	switch(select & 0xfff){
+	case SIZE_BOX_LINE:
+		if(Boxes) for(i = 0; i < nPoints; i++) 
+			if(Boxes[i]) Boxes[i]->SetSize(select, value);
+		return true;
+	}
+	return false;
+}
+
+bool
+DensDisp::SetColor(int select, DWORD col)
+{
+	int i;
+
+	switch(select) {
+	case COL_BOX_LINE:
+		if(Boxes) for(i = 0; i < nPoints; i++)
+			if(Boxes[i]) Boxes[i]->SetColor(select, col);
+		return true;
+	default:
+		return false;
+		}
+}
+
+void
+DensDisp::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(!parent) return;
+	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
+	if(use_xaxis || use_yaxis) {
+		ApplyAxes(o);
+		if(Boxes) for(i = 0; i < nPoints; i++) 
+			if(Boxes[i]) Boxes[i]->DoPlot(o);
+		parent->Command(CMD_AXIS, 0L, o);
+		}
+	else {
+		if(Boxes) for(i = 0; i < nPoints; i++) 
+			if(Boxes[i]) Boxes[i]->DoPlot(o);
+		}
+	dirty = false;
+}
+
+bool
+DensDisp::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	MouseEvent *mev;
+
+	switch (cmd) {
+	case CMD_MRK_DIRTY:
+		dirty = true;
+	case CMD_SETSCROLL:		case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		return false;
+	case CMD_SCALE:
+		DefLine.width *= ((scaleINFO*)tmpl)->sy.fy;		DefLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		DefFillLine.width *= ((scaleINFO*)tmpl)->sy.fy;	DefFillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		DefFill.scale *= ((scaleINFO*)tmpl)->sy.fy;
+		for(i = 0; i < nPoints; i++){
+			if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
+			}
+		return true;
+	case CMD_USEAXIS:
+		UseAxis(*((int*)tmpl));
+		return true;
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		mev = (MouseEvent *) tmpl;
+		switch(mev->Action) {
+		case MOUSE_LBUP:
+			for(i = nPoints-1; i >= 0 && !CurrGO; i--) {
+				if(Boxes[i] && Boxes[i]->Command(cmd, tmpl, o))return true;
+				}
+			break;
+			}
+		break;
+	case CMD_SET_DATAOBJ:
+		for(i = 0; i < nPoints; i++) if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
+		Id = GO_DENSDISP;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_DELOBJ:
+		if(!parent || !o) return false;
+		if(DeleteGOL((GraphObj***)&Boxes, nPoints, (GraphObj*)tmpl, o)) 
+			return parent->Command(CMD_REDRAW, 0L, o);
+		break;
+	case CMD_AUTOSCALE:
+		if(dirty){
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
+			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			}
+		else return true;
+		dirty = false;
+	case CMD_BOX_TYPE:		case CMD_BOX_FILL:
+		if(Boxes) for (i = 0; i < nPoints; i++)
+			if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_UPDATE:
+		if(Boxes) SavVarObs((GraphObj **)Boxes, nPoints, UNDO_CONTINUE);
+		DoUpdate();
+		return true;
+		}
+	return false;
+}
+
+void
+DensDisp::DoUpdate()
+{
+	AccRange *rX, *rY;
+	int i, j, k, l, ic, n;
+	double v, w;
+	lfPOINT fp1, fp2;
+	Box **op = Boxes;
+
+	if(xRange && yRange && (rX = new AccRange(xRange)) && (rY = new AccRange(yRange))) {
+		if((n=rX->CountItems()) == rY->CountItems()) {
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			if(!(Boxes = (Box**)realloc(Boxes, n * sizeof(Box*)))) return;
+			if(op && op != Boxes) Undo.InvalidGO(this);
+			for(i = nPoints; i < n; i++) {
+				Boxes[i] = 0L;
+				}
+			nPoints = n;
+			rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
+			rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
+			for(ic = 0; ic < n && !data->GetValue(j, i, &v); ic++) {
+				rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
+				}
+			rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
+			if(type & 0x10){			//vertical ?
+				fp2.fx = 0;		fp2.fy = v;
+				}
+			else {
+				fp2.fx = v;		fp2.fy = 0.0;
+				}
+			ic = 0;
+			do {
+				if(data->GetValue(j, i, &v) && data->GetValue(l, k, &w)){
+					fp1.fx = fp2.fx;	fp1.fy = fp2.fy;
+					if(type & 0x10) {
+						CheckBounds(w, fp1.fy);			CheckBounds(-w, v);
+						fp2.fy = v;		
+						switch(type & 0x3) {
+						case 1:		fp1.fx = fp2.fx = w/2.0;		break;
+						case 2:		fp1.fx = fp2.fx = -w/2.0;		break;
+						default:	fp2.fx = 0.0;					break;
+							}
+						}
+					else {
+						CheckBounds(fp1.fx, w);			CheckBounds(v, -w);
+						fp2.fx = v;
+						switch(type & 0x3) {
+						case 1:		fp1.fy = fp2.fy = w/2.0;		break;
+						case 2:		fp1.fy = fp2.fy = -w/2.0;		break;
+						default:	fp2.fy = 0.0;					break;
+							}
+						}
+					if(op && Boxes[ic]) {
+						Boxes[ic]->SetSize(SIZE_XPOS, fp1.fx);	Boxes[ic]->SetSize(SIZE_XPOS+1, fp2.fx);
+						Boxes[ic]->SetSize(SIZE_YPOS, fp1.fy);	Boxes[ic]->SetSize(SIZE_YPOS+1, fp2.fy);
+						Boxes[ic]->SetSize(SIZE_BOX, (type &0x03) ? w/2.0 : w);
+						}
+					else if(!op && (Boxes[ic] = new Box(this, data, fp1, fp2, BAR_WIDTHDATA)))
+						Boxes[ic]->SetSize(SIZE_BOX, (type &0x03) ? w/2.0 : w);
+					}
+				ic++;
+				}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
+			if(!op) {
+				SetSize(SIZE_BOX_LINE, DefLine.width);
+				SetColor(COL_BOX_LINE, DefLine.color);
+				Command(CMD_BOX_FILL, (void*)&DefFill, 0L);
+				}
+			}
+		delete(rX);		delete(rY);
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Stacked bars consist of several box-plots
+StackBar::StackBar(GraphObj *par, DataObj *d):Plot(par, d)
+{
+	FileIO(INIT_VARS);
+	Id = GO_STACKBAR;
+	if (!d && parent) parent->Command(CMD_DELOBJ, this, NULL);
+}
+
+StackBar::StackBar(int src):Plot(0L, 0L)
+{
+	int i;
+
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		//now set parent in all children
+		if(Boxes) for(i = 0; i < numPlots; i++)
+			if(Boxes[i]) Boxes[i]->parent = this;
+		if(xyPlots) for(i = 0; i < numXY; i++)
+			if(xyPlots[i]) xyPlots[i]->parent = this;
+		if(Polygons) for(i = 0; i < numPG; i++)
+			if(Polygons[i]) Polygons[i]->parent = this;
+		if(Lines) for(i = 0; i < numPL; i++)
+			if(Lines[i]) Lines[i]->parent = this;
+		}
+}
+
+StackBar::~StackBar()
+{
+	int i;
+
+	if(Boxes){
+		for(i = 0; i < numPlots; i++) if(Boxes[i]) DeleteGO(Boxes[i]);
+		free(Boxes);
+		}
+	if(xyPlots){
+		for(i = 0; i < numXY; i++) if(xyPlots[i]) DeleteGO(xyPlots[i]);
+		free(xyPlots);
+		}
+	if(Polygons) {
+		for(i = 0; i < numPG; i++) if(Polygons[i]) DeleteGO(Polygons[i]);
+		free(Polygons);
+		}
+	if(Lines) {
+		for(i = 0; i < numPL; i++) if(Lines[i]) DeleteGO(Lines[i]);
+		free(Lines);
+		}
+	if(ssXrange) free(ssXrange);	if(ssYrange) free(ssYrange);
+	if(CumData) delete CumData;		CumData = 0L;
+	if(name) free(name);			name=0L;
+	if(x_tv) delete x_tv;			x_tv = 0L;
+	if(y_tv) delete y_tv;			y_tv = 0L;
+	Undo.InvalidGO(this);
+}
+
+bool
+StackBar::SetSize(int select, double value)
+{
+	int i;
+
+	switch(select & 0xfff){
+	case SIZE_BAR:
+		if(xyPlots) for(i = 0; i < numXY; i++)
+			if(xyPlots[i]) xyPlots[i]->SetSize(select, value);
+		return true;
+	case SIZE_BOX:		case SIZE_BOX_LINE:
+		if(Boxes) for(i = 0; i < numPlots; i++) 
+			if(Boxes[i]) Boxes[i]->SetSize(select, value);
+		return true;
+	}
+	return false;
+}
+
+void
+StackBar::DoPlot(anyOutput *o)
+{
+	int i;
+	double dx, dy;
+	fRECT oldREC;
+
+	if(!o || !parent) return;
+	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
+	if(use_xaxis || use_yaxis) ApplyAxes(o);
+	dx = o->un2fix(dspm.fx);		dy = o->un2fiy(dspm.fy);
+	memcpy(&oldREC, &o->Box1, sizeof(fRECT));
+	if(Boxes) for(i = 0; i < numPlots; i++) if(Boxes[i]) {
+		if(Boxes[i]->Id >= GO_PLOT && Boxes[i]->Id < GO_GRAPH) {
+			if(((Plot*)Boxes[i])->hidden == 0) Boxes[i]->DoPlot(o);
+			}
+		else Boxes[i]->DoPlot(o);
+		}
+	if(xyPlots) for(i = 0; i < numXY; i++) if(xyPlots[i]) {
+		if(xyPlots[i]->Id >= GO_PLOT && xyPlots[i]->Id < GO_GRAPH) {
+			if(((Plot*)xyPlots[i])->hidden == 0) xyPlots[i]->DoPlot(o);
+			}
+		else xyPlots[i]->DoPlot(o);
+		}
+	if(Polygons) for(i = 0; i < numPG; i++)
+		if(Polygons[i]) Polygons[i]->DoPlot(o);
+	if(Lines) for(i = numPL-1; i >= 0; i--){
+		o->Box1.Xmin = oldREC.Xmin + dx*i;
+		o->Box1.Ymin = oldREC.Ymin + dy*i;
+		o->Box1.Xmax = oldREC.Xmax + dx*i;
+		o->Box1.Ymax = oldREC.Ymax + dy*i;
+		if(Lines[i]) Lines[i]->DoPlot(o);
+		}
+	dirty = false;
+	memcpy(&o->Box1, &oldREC, sizeof(fRECT));
+	if(use_xaxis || use_yaxis) parent->Command(CMD_AXIS, 0L, o);
+}
+
+bool
+StackBar::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	static MouseEvent *mev;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		mev = (MouseEvent *) tmpl;
+		switch(mev->Action) {
+		case MOUSE_LBUP:
+			if(Boxes && !CurrGO) for(i = 0; i < numPlots; i++)
+				if(Boxes[i] && Boxes[i]->Command(cmd, tmpl, o))return true;
+			if(xyPlots && !CurrGO) for(i = 0; i < numXY; i++)
+				if(xyPlots[i] && xyPlots[i]->Command(cmd, tmpl, o))return true;
+			if(Polygons && !CurrGO) for(i = 0; i < numPG; i++)
+				if(Polygons[i] && Polygons[i]->Command(cmd, tmpl, o))return true;
+			if(Lines && !CurrGO) for(i = 0; i < numPL; i++)
+				if(Lines[i] && Lines[i]->Command(cmd, tmpl, o))return true;
+			break;
+			}
+		break;
+	case CMD_OBJTREE:
+		if(!tmpl) return false;
+		if(Boxes) for(i = 0; i < numPlots; i++) if(Boxes[i]) 
+			((ObjTree*)tmpl)->Command(CMD_UPDATE, Boxes[i], 0L);
+		if(xyPlots) for(i = 0; i < numXY; i++) if(xyPlots[i]) 
+			((ObjTree*)tmpl)->Command(CMD_UPDATE, xyPlots[i], 0L);
+		return true;
+	case CMD_SCALE:
+		dspm.fx *= ((scaleINFO*)tmpl)->sx.fy;
+		dspm.fy *= ((scaleINFO*)tmpl)->sy.fy;
+	case CMD_LEGEND:
+		if(Boxes) for (i = 0; i < numPlots; i++)
+			if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
+		if(Polygons) for (i = numPG-1; i >= 0; i--)
+			if(Polygons[i]) Polygons[i]->Command(cmd, tmpl, o);
+		if(Lines) for (i = numPL-1; i >= 0; i--)
+			if(Lines[i]) Lines[i]->Command(cmd, tmpl, o);
+		if(xyPlots) for (i = 0; i < numXY; i++)
+			if(xyPlots[i]) xyPlots[i]->Command(cmd, tmpl, o);
+		break;
+	case CMD_USEAXIS:
+		UseAxis(*((int*)tmpl));
+		return true;
+	case CMD_MRK_DIRTY:
+		dirty = true;
+	case CMD_SETSCROLL:		case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		return false;
+	case CMD_BAR_TYPE:
+		if(xyPlots) for(i = 0; i < numXY; i++)
+			if(xyPlots[i]) xyPlots[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_SAVE_BARS:		case CMD_SAVE_BARS_CONT:
+		if(Boxes) for(i = 0; i < numPlots; i++)
+			if(Boxes[i])Boxes[i]->Command(CMD_SAVE_BARS_CONT, tmpl, o);
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_STACKBAR;
+		if(data == tmpl) return true;
+		data = (DataObj *)tmpl;
+	case CMD_AUTOSCALE:		case CMD_UPDATE:
+		if(cmd == CMD_AUTOSCALE) {
+			if(hidden) return false;
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
+			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			dirty = false;
+			}
+		if(cum_data_mode){
+			if(CumData) delete CumData;
+			if(CumData = CreaCumData(ssXrange, ssYrange, cum_data_mode, StartVal)) {
+				if(Polygons) for(i = 0; i < numPG; i++)
+					if(Polygons[i]) Polygons[i]->Command(CMD_SET_DATAOBJ, CumData, o);
+				if(Lines) for(i = 0; i < numPL; i++)
+					if(Lines[i]) Lines[i]->Command(CMD_SET_DATAOBJ, CumData, o);
+				if(xyPlots) for(i = 0; i < numXY; i++)
+					if(xyPlots[i]) xyPlots[i]->Command(CMD_SET_DATAOBJ, CumData, o);
+				if(Boxes) for (i = 0; i < numPlots; i++) 
+					if(Boxes[i]) Boxes[i]->Command(CMD_SET_DATAOBJ, CumData, o);
+				}
+			}
+		if(cmd == CMD_SET_DATAOBJ) tmpl = (void*) CumData;
+		if(Polygons) for(i = 0; i < numPG; i++)
+			if(Polygons[i]) Polygons[i]->Command(cmd, tmpl, o);
+		if(Lines) for(i = 0; i < numPL; i++)
+			if(Lines[i]) Lines[i]->Command(cmd, tmpl, o);
+		if(xyPlots) for(i = 0; i < numXY; i++)
+			if(xyPlots[i]) {
+				if(cmd == CMD_AUTOSCALE && xyPlots[i]->Id >= GO_PLOT && xyPlots[i]->Id < GO_GRAPH) {
+					if(((Plot*)xyPlots[i])->hidden == 0) xyPlots[i]->Command(cmd, tmpl, o);
+					}
+				else xyPlots[i]->Command(cmd, tmpl, o);
+				}
+	case CMD_BOX_TYPE:
+		if(Boxes) for (i = 0; i < numPlots; i++) 
+			if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_DELOBJ:
+		if(o) o->HideMark();
+		if(!tmpl || !parent) return false;
+		if(DeleteGOL((GraphObj***)&Polygons, numPG, (GraphObj*)tmpl, o)) 
+			return parent->Command(CMD_REDRAW, 0L, o);
+		if(DeleteGOL((GraphObj***)&Lines, numPL, (GraphObj*)tmpl, o)) 
+			return parent->Command(CMD_REDRAW, 0L, o);
+		if(DeleteGOL((GraphObj***)&xyPlots, numXY, (GraphObj*)tmpl, o)) 
+			return parent->Command(CMD_REDRAW, 0L, o);
+		if(DeleteGOL((GraphObj***)&Boxes, numPlots, (GraphObj*)tmpl, o)) 
+			return parent->Command(CMD_REDRAW, 0L, o);
+		if(xyPlots) for(i = 0; i < numXY; i++)
+			if(xyPlots[i] && xyPlots[i]->Command(cmd, tmpl, o)) return true;
+		if(Boxes) for(i = 0; i < numPlots; i++)
+			if(Boxes[i] && Boxes[i]->Command(cmd, tmpl, o)) return true;
+		return false;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Stacked polygons is based on stacked bar
+StackPG::StackPG(GraphObj *par, DataObj *d):StackBar(par, d)
+{
+	Id = GO_STACKPG;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// waterfall plot is based on stacked bar
+Waterfall::Waterfall(GraphObj *par, DataObj *d):StackBar(par, d)
+{
+	Id = GO_WATERFALL;
+	dspm.fx = dspm.fy = 0.0;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// multi data line plot is based on stacked bar
+MultiLines::MultiLines(GraphObj *par, DataObj *d):StackBar(par, d)
+{
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// simple pie chart
+PieChart::PieChart(GraphObj *par, DataObj *d):Plot(par, d)
+{
+	FileIO(INIT_VARS);
+	Id = GO_PIECHART;
+	if (!d && parent) parent->Command(CMD_DELOBJ, this, NULL);
+}
+
+PieChart::PieChart(int src):Plot(0L, 0L)
+{
+	int i;
+	
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		//now set parent in  children
+		if(Segments) 
+			for(i = 0; i < nPts; i++) if(Segments[i]) Segments[i]->parent = this;
+		}
+}
+
+PieChart::~PieChart()
+{
+	int i;
+
+	if(Segments) {
+		for(i = 0; i < nPts; i++) if(Segments[i]) DeleteGO(Segments[i]);
+		free(Segments);		Segments = 0L;
+		}
+	if(ssRefA) free(ssRefA);	if(ssRefR) free(ssRefR);
+	ssRefA = ssRefR = 0L;
+	if(name) free(name);		name=0L;
+	Undo.InvalidGO(this);
+}
+
+bool 
+PieChart::SetSize(int select, double value)
+{
+	int i;
+
+	switch(select & 0xfff) {
+	case SIZE_XPOS:
+	case SIZE_YPOS:
+	case SIZE_RADIUS1:
+	case SIZE_RADIUS2:
+		if(Segments) for(i = 0; i < nPts; i++) {
+			if(Segments[i]) Segments[i]->SetSize(select, value);
+			}
+		return true;
+	default:
+		return false;
+		}
+	return true;
+}
+
+void
+PieChart::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(Segments) for(i = 0; i < nPts; i++) if(Segments[i]) Segments[i]->DoPlot(o);
+}
+
+bool
+PieChart::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	MouseEvent *mev;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		mev = (MouseEvent *) tmpl;
+		switch(mev->Action) {
+		case MOUSE_LBUP:
+			//select objects invers to plot order
+			if(Segments && !CurrGO) for(i = nPts-1; i>=0; i--)
+				if(Segments[i]) if(Segments[i]->Command(cmd, tmpl, o))break;
+			break;
+			}
+		break;
+	case CMD_SETSCROLL:		case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		return false;
+	case CMD_UPDATE:
+		DoUpdate();
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_PIECHART;
+		data = (DataObj *)tmpl;
+	case CMD_SCALE:
+		if(cmd == CMD_SCALE) {
+			CtDef.fx *= ((scaleINFO*)tmpl)->sx.fy;	CtDef.fy *= ((scaleINFO*)tmpl)->sx.fy;
+			}
+	case CMD_SHIFT_OUT:		case CMD_SEG_FILL:		case CMD_SEG_LINE:
+	case CMD_SEG_MOVEABLE:	case CMD_LEGEND:
+		if(Segments) for(i = 0; i < nPts; i++)
+			if(Segments[i]) Segments[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_DELOBJ:
+		o->HideMark();
+		if(Segments && parent) for(i = 0; i < nPts; i++) {
+			if(Segments[i] && tmpl == (void *)Segments[i]) {
+				Undo.DeleteGO((GraphObj**)(&Segments[i]), 0L, o);
+				parent->Command(CMD_REDRAW, NULL, o);
+				return true;
+				}
+			}
+		break;
+	case CMD_SAVE_SYMBOLS:
+		return SavVarObs((GraphObj **)Segments, nPts, 0L);
+		}
+	return false;
+}
+
+void
+PieChart::DoUpdate()
+{
+	AccRange *rY = 0L, *rR = 0L;
+	double sum, fv, dang1, dang2;
+	int i, ix, iy, rix, riy;
+
+	if(ssRefA && (rY = new AccRange(ssRefA))) {
+		SavVarObs((GraphObj **)Segments, nPts, UNDO_CONTINUE);
+		if(ssRefR) rR = new AccRange(ssRefR);
+		rY->GetFirst(&ix, &iy);				rY->GetNext(&ix, &iy);
+		for(i = 0, sum = 0.0; i < nPts; i++){
+			if(data->GetValue(iy, ix, &fv)) sum += fv;
+			rY->GetNext(&ix, &iy);
+			}
+		sum /= CtDef.fy;
+		dang1 = dang2 = CtDef.fx;
+		rY->GetFirst(&ix, &iy);				rY->GetNext(&ix, &iy);
+		if(rR) {
+			rR->GetFirst(&rix, &riy);		rR->GetNext(&rix, &riy);
+			}
+		for(i = 0; i < nPts; i++){
+			if(data->GetValue(iy, ix, &fv)) {
+				dang2 -= (double)fv / sum;
+				if(dang2 < 0.0) dang2 += 360.0;
+				if(Segments[i]) {
+					Segments[i]->SetSize(SIZE_ANGLE1, dang1);
+					Segments[i]->SetSize(SIZE_ANGLE2, dang2);
+					if(rR && data->GetValue(riy, rix, &fv)){
+						fv *= FacRad;
+						Segments[i]->SetSize(SIZE_RADIUS2, fv);
+						}
+					}
+				dang1 = dang2;
+				}
+			rY->GetNext(&ix, &iy);
+			if(rR) rR->GetNext(&rix, &riy);
+			}
+		}
+	if(rY) delete rY;		if(rR) delete rR;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// ring chart is based on piechart
+RingChart::RingChart(GraphObj *par, DataObj *d):PieChart(par, d)
+{
+	Id = GO_RINGCHART;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// a GoGroup contains objects NOT referring to data (e.g. drawing objects)
+GoGroup::GoGroup(GraphObj *par, DataObj *d):Plot(par, d)
+{
+	Objects = 0L;
+	nObs = 0;
+	fPos.fx = fPos.fy = 0.0;
+	Id = GO_GROUP;
+}
+
+GoGroup::GoGroup(int src):Plot(0L, 0L)
+{
+	int i;
+	
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		//now set parent in  children
+		if(Objects) 
+			for(i = 0; i < nObs; i++) if(Objects[i]) Objects[i]->parent = this;
+		}
+}
+
+GoGroup::~GoGroup()
+{
+	int i;
+
+	if(Objects && nObs) {
+		for(i = 0; i < nObs; i++) if(Objects[i]) DeleteGO(Objects[i]);
+		free(Objects);
+		}
+	if(name) free(name);		name=0L;
+	Undo.InvalidGO(this);
+}
+
+double
+GoGroup::GetSize(int select)
+{
+	if(parent) switch(select){
+	case SIZE_GRECT_TOP:
+	case SIZE_GRECT_BOTTOM:
+		return parent->GetSize(select)-fPos.fy;
+	case SIZE_GRECT_LEFT:
+	case SIZE_GRECT_RIGHT:
+		return parent->GetSize(select)-fPos.fx;
+	case SIZE_XPOS:
+		return fPos.fx;
+	case SIZE_YPOS:
+		return fPos.fy;
+		}
+	return 0.0f;
+}
+
+void 
+GoGroup::DoPlot(anyOutput *o)
+{
+	int i;
+	double dx, dy;
+	
+	dx = o->un2fix(fPos.fx + (parent ? parent->GetSize(SIZE_GRECT_LEFT) : 0.0));
+	dy = o->un2fiy(fPos.fy + (parent ? parent->GetSize(SIZE_GRECT_TOP) : 0.0));
+	o->VPorg.fx += dx;				o->VPorg.fy += dy;
+	for(i = 0; i < nObs; i++) if(Objects[i]) Objects[i]->DoPlot(o);
+	o->VPorg.fx -= dx;				o->VPorg.fy -= dy;
+}
+
+bool
+GoGroup::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	GraphObj **tmp_go;
+	MouseEvent *mev;
+	int i;
+
+	switch(cmd) {
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		mev = (MouseEvent *) tmpl;
+		switch(mev->Action) {
+		case MOUSE_LBUP:
+			//select objects invers to plot order
+			if(Objects && !CurrGO) for(i = nObs-1; i>=0; i--)
+				if(Objects[i]) if(Objects[i]->Command(cmd, tmpl, o))break;
+			break;
+			}
+		break;
+	case CMD_SET_DATAOBJ:
+		Id = GO_GROUP;
+		if(Objects) for(i = 0; i < nObs; i++)
+			if(Objects[i]) Objects[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_DROP_OBJECT:
+		if(!Objects || nObs<1) {
+			if((Objects = (GraphObj **)calloc(1, sizeof(GraphObj*)))){
+				Objects[0] = (GraphObj *)tmpl;
+				nObs = 1;
+				return true;
+				}
+			}
+		else if((tmp_go = (GraphObj **)realloc(Objects, (nObs+1)*sizeof(GraphObj*)))) {
+			Objects = tmp_go;
+			Objects[nObs++] = (GraphObj *)tmpl;
+			return true;
+			}
+		break;
+	case CMD_SETSCROLL:
+	case CMD_REDRAW:
+		if(parent) return parent->Command(CMD_REDRAW, tmpl, o);
+		return false;
+	case CMD_DELOBJ:
+		if(Objects && parent) for(i = 0; i < nObs; i++) {
+			o->HideMark();
+			if(Objects[i] && tmpl == (void *)Objects[i]) {
+				Undo.DeleteGO((GraphObj**)(&Objects[i]), 0L, o);
+				parent->Command(CMD_REDRAW, NULL, o);
+				return true;
+				}
+			}
+		break;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// star chart
+StarChart::StarChart(GraphObj *par, DataObj *d):GoGroup(par, d)
+{
+	Id = GO_STARCHART;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// three dimensional scatterplot
+Scatt3D::Scatt3D(GraphObj *par, DataObj *d, DWORD flags):Plot(par, d)
+{
+	FileIO(INIT_VARS);
+	c_flags = flags;
+	Id = GO_SCATT3D;
+}
+
+Scatt3D::Scatt3D(GraphObj *par, DataObj *d, Brick **cols, long nob):Plot(par, d)
+{
+	int i;
+
+	FileIO(INIT_VARS);
+	c_flags = 0L;		Id = GO_SCATT3D;
+	Columns = cols;		nColumns = nob;
+	if(Columns) for(i = 0; i < nColumns; i++) if(Columns[i]) Columns[i]->parent=this;
+}
+
+Scatt3D::Scatt3D(GraphObj *par, DataObj *d, Sphere **ba, long nob):Plot(par, d)
+{
+	int i;
+
+	FileIO(INIT_VARS);
+	c_flags = 0L;		Id = GO_SCATT3D;
+	Balls = ba;			nBalls = nob;
+	if(Balls) for(i = 0; i < nBalls; i++) if(Balls[i]) Balls[i]->parent=this;
+}
+
+Scatt3D::Scatt3D(int src):Plot(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+Scatt3D::~Scatt3D()
+{
+	long i;
+
+	if(ssRefX) free(ssRefX);	if(ssRefY) free(ssRefY);
+	if(ssRefZ) free(ssRefZ);	ssRefX = ssRefY = ssRefZ = 0L;
+	Undo.InvalidGO(this);
+	if(Balls) {
+		for(i = 0; i < nBalls; i++) if(Balls[i]) DeleteGO(Balls[i]);
+		free(Balls);					Balls = 0L;
+		}
+	if(Columns) {
+		for(i = 0; i < nColumns; i++) if(Columns[i]) DeleteGO(Columns[i]);
+		free(Columns);					Columns = 0L;
+		}
+	if(DropLines) {
+		for(i = 0; i < nDropLines; i++) if(DropLines[i]) DeleteGO(DropLines[i]);
+		free(DropLines);				DropLines = 0L;
+		}
+	if(Arrows) {
+		for(i = 0; i < nArrows; i++) if(Arrows[i]) DeleteGO(Arrows[i]);
+		free(Arrows);					Arrows = 0L;
+		}
+	if(Line) {
+		DeleteGO(Line);					Line = 0L;
+		}
+	if(rib) {
+		DeleteGO(rib);					 rib = 0L;
+		}
+	if(name) free(name);				name=0L;
+	if(data_desc) free(data_desc);		data_desc = 0L;
+}
+
+double
+Scatt3D::GetSize(int select)
+{
+	if(parent) return parent->GetSize(select);
+	return 0.0;
+}
+
+bool
+Scatt3D::SetSize(int select, double value)
+{
+	int i;
+
+	switch(select & 0xfff) {
+	case SIZE_SYM_LINE:
+	case SIZE_SYMBOL:
+		if(Balls) for (i=0; i < nBalls; i++)
+			if(Balls[i]) Balls[i]->SetSize(select, value);
+		return true;
+	case SIZE_BAR_BASE:		case SIZE_BAR_LINE:		case SIZE_BAR:
+	case SIZE_BAR_DEPTH:
+		if(Columns) for (i=0; i < nColumns; i++)
+			if(Columns[i]) Columns[i]->SetSize(select, value);
+		return true;
+	case SIZE_ARROW_LINE:	case SIZE_ARROW_CAPWIDTH:
+	case SIZE_ARROW_CAPLENGTH:
+		if(Arrows) for (i=0; i < nArrows; i++)
+			if(Arrows[i]) Arrows[i]->SetSize(select, value);
+		return true;
+		}
+	return false;
+}
+
+bool
+Scatt3D::SetColor(int select, DWORD col)
+{
+	int i;
+
+	switch(select) {
+	case COL_SYM_LINE:			case COL_SYM_FILL:
+		if(Balls) for (i=0; i < nBalls; i++)
+			if(Balls[i]) Balls[i]->SetColor(select, col);
+		return true;
+	case COL_BAR_LINE:			case COL_BAR_FILL:
+		if(Columns) for (i=0; i < nColumns; i++)
+			if(Columns[i]) Columns[i]->SetColor(select, col);
+		return true;
+	case COL_ARROW:
+		if(Arrows) for (i=0; i < nArrows; i++)
+			if(Arrows[i]) Arrows[i]->SetColor(select, col);
+		return true;
+		}
+	return false;
+}
+
+void
+Scatt3D::DoPlot(anyOutput *o)
+{
+	long i;
+	RECT rc;
+
+	if(!o || !parent) return;
+	if(use_xaxis || use_yaxis || use_zaxis) ApplyAxes(o);
+	o->GetSize(&rc);
+	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
+	rDims.left = rc.right;			rDims.right = rc.left;
+	rDims.top = rc.bottom;			rDims.bottom = rc.top;
+	if(Balls) {
+		for(i = 0; i < nBalls; i++){
+			if(Balls[i]){
+				Balls[i]->DoPlot(o);
+				UpdateMinMaxRect(&rDims, Balls[i]->rDims.right, Balls[i]->rDims.top);
+				UpdateMinMaxRect(&rDims, Balls[i]->rDims.left, Balls[i]->rDims.bottom);
+				}
+			}
+		}
+	if(Columns) {
+		for(i = 0; i < nColumns; i++){
+			if(Columns[i]){
+				Columns[i]->DoPlot(o);
+				UpdateMinMaxRect(&rDims, Columns[i]->rDims.right, Columns[i]->rDims.top);
+				UpdateMinMaxRect(&rDims, Columns[i]->rDims.left, Columns[i]->rDims.bottom);
+				}
+			}
+		}
+	if(DropLines) {
+		for(i = 0; i < nDropLines; i++){
+			if(DropLines[i]){
+				DropLines[i]->DoPlot(o);
+				UpdateMinMaxRect(&rDims, DropLines[i]->rDims.right, DropLines[i]->rDims.top);
+				UpdateMinMaxRect(&rDims, DropLines[i]->rDims.left, DropLines[i]->rDims.bottom);
+				}
+			}
+		}
+	if(Arrows) {
+		for(i = 0; i < nArrows; i++){
+			if(Arrows[i]){
+				Arrows[i]->DoPlot(o);
+				UpdateMinMaxRect(&rDims, Arrows[i]->rDims.right, Arrows[i]->rDims.top);
+				UpdateMinMaxRect(&rDims, Arrows[i]->rDims.left, Arrows[i]->rDims.bottom);
+				}
+			}
+		}
+	if(Line) {
+		Line->DoPlot(o);
+		UpdateMinMaxRect(&rDims, Line->rDims.right, Line->rDims.top);
+		UpdateMinMaxRect(&rDims, Line->rDims.left, Line->rDims.bottom);
+		}
+	if(rib) {
+		rib->DoPlot(o);
+		UpdateMinMaxRect(&rDims, rib->rDims.right, rib->rDims.top);
+		UpdateMinMaxRect(&rDims, rib->rDims.left, rib->rDims.bottom);
+		}
+	if(use_xaxis || use_yaxis || use_zaxis)parent->Command(CMD_AXIS, 0L, o);
+	dirty = false;
+}
+
+bool
+Scatt3D::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	MouseEvent *mev;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		mev = (MouseEvent *) tmpl;
+		switch(mev->Action) {
+		case MOUSE_LBUP:
+			if(Balls && !CurrGO) for(i = 0; i < nBalls; i++)
+				if(Balls[i]) if(Balls[i]->Command(cmd, tmpl, o))return true;
+			if(Columns && !CurrGO) for(i = 0; i < nColumns; i++)
+				if(Columns[i]) if(Columns[i]->Command(cmd, tmpl, o))return true;
+			if(DropLines && !CurrGO) for(i = 0; i < nDropLines; i++)
+				if(DropLines[i]) if(DropLines[i]->Command(cmd, tmpl, o))return true;
+			if(Arrows && !CurrGO) for(i = 0; i < nArrows; i++)
+				if(Arrows[i]) if(Arrows[i]->Command(cmd, tmpl, o))return true;
+			if(Line && !CurrGO) if(Line->Command(cmd, tmpl, o)) return true;
+			if(rib && !CurrGO) if(rib->Command(cmd, tmpl, o)) return true;
+			break;
+			}
+		break;
+	case CMD_MRK_DIRTY:
+		dirty = true;
+	case CMD_SET_GO3D:		case CMD_SETSCROLL:		case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_DL_LINE:		case CMD_DL_TYPE:
+		if(DropLines) for(i = 0; i < nDropLines; i++)
+			if(DropLines[i]) DropLines[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_ARROW_TYPE:	case CMD_ARROW_ORG3D:
+		if(Arrows) for(i = 0; i < nArrows; i++)
+			if(Arrows[i]) Arrows[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_USEAXIS:
+		return UseAxis(*((int*)tmpl));
+	case CMD_LEGEND:
+		if(!tmpl) return false;
+		if(Balls) {
+			if(Line && Line->Id == GO_LINE3D) {
+				for (i = 0; i < nBalls && i < 100; i++)
+					if(Balls[i]) ((Legend*)tmpl)->HasSym(&Line->Line, Balls[i], 0L);
+				}
+			else {
+				for (i = 0; i < nBalls && i < 100; i++) {
+					if(Balls[i]) {
+						if(Balls[i]->type) Balls[i]->Command(cmd, tmpl, o);
+						else ((Legend*)tmpl)->HasSym(0L, Balls[i], 0L);
+						}
+					}
+				}
+			}
+		else if(Line) Line->Command(cmd, tmpl, o);
+		if(Columns) for(i = 0; i < nColumns; i++)
+			if(Columns[i]) Columns[i]->Command(cmd, tmpl, o);
+		if(rib) rib->Command(cmd, tmpl, o);
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_SCATT3D;
+		data = (DataObj *)tmpl;
+	case CMD_UPDATE:	case CMD_SCALE:
+		if(Balls) {
+			SavVarObs((GraphObj**)Balls, nBalls, UNDO_CONTINUE);
+			for(i = 0; i < nBalls; i++)	if(Balls[i]) Balls[i]->Command(cmd, tmpl, o);
+			}
+		if(Columns) {
+			SavVarObs((GraphObj**)Columns, nColumns, UNDO_CONTINUE);
+			for(i = 0; i < nColumns; i++) if(Columns[i]) Columns[i]->Command(cmd, tmpl, o);
+			}
+		if(DropLines) {
+			SavVarObs((GraphObj**)DropLines, nDropLines, UNDO_CONTINUE);
+			for(i = 0; i < nDropLines; i++) if(DropLines[i]) DropLines[i]->Command(cmd, tmpl, o);
+			}
+		if(Arrows) {
+			SavVarObs((GraphObj**)Arrows, nArrows, UNDO_CONTINUE);
+			for(i = 0; i < nArrows; i++) if(Arrows[i]) Arrows[i]->Command(cmd, tmpl, o);
+			}
+		if(Line) Line->Command(cmd, tmpl, o);
+		if(rib) rib->Command(cmd, tmpl, o);
+		return true;
+	case CMD_AUTOSCALE:
+		if(dirty) {
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
+			xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
+			if(Balls) for(i = 0; i < nBalls; i++)
+				if(Balls[i]) Balls[i]->Command(cmd, tmpl, o);
+			if(Columns) for(i = 0; i < nColumns; i++)
+				if(Columns[i]) Columns[i]->Command(cmd, tmpl, o);
+			if(DropLines) for(i = 0; i < nDropLines; i++)
+				if(DropLines[i]) DropLines[i]->Command(cmd, tmpl, o);
+			if(Arrows) for(i = 0; i < nArrows; i++)
+				if(Arrows[i]) Arrows[i]->Command(cmd, tmpl, o);
+			if(Line) Line->Command(cmd, tmpl, o);
+			if(rib) rib->Command(cmd, tmpl, o);
+			}
+		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH &&
+			xBounds.fx <= xBounds.fy && yBounds.fx <= yBounds.fy && zBounds.fx <= zBounds.fy){
+			((Plot*)parent)->CheckBounds3D(xBounds.fx, yBounds.fx, zBounds.fx);
+			((Plot*)parent)->CheckBounds3D(xBounds.fy, yBounds.fy, zBounds.fy);
+			}
+		dirty = false;
+		return true;
+	case CMD_DELOBJ:
+		if(o) o->HideMark();
+		if(!tmpl || !parent) return false;
+		if(rib && rib->Command(cmd, tmpl, o)) return true;
+		if(Balls) for(i = 0; i < nBalls; i++) if(Balls[i] == tmpl) {
+			Undo.DeleteGO((GraphObj**)(&Balls[i]), 0L, o);
+			return parent->Command(CMD_REDRAW, 0L, o);
+			}
+		if(Columns) for(i = 0; i < nColumns; i++) if(Columns[i] == tmpl) {
+			Undo.DeleteGO((GraphObj**)(&Columns[i]), 0L, o);
+			return parent->Command(CMD_REDRAW, 0L, o);
+			}
+		if(DropLines) for(i = 0; i < nDropLines; i++) if(DropLines[i] == tmpl) {
+			Undo.DeleteGO((GraphObj**)(&DropLines[i]), 0L, o);
+			return parent->Command(CMD_REDRAW, 0L, o);
+			}
+		if(Arrows) for(i = 0; i < nArrows; i++) if(Arrows[i] == tmpl) {
+			Undo.DeleteGO((GraphObj**)(&Arrows[i]), 0L, o);
+			return parent->Command(CMD_REDRAW, 0L, o);
+			}
+		if(Line && Line == tmpl) {
+			Undo.DeleteGO((GraphObj**)(&Line), 0L, o);
+			return parent->Command(CMD_REDRAW, 0L, o);
+			}
+		if(rib && rib == tmpl) {
+			Undo.DeleteGO((GraphObj**)(&rib), 0L, o);
+			return parent->Command(CMD_REDRAW, 0L, o);
+			}
+	case CMD_SYM_FILL:
+		if(Balls) for(i= 0; i < nBalls; i++) if(Balls[i])Balls[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_BAR_FILL:
+		if(Columns) for(i= 0; i < nColumns; i++) if(Columns[i])Columns[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_SAVE_SYMBOLS:
+		return SavVarObs((GraphObj **)Balls, nBalls, 0L);
+	case CMD_SAVE_BARS:
+		return SavVarObs((GraphObj **)Columns, nColumns, 0L);
+	case CMD_SAVE_ARROWS:
+		return SavVarObs((GraphObj **)Arrows, nArrows, 0L);
+	case CMD_SAVE_DROPLINES:
+		return SavVarObs((GraphObj **)DropLines, nDropLines, 0L);
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// three dimensional ribbon based on list of Plane3D objects
+Ribbon::Ribbon(GraphObj *par, DataObj *d, double z, double width, char *xr, char *yr)
+	:Plot(par, d)
+{
+	FileIO(INIT_VARS);		Id = GO_RIBBON;		type = 1;
+	if(xr && xr[0]) ssRefX = (char*)memdup(xr, (int)strlen(xr)+1, 0L);
+	if(yr && yr[0]) ssRefY = (char*)memdup(yr, (int)strlen(yr)+1, 0L);
+	z_value = z;	z_width = width;
+}
+
+Ribbon::Ribbon(GraphObj *par, DataObj *d, int which, char *xr, char *yr, char *zr)
+	:Plot(par, d)
+{
+	FileIO(INIT_VARS);		Id = GO_RIBBON;			type = which;
+	if(xr && xr[0]) ssRefX = (char*)memdup(xr, (int)strlen(xr)+1, 0L);
+	if(yr && yr[0]) ssRefY = (char*)memdup(yr, (int)strlen(yr)+1, 0L);
+	if(zr && zr[0]) ssRefZ = (char*)memdup(zr, (int)strlen(zr)+1, 0L);
+	CreateObs();
+}
+
+Ribbon::Ribbon(GraphObj *par, DataObj *d, GraphObj **go, int ngo)
+	:Plot(par, d)
+{
+	int i;
+
+	FileIO(INIT_VARS);		Id = GO_RIBBON;		type = 3;
+	planes = (Plane3D**)go;						nPlanes = ngo;
+	for(i = 0; i < ngo; i++) planes[i]->parent = this;
+}
+
+
+Ribbon::Ribbon(int src):Plot(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+Ribbon::~Ribbon()
+{
+	int i;
+
+	if(ssRefX) free(ssRefX);	if(ssRefY) free(ssRefY);
+	if(ssRefZ) free(ssRefZ);	ssRefX = ssRefY = ssRefZ = 0L;
+	Undo.InvalidGO(this);
+	if(planes) {
+		for(i = 0; i < nPlanes; i++) if(planes[i]) DeleteGO(planes[i]);
+		free(planes);		planes = 0L;
+		}
+	if(values) free(values);		values = 0L;	nVal = 0;
+	if(name) free(name);			name=0L;
+	if(data_desc) free(data_desc);	data_desc = 0L;
+}
+
+double
+Ribbon::GetSize(int select)
+{
+	switch(select) {
+	case SIZE_CELLWIDTH:	return relwidth;
+	case SIZE_ZPOS:			return z_value;
+		}
+	return 0.0;
+}
+
+bool
+Ribbon::SetSize(int select, double value)
+{
+	int i;
+
+	switch(select) {
+	case SIZE_SYM_LINE:
+		if(planes) for (i=0; i < nPlanes; i++)
+			if(planes[i]) planes[i]->SetSize(select, value);
+		return true;
+	case SIZE_CELLWIDTH:
+		if(value != relwidth) {
+			//assume planes saved already by CMD_SAVE_SYMBOLS
+			Undo.ValFloat(this, &relwidth, UNDO_CONTINUE);
+			relwidth = value;
+			if(planes) UpdateObs(false);
+			}
+		return true;
+	case SIZE_ZPOS:
+		if(value != z_value) {
+			//assume planes saved already by CMD_SAVE_SYMBOLS
+			Undo.ValFloat(this, &z_value, UNDO_CONTINUE);
+			z_value = value;
+			if(planes) UpdateObs(false);
+			}
+		return true;
+		}
+	return false;
+}
+
+bool
+Ribbon::SetColor(int select, DWORD col)
+{
+	int i;
+
+	switch(select) {
+	case COL_POLYLINE:
+		Line.color = col;
+	case COL_POLYGON:
+		if(select == COL_POLYGON) Fill.color = col;
+		if(planes) for (i=0; i < nPlanes; i++)
+			if(planes[i]) planes[i]->SetColor(select, col);
+		return true;
+		}
+	return false;
+}
+
+void
+Ribbon::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(!planes) CreateObs();
+	if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->DoPlot(o);
+	dirty = false;
+}
+
+bool
+Ribbon::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	MouseEvent *mev;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		mev = (MouseEvent *) tmpl;
+		switch(mev->Action) {
+		case MOUSE_LBUP:
+			if(planes && !CurrGO) for(i = 0; i < nPlanes; i++)
+				if(planes[i]) if(planes[i]->Command(cmd, tmpl, o)) return true;
+			break;
+			}
+		break;
+	case CMD_SCALE:
+		z_value *= ((scaleINFO*)tmpl)->sz.fy;
+		z_width *= ((scaleINFO*)tmpl)->sz.fy;
+		for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->Command(cmd, tmpl, o);
+		break;
+	case CMD_MRK_DIRTY:
+		dirty = true;
+	case CMD_SET_GO3D:		case CMD_SETSCROLL:		case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_SET_DATAOBJ:
+		Id = GO_RIBBON;
+		data = (DataObj *)tmpl;
+		if(planes) for(i = 0; i < nPlanes; i++)
+			if(planes[i]) planes[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_UPDATE:
+		SavVarObs((GraphObj **)planes, nPlanes, UNDO_CONTINUE);
+		Undo.DataMem(this, (void**)&values, nVal * sizeof(fPOINT3D), &nVal, UNDO_CONTINUE);
+		UpdateObs(dirty = true);
+		if(parent) parent->Command(CMD_MRK_DIRTY, tmpl, o);
+		return true;
+	case CMD_AUTOSCALE:
+		if(!planes) CreateObs();
+		if(dirty) {
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
+			xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
+			if(planes) for(i = 0; i < nPlanes; i++)
+				if(planes[i]) planes[i]->Command(cmd, tmpl, o);
+			}
+		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH &&
+			xBounds.fx <= xBounds.fy && yBounds.fx <= yBounds.fy && zBounds.fx <= zBounds.fy){
+			((Plot*)parent)->CheckBounds3D(xBounds.fx, yBounds.fx, zBounds.fx);
+			((Plot*)parent)->CheckBounds3D(xBounds.fy, yBounds.fy, zBounds.fy);
+			}
+		return true;
+	case CMD_DELOBJ:
+		if(!tmpl || !parent) return false;
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i] == tmpl) {
+			Undo.DeleteGO((GraphObj**)(&planes[i]), 0L, o);
+			return parent->Command(CMD_REDRAW, 0L, o);
+			}
+		return false;
+	case CMD_SYM_FILL:		case CMD_LEGEND:
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->Command(cmd, tmpl, o); 
+		return true;
+	case CMD_SAVE_SYMBOLS:
+		return SavVarObs((GraphObj **)planes, nPlanes, 0L);
+		}
+	return false;
+}
+
+void
+Ribbon::CreateObs()
+{
+	int i, j, n, rx, cx, ry, cy, rz, cz;
+	double fx, fy, fz, tmp;
+	fPOINT3D pg[5];
+	AccRange *rX, *rY, *rZ;
+	Triangle *trl, *trc, *trn;
+
+	if(planes || !data) return;
+	rX = rY = rZ = 0L;
+	switch(type) {
+	case 1:
+		if(!ssRefX || !ssRefY) return;
+		if(z_width == 0.0) z_width = 1.0;	if(relwidth == 0.0) relwidth = 0.6;
+		if((rX = new AccRange(ssRefX)) && (rY = new AccRange(ssRefY))) {
+			if(!data_desc) data_desc = rY->RangeDesc(data, 1);
+			tmp = relwidth*z_width/2.0;
+			if(!(values = (fPOINT3D*)calloc(i = rX->CountItems(), sizeof(fPOINT3D)))){
+				delete rX;	delete rY;	return;
+				}
+			if(!(planes = (Plane3D**)calloc(i-1, sizeof(Plane3D*)))){
+				free(values);	values = 0L;	delete rX;	delete rY;	return;
+				}
+			for(i = j = 0, rX->GetFirst(&cx, &rx), rY->GetFirst(&cy, &ry);
+				rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry); i++) {
+				if(data->GetValue(rx, cx, &fx) && data->GetValue(ry, cy, &fy)) {
+					values[i].fx = fx;	values[i].fy = fy;	values[i].fz = z_value;
+					pg[3].fx = pg[2].fx = fx;	pg[3].fy = pg[2].fy = fy;
+					pg[2].fz = z_value - tmp;	pg[3].fz = z_value +tmp;
+					if(j) {
+						pg[4].fx = pg[0].fx;	pg[4].fy = pg[0].fy; pg[4].fz = pg[0].fz;
+						planes[i-1] = new Plane3D(this, data, pg, 5);
+						if(planes[i-1]) planes[i-1]->Command(CMD_PG_FILL, &Fill, 0L);
+						}
+					j++;
+					pg[0].fx = pg[3].fx;	pg[0].fy = pg[3].fy; pg[0].fz = pg[3].fz;
+					pg[1].fx = pg[2].fx;	pg[1].fy = pg[2].fy; pg[1].fz = pg[2].fz;
+					}
+				}
+			nPlanes = i-1;		nVal = i;
+			}
+		break;
+	case 2:
+		if(!ssRefX || !ssRefY || !ssRefZ) return;
+		if((rX = new AccRange(ssRefX)) && (rY = new AccRange(ssRefY)) && (rZ = new AccRange(ssRefZ))) {
+			if(!(values = (fPOINT3D*)calloc(i = rX->CountItems(), sizeof(fPOINT3D)))){
+				delete rX;	delete rY;	delete rZ;	return;
+				}
+			if(!(planes = (Plane3D**)calloc(i-1, sizeof(Plane3D*)))){
+				free(values);	values = 0L;	delete rX;	delete rY;	delete rZ;	return;
+				}
+			if(!data_desc) data_desc = rY->RangeDesc(data, 1);
+			for(i = 0, rX->GetFirst(&cx, &rx), rY->GetFirst(&cy, &ry), rZ->GetFirst(&cz, &rz);
+				rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry) && rZ->GetNext(&cz, &rz); i++) {
+				if(data->GetValue(rx, cx, &fx) && data->GetValue(ry, cy, &fy) &&
+					data->GetValue(rz, cz, &fz)) {
+					values[i].fx = fx;	values[i].fy = fy;	values[i].fz = fz;
+					pg[3].fx = pg[2].fx = fx;	pg[2].fz = pg[3].fz = fz;		
+					pg[3].fy = 0.0;				pg[2].fy = fy;
+					if(i) {
+						pg[4].fx = pg[0].fx;	pg[4].fy = pg[0].fy; pg[4].fz = pg[0].fz;
+						planes[i-1] = new Plane3D(this, data, pg, 5);
+						if(planes[i-1]) planes[i-1]->Command(CMD_PG_FILL, &Fill, 0L);
+						}
+					pg[0].fx = pg[3].fx;		pg[0].fy = pg[3].fy;	pg[0].fz = pg[3].fz;
+					pg[1].fx = pg[2].fx;		pg[1].fy = pg[2].fy;	pg[1].fz = pg[2].fz;
+					}
+				}
+			nPlanes = i-1;		nVal = i;
+			}
+		break;
+	case 3:
+		if(!ssRefX || !ssRefY || !ssRefZ) break;
+		Undo.InvalidGO(this);
+		trl = Triangulate1(ssRefX, ssRefZ, ssRefY, data);
+		for(i = 0, trc = trl; trc; i++) trc = trc->next;
+		if((n = i) && (planes = (Plane3D**)malloc(n*sizeof(Plane3D*)))) 
+			for(i = nPlanes = 0, trc = trl; trc && i < n; i++) {
+			for(j = 0; j < 4; j++) {	//swap y and z values;
+				tmp = trc->pt[j].fz;	trc->pt[j].fz = trc->pt[j].fy;	trc->pt[j].fy = tmp;
+				}
+			planes[nPlanes++] = new Plane3D(this, data, trc->pt, 4);
+			trn = trc->next;	delete trc;		trc = trn;
+			}
+		dirty = true;			Command(CMD_AUTOSCALE, 0L, 0L);
+		break;
+		}
+	if(rX) delete rX;	if(rY) delete rY;	if(rZ) delete rZ;
+}
+
+void
+Ribbon::UpdateObs(bool bNewData)
+{
+	int i, j, k, rx, cx, ry, cy, rz, cz;
+	double fx, fy, fz, tmp, da1, da2;
+	AccRange *rX, *rY, *rZ;
+	int sel_id[] = {SIZE_XPOS, SIZE_YPOS, SIZE_ZPOS};
+
+	if(!planes || (!values && type < 3)) return;
+	rX = rY = rZ = 0L;
+	switch(type) {
+	case 1:
+		if(!ssRefX || !ssRefY || !data) return;
+		if(z_width == 0.0) z_width = 1.0;	if(relwidth == 0.0) relwidth = 0.6;
+		if((rX = new AccRange(ssRefX)) && (rY = new AccRange(ssRefY))) {
+			tmp = relwidth*z_width/2.0;
+			for(i = 0, rX->GetFirst(&cx, &rx), rY->GetFirst(&cy, &ry);
+				rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry) && i < nVal; i++) {
+				if(data->GetValue(rx, cx, &fx) && data->GetValue(ry, cy, &fy)) {
+					if(bNewData) {
+						values[i].fx = fx;	values[i].fy = fy;	values[i].fz = z_value;
+						}
+					else {
+						fx = values[i].fx;	fy = values[i].fy;	values[i].fz = z_value;
+						}
+					if(i && planes[i-1]) {
+						for(j = 0; j < 3; j++){
+							for(k = 0; k <5; k++) {
+								switch (j) {
+								case 0:	
+									da1 = values[i-1].fx;		da2 = values[i].fx;
+									break;
+								case 1:
+									da1 = values[i-1].fy;		da2 = values[i].fy;
+									break;
+								case 2:
+									if(k != 1 && k != 2) da1 = da2 = (values[i].fz + tmp);
+									else da1 = da2 = (values[i].fz - tmp);
+									break;
+									}
+								planes[i-1]->SetSize(sel_id[j]+k, (k != 2 && k != 3) ? da1 : da2);
+								}
+							}
+						}
+					}
+				}
+			}
+		break;
+	case 2:
+		if(!ssRefX || !ssRefY || !ssRefZ || !data) return;
+		if((rX = new AccRange(ssRefX)) && (rY = new AccRange(ssRefY)) && (rZ = new AccRange(ssRefZ))) {
+			for(i = 0, rX->GetFirst(&cx, &rx), rY->GetFirst(&cy, &ry), rZ->GetFirst(&cz, &rz);
+				rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry) && rZ->GetNext(&cz, &rz) && i < nVal; i++) {
+				if(data->GetValue(rx, cx, &fx) && data->GetValue(ry, cy, &fy) && data->GetValue(rz, cz, &fz)) {
+					values[i].fx = fx;	values[i].fy = fy;	values[i].fz = fz;
+					if(i && planes[i-1]) {
+						planes[i-1]->SetSize(SIZE_XPOS, values[i-1].fx);	planes[i-1]->SetSize(SIZE_XPOS+3, fx);
+						planes[i-1]->SetSize(SIZE_XPOS+1, values[i-1].fx);	planes[i-1]->SetSize(SIZE_XPOS+2, fx);
+						planes[i-1]->SetSize(SIZE_XPOS+4, values[i-1].fx);
+						planes[i-1]->SetSize(SIZE_YPOS+1, values[i-1].fy);	planes[i-1]->SetSize(SIZE_YPOS+2, fy);
+						planes[i-1]->SetSize(SIZE_ZPOS, values[i-1].fz);	planes[i-1]->SetSize(SIZE_ZPOS+3, fz);
+						planes[i-1]->SetSize(SIZE_ZPOS+1, values[i-1].fz);	planes[i-1]->SetSize(SIZE_ZPOS+2, fz);
+						planes[i-1]->SetSize(SIZE_ZPOS+4, values[i-1].fz);
+						}
+					}
+				}
+			}
+		break;
+	case 3:
+		if(planes) {
+			for(i = 0; i < nPlanes; i++) if(planes[i]) DeleteGO(planes[i]);
+			free(planes);		planes = 0L;	nPlanes = 0;
+			}
+		CreateObs();
+		}
+	if(rX) delete rX;	if(rY) delete rY;	if(rZ) delete rZ;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// draw a 3dimensional grid
+Grid3D::Grid3D(GraphObj *par, DataObj *d, int sel, double x1, double xstep, double z1, double zstep)
+	:Plot(par, d)
+{
+	FileIO(INIT_VARS);		Id = GO_GRID3D;
+	start.fx = x1;			step.fx = xstep;
+	start.fz = z1;			step.fz = zstep;
+	type = sel;
+}
+
+Grid3D::Grid3D(int src):Plot(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+Grid3D::~Grid3D()
+{
+	int i;
+
+	Undo.InvalidGO(this);
+	if(lines) {
+		for(i = 0; i < nLines; i++) if(lines[i]) DeleteGO(lines[i]);
+		free(lines);		lines = 0L;
+		}
+	if(planes) {
+		for(i = 0; i < nPlanes; i++) if(planes[i]) DeleteGO(planes[i]);
+		free(planes);		planes = 0L;
+		}
+	nLines = nPlanes = 0;
+	if(name) free(name);	name=0L;
+}
+
+bool 
+Grid3D::SetSize(int select, double value)
+{
+	int i;
+
+	switch (select) {
+	case SIZE_SYM_LINE:
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->SetSize(select, value); 
+		return true;
+		}
+	return false;
+}
+
+bool
+Grid3D::SetColor(int select, DWORD col)
+{
+	int i;
+
+	switch (select) {
+	case COL_POLYLINE:
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->SetColor(select, col); 
+		return true;
+		}
+	return false;
+}
+
+void
+Grid3D::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(!lines && !planes) CreateObs(false);
+	if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->DoPlot(o);
+	if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->DoPlot(o);
+	dirty = false;
+}
+
+bool
+Grid3D::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	MouseEvent *mev;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		mev = (MouseEvent *) tmpl;
+		switch(mev->Action) {
+		case MOUSE_LBUP:
+			if(lines && !CurrGO) for(i = 0; i < nLines; i++)
+				if(lines[i]) if(lines[i]->Command(cmd, tmpl, o)) return true;
+			if(planes && !CurrGO) for(i = 0; i < nPlanes; i++)
+				if(planes[i]) if(planes[i]->Command(cmd, tmpl, o)) return true;
+			break;
+			}
+		break;
+	case CMD_SET_LINE:
+		if(tmpl) {
+			memcpy(&Line, tmpl, sizeof(LineDEF));
+			if(lines) {
+				SavVarObs((GraphObj**)lines, nLines, 0L);
+				for(i = 0; i < nLines; i++)
+					if(lines[i]) lines[i]->Command(cmd, tmpl, o);
+				}
+			if(planes) {
+				SavVarObs((GraphObj**)planes, nPlanes, 0L);
+				for(i = 0; i < nPlanes; i++)
+					if(planes[i]) planes[i]->Command(cmd, tmpl, o);
+				}
+			}
+		break;
+	case CMD_LEGEND:
+		if(!hidden) ((Legend*)tmpl)->HasFill(&Line, planes ? &Fill : 0L, 0L);
+		break;
+	case CMD_CONFIG:
+		return Configure();
+	case CMD_MRK_DIRTY:
+		dirty = true;
+	case CMD_SET_GO3D:		case CMD_SETSCROLL:		case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_SET_DATAOBJ:
+		Id = GO_GRID3D;
+		if(tmpl == data) return true;
+		data = (DataObj *)tmpl;
+	case CMD_UPDATE:
+		if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->Command(cmd, tmpl, o);
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->Command(cmd, tmpl, o);
+		return dirty = true;
+	case CMD_AUTOSCALE:
+		if(!lines && !planes) CreateObs(false);
+		if(dirty) {
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
+			xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
+			if(lines) for(i = 0; i < nLines; i++)
+				if(lines[i]) lines[i]->Command(cmd, tmpl, o);
+			if(planes) for(i = 0; i < nPlanes; i++)
+				if(planes[i]) planes[i]->Command(cmd, tmpl, o);
+			}
+		if(zBounds.fx > zBounds.fy) zBounds.fx = zBounds.fy = 0.0;
+		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH &&
+			xBounds.fx <= xBounds.fy && yBounds.fx <= yBounds.fy){
+			((Plot*)parent)->CheckBounds3D(xBounds.fx, yBounds.fx, zBounds.fx);
+			((Plot*)parent)->CheckBounds3D(xBounds.fy, yBounds.fy, zBounds.fy);
+			}
+		return true;
+	case CMD_SYM_FILL:
+		if(!tmpl) return false;
+		memcpy(&Fill, tmpl, sizeof(FillDEF));
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->Command(cmd, tmpl, o); 
+		return true;
+	case CMD_SAVE_SYMBOLS:
+		return SavVarObs((GraphObj **)planes, nPlanes, 0L);
+	case CMD_DELOBJ:
+		if(!parent) return false;
+		if(DeleteGOL((GraphObj***)&lines,nLines,(GraphObj*)tmpl,o)) return parent->Command(CMD_REDRAW, 0L, o);
+		if(DeleteGOL((GraphObj***)&planes,nPlanes,(GraphObj*)tmpl,o)) return parent->Command(CMD_REDRAW, 0L, o);
+		break;
+		}
+	return false;
+}
+
+void
+Grid3D::CreateObs(bool set_undo)
+{
+	int i, ir, ic, idx, w, h;
+	fPOINT3D *vec;
+
+	if(!parent || !data || lines || planes) return;
+	dirty = true;
+	if(type == 0) {
+		if(!(vec = (fPOINT3D*)malloc(sizeof(fPOINT3D) * 2))) return;
+		data->GetSize(&w, &h);
+		if(0 >= (nLines = 2 * w * h - w - h)) return;
+		if(!(lines =(Line3D**)calloc(nLines, sizeof(Line3D*)))) return;
+		vec[0].fz = start.fz;			data->GetValue(0, 0, &vec[0].fy);
+		for(ic = 1, idx = 0; ic <= w; ic++) {
+			vec[0].fx = start.fx;
+			data->GetValue(0, ic-1, &vec[0].fy);
+			for(ir = 1; ir <= h; ir++){
+				if(ic < w && data->GetValue(ir-1, ic, &vec[1].fy)) {
+					vec[1].fz = vec[0].fz + step.fz;	vec[1].fx = vec[0].fx;
+					lines[idx++] = new Line3D(this, data, vec, 2, 
+						-1, -1, ic-1, ir-1, -1, -1, -1, -1, ic, ir-1, -1, -1);
+					}
+				if(ir < h && data->GetValue(ir, ic-1, &vec[1].fy)) {
+					vec[1].fz = vec[0].fz;	vec[1].fx = vec[0].fx + step.fx;
+					lines[idx++] = new Line3D(this, data, vec, 2,
+						-1, -1, ic-1, ir-1, -1, -1, -1, -1, ic-1, ir, -1, -1);
+					}
+				vec[0].fx += step.fx;	vec[0].fy = vec[1].fy;
+				}
+			vec[0].fz += step.fz;
+			}
+		for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->Command(CMD_SET_LINE, &Line, 0L);
+		free(vec);
+		}
+	else if(type == 1) {
+		if(!(vec = (fPOINT3D*)malloc(sizeof(fPOINT3D) * 5))) return;
+		vec[0].fz = vec[4].fz = start.fz;
+		vec[3].fz = (start.fz +step.fz);
+		data->GetSize(&w, &h);
+		if(0 >= (nPlanes = w * h)) return;
+		if(!(planes =(Plane3D**)calloc(nPlanes, sizeof(Plane3D*)))) return;
+		for(ic = 1, idx = 0; ic <= w; ic++) {
+			vec[0].fx = vec[3].fx = vec[4].fx = (start.fx+step.fx);
+			vec[1].fx = vec[2].fx = start.fx;
+			vec[1].fz = vec[4].fz;	vec[2].fz = vec[3].fz;
+			data->GetValue(0, ic-1, &vec[1].fy);	data->GetValue(0, ic, &vec[2].fy);
+			for(ir = 1; ir <= h; ir++){
+				if(ic < w && ir < h && data->GetValue(ir, ic, &vec[3].fy) 
+					&& data->GetValue(ir, ic-1, &vec[4].fy)) {
+					vec[0].fz = vec[4].fz;	vec[0].fy = vec[4].fy;	vec[0].fx = vec[4].fx;
+					planes[idx++] = new Plane3D(this, 0L, vec, 5);
+					}
+				vec[1].fz = vec[4].fz;		vec[1].fy = vec[4].fy;		vec[1].fx = vec[4].fx;
+				vec[2].fz = vec[3].fz;		vec[2].fy = vec[3].fy;		vec[2].fx = vec[3].fx;
+				vec[3].fx += step.fx;		vec[4].fx += step.fx;
+				}
+			vec[3].fz += step.fz;			vec[4].fz += step.fz;
+			}
+		nPlanes = idx;
+		for(i = 0; i < nPlanes; i++) if(planes[i]){
+			planes[i]->Command(CMD_SET_LINE, &Line, 0L);
+			planes[i]->Command(CMD_SYM_FILL, &Fill, 0L);
+			}
+		SetSize(SIZE_SYM_LINE, Line.width);		SetColor(COL_POLYLINE, Line.color);
+		free(vec);
+		}
+	if(set_undo) {
+		if(planes && nPlanes)Undo.StoreListGO(parent, (GraphObj***)&planes, &nPlanes, UNDO_CONTINUE);
+		if(lines && nLines)Undo.StoreListGO(parent, (GraphObj***)&lines, &nLines, UNDO_CONTINUE);
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// define minima and maxima rectangle to be used by graph
+Limits::Limits(int src):Plot(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+Limits::~Limits()
+{
+	if(name) free(name);		name=0L;
+}
+
+double
+Limits::GetSize(int select)
+{
+	return 0.0;
+}
+
+bool
+Limits::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch (cmd) {
+	case CMD_SET_DATAOBJ:
+		Id = GO_LIMITS;
+		data = (DataObj *)tmpl;	
+		return true;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Calculate and display a user defined function
+Function::Function(GraphObj *par, DataObj *d, char *desc):Plot(par, d)
+{
+	FileIO(INIT_VARS);		cmdxy = (char*)malloc(20*sizeof(char));
+	if(parent && parent->Id == GO_POLARPLOT) {
+		x1 = 0.0;			x2 = 360.0;			xstep = 0.5;
+		if(cmdxy)rlp_strcpy(cmdxy, 20, (char*)"sin(pi*x/30)+1.1");
+		}
+	else {
+		x1 = 0.0;			x2 = 100.0;			xstep = 0.5;
+		if(cmdxy)rlp_strcpy(cmdxy, 20, (char*)"sin(x)/x");
+		}
+	if(desc) name = (char*)memdup(desc, (int)strlen(desc)+1, 0);
+	Id = GO_FUNCTION;
+}
+
+Function::Function(int src):Plot(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+Function::~Function()
+{
+	if(cmdxy) free(cmdxy);		cmdxy = 0L;
+	if(param) free(param);		param = 0L;
+	if(dl) DeleteGO(dl);		dl = 0L;
+	if(name) free(name);		name=0L;
+}
+
+bool
+Function::SetSize(int select, double value)
+{
+	switch(select & 0xfff){
+	case SIZE_MIN_X:	x1 = value;		return true;
+	case SIZE_MAX_X:	x2 = value;		return true;
+	case SIZE_XSTEP:	xstep=value;	return true;
+		}
+	return false;
+}
+
+void
+Function::DoPlot(anyOutput *o)
+{
+	if((!dl || dirty) && cmdxy && cmdxy[0]) Update(o, 0);
+	dirty = false;
+	if(dl && o) {
+		dl->Command(CMD_SET_LINE, &Line, o);
+		dl->DoPlot(o);
+		}
+}
+
+bool
+Function::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch (cmd) {
+	case CMD_LEGEND:	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		if(dl) return dl->Command(cmd, tmpl, o);
+		break;
+	case CMD_SCALE:
+		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
+		return true;
+	case CMD_DELOBJ:
+		if(parent && tmpl && tmpl == dl) return parent->Command(CMD_DELOBJ, this, o);
+		break;
+	case CMD_MRK_DIRTY:
+		if(parent) parent->Command(cmd, tmpl, o);
+		return dirty = true;
+	case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_SET_LINE:
+		if(tmpl) memcpy(&Line, tmpl, sizeof(LineDEF));
+		break;
+	case CMD_SET_DATAOBJ:
+		if(dl) dl->Command(cmd, tmpl, o);
+		Id = GO_FUNCTION;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_SETPARAM:
+		if(tmpl) {
+			if(param) free(param);			param = 0L;
+			if(*((char*)tmpl))param = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0);
+			}
+		dirty = true;
+		return true;
+	case CMD_SETFUNC:
+		if(tmpl) {
+			if(cmdxy) free(cmdxy);			cmdxy = 0L;
+			if(*((char*)tmpl))cmdxy = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0);
+			}
+		dirty = true;
+		return true;
+	case CMD_UPDATE:
+		return Update(o, UNDO_CONTINUE);
+	case CMD_AUTOSCALE:
+		if(!dl) return Update(o, 0L);
+		if(dirty) {
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
+			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
+			xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
+			if(dl) dl->Command(cmd, tmpl, o);
+			dirty = false;
+			}
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH
+			&& Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin) {
+			((Plot*)parent)->CheckBounds(Bounds.Xmin, Bounds.Ymin);
+			((Plot*)parent)->CheckBounds(Bounds.Xmax, Bounds.Ymax);
+			}
+		return true;
+		}
+	return false;
+}
+
+bool
+Function::Update(anyOutput *o, DWORD flags)
+{
+	lfPOINT *xydata;
+	long ndata;
+
+	if(!parent || !cmdxy) return false;
+	LockData(false, false);
+	do_xyfunc(data, x1, x2, xstep, cmdxy, &xydata, &ndata, param);
+	LockData(false, false);
+	if(xydata && ndata >1) {
+		if(!dl) dl = new DataLine(this, data, xydata, ndata, name);
+		else dl->LineData(xydata, ndata);
+		dirty = true;
+		Command(CMD_AUTOSCALE, 0L, 0L);
+		return true;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Calculate and display a user defined function
+static char *lastFunc2D = 0L, *lastParam2D=0L;
+FitFunc::FitFunc(GraphObj *par, DataObj *d):Plot(par, d)
+{
+	FileIO(INIT_VARS);
+	x1 = 0.0;			x2 = 100.0;					xstep = 0.5;		dl = 0L;
+	if(lastFunc2D && lastFunc2D[0] && lastParam2D && lastParam2D[0]) {
+		cmdxy = (char*)memdup(lastFunc2D, (int)strlen(lastFunc2D)+1, 0);
+		parxy = (char*)memdup(lastParam2D, (int)strlen(lastParam2D)+1, 0);
+		}
+	if(!cmdxy || !parxy) {
+		cmdxy = (char*)malloc(20*sizeof(char));		parxy = (char*)malloc(20*sizeof(char));
+		if(cmdxy) rlp_strcpy(cmdxy, 20, "a+b*x^c");
+		if(parxy) rlp_strcpy(parxy, 20, "a=1; b=1; c=0.1;");
+		}
+	Id = GO_FITFUNC;
+}
+
+
+FitFunc::FitFunc(int src):Plot(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+FitFunc::~FitFunc()
+{
+	int i;
+
+	if(Symbols) {
+		for(i = 0; i< nPoints; i++) if(Symbols[i]) DeleteGO(Symbols[i]);
+		free(Symbols);
+		}
+	if(cmdxy) free(cmdxy);		cmdxy = 0L;
+	if(parxy) free(parxy);		parxy = 0L;
+	if(ssXref) free(ssXref);	ssXref = 0L;
+	if(ssYref) free(ssYref);	ssYref = 0L;
+	if(dl) DeleteGO(dl);		dl = 0L;
+	if(name) free(name);		name=0L;
+	Undo.InvalidGO(this);
+}
+
+bool
+FitFunc::SetSize(int select, double value)
+{
+	int i;
+
+	switch(select & 0xfff){
+	case SIZE_SYMBOL:
+	case SIZE_SYM_LINE:
+		if(Symbols)	for(i = 0; i < nPoints; i++) 
+			if(Symbols[i]) Symbols[i]->SetSize(select, value);
+		return true;
+		}
+	return false;
+}
+
+bool
+FitFunc::SetColor(int select, DWORD col)
+{
+	int i;
+
+	switch(select) {
+	case COL_SYM_LINE:
+	case COL_SYM_FILL:
+		if(Symbols) for(i = 0; i < nPoints; i++)
+			if(Symbols[i]) Symbols[i]->SetColor(select, col);
+		return true;
+		}
+	return false;
+}
+
+void
+FitFunc::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(!data || x1 >= x2) return;
+	dirty = false;
+	if(!dl && (dl = new Function(this, data, "Fitted function"))) {
+		dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
+		dl->SetSize(SIZE_XSTEP, xstep);			dl->Command(CMD_SETFUNC, cmdxy, 0L);
+		dl->Command(CMD_SETPARAM, parxy, 0L);	dl->Command(CMD_SET_LINE, &Line, 0L);
+		dl->Update(o, UNDO_CONTINUE);
+		}
+	if(dl && o) {
+		dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
+		dl->SetSize(SIZE_XSTEP, xstep);			dl->Command(CMD_SETFUNC, cmdxy, 0L);
+		dl->Command(CMD_SETPARAM, parxy, 0L);	dl->Command(CMD_SET_LINE, &Line, 0L);
+		dl->DoPlot(o);
+		}
+	if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->DoPlot(o);
+}
+
+bool 
+FitFunc::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	MouseEvent *mev;
+	LineDEF *ld;
+
+	switch(cmd) {
+	case CMD_LEGEND:
+		if(tmpl && ((GraphObj*)tmpl)->Id == GO_LEGEND && dl && dl->Id == GO_FUNCTION) {
+			ld = dl->GetLine();
+			if(Symbols) {
+				for (i = 0; i < nPoints && i < 100; i++)
+					if(Symbols[i]) ((Legend*)tmpl)->HasSym(ld, Symbols[i], "Fitted function");
+				}
+			else ((Legend*)tmpl)->HasFill(ld, 0L, dl->name);
+			return true;
+			}
+		return false;
+	case CMD_ENDDIALOG:
+		if(!cmdxy || !parxy) return false;
+		if(i = (int)strlen(cmdxy)) {
+			if(lastFunc2D = (char*)realloc(lastFunc2D, i+2))
+				rlp_strcpy(lastFunc2D, i+1, cmdxy);
+			}
+		if(i = (int)strlen(parxy)) {
+			if(lastParam2D = (char*)realloc(lastParam2D, i+2))
+				rlp_strcpy(lastParam2D, i+1, parxy);
+			}
+		return true;
+	case CMD_SCALE:
+		if(dl) return dl->Command(cmd, tmpl, o);
+		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
+		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
+		return true;
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		mev = (MouseEvent *) tmpl;
+		switch(mev->Action) {
+		case MOUSE_LBUP:
+			//select objects invers to plot order
+			if(Symbols && !CurrGO) for(i = nPoints-1; i >=0; i--)
+				if(Symbols[i] && Symbols[i]->Command(cmd, tmpl, o))return true;
+			break;
+			}
+		if(dl) return dl->Command(cmd, tmpl, o);
+		return false;
+	case CMD_AUTOSCALE:
+		if(dirty) {
+			if(!dl && (dl = new Function(this, data, "Fitted function"))) {
+				dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
+				dl->SetSize(SIZE_XSTEP, xstep);			dl->Command(CMD_SETFUNC, cmdxy, 0L);
+				dl->Command(CMD_SETPARAM, parxy, 0L);	dl->Command(CMD_SET_LINE, &Line, 0L);
+				dl->Update(o, UNDO_CONTINUE);
+				}
+			if(dl) {
+				dl->Command(cmd, tmpl, o);
+				memcpy(&Bounds, &dl->Bounds, sizeof(fRECT));
+				}
+			if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i])Symbols[i]->Command(cmd, tmpl, o);
+			dirty = false;
+			}
+		return true;
+	case CMD_UPDATE:
+		if(Symbols) {
+			SavVarObs((GraphObj**)Symbols, nPoints, UNDO_CONTINUE);
+			for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
+			}
+		Undo.String(this, &parxy, UNDO_CONTINUE);
+		do_fitfunc(data, ssXref, ssYref, 0L, &parxy, cmdxy, conv, maxiter, &chi2);
+		if(!dl) dl = new Function(this, data, "Fitted function");
+		if(dl){
+			dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
+			dl->SetSize(SIZE_XSTEP, xstep);			dl->Command(CMD_SETFUNC, cmdxy, 0L);
+			dl->Command(CMD_SETPARAM, parxy, 0L);	dl->Command(CMD_SET_LINE, &Line, 0L);
+			dl->Update(o, UNDO_CONTINUE);
+			}
+		dirty = true;
+		if(parent) parent->Command(CMD_MRK_DIRTY, 0L, o);
+		return true;
+	case CMD_DELOBJ:
+		if(!parent) return false;
+		if(tmpl && tmpl == dl) return parent->Command(CMD_DELOBJ, this, o);
+		else if(DeleteGOL((GraphObj***)&Symbols,nPoints,(GraphObj*)tmpl,o)) return parent->Command(CMD_REDRAW,0L,o);
+		else if(dl) return dl->Command(cmd, tmpl, o);
+		return false;
+	case CMD_MRK_DIRTY:
+		dirty = true;
+		if(dl){
+			dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
+			dl->SetSize(SIZE_XSTEP, xstep);
+			}
+	case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_SET_DATAOBJ:
+		if(dl) dl->Command(cmd, tmpl, o);
+		if(Symbols) for(i = 0; i < nPoints; i++)
+			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
+		Id = GO_FITFUNC;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_SYMTEXT:		case CMD_SYMTEXT_UNDO:	case CMD_SYM_RANGETEXT:
+	case CMD_SYMTEXTDEF:	case CMD_SYM_TYPE:
+		if(Symbols) for(i = 0; i < nPoints; i++)
+			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_SAVE_SYMBOLS:
+		return SavVarObs((GraphObj **)Symbols, nPoints, 0L);
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// normal quantile plot and derivates
+NormQuant::NormQuant(GraphObj *par, DataObj *d, char* range)
+	:Plot(par, d)
+{
+	FileIO(INIT_VARS);
+	if(range && range[0]) ssRef = (char*)memdup(range, (int)strlen(range)+1, 0);
+	else ssRef = 0L;
+	Id = GO_NORMQUANT;
+}
+
+NormQuant::NormQuant(GraphObj *par, DataObj *d, double *val, int nval)
+	:Plot(par, d)
+{
+	FileIO(INIT_VARS);		ssRef = 0L;
+	if(val && nval) {
+		src_data = (double*)memdup(val, nval*sizeof(double), 0);
+		SortArray(nData = nval, src_data);		ProcessData();
+		}
+	Id = GO_NORMQUANT;
+}
+
+NormQuant::NormQuant(int src):Plot(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		if(nData)SortArray(nData, src_data);	ProcessData();
+		}
+}
+
+NormQuant::~NormQuant()
+{
+	if(ssRef) free(ssRef);		ssRef = 0L;
+	if(x_info) free(x_info);	x_info = 0L;
+	if(y_info) free(y_info);	y_info = 0L;
+	if(x_vals) free(x_vals);	x_vals = 0L;
+	if(y_vals) free(y_vals);	y_vals = 0L;
+	if(src_data)free(src_data);	src_data = 0L;
+	if(sy)delete(sy);
+}
+
+void
+NormQuant::DoPlot(anyOutput *o)
+{
+	int i;
+
+	//draw symbols
+	if(sy && y_vals && src_data && y_vals && nValidData) {
+		sy->SetSize(SIZE_SYMBOL, defs.GetSize(SIZE_SYMBOL)/10.0);
+		for(i = 0; i < nValidData; i++) {
+			sy->SetSize(SIZE_XPOS, x_vals[i]);
+			sy->SetSize(SIZE_YPOS, y_vals[i]);
+			sy->DoPlot(o);
+			}
+		}
+}
+
+bool
+NormQuant::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch(cmd) {
+	case CMD_LEGEND:
+		if(sy) ((Legend*)tmpl)->HasSym(0L, sy, x_info ? x_info : (char*)"Data");
+		return true;
+	case CMD_SCALE:
+		return true;
+	case CMD_UPDATE:
+		return true;
+	case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_SET_DATAOBJ:
+		Id = GO_NORMQUANT;
+		if(sy) sy->Command(cmd, tmpl, o);
+		data = (DataObj *)tmpl;
+		return true;
+		}
+	return false;
+}
+
+bool
+NormQuant::ProcessData()
+{
+	int i, r, c, n;
+	AccRange *rD;
+	double y, dtmp, sum;
+
+	if(data && ssRef && ssRef[0] && (rD = new AccRange(ssRef))) {
+		if((n = rD->CountItems()) && (src_data = (double*)realloc(src_data, n * sizeof(double)))){
+			for(nData = 0, rD->GetFirst(&c, &r); rD->GetNext(&c, &r); ) {
+				if(data->GetValue(r, c, &dtmp)) src_data[nData++] = dtmp;
+				}
+			if(nData)SortArray(nData, src_data);
+			}
+		if(y_info = (char*)malloc(20)){
+			rlp_strcpy(y_info, 20, "Normal quantiles");
+			}
+		x_info = rD->RangeDesc(data, 2);
+		delete rD;
+		}
+	if(src_data && nData) {
+		Bounds.Ymin = HUGE_VAL;			Bounds.Ymax = -HUGE_VAL;
+		x_vals = (double*)realloc(x_vals, nData * sizeof(double));
+		y_vals = (double*)realloc(y_vals, nData * sizeof(double));
+		for(n = i = 0, sum = dtmp = 1.0/((double)nData); i < (nData-1); i++ ) {
+			y = distinv(norm_dist, 0.0, 1.0, sum, 0.5);
+			if(y > -HUGE_VAL && y < HUGE_VAL) {
+				y_vals[n] = y;			x_vals[n] = src_data[i];
+				if(y < Bounds.Ymin) Bounds.Ymin = y;
+				if(y > Bounds.Ymax) Bounds.Ymax = y;
+				n++;
+				}
+			sum += dtmp;
+			}
+		Bounds.Xmax = src_data[nData-1];	Bounds.Xmin = src_data[0];
+		if(Bounds.Ymax <= Bounds.Ymin) {
+			Bounds.Ymin = -5.0;		Bounds.Ymax = 5.0;
+			}
+		nValidData = n;
+		return (n > 3);
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Contour Plot
+ContourPlot::ContourPlot(GraphObj *par, DataObj *d)
+	:Plot(par, d)
+{
+	FileIO(INIT_VARS);		Id = GO_CONTOUR;
+}
+
+ContourPlot::ContourPlot(int src):Plot(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+ContourPlot::~ContourPlot()
+{
+	int i;
+
+	if(Symbols) {
+		for(i = 0; i < nSym; i++) if(Symbols[i]) DeleteGO(Symbols[i]);
+		free(Symbols);
+		Symbols = 0L;		nSym = 0;
+		}
+	if(Labels) {
+		for(i = 0; i < nLab; i++) if(Labels[i]) DeleteGO(Labels[i]);
+		free(Labels);
+		Labels = 0L;		nLab = 0;
+		}
+	if(zAxis)			DeleteGO(zAxis);
+	if(ssRefX) free(ssRefX);	if(ssRefY) free(ssRefY);
+	if(ssRefZ) free(ssRefZ);	ssRefX = ssRefY = ssRefZ = 0L;
+	Undo.InvalidGO(this);
+	if(name) free(name);		name=0L;
+	if(val) free(val);		val = 0L;
+}
+
+bool
+ContourPlot::SetSize(int select, double value)
+{
+	int i;
+
+	switch(select & 0xfff){
+	case SIZE_SYMBOL:		case SIZE_SYM_LINE:
+		if(Symbols) for(i = 0; i < nSym; i++) 
+			if(Symbols[i]) Symbols[i]->SetSize(select, value);
+		return true;
+	case SIZE_LB_XDIST:		case SIZE_LB_YDIST:
+		if(Labels) for(i = 0; i < nLab; i++) 
+			if(Labels[i]) Labels[i]->SetSize(select, value);
+		return true;
+	}
+	return false;
+}
+
+bool
+ContourPlot::SetColor(int select, DWORD col)
+{
+	int i;
+
+	switch(select) {
+	case COL_SYM_LINE:	case COL_SYM_FILL:
+		if(Symbols) for(i = 0; i < nSym; i++)
+			if(Symbols[i]) Symbols[i]->SetColor(select, col);
+		return true;
+		}
+	return false;
+}
+
+void 
+ContourPlot::DoPlot(anyOutput *o)
+{
+	FillDEF bg_fill={0, 0x0L, 1.0, 0L, 0x0L};
+	LineDEF bg_line = {0.0, 1.0, 0x0L, 0x0L};
+	POINT clp[4];
+	int i;
+
+	if(!zAxis){
+		DoAxis(o);
+		DoTriangulate();
+		}
+	if(zAxis) {
+		clp[0].x = clp[3].x = iround(o->Box1.Xmin);	clp[0].y = clp[1].y = iround(o->Box1.Ymin);
+		clp[1].x = clp[2].x = iround(o->Box1.Xmax);	clp[2].y = clp[3].y = iround(o->Box1.Ymax);
+		ClipBezier(0L, 0L, clp[0], clp[0], clp[0], clp[0], &clp[0], &clp[2]);	//set clipping rectangle
+		bg_fill.color = bg_fill.color2 = bg_line.color = zAxis->GradColor(z_axis.min);
+		o->SetFill(&bg_fill);				o->SetLine(&bg_line);
+		o->oPolygon(clp, 4, 0L);
+		zAxis->Command(CMD_DRAWPG, 0L, o);
+		if(Symbols) {
+			for(i = 0; i < nSym; i++) if(Symbols[i]) Symbols[i]->DoPlot(o);
+			}
+		if(Labels) {
+			for(i = 0; i < nLab; i++) if(Labels[i]) Labels[i]->DoPlot(o);
+			}
+		zAxis->DoPlot(o);
+		}
+}
+
+bool
+ContourPlot::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	int i;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		mev = (MouseEvent *) tmpl;
+		switch(mev->Action) {
+		case MOUSE_LBUP:
+			if(Symbols) for (i = nSym-1; i >= 0; i--)
+				if(Symbols[i] && Symbols[i]->Command(cmd, tmpl, o)) return true;
+			if(Labels) for (i = nLab-1; i >= 0; i--)
+				if(Labels[i] && Labels[i]->Command(cmd, tmpl, o)) return true;
+			if(zAxis && zAxis->Command(cmd, tmpl, o)) return true;
+			break;
+			}
+		break;
+	case CMD_SETTEXTDEF:
+		if(Labels) for (i = nLab-1; i >= 0; i--)
+			if(Labels[i]) Labels[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_SCALE:
+		if(Symbols) for (i = nSym-1; i >= 0; i--)
+			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
+		if(Labels) for (i = nLab-1; i >= 0; i--)
+			if(Labels[i]) Labels[i]->Command(cmd, tmpl, o);
+		if(zAxis) zAxis->Command(cmd, tmpl, o);
+		return true;
+	case CMD_DELOBJ:
+		if(parent && DeleteGOL((GraphObj***)&Symbols, nSym, (GraphObj*) tmpl, o))
+			return parent->Command(CMD_REDRAW,0L,o);
+		if(parent && DeleteGOL((GraphObj***)&Labels, nLab, (GraphObj*) tmpl, o))
+			return parent->Command(CMD_REDRAW,0L,o);
+		return false;
+	case CMD_UPDATE:
+		if(Symbols) Undo.DropListGO(this, (GraphObj***)&Symbols, &nSym, UNDO_CONTINUE);
+		if(Labels) Undo.DropListGO(this, (GraphObj***)&Labels, &nLab, UNDO_CONTINUE);
+		LoadData(ssRefX, ssRefY, ssRefZ);
+//		if(zAxis) zAxis->Command(CMD_RECALC, tmpl, o);
+//		else DoAxis(o);
+		DoAxis(o);
+		if(zAxis) DoTriangulate();
+		return true;
+	case CMD_RECALC:
+		if(zAxis) zAxis->Command(cmd, tmpl, o);
+		else DoAxis(o);
+		if(zAxis) DoTriangulate();
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_CONTOUR;
+		if(Symbols) for (i = nSym-1; i >= 0; i--)
+			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
+		if(Labels) for (i = nLab-1; i >= 0; i--)
+			if(Labels[i]) Labels[i]->Command(cmd, tmpl, o);
+		if(zAxis) zAxis->Command(cmd, tmpl, o);
+		break;
+	case CMD_MRK_DIRTY:
+		dirty = true;
+	case CMD_SETSCROLL:		case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		return false;
+	case CMD_USEAXIS:
+		return UseAxis(*((int*)tmpl));
+	case CMD_SAVE_SYMBOLS:
+		return SavVarObs((GraphObj **)Symbols, nSym, 0L);
+	case CMD_SAVE_LABELS:
+		return SavVarObs((GraphObj **)Labels, nLab, 0L);
+	case CMD_SYMTEXT:	case CMD_SYMTEXT_UNDO:		case CMD_SYM_RANGETEXT:
+	case CMD_SYMTEXTDEF:	case CMD_SYM_TYPE:
+		if(Symbols) for(i = 0; i < nSym; i++)
+			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
+		return true;
+		}
+	return false;
+}
+
+bool
+ContourPlot::LoadData(char *xref, char *yref, char *zref)
+{
+	AccRange *rX, *rY, *rZ;
+	int i, n, cx, cy, cz, rx, ry, rz;
+	int nVals, nTxt, nTime;
+	bool bValid;
+	anyResult arx, ary, arz;
+
+	if(!data || !xref || !xref[0] || !yref || !yref[0] || !zref || !zref[0]) return false;
+	if(!(rX = new AccRange(xref)) || !(rY = new AccRange(yref)) || !(rZ = new AccRange(zref))) return false;
+	if(val) free(val);		val = 0L;		nval = 0;
+	if(3 < (n = rX->CountItems())) val = (fPOINT3D*) malloc((n+1)*sizeof(fPOINT3D));
+	Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+	xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
+	xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
+	if(rX->DataTypes(data, &nVals, &nTxt, &nTime)){
+		if(!nVals && nTime > 1 && nTime > nTxt) x_dtype = ET_DATETIME;
+		else x_dtype = 0;
+		}
+	if(val && rX->GetFirst(&cx, &rx) && rY->GetFirst(&cy, &ry) && rZ->GetFirst(&cz, &rz)) {
+		i = 0;
+		while(rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry) && rZ->GetNext(&cz, &rz)) {
+			if(data->GetResult(&arx, rx, cx) && data->GetResult(&ary, ry, cy) && data->GetResult(&arz, rz, cz) 
+				&& ary.type == ET_VALUE && arz.type == ET_VALUE) {
+				bValid = false;
+				if(x_dtype == ET_DATETIME && (arx.type == ET_DATE || arx.type == ET_TIME || arx.type == ET_DATETIME))
+					bValid = true;
+				else if(!x_dtype && arx.type == ET_VALUE) bValid = true;
+				if(bValid) {
+					val[i].fx = arx.value;			val[i].fy = ary.value;
+					val[i].fz = arz.value;			i++;
+					if(arx.value < Bounds.Xmin) Bounds.Xmin = arx.value;
+					if(arx.value > Bounds.Xmax) Bounds.Xmax = arx.value;
+					if(ary.value < Bounds.Ymin) Bounds.Ymin = ary.value;
+					if(ary.value > Bounds.Ymax) Bounds.Ymax = ary.value;
+					if(arz.value < zBounds.fx) zBounds.fx = arz.value;
+					if(arz.value > zBounds.fy) zBounds.fy = arz.value;
+					}
+				}
+			}
+		xBounds.fx = Bounds.Xmin;		xBounds.fy = Bounds.Xmax;
+		yBounds.fx = Bounds.Ymin;		yBounds.fy = Bounds.Ymax;
+		nval = i;
+		}
+	delete	rX;		delete rY;		delete rZ;
+	return (nval >3);
+}
+
+bool
+ContourPlot::DoTriangulate()
+{
+	int i;
+	double srz, zsum;
+	Triangle *trl, *trn, *trc;
+	Triangulate *tria;
+
+	//check minima and maxima
+	if(!val || nval < 4) return false;
+	if((flags & 0x03) == 0x02 || Bounds.Xmax <= Bounds.Xmin || Bounds.Ymax <= Bounds.Ymin || zBounds.fy <= zBounds.fx) {
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
+		xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
+		for(i = 0, zsum = 0.0; i < nval; i++) {
+			if(val[i].fx < Bounds.Xmin) Bounds.Xmin = val[i].fx;
+			if(val[i].fx > Bounds.Xmax) Bounds.Xmax = val[i].fx;
+			if(val[i].fy < Bounds.Ymin) Bounds.Ymin = val[i].fy;
+			if(val[i].fy > Bounds.Ymax) Bounds.Ymax = val[i].fy;
+			if(val[i].fz < zBounds.fx) zBounds.fx = val[i].fz;
+			if(val[i].fz > zBounds.fy) zBounds.fy = val[i].fz;
+			zsum += val[i].fz;
+			}
+		xBounds.fx = Bounds.Xmin;		xBounds.fy = Bounds.Xmax;
+		yBounds.fx = Bounds.Ymin;		yBounds.fy = Bounds.Ymax;
+		}
+	if(Bounds.Xmax <= Bounds.Xmin || Bounds.Ymax <= Bounds.Ymin || zBounds.fy <= zBounds.fx) return false;
+	//setup two super triangles
+	switch (flags & 0x03) {
+		case 0:		default:
+			srz = zBounds.fx;				break;
+		case 1:
+			srz = zBounds.fy;				break;
+		case 2:
+			srz = (zsum/((double)nval));	break;
+		case 3:
+			srz = sr_zval;					break;
+			}
+	if(!(trl = new Triangle()) || !(trn = new Triangle())) return false;
+	trl->pt[0].fz = trl->pt[1].fz = trl->pt[2].fz = srz;
+	trn->pt[0].fz = trn->pt[1].fz = trn->pt[2].fz = srz;
+	trl->pt[0].fx = trn->pt[0].fx = trl->pt[2].fx = Bounds.Xmin-(Bounds.Xmax-Bounds.Xmin)*1.0E-8;
+	trl->pt[0].fy = trn->pt[0].fy = trn->pt[1].fy = Bounds.Ymin-(Bounds.Ymax-Bounds.Ymin)*1.0E-8;
+	trl->pt[1].fx = trn->pt[2].fx = trn->pt[1].fx = Bounds.Xmax+(Bounds.Xmax-Bounds.Xmin)*1.0E-8;
+	trl->pt[1].fy = trn->pt[2].fy = trl->pt[2].fy = Bounds.Ymax+(Bounds.Ymax-Bounds.Ymin)*1.0E-8;
+	trl->SetRect();			trn->SetRect();
+	trl->next = trn;		trn->next = 0L;
+	//do triangulation
+	if(!(tria = new Triangulate(trl))) {
+		delete tria;		delete trl;
+		return false;
+		}
+	for(i = 0; i < nval; i++) {
+		tria->AddVertex(&val[i]);
+		}
+	trl = tria->trl;		delete tria;		tria = 0L;
+	//cut surface: create isopleths
+	if(!zAxis) DoAxis(0L);
+	if(zAxis && !zAxis->NumTicks) zAxis->CreateTicks();
+	if(zAxis) for(i = 0; i < zAxis->NumTicks; i++) {
+		trc = trl;
+		while(trc) {
+			trn = trc->next;
+			if(zAxis->Ticks[i]) trc->IsoLine(zAxis->Ticks[i]->GetSize(SIZE_MINE), zAxis->Ticks[i]);
+			trc = trn;	
+			}
+		if(zAxis->Ticks[i]) zAxis->Ticks[i]->ProcSeg();
+		}
+	//create symbols
+	if(flags & 0x30) DoSymbols(trl);
+	//free triangle list
+	trc = trl;
+	while(trc) {
+		trn = trc->next; 		delete trc;			trc = trn;	
+		}
+	return false;
+}
+
+bool
+ContourPlot::DoAxis(anyOutput *o)
+{
+	TextDEF tlbdef;
+
+	if(!zAxis) {
+		z_axis.min = zBounds.fx;		z_axis.max = zBounds.fy;
+		NiceAxis(&z_axis, 5);
+		z_axis.flags = AXIS_AUTOSCALE | AXIS_POSTICKS;
+		z_axis.loc[0].fx = z_axis.loc[1].fx = defs.GetSize(SIZE_DRECT_LEFT) + defs.GetSize(SIZE_TEXT);
+		z_axis.loc[0].fy = defs.GetSize(SIZE_DRECT_TOP) + defs.GetSize(SIZE_TEXT);
+		z_axis.loc[1].fy = defs.GetSize(SIZE_DRECT_TOP) + defs.GetSize(SIZE_TEXT)*10.0;
+		if(!(zAxis = new Axis(this, data, &z_axis, AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_POSTICKS)))return false;
+		zAxis->SetSize(SIZE_LB_XDIST, -NiceValue(DefSize(SIZE_AXIS_TICKS)*3.0)); 
+		zAxis->SetSize(SIZE_TLB_XDIST, NiceValue(DefSize(SIZE_AXIS_TICKS)*2.0)); 
+		tlbdef.ColTxt = defs.Color(COL_AXIS);				tlbdef.ColBg = 0x00ffffffL;
+		tlbdef.RotBL = tlbdef.RotCHAR = 0.0;				tlbdef.iSize = 0;
+		tlbdef.fSize = DefSize(SIZE_TICK_LABELS);			tlbdef.Align = TXA_VCENTER | TXA_HLEFT;
+		tlbdef.Style = TXS_NORMAL;					tlbdef.Mode = TXM_TRANSPARENT;
+		tlbdef.Font = FONT_HELVETICA;					tlbdef.text = 0L;
+		zAxis->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);		zAxis->type = 4;
+		zAxis->CreateTicks();						zAxis->moveable = 1;
+		}
+	else if(z_axis.flags & AXIS_AUTOSCALE) {
+		z_axis.min = zBounds.fx;		z_axis.max = zBounds.fy;
+		NiceAxis(&z_axis, 4);
+		if(zAxis) {
+			zAxis->Command(CMD_AUTOSCALE, &z_axis, o);
+			zAxis->Command(CMD_RECALC, 0L, o);
+			}
+		}
+	return true;
+}
+
+void
+ContourPlot::DoSymbols(Triangle *trl)
+{
+	int i;
+	Triangle *trc;
+	fPOINT3D *vx;
+	bool isValid;
+	char lb_buff[20];
+	TextDEF td = {0x0L, 0x00ffffffL, defs.GetSize(SIZE_SYMBOL), 0.0, 0.0, 0, TXA_HLEFT | TXA_VCENTER, TXM_TRANSPARENT,
+		TXS_NORMAL, FONT_HELVETICA, lb_buff+1};
+
+	if(!val || nval < 4 || !trl || Symbols || Labels || !(flags & 0x30)) return;
+	if(!(vx = (fPOINT3D*)malloc(nval * sizeof(fPOINT3D)))) return;
+	switch(flags & 0x30) {
+	case 0x10:			// at minima
+		for(i = 0; i < nval; i++) {
+			trc = trl;	isValid = true;
+			do {
+				if((trc->pt[trc->order[1]].fx == val[i].fx && trc->pt[trc->order[1]].fy == val[i].fy
+					&& trc->pt[trc->order[1]].fz == val[i].fz) || (trc->pt[trc->order[2]].fx == val[i].fx 
+					&& trc->pt[trc->order[2]].fy == val[i].fy && trc->pt[trc->order[2]].fz == val[i].fz))
+					isValid = false;
+				trc = trc->next;
+				} while(trc && isValid);
+			if(isValid) {
+				vx[nSym].fx = val[i].fx;		vx[nSym].fy = val[i].fy;
+				vx[nSym].fz = val[i].fz;		nSym++;
+				}
+			}
+		break;
+	case 0x20:			// at maxima
+		for(i = 0; i < nval; i++) {
+			trc = trl;	isValid = true;
+			do {
+				if((trc->pt[trc->order[0]].fx == val[i].fx && trc->pt[trc->order[0]].fy == val[i].fy
+					&& trc->pt[trc->order[0]].fz == val[i].fz) || (trc->pt[trc->order[1]].fx == val[i].fx 
+					&& trc->pt[trc->order[1]].fy == val[i].fy && trc->pt[trc->order[1]].fz == val[i].fz))
+					isValid = false;
+				trc = trc->next;
+				} while(trc && isValid);
+			if(isValid) {
+				vx[nSym].fx = val[i].fx;		vx[nSym].fy = val[i].fy;
+				vx[nSym].fz = val[i].fz;		nSym++;
+				}
+			}
+		break;
+	case 0x30:			// all values
+		for(nSym = 0; nSym < nval; nSym++) {
+			vx[nSym].fx = val[nSym].fx;		vx[nSym].fy = val[nSym].fy;
+			vx[nSym].fz = val[nSym].fz;
+			}
+		break;
+	}
+	// create symbols
+	if(nSym && (flags & 0x40) && (Symbols = (Symbol**)malloc(nSym * sizeof(Symbol*)))) {
+		for(i =0; i < nSym; i++) {
+			Symbols[i] = new Symbol(this, data, vx[i].fx, vx[i].fy, 0);
+			}
+		}
+	// add labels to symbols?
+	if(nSym && (flags & 0x40) && (Labels = (Label**)malloc(nSym * sizeof(Label*)))) {
+		for(nLab = 0; nLab < nSym; nLab++) {
+			WriteNatFloatToBuff(lb_buff, vx[nLab].fz);
+			if(Labels[nLab] = new Label(this, data, vx[nLab].fx, vx[nLab].fy, &td, LB_X_DATA | LB_Y_DATA)){
+				Labels[nLab]->SetSize(SIZE_LB_XDIST, defs.GetSize(SIZE_SYMBOL));
+				}
+			}
+		}
+	free(vx);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// three dimensional graph
+Plot3D::Plot3D(GraphObj *par, DataObj *d, DWORD flags):Plot(par, d)
+{
+	RotDef = (double*)malloc(6 *sizeof(double));
+	FileIO(INIT_VARS);
+	Id = GO_PLOT3D;
+	crea_flags = flags;
+	xBounds.fx = yBounds.fx = zBounds.fx = Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
+	xBounds.fy = yBounds.fy = zBounds.fy = Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+}
+
+Plot3D::Plot3D(int src):Plot(0L, 0L)
+{
+	int i;
+
+	RotDef = (double*)malloc(6 *sizeof(double));
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		//now set parent in all children
+		if(Axes) for(i = 0; i < nAxes; i++)
+			if(Axes[i]) Axes[i]->parent = this;
+		if(plots) for(i = 0; i < nPlots; i++)
+			if(plots[i]) plots[i]->parent = this;
+		}
+}
+
+Plot3D::~Plot3D()
+{
+	int i;
+
+	if(plots) {
+		for(i = 0; i < nPlots; i++) if(plots[i]) DeleteGO(plots[i]);
+		free(plots);
+		}
+	if(Axes) {
+		for(i = 0; i < nAxes; i++) if(Axes[i]) DeleteGO(Axes[i]);
+		free(Axes);
+		}
+	plots = 0L;		nPlots = nAxes = 0;
+	if(nscp > 0 && nscp <= nPlots && Sc_Plots) free(Sc_Plots);
+	nscp = 0;			Sc_Plots = 0L;
+	if(drag) DeleteGO(drag);	drag = 0L;
+	if(dispObs) free(dispObs);	dispObs = 0L;
+	free(RotDef);
+	if(name) free(name);		name=0L;
+	Undo.InvalidGO(this);
+}
+
+double
+Plot3D::GetSize(int select)
+{
+	AxisDEF *ax;
+
+	switch(select){
+	//The Bounds values must be returned by every plot:
+	//   they are necessary for scaling !
+	case SIZE_BOUNDS_XMIN:
+		if(Axes && nAxes >2 && Axes[0] && (ax = Axes[0]->GetAxis()))
+			return (ax->flags & AXIS_INVERT) ? ax->max : ax->min;
+		return 0.0;
+	case SIZE_BOUNDS_XMAX:
+		if(Axes && nAxes >2 && Axes[0] && (ax = Axes[0]->GetAxis()))
+			return (ax->flags & AXIS_INVERT) ? ax->min : ax->max;
+		return 0.0;
+	case SIZE_BOUNDS_YMIN:
+		if(Axes && nAxes >2 && Axes[1] && (ax = Axes[1]->GetAxis())) 
+			return (ax->flags & AXIS_INVERT) ? ax->max : ax->min;
+		return 0.0;
+	case SIZE_BOUNDS_YMAX:
+		if(Axes && nAxes >2 && Axes[1] && (ax = Axes[1]->GetAxis())) 
+			return (ax->flags & AXIS_INVERT) ? ax->min : ax->max;
+		return 0.0;
+	case SIZE_BOUNDS_ZMIN:
+		if(Axes && nAxes >2 && Axes[2] && (ax = Axes[2]->GetAxis())) 
+			return (ax->flags & AXIS_INVERT) ? ax->max : ax->min;
+		return 0.0;
+	case SIZE_BOUNDS_ZMAX:
+		if(Axes && nAxes >2 && Axes[2] && (ax = Axes[2]->GetAxis())) 
+			return (ax->flags & AXIS_INVERT) ? ax->min : ax->max;
+		return 0.0;
+	case SIZE_XPOS:		case SIZE_XPOS+4:		return cu1.fx;
+	case SIZE_XPOS+1:	case SIZE_XPOS+5:		return cu2.fx;
+	case SIZE_XPOS+2:	case SIZE_XPOS+6:		return cu2.fx;
+	case SIZE_XPOS+3:	case SIZE_XPOS+7:		return cu1.fx;
+	case SIZE_YPOS:		case SIZE_YPOS+1:		case SIZE_YPOS+2:
+	case SIZE_YPOS+3:			return cu1.fy;
+	case SIZE_YPOS+4:	case SIZE_YPOS+5:		case SIZE_YPOS+6:
+	case SIZE_YPOS+7:			return cu2.fy;
+	case SIZE_ZPOS:		case SIZE_ZPOS+1:		case SIZE_ZPOS+4:
+	case SIZE_ZPOS+5:			return cu1.fz;
+	case SIZE_ZPOS+2:	case SIZE_ZPOS+3:		case SIZE_ZPOS+6:
+	case SIZE_ZPOS+7:			return cu2.fz;
+	case SIZE_XCENTER:	return rotC.fx;
+	case SIZE_YCENTER:	return rotC.fy;
+	case SIZE_ZCENTER:	return rotC.fz;
+	default:
+		return DefSize(select);
+		}
+}
+
+bool
+Plot3D::SetColor(int select, DWORD col)
+{
+	int i;
+
+	switch(select & 0xfff) {
+	case COL_AXIS:
+		if(Axes) for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->SetColor(select, col);
+		return true;
+		}
+	return false;
+}
+
+void
+Plot3D::DoPlot(anyOutput *o)
+{
+	long i, j;
+
+	nObs = 0;
+	if(!parent || !o) return;
+	if(nscp > 0 && nscp <= nPlots && Sc_Plots) free(Sc_Plots);
+	nscp = 0;			Sc_Plots = 0L;		o->MouseCursor(MC_WAIT, true);
+	if(dirty) DoAutoscale();
+	if(Axes && nAxes >2) {		//if no axes then parent is another Plot3D ...
+		o->LightSource(32.0, 16.0);						CurrAxes = Axes;
+		cu1.fx = cub1.fx;		cu1.fy = cub1.fy;		cu1.fz = cub1.fz;
+		cu2.fx = cub2.fx;		cu2.fy = cub2.fy;		cu2.fz = cub2.fz;
+		rc.fx = rotC.fx;		rc.fy = rotC.fy;		rc.fz = rotC.fz;
+		o->SetSpace(&cu1, &cu2, defs.cUnits, RotDef, &rc, Axes[0]->GetAxis(),
+			Axes[1]->GetAxis(), Axes[2]->GetAxis());
+		if(nAxes >3) {									//DEBUG
+			nAxes = nAxes;
+			}
+		for(i = 0; i< nAxes; i++) if(Axes[i]) Axes[i]->DoPlot(o);
+		}
+	else if(IsPlot3D(parent)) {
+		if (use_xaxis || use_yaxis || use_zaxis)ApplyAxes(o);
+		parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
+		}
+	else CurrAxes = 0L;
+	if(plots) for(i = 0; i < nPlots; i++) if(plots[i]){
+		if(plots[i]->Id >= GO_PLOT && plots[i]->Id < GO_GRAPH) {
+			if(((Plot*)plots[i])->hidden == 0) plots[i]->DoPlot(o);
+			}
+		else plots[i]->DoPlot(o);
+		if(i) {
+			UpdateMinMaxRect(&rDims, plots[i]->rDims.right, plots[i]->rDims.top);
+			UpdateMinMaxRect(&rDims, plots[i]->rDims.left, plots[i]->rDims.bottom);
+			}
+		else memcpy(&rDims, &plots[i]->rDims, sizeof(RECT));
+			}
+	for(i = 0; i< nAxes; i++) if(Axes[i]){
+		UpdateMinMaxRect(&rDims, Axes[i]->rDims.right, Axes[i]->rDims.top);
+		UpdateMinMaxRect(&rDims, Axes[i]->rDims.left, Axes[i]->rDims.bottom);
+		}
+	for(i = j = 1; i < nObs; i++) if(dispObs[i] && dispObs[i]->go) 
+		dispObs[j++] = dispObs[i];
+	nObs = j;
+	if(nObs >1  && dispObs){
+		SortObj();
+		for (i = 1; i <= nObs; i++){
+			for(j = 1; j <= nObs; j++) {
+				if(dispObs[j]->go->Id != GO_LINESEG && i != j) switch(dispObs[i]->go->Id) {
+				case GO_LINESEG:
+				case GO_SPHERE:
+				case GO_PLANE:
+					dispObs[i]->go->Command(CMD_CLIP, dispObs[j]->go, o);
+					break;
+					}
+				//the following line, if included, reduces time but clipping 
+				//   is not complete because of CMD_DRAW_LATER
+//				if(dispObs[j]->Zmin > dispObs[i]->Zmax) break;
+				}
+			}
+		for (i = 1; i <= nObs; i++)	dispObs[i]->go->Command(CMD_REDRAW, 0L, o);
+		}
+	if(IsPlot3D(parent) && (use_xaxis || use_yaxis || use_zaxis))parent->Command(CMD_AXIS, 0L, o);
+	dirty = false;
+	o->MouseCursor(MC_ARROW, true);
+}
+
+void
+Plot3D::DoMark(anyOutput *o, bool mark)
+{
+	RECT upd;
+
+	if(!drag) drag = new Drag3D(this);
+	if(mark && drag) drag->DoPlot(o);
+	else {
+		memcpy(&upd, &rDims, sizeof(RECT));
+		IncrementMinMaxRect(&upd, 6);
+		o->UpdateRect(&upd, false);
+		}
+}
+
+bool
+Plot3D::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	GraphObj **tmpPlots;
+
+	switch (cmd) {
+	case CMD_SCALE:
+		cub1.fx *= ((scaleINFO*)tmpl)->sx.fy;		cub1.fy *= ((scaleINFO*)tmpl)->sy.fy;
+		cub1.fz *= ((scaleINFO*)tmpl)->sz.fy;		cub2.fx *= ((scaleINFO*)tmpl)->sx.fy;
+		cub2.fy *= ((scaleINFO*)tmpl)->sy.fy;		cub2.fz *= ((scaleINFO*)tmpl)->sz.fy;
+		rotC.fx *= ((scaleINFO*)tmpl)->sx.fy;		rotC.fy *= ((scaleINFO*)tmpl)->sy.fy;
+		rotC.fz *= ((scaleINFO*)tmpl)->sz.fy;
+		if(plots) for(i = 0; i < nPlots; i++)
+			if(plots[i]) plots[i]->Command(cmd, tmpl, o);
+		if(Axes) for(i = 0; i < nAxes; i++)
+			if(Axes[i]) Axes[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_MOUSE_EVENT:
+		if(hidden || ((MouseEvent*)tmpl)->Action != MOUSE_LBUP || CurrGO) return false;
+		if(dispObs) for (i = nObs; i > 0; i--)	{
+			if(dispObs[i]) dispObs[i]->go->Command(cmd, tmpl, o);
+			if(CurrGO) return true;
+			}
+		if(IsInRect(&rDims, ((MouseEvent*)tmpl)->x, ((MouseEvent*)tmpl)->y)) {
+			o->ShowMark(CurrGO = this, MRK_GODRAW);
+			return true;
+			}
+		break;
+	case CMD_USEAXIS:
+		if(IsPlot3D(parent)) return UseAxis(*((int*)tmpl));
+		break;
+	case CMD_REG_AXISPLOT:	//notification: plot can handle its own axes
+		if(nscp > 0 && nscp <= nPlots && Sc_Plots)  {
+			for(i = 0; i < nscp; i++)
+				if(Sc_Plots[i] == (GraphObj*)tmpl) return true;
+			if(tmpPlots = (GraphObj**)realloc(Sc_Plots, (nscp+1)*sizeof(GraphObj*))){
+				tmpPlots[nscp++] = (GraphObj *)tmpl;
+				Sc_Plots = tmpPlots;
+				}
+			else {		//memory allocation error
+				nscp = 0;
+				Sc_Plots = 0L;
+				}
+			}
+		else {
+			if(Sc_Plots = (GraphObj **)calloc(1, sizeof(GraphObj*))){
+				Sc_Plots[0] = (GraphObj *)tmpl;
+				nscp = 1;
+				}
+			else nscp = 0;
+			}
+		return true;
+	case CMD_AXIS:			//one of the plots has changed scaling: reset
+		CurrAxes = Axes;
+		if(o) o->SetSpace(&cu1, &cu2, defs.cUnits, RotDef, &rc, Axes[0]->GetAxis(),
+			Axes[1]->GetAxis(), Axes[2]->GetAxis());
+		return true;
+	case CMD_OBJTREE:
+		if(!tmpl || !plots) return false;
+		for(i = 0; i < nPlots; i++) if(plots[i]) {
+			((ObjTree*)tmpl)->Command(CMD_UPDATE, plots[i], 0L);
+			if(plots[i]->Id > GO_PLOT && plots[i]->Id < GO_GRAPH) plots[i]->Command(cmd, tmpl, o);
+			}
+		return true;
+	case CMD_REPL_GO:
+		if(!(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
+		if(plots) for(i = 0; i < nPlots; i++) if(plots[i] && plots[i] == tmpPlots[0]){
+			return dirty = ReplaceGO((GraphObj**)&plots[i], tmpPlots);
+			}
+		return false;
+	case CMD_MRK_DIRTY:
+		if(IsPlot3D(parent)) return parent->Command(cmd, tmpl, o);
+		return dirty = true;
+	case CMD_ADDAXIS:
+		if(AddAxis()){
+			if(parent) return parent->Command(CMD_REDRAW, tmpl, o);
+			}
+		return false;
+	case CMD_SET_GO3D:
+		if(IsPlot3D(parent)) return parent->Command(CMD_REDRAW, 0L, o);
+		return AcceptObj((GraphObj *)tmpl);
+	case CMD_SETSCROLL:				case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_SHIFTLEFT:
+		return Rotate(-0.017452406, 0.0, 0.0, o, true);
+	case CMD_SHIFTRIGHT:
+		return Rotate(0.017452406, 0.0, 0.0, o, true);
+	case CMD_SHIFTUP:
+		return Rotate(0.0, 0.017452406, 0.0, o, true);
+	case CMD_SHIFTDOWN:
+		return Rotate(0.0, -0.017452406, 0.0, o, true);
+	case CMD_CURRIGHT:
+		return Rotate(0.087155742, 0.0, 0.0, o, true);
+	case CMD_CURRLEFT:
+		return Rotate(-0.087155742, 0.0, 0.0, o, true);
+	case CMD_CURRUP:
+		return Rotate(0.0, 0.087155742, 0.0, o, true);
+	case CMD_CURRDOWN:
+		return Rotate(0.0, -0.087155742, 0.0, o, true);
+	case CMD_ADDCHAR:
+		if(tmpl && *((int*)tmpl) == 'r') return Rotate(0.0, 0.0, 0.087155742, o, true);
+		if(tmpl && *((int*)tmpl) == 'l') return Rotate(0.0, 0.0, -0.087155742, o, true);
+		if(tmpl && *((int*)tmpl) == 'R') return Rotate(0.0, 0.0, 0.017452406, o, true);
+		if(tmpl && *((int*)tmpl) == 'L') return Rotate(0.0, 0.0, -0.017452406, o, true);
+		return false;
+	case CMD_LEGEND:
+		if(plots) for(i = 0; i < nPlots; i++)
+			if(plots[i]) plots[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_PLOT3D;
+		data = (DataObj *)tmpl;
+	case CMD_UPDATE:
+		if(plots) for(i = 0; i < nPlots; i++)
+			if(plots[i]) plots[i]->Command(cmd, tmpl, o);
+		if(Axes) for(i = 0; i < nAxes; i++)
+			if(Axes[i]) Axes[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_DELOBJ:
+		if(DeleteGOL((GraphObj***)&plots,nPlots,(GraphObj*)tmpl,o)) return parent->Command(CMD_REDRAW,0L,o);
+		return false;
+	case CMD_MOVE:
+		if(CurrGO && CurrGO->Id == GO_DRAGHANDLE) {
+			CalcRotation(((lfPOINT*)tmpl)[0].fx, ((lfPOINT*)tmpl)[0].fy, o, true);
+			if(parent) return parent->Command(CMD_REDRAW, 0L, 0L);
+			}
+		return true;
+	case CMD_AUTOSCALE:
+		if(dirty) {
+			DoAutoscale();
+			dirty = false;
+			}
+		return true;
+	case CMD_DROP_PLOT:
+		if(!parent || !tmpl || ((GraphObj*)tmpl)->Id < GO_PLOT) return false;
+		if(IsPlot3D(parent)) return parent->Command(cmd, tmpl, o);
+		if(!nPlots) {
+			plots = (GraphObj**)calloc(2, sizeof(GraphObj*));
+			if(plots) {
+				nPlots = 1;					plots[0] = (Plot *)tmpl;
+				plots[0]->parent = this;	CreateAxes();
+				return dirty = parent->Command(CMD_REDRAW, 0L, 0L);
+				}
+			}
+		else {
+			((Plot *)tmpl)->parent = this;
+			tmpPlots = (GraphObj**)memdup(plots, sizeof(GraphObj*) * (nPlots+2), 0);
+			Undo.ListGOmoved(plots, tmpPlots, nPlots);
+			Undo.SetGO(this, &tmpPlots[nPlots++], (Plot *)tmpl, 0L);
+			free(plots);			plots = tmpPlots;
+			return dirty = parent->Command(CMD_REDRAW, 0L, 0L);
+			}
+		return false;
+	case CMD_ADDPLOT:
+		return AddPlot(0x0);
+		}
+	return false;
+}
+
+void *
+Plot3D::ObjThere(int x, int y)
+{
+	if(drag) return drag->ObjThere(x, y);
+	return 0L;
+}
+
+void
+Plot3D::Track(POINT *p, anyOutput *o)
+{
+	fPOINT3D v, iv;
+	POINT pts[5];
+	RECT upd_rc;
+
+	CalcRotation(((lfPOINT*)p)->fx, ((lfPOINT*)p)->fy, o, false);
+	memcpy(&upd_rc, &rDims, sizeof(RECT));
+	IncrementMinMaxRect(&rDims, 3);
+	o->UpdateRect(&upd_rc, false);
+	memcpy(&v, &cu2, sizeof(fPOINT3D));
+	o->cvec2ivec(&v, &iv);
+	pts[0].x = iround(iv.fx);		pts[0].y = iround(iv.fy);
+	UpdateMinMaxRect(&rDims, pts[0].x, pts[0].y);
+	v.fx = cu1.fx;					o->cvec2ivec(&v, &iv); 
+	pts[1].x = iround(iv.fx);		pts[1].y = iround(iv.fy);
+	UpdateMinMaxRect(&rDims, pts[1].x, pts[1].y);
+	v.fy = cu1.fy;					o->cvec2ivec(&v, &iv); 
+	pts[2].x = iround(iv.fx);		pts[2].y = iround(iv.fy);
+	UpdateMinMaxRect(&rDims, pts[2].x, pts[2].y);
+	v.fz = cu1.fz;					o->cvec2ivec(&v, &iv); 
+	pts[3].x = iround(iv.fx);		pts[3].y = iround(iv.fy);
+	UpdateMinMaxRect(&rDims, pts[3].x, pts[3].y);
+	v.fy = cu2.fy;					o->cvec2ivec(&v, &iv); 
+	pts[4].x = iround(iv.fx);		pts[4].y = iround(iv.fy);
+	UpdateMinMaxRect(&rDims, pts[4].x, pts[4].y);
+	o->ShowLine(pts, 5, 0x000000ff);
+	v.fz = cu2.fz;					o->cvec2ivec(&v, &iv);
+	pts[0].x = iround(iv.fx);		pts[0].y = iround(iv.fy);
+	v.fz = cu1.fz;					o->cvec2ivec(&v, &iv); 
+	pts[1].x = iround(iv.fx);		pts[1].y = iround(iv.fy);
+	v.fx = cu2.fx;					o->cvec2ivec(&v, &iv);
+	pts[2].x = iround(iv.fx);		pts[2].y = iround(iv.fy);
+	v.fz = cu2.fz;					o->cvec2ivec(&v, &iv);
+	pts[3].x = iround(iv.fx);		pts[3].y = iround(iv.fy);
+	v.fy = cu1.fy;					o->cvec2ivec(&v, &iv);
+	pts[4].x = iround(iv.fx);		pts[4].y = iround(iv.fy);
+	o->ShowLine(pts, 5, 0x000000ff);
+	v.fy = cu2.fy;	v.fz = cu1.fz;		o->cvec2ivec(&v, &iv);
+	pts[0].x = iround(iv.fx);		pts[0].y = iround(iv.fy);
+	v.fy = cu1.fy;					o->cvec2ivec(&v, &iv);
+	pts[1].x = iround(iv.fx);		pts[1].y = iround(iv.fy);
+	v.fx = cu1.fx;					o->cvec2ivec(&v, &iv);
+	pts[2].x = iround(iv.fx);		pts[2].y = iround(iv.fy);
+	o->ShowLine(pts, 3, 0x000000ff);
+	v.fz = cu2.fz;					o->cvec2ivec(&v, &iv);
+	pts[0].x = iround(iv.fx);		pts[0].y = iround(iv.fy);
+	v.fx = cu2.fx;					o->cvec2ivec(&v, &iv);
+	pts[1].x = iround(iv.fx);		pts[1].y = iround(iv.fy);
+	v.fz = cu1.fz;					o->cvec2ivec(&v, &iv);
+	pts[2].x = iround(iv.fx);		pts[2].y = iround(iv.fy);
+	o->ShowLine(pts, 3, 0x000000ff);
+}
+
+void
+Plot3D::CreateAxes()
+{
+	typedef struct {
+		double x1, y1, z1, x2, y2, z2;
+		DWORD flags;
+		int a_type, t_type;
+		double lb_x, lb_y, tlb_x, tlb_y;
+		int txa;
+		}Axis3Ddef;
+	AxisDEF tmp_axis;
+	double ts = DefSize(SIZE_AXIS_TICKS);
+	int i;
+	if(Axes || !parent)return;
+	TextDEF tlbdef = {parent->GetColor(COL_AXIS), 0x00ffffffL, DefSize(SIZE_TICK_LABELS),
+		0.0, 0.0, 0, TXA_HLEFT | TXA_VCENTER, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, 0L};
+	Axis3Ddef *at = 0L;
+	Axis3Ddef at1[] = {
+		{cub1.fx, cub1.fy, 0.0, cub2.fx, cub1.fy, 0.0,
+			AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_NEGTICKS, 1, 3,
+			0.0, NiceValue((ts+DefSize(SIZE_AXIS_TICKS))*2.0), 0.0,
+			NiceValue(ts * 2.0), TXA_HCENTER | TXA_VTOP},
+		{cub1.fx, cub1.fy, 0.0, cub1.fx, cub2.fy, 0.0,
+			AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_NEGTICKS, 2, 2,
+			-NiceValue((ts+DefSize(SIZE_AXIS_TICKS))*3.0), 0.0,
+			-NiceValue(ts * 2.0), 0.0, TXA_HRIGHT | TXA_VCENTER},
+		{cub1.fx, cub1.fy, 0.0, cub1.fx, cub1.fy, cub2.fz,
+			AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_NEGTICKS, 3, 2,
+			-NiceValue((ts+DefSize(SIZE_AXIS_TICKS))*3.0), 0.0,
+			-NiceValue(ts * 2.0), 0.0, TXA_HRIGHT | TXA_VCENTER}};
+	Axis3Ddef at2[] = {
+		{at1[0].x1, at1[0].y1, at1[2].z2, at1[0].x2, at1[0].y2, at1[2].z2,
+			AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_NEGTICKS, 1, 3,
+			0.0, at1[0].lb_y, 0.0,	at1[0].tlb_y, TXA_HCENTER | TXA_VTOP},
+		{at1[0].x2, at1[1].y1, 0.0, at1[0].x2, at1[1].y2, 0.0,
+			AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_POSTICKS, 2, 2,
+			-at1[1].lb_x, 0.0, -at1[1].tlb_x, 0.0, TXA_HLEFT | TXA_VCENTER},
+		{at1[0].x2, at1[0].y1, 0.0, at1[0].x2, at1[0].y1, at1[2].z2,
+			AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_POSTICKS, 3, 2,
+			-at1[2].lb_x, 0.0, -at1[2].tlb_x, 0.0, TXA_HLEFT | TXA_VCENTER},
+		{at1[0].x1, at1[0].y1, 0.0, at1[0].x2, at1[0].y2, 0.0,
+			AXIS_3D, 1, 3, 0.0, at1[0].lb_y, 0.0, at1[0].tlb_y, TXA_HCENTER | TXA_VCENTER},
+		{at1[0].x1, at1[1].y2, 0.0, at1[0].x2, at1[1].y2, 0.0,
+			AXIS_3D, 1, 3, 0.0, at1[0].lb_y, 0.0, at1[0].tlb_y, TXA_HCENTER | TXA_VCENTER},
+		{at1[0].x1, at1[1].y1, 0.0, at1[0].x1, at1[1].y2, 0.0,
+			AXIS_3D, 2, 2, at1[1].lb_x, 0.0, at1[1].tlb_x, 0.0, TXA_HCENTER | TXA_VCENTER},
+		{at1[0].x1, at1[1].y1, at1[2].z2, at1[0].x1, at1[1].y2, at1[2].z2,
+			AXIS_3D, 2, 2, at1[1].lb_x, 0.0, at1[1].tlb_x, 0.0, TXA_HCENTER | TXA_VCENTER},
+		{at1[0].x1, at1[0].y1, 0.0, at1[0].x1, at1[0].y1, at1[2].z2,
+			AXIS_3D, 3, 2, at1[2].lb_x, 0.0, at1[2].tlb_x, 0.0, TXA_HCENTER | TXA_VCENTER},
+		{at1[0].x1, at1[1].y2, 0.0, at1[0].x1, at1[1].y2, at1[2].z2,
+			AXIS_3D, 3, 2, at1[2].lb_x, 0.0, at1[2].tlb_x, 0.0}, TXA_HCENTER | TXA_VCENTER};
+	Axis3Ddef at3[] = {
+		{at1[0].x1, (at1[1].y1+at1[1].y2)/2.0, at1[2].z2/2.0, at1[0].x2, 
+			(at1[1].y1+at1[1].y2)/2.0, at1[2].z2/2.0,
+			AXIS_3D | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_NEGTICKS, 1, 3,
+			0.0, at1[0].lb_y, 0.0,	at1[0].tlb_y, TXA_HCENTER | TXA_VTOP},
+		{(at1[0].x1 + at1[0].x2)/2.0, at1[1].y1, at1[2].z2/2.0, 
+			(at1[0].x1 + at1[0].x2)/2.0, at1[1].y2, at1[2].z2/2.0,
+			AXIS_3D | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_POSTICKS, 2, 2,
+			-at1[1].lb_x, 0.0, -at1[1].tlb_x, 0.0, TXA_HLEFT | TXA_VCENTER},
+		{(at1[0].x1 + at1[0].x2)/2.0, (at1[1].y1+at1[1].y2)/2.0, 0.0,
+			(at1[0].x1 + at1[0].x2)/2.0, (at1[1].y1+at1[1].y2)/2.0, at1[2].z2,
+			AXIS_3D | AXIS_AUTOTICK | AXIS_AUTOSCALE | AXIS_POSTICKS, 3, 2,
+			-at1[2].lb_x, 0.0, -at1[2].tlb_x, 0.0, TXA_HLEFT | TXA_VCENTER}};
+
+	tmp_axis.min = 0.0;			tmp_axis.max = 100.0;
+	tmp_axis.Start = 0.0;		tmp_axis.Step = 20.0;
+	tmp_axis.Center.fx = tmp_axis.Center.fy = 0.0;
+	tmp_axis.Radius = 0.0;		tmp_axis.nBreaks = 0;
+	tmp_axis.breaks = 0L;		tmp_axis.owner = 0L;
+	switch(AxisTempl3D){
+	case 0:		at = at1;		nAxes = 3;		break;
+	case 1:		at = at2;		nAxes = 9;		break;
+	case 2:		at = at3;		nAxes = 3;		break;
+		}
+	if(!(Axes = (Axis**)calloc(nAxes, sizeof(Axis *))))return;
+	if(at && nAxes) for(i = 0; i < nAxes; i++) {
+		tmp_axis.loc[0].fx = at[i].x1;		tmp_axis.loc[0].fy = at[i].y1;
+		tmp_axis.loc[0].fz = at[i].z1;		tmp_axis.loc[1].fx = at[i].x2;
+		tmp_axis.loc[1].fy = at[i].y2;		tmp_axis.loc[1].fz = at[i].z2;
+		tlbdef.Align = at[i].txa;
+		if((Axes[i] = new Axis(this, data, &tmp_axis, at[i].flags))){
+			Axes[i]->type = at[i].a_type;
+			Axes[i]->SetSize(SIZE_LB_YDIST, at[i].lb_y);
+			Axes[i]->SetSize(SIZE_LB_XDIST, at[i].lb_x);
+			Axes[i]->SetSize(SIZE_TLB_YDIST, at[i].tlb_y);
+			Axes[i]->SetSize(SIZE_TLB_XDIST, at[i].tlb_x);
+			Axes[i]->Command(CMD_TICK_TYPE, &at[i].t_type, 0L);
+			Axes[i]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		}
+}
+
+void
+Plot3D::DoAutoscale()
+{
+	int i;
+	AxisDEF *ad;
+
+	if(!plots) return;
+	Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+	xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
+	xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
+	for(i = 0; i < nPlots; i++) {
+		if(plots[i]) {
+			if(plots[i]->Id >= GO_PLOT && plots[i]->Id < GO_GRAPH) {
+				if(!((Plot*)plots[i])->hidden) plots[i]->Command(CMD_AUTOSCALE, 0L, 0L);
+				}
+			else plots[i]->Command(CMD_AUTOSCALE, 0L, 0L);
+			}
+		}
+	if(xBounds.fx <= xBounds.fy && yBounds.fx <= yBounds.fy && zBounds.fx <= zBounds.fy){
+		if(Axes)for(i = 0; i < 3; i++) if(Axes[i]){
+			ad = Axes[i]->axis;
+			if(ad->flags & AXIS_AUTOSCALE) {
+				switch(i) {
+				case 0:
+					if(xBounds.fx == xBounds.fy) {
+						xBounds.fx -= 1.0;	xBounds.fy += 1.0;
+						}
+					ad->min = xBounds.fx;	ad->max = xBounds.fy;	break;
+				case 1:
+					if(yBounds.fx == yBounds.fy) {
+						yBounds.fx -= 1.0;	yBounds.fy += 1.0;
+						}
+					ad->min = yBounds.fx;	ad->max = yBounds.fy;	break;
+				case 2:
+					if(zBounds.fx == zBounds.fy) {
+						zBounds.fx -= 1.0;	zBounds.fy += 1.0;
+						}
+					ad->min = zBounds.fx;	ad->max = zBounds.fy;	break;
+					}
+				NiceAxis(ad, 4);
+				if(ad->min <= 0.0 && ((ad->flags & 0xf000) == AXIS_LOG ||
+					(ad->flags & 0xf000) == AXIS_RECI)) {
+					ad->min = base4log(ad, i);
+					}
+				Axes[i]->Command(CMD_AUTOSCALE, ad, 0L);
+				}
+			}
+		else if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			((Plot*)parent)->CheckBounds3D(xBounds.fx, yBounds.fx, zBounds.fx);
+			((Plot*)parent)->CheckBounds3D(xBounds.fy, yBounds.fy, zBounds.fy);
+			}
+		}
+}
+
+//Implement some kind of virtual trackball
+//see: J. Hultquist: A Virtual Trackball
+//Graphic Gems, A.S. Glassner ed.; Academic Press Inc.
+//ISBN 0-12-286165-5, pp. 462-463
+void
+Plot3D::CalcRotation(double dx, double dy, anyOutput *o, bool accept)
+{
+	fPOINT3D V0, V1, A;
+	double R, R2, si, dp, NewRot[6];
+	double rotM[3][3], newM[3][3];			//rotation matrices
+
+	if(!CurrGO || CurrGO->Id != GO_DRAGHANDLE || CurrGO->type < DH_18 ||
+		CurrGO->type > DH_88) return;
+	//get coordinates for last accepted rotation
+	V0.fx = GetSize(SIZE_XPOS + CurrGO->type - DH_18);
+	V0.fy = GetSize(SIZE_YPOS + CurrGO->type - DH_18);
+	V0.fz = GetSize(SIZE_ZPOS + CurrGO->type - DH_18);
+	//revert to last matrix	
+	o->SetSpace(&cu1, &cu2, defs.cUnits, RotDef, &rc, Axes[0]->GetAxis(),
+		Axes[1]->GetAxis(), Axes[2]->GetAxis());
+	o->cvec2ivec(&V0, &V1);
+	memcpy(&V0, &V1, sizeof(fPOINT3D));
+	V1.fx += o->un2fix(dx);		V1.fy += o->un2fiy(dy);
+	V0.fx -= o->rotC.fx;		V0.fy -= o->rotC.fy;		V0.fz -= o->rotC.fz;
+	V1.fx -= o->rotC.fx;		V1.fy -= o->rotC.fy;		V1.fz -= o->rotC.fz;
+	R = sqrt(R2 = V0.fx * V0.fx + V0.fy * V0.fy + V0.fz * V0.fz);
+	R2 -= (V1.fx * V1.fx + V1.fy * V1.fy);
+	if (R2 <= 1.0) return;
+	V1.fz = V1.fz > 0.0 ? sqrt(R2) : -sqrt(R2);
+	V0.fx /= R;			V0.fy /= R;			V0.fz /= R;
+	V1.fx /= R;			V1.fy /= R;			V1.fz /= R;
+	A.fx = (V1.fy * V0.fz) - (V1.fz * V0.fy);
+	A.fy = (V1.fz * V0.fx) - (V1.fx * V0.fz);
+	A.fz = (V1.fx * V0.fy) - (V1.fy * V0.fx);
+
+	si = sqrt(A.fx * A.fx + A.fy * A.fy + A.fz * A.fz);
+	if(si > 0.001) {
+		NewRot[0] = A.fx;	NewRot[1] = A.fy;	NewRot[2] = A.fz;
+		NewRot[3] = si;		NewRot[4] = sqrt(1.0-si*si);	NewRot[5] = 1.0-NewRot[4];
+		//normalize vector part of NewRot
+		dp = sqrt(NewRot[0]*NewRot[0] + NewRot[1]*NewRot[1] + NewRot[2]*NewRot[2]);
+		NewRot[0] /= dp;	NewRot[1] /= dp;	NewRot[2] /= dp;
+		//set up rotation matrix from quaternion
+		//see: Graphic Gems, A.S. Glassner ed.; Academic Press Inc.
+		//M.E. Pique: Rotation Tools
+		// ISBN 0-12-286165-5, p.466
+		rotM[0][0] = NewRot[5]*NewRot[0]*NewRot[0] + NewRot[4];
+		rotM[0][1] = NewRot[5]*NewRot[0]*NewRot[1] + NewRot[3]*NewRot[2];
+		rotM[0][2] = NewRot[5]*NewRot[0]*NewRot[2] - NewRot[3]*NewRot[1];
+		rotM[1][0] = NewRot[5]*NewRot[0]*NewRot[1] - NewRot[3]*NewRot[2];
+		rotM[1][1] = NewRot[5]*NewRot[1]*NewRot[1] + NewRot[4];
+		rotM[1][2] = NewRot[5]*NewRot[1]*NewRot[2] + NewRot[3]*NewRot[0];
+		rotM[2][0] = NewRot[5]*NewRot[0]*NewRot[2] + NewRot[3]*NewRot[1];
+		rotM[2][1] = NewRot[5]*NewRot[1]*NewRot[2] - NewRot[3]*NewRot[0];
+		rotM[2][2] = NewRot[5]*NewRot[2]*NewRot[2] + NewRot[4];
+		//rotate rotation matrix
+		if(MatMul(o->rotM, rotM, newM)) memcpy(&o->rotM, &newM, sizeof(newM));
+		}
+	if(accept) {
+		//create new quaternion in RotDef from rotation matrix of output class
+		Undo.RotDef(this, &RotDef, 0L);
+		RotDef[4] = (o->rotM[0][0] + o->rotM[1][1] + o->rotM[2][2] -1)/2.0;
+		RotDef[3] = sqrt(1.0-RotDef[4]*RotDef[4]);
+		RotDef[0] = (o->rotM[1][2] - o->rotM[2][1])/(2.0 * RotDef[3]);
+		RotDef[1] = (o->rotM[2][0] - o->rotM[0][2])/(2.0 * RotDef[3]);
+		RotDef[2] = (o->rotM[0][1] - o->rotM[1][0])/(2.0 * RotDef[3]);
+		RotDef[5] = 1.0-RotDef[4];
+		}
+}
+
+bool
+Plot3D::AcceptObj(GraphObj *go)
+{
+	if(!dispObs && !(dispObs = (obj_desc**)
+		calloc(nmaxObs = 1024, sizeof(obj_desc*))))return false;
+	else if((nObs+1) >= nmaxObs) dispObs = (obj_desc**)
+		realloc(dispObs, (nmaxObs = nmaxObs +1024) * sizeof(obj_desc*));
+	if(dispObs[++nObs] = (obj_desc*)calloc(1, sizeof(obj_desc))){
+		dispObs[nObs]->Zmin = go->GetSize(SIZE_MIN_Z);
+		dispObs[nObs]->Zmax = go->GetSize(SIZE_MAX_Z);
+		dispObs[nObs]->go = go;
+		}
+	return true;
+}
+
+//Execute a heap sort before drawing the objects
+//W.H. pres, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1988/1989)
+//Numerical Recipes in C, Cambridge University Press, ISBN 0-521-35465-X
+// p. 245
+int
+Plot3D::cmp_obj_desc(obj_desc *obj1, obj_desc *obj2)
+{
+	double *v1, *v2, tmp1, tmp2;
+
+	if(obj1->go->Id == GO_PLANE && obj2->go->Id == GO_PLANE) {
+		if(obj1->Zmax < obj2->Zmin) return -1;
+		if(obj2->Zmax < obj1->Zmin) return 1;
+		if(obj1->Zmax == obj2->Zmax) {
+			if(obj1->Zmin < obj2->Zmin) return -1;
+			else if(obj1->Zmin > obj2->Zmin) return 1;
+			else return 0;
+			}
+		if((v1=((plane*)(obj1->go))->GetVec()) && (v2=((plane*)(obj2->go))->GetVec()) ) {
+			if(v1[0] == v2[0] && v1[1] == v2[1] && v1[2] == v2[2]){
+				if(obj1->Zmax < obj2->Zmax) return -1;
+				else if(obj2->Zmax < obj1->Zmax) return 1;
+				else return 0;
+				}
+			}
+		tmp1 = obj1->Zmax + obj1->Zmin;		tmp2 = obj2->Zmax + obj2->Zmin;
+		if(tmp1 < tmp2) return -1;
+		else if(tmp1 > tmp2) return 1;
+		else return 0;
+		}
+	else {
+		if(obj1->Zmin < obj2->Zmin) return -1;
+		if(obj1->Zmin > obj2->Zmin) return 1;
+		}
+	return 0;
+}
+
+void
+Plot3D::SortObj()
+{
+	int l, j, ir, i;
+	obj_desc *rra;
+
+	if(nObs < 2) return;
+	l=(nObs >> 1)+1;			ir = nObs;
+	for( ; ; ){
+		if(l > 1) rra = dispObs[--l];
+		else {
+			rra = dispObs[ir];
+			dispObs[ir] = dispObs[1];
+			if(--ir == 1) {
+				dispObs[1] = rra;
+				return;
+				}
+			}
+		i = l;					j = l << 1;
+		while(j <= ir) {
+			if(j < ir && cmp_obj_desc(dispObs[j], dispObs[j+1]) < 0) ++j;
+			if(cmp_obj_desc(rra, dispObs[j])) {
+				dispObs[i] = dispObs[j];
+				j += (i = j);
+				}
+			else j = ir + 1;
+			}
+		dispObs[i] = rra;
+		}
+}
+
+bool
+Plot3D::Rotate(double dx, double dy, double dz, anyOutput *o, bool accept)
+{
+	int i;
+	double si, NewRot[6];
+	double rotM[3][3], newM[3][3];			//rotation matrices
+	bool bRet = true;
+
+	o->SetSpace(&cu1, &cu2, defs.cUnits, RotDef, &rc, Axes[0]->GetAxis(),
+		Axes[1]->GetAxis(), Axes[2]->GetAxis());
+	for(i = 0; i < 3; i++) {
+		switch (i){
+		case 0:
+			if(dx > 0.0) {
+				NewRot[0] = -o->rotM[1][0];			NewRot[1] = -o->rotM[1][1];
+				NewRot[2] = -o->rotM[1][2];
+				NewRot[3] = si = dx;
+				}
+			else {
+				NewRot[0] = o->rotM[1][0];			NewRot[1] = o->rotM[1][1];
+				NewRot[2] = o->rotM[1][2];
+				NewRot[3] = si = -dx;
+				}
+			break;
+		case 1:
+			if(dy > 0.0) {
+				NewRot[0] = -o->rotM[0][0];			NewRot[1] = -o->rotM[0][1];
+				NewRot[2] = -o->rotM[0][2];
+				NewRot[3] = si = dy;
+				}
+			else {
+				NewRot[0] = o->rotM[0][0];			NewRot[1] = o->rotM[0][1];
+				NewRot[2] = o->rotM[0][2];
+				NewRot[3] = si = -dy;
+				}
+			break;
+		case 2:
+			if(dz > 0.0) {
+				NewRot[0] = -o->rotM[2][0];			NewRot[1] = -o->rotM[2][1];
+				NewRot[2] = -o->rotM[2][2];
+				NewRot[3] = si = dz;
+				}
+			else {
+				NewRot[0] = o->rotM[2][0];			NewRot[1] = o->rotM[2][1];
+				NewRot[2] = o->rotM[2][2];
+				NewRot[3] = si = -dz;
+				}
+			break;
+			}
+		if(si > 0.0) {
+			NewRot[4] = sqrt(1.0-si*si);	NewRot[5] = 1.0-NewRot[4];
+			//set up rotation matrix from quaternion
+			//see: Graphic Gems, A.S. Glassner ed.; Academic Press Inc.
+			//M.E. Pique: Rotation Tools
+			// ISBN 0-12-286165-5, p.466
+			rotM[0][0] = NewRot[5]*NewRot[0]*NewRot[0] + NewRot[4];
+			rotM[0][1] = NewRot[5]*NewRot[0]*NewRot[1] + NewRot[3]*NewRot[2];
+			rotM[0][2] = NewRot[5]*NewRot[0]*NewRot[2] - NewRot[3]*NewRot[1];
+			rotM[1][0] = NewRot[5]*NewRot[0]*NewRot[1] - NewRot[3]*NewRot[2];
+			rotM[1][1] = NewRot[5]*NewRot[1]*NewRot[1] + NewRot[4];
+			rotM[1][2] = NewRot[5]*NewRot[1]*NewRot[2] + NewRot[3]*NewRot[0];
+			rotM[2][0] = NewRot[5]*NewRot[0]*NewRot[2] + NewRot[3]*NewRot[1];
+			rotM[2][1] = NewRot[5]*NewRot[1]*NewRot[2] - NewRot[3]*NewRot[0];
+			rotM[2][2] = NewRot[5]*NewRot[2]*NewRot[2] + NewRot[4];
+			if(MatMul(o->rotM, rotM, newM))	memcpy(&o->rotM, &newM, sizeof(newM));
+			else accept = bRet = false;
+			}
+		}
+	if(accept && bRet) {
+		//create new quaternion in RotDef from rotation matrix of output class
+		Undo.RotDef(this, &RotDef, 0L);
+		RotDef[4] = (o->rotM[0][0] + o->rotM[1][1] + o->rotM[2][2] -1)/2.0;
+		RotDef[3] = sqrt(1.0-RotDef[4]*RotDef[4]);
+		RotDef[0] = (o->rotM[1][2] - o->rotM[2][1])/(2.0 * RotDef[3]);
+		RotDef[1] = (o->rotM[2][0] - o->rotM[0][2])/(2.0 * RotDef[3]);
+		RotDef[2] = (o->rotM[0][1] - o->rotM[1][0])/(2.0 * RotDef[3]);
+		RotDef[5] = 1.0-RotDef[4];
+		if(parent)return parent->Command(CMD_REDRAW, 0L, o);
+		}
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// use Plot3D to create a 2.5 dimensional chart
+Chart25D::Chart25D(GraphObj *par, DataObj *d, DWORD flags)
+	:Plot3D(par, d, flags)
+{
+	dspm.fx = dspm.fy = dspm.fz = 1.0;
+}
+
+Chart25D::~Chart25D()
+{
+	if(name) free(name);		name=0L;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// use Plot3D to create a 2.5 dimensional ribbon chart
+Ribbon25D::Ribbon25D(GraphObj *par, DataObj *d, DWORD flags)
+	:Plot3D(par, d, flags)
+{
+	dspm.fx = dspm.fy = dspm.fz = 1.0;
+}
+
+Ribbon25D::~Ribbon25D()
+{
+	if(name) free(name);		name=0L;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// use Plot3D to create a 3 dimensional bubble plot
+BubblePlot3D::BubblePlot3D(GraphObj *par, DataObj *d)
+	:Plot3D(par, d, 0x0L)
+{
+}
+
+BubblePlot3D::~BubblePlot3D()
+{
+	if(name) free(name);		name=0L;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 3D function plotter
+Func3D::Func3D(GraphObj *par, DataObj *d)
+	:Plot3D(par, d, 0x0L)
+{
+	FileIO(INIT_VARS);
+	if(cmdxy = (char*)malloc(40*sizeof(char)))
+		rlp_strcpy(cmdxy, 40, (char*)"r=sqrt(x*x+z*z)\ny=1-exp(-8/(r+1))");
+	Id = GO_FUNC3D;
+}
+
+Func3D::Func3D(int src):Plot3D(0)
+{
+	int i;
+
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		//now set parent in all children
+		if(Axes) for(i = 0; i < nAxes; i++)
+			if(Axes[i]) Axes[i]->parent = this;
+		if(plots) for(i = 0; i < nPlots; i++)
+			if(plots[i]) plots[i]->parent = this;
+		}
+}
+
+Func3D::~Func3D()
+{
+	if(param) free(param);		param = 0L;
+	if(cmdxy) free(cmdxy);		cmdxy = 0L;
+	if(gda) delete(gda);		gda = 0L;
+	if(name) free(name);		name=0L;
+}
+
+bool
+Func3D::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch(cmd) {
+	case CMD_SET_DATAOBJ:
+		Plot3D::Command(cmd, tmpl, o);
+		if(gob && gda) gob->Command(cmd, gda, o);
+		Id = GO_FUNC3D;
+		return true;
+	case CMD_UPDATE:
+		return Update();
+		break;
+		}
+	return Plot3D::Command(cmd, tmpl, o);
+}
+
+bool
+Func3D::Update()
+{
+	if(cmdxy) {
+		dirty = true;
+		if(xstep == 0.0) xstep = 1.0;	if(zstep == 0.0) zstep = 1.0;
+		if(!gda) gda = new DataObj();
+		if(gda && do_func3D(gda, x1, x2, xstep, z1, z2, zstep, cmdxy, param)) {
+			if(gob = new Grid3D(this, gda, type, x1, xstep, z1, zstep)) {
+				gob->Command(CMD_SET_LINE, &Line, 0L);
+				gob->Command(CMD_SYM_FILL, &Fill, 0L);
+				if(!plots && (plots = (GraphObj**)calloc(2, sizeof(GraphObj*)))) {
+					nPlots = 1;				plots[0] = (Plot *)gob;
+					if(parent->Id == GO_GRAPH) CreateAxes();
+					return dirty = parent->Command(CMD_REDRAW, 0L, 0L);
+					}
+				else if(plots && nPlots && plots[0]->Id == GO_GRID3D) {
+					Undo.DeleteGO(&plots[0], UNDO_CONTINUE, 0L);
+					Undo.SetGO(this, &plots[0], gob, UNDO_CONTINUE);
+					return true;
+					}
+				else {
+					DeleteGO(gob);		gob=0L;
+					}
+				}
+			}
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// fit 3D function to data
+static char *lastFunc3D = 0L, *lastParam3D=0L;
+FitFunc3D::FitFunc3D(GraphObj *par, DataObj *d)
+	:Plot3D(par, d, 0x0L)
+{
+	FileIO(INIT_VARS);
+	if(lastFunc3D && lastFunc3D[0] && lastParam3D && lastParam3D[0]) {
+		cmdxy = (char*)memdup(lastFunc3D, (int)strlen(lastFunc3D)+1, 0);
+		param = (char*)memdup(lastParam3D, (int)strlen(lastParam3D)+1, 0);
+		}
+	if(!cmdxy || !param) {
+		cmdxy = (char*)malloc(20*sizeof(char));		param = (char*)malloc(20*sizeof(char));
+		if(cmdxy) rlp_strcpy(cmdxy, 20, (char*)"a+b*x^c");
+		if(param) rlp_strcpy(param, 20, (char*)"a=1; b=1; c=0.1;");
+		}
+	Id = GO_FITFUNC3D;
+}
+
+FitFunc3D::FitFunc3D(int src):Plot3D(0)
+{
+	int i;
+
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		//now set parent in all children
+		if(Axes) for(i = 0; i < nAxes; i++)
+			if(Axes[i]) Axes[i]->parent = this;
+		if(plots) for(i = 0; i < nPlots; i++)
+			if(plots[i]) plots[i]->parent = this;
+		}
+}
+
+FitFunc3D::~FitFunc3D()
+{
+	if(param) free(param);		param = 0L;
+	if(cmdxy) free(cmdxy);		cmdxy = 0L;
+	if(gda) delete(gda);		gda = 0L;
+	if(name) free(name);		name=0L;
+}
+
+bool
+FitFunc3D::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+
+	switch(cmd) {
+	case CMD_ENDDIALOG:
+		if(!cmdxy || !param) return false;
+		if(i = (int)strlen(cmdxy)) {
+			if(lastFunc3D = (char*)realloc(lastFunc3D, i+2))
+				rlp_strcpy(lastFunc3D, i+1, cmdxy);
+			}
+		if(i = (int)strlen(param)) {
+			if(lastParam3D = (char*)realloc(lastParam3D, i+2))
+				rlp_strcpy(lastParam3D, i+1, param);
+			}
+		return true;
+	case CMD_SET_DATAOBJ:
+		Plot3D::Command(cmd, tmpl, o);
+		if(gob && gda) gob->Command(cmd, gda, o);
+		Id = GO_FITFUNC3D;
+		return true;
+	case CMD_UPDATE:
+		return Update();
+		break;
+		}
+	return Plot3D::Command(cmd, tmpl, o);
+}
+
+bool
+FitFunc3D::Update()
+{
+	if(cmdxy) {
+		dirty = true;
+		if(xstep == 0.0) xstep = 1.0;	if(zstep == 0.0) zstep = 1.0;
+		if(!gda) gda = new DataObj();
+		if(gda && do_func3D(gda, x1, x2, xstep, z1, z2, zstep, cmdxy, param)) {
+			if(gob = new Grid3D(this, gda, type, x1, xstep, z1, zstep)) {
+				gob->Command(CMD_SET_LINE, &Line, 0L);
+				gob->Command(CMD_SYM_FILL, &Fill, 0L);
+				if(!plots && (plots = (GraphObj**)calloc(3, sizeof(GraphObj*)))) {
+					nPlots = 1;				plots[0] = (Plot *)gob;
+					if(parent->Id == GO_GRAPH) CreateAxes();
+					return dirty = parent->Command(CMD_REDRAW, 0L, 0L);
+					}
+				else if(plots && nPlots && plots[0]->Id == GO_GRID3D) {
+					Undo.DeleteGO(&plots[0], UNDO_CONTINUE, 0L);
+					Undo.SetGO(this, &plots[0], gob, UNDO_CONTINUE);
+					return true;
+					}
+				else {
+					DeleteGO(gob);		gob=0L;
+					}
+				}
+			}
+		}
+	return false;
+}
diff --git a/PropertyDlg.cpp b/PropertyDlg.cpp
index 4a6230d..979d340 100755
--- a/PropertyDlg.cpp
+++ b/PropertyDlg.cpp
@@ -1,9867 +1,10124 @@
-//PropertyDlg.cpp, Copyright (c) 2001-2007 R.Lackner
-//Property dialogs for graphic objects
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-#include "rlplot.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <time.h>
-#include "TheDialog.h"
-
-extern tag_Units Units[];
-extern char TmpTxt[];
-extern TextDEF DlgText;
-extern Default defs;
-extern int dlgtxtheight;
-extern Axis **CurrAxes;
-extern UndoObj Undo;
-extern int AxisTempl3D;
-
-int ODtickstyle;
-
-//prototypes: WinSpec.cpp
-void *CreateDlgWnd(char *title, int x, int y, int width, int height, tag_DlgObj *d, DWORD flags);
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Symbol properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *SymDlg_DlgTmpl = 
-	"1,+,,DEFAULT,PUSHBUTTON,-1,145,10,60,12\n"
-	".,.,,,PUSHBUTTON,2,145,25,60,12\n"
-	".,.,,,PUSHBUTTON,-2,145,40,60,12\n"
-	".,50,5,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
-	".,+,100,ISPARENT | CHECKED,SHEET,3,5,10,130,113\n"
-	".,.,200,TOUCHEXIT | ISPARENT,SHEET,4,5,10,130,113\n"
-	".,,300,ISPARENT,SHEET,5,5,10,130,113\n"
-	"50,,,TOUCHEXIT,SYMBUTT,0,155,75,40,40\n"
-	"100,+,,,RTEXT,6,5,25,45,8\n"
-	".,.,,TOUCHEXIT,INCDECVAL1,9,55,25,32,10\n"
-	".,.,,,LTEXT,-3,89,25,20,8\n"
-	".,.,,,RTEXT,10,5,37,45,8\n"
-	".,.,,TOUCHEXIT,INCDECVAL1,11,55,37,32,10\n"
-	".,.,,,LTEXT,-3,89,37,20,8\n"
-	".,.,,,RTEXT,12,5,49,45,8\n"
-	".,.,,TOUCHEXIT | OWNDIALOG, COLBUTT,13,55,49,25,10\n"
-	".,.,,,RTEXT,14,5,61,45,8\n"
-	".,401,,TOUCHEXIT | OWNDIALOG,COLBUTT,15,55,61,25,10\n"
-	"200,204,201,CHECKED | ISPARENT, GROUPBOX,16,12,28,50,39\n"
-	".,+,,TOUCHEXIT, RADIO1,17,15,33,45,8\n"
-	".,.,,TOUCHEXIT, RADIO1,18,15,43,45,8\n"
-	".,,,TOUCHEXIT, RADIO1,19,15,53,43,8\n"
-	".,250,205,CHECKED | ISPARENT, GROUPBOX,20,72,28,57,39\n"
-	"205,+,,TOUCHEXIT, CHECKBOX,21,75,33,25,8\n"
-	".,.,,TOUCHEXIT, CHECKBOX,22,75,43,25,8\n"
-	".,,,TOUCHEXIT, CHECKBOX,23,75,53,25,8\n"
-	"250,+,,TOUCHEXIT | CHECKED, RADIO1,24,10,75,45,8\n"
-	".,.,,,EDTEXT,25,60,75,68,10\n"
-	".,.,,TOUCHEXIT,RADIO1,26,10,92,60,8\n"
-	".,,,,EDTEXT,27,20,102,100,10\n"
-	"300,+,,,RTEXT,-12,5,30,45,8\n"
-	".,.,,,EDVAL1,7,55,30,45,10\n"
-	".,.,,,RTEXT,-13,5,50,45,8\n"
-	".,,,LASTOBJ,EDVAL1,8,55,50,45,10";
-
-bool
-Symbol::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 47, 10, "Size & Color"};
-	TabSHEET tab2 = {47, 70, 10, "Text"};
-	TabSHEET tab3 = {70, 92, 10, "Edit"};
-	Symbol *PrevSym = 0L;
-	char text1[40], text2[100];
-	void *dyndata[] = {(void*)"Apply to Symbol", (void*)"Apply to PLOT", (void*)&tab1,
-		(void*)&tab2, (void*)&tab3, (void*)"size", (void*)&fPos.fx, (void*)&fPos.fy,
-		(void*)&size, (void*)"line width", (void*)&SymLine.width, (void*)"line color",
-		(void *)&SymLine.color, (void*)"fill color", (void *)&SymFill.color, (void*)" font ",
-		(void*)"Helvetica", (void*)"Times", (void*)"Courier", (void*)" style ", (void*)"bold",
-		(void*)"italic", (void*)"underlined", (void*)"fixed text:", (void*)text1,
-		(void*)"from spreadsheet range:", (void*)text2};
-	DlgInfo *SymDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, k, ix, iy, tmpType, res, width, height, n_syms;
-	DWORD tmpCol, undo_flags = 0L;
-	double tmpVal, o_size, n_size, o_lwidth, n_lwidth;
-	TextDEF textdef;
-	anyOutput *cdisp = Undo.cdisp;
-	lfPOINT o_pos, n_pos;
-	static const int syms[] = {SYM_CIRCLE, SYM_CIRCLEF, SYM_CIRCLEC, SYM_RECT, SYM_RECTF, SYM_RECTC, 
-		SYM_TRIAU, SYM_TRIAUF, SYM_TRIAUC, SYM_TRIAUL, SYM_TRIAUR, SYM_TRIAD, SYM_TRIADF, SYM_TRIADC,
-		SYM_TRIADL, SYM_TRIADR, SYM_DIAMOND, SYM_DIAMONDF, SYM_DIAMONDC, SYM_5GON, SYM_5GONF, SYM_5GONC,
-		SYM_4STAR, SYM_4STARF, SYM_5STAR, SYM_5STARF, SYM_6STAR, SYM_6STARF, SYM_1QUAD, SYM_2QUAD,
-		SYM_3QUAD, SYM_PLUS, SYM_CROSS, SYM_STAR, SYM_HLINE, SYM_VLINE, SYM_TEXT};
-
-	if(!(SymDlg = CompileDialog(SymDlg_DlgTmpl, dyndata)))return false;
-	if(!Command(CMD_GETTEXT, (void*)text1, 0L)) rlp_strcpy(text1, 40, "text");
-#ifdef USE_WIN_SECURE
-	if(parent && data && data->GetSize(&width, &height)) sprintf_s(text2, 100, "b1:b%d", height);
-#else
-	if(parent && data && data->GetSize(&width, &height)) sprintf(text2, "b1:b%d", height);
-#endif
-	else rlp_strcpy(text2, 100, "(not available)");
-	n_syms = sizeof(syms)/sizeof(int);
-	for(k = 1; !(SymDlg[k-1].flags & LASTOBJ); k++);
-	if(!parent) n_syms--;
-	if(!(SymDlg = (DlgInfo *)realloc(SymDlg, (k+n_syms)*sizeof(DlgInfo)))) return false;
-	SymDlg[k-1].flags &= (~LASTOBJ);
-	for(i = 1, iy = 66; i <= n_syms; i++, ix += 10) {
-		if((i%11) == 1) {
-			iy += 10;		ix = 15;
-			}
-		SymDlg[k].id = 400 + i;					SymDlg[k].next = 400 + i + 1;
-		SymDlg[k].first = 0;					SymDlg[k].flags = TOUCHEXIT;
-		SymDlg[k].type = SYMRADIO;				SymDlg[k].ptype = (void*)&syms[i-1];
-		SymDlg[k].x = ix;						SymDlg[k].y = iy;
-		if(type == syms[i-1]) SymDlg[k].flags |= CHECKED;
-		SymDlg[k].w = SymDlg[k].h = 10;			k++;
-		}
-	SymDlg[k-1].flags |= LASTOBJ;				if(parent) SymDlg[k-1].w = 30;
-	if(parent) {
-		SymDlg[0].ptype = dyndata[0];
-		}
-	else {
-		SymDlg[5].flags |= HIDDEN;				SymDlg[6].flags |= HIDDEN;
-		SymDlg[1].flags |= HIDDEN;				SymDlg[2].y = 25;
-		SymDlg[0].w = SymDlg[2].w = 45;			SymDlg[7].x = 145;
-		}
-	if((PrevSym = new Symbol(0L, data, 0.0f, 0.0f, type))){
-		PrevSym->SetColor(COL_SYM_LINE, SymLine.color);
-		PrevSym->SetColor(COL_SYM_FILL, SymFill.color);
-		PrevSym->SetSize(SIZE_SYMBOL, size);
-		PrevSym->SetSize(SIZE_SYM_LINE, SymLine.width);
-		PrevSym->Command(CMD_SETTEXT, (void*)text1, 0L);
-		PrevSym->Command(CMD_GETTEXTDEF, &textdef, 0L);
-		PrevSym->Command(CMD_SET_DATAOBJ, (void*)data, 0L);
-		if(Command(CMD_GETTEXTDEF, &textdef, 0L))
-			PrevSym->Command(CMD_SETTEXTDEF, &textdef, 0L);
-		PrevSym->idx = idx;
-		SymDlg[7].ptype = (void*)&PrevSym;
-		}
-	if(PrevSym && (Dlg = new DlgRoot(SymDlg, data))) {
-		Dlg->TextFont(201, FONT_HELVETICA);
-		Dlg->TextFont(202, FONT_TIMES);
-		Dlg->TextFont(203, FONT_COURIER);
-		Dlg->TextStyle(205, TXS_BOLD);
-		Dlg->TextStyle(206, TXS_ITALIC);
-		Dlg->TextStyle(207, TXS_UNDERLINE);
-		switch(textdef.Font) {
-		case FONT_TIMES:	Dlg->SetCheck(202, 0L, true);	break;
-		case FONT_COURIER:	Dlg->SetCheck(203, 0L, true);	break;
-		default:			Dlg->SetCheck(201, 0L, true);	break;
-			}
-		if(textdef.Style & TXS_BOLD) Dlg->SetCheck(205, 0L, true);
-		if(textdef.Style & TXS_ITALIC) Dlg->SetCheck(206, 0L, true);
-		if(textdef.Style & TXS_UNDERLINE) Dlg->SetCheck(207, 0L, true);
-		}
-	else return false;
-	if(parent && parent->name) {
-		width = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Symbol of ");
-		rlp_strcpy(TmpTxt+width, TMP_TXT_SIZE-width, parent->name);
-		width =10;
-		}
-	else {
-		rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Symbol properties");
-		}
-	if(!(hDlg = CreateDlgWnd(TmpTxt, 50, 50, parent ? 430 : 400, 292, Dlg, 0x0L)))return false;
-	tmpCol = 0x00c0c0c0L;	o_size = size;	o_lwidth = SymLine.width;
-	Dlg->GetValue(101, &o_size);		Dlg->GetValue(104, &o_lwidth);
-	n_size = o_size;					n_lwidth = o_lwidth;
-	o_pos.fx = fPos.fx;					o_pos.fy = fPos.fy;
-	if(Dlg->GetValue(301, &tmpVal)) o_pos.fx = tmpVal;	n_pos.fx = o_pos.fx;
-	if(Dlg->GetValue(303, &tmpVal)) o_pos.fy = tmpVal;	n_pos.fy = o_pos.fy;
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res){
-		case 1:		case 2:
-			Undo.SetDisp(cdisp);
-			if(parent && PrevSym->type == SYM_TEXT && Dlg->GetCheck(250)){
-				Dlg->GetText(251, text1, 40);
-				if(PrevSym->Command(CMD_GETTEXT, (void *)text2, 0L) && strcmp(text1, text2)) {
-					PrevSym->Command(CMD_SETTEXT, (void *)text1, 0L);
-					Dlg->DoPlot(NULL);
-					res = -1;
-					}
-				}
-			else if(parent && PrevSym->type == SYM_TEXT && Dlg->GetCheck(252)) {
-				if(Dlg->GetCheck(252) && Dlg->GetText(253, text2, 100))	
-					PrevSym->Command(CMD_RANGETEXT, &text2, 0L);
-				}
-			Dlg->GetValue(101, &n_size);		Dlg->GetValue(104, &n_lwidth);
-			break;
-		case 6:											//the text sheets
-			if(parent)Dlg->SetCheck(400+n_syms, 0L, true);
-			if(PrevSym->type != SYM_TEXT) {
-				PrevSym->type = SYM_TEXT;		Dlg->DoPlot(0L);
-				}
-			res = -1;
-			break;
-		case 201:	case 202:	case 203:				//fonts and styles
-		case 205:	case 206:	case 207:
-			if(PrevSym->Command(CMD_GETTEXTDEF, &textdef, 0L)) {
-				if(Dlg->GetCheck(202)) textdef.Font = FONT_TIMES;
-				else if(Dlg->GetCheck(203)) textdef.Font = FONT_COURIER;
-				else textdef.Font = FONT_HELVETICA;
-				textdef.Style = TXS_NORMAL;
-				if(Dlg->GetCheck(205)) textdef.Style |= TXS_BOLD;
-				if(Dlg->GetCheck(206)) textdef.Style |= TXS_ITALIC;
-				if(Dlg->GetCheck(207)) textdef.Style |= TXS_UNDERLINE;
-				PrevSym->Command(CMD_SETTEXTDEF, &textdef, 0L);
-				Dlg->DoPlot(0L);
-				}
-			res = -1;
-			break;
-		default:										//symbol selection ?
-			if(res > 400 && res <= (400+n_syms)) tmpType = syms[res-401];
-			else break;
-			PrevSym->type = tmpType;
-		case 107:										//line color button
-			if(res == 107 && Dlg->GetColor(107, &tmpCol))
-				PrevSym->SetColor(COL_SYM_LINE, tmpCol);
-		case 109:										//fill color button
-			if(res == 109 && Dlg->GetColor(109, &tmpCol)) 
-				PrevSym->SetColor(COL_SYM_FILL, tmpCol);
-		case 101:										//symbol size changed
-		case 104:										//line width changed
-		case 50:										//preview button
-			if(Dlg->GetValue(101, &tmpVal))	PrevSym->SetSize(SIZE_SYMBOL, tmpVal);
-			if(Dlg->GetValue(104, &tmpVal))	PrevSym->SetSize(SIZE_SYM_LINE, tmpVal);
-			if(PrevSym->type == SYM_TEXT) {
-				if(Dlg->GetCheck(250) && Dlg->GetText(251,text1,40))PrevSym->Command(CMD_SETTEXT, text1, 0L);
-				else if(Dlg->GetCheck(252) && Dlg->GetText(253, text2,100))	
-					PrevSym->Command(CMD_RANGETEXT, text2, 0L);
-				}
-			Dlg->DoPlot(0L);
-			res = -1;
-			break;
-		case 252:										//use spreadsheet text
-			if(!data) Dlg->SetCheck(250, 0L, true);
-		case 250:										//use fixed text
-			if(Dlg->GetCheck(250) && Dlg->GetText(251,text1,40))PrevSym->Command(CMD_SETTEXT, text1, 0L);
-			else if(Dlg->GetCheck(252) && Dlg->GetText(253, text2, 100))	
-				PrevSym->Command(CMD_RANGETEXT, text2, 0L);
-			Dlg->DoPlot(NULL);
-			res = -1;
-			break;
-			}
-		}while (res < 0);
-	switch (res) {
-	case 1:								//accept values for symbol
-		undo_flags = CheckNewFloat(&size, o_size, n_size, parent, undo_flags);
-		undo_flags = CheckNewFloat(&SymLine.width, o_lwidth, n_lwidth, parent, undo_flags);
-		if(Dlg->GetValue(301, &tmpVal)) n_pos.fx = tmpVal;
-		if(Dlg->GetValue(303, &tmpVal)) n_pos.fy = tmpVal;
-		undo_flags = CheckNewLFPoint(&fPos, &o_pos, &n_pos, parent, undo_flags);
-		if(Dlg->GetColor(107, &tmpCol)) undo_flags = 
-			CheckNewDword(&SymLine.color, SymLine.color, tmpCol, parent, undo_flags);
-		if(Dlg->GetColor(109, &tmpCol)) undo_flags = 
-			CheckNewDword(&SymFill.color, SymFill.color, tmpCol, parent, undo_flags);
-		undo_flags = CheckNewInt(&type, type, PrevSym->type, parent, undo_flags);
-		if(type == SYM_TEXT && PrevSym->Command(CMD_GETTEXTDEF, &textdef, 0L)){
-			if(SymTxt && cmpTextDEF(SymTxt, &textdef)){
-				Undo.TextDef(parent, SymTxt, undo_flags);	undo_flags |= UNDO_CONTINUE;
-				}
-			if(PrevSym->Command(CMD_GETTEXT, text1, 0L)) Command(CMD_SETTEXT, text1, 0L);
-			Command(CMD_SETTEXTDEF, &textdef, 0L);
-			}
-		break;
-	case 2:								//accept values for all symbols of plot
-		parent->Command(CMD_SAVE_SYMBOLS, 0L, 0L);
-		undo_flags |= UNDO_CONTINUE;
-		parent->SetSize(SIZE_SYMBOL, n_size);
-		parent->SetSize(SIZE_SYM_LINE, n_lwidth);
-		if(Dlg->GetColor(107, &tmpCol))	parent->SetColor(COL_SYM_LINE, tmpCol);
-		if(Dlg->GetColor(109, &tmpCol))	parent->SetColor(COL_SYM_FILL, tmpCol);
-		parent->Command(CMD_SYM_TYPE, (void*)(& PrevSym->type), 0L);
-		if(PrevSym->type == SYM_TEXT) {
-			if(Dlg->GetCheck(250) && PrevSym->Command(CMD_GETTEXT, text1, 0L))
-				parent->Command(CMD_SYMTEXT_UNDO, text1, 0L);
-			else if(Dlg->GetCheck(252) && Dlg->GetText(253, text2, 100))	
-				parent->Command(CMD_SYM_RANGETEXT, text2, 0L);
-			if(PrevSym->Command(CMD_GETTEXTDEF, &textdef, 0L))
-				parent->Command(CMD_SYMTEXTDEF, &textdef, 0L);
-			}
-		break;
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;			free(SymDlg);
-	delete PrevSym;			return undo_flags != 0;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Bubble properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *BubDlg_DlgTmpl = 
-	"1,2,,DEFAULT,PUSHBUTTON,1,130,10,60,12\n"
-	"2,3,,,PUSHBUTTON,2,130,25,60,12\n"
-	"3,4,,,PUSHBUTTON,-2,130,40,60,12\n"
-	"4,,5,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
-	"5,6,100,ISPARENT | CHECKED,SHEET,3,5,10,120,100\n"
-	"6,7,200,ISPARENT,SHEET,4,5,10,120,100\n"
-	"7,,300,ISPARENT,SHEET,5,5,10,120,100\n"
-	"100,109,,NOSELECT,ODBUTTON,6,18,57,90,50\n"
-	"109,110,,,SYMRADIO,7,30,30,20,20\n"
-	"110,111,,,SYMRADIO,8,50,30,20,20\n"
-	"111,112,,,SYMRADIO,9,70,30,20,20\n"
-	"112,,,,SYMRADIO,10,90,30,20,20\n"
-	"200,201,,,LTEXT,11,10,30,110,8\n"
-	"201,202,210, ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"202,203,,,LTEXT,12,10,64,110,8\n"
-	"203,, 220,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"210,211,,,RADIO1,-3,40,38,45,8\n"
-	"211,212,,,RADIO1,13,40,46,45,8\n"
-	"212,,,,RADIO1,14,40,54,45,8\n"
-	"220,221,,,RADIO1,15,40,72,45,8\n"
-	"221,222,,,RADIO1,16,40,80,45,8\n"
-	"222,0,,,RADIO1,17,40,88,45,8\n"
-	"300,301,,,RTEXT,-12,10,40,45,8\n"
-	"301,302,,,EDVAL1,18,60,40,35,10\n"
-	"302,303,,,RTEXT,-13,10,60,45,8\n"
-	"303,304,,,EDVAL1,19,60,60,35,10\n"
-	"304,305,,,RTEXT,20,10,80,45,8\n"
-	"305,,,LASTOBJ, EDVAL1,21,60,80,35,10";
-
-bool
-Bubble::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 52, 10, "Shape & Color"};
-	TabSHEET tab2 = {52, 84, 10, "Scaling"};
-	TabSHEET tab3 = {84, 106, 10, "Edit"};
-	int syms[] = {SYM_CIRCLE, SYM_RECT, SYM_TRIAU, SYM_TRIAD};
-	void *dyndata[] = {(void*)"Apply to BUBBLE", (void*)"Apply to PLOT", (void*)&tab1, (void*)&tab2,
-		(void*)&tab3, (void*)&OD_filldef, (void *)&syms[0], (void *)&syms[1], (void *)&syms[2], (void *)&syms[3],
-		(void*)"Sizes are given as", (void*)"Proportionality (relative to circle)", (void*)"scaling with X axis",
-		(void*)"scaling with Y axis", (void*)"diameter", (void*)"circumference", (void*)"area", (void*)&fPos.fx,
-		(void*)&fPos.fy, (void*)"size", (void*)&fs};
-	DlgInfo *BubDlg = CompileDialog(BubDlg_DlgTmpl, dyndata);
-	DlgRoot *Dlg;
-	void *hDlg;
-	int cb, res, tmpType;
-	lfPOINT o_pos, n_pos;
-	LineDEF newLine, newFillLine;
-	FillDEF newFill;
-	DWORD undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-	double o_size, n_size;
-	bool bRet = false;
-
-	if(!parent) return false;
-	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&BubbleLine, 0);
-	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&BubbleFill, 0);
-	Dlg = new DlgRoot(BubDlg, data);
-	switch(type & 0x00f) {
-	case BUBBLE_SQUARE:		Dlg->SetCheck(110, 0L, true);		break;
-	case BUBBLE_UPTRIA:		Dlg->SetCheck(111, 0L, true);		break;
-	case BUBBLE_DOWNTRIA:	Dlg->SetCheck(112, 0L, true);		break;
-	default:				Dlg->SetCheck(109, 0L, true);		break;
-		}
-	switch(type & 0x0f0) {
-	case BUBBLE_XAXIS:		Dlg->SetCheck(211, 0L, true);		break;
-	case BUBBLE_YAXIS:		Dlg->SetCheck(212, 0L, true);		break;
-	default:				Dlg->SetCheck(210, 0L, true);		break;
-		}
-	switch(type & 0xf00) {
-	case BUBBLE_CIRCUM:		Dlg->SetCheck(221, 0L, true);		break;
-	case BUBBLE_AREA:		Dlg->SetCheck(222, 0L, true);		break;
-	default:				Dlg->SetCheck(220, 0L, true);		break;
-		}
-	if(!Dlg->GetValue(301, &o_pos.fx))	o_pos.fx = fPos.fx;
-	if(!Dlg->GetValue(303, &o_pos.fy))	o_pos.fy = fPos.fy;
-	if(!Dlg->GetValue(305, &o_size))	o_size = fs;
-	n_pos.fx = o_pos.fx;	n_pos.fy = o_pos.fy;	n_size = o_size;
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Bubble of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Bubble properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 396, 260, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 1:			//accept for current bubble only
-		case 2:			//accept for plot
-			Undo.SetDisp(cdisp);
-			OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-			OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
-			memcpy(&newFillLine, &BubbleFillLine, sizeof(LineDEF));
-			if(newFill.hatch) memcpy(&newFillLine, newFill.hatch, sizeof(LineDEF));
-			if(Dlg->GetCheck(110)) tmpType = BUBBLE_SQUARE;
-			else if(Dlg->GetCheck(111)) tmpType = BUBBLE_UPTRIA;
-			else if(Dlg->GetCheck(112)) tmpType = BUBBLE_DOWNTRIA;
-			else tmpType = BUBBLE_CIRCLE;
-			if(Dlg->GetCheck(211)) tmpType |= BUBBLE_XAXIS;
-			else if(Dlg->GetCheck(212)) tmpType |= BUBBLE_YAXIS;
-			if(Dlg->GetCheck(221)) tmpType |= BUBBLE_CIRCUM;
-			else if(Dlg->GetCheck(222)) tmpType |= BUBBLE_AREA;
-			break;
-			}
-		}while (res < 0);
-	switch (res) {
-	case 1:				//new setting for current bubble only
-		Dlg->GetValue(301, &n_pos.fx);		Dlg->GetValue(303, &n_pos.fy);
-		undo_flags = CheckNewLFPoint(&fPos, &o_pos, &n_pos, parent, undo_flags);
-		undo_flags = CheckNewInt(&type, type, tmpType, parent, undo_flags);
-		Dlg->GetValue(305, &n_size);
-		undo_flags = CheckNewFloat(&fs, o_size, n_size, parent, undo_flags);
-		if(cmpLineDEF(&BubbleLine, &newLine)) {
-			Undo.Line(parent, &BubbleLine, undo_flags);		undo_flags |= UNDO_CONTINUE;
-			memcpy(&BubbleLine, &newLine, sizeof(LineDEF));
-			}
-		if(newFill.type && cmpLineDEF(&BubbleFillLine, &newFillLine)) {
-			Undo.Line(parent, &BubbleFillLine, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&BubbleFillLine, &newFillLine, sizeof(LineDEF));
-			}
-		if(cmpFillDEF(&BubbleFill, &newFill)) {
-			Undo.Fill(parent, &BubbleFill, undo_flags);		undo_flags |= UNDO_CONTINUE;
-			memcpy(&BubbleFill, &newFill, sizeof(FillDEF));
-			}
-		BubbleFill.hatch = &BubbleFillLine;
-		if(undo_flags & UNDO_CONTINUE) bRet = true;
-		break;
-	case 2:				//accept settings for plot
-		parent->Command(CMD_SAVE_SYMBOLS, 0L, 0L);
-		parent->Command(CMD_BUBBLE_TYPE, (void*)(& tmpType), 0L);
-		parent->Command(CMD_BUBBLE_ATTRIB, (void*)(& tmpType), 0L);
-		parent->Command(CMD_BUBBLE_FILL, (void*)&newFill, 0L);
-		parent->Command(CMD_BUBBLE_LINE, (void*)&newLine, 0L);
-		bRet = true;
-		break;
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;			free(BubDlg);		return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Bar properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *BarDlg_DlgTmpl = 
-	"1,+,,DEFAULT,PUSHBUTTON,1,130,10,55,12\n"
-	".,.,,,PUSHBUTTON,2,130,25,55,12\n"
-	".,.,,,PUSHBUTTON,-2,130,40,55,12\n"
-	".,,5,CHECKED | ISPARENT,GROUP,0,138,40,55,12\n"
-	"5,+,100,ISPARENT | CHECKED,SHEET,3,5,10,120,120\n"
-	".,.,200,ISPARENT,SHEET,4,5,10,120,120\n"
-	".,,300,ISPARENT,SHEET,5,5,10,120,120\n"
-	"100,109,,NOSELECT,ODBUTTON,6,18,30,90,50\n"
-	"109,+,,,LTEXT,7,10,80,45,8\n"
-	".,.,,,RADIO1,8,20,92,25,8\n"
-	".,.,,,EDTEXT,9,60,92,25,10\n"
-	".,.,,,LTEXT,-3,87,92,20,8\n"
-	".,.,,,RADIO1,10,20,104,25,8\n"
-	".,.,,,EDTEXT,11,60,104,25,10\n"
-	".,,,,LTEXT,-10,87,104,10,8\n"
-	"200,+,,TOUCHEXIT,RADIO2,12,20,30,45,8\n"
-	".,205,202,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
-	".,+,,TOUCHEXIT,RADIO1,13,30,40,35,8\n"
-	".,.,,TOUCHEXIT,RADIO1,14,30,48,35,8\n"
-	".,,,TOUCHEXIT,RADIO1,15,30,56,35,8\n"
-	"205,+,,,EDVAL1,16,65,56,35,10\n"
-	".,.,,TOUCHEXIT,RADIO2,17,20,70,45,8\n"
-	".,211,208,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
-	"208,+,,TOUCHEXIT,RADIO1,18,30,80,35,8\n"
-	".,.,,TOUCHEXIT,RADIO1,19,30,88,35,8\n"
-	".,,,TOUCHEXIT,RADIO1,20,30,96,35,8\n"
-	"211,+,,,EDVAL1,21,65,96,35,10\n"
-	".,,,,CHECKBOX,22,20,113,50,8\n"
-	"300,+,,,RTEXT,-12,10,50,45,8\n"
-	".,.,,,EDVAL1,23,60,50,40,10\n"
-	".,.,,,RTEXT,-13,10,75,45,8\n"
-	".,,,LASTOBJ,EDVAL1,24,60,75,40,10";
-bool
-Bar::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 50, 10, "Size & Color"};
-	TabSHEET tab2 = {50, 90, 10, "Baseline"};
-	TabSHEET tab3 = {90, 120, 10, "Edit"};
-	char sTxt1[20], sTxt2[20];
-	void *dyndata[] = {(void*)"Apply to BAR", (void*)"Apply to PLOT", (void*)&tab1,
-		(void*)&tab2, (void*)&tab3, (void*)OD_filldef, (void*)"bar width:", (void*)" fixed",
-		(void*)&sTxt1[1], (void*)" relative", (void*)&sTxt2[1], (void*)"vertical bars",
-		(void*)"bottom baseline", (void*)"top", (void*)"user y =", (void*)&BarBase.fy,
-		(void*)"horizontal bars", (void*)"left baseline", (void*)"right", (void*)"user x =",
-		(void*)&BarBase.fx, (void*)"bars centered across baseline", (void*)&fPos.fx, (void*)&fPos.fy};
-	DlgInfo *BarDlg = CompileDialog(BarDlg_DlgTmpl, dyndata);
-	DlgRoot *Dlg;
-	void *hDlg;
-	double n_size;
-	int cb, res, tmpType = type;
-	bool bRet = false;
-	LineDEF newLine, newFillLine;
-	FillDEF newFill;
-	DWORD undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-	lfPOINT o_bl, n_bl, o_pos, n_pos;
-
-	if(!parent) return false;
-	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&BarLine, 0);
-	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&BarFill, 0);
-	if(type & BAR_RELWIDTH) {
-		WriteNatFloatToBuff(sTxt2, size);
-		WriteNatFloatToBuff(sTxt1, DefSize(SIZE_BAR));
-		}
-	else {
-		WriteNatFloatToBuff(sTxt1, size);
-		rlp_strcpy(sTxt2, 20, " 50");
-		}
-	Dlg = new DlgRoot(BarDlg, data);
-	switch (type & 0xff) {
-	case BAR_VERTB:		case BAR_VERTT:		case BAR_VERTU:
-		Dlg->SetCheck(200, 0L, true);
-		Dlg->SetCheck(208, 0L, true);
-		switch(type & 0xff) {
-		case BAR_VERTT:	Dlg->SetCheck(203, 0L, true);	break;
-		case BAR_VERTU:	Dlg->SetCheck(204, 0L, true);	break;
-		default:		Dlg->SetCheck(202, 0L, true);	break;
-			}
-		break;
-	case BAR_HORL:		case BAR_HORR:		case BAR_HORU:
-		Dlg->SetCheck(206, 0L, true);
-		Dlg->SetCheck(202, 0L, true);
-		switch(type & 0xff) {
-		case BAR_HORR:	Dlg->SetCheck(209, 0L, true);	break;
-		case BAR_HORU:	Dlg->SetCheck(210, 0L, true);	break;
-		default:		Dlg->SetCheck(208, 0L, true);	break;
-			}
-		break;
-		}
-	if(type & BAR_RELWIDTH) Dlg->SetCheck(113, 0L, true);
-	else Dlg->SetCheck(110, 0L, true);
-	if(type & BAR_CENTERED) Dlg->SetCheck(212, 0L, true);
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Bar of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Bar properties");
-	if(!Dlg->GetValue(211, &o_bl.fx))	o_bl.fx = BarBase.fx;
-	if(!Dlg->GetValue(205, &o_bl.fy))	o_bl.fy = BarBase.fy;
-	if(!Dlg->GetValue(301, &o_pos.fx))	o_pos.fx = fPos.fx;
-	if(!Dlg->GetValue(303, &o_pos.fy))	o_pos.fy = fPos.fy;
-	n_bl.fx = o_bl.fx;			n_bl.fy = o_bl.fy;
-	n_pos.fx = o_pos.fx;		n_pos.fy = o_pos.fy;
-	n_size = size;
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 390, 300, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 202:	case 203:	case 204:
-			Dlg->SetCheck(200, NULL, true);
-			tmpType = res == 202 ? BAR_VERTB : res == 203 ? BAR_VERTT : BAR_VERTU;
-			res = -1;				//continue on radiobutton
-			break;
-		case 208:	case 209:	case 210:
-			Dlg->SetCheck(206, NULL, true);
-			tmpType = res == 208 ? BAR_HORL : res == 209 ? BAR_HORR : BAR_HORU;
-			res = -1;				//continue on radiobutton
-			break;
-		case 200:					//group of vertical bars
-			if(Dlg->GetCheck(203)) tmpType = BAR_VERTT;
-			else if(Dlg->GetCheck(204)) tmpType = BAR_VERTU;
-			else tmpType = BAR_VERTB;
-			res = -1;
-			break;
-		case 206:					//group of horizontal bars
-			if(Dlg->GetCheck(209)) tmpType = BAR_HORR;
-			else if(Dlg->GetCheck(210)) tmpType = BAR_HORU;
-			else tmpType = BAR_HORL;
-			res = -1;
-			break;
-		case 1:
-		case 2:
-			Undo.SetDisp(cdisp);
-			OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-			OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
-			memcpy(&newFillLine, &HatchLine, sizeof(LineDEF));
-			if(newFill.hatch) memcpy(&newFillLine, newFill.hatch, sizeof(LineDEF));
-			if(Dlg->GetCheck(113)) {
-				tmpType |= BAR_RELWIDTH;			Dlg->GetValue(114, &n_size);
-				}
-			else {
-				tmpType &= ~BAR_RELWIDTH;			Dlg->GetValue(111, &n_size);
-				}
-			if(Dlg->GetCheck(212))tmpType |= BAR_CENTERED; 
-			else tmpType &= ~BAR_CENTERED;
-			Dlg->GetValue(211, &n_bl.fx);			Dlg->GetValue(205, &n_bl.fy);
-			Dlg->GetValue(301, &n_pos.fx);			Dlg->GetValue(303, &n_pos.fy);
-			break;
-			}
-		}while (res < 0);
-	switch (res) {
-	case 1:				//new setting for current bar only
-		undo_flags = CheckNewInt(&type, type, tmpType, parent, undo_flags);
-		undo_flags = CheckNewLFPoint(&BarBase, &o_bl, &n_bl, parent, undo_flags);
-		undo_flags = CheckNewLFPoint(&fPos, &o_pos, &n_pos, parent, undo_flags);
-		if(undo_flags & UNDO_CONTINUE) parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-		undo_flags = CheckNewFloat(&size, size, n_size, parent, undo_flags);
-		if(cmpLineDEF(&BarLine, &newLine)) {
-			Undo.Line(parent, &BarLine, undo_flags);		undo_flags |= UNDO_CONTINUE;
-			memcpy(&BarLine, &newLine, sizeof(LineDEF));
-			}
-		if(newFill.type && cmpLineDEF(&HatchLine, &newFillLine)) {
-			Undo.Line(parent, &HatchLine, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&HatchLine, &newFillLine, sizeof(LineDEF));
-			}
-		if(cmpFillDEF(&BarFill, &newFill)) {
-			Undo.Fill(parent, &BarFill, undo_flags);		undo_flags |= UNDO_CONTINUE;
-			memcpy(&BarFill, &newFill, sizeof(FillDEF));
-			}
-		BarFill.hatch = &HatchLine;
-		if(undo_flags & UNDO_CONTINUE) bRet = true;
-		break;
-	case 2:				//new settings to all bars of plot
-		parent->Command(CMD_SAVE_BARS, 0L, 0L);
-		if(parent->Id == GO_PLOTSCATT && parent->parent 
-			&& parent->parent->Id == GO_STACKBAR){ 
-			parent->parent->SetSize(SIZE_BAR, n_size);
-			parent->parent->Command(CMD_BAR_TYPE, (void *)(& tmpType), 0L);
-			}
-		else {
-			parent->SetSize(SIZE_BAR, n_size);
-			parent->Command(CMD_BAR_TYPE, (void *)(& tmpType), 0L);
-			}
-		parent->SetColor(COL_BAR_LINE, newLine.color);
-		parent->SetSize(SIZE_BAR_LINE, newLine.width);
-		parent->SetSize(SIZE_YBASE, n_bl.fy);
-		parent->SetSize(SIZE_XBASE, n_bl.fx);
-		parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-		parent->Command(CMD_BAR_TYPE, (void *)(& tmpType), 0L);
-		parent->Command(CMD_BAR_FILL, (void *)&newFill, 0L);
-		bRet = true;
-		break;
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;		free(BarDlg);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Data line properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *LineDlg_DlgTmpl =
-	"1,2,,DEFAULT,PUSHBUTTON,-1,150,10,45,12\n"
-	"2,3,,,PUSHBUTTON,-2,150,25,45,12\n"
-	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"4,5,100,ISPARENT | CHECKED,SHEET,1,5,10,139,120\n"
-	"5,6,200,ISPARENT,SHEET,2,5,10,139,120\n"
-	"6,,300,ISPARENT | HIDDEN,SHEET,3,5,10,139,120\n"
-	"100,,,NOSELECT,ODBUTTON,9,10,38,130,100\n"
-	"200,201,,,LTEXT,4,10,32,130,100\n"
-	"201,202,,EXRADIO,ODBUTTON,10,12,45,25,25\n"
-	"202,203,,EXRADIO,ODBUTTON,10,37,45,25,25\n"
-	"203,204,,EXRADIO,ODBUTTON,10,62,45,25,25\n"
-	"204,205,,EXRADIO,ODBUTTON,10,87,45,25,25\n"
-	"205,206,,EXRADIO,ODBUTTON,10,112,45,25,25\n"
-	"206,207,,EXRADIO,ODBUTTON,10,37,70,25,25\n"
-	"207,208,,EXRADIO,ODBUTTON,10,62,70,25,25\n"
-	"208,209,,EXRADIO,ODBUTTON,10,87,70,25,25\n"
-	"209,210,,EXRADIO,ODBUTTON,10,112,70,25,25\n"
-	"210,211,,EXRADIO,ODBUTTON,10,37,95,25,25\n"
-	"211,212,,EXRADIO,ODBUTTON,10,62,95,25,25\n"
-	"212,,,EXRADIO,ODBUTTON,10,87,95,25,25\n"
-	"300,301,,,LTEXT,5,15,30,80,9\n"
-	"301,302,,,EDTEXT,7,15,40,119,10\n"
-	"302,303,,,LTEXT,6,15,55,80,9\n"
-	"303,,,LASTOBJ,EDTEXT,8,15,65,119,10";
-
-bool
-DataLine::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 40, 10, "Line"};
-	TabSHEET tab2 = {40, 80, 10, "Style"};
-	TabSHEET tab3 = {80, 120, 10, "Edit"};
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)"select style:", 
-		(void*)"range for x-values", (void*)"range for y-values", (void*)ssXref, (void*)ssYref,
-		(void*)OD_linedef, (void*)(OD_LineStyleTempl)};
-	DlgInfo *LineDlg = CompileDialog(LineDlg_DlgTmpl, dyndata);
-	DlgRoot *Dlg;
-	void *hDlg;
-	int cb, res, tmpType = type;
-	DWORD undo_flags = 0L;
-	LineDEF newLine;
-	bool bRet = false;
-	anyOutput *cdisp = Undo.cdisp;
-
-	if(!parent) return false;
-	if(parent->Id == GO_FUNCTION) return parent->PropertyDlg();
-	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
-	if(!(Dlg = new DlgRoot(LineDlg, data)))return false;
-	Dlg->SetCheck(201 + (type & 0x0f), 0L, true);
-	if(ssXref && ssYref) Dlg->ShowItem(6, true);
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Line of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Line properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 410, 300, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 201:	case 202:	case 203:	case 204:	case 205:	case 206:
-		case 207:	case 208:	case 209:	case 210:	case 211:	case 212:
-			if((tmpType & 0x0f) == (res-201)) res = 1;
-			else {
-				tmpType &= ~0x0f;	tmpType |= (res-201);
-				res = -1;
-				}
-			break;
-			}
-		}while (res < 0);
-	if(res == 1){						//OK pressed
-		Undo.SetDisp(cdisp);
-		if(ssXref && ssYref) {
-			TmpTxt[0] = 0;		Dlg->GetText(301, TmpTxt, TMP_TXT_SIZE); 
-			undo_flags = CheckNewString(&ssXref, ssXref, TmpTxt, this, undo_flags);
-			TmpTxt[0] = 0;	Dlg->GetText(303, TmpTxt, TMP_TXT_SIZE);
-			undo_flags = CheckNewString(&ssYref, ssYref, TmpTxt, this, undo_flags);
-			if(undo_flags & UNDO_CONTINUE) {
-				Command(CMD_UPDATE, 0L, cdisp);			parent->Command(CMD_MRK_DIRTY, 0L, cdisp);
-				}
-			}
-		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-		if(cmpLineDEF(&LineDef, &newLine)) {
-			Undo.Line(parent, &LineDef, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&LineDef, &newLine, sizeof(LineDEF));
-			}
-		undo_flags = CheckNewInt(&type, type, tmpType, parent, undo_flags);
-		if(undo_flags & UNDO_CONTINUE) bRet = true;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;		free(LineDlg);		return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Data polygon properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-DataPolygon::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 40, 10, "Polygon"};
-	DlgInfo LineDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 102, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 102, 25, 45, 12},
-		{3, 0, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 90, 75},
-		{100, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_filldef, 8, 30, 90, 50}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	LineDEF newLine, newFillLine;
-	FillDEF newFill;
-	DWORD undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-	int cb, res;
-	bool bRet = false;
-
-	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
-	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&pgFill, 0);
-	Dlg = new DlgRoot(LineDlg, data);
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Polygon of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Polygon properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 310, 210, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		}while (res < 0);
-	if(res == 1){						//OK pressed
-		Undo.SetDisp(cdisp);
-		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
-		memcpy(&newFillLine, &pgFillLine, sizeof(LineDEF));
-		if(newFill.hatch) memcpy(&newFillLine, newFill.hatch, sizeof(LineDEF));
-		if(cmpLineDEF(&LineDef, &newLine)) {
-			Undo.Line(parent, &LineDef, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&LineDef, &newLine, sizeof(LineDEF));
-			}
-		if(newFill.type && cmpLineDEF(&pgFillLine, &newFillLine)) {
-			Undo.Line(parent, &pgFillLine, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&pgFillLine, &newFillLine, sizeof(LineDEF));
-			}
-		if(cmpFillDEF(&pgFill, &newFill)) {
-			Undo.Fill(parent, &pgFill, undo_flags);		undo_flags |= UNDO_CONTINUE;
-			memcpy(&pgFill, &newFill, sizeof(FillDEF));
-			}
-		pgFill.hatch = &pgFillLine;
-		if(undo_flags & UNDO_CONTINUE) bRet = true;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Regression line properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-RegLine::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 30, 10, "Line"};
-	TabSHEET tab2 = {30, 60, 10, "Model"};
-	TabSHEET tab3 = {60, 95, 10, "Clipping"};
-	char text1[60], text2[60], text3[60], text4[60], text5[60];
-	DlgInfo LineDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 155, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 155, 25, 45, 12},
-		{3, 0, 4, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
-		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 144, 130},
-		{5, 6, 200, ISPARENT, SHEET, &tab2, 5, 10, 144, 130},
-		{6, 0, 300, ISPARENT, SHEET, &tab3, 5, 10, 144, 130},
-		{100, 0, 0, NOSELECT, ODBUTTON, (void*)OD_linedef, 12, 38, 130, 100},
-		{200, 201, 0, TOUCHEXIT, RADIO1, (void*)"y dependent on x", 15, 25, 130, 8},
-		{201, 202, 0, 0x0L, LTEXT, (void*)text1, 25, 33, 120, 8},
-		{202, 203, 0, TOUCHEXIT, RADIO1, (void*)"x dependent on y", 15, 45, 130, 8},
-		{203, 204, 0, 0x0L, LTEXT, (void*)text2, 25, 53, 120, 8},
-		{204, 205, 0, TOUCHEXIT, RADIO1, (void*)"mixed model", 15, 65, 130, 8},
-		{205, 206, 0, 0x0L, LTEXT, (void*)text3, 25, 73, 120, 8},
-		{206, 207, 0, TOUCHEXIT, RADIO1, (void*)"zero crossing (x = 0, y = 0)", 15, 85, 130, 8},
-		{207, 208, 0, 0x0L, LTEXT, (void*)text4, 25, 93, 120, 8},
-		{208, 209, 0, TOUCHEXIT, RADIO1, (void*)"set manually", 15, 105, 130, 8},
-		{209, 210, 0, 0x0L, LTEXT, (void*)text5, 25, 113, 60, 8},
-		{210, 211, 0, 0x0L, RTEXT, (void*)"a =", 92, 113, 10, 8},
-		{211, 212, 0, 0x0L, EDVAL1, &l5.fx, 104, 113, 40, 10},
-		{212, 213, 0, 0x0L, RTEXT, (void*)"b =", 92, 125, 10, 8},
-		{213, 0, 0, 0x0L, EDVAL1, &l5.fy, 104, 125, 40, 10},
-		{300, 301, 0, 0x0L, LTEXT, (void*)"Line length is limited by", 15, 30, 100, 8},
-		{301, 302, 0, TOUCHEXIT, RADIO1, (void*)"data minima and maxima", 15, 45, 100, 8},
-		{302, 303, 0, TOUCHEXIT, RADIO1, (void*)"frame rectangle", 15, 60, 100, 8},
-		{303, 304, 0, TOUCHEXIT, RADIO1, (void*)"user defined values", 15, 75, 100, 8},
-		{304, 305, 0, 0x0L, RTEXT, (void*)"x", 10, 89, 15, 8},
-		{305, 306, 0, 0x0L, EDVAL1, &uclip.Xmin, 27, 89, 40, 10},
-		{306, 307, 0, 0x0L, LTEXT, (void*)"-", 70, 89, 5, 8},
-		{307, 308, 0, 0x0L, EDVAL1, &uclip.Xmax, 76, 89, 40, 10},
-		{308, 309, 0, 0x0L, LTEXT, (void*)"[data]", 119, 89, 15, 8},
-		{309, 310, 0, 0x0L, RTEXT, (void*)"y", 10, 101, 15, 8},
-		{310, 311, 0, 0x0L, EDVAL1, &uclip.Ymin, 27, 101, 40, 10},
-		{311, 312, 0, 0x0L, LTEXT, (void*)"-", 70, 101, 5, 8},
-		{312, 313, 0, 0x0L, EDVAL1, &uclip.Ymax, 76, 101, 40, 10},
-		{313, 0, 0, LASTOBJ, LTEXT, (void*)"[data]", 119, 101, 15, 8}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int cb, res, tmpType;
-	bool bRet = false;
-	LineDEF newLine;
-	DWORD undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-	lfPOINT o_l5, n_l5;
-	fRECT o_clip, n_clip;
-	char *tx, *ty;
-
-	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
-	switch(type &0x700) {
-	case 0x100:		tx = "log(x)";	break;
-	case 0x200:		tx = "(1/x)";	break;
-	case 0x300:		tx = "sqrt(x)"; break;
-	default:		tx = "x";		break;
-		}
-	switch(type &0x7000) {
-	case 0x1000:	ty = "log(y)";	break;
-	case 0x2000:	ty = "(1/y)";	break;
-	case 0x3000:	ty = "sqrt(y)"; break;
-	default:		ty = "y";		break;
-		}
-#ifdef USE_WIN_SECURE
-	sprintf_s(text1, 60, "%s = %.3lf + %.3lf * %s   (n = %ld)", ty, l1.fx, l1.fy, tx, nPoints);
-	sprintf_s(text2, 60, "%s = %.3lf + %.3lf * %s", ty, l2.fx, l2.fy, tx);
-	sprintf_s(text3, 60, "%s = %.3lf + %.3lf * %s", ty, l3.fx, l3.fy, tx);
-	sprintf_s(text4, 60, "%s = %.3lf + %.3lf * %s", ty, l4.fx, l4.fy, tx);
-	sprintf_s(text5, 60, "%s = a + b * %s", ty, tx);
-#else
-	sprintf(text1, "%s = %.3lf + %.3lf * %s   (n = %ld)", ty, l1.fx, l1.fy, tx, nPoints);
-	sprintf(text2, "%s = %.3lf + %.3lf * %s", ty, l2.fx, l2.fy, tx);
-	sprintf(text3, "%s = %.3lf + %.3lf * %s", ty, l3.fx, l3.fy, tx);
-	sprintf(text4, "%s = %.3lf + %.3lf * %s", ty, l4.fx, l4.fy, tx);
-	sprintf(text5, "%s = a + b * %s", ty, tx);
-#endif
-	if(!(Dlg = new DlgRoot(LineDlg, data))) return false;
-	Dlg->Activate(211, false);	Dlg->Activate(213, false);
-	switch(type & 0x07) {
-	case 1:		Dlg->SetCheck(202, 0L, true);	break;
-	case 2:		Dlg->SetCheck(204, 0L, true);	break;
-	case 3:		Dlg->SetCheck(206, 0L, true);	break;
-	case 4:		
-		Dlg->SetCheck(208, 0L, true);
-		Dlg->Activate(211, true);	Dlg->Activate(213, true);
-		break;
-	default:	Dlg->SetCheck(200, 0L, true);	break;
-		}
-	switch(type & 0x70) {
-	case 0x10:	Dlg->SetCheck(302, 0L, true);	break;
-	case 0x20:	Dlg->SetCheck(303, 0L, true);	break;
-	default:	Dlg->SetCheck(301, 0L, true);	break;
-		}
-	if(0x20 == (type & 0x70)) {
-		Dlg->Activate(305, true);	Dlg->Activate(307, true);
-		Dlg->Activate(310, true);	Dlg->Activate(312, true);
-		}
-	else {
-		Dlg->Activate(305, false);	Dlg->Activate(307, false);
-		Dlg->Activate(310, false);	Dlg->Activate(312, false);
-		}
-	if(!Dlg->GetValue(211, &o_l5.fx))	o_l5.fx = l5.fx;
-	if(!Dlg->GetValue(213, &o_l5.fy))	o_l5.fy = l5.fy;
-	n_l5.fx = o_l5.fx;			n_l5.fy = o_l5.fy;
-	if(!Dlg->GetValue(305, &o_clip.Xmin)) o_clip.Xmin = uclip.Xmin;
-	if(!Dlg->GetValue(307, &o_clip.Xmax)) o_clip.Xmax = uclip.Xmax;
-	if(!Dlg->GetValue(310, &o_clip.Ymin)) o_clip.Ymin = uclip.Ymin;
-	if(!Dlg->GetValue(312, &o_clip.Ymax)) o_clip.Ymax = uclip.Ymax;
-	memcpy(&n_clip, &o_clip, sizeof(fRECT));
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Regression line of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Regression line properties");
-	hDlg = CreateDlgWnd(cp ? TmpTxt : 
-		(char*)"Linear regression analysis step 2/2", 50, 50, 420, 320, Dlg, 0x0L);
-	if(!cp) Dlg->SetCheck(5, 0L, true);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 200:	case 202:	case 204:	case 206:
-			Dlg->Activate(211, false);	Dlg->Activate(213, false);
-			res = -1;
-			break;
-		case 208:
-			Dlg->Activate(211, true);	Dlg->Activate(213, true);
-			res = -1;
-			break;
-		case 301:	case 302:
-			Dlg->Activate(305, false);	Dlg->Activate(307, false);
-			Dlg->Activate(310, false);	Dlg->Activate(312, false);
-			res = -1;
-			break;
-		case 303:
-			Dlg->Activate(305, true);	Dlg->Activate(307, true);
-			Dlg->Activate(310, true);	Dlg->Activate(312, true);
-			res = -1;
-			break;
-			}
-		}while (res < 0);
-	if(res == 1){						//OK pressed
-		Undo.SetDisp(cdisp);
-		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-		if(cmpLineDEF(&LineDef, &newLine)) {
-			Undo.Line(parent, &LineDef, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&LineDef, &newLine, sizeof(LineDEF));
-			}
-		tmpType = type & (~0x77);
-		if(Dlg->GetCheck(202)) tmpType |= 1;
-		else if(Dlg->GetCheck(204)) tmpType |= 2;
-		else if(Dlg->GetCheck(206)) tmpType |= 3;
-		else if(Dlg->GetCheck(208)) tmpType |= 4;
-		if(Dlg->GetCheck(302)) tmpType |= 0x10;
-		else if(Dlg->GetCheck(303)) tmpType |= 0x20;
-		undo_flags = CheckNewInt(&type, type, tmpType, parent, undo_flags);
-		Dlg->GetValue(211, &n_l5.fx);			Dlg->GetValue(213, &n_l5.fy);
-		undo_flags = CheckNewLFPoint(&l5, &o_l5, &n_l5, parent, undo_flags);
-		Dlg->GetValue(305, &n_clip.Xmin);		Dlg->GetValue(307, &n_clip.Xmax);
-		Dlg->GetValue(310, &n_clip.Ymin);		Dlg->GetValue(312, &n_clip.Ymax);
-		undo_flags = CheckNewFloat(&uclip.Xmin, o_clip.Xmin, n_clip.Xmin, parent, undo_flags);
-		undo_flags = CheckNewFloat(&uclip.Xmax, o_clip.Xmax, n_clip.Xmax, parent, undo_flags);
-		undo_flags = CheckNewFloat(&uclip.Ymin, o_clip.Ymin, n_clip.Ymin, parent, undo_flags);
-		undo_flags = CheckNewFloat(&uclip.Ymax, o_clip.Ymax, n_clip.Ymax, parent, undo_flags);
-		if(!cp || (undo_flags & UNDO_CONTINUE)) bRet = true;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// SDellipse properties
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char* SdEllipseDlg_Tmpl = 
-	"1,+,,DEFAULT,PUSHBUTTON,-1,150,10,45,12\n"
-	".,.,,,PUSHBUTTON,-2,150,25,45,12\n"
-	".,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"4,+,100,ISPARENT | CHECKED,SHEET,1,5,10,139,140\n"
-	".,,200,ISPARENT,SHEET,2,5,10,139,140\n"
-	"100,,,NOSELECT,ODBUTTON,3,10,48,130,100\n"
-	"200,+,,,CHECKBOX,4,20,26,80,9\n"
-	".,.,250,CHECKED, GROUPBOX,5,10,45,129,100\n"
-	"250,+,,,LTEXT,6,25,82,60,8\n"
-	".,.,,,RTEXT,7,20,92,60,8\n"
-	".,.,,,LTEXT,0,82,92,30,8\n"
-	".,.,,,RTEXT,8,20,100,60,8\n"
-	".,.,,,LTEXT,0,82,100,30,8\n"
-	".,.,,,LTEXT,9,25,112,60,8\n"
-	".,.,,,RTEXT,10,20,122,60,8\n"
-	".,.,,,LTEXT,0,82,122,30,8\n"
-	".,.,,,RTEXT,11,20,130,60,8\n"
-	".,.,,,LTEXT,0,82,130,30,8\n"
-	".,.,,,LTEXT,12,25,52,30,8\n"
-	".,.,,,RADIO1,13,50,52,30,8\n"
-	".,.,,,RADIO1,14,50,61,30,8\n"
-	".,,,LASTOBJ,RADIO1,15,50,70,30,8";
-bool
-SDellipse::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 40, 10, "Line"};
-	TabSHEET tab2 = {40, 80, 10, "Details"};
-	void *dyndata[] = {(void*)&tab1, (void*) &tab2, (void*)OD_linedef, (void*)" show regression line",
-		(void*)"  ellipse  ", (void*)"center (means of data)", (void*)"x =", (void*)"y =",
-		(void*)"standard deviation (S.D.)", (void*)"major axis", (void*)"minor axis", (void*)"size:",
-		(void*)" 1 x S.D.", (void*)" 2 x S.D.", (void*)" 3 x S.D."};
-	DlgInfo *ellDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	int cb, res, tmpType;
-	LineDEF newLine;
-	bool bRet = false;
-	DWORD undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-
-	if(!parent) return false;
-	if(!(ellDlg = CompileDialog(SdEllipseDlg_Tmpl, dyndata))) return false;
-	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
-	if(!(Dlg = new DlgRoot(ellDlg, data))) return false;
-	if(!(type & 0x10000)) Dlg->SetCheck(200, 0L, true);
-	WriteNatFloatToBuff(TmpTxt, mx);		Dlg->SetText(252, TmpTxt+1);
-	WriteNatFloatToBuff(TmpTxt, my);		Dlg->SetText(254, TmpTxt+1);
-	rlp_strcpy(TmpTxt, 4, "+/-");
-	WriteNatFloatToBuff(TmpTxt+3, sd1);		Dlg->SetText(259, TmpTxt);
-	WriteNatFloatToBuff(TmpTxt+3, sd2);		Dlg->SetText(257, TmpTxt);
-	switch(type & 0x60000) {
-		case 0x20000:	Dlg->SetCheck(262, 0L, true);	break;
-		case 0x40000:	Dlg->SetCheck(263, 0L, true);	break;
-		default:		Dlg->SetCheck(261, 0L, true);	break;
-		}
-	tmpType = type;
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "SD ellipse of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "SD ellipse properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 410, 340, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		}while (res < 0);
-	if(res == 1){						//OK pressed
-		Undo.SetDisp(cdisp);
-		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-		if(cmpLineDEF(&LineDef, &newLine)) {
-			Undo.Line(parent, &LineDef, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&LineDef, &newLine, sizeof(LineDEF));
-			}
-		if(Dlg->GetCheck(200)) tmpType &= ~0x10000;
-		else tmpType |= 0x10000;
-		tmpType &= ~0x60000;
-		if(Dlg->GetCheck(262)) tmpType |= 0x20000;
-		else if(Dlg->GetCheck(263)) tmpType |= 0x40000;
-		undo_flags = CheckNewInt(&type, type, tmpType, parent, undo_flags);
-		if(undo_flags & UNDO_CONTINUE) bRet = true;
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;			free(ellDlg);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Normal error bars properties
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-ErrorBar::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 38, 10, "Error Bar"};
-	TabSHEET tab2 = {38, 65, 10, "Type"};
-	TabSHEET tab3 = {65, 90, 10, "Edit"};
-	DlgInfo ErrDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to ERROR", 100, 10, 57, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 100, 25, 57, 12},
-		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 100, 40, 57, 12},
-		{4, 0, 5, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
-		{5, 6, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 90, 100},
-		{6, 7, 200, ISPARENT, SHEET, &tab2, 5, 10, 90, 100},
-		{7, 0, 300, TOUCHEXIT | ISPARENT, SHEET, &tab3, 5, 10, 90, 100},
-		{100, 101, 0, 0x0L, RTEXT, (void*)"cap width", 15, 40, 28, 8},
-		{101, 102, 0, 0x0L, EDVAL1, &SizeBar, 46, 40, 25, 10},
-		{102, 103, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 73, 40, 20, 8},
-		{103, 104, 0, 0x0L, RTEXT, (void*)"line width", 15, 55, 28, 8},
-		{104, 105, 0, 0x0L, EDVAL1, &ErrLine.width, 46, 55, 25, 10},
-		{105, 106, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 73, 55, 20, 8},
-		{106, 107, 0, 0x0L, RTEXT, (void*)"line color", 15, 70, 28, 8},
-		{107, 0, 0, OWNDIALOG, COLBUTT, (void *)&ErrLine.color, 46, 70, 25, 10},
-		{200, 0, 500, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{300, 301, 0, 0x0L, RTEXT, (void*)"x-value", 15, 30, 28, 8},
-		{301, 302, 0, 0x0L, EDVAL1, &fPos.fx, 46, 30, 35, 10},
-		{302, 303, 0, 0x0L, RTEXT, (void*)"y-value", 15, 45, 28, 8},
-		{303, 304, 0, 0x0L, EDVAL1, &fPos.fy, 46, 45, 35, 10},
-		{304, 305, 0, 0x0L, RTEXT, (void*)"error", 15, 60, 28, 8},
-		{305, 306, 0, 0x0L, EDVAL1, &ferr, 46, 60, 35, 10},
-		{306, 307, 0, 0x0L, LTEXT, (void*)"description:", 10, 80, 70, 8},
-		{307, 0, 0, 0x0L, EDTEXT, (void*)name, 10, 90, 80, 10},
-		{500, 501, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 12, 40, 25, 25},
-		{501, 502, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 37, 40, 25, 25},
-		{502, 503, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 62, 40, 25, 25},
-		{503, 504, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 12, 65, 25, 25},
-		{504, 505, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 37, 65, 25, 25},
-		{505, 0, 0, LASTOBJ | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 62, 65, 25, 25}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int cb, res, tmpType = type;
-	double n_sb, o_sb, n_lw, o_lw, n_err, o_err;
-	lfPOINT n_pos, o_pos;
-	DWORD n_col, undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-	bool bRet = false;
-	char desc[80];
-
-	if(!parent) return false;
-	desc[0] = 0;
-	if(!(Dlg = new DlgRoot(ErrDlg, data)))return false;
-	Dlg->SetCheck(500 + (type & 0x7), 0L, true);
-	if(!(Dlg->GetValue(101, &o_sb))) o_sb = SizeBar;
-	if(!(Dlg->GetValue(104, &o_lw))) o_lw = ErrLine.width;
-	if(!(Dlg->GetValue(305, &o_err))) o_err = ferr;
-	if(!(Dlg->GetValue(301, &o_pos.fx))) o_pos.fx = fPos.fx;
-	if(!(Dlg->GetValue(303, &o_pos.fy))) o_pos.fy = fPos.fy;
-	n_sb = o_sb;	n_lw = o_lw;	n_err = o_err; n_pos.fx = o_pos.fx;	n_pos.fy = o_pos.fy;
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Error bar of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Error bar properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 328, 260, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 500:	case 501:	case 502:
-		case 503:	case 504:	case 505:
-			tmpType = res-500;			res = -1;	break;
-		case 7:								//edit tab
-			Dlg->Activate(307, true);	res = -1;	break;
-		case 1:								//accept for this object
-		case 2:								//   or all objects of plot
-			desc[0] = 0;
-			Undo.SetDisp(cdisp);			Dlg->GetText(307, desc, 80);
-			Dlg->GetValue(101, &n_sb);		Dlg->GetValue(104, &n_lw);
-			Dlg->GetColor(107, &n_col);		Dlg->GetValue(305, &n_err);
-			Dlg->GetValue(301, &n_pos.fx);	Dlg->GetValue(303, &n_pos.fy);
-			break;
-			}
-		}while (res <0);
-	switch (res) {
-	case 1:				//new setting for current error bar only
-		undo_flags = CheckNewFloat(&ferr, o_err, n_err, parent, undo_flags);
-		undo_flags = CheckNewLFPoint(&fPos, &o_pos, &n_pos, parent, undo_flags);
-		if(undo_flags & UNDO_CONTINUE) parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-		undo_flags = CheckNewString(&name, name, desc, this, undo_flags);
-		undo_flags = CheckNewFloat(&SizeBar, o_sb, n_sb, parent, undo_flags);
-		undo_flags = CheckNewFloat(&ErrLine.width, o_lw, n_lw, parent, undo_flags);
-		undo_flags = CheckNewDword(&ErrLine.color, ErrLine.color, n_col, parent, undo_flags);
-		undo_flags = CheckNewInt(&type, type, tmpType, parent, undo_flags);
-		if(undo_flags & UNDO_CONTINUE) bRet = true;
-		break;
-	case 2:				//new settings to all error bars of plot
-		parent->Command(CMD_SAVE_ERRS, 0L, 0L);
-		if(desc[0] || name) parent->Command(CMD_ERRDESC, desc, 0L);
-		parent->SetSize(SIZE_ERRBAR, n_sb);
-		parent->SetSize(SIZE_ERRBAR_LINE, n_lw);
-		parent->SetColor(COL_ERROR_LINE, n_col);
-		if(type != tmpType) parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-		bRet = parent->Command(CMD_ERR_TYPE, (void*)(& tmpType), 0L);
-		break;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Arrow properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-Arrow::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 29, 10, "Arrow"};
-	TabSHEET tab2 = {29, 59, 10, "Type"};
-	TabSHEET tab3 = {59, 90, 10, "Edit"};
-	DlgInfo ArrowDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, type & ARROW_UNITS ? 
-			(void*)"OK" : (void*)"Apply to ARROW", 100, 10, 57, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 100, 25, 57, 12},
-		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 100, 
-			type & ARROW_UNITS ? 25 : 40, 57, 12},
-		{4, 50, 5, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
-		{5, 6, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 90, 100},
-		{6, 7, 200, ISPARENT, SHEET, &tab2, 5, 10, 90, 100},
-		{7, 0, 300, ISPARENT, SHEET, &tab3, 5, 10, 90, 100},
-		{50, 0, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
-		{100, 101, 0, 0x0L, RTEXT, (void*)"cap width", 15, 40, 28, 8},
-		{101, 102, 0, 0x0L, EDVAL1, &cw, 46, 40, 25, 10},
-		{102, 103, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 73, 40, 20, 8},
-		{103, 104, 0, 0x0L, RTEXT, (void*)"length", 15, 52, 28, 8},
-		{104, 105, 0, 0x0L, EDVAL1, &cl, 46, 52, 25, 10},
-		{105, 106, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 73, 52, 20, 8},
-		{106, 107, 0, 0x0L, RTEXT, (void*)"line width", 15, 70, 28, 8},
-		{107, 108, 0, 0x0L, EDVAL1, &LineDef.width, 46, 70, 25, 10},
-		{108, 109, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 73, 70, 20, 8},
-		{109, 110, 0, 0x0L, RTEXT, (void*)"color", 15, 82, 28, 8},
-		{110, 0, 0, OWNDIALOG, COLBUTT, (void *)&LineDef.color, 46, 82, 25, 10},
-		{200, 201, 0, TOUCHEXIT, RADIO1, (void*)"line only", 15, 40, 60, 8},
-		{201, 202, 0, TOUCHEXIT, RADIO1, (void*)"arrow with lines", 15, 55, 60, 8},
-		{202, 0, 0, TOUCHEXIT, RADIO1, (void*)"filled arrow", 15, 70, 60, 8},
-		{300, 301, 0, 0x0L, RTEXT, (void*)"x-value", 10, 30, 28, 8},
-		{301, 302, 0, 0x0L, EDVAL1, &pos2.fx, 46, 30, 35, 10},
-		{302, 303, 0, 0x0L, RTEXT, (void*)"y-value", 10, 42, 28, 8},
-		{303, 304, 0, 0x0L, EDVAL1, &pos2.fy, 46, 42, 35, 10},
-		{304, 305, 0, 0x0L, RTEXT, (void*)"origin x", 10, 60, 28, 8},
-		{305, 306, 0, 0x0L, EDVAL1, &pos1.fx, 46, 60, 35, 10},
-		{306, 307, 0, 0x0L, RTEXT, (void*)" y", 10, 72, 28, 8},
-		{307, 308, 0, 0x0L, EDVAL1, &pos1.fy, 46, 72, 35, 10},
-		{308, 309, 0, 0x0L, CHECKBOX, (void*)"set common origin to", 16, 85, 70, 8},
-		{309, 0, 0, 0x0L, LTEXT, (void*)"all arrows of plot", 25, 93, 65, 8},
-		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 108, 67, 15, 15},
-		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 134, 67, 15, 15},
-		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 134, 82, 15, 15},
-		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 108, 82, 15, 15}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	lfPOINT o_pos1, o_pos2, n_pos1, n_pos2;
-	double o_cw, o_cl, n_cw, n_cl, o_lw, n_lw;
-	int cb, res, tmptype = type, undo_level = *Undo.pcb;
-	bool bRet = false;
-	DWORD o_col, n_col, undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-
-	if(!parent) return false;
-	if(!(Dlg = new DlgRoot(ArrowDlg, data))) return false;
-	Dlg->GetValue(301, &o_pos2.fx);		Dlg->GetValue(303, &o_pos2.fy);
-	Dlg->GetValue(305, &o_pos1.fx);		Dlg->GetValue(307, &o_pos1.fy);
-	Dlg->GetValue(101, &o_cw);			Dlg->GetValue(104, &o_cl);
-	Dlg->GetValue(107, &o_lw);			Dlg->GetColor(110, &o_col);
-	if(parent->Id ==  GO_GRAPH || parent->Id == GO_PAGE) Dlg->ShowItem(50, true);
-	switch(type & 0xff) {
-	case ARROW_LINE:		Dlg->SetCheck(201, 0L, true);		break;
-	case ARROW_TRIANGLE:	Dlg->SetCheck(202, 0L, true);		break;
-	default:				Dlg->SetCheck(200, 0L, true);		break;
-		}
-	if(tmptype & ARROW_UNITS){
-		Dlg->ShowItem(2, false);
-		Dlg->ShowItem(308, false);		Dlg->ShowItem(309, false);
-		}
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Arrow of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Arrow properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 328, 260, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();			res = Dlg->GetResult();
-		switch (res) {
-		case 600:	case 601:	case 602:	case 603:
-			Undo.SetDisp(cdisp);
-			res = ExecDrawOrderButt(parent, this, res);
-			}
-		switch (res) {
-		case 200:	tmptype = ARROW_NOCAP;		res = -1;	break;
-		case 201:	tmptype = ARROW_LINE;		res = -1;	break;
-		case 202:	tmptype = ARROW_TRIANGLE;	res = -1;	break;
-		case 1:		case 2:
-			Undo.SetDisp(cdisp);
-			Dlg->GetValue(301, &n_pos2.fx);		Dlg->GetValue(303, &n_pos2.fy);
-			Dlg->GetValue(305, &n_pos1.fx);		Dlg->GetValue(307, &n_pos1.fy);
-			Dlg->GetValue(101, &n_cw);			Dlg->GetValue(104, &n_cl);
-			Dlg->GetValue(107, &n_lw);			Dlg->GetColor(110, &n_col);
-			break;
-			}
-		}while (res <0);
-	switch (res) {
-	case 1:				//new setting for current arrow
-		if(n_pos1.fx != o_pos1.fx || n_pos1.fy != o_pos1.fy ||
-			n_pos2.fx != o_pos2.fx || n_pos2.fy != o_pos2.fy){
-			Command(CMD_SAVEPOS, 0L, 0L);				undo_flags |= UNDO_CONTINUE;
-			memcpy(&pos1, &n_pos1, sizeof(lfPOINT));	memcpy(&pos2, &n_pos2, sizeof(lfPOINT));
-			if(!(type & ARROW_UNITS)) parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-			}
-		if((type & 0xff) != (tmptype & 0xff)){
-			Undo.ValInt(this, &type, undo_flags);
-			type &= ~0xff;	type |= (tmptype & 0xff);	undo_flags |= UNDO_CONTINUE;
-			}
-		undo_flags = CheckNewFloat(&cw, o_cw, n_cw, this, undo_flags);
-		undo_flags = CheckNewFloat(&cl, o_cl, n_cl, this, undo_flags);
-		undo_flags = CheckNewFloat(&LineDef.width, o_lw, n_lw, this, undo_flags);
-		undo_flags = CheckNewDword(&LineDef.color, o_col, n_col, this, undo_flags);
-		if(undo_flags & UNDO_CONTINUE) bModified = true;
-		bRet = true;
-		break;
-	case 2:				//new settings to all arrows of plot
-		if(parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			parent->Command(CMD_SAVE_ARROWS, 0L, 0L);
-			if(Dlg->GetCheck(308)) parent->Command(CMD_ARROW_ORG, &n_pos1, 0L);
-			parent->SetSize(SIZE_ARROW_LINE, n_lw);
-			parent->SetSize(SIZE_ARROW_CAPWIDTH, n_cw);
-			parent->SetSize(SIZE_ARROW_CAPLENGTH, n_cl);
-			parent->SetColor(COL_ARROW, n_col);
-			parent->Command(CMD_ARROW_TYPE, &tmptype, 0L);
-			bRet = true;
-			}
-		break;
-	case 3:								//Cancel
-		Undo.SetDisp(cdisp);
-		if(*Undo.pcb > undo_level) {	//restore plot order
-			while(*Undo.pcb > undo_level)	Undo.Restore(false, 0L);
-			bRet = true;
-			}
-		break;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Box properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-Box::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 50, 10, "Size & Color"};
-	TabSHEET tab2 = {50, 90, 10, "Edit"};
-	char sTxt1[20], sTxt2[20];
-	DlgInfo BoxDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to BOX", 130, 10, 55, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 130, 25, 55, 12},
-		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 40, 55, 12},
-		{4, 0, 5, CHECKED | ISPARENT, GROUP, 0L, 138, 40, 55, 12},
-		{5, 6, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 120, 115},
-		{6, 0, 300, ISPARENT, SHEET, &tab2, 5, 10, 120, 115},
-		{100, 105, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 8, 30, 90, 50},
-		{105, 0, 109, CHECKED | ISPARENT, GROUP, 0L, 0, 0, 0, 0},
-		{109, 110, 0, 0x0L, LTEXT, (void*)"bar width:", 10, 80, 45, 8},
-		{110, 111, 0, 0x0L, RADIO1, (void*)" fixed", 20, 92, 25, 8},
-		{111, 112, 0, 0x0L, EDTEXT, &sTxt1[1], 60, 92, 25, 10},
-		{112, 113, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 87, 92, 20, 8},
-		{113, 114, 0, 0x0L, RADIO1, (void*)" relative", 20, 104, 25, 8},
-		{114, 115, 0, 0x0L, EDTEXT, &sTxt2[1], 60, 104, 25, 10},
-		{115, 0, 0, 0x0L, LTEXT, (void*)"%", 87, 104, 10, 8},
-		{300, 301, 0, 0x0L, RTEXT, (void*)"point 1 x", 15, 40, 28, 8},
-		{301, 302, 0, 0x0L, EDVAL1, &pos1.fx, 46, 40, 35, 10},
-		{302, 303, 0, 0x0L, RTEXT, (void*)"y", 15, 52, 28, 8},
-		{303, 304, 0, 0x0L, EDVAL1, &pos1.fy, 46, 52, 35, 10},
-		{304, 305, 0, 0x0L, RTEXT, (void*)"point 2 x", 15, 70, 28, 8},
-		{305, 306, 0, 0x0L, EDVAL1, &pos2.fx, 46, 70, 35, 10},
-		{306, 307, 0, 0x0L, RTEXT, (void*)"y", 15, 82, 28, 8},
-		{307, 308, 0, 0x0L, EDVAL1, &pos2.fy, 46, 82, 35, 10},
-		{308, 309, 0, HIDDEN, RTEXT, (void*)"box width", 15, 100, 28, 8},
-		{309, 0, 0, HIDDEN | LASTOBJ, EDVAL1, &size, 46, 100, 35, 10},
-		};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int cb, res, tmpType = type;
-	FillDEF newFill;
-	LineDEF newLine, newHatchLine;
-	DWORD undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-	lfPOINT n_pos;
-	double tmpVal, o_size, n_size;
-	bool bRet = false;
-	GraphObj *ppar = parent;
-
-	if(!parent) return false;
-	memcpy(&newHatchLine, &Hatchline, sizeof(LineDEF));
-	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&Outline, 0);
-	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&Fill, 0);
-	if(type & BAR_RELWIDTH) {
-		WriteNatFloatToBuff(sTxt2, size);
-		WriteNatFloatToBuff(sTxt1, DefSize(SIZE_BAR));
-		}
-	else {
-		WriteNatFloatToBuff(sTxt1, size);
-		rlp_strcpy(sTxt2, 20, " 50");
-		}
-	Dlg = new DlgRoot(BoxDlg, data);
-	if(type & BAR_WIDTHDATA) {
-		Dlg->ShowItem(105, false);
-		Dlg->ShowItem(308, true);		Dlg->ShowItem(309, true);
-		}
-	if(type & BAR_RELWIDTH) Dlg->SetCheck(113, 0L, true);
-	else Dlg->SetCheck(110, 0L, true);
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Box of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Box properties");
-	Dlg->GetValue(309, &o_size);
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 390, 290, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 1:								//accept for this object
-		case 2:								//   or all objects of plot
-			Undo.SetDisp(cdisp);
-			OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-			OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
-			if(newFill.hatch) memcpy(&newHatchLine, newFill.hatch, sizeof(LineDEF));
-			newFill.hatch = &newHatchLine;
-			if(type & BAR_WIDTHDATA) Dlg->GetValue(309, &n_size);
-			else {
-				if(Dlg->GetCheck(113)) {
-					tmpType |= BAR_RELWIDTH;
-					Dlg->GetValue(114, &n_size);
-					}
-				else {
-					tmpType &= ~BAR_RELWIDTH;
-					Dlg->GetValue(111, &n_size);
-					}
-				}
-			break;
-			}
-		}while (res < 0);
-	switch (res) {
-	case 1:				//new setting for current box
-		if(cmpLineDEF(&Outline, &newLine)) {
-			Undo.Line(parent, &Outline, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&Outline, &newLine, sizeof(LineDEF));
-			}
-		if(newFill.type && cmpLineDEF(&Hatchline, &newHatchLine)) {
-			Undo.Line(parent, &Hatchline, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&Hatchline, &newHatchLine, sizeof(LineDEF));
-			}
-		if(cmpFillDEF(&Fill, &newFill)) {
-			Undo.Fill(parent, &Fill, undo_flags);		undo_flags |= UNDO_CONTINUE;
-			memcpy(&Fill, &newFill, sizeof(FillDEF));
-			}
-		Fill.hatch = &Hatchline;
-		if(Dlg->GetValue(301, &tmpVal)) n_pos.fx = tmpVal;
-		else n_pos.fx = pos1.fx;
-		if(Dlg->GetValue(303, &tmpVal)) n_pos.fy = tmpVal;
-		else n_pos.fy = pos1.fy;
-		undo_flags = CheckNewLFPoint(&pos1, &pos1, &n_pos, parent, undo_flags);
-		if(Dlg->GetValue(305, &tmpVal)) n_pos.fx = tmpVal;
-		else n_pos.fx = pos2.fx;
-		if(Dlg->GetValue(307, &tmpVal)) n_pos.fy = tmpVal;
-		else n_pos.fy = pos2.fy;
-		undo_flags = CheckNewLFPoint(&pos2, &pos2, &n_pos, parent, undo_flags);
-		undo_flags = CheckNewFloat(&size, o_size, n_size, parent, undo_flags);
-		if(undo_flags & UNDO_CONTINUE) bRet = true;
-		break;
-	case 2:				//new settings to all boxes of plot
-		if(type != tmpType || Outline.width != newLine.width || o_size != n_size) {
-			Undo.ValInt(this, &type, 0L);			//dummy: all following have UNDO_CONTINUE
-			undo_flags |= UNDO_CONTINUE;
-			if(parent->parent && parent->parent->Id == GO_STACKBAR) ppar = parent->parent;
-			}
-		else if(newLine.color != Outline.color || cmpFillDEF(&Fill, &newFill)) {
-			Undo.ValInt(this, &type, 0L);			//dummy: all following have UNDO_CONTINUE
-			}
-		ppar->Command(CMD_SAVE_BARS_CONT, 0L, 0L);
-		ppar->Command(CMD_BOX_TYPE, (void*)(& tmpType), 0L);
-		ppar->SetSize(SIZE_BOX_LINE, newLine.width);
-		ppar->SetSize(SIZE_BOX, n_size);
-		parent->SetColor(COL_BOX_LINE, newLine.color);
-		parent->Command(CMD_BOX_FILL, &newFill, 0L);
-		bRet = true;
-		break;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Whisker properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *WhiskerDlgTmpl =
-		"1,2,,DEFAULT,PUSHBUTTON,1,100,10,64,12\n"
-		"2,3,,,PUSHBUTTON,2,100,25,64,12\n"
-		"3,4,,,PUSHBUTTON,-2, 100, 40, 64, 12\n"
-		"4,,5,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"5,6,100,ISPARENT | CHECKED,SHEET,3, 5, 10, 90, 100\n"
-		"6,7,500,ISPARENT,SHEET,4,5,10,90,100\n"
-		"7,,300,ISPARENT,SHEET,5,5,10,90,100\n"
-		"100,101,,,RTEXT,6,15,40,28,8\n"
-		"101,102,,,EDVAL1,7,46,40,25,10\n"
-		"102,103,,,LTEXT,-3,73,40,20,8\n"
-		"103,104,,,RTEXT,8,15,55,28,8\n"
-		"104,105,,,EDVAL1,9,46,55,25,10\n"
-		"105,106,,,LTEXT,-3,73,55,20,8\n"
-		"106,107,,,RTEXT,10,15,70,28,8\n"
-		"107,,,OWNDIALOG,COLBUTT,11,46,70,25,10\n"
-		"300,301,,,RTEXT,12,15,30,28,8\n"
-		"301,302,,,EDVAL1,13,46,30,35,10\n"
-		"302,303,,,RTEXT,-5,15,42,28,8\n"
-		"303,304,,,EDVAL1,14,46,42,35,10\n"
-		"304,305,,,RTEXT,15,15,55,28,8\n"
-		"305,306,,,EDVAL1,16,46,55,35,10\n"
-		"306,307,,,RTEXT,-5,15,67,28,8\n"
-		"307,308,,,EDVAL1,17,46,67,35,10\n"
-		"308,309,,,LTEXT,18,10,85,70,8\n"
-		"309,,,,EDTEXT,19,10,95,80,10\n"
-		"500,501,,EXRADIO,ODBUTTON,20,32,40,18,18\n"
-		"501,502,,EXRADIO,ODBUTTON,20,14,40,18,18\n"
-		"502,503,,EXRADIO,ODBUTTON,20,50,40,18,18\n"
-		"503,504,,EXRADIO,ODBUTTON,20,68,40,18,18\n"
-		"504,,,LASTOBJ,LTEXT,21,14,30,30,9";
-bool
-Whisker::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 38, 10, "Whisker"};
-	TabSHEET tab2 = {65, 90, 10, "Edit"};
-	TabSHEET tab3 = {38, 65, 10, "Style"};
-	void *dyndata[] = {(void*)"Apply to WHISKER", (void*)"Apply to PLOT", (void*)&tab1,
-		(void*)&tab3, (void*)&tab2, (void*)"cap width", (void*)&size, (void*)"line width",
-		(void*)&LineDef.width, (void*)"line color", (void *)&LineDef.color, (void*)"point 1 x",
-		(void*)&pos1.fx, (void*)&pos1.fy, (void*)"point 2 x", (void*)&pos2.fx, (void*)&pos2.fy,
-		(void*)"description:", (void*)name, (void*)(OD_WhiskerTempl), (void*)"select style:"};
-	DlgInfo *ErrDlg = CompileDialog(WhiskerDlgTmpl, dyndata);
-	DlgRoot *Dlg;
-	void *hDlg;
-	int cb, res, tmp_type;
-	DWORD undo_flags = 0L, n_col = LineDef.color;
-	anyOutput *cdisp = Undo.cdisp;
-	lfPOINT n_pos;
-	double tmpVal, o_size, n_size, o_lw, n_lw;
-	bool bRet = false;
-	char desc[80];
-
-	if(!parent) return false;
-	desc[0] = 0;	tmp_type = type;
-	Dlg = new DlgRoot(ErrDlg, data);
-	Dlg->SetCheck(500 + (tmp_type & 0x03), 0L, true);
-	Dlg->GetValue(101, &o_size);		Dlg->GetValue(104, &o_lw);
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Whisker of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Whisker properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 339, 260, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 500:	case 501:	case 502:	case 503:
-			tmp_type &= ~0x0f;	tmp_type |= (res-500);	res = -1;
-			break;
-		case 1:								//accept for this object
-		case 2:								//   or all objects of plot
-			Undo.SetDisp(cdisp);			Dlg->GetValue(101, &n_size);
-			Dlg->GetValue(104, &n_lw);		Dlg->GetColor(107, &n_col);
-			desc[0] = 0;					Dlg->GetText(309, desc, 80);
-			break;
-			}
-		}while (res <0);
-	switch (res) {
-	case 1:				//new setting for current whisker
-		undo_flags = CheckNewString(&name, name, desc, this, undo_flags);
-		if(Dlg->GetValue(301, &tmpVal)) n_pos.fx = tmpVal;
-		else n_pos.fx = pos1.fx;
-		if(Dlg->GetValue(303, &tmpVal)) n_pos.fy = tmpVal;
-		else n_pos.fy = pos1.fy;
-		undo_flags = CheckNewLFPoint(&pos1, &pos1, &n_pos, parent, undo_flags);
-		if(Dlg->GetValue(305, &tmpVal)) n_pos.fx = tmpVal;
-		else n_pos.fx = pos2.fx;
-		if(Dlg->GetValue(307, &tmpVal)) n_pos.fy = tmpVal;
-		else n_pos.fy = pos2.fy;
-		undo_flags = CheckNewLFPoint(&pos2, &pos2, &n_pos, parent, undo_flags);
-		undo_flags = CheckNewFloat(&size, o_size, n_size, parent, undo_flags);
-		undo_flags = CheckNewInt(&type, type, tmp_type, parent, undo_flags);
-		undo_flags = CheckNewFloat(&LineDef.width, o_lw, n_lw, parent, undo_flags);
-		undo_flags = CheckNewDword(&LineDef.color, LineDef.color, n_col, parent, undo_flags);
-		if(undo_flags & UNDO_CONTINUE) bRet = true;
-		break;
-	case 2:				//new settings to all whiskers of plot
-		if(o_size == n_size && type == tmp_type && o_lw == n_lw 
-			&& n_col == LineDef.color && !desc[0] && !name) break;
-		parent->Command(CMD_SAVE_ERRS, 0L, 0L);
-		if(desc[0] || name) parent->Command(CMD_ERRDESC, desc, 0L);
-		parent->SetSize(SIZE_WHISKER, n_size);
-		parent->SetSize(SIZE_WHISKER_LINE, n_lw);
-		parent->SetColor(COL_WHISKER, n_col);
-		parent->Command(CMD_WHISKER_STYLE, &tmp_type, 0L);
-		bRet = true;
-		break;
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;		free(ErrDlg);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Drop line properties
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-DropLine::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 21, 10, "Line"};
-	TabSHEET tab2 = {21, 42, 10, "Edit"};
-	DlgInfo LineDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to LINE", 150, 10, 50, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 150, 25, 50, 12},
-		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 150, 40, 50, 12},
-		{4, 300, 10, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
-		{10, 11, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 139, 120},
-		{11, 0, 200, ISPARENT, SHEET, &tab2, 5, 10, 139, 120},
-		{100, 0, 0, NOSELECT, ODBUTTON, (void*)OD_linedef, 10, 38, 130, 100},
-		{200, 201, 0, 0x0L, RTEXT, (void*)"x-value", 35, 40, 28, 8},
-		{201, 202, 0, 0x0L, EDVAL1, &fPos.fx, 76, 40, 35, 10},
-		{202, 203, 0, 0x0L, RTEXT, (void*)"y-value", 35, 52, 28, 8},
-		{203, 204, 0, 0x0L, EDVAL1, &fPos.fy, 76, 52, 35, 10},
-		{204, 0, 250, CHECKED | ISPARENT, GROUPBOX, (void*)" Shape ", 10, 70, 129, 50},
-		{250, 251, 0, 0x0L, RTEXT, (void*)"line to:", 15, 80, 28, 8},
-		{251, 252, 0, 0x0L, CHECKBOX, (void*)"left", 46, 80, 35, 8},
-		{252, 253, 0, 0x0L, CHECKBOX, (void*)"right", 46, 90, 35, 8},
-		{253, 254, 0, 0x0L, CHECKBOX, (void*)"y-axis", 46, 100, 35, 8},
-		{254, 255, 0, 0x0L, CHECKBOX, (void*)"top", 86, 80, 35, 8},
-		{255, 256, 0, 0x0L, CHECKBOX, (void*)"bottom", 86, 90, 35, 8},
-		{256, 0, 0, LASTOBJ, CHECKBOX, (void*)"x-axis", 86, 100, 35, 8}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int cb, res, tmptype;
-	bool bRet = false;
-	LineDEF newLine;
-	DWORD undo_flags = 0L;
-	anyOutput * cdisp = Undo.cdisp;
-	lfPOINT o_pos, n_pos;
-
-	if(!parent) return false;
-	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
-	Dlg = new DlgRoot(LineDlg, data);
-	Dlg->SetCheck(251, 0L, type & DL_LEFT ? true : false);
-	Dlg->SetCheck(252, 0L, type & DL_RIGHT ? true : false);
-	Dlg->SetCheck(253, 0L, type & DL_YAXIS ? true : false);
-	Dlg->SetCheck(254, 0L, type & DL_TOP ? true : false);
-	Dlg->SetCheck(255, 0L, type & DL_BOTTOM ? true : false);
-	Dlg->SetCheck(256, 0L, type & DL_XAXIS ? true : false);
-	Dlg->GetValue(201, &o_pos.fx);		Dlg->GetValue(203, &o_pos.fy);
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Dropline of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Dropline properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 415, 300, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 1:							//this line
-		case 2:							//all lines of plot
-			Undo.SetDisp(cdisp);		tmptype = 0;
-			if(Dlg->GetCheck(251)) tmptype |= DL_LEFT;
-			if(Dlg->GetCheck(252)) tmptype |= DL_RIGHT;
-			if(Dlg->GetCheck(253)) tmptype |= DL_YAXIS;
-			if(Dlg->GetCheck(254)) tmptype |= DL_TOP;
-			if(Dlg->GetCheck(255)) tmptype |= DL_BOTTOM;
-			if(Dlg->GetCheck(256)) tmptype |= DL_XAXIS;
-			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-			break;
-			}
-		}while (res < 0);
-	if(res == 1){						//Apply to line
-		if(cmpLineDEF(&LineDef, &newLine)) {
-			Undo.Line(this, &LineDef, undo_flags);
-			memcpy(&LineDef, &newLine, sizeof(LineDEF));
-			undo_flags |= UNDO_CONTINUE;
-			}
-		Dlg->GetValue(201, &n_pos.fx);		Dlg->GetValue(203, &n_pos.fy);
-		undo_flags = CheckNewLFPoint(&fPos, &o_pos, &n_pos, this, undo_flags);
-		undo_flags = CheckNewInt(&type, type, tmptype, this, undo_flags);
-		if (undo_flags & UNDO_CONTINUE) bModified = true;
-		bRet = true;
-		}
-	else if(res == 2) {					//Apply to plot
-		if(cmpLineDEF(&LineDef, &newLine) || type != tmptype) {
-			parent->Command(CMD_SAVE_DROPLINES, 0L, 0L);
-			if(cmpLineDEF(&LineDef, &newLine)) parent->Command(CMD_DL_LINE, (void*)&newLine, 0L);
-			if(type != tmptype) parent->Command(CMD_DL_TYPE, (void*)(&tmptype), 0L);
-			bRet = true;
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Drop line properties: DropLine 3D
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-DropLine3D::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 21, 10, "Line"};
-	TabSHEET tab2 = {21, 42, 10, "Edit"};
-	DlgInfo LineDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to LINE", 150, 10, 50, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 150, 25, 50, 12},
-		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 150, 40, 50, 12},
-		{4, 300, 10, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
-		{10, 11, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 139, 120},
-		{11, 0, 200, ISPARENT, SHEET, &tab2, 5, 10, 139, 120},
-		{100, 0, 0, NOSELECT, ODBUTTON, (void*)OD_linedef, 10, 38, 130, 100},
-		{200, 201, 0, 0x0L, RTEXT, (void*)"x-value", 35, 30, 28, 8},
-		{201, 202, 0, 0x0L, EDVAL1, &fPos.fx, 76, 30, 35, 10},
-		{202, 203, 0, 0x0L, RTEXT, (void*)"y-value", 35, 42, 28, 8},
-		{203, 204, 0, 0x0L, EDVAL1, &fPos.fy, 76, 42, 35, 10},
-		{204, 205, 0, 0x0L, RTEXT, (void*)"z-value", 35, 54, 28, 8},
-		{205, 240, 0, 0x0L, EDVAL1, &fPos.fz, 76, 54, 35, 10},
-		{240, 0, 250, CHECKED | ISPARENT, GROUPBOX, (void*)" Shape ", 10, 70, 129, 50},
-		{250, 251, 0, 0x0L, RTEXT, (void*)"line to:", 15, 80, 28, 8},
-		{251, 252, 0, 0x0L, CHECKBOX, (void*)"bottom", 46, 80, 35, 8},
-		{252, 253, 0, 0x0L, CHECKBOX, (void*)"top", 86, 80, 35, 8},
-		{253, 254, 0, 0x0L, CHECKBOX, (void*)"back", 46, 90, 35, 8},
-		{254, 255, 0, 0x0L, CHECKBOX, (void*)"front", 86, 90, 35, 8},
-		{255, 256, 0, 0x0L, CHECKBOX, (void*)"left", 46, 100, 35, 8},
-		{256, 0, 0, LASTOBJ, CHECKBOX, (void*)"right", 86, 100, 35, 8}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int cb, res, tmptype, i;
-	bool bRet = false;
-	LineDEF newLine;
-	DWORD undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-	fPOINT3D o_pos, n_pos;
-
-	if(!parent) return false;
-	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-	Dlg = new DlgRoot(LineDlg, data);
-	for(i = 0; i < 6; i++) {
-		Dlg->SetCheck(251+i, 0L, (type & (1<<i)) ? true : false);
-		}
-	Dlg->GetValue(201, &o_pos.fx);		Dlg->GetValue(203, &o_pos.fy);
-	Dlg->GetValue(205, &o_pos.fz);
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Dropline of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Dropline properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 415, 300, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 1:							//this line
-		case 2:							//all lines of plot
-			Undo.SetDisp(cdisp);
-			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-			for(i = tmptype = 0; i < 6; i++) {
-				tmptype |= Dlg->GetCheck(251+i) ? 1<<i : 0;
-				}
-			break;
-			}
-		}while (res < 0);
-	if(res == 1){						//Apply to line
-		if(cmpLineDEF(&Line, &newLine)) {
-			Undo.Line(this, &Line, undo_flags);
-			memcpy(&Line, &newLine, sizeof(LineDEF));
-			undo_flags |= UNDO_CONTINUE;
-			}
-		Dlg->GetValue(201, &n_pos.fx);		Dlg->GetValue(203, &n_pos.fy);
-		Dlg->GetValue(205, &n_pos.fz);
-		if(n_pos.fx != o_pos.fx || n_pos.fy != o_pos.fy || n_pos.fz != o_pos.fz) {
-			Undo.ValLFP3D(this, &fPos, undo_flags);
-			memcpy(&fPos, &n_pos, sizeof(fPOINT3D));
-			undo_flags |= UNDO_CONTINUE;
-			}
-		undo_flags = CheckNewInt(&type, type, tmptype, this, undo_flags);
-		if (undo_flags & UNDO_CONTINUE) bModified = true;
-		bRet = true;
-		}
-	else if(res == 2) {					//Apply to plot
-		if(cmpLineDEF(&Line, &newLine) || type != tmptype) {
-			parent->Command(CMD_SAVE_DROPLINES, 0L, 0L);
-			if(cmpLineDEF(&Line, &newLine)) parent->Command(CMD_DL_LINE, (void*)&newLine, 0L);
-			if(type != tmptype) parent->Command(CMD_DL_TYPE, (void*)(&tmptype), 0L);
-			bRet = true;
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// sphere (ball) properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-Sphere::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 22, 10, "Ball"};
-	TabSHEET tab2 = {22, 45, 10, "Edit"};
-	FillDEF newFill;
-	char *type_text[] = {Units[defs.cUnits].display, "[x-data]", "[y-data]", "[z-data]"};
-	DlgInfo BallDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to BALL", 110, 10, 50, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 110, 25, 50, 12},
-		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 110, 40, 50, 12},
-		{4, 0, 10, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
-		{10, 11, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 100, 80},
-		{11, 0, 200, ISPARENT, SHEET, &tab2, 5, 10, 100, 80},
-		{100, 101, 0, 0x0L, RTEXT, (void*)"ball size", 15, 35, 28, 8},
-		{101, 102, 0, 0x0L, EDVAL1, &size, 46, 35, 25, 10},
-		{102, 103, 0, 0x0L, LTEXT, (void*)type_text[type < 4 ? type : 0], 73, 35, 15, 8},
-		{103, 104, 0, 0x0L, RTEXT, (void*)"line width", 15, 47, 28, 8},
-		{104, 105, 0, 0x0L, EDVAL1, &Line.width, 46, 47, 25, 10},
-		{105, 106, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 73, 47, 15, 8},
-		{106, 107, 0, 0x0L, RTEXT, (void*)"line color", 15, 59, 28, 8},
-		{107, 108, 0, OWNDIALOG, COLBUTT, (void *)&Line.color, 46, 59, 25, 10},
-		{108, 109, 0, 0x0L, RTEXT, (void*)"fill color", 15, 71, 28, 8},
-		{109, 0, 0, OWNDIALOG, SHADE3D, &newFill, 46, 71, 25, 10},
-		{200, 201, 0, 0x0L, RTEXT, (void*)"x-value", 15, 35, 28, 8},
-		{201, 202, 0, 0x0L, EDVAL1, &fPos.fx, 46, 35, 35, 10},
-		{202, 203, 0, 0x0L, RTEXT, (void*)"y-value", 15, 47, 28, 8},
-		{203, 204, 0, 0x0L, EDVAL1, &fPos.fy, 46, 47, 35, 10},
-		{204, 205, 0, 0x0L, RTEXT, (void*)"z-value", 15, 59, 28, 8},
-		{205, 0, 0, LASTOBJ, EDVAL1, &fPos.fz, 46, 59, 35, 10}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int cb, res;
-	bool bRet = false, bContinue = false;
-	fPOINT3D n_pos, o_pos;
-	DWORD new_lcolor = Line.color, undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-	double o_size, n_size, o_lsize, n_lsize;
-
-	if(!parent) return false;
-	memcpy(&newFill, &Fill, sizeof(FillDEF));
-	Dlg = new DlgRoot(BallDlg, data);
-	Dlg->GetValue(201, &o_pos.fx);			Dlg->GetValue(203, &o_pos.fy);
-	Dlg->GetValue(205, &o_pos.fz);			Dlg->GetValue(101, &o_size);
-	Dlg->GetValue(104, &o_lsize);
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Ball of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Ball properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 335, 220, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(bContinue) res = -1;
-			bContinue = false;
-			break;
-		case 1:		case 2:
-			Undo.SetDisp(cdisp);			Dlg->GetColor(107, &new_lcolor);
-			Dlg->GetValue(101, &n_size);	Dlg->GetValue(104, &n_lsize);
-			break;
-		case 109:
-			Dlg->DoPlot(0L);				bContinue = true;
-			break;
-			}
-		}while (res < 0);
-	if(res == 1){
-		Dlg->GetValue(201, &n_pos.fx);		Dlg->GetValue(203, &n_pos.fy);
-		Dlg->GetValue(205, &n_pos.fz);
-		if(n_pos.fx != o_pos.fx || n_pos.fy != o_pos.fy || n_pos.fz != o_pos.fz) {
-			Undo.ValLFP3D(this, &fPos, undo_flags);		undo_flags |= UNDO_CONTINUE;
-			memcpy(&fPos, &n_pos, sizeof(fPOINT3D));	parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-			}
-		undo_flags = CheckNewFloat(&size, o_size, n_size, this, undo_flags);
-		undo_flags = CheckNewFloat(&Line.width, o_lsize, n_lsize, this, undo_flags);
-		undo_flags = CheckNewDword(&Line.color, Line.color, new_lcolor, this, undo_flags);
-		undo_flags = CheckNewDword(&Fill.color, Fill.color, newFill.color, this, undo_flags);
-		undo_flags = CheckNewDword(&Fill.color2, Fill.color2, newFill.color2, this, undo_flags);
-		undo_flags = CheckNewInt(&Fill.type, Fill.type, newFill.type, this, undo_flags);
-		if(undo_flags & UNDO_CONTINUE) bRet = bModified = true;
-		}
-	else if(res == 2) {
-		if(cmpFillDEF(&Fill, &newFill) || n_size != o_size || n_lsize != o_lsize ||
-			new_lcolor != Line.color) {
-			parent->Command(CMD_SAVE_SYMBOLS, 0L, 0L);
-			if(cmpFillDEF(&Fill, &newFill)) parent->Command(CMD_SYM_FILL, &newFill, 0L);
-			if(n_size != o_size) parent->SetSize(SIZE_SYMBOL, n_size);
-			if(n_lsize != o_lsize) parent->SetSize(SIZE_SYM_LINE, n_lsize);
-			if(new_lcolor != Line.color) parent->SetColor(COL_SYM_LINE, new_lcolor);
-			bRet = true;
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// properties of 3D plane
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *Plane3D_DlgTmpl = 
-	"1,2,,DEFAULT, PUSHBUTTON,1,115,10,55,12\n"
-	"2,3,,,PUSHBUTTON,2,115,25,55,12\n"
-	"3,4,,,PUSHBUTTON,-2,115,40,55,12\n"
-	"4,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"10,,100,ISPARENT | CHECKED,SHEET,3,5,10,105,85\n"
-	"100,101,,,RTEXT,4,15,30,36,8\n"
-	"101,102,,,EDVAL1,5,53,30,25,10\n"
-	"102,103,,,LTEXT,-3,80,30,15,8\n"
-	"103,104,,,RTEXT,6,15,42,36,8\n"
-	"104,105,,OWNDIALOG,COLBUTT,7,53,42,25,10\n"
-	"105,106,,,RTEXT,8,15,54,36,8\n"
-	"106,200,,OWNDIALOG,SHADE3D,9,53,54,25,10\n"
-	"200,,201,CHECKED,GROUP,0,0,0,0,0\n"
-	"201,202,,,RTEXT,10,15,66,36,8\n"
-	"202,203,,,INCDECVAL1,11,53,66,25,10\n"
-	"203,204,,,LTEXT,-10,80,66,5,8\n"
-	"204,205,,,RTEXT,12,15,78,36,8\n"
-	"205,206,,,EDVAL1,13,53,78,25,10\n"
-	"206,,,LASTOBJ,LTEXT,14,80,78,40,8";
-
-bool
-Plane3D::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 27, 10, "Plane"};
-	double rb_width = 0.0, rb_z = 0.0;
-	FillDEF newFill;
-	void *dyndata[] = {(void*)"Apply to PLANE", (void*)"Apply to PLOT", (void*)&tab1,
-		 (void*)"line width",  (void*)&Line.width, (void*)"line color", (void *)&Line.color,
-		 (void*)"fill color", (void*)&newFill, (void*)"ribbon width", (void*)&rb_width,
-		 (void*)"ribbon pos.", (void*)&rb_z,  (void*)"[z-data]"};
-	DlgInfo *PlaneDlg = CompileDialog(Plane3D_DlgTmpl, dyndata);
-	DlgRoot *Dlg;
-	void *hDlg;
-	int cb, res;
-	bool bRet = false;
-	DWORD new_lcolor = Line.color, undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-	double o_lsize, n_lsize, o_rbw, n_rbw, o_rbz, n_rbz;
-
-	if(!parent) return false;
-	if(parent->Id == GO_GRID3D) return parent->PropertyDlg();
-	memcpy(&newFill, &Fill, sizeof(FillDEF));
-	if(parent->Id == GO_RIBBON && parent->type == 1) {
-		rb_width = parent->GetSize(SIZE_CELLWIDTH) *100.0;
-		rb_z = parent->GetSize(SIZE_ZPOS);
-		}
-	Dlg = new DlgRoot(PlaneDlg, data);
-	Dlg->GetValue(101, &o_lsize);		Dlg->GetValue(202, &o_rbw);
-	Dlg->GetValue(205, &o_rbz);
-	if(parent && ((parent->Id==GO_RIBBON && parent->type > 1) || parent->Id==GO_GRID3D))
-		Dlg->ShowItem(200, false);								//paravent plot
-	if(parent->Id == GO_RIBBON && parent->type >2) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "3D Surface Properties");
-	else if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Plane of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Plane properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 355, 226, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 1:		case 2:
-			Undo.SetDisp(cdisp);				Dlg->GetColor(104, &new_lcolor);
-			Dlg->GetValue(101, &n_lsize);		Dlg->GetValue(202, &n_rbw);
-			Dlg->GetValue(205, &n_rbz);
-			break;
-			}
-		}while (res < 0);
-	if(res == 1){
-		undo_flags = CheckNewFloat(&Line.width, o_lsize, n_lsize, this, undo_flags);
-		undo_flags = CheckNewDword(&Line.color, Line.color, new_lcolor, this, undo_flags);
-		undo_flags = CheckNewDword(&Fill.color, Fill.color, newFill.color, this, undo_flags);
-		undo_flags = CheckNewDword(&Fill.color2, Fill.color2, newFill.color2, this, undo_flags);
-		undo_flags = CheckNewInt(&Fill.type, Fill.type, newFill.type, this, undo_flags);
-		if(undo_flags & UNDO_CONTINUE) bRet = true;
-		}
-	else if(res == 2) {
-		if(cmpFillDEF(&Fill, &newFill) || n_lsize != o_lsize || new_lcolor != Line.color ||
-			o_rbw != n_rbw || o_rbz != n_rbz) {
-			parent->Command(CMD_SAVE_SYMBOLS, 0L, 0L);
-			if(cmpFillDEF(&Fill, &newFill)) parent->Command(CMD_SYM_FILL, &newFill, 0L);
-			if(n_lsize != o_lsize) parent->SetSize(SIZE_SYM_LINE, n_lsize);
-			if(new_lcolor != Line.color) parent->SetColor(COL_POLYLINE, new_lcolor);
-			if(parent->Id == GO_RIBBON && parent->type == 1) {
-				if(o_rbw != n_rbw) parent->SetSize(SIZE_CELLWIDTH, n_rbw/100.0);
-				if(o_rbz != n_rbz) parent->SetSize(SIZE_ZPOS, n_rbz);
-				}
-			bRet = true;
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// properties of 3D column
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-Brick::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 50, 10, "Size & Color"};
-//	TabSHEET tab2 = {50, 90, 10, "Baseline"};
-	TabSHEET tab3 = {50, 80, 10, "Edit"};
-	FillDEF newFill;
-	DlgInfo ColumnDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to COLUMN", 130, 10, 65, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 130, 25, 65, 12},
-		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 40, 65, 12},
-		{4, 0, 5, CHECKED | ISPARENT, GROUP, NULL, 138, 40, 55, 12},
-		{5, 7, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 120, 120},
-//		{6, 7, 0, ISPARENT, SHEET, &tab2, 5, 10, 120, 120},
-		{7, 0, 300, ISPARENT, SHEET, &tab3, 5, 10, 120, 120},
-		{100, 101, 0, 0x0L, RTEXT, (void*)"outline width", 18, 30, 40, 8},
-		{101, 102, 0, 0x0L, EDVAL1, &Line.width, 60, 30, 25, 10},
-		{102, 103, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 87, 30, 20, 8},
-		{103, 104, 0, 0x0L, RTEXT, (void*)"outline color", 18, 42, 40, 8},
-		{104, 105, 0, OWNDIALOG, COLBUTT, (void *)&Line.color, 60, 42, 25, 10},
-		{105, 106, 0, 0x0L, RTEXT,(void*)"fill color" , 18, 54, 40, 8},
-		{106, 107, 0, OWNDIALOG, SHADE3D, &newFill, 60, 54, 25, 10},
-		{107, 108, 0, 0x0L, RTEXT, (void*)"column width", 18, 74, 40, 8},
-		{108, 109, 0, 0x0L, EDVAL1, &width, 60, 74, 25, 10},
-		{109, 110, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 87, 74, 20, 8},
-		{110, 111, 0, 0x0L, RTEXT, (void*)"column depth", 18, 86, 40, 8},
-		{111, 112, 0, 0x0L, EDVAL1, &depth, 60, 86, 25, 10},
-		{112, 0, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 87, 86, 20, 8},
-		{300, 301, 0, 0x0L, RTEXT, (void*)"x-value", 10, 30, 35, 8},
-		{301, 302, 0, 0x0L, EDVAL1, &fPos.fx, 50, 30, 30, 10},
-		{302, 303, 0, 0x0L, RTEXT, (void*)"base (y)", 10, 75, 35, 8},
-		{303, 304, 0, 0x0L, EDVAL1, &fPos.fy, 50, 75, 30, 10},
-		{304, 305, 0, 0x0L, RTEXT, (void*)"z-value", 10, 42, 35, 8},
-		{305, 306, 0, 0x0L, EDVAL1, &fPos.fz, 50, 42, 30, 10},
-		{306, 307, 0, 0x0L, RTEXT, (void*)"height", 10, 87, 35, 8},
-		{307, 0, 0, LASTOBJ, EDVAL1, &height, 50, 87, 30, 10}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int cb, res;
-	bool bRet = false;
-	DWORD col1 = Line.color, undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-	double o_lw = Line.width, n_lw, o_height = height, n_height, o_width = width, n_width;
-	double o_depth = depth, n_depth;
-	fPOINT3D o_pos, n_pos;
-
-	if(!parent) return false;
-	memcpy(&newFill, &Fill, sizeof(FillDEF));
-	Dlg = new DlgRoot(ColumnDlg, data);
-	Dlg->GetValue(101, &o_lw);		Dlg->GetValue(301, &o_pos.fx);
-	Dlg->GetValue(303, &o_pos.fy);	Dlg->GetValue(305, &o_pos.fz);
-	Dlg->GetValue(307, &o_height);	Dlg->GetValue(108, &o_width);
-	Dlg->GetValue(111, &o_depth);
-	memcpy(&n_pos, &o_pos, sizeof(fPOINT3D));
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Column of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Column properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 405, 296, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 1:		case 2:
-			Undo.SetDisp(cdisp);			Dlg->GetColor(104, &col1);
-			Dlg->GetValue(101, &n_lw);		Dlg->GetValue(301, &n_pos.fx);
-			Dlg->GetValue(303, &n_pos.fy);	Dlg->GetValue(305, &n_pos.fz);
-			Dlg->GetValue(307, &n_height);	Dlg->GetValue(108, &n_width);
-			Dlg->GetValue(111, &n_depth);
-			break;
-			}
-		}while (res < 0);
-	switch (res) {
-	case 1:				//new setting for current column only
-		undo_flags = CheckNewFloat(&Line.width, o_lw, n_lw, this, undo_flags);
-		undo_flags = CheckNewDword(&Line.color, Line.color, col1, this, undo_flags);
-		undo_flags = CheckNewDword(&Fill.color, Fill.color, newFill.color, this, undo_flags);
-		undo_flags = CheckNewDword(&Fill.color2, Fill.color2, newFill.color2, this, undo_flags);
-		undo_flags = CheckNewInt(&Fill.type, Fill.type, newFill.type, this, undo_flags);
-		if(o_pos.fx != n_pos.fx || o_pos.fy != n_pos.fy || o_pos.fz != n_pos.fz) {
-			Undo.ValLFP3D(this, &fPos, undo_flags);		undo_flags |= UNDO_CONTINUE;
-			memcpy(&fPos, &n_pos, sizeof(fPOINT3D));	parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-			}
-		undo_flags = CheckNewFloat(&height, o_height, n_height, this, undo_flags);
-		if(o_height != n_height) parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-		undo_flags = CheckNewFloat(&width, o_width, n_width, this, undo_flags);
-		undo_flags = CheckNewFloat(&depth, o_depth, n_depth, this, undo_flags);
-		if(undo_flags & UNDO_CONTINUE) bModified = bRet = true;
-		break;
-	case 2:
-		if(cmpFillDEF(&Fill, &newFill) || o_lw != n_lw || Line.color != col1 ||
-			o_pos.fy != n_pos.fy || n_width != o_width || o_depth != n_depth) {
-			parent->Command(CMD_SAVE_BARS, 0L, 0L);
-			if(o_pos.fy != n_pos.fy){
-				parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-				parent->SetSize(SIZE_BAR_BASE, n_pos.fy);
-				}
-			if(n_lw != o_lw) parent->SetSize(SIZE_BAR_LINE, n_lw);
-			if(n_width != o_width) parent->SetSize(SIZE_BAR, n_width);
-			if(o_depth != n_depth) parent->SetSize(SIZE_BAR_DEPTH, n_depth);
-			if(Line.color != col1) parent->SetColor(COL_BAR_LINE, col1);
-			if(cmpFillDEF(&Fill, &newFill)) parent->Command(CMD_BAR_FILL, &newFill, 0L);
-			bRet = true;
-			}
-		break;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Properties of arrow in 3D space
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *Arrow3D_DlgTmpl =
-	"1,2,,DEFAULT, PUSHBUTTON,1,100,10,57,12\n"
-	"2,3,,,PUSHBUTTON,2,100,25,57,12\n"
-	"3,4,,,PUSHBUTTON,-2,100,40,57,12\n"
-	"4,,5,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"5,6,100,ISPARENT | CHECKED,SHEET,3,5,10,90,100\n"
-	"6,7,200,ISPARENT,SHEET,4,5,10,90,100\n"
-	"7,,300,ISPARENT,SHEET,5,5,10,90,100\n"
-	"100,101,,,RTEXT,6,15,40,28,8\n"
-	"101,102,,,EDVAL1,7,46,40,25,10\n"
-	"102,103,,,LTEXT,-3,73,40,20,8\n"
-	"103,104,,,RTEXT,8,15,52,28,8\n"
-	"104,105,,,EDVAL1,9,46,52,25,10\n"
-	"105,106,,,LTEXT,-3,73,52,20,8\n"
-	"106,107,,,RTEXT,10,15,70,28,8\n"
-	"107,108,,,EDVAL1,11,46,70,25,10\n"
-	"108,109,,,LTEXT,-3,73,70,20,8\n"
-	"109,110,,,RTEXT,-11,15,82,28,8\n"
-	"110,,,OWNDIALOG,COLBUTT,12,46,82,25,10\n"
-	"200,201,,TOUCHEXIT,RADIO1,13,15,40,60,8\n"
-	"201,202,,TOUCHEXIT,RADIO1,14,15,55,60,8\n"
-	"202,,,TOUCHEXIT,RADIO1,15,15,70,60,8\n"
-	"300,301,,,RTEXT,-12,10,25,28,8\n"
-	"301,302,,,EDVAL1,16,46,25,35,10\n"
-	"302,303,,,RTEXT,-13,10,36,28,8\n"
-	"303,304,,,EDVAL1,17,46,36,35,10\n"
-	"304,305,,,RTEXT,-14,10,47,28,8\n"
-	"305,306,,,EDVAL1,18,46,47,35,10\n"
-	"306,307,,,RTEXT,19,10,60,28,8\n"
-	"307,308,,,EDVAL1,20,46,60,35,10\n"
-	"308,309,,,RTEXT,-5,10,71,28,8\n"
-	"309,310,,,EDVAL1,21,46,71,35,10\n"
-	"310,311,,,RTEXT,-6,10,82,28,8\n"
-	"311,312,,,EDVAL1,22,46,82,35,10\n"
-	"312,,,LASTOBJ,CHECKBOX,23,16,95,70,8";
-
-bool
-Arrow3D::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 29, 10, "Arrow"};
-	TabSHEET tab2 = {29, 59, 10, "Type"};
-	TabSHEET tab3 = {59, 90, 10, "Edit"};
-	void *dyndata[] = {(void*)"Apply to ARROW", (void*)"Apply to PLOT", (void*)&tab1,
-		(void*)&tab2, (void*)&tab3, (void*)"cap width", (void*)&cw, (void*)"length",
-		(void*)&cl, (void*)"line width", (void*)&Line.width, (void *)&Line.color,
-		(void*)"line only", (void*)"arrow with lines", (void*)"filled arrow",
-		(void*)&fPos2.fx, (void*)&fPos2.fy, (void*)&fPos2.fz, (void*)"origin x",
-		(void*)&fPos1.fx, (void*)&fPos1.fy, (void*)&fPos1.fz, (void*)"set common origin"};
-	DlgInfo *ArrowDlg = CompileDialog(Arrow3D_DlgTmpl, dyndata);
-	DlgRoot *Dlg;
-	void *hDlg;
-	fPOINT3D o_pos1, o_pos2, n_pos1, n_pos2;
-	double o_cw, o_cl, n_cw, n_cl, o_lw, n_lw;
-	int cb, res, tmptype = type, undo_level = *Undo.pcb;
-	bool bRet = false;
-	DWORD o_col, n_col, undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-
-	if(!parent) return false;
-	if(!(Dlg = new DlgRoot(ArrowDlg, data))) return false;
-	Dlg->GetValue(301, &o_pos2.fx);		Dlg->GetValue(303, &o_pos2.fy);
-	Dlg->GetValue(305, &o_pos2.fz);		Dlg->GetValue(307, &o_pos1.fx);
-	Dlg->GetValue(309, &o_pos1.fy);		Dlg->GetValue(311, &o_pos1.fz);
-	Dlg->GetValue(101, &o_cw);			Dlg->GetValue(104, &o_cl);
-	Dlg->GetValue(107, &o_lw);			Dlg->GetColor(110, &o_col);
-	switch(type & 0xff) {
-	case ARROW_LINE:		Dlg->SetCheck(201, 0L, true);		break;
-	case ARROW_TRIANGLE:	Dlg->SetCheck(202, 0L, true);		break;
-	default:				Dlg->SetCheck(200, 0L, true);		break;
-		}
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Arrow of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Arrow properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 328, 260, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 200:	tmptype = ARROW_NOCAP;		res = -1;	break;
-		case 201:	tmptype = ARROW_LINE;		res = -1;	break;
-		case 202:	tmptype = ARROW_TRIANGLE;	res = -1;	break;
-		case 1:		case 2:
-			Undo.SetDisp(cdisp);
-			Dlg->GetValue(301, &n_pos2.fx);		Dlg->GetValue(303, &n_pos2.fy);
-			Dlg->GetValue(305, &n_pos2.fz);		Dlg->GetValue(307, &n_pos1.fx);
-			Dlg->GetValue(309, &n_pos1.fy);		Dlg->GetValue(311, &n_pos1.fz);
-			Dlg->GetValue(101, &n_cw);			Dlg->GetValue(104, &n_cl);
-			Dlg->GetValue(107, &n_lw);			Dlg->GetColor(110, &n_col);
-			break;
-			}
-		}while (res <0);
-	switch (res) {
-	case 1:				//new setting for current arrow
-		if(n_pos1.fx != o_pos1.fx || n_pos1.fy != o_pos1.fy ||
-			n_pos1.fz != o_pos1.fz){
-			Undo.ValLFP3D(this, &fPos1, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&fPos1, &n_pos1, sizeof(fPOINT3D));
-			parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-			}
-		if(n_pos2.fx != o_pos2.fx || n_pos2.fy != o_pos2.fy ||
-			n_pos2.fz != o_pos2.fz){
-			Undo.ValLFP3D(this, &fPos2, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&fPos2, &n_pos2, sizeof(fPOINT3D));
-			parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-			}
-		if((type & 0xff) != (tmptype & 0xff)){
-			Undo.ValInt(this, &type, undo_flags);
-			type &= ~0xff;	type |= (tmptype & 0xff);	undo_flags |= UNDO_CONTINUE;
-			}
-		undo_flags = CheckNewFloat(&cw, o_cw, n_cw, this, undo_flags);
-		undo_flags = CheckNewFloat(&cl, o_cl, n_cl, this, undo_flags);
-		undo_flags = CheckNewFloat(&Line.width, o_lw, n_lw, this, undo_flags);
-		undo_flags = CheckNewDword(&Line.color, o_col, n_col, this, undo_flags);
-		if(undo_flags & UNDO_CONTINUE) bModified = true;
-		bRet = true;
-		break;
-	case 2:				//new settings to all arrows of plot
-		if(parent->Id >= GO_PLOT && parent->Id < GO_GRAPH && (Dlg->GetCheck(312) || n_lw != o_lw
-			|| n_cw != o_cw || n_cl != o_cl || o_col != n_col || type != tmptype)) {
-			parent->Command(CMD_SAVE_ARROWS, 0L, 0L);
-			if(Dlg->GetCheck(312)) parent->Command(CMD_ARROW_ORG3D, &n_pos1, 0L);
-			if(n_lw != o_lw) parent->SetSize(SIZE_ARROW_LINE, n_lw);
-			if(n_cw != o_cw) parent->SetSize(SIZE_ARROW_CAPWIDTH, n_cw);
-			if(n_cl != o_cl) parent->SetSize(SIZE_ARROW_CAPLENGTH, n_cl);
-			if(o_col != n_col) parent->SetColor(COL_ARROW, n_col);
-			if(type != tmptype) parent->Command(CMD_ARROW_TYPE, &tmptype, 0L);
-			bRet = true;
-			}
-		break;
-		}
-	CloseDlgWnd(hDlg);	delete Dlg;		free(ArrowDlg);		return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// properties of 3D data line
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-Line3D::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 40, 10, "Line"};
-	DlgInfo LineDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 150, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 150, 25, 45, 12},
-		{3, 0, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
-		{4, 0, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 139, 130},
-		{100, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_linedef, 10, 30, 130, 90}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int cb, res;
-	bool bRet = false;
-	LineDEF newLine;
-	anyOutput *cdisp = Undo.cdisp;
-
-	if(!parent) return false;
-	if(parent->Id == GO_GRID3D) return parent->PropertyDlg();
-	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-	if(!(Dlg = new DlgRoot(LineDlg, data)))return false;
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Line of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Line properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 410, 314, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-	}while (res < 0);
-	switch(res) {
-	case 1:							//OK pressed
-		Undo.SetDisp(cdisp);
-		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-		if(cmpLineDEF(&Line, &newLine)) {
-			if(parent->Id == GO_GRID3D) {
-				parent->Command(CMD_SET_LINE, &newLine, 0L);
-				}
-			else {
-				Undo.Line(this, &Line, 0L);
-				memcpy(&Line, &newLine, sizeof(LineDEF));
-				bModified = true;
-				}
-			}
-		bRet = true;
-		break;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// label (text) properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-Label::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 27, 10, "Label"};
-	TabSHEET tab2 = {27, 75, 10, "Font & Style"};
-	TabSHEET tab3 = {75, 110, 10, "Position"};
-	bool isDataLabel = ((parent) && (parent->Id == GO_PLOTSCATT || parent->Id == GO_XYSTAT || parent->Id == GO_BOXPLOT));
-	int bwidth = isDataLabel ? 55 : 45;
-	double lspc = 100.0;
-	DlgInfo LabelDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, isDataLabel ? (void*)"Apply to LABEL" : (void*)"OK", 170, 10, bwidth, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 170, isDataLabel ? 40 : 25, bwidth, 12},
-		{3, isDataLabel ? 10 : 50, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
-		{4, 5, 100, ISPARENT, SHEET, &tab1, 5, 10, 159, 100},
-		{5, 6, 200, ISPARENT | CHECKED, SHEET, &tab2, 5, 10, 159, 100},
-		{6, 0, 300, ISPARENT, SHEET, &tab3, 5, 10, 159, 100},
-		{10, 50, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 170, 25, bwidth, 12},
-		{50, 0, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
-		{100, 101, 0, 0x0L, RTEXT, (void*)"size", 30, 33, 45, 8},
-		{101, 102, 0, 0x0L, INCDECVAL1, &TextDef.fSize, 80, 33, 33, 10},
-		{102, 103, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 115, 33, 20, 8},
-		{103, 104, 0, 0x0L, RTEXT, (void*)"color", 30, 45, 45, 8},
-		{104, 107, 0, OWNDIALOG, COLBUTT, (void *)&TextDef.ColTxt, 80, 45, 25, 10},
-		{105, 106, 0, 0x0L, LTEXT, (void*)"text:", 10, 83, 25, 8},
-		{106, 0, 0, TOUCHEXIT, EDTEXT, (void*)TextDef.text, 10, 95, 149, 10},
-		{107, 108, 0, 0x0L, RTEXT, (void*)"rotation", 30, 57, 45, 8},
-		{108, 109, 0, 0x0L, EDVAL1, &TextDef.RotBL, 80, 57, 25, 10},
-		{109, 150, 0, 0x0L, LTEXT, (void *)"deg.", 107, 57, 20, 8},
-		{150, 154, 151, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
-		{151, 152, 0, 0x0L, RTEXT, (void *)"line spacing", 30, 69, 45, 8},
-		{152, 153, 0, 0x0L, INCDECVAL1, &lspc, 80, 69, 33, 10},
-		{153, 0, 0, 0x0L, LTEXT, (void *)"%", 115, 69, 20, 8},
-		{154, 105, 0, 0x0L, CHECKBOX, (void*) " moveable", 80, 83, 60, 9},
-		{200, 244, 221, CHECKED | ISPARENT, GROUPBOX, (void*)" font ", 17, 28, 60, 50},
-		{221, 222, 0, TOUCHEXIT, RADIO1, (void*)"Helvetica", 20, 35, 45, 8},
-		{222, 223, 0, TOUCHEXIT, RADIO1, (void*)"Times", 20, 45, 45, 8},
-		{223, 224, 0, TOUCHEXIT, RADIO1, (void*)"Courier", 20, 55, 45, 8},
-		{224, 0, 0, TOUCHEXIT, RADIO1, (void*)"Greek", 20, 65, 45, 8},
-		{244, 105, 245, CHECKED | ISPARENT, GROUPBOX, (void*)" style ", 87, 28, 67, 60},
-		{245, 246, 0, TOUCHEXIT, CHECKBOX, (void*)"bold", 90, 35, 25, 8},
-		{246, 247, 0, TOUCHEXIT, CHECKBOX, (void*)"italic", 90, 45, 25, 8},
-		{247, 248, 0, TOUCHEXIT, CHECKBOX, (void*)"underlined", 90, 55, 25, 8},
-		{248, 249, 0, TOUCHEXIT, CHECKBOX, (void*)"superscript", 90, 65, 25, 8},
-		{249, 0, 0, TOUCHEXIT, CHECKBOX, (void*)"subscript", 90, 75, 25, 8},
-		{300, 301, 0, 0x0L, LTEXT, (void*)"text anchor at", 10, 25, 30, 8},
-		{301, 302, 0, 0x0L, RTEXT, (void*)"x", 5, 37, 15, 8},
-		{302, 303, 0, 0x0L, EDVAL1, &fPos.fx, 22, 37, 45, 10},
-		{303, 304, 0, 0x0L, LTEXT, (void*)0L, 69, 37, 10, 8},
-		{304, 305, 0, 0x0L, RTEXT, (void*)"y", 85, 37, 10, 8},
-		{305, 306, 0, 0x0L, EDVAL1, &fPos.fy, 97, 37, 45, 10},
-		{306, 307, 0, 0x0L, LTEXT, (void*)0L, 144, 37, 10, 8},
-		{307, 308, 0, 0x0L, LTEXT, (void*)"distance from anchor point", 10, 52, 50, 8},
-		{308, 309, 0, 0x0L, RTEXT, (void*)"dx", 5, 64, 15, 8},
-		{309, 310, 0, 0x0L, EDVAL1, &fDist.fx, 22, 64, 45, 10},
-		{310, 311, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 69, 64, 10, 8},
-		{311, 312, 0, 0x0L, RTEXT, (void*)"dy", 85, 64, 10, 8},
-		{312, 313, 0, 0x0L, EDVAL1, &fDist.fy, 97, 64, 45, 10},
-		{313, 314, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 144, 64, 10, 8},
-		{314, 315, 0, 0x0L, LTEXT, (void*)"hot spot (text alignment):", 10, 85, 80, 8},
-		{315, 0, 0, 0x0L, TXTHSP, (void*)&TextDef.Align, 97, 78, 45, 25},
-		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 185, 45, 15, 15},
-		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 185, 60, 15, 15},
-		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 185, 75, 15, 15},
-		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 185, 90, 15, 15}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, i, c_style, c_font;
-	bool RetVal = false, check;
-	double tmp;
-	DWORD undo_flags = 0x0;
-	anyOutput *cdisp = Undo.cdisp;
-	lfPOINT o_pos, o_dist, n_pos, n_dist;
-	TextDEF OldTxtDef, NewTxtDef;
-	Axis *pa;
-	fmtText *fmt = 0L;
-
-	if(!parent) return false;
-	if(parent->Id == GO_MLABEL) {
-		lspc = 100.0 * parent->GetSize(SIZE_LSPC);
-		}
-	if (lspc < 50.0) lspc = 100.0;
-	if(Dlg = new DlgRoot(LabelDlg, data)) {
-		if(parent->Id == GO_MLABEL) Dlg->ShowItem(150, true);
-		Dlg->TextFont(221, FONT_HELVETICA);		Dlg->TextFont(222, FONT_TIMES);
-		Dlg->TextFont(223, FONT_COURIER);		Dlg->TextFont(224, FONT_GREEK);
-		Dlg->TextStyle(205, TXS_BOLD);			Dlg->TextStyle(206, TXS_ITALIC);
-		Dlg->TextStyle(207, TXS_UNDERLINE);		Dlg->TextStyle(207, TXS_SUPER);
-		Dlg->TextStyle(207, TXS_SUB);
-		if(moveable){
-			Dlg->SetCheck(154, 0L, true);
-			}
-		switch(TextDef.Font) {
-		case FONT_TIMES:	Dlg->SetCheck(222, 0L, true);	break;
-		case FONT_COURIER:	Dlg->SetCheck(223, 0L, true);	break;
-		case FONT_GREEK:	Dlg->SetCheck(224, 0L, true);	break;
-		default:			Dlg->SetCheck(221, 0L, true);	break;
-			}
-		if(TextDef.Style & TXS_BOLD) Dlg->SetCheck(245, 0L, true);
-		if(TextDef.Style & TXS_ITALIC) Dlg->SetCheck(246, 0L, true);
-		if(TextDef.Style & TXS_UNDERLINE) Dlg->SetCheck(247, 0L, true);
-		if(TextDef.Style & TXS_SUPER) Dlg->SetCheck(248, 0L, true);
-		if(TextDef.Style & TXS_SUB) Dlg->SetCheck(249, 0L, true);
-		}
-	else return false;
-	if(parent->Id ==  GO_GRAPH || parent->Id == GO_PAGE) Dlg->ShowItem(50, true);
-	switch(flags & 0x03) {
-	case LB_X_DATA:
-		Dlg->SetText(303, "[data]");
-		break;
-	case LB_X_PARENT:
-		Dlg->SetText(303, " ");
-		TmpTxt[0] = 0;
-		if(parent->Id == GO_MLABEL) {
-			if(parent->parent->Id == GO_AXIS) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(axis)");
-			else if(parent->parent->Id == GO_TICK) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(tick)");
-			}
-		else {
-			if(parent->Id == GO_AXIS) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(axis)");
-			else if(parent->Id == GO_TICK) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(tick)");
-			}
-		if(TmpTxt[0]) {
-			Dlg->SetText(302, TmpTxt);			Dlg->Activate(302, false);
-			}
-		break;
-	default:
-		Dlg->SetText(303, Units[defs.cUnits].display);
-		break;
-		}
-	switch(flags & 0x30) {
-	case LB_Y_DATA:
-		Dlg->SetText(306, "[data]");
-		break;
-	case LB_Y_PARENT:
-		Dlg->SetText(306, " ");					TmpTxt[0] = 0;
-		if(parent->Id == GO_MLABEL) {
-			if(parent->parent->Id == GO_AXIS) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(axis)");
-			if(parent->parent->Id == GO_TICK) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(tick)");
-			}
-		else {
-			if(parent->Id == GO_AXIS) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(axis)");
-			if(parent->Id == GO_TICK) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(tick)");
-			}
-		if(TmpTxt[0]) {
-			Dlg->SetText(305, TmpTxt);			Dlg->Activate(305, false);
-			}
-		break;
-	default:
-		Dlg->SetText(306, Units[defs.cUnits].display);
-		break;
-		}
-	memcpy(&OldTxtDef, &TextDef, sizeof(TextDEF));	OldTxtDef.text = 0L;
-	Dlg->GetValue(101, &OldTxtDef.fSize);			Dlg->GetValue(108, &OldTxtDef.RotBL);
-	memcpy(&NewTxtDef, &OldTxtDef, sizeof(TextDEF));
-	o_pos.fx = fPos.fx;	o_pos.fy = fPos.fy;	o_dist.fx = fDist.fx;	o_dist.fy = fDist.fy;
-	Dlg->GetValue(302, &o_pos.fx);					Dlg->GetValue(305, &o_pos.fy);
-	Dlg->GetValue(309, &o_dist.fx);					Dlg->GetValue(312, &o_dist.fy);
-	n_pos.fx = o_pos.fx;	n_pos.fy = o_pos.fy;	n_dist.fx = o_dist.fx;	n_dist.fy = o_dist.fy;
-	hDlg = CreateDlgWnd("Label properties", 50, 50, isDataLabel ? 470 : 450, 258, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();			res = Dlg->GetResult();
-		switch (res) {
-		case 600:	case 601:	case 602:	case 603:
-			Undo.SetDisp(cdisp);
-			res = ExecDrawOrderButt(parent, this, res);
-			}
-		switch (res) {
-		case 10:
-			Undo.SetDisp(cdisp);
-			parent->Command(CMD_SAVE_LABELS, 0L, 0L);
-			undo_flags |= UNDO_CONTINUE;
-		case 1:
-			Dlg->GetValue(101, &NewTxtDef.fSize);	Dlg->GetColor(104, &NewTxtDef.ColTxt);
-			Dlg->GetValue(108, &NewTxtDef.RotBL);	Dlg->GetInt(315, &NewTxtDef.Align);
-			Dlg->GetValue(302, &n_pos.fx);			Dlg->GetValue(305, &n_pos.fy);
-			Dlg->GetValue(309, &n_dist.fx);			Dlg->GetValue(312, &n_dist.fy);
-			break;
-		case 106:													//text modified
-			if(!(Dlg->GetText(res, TmpTxt, TMP_TXT_SIZE))) TmpTxt[0] = 0;
-			else {
-				if(!fmt) fmt = new fmtText(0L, 0, 0, TmpTxt);
-				else fmt->SetText(0L, TmpTxt, 0L, 0L);
-				}
-			if(fmt && TmpTxt[0] && Dlg->GetInt(106, &i)) {
-				c_style = NewTxtDef.Style;	c_font = NewTxtDef.Font;
-				fmt->StyleAt(i, &NewTxtDef, &c_style, &c_font);
-				Dlg->SetCheck(245, 0L, TXS_BOLD == (c_style & TXS_BOLD));
-				Dlg->SetCheck(246, 0L, TXS_ITALIC == (c_style & TXS_ITALIC));
-				Dlg->SetCheck(247, 0L, TXS_UNDERLINE == (c_style & TXS_UNDERLINE));
-				Dlg->SetCheck(248, 0L, TXS_SUPER == (c_style & TXS_SUPER));
-				Dlg->SetCheck(249, 0L, TXS_SUB == (c_style & TXS_SUB));
-				switch(c_font) {
-				case FONT_HELVETICA:	Dlg->SetCheck(221, 0L, true);	break;
-				case FONT_TIMES:		Dlg->SetCheck(222, 0L, true);	break;
-				case FONT_COURIER:		Dlg->SetCheck(223, 0L, true);	break;
-				case FONT_GREEK:		Dlg->SetCheck(224, 0L, true);	break;
-					}
-				}
-			res = -1;
-			break;
-		case 221:	case 222:	case 223:	case 224:				//fonts
-			switch (res){
-			case 221:		res = FONT_HELVETICA;		break;
-			case 222:		res = FONT_TIMES;			break;
-			case 223:		res = FONT_COURIER;			break;
-			case 224:		res = FONT_GREEK;			break;
-				}
-			if(!Dlg->ItemCmd(106, CMD_SETFONT, &res)) NewTxtDef.Font = res;
-			res = -1;
-			break;
-		case 245:	case 246:	case 247:	case 248:	case 249:	//styles
-			check = Dlg->GetCheck(res);
-			switch (res){
-			case 245:		res = TXS_BOLD;			break;
-			case 246:		res = TXS_ITALIC;		break;
-			case 247:		res = TXS_UNDERLINE;	break;
-			case 248:		res = TXS_SUPER;		break;
-			case 249:		res = TXS_SUB;			break;
-				}
-			if(!check) {
-				res = ~res;
-				if(!Dlg->ItemCmd(106, CMD_SETSTYLE, &res)) NewTxtDef.Style &= res;
-				}
-			else if(!Dlg->ItemCmd(106, CMD_SETSTYLE, &res)) NewTxtDef.Style |= res;
-			res = -1;
-			break;
-			}
-		}while (res < 0);
-	if(res == 1 || res == 10) {
-		Undo.SetDisp(cdisp);
-		if(parent->Id == GO_TICK && parent->parent && parent->parent->Id == GO_AXIS)
-			pa = (Axis*)parent->parent;
-		else if(parent->Id == GO_MLABEL && parent->parent->Id == GO_TICK 
-			&& parent->parent->parent && parent->parent->parent->Id == GO_AXIS)
-			pa = (Axis*)parent->parent->parent;
-		else pa = 0L;
-		if(pa && (cmpTextDEF(&OldTxtDef, &NewTxtDef) || o_dist.fx != n_dist.fx ||
-			o_dist.fy != n_dist.fy)){
-			pa->Command(CMD_SAVE_TICKS, 0L, 0L);			undo_flags |= UNDO_CONTINUE;
-			}
-		i = Dlg->GetCheck(154) ? 1 : 0;
-		if(i != moveable) {
-			Undo.ValInt(parent, &moveable, undo_flags);
-			moveable = i;									undo_flags |= UNDO_CONTINUE;
-			}
-		TmpTxt[0] = 0;		Dlg->GetText(106, TmpTxt, TMP_TXT_SIZE);
-		if(res == 1) undo_flags = CheckNewString(&TextDef.text, TextDef.text, TmpTxt, this, undo_flags);
-		if(cmpTextDEF(&OldTxtDef, &NewTxtDef)){
-			if(NewTxtDef.ColTxt != TextDef.ColTxt) bBGvalid = false;
-			if (pa) pa->Command(CMD_TLB_TXTDEF, &NewTxtDef, 0L);
-			else if(res == 10 || parent->Id == GO_MLABEL) {
-				if(parent->Command(CMD_SETTEXTDEF, &NewTxtDef, 0L))RetVal = true;
-				}
-			else {
-				Undo.TextDef(this, &TextDef, undo_flags);
-				NewTxtDef.text = TextDef.text;	memcpy(&TextDef, &NewTxtDef, sizeof(TextDEF));	
-				undo_flags |= UNDO_CONTINUE;
-				}
-			}
-		if(n_pos.fx != o_pos.fx || n_pos.fy != o_pos.fy) {
-			if(parent->Id == GO_MLABEL) {
-				if(parent->SetSize(SIZE_XPOS, n_pos.fx) || 
-					parent->SetSize(SIZE_YPOS, n_pos.fy)) RetVal = true;
-				}
-			else {
-				Undo.SaveLFP(this, &fPos, undo_flags);		undo_flags |= UNDO_CONTINUE;
-				fPos.fx = n_pos.fx;		fPos.fy = n_pos.fy;
-				}
-			}
-		if(n_dist.fx != o_dist.fx || n_dist.fy != o_dist.fy) {
-			if(pa) {
-				pa->SetSize(SIZE_TLB_XDIST, n_dist.fx);		pa->SetSize(SIZE_TLB_YDIST, n_dist.fy);
-				}
-			else if(res == 10 || parent->Id == GO_MLABEL) {
-				if(n_dist.fx != o_dist.fx && parent->SetSize(SIZE_LB_XDIST, n_dist.fx)) RetVal = true;
-				if(n_dist.fy != o_dist.fy && parent->SetSize(SIZE_LB_YDIST, n_dist.fy)) RetVal = true;
-				}
-			else {
-				Undo.SaveLFP(this, &fDist, undo_flags);		undo_flags |= UNDO_CONTINUE;
-				fDist.fx = n_dist.fx;	fDist.fy = n_dist.fy;
-				}
-			}
-		if(parent->Id == GO_MLABEL && Dlg->GetValue(152, &tmp) && tmp != lspc) {
-			parent->SetSize(SIZE_LSPC, tmp/100.0);	RetVal = true;
-			}
-		if(undo_flags & UNDO_CONTINUE) RetVal = true;
-		}
-	CloseDlgWnd(hDlg);
-	if(fmt) delete(fmt);
-	delete Dlg;
-	return RetVal;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Text frame properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-TextFrame::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 27, 10, "Text"};
-	TabSHEET tab2 = {27, 57, 10, "Frame"};
-	double lspc2, lspc1 = lspc2 = lspc * 100.0;
-	DlgInfo TextFrmDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 130, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 25, 45, 12},
-		{3, 50, 4, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 119, 100},
-		{5, 0, 200, ISPARENT, SHEET, &tab2, 5, 10, 119, 100},
-		{50, 0, 600, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{100, 101, 0, 0x0L, RTEXT, (void*)"size", 10, 33, 45, 8},
-		{101, 102, 0, 0x0L, INCDECVAL1, &TextDef.fSize, 60, 33, 33, 10},
-		{102, 103, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 95, 33, 20, 8},
-		{103, 104, 0, 0x0L, RTEXT, (void*)"color", 10, 45, 45, 8},
-		{104, 150, 0, OWNDIALOG, COLBUTT, (void *)&TextDef.ColTxt, 60, 45, 25, 10},
-		{150, 0, 151, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{151, 152, 0, 0x0L, RTEXT, (void *)"line spacing", 10, 57, 45, 8},
-		{152, 153, 0, 0x0L, INCDECVAL1, &lspc1, 60, 57, 33, 10},
-		{153, 0, 0, 0x0L, LTEXT, (void *)"%", 95, 57, 20, 8},
-		{200, 0, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 20, 30, 90, 50},
-		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 145, 45, 15, 15},
-		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 145, 60, 15, 15},
-		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 145, 75, 15, 15},
-		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 145, 90, 15, 15}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	anyOutput *cdisp = Undo.cdisp;
-	int i, res;
-	bool bRet = false;
-	LineDEF newLine, newFillLine;
-	FillDEF newFill;
-	DWORD undo_flags = 0L;
-	TextDEF OldTxtDef, NewTxtDef;
-
-	if(!parent || !data) return false;
-	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&Fill, 0);
-	if(!(Dlg = new DlgRoot(TextFrmDlg, data)))return false;
-	memcpy(&OldTxtDef, &TextDef, sizeof(TextDEF));	OldTxtDef.text = 0L;
-	Dlg->GetValue(101, &OldTxtDef.fSize);			memcpy(&NewTxtDef, &OldTxtDef, sizeof(TextDEF));
-	Dlg->GetValue(152, &lspc1);
-	hDlg = CreateDlgWnd("Text Frame", 50, 50, 370, 258, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();			res = Dlg->GetResult();
-		switch (res) {
-		case 1:
-			Dlg->GetValue(101, &NewTxtDef.fSize);	Dlg->GetColor(104, &NewTxtDef.ColTxt);
-			Dlg->GetValue(152, &lspc2);			lspc1 /= 100.0;			lspc2 /= 100.0;
-			break;
-		case 600:	case 601:	case 602:	case 603:
-			Undo.SetDisp(cdisp);
-			res = ExecDrawOrderButt(parent, this, res);
-			break;
-			}
-		}while (res < 0);
-	if(res == 1){						//OK pressed
-		Undo.SetDisp(cdisp);
-		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
-		memcpy(&newFillLine, &FillLine, sizeof(LineDEF));
-		if(newFill.hatch) memcpy(&newFillLine, newFill.hatch, sizeof(LineDEF));
-		if(cmpTextDEF(&OldTxtDef, &NewTxtDef)){
-			Undo.TextDef(this, &TextDef, undo_flags);
-			memcpy(&TextDef, &NewTxtDef, sizeof(TextDEF));	
-			undo_flags |= UNDO_CONTINUE;	TextDef.text = 0L;
-			}
-		undo_flags = CheckNewFloat(&lspc, lspc1, lspc2, parent, undo_flags);
-		if(cmpLineDEF(&Line, &newLine)) {
-			Undo.Line(parent, &Line, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&Line, &newLine, sizeof(LineDEF));
-			}
-		if(newFill.type && cmpLineDEF(&FillLine, &newFillLine)) {
-			Undo.Line(parent, &FillLine, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&FillLine, &newFillLine, sizeof(LineDEF));
-			}
-		if(cmpFillDEF(&Fill, &newFill)) {
-			Undo.Fill(parent, &Fill, undo_flags);		undo_flags |= UNDO_CONTINUE;
-			memcpy(&Fill, &newFill, sizeof(FillDEF));
-			}
-		Fill.hatch = &FillLine;
-		if(undo_flags & UNDO_CONTINUE){
-			bRet = bModified = true;					lines2text();
-			Undo.TextBuffer(this, &csize, &cpos, &text, undo_flags, cdisp);
-			if(lines) {
-				for(i = 0; i < nlines; i++) if(lines[i]) free(lines[i]);
-				free(lines);			lines = 0L;
-				}
-			HideTextCursor();			cur_pos.x = cur_pos.y = 0;
-			}
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// segment properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-segment::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 50, 10, "Size & Color"};
-	TabSHEET tab2 = {50, 90, 10, "Edit"};
-	DlgInfo SegDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to SEGMENT", 130, 10, 65, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 130, 25, 65, 12},
-		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 40, 65, 12},
-		{4, 0, 5, CHECKED | ISPARENT, GROUP, NULL, 138, 40, 55, 12},
-		{5, 6, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 120, 115},
-		{6, 0, 200, ISPARENT, SHEET, &tab2, 5, 10, 120, 115},
-		{100, 109, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 25, 35, 90, 50},
-		{109, 110, 0, 0x0L, RTEXT, (void*)"shift out (explode)", 10, 90, 55, 8},
-		{110, 111, 0, 0x0L, EDVAL1, &shift, 67, 90, 25, 10},
-		{111, 112, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 94, 90, 20, 8},
-		{112, 0, 0, 0x0L, CHECKBOX, (void*)"enable mouse drag (moveable)", 12, 107, 100, 8},
-		{200, 201, 0, 0x0L, RTEXT, (void*)"center x", 10, 30, 40, 8},
-		{201, 202, 0, 0x0L, EDVAL1, &fCent.fx, 55, 30, 40, 10},
-		{202, 203, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 98, 30, 20, 8},
-		{203, 204, 0, 0x0L, RTEXT, (void*)"y", 10, 42, 40, 8},
-		{204, 205, 0, 0x0L, EDVAL1, &fCent.fy, 55, 42, 40, 10},
-		{205, 206, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 98, 42, 20, 8},
-		{206, 207, 0, 0x0L, RTEXT, (void*)"start angle", 10, 57, 40, 8},
-		{207, 208, 0, 0x0L, EDVAL1, &angle1, 55, 57, 40, 10},
-		{208, 209, 0, 0x0L, LTEXT, (void*)"deg.", 98, 57, 20, 8},
-		{209, 210, 0, 0x0L, RTEXT, (void*)"stop", 10, 69, 40, 8},
-		{210, 211, 0, 0x0L, EDVAL1, &angle2, 55, 69, 40, 10},
-		{211, 212, 0, 0x0L, LTEXT, (void*)"deg.", 98, 69, 20, 8},
-		{212, 213, 0, 0x0L, RTEXT, (void*)"outer radius", 10, 84, 40, 8},
-		{213, 214, 0, 0x0L, EDVAL1, &radius2, 55, 84, 40, 10},
-		{214, 215, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 98, 84, 20, 8},
-		{215, 216, 0, 0x0L, RTEXT, (void*)"inner", 10, 96, 40, 8},
-		{216, 217, 0, 0x0L, EDVAL1, &radius1, 55, 96, 40, 10},
-		{217, 0, 0, LASTOBJ, LTEXT, (void *) Units[defs.cUnits].display, 98, 96, 20, 8}};
-
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, new_mov;
-	double old_r1, old_r2, old_a1, old_a2, old_shift;
-	double new_r1, new_r2, new_a1, new_a2, new_shift;
-	lfPOINT old_cent, new_cent;
-	bool bRet = false;
-	LineDEF newLine, newFillLine;
-	FillDEF newFill;
-	DWORD undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-
-	if(!parent) return false;
-	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&segLine, 0);
-	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&segFill, 0);
-	Dlg = new DlgRoot(SegDlg, data);
-	Dlg->GetValue(216, &old_r1);		new_r1 = old_r1;
-	Dlg->GetValue(213, &old_r2);		new_r2 = old_r2;
-	Dlg->GetValue(201, &old_cent.fx);	new_cent.fx = old_cent.fx;
-	Dlg->GetValue(204, &old_cent.fy);	new_cent.fy = old_cent.fy;
-	Dlg->GetValue(207, &old_a1);		new_a1 = old_a1;
-	Dlg->GetValue(210, &old_a2);		new_a2 = old_a2;
-	Dlg->GetValue(110, &old_shift);		new_shift = old_shift;
-	if(moveable) Dlg->SetCheck(112, 0L, true);
-	hDlg = CreateDlgWnd("segment properties", 50, 50, 410, 290, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 1:		case 2:
-			Undo.SetDisp(cdisp);
-			OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-			OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
-			memcpy(&newFillLine, &segFillLine, sizeof(LineDEF));
-			if(newFill.hatch) memcpy(&newFillLine, newFill.hatch, sizeof(LineDEF));
-			Dlg->GetValue(201, &new_cent.fx);			Dlg->GetValue(204, &new_cent.fy);
-			Dlg->GetValue(207, &new_a1);				Dlg->GetValue(210, &new_a2);
-			Dlg->GetValue(216, &new_r1);				Dlg->GetValue(213, &new_r2);
-			Dlg->GetValue(110, &new_shift);
-			new_mov = Dlg->GetCheck(112) ? 1 : 0;
-			break;
-			}
-		}while (res < 0);
-	switch (res) {
-	case 1:				//new setting for current segment only
-		undo_flags = CheckNewLFPoint(&fCent, &old_cent, &new_cent, parent, undo_flags);
-		undo_flags = CheckNewFloat(&angle1, old_a1, new_a1, parent, undo_flags);
-		undo_flags = CheckNewFloat(&angle2, old_a2, new_a2, parent, undo_flags);
-		undo_flags = CheckNewFloat(&radius1, old_r1, new_r1, parent, undo_flags);
-		undo_flags = CheckNewFloat(&radius2, old_r2, new_r2, parent, undo_flags);
-		undo_flags = CheckNewFloat(&shift, old_shift, new_shift, parent, undo_flags);
-		if(cmpLineDEF(&segLine, &newLine)) {
-			Undo.Line(parent, &segLine, undo_flags);		undo_flags |= UNDO_CONTINUE;
-			memcpy(&segLine, &newLine, sizeof(LineDEF));
-			}
-		if(newFill.type && cmpLineDEF(&segFillLine, &newFillLine)) {
-			Undo.Line(parent, &segFillLine, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&segFillLine, &newFillLine, sizeof(LineDEF));
-			}
-		if(cmpFillDEF(&segFill, &newFill)) {
-			Undo.Fill(parent, &segFill, undo_flags);		undo_flags |= UNDO_CONTINUE;
-			memcpy(&segFill, &newFill, sizeof(FillDEF));
-			}
-		segFill.hatch = &segFillLine;
-		undo_flags = CheckNewInt(&moveable, moveable, new_mov, parent, undo_flags);
-		bRet = bModified = ((undo_flags & UNDO_CONTINUE) == UNDO_CONTINUE);
-		break;
-	case 2:				//new settings to all segments of chart
-		if(new_cent.fx != old_cent.fx || new_cent.fy != old_cent.fy || new_r1 != old_r1 || 
-			new_r2 != old_r2 || new_shift != old_shift || cmpLineDEF(&segLine, &newLine) ||
-			(newFill.type && cmpLineDEF(&segFillLine, &newFillLine)) || 
-			cmpFillDEF(&segFill, &newFill) || new_mov != moveable) {
-			parent->Command(CMD_SAVE_SYMBOLS, 0L, 0L);
-			if(new_cent.fx != old_cent.fx || new_cent.fy != old_cent.fy) {
-				parent->SetSize(SIZE_XPOS, new_cent.fx);	parent->SetSize(SIZE_YPOS, new_cent.fy);
-				}
-			if(new_r1 != old_r1 || new_r2 != old_r2) {
-				parent->SetSize(SIZE_RADIUS1, new_r1);		parent->SetSize(SIZE_RADIUS2, new_r2);
-				}
-			if(new_shift != old_shift) parent->Command(CMD_SHIFT_OUT, (void*)&new_shift, 0L);
-			if(cmpLineDEF(&segLine, &newLine))parent->Command(CMD_SEG_LINE, (void*)&newLine, 0L);
-			if((newFill.type && cmpLineDEF(&segFillLine, &newFillLine)) || 
-				cmpFillDEF(&segFill, &newFill)) parent->Command(CMD_SEG_FILL, (void*)&newFill, 0L);
-			if(new_mov != moveable) parent->Command(CMD_SEG_MOVEABLE, (void *)&new_mov, 0L);
-			bRet = true;
-			}
-		break;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// polyline properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-polyline::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 40, 10, "Line"};
-	DlgInfo LineDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 150, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 150, 25, 45, 12},
-		{3, 0, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
-		{4, 50, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 139, 130},
-		{50, 0, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
-		{100, 101, 0, NOSELECT, ODBUTTON, (void*)OD_linedef, 10, 30, 130, 90},
-		{101, 0, 0, 0x0L, CHECKBOX, (void*)"use this style as default", 10, 125, 60, 9},
-		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 165, 60, 15, 15},
-		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 165, 75, 15, 15},
-		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 165, 90, 15, 15},
-		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 165, 105, 15, 15}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, undo_level = *Undo.pcb;
-	anyOutput *cdisp = Undo.cdisp;
-	bool bRet = false;
-	LineDEF newLine;
-
-	if(!parent) return false;
-	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&pgLine, 0);
-	if(!(Dlg = new DlgRoot(LineDlg, data)))return false;
-	if(parent->Id ==  GO_GRAPH || parent->Id == GO_PAGE) Dlg->ShowItem(50, true);
-	hDlg = CreateDlgWnd("line properties", 50, 50, 410, 314, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();			res = Dlg->GetResult();
-		switch (res) {
-		case 600:	case 601:	case 602:	case 603:
-			Undo.SetDisp(cdisp);
-			res = ExecDrawOrderButt(parent, this, res);	break;
-		case 1:		case 2:
-			Undo.SetDisp(cdisp);	break;
-			}
-	}while (res < 0);
-	switch(res) {
-	case 1:							//OK pressed
-		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-		if(cmpLineDEF(&pgLine, &newLine)) {
-			Undo.Line(this, &pgLine, 0L);
-			memcpy(&pgLine, &newLine, sizeof(LineDEF));
-			bModified = true;
-			}
-		if(Dlg->GetCheck(101)) defs.plLineDEF(&pgLine);
-		bRet = true;
-		break;
-	case 2:							//Cancel
-		if(*Undo.pcb > undo_level) {	//restore plot order
-			while(*Undo.pcb > undo_level)	Undo.Restore(false, 0L);
-			bRet = true;
-			}
-		break;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// polygon properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-polygon::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 40, 10, "Polygon"};
-	DlgInfo PolygDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 102, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 102, 25, 45, 12},
-		{3, 50, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 90, 85},
-		{50, 0, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
-		{100, 101, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 8, 30, 90, 50},
-		{101, 0, 0, 0x0L, CHECKBOX, (void*)"use this style as default", 10, 82, 60, 9},
-		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 107, 52, 15, 15},
-		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 127, 52, 15, 15},
-		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 127, 67, 15, 15},
-		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 107, 67, 15, 15}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	LineDEF newLine, newFillLine;
-	FillDEF newFill;
-	DWORD undo_flags = 0L;
-	int res, undo_level = *Undo.pcb;
-	anyOutput *cdisp = Undo.cdisp;
-	bool bRet = false;
-
-	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&pgLine, 0);
-	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&pgFill, 0);
-	Dlg = new DlgRoot(PolygDlg, data);
-	if(parent->Id ==  GO_GRAPH || parent->Id == GO_PAGE) Dlg->ShowItem(50, true);
-	hDlg = CreateDlgWnd("polygon properties", 50, 50, 310, 224, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();			res = Dlg->GetResult();
-		switch(res) {
-		case 600:	case 601:	case 602:	case 603:
-			Undo.SetDisp(cdisp);
-			res = ExecDrawOrderButt(parent, this, res);		break;
-		case 1:		case 2:
-			Undo.SetDisp(cdisp);	break;
-			}
-		}while (res < 0);
-	switch (res) {
-	case 1:						//OK pressed
-		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
-		memcpy(&newFillLine, &pgFillLine, sizeof(LineDEF));
-		if(newFill.hatch) memcpy(&newFillLine, newFill.hatch, sizeof(LineDEF));
-		if(cmpLineDEF(&pgLine, &newLine)) {
-			Undo.Line(this, &pgLine, undo_flags);		undo_flags |= UNDO_CONTINUE;
-			memcpy(&pgLine, &newLine, sizeof(LineDEF));
-			}
-		if(newFill.type && cmpLineDEF(&pgFillLine, &newFillLine)) {
-			Undo.Line(this, &pgFillLine, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&pgFillLine, &newFillLine, sizeof(LineDEF));
-			}
-		if(cmpFillDEF(&pgFill, &newFill)) {
-			Undo.Fill(this, &pgFill, undo_flags);		undo_flags |= UNDO_CONTINUE;
-			memcpy(&pgFill, &newFill, sizeof(FillDEF));
-			}
-		pgFill.hatch = &pgFillLine;
-		if(undo_flags & UNDO_CONTINUE) bModified = true;
-		if(Dlg->GetCheck(101)){
-			defs.pgLineDEF(&pgLine);			defs.pgFillDEF(&pgFill);
-			}
-		bRet = true;
-		break;
-	case 2:							//Cancel
-		if(*Undo.pcb > undo_level) {	//restore plot order
-			while(*Undo.pcb > undo_level)	Undo.Restore(false, 0L);
-			bRet = true;
-			}
-		break;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// rectangle, round rectangle and ellipse properties dialogs
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-rectangle::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 40, 10, type == 1 ? (char*)"Ellipse" :(char*)"Rectangle"};
-	TabSHEET tab2 = {40, 63, 10, "Edit"};
-	DlgInfo RecDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 112, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 112, 25, 45, 12},
-		{3, 0, 10, CHECKED | ISPARENT, GROUP, 0L, 0, 0, 0, 0},
-		{10, 11, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 100, 97},
-		{11, 50, 200, ISPARENT, SHEET, &tab2, 5, 10, 100, 97},
-		{50, 0, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
-		{100, 101, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 8, 30, 90, 50},
-		{101, 120, 110, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{110, 111, 0, 0x0L, RTEXT, (void*)"edge radius", 0, 79, 48, 8},
-		{111, 112, 0, 0x0L, EDVAL1, &rad, 50, 79, 25, 10},
-		{112, 0, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 77, 79, 10, 8},
-		{120, 0, 0, 0x0L, CHECKBOX, (void*)"use this style as default", 10, 92, 60, 9},
-		{200, 201, 0, 0x0L, RTEXT, (void*)"top left x", 5, 30, 40, 8},
-		{201, 202, 0, 0x0L, EDVAL1, &fp1.fx, 47, 30, 38, 10},
-		{202, 203, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 87, 30, 10, 8},
-		{203, 204, 0, 0x0L, RTEXT, (void*)"y", 5, 42, 40, 8},
-		{204, 205, 0, 0x0L, EDVAL1, &fp1.fy, 47, 42, 38, 10},
-		{205, 206, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 87, 42, 10, 8},
-		{206, 207, 0, 0x0L, RTEXT, (void*)"lower right x", 5, 55, 40, 8},
-		{207, 208, 0, 0x0L, EDVAL1, &fp2.fx, 47, 55, 38, 10},
-		{208, 209, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 87, 55, 10, 8},
-		{209, 210, 0, 0x0L, RTEXT, (void*)"y", 5, 67, 40, 8},
-		{210, 211, 0, 0x0L, EDVAL1, &fp2.fy, 47, 67, 38, 10},
-		{211, 0, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 87, 67, 10, 8},
-		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 115, 64, 15, 15},
-		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 139, 64, 15, 15},
-		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 139, 79, 15, 15},
-		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 115, 79, 15, 15}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, undo_level = *Undo.pcb;
-	lfPOINT old_fp1, new_fp1, old_fp2, new_fp2;
-	LineDEF old_Line, new_Line, old_FillLine, new_FillLine;
-	FillDEF old_Fill, new_Fill;
-	DWORD undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-	bool bRet = false;
-
-	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&Fill, 0);
-	if(!(Dlg = new DlgRoot(RecDlg, data)))return false;
-	Dlg->GetValue(201, &old_fp1.fx);		Dlg->GetValue(204, &old_fp1.fy);
-	Dlg->GetValue(207, &old_fp2.fx);		Dlg->GetValue(210, &old_fp2.fy);
-	if(type != 2) Dlg->ShowItem(101, false);
-	if(parent->Id ==  GO_GRAPH || parent->Id == GO_PAGE) Dlg->ShowItem(50, true);
-	OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&old_Line, 0);
-	OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&old_Fill, 0);
-	if(old_Fill.hatch) memcpy(&old_FillLine, old_Fill.hatch, sizeof(LineDEF));
-	old_Fill.hatch = &old_FillLine;
-	hDlg = CreateDlgWnd(type == 1 ? (char*)"ellipse properties":
-		type == 2 ? (char*)"rounded rectangle" : (char*)"rectangle properties", 
-		50, 50, 330, 248, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();			res = Dlg->GetResult();
-		switch(res) {
-		case 600:	case 601:	case 602:	case 603:
-			Undo.SetDisp(cdisp);
-			res = ExecDrawOrderButt(parent, this, res);		break;
-		case 1:		case 2:
-			Undo.SetDisp(cdisp);	break;
-			}
-		}while (res < 0);
-	switch (res) {
-	case 1:						//OK pressed
-		Dlg->GetValue(201, &new_fp1.fx);		Dlg->GetValue(204, &new_fp1.fy);
-		Dlg->GetValue(207, &new_fp2.fx);		Dlg->GetValue(210, &new_fp2.fy);
-		undo_flags = CheckNewLFPoint(&fp1, &old_fp1, &new_fp1, this, undo_flags);
-		undo_flags = CheckNewLFPoint(&fp2, &old_fp2, &new_fp2, this, undo_flags);
-		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&new_Line, 0);
-		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&new_Fill, 0);
-		if(new_Fill.hatch) memcpy(&new_FillLine, new_Fill.hatch, sizeof(LineDEF));
-		new_Fill.hatch = &new_FillLine;
-		if(cmpLineDEF(&old_Line, &new_Line)) {
-			Undo.Line(this, &Line, undo_flags);
-			undo_flags |= UNDO_CONTINUE;
-			memcpy(&Line, &new_Line, sizeof(LineDEF));
-			}
-		if(new_Fill.type && cmpLineDEF(&old_FillLine, &new_FillLine)) {
-			Undo.Line(this, &FillLine, undo_flags);
-			undo_flags |= UNDO_CONTINUE;
-			memcpy(&FillLine, &new_FillLine, sizeof(LineDEF));
-			}
-		if(cmpFillDEF(&Fill, &new_Fill)) {
-			Undo.Fill(this, &Fill, undo_flags);
-			undo_flags |= UNDO_CONTINUE;
-			memcpy(&Fill, &new_Fill, sizeof(FillDEF));
-			}
-		Fill.hatch = &FillLine;
-		if(type == 2) Dlg->GetValue(111, &rad);
-		else rad = 0.0;
-		if(Dlg->GetCheck(120)){
-			defs.pgLineDEF(&Line);			defs.pgFillDEF(&Fill);
-			if(type == 2)defs.rrectRad(rad);
-			}
-		if(undo_flags) bModified = true;
-		bRet = true;
-		break;
-	case 2:							//Cancel
-		if(*Undo.pcb > undo_level) {	//restore plot order
-			while(*Undo.pcb > undo_level)	Undo.Restore(false, 0L);
-			bRet = true;
-			}
-		break;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Create a bar chart: note this is a PlotScatt function
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-PlotScatt::CreateBarChart()
-{
-	TabSHEET tab1 = {0, 25, 10, "Data"};
-	TabSHEET tab2 = {25, 55, 10, "Details"};
-	double start = 1.0, step = 1.0, bw = 60.0;
-	DlgInfo BarDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 130, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 25, 45, 12},
-		{3, 0, 4, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
-		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 120, 110},
-		{5, 10, 200, ISPARENT, SHEET, &tab2, 5, 10, 120, 110},
-		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
-		{100, 101, 0, 0x0L, LTEXT, (void*)"spread sheet range for values", 10, 25, 60, 8},
-		{101, 102, 0, 0x0L, RANGEINPUT, TmpTxt, 10, 38, 110, 10},
-		{102, 0, 110, ISPARENT | CHECKED, GROUPBOX, (void*)" style ", 10, 55, 110, 61}, 
-		{110, 0, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 25, 62, 90, 50},
-		{200, 201, 0, 0x0L, RTEXT, (void*)"start value", 10, 35, 38, 8},
-		{201, 202, 0, 0x0L, EDVAL1, (void*)&start, 58, 35, 35, 10},
-		{202, 203, 0, 0x0L, RTEXT, (void*)"step value", 10, 50, 38, 8},
-		{203, 204, 0, 0x0L, EDVAL1, (void*)&step, 58, 50, 35, 10},
-		{204, 205, 0, 0x0L, RTEXT, (void*)"bar width", 10, 65, 38, 8},
-		{205, 206, 0, 0x0L, EDVAL1, (void*)&bw, 58, 65, 35, 10},
-		{206, 0, 0, LASTOBJ, LTEXT, (void*)"%", 95, 65, 8, 8}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	bool bRet = false;
-	int k, l, n, ic, res;
-	double x, y;
-	AccRange *rY = 0L;
-	LineDEF Line;
-	FillDEF Fill; 
-
-	if(!parent || !data) return false;
-	memcpy(&Line, defs.GetOutLine(), sizeof(LineDEF));
-	memcpy(&Fill, defs.GetFill(), sizeof(FillDEF));
-	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&Fill, 0);
-	UseRangeMark(data, 1, TmpTxt);
-	if(!(Dlg = new DlgRoot(BarDlg, data)))return false;
-	hDlg = CreateDlgWnd("Simple Bar Chart", 50, 50, 370, 280, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 0:
-			if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case 1:
-			if(rY) delete rY;
-			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)){
-				rY = new AccRange(TmpTxt);
-				yRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
-				}
-			else {
-				rY = 0L;		yRange = 0L;
-				}
-			if(!(n = rY ? rY->CountItems() : 0)) {
-				res = -1;
-				ErrorBox("Data range not valid.");
-				}
-			break;
-			}
-	} while(res < 0);
-	if(res == 1 && n && rY) {
-		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&Fill, 0);
-		Command(CMD_FLUSH, 0L, 0L);
-		nPoints = n;
-		Bars = (Bar**)calloc(nPoints, sizeof(Bar*));
-		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-		rY->GetFirst(&k, &l);		rY->GetNext(&k, &l);
-		Dlg->GetValue(201, &start);	Dlg->GetValue(203, &step);
-		if(step < 0.001) step = 1.0;
-		Dlg->GetValue(205, &bw);
-		if(bw < 5) bw = 60;
-		ic = 0;
-		x = start;
-		if(Bars) do {
-			if(data->GetValue(l, k, &y)){
-				Bars[ic] = new Bar(this, data, x, y, BAR_VERTB | BAR_RELWIDTH, -1, -1, k, l);
-				CheckBounds(x, y);
-				x += step;
-				ic++;
-				}
-			}while(rY->GetNext(&k, &l));
-		if(ic){
-			bRet = true;
-			Command(CMD_BAR_FILL, &Fill, 0L);
-			SetColor(COL_BAR_LINE, Line.color);
-			SetSize(SIZE_BAR_LINE, Line.width);
-			SetSize(SIZE_BAR, bw);
-			BarDist.fx = step;
-			}
-		}
-	if(rY) delete rY;
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Scatter plot properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-PlotScatt::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 22, 10, "Data"};
-	TabSHEET tab2 = {22, 50, 10, "Layout"};
-	TabSHEET tab3 = {50, 88, 10, "Error Bars"};
-	TabSHEET tab4 = {88, 131, 10, "Data Labels"};
-	char text1[100], text2[100], text3[100], text4[100];
-	int icon = ICO_INFO;
-	DlgInfo XYDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 140, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 140, 25, 45, 12},
-		{3, 0, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
-		{4, 5, 100, TOUCHEXIT | ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 131, 100},
-		{5, 6, 200, TOUCHEXIT | ISPARENT, SHEET, &tab2, 5, 10, 131, 100},
-		{6, 7, 300, TOUCHEXIT | ISPARENT, SHEET, &tab3, 5, 10, 131, 100},
-		{7, 10, 400, TOUCHEXIT | ISPARENT, SHEET, &tab4, 5, 10, 131, 100},
-		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
-		{100, 101, 0, 0x0L, LTEXT, (void*)"range for X Data", 10, 30, 60, 8},
-		{101, 102, 0, 0x0L, RANGEINPUT, text1, 20, 40, 100, 10},
-		{102, 103, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 55, 60, 8},
-		{103, 104, 0, 0x0L, RANGEINPUT, text2, 20, 65, 100, 10},
-		{104, 105, 0, 0x0L, ICON, (void*)&icon, 10, 85, 10, 10},
-		{105, 106, 0, 0x0L, LTEXT, (void*)"Valid ranges include e.g. \'a1:g13\'", 30, 85, 30, 6},
-		{106, 107, 0, 0x0L, LTEXT, (void*)"or \'b4:j4\' if data are available.", 30, 91, 30, 6},
-		{107, 0, 0, 0x0L, LTEXT, (void*)"Separate multiple ranges by \' ; \'.", 30, 97, 30, 6},
-		{200, 201, 0, 0x0L, CHECKBOX, (void*)" symbols", 25, 30, 60, 8},
-		{201, 202, 250, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{202, 203, 255, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{203, 204, 0, 0x0L, CHECKBOX, (void*)" arrows", 25, 80, 60, 8},
-		{204, 0, 0, 0x0L, CHECKBOX, (void*)" drop lines", 25, 90, 60, 8},
-		{250, 251, 0, ISRADIO, CHECKBOX, (void*)" line", 25, 40, 60, 8},
-		{251, 0, 0, ISRADIO, CHECKBOX, (void*)" polygon", 25, 70, 60, 8},
-		{255, 256, 0, ISRADIO, CHECKBOX, (void*)" vertical bars", 25, 50, 60, 8},
-		{256, 0, 0, ISRADIO, CHECKBOX, (void*)" horizontal bars", 25, 60, 60, 8},
-		{300, 301, 500, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{301, 302, 0, 0x0L, CHECKBOX, (void*)"draw error bars", 15, 28, 50, 8},
-		{302, 303, 0, 0x0L, LTEXT, (void*)"style:", 35, 40, 20, 8},
-		{303, 304, 0, 0x0L, LTEXT, (void*)"range for error data:", 15, 82, 60, 8},
-		{304, 0, 0, 0x0L, RANGEINPUT, text3, 20, 93, 100, 10},
-		{400, 401, 0, 0x0L, CHECKBOX, (void*)"add labels to data points", 15, 28, 50, 8},
-		{401, 402, 0, 0x0L, LTEXT, (void*)"spread sheet range for labels:", 15, 40, 60, 8},
-		{402, 0, 0, 0x0L, RANGEINPUT, text4, 20, 51, 100, 10},
-		{500, 501, 0, TOUCHEXIT|CHECKED|ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl),60,40,20,20},
-		{501, 502, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 80, 40, 20, 20},
-		{502, 503, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 100, 40, 20, 20},
-		{503, 504, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 60, 60, 20, 20},
-		{504, 505, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 80, 60, 20, 20},
-		{505, 0, 0, LASTOBJ | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 100, 60, 20, 20}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int c, i, j, k, l, i1, j1, k1, l1, m, n, o, p, ic;
-	int ErrType = 0, res, BarType, nVals, nTxt, nTime;
-	double x, y, e;
-	lfPOINT fp1, fp2;
-	bool bRet = false, bLayout = false, bContinue = false, bValid;
-	anyResult xRes, yRes;
-	TextDEF lbdef = {defs.Color(COL_TEXT), defs.Color(COL_BG), DefSize(SIZE_TEXT), 0.0f, 0.0f, 0,
-		TXA_HLEFT | TXA_VBOTTOM, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, TmpTxt};
-	AccRange *rX, *rY, *rE, *rL;
-
-	if(!parent || !data) return false;
-	if(Id == GO_BARCHART) return CreateBarChart();
-	UseRangeMark(data, 1, text1, text2, text3, text4);
-	rX = rY = rE = rL = 0L;
-	if(!(Dlg = new DlgRoot(XYDlg, data)))return false;
-#ifdef _WINDOWS
-	for(i = 104; i <= 107; i++) Dlg->TextSize(i, 12);
-#else
-	for(i = 104; i <= 107; i++) Dlg->TextSize(i, 10);
-#endif
-	if(DefSel & 0x01)Dlg->SetCheck(200, 0L, true);		if(DefSel & 0x02)Dlg->SetCheck(250, 0L, true);
-	if(DefSel & 0x04)Dlg->SetCheck(255, 0L, true);		if(DefSel & 0x08)Dlg->SetCheck(256, 0L, true);
-	hDlg = CreateDlgWnd("XY Plot properties", 50, 50, 388, 260, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:								// focus lost
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 500:	case 501:	case 502:
-		case 503:	case 504:	case 505:
-			ErrType = res-500;
-			Dlg->SetCheck(301, 0L, true);
-			res = -1;
-			break;
-		case 4:								// the data tab sheet
-			Dlg->Activate(101, true);	res = -1;				break;
-		case 5:								// the layout tab sheet
-			bLayout = true;				res = -1;				break;
-		case 6:								// the error tab sheet
-			Dlg->Activate(304, true);	res = -1;				break;
-		case 7:								// the label tab sheet
-			Dlg->Activate(402, true);	res = -1;				break;
-		case 1:								// OK
-			if(rX) delete rX;	if(rY) delete rY; if(rE) delete rE;
-			rX = rY = rE = 0L;						// check x-range
-			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) rX = new AccRange(TmpTxt);
-			if(!(n = rX ? rX->CountItems() : 0)) {
-				Dlg->SetCheck(4, 0L, true);
-				res = -1;
-				bContinue = true;
-				ErrorBox("X-range not specified\nor not valid.");
-				}
-			else {							// check y-range
-				if(Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE)) rY = new AccRange(TmpTxt);
-				if(n != (rY ? rY->CountItems() : 0)) {
-					Dlg->SetCheck(4, 0L, true);
-					res = -1;
-					bContinue = true;
-					ErrorBox("Y-range missing\nor not valid.\n"
-						"Size must match X-range.");
-					}
-				}
-			//check for error bar
-			if(res >0 && Dlg->GetCheck(301)) {
-				if(Dlg->GetText(304, TmpTxt, TMP_TXT_SIZE)) rE = new AccRange(TmpTxt);
-				if(n != (rE ? rE->CountItems() : 0)) {
-					Dlg->SetCheck(6, 0L, true);
-					res = -1;
-					bContinue = true;
-					ErrorBox("Range for errors missing\nor not valid.\n"
-						"Size must match X- and Y-range.");
-					if(rE) delete (rE);
-					rE = 0L;
-					}
-				}
-			//check for data labels
-			if(res >0 && Dlg->GetCheck(400)) {
-				if(Dlg->GetText(402, TmpTxt, TMP_TXT_SIZE)) rL = new AccRange(TmpTxt);
-				if(n != (rL ? rL->CountItems() : 0)) {
-					Dlg->SetCheck(7, 0L, true);
-					res = -1;
-					bContinue = true;
-					ErrorBox("Range for labels missing\nor not valid.\n"
-						"Size must match X- and Y-range.");
-					if(rL) delete (rL);
-					rL = 0L;
-					}
-				}
-			//check if something left to do
-			if(res > 0 && !rE && !rL && !Dlg->GetCheck(200) && !Dlg->GetCheck(250) && 
-				!Dlg->GetCheck(251) && !Dlg->GetCheck(255) && !Dlg->GetCheck(256) && 
-				!Dlg->GetCheck(203)) {
-				Dlg->SetCheck(5, 0L, true);
-				res = -1;
-				bContinue = true;
-				ErrorBox("Nothing to do!\nSelect at least one item.");
-				}
-			//the layout menu must have been visited
-			if(res > 0 && !bLayout){
-				Dlg->SetCheck(5, 0L, bLayout = true);
-				res = -1;
-				}
-			break;
-			}
-		}while (res <0);
-	if(res == 1 && n && rX && rY){				//OK pressed
-		Command(CMD_FLUSH, 0L, 0L);
-		nPoints = n;
-		if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) xRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
-		if(Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE)) yRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
-		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-		data_desc = rY->RangeDesc(data, 1);
-		//analyse data types
-		if(rX->DataTypes(data, &nVals, &nTxt, &nTime)){
-			if(!nVals && nTxt > 1 && nTxt > nTime) x_tv = new TextValue();
-			else if(!nVals && nTime > 1 && nTime > nTxt) x_dtype = ET_DATETIME;
-			}
-		if(rY->DataTypes(data, &nVals, &nTxt, &nTime)){
-			if(!nVals && nTxt > 1 && nTxt > nTime) y_tv = new TextValue();
-			else if(!nVals && nTime > 1 && nTime > nTxt) y_dtype = ET_DATETIME;
-			}
-		//Create graphic objects
-		rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
-		rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
-		i1 = i;	j1 = j;	k1 = k;	l1 = l;
-		ic = c = 0;
-		if(Dlg->GetCheck(200)) Symbols = (Symbol**)calloc(nPoints, sizeof(Symbol*));
-		if((BarType = Dlg->GetCheck(255) ? BAR_VERTB : Dlg->GetCheck(256) ? BAR_HORL : 0))
-			Bars = (Bar**)calloc(nPoints, sizeof(Bar*));
-		BarType |= BAR_RELWIDTH;
-		if(Dlg->GetCheck(204)) DropLines = (DropLine**)calloc(nPoints, sizeof(DropLine*));
-		if(Dlg->GetCheck(203)) Arrows = (Arrow**)calloc(nPoints, sizeof(Arrow*));
-		if(Dlg->GetCheck(301) && rE) {					//error bars ?
-			Errors = (ErrorBar**)calloc(nPoints, sizeof(ErrorBar*));
-			if(Dlg->GetText(304, TmpTxt, TMP_TXT_SIZE)){
-				ErrRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
-				rE->GetFirst(&m, &n);	rE->GetNext(&m, &n);
-				}
-			else {
-				rE = 0L;		ErrRange = 0L;
-				}
-			}
-		if(Dlg->GetCheck(400) && rL) {					//labels ?
-			Labels = (Label**)calloc(nPoints, sizeof(Label*));
-			if(Dlg->GetText(402, TmpTxt, TMP_TXT_SIZE)) LbRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
-			rL->GetFirst(&o, &p);	rL->GetNext(&o, &p);
-			}
-		if(Dlg->GetCheck(250) && nPoints >1) TheLine = new DataLine(this, data, xRange, yRange); 
-		else if(Dlg->GetCheck(251) && nPoints >2) TheLine = new DataPolygon(this, data, xRange, yRange); 
-		do {
-			bValid = false;
-			if(data->GetResult(&xRes, j, i, false) && data->GetResult(&yRes, l, k, false)) {
-				bValid = true;
-				if(x_tv) {
-					if(xRes.type == ET_TEXT) x = x_tv->GetValue(xRes.text);
-					else bValid = false;
-					}
-				else if(x_dtype == ET_DATETIME) {
-					if(xRes.type == ET_DATE || xRes.type == ET_TIME  || xRes.type == ET_DATETIME) x = xRes.value;
-					else bValid = false;
-					}
-				else {
-					if(xRes.type == ET_VALUE) x = xRes.value;
-					else bValid = false;
-					}
-				if(y_tv) {
-					if(yRes.type == ET_TEXT) y = y_tv->GetValue(yRes.text);
-					else bValid = false;
-					}
-				else if(y_dtype == ET_DATETIME) {
-					if(yRes.type == ET_DATE || yRes.type == ET_TIME  || yRes.type == ET_DATETIME) y = yRes.value;
-					else bValid = false;
-					}
-				else {
-					if(yRes.type == ET_VALUE) y = yRes.value;
-					else bValid = false;
-					}
-				}
-			if(bValid){
-				if(Symbols && (Symbols[ic] = new Symbol(this, data, x, y, DefSym, i, j, k, l))){
-					Symbols[ic]->idx = c;
-					}
-				if(Bars)Bars[ic] = new Bar(this, data, x, y, BarType, i, j, k, l);
-				if(DropLines) DropLines[ic] = new DropLine(this, data, x, y, 
-					DL_YAXIS | DL_XAXIS, i, j, k, l);
-				if(Arrows) {
-					if(ic){
-						fp1.fx = fp2.fx;	fp1.fy = fp2.fy;
-						}
-					else {
-						fp1.fx = x;			fp1.fy = y;
-						}
-					fp2.fx = x;			fp2.fy = y;
-					Arrows[ic] = new Arrow(this, data, ic ? fp1 : fp2, fp2, ARROW_LINE,
-						i1, j1, k1, l1, i, j, k, l);
-					//the first arrow has zero length
-					//all other arrows conncect to the following point
-					i1 = i;	j1 = j;	k1 = k;	l1 = l;
-					}
-				if(Labels && rL) {
-					if(data->GetText(p, o, TmpTxt, TMP_TXT_SIZE)) 
-						Labels[ic] = new Label(this, data, x, y, &lbdef, 
-							LB_X_DATA | LB_Y_DATA, i, j, k, l, o, p);
-					rL->GetNext(&o, &p);
-					}
-				if(Errors && rE){
-					if(data->GetValue(n, m, &e))
-						Errors[ic]= new ErrorBar(this, data, x, y, e, ErrType,
-						i, j, k, l, m, n);
-					rE->GetNext(&m, &n);
-					}
-				else CheckBounds(x, y);
-				ic++;
-				}
-			else {
-				if(Labels && rL) rL->GetNext(&o, &p);
-				if(Errors && rE) rE->GetNext(&m, &n);
-				}
-			c++;
-			}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
-		if(ic) bRet = true;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(rX) delete rX;	if(rY) delete rY;	if(rE) delete rE;	if(rL) delete rL;
-	return (dirty = bRet);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// calculate means and error to create a xy-plot
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-xyStat::PropertyDlg()
-{
-	char text1[100], text2[100];
-	DlgInfo StatDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 130, 10, 45, 12},
-		{2, 10, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 25, 45, 12},
-		{10, 100, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
-		{100, 101, 0, 0x0L, LTEXT, (void*)"range for grouping variable", 10, 10, 90, 9},
-		{101, 102, 0, 0x0L, LTEXT, (void*)"(X data)", 10, 20, 60, 9},
-		{102, 103, 0, 0x0L, RANGEINPUT, text1, 10, 30, 100, 10},
-		{103, 104, 0, 0x0L, LTEXT, (void*)"range for Y data", 10, 45, 90, 9},
-		{104, 200, 0, 0x0L, RANGEINPUT, text2, 10, 55, 100, 10},
-		{200, 300, 201, ISPARENT | CHECKED, GROUPBOX, (void*) " draw means ", 10, 75, 165, 50},
-		{201, 202, 0, CHECKED, CHECKBOX, (void*)" line", 15, 80, 50, 9},
-		{202, 203, 0, CHECKED, CHECKBOX, (void*)" symbols", 15, 90, 50, 9},
-		{203, 204, 0, 0x0L, CHECKBOX, (void*)" bars", 15, 100, 50, 9},
-		{204, 205, 0, 0x0L, LTEXT, (void*)"using", 65, 90, 30, 9},
-		{205, 206, 0, CHECKED, RADIO1, (void*)" arithmetic mean", 95, 80, 50, 9},
-		{206, 207, 0, 0x0L, RADIO1, (void*)" geometric mean", 95, 90, 50, 9},
-		{207, 208, 0, 0x0L, RADIO1, (void*)" harmonic mean", 95, 100, 50, 9},
-		{208, 0, 0, 0x0L, RADIO1, (void*)" median", 95, 110, 50, 9},
-		{300, 400, 301, ISPARENT | CHECKED, GROUPBOX, (void*) " draw error bars ", 10, 130, 165, 40},
-		{301, 302, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" std. deviation (SD)", 15, 135, 70, 9},
-		{302, 303, 0, ISRADIO, CHECKBOX, (void*)" std. error (SEM)", 15, 145, 70, 9},
-		{303, 304, 0, ISRADIO, CHECKBOX, (void*)" 25, 75% percentiles", 95, 135, 70, 9},
-		{304, 305, 0, ISRADIO, CHECKBOX, (void*)" min and max", 95, 145, 70, 9},
-		{305, 306, 0, ISRADIO, CHECKBOX, (void*)" ", 15, 155, 70, 9},
-		{306, 307, 0, 0x0L, EDVAL1, &ci, 28, 154, 15, 10},
-		{307, 0, 0, 0x0L, LTEXT, (void*) "%  conf. interval", 45, 155, 70, 9},
-		{400, 0, 401, ISPARENT | CHECKED, GROUPBOX, (void*) " number of cases ", 10, 175, 165, 30},
-		{401, 402, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" on top of error", 15, 180, 70, 9},
-		{402, 403, 0, ISRADIO, CHECKBOX, (void*)" on top of mean", 95, 180, 70, 9},
-		{403, 404, 0, 0x0L, LTEXT, (void*)"prefix:", 15, 190, 24, 9},
-		{404, 0, 0, LASTOBJ, EDTEXT, (void*)"n = ", 40, 189, 30, 10}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	bool bRet = false;
-	int i, res, width, height, cb_mdesc;
-	double x, y, e, f, dx, dy;
-	lfPOINT fp1, fp2;
-	char errdesc[40], *mdesc;
-	TextDEF lbdef = {defs.Color(COL_TEXT), defs.Color(COL_BG), DefSize(SIZE_TEXT), 0.0f, 0.0f, 0,
-		TXA_HLEFT | TXA_VBOTTOM, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, TmpTxt};
-
-	if(!parent || !data) return false;
-	UseRangeMark(data, 1, text1, text2);
-	if(!(Dlg = new DlgRoot(StatDlg, data)))return false;
-	text1[0] = text2[0] = 0;
-	hDlg = CreateDlgWnd("Mean and Error Plot", 50, 50, 370, 450, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 0:
-			if(Dlg->GetCheck(10)) res=-1;
-			break;
-		case 1:
-			if(!(Dlg->GetText(102, text1, 100) && Dlg->GetText(104, text2, 100) && text1[0] && text2[0])) res = 2;
-			break;
-			}
-		}while (res <0);
-	if(res == 1) {
-		xRange = (char*)memdup(text1, ((int)strlen(text1))+2, 0);
-		yRange = (char*)memdup(text2, ((int)strlen(text2))+2, 0);
-		type = 0;
-		if(Dlg->GetCheck(201)) type |= 0x0001;		if(Dlg->GetCheck(202)) type |= 0x0002;
-		if(Dlg->GetCheck(203)) type |= 0x0004;
-		if(Dlg->GetCheck(205)) type |= 0x0010;		if(Dlg->GetCheck(206)) type |= 0x0020;
-		if(Dlg->GetCheck(207)) type |= 0x0040;		if(Dlg->GetCheck(208)) type |= 0x0080;
-		if(Dlg->GetCheck(301)) type |= 0x0100;		if(Dlg->GetCheck(302)) type |= 0x0200;
-		if(Dlg->GetCheck(303)) type |= 0x0400;		if(Dlg->GetCheck(304)) type |= 0x0800;
-		if(Dlg->GetCheck(305)) type |= 0x1000;
-		if(Dlg->GetCheck(401)) type |= 0x2000;		if(Dlg->GetCheck(402)) type |= 0x4000;
-		Dlg->GetValue(306, &ci);					TmpTxt[0] = 0;
-		if(Dlg->GetText(404, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0]) case_prefix = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
-		CreateData();
-		if(type && curr_data) {
-			switch (type & 0x00f0) {
-				case 0x0010:		mdesc = "Mean";					break;
-				case 0x0020:		mdesc = "Geometric mean";		break;
-				case 0x0040:		mdesc = "Harmonic mean";		break;
-				case 0x0080:		mdesc = "Median";				break;
-				default:			mdesc = "n.a.";					break;
-				}
-			cb_mdesc = (int)strlen(mdesc);
-			curr_data->GetSize(&width, &height);		nPoints = height;
-#ifdef USE_WIN_SECURE
-			sprintf_s(text1, 100, "a1:a%d", height);	sprintf_s(text2, 100, "b1:b%d", height);
-#else
-			sprintf(text1, "a1:a%d", height);			sprintf(text2, "b1:b%d", height);
-#endif
-			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-			if(type & 0x0001) {
-				if(nPoints >1 && (TheLine = new DataLine(this, curr_data, text1, text2)) &&
-					(TheLine->name = (char*)malloc(cb_mdesc+2))) rlp_strcpy(TheLine->name, cb_mdesc+2, mdesc);
-				}
-			if((type & 0x0002) && (Symbols = (Symbol**)calloc(nPoints, sizeof(Symbol*)))) {
-				for(i = 0; i < height; i++) {
-					if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 1, &y)
-						&& (Symbols[i] = new Symbol(this, curr_data, x, y, DefSym, 0, i, 1, i))){
-						Symbols[i]->idx = i;
-						if(Symbols[i]->name = (char*)malloc(cb_mdesc+2))
-							rlp_strcpy(Symbols[i]->name, cb_mdesc+2, mdesc);
-						}
-					}
-				}
-			if((type & 0x0004) && (Bars = (Bar**)calloc(nPoints, sizeof(Bar*)))) {
-				for(i = 0; i < height; i++) {
-					if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 1, &y)
-						&& (Bars[i] = new Bar(this, curr_data, x, y, BAR_VERTB | BAR_RELWIDTH, 0, i, 1, i))){
-						if(Bars[i]->name = (char*)malloc(cb_mdesc+2))
-							rlp_strcpy(Bars[i]->name, cb_mdesc+2, mdesc);
-						}
-					}
-				}
-			if(type & 0x1f00) {
-				Errors = (ErrorBar**)calloc(nPoints, sizeof(ErrorBar*));
-				switch(type & 0x1f00) {
-				case 0x0100:	rlp_strcpy(errdesc, 40, "Std. Dev.");			break;
-				case 0x0200:	rlp_strcpy(errdesc, 40, "Std. Err.");			break;
-				case 0x0400:	rlp_strcpy(errdesc, 40, "25, 75% Perc.");		break;
-				case 0x0800:	rlp_strcpy(errdesc, 40, "Min./Max.");			break;
-#ifdef USE_WIN_SECURE
-				case 0x1000:	sprintf_s(errdesc, 40, "'%g%% CI", ci);			break;
-#else
-				case 0x1000:	sprintf(errdesc, "'%g%% CI", ci);				break;
-#endif
-				default:		rlp_strcpy(errdesc, 40, "error");
-					}
-				}
-			if((type & 0x1300) && Errors) {
-				for(i = 0; i < height; i++) {
-					if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 1, &y)
-						&& curr_data->GetValue(i, 2, &e)) {
-						Errors[i]= new ErrorBar(this, curr_data, x, y, e, 0, 0, i, 1, i, 2, i);
-						if(Errors[i]) Errors[i]->Command(CMD_ERRDESC, errdesc, 0L);
-						}
-					}
-				}
-			if((type & 0x0c00) && Errors) {
-				for(i = 0; i < height; i++) {
-					if(curr_data->GetValue(i, 0, &x) && curr_data->GetValue(i, 2, &e) && curr_data->GetValue(i, 3, &f)){
-						fp1.fx = fp2.fx = x;	fp1.fy = e;		fp2.fy = f;
-						Errors[i]= (ErrorBar*)new Whisker(this, curr_data, fp1, fp2, 0, 0, i, 2, i, 0, i, 3, i);
-						if(Errors[i]) Errors[i]->Command(CMD_ERRDESC, errdesc, 0L);
-						}
-					}
-				}
-			if((type & 0x6000) && (Labels = (Label**)calloc(nPoints, sizeof(Label*)))) {
-				dy = -0.4 * DefSize(SIZE_SYMBOL);
-				if(type & 0x2000){
-					lbdef.Align = TXA_HCENTER | TXA_VBOTTOM;
-					dx = 0.0;
-					}
-				else {
-					lbdef.Align = TXA_HLEFT | TXA_VBOTTOM;
-					dx = -dy;
-					}
-				for(i = 0; i < height; i++) {
-					if(curr_data->GetValue(i, 0, &x) && curr_data->GetText(i, 5, TmpTxt, TMP_TXT_SIZE, false)){
-						if((type & 0x2000) && curr_data->GetValue(i, 4, &y)) 
-							Labels[i] = new Label(this, curr_data, x, y, &lbdef, 
-							LB_X_DATA | LB_Y_DATA, 0, i, 4, i, 5, i);
-						else if((type & 0x4000) && curr_data->GetValue(i, 1, &y)) 
-							Labels[i] = new Label(this, curr_data, x, y, &lbdef, 
-							LB_X_DATA | LB_Y_DATA, 0, i, 1, i, 5, i);
-						if(Labels[i]){
-							Labels[i]->SetSize(SIZE_LB_YDIST, dy);
-							Labels[i]->SetSize(SIZE_LB_XDIST, dx);
-							}
-						}
-					}
-				}
-			Command(CMD_AUTOSCALE, 0L, 0L);
-			bRet = true;
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Frequency distribution
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *FreqDlg_Tmpl =
-	"1,2,,DEFAULT,PUSHBUTTON,-1,130,10,45,12\n"
-	"2,3,,,PUSHBUTTON,-2,130,25,45,12\n"
-	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"4,5,100,ISPARENT | CHECKED,SHEET,1,5,10,120,153\n"
-	"5,10,200,ISPARENT,SHEET,2,5,10,120,153\n"
-	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-	"100,101,,,LTEXT,3,10,25,60,8\n"
-	"101,102,,,RANGEINPUT,-15,10,38,110,10\n"
-	"102,103,120,ISPARENT | CHECKED,GROUPBOX,4,10,55,110,42\n"
-	"103,,150,ISPARENT | CHECKED,GROUPBOX,5,10,102,110,57\n" 
-	"120,121,,CHECKED,RADIO1,6,15,60,30,9\n"
-	"121,122,,, EDVAL1,7,47,60,15,10\n"
-	"122,123,,, LTEXT,8,64,60,35,8\n"
-	"123,124,,, RADIO1,9, 15, 72, 45, 9\n"
-	"124,125,,, EDTEXT,-16,65,72,50,10\n"
-	"125,126,,, RTEXT,10,15,84, 47,8\n"
-	"126,,,,EDTEXT,-16,65,84,50,10\n"
-	"150,151,,ISRADIO,CHECKBOX,13,15,107,30, 8\n"
-	"151,152,,ISRADIO,CHECKBOX,14,15,117,30, 8\n"
-	"152,153,,ISRADIO,CHECKBOX,15,65,107,30, 8\n"
-	"153,154,,ISRADIO,CHECKBOX,16,65,117,30,8\n"
-	"154,155,,ISRADIO,CHECKBOX,17,15,127,30,8\n"
-	"155,156,,ISRADIO,CHECKBOX,18,15,137,30,8\n"
-	"156,,,ISRADIO,CHECKBOX,19,15,147,30,8\n"
-	"200,, 210,ISPARENT | CHECKED,GROUPBOX,11,10,27,110,61\n" 
-	"210,,,LASTOBJ | NOSELECT,ODBUTTON,12,25,34,90,50";
-
-bool
-FreqDist::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 25, 10, "Data"};
-	TabSHEET tab2 = {25, 52, 10, "Style"};
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)"spread sheet range for values",
-		(void*)" classes ", (void*)" plot distribution ", (void*)"create", (void*)&step,
-		(void*)"classes and bars", (void*)"class size is", (void*)"starting at",
-		(void*)" bar style ", (void*)OD_filldef, (void*)" normal", (void*)" log-normal",
-		(void*)" binomial", (void*)" poisson", (void*)" exponential", (void*)" rectangular",
-		(void*)" chi-square"};
-	DlgInfo *FreqDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	bool bRet = false;
-	int res, r, c;
-	double tmp;
-	char *mrk;
-	AccRange *aR;
-
-	if(!parent || !data) return false;
-	if(!(FreqDlg = CompileDialog(FreqDlg_Tmpl, dyndata))) return false;
-	step = 7;	TmpTxt[100] = 0;
-	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&BarLine, 0);
-	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&BarFill, 0);
-	if(data->Command(CMD_GETMARK, &mrk, 0L)) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, mrk);
-	else TmpTxt[0] = 0;
-	if(!(Dlg = new DlgRoot(FreqDlg, data)))return false;
-	hDlg = CreateDlgWnd("Frequency Distribution", 50, 50, 370, 370, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 0:
-			if(Dlg->GetCheck(10)) res=-1;
-			break;
-		case 1:
-			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) ssRef = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
-			if(Dlg->GetCheck(150)) type = 1;
-			else if(Dlg->GetCheck(151)) type = 2;
-			else if(Dlg->GetCheck(154)) type = 3;
-			else if(Dlg->GetCheck(155)) type = 4;
-			else if(Dlg->GetCheck(156)) type = 5;
-			else if(Dlg->GetCheck(152)) type = 10;
-			else if(Dlg->GetCheck(153)) type = 11;
-			else type = 0;
-			break;
-			}
-		}while (res <0);
-	if(res==1 && (plots = (GraphObj**)calloc(nPlots=3, sizeof(GraphObj*)))) {
-		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&BarLine, 0);
-		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&BarFill, 0);
-		if(BarFill.hatch) memcpy(&HatchLine, BarFill.hatch, sizeof(LineDEF));
-		BarFill.hatch = &HatchLine;
-		if(Dlg->GetCheck(123) && Dlg->GetValue(124, &step) && Dlg->GetValue(126, &start)) ProcData(-2);
-		else {
-			Dlg->GetValue(121, &step);		ProcData(-1);
-			}
-		if(y_info = (char*)malloc(25*sizeof(char))) rlp_strcpy(y_info, 25, "No. of observations");
-		if(x_info = (char*)malloc(25*sizeof(char))){
-			rlp_strcpy(x_info, 25, "Categories");
-			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE) && (aR = new AccRange(TmpTxt))) {
-				if(aR->GetFirst(&c, &r) && !data->GetValue(r, c, &tmp) && data->GetText(r, c, TmpTxt, 150, false))
-					rlp_strcpy(x_info, 25, TmpTxt);
-				delete aR;
-				}
-			}
-		if(plots[0]) bRet = true;
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;		free(FreqDlg);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Regression properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char* RegDlg_Tmpl = 
-	"1,+,,DEFAULT,PUSHBUTTON,-1,140,10,45,12\n"
-	".,.,,,PUSHBUTTON,-2,140,25,45,12\n"
-	".,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"4,+,100,ISPARENT | CHECKED,SHEET,1,5,10,130,85\n"
-	".,10,200,ISPARENT,SHEET,2,5,10,130,85\n"
-	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-	"100,+,,,LTEXT,3,10,22,60,8\n"
-	".,.,,,RANGEINPUT,-16,20,32,100,10\n"
-	".,.,,,LTEXT,4,10,45,60,8\n"
-	".,.,,,RANGEINPUT,-17,20,55,100,10\n"
-	".,.,,CHECKED,CHECKBOX,5,10,70,100,8\n"
-	".,,,,CHECKBOX,6,10,80,100,8\n" 
-	"200,210,201,CHECKED | ISPARENT, GROUPBOX,7,10,30,58,56\n"
-	"201,+,,CHECKED, RADIO1,8,20,40,30,8\n"
-	".,.,,,RADIO1,9,20,50,30,8\n"
-	".,.,,,RADIO1,10,20,60,30,8\n"
-	".,,,,RADIO1,11,20,70,30,8\n"
-	"210,,211,CHECKED | ISPARENT,GROUPBOX,12,72,30,58,56\n"
-	".,+,,CHECKED,RADIO1,13,82,40,30,8\n"
-	".,.,,,RADIO1,14,82,50,30,8\n"
-	".,.,,,RADIO1,15,82,60,30,8\n"
-	".,,,LASTOBJ,RADIO1,16,82,70,30,8";
-
-bool
-Regression::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 28, 10, "Data"};
-	TabSHEET tab2 = {28, 70, 10, "Transform"};
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)"range for X Data",
-		(void*)"range for Y Data", (void*)" include symbols in plot", (void*) " draw SD ellipse",
-		(void*)"  x-values  ", (void*)"x = x", (void*)"x = log(x)", (void*)"x = 1/x",
-		(void*)"x = sqrt(x)", (void*)"  y-values  ", (void*)"y = y", (void*)"y = log(y)",
-		(void*)"y = 1/y",(void*)"y = sqrt(y)"};
-	DlgInfo *RegDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	int c, i, j, k, l, ic, res, n;
-	double x, y;
-	AccRange *rX, *rY;
-	bool bRet = false, bContinue = false, dValid;
-	lfPOINT *values = 0L;
-
-	if(!parent || !data) return false;
-	if(!(RegDlg = CompileDialog(RegDlg_Tmpl, dyndata))) return false;
-	rX = rY = 0L;
-	UseRangeMark(data, 1, TmpTxt+100, TmpTxt+200);
-	if(!(Dlg = new DlgRoot(RegDlg, data)))return false;
-	hDlg = CreateDlgWnd("Linear regression analysis step 1/2", 50, 50, 380, 230, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:								// focus lost
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 1:								// OK
-			if(rX) delete rX;	if(rY) delete rY;
-			rX = rY = 0L;						// check x-range
-			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) rX = new AccRange(TmpTxt);
-			if(!(n = rX ? rX->CountItems() : 0)) {
-				Dlg->SetCheck(4, 0L, true);
-				res = -1;
-				bContinue = true;
-				ErrorBox("X-range not specified\nor not valid.");
-				}
-			else {							// check y-range
-				if(Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE)) rY = new AccRange(TmpTxt);
-				if(n != (rY ? rY->CountItems() : 0)) {
-					res = -1;
-					bContinue = true;
-					ErrorBox("Y-range missing\nor not valid.\n"
-						"Size must match X-range.");
-					}
-				}
-			}
-		}while (res <0);
-	if(res==1 && n && rX && rY && (values =(lfPOINT*)calloc(nPoints=n, sizeof(lfPOINT)))){				//OK pressed
-		Command(CMD_FLUSH, 0L, 0L);
-		if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) xRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
-		if(Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE)) yRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
-		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-		rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
-		rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
-		if(Dlg->GetCheck(202)) type = 0x100;
-		else if(Dlg->GetCheck(203)) type = 0x200;
-		else if(Dlg->GetCheck(204)) type = 0x300;
-		if(Dlg->GetCheck(212)) type |= 0x1000;
-		else if(Dlg->GetCheck(213)) type |= 0x2000;
-		else if(Dlg->GetCheck(214)) type |= 0x3000;
-		ic = c = 0;
-		if(Dlg->GetCheck(104)) Symbols = (Symbol**)calloc(nPoints, sizeof(Symbol*));
-		do {
-			if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y)){
-				dValid = true;
-				switch(type & 0x700) {
-				case 0x100:					//logarithmic x
-					if(dValid = x > defs.min4log) values[ic].fx = log10(x);
-					break;
-				case 0x200:					//reciprocal x
-					if(dValid = fabs(x) >defs.min4log) values[ic].fx = 1.0/x;
-					break;
-				case 0x300:					//square root x
-					if(dValid = fabs(x) >defs.min4log) values[ic].fx = sqrt(x);
-					break;
-				default:	values[ic].fx = x;	break;		//linear x
-					}
-				if(dValid) switch(type & 0x7000) {
-				case 0x1000:				//logarithmic y
-					if(dValid = y > defs.min4log) values[ic].fy = log10(y);
-					break;
-				case 0x2000:				//reciprocal y
-					if(dValid = fabs(y) > defs.min4log) values[ic].fy = 1.0/y;
-					break;
-				case 0x3000:				//square root y
-					if(dValid = fabs(y) > defs.min4log) values[ic].fy = sqrt(y);
-					break;
-				default:	values[ic].fy = y;	break;		//linear y
-					}
-				if(dValid && Symbols && (Symbols[ic] = new Symbol(this, data, x, y, 
-					SYM_CIRCLE, i, j, k, l))){
-					Symbols[ic]->idx = c;
-					}
-				if(dValid) {
-					CheckBounds(x, y);
-					ic++;
-					}
-				}
-			c++;
-			}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
-		if(ic) {
-			if(Dlg->GetCheck(105) && (sde = new SDellipse(this, data, values, ic, type | 0x20002))&&
-				(bRet= sde->Command(CMD_INIT, 0L, 0L)))sde->Command(CMD_BOUNDS, &Bounds, 0L);
-			else if((rLine = new RegLine(this, data, values, ic, type)) && 
-				(bRet= rLine->PropertyDlg()))rLine->Command(CMD_BOUNDS, &Bounds, 0L);
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(rX) delete rX;	if(rY) delete rY;
-	free(RegDlg);		if(values) free(values);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Bubble plot properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-BubblePlot::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 40, 10, "Data"};
-	TabSHEET tab2 = {40, 85, 10, "Layout"};
-	TabSHEET tab3 = {85, 130, 10, "Scaling"};
-	char text1[100], text2[100], text3[100];
-	int syms[] = {SYM_CIRCLE, SYM_RECT, SYM_TRIAU, SYM_TRIAD};
-	FillDEF ShowFill;
-	DlgInfo PlotDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 148, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 148, 25, 45, 12},
-		{3, 500, 4, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
-		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 130, 120},
-		{5, 6, 200, ISPARENT, SHEET, &tab2, 5, 10, 130, 120},
-		{6, 10, 300, ISPARENT, SHEET, &tab3, 5, 10, 130, 120},
-		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
-		{100, 101, 0, 0x0L, LTEXT, (void*)"range for X Data", 10, 40, 60, 8},
-		{101, 102, 0, 0x0L, RANGEINPUT, text1, 20, 50, 100, 10},
-		{102, 103, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 65, 60, 8},
-		{103, 104, 0, 0x0L, RANGEINPUT, text2, 20, 75, 100, 10},
-		{104, 105, 0, 0x0L, LTEXT, (void*)"range for sizes", 10, 90, 60, 8},
-		{105, 0, 0, 0x0L, RANGEINPUT, text3, 20, 100, 100, 10},
-		{200, 201, 0, 0x0L, LTEXT, (void*)"Select one of the following shapes", 10, 30, 110, 8},
-		{201, 202, 0, CHECKED, SYMRADIO, (void *)&syms[0], 30, 40, 20, 20},
-		{202, 203, 0, 0x0L, SYMRADIO, (void *)&syms[1], 50, 40, 20, 20},
-		{203, 204, 0, 0x0L, SYMRADIO, (void *)&syms[2], 70, 40, 20, 20},
-		{204, 205, 0, 0x0L, SYMRADIO, (void *)&syms[3], 90, 40, 20, 20},
-		{205, 206, 0, 0x0L, LTEXT, (void*)"outline:", 7, 67, 45, 8},
-		{206, 207, 0, 0x0L, RTEXT, (void*)"color", 7, 75, 20, 8},
-		{207, 208, 0, OWNDIALOG, COLBUTT, (void *)&BubbleLine.color, 29, 75, 25, 10},
-		{208, 209, 0, 0x0L, RTEXT, (void*)"line width", 67, 75, 20, 8},
-		{209, 210, 0, 0x0L, EDVAL1, &BubbleLine.width, 88, 75, 25, 10},
-		{210, 211, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 114, 75, 15, 8},
-		{211, 212, 0, 0x0L, LTEXT, (void*)"fill:", 7, 97, 45, 8},
-		{212, 213, 0, 0x0L, RTEXT, (void*)"color", 7, 105, 20, 8},
-		{213, 214, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&BubbleFill.color, 29, 105, 25, 10},
-		{214, 215, 0, 0x0L, RTEXT, (void*)"pattern", 67, 105, 20, 8},
-		{215, 0, 0, TOUCHEXIT | OWNDIALOG, FILLBUTTON, (void *)&ShowFill, 88, 105, 25, 10},
-		{300, 301, 0, 0x0L, LTEXT, (void*)"Sizes are given as", 10, 30, 110, 8},
-		{301, 302, 400, ISPARENT | CHECKED, GROUP, NULL, 0, 0, 0, 0},
-		{302, 303, 0, 0x0L, LTEXT, (void*)"Proportionality (relative to circle)", 10, 75, 110, 8},
-		{303, 0, 410, ISPARENT | CHECKED, GROUP, NULL, 0, 0, 0, 0},
-		{400, 401, 0, CHECKED, RADIO1, (void *) Units[defs.cUnits].display, 40, 40, 45, 8},
-		{401, 402, 0, 0x0L, RADIO1, (void*)"scaling with X axis", 40, 50, 45, 8},
-		{402, 0, 0, 0x0L, RADIO1, (void*)"scaling with Y axis", 40, 60, 45, 8},
-		{410, 411, 0, CHECKED, RADIO1, (void*)"diameter", 40, 85, 45, 8},
-		{411, 412, 0, 0x0L, RADIO1, (void*)"circumference", 40, 95, 45, 8},
-		{412, 0, 0, LASTOBJ, RADIO1, (void*)"area", 40, 105, 45, 8}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, j, k, l, m, n, ic, res, BubbleType;
-	double x, y, s;
-	double tmp;
-	bool bRetVal = false, bFillChanged, bContinue = false;
-	AccRange *rX, *rY, *rS;
-	LineDEF ShowFillLine ={0.2f, 1.0f, 0x0L, 0x0L};
-
-	if(!parent || !data) return false;
-	UseRangeMark(data, 1, text1, text2, text3);
-	memcpy(&ShowFill, &BubbleFill, sizeof(FillDEF));
-	if(BubbleFill.hatch) memcpy(&ShowFillLine, BubbleFill.hatch, sizeof(LineDEF));
-	ShowFill.hatch = &ShowFillLine;
-	if(!(Dlg = new DlgRoot(PlotDlg, data)))return false;
-	hDlg = CreateDlgWnd("Create Bubble Plot", 50, 50, 400, 300, Dlg, 0x0L);
-	rX = rY = rS = 0L;
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:								// focus lost
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 213:					//fillcolor changed
-			Dlg->GetColor(213, &ShowFill.color);
-			Dlg->DoPlot(NULL);
-			res = -1;
-			break;
-		case 215:					//copy color from pattern dialog
-			Dlg->SetColor(213, ShowFill.color);
-			bFillChanged = true;
-			res = -1;
-			break;
-		case 1:						//OK button
-			if(Dlg->GetText(101, text1, 100) && Dlg->GetText(103, text2, 100) && 
-				Dlg->GetText(105, text3, 100) && (rX = new AccRange(text1)) &&
-				(rY = new AccRange(text2)) && (rS = new AccRange(text3))) {
-				if((i = rX->CountItems()) == rY->CountItems() && i == rS->CountItems()){
-					// OK pressed and ranges checked: exit loop and process data
-					}
-				else {
-					if(rX) delete (rX);	if(rY) delete (rY);	if(rS) delete (rS);
-					rX = rY = rS = 0L;
-					ErrorBox("Ranges must be of equal size");
-					Dlg->SetCheck(4, 0L, bContinue = true);
-					res = -1;
-					}
-				}
-			else res = -1;				//continue with dialog if error
-			}
-		}while (res <0);
-	if(res ==1 && rX && rY && rS && (nPoints = rX->CountItems()) && 
-		(Bubbles = (Bubble**)calloc(nPoints, sizeof(Bubble*)))) {
-		//accept settings and create bubbles for plot
-		if(Dlg->GetCheck(202)) BubbleType = BUBBLE_SQUARE;
-		else if(Dlg->GetCheck(203)) BubbleType = BUBBLE_UPTRIA;
-		else if(Dlg->GetCheck(204)) BubbleType = BUBBLE_DOWNTRIA;
-		else BubbleType = BUBBLE_CIRCLE;
-		if(Dlg->GetCheck(401)) BubbleType |= BUBBLE_XAXIS;
-		else if(Dlg->GetCheck(402)) BubbleType |= BUBBLE_YAXIS;
-		if(Dlg->GetCheck(411)) BubbleType |= BUBBLE_CIRCUM;
-		else if(Dlg->GetCheck(412)) BubbleType |= BUBBLE_AREA;
-		if(Dlg->GetValue(209, &tmp)) BubbleLine.width = (float)tmp;
-		Dlg->GetColor(207, &BubbleLine.color);		Dlg->GetColor(213, &BubbleFill.color);
-		rX->GetFirst(&i, &j);		rY->GetFirst(&k, &l);	rS->GetFirst(&m, &n);
-		rX->GetNext(&i, &j);		rY->GetNext(&k, &l);	rS->GetNext(&m, &n);
-		ic = 0;
-		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-		if(Bubbles) do {
-			if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y) && data->GetValue(n, m, &s)){
-				CheckBounds(x, y);
-				Bubbles[ic++] = new Bubble(this, data, x, y, s, BubbleType, &ShowFill, 
-					&BubbleLine, i, j, k, l, m, n);
-				}
-			}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l) && rS->GetNext(&m, &n));
-		bRetVal = ic >0;
-		}
-	CloseDlgWnd(hDlg);
-	if(rX) delete (rX);		if(rY) delete (rY);		if(rS) delete (rS);
-	delete Dlg;
-	return bRetVal;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Polar plot properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *AddPolDlg_Tmpl = 
-	"1,2,,DEFAULT,PUSHBUTTON,-1,140,14,45,12\n"
-	"2,3,,,PUSHBUTTON,-2,140,29,45,12\n"
-	"3,10,200,ISPARENT | CHECKED,GROUPBOX,1,5,14,131,96\n"
-	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-	"200,201,,CHECKED | EXRADIO,ODBUTTON,2,10,24,20,20\n"
-	"201,202,,EXRADIO,ODBUTTON,2,30,24,20,20\n"
-	"202,203,,EXRADIO,ODBUTTON,2,50,24,20,20\n"
-	"203,204,,EXRADIO,ODBUTTON,2,70,24,20,20\n"
-	"204,210,,EXRADIO,ODBUTTON,2,90,24,20,20\n"
-	"210,211,,,LTEXT,3,10,50,50,8\n"
-	"211,212,,,RANGEINPUT,-15,20,62,100,10\n"
-	"212,213,,,LTEXT,4,10,75,50,8\n"
-	"213,,,LASTOBJ,RANGEINPUT,-16,20,87,100,10";
-
-bool
-PolarPlot::AddPlot()
-{
-	void *dyndata[] = {(void*)" select template and data range ", (void*)OD_PolarTempl,
-		(void*)"range for x-data (circular or angular data)",
-		(void*)"range for y-data (radial data)"};
-	DlgInfo *PolDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, j, k, l, ic, n, res, cType = 200;
-	bool bRet = false, bContinue = false;
-	double x, y;
-	AccRange *rX = 0L, *rY = 0L;
-	Symbol **Symbols = 0L;
-	DataLine *TheLine = 0L;
-	Plot **tmpPlots;
-	Function *func;
-	anyOutput *cdisp = Undo.cdisp;
-
-	if(!parent || !data) return false;
-	if(!(PolDlg = CompileDialog(AddPolDlg_Tmpl, dyndata))) return false;
-	UseRangeMark(data, 1, TmpTxt, TmpTxt+100);
-	if(!(Dlg = new DlgRoot(PolDlg, data)))return false;
-	hDlg = CreateDlgWnd("Add Polar Plot", 50, 50, 388, 260, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:								// focus lost
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 200: case 201:	case 202:	case 203:	case 204:
-			if(res == 204) {
-				Dlg->Activate(211, false);			Dlg->Activate(213, false);
-				}
-			else if(cType == 204) {
-				Dlg->Activate(211, true);			Dlg->Activate(213, true);
-				}
-			if(res == cType) res = 1;
-			else {
-				cType = res;
-				res = -1;
-				}
-			break;
-			}
-		}while (res <0);
-	if(res == 1 && Dlg->GetText(211, TmpTxt, 100) && Dlg->GetText(213, TmpTxt+100, 100) && 
-		(rX = new AccRange(TmpTxt)) && (rY = new AccRange(TmpTxt+100)) &&
-		(n = rX ? rX->CountItems() : 0) && 
-		(tmpPlots = (Plot**)realloc(Plots, (nPlots+2)*sizeof(Plot*)))) {
-		Undo.SetDisp(cdisp);
-		Plots = tmpPlots;
-		if(Dlg->GetCheck(200) || Dlg->GetCheck(201)) 
-			Symbols = (Symbol**) calloc(n+1, sizeof(Symbol*));
-		if(Dlg->GetCheck(201) || Dlg->GetCheck(202)) 
-			TheLine = new DataLine(this, data, TmpTxt, TmpTxt+100);
-		else if(Dlg->GetCheck(203))
-			TheLine = new DataPolygon(this, data, TmpTxt, TmpTxt+100);
-		else if(Dlg->GetCheck(204)) {
-			if(func = new Function(this, data, "Function")){
-				if(bRet = func->PropertyDlg()){
-					Undo.SetGO(this, (GraphObj**) &Plots[nPlots++], func, 0L);
-					memcpy(&Bounds, &Plots[nPlots-1]->Bounds, sizeof(fRECT));
-					}
-				else DeleteGO(func);
-				}
-			}
-		rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
-		rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
-		ic = 0;
-		if(Symbols || TheLine) {
-			do {
-				if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y)){
-					CheckBounds(y, y);
-					if(Symbols)Symbols[ic++] = new Symbol(this,data,x,y,SYM_CIRCLE,i,j,k,l);
-					}
-				}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
-			Undo.SetGO(this, (GraphObj**) &Plots[nPlots++], 
-				new PlotScatt(this, data, ic, Symbols, TheLine), 0L);
-			bRet = true;
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;			free(PolDlg);
-	if(rX) delete rX;	if(rY) delete rY;
-	return bRet;
-}
-
-bool
-PolarPlot::Config()
-{
-	TabSHEET tab1 = {0, 40, 10, "Plot"};
-	DlgInfo PPDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 102, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 102, 25, 45, 12},
-		{3, 0, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 90, 90},
-		{100, 101, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 8, 30, 90, 50},
-		{101, 0, 0, LASTOBJ, CHECKBOX, (void*)"show radial axis", 15, 85, 60, 9}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res;
-	bool bRet = false;
-	LineDEF OutLine;
-
-	memcpy(&OutLine, defs.GetOutLine(), sizeof(LineDEF));
-	if(Axes && Axes[0]) {
-		OutLine.color = Axes[0]->GetColor(COL_AXIS);
-		OutLine.width = Axes[0]->GetSize(SIZE_AXIS_LINE);
-		}
-	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&OutLine, 0);
-	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&Fill, 0);
-	Dlg = new DlgRoot(PPDlg, data);
-	if(!(type & 0x01))Dlg->SetCheck(101, 0L, true);
-	hDlg = CreateDlgWnd("Polar Plot properties", 50, 50, 310, 234, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		}while (res < 0);
-	if(res == 1){						//OK pressed
-		if(Dlg->GetCheck(101)) type &= ~0x01;
-		else type |= 0x01;
-		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&OutLine, 0);
-		if(Axes && Axes[0]) {
-			Axes[0]->SetColor(COL_AXIS, OutLine.color);
-			Axes[0]->SetSize(SIZE_AXIS_LINE, OutLine.width);
-			}
-		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&Fill, 0);
-		if(Fill.hatch) memcpy(&FillLine, Fill.hatch, sizeof(LineDEF));
-		Fill.hatch = &FillLine;
-		bRet = true;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-bool
-PolarPlot::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 45, 10, "Coordinates"};
-	TabSHEET tab2 = {45, 70, 10, "Type"};
-	char text1[100], text2[100];
-	double lox = 0.0, hix = 360.0, fcx =10.0, fcy = 20.0, frad=40.0;
-	DlgInfo PolDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 140, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 140, 25, 45, 12},
-		{3, 50, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
-		{4, 5, 100, ISPARENT | TOUCHEXIT, SHEET, &tab1, 5, 10, 131, 100},
-		{5, 10, 200, ISPARENT | TOUCHEXIT | CHECKED, SHEET, &tab2, 5, 10, 131, 100},
-		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
-		{100, 101, 0, 0x0L, LTEXT, (void*)"angular range (full circle)", 10, 25, 60, 8},
-		{101, 102, 0, 0x0L, RTEXT, (void*)"min =", 5, 37, 25, 8},
-		{102, 103, 0, 0x0L, EDVAL1, &lox, 30, 37, 30, 10},
-		{103, 104, 0, 0x0L, RTEXT, (void*)"max =", 60, 37, 25, 8},
-		{104, 105, 0, 0x0L, EDVAL1, &hix, 85, 37, 30, 10},
-		{105, 106, 0, 0x0L, RTEXT, (void*)"angular offset:", 10, 49, 50, 8},
-		{106, 107, 0, 0x0L, EDVAL1, &offs, 62, 49, 30, 10},
-		{107, 108, 0, 0x0L, LTEXT, (void*)"position of center:", 10, 65, 40, 8},
-		{108, 109, 0, 0x0L, RTEXT, (void*)"x =", 5, 77, 25, 8},
-		{109, 110, 0, 0x0L, EDVAL1, &fcx, 30, 77, 30, 10},
-		{110, 111, 0, 0x0L, RTEXT, (void*)"y =", 60, 77, 25, 8},
-		{111, 112, 0, 0x0L, EDVAL1, &fcy, 85, 77, 30, 10},
-		{112, 113, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 117, 77, 15, 8}, 
-		{113, 114, 0, 0x0L, EDVAL1, &frad, 62, 89, 30, 10},
-		{114, 115, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 94, 89, 15, 8},
-		{115, 0, 0, 0x0L, RTEXT, (void*)"radius:", 10, 89, 50, 8},
-		{200, 201, 0, CHECKED | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 10, 25, 20, 20},
-		{201, 202, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 30, 25, 20, 20},
-		{202, 203, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 50, 25, 20, 20},
-		{203, 204, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 70, 25, 20, 20},
-		{204, 210, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 90, 25, 20, 20},
-		{210, 211, 0, 0x0L, LTEXT, (void*)"range for x-data (circular or angular data)", 10, 55, 50, 8},
-		{211, 212, 0, 0x0L, RANGEINPUT, (void*)text1, 20, 67, 100, 10},
-		{212, 213, 0, 0x0L, LTEXT, (void*)"range for y-data (radial data)", 10, 80, 50, 8},
-		{213, 0, 0, LASTOBJ, RANGEINPUT, (void*)text2, 20, 92, 100, 10}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, i, j, k, l, n, ic, cType = 200;
-	double x, y;
-	bool bRet = false, bType = false;
-	AccRange *rX = 0L, *rY = 0L;
-	Symbol **Symbols = 0L;
-	DataLine *TheLine = 0L;
-	TextDEF tlbdef;
-	AxisDEF ang_axis, rad_axis;
-
-	if(!parent || !data) return false;
-	if(Plots) return Config();
-	frad = (parent->GetSize(SIZE_DRECT_BOTTOM) - parent->GetSize(SIZE_DRECT_TOP))/2.0f;
-	fcx = parent->GetSize(SIZE_GRECT_LEFT) + parent->GetSize(SIZE_DRECT_LEFT)*1.5 + frad;
-	fcy = parent->GetSize(SIZE_GRECT_TOP) + parent->GetSize(SIZE_DRECT_TOP) + frad;
-	UseRangeMark(data, 1, text1, text2);
-	tlbdef.ColTxt = defs.Color(COL_AXIS);
-	tlbdef.ColBg = 0x00ffffffL;
-	tlbdef.RotBL = tlbdef.RotCHAR = 0.0f;
-	tlbdef.fSize = DefSize(SIZE_TICK_LABELS);
-	tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
-	tlbdef.Style = TXS_NORMAL;
-	tlbdef.Mode = TXM_TRANSPARENT;
-	tlbdef.Font = FONT_HELVETICA;
-	tlbdef.text = 0L;
-	if(!(Dlg = new DlgRoot(PolDlg, data)))return false;
-	hDlg = CreateDlgWnd("Create Polar Plot", 50, 50, 388, 260, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case 5:		case 4:
-			bType = true;
-			res = -1;
-			break;
-		case 1:
-			if(!bType) {		//the 'Coordinates' sheet must have been visited
-				bType = true;
-				Dlg->SetCheck(4, 0L, true);
-				res = -1;
-				}
-			break;
-		case 200: case 201:	case 202:	case 203:	case 204:
-			if(res == 204) {
-				Dlg->Activate(211, false);			Dlg->Activate(213, false);
-				}
-			else if(cType == 204) {
-				Dlg->Activate(211, true);			Dlg->Activate(213, true);
-				}
-			if(res == cType) res = 1;
-			else {
-				cType = res;
-				res = -1;
-				}
-			break;
-			}
-		}while (res <0);
-	if(res == 1) {
-		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-		//set axis information in ang_axis and rad_axis
-		ang_axis.owner = rad_axis.owner = 0L;
-		ang_axis.breaks = rad_axis.breaks = 0L;
-		ang_axis.nBreaks = rad_axis.nBreaks = 0;
-		ang_axis.flags = AXIS_ANGULAR;	
-		rad_axis.flags = AXIS_RADIAL | AXIS_DEFRECT;
-		Dlg->GetValue(109, &ang_axis.Center.fx);	
-		rad_axis.Center.fx = ang_axis.Center.fx;
-		Dlg->GetValue(111, &ang_axis.Center.fy);	
-		rad_axis.Center.fy = ang_axis.Center.fy;
-		Dlg->GetValue(113, &ang_axis.Radius);
-		rad_axis.Radius = ang_axis.Radius;
-		Dlg->GetValue(102, &ang_axis.min);
-		Dlg->GetValue(104, &ang_axis.max);
-		Dlg->GetValue(106, &offs);
-		ang_axis.loc[0].fy = ang_axis.loc[1].fy = ang_axis.Center.fy + ang_axis.Radius;
-		ang_axis.loc[0].fx = ang_axis.Center.fx - ang_axis.Radius;
-		ang_axis.loc[1].fx = ang_axis.Center.fx + ang_axis.Radius;
-		rad_axis.loc[0].fx = rad_axis.loc[1].fx = 
-			parent->GetSize(SIZE_GRECT_LEFT) + parent->GetSize(SIZE_DRECT_LEFT);
-		rad_axis.loc[0].fy = rad_axis.Center.fy - rad_axis.Radius;
-		rad_axis.loc[1].fy = rad_axis.Center.fy;
-		if(Dlg->GetText(211, text1, 100) && Dlg->GetText(213, text2, 100) && 
-			(rX = new AccRange(text1)) && (rY = new AccRange(text2)) &&
-			(n = rX ? rX->CountItems() : 0) && (Plots = (Plot**)calloc(2, sizeof(Plot*)))) {
-			if(Dlg->GetCheck(200) || Dlg->GetCheck(201)) 
-				Symbols = (Symbol**) calloc(n+1, sizeof(Symbol*));
-			if(Dlg->GetCheck(201) || Dlg->GetCheck(202)) 
-				TheLine = new DataLine(this, data, text1, text2);
-			else if(Dlg->GetCheck(203))
-				TheLine = new DataPolygon(this, data, text1, text2);
-			else if(Dlg->GetCheck(204)) {
-				if(Plots[nPlots++] = new Function(this, data, "Function")){
-					if(bRet = Plots[nPlots-1]->PropertyDlg())
-						memcpy(&Bounds, &Plots[nPlots-1]->Bounds, sizeof(fRECT));
-					else {
-						DeleteGO(Plots[nPlots-1]);
-						Plots[nPlots-1] = 0L;
-						}
-					}
-				}
-			rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
-			rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
-			ic = 0;
-			if(Symbols || TheLine) do {
-				if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y)){
-					CheckBounds(y, y);
-					if(Symbols) Symbols[ic++] = new Symbol(this, data, x, y, 
-						SYM_CIRCLE, i, j, k, l);
-					}
-				}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
-			rad_axis.min = Bounds.Ymin;			rad_axis.max = Bounds.Ymax;
-			NiceAxis(&rad_axis, 4);
-			ang_axis.Start = ang_axis.Step = 0.0;
-			if(Symbols || TheLine) 
-				Plots[nPlots++] = new PlotScatt(this, data, ic, Symbols, TheLine);
-			if(Plots[0] && (Axes = (GraphObj**)calloc(3, sizeof(Axis*)))){
-				Axes[0] = new Axis(this, data, &ang_axis, ang_axis.flags);
-				Axes[1] = new Axis(this, data, &rad_axis, 
-					rad_axis.flags | AXIS_AUTOTICK | AXIS_NEGTICKS);
-				Axes[1]->SetSize(SIZE_LB_XDIST, 
-					NiceValue(-DefSize(SIZE_AXIS_TICKS)*6.0)); 
-				Axes[1]->SetSize(SIZE_TLB_XDIST, 
-					NiceValue(-DefSize(SIZE_AXIS_TICKS)*2.0)); 
-				Axes[1]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-				nAxes = 2;
-				bRet = true;
-				}
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(rX) delete rX;	if(rY) delete rY;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Box plot properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static int boxplot_mode_sel = 52;
-bool
-BoxPlot::PropertyDlg()
-{
-	DlgInfo PlotDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 130, 10, 45, 12},
-		{2, 10, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 25, 45, 12},
-		{10, 50, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
-		{50, 60, 51, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{51, 52, 0, 0x0L, LTEXT, (void*)"Data Source:", 10, 12, 40, 9},
-		{52, 53, 0, TOUCHEXIT, RADIO2, (void*)" user values", 60, 12, 50, 9},
-		{53, 0, 0, TOUCHEXIT, RADIO2, (void*)" statistical data", 60, 22, 60, 9},
-		{60, 61, 100, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{61, 0, 200, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{100, 102, 0, 0x0L, LTEXT, (void*)"range for grouping variable (X data)", 10, 39, 140, 9},
-		{102, 103, 0, 0x0L, RANGEINPUT, TmpTxt+100, 10, 49, 165, 10},
-		{103, 104, 0, 0x0L, LTEXT, (void*)"range for Y data", 10, 60, 90, 9},
-		{104, 150, 0, 0x0L, RANGEINPUT, TmpTxt+200, 10, 70, 165, 10},
-		{150, 160, 151, ISPARENT | CHECKED, GROUPBOX, (void*) " draw means ", 10, 87, 165, 45},
-		{151, 152, 0, 0x0L, CHECKBOX, (void*)" line", 15, 92, 50, 9},
-		{152, 153, 0, CHECKED, CHECKBOX, (void*)" symbols", 15, 101, 50, 9},
-		{153, 154, 0, 0x0L, LTEXT, (void*)"using", 65, 101, 30, 9},
-		{154, 155, 0, 0x0L, RADIO1, (void*)" arithmetic mean", 95, 90, 50, 9},
-		{155, 156, 0, 0x0L, RADIO1, (void*)" geometric mean", 95, 99, 50, 9},
-		{156, 157, 0, 0x0L, RADIO1, (void*)" harmonic mean", 95, 108, 50, 9},
-		{157, 0, 0, CHECKED, RADIO1, (void*)" median", 95, 117, 50, 9},
-		{160, 170, 161, ISPARENT | CHECKED, GROUPBOX, (void*) " draw boxes ", 10, 137, 165, 38},
-		{161, 162, 0, ISRADIO, CHECKBOX, (void*)" std. deviation (SD)", 15, 142, 70, 9},
-		{162, 163, 0, ISRADIO, CHECKBOX, (void*)" std. error (SEM)", 15, 151, 70, 9},
-		{163, 164, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" 25, 75% percentiles", 95, 142, 70, 9},
-		{164, 165, 0, ISRADIO, CHECKBOX, (void*)" min and max", 95, 151, 70, 9},
-		{165, 166, 0, ISRADIO, CHECKBOX, (void*)" ", 15, 161, 70, 9},
-		{166, 167, 0, 0x0L, EDVAL1, &ci_box, 28, 160, 15, 10},
-		{167, 0, 0, 0x0L, LTEXT, (void*) "%  conf. interval", 45, 161, 70, 9},
-		{170, 400, 171, ISPARENT | CHECKED, GROUPBOX, (void*) " draw whiskers ", 10, 180, 165, 38},
-		{171, 172, 0, ISRADIO, CHECKBOX, (void*)" std. deviation (SD)", 15, 185, 70, 9},
-		{172, 173, 0, ISRADIO, CHECKBOX, (void*)" std. error (SEM)", 15, 194, 70, 9},
-		{173, 174, 0, ISRADIO, CHECKBOX, (void*)" 25, 75% percentiles", 95, 185, 70, 9},
-		{174, 175, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" min and max", 95, 194, 70, 9},
-		{175, 176, 0, ISRADIO, CHECKBOX, (void*)" ", 15, 204, 70, 9},
-		{176, 177, 0, 0x0L, EDVAL1, &ci_err, 28, 203, 15, 10},
-		{177, 0, 0, 0x0L, LTEXT, (void*) "%  conf. interval", 45, 203, 70, 9},
-		{200, 202, 0, 0x0L, LTEXT, (void*)"range for common X values", 10, 39, 140, 9},
-		{202, 250, 0, 0x0L, RANGEINPUT, TmpTxt+100, 10, 49, 165, 10},
-		{250, 260, 251, ISPARENT | CHECKED, GROUPBOX, (void*) "                        ", 10, 68, 165, 30},
-		{251, 252, 0, 0x0L, CHECKBOX, (void*)" draw line", 15, 63, 50, 9},
-		{252, 253, 0, 0x0L, LTEXT, (void*)"range for line values", 15, 73, 80, 9},
-		{253, 0, 0, 0x0L, RANGEINPUT, TmpTxt+200, 15, 83, 155, 10},
-		{260, 270, 261, ISPARENT | CHECKED, GROUPBOX, (void*) "                               ", 10, 106, 165, 30},
-		{261, 262, 0, CHECKED, CHECKBOX, (void*)" draw symbols", 15, 101, 50, 9},
-		{262, 263, 0, 0x0L, LTEXT, (void*)"range for symbol values", 15, 111, 80, 9},
-		{263, 0, 0, 0x0L, RANGEINPUT, TmpTxt+200, 15, 121, 155, 10},
-		{270, 280, 271, ISPARENT | CHECKED, GROUPBOX, (void*) "                          ", 10, 144, 165, 50},
-		{271, 272, 0, CHECKED, CHECKBOX, (void*)" draw boxes", 15, 139, 50, 9},
-		{272, 273, 0, 0x0L, LTEXT, (void*)"range for HI values", 15, 149, 80, 9},
-		{273, 274, 0, 0x0L, RANGEINPUT, TmpTxt+300, 15, 159, 155, 10},
-		{274, 275, 0, 0x0L, LTEXT, (void*)"range for LO values", 15, 169, 80, 9},
-		{275, 0, 0, 0x0L, RANGEINPUT, TmpTxt+400, 15, 179, 155, 10},
-		{280, 0, 281, ISPARENT | CHECKED, GROUPBOX, (void*) "                              ", 10, 202, 165, 50},
-		{281, 282, 0, CHECKED, CHECKBOX, (void*)" draw whiskers", 15, 197, 50, 9},
-		{282, 283, 0, 0x0L, LTEXT, (void*)"range for HI values", 15, 207, 80, 9},
-		{283, 284, 0, 0x0L, RANGEINPUT, TmpTxt+500, 15, 217, 155, 10},
-		{284, 285, 0, 0x0L, LTEXT, (void*)"range for LO values", 15, 227, 80, 9},
-		{285, 0, 0, 0x0L, RANGEINPUT, TmpTxt+600, 15, 237, 155, 10},
-		{400, 0, 401, ISPARENT | CHECKED, GROUPBOX, (void*) " number of cases ", 10, 223, 165, 30},
-		{401, 402, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" on top of error", 15, 228, 70, 9},
-		{402, 403, 0, ISRADIO, CHECKBOX, (void*)" on top of mean", 95, 228, 70, 9},
-		{403, 404, 0, 0x0L, LTEXT, (void*)"prefix:", 15, 238, 24, 9},
-		{404, 0, 0, LASTOBJ, EDTEXT, (void*)"n = ", 40, 237, 30, 10}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	bool bRet = false;
-	int i, j, k, k1, l, l1, n, ic, c, res, cb, width, height;
-	double x, y1, y2, dx, dy;
-	char errdesc[40], boxdesc[40], symdesc[40];
-	lfPOINT fp1, fp2;
-	TextDEF lbdef = {defs.Color(COL_TEXT), defs.Color(COL_BG), DefSize(SIZE_TEXT), 0.0f, 0.0f, 0,
-		TXA_HLEFT | TXA_VBOTTOM, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, TmpTxt};
-	AccRange *rX = 0L, *rY1 = 0L, *rY2 = 0L;
-
-	if(!parent || !data) return false;
-	UseRangeMark(data, 1, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400, TmpTxt+500, TmpTxt+600);
-	ci_box = ci_err = 95.0;
-	if(!(Dlg = new DlgRoot(PlotDlg, data)))return false;
-	TmpTxt[0] = TmpTxt[100] = 0;
-	//restore previous style
-	if(boxplot_mode_sel == 53) {
-		Dlg->ShowItem(61, false);			Dlg->SetCheck(53, 0L, true);
-		}
-	else {
-		Dlg->SetCheck(52, 0L, true);		Dlg->ShowItem(60, false);
-		}
-	hDlg = CreateDlgWnd("Box and Whisker Plot", 50, 50, 370, 550, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 0:
-			if(Dlg->GetCheck(10)) res=-1;
-			break;
-		case 52:	case 53:
-			boxplot_mode_sel = res;
-			if(res == 53) {
-				Dlg->ShowItem(60, true);	Dlg->ShowItem(61, false);
-				}
-			else {
-				Dlg->ShowItem(60, false);	Dlg->ShowItem(61, true);
-				}
-			Dlg->Command(CMD_REDRAW, 0L, 0L);	res=-1;
-			break;
-			}
-		}while (res <0);
-	if(res == 1) {
-		type = 0;				dirty = true;
-		if(Dlg->GetCheck(52) && Dlg->GetText(202, TmpTxt+100, 50) && TmpTxt[100] &&(rX = new AccRange(TmpTxt+100))) {
-			xRange = (char*)memdup(TmpTxt+100, ((int)strlen(TmpTxt+100))+2, 0);
-			n = rX->CountItems();	nPoints = n;
-			//  data line
-			if(n > 1 && Dlg->GetCheck(251) && Dlg->GetText(253, TmpTxt, TMP_TXT_SIZE)) {
-				TheLine = new DataLine(this, data, TmpTxt+100, TmpTxt);
-				bRet = true;
-				}
-			// symbols
-			if(n > 0 && Dlg->GetCheck(261) && Dlg->GetText(263, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0] 
-				&& (Symbols = (Symbol**)calloc(n, sizeof(Symbol*)))
-				&& (rY1 = new AccRange(TmpTxt))) {
-				yRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
-				rX->GetFirst(&i, &j);	rY1->GetFirst(&k, &l);
-				rX->GetNext(&i, &j);	rY1->GetNext(&k, &l);
-				ic = c = 0;
-				do {
-					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1)) {
-						if(Symbols[ic] = new Symbol(this, data, x, y1, SYM_PLUS, i, j, k, l))
-							Symbols[ic++]->idx = c;
-						}
-					c++;
-					}while(rX->GetNext(&i, &j) && rY1->GetNext(&k, &l));
-				delete rY1;		rY1 = 0L;
-				if(ic) bRet = true;
-				}
-			// boxes
-			if(n > 0 && Dlg->GetCheck(271) && Dlg->GetText(273, TmpTxt+300, 50) && Dlg->GetText(275, TmpTxt+400, 50)
-				&& (Boxes = (Box**)calloc(n, sizeof(Box*)))
-				&& (rY1 = new AccRange(TmpTxt+300)) && (rY2 = new AccRange(TmpTxt+400))) {
-				rX->GetFirst(&i, &j);	rY1->GetFirst(&k, &l);	rY2->GetFirst(&k1, &l1);
-				rX->GetNext(&i, &j);	rY1->GetNext(&k, &l);	rY2->GetNext(&k1, &l1);
-				ic = 0;
-				do {
-					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1) && data->GetValue(l1, k1, &y2)) {
-						fp1.fy = y1;	fp2.fy = y2;		fp1.fx = fp2.fx = x;
-						Boxes[ic] = new Box(this, data, fp1, fp2, BAR_RELWIDTH, i, j, k, l, i, j, k1, l1);
-						if(Boxes[ic]) Boxes[ic++]->SetSize(SIZE_BOX, 60.0);
-						}
-					}while(rX->GetNext(&i, &j) && rY1->GetNext(&k, &l) && rY2->GetNext(&k1, &l1));
-				delete rY1;		rY1 = 0L;		delete rY2;		rY2 = 0L;
-				if(ic) bRet = true;
-				}
-			// whiskers
-			if(n > 0 && Dlg->GetCheck(281) && Dlg->GetText(283, TmpTxt+300, 50) && Dlg->GetText(285, TmpTxt+400, 50)
-				&& (Whiskers = (Whisker**)calloc(n, sizeof(Whisker*)))
-				&& (rY1 = new AccRange(TmpTxt+300)) && (rY2 = new AccRange(TmpTxt+400))) {
-				rX->GetFirst(&i, &j);	rY1->GetFirst(&k, &l);	rY2->GetFirst(&k1, &l1);
-				rX->GetNext(&i, &j);	rY1->GetNext(&k, &l);	rY2->GetNext(&k1, &l1);
-				ic = 0;
-				do {
-					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1) && data->GetValue(l1, k1, &y2)) {
-						fp1.fy = y1;	fp2.fy = y2;		fp1.fx = fp2.fx = x;
-						Whiskers[ic++] = new Whisker(this, data, fp1, fp2, 0, i, j, k, l, i, j, k1, l1);
-						}
-					}while(rX->GetNext(&i, &j) && rY1->GetNext(&k, &l) && rY2->GetNext(&k1, &l1));
-				delete rY1;		rY1 = 0L;		delete rY2;		rY2 = 0L;
-				if(ic) bRet = true;
-				}
-			if (bRet) Command(CMD_AUTOSCALE, 0L, 0L);
-			}
-		else if(Dlg->GetText(102, TmpTxt+100, 50) && TmpTxt[100] && Dlg->GetText(104, TmpTxt+200, 50) && TmpTxt[200]){
-			xRange = (char*)memdup(TmpTxt+100, ((int)strlen(TmpTxt+100))+2, 0);
-			yRange = (char*)memdup(TmpTxt+200, ((int)strlen(TmpTxt+200))+2, 0);
-			if((rX = new AccRange(xRange)) && (rY1 = new AccRange(yRange))) {
-				x_info = rX->RangeDesc(data, 2);		y_info = rY1->RangeDesc(data, 2);
-				delete rX;		delete rY1;		rX = rY1 = 0L;
-				}
-			if(Dlg->GetCheck(154)) type |= 0x0001;		if(Dlg->GetCheck(155)) type |= 0x0002;
-			if(Dlg->GetCheck(156)) type |= 0x0003;		if(Dlg->GetCheck(157)) type |= 0x0004;
-			if(Dlg->GetCheck(161)) type |= 0x0010;		if(Dlg->GetCheck(162)) type |= 0x0020;
-			if(Dlg->GetCheck(163)) type |= 0x0030;		if(Dlg->GetCheck(164)) type |= 0x0040;
-			if(Dlg->GetCheck(165)) type |= 0x0050;
-			if(Dlg->GetCheck(171)) type |= 0x0100;		if(Dlg->GetCheck(172)) type |= 0x0200;
-			if(Dlg->GetCheck(173)) type |= 0x0300;		if(Dlg->GetCheck(174)) type |= 0x0400;
-			if(Dlg->GetCheck(175)) type |= 0x0500;
-			if(Dlg->GetCheck(151)) type |= 0x1000;		if(Dlg->GetCheck(152)) type |= 0x2000;
-			if(Dlg->GetCheck(401)) type |= 0x4000;		if(Dlg->GetCheck(402)) type |= 0x8000;
-			Dlg->GetValue(166, &ci_box);				Dlg->GetValue(176, &ci_err);
-			if(Dlg->GetText(404, TmpTxt, TMP_TXT_SIZE)) case_prefix = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
-			CreateData();
-			if(curr_data && type) {
-				curr_data->GetSize(&width, &height);
-#ifdef USE_WIN_SECURE
-				sprintf_s(TmpTxt+100, 50, "a1:a%d", height);	sprintf_s(TmpTxt+200, 50, "b1:b%d", height);
-#else	
-				sprintf(TmpTxt+100, "a1:a%d", height);			sprintf(TmpTxt+200, "b1:b%d", height);
-#endif
-				nPoints = height;
-				if(nPoints > 1 && (type & 0x1000)) {
-					TheLine = new DataLine(this, curr_data, TmpTxt+100, TmpTxt+200);
-					bRet = true;
-					}
-				if(nPoints > 0 && (type & 0x2000) && (Symbols = (Symbol**)calloc(nPoints, sizeof(Symbol*)))) {
-					switch(type & 0x000f) {
-					case 0x0001:	cb = rlp_strcpy(symdesc, 40, "Mean");				break;
-					case 0x0002:	cb = rlp_strcpy(symdesc, 40, "Geometric mean");		break;
-					case 0x0003:	cb = rlp_strcpy(symdesc, 40, "Harmonic mean");		break;
-					case 0x0004:	cb = rlp_strcpy(symdesc, 40, "Median");				break;
-					default:		cb = rlp_strcpy(symdesc, 40, "n.a.");				break;
-						}
-					for(i = 0; i < height; i++) {
-						if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 1, &y1)
-							&& (Symbols[i] = new Symbol(this, curr_data, x, y1, SYM_PLUS, 0, i, 1, i))){
-							Symbols[i]->idx = i;
-							Symbols[i]->name = (char*)memdup(symdesc, cb+1, 0);
-							}
-						}
-					bRet = true;
-					}
-				if(nPoints > 0 && (type & 0x00f0) && (Boxes = (Box**)calloc(nPoints, sizeof(Box*)))) {
-					switch(type & 0x00f0) {
-					case 0x0010:	cb = rlp_strcpy(boxdesc, 40, "Std. Dev.");			break;
-					case 0x0020:	cb = rlp_strcpy(boxdesc, 40, "Std. Err.");			break;
-					case 0x0030:	cb = rlp_strcpy(boxdesc, 40, "25, 75% Perc.");		break;
-					case 0x0040:	cb = rlp_strcpy(boxdesc, 40, "Min./Max.");			break;
-#ifdef USE_WIN_SECURE
-					case 0x0500:	cb = sprintf_s(boxdesc, 40, "'%g%% CI", ci_err);	break;
-#else
-					case 0x0500:	cb = sprintf(boxdesc, "'%g%% CI", ci_err);			break;
-#endif
-					default:		cb = rlp_strcpy(boxdesc, 40, "n.a.");
-						}
-					for(i = 0; i < height; i++) {
-						if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 2, &y1)
-							&&	curr_data->GetValue(i, 3, &y2)) {
-							fp1.fy = y1;	fp2.fy = y2;		fp1.fx = fp2.fx = x;
-							Boxes[i] = new Box(this, curr_data, fp1, fp2, BAR_RELWIDTH, 0, i, 2, i, 0, i, 3, i);
-							if(Boxes[i]){
-								Boxes[i]->SetSize(SIZE_BOX, 60.0);
-								Boxes[i]->name = (char*)memdup(boxdesc, cb+1, 0);
-								}
-							}
-						}
-					bRet = true;
-					}
-				if(nPoints > 0 && (type & 0x0f00) && (Whiskers = (Whisker**)calloc(nPoints, sizeof(Whisker*)))) {
-					switch(type & 0x0f00) {
-					case 0x0100:	rlp_strcpy(errdesc, 40, "Std. Dev.");			break;
-					case 0x0200:	rlp_strcpy(errdesc, 40, "Std. Err.");			break;
-					case 0x0300:	rlp_strcpy(errdesc, 40, "25, 75% Perc.");		break;
-					case 0x0400:	rlp_strcpy(errdesc, 40, "Min./Max.");			break;
-#ifdef USE_WIN_SECURE
-					case 0x0500:	sprintf_s(errdesc, 40, "'%g%% CI", ci_err);		break;
-#else
-					case 0x0500:	sprintf(errdesc, "'%g%% CI", ci_err);			break;
-#endif
-					default:		rlp_strcpy(errdesc, 40, "error");
-						}
-					for(i = 0; i < height; i++) {
-						if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 4, &y1)
-							&&	curr_data->GetValue(i, 5, &y2)) {
-							fp1.fy = y1;	fp2.fy = y2;		fp1.fx = fp2.fx = x;
-							Whiskers[i] = new Whisker(this, curr_data, fp1, fp2, 0, 0, i, 4, i, 0, i, 5, i);
-							if(Whiskers[i]) Whiskers[i]->Command(CMD_ERRDESC, errdesc, 0L);
-							}
-						}
-					bRet = true;
-					}
-				if(nPoints > 0 && (type & 0xc000) && (Labels = (Label**)calloc(nPoints, sizeof(Label*)))) {
-					dy = -0.4 * DefSize(SIZE_SYMBOL);
-					if(type & 0x4000){
-						lbdef.Align = TXA_HCENTER | TXA_VBOTTOM;
-						dx = 0.0;
-						}
-					else {
-						lbdef.Align = TXA_HLEFT | TXA_VBOTTOM;
-						dx = -dy;
-						}
-					for(i = 0; i < height; i++) {
-						if(curr_data->GetValue(i, 0, &x) && curr_data->GetText(i, 7, TmpTxt, TMP_TXT_SIZE)){
-							if(curr_data->GetValue(i, 6, &y1))Labels[i] = new Label(this, curr_data, x, y1, &lbdef, 
-								LB_X_DATA | LB_Y_DATA, 0, i, 6, i, 7, i);
-							if(Labels[i]){
-								Labels[i]->SetSize(SIZE_LB_YDIST, dy);	Labels[i]->SetSize(SIZE_LB_XDIST, dx);
-								}
-							}
-						}
-					bRet = true;
-					}
-				}
-			}
-		}
-	if(bRet) {
-		dirty = true;
-		Command(CMD_AUTOSCALE, 0L, 0L);
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(rX) delete rX;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create density distribution plot 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-DensDisp::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 25, 10, "Data"};
-	TabSHEET tab2 = {25, 52, 10, "Style"};
-	char text1[100], text2[100];
-	DlgInfo PlotDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 148, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 148, 25, 45, 12},
-		{3, 0, 4, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
-		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 130, 100},
-		{5, 10, 200, TOUCHEXIT | ISPARENT, SHEET, &tab2, 5, 10, 130, 100},
-		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
-		{100, 101, 0, 0x0L, LTEXT, (void*)"range for direction (time, depth) data", 10, 30, 60, 8},
-		{101, 102, 0, 0x0L, RANGEINPUT, text1, 20, 40, 100, 10},
-		{102, 103, 0, 0x0L, LTEXT, (void*)"range for width (density) data", 10, 55, 60, 8},
-		{103, 104, 0, 0x0L, RANGEINPUT, text2, 20, 65, 100, 10},
-		{104, 0, 0, 0x0L, CHECKBOX, (void*)"vertical profile", 10, 90, 100, 8},
-		{200, 201, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 25, 30, 90, 45},
-		{201, 202, 0, TOUCHEXIT | CHECKED, RADIO1, (void*)"symmetric bars", 25, 80, 60, 8},
-		{202, 203, 0, TOUCHEXIT, RADIO1, 0L, 25, 88, 60, 8},
-		{203, 0, 0, TOUCHEXIT | LASTOBJ, RADIO1, 0L, 25, 96, 60, 9}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int n, res, align = 0;
-	bool bRet = false, bContinue = false, bVert;
-	AccRange *rX = 0L, *rY = 0L;
-
-	if(!parent || !data) return false;
-	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)defs.GetOutLine(), 0);
-	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)defs.GetFill(), 0);
-	UseRangeMark(data, 1, text1, text2);
-	if(!(Dlg = new DlgRoot(PlotDlg, data)))return false;
-	hDlg = CreateDlgWnd("Density profile", 50, 50, 420, 260, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 5:
-			if(Dlg->GetCheck(104)) {
-				Dlg->SetText(202, "bars right");	Dlg->SetText(203, "bars left");
-				}
-			else {
-				Dlg->SetText(202, "bars up");		Dlg->SetText(203, "bars down");
-				}
-			res = -1;
-			break;
-		case 201:	case 202:	case 203:
-			align = res - 201;
-			res = -1;
-			break;
-		case 1:
-			if(rX) delete rX;	if(rY) delete rY;
-			rX = rY = 0L;
-			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) rX = new AccRange(TmpTxt);
-			n = rX ? rX->CountItems() : 0;
-			if(!n) {
-				ErrorBox("direction range not specified\nor not valid.");
-				bContinue = true;
-				res = -1;
-				}
-			if(n && Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE) && (rY = new AccRange(TmpTxt))){
-				if(n != rY->CountItems()) {
-					ErrorBox("both ranges must be given\nand must have the same size");
-					bContinue = true;
-					res = -1;
-					}
-				}
-			}
-		}while (res < 0);
-	if(res == 1 && n && rX && rY) {
-		if(Dlg->GetCheck(104)) {
-			y_info = rX->RangeDesc(data, 0);		x_info = rY->RangeDesc(data, 0);
-			}
-		else {
-			x_info = rX->RangeDesc(data, 0);		y_info = rY->RangeDesc(data, 0);
-			}
-		type = (bVert = Dlg->GetCheck(104)) ? align | 0x10 : align;
-		if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) xRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
-		if(Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE)) yRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
-		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&DefLine, 0);
-		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&DefFill, 0);
-		if(DefFill.hatch) memcpy(&DefFillLine, DefFill.hatch, sizeof(LineDEF));
-		DefFill.hatch = &DefFillLine;
-		DoUpdate();
-		bRet = true;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(rX) delete rX;		if(rY) delete rY;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create stacked bar or stacked polygon 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-static char *StackBar_DlgTmpl =
-		"1,2,,DEFAULT,PUSHBUTTON,-1,158,10,45,12\n"
-		"2,3,,,PUSHBUTTON,-2,158,25,45,12\n"
-		"3,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"10,11,100,ISPARENT | CHECKED, SHEET,1,5,10,140,100\n"
-		"11,12,200,ISPARENT,SHEET,2,5,10,140,100\n"
-		"12,20,300,ISPARENT,SHEET,3,5,10,140,100\n"
-		"20,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-		"100,101,,,LTEXT,4,15,30,60,8\n"
-		"101,152,,,RANGEINPUT,5,25,40,100,10\n"
-		"152,153,,ISPARENT | CHECKED,GROUPBOX,6, 12, 60, 128, 45\n"
-		"153,154,,,LTEXT,0,25,65,60,8\n"
-		"154,155,,,RANGEINPUT,5,25,75,100,10\n"
-		"155,156,0,,PUSHBUTTON,-8,95,87,30,12\n"
-		"156,,,,PUSHBUTTON,-9,60,87,35,12\n"
-		"200,201,,CHECKED,RADIO1,7,25,35,60,8\n"
-		"201,202,,,RADIO1,8,25,50,60,8\n"
-		"202,203,,,RTEXT,9,31,65,38,8\n"
-		"203,204,,,EDVAL1,10,70,65,30,10\n"
-		"204,,,,CHECKBOX,11,25,90,60,8\n"
-		"300,,,LASTOBJ | NOSELECT, ODBUTTON,12,20,35,80,60";
-
-bool
-StackBar::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 25, 10, "Data"};
-	TabSHEET tab2 = {25, 55, 10, "Details"};
-	TabSHEET tab3 = {55, 90, 10, "Scheme"};
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)"range for common x values",
-		(void*)TmpTxt, (void*)" ranges for y values ", (void*)" add each y to start value",
-		(void*)" subtract each y from start value", (void*)"start value:", (void*)&StartVal,
-		(void*)" horizontal plot", (void*)(OD_scheme)};
-	DlgInfo *StackBarDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, sc, j, res, currYR = 0, maxYR = 0, nx = 0, ny, c_num, c_txt, c_datetime;
-	bool updateYR = true, bContinue = false, bSub, bRet = false, bHor;
-	char **rd = 0L, *rname;
-	AccRange *rX = 0L, *rY = 0L;
-	TextValue *tv = 0L;
-
-	if(!parent || !data) return false;
-	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
-		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return false;
-	if(!(StackBarDlg = CompileDialog(StackBar_DlgTmpl, dyndata))) return false;
-	if(TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
-		for(i=100, j= 0; i <= 1000; i +=100) if(TmpTxt[i]) 
-			rd[j++] = (char*)memdup(TmpTxt+i, ((int)strlen(TmpTxt+i))+2, 0);	 maxYR = j-1;
-		}
-	if(!rd && !(rd = (char**)calloc(1, sizeof(char*))))return false;
-	if(!(Dlg = new DlgRoot(StackBarDlg, data))) return false;
-	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
-	hDlg = CreateDlgWnd(Id == GO_STACKBAR ? (char*)"Stacked Bar Plot" : 
-		(char*)"Stacked Polygons", 50, 50, 420, 260, Dlg, 0x0L);
-	do {
-		if(updateYR) {
-			if(currYR >0) {
-				Dlg->ShowItem(156, true);				Dlg->Activate(101, false);
-				}
-			else {
-				Dlg->ShowItem(156, false);				Dlg->Activate(101, true);
-				}
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "y-values # %d/%d", currYR+1, maxYR+1);
-#else
-			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
-#endif
-			//SetText will also cause a redraw of the whole dialog
-			Dlg->SetText(153, TmpTxt);
-			updateYR = false;
-			}
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		ny = 0;
-		if(rX) delete rX;		rX = 0L;
-		switch(res) {
-		case 0:
-			if(bContinue || Dlg->GetCheck(20)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 1:		
-			Dlg->GetValue(203, &StartVal);
-			bSub = Dlg->GetCheck(201);			bHor = Dlg->GetCheck(204);
-		case 155:		case 156:
-			res = com_StackDlg(res, Dlg, &rX, &nx, &rd, &currYR,
-				&rY, &bContinue, &ny, &maxYR, &updateYR);
-			break;
-			}
-		}while (res < 0);
-	if(res == 1 && nx && rX && rd && rd[0] && rd[0][0]){	//accept settings and create plot
-		maxYR++;
-		for(i = j = 0; i < maxYR; i++) {
-			if(i) TmpTxt[j++] = '&';
-			j+= rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, rd[i]);
-			}
-		rX->DataTypes(data, &c_num, &c_txt, &c_datetime);
-		if(!c_num && (c_txt + c_datetime) > 0 ) tv = new TextValue();
-		if(Dlg->GetCheck(204)) {
-			y_info = rX->RangeDesc(data, 3);	y_tv = tv;
-			}
-		else {
-			x_info = rX->RangeDesc(data, 3);	x_tv = tv;
-			}
-		ssYrange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+1, 0);
-		if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) ssXrange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+1, 0);;
-		cum_data_mode = Dlg->GetCheck(200) ? 1 : 2;
-		if(Id == GO_STACKPG) cum_data_mode += 2;
-		CumData = CreaCumData(ssXrange, ssYrange, cum_data_mode, StartVal);
-		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-		//do stacked bar
-		if(rY)delete rY;	rY = 0L;
-		if(Id == GO_STACKBAR){
-			numPlots = maxYR;
-			if(Boxes = (BoxPlot**)calloc(numPlots, sizeof(BoxPlot*))) 
-				for(i = sc = 0; i < (maxYR) && rd[i] && *rd[i]; i++) {
-					rY = new AccRange(rd[i]);				rname = rY->RangeDesc(data, 3);
-					if(Boxes[i]= new BoxPlot(0L, CumData, Dlg->GetCheck(204)? 2:1, 0, i+1, i+2, rname)){
-					Boxes[i]->Command(CMD_UPDATE, 0L, 0L);	Boxes[i]->parent = this;
-					Boxes[i]->Command(CMD_AUTOSCALE, 0L, 0L);
-					Boxes[i]->Command(CMD_BOX_FILL, GetSchemeFill(&sc), 0L);
-					Boxes[i]->SetSize(SIZE_BOX, 60.0);
-					if(rname) free(rname);	delete rY;	rY = 0L;
-					}
-				}
-			}
-		//do stacked polygon
-		else if(Id == GO_STACKPG){
-			numPG = maxYR;
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, 20, "a1:a%d", nx*2);
-#else
-			sprintf(TmpTxt, "a1:a%d", nx*2);
-#endif
-			if(Polygons=(DataPolygon**)calloc(numPG,sizeof(DataPolygon*)))
-				for(i=sc=0; i < maxYR && rd[i] && *rd[i];i++){
-#ifdef USE_WIN_SECURE
-				sprintf_s(TmpTxt+20, 20, "%c1:%c%d", 'c'+i, 'c'+i, nx*2);
-#else
-				sprintf(TmpTxt+20, "%c1:%c%d", 'c'+i, 'c'+i, nx*2);
-#endif
-				rY = new AccRange(rd[i]);				rname = rY->RangeDesc(data, 3);
-				if(Dlg->GetCheck(204)) Polygons[i]=new DataPolygon(this,CumData,TmpTxt+20,TmpTxt, rname);
-				else Polygons[i] = new DataPolygon(this, CumData, TmpTxt, TmpTxt+20, rname);
-				if(Polygons[i]) {
-					Polygons[i]->Command(CMD_AUTOSCALE, 0L, 0L);
-					Polygons[i]->Command(CMD_PG_FILL, GetSchemeFill(&sc), 0L);
-					}
-				if(rname) free(rname);	delete rY;	rY = 0L;
-				}
-			}
-		if(Bounds.Xmax >= Bounds.Xmin && Bounds.Ymax >= Bounds.Ymin) bRet = true;
-		else bRet = false;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(rd) {
-		for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
-		free(rd);
-		}
-	if(rX) delete rX;		if(rY) delete rY;		free(StackBarDlg);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create grouped bars chart
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *GBDlg_Tmpl =
-	"1,+,,DEFAULT,PUSHBUTTON,-1,140,10,45,12\n"
-	".,.,,,PUSHBUTTON,-2,140,25,45,12\n"
-	".,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"4,+,99,ISPARENT | CHECKED | TOUCHEXIT,SHEET,1,5,10,128,100\n"
-	".,.,200,ISPARENT,SHEET,2,5,10,128,100\n"
-	".,.,300,ISPARENT,SHEET,3,5,10,128,100\n"
-	".,10,250,ISPARENT | TOUCHEXIT,SHEET,16,5,10,128,100\n"
-	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-	"99,100,110,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"100,,160,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"101,+,,,LTEXT,0,25,39,60,8\n"
-	".,.,,,RANGEINPUT,-15,25,49,100,10\n"
-	".,.,,,LTEXT,0,25,61,60,8\n"
-	".,.,,,RANGEINPUT,-16,25,71,100,10\n"
-	".,.,,,PUSHBUTTON,-8,95,87,30,12\n"
-	".,,,,PUSHBUTTON,-9,60,87,35,12\n"
-	"110,+,,,LTEXT,4,10,25,60,8\n"
-	".,150,,,LTEXT,5,10,33,60,8\n"
-	"150,,153,ISPARENT | CHECKED,GROUPBOX,6,10,50,118,55\n"
-	"160,101,,ISPARENT | CHECKED,GROUPBOX,19,10,30,118,75\n"
-	"153,+,,,LTEXT,0,15,60,60,8\n"
-	".,.,,,RANGEINPUT,-1,15,70,108,10\n"
-	".,.,,,PUSHBUTTON,-8,93,87,30,12\n"
-	".,,,,PUSHBUTTON,-9,58,87,35,12\n"
-	"200,+,,,RTEXT,7,10,35,38,8\n"
-	".,.,,,EDVAL1,8,58,35,35,10\n"
-	".,.,,,RTEXT,9,10,50,38,8\n"
-	".,.,,,EDVAL1,10,58,50,35,10\n"
-	".,.,,,RTEXT,11,10,65,38,8\n"
-	".,.,,,EDVAL1,12,58,65,35,10\n"
-	".,.,,,LTEXT,-10,95,65,8,8\n"
-	".,.,,,RTEXT,13,10,80,38,8\n"
-	".,.,,,EDVAL1,14,58,80,35,10\n"
-	".,,,,LTEXT,-10,95,80,8,8\n"
-	"250,+,TOUCHEXIT,CHECKED,RADIO1,17,20,35,70,8\n"
-	".,.,,TOUCHEXIT,RADIO1,18,20,50,70,8\n"
-	".,,,,RANGEINPUT,0,15,65,108,10\n"
-	"300,,,LASTOBJ | NOSELECT,ODBUTTON,15,24,30,90,60";
-
-bool
-GroupBars::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 27, 10, "Data"};
-	TabSHEET tab2 = {27, 59, 10, "Details"};
-	TabSHEET tab3 = {59, 96, 10, "Scheme"};
-	TabSHEET tab4 = {96, 128, 10, "Labels"};
-	double start = 1.0, step = 1.0, bw = 100.0, gg = 100.0;
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)"Get values from spreadsheet:",
-		(void*)"All ranges should have equal size!", (void*)" ranges for y values ", (void*)"start value",
-		(void*)&start, (void*)"group step", (void*)&step, (void*)"bar width", (void*)&bw,
-		(void*)"group gap", (void*)&gg, (void*)(OD_scheme), (void*)&tab4, (void*)" no group labels",
-		(void*)"from spreadsheet range:", (void*)" ranges for y- and error- data "};
-	DlgInfo *GBDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	bool bRet = false, updateYR = true, bContinue = false, bError;
-	char **rd=0L, **rdx=0L, **rdy=0L, *desc;
-	Bar **bars = 0L;
-	ErrorBar **errs = 0L;
-	AccRange *rX = 0L, *rY = 0L;
-	anyResult ares;
-	int i, j, ic, res, ix, iy, ex, ey, ny, s1, s2, sc = 0, currYR = 0, maxYR = 0;
-	double x, y, xinc, e, ebw;
-	
-	if(!parent || !data) return false;
-	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
-		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return false;
-	if(!(GBDlg = CompileDialog(GBDlg_Tmpl, dyndata)))return false;
-	if(mode == 0 && TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
-		for(i=0, j= 0; i <= 1000; i +=100) 
-			if(TmpTxt[i]) rd[j++] = (char*)memdup(TmpTxt+i, ((int)strlen(TmpTxt+i))+2, 0);
-			maxYR = j-1;
-		}
-	else if(mode == 1 && TmpTxt[0] && TmpTxt[100] && (rdx = (char**)calloc(12, sizeof(char*))) 
-		&& (rdy = (char**)calloc(12, sizeof(char*)))) {
-		for(i=j=0; i <= 1000; i +=200) if(TmpTxt[i]) {
-			rdx[j] = (char*)memdup(TmpTxt+i, (int)strlen(TmpTxt)+1, 0);	
-			rdy[j] = (char*)memdup(TmpTxt+i+100, (int)strlen(TmpTxt+i+100)+1, 0);		maxYR = j++;
-			}
-		}
-	Id = GO_STACKBAR;
-	if(mode == 0 && !rd && !(rd = (char**)calloc(1, sizeof(char*))))return false;
-	if(mode == 1 && !rdx && !rdy && !(rdx = (char**)calloc(1, sizeof(char*))) 
-		&& !(rdy = (char**)calloc(1, sizeof(char*))))return false;
-	if(!(Dlg = new DlgRoot(GBDlg, data)))return false;
-	if(mode == 1) {
-		Dlg->ShowItem(99, false);		Dlg->ShowItem(100, true);
-		}
-	else {
-		Dlg->ShowItem(99, true);		Dlg->ShowItem(100, false);
-		}
-	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
-	else Dlg->SetText(154, "");
-	hDlg = CreateDlgWnd(mode == 0 ? (char*)"Grouped Bar Chart" : (char*)"Grouped Bar Chart with Error Bars", 50, 50, 390, 260, Dlg, 0x0L);
-	do {
-		if(updateYR) {
-			if(currYR >0) {
-				Dlg->ShowItem(156, true);		Dlg->ShowItem(106, true);
-				}
-			else {
-				Dlg->ShowItem(156, false);		Dlg->ShowItem(106, false);		
-				}
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, 20, "y-values # %d/%d", currYR+1, maxYR+1);
-			sprintf_s(TmpTxt+100, 20, "errors # %d/%d", currYR+1, maxYR+1);
-#else
-			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
-			sprintf(TmpTxt+100,"errors # %d/%d", currYR+1, maxYR+1);
-#endif
-			//SetText will also cause a redraw of the whole dialog
-			if(mode == 0) 	Dlg->SetText(153, TmpTxt);
-			else {
-				Dlg->SetText(101, TmpTxt);		Dlg->SetText(103, TmpTxt+100);
-				}
-			updateYR = false;
-			}
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(bContinue || Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 250:		case 251:
-		case 7:
-			Dlg->Activate(252, true);
-			res = -1;			break;
-		case 4:
-			Dlg->Activate(mode ? 102:154, true);
-			res = -1;			break;
-		case 1:		
-			Dlg->GetValue(201, &start);			Dlg->GetValue(203, &step);
-			Dlg->GetValue(205, &bw);			Dlg->GetValue(208, &gg);
-			res = com_StackDlg(res, Dlg, 0L, 0L, &rd, &currYR,
-				&rY, &bContinue, &ny, &maxYR, &updateYR);
-			if(!mode) break;
-		case 105:										//next button
-			bError=false;	s1 = s2 = 0;
-			if(Dlg->GetText(102, TmpTxt, 100)) {
-				if(rX = new AccRange(TmpTxt)) {
-					s1 = rX->CountItems();
-					if(s1 < 2) {
-						ErrorBox("y-range not valid");
-						bContinue=bError=true;
-						Dlg->Activate(102, true);
-						}
-					delete rX;
-					}
-				else bError = true;
-				}
-			else bError = true;
-			if(Dlg->GetText(104, TmpTxt+100, 100) && !bError) {
-				if(rY = new AccRange(TmpTxt+100)) {
-					s2 = rY->CountItems();
-					if(s2 < 2) {
-						ErrorBox("error-range not valid");
-						bContinue=bError=true;
-						Dlg->Activate(104, true);
-						}
-					delete rY;
-					}
-				else bError = true;
-				}
-			else {
-				Dlg->Activate(104, true);
-				bError = true;
-				}
-			if(!s1 || !s2) bError = true;
-			rX = rY = 0L;
-			if(!bError && s1!=s2) {
-				ErrorBox("Y-range and error-range are\ndifferent in size");
-				bContinue=bError=true;
-				}
-			if(!bError) {
-				if((currYR+1) > maxYR) {
-					rdx = (char**)realloc(rdx, sizeof(char*)*(currYR+2));
-					rdy = (char**)realloc(rdy, sizeof(char*)*(currYR+2));
-					rdx[currYR] = rdx[currYR+1] = rdy[currYR] = rdy[currYR+1] = 0L;
-					maxYR = currYR+1;
-					}
-				if(rdx[currYR]) free(rdx[currYR]);		//store x-range
-				rdx[currYR] = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-				if(rdy[currYR]) free(rdy[currYR]);		//store y range
-				rdy[currYR] = (char*)memdup(TmpTxt+100, (int)strlen(TmpTxt+100)+1, 0);
-				updateYR = true;						currYR++;
-				Dlg->SetText(102, rdx[currYR]);			Dlg->SetText(104, rdy[currYR]);
-				Dlg->Activate(102, true);				
-				if(res != 1) res = -1;
-				}
-			else if(res != 1){
-				bContinue = true;
-				res = -1;
-				}
-			break;
-		case 106:										//prev button
-			if(Dlg->GetText(102, TmpTxt, 100) && Dlg->GetText(104, TmpTxt+100, 100)){
-				if(rdx[currYR]) free(rdx[currYR]);		if(rdy[currYR]) free(rdy[currYR]);
-				rdx[currYR] = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-				rdy[currYR] = (char*)memdup(TmpTxt+100, (int)strlen(TmpTxt+100)+1, 0);
-				}
-			else if(currYR == maxYR) maxYR--;
-			currYR--;	
-			Dlg->SetText(102, rdx[currYR]);
-			Dlg->SetText(104, rdy[currYR]);				Dlg->Activate(102, true);
-			updateYR = true;
-			res = -1;
-			break;
-		case 155:		case 156:
-			res = com_StackDlg(res, Dlg, 0L, 0L, &rd, &currYR,
-				&rY, &bContinue, &ny, &maxYR, &updateYR);
-			break;
-			}
-		} while(res < 0);
-	if(res == 1 && ((rd && rd[0] && rd[0][0])||(rdx && rdy && rdx[0] && rdy[0] && rdx[0][0] && rdy[0][0]))){
-		//accept settings and create plots
-		if((mode == 0 && rd[maxYR]) || (mode == 1 && rdx[maxYR])) maxYR++;
-		ebw = defs.GetSize(SIZE_ERRBAR)*9.0/((double)(s1*maxYR));
-		if(Dlg->GetCheck(251) && Dlg->GetText(252, TmpTxt, 100) && (rX = new AccRange(TmpTxt))
-			&& rX->GetFirst(&ix, &iy)){
-			x_tv = new TextValue();				rX->GetNext(&ix, &iy);
-			i = 1;								start = step = 1.0;
-			do {
-				if(data->GetResult(&ares, iy, ix, false)) switch(ares.type) {
-					case ET_TEXT:
-						x_tv->GetValue(ares.text);			break;
-					case ET_VALUE:		case ET_DATE:		case ET_TIME:
-					case ET_DATETIME:	case ET_BOOL:
-						TranslateResult(&ares);
-						x_tv->GetValue(ares.text);			break;
-					}
-				i++;
-				}while(rX->GetNext(&ix, &iy));
-			}
-		if(rX) delete rX;							rX = 0L;
-		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-		if(!(xyPlots = (PlotScatt**)calloc(maxYR, sizeof(PlotScatt*)))){
-			CloseDlgWnd(hDlg);
-			delete Dlg;
-			if(rd) {
-				for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
-				free(rd);
-				}
-			return false;
-			}
-		xinc = fabs(step / ((double)maxYR + gg/100.0));
-		start -= (xinc * ((double)maxYR-1))/2.0;
-		for(i = 0, desc = 0L; i < maxYR; i++) {
-			x = start + xinc * (double)i;
-			if(mode == 0 && rd) {
-				if(rd[i] && (rY = new AccRange(rd[i]))) ny = rY->CountItems();
-				else {
-					rY = 0L;	ny = 0;
-					}
-				}
-			else if(mode == 1 && rdx && rdy) {
-				if(rdx[i] && (rY = new AccRange(rdx[i]))) ny = rY->CountItems();
-				else {
-					rY = 0L;	ny = 0;
-					}
-				}
-			if(rY) {
-				desc = rY->RangeDesc(data, 1);
-				rY->GetFirst(&ix, &iy);
-				if(mode == 1 && (rX = new AccRange(rdy[i]))) {
-					errs = (ErrorBar**)calloc(ny, sizeof(ErrorBar*)); 
-					rX->GetFirst(&ex, &ey);
-					}
-				rY->GetNext(&ix, &iy);
-				if(ny && rY && (bars = (Bar **)calloc(ny, sizeof(Bar*)))){
-					for(ic = 0; ic < ny; ic++) {
-						if(bContinue = data->GetValue(iy, ix, &y)){
-							bars[ic] = new Bar(0L, data, x, y, BAR_VERTB | BAR_RELWIDTH,
-								-1, -1, ix, iy, desc);
-							CheckBounds(x, y);
-							}
-						if(errs && rX && rX->GetNext(&ex, &ey) && data->GetValue(ey, ex, &e)) {
-							if(bContinue && (errs[ic] = new ErrorBar(0L, data, x, y, e, 0, -1, -1, ix, iy, ex, ey)))
-								errs[ic]->SetSize(SIZE_ERRBAR, ebw);
-							CheckBounds(x, y+e);	CheckBounds(x, y-e);
-							}
-						x += step;
-						rY->GetNext(&ix, &iy);
-						}
-					xyPlots[numXY++] = new PlotScatt(this, data, ic, bars, errs);
-					if(xyPlots[i]) {
-						xyPlots[i]->SetSize(SIZE_BARMINX, xinc);
-						xyPlots[i]->SetSize(SIZE_BAR, bw);
-						xyPlots[i]->Command(CMD_BAR_FILL, GetSchemeFill(&sc), 0L);
-						}
-					for(ic = 0; ic < ny; ic++) if(bars[ic]) delete(bars[ic]);
-					if(errs) for(ic = 0; ic < ny; ic++) if(errs[ic]) delete(errs[ic]);
-					free(bars);		if(errs) free(errs);	bRet = true;
-					}
-				delete(rY);		if(desc) free(desc);		rY = 0L;
-				}
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(rd) {
-		for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
-		free(rd);
-		}
-	if(rdx) {
-		for (i = 0; i < maxYR; i++)	if(rdx[i]) free(rdx[i]);
-		free(rdx);
-		}
-	if(rdy) {
-		for (i = 0; i < maxYR; i++)	if(rdy[i]) free(rdy[i]);
-		free(rdy);
-		}
-	if(rY) delete rY;		if(rX) delete rX;		free(GBDlg);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create a waterfall graph
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-Waterfall::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 25, 10, "Data"};
-	TabSHEET tab2 = {25, 55, 10, "Details"};
-	TabSHEET tab3 = {55, 90, 10, "Scheme"};
-	static DWORD colarr[] = {0x00000000L, 0x00ff0000L, 0x0000ff00L, 0x000000ffL,
-		0x00ff00ff, 0x00ffff00L, 0x0000ffff, 0x00c0c0c0};
-	static DWORD defcol = 0x0L;
-	char text1[100];
-	DlgInfo StackBarDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 158, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 158, 25, 45, 12},
-		{3, 0, 10, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
-		{10, 11, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 140, 100},
-		{11, 12, 200, ISPARENT, SHEET, &tab2, 5, 10, 140, 100},
-		{12, 20, 300, ISPARENT, SHEET, &tab3, 5, 10, 140, 100},
-		{20, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
-		{100, 101, 0, 0x0L, LTEXT, (void*)"range for common x values", 15, 30, 60, 8},
-		{101, 152, 0, 0x0L, RANGEINPUT, TmpTxt, 25, 40, 100, 10},
-		{152, 153, 0, ISPARENT | CHECKED, GROUPBOX, (void*)" ranges for y values ", 12, 60, 128, 45},
-		{153, 154, 0, 0x0L, LTEXT, 0L, 25, 65, 60, 8},
-		{154, 155, 0, 0x0L, RANGEINPUT, TmpTxt, 25, 75, 100, 10},
-		{155, 156, 0, 0x0L, PUSHBUTTON, (void*)"Next >>", 95, 87, 30, 12},
-		{156, 0, 0, 0x0L, PUSHBUTTON, (void*)"<< Prev.", 60, 87, 35, 12},
-		{200, 201, 0, 0x0L, LTEXT, (void*)"line to line displacement:", 20, 35, 80, 8},
-		{201, 202, 0, 0x0L, RTEXT, (void*)"dx =", 28, 45, 13, 8},
-		{202, 203, 0, 0x0L, EDVAL1, &dspm.fx, 43, 45, 25, 10},
-		{203, 204, 0, 0x0L, RTEXT, (void*)"dy =", 73, 45, 13, 8},
-		{204, 205, 0, 0x0L, EDVAL1, &dspm.fy, 88, 45, 25, 10},
-		{205, 0, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 115, 45, 15, 8}, 
-		{300, 301, 0, CHECKED, RADIO1, (void*)" common color for lines:", 20, 35, 80, 9},
-		{301, 302, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&defcol, 105, 35, 20, 10},
-		{302, 303, 0, 0x0L, RADIO1, (void*)" increment color scheme:", 20, 55, 80, 9},
-		{303, 304, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[0], 25, 70, 10, 10},
-		{304, 305, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[1], 37, 70, 10, 10},
-		{305, 306, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[2], 49, 70, 10, 10},
-		{306, 307, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[3], 61, 70, 10, 10},
-		{307, 308, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[4], 73, 70, 10, 10},
-		{308, 309, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[5], 85, 70, 10, 10},
-		{309, 310, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[6], 97, 70, 10, 10},
-		{310, 0, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[7], 109, 70, 10, 10},
-		{500, 0, 0, LASTOBJ, NONE, 0L, 0, 0, 0, 0}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, j, res, currYR=0, maxYR=0, nx=0, ny;
-	char **rd = 0L;
-	bool updateYR = true, bContinue = false, bRet = false, bUseSch;
-	AccRange *rX = 0L, *rY = 0L;
-
-	if(!parent || !data) return false;
-	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
-		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return false;
-	if(TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
-		for(i=100, j= 0; i <= 1000; i +=100) if(TmpTxt[i]) 
-			rd[j++] = (char*)memdup(TmpTxt+i, ((int)strlen(TmpTxt+i))+2, 0);
-			maxYR = j-1;
-		}
-	if(!rd && !(rd = (char**)calloc(1, sizeof(char*))))return false;
-	if(!(Dlg = new DlgRoot(StackBarDlg, data))) return false;
-	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
-	hDlg = CreateDlgWnd("Create waterfall plot", 50, 50, 420, 260, Dlg, 0x0L);
-	do {
-		if(updateYR) {
-			if(currYR >0) {
-				Dlg->ShowItem(156, true);				Dlg->Activate(101, false);
-				}
-			else {
-				Dlg->ShowItem(156, false);				Dlg->Activate(101, true);
-				}
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, 20, "y-values # %d/%d", currYR+1, maxYR+1);
-#else
-			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
-#endif
-			//SetText will also cause a redraw of the whole dialog
-			Dlg->SetText(153, TmpTxt);
-			updateYR = false;
-			}
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		ny = 0;
-		if(rX) delete rX;
-		rX = 0L;
-		switch(res) {
-		case 0:
-			if(bContinue || Dlg->GetCheck(20)) res = -1;
-			break;
-		case -1:
-			bContinue = false;			break;
-		case 1:
-			for(i = 0; i < 8; i++) Dlg->GetColor(303+i, &colarr[i]);
-			Dlg->GetColor(301, &defcol);
-			bUseSch = Dlg->GetCheck(302);
-			Dlg->GetValue(202, &dspm.fx);	Dlg->GetValue(204, &dspm.fy);
-			//execute com_StackDlg for <OK>
-		case 155:		case 156:
-			res = com_StackDlg(res, Dlg, &rX, &nx, &rd, &currYR,
-				&rY, &bContinue, &ny, &maxYR, &updateYR);
-			break;
-		case 301:
-			Dlg->SetCheck(300, 0L, true);
-			res = -1;	break;
-		case 303:	case 304:	case 305:	case 306:
-		case 307:	case 308:	case 309:	case 310:
-			Dlg->SetCheck(302, 0L, true);
-			res = -1;	break;
-			}
-		}while (res < 0);
-	if(res == 1 && nx && rX && rd && rd[0] && rd[0][0]) {
-		maxYR++;
-		numPL = maxYR;
-		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-		Dlg->GetText(101, text1, 100);
-		if(Lines=(DataLine**)calloc(numPL,sizeof(DataLine*)))for(i=0;i<maxYR;i++){
-			if(rd[i] && rd[i][0]) Lines[i] = new DataLine(this, data, text1, rd[i]);
-			if(Lines[i]) {
-				if(bUseSch) Lines[i]->SetColor(COL_DATA_LINE, colarr[(i & 0x07)]);
-				else Lines[i]->SetColor(COL_DATA_LINE, defcol);
-				bRet = true;
-				}
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(rd) {
-		for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
-		free(rd);
-		}
-	if(rX) delete rX;		if(rY) delete rY;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create a multi data line plot
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *MultiLineDlg_Tmpl = 
-	"1,2,,DEFAULT,PUSHBUTTON,-1,158,10,45,12\n"
-	"2,3,,,PUSHBUTTON,-2,158,25,45,12\n"
-	"3,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"10,11,100,ISPARENT | CHECKED,SHEET,1,5,10,140,100\n"
-	"11,12,300,ISPARENT,SHEET,2,5,10,140,100\n"
-	"12,20,200,ISPARENT,SHEET,15,5,10,140,100\n"
-	"20,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-	"100,101,,ISPARENT | CHECKED,GROUPBOX,3,12,30,128,75\n"
-	"101,102,,,LTEXT,0,25,39,60,8\n"
-	"102,103,,,RANGEINPUT,-15,25,49,100,10\n"
-	"103,104,,,LTEXT,0,25,61,60,8\n"
-	"104,105,,,RANGEINPUT,-16,25,71,100,10\n"
-	"105,106,,,PUSHBUTTON,-8,95,87,30,12\n"
-	"106,107,,,PUSHBUTTON,-9,60,87,35,12\n"
-	"107,108,,OWNDIALOG,COLBUTT,4,25,87,20,12\n"
-	"108,,,,LTEXT,12,47,90,30,9\n"
-	"200,201,,CHECKED,CHECKBOX,16,25,35,80,9\n"
-	"201,202,,ODEXIT,SYMBUTT,17,25,70,10,10\n"
-	"202,203,,ODEXIT,SYMBUTT,18,37,70,10,10\n"
-	"203,204,,ODEXIT,SYMBUTT,19,49,70,10,10\n"
-	"204,205,,ODEXIT,SYMBUTT,20,61,70,10,10\n"
-	"205,206,,ODEXIT,SYMBUTT,21,73,70,10,10\n"
-	"206,207,,ODEXIT,SYMBUTT,22,85,70,10,10\n"
-	"207,208,,ODEXIT,SYMBUTT,23,97,70,10,10\n"
-	"208,209,,ODEXIT,SYMBUTT,24,109,70,10,10\n"
-	"209,,,CHECKED,CHECKBOX,25,25,50,80,9\n"
-	"300,301,,TOUCHEXIT,RADIO1,13,20,35,80,9\n"
-	"301,302,,ODEXIT,COLBUTT,4,105,35,20,10\n"
-	"302,303,,CHECKED | TOUCHEXIT, RADIO1,14,20,55,80,9\n"
-	"303,304,,ODEXIT,COLBUTT,4,25,70,10,10\n"
-	"304,305,,ODEXIT,COLBUTT,5,37,70,10,10\n"
-	"305,306,,ODEXIT,COLBUTT,6,49,70,10,10\n"
-	"306,307,,ODEXIT,COLBUTT,7,61,70,10,10\n"
-	"307,308,,ODEXIT,COLBUTT,8,73,70,10,10\n"
-	"308,309,,ODEXIT,COLBUTT,9,85,70,10,10\n"
-	"309,310,,ODEXIT,COLBUTT,10,97,70,10,10\n"
-	"310,,,LASTOBJ | ODEXIT,COLBUTT,11,109,70,10,10\n";
-
-bool
-MultiLines::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 25, 10, "Data"};
-	TabSHEET tab2 = {25, 60, 10, "Scheme"};
-	TabSHEET tab3 = {60, 97, 10, "Symbols"};
-	static DWORD colarr[] = {0x00000000L, 0x00ff0000L, 0x0000ff00L, 0x000000ffL,
-		0x00ff00ff, 0x00ffff00L, 0x0000ffff, 0x00c0c0c0};
-	Symbol *syms[8], **lsyms;
-	static DWORD defcol = 0x0L;
-	char x_txt[100], y_txt[100];
-	DlgInfo *StackBarDlg;
-	void *dyndata[] = {(void*) &tab1, (void*)&tab2, (void*)" ranges for x- and y- values ",
-		(void*)&colarr[0], (void*)&colarr[1], (void*)&colarr[2], (void*)&colarr[3],
-		(void*)&colarr[4], (void*)&colarr[5], (void*)&colarr[6], (void*)&colarr[7],
-		(void*)"line color", (void*)" common color for lines:", (void*)" increment color scheme:",
-		(void*) &tab3, (void*)" draw symbols", (void*)&syms[0], (void*)&syms[1], (void*)&syms[2],
-		(void*)&syms[3], (void*)&syms[4], (void*)&syms[5], (void*)&syms[6], (void*)&syms[7],
-		(void*) " use line color for symbols"};
-	DlgRoot *Dlg;
-	void *hDlg;
-	char **rdx=0L, **rdy=0L;
-	DWORD *rdc = 0L, curr_col;
-	int i, j, nd, res, currYR=0, maxYR=0, s1, s2, rx, ry, cx, cy;
-	double symsize, x, y;
-	bool updateYR = true, bContinue = false, bError, bRet = false;
-	AccRange *rX = 0L, *rY = 0L;
-	DataLine *dl;
-
-	if(!parent || !data) return false;
-	symsize = NiceValue(defs.GetSize(SIZE_SYMBOL)*.8);
-	for(i = 0; i < 8; i++) if(syms[i] = new Symbol(0L, data, 0.0, 0.0, i)) {
-		if(i != 2 && i != 3)syms[i]->SetSize(SIZE_SYMBOL, symsize);
-		}
-	if(!(StackBarDlg = CompileDialog(MultiLineDlg_Tmpl, dyndata)))return false;
-	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
-		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return false;
-	if(TmpTxt[0] && TmpTxt[100] && (rdx = (char**)calloc(12, sizeof(char*))) 
-		&& (rdy = (char**)calloc(12, sizeof(char*))) && (rdc = (DWORD*)malloc(12*sizeof(DWORD)))) {
-		for(i=100, j= 0; i <= 1000; i +=100) if(TmpTxt[i]) {
-			rdx[j] = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);			rdc[j] = colarr[j%8];	
-			rdy[j] = (char*)memdup(TmpTxt+i, (int)strlen(TmpTxt+i)+1, 0);		maxYR = j++;
-			}
-		}
-	if(!(Dlg = new DlgRoot(StackBarDlg, data))) return false;
-	hDlg = CreateDlgWnd("Create Multi Line Plot", 50, 50, 420, 260, Dlg, 0x0L);
-	do {
-		if(updateYR) {
-			if(currYR >0) {
-				Dlg->ShowItem(106, true);	Dlg->ShowItem(108, false);
-				}
-			else {
-				Dlg->ShowItem(106, false);	Dlg->ShowItem(108, true);
-				}
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "x-range # %d/%d", currYR+1, maxYR+1);
-#else
-			sprintf(TmpTxt,"x-range # %d/%d", currYR+1, maxYR+1);
-#endif
-			//SetText will also cause a redraw of the whole dialog
-			Dlg->SetText(101, TmpTxt);
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "y-range # %d/%d", currYR+1, maxYR+1);
-#else
-			sprintf(TmpTxt,"y-range # %d/%d", currYR+1, maxYR+1);
-#endif
-			Dlg->SetText(103, TmpTxt);
-			updateYR = false;
-			}
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 0:
-			if(bContinue || Dlg->GetCheck(20)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 1:
-		case 105:										//next button
-			bError=false;	s1 = s2 = 0;
-			if(Dlg->GetText(102, x_txt, 100)) {
-				if(rX = new AccRange(x_txt)) {
-					s1 = rX->CountItems();
-					if(s1 < 2) {
-						ErrorBox("x-range not valid");
-						bContinue=bError=true;
-						Dlg->Activate(102, true);
-						}
-					delete rX;
-					}
-				else bError = true;
-				}
-			else bError = true;
-			if(Dlg->GetText(104, y_txt, 100) && !bError) {
-				if(rY = new AccRange(y_txt)) {
-					s2 = rY->CountItems();
-					if(s2 < 2) {
-						ErrorBox("y-range not valid");
-						bContinue=bError=true;
-						Dlg->Activate(104, true);
-						}
-					delete rY;
-					}
-				else bError = true;
-				}
-			else {
-				Dlg->Activate(104, true);
-				bError = true;
-				}
-			if(!s1 || !s2) bError = true;
-			rX = rY = 0L;
-			if(!bError && s1!=s2) {
-				ErrorBox("X-range and y-range are\ndifferent in size");
-				bContinue=bError=true;
-				}
-			if(!bError) {
-				if((currYR+1) > maxYR) {
-					rdx = (char**)realloc(rdx, sizeof(char*)*(currYR+2));
-					rdy = (char**)realloc(rdy, sizeof(char*)*(currYR+2));
-					rdc = (DWORD*)realloc(rdc, sizeof(DWORD)*(currYR+2));
-					rdx[currYR] = rdx[currYR+1] = rdy[currYR] = rdy[currYR+1] = 0L;
-					maxYR = currYR+1;
-					rdc[currYR] = rdc[currYR+1] = Dlg->GetCheck(302) ? colarr[maxYR & 0x07] : defcol;
-					}
-				if(rdx[currYR]) free(rdx[currYR]);		//store x-range
-				rdx[currYR] = (char*)memdup(x_txt, (int)strlen(x_txt)+1, 0);
-				if(rdy[currYR]) free(rdy[currYR]);		//store y range
-				rdy[currYR] = (char*)memdup(y_txt, (int)strlen(y_txt)+1, 0);
-				Dlg->GetColor(107, &curr_col);			rdc[currYR] = curr_col;
-				updateYR = true;						currYR++;
-				Dlg->SetColor(107, rdc[currYR]);		Dlg->SetText(102, rdx[currYR]);
-				Dlg->SetText(104, rdy[currYR]);			Dlg->Activate(102, true);				
-				if(res != 1) res = -1;
-				}
-			else if(res != 1){
-				bContinue = true;
-				res = -1;
-				}
-			break;
-		case 106:										//prev button
-			if(Dlg->GetText(102, x_txt, 100) && Dlg->GetText(104, y_txt, 100)){
-				if(rdx[currYR]) free(rdx[currYR]);		if(rdy[currYR]) free(rdy[currYR]);
-				rdx[currYR] = (char*)memdup(x_txt, (int)strlen(x_txt)+1, 0);
-				rdy[currYR] = (char*)memdup(y_txt, (int)strlen(y_txt)+1, 0);
-				Dlg->GetColor(107, &curr_col);			rdc[currYR] = curr_col;
-				}
-			else if(currYR == maxYR) maxYR--;
-			currYR--;
-			Dlg->SetColor(107, rdc[currYR]);			Dlg->SetText(102, rdx[currYR]);
-			Dlg->SetText(104, rdy[currYR]);				Dlg->Activate(102, true);
-			updateYR = true;
-			res = -1;
-			break;
-		case 201:	case 202:	case 203:	case 204:
-		case 205:	case 206:	case 207:	case 208:
-			res = -1;
-		case 300:
-			Dlg->SetColor(107, defcol);
-			bContinue = true;
-			res = -1;	break;
-		case 301:
-			Dlg->SetCheck(300, 0L, true);				Dlg->GetColor(res, &defcol);
-			res = -1;	break;
-		case 302:
-			Dlg->SetColor(107, colarr[currYR & 0x07]);
-			bContinue = true;
-			res = -1;	break;
-		case 303:	case 304:	case 305:	case 306:
-		case 307:	case 308:	case 309:	case 310:
-			Dlg->SetCheck(302, 0L, true);
-			i = res-303;
-			if(rdx && rdy && i <= maxYR && rdc[i] == colarr[i]) {
-				Dlg->GetColor(res, &colarr[i]);		rdc[i] = colarr[i];
-				Dlg->SetColor(107, rdc[currYR]);
-				}
-			else {
-				Dlg->GetColor(res, &colarr[i]);
-				}
-			res = -1;	break;
-			}
-		}while (res < 0);
-	if(res == 1 && rdx && rdy && maxYR) {
-		maxYR++;		rX = rY = 0L;
-		if(xyPlots=(PlotScatt**)calloc(maxYR, sizeof(PlotScatt*))) for(i = numXY = 0; i < maxYR; i++){
-			if(rdx[i] && rdy[i] && rdx[i][0] && rdy[i][0]) {
-				if(Dlg->GetCheck(200) && (rX = new AccRange(rdx[i])) && (rY = new AccRange(rdy[i]))) {
-					lsyms = (Symbol**)calloc(rX->CountItems()+1, sizeof(Symbol*));
-					symsize = syms[i &0x07]->GetSize(SIZE_SYMBOL);
-					for(nd = 0, rX->GetFirst(&cx, &rx), rY->GetFirst(&cy, &ry); rX->GetNext(&cx, &rx), rY->GetNext(&cy, &ry); ) {
-						if(data->GetValue(rx, cx, &x) && data->GetValue(ry, cy, &y)) {
-							lsyms[nd] = new Symbol(0L, data, x, y, syms[i &0x07]->type, cx, rx, cy, ry);
-							if(Dlg->GetCheck(209)) lsyms[nd]->SetColor(COL_SYM_LINE, Dlg->GetCheck(300) ? defcol : rdc[i & 0x07]);
-							else {
-								lsyms[nd]->SetColor(COL_SYM_LINE, syms[i &0x07]->GetColor(COL_SYM_LINE));
-								lsyms[nd]->SetColor(COL_SYM_FILL, syms[i &0x07]->GetColor(COL_SYM_FILL));
-								}
-							lsyms[nd]->SetSize(SIZE_SYMBOL, symsize);
-							nd++;
-							}
-						}
-					}
-				else {
-					nd = 0;		lsyms = 0L;
-					}
-				if(dl = new DataLine(this, data, rdx[i], rdy[i])) {
-					dl->SetColor(COL_DATA_LINE, Dlg->GetCheck(300) ? defcol : rdc[i & 0xf]);
-					if(xyPlots[numXY] = new PlotScatt(this, data, nd, lsyms, dl)) {
-						if(rY) xyPlots[numXY]->data_desc = rY->RangeDesc(data, 1);
-						numXY++;
-						}
-					else delete dl;
-					}
-				if(rX)delete rX;		if(rY)delete rY;		rX = rY = 0L;
-				}
-			}
-		if(numXY) bRet = true;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(rdx) {
-		for (i = 0; i < maxYR; i++)	if(rdx[i]) free(rdx[i]);
-		free(rdx);
-		}
-	if(rdy) {
-		for (i = 0; i < maxYR; i++)	if(rdy[i]) free(rdy[i]);
-		free(rdy);
-		}
-	for(i = 0; i < 8; i++) if(syms[i]) delete syms[i];
-	free(StackBarDlg);		if(rdc) free(rdc);
-	if(bRet) {
-		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;	Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
-		Command(CMD_AUTOSCALE, 0L, 0L);
-		}
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Pie and ring chart properties
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *PieDlgTmpl = 
-		"1,2,,DEFAULT,PUSHBUTTON,-1,130,10,45,12\n"
-		"2,3,,,PUSHBUTTON,-2,130,25,45,12\n"
-		"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"4,5,100,ISPARENT | CHECKED,SHEET,1,5,10,120,103\n"
-		"5,6,200,ISPARENT,SHEET,2,5,10,120,103\n"
-		"6,10,300,ISPARENT,SHEET,3,5,10,120,103\n"
-		"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-		"100,101,,,LTEXT,4,10,25,60,8\n"
-		"101,105,,,RANGEINPUT,-15,15,35,100,10\n"
-		"105,106,500,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"106,107,600,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"107,108,,,EDVAL1,6,58,59,30,10\n"
-		"108,,,,LTEXT,-3,89,59,15,8\n" 
-		"200,201,,,LTEXT,7,15,30,60,8\n"
-		"201,202,,,RTEXT,-4,2,42,20,8\n"
-		"202,204,,,EDVAL1,8,23,42,30,10\n"
-		"204,205,,,RTEXT,-5,47,42,20,8\n"
-		"205,206,,,EDVAL1,9,68,42,30,10\n"
-		"206,207,,,LTEXT,-3,99,42,15,8\n" 
-		"207,208,,,RTEXT,10,27,58,20,8\n"
-		"208,209,,,EDVAL1,11,48,58,30,10\n"
-		"209,210,,,LTEXT,12,79,58,15,8\n" 
-		"210,211,400,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"211,212,410,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"212,,,,LTEXT,13,15,80,30,8\n"
-		"300,,,NOSELECT,ODBUTTON,14,20,35,80,60\n"
-		"400,401,,EXRADIO | CHECKED,ODBUTTON,15,40,75,30,30\n"
-		"401,,,EXRADIO,ODBUTTON,15,70,75,30,30\n"
-		"410,411,,EXRADIO | CHECKED,ODBUTTON,15,40,75,30,30\n"
-		"411,,,EXRADIO,ODBUTTON,15,70,75,30,30\n"
-		"500,501,,CHECKED,RADIO1,16,10,59,20,8\n"
-		"501,502,,,RADIO1,17,10,71,40,8\n"
-		"502,503,,,RANGEINPUT,-16,15,82,100,10\n"
-		"503,504,,,LTEXT,19,15,94,10,8\n"
-		"504,505,,,EDVAL1,20,42,94,25,10\n"
-		"505,,,,LTEXT,21,70,94,15,8\n"
-		"600,601,,,RTEXT,22,8,59,45,8\n"
-		"601,602,,,RTEXT,23,8,74,45,8\n"
-		"602,603,,,EDVAL1,24,58,74,30,10\n"
-		"603,,,LASTOBJ,LTEXT,-3,89,74,15,8";
-bool
-PieChart::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 25, 10, "Data"};
-	TabSHEET tab2 = {25, 55, 10, "Details"};
-	TabSHEET tab3 = {55, 90, 10, "Scheme"};
-	double fcx =10.0, fcy = 20.0, frad=40.0, firad = 30.0;
-	char txt2[80];
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)"spread sheet range for values",
-		0L, (void*)&frad, (void*)"position of center:", (void*)&fcx, (void*)&fcy,
-		(void*)"start angle", (void*)&CtDef.fx, (void*)"degree", (void*)"style:", (void*)(OD_scheme),
-		(void*)(OD_PieTempl), (void*)"fixed radius", (void*)"pick radii from spreadsheet range",
-		0L, (void*)"x  factor", (void*)&FacRad, (void*)&txt2, (void*)"outer radius",
-		(void*)"inner radius", (void*)&firad};
-	DlgInfo *PieDlg = CompileDialog(PieDlgTmpl, dyndata);
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, ix, iy, rix, riy, ny, res, cf, cb;
-	bool bRet = false, bContinue = false;
-	double sum = 0.0, dang1, dang2;
-	double fv;
-	lfPOINT fpCent;
-	AccRange *rY = 0L, *rR = 0L;
-
-	if(!parent || !data) return false;
-	UseRangeMark(data, 1, TmpTxt, TmpTxt+100);
-	cb = rlp_strcpy(txt2, 80, "= [");	cb += rlp_strcpy(txt2+cb, 80-cb, Units[defs.cUnits].display);
-	rlp_strcpy(txt2+cb, 80-cb, "]");
-	frad = (parent->GetSize(SIZE_DRECT_BOTTOM) - parent->GetSize(SIZE_DRECT_TOP))/2.0;
-	fcx = parent->GetSize(SIZE_GRECT_LEFT) + (parent->GetSize(SIZE_DRECT_LEFT))/2.0 + frad;
-	fcy = parent->GetSize(SIZE_GRECT_TOP) + parent->GetSize(SIZE_DRECT_TOP) + frad;
-	firad = frad-frad/10.0f;
-	if(!(Dlg = new DlgRoot(PieDlg, data)))return false;
-	if(Id == GO_PIECHART) {
-		Dlg->ShowItem(105, true);		Dlg->ShowItem(106, false);
-		Dlg->ShowItem(210, true);		Dlg->ShowItem(211, false);
-		}
-	else {
-		Dlg->ShowItem(105, false);		Dlg->ShowItem(106, true);
-		Dlg->ShowItem(210, false);		Dlg->ShowItem(211, true);
-		}
-	hDlg = CreateDlgWnd(Id == GO_PIECHART ? (char*)"Create pie chart" : 
-		(char*)"Create ring chart",	50, 50, 370, 266, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		//rY is defined by OK. If other buttons use rY reset to 0L!
-		switch(res) {
-		case 0:							//lost focus ?
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 400:	case 410:
-			Dlg->SetText(208, "90");		Dlg->DoPlot(0L);
-			CtDef.fy = 360.0;					res = -1;
-			break;
-		case 401:	case 411:
-			Dlg->SetText(208, "180");		Dlg->DoPlot(0L);
-			CtDef.fy = 180.0;					res = -1;
-			break;
-		case 1:
-			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0] && (rY = new AccRange(TmpTxt))) 
-				ny = rY->CountItems();
-			else ny = 0;
-			Dlg->GetValue(208, &CtDef.fx);		Dlg->GetValue(202, &fcx);
-			Dlg->GetValue(205, &fcy);			Dlg->GetValue(107, &frad);
-			Dlg->GetValue(602, &firad);			Dlg->GetValue(504, &FacRad);
-			if(Dlg->GetCheck(501) && ny && Dlg->GetText(502, TmpTxt, TMP_TXT_SIZE) && 
-				(rR = new AccRange(TmpTxt))){
-				if(rR->CountItems() != ny) {
-					delete rR;
-					delete rY;
-					rR = rY = 0L;
-					ErrorBox("Range for values and\nrange for radii must\nhave the same size!");
-					bContinue = true;
-					res = -1;
-					}
-				}
-			break;
-			}
-		}while (res < 0);
-
-	if(res == 1 && rY && ny >1 && (Segments = (segment **)calloc(ny, sizeof(segment*)))) {
-		nPts = ny;
-		if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) ssRefA = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-		if(rR && Dlg->GetText(502, TmpTxt, TMP_TXT_SIZE)) ssRefR = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-		Bounds.Xmax = Bounds.Ymax = 100.0;		Bounds.Xmin = Bounds.Ymin = -100.0;
-		fpCent.fx = fcx;					fpCent.fy = fcy;
-		rY->GetFirst(&ix, &iy);				rY->GetNext(&ix, &iy);
-		for(i = 0; i < ny; i++){
-			if(data->GetValue(iy, ix, &fv)) sum += fv;
-			rY->GetNext(&ix, &iy);
-			}
-		sum /= CtDef.fy;
-		dang1 = dang2 = CtDef.fx;
-		rY->GetFirst(&ix, &iy);				rY->GetNext(&ix, &iy);
-		if(rR) {
-			rR->GetFirst(&rix, &riy);		rR->GetNext(&rix, &riy);
-			}
-		for(i = cf = 0; i < ny; i++){
-			if(data->GetValue(iy, ix, &fv)) {
-				dang2 -= (double)fv / sum;
-				if(dang2 < 0.0) dang2 += 360.0;
-				if(rR && data->GetValue(riy, rix, &frad)) frad *= FacRad;
-				Segments[i] = new segment(this, data, &fpCent, 
-					Id == GO_PIECHART ? 0.0 : firad, frad,
-					dang1, dang2);
-				if(Segments[i])Segments[i]->Command(CMD_SEG_FILL, GetSchemeFill(&cf), 0L);
-				dang1 = dang2;
-				}
-			rY->GetNext(&ix, &iy);
-			if(rR) rR->GetNext(&rix, &riy);
-			}
-		bRet = true;
-		}
-	if(rY) delete rY;		if(rR) delete rR;
-	CloseDlgWnd(hDlg);		delete Dlg;				free(PieDlg);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Create a star chart
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-StarChart::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 25, 10, "Data"};
-	TabSHEET tab2 = {25, 55, 10, "Labels"};
-	double sa = 90.0, factor = 1.0, lbdist = NiceValue(DefSize(SIZE_TEXT)*1.2);
-	char txt1[80], txt2[80];
-	DlgInfo StarDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 130, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 25, 45, 12},
-		{3, 0, 4, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
-		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 120, 103},
-		{5, 10, 200, ISPARENT, SHEET, &tab2, 5, 10, 120, 103},
-		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
-		{100, 101, 0, 0x0L, LTEXT, (void*)"spread sheet range for values", 10, 30, 60, 8},
-		{101, 102, 0, 0x0L, RANGEINPUT, txt1, 15, 45, 100, 10},
-		{102, 103, 0, 0x0L, RTEXT, (void*)"x  factor", 17, 57, 30, 8},
-		{103, 104, 0, 0x0L, EDVAL1, &factor, 48, 57, 30, 10},
-		{104, 105, 0, 0x0L, LTEXT, &txt2, 79, 57, 15, 8},
-		{105, 106, 0, 0x0L, RTEXT, (void*)"start angle", 17, 72, 30, 8},
-		{106, 107, 0, 0x0L, EDVAL1, &sa, 48, 72, 30, 10},
-		{107, 108, 0, 0x0L, LTEXT, (void*)"degree", 79, 72, 15, 8}, 
-		{108, 109, 0, CHECKED, CHECKBOX, (void*)"draw polygon", 25, 87, 20, 8},
-		{109, 0, 0, CHECKED, CHECKBOX, (void*)"draw rays", 25, 97, 20, 8},
-		{200, 201, 0, 0x0L, CHECKBOX, (void*)"add labels to data points", 15, 28, 50, 8},
-		{201, 202, 0, 0x0L, RANGEINPUT, (void*)txt1, 15, 51, 100, 10},
-		{202, 203, 0, 0x0L, LTEXT, (void*)"spread sheet range for labels:", 15, 40, 60, 8},
-		{203, 204, 0, 0x0L, RTEXT, (void*)"distance:", 10, 70, 40, 8},
-		{204, 205, 0, 0x0L, EDVAL1, &lbdist, 52, 70, 30, 10},
-		{205, 0, 0, LASTOBJ, LTEXT, (void*)Units[defs.cUnits].display, 85, 70, 10, 8}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, ix, iy, res, width, height, ny;
-	bool bRet = false, bContinue = false;
-	AccRange *rY = 0L, *rL = 0L;
-	Label *lb;
-	lfPOINT *fp = 0L, *fpt = 0L, fl[2];
-	double fx, fy, tmpval, frad;
-	double sia, csia;
-	polyline *plo;
-	TextDEF td = {0x00000000L, 0x00ffffffL, DefSize(SIZE_TEXT), 0.0, 0.0, 0,
-		TXA_HCENTER | TXA_VCENTER, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, 0L}; 
-
-	if(!parent || !data) return false;
-	data->GetSize(&width, &height);
-#ifdef USE_WIN_SECURE
-	sprintf_s(txt1, 80,"a1:a%d", height);	sprintf_s(txt2, 80, "= [%s]", Units[defs.cUnits].display);
-#else
-	sprintf(txt1, "a1:a%d", height);		sprintf(txt2, "= [%s]", Units[defs.cUnits].display);
-#endif
-	if(parent) {
-		frad = (parent->GetSize(SIZE_DRECT_BOTTOM) - parent->GetSize(SIZE_DRECT_TOP))/2.0f;
-		fPos.fx = parent->GetSize(SIZE_GRECT_LEFT) + parent->GetSize(SIZE_DRECT_LEFT) + frad;
-		fPos.fy = parent->GetSize(SIZE_GRECT_TOP) + parent->GetSize(SIZE_DRECT_TOP) + frad;
-		}
-	if(!(Dlg = new DlgRoot(StarDlg, data)))return false;
-	hDlg = CreateDlgWnd("Create star chart", 50, 50, 370, 260, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 0:							//lost focus ?
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 1:
-			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0] && (rY = new AccRange(TmpTxt))) 
-				ny = rY->CountItems();
-			else ny = 0;
-			Dlg->GetValue(106, &sa);			Dlg->GetValue(103, &factor);
-			Dlg->GetValue(204, &lbdist);
-			break;
-			}
-		}while (res < 0);
-	if(res == 1 && rY && ny >1 && (fp = (lfPOINT*)calloc(ny+1, sizeof(lfPOINT))) &&
-		(fpt = (lfPOINT*)calloc(ny+1, sizeof(lfPOINT)))){
-		Bounds.Xmax = Bounds.Ymax = 100.0;		Bounds.Xmin = Bounds.Ymin = -100.0;
-		rY->GetFirst(&ix, &iy);
-		for(i = 0; i < ny; i++){
-			rY->GetNext(&ix, &iy);
-			if(data->GetValue(iy, ix, &tmpval)){
-				tmpval *= factor;
-				sia = sin(sa * 0.01745329252);			csia = cos(sa * 0.01745329252);
-				fx = (tmpval * csia);					fy = (-tmpval * sia);
-				}
-			else fx = fy = 0.0f;
-			fp[i].fx = fpt[i].fx = fx;			fp[i].fy = fpt[i].fy = fy;
-			fpt[i].fx += lbdist *csia;			fpt[i].fy += (-lbdist * sia);
-			sa -= 360.0/ny;
-			}
-		fp[i].fx = fp[0].fx;		fp[i].fy = fp[0].fy;
-		if(Dlg->GetCheck(108)){
-			if((plo = new polygon(this, data, fp, ny+1))) {		//ny+1 to close the shape!
-				if(!(bRet = Command(CMD_DROP_OBJECT, (void*)plo, 0L))) delete plo;
-				}
-			}
-		if(Dlg->GetCheck(109)){
-			fl[0].fx = fl[0].fy = 0.0f;
-			for(i = 0; i < ny; i++) {
-				fl[1].fx = fp[i].fx;		fl[1].fy = fp[i].fy;
-				if((plo = new polyline(this, data, fl, 2))) {
-					if(Command(CMD_DROP_OBJECT, (void*)plo, 0L)) bRet = true;
-					else delete plo;
-					}
-				}
-			}
-		if(Dlg->GetCheck(200) && Dlg->GetText(201, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0] && (rL = new AccRange(TmpTxt))){
-			rL->GetFirst(&ix, &iy);
-			td.text = TmpTxt;
-			for(i = 0; i < ny; i++) {
-				rL->GetNext(&ix, &iy);
-				if(data->GetText(iy, ix, TmpTxt, TMP_TXT_SIZE)){
-					if((lb = new Label(this, data, fpt[i].fx, fpt[i].fy, &td, 0L))) {
-						if(Command(CMD_DROP_OBJECT, (void*)lb, 0L)) bRet = true;
-						else delete lb;
-						}
-					}
-				}
-			}
-		}
-	if(rY) delete rY;			if(rL) delete rL;
-	if(fp) free(fp);			if(fpt) free(fpt);
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Grid3D represents a surface in space
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-Grid3D::PropertyDlg()
-{
-	return Configure();
-}
-
-bool
-Grid3D::Configure()
-{
-	TabSHEET tab1 = {0, 37, 10, "Function"};
-	TabSHEET tab2 = {37, 65, 10, "Style"};
-	FillDEF newFill;
-	DlgInfo GridDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 155, 10, 50, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 155, 25, 50, 12},
-		{3, 0, 300, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
-		{300, 301, 500, HIDDEN | CHECKED, GROUPBOX, (void*)" grid lines ", 10, 10, 140, 100},
-		{301, 305, 400, HIDDEN | CHECKED, GROUPBOX, (void*)" surface ", 10, 10, 140, 100},
-		{305, 306, 0, TOUCHEXIT, RADIO1, (void*) " grid lines", 155, 45, 50, 10},
-		{306, 0, 0, TOUCHEXIT, RADIO1, (void*) " surface", 155, 57, 50, 10},
-		{400, 401, 0, 0x0L, RTEXT, (void*)"grid line width", 38, 20, 40, 8},
-		{401, 402, 0, 0x0L, EDVAL1, &Line.width, 80, 20, 25, 10},
-		{402, 403, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 107, 20, 20, 8},
-		{403, 404, 0, 0x0L, RTEXT, (void*)"grid line color", 38, 32, 40, 8},
-		{404, 405, 0, OWNDIALOG, COLBUTT, (void *)&Line.color, 80, 32, 25, 10},
-		{405, 406, 0, 0x0L, RTEXT,(void*)"plane color" , 38, 44, 40, 8},
-		{406, 0, 0, OWNDIALOG, SHADE3D, &newFill, 80, 44, 25, 10},
-		{500, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_linedef, 15, 15, 130, 100}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, cb, new_type, undo_level = *Undo.pcb;
-	bool bRet = false;
-	double tmp;
-	DWORD new_col;
-	LineDEF newLine;
-	anyOutput *cdisp = Undo.cdisp;
-
-	if(!parent) return false;
-	memcpy(&newFill, &Fill, sizeof(FillDEF));
-	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-	if(!(Dlg = new DlgRoot(GridDlg, data))) return false;
-	if(!type) {
-		Dlg->ShowItem(300, true);	Dlg->SetCheck(305, 0L, true);
-		}
-	else {
-		Dlg->ShowItem(301, true);	Dlg->SetCheck(306, 0L, true);
-		}
-	if(parent->name) {
-		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Grid of ");
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
-		}
-	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "3D Grid");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 426, 260, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 305:		case 306:
-			if(Dlg->GetCheck(305)) {
-				Dlg->ShowItem(300, true);	Dlg->ShowItem(301, false);
-				}
-			else {
-				Dlg->ShowItem(300, false);	Dlg->ShowItem(301, true);
-				}
-			Dlg->Command(CMD_REDRAW, 0L, 0L);
-			res = -1;
-			break;
-			}
-		}while (res < 0);
-	Undo.SetDisp(cdisp);
-	while(*Undo.pcb > undo_level)	Undo.Pop(cdisp);
-	if(res == 1) {
-		if(Dlg->GetCheck(305) && type == 0) {
-			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-			if(cmpLineDEF(&Line, &newLine)) {
-				Command(CMD_SET_LINE, &newLine, 0L);
-				bRet = true;
-				}
-			}
-		else if(Dlg->GetCheck(306) && type == 1) {
-			Dlg->GetValue(401, &tmp);			Dlg->GetColor(404, &new_col);
-			if(planes && (cmpFillDEF(&Fill, &newFill) || tmp != Line.width || new_col != Line.color)) {
-				Command(CMD_SAVE_SYMBOLS, 0L, 0L);
-				Command(CMD_SYM_FILL, &newFill, 0L);
-				if(tmp != Line.width) SetSize(SIZE_SYM_LINE, tmp);
-				if(new_col != Line.color) SetColor(COL_POLYLINE, new_col);
-				bRet = true;
-				}
-			}
-		else {
-			Undo.ValInt(parent, &type, 0L);
-			Undo.Line(this, &Line, UNDO_CONTINUE);	Undo.Fill(this, &Fill, UNDO_CONTINUE);
-			if(Dlg->GetCheck(305)) {
-				new_type = 0;
-				OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-				}
-			else {
-				new_type = 1;
-				memcpy(&Fill, &newFill, sizeof(FillDEF));
-				Dlg->GetValue(401, &Line.width);
-				Dlg->GetColor(404, &Line.color);
-				Line.pattern = 0L;
-				Line.patlength = 1;
-				}
-			if(planes && nPlanes) Undo.DropListGO(parent, (GraphObj***)&planes, &nPlanes, UNDO_CONTINUE);
-			if(lines && nLines) Undo.DropListGO(parent, (GraphObj***)&lines, &nLines, UNDO_CONTINUE);
-			Undo.VoidPtr(parent, (void**)&planes, 0L, 0L, UNDO_CONTINUE);
-			Undo.VoidPtr(parent, (void**)&lines, 0L, 0L, UNDO_CONTINUE);
-			type = new_type;
-			CreateObs(true);
-			bRet = true;
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Scatt3D is a layer representing most simple 3D plots
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *Dlg3DTmpl = 
-		"1,2,,DEFAULT,PUSHBUTTON,-1,142,10,45,12\n"
-		"2,3,,,PUSHBUTTON,-2,142,25,45,12\n"
-		"3,50,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"4,5,100,TOUCHEXIT | ISPARENT | CHECKED, SHEET,1,5,10,131,100\n"
-		"5,6,200,TOUCHEXIT | ISPARENT, SHEET,2,5,10,131,100\n"
-		"6,10,400, TOUCHEXIT | ISPARENT, SHEET,3,5,10,131,100\n"
-		"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-		"50,60,,NOSELECT, ODBUTTON,4,142,65,45,45\n"
-		"60,61,,,ICON,5,10,114,20,20\n"
-		"61,62,,,LTEXT,6,30,116,100,6\n"
-		"62,,,,LTEXT,7,30,122,100,6\n"
-		"100,101,,,LTEXT,8,10,30,60,8\n"
-		"101,102,,,RANGEINPUT,9,20,40,100,10\n"
-		"102,103,,,LTEXT,10,10,55,60,8\n"
-		"103,104,,,RANGEINPUT,11,20,65,100,10\n"
-		"104,105,,,LTEXT,12,10,80,60,8\n"
-		"105,,,,RANGEINPUT,13,20,90,100,10\n"
-		"200,201,,,LTEXT,14,25,30,60,8\n"
-		"201,202,,,CHECKBOX,15,30,55,60,8\n"
-		"202,203,,TOUCHEXIT,CHECKBOX,16,30,65,60,8\n"
-		"203,204,,TOUCHEXIT,CHECKBOX,17,30,45,60,8\n"
-		"204,205,,TOUCHEXIT,CHECKBOX,18,30,75,60,8\n"
-		"205,,,TOUCHEXIT,CHECKBOX,19,30,85,60,8\n"
-		"400,410,,,LTEXT,20,20,30,60,8\n"
-		"410,411,,EXRADIO,ODBUTTON,21,20,42,25,25\n"
-		"411,412,,EXRADIO,ODBUTTON,21,45,42,25,25\n"
-		"412,,,LASTOBJ | EXRADIO,ODBUTTON,21,70,42,25,25";
-
-bool
-Scatt3D::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 22, 10, "Data"};
-	TabSHEET tab2 = {22, 50, 10, "Layout"};
-	TabSHEET tab3 = {50, 75, 10, "Axes"};
-	char text1[100], text2[100], text3[100];
-	int icon = ICO_INFO;
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)(OD_AxisDesc3D), (void*)&icon,
-		(void*)"Use [arrow keys], [shift]+[arrow key],", (void*)"and [r], [R], [l] or [L] to rotate graph.",
-		(void*)"range for X Data", (void*)text1, (void*)"range for Y Data", (void*)text2,
-		(void*)"range for Z Data", (void*)text3, (void*)"select style:", (void*)" balls", (void*)" columns",
-		(void*)" line", (void*)" drop lines", (void*)" arrows", (void*)"select template:", 
-		(void*)(OD_AxisTempl3D)};
-	DlgInfo *Dlg3D = CompileDialog(Dlg3DTmpl, dyndata);
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, n1, n2, n3, ic = 0;
-	int i, j, k, l, m, n, i2, j2, k2, l2, m2, cb;
-	double x, y, z, bar_w, bar_d, rad;
-	bool bRet = false, bContinue = false;
-	AccRange *rX, *rY, *rZ;
-	fPOINT3D pos1, pos2;
-
-	if(!data || !parent)return false;
-	UseRangeMark(data, 1, text1, text2, text3);
-	if(!(Dlg = new DlgRoot(Dlg3D, data)))return false;
-	Dlg->SetCheck(410 + AxisTempl3D, 0L, true);
-	for(i = 0; i < 5; i++) Dlg->SetCheck(201+i, 0L, (c_flags & (1<<i))!=0);
-	if(c_flags == 0x2000 || c_flags == 0x4000) {
-		Dlg->ShowItem(5, false);		Dlg->ShowItem(6, false);
-		}
-	else Dlg->ShowItem(6, (c_flags & 0x1000) == 0x1000);
-	rX = rY = rZ = 0L;					rad = DefSize(SIZE_SYMBOL);
-#ifdef _WINDOWS
-	for(i = 61; i <= 62; i++) Dlg->TextSize(i, 12);
-#else
-	for(i = 61; i <= 62; i++) Dlg->TextSize(i, 10);
-#endif
-	hDlg = CreateDlgWnd(c_flags == 0x2000 ? (char*)"Create Paravent Plot": 
-	c_flags == 0x4000 ? (char*)"Delauney Surface" : (char*)"Create 3D Plot", 50, 50, 388, 300, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:								// focus lost
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 4:		case 5:		case 6:		//the tab sheets
-			res = -1;
-			break;
-		case 202:		Dlg->SetCheck(204, 0L, false);		res = -1;		break;
-		case 204:		Dlg->SetCheck(202, 0L, false);		res = -1;		break;
-		case 203:		Dlg->SetCheck(205, 0L, false);		res = -1;		break;
-		case 205:		Dlg->SetCheck(203, 0L, false);		res = -1;		break;
-		case 410:	case 411:	case 412:	//axis templates
-			AxisTempl3D = res-410;
-			res = -1;
-			break;
-		case 1:
-			if(rX) delete rX;	if(rY) delete rY;	if(rZ) delete rZ;
-			rX = rY = rZ = 0L;	n1 = n2 = n3 = 0;
-			if(Dlg->GetText(101, text1, 100) && (rX = new AccRange(text1))) n1 = rX->CountItems();
-			if(Dlg->GetText(103, text2, 100) && (rY = new AccRange(text2))) n2 = rY->CountItems();
-			if(Dlg->GetText(105, text3, 100) && (rZ = new AccRange(text3))) n3 = rZ->CountItems();
-			if(n1 && n2 && n3){
-				if(c_flags == 0x2000 || c_flags == 0x4000) {
-					//no more but a ribbon or surface
-					}
-				else if(n1 == n2 && n2 == n3) {
-					//o.k., three ranges of equal size have been defined
-					c_flags = 0;
-					for(i = 0; i < 5; i++) if(Dlg->GetCheck(201+i)) c_flags |= (1<<i);
-					}
-				else {
-					InfoBox("All ranges must have\nthe same size."); 
-					res = -1;	bContinue = true;
-					}
-				}
-			else {
-				cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Ranges for ");
-				if(!n1) cb += rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, "\n-  X Data");
-				if(!n2) cb += rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, "\n-  Y Data");
-				if(!n3) cb += rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, "\n-  Z Data");
-				rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, "\nnot given or not valid.");
-				InfoBox(TmpTxt);
-				res = -1;	bContinue = true;
-				}
-			break;
-			}
-		}while (res <0);
-	if(res == 1 && rX && rY && rZ) {
-		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-		xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
-		xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
-		bar_w = bar_d = (DefSize(SIZE_BAR)/2.0);
-		if(c_flags & 0x01) (Balls = (Sphere**)calloc((nBalls = n1)+1, sizeof(Sphere*)));
-		if(c_flags & 0x02) (Columns = (Brick**)calloc((nColumns = n1)+1, sizeof(Brick*)));
-		if(c_flags & 0x04) Line = new Line3D(this, data, text1, text2, text3);
-		if(c_flags & 0x08) (DropLines = (DropLine3D**)calloc((nDropLines = n1)+1, sizeof(DropLine3D*)));
-		if(c_flags & 0x010) (Arrows = (Arrow3D**)calloc((nArrows = n1)+1, sizeof(Arrow3D*)));
-		rX->GetFirst(&i, &j);		rX->GetNext(&i, &j);
-		rY->GetFirst(&k, &l);		rY->GetNext(&k, &l);
-		rZ->GetFirst(&m, &n);		rZ->GetNext(&m, &n);
-		i2 = i;	j2 = j;	k2 = k;	l2 = l;	m2 = m;	n2 = n;
-		if(c_flags == 0x2000){
-			Bounds.Ymin = 0.0;
-			rib = new Ribbon(this, data, 2, text1, text2, text3);
-			}
-		else if(c_flags == 0x4000){
-			rib = new Ribbon(this, data, 3, text1, text2, text3);
-			}
-		do {
-			if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y) && 
-				data->GetValue(n, m, &z)){
-				if(ic) {
-					pos1.fx = pos2.fx;		pos1.fy = pos2.fy;	pos1.fz = pos2.fz;
-					}
-				else {
-					pos1.fx = x;			pos1.fy = y;		pos1.fz = z;
-					}
-				pos2.fx = x;	pos2.fy = y;	pos2.fz = z;
-				if(!bRet) memcpy(&pos1, &pos2, sizeof(fPOINT3D));
-				if(Balls) Balls[ic] = new Sphere(this, data, 0, x, y, z, rad, i, j, k, l, m, n);
-				if(Columns)Columns[ic] = new Brick(this, data, x, 0.0, z, 
-					bar_d, bar_w, y, 0x800L, i, j, -1, -1, m, n, -1, -1, -1, -1, k, l);
-				if(DropLines) DropLines[ic] = new DropLine3D(this, data, &pos2, i, j, k, l, m, n);
-				if(Arrows) Arrows[ic] = new Arrow3D(this, data, &pos1, &pos2, 
-					i, j, k, l, m, n, i2, j2, k2, l2, m2, n2);
-				((Plot3D *)parent)->CheckBounds3D(x, y, z);		CheckBounds3D(x, y, z);
-				bRet = true;
-				}
-			i2 = i;	j2 = j;	k2 = k;	l2 = l;	m2 = m;	n2 = n;
-			ic++;
-			}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l) && rZ->GetNext(&m, &n));
-		if(!bRet) InfoBox("The selected data range\nis empty or does not contain\nvalid"
-			" data triplets!");
-		bRet = true;
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;			free(Dlg3D);
-	if(rX) delete rX;		if(rY) delete rY;	if(rZ) delete rZ;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// user defined function properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-Function::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 37, 10, "Function"};
-	TabSHEET tab2 = {37, 60, 10, "Line"};
-	DlgInfo FuncDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 160, 10, 32, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 160, 25, 32, 12},
-		{3, 10, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
-		{4, 5, 100, ISPARENT, SHEET, &tab1, 5, 10, 149, 110},
-		{5, 0, 500, ISPARENT | CHECKED, SHEET, &tab2, 5, 10, 149, 110},
-		{10, 600, 0, 0x0L, CHECKPIN, 0L, 5, 0, 12, 8},
-		{100, 101, 0, 0x0L, LTEXT, (void*)"plot user defined function", 10, 24, 100, 8},
-		{101, 102, 0, 0x0L, RTEXT, (void*)"where x=", 10, 40, 28, 8}, 
-		{102, 103, 0, 0x0L, EDVAL1, &x1, 38, 40, 25, 10},
-		{103, 104, 0, 0x0L, RTEXT, (void*)"until", 61, 40, 17, 8}, 
-		{104, 105, 0, 0x0L, EDVAL1, &x2, 78, 40, 25, 10},
-		{105, 106, 0, 0x0L, RTEXT, (void*)"step", 102, 40, 17, 8}, 
-		{106, 107, 0, 0x0L, EDVAL1, &xstep, 119, 40, 25, 10},
-		{107, 200, 0, 0x0L, RTEXT, (void*)"y=", 10, 56, 10, 8}, 
-		{200, 0, 0, 0x0L, TEXTBOX, (void*)cmdxy, 22, 54, 122, 40},
-		{500, 0, 0, NOSELECT, ODBUTTON, (void*)OD_linedef, 15, 25, 130, 100},
-		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 170, 45, 15, 15},
-		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 170, 60, 15, 15},
-		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 170, 75, 15, 15},
-		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 170, 90, 15, 15}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, undo_level = *Undo.pcb;
-	bool bRet = false, bNew = (dl == 0L);
-	DWORD undo_flags = 0L;
-	LineDEF newLine;
-	double o_x1, n_x1, o_x2, n_x2, o_xstep, n_xstep;
-	anyOutput *cdisp = Undo.cdisp;
-
-	if(!parent) return false;
-	if(parent->Id == GO_FITFUNC) return parent->PropertyDlg();
-	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-	if(!(Dlg = new DlgRoot(FuncDlg, data))) return false;
-	if(!bNew) Dlg->ShowItem(10, false);
-	Dlg->GetValue(102, &o_x1);		n_x1 = o_x1;
-	Dlg->GetValue(104, &o_x2);		n_x2 = o_x2;
-	Dlg->GetValue(106, &o_xstep);	n_xstep = o_xstep;
-	hDlg = CreateDlgWnd("Function Plot", 50, 50, 400, 276, Dlg, 0x0L);
-	if(bNew) Dlg->SetCheck(4, 0L, true);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case 600:	case 601:	case 602:	case 603:
-			Undo.SetDisp(cdisp);
-			res = ExecDrawOrderButt(parent, this, res);
-			}
-		}while (res < 0);
-	Undo.SetDisp(cdisp);
-	if(res == 2) while(*Undo.pcb > undo_level)	Undo.Restore(true, cdisp);
-	else if(res == 1){					//OK pressed
-		if(bNew) {						//create function
-			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-			Dlg->GetValue(102, &x1);		Dlg->GetValue(104, &x2);
-			Dlg->GetValue(106, &xstep);		Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE);
-			cmdxy = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-			ReshapeFormula(&cmdxy);			bRet = Update(0L, 0L);
-			}
-		else {							//edit existing function
-			Dlg->GetValue(102, &n_x1);		Dlg->GetValue(104, &n_x2);
-			Dlg->GetValue(106, &n_xstep);
-			undo_flags = CheckNewFloat(&x1, o_x1, n_x1, this, undo_flags);
-			undo_flags = CheckNewFloat(&x2, o_x2, n_x2, this, undo_flags);
-			undo_flags = CheckNewFloat(&xstep, o_xstep, n_xstep, this, undo_flags);
-			if(Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE))
-				undo_flags = CheckNewString(&cmdxy, cmdxy, TmpTxt, this, undo_flags);
-			if(undo_flags & UNDO_CONTINUE){
-				Update(0L, UNDO_CONTINUE);		Command(CMD_MRK_DIRTY, 0L, 0L);
-				}
-			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-			if(cmpLineDEF(&Line, &newLine)) {
-				Undo.Line(parent, &Line, undo_flags);	undo_flags |= UNDO_CONTINUE;
-				memcpy(&Line, &newLine, sizeof(LineDEF));
-				}
-			bRet = (undo_flags & UNDO_CONTINUE) != 0;
-			}
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// fit function by nonlinear regression
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-FitFunc::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 22, 10, "Data"};
-	TabSHEET tab2 = {22, 59, 10, "Function"};
-	TabSHEET tab3 = {59, 82, 10, "Line"};
-	char text1[100], text2[100];
-	double iter;
-	bool bNew = (dl == 0L);
-	DlgInfo FuncDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 160, 10, 32, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 160, 25, 32, 12},
-		{3, 10, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
-		{4, 5, 400, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 149, 125},
-		{5, 6, 100, ISPARENT, SHEET, &tab2, 5, 10, 149, 125},
-		{6, 7, 500, ISPARENT, SHEET, &tab3, 5, 10, 149, 125},
-		{7, 0, 0, 0x0L, PUSHBUTTON, (void*)"Fit", 160, 123, 32, 12},
-		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
-		{100, 101, 0, 0x0L, LTEXT, (void*)"fit function by nonlinear regression", 10, 24, 100, 8},
-		{101, 102, 0, 0x0L, LTEXT, (void*)"parameters and initial values:", 10, 34, 100, 8},
-		{102, 150, 0, 0x0L, TEXTBOX, (void*)parxy, 22, 44, 122, 25},
-		{150, 151, 0, 0x0L, LTEXT, (void*)"function, y=f(x):", 10, 72, 10, 8}, 
-		{151, 152, 0, 0x0L, RTEXT, (void*)"y=", 10, 86, 10, 8}, 
-		{152, 153, 0, 0x0L, RTEXT, (void*)"converg.:", 20, 118, 26, 8}, 
-		{153, 154, 0, 0x0L, EDVAL1, &conv, 46, 118, 25, 10},
-		{154, 155, 0, 0x0L, RTEXT, (void*)"iterations:", 72, 118, 47, 8}, 
-		{155, 200, 0, 0x0L, EDVAL1, &iter, 119, 118, 25, 10},
-		{200, 0, 0, 0x0L, TEXTBOX, (void*)cmdxy, 22, 84, 122, 30},
-		{400, 401, 0, 0x0L, LTEXT, (void*)"range for X Data", 10, 30, 60, 8},
-		{401, 402, 0, 0x0L, RANGEINPUT, (void*)text1, 20, 40, 100, 10},
-		{402, 403, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 55, 60, 8},
-		{403, 404, 0, 0x0L, RANGEINPUT, (void*)text2, 20, 65, 100, 10},
-		{404, 405, 0, CHECKED, CHECKBOX, (void*)"draw symbols", 20, 95, 60, 8},
-		{405, 0, 0, HIDDEN, LTEXT, 0L, 20, 95, 60, 8},
-		{500, 550, 501, CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{501, 502, 0, 0x0L, RTEXT, (void*)"plot x=", 10, 30, 28, 8}, 
-		{502, 503, 0, 0x0L, EDVAL1, &x1, 38, 30, 25, 10},
-		{503, 504, 0, 0x0L, RTEXT, (void*)"until", 61, 30, 17, 8}, 
-		{504, 505, 0, 0x0L, EDVAL1, &x2, 78, 30, 25, 10},
-		{505, 506, 0, 0x0L, RTEXT, (void*)"step", 102, 30, 17, 8}, 
-		{506, 0, 0, 0x0L, EDVAL1, &xstep, 119, 30, 25, 10},
-		{550, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_linedef, 15, bNew ? 35:45, 130, 100}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, undo_level = *Undo.pcb, i, j, k, l, cs;
-	size_t cb;
-	bool bRet = false, bContinue = false;
-	DWORD undo_flags = 0L;
-	LineDEF newLine;
-	double tmp, tmpy, o_x1, n_x1, o_x2, n_x2, o_xstep, n_xstep, n_chi2=chi2;
-	AccRange *rX, *rY;
-	char *o_cmdxy, *o_parxy, *tmp_char;
-	anyOutput *cdisp = Undo.cdisp;
-	anyResult *ares;
-
-	if(!parent || !data) return false;
-	if(!(o_cmdxy = (char*)memdup(cmdxy, (int)strlen(cmdxy)+1, 0))) return false;;
-	if(!(o_parxy = (char*)memdup(parxy, (int)strlen(parxy)+1, 0))) return false;;
-	UseRangeMark(data, 1, text1, text2);
-	iter = (double)maxiter;
-	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-	if(!(Dlg = new DlgRoot(FuncDlg, data))) return false;
-	if(!bNew){
-		Dlg->ShowItem(10, false);		Dlg->Activate(401, false);
-		Dlg->Activate(403, false);		Dlg->SetCheck(6, 0L, true);
-		Dlg->SetCheck(4, 0L, false);	Dlg->ShowItem(404, false);
-		if(chi2 > 0.0) {
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "Chi 2 = %g", chi2);
-#else
-			sprintf(TmpTxt, "Chi 2 = %g", chi2);
-#endif
-			Dlg->SetText(405,TmpTxt);	Dlg->ShowItem(405, true);
-			}
-		}
-	else {
-		Dlg->ShowItem(500, false);
-		}
-	Dlg->GetValue(502, &o_x1);		n_x1 = o_x1;
-	Dlg->GetValue(504, &o_x2);		n_x2 = o_x2;
-	Dlg->GetValue(506, &o_xstep);	n_xstep = o_xstep;
-	hDlg = CreateDlgWnd("Fit Function to Data", 50, 50, 400, 306, Dlg, 0x0L);
-	if(bNew) Dlg->SetCheck(4, 0L, true);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(Dlg->GetCheck(10)) res = -1;
-			if(bContinue) res = -1;
-			bContinue = false;
-			break;
-		case 1:
-			if(!bNew){
-				if(Dlg->GetText(102, TmpTxt, TMP_TXT_SIZE)) {
-					if(parxy = (char*)realloc(parxy, (cb = strlen(TmpTxt)+2)))
-						rlp_strcpy(parxy, (int)cb, TmpTxt);
-					}
-				if(Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE)) {
-					if(cmdxy = (char*)realloc(cmdxy, (cb = strlen(TmpTxt)+2)))
-						rlp_strcpy(cmdxy, (int)cb, TmpTxt);
-					}
-				ReshapeFormula(&parxy);		ReshapeFormula(&cmdxy);
-				dirty = true;
-				break;
-				}
-		case 7:								//Start: do nonlinear regression
-			Undo.SetDisp(cdisp);
-			if(Dlg->GetCheck(5)) {			//  the function tab must be shown
-				if(Dlg->CurrDisp) Dlg->CurrDisp->MouseCursor(MC_WAIT, true);
-				Dlg->GetText(401, text1, 100);		Dlg->GetText(403, text2, 100);
-				if(Dlg->GetText(102, TmpTxt, TMP_TXT_SIZE)) {
-					if(parxy = (char*)realloc(parxy, (cb = strlen(TmpTxt)+2)))
-						rlp_strcpy(parxy, (int)cb, TmpTxt);
-					}
-				if(Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE)) {
-					if(cmdxy = (char*)realloc(cmdxy, (cb = strlen(TmpTxt)+2)))
-						rlp_strcpy(cmdxy, (int)cb, TmpTxt);
-					}
-				Dlg->GetValue(153, &conv);	Dlg->GetValue(155, &iter);
-				ReshapeFormula(&parxy);		ReshapeFormula(&cmdxy);
-				do_formula(data, 0L);		//clear any error condition
-				ares = do_formula(data, parxy);
-				if(ares->type != ET_VALUE) {
-					ErrorBox("Syntax Error in parameters.");
-					bContinue = true;	res = -1;
-					break;
-					}
-				ares = do_formula(data, cmdxy);
-				if(ares->type != ET_VALUE) {
-					ErrorBox("Syntax Error in formula.");
-					bContinue = true;	res = -1;
-					break;
-					}
-				i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE-2, parxy);	i += rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, ";x=1;");
-				rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, cmdxy);	yywarn(0L, true);
-				ares = do_formula(data, TmpTxt);
-				if(tmp_char = yywarn(0L, false)) {
-					ErrorBox(tmp_char);
-					bContinue = true;	res = -1;
-					break;
-					}
-				i = do_fitfunc(data, text1, text2, 0L, &parxy, cmdxy, conv, (int)iter, &chi2);
-				Dlg->SetText(102, parxy);
-				if(i >1 || res == 7) {
-#ifdef USE_WIN_SECURE
-					sprintf_s(TmpTxt, TMP_TXT_SIZE, "The Levenberg-Marquart algorithm\nexited after %d iterations.\n\nChi2 = %g", i, chi2);
-#else
-					sprintf(TmpTxt, "The Levenberg-Marquart algorithm\nexited after %d iterations.\n\nChi2 = %g", i, chi2);
-#endif
-					InfoBox(TmpTxt);
-					}
-				bContinue = true;
-				if(res == 7) res = -1;
-				if(Dlg->CurrDisp) Dlg->CurrDisp->MouseCursor(MC_ARROW, true);
-				}
-			else {							//diplay function tab first
-				Dlg->SetCheck(5, 0L, true);
-				res = -1;
-				}
-			break;
-			}
-		}while (res < 0);
-	Undo.SetDisp(cdisp);
-	while(*Undo.pcb > undo_level)	Undo.Pop(cdisp);
-	if(res == 1){						//OK pressed
-		//get ranges for x- and y data (again).
-		chi2 = n_chi2;
-		if(Dlg->GetText(401, text1, 100) && (ssXref = (char*)realloc(ssXref, (cb = strlen(text1)+2))))
-			rlp_strcpy(ssXref, (int)cb, text1);
-		if(Dlg->GetText(403, text2, 100) && (ssYref = (char*)realloc(ssYref, (cb = strlen(text2)+2))))
-			rlp_strcpy(ssYref, (int)cb, text2);
-		if(bNew) {						//create function
-			if(!(rX = new AccRange(text1)) || ! (rY = new AccRange(text2))) return false;
-			i = rX->CountItems();	maxiter = int(iter);
-			if(Dlg->GetCheck(404)) Symbols = (Symbol**)calloc(i, sizeof(Symbol*));
-			x1 = HUGE_VAL;	x2 = -HUGE_VAL;	cs = 0;
-			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
-			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
-			rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
-			rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
-			do {
-				if(data->GetValue(j, i, &tmp) && data->GetValue(l, k, &tmpy)){
-					if(tmp < x1) x1 = tmp;			if(tmp > x2) x2 = tmp;
-					if(Symbols) Symbols[cs++] = new Symbol(this, data, tmp, tmpy, SYM_CIRCLE, i, j, k, l);
-					nPoints = cs;					CheckBounds(tmp, tmpy);
-					}
-				}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
-			delete rX;	delete rY;
-			if(x1 >= x2 || !(dl = new Function(this, data, "Fitted function"))){
-				CloseDlgWnd(hDlg);
-				delete Dlg;
-				return false;
-				}
-			xstep = (x2 - x1)/100.0;
-			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-			dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
-			dl->SetSize(SIZE_XSTEP, xstep);			dl->Command(CMD_SETFUNC, cmdxy, 0L);
-			dl->Command(CMD_SETPARAM, parxy, 0L);	dl->Command(CMD_SET_LINE, &Line, 0L);
-			dl->Command(CMD_AUTOSCALE, 0L, 0L);		CheckBounds(dl->Bounds.Xmin, dl->Bounds.Ymin);
-			CheckBounds(dl->Bounds.Xmax, dl->Bounds.Ymax);
-			Command(CMD_ENDDIALOG, 0L, 0L);			bRet = true;
-			}
-		else {							//edit existing function
-			Dlg->GetValue(502, &n_x1);		Dlg->GetValue(504, &n_x2);
-			Dlg->GetValue(506, &n_xstep);
-			undo_flags = CheckNewFloat(&x1, o_x1, n_x1, this, undo_flags);
-			undo_flags = CheckNewFloat(&x2, o_x2, n_x2, this, undo_flags);
-			undo_flags = CheckNewFloat(&xstep, o_xstep, n_xstep, this, undo_flags);
-			if(undo_flags){
-				dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
-				dl->SetSize(SIZE_XSTEP, xstep);			dl->Command(CMD_SETFUNC, cmdxy, 0L);
-				dl->Command(CMD_SETPARAM, parxy, 0L);	dl->Command(CMD_SET_LINE, &Line, 0L);
-				dl->Update(0L, UNDO_CONTINUE);
-				}
-			undo_flags = CheckNewString(&parxy, o_parxy, parxy, this, undo_flags);
-			undo_flags = CheckNewString(&cmdxy, o_cmdxy, cmdxy, this, undo_flags);
-			if(undo_flags & UNDO_CONTINUE) {
-				Undo.ValInt(parent, (int*)&dirty, undo_flags);
-				Command(CMD_MRK_DIRTY, 0L, 0L);
-				}
-			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-			if(cmpLineDEF(&Line, &newLine)) {
-				Undo.Line(parent, &Line, undo_flags);	undo_flags |= UNDO_CONTINUE;
-				memcpy(&Line, &newLine, sizeof(LineDEF));
-				}
-			bRet = (undo_flags & UNDO_CONTINUE) != 0;
-			Command(CMD_ENDDIALOG, 0L, 0L);
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(o_parxy) free(o_parxy);		if(o_cmdxy) free(o_cmdxy);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Create a new normal quantile plot
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *NormQuantDlg_Tmpl = 
-	"1,2,,DEFAULT,PUSHBUTTON,-1,148,10,45,12\n"
-	"2,3,,,PUSHBUTTON,-2,148,25,45,12\n"
-	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"4,10,100,ISPARENT | CHECKED,SHEET,1,5,10,130,80\n"
-	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-	"100,101,,,LTEXT,2,10,30,60,8\n"
-	"101,,,LASTOBJ,RANGEINPUT,-15,20,40,100,10";
-
-bool
-NormQuant::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 25, 10, "Data"};
-	DlgInfo *QuantDlg;
-	void *dyndata[] = {(void*)&tab1, (void*)"range for variables"};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res;
-	char *mrk;
-	bool bContinue = false, bRet = false;
-
-	if(!parent || !data) return false;
-	if(!(QuantDlg = CompileDialog(NormQuantDlg_Tmpl, dyndata))) return false;
-	if(data->Command(CMD_GETMARK, &mrk, 0L))rlp_strcpy(TmpTxt, TMP_TXT_SIZE, mrk);
-	else UseRangeMark(data, 1, TmpTxt);
-	if(!(Dlg = new DlgRoot(QuantDlg, data)))return false;
-	hDlg = CreateDlgWnd("Normal Quantiles Plot", 50, 50, 420, 220, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-			}
-		}while (res < 0);
-	if(res == 1 && Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)){
-		ssRef = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-		bRet = ProcessData();
-		}
-	CloseDlgWnd(hDlg);	delete Dlg;	free(QuantDlg);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Create a three dimensional graph
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *AddAxis3D_Tmpl =
-		"1,2,,DEFAULT, PUSHBUTTON,-1,148,10,45,12\n"
-		"2,3,,,PUSHBUTTON,-2,148,25,45,12\n"
-		"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"4,5,50,TOUCHEXIT | ISPARENT,SHEET,1,5,10,130,148\n"
-		"5,6,200,ISPARENT | CHECKED,SHEET,2,5,10,130,148\n"
-		"6,20,300,ISPARENT,SHEET,3,5,10,130,148\n"
-		"20,,,NOSELECT,ODBUTTON,22,142,65,45,45\n"
-		"50,51,100,ISPARENT | CHECKED,GROUPBOX,4,10,30,120,36\n"
-		"51,120,,,CHECKBOX,5,17,37,80,8\n"
-		"100,101,,,RTEXT,6,10,51,35,8\n"
-		"101,102,,,EDVAL1,7,48,51,32,10\n"
-		"102,103,,,CTEXT,8,81,51,11,8\n"
-		"103,,,,EDVAL1,9,93,51,32,10\n"
-		"120,,121,ISPARENT | CHECKED,GROUPBOX,10,10,72,120,20\n"
-		"121,122,,,RTEXT,11,10,77,25,8\n"
-		"122,123,,,EDVAL1,12,37,77,25,10\n"
-		"123,124,,,LTEXT,-3,63,77,10,8\n"
-		"124,125,,,RTEXT,-11,73,77,25,8\n"
-		"125,130,,OWNDIALOG,COLBUTT,14,100,77,25,10\n"
-		"130,131,,ISPARENT | CHECKED,GROUPBOX,15,10,98,120,20\n"
-		"131,,,,EDTEXT,0,15,103,110,10\n"
-		"200,201,,,LTEXT,16,10,23,70,9\n"
-		"201,202,,CHECKED | EXRADIO,ODBUTTON,17,20,35,25,25\n"
-		"202,203,,EXRADIO,ODBUTTON,17,45,35,25,25\n"
-		"203,204,,EXRADIO,ODBUTTON,17,70,35,25,25\n"
-		"204,205,,EXRADIO,ODBUTTON,17,95,35,25,25\n"
-		"205,206,,EXRADIO,ODBUTTON,17,20,60,25,25\n"
-		"206,207,,EXRADIO,ODBUTTON,17,45,60,25,25\n"
-		"207,208,,EXRADIO,ODBUTTON,17,70,60,25,25\n"
-		"208,209,,EXRADIO,ODBUTTON,17,95,60,25,25\n"
-		"209,210,,EXRADIO,ODBUTTON,17,20,85,25,25\n"
-		"210,211,,EXRADIO,ODBUTTON,17,45,85,25,25\n"
-		"211,212,,EXRADIO,ODBUTTON,17,70,85,25,25\n"
-		"212,213,,EXRADIO,ODBUTTON,17,95,85,25,25\n"
-		"213,214,,,LTEXT,-12,20,120,70,9\n"
-		"214,215,,,LTEXT,-13,20,132,70,9\n"
-		"215,216,,,LTEXT,-14,20,144,70,9\n"
-		"216,217,250,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"217,218,260,HIDDEN | ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"218,,270,HIDDEN |ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"250,251,,,LTEXT,19,10,110,70,9\n"
-		"251,252,,,EDVAL1,0,58,120,32,10\n"
-		"252,253,,,LTEXT,-3,92,120,20,9\n"
-		"253,254,,,EDVAL1,0,43,132,32,10\n"
-		"254,255,,,CTEXT,-7,75,132,7,9\n"
-		"255,256,,,EDVAL1,0,82,132,32,10\n"
-		"256,257,,,LTEXT,-3,116,132,20,9\n"
-		"257,268,,,EDVAL1,0,58,144,32,10\n"
-		"260,261,,,LTEXT,20,10,110,70,9\n"
-		"261,262,,,EDVAL1,0,43,120,32,10\n"
-		"262,263,,,CTEXT,-7,75,120,7,9\n"
-		"263,264,,,EDVAL1,0,82,120,32,10\n"
-		"264,265,,,LTEXT,-3,116,120,20,9\n"
-		"265,266,,,EDVAL1,0,58,132,32,10\n"
-		"266,267,,,LTEXT,-3,92,132,20,9\n"
-		"267,268,,,EDVAL1,0,58,144,32,10\n"
-		"268,,,,LTEXT,-3,92,144,20,9\n"
-		"270,271,,,LTEXT,21,10,110,70,9\n"
-		"271,272,,,EDVAL1,0,58,120,32,10\n"
-		"272,273,,,LTEXT,-3,92,120,20,9\n"
-		"273,274,,,EDVAL1,0,58,132,32,10\n"
-		"274,275,,,LTEXT,-3,92,132,20,9\n"
-		"275,276,,,EDVAL1,0,43,144,32,10\n"
-		"276,277,,,CTEXT,-7,75,144,7,9\n"
-		"277,278,,,EDVAL1,0,82,144,32,10\n"
-		"278,,,,LTEXT,-3,116,144,20,9\n"
-		"300,,,LASTOBJ | NOSELECT,ODBUTTON,18,15,30,110,140";
-bool
-Plot3D::AddAxis()
-{
-	TabSHEET tab1 = {0, 25, 10, "Axis"};
-	TabSHEET tab2 = {25, 52, 10, "Style"};
-	TabSHEET tab3 = {52, 78, 10, "Plots"};
-	AxisDEF axis, ax_def[12], *caxdef;
-	double sizAxLine = DefSize(SIZE_AXIS_LINE);
-	DWORD colAxis = 0x0;
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)" scaling ", (void*)" automatic scaling",
-		(void*)"axis from", (void*)&axis.min, (void*)"to", (void*)&axis.max, (void*)" line ", (void*)"width",
-		(void*)&sizAxLine, (void*)0L, (void *)&colAxis, (void*)" axis label ", (void*)"select a template:",
-		(void*)(OD_NewAxisTempl3D), (void*)OD_axisplot, (void*)"y-axis at:", (void*)"x-axis at:", (void*)"z-axis at:",
-		(void*)(OD_AxisDesc3D)};
-	DlgInfo *NewAxisDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, j, res, currTempl = 201, ax_type = 2, tick_type = 2;
-	double tlb_dx, tlb_dy, lb_x, lb_y;
-	TextDEF label_def, tlbdef;
-	anyOutput *cdisp = Undo.cdisp;
-	Axis *the_new, **tmpAxes;
-	bool bAxis = false, bRet = false;
-	char **names;
-	GraphObj **somePlots;
-	Label *label;
-
-	if(!Axes || nAxes < 3 || !(NewAxisDlg = CompileDialog(AddAxis3D_Tmpl, dyndata))) return false;
-	lb_y = 0.0;		lb_x = DefSize(SIZE_AXIS_TICKS)*4.0;
-	tlb_dy = 0.0;	tlb_dx = -DefSize(SIZE_AXIS_TICKS)*2.0;
-	tlbdef.ColTxt = colAxis;				tlbdef.ColBg = 0x00ffffffL;
-	tlbdef.RotBL = tlbdef.RotCHAR = 0.0f;	tlbdef.fSize = DefSize(SIZE_TICK_LABELS);
-	tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
-	tlbdef.Style = TXS_NORMAL;				tlbdef.Mode = TXM_TRANSPARENT;
-	tlbdef.Font = FONT_HELVETICA;			tlbdef.text = 0L;
-	if(!(names = (char**)calloc(nscp+2, sizeof(char*))))return false;
-	if(!(somePlots = (GraphObj**)calloc(nscp+2, sizeof(GraphObj*))))return false;
-	for(i = 0; i < 12; i++) {
-		ax_def[i].flags = AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK;
-		ax_def[i].owner = 0L;		ax_def[i].breaks = 0L;
-		ax_def[i].Center.fx = ax_def[i].Center.fy = ax_def[i].Radius = 0.0;
-		ax_def[i].nBreaks = 0L;
-		}
-	//y-axes
-	ax_def[0].min = ax_def[1].min = ax_def[2].min = ax_def[3].min = (caxdef = Axes[1]->GetAxis())->min;
-	ax_def[0].max = ax_def[1].max = ax_def[2].max = ax_def[3].max = caxdef->max;
-	ax_def[0].Start = ax_def[1].Start = ax_def[2].Start = ax_def[3].Start = caxdef->Start;
-	ax_def[0].Step = ax_def[1].Step = ax_def[2].Step = ax_def[3].Step = caxdef->Step;
-	ax_def[0].loc[0].fy  = ax_def[1].loc[0].fy = ax_def[2].loc[0].fy = ax_def[3].loc[0].fy = caxdef->loc[0].fy;
-	ax_def[0].loc[1].fy  = ax_def[1].loc[1].fy = ax_def[2].loc[1].fy = ax_def[3].loc[1].fy = caxdef->loc[1].fy;
-	ax_def[0].loc[0].fx = ax_def[0].loc[1].fx = ax_def[3].loc[0].fx = ax_def[3].loc[1].fx = cu1.fx;
-	ax_def[1].loc[0].fx = ax_def[1].loc[1].fx = ax_def[2].loc[0].fx = ax_def[2].loc[1].fx = cu2.fx;
-	ax_def[0].loc[0].fz = ax_def[0].loc[1].fz = ax_def[1].loc[0].fz = ax_def[1].loc[1].fz = cu2.fz;
-	ax_def[2].loc[0].fz = ax_def[2].loc[1].fz = ax_def[3].loc[0].fz = ax_def[3].loc[1].fz = cu1.fz;
-	ax_def[1].flags = ax_def[2].flags = AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_POSTICKS;
-	ax_def[0].flags = ax_def[3].flags = AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_NEGTICKS;
-	//x-axes
-	ax_def[4].min = ax_def[6].min = ax_def[8].min = ax_def[10].min = (caxdef = Axes[0]->GetAxis())->min;
-	ax_def[4].max = ax_def[6].max = ax_def[8].max = ax_def[10].max = caxdef->max;
-	ax_def[4].Start = ax_def[6].Start = ax_def[8].Start = ax_def[10].Start = caxdef->Start;
-	ax_def[4].Step = ax_def[6].Step = ax_def[8].Step = ax_def[10].Step = caxdef->Step;
-	ax_def[4].loc[0].fx  = ax_def[6].loc[0].fx = ax_def[8].loc[0].fx = ax_def[10].loc[0].fx = caxdef->loc[0].fx;
-	ax_def[4].loc[1].fx  = ax_def[6].loc[1].fx = ax_def[8].loc[1].fx = ax_def[10].loc[1].fx = caxdef->loc[1].fx;
-	ax_def[4].loc[0].fy = ax_def[4].loc[1].fy = ax_def[6].loc[0].fy = ax_def[6].loc[1].fy = cu1.fy;
-	ax_def[8].loc[0].fy = ax_def[8].loc[1].fy = ax_def[10].loc[0].fy = ax_def[10].loc[1].fy = cu2.fy;
-	ax_def[4].loc[0].fz = ax_def[4].loc[1].fz = ax_def[8].loc[0].fz = ax_def[8].loc[1].fz = cu2.fz;
-	ax_def[6].loc[0].fz = ax_def[6].loc[1].fz = ax_def[10].loc[0].fz = ax_def[10].loc[1].fz = cu1.fz;
-	ax_def[4].flags = ax_def[6].flags = AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_NEGTICKS;
-	ax_def[8].flags = ax_def[10].flags = AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_POSTICKS;
-	//z-axes
-	ax_def[5].min = ax_def[7].min = ax_def[9].min = ax_def[11].min = (caxdef = Axes[2]->GetAxis())->min;
-	ax_def[5].max = ax_def[7].max = ax_def[9].max = ax_def[11].max = caxdef->max;
-	ax_def[5].Start = ax_def[7].Start = ax_def[9].Start = ax_def[11].Start = caxdef->Start;
-	ax_def[5].Step = ax_def[7].Step = ax_def[9].Step = ax_def[11].Step = caxdef->Step;
-	ax_def[5].loc[0].fz  = ax_def[7].loc[0].fz = ax_def[9].loc[0].fz = ax_def[11].loc[0].fz = caxdef->loc[0].fz;
-	ax_def[5].loc[1].fz  = ax_def[7].loc[1].fz = ax_def[9].loc[1].fz = ax_def[11].loc[1].fz = caxdef->loc[1].fz;
-	ax_def[5].loc[0].fx = ax_def[5].loc[1].fx = ax_def[9].loc[0].fx = ax_def[9].loc[1].fx = cu2.fx;
-	ax_def[7].loc[0].fx = ax_def[7].loc[1].fx = ax_def[11].loc[0].fx = ax_def[11].loc[1].fx = cu1.fx;
-	ax_def[5].loc[0].fy = ax_def[5].loc[1].fy = ax_def[7].loc[0].fy = ax_def[7].loc[1].fy = cu1.fy;
-	ax_def[9].loc[0].fy = ax_def[9].loc[1].fy = ax_def[11].loc[0].fy = ax_def[11].loc[1].fy = cu2.fy;
-	ax_def[5].flags = ax_def[9].flags = AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_POSTICKS;
-	ax_def[7].flags = ax_def[11].flags = AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_NEGTICKS;
-	//the default axis is the first
-	memcpy(&axis, &ax_def[0], sizeof(AxisDEF));
-	if(names[0] = (char*)malloc(10)) rlp_strcpy(names[0], 10, "[none]");
-	for(i = 0, j = 1; i < nscp; i++) {
-		if(Sc_Plots[i] && Sc_Plots[i]->name){
-			names[j] = (char*)memdup(Sc_Plots[i]->name, (int)strlen(Sc_Plots[i]->name)+1, 0);
-			somePlots[j++] = Sc_Plots[i];
-			}
-		}
-	OD_axisplot(OD_ACCEPT, 0L, 0L, 0L, names, 0);
-	if(!(Dlg = new DlgRoot(NewAxisDlg, data)))return false;
-	Dlg->SetValue(251, ax_def[0].loc[0].fx);		Dlg->SetValue(253, ax_def[0].loc[0].fy);
-	Dlg->SetValue(255, ax_def[0].loc[1].fy);		Dlg->SetValue(257, ax_def[0].loc[0].fz);
-	if(!nscp){						//must be root plot3d to link to plot
-		Dlg->ShowItem(6, false);
-		}
-	hDlg = CreateDlgWnd("Add Axis to 3D Plot", 50, 50, 400, 360, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res){
-		case 4:											//the axis sheet
-			res = -1;
-			bAxis = true;
-			break;
-		//axis templates
-		case 201:	case 202:	case 203:	case 204:	case 205:	case 206:
-		case 207:	case 208:	case 209:	case 210:	case 211:	case 212:
-			i = currTempl-201;
-			switch(currTempl){
-				case 201:	case 202:	case 203:	case 204:		//prevoius is y-template
-					Dlg->GetValue(251, &axis.loc[0].fx);	Dlg->GetValue(253, &axis.loc[0].fy);
-					Dlg->GetValue(255, &axis.loc[1].fy);	Dlg->GetValue(257, &axis.loc[0].fz);
-					axis.loc[1].fx = axis.loc[0].fx;		axis.loc[1].fz = axis.loc[0].fz;
-					memcpy(&ax_def[i], &axis, sizeof(AxisDEF));
-					break;
-				case 205:	case 207:	case 209:	case 211:		//prevoius is x-template
-					Dlg->GetValue(261, &axis.loc[0].fx);	Dlg->GetValue(263, &axis.loc[1].fx);
-					Dlg->GetValue(265, &axis.loc[0].fy);	Dlg->GetValue(267, &axis.loc[0].fz);
-					axis.loc[1].fy = axis.loc[0].fy;		axis.loc[1].fz = axis.loc[0].fz;
-					memcpy(&ax_def[i], &axis, sizeof(AxisDEF));
-					break;
-				case 206:	case 208:	case 210:	case 212:		//previous is z-template
-					Dlg->GetValue(271, &axis.loc[0].fx);	Dlg->GetValue(273, &axis.loc[0].fy);
-					Dlg->GetValue(275, &axis.loc[0].fz);	Dlg->GetValue(277, &axis.loc[1].fz);
-					axis.loc[1].fx = axis.loc[0].fx;		axis.loc[1].fy = axis.loc[0].fy;
-					memcpy(&ax_def[i], &axis, sizeof(AxisDEF));
-					break;
-				}
-			i = res-201;
-			switch (res) {
-			case 201:	case 202:	case 203:	case 204:			//y-template
-				Dlg->ShowItem(216, true);		Dlg->ShowItem(217, false);
-				Dlg->ShowItem(218, false);
-				Dlg->SetValue(251, ax_def[i].loc[0].fx);	Dlg->SetValue(253, ax_def[i].loc[0].fy);
-				Dlg->SetValue(255, ax_def[i].loc[1].fy);	Dlg->SetValue(257, ax_def[i].loc[0].fz);
-				ax_type = tick_type = 2;	tlb_dy = 0.0;
-				if(res == 202 || res == 203){
-					tlb_dx = DefSize(SIZE_AXIS_TICKS)*2.0;
-					tlbdef.Align = TXA_VCENTER | TXA_HLEFT;
-					}
-				else {
-					tlb_dx = -DefSize(SIZE_AXIS_TICKS)*2.0;
-					tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
-					}
-				break;
-			case 205:	case 207:	case 209:	case 211:			//x-template
-				Dlg->ShowItem(216, false);		Dlg->ShowItem(217, true);
-				Dlg->ShowItem(218, false);
-				Dlg->SetValue(261, ax_def[i].loc[0].fx);	Dlg->SetValue(263, ax_def[i].loc[1].fx);
-				Dlg->SetValue(265, ax_def[i].loc[0].fy);	Dlg->SetValue(267, ax_def[i].loc[0].fz);
-				ax_type = 1;	tick_type = 3; tlb_dx = 0;
-				if(res == 205 || res == 207){
-					tlb_dy = DefSize(SIZE_AXIS_TICKS)*2.0;
-					tlbdef.Align = TXA_VTOP | TXA_HCENTER;
-					}
-				else {
-					tlb_dy = -DefSize(SIZE_AXIS_TICKS)*2.0;
-					tlbdef.Align = TXA_VBOTTOM | TXA_HCENTER;
-					}
-				break;
-			case 206:	case 208:	case 210:	case 212:			//z-template
-				Dlg->ShowItem(216, false);		Dlg->ShowItem(217, false);
-				Dlg->ShowItem(218, true);
-				Dlg->SetValue(271, ax_def[i].loc[0].fx);	Dlg->SetValue(273, ax_def[i].loc[0].fy);
-				Dlg->SetValue(275, ax_def[i].loc[0].fz);	Dlg->SetValue(277, ax_def[i].loc[1].fz);
-				ax_type = 3;	tick_type = 2;	tlb_dy = 0;
-				if(res == 206 || res == 210){
-					tlb_dx = DefSize(SIZE_AXIS_TICKS)*2.0;
-					tlbdef.Align = TXA_VCENTER | TXA_HLEFT;
-					}
-				else {
-					tlb_dx = -DefSize(SIZE_AXIS_TICKS)*2.0;
-					tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
-					}
-				break;
-				}
-			memcpy(&axis, &ax_def[res-201], sizeof(AxisDEF));
-			currTempl = res;
-			Dlg->DoPlot(0L);
-			res = -1;
-			break;
-			}
-		}while (res < 0);
-	if(res == 1) {
-		Undo.SetDisp(cdisp);
-		Dlg->GetValue(122, &sizAxLine);				Dlg->GetColor(125, &colAxis);
-		Dlg->GetValue(101, &axis.min);				Dlg->GetValue(103, &axis.max);
-		axis.Start = axis.min;						axis.Center.fx = axis.Center.fy = 0.0;
-		axis.nBreaks = 0;			axis.breaks = 0L;		axis.Radius = 0.0;	
-		tlbdef.ColTxt =	colAxis;
-		label_def.ColTxt = colAxis;					label_def.ColBg = 0x00ffffffL;
-		label_def.fSize = DefSize(SIZE_TICK_LABELS)*1.2f;	label_def.RotBL = 0.0f;
-		label_def.RotCHAR = 0.0f;								label_def.iSize = 0;
-		label_def.Align = TXA_VTOP | TXA_HCENTER;		label_def.Mode = TXM_TRANSPARENT;
-		label_def.Style = TXS_NORMAL;	label_def.Font = FONT_HELVETICA;
-		if(Dlg->GetCheck(51)) axis.flags |= AXIS_AUTOSCALE;
-		if(the_new = new Axis(this, data, &axis, axis.flags)){
-			the_new->SetSize(SIZE_TLB_YDIST, tlb_dy);	the_new->SetSize(SIZE_TLB_XDIST, tlb_dx); 
-			the_new->SetSize(SIZE_AXIS_LINE, sizAxLine);
-			the_new->SetColor(COL_AXIS, colAxis);
-			the_new->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			the_new->SetSize(SIZE_LB_XDIST, lb_x);		the_new->SetSize(SIZE_LB_YDIST, lb_y);
-			the_new->type = ax_type;
-			the_new->Command(CMD_TICK_TYPE, &tick_type, 0L);
-			if(Dlg->GetText(131, TmpTxt, TMP_TXT_SIZE)) label_def.text = TmpTxt;
-			else label_def.text = 0L;
-			if(label = new Label(Axes[0], data, (axis.loc[0].fx + axis.loc[1].fx)/2.0,
-				(axis.loc[0].fy + axis.loc[1].fy)/2.0, &label_def, 
-				label_def.RotBL < 45.0 ? LB_Y_PARENT : LB_X_PARENT)){
-				label->SetSize(SIZE_LB_XDIST, lb_x);	label->SetSize(SIZE_LB_YDIST, lb_y); 
-				if(the_new->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
-				else DeleteGO(label);
-				}
-			for(i = 0; i < nAxes && Axes[i]; i++);
-			if(i < nAxes) {
-				Undo.SetGO(this, (GraphObj**)(&Axes[i]), the_new, 0L);
-				bRet = true;
-				}
-			else {
-				if(tmpAxes = (Axis**)calloc(nAxes+1, sizeof(Axis*))){
-					memcpy(tmpAxes, Axes, nAxes * sizeof(Axis*));
-					Undo.ListGOmoved((GraphObj**)Axes, (GraphObj**)tmpAxes, nAxes);
-					Undo.SetGO(this, (GraphObj**)(&tmpAxes[nAxes]), the_new, 0L);
-					free(Axes);			Axes = tmpAxes;
-					i = nAxes++;		bRet = true;
-					}
-				}
-			CurrAxes = Axes;
-			if(bRet) {
-				OD_axisplot(OD_ACCEPT, 0L, 0L, (anyOutput*) &res, 0L, 0);
-				if(res && i) somePlots[res]->Command(CMD_USEAXIS, &i, 0L);
-				}
-			}
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;			free(NewAxisDlg);
-	if(names) {
-		for(j = 0; names[j]; j++) if(names[j]) free(names[j]);
-		free(names);
-		}
-	if(somePlots) free(somePlots);
-	return bRet;
-}
-
-static char *AddPlot3Dtmpl =
-		"1,2,,DEFAULT,PUSHBUTTON,-1,150,10,45,12\n"
-		"2,3,,,PUSHBUTTON,-2,150,25,45,12\n"
-		"3,,560,ISPARENT | CHECKED,GROUPBOX,1,5,10,140,70\n"
-		"560,561,,EXRADIO | CHECKED,ODBUTTON,2,12,20,25,25\n"
-		"561,562,,EXRADIO,ODBUTTON,2,37,20,25,25\n"
-		"562,563,,EXRADIO,ODBUTTON,2,62,20,25,25\n"
-		"563,564,,EXRADIO,ODBUTTON,2,87,20,25,25\n"
-		"564,565,,EXRADIO,ODBUTTON,2,112,20,25,25\n"
-		"565,566,,EXRADIO,ODBUTTON,2,12,45,25,25\n"
-		"566,567,,EXRADIO,ODBUTTON,2,37,45,25,25\n"
-		"567,,,LASTOBJ | EXRADIO,ODBUTTON,2,62,45,25,25";
-
-bool
-Plot3D::AddPlot(int family)
-{
-	void *dyndata[] = {(void *)"  select template  ", (void*)(OD_PlotTempl)};
-	DlgInfo *PlotsDlg = CompileDialog(AddPlot3Dtmpl, dyndata);
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, cSel = 560;
-	bool bRet = false;
-	Plot *p;
-
-	if(!(Dlg = new DlgRoot(PlotsDlg, data)))return false;
-	hDlg = CreateDlgWnd("Add Plot", 50, 50, 410, 204, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 560:	case 561:	case 562:	case 563:	case 564:
-		case 565:	case 566:	case 567:
-			if(res == cSel) res = 1;
-			else {
-				cSel = res;		res = -1;
-				}
-			break;
-			}
-		}while (res < 0);
-	if(res == 1){						//OK pressed
-		switch (cSel) {
-		case 560:		p = new Scatt3D(this, data, 0x01);			break;
-		case 561:		p = new Scatt3D(this, data, 0x02);			break;
-		case 562:		p = new Scatt3D(this, data, 0x04);			break;
-		case 563:		p = new BubblePlot3D(this, data);			break;
-		case 564:		p = new Scatt3D(this, data, 0x2000);		break;
-		case 565:		p = new Func3D(this, data);					break;
-		case 566:		p = new FitFunc3D(this, data);				break;
-		case 567:		p = new Scatt3D(this, data, 0x4000);		break;
-		default:		p = 0L;										break;
-			}
-		if(p && p->PropertyDlg()) {
-			if(!(bRet = Command(CMD_DROP_PLOT, p, (anyOutput *)NULL))) delete p;
-			}
-		else if(p) delete p;
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;		free(PlotsDlg);
-	return bRet;
-}
-
-bool
-Plot3D::PropertyDlg()
-{
-	Plot *p;
-	bool bRet = false;
-
-	if(plots) {
-		//plots already created - jump to configuration dialog
-		return false;
-		}
-	if((p = new Scatt3D(this, data, crea_flags)) && p->PropertyDlg()) {
-		if(!(bRet = Command(CMD_DROP_PLOT, p, (anyOutput *)NULL))) DeleteGO(p);
-		}
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Create a 2.5 dimensional bar chart
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *Base25D_DlgTmpl = 
-		"1,2,,DEFAULT, PUSHBUTTON,-1,158,10,45,12\n"
-		"2,3,,,PUSHBUTTON,-2,158,25,45,12\n"
-		"3,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"10,11,100,ISPARENT | CHECKED,SHEET,1,5,10,140,100\n"
-		"11,12,200,ISPARENT,SHEET,2,5,10,140,100\n"
-		"12,20,300,ISPARENT,SHEET,3,5,10,140,100\n"
-		"20,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-		"100,101,,,LTEXT,4,15,30,60,8\n"
-		"101,152,,,RANGEINPUT,5,25,40,100,10\n"
-		"152,153,,ISPARENT | CHECKED,GROUPBOX,6,12,60,128,45\n"
-		"153,154,,,LTEXT,0,25,65,60,8\n"
-		"154,155,,,RANGEINPUT,5,25,75,100,10\n"
-		"155,156,,,PUSHBUTTON,-8,95,87,30,12\n"
-		"156,,,,PUSHBUTTON,-9,60,87,35,12\n"
-		"200,201,,,LTEXT,7,20,35,80,8\n"
-		"201,202,,,RTEXT,8,48,45,13,8\n"
-		"202,203,,,EDVAL1,9,65,45,25,10\n"
-		"203,204,,,RTEXT,10,48,57,13,8\n"
-		"204,,,,EDVAL1,11,65,57,25,10\n"
-		"300,301,,,RADIO1,12,15,35,80,9\n"
-		"301,302,,ODEXIT,COLBUTT,13,110,35,20,10\n"
-		"302,303,,CHECKED,RADIO1,14,15,55,80,9\n"
-		"303,304,,ODEXIT,COLBUTT,15,25,70,10,10\n"
-		"304,305,,ODEXIT,COLBUTT,16,37,70,10,10\n"
-		"305,306,,ODEXIT,COLBUTT,17,49,70,10,10\n"
-		"306,307,,ODEXIT,COLBUTT,18,61,70,10,10\n"
-		"307,308,,ODEXIT,COLBUTT,19,73,70,10,10\n"
-		"308,309,,ODEXIT,COLBUTT,20,85,70,10,10\n"
-		"309,310,,ODEXIT,COLBUTT,21,97,70,10,10\n"
-		"310,,,LASTOBJ | ODEXIT,COLBUTT,22,109,70,10,10";
-bool
-Chart25D::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 25, 10, "Data"};
-	TabSHEET tab2 = {25, 55, 10, "Details"};
-	TabSHEET tab3 = {55, 90, 10, "Scheme"};
-	static DWORD colarr[] = {0x000080ffL, 0x00ff8000L, 0x0000ff00L, 0x000000ffL,
-		0x00ff00ff, 0x00ffff00L, 0x0000ffff, 0x00c0c0c0};
-	static DWORD defcol = 0x00ffffffL;
-	double start_z = 1.0;
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)"range for common x values",
-		(void*)TmpTxt, (void*)" ranges for y values ", (void*)"distances:", (void*)"start z =", 
-		(void*)&start_z, (void*)"step =", (void*)&dspm.fz, (void*)" common color for columns:",
-		(void*)&defcol, (void*)" increment color scheme:", (void*)&colarr[0], (void*)&colarr[1],
-		(void*)&colarr[2], (void*)&colarr[3], (void*)&colarr[4], (void*)&colarr[5], (void*)&colarr[6],
-		(void*)&colarr[7]};
-	DlgInfo *Bar3D_Dlg = CompileDialog(Base25D_DlgTmpl, dyndata);
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, j, ic, res, currYR=0, maxYR=0, nx=0, ny, rx, cx, ry, cy, oax;
-	char **rd = 0L;
-	double fx, fy, fz, fsz;
-	bool updateYR = true, bContinue = false, bRet = false, bUseSch;
-	AccRange *rX = 0L, *rY = 0L;
-	Brick **cols;
-	Scatt3D *plot;
-	AxisDEF *ax;
-
-	if(!parent || !data) return false;
-	if(plots) {
-		//Plots alredy defined: jump to config dialog
-		return false;
-		}
-	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
-		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return false;
-	if(TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
-		for(i=100, j= 0; i <= 1000; i +=100){ 
-			if(TmpTxt[i]) rd[j++] = (char*)memdup(TmpTxt+i, (int)strlen(TmpTxt+i)+1, 0);
-			}
-		if(j) maxYR = j-1;
-		}
-	if(!rd && !(rd = (char**)calloc(1, sizeof(char*))))return false;
-	if(!(Dlg = new DlgRoot(Bar3D_Dlg, data))) return false;
-	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
-	hDlg = CreateDlgWnd("Create 3D Bar Chart", 50, 50, 420, 260, Dlg, 0x0L);
-	do {
-		if(updateYR) {
-			if(currYR >0) {
-				Dlg->ShowItem(156, true);
-				Dlg->Activate(101, false);
-				}
-			else {
-				Dlg->ShowItem(156, false);
-				Dlg->Activate(101, true);
-				}
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "y-values # %d/%d", currYR+1, maxYR+1);
-#else
-			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
-#endif
-			//SetText will also cause a redraw of the whole dialog
-			Dlg->SetText(153, TmpTxt);
-			updateYR = false;
-			}
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		ny = 0;
-		if(rX) delete rX;
-		rX = 0L;
-		switch(res) {
-		case 0:
-			if(bContinue || Dlg->GetCheck(20)) res = -1;
-			break;
-		case -1:
-			bContinue = false;			break;
-		case 1:
-			for(i = 0; i < 8; i++) Dlg->GetColor(303+i, &colarr[i]);
-			Dlg->GetColor(301, &defcol);
-			bUseSch = Dlg->GetCheck(302);
-			Dlg->GetValue(202, &start_z);	Dlg->GetValue(204, &dspm.fz);
-			//execute com_StackDlg for <OK>
-		case 155:		case 156:
-			res = com_StackDlg(res, Dlg, &rX, &nx, &rd, &currYR,
-				&rY, &bContinue, &ny, &maxYR, &updateYR);
-			break;
-		case 301:
-			Dlg->SetCheck(300, 0L, true);
-			res = -1;	break;
-		case 303:	case 304:	case 305:	case 306:
-		case 307:	case 308:	case 309:	case 310:
-			Dlg->SetCheck(302, 0L, true);
-			res = -1;	break;
-			}
-		}while (res < 0);
-	if(res == 1 && nx && rX && rd && rd[0] && rd[0][0]) {
-		if(rd[maxYR]) maxYR++;
-		fsz = DefSize(SIZE_BAR)/2.0;	fz = start_z;
-		oax = AxisTempl3D;	AxisTempl3D = 1;	CreateAxes();
-		if(Axes && nAxes > 2 && Axes[2] && (ax = Axes[2]->GetAxis())){
-			ax->flags = AXIS_3D | AXIS_INVERT | AXIS_DEFRECT;
-			ax->min = start_z-dspm.fz;
-			ax->max = start_z+dspm.fz*maxYR;
-			if(Axes[1] && (ax = Axes[1]->GetAxis())){
-				ax->flags |= AXIS_GRIDLINE;
-				i = 0x0c;
-				Axes[1]->Command(CMD_SET_GRIDTYPE, &i, 0L);
-				}
-			AxisTempl3D = oax;
-			}
-		if(plots = (GraphObj**)calloc(maxYR, sizeof(GraphObj*))) {
-			for(i = 0; i < maxYR; i++, fz += dspm.fz) {
-				if(rd[i] && (cols = (Brick**)calloc(nx, sizeof(Brick*))) && (rY = new AccRange(rd[i]))) {
-					ic = 0;
-					if(rX->GetFirst(&cx, &rx) && rX->GetNext(&cx, &rx) &&
-						rY->GetFirst(&cy, &ry) && rY->GetNext(&cy, &ry)) {
-						do {
-							if(data->GetValue(rx, cx, &fx) && data->GetValue(ry, cy, &fy)){
-								cols[ic] = new Brick(this, data, fx, 0.0, fz, 
-									fsz, fsz, fy, 0x800L, cx, rx, -1, -1, -1, -1,
-									-1, -1, -1, -1, cy, ry);
-								}
-							ic++;
-							}while(rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry));
-						if(ic) bRet = true;
-						}
-					if(plot = new Scatt3D(this, data, cols, ic)){
-						if(bUseSch) plot->SetColor(COL_BAR_FILL, colarr[(i & 0x07)]);
-						else plot->SetColor(COL_BAR_FILL, defcol);
-						plots[nPlots++] = plot;
-						}
-					if(rY) {
-						plot->data_desc = rY->RangeDesc(data, 1);
-						delete(rY);		rY = 0L;
-						}
-					}
-				}
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(rd) {
-		for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
-		free(rd);
-		}
-	if(rX) delete rX;		if(rY) delete rY;		free(Bar3D_Dlg);
-	if(bRet) Command(CMD_MRK_DIRTY, 0L, 0L);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Create a 2.5 dimensional ribbon chart
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-Ribbon25D::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 25, 10, "Data"};
-	TabSHEET tab2 = {25, 55, 10, "Details"};
-	TabSHEET tab3 = {55, 90, 10, "Scheme"};
-	static DWORD colarr[] = {0x000080ffL, 0x00ff8000L, 0x0000ff00L, 0x000000ffL,
-		0x00ff00ff, 0x00ffff00L, 0x0000ffff, 0x00c0c0c0};
-	static DWORD defcol = 0x00ffffffL;
-	double start_z = 1.0;
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)"range for common x values",
-		(void*)TmpTxt, (void*)" ranges for y values ", (void*)"distances:", (void*)"start z =", 
-		(void*)&start_z, (void*)"step =", (void*)&dspm.fz, (void*)" common color for ribbons:",
-		(void*)&defcol, (void*)" increment color scheme:", (void*)&colarr[0], (void*)&colarr[1],
-		(void*)&colarr[2], (void*)&colarr[3], (void*)&colarr[4], (void*)&colarr[5], (void*)&colarr[6],
-		(void*)&colarr[7]};
-	DlgInfo *Bar3D_Dlg = CompileDialog(Base25D_DlgTmpl, dyndata);
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, j, res, currYR=0, maxYR=0, nx=0, ny, oax;
-	char **rd = 0L, xrange[100];
-	double fz;
-	bool updateYR = true, bContinue = false, bRet = false, bUseSch;
-	AccRange *rX = 0L, *rY = 0L;
-	AxisDEF *ax;
-	Ribbon *plot;
-
-	if(!parent || !data) return false;
-	if(plots) {
-		//Plots alredy defined: jump to config dialog
-		return false;
-		}
-	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
-		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return false;
-	if(TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
-		for(i=100, j= 0; i <= 1000; i +=100){ 
-			if(TmpTxt[i]) rd[j++] = (char*)memdup(TmpTxt+i, (int)strlen(TmpTxt+i)+1, 0);
-			}
-		if(j) maxYR = j-1;
-		}
-	if(!rd && !(rd = (char**)calloc(1, sizeof(char*))))return false;
-	if(!(Dlg = new DlgRoot(Bar3D_Dlg, data)))return false;
-	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
-	hDlg = CreateDlgWnd("Create 3D Ribbon Chart", 50, 50, 420, 260, Dlg, 0x0L);
-	do {
-		if(updateYR) {
-			if(currYR >0) {
-				Dlg->ShowItem(156, true);		Dlg->Activate(101, false);
-				}
-			else {
-				Dlg->ShowItem(156, false);		Dlg->Activate(101, true);
-				}
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "y-values # %d/%d", currYR+1, maxYR+1);
-#else
-			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
-#endif
-			//SetText will also cause a redraw of the whole dialog
-			Dlg->SetText(153, TmpTxt);
-			updateYR = false;
-			}
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		ny = 0;		if(rX) delete rX;		rX = 0L;
-		switch(res) {
-		case 0:
-			if(bContinue || Dlg->GetCheck(20)) res = -1;
-			break;
-		case -1:
-			bContinue = false;			break;
-		case 1:
-			for(i = 0; i < 8; i++) Dlg->GetColor(303+i, &colarr[i]);
-			Dlg->GetColor(301, &defcol);	bUseSch = Dlg->GetCheck(302);
-			Dlg->GetValue(202, &start_z);	Dlg->GetValue(204, &dspm.fz);
-			//execute com_StackDlg for <OK>
-		case 155:		case 156:
-			res = com_StackDlg(res, Dlg, &rX, &nx, &rd, &currYR,
-				&rY, &bContinue, &ny, &maxYR, &updateYR);
-			break;
-		case 301:
-			Dlg->SetCheck(300, 0L, true);
-			res = -1;	break;
-		case 303:	case 304:	case 305:	case 306:
-		case 307:	case 308:	case 309:	case 310:
-			Dlg->SetCheck(302, 0L, true);
-			res = -1;	break;
-			}
-		}while (res < 0);
-	if(res == 1 && nx && rX && rd && rd[0] && rd[0][0]) {
-		if(rd[maxYR]) maxYR++;					fz = start_z;
-		Dlg->GetText(101, TmpTxt+100, 100);
-		oax = AxisTempl3D;	AxisTempl3D = 1;	CreateAxes();
-		if(Axes && nAxes > 2 && Axes[2] && (ax = Axes[2]->GetAxis())){
-			ax->flags = AXIS_3D | AXIS_INVERT | AXIS_DEFRECT;
-			ax->min = start_z-dspm.fz;
-			ax->max = start_z+dspm.fz*maxYR;
-			if(Axes[1] && (ax = Axes[1]->GetAxis())){
-				ax->flags |= AXIS_GRIDLINE;	
-				i = 0x0c;
-				Axes[1]->Command(CMD_SET_GRIDTYPE, &i, 0L);
-				}
-			AxisTempl3D = oax;
-			}
-		if(plots = (GraphObj**)calloc(maxYR, sizeof(GraphObj*))) {
-			Dlg->GetText(101, xrange, 100);
-			for(i = 0; i < maxYR; i++, fz += dspm.fz) {
-				if(plot = new Ribbon(this, data, fz, dspm.fz, xrange, rd[i])){
-					if(bUseSch) plot->SetColor(COL_POLYGON, colarr[(i & 0x07)]);
-					else plot->SetColor(COL_POLYGON, defcol);
-					plots[nPlots++] = plot;
-					}
-				}
-			}
-		Command(CMD_MRK_DIRTY, 0L, 0L);		Command(CMD_AUTOSCALE, 0L, 0L);
-		bRet = true;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(rd) {
-		for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
-		free(rd);
-		}
-	if(rX) delete rX;		if(rY) delete rY;		free(Bar3D_Dlg);
-	if(bRet) Command(CMD_MRK_DIRTY, 0L, 0L);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Create a 3 dimensional bubble plot
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-BubblePlot3D::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 22, 10, "Data"};
-	TabSHEET tab3 = {22, 47, 10, "Axes"};
-	char text1[100], text2[100], text3[100], text4[100];
-	DlgInfo BubDlg3D[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 142, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 142, 25, 45, 12},
-		{3, 50, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
-		{4, 5, 100, TOUCHEXIT | ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 131, 142},
-		{5, 10, 400, TOUCHEXIT | ISPARENT, SHEET, &tab3, 5, 10, 131, 142},
-		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
-		{50, 0, 0, NOSELECT, ODBUTTON, (void*)(OD_AxisDesc3D), 142, 65, 45, 45},
-		{100, 101, 0, 0x0L, LTEXT, (void*)"range for X Data", 10, 25, 60, 8},
-		{101, 102, 0, 0x0L, RANGEINPUT, text1, 20, 35, 100, 10},
-		{102, 103, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 48, 60, 8},
-		{103, 104, 0, 0x0L, RANGEINPUT, text2, 20, 58, 100, 10},
-		{104, 105, 0, 0x0L, LTEXT, (void*)"range for Z Data", 10, 71, 60, 8},
-		{105, 106, 0, 0x0L, RANGEINPUT, text3, 20, 81, 100, 10},
-		{106, 0, 150, ISPARENT | CHECKED, GROUPBOX, (void*)" diameter ", 8, 98, 125, 50},
-		{150, 151, 0, 0x0L, LTEXT, (void*)"range for diameters", 12, 102, 60, 8},
-		{151, 152, 0, 0x0L, RANGEINPUT, text4, 20, 112, 100, 10},
-		{152, 153, 0, 0x0L, LTEXT, (void*)"scaling:", 12, 125, 20, 8},
-		{153, 154, 0, TOUCHEXIT | CHECKED, RADIO1, (void*)Units[defs.cUnits].display, 38, 125, 20, 8},
-		{154, 155, 0, TOUCHEXIT, RADIO1, (void*)"with x-values", 70, 125, 20, 8},
-		{155, 156, 0, TOUCHEXIT, RADIO1, (void*)"with y-values", 20, 135, 20, 8},
-		{156, 0, 0, TOUCHEXIT, RADIO1, (void*)"with z-values", 70, 135, 20, 8},
-		{400, 410, 0, 0x0L, LTEXT, (void*)"select template:", 20, 30, 60, 8},
-		{410, 411, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_AxisTempl3D), 20, 42, 25, 25},
-		{411, 412, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_AxisTempl3D), 45, 42, 25, 25},
-		{412, 0, 0, LASTOBJ | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_AxisTempl3D), 70, 42, 25, 25}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, res, count;
-	int cx, rx, cy, ry, cz, rz, cr, rr, s_type = 5;
-	bool bRet = false;
-	double fx, fy, fz, fr;
-	Sphere **Balls;
-	AccRange *rX, *rY, *rZ, *rR;
-	Scatt3D *sc_plot;
-
-	if(!data || !parent)return false;
-	UseRangeMark(data, 1, text1, text2, text3, text4);
-	if(!(Dlg = new DlgRoot(BubDlg3D, data)))return false;
-	rX = rY = rZ = rR = 0L;
-	Dlg->SetCheck(410 + AxisTempl3D, 0L, true);
-	hDlg = CreateDlgWnd("Bubble Plot 3D", 50, 50, 388, 340, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 0:
-			if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case 4:		case 5:					//the tab sheets
-			res = -1;
-			break;
-		case 153:	
-			s_type = 5;						//absolute size, but use 5 to distinguish
-			res = -1;						//  from symbol
-			break;
-		case 154:	case 155:	case 156:
-			s_type = res - 153;
-			res = -1;
-			break;
-		case 410:	case 411:	case 412:	//axis templates
-			AxisTempl3D = res-410;
-			res = -1;
-			break;
-			}
-		}while (res <0);
-	if(res == 1) {
-		if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) rX = new AccRange(TmpTxt);
-		if(Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE)) rY = new AccRange(TmpTxt);
-		if(Dlg->GetText(105, TmpTxt, TMP_TXT_SIZE)) rZ = new AccRange(TmpTxt);
-		if(Dlg->GetText(151, TmpTxt, TMP_TXT_SIZE)) rR = new AccRange(TmpTxt);
-		if(rX && rY && rZ && rR && (count = rX->CountItems()) 
-			&& (Balls = (Sphere**)calloc(count, sizeof(Sphere*)))) {
-			rX->GetFirst(&cx, &rx);		rY->GetFirst(&cy, &ry);
-			rZ->GetFirst(&cz, &rz);		rR->GetFirst(&cr, &rr);
-			for(i = 0; i < count; i++){
-				if(rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry)
-					&& rZ->GetNext(&cz, &rz) && rR->GetNext(&cr, &rr)
-					&& data->GetValue(rx, cx, &fx) && data->GetValue(ry, cy, &fy)
-					&& data->GetValue(rz, cz, &fz) && data->GetValue(rr, cr, &fr)) {
-					Balls[i] = new Sphere(this, data, s_type, fx, fy, fz, fr, cx, rx,
-						cy, ry, cz, rz, cr, rr);
-					}
-				}
-			sc_plot = new Scatt3D(this, data, Balls, count);
-			if(parent->Id == GO_PLOT3D || parent->Id == GO_FUNC3D || parent->Id == GO_FITFUNC3D) {
-				if(!(parent->Command(CMD_DROP_PLOT, sc_plot, 0L))) delete(sc_plot);
-				bRet = true;
-				}
-			else if(!(bRet = Command(CMD_DROP_PLOT, sc_plot, 0L))) delete(sc_plot);
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(rX) delete rX;	if(rY) delete rY;	if(rZ) delete rZ;	if(rR) delete rR;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Create a 3D function plot
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-Func3D::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 37, 10, "Function"};
-	TabSHEET tab2 = {37, 65, 10, "Style"};
-	FillDEF newFill;
-	DlgInfo FuncDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 160, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 160, 25, 45, 12},
-		{3, 10, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
-		{4, 5, 100, ISPARENT, SHEET, &tab1, 5, 10, 149, 134},
-		{5, 50, 300, ISPARENT | CHECKED, SHEET, &tab2, 5, 10, 149, 134},
-		{10, 0, 0, 0x0L, CHECKPIN, 0L, 5, 0, 12, 8},
-		{50, 0, 0, NOSELECT, ODBUTTON, (void*)(OD_AxisDesc3D), 160, 85, 45, 45},
-		{100, 101, 0, 0x0L, LTEXT, (void*)"plot user defined function", 10, 30, 100, 8},
-		{101, 102, 0, 0x0L, RTEXT, (void*)"where x=", 10, 50, 28, 8}, 
-		{102, 103, 0, 0x0L, EDVAL1, &x1, 38, 50, 25, 10},
-		{103, 104, 0, 0x0L, RTEXT, (void*)"until", 61, 50, 17, 8}, 
-		{104, 105, 0, 0x0L, EDVAL1, &x2, 78, 50, 25, 10},
-		{105, 106, 0, 0x0L, RTEXT, (void*)"step", 102, 50, 17, 8}, 
-		{106, 107, 0, 0x0L, EDVAL1, &xstep, 119, 50, 25, 10},
-		{107, 108, 0, 0x0L, RTEXT, (void*)"z=", 10, 62, 28, 8}, 
-		{108, 109, 0, 0x0L, EDVAL1, &z1, 38, 62, 25, 10},
-		{109, 110, 0, 0x0L, EDVAL1, &z2, 78, 62, 25, 10},
-		{110, 150, 0, 0x0L, EDVAL1, &zstep, 119, 62, 25, 10},
-		{150, 200, 0, 0x0L, RTEXT, (void*)"y=", 10, 91, 10, 8}, 
-		{200, 0, 0, 0x0L, TEXTBOX, (void*)cmdxy, 22, 89, 122, 40},
-		{300, 301, 500, CHECKED, GROUPBOX, (void*)" grid ", 10, 40, 140, 100},
-		{301, 305, 400, HIDDEN | CHECKED, GROUPBOX, (void*)" surface ", 10, 40, 140, 100},
-		{305, 306, 0, CHECKED | TOUCHEXIT, RADIO1, (void*) " grid lines", 15, 25, 50, 10},
-		{306, 0, 0, TOUCHEXIT, RADIO1, (void*) " surface", 85, 25, 50, 10},
-		{400, 401, 0, 0x0L, RTEXT, (void*)"grid line width", 38, 50, 40, 8},
-		{401, 402, 0, 0x0L, EDVAL1, &Line.width, 80, 50, 25, 10},
-		{402, 403, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 107, 50, 20, 8},
-		{403, 404, 0, 0x0L, RTEXT, (void*)"grid line color", 38, 62, 40, 8},
-		{404, 405, 0, OWNDIALOG, COLBUTT, (void *)&Line.color, 80, 62, 25, 10},
-		{405, 406, 0, 0x0L, RTEXT,(void*)"plane color" , 38, 74, 40, 8},
-		{406, 0, 0, OWNDIALOG, SHADE3D, &newFill, 80, 74, 25, 10},
-		{500, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_linedef, 15, 45, 130, 100}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, undo_level = *Undo.pcb;
-	bool bRet = false, bNew = true;
-	DWORD undo_flags = 0L;
-	LineDEF newLine;
-	double o_x1, n_x1, o_x2, n_x2, o_xstep, n_xstep;
-	double o_z1, n_z1, o_z2, n_z2, o_zstep, n_zstep;
-	anyOutput *cdisp = Undo.cdisp;
-
-	if(!parent) return false;
-//	if(parent->Id == GO_FITFUNC) return parent->PropertyDlg();
-	memcpy(&newFill, &Fill, sizeof(FillDEF));
-	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-	if(!(Dlg = new DlgRoot(FuncDlg, data))) return false;
-	if(!bNew) Dlg->ShowItem(10, false);
-	Dlg->GetValue(102, &o_x1);		n_x1 = o_x1;
-	Dlg->GetValue(104, &o_x2);		n_x2 = o_x2;
-	Dlg->GetValue(106, &o_xstep);	n_xstep = o_xstep;
-	Dlg->GetValue(108, &o_z1);		n_z1 = o_z1;
-	Dlg->GetValue(109, &o_z2);		n_z2 = o_z2;
-	Dlg->GetValue(110, &o_zstep);	n_zstep = o_zstep;
-	hDlg = CreateDlgWnd("3D Function Plot", 50, 50, 426, 328, Dlg, 0x0L);
-	if(bNew) Dlg->SetCheck(4, 0L, true);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 305:		case 306:
-			if(Dlg->GetCheck(305)) {
-				Dlg->ShowItem(300, true);	Dlg->ShowItem(301, false);
-				}
-			else {
-				Dlg->ShowItem(300, false);	Dlg->ShowItem(301, true);
-				}
-			Dlg->DoPlot(0L);
-			res = -1;
-			break;
-		case 0:
-			if(Dlg->GetCheck(10)) res = -1;
-			break;
-			}
-		}while (res < 0);
-	Undo.SetDisp(cdisp);
-	while(*Undo.pcb > undo_level)	Undo.Pop(cdisp);
-	if(res == 1){						//OK pressed
-		if(bNew) {						//create function
-			if(Dlg->GetCheck(305)) {
-				type = 0;
-				OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-				}
-			else {
-				type = 1;
-				memcpy(&Fill, &newFill, sizeof(FillDEF));
-				Dlg->GetValue(401, &Line.width);
-				Dlg->GetColor(404, &Line.color);
-				Line.pattern = 0L;
-				Line.patlength = 1;
-				}
-			Dlg->GetValue(102, &x1);		Dlg->GetValue(104, &x2);
-			Dlg->GetValue(106, &xstep);
-			Dlg->GetValue(108, &z1);		Dlg->GetValue(109, &z2);
-			Dlg->GetValue(110, &zstep);		type = Dlg->GetCheck(305) ? 0 : 1;
-			if(Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE)) {
-				if(cmdxy) free(cmdxy);		cmdxy = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-				ReshapeFormula(&cmdxy);		bRet = Update();
-				}
-			}
-		else {							//edit existing function
-			Dlg->GetValue(102, &n_x1);		Dlg->GetValue(104, &n_x2);
-			Dlg->GetValue(106, &n_xstep);
-			undo_flags = CheckNewFloat(&x1, o_x1, n_x1, this, undo_flags);
-			undo_flags = CheckNewFloat(&x2, o_x2, n_x2, this, undo_flags);
-			undo_flags = CheckNewFloat(&xstep, o_xstep, n_xstep, this, undo_flags);
-			TmpTxt[0] = 0;	Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE);
-			undo_flags = CheckNewString(&cmdxy, cmdxy, TmpTxt, this, undo_flags);
-//			if(undo_flags & UNDO_CONTINUE) Update(0L, UNDO_CONTINUE);
-			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-			if(cmpLineDEF(&Line, &newLine)) {
-				Undo.Line(parent, &Line, undo_flags);	undo_flags |= UNDO_CONTINUE;
-				memcpy(&Line, &newLine, sizeof(LineDEF));
-				}
-			bRet = (undo_flags & UNDO_CONTINUE) != 0;
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Fit a 3D function to data
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-FitFunc3D::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 22, 10, "Data"};
-	TabSHEET tab2 = {22, 59, 10, "Function"};
-	TabSHEET tab3 = {59, 87, 10, "Style"};
-	char text1[100], text2[100], text3[100];
-	FillDEF newFill;
-	double iter;
-//	bool bNew = (dl == 0L);
-	bool bNew = true;
-	DlgInfo FuncDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 160, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 160, 25, 45, 12},
-		{3, 10, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
-		{4, 5, 400, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 149, 134},
-		{5, 6, 100, ISPARENT, SHEET, &tab2, 5, 10, 149, 134},
-		{6, 7, 300, ISPARENT, SHEET, &tab3, 5, 10, 149, 134},
-		{7, 0, 0, 0x0L, PUSHBUTTON, (void*)"Fit", 160, 132, 45, 12},
-		{10, 50, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
-		{50, 0, 0, NOSELECT, ODBUTTON, (void*)(OD_AxisDesc3D), 160, 65, 45, 45},
-		{100, 101, 0, 0x0L, LTEXT, (void*)"fit function by nonlinear regression", 10, 24, 100, 8},
-		{101, 102, 0, 0x0L, LTEXT, (void*)"parameters and initial values:", 10, 34, 100, 8},
-		{102, 150, 0, 0x0L, TEXTBOX, (void*)param, 22, 44, 122, 30},
-		{150, 151, 0, 0x0L, LTEXT, (void*)"function, y=f(x,z):", 10, 77, 10, 8}, 
-		{151, 152, 0, 0x0L, RTEXT, (void*)"y=", 10, 91, 10, 8}, 
-		{152, 153, 0, 0x0L, RTEXT, (void*)"converg.:", 20, 128, 26, 8}, 
-		{153, 154, 0, 0x0L, EDVAL1, &conv, 46, 128, 25, 10},
-		{154, 155, 0, 0x0L, RTEXT, (void*)"iterations:", 72, 128, 47, 8}, 
-		{155, 200, 0, 0x0L, EDVAL1, &iter, 119, 128, 25, 10},
-		{200, 0, 0, 0x0L, TEXTBOX, (void*)cmdxy, 22, 89, 122, 30},
-		{300, 301, 550, CHECKED, GROUPBOX, (void*)" grid ", 10, 40, 140, 100},
-		{301, 305, 350, HIDDEN | CHECKED, GROUPBOX, (void*)" surface ", 10, 40, 140, 100},
-		{305, 306, 0, CHECKED | TOUCHEXIT, RADIO1, (void*) " grid lines", 15, 25, 50, 10},
-		{306, 0, 0, TOUCHEXIT, RADIO1, (void*) " surface", 85, 25, 50, 10},
-		{350, 351, 0, 0x0L, RTEXT, (void*)"grid line width", 38, 50, 40, 8},
-		{351, 352, 0, 0x0L, EDVAL1, &Line.width, 80, 50, 25, 10},
-		{352, 353, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 107, 50, 20, 8},
-		{353, 354, 0, 0x0L, RTEXT, (void*)"grid line color", 38, 62, 40, 8},
-		{354, 355, 0, OWNDIALOG, COLBUTT, (void *)&Line.color, 80, 62, 25, 10},
-		{355, 356, 0, 0x0L, RTEXT,(void*)"plane color" , 38, 74, 40, 8},
-		{356, 0, 0, OWNDIALOG, SHADE3D, &newFill, 80, 74, 25, 10},
-		{400, 401, 0, 0x0L, LTEXT, (void*)"range for X data", 10, 30, 60, 8},
-		{401, 402, 0, 0x0L, RANGEINPUT, (void*)text1, 20, 40, 100, 10},
-		{402, 403, 0, 0x0L, LTEXT, (void*)"range for Y data", 10, 55, 60, 8},
-		{403, 404, 0, 0x0L, RANGEINPUT, (void*)text2, 20, 65, 100, 10},
-		{404, 405, 0, 0x0L, LTEXT, (void*)"range for Z data", 10, 80, 60, 8},
-		{405, 406, 0, 0x0L, RANGEINPUT, (void*)text3, 20, 90, 100, 10},
-		{406, 407, 0, CHECKED, CHECKBOX, (void*)"draw symbols", 20, 110, 60, 8},
-		{407, 0, 0, HIDDEN, LTEXT, 0L, 20, 110, 60, 8},
-		{500, 550, 501, CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{501, 502, 0, 0x0L, RTEXT, (void*)"plot x=", 10, 30, 28, 8}, 
-		{502, 503, 0, 0x0L, EDVAL1, &x1, 38, 30, 25, 10},
-		{503, 504, 0, 0x0L, RTEXT, (void*)"until", 61, 30, 17, 8}, 
-		{504, 505, 0, 0x0L, EDVAL1, &x2, 78, 30, 25, 10},
-		{505, 506, 0, 0x0L, RTEXT, (void*)"step", 102, 30, 17, 8}, 
-		{506, 0, 0, 0x0L, EDVAL1, &xstep, 119, 30, 25, 10},
-		{550, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_linedef, 15, bNew ? 45:45, 130, 100}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, undo_level = *Undo.pcb, i, j, k, l, m, n, ns = 0;
-	bool bRet = false, bContinue = false;
-	DWORD undo_flags = 0L;
-	LineDEF newLine;
-	double x, y, z, o_x1, n_x1, o_x2, n_x2, o_xstep, n_xstep, n_chi2=chi2, rad;
-	AccRange *rX, *rY, *rZ;
-	char *o_cmdxy, *o_param, *tmp_char;
-	anyOutput *cdisp = Undo.cdisp;
-	anyResult *ares;
-	Sphere **Balls;
-
-	if(!parent || !data) return false;
-	UseRangeMark(data, 1, text1, text2, text3);
-	if(!(o_cmdxy = (char*)memdup(cmdxy, (int)strlen(cmdxy)+1, 0)))return false;
-	if(!(o_param = (char*)memdup(param, (int)strlen(param)+1, 0)))return false;
-	rX = rY = rZ = 0L;			iter = (double)maxiter;
-	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-	memcpy(&newFill, &Fill, sizeof(FillDEF));
-	memcpy(&newLine, &Line, sizeof(LineDEF));
-	if(!(Dlg = new DlgRoot(FuncDlg, data))) return false;
-	if(!bNew){
-		Dlg->ShowItem(10, false);		Dlg->Activate(401, false);
-		Dlg->Activate(403, false);		Dlg->Activate(405, false);		
-		Dlg->SetCheck(6, 0L, true);
-		Dlg->SetCheck(4, 0L, false);	Dlg->ShowItem(404, false);
-		if(chi2 > 0.0) {
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "Chi 2 = %g", chi2);
-#else
-			sprintf(TmpTxt, "Chi 2 = %g", chi2);
-#endif
-			Dlg->SetText(405,TmpTxt);	Dlg->ShowItem(405, true);
-			}
-		}
-	else Dlg->ShowItem(500, false);
-	Dlg->GetValue(502, &o_x1);		n_x1 = o_x1;
-	Dlg->GetValue(504, &o_x2);		n_x2 = o_x2;
-	Dlg->GetValue(506, &o_xstep);	n_xstep = o_xstep;
-	hDlg = CreateDlgWnd("Fit Function to Data in 3D Space", 50, 50, 426, 328, Dlg, 0x0L);
-	if(bNew) Dlg->SetCheck(4, 0L, true);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(Dlg->GetCheck(10)) res = -1;
-			if(bContinue) res = -1;
-			bContinue = false;
-			break;
-		case 305:		case 306:
-			if(Dlg->GetCheck(305)) {
-				Dlg->ShowItem(300, true);	Dlg->ShowItem(301, false);
-				}
-			else {
-				Dlg->ShowItem(300, false);	Dlg->ShowItem(301, true);
-				}
-			Dlg->DoPlot(0L);
-			res = -1;
-			break;
-		case 1:
-			if(!bNew){
-				if(Dlg->GetText(102, TmpTxt, TMP_TXT_SIZE)) {
-					if(param) free(param);	
-					param = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-					}
-				if(Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE)) {
-					if(cmdxy) free(cmdxy);	
-					cmdxy = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-					}
-				ReshapeFormula(&param);		ReshapeFormula(&cmdxy);
-				dirty = true;
-				break;
-				}
-		case 7:								//Start: do nonlinear regression
-			Undo.SetDisp(cdisp);
-			if(!Dlg->GetText(401, text1, 100) || !Dlg->GetText(403, text2, 100) || !Dlg->GetText(405, text3, 100)) {
-				Dlg->SetCheck(4, 0L, true);			bContinue = true;
-				InfoBox("Invalid or missing range");
-				res = -1;
-				}
-			else if(Dlg->GetCheck(5)) {		//  the function tab must be shown
-				if(Dlg->CurrDisp) Dlg->CurrDisp->MouseCursor(MC_WAIT, true);
-				if(Dlg->GetText(102, TmpTxt, TMP_TXT_SIZE)) {
-					if(param) free(param);	
-					param = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-					}
-				if(Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE)) {
-					if(cmdxy) free(cmdxy);	
-					cmdxy = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-					}
-				Dlg->GetValue(153, &conv);	Dlg->GetValue(155, &iter);
-				ReshapeFormula(&param);		ReshapeFormula(&cmdxy);
-				do_formula(data, 0L);		//clear any error condition
-				ares = do_formula(data, param);
-				if(ares->type != ET_VALUE) {
-					ErrorBox("Syntax Error in parameters.");
-					bContinue = true;	res = -1;
-					break;
-					}
-				ares = do_formula(data, cmdxy);
-				if(ares->type != ET_VALUE) {
-					ErrorBox("Syntax Error in formula.");
-					bContinue = true;	res = -1;
-					break;
-					}
-				i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE-2, param);	i += rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, ";x=1;z=1;");
-				rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, cmdxy);	yywarn(0L, true);
-				ares = do_formula(data, TmpTxt);
-				if(tmp_char = yywarn(0L, false)) {
-					ErrorBox(tmp_char);
-					bContinue = true;	res = -1;
-					break;
-					}
-				i = do_fitfunc(data, text1, text2, text3, &param, cmdxy, conv, (int)iter, &chi2);
-				Dlg->SetText(102, param);
-				if(i >1 || res == 7) {
-#ifdef USE_WIN_SECURE
-					sprintf_s(TmpTxt, TMP_TXT_SIZE, "The Levenberg-Marquart algorithm\nexited after %d iterations.\n\nChi2 = %g", i, chi2);
-#else
-					sprintf(TmpTxt, "The Levenberg-Marquart algorithm\nexited after %d iterations.\n\nChi2 = %g", i, chi2);
-#endif
-					InfoBox(TmpTxt);
-					}
-				bContinue = true;
-				if(res == 7) res = -1;
-				if(Dlg->CurrDisp) Dlg->CurrDisp->MouseCursor(MC_ARROW, true);
-				}
-			else {							//diplay function tab first
-				Dlg->SetCheck(5, 0L, true);
-				res = -1;
-				}
-			break;
-			}
-		}while (res < 0);
-	Undo.SetDisp(cdisp);
-	while(*Undo.pcb > undo_level)	Undo.Pop(cdisp);
-	if(res == 1 && (rX=new AccRange(text1)) && (rY=new AccRange(text2)) && (rZ=new AccRange(text3))){
-		//OK pressed
-		if(bNew) {						//create function
-			if(Dlg->GetCheck(305)) {
-				type = 0;
-				OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&Line, 0);
-				}
-			else {
-				type = 1;
-				memcpy(&Fill, &newFill, sizeof(FillDEF));
-				Dlg->GetValue(401, &Line.width);
-				Dlg->GetColor(404, &Line.color);
-				Line.pattern = 0L;			Line.patlength = 1;
-				}
-			rX->GetFirst(&i, &j);		rX->GetNext(&i, &j);
-			rY->GetFirst(&k, &l);		rY->GetNext(&k, &l);
-			rZ->GetFirst(&m, &n);		rZ->GetNext(&m, &n);
-			do {
-				if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y) && data->GetValue(n, m, &z))
-					CheckBounds3D(x,y,z);
-				}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l) && rZ->GetNext(&m, &n));
-			x1 = xBounds.fx;		x2 = xBounds.fy;		xstep = (x2-x1)/10.0;
-			z1 = zBounds.fx;		z2 = zBounds.fy;		zstep = (z2-z1)/10.0;
-			if(Dlg->GetText(102, TmpTxt, TMP_TXT_SIZE)) {
-				if(param) free(param);	
-				param = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-				}
-			if(Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE)) {
-				if(cmdxy) free(cmdxy);	
-				cmdxy = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-				}
-			ReshapeFormula(&param);		ReshapeFormula(&cmdxy);
-			if((bRet = Update()) && gob && nPlots == 1 && (Balls=(Sphere**)calloc(rX->CountItems(), sizeof(Sphere*)))) {
-				rX->GetFirst(&i, &j);		rX->GetNext(&i, &j);
-				rY->GetFirst(&k, &l);		rY->GetNext(&k, &l);
-				rZ->GetFirst(&m, &n);		rZ->GetNext(&m, &n);
-				rad = DefSize(SIZE_SYMBOL);
-				do {
-					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y) && data->GetValue(n, m, &z)) {
-						Balls[ns++] = new Sphere(this, data, 0, x, y, z, rad, i, j, k, l, m, n);
-						}
-					}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l) && rZ->GetNext(&m, &n));
-				if(ns) {
-					plots[1] = (GraphObj*) new Scatt3D(this, data, Balls, ns);
-					nPlots = 2;
-					}
-				else free(Balls);
-				}
-			if(bRet)Command(CMD_ENDDIALOG, 0L, 0L);
-			}
-		else {							//edit existing function
-			Dlg->GetValue(102, &x1);		Dlg->GetValue(104, &x2);
-			Dlg->GetValue(106, &xstep);
-			Dlg->GetValue(108, &z1);		Dlg->GetValue(109, &z2);
-			Dlg->GetValue(110, &zstep);		type = Dlg->GetCheck(305) ? 0 : 1;
-			InfoBox("Not Implemented");
-			}
-		}
-	if(rX) delete(rX);		if(rY) delete(rY);		if(rZ) delete(rZ);
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Grid line properties
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *GridLineDlg_Tmpl =
-	"1,+,,DEFAULT,PUSHBUTTON, 1,150,10,50,12\n"
-	".,.,,,PUSHBUTTON,2,150,25,50,12\n"
-	".,10,,,PUSHBUTTON,-2,150,40,50,12\n"
-	"10,,100,ISPARENT | CHECKED,GROUPBOX,3,5,10,139,123\n"
-	"100,+,,NOSELECT,ODBUTTON,4,10,18,130,100\n"
-	".,.,112,HIDDEN | CHECKED,GROUP,0,0,0,0,0\n"
-	".,.,115,HIDDEN | CHECKED,GROUP,0,0,0,0,0\n"
-	".,.,120,HIDDEN | CHECKED,GROUP,0,0,0,0,0\n"
-	".,.,125,HIDDEN | CHECKED,GROUP,0,0,0,0,0\n"
-	".,,130,HIDDEN | CHECKED,GROUP,0,0,0,0,0\n"
-	"111,,,,RTEXT,5,15,117,23,8\n"
-	"112,+,,,CHECKBOX,-20,41,117,25,8\n"
-	".,.,,,CHECKBOX,-21,105,117,25,8\n"
-	".,111,,,CHECKBOX,-25,70,117,25,8\n"
-	"115,+,,,CHECKBOX,-22,41,117,25,8\n"
-	".,.,,,CHECKBOX,-23,105,117,25,8\n"
-	".,111,,,CHECKBOX,-24,70,117,25,8\n"
-	"120,+,,,CHECKBOX,-24,55,117,25,8\n"
-	".,135,,,CHECKBOX,-25,90,117,25,8\n"
-	"125,+,,,CHECKBOX,-24,55,117,25,8\n"
-	".,135,,,CHECKBOX,-26,90,117,25,8\n"
-	"130,+,,,CHECKBOX,-25,55,117,25,8\n"
-	".,135,,,CHECKBOX,-26,90,117,25,8\n"
-	"135,,,LASTOBJ,LTEXT,6,15,117,55,8";
-bool
-GridLine::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 21, 10, "Line"};
-	int dh = ((flags & AXIS_ANGULAR) || (flags & AXIS_RADIAL))? -20 : 0;
-	void *dyndata[] = {(void*)"Apply to LINE", (void*)"Apply to AXIS", (void*)" grid line ", 
-		(void*)OD_linedef, (void*)"line to:", (void*)"parallel to:"}; 
-	DlgInfo *LineDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, tmptype;
-	bool bRet = false;
-	DWORD undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-	LineDEF newLine;
-
-	if(!parent || !parent->parent) return false;
-	if(!(LineDlg = CompileDialog(GridLineDlg_Tmpl, dyndata)))return false;
-	LineDlg[3].h += dh;
-	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
-	if(!(Dlg = new DlgRoot(LineDlg, data)))return false;
-	if ((flags & AXIS_ANGULAR) || (flags & AXIS_RADIAL)) {
-		//no checkboxes, changed sizes
-		}
-	else if(flags &AXIS_3D) {
-		Dlg->SetCheck(120, 0L, type & 0x01 ? true : false);
-		Dlg->SetCheck(121, 0L, type & 0x02 ? true : false);
-		Dlg->SetCheck(125, 0L, type & 0x04 ? true : false);
-		Dlg->SetCheck(126, 0L, type & 0x08 ? true : false);
-		Dlg->SetCheck(130, 0L, type & 0x10 ? true : false);
-		Dlg->SetCheck(131, 0L, type & 0x20 ? true : false);
-		switch(parent->parent->type) {
-		case 1:	Dlg->ShowItem(105, true);	break;
-		case 2:	Dlg->ShowItem(104, true);	break;
-		case 3: Dlg->ShowItem(103, true);	break;
-			}
-		}
-	else {
-		Dlg->SetCheck(112, 0L, type & DL_LEFT ? true : false);
-		Dlg->SetCheck(113, 0L, type & DL_RIGHT ? true : false);
-		Dlg->SetCheck(114, 0L, type & DL_YAXIS ? true : false);
-		Dlg->SetCheck(115, 0L, type & DL_TOP ? true : false);
-		Dlg->SetCheck(116, 0L, type & DL_BOTTOM ? true : false);
-		Dlg->SetCheck(117, 0L, type & DL_XAXIS ? true : false);
-		if(type & 0x07) Dlg->ShowItem(101, true);
-		else Dlg->ShowItem(102, true);
-		}
-	hDlg = CreateDlgWnd("Grid line properties", 50, 50, 415, 310 + dh*2, Dlg, 0x0L);
-	do{												//dh*2 means dh * ybase
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 1:							//this line
-		case 2:							//all lines of plot
-			Undo.SetDisp(cdisp);
-			if(flags &AXIS_3D) {
-				tmptype = 0;
-				if(Dlg->GetCheck(120)) tmptype |= 0x01;		if(Dlg->GetCheck(121)) tmptype |= 0x02;
-				if(Dlg->GetCheck(125)) tmptype |= 0x04;		if(Dlg->GetCheck(126)) tmptype |= 0x08;
-				if(Dlg->GetCheck(130)) tmptype |= 0x10;		if(Dlg->GetCheck(131)) tmptype |= 0x20;
-				}
-			else {
-				tmptype = (type & ~0xff);
-				if(Dlg->GetCheck(112)) tmptype |= DL_LEFT;		if(Dlg->GetCheck(113)) tmptype |= DL_RIGHT;
-				if(Dlg->GetCheck(114)) tmptype |= DL_YAXIS;		if(Dlg->GetCheck(115)) tmptype |= DL_TOP;
-				if(Dlg->GetCheck(116)) tmptype |= DL_BOTTOM;	if(Dlg->GetCheck(117)) tmptype |= DL_XAXIS;
-				}
-			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
-			break;
-			}
-		}while (res < 0);
-	if(res == 1){			//Apply to line
-		undo_flags = CheckNewInt(&type, type, tmptype, parent, undo_flags);
-		if(cmpLineDEF(&LineDef, &newLine)) {
-			Undo.Line(parent, &LineDef, undo_flags);	undo_flags |= UNDO_CONTINUE;
-			memcpy(&LineDef, &newLine, sizeof(LineDEF));
-			}
-		if(undo_flags & UNDO_CONTINUE) bRet = bModified = true;
-		}
-	else if(res == 2) {		//Apply to axis
-		if(parent->Id == GO_TICK && parent->parent->Id == GO_AXIS &&
-			(tmptype != type || cmpLineDEF(&LineDef, &newLine))) {
-			parent->parent->Command(CMD_SAVE_TICKS, 0L, 0L);
-			if(cmpLineDEF(&LineDef, &newLine)) parent->parent->Command(CMD_SET_GRIDLINE, (void*)&newLine, 0L);
-			if(tmptype != type) parent->parent->Command(CMD_SET_GRIDTYPE, (void*)(& tmptype), 0L);
-			bRet = true;
-			}
-		}
-	CloseDlgWnd(hDlg);	delete Dlg;		free(LineDlg);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Tick properties
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *TickDlg_Tmpl =
-	"1,2,,,PUSHBUTTON,1,120,10,60,12\n"
-	"2,3,,DEFAULT, PUSHBUTTON,2,120,25,60,12\n"
-	"3,4,,,PUSHBUTTON,-2,120,40,60,12\n"
-	"4,,5,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"5,6,100,ISPARENT | CHECKED,SHEET,3,5,10,110,100\n"
-	"6,7,200,ISPARENT,SHEET,4,5,10,110,100\n"
-	"7,,300,ISPARENT,SHEET,5,5,10,110,100\n"
-	"100,101,,,RTEXT,6,10,30,35,8\n"
-	"101,102,,,EDVAL1,7,50,30,25,10\n"
-	"102,103,,,LTEXT,-3,77,30,20,8\n"
-	"103,104,,,CHECKBOX,8,18,45,30, 8\n"
-	"104,105,,,LTEXT,9,18,60,20,8\n"
-	"105,106,,,RADIO1,0,40,70,35,8\n"
-	"106,107,,,RADIO1,0,40,60,35,8\n"
-	"107,108,,,RADIO1,10,40,80,35,8\n"
-	"108,,,,CHECKBOX,11,18,95,30,8\n"
-	"200,201,,,RTEXT,12,10,35,23,8\n"
-	"201,202,,,EDVAL1,13,40,35,65,10\n"
-	"202,203,,,LTEXT,14,15,55,20,8\n"
-	"203,,,,EDTEXT,0,15,67,90,10\n"
-	"300,301,,,LTEXT,15,15,30,70,8\n"
-	"301,302,,TOUCHEXIT,RADIO1,16,15,42,70,8\n"
-	"302,303,,TOUCHEXIT,RADIO1,17,15,52,70,8\n"
-	"303,304,,TOUCHEXIT,RADIO1,18,15,74,70,8\n"
-	"304,305,,TOUCHEXIT,RADIO1,19,15,84,70,8\n"
-	"305,306,,TOUCHEXIT,RADIO1,20,15,94,70,8\n"
-	"306,307,,, EDVAL1,21,28,62,35,10\n"
-	"307,,,LASTOBJ,LTEXT,22,65,62,20,8";
-
-bool
-Tick::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 25, 10, "Tick"};
-	TabSHEET tab2 = {64, 90, 10, "Edit"};
-	TabSHEET tab3 = {25, 64, 10, "Direction"};
-	double tick_rot;
-	void *dyndata[] = {(void*)"Apply to TICK", (void*)"Apply to AXIS",  (void*)&tab1, (void*)&tab2,
-		(void*)&tab3, (void*)"tick size", (void*)&size, (void*)"major tick", (void*)"type:",
-		(void*)"symmetric", (void*)"draw grid line(s)", (void*)"value:", (void*)&value, (void*)"label:",
-		(void*)"direction of ticks", (void*)"perpendicular to axis", (void*)"fixed angle",
-		(void*)"x-axis", (void*)"y-axis", (void*)"z-axis", (void*)&tick_rot, (void*)"deg."};
-	DlgInfo *TickDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	DWORD old_flags;
-	char old_value[80];
-	double new_size, old_size, new_angle, old_angle, new_value;
-	int res, tmp_type = type;
-	bool bRet = false;
-	DWORD new_flags = flags, undo_flags = 0;
-	anyOutput *cdisp = Undo.cdisp;
-	char *old_label = 0L;
-	TextDEF *td;
-
-	if(!parent || parent->Id != GO_AXIS) return false;
-	if(!(TickDlg = CompileDialog(TickDlg_Tmpl, dyndata))) return false;
-	switch(type & 0x0f) {
-	case 1:		tick_rot = angle;					break;
-	default:	tick_rot = trig2deg(lsi, lcsi);		break;
-		}
-	old_flags = flags;
-	Dlg = new DlgRoot(TickDlg, data);
-	Dlg->SetCheck(301 + (type & 0x07), 0L, true);
-	if(flags & AXIS_ANGULAR) {
-		Dlg->SetText(105, "outside");		Dlg->SetText(106, "inside");
-		Dlg->ShowItem(303, false);			Dlg->ShowItem(304, false);
-		Dlg->ShowItem(305, false);
-		}
-	else if(flags & AXIS_3D) {
-		Dlg->SetText(105, "positive");		Dlg->SetText(106, "negative");
-		if(parent->Id == GO_AXIS) {
-			//disable tick direction onto the axis
-			switch(parent->type) {
-			case 1:	Dlg->Activate(303, false);	break;
-			case 2:	Dlg->Activate(304, false);	break;
-			case 3:	Dlg->Activate(305, false);	break;
-				}
-			}
-		}
-	else {
-		Dlg->ShowItem(303, false);	Dlg->ShowItem(304, false);
-		Dlg->ShowItem(305, false);
-		if(abs(pts[1].x - pts[0].x) > abs(pts[1].y - pts[0].y)){
-			Dlg->SetText(105, "right");		Dlg->SetText(106, "left");
-			}
-		else {
-			Dlg->SetText(105, "up");		Dlg->SetText(106, "down");
-			}
-		}
-	if(label) {
-		if(label->Command(CMD_GETTEXT, &TmpTxt, 0L) && TmpTxt[0] && 
-			(old_label = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0)))
-			Dlg->SetText(203, old_label);
-		if(label->Id != GO_LABEL) Dlg->Activate(203, false);
-		}
-	switch(flags &0x03) {
-	case AXIS_NEGTICKS:		Dlg->SetCheck(106, 0L, true);		break;
-	case AXIS_SYMTICKS:		Dlg->SetCheck(107, 0L, true);		break;
-	default:				Dlg->SetCheck(105, 0L, true);		break;
-		}
-	if(flags & AXIS_GRIDLINE) Dlg->SetCheck(108, 0L, true); 
-	if(!(flags & AXIS_MINORTICK)) Dlg->SetCheck(103, 0L, true);
-	if(Dlg->GetValue(101, &old_size)) new_size = old_size;
-	else new_size = old_size = size;
-	if(Dlg->GetValue(306, &old_angle)) new_angle = old_angle;
-	else new_angle = old_angle = tick_rot;
-	if(flags & AXIS_DATETIME) Dlg->SetText(201, NiceTime(value));
-	if(!(Dlg->GetText(201, old_value, sizeof(old_value)))) {
-		new_value = value;	old_value[0] = 0;
-		}
-	hDlg = CreateDlgWnd("Tick properties", 50, 50, 375, 265, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 1:		case 2:
-			Undo.SetDisp(cdisp);				new_flags &= ~0x7;
-			if(Dlg->GetCheck(105)) new_flags |= AXIS_POSTICKS;
-			else if(Dlg->GetCheck(106)) new_flags |= AXIS_NEGTICKS;
-			else new_flags |= AXIS_SYMTICKS;
-			if(Dlg->GetCheck(108)) new_flags |= AXIS_GRIDLINE;
-			Dlg->GetValue(101, &new_size);		Dlg->GetValue(306, &new_angle);
-			break;
-		case 301:	case 302:	case 303:	case 304:	case 305:
-			tmp_type = (tmp_type & ~0x0f) | (res -301);
-			res = -1;
-			break;
-			}
-		}while (res <0);
-	if(res == 1) {
-		if(Dlg->GetCheck(103)) new_flags &= ~AXIS_MINORTICK;
-		else new_flags |= AXIS_MINORTICK;
-		undo_flags = CheckNewDword(&flags, flags, new_flags, parent, undo_flags);
-		undo_flags = CheckNewInt(&type, type, tmp_type, parent, undo_flags);
-		undo_flags = CheckNewFloat(&size, old_size, new_size, parent, undo_flags);
-		undo_flags = CheckNewFloat(&angle, old_angle, new_angle, parent, undo_flags);
-		if(Dlg->GetText(201, TmpTxt, 80) && strcmp(TmpTxt, old_value)) {
-			Undo.ValFloat(parent, &value, undo_flags);		undo_flags |= UNDO_CONTINUE;
-			if(flags & AXIS_DATETIME) date_value(TmpTxt, 0L, &value);
-			else Dlg->GetValue(201, &value);
-			}
-		if(label && label->Id == GO_LABEL) {
-			td = ((Label*)label)->GetTextDef();
-			if(!Dlg->GetText(203, TmpTxt, TMP_TXT_SIZE)) TmpTxt[0] = 0;
-			if(undo_flags = CheckNewString(&td->text, td->text, TmpTxt, this, undo_flags))
-				label->Command(CMD_SETTEXT, TmpTxt, 0L);
-			}
-		if (undo_flags & UNDO_CONTINUE) bRet = true;
-		}
-	else if(res == 2) {
-		parent->Command(CMD_SAVE_TICKS, 0L, 0L);
-		if((new_flags & 0x07) != (old_flags & 0x07)) 
-			parent->Command(CMD_SET_TICKSTYLE, &new_flags, 0L);
-		parent->SetSize(SIZE_AXIS_TICKS, new_size);
-		parent->Command(CMD_TICK_TYPE, &tmp_type, 0L);
-		parent->SetSize(SIZE_TICK_ANGLE, new_angle);
-		parent->Command(CMD_REDRAW, 0L, 0L);
-		bRet = true;
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;		free(TickDlg);
-	if(old_label) free(old_label);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Axis properties dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *ssTickTmpl =
-		"1,2,,DEFAULT, PUSHBUTTON,-1,113,10,45,12\n"
-		"2,3,,, PUSHBUTTON,-2,113,25,45,12\n"
-		"3,4,,, LTEXT,1,5,10,100,9\n"
-		"4,5,,TOUCHEXIT,RANGEINPUT,4,10,20,90,10\n"
-		"5,6,,, LTEXT,2,5, 32, 100, 9\n"
-		"6,7,,TOUCHEXIT,RANGEINPUT,5,10,42,90,10\n"
-		"7, 8,,,LTEXT,3, 5, 54, 80, 8\n"
-		"8,,,LASTOBJ | TOUCHEXIT,RANGEINPUT,6,10,64,90,10";
-
-bool
-Axis::ssTicks()
-{
-	void *dyndata[] ={(void*)"range for major tick VALUES:", (void*)"range for major tick LABELS:",
-		(void*)"minor tick VALUES:", (void*)ssMATval, (void*)ssMATlbl, (void*)ssMITval};
-	DlgInfo *TickDlg = CompileDialog(ssTickTmpl, dyndata);
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, n, n1, n2, n3;
-	bool bRet = false, bContinue = true;
-	AccRange *rT, *rL, *rMT;
-
-	if(!data) return false;
-	n = n1 = n2 = n3 = 0;			rT = rL = rMT = 0L;
-	if(!(Dlg = new DlgRoot(TickDlg, data)))return false;
-	hDlg = CreateDlgWnd("choose ticks from spreadsheet", 50, 50, 330, 190, Dlg, 0x0L);
-	Dlg->Activate(4, true);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:								// focus lost
-			if(bContinue) res = -1;
-			break;
-		case 4:		case 6:		case 8:
-			bContinue = true;
-			res = -1;			break;
-		case 1:
-			if(rT) delete rT;		if(rL) delete rL;		if(rMT) delete rMT;
-			n = n1 = n2 = n3 = 0;	rT = rL = rMT = 0L;
-			if(Dlg->GetText(4,TmpTxt, TMP_TXT_SIZE) && (rT=new AccRange(TmpTxt)) && (n1=rT->CountItems())){
-				if(Dlg->GetText(6, TmpTxt, TMP_TXT_SIZE) && (rL = new AccRange(TmpTxt)) && 
-					(n2 = rL->CountItems()) && n1 != n2){
-					ErrorBox("Ranges for tick values\nand tick labels must"
-						" have\nthe same size");
-					res = -1;		bContinue = true;
-					break;
-					}
-				}
-			if(Dlg->GetText(8,TmpTxt,TMP_TXT_SIZE) && (rMT=new AccRange(TmpTxt)) && (n3=rMT->CountItems())){
-				//minor ticks are valid
-				}
-			if(!(n = n1 + n3)) {
-				ErrorBox("Ranges not valid or\nno range specified");
-				res = -1;			bContinue = true;
-				}
-			}
-		}while (res < 0);
-	if(res == 1 && n) {
-		if(n1) {
-			if(ssMATval) free(ssMATval);	if(ssMATlbl) free(ssMATlbl);
-			if(ssMITval) free(ssMITval);	ssMATval = ssMATlbl = ssMITval = 0L;
-			if(Dlg->GetText(4, TmpTxt, TMP_TXT_SIZE)) 
-				ssMATval = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-			if(Dlg->GetText(6, TmpTxt, TMP_TXT_SIZE))
-				ssMATlbl = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-			if(Dlg->GetText(8, TmpTxt, TMP_TXT_SIZE))
-				ssMITval = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-			UpdateTicks();
-			}
-		bRet = true;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;			free(TickDlg);
-	if(rT) delete rT;	if(rL) delete rL;	if(rMT) delete rMT;
-	return bRet;
-}
-
-bool
-Axis::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 25, 10, "Axis"};
-	TabSHEET tab2 = {25, 50, 10, "Ticks"};
-	TabSHEET tab3 = {50, 92, 10, "Transforms"};
-	TabSHEET tab4 = {92, 123, 10, "Breaks"};
-	TabSHEET tab5 = {123, 148, 10, "Plots"};
-	int v1 = (axis->flags & AXIS_3D) ? 77 : (axis->flags & AXIS_RADIAL) ? 83 : 89; 
-	DlgInfo AxisDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 166, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 166, 25, 45, 12},
-		{3, 0, 4, ISPARENT | CHECKED, GROUP, NULL, 0, 0, 0, 0},
-		{4, 5, 50, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 148, 165},
-		{5, 6, 200, ISPARENT, SHEET, &tab2, 5, 10, 148, 165},
-		{6, 7, 300, ISPARENT, SHEET, &tab3, 5, 10, 148, 165},
-		{7, 8, 400, ISPARENT, SHEET, &tab4, 5, 10, 148, 165},
-		{8, 0, 500, HIDDEN | ISPARENT, SHEET, &tab5, 5, 10, 148, 165},
-		{50, 51, 100, ISPARENT | CHECKED, GROUPBOX, (void*)" scaling ", 10, 30, 138, 36},
-		{51, 104, 0, TOUCHEXIT, CHECKBOX, (void*)" automatic scaling", 17, 37, 80, 8},
-		{100, 101, 0, 0x0L, RTEXT, (void*)"axis from", 10, 51, 35, 8},
-		{101, 102, 0, 0x0L, EDVAL1, &axis->min, 48, 51, 41, 10},
-		{102, 103, 0, 0x0L, CTEXT, (void*)"to", 90, 51, 11, 8},
-		{103, 0, 0, 0x0L, EDVAL1, &axis->max, 102, 51, 41, 10},
-		{104, 120, 105, ISPARENT | CHECKED, GROUPBOX, (void*)" placement ", 10, 72, 138, 45},
-		{105, 106, 0, TOUCHEXIT | ISRADIO, CHECKBOX, (void*)"left", 54, 77, 20, 8},
-		{106, 107, 0, TOUCHEXIT | ISRADIO, CHECKBOX, (void*)"right", 84, 77, 20, 8},
-		{107, 108, 0, TOUCHEXIT | ISRADIO, CHECKBOX, (void*)"top", 54, 77, 20, 8},
-		{108, 109, 0, TOUCHEXIT | ISRADIO, CHECKBOX, (void*)"bottom", 84, 77, 20, 8},
-		{109, 110, 0, 0x0L, RTEXT, (void*)"x", 10, v1, 15, 8},
-		{110, 111, 0, 0x0L, EDVAL1, &axis->loc[0].fx, 27, v1, 45, 10},
-		{111, 112, 0, 0x0L, LTEXT, (void*)"-", 75, v1, 5, 8},
-		{112, 113, 0, 0x0L, EDVAL1, &axis->loc[1].fx, 81, v1, 45, 10},
-		{113, 114, 0, 0x0L, LTEXT, (void*)0L, 129, v1, 15, 8},
-		{114, 115, 0, 0x0L, RTEXT, (void*)"y", 10, v1+12, 15, 8},
-		{115, 116, 0, 0x0L, EDVAL1, &axis->loc[0].fy, 27, v1+12, 45, 10},
-		{116, 117, 0, 0x0L, LTEXT, (void*)"-", 75, v1+12, 5, 8},
-		{117, 118, 0, 0x0L, EDVAL1, &axis->loc[1].fy, 81, v1+12, 45, 10},
-		{118, 150, 0, 0x0L, LTEXT, (void*)0L, 129, v1+12, 15, 8},
-		{150, 151, 0, 0x0L, RTEXT, (void*)"z", 10, v1+24, 15, 8},
-		{151, 152, 0, 0x0L, EDVAL1, &axis->loc[0].fz, 27, v1+24, 45, 10},
-		{152, 153, 0, 0x0L, LTEXT, (void*)"-", 75, v1+24, 5, 8},
-		{153, 154, 0, 0x0L, EDVAL1, &axis->loc[1].fz, 81, v1+24, 45, 10},
-		{154, 0, 0, 0x0L, LTEXT, (void*)0L, 129, v1+24, 15, 8},
-		{120, 180, 121, ISPARENT | CHECKED, GROUPBOX, (void*)" line ", 10, 123, 138, 20},
-		{121, 122, 0, 0x0L, RTEXT, (void*)"width", 10, 128, 25, 8},
-		{122, 123, 0, 0x0L, EDVAL1, &sizAxLine, 37, 128, 25, 10},
-		{123, 124, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 63, 128, 10, 8},
-		{124, 125, 0, 0x0L, RTEXT, (void*)"color", 82, 128, 25, 8},
-		{125, 130, 0, OWNDIALOG, COLBUTT, (void *)&colAxis, 109, 128, 25, 10},
-		{130, 0, 131, ISPARENT | CHECKED, GROUPBOX, (void*)" axis label ", 10, 149, 138, 20},
-		{131, 0, 0, 0x0L, EDTEXT, (void*)"abc", 15, 154, 128, 10},
-		{180, 0, 181, HIDDEN | ISPARENT | CHECKED, GROUPBOX, (void*)" placement ", 10, 72, 138, 45},
-		{181, 182, 0, 0x0L, RTEXT, (void*)"center x", 10, v1-12, 50, 8},
-		{182, 183, 0, 0x0L, EDVAL1, &axis->Center.fx, 62, v1-12, 45, 10},
-		{183, 184, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 109, v1-12, 15, 8},
-		{184, 185, 0, 0x0L, RTEXT, (void*)"y", 10, v1, 50, 8},
-		{185, 186, 0, 0x0L, EDVAL1, &axis->Center.fy, 62, v1, 45, 10},
-		{186, 187, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 109, v1, 15, 8},
-		{187, 188, 0, 0x0L, RTEXT, (void*)"radius", 10, v1+12, 50, 8},
-		{188, 189, 0, 0x0L, EDVAL1, &axis->Radius, 62, v1+12, 45, 10},
-		{189, 0, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 109, v1+12, 15, 8},
-		{200, 201, 0, TOUCHEXIT, RADIO1, (void*)"no ticks", 20, 35, 40, 8},
-		{201, 202, 0, TOUCHEXIT, RADIO1, (void*)"automatic", 20, 47, 40, 8},
-		{202, 204, 0, TOUCHEXIT, RADIO1, (void*)"leave unchanged", 20, 59, 40, 8},
-		{203, 205, 0, TOUCHEXIT, RADIO1, (void*)"set manually", 20, 71, 40, 8},
-		{204, 203, 250, CHECKED | ISPARENT, GROUPBOX, (void*)"                            ", 15, 75, 110, 50},
-		{205, 206, 0, 0x0L, PUSHBUTTON, (void*)"use spread sheet values", 30, 130, 80, 12},
-		{206, 0, 0, TOUCHEXIT, RADIO1, 0L, 20, 130, 8, 8},
-		{250, 251, 0, 0x0L, RTEXT, (void*)"start value", 30, 83, 45, 8},
-		{251, 252, 0, 0x0L, EDVAL1, &axis->Start, 77, 83, 35, 10},
-		{252, 253, 0, 0x0L, RTEXT, (void*)"interval", 30, 95, 45, 8},
-		{253, 254, 0, 0x0L, EDVAL1, &axis->Step, 77, 95, 35, 10},
-		{254, 255, 0, 0x0L, RTEXT, (void*)"minor ticks per interval", 20, 107, 75, 8},
-		{255, 0, 0, 0x0L, EDTEXT, (void*)"0", 97, 107, 15, 10},
-		{300, 301, 0, 0x0L, LTEXT, (void*)"Transforms:", 10, 30, 110, 8},
-		{301, 302, 0, TOUCHEXIT, RADIO1, (void*)"none (linear)", 20, 45, 70, 8},
-		{302, 303, 0, TOUCHEXIT, RADIO1, (void*)"logarithmic (log base 10)", 20, 57, 70, 8},
-		{303, 304, 0, TOUCHEXIT, RADIO1, (void*)"reciprocal (1/x)", 20, 69, 70, 8},
-		{304, 305, 0, TOUCHEXIT, RADIO1, (void*)"square root", 20, 81, 70, 8},
-		{305, 0, 0, 0x0L, CHECKBOX, axis->loc[0].fx == axis->loc[1].fx ? 
-			(void*)"low vaues top of graph" : (void*)"low values right of graph", 20, 115, 80, 12},
-		{400, 450, 0, 0x0L, LTEXT, (void*)"style:", 10, 30, 110, 8},
-		{401, 402, 0, ISRADIO, ODBUTTON, (void*)(OD_BreakTempl), 29, 40, 25, 25},
-		{402, 403, 0, ISRADIO, ODBUTTON, (void*)(OD_BreakTempl), 54, 40, 25, 25},
-		{403, 404, 0, ISRADIO, ODBUTTON, (void*)(OD_BreakTempl), 79, 40, 25, 25},
-		{404, 405, 0, ISRADIO, ODBUTTON, (void*)(OD_BreakTempl), 104, 40, 25, 25},
-		{405, 406, 0, 0x0L, RTEXT, (void*)"break gap", 15, 75, 48, 8},
-		{406, 407, 0, 0x0L, EDVAL1, &brkgap, 65, 75, 29, 10},
-		{407, 408, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 97, 75, 10, 8},
-		{408, 409, 0, 0x0L, RTEXT, (void*)"symbol size", 15, 87, 48, 8},
-		{409, 410, 0, 0x0L, EDVAL1, &brksymsize, 65, 87, 29, 10},
-		{410, 0, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 97, 87, 10, 8},
-		{450, 451, 0, ISPARENT | CHECKED, GROUPBOX, (void*)" break data ", 10, 110, 138, 50},
-		{451, 452, 0, 0x0L, LTEXT, 0L, 20, 115, 60, 8},
-		{452, 453, 0, 0x0L, PUSHBUTTON, (void*)"Next >>", 98, 142, 41, 12},
-		{453, 454, 0, 0x0L, PUSHBUTTON, (void*)"<< Prev.", 57, 142, 41, 12},
-		{454, 455, 0, 0x0L, RTEXT, (void*)"from", 15, 125, 20, 8},
-		{455, 456, 0, 0x0L, EDTEXT, 0L, 37, 125, 41, 10},
-		{456, 457, 0, 0x0L, RTEXT, (void*)"to", 82, 125, 9, 8},
-		{457, 458, 0, 0x0L, EDTEXT, 0L, 93, 125, 41, 10},
-		{458, 401, 0, 0x0L, PUSHBUTTON, (void*)"Delete", 16, 142, 41, 12},
-		{500, 0, 0, NOSELECT, ODBUTTON, (void*)OD_axisplot, 25, 30, 110, 140},
-		{600, 0, 0, LASTOBJ, NONE, 0L, 0, 0, 0, 0}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, j, res, nbrk, cbrk, ttmpl;
-	double tmp, tmp2, old_x1, old_x2, old_y1, old_y2;
-	bool bRet = false, bChanged = false, upd_brk = true, bContinue = false;
-	DWORD new_color, undo_flags = 0L;
-	anyOutput *cdisp = Undo.cdisp;
-	lfPOINT *brks = 0L, *tmpbrks = 0L;
-	char *old_Label = 0L, *type_txt;
-	TextDEF label_def, *lb_def;
-	char **names;
-	GraphObj **somePlots = 0L, **scp = 0L;
-	AxisDEF old_a, new_a;
-	void *sv_ptr;
-
-
-	nbrk = cbrk = 0;
-	if(!parent) return false;
-	if(parent->Id == GO_GRAPH && (res=((Graph*)parent)->nscp)){
-		scp = ((Graph*)parent)->Sc_Plots;
-		CurrAxes = ((Graph*)parent)->Axes;
-		if(!scp || !(names = (char**)calloc(res+2, sizeof(char*))) ||
-			!(somePlots = (GraphObj**)calloc(res+2, sizeof(GraphObj*))))
-			return false;
-		if(names[0] = (char*)malloc(15 * sizeof(char))) rlp_strcpy(names[0], 15, "[unchanged]");
-		for(i = 0, j = 1; i < res; i++) {
-			if(scp[i] && scp[i]->name){
-				names[j] = (char*)memdup(scp[i]->name, (int)strlen(scp[i]->name)+1, 0);
-				somePlots[j++] = scp[i];
-				}
-			}
-		}
-	else if(IsPlot3D(parent) && (res=((Plot3D*)parent)->nscp)){
-		scp = ((Plot3D*)parent)->Sc_Plots;
-		CurrAxes = ((Plot3D*)parent)->Axes;
-		if(!scp || !(names = (char**)calloc(res+2, sizeof(char*))) ||
-			!(somePlots = (GraphObj**)calloc(res+2, sizeof(GraphObj*))))
-			return false;
-		if(names[0] = (char*)malloc(15 * sizeof(char))) rlp_strcpy(names[0], 15, "[unchanged]");
-		for(i = 0, j = 1; i < res; i++) {
-			if(scp[i] && scp[i]->name){
-				names[j] = (char*)memdup(scp[i]->name, (int)strlen(scp[i]->name)+1, 0);
-				somePlots[j++] = scp[i];
-				}
-			}
-		}
-	else {
-		names = (char**)calloc(2, sizeof(char*));	names[0] = (char*)malloc(10*sizeof(char));
-		rlp_strcpy(names[0], 10, "n.a.");
-		}
-	OD_axisplot(OD_ACCEPT, 0L, 0L, 0L, names, 0);
-	if(!(Dlg = new DlgRoot(AxisDlg, data)))return false;
-	if(names && somePlots) Dlg->ShowItem(8, true);		//show tab
-	if(axis->breaks && axis->nBreaks) {
-		if(!(brks = (lfPOINT*)calloc(axis->nBreaks+2, sizeof(lfPOINT)))) return false;
-		memcpy(brks, axis->breaks, axis->nBreaks*sizeof(lfPOINT));
-		nbrk = axis->nBreaks-1;
-		WriteNatFloatToBuff(TmpTxt, brks[cbrk].fx);
-		Dlg->SetText(455, TmpTxt+1);
-		WriteNatFloatToBuff(TmpTxt, brks[cbrk].fy);
-		Dlg->SetText(457, TmpTxt+1);
-		}
-	switch(brksym) {
-	case 2:		Dlg->SetCheck(402, 0L, true);	break;
-	case 3:		Dlg->SetCheck(403, 0L, true);	break;
-	case 4:		Dlg->SetCheck(404, 0L, true);	break;
-	default:	Dlg->SetCheck(401, 0L, true);	break;
-		}
-	if(!(axis->flags & 0x03)) Dlg->SetCheck(ttmpl = 200, 0L, true);
-	else if(axis->flags & AXIS_AUTOTICK)Dlg->SetCheck(ttmpl = 201, 0L, true);
-	else if(Ticks) Dlg->SetCheck(ttmpl = 202, 0L, true);
-	else Dlg->SetCheck(ttmpl = 201, 0L, true);
-	Dlg->Activate(251, false);	Dlg->Activate(253, false);	Dlg->Activate(255, false);
-	Dlg->SetCheck(305, 0L, (AXIS_INVERT == (axis->flags & AXIS_INVERT)));
-	if(axis->flags & AXIS_AUTOSCALE) {
-		Dlg->SetCheck(51, 0L, true);
-		Dlg->Activate(101, false);		Dlg->Activate(103, false);
-		}
-	//check transforms
-	switch(axis->flags & 0x7000L) {
-	case AXIS_LINEAR:	Dlg->SetCheck(301, 0L, true);	break;
-	case AXIS_LOG:		Dlg->SetCheck(302, 0L, true);	break;
-	case AXIS_RECI:		Dlg->SetCheck(303, 0L, true);	break;
-	case AXIS_SQR:		Dlg->SetCheck(304, 0L, true);	break;
-		}
-	//align to frame ?
-	if(axis->flags & AXIS_3D) {
-		Dlg->ShowItem(105, false);	Dlg->ShowItem(106, false);
-		Dlg->ShowItem(107, false);	Dlg->ShowItem(108, false);
-		}
-	else if(axis->flags & AXIS_ANGULAR) {
-		Dlg->ShowItem(104, false);	Dlg->ShowItem(130, false);
-		Dlg->ShowItem(180, true);	Dlg->ShowItem(7, false);
-		}
-	else {
-		if(axis->flags & AXIS_RADIAL) {
-			Dlg->ShowItem(105, false);	Dlg->ShowItem(106, false);
-			Dlg->ShowItem(107, false);	Dlg->ShowItem(108, false);
-			}
-		Dlg->ShowItem(150, false);	Dlg->ShowItem(151, false);
-		Dlg->ShowItem(152, false);	Dlg->ShowItem(153, false);
-		Dlg->ShowItem(154, false);
-		if(axis->loc[0].fx != axis->loc[1].fx) {
-			Dlg->ShowItem(105, false);	Dlg->ShowItem(106, false);
-			}
-		if(axis->loc[0].fy != axis->loc[1].fy) {
-			Dlg->ShowItem(107, false);	Dlg->ShowItem(108, false);
-			}
-		}
-	switch(axis->flags & 0x70) {
-		case AXIS_LEFT: Dlg->SetCheck(105, 0L, true);	break;
-		case AXIS_RIGHT: Dlg->SetCheck(106, 0L, true);	break;
-		case AXIS_TOP: Dlg->SetCheck(107, 0L, true);	break;
-		case AXIS_BOTTOM: Dlg->SetCheck(108, 0L, true);	break;
-		}
-	if(axis->flags & AXIS_X_DATA) Dlg->SetText(113, "[data]");
-	else Dlg->SetText(113, Units[defs.cUnits].display);
-	if(axis->flags & AXIS_Y_DATA) Dlg->SetText(118, "[data]");
-	else Dlg->SetText(118, Units[defs.cUnits].display);
-	if(axis->flags & AXIS_Z_DATA) Dlg->SetText(118, "[data]");
-	else Dlg->SetText(154, Units[defs.cUnits].display);
-	//any label ?
-	if(axisLabel){
-		TmpTxt[0] = 0;
-		axisLabel->Command(CMD_GETTEXT, TmpTxt, 0L);
-		Dlg->SetText(131,TmpTxt);
-		old_Label = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-		if(axisLabel->Id == GO_MLABEL) Dlg->Activate(131, false);
-		}
-	else Dlg->SetText(131, 0L);
-	//remember: any updated values ?
-	Dlg->GetValue(110, &old_x1);		Dlg->GetValue(112, &old_x2);
-	Dlg->GetValue(115, &old_y1);		Dlg->GetValue(117, &old_y2);
-	switch(type) {
-	case 1:		type_txt = (char*)(&"X-a");		break;
-	case 2:		type_txt = (char*)(&"Y-a");		break;
-	case 3:		type_txt = (char*)(&"Z-a");		break;
-	default:	type_txt = (char*)(&"A");		break;
-		}
-	//angular radial axis specials
-	if(axis->flags & AXIS_ANGULAR){
-		Dlg->SetText(305, "set direction cw");
-		type_txt = (char*)(&"angular a");
-		}
-	if(axis->flags & AXIS_RADIAL) type_txt = (char*)(&"radial a");
-	//save old axis definition
-	memcpy(&old_a, axis, sizeof(AxisDEF));
-	Dlg->GetValue(101, &old_a.min);			Dlg->GetValue(103, &old_a.max);
-	Dlg->GetValue(110, &old_a.loc[0].fx);	Dlg->GetValue(112, &old_a.loc[1].fx);
-	Dlg->GetValue(115, &old_a.loc[0].fy);	Dlg->GetValue(117, &old_a.loc[1].fy);
-	Dlg->GetValue(151, &old_a.loc[0].fz);	Dlg->GetValue(153, &old_a.loc[1].fz);
-	Dlg->GetValue(182, &old_a.Center.fx);	Dlg->GetValue(185, &old_a.Center.fy);
-	Dlg->GetValue(188, &old_a.Radius);
-	memcpy(&new_a, &old_a, sizeof(AxisDEF));
-	i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, type_txt);
-	rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, "xis properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 436, 390, Dlg, 0x0L);
-	switch(axis->flags & 0x70) {
-		case AXIS_LEFT: 
-		case AXIS_RIGHT:
-			Dlg->Activate(110, false);			Dlg->Activate(112, false);
-			break;
-		case AXIS_TOP:
-		case AXIS_BOTTOM:
-			Dlg->Activate(115, false);			Dlg->Activate(117, false);
-			break;
-			}
-	do{
-		if(upd_brk) {
-			Dlg->ShowItem(453, cbrk > 0);
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "break # %d/%d", cbrk+1, nbrk+1);
-#else
-			sprintf(TmpTxt,"break # %d/%d", cbrk+1, nbrk+1);
-#endif
-			Dlg->SetText(451, TmpTxt);
-			upd_brk = false;
-			}
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 0:							//lost focus ?
-			if(bContinue) res = -1;
-			ShowDlgWnd(hDlg);
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 51:
-			if(Dlg->GetCheck(51)) {
-				Dlg->Activate(101, false);		Dlg->Activate(103, false);
-				}
-			else {
-				Dlg->Activate(101, true);		Dlg->Activate(103, true);
-				}
-			res = -1;
-			break;
-		case 105:	case 106:	case 107:	case 108:		//axis left | right | top | bottom
-			if(Dlg->GetCheck(105) || Dlg->GetCheck(106)) {
-				Dlg->Activate(110, false);			Dlg->Activate(112, false);
-				}
-			else {
-				Dlg->Activate(110, true);			Dlg->Activate(112, true);
-				}
-			if(Dlg->GetCheck(107) || Dlg->GetCheck(108)) {
-				Dlg->Activate(115, false);			Dlg->Activate(117, false);
-				}
-			else {
-				Dlg->Activate(115, true);			Dlg->Activate(117, true);
-				}
-			res = -1;
-			break;
-		case 301:	case 302:	case 303:	case 304:		//transform radiobuttons
-			new_a.flags &= ~0x7000L;
-			if(res == 302) new_a.flags |= AXIS_LOG;
-			else if(res == 303) new_a.flags |= AXIS_RECI;
-			else if(res == 304) new_a.flags |= AXIS_SQR;
-			res = -1;
-			break;
-		case 200:	case 201:	case 202:
-			ttmpl = res;
-			Dlg->Activate(251, false);	Dlg->Activate(253, false);	Dlg->Activate(255, false);
-			res = -1;
-			break;
-		case 203:
-			ttmpl = res;
-			Dlg->Activate(251, true);	Dlg->Activate(253, true);	Dlg->Activate(255, true);
-			res = -1;
-			break;
-		case 205:	case 206:
-			Dlg->SetCheck(206, 0L, true);
-			if (ssTicks()) {
-				Dlg->SetCheck(ttmpl = 202, 0L, true);	Dlg->Activate(251, false);
-				Dlg->Activate(253, false);	Dlg->Activate(255, false);
-				undo_flags |= UNDO_CONTINUE;	res = 1;
-				}
-			else {
-				Dlg->SetCheck(ttmpl, 0L, true);
-				res = -1;
-				}
-			bContinue = true;
-			break;
-		case 452:			//next break
-			if(Dlg->GetValue(455, &tmp) && Dlg->GetValue(457, &tmp2)){
-				if(!brks && (brks = (lfPOINT*)calloc(2, sizeof(lfPOINT)))) {
-					brks[0].fx = tmp;		brks[0].fy = tmp2;
-					cbrk = nbrk = 0;
-					}
-				if(!brks) return false;		//mem allocation error
-				if(cbrk && cbrk >= nbrk) {
-					if(tmpbrks = (lfPOINT*)realloc(brks, (cbrk+2)*sizeof(lfPOINT))){
-						brks = tmpbrks;
-						brks[cbrk].fx = tmp;		brks[cbrk].fy = tmp2;
-						brks[cbrk+1].fx = brks[cbrk+1].fy = 0.0;
-						}
-					else return false;		//mem allocation error
-					}
-				else {
-					brks[cbrk].fx = tmp;		brks[cbrk].fy = tmp2;
-					}
-				cbrk++;
-				}
-			if(cbrk>nbrk){
-				nbrk = cbrk;
-				Dlg->SetText(455,0L);		Dlg->SetText(457,0L);
-				}
-			else if(nbrk){
-				if(brks[cbrk].fx == brks[cbrk].fy && cbrk == nbrk) {
-					Dlg->SetText(455,0L);		Dlg->SetText(457,0L);
-					}
-				else {
-					WriteNatFloatToBuff(TmpTxt, brks[cbrk].fx);
-					Dlg->SetText(455, TmpTxt+1);
-					WriteNatFloatToBuff(TmpTxt, brks[cbrk].fy);
-					Dlg->SetText(457, TmpTxt+1);
-					}
-				}
-			bChanged = upd_brk = true;
-			res= -1;
-			break;
-		case 453:			//previous break
-			if(cbrk >0){
-				if(Dlg->GetValue(455, &tmp) && Dlg->GetValue(457, &tmp2)){
-					brks[cbrk].fx = tmp;		brks[cbrk].fy = tmp2;
-					}
-				else if(cbrk == nbrk)nbrk--;
-				cbrk--;
-				WriteNatFloatToBuff(TmpTxt, brks[cbrk].fx);
-				Dlg->SetText(455, TmpTxt+1);
-				WriteNatFloatToBuff(TmpTxt, brks[cbrk].fy);
-				Dlg->SetText(457, TmpTxt+1);
-				}
-			bChanged = upd_brk = true;
-			res= -1;
-			break;
-		case 458:			//delete break;
-			if(brks && nbrk > cbrk) {
-				for(i = cbrk; i < nbrk; i++) {
-					brks[i].fx = brks[i+1].fx;
-					brks[i].fy = brks[i+1].fy;
-					}
-				nbrk--;
-				if(brks[cbrk].fx == brks[cbrk].fy && cbrk == nbrk) {
-					Dlg->SetText(455,0L);		Dlg->SetText(457,0L);
-					}
-				else {
-					WriteNatFloatToBuff(TmpTxt, brks[cbrk].fx);
-					Dlg->SetText(455, TmpTxt+1);
-					WriteNatFloatToBuff(TmpTxt, brks[cbrk].fy);
-					Dlg->SetText(457, TmpTxt+1);
-					}
-				}
-			else {
-				Dlg->SetText(455,0L);		Dlg->SetText(457,0L);
-				}
-			bChanged = upd_brk = true;
-			res = -1;
-			break;
-			}
-		}while (res < 0);
-	Undo.SetDisp(cdisp);
-	switch (res) {
-	case 1:									//OK pressed
-		bModified = true;
-		if(names && CurrAxes && somePlots) {			//apply axis to plot ?
-			for(i = 0; CurrAxes[i] != this; i++);		//find index 
-			OD_axisplot(OD_ACCEPT, 0L, 0L, (anyOutput*) &res, 0L, 0);
-			if(somePlots[res] && somePlots[res]->Command(CMD_USEAXIS, &i, 0L))
-				undo_flags |= UNDO_CONTINUE;
-			}
-		if(Dlg->GetValue(455, &tmp) && Dlg->GetValue(457, &tmp2)){
-			if(!brks && (brks = (lfPOINT*)calloc(2, sizeof(lfPOINT)))) {
-				brks[0].fx = tmp;	brks[0].fy = tmp2;	cbrk = nbrk = 1;
-				}
-			else if(brks) {
-				brks[cbrk].fx = tmp;	brks[cbrk].fy = tmp2;	nbrk++;
-				}
-			}
-		Dlg->GetValue(101, &new_a.min);			Dlg->GetValue(103, &new_a.max);
-		Dlg->GetValue(110, &new_a.loc[0].fx);	Dlg->GetValue(112, &new_a.loc[1].fx);
-		Dlg->GetValue(115, &new_a.loc[0].fy);	Dlg->GetValue(117, &new_a.loc[1].fy);
-		Dlg->GetValue(151, &new_a.loc[0].fz);	Dlg->GetValue(153, &new_a.loc[1].fz);
-		Dlg->GetValue(182, &new_a.Center.fx);	Dlg->GetValue(185, &new_a.Center.fy);
-		Dlg->GetValue(188, &new_a.Radius);
-		new_a.breaks = brks;					new_a.nBreaks = nbrk;
-		if(new_a.breaks) SortAxisBreaks(&new_a);
-		brks = 0L;	nbrk = 0;
-		if(Dlg->GetCheck(51)) {
-			if(!(new_a.flags & AXIS_AUTOSCALE)) parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-			new_a.flags |= AXIS_AUTOSCALE;
-			}
-		else new_a.flags &= ~AXIS_AUTOSCALE;
-		if(Dlg->GetCheck(201)) new_a.flags |= AXIS_AUTOTICK;
-		else new_a.flags &= ~AXIS_AUTOTICK;
-		if(Dlg->GetCheck(200)) new_a.flags &= ~0x03;
-		if(Dlg->GetCheck(305)) new_a.flags |= AXIS_INVERT;
-		else new_a.flags &= ~AXIS_INVERT;
-		new_a.flags &= ~0x70;
-		if(Dlg->GetCheck(105)) new_a.flags |= AXIS_LEFT;
-		else if(Dlg->GetCheck(106)) new_a.flags |= AXIS_RIGHT;
-		else if(Dlg->GetCheck(107)) new_a.flags |= AXIS_TOP;
-		else if(Dlg->GetCheck(108)) new_a.flags |= AXIS_BOTTOM;
-		if((new_a.flags & AXIS_LOG)== AXIS_LOG && new_a.min < defs.min4log) {
-			switch(type & 0x0f) {
-			case 1:
-				new_a.min = parent->GetSize(SIZE_BOUNDS_XMIN);	break;
-			case 2:
-				new_a.min = parent->GetSize(SIZE_BOUNDS_YMIN);	break;
-			case 3:
-				new_a.min = parent->GetSize(SIZE_BOUNDS_ZMIN);	break;
-				}
-			}
-		if(cmpAxisDEF(&old_a, &new_a)) {
-			bChanged = true;
-			if(axis->flags != new_a.flags) parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-			Undo.AxisDef(this, axis, undo_flags);
-			memcpy(axis, &new_a, sizeof(AxisDEF));	undo_flags |= UNDO_CONTINUE;
-			axis->Start = axis->min;
-			}
-		else if(new_a.breaks) free(new_a.breaks);
-		if(axis->nBreaks && Dlg->GetValue(406, &tmp))
-			undo_flags = CheckNewFloat(&brkgap, brkgap, tmp, this, undo_flags);
-		if(axis->nBreaks && Dlg->GetValue(409, &tmp))
-			undo_flags = CheckNewFloat(&brksymsize, brksymsize, tmp, this, undo_flags);
-		if(Dlg->GetCheck(402)) i = 2;
-		else if(Dlg->GetCheck(403)) i = 3;
-		else if(Dlg->GetCheck(404)) i = 4;
-		else i = 0;
-		if(axis->nBreaks) undo_flags = CheckNewInt(&brksym, brksym, i, this, undo_flags);
-		if(Dlg->GetColor(125, &new_color) && new_color != colAxis) {
-			undo_flags = CheckNewDword(&colAxis, colAxis, new_color, this, undo_flags);
-			if ((axis->flags & AXIS_ANGULAR) || (axis->flags & AXIS_RADIAL)) {
-				Undo.ValDword(this, &GridLine.color, undo_flags);
-				GridLine.color = new_color;
-				}
-			if(Ticks || axisLabel) {
-				SavVarInit(200 * NumTicks);
-				if(axisLabel){
-					axisLabel->FileIO(SAVE_VARS);
-					axisLabel->SetColor(COL_TEXT, colAxis);
-					}
-				if(Ticks) for(i = 0; i < NumTicks; i++)  if(Ticks[i]){
-					Ticks[i]->FileIO(SAVE_VARS);
-					Ticks[i]->SetColor(COL_AXIS, colAxis);
-					}
-				sv_ptr = SavVarFetch();
-				Undo.SavVarBlock(this, &sv_ptr, undo_flags);
-				}
-			}
-		if(Dlg->GetValue(122, &tmp)) 
-			undo_flags = CheckNewFloat(&sizAxLine, sizAxLine, tmp, this, undo_flags);
-		if(Dlg->GetText(131, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0]) {
-			if(old_Label && strcmp(old_Label,TmpTxt) && axisLabel->Id == GO_LABEL){
-				lb_def = ((Label*)axisLabel)->GetTextDef();
-				undo_flags = CheckNewString(&lb_def->text, old_Label, TmpTxt, this, undo_flags);
-				}
-			else if(!axisLabel) {
-				label_def.ColTxt = colAxis;				label_def.ColBg = 0x00ffffffL;
-				label_def.fSize = DefSize(SIZE_TICK_LABELS)*1.2;
-				label_def.RotBL = fabs(si)>0.80 ? 90.0 : 0.0;
-				label_def.RotCHAR = 0.0f;				label_def.iSize = 0;
-				label_def.Align = TXA_VCENTER | TXA_HCENTER;
-				label_def.Mode = TXM_TRANSPARENT;		label_def.Style = TXS_NORMAL;
-				label_def.Font = FONT_HELVETICA;		label_def.text = TmpTxt;
-				Undo.SetGO(this, &axisLabel, new Label(this, data, 0, 0, &label_def,
-					LB_Y_PARENT | LB_X_PARENT), undo_flags);
-				undo_flags |= UNDO_CONTINUE;
-				}
-			}
-		else if(axisLabel) {
-			Undo.DeleteGO(&axisLabel, undo_flags, 0L);	undo_flags |= UNDO_CONTINUE;
-			}
-		if((!(axis->flags & 0x03) && Ticks && NumTicks)	|| (Dlg->GetCheck(203)) ||
-			((old_a.flags & 0x7000) != (new_a.flags & 0x7000) && (new_a.flags & AXIS_AUTOTICK))
-			|| ((new_a.flags & AXIS_AUTOTICK) &&(new_a.min != old_a.min || new_a.max != old_a.max))){
-			Undo.DropListGO(this, (GraphObj ***)&Ticks, &NumTicks, undo_flags);
-			undo_flags |= UNDO_CONTINUE;
-			}
-		if(Dlg->GetCheck(203)) {			//set ticks manually
-			if(!Dlg->GetValue(255, &tmp)) tmp = 0.0;
-			i = (int)tmp;
-			if(!Dlg->GetValue(251, &tmp)) tmp = axis->Start;
-			if(!Dlg->GetValue(253, &tmp2)) tmp2 = axis->Step;
-			if(!(axis->flags & 0x03)){
-				if(axis->flags & AXIS_ANGULAR) axis->flags |= AXIS_POSTICKS;
-				else axis->flags |= AXIS_SYMTICKS;
-				}
-			axis->flags &= ~AXIS_AUTOTICK;
-			ManuTicks(tmp, tmp2, i, axis->flags);
-			}
-		if(!(new_a.flags & 0x03) && (new_a.flags & AXIS_AUTOTICK)){
-			if(new_a.flags & AXIS_ANGULAR) axis->flags |= AXIS_POSTICKS;
-			else axis->flags |= AXIS_SYMTICKS;
-			}
-		if(undo_flags & UNDO_CONTINUE) {
-			bRet= true;
-			if((axis->flags & AXIS_RADIAL)||(axis->flags & AXIS_ANGULAR))
-				parent->Command(CMD_AXIS, this, 0L);
-			}
-		break;
-		}
-	if(brks && nbrk) free(brks);
-	if(old_Label) free(old_Label);
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(names) {
-		for(j = 0; names[j]; j++) if(names[j]) free(names[j]);
-		free(names);
-		}
-	if(somePlots) free(somePlots);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Graph dialogs
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *AddPlotTmpl =
-		"1,2,,DEFAULT,PUSHBUTTON,-1,150,10,45,12\n"
-		"2,3,,,PUSHBUTTON,-2,150,25,45,12\n"
-		"3,,520,ISPARENT | CHECKED,GROUPBOX,1,5,10,135,95\n"
-		"520,521,,EXRADIO | CHECKED,ODBUTTON,2,10,20,25,25\n"
-		"521,522,,EXRADIO,ODBUTTON,2,35,20,25,25\n"
-		"522,523,,EXRADIO,ODBUTTON,2,60,20,25,25\n"
-		"523,524,,EXRADIO,ODBUTTON,2,85,20,25,25\n"
-		"524,525,,EXRADIO,ODBUTTON,2,110,20,25,25\n"
-		"525,526,,EXRADIO,ODBUTTON,2,10,45,25,25\n"
-		"526,528,,EXRADIO,ODBUTTON,2,35,45,25,25\n"
-		"528,529,,EXRADIO,ODBUTTON,2,60,45,25,25\n"
-		"529,530,,EXRADIO,ODBUTTON,2,85,45,25,25\n"
-		"530,531,,EXRADIO,ODBUTTON,2,110,45,25,25\n"
-		"531,532,,EXRADIO,ODBUTTON,2,10,70,25,25\n"
-		"532,540,,EXRADIO,ODBUTTON,2,35,70,25,25\n"
-		"540,541,,EXRADIO,ODBUTTON,2,60,70,25,25\n"
-		"541,,,LASTOBJ | EXRADIO,ODBUTTON, 2, 85,70,25,25";
-
-bool
-Graph::AddPlot(int family)
-{
-	void *dyndata[] = {(void *)"  select template  ",(void*)OD_PlotTempl};
-	DlgInfo *GraphDlg = CompileDialog(AddPlotTmpl, dyndata);
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, res, cSel = 520;
-	bool bRet = false;
-	Plot *p;
-
-	switch(type) {
-	case GT_STANDARD:
-		break;
-	case GT_POLARPLOT:
-		for(i = 0; i < NumPlots; i++) {
-			if(Plots[i] && Plots[i]->Id == GO_POLARPLOT) {
-				if (((PolarPlot*)Plots[i])->AddPlot()) 
-					return Command(CMD_REDRAW, 0L, 0L);
-				}
-			}
-		return false;
-	default:
-		InfoBox("Don\'t know how to\nadd a plot to the\ncurrent graph.");
-		return false;
-		}
-	Dlg = new DlgRoot(GraphDlg, data);
-	hDlg = CreateDlgWnd("Add Plot", 50, 50, 410, 240, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 520:	case 521:	case 522:	case 523:	case 524:
-		case 525:	case 526:	case 528:	case 529:	case 530:
-		case 531:	case 532:	case 540:	case 541:
-			if(res == cSel) res = 1;
-			else {
-				cSel = res;				res = -1;
-				}
-			break;
-			}
-		}while (res < 0);
-	if(res == 1){						//OK pressed
-		if(Dlg->GetCheck(524)) p = new BubblePlot(this, data);
-		else if(Dlg->GetCheck(525)) p = new BoxPlot(this, data);
-		else if(Dlg->GetCheck(521)) p = new PlotScatt(this, data, 0x03);
-		else if(Dlg->GetCheck(522)) p = new PlotScatt(this, data, 0x08);
-		else if(Dlg->GetCheck(523)) p = new PlotScatt(this, data, 0x04);
-		else if(Dlg->GetCheck(526)) p = new Regression(this, data);
-		else if(Dlg->GetCheck(528)) p = new DensDisp(this, data);
-		else if(Dlg->GetCheck(529)) p = new Function(this, data, "Function");
-		else if(Dlg->GetCheck(530)) p = new FitFunc(this, data);
-		else if(Dlg->GetCheck(531)) p = new MultiLines(this, data);
-		else if(Dlg->GetCheck(532)) p = new xyStat(this, data);
-		else if(Dlg->GetCheck(540)) p = new StackBar(this, data);
-		else if(Dlg->GetCheck(541)) p = new StackPG(this, data);
-		else p = new PlotScatt(this, data, 0x01);
-		if(p && p->PropertyDlg()) {
-			if(!Command(CMD_DROP_PLOT, p, (anyOutput *)NULL)) delete p;
-			else bRet = true;
-			}
-		else if(p) delete p;
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;		free(GraphDlg);
-	return bRet;
-}
-
-static char *GraphDlgTmpl = 
-		"1,+,,DEFAULT,PUSHBUTTON,-1,170,10,45,12\n"
-		".,.,,,PUSHBUTTON,-2,170,25,45,12\n"
-		".,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"4,+,100,ISPARENT | CHECKED,SHEET,1,5,10,157,122\n"
-		".,.,200,ISPARENT,SHEET,2,5,10,157,122\n"
-		".,,300,ISPARENT,SHEET,3,5,10,157,122\n"
-		"100,+,,,LTEXT,4,10,25,60,8\n"
-		".,.,500,TOUCHEXIT | ISPARENT,SHEET,5,10,37,147,90\n"
-		".,.,520,TOUCHEXIT | ISPARENT,SHEET,6,10,37,147,90\n"
-		".,.,540,TOUCHEXIT | ISPARENT,SHEET,7,10,37,147,90\n"
-		".,,560,TOUCHEXIT | ISPARENT,SHEET,8,10,37,147,90\n"
-		"200,+,,,LTEXT,9,10,35,60,8\n"
-		".,.,,,RTEXT,10,5,47,58,8\n"
-		".,.,,,EDVAL1,11,64,47,30,10\n"
-		".,.,,,RTEXT,-5,95,47,10,8\n"
-		".,.,,,EDVAL1,12,107,47,30,10\n"
-		".,.,,,LTEXT,-3,140,47,20,8\n"
-		".,.,,,RTEXT,13,5,59,58,8\n"
-		".,.,,,EDVAL1,14,64,59,30,10\n"
-		".,.,,,RTEXT,-5,95,59,10,8\n"
-		".,.,,,EDVAL1,15,107,59,30,10\n"
-		".,.,,,LTEXT,-3,140,59,20,8\n"
-		".,.,,,LTEXT,16,10,84,60,8\n"
-		".,.,,,RTEXT,17,5,96,58,8\n"
-		".,.,,,EDVAL1,18,64,96,30,10\n"
-		".,.,,,RTEXT,-5,95,96,10,8\n"
-		".,.,,,EDVAL1,19,107,96,30,10\n"
-		".,.,,,LTEXT,-3,140,96,20,8\n"
-		".,.,,,RTEXT,20,5,108,58,8\n"
-		".,.,,,EDVAL1,21,64,108,30,10\n"
-		".,.,,,RTEXT,-5,95,108,10,8\n"
-		".,.,,,EDVAL1,22,107,108,30,10\n"
-		".,,,,LTEXT,-3,140,108,20,8\n"
-		"300,+,,,LTEXT,23,20,30,60,8\n"
-		".,400,310,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
-		"310,+,,EXRADIO,ODBUTTON,24,20,42,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,24,45,42,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,24,70,42,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,24,95,42,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,24,120,42,25,25\n"
-		".,.,,CHECKED | TOUCHEXIT,RADIO1, 25, 12,85,40,8\n"
-		".,.,,TOUCHEXIT,RADIO1,26,12,93,40,8\n"
-		".,.,,TOUCHEXIT,RADIO1,27,12,101,40,8\n"
-		".,.,,TOUCHEXIT,CHECKBOX,28,80,85,40,8\n"
-		".,,,TOUCHEXIT, CHECKBOX,29,80,93,40,8\n"
-		"400,,410,HIDDEN | CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
-		"410,+,,EXRADIO,ODBUTTON,30,20,42,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,30,45,42,25,25\n"
-		".,,,EXRADIO, ODBUTTON,30,70,42,25,25\n"
-		"500,+,,EXRADIO,ODBUTTON,31,20,60,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,45,60,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,70,60,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,20,85,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,45,85,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,95,85,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,120,85,25,25\n"
-		".,,,EXRADIO,ODBUTTON,31,70,85,25,25\n"
-		"520,+,,EXRADIO,ODBUTTON, 31, 20,50,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,45,50,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,70,50,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,95,50,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,120,50,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,20,75,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,45,75,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,70,75,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,95,75,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,120,75,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,20,100,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,45,100,25,25\n"
-		".,,,EXRADIO,ODBUTTON,31,70,100,25,25\n"
-		"540,+,,EXRADIO,ODBUTTON,31,20,60,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,45,60,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,70,60,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,95,60,25,25\n"
-		".,,,TOUCHEXIT | ISRADIO,ODBUTTON, 31, 120,60,25,25\n"
-		"560,+,,EXRADIO,ODBUTTON, 31,20,60,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,45,60,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,70,60,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,95,60,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,120,60,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,20,85,25,25\n"
-		".,.,,EXRADIO,ODBUTTON,31,45,85,25,25\n"
-		".,,,LASTOBJ | EXRADIO, ODBUTTON, 31, 70,85,25,25";
-
-static int selSheet = 102, selPlt = 520, selAxis = 310;
-bool
-Graph::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 22, 10, "Data"};
-	TabSHEET tab2 = {22, 62, 10, "Placement"};
-	TabSHEET tab3 = {62, 87, 10, "Axes"};
-	TabSHEET tab_A = {0, 27, 10, "only Y"};
-	TabSHEET tab_B = {27, 65, 10, "XY values"};
-	TabSHEET tab_C = {65, 104, 10, "X, many Y"};
-	TabSHEET tab_D = {104, 147, 10, "XYZ values"};
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)"arrangement of data: select plot",
-		(void*)&tab_A, (void*)&tab_B, (void*)&tab_C, (void*)&tab_D, (void*)"bounding rectangle (relative to page)",
-		(void*)"upper left corner x", (void*)&GRect.Xmin, (void*)&GRect.Ymin, (void*)"lower right x",
-		(void*)&GRect.Xmax, (void*)&GRect.Ymax, (void*)"plotting rectangle (relative to bounding rectangle)",
-		(void*)"upper left corner x", (void*)&DRect.Xmin, (void*)&DRect.Ymin, (void*)"lower right x",
-		(void*)&DRect.Xmax, (void*)&DRect.Ymax, (void*)"select template:", (void*)(OD_AxisTempl),
-		(void*)"ticks outside", (void*)"ticks inside", (void*)"ticks symmetrical", (void*)"horizontal grid lines",
-		(void*)"vertical grid lines", (void*)(OD_AxisTempl3D), (void*)(OD_PlotTempl)};
-	DlgInfo *GraphDlg = CompileDialog(GraphDlgTmpl, dyndata);
-	DlgRoot *Dlg;
-	GraphObj *p;
-	void *hDlg;
-	int i, res;
-	bool bRet, bContinue;
-	fRECT rc1, rc2;
-
-	ODtickstyle = tickstyle;
-	AxisTempl = 0;
-	if(!parent) return false;
-	Dlg = new DlgRoot(GraphDlg, data);
-	Dlg->SetCheck(410 + AxisTempl3D, 0L, true);
-	if(parent->Id != GO_PAGE) {
-		Dlg->Activate(202, false);	Dlg->Activate(204, false);
-		}
-	//restore previous settitings
-	switch(selSheet) {
-		case 101:
-			if(selPlt >= 500 && selPlt <=507) Dlg->SetCheck(selPlt, 0L, true);
-			else Dlg->SetCheck(500, 0L, true);
-			Dlg->SetCheck(520, 0L, true);		Dlg->SetCheck(540, 0L, true);
-			Dlg->SetCheck(560, 0L, true);		break;
-		default:
-			if(selPlt >= 520 && selPlt <=532) Dlg->SetCheck(selPlt, 0L, true);
-			else Dlg->SetCheck(520, 0L, true);	selSheet = 102;
-			Dlg->SetCheck(500, 0L, true);		Dlg->SetCheck(540, 0L, true);
-			Dlg->SetCheck(560, 0L, true);		break;
-		case 103:
-			if(selPlt >= 540 && selPlt <=544) Dlg->SetCheck(selPlt, 0L, true);
-			else Dlg->SetCheck(540, 0L, true);
-			Dlg->SetCheck(520, 0L, true);		Dlg->SetCheck(500, 0L, true);
-			Dlg->SetCheck(560, 0L, true);		break;
-		case 104:
-			if(selPlt >= 560 && selPlt <=567) Dlg->SetCheck(selPlt, 0L, true);
-			else Dlg->SetCheck(560, 0L, true);
-			Dlg->ShowItem(301, false);	Dlg->ShowItem(400, true);
-			Dlg->SetCheck(520, 0L, true);		Dlg->SetCheck(540, 0L, true);
-			Dlg->SetCheck(500, 0L, true);		break;
-		}
-	Dlg->SetCheck(selSheet, 0L, true);
-	if(selAxis >= 310 && selAxis <= 314) Dlg->SetCheck(selAxis, 0L, true);
-	else Dlg->SetCheck(310, 0L, true);
-	if(selAxis >= 410 && selAxis <= 412) Dlg->SetCheck(selAxis, 0L, true);
-	else Dlg->SetCheck(410, 0L, true);
-	//display the dialog
-	hDlg = CreateDlgWnd("Create graph", 50, 50, 450, 300, Dlg, 0x0L);
-	bContinue = false;
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		bRet = false;
-		switch(res) {
-		case 0:
-			if(bContinue) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 101:	//only y data
-			for(i = 500; i <= 506; i++) if(Dlg->GetCheck(i))selPlt = i;
-			Dlg->ShowItem(301, true);	Dlg->ShowItem(400, false);
-			selSheet = res;				res = -1;
-			break;
-		case 102:	//xy data
-			for(i = 520; i <= 532; i++) if(Dlg->GetCheck(i))selPlt = i;
-			Dlg->ShowItem(301, true);	Dlg->ShowItem(400, false);
-			selSheet = res;				res = -1;
-			break;
-		case 103:	//x many y data
-			for(i = 540; i <= 544; i++) if(Dlg->GetCheck(i))selPlt = i;
-			Dlg->ShowItem(301, true);	Dlg->ShowItem(400, false);
-			selSheet = res;				res = -1;
-			break;
-		case 104:	//xyz data
-			for(i = 560; i <= 567; i++) if(Dlg->GetCheck(i))selPlt = i;
-			Dlg->ShowItem(301, false);	Dlg->ShowItem(400, true);
-			selSheet = res;				res = -1;
-			break;
-		case 310:	case 311:	case 312:	case 313:	case 314:
-			AxisTempl = res-310;
-			if(res == selAxis) Dlg->SetCheck(4, 0L, true);
-			selAxis = res;
-			res = -1;
-			break;
-		case 315:	case 316:	case 317:	//tick style
-			tickstyle = res -315;
-		case 318:	case 319:				//horizontal or vertical grid
-			tickstyle &= ~0x300;
-			if(Dlg->GetCheck(318)) tickstyle |= 0x200;
-			if(Dlg->GetCheck(319)) tickstyle |= 0x100;
-			ODtickstyle = tickstyle;
-			Dlg->DoPlot(0L);
-			res = -1;
-			break;
-		case 410:	case 411:	case 412:	//axis templates
-			AxisTempl3D = res-410;
-			res = -1;
-			break;
-		case 500:	case 501:	case 502:	case 503:
-		case 504:	case 505:	case 506:	case 507:
-		case 520:	case 521:	case 522:	case 523:
-		case 524:	case 525:	case 526:	case 527:
-		case 528:	case 529:	case 530:	case 531:	case 532:
-		case 540:	case 541:	case 542:	case 543:
-		case 544:
-		case 560:	case 561:	case 562:	case 563:
-		case 564:	case 565:	case 566:	case 567:
-			if(res != selPlt) {
-				selPlt = res;
-				res = -1;
-				break;
-				}
-			//double click means select: continue as if OK
-			res = 1;
-		case 1:
-			if(!Dlg->GetCheck(4)) {
-				Dlg->SetCheck(4, 0L, true);
-				res = -1;
-				break;
-				}
-			memcpy(&rc1, &GRect, sizeof(fRECT));
-			memcpy(&rc2, &DRect, sizeof(fRECT));
-			Dlg->GetValue(202, &rc1.Xmin);			Dlg->GetValue(204, &rc1.Ymin);
-			Dlg->GetValue(207, &rc1.Xmax);			Dlg->GetValue(209, &rc1.Ymax);
-			Dlg->GetValue(213, &rc2.Xmin);			Dlg->GetValue(215, &rc2.Ymin);
-			Dlg->GetValue(218, &rc2.Xmax);			Dlg->GetValue(220, &rc2.Ymax);
-			if(rc1.Xmin < 0.0 || rc1.Xmax < 0.0 || rc1.Ymin < 0.0 || 
-				rc1.Ymax < 0.0 || rc2.Xmin < 0.0 || rc2.Xmax < 0.0 || 
-				rc2.Ymin < 0.0 || rc2.Ymax < 0.0) {
-				ErrorBox("All values defining\nthe placement of the plot\n"
-					"must be positive.");
-				res = -1;
-				bContinue = true;
-				Dlg->SetCheck(5, 0L, true);
-				break;
-				}
-			if(rc2.Xmin > (rc1.Xmax-rc1.Xmin) || rc2.Ymax > (rc1.Ymax-rc1.Ymin) 
-				|| (rc2.Xmax-rc2.Xmin) > (rc1.Xmax-rc1.Xmin)
-				|| (rc2.Ymax-rc2.Ymin) > (rc1.Ymax-rc1.Ymin)){
-				ErrorBox("The plotting rectangle must\nfit inside the\n"
-					"bounding rectangle.");
-				res = -1;
-				bContinue = true;
-				Dlg->SetCheck(5, 0L, true);
-				break;
-				}
-			memcpy(&GRect, &rc1, sizeof(fRECT));
-			memcpy(&DRect, &rc2, sizeof(fRECT));
-			p = 0L;
-			if(Dlg->GetCheck(101)) {
-				if(Dlg->GetCheck(500))p = new PieChart(this, data);
-				else if(Dlg->GetCheck(501))p = new RingChart(this, data);
-				else if(Dlg->GetCheck(502))p = new StarChart(this, data);
-				else if(Dlg->GetCheck(503))p = new BarChart(this, data);
-				else if(Dlg->GetCheck(504))p = new GroupBars(this, data, 0);
-				else if(Dlg->GetCheck(505))p = new FreqDist(this, data);
-				else if(Dlg->GetCheck(506))p = new NormQuant(this, data, 0L);
-				else if(Dlg->GetCheck(507))p = new GroupBars(this, data, 1);
-				}
-			else if(Dlg->GetCheck(102)){
-				if(Dlg->GetCheck(524)) p = new BubblePlot(this, data);
-				else if(Dlg->GetCheck(525)) p = new BoxPlot(this, data);
-				else if(Dlg->GetCheck(521)) p = new PlotScatt(this, data, 0x03);
-				else if(Dlg->GetCheck(522)) p = new PlotScatt(this, data, 0x08);
-				else if(Dlg->GetCheck(523)) p = new PlotScatt(this, data, 0x04);
-				else if(Dlg->GetCheck(526)) p = new Regression(this, data);
-				else if(Dlg->GetCheck(527)) p = new PolarPlot(this, data);
-				else if(Dlg->GetCheck(528)) p = new DensDisp(this, data);
-				else if(Dlg->GetCheck(529)) p = new Function(this, data, "Function");
-				else if(Dlg->GetCheck(530)) p = new FitFunc(this, data);
-				else if(Dlg->GetCheck(531)) p = new MultiLines(this, data);
-				else if(Dlg->GetCheck(532)) p = new xyStat(this, data);
-				else p = new PlotScatt(this, data, 0x01);
-				}
-			else if(Dlg->GetCheck(103)) {
-				if(Dlg->GetCheck(540)) p = new StackBar(this, data);
-				else if(Dlg->GetCheck(542)) p = new Waterfall(this, data);
-				else if(Dlg->GetCheck(543)) p = new Chart25D(this, data, 0L);
-				else if(Dlg->GetCheck(544)) p = new Ribbon25D(this, data, 0L);
-				else p = new StackPG(this, data);
-				}
-			else if(Dlg->GetCheck(104)) {
-				if(Dlg->GetCheck(560)) p = new Plot3D(this, data, 0x1001);
-				else if(Dlg->GetCheck(561)) p = new Plot3D(this, data, 0x1002);
-				else if(Dlg->GetCheck(562)) p = new Plot3D(this, data, 0x1004);
-				else if(Dlg->GetCheck(563)) p = new BubblePlot3D(this, data);
-				else if(Dlg->GetCheck(564)) p = new Plot3D(this, data, 0x2000);
-				else if(Dlg->GetCheck(565)) p = new Func3D(this, data);
-				else if(Dlg->GetCheck(566)) p = new FitFunc3D(this, data);
-				else if(Dlg->GetCheck(567)) p = new Plot3D(this, data, 0x4000);
-				}
-			if(p && p->PropertyDlg()) {
-				if(!Command(CMD_DROP_PLOT, p, 0L)) DeleteGO(p);
-				else bRet = true;
-				}
-			else if(p) {
-				Dlg->SetCheck(410 + AxisTempl3D, 0L, true);
-				DeleteGO(p);		Dlg->DoPlot(0L);			ShowDlgWnd(hDlg);
-				}
-			if(!bRet) {
-				res = -1;
-				//we might have lost the focus
-				bContinue = true;
-				}
-			break;
-			}
-		}while(res <0);
-	Command(CMD_SET_DATAOBJ, (void*)data, 0L);
-	CloseDlgWnd(hDlg);		delete Dlg;		free(GraphDlg);
-	return bRet;
-}
-
-bool
-Graph::Configure()
-{
-	TabSHEET tab1 = {0, 28, 10, "Colors"};
-	TabSHEET tab2 = {28, 68, 10, "Placement"};
-	DlgInfo GraphDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 170, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 170, 25, 45, 12},
-		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Add Plot", 170, 73, 45, 12},
-		{4, 5, 0, 0x0L, PUSHBUTTON, (void*)"Add Axis", 170, 88, 45, 12},
-		{5, 6, 0, 0x0L, PUSHBUTTON, (void*)"Add Legend", 170, 103, 45, 12},
-		{6, 13, 0, 0x0L, PUSHBUTTON, (void*)"Layers", 170, 118, 45, 12},
-		{13, 50, 14, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
-		{14, 15, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 155, 120},
-		{15, 0, 200, ISPARENT, SHEET, &tab2, 5, 10, 155, 120},
-		{50, 0, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
-		{100, 101, 0, 0x0L, LTEXT, (void*)"bounding rectangle", 20, 30, 60, 8},
-		{101, 102, 0, 0x0L, RTEXT, (void*)"fill color", 15, 42, 58, 8},
-		{102, 103, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&ColGR, 74, 42, 25, 10},
-		{103, 104, 0, 0x0L, RTEXT, (void*)"outline color", 15, 54, 58, 8},
-		{104, 105, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&ColGRL, 74, 54, 25, 10},
-		{105, 106, 0, 0x0L, LTEXT, (void*)"plotting rectangle", 20, 68, 60, 8},
-		{106, 107, 0, 0x0L, RTEXT, (void*)"fill color", 15, 80, 58, 8},
-		{107, 108, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&ColDR, 74, 80, 25, 10},
-		{108, 109, 0, 0x0L, LTEXT, (void*)"axes, ticks, and axis labels", 20, 94, 60, 8},
-		{109, 110, 0, 0x0L, RTEXT, (void*)"axis color", 15, 106, 58, 8},
-		{110, 0, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&ColAX, 74, 106, 25, 10},
-		{200, 201, 0, 0x0L, LTEXT, (void*)"bounding rectangle (relative to page)", 10, 35, 60, 8},
-		{201, 202, 0, 0x0L, RTEXT, (void*)"upper left corner x", 5, 47, 58, 8},
-		{202, 203, 0, 0x0L, EDVAL1, &GRect.Xmin, 64, 47, 30, 10},
-		{203, 204, 0, 0x0L, RTEXT, (void*)"y", 95, 47, 10, 8},
-		{204, 205, 0, 0x0L, EDVAL1, &GRect.Ymin, 107, 47, 30, 10},
-		{205, 206, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 140, 47, 20, 8},
-		{206, 207, 0, 0x0L, RTEXT, (void*)"lower right x", 5, 59, 58, 8},
-		{207, 208, 0, 0x0L, EDVAL1, &GRect.Xmax, 64, 59, 30, 10},
-		{208, 209, 0, 0x0L, RTEXT, (void*)"y", 95, 59, 10, 8},
-		{209, 210, 0, 0x0L, EDVAL1, &GRect.Ymax, 107, 59, 30, 10},
-		{210, 211, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 140, 59, 20, 8},
-		{211, 212, 0, 0x0L, LTEXT, (void*)"plotting rectangle (relative to bounding rectangle)", 10, 84, 60, 8},
-		{212, 213, 0, 0x0L, RTEXT, (void*)"upper left corner x", 5, 96, 58, 8},
-		{213, 214, 0, 0x0L, EDVAL1, &DRect.Xmin, 64, 96, 30, 10},
-		{214, 215, 0, 0x0L, RTEXT, (void*)"y", 95, 96, 10, 8},
-		{215, 216, 0, 0x0L, EDVAL1, &DRect.Ymin, 107, 96, 30, 10},
-		{216, 217, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 140, 96, 20, 8},
-		{217, 218, 0, 0x0L, RTEXT, (void*)"lower right x", 5, 108, 58, 8},
-		{218, 219, 0, 0x0L, EDVAL1, &DRect.Xmax, 64, 108, 30, 10},
-		{219, 220, 0, 0x0L, RTEXT, (void*)"y", 95, 108, 10, 8},
-		{220, 221, 0, 0x0L, EDVAL1, &DRect.Ymax, 107, 108, 30, 10},
-		{221, 0, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 140, 108, 20, 8},
-		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 170, 40, 15, 15},
-		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 190, 40, 15, 15},
-		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 190, 55, 15, 15},
-		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 170, 55, 15, 15}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, res, undo_level = *Undo.pcb;
-	DWORD undo_flags = 0, tmpcol;
-	anyOutput *cdisp = Undo.cdisp;
-	bool bRet = false, bContinue = false;
-	fRECT o_gr, n_gr, o_dr, n_dr;
-
-	if(!(Dlg = new DlgRoot(GraphDlg, data)))return false;
-	Dlg->GetValue(202, &o_gr.Xmin);			Dlg->GetValue(204, &o_gr.Ymin);
-	Dlg->GetValue(207, &o_gr.Xmax);			Dlg->GetValue(209, &o_gr.Ymax);
-	Dlg->GetValue(213, &o_dr.Xmin);			Dlg->GetValue(215, &o_dr.Ymin);
-	Dlg->GetValue(218, &o_dr.Xmax);			Dlg->GetValue(220, &o_dr.Ymax);
-	if(parent && parent->Id == GO_PAGE) Dlg->ShowItem(50, true);
-	i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, name ? name : (char*)"Graph");
-	rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, (char*)" properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 450, 300, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();			res = Dlg->GetResult();
-		switch (res) {
-		case 600:	case 601:	case 602:	case 603:
-			Undo.SetDisp(cdisp);
-			res = ExecDrawOrderButt(parent, this, res);
-			}
-		switch(res) {
-		case 0:
-			if(bContinue) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 102:	case 104:	case 107:	case 110:
-			res = -1;
-			break;
-		case 1:
-			Dlg->GetValue(202, &n_gr.Xmin);			Dlg->GetValue(204, &n_gr.Ymin);
-			Dlg->GetValue(207, &n_gr.Xmax);			Dlg->GetValue(209, &n_gr.Ymax);
-			Dlg->GetValue(213, &n_dr.Xmin);			Dlg->GetValue(215, &n_dr.Ymin);
-			Dlg->GetValue(218, &n_dr.Xmax);			Dlg->GetValue(220, &n_dr.Ymax);
-			if(n_gr.Xmin < 0.0 || n_gr.Xmax < 0.0 || n_gr.Ymin < 0.0 || 
-				n_gr.Ymax < 0.0 || n_dr.Xmin < 0.0 || n_dr.Xmax < 0.0 || 
-				n_dr.Ymin < 0.0 || n_dr.Ymax < 0.0) {
-				ErrorBox("All values defining\nthe placement of the plot\n"
-					"must be positive.");
-				res = -1;
-				bContinue = true;
-				Dlg->SetCheck(5, 0L, true);
-				break;
-				}
-			if(n_dr.Xmin > (n_gr.Xmax-n_gr.Xmin) || n_dr.Ymax > (n_gr.Ymax-n_gr.Ymin) 
-				|| (n_dr.Xmax-n_dr.Xmin) > (n_gr.Xmax-n_gr.Xmin)
-				|| (n_dr.Ymax-n_dr.Ymin) > (n_gr.Ymax-n_gr.Ymin)){
-				ErrorBox("The plotting rectangle must\nfit inside the\n"
-					"bounding rectangle.");
-				res = -1;
-				bContinue = true;
-				Dlg->SetCheck(5, 0L, true);
-				break;
-				}
-			bRet = true;
-			break;
-		case 3:
-			if(bRet = Command(CMD_ADDPLOT, 0L, 0L)) break;
-		case 4:
-			if(res == 4 && (bRet = Command(CMD_ADDAXIS, 0L, 0L))) break;
-			bContinue = true;
-			res = -1;
-			break;
-		case 5:
-			Command(CMD_LEGEND, 0L, 0L);
-			break;
-		case 6:
-			Command(CMD_LAYERS, 0L, 0L);
-			bContinue = true;
-			res = -1;
-			break;
-			}
-		}while(res <0);
-	if(res == 1 && bRet) {
-		Undo.SetDisp(cdisp);
-		if(n_gr.Xmin != o_gr.Xmin || n_gr.Xmax != o_gr.Xmax ||
-			n_gr.Ymin != o_gr.Ymin || n_gr.Ymax != o_gr.Ymax ||
-			n_dr.Xmin != o_dr.Xmin || n_dr.Xmax != o_dr.Xmax ||
-			n_dr.Ymin != o_dr.Ymin || n_dr.Ymax != o_dr.Ymax){
-			Command(CMD_SAVEPOS, 0L, 0L);
-			memcpy(&GRect, &n_gr, sizeof(fRECT));
-			memcpy(&DRect, &n_dr, sizeof(fRECT));
-			undo_flags |= UNDO_CONTINUE;
-			}
-		if(Dlg->GetColor(102, &tmpcol))
-			undo_flags = CheckNewDword(&ColGR, ColGR, tmpcol, this, undo_flags);
-		if(Dlg->GetColor(104, &tmpcol))
-			undo_flags = CheckNewDword(&ColGRL, ColGRL, tmpcol, this, undo_flags);
-		if(Dlg->GetColor(107, &tmpcol))
-			undo_flags = CheckNewDword(&ColDR, ColDR, tmpcol, this, undo_flags);
-		if(Dlg->GetColor(110, &tmpcol) && tmpcol != ColAX) {
-			undo_flags = CheckNewDword(&ColAX, ColAX, tmpcol, this, undo_flags);
-			if(Axes) for(i = 0; i < NumAxes; i++)
-				if(Axes[i]) Axes[i]->SetColor(COL_AXIS | UNDO_STORESET, ColAX);
-			if(Plots && NumPlots && Plots[0] && (Plots[0]->Id == GO_PLOT3D || Plots[0]->Id == GO_FUNC3D)) 
-				Plots[0]->SetColor(COL_AXIS | UNDO_STORESET, ColAX);
-			}
-		}
-	else if(res == 2) {
-		Undo.SetDisp(cdisp);
-		if(*Undo.pcb > undo_level) {	//restore plot order
-			while(*Undo.pcb > undo_level)	Undo.Restore(false, 0L);
-			bRet = true;
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-static char *AddAxisTmpl =
-	"1,2,,DEFAULT, PUSHBUTTON,-1,148,10,45,12\n"
-	"2,3,,,PUSHBUTTON,-2,148,25,45,12\n"
-	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"4,5,50,TOUCHEXIT | ISPARENT,SHEET,1,5,10,130,130\n"
-	"5,6,200,ISPARENT | CHECKED,SHEET,2,5,10,130,130\n"
-	"6,,300,ISPARENT,SHEET,3,5,10,130,130\n"
-	"50,51,100,ISPARENT | CHECKED,GROUPBOX,4,10,30,120,36\n"
-	"51,120,,,CHECKBOX,5,17,37,80,8\n"
-	"100,101,,,RTEXT,6,10,51,35,8\n"
-	"101,102,,,EDVAL1,7,48,51,32,10\n"
-	"102,103,,,CTEXT,8,81,51,11,8\n"
-	"103,,,,EDVAL1,9,93,51,32,10\n"
-	"120,,121,ISPARENT | CHECKED,GROUPBOX,10,10,72,120,20\n"
-	"121,122,,,RTEXT,11,10,77,25,8\n"
-	"122,123,,,EDVAL1,12,37,77,25,10\n"
-	"123,124,,,LTEXT,-3,63,77,10,8\n"
-	"124,125,,,RTEXT,-11,73,77,25,8\n"
-	"125,130,,OWNDIALOG,COLBUTT,14,100,77,25,10\n"
-	"130,131,,ISPARENT | CHECKED,GROUPBOX,15,10,98,120,20\n"
-	"131,,,,EDTEXT,0,15,103,110,10\n"
-	"200,201,,,LTEXT,16,10,30,70,9\n"
-	"201,202,,CHECKED | EXRADIO,ODBUTTON,17,20,42,25,25\n"
-	"202,203,,EXRADIO,ODBUTTON,17,45,42,25,25\n"
-	"203,204,,EXRADIO,ODBUTTON,17,70,42,25,25\n"
-	"204,205,,EXRADIO,ODBUTTON,17,95,42,25,25\n"
-	"205,206,,EXRADIO,ODBUTTON,17,20,67,25,25\n"
-	"206,207,,EXRADIO,ODBUTTON,17,45,67,25,25\n"
-	"207,208,,EXRADIO,ODBUTTON,17,70,67,25,25\n"
-	"208,210,,EXRADIO,ODBUTTON,17,95,67,25,25\n"
-	"210,,220,ISPARENT | CHECKED, GROUPBOX,18,10,97,120,35\n"
-	"220,221,,,RTEXT,-4,10,105,15,8\n"
-	"221,222,,,EDVAL1,19,27,105,35,10\n"
-	"222,223,,,LTEXT,-7,65,105,5,8\n"
-	"223,224,,,EDVAL1,20,71,105,35,10\n"
-	"224,225,,,LTEXT,-3,109,105,15,8\n"
-	"225,226,,,RTEXT,-5,10,117,15,8\n"
-	"226,227,,,EDVAL1,21,27,117,35,10\n"
-	"227,228,,,LTEXT,-7,65,117,5,8\n"
-	"228,229,,,EDVAL1,22,71,117,35,10\n"
-	"229,,,,LTEXT,-3,109,117,15,8\n"
-	"300,,,LASTOBJ | NOSELECT,ODBUTTON,23,15,30,110,140";
-
-bool
-Graph::AddAxis()
-{
-	TabSHEET tab1 = {0, 25, 10, "Axis"};
-	TabSHEET tab2 = {25, 52, 10, "Style"};
-	TabSHEET tab3 = {52, 78, 10, "Plots"};
-	AxisDEF axis;
-	double sizAxLine = DefSize(SIZE_AXIS_LINE);
-	DWORD colAxis = ColAX;
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)" scaling ", (void*)" automatic scaling",
-		(void*)"axis from", (void*)&y_axis.min, (void*)"to", (void*)&y_axis.max, (void*)" line ", (void*)"width",
-		(void*)&sizAxLine, (void*)0L, (void *)&colAxis, (void*)" axis label ", (void*)"select a template:",
-		(void*)(OD_NewAxisTempl), (void*)" placement ", (void*)&axis.loc[0].fx, (void*)&axis.loc[1].fx,
-		(void*)&axis.loc[0].fy, (void*)&axis.loc[1].fy,  (void*)OD_axisplot};
-	DlgInfo *NewAxisDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, j, res, currTempl = 201;
-	double vx1, vx2, vy1, vy2, hx1, hx2, hy1, hy2;
-	double tlb_dist, tlb_dx, tlb_dy, lb_x, lb_y;
-	TextDEF label_def, tlbdef;
-	DWORD flags;
-	anyOutput *cdisp = Undo.cdisp;
-	Axis *the_new, **tmpAxes;
-	bool bAxis = false, bRet = false;
-	char **names;
-	GraphObj **somePlots;
-	Label *label;
-
-	if(!(NewAxisDlg = CompileDialog(AddAxisTmpl, dyndata)))return false;
-	if(!(names = (char**)calloc(nscp+2, sizeof(char*))))return false;
-	if(!(somePlots = (GraphObj**)calloc(nscp+2, sizeof(GraphObj*))))return false;
-	if(!Axes) Axes = (Axis**)calloc(2, sizeof(Axis*));
-	if(names[0] = (char*)malloc(10)) rlp_strcpy(names[0], 10, "[none]");
-	for(i = 0, j = 1; i < nscp; i++) {
-		if(Sc_Plots[i] && Sc_Plots[i]->name){
-			names[j] = (char*)memdup(Sc_Plots[i]->name, (int)strlen(Sc_Plots[i]->name)+1, 0);
-			somePlots[j++] = Sc_Plots[i];
-			}
-		}
-	OD_axisplot(OD_ACCEPT, 0L, 0L, 0L, names, 0);
-	axis.loc[0].fx = axis.loc[1].fx = vx1 = vx2 = (DRect.Xmin + DRect.Xmax)/2.0;
-	axis.loc[0].fy = vy1 = DRect.Ymin;
-	axis.loc[1].fy = vy2 = DRect.Ymax;
-	axis.min = y_axis.min;		axis.max = y_axis.max;
-	hy1 = hy2 = (DRect.Ymax + DRect.Ymin)/2.0;
-	hx1 = DRect.Xmin;			hx2 = DRect.Xmax;
-	if(!(Dlg = new DlgRoot(NewAxisDlg, data)))return false;
-	if(type != 1 || !nscp){		//must be standard graph to link to plot
-		Dlg->ShowItem(6, false);
-		}
-	hDlg = CreateDlgWnd("New axis properties", 50, 50, 400, 318, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res){
-		case 4:											//the axis sheet
-			res = -1;
-			bAxis = true;
-			break;
-		case 201:	case 202:	case 203:	case 204:	//axis templates
-		case 205:	case 206:	case 207:	case 208:
-			if(currTempl > 204) {
-				Dlg->GetValue(221, &hx1);				Dlg->GetValue(223, &hx2);
-				Dlg->GetValue(226, &hy1);				Dlg->GetValue(228, &hy2);
-				}
-			else {
-				Dlg->GetValue(221, &vx1);				Dlg->GetValue(223, &vx2);
-				Dlg->GetValue(226, &vy1);				Dlg->GetValue(228, &vy2);
-				}
-			if(res > 204) {
-				Dlg->SetValue(221, hx1);				Dlg->SetValue(223, hx2);
-				Dlg->SetValue(226, hy1);				Dlg->SetValue(228, hy2);
-				if(! bAxis) {
-					Dlg->SetValue(101, x_axis.min);		Dlg->SetValue(103, x_axis.max);
-					}
-				}
-			else {
-				Dlg->SetValue(221, vx1);				Dlg->SetValue(223, vx2);
-				Dlg->SetValue(226, vy1);				Dlg->SetValue(228, vy2);
-				if(! bAxis) {
-					Dlg->SetValue(101, y_axis.min);		Dlg->SetValue(103, y_axis.max);
-					}
-				}
-			currTempl = res;
-			Dlg->DoPlot(0L);
-			res = -1;
-			break;
-			}
-		}while (res < 0);
-	if(res == 1) {
-		Undo.SetDisp(cdisp);
-		Dlg->GetValue(122, &sizAxLine);				Dlg->GetColor(125, &colAxis);
-		Dlg->GetValue(221, &axis.loc[0].fx);		Dlg->GetValue(223, &axis.loc[1].fx);
-		Dlg->GetValue(226, &axis.loc[0].fy);		Dlg->GetValue(228, &axis.loc[1].fy);
-		axis.loc[0].fz = axis.loc[1].fz = 0.0;		axis.owner = 0L;
-		Dlg->GetValue(101, &axis.min);				Dlg->GetValue(103, &axis.max);
-		axis.Start = axis.min;				axis.Center.fx = axis.Center.fy = 0.0;
-		axis.nBreaks = 0;			axis.breaks = 0L;		axis.Radius = 0.0;	
-		tlb_dist = NiceValue(DefSize(SIZE_AXIS_TICKS));
-		tlb_dx = tlb_dy = 0.0;
-		tlbdef.ColTxt = colAxis;				tlbdef.ColBg = 0x00ffffffL;
-		tlbdef.RotBL = tlbdef.RotCHAR = 0.0f;	tlbdef.fSize = DefSize(SIZE_TICK_LABELS);
-		tlbdef.Align = TXA_VCENTER | TXA_HCENTER;
-		tlbdef.Style = TXS_NORMAL;				tlbdef.Mode = TXM_TRANSPARENT;
-		tlbdef.Font = FONT_HELVETICA;			tlbdef.text = 0L;
-		label_def.ColTxt = colAxis;				label_def.ColBg = 0x00ffffffL;
-		label_def.fSize = DefSize(SIZE_TICK_LABELS)*1.2f;	label_def.RotBL = 0.0f;
-		label_def.RotCHAR = 0.0f;								label_def.iSize = 0;
-		label_def.Align = TXA_VTOP | TXA_HCENTER;		label_def.Mode = TXM_TRANSPARENT;
-		label_def.Style = TXS_NORMAL;	label_def.Font = FONT_HELVETICA;
-		switch (currTempl) {
-		default:	flags = AXIS_NEGTICKS;	axis.Step = y_axis.Step;
-			tlb_dx = -tlb_dist * 2.0;	tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
-			lb_x = -tlb_dist * 6.0;		lb_y = 0;	label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
-			label_def.RotBL = 90.0;
-			break;
-		case 202:	flags = AXIS_POSTICKS;	axis.Step = y_axis.Step;
-			tlb_dx = -tlb_dist;			tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
-			lb_x = -tlb_dist * 4.5;		lb_y = 0;	label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
-			label_def.RotBL = 90.0;
-			break;
-		case 203:	flags = AXIS_POSTICKS;	axis.Step = y_axis.Step;
-			tlb_dx = tlb_dist * 2.0;	tlbdef.Align = TXA_VCENTER | TXA_HLEFT;
-			lb_x = tlb_dist * 6;		lb_y = 0;	label_def.Align = TXA_VTOP | TXA_HCENTER;
-			label_def.RotBL = 90.0;
-			break;
-		case 204:	flags = AXIS_NEGTICKS;	axis.Step = y_axis.Step;
-			tlb_dx = tlb_dist;			tlbdef.Align = TXA_VCENTER | TXA_HLEFT;
-			lb_x = tlb_dist * 4.5;		lb_y = 0;	label_def.Align = TXA_VTOP | TXA_HCENTER;
-			label_def.RotBL = 90.0;
-			break;
-		case 205:	flags = AXIS_NEGTICKS;	axis.Step = x_axis.Step;
-			tlb_dy = tlb_dist * 2.0;	tlbdef.Align = TXA_VTOP | TXA_HCENTER;
-			lb_x = 0;					lb_y = tlb_dist * 4.0;
-			break;
-		case 206:	flags = AXIS_POSTICKS;	axis.Step = x_axis.Step;
-			tlb_dy = tlb_dist;			tlbdef.Align = TXA_VTOP | TXA_HCENTER;
-			lb_x = 0;					lb_y = tlb_dist * 3.0;
-			break;
-		case 207:	flags = AXIS_POSTICKS;	axis.Step = x_axis.Step;
-			tlb_dy = -tlb_dist * 2.0;	tlbdef.Align = TXA_VBOTTOM | TXA_HCENTER;
-			lb_x = 0;	lb_y = -tlb_dist * 4.0;	label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
-			break;
-		case 208:	flags = AXIS_NEGTICKS;	axis.Step = x_axis.Step;
-			tlb_dy = -tlb_dist;			tlbdef.Align = TXA_VBOTTOM | TXA_HCENTER;
-			lb_x = 0;	lb_y = -tlb_dist * 3.0;	label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
-			break;
-			}
-		flags |= AXIS_AUTOTICK;			flags |= AXIS_DEFRECT;
-		if(Dlg->GetCheck(51)) flags |= AXIS_AUTOSCALE;
-		if(the_new = new Axis(this, data, &axis, flags)){
-			the_new->SetSize(SIZE_TLB_YDIST, tlb_dy);	the_new->SetSize(SIZE_TLB_XDIST, tlb_dx); 
-			the_new->SetSize(SIZE_AXIS_LINE, sizAxLine);
-			the_new->SetColor(COL_AXIS, colAxis);
-			the_new->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			the_new->SetSize(SIZE_LB_XDIST, lb_x);		the_new->SetSize(SIZE_LB_YDIST, lb_y); 
-			if(Dlg->GetText(131, TmpTxt, TMP_TXT_SIZE)) label_def.text = TmpTxt;
-			else label_def.text = 0L;
-			if(label = new Label(Axes[0], data, (axis.loc[0].fx + axis.loc[1].fx)/2.0,
-				(axis.loc[0].fy + axis.loc[1].fy)/2.0, &label_def, 
-				label_def.RotBL < 45.0 ? LB_Y_PARENT : LB_X_PARENT)){
-				label->SetSize(SIZE_LB_XDIST, lb_x);	label->SetSize(SIZE_LB_YDIST, lb_y); 
-				if(the_new->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
-				else DeleteGO(label);
-				}
-			for(i = 0; i < NumAxes && Axes[i]; i++);
-			if(i < NumAxes) {
-				Undo.SetGO(this, (GraphObj**)(&Axes[i]), the_new, 0L);
-				bRet = true;
-				}
-			else {
-				if(tmpAxes = (Axis**)calloc(NumAxes+1, sizeof(Axis*))){
-					memcpy(tmpAxes, Axes, NumAxes * sizeof(Axis*));
-					Undo.ListGOmoved((GraphObj**)Axes, (GraphObj**)tmpAxes, NumAxes);
-					Undo.SetGO(this, (GraphObj**)(&tmpAxes[NumAxes]), the_new, 0L);
-					free(Axes);			Axes = tmpAxes;
-					i = NumAxes++;		bRet = true;
-					}
-				else delete (the_new);	//very unlikely memory allocation error
-				}
-			CurrAxes = Axes;
-			if(bRet) {
-				OD_axisplot(OD_ACCEPT, 0L, 0L, (anyOutput*) &res, 0L, 0);
-				if(res && i) somePlots[res]->Command(CMD_USEAXIS, &i, 0L);
-				}
-			}
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;			free(NewAxisDlg);
-	if(names) {
-		for(j = 0; names[j]; j++) if(names[j]) free(names[j]);
-		free(names);
-		}
-	if(somePlots) free(somePlots);
-	return bRet;
-}
-
-bool
-Page::Configure()
-{
-	TabSHEET tab1 = {0, 50, 10, "Paper Size"};
-	DlgInfo PageDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 135, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 135, 25, 45, 12},
-		{3, 0, 4, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 120, 120},
-		{5, 0, 0, 0x0L, PUSHBUTTON, (void*)"Layers", 135, 118, 45, 12},
-		{100, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_paperdef, 15, 30, 110, 140}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, cb;
-	bool bRet = false, bContinue = false;
-
-	FindPaper(GRect.Xmax - GRect.Xmin, GRect.Ymax -GRect.Ymin, .0001);
-	if(!(Dlg = new DlgRoot(PageDlg, data)))return false;
-	if(name)cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, name);
-	else cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Page");
-	rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE - cb, " properties");
-	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 380, 300, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 0:
-			if(bContinue) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 5:
-			Command(CMD_LAYERS, 0L, 0L);
-			bContinue = true;
-			res = -1;
-			}
-		}while(res <0);
-	if(res == 1) {
-		OD_paperdef(OD_ACCEPT, 0L, 0L, 0L, 0L, 0);
-		GRect.Xmin = GRect.Ymin = 0.0;
-		GetPaper(&GRect.Xmax, &GRect.Ymax);
-		DoPlot(CurrDisp);
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Edit global defaults
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool
-Default::PropertyDlg()
-{
-	TabSHEET tab1 = {0, 22, 10, "Line"};
-	TabSHEET tab2 = {22, 52, 10, "Shapes"};
-	TabSHEET tab3 = {52, 82, 10, "Dialogs"};
-	TabSHEET tab4 = {82, 116, 10, "Internat."};
-	TabSHEET tab5 = {116, 155, 10, "Date/Time"};
-	double ts =  dlgtxtheight;
-	time_t ti = time(0L);
-	char dt_info[50], date_info[50], datetime_info[50], time_info[50];
-	DlgInfo DefsDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 180, 10, 40, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 180, 25, 40, 12},
-		{3, 0, 4, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{4, 5, 100, TOUCHEXIT | ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 170, 130},
-		{5, 6, 200, TOUCHEXIT | ISPARENT, SHEET, &tab2, 5, 10, 170, 130},
-		{6, 7, 300, TOUCHEXIT | ISPARENT, SHEET, &tab3, 5, 10, 170, 130},
-		{7, 8, 400, TOUCHEXIT | ISPARENT, SHEET, &tab4, 5, 10, 170, 130},
-		{8, 0, 350, TOUCHEXIT | ISPARENT, SHEET, &tab5, 5, 10, 170, 130},
-		{100, 0, 0, NOSELECT, ODBUTTON, (void*)OD_linedef, 10, 38, 130, 100},
-		{200, 0, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 25, 40, 90, 50},
-		{300, 301, 0, 0x0L, RTEXT, (void*)"text size", 20, 30, 38, 8},
-		{301, 302, 0, 0x0L, EDVAL1, (void*)&ts, 60, 30, 20, 10},
-		{302, 0, 0, 0x0L, LTEXT, (void*)"pixel", 82, 30, 20, 8},
-		{350, 351, 0, 0x0L, LTEXT, (void*)dt_info, 10, 30, 120, 8},
-		{351, 352, 0, 0x0L, LTEXT, (void*)"date format:", 10, 43, 70, 8},
-		{352, 353, 0, 0x0L, EDTEXT, (void*)defs.fmt_date, 10, 53, 60, 10},
-		{353, 354, 0, 0x0L, LTEXT, (void*)date_info, 80, 54, 40, 10}, 
-		{354, 355, 0, 0x0L, LTEXT, (void*)"date + time format:", 10, 65, 70, 8},
-		{355, 356, 0, 0x0L, EDTEXT, (void*)defs.fmt_datetime, 10, 75, 60, 10},
-		{356, 357, 0, 0x0L, LTEXT, (void*)datetime_info, 80, 76, 40, 10}, 
-		{357, 358, 0, 0x0L, LTEXT, (void*)"time format:", 10, 87, 70, 8},
-		{358, 359, 0, 0x0L, EDTEXT, (void*)defs.fmt_time, 10, 97, 60, 10},
-		{359, 360, 0, 0x0L, LTEXT, (void*)time_info, 80, 98, 40, 10}, 
-		{360, 361, 0, 0x0L, LTEXT, (void*)"For further information about formats see", 10, 119, 140, 8},
-		{361, 362, 0, HREF | TOUCHEXIT, LTEXT, (void*)"http://rlplot.sourceforge.net/Docs/functions/datetime.html", 10, 127, 140, 8},
-		{362, 0, 0, 0x0L, PUSHBUTTON, (void*)"Test", 130, 107, 40, 12},
-		{400, 401, 0, 0x0L, LTEXT, (void*)"edit country specific information", 20, 30, 100, 8},
-		{401, 402, 0, 0x0L, RTEXT, (void*)"decimal point", 45, 45, 40, 8},
-		{402, 403, 0, 0x0L, EDTEXT, (void*)DecPoint, 90, 45, 10, 10},
-		{403, 404, 0, 0x0L, RTEXT, (void*)"column separator", 45, 57, 40, 8},
-		{404, 405, 0, 0x0L, EDTEXT, (void*)ColSep, 90, 57, 10, 10},
-		{405, 406, 0, 0x0L, RTEXT, (void*)"use units:", 25, 75, 47, 8},
-		{406, 0, 420, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{420, 421, 0, 0x0L, RADIO1, (void*)Units[0].display, 75, 75, 20, 8},
-		{421, 422, 0, 0x0L, RADIO1, (void*)Units[1].display, 75, 83, 20, 8},
-		{422, 0, 0, LASTOBJ, RADIO1, (void*)Units[2].display, 75, 91, 20, 8}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, cb, res, tmpUnits = cUnits;
-	bool bRet = false, bContinue = false;
-	double dt;
-	LineDEF LineDef;
-	FillDEF FillDef;
-
-	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)GetLine(), 0);
-	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)GetOutLine(), 0);
-	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)GetFill(), 0);
-	cb = rlp_strcpy(dt_info, 20, "today is ");
-#ifdef USE_WIN_SECURE
-	ctime_s(dt_info+cb, 50-cb, &ti);
-#else
-	rlp_strcpy(dt_info+cb, 50-cb, ctime(&ti));
-#endif
-	dt_info[cb+24] = 0;
-	date_value(dt_info+13, "x z H:M:S Y", &dt);
-	rlp_strcpy(date_info, 50, value_date(dt, defs.fmt_date));
-	rlp_strcpy(datetime_info, 50, value_date(dt, defs.fmt_datetime));
-	rlp_strcpy(time_info, 50, value_date(dt, defs.fmt_time));
-	Dlg = new DlgRoot(DefsDlg, 0L);
-	switch(dUnits) {
-	case 1:		Dlg->SetCheck(421, 0L, true);	break;
-	case 2:		Dlg->SetCheck(422, 0L, true);	break;
-	default:	Dlg->SetCheck(420, 0L, true);	break;
-		}
-#ifdef _WINDOWS
-	for(i = 360; i <= 361; i++) Dlg->TextSize(i, 12);
-#else
-	for(i = 360; i <= 361; i++) Dlg->TextSize(i, 10);
-#endif
-	hDlg = CreateDlgWnd("Edit Global Preferences", 50, 50, 460, 316, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 0:
-			if(bContinue) res = -1;
-			break;
-		case 362:				//update date/time display
-			ti = time(0L);		cb = rlp_strcpy(dt_info, 20, "today is ");
-#ifdef USE_WIN_SECURE
-			ctime_s(dt_info+cb, 50-cb, &ti);
-#else
-			rlp_strcpy(dt_info+cb, 50-cb, ctime(&ti));
-#endif
-			dt_info[cb+24] = 0;												Dlg->SetText(350, dt_info);
-			date_value(dt_info+13, "x z H:M:S Y", &dt);
-			if(!(Dlg->GetText(352, date_info, 50))) rlp_strcpy(date_info, 50, defs.fmt_date);
-			rlp_strcpy(date_info, 50, value_date(dt, date_info));			Dlg->SetText(353, date_info);
-			if(!(Dlg->GetText(355, datetime_info, 50))) rlp_strcpy(datetime_info, 50, defs.fmt_datetime);
-			rlp_strcpy(datetime_info, 50, value_date(dt, datetime_info));	Dlg->SetText(356, datetime_info);
-			if(!(Dlg->GetText(358, time_info, 50))) rlp_strcpy(time_info, 50, defs.fmt_time);
-			rlp_strcpy(time_info, 50, value_date(dt, time_info));			Dlg->SetText(359, time_info);
-			bContinue = false;		res = -1;
-			break;
-		case 361:			//call browser
-			bContinue = true;		res = -1;
-			break;
-		case 4:		case 5:		case 6:		case 7:
-			bContinue = false;		res = -1;
-			break;
-		case 8:
-			bContinue = true;		res = -1;
-			break;
-		case 1:
-			if(Dlg->GetCheck(421)) dUnits = 1;
-			else if(Dlg->GetCheck(422)) dUnits = 2;
-			else dUnits = 0;
-			}
-		}while (res < 0);
-	if(res == 1) {
-		if(Dlg->GetText(402, TmpTxt, TMP_TXT_SIZE))	DecPoint[0] = TmpTxt[0];
-		if(Dlg->GetText(404, TmpTxt, TMP_TXT_SIZE))	ColSep[0] = TmpTxt[0];
-		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
-		SetLine(tmpUnits, &LineDef, 0);
-		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
-		SetLine(tmpUnits, &LineDef, 2);
-		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&FillDef, 0);
-		SetFill(tmpUnits, &FillDef);
-		Dlg->GetInt(301, &dlgtxtheight);
-		if(Dlg->GetText(352, date_info, 50) && date_info[0] && strcmp(date_info, defs.fmt_date)) {
-			if(defs.fmt_date = (char*)realloc(defs.fmt_date, (cb = (int)strlen(date_info) +2)))
-				rlp_strcpy(defs.fmt_date, cb, date_info);
-			}
-		if(Dlg->GetText(355, datetime_info, 50) && datetime_info[0] && strcmp(datetime_info, defs.fmt_datetime)) {
-			if(defs.fmt_datetime = (char*)realloc(defs.fmt_datetime, (cb = (int)strlen(datetime_info) +2)))
-				rlp_strcpy(defs.fmt_datetime, cb, datetime_info);
-			}
-		if(Dlg->GetText(358, time_info, 50) && time_info[0] && strcmp(time_info, defs.fmt_time)) {
-			if(defs.fmt_time = (char*)realloc(defs.fmt_time, (cb = (int)strlen(time_info) +2)))
-				rlp_strcpy(defs.fmt_time, cb, time_info);
-			}
-		bRet = true;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	return bRet;
-}
+//PropertyDlg.cpp, Copyright (c) 2001-2008 R.Lackner
+//Property dialogs for graphic objects
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+#include "rlplot.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#include "TheDialog.h"
+
+extern tag_Units Units[];
+extern char TmpTxt[];
+extern TextDEF DlgText;
+extern Default defs;
+extern int dlgtxtheight;
+extern Axis **CurrAxes;
+extern UndoObj Undo;
+extern int AxisTempl3D;
+
+int ODtickstyle;
+
+//prototypes: WinSpec.cpp
+void *CreateDlgWnd(char *title, int x, int y, int width, int height, tag_DlgObj *d, DWORD flags);
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Symbol properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *SymDlg_DlgTmpl = 
+	"1,+,,DEFAULT,PUSHBUTTON,-1,145,10,60,12\n"
+	".,.,,,PUSHBUTTON,2,145,25,60,12\n"
+	".,.,,,PUSHBUTTON,-2,145,40,60,12\n"
+	".,50,5,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
+	".,+,100,TOUCHEXIT | ISPARENT | CHECKED,SHEET,3,5,10,130,113\n"
+	".,.,200,TOUCHEXIT | ISPARENT,SHEET,4,5,10,130,113\n"
+	".,,300,TOUCHEXIT | ISPARENT,SHEET,5,5,10,130,113\n"
+	"50,,,TOUCHEXIT,SYMBUTT,0,155,75,40,40\n"
+	"100,+,,,RTEXT,6,5,25,45,8\n"
+	".,.,,TOUCHEXIT,INCDECVAL1,9,55,25,32,10\n"
+	".,.,,,LTEXT,-3,89,25,20,8\n"
+	".,.,,,RTEXT,10,5,37,45,8\n"
+	".,.,,TOUCHEXIT,INCDECVAL1,11,55,37,32,10\n"
+	".,.,,,LTEXT,-3,89,37,20,8\n"
+	".,.,,,RTEXT,12,5,49,45,8\n"
+	".,.,,TOUCHEXIT | OWNDIALOG, COLBUTT,13,55,49,25,10\n"
+	".,.,,,RTEXT,14,5,61,45,8\n"
+	".,401,,TOUCHEXIT | OWNDIALOG,COLBUTT,15,55,61,25,10\n"
+	"200,204,201,CHECKED | ISPARENT, GROUPBOX,16,12,28,50,39\n"
+	".,+,,TOUCHEXIT, RADIO1,17,15,33,45,8\n"
+	".,.,,TOUCHEXIT, RADIO1,18,15,43,45,8\n"
+	".,,,TOUCHEXIT, RADIO1,19,15,53,43,8\n"
+	".,250,205,CHECKED | ISPARENT, GROUPBOX,20,72,28,57,39\n"
+	"205,+,,TOUCHEXIT, CHECKBOX,21,75,33,25,8\n"
+	".,.,,TOUCHEXIT, CHECKBOX,22,75,43,25,8\n"
+	".,,,TOUCHEXIT, CHECKBOX,23,75,53,25,8\n"
+	"250,+,,TOUCHEXIT | CHECKED, RADIO1,24,10,75,45,8\n"
+	".,.,,,EDTEXT,25,60,75,68,10\n"
+	".,.,,TOUCHEXIT,RADIO1,26,10,92,60,8\n"
+	".,,,,RANGEINPUT,27,20,102,100,10\n"
+	"300,+,,,RTEXT,-12,5,30,45,8\n"
+	".,.,,,EDVAL1,7,55,30,45,10\n"
+	".,.,,,RTEXT,-13,5,50,45,8\n"
+	".,,,LASTOBJ,EDVAL1,8,55,50,45,10";
+
+bool
+Symbol::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 47, 10, "Size & Color"};
+	TabSHEET tab2 = {47, 70, 10, "Text"};
+	TabSHEET tab3 = {70, 92, 10, "Edit"};
+	Symbol *PrevSym = 0L;
+	char text1[40], text2[100];
+	void *dyndata[] = {(void*)"Apply to Symbol", (void*)"Apply to PLOT", (void*)&tab1,
+		(void*)&tab2, (void*)&tab3, (void*)"size", (void*)&fPos.fx, (void*)&fPos.fy,
+		(void*)&size, (void*)"line width", (void*)&SymLine.width, (void*)"line color",
+		(void *)&SymLine.color, (void*)"fill color", (void *)&SymFill.color, (void*)" font ",
+		(void*)"Helvetica", (void*)"Times", (void*)"Courier", (void*)" style ", (void*)"bold",
+		(void*)"italic", (void*)"underlined", (void*)"fixed text:", (void*)text1,
+		(void*)"from spreadsheet range:", (void*)text2};
+	DlgInfo *SymDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, k, ix, iy, tmpType, res, width, height, n_syms;
+	DWORD tmpCol, undo_flags = 0L;
+	double tmpVal, o_size, n_size, o_lwidth, n_lwidth;
+	TextDEF textdef;
+	bool bContinue = false;
+	anyOutput *cdisp = Undo.cdisp;
+	lfPOINT o_pos, n_pos;
+	static const int syms[] = {SYM_CIRCLE, SYM_CIRCLEF, SYM_CIRCLEC, SYM_RECT, SYM_RECTF, SYM_RECTC, 
+		SYM_TRIAU, SYM_TRIAUF, SYM_TRIAUC, SYM_TRIAUL, SYM_TRIAUR, SYM_TRIAD, SYM_TRIADF, SYM_TRIADC,
+		SYM_TRIADL, SYM_TRIADR, SYM_DIAMOND, SYM_DIAMONDF, SYM_DIAMONDC, SYM_5GON, SYM_5GONF, SYM_5GONC,
+		SYM_4STAR, SYM_4STARF, SYM_5STAR, SYM_5STARF, SYM_6STAR, SYM_6STARF, SYM_1QUAD, SYM_2QUAD,
+		SYM_3QUAD, SYM_PLUS, SYM_CROSS, SYM_STAR, SYM_HLINE, SYM_VLINE, SYM_TEXT};
+
+	if(!(SymDlg = CompileDialog(SymDlg_DlgTmpl, dyndata)))return false;
+	if(!Command(CMD_GETTEXT, (void*)text1, 0L)) rlp_strcpy(text1, 40, "text");
+#ifdef USE_WIN_SECURE
+	if(parent && data && data->GetSize(&width, &height)) sprintf_s(text2, 100, "b1:b%d", height);
+#else
+	if(parent && data && data->GetSize(&width, &height)) sprintf(text2, "b1:b%d", height);
+#endif
+	else rlp_strcpy(text2, 100, "(not available)");
+	n_syms = sizeof(syms)/sizeof(int);
+	for(k = 1; !(SymDlg[k-1].flags & LASTOBJ); k++);
+	if(!parent) n_syms--;
+	if(!(SymDlg = (DlgInfo *)realloc(SymDlg, (k+n_syms)*sizeof(DlgInfo)))) return false;
+	SymDlg[k-1].flags &= (~LASTOBJ);
+	for(i = 1, iy = 66; i <= n_syms; i++, ix += 10) {
+		if((i%11) == 1) {
+			iy += 10;		ix = 15;
+			}
+		SymDlg[k].id = 400 + i;					SymDlg[k].next = 400 + i + 1;
+		SymDlg[k].first = 0;					SymDlg[k].flags = TOUCHEXIT;
+		SymDlg[k].type = SYMRADIO;				SymDlg[k].ptype = (void*)&syms[i-1];
+		SymDlg[k].x = ix;						SymDlg[k].y = iy;
+		if(type == syms[i-1]) SymDlg[k].flags |= CHECKED;
+		SymDlg[k].w = SymDlg[k].h = 10;			k++;
+		}
+	SymDlg[k-1].flags |= LASTOBJ;				if(parent) SymDlg[k-1].w = 30;
+	if(parent) {
+		SymDlg[0].ptype = dyndata[0];
+		}
+	else {
+		SymDlg[5].flags |= HIDDEN;				SymDlg[6].flags |= HIDDEN;
+		SymDlg[1].flags |= HIDDEN;				SymDlg[2].y = 25;
+		SymDlg[0].w = SymDlg[2].w = 45;			SymDlg[7].x = 145;
+		}
+	if((PrevSym = new Symbol(0L, data, 0.0f, 0.0f, type))){
+		PrevSym->SetColor(COL_SYM_LINE, SymLine.color);
+		PrevSym->SetColor(COL_SYM_FILL, SymFill.color);
+		PrevSym->SetSize(SIZE_SYMBOL, size);
+		PrevSym->SetSize(SIZE_SYM_LINE, SymLine.width);
+		PrevSym->Command(CMD_SETTEXT, (void*)text1, 0L);
+		PrevSym->Command(CMD_GETTEXTDEF, &textdef, 0L);
+		PrevSym->Command(CMD_SET_DATAOBJ, (void*)data, 0L);
+		if(Command(CMD_GETTEXTDEF, &textdef, 0L))
+			PrevSym->Command(CMD_SETTEXTDEF, &textdef, 0L);
+		PrevSym->idx = idx;
+		SymDlg[7].ptype = (void*)&PrevSym;
+		}
+	if(PrevSym && (Dlg = new DlgRoot(SymDlg, data))) {
+		Dlg->TextFont(201, FONT_HELVETICA);
+		Dlg->TextFont(202, FONT_TIMES);
+		Dlg->TextFont(203, FONT_COURIER);
+		Dlg->TextStyle(205, TXS_BOLD);
+		Dlg->TextStyle(206, TXS_ITALIC);
+		Dlg->TextStyle(207, TXS_UNDERLINE);
+		switch(textdef.Font) {
+		case FONT_TIMES:	Dlg->SetCheck(202, 0L, true);	break;
+		case FONT_COURIER:	Dlg->SetCheck(203, 0L, true);	break;
+		default:			Dlg->SetCheck(201, 0L, true);	break;
+			}
+		if(textdef.Style & TXS_BOLD) Dlg->SetCheck(205, 0L, true);
+		if(textdef.Style & TXS_ITALIC) Dlg->SetCheck(206, 0L, true);
+		if(textdef.Style & TXS_UNDERLINE) Dlg->SetCheck(207, 0L, true);
+		}
+	else return false;
+	if(parent && parent->name) {
+		width = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Symbol of ");
+		rlp_strcpy(TmpTxt+width, TMP_TXT_SIZE-width, parent->name);
+		width =10;
+		}
+	else {
+		rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Symbol properties");
+		}
+	if(!(hDlg = CreateDlgWnd(TmpTxt, 50, 50, parent ? 430 : 400, 292, Dlg, 0x4L)))return false;
+	tmpCol = 0x00c0c0c0L;	o_size = size;	o_lwidth = SymLine.width;
+	Dlg->GetValue(101, &o_size);		Dlg->GetValue(104, &o_lwidth);
+	n_size = o_size;					n_lwidth = o_lwidth;
+	o_pos.fx = fPos.fx;					o_pos.fy = fPos.fy;
+	if(Dlg->GetValue(301, &tmpVal)) o_pos.fx = tmpVal;	n_pos.fx = o_pos.fx;
+	if(Dlg->GetValue(303, &tmpVal)) o_pos.fy = tmpVal;	n_pos.fy = o_pos.fy;
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res){
+		case 0:
+			if(bContinue) res = -1;
+			break;
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);
+			if(parent && PrevSym->type == SYM_TEXT && Dlg->GetCheck(250)){
+				Dlg->GetText(251, text1, 40);
+				if(PrevSym->Command(CMD_GETTEXT, (void *)text2, 0L) && strcmp(text1, text2)) {
+					PrevSym->Command(CMD_SETTEXT, (void *)text1, 0L);
+					Dlg->DoPlot(NULL);
+					res = -1;
+					}
+				}
+			else if(parent && PrevSym->type == SYM_TEXT && Dlg->GetCheck(252)) {
+				if(Dlg->GetCheck(252) && Dlg->GetText(253, text2, 100))	
+					PrevSym->Command(CMD_RANGETEXT, &text2, 0L);
+				}
+			Dlg->GetValue(101, &n_size);		Dlg->GetValue(104, &n_lwidth);
+			break;
+		case 5:		case 7:
+			bContinue = false;			res = -1;
+		case 6:											//the text sheets
+			if(parent)Dlg->SetCheck(400+n_syms, 0L, true);
+			if(PrevSym->type != SYM_TEXT) {
+				PrevSym->type = SYM_TEXT;		Dlg->DoPlot(0L);
+				}
+			res = -1;		bContinue = true;
+			break;
+		case 201:	case 202:	case 203:				//fonts and styles
+		case 205:	case 206:	case 207:
+			if(PrevSym->Command(CMD_GETTEXTDEF, &textdef, 0L)) {
+				if(Dlg->GetCheck(202)) textdef.Font = FONT_TIMES;
+				else if(Dlg->GetCheck(203)) textdef.Font = FONT_COURIER;
+				else textdef.Font = FONT_HELVETICA;
+				textdef.Style = TXS_NORMAL;
+				if(Dlg->GetCheck(205)) textdef.Style |= TXS_BOLD;
+				if(Dlg->GetCheck(206)) textdef.Style |= TXS_ITALIC;
+				if(Dlg->GetCheck(207)) textdef.Style |= TXS_UNDERLINE;
+				PrevSym->Command(CMD_SETTEXTDEF, &textdef, 0L);
+				Dlg->DoPlot(0L);
+				}
+			res = -1;
+			break;
+		default:										//symbol selection ?
+			if(res > 400 && res <= (400+n_syms)) tmpType = syms[res-401];
+			else break;
+			PrevSym->type = tmpType;
+		case 107:										//line color button
+			if(res == 107 && Dlg->GetColor(107, &tmpCol))
+				PrevSym->SetColor(COL_SYM_LINE, tmpCol);
+		case 109:										//fill color button
+			if(res == 109 && Dlg->GetColor(109, &tmpCol)) 
+				PrevSym->SetColor(COL_SYM_FILL, tmpCol);
+		case 101:										//symbol size changed
+		case 104:										//line width changed
+		case 50:										//preview button
+			if(Dlg->GetValue(101, &tmpVal))	PrevSym->SetSize(SIZE_SYMBOL, tmpVal);
+			if(Dlg->GetValue(104, &tmpVal))	PrevSym->SetSize(SIZE_SYM_LINE, tmpVal);
+			if(PrevSym->type == SYM_TEXT) {
+				if(Dlg->GetCheck(250) && Dlg->GetText(251,text1,40))PrevSym->Command(CMD_SETTEXT, text1, 0L);
+				else if(Dlg->GetCheck(252) && Dlg->GetText(253, text2,100))	
+					PrevSym->Command(CMD_RANGETEXT, text2, 0L);
+				}
+			Dlg->DoPlot(0L);
+			res = -1;
+			break;
+		case 252:										//use spreadsheet text
+			if(!data) Dlg->SetCheck(250, 0L, true);
+		case 250:										//use fixed text
+			if(Dlg->GetCheck(250) && Dlg->GetText(251,text1,40))PrevSym->Command(CMD_SETTEXT, text1, 0L);
+			else if(Dlg->GetCheck(252) && Dlg->GetText(253, text2, 100))	
+				PrevSym->Command(CMD_RANGETEXT, text2, 0L);
+			Dlg->DoPlot(NULL);
+			res = -1;
+			break;
+			}
+		}while (res < 0);
+	switch (res) {
+	case 1:								//accept values for symbol
+		undo_flags = CheckNewFloat(&size, o_size, n_size, parent, undo_flags);
+		undo_flags = CheckNewFloat(&SymLine.width, o_lwidth, n_lwidth, parent, undo_flags);
+		if(Dlg->GetValue(301, &tmpVal)) n_pos.fx = tmpVal;
+		if(Dlg->GetValue(303, &tmpVal)) n_pos.fy = tmpVal;
+		undo_flags = CheckNewLFPoint(&fPos, &o_pos, &n_pos, parent, undo_flags);
+		if(Dlg->GetColor(107, &tmpCol)) undo_flags = 
+			CheckNewDword(&SymLine.color, SymLine.color, tmpCol, parent, undo_flags);
+		if(Dlg->GetColor(109, &tmpCol)) undo_flags = 
+			CheckNewDword(&SymFill.color, SymFill.color, tmpCol, parent, undo_flags);
+		undo_flags = CheckNewInt(&type, type, PrevSym->type, parent, undo_flags);
+		if(type == SYM_TEXT && PrevSym->Command(CMD_GETTEXTDEF, &textdef, 0L)){
+			if(SymTxt && cmpTextDEF(SymTxt, &textdef)){
+				Undo.TextDef(parent, SymTxt, undo_flags);	undo_flags |= UNDO_CONTINUE;
+				}
+			if(PrevSym->Command(CMD_GETTEXT, text1, 0L)) Command(CMD_SETTEXT, text1, 0L);
+			Command(CMD_SETTEXTDEF, &textdef, 0L);
+			}
+		break;
+	case 2:								//accept values for all symbols of plot
+		parent->Command(CMD_SAVE_SYMBOLS, 0L, 0L);
+		undo_flags |= UNDO_CONTINUE;
+		parent->SetSize(SIZE_SYMBOL, n_size);
+		parent->SetSize(SIZE_SYM_LINE, n_lwidth);
+		if(Dlg->GetColor(107, &tmpCol))	parent->SetColor(COL_SYM_LINE, tmpCol);
+		if(Dlg->GetColor(109, &tmpCol))	parent->SetColor(COL_SYM_FILL, tmpCol);
+		parent->Command(CMD_SYM_TYPE, (void*)(& PrevSym->type), 0L);
+		if(PrevSym->type == SYM_TEXT) {
+			if(Dlg->GetCheck(250) && PrevSym->Command(CMD_GETTEXT, text1, 0L))
+				parent->Command(CMD_SYMTEXT_UNDO, text1, 0L);
+			else if(Dlg->GetCheck(252) && Dlg->GetText(253, text2, 100))	
+				parent->Command(CMD_SYM_RANGETEXT, text2, 0L);
+			if(PrevSym->Command(CMD_GETTEXTDEF, &textdef, 0L))
+				parent->Command(CMD_SYMTEXTDEF, &textdef, 0L);
+			}
+		break;
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;			free(SymDlg);
+	delete PrevSym;			return undo_flags != 0;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Bubble properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *BubDlg_DlgTmpl = 
+	"1,+,,DEFAULT,PUSHBUTTON,1,130,10,60,12\n"
+	".,.,,,PUSHBUTTON,2,130,25,60,12\n"
+	".,.,,,PUSHBUTTON,-2,130,40,60,12\n"
+	".,,5,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
+	"5,+,100,ISPARENT | CHECKED,SHEET,3,5,10,120,100\n"
+	".,.,200,ISPARENT,SHEET,4,5,10,120,100\n"
+	".,,300,ISPARENT,SHEET,5,5,10,120,100\n"
+	"100,109,,NOSELECT,ODBUTTON,6,18,57,90,50\n"
+	"109,+,,ISRADIO,ODBUTTON,7,30,30,20,20\n"
+	".,.,,ISRADIO,ODBUTTON,7,50,30,20,20\n"
+	".,.,,ISRADIO,ODBUTTON,7,70,30,20,20\n"
+	".,,,ISRADIO,ODBUTTON,7,90,30,20,20\n"
+	"200,+,,,LTEXT,8,10,30,110,8\n"
+	".,.,210,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	".,.,,,LTEXT,9,10,64,110,8\n"
+	".,,220,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"210,+,,,RADIO1,-3,40,38,45,8\n"
+	".,.,,,RADIO1,10,40,46,45,8\n"
+	".,,,,RADIO1,11,40,54,45,8\n"
+	"220,+,,,RADIO1,12,40,72,45,8\n"
+	".,.,,,RADIO1,13,40,80,45,8\n"
+	".,,,,RADIO1,14,40,88,45,8\n"
+	"300,+,,,RTEXT,-12,10,40,45,8\n"
+	".,.,,,EDVAL1,15,60,40,35,10\n"
+	".,.,,,RTEXT,-13,10,60,45,8\n"
+	".,.,,,EDVAL1,16,60,60,35,10\n"
+	".,.,,,RTEXT,17,10,80,45,8\n"
+	".,,,LASTOBJ,EDVAL1,18,60,80,35,10";
+
+bool
+Bubble::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 52, 10, "Shape & Color"};
+	TabSHEET tab2 = {52, 84, 10, "Scaling"};
+	TabSHEET tab3 = {84, 106, 10, "Edit"};
+	int syms[] = {SYM_CIRCLE, SYM_RECT, SYM_TRIAU, SYM_TRIAD};
+	void *dyndata[] = {(void*)"Apply to BUBBLE", (void*)"Apply to PLOT", (void*)&tab1, (void*)&tab2,
+		(void*)&tab3, (void*)&OD_filldef, (void *)OD_BubbleTempl, (void*)"sizes are given as",
+		(void*)"proportionality (relative to circle)", (void*)"scaling with X axis",
+		(void*)"scaling with Y axis", (void*)"diameter", (void*)"circumference", (void*)"area", (void*)&fPos.fx,
+		(void*)&fPos.fy, (void*)"size", (void*)&fs};
+	DlgInfo *BubDlg = CompileDialog(BubDlg_DlgTmpl, dyndata);
+	DlgRoot *Dlg;
+	void *hDlg;
+	int cb, res, tmpType;
+	lfPOINT o_pos, n_pos;
+	LineDEF newLine, newFillLine;
+	FillDEF newFill;
+	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+	double o_size, n_size;
+	bool bRet = false;
+
+	if(!parent) return false;
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&BubbleLine, 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&BubbleFill, 0);
+	Dlg = new DlgRoot(BubDlg, data);
+	switch(type & 0x00f) {
+	case BUBBLE_SQUARE:		Dlg->SetCheck(110, 0L, true);		break;
+	case BUBBLE_UPTRIA:		Dlg->SetCheck(111, 0L, true);		break;
+	case BUBBLE_DOWNTRIA:	Dlg->SetCheck(112, 0L, true);		break;
+	default:				Dlg->SetCheck(109, 0L, true);		break;
+		}
+	switch(type & 0x0f0) {
+	case BUBBLE_XAXIS:		Dlg->SetCheck(211, 0L, true);		break;
+	case BUBBLE_YAXIS:		Dlg->SetCheck(212, 0L, true);		break;
+	default:				Dlg->SetCheck(210, 0L, true);		break;
+		}
+	switch(type & 0xf00) {
+	case BUBBLE_CIRCUM:		Dlg->SetCheck(221, 0L, true);		break;
+	case BUBBLE_AREA:		Dlg->SetCheck(222, 0L, true);		break;
+	default:				Dlg->SetCheck(220, 0L, true);		break;
+		}
+	if(!Dlg->GetValue(301, &o_pos.fx))	o_pos.fx = fPos.fx;
+	if(!Dlg->GetValue(303, &o_pos.fy))	o_pos.fy = fPos.fy;
+	if(!Dlg->GetValue(305, &o_size))	o_size = fs;
+	n_pos.fx = o_pos.fx;	n_pos.fy = o_pos.fy;	n_size = o_size;
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Bubble of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Bubble Properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 396, 260, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 1:			//accept for current bubble only
+		case 2:			//accept for plot
+			Undo.SetDisp(cdisp);
+			OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+			OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
+			memcpy(&newFillLine, &BubbleFillLine, sizeof(LineDEF));
+			if(newFill.hatch) memcpy(&newFillLine, newFill.hatch, sizeof(LineDEF));
+			if(Dlg->GetCheck(110)) tmpType = BUBBLE_SQUARE;
+			else if(Dlg->GetCheck(111)) tmpType = BUBBLE_UPTRIA;
+			else if(Dlg->GetCheck(112)) tmpType = BUBBLE_DOWNTRIA;
+			else tmpType = BUBBLE_CIRCLE;
+			if(Dlg->GetCheck(211)) tmpType |= BUBBLE_XAXIS;
+			else if(Dlg->GetCheck(212)) tmpType |= BUBBLE_YAXIS;
+			if(Dlg->GetCheck(221)) tmpType |= BUBBLE_CIRCUM;
+			else if(Dlg->GetCheck(222)) tmpType |= BUBBLE_AREA;
+			break;
+			}
+		}while (res < 0);
+	switch (res) {
+	case 1:				//new setting for current bubble only
+		Dlg->GetValue(301, &n_pos.fx);		Dlg->GetValue(303, &n_pos.fy);
+		undo_flags = CheckNewLFPoint(&fPos, &o_pos, &n_pos, parent, undo_flags);
+		undo_flags = CheckNewInt(&type, type, tmpType, parent, undo_flags);
+		Dlg->GetValue(305, &n_size);
+		undo_flags = CheckNewFloat(&fs, o_size, n_size, parent, undo_flags);
+		if(cmpLineDEF(&BubbleLine, &newLine)) {
+			Undo.Line(parent, &BubbleLine, undo_flags);		undo_flags |= UNDO_CONTINUE;
+			memcpy(&BubbleLine, &newLine, sizeof(LineDEF));
+			}
+		if(newFill.type && cmpLineDEF(&BubbleFillLine, &newFillLine)) {
+			Undo.Line(parent, &BubbleFillLine, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&BubbleFillLine, &newFillLine, sizeof(LineDEF));
+			}
+		if(cmpFillDEF(&BubbleFill, &newFill)) {
+			Undo.Fill(parent, &BubbleFill, undo_flags);		undo_flags |= UNDO_CONTINUE;
+			memcpy(&BubbleFill, &newFill, sizeof(FillDEF));
+			}
+		BubbleFill.hatch = &BubbleFillLine;
+		if(undo_flags & UNDO_CONTINUE) bRet = true;
+		break;
+	case 2:				//accept settings for plot
+		parent->Command(CMD_SAVE_SYMBOLS, 0L, 0L);
+		parent->Command(CMD_BUBBLE_TYPE, (void*)(& tmpType), 0L);
+		parent->Command(CMD_BUBBLE_ATTRIB, (void*)(& tmpType), 0L);
+		parent->Command(CMD_BUBBLE_FILL, (void*)&newFill, 0L);
+		parent->Command(CMD_BUBBLE_LINE, (void*)&newLine, 0L);
+		bRet = true;
+		break;
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;			free(BubDlg);		return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Bar properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *BarDlg_DlgTmpl = 
+	"1,+,,DEFAULT,PUSHBUTTON,1,130,10,55,12\n"
+	".,.,,,PUSHBUTTON,2,130,25,55,12\n"
+	".,.,,,PUSHBUTTON,-2,130,40,55,12\n"
+	".,,5,CHECKED | ISPARENT,GROUP,0,138,40,55,12\n"
+	"5,+,100,ISPARENT | CHECKED,SHEET,3,5,10,120,120\n"
+	".,.,200,ISPARENT,SHEET,4,5,10,120,120\n"
+	".,,300,ISPARENT,SHEET,5,5,10,120,120\n"
+	"100,109,,NOSELECT,ODBUTTON,6,18,30,90,50\n"
+	"109,+,,,LTEXT,7,10,80,45,8\n"
+	".,.,,,RADIO1,8,20,92,25,8\n"
+	".,.,,,EDTEXT,9,60,92,25,10\n"
+	".,.,,,LTEXT,-3,87,92,20,8\n"
+	".,.,,,RADIO1,10,20,104,25,8\n"
+	".,.,,,EDTEXT,11,60,104,25,10\n"
+	".,,,,LTEXT,-10,87,104,10,8\n"
+	"200,+,,TOUCHEXIT,RADIO2,12,20,30,45,8\n"
+	".,205,202,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
+	".,+,,TOUCHEXIT,RADIO1,13,30,40,35,8\n"
+	".,.,,TOUCHEXIT,RADIO1,14,30,48,35,8\n"
+	".,,,TOUCHEXIT,RADIO1,15,30,56,35,8\n"
+	"205,+,,,EDVAL1,16,65,56,35,10\n"
+	".,.,,TOUCHEXIT,RADIO2,17,20,70,45,8\n"
+	".,211,208,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
+	"208,+,,TOUCHEXIT,RADIO1,18,30,80,35,8\n"
+	".,.,,TOUCHEXIT,RADIO1,19,30,88,35,8\n"
+	".,,,TOUCHEXIT,RADIO1,20,30,96,35,8\n"
+	"211,+,,,EDVAL1,21,65,96,35,10\n"
+	".,,,,CHECKBOX,22,20,113,50,8\n"
+	"300,+,,,RTEXT,-12,10,50,45,8\n"
+	".,.,,,EDVAL1,23,60,50,40,10\n"
+	".,.,,,RTEXT,-13,10,75,45,8\n"
+	".,,,LASTOBJ,EDVAL1,24,60,75,40,10";
+bool
+Bar::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 50, 10, "Size & Color"};
+	TabSHEET tab2 = {50, 90, 10, "Baseline"};
+	TabSHEET tab3 = {90, 120, 10, "Edit"};
+	char sTxt1[20], sTxt2[20];
+	void *dyndata[] = {(void*)"Apply to BAR", (void*)"Apply to PLOT", (void*)&tab1,
+		(void*)&tab2, (void*)&tab3, (void*)OD_filldef, (void*)"bar width:", (void*)" fixed",
+		(void*)&sTxt1[1], (void*)" relative", (void*)&sTxt2[1], (void*)"vertical bars",
+		(void*)"bottom baseline", (void*)"top", (void*)"user y =", (void*)&BarBase.fy,
+		(void*)"horizontal bars", (void*)"left baseline", (void*)"right", (void*)"user x =",
+		(void*)&BarBase.fx, (void*)"bars centered across baseline", (void*)&fPos.fx, (void*)&fPos.fy};
+	DlgInfo *BarDlg = CompileDialog(BarDlg_DlgTmpl, dyndata);
+	DlgRoot *Dlg;
+	void *hDlg;
+	double n_size;
+	int cb, res, tmpType = type;
+	bool bRet = false;
+	LineDEF newLine, newFillLine;
+	FillDEF newFill;
+	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+	lfPOINT o_bl, n_bl, o_pos, n_pos;
+
+	if(!parent) return false;
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&BarLine, 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&BarFill, 0);
+	if(type & BAR_RELWIDTH) {
+		WriteNatFloatToBuff(sTxt2, size);
+		WriteNatFloatToBuff(sTxt1, DefSize(SIZE_BAR));
+		}
+	else {
+		WriteNatFloatToBuff(sTxt1, size);
+		rlp_strcpy(sTxt2, 20, " 50");
+		}
+	Dlg = new DlgRoot(BarDlg, data);
+	switch (type & 0xff) {
+	case BAR_VERTB:		case BAR_VERTT:		case BAR_VERTU:
+		Dlg->SetCheck(200, 0L, true);
+		Dlg->SetCheck(208, 0L, true);
+		switch(type & 0xff) {
+		case BAR_VERTT:	Dlg->SetCheck(203, 0L, true);	break;
+		case BAR_VERTU:	Dlg->SetCheck(204, 0L, true);	break;
+		default:		Dlg->SetCheck(202, 0L, true);	break;
+			}
+		break;
+	case BAR_HORL:		case BAR_HORR:		case BAR_HORU:
+		Dlg->SetCheck(206, 0L, true);
+		Dlg->SetCheck(202, 0L, true);
+		switch(type & 0xff) {
+		case BAR_HORR:	Dlg->SetCheck(209, 0L, true);	break;
+		case BAR_HORU:	Dlg->SetCheck(210, 0L, true);	break;
+		default:		Dlg->SetCheck(208, 0L, true);	break;
+			}
+		break;
+		}
+	if(type & BAR_RELWIDTH) Dlg->SetCheck(113, 0L, true);
+	else Dlg->SetCheck(110, 0L, true);
+	if(type & BAR_CENTERED) Dlg->SetCheck(212, 0L, true);
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Bar of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Bar properties");
+	if(!Dlg->GetValue(211, &o_bl.fx))	o_bl.fx = BarBase.fx;
+	if(!Dlg->GetValue(205, &o_bl.fy))	o_bl.fy = BarBase.fy;
+	if(!Dlg->GetValue(301, &o_pos.fx))	o_pos.fx = fPos.fx;
+	if(!Dlg->GetValue(303, &o_pos.fy))	o_pos.fy = fPos.fy;
+	n_bl.fx = o_bl.fx;			n_bl.fy = o_bl.fy;
+	n_pos.fx = o_pos.fx;		n_pos.fy = o_pos.fy;
+	n_size = size;
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 390, 300, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 202:	case 203:	case 204:
+			Dlg->SetCheck(200, NULL, true);
+			tmpType = res == 202 ? BAR_VERTB : res == 203 ? BAR_VERTT : BAR_VERTU;
+			res = -1;				//continue on radiobutton
+			break;
+		case 208:	case 209:	case 210:
+			Dlg->SetCheck(206, NULL, true);
+			tmpType = res == 208 ? BAR_HORL : res == 209 ? BAR_HORR : BAR_HORU;
+			res = -1;				//continue on radiobutton
+			break;
+		case 200:					//group of vertical bars
+			if(Dlg->GetCheck(203)) tmpType = BAR_VERTT;
+			else if(Dlg->GetCheck(204)) tmpType = BAR_VERTU;
+			else tmpType = BAR_VERTB;
+			res = -1;
+			break;
+		case 206:					//group of horizontal bars
+			if(Dlg->GetCheck(209)) tmpType = BAR_HORR;
+			else if(Dlg->GetCheck(210)) tmpType = BAR_HORU;
+			else tmpType = BAR_HORL;
+			res = -1;
+			break;
+		case 1:
+		case 2:
+			Undo.SetDisp(cdisp);
+			OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+			OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
+			memcpy(&newFillLine, &HatchLine, sizeof(LineDEF));
+			if(newFill.hatch) memcpy(&newFillLine, newFill.hatch, sizeof(LineDEF));
+			if(Dlg->GetCheck(113)) {
+				tmpType |= BAR_RELWIDTH;			Dlg->GetValue(114, &n_size);
+				}
+			else {
+				tmpType &= ~BAR_RELWIDTH;			Dlg->GetValue(111, &n_size);
+				}
+			if(Dlg->GetCheck(212))tmpType |= BAR_CENTERED; 
+			else tmpType &= ~BAR_CENTERED;
+			Dlg->GetValue(211, &n_bl.fx);			Dlg->GetValue(205, &n_bl.fy);
+			Dlg->GetValue(301, &n_pos.fx);			Dlg->GetValue(303, &n_pos.fy);
+			break;
+			}
+		}while (res < 0);
+	switch (res) {
+	case 1:				//new setting for current bar only
+		undo_flags = CheckNewInt(&type, type, tmpType, parent, undo_flags);
+		undo_flags = CheckNewLFPoint(&BarBase, &o_bl, &n_bl, parent, undo_flags);
+		undo_flags = CheckNewLFPoint(&fPos, &o_pos, &n_pos, parent, undo_flags);
+		if(undo_flags & UNDO_CONTINUE) parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+		undo_flags = CheckNewFloat(&size, size, n_size, parent, undo_flags);
+		if(cmpLineDEF(&BarLine, &newLine)) {
+			Undo.Line(parent, &BarLine, undo_flags);		undo_flags |= UNDO_CONTINUE;
+			memcpy(&BarLine, &newLine, sizeof(LineDEF));
+			}
+		if(newFill.type && cmpLineDEF(&HatchLine, &newFillLine)) {
+			Undo.Line(parent, &HatchLine, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&HatchLine, &newFillLine, sizeof(LineDEF));
+			}
+		if(cmpFillDEF(&BarFill, &newFill)) {
+			Undo.Fill(parent, &BarFill, undo_flags);		undo_flags |= UNDO_CONTINUE;
+			memcpy(&BarFill, &newFill, sizeof(FillDEF));
+			}
+		BarFill.hatch = &HatchLine;
+		if(undo_flags & UNDO_CONTINUE) bRet = true;
+		break;
+	case 2:				//new settings to all bars of plot
+		parent->Command(CMD_SAVE_BARS, 0L, 0L);
+		if(parent->Id == GO_PLOTSCATT && parent->parent 
+			&& parent->parent->Id == GO_STACKBAR){ 
+			parent->parent->SetSize(SIZE_BAR, n_size);
+			parent->parent->Command(CMD_BAR_TYPE, (void *)(& tmpType), 0L);
+			}
+		else {
+			parent->SetSize(SIZE_BAR, n_size);
+			parent->Command(CMD_BAR_TYPE, (void *)(& tmpType), 0L);
+			}
+		parent->SetColor(COL_BAR_LINE, newLine.color);
+		parent->SetSize(SIZE_BAR_LINE, newLine.width);
+		parent->SetSize(SIZE_YBASE, n_bl.fy);
+		parent->SetSize(SIZE_XBASE, n_bl.fx);
+		parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+		parent->Command(CMD_BAR_TYPE, (void *)(& tmpType), 0L);
+		parent->Command(CMD_BAR_FILL, (void *)&newFill, 0L);
+		bRet = true;
+		break;
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;		free(BarDlg);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Data line properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *LineDlg_DlgTmpl =
+	"1,2,,DEFAULT,PUSHBUTTON,-1,150,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,150,25,45,12\n"
+	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,5,100,ISPARENT | CHECKED,SHEET,1,5,10,139,120\n"
+	"5,6,200,ISPARENT,SHEET,2,5,10,139,120\n"
+	"6,,300,ISPARENT | HIDDEN,SHEET,3,5,10,139,120\n"
+	"100,,,NOSELECT,ODBUTTON,9,10,38,130,100\n"
+	"200,201,,,LTEXT,-27,10,32,130,100\n"
+	"201,202,,EXRADIO,ODBUTTON,10,12,45,25,25\n"
+	"202,203,,EXRADIO,ODBUTTON,10,37,45,25,25\n"
+	"203,204,,EXRADIO,ODBUTTON,10,62,45,25,25\n"
+	"204,205,,EXRADIO,ODBUTTON,10,87,45,25,25\n"
+	"205,206,,EXRADIO,ODBUTTON,10,112,45,25,25\n"
+	"206,207,,EXRADIO,ODBUTTON,10,37,70,25,25\n"
+	"207,208,,EXRADIO,ODBUTTON,10,62,70,25,25\n"
+	"208,209,,EXRADIO,ODBUTTON,10,87,70,25,25\n"
+	"209,210,,EXRADIO,ODBUTTON,10,112,70,25,25\n"
+	"210,211,,EXRADIO,ODBUTTON,10,37,95,25,25\n"
+	"211,212,,EXRADIO,ODBUTTON,10,62,95,25,25\n"
+	"212,,,EXRADIO,ODBUTTON,10,87,95,25,25\n"
+	"300,301,,,LTEXT,5,15,30,80,9\n"
+	"301,302,,,EDTEXT,7,15,40,119,10\n"
+	"302,303,,,LTEXT,6,15,55,80,9\n"
+	"303,,,LASTOBJ,EDTEXT,8,15,65,119,10";
+
+bool
+DataLine::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 40, 10, "Line"};
+	TabSHEET tab2 = {40, 80, 10, "Style"};
+	TabSHEET tab3 = {80, 120, 10, "Edit"};
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)0L, 
+		(void*)"range for x-values", (void*)"range for y-values", (void*)ssXref, (void*)ssYref,
+		(void*)OD_linedef, (void*)(OD_LineStyleTempl)};
+	DlgInfo *LineDlg = CompileDialog(LineDlg_DlgTmpl, dyndata);
+	DlgRoot *Dlg;
+	void *hDlg;
+	int cb, res, tmpType = type;
+	DWORD undo_flags = 0L;
+	LineDEF newLine;
+	bool bRet = false;
+	anyOutput *cdisp = Undo.cdisp;
+
+	if(!parent) return false;
+	if(parent->Id == GO_FUNCTION) return parent->PropertyDlg();
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
+	if(!(Dlg = new DlgRoot(LineDlg, data)))return false;
+	Dlg->SetCheck(201 + (type & 0x0f), 0L, true);
+	if(ssXref && ssYref) Dlg->ShowItem(6, true);
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Line of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Line properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 410, 300, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 201:	case 202:	case 203:	case 204:	case 205:	case 206:
+		case 207:	case 208:	case 209:	case 210:	case 211:	case 212:
+			if((tmpType & 0x0f) == (res-201)) res = 1;
+			else {
+				tmpType &= ~0x0f;	tmpType |= (res-201);
+				res = -1;
+				}
+			break;
+			}
+		}while (res < 0);
+	if(res == 1){						//OK pressed
+		Undo.SetDisp(cdisp);
+		if(ssXref && ssYref) {
+			TmpTxt[0] = 0;		Dlg->GetText(301, TmpTxt, TMP_TXT_SIZE); 
+			undo_flags = CheckNewString(&ssXref, ssXref, TmpTxt, this, undo_flags);
+			TmpTxt[0] = 0;	Dlg->GetText(303, TmpTxt, TMP_TXT_SIZE);
+			undo_flags = CheckNewString(&ssYref, ssYref, TmpTxt, this, undo_flags);
+			if(undo_flags & UNDO_CONTINUE) {
+				Command(CMD_UPDATE, 0L, cdisp);			parent->Command(CMD_MRK_DIRTY, 0L, cdisp);
+				}
+			}
+		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+		if(cmpLineDEF(&LineDef, &newLine)) {
+			Undo.Line(parent, &LineDef, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&LineDef, &newLine, sizeof(LineDEF));
+			}
+		undo_flags = CheckNewInt(&type, type, tmpType, parent, undo_flags);
+		if(undo_flags & UNDO_CONTINUE) bRet = true;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;		free(LineDlg);		return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Data polygon properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *PolygonDlg_DlgTmpl =
+	"1,2,,DEFAULT,PUSHBUTTON,-1,120,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,120,25,45,12\n"
+	"3,,4,CHECKED,GROUP,0,0,0,0,0\n"
+	"4,5,100,ISPARENT | CHECKED, SHEET,1, 5,10,110,75\n"
+	"5,,200,ISPARENT,SHEET,2,5,10,110,75\n"
+	"100,,,NOSELECT,ODBUTTON,3,23,30,90,50\n"
+	"200,201,,,LTEXT,-27,10,32,130,100\n"
+	"201,202,,EXRADIO,ODBUTTON,4,10,45,25,25\n"
+	"202,203,,EXRADIO,ODBUTTON,4,35,45,25,25\n"
+	"203,213,,EXRADIO,ODBUTTON,4,60,45,25,25\n"
+	"213,,,LASTOBJ | EXRADIO,ODBUTTON,4,85,45,25,25";
+bool
+DataPolygon::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 40, 10, "Polygon"};
+	TabSHEET tab2 = {40, 70, 10, "Style"};
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)OD_filldef, (void*)OD_PolygonStyleTempl};
+	DlgInfo *PolygonDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	LineDEF newLine, newFillLine;
+	FillDEF newFill;
+	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+	int cb, res, tmpType = type;
+	bool bRet = false;
+
+	if(!data || !parent) return false;
+	if(!(PolygonDlg = CompileDialog(PolygonDlg_DlgTmpl, dyndata))) return false;
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&pgFill, 0);
+	Dlg = new DlgRoot(PolygonDlg, data);
+	switch (type & 0x0f) {
+		case 0:		default:
+			Dlg->SetCheck(201, 0L, true);
+			break;
+		case 1:		case 2:		case 12:
+			Dlg->SetCheck(201+type, 0L, true);
+			break;
+		}
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Polygon of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else if(parent->Id == GO_TICK) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Isopleth \"");
+		if(parent->Command(CMD_GETTEXT, TmpTxt+cb, 0L)) {
+			cb = (int)strlen(TmpTxt); TmpTxt[cb++] = '"';	TmpTxt[cb++] = ' ';
+			}
+		else cb--;
+		cb += rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE, "Properties");
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Polygon Properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 348, 210, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 201:	case 202:	case 203:	case 213:
+			if((tmpType & 0x0f) == (res-201)) res = 1;
+			else {
+				tmpType &= ~0x0f;	tmpType |= (res-201);
+				res = -1;
+				}
+			break;
+			}
+		}while (res < 0);
+	if(res == 1){						//OK pressed
+		Undo.SetDisp(cdisp);
+		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
+		memcpy(&newFillLine, &pgFillLine, sizeof(LineDEF));
+		if(newFill.hatch) memcpy(&newFillLine, newFill.hatch, sizeof(LineDEF));
+		if(cmpLineDEF(&LineDef, &newLine)) {
+			Undo.Line(parent, &LineDef, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&LineDef, &newLine, sizeof(LineDEF));
+			}
+		if(newFill.type && cmpLineDEF(&pgFillLine, &newFillLine)) {
+			Undo.Line(parent, &pgFillLine, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&pgFillLine, &newFillLine, sizeof(LineDEF));
+			}
+		if(cmpFillDEF(&pgFill, &newFill)) {
+			Undo.Fill(parent, &pgFill, undo_flags);		undo_flags |= UNDO_CONTINUE;
+			memcpy(&pgFill, &newFill, sizeof(FillDEF));
+			}
+		pgFill.hatch = &pgFillLine;
+		undo_flags = CheckNewInt(&type, type, tmpType, parent, undo_flags);
+		if(undo_flags & UNDO_CONTINUE) bRet = true;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;	free(PolygonDlg);	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Regression line properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+RegLine::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 30, 10, "Line"};
+	TabSHEET tab2 = {30, 60, 10, "Model"};
+	TabSHEET tab3 = {60, 95, 10, "Clipping"};
+	char text1[60], text2[60], text3[60], text4[60], text5[60];
+	DlgInfo LineDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 155, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 155, 25, 45, 12},
+		{3, 0, 4, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
+		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 144, 130},
+		{5, 6, 200, ISPARENT, SHEET, &tab2, 5, 10, 144, 130},
+		{6, 0, 300, ISPARENT, SHEET, &tab3, 5, 10, 144, 130},
+		{100, 0, 0, NOSELECT, ODBUTTON, (void*)OD_linedef, 12, 38, 130, 100},
+		{200, 201, 0, TOUCHEXIT, RADIO1, (void*)"y dependent on x", 15, 25, 130, 8},
+		{201, 202, 0, 0x0L, LTEXT, (void*)text1, 25, 33, 120, 8},
+		{202, 203, 0, TOUCHEXIT, RADIO1, (void*)"x dependent on y", 15, 45, 130, 8},
+		{203, 204, 0, 0x0L, LTEXT, (void*)text2, 25, 53, 120, 8},
+		{204, 205, 0, TOUCHEXIT, RADIO1, (void*)"mixed model", 15, 65, 130, 8},
+		{205, 206, 0, 0x0L, LTEXT, (void*)text3, 25, 73, 120, 8},
+		{206, 207, 0, TOUCHEXIT, RADIO1, (void*)"zero crossing (x = 0, y = 0)", 15, 85, 130, 8},
+		{207, 208, 0, 0x0L, LTEXT, (void*)text4, 25, 93, 120, 8},
+		{208, 209, 0, TOUCHEXIT, RADIO1, (void*)"set manually", 15, 105, 130, 8},
+		{209, 210, 0, 0x0L, LTEXT, (void*)text5, 25, 113, 60, 8},
+		{210, 211, 0, 0x0L, RTEXT, (void*)"a =", 92, 113, 10, 8},
+		{211, 212, 0, 0x0L, EDVAL1, &l5.fx, 104, 113, 40, 10},
+		{212, 213, 0, 0x0L, RTEXT, (void*)"b =", 92, 125, 10, 8},
+		{213, 0, 0, 0x0L, EDVAL1, &l5.fy, 104, 125, 40, 10},
+		{300, 301, 0, 0x0L, LTEXT, (void*)"Line length is limited by", 15, 30, 100, 8},
+		{301, 302, 0, TOUCHEXIT, RADIO1, (void*)"data minima and maxima", 15, 45, 100, 8},
+		{302, 303, 0, TOUCHEXIT, RADIO1, (void*)"frame rectangle", 15, 60, 100, 8},
+		{303, 304, 0, TOUCHEXIT, RADIO1, (void*)"user defined values", 15, 75, 100, 8},
+		{304, 305, 0, 0x0L, RTEXT, (void*)"x", 10, 89, 15, 8},
+		{305, 306, 0, 0x0L, EDVAL1, &uclip.Xmin, 27, 89, 40, 10},
+		{306, 307, 0, 0x0L, LTEXT, (void*)"-", 70, 89, 5, 8},
+		{307, 308, 0, 0x0L, EDVAL1, &uclip.Xmax, 76, 89, 40, 10},
+		{308, 309, 0, 0x0L, LTEXT, (void*)"[data]", 119, 89, 15, 8},
+		{309, 310, 0, 0x0L, RTEXT, (void*)"y", 10, 101, 15, 8},
+		{310, 311, 0, 0x0L, EDVAL1, &uclip.Ymin, 27, 101, 40, 10},
+		{311, 312, 0, 0x0L, LTEXT, (void*)"-", 70, 101, 5, 8},
+		{312, 313, 0, 0x0L, EDVAL1, &uclip.Ymax, 76, 101, 40, 10},
+		{313, 0, 0, LASTOBJ, LTEXT, (void*)"[data]", 119, 101, 15, 8}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int cb, res, tmpType;
+	bool bRet = false;
+	LineDEF newLine;
+	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+	lfPOINT o_l5, n_l5;
+	fRECT o_clip, n_clip;
+	char *tx, *ty;
+
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
+	switch(type &0x700) {
+	case 0x100:		tx = "log(x)";	break;
+	case 0x200:		tx = "(1/x)";	break;
+	case 0x300:		tx = "sqrt(x)"; break;
+	default:		tx = "x";		break;
+		}
+	switch(type &0x7000) {
+	case 0x1000:	ty = "log(y)";	break;
+	case 0x2000:	ty = "(1/y)";	break;
+	case 0x3000:	ty = "sqrt(y)"; break;
+	default:		ty = "y";		break;
+		}
+#ifdef USE_WIN_SECURE
+	sprintf_s(text1, 60, "%s = %.3lf + %.3lf * %s   (n = %ld)", ty, l1.fx, l1.fy, tx, nPoints);
+	sprintf_s(text2, 60, "%s = %.3lf + %.3lf * %s", ty, l2.fx, l2.fy, tx);
+	sprintf_s(text3, 60, "%s = %.3lf + %.3lf * %s", ty, l3.fx, l3.fy, tx);
+	sprintf_s(text4, 60, "%s = %.3lf + %.3lf * %s", ty, l4.fx, l4.fy, tx);
+	sprintf_s(text5, 60, "%s = a + b * %s", ty, tx);
+#else
+	sprintf(text1, "%s = %.3lf + %.3lf * %s   (n = %ld)", ty, l1.fx, l1.fy, tx, nPoints);
+	sprintf(text2, "%s = %.3lf + %.3lf * %s", ty, l2.fx, l2.fy, tx);
+	sprintf(text3, "%s = %.3lf + %.3lf * %s", ty, l3.fx, l3.fy, tx);
+	sprintf(text4, "%s = %.3lf + %.3lf * %s", ty, l4.fx, l4.fy, tx);
+	sprintf(text5, "%s = a + b * %s", ty, tx);
+#endif
+	if(!(Dlg = new DlgRoot(LineDlg, data))) return false;
+	Dlg->Activate(211, false);	Dlg->Activate(213, false);
+	switch(type & 0x07) {
+	case 1:		Dlg->SetCheck(202, 0L, true);	break;
+	case 2:		Dlg->SetCheck(204, 0L, true);	break;
+	case 3:		Dlg->SetCheck(206, 0L, true);	break;
+	case 4:		
+		Dlg->SetCheck(208, 0L, true);
+		Dlg->Activate(211, true);	Dlg->Activate(213, true);
+		break;
+	default:	Dlg->SetCheck(200, 0L, true);	break;
+		}
+	switch(type & 0x70) {
+	case 0x10:	Dlg->SetCheck(302, 0L, true);	break;
+	case 0x20:	Dlg->SetCheck(303, 0L, true);	break;
+	default:	Dlg->SetCheck(301, 0L, true);	break;
+		}
+	if(0x20 == (type & 0x70)) {
+		Dlg->Activate(305, true);	Dlg->Activate(307, true);
+		Dlg->Activate(310, true);	Dlg->Activate(312, true);
+		}
+	else {
+		Dlg->Activate(305, false);	Dlg->Activate(307, false);
+		Dlg->Activate(310, false);	Dlg->Activate(312, false);
+		}
+	if(!Dlg->GetValue(211, &o_l5.fx))	o_l5.fx = l5.fx;
+	if(!Dlg->GetValue(213, &o_l5.fy))	o_l5.fy = l5.fy;
+	n_l5.fx = o_l5.fx;			n_l5.fy = o_l5.fy;
+	if(!Dlg->GetValue(305, &o_clip.Xmin)) o_clip.Xmin = uclip.Xmin;
+	if(!Dlg->GetValue(307, &o_clip.Xmax)) o_clip.Xmax = uclip.Xmax;
+	if(!Dlg->GetValue(310, &o_clip.Ymin)) o_clip.Ymin = uclip.Ymin;
+	if(!Dlg->GetValue(312, &o_clip.Ymax)) o_clip.Ymax = uclip.Ymax;
+	memcpy(&n_clip, &o_clip, sizeof(fRECT));
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Regression line of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Regression line properties");
+	hDlg = CreateDlgWnd(cp ? TmpTxt : 
+		(char*)"Linear regression analysis step 2/2", 50, 50, 420, 320, Dlg, 0x4L);
+	if(!cp) Dlg->SetCheck(5, 0L, true);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 200:	case 202:	case 204:	case 206:
+			Dlg->Activate(211, false);	Dlg->Activate(213, false);
+			res = -1;
+			break;
+		case 208:
+			Dlg->Activate(211, true);	Dlg->Activate(213, true);
+			res = -1;
+			break;
+		case 301:	case 302:
+			Dlg->Activate(305, false);	Dlg->Activate(307, false);
+			Dlg->Activate(310, false);	Dlg->Activate(312, false);
+			res = -1;
+			break;
+		case 303:
+			Dlg->Activate(305, true);	Dlg->Activate(307, true);
+			Dlg->Activate(310, true);	Dlg->Activate(312, true);
+			res = -1;
+			break;
+			}
+		}while (res < 0);
+	if(res == 1){						//OK pressed
+		Undo.SetDisp(cdisp);
+		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+		if(cmpLineDEF(&LineDef, &newLine)) {
+			Undo.Line(parent, &LineDef, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&LineDef, &newLine, sizeof(LineDEF));
+			}
+		tmpType = type & (~0x77);
+		if(Dlg->GetCheck(202)) tmpType |= 1;
+		else if(Dlg->GetCheck(204)) tmpType |= 2;
+		else if(Dlg->GetCheck(206)) tmpType |= 3;
+		else if(Dlg->GetCheck(208)) tmpType |= 4;
+		if(Dlg->GetCheck(302)) tmpType |= 0x10;
+		else if(Dlg->GetCheck(303)) tmpType |= 0x20;
+		undo_flags = CheckNewInt(&type, type, tmpType, parent, undo_flags);
+		Dlg->GetValue(211, &n_l5.fx);			Dlg->GetValue(213, &n_l5.fy);
+		undo_flags = CheckNewLFPoint(&l5, &o_l5, &n_l5, parent, undo_flags);
+		Dlg->GetValue(305, &n_clip.Xmin);		Dlg->GetValue(307, &n_clip.Xmax);
+		Dlg->GetValue(310, &n_clip.Ymin);		Dlg->GetValue(312, &n_clip.Ymax);
+		undo_flags = CheckNewFloat(&uclip.Xmin, o_clip.Xmin, n_clip.Xmin, parent, undo_flags);
+		undo_flags = CheckNewFloat(&uclip.Xmax, o_clip.Xmax, n_clip.Xmax, parent, undo_flags);
+		undo_flags = CheckNewFloat(&uclip.Ymin, o_clip.Ymin, n_clip.Ymin, parent, undo_flags);
+		undo_flags = CheckNewFloat(&uclip.Ymax, o_clip.Ymax, n_clip.Ymax, parent, undo_flags);
+		if(!cp || (undo_flags & UNDO_CONTINUE)) bRet = true;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// SDellipse properties
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char* SdEllipseDlg_Tmpl = 
+	"1,+,,DEFAULT,PUSHBUTTON,-1,150,10,45,12\n"
+	".,.,,,PUSHBUTTON,-2,150,25,45,12\n"
+	".,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,+,100,ISPARENT | CHECKED,SHEET,1,5,10,139,140\n"
+	".,,200,ISPARENT,SHEET,2,5,10,139,140\n"
+	"100,,,NOSELECT,ODBUTTON,3,10,48,130,100\n"
+	"200,+,,,CHECKBOX,4,20,26,80,9\n"
+	".,.,250,CHECKED, GROUPBOX,5,10,45,129,100\n"
+	"250,+,,,LTEXT,6,25,82,60,8\n"
+	".,.,,,RTEXT,7,20,92,60,8\n"
+	".,.,,,LTEXT,0,82,92,30,8\n"
+	".,.,,,RTEXT,8,20,100,60,8\n"
+	".,.,,,LTEXT,0,82,100,30,8\n"
+	".,.,,,LTEXT,9,25,112,60,8\n"
+	".,.,,,RTEXT,10,20,122,60,8\n"
+	".,.,,,LTEXT,0,82,122,30,8\n"
+	".,.,,,RTEXT,11,20,130,60,8\n"
+	".,.,,,LTEXT,0,82,130,30,8\n"
+	".,.,,,LTEXT,12,25,52,30,8\n"
+	".,.,,,RADIO1,13,50,52,30,8\n"
+	".,.,,,RADIO1,14,50,61,30,8\n"
+	".,,,LASTOBJ,RADIO1,15,50,70,30,8";
+bool
+SDellipse::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 40, 10, "Line"};
+	TabSHEET tab2 = {40, 80, 10, "Details"};
+	void *dyndata[] = {(void*)&tab1, (void*) &tab2, (void*)OD_linedef, (void*)" show regression line",
+		(void*)"  ellipse  ", (void*)"center (means of data)", (void*)"x =", (void*)"y =",
+		(void*)"standard deviation (S.D.)", (void*)"major axis", (void*)"minor axis", (void*)"size:",
+		(void*)" 1 x S.D.", (void*)" 2 x S.D.", (void*)" 3 x S.D."};
+	DlgInfo *ellDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int cb, res, tmpType;
+	LineDEF newLine;
+	bool bRet = false;
+	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+
+	if(!parent) return false;
+	if(!(ellDlg = CompileDialog(SdEllipseDlg_Tmpl, dyndata))) return false;
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
+	if(!(Dlg = new DlgRoot(ellDlg, data))) return false;
+	if(!(type & 0x10000)) Dlg->SetCheck(200, 0L, true);
+	WriteNatFloatToBuff(TmpTxt, mx);		Dlg->SetText(252, TmpTxt+1);
+	WriteNatFloatToBuff(TmpTxt, my);		Dlg->SetText(254, TmpTxt+1);
+	rlp_strcpy(TmpTxt, 4, "+/-");
+	WriteNatFloatToBuff(TmpTxt+3, sd1);		Dlg->SetText(259, TmpTxt);
+	WriteNatFloatToBuff(TmpTxt+3, sd2);		Dlg->SetText(257, TmpTxt);
+	switch(type & 0x60000) {
+		case 0x20000:	Dlg->SetCheck(262, 0L, true);	break;
+		case 0x40000:	Dlg->SetCheck(263, 0L, true);	break;
+		default:		Dlg->SetCheck(261, 0L, true);	break;
+		}
+	tmpType = type;
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "SD ellipse of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "SD ellipse properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 410, 340, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		}while (res < 0);
+	if(res == 1){						//OK pressed
+		Undo.SetDisp(cdisp);
+		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+		if(cmpLineDEF(&LineDef, &newLine)) {
+			Undo.Line(parent, &LineDef, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&LineDef, &newLine, sizeof(LineDEF));
+			}
+		if(Dlg->GetCheck(200)) tmpType &= ~0x10000;
+		else tmpType |= 0x10000;
+		tmpType &= ~0x60000;
+		if(Dlg->GetCheck(262)) tmpType |= 0x20000;
+		else if(Dlg->GetCheck(263)) tmpType |= 0x40000;
+		undo_flags = CheckNewInt(&type, type, tmpType, parent, undo_flags);
+		if(undo_flags & UNDO_CONTINUE) bRet = true;
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;			free(ellDlg);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Normal error bars properties
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+ErrorBar::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 38, 10, "Error Bar"};
+	TabSHEET tab2 = {38, 65, 10, "Type"};
+	TabSHEET tab3 = {65, 90, 10, "Edit"};
+	DlgInfo ErrDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to ERROR", 100, 10, 57, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 100, 25, 57, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 100, 40, 57, 12},
+		{4, 0, 5, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{5, 6, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 90, 100},
+		{6, 7, 200, ISPARENT, SHEET, &tab2, 5, 10, 90, 100},
+		{7, 0, 300, TOUCHEXIT | ISPARENT, SHEET, &tab3, 5, 10, 90, 100},
+		{100, 101, 0, 0x0L, RTEXT, (void*)"cap width", 15, 40, 28, 8},
+		{101, 102, 0, 0x0L, EDVAL1, &SizeBar, 46, 40, 25, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 73, 40, 20, 8},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"line width", 15, 55, 28, 8},
+		{104, 105, 0, 0x0L, EDVAL1, &ErrLine.width, 46, 55, 25, 10},
+		{105, 106, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 73, 55, 20, 8},
+		{106, 107, 0, 0x0L, RTEXT, (void*)"line color", 15, 70, 28, 8},
+		{107, 0, 0, OWNDIALOG, COLBUTT, (void *)&ErrLine.color, 46, 70, 25, 10},
+		{200, 0, 500, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{300, 301, 0, 0x0L, RTEXT, (void*)"x-value", 15, 30, 28, 8},
+		{301, 302, 0, 0x0L, EDVAL1, &fPos.fx, 46, 30, 35, 10},
+		{302, 303, 0, 0x0L, RTEXT, (void*)"y-value", 15, 45, 28, 8},
+		{303, 304, 0, 0x0L, EDVAL1, &fPos.fy, 46, 45, 35, 10},
+		{304, 305, 0, 0x0L, RTEXT, (void*)"error", 15, 60, 28, 8},
+		{305, 306, 0, 0x0L, EDVAL1, &ferr, 46, 60, 35, 10},
+		{306, 307, 0, 0x0L, LTEXT, (void*)"description:", 10, 80, 70, 8},
+		{307, 0, 0, 0x0L, EDTEXT, (void*)name, 10, 90, 80, 10},
+		{500, 501, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 12, 40, 25, 25},
+		{501, 502, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 37, 40, 25, 25},
+		{502, 503, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 62, 40, 25, 25},
+		{503, 504, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 12, 65, 25, 25},
+		{504, 505, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 37, 65, 25, 25},
+		{505, 0, 0, LASTOBJ | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 62, 65, 25, 25}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int cb, res, tmpType = type;
+	double n_sb, o_sb, n_lw, o_lw, n_err, o_err;
+	lfPOINT n_pos, o_pos;
+	DWORD n_col, undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+	bool bRet = false;
+	char desc[80];
+
+	if(!parent) return false;
+	desc[0] = 0;
+	if(!(Dlg = new DlgRoot(ErrDlg, data)))return false;
+	Dlg->SetCheck(500 + (type & 0x7), 0L, true);
+	if(!(Dlg->GetValue(101, &o_sb))) o_sb = SizeBar;
+	if(!(Dlg->GetValue(104, &o_lw))) o_lw = ErrLine.width;
+	if(!(Dlg->GetValue(305, &o_err))) o_err = ferr;
+	if(!(Dlg->GetValue(301, &o_pos.fx))) o_pos.fx = fPos.fx;
+	if(!(Dlg->GetValue(303, &o_pos.fy))) o_pos.fy = fPos.fy;
+	n_sb = o_sb;	n_lw = o_lw;	n_err = o_err; n_pos.fx = o_pos.fx;	n_pos.fy = o_pos.fy;
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Error bar of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Error bar properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 328, 260, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 500:	case 501:	case 502:
+		case 503:	case 504:	case 505:
+			tmpType = res-500;			res = -1;	break;
+		case 7:								//edit tab
+			Dlg->Activate(307, true);	res = -1;	break;
+		case 1:								//accept for this object
+		case 2:								//   or all objects of plot
+			desc[0] = 0;
+			Undo.SetDisp(cdisp);			Dlg->GetText(307, desc, 80);
+			Dlg->GetValue(101, &n_sb);		Dlg->GetValue(104, &n_lw);
+			Dlg->GetColor(107, &n_col);		Dlg->GetValue(305, &n_err);
+			Dlg->GetValue(301, &n_pos.fx);	Dlg->GetValue(303, &n_pos.fy);
+			break;
+			}
+		}while (res <0);
+	switch (res) {
+	case 1:				//new setting for current error bar only
+		undo_flags = CheckNewFloat(&ferr, o_err, n_err, parent, undo_flags);
+		undo_flags = CheckNewLFPoint(&fPos, &o_pos, &n_pos, parent, undo_flags);
+		if(undo_flags & UNDO_CONTINUE) parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+		undo_flags = CheckNewString(&name, name, desc, this, undo_flags);
+		undo_flags = CheckNewFloat(&SizeBar, o_sb, n_sb, parent, undo_flags);
+		undo_flags = CheckNewFloat(&ErrLine.width, o_lw, n_lw, parent, undo_flags);
+		undo_flags = CheckNewDword(&ErrLine.color, ErrLine.color, n_col, parent, undo_flags);
+		undo_flags = CheckNewInt(&type, type, tmpType, parent, undo_flags);
+		if(undo_flags & UNDO_CONTINUE) bRet = true;
+		break;
+	case 2:				//new settings to all error bars of plot
+		parent->Command(CMD_SAVE_ERRS, 0L, 0L);
+		if(desc[0] || name) parent->Command(CMD_ERRDESC, desc, 0L);
+		parent->SetSize(SIZE_ERRBAR, n_sb);
+		parent->SetSize(SIZE_ERRBAR_LINE, n_lw);
+		parent->SetColor(COL_ERROR_LINE, n_col);
+		if(type != tmpType) parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+		bRet = parent->Command(CMD_ERR_TYPE, (void*)(& tmpType), 0L);
+		break;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Arrow properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Arrow::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 29, 10, "Arrow"};
+	TabSHEET tab2 = {29, 59, 10, "Type"};
+	TabSHEET tab3 = {59, 90, 10, "Edit"};
+	DlgInfo ArrowDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, type & ARROW_UNITS ? 
+			(void*)"OK" : (void*)"Apply to ARROW", 100, 10, 57, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 100, 25, 57, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 100, 
+			type & ARROW_UNITS ? 25 : 40, 57, 12},
+		{4, 50, 5, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{5, 6, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 90, 100},
+		{6, 7, 200, ISPARENT, SHEET, &tab2, 5, 10, 90, 100},
+		{7, 0, 300, ISPARENT, SHEET, &tab3, 5, 10, 90, 100},
+		{50, 0, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
+		{100, 101, 0, 0x0L, RTEXT, (void*)"cap width", 15, 40, 28, 8},
+		{101, 102, 0, 0x0L, EDVAL1, &cw, 46, 40, 25, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 73, 40, 20, 8},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"length", 15, 52, 28, 8},
+		{104, 105, 0, 0x0L, EDVAL1, &cl, 46, 52, 25, 10},
+		{105, 106, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 73, 52, 20, 8},
+		{106, 107, 0, 0x0L, RTEXT, (void*)"line width", 15, 70, 28, 8},
+		{107, 108, 0, 0x0L, EDVAL1, &LineDef.width, 46, 70, 25, 10},
+		{108, 109, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 73, 70, 20, 8},
+		{109, 110, 0, 0x0L, RTEXT, (void*)"color", 15, 82, 28, 8},
+		{110, 0, 0, OWNDIALOG, COLBUTT, (void *)&LineDef.color, 46, 82, 25, 10},
+		{200, 201, 0, TOUCHEXIT, RADIO1, (void*)"line only", 15, 40, 60, 8},
+		{201, 202, 0, TOUCHEXIT, RADIO1, (void*)"arrow with lines", 15, 55, 60, 8},
+		{202, 0, 0, TOUCHEXIT, RADIO1, (void*)"filled arrow", 15, 70, 60, 8},
+		{300, 301, 0, 0x0L, RTEXT, (void*)"x-value", 10, 30, 28, 8},
+		{301, 302, 0, 0x0L, EDVAL1, &pos2.fx, 46, 30, 35, 10},
+		{302, 303, 0, 0x0L, RTEXT, (void*)"y-value", 10, 42, 28, 8},
+		{303, 304, 0, 0x0L, EDVAL1, &pos2.fy, 46, 42, 35, 10},
+		{304, 305, 0, 0x0L, RTEXT, (void*)"origin x", 10, 60, 28, 8},
+		{305, 306, 0, 0x0L, EDVAL1, &pos1.fx, 46, 60, 35, 10},
+		{306, 307, 0, 0x0L, RTEXT, (void*)" y", 10, 72, 28, 8},
+		{307, 308, 0, 0x0L, EDVAL1, &pos1.fy, 46, 72, 35, 10},
+		{308, 309, 0, 0x0L, CHECKBOX, (void*)"set common origin to", 16, 85, 70, 8},
+		{309, 0, 0, 0x0L, LTEXT, (void*)"all arrows of plot", 25, 93, 65, 8},
+		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 108, 67, 15, 15},
+		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 134, 67, 15, 15},
+		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 134, 82, 15, 15},
+		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 108, 82, 15, 15}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	lfPOINT o_pos1, o_pos2, n_pos1, n_pos2;
+	double o_cw, o_cl, n_cw, n_cl, o_lw, n_lw;
+	int cb, res, tmptype = type, undo_level = *Undo.pcb;
+	bool bRet = false;
+	DWORD o_col, n_col, undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+
+	if(!parent) return false;
+	if(!(Dlg = new DlgRoot(ArrowDlg, data))) return false;
+	Dlg->GetValue(301, &o_pos2.fx);		Dlg->GetValue(303, &o_pos2.fy);
+	Dlg->GetValue(305, &o_pos1.fx);		Dlg->GetValue(307, &o_pos1.fy);
+	Dlg->GetValue(101, &o_cw);			Dlg->GetValue(104, &o_cl);
+	Dlg->GetValue(107, &o_lw);			Dlg->GetColor(110, &o_col);
+	if(parent->Id ==  GO_GRAPH || parent->Id == GO_PAGE) Dlg->ShowItem(50, true);
+	switch(type & 0xff) {
+	case ARROW_LINE:		Dlg->SetCheck(201, 0L, true);		break;
+	case ARROW_TRIANGLE:	Dlg->SetCheck(202, 0L, true);		break;
+	default:				Dlg->SetCheck(200, 0L, true);		break;
+		}
+	if(tmptype & ARROW_UNITS){
+		Dlg->ShowItem(2, false);
+		Dlg->ShowItem(308, false);		Dlg->ShowItem(309, false);
+		}
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Arrow of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Arrow properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 328, 260, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();			res = Dlg->GetResult();
+		switch (res) {
+		case 600:	case 601:	case 602:	case 603:
+			Undo.SetDisp(cdisp);
+			res = ExecDrawOrderButt(parent, this, res);
+			}
+		switch (res) {
+		case 200:	tmptype = ARROW_NOCAP;		res = -1;	break;
+		case 201:	tmptype = ARROW_LINE;		res = -1;	break;
+		case 202:	tmptype = ARROW_TRIANGLE;	res = -1;	break;
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);
+			Dlg->GetValue(301, &n_pos2.fx);		Dlg->GetValue(303, &n_pos2.fy);
+			Dlg->GetValue(305, &n_pos1.fx);		Dlg->GetValue(307, &n_pos1.fy);
+			Dlg->GetValue(101, &n_cw);			Dlg->GetValue(104, &n_cl);
+			Dlg->GetValue(107, &n_lw);			Dlg->GetColor(110, &n_col);
+			break;
+			}
+		}while (res <0);
+	switch (res) {
+	case 1:				//new setting for current arrow
+		if(n_pos1.fx != o_pos1.fx || n_pos1.fy != o_pos1.fy ||
+			n_pos2.fx != o_pos2.fx || n_pos2.fy != o_pos2.fy){
+			Command(CMD_SAVEPOS, 0L, 0L);				undo_flags |= UNDO_CONTINUE;
+			memcpy(&pos1, &n_pos1, sizeof(lfPOINT));	memcpy(&pos2, &n_pos2, sizeof(lfPOINT));
+			if(!(type & ARROW_UNITS)) parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+			}
+		if((type & 0xff) != (tmptype & 0xff)){
+			Undo.ValInt(this, &type, undo_flags);
+			type &= ~0xff;	type |= (tmptype & 0xff);	undo_flags |= UNDO_CONTINUE;
+			}
+		undo_flags = CheckNewFloat(&cw, o_cw, n_cw, this, undo_flags);
+		undo_flags = CheckNewFloat(&cl, o_cl, n_cl, this, undo_flags);
+		undo_flags = CheckNewFloat(&LineDef.width, o_lw, n_lw, this, undo_flags);
+		undo_flags = CheckNewDword(&LineDef.color, o_col, n_col, this, undo_flags);
+		if(undo_flags & UNDO_CONTINUE) bModified = true;
+		bRet = true;
+		break;
+	case 2:				//new settings to all arrows of plot
+		if(parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			parent->Command(CMD_SAVE_ARROWS, 0L, 0L);
+			if(Dlg->GetCheck(308)) parent->Command(CMD_ARROW_ORG, &n_pos1, 0L);
+			parent->SetSize(SIZE_ARROW_LINE, n_lw);
+			parent->SetSize(SIZE_ARROW_CAPWIDTH, n_cw);
+			parent->SetSize(SIZE_ARROW_CAPLENGTH, n_cl);
+			parent->SetColor(COL_ARROW, n_col);
+			parent->Command(CMD_ARROW_TYPE, &tmptype, 0L);
+			bRet = true;
+			}
+		break;
+	case 3:								//Cancel
+		Undo.SetDisp(cdisp);
+		if(*Undo.pcb > undo_level) {	//restore plot order
+			while(*Undo.pcb > undo_level)	Undo.Restore(false, 0L);
+			bRet = true;
+			}
+		break;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Box properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Box::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 50, 10, "Size & Color"};
+	TabSHEET tab2 = {50, 90, 10, "Edit"};
+	char sTxt1[20], sTxt2[20];
+	DlgInfo BoxDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to BOX", 130, 10, 55, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 130, 25, 55, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 40, 55, 12},
+		{4, 0, 5, CHECKED | ISPARENT, GROUP, 0L, 138, 40, 55, 12},
+		{5, 6, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 120, 115},
+		{6, 0, 300, ISPARENT, SHEET, &tab2, 5, 10, 120, 115},
+		{100, 105, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 8, 30, 90, 50},
+		{105, 0, 109, CHECKED | ISPARENT, GROUP, 0L, 0, 0, 0, 0},
+		{109, 110, 0, 0x0L, LTEXT, (void*)"bar width:", 10, 80, 45, 8},
+		{110, 111, 0, 0x0L, RADIO1, (void*)" fixed", 20, 92, 25, 8},
+		{111, 112, 0, 0x0L, EDTEXT, &sTxt1[1], 60, 92, 25, 10},
+		{112, 113, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 87, 92, 20, 8},
+		{113, 114, 0, 0x0L, RADIO1, (void*)" relative", 20, 104, 25, 8},
+		{114, 115, 0, 0x0L, EDTEXT, &sTxt2[1], 60, 104, 25, 10},
+		{115, 0, 0, 0x0L, LTEXT, (void*)"%", 87, 104, 10, 8},
+		{300, 301, 0, 0x0L, RTEXT, (void*)"point 1 x", 15, 40, 28, 8},
+		{301, 302, 0, 0x0L, EDVAL1, &pos1.fx, 46, 40, 35, 10},
+		{302, 303, 0, 0x0L, RTEXT, (void*)"y", 15, 52, 28, 8},
+		{303, 304, 0, 0x0L, EDVAL1, &pos1.fy, 46, 52, 35, 10},
+		{304, 305, 0, 0x0L, RTEXT, (void*)"point 2 x", 15, 70, 28, 8},
+		{305, 306, 0, 0x0L, EDVAL1, &pos2.fx, 46, 70, 35, 10},
+		{306, 307, 0, 0x0L, RTEXT, (void*)"y", 15, 82, 28, 8},
+		{307, 308, 0, 0x0L, EDVAL1, &pos2.fy, 46, 82, 35, 10},
+		{308, 309, 0, HIDDEN, RTEXT, (void*)"box width", 15, 100, 28, 8},
+		{309, 0, 0, HIDDEN | LASTOBJ, EDVAL1, &size, 46, 100, 35, 10},
+		};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int cb, res, tmpType = type;
+	FillDEF newFill;
+	LineDEF newLine, newHatchLine;
+	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+	lfPOINT n_pos;
+	double tmpVal, o_size, n_size;
+	bool bRet = false;
+	GraphObj *ppar = parent;
+
+	if(!parent) return false;
+	memcpy(&newHatchLine, &Hatchline, sizeof(LineDEF));
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&Outline, 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&Fill, 0);
+	if(type & BAR_RELWIDTH) {
+		WriteNatFloatToBuff(sTxt2, size);
+		WriteNatFloatToBuff(sTxt1, DefSize(SIZE_BAR));
+		}
+	else {
+		WriteNatFloatToBuff(sTxt1, size);
+		rlp_strcpy(sTxt2, 20, " 50");
+		}
+	Dlg = new DlgRoot(BoxDlg, data);
+	if(type & BAR_WIDTHDATA) {
+		Dlg->ShowItem(105, false);
+		Dlg->ShowItem(308, true);		Dlg->ShowItem(309, true);
+		}
+	if(type & BAR_RELWIDTH) Dlg->SetCheck(113, 0L, true);
+	else Dlg->SetCheck(110, 0L, true);
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Box of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Box properties");
+	Dlg->GetValue(309, &o_size);
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 390, 290, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 1:								//accept for this object
+		case 2:								//   or all objects of plot
+			Undo.SetDisp(cdisp);
+			OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+			OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
+			if(newFill.hatch) memcpy(&newHatchLine, newFill.hatch, sizeof(LineDEF));
+			newFill.hatch = &newHatchLine;
+			if(type & BAR_WIDTHDATA) Dlg->GetValue(309, &n_size);
+			else {
+				if(Dlg->GetCheck(113)) {
+					tmpType |= BAR_RELWIDTH;
+					Dlg->GetValue(114, &n_size);
+					}
+				else {
+					tmpType &= ~BAR_RELWIDTH;
+					Dlg->GetValue(111, &n_size);
+					}
+				}
+			break;
+			}
+		}while (res < 0);
+	switch (res) {
+	case 1:				//new setting for current box
+		if(cmpLineDEF(&Outline, &newLine)) {
+			Undo.Line(parent, &Outline, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&Outline, &newLine, sizeof(LineDEF));
+			}
+		if(newFill.type && cmpLineDEF(&Hatchline, &newHatchLine)) {
+			Undo.Line(parent, &Hatchline, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&Hatchline, &newHatchLine, sizeof(LineDEF));
+			}
+		if(cmpFillDEF(&Fill, &newFill)) {
+			Undo.Fill(parent, &Fill, undo_flags);		undo_flags |= UNDO_CONTINUE;
+			memcpy(&Fill, &newFill, sizeof(FillDEF));
+			}
+		Fill.hatch = &Hatchline;
+		if(Dlg->GetValue(301, &tmpVal)) n_pos.fx = tmpVal;
+		else n_pos.fx = pos1.fx;
+		if(Dlg->GetValue(303, &tmpVal)) n_pos.fy = tmpVal;
+		else n_pos.fy = pos1.fy;
+		undo_flags = CheckNewLFPoint(&pos1, &pos1, &n_pos, parent, undo_flags);
+		if(Dlg->GetValue(305, &tmpVal)) n_pos.fx = tmpVal;
+		else n_pos.fx = pos2.fx;
+		if(Dlg->GetValue(307, &tmpVal)) n_pos.fy = tmpVal;
+		else n_pos.fy = pos2.fy;
+		undo_flags = CheckNewLFPoint(&pos2, &pos2, &n_pos, parent, undo_flags);
+		undo_flags = CheckNewFloat(&size, o_size, n_size, parent, undo_flags);
+		if(undo_flags & UNDO_CONTINUE) bRet = true;
+		break;
+	case 2:				//new settings to all boxes of plot
+		if(type != tmpType || Outline.width != newLine.width || o_size != n_size) {
+			Undo.ValInt(this, &type, 0L);			//dummy: all following have UNDO_CONTINUE
+			undo_flags |= UNDO_CONTINUE;
+			if(parent->parent && parent->parent->Id == GO_STACKBAR) ppar = parent->parent;
+			}
+		else if(newLine.color != Outline.color || cmpFillDEF(&Fill, &newFill)) {
+			Undo.ValInt(this, &type, 0L);			//dummy: all following have UNDO_CONTINUE
+			}
+		ppar->Command(CMD_SAVE_BARS_CONT, 0L, 0L);
+		ppar->Command(CMD_BOX_TYPE, (void*)(& tmpType), 0L);
+		ppar->SetSize(SIZE_BOX_LINE, newLine.width);
+		ppar->SetSize(SIZE_BOX, n_size);
+		parent->SetColor(COL_BOX_LINE, newLine.color);
+		parent->Command(CMD_BOX_FILL, &newFill, 0L);
+		bRet = true;
+		break;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Whisker properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *WhiskerDlgTmpl =
+		"1,2,,DEFAULT,PUSHBUTTON,1,100,10,64,12\n"
+		"2,3,,,PUSHBUTTON,2,100,25,64,12\n"
+		"3,4,,,PUSHBUTTON,-2, 100, 40, 64, 12\n"
+		"4,,5,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+		"5,6,100,ISPARENT | CHECKED,SHEET,3, 5, 10, 90, 100\n"
+		"6,7,500,ISPARENT,SHEET,4,5,10,90,100\n"
+		"7,,300,ISPARENT,SHEET,5,5,10,90,100\n"
+		"100,101,,,RTEXT,6,15,40,28,8\n"
+		"101,102,,,EDVAL1,7,46,40,25,10\n"
+		"102,103,,,LTEXT,-3,73,40,20,8\n"
+		"103,104,,,RTEXT,8,15,55,28,8\n"
+		"104,105,,,EDVAL1,9,46,55,25,10\n"
+		"105,106,,,LTEXT,-3,73,55,20,8\n"
+		"106,107,,,RTEXT,10,15,70,28,8\n"
+		"107,,,OWNDIALOG,COLBUTT,11,46,70,25,10\n"
+		"300,301,,,RTEXT,12,15,30,28,8\n"
+		"301,302,,,EDVAL1,13,46,30,35,10\n"
+		"302,303,,,RTEXT,-5,15,42,28,8\n"
+		"303,304,,,EDVAL1,14,46,42,35,10\n"
+		"304,305,,,RTEXT,15,15,55,28,8\n"
+		"305,306,,,EDVAL1,16,46,55,35,10\n"
+		"306,307,,,RTEXT,-5,15,67,28,8\n"
+		"307,308,,,EDVAL1,17,46,67,35,10\n"
+		"308,309,,,LTEXT,18,10,85,70,8\n"
+		"309,,,,EDTEXT,19,10,95,80,10\n"
+		"500,501,,EXRADIO,ODBUTTON,20,32,40,18,18\n"
+		"501,502,,EXRADIO,ODBUTTON,20,14,40,18,18\n"
+		"502,503,,EXRADIO,ODBUTTON,20,50,40,18,18\n"
+		"503,504,,EXRADIO,ODBUTTON,20,68,40,18,18\n"
+		"504,,,LASTOBJ,LTEXT,-27,14,30,30,9";
+bool
+Whisker::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 38, 10, "Whisker"};
+	TabSHEET tab2 = {65, 90, 10, "Edit"};
+	TabSHEET tab3 = {38, 65, 10, "Style"};
+	void *dyndata[] = {(void*)"Apply to WHISKER", (void*)"Apply to PLOT", (void*)&tab1,
+		(void*)&tab3, (void*)&tab2, (void*)"cap width", (void*)&size, (void*)"line width",
+		(void*)&LineDef.width, (void*)"line color", (void *)&LineDef.color, (void*)"point 1 x",
+		(void*)&pos1.fx, (void*)&pos1.fy, (void*)"point 2 x", (void*)&pos2.fx, (void*)&pos2.fy,
+		(void*)"description:", (void*)name, (void*)(OD_WhiskerTempl)};
+	DlgInfo *ErrDlg = CompileDialog(WhiskerDlgTmpl, dyndata);
+	DlgRoot *Dlg;
+	void *hDlg;
+	int cb, res, tmp_type;
+	DWORD undo_flags = 0L, n_col = LineDef.color;
+	anyOutput *cdisp = Undo.cdisp;
+	lfPOINT n_pos;
+	double tmpVal, o_size, n_size, o_lw, n_lw;
+	bool bRet = false;
+	char desc[80];
+
+	if(!parent) return false;
+	desc[0] = 0;	tmp_type = type;
+	Dlg = new DlgRoot(ErrDlg, data);
+	Dlg->SetCheck(500 + (tmp_type & 0x03), 0L, true);
+	Dlg->GetValue(101, &o_size);		Dlg->GetValue(104, &o_lw);
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Whisker of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Whisker properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 339, 260, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 500:	case 501:	case 502:	case 503:
+			tmp_type &= ~0x0f;	tmp_type |= (res-500);	res = -1;
+			break;
+		case 1:								//accept for this object
+		case 2:								//   or all objects of plot
+			Undo.SetDisp(cdisp);			Dlg->GetValue(101, &n_size);
+			Dlg->GetValue(104, &n_lw);		Dlg->GetColor(107, &n_col);
+			desc[0] = 0;					Dlg->GetText(309, desc, 80);
+			break;
+			}
+		}while (res <0);
+	switch (res) {
+	case 1:				//new setting for current whisker
+		undo_flags = CheckNewString(&name, name, desc, this, undo_flags);
+		if(Dlg->GetValue(301, &tmpVal)) n_pos.fx = tmpVal;
+		else n_pos.fx = pos1.fx;
+		if(Dlg->GetValue(303, &tmpVal)) n_pos.fy = tmpVal;
+		else n_pos.fy = pos1.fy;
+		undo_flags = CheckNewLFPoint(&pos1, &pos1, &n_pos, parent, undo_flags);
+		if(Dlg->GetValue(305, &tmpVal)) n_pos.fx = tmpVal;
+		else n_pos.fx = pos2.fx;
+		if(Dlg->GetValue(307, &tmpVal)) n_pos.fy = tmpVal;
+		else n_pos.fy = pos2.fy;
+		undo_flags = CheckNewLFPoint(&pos2, &pos2, &n_pos, parent, undo_flags);
+		undo_flags = CheckNewFloat(&size, o_size, n_size, parent, undo_flags);
+		undo_flags = CheckNewInt(&type, type, tmp_type, parent, undo_flags);
+		undo_flags = CheckNewFloat(&LineDef.width, o_lw, n_lw, parent, undo_flags);
+		undo_flags = CheckNewDword(&LineDef.color, LineDef.color, n_col, parent, undo_flags);
+		if(undo_flags & UNDO_CONTINUE) bRet = true;
+		break;
+	case 2:				//new settings to all whiskers of plot
+		if(o_size == n_size && type == tmp_type && o_lw == n_lw 
+			&& n_col == LineDef.color && !desc[0] && !name) break;
+		parent->Command(CMD_SAVE_ERRS, 0L, 0L);
+		if(desc[0] || name) parent->Command(CMD_ERRDESC, desc, 0L);
+		parent->SetSize(SIZE_WHISKER, n_size);
+		parent->SetSize(SIZE_WHISKER_LINE, n_lw);
+		parent->SetColor(COL_WHISKER, n_col);
+		parent->Command(CMD_WHISKER_STYLE, &tmp_type, 0L);
+		bRet = true;
+		break;
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;		free(ErrDlg);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Drop line properties
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+DropLine::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 21, 10, "Line"};
+	TabSHEET tab2 = {21, 42, 10, "Edit"};
+	DlgInfo LineDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to LINE", 150, 10, 50, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 150, 25, 50, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 150, 40, 50, 12},
+		{4, 300, 10, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
+		{10, 11, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 139, 120},
+		{11, 0, 200, ISPARENT, SHEET, &tab2, 5, 10, 139, 120},
+		{100, 0, 0, NOSELECT, ODBUTTON, (void*)OD_linedef, 10, 38, 130, 100},
+		{200, 201, 0, 0x0L, RTEXT, (void*)"x-value", 35, 40, 28, 8},
+		{201, 202, 0, 0x0L, EDVAL1, &fPos.fx, 76, 40, 35, 10},
+		{202, 203, 0, 0x0L, RTEXT, (void*)"y-value", 35, 52, 28, 8},
+		{203, 204, 0, 0x0L, EDVAL1, &fPos.fy, 76, 52, 35, 10},
+		{204, 0, 250, CHECKED | ISPARENT, GROUPBOX, (void*)" Shape ", 10, 70, 129, 50},
+		{250, 251, 0, 0x0L, RTEXT, (void*)"line to:", 15, 80, 28, 8},
+		{251, 252, 0, 0x0L, CHECKBOX, (void*)"left", 46, 80, 35, 8},
+		{252, 253, 0, 0x0L, CHECKBOX, (void*)"right", 46, 90, 35, 8},
+		{253, 254, 0, 0x0L, CHECKBOX, (void*)"y-axis", 46, 100, 35, 8},
+		{254, 255, 0, 0x0L, CHECKBOX, (void*)"top", 86, 80, 35, 8},
+		{255, 256, 0, 0x0L, CHECKBOX, (void*)"bottom", 86, 90, 35, 8},
+		{256, 0, 0, LASTOBJ, CHECKBOX, (void*)"x-axis", 86, 100, 35, 8}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int cb, res, tmptype;
+	bool bRet = false;
+	LineDEF newLine;
+	DWORD undo_flags = 0L;
+	anyOutput * cdisp = Undo.cdisp;
+	lfPOINT o_pos, n_pos;
+
+	if(!parent) return false;
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
+	Dlg = new DlgRoot(LineDlg, data);
+	Dlg->SetCheck(251, 0L, type & DL_LEFT ? true : false);
+	Dlg->SetCheck(252, 0L, type & DL_RIGHT ? true : false);
+	Dlg->SetCheck(253, 0L, type & DL_YAXIS ? true : false);
+	Dlg->SetCheck(254, 0L, type & DL_TOP ? true : false);
+	Dlg->SetCheck(255, 0L, type & DL_BOTTOM ? true : false);
+	Dlg->SetCheck(256, 0L, type & DL_XAXIS ? true : false);
+	Dlg->GetValue(201, &o_pos.fx);		Dlg->GetValue(203, &o_pos.fy);
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Dropline of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Dropline properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 415, 300, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 1:							//this line
+		case 2:							//all lines of plot
+			Undo.SetDisp(cdisp);		tmptype = 0;
+			if(Dlg->GetCheck(251)) tmptype |= DL_LEFT;
+			if(Dlg->GetCheck(252)) tmptype |= DL_RIGHT;
+			if(Dlg->GetCheck(253)) tmptype |= DL_YAXIS;
+			if(Dlg->GetCheck(254)) tmptype |= DL_TOP;
+			if(Dlg->GetCheck(255)) tmptype |= DL_BOTTOM;
+			if(Dlg->GetCheck(256)) tmptype |= DL_XAXIS;
+			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+			break;
+			}
+		}while (res < 0);
+	if(res == 1){						//Apply to line
+		if(cmpLineDEF(&LineDef, &newLine)) {
+			Undo.Line(this, &LineDef, undo_flags);
+			memcpy(&LineDef, &newLine, sizeof(LineDEF));
+			undo_flags |= UNDO_CONTINUE;
+			}
+		Dlg->GetValue(201, &n_pos.fx);		Dlg->GetValue(203, &n_pos.fy);
+		undo_flags = CheckNewLFPoint(&fPos, &o_pos, &n_pos, this, undo_flags);
+		undo_flags = CheckNewInt(&type, type, tmptype, this, undo_flags);
+		if (undo_flags & UNDO_CONTINUE) bModified = true;
+		bRet = true;
+		}
+	else if(res == 2) {					//Apply to plot
+		if(cmpLineDEF(&LineDef, &newLine) || type != tmptype) {
+			parent->Command(CMD_SAVE_DROPLINES, 0L, 0L);
+			if(cmpLineDEF(&LineDef, &newLine)) parent->Command(CMD_DL_LINE, (void*)&newLine, 0L);
+			if(type != tmptype) parent->Command(CMD_DL_TYPE, (void*)(&tmptype), 0L);
+			bRet = true;
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Drop line properties: DropLine 3D
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+DropLine3D::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 21, 10, "Line"};
+	TabSHEET tab2 = {21, 42, 10, "Edit"};
+	DlgInfo LineDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to LINE", 150, 10, 50, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 150, 25, 50, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 150, 40, 50, 12},
+		{4, 300, 10, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
+		{10, 11, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 139, 120},
+		{11, 0, 200, ISPARENT, SHEET, &tab2, 5, 10, 139, 120},
+		{100, 0, 0, NOSELECT, ODBUTTON, (void*)OD_linedef, 10, 38, 130, 100},
+		{200, 201, 0, 0x0L, RTEXT, (void*)"x-value", 35, 30, 28, 8},
+		{201, 202, 0, 0x0L, EDVAL1, &fPos.fx, 76, 30, 35, 10},
+		{202, 203, 0, 0x0L, RTEXT, (void*)"y-value", 35, 42, 28, 8},
+		{203, 204, 0, 0x0L, EDVAL1, &fPos.fy, 76, 42, 35, 10},
+		{204, 205, 0, 0x0L, RTEXT, (void*)"z-value", 35, 54, 28, 8},
+		{205, 240, 0, 0x0L, EDVAL1, &fPos.fz, 76, 54, 35, 10},
+		{240, 0, 250, CHECKED | ISPARENT, GROUPBOX, (void*)" Shape ", 10, 70, 129, 50},
+		{250, 251, 0, 0x0L, RTEXT, (void*)"line to:", 15, 80, 28, 8},
+		{251, 252, 0, 0x0L, CHECKBOX, (void*)"bottom", 46, 80, 35, 8},
+		{252, 253, 0, 0x0L, CHECKBOX, (void*)"top", 86, 80, 35, 8},
+		{253, 254, 0, 0x0L, CHECKBOX, (void*)"back", 46, 90, 35, 8},
+		{254, 255, 0, 0x0L, CHECKBOX, (void*)"front", 86, 90, 35, 8},
+		{255, 256, 0, 0x0L, CHECKBOX, (void*)"left", 46, 100, 35, 8},
+		{256, 0, 0, LASTOBJ, CHECKBOX, (void*)"right", 86, 100, 35, 8}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int cb, res, tmptype, i;
+	bool bRet = false;
+	LineDEF newLine;
+	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+	fPOINT3D o_pos, n_pos;
+
+	if(!parent) return false;
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+	Dlg = new DlgRoot(LineDlg, data);
+	for(i = 0; i < 6; i++) {
+		Dlg->SetCheck(251+i, 0L, (type & (1<<i)) ? true : false);
+		}
+	Dlg->GetValue(201, &o_pos.fx);		Dlg->GetValue(203, &o_pos.fy);
+	Dlg->GetValue(205, &o_pos.fz);
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Dropline of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Dropline properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 415, 300, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 1:							//this line
+		case 2:							//all lines of plot
+			Undo.SetDisp(cdisp);
+			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+			for(i = tmptype = 0; i < 6; i++) {
+				tmptype |= Dlg->GetCheck(251+i) ? 1<<i : 0;
+				}
+			break;
+			}
+		}while (res < 0);
+	if(res == 1){						//Apply to line
+		if(cmpLineDEF(&Line, &newLine)) {
+			Undo.Line(this, &Line, undo_flags);
+			memcpy(&Line, &newLine, sizeof(LineDEF));
+			undo_flags |= UNDO_CONTINUE;
+			}
+		Dlg->GetValue(201, &n_pos.fx);		Dlg->GetValue(203, &n_pos.fy);
+		Dlg->GetValue(205, &n_pos.fz);
+		if(n_pos.fx != o_pos.fx || n_pos.fy != o_pos.fy || n_pos.fz != o_pos.fz) {
+			Undo.ValLFP3D(this, &fPos, undo_flags);
+			memcpy(&fPos, &n_pos, sizeof(fPOINT3D));
+			undo_flags |= UNDO_CONTINUE;
+			}
+		undo_flags = CheckNewInt(&type, type, tmptype, this, undo_flags);
+		if (undo_flags & UNDO_CONTINUE) bModified = true;
+		bRet = true;
+		}
+	else if(res == 2) {					//Apply to plot
+		if(cmpLineDEF(&Line, &newLine) || type != tmptype) {
+			parent->Command(CMD_SAVE_DROPLINES, 0L, 0L);
+			if(cmpLineDEF(&Line, &newLine)) parent->Command(CMD_DL_LINE, (void*)&newLine, 0L);
+			if(type != tmptype) parent->Command(CMD_DL_TYPE, (void*)(&tmptype), 0L);
+			bRet = true;
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// sphere (ball) properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Sphere::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 22, 10, "Ball"};
+	TabSHEET tab2 = {22, 45, 10, "Edit"};
+	FillDEF newFill;
+	char *type_text[] = {Units[defs.cUnits].display, "[x-data]", "[y-data]", "[z-data]"};
+	DlgInfo BallDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to BALL", 110, 10, 50, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 110, 25, 50, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 110, 40, 50, 12},
+		{4, 0, 10, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
+		{10, 11, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 100, 80},
+		{11, 0, 200, ISPARENT, SHEET, &tab2, 5, 10, 100, 80},
+		{100, 101, 0, 0x0L, RTEXT, (void*)"ball size", 15, 35, 28, 8},
+		{101, 102, 0, 0x0L, INCDECVAL1, &size, 46, 35, 32, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void*)type_text[type < 4 ? type : 0], 80, 35, 15, 8},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"line width", 15, 47, 28, 8},
+		{104, 105, 0, 0x0L, INCDECVAL1, &Line.width, 46, 47, 32, 10},
+		{105, 106, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 80, 47, 15, 8},
+		{106, 107, 0, 0x0L, RTEXT, (void*)"line color", 15, 59, 28, 8},
+		{107, 108, 0, OWNDIALOG, COLBUTT, (void *)&Line.color, 46, 59, 25, 10},
+		{108, 109, 0, 0x0L, RTEXT, (void*)"fill color", 15, 71, 28, 8},
+		{109, 0, 0, OWNDIALOG, SHADE3D, &newFill, 46, 71, 25, 10},
+		{200, 201, 0, 0x0L, RTEXT, (void*)"x-value", 15, 35, 28, 8},
+		{201, 202, 0, 0x0L, EDVAL1, &fPos.fx, 46, 35, 35, 10},
+		{202, 203, 0, 0x0L, RTEXT, (void*)"y-value", 15, 47, 28, 8},
+		{203, 204, 0, 0x0L, EDVAL1, &fPos.fy, 46, 47, 35, 10},
+		{204, 205, 0, 0x0L, RTEXT, (void*)"z-value", 15, 59, 28, 8},
+		{205, 0, 0, LASTOBJ, EDVAL1, &fPos.fz, 46, 59, 35, 10}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int cb, res;
+	bool bRet = false, bContinue = false;
+	fPOINT3D n_pos, o_pos;
+	DWORD new_lcolor = Line.color, undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+	double o_size, n_size, o_lsize, n_lsize;
+
+	if(!parent) return false;
+	memcpy(&newFill, &Fill, sizeof(FillDEF));
+	Dlg = new DlgRoot(BallDlg, data);
+	Dlg->GetValue(201, &o_pos.fx);			Dlg->GetValue(203, &o_pos.fy);
+	Dlg->GetValue(205, &o_pos.fz);			Dlg->GetValue(101, &o_size);
+	Dlg->GetValue(104, &o_lsize);
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Ball of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Ball properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 335, 220, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue) res = -1;
+			bContinue = false;
+			break;
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);			Dlg->GetColor(107, &new_lcolor);
+			Dlg->GetValue(101, &n_size);	Dlg->GetValue(104, &n_lsize);
+			break;
+		case 109:
+			Dlg->DoPlot(0L);				bContinue = true;
+			break;
+			}
+		}while (res < 0);
+	if(res == 1){
+		Dlg->GetValue(201, &n_pos.fx);		Dlg->GetValue(203, &n_pos.fy);
+		Dlg->GetValue(205, &n_pos.fz);
+		if(n_pos.fx != o_pos.fx || n_pos.fy != o_pos.fy || n_pos.fz != o_pos.fz) {
+			Undo.ValLFP3D(this, &fPos, undo_flags);		undo_flags |= UNDO_CONTINUE;
+			memcpy(&fPos, &n_pos, sizeof(fPOINT3D));	parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+			}
+		undo_flags = CheckNewFloat(&size, o_size, n_size, this, undo_flags);
+		undo_flags = CheckNewFloat(&Line.width, o_lsize, n_lsize, this, undo_flags);
+		undo_flags = CheckNewDword(&Line.color, Line.color, new_lcolor, this, undo_flags);
+		undo_flags = CheckNewDword(&Fill.color, Fill.color, newFill.color, this, undo_flags);
+		undo_flags = CheckNewDword(&Fill.color2, Fill.color2, newFill.color2, this, undo_flags);
+		undo_flags = CheckNewInt(&Fill.type, Fill.type, newFill.type, this, undo_flags);
+		if(undo_flags & UNDO_CONTINUE) bRet = bModified = true;
+		}
+	else if(res == 2) {
+		if(cmpFillDEF(&Fill, &newFill) || n_size != o_size || n_lsize != o_lsize ||
+			new_lcolor != Line.color) {
+			parent->Command(CMD_SAVE_SYMBOLS, 0L, 0L);
+			if(cmpFillDEF(&Fill, &newFill)) parent->Command(CMD_SYM_FILL, &newFill, 0L);
+			if(n_size != o_size) parent->SetSize(SIZE_SYMBOL, n_size);
+			if(n_lsize != o_lsize) parent->SetSize(SIZE_SYM_LINE, n_lsize);
+			if(new_lcolor != Line.color) parent->SetColor(COL_SYM_LINE, new_lcolor);
+			bRet = true;
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// properties of 3D plane
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *Plane3D_DlgTmpl = 
+	"1,2,,DEFAULT, PUSHBUTTON,1,115,10,55,12\n"
+	"2,3,,,PUSHBUTTON,2,115,25,55,12\n"
+	"3,4,,,PUSHBUTTON,-2,115,40,55,12\n"
+	"4,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"10,,100,ISPARENT | CHECKED,SHEET,3,5,10,105,85\n"
+	"100,101,,,RTEXT,4,15,30,36,8\n"
+	"101,102,,,EDVAL1,5,53,30,25,10\n"
+	"102,103,,,LTEXT,-3,80,30,15,8\n"
+	"103,104,,,RTEXT,6,15,42,36,8\n"
+	"104,105,,OWNDIALOG,COLBUTT,7,53,42,25,10\n"
+	"105,106,,,RTEXT,8,15,54,36,8\n"
+	"106,200,,OWNDIALOG,SHADE3D,9,53,54,25,10\n"
+	"200,,201,CHECKED,GROUP,0,0,0,0,0\n"
+	"201,202,,,RTEXT,10,15,66,36,8\n"
+	"202,203,,,INCDECVAL1,11,53,66,25,10\n"
+	"203,204,,,LTEXT,-10,80,66,5,8\n"
+	"204,205,,,RTEXT,12,15,78,36,8\n"
+	"205,206,,,EDVAL1,13,53,78,25,10\n"
+	"206,,,LASTOBJ,LTEXT,14,80,78,40,8";
+
+bool
+Plane3D::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 27, 10, "Plane"};
+	double rb_width = 0.0, rb_z = 0.0;
+	FillDEF newFill;
+	void *dyndata[] = {(void*)"Apply to PLANE", (void*)"Apply to PLOT", (void*)&tab1,
+		 (void*)"line width",  (void*)&Line.width, (void*)"line color", (void *)&Line.color,
+		 (void*)"fill color", (void*)&newFill, (void*)"ribbon width", (void*)&rb_width,
+		 (void*)"ribbon pos.", (void*)&rb_z,  (void*)"[z-data]"};
+	DlgInfo *PlaneDlg = CompileDialog(Plane3D_DlgTmpl, dyndata);
+	DlgRoot *Dlg;
+	void *hDlg;
+	int cb, res;
+	bool bRet = false;
+	DWORD new_lcolor = Line.color, undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+	double o_lsize, n_lsize, o_rbw, n_rbw, o_rbz, n_rbz;
+
+	if(!parent) return false;
+	if(parent->Id == GO_GRID3D) return parent->PropertyDlg();
+	memcpy(&newFill, &Fill, sizeof(FillDEF));
+	if(parent->Id == GO_RIBBON && parent->type == 1) {
+		rb_width = parent->GetSize(SIZE_CELLWIDTH) *100.0;
+		rb_z = parent->GetSize(SIZE_ZPOS);
+		}
+	Dlg = new DlgRoot(PlaneDlg, data);
+	Dlg->GetValue(101, &o_lsize);		Dlg->GetValue(202, &o_rbw);
+	Dlg->GetValue(205, &o_rbz);
+	if(parent && ((parent->Id==GO_RIBBON && parent->type > 1) || parent->Id==GO_GRID3D))
+		Dlg->ShowItem(200, false);								//paravent plot
+	if(parent->Id == GO_RIBBON && parent->type >2) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "3D Surface Properties");
+	else if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Plane of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Plane properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 355, 226, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);				Dlg->GetColor(104, &new_lcolor);
+			Dlg->GetValue(101, &n_lsize);		Dlg->GetValue(202, &n_rbw);
+			Dlg->GetValue(205, &n_rbz);
+			break;
+			}
+		}while (res < 0);
+	if(res == 1){
+		undo_flags = CheckNewFloat(&Line.width, o_lsize, n_lsize, this, undo_flags);
+		undo_flags = CheckNewDword(&Line.color, Line.color, new_lcolor, this, undo_flags);
+		undo_flags = CheckNewDword(&Fill.color, Fill.color, newFill.color, this, undo_flags);
+		undo_flags = CheckNewDword(&Fill.color2, Fill.color2, newFill.color2, this, undo_flags);
+		undo_flags = CheckNewInt(&Fill.type, Fill.type, newFill.type, this, undo_flags);
+		if(undo_flags & UNDO_CONTINUE) bRet = true;
+		}
+	else if(res == 2) {
+		if(cmpFillDEF(&Fill, &newFill) || n_lsize != o_lsize || new_lcolor != Line.color ||
+			o_rbw != n_rbw || o_rbz != n_rbz) {
+			parent->Command(CMD_SAVE_SYMBOLS, 0L, 0L);
+			if(cmpFillDEF(&Fill, &newFill)) parent->Command(CMD_SYM_FILL, &newFill, 0L);
+			if(n_lsize != o_lsize) parent->SetSize(SIZE_SYM_LINE, n_lsize);
+			if(new_lcolor != Line.color) parent->SetColor(COL_POLYLINE, new_lcolor);
+			if(parent->Id == GO_RIBBON && parent->type == 1) {
+				if(o_rbw != n_rbw) parent->SetSize(SIZE_CELLWIDTH, n_rbw/100.0);
+				if(o_rbz != n_rbz) parent->SetSize(SIZE_ZPOS, n_rbz);
+				}
+			bRet = true;
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// properties of 3D column
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Brick::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 50, 10, "Size & Color"};
+//	TabSHEET tab2 = {50, 90, 10, "Baseline"};
+	TabSHEET tab3 = {50, 80, 10, "Edit"};
+	FillDEF newFill;
+	DlgInfo ColumnDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to COLUMN", 130, 10, 65, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 130, 25, 65, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 40, 65, 12},
+		{4, 0, 5, CHECKED | ISPARENT, GROUP, NULL, 138, 40, 55, 12},
+		{5, 7, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 120, 120},
+//		{6, 7, 0, ISPARENT, SHEET, &tab2, 5, 10, 120, 120},
+		{7, 0, 300, ISPARENT, SHEET, &tab3, 5, 10, 120, 120},
+		{100, 101, 0, 0x0L, RTEXT, (void*)"outline width", 18, 30, 40, 8},
+		{101, 102, 0, 0x0L, EDVAL1, &Line.width, 60, 30, 25, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 87, 30, 20, 8},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"outline color", 18, 42, 40, 8},
+		{104, 105, 0, OWNDIALOG, COLBUTT, (void *)&Line.color, 60, 42, 25, 10},
+		{105, 106, 0, 0x0L, RTEXT,(void*)"fill color" , 18, 54, 40, 8},
+		{106, 107, 0, OWNDIALOG, SHADE3D, &newFill, 60, 54, 25, 10},
+		{107, 108, 0, 0x0L, RTEXT, (void*)"column width", 18, 74, 40, 8},
+		{108, 109, 0, 0x0L, EDVAL1, &width, 60, 74, 25, 10},
+		{109, 110, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 87, 74, 20, 8},
+		{110, 111, 0, 0x0L, RTEXT, (void*)"column depth", 18, 86, 40, 8},
+		{111, 112, 0, 0x0L, EDVAL1, &depth, 60, 86, 25, 10},
+		{112, 0, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 87, 86, 20, 8},
+		{300, 301, 0, 0x0L, RTEXT, (void*)"x-value", 10, 30, 35, 8},
+		{301, 302, 0, 0x0L, EDVAL1, &fPos.fx, 50, 30, 30, 10},
+		{302, 303, 0, 0x0L, RTEXT, (void*)"base (y)", 10, 75, 35, 8},
+		{303, 304, 0, 0x0L, EDVAL1, &fPos.fy, 50, 75, 30, 10},
+		{304, 305, 0, 0x0L, RTEXT, (void*)"z-value", 10, 42, 35, 8},
+		{305, 306, 0, 0x0L, EDVAL1, &fPos.fz, 50, 42, 30, 10},
+		{306, 307, 0, 0x0L, RTEXT, (void*)"height", 10, 87, 35, 8},
+		{307, 0, 0, LASTOBJ, EDVAL1, &height, 50, 87, 30, 10}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int cb, res;
+	bool bRet = false;
+	DWORD col1 = Line.color, undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+	double o_lw = Line.width, n_lw, o_height = height, n_height, o_width = width, n_width;
+	double o_depth = depth, n_depth;
+	fPOINT3D o_pos, n_pos;
+
+	if(!parent) return false;
+	memcpy(&newFill, &Fill, sizeof(FillDEF));
+	Dlg = new DlgRoot(ColumnDlg, data);
+	Dlg->GetValue(101, &o_lw);		Dlg->GetValue(301, &o_pos.fx);
+	Dlg->GetValue(303, &o_pos.fy);	Dlg->GetValue(305, &o_pos.fz);
+	Dlg->GetValue(307, &o_height);	Dlg->GetValue(108, &o_width);
+	Dlg->GetValue(111, &o_depth);
+	memcpy(&n_pos, &o_pos, sizeof(fPOINT3D));
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Column of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Column properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 405, 296, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);			Dlg->GetColor(104, &col1);
+			Dlg->GetValue(101, &n_lw);		Dlg->GetValue(301, &n_pos.fx);
+			Dlg->GetValue(303, &n_pos.fy);	Dlg->GetValue(305, &n_pos.fz);
+			Dlg->GetValue(307, &n_height);	Dlg->GetValue(108, &n_width);
+			Dlg->GetValue(111, &n_depth);
+			break;
+			}
+		}while (res < 0);
+	switch (res) {
+	case 1:				//new setting for current column only
+		undo_flags = CheckNewFloat(&Line.width, o_lw, n_lw, this, undo_flags);
+		undo_flags = CheckNewDword(&Line.color, Line.color, col1, this, undo_flags);
+		undo_flags = CheckNewDword(&Fill.color, Fill.color, newFill.color, this, undo_flags);
+		undo_flags = CheckNewDword(&Fill.color2, Fill.color2, newFill.color2, this, undo_flags);
+		undo_flags = CheckNewInt(&Fill.type, Fill.type, newFill.type, this, undo_flags);
+		if(o_pos.fx != n_pos.fx || o_pos.fy != n_pos.fy || o_pos.fz != n_pos.fz) {
+			Undo.ValLFP3D(this, &fPos, undo_flags);		undo_flags |= UNDO_CONTINUE;
+			memcpy(&fPos, &n_pos, sizeof(fPOINT3D));	parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+			}
+		undo_flags = CheckNewFloat(&height, o_height, n_height, this, undo_flags);
+		if(o_height != n_height) parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+		undo_flags = CheckNewFloat(&width, o_width, n_width, this, undo_flags);
+		undo_flags = CheckNewFloat(&depth, o_depth, n_depth, this, undo_flags);
+		if(undo_flags & UNDO_CONTINUE) bModified = bRet = true;
+		break;
+	case 2:
+		if(cmpFillDEF(&Fill, &newFill) || o_lw != n_lw || Line.color != col1 ||
+			o_pos.fy != n_pos.fy || n_width != o_width || o_depth != n_depth) {
+			parent->Command(CMD_SAVE_BARS, 0L, 0L);
+			if(o_pos.fy != n_pos.fy){
+				parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+				parent->SetSize(SIZE_BAR_BASE, n_pos.fy);
+				}
+			if(n_lw != o_lw) parent->SetSize(SIZE_BAR_LINE, n_lw);
+			if(n_width != o_width) parent->SetSize(SIZE_BAR, n_width);
+			if(o_depth != n_depth) parent->SetSize(SIZE_BAR_DEPTH, n_depth);
+			if(Line.color != col1) parent->SetColor(COL_BAR_LINE, col1);
+			if(cmpFillDEF(&Fill, &newFill)) parent->Command(CMD_BAR_FILL, &newFill, 0L);
+			bRet = true;
+			}
+		break;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Properties of arrow in 3D space
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *Arrow3D_DlgTmpl =
+	"1,2,,DEFAULT, PUSHBUTTON,1,100,10,57,12\n"
+	"2,3,,,PUSHBUTTON,2,100,25,57,12\n"
+	"3,4,,,PUSHBUTTON,-2,100,40,57,12\n"
+	"4,,5,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"5,6,100,ISPARENT | CHECKED,SHEET,3,5,10,90,100\n"
+	"6,7,200,ISPARENT,SHEET,4,5,10,90,100\n"
+	"7,,300,ISPARENT,SHEET,5,5,10,90,100\n"
+	"100,101,,,RTEXT,6,15,40,28,8\n"
+	"101,102,,,EDVAL1,7,46,40,25,10\n"
+	"102,103,,,LTEXT,-3,73,40,20,8\n"
+	"103,104,,,RTEXT,8,15,52,28,8\n"
+	"104,105,,,EDVAL1,9,46,52,25,10\n"
+	"105,106,,,LTEXT,-3,73,52,20,8\n"
+	"106,107,,,RTEXT,10,15,70,28,8\n"
+	"107,108,,,EDVAL1,11,46,70,25,10\n"
+	"108,109,,,LTEXT,-3,73,70,20,8\n"
+	"109,110,,,RTEXT,-11,15,82,28,8\n"
+	"110,,,OWNDIALOG,COLBUTT,12,46,82,25,10\n"
+	"200,201,,TOUCHEXIT,RADIO1,13,15,40,60,8\n"
+	"201,202,,TOUCHEXIT,RADIO1,14,15,55,60,8\n"
+	"202,,,TOUCHEXIT,RADIO1,15,15,70,60,8\n"
+	"300,301,,,RTEXT,-12,10,25,28,8\n"
+	"301,302,,,EDVAL1,16,46,25,35,10\n"
+	"302,303,,,RTEXT,-13,10,36,28,8\n"
+	"303,304,,,EDVAL1,17,46,36,35,10\n"
+	"304,305,,,RTEXT,-14,10,47,28,8\n"
+	"305,306,,,EDVAL1,18,46,47,35,10\n"
+	"306,307,,,RTEXT,19,10,60,28,8\n"
+	"307,308,,,EDVAL1,20,46,60,35,10\n"
+	"308,309,,,RTEXT,-5,10,71,28,8\n"
+	"309,310,,,EDVAL1,21,46,71,35,10\n"
+	"310,311,,,RTEXT,-6,10,82,28,8\n"
+	"311,312,,,EDVAL1,22,46,82,35,10\n"
+	"312,,,LASTOBJ,CHECKBOX,23,16,95,70,8";
+
+bool
+Arrow3D::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 29, 10, "Arrow"};
+	TabSHEET tab2 = {29, 59, 10, "Type"};
+	TabSHEET tab3 = {59, 90, 10, "Edit"};
+	void *dyndata[] = {(void*)"Apply to ARROW", (void*)"Apply to PLOT", (void*)&tab1,
+		(void*)&tab2, (void*)&tab3, (void*)"cap width", (void*)&cw, (void*)"length",
+		(void*)&cl, (void*)"line width", (void*)&Line.width, (void *)&Line.color,
+		(void*)"line only", (void*)"arrow with lines", (void*)"filled arrow",
+		(void*)&fPos2.fx, (void*)&fPos2.fy, (void*)&fPos2.fz, (void*)"origin x",
+		(void*)&fPos1.fx, (void*)&fPos1.fy, (void*)&fPos1.fz, (void*)"set common origin"};
+	DlgInfo *ArrowDlg = CompileDialog(Arrow3D_DlgTmpl, dyndata);
+	DlgRoot *Dlg;
+	void *hDlg;
+	fPOINT3D o_pos1, o_pos2, n_pos1, n_pos2;
+	double o_cw, o_cl, n_cw, n_cl, o_lw, n_lw;
+	int cb, res, tmptype = type, undo_level = *Undo.pcb;
+	bool bRet = false;
+	DWORD o_col, n_col, undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+
+	if(!parent) return false;
+	if(!(Dlg = new DlgRoot(ArrowDlg, data))) return false;
+	Dlg->GetValue(301, &o_pos2.fx);		Dlg->GetValue(303, &o_pos2.fy);
+	Dlg->GetValue(305, &o_pos2.fz);		Dlg->GetValue(307, &o_pos1.fx);
+	Dlg->GetValue(309, &o_pos1.fy);		Dlg->GetValue(311, &o_pos1.fz);
+	Dlg->GetValue(101, &o_cw);			Dlg->GetValue(104, &o_cl);
+	Dlg->GetValue(107, &o_lw);			Dlg->GetColor(110, &o_col);
+	switch(type & 0xff) {
+	case ARROW_LINE:		Dlg->SetCheck(201, 0L, true);		break;
+	case ARROW_TRIANGLE:	Dlg->SetCheck(202, 0L, true);		break;
+	default:				Dlg->SetCheck(200, 0L, true);		break;
+		}
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Arrow of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Arrow properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 328, 260, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 200:	tmptype = ARROW_NOCAP;		res = -1;	break;
+		case 201:	tmptype = ARROW_LINE;		res = -1;	break;
+		case 202:	tmptype = ARROW_TRIANGLE;	res = -1;	break;
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);
+			Dlg->GetValue(301, &n_pos2.fx);		Dlg->GetValue(303, &n_pos2.fy);
+			Dlg->GetValue(305, &n_pos2.fz);		Dlg->GetValue(307, &n_pos1.fx);
+			Dlg->GetValue(309, &n_pos1.fy);		Dlg->GetValue(311, &n_pos1.fz);
+			Dlg->GetValue(101, &n_cw);			Dlg->GetValue(104, &n_cl);
+			Dlg->GetValue(107, &n_lw);			Dlg->GetColor(110, &n_col);
+			break;
+			}
+		}while (res <0);
+	switch (res) {
+	case 1:				//new setting for current arrow
+		if(n_pos1.fx != o_pos1.fx || n_pos1.fy != o_pos1.fy ||
+			n_pos1.fz != o_pos1.fz){
+			Undo.ValLFP3D(this, &fPos1, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&fPos1, &n_pos1, sizeof(fPOINT3D));
+			parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+			}
+		if(n_pos2.fx != o_pos2.fx || n_pos2.fy != o_pos2.fy ||
+			n_pos2.fz != o_pos2.fz){
+			Undo.ValLFP3D(this, &fPos2, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&fPos2, &n_pos2, sizeof(fPOINT3D));
+			parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+			}
+		if((type & 0xff) != (tmptype & 0xff)){
+			Undo.ValInt(this, &type, undo_flags);
+			type &= ~0xff;	type |= (tmptype & 0xff);	undo_flags |= UNDO_CONTINUE;
+			}
+		undo_flags = CheckNewFloat(&cw, o_cw, n_cw, this, undo_flags);
+		undo_flags = CheckNewFloat(&cl, o_cl, n_cl, this, undo_flags);
+		undo_flags = CheckNewFloat(&Line.width, o_lw, n_lw, this, undo_flags);
+		undo_flags = CheckNewDword(&Line.color, o_col, n_col, this, undo_flags);
+		if(undo_flags & UNDO_CONTINUE) bModified = true;
+		bRet = true;
+		break;
+	case 2:				//new settings to all arrows of plot
+		if(parent->Id >= GO_PLOT && parent->Id < GO_GRAPH && (Dlg->GetCheck(312) || n_lw != o_lw
+			|| n_cw != o_cw || n_cl != o_cl || o_col != n_col || type != tmptype)) {
+			parent->Command(CMD_SAVE_ARROWS, 0L, 0L);
+			if(Dlg->GetCheck(312)) parent->Command(CMD_ARROW_ORG3D, &n_pos1, 0L);
+			if(n_lw != o_lw) parent->SetSize(SIZE_ARROW_LINE, n_lw);
+			if(n_cw != o_cw) parent->SetSize(SIZE_ARROW_CAPWIDTH, n_cw);
+			if(n_cl != o_cl) parent->SetSize(SIZE_ARROW_CAPLENGTH, n_cl);
+			if(o_col != n_col) parent->SetColor(COL_ARROW, n_col);
+			if(type != tmptype) parent->Command(CMD_ARROW_TYPE, &tmptype, 0L);
+			bRet = true;
+			}
+		break;
+		}
+	CloseDlgWnd(hDlg);	delete Dlg;		free(ArrowDlg);		return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// properties of 3D data line
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Line3D::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 40, 10, "Line"};
+	DlgInfo LineDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 150, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 150, 25, 45, 12},
+		{3, 0, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{4, 0, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 139, 130},
+		{100, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_linedef, 10, 30, 130, 90}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int cb, res;
+	bool bRet = false;
+	LineDEF newLine;
+	anyOutput *cdisp = Undo.cdisp;
+
+	if(!parent) return false;
+	if(parent->Id == GO_GRID3D) return parent->PropertyDlg();
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+	if(!(Dlg = new DlgRoot(LineDlg, data)))return false;
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Line of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Line properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 410, 314, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+	}while (res < 0);
+	switch(res) {
+	case 1:							//OK pressed
+		Undo.SetDisp(cdisp);
+		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+		if(cmpLineDEF(&Line, &newLine)) {
+			if(parent->Id == GO_GRID3D) {
+				parent->Command(CMD_SET_LINE, &newLine, 0L);
+				}
+			else {
+				Undo.Line(this, &Line, 0L);
+				memcpy(&Line, &newLine, sizeof(LineDEF));
+				bModified = true;
+				}
+			}
+		bRet = true;
+		break;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// label (text) properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Label::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 27, 10, "Label"};
+	TabSHEET tab2 = {27, 75, 10, "Font & Style"};
+	TabSHEET tab3 = {75, 110, 10, "Position"};
+	bool isDataLabel = ((parent) && (parent->Id == GO_PLOTSCATT || parent->Id == GO_XYSTAT || parent->Id == GO_BOXPLOT
+		|| parent->Id == GO_CONTOUR));
+	int bwidth = isDataLabel ? 55 : 45;
+	double lspc = 100.0;
+	DlgInfo LabelDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, isDataLabel ? (void*)"Apply to LABEL" : (void*)"OK", 170, 10, bwidth, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 170, isDataLabel ? 40 : 25, bwidth, 12},
+		{3, isDataLabel ? 10 : 50, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{4, 5, 100, ISPARENT, SHEET, &tab1, 5, 10, 159, 100},
+		{5, 6, 200, ISPARENT | CHECKED, SHEET, &tab2, 5, 10, 159, 100},
+		{6, 0, 300, ISPARENT, SHEET, &tab3, 5, 10, 159, 100},
+		{10, 50, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 170, 25, bwidth, 12},
+		{50, 0, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
+		{100, 101, 0, 0x0L, RTEXT, (void*)"size", 30, 33, 45, 8},
+		{101, 102, 0, 0x0L, INCDECVAL1, &TextDef.fSize, 80, 33, 33, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 115, 33, 20, 8},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"color", 30, 45, 45, 8},
+		{104, 107, 0, OWNDIALOG, COLBUTT, (void *)&TextDef.ColTxt, 80, 45, 25, 10},
+		{105, 106, 0, 0x0L, LTEXT, (void*)"text:", 10, 83, 25, 8},
+		{106, 0, 0, TOUCHEXIT, EDTEXT, (void*)TextDef.text, 10, 95, 149, 10},
+		{107, 108, 0, 0x0L, RTEXT, (void*)"rotation", 30, 57, 45, 8},
+		{108, 109, 0, 0x0L, EDVAL1, &TextDef.RotBL, 80, 57, 25, 10},
+		{109, 150, 0, 0x0L, LTEXT, (void *)"deg.", 107, 57, 20, 8},
+		{150, 154, 151, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
+		{151, 152, 0, 0x0L, RTEXT, (void *)"line spacing", 30, 69, 45, 8},
+		{152, 153, 0, 0x0L, INCDECVAL1, &lspc, 80, 69, 33, 10},
+		{153, 0, 0, 0x0L, LTEXT, (void *)"%", 115, 69, 20, 8},
+		{154, 105, 0, 0x0L, CHECKBOX, (void*) " moveable", 80, 83, 60, 9},
+		{200, 244, 221, CHECKED | ISPARENT, GROUPBOX, (void*)" font ", 17, 28, 60, 50},
+		{221, 222, 0, TOUCHEXIT, RADIO1, (void*)"Helvetica", 20, 35, 45, 8},
+		{222, 223, 0, TOUCHEXIT, RADIO1, (void*)"Times", 20, 45, 45, 8},
+		{223, 224, 0, TOUCHEXIT, RADIO1, (void*)"Courier", 20, 55, 45, 8},
+		{224, 0, 0, TOUCHEXIT, RADIO1, (void*)"Greek", 20, 65, 45, 8},
+		{244, 105, 245, CHECKED | ISPARENT, GROUPBOX, (void*)" style ", 87, 28, 67, 60},
+		{245, 246, 0, TOUCHEXIT, CHECKBOX, (void*)"bold", 90, 35, 25, 8},
+		{246, 247, 0, TOUCHEXIT, CHECKBOX, (void*)"italic", 90, 45, 25, 8},
+		{247, 248, 0, TOUCHEXIT, CHECKBOX, (void*)"underlined", 90, 55, 25, 8},
+		{248, 249, 0, TOUCHEXIT, CHECKBOX, (void*)"superscript", 90, 65, 25, 8},
+		{249, 0, 0, TOUCHEXIT, CHECKBOX, (void*)"subscript", 90, 75, 25, 8},
+		{300, 301, 0, 0x0L, LTEXT, (void*)"text anchor at", 10, 25, 30, 8},
+		{301, 302, 0, 0x0L, RTEXT, (void*)"x", 5, 37, 15, 8},
+		{302, 303, 0, 0x0L, EDVAL1, &fPos.fx, 22, 37, 45, 10},
+		{303, 304, 0, 0x0L, LTEXT, (void*)0L, 69, 37, 10, 8},
+		{304, 305, 0, 0x0L, RTEXT, (void*)"y", 85, 37, 10, 8},
+		{305, 306, 0, 0x0L, EDVAL1, &fPos.fy, 97, 37, 45, 10},
+		{306, 307, 0, 0x0L, LTEXT, (void*)0L, 144, 37, 10, 8},
+		{307, 308, 0, 0x0L, LTEXT, (void*)"distance from anchor point", 10, 52, 50, 8},
+		{308, 309, 0, 0x0L, RTEXT, (void*)"dx", 5, 64, 15, 8},
+		{309, 310, 0, 0x0L, EDVAL1, &fDist.fx, 22, 64, 45, 10},
+		{310, 311, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 69, 64, 10, 8},
+		{311, 312, 0, 0x0L, RTEXT, (void*)"dy", 85, 64, 10, 8},
+		{312, 313, 0, 0x0L, EDVAL1, &fDist.fy, 97, 64, 45, 10},
+		{313, 314, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 144, 64, 10, 8},
+		{314, 315, 0, 0x0L, LTEXT, (void*)"hot spot (text alignment):", 10, 85, 80, 8},
+		{315, 0, 0, 0x0L, TXTHSP, (void*)&TextDef.Align, 97, 78, 45, 25},
+		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 185, 45, 15, 15},
+		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 185, 60, 15, 15},
+		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 185, 75, 15, 15},
+		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 185, 90, 15, 15}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, i, c_style, c_font;
+	bool RetVal = false, check;
+	double tmp;
+	DWORD undo_flags = 0x0;
+	anyOutput *cdisp = Undo.cdisp;
+	lfPOINT o_pos, o_dist, n_pos, n_dist;
+	TextDEF OldTxtDef, NewTxtDef;
+	Axis *pa;
+	fmtText *fmt = 0L;
+
+	if(!parent) return false;
+	if(parent->Id == GO_MLABEL) {
+		lspc = 100.0 * parent->GetSize(SIZE_LSPC);
+		}
+	if (lspc < 50.0) lspc = 100.0;
+	if(Dlg = new DlgRoot(LabelDlg, data)) {
+		if(parent->Id == GO_MLABEL) Dlg->ShowItem(150, true);
+		Dlg->TextFont(221, FONT_HELVETICA);		Dlg->TextFont(222, FONT_TIMES);
+		Dlg->TextFont(223, FONT_COURIER);		Dlg->TextFont(224, FONT_GREEK);
+		Dlg->TextStyle(205, TXS_BOLD);			Dlg->TextStyle(206, TXS_ITALIC);
+		Dlg->TextStyle(207, TXS_UNDERLINE);		Dlg->TextStyle(207, TXS_SUPER);
+		Dlg->TextStyle(207, TXS_SUB);
+		if(moveable){
+			Dlg->SetCheck(154, 0L, true);
+			}
+		switch(TextDef.Font) {
+		case FONT_TIMES:	Dlg->SetCheck(222, 0L, true);	break;
+		case FONT_COURIER:	Dlg->SetCheck(223, 0L, true);	break;
+		case FONT_GREEK:	Dlg->SetCheck(224, 0L, true);	break;
+		default:			Dlg->SetCheck(221, 0L, true);	break;
+			}
+		if(TextDef.Style & TXS_BOLD) Dlg->SetCheck(245, 0L, true);
+		if(TextDef.Style & TXS_ITALIC) Dlg->SetCheck(246, 0L, true);
+		if(TextDef.Style & TXS_UNDERLINE) Dlg->SetCheck(247, 0L, true);
+		if(TextDef.Style & TXS_SUPER) Dlg->SetCheck(248, 0L, true);
+		if(TextDef.Style & TXS_SUB) Dlg->SetCheck(249, 0L, true);
+		}
+	else return false;
+	if(parent->Id ==  GO_GRAPH || parent->Id == GO_PAGE) Dlg->ShowItem(50, true);
+	switch(flags & 0x03) {
+	case LB_X_DATA:
+		Dlg->SetText(303, "[data]");
+		break;
+	case LB_X_PARENT:
+		Dlg->SetText(303, " ");
+		TmpTxt[0] = 0;
+		if(parent->Id == GO_MLABEL) {
+			if(parent->parent->Id == GO_AXIS) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(axis)");
+			else if(parent->parent->Id == GO_TICK) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(tick)");
+			}
+		else {
+			if(parent->Id == GO_AXIS) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(axis)");
+			else if(parent->Id == GO_TICK) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(tick)");
+			}
+		if(TmpTxt[0]) {
+			Dlg->SetText(302, TmpTxt);			Dlg->Activate(302, false);
+			}
+		break;
+	default:
+		Dlg->SetText(303, Units[defs.cUnits].display);
+		break;
+		}
+	switch(flags & 0x30) {
+	case LB_Y_DATA:
+		Dlg->SetText(306, "[data]");
+		break;
+	case LB_Y_PARENT:
+		Dlg->SetText(306, " ");					TmpTxt[0] = 0;
+		if(parent->Id == GO_MLABEL) {
+			if(parent->parent->Id == GO_AXIS) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(axis)");
+			if(parent->parent->Id == GO_TICK) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(tick)");
+			}
+		else {
+			if(parent->Id == GO_AXIS) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(axis)");
+			if(parent->Id == GO_TICK) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "(tick)");
+			}
+		if(TmpTxt[0]) {
+			Dlg->SetText(305, TmpTxt);			Dlg->Activate(305, false);
+			}
+		break;
+	default:
+		Dlg->SetText(306, Units[defs.cUnits].display);
+		break;
+		}
+	memcpy(&OldTxtDef, &TextDef, sizeof(TextDEF));	OldTxtDef.text = 0L;
+	Dlg->GetValue(101, &OldTxtDef.fSize);			Dlg->GetValue(108, &OldTxtDef.RotBL);
+	memcpy(&NewTxtDef, &OldTxtDef, sizeof(TextDEF));
+	o_pos.fx = fPos.fx;	o_pos.fy = fPos.fy;	o_dist.fx = fDist.fx;	o_dist.fy = fDist.fy;
+	Dlg->GetValue(302, &o_pos.fx);					Dlg->GetValue(305, &o_pos.fy);
+	Dlg->GetValue(309, &o_dist.fx);					Dlg->GetValue(312, &o_dist.fy);
+	n_pos.fx = o_pos.fx;	n_pos.fy = o_pos.fy;	n_dist.fx = o_dist.fx;	n_dist.fy = o_dist.fy;
+	hDlg = CreateDlgWnd("Label properties", 50, 50, isDataLabel ? 470 : 450, 258, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();			res = Dlg->GetResult();
+		switch (res) {
+		case 600:	case 601:	case 602:	case 603:
+			Undo.SetDisp(cdisp);
+			res = ExecDrawOrderButt(parent, this, res);
+			}
+		switch (res) {
+		case 10:
+			Undo.SetDisp(cdisp);
+			parent->Command(CMD_SAVE_LABELS, 0L, 0L);
+			undo_flags |= UNDO_CONTINUE;
+		case 1:
+			Dlg->GetValue(101, &NewTxtDef.fSize);	Dlg->GetColor(104, &NewTxtDef.ColTxt);
+			Dlg->GetValue(108, &NewTxtDef.RotBL);	Dlg->GetInt(315, &NewTxtDef.Align);
+			Dlg->GetValue(302, &n_pos.fx);			Dlg->GetValue(305, &n_pos.fy);
+			Dlg->GetValue(309, &n_dist.fx);			Dlg->GetValue(312, &n_dist.fy);
+			break;
+		case 106:													//text modified
+			if(!(Dlg->GetText(res, TmpTxt, TMP_TXT_SIZE))) TmpTxt[0] = 0;
+			else {
+				if(!fmt) fmt = new fmtText(0L, 0, 0, TmpTxt);
+				else fmt->SetText(0L, TmpTxt, 0L, 0L);
+				}
+			if(fmt && TmpTxt[0] && Dlg->GetInt(106, &i)) {
+				c_style = NewTxtDef.Style;	c_font = NewTxtDef.Font;
+				fmt->StyleAt(i, &NewTxtDef, &c_style, &c_font);
+				Dlg->SetCheck(245, 0L, TXS_BOLD == (c_style & TXS_BOLD));
+				Dlg->SetCheck(246, 0L, TXS_ITALIC == (c_style & TXS_ITALIC));
+				Dlg->SetCheck(247, 0L, TXS_UNDERLINE == (c_style & TXS_UNDERLINE));
+				Dlg->SetCheck(248, 0L, TXS_SUPER == (c_style & TXS_SUPER));
+				Dlg->SetCheck(249, 0L, TXS_SUB == (c_style & TXS_SUB));
+				switch(c_font) {
+				case FONT_HELVETICA:	Dlg->SetCheck(221, 0L, true);	break;
+				case FONT_TIMES:		Dlg->SetCheck(222, 0L, true);	break;
+				case FONT_COURIER:		Dlg->SetCheck(223, 0L, true);	break;
+				case FONT_GREEK:		Dlg->SetCheck(224, 0L, true);	break;
+					}
+				}
+			res = -1;
+			break;
+		case 221:	case 222:	case 223:	case 224:				//fonts
+			switch (res){
+			case 221:		res = FONT_HELVETICA;		break;
+			case 222:		res = FONT_TIMES;			break;
+			case 223:		res = FONT_COURIER;			break;
+			case 224:		res = FONT_GREEK;			break;
+				}
+			if(!Dlg->ItemCmd(106, CMD_SETFONT, &res)) NewTxtDef.Font = res;
+			res = -1;
+			break;
+		case 245:	case 246:	case 247:	case 248:	case 249:	//styles
+			check = Dlg->GetCheck(res);
+			switch (res){
+			case 245:		res = TXS_BOLD;			break;
+			case 246:		res = TXS_ITALIC;		break;
+			case 247:		res = TXS_UNDERLINE;	break;
+			case 248:		res = TXS_SUPER;		break;
+			case 249:		res = TXS_SUB;			break;
+				}
+			if(!check) {
+				res = ~res;
+				if(!Dlg->ItemCmd(106, CMD_SETSTYLE, &res)) NewTxtDef.Style &= res;
+				}
+			else if(!Dlg->ItemCmd(106, CMD_SETSTYLE, &res)) NewTxtDef.Style |= res;
+			res = -1;
+			break;
+			}
+		}while (res < 0);
+	if(res == 1 || res == 10) {
+		Undo.SetDisp(cdisp);
+		if(parent->Id == GO_TICK && parent->parent && parent->parent->Id == GO_AXIS)
+			pa = (Axis*)parent->parent;
+		else if(parent->Id == GO_MLABEL && parent->parent->Id == GO_TICK 
+			&& parent->parent->parent && parent->parent->parent->Id == GO_AXIS)
+			pa = (Axis*)parent->parent->parent;
+		else pa = 0L;
+		if(pa && (cmpTextDEF(&OldTxtDef, &NewTxtDef) || o_dist.fx != n_dist.fx ||
+			o_dist.fy != n_dist.fy)){
+			pa->Command(CMD_SAVE_TICKS, 0L, 0L);			undo_flags |= UNDO_CONTINUE;
+			}
+		i = Dlg->GetCheck(154) ? 1 : 0;
+		if(i != moveable) {
+			Undo.ValInt(parent, &moveable, undo_flags);
+			moveable = i;									undo_flags |= UNDO_CONTINUE;
+			}
+		TmpTxt[0] = 0;		Dlg->GetText(106, TmpTxt, TMP_TXT_SIZE);
+		if(res == 1) undo_flags = CheckNewString(&TextDef.text, TextDef.text, TmpTxt, this, undo_flags);
+		if(cmpTextDEF(&OldTxtDef, &NewTxtDef)){
+			if(NewTxtDef.ColTxt != TextDef.ColTxt) bBGvalid = false;
+			if (pa) pa->Command(CMD_TLB_TXTDEF, &NewTxtDef, 0L);
+			else if(res == 10 || parent->Id == GO_MLABEL) {
+				if(parent->Command(CMD_SETTEXTDEF, &NewTxtDef, 0L))RetVal = true;
+				}
+			else {
+				Undo.TextDef(this, &TextDef, undo_flags);
+				NewTxtDef.text = TextDef.text;	memcpy(&TextDef, &NewTxtDef, sizeof(TextDEF));	
+				undo_flags |= UNDO_CONTINUE;
+				}
+			}
+		if(n_pos.fx != o_pos.fx || n_pos.fy != o_pos.fy) {
+			if(parent->Id == GO_MLABEL) {
+				if(parent->SetSize(SIZE_XPOS, n_pos.fx) || 
+					parent->SetSize(SIZE_YPOS, n_pos.fy)) RetVal = true;
+				}
+			else {
+				Undo.SaveLFP(this, &fPos, undo_flags);		undo_flags |= UNDO_CONTINUE;
+				fPos.fx = n_pos.fx;		fPos.fy = n_pos.fy;
+				}
+			}
+		if(n_dist.fx != o_dist.fx || n_dist.fy != o_dist.fy) {
+			if(pa) {
+				pa->SetSize(SIZE_TLB_XDIST, n_dist.fx);		pa->SetSize(SIZE_TLB_YDIST, n_dist.fy);
+				}
+			else if(res == 10 || parent->Id == GO_MLABEL) {
+				if(n_dist.fx != o_dist.fx && parent->SetSize(SIZE_LB_XDIST, n_dist.fx)) RetVal = true;
+				if(n_dist.fy != o_dist.fy && parent->SetSize(SIZE_LB_YDIST, n_dist.fy)) RetVal = true;
+				}
+			else {
+				Undo.SaveLFP(this, &fDist, undo_flags);		undo_flags |= UNDO_CONTINUE;
+				fDist.fx = n_dist.fx;	fDist.fy = n_dist.fy;
+				}
+			}
+		if(parent->Id == GO_MLABEL && Dlg->GetValue(152, &tmp) && tmp != lspc) {
+			parent->SetSize(SIZE_LSPC, tmp/100.0);	RetVal = true;
+			}
+		if(undo_flags & UNDO_CONTINUE) RetVal = true;
+		}
+	CloseDlgWnd(hDlg);
+	if(fmt) delete(fmt);
+	delete Dlg;
+	return RetVal;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Text frame properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+TextFrame::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 27, 10, "Text"};
+	TabSHEET tab2 = {27, 57, 10, "Frame"};
+	double lspc2, lspc1 = lspc2 = lspc * 100.0;
+	DlgInfo TextFrmDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 130, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 25, 45, 12},
+		{3, 50, 4, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 119, 100},
+		{5, 0, 200, ISPARENT, SHEET, &tab2, 5, 10, 119, 100},
+		{50, 0, 600, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{100, 101, 0, 0x0L, RTEXT, (void*)"size", 10, 33, 45, 8},
+		{101, 102, 0, 0x0L, INCDECVAL1, &TextDef.fSize, 60, 33, 33, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 95, 33, 20, 8},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"color", 10, 45, 45, 8},
+		{104, 150, 0, OWNDIALOG, COLBUTT, (void *)&TextDef.ColTxt, 60, 45, 25, 10},
+		{150, 0, 151, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{151, 152, 0, 0x0L, RTEXT, (void *)"line spacing", 10, 57, 45, 8},
+		{152, 153, 0, 0x0L, INCDECVAL1, &lspc1, 60, 57, 33, 10},
+		{153, 0, 0, 0x0L, LTEXT, (void *)"%", 95, 57, 20, 8},
+		{200, 0, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 20, 30, 90, 50},
+		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 145, 45, 15, 15},
+		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 145, 60, 15, 15},
+		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 145, 75, 15, 15},
+		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 145, 90, 15, 15}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	anyOutput *cdisp = Undo.cdisp;
+	int i, res;
+	bool bRet = false;
+	LineDEF newLine, newFillLine;
+	FillDEF newFill;
+	DWORD undo_flags = 0L;
+	TextDEF OldTxtDef, NewTxtDef;
+
+	if(!parent || !data) return false;
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&Fill, 0);
+	if(!(Dlg = new DlgRoot(TextFrmDlg, data)))return false;
+	memcpy(&OldTxtDef, &TextDef, sizeof(TextDEF));	OldTxtDef.text = 0L;
+	Dlg->GetValue(101, &OldTxtDef.fSize);			memcpy(&NewTxtDef, &OldTxtDef, sizeof(TextDEF));
+	Dlg->GetValue(152, &lspc1);
+	hDlg = CreateDlgWnd("Text Frame", 50, 50, 370, 258, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();			res = Dlg->GetResult();
+		switch (res) {
+		case 1:
+			Dlg->GetValue(101, &NewTxtDef.fSize);	Dlg->GetColor(104, &NewTxtDef.ColTxt);
+			Dlg->GetValue(152, &lspc2);			lspc1 /= 100.0;			lspc2 /= 100.0;
+			break;
+		case 600:	case 601:	case 602:	case 603:
+			Undo.SetDisp(cdisp);
+			res = ExecDrawOrderButt(parent, this, res);
+			break;
+			}
+		}while (res < 0);
+	if(res == 1){						//OK pressed
+		Undo.SetDisp(cdisp);
+		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
+		memcpy(&newFillLine, &FillLine, sizeof(LineDEF));
+		if(newFill.hatch) memcpy(&newFillLine, newFill.hatch, sizeof(LineDEF));
+		if(cmpTextDEF(&OldTxtDef, &NewTxtDef)){
+			Undo.TextDef(this, &TextDef, undo_flags);
+			memcpy(&TextDef, &NewTxtDef, sizeof(TextDEF));	
+			undo_flags |= UNDO_CONTINUE;	TextDef.text = 0L;
+			}
+		undo_flags = CheckNewFloat(&lspc, lspc1, lspc2, parent, undo_flags);
+		if(cmpLineDEF(&Line, &newLine)) {
+			Undo.Line(parent, &Line, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&Line, &newLine, sizeof(LineDEF));
+			}
+		if(newFill.type && cmpLineDEF(&FillLine, &newFillLine)) {
+			Undo.Line(parent, &FillLine, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&FillLine, &newFillLine, sizeof(LineDEF));
+			}
+		if(cmpFillDEF(&Fill, &newFill)) {
+			Undo.Fill(parent, &Fill, undo_flags);		undo_flags |= UNDO_CONTINUE;
+			memcpy(&Fill, &newFill, sizeof(FillDEF));
+			}
+		Fill.hatch = &FillLine;
+		if(undo_flags & UNDO_CONTINUE){
+			bRet = bModified = true;					lines2text();
+			Undo.TextBuffer(this, &csize, &cpos, &text, undo_flags, cdisp);
+			if(lines) {
+				for(i = 0; i < nlines; i++) if(lines[i]) free(lines[i]);
+				free(lines);			lines = 0L;
+				}
+			HideTextCursor();			cur_pos.x = cur_pos.y = 0;
+			}
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// segment properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+segment::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 50, 10, "Size & Color"};
+	TabSHEET tab2 = {50, 90, 10, "Edit"};
+	DlgInfo SegDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to SEGMENT", 130, 10, 65, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 130, 25, 65, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 40, 65, 12},
+		{4, 0, 5, CHECKED | ISPARENT, GROUP, NULL, 138, 40, 55, 12},
+		{5, 6, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 120, 115},
+		{6, 0, 200, ISPARENT, SHEET, &tab2, 5, 10, 120, 115},
+		{100, 109, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 25, 35, 90, 50},
+		{109, 110, 0, 0x0L, RTEXT, (void*)"shift out (explode)", 10, 90, 55, 8},
+		{110, 111, 0, 0x0L, EDVAL1, &shift, 67, 90, 25, 10},
+		{111, 112, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 94, 90, 20, 8},
+		{112, 0, 0, 0x0L, CHECKBOX, (void*)"enable mouse drag (moveable)", 12, 107, 100, 8},
+		{200, 201, 0, 0x0L, RTEXT, (void*)"center x", 10, 30, 40, 8},
+		{201, 202, 0, 0x0L, EDVAL1, &fCent.fx, 55, 30, 40, 10},
+		{202, 203, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 98, 30, 20, 8},
+		{203, 204, 0, 0x0L, RTEXT, (void*)"y", 10, 42, 40, 8},
+		{204, 205, 0, 0x0L, EDVAL1, &fCent.fy, 55, 42, 40, 10},
+		{205, 206, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 98, 42, 20, 8},
+		{206, 207, 0, 0x0L, RTEXT, (void*)"start angle", 10, 57, 40, 8},
+		{207, 208, 0, 0x0L, EDVAL1, &angle1, 55, 57, 40, 10},
+		{208, 209, 0, 0x0L, LTEXT, (void*)"deg.", 98, 57, 20, 8},
+		{209, 210, 0, 0x0L, RTEXT, (void*)"stop", 10, 69, 40, 8},
+		{210, 211, 0, 0x0L, EDVAL1, &angle2, 55, 69, 40, 10},
+		{211, 212, 0, 0x0L, LTEXT, (void*)"deg.", 98, 69, 20, 8},
+		{212, 213, 0, 0x0L, RTEXT, (void*)"outer radius", 10, 84, 40, 8},
+		{213, 214, 0, 0x0L, EDVAL1, &radius2, 55, 84, 40, 10},
+		{214, 215, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 98, 84, 20, 8},
+		{215, 216, 0, 0x0L, RTEXT, (void*)"inner", 10, 96, 40, 8},
+		{216, 217, 0, 0x0L, EDVAL1, &radius1, 55, 96, 40, 10},
+		{217, 0, 0, LASTOBJ, LTEXT, (void *) Units[defs.cUnits].display, 98, 96, 20, 8}};
+
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, new_mov;
+	double old_r1, old_r2, old_a1, old_a2, old_shift;
+	double new_r1, new_r2, new_a1, new_a2, new_shift;
+	lfPOINT old_cent, new_cent;
+	bool bRet = false;
+	LineDEF newLine, newFillLine;
+	FillDEF newFill;
+	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+
+	if(!parent) return false;
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&segLine, 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&segFill, 0);
+	Dlg = new DlgRoot(SegDlg, data);
+	Dlg->GetValue(216, &old_r1);		new_r1 = old_r1;
+	Dlg->GetValue(213, &old_r2);		new_r2 = old_r2;
+	Dlg->GetValue(201, &old_cent.fx);	new_cent.fx = old_cent.fx;
+	Dlg->GetValue(204, &old_cent.fy);	new_cent.fy = old_cent.fy;
+	Dlg->GetValue(207, &old_a1);		new_a1 = old_a1;
+	Dlg->GetValue(210, &old_a2);		new_a2 = old_a2;
+	Dlg->GetValue(110, &old_shift);		new_shift = old_shift;
+	if(moveable) Dlg->SetCheck(112, 0L, true);
+	hDlg = CreateDlgWnd("segment properties", 50, 50, 410, 290, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);
+			OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+			OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
+			memcpy(&newFillLine, &segFillLine, sizeof(LineDEF));
+			if(newFill.hatch) memcpy(&newFillLine, newFill.hatch, sizeof(LineDEF));
+			Dlg->GetValue(201, &new_cent.fx);			Dlg->GetValue(204, &new_cent.fy);
+			Dlg->GetValue(207, &new_a1);				Dlg->GetValue(210, &new_a2);
+			Dlg->GetValue(216, &new_r1);				Dlg->GetValue(213, &new_r2);
+			Dlg->GetValue(110, &new_shift);
+			new_mov = Dlg->GetCheck(112) ? 1 : 0;
+			break;
+			}
+		}while (res < 0);
+	switch (res) {
+	case 1:				//new setting for current segment only
+		undo_flags = CheckNewLFPoint(&fCent, &old_cent, &new_cent, parent, undo_flags);
+		undo_flags = CheckNewFloat(&angle1, old_a1, new_a1, parent, undo_flags);
+		undo_flags = CheckNewFloat(&angle2, old_a2, new_a2, parent, undo_flags);
+		undo_flags = CheckNewFloat(&radius1, old_r1, new_r1, parent, undo_flags);
+		undo_flags = CheckNewFloat(&radius2, old_r2, new_r2, parent, undo_flags);
+		undo_flags = CheckNewFloat(&shift, old_shift, new_shift, parent, undo_flags);
+		if(cmpLineDEF(&segLine, &newLine)) {
+			Undo.Line(parent, &segLine, undo_flags);		undo_flags |= UNDO_CONTINUE;
+			memcpy(&segLine, &newLine, sizeof(LineDEF));
+			}
+		if(newFill.type && cmpLineDEF(&segFillLine, &newFillLine)) {
+			Undo.Line(parent, &segFillLine, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&segFillLine, &newFillLine, sizeof(LineDEF));
+			}
+		if(cmpFillDEF(&segFill, &newFill)) {
+			Undo.Fill(parent, &segFill, undo_flags);		undo_flags |= UNDO_CONTINUE;
+			memcpy(&segFill, &newFill, sizeof(FillDEF));
+			}
+		segFill.hatch = &segFillLine;
+		undo_flags = CheckNewInt(&moveable, moveable, new_mov, parent, undo_flags);
+		bRet = bModified = ((undo_flags & UNDO_CONTINUE) == UNDO_CONTINUE);
+		break;
+	case 2:				//new settings to all segments of chart
+		if(new_cent.fx != old_cent.fx || new_cent.fy != old_cent.fy || new_r1 != old_r1 || 
+			new_r2 != old_r2 || new_shift != old_shift || cmpLineDEF(&segLine, &newLine) ||
+			(newFill.type && cmpLineDEF(&segFillLine, &newFillLine)) || 
+			cmpFillDEF(&segFill, &newFill) || new_mov != moveable) {
+			parent->Command(CMD_SAVE_SYMBOLS, 0L, 0L);
+			if(new_cent.fx != old_cent.fx || new_cent.fy != old_cent.fy) {
+				parent->SetSize(SIZE_XPOS, new_cent.fx);	parent->SetSize(SIZE_YPOS, new_cent.fy);
+				}
+			if(new_r1 != old_r1 || new_r2 != old_r2) {
+				parent->SetSize(SIZE_RADIUS1, new_r1);		parent->SetSize(SIZE_RADIUS2, new_r2);
+				}
+			if(new_shift != old_shift) parent->Command(CMD_SHIFT_OUT, (void*)&new_shift, 0L);
+			if(cmpLineDEF(&segLine, &newLine))parent->Command(CMD_SEG_LINE, (void*)&newLine, 0L);
+			if((newFill.type && cmpLineDEF(&segFillLine, &newFillLine)) || 
+				cmpFillDEF(&segFill, &newFill)) parent->Command(CMD_SEG_FILL, (void*)&newFill, 0L);
+			if(new_mov != moveable) parent->Command(CMD_SEG_MOVEABLE, (void *)&new_mov, 0L);
+			bRet = true;
+			}
+		break;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// polyline properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+polyline::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 40, 10, "Line"};
+	DlgInfo LineDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 150, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 150, 25, 45, 12},
+		{3, 0, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{4, 50, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 139, 130},
+		{50, 0, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
+		{100, 101, 0, NOSELECT, ODBUTTON, (void*)OD_linedef, 10, 30, 130, 90},
+		{101, 0, 0, 0x0L, CHECKBOX, (void*)"use this style as default", 10, 125, 60, 9},
+		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 165, 60, 15, 15},
+		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 165, 75, 15, 15},
+		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 165, 90, 15, 15},
+		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 165, 105, 15, 15}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, undo_level = *Undo.pcb;
+	anyOutput *cdisp = Undo.cdisp;
+	bool bRet = false;
+	LineDEF newLine;
+
+	if(!parent) return false;
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&pgLine, 0);
+	if(!(Dlg = new DlgRoot(LineDlg, data)))return false;
+	if(parent->Id ==  GO_GRAPH || parent->Id == GO_PAGE) Dlg->ShowItem(50, true);
+	hDlg = CreateDlgWnd("line properties", 50, 50, 410, 314, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();			res = Dlg->GetResult();
+		switch (res) {
+		case 600:	case 601:	case 602:	case 603:
+			Undo.SetDisp(cdisp);
+			res = ExecDrawOrderButt(parent, this, res);	break;
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);	break;
+			}
+	}while (res < 0);
+	switch(res) {
+	case 1:							//OK pressed
+		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+		if(cmpLineDEF(&pgLine, &newLine)) {
+			Undo.Line(this, &pgLine, 0L);
+			memcpy(&pgLine, &newLine, sizeof(LineDEF));
+			bModified = true;
+			}
+		if(Dlg->GetCheck(101)) defs.plLineDEF(&pgLine);
+		bRet = true;
+		break;
+	case 2:							//Cancel
+		if(*Undo.pcb > undo_level) {	//restore plot order
+			while(*Undo.pcb > undo_level)	Undo.Restore(false, 0L);
+			bRet = true;
+			}
+		break;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// polygon properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+polygon::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 40, 10, "Polygon"};
+	DlgInfo PolygDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 102, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 102, 25, 45, 12},
+		{3, 50, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 90, 85},
+		{50, 0, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
+		{100, 101, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 8, 30, 90, 50},
+		{101, 0, 0, 0x0L, CHECKBOX, (void*)"use this style as default", 10, 82, 60, 9},
+		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 107, 52, 15, 15},
+		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 127, 52, 15, 15},
+		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 127, 67, 15, 15},
+		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 107, 67, 15, 15}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	LineDEF newLine, newFillLine;
+	FillDEF newFill;
+	DWORD undo_flags = 0L;
+	int res, undo_level = *Undo.pcb;
+	anyOutput *cdisp = Undo.cdisp;
+	bool bRet = false;
+
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&pgLine, 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&pgFill, 0);
+	Dlg = new DlgRoot(PolygDlg, data);
+	if(parent->Id ==  GO_GRAPH || parent->Id == GO_PAGE) Dlg->ShowItem(50, true);
+	hDlg = CreateDlgWnd("polygon properties", 50, 50, 310, 224, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();			res = Dlg->GetResult();
+		switch(res) {
+		case 600:	case 601:	case 602:	case 603:
+			Undo.SetDisp(cdisp);
+			res = ExecDrawOrderButt(parent, this, res);		break;
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);	break;
+			}
+		}while (res < 0);
+	switch (res) {
+	case 1:						//OK pressed
+		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
+		memcpy(&newFillLine, &pgFillLine, sizeof(LineDEF));
+		if(newFill.hatch) memcpy(&newFillLine, newFill.hatch, sizeof(LineDEF));
+		if(cmpLineDEF(&pgLine, &newLine)) {
+			Undo.Line(this, &pgLine, undo_flags);		undo_flags |= UNDO_CONTINUE;
+			memcpy(&pgLine, &newLine, sizeof(LineDEF));
+			}
+		if(newFill.type && cmpLineDEF(&pgFillLine, &newFillLine)) {
+			Undo.Line(this, &pgFillLine, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&pgFillLine, &newFillLine, sizeof(LineDEF));
+			}
+		if(cmpFillDEF(&pgFill, &newFill)) {
+			Undo.Fill(this, &pgFill, undo_flags);		undo_flags |= UNDO_CONTINUE;
+			memcpy(&pgFill, &newFill, sizeof(FillDEF));
+			}
+		pgFill.hatch = &pgFillLine;
+		if(undo_flags & UNDO_CONTINUE) bModified = true;
+		if(Dlg->GetCheck(101)){
+			defs.pgLineDEF(&pgLine);			defs.pgFillDEF(&pgFill);
+			}
+		bRet = true;
+		break;
+	case 2:							//Cancel
+		if(*Undo.pcb > undo_level) {	//restore plot order
+			while(*Undo.pcb > undo_level)	Undo.Restore(false, 0L);
+			bRet = true;
+			}
+		break;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// rectangle, round rectangle and ellipse properties dialogs
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+rectangle::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 40, 10, type == 1 ? (char*)"Ellipse" :(char*)"Rectangle"};
+	TabSHEET tab2 = {40, 63, 10, "Edit"};
+	DlgInfo RecDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 112, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 112, 25, 45, 12},
+		{3, 0, 10, CHECKED | ISPARENT, GROUP, 0L, 0, 0, 0, 0},
+		{10, 11, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 100, 97},
+		{11, 50, 200, ISPARENT, SHEET, &tab2, 5, 10, 100, 97},
+		{50, 0, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
+		{100, 101, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 8, 30, 90, 50},
+		{101, 120, 110, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{110, 111, 0, 0x0L, RTEXT, (void*)"edge radius", 0, 79, 48, 8},
+		{111, 112, 0, 0x0L, EDVAL1, &rad, 50, 79, 25, 10},
+		{112, 0, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 77, 79, 10, 8},
+		{120, 0, 0, 0x0L, CHECKBOX, (void*)"use this style as default", 10, 92, 60, 9},
+		{200, 201, 0, 0x0L, RTEXT, (void*)"top left x", 5, 30, 40, 8},
+		{201, 202, 0, 0x0L, EDVAL1, &fp1.fx, 47, 30, 38, 10},
+		{202, 203, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 87, 30, 10, 8},
+		{203, 204, 0, 0x0L, RTEXT, (void*)"y", 5, 42, 40, 8},
+		{204, 205, 0, 0x0L, EDVAL1, &fp1.fy, 47, 42, 38, 10},
+		{205, 206, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 87, 42, 10, 8},
+		{206, 207, 0, 0x0L, RTEXT, (void*)"lower right x", 5, 55, 40, 8},
+		{207, 208, 0, 0x0L, EDVAL1, &fp2.fx, 47, 55, 38, 10},
+		{208, 209, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 87, 55, 10, 8},
+		{209, 210, 0, 0x0L, RTEXT, (void*)"y", 5, 67, 40, 8},
+		{210, 211, 0, 0x0L, EDVAL1, &fp2.fy, 47, 67, 38, 10},
+		{211, 0, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 87, 67, 10, 8},
+		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 115, 64, 15, 15},
+		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 139, 64, 15, 15},
+		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 139, 79, 15, 15},
+		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 115, 79, 15, 15}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, undo_level = *Undo.pcb;
+	lfPOINT old_fp1, new_fp1, old_fp2, new_fp2;
+	LineDEF old_Line, new_Line, old_FillLine, new_FillLine;
+	FillDEF old_Fill, new_Fill;
+	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+	bool bRet = false;
+
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&Fill, 0);
+	if(!(Dlg = new DlgRoot(RecDlg, data)))return false;
+	Dlg->GetValue(201, &old_fp1.fx);		Dlg->GetValue(204, &old_fp1.fy);
+	Dlg->GetValue(207, &old_fp2.fx);		Dlg->GetValue(210, &old_fp2.fy);
+	if(type != 2) Dlg->ShowItem(101, false);
+	if(parent->Id ==  GO_GRAPH || parent->Id == GO_PAGE) Dlg->ShowItem(50, true);
+	OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&old_Line, 0);
+	OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&old_Fill, 0);
+	if(old_Fill.hatch) memcpy(&old_FillLine, old_Fill.hatch, sizeof(LineDEF));
+	old_Fill.hatch = &old_FillLine;
+	hDlg = CreateDlgWnd(type == 1 ? (char*)"ellipse properties":
+		type == 2 ? (char*)"rounded rectangle" : (char*)"rectangle properties", 
+		50, 50, 330, 248, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();			res = Dlg->GetResult();
+		switch(res) {
+		case 600:	case 601:	case 602:	case 603:
+			Undo.SetDisp(cdisp);
+			res = ExecDrawOrderButt(parent, this, res);		break;
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);	break;
+			}
+		}while (res < 0);
+	switch (res) {
+	case 1:						//OK pressed
+		Dlg->GetValue(201, &new_fp1.fx);		Dlg->GetValue(204, &new_fp1.fy);
+		Dlg->GetValue(207, &new_fp2.fx);		Dlg->GetValue(210, &new_fp2.fy);
+		undo_flags = CheckNewLFPoint(&fp1, &old_fp1, &new_fp1, this, undo_flags);
+		undo_flags = CheckNewLFPoint(&fp2, &old_fp2, &new_fp2, this, undo_flags);
+		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&new_Line, 0);
+		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&new_Fill, 0);
+		if(new_Fill.hatch) memcpy(&new_FillLine, new_Fill.hatch, sizeof(LineDEF));
+		new_Fill.hatch = &new_FillLine;
+		if(cmpLineDEF(&old_Line, &new_Line)) {
+			Undo.Line(this, &Line, undo_flags);
+			undo_flags |= UNDO_CONTINUE;
+			memcpy(&Line, &new_Line, sizeof(LineDEF));
+			}
+		if(new_Fill.type && cmpLineDEF(&old_FillLine, &new_FillLine)) {
+			Undo.Line(this, &FillLine, undo_flags);
+			undo_flags |= UNDO_CONTINUE;
+			memcpy(&FillLine, &new_FillLine, sizeof(LineDEF));
+			}
+		if(cmpFillDEF(&Fill, &new_Fill)) {
+			Undo.Fill(this, &Fill, undo_flags);
+			undo_flags |= UNDO_CONTINUE;
+			memcpy(&Fill, &new_Fill, sizeof(FillDEF));
+			}
+		Fill.hatch = &FillLine;
+		if(type == 2) Dlg->GetValue(111, &rad);
+		else rad = 0.0;
+		if(Dlg->GetCheck(120)){
+			defs.pgLineDEF(&Line);			defs.pgFillDEF(&Fill);
+			if(type == 2)defs.rrectRad(rad);
+			}
+		if(undo_flags) bModified = true;
+		bRet = true;
+		break;
+	case 2:							//Cancel
+		if(*Undo.pcb > undo_level) {	//restore plot order
+			while(*Undo.pcb > undo_level)	Undo.Restore(false, 0L);
+			bRet = true;
+			}
+		break;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Create a bar chart: note this is a PlotScatt function
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+PlotScatt::CreateBarChart()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 55, 10, "Details"};
+	double start = 1.0, step = 1.0, bw = 60.0;
+	DlgInfo BarDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 130, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 25, 45, 12},
+		{3, 0, 4, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
+		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 120, 110},
+		{5, 10, 200, ISPARENT, SHEET, &tab2, 5, 10, 120, 110},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"spread sheet range for values", 10, 25, 60, 8},
+		{101, 102, 0, 0x0L, RANGEINPUT, TmpTxt, 10, 38, 110, 10},
+		{102, 0, 110, ISPARENT | CHECKED, GROUPBOX, (void*)" style ", 10, 55, 110, 61}, 
+		{110, 0, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 25, 62, 90, 50},
+		{200, 201, 0, 0x0L, RTEXT, (void*)"start value", 10, 35, 38, 8},
+		{201, 202, 0, 0x0L, EDVAL1, (void*)&start, 58, 35, 35, 10},
+		{202, 203, 0, 0x0L, RTEXT, (void*)"step value", 10, 50, 38, 8},
+		{203, 204, 0, 0x0L, EDVAL1, (void*)&step, 58, 50, 35, 10},
+		{204, 205, 0, 0x0L, RTEXT, (void*)"bar width", 10, 65, 38, 8},
+		{205, 206, 0, 0x0L, EDVAL1, (void*)&bw, 58, 65, 35, 10},
+		{206, 0, 0, LASTOBJ, LTEXT, (void*)"%", 95, 65, 8, 8}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	bool bRet = false;
+	int k, l, n, ic, res;
+	double x, y;
+	AccRange *rY = 0L;
+	LineDEF Line;
+	FillDEF Fill; 
+
+	if(!parent || !data) return false;
+	memcpy(&Line, defs.GetOutLine(), sizeof(LineDEF));
+	memcpy(&Fill, defs.GetFill(), sizeof(FillDEF));
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&Fill, 0);
+	UseRangeMark(data, 1, TmpTxt);
+	if(!(Dlg = new DlgRoot(BarDlg, data)))return false;
+	hDlg = CreateDlgWnd("Simple Bar Chart", 50, 50, 370, 280, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 0:
+			if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case 1:
+			if(rY) delete rY;
+			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)){
+				rY = new AccRange(TmpTxt);
+				yRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
+				}
+			else {
+				rY = 0L;		yRange = 0L;
+				}
+			if(!(n = rY ? rY->CountItems() : 0)) {
+				res = -1;
+				ErrorBox("Data range not valid.");
+				}
+			break;
+			}
+	} while(res < 0);
+	if(res == 1 && n && rY) {
+		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&Fill, 0);
+		Command(CMD_FLUSH, 0L, 0L);
+		nPoints = n;
+		Bars = (Bar**)calloc(nPoints, sizeof(Bar*));
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		rY->GetFirst(&k, &l);		rY->GetNext(&k, &l);
+		Dlg->GetValue(201, &start);	Dlg->GetValue(203, &step);
+		if(step < 0.001) step = 1.0;
+		Dlg->GetValue(205, &bw);
+		if(bw < 5) bw = 60;
+		ic = 0;
+		x = start;
+		if(Bars) do {
+			if(data->GetValue(l, k, &y)){
+				Bars[ic] = new Bar(this, data, x, y, BAR_VERTB | BAR_RELWIDTH, -1, -1, k, l);
+				CheckBounds(x, y);
+				x += step;
+				ic++;
+				}
+			}while(rY->GetNext(&k, &l));
+		if(ic){
+			bRet = true;
+			Command(CMD_BAR_FILL, &Fill, 0L);
+			SetColor(COL_BAR_LINE, Line.color);
+			SetSize(SIZE_BAR_LINE, Line.width);
+			SetSize(SIZE_BAR, bw);
+			BarDist.fx = step;
+			}
+		}
+	if(rY) delete rY;
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Scatter plot properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+PlotScatt::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 22, 10, "Data"};
+	TabSHEET tab2 = {22, 50, 10, "Layout"};
+	TabSHEET tab3 = {50, 88, 10, "Error Bars"};
+	TabSHEET tab4 = {88, 131, 10, "Data Labels"};
+	char text1[100], text2[100], text3[100], text4[100];
+	int icon = ICO_INFO;
+	DlgInfo XYDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 140, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 140, 25, 45, 12},
+		{3, 0, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{4, 5, 100, TOUCHEXIT | ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 131, 100},
+		{5, 6, 200, TOUCHEXIT | ISPARENT, SHEET, &tab2, 5, 10, 131, 100},
+		{6, 7, 300, TOUCHEXIT | ISPARENT, SHEET, &tab3, 5, 10, 131, 100},
+		{7, 10, 400, TOUCHEXIT | ISPARENT, SHEET, &tab4, 5, 10, 131, 100},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"range for X Data", 10, 30, 60, 8},
+		{101, 102, 0, 0x0L, RANGEINPUT, text1, 20, 40, 100, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 55, 60, 8},
+		{103, 104, 0, 0x0L, RANGEINPUT, text2, 20, 65, 100, 10},
+		{104, 105, 0, 0x0L, ICON, (void*)&icon, 10, 85, 10, 10},
+		{105, 106, 0, 0x0L, LTEXT, (void*)"Valid ranges include e.g. \'a1:g13\'", 30, 85, 30, 6},
+		{106, 107, 0, 0x0L, LTEXT, (void*)"or \'b4:j4\' if data are available.", 30, 91, 30, 6},
+		{107, 0, 0, 0x0L, LTEXT, (void*)"Separate multiple ranges by \' ; \'.", 30, 97, 30, 6},
+		{200, 201, 0, 0x0L, CHECKBOX, (void*)" symbols", 25, 30, 60, 8},
+		{201, 202, 250, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{202, 203, 255, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{203, 204, 0, 0x0L, CHECKBOX, (void*)" arrows", 25, 80, 60, 8},
+		{204, 0, 0, 0x0L, CHECKBOX, (void*)" drop lines", 25, 90, 60, 8},
+		{250, 251, 0, ISRADIO, CHECKBOX, (void*)" line", 25, 40, 60, 8},
+		{251, 0, 0, ISRADIO, CHECKBOX, (void*)" polygon", 25, 70, 60, 8},
+		{255, 256, 0, ISRADIO, CHECKBOX, (void*)" vertical bars", 25, 50, 60, 8},
+		{256, 0, 0, ISRADIO, CHECKBOX, (void*)" horizontal bars", 25, 60, 60, 8},
+		{300, 301, 500, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{301, 302, 0, 0x0L, CHECKBOX, (void*)"draw error bars", 15, 28, 50, 8},
+		{302, 303, 0, 0x0L, LTEXT, (void*)"style:", 35, 40, 20, 8},
+		{303, 304, 0, 0x0L, LTEXT, (void*)"range for error data:", 15, 82, 60, 8},
+		{304, 0, 0, 0x0L, RANGEINPUT, text3, 20, 93, 100, 10},
+		{400, 401, 0, 0x0L, CHECKBOX, (void*)"add labels to data points", 15, 28, 50, 8},
+		{401, 402, 0, 0x0L, LTEXT, (void*)"spread sheet range for labels:", 15, 40, 60, 8},
+		{402, 0, 0, 0x0L, RANGEINPUT, text4, 20, 51, 100, 10},
+		{500, 501, 0, TOUCHEXIT|CHECKED|ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl),60,40,20,20},
+		{501, 502, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 80, 40, 20, 20},
+		{502, 503, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 100, 40, 20, 20},
+		{503, 504, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 60, 60, 20, 20},
+		{504, 505, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 80, 60, 20, 20},
+		{505, 0, 0, LASTOBJ | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 100, 60, 20, 20}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int c, i, j, k, l, i1, j1, k1, l1, m, n, o, p, ic;
+	int ErrType = 0, res, BarType, nVals, nTxt, nTime;
+	double x, y, e;
+	lfPOINT fp1, fp2;
+	bool bRet = false, bLayout = false, bContinue = false, bValid;
+	anyResult xRes, yRes;
+	TextDEF lbdef = {defs.Color(COL_TEXT), defs.Color(COL_BG), DefSize(SIZE_TEXT), 0.0f, 0.0f, 0,
+		TXA_HLEFT | TXA_VBOTTOM, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, TmpTxt};
+	AccRange *rX, *rY, *rE, *rL;
+
+	if(!parent || !data) return false;
+	if(Id == GO_BARCHART) return CreateBarChart();
+	UseRangeMark(data, 1, text1, text2, text3, text4);
+	rX = rY = rE = rL = 0L;
+	if(!(Dlg = new DlgRoot(XYDlg, data)))return false;
+#ifdef _WINDOWS
+	for(i = 104; i <= 107; i++) Dlg->TextSize(i, 12);
+#else
+	for(i = 104; i <= 107; i++) Dlg->TextSize(i, 10);
+#endif
+	if(DefSel & 0x01)Dlg->SetCheck(200, 0L, true);		if(DefSel & 0x02)Dlg->SetCheck(250, 0L, true);
+	if(DefSel & 0x04)Dlg->SetCheck(255, 0L, true);		if(DefSel & 0x08)Dlg->SetCheck(256, 0L, true);
+	hDlg = CreateDlgWnd("XY Plot properties", 50, 50, 388, 260, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:								// focus lost
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 500:	case 501:	case 502:
+		case 503:	case 504:	case 505:
+			ErrType = res-500;
+			Dlg->SetCheck(301, 0L, true);
+			res = -1;
+			break;
+		case 4:								// the data tab sheet
+			Dlg->Activate(101, true);	res = -1;				break;
+		case 5:								// the layout tab sheet
+			bLayout = true;				res = -1;				break;
+		case 6:								// the error tab sheet
+			Dlg->Activate(304, true);	res = -1;				break;
+		case 7:								// the label tab sheet
+			Dlg->Activate(402, true);	res = -1;				break;
+		case 1:								// OK
+			if(rX) delete rX;	if(rY) delete rY; if(rE) delete rE;
+			rX = rY = rE = 0L;						// check x-range
+			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) rX = new AccRange(TmpTxt);
+			if(!(n = rX ? rX->CountItems() : 0)) {
+				Dlg->SetCheck(4, 0L, true);
+				res = -1;
+				bContinue = true;
+				ErrorBox("X-range not specified\nor not valid.");
+				}
+			else {							// check y-range
+				if(Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE)) rY = new AccRange(TmpTxt);
+				if(n != (rY ? rY->CountItems() : 0)) {
+					Dlg->SetCheck(4, 0L, true);
+					res = -1;
+					bContinue = true;
+					ErrorBox("Y-range missing\nor not valid.\n"
+						"Size must match X-range.");
+					}
+				}
+			//check for error bar
+			if(res >0 && Dlg->GetCheck(301)) {
+				if(Dlg->GetText(304, TmpTxt, TMP_TXT_SIZE)) rE = new AccRange(TmpTxt);
+				if(n != (rE ? rE->CountItems() : 0)) {
+					Dlg->SetCheck(6, 0L, true);
+					res = -1;
+					bContinue = true;
+					ErrorBox("Range for errors missing\nor not valid.\n"
+						"Size must match X- and Y-range.");
+					if(rE) delete (rE);
+					rE = 0L;
+					}
+				}
+			//check for data labels
+			if(res >0 && Dlg->GetCheck(400)) {
+				if(Dlg->GetText(402, TmpTxt, TMP_TXT_SIZE)) rL = new AccRange(TmpTxt);
+				if(n != (rL ? rL->CountItems() : 0)) {
+					Dlg->SetCheck(7, 0L, true);
+					res = -1;
+					bContinue = true;
+					ErrorBox("Range for labels missing\nor not valid.\n"
+						"Size must match X- and Y-range.");
+					if(rL) delete (rL);
+					rL = 0L;
+					}
+				}
+			//check if something left to do
+			if(res > 0 && !rE && !rL && !Dlg->GetCheck(200) && !Dlg->GetCheck(250) && 
+				!Dlg->GetCheck(251) && !Dlg->GetCheck(255) && !Dlg->GetCheck(256) && 
+				!Dlg->GetCheck(203)) {
+				Dlg->SetCheck(5, 0L, true);
+				res = -1;
+				bContinue = true;
+				ErrorBox("Nothing to do!\nSelect at least one item.");
+				}
+			//the layout menu must have been visited
+			if(res > 0 && !bLayout){
+				Dlg->SetCheck(5, 0L, bLayout = true);
+				res = -1;
+				}
+			break;
+			}
+		}while (res <0);
+	if(res == 1 && n && rX && rY){				//OK pressed
+		Command(CMD_FLUSH, 0L, 0L);
+		nPoints = n;
+		if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) xRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
+		if(Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE)) yRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		data_desc = rY->RangeDesc(data, 1);
+		//analyse data types
+		if(rX->DataTypes(data, &nVals, &nTxt, &nTime)){
+			if(!nVals && nTxt > 1 && nTxt > nTime) x_tv = new TextValue();
+			else if(!nVals && nTime > 1 && nTime > nTxt) x_dtype = ET_DATETIME;
+			}
+		if(rY->DataTypes(data, &nVals, &nTxt, &nTime)){
+			if(!nVals && nTxt > 1 && nTxt > nTime) y_tv = new TextValue();
+			else if(!nVals && nTime > 1 && nTime > nTxt) y_dtype = ET_DATETIME;
+			}
+		//Create graphic objects
+		rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
+		rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
+		i1 = i;	j1 = j;	k1 = k;	l1 = l;
+		ic = c = 0;
+		if(Dlg->GetCheck(200)) Symbols = (Symbol**)calloc(nPoints, sizeof(Symbol*));
+		if((BarType = Dlg->GetCheck(255) ? BAR_VERTB : Dlg->GetCheck(256) ? BAR_HORL : 0))
+			Bars = (Bar**)calloc(nPoints, sizeof(Bar*));
+		BarType |= BAR_RELWIDTH;
+		if(Dlg->GetCheck(204)) DropLines = (DropLine**)calloc(nPoints, sizeof(DropLine*));
+		if(Dlg->GetCheck(203)) Arrows = (Arrow**)calloc(nPoints, sizeof(Arrow*));
+		if(Dlg->GetCheck(301) && rE) {					//error bars ?
+			Errors = (ErrorBar**)calloc(nPoints, sizeof(ErrorBar*));
+			if(Dlg->GetText(304, TmpTxt, TMP_TXT_SIZE)){
+				ErrRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
+				rE->GetFirst(&m, &n);	rE->GetNext(&m, &n);
+				}
+			else {
+				rE = 0L;		ErrRange = 0L;
+				}
+			}
+		if(Dlg->GetCheck(400) && rL) {					//labels ?
+			Labels = (Label**)calloc(nPoints, sizeof(Label*));
+			if(Dlg->GetText(402, TmpTxt, TMP_TXT_SIZE)) LbRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
+			rL->GetFirst(&o, &p);	rL->GetNext(&o, &p);
+			}
+		if(Dlg->GetCheck(250) && nPoints >1) TheLine = new DataLine(this, data, xRange, yRange); 
+		else if(Dlg->GetCheck(251) && nPoints >2) TheLine = new DataPolygon(this, data, xRange, yRange); 
+		do {
+			bValid = false;
+			if(data->GetResult(&xRes, j, i, false) && data->GetResult(&yRes, l, k, false)) {
+				bValid = true;
+				if(x_tv) {
+					if(xRes.type == ET_TEXT) x = x_tv->GetValue(xRes.text);
+					else bValid = false;
+					}
+				else if(x_dtype == ET_DATETIME) {
+					if(xRes.type == ET_DATE || xRes.type == ET_TIME  || xRes.type == ET_DATETIME) x = xRes.value;
+					else bValid = false;
+					}
+				else {
+					if(xRes.type == ET_VALUE) x = xRes.value;
+					else bValid = false;
+					}
+				if(y_tv) {
+					if(yRes.type == ET_TEXT) y = y_tv->GetValue(yRes.text);
+					else bValid = false;
+					}
+				else if(y_dtype == ET_DATETIME) {
+					if(yRes.type == ET_DATE || yRes.type == ET_TIME  || yRes.type == ET_DATETIME) y = yRes.value;
+					else bValid = false;
+					}
+				else {
+					if(yRes.type == ET_VALUE) y = yRes.value;
+					else bValid = false;
+					}
+				}
+			if(bValid){
+				if(Symbols && (Symbols[ic] = new Symbol(this, data, x, y, DefSym, i, j, k, l))){
+					Symbols[ic]->idx = c;
+					}
+				if(Bars)Bars[ic] = new Bar(this, data, x, y, BarType, i, j, k, l);
+				if(DropLines) DropLines[ic] = new DropLine(this, data, x, y, 
+					DL_YAXIS | DL_XAXIS, i, j, k, l);
+				if(Arrows) {
+					if(ic){
+						fp1.fx = fp2.fx;	fp1.fy = fp2.fy;
+						}
+					else {
+						fp1.fx = x;			fp1.fy = y;
+						}
+					fp2.fx = x;			fp2.fy = y;
+					Arrows[ic] = new Arrow(this, data, ic ? fp1 : fp2, fp2, ARROW_LINE,
+						i1, j1, k1, l1, i, j, k, l);
+					//the first arrow has zero length
+					//all other arrows conncect to the following point
+					i1 = i;	j1 = j;	k1 = k;	l1 = l;
+					}
+				if(Labels && rL) {
+					if(data->GetText(p, o, TmpTxt, TMP_TXT_SIZE)) 
+						Labels[ic] = new Label(this, data, x, y, &lbdef, 
+							LB_X_DATA | LB_Y_DATA, i, j, k, l, o, p);
+					rL->GetNext(&o, &p);
+					}
+				if(Errors && rE){
+					if(data->GetValue(n, m, &e))
+						Errors[ic]= new ErrorBar(this, data, x, y, e, ErrType,
+						i, j, k, l, m, n);
+					rE->GetNext(&m, &n);
+					}
+				else CheckBounds(x, y);
+				ic++;
+				}
+			else {
+				if(Labels && rL) rL->GetNext(&o, &p);
+				if(Errors && rE) rE->GetNext(&m, &n);
+				}
+			c++;
+			}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
+		if(ic) bRet = true;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rX) delete rX;	if(rY) delete rY;	if(rE) delete rE;	if(rL) delete rL;
+	return (dirty = bRet);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// calculate means and error to create a xy-plot
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+xyStat::PropertyDlg()
+{
+	char text1[100], text2[100];
+	DlgInfo StatDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 130, 10, 45, 12},
+		{2, 10, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 25, 45, 12},
+		{10, 100, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"range for grouping variable", 10, 10, 90, 9},
+		{101, 102, 0, 0x0L, LTEXT, (void*)"(X data)", 10, 20, 60, 9},
+		{102, 103, 0, 0x0L, RANGEINPUT, text1, 10, 30, 100, 10},
+		{103, 104, 0, 0x0L, LTEXT, (void*)"range for Y data", 10, 45, 90, 9},
+		{104, 200, 0, 0x0L, RANGEINPUT, text2, 10, 55, 100, 10},
+		{200, 300, 201, ISPARENT | CHECKED, GROUPBOX, (void*) " draw means ", 10, 75, 165, 50},
+		{201, 202, 0, CHECKED, CHECKBOX, (void*)" line", 15, 80, 50, 9},
+		{202, 203, 0, CHECKED, CHECKBOX, (void*)" symbols", 15, 90, 50, 9},
+		{203, 204, 0, 0x0L, CHECKBOX, (void*)" bars", 15, 100, 50, 9},
+		{204, 205, 0, 0x0L, LTEXT, (void*)"using", 65, 90, 30, 9},
+		{205, 206, 0, CHECKED, RADIO1, (void*)" arithmetic mean", 95, 80, 50, 9},
+		{206, 207, 0, 0x0L, RADIO1, (void*)" geometric mean", 95, 90, 50, 9},
+		{207, 208, 0, 0x0L, RADIO1, (void*)" harmonic mean", 95, 100, 50, 9},
+		{208, 0, 0, 0x0L, RADIO1, (void*)" median", 95, 110, 50, 9},
+		{300, 400, 301, ISPARENT | CHECKED, GROUPBOX, (void*) " draw error bars ", 10, 130, 165, 40},
+		{301, 302, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" std. deviation (SD)", 15, 135, 70, 9},
+		{302, 303, 0, ISRADIO, CHECKBOX, (void*)" std. error (SEM)", 15, 145, 70, 9},
+		{303, 304, 0, ISRADIO, CHECKBOX, (void*)" 25, 75% percentiles", 95, 135, 70, 9},
+		{304, 305, 0, ISRADIO, CHECKBOX, (void*)" min and max", 95, 145, 70, 9},
+		{305, 306, 0, ISRADIO, CHECKBOX, (void*)" ", 15, 155, 70, 9},
+		{306, 307, 0, 0x0L, EDVAL1, &ci, 28, 154, 15, 10},
+		{307, 0, 0, 0x0L, LTEXT, (void*) "%  conf. interval", 45, 155, 70, 9},
+		{400, 0, 401, ISPARENT | CHECKED, GROUPBOX, (void*) " number of cases ", 10, 175, 165, 30},
+		{401, 402, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" on top of error", 15, 180, 70, 9},
+		{402, 403, 0, ISRADIO, CHECKBOX, (void*)" on top of mean", 95, 180, 70, 9},
+		{403, 404, 0, 0x0L, LTEXT, (void*)"prefix:", 15, 190, 24, 9},
+		{404, 0, 0, LASTOBJ, EDTEXT, (void*)"n = ", 40, 189, 30, 10}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	bool bRet = false;
+	int i, res, width, height, cb_mdesc;
+	double x, y, e, f, dx, dy;
+	lfPOINT fp1, fp2;
+	char errdesc[40], *mdesc;
+	TextDEF lbdef = {defs.Color(COL_TEXT), defs.Color(COL_BG), DefSize(SIZE_TEXT), 0.0f, 0.0f, 0,
+		TXA_HLEFT | TXA_VBOTTOM, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, TmpTxt};
+
+	if(!parent || !data) return false;
+	UseRangeMark(data, 1, text1, text2);
+	if(!(Dlg = new DlgRoot(StatDlg, data)))return false;
+	text1[0] = text2[0] = 0;
+	hDlg = CreateDlgWnd("Mean and Error Plot", 50, 50, 370, 450, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 0:
+			if(Dlg->GetCheck(10)) res=-1;
+			break;
+		case 1:
+			if(!(Dlg->GetText(102, text1, 100) && Dlg->GetText(104, text2, 100) && text1[0] && text2[0])) res = 2;
+			break;
+			}
+		}while (res <0);
+	if(res == 1) {
+		xRange = (char*)memdup(text1, ((int)strlen(text1))+2, 0);
+		yRange = (char*)memdup(text2, ((int)strlen(text2))+2, 0);
+		type = 0;
+		if(Dlg->GetCheck(201)) type |= 0x0001;		if(Dlg->GetCheck(202)) type |= 0x0002;
+		if(Dlg->GetCheck(203)) type |= 0x0004;
+		if(Dlg->GetCheck(205)) type |= 0x0010;		if(Dlg->GetCheck(206)) type |= 0x0020;
+		if(Dlg->GetCheck(207)) type |= 0x0040;		if(Dlg->GetCheck(208)) type |= 0x0080;
+		if(Dlg->GetCheck(301)) type |= 0x0100;		if(Dlg->GetCheck(302)) type |= 0x0200;
+		if(Dlg->GetCheck(303)) type |= 0x0400;		if(Dlg->GetCheck(304)) type |= 0x0800;
+		if(Dlg->GetCheck(305)) type |= 0x1000;
+		if(Dlg->GetCheck(401)) type |= 0x2000;		if(Dlg->GetCheck(402)) type |= 0x4000;
+		Dlg->GetValue(306, &ci);					TmpTxt[0] = 0;
+		if(Dlg->GetText(404, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0]) case_prefix = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
+		CreateData();
+		if(type && curr_data) {
+			switch (type & 0x00f0) {
+				case 0x0010:		mdesc = "Mean";					break;
+				case 0x0020:		mdesc = "Geometric mean";		break;
+				case 0x0040:		mdesc = "Harmonic mean";		break;
+				case 0x0080:		mdesc = "Median";				break;
+				default:			mdesc = "n.a.";					break;
+				}
+			cb_mdesc = (int)strlen(mdesc);
+			curr_data->GetSize(&width, &height);		nPoints = height;
+#ifdef USE_WIN_SECURE
+			sprintf_s(text1, 100, "a1:a%d", height);	sprintf_s(text2, 100, "b1:b%d", height);
+#else
+			sprintf(text1, "a1:a%d", height);			sprintf(text2, "b1:b%d", height);
+#endif
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			if(type & 0x0001) {
+				if(nPoints >1 && (TheLine = new DataLine(this, curr_data, text1, text2)) &&
+					(TheLine->name = (char*)malloc(cb_mdesc+2))) rlp_strcpy(TheLine->name, cb_mdesc+2, mdesc);
+				}
+			if((type & 0x0002) && (Symbols = (Symbol**)calloc(nPoints, sizeof(Symbol*)))) {
+				for(i = 0; i < height; i++) {
+					if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 1, &y)
+						&& (Symbols[i] = new Symbol(this, curr_data, x, y, DefSym, 0, i, 1, i))){
+						Symbols[i]->idx = i;
+						if(Symbols[i]->name = (char*)malloc(cb_mdesc+2))
+							rlp_strcpy(Symbols[i]->name, cb_mdesc+2, mdesc);
+						}
+					}
+				}
+			if((type & 0x0004) && (Bars = (Bar**)calloc(nPoints, sizeof(Bar*)))) {
+				for(i = 0; i < height; i++) {
+					if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 1, &y)
+						&& (Bars[i] = new Bar(this, curr_data, x, y, BAR_VERTB | BAR_RELWIDTH, 0, i, 1, i))){
+						if(Bars[i]->name = (char*)malloc(cb_mdesc+2))
+							rlp_strcpy(Bars[i]->name, cb_mdesc+2, mdesc);
+						}
+					}
+				}
+			if(type & 0x1f00) {
+				Errors = (ErrorBar**)calloc(nPoints, sizeof(ErrorBar*));
+				switch(type & 0x1f00) {
+				case 0x0100:	rlp_strcpy(errdesc, 40, "Std. Dev.");			break;
+				case 0x0200:	rlp_strcpy(errdesc, 40, "Std. Err.");			break;
+				case 0x0400:	rlp_strcpy(errdesc, 40, "25, 75% Perc.");		break;
+				case 0x0800:	rlp_strcpy(errdesc, 40, "Min./Max.");			break;
+#ifdef USE_WIN_SECURE
+				case 0x1000:	sprintf_s(errdesc, 40, "'%g%% CI", ci);			break;
+#else
+				case 0x1000:	sprintf(errdesc, "'%g%% CI", ci);				break;
+#endif
+				default:		rlp_strcpy(errdesc, 40, "error");
+					}
+				}
+			if((type & 0x1300) && Errors) {
+				for(i = 0; i < height; i++) {
+					if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 1, &y)
+						&& curr_data->GetValue(i, 2, &e)) {
+						Errors[i]= new ErrorBar(this, curr_data, x, y, e, 0, 0, i, 1, i, 2, i);
+						if(Errors[i]) Errors[i]->Command(CMD_ERRDESC, errdesc, 0L);
+						}
+					}
+				}
+			if((type & 0x0c00) && Errors) {
+				for(i = 0; i < height; i++) {
+					if(curr_data->GetValue(i, 0, &x) && curr_data->GetValue(i, 2, &e) && curr_data->GetValue(i, 3, &f)){
+						fp1.fx = fp2.fx = x;	fp1.fy = e;		fp2.fy = f;
+						Errors[i]= (ErrorBar*)new Whisker(this, curr_data, fp1, fp2, 0, 0, i, 2, i, 0, i, 3, i);
+						if(Errors[i]) Errors[i]->Command(CMD_ERRDESC, errdesc, 0L);
+						}
+					}
+				}
+			if((type & 0x6000) && (Labels = (Label**)calloc(nPoints, sizeof(Label*)))) {
+				dy = -0.4 * DefSize(SIZE_SYMBOL);
+				if(type & 0x2000){
+					lbdef.Align = TXA_HCENTER | TXA_VBOTTOM;
+					dx = 0.0;
+					}
+				else {
+					lbdef.Align = TXA_HLEFT | TXA_VBOTTOM;
+					dx = -dy;
+					}
+				for(i = 0; i < height; i++) {
+					if(curr_data->GetValue(i, 0, &x) && curr_data->GetText(i, 5, TmpTxt, TMP_TXT_SIZE, false)){
+						if((type & 0x2000) && curr_data->GetValue(i, 4, &y)) 
+							Labels[i] = new Label(this, curr_data, x, y, &lbdef, 
+							LB_X_DATA | LB_Y_DATA, 0, i, 4, i, 5, i);
+						else if((type & 0x4000) && curr_data->GetValue(i, 1, &y)) 
+							Labels[i] = new Label(this, curr_data, x, y, &lbdef, 
+							LB_X_DATA | LB_Y_DATA, 0, i, 1, i, 5, i);
+						if(Labels[i]){
+							Labels[i]->SetSize(SIZE_LB_YDIST, dy);
+							Labels[i]->SetSize(SIZE_LB_XDIST, dx);
+							}
+						}
+					}
+				}
+			Command(CMD_AUTOSCALE, 0L, 0L);
+			bRet = true;
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Frequency distribution
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *FreqDlg_Tmpl =
+	"1,2,,DEFAULT,PUSHBUTTON,-1,130,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,130,25,45,12\n"
+	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,5,100,ISPARENT | CHECKED,SHEET,1,5,10,120,153\n"
+	"5,10,200,ISPARENT,SHEET,2,5,10,120,153\n"
+	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,101,,,LTEXT,3,10,25,60,8\n"
+	"101,102,,,RANGEINPUT,-15,10,38,110,10\n"
+	"102,103,120,ISPARENT | CHECKED,GROUPBOX,4,10,55,110,42\n"
+	"103,,150,ISPARENT | CHECKED,GROUPBOX,5,10,102,110,57\n" 
+	"120,121,,CHECKED,RADIO1,6,15,60,30,9\n"
+	"121,122,,, EDVAL1,7,47,60,15,10\n"
+	"122,123,,, LTEXT,8,64,60,35,8\n"
+	"123,124,,, RADIO1,9, 15, 72, 45, 9\n"
+	"124,125,,, EDTEXT,-16,65,72,50,10\n"
+	"125,126,,, RTEXT,10,15,84, 47,8\n"
+	"126,,,,EDTEXT,-16,65,84,50,10\n"
+	"150,151,,ISRADIO,CHECKBOX,13,15,107,30, 8\n"
+	"151,152,,ISRADIO,CHECKBOX,14,15,117,30, 8\n"
+	"152,153,,ISRADIO,CHECKBOX,15,65,107,30, 8\n"
+	"153,154,,ISRADIO,CHECKBOX,16,65,117,30,8\n"
+	"154,155,,ISRADIO,CHECKBOX,17,15,127,30,8\n"
+	"155,156,,ISRADIO,CHECKBOX,18,15,137,30,8\n"
+	"156,,,ISRADIO,CHECKBOX,19,15,147,30,8\n"
+	"200,, 210,ISPARENT | CHECKED,GROUPBOX,11,10,27,110,61\n" 
+	"210,,,LASTOBJ | NOSELECT,ODBUTTON,12,25,34,90,50";
+
+bool
+FreqDist::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 52, 10, "Style"};
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)"spread sheet range for values",
+		(void*)" classes ", (void*)" plot distribution ", (void*)"create", (void*)&step,
+		(void*)"classes and bars", (void*)"class size is", (void*)"starting at",
+		(void*)" bar style ", (void*)OD_filldef, (void*)" normal", (void*)" log-normal",
+		(void*)" binomial", (void*)" poisson", (void*)" exponential", (void*)" rectangular",
+		(void*)" chi-square"};
+	DlgInfo *FreqDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	bool bRet = false;
+	int res, r, c;
+	double tmp;
+	char *mrk;
+	AccRange *aR;
+
+	if(!parent || !data) return false;
+	if(!(FreqDlg = CompileDialog(FreqDlg_Tmpl, dyndata))) return false;
+	step = 7;	TmpTxt[100] = 0;
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&BarLine, 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&BarFill, 0);
+	if(data->Command(CMD_GETMARK, &mrk, 0L)) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, mrk);
+	else TmpTxt[0] = 0;
+	if(!(Dlg = new DlgRoot(FreqDlg, data)))return false;
+	hDlg = CreateDlgWnd("Frequency Distribution", 50, 50, 370, 370, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 0:
+			if(Dlg->GetCheck(10)) res=-1;
+			break;
+		case 1:
+			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) ssRef = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
+			if(Dlg->GetCheck(150)) type = 1;
+			else if(Dlg->GetCheck(151)) type = 2;
+			else if(Dlg->GetCheck(154)) type = 3;
+			else if(Dlg->GetCheck(155)) type = 4;
+			else if(Dlg->GetCheck(156)) type = 5;
+			else if(Dlg->GetCheck(152)) type = 10;
+			else if(Dlg->GetCheck(153)) type = 11;
+			else type = 0;
+			break;
+			}
+		}while (res <0);
+	if(res==1 && (plots = (GraphObj**)calloc(nPlots=3, sizeof(GraphObj*)))) {
+		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&BarLine, 0);
+		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&BarFill, 0);
+		if(BarFill.hatch) memcpy(&HatchLine, BarFill.hatch, sizeof(LineDEF));
+		BarFill.hatch = &HatchLine;
+		if(Dlg->GetCheck(123) && Dlg->GetValue(124, &step) && Dlg->GetValue(126, &start)) ProcData(-2);
+		else {
+			Dlg->GetValue(121, &step);		ProcData(-1);
+			}
+		if(y_info = (char*)malloc(25*sizeof(char))) rlp_strcpy(y_info, 25, "No. of observations");
+		if(x_info = (char*)malloc(25*sizeof(char))){
+			rlp_strcpy(x_info, 25, "Categories");
+			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE) && (aR = new AccRange(TmpTxt))) {
+				if(aR->GetFirst(&c, &r) && !data->GetValue(r, c, &tmp) && data->GetText(r, c, TmpTxt, 150, false))
+					rlp_strcpy(x_info, 25, TmpTxt);
+				delete aR;
+				}
+			}
+		if(plots[0]) bRet = true;
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;		free(FreqDlg);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Regression properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char* RegDlg_Tmpl = 
+	"1,+,,DEFAULT,PUSHBUTTON,-1,140,10,45,12\n"
+	".,.,,,PUSHBUTTON,-2,140,25,45,12\n"
+	".,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,+,100,ISPARENT | CHECKED,SHEET,1,5,10,130,85\n"
+	".,10,200,ISPARENT,SHEET,2,5,10,130,85\n"
+	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,+,,,LTEXT,3,10,22,60,8\n"
+	".,.,,,RANGEINPUT,-16,20,32,100,10\n"
+	".,.,,,LTEXT,4,10,45,60,8\n"
+	".,.,,,RANGEINPUT,-17,20,55,100,10\n"
+	".,.,,CHECKED,CHECKBOX,5,10,70,100,8\n"
+	".,,,,CHECKBOX,6,10,80,100,8\n" 
+	"200,210,201,CHECKED | ISPARENT, GROUPBOX,7,10,30,58,56\n"
+	"201,+,,CHECKED, RADIO1,8,20,40,30,8\n"
+	".,.,,,RADIO1,9,20,50,30,8\n"
+	".,.,,,RADIO1,10,20,60,30,8\n"
+	".,,,,RADIO1,11,20,70,30,8\n"
+	"210,,211,CHECKED | ISPARENT,GROUPBOX,12,72,30,58,56\n"
+	".,+,,CHECKED,RADIO1,13,82,40,30,8\n"
+	".,.,,,RADIO1,14,82,50,30,8\n"
+	".,.,,,RADIO1,15,82,60,30,8\n"
+	".,,,LASTOBJ,RADIO1,16,82,70,30,8";
+
+bool
+Regression::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 28, 10, "Data"};
+	TabSHEET tab2 = {28, 70, 10, "Transform"};
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)"range for X Data",
+		(void*)"range for Y Data", (void*)" include symbols in plot", (void*) " draw SD ellipse",
+		(void*)"  x-values  ", (void*)"x = x", (void*)"x = log(x)", (void*)"x = 1/x",
+		(void*)"x = sqrt(x)", (void*)"  y-values  ", (void*)"y = y", (void*)"y = log(y)",
+		(void*)"y = 1/y",(void*)"y = sqrt(y)"};
+	DlgInfo *RegDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int c, i, j, k, l, ic, res, n;
+	double x, y;
+	AccRange *rX, *rY;
+	bool bRet = false, bContinue = false, dValid;
+	lfPOINT *values = 0L;
+
+	if(!parent || !data) return false;
+	if(!(RegDlg = CompileDialog(RegDlg_Tmpl, dyndata))) return false;
+	rX = rY = 0L;
+	UseRangeMark(data, 1, TmpTxt+100, TmpTxt+200);
+	if(!(Dlg = new DlgRoot(RegDlg, data)))return false;
+	hDlg = CreateDlgWnd("Linear regression analysis step 1/2", 50, 50, 380, 230, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:								// focus lost
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 1:								// OK
+			if(rX) delete rX;	if(rY) delete rY;
+			rX = rY = 0L;						// check x-range
+			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) rX = new AccRange(TmpTxt);
+			if(!(n = rX ? rX->CountItems() : 0)) {
+				Dlg->SetCheck(4, 0L, true);
+				res = -1;
+				bContinue = true;
+				ErrorBox("X-range not specified\nor not valid.");
+				}
+			else {							// check y-range
+				if(Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE)) rY = new AccRange(TmpTxt);
+				if(n != (rY ? rY->CountItems() : 0)) {
+					res = -1;
+					bContinue = true;
+					ErrorBox("Y-range missing\nor not valid.\n"
+						"Size must match X-range.");
+					}
+				}
+			}
+		}while (res <0);
+	if(res==1 && n && rX && rY && (values =(lfPOINT*)calloc(nPoints=n, sizeof(lfPOINT)))){				//OK pressed
+		Command(CMD_FLUSH, 0L, 0L);
+		if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) xRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
+		if(Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE)) yRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
+		rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
+		if(Dlg->GetCheck(202)) type = 0x100;
+		else if(Dlg->GetCheck(203)) type = 0x200;
+		else if(Dlg->GetCheck(204)) type = 0x300;
+		if(Dlg->GetCheck(212)) type |= 0x1000;
+		else if(Dlg->GetCheck(213)) type |= 0x2000;
+		else if(Dlg->GetCheck(214)) type |= 0x3000;
+		ic = c = 0;
+		if(Dlg->GetCheck(104)) Symbols = (Symbol**)calloc(nPoints, sizeof(Symbol*));
+		do {
+			if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y)){
+				dValid = true;
+				switch(type & 0x700) {
+				case 0x100:					//logarithmic x
+					if(dValid = x > defs.min4log) values[ic].fx = log10(x);
+					break;
+				case 0x200:					//reciprocal x
+					if(dValid = fabs(x) >defs.min4log) values[ic].fx = 1.0/x;
+					break;
+				case 0x300:					//square root x
+					if(dValid = fabs(x) >defs.min4log) values[ic].fx = sqrt(x);
+					break;
+				default:	values[ic].fx = x;	break;		//linear x
+					}
+				if(dValid) switch(type & 0x7000) {
+				case 0x1000:				//logarithmic y
+					if(dValid = y > defs.min4log) values[ic].fy = log10(y);
+					break;
+				case 0x2000:				//reciprocal y
+					if(dValid = fabs(y) > defs.min4log) values[ic].fy = 1.0/y;
+					break;
+				case 0x3000:				//square root y
+					if(dValid = fabs(y) > defs.min4log) values[ic].fy = sqrt(y);
+					break;
+				default:	values[ic].fy = y;	break;		//linear y
+					}
+				if(dValid && Symbols && (Symbols[ic] = new Symbol(this, data, x, y, 
+					SYM_CIRCLE, i, j, k, l))){
+					Symbols[ic]->idx = c;
+					}
+				if(dValid) {
+					CheckBounds(x, y);
+					ic++;
+					}
+				}
+			c++;
+			}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
+		if(ic) {
+			if(Dlg->GetCheck(105) && (sde = new SDellipse(this, data, values, ic, type | 0x20002))&&
+				(bRet= sde->Command(CMD_INIT, 0L, 0L)))sde->Command(CMD_BOUNDS, &Bounds, 0L);
+			else if((rLine = new RegLine(this, data, values, ic, type)) && 
+				(bRet= rLine->PropertyDlg()))rLine->Command(CMD_BOUNDS, &Bounds, 0L);
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rX) delete rX;	if(rY) delete rY;
+	free(RegDlg);		if(values) free(values);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Bubble plot properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *BubPlotDlg_Tmpl = 
+	"1, 2, 0, DEFAULT, PUSHBUTTON,-1,148,10,45,12\n"
+	"2, 3, 0, 0x0L, PUSHBUTTON,-2,148,25,45,12\n"
+	"3, 500, 4, ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4, 5, 100, ISPARENT | CHECKED,SHEET,1,5,10,130,120\n"
+	"5, 6, 200, ISPARENT, SHEET,2,5,10,130,120\n"
+	"6, 10, 300, ISPARENT, SHEET,3,5,10,130,120\n"
+	"10,,, CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,+,,,LTEXT,4,10,40,60,8\n"
+	".,.,,,RANGEINPUT,-15,20,50,100,10\n"
+	".,.,,,LTEXT,5,10,65,60,8\n"
+	".,.,,,RANGEINPUT,-16,20,75,100,10\n"
+	".,.,,,LTEXT,6,10,90,60,8\n"
+	".,,,,RANGEINPUT,-17,20,100,100,10\n"
+	"200,+,,,LTEXT,-27,10,30,110,8\n"
+	".,.,,ISRADIO | CHECKED,ODBUTTON,7,30,40,20,20\n"
+	".,.,,ISRADIO,ODBUTTON,7,50,40,20,20\n"
+	".,.,,ISRADIO,ODBUTTON,7,70,40,20,20\n"
+	".,.,,ISRADIO,ODBUTTON,7,90,40,20,20\n"
+	".,.,,,LTEXT,8,7,67,45,8\n"
+	".,.,,,RTEXT,9,7,75,20,8\n"
+	".,.,,OWNDIALOG,COLBUTT,10,29,75,25,10\n"
+	".,.,,,RTEXT,11,67,75,20,8\n"
+	".,.,,,EDVAL1,12,88,75,25,10\n"
+	".,.,,,LTEXT,-3,114,75,15,8\n"
+	".,.,,,LTEXT,13,7,97,45,8\n"
+	".,.,,,RTEXT,-11,7,105,20,8\n"
+	".,.,,TOUCHEXIT | OWNDIALOG,COLBUTT,14,29,105,25,10\n"
+	".,.,,,RTEXT,15,67,105,20,8\n"
+	".,,,TOUCHEXIT | OWNDIALOG,FILLBUTTON,16,88,105,25,10\n"
+	"300,+,,,LTEXT,17,10,30,110,8\n"
+	".,.,400,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	".,.,,,LTEXT,18,10,75,110,8\n"
+	".,,410,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"400,+,,CHECKED,RADIO1,-3,40,40,45,8\n"
+	".,.,,,RADIO1,19,40,50,45,8\n"
+	".,,,,RADIO1,20,40,60,45,8\n"
+	"410,+,,CHECKED,RADIO1,21,40,85,45,8\n"
+	".,.,,,RADIO1,22,40,95,45,8\n"
+	".,,,LASTOBJ,RADIO1,23,40,105,45,8";
+bool
+BubblePlot::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 57, 10, "Layout"};
+	TabSHEET tab3 = {57, 90, 10, "Scaling"};
+	int syms[] = {SYM_CIRCLE, SYM_RECT, SYM_TRIAU, SYM_TRIAD};
+	FillDEF ShowFill;
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)"range for X Data",
+		(void*)"range for Y Data", (void*)"range for sizes", (void*)OD_BubbleTempl,
+		(void*)"outline:", (void*)"color", (void *)&BubbleLine.color, (void*)"line width",
+		(void*)&BubbleLine.width, (void*)"fill:", (void *)&BubbleFill.color, (void*)"pattern",
+		(void *)&ShowFill, (void*)"sizes are given as", (void*)"proportionality (relative to circle)",
+		(void*)"scaling with X axis", (void*)"scaling with Y axis", (void*)"diameter",
+		(void*)"circumference", (void*)"area"};
+	DlgInfo *PlotDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, k, l, m, n, ic, res, BubbleType;
+	double x, y, s;
+	double tmp;
+	bool bRetVal = false, bFillChanged, bContinue = false;
+	AccRange *rX, *rY, *rS;
+	LineDEF ShowFillLine ={0.2f, 1.0f, 0x0L, 0x0L};
+
+	if(!parent || !data) return false;
+	if(!(PlotDlg = CompileDialog(BubPlotDlg_Tmpl, dyndata))) return false;
+	UseRangeMark(data, 1, TmpTxt, TmpTxt+100, TmpTxt+200);
+	memcpy(&ShowFill, &BubbleFill, sizeof(FillDEF));
+	if(BubbleFill.hatch) memcpy(&ShowFillLine, BubbleFill.hatch, sizeof(LineDEF));
+	ShowFill.hatch = &ShowFillLine;
+	if(!(Dlg = new DlgRoot(PlotDlg, data)))return false;
+	hDlg = CreateDlgWnd("Create Bubble Plot", 50, 50, 400, 300, Dlg, 0x4L);
+	rX = rY = rS = 0L;
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:								// focus lost
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 213:					//fillcolor changed
+			Dlg->GetColor(213, &ShowFill.color);
+			Dlg->DoPlot(NULL);
+			res = -1;
+			break;
+		case 215:					//copy color from pattern dialog
+			Dlg->SetColor(213, ShowFill.color);
+			bFillChanged = true;
+			res = -1;
+			break;
+		case 1:						//OK button
+			if(Dlg->GetText(101, TmpTxt, 100) && Dlg->GetText(103, TmpTxt+100, 100) && 
+				Dlg->GetText(105, TmpTxt+200, 100) && (rX = new AccRange(TmpTxt)) &&
+				(rY = new AccRange(TmpTxt+100)) && (rS = new AccRange(TmpTxt+200))) {
+				if((i = rX->CountItems()) == rY->CountItems() && i == rS->CountItems()){
+					// OK pressed and ranges checked: exit loop and process data
+					}
+				else {
+					if(rX) delete (rX);	if(rY) delete (rY);	if(rS) delete (rS);
+					rX = rY = rS = 0L;
+					ErrorBox("Ranges must be of equal size");
+					Dlg->SetCheck(4, 0L, bContinue = true);
+					res = -1;
+					}
+				}
+			else res = -1;				//continue with dialog if error
+			}
+		}while (res <0);
+	if(res ==1 && rX && rY && rS && (nPoints = rX->CountItems()) && 
+		(Bubbles = (Bubble**)calloc(nPoints, sizeof(Bubble*)))) {
+		//accept settings and create bubbles for plot
+		if(Dlg->GetCheck(202)) BubbleType = BUBBLE_SQUARE;
+		else if(Dlg->GetCheck(203)) BubbleType = BUBBLE_UPTRIA;
+		else if(Dlg->GetCheck(204)) BubbleType = BUBBLE_DOWNTRIA;
+		else BubbleType = BUBBLE_CIRCLE;
+		if(Dlg->GetCheck(401)) BubbleType |= BUBBLE_XAXIS;
+		else if(Dlg->GetCheck(402)) BubbleType |= BUBBLE_YAXIS;
+		if(Dlg->GetCheck(411)) BubbleType |= BUBBLE_CIRCUM;
+		else if(Dlg->GetCheck(412)) BubbleType |= BUBBLE_AREA;
+		if(Dlg->GetValue(209, &tmp)) BubbleLine.width = (float)tmp;
+		Dlg->GetColor(207, &BubbleLine.color);		Dlg->GetColor(213, &BubbleFill.color);
+		rX->GetFirst(&i, &j);		rY->GetFirst(&k, &l);	rS->GetFirst(&m, &n);
+		rX->GetNext(&i, &j);		rY->GetNext(&k, &l);	rS->GetNext(&m, &n);
+		ic = 0;
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		if(Bubbles) do {
+			if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y) && data->GetValue(n, m, &s)){
+				CheckBounds(x, y);
+				Bubbles[ic++] = new Bubble(this, data, x, y, s, BubbleType, &ShowFill, 
+					&BubbleLine, i, j, k, l, m, n);
+				}
+			}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l) && rS->GetNext(&m, &n));
+		bRetVal = ic >0;
+		}
+	CloseDlgWnd(hDlg);
+	if(rX) delete (rX);		if(rY) delete (rY);		if(rS) delete (rS);
+	delete Dlg;			free(PlotDlg);			return bRetVal;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Polar plot properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *AddPolDlg_Tmpl = 
+	"1,2,,DEFAULT,PUSHBUTTON,-1,140,14,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,140,29,45,12\n"
+	"3,10,200,ISPARENT | CHECKED,GROUPBOX,1,5,14,131,96\n"
+	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"200,201,,CHECKED | EXRADIO,ODBUTTON,2,10,24,20,20\n"
+	"201,202,,EXRADIO,ODBUTTON,2,30,24,20,20\n"
+	"202,203,,EXRADIO,ODBUTTON,2,50,24,20,20\n"
+	"203,204,,EXRADIO,ODBUTTON,2,70,24,20,20\n"
+	"204,210,,EXRADIO,ODBUTTON,2,90,24,20,20\n"
+	"210,211,,,LTEXT,3,10,50,50,8\n"
+	"211,212,,,RANGEINPUT,-15,20,62,100,10\n"
+	"212,213,,,LTEXT,4,10,75,50,8\n"
+	"213,,,LASTOBJ,RANGEINPUT,-16,20,87,100,10";
+
+bool
+PolarPlot::AddPlot()
+{
+	void *dyndata[] = {(void*)" select template and data range ", (void*)OD_PolarTempl,
+		(void*)"range for x-data (circular or angular data)",
+		(void*)"range for y-data (radial data)"};
+	DlgInfo *PolDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, k, l, ic, n, res, cType = 200;
+	bool bRet = false, bContinue = false;
+	double x, y;
+	AccRange *rX = 0L, *rY = 0L;
+	Symbol **Symbols = 0L;
+	DataLine *TheLine = 0L;
+	Plot **tmpPlots;
+	Function *func;
+	anyOutput *cdisp = Undo.cdisp;
+
+	if(!parent || !data) return false;
+	if(!(PolDlg = CompileDialog(AddPolDlg_Tmpl, dyndata))) return false;
+	UseRangeMark(data, 1, TmpTxt, TmpTxt+100);
+	if(!(Dlg = new DlgRoot(PolDlg, data)))return false;
+	Dlg->bModal = false;
+	hDlg = CreateDlgWnd("Add Polar Plot", 50, 50, 388, 260, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:								// focus lost
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 200: case 201:	case 202:	case 203:	case 204:
+			if(res == 204) {
+				Dlg->Activate(211, false);			Dlg->Activate(213, false);
+				}
+			else if(cType == 204) {
+				Dlg->Activate(211, true);			Dlg->Activate(213, true);
+				}
+			if(res == cType) res = 1;
+			else {
+				cType = res;
+				res = -1;
+				}
+			break;
+			}
+		}while (res <0);
+	if(res == 1 && Dlg->GetText(211, TmpTxt, 100) && Dlg->GetText(213, TmpTxt+100, 100) && 
+		(rX = new AccRange(TmpTxt)) && (rY = new AccRange(TmpTxt+100)) &&
+		(n = rX ? rX->CountItems() : 0) && 
+		(tmpPlots = (Plot**)realloc(Plots, (nPlots+2)*sizeof(Plot*)))) {
+		Undo.SetDisp(cdisp);
+		Plots = tmpPlots;
+		if(Dlg->GetCheck(200) || Dlg->GetCheck(201)) 
+			Symbols = (Symbol**) calloc(n+1, sizeof(Symbol*));
+		if(Dlg->GetCheck(201) || Dlg->GetCheck(202)) 
+			TheLine = new DataLine(this, data, TmpTxt, TmpTxt+100);
+		else if(Dlg->GetCheck(203))
+			TheLine = new DataPolygon(this, data, TmpTxt, TmpTxt+100);
+		else if(Dlg->GetCheck(204)) {
+			if(func = new Function(this, data, "Function")){
+				if(bRet = func->PropertyDlg()){
+					Undo.SetGO(this, (GraphObj**) &Plots[nPlots++], func, 0L);
+					memcpy(&Bounds, &Plots[nPlots-1]->Bounds, sizeof(fRECT));
+					}
+				else DeleteGO(func);
+				}
+			}
+		rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
+		rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
+		ic = 0;
+		if(Symbols || TheLine) {
+			do {
+				if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y)){
+					CheckBounds(y, y);
+					if(Symbols)Symbols[ic++] = new Symbol(this,data,x,y,SYM_CIRCLE,i,j,k,l);
+					}
+				}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
+			Undo.SetGO(this, (GraphObj**) &Plots[nPlots++], 
+				new PlotScatt(this, data, ic, Symbols, TheLine), 0L);
+			bRet = true;
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;			free(PolDlg);
+	if(rX) delete rX;	if(rY) delete rY;
+	return bRet;
+}
+
+bool
+PolarPlot::Config()
+{
+	TabSHEET tab1 = {0, 40, 10, "Plot"};
+	DlgInfo PPDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 102, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 102, 25, 45, 12},
+		{3, 0, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 90, 90},
+		{100, 101, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 8, 30, 90, 50},
+		{101, 0, 0, LASTOBJ, CHECKBOX, (void*)"show radial axis", 15, 85, 60, 9}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res;
+	bool bRet = false;
+	LineDEF OutLine;
+
+	memcpy(&OutLine, defs.GetOutLine(), sizeof(LineDEF));
+	if(Axes && Axes[0]) {
+		OutLine.color = Axes[0]->GetColor(COL_AXIS);
+		OutLine.width = Axes[0]->GetSize(SIZE_AXIS_LINE);
+		}
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&OutLine, 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&Fill, 0);
+	Dlg = new DlgRoot(PPDlg, data);
+	if(!(type & 0x01))Dlg->SetCheck(101, 0L, true);
+	hDlg = CreateDlgWnd("Polar Plot properties", 50, 50, 310, 234, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		}while (res < 0);
+	if(res == 1){						//OK pressed
+		if(Dlg->GetCheck(101)) type &= ~0x01;
+		else type |= 0x01;
+		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&OutLine, 0);
+		if(Axes && Axes[0]) {
+			Axes[0]->SetColor(COL_AXIS, OutLine.color);
+			Axes[0]->SetSize(SIZE_AXIS_LINE, OutLine.width);
+			}
+		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&Fill, 0);
+		if(Fill.hatch) memcpy(&FillLine, Fill.hatch, sizeof(LineDEF));
+		Fill.hatch = &FillLine;
+		bRet = true;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+bool
+PolarPlot::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 45, 10, "Coordinates"};
+	TabSHEET tab2 = {45, 70, 10, "Type"};
+	char text1[100], text2[100];
+	double lox = 0.0, hix = 360.0, fcx =10.0, fcy = 20.0, frad=40.0;
+	DlgInfo PolDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 140, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 140, 25, 45, 12},
+		{3, 50, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{4, 5, 100, ISPARENT | TOUCHEXIT, SHEET, &tab1, 5, 10, 131, 100},
+		{5, 10, 200, ISPARENT | TOUCHEXIT | CHECKED, SHEET, &tab2, 5, 10, 131, 100},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"angular range (full circle)", 10, 25, 60, 8},
+		{101, 102, 0, 0x0L, RTEXT, (void*)"min =", 5, 37, 25, 8},
+		{102, 103, 0, 0x0L, EDVAL1, &lox, 30, 37, 30, 10},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"max =", 60, 37, 25, 8},
+		{104, 105, 0, 0x0L, EDVAL1, &hix, 85, 37, 30, 10},
+		{105, 106, 0, 0x0L, RTEXT, (void*)"angular offset:", 10, 49, 50, 8},
+		{106, 107, 0, 0x0L, EDVAL1, &offs, 62, 49, 30, 10},
+		{107, 108, 0, 0x0L, LTEXT, (void*)"position of center:", 10, 65, 40, 8},
+		{108, 109, 0, 0x0L, RTEXT, (void*)"x =", 5, 77, 25, 8},
+		{109, 110, 0, 0x0L, EDVAL1, &fcx, 30, 77, 30, 10},
+		{110, 111, 0, 0x0L, RTEXT, (void*)"y =", 60, 77, 25, 8},
+		{111, 112, 0, 0x0L, EDVAL1, &fcy, 85, 77, 30, 10},
+		{112, 113, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 117, 77, 15, 8}, 
+		{113, 114, 0, 0x0L, EDVAL1, &frad, 62, 89, 30, 10},
+		{114, 115, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 94, 89, 15, 8},
+		{115, 0, 0, 0x0L, RTEXT, (void*)"radius:", 10, 89, 50, 8},
+		{200, 201, 0, CHECKED | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 10, 25, 20, 20},
+		{201, 202, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 30, 25, 20, 20},
+		{202, 203, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 50, 25, 20, 20},
+		{203, 204, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 70, 25, 20, 20},
+		{204, 210, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 90, 25, 20, 20},
+		{210, 211, 0, 0x0L, LTEXT, (void*)"range for x-data (circular or angular data)", 10, 55, 50, 8},
+		{211, 212, 0, 0x0L, RANGEINPUT, (void*)text1, 20, 67, 100, 10},
+		{212, 213, 0, 0x0L, LTEXT, (void*)"range for y-data (radial data)", 10, 80, 50, 8},
+		{213, 0, 0, LASTOBJ, RANGEINPUT, (void*)text2, 20, 92, 100, 10}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, i, j, k, l, n, ic, cType = 200;
+	double x, y;
+	bool bRet = false, bType = false;
+	AccRange *rX = 0L, *rY = 0L;
+	Symbol **Symbols = 0L;
+	DataLine *TheLine = 0L;
+	TextDEF tlbdef;
+	AxisDEF ang_axis, rad_axis;
+
+	if(!parent || !data) return false;
+	if(Plots) return Config();
+	frad = (parent->GetSize(SIZE_DRECT_BOTTOM) - parent->GetSize(SIZE_DRECT_TOP))/2.0f;
+	fcx = parent->GetSize(SIZE_GRECT_LEFT) + parent->GetSize(SIZE_DRECT_LEFT)*1.5 + frad;
+	fcy = parent->GetSize(SIZE_GRECT_TOP) + parent->GetSize(SIZE_DRECT_TOP) + frad;
+	UseRangeMark(data, 1, text1, text2);
+	tlbdef.ColTxt = defs.Color(COL_AXIS);
+	tlbdef.ColBg = 0x00ffffffL;
+	tlbdef.RotBL = tlbdef.RotCHAR = 0.0f;
+	tlbdef.fSize = DefSize(SIZE_TICK_LABELS);
+	tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
+	tlbdef.Style = TXS_NORMAL;
+	tlbdef.Mode = TXM_TRANSPARENT;
+	tlbdef.Font = FONT_HELVETICA;
+	tlbdef.text = 0L;
+	if(!(Dlg = new DlgRoot(PolDlg, data)))return false;
+	hDlg = CreateDlgWnd("Create Polar Plot", 50, 50, 388, 260, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case 5:		case 4:
+			bType = true;
+			res = -1;
+			break;
+		case 1:
+			if(!bType) {		//the 'Coordinates' sheet must have been visited
+				bType = true;
+				Dlg->SetCheck(4, 0L, true);
+				res = -1;
+				}
+			break;
+		case 200: case 201:	case 202:	case 203:	case 204:
+			if(res == 204) {
+				Dlg->Activate(211, false);			Dlg->Activate(213, false);
+				}
+			else if(cType == 204) {
+				Dlg->Activate(211, true);			Dlg->Activate(213, true);
+				}
+			if(res == cType) res = 1;
+			else {
+				cType = res;
+				res = -1;
+				}
+			break;
+			}
+		}while (res <0);
+	if(res == 1) {
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		//set axis information in ang_axis and rad_axis
+		ang_axis.owner = rad_axis.owner = 0L;
+		ang_axis.breaks = rad_axis.breaks = 0L;
+		ang_axis.nBreaks = rad_axis.nBreaks = 0;
+		ang_axis.flags = AXIS_ANGULAR;	
+		rad_axis.flags = AXIS_RADIAL | AXIS_DEFRECT;
+		Dlg->GetValue(109, &ang_axis.Center.fx);	
+		rad_axis.Center.fx = ang_axis.Center.fx;
+		Dlg->GetValue(111, &ang_axis.Center.fy);	
+		rad_axis.Center.fy = ang_axis.Center.fy;
+		Dlg->GetValue(113, &ang_axis.Radius);
+		rad_axis.Radius = ang_axis.Radius;
+		Dlg->GetValue(102, &ang_axis.min);
+		Dlg->GetValue(104, &ang_axis.max);
+		Dlg->GetValue(106, &offs);
+		ang_axis.loc[0].fy = ang_axis.loc[1].fy = ang_axis.Center.fy + ang_axis.Radius;
+		ang_axis.loc[0].fx = ang_axis.Center.fx - ang_axis.Radius;
+		ang_axis.loc[1].fx = ang_axis.Center.fx + ang_axis.Radius;
+		rad_axis.loc[0].fx = rad_axis.loc[1].fx = 
+			parent->GetSize(SIZE_GRECT_LEFT) + parent->GetSize(SIZE_DRECT_LEFT);
+		rad_axis.loc[0].fy = rad_axis.Center.fy - rad_axis.Radius;
+		rad_axis.loc[1].fy = rad_axis.Center.fy;
+		if(Dlg->GetText(211, text1, 100) && Dlg->GetText(213, text2, 100) && 
+			(rX = new AccRange(text1)) && (rY = new AccRange(text2)) &&
+			(n = rX ? rX->CountItems() : 0) && (Plots = (Plot**)calloc(2, sizeof(Plot*)))) {
+			if(Dlg->GetCheck(200) || Dlg->GetCheck(201)) 
+				Symbols = (Symbol**) calloc(n+1, sizeof(Symbol*));
+			if(Dlg->GetCheck(201) || Dlg->GetCheck(202)) 
+				TheLine = new DataLine(this, data, text1, text2);
+			else if(Dlg->GetCheck(203))
+				TheLine = new DataPolygon(this, data, text1, text2);
+			else if(Dlg->GetCheck(204)) {
+				if(Plots[nPlots++] = new Function(this, data, "Function")){
+					if(bRet = Plots[nPlots-1]->PropertyDlg())
+						memcpy(&Bounds, &Plots[nPlots-1]->Bounds, sizeof(fRECT));
+					else {
+						DeleteGO(Plots[nPlots-1]);
+						Plots[nPlots-1] = 0L;
+						}
+					}
+				}
+			rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
+			rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
+			ic = 0;
+			if(Symbols || TheLine) do {
+				if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y)){
+					CheckBounds(y, y);
+					if(Symbols) Symbols[ic++] = new Symbol(this, data, x, y, 
+						SYM_CIRCLE, i, j, k, l);
+					}
+				}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
+			rad_axis.min = Bounds.Ymin;			rad_axis.max = Bounds.Ymax;
+			NiceAxis(&rad_axis, 4);
+			ang_axis.Start = ang_axis.Step = 0.0;
+			if(Symbols || TheLine) 
+				Plots[nPlots++] = new PlotScatt(this, data, ic, Symbols, TheLine);
+			if(Plots[0] && (Axes = (GraphObj**)calloc(3, sizeof(Axis*)))){
+				Axes[0] = new Axis(this, data, &ang_axis, ang_axis.flags);
+				Axes[1] = new Axis(this, data, &rad_axis, 
+					rad_axis.flags | AXIS_AUTOTICK | AXIS_NEGTICKS);
+				Axes[1]->SetSize(SIZE_LB_XDIST, 
+					NiceValue(-DefSize(SIZE_AXIS_TICKS)*6.0)); 
+				Axes[1]->SetSize(SIZE_TLB_XDIST, 
+					NiceValue(-DefSize(SIZE_AXIS_TICKS)*2.0)); 
+				Axes[1]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+				nAxes = 2;
+				bRet = true;
+				}
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rX) delete rX;	if(rY) delete rY;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Box plot properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static int boxplot_mode_sel = 52;
+bool
+BoxPlot::PropertyDlg()
+{
+	DlgInfo PlotDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 130, 10, 45, 12},
+		{2, 10, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 25, 45, 12},
+		{10, 50, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
+		{50, 60, 51, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{51, 52, 0, 0x0L, LTEXT, (void*)"Data Source:", 10, 12, 40, 9},
+		{52, 53, 0, TOUCHEXIT, RADIO2, (void*)" user values", 60, 12, 50, 9},
+		{53, 0, 0, TOUCHEXIT, RADIO2, (void*)" statistical data", 60, 22, 60, 9},
+		{60, 61, 100, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{61, 0, 200, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{100, 102, 0, 0x0L, LTEXT, (void*)"range for grouping variable (X data)", 10, 39, 140, 9},
+		{102, 103, 0, 0x0L, RANGEINPUT, TmpTxt+100, 10, 49, 165, 10},
+		{103, 104, 0, 0x0L, LTEXT, (void*)"range for Y data", 10, 60, 90, 9},
+		{104, 150, 0, 0x0L, RANGEINPUT, TmpTxt+200, 10, 70, 165, 10},
+		{150, 160, 151, ISPARENT | CHECKED, GROUPBOX, (void*) " draw means ", 10, 87, 165, 45},
+		{151, 152, 0, 0x0L, CHECKBOX, (void*)" line", 15, 92, 50, 9},
+		{152, 153, 0, CHECKED, CHECKBOX, (void*)" symbols", 15, 101, 50, 9},
+		{153, 154, 0, 0x0L, LTEXT, (void*)"using", 65, 101, 30, 9},
+		{154, 155, 0, 0x0L, RADIO1, (void*)" arithmetic mean", 95, 90, 50, 9},
+		{155, 156, 0, 0x0L, RADIO1, (void*)" geometric mean", 95, 99, 50, 9},
+		{156, 157, 0, 0x0L, RADIO1, (void*)" harmonic mean", 95, 108, 50, 9},
+		{157, 0, 0, CHECKED, RADIO1, (void*)" median", 95, 117, 50, 9},
+		{160, 170, 161, ISPARENT | CHECKED, GROUPBOX, (void*) " draw boxes ", 10, 137, 165, 38},
+		{161, 162, 0, ISRADIO, CHECKBOX, (void*)" std. deviation (SD)", 15, 142, 70, 9},
+		{162, 163, 0, ISRADIO, CHECKBOX, (void*)" std. error (SEM)", 15, 151, 70, 9},
+		{163, 164, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" 25, 75% percentiles", 95, 142, 70, 9},
+		{164, 165, 0, ISRADIO, CHECKBOX, (void*)" min and max", 95, 151, 70, 9},
+		{165, 166, 0, ISRADIO, CHECKBOX, (void*)" ", 15, 161, 70, 9},
+		{166, 167, 0, 0x0L, EDVAL1, &ci_box, 28, 160, 15, 10},
+		{167, 0, 0, 0x0L, LTEXT, (void*) "%  conf. interval", 45, 161, 70, 9},
+		{170, 400, 171, ISPARENT | CHECKED, GROUPBOX, (void*) " draw whiskers ", 10, 180, 165, 38},
+		{171, 172, 0, ISRADIO, CHECKBOX, (void*)" std. deviation (SD)", 15, 185, 70, 9},
+		{172, 173, 0, ISRADIO, CHECKBOX, (void*)" std. error (SEM)", 15, 194, 70, 9},
+		{173, 174, 0, ISRADIO, CHECKBOX, (void*)" 25, 75% percentiles", 95, 185, 70, 9},
+		{174, 175, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" min and max", 95, 194, 70, 9},
+		{175, 176, 0, ISRADIO, CHECKBOX, (void*)" ", 15, 204, 70, 9},
+		{176, 177, 0, 0x0L, EDVAL1, &ci_err, 28, 203, 15, 10},
+		{177, 0, 0, 0x0L, LTEXT, (void*) "%  conf. interval", 45, 203, 70, 9},
+		{200, 202, 0, 0x0L, LTEXT, (void*)"range for common X values", 10, 39, 140, 9},
+		{202, 250, 0, 0x0L, RANGEINPUT, TmpTxt+100, 10, 49, 165, 10},
+		{250, 260, 251, ISPARENT | CHECKED, GROUPBOX, (void*) "                        ", 10, 68, 165, 30},
+		{251, 252, 0, 0x0L, CHECKBOX, (void*)" draw line", 15, 63, 50, 9},
+		{252, 253, 0, 0x0L, LTEXT, (void*)"range for line values", 15, 73, 80, 9},
+		{253, 0, 0, 0x0L, RANGEINPUT, TmpTxt+200, 15, 83, 155, 10},
+		{260, 270, 261, ISPARENT | CHECKED, GROUPBOX, (void*) "                               ", 10, 106, 165, 30},
+		{261, 262, 0, CHECKED, CHECKBOX, (void*)" draw symbols", 15, 101, 50, 9},
+		{262, 263, 0, 0x0L, LTEXT, (void*)"range for symbol values", 15, 111, 80, 9},
+		{263, 0, 0, 0x0L, RANGEINPUT, TmpTxt+200, 15, 121, 155, 10},
+		{270, 280, 271, ISPARENT | CHECKED, GROUPBOX, (void*) "                          ", 10, 144, 165, 50},
+		{271, 272, 0, CHECKED, CHECKBOX, (void*)" draw boxes", 15, 139, 50, 9},
+		{272, 273, 0, 0x0L, LTEXT, (void*)"range for HI values", 15, 149, 80, 9},
+		{273, 274, 0, 0x0L, RANGEINPUT, TmpTxt+300, 15, 159, 155, 10},
+		{274, 275, 0, 0x0L, LTEXT, (void*)"range for LO values", 15, 169, 80, 9},
+		{275, 0, 0, 0x0L, RANGEINPUT, TmpTxt+400, 15, 179, 155, 10},
+		{280, 0, 281, ISPARENT | CHECKED, GROUPBOX, (void*) "                              ", 10, 202, 165, 50},
+		{281, 282, 0, CHECKED, CHECKBOX, (void*)" draw whiskers", 15, 197, 50, 9},
+		{282, 283, 0, 0x0L, LTEXT, (void*)"range for HI values", 15, 207, 80, 9},
+		{283, 284, 0, 0x0L, RANGEINPUT, TmpTxt+500, 15, 217, 155, 10},
+		{284, 285, 0, 0x0L, LTEXT, (void*)"range for LO values", 15, 227, 80, 9},
+		{285, 0, 0, 0x0L, RANGEINPUT, TmpTxt+600, 15, 237, 155, 10},
+		{400, 0, 401, ISPARENT | CHECKED, GROUPBOX, (void*) " number of cases ", 10, 223, 165, 30},
+		{401, 402, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" on top of error", 15, 228, 70, 9},
+		{402, 403, 0, ISRADIO, CHECKBOX, (void*)" on top of mean", 95, 228, 70, 9},
+		{403, 404, 0, 0x0L, LTEXT, (void*)"prefix:", 15, 238, 24, 9},
+		{404, 0, 0, LASTOBJ, EDTEXT, (void*)"n = ", 40, 237, 30, 10}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	bool bRet = false;
+	int i, j, k, k1, l, l1, n, ic, c, res, cb, width, height;
+	double x, y1, y2, dx, dy;
+	char errdesc[40], boxdesc[40], symdesc[40];
+	lfPOINT fp1, fp2;
+	TextDEF lbdef = {defs.Color(COL_TEXT), defs.Color(COL_BG), DefSize(SIZE_TEXT), 0.0f, 0.0f, 0,
+		TXA_HLEFT | TXA_VBOTTOM, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, TmpTxt};
+	AccRange *rX = 0L, *rY1 = 0L, *rY2 = 0L;
+
+	if(!parent || !data) return false;
+	UseRangeMark(data, 1, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400, TmpTxt+500, TmpTxt+600);
+	ci_box = ci_err = 95.0;
+	if(!(Dlg = new DlgRoot(PlotDlg, data)))return false;
+	TmpTxt[0] = TmpTxt[100] = 0;
+	//restore previous style
+	if(boxplot_mode_sel == 53) {
+		Dlg->ShowItem(61, false);			Dlg->SetCheck(53, 0L, true);
+		}
+	else {
+		Dlg->SetCheck(52, 0L, true);		Dlg->ShowItem(60, false);
+		}
+	hDlg = CreateDlgWnd("Box and Whisker Plot", 50, 50, 370, 550, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 0:
+			if(Dlg->GetCheck(10)) res=-1;
+			break;
+		case 52:	case 53:
+			boxplot_mode_sel = res;
+			if(res == 53) {
+				Dlg->ShowItem(60, true);	Dlg->ShowItem(61, false);
+				}
+			else {
+				Dlg->ShowItem(60, false);	Dlg->ShowItem(61, true);
+				}
+			Dlg->Command(CMD_REDRAW, 0L, 0L);	res=-1;
+			break;
+			}
+		}while (res <0);
+	if(res == 1) {
+		type = 0;				dirty = true;
+		if(Dlg->GetCheck(52) && Dlg->GetText(202, TmpTxt+100, 50) && TmpTxt[100] &&(rX = new AccRange(TmpTxt+100))) {
+			xRange = (char*)memdup(TmpTxt+100, ((int)strlen(TmpTxt+100))+2, 0);
+			n = rX->CountItems();	nPoints = n;
+			//  data line
+			if(n > 1 && Dlg->GetCheck(251) && Dlg->GetText(253, TmpTxt, TMP_TXT_SIZE)) {
+				TheLine = new DataLine(this, data, TmpTxt+100, TmpTxt);
+				bRet = true;
+				}
+			// symbols
+			if(n > 0 && Dlg->GetCheck(261) && Dlg->GetText(263, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0] 
+				&& (Symbols = (Symbol**)calloc(n, sizeof(Symbol*)))
+				&& (rY1 = new AccRange(TmpTxt))) {
+				yRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
+				rX->GetFirst(&i, &j);	rY1->GetFirst(&k, &l);
+				rX->GetNext(&i, &j);	rY1->GetNext(&k, &l);
+				ic = c = 0;
+				do {
+					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1)) {
+						if(Symbols[ic] = new Symbol(this, data, x, y1, SYM_PLUS, i, j, k, l))
+							Symbols[ic++]->idx = c;
+						}
+					c++;
+					}while(rX->GetNext(&i, &j) && rY1->GetNext(&k, &l));
+				delete rY1;		rY1 = 0L;
+				if(ic) bRet = true;
+				}
+			// boxes
+			if(n > 0 && Dlg->GetCheck(271) && Dlg->GetText(273, TmpTxt+300, 50) && Dlg->GetText(275, TmpTxt+400, 50)
+				&& (Boxes = (Box**)calloc(n, sizeof(Box*)))
+				&& (rY1 = new AccRange(TmpTxt+300)) && (rY2 = new AccRange(TmpTxt+400))) {
+				rX->GetFirst(&i, &j);	rY1->GetFirst(&k, &l);	rY2->GetFirst(&k1, &l1);
+				rX->GetNext(&i, &j);	rY1->GetNext(&k, &l);	rY2->GetNext(&k1, &l1);
+				ic = 0;
+				do {
+					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1) && data->GetValue(l1, k1, &y2)) {
+						fp1.fy = y1;	fp2.fy = y2;		fp1.fx = fp2.fx = x;
+						Boxes[ic] = new Box(this, data, fp1, fp2, BAR_RELWIDTH, i, j, k, l, i, j, k1, l1);
+						if(Boxes[ic]) Boxes[ic++]->SetSize(SIZE_BOX, 60.0);
+						}
+					}while(rX->GetNext(&i, &j) && rY1->GetNext(&k, &l) && rY2->GetNext(&k1, &l1));
+				delete rY1;		rY1 = 0L;		delete rY2;		rY2 = 0L;
+				if(ic) bRet = true;
+				}
+			// whiskers
+			if(n > 0 && Dlg->GetCheck(281) && Dlg->GetText(283, TmpTxt+300, 50) && Dlg->GetText(285, TmpTxt+400, 50)
+				&& (Whiskers = (Whisker**)calloc(n, sizeof(Whisker*)))
+				&& (rY1 = new AccRange(TmpTxt+300)) && (rY2 = new AccRange(TmpTxt+400))) {
+				rX->GetFirst(&i, &j);	rY1->GetFirst(&k, &l);	rY2->GetFirst(&k1, &l1);
+				rX->GetNext(&i, &j);	rY1->GetNext(&k, &l);	rY2->GetNext(&k1, &l1);
+				ic = 0;
+				do {
+					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1) && data->GetValue(l1, k1, &y2)) {
+						fp1.fy = y1;	fp2.fy = y2;		fp1.fx = fp2.fx = x;
+						Whiskers[ic++] = new Whisker(this, data, fp1, fp2, 0, i, j, k, l, i, j, k1, l1);
+						}
+					}while(rX->GetNext(&i, &j) && rY1->GetNext(&k, &l) && rY2->GetNext(&k1, &l1));
+				delete rY1;		rY1 = 0L;		delete rY2;		rY2 = 0L;
+				if(ic) bRet = true;
+				}
+			if (bRet) Command(CMD_AUTOSCALE, 0L, 0L);
+			}
+		else if(Dlg->GetText(102, TmpTxt+100, 50) && TmpTxt[100] && Dlg->GetText(104, TmpTxt+200, 50) && TmpTxt[200]){
+			xRange = (char*)memdup(TmpTxt+100, ((int)strlen(TmpTxt+100))+2, 0);
+			yRange = (char*)memdup(TmpTxt+200, ((int)strlen(TmpTxt+200))+2, 0);
+			if((rX = new AccRange(xRange)) && (rY1 = new AccRange(yRange))) {
+				x_info = rX->RangeDesc(data, 2);		y_info = rY1->RangeDesc(data, 2);
+				delete rX;		delete rY1;		rX = rY1 = 0L;
+				}
+			if(Dlg->GetCheck(154)) type |= 0x0001;		if(Dlg->GetCheck(155)) type |= 0x0002;
+			if(Dlg->GetCheck(156)) type |= 0x0003;		if(Dlg->GetCheck(157)) type |= 0x0004;
+			if(Dlg->GetCheck(161)) type |= 0x0010;		if(Dlg->GetCheck(162)) type |= 0x0020;
+			if(Dlg->GetCheck(163)) type |= 0x0030;		if(Dlg->GetCheck(164)) type |= 0x0040;
+			if(Dlg->GetCheck(165)) type |= 0x0050;
+			if(Dlg->GetCheck(171)) type |= 0x0100;		if(Dlg->GetCheck(172)) type |= 0x0200;
+			if(Dlg->GetCheck(173)) type |= 0x0300;		if(Dlg->GetCheck(174)) type |= 0x0400;
+			if(Dlg->GetCheck(175)) type |= 0x0500;
+			if(Dlg->GetCheck(151)) type |= 0x1000;		if(Dlg->GetCheck(152)) type |= 0x2000;
+			if(Dlg->GetCheck(401)) type |= 0x4000;		if(Dlg->GetCheck(402)) type |= 0x8000;
+			Dlg->GetValue(166, &ci_box);				Dlg->GetValue(176, &ci_err);
+			if(Dlg->GetText(404, TmpTxt, TMP_TXT_SIZE)) case_prefix = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
+			CreateData();
+			if(curr_data && type) {
+				curr_data->GetSize(&width, &height);
+#ifdef USE_WIN_SECURE
+				sprintf_s(TmpTxt+100, 50, "a1:a%d", height);	sprintf_s(TmpTxt+200, 50, "b1:b%d", height);
+#else	
+				sprintf(TmpTxt+100, "a1:a%d", height);			sprintf(TmpTxt+200, "b1:b%d", height);
+#endif
+				nPoints = height;
+				if(nPoints > 1 && (type & 0x1000)) {
+					TheLine = new DataLine(this, curr_data, TmpTxt+100, TmpTxt+200);
+					bRet = true;
+					}
+				if(nPoints > 0 && (type & 0x2000) && (Symbols = (Symbol**)calloc(nPoints, sizeof(Symbol*)))) {
+					switch(type & 0x000f) {
+					case 0x0001:	cb = rlp_strcpy(symdesc, 40, "Mean");				break;
+					case 0x0002:	cb = rlp_strcpy(symdesc, 40, "Geometric mean");		break;
+					case 0x0003:	cb = rlp_strcpy(symdesc, 40, "Harmonic mean");		break;
+					case 0x0004:	cb = rlp_strcpy(symdesc, 40, "Median");				break;
+					default:		cb = rlp_strcpy(symdesc, 40, "n.a.");				break;
+						}
+					for(i = 0; i < height; i++) {
+						if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 1, &y1)
+							&& (Symbols[i] = new Symbol(this, curr_data, x, y1, SYM_PLUS, 0, i, 1, i))){
+							Symbols[i]->idx = i;
+							Symbols[i]->name = (char*)memdup(symdesc, cb+1, 0);
+							}
+						}
+					bRet = true;
+					}
+				if(nPoints > 0 && (type & 0x00f0) && (Boxes = (Box**)calloc(nPoints, sizeof(Box*)))) {
+					switch(type & 0x00f0) {
+					case 0x0010:	cb = rlp_strcpy(boxdesc, 40, "Std. Dev.");			break;
+					case 0x0020:	cb = rlp_strcpy(boxdesc, 40, "Std. Err.");			break;
+					case 0x0030:	cb = rlp_strcpy(boxdesc, 40, "25, 75% Perc.");		break;
+					case 0x0040:	cb = rlp_strcpy(boxdesc, 40, "Min./Max.");			break;
+#ifdef USE_WIN_SECURE
+					case 0x0500:	cb = sprintf_s(boxdesc, 40, "'%g%% CI", ci_err);	break;
+#else
+					case 0x0500:	cb = sprintf(boxdesc, "'%g%% CI", ci_err);			break;
+#endif
+					default:		cb = rlp_strcpy(boxdesc, 40, "n.a.");
+						}
+					for(i = 0; i < height; i++) {
+						if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 2, &y1)
+							&&	curr_data->GetValue(i, 3, &y2)) {
+							fp1.fy = y1;	fp2.fy = y2;		fp1.fx = fp2.fx = x;
+							Boxes[i] = new Box(this, curr_data, fp1, fp2, BAR_RELWIDTH, 0, i, 2, i, 0, i, 3, i);
+							if(Boxes[i]){
+								Boxes[i]->SetSize(SIZE_BOX, 60.0);
+								Boxes[i]->name = (char*)memdup(boxdesc, cb+1, 0);
+								}
+							}
+						}
+					bRet = true;
+					}
+				if(nPoints > 0 && (type & 0x0f00) && (Whiskers = (Whisker**)calloc(nPoints, sizeof(Whisker*)))) {
+					switch(type & 0x0f00) {
+					case 0x0100:	rlp_strcpy(errdesc, 40, "Std. Dev.");			break;
+					case 0x0200:	rlp_strcpy(errdesc, 40, "Std. Err.");			break;
+					case 0x0300:	rlp_strcpy(errdesc, 40, "25, 75% Perc.");		break;
+					case 0x0400:	rlp_strcpy(errdesc, 40, "Min./Max.");			break;
+#ifdef USE_WIN_SECURE
+					case 0x0500:	sprintf_s(errdesc, 40, "'%g%% CI", ci_err);		break;
+#else
+					case 0x0500:	sprintf(errdesc, "'%g%% CI", ci_err);			break;
+#endif
+					default:		rlp_strcpy(errdesc, 40, "error");
+						}
+					for(i = 0; i < height; i++) {
+						if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 4, &y1)
+							&&	curr_data->GetValue(i, 5, &y2)) {
+							fp1.fy = y1;	fp2.fy = y2;		fp1.fx = fp2.fx = x;
+							Whiskers[i] = new Whisker(this, curr_data, fp1, fp2, 0, 0, i, 4, i, 0, i, 5, i);
+							if(Whiskers[i]) Whiskers[i]->Command(CMD_ERRDESC, errdesc, 0L);
+							}
+						}
+					bRet = true;
+					}
+				if(nPoints > 0 && (type & 0xc000) && (Labels = (Label**)calloc(nPoints, sizeof(Label*)))) {
+					dy = -0.4 * DefSize(SIZE_SYMBOL);
+					if(type & 0x4000){
+						lbdef.Align = TXA_HCENTER | TXA_VBOTTOM;
+						dx = 0.0;
+						}
+					else {
+						lbdef.Align = TXA_HLEFT | TXA_VBOTTOM;
+						dx = -dy;
+						}
+					for(i = 0; i < height; i++) {
+						if(curr_data->GetValue(i, 0, &x) && curr_data->GetText(i, 7, TmpTxt, TMP_TXT_SIZE)){
+							if(curr_data->GetValue(i, 6, &y1))Labels[i] = new Label(this, curr_data, x, y1, &lbdef, 
+								LB_X_DATA | LB_Y_DATA, 0, i, 6, i, 7, i);
+							if(Labels[i]){
+								Labels[i]->SetSize(SIZE_LB_YDIST, dy);	Labels[i]->SetSize(SIZE_LB_XDIST, dx);
+								}
+							}
+						}
+					bRet = true;
+					}
+				}
+			}
+		}
+	if(bRet) {
+		dirty = true;
+		Command(CMD_AUTOSCALE, 0L, 0L);
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rX) delete rX;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create density distribution plot 
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+DensDisp::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 52, 10, "Style"};
+	char text1[100], text2[100];
+	DlgInfo PlotDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 148, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 148, 25, 45, 12},
+		{3, 0, 4, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
+		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 130, 100},
+		{5, 10, 200, TOUCHEXIT | ISPARENT, SHEET, &tab2, 5, 10, 130, 100},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"range for direction (time, depth) data", 10, 30, 60, 8},
+		{101, 102, 0, 0x0L, RANGEINPUT, text1, 20, 40, 100, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void*)"range for width (density) data", 10, 55, 60, 8},
+		{103, 104, 0, 0x0L, RANGEINPUT, text2, 20, 65, 100, 10},
+		{104, 0, 0, 0x0L, CHECKBOX, (void*)"vertical profile", 10, 90, 100, 8},
+		{200, 201, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 25, 30, 90, 45},
+		{201, 202, 0, TOUCHEXIT | CHECKED, RADIO1, (void*)"symmetric bars", 25, 80, 60, 8},
+		{202, 203, 0, TOUCHEXIT, RADIO1, 0L, 25, 88, 60, 8},
+		{203, 0, 0, TOUCHEXIT | LASTOBJ, RADIO1, 0L, 25, 96, 60, 9}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int n, res, align = 0;
+	bool bRet = false, bContinue = false, bVert;
+	AccRange *rX = 0L, *rY = 0L;
+
+	if(!parent || !data) return false;
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)defs.GetOutLine(), 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)defs.GetFill(), 0);
+	UseRangeMark(data, 1, text1, text2);
+	if(!(Dlg = new DlgRoot(PlotDlg, data)))return false;
+	hDlg = CreateDlgWnd("Density profile", 50, 50, 420, 260, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 5:
+			if(Dlg->GetCheck(104)) {
+				Dlg->SetText(202, "bars right");	Dlg->SetText(203, "bars left");
+				}
+			else {
+				Dlg->SetText(202, "bars up");		Dlg->SetText(203, "bars down");
+				}
+			res = -1;
+			break;
+		case 201:	case 202:	case 203:
+			align = res - 201;
+			res = -1;
+			break;
+		case 1:
+			if(rX) delete rX;	if(rY) delete rY;
+			rX = rY = 0L;
+			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) rX = new AccRange(TmpTxt);
+			n = rX ? rX->CountItems() : 0;
+			if(!n) {
+				ErrorBox("direction range not specified\nor not valid.");
+				bContinue = true;
+				res = -1;
+				}
+			if(n && Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE) && (rY = new AccRange(TmpTxt))){
+				if(n != rY->CountItems()) {
+					ErrorBox("both ranges must be given\nand must have the same size");
+					bContinue = true;
+					res = -1;
+					}
+				}
+			}
+		}while (res < 0);
+	if(res == 1 && n && rX && rY) {
+		if(Dlg->GetCheck(104)) {
+			y_info = rX->RangeDesc(data, 0);		x_info = rY->RangeDesc(data, 0);
+			}
+		else {
+			x_info = rX->RangeDesc(data, 0);		y_info = rY->RangeDesc(data, 0);
+			}
+		type = (bVert = Dlg->GetCheck(104)) ? align | 0x10 : align;
+		if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) xRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
+		if(Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE)) yRange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
+		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&DefLine, 0);
+		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&DefFill, 0);
+		if(DefFill.hatch) memcpy(&DefFillLine, DefFill.hatch, sizeof(LineDEF));
+		DefFill.hatch = &DefFillLine;
+		DoUpdate();
+		bRet = true;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rX) delete rX;		if(rY) delete rY;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create stacked bar or stacked polygon 
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+static char *StackBar_DlgTmpl =
+	"1,2,,DEFAULT,PUSHBUTTON,-1,158,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,158,25,45,12\n"
+	"3,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"10,11,100,ISPARENT | CHECKED, SHEET,1,5,10,140,100\n"
+	"11,12,200,ISPARENT,SHEET,2,5,10,140,100\n"
+	"12,20,300,ISPARENT,SHEET,3,5,10,140,100\n"
+	"20,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,101,,,LTEXT,4,15,30,60,8\n"
+	"101,152,,,RANGEINPUT,5,25,40,100,10\n"
+	"152,153,,ISPARENT | CHECKED,GROUPBOX,6, 12, 60, 128, 45\n"
+	"153,154,,,LTEXT,0,25,65,60,8\n"
+	"154,155,,,RANGEINPUT,5,25,75,100,10\n"
+	"155,156,0,,PUSHBUTTON,-8,95,87,30,12\n"
+	"156,,,,PUSHBUTTON,-9,60,87,35,12\n"
+	"200,201,,CHECKED,RADIO1,7,25,35,60,8\n"
+	"201,202,,,RADIO1,8,25,50,60,8\n"
+	"202,203,,,RTEXT,9,31,65,38,8\n"
+	"203,204,,,EDVAL1,10,70,65,30,10\n"
+	"204,,,,CHECKBOX,11,25,90,60,8\n"
+	"300,,,LASTOBJ | NOSELECT, ODBUTTON,12,20,35,80,60";
+
+bool
+StackBar::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 55, 10, "Details"};
+	TabSHEET tab3 = {55, 90, 10, "Scheme"};
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)"range for common x values",
+		(void*)TmpTxt, (void*)" ranges for y values ", (void*)" add each y to start value",
+		(void*)" subtract each y from start value", (void*)"start value:", (void*)&StartVal,
+		(void*)" horizontal plot", (void*)(OD_scheme)};
+	DlgInfo *StackBarDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, sc, j, res, currYR = 0, maxYR = 0, nx = 0, ny, c_num, c_txt, c_datetime;
+	bool updateYR = true, bContinue = false, bSub, bRet = false, bHor;
+	char **rd = 0L, *rname;
+	AccRange *rX = 0L, *rY = 0L;
+	TextValue *tv = 0L;
+
+	if(!parent || !data) return false;
+	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
+		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return false;
+	if(!(StackBarDlg = CompileDialog(StackBar_DlgTmpl, dyndata))) return false;
+	if(TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
+		for(i=100, j= 0; i <= 1000; i +=100) if(TmpTxt[i]) 
+			rd[j++] = (char*)memdup(TmpTxt+i, ((int)strlen(TmpTxt+i))+2, 0);	 maxYR = j-1;
+		}
+	if(!rd && !(rd = (char**)calloc(1, sizeof(char*))))return false;
+	if(!(Dlg = new DlgRoot(StackBarDlg, data))) return false;
+	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
+	hDlg = CreateDlgWnd(Id == GO_STACKBAR ? (char*)"Stacked Bar Plot" : 
+		(char*)"Stacked Polygons", 50, 50, 420, 260, Dlg, 0x4L);
+	do {
+		if(updateYR) {
+			if(currYR >0) {
+				Dlg->ShowItem(156, true);				Dlg->Activate(101, false);
+				}
+			else {
+				Dlg->ShowItem(156, false);				Dlg->Activate(101, true);
+				}
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "y-values # %d/%d", currYR+1, maxYR+1);
+#else
+			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
+#endif
+			//SetText will also cause a redraw of the whole dialog
+			Dlg->SetText(153, TmpTxt);
+			updateYR = false;
+			}
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		ny = 0;
+		if(rX) delete rX;		rX = 0L;
+		switch(res) {
+		case 0:
+			if(bContinue || Dlg->GetCheck(20)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 1:		
+			Dlg->GetValue(203, &StartVal);
+			bSub = Dlg->GetCheck(201);			bHor = Dlg->GetCheck(204);
+		case 155:		case 156:
+			res = com_StackDlg(res, Dlg, &rX, &nx, &rd, &currYR,
+				&rY, &bContinue, &ny, &maxYR, &updateYR);
+			break;
+			}
+		}while (res < 0);
+	if(res == 1 && nx && rX && rd && rd[0] && rd[0][0]){	//accept settings and create plot
+		maxYR++;
+		for(i = j = 0; i < maxYR; i++) {
+			if(i) TmpTxt[j++] = '&';
+			j+= rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, rd[i]);
+			}
+		rX->DataTypes(data, &c_num, &c_txt, &c_datetime);
+		if(!c_num && (c_txt + c_datetime) > 0 ) tv = new TextValue();
+		if(Dlg->GetCheck(204)) {
+			y_info = rX->RangeDesc(data, 3);	y_tv = tv;
+			}
+		else {
+			x_info = rX->RangeDesc(data, 3);	x_tv = tv;
+			}
+		ssYrange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+1, 0);
+		if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) ssXrange = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+1, 0);;
+		cum_data_mode = Dlg->GetCheck(200) ? 1 : 2;
+		if(Id == GO_STACKPG) cum_data_mode += 2;
+		CumData = CreaCumData(ssXrange, ssYrange, cum_data_mode, StartVal);
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		//do stacked bar
+		if(rY)delete rY;	rY = 0L;
+		if(Id == GO_STACKBAR){
+			numPlots = maxYR;
+			if(Boxes = (BoxPlot**)calloc(numPlots, sizeof(BoxPlot*))) 
+				for(i = sc = 0; i < (maxYR) && rd[i] && *rd[i]; i++) {
+					rY = new AccRange(rd[i]);				rname = rY->RangeDesc(data, 3);
+					if(Boxes[i]= new BoxPlot(0L, CumData, Dlg->GetCheck(204)? 2:1, 0, i+1, i+2, rname)){
+					Boxes[i]->Command(CMD_UPDATE, 0L, 0L);	Boxes[i]->parent = this;
+					Boxes[i]->Command(CMD_AUTOSCALE, 0L, 0L);
+					Boxes[i]->Command(CMD_BOX_FILL, GetSchemeFill(&sc), 0L);
+					Boxes[i]->SetSize(SIZE_BOX, 60.0);
+					if(rname) free(rname);	delete rY;	rY = 0L;
+					}
+				}
+			}
+		//do stacked polygon
+		else if(Id == GO_STACKPG){
+			numPG = maxYR;
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, 20, "a1:a%d", nx*2);
+#else
+			sprintf(TmpTxt, "a1:a%d", nx*2);
+#endif
+			if(Polygons=(DataPolygon**)calloc(numPG,sizeof(DataPolygon*)))
+				for(i=sc=0; i < maxYR && rd[i] && *rd[i];i++){
+#ifdef USE_WIN_SECURE
+				sprintf_s(TmpTxt+20, 20, "%c1:%c%d", 'c'+i, 'c'+i, nx*2);
+#else
+				sprintf(TmpTxt+20, "%c1:%c%d", 'c'+i, 'c'+i, nx*2);
+#endif
+				rY = new AccRange(rd[i]);				rname = rY->RangeDesc(data, 3);
+				if(Dlg->GetCheck(204)) Polygons[i]=new DataPolygon(this,CumData,TmpTxt+20,TmpTxt, rname);
+				else Polygons[i] = new DataPolygon(this, CumData, TmpTxt, TmpTxt+20, rname);
+				if(Polygons[i]) {
+					Polygons[i]->Command(CMD_AUTOSCALE, 0L, 0L);
+					Polygons[i]->Command(CMD_PG_FILL, GetSchemeFill(&sc), 0L);
+					}
+				if(rname) free(rname);	delete rY;	rY = 0L;
+				}
+			}
+		if(Bounds.Xmax >= Bounds.Xmin && Bounds.Ymax >= Bounds.Ymin) bRet = true;
+		else bRet = false;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rd) {
+		for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
+		free(rd);
+		}
+	if(rX) delete rX;		if(rY) delete rY;		free(StackBarDlg);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create grouped bars chart
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *GBDlg_Tmpl =
+	"1,+,,DEFAULT,PUSHBUTTON,-1,140,10,45,12\n"
+	".,.,,,PUSHBUTTON,-2,140,25,45,12\n"
+	".,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,+,99,ISPARENT | CHECKED | TOUCHEXIT,SHEET,1,5,10,128,100\n"
+	".,.,200,ISPARENT,SHEET,2,5,10,128,100\n"
+	".,.,300,ISPARENT,SHEET,3,5,10,128,100\n"
+	".,10,250,ISPARENT | TOUCHEXIT,SHEET,16,5,10,128,100\n"
+	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"99,100,110,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"100,,160,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"101,+,,,LTEXT,0,25,39,60,8\n"
+	".,.,,,RANGEINPUT,-15,25,49,100,10\n"
+	".,.,,,LTEXT,0,25,61,60,8\n"
+	".,.,,,RANGEINPUT,-16,25,71,100,10\n"
+	".,.,,,PUSHBUTTON,-8,95,87,30,12\n"
+	".,,,,PUSHBUTTON,-9,60,87,35,12\n"
+	"110,+,,,LTEXT,4,10,25,60,8\n"
+	".,150,,,LTEXT,5,10,33,60,8\n"
+	"150,,153,ISPARENT | CHECKED,GROUPBOX,6,10,50,118,55\n"
+	"160,101,,ISPARENT | CHECKED,GROUPBOX,19,10,30,118,75\n"
+	"153,+,,,LTEXT,0,15,60,60,8\n"
+	".,.,,,RANGEINPUT,-1,15,70,108,10\n"
+	".,.,,,PUSHBUTTON,-8,93,87,30,12\n"
+	".,,,,PUSHBUTTON,-9,58,87,35,12\n"
+	"200,+,,,RTEXT,7,10,35,38,8\n"
+	".,.,,,EDVAL1,8,58,35,35,10\n"
+	".,.,,,RTEXT,9,10,50,38,8\n"
+	".,.,,,EDVAL1,10,58,50,35,10\n"
+	".,.,,,RTEXT,11,10,65,38,8\n"
+	".,.,,,EDVAL1,12,58,65,35,10\n"
+	".,.,,,LTEXT,-10,95,65,8,8\n"
+	".,.,,,RTEXT,13,10,80,38,8\n"
+	".,.,,,EDVAL1,14,58,80,35,10\n"
+	".,,,,LTEXT,-10,95,80,8,8\n"
+	"250,+,TOUCHEXIT,CHECKED,RADIO1,17,20,35,70,8\n"
+	".,.,,TOUCHEXIT,RADIO1,18,20,50,70,8\n"
+	".,,,,RANGEINPUT,0,15,65,108,10\n"
+	"300,,,LASTOBJ | NOSELECT,ODBUTTON,15,24,30,90,60";
+
+bool
+GroupBars::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 27, 10, "Data"};
+	TabSHEET tab2 = {27, 59, 10, "Details"};
+	TabSHEET tab3 = {59, 96, 10, "Scheme"};
+	TabSHEET tab4 = {96, 128, 10, "Labels"};
+	double start = 1.0, step = 1.0, bw = 100.0, gg = 100.0;
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)"Get values from spreadsheet:",
+		(void*)"All ranges should have equal size!", (void*)" ranges for y values ", (void*)"start value",
+		(void*)&start, (void*)"group step", (void*)&step, (void*)"bar width", (void*)&bw,
+		(void*)"group gap", (void*)&gg, (void*)(OD_scheme), (void*)&tab4, (void*)" no group labels",
+		(void*)"from spreadsheet range:", (void*)" ranges for y- and error- data "};
+	DlgInfo *GBDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	bool bRet = false, updateYR = true, bContinue = false, bError;
+	char **rd=0L, **rdx=0L, **rdy=0L, *desc;
+	Bar **bars = 0L;
+	ErrorBar **errs = 0L;
+	AccRange *rX = 0L, *rY = 0L;
+	anyResult ares;
+	int i, j, ic, res, ix, iy, ex, ey, ny, s1, s2, sc = 0, currYR = 0, maxYR = 0;
+	double x, y, xinc, e, ebw;
+	
+	if(!parent || !data) return false;
+	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
+		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return false;
+	if(!(GBDlg = CompileDialog(GBDlg_Tmpl, dyndata)))return false;
+	if(mode == 0 && TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
+		for(i=0, j= 0; i <= 1000; i +=100) 
+			if(TmpTxt[i]) rd[j++] = (char*)memdup(TmpTxt+i, ((int)strlen(TmpTxt+i))+2, 0);
+			maxYR = j-1;
+		}
+	else if(mode == 1 && TmpTxt[0] && TmpTxt[100] && (rdx = (char**)calloc(12, sizeof(char*))) 
+		&& (rdy = (char**)calloc(12, sizeof(char*)))) {
+		for(i=j=0; i <= 1000; i +=200) if(TmpTxt[i]) {
+			rdx[j] = (char*)memdup(TmpTxt+i, (int)strlen(TmpTxt)+1, 0);	
+			rdy[j] = (char*)memdup(TmpTxt+i+100, (int)strlen(TmpTxt+i+100)+1, 0);		maxYR = j++;
+			}
+		}
+	Id = GO_STACKBAR;
+	if(mode == 0 && !rd && !(rd = (char**)calloc(1, sizeof(char*))))return false;
+	if(mode == 1 && !rdx && !rdy && !(rdx = (char**)calloc(1, sizeof(char*))) 
+		&& !(rdy = (char**)calloc(1, sizeof(char*))))return false;
+	if(!(Dlg = new DlgRoot(GBDlg, data)))return false;
+	if(mode == 1) {
+		Dlg->ShowItem(99, false);		Dlg->ShowItem(100, true);
+		}
+	else {
+		Dlg->ShowItem(99, true);		Dlg->ShowItem(100, false);
+		}
+	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
+	else Dlg->SetText(154, "");
+	hDlg = CreateDlgWnd(mode == 0 ? (char*)"Grouped Bar Chart" : (char*)"Grouped Bar Chart with Error Bars", 50, 50, 390, 260, Dlg, 0x4L);
+	do {
+		if(updateYR) {
+			if(currYR >0) {
+				Dlg->ShowItem(156, true);		Dlg->ShowItem(106, true);
+				}
+			else {
+				Dlg->ShowItem(156, false);		Dlg->ShowItem(106, false);		
+				}
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, 20, "y-values # %d/%d", currYR+1, maxYR+1);
+			sprintf_s(TmpTxt+100, 20, "errors # %d/%d", currYR+1, maxYR+1);
+#else
+			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
+			sprintf(TmpTxt+100,"errors # %d/%d", currYR+1, maxYR+1);
+#endif
+			//SetText will also cause a redraw of the whole dialog
+			if(mode == 0) 	Dlg->SetText(153, TmpTxt);
+			else {
+				Dlg->SetText(101, TmpTxt);		Dlg->SetText(103, TmpTxt+100);
+				}
+			updateYR = false;
+			}
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue || Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 250:		case 251:
+		case 7:
+			Dlg->Activate(252, true);
+			res = -1;			break;
+		case 4:
+			Dlg->Activate(mode ? 102:154, true);
+			res = -1;			break;
+		case 1:		
+			Dlg->GetValue(201, &start);			Dlg->GetValue(203, &step);
+			Dlg->GetValue(205, &bw);			Dlg->GetValue(208, &gg);
+			res = com_StackDlg(res, Dlg, 0L, 0L, &rd, &currYR,
+				&rY, &bContinue, &ny, &maxYR, &updateYR);
+			if(!mode) break;
+		case 105:										//next button
+			bError=false;	s1 = s2 = 0;
+			if(Dlg->GetText(102, TmpTxt, 100)) {
+				if(rX = new AccRange(TmpTxt)) {
+					s1 = rX->CountItems();
+					if(s1 < 2) {
+						ErrorBox("y-range not valid");
+						bContinue=bError=true;
+						Dlg->Activate(102, true);
+						}
+					delete rX;
+					}
+				else bError = true;
+				}
+			else bError = true;
+			if(Dlg->GetText(104, TmpTxt+100, 100) && !bError) {
+				if(rY = new AccRange(TmpTxt+100)) {
+					s2 = rY->CountItems();
+					if(s2 < 2) {
+						ErrorBox("error-range not valid");
+						bContinue=bError=true;
+						Dlg->Activate(104, true);
+						}
+					delete rY;
+					}
+				else bError = true;
+				}
+			else {
+				Dlg->Activate(104, true);
+				bError = true;
+				}
+			if(!s1 || !s2) bError = true;
+			rX = rY = 0L;
+			if(!bError && s1!=s2) {
+				ErrorBox("Y-range and error-range are\ndifferent in size");
+				bContinue=bError=true;
+				}
+			if(!bError) {
+				if((currYR+1) > maxYR) {
+					rdx = (char**)realloc(rdx, sizeof(char*)*(currYR+2));
+					rdy = (char**)realloc(rdy, sizeof(char*)*(currYR+2));
+					rdx[currYR] = rdx[currYR+1] = rdy[currYR] = rdy[currYR+1] = 0L;
+					maxYR = currYR+1;
+					}
+				if(rdx[currYR]) free(rdx[currYR]);		//store x-range
+				rdx[currYR] = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+				if(rdy[currYR]) free(rdy[currYR]);		//store y range
+				rdy[currYR] = (char*)memdup(TmpTxt+100, (int)strlen(TmpTxt+100)+1, 0);
+				updateYR = true;						currYR++;
+				Dlg->SetText(102, rdx[currYR]);			Dlg->SetText(104, rdy[currYR]);
+				Dlg->Activate(102, true);				
+				if(res != 1) res = -1;
+				}
+			else if(res != 1){
+				bContinue = true;
+				res = -1;
+				}
+			break;
+		case 106:										//prev button
+			if(Dlg->GetText(102, TmpTxt, 100) && Dlg->GetText(104, TmpTxt+100, 100)){
+				if(rdx[currYR]) free(rdx[currYR]);		if(rdy[currYR]) free(rdy[currYR]);
+				rdx[currYR] = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+				rdy[currYR] = (char*)memdup(TmpTxt+100, (int)strlen(TmpTxt+100)+1, 0);
+				}
+			else if(currYR == maxYR) maxYR--;
+			currYR--;	
+			Dlg->SetText(102, rdx[currYR]);
+			Dlg->SetText(104, rdy[currYR]);				Dlg->Activate(102, true);
+			updateYR = true;
+			res = -1;
+			break;
+		case 155:		case 156:
+			res = com_StackDlg(res, Dlg, 0L, 0L, &rd, &currYR,
+				&rY, &bContinue, &ny, &maxYR, &updateYR);
+			break;
+			}
+		} while(res < 0);
+	if(res == 1 && ((rd && rd[0] && rd[0][0])||(rdx && rdy && rdx[0] && rdy[0] && rdx[0][0] && rdy[0][0]))){
+		//accept settings and create plots
+		if((mode == 0 && rd[maxYR]) || (mode == 1 && rdx[maxYR])) maxYR++;
+		ebw = defs.GetSize(SIZE_ERRBAR)*9.0/((double)(s1*maxYR));
+		if(Dlg->GetCheck(251) && Dlg->GetText(252, TmpTxt, 100) && (rX = new AccRange(TmpTxt))
+			&& rX->GetFirst(&ix, &iy)){
+			x_tv = new TextValue();				rX->GetNext(&ix, &iy);
+			i = 1;								start = step = 1.0;
+			do {
+				if(data->GetResult(&ares, iy, ix, false)) switch(ares.type) {
+					case ET_TEXT:
+						x_tv->GetValue(ares.text);			break;
+					case ET_VALUE:		case ET_DATE:		case ET_TIME:
+					case ET_DATETIME:	case ET_BOOL:
+						TranslateResult(&ares);
+						x_tv->GetValue(ares.text);			break;
+					}
+				i++;
+				}while(rX->GetNext(&ix, &iy));
+			}
+		if(rX) delete rX;							rX = 0L;
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		if(!(xyPlots = (PlotScatt**)calloc(maxYR, sizeof(PlotScatt*)))){
+			CloseDlgWnd(hDlg);
+			delete Dlg;
+			if(rd) {
+				for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
+				free(rd);
+				}
+			return false;
+			}
+		xinc = fabs(step / ((double)maxYR + gg/100.0));
+		start -= (xinc * ((double)maxYR-1))/2.0;
+		for(i = 0, desc = 0L; i < maxYR; i++) {
+			x = start + xinc * (double)i;
+			if(mode == 0 && rd) {
+				if(rd[i] && (rY = new AccRange(rd[i]))) ny = rY->CountItems();
+				else {
+					rY = 0L;	ny = 0;
+					}
+				}
+			else if(mode == 1 && rdx && rdy) {
+				if(rdx[i] && (rY = new AccRange(rdx[i]))) ny = rY->CountItems();
+				else {
+					rY = 0L;	ny = 0;
+					}
+				}
+			if(rY) {
+				desc = rY->RangeDesc(data, 1);
+				rY->GetFirst(&ix, &iy);
+				if(mode == 1 && (rX = new AccRange(rdy[i]))) {
+					errs = (ErrorBar**)calloc(ny, sizeof(ErrorBar*)); 
+					rX->GetFirst(&ex, &ey);
+					}
+				rY->GetNext(&ix, &iy);
+				if(ny && rY && (bars = (Bar **)calloc(ny, sizeof(Bar*)))){
+					for(ic = 0; ic < ny; ic++) {
+						if(bContinue = data->GetValue(iy, ix, &y)){
+							bars[ic] = new Bar(0L, data, x, y, BAR_VERTB | BAR_RELWIDTH,
+								-1, -1, ix, iy, desc);
+							CheckBounds(x, y);
+							}
+						if(errs && rX && rX->GetNext(&ex, &ey) && data->GetValue(ey, ex, &e)) {
+							if(bContinue && (errs[ic] = new ErrorBar(0L, data, x, y, e, 0, -1, -1, ix, iy, ex, ey)))
+								errs[ic]->SetSize(SIZE_ERRBAR, ebw);
+							CheckBounds(x, y+e);	CheckBounds(x, y-e);
+							}
+						x += step;
+						rY->GetNext(&ix, &iy);
+						}
+					xyPlots[numXY++] = new PlotScatt(this, data, ic, bars, errs);
+					if(xyPlots[i]) {
+						xyPlots[i]->SetSize(SIZE_BARMINX, xinc);
+						xyPlots[i]->SetSize(SIZE_BAR, bw);
+						xyPlots[i]->Command(CMD_BAR_FILL, GetSchemeFill(&sc), 0L);
+						}
+					for(ic = 0; ic < ny; ic++) if(bars[ic]) delete(bars[ic]);
+					if(errs) for(ic = 0; ic < ny; ic++) if(errs[ic]) delete(errs[ic]);
+					free(bars);		if(errs) free(errs);	bRet = true;
+					}
+				delete(rY);		if(desc) free(desc);		rY = 0L;
+				}
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rd) {
+		for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
+		free(rd);
+		}
+	if(rdx) {
+		for (i = 0; i < maxYR; i++)	if(rdx[i]) free(rdx[i]);
+		free(rdx);
+		}
+	if(rdy) {
+		for (i = 0; i < maxYR; i++)	if(rdy[i]) free(rdy[i]);
+		free(rdy);
+		}
+	if(rY) delete rY;		if(rX) delete rX;		free(GBDlg);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create a waterfall graph
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Waterfall::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 55, 10, "Details"};
+	TabSHEET tab3 = {55, 90, 10, "Scheme"};
+	static DWORD colarr[] = {0x00000000L, 0x00ff0000L, 0x0000ff00L, 0x000000ffL,
+		0x00ff00ff, 0x00ffff00L, 0x0000ffff, 0x00c0c0c0};
+	static DWORD defcol = 0x0L;
+	char text1[100];
+	DlgInfo StackBarDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 158, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 158, 25, 45, 12},
+		{3, 0, 10, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
+		{10, 11, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 140, 100},
+		{11, 12, 200, ISPARENT, SHEET, &tab2, 5, 10, 140, 100},
+		{12, 20, 300, ISPARENT, SHEET, &tab3, 5, 10, 140, 100},
+		{20, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"range for common x values", 15, 30, 60, 8},
+		{101, 152, 0, 0x0L, RANGEINPUT, TmpTxt, 25, 40, 100, 10},
+		{152, 153, 0, ISPARENT | CHECKED, GROUPBOX, (void*)" ranges for y values ", 12, 60, 128, 45},
+		{153, 154, 0, 0x0L, LTEXT, 0L, 25, 65, 60, 8},
+		{154, 155, 0, 0x0L, RANGEINPUT, TmpTxt, 25, 75, 100, 10},
+		{155, 156, 0, 0x0L, PUSHBUTTON, (void*)"Next >>", 95, 87, 30, 12},
+		{156, 0, 0, 0x0L, PUSHBUTTON, (void*)"<< Prev.", 60, 87, 35, 12},
+		{200, 201, 0, 0x0L, LTEXT, (void*)"line to line displacement:", 20, 35, 80, 8},
+		{201, 202, 0, 0x0L, RTEXT, (void*)"dx =", 28, 45, 13, 8},
+		{202, 203, 0, 0x0L, EDVAL1, &dspm.fx, 43, 45, 25, 10},
+		{203, 204, 0, 0x0L, RTEXT, (void*)"dy =", 73, 45, 13, 8},
+		{204, 205, 0, 0x0L, EDVAL1, &dspm.fy, 88, 45, 25, 10},
+		{205, 0, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 115, 45, 15, 8}, 
+		{300, 301, 0, CHECKED, RADIO1, (void*)" common color for lines:", 20, 35, 80, 9},
+		{301, 302, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&defcol, 105, 35, 20, 10},
+		{302, 303, 0, 0x0L, RADIO1, (void*)" increment color scheme:", 20, 55, 80, 9},
+		{303, 304, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[0], 25, 70, 10, 10},
+		{304, 305, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[1], 37, 70, 10, 10},
+		{305, 306, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[2], 49, 70, 10, 10},
+		{306, 307, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[3], 61, 70, 10, 10},
+		{307, 308, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[4], 73, 70, 10, 10},
+		{308, 309, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[5], 85, 70, 10, 10},
+		{309, 310, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[6], 97, 70, 10, 10},
+		{310, 0, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, (void*)&colarr[7], 109, 70, 10, 10},
+		{500, 0, 0, LASTOBJ, NONE, 0L, 0, 0, 0, 0}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, res, currYR=0, maxYR=0, nx=0, ny;
+	char **rd = 0L;
+	bool updateYR = true, bContinue = false, bRet = false, bUseSch;
+	AccRange *rX = 0L, *rY = 0L;
+
+	if(!parent || !data) return false;
+	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
+		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return false;
+	if(TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
+		for(i=100, j= 0; i <= 1000; i +=100) if(TmpTxt[i]) 
+			rd[j++] = (char*)memdup(TmpTxt+i, ((int)strlen(TmpTxt+i))+2, 0);
+			maxYR = j-1;
+		}
+	if(!rd && !(rd = (char**)calloc(1, sizeof(char*))))return false;
+	if(!(Dlg = new DlgRoot(StackBarDlg, data))) return false;
+	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
+	hDlg = CreateDlgWnd("Create Waterfall Plot", 50, 50, 420, 260, Dlg, 0x4L);
+	do {
+		if(updateYR) {
+			if(currYR >0) {
+				Dlg->ShowItem(156, true);				Dlg->Activate(101, false);
+				}
+			else {
+				Dlg->ShowItem(156, false);				Dlg->Activate(101, true);
+				}
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, 20, "y-values # %d/%d", currYR+1, maxYR+1);
+#else
+			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
+#endif
+			//SetText will also cause a redraw of the whole dialog
+			Dlg->SetText(153, TmpTxt);
+			updateYR = false;
+			}
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		ny = 0;
+		if(rX) delete rX;
+		rX = 0L;
+		switch(res) {
+		case 0:
+			if(bContinue || Dlg->GetCheck(20)) res = -1;
+			break;
+		case -1:
+			bContinue = false;			break;
+		case 1:
+			for(i = 0; i < 8; i++) Dlg->GetColor(303+i, &colarr[i]);
+			Dlg->GetColor(301, &defcol);
+			bUseSch = Dlg->GetCheck(302);
+			Dlg->GetValue(202, &dspm.fx);	Dlg->GetValue(204, &dspm.fy);
+			//execute com_StackDlg for <OK>
+		case 155:		case 156:
+			res = com_StackDlg(res, Dlg, &rX, &nx, &rd, &currYR,
+				&rY, &bContinue, &ny, &maxYR, &updateYR);
+			break;
+		case 301:
+			Dlg->SetCheck(300, 0L, true);
+			res = -1;	break;
+		case 303:	case 304:	case 305:	case 306:
+		case 307:	case 308:	case 309:	case 310:
+			Dlg->SetCheck(302, 0L, true);
+			res = -1;	break;
+			}
+		}while (res < 0);
+	if(res == 1 && nx && rX && rd && rd[0] && rd[0][0]) {
+		maxYR++;
+		numPL = maxYR;
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		Dlg->GetText(101, text1, 100);
+		if(Lines=(DataLine**)calloc(numPL,sizeof(DataLine*)))for(i=0;i<maxYR;i++){
+			if(rd[i] && rd[i][0]) Lines[i] = new DataLine(this, data, text1, rd[i]);
+			if(Lines[i]) {
+				if(bUseSch) Lines[i]->SetColor(COL_DATA_LINE, colarr[(i & 0x07)]);
+				else Lines[i]->SetColor(COL_DATA_LINE, defcol);
+				bRet = true;
+				}
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rd) {
+		for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
+		free(rd);
+		}
+	if(rX) delete rX;		if(rY) delete rY;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create a multi data line plot
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *MultiLineDlg_Tmpl = 
+	"1,2,,DEFAULT,PUSHBUTTON,-1,158,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,158,25,45,12\n"
+	"3,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"10,11,100,ISPARENT | CHECKED,SHEET,1,5,10,140,100\n"
+	"11,12,300,ISPARENT,SHEET,2,5,10,140,100\n"
+	"12,20,200,ISPARENT,SHEET,15,5,10,140,100\n"
+	"20,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,101,,ISPARENT | CHECKED,GROUPBOX,3,12,30,128,75\n"
+	"101,102,,,LTEXT,0,25,39,60,8\n"
+	"102,103,,,RANGEINPUT,-15,25,49,100,10\n"
+	"103,104,,,LTEXT,0,25,61,60,8\n"
+	"104,105,,,RANGEINPUT,-16,25,71,100,10\n"
+	"105,106,,,PUSHBUTTON,-8,95,87,30,12\n"
+	"106,107,,,PUSHBUTTON,-9,60,87,35,12\n"
+	"107,108,,OWNDIALOG,COLBUTT,4,25,87,20,12\n"
+	"108,,,,LTEXT,12,47,90,30,9\n"
+	"200,201,,CHECKED,CHECKBOX,16,25,35,80,9\n"
+	"201,202,,ODEXIT,SYMBUTT,17,25,70,10,10\n"
+	"202,203,,ODEXIT,SYMBUTT,18,37,70,10,10\n"
+	"203,204,,ODEXIT,SYMBUTT,19,49,70,10,10\n"
+	"204,205,,ODEXIT,SYMBUTT,20,61,70,10,10\n"
+	"205,206,,ODEXIT,SYMBUTT,21,73,70,10,10\n"
+	"206,207,,ODEXIT,SYMBUTT,22,85,70,10,10\n"
+	"207,208,,ODEXIT,SYMBUTT,23,97,70,10,10\n"
+	"208,209,,ODEXIT,SYMBUTT,24,109,70,10,10\n"
+	"209,,,CHECKED,CHECKBOX,25,25,50,80,9\n"
+	"300,301,,TOUCHEXIT,RADIO1,13,20,35,80,9\n"
+	"301,302,,ODEXIT,COLBUTT,4,105,35,20,10\n"
+	"302,303,,CHECKED | TOUCHEXIT, RADIO1,14,20,55,80,9\n"
+	"303,304,,ODEXIT,COLBUTT,4,25,70,10,10\n"
+	"304,305,,ODEXIT,COLBUTT,5,37,70,10,10\n"
+	"305,306,,ODEXIT,COLBUTT,6,49,70,10,10\n"
+	"306,307,,ODEXIT,COLBUTT,7,61,70,10,10\n"
+	"307,308,,ODEXIT,COLBUTT,8,73,70,10,10\n"
+	"308,309,,ODEXIT,COLBUTT,9,85,70,10,10\n"
+	"309,310,,ODEXIT,COLBUTT,10,97,70,10,10\n"
+	"310,,,LASTOBJ | ODEXIT,COLBUTT,11,109,70,10,10\n";
+
+bool
+MultiLines::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 60, 10, "Scheme"};
+	TabSHEET tab3 = {60, 97, 10, "Symbols"};
+	static DWORD colarr[] = {0x00000000L, 0x00ff0000L, 0x0000ff00L, 0x000000ffL,
+		0x00ff00ff, 0x00ffff00L, 0x0000ffff, 0x00c0c0c0};
+	Symbol *syms[8], **lsyms;
+	static DWORD defcol = 0x0L;
+	char x_txt[100], y_txt[100];
+	DlgInfo *StackBarDlg;
+	void *dyndata[] = {(void*) &tab1, (void*)&tab2, (void*)" ranges for x- and y- values ",
+		(void*)&colarr[0], (void*)&colarr[1], (void*)&colarr[2], (void*)&colarr[3],
+		(void*)&colarr[4], (void*)&colarr[5], (void*)&colarr[6], (void*)&colarr[7],
+		(void*)"line color", (void*)" common color for lines:", (void*)" increment color scheme:",
+		(void*) &tab3, (void*)" draw symbols", (void*)&syms[0], (void*)&syms[1], (void*)&syms[2],
+		(void*)&syms[3], (void*)&syms[4], (void*)&syms[5], (void*)&syms[6], (void*)&syms[7],
+		(void*) " use line color for symbols"};
+	DlgRoot *Dlg;
+	void *hDlg;
+	char **rdx=0L, **rdy=0L;
+	DWORD *rdc = 0L, curr_col;
+	int i, j, nd, res, currYR=0, maxYR=0, s1, s2, rx, ry, cx, cy;
+	double symsize, x, y;
+	bool updateYR = true, bContinue = false, bError, bRet = false;
+	AccRange *rX = 0L, *rY = 0L;
+	DataLine *dl;
+
+	if(!parent || !data) return false;
+	symsize = NiceValue(defs.GetSize(SIZE_SYMBOL)*.8);
+	for(i = 0; i < 8; i++) if(syms[i] = new Symbol(0L, data, 0.0, 0.0, i)) {
+		if(i != 2 && i != 3)syms[i]->SetSize(SIZE_SYMBOL, symsize);
+		}
+	if(!(StackBarDlg = CompileDialog(MultiLineDlg_Tmpl, dyndata)))return false;
+	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
+		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return false;
+	if(TmpTxt[0] && TmpTxt[100] && (rdx = (char**)calloc(12, sizeof(char*))) 
+		&& (rdy = (char**)calloc(12, sizeof(char*))) && (rdc = (DWORD*)malloc(12*sizeof(DWORD)))) {
+		for(i=100, j= 0; i <= 1000; i +=100) if(TmpTxt[i]) {
+			rdx[j] = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);			rdc[j] = colarr[j%8];	
+			rdy[j] = (char*)memdup(TmpTxt+i, (int)strlen(TmpTxt+i)+1, 0);		maxYR = j++;
+			}
+		}
+	if(!(Dlg = new DlgRoot(StackBarDlg, data))) return false;
+	hDlg = CreateDlgWnd("Create Multi Line Plot", 50, 50, 420, 260, Dlg, 0x4L);
+	do {
+		if(updateYR) {
+			if(currYR >0) {
+				Dlg->ShowItem(106, true);	Dlg->ShowItem(108, false);
+				}
+			else {
+				Dlg->ShowItem(106, false);	Dlg->ShowItem(108, true);
+				}
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "x-range # %d/%d", currYR+1, maxYR+1);
+#else
+			sprintf(TmpTxt,"x-range # %d/%d", currYR+1, maxYR+1);
+#endif
+			//SetText will also cause a redraw of the whole dialog
+			Dlg->SetText(101, TmpTxt);
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "y-range # %d/%d", currYR+1, maxYR+1);
+#else
+			sprintf(TmpTxt,"y-range # %d/%d", currYR+1, maxYR+1);
+#endif
+			Dlg->SetText(103, TmpTxt);
+			updateYR = false;
+			}
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 0:
+			if(bContinue || Dlg->GetCheck(20)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 1:
+		case 105:										//next button
+			bError=false;	s1 = s2 = 0;
+			if(Dlg->GetText(102, x_txt, 100)) {
+				if(rX = new AccRange(x_txt)) {
+					s1 = rX->CountItems();
+					if(s1 < 2) {
+						ErrorBox("x-range not valid");
+						bContinue=bError=true;
+						Dlg->Activate(102, true);
+						}
+					delete rX;
+					}
+				else bError = true;
+				}
+			else bError = true;
+			if(Dlg->GetText(104, y_txt, 100) && !bError) {
+				if(rY = new AccRange(y_txt)) {
+					s2 = rY->CountItems();
+					if(s2 < 2) {
+						ErrorBox("y-range not valid");
+						bContinue=bError=true;
+						Dlg->Activate(104, true);
+						}
+					delete rY;
+					}
+				else bError = true;
+				}
+			else {
+				Dlg->Activate(104, true);
+				bError = true;
+				}
+			if(!s1 || !s2) bError = true;
+			rX = rY = 0L;
+			if(!bError && s1!=s2) {
+				ErrorBox("X-range and y-range are\ndifferent in size");
+				bContinue=bError=true;
+				}
+			if(!bError) {
+				if((currYR+1) > maxYR) {
+					rdx = (char**)realloc(rdx, sizeof(char*)*(currYR+2));
+					rdy = (char**)realloc(rdy, sizeof(char*)*(currYR+2));
+					rdc = (DWORD*)realloc(rdc, sizeof(DWORD)*(currYR+2));
+					rdx[currYR] = rdx[currYR+1] = rdy[currYR] = rdy[currYR+1] = 0L;
+					maxYR = currYR+1;
+					rdc[currYR] = rdc[currYR+1] = Dlg->GetCheck(302) ? colarr[maxYR & 0x07] : defcol;
+					}
+				if(rdx[currYR]) free(rdx[currYR]);		//store x-range
+				rdx[currYR] = (char*)memdup(x_txt, (int)strlen(x_txt)+1, 0);
+				if(rdy[currYR]) free(rdy[currYR]);		//store y range
+				rdy[currYR] = (char*)memdup(y_txt, (int)strlen(y_txt)+1, 0);
+				Dlg->GetColor(107, &curr_col);			rdc[currYR] = curr_col;
+				updateYR = true;						currYR++;
+				Dlg->SetColor(107, rdc[currYR]);		Dlg->SetText(102, rdx[currYR]);
+				Dlg->SetText(104, rdy[currYR]);			Dlg->Activate(102, true);				
+				if(res != 1) res = -1;
+				}
+			else if(res != 1){
+				bContinue = true;
+				res = -1;
+				}
+			break;
+		case 106:										//prev button
+			if(Dlg->GetText(102, x_txt, 100) && Dlg->GetText(104, y_txt, 100)){
+				if(rdx[currYR]) free(rdx[currYR]);		if(rdy[currYR]) free(rdy[currYR]);
+				rdx[currYR] = (char*)memdup(x_txt, (int)strlen(x_txt)+1, 0);
+				rdy[currYR] = (char*)memdup(y_txt, (int)strlen(y_txt)+1, 0);
+				Dlg->GetColor(107, &curr_col);			rdc[currYR] = curr_col;
+				}
+			else if(currYR == maxYR) maxYR--;
+			currYR--;
+			Dlg->SetColor(107, rdc[currYR]);			Dlg->SetText(102, rdx[currYR]);
+			Dlg->SetText(104, rdy[currYR]);				Dlg->Activate(102, true);
+			updateYR = true;
+			res = -1;
+			break;
+		case 201:	case 202:	case 203:	case 204:
+		case 205:	case 206:	case 207:	case 208:
+			res = -1;
+		case 300:
+			Dlg->SetColor(107, defcol);
+			bContinue = true;
+			res = -1;	break;
+		case 301:
+			Dlg->SetCheck(300, 0L, true);				Dlg->GetColor(res, &defcol);
+			res = -1;	break;
+		case 302:
+			Dlg->SetColor(107, colarr[currYR & 0x07]);
+			bContinue = true;
+			res = -1;	break;
+		case 303:	case 304:	case 305:	case 306:
+		case 307:	case 308:	case 309:	case 310:
+			Dlg->SetCheck(302, 0L, true);
+			i = res-303;
+			if(rdx && rdy && i <= maxYR && rdc[i] == colarr[i]) {
+				Dlg->GetColor(res, &colarr[i]);		rdc[i] = colarr[i];
+				Dlg->SetColor(107, rdc[currYR]);
+				}
+			else {
+				Dlg->GetColor(res, &colarr[i]);
+				}
+			res = -1;	break;
+			}
+		}while (res < 0);
+	if(res == 1 && rdx && rdy && maxYR) {
+		maxYR++;		rX = rY = 0L;
+		if(xyPlots=(PlotScatt**)calloc(maxYR, sizeof(PlotScatt*))) for(i = numXY = 0; i < maxYR; i++){
+			if(rdx[i] && rdy[i] && rdx[i][0] && rdy[i][0]) {
+				if(Dlg->GetCheck(200) && (rX = new AccRange(rdx[i])) && (rY = new AccRange(rdy[i]))) {
+					lsyms = (Symbol**)calloc(rX->CountItems()+1, sizeof(Symbol*));
+					symsize = syms[i &0x07]->GetSize(SIZE_SYMBOL);
+					for(nd = 0, rX->GetFirst(&cx, &rx), rY->GetFirst(&cy, &ry); rX->GetNext(&cx, &rx), rY->GetNext(&cy, &ry); ) {
+						if(data->GetValue(rx, cx, &x) && data->GetValue(ry, cy, &y)) {
+							lsyms[nd] = new Symbol(0L, data, x, y, syms[i &0x07]->type, cx, rx, cy, ry);
+							if(Dlg->GetCheck(209)) lsyms[nd]->SetColor(COL_SYM_LINE, Dlg->GetCheck(300) ? defcol : rdc[i & 0x07]);
+							else {
+								lsyms[nd]->SetColor(COL_SYM_LINE, syms[i &0x07]->GetColor(COL_SYM_LINE));
+								lsyms[nd]->SetColor(COL_SYM_FILL, syms[i &0x07]->GetColor(COL_SYM_FILL));
+								}
+							lsyms[nd]->SetSize(SIZE_SYMBOL, symsize);
+							nd++;
+							}
+						}
+					}
+				else {
+					nd = 0;		lsyms = 0L;
+					}
+				if(dl = new DataLine(this, data, rdx[i], rdy[i])) {
+					dl->SetColor(COL_DATA_LINE, Dlg->GetCheck(300) ? defcol : rdc[i & 0xf]);
+					if(xyPlots[numXY] = new PlotScatt(this, data, nd, lsyms, dl)) {
+						if(rY) xyPlots[numXY]->data_desc = rY->RangeDesc(data, 1);
+						numXY++;
+						}
+					else delete dl;
+					}
+				if(rX)delete rX;		if(rY)delete rY;		rX = rY = 0L;
+				}
+			}
+		if(numXY) bRet = true;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rdx) {
+		for (i = 0; i < maxYR; i++)	if(rdx[i]) free(rdx[i]);
+		free(rdx);
+		}
+	if(rdy) {
+		for (i = 0; i < maxYR; i++)	if(rdy[i]) free(rdy[i]);
+		free(rdy);
+		}
+	for(i = 0; i < 8; i++) if(syms[i]) delete syms[i];
+	free(StackBarDlg);		if(rdc) free(rdc);
+	if(bRet) {
+		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;	Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
+		Command(CMD_AUTOSCALE, 0L, 0L);
+		}
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Pie and ring chart properties
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *PieDlgTmpl = 
+	"1,2,,DEFAULT,PUSHBUTTON,-1,130,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,130,25,45,12\n"
+	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,5,100,ISPARENT | CHECKED,SHEET,1,5,10,120,103\n"
+	"5,6,200,ISPARENT,SHEET,2,5,10,120,103\n"
+	"6,10,300,ISPARENT,SHEET,3,5,10,120,103\n"
+	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,+,,,LTEXT,4,10,25,60,8\n"
+	".,105,,,RANGEINPUT,-15,15,35,100,10\n"
+	"105,.,500,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	".,.,600,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	".,.,,,EDVAL1,6,58,59,30,10\n"
+	".,,,,LTEXT,-3,89,59,15,8\n" 
+	"200,+,,,LTEXT,7,15,30,60,8\n"
+	".,.,,,RTEXT,-4,2,42,20,8\n"
+	".,204,,,EDVAL1,8,23,42,30,10\n"
+	"204,.,,,RTEXT,-5,47,42,20,8\n"
+	".,.,,,EDVAL1,9,68,42,30,10\n"
+	".,.,,,LTEXT,-3,99,42,15,8\n" 
+	".,.,,,RTEXT,10,27,58,20,8\n"
+	".,.,,,EDVAL1,11,48,58,30,10\n"
+	".,210,,,LTEXT,12,79,58,15,8\n" 
+	"210,.,400,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	".,.,410,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	".,,,,LTEXT,-27,15,80,30,8\n"
+	"300,,,NOSELECT,ODBUTTON,14,20,35,80,60\n"
+	"400,+,,EXRADIO | CHECKED,ODBUTTON,15,55,75,30,30\n"
+	".,,,EXRADIO,ODBUTTON,15,85,75,30,30\n"
+	"410,+,,EXRADIO | CHECKED,ODBUTTON,15,55,75,30,30\n"
+	".,,,EXRADIO,ODBUTTON,15,85,75,30,30\n"
+	"500,+,,CHECKED,RADIO1,16,10,59,20,8\n"
+	".,.,,,RADIO1,17,10,71,40,8\n"
+	".,.,,,RANGEINPUT,-16,15,82,100,10\n"
+	".,.,,,LTEXT,19,15,94,10,8\n"
+	".,.,,,EDVAL1,20,42,94,25,10\n"
+	".,,,,LTEXT,21,70,94,15,8\n"
+	"600,+,,,RTEXT,22,8,59,45,8\n"
+	".,.,,,RTEXT,23,8,74,45,8\n"
+	".,.,,,EDVAL1,24,58,74,30,10\n"
+	".,,,LASTOBJ,LTEXT,-3,89,74,15,8";
+bool
+PieChart::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 55, 10, "Details"};
+	TabSHEET tab3 = {55, 90, 10, "Scheme"};
+	double fcx =10.0, fcy = 20.0, frad=40.0, firad = 30.0;
+	char txt2[80];
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)"spread sheet range for values",
+		(void*)0L, (void*)&frad, (void*)"position of center:", (void*)&fcx, (void*)&fcy,
+		(void*)"start angle", (void*)&CtDef.fx, (void*)"degree", (void*)0L, (void*)(OD_scheme),
+		(void*)(OD_PieTempl), (void*)"fixed radius", (void*)"pick radii from spreadsheet range",
+		(void*)0L, (void*)"x  factor", (void*)&FacRad, (void*)&txt2, (void*)"outer radius",
+		(void*)"inner radius", (void*)&firad};
+	DlgInfo *PieDlg = CompileDialog(PieDlgTmpl, dyndata);
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, ix, iy, rix, riy, ny, res, cf, cb;
+	bool bRet = false, bContinue = false;
+	double sum = 0.0, dang1, dang2;
+	double fv;
+	lfPOINT fpCent;
+	AccRange *rY = 0L, *rR = 0L;
+
+	if(!parent || !data) return false;
+	UseRangeMark(data, 1, TmpTxt, TmpTxt+100);
+	cb = rlp_strcpy(txt2, 80, "= [");	cb += rlp_strcpy(txt2+cb, 80-cb, Units[defs.cUnits].display);
+	rlp_strcpy(txt2+cb, 80-cb, "]");
+	frad = (parent->GetSize(SIZE_DRECT_BOTTOM) - parent->GetSize(SIZE_DRECT_TOP))/2.0;
+	fcx = parent->GetSize(SIZE_GRECT_LEFT) + (parent->GetSize(SIZE_DRECT_LEFT))/2.0 + frad;
+	fcy = parent->GetSize(SIZE_GRECT_TOP) + parent->GetSize(SIZE_DRECT_TOP) + frad;
+	firad = frad-frad/10.0f;
+	if(!(Dlg = new DlgRoot(PieDlg, data)))return false;
+	if(Id == GO_PIECHART) {
+		Dlg->ShowItem(105, true);		Dlg->ShowItem(106, false);
+		Dlg->ShowItem(210, true);		Dlg->ShowItem(211, false);
+		}
+	else {
+		Dlg->ShowItem(105, false);		Dlg->ShowItem(106, true);
+		Dlg->ShowItem(210, false);		Dlg->ShowItem(211, true);
+		}
+	hDlg = CreateDlgWnd(Id == GO_PIECHART ? (char*)"Create pie chart" : 
+		(char*)"Create ring chart",	50, 50, 370, 266, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		//rY is defined by OK. If other buttons use rY reset to 0L!
+		switch(res) {
+		case 0:							//lost focus ?
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 400:	case 410:
+			Dlg->SetText(208, "90");		Dlg->DoPlot(0L);
+			CtDef.fy = 360.0;					res = -1;
+			break;
+		case 401:	case 411:
+			Dlg->SetText(208, "180");		Dlg->DoPlot(0L);
+			CtDef.fy = 180.0;					res = -1;
+			break;
+		case 1:
+			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0] && (rY = new AccRange(TmpTxt))) 
+				ny = rY->CountItems();
+			else ny = 0;
+			Dlg->GetValue(208, &CtDef.fx);		Dlg->GetValue(202, &fcx);
+			Dlg->GetValue(205, &fcy);			Dlg->GetValue(107, &frad);
+			Dlg->GetValue(602, &firad);			Dlg->GetValue(504, &FacRad);
+			if(Dlg->GetCheck(501) && ny && Dlg->GetText(502, TmpTxt, TMP_TXT_SIZE) && 
+				(rR = new AccRange(TmpTxt))){
+				if(rR->CountItems() != ny) {
+					delete rR;
+					delete rY;
+					rR = rY = 0L;
+					ErrorBox("Range for values and\nrange for radii must\nhave the same size!");
+					bContinue = true;
+					res = -1;
+					}
+				}
+			break;
+			}
+		}while (res < 0);
+
+	if(res == 1 && rY && ny >1 && (Segments = (segment **)calloc(ny, sizeof(segment*)))) {
+		nPts = ny;
+		if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) ssRefA = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+		if(rR && Dlg->GetText(502, TmpTxt, TMP_TXT_SIZE)) ssRefR = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+		Bounds.Xmax = Bounds.Ymax = 100.0;		Bounds.Xmin = Bounds.Ymin = -100.0;
+		fpCent.fx = fcx;					fpCent.fy = fcy;
+		rY->GetFirst(&ix, &iy);				rY->GetNext(&ix, &iy);
+		for(i = 0; i < ny; i++){
+			if(data->GetValue(iy, ix, &fv)) sum += fv;
+			rY->GetNext(&ix, &iy);
+			}
+		sum /= CtDef.fy;
+		dang1 = dang2 = CtDef.fx;
+		rY->GetFirst(&ix, &iy);				rY->GetNext(&ix, &iy);
+		if(rR) {
+			rR->GetFirst(&rix, &riy);		rR->GetNext(&rix, &riy);
+			}
+		for(i = cf = 0; i < ny; i++){
+			if(data->GetValue(iy, ix, &fv)) {
+				dang2 -= (double)fv / sum;
+				if(dang2 < 0.0) dang2 += 360.0;
+				if(rR && data->GetValue(riy, rix, &frad)) frad *= FacRad;
+				Segments[i] = new segment(this, data, &fpCent, 
+					Id == GO_PIECHART ? 0.0 : firad, frad,
+					dang1, dang2);
+				if(Segments[i])Segments[i]->Command(CMD_SEG_FILL, GetSchemeFill(&cf), 0L);
+				dang1 = dang2;
+				}
+			rY->GetNext(&ix, &iy);
+			if(rR) rR->GetNext(&rix, &riy);
+			}
+		bRet = true;
+		}
+	if(rY) delete rY;		if(rR) delete rR;
+	CloseDlgWnd(hDlg);		delete Dlg;				free(PieDlg);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Create a star chart
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+StarChart::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 55, 10, "Labels"};
+	double sa = 90.0, factor = 1.0, lbdist = NiceValue(DefSize(SIZE_TEXT)*1.2);
+	char txt1[80], txt2[80];
+	DlgInfo StarDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 130, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 25, 45, 12},
+		{3, 0, 4, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
+		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 120, 103},
+		{5, 10, 200, ISPARENT, SHEET, &tab2, 5, 10, 120, 103},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"spread sheet range for values", 10, 30, 60, 8},
+		{101, 102, 0, 0x0L, RANGEINPUT, txt1, 15, 45, 100, 10},
+		{102, 103, 0, 0x0L, RTEXT, (void*)"x  factor", 17, 57, 30, 8},
+		{103, 104, 0, 0x0L, EDVAL1, &factor, 48, 57, 30, 10},
+		{104, 105, 0, 0x0L, LTEXT, &txt2, 79, 57, 15, 8},
+		{105, 106, 0, 0x0L, RTEXT, (void*)"start angle", 17, 72, 30, 8},
+		{106, 107, 0, 0x0L, EDVAL1, &sa, 48, 72, 30, 10},
+		{107, 108, 0, 0x0L, LTEXT, (void*)"degree", 79, 72, 15, 8}, 
+		{108, 109, 0, CHECKED, CHECKBOX, (void*)"draw polygon", 25, 87, 20, 8},
+		{109, 0, 0, CHECKED, CHECKBOX, (void*)"draw rays", 25, 97, 20, 8},
+		{200, 201, 0, 0x0L, CHECKBOX, (void*)"add labels to data points", 15, 28, 50, 8},
+		{201, 202, 0, 0x0L, RANGEINPUT, (void*)txt1, 15, 51, 100, 10},
+		{202, 203, 0, 0x0L, LTEXT, (void*)"spread sheet range for labels:", 15, 40, 60, 8},
+		{203, 204, 0, 0x0L, RTEXT, (void*)"distance:", 10, 70, 40, 8},
+		{204, 205, 0, 0x0L, EDVAL1, &lbdist, 52, 70, 30, 10},
+		{205, 0, 0, LASTOBJ, LTEXT, (void*)Units[defs.cUnits].display, 85, 70, 10, 8}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, ix, iy, res, width, height, ny;
+	bool bRet = false, bContinue = false;
+	AccRange *rY = 0L, *rL = 0L;
+	Label *lb;
+	lfPOINT *fp = 0L, *fpt = 0L, fl[2];
+	double fx, fy, tmpval, frad;
+	double sia, csia;
+	polyline *plo;
+	TextDEF td = {0x00000000L, 0x00ffffffL, DefSize(SIZE_TEXT), 0.0, 0.0, 0,
+		TXA_HCENTER | TXA_VCENTER, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, 0L}; 
+
+	if(!parent || !data) return false;
+	data->GetSize(&width, &height);
+#ifdef USE_WIN_SECURE
+	sprintf_s(txt1, 80,"a1:a%d", height);	sprintf_s(txt2, 80, "= [%s]", Units[defs.cUnits].display);
+#else
+	sprintf(txt1, "a1:a%d", height);		sprintf(txt2, "= [%s]", Units[defs.cUnits].display);
+#endif
+	if(parent) {
+		frad = (parent->GetSize(SIZE_DRECT_BOTTOM) - parent->GetSize(SIZE_DRECT_TOP))/2.0f;
+		fPos.fx = parent->GetSize(SIZE_GRECT_LEFT) + parent->GetSize(SIZE_DRECT_LEFT) + frad;
+		fPos.fy = parent->GetSize(SIZE_GRECT_TOP) + parent->GetSize(SIZE_DRECT_TOP) + frad;
+		}
+	if(!(Dlg = new DlgRoot(StarDlg, data)))return false;
+	hDlg = CreateDlgWnd("Create star chart", 50, 50, 370, 260, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 0:							//lost focus ?
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 1:
+			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0] && (rY = new AccRange(TmpTxt))) 
+				ny = rY->CountItems();
+			else ny = 0;
+			Dlg->GetValue(106, &sa);			Dlg->GetValue(103, &factor);
+			Dlg->GetValue(204, &lbdist);
+			break;
+			}
+		}while (res < 0);
+	if(res == 1 && rY && ny >1 && (fp = (lfPOINT*)calloc(ny+1, sizeof(lfPOINT))) &&
+		(fpt = (lfPOINT*)calloc(ny+1, sizeof(lfPOINT)))){
+		Bounds.Xmax = Bounds.Ymax = 100.0;		Bounds.Xmin = Bounds.Ymin = -100.0;
+		rY->GetFirst(&ix, &iy);
+		for(i = 0; i < ny; i++){
+			rY->GetNext(&ix, &iy);
+			if(data->GetValue(iy, ix, &tmpval)){
+				tmpval *= factor;
+				sia = sin(sa * 0.01745329252);			csia = cos(sa * 0.01745329252);
+				fx = (tmpval * csia);					fy = (-tmpval * sia);
+				}
+			else fx = fy = 0.0f;
+			fp[i].fx = fpt[i].fx = fx;			fp[i].fy = fpt[i].fy = fy;
+			fpt[i].fx += lbdist *csia;			fpt[i].fy += (-lbdist * sia);
+			sa -= 360.0/ny;
+			}
+		fp[i].fx = fp[0].fx;		fp[i].fy = fp[0].fy;
+		if(Dlg->GetCheck(108)){
+			if((plo = new polygon(this, data, fp, ny+1))) {		//ny+1 to close the shape!
+				if(!(bRet = Command(CMD_DROP_OBJECT, (void*)plo, 0L))) delete plo;
+				}
+			}
+		if(Dlg->GetCheck(109)){
+			fl[0].fx = fl[0].fy = 0.0f;
+			for(i = 0; i < ny; i++) {
+				fl[1].fx = fp[i].fx;		fl[1].fy = fp[i].fy;
+				if((plo = new polyline(this, data, fl, 2))) {
+					if(Command(CMD_DROP_OBJECT, (void*)plo, 0L)) bRet = true;
+					else delete plo;
+					}
+				}
+			}
+		if(Dlg->GetCheck(200) && Dlg->GetText(201, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0] && (rL = new AccRange(TmpTxt))){
+			rL->GetFirst(&ix, &iy);
+			td.text = TmpTxt;
+			for(i = 0; i < ny; i++) {
+				rL->GetNext(&ix, &iy);
+				if(data->GetText(iy, ix, TmpTxt, TMP_TXT_SIZE)){
+					if((lb = new Label(this, data, fpt[i].fx, fpt[i].fy, &td, 0L))) {
+						if(Command(CMD_DROP_OBJECT, (void*)lb, 0L)) bRet = true;
+						else delete lb;
+						}
+					}
+				}
+			}
+		}
+	if(rY) delete rY;			if(rL) delete rL;
+	if(fp) free(fp);			if(fpt) free(fpt);
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Grid3D represents a surface in space
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Grid3D::PropertyDlg()
+{
+	return Configure();
+}
+
+bool
+Grid3D::Configure()
+{
+	TabSHEET tab1 = {0, 37, 10, "Function"};
+	TabSHEET tab2 = {37, 65, 10, "Style"};
+	FillDEF newFill;
+	DlgInfo GridDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 155, 10, 50, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 155, 25, 50, 12},
+		{3, 0, 300, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{300, 301, 500, HIDDEN | CHECKED, GROUPBOX, (void*)" grid lines ", 10, 10, 140, 100},
+		{301, 305, 400, HIDDEN | CHECKED, GROUPBOX, (void*)" surface ", 10, 10, 140, 100},
+		{305, 306, 0, TOUCHEXIT, RADIO1, (void*) " grid lines", 155, 45, 50, 10},
+		{306, 0, 0, TOUCHEXIT, RADIO1, (void*) " surface", 155, 57, 50, 10},
+		{400, 401, 0, 0x0L, RTEXT, (void*)"grid line width", 38, 20, 40, 8},
+		{401, 402, 0, 0x0L, EDVAL1, &Line.width, 80, 20, 25, 10},
+		{402, 403, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 107, 20, 20, 8},
+		{403, 404, 0, 0x0L, RTEXT, (void*)"grid line color", 38, 32, 40, 8},
+		{404, 405, 0, OWNDIALOG, COLBUTT, (void *)&Line.color, 80, 32, 25, 10},
+		{405, 406, 0, 0x0L, RTEXT,(void*)"plane color" , 38, 44, 40, 8},
+		{406, 0, 0, OWNDIALOG, SHADE3D, &newFill, 80, 44, 25, 10},
+		{500, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_linedef, 15, 15, 130, 100}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, cb, new_type, undo_level = *Undo.pcb;
+	bool bRet = false;
+	double tmp;
+	DWORD new_col;
+	LineDEF newLine;
+	anyOutput *cdisp = Undo.cdisp;
+
+	if(!parent) return false;
+	memcpy(&newFill, &Fill, sizeof(FillDEF));
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+	if(!(Dlg = new DlgRoot(GridDlg, data))) return false;
+	if(!type) {
+		Dlg->ShowItem(300, true);	Dlg->SetCheck(305, 0L, true);
+		}
+	else {
+		Dlg->ShowItem(301, true);	Dlg->SetCheck(306, 0L, true);
+		}
+	if(parent->name) {
+		cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Grid of ");
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, parent->name);
+		}
+	else rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "3D Grid");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 426, 260, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 305:		case 306:
+			if(Dlg->GetCheck(305)) {
+				Dlg->ShowItem(300, true);	Dlg->ShowItem(301, false);
+				}
+			else {
+				Dlg->ShowItem(300, false);	Dlg->ShowItem(301, true);
+				}
+			Dlg->Command(CMD_REDRAW, 0L, 0L);
+			res = -1;
+			break;
+			}
+		}while (res < 0);
+	Undo.SetDisp(cdisp);
+	while(*Undo.pcb > undo_level)	Undo.Pop(cdisp);
+	if(res == 1) {
+		if(Dlg->GetCheck(305) && type == 0) {
+			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+			if(cmpLineDEF(&Line, &newLine)) {
+				Command(CMD_SET_LINE, &newLine, 0L);
+				bRet = true;
+				}
+			}
+		else if(Dlg->GetCheck(306) && type == 1) {
+			Dlg->GetValue(401, &tmp);			Dlg->GetColor(404, &new_col);
+			if(planes && (cmpFillDEF(&Fill, &newFill) || tmp != Line.width || new_col != Line.color)) {
+				Command(CMD_SAVE_SYMBOLS, 0L, 0L);
+				Command(CMD_SYM_FILL, &newFill, 0L);
+				if(tmp != Line.width) SetSize(SIZE_SYM_LINE, tmp);
+				if(new_col != Line.color) SetColor(COL_POLYLINE, new_col);
+				bRet = true;
+				}
+			}
+		else {
+			Undo.ValInt(parent, &type, 0L);
+			Undo.Line(this, &Line, UNDO_CONTINUE);	Undo.Fill(this, &Fill, UNDO_CONTINUE);
+			if(Dlg->GetCheck(305)) {
+				new_type = 0;
+				OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+				}
+			else {
+				new_type = 1;
+				memcpy(&Fill, &newFill, sizeof(FillDEF));
+				Dlg->GetValue(401, &Line.width);
+				Dlg->GetColor(404, &Line.color);
+				Line.pattern = 0L;
+				Line.patlength = 1;
+				}
+			if(planes && nPlanes) Undo.DropListGO(parent, (GraphObj***)&planes, &nPlanes, UNDO_CONTINUE);
+			if(lines && nLines) Undo.DropListGO(parent, (GraphObj***)&lines, &nLines, UNDO_CONTINUE);
+			Undo.VoidPtr(parent, (void**)&planes, 0L, 0L, UNDO_CONTINUE);
+			Undo.VoidPtr(parent, (void**)&lines, 0L, 0L, UNDO_CONTINUE);
+			type = new_type;
+			CreateObs(true);
+			bRet = true;
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Scatt3D is a layer representing most simple 3D plots
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *Dlg3DTmpl = 
+		"1,2,,DEFAULT,PUSHBUTTON,-1,142,10,45,12\n"
+		"2,3,,,PUSHBUTTON,-2,142,25,45,12\n"
+		"3,50,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+		"4,5,100,TOUCHEXIT | ISPARENT | CHECKED, SHEET,1,5,10,131,100\n"
+		"5,6,200,TOUCHEXIT | ISPARENT, SHEET,2,5,10,131,100\n"
+		"6,10,400, TOUCHEXIT | ISPARENT, SHEET,3,5,10,131,100\n"
+		"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+		"50,60,,NOSELECT, ODBUTTON,4,142,65,45,45\n"
+		"60,61,,,ICON,5,10,114,20,20\n"
+		"61,62,,,LTEXT,6,30,116,100,6\n"
+		"62,,,,LTEXT,7,30,122,100,6\n"
+		"100,101,,,LTEXT,8,10,30,60,8\n"
+		"101,102,,,RANGEINPUT,9,20,40,100,10\n"
+		"102,103,,,LTEXT,10,10,55,60,8\n"
+		"103,104,,,RANGEINPUT,11,20,65,100,10\n"
+		"104,105,,,LTEXT,12,10,80,60,8\n"
+		"105,,,,RANGEINPUT,13,20,90,100,10\n"
+		"200,201,,,LTEXT,-27,25,30,60,8\n"
+		"201,202,,,CHECKBOX,15,30,55,60,8\n"
+		"202,203,,TOUCHEXIT,CHECKBOX,16,30,65,60,8\n"
+		"203,204,,TOUCHEXIT,CHECKBOX,17,30,45,60,8\n"
+		"204,205,,TOUCHEXIT,CHECKBOX,18,30,75,60,8\n"
+		"205,,,TOUCHEXIT,CHECKBOX,19,30,85,60,8\n"
+		"400,410,,,LTEXT,20,20,30,60,8\n"
+		"410,411,,EXRADIO,ODBUTTON,21,20,42,25,25\n"
+		"411,412,,EXRADIO,ODBUTTON,21,45,42,25,25\n"
+		"412,,,LASTOBJ | EXRADIO,ODBUTTON,21,70,42,25,25";
+
+bool
+Scatt3D::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 22, 10, "Data"};
+	TabSHEET tab2 = {22, 50, 10, "Layout"};
+	TabSHEET tab3 = {50, 75, 10, "Axes"};
+	char text1[100], text2[100], text3[100];
+	int icon = ICO_INFO;
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)(OD_AxisDesc3D), (void*)&icon,
+		(void*)"Use [arrow keys], [shift]+[arrow key],", (void*)"and [r], [R], [l] or [L] to rotate graph.",
+		(void*)"range for X Data", (void*)text1, (void*)"range for Y Data", (void*)text2,
+		(void*)"range for Z Data", (void*)text3, (void*)0L, (void*)" balls", (void*)" columns",
+		(void*)" line", (void*)" drop lines", (void*)" arrows", (void*)"select template:", 
+		(void*)(OD_AxisTempl3D)};
+	DlgInfo *Dlg3D = CompileDialog(Dlg3DTmpl, dyndata);
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, n1, n2, n3, ic = 0;
+	int i, j, k, l, m, n, i2, j2, k2, l2, m2, cb;
+	double x, y, z, bar_w, bar_d, rad;
+	bool bRet = false, bContinue = false;
+	AccRange *rX, *rY, *rZ;
+	fPOINT3D pos1, pos2;
+
+	if(!data || !parent)return false;
+	UseRangeMark(data, 1, text1, text2, text3);
+	if(!(Dlg = new DlgRoot(Dlg3D, data)))return false;
+	Dlg->SetCheck(410 + AxisTempl3D, 0L, true);
+	for(i = 0; i < 5; i++) Dlg->SetCheck(201+i, 0L, (c_flags & (1<<i))!=0);
+	if(c_flags == 0x2000 || c_flags == 0x4000) {
+		Dlg->ShowItem(5, false);		Dlg->ShowItem(6, false);
+		}
+	else Dlg->ShowItem(6, (c_flags & 0x1000) == 0x1000);
+	rX = rY = rZ = 0L;					rad = DefSize(SIZE_SYMBOL);
+#ifdef _WINDOWS
+	for(i = 61; i <= 62; i++) Dlg->TextSize(i, 12);
+#else
+	for(i = 61; i <= 62; i++) Dlg->TextSize(i, 10);
+#endif
+	hDlg = CreateDlgWnd(c_flags == 0x2000 ? (char*)"Create Paravent Plot": 
+	c_flags == 0x4000 ? (char*)"Delauney Surface" : (char*)"Create 3D Plot", 50, 50, 388, 300, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:								// focus lost
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 4:		case 5:		case 6:		//the tab sheets
+			res = -1;
+			break;
+		case 202:		Dlg->SetCheck(204, 0L, false);		res = -1;		break;
+		case 204:		Dlg->SetCheck(202, 0L, false);		res = -1;		break;
+		case 203:		Dlg->SetCheck(205, 0L, false);		res = -1;		break;
+		case 205:		Dlg->SetCheck(203, 0L, false);		res = -1;		break;
+		case 410:	case 411:	case 412:	//axis templates
+			AxisTempl3D = res-410;
+			res = -1;
+			break;
+		case 1:
+			if(rX) delete rX;	if(rY) delete rY;	if(rZ) delete rZ;
+			rX = rY = rZ = 0L;	n1 = n2 = n3 = 0;
+			if(Dlg->GetText(101, text1, 100) && (rX = new AccRange(text1))) n1 = rX->CountItems();
+			if(Dlg->GetText(103, text2, 100) && (rY = new AccRange(text2))) n2 = rY->CountItems();
+			if(Dlg->GetText(105, text3, 100) && (rZ = new AccRange(text3))) n3 = rZ->CountItems();
+			if(n1 && n2 && n3){
+				if(c_flags == 0x2000 || c_flags == 0x4000) {
+					//no more but a ribbon or surface
+					}
+				else if(n1 == n2 && n2 == n3) {
+					//o.k., three ranges of equal size have been defined
+					c_flags = 0;
+					for(i = 0; i < 5; i++) if(Dlg->GetCheck(201+i)) c_flags |= (1<<i);
+					}
+				else {
+					InfoBox("All ranges must have\nthe same size."); 
+					res = -1;	bContinue = true;
+					}
+				}
+			else {
+				cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Ranges for ");
+				if(!n1) cb += rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, "\n-  X Data");
+				if(!n2) cb += rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, "\n-  Y Data");
+				if(!n3) cb += rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, "\n-  Z Data");
+				rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE-cb, "\nnot given or not valid.");
+				InfoBox(TmpTxt);
+				res = -1;	bContinue = true;
+				}
+			break;
+			}
+		}while (res <0);
+	if(res == 1 && rX && rY && rZ) {
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		xBounds.fx = yBounds.fx = zBounds.fx = HUGE_VAL;
+		xBounds.fy = yBounds.fy = zBounds.fy = -HUGE_VAL;
+		bar_w = bar_d = (DefSize(SIZE_BAR)/2.0);
+		if(c_flags & 0x01) (Balls = (Sphere**)calloc((nBalls = n1)+1, sizeof(Sphere*)));
+		if(c_flags & 0x02) (Columns = (Brick**)calloc((nColumns = n1)+1, sizeof(Brick*)));
+		if(c_flags & 0x04) Line = new Line3D(this, data, text1, text2, text3);
+		if(c_flags & 0x08) (DropLines = (DropLine3D**)calloc((nDropLines = n1)+1, sizeof(DropLine3D*)));
+		if(c_flags & 0x010) (Arrows = (Arrow3D**)calloc((nArrows = n1)+1, sizeof(Arrow3D*)));
+		rX->GetFirst(&i, &j);		rX->GetNext(&i, &j);
+		rY->GetFirst(&k, &l);		rY->GetNext(&k, &l);
+		rZ->GetFirst(&m, &n);		rZ->GetNext(&m, &n);
+		i2 = i;	j2 = j;	k2 = k;	l2 = l;	m2 = m;	n2 = n;
+		if(c_flags == 0x2000){
+			Bounds.Ymin = 0.0;
+			rib = new Ribbon(this, data, 2, text1, text2, text3);
+			}
+		else if(c_flags == 0x4000){
+			rib = new Ribbon(this, data, 3, text1, text2, text3);
+			}
+		do {
+			if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y) && 
+				data->GetValue(n, m, &z)){
+				if(ic) {
+					pos1.fx = pos2.fx;		pos1.fy = pos2.fy;	pos1.fz = pos2.fz;
+					}
+				else {
+					pos1.fx = x;			pos1.fy = y;		pos1.fz = z;
+					}
+				pos2.fx = x;	pos2.fy = y;	pos2.fz = z;
+				if(!bRet) memcpy(&pos1, &pos2, sizeof(fPOINT3D));
+				if(Balls) Balls[ic] = new Sphere(this, data, 0, x, y, z, rad, i, j, k, l, m, n);
+				if(Columns)Columns[ic] = new Brick(this, data, x, 0.0, z, 
+					bar_d, bar_w, y, 0x800L, i, j, -1, -1, m, n, -1, -1, -1, -1, k, l);
+				if(DropLines) DropLines[ic] = new DropLine3D(this, data, &pos2, i, j, k, l, m, n);
+				if(Arrows) Arrows[ic] = new Arrow3D(this, data, &pos1, &pos2, 
+					i, j, k, l, m, n, i2, j2, k2, l2, m2, n2);
+				((Plot3D *)parent)->CheckBounds3D(x, y, z);		CheckBounds3D(x, y, z);
+				bRet = true;
+				}
+			i2 = i;	j2 = j;	k2 = k;	l2 = l;	m2 = m;	n2 = n;
+			ic++;
+			}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l) && rZ->GetNext(&m, &n));
+		if(!bRet) InfoBox("The selected data range\nis empty or does not contain\nvalid"
+			" data triplets!");
+		bRet = true;
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;			free(Dlg3D);
+	if(rX) delete rX;		if(rY) delete rY;	if(rZ) delete rZ;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// user defined function properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Function::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 37, 10, "Function"};
+	TabSHEET tab2 = {37, 60, 10, "Line"};
+	DlgInfo FuncDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 160, 10, 32, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 160, 25, 32, 12},
+		{3, 10, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{4, 5, 100, ISPARENT, SHEET, &tab1, 5, 10, 149, 110},
+		{5, 0, 500, ISPARENT | CHECKED, SHEET, &tab2, 5, 10, 149, 110},
+		{10, 600, 0, 0x0L, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"plot user defined function", 10, 24, 100, 8},
+		{101, 102, 0, 0x0L, RTEXT, (void*)"where x=", 10, 40, 28, 8}, 
+		{102, 103, 0, 0x0L, EDVAL1, &x1, 38, 40, 25, 10},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"until", 61, 40, 17, 8}, 
+		{104, 105, 0, 0x0L, EDVAL1, &x2, 78, 40, 25, 10},
+		{105, 106, 0, 0x0L, RTEXT, (void*)"step", 102, 40, 17, 8}, 
+		{106, 107, 0, 0x0L, EDVAL1, &xstep, 119, 40, 25, 10},
+		{107, 200, 0, 0x0L, RTEXT, (void*)"y=", 10, 56, 10, 8}, 
+		{200, 0, 0, 0x0L, TEXTBOX, (void*)cmdxy, 22, 54, 122, 40},
+		{500, 0, 0, NOSELECT, ODBUTTON, (void*)OD_linedef, 15, 25, 130, 100},
+		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 170, 45, 15, 15},
+		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 170, 60, 15, 15},
+		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 170, 75, 15, 15},
+		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 170, 90, 15, 15}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, undo_level = *Undo.pcb;
+	bool bRet = false, bNew = (dl == 0L);
+	DWORD undo_flags = 0L;
+	LineDEF newLine;
+	double o_x1, n_x1, o_x2, n_x2, o_xstep, n_xstep;
+	anyOutput *cdisp = Undo.cdisp;
+
+	if(!parent) return false;
+	if(parent->Id == GO_FITFUNC) return parent->PropertyDlg();
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+	if(!(Dlg = new DlgRoot(FuncDlg, data))) return false;
+	if(!bNew) Dlg->ShowItem(10, false);
+	Dlg->GetValue(102, &o_x1);		n_x1 = o_x1;
+	Dlg->GetValue(104, &o_x2);		n_x2 = o_x2;
+	Dlg->GetValue(106, &o_xstep);	n_xstep = o_xstep;
+	hDlg = CreateDlgWnd("Function Plot", 50, 50, 400, 276, Dlg, 0x4L);
+	if(bNew) Dlg->SetCheck(4, 0L, true);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case 600:	case 601:	case 602:	case 603:
+			Undo.SetDisp(cdisp);
+			res = ExecDrawOrderButt(parent, this, res);
+			}
+		}while (res < 0);
+	Undo.SetDisp(cdisp);
+	if(res == 2) while(*Undo.pcb > undo_level)	Undo.Restore(true, cdisp);
+	else if(res == 1){					//OK pressed
+		if(bNew) {						//create function
+			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+			Dlg->GetValue(102, &x1);		Dlg->GetValue(104, &x2);
+			Dlg->GetValue(106, &xstep);		Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE);
+			cmdxy = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+			ReshapeFormula(&cmdxy);			bRet = Update(0L, 0L);
+			}
+		else {							//edit existing function
+			Dlg->GetValue(102, &n_x1);		Dlg->GetValue(104, &n_x2);
+			Dlg->GetValue(106, &n_xstep);
+			undo_flags = CheckNewFloat(&x1, o_x1, n_x1, this, undo_flags);
+			undo_flags = CheckNewFloat(&x2, o_x2, n_x2, this, undo_flags);
+			undo_flags = CheckNewFloat(&xstep, o_xstep, n_xstep, this, undo_flags);
+			if(Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE))
+				undo_flags = CheckNewString(&cmdxy, cmdxy, TmpTxt, this, undo_flags);
+			if(undo_flags & UNDO_CONTINUE){
+				Update(0L, UNDO_CONTINUE);		Command(CMD_MRK_DIRTY, 0L, 0L);
+				}
+			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+			if(cmpLineDEF(&Line, &newLine)) {
+				Undo.Line(parent, &Line, undo_flags);	undo_flags |= UNDO_CONTINUE;
+				memcpy(&Line, &newLine, sizeof(LineDEF));
+				}
+			bRet = (undo_flags & UNDO_CONTINUE) != 0;
+			}
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// fit function by nonlinear regression
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+FitFunc::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 22, 10, "Data"};
+	TabSHEET tab2 = {22, 59, 10, "Function"};
+	TabSHEET tab3 = {59, 82, 10, "Line"};
+	char text1[100], text2[100];
+	double iter;
+	bool bNew = (dl == 0L);
+	DlgInfo FuncDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 160, 10, 32, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 160, 25, 32, 12},
+		{3, 10, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{4, 5, 400, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 149, 125},
+		{5, 6, 100, ISPARENT, SHEET, &tab2, 5, 10, 149, 125},
+		{6, 7, 500, ISPARENT, SHEET, &tab3, 5, 10, 149, 125},
+		{7, 0, 0, 0x0L, PUSHBUTTON, (void*)"Fit", 160, 123, 32, 12},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"fit function by nonlinear regression", 10, 24, 100, 8},
+		{101, 102, 0, 0x0L, LTEXT, (void*)"parameters and initial values:", 10, 34, 100, 8},
+		{102, 150, 0, 0x0L, TEXTBOX, (void*)parxy, 22, 44, 122, 25},
+		{150, 151, 0, 0x0L, LTEXT, (void*)"function, y=f(x):", 10, 72, 10, 8}, 
+		{151, 152, 0, 0x0L, RTEXT, (void*)"y=", 10, 86, 10, 8}, 
+		{152, 153, 0, 0x0L, RTEXT, (void*)"converg.:", 20, 118, 26, 8}, 
+		{153, 154, 0, 0x0L, EDVAL1, &conv, 46, 118, 25, 10},
+		{154, 155, 0, 0x0L, RTEXT, (void*)"iterations:", 72, 118, 47, 8}, 
+		{155, 200, 0, 0x0L, EDVAL1, &iter, 119, 118, 25, 10},
+		{200, 0, 0, 0x0L, TEXTBOX, (void*)cmdxy, 22, 84, 122, 30},
+		{400, 401, 0, 0x0L, LTEXT, (void*)"range for X Data", 10, 30, 60, 8},
+		{401, 402, 0, 0x0L, RANGEINPUT, (void*)text1, 20, 40, 100, 10},
+		{402, 403, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 55, 60, 8},
+		{403, 404, 0, 0x0L, RANGEINPUT, (void*)text2, 20, 65, 100, 10},
+		{404, 405, 0, CHECKED, CHECKBOX, (void*)"draw symbols", 20, 95, 60, 8},
+		{405, 0, 0, HIDDEN, LTEXT, 0L, 20, 95, 60, 8},
+		{500, 550, 501, CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{501, 502, 0, 0x0L, RTEXT, (void*)"plot x=", 10, 30, 28, 8}, 
+		{502, 503, 0, 0x0L, EDVAL1, &x1, 38, 30, 25, 10},
+		{503, 504, 0, 0x0L, RTEXT, (void*)"until", 61, 30, 17, 8}, 
+		{504, 505, 0, 0x0L, EDVAL1, &x2, 78, 30, 25, 10},
+		{505, 506, 0, 0x0L, RTEXT, (void*)"step", 102, 30, 17, 8}, 
+		{506, 0, 0, 0x0L, EDVAL1, &xstep, 119, 30, 25, 10},
+		{550, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_linedef, 15, bNew ? 35:45, 130, 100}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, undo_level = *Undo.pcb, i, j, k, l, cs;
+	size_t cb;
+	bool bRet = false, bContinue = false;
+	DWORD undo_flags = 0L;
+	LineDEF newLine;
+	double tmp, tmpy, o_x1, n_x1, o_x2, n_x2, o_xstep, n_xstep, n_chi2=chi2;
+	AccRange *rX, *rY;
+	char *o_cmdxy, *o_parxy, *tmp_char;
+	anyOutput *cdisp = Undo.cdisp;
+	anyResult *ares;
+
+	if(!parent || !data) return false;
+	if(!(o_cmdxy = (char*)memdup(cmdxy, (int)strlen(cmdxy)+1, 0))) return false;;
+	if(!(o_parxy = (char*)memdup(parxy, (int)strlen(parxy)+1, 0))) return false;;
+	UseRangeMark(data, 1, text1, text2);
+	iter = (double)maxiter;
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+	if(!(Dlg = new DlgRoot(FuncDlg, data))) return false;
+	if(!bNew){
+		Dlg->ShowItem(10, false);		Dlg->Activate(401, false);
+		Dlg->Activate(403, false);		Dlg->SetCheck(6, 0L, true);
+		Dlg->SetCheck(4, 0L, false);	Dlg->ShowItem(404, false);
+		if(chi2 > 0.0) {
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "Chi 2 = %g", chi2);
+#else
+			sprintf(TmpTxt, "Chi 2 = %g", chi2);
+#endif
+			Dlg->SetText(405,TmpTxt);	Dlg->ShowItem(405, true);
+			}
+		}
+	else {
+		Dlg->ShowItem(500, false);
+		}
+	Dlg->GetValue(502, &o_x1);		n_x1 = o_x1;
+	Dlg->GetValue(504, &o_x2);		n_x2 = o_x2;
+	Dlg->GetValue(506, &o_xstep);	n_xstep = o_xstep;
+	hDlg = CreateDlgWnd("Fit Function to Data", 50, 50, 400, 306, Dlg, 0x4L);
+	if(bNew) Dlg->SetCheck(4, 0L, true);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(Dlg->GetCheck(10)) res = -1;
+			if(bContinue) res = -1;
+			bContinue = false;
+			break;
+		case 1:
+			if(!bNew){
+				if(Dlg->GetText(102, TmpTxt, TMP_TXT_SIZE)) {
+					if(parxy = (char*)realloc(parxy, (cb = strlen(TmpTxt)+2)))
+						rlp_strcpy(parxy, (int)cb, TmpTxt);
+					}
+				if(Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE)) {
+					if(cmdxy = (char*)realloc(cmdxy, (cb = strlen(TmpTxt)+2)))
+						rlp_strcpy(cmdxy, (int)cb, TmpTxt);
+					}
+				ReshapeFormula(&parxy);		ReshapeFormula(&cmdxy);
+				dirty = true;
+				break;
+				}
+		case 7:								//Start: do nonlinear regression
+			Undo.SetDisp(cdisp);
+			if(Dlg->GetCheck(5)) {			//  the function tab must be shown
+				if(Dlg->CurrDisp) Dlg->CurrDisp->MouseCursor(MC_WAIT, true);
+				Dlg->GetText(401, text1, 100);		Dlg->GetText(403, text2, 100);
+				if(Dlg->GetText(102, TmpTxt, TMP_TXT_SIZE)) {
+					if(parxy = (char*)realloc(parxy, (cb = strlen(TmpTxt)+2)))
+						rlp_strcpy(parxy, (int)cb, TmpTxt);
+					}
+				if(Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE)) {
+					if(cmdxy = (char*)realloc(cmdxy, (cb = strlen(TmpTxt)+2)))
+						rlp_strcpy(cmdxy, (int)cb, TmpTxt);
+					}
+				Dlg->GetValue(153, &conv);	Dlg->GetValue(155, &iter);
+				ReshapeFormula(&parxy);		ReshapeFormula(&cmdxy);
+				do_formula(data, 0L);		//clear any error condition
+				ares = do_formula(data, parxy);
+				if(ares->type != ET_VALUE) {
+					ErrorBox("Syntax Error in parameters.");
+					bContinue = true;	res = -1;
+					break;
+					}
+				ares = do_formula(data, cmdxy);
+				if(ares->type != ET_VALUE) {
+					ErrorBox("Syntax Error in formula.");
+					bContinue = true;	res = -1;
+					break;
+					}
+				i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE-2, parxy);	i += rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, ";x=1;");
+				rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, cmdxy);	yywarn(0L, true);
+				ares = do_formula(data, TmpTxt);
+				if(tmp_char = yywarn(0L, false)) {
+					ErrorBox(tmp_char);
+					bContinue = true;	res = -1;
+					break;
+					}
+				i = do_fitfunc(data, text1, text2, 0L, &parxy, cmdxy, conv, (int)iter, &chi2);
+				Dlg->SetText(102, parxy);
+				if(i >1 || res == 7) {
+#ifdef USE_WIN_SECURE
+					sprintf_s(TmpTxt, TMP_TXT_SIZE, "The Levenberg-Marquart algorithm\nexited after %d iterations.\n\nChi2 = %g", i, chi2);
+#else
+					sprintf(TmpTxt, "The Levenberg-Marquart algorithm\nexited after %d iterations.\n\nChi2 = %g", i, chi2);
+#endif
+					InfoBox(TmpTxt);
+					}
+				bContinue = true;
+				if(res == 7) res = -1;
+				if(Dlg->CurrDisp) Dlg->CurrDisp->MouseCursor(MC_ARROW, true);
+				}
+			else {							//diplay function tab first
+				Dlg->SetCheck(5, 0L, true);
+				res = -1;
+				}
+			break;
+			}
+		}while (res < 0);
+	Undo.SetDisp(cdisp);
+	while(*Undo.pcb > undo_level)	Undo.Pop(cdisp);
+	if(res == 1){						//OK pressed
+		//get ranges for x- and y data (again).
+		chi2 = n_chi2;
+		if(Dlg->GetText(401, text1, 100) && (ssXref = (char*)realloc(ssXref, (cb = strlen(text1)+2))))
+			rlp_strcpy(ssXref, (int)cb, text1);
+		if(Dlg->GetText(403, text2, 100) && (ssYref = (char*)realloc(ssYref, (cb = strlen(text2)+2))))
+			rlp_strcpy(ssYref, (int)cb, text2);
+		if(bNew) {						//create function
+			if(!(rX = new AccRange(text1)) || ! (rY = new AccRange(text2))) return false;
+			i = rX->CountItems();	maxiter = int(iter);
+			if(Dlg->GetCheck(404)) Symbols = (Symbol**)calloc(i, sizeof(Symbol*));
+			x1 = HUGE_VAL;	x2 = -HUGE_VAL;	cs = 0;
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
+			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
+			rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
+			do {
+				if(data->GetValue(j, i, &tmp) && data->GetValue(l, k, &tmpy)){
+					if(tmp < x1) x1 = tmp;			if(tmp > x2) x2 = tmp;
+					if(Symbols) Symbols[cs++] = new Symbol(this, data, tmp, tmpy, SYM_CIRCLE, i, j, k, l);
+					nPoints = cs;					CheckBounds(tmp, tmpy);
+					}
+				}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
+			delete rX;	delete rY;
+			if(x1 >= x2 || !(dl = new Function(this, data, "Fitted function"))){
+				CloseDlgWnd(hDlg);
+				delete Dlg;
+				return false;
+				}
+			xstep = (x2 - x1)/100.0;
+			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+			dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
+			dl->SetSize(SIZE_XSTEP, xstep);			dl->Command(CMD_SETFUNC, cmdxy, 0L);
+			dl->Command(CMD_SETPARAM, parxy, 0L);	dl->Command(CMD_SET_LINE, &Line, 0L);
+			dl->Command(CMD_AUTOSCALE, 0L, 0L);		CheckBounds(dl->Bounds.Xmin, dl->Bounds.Ymin);
+			CheckBounds(dl->Bounds.Xmax, dl->Bounds.Ymax);
+			Command(CMD_ENDDIALOG, 0L, 0L);			bRet = true;
+			}
+		else {							//edit existing function
+			Dlg->GetValue(502, &n_x1);		Dlg->GetValue(504, &n_x2);
+			Dlg->GetValue(506, &n_xstep);
+			undo_flags = CheckNewFloat(&x1, o_x1, n_x1, this, undo_flags);
+			undo_flags = CheckNewFloat(&x2, o_x2, n_x2, this, undo_flags);
+			undo_flags = CheckNewFloat(&xstep, o_xstep, n_xstep, this, undo_flags);
+			if(undo_flags){
+				dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
+				dl->SetSize(SIZE_XSTEP, xstep);			dl->Command(CMD_SETFUNC, cmdxy, 0L);
+				dl->Command(CMD_SETPARAM, parxy, 0L);	dl->Command(CMD_SET_LINE, &Line, 0L);
+				dl->Update(0L, UNDO_CONTINUE);
+				}
+			undo_flags = CheckNewString(&parxy, o_parxy, parxy, this, undo_flags);
+			undo_flags = CheckNewString(&cmdxy, o_cmdxy, cmdxy, this, undo_flags);
+			if(undo_flags & UNDO_CONTINUE) {
+				Undo.ValInt(parent, (int*)&dirty, undo_flags);
+				Command(CMD_MRK_DIRTY, 0L, 0L);
+				}
+			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+			if(cmpLineDEF(&Line, &newLine)) {
+				Undo.Line(parent, &Line, undo_flags);	undo_flags |= UNDO_CONTINUE;
+				memcpy(&Line, &newLine, sizeof(LineDEF));
+				}
+			bRet = (undo_flags & UNDO_CONTINUE) != 0;
+			Command(CMD_ENDDIALOG, 0L, 0L);
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(o_parxy) free(o_parxy);		if(o_cmdxy) free(o_cmdxy);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Create a new normal quantile plot
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *NormQuantDlg_Tmpl = 
+	"1,2,,DEFAULT,PUSHBUTTON,-1,148,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,148,25,45,12\n"
+	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,10,100,ISPARENT | CHECKED,SHEET,1,5,10,130,80\n"
+	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,101,,,LTEXT,2,10,30,60,8\n"
+	"101,,,LASTOBJ,RANGEINPUT,-15,20,40,100,10";
+
+bool
+NormQuant::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	DlgInfo *QuantDlg;
+	void *dyndata[] = {(void*)&tab1, (void*)"range for variables"};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res;
+	char *mrk;
+	bool bContinue = false, bRet = false;
+
+	if(!parent || !data) return false;
+	if(!(QuantDlg = CompileDialog(NormQuantDlg_Tmpl, dyndata))) return false;
+	if(data->Command(CMD_GETMARK, &mrk, 0L))rlp_strcpy(TmpTxt, TMP_TXT_SIZE, mrk);
+	else UseRangeMark(data, 1, TmpTxt);
+	if(!(Dlg = new DlgRoot(QuantDlg, data)))return false;
+	hDlg = CreateDlgWnd("Normal Quantiles Plot", 50, 50, 420, 220, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+			}
+		}while (res < 0);
+	if(res == 1 && Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)){
+		ssRef = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+		bRet = ProcessData();
+		}
+	CloseDlgWnd(hDlg);	delete Dlg;	free(QuantDlg);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Contour Plot
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *ContourPlot_Tmpl = 
+	"1,2,,DEFAULT,PUSHBUTTON,-1,142,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,142,25,45,12\n"
+	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,5,100,ISPARENT | CHECKED,SHEET,1,5,10,131,100\n"
+	"5,6,200,ISPARENT,SHEET,5,5,10,131,100\n"
+	"6,10,300,ISPARENT,SHEET,13,5,10,131,100\n"
+	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,+,,,LTEXT,2,10,30,60,8\n"
+	".,.,,,RANGEINPUT,-15,20,40,100,10\n"
+	".,.,,,LTEXT,3,10,55,60,8\n"
+	".,.,,,RANGEINPUT,-16,20,65,100,10\n"
+	".,.,,,LTEXT,4,10,80,60,8\n"
+	".,,,,RANGEINPUT,-17,20,90,100,10\n"
+	"200,,201,CHECKED,GROUPBOX,6,10,30,121,70\n"
+	".,+,,CHECKED,RADIO1,7,20,38,60,9\n"
+	".,.,,,RADIO1,8,20,48,60,9\n"
+	".,.,,,RADIO1,9,20,58,60,9\n"
+	".,.,,,RADIO1,10,20,68,60,9\n"
+	".,.,,,EDVAL1,11,50,78,30,10\n"
+	".,,,,RTEXT,12,15,79,33,8\n"
+	"300,310,301,CHECKED,GROUPBOX,14,10,30,121,55\n"
+	".,+,,CHECKED,RADIO1,15,20,38,60,9\n"
+	".,.,,,RADIO1,16,20,48,60,9\n"
+	".,.,,,RADIO1,17,20,58,60,9\n"
+	".,,,,RADIO1,18,20,68,60,9\n"
+	"310,,,LASTOBJ,CHECKBOX,19,20,90,60,9";
+
+bool
+ContourPlot::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25,56, 10, "Details"};
+	TabSHEET tab3 = {56,92, 10, "Symbols"};
+	void *dyndata[] = {(void*)&tab1, (void*)"range for x-data", (void*)"range for y-data", 
+		(void*)"range for z-data", (void*)&tab2, (void*)" super rectangle ", (void*)" at z minimum",
+		(void*)" at z maximum", (void*)" at z mean", (void*)" at user defined level,", (void*)&sr_zval, (void*)"z =",
+		(void*)&tab3, (void*)"  draw symbols  ", (void*)" no symbols", (void*)" draw minima", (void*)" draw maxima",
+		(void*)" draw all source data", (void*)" symbols with labels"};
+	DlgInfo *DlgInf;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res;
+	bool bRet = false, bContinue = false;
+
+	if(!data || !parent)return false;
+	if(!(DlgInf = CompileDialog(ContourPlot_Tmpl, dyndata)))return false;
+	UseRangeMark(data, 1, TmpTxt, TmpTxt+100, TmpTxt+200);
+	if(!(Dlg = new DlgRoot(DlgInf, data)))return false;
+	hDlg = CreateDlgWnd((char*)"Create Contour Plot", 50, 50, 388, 260, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:								// focus lost
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 1:
+			res = -1;		bContinue = false;
+			if(Dlg->GetCheck(202))flags = 0x01;
+			else if(Dlg->GetCheck(203))flags = 0x02;
+			else if(Dlg->GetCheck(204)){
+				flags = 0x03;	Dlg->GetValue(205, &sr_zval);
+				}
+			else flags = 0x00;
+			if(Dlg->GetCheck(302)) flags |= 0x10;
+			else if(Dlg->GetCheck(303)) flags |= 0x20;
+			else if(Dlg->GetCheck(304)) flags |= 0x30;
+			if(Dlg->GetCheck(310)) flags |= 0x40;
+			if(Dlg->GetText(101, TmpTxt, 100) && Dlg->GetText(103, TmpTxt+100, 100) 
+				&& Dlg->GetText(105, TmpTxt+200, 100)){
+				if(LoadData(TmpTxt, TmpTxt+100, TmpTxt+200)) {
+					ssRefX = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+					ssRefY = (char*)memdup(TmpTxt+100, (int)strlen(TmpTxt+100)+1, 0);
+					ssRefZ = (char*)memdup(TmpTxt+200, (int)strlen(TmpTxt+200)+1, 0);
+					res = 1;		bRet = true;
+					}
+				}
+			if(res != 1) {
+				bContinue = true;
+				ErrorBox("Missing or too\nfew data\n");
+				}
+			break;
+			}
+		}while (res <0);
+	CloseDlgWnd(hDlg);		delete Dlg;			free(DlgInf);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Create a three dimensional graph
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *AddAxis3D_Tmpl =
+		"1,2,,DEFAULT, PUSHBUTTON,-1,148,10,45,12\n"
+		"2,3,,,PUSHBUTTON,-2,148,25,45,12\n"
+		"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+		"4,5,50,TOUCHEXIT | ISPARENT,SHEET,1,5,10,130,148\n"
+		"5,6,200,ISPARENT | CHECKED,SHEET,2,5,10,130,148\n"
+		"6,20,300,ISPARENT,SHEET,3,5,10,130,148\n"
+		"20,,,NOSELECT,ODBUTTON,22,142,65,45,45\n"
+		"50,51,100,ISPARENT | CHECKED,GROUPBOX,4,10,30,120,36\n"
+		"51,120,,,CHECKBOX,5,17,37,80,8\n"
+		"100,101,,,RTEXT,6,10,51,35,8\n"
+		"101,102,,,EDVAL1,7,48,51,32,10\n"
+		"102,103,,,CTEXT,8,81,51,11,8\n"
+		"103,,,,EDVAL1,9,93,51,32,10\n"
+		"120,,121,ISPARENT | CHECKED,GROUPBOX,10,10,72,120,20\n"
+		"121,122,,,RTEXT,11,10,77,25,8\n"
+		"122,123,,,EDVAL1,12,37,77,25,10\n"
+		"123,124,,,LTEXT,-3,63,77,10,8\n"
+		"124,125,,,RTEXT,-11,73,77,25,8\n"
+		"125,130,,OWNDIALOG,COLBUTT,14,100,77,25,10\n"
+		"130,131,,ISPARENT | CHECKED,GROUPBOX,15,10,98,120,20\n"
+		"131,,,,EDTEXT,0,15,103,110,10\n"
+		"200,201,,,LTEXT,16,10,23,70,9\n"
+		"201,202,,CHECKED | EXRADIO,ODBUTTON,17,20,35,25,25\n"
+		"202,203,,EXRADIO,ODBUTTON,17,45,35,25,25\n"
+		"203,204,,EXRADIO,ODBUTTON,17,70,35,25,25\n"
+		"204,205,,EXRADIO,ODBUTTON,17,95,35,25,25\n"
+		"205,206,,EXRADIO,ODBUTTON,17,20,60,25,25\n"
+		"206,207,,EXRADIO,ODBUTTON,17,45,60,25,25\n"
+		"207,208,,EXRADIO,ODBUTTON,17,70,60,25,25\n"
+		"208,209,,EXRADIO,ODBUTTON,17,95,60,25,25\n"
+		"209,210,,EXRADIO,ODBUTTON,17,20,85,25,25\n"
+		"210,211,,EXRADIO,ODBUTTON,17,45,85,25,25\n"
+		"211,212,,EXRADIO,ODBUTTON,17,70,85,25,25\n"
+		"212,213,,EXRADIO,ODBUTTON,17,95,85,25,25\n"
+		"213,214,,,LTEXT,-12,20,120,70,9\n"
+		"214,215,,,LTEXT,-13,20,132,70,9\n"
+		"215,216,,,LTEXT,-14,20,144,70,9\n"
+		"216,217,250,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+		"217,218,260,HIDDEN | ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+		"218,,270,HIDDEN |ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+		"250,251,,,LTEXT,19,10,110,70,9\n"
+		"251,252,,,EDVAL1,0,58,120,32,10\n"
+		"252,253,,,LTEXT,-3,92,120,20,9\n"
+		"253,254,,,EDVAL1,0,43,132,32,10\n"
+		"254,255,,,CTEXT,-7,75,132,7,9\n"
+		"255,256,,,EDVAL1,0,82,132,32,10\n"
+		"256,257,,,LTEXT,-3,116,132,20,9\n"
+		"257,268,,,EDVAL1,0,58,144,32,10\n"
+		"260,261,,,LTEXT,20,10,110,70,9\n"
+		"261,262,,,EDVAL1,0,43,120,32,10\n"
+		"262,263,,,CTEXT,-7,75,120,7,9\n"
+		"263,264,,,EDVAL1,0,82,120,32,10\n"
+		"264,265,,,LTEXT,-3,116,120,20,9\n"
+		"265,266,,,EDVAL1,0,58,132,32,10\n"
+		"266,267,,,LTEXT,-3,92,132,20,9\n"
+		"267,268,,,EDVAL1,0,58,144,32,10\n"
+		"268,,,,LTEXT,-3,92,144,20,9\n"
+		"270,271,,,LTEXT,21,10,110,70,9\n"
+		"271,272,,,EDVAL1,0,58,120,32,10\n"
+		"272,273,,,LTEXT,-3,92,120,20,9\n"
+		"273,274,,,EDVAL1,0,58,132,32,10\n"
+		"274,275,,,LTEXT,-3,92,132,20,9\n"
+		"275,276,,,EDVAL1,0,43,144,32,10\n"
+		"276,277,,,CTEXT,-7,75,144,7,9\n"
+		"277,278,,,EDVAL1,0,82,144,32,10\n"
+		"278,,,,LTEXT,-3,116,144,20,9\n"
+		"300,,,LASTOBJ | NOSELECT,ODBUTTON,18,15,30,110,140";
+bool
+Plot3D::AddAxis()
+{
+	TabSHEET tab1 = {0, 25, 10, "Axis"};
+	TabSHEET tab2 = {25, 52, 10, "Style"};
+	TabSHEET tab3 = {52, 78, 10, "Plots"};
+	AxisDEF axis, ax_def[12], *caxdef;
+	double sizAxLine = DefSize(SIZE_AXIS_LINE);
+	DWORD colAxis = 0x0;
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)" scaling ", (void*)" automatic scaling",
+		(void*)"axis from", (void*)&axis.min, (void*)"to", (void*)&axis.max, (void*)" line ", (void*)"width",
+		(void*)&sizAxLine, (void*)0L, (void *)&colAxis, (void*)" axis label ", (void*)"select a template:",
+		(void*)(OD_NewAxisTempl3D), (void*)OD_axisplot, (void*)"y-axis at:", (void*)"x-axis at:", (void*)"z-axis at:",
+		(void*)(OD_AxisDesc3D)};
+	DlgInfo *NewAxisDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, res, currTempl = 201, ax_type = 2, tick_type = 2;
+	double tlb_dx, tlb_dy, lb_x, lb_y;
+	TextDEF label_def, tlbdef;
+	anyOutput *cdisp = Undo.cdisp;
+	Axis *the_new, **tmpAxes;
+	bool bAxis = false, bRet = false;
+	char **names;
+	GraphObj **somePlots;
+	Label *label;
+
+	if(!Axes || nAxes < 3 || !(NewAxisDlg = CompileDialog(AddAxis3D_Tmpl, dyndata))) return false;
+	lb_y = 0.0;		lb_x = DefSize(SIZE_AXIS_TICKS)*4.0;
+	tlb_dy = 0.0;	tlb_dx = -DefSize(SIZE_AXIS_TICKS)*2.0;
+	tlbdef.ColTxt = colAxis;				tlbdef.ColBg = 0x00ffffffL;
+	tlbdef.RotBL = tlbdef.RotCHAR = 0.0f;	tlbdef.fSize = DefSize(SIZE_TICK_LABELS);
+	tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
+	tlbdef.Style = TXS_NORMAL;				tlbdef.Mode = TXM_TRANSPARENT;
+	tlbdef.Font = FONT_HELVETICA;			tlbdef.text = 0L;
+	if(!(names = (char**)calloc(nscp+2, sizeof(char*))))return false;
+	if(!(somePlots = (GraphObj**)calloc(nscp+2, sizeof(GraphObj*))))return false;
+	for(i = 0; i < 12; i++) {
+		ax_def[i].flags = AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK;
+		ax_def[i].owner = 0L;		ax_def[i].breaks = 0L;
+		ax_def[i].Center.fx = ax_def[i].Center.fy = ax_def[i].Radius = 0.0;
+		ax_def[i].nBreaks = 0L;
+		}
+	//y-axes
+	ax_def[0].min = ax_def[1].min = ax_def[2].min = ax_def[3].min = (caxdef = Axes[1]->GetAxis())->min;
+	ax_def[0].max = ax_def[1].max = ax_def[2].max = ax_def[3].max = caxdef->max;
+	ax_def[0].Start = ax_def[1].Start = ax_def[2].Start = ax_def[3].Start = caxdef->Start;
+	ax_def[0].Step = ax_def[1].Step = ax_def[2].Step = ax_def[3].Step = caxdef->Step;
+	ax_def[0].loc[0].fy  = ax_def[1].loc[0].fy = ax_def[2].loc[0].fy = ax_def[3].loc[0].fy = caxdef->loc[0].fy;
+	ax_def[0].loc[1].fy  = ax_def[1].loc[1].fy = ax_def[2].loc[1].fy = ax_def[3].loc[1].fy = caxdef->loc[1].fy;
+	ax_def[0].loc[0].fx = ax_def[0].loc[1].fx = ax_def[3].loc[0].fx = ax_def[3].loc[1].fx = cu1.fx;
+	ax_def[1].loc[0].fx = ax_def[1].loc[1].fx = ax_def[2].loc[0].fx = ax_def[2].loc[1].fx = cu2.fx;
+	ax_def[0].loc[0].fz = ax_def[0].loc[1].fz = ax_def[1].loc[0].fz = ax_def[1].loc[1].fz = cu2.fz;
+	ax_def[2].loc[0].fz = ax_def[2].loc[1].fz = ax_def[3].loc[0].fz = ax_def[3].loc[1].fz = cu1.fz;
+	ax_def[1].flags = ax_def[2].flags = AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_POSTICKS;
+	ax_def[0].flags = ax_def[3].flags = AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_NEGTICKS;
+	//x-axes
+	ax_def[4].min = ax_def[6].min = ax_def[8].min = ax_def[10].min = (caxdef = Axes[0]->GetAxis())->min;
+	ax_def[4].max = ax_def[6].max = ax_def[8].max = ax_def[10].max = caxdef->max;
+	ax_def[4].Start = ax_def[6].Start = ax_def[8].Start = ax_def[10].Start = caxdef->Start;
+	ax_def[4].Step = ax_def[6].Step = ax_def[8].Step = ax_def[10].Step = caxdef->Step;
+	ax_def[4].loc[0].fx  = ax_def[6].loc[0].fx = ax_def[8].loc[0].fx = ax_def[10].loc[0].fx = caxdef->loc[0].fx;
+	ax_def[4].loc[1].fx  = ax_def[6].loc[1].fx = ax_def[8].loc[1].fx = ax_def[10].loc[1].fx = caxdef->loc[1].fx;
+	ax_def[4].loc[0].fy = ax_def[4].loc[1].fy = ax_def[6].loc[0].fy = ax_def[6].loc[1].fy = cu1.fy;
+	ax_def[8].loc[0].fy = ax_def[8].loc[1].fy = ax_def[10].loc[0].fy = ax_def[10].loc[1].fy = cu2.fy;
+	ax_def[4].loc[0].fz = ax_def[4].loc[1].fz = ax_def[8].loc[0].fz = ax_def[8].loc[1].fz = cu2.fz;
+	ax_def[6].loc[0].fz = ax_def[6].loc[1].fz = ax_def[10].loc[0].fz = ax_def[10].loc[1].fz = cu1.fz;
+	ax_def[4].flags = ax_def[6].flags = AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_NEGTICKS;
+	ax_def[8].flags = ax_def[10].flags = AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_POSTICKS;
+	//z-axes
+	ax_def[5].min = ax_def[7].min = ax_def[9].min = ax_def[11].min = (caxdef = Axes[2]->GetAxis())->min;
+	ax_def[5].max = ax_def[7].max = ax_def[9].max = ax_def[11].max = caxdef->max;
+	ax_def[5].Start = ax_def[7].Start = ax_def[9].Start = ax_def[11].Start = caxdef->Start;
+	ax_def[5].Step = ax_def[7].Step = ax_def[9].Step = ax_def[11].Step = caxdef->Step;
+	ax_def[5].loc[0].fz  = ax_def[7].loc[0].fz = ax_def[9].loc[0].fz = ax_def[11].loc[0].fz = caxdef->loc[0].fz;
+	ax_def[5].loc[1].fz  = ax_def[7].loc[1].fz = ax_def[9].loc[1].fz = ax_def[11].loc[1].fz = caxdef->loc[1].fz;
+	ax_def[5].loc[0].fx = ax_def[5].loc[1].fx = ax_def[9].loc[0].fx = ax_def[9].loc[1].fx = cu2.fx;
+	ax_def[7].loc[0].fx = ax_def[7].loc[1].fx = ax_def[11].loc[0].fx = ax_def[11].loc[1].fx = cu1.fx;
+	ax_def[5].loc[0].fy = ax_def[5].loc[1].fy = ax_def[7].loc[0].fy = ax_def[7].loc[1].fy = cu1.fy;
+	ax_def[9].loc[0].fy = ax_def[9].loc[1].fy = ax_def[11].loc[0].fy = ax_def[11].loc[1].fy = cu2.fy;
+	ax_def[5].flags = ax_def[9].flags = AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_POSTICKS;
+	ax_def[7].flags = ax_def[11].flags = AXIS_3D | AXIS_DEFRECT | AXIS_AUTOTICK | AXIS_NEGTICKS;
+	//the default axis is the first
+	memcpy(&axis, &ax_def[0], sizeof(AxisDEF));
+	if(names[0] = (char*)malloc(10)) rlp_strcpy(names[0], 10, "[none]");
+	for(i = 0, j = 1; i < nscp; i++) {
+		if(Sc_Plots[i] && Sc_Plots[i]->name){
+			names[j] = (char*)memdup(Sc_Plots[i]->name, (int)strlen(Sc_Plots[i]->name)+1, 0);
+			somePlots[j++] = Sc_Plots[i];
+			}
+		}
+	OD_axisplot(OD_ACCEPT, 0L, 0L, 0L, names, 0);
+	if(!(Dlg = new DlgRoot(NewAxisDlg, data)))return false;
+	Dlg->SetValue(251, ax_def[0].loc[0].fx);		Dlg->SetValue(253, ax_def[0].loc[0].fy);
+	Dlg->SetValue(255, ax_def[0].loc[1].fy);		Dlg->SetValue(257, ax_def[0].loc[0].fz);
+	if(!nscp){						//must be root plot3d to link to plot
+		Dlg->ShowItem(6, false);
+		}
+	hDlg = CreateDlgWnd("Add Axis to 3D Plot", 50, 50, 400, 360, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res){
+		case 4:											//the axis sheet
+			res = -1;
+			bAxis = true;
+			break;
+		//axis templates
+		case 201:	case 202:	case 203:	case 204:	case 205:	case 206:
+		case 207:	case 208:	case 209:	case 210:	case 211:	case 212:
+			i = currTempl-201;
+			switch(currTempl){
+				case 201:	case 202:	case 203:	case 204:		//prevoius is y-template
+					Dlg->GetValue(251, &axis.loc[0].fx);	Dlg->GetValue(253, &axis.loc[0].fy);
+					Dlg->GetValue(255, &axis.loc[1].fy);	Dlg->GetValue(257, &axis.loc[0].fz);
+					axis.loc[1].fx = axis.loc[0].fx;		axis.loc[1].fz = axis.loc[0].fz;
+					memcpy(&ax_def[i], &axis, sizeof(AxisDEF));
+					break;
+				case 205:	case 207:	case 209:	case 211:		//prevoius is x-template
+					Dlg->GetValue(261, &axis.loc[0].fx);	Dlg->GetValue(263, &axis.loc[1].fx);
+					Dlg->GetValue(265, &axis.loc[0].fy);	Dlg->GetValue(267, &axis.loc[0].fz);
+					axis.loc[1].fy = axis.loc[0].fy;		axis.loc[1].fz = axis.loc[0].fz;
+					memcpy(&ax_def[i], &axis, sizeof(AxisDEF));
+					break;
+				case 206:	case 208:	case 210:	case 212:		//previous is z-template
+					Dlg->GetValue(271, &axis.loc[0].fx);	Dlg->GetValue(273, &axis.loc[0].fy);
+					Dlg->GetValue(275, &axis.loc[0].fz);	Dlg->GetValue(277, &axis.loc[1].fz);
+					axis.loc[1].fx = axis.loc[0].fx;		axis.loc[1].fy = axis.loc[0].fy;
+					memcpy(&ax_def[i], &axis, sizeof(AxisDEF));
+					break;
+				}
+			i = res-201;
+			switch (res) {
+			case 201:	case 202:	case 203:	case 204:			//y-template
+				Dlg->ShowItem(216, true);		Dlg->ShowItem(217, false);
+				Dlg->ShowItem(218, false);
+				Dlg->SetValue(251, ax_def[i].loc[0].fx);	Dlg->SetValue(253, ax_def[i].loc[0].fy);
+				Dlg->SetValue(255, ax_def[i].loc[1].fy);	Dlg->SetValue(257, ax_def[i].loc[0].fz);
+				ax_type = tick_type = 2;	tlb_dy = 0.0;
+				if(res == 202 || res == 203){
+					tlb_dx = DefSize(SIZE_AXIS_TICKS)*2.0;
+					tlbdef.Align = TXA_VCENTER | TXA_HLEFT;
+					}
+				else {
+					tlb_dx = -DefSize(SIZE_AXIS_TICKS)*2.0;
+					tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
+					}
+				break;
+			case 205:	case 207:	case 209:	case 211:			//x-template
+				Dlg->ShowItem(216, false);		Dlg->ShowItem(217, true);
+				Dlg->ShowItem(218, false);
+				Dlg->SetValue(261, ax_def[i].loc[0].fx);	Dlg->SetValue(263, ax_def[i].loc[1].fx);
+				Dlg->SetValue(265, ax_def[i].loc[0].fy);	Dlg->SetValue(267, ax_def[i].loc[0].fz);
+				ax_type = 1;	tick_type = 3; tlb_dx = 0;
+				if(res == 205 || res == 207){
+					tlb_dy = DefSize(SIZE_AXIS_TICKS)*2.0;
+					tlbdef.Align = TXA_VTOP | TXA_HCENTER;
+					}
+				else {
+					tlb_dy = -DefSize(SIZE_AXIS_TICKS)*2.0;
+					tlbdef.Align = TXA_VBOTTOM | TXA_HCENTER;
+					}
+				break;
+			case 206:	case 208:	case 210:	case 212:			//z-template
+				Dlg->ShowItem(216, false);		Dlg->ShowItem(217, false);
+				Dlg->ShowItem(218, true);
+				Dlg->SetValue(271, ax_def[i].loc[0].fx);	Dlg->SetValue(273, ax_def[i].loc[0].fy);
+				Dlg->SetValue(275, ax_def[i].loc[0].fz);	Dlg->SetValue(277, ax_def[i].loc[1].fz);
+				ax_type = 3;	tick_type = 2;	tlb_dy = 0;
+				if(res == 206 || res == 210){
+					tlb_dx = DefSize(SIZE_AXIS_TICKS)*2.0;
+					tlbdef.Align = TXA_VCENTER | TXA_HLEFT;
+					}
+				else {
+					tlb_dx = -DefSize(SIZE_AXIS_TICKS)*2.0;
+					tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
+					}
+				break;
+				}
+			memcpy(&axis, &ax_def[res-201], sizeof(AxisDEF));
+			currTempl = res;
+			Dlg->DoPlot(0L);
+			res = -1;
+			break;
+			}
+		}while (res < 0);
+	if(res == 1) {
+		Undo.SetDisp(cdisp);
+		Dlg->GetValue(122, &sizAxLine);				Dlg->GetColor(125, &colAxis);
+		Dlg->GetValue(101, &axis.min);				Dlg->GetValue(103, &axis.max);
+		axis.Start = axis.min;						axis.Center.fx = axis.Center.fy = 0.0;
+		axis.nBreaks = 0;			axis.breaks = 0L;		axis.Radius = 0.0;	
+		tlbdef.ColTxt =	colAxis;
+		label_def.ColTxt = colAxis;					label_def.ColBg = 0x00ffffffL;
+		label_def.fSize = DefSize(SIZE_TICK_LABELS)*1.2f;	label_def.RotBL = 0.0f;
+		label_def.RotCHAR = 0.0f;								label_def.iSize = 0;
+		label_def.Align = TXA_VTOP | TXA_HCENTER;		label_def.Mode = TXM_TRANSPARENT;
+		label_def.Style = TXS_NORMAL;	label_def.Font = FONT_HELVETICA;
+		if(Dlg->GetCheck(51)) axis.flags |= AXIS_AUTOSCALE;
+		if(the_new = new Axis(this, data, &axis, axis.flags)){
+			the_new->SetSize(SIZE_TLB_YDIST, tlb_dy);	the_new->SetSize(SIZE_TLB_XDIST, tlb_dx); 
+			the_new->SetSize(SIZE_AXIS_LINE, sizAxLine);
+			the_new->SetColor(COL_AXIS, colAxis);
+			the_new->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			the_new->SetSize(SIZE_LB_XDIST, lb_x);		the_new->SetSize(SIZE_LB_YDIST, lb_y);
+			the_new->type = ax_type;
+			the_new->Command(CMD_TICK_TYPE, &tick_type, 0L);
+			if(Dlg->GetText(131, TmpTxt, TMP_TXT_SIZE)) label_def.text = TmpTxt;
+			else label_def.text = 0L;
+			if(label = new Label(Axes[0], data, (axis.loc[0].fx + axis.loc[1].fx)/2.0,
+				(axis.loc[0].fy + axis.loc[1].fy)/2.0, &label_def, 
+				label_def.RotBL < 45.0 ? LB_Y_PARENT : LB_X_PARENT)){
+				label->SetSize(SIZE_LB_XDIST, lb_x);	label->SetSize(SIZE_LB_YDIST, lb_y); 
+				if(the_new->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
+				else DeleteGO(label);
+				}
+			for(i = 0; i < nAxes && Axes[i]; i++);
+			if(i < nAxes) {
+				Undo.SetGO(this, (GraphObj**)(&Axes[i]), the_new, 0L);
+				bRet = true;
+				}
+			else {
+				if(tmpAxes = (Axis**)calloc(nAxes+1, sizeof(Axis*))){
+					memcpy(tmpAxes, Axes, nAxes * sizeof(Axis*));
+					Undo.ListGOmoved((GraphObj**)Axes, (GraphObj**)tmpAxes, nAxes);
+					Undo.SetGO(this, (GraphObj**)(&tmpAxes[nAxes]), the_new, 0L);
+					free(Axes);			Axes = tmpAxes;
+					i = nAxes++;		bRet = true;
+					}
+				}
+			CurrAxes = Axes;
+			if(bRet) {
+				OD_axisplot(OD_ACCEPT, 0L, 0L, (anyOutput*) &res, 0L, 0);
+				if(res && i) somePlots[res]->Command(CMD_USEAXIS, &i, 0L);
+				}
+			}
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;			free(NewAxisDlg);
+	if(names) {
+		for(j = 0; names[j]; j++) if(names[j]) free(names[j]);
+		free(names);
+		}
+	if(somePlots) free(somePlots);
+	return bRet;
+}
+
+static char *AddPlot3Dtmpl =
+		"1,2,,DEFAULT,PUSHBUTTON,-1,150,10,45,12\n"
+		"2,3,,,PUSHBUTTON,-2,150,25,45,12\n"
+		"3,,560,ISPARENT | CHECKED,GROUPBOX,1,5,10,140,70\n"
+		"560,561,,EXRADIO | CHECKED,ODBUTTON,2,12,20,25,25\n"
+		"561,562,,EXRADIO,ODBUTTON,2,37,20,25,25\n"
+		"562,563,,EXRADIO,ODBUTTON,2,62,20,25,25\n"
+		"563,564,,EXRADIO,ODBUTTON,2,87,20,25,25\n"
+		"564,565,,EXRADIO,ODBUTTON,2,112,20,25,25\n"
+		"565,566,,EXRADIO,ODBUTTON,2,12,45,25,25\n"
+		"566,567,,EXRADIO,ODBUTTON,2,37,45,25,25\n"
+		"567,,,LASTOBJ | EXRADIO,ODBUTTON,2,62,45,25,25";
+
+bool
+Plot3D::AddPlot(int family)
+{
+	void *dyndata[] = {(void *)"  select template  ", (void*)(OD_PlotTempl)};
+	DlgInfo *PlotsDlg = CompileDialog(AddPlot3Dtmpl, dyndata);
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, cSel = 560;
+	bool bRet = false;
+	Plot *p;
+
+	if(!(Dlg = new DlgRoot(PlotsDlg, data)))return false;
+	Dlg->bModal = false;
+	hDlg = CreateDlgWnd("Add Plot", 50, 50, 410, 204, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 560:	case 561:	case 562:	case 563:	case 564:
+		case 565:	case 566:	case 567:
+			if(res == cSel) res = 1;
+			else {
+				cSel = res;		res = -1;
+				}
+			break;
+			}
+		}while (res < 0);
+	if(res == 1){						//OK pressed
+		switch (cSel) {
+		case 560:		p = new Scatt3D(this, data, 0x01);			break;
+		case 561:		p = new Scatt3D(this, data, 0x02);			break;
+		case 562:		p = new Scatt3D(this, data, 0x04);			break;
+		case 563:		p = new BubblePlot3D(this, data);			break;
+		case 564:		p = new Scatt3D(this, data, 0x2000);		break;
+		case 565:		p = new Func3D(this, data);					break;
+		case 566:		p = new FitFunc3D(this, data);				break;
+		case 567:		p = new Scatt3D(this, data, 0x4000);		break;
+		default:		p = 0L;										break;
+			}
+		if(p && p->PropertyDlg()) {
+			if(!(bRet = Command(CMD_DROP_PLOT, p, (anyOutput *)NULL))) delete p;
+			}
+		else if(p) delete p;
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;		free(PlotsDlg);
+	return bRet;
+}
+
+bool
+Plot3D::PropertyDlg()
+{
+	Plot *p;
+	bool bRet = false;
+
+	if(plots) {
+		//plots already created - jump to configuration dialog
+		return false;
+		}
+	if((p = new Scatt3D(this, data, crea_flags)) && p->PropertyDlg()) {
+		if(!(bRet = Command(CMD_DROP_PLOT, p, (anyOutput *)NULL))) DeleteGO(p);
+		}
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Create a 2.5 dimensional bar chart
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *Base25D_DlgTmpl = 
+		"1,2,,DEFAULT, PUSHBUTTON,-1,158,10,45,12\n"
+		"2,3,,,PUSHBUTTON,-2,158,25,45,12\n"
+		"3,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+		"10,11,100,ISPARENT | CHECKED,SHEET,1,5,10,140,100\n"
+		"11,12,200,ISPARENT,SHEET,2,5,10,140,100\n"
+		"12,20,300,ISPARENT,SHEET,3,5,10,140,100\n"
+		"20,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+		"100,101,,,LTEXT,4,15,30,60,8\n"
+		"101,152,,,RANGEINPUT,5,25,40,100,10\n"
+		"152,153,,ISPARENT | CHECKED,GROUPBOX,6,12,60,128,45\n"
+		"153,154,,,LTEXT,0,25,65,60,8\n"
+		"154,155,,,RANGEINPUT,5,25,75,100,10\n"
+		"155,156,,,PUSHBUTTON,-8,95,87,30,12\n"
+		"156,,,,PUSHBUTTON,-9,60,87,35,12\n"
+		"200,201,,,LTEXT,7,20,35,80,8\n"
+		"201,202,,,RTEXT,8,48,45,13,8\n"
+		"202,203,,,EDVAL1,9,65,45,25,10\n"
+		"203,204,,,RTEXT,10,48,57,13,8\n"
+		"204,,,,EDVAL1,11,65,57,25,10\n"
+		"300,301,,,RADIO1,12,15,35,80,9\n"
+		"301,302,,ODEXIT,COLBUTT,13,110,35,20,10\n"
+		"302,303,,CHECKED,RADIO1,14,15,55,80,9\n"
+		"303,304,,ODEXIT,COLBUTT,15,25,70,10,10\n"
+		"304,305,,ODEXIT,COLBUTT,16,37,70,10,10\n"
+		"305,306,,ODEXIT,COLBUTT,17,49,70,10,10\n"
+		"306,307,,ODEXIT,COLBUTT,18,61,70,10,10\n"
+		"307,308,,ODEXIT,COLBUTT,19,73,70,10,10\n"
+		"308,309,,ODEXIT,COLBUTT,20,85,70,10,10\n"
+		"309,310,,ODEXIT,COLBUTT,21,97,70,10,10\n"
+		"310,,,LASTOBJ | ODEXIT,COLBUTT,22,109,70,10,10";
+bool
+Chart25D::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 55, 10, "Details"};
+	TabSHEET tab3 = {55, 90, 10, "Scheme"};
+	static DWORD colarr[] = {0x000080ffL, 0x00ff8000L, 0x0000ff00L, 0x000000ffL,
+		0x00ff00ff, 0x00ffff00L, 0x0000ffff, 0x00c0c0c0};
+	static DWORD defcol = 0x00ffffffL;
+	double start_z = 1.0;
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)"range for common x values",
+		(void*)TmpTxt, (void*)" ranges for y values ", (void*)"distances:", (void*)"start z =", 
+		(void*)&start_z, (void*)"step =", (void*)&dspm.fz, (void*)" common color for columns:",
+		(void*)&defcol, (void*)" increment color scheme:", (void*)&colarr[0], (void*)&colarr[1],
+		(void*)&colarr[2], (void*)&colarr[3], (void*)&colarr[4], (void*)&colarr[5], (void*)&colarr[6],
+		(void*)&colarr[7]};
+	DlgInfo *Bar3D_Dlg = CompileDialog(Base25D_DlgTmpl, dyndata);
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, ic, res, currYR=0, maxYR=0, nx=0, ny, rx, cx, ry, cy, oax;
+	char **rd = 0L;
+	double fx, fy, fz, fsz;
+	bool updateYR = true, bContinue = false, bRet = false, bUseSch;
+	AccRange *rX = 0L, *rY = 0L;
+	Brick **cols;
+	Scatt3D *plot;
+	AxisDEF *ax;
+
+	if(!parent || !data) return false;
+	if(plots) {
+		//Plots alredy defined: jump to config dialog
+		return false;
+		}
+	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
+		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return false;
+	if(TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
+		for(i=100, j= 0; i <= 1000; i +=100){ 
+			if(TmpTxt[i]) rd[j++] = (char*)memdup(TmpTxt+i, (int)strlen(TmpTxt+i)+1, 0);
+			}
+		if(j) maxYR = j-1;
+		}
+	if(!rd && !(rd = (char**)calloc(1, sizeof(char*))))return false;
+	if(!(Dlg = new DlgRoot(Bar3D_Dlg, data))) return false;
+	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
+	hDlg = CreateDlgWnd("Create 3D Bar Chart", 50, 50, 420, 260, Dlg, 0x4L);
+	do {
+		if(updateYR) {
+			if(currYR >0) {
+				Dlg->ShowItem(156, true);
+				Dlg->Activate(101, false);
+				}
+			else {
+				Dlg->ShowItem(156, false);
+				Dlg->Activate(101, true);
+				}
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "y-values # %d/%d", currYR+1, maxYR+1);
+#else
+			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
+#endif
+			//SetText will also cause a redraw of the whole dialog
+			Dlg->SetText(153, TmpTxt);
+			updateYR = false;
+			}
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		ny = 0;
+		if(rX) delete rX;
+		rX = 0L;
+		switch(res) {
+		case 0:
+			if(bContinue || Dlg->GetCheck(20)) res = -1;
+			break;
+		case -1:
+			bContinue = false;			break;
+		case 1:
+			for(i = 0; i < 8; i++) Dlg->GetColor(303+i, &colarr[i]);
+			Dlg->GetColor(301, &defcol);
+			bUseSch = Dlg->GetCheck(302);
+			Dlg->GetValue(202, &start_z);	Dlg->GetValue(204, &dspm.fz);
+			//execute com_StackDlg for <OK>
+		case 155:		case 156:
+			res = com_StackDlg(res, Dlg, &rX, &nx, &rd, &currYR,
+				&rY, &bContinue, &ny, &maxYR, &updateYR);
+			break;
+		case 301:
+			Dlg->SetCheck(300, 0L, true);
+			res = -1;	break;
+		case 303:	case 304:	case 305:	case 306:
+		case 307:	case 308:	case 309:	case 310:
+			Dlg->SetCheck(302, 0L, true);
+			res = -1;	break;
+			}
+		}while (res < 0);
+	if(res == 1 && nx && rX && rd && rd[0] && rd[0][0]) {
+		if(rd[maxYR]) maxYR++;
+		fsz = DefSize(SIZE_BAR)/2.0;	fz = start_z;
+		oax = AxisTempl3D;	AxisTempl3D = 1;	CreateAxes();
+		if(Axes && nAxes > 2 && Axes[2] && (ax = Axes[2]->GetAxis())){
+			ax->flags = AXIS_3D | AXIS_INVERT | AXIS_DEFRECT;
+			ax->min = start_z-dspm.fz;
+			ax->max = start_z+dspm.fz*maxYR;
+			if(Axes[1] && (ax = Axes[1]->GetAxis())){
+				ax->flags |= AXIS_GRIDLINE;
+				i = 0x0c;
+				Axes[1]->Command(CMD_SET_GRIDTYPE, &i, 0L);
+				}
+			AxisTempl3D = oax;
+			}
+		if(plots = (GraphObj**)calloc(maxYR, sizeof(GraphObj*))) {
+			for(i = 0; i < maxYR; i++, fz += dspm.fz) {
+				if(rd[i] && (cols = (Brick**)calloc(nx, sizeof(Brick*))) && (rY = new AccRange(rd[i]))) {
+					ic = 0;
+					if(rX->GetFirst(&cx, &rx) && rX->GetNext(&cx, &rx) &&
+						rY->GetFirst(&cy, &ry) && rY->GetNext(&cy, &ry)) {
+						do {
+							if(data->GetValue(rx, cx, &fx) && data->GetValue(ry, cy, &fy)){
+								cols[ic] = new Brick(this, data, fx, 0.0, fz, 
+									fsz, fsz, fy, 0x800L, cx, rx, -1, -1, -1, -1,
+									-1, -1, -1, -1, cy, ry);
+								}
+							ic++;
+							}while(rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry));
+						if(ic) bRet = true;
+						}
+					if(plot = new Scatt3D(this, data, cols, ic)){
+						if(bUseSch) plot->SetColor(COL_BAR_FILL, colarr[(i & 0x07)]);
+						else plot->SetColor(COL_BAR_FILL, defcol);
+						plots[nPlots++] = plot;
+						}
+					if(rY) {
+						plot->data_desc = rY->RangeDesc(data, 1);
+						delete(rY);		rY = 0L;
+						}
+					}
+				}
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rd) {
+		for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
+		free(rd);
+		}
+	if(rX) delete rX;		if(rY) delete rY;		free(Bar3D_Dlg);
+	if(bRet) Command(CMD_MRK_DIRTY, 0L, 0L);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Create a 2.5 dimensional ribbon chart
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Ribbon25D::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 55, 10, "Details"};
+	TabSHEET tab3 = {55, 90, 10, "Scheme"};
+	static DWORD colarr[] = {0x000080ffL, 0x00ff8000L, 0x0000ff00L, 0x000000ffL,
+		0x00ff00ff, 0x00ffff00L, 0x0000ffff, 0x00c0c0c0};
+	static DWORD defcol = 0x00ffffffL;
+	double start_z = 1.0;
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)"range for common x values",
+		(void*)TmpTxt, (void*)" ranges for y values ", (void*)"distances:", (void*)"start z =", 
+		(void*)&start_z, (void*)"step =", (void*)&dspm.fz, (void*)" common color for ribbons:",
+		(void*)&defcol, (void*)" increment color scheme:", (void*)&colarr[0], (void*)&colarr[1],
+		(void*)&colarr[2], (void*)&colarr[3], (void*)&colarr[4], (void*)&colarr[5], (void*)&colarr[6],
+		(void*)&colarr[7]};
+	DlgInfo *Bar3D_Dlg = CompileDialog(Base25D_DlgTmpl, dyndata);
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, res, currYR=0, maxYR=0, nx=0, ny, oax;
+	char **rd = 0L, xrange[100];
+	double fz;
+	bool updateYR = true, bContinue = false, bRet = false, bUseSch;
+	AccRange *rX = 0L, *rY = 0L;
+	AxisDEF *ax;
+	Ribbon *plot;
+
+	if(!parent || !data) return false;
+	if(plots) {
+		//Plots alredy defined: jump to config dialog
+		return false;
+		}
+	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
+		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return false;
+	if(TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
+		for(i=100, j= 0; i <= 1000; i +=100){ 
+			if(TmpTxt[i]) rd[j++] = (char*)memdup(TmpTxt+i, (int)strlen(TmpTxt+i)+1, 0);
+			}
+		if(j) maxYR = j-1;
+		}
+	if(!rd && !(rd = (char**)calloc(1, sizeof(char*))))return false;
+	if(!(Dlg = new DlgRoot(Bar3D_Dlg, data)))return false;
+	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
+	hDlg = CreateDlgWnd("Create 3D Ribbon Chart", 50, 50, 420, 260, Dlg, 0x4L);
+	do {
+		if(updateYR) {
+			if(currYR >0) {
+				Dlg->ShowItem(156, true);		Dlg->Activate(101, false);
+				}
+			else {
+				Dlg->ShowItem(156, false);		Dlg->Activate(101, true);
+				}
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "y-values # %d/%d", currYR+1, maxYR+1);
+#else
+			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
+#endif
+			//SetText will also cause a redraw of the whole dialog
+			Dlg->SetText(153, TmpTxt);
+			updateYR = false;
+			}
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		ny = 0;		if(rX) delete rX;		rX = 0L;
+		switch(res) {
+		case 0:
+			if(bContinue || Dlg->GetCheck(20)) res = -1;
+			break;
+		case -1:
+			bContinue = false;			break;
+		case 1:
+			for(i = 0; i < 8; i++) Dlg->GetColor(303+i, &colarr[i]);
+			Dlg->GetColor(301, &defcol);	bUseSch = Dlg->GetCheck(302);
+			Dlg->GetValue(202, &start_z);	Dlg->GetValue(204, &dspm.fz);
+			//execute com_StackDlg for <OK>
+		case 155:		case 156:
+			res = com_StackDlg(res, Dlg, &rX, &nx, &rd, &currYR,
+				&rY, &bContinue, &ny, &maxYR, &updateYR);
+			break;
+		case 301:
+			Dlg->SetCheck(300, 0L, true);
+			res = -1;	break;
+		case 303:	case 304:	case 305:	case 306:
+		case 307:	case 308:	case 309:	case 310:
+			Dlg->SetCheck(302, 0L, true);
+			res = -1;	break;
+			}
+		}while (res < 0);
+	if(res == 1 && nx && rX && rd && rd[0] && rd[0][0]) {
+		if(rd[maxYR]) maxYR++;					fz = start_z;
+		Dlg->GetText(101, TmpTxt+100, 100);
+		oax = AxisTempl3D;	AxisTempl3D = 1;	CreateAxes();
+		if(Axes && nAxes > 2 && Axes[2] && (ax = Axes[2]->GetAxis())){
+			ax->flags = AXIS_3D | AXIS_INVERT | AXIS_DEFRECT;
+			ax->min = start_z-dspm.fz;
+			ax->max = start_z+dspm.fz*maxYR;
+			if(Axes[1] && (ax = Axes[1]->GetAxis())){
+				ax->flags |= AXIS_GRIDLINE;	
+				i = 0x0c;
+				Axes[1]->Command(CMD_SET_GRIDTYPE, &i, 0L);
+				}
+			AxisTempl3D = oax;
+			}
+		if(plots = (GraphObj**)calloc(maxYR, sizeof(GraphObj*))) {
+			Dlg->GetText(101, xrange, 100);
+			for(i = 0; i < maxYR; i++, fz += dspm.fz) {
+				if(plot = new Ribbon(this, data, fz, dspm.fz, xrange, rd[i])){
+					if(bUseSch) plot->SetColor(COL_POLYGON, colarr[(i & 0x07)]);
+					else plot->SetColor(COL_POLYGON, defcol);
+					plots[nPlots++] = plot;
+					}
+				}
+			}
+		Command(CMD_MRK_DIRTY, 0L, 0L);		Command(CMD_AUTOSCALE, 0L, 0L);
+		bRet = true;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rd) {
+		for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
+		free(rd);
+		}
+	if(rX) delete rX;		if(rY) delete rY;		free(Bar3D_Dlg);
+	if(bRet) Command(CMD_MRK_DIRTY, 0L, 0L);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Create a 3 dimensional bubble plot
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+BubblePlot3D::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 22, 10, "Data"};
+	TabSHEET tab3 = {22, 47, 10, "Axes"};
+	char text1[100], text2[100], text3[100], text4[100];
+	DlgInfo BubDlg3D[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 142, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 142, 25, 45, 12},
+		{3, 50, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{4, 5, 100, TOUCHEXIT | ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 131, 142},
+		{5, 10, 400, TOUCHEXIT | ISPARENT, SHEET, &tab3, 5, 10, 131, 142},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
+		{50, 0, 0, NOSELECT, ODBUTTON, (void*)(OD_AxisDesc3D), 142, 65, 45, 45},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"range for X Data", 10, 25, 60, 8},
+		{101, 102, 0, 0x0L, RANGEINPUT, text1, 20, 35, 100, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 48, 60, 8},
+		{103, 104, 0, 0x0L, RANGEINPUT, text2, 20, 58, 100, 10},
+		{104, 105, 0, 0x0L, LTEXT, (void*)"range for Z Data", 10, 71, 60, 8},
+		{105, 106, 0, 0x0L, RANGEINPUT, text3, 20, 81, 100, 10},
+		{106, 0, 150, ISPARENT | CHECKED, GROUPBOX, (void*)" diameter ", 8, 98, 125, 50},
+		{150, 151, 0, 0x0L, LTEXT, (void*)"range for diameters", 12, 102, 60, 8},
+		{151, 152, 0, 0x0L, RANGEINPUT, text4, 20, 112, 100, 10},
+		{152, 153, 0, 0x0L, LTEXT, (void*)"scaling:", 12, 125, 20, 8},
+		{153, 154, 0, TOUCHEXIT | CHECKED, RADIO1, (void*)Units[defs.cUnits].display, 38, 125, 20, 8},
+		{154, 155, 0, TOUCHEXIT, RADIO1, (void*)"with x-values", 70, 125, 20, 8},
+		{155, 156, 0, TOUCHEXIT, RADIO1, (void*)"with y-values", 20, 135, 20, 8},
+		{156, 0, 0, TOUCHEXIT, RADIO1, (void*)"with z-values", 70, 135, 20, 8},
+		{400, 410, 0, 0x0L, LTEXT, (void*)"select template:", 20, 30, 60, 8},
+		{410, 411, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_AxisTempl3D), 20, 42, 25, 25},
+		{411, 412, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_AxisTempl3D), 45, 42, 25, 25},
+		{412, 0, 0, LASTOBJ | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_AxisTempl3D), 70, 42, 25, 25}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, res, count;
+	int cx, rx, cy, ry, cz, rz, cr, rr, s_type = 5;
+	bool bRet = false;
+	double fx, fy, fz, fr;
+	Sphere **Balls;
+	AccRange *rX, *rY, *rZ, *rR;
+	Scatt3D *sc_plot;
+
+	if(!data || !parent)return false;
+	UseRangeMark(data, 1, text1, text2, text3, text4);
+	if(!(Dlg = new DlgRoot(BubDlg3D, data)))return false;
+	rX = rY = rZ = rR = 0L;
+	Dlg->SetCheck(410 + AxisTempl3D, 0L, true);
+	hDlg = CreateDlgWnd("Bubble Plot 3D", 50, 50, 388, 340, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 0:
+			if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case 4:		case 5:					//the tab sheets
+			res = -1;
+			break;
+		case 153:	
+			s_type = 5;						//absolute size, but use 5 to distinguish
+			res = -1;						//  from symbol
+			break;
+		case 154:	case 155:	case 156:
+			s_type = res - 153;
+			res = -1;
+			break;
+		case 410:	case 411:	case 412:	//axis templates
+			AxisTempl3D = res-410;
+			res = -1;
+			break;
+			}
+		}while (res <0);
+	if(res == 1) {
+		if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) rX = new AccRange(TmpTxt);
+		if(Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE)) rY = new AccRange(TmpTxt);
+		if(Dlg->GetText(105, TmpTxt, TMP_TXT_SIZE)) rZ = new AccRange(TmpTxt);
+		if(Dlg->GetText(151, TmpTxt, TMP_TXT_SIZE)) rR = new AccRange(TmpTxt);
+		if(rX && rY && rZ && rR && (count = rX->CountItems()) 
+			&& (Balls = (Sphere**)calloc(count, sizeof(Sphere*)))) {
+			rX->GetFirst(&cx, &rx);		rY->GetFirst(&cy, &ry);
+			rZ->GetFirst(&cz, &rz);		rR->GetFirst(&cr, &rr);
+			for(i = 0; i < count; i++){
+				if(rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry)
+					&& rZ->GetNext(&cz, &rz) && rR->GetNext(&cr, &rr)
+					&& data->GetValue(rx, cx, &fx) && data->GetValue(ry, cy, &fy)
+					&& data->GetValue(rz, cz, &fz) && data->GetValue(rr, cr, &fr)) {
+					Balls[i] = new Sphere(this, data, s_type, fx, fy, fz, fr, cx, rx,
+						cy, ry, cz, rz, cr, rr);
+					}
+				}
+			sc_plot = new Scatt3D(this, data, Balls, count);
+			if(parent->Id == GO_PLOT3D || parent->Id == GO_FUNC3D || parent->Id == GO_FITFUNC3D) {
+				if(!(parent->Command(CMD_DROP_PLOT, sc_plot, 0L))) delete(sc_plot);
+				bRet = true;
+				}
+			else if(!(bRet = Command(CMD_DROP_PLOT, sc_plot, 0L))) delete(sc_plot);
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rX) delete rX;	if(rY) delete rY;	if(rZ) delete rZ;	if(rR) delete rR;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Create a 3D function plot
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Func3D::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 37, 10, "Function"};
+	TabSHEET tab2 = {37, 65, 10, "Style"};
+	FillDEF newFill;
+	DlgInfo FuncDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 160, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 160, 25, 45, 12},
+		{3, 10, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{4, 5, 100, ISPARENT, SHEET, &tab1, 5, 10, 149, 134},
+		{5, 50, 300, ISPARENT | CHECKED, SHEET, &tab2, 5, 10, 149, 134},
+		{10, 0, 0, 0x0L, CHECKPIN, 0L, 5, 0, 12, 8},
+		{50, 0, 0, NOSELECT, ODBUTTON, (void*)(OD_AxisDesc3D), 160, 85, 45, 45},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"plot user defined function", 10, 30, 100, 8},
+		{101, 102, 0, 0x0L, RTEXT, (void*)"where x=", 10, 50, 28, 8}, 
+		{102, 103, 0, 0x0L, EDVAL1, &x1, 38, 50, 25, 10},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"until", 61, 50, 17, 8}, 
+		{104, 105, 0, 0x0L, EDVAL1, &x2, 78, 50, 25, 10},
+		{105, 106, 0, 0x0L, RTEXT, (void*)"step", 102, 50, 17, 8}, 
+		{106, 107, 0, 0x0L, EDVAL1, &xstep, 119, 50, 25, 10},
+		{107, 108, 0, 0x0L, RTEXT, (void*)"z=", 10, 62, 28, 8}, 
+		{108, 109, 0, 0x0L, EDVAL1, &z1, 38, 62, 25, 10},
+		{109, 110, 0, 0x0L, EDVAL1, &z2, 78, 62, 25, 10},
+		{110, 150, 0, 0x0L, EDVAL1, &zstep, 119, 62, 25, 10},
+		{150, 200, 0, 0x0L, RTEXT, (void*)"y=", 10, 91, 10, 8}, 
+		{200, 0, 0, 0x0L, TEXTBOX, (void*)cmdxy, 22, 89, 122, 40},
+		{300, 301, 500, CHECKED, GROUPBOX, (void*)" grid ", 10, 40, 140, 100},
+		{301, 305, 400, HIDDEN | CHECKED, GROUPBOX, (void*)" surface ", 10, 40, 140, 100},
+		{305, 306, 0, CHECKED | TOUCHEXIT, RADIO1, (void*) " grid lines", 15, 25, 50, 10},
+		{306, 0, 0, TOUCHEXIT, RADIO1, (void*) " surface", 85, 25, 50, 10},
+		{400, 401, 0, 0x0L, RTEXT, (void*)"grid line width", 38, 50, 40, 8},
+		{401, 402, 0, 0x0L, EDVAL1, &Line.width, 80, 50, 25, 10},
+		{402, 403, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 107, 50, 20, 8},
+		{403, 404, 0, 0x0L, RTEXT, (void*)"grid line color", 38, 62, 40, 8},
+		{404, 405, 0, OWNDIALOG, COLBUTT, (void *)&Line.color, 80, 62, 25, 10},
+		{405, 406, 0, 0x0L, RTEXT,(void*)"plane color" , 38, 74, 40, 8},
+		{406, 0, 0, OWNDIALOG, SHADE3D, &newFill, 80, 74, 25, 10},
+		{500, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_linedef, 15, 45, 130, 100}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, undo_level = *Undo.pcb;
+	bool bRet = false, bNew = true;
+	DWORD undo_flags = 0L;
+	LineDEF newLine;
+	double o_x1, n_x1, o_x2, n_x2, o_xstep, n_xstep;
+	double o_z1, n_z1, o_z2, n_z2, o_zstep, n_zstep;
+	anyOutput *cdisp = Undo.cdisp;
+
+	if(!parent) return false;
+//	if(parent->Id == GO_FITFUNC) return parent->PropertyDlg();
+	memcpy(&newFill, &Fill, sizeof(FillDEF));
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+	if(!(Dlg = new DlgRoot(FuncDlg, data))) return false;
+	if(!bNew) Dlg->ShowItem(10, false);
+	Dlg->GetValue(102, &o_x1);		n_x1 = o_x1;
+	Dlg->GetValue(104, &o_x2);		n_x2 = o_x2;
+	Dlg->GetValue(106, &o_xstep);	n_xstep = o_xstep;
+	Dlg->GetValue(108, &o_z1);		n_z1 = o_z1;
+	Dlg->GetValue(109, &o_z2);		n_z2 = o_z2;
+	Dlg->GetValue(110, &o_zstep);	n_zstep = o_zstep;
+	hDlg = CreateDlgWnd("3D Function Plot", 50, 50, 426, 328, Dlg, 0x4L);
+	if(bNew) Dlg->SetCheck(4, 0L, true);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 305:		case 306:
+			if(Dlg->GetCheck(305)) {
+				Dlg->ShowItem(300, true);	Dlg->ShowItem(301, false);
+				}
+			else {
+				Dlg->ShowItem(300, false);	Dlg->ShowItem(301, true);
+				}
+			Dlg->DoPlot(0L);
+			res = -1;
+			break;
+		case 0:
+			if(Dlg->GetCheck(10)) res = -1;
+			break;
+			}
+		}while (res < 0);
+	Undo.SetDisp(cdisp);
+	while(*Undo.pcb > undo_level)	Undo.Pop(cdisp);
+	if(res == 1){						//OK pressed
+		if(bNew) {						//create function
+			if(Dlg->GetCheck(305)) {
+				type = 0;
+				OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+				}
+			else {
+				type = 1;
+				memcpy(&Fill, &newFill, sizeof(FillDEF));
+				Dlg->GetValue(401, &Line.width);
+				Dlg->GetColor(404, &Line.color);
+				Line.pattern = 0L;
+				Line.patlength = 1;
+				}
+			Dlg->GetValue(102, &x1);		Dlg->GetValue(104, &x2);
+			Dlg->GetValue(106, &xstep);
+			Dlg->GetValue(108, &z1);		Dlg->GetValue(109, &z2);
+			Dlg->GetValue(110, &zstep);		type = Dlg->GetCheck(305) ? 0 : 1;
+			if(Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE)) {
+				if(cmdxy) free(cmdxy);		cmdxy = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+				ReshapeFormula(&cmdxy);		bRet = Update();
+				}
+			}
+		else {							//edit existing function
+			Dlg->GetValue(102, &n_x1);		Dlg->GetValue(104, &n_x2);
+			Dlg->GetValue(106, &n_xstep);
+			undo_flags = CheckNewFloat(&x1, o_x1, n_x1, this, undo_flags);
+			undo_flags = CheckNewFloat(&x2, o_x2, n_x2, this, undo_flags);
+			undo_flags = CheckNewFloat(&xstep, o_xstep, n_xstep, this, undo_flags);
+			TmpTxt[0] = 0;	Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE);
+			undo_flags = CheckNewString(&cmdxy, cmdxy, TmpTxt, this, undo_flags);
+//			if(undo_flags & UNDO_CONTINUE) Update(0L, UNDO_CONTINUE);
+			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+			if(cmpLineDEF(&Line, &newLine)) {
+				Undo.Line(parent, &Line, undo_flags);	undo_flags |= UNDO_CONTINUE;
+				memcpy(&Line, &newLine, sizeof(LineDEF));
+				}
+			bRet = (undo_flags & UNDO_CONTINUE) != 0;
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Fit a 3D function to data
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+FitFunc3D::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 22, 10, "Data"};
+	TabSHEET tab2 = {22, 59, 10, "Function"};
+	TabSHEET tab3 = {59, 87, 10, "Style"};
+	char text1[100], text2[100], text3[100];
+	FillDEF newFill;
+	double iter;
+//	bool bNew = (dl == 0L);
+	bool bNew = true;
+	DlgInfo FuncDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 160, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 160, 25, 45, 12},
+		{3, 10, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{4, 5, 400, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 149, 134},
+		{5, 6, 100, ISPARENT, SHEET, &tab2, 5, 10, 149, 134},
+		{6, 7, 300, ISPARENT, SHEET, &tab3, 5, 10, 149, 134},
+		{7, 0, 0, 0x0L, PUSHBUTTON, (void*)"Fit", 160, 132, 45, 12},
+		{10, 50, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
+		{50, 0, 0, NOSELECT, ODBUTTON, (void*)(OD_AxisDesc3D), 160, 65, 45, 45},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"fit function by nonlinear regression", 10, 24, 100, 8},
+		{101, 102, 0, 0x0L, LTEXT, (void*)"parameters and initial values:", 10, 34, 100, 8},
+		{102, 150, 0, 0x0L, TEXTBOX, (void*)param, 22, 44, 122, 30},
+		{150, 151, 0, 0x0L, LTEXT, (void*)"function, y=f(x,z):", 10, 77, 10, 8}, 
+		{151, 152, 0, 0x0L, RTEXT, (void*)"y=", 10, 91, 10, 8}, 
+		{152, 153, 0, 0x0L, RTEXT, (void*)"converg.:", 20, 128, 26, 8}, 
+		{153, 154, 0, 0x0L, EDVAL1, &conv, 46, 128, 25, 10},
+		{154, 155, 0, 0x0L, RTEXT, (void*)"iterations:", 72, 128, 47, 8}, 
+		{155, 200, 0, 0x0L, EDVAL1, &iter, 119, 128, 25, 10},
+		{200, 0, 0, 0x0L, TEXTBOX, (void*)cmdxy, 22, 89, 122, 30},
+		{300, 301, 550, CHECKED, GROUPBOX, (void*)" grid ", 10, 40, 140, 100},
+		{301, 305, 350, HIDDEN | CHECKED, GROUPBOX, (void*)" surface ", 10, 40, 140, 100},
+		{305, 306, 0, CHECKED | TOUCHEXIT, RADIO1, (void*) " grid lines", 15, 25, 50, 10},
+		{306, 0, 0, TOUCHEXIT, RADIO1, (void*) " surface", 85, 25, 50, 10},
+		{350, 351, 0, 0x0L, RTEXT, (void*)"grid line width", 38, 50, 40, 8},
+		{351, 352, 0, 0x0L, EDVAL1, &Line.width, 80, 50, 25, 10},
+		{352, 353, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 107, 50, 20, 8},
+		{353, 354, 0, 0x0L, RTEXT, (void*)"grid line color", 38, 62, 40, 8},
+		{354, 355, 0, OWNDIALOG, COLBUTT, (void *)&Line.color, 80, 62, 25, 10},
+		{355, 356, 0, 0x0L, RTEXT,(void*)"plane color" , 38, 74, 40, 8},
+		{356, 0, 0, OWNDIALOG, SHADE3D, &newFill, 80, 74, 25, 10},
+		{400, 401, 0, 0x0L, LTEXT, (void*)"range for X data", 10, 30, 60, 8},
+		{401, 402, 0, 0x0L, RANGEINPUT, (void*)text1, 20, 40, 100, 10},
+		{402, 403, 0, 0x0L, LTEXT, (void*)"range for Y data", 10, 55, 60, 8},
+		{403, 404, 0, 0x0L, RANGEINPUT, (void*)text2, 20, 65, 100, 10},
+		{404, 405, 0, 0x0L, LTEXT, (void*)"range for Z data", 10, 80, 60, 8},
+		{405, 406, 0, 0x0L, RANGEINPUT, (void*)text3, 20, 90, 100, 10},
+		{406, 407, 0, CHECKED, CHECKBOX, (void*)"draw symbols", 20, 110, 60, 8},
+		{407, 0, 0, HIDDEN, LTEXT, 0L, 20, 110, 60, 8},
+		{500, 550, 501, CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{501, 502, 0, 0x0L, RTEXT, (void*)"plot x=", 10, 30, 28, 8}, 
+		{502, 503, 0, 0x0L, EDVAL1, &x1, 38, 30, 25, 10},
+		{503, 504, 0, 0x0L, RTEXT, (void*)"until", 61, 30, 17, 8}, 
+		{504, 505, 0, 0x0L, EDVAL1, &x2, 78, 30, 25, 10},
+		{505, 506, 0, 0x0L, RTEXT, (void*)"step", 102, 30, 17, 8}, 
+		{506, 0, 0, 0x0L, EDVAL1, &xstep, 119, 30, 25, 10},
+		{550, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_linedef, 15, bNew ? 45:45, 130, 100}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, undo_level = *Undo.pcb, i, j, k, l, m, n, ns = 0;
+	bool bRet = false, bContinue = false;
+	DWORD undo_flags = 0L;
+	LineDEF newLine;
+	double x, y, z, o_x1, n_x1, o_x2, n_x2, o_xstep, n_xstep, n_chi2=chi2, rad;
+	AccRange *rX, *rY, *rZ;
+	char *o_cmdxy, *o_param, *tmp_char;
+	anyOutput *cdisp = Undo.cdisp;
+	anyResult *ares;
+	Sphere **Balls;
+
+	if(!parent || !data) return false;
+	UseRangeMark(data, 1, text1, text2, text3);
+	if(!(o_cmdxy = (char*)memdup(cmdxy, (int)strlen(cmdxy)+1, 0)))return false;
+	if(!(o_param = (char*)memdup(param, (int)strlen(param)+1, 0)))return false;
+	rX = rY = rZ = 0L;			iter = (double)maxiter;
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+	memcpy(&newFill, &Fill, sizeof(FillDEF));
+	memcpy(&newLine, &Line, sizeof(LineDEF));
+	if(!(Dlg = new DlgRoot(FuncDlg, data))) return false;
+	if(!bNew){
+		Dlg->ShowItem(10, false);		Dlg->Activate(401, false);
+		Dlg->Activate(403, false);		Dlg->Activate(405, false);		
+		Dlg->SetCheck(6, 0L, true);
+		Dlg->SetCheck(4, 0L, false);	Dlg->ShowItem(404, false);
+		if(chi2 > 0.0) {
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "Chi 2 = %g", chi2);
+#else
+			sprintf(TmpTxt, "Chi 2 = %g", chi2);
+#endif
+			Dlg->SetText(405,TmpTxt);	Dlg->ShowItem(405, true);
+			}
+		}
+	else Dlg->ShowItem(500, false);
+	Dlg->GetValue(502, &o_x1);		n_x1 = o_x1;
+	Dlg->GetValue(504, &o_x2);		n_x2 = o_x2;
+	Dlg->GetValue(506, &o_xstep);	n_xstep = o_xstep;
+	hDlg = CreateDlgWnd("Fit Function to Data in 3D Space", 50, 50, 426, 328, Dlg, 0x4L);
+	if(bNew) Dlg->SetCheck(4, 0L, true);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(Dlg->GetCheck(10)) res = -1;
+			if(bContinue) res = -1;
+			bContinue = false;
+			break;
+		case 305:		case 306:
+			if(Dlg->GetCheck(305)) {
+				Dlg->ShowItem(300, true);	Dlg->ShowItem(301, false);
+				}
+			else {
+				Dlg->ShowItem(300, false);	Dlg->ShowItem(301, true);
+				}
+			Dlg->DoPlot(0L);
+			res = -1;
+			break;
+		case 1:
+			if(!bNew){
+				if(Dlg->GetText(102, TmpTxt, TMP_TXT_SIZE)) {
+					if(param) free(param);	
+					param = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+					}
+				if(Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE)) {
+					if(cmdxy) free(cmdxy);	
+					cmdxy = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+					}
+				ReshapeFormula(&param);		ReshapeFormula(&cmdxy);
+				dirty = true;
+				break;
+				}
+		case 7:								//Start: do nonlinear regression
+			Undo.SetDisp(cdisp);
+			if(!Dlg->GetText(401, text1, 100) || !Dlg->GetText(403, text2, 100) || !Dlg->GetText(405, text3, 100)) {
+				Dlg->SetCheck(4, 0L, true);			bContinue = true;
+				InfoBox("Invalid or missing range");
+				res = -1;
+				}
+			else if(Dlg->GetCheck(5)) {		//  the function tab must be shown
+				if(Dlg->CurrDisp) Dlg->CurrDisp->MouseCursor(MC_WAIT, true);
+				if(Dlg->GetText(102, TmpTxt, TMP_TXT_SIZE)) {
+					if(param) free(param);	
+					param = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+					}
+				if(Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE)) {
+					if(cmdxy) free(cmdxy);	
+					cmdxy = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+					}
+				Dlg->GetValue(153, &conv);	Dlg->GetValue(155, &iter);
+				ReshapeFormula(&param);		ReshapeFormula(&cmdxy);
+				do_formula(data, 0L);		//clear any error condition
+				ares = do_formula(data, param);
+				if(ares->type != ET_VALUE) {
+					ErrorBox("Syntax Error in parameters.");
+					bContinue = true;	res = -1;
+					break;
+					}
+				ares = do_formula(data, cmdxy);
+				if(ares->type != ET_VALUE) {
+					ErrorBox("Syntax Error in formula.");
+					bContinue = true;	res = -1;
+					break;
+					}
+				i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE-2, param);	i += rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, ";x=1;z=1;");
+				rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, cmdxy);	yywarn(0L, true);
+				ares = do_formula(data, TmpTxt);
+				if(tmp_char = yywarn(0L, false)) {
+					ErrorBox(tmp_char);
+					bContinue = true;	res = -1;
+					break;
+					}
+				i = do_fitfunc(data, text1, text2, text3, &param, cmdxy, conv, (int)iter, &chi2);
+				Dlg->SetText(102, param);
+				if(i >1 || res == 7) {
+#ifdef USE_WIN_SECURE
+					sprintf_s(TmpTxt, TMP_TXT_SIZE, "The Levenberg-Marquart algorithm\nexited after %d iterations.\n\nChi2 = %g", i, chi2);
+#else
+					sprintf(TmpTxt, "The Levenberg-Marquart algorithm\nexited after %d iterations.\n\nChi2 = %g", i, chi2);
+#endif
+					InfoBox(TmpTxt);
+					}
+				bContinue = true;
+				if(res == 7) res = -1;
+				if(Dlg->CurrDisp) Dlg->CurrDisp->MouseCursor(MC_ARROW, true);
+				}
+			else {							//diplay function tab first
+				Dlg->SetCheck(5, 0L, true);
+				res = -1;
+				}
+			break;
+			}
+		}while (res < 0);
+	Undo.SetDisp(cdisp);
+	while(*Undo.pcb > undo_level)	Undo.Pop(cdisp);
+	if(res == 1 && (rX=new AccRange(text1)) && (rY=new AccRange(text2)) && (rZ=new AccRange(text3))){
+		//OK pressed
+		if(bNew) {						//create function
+			if(Dlg->GetCheck(305)) {
+				type = 0;
+				OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+				}
+			else {
+				type = 1;
+				memcpy(&Fill, &newFill, sizeof(FillDEF));
+				Dlg->GetValue(401, &Line.width);
+				Dlg->GetColor(404, &Line.color);
+				Line.pattern = 0L;			Line.patlength = 1;
+				}
+			rX->GetFirst(&i, &j);		rX->GetNext(&i, &j);
+			rY->GetFirst(&k, &l);		rY->GetNext(&k, &l);
+			rZ->GetFirst(&m, &n);		rZ->GetNext(&m, &n);
+			do {
+				if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y) && data->GetValue(n, m, &z))
+					CheckBounds3D(x,y,z);
+				}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l) && rZ->GetNext(&m, &n));
+			x1 = xBounds.fx;		x2 = xBounds.fy;		xstep = (x2-x1)/10.0;
+			z1 = zBounds.fx;		z2 = zBounds.fy;		zstep = (z2-z1)/10.0;
+			if(Dlg->GetText(102, TmpTxt, TMP_TXT_SIZE)) {
+				if(param) free(param);	
+				param = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+				}
+			if(Dlg->GetText(200, TmpTxt, TMP_TXT_SIZE)) {
+				if(cmdxy) free(cmdxy);	
+				cmdxy = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+				}
+			ReshapeFormula(&param);		ReshapeFormula(&cmdxy);
+			if((bRet = Update()) && gob && nPlots == 1 && (Balls=(Sphere**)calloc(rX->CountItems(), sizeof(Sphere*)))) {
+				rX->GetFirst(&i, &j);		rX->GetNext(&i, &j);
+				rY->GetFirst(&k, &l);		rY->GetNext(&k, &l);
+				rZ->GetFirst(&m, &n);		rZ->GetNext(&m, &n);
+				rad = DefSize(SIZE_SYMBOL);
+				do {
+					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y) && data->GetValue(n, m, &z)) {
+						Balls[ns++] = new Sphere(this, data, 0, x, y, z, rad, i, j, k, l, m, n);
+						}
+					}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l) && rZ->GetNext(&m, &n));
+				if(ns) {
+					plots[1] = (GraphObj*) new Scatt3D(this, data, Balls, ns);
+					nPlots = 2;
+					}
+				else free(Balls);
+				}
+			if(bRet)Command(CMD_ENDDIALOG, 0L, 0L);
+			}
+		else {							//edit existing function
+			Dlg->GetValue(102, &x1);		Dlg->GetValue(104, &x2);
+			Dlg->GetValue(106, &xstep);
+			Dlg->GetValue(108, &z1);		Dlg->GetValue(109, &z2);
+			Dlg->GetValue(110, &zstep);		type = Dlg->GetCheck(305) ? 0 : 1;
+			InfoBox("Not Implemented");
+			}
+		}
+	if(rX) delete(rX);		if(rY) delete(rY);		if(rZ) delete(rZ);
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Grid line properties
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *GridLineDlg_Tmpl =
+	"1,+,,DEFAULT,PUSHBUTTON, 1,150,10,50,12\n"
+	".,.,,,PUSHBUTTON,2,150,25,50,12\n"
+	".,10,,,PUSHBUTTON,-2,150,40,50,12\n"
+	"10,,100,ISPARENT | CHECKED,GROUPBOX,3,5,10,139,123\n"
+	"100,+,,NOSELECT,ODBUTTON,4,10,18,130,100\n"
+	".,.,112,HIDDEN | CHECKED,GROUP,0,0,0,0,0\n"
+	".,.,115,HIDDEN | CHECKED,GROUP,0,0,0,0,0\n"
+	".,.,120,HIDDEN | CHECKED,GROUP,0,0,0,0,0\n"
+	".,.,125,HIDDEN | CHECKED,GROUP,0,0,0,0,0\n"
+	".,,130,HIDDEN | CHECKED,GROUP,0,0,0,0,0\n"
+	"111,,,,RTEXT,5,15,117,23,8\n"
+	"112,+,,,CHECKBOX,-20,41,117,25,8\n"
+	".,.,,,CHECKBOX,-21,105,117,25,8\n"
+	".,111,,,CHECKBOX,-25,70,117,25,8\n"
+	"115,+,,,CHECKBOX,-22,41,117,25,8\n"
+	".,.,,,CHECKBOX,-23,105,117,25,8\n"
+	".,111,,,CHECKBOX,-24,70,117,25,8\n"
+	"120,+,,,CHECKBOX,-24,55,117,25,8\n"
+	".,135,,,CHECKBOX,-25,90,117,25,8\n"
+	"125,+,,,CHECKBOX,-24,55,117,25,8\n"
+	".,135,,,CHECKBOX,-26,90,117,25,8\n"
+	"130,+,,,CHECKBOX,-25,55,117,25,8\n"
+	".,135,,,CHECKBOX,-26,90,117,25,8\n"
+	"135,,,LASTOBJ,LTEXT,6,15,117,55,8";
+bool
+GridLine::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 21, 10, "Line"};
+	int dh = ((flags & AXIS_ANGULAR) || (flags & AXIS_RADIAL))? -20 : 0;
+	void *dyndata[] = {(void*)"Apply to LINE", (void*)"Apply to AXIS", (void*)" grid line ", 
+		(void*)OD_linedef, (void*)"line to:", (void*)"parallel to:"}; 
+	DlgInfo *LineDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, tmptype;
+	bool bRet = false;
+	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+	LineDEF newLine;
+
+	if(!parent || !parent->parent) return false;
+	if(!(LineDlg = CompileDialog(GridLineDlg_Tmpl, dyndata)))return false;
+	LineDlg[3].h += dh;
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
+	if(!(Dlg = new DlgRoot(LineDlg, data)))return false;
+	if ((flags & AXIS_ANGULAR) || (flags & AXIS_RADIAL)) {
+		//no checkboxes, changed sizes
+		}
+	else if(flags &AXIS_3D) {
+		Dlg->SetCheck(120, 0L, type & 0x01 ? true : false);
+		Dlg->SetCheck(121, 0L, type & 0x02 ? true : false);
+		Dlg->SetCheck(125, 0L, type & 0x04 ? true : false);
+		Dlg->SetCheck(126, 0L, type & 0x08 ? true : false);
+		Dlg->SetCheck(130, 0L, type & 0x10 ? true : false);
+		Dlg->SetCheck(131, 0L, type & 0x20 ? true : false);
+		switch(parent->parent->type) {
+		case 1:	Dlg->ShowItem(105, true);	break;
+		case 2:	Dlg->ShowItem(104, true);	break;
+		case 3: Dlg->ShowItem(103, true);	break;
+			}
+		}
+	else {
+		Dlg->SetCheck(112, 0L, type & DL_LEFT ? true : false);
+		Dlg->SetCheck(113, 0L, type & DL_RIGHT ? true : false);
+		Dlg->SetCheck(114, 0L, type & DL_YAXIS ? true : false);
+		Dlg->SetCheck(115, 0L, type & DL_TOP ? true : false);
+		Dlg->SetCheck(116, 0L, type & DL_BOTTOM ? true : false);
+		Dlg->SetCheck(117, 0L, type & DL_XAXIS ? true : false);
+		if(type & 0x07) Dlg->ShowItem(101, true);
+		else Dlg->ShowItem(102, true);
+		}
+	hDlg = CreateDlgWnd("Grid line properties", 50, 50, 415, 310 + dh*2, Dlg, 0x4L);
+	do{												//dh*2 means dh * ybase
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 1:							//this line
+		case 2:							//all lines of plot
+			Undo.SetDisp(cdisp);
+			if(flags &AXIS_3D) {
+				tmptype = 0;
+				if(Dlg->GetCheck(120)) tmptype |= 0x01;		if(Dlg->GetCheck(121)) tmptype |= 0x02;
+				if(Dlg->GetCheck(125)) tmptype |= 0x04;		if(Dlg->GetCheck(126)) tmptype |= 0x08;
+				if(Dlg->GetCheck(130)) tmptype |= 0x10;		if(Dlg->GetCheck(131)) tmptype |= 0x20;
+				}
+			else {
+				tmptype = (type & ~0xff);
+				if(Dlg->GetCheck(112)) tmptype |= DL_LEFT;		if(Dlg->GetCheck(113)) tmptype |= DL_RIGHT;
+				if(Dlg->GetCheck(114)) tmptype |= DL_YAXIS;		if(Dlg->GetCheck(115)) tmptype |= DL_TOP;
+				if(Dlg->GetCheck(116)) tmptype |= DL_BOTTOM;	if(Dlg->GetCheck(117)) tmptype |= DL_XAXIS;
+				}
+			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
+			break;
+			}
+		}while (res < 0);
+	if(res == 1){			//Apply to line
+		undo_flags = CheckNewInt(&type, type, tmptype, parent, undo_flags);
+		if(cmpLineDEF(&LineDef, &newLine)) {
+			Undo.Line(parent, &LineDef, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			memcpy(&LineDef, &newLine, sizeof(LineDEF));
+			}
+		if(undo_flags & UNDO_CONTINUE) bRet = bModified = true;
+		}
+	else if(res == 2) {		//Apply to axis
+		if(parent->Id == GO_TICK && parent->parent->Id == GO_AXIS &&
+			(tmptype != type || cmpLineDEF(&LineDef, &newLine))) {
+			parent->parent->Command(CMD_SAVE_TICKS, 0L, 0L);
+			if(cmpLineDEF(&LineDef, &newLine)) parent->parent->Command(CMD_SET_GRIDLINE, (void*)&newLine, 0L);
+			if(tmptype != type) parent->parent->Command(CMD_SET_GRIDTYPE, (void*)(& tmptype), 0L);
+			bRet = true;
+			}
+		}
+	CloseDlgWnd(hDlg);	delete Dlg;		free(LineDlg);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Tick properties
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *TickDlg_Tmpl =
+	"1,2,,,PUSHBUTTON,1,120,10,60,12\n"
+	"2,3,,DEFAULT, PUSHBUTTON,2,120,25,60,12\n"
+	"3,4,,,PUSHBUTTON,-2,120,40,60,12\n"
+	"4,,5,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"5,6,100,ISPARENT | CHECKED,SHEET,3,5,10,110,100\n"
+	"6,7,200,ISPARENT,SHEET,4,5,10,110,100\n"
+	"7,,300,ISPARENT,SHEET,5,5,10,110,100\n"
+	"100,101,,,RTEXT,6,10,30,35,8\n"
+	"101,102,,,EDVAL1,7,50,30,25,10\n"
+	"102,103,,,LTEXT,-3,77,30,20,8\n"
+	"103,104,,,CHECKBOX,8,18,45,30, 8\n"
+	"104,105,,,LTEXT,9,18,60,20,8\n"
+	"105,106,,,RADIO1,0,40,70,35,8\n"
+	"106,107,,,RADIO1,0,40,60,35,8\n"
+	"107,108,,,RADIO1,10,40,80,35,8\n"
+	"108,,,,CHECKBOX,11,18,95,30,8\n"
+	"200,201,,,RTEXT,12,10,35,23,8\n"
+	"201,202,,,EDVAL1,13,40,35,65,10\n"
+	"202,203,,,LTEXT,14,15,55,20,8\n"
+	"203,,,,EDTEXT,0,15,67,90,10\n"
+	"300,301,,,LTEXT,15,15,30,70,8\n"
+	"301,302,,TOUCHEXIT,RADIO1,16,15,42,70,8\n"
+	"302,303,,TOUCHEXIT,RADIO1,17,15,52,70,8\n"
+	"303,304,,TOUCHEXIT,RADIO1,18,15,74,70,8\n"
+	"304,305,,TOUCHEXIT,RADIO1,19,15,84,70,8\n"
+	"305,306,,TOUCHEXIT,RADIO1,20,15,94,70,8\n"
+	"306,307,,, EDVAL1,21,28,62,35,10\n"
+	"307,,,LASTOBJ,LTEXT,22,65,62,20,8";
+
+bool
+Tick::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Tick"};
+	TabSHEET tab2 = {64, 90, 10, "Edit"};
+	TabSHEET tab3 = {25, 64, 10, "Direction"};
+	double tick_rot;
+	void *dyndata[] = {(void*)"Apply to TICK", (void*)"Apply to AXIS",  (void*)&tab1, (void*)&tab2,
+		(void*)&tab3, (void*)"tick size", (void*)&size, (void*)"major tick", (void*)"type:",
+		(void*)"symmetric", (void*)"draw grid line(s)", (void*)"value:", (void*)&value, (void*)"label:",
+		(void*)"direction of ticks", (void*)"perpendicular to axis", (void*)"fixed angle",
+		(void*)"x-axis", (void*)"y-axis", (void*)"z-axis", (void*)&tick_rot, (void*)"deg."};
+	DlgInfo *TickDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	DWORD old_flags;
+	char old_value[80];
+	double new_size, old_size, new_angle, old_angle, new_value;
+	int res, tmp_type = type;
+	bool bRet = false;
+	DWORD new_flags = flags, undo_flags = 0;
+	anyOutput *cdisp = Undo.cdisp;
+	char *old_label = 0L;
+	TextDEF *td;
+
+	if(!parent || parent->Id != GO_AXIS) return false;
+	if(!(TickDlg = CompileDialog(TickDlg_Tmpl, dyndata))) return false;
+	switch(type & 0x0f) {
+	case 1:		tick_rot = angle;					break;
+	default:	tick_rot = trig2deg(lsi, lcsi);		break;
+		}
+	old_flags = flags;
+	Dlg = new DlgRoot(TickDlg, data);
+	Dlg->SetCheck(301 + (type & 0x07), 0L, true);
+	if(flags & AXIS_ANGULAR) {
+		Dlg->SetText(105, "outside");		Dlg->SetText(106, "inside");
+		Dlg->ShowItem(303, false);			Dlg->ShowItem(304, false);
+		Dlg->ShowItem(305, false);
+		}
+	else if(flags & AXIS_3D) {
+		Dlg->SetText(105, "positive");		Dlg->SetText(106, "negative");
+		if(parent->Id == GO_AXIS) {
+			//disable tick direction onto the axis
+			switch(parent->type) {
+			case 1:	Dlg->Activate(303, false);	break;
+			case 2:	Dlg->Activate(304, false);	break;
+			case 3:	Dlg->Activate(305, false);	break;
+				}
+			}
+		}
+	else {
+		Dlg->ShowItem(303, false);	Dlg->ShowItem(304, false);
+		Dlg->ShowItem(305, false);
+		if(abs(pts[1].x - pts[0].x) > abs(pts[1].y - pts[0].y)){
+			Dlg->SetText(105, "right");		Dlg->SetText(106, "left");
+			}
+		else {
+			Dlg->SetText(105, "up");		Dlg->SetText(106, "down");
+			}
+		}
+	if(label) {
+		if(label->Command(CMD_GETTEXT, &TmpTxt, 0L) && TmpTxt[0] && 
+			(old_label = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0)))
+			Dlg->SetText(203, old_label);
+		if(label->Id != GO_LABEL) Dlg->Activate(203, false);
+		}
+	switch(flags &0x03) {
+	case AXIS_NEGTICKS:		Dlg->SetCheck(106, 0L, true);		break;
+	case AXIS_SYMTICKS:		Dlg->SetCheck(107, 0L, true);		break;
+	default:				Dlg->SetCheck(105, 0L, true);		break;
+		}
+	if(flags & AXIS_GRIDLINE) Dlg->SetCheck(108, 0L, true); 
+	if(!(flags & AXIS_MINORTICK)) Dlg->SetCheck(103, 0L, true);
+	if(Dlg->GetValue(101, &old_size)) new_size = old_size;
+	else new_size = old_size = size;
+	if(Dlg->GetValue(306, &old_angle)) new_angle = old_angle;
+	else new_angle = old_angle = tick_rot;
+	if(flags & AXIS_DATETIME) Dlg->SetText(201, NiceTime(value));
+	if(!(Dlg->GetText(201, old_value, sizeof(old_value)))) {
+		new_value = value;	old_value[0] = 0;
+		}
+	hDlg = CreateDlgWnd("Tick properties", 50, 50, 375, 265, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);				new_flags &= ~0x7;
+			if(Dlg->GetCheck(105)) new_flags |= AXIS_POSTICKS;
+			else if(Dlg->GetCheck(106)) new_flags |= AXIS_NEGTICKS;
+			else new_flags |= AXIS_SYMTICKS;
+			if(Dlg->GetCheck(108)) new_flags |= AXIS_GRIDLINE;
+			Dlg->GetValue(101, &new_size);		Dlg->GetValue(306, &new_angle);
+			break;
+		case 301:	case 302:	case 303:	case 304:	case 305:
+			tmp_type = (tmp_type & ~0x0f) | (res -301);
+			res = -1;
+			break;
+			}
+		}while (res <0);
+	if(res == 1) {
+		if(Dlg->GetCheck(103)) new_flags &= ~AXIS_MINORTICK;
+		else new_flags |= AXIS_MINORTICK;
+		undo_flags = CheckNewDword(&flags, flags, new_flags, parent, undo_flags);
+		undo_flags = CheckNewInt(&type, type, tmp_type, parent, undo_flags);
+		undo_flags = CheckNewFloat(&size, old_size, new_size, parent, undo_flags);
+		undo_flags = CheckNewFloat(&angle, old_angle, new_angle, parent, undo_flags);
+		if(Dlg->GetText(201, TmpTxt, 80) && strcmp(TmpTxt, old_value)) {
+			Undo.ValFloat(parent, &value, undo_flags);		undo_flags |= UNDO_CONTINUE;
+			if(flags & AXIS_DATETIME) date_value(TmpTxt, 0L, &value);
+			else Dlg->GetValue(201, &value);
+			}
+		if(label && label->Id == GO_LABEL) {
+			td = ((Label*)label)->GetTextDef();
+			if(!Dlg->GetText(203, TmpTxt, TMP_TXT_SIZE)) TmpTxt[0] = 0;
+			if(undo_flags = CheckNewString(&td->text, td->text, TmpTxt, this, undo_flags))
+				label->Command(CMD_SETTEXT, TmpTxt, 0L);
+			}
+		if (undo_flags & UNDO_CONTINUE) bRet = true;
+		}
+	else if(res == 2) {
+		parent->Command(CMD_SAVE_TICKS, 0L, 0L);
+		if((new_flags & 0x07) != (old_flags & 0x07)) 
+			parent->Command(CMD_SET_TICKSTYLE, &new_flags, 0L);
+		parent->SetSize(SIZE_AXIS_TICKS, new_size);
+		parent->Command(CMD_TICK_TYPE, &tmp_type, 0L);
+		parent->SetSize(SIZE_TICK_ANGLE, new_angle);
+		parent->Command(CMD_REDRAW, 0L, 0L);
+		bRet = true;
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;		free(TickDlg);
+	if(old_label) free(old_label);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Axis properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *ssTickTmpl =
+	"1,2,,DEFAULT, PUSHBUTTON,-1,113,10,45,12\n"
+	"2,3,,, PUSHBUTTON,-2,113,25,45,12\n"
+	"3,4,,, LTEXT,1,5,10,100,9\n"
+	"4,5,,TOUCHEXIT,RANGEINPUT,4,10,20,90,10\n"
+	"5,6,,, LTEXT,2,5, 32, 100, 9\n"
+	"6,7,,TOUCHEXIT,RANGEINPUT,5,10,42,90,10\n"
+	"7, 8,,,LTEXT,3, 5, 54, 80, 8\n"
+	"8,,,LASTOBJ | TOUCHEXIT,RANGEINPUT,6,10,64,90,10";
+
+bool
+Axis::ssTicks()
+{
+	void *dyndata[] ={(void*)"range for major tick VALUES:", (void*)"range for major tick LABELS:",
+		(void*)"minor tick VALUES:", (void*)ssMATval, (void*)ssMATlbl, (void*)ssMITval};
+	DlgInfo *TickDlg = CompileDialog(ssTickTmpl, dyndata);
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, n, n1, n2, n3;
+	bool bRet = false, bContinue = true;
+	AccRange *rT, *rL, *rMT;
+
+	if(!data) return false;
+	n = n1 = n2 = n3 = 0;			rT = rL = rMT = 0L;
+	if(!(Dlg = new DlgRoot(TickDlg, data)))return false;
+	hDlg = CreateDlgWnd("choose ticks from spreadsheet", 50, 50, 330, 190, Dlg, 0x4L);
+	Dlg->Activate(4, true);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:								// focus lost
+			if(bContinue) res = -1;
+			break;
+		case 4:		case 6:		case 8:
+			bContinue = true;
+			res = -1;			break;
+		case 1:
+			if(rT) delete rT;		if(rL) delete rL;		if(rMT) delete rMT;
+			n = n1 = n2 = n3 = 0;	rT = rL = rMT = 0L;
+			if(Dlg->GetText(4,TmpTxt, TMP_TXT_SIZE) && (rT=new AccRange(TmpTxt)) && (n1=rT->CountItems())){
+				if(Dlg->GetText(6, TmpTxt, TMP_TXT_SIZE) && (rL = new AccRange(TmpTxt)) && 
+					(n2 = rL->CountItems()) && n1 != n2){
+					ErrorBox("Ranges for tick values\nand tick labels must"
+						" have\nthe same size");
+					res = -1;		bContinue = true;
+					break;
+					}
+				}
+			if(Dlg->GetText(8,TmpTxt,TMP_TXT_SIZE) && (rMT=new AccRange(TmpTxt)) && (n3=rMT->CountItems())){
+				//minor ticks are valid
+				}
+			if(!(n = n1 + n3)) {
+				ErrorBox("Ranges not valid or\nno range specified");
+				res = -1;			bContinue = true;
+				}
+			}
+		}while (res < 0);
+	if(res == 1 && n) {
+		if(n1) {
+			if(ssMATval) free(ssMATval);	if(ssMATlbl) free(ssMATlbl);
+			if(ssMITval) free(ssMITval);	ssMATval = ssMATlbl = ssMITval = 0L;
+			if(Dlg->GetText(4, TmpTxt, TMP_TXT_SIZE)) 
+				ssMATval = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+			if(Dlg->GetText(6, TmpTxt, TMP_TXT_SIZE))
+				ssMATlbl = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+			if(Dlg->GetText(8, TmpTxt, TMP_TXT_SIZE))
+				ssMITval = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+			UpdateTicks();
+			}
+		bRet = true;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;			free(TickDlg);
+	if(rT) delete rT;	if(rL) delete rL;	if(rMT) delete rMT;
+	return bRet;
+}
+
+static char *AxisPropDlg_Tmpl =
+	"1,2,,DEFAULT,PUSHBUTTON,-1,186,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,186,25,45,12\n"
+	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,5,50,ISPARENT | CHECKED,SHEET,1,5,10,168,165\n"
+	"5,6,200,ISPARENT,SHEET,2,5,10,168,165\n"
+	"6,7,300,ISPARENT,SHEET,3,5,10,168,165\n"
+	"7,8,400,ISPARENT,SHEET,4,5,10,168,165\n"
+	"8,9,500,HIDDEN | ISPARENT,SHEET,5,5,10,168,165\n"
+	"9,,550,HIDDEN | ISPARENT,SHEET,6,5,10,168,165\n"
+	"50,+,100,ISPARENT | CHECKED,GROUPBOX,7,10,30,158,36\n"
+	".,104,,TOUCHEXIT,CHECKBOX,8,17,37,80,8\n"
+	"100,+,,,RTEXT,9,10,51,35,8\n"
+	".,.,,,EDVAL1,10,48,51,51,10\n"
+	".,.,,,CTEXT,11,100,51,11,8\n"
+	".,,,,EDVAL1,12,112,51,51,10\n"
+	".,120,105,ISPARENT | CHECKED,GROUPBOX,13,10,72,158,45\n"
+	".,106,,TOUCHEXIT | ISRADIO,CHECKBOX,-20,54,77,20,8\n"
+	".,.,,TOUCHEXIT | ISRADIO,CHECKBOX,-21,84,77,20,8\n"
+	".,.,,TOUCHEXIT | ISRADIO,CHECKBOX,-22,54,77,20,8\n"
+	".,.,,TOUCHEXIT | ISRADIO,CHECKBOX,-23,84,77,20,8\n"
+	".,.,,,RTEXT,-4,10,-50,15,8\n"
+	".,.,,,EDVAL1,14,27,-50,45,10\n"
+	".,.,,,LTEXT,-7,75,-50,5,8\n"
+	".,.,,,EDVAL1,15,81,-50,45,10\n"
+	".,.,,,LTEXT,0,129,-50,15,8\n"
+	".,.,,,RTEXT,-5,10,-38,15,8\n"
+	".,.,,,EDVAL1,16,27,-38,45,10\n"
+	".,.,,,LTEXT,-7,75,-38,5,8\n"
+	".,.,,,EDVAL1,17,81,-38,45,10\n"
+	".,150,,,LTEXT,0,129,-38,15,8\n"
+	"150,+,,,RTEXT,-6,10,-26,15,8\n"
+	".,.,,,EDVAL1,18,27,-26,45,10\n"
+	".,.,,,LTEXT,-7,75,-26,5,8\n"
+	".,.,,,EDVAL1,19,81,-26,45,10\n"
+	".,,,,LTEXT,0,129,-26,15,8\n"
+	"120,180, 121, ISPARENT | CHECKED,GROUPBOX,20,10,123,158,20\n"
+	".,+,,,RTEXT,21,20,128,25,8\n"
+	".,.,,,EDVAL1,22,47,128,25,10\n"
+	".,.,,,LTEXT,-3,73,128, 10,8\n"
+	".,.,,,RTEXT,-11,102,128,25,8\n"
+	".,130,,OWNDIALOG, COLBUTT,23,129,128,25,10\n"
+	"130,,131,ISPARENT | CHECKED,GROUPBOX,24,10,149,158,20\n"
+	"131,,,,EDTEXT,0,15,154,148,10\n"
+	"180,,181,HIDDEN | ISPARENT | CHECKED,GROUPBOX,25,10,72,158,45\n"
+	"181,+,,,RTEXT,26,10,-38,50,8\n"
+	".,.,,,EDVAL1,27,62,-38,45,10\n"
+	".,.,,,LTEXT,-3,109,-38,15,8\n"
+	".,.,,,RTEXT,-5,10,-50,50,8\n"
+	".,.,,,EDVAL1,28,62,-50,45,10\n"
+	".,.,,,LTEXT,-3,109,-50,15,8\n"
+	".,.,,,RTEXT,29,10,-38,50,8\n"
+	".,.,,,EDVAL1,30,62,-38,45,10\n"
+	".,.,,,LTEXT,-3,109,-38,15,8\n"
+	"200,+,,TOUCHEXIT,RADIO1,31,20,35,40,8\n"
+	".,.,,TOUCHEXIT,RADIO1,32,20,47,40,8\n"
+	".,204,,TOUCHEXIT,RADIO1,33,20,59,40,8\n"
+	".,.,,TOUCHEXIT,RADIO1,34,20,71,40,8\n"
+	".,203,250,CHECKED | ISPARENT,GROUPBOX,35,15,75,148,50\n"
+	".,206,,,PUSHBUTTON,36,30,130,90,12\n"
+	".,,,TOUCHEXIT,RADIO1,0,20,130,8,8\n"
+	"250,+,,,RTEXT,37,35,83,45,8\n"
+	".,.,,,EDVAL1,38,87,83,65,10\n"
+	".,.,,,RTEXT,39,35,95,45,8\n"
+	".,.,,,EDVAL1,40,87,95,65,10\n"
+	".,.,,,RTEXT,41,25,107,75,8\n"
+	".,,,,EDTEXT,42,107,107,45,10\n"
+	"300,+,,,LTEXT,43,20,30,110,8\n"
+	".,.,,TOUCHEXIT,RADIO1,44,40,45,70,8\n"
+	".,.,,TOUCHEXIT,RADIO1,45,40,57,70,8\n"
+	".,.,,TOUCHEXIT,RADIO1,46,40,69,70,8\n"
+	".,.,,TOUCHEXIT,RADIO1,47,40,81,70,8\n"
+	".,,,,CHECKBOX,48,20,115,80,12\n"
+	"400,450,,,LTEXT,-27,10,30,110,8\n"
+	".,402,,ISRADIO,ODBUTTON,49,49,40,25,25\n"
+	".,.,,ISRADIO,ODBUTTON,49,74,40,25,25\n"
+	".,.,,ISRADIO,ODBUTTON,49,99,40,25,25\n"
+	".,.,,ISRADIO,ODBUTTON,49,124,40,25,25\n"
+	".,.,,,RTEXT,50,25,75,48,8\n"
+	".,.,,,EDVAL1,51,75,75,29,10\n"
+	".,.,,,LTEXT,-3,107,75,10,8\n"
+	".,.,,,RTEXT,52,25,87,48,8\n"
+	".,.,,,EDVAL1,53,75,87,29,10\n"
+	".,,,,LTEXT,-3,107,87,10,8\n"
+	"450,+,,ISPARENT | CHECKED,GROUPBOX,54,10,110,158,50\n"
+	".,.,,,LTEXT,0,30,115,60,8\n"
+	".,.,,,PUSHBUTTON,-8,108,142,41,12\n"
+	".,.,,,PUSHBUTTON,-9,67,142,41,12\n"
+	".,.,,,RTEXT,55,25,125,20,8\n"
+	".,.,,,EDTEXT,0,47,125,41,10\n"
+	".,.,,,RTEXT,56,92,125,9,8\n"
+	".,.,,,EDTEXT,0,103,125,41,10\n"
+	".,401,,,PUSHBUTTON,57,26,142,41,12\n"
+	"500,,,NOSELECT,ODBUTTON,58,25,30,140,140\n"
+	"550,,551,ISPARENT | CHECKED,GROUPBOX,59,15,30,148,123\n"
+	".,+,,TOUCHEXIT,RADIO1,60,30,38,75,9\n"
+	".,.,,ODEXIT,COLBUTT,61,112,37,25,10\n"
+	".,.,,TOUCHEXIT,RADIO1,62,30,52,80,9\n"
+	".,.,,TOUCHEXIT,RADIO1,63,30,76,80,9\n"
+	".,.,,TOUCHEXIT,RADIO1,64,30,88,80,9\n"
+	".,.,,TOUCHEXIT,RADIO1,65,30,64,80,9\n"
+	".,.,,OWNDIALOG | TOUCHEXIT,COLBUTT,66,50,100,25,10\n"
+	".,.,,,CTEXT,56,76,100,18,9\n"
+	".,.,,ODEXIT,COLBUTT,67,95,100,25,10\n"
+	".,.,,,CHECKBOX,68,30,115,80,9\n"
+	".,.,,,RTEXT,69,30,128,45,9\n"
+	".,.,,,INCDECVAL1,70,77,128,32,10\n"
+	".,,,LASTOBJ,LTEXT,-10,111,128,10,9";
+
+bool
+Axis::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Axis"};
+	TabSHEET tab2 = {25, 50, 10, "Ticks"};
+	TabSHEET tab3 = {50, 97, 10, "Transforms"};
+	TabSHEET tab4 = {97, 128, 10, "Breaks"};
+	TabSHEET tab5 = {128, 153, 10, "Plots"};
+	TabSHEET tab6 = {97, 135, 10, "Gradient"};
+	int v1 = (axis->flags & AXIS_3D) ? 77 : (axis->flags & AXIS_RADIAL) ? 83 : 89; 
+	double transp = (double)iround((double)((gTrans>>24) & 0xff)/2.55);
+	void *dyndata[] ={(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)&tab4, (void*)&tab5, (void*)&tab6,
+		(void*)" scaling ", (void*)" automatic scaling", (void*)"axis from", (void*)&axis->min, (void*)"to",
+		(void*)&axis->max, (void*)" placement ", (void*)&axis->loc[0].fx, (void*)&axis->loc[1].fx,
+		(void*)&axis->loc[0].fy, (void*)&axis->loc[1].fy, (void*)&axis->loc[0].fz, (void*)&axis->loc[1].fz,
+		(void*)" line ", (void*)"width", (void*)&sizAxLine, (void*)&colAxis, (void*)" axis label ",
+		(void*)" placement ", (void*)"center x", (void*)&axis->Center.fx, (void*)&axis->Center.fy,
+		(void*)"radius", (void*)&axis->Radius, (void*)"no ticks", (void*)"automatic", (void*)"leave unchanged",
+		(void*)"set manually", (void*)"                            ", (void*)"use spread sheet values",
+		(void*)"start value", (void*)&axis->Start, (void*)"interval", (void*)&axis->Step,
+		(void*)"minor ticks per interval", (void*)"0", (void*)"Transforms:",  (void*)"none (linear)",
+		(void*)"logarithmic (log base 10)", (void*)"reciprocal (1/x)",(void*)"square root",
+		(axis->loc[0].fx == axis->loc[1].fx) ? (void*)"low vaues top of graph" : (void*)"low values right of graph",
+		(void*)(OD_BreakTempl), (void*)"break gap", (void*)&brkgap, (void*)"symbol size",  (void*)&brksymsize,
+		(void*)" break data ", (void*)"from", (void*)"to", (void*)"Delete", (void*)OD_axisplot,
+		(void*)" color gradient ", (void*)"use common fill color:", (void *)&gCol_0,
+		(void*)"rainbow color gradient I", (void*)"extented rainbow colors", (void*)"simple color gradient",
+		(void*)"rainbow color gradient II", (void *)&gCol_1, (void *)&gCol_2, (void*)"invert gradient",
+		(void*)"transparency", (void*)&transp};
+	DlgInfo *AxisPropDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, res, nbrk, cbrk, ttmpl, n_gradient;
+	double tmp, tmp2, old_x1, old_x2, old_y1, old_y2;
+	bool bRet = false, bChanged = false, upd_brk = true;
+	bool bContinue = false, bUpdPG = false;
+	double use_step = 10.0, use_minmax[] = {0.0, 100.0};
+	DWORD new_color, undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
+	lfPOINT *brks = 0L, *tmpbrks = 0L;
+	char *old_Label = 0L, *type_txt;
+	TextDEF label_def, *lb_def;
+	char **names;
+	GraphObj **somePlots = 0L, **scp = 0L;
+	AxisDEF old_a, new_a;
+	void *sv_ptr;
+
+	nbrk = cbrk = 0;
+	if(!parent) return false;
+	if(!(AxisPropDlg = CompileDialog(AxisPropDlg_Tmpl, dyndata))) return false;
+	n_gradient = grad_type;
+	//adjust dialog to 3D plot, polar plot ...
+	for(i = 0, v1+= 50; !(AxisPropDlg[i].flags & LASTOBJ); i++) {
+		if(AxisPropDlg[i].y < 0) AxisPropDlg[i].y += v1;
+		}
+	if(parent->Id == GO_GRAPH && (res=((Graph*)parent)->nscp)){
+		scp = ((Graph*)parent)->Sc_Plots;
+		CurrAxes = ((Graph*)parent)->Axes;
+		if(!scp || !(names = (char**)calloc(res+2, sizeof(char*))) ||
+			!(somePlots = (GraphObj**)calloc(res+2, sizeof(GraphObj*))))
+			return false;
+		if(names[0] = (char*)malloc(15 * sizeof(char))) rlp_strcpy(names[0], 15, "[unchanged]");
+		for(i = 0, j = 1; i < res; i++) {
+			if(scp[i] && scp[i]->name){
+				names[j] = (char*)memdup(scp[i]->name, (int)strlen(scp[i]->name)+1, 0);
+				somePlots[j++] = scp[i];
+				}
+			}
+		}
+	else if(IsPlot3D(parent) && (res=((Plot3D*)parent)->nscp)){
+		scp = ((Plot3D*)parent)->Sc_Plots;
+		CurrAxes = ((Plot3D*)parent)->Axes;
+		if(!scp || !(names = (char**)calloc(res+2, sizeof(char*))) ||
+			!(somePlots = (GraphObj**)calloc(res+2, sizeof(GraphObj*))))
+			return false;
+		if(names[0] = (char*)malloc(15 * sizeof(char))) rlp_strcpy(names[0], 15, "[unchanged]");
+		for(i = 0, j = 1; i < res; i++) {
+			if(scp[i] && scp[i]->name){
+				names[j] = (char*)memdup(scp[i]->name, (int)strlen(scp[i]->name)+1, 0);
+				somePlots[j++] = scp[i];
+				}
+			}
+		}
+	else {
+		names = (char**)calloc(2, sizeof(char*));	names[0] = (char*)malloc(10*sizeof(char));
+		rlp_strcpy(names[0], 10, "n.a.");
+		}
+	OD_axisplot(OD_ACCEPT, 0L, 0L, 0L, names, 0);
+	if(!(Dlg = new DlgRoot(AxisPropDlg, data)))return false;
+	if(names && somePlots) Dlg->ShowItem(8, true);		//show tab
+	if(axis->breaks && axis->nBreaks) {
+		if(!(brks = (lfPOINT*)calloc(axis->nBreaks+2, sizeof(lfPOINT)))) return false;
+		memcpy(brks, axis->breaks, axis->nBreaks*sizeof(lfPOINT));
+		nbrk = axis->nBreaks-1;
+		WriteNatFloatToBuff(TmpTxt, brks[cbrk].fx);
+		Dlg->SetText(455, TmpTxt+1);
+		WriteNatFloatToBuff(TmpTxt, brks[cbrk].fy);
+		Dlg->SetText(457, TmpTxt+1);
+		}
+	switch(brksym) {
+	case 2:		Dlg->SetCheck(402, 0L, true);	break;
+	case 3:		Dlg->SetCheck(403, 0L, true);	break;
+	case 4:		Dlg->SetCheck(404, 0L, true);	break;
+	default:	Dlg->SetCheck(401, 0L, true);	break;
+		}
+	if(!(axis->flags & 0x03)) Dlg->SetCheck(ttmpl = 200, 0L, true);
+	else if(axis->flags & AXIS_AUTOTICK)Dlg->SetCheck(ttmpl = 201, 0L, true);
+	else if(Ticks) Dlg->SetCheck(ttmpl = 202, 0L, true);
+	else Dlg->SetCheck(ttmpl = 201, 0L, true);
+	Dlg->Activate(251, false);	Dlg->Activate(253, false);	Dlg->Activate(255, false);
+	Dlg->SetCheck(305, 0L, (AXIS_INVERT == (axis->flags & AXIS_INVERT)));
+	if(axis->flags & AXIS_AUTOSCALE) {
+		Dlg->SetCheck(51, 0L, true);
+		Dlg->Activate(101, false);		Dlg->Activate(103, false);
+		}
+	//check transforms
+	switch(axis->flags & 0x7000L) {
+	case AXIS_LINEAR:	Dlg->SetCheck(301, 0L, true);	break;
+	case AXIS_LOG:		Dlg->SetCheck(302, 0L, true);	break;
+	case AXIS_RECI:		Dlg->SetCheck(303, 0L, true);	break;
+	case AXIS_SQR:		Dlg->SetCheck(304, 0L, true);	break;
+		}
+	if(axis->flags & AXIS_3D) {
+		Dlg->ShowItem(105, false);	Dlg->ShowItem(106, false);
+		Dlg->ShowItem(107, false);	Dlg->ShowItem(108, false);
+		}
+	else if(axis->flags & AXIS_ANGULAR) {
+		Dlg->ShowItem(104, false);	Dlg->ShowItem(130, false);
+		Dlg->ShowItem(180, true);	Dlg->ShowItem(7, false);
+		}
+	else {
+		if(axis->flags & AXIS_RADIAL) {
+			Dlg->ShowItem(105, false);	Dlg->ShowItem(106, false);
+			Dlg->ShowItem(107, false);	Dlg->ShowItem(108, false);
+			}
+		Dlg->ShowItem(150, false);	Dlg->ShowItem(151, false);
+		Dlg->ShowItem(152, false);	Dlg->ShowItem(153, false);
+		Dlg->ShowItem(154, false);
+		if(axis->loc[0].fx != axis->loc[1].fx) {
+			Dlg->ShowItem(105, false);	Dlg->ShowItem(106, false);
+			}
+		if(axis->loc[0].fy != axis->loc[1].fy) {
+			Dlg->ShowItem(107, false);	Dlg->ShowItem(108, false);
+			}
+		}
+	//align to frame ?
+	switch(axis->flags & 0x70) {
+		case AXIS_LEFT: Dlg->SetCheck(105, 0L, true);	break;
+		case AXIS_RIGHT: Dlg->SetCheck(106, 0L, true);	break;
+		case AXIS_TOP: Dlg->SetCheck(107, 0L, true);	break;
+		case AXIS_BOTTOM: Dlg->SetCheck(108, 0L, true);	break;
+		}
+	if(axis->flags & AXIS_X_DATA) Dlg->SetText(113, "[data]");
+	else Dlg->SetText(113, Units[defs.cUnits].display);
+	if(axis->flags & AXIS_Y_DATA) Dlg->SetText(118, "[data]");
+	else Dlg->SetText(118, Units[defs.cUnits].display);
+	if(axis->flags & AXIS_Z_DATA) Dlg->SetText(118, "[data]");
+	else Dlg->SetText(154, Units[defs.cUnits].display);
+	//any label ?
+	if(axisLabel){
+		TmpTxt[0] = 0;
+		axisLabel->Command(CMD_GETTEXT, TmpTxt, 0L);
+		Dlg->SetText(131,TmpTxt);
+		old_Label = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+		if(axisLabel->Id == GO_MLABEL) Dlg->Activate(131, false);
+		}
+	else Dlg->SetText(131, 0L);
+	//remember: any updated values ?
+	Dlg->GetValue(110, &old_x1);		Dlg->GetValue(112, &old_x2);
+	Dlg->GetValue(115, &old_y1);		Dlg->GetValue(117, &old_y2);
+	switch(type) {
+	case 1:		type_txt = (char*)(&"X-a");		break;
+	case 2:		type_txt = (char*)(&"Y-a");		break;
+	case 3:		type_txt = (char*)(&"Z-a");		break;
+	case 4:		type_txt = (char*)(&"Gradient A");	break;
+	default:	type_txt = (char*)(&"A");		break;
+		}
+	//angular radial axis specials
+	if(axis->flags & AXIS_ANGULAR){
+		Dlg->SetText(305, "set direction cw");
+		type_txt = (char*)(&"angular a");
+		}
+	if(axis->flags & AXIS_RADIAL) type_txt = (char*)(&"radial a");
+	//save old axis definition
+	memcpy(&old_a, axis, sizeof(AxisDEF));
+	Dlg->GetValue(101, &old_a.min);		Dlg->GetValue(103, &old_a.max);
+	Dlg->GetValue(110, &old_a.loc[0].fx);	Dlg->GetValue(112, &old_a.loc[1].fx);
+	Dlg->GetValue(115, &old_a.loc[0].fy);	Dlg->GetValue(117, &old_a.loc[1].fy);
+	Dlg->GetValue(151, &old_a.loc[0].fz);	Dlg->GetValue(153, &old_a.loc[1].fz);
+	Dlg->GetValue(182, &old_a.Center.fx);	Dlg->GetValue(185, &old_a.Center.fy);
+	Dlg->GetValue(188, &old_a.Radius);
+	memcpy(&new_a, &old_a, sizeof(AxisDEF));
+	i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, type_txt);
+	rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, "xis Properties");
+	if((type &0x0f) == 4) {
+		Dlg->ShowItem(7, false);	Dlg->ShowItem(8, false);
+		Dlg->ShowItem(9, true);		upd_brk = false;
+		switch(grad_type & 0x0f) {
+		case 0:
+			Dlg->SetCheck(551, 0L, true);				break;
+		case 1:		default:
+			grad_type = n_gradient = 1;
+			Dlg->SetCheck(553, 0L, true);				break;
+		case 2:		case 3:		case 4:
+			Dlg->SetCheck(552+(grad_type & 0x0f), 0L, true);	break;
+			}
+		if(grad_type & 0x10) Dlg->SetCheck(560, 0L, true);
+		Dlg->ItemCmd(562, CMD_STEP, (void*)&use_step);
+		Dlg->ItemCmd(562, CMD_MINMAX, (void*)&use_minmax);
+		}
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 476, 390, Dlg, 0x4L);
+	switch(axis->flags & 0x70) {
+		case AXIS_LEFT: 
+		case AXIS_RIGHT:
+			Dlg->Activate(110, false);			Dlg->Activate(112, false);
+			break;
+		case AXIS_TOP:
+		case AXIS_BOTTOM:
+			Dlg->Activate(115, false);			Dlg->Activate(117, false);
+			break;
+			}
+	do{
+		if(upd_brk) {
+			Dlg->ShowItem(453, cbrk > 0);
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "break # %d/%d", cbrk+1, nbrk+1);
+#else
+			sprintf(TmpTxt,"break # %d/%d", cbrk+1, nbrk+1);
+#endif
+			Dlg->SetText(451, TmpTxt);
+			upd_brk = false;
+			}
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 0:							//lost focus ?
+			if(bContinue) res = -1;
+			ShowDlgWnd(hDlg);
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 51:
+			if(Dlg->GetCheck(51)) {
+				Dlg->Activate(101, false);		Dlg->Activate(103, false);
+				}
+			else {
+				Dlg->Activate(101, true);		Dlg->Activate(103, true);
+				}
+			res = -1;
+			break;
+		case 105:	case 106:	case 107:	case 108:		//axis left | right | top | bottom
+			if(Dlg->GetCheck(105) || Dlg->GetCheck(106)) {
+				Dlg->Activate(110, false);			Dlg->Activate(112, false);
+				}
+			else {
+				Dlg->Activate(110, true);			Dlg->Activate(112, true);
+				}
+			if(Dlg->GetCheck(107) || Dlg->GetCheck(108)) {
+				Dlg->Activate(115, false);			Dlg->Activate(117, false);
+				}
+			else {
+				Dlg->Activate(115, true);			Dlg->Activate(117, true);
+				}
+			res = -1;
+			break;
+		case 301:	case 302:	case 303:	case 304:		//transform radiobuttons
+			new_a.flags &= ~0x7000L;
+			if(res == 302) new_a.flags |= AXIS_LOG;
+			else if(res == 303) new_a.flags |= AXIS_RECI;
+			else if(res == 304) new_a.flags |= AXIS_SQR;
+			res = -1;
+			break;
+		case 200:	case 201:	case 202:
+			ttmpl = res;
+			Dlg->Activate(251, false);	Dlg->Activate(253, false);	Dlg->Activate(255, false);
+			res = -1;
+			break;
+		case 203:
+			ttmpl = res;
+			Dlg->Activate(251, true);	Dlg->Activate(253, true);	Dlg->Activate(255, true);
+			res = -1;
+			break;
+		case 205:	case 206:
+			Dlg->SetCheck(206, 0L, true);
+			if (ssTicks()) {
+				Dlg->SetCheck(ttmpl = 202, 0L, true);	Dlg->Activate(251, false);
+				Dlg->Activate(253, false);	Dlg->Activate(255, false);
+				undo_flags |= UNDO_CONTINUE;	res = 1;
+				}
+			else {
+				Dlg->SetCheck(ttmpl, 0L, true);
+				res = -1;
+				}
+			bContinue = true;
+			break;
+		case 452:			//next break
+			if(Dlg->GetValue(455, &tmp) && Dlg->GetValue(457, &tmp2)){
+				if(!brks && (brks = (lfPOINT*)calloc(2, sizeof(lfPOINT)))) {
+					brks[0].fx = tmp;		brks[0].fy = tmp2;
+					cbrk = nbrk = 0;
+					}
+				if(!brks) return false;		//mem allocation error
+				if(cbrk && cbrk >= nbrk) {
+					if(tmpbrks = (lfPOINT*)realloc(brks, (cbrk+2)*sizeof(lfPOINT))){
+						brks = tmpbrks;
+						brks[cbrk].fx = tmp;		brks[cbrk].fy = tmp2;
+						brks[cbrk+1].fx = brks[cbrk+1].fy = 0.0;
+						}
+					else return false;		//mem allocation error
+					}
+				else {
+					brks[cbrk].fx = tmp;		brks[cbrk].fy = tmp2;
+					}
+				cbrk++;
+				}
+			if(cbrk>nbrk){
+				nbrk = cbrk;
+				Dlg->SetText(455,0L);		Dlg->SetText(457,0L);
+				}
+			else if(nbrk){
+				if(brks[cbrk].fx == brks[cbrk].fy && cbrk == nbrk) {
+					Dlg->SetText(455,0L);		Dlg->SetText(457,0L);
+					}
+				else {
+					WriteNatFloatToBuff(TmpTxt, brks[cbrk].fx);
+					Dlg->SetText(455, TmpTxt+1);
+					WriteNatFloatToBuff(TmpTxt, brks[cbrk].fy);
+					Dlg->SetText(457, TmpTxt+1);
+					}
+				}
+			bChanged = upd_brk = true;
+			res= -1;
+			break;
+		case 453:			//previous break
+			if(cbrk >0){
+				if(Dlg->GetValue(455, &tmp) && Dlg->GetValue(457, &tmp2)){
+					brks[cbrk].fx = tmp;		brks[cbrk].fy = tmp2;
+					}
+				else if(cbrk == nbrk)nbrk--;
+				cbrk--;
+				WriteNatFloatToBuff(TmpTxt, brks[cbrk].fx);
+				Dlg->SetText(455, TmpTxt+1);
+				WriteNatFloatToBuff(TmpTxt, brks[cbrk].fy);
+				Dlg->SetText(457, TmpTxt+1);
+				}
+			bChanged = upd_brk = true;
+			res= -1;
+			break;
+		case 458:			//delete break;
+			if(brks && nbrk > cbrk) {
+				for(i = cbrk; i < nbrk; i++) {
+					brks[i].fx = brks[i+1].fx;
+					brks[i].fy = brks[i+1].fy;
+					}
+				nbrk--;
+				if(brks[cbrk].fx == brks[cbrk].fy && cbrk == nbrk) {
+					Dlg->SetText(455,0L);		Dlg->SetText(457,0L);
+					}
+				else {
+					WriteNatFloatToBuff(TmpTxt, brks[cbrk].fx);
+					Dlg->SetText(455, TmpTxt+1);
+					WriteNatFloatToBuff(TmpTxt, brks[cbrk].fy);
+					Dlg->SetText(457, TmpTxt+1);
+					}
+				}
+			else {
+				Dlg->SetText(455,0L);		Dlg->SetText(457,0L);
+				}
+			bChanged = upd_brk = true;
+			res = -1;
+			break;
+		case 551:
+			n_gradient = 0;
+			res = -1;		break;
+		case 552:					//gradient color button 0
+			Dlg->SetCheck(551, 0L, true);	n_gradient = 0;		res = -1;
+			break;
+		case 553:	case 554:	case 555:	case 556:
+			n_gradient = (res-552);
+			res = -1;		break;
+		case 557:	case 559:			//gradient color button 1 and 2
+			Dlg->SetCheck(555, 0L, true);	n_gradient = 3;		res = -1;
+			break;
+			}
+		}while (res < 0);
+	Undo.SetDisp(cdisp);
+	switch (res) {
+	case 1:									//OK pressed
+		bModified = true;
+		if(Dlg->GetValue(455, &tmp) && Dlg->GetValue(457, &tmp2)){
+			if(!brks && (brks = (lfPOINT*)calloc(2, sizeof(lfPOINT)))) {
+				brks[0].fx = tmp;	brks[0].fy = tmp2;	cbrk = nbrk = 1;
+				}
+			else if(brks) {
+				brks[cbrk].fx = tmp;	brks[cbrk].fy = tmp2;	nbrk++;
+				}
+			}
+		Dlg->GetValue(101, &new_a.min);		Dlg->GetValue(103, &new_a.max);
+		Dlg->GetValue(110, &new_a.loc[0].fx);	Dlg->GetValue(112, &new_a.loc[1].fx);
+		Dlg->GetValue(115, &new_a.loc[0].fy);	Dlg->GetValue(117, &new_a.loc[1].fy);
+		Dlg->GetValue(151, &new_a.loc[0].fz);	Dlg->GetValue(153, &new_a.loc[1].fz);
+		Dlg->GetValue(182, &new_a.Center.fx);	Dlg->GetValue(185, &new_a.Center.fy);
+		Dlg->GetValue(188, &new_a.Radius);
+		new_a.breaks = brks;					new_a.nBreaks = nbrk;
+		if(new_a.breaks) SortAxisBreaks(&new_a);
+		brks = 0L;	nbrk = 0;
+		if(Dlg->GetCheck(51)) {
+			if(!(new_a.flags & AXIS_AUTOSCALE)) parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+			new_a.flags |= AXIS_AUTOSCALE;
+			}
+		else new_a.flags &= ~AXIS_AUTOSCALE;
+		if(Dlg->GetCheck(201)) new_a.flags |= AXIS_AUTOTICK;
+		else new_a.flags &= ~AXIS_AUTOTICK;
+		if(Dlg->GetCheck(200)) new_a.flags &= ~0x03;
+		if(Dlg->GetCheck(305)) new_a.flags |= AXIS_INVERT;
+		else new_a.flags &= ~AXIS_INVERT;
+		new_a.flags &= ~0x70;
+		if(Dlg->GetCheck(105)) new_a.flags |= AXIS_LEFT;
+		else if(Dlg->GetCheck(106)) new_a.flags |= AXIS_RIGHT;
+		else if(Dlg->GetCheck(107)) new_a.flags |= AXIS_TOP;
+		else if(Dlg->GetCheck(108)) new_a.flags |= AXIS_BOTTOM;
+		if((new_a.flags & AXIS_LOG)== AXIS_LOG && new_a.min < defs.min4log) {
+			switch(type & 0x0f) {
+			case 1:
+				new_a.min = parent->GetSize(SIZE_BOUNDS_XMIN);	break;
+			case 2:
+				new_a.min = parent->GetSize(SIZE_BOUNDS_YMIN);	break;
+			case 3:
+				new_a.min = parent->GetSize(SIZE_BOUNDS_ZMIN);	break;
+				}
+			}
+		if(cmpAxisDEF(&old_a, &new_a)) {
+			bChanged = true;
+			if(axis->flags != new_a.flags) parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+			Undo.AxisDef(this, axis, undo_flags);
+			memcpy(axis, &new_a, sizeof(AxisDEF));	undo_flags |= UNDO_CONTINUE;
+			axis->Start = axis->min;
+			}
+		else if(new_a.breaks) free(new_a.breaks);
+		if(axis->nBreaks && Dlg->GetValue(406, &tmp))
+			undo_flags = CheckNewFloat(&brkgap, brkgap, tmp, this, undo_flags);
+		if(axis->nBreaks && Dlg->GetValue(409, &tmp))
+			undo_flags = CheckNewFloat(&brksymsize, brksymsize, tmp, this, undo_flags);
+		if(Dlg->GetCheck(402)) i = 2;
+		else if(Dlg->GetCheck(403)) i = 3;
+		else if(Dlg->GetCheck(404)) i = 4;
+		else i = 0;
+		if(axis->nBreaks) undo_flags = CheckNewInt(&brksym, brksym, i, this, undo_flags);
+		if((!(axis->flags & 0x03) && Ticks && NumTicks)	|| (Dlg->GetCheck(203)) ||
+			((old_a.flags & 0x7000) != (new_a.flags & 0x7000) && (new_a.flags & AXIS_AUTOTICK))
+			|| ((new_a.flags & AXIS_AUTOTICK) &&(new_a.min != old_a.min || new_a.max != old_a.max))){
+			Undo.DropListGO(this, (GraphObj ***)&Ticks, &NumTicks, undo_flags);
+			undo_flags |= UNDO_CONTINUE;
+			}
+		if(Dlg->GetCheck(203)) {			//set ticks manually
+			if(!Dlg->GetValue(255, &tmp)) tmp = 0.0;
+			i = (int)tmp;
+			if(!Dlg->GetValue(251, &tmp)) tmp = axis->Start;
+			if(!Dlg->GetValue(253, &tmp2)) tmp2 = axis->Step;
+			if(!(axis->flags & 0x03)){
+				if(axis->flags & AXIS_ANGULAR) axis->flags |= AXIS_POSTICKS;
+				else axis->flags |= AXIS_SYMTICKS;
+				}
+			axis->flags &= ~AXIS_AUTOTICK;
+			ManuTicks(tmp, tmp2, i, axis->flags);
+			}
+		if(!(new_a.flags & 0x03) && (new_a.flags & AXIS_AUTOTICK)){
+			if(new_a.flags & AXIS_ANGULAR) axis->flags |= AXIS_POSTICKS;
+			else axis->flags |= AXIS_SYMTICKS;
+			}
+		if(undo_flags & UNDO_CONTINUE) {
+			if((axis->flags & AXIS_RADIAL)||(axis->flags & AXIS_ANGULAR))
+				parent->Command(CMD_AXIS, this, 0L);
+			if(parent->Id == GO_CONTOUR) parent->Command(CMD_RECALC, 0L, 0L);
+			}
+		if(names && CurrAxes && somePlots) {			//apply axis to plot ?
+			for(i = 0; CurrAxes[i] != this; i++);		//find index 
+			OD_axisplot(OD_ACCEPT, 0L, 0L, (anyOutput*) &res, 0L, 0);
+			if(somePlots[res] && somePlots[res]->Command(CMD_USEAXIS, &i, 0L))
+				undo_flags |= UNDO_CONTINUE;
+			}
+		if(Dlg->GetColor(125, &new_color) && new_color != colAxis) {
+			undo_flags = CheckNewDword(&colAxis, colAxis, new_color, this, undo_flags);
+			if ((axis->flags & AXIS_ANGULAR) || (axis->flags & AXIS_RADIAL)) {
+				Undo.ValDword(this, &GridLine.color, undo_flags);
+				GridLine.color = new_color;
+				}
+			if(Ticks || axisLabel) {
+				SavVarInit(200 * NumTicks);
+				if(axisLabel){
+					axisLabel->FileIO(SAVE_VARS);
+					axisLabel->SetColor(COL_TEXT, colAxis);
+					}
+				if(Ticks) for(i = 0; i < NumTicks; i++)  if(Ticks[i]){
+					Ticks[i]->FileIO(SAVE_VARS);
+					Ticks[i]->SetColor(COL_AXIS, colAxis);
+					}
+				sv_ptr = SavVarFetch();
+				Undo.SavVarBlock(this, &sv_ptr, undo_flags);
+				}
+			}
+		if(Dlg->GetValue(122, &tmp)) 
+			undo_flags = CheckNewFloat(&sizAxLine, sizAxLine, tmp, this, undo_flags);
+		if(Dlg->GetText(131, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0]) {
+			if(old_Label && strcmp(old_Label,TmpTxt) && axisLabel->Id == GO_LABEL){
+				lb_def = ((Label*)axisLabel)->GetTextDef();
+				undo_flags = CheckNewString(&lb_def->text, old_Label, TmpTxt, this, undo_flags);
+				}
+			else if(!axisLabel) {
+				label_def.ColTxt = colAxis;				label_def.ColBg = 0x00ffffffL;
+				label_def.fSize = DefSize(SIZE_TICK_LABELS)*1.2;
+				label_def.RotBL = fabs(si)>0.80 ? 90.0 : 0.0;
+				label_def.RotCHAR = 0.0f;				label_def.iSize = 0;
+				label_def.Align = TXA_VCENTER | TXA_HCENTER;
+				label_def.Mode = TXM_TRANSPARENT;		label_def.Style = TXS_NORMAL;
+				label_def.Font = FONT_HELVETICA;		label_def.text = TmpTxt;
+				Undo.SetGO(this, &axisLabel, new Label(this, data, 0, 0, &label_def,
+					LB_Y_PARENT | LB_X_PARENT), undo_flags);
+				undo_flags |= UNDO_CONTINUE;
+				}
+			}
+		else if(axisLabel) {
+			Undo.DeleteGO(&axisLabel, undo_flags, 0L);	undo_flags |= UNDO_CONTINUE;
+			}
+		if((type & 0x0f) == 4) {
+			if(Dlg->GetColor(552, &new_color) && gCol_0 != new_color){
+				Undo.ValDword(this, &gCol_0, undo_flags);
+				gCol_0 = new_color;
+				undo_flags |= UNDO_CONTINUE;	bUpdPG = true;
+				}
+			if(Dlg->GetColor(557, &new_color) && gCol_1 != new_color){
+				Undo.ValDword(this, &gCol_1, undo_flags);
+				gCol_1 = new_color;
+				undo_flags |= UNDO_CONTINUE;	bUpdPG = true;
+				}
+			if(Dlg->GetColor(559, &new_color) && gCol_2 != new_color){
+				Undo.ValDword(this, &gCol_2, undo_flags);
+				gCol_2 = new_color;
+				undo_flags |= UNDO_CONTINUE;	bUpdPG = true;
+				}
+			Dlg->GetValue(562, &transp);
+			new_color = ((((int)(transp*2.55))<<24)&0xff000000);
+			if(gTrans != new_color){
+				Undo.ValDword(this, &gTrans, undo_flags);
+				gTrans = new_color;
+				undo_flags |= UNDO_CONTINUE;	bUpdPG = true;
+				}
+			if(Dlg->GetCheck(560)) n_gradient |= 0x10;
+			else n_gradient &= ~0x10;
+			if(n_gradient != grad_type) {
+				Undo.ValInt(this, &grad_type, undo_flags);
+				grad_type = n_gradient;
+				undo_flags |= UNDO_CONTINUE;	bUpdPG = true;
+				}
+			if(bUpdPG){
+				Command(CMD_UPDPG, 0L, 0L);		bRet= true;
+				}
+			}
+		if(undo_flags & UNDO_CONTINUE) bRet= true;
+		break;
+		}
+	if(brks && nbrk) free(brks);
+	if(old_Label) free(old_Label);
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(names) {
+		for(j = 0; names[j]; j++) if(names[j]) free(names[j]);
+		free(names);
+		}
+	if(somePlots) free(somePlots);		free(AxisPropDlg);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Graph dialogs
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *AddPlotTmpl =
+		"1,2,,DEFAULT,PUSHBUTTON,-1,150,10,45,12\n"
+		"2,3,,,PUSHBUTTON,-2,150,25,45,12\n"
+		"3,,520,ISPARENT | CHECKED,GROUPBOX,1,5,10,135,95\n"
+		"520,521,,EXRADIO | CHECKED,ODBUTTON,2,10,20,25,25\n"
+		"521,522,,EXRADIO,ODBUTTON,2,35,20,25,25\n"
+		"522,523,,EXRADIO,ODBUTTON,2,60,20,25,25\n"
+		"523,524,,EXRADIO,ODBUTTON,2,85,20,25,25\n"
+		"524,525,,EXRADIO,ODBUTTON,2,110,20,25,25\n"
+		"525,526,,EXRADIO,ODBUTTON,2,10,45,25,25\n"
+		"526,528,,EXRADIO,ODBUTTON,2,35,45,25,25\n"
+		"528,529,,EXRADIO,ODBUTTON,2,60,45,25,25\n"
+		"529,530,,EXRADIO,ODBUTTON,2,85,45,25,25\n"
+		"530,531,,EXRADIO,ODBUTTON,2,110,45,25,25\n"
+		"531,532,,EXRADIO,ODBUTTON,2,10,70,25,25\n"
+		"532,540,,EXRADIO,ODBUTTON,2,35,70,25,25\n"
+		"540,541,,EXRADIO,ODBUTTON,2,60,70,25,25\n"
+		"541,,,LASTOBJ | EXRADIO,ODBUTTON, 2, 85,70,25,25";
+
+bool
+Graph::AddPlot(int family)
+{
+	void *dyndata[] = {(void *)"  select template  ",(void*)OD_PlotTempl};
+	DlgInfo *GraphDlg = CompileDialog(AddPlotTmpl, dyndata);
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, res, cSel = 520;
+	bool bRet = false;
+	Plot *p;
+
+	switch(type) {
+	case GT_STANDARD:
+		break;
+	case GT_POLARPLOT:
+		for(i = 0; i < NumPlots; i++) {
+			if(Plots[i] && Plots[i]->Id == GO_POLARPLOT) {
+				if (((PolarPlot*)Plots[i])->AddPlot()) 
+					return Command(CMD_REDRAW, 0L, 0L);
+				}
+			}
+		return false;
+	default:
+		InfoBox("Don\'t know how to\nadd a plot to the\ncurrent graph.");
+		return false;
+		}
+	Dlg = new DlgRoot(GraphDlg, data);		Dlg->bModal = false;
+	hDlg = CreateDlgWnd("Add Plot", 50, 50, 410, 240, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 520:	case 521:	case 522:	case 523:	case 524:
+		case 525:	case 526:	case 528:	case 529:	case 530:
+		case 531:	case 532:	case 540:	case 541:
+			if(res == cSel) res = 1;
+			else {
+				cSel = res;				res = -1;
+				}
+			break;
+			}
+		}while (res < 0);
+	if(res == 1){						//OK pressed
+		if(Dlg->GetCheck(524)) p = new BubblePlot(this, data);
+		else if(Dlg->GetCheck(525)) p = new BoxPlot(this, data);
+		else if(Dlg->GetCheck(521)) p = new PlotScatt(this, data, 0x03);
+		else if(Dlg->GetCheck(522)) p = new PlotScatt(this, data, 0x08);
+		else if(Dlg->GetCheck(523)) p = new PlotScatt(this, data, 0x04);
+		else if(Dlg->GetCheck(526)) p = new Regression(this, data);
+		else if(Dlg->GetCheck(528)) p = new DensDisp(this, data);
+		else if(Dlg->GetCheck(529)) p = new Function(this, data, "Function");
+		else if(Dlg->GetCheck(530)) p = new FitFunc(this, data);
+		else if(Dlg->GetCheck(531)) p = new MultiLines(this, data);
+		else if(Dlg->GetCheck(532)) p = new xyStat(this, data);
+		else if(Dlg->GetCheck(540)) p = new StackBar(this, data);
+		else if(Dlg->GetCheck(541)) p = new StackPG(this, data);
+		else p = new PlotScatt(this, data, 0x01);
+		if(p && p->PropertyDlg()) {
+			if(!Command(CMD_DROP_PLOT, p, (anyOutput *)NULL)) delete p;
+			else bRet = true;
+			}
+		else if(p) delete p;
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;		free(GraphDlg);
+	return bRet;
+}
+
+static char *GraphDlgTmpl = 
+		"1,+,,DEFAULT,PUSHBUTTON,-1,170,10,45,12\n"
+		".,.,,,PUSHBUTTON,-2,170,25,45,12\n"
+		".,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+		"4,+,100,ISPARENT | CHECKED,SHEET,1,5,10,157,122\n"
+		".,.,200,ISPARENT,SHEET,2,5,10,157,122\n"
+		".,,300,ISPARENT,SHEET,3,5,10,157,122\n"
+		"100,+,,,LTEXT,4,10,25,60,8\n"
+		".,.,500,TOUCHEXIT | ISPARENT,SHEET,5,10,37,147,90\n"
+		".,.,520,TOUCHEXIT | ISPARENT,SHEET,6,10,37,147,90\n"
+		".,.,540,TOUCHEXIT | ISPARENT,SHEET,7,10,37,147,90\n"
+		".,,560,TOUCHEXIT | ISPARENT,SHEET,8,10,37,147,90\n"
+		"200,+,,,LTEXT,9,10,35,60,8\n"
+		".,.,,,RTEXT,10,5,47,58,8\n"
+		".,.,,,EDVAL1,11,64,47,30,10\n"
+		".,.,,,RTEXT,-5,95,47,10,8\n"
+		".,.,,,EDVAL1,12,107,47,30,10\n"
+		".,.,,,LTEXT,-3,140,47,20,8\n"
+		".,.,,,RTEXT,13,5,59,58,8\n"
+		".,.,,,EDVAL1,14,64,59,30,10\n"
+		".,.,,,RTEXT,-5,95,59,10,8\n"
+		".,.,,,EDVAL1,15,107,59,30,10\n"
+		".,.,,,LTEXT,-3,140,59,20,8\n"
+		".,.,,,LTEXT,16,10,84,60,8\n"
+		".,.,,,RTEXT,17,5,96,58,8\n"
+		".,.,,,EDVAL1,18,64,96,30,10\n"
+		".,.,,,RTEXT,-5,95,96,10,8\n"
+		".,.,,,EDVAL1,19,107,96,30,10\n"
+		".,.,,,LTEXT,-3,140,96,20,8\n"
+		".,.,,,RTEXT,20,5,108,58,8\n"
+		".,.,,,EDVAL1,21,64,108,30,10\n"
+		".,.,,,RTEXT,-5,95,108,10,8\n"
+		".,.,,,EDVAL1,22,107,108,30,10\n"
+		".,,,,LTEXT,-3,140,108,20,8\n"
+		"300,+,,,LTEXT,23,20,30,60,8\n"
+		".,400,310,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
+		"310,+,,EXRADIO,ODBUTTON,24,20,42,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,24,45,42,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,24,70,42,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,24,95,42,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,24,120,42,25,25\n"
+		".,.,,CHECKED | TOUCHEXIT,RADIO1, 25, 12,85,40,8\n"
+		".,.,,TOUCHEXIT,RADIO1,26,12,93,40,8\n"
+		".,.,,TOUCHEXIT,RADIO1,27,12,101,40,8\n"
+		".,.,,TOUCHEXIT,CHECKBOX,28,80,85,40,8\n"
+		".,,,TOUCHEXIT, CHECKBOX,29,80,93,40,8\n"
+		"400,,410,HIDDEN | CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
+		"410,+,,EXRADIO,ODBUTTON,30,20,42,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,30,45,42,25,25\n"
+		".,,,EXRADIO, ODBUTTON,30,70,42,25,25\n"
+		"500,+,,EXRADIO,ODBUTTON,31,20,60,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,45,60,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,70,60,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,20,85,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,45,85,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,95,85,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,120,85,25,25\n"
+		".,,,EXRADIO,ODBUTTON,31,70,85,25,25\n"
+		"520,+,,EXRADIO,ODBUTTON, 31, 20,50,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,45,50,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,70,50,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,95,50,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,120,50,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,20,75,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,45,75,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,70,75,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,95,75,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,120,75,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,20,100,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,45,100,25,25\n"
+		".,,,EXRADIO,ODBUTTON,31,70,100,25,25\n"
+		"540,+,,EXRADIO,ODBUTTON,31,20,60,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,45,60,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,70,60,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,95,60,25,25\n"
+		".,,,TOUCHEXIT | ISRADIO,ODBUTTON, 31, 120,60,25,25\n"
+		"560,+,,EXRADIO,ODBUTTON, 31,20,60,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,45,60,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,70,60,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,95,60,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,120,60,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,20,85,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,45,85,25,25\n"
+		".,.,,EXRADIO,ODBUTTON,31,70,85,25,25\n"
+		".,,,LASTOBJ | EXRADIO, ODBUTTON,31,95,85,25,25";
+
+static int selSheet = 102, selPlt = 520, selAxis = 310;
+bool
+Graph::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 22, 10, "Data"};
+	TabSHEET tab2 = {22, 62, 10, "Placement"};
+	TabSHEET tab3 = {62, 87, 10, "Axes"};
+	TabSHEET tab_A = {0, 27, 10, "only Y"};
+	TabSHEET tab_B = {27, 65, 10, "XY values"};
+	TabSHEET tab_C = {65, 104, 10, "X, many Y"};
+	TabSHEET tab_D = {104, 147, 10, "XYZ values"};
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)"arrangement of data: select plot",
+		(void*)&tab_A, (void*)&tab_B, (void*)&tab_C, (void*)&tab_D, (void*)"bounding rectangle (relative to page)",
+		(void*)"upper left corner x", (void*)&GRect.Xmin, (void*)&GRect.Ymin, (void*)"lower right x",
+		(void*)&GRect.Xmax, (void*)&GRect.Ymax, (void*)"plotting rectangle (relative to bounding rectangle)",
+		(void*)"upper left corner x", (void*)&DRect.Xmin, (void*)&DRect.Ymin, (void*)"lower right x",
+		(void*)&DRect.Xmax, (void*)&DRect.Ymax, (void*)"select template:", (void*)(OD_AxisTempl),
+		(void*)"ticks outside", (void*)"ticks inside", (void*)"ticks symmetrical", (void*)"horizontal grid lines",
+		(void*)"vertical grid lines", (void*)(OD_AxisTempl3D), (void*)(OD_PlotTempl)};
+	DlgInfo *GraphDlg = CompileDialog(GraphDlgTmpl, dyndata);
+	DlgRoot *Dlg;
+	GraphObj *p;
+	void *hDlg;
+	int i, res;
+	bool bRet, bContinue;
+	fRECT rc1, rc2;
+
+	ODtickstyle = tickstyle;
+	AxisTempl = 0;
+	if(!parent) return false;
+	Dlg = new DlgRoot(GraphDlg, data);	Dlg->bModal = false;
+	Dlg->SetCheck(410 + AxisTempl3D, 0L, true);
+	if(parent->Id != GO_PAGE) {
+		Dlg->Activate(202, false);	Dlg->Activate(204, false);
+		}
+	//restore previous settitings
+	switch(selSheet) {
+		case 101:
+			if(selPlt >= 500 && selPlt <=507) Dlg->SetCheck(selPlt, 0L, true);
+			else Dlg->SetCheck(500, 0L, true);
+			Dlg->SetCheck(520, 0L, true);		Dlg->SetCheck(540, 0L, true);
+			Dlg->SetCheck(560, 0L, true);		break;
+		default:
+			if(selPlt >= 520 && selPlt <=532) Dlg->SetCheck(selPlt, 0L, true);
+			else Dlg->SetCheck(520, 0L, true);	selSheet = 102;
+			Dlg->SetCheck(500, 0L, true);		Dlg->SetCheck(540, 0L, true);
+			Dlg->SetCheck(560, 0L, true);		break;
+		case 103:
+			if(selPlt >= 540 && selPlt <=544) Dlg->SetCheck(selPlt, 0L, true);
+			else Dlg->SetCheck(540, 0L, true);
+			Dlg->SetCheck(520, 0L, true);		Dlg->SetCheck(500, 0L, true);
+			Dlg->SetCheck(560, 0L, true);		break;
+		case 104:
+			if(selPlt >= 560 && selPlt <=568) Dlg->SetCheck(selPlt, 0L, true);
+			else Dlg->SetCheck(560, 0L, true);
+			Dlg->ShowItem(301, false);	Dlg->ShowItem(400, true);
+			Dlg->SetCheck(520, 0L, true);		Dlg->SetCheck(540, 0L, true);
+			Dlg->SetCheck(500, 0L, true);		break;
+		}
+	Dlg->SetCheck(selSheet, 0L, true);
+	if(selAxis >= 310 && selAxis <= 314) Dlg->SetCheck(selAxis, 0L, true);
+	else Dlg->SetCheck(310, 0L, true);
+	if(selAxis >= 410 && selAxis <= 412) Dlg->SetCheck(selAxis, 0L, true);
+	else Dlg->SetCheck(410, 0L, true);
+	//display the dialog
+	hDlg = CreateDlgWnd("Create graph", 50, 50, 450, 300, Dlg, 0x4L);
+	bContinue = false;
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		bRet = false;
+		switch(res) {
+		case 0:
+			if(bContinue) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 101:	//only y data
+			for(i = 500; i <= 506; i++) if(Dlg->GetCheck(i))selPlt = i;
+			Dlg->ShowItem(301, true);	Dlg->ShowItem(400, false);
+			selSheet = res;				res = -1;
+			break;
+		case 102:	//xy data
+			for(i = 520; i <= 532; i++) if(Dlg->GetCheck(i))selPlt = i;
+			Dlg->ShowItem(301, true);	Dlg->ShowItem(400, false);
+			selSheet = res;				res = -1;
+			break;
+		case 103:	//x many y data
+			for(i = 540; i <= 544; i++) if(Dlg->GetCheck(i))selPlt = i;
+			Dlg->ShowItem(301, true);	Dlg->ShowItem(400, false);
+			selSheet = res;				res = -1;
+			break;
+		case 104:	//xyz data
+			for(i = 560; i <= 567; i++) if(Dlg->GetCheck(i))selPlt = i;
+			Dlg->ShowItem(301, false);	Dlg->ShowItem(400, true);
+			selSheet = res;				res = -1;
+			break;
+		case 310:	case 311:	case 312:	case 313:	case 314:
+			AxisTempl = res-310;
+			if(res == selAxis) Dlg->SetCheck(4, 0L, true);
+			selAxis = res;
+			res = -1;
+			break;
+		case 315:	case 316:	case 317:	//tick style
+			tickstyle = res -315;
+		case 318:	case 319:				//horizontal or vertical grid
+			tickstyle &= ~0x300;
+			if(Dlg->GetCheck(318)) tickstyle |= 0x200;
+			if(Dlg->GetCheck(319)) tickstyle |= 0x100;
+			ODtickstyle = tickstyle;
+			Dlg->DoPlot(0L);
+			res = -1;
+			break;
+		case 410:	case 411:	case 412:	//axis templates
+			AxisTempl3D = res-410;
+			res = -1;
+			break;
+		case 500:	case 501:	case 502:	case 503:
+		case 504:	case 505:	case 506:	case 507:
+		case 520:	case 521:	case 522:	case 523:
+		case 524:	case 525:	case 526:	case 527:
+		case 528:	case 529:	case 530:	case 531:	case 532:
+		case 540:	case 541:	case 542:	case 543:
+		case 544:
+		case 560:	case 561:	case 562:	case 563:
+		case 564:	case 565:	case 566:	case 567:	case 568:
+			if(res != selPlt) {
+				selPlt = res;
+				res = -1;
+				break;
+				}
+			//double click means select: continue as if OK
+			res = 1;
+		case 1:
+			if(!Dlg->GetCheck(4)) {
+				Dlg->SetCheck(4, 0L, true);
+				res = -1;
+				break;
+				}
+			memcpy(&rc1, &GRect, sizeof(fRECT));
+			memcpy(&rc2, &DRect, sizeof(fRECT));
+			Dlg->GetValue(202, &rc1.Xmin);			Dlg->GetValue(204, &rc1.Ymin);
+			Dlg->GetValue(207, &rc1.Xmax);			Dlg->GetValue(209, &rc1.Ymax);
+			Dlg->GetValue(213, &rc2.Xmin);			Dlg->GetValue(215, &rc2.Ymin);
+			Dlg->GetValue(218, &rc2.Xmax);			Dlg->GetValue(220, &rc2.Ymax);
+			if(rc1.Xmin < 0.0 || rc1.Xmax < 0.0 || rc1.Ymin < 0.0 || 
+				rc1.Ymax < 0.0 || rc2.Xmin < 0.0 || rc2.Xmax < 0.0 || 
+				rc2.Ymin < 0.0 || rc2.Ymax < 0.0) {
+				ErrorBox("All values defining\nthe placement of the plot\n"
+					"must be positive.");
+				res = -1;
+				bContinue = true;
+				Dlg->SetCheck(5, 0L, true);
+				break;
+				}
+			if(rc2.Xmin > (rc1.Xmax-rc1.Xmin) || rc2.Ymax > (rc1.Ymax-rc1.Ymin) 
+				|| (rc2.Xmax-rc2.Xmin) > (rc1.Xmax-rc1.Xmin)
+				|| (rc2.Ymax-rc2.Ymin) > (rc1.Ymax-rc1.Ymin)){
+				ErrorBox("The plotting rectangle must\nfit inside the\n"
+					"bounding rectangle.");
+				res = -1;
+				bContinue = true;
+				Dlg->SetCheck(5, 0L, true);
+				break;
+				}
+			memcpy(&GRect, &rc1, sizeof(fRECT));
+			memcpy(&DRect, &rc2, sizeof(fRECT));
+			p = 0L;
+			if(Dlg->GetCheck(101)) {
+				if(Dlg->GetCheck(500))p = new PieChart(this, data);
+				else if(Dlg->GetCheck(501))p = new RingChart(this, data);
+				else if(Dlg->GetCheck(502))p = new StarChart(this, data);
+				else if(Dlg->GetCheck(503))p = new BarChart(this, data);
+				else if(Dlg->GetCheck(504))p = new GroupBars(this, data, 0);
+				else if(Dlg->GetCheck(505))p = new FreqDist(this, data);
+				else if(Dlg->GetCheck(506))p = new NormQuant(this, data, 0L);
+				else if(Dlg->GetCheck(507))p = new GroupBars(this, data, 1);
+				}
+			else if(Dlg->GetCheck(102)){
+				if(Dlg->GetCheck(524)) p = new BubblePlot(this, data);
+				else if(Dlg->GetCheck(525)) p = new BoxPlot(this, data);
+				else if(Dlg->GetCheck(521)) p = new PlotScatt(this, data, 0x03);
+				else if(Dlg->GetCheck(522)) p = new PlotScatt(this, data, 0x08);
+				else if(Dlg->GetCheck(523)) p = new PlotScatt(this, data, 0x04);
+				else if(Dlg->GetCheck(526)) p = new Regression(this, data);
+				else if(Dlg->GetCheck(527)) p = new PolarPlot(this, data);
+				else if(Dlg->GetCheck(528)) p = new DensDisp(this, data);
+				else if(Dlg->GetCheck(529)) p = new Function(this, data, "Function");
+				else if(Dlg->GetCheck(530)) p = new FitFunc(this, data);
+				else if(Dlg->GetCheck(531)) p = new MultiLines(this, data);
+				else if(Dlg->GetCheck(532)) p = new xyStat(this, data);
+				else p = new PlotScatt(this, data, 0x01);
+				}
+			else if(Dlg->GetCheck(103)) {
+				if(Dlg->GetCheck(540)) p = new StackBar(this, data);
+				else if(Dlg->GetCheck(542)) p = new Waterfall(this, data);
+				else if(Dlg->GetCheck(543)) p = new Chart25D(this, data, 0L);
+				else if(Dlg->GetCheck(544)) p = new Ribbon25D(this, data, 0L);
+				else p = new StackPG(this, data);
+				}
+			else if(Dlg->GetCheck(104)) {
+				if(Dlg->GetCheck(560)) p = new Plot3D(this, data, 0x1001);
+				else if(Dlg->GetCheck(561)) p = new Plot3D(this, data, 0x1002);
+				else if(Dlg->GetCheck(562)) p = new Plot3D(this, data, 0x1004);
+				else if(Dlg->GetCheck(563)) p = new BubblePlot3D(this, data);
+				else if(Dlg->GetCheck(564)) p = new Plot3D(this, data, 0x2000);
+				else if(Dlg->GetCheck(565)) p = new Func3D(this, data);
+				else if(Dlg->GetCheck(566)) p = new FitFunc3D(this, data);
+				else if(Dlg->GetCheck(567)) p = new Plot3D(this, data, 0x4000);
+				else if(Dlg->GetCheck(568)) p = new ContourPlot(this, data);
+				}
+			if(p && p->PropertyDlg()) {
+				if(!Command(CMD_DROP_PLOT, p, 0L)) DeleteGO(p);
+				else bRet = true;
+				}
+			else if(p) {
+				Dlg->SetCheck(410 + AxisTempl3D, 0L, true);
+				DeleteGO(p);		Dlg->DoPlot(0L);			ShowDlgWnd(hDlg);
+				}
+			if(!bRet) {
+				res = -1;
+				//we might have lost the focus
+				bContinue = true;
+				}
+			break;
+			}
+		}while(res <0);
+	Command(CMD_SET_DATAOBJ, (void*)data, 0L);
+	CloseDlgWnd(hDlg);		delete Dlg;		free(GraphDlg);
+	return bRet;
+}
+
+bool
+Graph::Configure()
+{
+	TabSHEET tab1 = {0, 28, 10, "Colors"};
+	TabSHEET tab2 = {28, 68, 10, "Placement"};
+	DlgInfo GraphDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 170, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 170, 25, 45, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Add Plot", 170, 73, 45, 12},
+		{4, 5, 0, 0x0L, PUSHBUTTON, (void*)"Add Axis", 170, 88, 45, 12},
+		{5, 6, 0, 0x0L, PUSHBUTTON, (void*)"Add Legend", 170, 103, 45, 12},
+		{6, 13, 0, 0x0L, PUSHBUTTON, (void*)"Layers", 170, 118, 45, 12},
+		{13, 50, 14, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{14, 15, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 155, 120},
+		{15, 0, 200, ISPARENT, SHEET, &tab2, 5, 10, 155, 120},
+		{50, 0, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"bounding rectangle", 20, 30, 60, 8},
+		{101, 102, 0, 0x0L, RTEXT, (void*)"fill color", 15, 42, 58, 8},
+		{102, 103, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&ColGR, 74, 42, 25, 10},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"outline color", 15, 54, 58, 8},
+		{104, 105, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&ColGRL, 74, 54, 25, 10},
+		{105, 106, 0, 0x0L, LTEXT, (void*)"plotting rectangle", 20, 68, 60, 8},
+		{106, 107, 0, 0x0L, RTEXT, (void*)"fill color", 15, 80, 58, 8},
+		{107, 108, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&ColDR, 74, 80, 25, 10},
+		{108, 109, 0, 0x0L, LTEXT, (void*)"axes, ticks, and axis labels", 20, 94, 60, 8},
+		{109, 110, 0, 0x0L, RTEXT, (void*)"axis color", 15, 106, 58, 8},
+		{110, 0, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&ColAX, 74, 106, 25, 10},
+		{200, 201, 0, 0x0L, LTEXT, (void*)"bounding rectangle (relative to page)", 10, 35, 60, 8},
+		{201, 202, 0, 0x0L, RTEXT, (void*)"upper left corner x", 5, 47, 58, 8},
+		{202, 203, 0, 0x0L, EDVAL1, &GRect.Xmin, 64, 47, 30, 10},
+		{203, 204, 0, 0x0L, RTEXT, (void*)"y", 95, 47, 10, 8},
+		{204, 205, 0, 0x0L, EDVAL1, &GRect.Ymin, 107, 47, 30, 10},
+		{205, 206, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 140, 47, 20, 8},
+		{206, 207, 0, 0x0L, RTEXT, (void*)"lower right x", 5, 59, 58, 8},
+		{207, 208, 0, 0x0L, EDVAL1, &GRect.Xmax, 64, 59, 30, 10},
+		{208, 209, 0, 0x0L, RTEXT, (void*)"y", 95, 59, 10, 8},
+		{209, 210, 0, 0x0L, EDVAL1, &GRect.Ymax, 107, 59, 30, 10},
+		{210, 211, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 140, 59, 20, 8},
+		{211, 212, 0, 0x0L, LTEXT, (void*)"plotting rectangle (relative to bounding rectangle)", 10, 84, 60, 8},
+		{212, 213, 0, 0x0L, RTEXT, (void*)"upper left corner x", 5, 96, 58, 8},
+		{213, 214, 0, 0x0L, EDVAL1, &DRect.Xmin, 64, 96, 30, 10},
+		{214, 215, 0, 0x0L, RTEXT, (void*)"y", 95, 96, 10, 8},
+		{215, 216, 0, 0x0L, EDVAL1, &DRect.Ymin, 107, 96, 30, 10},
+		{216, 217, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 140, 96, 20, 8},
+		{217, 218, 0, 0x0L, RTEXT, (void*)"lower right x", 5, 108, 58, 8},
+		{218, 219, 0, 0x0L, EDVAL1, &DRect.Xmax, 64, 108, 30, 10},
+		{219, 220, 0, 0x0L, RTEXT, (void*)"y", 95, 108, 10, 8},
+		{220, 221, 0, 0x0L, EDVAL1, &DRect.Ymax, 107, 108, 30, 10},
+		{221, 0, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 140, 108, 20, 8},
+		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 170, 40, 15, 15},
+		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 190, 40, 15, 15},
+		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 190, 55, 15, 15},
+		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 170, 55, 15, 15}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, res, undo_level = *Undo.pcb;
+	DWORD undo_flags = 0, tmpcol;
+	anyOutput *cdisp = Undo.cdisp;
+	bool bRet = false, bContinue = false;
+	fRECT o_gr, n_gr, o_dr, n_dr;
+
+	if(!(Dlg = new DlgRoot(GraphDlg, data)))return false;
+	Dlg->GetValue(202, &o_gr.Xmin);			Dlg->GetValue(204, &o_gr.Ymin);
+	Dlg->GetValue(207, &o_gr.Xmax);			Dlg->GetValue(209, &o_gr.Ymax);
+	Dlg->GetValue(213, &o_dr.Xmin);			Dlg->GetValue(215, &o_dr.Ymin);
+	Dlg->GetValue(218, &o_dr.Xmax);			Dlg->GetValue(220, &o_dr.Ymax);
+	if(parent && parent->Id == GO_PAGE) Dlg->ShowItem(50, true);
+	i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, name ? name : (char*)"Graph");
+	rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, (char*)" properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 450, 300, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();			res = Dlg->GetResult();
+		switch (res) {
+		case 600:	case 601:	case 602:	case 603:
+			Undo.SetDisp(cdisp);
+			res = ExecDrawOrderButt(parent, this, res);
+			}
+		switch(res) {
+		case 0:
+			if(bContinue) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 102:	case 104:	case 107:	case 110:
+			res = -1;
+			break;
+		case 1:
+			Dlg->GetValue(202, &n_gr.Xmin);			Dlg->GetValue(204, &n_gr.Ymin);
+			Dlg->GetValue(207, &n_gr.Xmax);			Dlg->GetValue(209, &n_gr.Ymax);
+			Dlg->GetValue(213, &n_dr.Xmin);			Dlg->GetValue(215, &n_dr.Ymin);
+			Dlg->GetValue(218, &n_dr.Xmax);			Dlg->GetValue(220, &n_dr.Ymax);
+			if(n_gr.Xmin < 0.0 || n_gr.Xmax < 0.0 || n_gr.Ymin < 0.0 || 
+				n_gr.Ymax < 0.0 || n_dr.Xmin < 0.0 || n_dr.Xmax < 0.0 || 
+				n_dr.Ymin < 0.0 || n_dr.Ymax < 0.0) {
+				ErrorBox("All values defining\nthe placement of the plot\n"
+					"must be positive.");
+				res = -1;
+				bContinue = true;
+				Dlg->SetCheck(5, 0L, true);
+				break;
+				}
+			if(n_dr.Xmin > (n_gr.Xmax-n_gr.Xmin) || n_dr.Ymax > (n_gr.Ymax-n_gr.Ymin) 
+				|| (n_dr.Xmax-n_dr.Xmin) > (n_gr.Xmax-n_gr.Xmin)
+				|| (n_dr.Ymax-n_dr.Ymin) > (n_gr.Ymax-n_gr.Ymin)){
+				ErrorBox("The plotting rectangle must\nfit inside the\n"
+					"bounding rectangle.");
+				res = -1;
+				bContinue = true;
+				Dlg->SetCheck(5, 0L, true);
+				break;
+				}
+			bRet = true;
+			break;
+		case 3:
+			if(bRet = Command(CMD_ADDPLOT, 0L, 0L)) break;
+		case 4:
+			if(res == 4 && (bRet = Command(CMD_ADDAXIS, 0L, 0L))) break;
+			bContinue = true;
+			res = -1;
+			break;
+		case 5:
+			Command(CMD_LEGEND, 0L, 0L);
+			break;
+		case 6:
+			Command(CMD_LAYERS, 0L, 0L);
+			bContinue = true;
+			res = -1;
+			break;
+			}
+		}while(res <0);
+	if(res == 1 && bRet) {
+		Undo.SetDisp(cdisp);
+		if(n_gr.Xmin != o_gr.Xmin || n_gr.Xmax != o_gr.Xmax ||
+			n_gr.Ymin != o_gr.Ymin || n_gr.Ymax != o_gr.Ymax ||
+			n_dr.Xmin != o_dr.Xmin || n_dr.Xmax != o_dr.Xmax ||
+			n_dr.Ymin != o_dr.Ymin || n_dr.Ymax != o_dr.Ymax){
+			Command(CMD_SAVEPOS, 0L, 0L);
+			memcpy(&GRect, &n_gr, sizeof(fRECT));
+			memcpy(&DRect, &n_dr, sizeof(fRECT));
+			undo_flags |= UNDO_CONTINUE;
+			}
+		if(Dlg->GetColor(102, &tmpcol))
+			undo_flags = CheckNewDword(&ColGR, ColGR, tmpcol, this, undo_flags);
+		if(Dlg->GetColor(104, &tmpcol))
+			undo_flags = CheckNewDword(&ColGRL, ColGRL, tmpcol, this, undo_flags);
+		if(Dlg->GetColor(107, &tmpcol))
+			undo_flags = CheckNewDword(&ColDR, ColDR, tmpcol, this, undo_flags);
+		if(Dlg->GetColor(110, &tmpcol) && tmpcol != ColAX) {
+			undo_flags = CheckNewDword(&ColAX, ColAX, tmpcol, this, undo_flags);
+			if(Axes) for(i = 0; i < NumAxes; i++)
+				if(Axes[i]) Axes[i]->SetColor(COL_AXIS | UNDO_STORESET, ColAX);
+			if(Plots && NumPlots && Plots[0] && (Plots[0]->Id == GO_PLOT3D || Plots[0]->Id == GO_FUNC3D)) 
+				Plots[0]->SetColor(COL_AXIS | UNDO_STORESET, ColAX);
+			}
+		}
+	else if(res == 2) {
+		Undo.SetDisp(cdisp);
+		if(*Undo.pcb > undo_level) {	//restore plot order
+			while(*Undo.pcb > undo_level)	Undo.Restore(false, 0L);
+			bRet = true;
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+static char *AddAxisTmpl =
+	"1,2,,DEFAULT, PUSHBUTTON,-1,148,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,148,25,45,12\n"
+	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,5,50,TOUCHEXIT | ISPARENT,SHEET,1,5,10,130,130\n"
+	"5,6,200,ISPARENT | CHECKED,SHEET,2,5,10,130,130\n"
+	"6,,300,ISPARENT,SHEET,3,5,10,130,130\n"
+	"50,51,100,ISPARENT | CHECKED,GROUPBOX,4,10,30,120,36\n"
+	"51,120,,,CHECKBOX,5,17,37,80,8\n"
+	"100,101,,,RTEXT,6,10,51,35,8\n"
+	"101,102,,,EDVAL1,7,48,51,32,10\n"
+	"102,103,,,CTEXT,8,81,51,11,8\n"
+	"103,,,,EDVAL1,9,93,51,32,10\n"
+	"120,,121,ISPARENT | CHECKED,GROUPBOX,10,10,72,120,20\n"
+	"121,122,,,RTEXT,11,10,77,25,8\n"
+	"122,123,,,EDVAL1,12,37,77,25,10\n"
+	"123,124,,,LTEXT,-3,63,77,10,8\n"
+	"124,125,,,RTEXT,-11,73,77,25,8\n"
+	"125,130,,OWNDIALOG,COLBUTT,14,100,77,25,10\n"
+	"130,131,,ISPARENT | CHECKED,GROUPBOX,15,10,98,120,20\n"
+	"131,,,,EDTEXT,0,15,103,110,10\n"
+	"200,201,,,LTEXT,16,10,30,70,9\n"
+	"201,202,,CHECKED | EXRADIO,ODBUTTON,17,20,42,25,25\n"
+	"202,203,,EXRADIO,ODBUTTON,17,45,42,25,25\n"
+	"203,204,,EXRADIO,ODBUTTON,17,70,42,25,25\n"
+	"204,205,,EXRADIO,ODBUTTON,17,95,42,25,25\n"
+	"205,206,,EXRADIO,ODBUTTON,17,20,67,25,25\n"
+	"206,207,,EXRADIO,ODBUTTON,17,45,67,25,25\n"
+	"207,208,,EXRADIO,ODBUTTON,17,70,67,25,25\n"
+	"208,210,,EXRADIO,ODBUTTON,17,95,67,25,25\n"
+	"210,,220,ISPARENT | CHECKED, GROUPBOX,18,10,97,120,35\n"
+	"220,221,,,RTEXT,-4,10,105,15,8\n"
+	"221,222,,,EDVAL1,19,27,105,35,10\n"
+	"222,223,,,LTEXT,-7,65,105,5,8\n"
+	"223,224,,,EDVAL1,20,71,105,35,10\n"
+	"224,225,,,LTEXT,-3,109,105,15,8\n"
+	"225,226,,,RTEXT,-5,10,117,15,8\n"
+	"226,227,,,EDVAL1,21,27,117,35,10\n"
+	"227,228,,,LTEXT,-7,65,117,5,8\n"
+	"228,229,,,EDVAL1,22,71,117,35,10\n"
+	"229,,,,LTEXT,-3,109,117,15,8\n"
+	"300,,,LASTOBJ | NOSELECT,ODBUTTON,23,15,30,110,140";
+
+bool
+Graph::AddAxis()
+{
+	TabSHEET tab1 = {0, 25, 10, "Axis"};
+	TabSHEET tab2 = {25, 52, 10, "Style"};
+	TabSHEET tab3 = {52, 78, 10, "Plots"};
+	AxisDEF axis;
+	double sizAxLine = DefSize(SIZE_AXIS_LINE);
+	DWORD colAxis = ColAX;
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)&tab3, (void*)" scaling ", (void*)" automatic scaling",
+		(void*)"axis from", (void*)&y_axis.min, (void*)"to", (void*)&y_axis.max, (void*)" line ", (void*)"width",
+		(void*)&sizAxLine, (void*)0L, (void *)&colAxis, (void*)" axis label ", (void*)"select a template:",
+		(void*)(OD_NewAxisTempl), (void*)" placement ", (void*)&axis.loc[0].fx, (void*)&axis.loc[1].fx,
+		(void*)&axis.loc[0].fy, (void*)&axis.loc[1].fy,  (void*)OD_axisplot};
+	DlgInfo *NewAxisDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, res, currTempl = 201;
+	double vx1, vx2, vy1, vy2, hx1, hx2, hy1, hy2;
+	double tlb_dist, tlb_dx, tlb_dy, lb_x, lb_y;
+	TextDEF label_def, tlbdef;
+	DWORD flags;
+	anyOutput *cdisp = Undo.cdisp;
+	Axis *the_new, **tmpAxes;
+	bool bAxis = false, bRet = false;
+	char **names;
+	GraphObj **somePlots;
+	Label *label;
+
+	if(!(NewAxisDlg = CompileDialog(AddAxisTmpl, dyndata)))return false;
+	if(!(names = (char**)calloc(nscp+2, sizeof(char*))))return false;
+	if(!(somePlots = (GraphObj**)calloc(nscp+2, sizeof(GraphObj*))))return false;
+	if(!Axes) Axes = (Axis**)calloc(2, sizeof(Axis*));
+	if(names[0] = (char*)malloc(10)) rlp_strcpy(names[0], 10, "[none]");
+	for(i = 0, j = 1; i < nscp; i++) {
+		if(Sc_Plots[i] && Sc_Plots[i]->name){
+			names[j] = (char*)memdup(Sc_Plots[i]->name, (int)strlen(Sc_Plots[i]->name)+1, 0);
+			somePlots[j++] = Sc_Plots[i];
+			}
+		}
+	OD_axisplot(OD_ACCEPT, 0L, 0L, 0L, names, 0);
+	axis.loc[0].fx = axis.loc[1].fx = vx1 = vx2 = (DRect.Xmin + DRect.Xmax)/2.0;
+	axis.loc[0].fy = vy1 = DRect.Ymin;
+	axis.loc[1].fy = vy2 = DRect.Ymax;
+	axis.min = y_axis.min;		axis.max = y_axis.max;
+	hy1 = hy2 = (DRect.Ymax + DRect.Ymin)/2.0;
+	hx1 = DRect.Xmin;			hx2 = DRect.Xmax;
+	if(!(Dlg = new DlgRoot(NewAxisDlg, data)))return false;
+	if(type != 1 || !nscp){		//must be standard graph to link to plot
+		Dlg->ShowItem(6, false);
+		}
+	hDlg = CreateDlgWnd("New axis properties", 50, 50, 400, 318, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res){
+		case 4:											//the axis sheet
+			res = -1;
+			bAxis = true;
+			break;
+		case 201:	case 202:	case 203:	case 204:	//axis templates
+		case 205:	case 206:	case 207:	case 208:
+			if(currTempl > 204) {
+				Dlg->GetValue(221, &hx1);				Dlg->GetValue(223, &hx2);
+				Dlg->GetValue(226, &hy1);				Dlg->GetValue(228, &hy2);
+				}
+			else {
+				Dlg->GetValue(221, &vx1);				Dlg->GetValue(223, &vx2);
+				Dlg->GetValue(226, &vy1);				Dlg->GetValue(228, &vy2);
+				}
+			if(res > 204) {
+				Dlg->SetValue(221, hx1);				Dlg->SetValue(223, hx2);
+				Dlg->SetValue(226, hy1);				Dlg->SetValue(228, hy2);
+				if(! bAxis) {
+					Dlg->SetValue(101, x_axis.min);		Dlg->SetValue(103, x_axis.max);
+					}
+				}
+			else {
+				Dlg->SetValue(221, vx1);				Dlg->SetValue(223, vx2);
+				Dlg->SetValue(226, vy1);				Dlg->SetValue(228, vy2);
+				if(! bAxis) {
+					Dlg->SetValue(101, y_axis.min);		Dlg->SetValue(103, y_axis.max);
+					}
+				}
+			currTempl = res;
+			Dlg->DoPlot(0L);
+			res = -1;
+			break;
+			}
+		}while (res < 0);
+	if(res == 1) {
+		Undo.SetDisp(cdisp);
+		Dlg->GetValue(122, &sizAxLine);				Dlg->GetColor(125, &colAxis);
+		Dlg->GetValue(221, &axis.loc[0].fx);		Dlg->GetValue(223, &axis.loc[1].fx);
+		Dlg->GetValue(226, &axis.loc[0].fy);		Dlg->GetValue(228, &axis.loc[1].fy);
+		axis.loc[0].fz = axis.loc[1].fz = 0.0;		axis.owner = 0L;
+		Dlg->GetValue(101, &axis.min);				Dlg->GetValue(103, &axis.max);
+		axis.Start = axis.min;				axis.Center.fx = axis.Center.fy = 0.0;
+		axis.nBreaks = 0;			axis.breaks = 0L;		axis.Radius = 0.0;	
+		tlb_dist = NiceValue(DefSize(SIZE_AXIS_TICKS));
+		tlb_dx = tlb_dy = 0.0;
+		tlbdef.ColTxt = colAxis;				tlbdef.ColBg = 0x00ffffffL;
+		tlbdef.RotBL = tlbdef.RotCHAR = 0.0f;	tlbdef.fSize = DefSize(SIZE_TICK_LABELS);
+		tlbdef.Align = TXA_VCENTER | TXA_HCENTER;
+		tlbdef.Style = TXS_NORMAL;				tlbdef.Mode = TXM_TRANSPARENT;
+		tlbdef.Font = FONT_HELVETICA;			tlbdef.text = 0L;
+		label_def.ColTxt = colAxis;				label_def.ColBg = 0x00ffffffL;
+		label_def.fSize = DefSize(SIZE_TICK_LABELS)*1.2f;	label_def.RotBL = 0.0f;
+		label_def.RotCHAR = 0.0f;								label_def.iSize = 0;
+		label_def.Align = TXA_VTOP | TXA_HCENTER;		label_def.Mode = TXM_TRANSPARENT;
+		label_def.Style = TXS_NORMAL;	label_def.Font = FONT_HELVETICA;
+		switch (currTempl) {
+		default:	flags = AXIS_NEGTICKS;	axis.Step = y_axis.Step;
+			tlb_dx = -tlb_dist * 2.0;	tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
+			lb_x = -tlb_dist * 6.0;		lb_y = 0;	label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
+			label_def.RotBL = 90.0;
+			break;
+		case 202:	flags = AXIS_POSTICKS;	axis.Step = y_axis.Step;
+			tlb_dx = -tlb_dist;			tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
+			lb_x = -tlb_dist * 4.5;		lb_y = 0;	label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
+			label_def.RotBL = 90.0;
+			break;
+		case 203:	flags = AXIS_POSTICKS;	axis.Step = y_axis.Step;
+			tlb_dx = tlb_dist * 2.0;	tlbdef.Align = TXA_VCENTER | TXA_HLEFT;
+			lb_x = tlb_dist * 6;		lb_y = 0;	label_def.Align = TXA_VTOP | TXA_HCENTER;
+			label_def.RotBL = 90.0;
+			break;
+		case 204:	flags = AXIS_NEGTICKS;	axis.Step = y_axis.Step;
+			tlb_dx = tlb_dist;			tlbdef.Align = TXA_VCENTER | TXA_HLEFT;
+			lb_x = tlb_dist * 4.5;		lb_y = 0;	label_def.Align = TXA_VTOP | TXA_HCENTER;
+			label_def.RotBL = 90.0;
+			break;
+		case 205:	flags = AXIS_NEGTICKS;	axis.Step = x_axis.Step;
+			tlb_dy = tlb_dist * 2.0;	tlbdef.Align = TXA_VTOP | TXA_HCENTER;
+			lb_x = 0;					lb_y = tlb_dist * 4.0;
+			break;
+		case 206:	flags = AXIS_POSTICKS;	axis.Step = x_axis.Step;
+			tlb_dy = tlb_dist;			tlbdef.Align = TXA_VTOP | TXA_HCENTER;
+			lb_x = 0;					lb_y = tlb_dist * 3.0;
+			break;
+		case 207:	flags = AXIS_POSTICKS;	axis.Step = x_axis.Step;
+			tlb_dy = -tlb_dist * 2.0;	tlbdef.Align = TXA_VBOTTOM | TXA_HCENTER;
+			lb_x = 0;	lb_y = -tlb_dist * 4.0;	label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
+			break;
+		case 208:	flags = AXIS_NEGTICKS;	axis.Step = x_axis.Step;
+			tlb_dy = -tlb_dist;			tlbdef.Align = TXA_VBOTTOM | TXA_HCENTER;
+			lb_x = 0;	lb_y = -tlb_dist * 3.0;	label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
+			break;
+			}
+		flags |= AXIS_AUTOTICK;			flags |= AXIS_DEFRECT;
+		if(Dlg->GetCheck(51)) flags |= AXIS_AUTOSCALE;
+		if(the_new = new Axis(this, data, &axis, flags)){
+			the_new->SetSize(SIZE_TLB_YDIST, tlb_dy);	the_new->SetSize(SIZE_TLB_XDIST, tlb_dx); 
+			the_new->SetSize(SIZE_AXIS_LINE, sizAxLine);
+			the_new->SetColor(COL_AXIS, colAxis);
+			the_new->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			the_new->SetSize(SIZE_LB_XDIST, lb_x);		the_new->SetSize(SIZE_LB_YDIST, lb_y); 
+			if(Dlg->GetText(131, TmpTxt, TMP_TXT_SIZE)) label_def.text = TmpTxt;
+			else label_def.text = 0L;
+			if(label = new Label(Axes[0], data, (axis.loc[0].fx + axis.loc[1].fx)/2.0,
+				(axis.loc[0].fy + axis.loc[1].fy)/2.0, &label_def, 
+				label_def.RotBL < 45.0 ? LB_Y_PARENT : LB_X_PARENT)){
+				label->SetSize(SIZE_LB_XDIST, lb_x);	label->SetSize(SIZE_LB_YDIST, lb_y); 
+				if(the_new->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
+				else DeleteGO(label);
+				}
+			for(i = 0; i < NumAxes && Axes[i]; i++);
+			if(i < NumAxes) {
+				Undo.SetGO(this, (GraphObj**)(&Axes[i]), the_new, 0L);
+				bRet = true;
+				}
+			else {
+				if(tmpAxes = (Axis**)calloc(NumAxes+1, sizeof(Axis*))){
+					memcpy(tmpAxes, Axes, NumAxes * sizeof(Axis*));
+					Undo.ListGOmoved((GraphObj**)Axes, (GraphObj**)tmpAxes, NumAxes);
+					Undo.SetGO(this, (GraphObj**)(&tmpAxes[NumAxes]), the_new, 0L);
+					free(Axes);			Axes = tmpAxes;
+					i = NumAxes++;		bRet = true;
+					}
+				else delete (the_new);	//very unlikely memory allocation error
+				}
+			CurrAxes = Axes;
+			if(bRet) {
+				OD_axisplot(OD_ACCEPT, 0L, 0L, (anyOutput*) &res, 0L, 0);
+				if(res && i) somePlots[res]->Command(CMD_USEAXIS, &i, 0L);
+				}
+			}
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;			free(NewAxisDlg);
+	if(names) {
+		for(j = 0; names[j]; j++) if(names[j]) free(names[j]);
+		free(names);
+		}
+	if(somePlots) free(somePlots);
+	return bRet;
+}
+
+bool
+Page::Configure()
+{
+	TabSHEET tab1 = {0, 50, 10, "Paper Size"};
+	DlgInfo PageDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 135, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 135, 25, 45, 12},
+		{3, 0, 4, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 120, 120},
+		{5, 0, 0, 0x0L, PUSHBUTTON, (void*)"Layers", 135, 118, 45, 12},
+		{100, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_paperdef, 15, 30, 110, 140}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, cb;
+	bool bRet = false, bContinue = false;
+
+	FindPaper(GRect.Xmax - GRect.Xmin, GRect.Ymax -GRect.Ymin, .0001);
+	if(!(Dlg = new DlgRoot(PageDlg, data)))return false;
+	if(name)cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, name);
+	else cb = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Page");
+	rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE - cb, " properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 380, 300, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 0:
+			if(bContinue) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 5:
+			Command(CMD_LAYERS, 0L, 0L);
+			bContinue = true;
+			res = -1;
+			}
+		}while(res <0);
+	if(res == 1) {
+		OD_paperdef(OD_ACCEPT, 0L, 0L, 0L, 0L, 0);
+		GRect.Xmin = GRect.Ymin = 0.0;
+		GetPaper(&GRect.Xmax, &GRect.Ymax);
+		DoPlot(CurrDisp);
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Edit global defaults
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Default::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 22, 10, "Line"};
+	TabSHEET tab2 = {22, 52, 10, "Shapes"};
+	TabSHEET tab3 = {52, 82, 10, "Dialogs"};
+	TabSHEET tab4 = {82, 116, 10, "Internat."};
+	TabSHEET tab5 = {116, 155, 10, "Date/Time"};
+	double ts =  dlgtxtheight;
+	time_t ti = time(0L);
+	char dt_info[50], date_info[50], datetime_info[50], time_info[50];
+	DlgInfo DefsDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 180, 10, 40, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 180, 25, 40, 12},
+		{3, 0, 4, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{4, 5, 100, TOUCHEXIT | ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 170, 130},
+		{5, 6, 200, TOUCHEXIT | ISPARENT, SHEET, &tab2, 5, 10, 170, 130},
+		{6, 7, 300, TOUCHEXIT | ISPARENT, SHEET, &tab3, 5, 10, 170, 130},
+		{7, 8, 400, TOUCHEXIT | ISPARENT, SHEET, &tab4, 5, 10, 170, 130},
+		{8, 0, 350, TOUCHEXIT | ISPARENT, SHEET, &tab5, 5, 10, 170, 130},
+		{100, 0, 0, NOSELECT, ODBUTTON, (void*)OD_linedef, 10, 38, 130, 100},
+		{200, 0, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 25, 40, 90, 50},
+		{300, 301, 0, 0x0L, RTEXT, (void*)"text size", 20, 30, 38, 8},
+		{301, 302, 0, 0x0L, EDVAL1, (void*)&ts, 60, 30, 20, 10},
+		{302, 0, 0, 0x0L, LTEXT, (void*)"pixel", 82, 30, 20, 8},
+		{350, 351, 0, 0x0L, LTEXT, (void*)dt_info, 10, 30, 120, 8},
+		{351, 352, 0, 0x0L, LTEXT, (void*)"date format:", 10, 43, 70, 8},
+		{352, 353, 0, 0x0L, EDTEXT, (void*)defs.fmt_date, 10, 53, 60, 10},
+		{353, 354, 0, 0x0L, LTEXT, (void*)date_info, 80, 54, 40, 10}, 
+		{354, 355, 0, 0x0L, LTEXT, (void*)"date + time format:", 10, 65, 70, 8},
+		{355, 356, 0, 0x0L, EDTEXT, (void*)defs.fmt_datetime, 10, 75, 60, 10},
+		{356, 357, 0, 0x0L, LTEXT, (void*)datetime_info, 80, 76, 40, 10}, 
+		{357, 358, 0, 0x0L, LTEXT, (void*)"time format:", 10, 87, 70, 8},
+		{358, 359, 0, 0x0L, EDTEXT, (void*)defs.fmt_time, 10, 97, 60, 10},
+		{359, 360, 0, 0x0L, LTEXT, (void*)time_info, 80, 98, 40, 10}, 
+		{360, 361, 0, 0x0L, LTEXT, (void*)"For further information about formats see", 10, 119, 140, 8},
+		{361, 362, 0, HREF | TOUCHEXIT, LTEXT, (void*)"http://rlplot.sourceforge.net/Docs/functions/datetime.html", 10, 127, 140, 8},
+		{362, 0, 0, 0x0L, PUSHBUTTON, (void*)"Test", 130, 107, 40, 12},
+		{400, 401, 0, 0x0L, LTEXT, (void*)"edit country specific information", 20, 30, 100, 8},
+		{401, 402, 0, 0x0L, RTEXT, (void*)"decimal point", 45, 45, 40, 8},
+		{402, 403, 0, 0x0L, EDTEXT, (void*)DecPoint, 90, 45, 10, 10},
+		{403, 404, 0, 0x0L, RTEXT, (void*)"column separator", 45, 57, 40, 8},
+		{404, 405, 0, 0x0L, EDTEXT, (void*)ColSep, 90, 57, 10, 10},
+		{405, 406, 0, 0x0L, RTEXT, (void*)"use units:", 25, 75, 47, 8},
+		{406, 0, 420, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{420, 421, 0, 0x0L, RADIO1, (void*)Units[0].display, 75, 75, 20, 8},
+		{421, 422, 0, 0x0L, RADIO1, (void*)Units[1].display, 75, 83, 20, 8},
+		{422, 0, 0, LASTOBJ, RADIO1, (void*)Units[2].display, 75, 91, 20, 8}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, cb, res, tmpUnits = cUnits;
+	bool bRet = false, bContinue = false;
+	double dt;
+	LineDEF LineDef;
+	FillDEF FillDef;
+
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)GetLine(), 0);
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)GetOutLine(), 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)GetFill(), 0);
+	cb = rlp_strcpy(dt_info, 20, "today is ");
+#ifdef USE_WIN_SECURE
+	ctime_s(dt_info+cb, 50-cb, &ti);
+#else
+	rlp_strcpy(dt_info+cb, 50-cb, ctime(&ti));
+#endif
+	dt_info[cb+24] = 0;
+	date_value(dt_info+13, "x z H:M:S Y", &dt);
+	rlp_strcpy(date_info, 50, value_date(dt, defs.fmt_date));
+	rlp_strcpy(datetime_info, 50, value_date(dt, defs.fmt_datetime));
+	rlp_strcpy(time_info, 50, value_date(dt, defs.fmt_time));
+	Dlg = new DlgRoot(DefsDlg, 0L);
+	switch(dUnits) {
+	case 1:		Dlg->SetCheck(421, 0L, true);	break;
+	case 2:		Dlg->SetCheck(422, 0L, true);	break;
+	default:	Dlg->SetCheck(420, 0L, true);	break;
+		}
+#ifdef _WINDOWS
+	for(i = 360; i <= 361; i++) Dlg->TextSize(i, 12);
+#else
+	for(i = 360; i <= 361; i++) Dlg->TextSize(i, 10);
+#endif
+	hDlg = CreateDlgWnd("Edit Global Preferences", 50, 50, 460, 316, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 0:
+			if(bContinue) res = -1;
+			break;
+		case 362:				//update date/time display
+			ti = time(0L);		cb = rlp_strcpy(dt_info, 20, "today is ");
+#ifdef USE_WIN_SECURE
+			ctime_s(dt_info+cb, 50-cb, &ti);
+#else
+			rlp_strcpy(dt_info+cb, 50-cb, ctime(&ti));
+#endif
+			dt_info[cb+24] = 0;												Dlg->SetText(350, dt_info);
+			date_value(dt_info+13, "x z H:M:S Y", &dt);
+			if(!(Dlg->GetText(352, date_info, 50))) rlp_strcpy(date_info, 50, defs.fmt_date);
+			rlp_strcpy(date_info, 50, value_date(dt, date_info));			Dlg->SetText(353, date_info);
+			if(!(Dlg->GetText(355, datetime_info, 50))) rlp_strcpy(datetime_info, 50, defs.fmt_datetime);
+			rlp_strcpy(datetime_info, 50, value_date(dt, datetime_info));	Dlg->SetText(356, datetime_info);
+			if(!(Dlg->GetText(358, time_info, 50))) rlp_strcpy(time_info, 50, defs.fmt_time);
+			rlp_strcpy(time_info, 50, value_date(dt, time_info));			Dlg->SetText(359, time_info);
+			bContinue = false;		res = -1;
+			break;
+		case 361:			//call browser
+			bContinue = true;		res = -1;
+			break;
+		case 4:		case 5:		case 6:		case 7:
+			bContinue = false;		res = -1;
+			break;
+		case 8:
+			bContinue = true;		res = -1;
+			break;
+		case 1:
+			if(Dlg->GetCheck(421)) dUnits = 1;
+			else if(Dlg->GetCheck(422)) dUnits = 2;
+			else dUnits = 0;
+			}
+		}while (res < 0);
+	if(res == 1) {
+		if(Dlg->GetText(402, TmpTxt, TMP_TXT_SIZE))	DecPoint[0] = TmpTxt[0];
+		if(Dlg->GetText(404, TmpTxt, TMP_TXT_SIZE))	ColSep[0] = TmpTxt[0];
+		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
+		SetLine(tmpUnits, &LineDef, 0);
+		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
+		SetLine(tmpUnits, &LineDef, 2);
+		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&FillDef, 0);
+		SetFill(tmpUnits, &FillDef);
+		Dlg->GetInt(301, &dlgtxtheight);
+		if(Dlg->GetText(352, date_info, 50) && date_info[0] && strcmp(date_info, defs.fmt_date)) {
+			if(defs.fmt_date = (char*)realloc(defs.fmt_date, (cb = (int)strlen(date_info) +2)))
+				rlp_strcpy(defs.fmt_date, cb, date_info);
+			}
+		if(Dlg->GetText(355, datetime_info, 50) && datetime_info[0] && strcmp(datetime_info, defs.fmt_datetime)) {
+			if(defs.fmt_datetime = (char*)realloc(defs.fmt_datetime, (cb = (int)strlen(datetime_info) +2)))
+				rlp_strcpy(defs.fmt_datetime, cb, datetime_info);
+			}
+		if(Dlg->GetText(358, time_info, 50) && time_info[0] && strcmp(time_info, defs.fmt_time)) {
+			if(defs.fmt_time = (char*)realloc(defs.fmt_time, (cb = (int)strlen(time_info) +2)))
+				rlp_strcpy(defs.fmt_time, cb, time_info);
+			}
+		bRet = true;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
diff --git a/QT3_Spec.h b/QT3_Spec.h
index 8dec2a6..8ac0efa 100755
--- a/QT3_Spec.h
+++ b/QT3_Spec.h
@@ -1,4 +1,4 @@
-//QT_Spec.h, Copyright (c) 2001-2007 R.Lackner
+//QT_Spec.h, Copyright (c) 2001-2008 R.Lackner
 //
 //    This file is part of RLPlot.
 //
@@ -81,10 +81,11 @@ private:
 class RLPserver {
 public:
 	GraphObj *SourceGO;
+	bool bValid;
 
 	RLPserver(QObject* parent=0, GraphObj *g=0);
 	~RLPserver();
-
+	void CreateThread();
 	void SetGO(GraphObj *g);
 	char *GetXML();
 	char *GetRLP();
@@ -145,6 +146,10 @@ public slots:
 	void cmZoomFit(){ProcMenuEvent(CM_ZOOMFIT, this, OutputClass, BaseObj);};
 	void cmPaste();
 	void cmUndo(){if(BaseObj) BaseObj->Command(CMD_UNDO, 0L, OutputClass);};
+	void cmSave(){ProcMenuEvent(CM_SAVE, this, OutputClass, BaseObj);};
+	void cmOpen(){ProcMenuEvent(CM_OPEN, this, OutputClass, BaseObj);};
+	void cmPrint(){ProcMenuEvent(CM_PRINT, this, OutputClass, BaseObj);};
+	void cmNewInst(){ProcMenuEvent(CM_NEWINST, this, OutputClass, BaseObj);};
 
 protected:
 	void paintEvent(QPaintEvent *);
@@ -170,7 +175,7 @@ public:
 	QPixmap *mempic;
 	anyOutput *OutputClass;
 
-	DlgWidget(QWidget *par=0, const char *name=0, tag_DlgObj *d = 0);
+	DlgWidget(QWidget *par=0, const char *name=0, tag_DlgObj *d = 0, DWORD flags = 0L);
 	~DlgWidget();
 
 protected:
@@ -238,13 +243,15 @@ public:
 	~OutputQT();
 	bool ActualSize(RECT *rc);
 	void Focus(){if(widget){widget->show(); widget->raise();}};
-	void Caption(char *txt);
+	void Caption(char *txt, bool bModified);
 	void MouseCursor(int cid, bool force);
 	bool SetScroll(bool isVert, int iMin, int iMax, int iPSize, int iPos);
 	bool EndPage();
+	void MouseCapture(bool bgrab);
 	bool UpdateRect(RECT *rc, bool invert);
 	void ShowLine(POINT * pts, int cp, DWORD color);
-	void ShowEllipse(POINT p1, POINT p2, DWORD color); 
+	void ShowEllipse(POINT p1, POINT p2, DWORD color);
+	void ShowInvert(RECT *rec);
 	bool SetMenu(int type);
 	void CheckMenu(int mid, bool check);
 	void FileHistory();
diff --git a/QT_Spec.cpp b/QT_Spec.cpp
index 391157e..7595d5b 100755
--- a/QT_Spec.cpp
+++ b/QT_Spec.cpp
@@ -1,4 +1,4 @@
-//QT_Spec.cpp, Copyright (c) 2001-2007 R.Lackner
+//QT_Spec.cpp, Copyright (c) 2001-2008 R.Lackner
 //
 //    This file is part of RLPlot.
 //
@@ -26,6 +26,10 @@
 	#include "QT_Spec.h"
 #endif
 
+#ifndef __GCC__						//GCC version >= 3 required for threads: used for clipboard only
+	#define __GCC__ 3
+#endif
+
 extern tag_Units Units[];
 extern GraphObj *CurrGO;			//Selected Graphic Objects
 extern Graph *CurrGraph;
@@ -39,11 +43,18 @@ QApplication *QAppl;
 QWidget *MainWidget =0L, *CurrWidget = 0L;
 POINT CurrWidgetPos = {0,0};
 static int mouse_buttons_down = 0;
+static char *ShellCmd;
 
 #if QT_VERSION >= 0x040000
 static QIcon *rlp_icon = 0L;
 #endif
 
+#if QT_VERSION < 0x040000
+	#define MK_QCOLOR(col) QColor(col&0xff,(col>>8)&0xff,(col>>16)&0xff)
+#else
+	#define MK_QCOLOR(col) QColor(col&0xff,(col>>8)&0xff,(col>>16)&0xff,255-((col>>24)&0xff))
+#endif
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Export a QPixmap as supported by Qt
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -372,18 +383,6 @@ void Qt_Box()
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Swap red and blue in RGB value
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-DWORD SwapRB(DWORD col)
-{
-	DWORD nc = col & 0x0000ff00L;
-
-	nc |= (col>>16)&0x000000ffL;
-	nc |= (col<<16)&0x00ff0000L;
-	return nc;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Temporary visible objects: show action
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 class eph_obj {
@@ -466,7 +465,7 @@ eph_line::DoPlot(QPainter *qp)
 
 	if(next)next->DoPlot(qp);
 	if(!points || cp < 2) return;
-	qpen.setColor(SwapRB(lcolor));		qpen.setWidth(1);
+	qpen.setColor(MK_QCOLOR(lcolor));	qpen.setWidth(1);
 	qpen.setStyle(Qt::SolidLine);		qpen.setCapStyle(Qt::RoundCap);
 	qpen.setJoinStyle(Qt::RoundJoin);	qp->setPen(qpen);
 	for (i = 1; i < cp; i++)qp->drawLine(points[i-1].x, points[i-1].y, points[i].x, points[i].y);
@@ -490,7 +489,7 @@ eph_ellipse::DoPlot(QPainter *qp)
 	QPen qpen;
 
 	if(next)next->DoPlot(qp);
-	qpen.setColor(SwapRB(lcolor));		qpen.setWidth(1);
+	qpen.setColor(MK_QCOLOR(lcolor));	qpen.setWidth(1);
 	qpen.setStyle(Qt::SolidLine);		qpen.setCapStyle(Qt::RoundCap);
 	qpen.setJoinStyle(Qt::RoundJoin);	qp->setPen(qpen);
 	qp->drawArc(p1.x, p1.y, p2.x-p1.x, p2.y-p1.y, 0, 5760);
@@ -650,6 +649,7 @@ void ShowTextCursor(anyOutput *out, RECT *disp, DWORD color)
 {
 	LineDEF liTxtCur = {0.0, 1.0, 0x0L, 0L};
 
+	if(!out || (out->OC_type&0xff) != OC_BITMAP) return;
 	coTxtCur = color;				HideTextCursor();
 	oTxtCur = out;					bSuspend = false;
 	memcpy(&rTxtCur, disp, sizeof(RECT));		ptTxtCurLine[0].x = 0;
@@ -685,6 +685,7 @@ void ShowCopyMark(anyOutput *out, RECT *mrk, int nRec)
 {
 	int i;
 
+	if(!out || (out->OC_type&0xff) != OC_BITMAP) return;
 	HideCopyMark();			bSuspend = false;
 	if(!out || !mrk || !nRec || !cTxtCur) return;
 	if(((OutputQT*)out)->ShowAnimated) delete (eph_obj*)(((OutputQT*)oCopyMark)->ShowAnimated);
@@ -703,6 +704,7 @@ void ShowCopyMark(anyOutput *out, RECT *mrk, int nRec)
 
 void InvalidateOutput(anyOutput *o)
 {
+	if(!o || (o->OC_type&0xff) != OC_BITMAP) return;
 	if(!o || !cTxtCur) return;
 	if(o == oCopyMark) oCopyMark = 0L;
 	if(o == oTxtCur) {
@@ -712,6 +714,7 @@ void InvalidateOutput(anyOutput *o)
 
 void SuspendAnimation(anyOutput *o, bool bSusp)
 {
+	if(!o || (o->OC_type&0xff) != OC_BITMAP) return;
 	if(!o || !cTxtCur) return;
 	if(!bSusp) bSuspend = false;
 	else {
@@ -722,6 +725,7 @@ void SuspendAnimation(anyOutput *o, bool bSusp)
 
 static void showCopyMark()
 {
+	if(!oCopyMark || (oCopyMark->OC_type&0xff) != OC_BITMAP) return;
 	if(oCopyMark && ((OutputQT*)oCopyMark)->ShowAnimated) 
 		((eph_obj*)(((OutputQT*)oCopyMark)->ShowAnimated))->Animate(oCopyMark);
 
@@ -777,6 +781,7 @@ static RLPserver *rlpsrv = 0L;
 
 #ifdef RLP_PORT
 static char *cb_owner = 0L;		//user name
+#if __GCC__ >= 3
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // close remote clipboard server
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -793,17 +798,27 @@ bool CloseCB(char *msg)
 	memcpy(&cb_host.sin_addr, hp->h_addr, hp->h_length);
 	cb_host.sin_family = AF_INET;		cb_host.sin_port = htons(RLP_PORT);
 	if(connect(sock, (sockaddr*)&cb_host, sizeof(cb_host)) >= 0) {
-//		if(ioctl(sock, FIONREAD, &i) >= 0) {
-//			if((cb = read(sock, &buff, 1024) > 0) && cb < 1024) {
-//				if(strcmp("user:", buff) == 0) {
-//					write(sock, cb_owner, strlen(cb_owner)+1);
-//					ioctl(sock, BLKFLSBUF, &i);
-//					}
-//				}
-//			}
+		if(ioctl(sock, FIONREAD, &i) >= 0 && i > 0) {
+			if((cb = read(sock, &buff, 1024) > 0) && cb < 1024) {
+				if(strcmp("user:", buff) == 0) {
+					write(sock, cb_owner, strlen(cb_owner)+1);
+					}
+				else goto CloseCBerror;
+				}
+			else goto CloseCBerror;
+			if((cb = read(sock, &buff, 1024) > 0) && cb < 1024) {
+				if(strcmp("OK", buff) != 0) goto CloseCBerror;
+				}
+			else goto CloseCBerror;
+			}
+		else goto CloseCBerror;
 		write(sock, msg, strlen(msg)+1);
+		for(i = 0; i < 4; i++) {
+			if(ioctl(sock, TIOCOUTQ, &cb) <= 0 || cb <= 0) break;
+			}
 		close(sock);		return true;
 		}
+CloseCBerror:
 	close(sock);	return false;
 }
 
@@ -824,18 +839,30 @@ bool ReadCB(char *server, GraphObj *g)
 	memcpy(&cb_host.sin_addr, hp->h_addr, hp->h_length);
 	cb_host.sin_family = AF_INET;		cb_host.sin_port = htons(RLP_PORT);
 	if(connect(sock, (sockaddr*)&cb_host, sizeof(cb_host)) >= 0) { 
-//		if(ioctl(sock, FIONREAD, &i) >= 0) {
-//			if((cb = read(sock, &buff, 1024) > 0) && cb < 1024) {
-//				if(strcmp("user:", buff) == 0) {
-//					write(sock, cb_owner, strlen(cb_owner)+1);
-//					ioctl(sock, BLKFLSBUF, &i);
-//					}
-//				}
-//			}
+		if(ioctl(sock, FIONREAD, &i) >= 0 && i > 0) {
+			if((cb = read(sock, &buff, 1024) > 0) && cb < 1024) {
+				if(strcmp("user:", buff) == 0) {
+					write(sock, cb_owner, strlen(cb_owner)+1);
+					}
+				else return false;
+				}
+			else return false;
+			if((cb = read(sock, &buff, 1024) > 0) && cb < 1024) {
+				if(strcmp("OK", buff) != 0) {
+					close(sock);	return false;
+					}
+				}
+			else {
+				close(sock);		return false;
+				}
+			}
+		else {
+			close(sock);			return false;
+			}
 		if(!(write(sock, "enum formats\n", 14) > 0)) {
-			close(sock);	return false;
+			close(sock);			return false;
 			}
-		if((cb = read(sock, &buff, 1024)) && cb < 1024) {
+		if((cb = read(sock, &buff, 1024)) >= 0 && cb < 1024) {
 			for(i = 0, format[0] = 0; i < cb; ) {
 				for (j = 0; j < 20 && i < cb; j++) {
 					if((line[j] = buff[i++]) < 32) break;
@@ -901,18 +928,23 @@ static void *ServerCB(void* attr)
 	bool bValid;
 
 	if(!parent) return 0L;
+	parent->bValid = true;
 	for( ; ; ) {
 		bValid = false;
 		if((msgsock = accept(parent->Socket(), (sockaddr*)&client, &clientlen)) >= 0){
-//			if(write(msgsock, "user:", 6) > 0) {
-//				if((cb = read(msgsock, &buff, 1024) > 0) && cb < 1024){
-//					if(strcmp(buff, cb_owner) == 0) bValid = true;
-//					}
-//				}
-
-			bValid = true;			//FIXME
-
-			while(bValid && (cb = read(msgsock, &buff, 1024) > 0) && cb < 1024) {
+			if(write(msgsock, "user:", 6) > 0) {
+				if((cb = read(msgsock, &buff, 1024) > 0) && cb < 1024){
+					if(strcmp(buff, cb_owner) == 0) {
+						bValid = true;
+						write(msgsock, "OK\0", 3);
+						}
+					else {
+						write(msgsock, "NO\0", 3);
+						close(msgsock);
+						}
+					}
+				}
+			while(bValid && parent && (cb = read(msgsock, &buff, 1024) > 0) && cb < 1024) {
 				if(strcmp(buff, "enum formats\n") == 0 && parent->SourceGO) {
 					if(parent->SourceGO->Id == GO_SPREADDATA) {
 						write(msgsock, fmts_1, strlen(fmts_1)+1);
@@ -940,7 +972,9 @@ static void *ServerCB(void* attr)
 					}
 				else if(strcmp(buff, "exit\n") == 0 || strcmp(buff, "close\n") == 0
 					|| strcmp(buff, "exit") == 0 || strcmp(buff, "close") == 0) {
-					delete(parent);		return 0L;
+					HideCopyMark();		parent->bValid = false;
+					if(parent->SourceGO) parent->SourceGO->Command(CMD_HIDEMARK, 0L, 0L);
+					return 0L;
 					}
 				else if(strcmp(buff, "exit_thread\n") == 0) {
 					return 0L;
@@ -957,29 +991,13 @@ static void *ServerCB(void* attr)
 }
 // end of thread
 #endif	//RLP_PORT
+#endif	//__GCC__
 
 RLPserver::RLPserver(QObject* parent, GraphObj *g)
 {
+	bValid = false;
 	text_xml = text_rlp = text_plain = 0L;
-	SetGO(g);
-#ifdef RLP_PORT
-	int i;
-	sockaddr_in server, client;
-	socklen_t clientlen = sizeof(client);
-
-	if((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) return;
-	i = 1;
-	setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(int));
-	if(fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) & ~O_NONBLOCK) < 0);
-	memset(&server, 0, sizeof(server));
-	server.sin_family = AF_UNIX;
-	server.sin_addr.s_addr = INADDR_ANY;
-	server.sin_port = htons(RLP_PORT);
-	if(bind(sock, (struct sockaddr*)&server, sizeof(server)) < 0) return;
-	if(listen(sock, 5) < 0) return;
-	pthread_attr_init(&thread_attr);
-	pthread_create(&thread, &thread_attr, ServerCB, this);
-#endif
+	SetGO(g);	CreateThread();
 }
 
 RLPserver::~RLPserver()
@@ -990,11 +1008,40 @@ RLPserver::~RLPserver()
 	if(text_rlp) free(text_rlp);		text_rlp =  0L;
 	if(text_plain) free(text_plain);	text_plain = 0L;
 	if(SourceGO) SourceGO->Command(CMD_HIDEMARK, 0L, 0L);
+#if __GCC__ >= 3
 #ifdef RLP_PORT
 	CloseCB("exit_thread\n");		close(sock);
 	pthread_attr_destroy(&thread_attr);
 
-#endif
+#endif	//RLP_PORT
+#endif	//__GCC__
+}
+
+void 
+RLPserver::CreateThread()
+{
+#if __GCC__ >= 3
+#ifdef RLP_PORT
+	int i;
+	sockaddr_in server, client;
+	socklen_t clientlen = sizeof(client);
+
+	if(!bValid) {
+		if((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) return;
+		i = 1;
+//		setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &i, sizeof(int));
+		if(fcntl(sock, F_SETFL, fcntl(sock, F_GETFL, 0) & ~O_NONBLOCK) < 0);
+		memset(&server, 0, sizeof(server));
+		server.sin_family = AF_UNIX;
+		server.sin_addr.s_addr = INADDR_ANY;
+		server.sin_port = htons(RLP_PORT);
+		if(bind(sock, (struct sockaddr*)&server, sizeof(server)) < 0) return;
+		if(listen(sock, 5) < 0) return;
+		pthread_attr_init(&thread_attr);
+		pthread_create(&thread, &thread_attr, ServerCB, this);
+		}
+#endif	//RLP_PORT
+#endif	//GCC
 }
 
 void 
@@ -1019,6 +1066,7 @@ RLPserver::SetGO(GraphObj *g)
 #endif
 			}
 		}
+	if(!bValid) CreateThread();
 }
 
 char *
@@ -1057,12 +1105,16 @@ void TestClipboard(GraphObj *g)
 			|| rlpsrv->SourceGO->Id == GO_ELLIPSE || rlpsrv->SourceGO->Id == GO_BEZIER
 			)
 			OpenGraph(g, 0L, (unsigned char*)rlpsrv->GetRLP(), true);
-		else if(rlpsrv->SourceGO->Id == GO_SPREADDATA && g->Id == GO_SPREADDATA)
+		else if(rlpsrv->SourceGO->Id == GO_SPREADDATA && g->Id == GO_SPREADDATA){
 			g->Command(CMD_PASTE_XML, (void*)rlpsrv->GetXML(), 0L);
+			return;
+			}
 		}
+#if __GCC__ >= 3
 #ifdef RLP_PORT
 	else if(ReadCB(0L, g)) return;
 #endif
+#endif
 #if QT_VERSION < 0x040000
 	QDragObject *mime;
 
@@ -1095,13 +1147,7 @@ void TestClipboard(GraphObj *g)
 void CopyData(GraphObj *g)
 {
 	QAppl->clipboard()->clear();
-	if(rlpsrv) {
-		if(rlpsrv->ok()) rlpsrv->SetGO(g);
-		else {
-			delete rlpsrv;
-			rlpsrv = new RLPserver(0, g);
-			}
-		}
+	if(rlpsrv) rlpsrv->SetGO(g);
 	else rlpsrv = new RLPserver(0, g);
 }
 
@@ -1113,19 +1159,24 @@ void CopyGraph(GraphObj *g)
 
 void EmptyClip()
 {
-	if(rlpsrv) {
-		delete(rlpsrv);			rlpsrv = 0;
+#if __GCC__ >= 3
+#ifdef RLP_PORT
+	CloseCB("close\n");
+#endif	//RLP_PORT
+#endif	//GCC
+	if(rlpsrv && !rlpsrv->bValid) {
+		delete(rlpsrv);			rlpsrv = 0L;
 		}
-	else CloseCB("close\n");
 	HideCopyMark();
 	QAppl->clipboard()->clear();
 }
 
 void CopyText(char *txt, int len)
 {
+
 	QClipboard *cb;
 
-	HideCopyMark();
+	EmptyClip();
 	if(!(cb = QAppl->clipboard()) || !txt || !txt[0]) return;
 	cb->clear();
 	cb->setText(txt);
@@ -1216,18 +1267,18 @@ bool com_QStringOut(int x, int y, QString txt, TextDEF *TxtSet, QPainter *qP, an
 	if(TxtSet->Align & TXA_HCENTER) ix = x - (w >> 1);
 	else if(TxtSet->Align & TXA_HRIGHT) ix = x - w;
 	else ix = x;
-	currPen.setColor(SwapRB(TxtSet->ColTxt));
+	currPen.setColor(MK_QCOLOR(TxtSet->ColTxt));
 	qP->setPen(currPen);
 	if(fabs(TxtSet->RotBL) >.01 || fabs(TxtSet->RotCHAR) >.01) {
 		xf.translate(x, y);
 		xf.rotate(-TxtSet->RotBL);
 		qP->setWorldMatrix(xf, TRUE);
 		if(TxtSet->Mode == TXM_OPAQUE){
-			currPen.setColor(SwapRB(TxtSet->ColBg));
+			currPen.setColor(MK_QCOLOR(TxtSet->ColBg));
 			qP->setPen(currPen);
-			qP->setBrush(QColor(SwapRB(TxtSet->ColBg)));
+			qP->setBrush(MK_QCOLOR(TxtSet->ColBg));
 			qP->drawRect(0, - iround(h*.8), w, h);
-			currPen.setColor(SwapRB(TxtSet->ColTxt));
+			currPen.setColor(MK_QCOLOR(TxtSet->ColTxt));
 			qP->setPen(currPen);
 			}
 		if(TxtSet->Style & TXS_SUB) iy += ((TxtSet->iSize <<2)/10);
@@ -1236,11 +1287,11 @@ bool com_QStringOut(int x, int y, QString txt, TextDEF *TxtSet, QPainter *qP, an
 		}
 	else {
 		if(TxtSet->Mode == TXM_OPAQUE){
-			currPen.setColor(SwapRB(TxtSet->ColBg));
+			currPen.setColor(MK_QCOLOR(TxtSet->ColBg));
 			qP->setPen(currPen);
-			qP->setBrush(QColor(SwapRB(TxtSet->ColBg)));
+			qP->setBrush(MK_QCOLOR(TxtSet->ColBg));
 			qP->drawRect(ix, iy - iround(h*.8), w, h);
-			currPen.setColor(SwapRB(TxtSet->ColTxt));
+			currPen.setColor(MK_QCOLOR(TxtSet->ColTxt));
 			qP->setPen(currPen);
 			}
 		if(TxtSet->Style & TXS_SUB) iy += o->un2iy(TxtSet->fSize*0.4);
@@ -1524,7 +1575,8 @@ BitMapQT::BitMapQT(GraphObj *g, QMainWindow *wi, int vr, int hr):anyOutput()
 	hres = (double)hr;		vres = (double)vr;
 	image = 0L;			hgo = 0L;
 	dlgwidget = 0L;			widget = wi;
-	ShowObj = ShowAnimated = 0L;
+	ShowObj = ShowAnimated = 0L;	minLW = 1;
+	OC_type = OC_BITMAP;
 	if(wi) {
 		w = wi->width();		h = wi->height();
 		}
@@ -1553,7 +1605,8 @@ BitMapQT::BitMapQT(GraphObj *g, QWidget *wi, int vr, int hr):anyOutput()
 	hres = (double)hr;		vres = (double)vr;
 	image = 0L;			hgo = 0L;			
 	dlgwidget = wi;			widget = 0L;
-	ShowObj = ShowAnimated = 0L;
+	ShowObj = ShowAnimated = 0L;	minLW = 1;
+	OC_type = OC_BITMAP;
 	if(wi) {
 		w = wi->width();		h = wi->height();
 		}
@@ -1580,10 +1633,10 @@ BitMapQT::BitMapQT(int w, int h, double hr, double vr):anyOutput()
 	hres = hr;		vres = vr;
 	image = 0L;		hgo = 0L;			widget = 0L;
 	w = abs(w);		h = abs(h);			ShowObj = ShowAnimated = 0L;
-	Box1.Xmin = Box1.Ymin = 0.0;
+	Box1.Xmin = Box1.Ymin = 0.0;			minLW = 1;
 	Box1.Xmax = w;					Box1.Ymax = h;
 	DeskRect.right = w;				DeskRect.bottom = h;
-	DeskRect.left = DeskRect.top = 0;
+	DeskRect.left = DeskRect.top = 0;		OC_type = OC_BITMAP;
 	mempic = new QPixmap(w, h);
 	mempic->fill(0x00ffffffL);
 	qPainter.begin(mempic);
@@ -1619,10 +1672,10 @@ BitMapQT::SetLine(LineDEF *lDef)
 		RLP.finc = 256.0/un2fix(lDef->patlength*8.0);
 		RLP.fp = 0.0;
 		if(iLine == iw && dLineCol == lDef->color) return true;
-		iLine = iw;
+		iLine = iw > minLW ? iw : minLW;
 		dLineCol = lDef->color;
-		qPen.setColor(SwapRB(dLineCol));
-		qPen.setWidth(iw);
+		qPen.setColor(MK_QCOLOR(dLineCol));
+		qPen.setWidth(iLine);
 		qPen.setStyle(Qt::SolidLine);
 		qPen.setCapStyle(Qt::RoundCap);
 		qPen.setJoinStyle(Qt::RoundJoin);
@@ -1643,12 +1696,10 @@ BitMapQT::SetFill(FillDEF *fill)
 		if(hgo) hgo->SetFill(fill);
 		}
 	else {
-		if(hgo) delete hgo;
-		hgo = 0L;
+		if(hgo) delete hgo;		hgo = 0L;
 		}
-	qPainter.setBrush(QColor(SwapRB(fill->color)));
-	dFillCol = fill->color;
-	dFillCol2 = fill->color2;
+	qPainter.setBrush(MK_QCOLOR(fill->color));
+	dFillCol = fill->color;			dFillCol2 = fill->color2;
 	return true;
 }
 
@@ -1713,10 +1764,9 @@ BitMapQT::oGetPix(int x, int y, DWORD *col)
 #else
 	if(!image && !(image = new QImage(mempic->toImage())))return false;
 #endif
-	if(x >= DeskRect.left && x < DeskRect.right &&
-		y >= DeskRect.top && y < DeskRect.bottom){
-		pix = SwapRB(image->pixel(x, y));
-		*col = pix;
+	if(x >= DeskRect.left && x < DeskRect.right && y >= DeskRect.top && y < DeskRect.bottom){
+		pix = image->pixel(x,y);		*col = pix & 0x0000ff00L;
+		*col |= (pix>>16)&0x000000ffL;		*col |= (pix<<16)&0x00ff0000L;
 		return true;
 		}
 	return false;
@@ -1788,7 +1838,7 @@ BitMapQT::oRectangle(int x1, int y1, int x2, int y2, char *nam)
 #if QT_VERSION < 0x040000
 	qPainter.drawRect(x1, y1, x2-x1, y2-y1);
 #else
-	qPainter.drawRect(x1, y1, x2-x1-1, y2-y1-1);
+	qPainter.drawRect(x1, y1, x2-x1, y2-y1);
 #endif
 	if(hgo) hgo->oRectangle(x1, y1, x2, y2, 0L);
 	return true;
@@ -1863,10 +1913,10 @@ bool ProcMenuEvent(int id, QWidget *parent, anyOutput *OutputClass, GraphObj *Ba
 	if(BaseObj) switch(id) {
 	case CM_OPEN:
 		BaseObj->Command(CMD_OPEN, 0L, OutputClass);		return true;
-	case CM_SAVEDATA:
-		BaseObj->Command(CMD_SAVEDATA, 0L, OutputClass);	return true;
-	case CM_SAVEDATAAS:
-		BaseObj->Command(CMD_SAVEDATAAS, 0L, OutputClass);	return true;
+	case CM_SAVE:
+		BaseObj->Command(CMD_SAVE, 0L, OutputClass);	return true;
+	case CM_SAVEAS:
+		BaseObj->Command(CMD_SAVEAS, 0L, OutputClass);	return true;
 	case CM_INSROW:
 		BaseObj->Command(CMD_INSROW, 0L, OutputClass);		return true;
 	case CM_INSCOL:
@@ -1875,8 +1925,6 @@ bool ProcMenuEvent(int id, QWidget *parent, anyOutput *OutputClass, GraphObj *Ba
 		BaseObj->Command(CMD_DELROW, 0L, OutputClass);		return true;
 	case CM_DELCOL:
 		BaseObj->Command(CMD_DELCOL, 0L, OutputClass);		return true;
-	case CM_SAVEGRAPHAS:
-		SaveGraphAs(BaseObj);					return true;
 	case CM_EXPORT:
 		if(OutputClass) {
 			OutputClass->HideMark();	OpenExportName(BaseObj, 0L);
@@ -1936,6 +1984,8 @@ bool ProcMenuEvent(int id, QWidget *parent, anyOutput *OutputClass, GraphObj *Ba
 		BaseObj->Command(CMD_CONFIG, 0L, OutputClass);		return true;
 	case CM_REPANOV:
 		rep_anova(BaseObj, BaseObj->data);			return true;
+	case CM_REPBDANOV:
+		rep_bdanova(BaseObj, BaseObj->data);			return true;
 	case CM_REPKRUSKAL:
 		rep_kruskal(BaseObj, BaseObj->data);			return true;
 	case CM_REPTWANR:
@@ -1978,8 +2028,10 @@ bool ProcMenuEvent(int id, QWidget *parent, anyOutput *OutputClass, GraphObj *Ba
 			else if(BaseObj->Id == GO_SPREADDATA) QAppl->exit(0);
 			}
 		return true;
+	case CM_NEWINST:
+		if(ShellCmd && ShellCmd[0]) system(ShellCmd);
+		return true;
 	case CM_COPY:	case CM_CUT:	case CM_COPYGRAPH:
-		EmptyClip();
 		if(CurrGO && BaseObj->Id != GO_SPREADDATA) {
 			if(CurrGO->Id == GO_POLYLINE || CurrGO->Id == GO_POLYGON || CurrGO->Id == GO_RECTANGLE
 				|| CurrGO->Id == GO_ROUNDREC || CurrGO->Id == GO_ELLIPSE || CurrGO->Id == GO_BEZIER) {
@@ -2204,9 +2256,11 @@ OutputQT::ActualSize(RECT *rc)
 }
 
 void
-OutputQT::Caption(char *txt)
+OutputQT::Caption(char *txt, bool bModified)
 {
 	QString cap(txt);
+
+	if(bModified) cap.append(" [modified]");
 #if QT_VERSION < 0x040000
 	if(widget) widget->setCaption(cap);
 	else if(dlgwidget) dlgwidget->setCaption(cap);
@@ -2553,6 +2607,19 @@ OutputQT::EndPage()
 	return true;
 }
 
+void
+OutputQT::MouseCapture(bool bgrab)
+{
+#if QT_VERSION >= 0x040000 
+	if(!dlgwidget) return;
+	if(bgrab && dlgwidget->isModal()) return;
+	if(!bgrab && !dlgwidget->isModal()) return;
+	dlgwidget->hide();
+	dlgwidget->setWindowModality(bgrab ? Qt::ApplicationModal : Qt::NonModal);
+	dlgwidget->show();
+#endif
+}
+
 bool
 OutputQT::UpdateRect(RECT *rc, bool invert)
 {
@@ -2600,7 +2667,7 @@ OutputQT::ShowLine(POINT * pts, int cp, DWORD color)
 		}
 	rec.left -= 2;		rec.top -= 2;		rec.bottom += 2;	rec.right += 2;
 	if(rec.bottom < 2 && rec.top < 2) return;
-	ShowObj = new eph_line((eph_line*)ShowObj, pts, cp, color);
+	if(!(ShowObj = new eph_line((eph_line*)ShowObj, pts, cp, color)))return;
 #if QT_VERSION >= 0x040000
 	UpdateRect(&rec, false);
 #else
@@ -2617,7 +2684,7 @@ OutputQT::ShowEllipse(POINT p1, POINT p2, DWORD color)
 	rec.left = rec.right = p1.x;			rec.top = rec.bottom = p1.y;
 	UpdateMinMaxRect(&rec, p2.x, p2.y);		IncrementMinMaxRect(&rec, 2);
 	if(rec.bottom < 2 && rec.top < 2) return;
-	ShowObj = new eph_ellipse((eph_obj*)ShowObj, p1, p2, color);
+	if(!(ShowObj = new eph_ellipse((eph_obj*)ShowObj, p1, p2, color)))return;
 #if QT_VERSION >= 0x040000
 	UpdateRect(&rec, false);
 #else
@@ -2625,6 +2692,17 @@ OutputQT::ShowEllipse(POINT p1, POINT p2, DWORD color)
 	((eph_obj*)ShowObj)->DoPlot(&qp);
 #endif
 }
+void
+OutputQT::ShowInvert(RECT *rec)
+{
+	POINT spts[5];
+
+	spts[0].x = spts[4].x = spts[3].x = rec->left;
+	spts[0].y = spts[4].y = spts[1].y = rec->top;
+	spts[1].x = spts[2].x = rec->right;
+	spts[2].y = spts[3].y = rec->bottom;
+	ShowLine(spts, 5, 0x0);
+}
 
 #if QT_VERSION < 0x040000
 	#define RLP_MITEM(menu,par,o,go,txt,id) menu->insertItem(txt,id)
@@ -2699,11 +2777,24 @@ OutputQT::SetMenu(int type)
 		QMenu *graph = ((RLPwidget*)widget)->menu_bar->addMenu(widget->tr("&Graph"));
 		QMenu *about = ((RLPwidget*)widget)->menu_bar->addMenu(widget->tr("&?"));
 #endif
-		RLP_MITEM(file, widget, this, BaseObj, "&Open", CM_OPEN);
-		RLP_MITEM(file, widget, this, BaseObj, "&Save", CM_SAVEDATA);
-		RLP_MITEM(file, widget, this, BaseObj, "Save &as", CM_SAVEDATAAS);
+#if QT_VERSION < 0x040000
+		file->insertItem("&New Instance", widget, SLOT(cmNewInst()), Qt::CTRL + Qt::Key_N);
+		file->insertSeparator();
+		file->insertItem("&Open", widget, SLOT(cmOpen()), Qt::CTRL + Qt::Key_O);
+		file->insertItem("&Save", widget, SLOT(cmSave()), Qt::CTRL + Qt::Key_S);
+#else
+		file->addAction("&New Instance", widget, SLOT(cmNewInst()), Qt::CTRL + Qt::Key_N);
+		file->addSeparator();
+		file->addAction("&Open", widget, SLOT(cmOpen()), Qt::CTRL +Qt::Key_O);
+		file->addAction("&Save", widget, SLOT(cmSave()), Qt::CTRL +Qt::Key_S);
+#endif
+		RLP_MITEM(file, widget, this, BaseObj, "Save &as", CM_SAVEAS);
 		RLP_MSEPARATOR(file);
-		RLP_MITEM(file, widget, this, BaseObj, "&Print", CM_PRINT);
+#if QT_VERSION < 0x040000
+		file->insertItem("&Print", widget, SLOT(cmPrint()), Qt::CTRL + Qt::Key_P);
+#else
+		file->addAction("&Print", widget, SLOT(cmPrint()), Qt::CTRL + Qt::Key_P);
+#endif
 		RLP_MSEPARATOR(file);
 		RLP_MITEM(file, widget, this, BaseObj, "E&xit", CM_EXIT);
 		RLP_MSEPARATOR(file);
@@ -2769,6 +2860,9 @@ OutputQT::SetMenu(int type)
 		RLP_MITEM(corr, widget, this, BaseObj, "Correlation &Matrix", CM_CORRELM);
 		RLP_MITEM(corr, widget, this, BaseObj, "Tiled &Plots", CM_CORRELT);
 		stats->insertItem("C&orrelations", corr);
+		QPopupMenu *bdown = new QPopupMenu(widget);
+		RLP_MITEM(bdown, widget, this, BaseObj, "&One Way Anova", CM_REPBDANOV);
+		stats->insertItem("&Breakdowns", bdown);
 #else
 		QMenu *anov = stats->addMenu("&Anova");
 		RLP_MITEM(anov, widget, this, BaseObj, "&One Way Anova", CM_REPANOV);
@@ -2782,6 +2876,8 @@ OutputQT::SetMenu(int type)
 		QMenu *corr = stats->addMenu("C&orrelations");
 		RLP_MITEM(corr, widget, this, BaseObj, "Correlation &Matrix", CM_CORRELM);
 		RLP_MITEM(corr, widget, this, BaseObj, "Tiled &Plots", CM_CORRELT);
+		QMenu *bdown = stats->addMenu("&Breakdowns");
+		RLP_MITEM(bdown, widget, this, BaseObj, "&One Way Anova", CM_REPBDANOV);
 #endif
 		RLP_MITEM(stats, widget, this, BaseObj, "&2x2 Table", CM_REPTWOWAY);
 
@@ -2810,6 +2906,7 @@ OutputQT::SetMenu(int type)
 		QPopupMenu *displ = new QPopupMenu(widget);
 		QPopupMenu *plots = new QPopupMenu(widget);
 		QPopupMenu *about = new QPopupMenu(widget);
+		file->insertItem("&Open", widget, SLOT(cmOpen()), Qt::CTRL + Qt::Key_O);
 #else
 		QMenu *file = ((RLPwidget*)widget)->menu_bar->addMenu(widget->tr("&File"));
 		QMenu *edit = ((RLPwidget*)widget)->menu_bar->addMenu(widget->tr("&Edit"));
@@ -2817,11 +2914,15 @@ OutputQT::SetMenu(int type)
 		MkToolMenu(((RLPwidget*)widget)->menu_bar->addMenu(widget->tr("&Tools")),  widget, this, BaseObj);
 		QMenu *plots = ((RLPwidget*)widget)->menu_bar->addMenu(widget->tr("&Plots"));
 		QMenu *about = ((RLPwidget*)widget)->menu_bar->addMenu(widget->tr("&?"));
+		file->addAction("&Open", widget, SLOT(cmOpen()), Qt::CTRL + Qt::Key_O);
 #endif
-		RLP_MITEM(file, widget, this, BaseObj, "&Open", CM_OPEN);
-		RLP_MITEM(file, widget, this, BaseObj, "Save &as", CM_SAVEGRAPHAS);
+		RLP_MITEM(file, widget, this, BaseObj, "Save &as", CM_SAVEAS);
 		RLP_MSEPARATOR(file);
-		RLP_MITEM(file, widget, this, BaseObj, "&Print", CM_PRINT);
+#if QT_VERSION < 0x040000
+		file->insertItem("&Print", widget, SLOT(cmPrint()), Qt::CTRL + Qt::Key_P);
+#else
+		file->addAction("&Print", widget, SLOT(cmPrint()), Qt::CTRL + Qt::Key_P);
+#endif
 		RLP_MITEM(file, widget, this, BaseObj, "&Export", CM_EXPORT);
 		RLP_MSEPARATOR(file);
 		RLP_MITEM(file, widget, this, BaseObj, "&Close", CM_EXIT);
@@ -2871,6 +2972,7 @@ OutputQT::SetMenu(int type)
 		QPopupMenu *displ = new QPopupMenu(widget);
 		QPopupMenu *plots = new QPopupMenu(widget);
 		QPopupMenu *about = new QPopupMenu(widget);
+		file->insertItem("&Open", widget, SLOT(cmOpen()), Qt::CTRL + Qt::Key_O);
 #else
 		QMenu *file = ((RLPwidget*)widget)->menu_bar->addMenu(widget->tr("&File"));
 		QMenu *edit = ((RLPwidget*)widget)->menu_bar->addMenu(widget->tr("&Edit"));
@@ -2878,11 +2980,15 @@ OutputQT::SetMenu(int type)
 		MkToolMenu(((RLPwidget*)widget)->menu_bar->addMenu(widget->tr("&Tools")),  widget, this, BaseObj);
 		QMenu *plots = ((RLPwidget*)widget)->menu_bar->addMenu(widget->tr("&Plots"));
 		QMenu *about = ((RLPwidget*)widget)->menu_bar->addMenu(widget->tr("&?"));
+		file->addAction("&Open", widget, SLOT(cmOpen()), Qt::CTRL + Qt::Key_O);
 #endif
-		RLP_MITEM(file, widget, this, BaseObj, "&Open", CM_OPEN);
-		RLP_MITEM(file, widget, this, BaseObj, "Save &as", CM_SAVEGRAPHAS);
+		RLP_MITEM(file, widget, this, BaseObj, "Save &as", CM_SAVEAS);
 		RLP_MSEPARATOR(file);
-		RLP_MITEM(file, widget, this, BaseObj, "&Print", CM_PRINT);
+#if QT_VERSION < 0x040000
+		file->insertItem("&Print", widget, SLOT(cmPrint()), Qt::CTRL + Qt::Key_P);
+#else
+		file->addAction("&Print", widget, SLOT(cmPrint()), Qt::CTRL + Qt::Key_P);
+#endif
 		RLP_MITEM(file, widget, this, BaseObj, "&Export", CM_EXPORT);
 		RLP_MSEPARATOR(file);
 		RLP_MITEM(file, widget, this, BaseObj, "&Close", CM_EXIT);
@@ -2936,7 +3042,9 @@ OutputQT::SetMenu(int type)
 	MenuHeight = ((RLPwidget*)widget)->menu_bar->height();
 	widget->resize(widget->width()+8, widget->height()+8);
 #endif
-	if(MenuHeight< 20) MenuHeight = 24;
+	if(MenuHeight< 20) MenuHeight = 25;
+	if(MenuHeight< defs.iMenuHeight) MenuHeight = defs.iMenuHeight;
+	else defs.iMenuHeight = MenuHeight;
 	return true;
 }
 #undef RLP_MITEM
@@ -3238,6 +3346,10 @@ RLPwidget::mouseMoveEvent(QMouseEvent *e)
 {
 	int i;
 	MouseEvent mev = {mouse_buttons_down, MOUSE_MOVE, e->x(), e->y()};
+
+	if(rlpsrv && !rlpsrv->bValid) {
+		delete(rlpsrv);			rlpsrv = 0L;
+		}
 #if QT_VERSION < 0x040000
 	i = e->state();
 	if(i & Qt::ShiftButton) mev.StateFlags |= 0x08;
@@ -3247,7 +3359,7 @@ RLPwidget::mouseMoveEvent(QMouseEvent *e)
 	if(i & Qt::ShiftModifier) mev.StateFlags |= 0x08;
 	if(i & Qt::ControlModifier) mev.StateFlags |= 0x10;
 #endif
-	if(((OutputQT*)OutputClass)->ShowObj) {
+	if(OutputClass && ((OutputQT*)OutputClass)->ShowObj) {
 		delete((eph_obj*)((OutputQT*)OutputClass)->ShowObj);
 		((OutputQT*)OutputClass)->ShowObj = 0L;
 		}
@@ -3328,6 +3440,7 @@ RLPwidget::keyPressEvent(QKeyEvent *e)
 				if(uc == 3) break;
 				else if(uc == 27 && OutputClass) {
 					OutputClass->HideMark();
+					EmptyClip();
 					ProcMenuEvent(CM_T_STANDARD, this, OutputClass, BaseObj);
 					}
 				else if(uc == 22) cmPaste();
@@ -3435,7 +3548,7 @@ PrintQT::PrintQT(GraphObj *g, char *file)
 {
 	units = defs.cUnits;
 	dxf.setMatrix(0.1, 0.0, 0.0, 0.1, 0.0, 0.0);
-	hgo = 0L;
+	hgo = 0L;	minLW = 1;
 	if(file) fileName = strdup(file);
 	else fileName = 0L;
 	go = g;
@@ -3487,19 +3600,14 @@ PrintQT::SetLine(LineDEF *lDef)
 	if(lDef->width != LineWidth || lDef->width != LineWidth ||
 		lDef->pattern != dPattern || lDef->color != dLineCol) {
 		LineWidth = lDef->width;
-		iw = iround(un2fix(lDef->width));
-		dPattern = lDef->pattern;
+		iw = iround(un2fix(lDef->width));	dPattern = lDef->pattern;
 		RLP.finc = 256.0/un2fix(lDef->patlength*8.0);
 		RLP.fp = 0.0;
 		if(iLine == iw && dLineCol == lDef->color) return true;
-		iLine = iw;
-		dLineCol = lDef->color;
-		qPen.setColor(SwapRB(dLineCol));
-		qPen.setWidth(iw);
-		qPen.setStyle(Qt::SolidLine);
-		qPen.setCapStyle(Qt::RoundCap);
-		qPen.setJoinStyle(Qt::RoundJoin);
-		qPainter.setPen(qPen);
+		iLine = iw > minLW ? iw : minLW;	dLineCol = lDef->color;
+		qPen.setColor(MK_QCOLOR(dLineCol));	qPen.setWidth(iLine);
+		qPen.setStyle(Qt::SolidLine);		qPen.setCapStyle(Qt::RoundCap);
+		qPen.setJoinStyle(Qt::RoundJoin);	qPainter.setPen(qPen);
 		}
 	return true;
 }
@@ -3509,16 +3617,13 @@ PrintQT::SetFill(FillDEF *fill)
 {
 	if(!fill) return false;
 	if((fill->type & 0xff) != FILL_NONE) {
-		if(!hgo) hgo = new HatchOut(this);
-		if(hgo) hgo->SetFill(fill);
+		if(!hgo) hgo = new HatchOut(this);	if(hgo) hgo->SetFill(fill);
 		}
 	else {
-		if(hgo) delete hgo;
-		hgo = 0L;
+		if(hgo) delete hgo;			hgo = 0L;
 		}
-	qPainter.setBrush(QColor(SwapRB(fill->color)));
-	dFillCol = fill->color;
-	dFillCol2 = fill->color2;
+	qPainter.setBrush(MK_QCOLOR(fill->color));
+	dFillCol = fill->color;				dFillCol2 = fill->color2;
 	return true;
 }
 
@@ -3619,7 +3724,7 @@ PrintQT::oRectangle(int x1, int y1, int x2, int y2, char *nam)
 #if QT_VERSION < 0x040000
 	qPainter.drawRect(x1, y1, x2-x1, y2-y1);
 #else
-	qPainter.drawRect(x1, y1, x2-x1-1, y2-y1-1);
+	qPainter.drawRect(x1, y1, x2-x1, y2-y1);
 #endif
 	if(hgo) hgo->oRectangle(x1, y1, x2, y2, 0L);
 	return true;
@@ -3680,7 +3785,8 @@ PrintQT::oPolygon(POINT *pts, int cp, char *nam)
 void FindBrowser()
 {
 	//find a suitable browser
-	if(FileExist("/usr/bin/mozilla")) WWWbrowser = strdup("mozilla");
+	if(FileExist("/usr/bin/firefox")) WWWbrowser = strdup("firefox");
+	else if(FileExist("/usr/bin/mozilla")) WWWbrowser = strdup("mozilla");
 	else if(FileExist("/usr/bin/netscape")) WWWbrowser = strdup("netscape");
 	else if(FileExist("/usr/bin/konqueror")) WWWbrowser = strdup("konqueror");
 	else if(FileExist("/opt/kde3/bin/konqueror")) WWWbrowser = strdup("konqueror");
@@ -3709,9 +3815,15 @@ int main (int argc, char **argv)
 {
 	QApplication a(argc, argv);
 	DefsRW *drw;
+	int cb;
 
 	if(argc > 1 && argv[1]  && argv[1][0] && FileExist(argv[1]))
 		LoadFile = strdup(argv[1]);
+	if(argc > 0 && argv[0] && argv[0][0]) {
+		ShellCmd = (char*)malloc(cb = strlen(argv[0]) +10);
+		cb = rlp_strcpy(ShellCmd, cb+1, argv[0]);
+		rlp_strcpy(ShellCmd + cb, 4, " &");
+		}
 	QAppl = &a;
 	InitTextCursor(true);
 	ShowBanner(true);
@@ -3727,14 +3839,14 @@ int main (int argc, char **argv)
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Dialog box support
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-DlgWidget::DlgWidget(QWidget *par, const char *name, tag_DlgObj *d)
+DlgWidget::DlgWidget(QWidget *par, const char *name, tag_DlgObj *d, DWORD flags)
 #if QT_VERSION < 0x030000				//n.a. in Qt version 2
 : QWidget(par, name, Qt::WType_Dialog)
 #elif QT_VERSION < 0x040000
 : QWidget(par, name, 0x0000002)
 #else		//Qt 4.0
 //: QWidget(par ? par : CurrWidget, Qt::Window)
-: QWidget(0L, Qt::Window)
+: QWidget(0L, flags & 0x00000004 ? Qt::Dialog : Qt::Window)
 #endif
 {
 	parent = par;
@@ -3763,8 +3875,10 @@ DlgWidget::paintEvent(QPaintEvent *range)
 	rc = range->rect();
 	qpainter.drawPixmap(rc.left(), rc.top(), *mempic, rc.left(), rc.top(), rc.width()+1, rc.height()+1);
 #if QT_VERSION >=0x040000
-	if(((OutputQT*)OutputClass)->ShowObj)((eph_obj*)(((OutputQT*)OutputClass)->ShowObj))->DoPlot(&qpainter);
-	if(((OutputQT*)OutputClass)->ShowAnimated)((eph_obj*)(((OutputQT*)OutputClass)->ShowAnimated))->DoPlot(&qpainter);
+	if(OutputClass && ((OutputQT*)OutputClass)->ShowObj)
+		((eph_obj*)(((OutputQT*)OutputClass)->ShowObj))->DoPlot(&qpainter);
+	if(OutputClass && ((OutputQT*)OutputClass)->ShowAnimated)
+		((eph_obj*)(((OutputQT*)OutputClass)->ShowAnimated))->DoPlot(&qpainter);
 #endif
 }
 
@@ -3943,16 +4057,17 @@ void *CreateDlgWnd(char *title, int x, int y, int width, int height, tag_DlgObj
 	OutputQT *o;
 	int dw, dh;
 
-	w = new DlgWidget(pw = QAppl->activeWindow(), 0, d);
+	w = new DlgWidget(pw = QAppl->activeWindow(), 0, d, flags);
 #if QT_VERSION < 0x040000
 	w->setCaption(title);
 #else
 	w->setWindowTitle(title);
+	if(d->bModal) w->setWindowModality(Qt::ApplicationModal);
 #endif
 	if(flags & 0x2) w->setFixedSize(width, height);
 	else w->setFixedSize(width-6, height-16);
 	o = new OutputQT(w);	o->units = defs.cUnits;
-	o->Erase(0x00e0e0e0L);	if(flags & 0x04) w->startTimer(100);
+	o->Erase(0x00e0e0e0L);	if(flags & 0x08) w->startTimer(100);
 	if(flags & 0x1) {
 		GetDesktopSize(&dw, &dh);
 		w->move((dw>>1) - ((w->width())>>1), (dh>>1) - ((w->height())>>1));
@@ -4043,3 +4158,5 @@ bool DelBitmapClass(anyOutput *w)
 	return true;
 }
 
+
+
diff --git a/QT_Spec.h b/QT_Spec.h
index 0684c1c..caae91d 100755
--- a/QT_Spec.h
+++ b/QT_Spec.h
@@ -1,4 +1,4 @@
-//QT_Spec.h, Copyright (c) 2001-2007 R.Lackner
+//QT_Spec.h, Copyright (c) 2001-2008 R.Lackner
 //
 //    This file is part of RLPlot.
 //
@@ -78,10 +78,11 @@ private:
 class RLPserver {
 public:
 	GraphObj *SourceGO;
+	bool bValid;
 
 	RLPserver(QObject* parent=0, GraphObj *g=0);
 	~RLPserver();
-
+	void CreateThread();
 	void SetGO(GraphObj *g);
 	char *GetXML();
 	char *GetRLP();
@@ -165,6 +166,10 @@ public slots:
 	void cmZoomFit(){ProcMenuEvent(CM_ZOOMFIT, this, OutputClass, BaseObj);};
 	void cmPaste();
 	void cmUndo(){if(BaseObj) BaseObj->Command(CMD_UNDO, 0L, OutputClass);};
+	void cmSave(){ProcMenuEvent(CM_SAVE, this, OutputClass, BaseObj);};
+	void cmOpen(){ProcMenuEvent(CM_OPEN, this, OutputClass, BaseObj);};
+	void cmPrint(){ProcMenuEvent(CM_PRINT, this, OutputClass, BaseObj);};
+	void cmNewInst(){ProcMenuEvent(CM_NEWINST, this, OutputClass, BaseObj);};
 
 protected:
 	void paintEvent(QPaintEvent *);
@@ -194,7 +199,7 @@ public:
 	QPixmap *mempic;
 	anyOutput *OutputClass;
 
-	DlgWidget(QWidget *par=0, const char *name=0, tag_DlgObj *d = 0);
+	DlgWidget(QWidget *par=0L, const char *name = 0L, tag_DlgObj *d = 0L, DWORD flags = 0L);
 	~DlgWidget();
 
 protected:
@@ -239,6 +244,7 @@ public:
 	virtual bool StartPage() {return true;};
 	bool CopyBitmap(int x, int y, anyOutput* src, int sx, int sy,
 		int sw, int sh, bool invert);
+	virtual void MouseCapture(bool bgrab){return;};
 	bool oGetTextExtent(char *text, int cb, int *width, int *height);
 	bool oGetTextExtentW(w_char *text, int cb, int *width, int *height);
 	bool oGetPix(int x, int y, DWORD *col);
@@ -268,13 +274,15 @@ public:
 	~OutputQT();
 	bool ActualSize(RECT *rc);
 	void Focus(){if(widget){widget->show(); widget->activateWindow();widget->raise();}};
-	void Caption(char *txt);
+	void Caption(char *txt, bool bModified);
 	void MouseCursor(int cid, bool force);
 	bool SetScroll(bool isVert, int iMin, int iMax, int iPSize, int iPos);
 	bool EndPage();
+	void MouseCapture(bool bgrab);
 	bool UpdateRect(RECT *rc, bool invert);
 	void ShowLine(POINT * pts, int cp, DWORD color);
 	void ShowEllipse(POINT p1, POINT p2, DWORD color); 
+	void ShowInvert(RECT *rec);
 	bool SetMenu(int type);
 	void CheckMenu(int mid, bool check);
 	void FileHistory();
diff --git a/RLPLOT.RC b/RLPLOT.RC
new file mode 100755
index 0000000..490caac
--- /dev/null
+++ b/RLPLOT.RC
@@ -0,0 +1,307 @@
+//RLPlot.RC, (C)2000-2006 R.Lackner
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+
+#define MENU_1  400
+#define MENU_2  401
+#define MENU_3  402
+
+#define IDI_EVAL                   50
+
+#define ACCELERATORS_1  1
+
+#ifdef RC_INVOKED
+#ifndef WORKSHOP_INVOKED
+#include <windows.h>
+#include "Version.h"
+#include "menu.h"
+#endif
+
+MENU_1 MENU DISCARDABLE
+BEGIN
+    POPUP "&File"
+    BEGIN
+        MENUITEM "&Open",                       CM_OPEN
+        MENUITEM "&Save as",                    CM_SAVEAS
+        MENUITEM SEPARATOR
+        MENUITEM "&Print",                      CM_PRINT
+        MENUITEM "&Export",                     CM_EXPORT
+        MENUITEM SEPARATOR
+        MENUITEM "&Close",                      CM_EXIT
+    END
+    POPUP "&Edit"
+    BEGIN
+        MENUITEM "&Undo"                        CM_UNDO
+        MENUITEM SEPARATOR
+        MENUITEM "&Copy",                       CM_COPYGRAPH
+        MENUITEM "&Paste",                      CM_PASTE
+        MENUITEM SEPARATOR
+        MENUITEM "&Update Values",              CM_UPDATE
+        MENUITEM SEPARATOR
+        MENUITEM "&Delete Object",              CM_DELOBJ
+        END
+    POPUP "&Display"
+    BEGIN
+        MENUITEM "&Redraw",                     CM_REDRAW
+        POPUP "&Zoom"
+        BEGIN
+            MENUITEM "zoom &in"                 CM_ZOOMIN
+            MENUITEM "zoom &out"                CM_ZOOMOUT
+            MENUITEM "&fit to window"           CM_ZOOMFIT
+            MENUITEM SEPARATOR
+            MENUITEM "25%",                     CM_ZOOM25
+            MENUITEM "50%",                     CM_ZOOM50
+            MENUITEM "100%",                    CM_ZOOM100
+            MENUITEM "200%",                    CM_ZOOM200
+            MENUITEM "400%",                    CM_ZOOM400
+        END
+        MENUITEM "&Layers",                     CM_LAYERS
+    END
+    POPUP "&Tools"
+    BEGIN
+        MENUITEM "&Standard",                   CM_T_STANDARD
+        MENUITEM SEPARATOR
+        MENUITEM "&Draw",                       CM_T_DRAW
+        MENUITEM "Poly&line",                   CM_T_POLYLINE
+        MENUITEM "Poly&gon",                    CM_T_POLYGON
+        MENUITEM "&Rectangle",                  CM_T_RECTANGLE
+        MENUITEM "r&ound Rect.",                CM_T_ROUNDREC
+        MENUITEM "&Ellipse",                    CM_T_ELLIPSE
+        MENUITEM "&Arrow",                      CM_T_ARROW
+        MENUITEM "&Text",                       CM_T_TEXT
+    END
+    POPUP "&Plots"
+    BEGIN
+        MENUITEM "Add &Plot",                   CM_ADDPLOT
+        MENUITEM "Add &Axis",                   CM_ADDAXIS
+        MENUITEM "Add &Legend",                 CM_LEGEND
+        MENUITEM SEPARATOR
+        MENUITEM "&Configure",                  CM_DEFAULTS
+    END
+    POPUP "&?"
+    BEGIN
+        MENUITEM "&About ...",                  CM_ABOUT
+    END
+END
+
+MENU_2 MENU DISCARDABLE
+BEGIN
+    POPUP "&File"
+    BEGIN
+	MENUITEM "&New Instance"		CM_NEWINST
+	MENUITEM SEPARATOR
+        MENUITEM "&Open",                       CM_OPEN
+        MENUITEM "&Save",			CM_SAVE
+        MENUITEM "Save &as",                    CM_SAVEAS
+        MENUITEM SEPARATOR
+        MENUITEM "&Print",                      CM_PRINT
+        MENUITEM SEPARATOR
+        MENUITEM "E&xit",                       CM_EXIT
+    END
+    POPUP "&Edit"
+    BEGIN
+        MENUITEM "&Undo"                        CM_UNDO
+        MENUITEM SEPARATOR
+        MENUITEM "&Rows/Cols",                  CM_ADDROWCOL
+        POPUP "&Insert"
+        BEGIN
+        	MENUITEM "&Rows",					CM_INSROW
+        	MENUITEM "&Columns",				CM_INSCOL
+        END
+        POPUP "&Delete"
+        BEGIN
+        	MENUITEM "&Rows",					CM_DELROW
+        	MENUITEM "&Columns",				CM_DELCOL
+        END
+        MENUITEM SEPARATOR
+        MENUITEM "&Copy",                       CM_COPY
+        MENUITEM "C&ut",						CM_CUT
+        MENUITEM "&Paste",                      CM_PASTE
+        MENUITEM SEPARATOR
+        MENUITEM "&Fill Range",					CM_FILLRANGE
+    END
+    POPUP "&Statistics"
+    BEGIN
+    	MENUITEM "&Sample Stats"				CM_SMPLSTAT
+    	MENUITEM "&Comp. Means"					CM_REPCMEANS
+    	POPUP "&Anova"
+    	BEGIN
+    		MENUITEM "&One Way Anova"			CM_REPANOV
+    		MENUITEM "&Kruskal Wallis"			CM_REPKRUSKAL
+    		MENUITEM "&Two Way Anova",			CM_REPTWANOV
+    		MENUITEM "&Friedman Anova",			CM_REPFRIEDM
+    		MENUITEM "&Two Way /w Replica"		CM_REPTWANR
+    	END
+    	POPUP "&Regression"
+    	BEGIN
+    		MENUITEM "&Linear Regression"		CM_REPREGR
+    		MENUITEM "&Robust Line-Fit",		CM_ROBUSTLINE
+    	END
+ 		POPUP "C&orrelations"
+		BEGIN
+			MENUITEM "Correlation &Matrix"		CM_CORRELM
+			MENUITEM "Tiled &Plots"				CM_CORRELT
+		END
+   	MENUITEM "&2x2 Table"					CM_REPTWOWAY
+    END
+    POPUP "&Graph"
+    BEGIN
+        MENUITEM "Create &Graph",               CM_NEWGRAPH
+        MENUITEM "Create &Page",                CM_NEWPAGE
+        MENUITEM "&Flush Graph(s)",             CM_DELGRAPH
+        MENUITEM SEPARATOR
+        MENUITEM "&Settings",                   CM_DEFAULTS
+    END
+    POPUP "&?"
+    BEGIN
+        MENUITEM "&About ...",                  CM_ABOUT
+    END
+END
+
+MENU_3 MENU DISCARDABLE
+BEGIN
+    POPUP "&File"
+    BEGIN
+        MENUITEM "&Open",                       CM_OPEN
+        MENUITEM "&Save as",                    CM_SAVEAS
+        MENUITEM SEPARATOR
+        MENUITEM "&Print",                      CM_PRINT
+        MENUITEM "&Export",                     CM_EXPORT
+        MENUITEM SEPARATOR
+        MENUITEM "&Close",                      CM_EXIT
+    END
+    POPUP "&Edit"
+    BEGIN
+        MENUITEM "&Undo"                        CM_UNDO
+        MENUITEM SEPARATOR
+        MENUITEM "&Copy",                       CM_COPYGRAPH
+        MENUITEM "&Paste",                      CM_PASTE
+        MENUITEM SEPARATOR
+        MENUITEM "&Update Values",              CM_UPDATE
+        MENUITEM SEPARATOR
+        MENUITEM "&Delete Object",              CM_DELOBJ
+    END
+    POPUP "&Display"
+    BEGIN
+        MENUITEM "&Redraw",                     CM_REDRAW
+        POPUP "&Zoom"
+        BEGIN
+            MENUITEM "zoom &in"                 CM_ZOOMIN
+            MENUITEM "zoom &out"                CM_ZOOMOUT
+            MENUITEM "&fit to window"           CM_ZOOMFIT
+            MENUITEM SEPARATOR
+            MENUITEM "25%",                     CM_ZOOM25
+            MENUITEM "50%",                     CM_ZOOM50
+            MENUITEM "100%",                    CM_ZOOM100
+            MENUITEM "200%",                    CM_ZOOM200
+            MENUITEM "400%",                    CM_ZOOM400
+        END
+        MENUITEM "&Layers",                     CM_LAYERS
+    END
+    POPUP "&Tools"
+    BEGIN
+        MENUITEM "&Standard",                   CM_T_STANDARD
+        MENUITEM SEPARATOR
+        MENUITEM "&Draw",                       CM_T_DRAW
+        MENUITEM "Poly&line",                   CM_T_POLYLINE
+        MENUITEM "Poly&gon",                    CM_T_POLYGON
+        MENUITEM "&Rectangle",                  CM_T_RECTANGLE
+        MENUITEM "r&ound Rect.",                CM_T_ROUNDREC
+        MENUITEM "&Ellipse",                    CM_T_ELLIPSE
+        MENUITEM "&Arrow",                      CM_T_ARROW
+        MENUITEM "&Text",                       CM_T_TEXT
+    END
+    POPUP "&Plots"
+    BEGIN
+        MENUITEM "Add &Graph",                  CM_NEWGRAPH
+        MENUITEM "Add &Plot",                   CM_ADDPLOT
+        MENUITEM "Add &Axis",                   CM_ADDAXIS
+        MENUITEM "Add &Legend",                 CM_LEGEND
+        MENUITEM SEPARATOR
+        MENUITEM "Page &Settings",              CM_DEFAULTS
+    END
+    POPUP "&?"
+    BEGIN
+        MENUITEM "&About ...",                  CM_ABOUT
+    END
+END
+
+IDI_EVAL ICON DISCARDABLE "rlplot.ico"
+
+
+VERSIONINFO_1 VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x0L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x10001L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904E4"
+        BEGIN
+            VALUE "CompanyName", "R.Lackner\0"
+            VALUE "FileDescription", "Data Plot Program\0"
+                        VALUE "FileVersion", SZ_VERSION"\000\000"
+            VALUE "InternalName", "RLPlot\0"
+            VALUE "LegalCopyright", "Copyright � R. Lackner 2000-2004\0"
+            VALUE "OriginalFilename", "RLPlot.exe\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1252
+    END
+END
+
+ACCELERATORS_1 ACCELERATORS MOVEABLE PURE
+BEGIN
+    VK_DELETE,      CM_DELKEY,              VIRTKEY
+    VK_LEFT,        CM_LEFTARRKEY,          VIRTKEY
+    VK_RIGHT,       CM_RIGHTARRKEY,         VIRTKEY
+    VK_UP,          CM_UPARRKEY,            VIRTKEY
+    VK_DOWN,        CM_SHDOWN,              VIRTKEY, SHIFT
+    VK_LEFT,        CM_SHLEFT,              VIRTKEY, SHIFT
+    VK_RIGHT,       CM_SHRIGHT,             VIRTKEY, SHIFT
+    VK_UP,          CM_SHUP,                VIRTKEY, SHIFT
+    VK_DOWN,        CM_DOWNARRKEY,          VIRTKEY
+    VK_TAB,         CM_TAB,                 VIRTKEY
+    VK_TAB,         CM_SHTAB,               VIRTKEY, SHIFT
+    VK_PRIOR,       CM_PGUP,                VIRTKEY
+    VK_NEXT,        CM_PGDOWN,              VIRTKEY
+    VK_PRIOR,       CM_SHPGUP,              VIRTKEY, SHIFT
+    VK_NEXT,        CM_SHPGDOWN,            VIRTKEY, SHIFT
+    VK_HOME,        CM_POS_FIRST,           VIRTKEY
+    VK_END,         CM_POS_LAST,            VIRTKEY
+    VK_ESCAPE,		CM_T_STANDARD,			VIRTKEY
+    "^C",           CM_COPY,                ASCII
+    "^X",			CM_CUT,					ASCII
+    "^V",           CM_PASTE,               ASCII
+    "^Z",           CM_UNDO,                ASCII
+    "^F",			CM_ZOOMFIT,				ASCII
+END
+
+#endif  // RC_INVOKED
+
+
diff --git a/TheDialog.cpp b/TheDialog.cpp
index 8350595..bc7ea4a 100755
--- a/TheDialog.cpp
+++ b/TheDialog.cpp
@@ -1,4947 +1,5027 @@
-//TheDialog.cpp, Copyright (c) 2001-2007 R.Lackner
-//Operating system independent code for dialog boxes
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-#include "rlplot.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include "TheDialog.h"
-
-extern tag_Units Units[];
-extern char TmpTxt[];
-extern Default defs;
-extern GraphObj *CurrGO;
-extern EditText *CurrText;		//current EditText object
-extern RECT rTxtCur;			//text cursor position and direction
-extern UndoObj Undo;
-
-char *WWWbrowser = 0L;
-char *LoadFile = 0L;
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// internal declarations 
-static int xbase = 2;
-static int ybase = 2;
-int dlgtxtheight = 10;
-static unsigned long DlgBGcolor = 0x00e0e0e0L;
-static unsigned long DlgBGhigh = 0x00e8e8e8L;
-TextDEF DlgText = {0x00000000L, 0x00ffffffL, 4.0, 0.0, 0.0, 0, 
-	TXA_HLEFT | TXA_VTOP, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, 0L}; 
-
-//prototypes: WinSpec.cpp
-void *CreateDlgWnd(char *title, int x, int y, int width, int height, tag_DlgObj *d, DWORD flags);
-
-//The dialog object which just has the input focus
-Dialog *DialogFocus = 0L;
-Dialog *DialogDefault = 0L;
-Dialog *DialogTabStop = 0L;
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Base classes to dialog items
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-DlgRoot::DlgRoot(DlgInfo *tmpl, DataObj *d)
-{
-	int i;
-	RECT rc;
-
-	dlg = 0L;			flags = 0L;		tabstops = 0L;
-	DlgText.iSize = dlgtxtheight;		DlgText.ColBg = DlgBGcolor;
-	DlgText.fSize = defs.GetSize(SIZE_TEXT);
-	type = NONE;		Id = -2;		cContinue = 0;
-	bActive = bRedraw = false;			c_go = CurrGO;
-	CurrDisp = 0L;		oldFocus = DialogFocus;		DialogFocus = 0L;
-	oldDefault = DialogDefault;			oldTabStop = DialogTabStop;
-	data = d;			res_put = res_get = 0;		hDialog = 0L;
-	if(ParentOut = Undo.cdisp) ParentOut->MouseCursor(MC_WAIT, false);
-	mrk_item = 0L;		//if an item has a mark its this one
-	if(tmpl) {
-		//count number of items first, then allocate memory
-		for(cDlgs=1;!(tmpl[cDlgs-1].flags & LASTOBJ); cDlgs++);
-		dlg = (DlgTmpl **)calloc(cDlgs+1, sizeof(DlgTmpl*));
-		tabstops =(Dialog**)calloc(cDlgs, sizeof(Dialog*));
-		if(dlg) for (i = 0; i < cDlgs; i++) {
-			dlg[i] = (DlgTmpl *)malloc(sizeof(DlgTmpl));
-			if(dlg[i]) {
-				dlg[i]->id = tmpl[i].id;
-				dlg[i]->next = tmpl[i].next;
-				dlg[i]->first = tmpl[i].first;
-				dlg[i]->flags = tmpl[i].flags;
-				rc.left = tmpl[i].x * xbase;
-				rc.right = rc.left + tmpl[i].w * xbase;
-				rc.top = tmpl[i].y * ybase;
-				rc.bottom = rc.top + tmpl[i].h * ybase;
-				//an item appearing in the following list should have a corresponding
-				//  entry in the list of ~DlgRoot()
-				switch(tmpl[i].type) {
-				case PUSHBUTTON:
-					dlg[i]->dialog = new PushButton(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
-					break;
-				case TEXTBOX:
-					dlg[i]->dialog = new TextBox(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
-					break;
-				case ARROWBUTT:
-					dlg[i]->dialog = new ArrowButton(this, &tmpl[i],rc,(int*)tmpl[i].ptype);
-					break;
-				case COLBUTT:
-					dlg[i]->dialog = new ColorButton(this, &tmpl[i],rc, (DWORD *)tmpl[i].ptype);
-					break;
-				case FILLBUTTON:
-					dlg[i]->dialog = new FillButton(this, &tmpl[i],rc, (FillDEF *)tmpl[i].ptype);
-					break;
-				case SHADE3D:
-					dlg[i]->dialog = new Shade3D(this, &tmpl[i],rc, (FillDEF *)tmpl[i].ptype);
-					break;
-				case LINEBUTT:
-					dlg[i]->dialog = new LineButton(this, &tmpl[i],rc, (LineDEF *)tmpl[i].ptype);
-					break;
-				case SYMBUTT:
-					dlg[i]->dialog = new SymButton(this, &tmpl[i],rc, (Symbol **)tmpl[i].ptype);
-					break;
-				case FILLRADIO:
-					dlg[i]->dialog = new FillRadioButt(this, &tmpl[i],rc, *((unsigned int*)tmpl[i].ptype));
-					break;
-				case SYMRADIO:
-					dlg[i]->dialog = new SymRadioButt(this, &tmpl[i],rc, (int*)tmpl[i].ptype);
-					break;
-				case CHECKBOX:
-					dlg[i]->dialog = new CheckBox(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
-					break;
-				case CHECKPIN:
-					dlg[i]->dialog = new CheckPin(this, &tmpl[i],rc);
-					break;
-				case TRASH:
-					dlg[i]->dialog = new Trash(this, &tmpl[i],rc);
-					break;
-				case CONFIG:
-					dlg[i]->dialog = new Config(this, &tmpl[i],rc);
-					break;
-				case RADIO0:	case RADIO1:	case RADIO2:
-					dlg[i]->dialog = new RadioButton(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
-					break;
-				case LTEXT:		case RTEXT:		case CTEXT:
-					dlg[i]->dialog = new Text(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
-					break;
-				case EDTEXT:
-					dlg[i]->dialog = new InputText(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
-					break;
-				case RANGEINPUT:
-					dlg[i]->dialog = new RangeInput(this, &tmpl[i],rc,(char*)tmpl[i].ptype, data);
-					break;
-				case EDVAL1:
-					dlg[i]->dialog = new InputValue(this, &tmpl[i],rc,(double*)tmpl[i].ptype);
-					break;
-				case INCDECVAL1:
-					dlg[i]->dialog = new IncDecValue(this, &tmpl[i],rc,(double*)tmpl[i].ptype);
-					break;
-				case TXTHSP:
-					dlg[i]->dialog = new TxtHSP(this, &tmpl[i],rc, (int*)tmpl[i].ptype);
-					break;
-				case VSCROLL:
-					dlg[i]->dialog = new ScrollBar(this, &tmpl[i],rc, true);
-					break;
-				case HSCROLL:
-					dlg[i]->dialog = new ScrollBar(this, &tmpl[i],rc, false);
-					break;
-				case ICON:
-					dlg[i]->dialog = new Icon(this, &tmpl[i],rc, (int*)tmpl[i].ptype);
-					break;
-				case GROUP:
-					dlg[i]->dialog = new Group(this, &tmpl[i], rc);
-					break;
-				case GROUPBOX:
-					dlg[i]->dialog = new GroupBox(this, &tmpl[i],rc, (char*)tmpl[i].ptype);
-					break;
-				case SHEET:
-					dlg[i]->dialog = new TabSheet(this, &tmpl[i],rc, (TabSHEET *)tmpl[i].ptype, data);
-					break;
-				case ODBUTTON:
-					dlg[i]->dialog = new ODbutton(this, &tmpl[i],rc, tmpl[i].ptype);
-					break;
-				case LISTBOX1:
-					dlg[i]->dialog = new Listbox(this, &tmpl[i],rc, (char**)tmpl[i].ptype);
-					break;
-				case TREEVIEW:
-					dlg[i]->dialog = new Treeview(this, &tmpl[i],rc, (GraphObj*)tmpl[i].ptype);
-					break;
-				case LINEPAT:
-					dlg[i]->dialog = new LinePat(this, &tmpl[i],rc, (LineDEF *)tmpl[i].ptype);
-					break;
-				default:
-					dlg[i]->dialog = NULL;
-					}
-				}
-			else break;
-			}
-		}
-}
-
-DlgRoot::~DlgRoot()
-{
-	int i;
-
-	if(data) data->Command(CMD_ETRACC, 0L, 0L);
-	HideTextCursor();
-	if(dlg){
-		for (i = 0; dlg[i] && i < cDlgs; i++) {
-			//we need to delete each object using a cast on its proper type
-			//to call the proper destructor
-			if(dlg[i]->dialog){
-				switch(dlg[i]->dialog->type) {
-				case PUSHBUTTON:	delete((PushButton*)dlg[i]->dialog);		break;
-				case TEXTBOX:		delete((TextBox*)dlg[i]->dialog);			break;
-				case ARROWBUTT:		delete((ArrowButton*)dlg[i]->dialog);		break;
-				case COLBUTT:		delete((ColorButton*)dlg[i]->dialog);		break;
-				case FILLBUTTON:	delete((FillButton*)dlg[i]->dialog);		break;
-				case SHADE3D:		delete((Shade3D*)dlg[i]->dialog);			break;
-				case LINEBUTT:		delete((LineButton*)dlg[i]->dialog);		break;
-				case SYMBUTT:		delete((SymButton*)dlg[i]->dialog);			break;
-				case FILLRADIO:		delete((FillRadioButt*)dlg[i]->dialog);		break;
-				case SYMRADIO:		delete((SymRadioButt*)dlg[i]->dialog);		break;
-				case CHECKBOX:		delete((CheckBox*)dlg[i]->dialog);			break;
-				case CHECKPIN:		delete((CheckPin*)dlg[i]->dialog);			break;
-				case TRASH:			delete((Trash*)dlg[i]->dialog);				break;
-				case CONFIG:		delete((Config*)dlg[i]->dialog);			break;
-				case RADIO0:	case RADIO1:
-				case RADIO2:		delete((RadioButton*)dlg[i]->dialog);		break;
-				case LTEXT:	case RTEXT:	
-				case CTEXT:	delete((Text*)dlg[i]->dialog);						break;
-				case EDTEXT:		delete((InputText*)dlg[i]->dialog);			break;
-				case RANGEINPUT:	delete((RangeInput*)dlg[i]->dialog);			break;
-				case EDVAL1:		delete((InputValue*)dlg[i]->dialog);		break;
-				case INCDECVAL1:	delete((IncDecValue*)dlg[i]->dialog);		break;
-				case TXTHSP:		delete((TxtHSP*)dlg[i]->dialog);			break;
-				case HSCROLL:
-				case VSCROLL:		delete((ScrollBar*)dlg[i]->dialog);			break;
-				case ICON:			delete((Icon*)dlg[i]->dialog);				break;
-				case GROUP:			delete((Group*)dlg[i]->dialog);				break;
-				case GROUPBOX:		delete((GroupBox*)dlg[i]->dialog);			break;
-				case SHEET:			delete((TabSheet*)dlg[i]->dialog);			break;
-				case ODBUTTON:		delete((ODbutton*)dlg[i]->dialog);			break;
-				case LISTBOX1:		delete((Listbox*)dlg[i]->dialog);			break;
-				case TREEVIEW:		delete((Treeview*)dlg[i]->dialog);			break;
-				case LINEPAT:		delete((LinePat*)dlg[i]->dialog);			break;
-				default:
-				//DEBUG: we should issue a message that an unknown item is
-				//   deleted: this might result in a memory leak;
-					InfoBox("unknown dialog object found\nin \"DlgRoot::~DlgRoot()\"");
-					delete(dlg[i]->dialog);
-					break;
-					}
-				}
-			free(dlg[i]);
-			}
-		free(dlg);					dlg=0L;
-		}
-	if(tabstops) free(tabstops);	tabstops = 0L;
-	DialogFocus = oldFocus;			DialogDefault = oldDefault;
-	DialogTabStop = oldTabStop;		CurrGO = c_go;
-	if(Undo.cdisp)Undo.cdisp->MouseCursor(MC_ARROW, false);
-}
-
-bool
-DlgRoot::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	Dialog *d;
-	int i, j;
-	RECT rc;
-
-	switch (cmd) {
-	case CMD_UNDO:
-		if(CurrDisp) {
-			Undo.Restore(false, CurrDisp);
-			DoPlot(CurrDisp);
-			}
-		return true;
-	case CMD_REDRAW:
-		if(CurrDisp) {
-			CurrDisp->Erase(DlgBGcolor);		DoPlot(CurrDisp);
-			CurrDisp->GetSize(&rc);				CurrDisp->UpdateRect(&rc, false);
-			}
-		return true;
-	case CMD_MOUSE_EVENT:
-		if(DialogFocus && DialogFocus->type == TEXTBOX && DialogFocus->Command(cmd, tmpl, o)) return true; 
-		mev = (MouseEvent *) tmpl;
-		switch(mev->Action) {
-		case MOUSE_LBDOWN:
-			bActive = true;
-		case MOUSE_MOVE:
-			//track mouse and display controls accordingly
-			if(!(mev->StateFlags & 1))return false;
-			if(bActive)ForEach(CMD_MOUSE_EVENT, 0, o);
-			return true;
-		case MOUSE_LBDOUBLECLICK:
-			ForEach(CMD_MOUSE_EVENT, 0, o);
-			bActive = false;				//skip next event (LB up);
-			return true;
-		case MOUSE_LBUP:
-			if(bActive)ForEach(CMD_LBUP, 0, o);
-			return true;
-			}
-		return true;
-	case CMD_ENDDIALOG:
-		d = (Dialog *)tmpl;
-		if(d) {
-			res_q[res_put++] = d->Id;		// end dialog by object
-			cContinue = 0;
-			}
-		else if(cContinue >0) {				// no end upon killing the focus
-			cContinue--;
-			return true;
-			}
-		else {
-			res_q[res_put++] = 0;			// end dialog with closebox or loose focus
-			bRedraw = true;
-			}
-		res_put &= 0xff;
-		return true;
-	case CMD_CONTINUE:
-		cContinue++;
-		return true;
-	case CMD_TABDLG:
-		if(tabstops) for (i = 0; i < cDlgs; i++) 
-			if(!tabstops[i] || tabstops[i] == (Dialog*)tmpl) {
-				tabstops[i] = (Dialog*)tmpl;
-				return true;
-				}
-		return false;
-	case CMD_NOTABDLG:
-		if(tabstops) for (i = j = 0; i < cDlgs; i++) {
-			if(tabstops[i] == (Dialog*)tmpl) tabstops[i] = 0L;
-			if(tabstops[i]) tabstops[j++] = tabstops[i];
-			}
-		return true;
-	case CMD_TAB:
-		HideTextCursor();
-		if(tabstops && DialogTabStop) {
-			for(i = 0; tabstops[i] && tabstops[i] != DialogTabStop && i < cDlgs; i++);
-			if((tabstops[i]) || (tabstops[i]->flags & HIDDEN)) i++;
-			if(!tabstops[i]) i = 0;
-			switch(tabstops[i]->type) {
-			case PUSHBUTTON:
-				d = DialogDefault;
-				DialogTabStop = DialogDefault = tabstops[i];
-				if(d) d->DoPlot(o);
-				DialogDefault->DoPlot(o);
-				break;
-			case EDTEXT:			case EDVAL1:	case RANGEINPUT:
-			case INCDECVAL1:
-				DialogTabStop = DialogFocus = tabstops[i];
-				if((InputText*)DialogFocus->bActive)
-					((InputText*)DialogFocus)->Activate(DialogFocus->Id, true);
-				else Command(cmd, tmpl, o);
-				break;
-				}
-			}
-		return true;
-	case CMD_SHTAB:
-		HideTextCursor();
-		if(tabstops && DialogTabStop) {
-			for(j = 0; tabstops[j]; j++);
-			for(i = j-1; tabstops[i] != DialogTabStop && i; i--);
-			i = i >0 ? i-1 : j-1;
-			switch(tabstops[i]->type) {
-			case PUSHBUTTON:
-				d = DialogDefault;
-				DialogTabStop = DialogDefault = tabstops[i];
-				d->DoPlot(o);
-				DialogDefault->DoPlot(o);
-				break;
-			case EDTEXT:			case EDVAL1:			case INCDECVAL1:
-			case RANGEINPUT:
-				DialogTabStop = DialogFocus = tabstops[i];
-				((InputText*)DialogFocus)->Activate(DialogFocus->Id, true);
-				break;
-				}
-			}
-		return true;
-	case CMD_CURRUP:	case CMD_CURRDOWN:
-		if(DialogFocus && DialogFocus->type == TEXTBOX)
-			return DialogFocus->Command(cmd, tmpl, o);
-        else return CurUpDown(cmd);
-	case CMD_CURRLEFT:	case CMD_CURRIGHT:	case CMD_DELETE:
-	case CMD_POS_FIRST:	case CMD_POS_LAST:	case CMD_SHIFTLEFT:
-	case CMD_SHIFTRIGHT:	case CMD_COPY:	case CMD_PASTE:
-		Undo.SetDisp(CurrDisp);
-		bActive = true;
-		if(DialogFocus)return DialogFocus->Command(cmd, tmpl, CurrDisp);
-        else return false;
-	case CMD_ADDCHAR:
-		if(!tmpl) return false;
-		bActive = true;
-		if(*((int*)tmpl) == 27) {						//Esc
-			HideCopyMark();
-			for (i = 0; dlg[i] && i < cDlgs; i++) 
-				if(dlg[i]->dialog) dlg[i]->dialog->Command(cmd, tmpl, o);
-			return Command(CMD_REDRAW, 0L, 0L);
-			}
-		if(DialogDefault && *((int*)tmpl) == 0x0d){		//return pressed
-			HideTextCursor();
-			return DialogDefault->Command(cmd, tmpl, o);
-			}
-		if(DialogFocus)return DialogFocus->Command(cmd, tmpl, o);
-        else return false;
-	case CMD_UNLOCK:
-		CurrDisp = 0L;
-		for(i = 0; i < cDlgs; i++)
-			if(dlg[i] && dlg[i]->dialog) dlg[i]->dialog->Command(CMD_UNLOCK, 0L, 0L);
-		break;
-	case CMD_MARKOBJ:
-		if(mrk_item && mrk_item != (Dialog*)tmpl){
-			i = 27;
-			mrk_item->Command(CMD_ADDCHAR, &i, o);
-			}
-		mrk_item = (Dialog*)tmpl;
-		break;
-		}
-	return false;
-}
-
-void
-DlgRoot::DoPlot(anyOutput *o)
-{
-	int i;
-
-	HideCopyMark();			mrk_item = 0L;			bRedraw = false;
-	HideTextCursor();
-	if(tabstops) for(i = 0; i < cDlgs; tabstops[i++] = 0);
-	if(o)CurrDisp = o;		DialogDefault = 0L;
-	if(CurrDisp) {
-		CurrDisp->SetTextSpec(&DlgText);
-		ForEach(CMD_DOPLOT, 0, CurrDisp);
-		}
-}
-
-bool
-DlgRoot::CurUpDown(int cmd)
-{
-	int i, ya, yb, dy;
-	Dialog *above=0L, *below=0L;
-
-	ya = -1000;		yb = 10000;
-	if(DialogFocus && tabstops && DialogTabStop == DialogFocus) {
-		for(i = 0; tabstops[i] && i < cDlgs; i++) {
-			if(tabstops[i] != DialogTabStop) {
-				switch(tabstops[i]->type) {
-				case EDVAL1:	case INCDECVAL1:	case EDTEXT:	case RANGEINPUT:
-					if(rTxtCur.left > tabstops[i]->cr.left && 
-						rTxtCur.right < tabstops[i]->cr.right) {
-						if((dy = (tabstops[i]->cr.top - rTxtCur.top))< 0) {
-							if(dy > ya) {
-								ya = dy;	above = tabstops[i];
-								}
-							}
-						else {
-							if(dy < yb) {
-								yb = dy;	below = tabstops[i];
-								}
-							}
-						}
-					break;
-					}
-				}
-			}
-		switch(cmd) {
-		case CMD_CURRUP:
-			if(above) {
-				above->Select(rTxtCur.left, (above->cr.top + above->cr.bottom)>>1, CurrDisp); 
-				}
-			break;
-		case CMD_CURRDOWN:
-			if(below) {
-				below->Select(rTxtCur.left, (below->cr.top + below->cr.bottom)>>1, CurrDisp); 
-				}
-			break;
-			}
-		}
-	return false;
-}
-
-bool
-DlgRoot::GetColor(int id, DWORD *color)
-{
-	int i;
-
-	i = FindIndex(id);
-	if(i && dlg[i] && dlg[i]->dialog) return dlg[i]->dialog->GetColor(id, color);
-	return false;
-}
-
-void
-DlgRoot::SetColor(int id, DWORD color)
-{
-	int i;
-
-	i = FindIndex(id);
-	if(i && dlg[i]) {
-		dlg[i]->dialog->SetColor(id, color);
-		if(CurrDisp && !(dlg[i]->dialog->flags & HIDDEN)) dlg[i]->dialog->DoPlot(CurrDisp);
-		}
-}
-
-bool
-DlgRoot::GetValue(int id, double *val)
-{
-	int i;
-
-	i = FindIndex(id);
-	if(i && dlg[i]) return dlg[i]->dialog->GetValue(id, val);
-	return false;
-}
-
-bool
-DlgRoot::GetInt(int id, int *val)
-{
-	int i;
-
-	i = FindIndex(id);
-	if(i && dlg[i]) return dlg[i]->dialog->GetInt(id, val);
-	return false;
-}
-
-bool
-DlgRoot::SetCheck(int id, anyOutput *o, bool state)
-{
-	int i;
-
-	i = FindIndex(id);
-	if(i && dlg[i]) return dlg[i]->dialog->SetCheck(id, o ? o : CurrDisp, state);
-	return false;
-}
-
-bool
-DlgRoot::GetCheck(int Id)
-{
-	int i;
-
-	i = FindIndex(Id);
-	if(i && dlg[i]) return dlg[i]->dialog->GetCheck(Id);
-	return false;
-}
-
-bool
-DlgRoot::GetText(int id, char *txt, int size)
-{
-	int i;
-
-	i = FindIndex(id);
-	if(i && dlg[i]) return dlg[i]->dialog->GetText(id, txt, size);
-	return false;
-}
-
-bool
-DlgRoot::SetText(int id, char *txt)
-{
-	int i;
-
-	i = FindIndex(id);
-	if(i && dlg[i] && dlg[i]->dialog->Command(CMD_SETTEXT, txt, CurrDisp))bRedraw = true;
-	else return false;
-	return true;
-}
-
-bool
-DlgRoot::SetValue(int id, double val)
-{
-	int i;
-	char tmp_txt[80];
-
-	i = FindIndex(id);
-	WriteNatFloatToBuff(tmp_txt, val);
-	if(i && dlg[i] && dlg[i]->dialog->Command(CMD_SETTEXT, tmp_txt+1, CurrDisp))bRedraw = true;
-	else return false;
-	return true;
-}
-
-
-bool
-DlgRoot::TextStyle(int id, int style)
-{
-	int i;
-
-	i = FindIndex(id);
-	if(i && dlg[i]) dlg[i]->dialog->TextDef.Style = style;
-	else return false;
-	return true;
-}
-
-bool
-DlgRoot::TextFont(int id, int font)
-{
-	int i;
-
-	i = FindIndex(id);
-	if(i && dlg[i]) dlg[i]->dialog->TextDef.Font = font;
-	else return false;
-	return true;
-}
-
-bool
-DlgRoot::TextSize(int id, int size)
-{
-	int i;
-
-	i = FindIndex(id);
-	if(size <= 0.001f) return false;
-	if(i && dlg[i]) dlg[i]->dialog->TextDef.iSize = size;
-	else return false;
-	return true;
-}
-
-bool
-DlgRoot::ShowItem(int id, bool show)
-{
-	int i;
-
-	i = FindIndex(id);
-	if(i && dlg[i]) dlg[i]->dialog->flags = show ? 
-		dlg[i]->dialog->flags & ~HIDDEN : dlg[i]->dialog->flags | HIDDEN;
-	else return false;
-	return true;
-}
-
-int
-DlgRoot::GetResult()
-{
-	int ret;
-
-	if(res_put != res_get) ret = res_q[res_get++];
-	else ret = -1;
-	res_get &= 0xff;
-	if(bRedraw)DoPlot(0L);
-	if(ret >= 0 && ParentOut) Undo.SetDisp(ParentOut);
-	return ret;
-}
-
-int
-DlgRoot::FindIndex(unsigned short id)
-{
-	int i;
-
-	for (i = 0; dlg[i] && i < cDlgs; i++) if(dlg[i]->id == id) return i;
-	return 0;
-}
-
-void
-DlgRoot::ForEach(int cmd, int start, anyOutput *o)
-{
-	int i, j, next;
-
-	if(o)CurrDisp = o;
-	if(dlg && CurrDisp) {
-		next = start;
-		do {
-			if(dlg[next] && dlg[next]->first) {
-				if(dlg[next]->flags && ISPARENT) {
-					if(dlg[next]->dialog) {
-						dlg[next]->dialog->Command(CMD_FLUSH, 0L, 0L);
-						//if j equals cDlgs we are caught in a circular reference
-						for(j = 0, i = dlg[next]->first; i && j < cDlgs; j++) {
-							if(i = FindIndex(i)) {
-								dlg[next]->dialog->Command(CMD_ADDCHILD, (void*)dlg[i]->dialog, 0L);
-								i = dlg[i]->next;
-								}
-							else{
-								i=i;
-								}
-							}
-						}
-					else return;	//error bad structured template
-					}
-				else {	//a debugging aid ....
-					InfoBox("Warning:\ndialog contains\ngroup which is not parent");
-					}
-				//resolve sub-groups recursively
-				//  this will not result in any action for children 
-				//  because the parent is not this!
-				ForEach(cmd, FindIndex(dlg[next]->first), 0L);
-				}
-			//parent objects (groups) will channel command to children
-			if(dlg[next] && dlg[next]->dialog && dlg[next]->dialog->parent == this && 
-				!(dlg[next]->dialog->flags & HIDDEN)) switch(cmd) {
-				case CMD_DOPLOT: 
-					dlg[next]->dialog->DoPlot(CurrDisp);
-					break;
-				case CMD_LBUP:
-					dlg[next]->dialog->Select(mev->x, mev->y, CurrDisp);
-					break;
-				case CMD_MOUSE_EVENT:
-					dlg[next]->dialog->MBtrack(mev, CurrDisp);
-					break;
-				case CMD_SELECT:
-					dlg[next]->dialog->Select(mev->x, mev->y, CurrDisp);
-					break;
-				}
-			next = FindIndex(dlg[next] ? dlg[next]->next : 0);
-			}while(next && next < cDlgs);
-		}
-}
-
-void
-DlgRoot::Activate(int id, bool active)
-{
-	int i;
-
-	i = FindIndex(id);
-	if(i && dlg[i]) dlg[i]->dialog->Activate(id, active);
-}
-
-bool
-DlgRoot::ItemCmd(int id, int cmd, void *tmpl)
-{
-	int i;
-
-	if((i = FindIndex(id)) && dlg[i]){ 
-		bRedraw = true;
-		return dlg[i]->dialog->Command(cmd, tmpl, CurrDisp);
-		}
-	return false;
-}
-
-Dialog::Dialog(tag_DlgObj *par, DlgInfo *desc, RECT rec)
-{
-	parent = par;
-	Id = desc->id;
-	flags = desc->flags;
-	memcpy(&cr, &rec, sizeof(RECT));	memcpy(&hcr, &rec, sizeof(RECT));
-	Line.width = 0.0;					Line.patlength = 1.0;
-	Line.color = DlgBGcolor;			Line.pattern = 0x00000000L;
-	Fill.type = FILL_NONE;				Fill.color = DlgBGcolor;
-	Fill.scale = 1.0;					Fill.hatch = 0L;
-	memcpy(&TextDef, &DlgText, sizeof(TextDEF));
-	type = desc->type;
-	bChecked = flags & CHECKED ? true : false;
-	bLBdown = false;
-	if(DEFAULT == (flags & DEFAULT)) DialogDefault = DialogTabStop = this;
-	bActive = true;
-}
-
-bool
-Dialog::Select(int x, int y, anyOutput *o)
-{
-	if(x > cr.left && x < cr.right && y > cr.top && y < cr.bottom) {
-		if((flags & TOUCHEXIT) && parent)
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		return true;
-		}
-	return false;
-}
-
-bool 
-Dialog::SetCheck(int id, anyOutput *o, bool state)
-{
-	if(state != bChecked) {
-		if(parent && state && (flags & ISRADIO)) parent->Command(CMD_RADIOBUTT, (void *)this, o);
-		bChecked = state;
-		if(o) DoPlot(o);
-		return true;
-		}
-	return false;
-}
-
-void
-Dialog::MBtrack(MouseEvent *mev, anyOutput *o)
-{
-	bool bLBstate = false;
-
-	if(mev->Action == MOUSE_LBDOUBLECLICK) {
-		Select(mev->x, mev->y, o);
-		return;
-		}
-	switch(type){
-	case PUSHBUTTON:	case ARROWBUTT:		case CHECKBOX:		case RADIO1:
-	case RADIO2:		case TRASH:			case CONFIG:		case CHECKPIN:
-		if(mev->StateFlags &1) bLBstate = true;
-		if(IsInRect(&hcr, mev->x, mev->y) && bLBstate){
-			if(parent && type != CHECKBOX && type != RADIO1 && type != RADIO2) parent->Command(CMD_MARKOBJ, this, o);
-			if(!bLBdown){
-				bLBdown = bLBstate;			DoPlot(o);
-				return;
-				}
-			}
-		else if(bLBdown){
-			bLBdown = false;
-			DoPlot(o);
-			}
-		break;
-		}
-}
-
-void
-Dialog::Activate(int id, bool active)
-{
-	if(id == Id) bActive = active;
-}
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Collection of dialog items
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// a simple text button
-PushButton::PushButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
-	:Dialog(par, desc, rec)
-{
-	if(text && text[0]) Text = (char*)memdup(text, (int)strlen(text)+1, 0);
-	else Text = 0L;
-	TextDef.Align = TXA_HCENTER | TXA_VCENTER;
-}
-
-PushButton::~PushButton()
-{
-	if(Text) free (Text);
-}
-
-bool
-PushButton::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch(cmd) {
-	case CMD_ENDDIALOG:
-		parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		return true;
-	case CMD_ADDCHAR:
-		HideCopyMark();
-		if(parent && *((int*)tmpl) == 0x0d)		//return pressed
-			 parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		return true;
-		}
-	return false;
-}
-
-void
-PushButton::DoPlot(anyOutput *o)
-{
-	POINT pts[3];
-
-	Line.color = 0x00000000L;			Fill.color = DlgBGhigh;
-	o->SetLine(&Line);					o->SetFill(&Fill);
-	if(bLBdown) o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
-	else {
-		o->oRectangle(cr.left, cr.top, cr.right-1, cr.bottom-1);
-		Line.color = DlgBGcolor;
-		o->SetLine(&Line);
-		pts[0].x = cr.left;					pts[0].y = pts[1].y = cr.bottom-1;
-		pts[1].x = pts[2].x = cr.right-1;	pts[2].y = cr.top-1;
-		o->oPolyline(pts, 3);
-		Line.color = 0x00000000L;
-		o->SetLine(&Line);
-		pts[0].x = cr.left+5;				pts[0].y = pts[1].y = cr.bottom-1;
-		pts[1].x = pts[2].x = cr.right-1;	pts[2].y = cr.top +1;
-		o->oPolyline(pts, 3);
-		Line.color = 0x00ffffffL;
-		o->SetLine(&Line);
-		pts[0].x = pts[1].x = cr.left;
-		pts[0].y = cr.bottom -3;
-		pts[1].y = pts[2].y = cr.top;
-		pts[2].x = cr.right -2;
-		o->oPolyline(pts, 3);
-		}
-	if(Text) {
-		TextDef.Style = DialogDefault == this ? TXS_BOLD : TXS_NORMAL;
-		o->SetTextSpec(&TextDef);
-		o->oTextOut((cr.left + cr.right)/2-2, (cr.top + cr.bottom)/2-1, Text, 0);
-		}
-	o->UpdateRect(&cr, false);
-	if(parent)parent->Command(CMD_TABDLG, this, o);
-}
-
-bool
-PushButton::Select(int x, int y, anyOutput *o)
-{
-	if(IsInRect(&cr, x, y)) {
-		bLBdown = false;		DoPlot(o);
-		if(parent) parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		return true;
-		}
-	else if(bLBdown) {
-		bLBdown = false;		DoPlot(o);
-		}
-	return false;
-}
-
-TextBox::TextBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
-	:Dialog(par, desc, rec)
-{
-	lfPOINT lfp1, lfp2;
-
-	TextDef.Font = FONT_COURIER;	TextDef.Align = TXA_VBOTTOM | TXA_HLEFT;
-#ifdef _WINDOWS
-//	TextDef.fSize unchanged
-#else
-	TextDef.fSize *= .8;
-#endif
-	Fill.color = 0x00ffffffL;	Line.color = 0x00000000L;	cont = 0L;
-	lfp1.fx = rec.left;				lfp1.fy = rec.top;
-	lfp2.fx = rec.right;			lfp2.fy = rec.bottom;
-	if(cont = new TextFrame(0L, 0L, &lfp1, &lfp2, text)) cont->Command(CMD_SETTEXTDEF, &TextDef, 0L);
-}
-
-TextBox::~TextBox()
-{
-	if(cont)DeleteGO(cont);
-}
-
-bool
-TextBox::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch(cmd) {
-	case CMD_CURRLEFT:		case CMD_CURRIGHT:		case CMD_DELETE:
-	case CMD_POS_FIRST:		case CMD_POS_LAST:		case CMD_SHIFTLEFT:
-	case CMD_SHIFTRIGHT:	case CMD_ADDCHAR:		case CMD_MOUSE_EVENT:
-	case CMD_COPY:			case CMD_PASTE:			case CMD_CURRUP:
-	case CMD_CURRDOWN:
-		if(bChecked && CurrGO && CurrGO == cont) return CurrGO->Command(cmd, tmpl, o);
-		break;
-	case CMD_SETTEXT:
-		if(cont)return cont->Command(cmd, tmpl, o);
-		return false;
-		}
-	return false;
-}
-
-void
-TextBox::DoPlot(anyOutput *o)
-{
-	if(cont && o)cont->DoPlot(o);
-}
-
-bool
-TextBox::Select(int x, int y, anyOutput *o)
-{
-	POINT p1;
-
-	p1.x = x;			p1.y = y;
-	if(bActive && IsInRect(&cr, x, y) && !(flags & NOEDIT)) {
-		DialogDefault = DialogFocus = DialogTabStop = this;
-		if(CurrGO = cont) bChecked = cont->Command(CMD_SELECT, &p1, o);
-		}
-	return false;
-}
-
-void
-TextBox::MBtrack(MouseEvent *mev, anyOutput *o)
-{
-	bool bLBstate;
-	int x, y;
-
-	x = mev->x;						y = mev->y;
-	if(mev->StateFlags &1) bLBstate = true;
-	else bLBstate = false;
-	if(bActive && IsInRect(&cr, x, y) && !(flags & NOEDIT) && 
-		bLBstate && cont && parent) {
-		DialogFocus = this;			bChecked = true;
-		parent->Command(CMD_FOCTXT, (void*)this, 0L);
-		cont->Command(CMD_MOUSE_EVENT, mev, o);
-		parent->Command(CMD_MARKOBJ, this, o);
-		}
-}
-
-bool
-TextBox::GetText(int id, char *txt, int size)
-{
-	if(cont) {
-		if(!(cont->Command(CMD_ALLTEXT, TmpTxt, 0L))) return false;
-		rlp_strcpy(txt, size, TmpTxt);
-		return true;
-		}
-	return false;
-}
-
-ArrowButton::ArrowButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, int *which)
-	:Dialog(par, desc, rec)
-{
-	direct = which ? *which : 0;
-}
-
-void
-ArrowButton::DoPlot(anyOutput *o)
-{
-	POINT pts[4];
-	int ix, iy, dx, dy;
-
-	Line.color = 0x00000000L;
-	Fill.color = DlgBGhigh;
-	ix = (dx =(cr.right-cr.left))>5 ? dx>>2 : 2;
-	iy = (dy =(cr.bottom-cr.top))>5 ? dy>>2 : 2;
-	o->SetLine(&Line);						o->SetFill(&Fill);
-	if(bLBdown) o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
-	else {
-		o->oRectangle(cr.left, cr.top, cr.right-1, cr.bottom-1);
-		Line.color = DlgBGcolor;
-		o->SetLine(&Line);
-		pts[0].x = cr.left;					pts[0].y = pts[1].y = cr.bottom-1;
-		pts[1].x = pts[2].x = cr.right-1;	pts[2].y = cr.top-1;
-		o->oPolyline(pts, 3);				Line.color = 0x00ffffffL;
-		o->SetLine(&Line);					pts[0].x = pts[1].x = cr.left;
-		pts[0].y = cr.bottom -3;			pts[1].y = pts[2].y = cr.top;
-		pts[2].x = cr.right -2;				o->oPolyline(pts, 3);
-		}
-	Fill.color = Line.color = 0x00000000L;
-	o->SetLine(&Line);						o->SetFill(&Fill);
-	switch(direct) {
-	case 1:
-		pts[0].x = pts[3].x = cr.left+ix;	pts[0].y = pts[3].y = pts[1].y = cr.bottom-(iy<<1);
-		pts[1].x = cr.right-(ix<<1);		pts[2].x = (cr.right + cr.left)/2 -1;
-		pts[2].y = cr.top+iy;				o->oPolygon(pts, 4);
-		break;
-	case 2:
-		pts[0].x = pts[3].x = cr.left+ix;	pts[0].y = pts[3].y = pts[1].y = cr.top+iy;
-		pts[1].x = cr.right-(ix<<1);		pts[2].x = (cr.right + cr.left)/2 -1;
-		pts[2].y = cr.bottom-(iy<<1);		o->oPolygon(pts, 4);
-		break;
-	case 3:
-		pts[0].x = pts[3].x = cr.left+ix;	pts[0].y = pts[3].y = (cr.bottom + cr.top)/2-1;
-		pts[1].x = pts[2].x = cr.right-(ix<<1);
-		pts[1].y = cr.bottom-(iy<<1);		pts[2].y = cr.top+iy;
-		o->oPolygon(pts, 4);
-		break;
-	case 4:
-		pts[0].x = pts[3].x = cr.right-(ix<<1);
-		pts[0].y = pts[3].y = (cr.bottom + cr.top)/2-1;
-		pts[1].x = pts[2].x = cr.left+2;	pts[1].y = cr.bottom-(iy<<1);
-		pts[2].y = cr.top+iy;				o->oPolygon(pts, 4);
-		break;
-		}
-	o->UpdateRect(&cr, false);
-}
-
-bool
-ArrowButton::Select(int x, int y, anyOutput *o)
-{
-	if(IsInRect(&cr, x, y)) {
-		bLBdown = false;					DoPlot(o);
-		if(parent) parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		return true;
-		}
-	else if(bLBdown) {
-		bLBdown = false;					DoPlot(o);
-		}
-	return false;
-}
-
-
-ColorButton::ColorButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, DWORD *color)
-	:Dialog(par, desc, rec)
-{
-	col = color ? *color : 0x0L;
-}
-
-void
-ColorButton::DoPlot(anyOutput *o)
-{
-	POINT pts[5];
-
-	Fill.color = col;
-	o->SetFill(&Fill);
-	if(flags & ISRADIO) {
-		Line.color = bChecked ? 0x00000000L : DlgBGcolor;
-		o->SetLine(&Line);
-		pts[0].x = pts[3].x = pts[4].x = cr.left;
-		pts[0].y = pts[1].y = pts[4].y = cr.top;
-		pts[1].x = pts[2].x = cr.right-1;
-		pts[2].y = pts[3].y = cr.bottom-1;
-		o->oPolyline(pts, 5);
-		Line.color = 0x00000000L;
-		o->SetLine(&Line);
-		o->oRectangle(cr.left+3, cr.top+3, cr.right-3, cr.bottom-3);
-		}
-	else {
-		Line.color = 0x00000000L;
-		o->SetLine(&Line);
-		o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
-		}
-	o->UpdateRect(&cr, false);
-}
-
-bool
-ColorButton::Select(int x, int y, anyOutput *o)
-{
-	if(!parent) return false;
-	if(IsInRect(&cr, x, y)) {
-		bChecked = true;
-		if((flags & OWNDIALOG)) {
-			parent->Command(CMD_CONTINUE, 0L, o);
-			col = GetNewColor(col);
-			}
-		if(flags & ISRADIO) parent->Command(CMD_RADIOBUTT, (void *)this, o);
-		if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		DoPlot(o);
-		return true;
-		}
-	return false;
-}
-
-FillButton::FillButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, FillDEF *fill)
-	:Dialog(par, desc, rec)
-{
-	CurrFill = fill;
-}
-
-void
-FillButton::DoPlot(anyOutput *o)
-{
-	Line.color = 0x00000000L;
-	o->SetLine(&Line);
-	o->SetFill(CurrFill);
-	o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
-	o->UpdateRect(&cr, false);
-}
-
-bool
-FillButton::Select(int x, int y, anyOutput *o)
-{
-	if(IsInRect(&cr, x, y)) {
-		if((flags & OWNDIALOG)) {
-			if(parent)parent->Command(CMD_CONTINUE, 0L, o);
-			GetNewFill(CurrFill);
-			}
-		if((flags & TOUCHEXIT) && parent) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		DoPlot(o);
-		return true;
-		}
-	return false;
-}
-
-Shade3D::Shade3D(tag_DlgObj *par, DlgInfo * desc, RECT rec, FillDEF *fill)
-	:Dialog(par, desc, rec)
-{
-	CurrFill = fill;
-}
-
-void
-Shade3D::DoPlot(anyOutput *o)
-{
-	POINT pts[5];
-	FillDEF fd;
-	int dx;
-
-	Line.color = 0x00000000L;
-	o->SetLine(&Line);
-	if(CurrFill->type & FILL_LIGHT3D) {
-		fd.color = fd.color2 = CurrFill->color;
-		fd.hatch = 0L;		fd.scale = 1.0;
-		fd.type = 0L;
-		dx = iround(((double)(cr.bottom - cr.top))*.26);
-		pts[0].x = pts[1].x = pts[4].x = ((cr.left + cr.right)>>1);
-		pts[0].y = pts[4].y = cr.bottom;
-		pts[1].y = ((cr.top + cr.bottom)>>1);
-		pts[2].x = pts[3].x = pts[0].x + (dx<<1);
-		pts[2].y = pts[1].y - dx;		pts[3].y = pts[0].y - dx;
-		o->SetFill(&fd);
-		o->oPolygon(pts, 5, 0L);
-		fd.color = fd.color2 = IpolCol(CurrFill->color, CurrFill->color2, 0.4);
-		pts[2].x = pts[3].x = pts[0].x - (dx<<1);
-		o->SetFill(&fd);
-		o->oPolygon(pts, 5, 0L);
-		fd.color = fd.color2 = CurrFill->color2;
-		pts[0].y = pts[4].y = pts[1].y;
-		pts[1].x = pts[2].x;			pts[1].y = pts[3].y = pts[2].y;
-		pts[2].x = pts[0].x;			pts[2].y -= dx;
-		pts[3].x = pts[0].x + (dx<<1);
-		o->SetFill(&fd);
-		o->oPolygon(pts, 5, 0L);
-		}
-	else {
-		o->SetFill(CurrFill);
-		o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
-		}
-	o->UpdateRect(&cr, false);
-}
-
-void ConfShade(FillDEF *oldfill);
-bool
-Shade3D::Select(int x, int y, anyOutput *o)
-{
-	if(IsInRect(&cr, x, y)) {
-		if((flags & OWNDIALOG)) {
-			if(parent)parent->Command(CMD_CONTINUE, 0L, o);
-			ConfShade(CurrFill);
-			}
-		if((flags & TOUCHEXIT) && parent) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		DoPlot(o);
-		return true;
-		}
-	return false;
-}
-
-LineButton::LineButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, LineDEF *line)
-	:Dialog(par, desc, rec)
-{
-	Line.color = 0x00000000L;
-	CurrLine = line;
-	Fill.color = defs.Color(COL_BG);
-	pts[0].x = cr.left+4;
-	pts[1].x = cr.right-5;
-	pts[0].y = pts[1].y = (cr.top + cr.bottom)/2;
-}
-
-void
-LineButton::DoPlot(anyOutput *o)
-{
-	o->SetLine(&Line);
-	o->SetFill(&Fill);
-	o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
-	o->SetLine(CurrLine);
-	o->oPolyline(pts, 2);
-	o->UpdateRect(&cr, false);
-}
-
-bool
-LineButton::Select(int x, int y, anyOutput *o)
-{
-	if(IsInRect(&cr, x, y)) {
-		if((flags & TOUCHEXIT) && parent) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		DoPlot(o);
-		return true;
-		}
-	return false;
-}
-
-SymButton::SymButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, Symbol **sym)
-	:Dialog(par, desc, rec)
-{
-	symbol = sym;
-	Line.color = 0x00000000L;
-	Fill.color = 0x00ffffffL;
-}
-
-void
-SymButton::DoPlot(anyOutput *o)
-{
-	Line.color = 0x00000000L;
-	o->SetLine(&Line);
-	o->SetFill(&Fill);
-	o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
-	if(*(symbol)) {		//center symbol in the rectangle
-		(*symbol)->SetSize(SIZE_XPOS, (cr.right+cr.left)/2.0);
-		(*symbol)->SetSize(SIZE_YPOS, (cr.bottom+cr.top)/2.0);
-		(*symbol)->DoPlot(o);
-		}
-	o->UpdateRect(&cr, false);
-}
-
-bool
-SymButton::Select(int x, int y, anyOutput *o)
-{
-	if(parent && IsInRect(&cr, x, y)) {
-		if((flags & OWNDIALOG) && (*(symbol))) {
-			parent->Command(CMD_CONTINUE, 0L, o);
-			(*symbol)->PropertyDlg();
-			}
-		if((flags & TOUCHEXIT)) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		DoPlot(o);
-		return true;
-		}
-	return false;
-}
-
-FillRadioButt::FillRadioButt(tag_DlgObj *par, DlgInfo * desc, RECT rec, unsigned int pattern)
-	:Dialog(par, desc, rec)
-{
-	Line.pattern = 0x00000000L;			Fill.type = pattern;
-	Fill.color = 0x00ffffffL;			Fill.hatch = &Line;
-	flags |= ISRADIO;
-}
-
-void
-FillRadioButt::DoPlot(anyOutput *o)
-{
-	POINT pts[5];
-
-	Line.color = bChecked ? 0x00000000L : DlgBGcolor;
-	o->SetLine(&Line);
-	pts[0].x = pts[3].x = pts[4].x = cr.left;
-	pts[0].y = pts[1].y = pts[4].y = cr.top;
-	pts[1].x = pts[2].x = cr.right-1;
-	pts[2].y = pts[3].y = cr.bottom-1;
-	o->oPolyline(pts, 5);
-	Line.color = 0x00000000L;
-	o->SetLine(&Line);
-	o->SetFill(&Fill);
-	o->oRectangle(cr.left+3, cr.top+3, cr.right-3, cr.bottom-3);
-	o->UpdateRect(&cr, false);
-}
-
-bool
-FillRadioButt::Select(int x, int y, anyOutput *o)
-{
-	if(IsInRect(&cr, x, y)) {
-		bChecked = true;
-		DoPlot(o);
-		if(parent) parent->Command(CMD_RADIOBUTT, (void *)this, o);
-		if((flags & TOUCHEXIT) && parent) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		return true;
-		}
-	return false;
-}
-
-SymRadioButt::SymRadioButt(tag_DlgObj *par, DlgInfo * desc, RECT rec, int* type)
-	:Dialog(par, desc, rec)
-{
-	double size;
-
-	if(type && (Sym = new Symbol(0L, 0L, ((double)(cr.right+cr.left))/2.0, 
-		((double)(cr.bottom+cr.top))/2.0, *type))){
-		size = (0.14*(double)(cr.bottom-cr.top))/Units[defs.cUnits].convert;
-		Sym->SetSize(SIZE_SYMBOL, size);			Sym->SetSize(SIZE_SYM_LINE, size/10.0);
-		Sym->SetColor(COL_SYM_LINE, 0x00000000L);	Sym->SetColor(COL_SYM_FILL, 0x00ffffffL);
-		}
-	Fill.color = DlgBGhigh;		flags |= ISRADIO;
-}
-
-SymRadioButt::~SymRadioButt()
-{
-	if (Sym) delete Sym;
-}
-
-void
-SymRadioButt::DoPlot(anyOutput *o)
-{
-	if(!Sym) return;
-	Line.color = bChecked ? 0x00000000L : Fill.color;
-	o->SetFill(&Fill);
-	o->SetLine(&Line);
-	o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
-	Sym->DoPlot(o);
-	o->UpdateRect(&cr, false);
-}
-
-bool
-SymRadioButt::Select(int x, int y, anyOutput *o)
-{
-	if(IsInRect(&cr, x, y)) {
-		bChecked = true;
-		DoPlot(o);
-		if(parent) parent->Command(CMD_RADIOBUTT, (void *)this, o);
-		if((flags & TOUCHEXIT) && parent) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		return true;
-		}
-	return false;
-}
-
-CheckBox::CheckBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
-	:Dialog(par, desc, rec)
-{
-	if(text && text[0])Text = (char*)memdup(text, (int)strlen(text)+1, 0);
-	else Text = 0L;
-	hcr.left = cr.left+2;		hcr.right = cr.left+14;
-	hcr.top = cr.top+3;			hcr.bottom = cr.top+15;
-}
-
-CheckBox::~CheckBox()
-{
-	if(Text) free (Text);
-}
-
-bool
-CheckBox::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch(cmd) {
-	case CMD_SETTEXT:
-		if(Text) free(Text);
-		if(tmpl && *((char*)tmpl)) Text = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1,0);
-		else Text = 0L;
-		return true;
-		}
-	return false;
-}
-
-void
-CheckBox::DoPlot(anyOutput *o)
-{
-	POINT pts[3];
-
-	if(flags & HIDDEN) return;
-	Line.color = 0x00000000L;
-	Line.width = 0.0;
-	Fill.color = bLBdown ? DlgBGcolor : 0x00ffffffL;
-	o->SetLine(&Line);
-	o->SetFill(&Fill);
-	o->oRectangle(cr.left+2, cr.top+3, cr.left+14, cr.top+15);
-	if(bChecked) {
-		Line.width = defs.GetSize(SIZE_SYM_LINE)*2.0;
-		o->SetLine(&Line);
-		pts[0].x = cr.left+4;			pts[0].y = cr.top+5;
-		pts[1].x = cr.left+11;			pts[1].y = cr.top+12;
-		o->oSolidLine(pts);
-		pts[0].x = cr.left+11;			pts[1].x = cr.left+4;
-		o->oSolidLine(pts);
-		}
-	if(Text) {
-		o->SetTextSpec(&TextDef);
-		o->oTextOut(cr.left + 20, cr.top + 1, Text, (int)strlen(Text));
-		}
-	o->UpdateRect(&cr, false);
-}
-
-bool
-CheckBox::Select(int x, int y, anyOutput *o)
-{
-	if(!(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
-		bChecked ^= 0x01;	//toggle selection
-		bLBdown = false;
-		DoPlot(o);
-		if((flags & ISRADIO) && parent)
-			parent->Command(CMD_RADIOBUTT, (void *)this, o);
-		if((flags & TOUCHEXIT) && parent) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		return true;
-		}
-	return false;
-}
-
-CheckPin::CheckPin(tag_DlgObj *par, DlgInfo * desc, RECT rec)
-	:Dialog(par, desc, rec)
-{
-	hcr.left = cr.left+2;		hcr.right = cr.right-2;
-	hcr.top = cr.top+2;			hcr.bottom = cr.bottom-2;
-}
-
-CheckPin::~CheckPin()
-{
-}
-
-bool
-CheckPin::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch(cmd) {
-	case CMD_UNLOCK:
-		bChecked = false;
-		return true;
-		}
-	return false;
-}
-
-void
-CheckPin::DoPlot(anyOutput *o)
-{
-	POINT pts[20];
-
-	if(!o || flags & HIDDEN) return;
-	Line.width = 0.0;
-	Line.color = DlgBGcolor;		Fill.color = DlgBGcolor;
-	o->SetLine(&Line);		o->SetFill(&Fill);
-	o->oRectangle(hcr.left+2, hcr.top, hcr.right, hcr.bottom);
-	Line.color = 0x00000000L;		Fill.color = bLBdown ? 0x00ffffff : 0x00e8e8e8L;
-	o->SetLine(&Line);		o->SetFill(&Fill);
-	if(bChecked) {
-		o->oCircle(hcr.left + 5, hcr.top + 1, hcr.left + 15, hcr.top + 12);
-		o->oCircle(hcr.left + 8, hcr.top + 3, hcr.left + 15, hcr.top + 10);
-		}
-	else {
-		pts[0].x = hcr.left + 8;	pts[0].y = hcr.top + 4;
-		pts[1].x = pts[0].x - 5;	pts[1].y = pts[0].y + 1;
-		pts[2].x = pts[0].x;		pts[2].y = pts[1].y + 1;
-		pts[3].x = pts[0].x;		pts[3].y = pts[0].y;
-		o->oPolygon(pts, 4);
-		pts[0].x = hcr.left + 8;	pts[0].y = hcr.top + 1;
-		pts[1].x = pts[0].x;		pts[1].y = pts[0].y + 9;
-		pts[2].x = pts[1].x + 3;	pts[2].y = pts[1].y - 3;
-		pts[3].x = pts[2].x + 4;	pts[3].y = pts[2].y;
-		pts[4].x = pts[3].x;		pts[4].y = pts[3].y - 3;
-		pts[5].x = pts[4].x - 4;	pts[5].y = pts[4].y;
-		pts[6].x = pts[0].x;		pts[6].y = pts[0].y;
-		o->oPolygon(pts, 7);
-		o->oCircle(pts[4].x-2, pts[0].y, pts[4].x+3, pts[1].y+1);
-		}
-	o->UpdateRect(&cr, false);
-}
-
-bool
-CheckPin::Select(int x, int y, anyOutput *o)
-{
-	if(!(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
-		bChecked ^= 0x01;	//toggle selection
-		bLBdown = false;	DoPlot(o);
-		if((flags & TOUCHEXIT) && parent) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		bLBdown=false;				DoPlot(o);
-		return true;
-		}
-	return false;
-}
-
-Trash::Trash(tag_DlgObj *par, DlgInfo * desc, RECT rec)
-	:Dialog(par, desc, rec)
-{
-	hcr.left = cr.left+2;		hcr.right = cr.right-2;
-	hcr.top = cr.top+2;			hcr.bottom = cr.bottom-2;
-}
-
-Trash::~Trash()
-{
-}
-
-bool
-Trash::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	return false;
-}
-
-void
-Trash::DoPlot(anyOutput *o)
-{
-	POINT pts[2];
-
-	if(!o || flags & HIDDEN) return;
-	Line.width = 0.0;
-	Line.color = DlgBGcolor;		Fill.color = DlgBGcolor;
-	o->SetLine(&Line);		o->SetFill(&Fill);
-	o->oRectangle(hcr.left+2, hcr.top, hcr.right, hcr.bottom);
-	Line.color = 0x00000000L;		Fill.color = bLBdown ? 0x00ffffff : 0x00e8e8e8L;
-	o->SetLine(&Line);		o->SetFill(&Fill);
-	o->oRectangle(cr.left+8, cr.top+10, cr.right-8, cr.bottom-3);
-	o->oRectangle(cr.left+6, cr.top+5, cr.right-6, cr.top+10);
-	o->oRectangle(cr.left+11, cr.top+2, cr.right-11, cr.top+5);
-	pts[0].y = cr.top + 12;						pts[1].y = cr.bottom - 5;
-	pts[0].x = pts[1].x = cr.left + 12;			o->oSolidLine(pts);
-	pts[0].x = pts[1].x = cr.right - 13;		o->oSolidLine(pts);
-	o->UpdateRect(&cr, false);
-}
-
-bool
-Trash::Select(int x, int y, anyOutput *o)
-{
-	if(!(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
-		if((flags & TOUCHEXIT) && parent) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		bLBdown=false;				DoPlot(o);
-		return true;
-		}
-	return false;
-}
-
-Config::Config(tag_DlgObj *par, DlgInfo * desc, RECT rec)
-	:Dialog(par, desc, rec)
-{
-	hcr.left = cr.left+2;		hcr.right = cr.right-2;
-	hcr.top = cr.top+2;			hcr.bottom = cr.bottom-2;
-}
-
-Config::~Config()
-{
-}
-
-bool
-Config::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	return false;
-}
-
-void
-Config::DoPlot(anyOutput *o)
-{
-	POINT pts[2];
-
-	if(!o || flags & HIDDEN) return;
-	Line.width = 0.0;
-	Line.color = DlgBGcolor;		Fill.color = DlgBGcolor;
-	o->SetLine(&Line);		o->SetFill(&Fill);
-	o->oRectangle(hcr.left+2, hcr.top, hcr.right, hcr.bottom);
-	Line.color = 0x00000000L;		Fill.color = bLBdown ? 0x00ffffff : 0x00e8e8e8L;
-	o->SetLine(&Line);		o->SetFill(&Fill);
-	o->oRectangle(cr.left+3, cr.top+3, cr.right-3, cr.bottom-3);
-	o->oRectangle(cr.left+8, cr.top+6, cr.left+11, cr.top+9);
-	o->oRectangle(cr.left+8, cr.top+10, cr.left+11, cr.top+13);
-	o->oRectangle(cr.left+8, cr.top+14, cr.left+11, cr.top+17);
-	o->oRectangle(cr.left+7, cr.top+18, cr.right-7, cr.bottom-5);
-	pts[0].x = cr.left + 14;					pts[1].x = cr.right - 8;
-	pts[0].y = pts[1].y = cr.top + 7;			o->oSolidLine(pts);
-	pts[0].y = pts[1].y = cr.top + 11;			o->oSolidLine(pts);
-	pts[0].y = pts[1].y = cr.top + 15;			o->oSolidLine(pts);
-	o->UpdateRect(&cr, false);
-}
-
-bool
-Config::Select(int x, int y, anyOutput *o)
-{
-	if(!(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
-		if((flags & TOUCHEXIT) && parent) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		bLBdown=false;				DoPlot(o);
-		return true;
-		}
-	return false;
-}
-
-RadioButton::RadioButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
-	:Dialog(par, desc, rec)
-{
-	if(text && text[0])Text = (char*)memdup(text, (int)strlen(text)+1, 0);
-	else Text = 0L;
-	switch(type) {
-	case RADIO0:
-		hcr.left = cr.left+4;		hcr.right = cr.left+13;
-		hcr.top = cr.top+4;			hcr.bottom = cr.top+13;
-		break;
-	case RADIO1:
-		hcr.left = cr.left+2;		hcr.right = cr.left+15;
-		hcr.top = cr.top+3;			hcr.bottom = cr.top+16;
-		break;
-	case RADIO2:
-		hcr.left = cr.left;			hcr.right = cr.left+15;
-		hcr.top = cr.top+1;			hcr.bottom = cr.top+16;
-		break;
-		}
-	flags |= ISRADIO;
-}
-
-RadioButton::~RadioButton()
-{
-	if(Text) free (Text);
-}
-
-bool
-RadioButton::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch(cmd) {
-	case CMD_SETTEXT:
-		if(Text) free(Text);
-		if(tmpl && *((char*)tmpl))Text = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0);
-		else Text = 0L;
-		return true;
-		}
-	return false;
-}
-
-void
-RadioButton::DoPlot(anyOutput *o)
-{
-	if(flags & HIDDEN) return;
-	Line.color = 0x00000000L;
-	Line.width = 0.0;
-	Fill.color = bLBdown ? DlgBGcolor : 0x00ffffffL;
-	if(bActive) {
-		o->SetLine(&Line);
-		o->SetFill(&Fill);
-		o->oCircle(hcr.left, hcr.top, hcr.right, hcr.bottom);
-		if(bChecked) {
-			Fill.color = 0x00000000L;		o->SetFill(&Fill);
-			switch (type) {
-			case RADIO0:
-				o->oCircle(cr.left+6, cr.top+6, cr.left+11, cr.top+11);
-				break;
-			case RADIO1:
-				o->oCircle(cr.left+5, cr.top+6, cr.left+12, cr.top+13);
-				break;
-			case RADIO2:
-				o->oCircle(cr.left+3, cr.top+4, cr.left+12, cr.top+13);
-				break;
-				}	
-			}
-		}
-	if(Text) {
-		o->SetTextSpec(&TextDef);
-		o->oTextOut(cr.left + 20, cr.top + 1, Text, (int)strlen(Text));
-		}
-	o->UpdateRect(&cr, false);
-}
-
-bool
-RadioButton::Select(int x, int y, anyOutput *o)
-{
-	if(bActive && !(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
-		bChecked = true;		bLBdown = false;
-		DoPlot(o);
-		if(parent) parent->Command(CMD_RADIOBUTT, (void *)this, o);
-		if((flags & TOUCHEXIT) && parent) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		return true;
-		}
-	return false;
-}
-
-void
-RadioButton::SetColor(int id, DWORD color)
-{
-	TextDef.ColTxt = color;
-}
-
-Text::Text(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
-	:Dialog(par, desc, rec)
-{
-	if(text && text[0])txt = (char*)memdup(text, (int)strlen(text)+1, 0);
-	else txt = 0L;
-	switch (type) {
-	case RTEXT:		TextDef.Align = TXA_HRIGHT | TXA_VTOP;	break;
-	case CTEXT:		TextDef.Align = TXA_HCENTER | TXA_VTOP;	break;
-	default:		TextDef.Align = TXA_HLEFT | TXA_VTOP;	break;
-		}
-}
-
-Text::~Text()
-{
-	if(txt) free(txt);
-}
-
-bool
-Text::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch(cmd) {
-	case CMD_SETTEXT:
-		if(txt) free(txt);
-		if(tmpl && *((char*)tmpl))txt = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0);
-		else txt = 0L;
-		return true;
-		}
-	return false;
-}
-
-void
-Text::DoPlot(anyOutput *o)
-{
-	if(WWWbrowser && WWWbrowser[0] && (flags & HREF)) {
-		TextDef.ColTxt = 0x00ff0000L;
-		TextDef.Style |= TXS_UNDERLINE;
-		}
-	if(txt){
-		o->SetTextSpec(&TextDef);
-		switch(type) {
-		case RTEXT: o->oTextOut(cr.right - 2, cr.top + 1, txt, 0);				break;
-		case CTEXT: o->oTextOut((cr.left + cr.right)/2, cr.top + 1, txt, 0);	break;
-		default:	o->oTextOut(cr.left + 2, cr.top + 1, txt, 0);				break;
-			}
-		o->UpdateRect(&cr, false);
-		}
-}
-
-bool
-Text::Select(int x, int y, anyOutput *o)
-{
-	int i;
-
-	if(WWWbrowser && WWWbrowser[0] && txt && (flags & HREF) && IsInRect(&cr, x, y)) {
-		o->MouseCursor(MC_WAIT, false);
-		i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE-50, WWWbrowser);
-		TmpTxt[i++] = ' ';
-		rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, txt);
-		if(parent && (flags & TOUCHEXIT))parent->Command(CMD_ENDDIALOG, 0L, o);
-		else if(parent)parent->Command(CMD_CONTINUE, 0L, o);
-#ifdef _WINDOWS
-		WinExec(TmpTxt, SW_SHOWMAXIMIZED); 
-#else
-		strcat(TmpTxt, " &");
-		system(TmpTxt);
-#endif
-		o->MouseCursor(MC_ARROW, false);
-		return true;
-		}
-	return false;
-}
-
-void
-Text::SetColor(int id, DWORD color)
-{
-	TextDef.ColTxt = color;
-}
-
-void
-Text::MBtrack(MouseEvent *mev, anyOutput *o)
-{
-	if((mev->StateFlags &1) && IsInRect(&hcr, mev->x, mev->y) && parent) parent->Command(CMD_MARKOBJ, this, o);
-}
-
-InputText::InputText(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
-	:Dialog(par, desc, rec)
-{
-	RECT rc;
-
-	rc.left = rec.left+1;				rc.top = rec.top+1;
-	rc.right = rec.right-1;				rc.bottom = rec.bottom-1;
-	if(type == INCDECVAL1) cr.right = rc.right = cr.right - 7*xbase;
-	Line.color = 0x00000000L;
-	if(Text = new EditText(0L, text, -1, -1)) Text->SetRec(&rc);
-	Disp = 0L;
-}
-
-InputText::~InputText()
-{
-	if(Text) delete (Text);		Text = 0L;
-}
-
-bool
-InputText::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	double tmpVal;
-	bool bRet;
-
-	switch(cmd) {
-	case CMD_CURRLEFT:	case CMD_CURRIGHT:	case CMD_DELETE:
-	case CMD_POS_FIRST:	case CMD_POS_LAST:	case CMD_SHIFTLEFT:
-	case CMD_SHIFTRIGHT:
-		if(Text && bActive && !(flags & NOEDIT)){
-			if(o) Undo.SetDisp(o);
-			o->SetTextSpec(&TextDef);
-			bRet = Text->Command(cmd, o, NULL);
-			if(bRet && (flags & TOUCHEXIT)) parent->Command(CMD_ENDDIALOG, (void *)this, o);
-			return bRet;
-			}
-		break;
-	case CMD_SETFONT:	case CMD_SETSTYLE:
-		if(Text) return Text->Command(cmd, o, tmpl);
-		return false;
-	case CMD_UPDATE:
-		if(Text && bActive && o) Text->Command(cmd, o, 0L);
-		break;
-	case CMD_COPY:		case CMD_PASTE:
-		if(Text && bActive && !(flags & NOEDIT)) return  Text->Command(cmd, o, NULL);
-		return false;
-	case CMD_ADDCHAR:
-		if(Text && bActive && !(flags & NOEDIT)){
-			if(o) Undo.SetDisp(o);
-			o->SetTextSpec(&TextDef);
-			switch(*((int*)tmpl)) {
-			case 8: return Text->Command(CMD_BACKSP, o, NULL);	//Backspace
-			default: return Text->AddChar(*((int*)tmpl), o, 0L);
-				}
-			if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, o);
-			}
-		return false;
-	case CMD_SETTEXT:
-		if(Text) return Text->SetText((char*)tmpl);
-		break;
-	case CMD_ENDDIALOG:		//we should come here for IncDecValue class only
-		if(type == INCDECVAL1 && GetValue(Id, &tmpVal)) {
-			if(((Dialog*)tmpl)->Id == 1) tmpVal *= 1.2;
-			else tmpVal /= 1.2;
-			tmpVal = NiceValue(tmpVal);
-			if(fabs(tmpVal) < 0.0001) switch(defs.cUnits) {
-				case 1:		tmpVal = 0.01;	break;
-				case 2:		tmpVal = 0.004;	break;
-				default:	tmpVal = 0.1;	break;
-				}
-			WriteNatFloatToBuff(TmpTxt, tmpVal);
-			if(Text) {
-				Text->SetText(TmpTxt+1);				DoPlot(o);
-				}
-			}
-		break;
-		}
-	return false;
-}
-
-void
-InputText::DoPlot(anyOutput *o)
-{
-	POINT pts[5];
-
-	if(!o) return;
-	Disp = o;
-	pts[0].x = pts[0].y = 0;		//that means no caret with update(5)
-	o->SetTextSpec(&TextDef);
-	if(Text) Text->Update(bActive ? 5 : 2, o, &pts[0]);
-	o->SetLine(&Line);
-	pts[0].x = pts[1].x = pts[4].x = cr.left;
-	pts[0].y = pts[3].y = pts[4].y = cr.top;
-	pts[1].y = pts[2].y = cr.bottom-1;
-	pts[2].x = pts[3].x = cr.right-1;
-	o->oPolyline(pts, 5);
-	o->UpdateRect(&cr, false);
-	if(parent && bActive)parent->Command(CMD_TABDLG, this, o);
-}
-
-bool
-InputText::Select(int x, int y, anyOutput *o)
-{
-	POINT p1;
-
-	p1.x = x;		p1.y = y;
-	if(bActive && IsInRect(&cr, x, y) && !(flags & NOEDIT)) {
-		DialogFocus = DialogTabStop = this;
-		if(parent) parent->Command(CMD_FOCTXT, (void*)this, 0L);
-		o->SetTextSpec(&TextDef);
-		if(Text) Text->Update(1, o, &p1); 
-		if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		return true;
-		}
-	return false;
-}
-
-bool
-InputText::GetValue(int id, double *val)
-{
-	if(Text) {
-		Text->Update(20, NULL, 0L);			//convert string to value
-		return Text->GetValue(val);
-		}
-	return false;
-}
-
-bool
-InputText::GetInt(int id, int *val)
-{
-	if(type == EDTEXT) {
-		if(Text) *val = Text->Cursor();
-		else *val = 0;
-		return true;
-		}
-	if(Text && Text->text) {
-#ifdef USE_WIN_SECURE
-		sscanf_s(Text->text, "%d", val);
-#else
-		sscanf(Text->text, "%d", val);
-#endif
-		return true;
-		}
-	return false;
-}
-
-bool
-InputText::GetText(int id, char *txt, int size)
-{
-	if(Text && Text->text && Text->text[0]) {
-		rlp_strcpy(txt, size, Text->text);
-		return true;
-		}
-	return false;
-}
-
-void
-InputText::MBtrack(MouseEvent *mev, anyOutput *o)
-{
-	bool bLBstate;
-	int x, y;
-
-	x = mev->x;						y = mev->y;
-	if(mev->StateFlags &1) bLBstate = true;
-	else bLBstate = false;
-	if(bActive && IsInRect(&cr, x, y) && !(flags & NOEDIT) && 
-		bLBstate && Text && parent) {
-		DialogFocus = this;
-		parent->Command(CMD_FOCTXT, (void*)this, 0L);
-		o->SetTextSpec(&TextDef);
-		Text->Command(CMD_MOUSE_EVENT, o, (DataObj *)mev);
-		parent->Command(CMD_MARKOBJ, this, o);
-		}
-}
-
-void
-InputText::Activate(int id, bool activate)
-{
-	bActive = activate;
-	if(Text && Disp) {
-		Disp->SetTextSpec(&TextDef);
-		if(bActive){
-			Text->Update(1, Disp, 0L);
-			DialogFocus = DialogTabStop = this;
-			if(parent) parent->Command(CMD_FOCTXT, this, Disp);
-			}
-		else Text->Update(2, Disp, 0L);
-		}
-}
-
-RangeInput::RangeInput(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text, DataObj *d)
-	:InputText(par, desc, rec, text)
-{
-	data = d;
-}
-
-RangeInput::~RangeInput()
-{
-	if(data) data->Command(CMD_ETRACC, 0L, 0L);
-	if(Text) delete (Text);		Text = 0L;
-}
-
-bool
-RangeInput::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch(cmd) {
-	case CMD_SET_DATAOBJ:
-		data = (DataObj*)tmpl;
-		return true;
-		}
-	return InputText::Command(cmd, tmpl, o);
-}
-
-bool
-RangeInput::Select(int x, int y, anyOutput *o)
-{
-	bool bRes;
-
-	bRes = InputText::Select(x, y, o);
-	if(bRes && data) {
-		if(DialogFocus == this){
-			data->Command(CMD_ETRACC, Text, 0L);
-			if(Text) Text->Update(1, o, 0L); 
-			}
-		else data->Command(CMD_ETRACC, 0L, 0L);
-		}
-	return bRes;
-}
-
-void
-RangeInput::Activate(int id, bool activate)
-{
-	InputText::Activate(id, activate);
-	if(activate && data) data->Command(CMD_ETRACC, Text, 0L);
-}
-
-InputValue::InputValue(tag_DlgObj *par, DlgInfo * desc, RECT rec, double *value)
-	:InputText(par, desc, rec, 0L)
-{
-	if(value) {
-		WriteNatFloatToBuff(TmpTxt, *value);
-		if(Text) Text->text = (char*)memdup(TmpTxt+1, (int)strlen(TmpTxt+1)+1, 0);
-		}
-	else if(Text) Text->SetText("");
-}
-
-InputValue::~InputValue()
-{
-	if(Text) delete (Text);
-	Text = 0L;					//in fact the destructor of InputText is also
-								//  called. This prevents a double delete of Text.
-}
-
-IncDecValue::IncDecValue(tag_DlgObj *par, DlgInfo * desc, RECT rec, double *value)
-	:InputText(par, desc, rec, 0L)
-{
-	int ab1 = 1, ab2 = 2;
-	static DlgInfo ab[] = {
-		{1, 2, 0, 0x0L, ARROWBUTT, (void*)&ab1, 0, 0, 0, 0},
-		{2, 0, 0, LASTOBJ, ARROWBUTT, (void*)&ab2, 0, 0, 0, 0}};
-	RECT br;
-
-	WriteNatFloatToBuff(TmpTxt, *value);
-	if(Text) Text->text = (char*)memdup(TmpTxt+1, (int)strlen(TmpTxt+1)+1, 0);
-	br.left = cr.right+1;		br.right = br.left + 7*xbase;
-	br.top = cr.top+1;			br.bottom = br.top + 5*ybase;
-	butts[0] = new ArrowButton(this, &ab[0], br, &ab1);
-	br.top += 5*ybase;			br.bottom += 5*ybase;
-	butts[1] = new ArrowButton(this, &ab[1], br, &ab2);
-}
-
-IncDecValue::~IncDecValue()
-{
-	if(Text) delete (Text);
-	if(butts[0]) delete (butts[0]);
-	if(butts[1]) delete (butts[1]);
-	Text = 0L;
-}
-
-void
-IncDecValue::DoPlot(anyOutput *o)
-{
-	InputText::DoPlot(o);
-	if(butts[0]) butts[0]->DoPlot(o);
-	if(butts[1]) butts[1]->DoPlot(o);
-}
-
-bool
-IncDecValue::Select(int x, int y, anyOutput *o)
-{
-	bool bRet = false;
-
-	if(x > cr.right) {
-		if(butts[0]) bRet = butts[0]->Select(x, y, o);
-		if(!bRet && butts[1]) bRet = butts[1]->Select(x, y, o);
-		}
-	else return InputText::Select(x, y, o);
-	if(bRet && parent) {
-		DialogFocus = DialogTabStop = 0L;
-		if(parent) parent->Command(CMD_FOCTXT, (void*)0L, 0L);
-		if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		}
-	return bRet;
-}
-
-void
-IncDecValue::MBtrack(MouseEvent *mev, anyOutput *o)
-{
-	if(mev->x >cr.right) {
-		if(butts[0]) butts[0]->MBtrack(mev, o);
-		if(butts[1]) butts[1]->MBtrack(mev, o);
-		}
-	else InputText::MBtrack(mev, o);
-}
-
-TxtHSP::TxtHSP(tag_DlgObj *par, DlgInfo *desc, RECT rec, int *align)
-	:Dialog(par, desc, rec)
-{
-	int x1 = cr.left/xbase,			x2 = ((cr.left>>1) + (cr.right>>1))/xbase-4,
-		x3 = cr.right/xbase-8,		y1 = cr.top/ybase,
-		y2 = ((cr.top>>1) + (cr.bottom>>1))/ybase-4,	y3 = cr.bottom/ybase-8;
-	int i;
-	RECT br;
-	DlgInfo d1[9];
-
-	for(i = 0; i < 9; i++) {
-		d1[i].id = i+1;			d1[i].next = i+2;			d1[i].first = 0;
-		d1[i].flags = ISRADIO;	d1[i].type = RADIO0;		d1[i].ptype = 0L;
-		d1[i].w = d1[i].h = 8;
-		switch (i / 3) {
-			case 0:		d1[i].y = y1;	break;
-			case 1:		d1[i].y = y2;	break;
-			case 2:		d1[i].y = y3;	break;
-			}
-		switch (i % 3) {
-			case 0:		d1[i].x = x1;	break;
-			case 1:		d1[i].x = x2;	break;
-			case 2:		d1[i].x = x3;	break;
-			}
-		}
-	d1[8].next = 0;			d1[8].flags |= LASTOBJ;
-	txt.ColTxt = 0x00808080L;	txt.ColBg = 0x00ffffff;
-	txt.fSize = defs.GetSize(SIZE_TEXT)*2.0;		
-	txt.RotBL = txt.RotCHAR = 0.0;
-	txt.iSize = 0;			txt.Align = TXA_HCENTER | TXA_VCENTER;
-	txt.Style = TXS_NORMAL;	txt.Font = FONT_HELVETICA;
-	txt.Mode = TXM_TRANSPARENT;
-	if(txt.text = (char*)malloc(20*sizeof(char))) rlp_strcpy(txt.text, 20, "text");
-	if((d2 = (DlgInfo*)malloc(9*sizeof(DlgInfo)))){
-		memcpy(d2, &d1,9*sizeof(DlgInfo));
-		if(align) switch(*align) {
-		case 1:		d2[1].flags |= CHECKED;		break;
-		case 2:		d2[2].flags |= CHECKED;		break;
-		case 4:		d2[3].flags |= CHECKED;		break;
-		case 5:		d2[4].flags |= CHECKED;		break;
-		case 6:		d2[5].flags |= CHECKED;		break;
-		case 8:		d2[6].flags |= CHECKED;		break;
-		case 9:		d2[7].flags |= CHECKED;		break;
-		case 10:	d2[8].flags |= CHECKED;		break;
-		default:	d2[0].flags |= CHECKED;		break;
-			}
-		for(i = 0; i < 9; i++) {
-			br.left = d2[i].x*xbase;
-			br.right = br.left + d2[i].w*xbase;
-			br.top = d2[i].y*ybase;
-			br.bottom = br.top + d2[i].h*ybase;
-			butts[i] = new RadioButton(this, &d2[i], br, 0L);
-			}
-		}
-}
-
-TxtHSP::~TxtHSP()
-{
-	int i;
-
-	if(txt.text) free(txt.text);
-	if(d2){
-		for(i = 0; i < 9; i++) if(butts[i]) delete(butts[i]);
-		free(d2);
-		}
-}
-
-bool
-TxtHSP::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-	Dialog *d;
-
-	switch(cmd) {
-	case CMD_RADIOBUTT:
-		d = (Dialog *)tmpl;
-		for(i = 0; i < 9; i++) 
-			if(butts[i] && butts[i] != d && butts[i]->type == d->type)
-				butts[i]->SetCheck(0, o, false);
-		break;
-		}
-	return false;
-}
-
-void
-TxtHSP::DoPlot(anyOutput *o)
-{
-	int i;
-
-	if(txt.text){
-		o->SetTextSpec(&txt);
-		o->oTextOut((cr.left + cr.right)>>1, (cr.top + cr.bottom)>>1, txt.text, 0);
-		}
-	for(i = 0; i < 9; i++) if(butts[i]) butts[i]->DoPlot(o);
-	o->UpdateRect(&cr, false);
-}
-
-bool
-TxtHSP::Select(int x, int y, anyOutput *o)
-{
-	int i;
-	bool bRet = false;
-
-	for(i = 0; i < 9; i++) if(butts[i] && butts[i]->Select(x, y, o)) bRet = true;
-	return bRet;
-}
-
-bool
-TxtHSP::GetInt(int id, int *val)
-{
-	int i;
-	bool bRet = false;
-
-	for(i = 0; i < 9; i++) {
-		if(butts[i] && butts[i]->GetCheck(i+1)) {
-			bRet = true;
-			switch (i) {
-			case 0:		*val = 0;	break;
-			case 1:		*val = 1;	break;
-			case 2:		*val = 2;	break;
-			case 3:		*val = 4;	break;
-			case 4:		*val = 5;	break;
-			case 5:		*val = 6;	break;
-			case 6:		*val = 8;	break;
-			case 7:		*val = 9;	break;
-			case 8:		*val = 10;	break;
-				}
-			}
-		}
-	return bRet;
-}
-
-void
-TxtHSP::MBtrack(MouseEvent *mev, anyOutput *o)
-{
-	int i;
-
-	for(i = 0; i < 9; i++) if(butts[i]) butts[i]->MBtrack(mev, o);
-}
-
-SlideRect::SlideRect(tag_DlgObj *par, DlgInfo *desc, RECT rec, bool isVert)
-	:Dialog(par, desc, rec)
-{
-	bV = isVert;
-	if(isVert) {
-		buttrc.left = cr.left+1;	buttrc.top = cr.top;
-		buttrc.right = cr.right;	buttrc.bottom = cr.top + (w = h = (cr.right-cr.left));
-		}
-	else {
-		buttrc.left = cr.left;		buttrc.top = cr.top+1;
-		buttrc.right = cr.left+ (w = h = (cr.bottom - cr.top));	buttrc.bottom = cr.bottom;
-		}
-	dx = w>>1;		dy = h>>1;
-	sLine = 1;
-	puSel = pdSel = false;
-
-}
-
-SlideRect::~SlideRect()
-{
-}
-
-bool
-SlideRect::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-	double tmp;
-
-	switch(cmd) {
-	case CMD_SETSCROLL:
-		//DEBUG: replace width/height calulation by variables w and h
-		tmp = *((double*)tmpl);
-		if(tmp < 0.0) tmp = 0.0;	if(tmp > 1.0) tmp = 1.0;
-		if(bV) {
-			i = (int)((double)(cr.bottom -cr.top -7*ybase)*tmp);
-			buttrc.bottom -= buttrc.top;
-			buttrc.top = i + cr.top;		buttrc.bottom += (i+cr.top);
-			}
-		else {
-			i = (int)((double)(cr.right -cr.left -7*xbase)*tmp);
-			buttrc.right -= buttrc.left;
-			buttrc.left = i + cr.left;		buttrc.right += (i+cr.left);
-			}
-		if(o)DoPlot(o);
-		return true;
-	case CMD_LINEUP:
-		i = -sLine;
-	case CMD_LINEDOWN:
-		if(cmd == CMD_LINEDOWN) i = sLine;
-		if(bV) {
-			buttrc.top += i;
-			if(buttrc.top < cr.top) buttrc.top = cr.top;
-			if((buttrc.top + h) > cr.bottom) buttrc.top = cr.bottom -h;
-			buttrc.bottom = buttrc.top + h;
-			}
-		else {
-			buttrc.left += i;
-			if(buttrc.left < cr.left) buttrc.left = cr.left;
-			if((buttrc.left + w) > cr.right) buttrc.left = cr.right -w;
-			buttrc.right = buttrc.left +w;
-			}
-		return true;
-		}
-	return false;
-}
-
-
-void
-SlideRect::DoPlot(anyOutput *o)
-{
-	POINT pts[3];
-
-	Line.color = DlgBGcolor;	Fill.color = 0x00e0e0e0L;
-	o->SetLine(&Line);			o->SetFill(&Fill);
-	o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
-	puRC.top = cr.top;		puRC.left = cr.left;
-	pdRC.right = cr.right;	pdRC.bottom = cr.bottom;
-	if(bV) {
-		puRC.bottom = buttrc.top;	puRC.right = cr.right;
-		pdRC.top = buttrc.bottom;	pdRC.left = cr.left;
-		}
-	else {
-		puRC.bottom = cr.bottom;	puRC.right = buttrc.left;
-		pdRC.top = cr.top;			pdRC.left = buttrc.right;
-		}
-	Fill.color = DlgBGhigh;
-	o->SetFill(&Fill);
-	if(bLBdown){
-		Line.color = 0x00808080L;		o->SetLine(&Line);
-		o->oRectangle(buttrc.left, buttrc.top, buttrc.right, buttrc.bottom);
-		}
-	else {
-		o->oRectangle(buttrc.left, buttrc.top, buttrc.right-1, buttrc.bottom-1);
-		Line.color = 0x0L;
-		o->SetLine(&Line);
-		pts[0].x = buttrc.left;					pts[0].y = pts[1].y = buttrc.bottom-1;
-		pts[1].x = pts[2].x = buttrc.right-1;	pts[2].y = buttrc.top-1;
-		o->oPolyline(pts, 3);
-		Line.color = 0x00ffffffL;
-		o->SetLine(&Line);
-		pts[0].x = pts[1].x = buttrc.left;
-		pts[0].y = buttrc.bottom -3;
-		pts[1].y = pts[2].y = buttrc.top;
-		pts[2].x = buttrc.right -2;
-		o->oPolyline(pts, 3);
-		}
-	o->UpdateRect(&cr, false);
-	puSel = pdSel = false;
-}
-
-bool
-SlideRect::Select(int x, int y, anyOutput *o)
-{
-	if(IsInRect(&buttrc, x, y)) {
-		bLBdown = false;
-		DoPlot(o);
-		return true;
-		}
-	else if(bLBdown || puSel || pdSel) {
-		bLBdown = false;
-		DoPlot(o);
-		}
-	if(IsInRect(&puRC, x, y) && parent) parent->Command(CMD_PAGEUP, 0L, o);
-	else if(IsInRect(&pdRC, x, y) && parent) parent->Command(CMD_PAGEDOWN, 0L, o);
-	return false;
-}
-
-bool
-SlideRect::GetValue(int id, double *val)
-{
-	double res;
-
-	if(bV) {
-		res = ((double)(buttrc.top - cr.top))/
-			((double)(cr.bottom-cr.top-(cr.right-cr.left)));
-		}
-	else {
-		res = ((double)(buttrc.left - cr.left))/
-			((double)(cr.right-cr.left-(cr.bottom-cr.top)));
-		}
-	*val = res;
-	return true;
-}
-
-void
-SlideRect::MBtrack(MouseEvent *mev, anyOutput *o)
-{
-	bool bLBstate = false;
-
-	if(mev->Action == MOUSE_LBDOUBLECLICK) {
-		bLBdown = false;
-		DoPlot(o);
-		return;
-		}
-	if(mev->StateFlags &1) bLBstate = true;
-	if(IsInRect(&buttrc, mev->x, mev->y)){
-		if(mev->Action == MOUSE_LBDOWN){
-			dx = mev->x-buttrc.left;	dy = mev->y - buttrc.top;
-			}
-		if(bLBstate && !bLBdown){
-			bLBdown = bLBstate;
-			DoPlot(o);
-			return;
-			}
-		}
-	if(IsInRect(&puRC, mev->x, mev->y)){
-		if(pdSel) DoPlot(o);
-		Line.color = Fill.color = bLBstate ? DlgBGcolor : 0x00e0e0e0L;
-		o->SetLine(&Line);			o->SetFill(&Fill);
-		o->oRectangle(puRC.left+3, puRC.top+3, puRC.right-3, puRC.bottom-3);
-		o->UpdateRect(&puRC, false);
-		puSel = true;
-		}
-	else if(puSel) DoPlot(o);
-	if(IsInRect(&pdRC, mev->x, mev->y)){
-		if(puSel) DoPlot(o);
-		Line.color = Fill.color = bLBstate ? DlgBGcolor : 0x00e0e0e0L;
-		o->SetLine(&Line);			o->SetFill(&Fill);
-		o->oRectangle(pdRC.left+3, pdRC.top+3, pdRC.right-3, pdRC.bottom-3);
-		o->UpdateRect(&pdRC, false);
-		pdSel = true;
-		}
-	else if(pdSel) DoPlot(o);
-	if(bLBdown && IsInRect(&cr, mev->x, mev->y)){
-		if(bV) {
-			buttrc.top = mev->y - dy;
-			if(buttrc.top < cr.top) buttrc.top = cr.top;
-			if((buttrc.top + h) > cr.bottom) buttrc.top = cr.bottom -h;
-			buttrc.bottom = buttrc.top + h;
-			}
-		else {
-			buttrc.left = mev->x - dx;
-			if(buttrc.left < cr.left) buttrc.left = cr.left;
-			if((buttrc.left + w) > cr.right) buttrc.left = cr.right -w;
-			buttrc.right = buttrc.left +w;
-			}
-		if(parent && ((Dialog*)parent)->parent && 
-			((Dialog*)parent)->parent->Command(CMD_REDRAW, 0L, o));
-		else DoPlot(o);
-		}
-	else if(bLBdown){
-		bLBdown = false;
-		DoPlot(o);
-		}
-}
-
-ScrollBar::ScrollBar(tag_DlgObj *par, DlgInfo *desc, RECT rec, bool isVert)
-	:Dialog(par, desc, rec)
-{
-	int ab1 = 1, ab2 = 2, ab3 = 3, ab4 = 4;
-	static DlgInfo ab[] = {
-		{1, 2, 0, 0x0L, ARROWBUTT, (void*)0L, 0, 0, 0, 0},
-		{2, 0, 0, 0x0L, ARROWBUTT, (void*)0L, 0, 0, 0, 0},
-		{3, 0, 0, LASTOBJ, NONE, 0L, 0, 0, 0, 0}};
-	RECT br, sr;
-
-	if(isVert) {
-		sr.left = cr.left;		sr.right = cr.right;
-		br.left = cr.left+1;	br.right = cr.right+1;
-		br.top = cr.top+1;		br.bottom = sr.top = br.top + 5*ybase + 1;
-		sr.top--;	hcr.top = sr.top;
-		butts[0] = new ArrowButton(this, &ab[0], br, &ab1);
-		br.top = cr.bottom-5*ybase;		br.bottom = cr.bottom+1;
-		butts[1] = new ArrowButton(this, &ab[1], br, &ab2);
-		br.bottom -= 5*ybase;	br.top -= 5*ybase;
-		hcr.bottom = sr.bottom = br.top;
-		butts[2] = new ArrowButton(this, &ab[0], br, &ab1);
-		}
-	else {
-		sr.top = cr.top;		sr.bottom = cr.bottom;
-		br.left = cr.left+1;	br.right = sr.left = br.left + 5*xbase;
-		sr.left--;	hcr.left = sr.left;
-		br.top = cr.top+1;		br.bottom = cr.bottom+1;
-		butts[0] = new ArrowButton(this, &ab[0], br, &ab3);
-		br.left = cr.right - 5*xbase;	br.right = cr.right+1;
-		butts[1] = new ArrowButton(this, &ab[1], br, &ab4);
-		br.left -= 5*xbase;		br.right -= 5*xbase;
-		hcr.right = sr.right = br.left;
-		butts[2] = new ArrowButton(this, &ab[0], br, &ab3);
-		}
-	slrc = new SlideRect(this, &ab[2], sr, isVert);
-	sLine = 1;		sPage = 8;
-}
-
-ScrollBar::~ScrollBar()
-{
-	int i;
-
-	for(i = 0; i < 3; i++) if(butts[i]) delete butts[i];
-	if(slrc) delete slrc;
-}
-
-bool
-ScrollBar::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-
-	switch(cmd) {
-	case CMD_ENDDIALOG:
-		if(!tmpl || !slrc) return false;
-		i = ((Dialog*)tmpl)->Id;
-		switch(i) {
-		case 1:	return slrc->Command(CMD_LINEUP, 0L, o);
-		case 2:	return slrc->Command(CMD_LINEDOWN, 0L, o);
-		default:	return false;
-			}
-	case CMD_PAGEUP:
-	case CMD_PAGEDOWN:
-		if(parent)return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_SETSCROLL:
-		if(slrc) return slrc->Command(cmd, tmpl, o);
-		break;
-		}
-	return false;
-}
-
-void
-ScrollBar::DoPlot(anyOutput *o)
-{
-	int i;
-
-	for(i = 0; i < 3; i++) if(butts[i]) butts[i]->DoPlot(o);
-	if(slrc){
-		slrc->sLine = sLine;
-		slrc->DoPlot(o);
-		}
-}
-
-bool
-ScrollBar::Select(int x, int y, anyOutput *o)
-{
-	int i;
-	bool bRet = false;
-
-	if(!IsInRect(&cr, x, y)) return false;
-	for(i = 0; i < 3; i++) if(butts[i] && butts[i]->Select(x, y, o)) bRet = true;
-	if(!bRet && slrc) bRet = slrc->Select(x, y, o);
-	if(bRet && parent) parent->Command(CMD_REDRAW, 0L, o);
-	return bRet;
-}
-
-bool
-ScrollBar::GetValue(int id, double *val)
-{
-	if(slrc) return slrc->GetValue(id, val);
-	return false;
-}
-
-void
-ScrollBar::MBtrack(MouseEvent *mev, anyOutput *o)
-{
-	int i;
-
-	for(i = 0; i < 3; i++) if(butts[i]) butts[i]->MBtrack(mev, o);
-	if(slrc) slrc->MBtrack(mev, o);
-}
-
-Icon::Icon(tag_DlgObj *par, DlgInfo * desc, RECT rec, int *ico)
-	:Dialog(par, desc, rec)
-{
-	icon = ico ? *ico : 0;
-}
-
-void
-Icon::DoPlot(anyOutput *o)
-{
-	o->oDrawIcon(icon, cr.left, cr.top);
-	o->UpdateRect(&cr, false);
-}
-
-Group::Group(tag_DlgObj *par, DlgInfo *desc, RECT rec)
-	:Dialog(par, desc, rec)
-{
-	numChildren = 5;
-	Children = (Dialog **)calloc(numChildren, sizeof(Dialog *));
-	TextFocus = 0L;
-}
-
-Group::~Group()
-{
-	//pointers to child objects are allocated and freed by parent:
-	//  pointers are copies and we do not care further;
-	if(Children) free(Children);
-}
-
-bool
-Group::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	Dialog *d, **tmp;
-
-	int i;
-
-	switch (cmd) {
-	case CMD_FLUSH:
-		if(Children) for(i = 0; i < numChildren; Children[i++] = 0L);
-		break;
-	case CMD_CONTINUE:		case CMD_ENDDIALOG:		case CMD_TABDLG:
-	case CMD_NOTABDLG:		case CMD_MARKOBJ:
-		if(parent && (flags & HIDDEN) != HIDDEN) return parent->Command(cmd, tmpl, o);
-		else return false;
-	case CMD_ADDCHILD:
-		if(Children && numChildren && (d = (Dialog*)tmpl)) {
-			d->parent = this;
-			if(!TextFocus &&(d->type == EDTEXT || d->type == RANGEINPUT ||
-				d->type == INCDECVAL1 || d->type == EDVAL1))
-				TextFocus = ((InputText*)tmpl)->bActive ? (InputText*)tmpl: 0L ;
-			for(i = 0; i < numChildren; i++) {
-				if(!Children[i] || Children[i] == d) {
-					Children[i] = d;
-					return true;
-					}
-				}
-			//if we come here the list for children is too short
-			tmp = (Dialog **)realloc(Children, (numChildren+1) * sizeof(Dialog *));
-			if(!tmp) return false;
-			Children = tmp;
-			Children[numChildren++] = d;
-			return true;
-			}
-		break;
-	case CMD_RADIOBUTT:
-		if(Children && numChildren) {
-			d = (Dialog *)tmpl;
-			for(i = 0; i < numChildren; i++) {
-				if(Children[i] && Children[i] != d && Children[i]->type == d->type &&
-					Children[i]->bChecked) {
-					Children[i]->bChecked = false;
-					Children[i]->DoPlot(o);
-					}
-				}
-			}
-		break;
-	case CMD_FOCTXT:
-		DialogTabStop = TextFocus = (InputText *)tmpl;
-		break;
-		}
-	return false;
-}
-
-void
-Group::DoPlot(anyOutput *o)
-{
-	int i;
-
-	if(flags & HIDDEN) return;
-	if(Children && numChildren && bChecked) {
-		for(i = 0; i < numChildren; i++) {
-			if(Children[i] && !(Children[i]->flags & HIDDEN)) {
-				Children[i]->DoPlot(o);
-				if(Children[i] == (Dialog*)TextFocus && DialogFocus != DialogTabStop)
-					TextFocus->Activate(TextFocus->Id, true);
-				}
-			}
-		}
-	else if(Children && numChildren && parent) {
-		for(i = 0; i < numChildren; i++) {
-			if(Children[i]) parent->Command(CMD_NOTABDLG, Children[i], o); 
-			}
-		}
-}
-
-bool
-Group::Select(int x, int y, anyOutput *o)
-{
-	int i;
-
-	if(Children && numChildren) {
-		for(i = 0; i < numChildren; i++) {
-			if(Children[i] && !(Children[i]->flags &HIDDEN) && Children[i]->Select(x, y, o)) 
-				return (bChecked = true);
-			}
-		}
-	return false;
-}
-
-void
-Group::MBtrack(MouseEvent *mev, anyOutput *o)
-{
-	int i;
-
-	if(bChecked && Children && numChildren) {
-		for(i = 0; i < numChildren; i++) {
-			if(Children[i]&& !(Children[i]->flags &HIDDEN)) Children[i]->MBtrack(mev, o);
-			}
-		}
-}
-
-GroupBox::GroupBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *txt)
-	:Group(par,desc, rec)
-{
-	if(txt && txt[0])Text = (char*)memdup(txt, (int)strlen(txt)+1, 0);
-	else Text = 0L;
-	Line.color = 0x00000000L;					Line.width = 0.0;
-	Fill.color = DlgBGhigh;						TextDef.ColBg = Fill.color;
-	TextDef.Align = TXA_HLEFT | TXA_VCENTER;	TextDef.Mode = TXM_OPAQUE;
-}
-
-GroupBox::~GroupBox()
-{
-	if(Text) free(Text);
-}
-
-void
-GroupBox::DoPlot(anyOutput *o)
-{
-	o->SetLine(&Line);
-	o->SetFill(&Fill);
-	o->oRectangle(cr.left, cr.top, cr.right-1, cr.bottom-1);
-	if(Text) {
-		o->SetTextSpec(&TextDef);
-		hcr.top = cr.top - TextDef.iSize;
-		o->oTextOut(cr.left+4, cr.top, Text, 0);
-		}
-	Group::DoPlot(o);
-	o->UpdateRect(&hcr, false);
-}
-
-TabSheet::TabSheet(tag_DlgObj *par, DlgInfo * desc, RECT rec, TabSHEET * sh, DataObj *d)
-	:Group(par, desc, rec)
-{
-	if(sh->text && sh->text[0])Text = (char*)memdup(sh->text, (int)strlen(sh->text)+1, 0);
-	else Text = 0L;
-	rctab.left = cr.left + sh->x1 * xbase;			rctab.right = cr.left + sh->x2 * xbase;
-	rctab.top = cr.top;								rctab.bottom = cr.top + sh->height * ybase;
-	TextDef.Align = TXA_HRIGHT | TXA_VTOP;			flags |= ISRADIO;
-	data = d;
-}
-
-TabSheet::~TabSheet()
-{
-	if(Text) free (Text);
-}
-
-void
-TabSheet::DoPlot(anyOutput *o)
-{
-	POINT pts[6];
-
-	pts[0].x = pts[1].x = pts[5].x = rctab.left;
-	pts[0].y = pts[4].y = pts[5].y = rctab.bottom;
-	pts[1].y = (rctab.top + rctab.bottom)/2;
-	pts[2].x = rctab.left + (rctab.bottom - rctab.top)/2;
-	pts[2].y = pts[3].y = rctab.top;
-	pts[3].x = pts[4].x = rctab.right-1;
-	HideCopyMark();
-	Line.color = 0x0L;				Line.width = 0.0;
-	Fill.color = bChecked ? DlgBGhigh : DlgBGcolor;
-	o->SetLine(&Line);				o->SetFill(&Fill);
-	o->oPolygon(pts, 6);
-	if(bChecked) {
-		o->oRectangle(cr.left, rctab.bottom, cr.right, cr.bottom);
-		Line.color = DlgBGhigh;
-		o->SetLine(&Line);
-		pts[4].x--;
-		o->oSolidLine(pts+4);
-		}
-	o->SetTextSpec(&TextDef);
-#ifdef _WINDOWS
-	o->oTextOut(rctab.right - 6, rctab.top + 3, Text, 0);
-#else
-	o->oTextOut(rctab.right - 6, rctab.top + 5, Text, 0);
-	if(bChecked) {
-		Line.color = 0x0L;			o->SetLine(&Line);
-		pts[0].y++;					o->oSolidLine(pts);
-		}
-#endif
-	Group::DoPlot(o);
-	o->UpdateRect(&cr, false);
-}
-
-bool
-TabSheet::Select(int x, int y, anyOutput *o)
-{
-	if(IsInRect(&rctab, x, y)) {
-		if(data)data->Command(CMD_ETRACC, 0L, 0L);
-		if(parent) {
-			parent->Command(CMD_RADIOBUTT, (void *)this, o);
-			parent->Command(CMD_MARKOBJ, this, o);
-			}
-		bChecked = true;		DoPlot(o);
-		if((flags & TOUCHEXIT) && parent) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		return true;
-		}
-	if(bChecked) return Group::Select(x, y, o);
-	return false;
-}
-
-ODbutton::ODbutton(tag_DlgObj *par, DlgInfo * desc, RECT rec, void *proc)
-	:Dialog(par, desc, rec)
-{
-	ODexec = (void(*)(int, void *, RECT *, anyOutput *, void *, int))proc;
-	if(ODexec) (*ODexec)(OD_CREATE, parent, &cr, 0L, 0L, Id);
-}
-
-ODbutton::~ODbutton()
-{
-	if(ODexec) (*ODexec)(OD_DELETE, (void*)parent, &cr, 0L, 0L, Id);
-}
-
-void 
-ODbutton::DoPlot(anyOutput *o)
-{
-	if(ODexec) (*ODexec)(bChecked ? OD_DRAWSELECTED : OD_DRAWNORMAL, (void*)parent, &cr, o, 0L, Id);
-}
-
-bool
-ODbutton::Select(int x, int y, anyOutput *o)
-{
-	POINT p;
-
-	if(!(flags & HIDDEN) && IsInRect(&cr, x, y)) {
-		if(flags & ISRADIO) bChecked = true;
-		else bChecked = bChecked ? false : true;
-		if(flags & NOSELECT) bChecked = false;
-		bLBdown = false;
-		p.x = x;			p.y = y;
-		if(ODexec) {
-			(*ODexec)(OD_SELECT, (void*)parent, &cr, o, (void*)&p, Id);
-			if(!(flags & NOSELECT))DoPlot(o);
-			}
-		if((flags & ISRADIO) && parent)
-			parent->Command(CMD_RADIOBUTT, (void *)this, o);
-		if((flags & TOUCHEXIT) && parent) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		return true;
-		}
-	return false;
-}
-
-void
-ODbutton::MBtrack(MouseEvent *mev, anyOutput *o)
-{
-	int x = mev->x, y = mev->y;
-
-	if(mev->Action == MOUSE_LBDOUBLECLICK) {
-		Select(mev->x, mev->y, o);
-		return;
-		}
-	if(!(flags & HIDDEN) && IsInRect(&cr, x, y) && ODexec) 
-			(*ODexec)(OD_MBTRACK, (void*)parent, &cr, o, mev, Id);
-}
-
-Listbox::Listbox(tag_DlgObj *par, DlgInfo *des, RECT rec, char **list)
-	:Dialog(par, des, rec)
-{
-	static DlgInfo sbd[] = {
-		{1, 2, 0, 0x0L, VSCROLL, 0L, 0, 0, 0, 0}};
-	RECT sr;
-	int i;
-
-	sbd[0].x = (sr.left = (cr.right-7*xbase))/xbase;
-	sbd[0].y = (sr.top = cr.top)/ybase;		sr.right = cr.right;	sbd[0].w = 7;							
-	sbd[0].h = ((sr.bottom = cr.bottom) - cr.top)/ybase;
-	sb = new ScrollBar(this, &sbd[0], sr, true);
-	bmp = 0L;		cl = bmh = 0;		hcr.right -= 7*xbase;
-	Fill.color = 0x00ffffffL;			Line.color = 0x00000000L;
-	for(i = ns = 0; list && list[i]; i++);	//count lines
-	if(strings = (char **)calloc(i+1, sizeof(char*))){
-		for(ns = i, i = 0; i < ns; i++){
-			strings[i] = (char*)memdup(list[i], (int)strlen(list[i])+1, 0);
-			}
-		}
-}
-
-Listbox::~Listbox()
-{
-	int i;
-
-	if(sb) delete(sb);
-	if(bmp) DelBitmapClass(bmp);
-	if(strings) {
-		for(i = 0; i < ns; i++) if(strings[i])free(strings[i]);
-		free(strings);
-		}
-}
-
-bool
-Listbox::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-	double ps;
-	char *txt;
-
-	ps = ((double)(cr.bottom - cr.top))/((double)(bmh+TextDef.iSize+_SBINC));
-	switch(cmd) {
-	case CMD_REDRAW:
-		DoPlot(o);
-		return true;
-	case CMD_PAGEUP:
-		ps *= -1.0;
-	case CMD_PAGEDOWN:
-		if(sb && sb->GetValue(1, &spos)){
-			spos += ps;
-			sb->Command(CMD_SETSCROLL, (void*)&spos, o);
-			DoPlot(o);
-			}
-		return true;
-	case CMD_SETSCROLL:
-		if(sb) return sb->Command(cmd, tmpl, o);
-		break;
-	case CMD_FINDTEXT:
-		txt = (char*)tmpl;
-		if(strings) for (i = 0; i < ns; i++) {
-			if(strings[i] && 0 == strcmp(txt, strings[i])){
-				cl = i;
-				return true;
-				}
-			}
-		return false;
-		}
-	return false;
-}
-
-void
-Listbox::DoPlot(anyOutput *o)
-{
-	RECT mrk;
-
-	if(!ns) return;
-	if(!bmp && !CreateBitMap(o))return;
-	startY = 0;
-	if(sb && sb->GetValue(1, &spos)) startY = (int)(spos*(double)bmh);
-	o->SetLine(&Line);			o->SetFill(&Fill);
-	o->oRectangle(hcr.left, hcr.top, hcr.right+1, hcr.bottom);
-	if(sb){
-		sb->sLine = 1+(sb->hcr.bottom-sb->hcr.top-7*ybase)/ns;
-		sb->DoPlot(o);
-		}
-	o->CopyBitmap(hcr.left+1, hcr.top+1, bmp, 0, startY, (hcr.right-hcr.left)-2, 
-		(hcr.bottom-hcr.top)-2, false); 
-	mrk.left = hcr.left+2;		mrk.top = (cl)*(TextDef.iSize+_SBINC) - startY + hcr.top;
-	mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
-	if(mrk.bottom > (hcr.top+1) && mrk.top < (hcr.bottom-1)){
-		if(mrk.top < (hcr.top+1)) mrk.top = hcr.top+1;
-		if(mrk.bottom > (hcr.bottom-1)) mrk.bottom = hcr.bottom-1;
-		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, cl*(TextDef.iSize+_SBINC), 
-			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true);
-		}
-	o->UpdateRect(&cr, false);
-}
-
-bool
-Listbox::Select(int x, int y, anyOutput *o)
-{
-	int il;
-	RECT mrk;
-
-	if(IsInRect(&hcr, x, y)) {
-		il = (y+startY-hcr.top)/(TextDef.iSize+_SBINC);
-		if(il >= ns || il < 0){
-			o->UpdateRect(&hcr, false);
-			return false;
-			}
-		cl = il;
-		mrk.left = hcr.left+2;		mrk.top = (il)*(TextDef.iSize+_SBINC) - startY + hcr.top;
-		mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
-		o->CopyBitmap(hcr.left+1, hcr.top+1, bmp, 0, startY, (hcr.right-hcr.left)-2, 
-			(hcr.bottom-hcr.top)-2, false); 
-		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, (cl)*(TextDef.iSize+_SBINC), 
-			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true); 
-		o->UpdateRect(&hcr, false);
-		if((flags & TOUCHEXIT) && parent) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		return true;
-		}
-	if(sb)return sb->Select(x, y, o);
-	return false;
-}
-
-bool
-Listbox::GetInt(int id, int *val)
-{
-	*val = cl;
-	return true;
-}
-
-bool
-Listbox::GetText(int id, char *txt, int size)
-{
-	if(strings && ns && cl >= 0 && cl < ns){
-		rlp_strcpy(txt, size, strings[cl]);
-		return true;
-		}
-	return false;
-}
-
-void
-Listbox::MBtrack(MouseEvent *mev, anyOutput *o)
-{
-	if(sb) sb->MBtrack(mev, o);
-}
-
-bool
-Listbox::CreateBitMap(anyOutput *tmpl)
-{
-	int i, w, h;
-
-	if(bmp) DelBitmapClass(bmp);
-	bmp = 0L;
-	if(tmpl && strings) {
-		h = (TextDef.iSize+_SBINC) * ns;		w = (hcr.right - hcr.left);
-		if(bmp = NewBitmapClass(w, h, tmpl->hres, tmpl->vres)){
-			bmp->Erase(0x00ffffffL);
-			bmp->SetTextSpec(&TextDef);
-			bmh = h - (TextDef.iSize+_SBINC);
-			for(i = 0; i < ns; i++) {
-				bmp->oTextOut(5, i*(TextDef.iSize+_SBINC), strings[i], 0);
-				}
-			return true;
-			}
-		}
-	return false;
-}
-
-Treeview::Treeview(tag_DlgObj *par, DlgInfo *des, RECT rec, GraphObj *g)
-	:Dialog(par, des, rec)
-{
-	static DlgInfo sbd[] = {
-		{1, 2, 0, 0x0L, VSCROLL, 0L, 0, 0, 0, 0}};
-	RECT sr;
-
-	sbd[0].x = (sr.left = (cr.right-7*xbase))/xbase;
-	sbd[0].y = (sr.top = cr.top)/ybase;		sr.right = cr.right;	sbd[0].w = 7;							
-	sbd[0].h = ((sr.bottom = cr.bottom) - cr.top)/ybase;
-	sb = new ScrollBar(this, &sbd[0], sr, true);
-	Fill.color = 0x00ffffffL;			Line.color = 0x00000000L;
-	bmp = 0L;		startY = cl = 0;	hcr.right -= 7*xbase;
-	bmh = hcr.bottom - hcr.top;			bmw = hcr.right - hcr.left;	
-	ot = new ObjTree(0L, 0L, go = g);
-	if(ot) ot->Command(CMD_TEXTDEF, &TextDef, 0L);
-}
-
-Treeview::~Treeview()
-{
-	if(sb) delete(sb);				sb = 0L;
-	if(ot) delete(ot);				ot = 0L;
-	if(bmp) DelBitmapClass(bmp);	bmp = 0L;
-}
-
-bool
-Treeview::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	double ps;
-
-	ps = ((double)(cr.bottom - cr.top))/((double)(bmh+TextDef.iSize+_SBINC));
-	switch(cmd) {
-	case CMD_LAYERS:
-		ot->Command(cmd, tmpl, o);
-	case CMD_UPDATE:
-		if(bmp) DelBitmapClass(bmp);
-		bmh = hcr.bottom - hcr.top;			bmw = hcr.right - hcr.left;	
-		bmp = ot->CreateBitmap(&bmw, &bmh, o);
-		return true;
-	case CMD_REDRAW:
-		DoPlot(o);
-		return true;
-	case CMD_OBJTREE:
-		if(tmpl) *((ObjTree**)tmpl) = ot;
-		return true;
-	case CMD_PAGEUP:
-		ps *= -1.0;
-	case CMD_PAGEDOWN:
-		if(sb && sb->GetValue(1, &spos)){
-			spos += ps;
-			sb->Command(CMD_SETSCROLL, (void*)&spos, o);
-			DoPlot(o);
-			}
-		return true;
-	case CMD_SETSCROLL:
-		if(sb) return sb->Command(cmd, tmpl, o);
-		break;
-		}
-	return false;
-}
-
-void
-Treeview::DoPlot(anyOutput *o)
-{
-	RECT mrk;
-
-	if(!o || !ot) return;
-	ot->Command(CMD_TEXTDEF, &TextDef, 0L);
-	ns = ot->count_lines();
-	if(bmp) ot->DoPlot(bmp);
-	else if(!(bmp = ot->CreateBitmap(&bmw, &bmh, o)))return;
-	startY = 0;
-	if(sb && sb->GetValue(1, &spos)) startY = (int)(spos*(double)bmh);
-	o->SetLine(&Line);			o->SetFill(&Fill);
-	o->oRectangle(hcr.left, hcr.top, hcr.right+1, hcr.bottom);
-	if(sb){
-		sb->sLine = 1+(sb->hcr.bottom-sb->hcr.top-7*ybase)/ns;
-		sb->DoPlot(o);
-		}
-	o->CopyBitmap(hcr.left+1, hcr.top+1, bmp, 0, startY, (hcr.right-hcr.left)-2, 
-		(hcr.bottom-hcr.top)-2, false); 
-	mrk.left = hcr.left+2;		mrk.top = (cl)*(TextDef.iSize+_SBINC) - startY + hcr.top;
-	mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
-	if(mrk.bottom > (hcr.top+1) && mrk.top < (hcr.bottom-1)){
-		if(mrk.top < (hcr.top+1)) mrk.top = hcr.top+1;
-		if(mrk.bottom > (hcr.bottom-1)) mrk.bottom = hcr.bottom-1;
-		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, cl*(TextDef.iSize+_SBINC), 
-			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true);
-		}
-	o->UpdateRect(&cr, false);
-}
-
-bool
-Treeview::Select(int x, int y, anyOutput *o)
-{
-	int il;
-	RECT mrk;
-
-	if(IsInRect(&hcr, x, y)) {
-		il = (y+startY-hcr.top)/(TextDef.iSize+_SBINC);
-		if(il >= ns || il < 0){
-			o->UpdateRect(&hcr, false);
-			return false;
-			}
-		cl = il;
-		mrk.left = hcr.left+2;		mrk.top = (il)*(TextDef.iSize+_SBINC) - startY + hcr.top;
-		mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
-		o->CopyBitmap(hcr.left+1, hcr.top+1, bmp, 0, startY, (hcr.right-hcr.left)-2, 
-			(hcr.bottom-hcr.top)-2, false); 
-		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, (cl)*(TextDef.iSize+_SBINC), 
-			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true); 
-		o->UpdateRect(&hcr, false);
-		if((flags & TOUCHEXIT) && parent) 
-			parent->Command(CMD_ENDDIALOG, (void *)this, o);
-		return true;
-		}
-	if(sb)return sb->Select(x, y, o);
-	return false;
-}
-
-void
-Treeview::MBtrack(MouseEvent *mev, anyOutput *o)
-{
-	if(sb) sb->MBtrack(mev, o);
-}
-
-bool
-Treeview::GetInt(int id, int *val)
-{
-	*val = cl;
-	return true;
-}
-
-LinePat::LinePat(tag_DlgObj *par, DlgInfo * desc, RECT rec, LineDEF *line)
-	:Dialog(par, desc, rec)
-{
-	pPattern = &line->pattern;
-	Line.color = 0x00000000L;
-	bDraw = true;
-	cr.right = cr.left + (cr.bottom-cr.top)*32;
-}
-
-void
-LinePat::DoPlot(anyOutput *o)
-{
-	int i, h;
-	POINT ruler[2];
-	RECT gr;
-
-	h = cr.bottom-cr.top;
-	memcpy(&gr, &cr, sizeof(RECT));
-	o->SetLine(&Line);
-	for(i = 0; i < 32; i++) {
-		if(*pPattern &(1<<i)) Fill.color = 0x00ffffffL;
-		else Fill.color = 0x00808080L;
-		o->SetFill(&Fill);
-		o->oRectangle(cr.left+i*h, cr.top, cr.left+i*h+h, cr.top+h);
-		}
-	ruler[0].y = cr.top+h;
-	ruler[1].y = ruler[0].y + h/2;
-	for(i = 0; i <=4; i++) {
-		ruler[0].x = ruler[1].x = cr.left+i*8*h+(i ? -1:0);
-		o->oSolidLine(ruler);
-		}
-	gr.bottom += h;
-	o->UpdateRect(&gr, false);
-}
-
-void
-LinePat::MBtrack(MouseEvent *mev, anyOutput *o)
-{
-	int i, x, y;
-	DWORD mask, oldpat;
-
-	if(!(mev->StateFlags &1))return;	//draw with mouse left button
-	x = mev->x;						y = mev->y;
-	if(x > cr.left && x < cr.right && y > cr.top && y < cr.bottom){
-		i = (x-cr.left)/(cr.bottom-cr.top);
-		oldpat = *pPattern;
-		mask = (1<<i);
-		//use first hit (button down) to select color, true or false
-		bDraw = mev->Action == MOUSE_LBDOWN ? 0L != (*pPattern & mask) : bDraw;
-		if(bDraw) *pPattern &= ~mask;
-		else *pPattern |= mask;
-		if(oldpat != *pPattern) {
-			DoPlot(o);
-			if((flags & TOUCHEXIT) && parent) parent->Command(CMD_ENDDIALOG, (void *)this, o);
-			}
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Common code for multiple range dialogs
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-int com_StackDlg(int res, DlgRoot *Dlg, AccRange **rX, int *nx, char ***rd, int *currYR,
-	AccRange **rY, bool *bContinue, int *ny, int *maxYR, bool *updateYR)
-{
-	char **tmprd;
-
-	switch (res) {
-		case 1:
-			if(rX && nx && Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0] && 
-				(*rX = new AccRange(TmpTxt))) *nx = rX[0]->CountItems();
-			else if(nx) *nx = 0;
-			if(Dlg->GetText(154, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0]) {
-				if(rd[0][*currYR]) free(rd[0][*currYR]);
-				rd[0][*currYR] = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
-				}
-			break;
-		case 155:
-			res = -1;
-			*ny = 0;
-			if(rX) {
-				if(!(*currYR) && Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0]) {
-					if(*rX = new AccRange(TmpTxt)){
-						*nx = rX[0]->CountItems();
-						delete *rX;						*rX = 0L;
-						}
-					}
-				if(!(*nx)) {
-					ErrorBox("X-range is empty\nor not valid!\n\nEnter a valid range\n"
-						"for common x-values.");
-					*bContinue = true;
-					break;
-					}
-				}
-			if(Dlg->GetText(154, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0]) {
-				if(*rY = new AccRange(TmpTxt)){
-					*ny = rY[0]->CountItems();
-					delete *rY;
-					*rY = 0L;
-					}
-				}
-			if(!(*ny)) {
-				ErrorBox("Y-range is empty\nor not valid!\n\nEnter a valid range\n"
-					"for y-values with the same\nsize as the x-range.");
-				*bContinue = true;
-				break;
-				}
-			if((*currYR)+1 > *maxYR) {
-				tmprd = (char**)realloc(*rd, sizeof(char*)*((*currYR)+2));
-				if(tmprd) *rd = tmprd;
-				else break;
-				*maxYR = (*currYR)+1;
-				rd[0][*currYR] = 0L;
-				rd[0][(*currYR)+1] = 0L;
-				}
-			if(rd[0][*currYR]) free(rd[0][*currYR]);
-			rd[0][*currYR] = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);	//store y-ranges
-			*updateYR = true;
-			(*currYR)++;
-			Dlg->SetText(154, rd[0][*currYR]);
-			Dlg->Activate(154, true);
-			break;
-		case 156:
-			if(Dlg->GetText(154, TmpTxt, TMP_TXT_SIZE)){
-				if(rd[0][*currYR]) free(rd[0][*currYR]);
-				rd[0][*currYR] = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);;
-				}
-			else if(*currYR == *maxYR) (*maxYR)--;
-			(*currYR)--;
-			Dlg->SetText(154, rd[0][*currYR]);
-			*updateYR = true;
-			res = -1;
-			break;
-		}
-	return res;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Dialog meta compiler
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *std_text[] = {"OK", "Cancel", "", "x", "y", "z", "-", "Next >>", "<< Prev.",
-	"%", "color", "x-value", "y-value", "z-value", TmpTxt, TmpTxt+100, TmpTxt+200,
-	TmpTxt+300, TmpTxt+400, "left", "right", "top", "bottom", "x-axis", "y-axis", "z-axis"};
-
-DlgInfo *CompileDialog(char* tmpl, void **ptypes)
-{
-	char **lines, **fields, **flags, error[80];
-	int i, j, nlines, nfields, nflags, last_id, last_next;
-	unsigned int hv;
-	DlgInfo *Dlg;
-
-	std_text[2] = Units[defs.cUnits].display;
-	lines = split(tmpl, '\n', &nlines);
-	if(!lines || nlines <1 ||(!(Dlg = (DlgInfo*)malloc(nlines*sizeof(DlgInfo))))) return 0L;
-	for(i = last_id = last_next = 0; i < nlines; i++) if(lines[i] && lines[i][0]){
-		if(fields = split(lines[i], ',', &nfields)) {
-			if(nfields == 10) {
-				Dlg[i].id = Dlg[i].next = Dlg[i].first = 0;
-				if(fields[0][0]) {
-					if(fields[0][0] == '.') Dlg[i].id = (last_id += 1);
-					else 
-#ifdef USE_WIN_SECURE
-						sscanf_s(fields[0], "%d", &Dlg[i].id);		last_id = Dlg[i].id;
-#else
-						sscanf(fields[0], "%d", &Dlg[i].id);		last_id = Dlg[i].id;
-#endif
-					}
-				if(fields[1][0]) {
-					if(fields[1][0] == '+') Dlg[i].next = (last_id + 1);
-					else if(fields[1][0] == '.') Dlg[i].next = (last_next += 1);
-					else 
-#ifdef USE_WIN_SECURE
-						sscanf_s(fields[1], "%d", &Dlg[i].next);	last_next = Dlg[i].next;
-#else
-						sscanf(fields[1], "%d", &Dlg[i].next);		last_next = Dlg[i].next;
-#endif
-					}
-#ifdef USE_WIN_SECURE
-				sscanf_s(fields[2], "%d", &Dlg[i].first);
-#else
-				sscanf(fields[2], "%d", &Dlg[i].first);
-#endif
-				Dlg[i].flags = 0L;
-				if(flags = split(fields[3], '|', &nflags)) {
-					for(j = 0; j < nflags; j++){
-						hv = HashValue((unsigned char *)str_trim(flags[j]));
-						switch(hv) {
-						case 196904:		Dlg[i].flags |= CHECKED;		break;
-						case 4444568:		Dlg[i].flags |= TOUCHEXIT;		break;
-						case 284491160:		Dlg[i].flags |= TOUCHSELEXIT;	break;
-						case 235859:		Dlg[i].flags |= ISRADIO;		break;
-						case 942268:		Dlg[i].flags |= ISPARENT;		break;
-						case 4220131:		Dlg[i].flags |= OWNDIALOG;		break;
-						case 198260:		Dlg[i].flags |= DEFAULT;		break;
-						case 54530:			Dlg[i].flags |= HIDDEN;			break;
-						case 1011472:		Dlg[i].flags |= NOSELECT;		break;
-						case 3546:			Dlg[i].flags |= HREF;			break;
-						case 62296:			Dlg[i].flags |= NOEDIT;			break;
-						case 231330:		Dlg[i].flags |= LASTOBJ;		break;
-						case 224595:		Dlg[i].flags |= EXRADIO;		break;
-						case 60824:			Dlg[i].flags |= ODEXIT;			break;
-							}
-						free(flags[j]);
-						}
-					free(flags);
-					}
-				hv = HashValue((unsigned char *)str_trim(fields[4]));
-				switch(hv) {
-				case 17108522:		Dlg[i].type = PUSHBUTTON;	break;
-				case 3252180:		Dlg[i].type = ARROWBUTT;	break;
-				case 206036:		Dlg[i].type = COLBUTT;		break;
-				case 13602346:		Dlg[i].type = FILLBUTTON;	break;
-				case 261312:		Dlg[i].type = SHADE3D;		break;
-				case 948692:		Dlg[i].type = LINEBUTT;		break;
-				case 282068:		Dlg[i].type = SYMBUTT;		break;
-				case 3403091:		Dlg[i].type = FILLRADIO;	break;
-				case 1130835:		Dlg[i].type = SYMRADIO;		break;
-				case 787668:		Dlg[i].type = CHECKBOX;		break;
-				case 62812:			Dlg[i].type = RADIO0;		break;
-				case 62813:			Dlg[i].type = RADIO1;		break;
-				case 62814:			Dlg[i].type = RADIO2;		break;
-				case 15460:			Dlg[i].type = LTEXT;		break;
-				case 16996:			Dlg[i].type = RTEXT;		break;
-				case 13156:			Dlg[i].type = CTEXT;		break;
-				case 51300:			Dlg[i].type = EDTEXT;		break;
-				case 16235656:		Dlg[i].type = RANGEINPUT;	break;
-				case 51281:			Dlg[i].type = EDVAL1;		break;
-				case 14534481:		Dlg[i].type = INCDECVAL1;	break;
-				case 229196:		Dlg[i].type = HSCROLL;		break;
-				case 286540:		Dlg[i].type = VSCROLL;		break;
-				case 71804:			Dlg[i].type = TXTHSP;		break;
-				case 3418:			Dlg[i].type = ICON;			break;
-				case 14196:			Dlg[i].type = GROUP;		break;
-				case 909332:		Dlg[i].type = GROUPBOX;		break;
-				case 16408:			Dlg[i].type = SHEET;		break;
-				case 970282:		Dlg[i].type = ODBUTTON;		break;
-				case 957537:		Dlg[i].type = LISTBOX1;		break;
-				case 1108443:		Dlg[i].type = TREEVIEW;		break;
-				case 237304:		Dlg[i].type = LINEPAT;		break;
-				case 269332:		Dlg[i].type = TEXTBOX;		break;
-				case 787858:		Dlg[i].type = CHECKPIN;		break;
-				case 17284:			Dlg[i].type = TRASH;		break;
-				case 51627:			Dlg[i].type = CONFIG;		break;
-				default:			Dlg[i].type = NONE;			break;
-					}
-#ifdef USE_WIN_SECURE
-				sscanf_s(fields[5], "%d", &j);
-#else
-				sscanf(fields[5], "%d", &j);
-#endif
-				if(j < 0) Dlg[i].ptype = (void*)std_text[(-j)-1];
-				else if(j > 0) Dlg[i].ptype = ptypes[j-1];
-				else Dlg[i].ptype = (void*)0L;
-#ifdef USE_WIN_SECURE
-				sscanf_s(fields[6], "%d", &Dlg[i].x);	sscanf_s(fields[7], "%d", &Dlg[i].y);
-				sscanf_s(fields[8], "%d", &Dlg[i].w);	sscanf_s(fields[9], "%d", &Dlg[i].h);
-#else
-				sscanf(fields[6], "%d", &Dlg[i].x);		sscanf(fields[7], "%d", &Dlg[i].y);
-				sscanf(fields[8], "%d", &Dlg[i].w);		sscanf(fields[9], "%d", &Dlg[i].h);
-#endif
-				}
-			else {
-#ifdef USE_WIN_SECURE
-				sprintf_s(error, 80, "Wrong number of arguments in template line %d", i);
-#else
-				sprintf(error,"Wrong number of arguments in template line %d", i);
-#endif
-				InfoBox(error);
-				Dlg[i].id = Dlg[i].next = Dlg[i].first = 0;
-				Dlg[i].flags = 0L;		Dlg[i].type = NONE;
-				Dlg[i].ptype = 0L;		Dlg[i].x = Dlg[i].y = Dlg[i].w = Dlg[i].h = 0;
-				}
-			for(j = 0; j < nfields; j++)free(fields[j]);
-			free(fields);
-			}
-		free(lines[i]);
-		}
-	free(lines);
-	return Dlg;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Use marked ranges as default for dialog ranges
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool UseRangeMark(DataObj *d, int type, char *r0, char *r1, char *r2, char *r3,
-	char *r4, char *r5, char *r6, char *r7, char *r8, char *r9, char *r10)
-{
-	char *dst[] = {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10};
-	char *mrk, **ra =0L;;
-	int i, j, ranges=0;
-	bool success = false, bErr=false;
-	RECT vrc, rrc;
-	AccRange *ar;
-	bool bRet = true;
-
-	for(i = 0; i < 11; i++) if(dst[i]) *dst[i] = 0;
-	if(d && type) {
-		d->ValueRec(&vrc);
-		if(d->Command(CMD_GETMARK, &mrk, 0L)) {
-			ra = split(mrk, ';', &ranges);
-			ar = new AccRange(mrk);			ar->BoundRec(&vrc);			delete ar;
-			}
-		switch(type) {
-		case 1:
-			if(ranges > 1) {
-				for(i = 0; i < 11; i++) if(dst[i]) {
-					rlp_strcpy(dst[i], 100, i < ranges ? ra[i] : mkRangeRef(vrc.top, vrc.left, vrc.bottom, vrc.left));
-					}
-				success = true;
-				}
-			else if(vrc.top == vrc.bottom && (vrc.right - vrc.left) >2) {
-				for(i = 0; i < 11; i++) if(dst[i]){
-					rlp_strcpy(dst[i], 100, mkRangeRef(vrc.top+i, vrc.left, vrc.top+i, vrc.right));
-					}
-				success = true;
-				}
-			else if(vrc.right == vrc.left && (vrc.bottom - vrc.top) >2) {
-				for(i = 0; i < 11; i++) if(dst[i]){
-					rlp_strcpy(dst[i], 100, mkRangeRef(vrc.top, vrc.left+i, vrc.bottom, vrc.left+i));
-					}
-				success = true;
-				}
-			break;
-		case 2:
-			for(i = 0; i < 11; i++) if(dst[i]) *(dst[i]) = 0;
-			if(ranges == 1) {
-				for(i = 0, j = vrc.left; i < 11 && j <= vrc.right; i++, j++) if(dst[i]){
-					rlp_strcpy(dst[i], 100, mkRangeRef(vrc.top, vrc.left+i, vrc.bottom, vrc.left+i));
-					}
-				}
-			else if(ranges > 1) {
-				ar = new AccRange(ra[0]);		j = ar->CountItems();
-				ar->BoundRec(&rrc);				delete ar;
-				if(rrc.left == rrc.right)for(i = 1; i < 11 && i < ranges && !bErr; i++){
-					ar = new AccRange(ra[i]);	ar->BoundRec(&rrc);
-					if(rrc.left != rrc.right) bErr = true;
-					delete ar;
-					}
-				else if(rrc.top == rrc.bottom)for(i = 1; i < 11 && i < ranges && !bErr; i++){
-					ar = new AccRange(ra[i]);	ar->BoundRec(&rrc);
-					if(rrc.top != rrc.bottom) bErr = true;
-					delete ar;
-					}
-				else for(i = 1; i < 11 && i < ranges && !bErr; i++){
-					ar = new AccRange(ra[i]);
-					if(j != ar->CountItems()) bErr = true;
-					delete ar;
-					}
-				if(i < 12 && !bErr) for(i = 0; i < 11 && i < ranges; i++){
-					if(dst[i]) rlp_strcpy(dst[i], 100, ra[i]);
-					}
-				}
-			if(bErr) {
-				InfoBox("Cannot resolve multiple ranges\nwith different sizes.\n\n"
-					"Please select ranges with equal size!");
-				i = 12;		bRet = false;
-				}
-			success = true;
-			}
-		}
-	else {
-		vrc.left = vrc.top = 0;		vrc.right = vrc.bottom = 9;
-		}
-	if(!success) for(i = 0; i < 11; i++) if(dst[i]){
-		rlp_strcpy(dst[i], 100, mkRangeRef(vrc.top, vrc.left+i, vrc.bottom, vrc.left+i));
-		}
-	if(ra) {
-		for(i = 0; i < ranges; i++) if(ra[i]) free(ra[i]);
-		free(ra);
-		}
-	return bRet;
-}
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Select color out of predefined palette
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *NewColorTmpl =
-	"1,2,,DEFAULT,PUSHBUTTON,-1,200,10,45,12\n"
-	"2,3,,,PUSHBUTTON,-2,200,25,45,12\n"
-	"3,7,4,CHECKED | ISPARENT,GROUPBOX,1,200,55,45,40\n"
-	"4,5,,,LTEXT,0,210,60,45,8\n"
-	"5,6,,,LTEXT,0,210,70,45,8\n"
-	"6,,,,LTEXT,0,210,80,45,8\n"
-	"7,,8,CHECKED | ISPARENT,GROUP,0,0,0,0,0";
-
-DWORD GetNewColor(DWORD oldColor)
-{
-	void *ptypes[] = {(void*)" RGB "};
-	DlgInfo *ColDlg = CompileDialog(NewColorTmpl, ptypes);
-	DWORD currcol, newcol, palette[230];
-	int ilevels[] = {0x0, 0x40, 0x80, 0xc0, 0xe0, 0xff};
-	int i, j, ir, ig, ib, col, row, res;
-	DlgRoot *Dlg;
-	void *hDlg;
-
-	if(!(ColDlg = (DlgInfo *)realloc(ColDlg, 230 * sizeof(DlgInfo))))return oldColor;
-	for(ir=row=col=0, i=7, j=0; ir<6; ir++) for(ig=0; ig<6; ig++) for(ib=0; ib<6; ib++) {
-		ColDlg[i].id = i+1;					ColDlg[i].next = i+2;
-		ColDlg[i].type = COLBUTT;			ColDlg[i].first = 0;
-		palette[j] = currcol = (ilevels[ib]<<16)|(ilevels[ig]<<8)|(ilevels[ir]);
-		ColDlg[i].flags = TOUCHEXIT | ISRADIO;
-		if(currcol == oldColor) ColDlg[i].flags |= CHECKED;
-		ColDlg[i].ptype = (void *)&palette[j];
-		ColDlg[i].x = 5 + col*10;			ColDlg[i].y = 5 + row*10;
-		ColDlg[i].w = ColDlg[i].h = 10;
-		col++;
-		if(col >= 18) {
-			col = 0;			row++;
-			}
-		i++, j++;
-		}
-	j=j;
-	ColDlg[i-1].next = 0;
-	ColDlg[i-1].flags |= LASTOBJ;
-	newcol = currcol = oldColor;
-	Dlg = new DlgRoot(ColDlg, 0L);
-#ifdef USE_WIN_SECURE
-	sprintf_s(TmpTxt, 10, "R: 0x%02x", currcol & 0xff);				Dlg->SetText(4, TmpTxt);
-	sprintf_s(TmpTxt, 10, "G: 0x%02x", (currcol>>8) & 0xff);		Dlg->SetText(5, TmpTxt);
-	sprintf_s(TmpTxt, 10, "B: 0x%02x", (currcol>>16) & 0xff);		Dlg->SetText(6, TmpTxt);
-#else
-	sprintf(TmpTxt, "R: 0x%02x", currcol & 0xff);			Dlg->SetText(4, TmpTxt);
-	sprintf(TmpTxt, "G: 0x%02x", (currcol>>8) & 0xff);		Dlg->SetText(5, TmpTxt);
-	sprintf(TmpTxt, "B: 0x%02x", (currcol>>16) & 0xff);		Dlg->SetText(6, TmpTxt);
-#endif
-	hDlg = CreateDlgWnd("Select color", 50, 50, 510, 310, Dlg,0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:						//close window
-		case 2:						//cancel
-			currcol = oldColor;
-			break;
-		case 1:						//ok
-			currcol = newcol;
-			break;
-		default:
-			if(res > 7 && res < 225) {
-				currcol = newcol;
-				if(Dlg->GetColor(res, &newcol) && currcol != newcol) {
-					res = -1;
-#ifdef USE_WIN_SECURE
-					sprintf_s(TmpTxt, 10, "R: 0x%02x", newcol & 0xff);				Dlg->SetText(4, TmpTxt);
-					sprintf_s(TmpTxt, 10, "G: 0x%02x", (newcol>>8) & 0xff);			Dlg->SetText(5, TmpTxt);
-					sprintf_s(TmpTxt, 10, "B: 0x%02x", (newcol>>16) & 0xff);		Dlg->SetText(6, TmpTxt);
-#else
-					sprintf(TmpTxt, "R: 0x%02x", newcol & 0xff);					Dlg->SetText(4, TmpTxt);
-					sprintf(TmpTxt, "G: 0x%02x", (newcol>>8) & 0xff);				Dlg->SetText(5, TmpTxt);
-					sprintf(TmpTxt, "B: 0x%02x", (newcol>>16) & 0xff);				Dlg->SetText(6, TmpTxt);
-#endif
-					}
-				}
-			break;
-			}
-		}while (res < 0);
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	free(ColDlg);
-	return currcol;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Configure 3D shading
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *ConfShade_DlgTmpl = 
-	"1,2,,DEFAULT,PUSHBUTTON,-1,95,10,45,12\n"
-	"2,3,,,PUSHBUTTON,-2,95,25,45,12\n"
-	"3,4,100,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
-	"4,5,,ODEXIT,COLBUTT,1,60,10,15,10\n"
-	"5,6,,ODEXIT,COLBUTT,2,60,37,15,10\n"
-	"6,7,,ODEXIT,COLBUTT,1,60,49,15,10\n"
-	"7,8,,,RTEXT,3,25,37,30,9\n"
-	"8,9,,,RTEXT,4,25,49,30,9\n"
-	"9,,,,SHADE3D,5,95,50,22,20\n"
-	"100,101,,TOUCHEXIT,RADIO1,6,10,10,50,9\n"
-	"101,,,TOUCHEXIT | LASTOBJ,RADIO1,7,10,25,60,9";
-
-void ConfShade(FillDEF *oldfill)
-{
-	FillDEF newFill;
-	void *dyndata[] = {(void*)&oldfill->color, (void*)&oldfill->color2, (void*)"HI-color:",
-		(void*)"LO-color:", (void*)&newFill, (void*)"fixed color:", (void*)"use light sorce:"};
-	DlgInfo *ShadeDlg = CompileDialog(ConfShade_DlgTmpl, dyndata);
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res;
-	bool bRedraw;
-	anyOutput *o;
-	RECT rc_prev;
-
-	if(!oldfill) return;
-	memcpy(&newFill, oldfill, sizeof(FillDEF));
-	newFill.type = FILL_LIGHT3D;
-	if(!(Dlg = new DlgRoot(ShadeDlg, 0L))) return;
-	if(oldfill->type & FILL_LIGHT3D) Dlg->SetCheck(101, 0L, true);
-	else {
-		Dlg->SetCheck(100, 0L, true);
-		newFill.color2 = newFill.color;
-		}
-	hDlg = CreateDlgWnd("Shade and Fill Color", 50, 50, 300, 180, Dlg, 0x0L);
-	bRedraw = true;
-	o = Dlg->GetOutputClass();			o->LightSource(32.0, 16.0);
-	rc_prev.left =240;					rc_prev.right = 280;
-	rc_prev.top = 100;					rc_prev.bottom = 140;
-	do {
-		if(bRedraw) {
-			Dlg->DoPlot(0L);			o->SetFill(&newFill);
-			o->oSphere(260, 120, 17, 0L, 0);
-			o->UpdateRect(&rc_prev, false);
-			bRedraw = false;
-			}
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 4:		case 100:
-			Dlg->GetColor(4, &newFill.color);
-			newFill.color2 = newFill.color;
-			Dlg->SetCheck(100, 0L, true);
-			bRedraw = true;
-			res = -1;
-			break;
-		case 5:		case 6:		case 101:
-			Dlg->GetColor(5, &newFill.color2);
-			Dlg->GetColor(6, &newFill.color);
-			Dlg->SetCheck(101, 0L, true);
-			bRedraw = true;
-			res = -1;
-			break;
-			}
-		}while (res < 0);
-	if(res == 1) {
-		memcpy(oldfill, &newFill, sizeof(FillDEF));
-		if(Dlg->GetCheck(100)) oldfill->type &= ~FILL_LIGHT3D;
-		else oldfill->type |= FILL_LIGHT3D;
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;			 free(ShadeDlg);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Select fill pattern
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void GetNewFill(FillDEF *oldfill)
-{
-	LineDEF PrevLine;
-	FillDEF PrevFill;
-	unsigned int EnumFills[] = {FILL_NONE, FILL_HLINES, FILL_VLINES, FILL_HVCROSS, FILL_DLINEU, FILL_DLINED,
-	FILL_DCROSS, FILL_STIPPLE1, FILL_STIPPLE2, FILL_STIPPLE3, FILL_STIPPLE4, 
-	FILL_STIPPLE5, FILL_ZIGZAG, FILL_COMBS, FILL_BRICKH, FILL_BRICKV, FILL_BRICKDU, 
-	FILL_BRICKDD, FILL_TEXTURE1, FILL_TEXTURE2, FILL_WAVES1, FILL_SCALES, FILL_SHINGLES, 
-	FILL_WAVES2, FILL_HERRING, FILL_CIRCLES, FILL_GRASS, FILL_FOAM, FILL_RECS, FILL_HASH,
-	FILL_WATER};
-	double fscale;
-	DlgInfo FillDlgBase[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 170, 10, 45, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 170, 25, 45, 12},
-		{3, 100, 500, CHECKED | ISPARENT, GROUP, NULL, 0, 0, 0, 0},
-		{100, 101, 0, TOUCHEXIT, FILLBUTTON, (void *)&PrevFill, 170, 75, 45, 45},
-		{101, 102, 0, 0x0L, RTEXT, (void*)"line width", 78, 95, 40, 8},
-		{102, 103, 0, TOUCHEXIT, INCDECVAL1, &PrevLine.width, 119, 95, 32, 10},
-		{103, 104, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 152, 95, 15, 8},
-		{104, 105, 0, 0x0L, RTEXT, (void*)"line color", 5, 95, 30, 8},
-		{105, 106, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, 0x0L, 36, 95, 25, 10},
-		{106, 107, 0, 0x0L, RTEXT, (void*)"pattern size", 78, 110, 40, 8},
-		{107, 108, 0, TOUCHEXIT, INCDECVAL1, (void*)&fscale, 119, 110, 32, 10},
-		{108, 109, 0, 0x0L, LTEXT, (void*)"%", 152, 110, 8, 8},
-		{109, 110, 0, 0x0L, RTEXT, (void*)"BG color", 5, 110, 30, 8},
-		{110, 0, 0, OWNDIALOG | TOUCHEXIT, COLBUTT, 0x0L, 36, 110, 25, 10}};
-	DlgInfo *FillDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, j, res;
-	anyOutput *o;
-	bool bRedraw;
-	double tmp;
-
-	PrevLine.width = (oldfill && oldfill->hatch)? oldfill->hatch->width : 0.2f;
-	PrevLine.patlength = 1.0f;
-	PrevLine.color = (oldfill && oldfill->hatch)? oldfill->hatch->color : 0x00000000L;
-	PrevLine.pattern = 0x00000000L;
-	PrevFill.type = oldfill ? oldfill->type : FILL_NONE;
-	PrevFill.color = oldfill ? oldfill->color : 0x00ffffffL;
-	PrevFill.scale = oldfill ? oldfill->scale : 1.0f;
-	PrevFill.hatch = &PrevLine;
-	PrevFill.color2 = oldfill ? oldfill->color2 : 0x00ffffffL;
-	fscale = PrevFill.scale *100.0f;
-	FillDlg = (DlgInfo*)calloc(NUM_FILLS + 15, sizeof(DlgInfo));
-	if(FillDlg) {
-		memcpy(FillDlg, FillDlgBase, 14 * sizeof(DlgInfo));
-		for(i = 14, j = 0; j < NUM_FILLS; i++, j++) {
-			FillDlg[i].id = j+500;				FillDlg[i].next = j+501;
-			FillDlg[i].first = 0;				FillDlg[i].flags = TOUCHEXIT;
-			FillDlg[i].type = FILLRADIO;		FillDlg[i].ptype = (void*)(EnumFills+j);
-			FillDlg[i].x = (j &0x07)*20+5;		FillDlg[i].y = (j>>3)*20+5;
-			FillDlg[i].w = FillDlg[i].h = 20;
-			}
-		FillDlg[i-1].next = 0;
-		FillDlg[i-1].flags |= LASTOBJ;
-		}
-	else return;
-	Dlg = new DlgRoot(FillDlg, 0L);
-	for(i = Dlg->FindIndex(500), j = 0; FillDlg[i].type == FILLRADIO; i++, j++)
-		if(*((int*)FillDlg[i].ptype) == PrevFill.type)Dlg->SetCheck(500+j, 0L, true);
-	Dlg->SetColor(105, PrevLine.color);
-	Dlg->SetColor(110, PrevFill.color);
-	hDlg = CreateDlgWnd("Fill patterns", 50, 50, 450, 280, Dlg, 0x0L);
-	bRedraw = true;
-	o = Dlg->GetOutputClass();
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		if (res >= 0) switch (res) {		// first the radio buttons: fill type
-			default:						// test for pattern
-				i = Dlg->FindIndex(res);
-				if(FillDlg[i].type == FILLRADIO) {
-					bRedraw = true;
-					i = *((int*)FillDlg[i].ptype);
-					if(i != PrevFill.type){		// process double click on pattern
-						PrevFill.type = i;
-						res = -1;
-						}
-					else res = 1;			// assume OK pressed
-					break;
-					}
-				else break;
-			case 110:						// fill color changed
-			case 102:						// line width changed
-			case 105:						// line color changed
-			case 107:						// scaling changed
-			case 100:						// update preview
-				res = -1;					// ...these buttons do not exit dialog
-			case 1:							// but exit with OK
-				if((bRedraw = Dlg->GetValue(102, &tmp)))PrevLine.width = tmp;
-				if(bRedraw) bRedraw = Dlg->GetColor(105, &PrevLine.color);
-				if(bRedraw) bRedraw = Dlg->GetColor(110, &PrevFill.color);
-				if(bRedraw && (bRedraw = Dlg->GetValue(107, &tmp))) 
-					PrevFill.scale = tmp/100.0;
-				break;
-			}
-		if (o && bRedraw) {					// send update preview to dialog
-			Dlg->ForEach(CMD_DOPLOT, Dlg->FindIndex(100), o);
-			bRedraw = false;
-			}
-		}while (res < 0);
-	switch(res) {
-	case 1:									//OK button pressed
-		if(oldfill) {
-			oldfill->type = PrevFill.type;
-			oldfill->color = PrevFill.color;
-			oldfill->scale = PrevFill.scale;
-			if(oldfill->hatch) {
-				oldfill->hatch->width = PrevLine.width;
-				oldfill->hatch->patlength = PrevLine.patlength;
-				oldfill->hatch->color = PrevLine.color;
-				oldfill->hatch->pattern = PrevLine.pattern;
-				}
-			}
-		break;
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	free(FillDlg);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// execute layers dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char* LayerDlg_Tmpl =
-	"1,3,0,DEFAULT,PUSHBUTTON,-1,165,10,45,12\n"
-	"3,20,100,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"20,550,,,CHECKPIN,0,10,0,12,8\n"
-	"100,101,,TOUCHEXIT,TREEVIEW,1,10,15,100,70\n"
-	"101,102,,,EDTEXT,2, 120, 25, 90, 10\n"
-	"102,200,150,ISPARENT | CHECKED | HIDDEN,GROUP,0L,0,0,0,0\n"
-	"150,151,,TOUCHEXIT,RADIO1,3,120,35,40,9\n"
-	"151,,,TOUCHEXIT,RADIO1,4,160,35,40,9\n"
-	"200,500,,,LTEXT,5,10,5,110,9\n"
-	"500,501,600,ISPARENT | CHECKED | HIDDEN,GROUP,0,0,0,0,0\n"
-	"501,,,HIDDEN | TOUCHEXIT,TRASH,0,125,72,15,15\n"
-	"550,,,HIDDEN | TOUCHEXIT,CONFIG,0,140,72,15,15\n"
-	"600,601,,TOUCHEXIT, ODBUTTON,6,125,50,15,15\n"
-	"601,602,,TOUCHEXIT, ODBUTTON,6,145,50,15,15\n"
-	"602,603,,TOUCHEXIT, ODBUTTON,6,165,50,15,15\n"
-	"603,,,LASTOBJ | TOUCHEXIT, ODBUTTON,6,185,50,15,15";
-
-bool ShowLayers(GraphObj *root)
-{
-	char curr_name[50];
-	void *dyndata[] = {(void*)root, (void*)curr_name, (void*)"hidden", (void*)"visible",
-		(void*)"Layers:", (void*)OD_DrawOrder};
-	DlgInfo *LayerDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, cl;
-	bool bContinue = false;
-	ObjTree *ot = 0L;
-	GraphObj *cgo = 0L;
-
-	if(!root || !(LayerDlg = CompileDialog(LayerDlg_Tmpl, dyndata))) return false;
-	rlp_strcpy(curr_name, 50, "(root)");
-	if(!(Dlg = new DlgRoot(LayerDlg, 0L)))return false;
-	Dlg->ItemCmd(100, CMD_OBJTREE, &ot);
-	if(!ot) {
-		delete Dlg;			return false;
-		}
-	Dlg->SetText(101, ot->get_name(0));		Dlg->Activate(101, false);
-	Dlg->SetColor(150, 0x00000080L);		Dlg->SetColor(151, 0x00008000L);
-	if(root->Id == GO_GRAPH || root->Id == GO_PAGE) Dlg->ShowItem(550, true);
-	hDlg = CreateDlgWnd("Layer Control", 50, 50, 440, 210, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(bContinue || Dlg->GetCheck(20)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 550:
-			bContinue = true;
-		case 501:
-			if (res == 501) {
-				if(cgo && cgo->parent)	cgo->parent->Command(CMD_DELOBJ, cgo, 0L);
-				Dlg->ItemCmd(100, CMD_LAYERS, 0L);
-				}
-		case 100:
-			if(Dlg->GetInt(100, &cl)) Dlg->SetText(101, ot->get_name(cl));
-			switch(ot->get_vis(cl)) {
-			case 0:
-				Dlg->SetCheck(150, 0L, true);			Dlg->ShowItem(102, true);
-				break;
-			case 1:
-				Dlg->SetCheck(151, 0L, true);			Dlg->ShowItem(102, true);
-				break;
-			case 2:
-				Dlg->ShowItem(102, false);
-				break;
-				}
-			if((cgo = ot->get_obj(cl)) && cgo->parent) {
-				if(cgo->parent->Id == GO_STACKBAR || cgo->parent->Id == GO_STACKPG ||
-					cgo->parent->Id == GO_WATERFALL || cgo->parent->Id == GO_PLOT3D ||
-					cgo->parent->Id == GO_FUNC3D) {
-					Dlg->ShowItem(501, true);
-					}
-				else if(cgo->parent->Id == GO_GRAPH || cgo->parent->Id == GO_PAGE){ 
-					Dlg->ShowItem(500, cgo->parent->Command(CMD_HASSTACK, 0L, 0L));
-					Dlg->ShowItem(501, true);
-					}
-				else {
-					Dlg->ShowItem(500, false);			Dlg->ShowItem(501, false);
-					}
-				if(cgo->Id == GO_GRAPH || cgo->Id == GO_PAGE || cgo->Id == GO_POLARPLOT
-					|| cgo->Id == GO_GRID3D){
-					Dlg->ShowItem(550, true);
-					if(res == 550) if(cgo->Command(CMD_CONFIG, 0L, 0L))
-						cgo->Command(CMD_REDRAW, 0L, 0L);
-					}
-				else Dlg->ShowItem(550, false);
-				}
-			Dlg->Command(CMD_REDRAW, 0L, 0L);
-			res = -1;
-			break;
-		case 150:		case 151:
-			ot->set_vis(cl, res == 151);
-			Dlg->ItemCmd(100, CMD_UPDATE, 0L);			Dlg->Command(CMD_REDRAW, 0L, 0L);
-			res = -1;
-			break;
-		case 600:	case 601:	case 602:	case 603:
-			if(cgo && cgo->parent){
-				ExecDrawOrderButt(cgo->parent, cgo, res);
-				}
-			Dlg->ItemCmd(100, CMD_UPDATE, 0L);			Dlg->Command(CMD_REDRAW, 0L, 0L);
-			res = -1;
-			}
-		}while(res <0);
-	CloseDlgWnd(hDlg);		delete Dlg;			free(LayerDlg);
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// display a welcome banner
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void ShowBanner(bool show)
-{
-	int icon = ICO_RLPLOT;
-	static DlgInfo BannerDlg[] = {
-		{1, 2, 0, 0x0L, PUSHBUTTON, 0L, 0, 0, 100, 40},
-		{2, 3, 0, 0x0L, ICON, (void*)&icon, 5, 5, 20, 20},
-		{3, 4, 0, 0x0L, LTEXT, (void*)"RLPlot", 40, 5, 50, 20},
-		{4, 5, 0, 0x0L, LTEXT, (void*)SZ_VERSION, 8, 25, 35, 9},
-		{5, 0, 0, LASTOBJ, LTEXT, (void*)"... is loading", 45, 25, 50, 9}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	bool init = true;
-	int res, cnt = 5;
-
-	if(!show) return;
-	if(!(Dlg = new DlgRoot(BannerDlg, 0L)))return;
-#ifdef _WINDOWS
-	Dlg->TextSize(3, 36);
-#else
-	Dlg->TextSize(3, 24);
-#endif
-	Dlg->TextStyle(3, TXS_ITALIC | TXS_BOLD);
-	Dlg->TextFont(3, FONT_TIMES);
-	Dlg->SetColor(3, 0x00ff0000L);
-	hDlg = CreateDlgWnd("RLPlot", 50, 50, 200, 80, Dlg, 0x7L);
-	ShowDlgWnd(hDlg);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:						//loose focus: get it again
-			ShowDlgWnd(hDlg);
-			cnt = 5;		res = -1;
-			break;
-		case -2:					//Timer event
-			if(init) {
-				init = false;
-				FindBrowser();		SpreadMain(true);
-				ShowDlgWnd(hDlg);	cnt = 4;
-				}
-			if((cnt--) <=0) res = 1;
-			break;
-			}
-		}while(res < 0);
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// the RLPlot about dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void RLPlotInfo()
-{
-	int ico_rlp = ICO_RLPLOT;
-#ifdef _WINDOWS
-	DlgInfo AboutDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 40, 110, 40, 12},
-		{2, 3, 0, 0x0L, ICON, (void*)&ico_rlp, 10, 10, 16, 16},
-		{3, 4, 0, 0x0L, LTEXT, (void*)"RLPlot", 40, 10, 60, 18},
-		{4, 5, 0, 0x0L, CTEXT, (void*)"scientific plotting program", 10, 30, 100, 8},
-		{5, 6, 0, 0x0L, CTEXT, (void*)"version "SZ_VERSION, 10, 38, 100, 8},
-		{6, 7, 0, HREF, CTEXT, (void*)"http://rlplot.sourceforge.net/", 5, 46, 110, 8},
-		{7, 8, 0, 0x0L, CTEXT, (void*)"Copyright (C) 2002-2007 R. Lackner", 5, 54, 110, 8},
-		{8, 9, 0, 0x0L, CTEXT, (void*)"reinhard.lackner at uibk.ac.at", 5, 62, 110, 9},
-		{9, 10, 0, 0x0L, CTEXT, (void*)"This is free software published", 5, 80, 110, 8},
-		{10, 11, 0, 0x0L, CTEXT, (void*)"under the GNU general public.", 5, 88, 110, 8},
-		{11, 0, 0, LASTOBJ, CTEXT, (void*)"license (GPL).", 5, 96, 110, 9}};
-#else
-	int ico_qt = ICO_QT;
-	DlgInfo AboutDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 40, 128, 40, 12},
-		{2, 3, 0, 0x0L, ICON, (void*)&ico_rlp, 10, 10, 16, 16},
-		{3, 4, 0, 0x0L, LTEXT, (void*)"RLPlot", 40, 10, 60, 18},
-		{4, 5, 0, 0x0L, CTEXT, (void*)"scientific plotting program", 10, 30, 100, 8},
-		{5, 6, 0, 0x0L, CTEXT, (void*)"version "SZ_VERSION, 10, 38, 100, 8},
-		{6, 7, 0, HREF, CTEXT, (void*)"http://rlplot.sourceforge.net/", 5, 46, 110, 8},
-		{7, 8, 0, 0x0L, CTEXT, (void*)"Copyright (C) 2002-2007 R. Lackner", 5, 54, 110, 8},
-		{8, 9, 0, 0x0L, CTEXT, (void*)"reinhard.lackner at uibk.ac.at", 5, 62, 110, 9},
-		{9, 10, 0, 0x0L, LTEXT, (void*)"powered by Trolltech\'s Qt", 35, 72, 80, 8},
-		{10, 11, 0, HREF, LTEXT, (void*)"http://www.trolltech.com", 35, 80, 80, 9},
-		{11, 12, 0, TOUCHEXIT, ICON, (void*)&ico_qt, 5, 72, 25, 25},
-		{12, 13, 0, 0x0L, CTEXT, (void*)"This is free software published", 5, 100, 110, 8},
-		{13, 14, 0, 0x0L, CTEXT, (void*)"under the GNU general public.", 5, 108, 110, 8},
-		{14, 0, 0, LASTOBJ, CTEXT, (void*)"license (GPL).", 5, 116, 110, 9}};
-#endif // _WINDOWS
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res;
-
-	if((Dlg = new DlgRoot(AboutDlg, 0L))) {
-		Dlg->TextStyle(3, TXS_ITALIC | TXS_BOLD);
-		Dlg->TextFont(3, FONT_TIMES);
-#ifdef _WINDOWS
-		Dlg->TextSize(3, 36);
-#else
-		Dlg->TextSize(3, 32);
-#endif // _WINDOWS
-		Dlg->SetColor(3, 0x00ff0000L);
-		}
-	else return;
-#ifdef _WINDOWS
-	hDlg = CreateDlgWnd("About RLPlot", 50, 50, 240, 280, Dlg, 0x0L);
-#else
-	hDlg = CreateDlgWnd("About RLPlot", 50, 50, 240, 310, Dlg, 0x0L);
-#endif // _WINDOWS
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		if(res == 11) Qt_Box();
-		}while(res <0);
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// change spreadsheet settings
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char * SSDlg_Tmpl = 
-	"1,2,,DEFAULT,PUSHBUTTON,-1,122,10,40,12\n"
-	"2,3,,,PUSHBUTTON,-2,122,25,40,12\n"
-	"3,,5,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
-	"5,6,100,ISPARENT | CHECKED,SHEET,1,5,10,108,80\n"
-	"6,,200,ISPARENT,SHEET,2,5,10,108, 80\n"
-	"100,101,,,LTEXT,3,15,25,60,8\n"
-	"101,102,,,EDTEXT,-16,40,37,40,10\n"
-	"102,103,,,LTEXT,4,15,52,60,8\n"
-	"103,,,,EDTEXT,-17,40,64,40,10\n"
-	"200,201,,,RTEXT,5,10,29,40,8\n"
-	"201,202,,,EDVAL1,6,52,29,25,10\n"
-	"202,203,,,LTEXT,7,79,29,20,8\n"
-	"203,204,,,RTEXT,8,10,44,40,8\n"
-	"204,205,,,EDVAL1,9,52,44,25,10\n"
-	"205,206,,,LTEXT,7,79,44,20,8\n"
-	"206,207,,,RTEXT,10,10,59,40,8\n"
-	"207,208,,,EDVAL1,11,52,59,25,10\n"
-	"208,209,,,LTEXT,-3,79,59,20,8\n"
-	"209,210,,,RTEXT,12,10,74,40,8\n"
-	"210,211,,,INCDECVAL1,13,52,74,33,10\n"
-	"211,,,LASTOBJ,LTEXT,-10,87,74,20,8";
-bool
-DoSpShSize(DataObj *dt, GraphObj *parent)
-{
-	TabSHEET tab1 = {0, 45, 10, "Dimensions"};
-	TabSHEET tab2 = {45, 108, 10, "Width and Height"};
-	double fw, cw, ch, th;
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)"number of columns:", (void*)"number of rows:",
-		(void*)"row buttons", (void*)&fw, (void*)"[digits]", (void*)"column width", (void*)&cw,
-		(void*)"row height", (void*)&ch, (void*)"text size", (void*)&th};
-	DlgInfo *SSDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	int w1, w2, h1, h2, res, celldim[3], ith;
-	bool bRet = false;
-	double fw1, cw1, ch1, th1;
-
-	if(!dt || !dt->GetSize(&w1, &h1)) return false;
-	if(!(SSDlg = CompileDialog(SSDlg_Tmpl, dyndata))) return false;
-	dt->Command(CMD_GET_CELLDIMS, &celldim, 0L);
-	fw1 = fw = NiceValue(((double)celldim[0])/((double)(celldim[2]-2)/2.0));
-	cw1 = cw = NiceValue(((double)celldim[1])/((double)(celldim[2]-2)/2.0));
-	switch(defs.cUnits) {
-	case 1:		ch = NiceValue(((double)(celldim[2]-2))*0.0259183673);	break;
-	case 2:		ch = NiceValue(((double)(celldim[2]-2))/98.0);			break;
-	default:	ch = NiceValue(((double)(celldim[2]-2))*0.259183673);	break;
-		}
-	ch1 = ch;						th = th1 = defs.ss_txt*100.0;
-#ifdef USE_WIN_SECURE
-	sprintf_s(TmpTxt+100, 40, "%d", w1);		sprintf_s(TmpTxt+200, 40, "%d", h1);
-#else
-	sprintf(TmpTxt+100, "%d", w1);			sprintf(TmpTxt+200, "%d", h1);
-#endif
-	Dlg = new DlgRoot(SSDlg, dt);
-	hDlg = CreateDlgWnd("Change spread sheet settings", 50, 50, 340, 220, Dlg, 0x0L);
-	Dlg->GetValue(201, &fw1);	Dlg->GetValue(204, &cw1);	Dlg->GetValue(207, &ch1);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 1:					//OK pressed
-			if(Dlg->GetText(101, TmpTxt+100, 40) && Dlg->GetText(103, TmpTxt+200, 40)) {
-				w2 = atol(TmpTxt+100);		h2 = atol(TmpTxt+200);
-				w2 = w2 > 0 ? w2: w1;	h2 = h2 > 0 ? h2: h1;
-				}
-			else res = -1;
-			break;
-			}
-		}while(res <0);
-	if(res == 1){
-		if(Dlg->GetValue(207, &ch) && Dlg->GetValue(210, &th) && ch > 0.001) {
-			Undo.ValFloat(parent, &defs.ss_txt, 0L);
-			defs.ss_txt = th = th >= 10.0 && th <= 100 ? th/100.0 : 1.0; 
-			switch(defs.cUnits) {
-			case 1:		
-				celldim[2] = iround(ch/0.0259183673)+2;		ith = iround(th * ch/0.0259183673);
-				break;
-			case 2:		
-				celldim[2] = iround(ch*98.0)+2;				ith = iround(th * ch*98.0);
-				break;
-			default:	
-				celldim[2] = iround(ch/0.259183673)+2;		ith = iround(th * ch/0.259183673);
-				break;
-				}
-			bRet = dt->Command(CMD_TEXTSIZE, (void*)(& ith), 0L);
-			}
-		Dlg->GetValue(201, &fw);	Dlg->GetValue(204, &cw);
-		if(fw >0.001 && (fw != fw1 || ch != ch1))
-			celldim[0] = iround(fw * ((double)(celldim[2]-3)/2.0));
-		if(cw >0.001 && (cw != cw1 || ch != ch1))
-			celldim[1] = iround(cw * ((double)(celldim[2]-3)/2.0));
-		if(fw != fw1 || cw != cw1 || ch != ch1)
-			dt->Command(CMD_SET_CELLDIMS, &celldim, 0L);
-		if(!dt->ChangeSize(w2, h2, true))
-			ErrorBox("Failed to set new dimensions\nof Spreadsheet.");
-		else bRet = true;
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;			free(SSDlg);
-	return bRet;
-}
-
-
-bool FillSsRange(DataObj *d, char **range, GraphObj *msg_go)
-{
-	TabSHEET tab1 = {0, 37, 10, "fill range "};
-	char *ra = range ? *range:0L;
-	double startval = 1.0, stepval = 1.0;
-	static char *formula = (char*)malloc(500 * sizeof(char));
-	if(formula && CurrText && CurrText->isFormula()) {
-		if(CurrText->GetText(TmpTxt,TMP_TXT_SIZE, false)) rlp_strcpy(formula, 500, TmpTxt);
-		}
-	if(formula) rlp_strcpy(formula, 500, "=a1");
-	DlgInfo RangeDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 162, 5, 38, 12},
-		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 162, 20, 38, 12},
-		{3, 0, 5, CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{5, 0, 6, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 150, 75},
-		{6, 7, 0, 0x0L, RANGEINPUT, (void*)ra, 10, 25, 140, 10},
-		{7, 8, 0, CHECKED | TOUCHEXIT, RADIO1, (void*)"with values", 10, 37, 45, 8},
-		{8, 9, 0, TOUCHEXIT, RADIO1, (void*)"with formulas", 60, 37, 60, 8},
-		{9, 10, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
-		{10, 20, 11, CHECKED, GROUP, 0L, 0, 0, 0, 0},
-		{11, 12, 0, 0x0L, RTEXT, (void*)"start value=", 10, 60, 35, 8},
-		{12, 13, 0, 0x0L, EDVAL1, &startval, 45, 60, 30, 10},
-		{13, 14, 0, 0x0L, RTEXT, (void*)"increment by", 77, 60, 43, 8},
-		{14, 0, 0, 0x0L, EDVAL1, &stepval, 122, 60, 30, 10},
-		{20, 0, 21, CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
-		{21, 22, 0, 0x0L, RTEXT, (void*)"first cell formula:", 10, 52, 50, 8},
-		{22, 23, 0, 0x0L, EDTEXT, (void*)formula, 60, 52, 90, 10},
-		{23, 24, 0, 0x0L, LTEXT, (void*)"For a list with available funtions see:", 10, 65, 90, 8},
-		{24, 0, 0, HREF | LASTOBJ | TOUCHEXIT, LTEXT, (void*)"http://rlplot.sourceforge.net/Docs/parser.html", 10, 72, 90, 8}};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, res, row, col, r1, c1;
-	bool bRet = false, bContinue = false;
-	AccRange *rF;
-	RECT rc_dest;
-	anyOutput *cdisp = 	Undo.cdisp;
-
-	if(!d || !range) return false;
-	if(!(Dlg = new DlgRoot(RangeDlg, d))) return false;
-#ifdef _WINDOWS
-	for(i = 23; i <= 24; i++) Dlg->TextSize(i, 12);
-#else
-	for(i = 23; i <= 24; i++) Dlg->TextSize(i, 10);
-#endif
-	hDlg = CreateDlgWnd("Fill Spreadsheet Range", 50, 50, 414, 206, Dlg, 0x0L);
-	do{
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res){
-		case 0:
-			if(bContinue) {
-				bContinue = false;				res = -1;
-				}
-			else if(Dlg->GetCheck(9)) res = -1;
-			break;
-		case 1:
-			ra = 0L;
-			if(!Dlg->GetText(6, TmpTxt, TMP_TXT_SIZE)) {
-				InfoBox("Range not specified\nor not valid.");
-				bContinue = true;				res = -1;
-				}
-			else ra = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-			rc_dest.left = rc_dest.right = rc_dest.top = rc_dest.bottom = 0;
-			break;
-		case 7:
-			Dlg->ShowItem(10, true);			Dlg->ShowItem(20, false);
-			Dlg->Command(CMD_REDRAW, 0L, 0L);	res = -1;
-			break;
-		case 8:
-			Dlg->ShowItem(10, false);	Dlg->ShowItem(20, true);
-			Dlg->Command(CMD_REDRAW, 0L, 0L);	res = -1;
-			break;
-		case 24:
-			bContinue=true;
-			res = -1;
-			break;
-			}
-		}while(res <0);
-	Undo.SetDisp(cdisp);
-	if(res == 1 && ra) {
-		if(Dlg->GetCheck(7)) {
-			Dlg->GetValue(12, &startval);	Dlg->GetValue(14, &stepval);
-			if((rF = new AccRange(ra)) && rF->GetFirst(&col, &row)) {
-				rF->BoundRec(&rc_dest);
-				Undo.DataObject(msg_go, Undo.cdisp, d, &rc_dest, 0L);
-				for( ; rF->GetNext(&col, &row); startval += stepval) {
-					d->SetValue(row, col, startval);
-					}
-				delete rF;							bRet = true;
-				}
-			}
-		else if(Dlg->GetCheck(8)) {
-			if((rF = new AccRange(ra)) && rF->GetFirst(&col, &row) && 
-				Dlg->GetText(22, TmpTxt, TMP_TXT_SIZE) && formula){
-				rlp_strcpy(formula, 500, TmpTxt);
-				r1 = row;		c1 = col;
-				for( ; rF->GetNext(&col, &row); startval += stepval) {
-					if(formula[0] == '=') {
-						MoveFormula(d, formula, TmpTxt, TMP_TXT_SIZE, col-c1, row-r1, -1, -1);
-						d->SetText(row, col, TmpTxt);
-						}
-					else d->SetText(row, col, formula);
-					}
-				delete rF;							bRet = true;
-				}
-			}
-		free(ra);
-		}
-	CloseDlgWnd(hDlg);			delete Dlg;
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Get resolution and size for exported bitmap
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char * ResDlg_Tmpl = 
-	"1,2,,DEFAULT,PUSHBUTTON,-1,125,10,35,12\n"
-	"2,100,,,PUSHBUTTON,-2,125,25,35,12\n"
-	"100,+,,,LTEXT,1,20,10,50,9\n"
-	".,.,,,RTEXT,2,20,22,35,9\n"
-	".,.,,,EDVAL1,3,57,22,30,10\n"
-	".,.,,,LTEXT,-3,89,22,20,8\n"
-	".,.,,,RTEXT,4,20,34,35,9\n"
-	".,.,,,EDVAL1,5,57,34,30,10\n"
-	".,.,,,LTEXT,-3,89,34,20,8\n"
-	".,.,,,RTEXT,6,20,46,35,9\n"
-	".,.,,,EDVAL1,7,57,46,30,10\n"
-	".,,,LASTOBJ,LTEXT,8,89,46,20,8";
-
-bool GetBitmapRes(double *dpi, double *width, double *height, char *header)
-{
-	void *dyndata[] = {(void*)"Image properties:", (void*)"width", (void*)width,
-		(void*)"height", (void*)height, (void*)"resolution", (void*)dpi,  (void *) "dpi"};
-	DlgInfo *ResDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	bool bRet = false;
-	int res;
-	
-	if(!(ResDlg = CompileDialog(ResDlg_Tmpl, dyndata))) return false;
-	if(!(Dlg = new DlgRoot(ResDlg, 0L))) return false;
-	if(!(hDlg = CreateDlgWnd(header, 50, 50, 340, 160, Dlg, 0x0L)))return false;
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		if(res==1) {
-			Dlg->GetValue(102, width);		Dlg->GetValue(105, height);
-			Dlg->GetValue(108, dpi);
-			}
-		}while (res < 0);
-	bRet = (res == 1);
-	CloseDlgWnd(hDlg);	delete Dlg;		free(ResDlg);
-	return bRet;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Execute schemes dialog as owner drawn button
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static LineDEF Sch0_LINE = {.1, 1.0, 0x0L, 0x0L};
-static LineDEF Sch3_LINE = {.1, 1.0, 0x0L, 0x0L};
-static FillDEF Scheme0 = {FILL_NONE, 0x00ffffffL, 1.0, &Sch0_LINE, 0x00ffffffL};
-static DWORD Scheme1[] = {0x000000ffL, 0x0000ff00L, 0x00ff0000L, 0x0000ffffL,
-	0x00ff00ffL, 0x00ffff00L, 0x00000000L, 0x00ffffffL};
-static DWORD Scheme2[] = {0x00ffffffL, 0x00e0e0e0L, 0x00c0c0c0L, 0x00808080L,
-	0x000000L, 0x00808080L, 0x00c0c0c0L, 0x00e0e0e0L};
-static FillDEF Scheme3[] = {
-	{FILL_HLINES, 0x00ffffffL, 1.0, &Sch3_LINE}, {FILL_VLINES, 0x00ffffffL, 1.0, &Sch3_LINE, 0x00ffffffL},
-	{FILL_HVCROSS, 0x00ffffffL, 1.0, &Sch3_LINE}, {FILL_DLINEU, 0x00ffffffL, 1.0, &Sch3_LINE, 0x00ffffffL},
-	{FILL_DLINED, 0x00ffffffL, 1.0, &Sch3_LINE}, {FILL_DCROSS, 0x00ffffffL, 1.0, &Sch3_LINE, 0x00ffffffL},
-	{FILL_BRICKH, 0x00ffffffL, 1.0, &Sch3_LINE}, {FILL_BRICKV, 0x00ffffffL, 1.0, &Sch3_LINE, 0x00ffffffL}};
-static DlgInfo SchBase[] = {
-	{0, 0, 5, CHECKED, GROUP, 0L, 0, 0, 0, 0},
-	{5, 6, 0, 0x0L, RADIO1, (void*)"constant fill", 0, 0, 50, 8},
-	{6, 7, 0, 0x0L, RADIO1, (void*)"colors 1", 0, 10, 50, 8},
-	{7, 8, 0, 0x0L, RADIO1, (void*)"colors 2", 0, 28, 50, 8},
-	{8, 10, 0, 0x0L, RADIO1, (void*)"hatches", 0, 46, 50, 8},
-	{10, 20, 0, OWNDIALOG | TOUCHEXIT, FILLBUTTON, (void*)&Scheme0, 56, 0, 20, 8}};
-
-static int CurrScheme = 1;
-		
-void OD_scheme(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	int x = rec ? rec->left/xbase :0, y = rec ? rec->top/ybase:0;
-	static DlgRoot *Dlg = 0L;
-	POINT *mpos;
-	MouseEvent mev;
-	int i, j, k, res;
-	static DlgInfo *ComSchDlg = 0L;
-
-	switch(cmd) {
-	case OD_CREATE:
-		Dlg = 0L;
-		//set line width: internationalization may be different
-		Sch0_LINE.width = Sch3_LINE.width = defs.GetSize(SIZE_HAIRLINE);
-		ComSchDlg = (DlgInfo*)calloc(40, sizeof(DlgInfo));
-		if(ComSchDlg) {
-			memcpy(ComSchDlg, SchBase, 6 * sizeof(DlgInfo));
-			for (i = 1; i < 6; i++) {
-				ComSchDlg[i].x += x;	ComSchDlg[i].y += y;
-				}
-			for (k = 20, j = 6; k < 50; k += 10) {
-				for(i = 0; i < 8; i++) {
-					ComSchDlg[j].id = i+k;				ComSchDlg[j].next = i+k+1;
-					ComSchDlg[j].type = k < 40 ? COLBUTT : FILLBUTTON;		
-					ComSchDlg[j].flags = OWNDIALOG | TOUCHEXIT;
-					switch(k) {
-					case 20:	ComSchDlg[j].ptype = (void *)&Scheme1[i];	break;
-					case 30:	ComSchDlg[j].ptype = (void *)&Scheme2[i];	break;
-					case 40:	ComSchDlg[j].ptype = (void *)&Scheme3[i];	break;
-						}
-					ComSchDlg[j].x = 5 + i*9 + x;		ComSchDlg[j].y = 18*(k-10)/10 + y;
-					ComSchDlg[j].w = ComSchDlg[j].h = 8;
-					j++;
-					}
-				ComSchDlg[j-1].next = k+10;
-				}
-			ComSchDlg[j-1].next = 0;	ComSchDlg[j-1].flags |= LASTOBJ;
-			Dlg = new DlgRoot(ComSchDlg, 0L);
-			}
-		if(Dlg){
-			if(CurrScheme < 4) Dlg->SetCheck(5+CurrScheme, 0L, true);
-			mev.x =  mev.y = 0;			//activate RootDlg !
-			mev.Action = MOUSE_LBDOWN;
-			mev.StateFlags = 0L;
-			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);	//fake
-			}
-		break;
-	case OD_DELETE:
-		if(Dlg) delete Dlg;
-		Dlg = 0L;
-		if(ComSchDlg) free(ComSchDlg);
-		ComSchDlg = 0L;
-		break;
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		if(Dlg && o) Dlg->DoPlot(o);
-		break;
-	case OD_SELECT:
-		mpos = (POINT*)data;
-		mev.x = mpos->x;			mev.y = mpos->y;
-		mev.Action = MOUSE_LBUP;
-		mev.StateFlags = 0L;
-		if(Dlg){
-			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
-			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
-			res = Dlg->GetResult();
-			if(res >= 10) Dlg->SetCheck(4 + res/10, 0L, true);
-			if(Dlg->GetCheck(5)) CurrScheme = 0;
-			else if(Dlg->GetCheck(7)) CurrScheme = 2;
-			else if(Dlg->GetCheck(8)) CurrScheme = 3;
-			else CurrScheme = 1;
-			for(i = 0; i < 8; i++) {
-				Dlg->GetColor(i+20, &Scheme1[i]);
-				Dlg->GetColor(i+30, &Scheme2[i]);
-				}
-			}
-		break;
-	case OD_MBTRACK:
-		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
-		break;
-		}
-}
-
-FillDEF *GetSchemeFill(int *i)
-{
-	FillDEF curfill = {FILL_NONE, 0x00c0c0c0L, 1.0, 0L};
-	static FillDEF RetFill;
-
-	switch(CurrScheme) {
-	case 0:
-		memcpy(&RetFill, &Scheme0, sizeof(FillDEF));
-		break;
-	default:
-	case 1:
-		curfill.color = Scheme1[*i&0x07];
-		memcpy(&RetFill, &curfill, sizeof(FillDEF));
-		break;
-	case 2:
-		curfill.color = Scheme2[*i&0x07];
-		memcpy(&RetFill, &curfill, sizeof(FillDEF));
-		break;
-	case 3:
-		memcpy(&RetFill, &Scheme3[*i&0x07], sizeof(FillDEF));
-		break;
-		}
-	*i += 1;
-	return &RetFill;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Execute common line properties as owner drawn dialog
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static LineDEF EditLine = {0.0f, 6.0f, 0x0, 0x0}; 
-static DlgInfo LinePropBase[] = {
-	{100, 101, 0, 0x0L, RTEXT, (void*)"line width", 0, 0, 45, 8},
-	{101, 102, 0, TOUCHEXIT, INCDECVAL1, &EditLine.width, 50, 0, 32, 10},
-	{102, 103, 0, 0x0L, LTEXT, 0L, 84, 0, 20, 8},
-	{103, 104, 0, 0x0L, RTEXT, (void*)"line color", 0, 12, 45, 8},
-	{104, 105, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&EditLine.color, 50, 12, 25, 10},
-	{105, 106, 0, 0x0L, LTEXT, (void*)"pattern:", 0, 24, 25, 8},
-	{106, 107, 0, TOUCHEXIT, LINEPAT, (void *)&EditLine, 0, 34, 128, 4},
-	{107, 108, 0, 0x0L, RTEXT, (void*)"pattern length", 0, 47, 45, 8},
-	{108, 109, 0, TOUCHEXIT, INCDECVAL1, &EditLine.patlength, 50, 47, 32, 10},
-	{109, 110, 0, 0x0L, LTEXT, 0L, 84, 47, 20, 8},
-	{110, 0, 0, LASTOBJ | TOUCHEXIT, LINEBUTT, (void *)&EditLine, 0, 67, 128, 20}};
-
-void OD_linedef(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	int x = rec ? rec->left/xbase :0, y = rec ? rec->top/ybase:0;
-	int i, res;
-	POINT *mpos;
-	MouseEvent mev;
-	static DlgRoot *Dlg = 0L;
-	static DlgInfo *LinePropDlg= 0L;
-
-	switch(cmd) {
-	case OD_CREATE:
-		Dlg = 0L;
-		LinePropDlg = (DlgInfo*)calloc(40, sizeof(DlgInfo));
-		if(LinePropDlg) {
-			memcpy(LinePropDlg, LinePropBase, 11 * sizeof(DlgInfo));
-			LinePropDlg[2].ptype = LinePropDlg[9].ptype =
-				(void *)Units[defs.cUnits].display;
-			for (i = 0; i < 11; i++) {
-				LinePropDlg[i].x += x;	LinePropDlg[i].y += y;
-				}
-			Dlg = new DlgRoot(LinePropDlg, 0L);
-			}
-		if(Dlg){
-			Dlg->SetColor(104, EditLine.color);
-			mev.x =  mev.y = 0;			//activate RootDlg !
-			mev.Action = MOUSE_LBDOWN;
-			mev.StateFlags = 0L;
-			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);	//fake
-			}
-		break;
-	case OD_DELETE:
-		if(Dlg) delete Dlg;
-		Dlg = 0L;
-		if(LinePropDlg) free(LinePropDlg);
-		LinePropDlg = 0L;
-		break;
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		if(Dlg && o) Dlg->DoPlot(o);
-		break;
-	case OD_SELECT:
-		mpos = (POINT*)data;
-		mev.x = mpos->x;			mev.y = mpos->y;
-		mev.Action = MOUSE_LBUP;
-		mev.StateFlags = 0L;
-		if(Dlg){
-			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
-			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
-			res = Dlg->GetResult();
-			switch (res) {
-			case 101:						//line width changed
-			case 108:						//pattern length changed
-			case 110:						//preview button
-				Dlg->GetValue(101, &EditLine.width);
-				Dlg->GetValue(108, &EditLine.patlength);
-			case 104:						//color button
-				Dlg->GetColor(104, &EditLine.color);
-			case 106:						//line pattern
-				Dlg->DoPlot(0);
-				break;
-				}
-			}
-		break;
-	case OD_MBTRACK:
-		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
-		break;
-	case OD_SETLINE:
-		if(data) {
-			memcpy(&EditLine, data, sizeof(LineDEF));
-			if(Dlg && LinePropDlg) Dlg->DoPlot(0);
-			}
-		break;
-	case OD_GETLINE:
-		if(Dlg) {
-				Dlg->GetValue(101, &EditLine.width);
-				Dlg->GetValue(108, &EditLine.patlength);
-			}
-		if(data) memcpy(data, &EditLine, sizeof(LineDEF));
-		break;
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Execute common FILL properties as owner drawn button
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static LineDEF ODLine = {0.0f, 6.0f, 0x0, 0x0}; 
-static LineDEF ODFillLine = {0.0f, 6.0f, 0x0, 0x0}; 
-static FillDEF ODFill = {FILL_NONE, 0x00ffffffL, 1.0f, &ODFillLine, 0x00ffffffL}; 
-static DlgInfo FillPropBase[] = {
-	{100, 101, 0, 0x0L, RTEXT, (void*)"outline width", 0, 0, 40, 8},
-	{101, 102, 0, 0x0L, EDVAL1, &ODLine.width, 42, 0, 25, 10},
-	{102, 103, 0, 0x0L, LTEXT, 0L, 69, 0, 20, 8},
-	{103, 104, 0, 0x0L, RTEXT, (void*)"outline color", 0, 12, 40, 8},
-	{104, 105, 0, OWNDIALOG, COLBUTT, (void *)&ODLine.color, 42, 12, 25, 10},
-	{105, 106, 0, 0x0L, RTEXT,(void*)"fill color" , 0, 24, 40, 8},
-	{106, 107, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&ODFill.color, 42, 24, 25, 10},
-	{107, 108, 0, 0x0L, RTEXT, (void*)"pattern", 0, 36, 40, 8},
-	{108, 0, 0, LASTOBJ | TOUCHEXIT | OWNDIALOG, FILLBUTTON, (void*)&ODFill, 42, 36, 25, 10}};
-
-void OD_filldef(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	int x = rec ? rec->left/xbase :0, y = rec ? rec->top/ybase:0;
-	int i, res;
-	POINT *mpos;
-	MouseEvent mev;
-	static DlgRoot *Dlg = 0L;
-	static DlgInfo *FillPropDlg= 0L;
-
-	switch(cmd) {
-	case OD_CREATE:
-		Dlg = 0L;
-		FillPropDlg = (DlgInfo*)calloc(9, sizeof(DlgInfo));
-		if(FillPropDlg) {
-			memcpy(FillPropDlg, FillPropBase, 9 * sizeof(DlgInfo));
-			FillPropDlg[2].ptype = (void *) Units[defs.cUnits].display;
-			for (i = 0; i < 9; i++) {
-				FillPropDlg[i].x += x;	FillPropDlg[i].y += y;
-				}
-			Dlg = new DlgRoot(FillPropDlg, 0L);
-			}
-		if(Dlg){
-			Dlg->SetColor(104, ODLine.color);
-			Dlg->SetColor(106, ODFill.color);
-			mev.x =  mev.y = 0;			//activate RootDlg !
-			mev.Action = MOUSE_LBDOWN;
-			mev.StateFlags = 0L;
-			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);	//fake
-			}
-		break;
-	case OD_DELETE:
-		if(Dlg) delete Dlg;
-		Dlg = 0L;
-		if(FillPropDlg) free(FillPropDlg);
-		FillPropDlg = 0L;
-		break;
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		if(Dlg && o) Dlg->DoPlot(o);
-		break;
-	case OD_SELECT:
-		mpos = (POINT*)data;
-		mev.x = mpos->x;			mev.y = mpos->y;
-		mev.Action = MOUSE_LBUP;
-		mev.StateFlags = 0L;
-		if(Dlg){
-			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
-			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
-			res = Dlg->GetResult();
-			switch (res) {
-			case 106:					//fill color changed
-				Dlg->GetColor(106, &ODFill.color);
-				Dlg->DoPlot(NULL);
-				break;
-			case 108:					//copy color from pattern dialog
-				Dlg->SetColor(106, ODFill.color);
-				break;
-				}
-			}
-		break;
-	case OD_MBTRACK:
-		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
-		break;
-	case OD_SETLINE:
-		if(data) {
-			memcpy(&ODLine, data, sizeof(LineDEF));
-			if(Dlg && FillPropDlg) Dlg->DoPlot(0);
-			}
-		break;
-	case OD_GETLINE:
-		if(Dlg) {
-			Dlg->GetValue(101, &ODLine.width);
-			Dlg->GetColor(104, &ODLine.color);
-			}
-		if(data) memcpy(data, &ODLine, sizeof(LineDEF));
-		break;
-	case OD_SETFILL:
-		if(data) {
-			memcpy(&ODFill, data, sizeof(FillDEF));
-			if(ODFill.hatch) memcpy(&ODFillLine, ((FillDEF*)data)->hatch, sizeof(LineDEF));
-			ODFill.hatch = &ODFillLine;
-			if(Dlg) Dlg->SetColor(106, ODFill.color);
-			}
-		break;
-	case OD_GETFILL:
-		Dlg->GetColor(106, &ODFill.color);
-		if(data) memcpy(data, &ODFill, sizeof(FillDEF));
-		break;
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Execute paper size properiteis as owner drawn button
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-typedef struct{
-	char *name;
-	double mwidth;		double mheight;
-	double iwidth;		double iheight;
-}paper;
-
-static paper p_formats[] = {
-	{"A0", 841, 1189, 33.11, 46.81},	{"A1", 594, 841, 23.39, 33.11},
-	{"A2", 420, 594, 16.54, 23.39},		{"A3", 297, 420, 11.69, 16.54},
-	{"A4", 210, 297, 8.27, 11.69},		{"A5", 148, 210, 5.83, 8.27},
-	{"A6", 105, 148, 4.13, 5.83},		{"A7", 74, 105, 2.91, 4.13},
-	{"B0", 1030, 1456, 40.55, 57.32},	{"B1", 728, 1030, 28.66, 40.55},
-	{"B2", 515, 728, 20.28, 28.66},		{"B3", 364, 515, 14.33, 20.28},
-	{"B4", 257, 364, 10.12, 14.33},		{"B5", 182, 257, 7.17, 10.12},
-	{"B6", 128, 182, 5.04, 7.17},		{"B7", 91, 128, 3.58, 5.04},
-	{"Executive", 191, 254, 7.52, 10},	{"Folio", 210, 330, 8.27, 12.99},
-	{"Ledger", 432, 279, 17.01, 10.98},	{"Legal", 216, 356, 8.5, 14.02},
-	{"Letter", 216, 279, 8.5, 10.98},
-	{"Custom", 210, 297, 8.27, 11.69}};
-static int cpformats = sizeof(p_formats)/sizeof(paper);
-static double cu_width = 123.0, cu_height = 234.0;
-static int pg_sel = 4;
-
-static DlgInfo PaperDlg[] = {
-	{100, 110, 0, 0x0L, NONE, (void*)0L, 0, 0, 0, 0},
-	{110, 120, 0, TOUCHEXIT, LISTBOX1, (void*)0L, 0, 15, 100, 70},
-	{120, 130, 0, NOEDIT, EDTEXT, (void*)"n.a.", 0, 0, 100, 10},
-	{130, 0, 132, CHECKED, GROUP, 0, 0, 0, 0},
-	{132, 133, 0, 0x0L, EDVAL1, (void*)&cu_width, 27, 0, 25, 10},
-	{133, 134, 0, 0x0L, LTEXT, (void*)"x", 52, 0, 5, 8},
-	{134, 135, 0, 0x0L, EDVAL1, (void*)&cu_height, 58, 0, 25, 10},
-	{135, 0, 0, LASTOBJ, LTEXT, (void*)0L, 83, 0, 14, 8}};
-
-void OD_paperdef(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	int x = rec ? rec->left/xbase :0, y = rec ? rec->top/ybase:0;
-	int i, res;
-	POINT *mpos;
-	MouseEvent mev;
-	static DlgRoot *Dlg = 0L;
-	static DlgInfo *PaperPropDlg= 0L;
-	char **dispsize = 0L;
-	double dtmp;
-
-	switch(cmd) {
-	case OD_CREATE:
-		GetPaper(&cu_width, &cu_height);
-		Dlg = 0L;
-		if((PaperPropDlg = (DlgInfo*)calloc(8, sizeof(DlgInfo))) && 
-			(dispsize = (char**)calloc(cpformats+1, sizeof(char*)))) {
-			memcpy(PaperPropDlg, PaperDlg, 8 * sizeof(DlgInfo));
-			for (i = 0; i < cpformats; i++) {
-				if(i < cpformats -1) {
-					switch(defs.cUnits) {
-					case 1:
-#ifdef USE_WIN_SECURE
-						sprintf_s(TmpTxt, TMP_TXT_SIZE, " %s  (%.1lf x %.1lf cm)", p_formats[i].name, 
-							p_formats[i].mwidth/10.0, p_formats[i].mheight/10.0);
-#else
-						sprintf(TmpTxt, " %s  (%.1lf x %.1lf cm)", p_formats[i].name, 
-							p_formats[i].mwidth/10.0, p_formats[i].mheight/10.0);
-#endif
-						break;
-					case 2:
-#ifdef USE_WIN_SECURE
-						sprintf_s(TmpTxt, TMP_TXT_SIZE, " %s  (%.2lf x %.2lf inch)", p_formats[i].name, 
-							p_formats[i].iwidth, p_formats[i].iheight);
-#else
-						sprintf(TmpTxt, " %s  (%.2lf x %.2lf inch)", p_formats[i].name, 
-							p_formats[i].iwidth, p_formats[i].iheight);
-#endif
-						break;
-					default:
-#ifdef USE_WIN_SECURE
-						sprintf_s(TmpTxt, TMP_TXT_SIZE, " %s  (%.0lf x %.0lf mm)", p_formats[i].name, 
-							p_formats[i].mwidth, p_formats[i].mheight);
-#else
-						sprintf(TmpTxt, " %s  (%.0lf x %.0lf mm)", p_formats[i].name, 
-							p_formats[i].mwidth, p_formats[i].mheight);
-#endif
-						break;
-						}
-					dispsize[i] = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
-					}
-				else if(dispsize[i] = (char*)malloc(15*sizeof(char)))
-					rlp_strcpy(dispsize[i], 15, " Custom");
-				}
-			PaperPropDlg[1].ptype = (void*)dispsize;
-			PaperPropDlg[7].ptype = (void*)Units[defs.cUnits].display;
-			if(pg_sel <(cpformats -1))PaperPropDlg[3].flags |= HIDDEN;
-			for (i = 0; i < 8; i++) {
-				PaperPropDlg[i].x += x;	PaperPropDlg[i].y += y;
-				}
-			if(Dlg = new DlgRoot(PaperPropDlg, 0L)){
-				Dlg->Activate(120, false);
-				if(Dlg->ItemCmd(110, CMD_FINDTEXT, (void*)dispsize[pg_sel])){
-					if(pg_sel < (cpformats-1)) Dlg->SetText(120, dispsize[pg_sel]);
-					else Dlg->SetText(120, dispsize[pg_sel]+1);
-					dtmp = ((double)pg_sel)/((double) cpformats +10.0);
-					Dlg->ItemCmd(110, CMD_SETSCROLL, (void*)&dtmp);
-					}
-				}
-			}
-		break;
-	case OD_DELETE:
-		if(Dlg) delete Dlg;
-		Dlg = 0L;
-		if(PaperPropDlg) free(PaperPropDlg);
-		PaperPropDlg = 0L;
-		if(dispsize) {
-			for (i = 0; i < 20; i++) if(dispsize[i])free(dispsize[i]);
-			free(dispsize);
-			dispsize = 0L;
-			}
-		break;
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		if(Dlg && o) Dlg->DoPlot(o);
-		break;
-	case OD_SELECT:
-		mpos = (POINT*)data;
-		mev.x = mpos->x;			mev.y = mpos->y;
-		mev.Action = MOUSE_LBUP;
-		mev.StateFlags = 0L;
-		if(Dlg){
-			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
-			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
-			res = Dlg->GetResult();
-			if(res == 110 && Dlg->GetText(110, TmpTxt, TMP_TXT_SIZE)){
-				if(Dlg->GetInt(110, &i)) {
-					if(i == cpformats-1){
-						Dlg->ShowItem(130, true);
-						Dlg->SetText(120, TmpTxt+1);
-						}
-					else {
-						Dlg->ShowItem(130, false);
-						Dlg->SetText(120, TmpTxt);
-						}
-					}
-				Dlg->DoPlot(o);
-				}
-			}
-		break;
-	case OD_MBTRACK:
-		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
-		break;
-	case OD_ACCEPT:
-		if(Dlg) {
-			if(Dlg->GetInt(110, &pg_sel) && pg_sel == (i=cpformats-1)){
-				Dlg->GetValue(132, &cu_width);
-				Dlg->GetValue(134, &cu_height);
-				switch(defs.cUnits){
-				case 1:
-					p_formats[i].mwidth = cu_width*10.0;
-					p_formats[i].mheight = cu_height*10.0;
-					p_formats[i].iwidth = cu_width/2.54;
-					p_formats[i].iheight = cu_height/2.54;
-					break;
-				case 2:
-					p_formats[i].mwidth = cu_width*25.4;
-					p_formats[i].mheight = cu_height*25.4;
-					p_formats[i].iwidth = cu_width;
-					p_formats[i].iheight = cu_height;
-					break;
-				default:
-					p_formats[i].mwidth = cu_width;
-					p_formats[i].mheight = cu_height;
-					p_formats[i].iwidth = cu_width/25.4;
-					p_formats[i].iheight = cu_height/25.4;
-					break;
-					}
-				}
-			}
-		break;
-		}
-}
-
-//Find a suitable paper size with width w and height h
-void FindPaper(double w, double h, double tol)
-{
-	int i;
-	double lw, hw, lh, hh;
-
-	lw = w *(1.0-tol);		hw = w *(1.0+tol);
-	lh = h *(1.0-tol);		hh = h *(1.0+tol);
-	for(i = 0; i < cpformats; i++) {
-		switch(defs.cUnits) {
-		case 1:					//units are cm
-			if(p_formats[i].mwidth >= lw*10.0 && p_formats[i].mwidth <= hw*10.0 &&
-				p_formats[i].mheight >= lh*10.0 && p_formats[i].mheight <= hh*10.0){
-					pg_sel = i;
-					return;
-				}
-			break;
-		case 2:					//units are inch
-			if(p_formats[i].iwidth >= lw && p_formats[i].iwidth <= hw &&
-				p_formats[i].iheight >= lh && p_formats[i].iheight <= hh){
-					pg_sel = i;
-					return;
-				}
-			break;
-		default:				//units are mm
-			if(p_formats[i].mwidth >= lw && p_formats[i].mwidth <= hw &&
-				p_formats[i].mheight >= lh && p_formats[i].mheight <= hh){
-					pg_sel = i;
-					return;
-				}
-			break;
-			}
-		}
-	//The paper format is non standard
-	pg_sel = i = cpformats-1;
-	switch(defs.cUnits){
-	case 1:
-		p_formats[i].mwidth = w*10.0;		p_formats[i].mheight = h*10.0;
-		p_formats[i].iwidth = w/2.54;		p_formats[i].iheight = h/2.54;
-		break;
-	case 2:
-		p_formats[i].mwidth = w*25.4;		p_formats[i].mheight = h*25.4;
-		p_formats[i].iwidth = w;			p_formats[i].iheight = h;
-		break;
-	default:
-		p_formats[i].mwidth = w;			p_formats[i].mheight = h;
-		p_formats[i].iwidth = w/25.4;		p_formats[i].iheight = h/25.4;
-		break;
-		}
-}
-
-//Get (default) paper size
-bool GetPaper(double *w, double *h)
-{
-	switch(defs.cUnits) {
-		case 1:					//units are cm
-			*w = p_formats[pg_sel].mwidth/10.0;
-			*h = p_formats[pg_sel].mheight/10.0;
-			break;
-		case 2:					//units are inch
-			*w = p_formats[pg_sel].iwidth;
-			*h = p_formats[pg_sel].iheight;
-			break;
-		default:				//units are mm
-			*w = p_formats[pg_sel].mwidth;
-			*h = p_formats[pg_sel].mheight;
-			break;
-		}
-	return true;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Select axis for plot as owner drawn button
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static DlgInfo PlotsDlg[] = {
-	{100, 110, 0, 0x0L, NONE, (void*)0L, 0, 0, 0, 0},
-	{110, 150, 0, TOUCHEXIT, LISTBOX1, (void*)0L, 20, 50, 100, 70},
-	{150, 0, 0, LASTOBJ, LTEXT, (void*)"Apply this axis to plot:", 20, 35, 50, 9}};
-static int axisplot_sel = 0;
-
-void OD_axisplot(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id)
-{
-	static DlgInfo *PlotsPropDlg= 0L;
-	static DlgRoot *Dlg = 0L;
-	int i, res;
-	POINT *mpos;
-	MouseEvent mev;
-	static char **names = 0L;
-
-	switch(cmd) {
-	case OD_CREATE:
-		Dlg = 0L;
-		if(PlotsPropDlg = (DlgInfo*)calloc(3, sizeof(DlgInfo))){
-			memcpy(PlotsPropDlg, PlotsDlg, 3 * sizeof(DlgInfo));
-			PlotsPropDlg[1].ptype = (void*)names;
-			Dlg = new DlgRoot(PlotsPropDlg, 0L);
-			}
-		axisplot_sel = 0;
-		break;
-	case OD_DELETE:
-		if(Dlg) delete Dlg;
-		Dlg = 0L;
-		if(PlotsPropDlg) free(PlotsPropDlg);
-		PlotsPropDlg = 0L;
-		break;
-	case OD_DRAWNORMAL:
-	case OD_DRAWSELECTED:
-		if(Dlg && o) Dlg->DoPlot(o);
-		break;
-	case OD_SELECT:
-		mpos = (POINT*)data;
-		mev.x = mpos->x;			mev.y = mpos->y;
-		mev.Action = MOUSE_LBUP;
-		mev.StateFlags = 0L;
-		if(Dlg){
-			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
-			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
-			res = Dlg->GetResult();
-			if(res == 110 && Dlg->GetText(110, TmpTxt, TMP_TXT_SIZE)){
-				if(Dlg->GetInt(110, &i)) {
-					//get selection
-					}
-				Dlg->DoPlot(o);
-				}
-			Dlg->GetInt(110, &axisplot_sel);
-			}
-		break;
-	case OD_MBTRACK:
-		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
-		break;
-	case OD_ACCEPT:
-		if(data) names = (char**)data;
-		else if(Dlg) Dlg->GetInt(110, &axisplot_sel);
-		if(o) *((int*)o) = axisplot_sel;
-		}
-}
+//TheDialog.cpp, Copyright (c) 2001-2008 R.Lackner
+//Operating system independent code for dialog boxes
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+#include "rlplot.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "TheDialog.h"
+
+extern tag_Units Units[];
+extern char TmpTxt[];
+extern Default defs;
+extern GraphObj *CurrGO;
+extern EditText *CurrText;		//current EditText object
+extern RECT rTxtCur;			//text cursor position and direction
+extern UndoObj Undo;
+
+char *WWWbrowser = 0L;
+char *LoadFile = 0L;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// internal declarations 
+static int xbase = 2;
+static int ybase = 2;
+int dlgtxtheight = 10;
+static unsigned long DlgBGcolor = 0x00e0e0e0L;
+static unsigned long DlgBGhigh = 0x00e8e8e8L;
+TextDEF DlgText = {0x00000000L, 0x00ffffffL, 4.0, 0.0, 0.0, 0, 
+	TXA_HLEFT | TXA_VTOP, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, 0L}; 
+
+//prototypes: WinSpec.cpp
+void *CreateDlgWnd(char *title, int x, int y, int width, int height, tag_DlgObj *d, DWORD flags);
+
+//The dialog object which just has the input focus
+Dialog *DialogFocus = 0L;
+Dialog *DialogDefault = 0L;
+Dialog *DialogTabStop = 0L;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Base classes to dialog items
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+DlgRoot::DlgRoot(DlgInfo *tmpl, DataObj *d)
+{
+	int i;
+	RECT rc;
+
+	dlg = 0L;			flags = 0L;		tabstops = 0L;
+	DlgText.iSize = dlgtxtheight;		DlgText.ColBg = DlgBGcolor;
+	DlgText.fSize = defs.GetSize(SIZE_TEXT);
+	type = NONE;		Id = -2;		cContinue = 0;
+	bActive = bRedraw = false;			c_go = CurrGO;
+	CurrDisp = 0L;		oldFocus = DialogFocus;		DialogFocus = 0L;
+	oldDefault = DialogDefault;			oldTabStop = DialogTabStop;
+	data = d;			res_put = res_get = 0;		hDialog = 0L;
+	if(ParentOut = Undo.cdisp) ParentOut->MouseCursor(MC_WAIT, false);
+	mrk_item = 0L;		//if an item has a mark its this one
+	bModal = true;		//assume its a modal dialog
+	if(tmpl) {
+		//count number of items first, then allocate memory
+		for(cDlgs=1;!(tmpl[cDlgs-1].flags & LASTOBJ); cDlgs++);
+		dlg = (DlgTmpl **)calloc(cDlgs+1, sizeof(DlgTmpl*));
+		tabstops =(Dialog**)calloc(cDlgs, sizeof(Dialog*));
+		if(dlg) for (i = 0; i < cDlgs; i++) {
+			dlg[i] = (DlgTmpl *)malloc(sizeof(DlgTmpl));
+			if(dlg[i]) {
+				dlg[i]->id = tmpl[i].id;			dlg[i]->next = tmpl[i].next;
+				dlg[i]->first = tmpl[i].first;			dlg[i]->flags = tmpl[i].flags;
+				rc.left = tmpl[i].x * xbase;			rc.right = rc.left + tmpl[i].w * xbase;
+				rc.top = tmpl[i].y * ybase;			rc.bottom = rc.top + tmpl[i].h * ybase;
+				//an item appearing in the following list should have a corresponding
+				//  entry in the list of ~DlgRoot()
+				switch(tmpl[i].type) {
+				case PUSHBUTTON:
+					dlg[i]->dialog = new PushButton(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
+					break;
+				case TEXTBOX:
+					dlg[i]->dialog = new TextBox(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
+					break;
+				case ARROWBUTT:
+					dlg[i]->dialog = new ArrowButton(this, &tmpl[i],rc,(int*)tmpl[i].ptype);
+					break;
+				case COLBUTT:
+					dlg[i]->dialog = new ColorButton(this, &tmpl[i],rc, (DWORD *)tmpl[i].ptype);
+					break;
+				case FILLBUTTON:
+					dlg[i]->dialog = new FillButton(this, &tmpl[i],rc, (FillDEF *)tmpl[i].ptype);
+					break;
+				case SHADE3D:
+					dlg[i]->dialog = new Shade3D(this, &tmpl[i],rc, (FillDEF *)tmpl[i].ptype);
+					break;
+				case LINEBUTT:
+					dlg[i]->dialog = new LineButton(this, &tmpl[i],rc, (LineDEF *)tmpl[i].ptype);
+					break;
+				case SYMBUTT:
+					dlg[i]->dialog = new SymButton(this, &tmpl[i],rc, (Symbol **)tmpl[i].ptype);
+					break;
+				case FILLRADIO:
+					dlg[i]->dialog = new FillRadioButt(this, &tmpl[i],rc, *((unsigned int*)tmpl[i].ptype));
+					break;
+				case SYMRADIO:
+					dlg[i]->dialog = new SymRadioButt(this, &tmpl[i],rc, (int*)tmpl[i].ptype);
+					break;
+				case CHECKBOX:
+					dlg[i]->dialog = new CheckBox(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
+					break;
+				case CHECKPIN:
+					dlg[i]->dialog = new CheckPin(this, &tmpl[i],rc);
+					break;
+				case TRASH:
+					dlg[i]->dialog = new Trash(this, &tmpl[i],rc);
+					break;
+				case CONFIG:
+					dlg[i]->dialog = new Config(this, &tmpl[i],rc);
+					break;
+				case RADIO0:	case RADIO1:	case RADIO2:
+					dlg[i]->dialog = new RadioButton(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
+					break;
+				case LTEXT:		case RTEXT:		case CTEXT:
+					dlg[i]->dialog = new Text(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
+					break;
+				case EDTEXT:
+					dlg[i]->dialog = new InputText(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
+					break;
+				case RANGEINPUT:
+					dlg[i]->dialog = new RangeInput(this, &tmpl[i],rc,(char*)tmpl[i].ptype, data);
+					break;
+				case EDVAL1:
+					dlg[i]->dialog = new InputValue(this, &tmpl[i],rc,(double*)tmpl[i].ptype);
+					break;
+				case INCDECVAL1:
+					dlg[i]->dialog = new IncDecValue(this, &tmpl[i],rc,(double*)tmpl[i].ptype);
+					break;
+				case TXTHSP:
+					dlg[i]->dialog = new TxtHSP(this, &tmpl[i],rc, (int*)tmpl[i].ptype);
+					break;
+				case VSCROLL:
+					dlg[i]->dialog = new ScrollBar(this, &tmpl[i],rc, true);
+					break;
+				case HSCROLL:
+					dlg[i]->dialog = new ScrollBar(this, &tmpl[i],rc, false);
+					break;
+				case ICON:
+					dlg[i]->dialog = new Icon(this, &tmpl[i],rc, (int*)tmpl[i].ptype);
+					break;
+				case GROUP:
+					dlg[i]->dialog = new Group(this, &tmpl[i], rc);
+					break;
+				case GROUPBOX:
+					dlg[i]->dialog = new GroupBox(this, &tmpl[i],rc, (char*)tmpl[i].ptype);
+					break;
+				case SHEET:
+					dlg[i]->dialog = new TabSheet(this, &tmpl[i],rc, (TabSHEET *)tmpl[i].ptype, data);
+					break;
+				case ODBUTTON:
+					dlg[i]->dialog = new ODbutton(this, &tmpl[i],rc, tmpl[i].ptype);
+					break;
+				case LISTBOX1:
+					dlg[i]->dialog = new Listbox(this, &tmpl[i],rc, (char**)tmpl[i].ptype);
+					break;
+				case TREEVIEW:
+					dlg[i]->dialog = new Treeview(this, &tmpl[i],rc, (GraphObj*)tmpl[i].ptype);
+					break;
+				case LINEPAT:
+					dlg[i]->dialog = new LinePat(this, &tmpl[i],rc, (LineDEF *)tmpl[i].ptype);
+					break;
+				default:
+					dlg[i]->dialog = NULL;
+					}
+				}
+			else break;
+			}
+		}
+}
+
+DlgRoot::~DlgRoot()
+{
+	int i;
+
+	if(data) data->Command(CMD_ETRACC, 0L, 0L);
+	defs.Idle(CMD_FLUSH);
+	HideTextCursor();
+	if(dlg){
+		for (i = 0; dlg[i] && i < cDlgs; i++) {
+			//we need to delete each object using a cast on its proper type
+			//to call the proper destructor
+			if(dlg[i]->dialog){
+				switch(dlg[i]->dialog->type) {
+				case PUSHBUTTON:	delete((PushButton*)dlg[i]->dialog);		break;
+				case TEXTBOX:		delete((TextBox*)dlg[i]->dialog);			break;
+				case ARROWBUTT:		delete((ArrowButton*)dlg[i]->dialog);		break;
+				case COLBUTT:		delete((ColorButton*)dlg[i]->dialog);		break;
+				case FILLBUTTON:	delete((FillButton*)dlg[i]->dialog);		break;
+				case SHADE3D:		delete((Shade3D*)dlg[i]->dialog);			break;
+				case LINEBUTT:		delete((LineButton*)dlg[i]->dialog);		break;
+				case SYMBUTT:		delete((SymButton*)dlg[i]->dialog);			break;
+				case FILLRADIO:		delete((FillRadioButt*)dlg[i]->dialog);		break;
+				case SYMRADIO:		delete((SymRadioButt*)dlg[i]->dialog);		break;
+				case CHECKBOX:		delete((CheckBox*)dlg[i]->dialog);			break;
+				case CHECKPIN:		delete((CheckPin*)dlg[i]->dialog);			break;
+				case TRASH:			delete((Trash*)dlg[i]->dialog);				break;
+				case CONFIG:		delete((Config*)dlg[i]->dialog);			break;
+				case RADIO0:	case RADIO1:
+				case RADIO2:		delete((RadioButton*)dlg[i]->dialog);		break;
+				case LTEXT:	case RTEXT:	
+				case CTEXT:	delete((Text*)dlg[i]->dialog);						break;
+				case EDTEXT:		delete((InputText*)dlg[i]->dialog);			break;
+				case RANGEINPUT:	delete((RangeInput*)dlg[i]->dialog);			break;
+				case EDVAL1:		delete((InputValue*)dlg[i]->dialog);		break;
+				case INCDECVAL1:	delete((IncDecValue*)dlg[i]->dialog);		break;
+				case TXTHSP:		delete((TxtHSP*)dlg[i]->dialog);			break;
+				case HSCROLL:
+				case VSCROLL:		delete((ScrollBar*)dlg[i]->dialog);			break;
+				case ICON:			delete((Icon*)dlg[i]->dialog);				break;
+				case GROUP:			delete((Group*)dlg[i]->dialog);				break;
+				case GROUPBOX:		delete((GroupBox*)dlg[i]->dialog);			break;
+				case SHEET:			delete((TabSheet*)dlg[i]->dialog);			break;
+				case ODBUTTON:		delete((ODbutton*)dlg[i]->dialog);			break;
+				case LISTBOX1:		delete((Listbox*)dlg[i]->dialog);			break;
+				case TREEVIEW:		delete((Treeview*)dlg[i]->dialog);			break;
+				case LINEPAT:		delete((LinePat*)dlg[i]->dialog);			break;
+				default:
+				//DEBUG: we should issue a message that an unknown item is
+				//   deleted: this might result in a memory leak;
+					InfoBox("unknown dialog object found\nin \"DlgRoot::~DlgRoot()\"");
+					delete(dlg[i]->dialog);
+					break;
+					}
+				}
+			free(dlg[i]);
+			}
+		free(dlg);					dlg=0L;
+		}
+	if(tabstops) free(tabstops);	tabstops = 0L;
+	DialogFocus = oldFocus;			DialogDefault = oldDefault;
+	DialogTabStop = oldTabStop;		CurrGO = c_go;
+	if(Undo.cdisp)Undo.cdisp->MouseCursor(MC_ARROW, false);
+}
+
+bool
+DlgRoot::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	Dialog *d;
+	int i, j;
+
+	switch (cmd) {
+	case CMD_UNDO:
+		if(CurrDisp) {
+			Undo.Restore(false, CurrDisp);
+			DoPlot(CurrDisp);
+			}
+		return true;
+	case CMD_REDRAW:
+		if(CurrDisp) {
+			CurrDisp->Erase(DlgBGcolor);		DoPlot(CurrDisp);
+			defs.Idle(CMD_UPDATE);
+			}
+		return true;
+	case CMD_MOUSE_EVENT:
+		if(DialogFocus && DialogFocus->type == TEXTBOX && DialogFocus->Command(cmd, tmpl, o)) return true; 
+		mev = (MouseEvent *) tmpl;
+		switch(mev->Action) {
+		case MOUSE_LBDOWN:
+			bActive = true;
+		case MOUSE_MOVE:
+			//track mouse and display controls accordingly
+			if(!(mev->StateFlags & 1)) break;
+			if(bActive)ForEach(CMD_MOUSE_EVENT, 0, o);
+			break;
+		case MOUSE_LBDOUBLECLICK:
+			ForEach(CMD_MOUSE_EVENT, 0, o);
+			bActive = false;				//skip next event (LB up);
+			break;
+		case MOUSE_LBUP:
+			if(bActive)ForEach(CMD_LBUP, 0, o);
+			break;
+			}
+		defs.Idle(CMD_UPDATE);
+		return true;
+	case CMD_ENDDIALOG:
+		d = (Dialog *)tmpl;
+		if(d) {
+			res_q[res_put++] = d->Id;		// end dialog by object
+			cContinue = 0;
+			}
+		else if(cContinue >0) {				// no end upon killing the focus
+			cContinue--;
+			return true;
+			}
+		else {
+			res_q[res_put++] = 0;			// end dialog with closebox or loose focus
+			bRedraw = true;
+			}
+		res_put &= 0xff;
+		return true;
+	case CMD_CONTINUE:
+		cContinue++;
+		return true;
+	case CMD_TABDLG:
+		if(tabstops) for (i = 0; i < cDlgs; i++) 
+			if(!tabstops[i] || tabstops[i] == (Dialog*)tmpl) {
+				tabstops[i] = (Dialog*)tmpl;
+				return true;
+				}
+		return false;
+	case CMD_NOTABDLG:
+		if(tabstops) for (i = j = 0; i < cDlgs; i++) {
+			if(tabstops[i] == (Dialog*)tmpl) tabstops[i] = 0L;
+			if(tabstops[i]) tabstops[j++] = tabstops[i];
+			}
+		return true;
+	case CMD_TAB:
+		HideTextCursor();
+		if(tabstops && DialogTabStop) {
+			for(i = 0; tabstops[i] && tabstops[i] != DialogTabStop && i < cDlgs; i++);
+			if((tabstops[i]) || (tabstops[i]->flags & HIDDEN)) i++;
+			if(!tabstops[i]) i = 0;
+			switch(tabstops[i]->type) {
+			case PUSHBUTTON:
+				d = DialogDefault;
+				DialogTabStop = DialogDefault = tabstops[i];
+				if(d) d->DoPlot(o);
+				DialogDefault->DoPlot(o);
+				break;
+			case EDTEXT:			case EDVAL1:	case RANGEINPUT:
+			case INCDECVAL1:
+				DialogTabStop = DialogFocus = tabstops[i];
+				if((InputText*)DialogFocus->bActive)
+					((InputText*)DialogFocus)->Activate(DialogFocus->Id, true);
+				else Command(cmd, tmpl, o);
+				break;
+				}
+			}
+		return true;
+	case CMD_SHTAB:
+		HideTextCursor();
+		if(tabstops && DialogTabStop) {
+			for(j = 0; tabstops[j]; j++);
+			for(i = j-1; tabstops[i] != DialogTabStop && i; i--);
+			i = i >0 ? i-1 : j-1;
+			switch(tabstops[i]->type) {
+			case PUSHBUTTON:
+				d = DialogDefault;
+				DialogTabStop = DialogDefault = tabstops[i];
+				d->DoPlot(o);
+				DialogDefault->DoPlot(o);
+				break;
+			case EDTEXT:			case EDVAL1:			case INCDECVAL1:
+			case RANGEINPUT:
+				DialogTabStop = DialogFocus = tabstops[i];
+				((InputText*)DialogFocus)->Activate(DialogFocus->Id, true);
+				break;
+				}
+			}
+		return true;
+	case CMD_CURRUP:	case CMD_CURRDOWN:
+		if(DialogFocus && DialogFocus->type == TEXTBOX)
+			return DialogFocus->Command(cmd, tmpl, o);
+        else return CurUpDown(cmd);
+	case CMD_CURRLEFT:	case CMD_CURRIGHT:	case CMD_DELETE:
+	case CMD_POS_FIRST:	case CMD_POS_LAST:	case CMD_SHIFTLEFT:
+	case CMD_SHIFTRIGHT:	case CMD_COPY:	case CMD_PASTE:
+		Undo.SetDisp(CurrDisp);
+		bActive = true;
+		if(DialogFocus)return DialogFocus->Command(cmd, tmpl, CurrDisp);
+        else return false;
+	case CMD_ADDCHAR:
+		if(!tmpl) return false;
+		bActive = true;
+		if(*((int*)tmpl) == 27) {						//Esc
+			HideCopyMark();
+			for (i = 0; dlg[i] && i < cDlgs; i++) 
+				if(dlg[i]->dialog) dlg[i]->dialog->Command(cmd, tmpl, o);
+			return Command(CMD_REDRAW, 0L, 0L);
+			}
+		if(DialogDefault && *((int*)tmpl) == 0x0d){		//return pressed
+			HideTextCursor();
+			return DialogDefault->Command(cmd, tmpl, o);
+			}
+		if(DialogFocus)return DialogFocus->Command(cmd, tmpl, o);
+        else return false;
+	case CMD_UNLOCK:
+		CurrDisp = 0L;
+		for(i = 0; i < cDlgs; i++)
+			if(dlg[i] && dlg[i]->dialog) dlg[i]->dialog->Command(CMD_UNLOCK, 0L, 0L);
+		break;
+	case CMD_MARKOBJ:
+		if(mrk_item && mrk_item != (Dialog*)tmpl){
+			i = 27;
+			mrk_item->Command(CMD_ADDCHAR, &i, o);
+			}
+		mrk_item = (Dialog*)tmpl;
+		break;
+		}
+	return false;
+}
+
+void
+DlgRoot::DoPlot(anyOutput *o)
+{
+	int i;
+
+	HideCopyMark();			mrk_item = 0L;			bRedraw = false;
+	HideTextCursor();
+	if(tabstops) for(i = 0; i < cDlgs; tabstops[i++] = 0);
+	if(o)CurrDisp = o;		DialogDefault = 0L;
+	if(CurrDisp) {
+		CurrDisp->SetTextSpec(&DlgText);
+		ForEach(CMD_DOPLOT, 0, CurrDisp);
+		}
+	defs.Idle(CMD_UPDATE);
+}
+
+bool
+DlgRoot::CurUpDown(int cmd)
+{
+	int i, ya, yb, dy;
+	Dialog *above=0L, *below=0L;
+
+	ya = -1000;		yb = 10000;
+	if(DialogFocus && tabstops && DialogTabStop == DialogFocus) {
+		for(i = 0; tabstops[i] && i < cDlgs; i++) {
+			if(tabstops[i] != DialogTabStop) {
+				switch(tabstops[i]->type) {
+				case EDVAL1:	case INCDECVAL1:	case EDTEXT:	case RANGEINPUT:
+					if(rTxtCur.left > tabstops[i]->cr.left && 
+						rTxtCur.right < tabstops[i]->cr.right) {
+						if((dy = (tabstops[i]->cr.top - rTxtCur.top))< 0) {
+							if(dy > ya) {
+								ya = dy;	above = tabstops[i];
+								}
+							}
+						else {
+							if(dy < yb) {
+								yb = dy;	below = tabstops[i];
+								}
+							}
+						}
+					break;
+					}
+				}
+			}
+		switch(cmd) {
+		case CMD_CURRUP:
+			if(above) {
+				above->Select(rTxtCur.left, (above->cr.top + above->cr.bottom)>>1, CurrDisp); 
+				}
+			break;
+		case CMD_CURRDOWN:
+			if(below) {
+				below->Select(rTxtCur.left, (below->cr.top + below->cr.bottom)>>1, CurrDisp); 
+				}
+			break;
+			}
+		}
+	return false;
+}
+
+bool
+DlgRoot::GetColor(int id, DWORD *color)
+{
+	int i;
+
+	i = FindIndex(id);
+	if(i && color && dlg[i] && dlg[i]->dialog) return dlg[i]->dialog->GetColor(id, color);
+	return false;
+}
+
+void
+DlgRoot::SetColor(int id, DWORD color)
+{
+	int i;
+
+	i = FindIndex(id);
+	if(i && dlg[i]) {
+		dlg[i]->dialog->SetColor(id, color);
+		if(CurrDisp && !(dlg[i]->dialog->flags & HIDDEN)) dlg[i]->dialog->DoPlot(CurrDisp);
+		}
+}
+
+bool
+DlgRoot::GetValue(int id, double *val)
+{
+	int i;
+
+	i = FindIndex(id);
+	if(i && dlg[i]) return dlg[i]->dialog->GetValue(id, val);
+	return false;
+}
+
+bool
+DlgRoot::GetInt(int id, int *val)
+{
+	int i;
+
+	i = FindIndex(id);
+	if(i && dlg[i]) return dlg[i]->dialog->GetInt(id, val);
+	return false;
+}
+
+bool
+DlgRoot::SetCheck(int id, anyOutput *o, bool state)
+{
+	int i;
+
+	i = FindIndex(id);
+	if(i && dlg[i]) return dlg[i]->dialog->SetCheck(id, o ? o : CurrDisp, state);
+	return false;
+}
+
+bool
+DlgRoot::GetCheck(int Id)
+{
+	int i;
+
+	i = FindIndex(Id);
+	if(i && dlg[i]) return dlg[i]->dialog->GetCheck(Id);
+	return false;
+}
+
+bool
+DlgRoot::GetText(int id, char *txt, int size)
+{
+	int i;
+
+	i = FindIndex(id);
+	if(i && dlg[i]) return dlg[i]->dialog->GetText(id, txt, size);
+	return false;
+}
+
+bool
+DlgRoot::SetText(int id, char *txt)
+{
+	int i;
+
+	i = FindIndex(id);
+	if(i && dlg[i] && dlg[i]->dialog->Command(CMD_SETTEXT, txt, CurrDisp))bRedraw = true;
+	else return false;
+	return true;
+}
+
+bool
+DlgRoot::SetValue(int id, double val)
+{
+	int i;
+	char tmp_txt[80];
+
+	i = FindIndex(id);
+	WriteNatFloatToBuff(tmp_txt, val);
+	if(i && dlg[i] && dlg[i]->dialog->Command(CMD_SETTEXT, tmp_txt+1, CurrDisp))bRedraw = true;
+	else return false;
+	return true;
+}
+
+
+bool
+DlgRoot::TextStyle(int id, int style)
+{
+	int i;
+
+	i = FindIndex(id);
+	if(i && dlg[i]) dlg[i]->dialog->TextDef.Style = style;
+	else return false;
+	return true;
+}
+
+bool
+DlgRoot::TextFont(int id, int font)
+{
+	int i;
+
+	i = FindIndex(id);
+	if(i && dlg[i]) dlg[i]->dialog->TextDef.Font = font;
+	else return false;
+	return true;
+}
+
+bool
+DlgRoot::TextSize(int id, int size)
+{
+	int i;
+
+	i = FindIndex(id);
+	if(size <= 0.001f) return false;
+	if(i && dlg[i]) dlg[i]->dialog->TextDef.iSize = size;
+	else return false;
+	return true;
+}
+
+bool
+DlgRoot::ShowItem(int id, bool show)
+{
+	int i;
+
+	i = FindIndex(id);
+	if(i && dlg[i]) dlg[i]->dialog->flags = show ? 
+		dlg[i]->dialog->flags & ~HIDDEN : dlg[i]->dialog->flags | HIDDEN;
+	else return false;
+	return true;
+}
+
+int
+DlgRoot::GetResult()
+{
+	int ret;
+
+	if(res_put != res_get) ret = res_q[res_get++];
+	else ret = -1;
+	res_get &= 0xff;
+	if(bRedraw)DoPlot(0L);
+	else defs.Idle(CMD_UPDATE);
+	if(ret >= 0 && ParentOut) Undo.SetDisp(ParentOut);
+	return ret;
+}
+
+int
+DlgRoot::FindIndex(unsigned short id)
+{
+	int i;
+
+	for (i = 0; dlg[i] && i < cDlgs; i++) if(dlg[i]->id == id) return i;
+	return 0;
+}
+
+void
+DlgRoot::ForEach(int cmd, int start, anyOutput *o)
+{
+	int i, j, next;
+
+	if(o)CurrDisp = o;
+	if(dlg && CurrDisp) {
+		next = start;
+		do {
+			if(dlg[next] && dlg[next]->first) {
+				if(dlg[next]->flags && ISPARENT) {
+					if(dlg[next]->dialog) {
+						dlg[next]->dialog->Command(CMD_FLUSH, 0L, 0L);
+						//if j equals cDlgs we are caught in a circular reference
+						for(j = 0, i = dlg[next]->first; i && j < cDlgs; j++) {
+							if(i = FindIndex(i)) {
+								dlg[next]->dialog->Command(CMD_ADDCHILD, (void*)dlg[i]->dialog, 0L);
+								i = dlg[i]->next;
+								}
+							else{
+								i=i;
+								}
+							}
+						}
+					else return;	//error bad structured template
+					}
+				else {	//a debugging aid ....
+					InfoBox("Warning:\ndialog contains\ngroup which is not parent");
+					}
+				//resolve sub-groups recursively
+				//  this will not result in any action for children 
+				//  because the parent is not this!
+				ForEach(cmd, FindIndex(dlg[next]->first), 0L);
+				}
+			//parent objects (groups) will channel command to children
+			if(dlg[next] && dlg[next]->dialog && dlg[next]->dialog->parent == this && 
+				!(dlg[next]->dialog->flags & HIDDEN)) switch(cmd) {
+				case CMD_DOPLOT: 
+					dlg[next]->dialog->DoPlot(CurrDisp);
+					break;
+				case CMD_LBUP:
+					dlg[next]->dialog->Select(mev->x, mev->y, CurrDisp);
+					break;
+				case CMD_MOUSE_EVENT:
+					dlg[next]->dialog->MBtrack(mev, CurrDisp);
+					break;
+				case CMD_SELECT:
+					dlg[next]->dialog->Select(mev->x, mev->y, CurrDisp);
+					break;
+				}
+			next = FindIndex(dlg[next] ? dlg[next]->next : 0);
+			}while(next && next < cDlgs);
+		}
+}
+
+void
+DlgRoot::Activate(int id, bool active)
+{
+	int i;
+
+	i = FindIndex(id);
+	if(i && dlg[i]) dlg[i]->dialog->Activate(id, active);
+}
+
+bool
+DlgRoot::ItemCmd(int id, int cmd, void *tmpl)
+{
+	int i;
+
+	if((i = FindIndex(id)) && dlg[i]){ 
+		bRedraw = true;
+		return dlg[i]->dialog->Command(cmd, tmpl, CurrDisp);
+		}
+	return false;
+}
+
+Dialog::Dialog(tag_DlgObj *par, DlgInfo *desc, RECT rec)
+{
+	parent = par;
+	Id = desc->id;
+	flags = desc->flags;
+	memcpy(&cr, &rec, sizeof(RECT));	memcpy(&hcr, &rec, sizeof(RECT));
+	Line.width = 0.0;					Line.patlength = 1.0;
+	Line.color = DlgBGcolor;			Line.pattern = 0x00000000L;
+	Fill.type = FILL_NONE;				Fill.color = DlgBGcolor;
+	Fill.scale = 1.0;					Fill.hatch = 0L;
+	memcpy(&TextDef, &DlgText, sizeof(TextDEF));
+	type = desc->type;
+	bChecked = flags & CHECKED ? true : false;
+	bLBdown = false;
+	if(DEFAULT == (flags & DEFAULT)) DialogDefault = DialogTabStop = this;
+	bActive = true;
+}
+
+bool
+Dialog::Select(int x, int y, anyOutput *o)
+{
+	if(x > cr.left && x < cr.right && y > cr.top && y < cr.bottom) {
+		if((flags & TOUCHEXIT) && parent)
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
+		}
+	return false;
+}
+
+bool 
+Dialog::SetCheck(int id, anyOutput *o, bool state)
+{
+	if(state != bChecked) {
+		if(parent && state && (flags & ISRADIO)) parent->Command(CMD_RADIOBUTT, (void *)this, o);
+		bChecked = state;
+		if(o) DoPlot(o);
+		return true;
+		}
+	return false;
+}
+
+void
+Dialog::MBtrack(MouseEvent *mev, anyOutput *o)
+{
+	bool bLBstate = false;
+
+	if(mev->Action == MOUSE_LBDOUBLECLICK) {
+		Select(mev->x, mev->y, o);
+		return;
+		}
+	switch(type){
+	case PUSHBUTTON:	case ARROWBUTT:		case CHECKBOX:		case RADIO1:
+	case RADIO2:		case TRASH:			case CONFIG:		case CHECKPIN:
+		if(mev->StateFlags &1) bLBstate = true;
+		if(IsInRect(&hcr, mev->x, mev->y) && bLBstate){
+			if(parent && type != CHECKBOX && type != RADIO1 && type != RADIO2) parent->Command(CMD_MARKOBJ, this, o);
+			if(!bLBdown){
+				bLBdown = bLBstate;			DoPlot(o);
+				return;
+				}
+			}
+		else if(bLBdown){
+			bLBdown = false;
+			DoPlot(o);
+			}
+		break;
+		}
+}
+
+void
+Dialog::Activate(int id, bool active)
+{
+	if(id == Id) bActive = active;
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Collection of dialog items
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// a simple text button
+PushButton::PushButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
+	:Dialog(par, desc, rec)
+{
+	if(text && text[0]) Text = (char*)memdup(text, (int)strlen(text)+1, 0);
+	else Text = 0L;
+	TextDef.Align = TXA_HCENTER | TXA_VCENTER;
+}
+
+PushButton::~PushButton()
+{
+	if(Text) free (Text);
+}
+
+bool
+PushButton::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch(cmd) {
+	case CMD_ENDDIALOG:
+		parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
+	case CMD_ADDCHAR:
+		HideCopyMark();
+		if(parent && *((int*)tmpl) == 0x0d)		//return pressed
+			 parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
+		}
+	return false;
+}
+
+void
+PushButton::DoPlot(anyOutput *o)
+{
+	POINT pts[3];
+
+	Line.color = 0x00000000L;			Fill.color = DlgBGhigh;
+	o->SetLine(&Line);					o->SetFill(&Fill);
+	if(bLBdown) o->oRectangle(cr.left, cr.top, cr.right-1, cr.bottom-1);
+	else {
+		o->oRectangle(cr.left, cr.top, cr.right-2, cr.bottom-2);
+		Line.color = DlgBGcolor;
+		o->SetLine(&Line);
+		pts[0].x = cr.left;					pts[0].y = pts[1].y = cr.bottom-1;
+		pts[1].x = pts[2].x = cr.right-1;	pts[2].y = cr.top-1;
+		o->oPolyline(pts, 3);
+		Line.color = 0x00000000L;
+		o->SetLine(&Line);
+		pts[0].x = cr.left+5;				pts[0].y = pts[1].y = cr.bottom-1;
+		pts[1].x = pts[2].x = cr.right-1;	pts[2].y = cr.top +1;
+		o->oPolyline(pts, 3);
+		Line.color = 0x00ffffffL;
+		o->SetLine(&Line);
+		pts[0].x = pts[1].x = cr.left;
+		pts[0].y = cr.bottom -3;
+		pts[1].y = pts[2].y = cr.top;
+		pts[2].x = cr.right -2;
+		o->oPolyline(pts, 3);
+		}
+	if(Text) {
+		TextDef.Style = DialogDefault == this ? TXS_BOLD : TXS_NORMAL;
+		o->SetTextSpec(&TextDef);
+		o->oTextOut((cr.left + cr.right)/2-2, (cr.top + cr.bottom)/2-1, Text, 0);
+		}
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+	if(parent)parent->Command(CMD_TABDLG, this, o);
+}
+
+bool
+PushButton::Select(int x, int y, anyOutput *o)
+{
+	if(IsInRect(&cr, x, y)) {
+		bLBdown = false;		DoPlot(o);
+		if(parent) parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
+		}
+	else if(bLBdown) {
+		bLBdown = false;		DoPlot(o);
+		}
+	return false;
+}
+
+TextBox::TextBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
+	:Dialog(par, desc, rec)
+{
+	lfPOINT lfp1, lfp2;
+
+	TextDef.Font = FONT_COURIER;	TextDef.Align = TXA_VBOTTOM | TXA_HLEFT;
+#ifdef _WINDOWS
+//	TextDef.fSize unchanged
+#else
+	TextDef.fSize *= .8;
+#endif
+	Fill.color = 0x00ffffffL;	Line.color = 0x00000000L;	cont = 0L;
+	lfp1.fx = rec.left;				lfp1.fy = rec.top;
+	lfp2.fx = rec.right;			lfp2.fy = rec.bottom;
+	if(cont = new TextFrame(0L, 0L, &lfp1, &lfp2, text)) cont->Command(CMD_SETTEXTDEF, &TextDef, 0L);
+}
+
+TextBox::~TextBox()
+{
+	if(cont)DeleteGO(cont);
+}
+
+bool
+TextBox::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch(cmd) {
+	case CMD_CURRLEFT:		case CMD_CURRIGHT:		case CMD_DELETE:
+	case CMD_POS_FIRST:		case CMD_POS_LAST:		case CMD_SHIFTLEFT:
+	case CMD_SHIFTRIGHT:	case CMD_ADDCHAR:		case CMD_MOUSE_EVENT:
+	case CMD_COPY:			case CMD_PASTE:			case CMD_CURRUP:
+	case CMD_CURRDOWN:
+		if(bChecked && CurrGO && CurrGO == cont) return CurrGO->Command(cmd, tmpl, o);
+		break;
+	case CMD_SETTEXT:
+		if(cont)return cont->Command(cmd, tmpl, o);
+		return false;
+		}
+	return false;
+}
+
+void
+TextBox::DoPlot(anyOutput *o)
+{
+	if(cont && o)cont->DoPlot(o);
+}
+
+bool
+TextBox::Select(int x, int y, anyOutput *o)
+{
+	POINT p1;
+
+	p1.x = x;			p1.y = y;
+	if(bActive && IsInRect(&cr, x, y) && !(flags & NOEDIT)) {
+		DialogDefault = DialogFocus = DialogTabStop = this;
+		if(CurrGO = cont) bChecked = cont->Command(CMD_SELECT, &p1, o);
+		}
+	return false;
+}
+
+void
+TextBox::MBtrack(MouseEvent *mev, anyOutput *o)
+{
+	bool bLBstate;
+	int x, y;
+
+	x = mev->x;						y = mev->y;
+	if(mev->StateFlags &1) bLBstate = true;
+	else bLBstate = false;
+	if(bActive && IsInRect(&cr, x, y) && !(flags & NOEDIT) && 
+		bLBstate && cont && parent) {
+		DialogFocus = this;			bChecked = true;
+		parent->Command(CMD_FOCTXT, (void*)this, 0L);
+		cont->Command(CMD_MOUSE_EVENT, mev, o);
+		parent->Command(CMD_MARKOBJ, this, o);
+		}
+}
+
+bool
+TextBox::GetText(int id, char *txt, int size)
+{
+	if(cont) {
+		if(!(cont->Command(CMD_ALLTEXT, TmpTxt, 0L))) return false;
+		rlp_strcpy(txt, size, TmpTxt);
+		return true;
+		}
+	return false;
+}
+
+ArrowButton::ArrowButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, int *which)
+	:Dialog(par, desc, rec)
+{
+	direct = which ? *which : 0;
+}
+
+void
+ArrowButton::DoPlot(anyOutput *o)
+{
+	POINT pts[4];
+	int ix, iy, dx, dy;
+
+	Line.color = 0x00000000L;
+	Fill.color = DlgBGhigh;
+	ix = (dx =(cr.right-cr.left))>5 ? dx>>2 : 2;
+	iy = (dy =(cr.bottom-cr.top))>5 ? dy>>2 : 2;
+	o->SetLine(&Line);						o->SetFill(&Fill);
+	if(bLBdown) o->oRectangle(cr.left, cr.top, cr.right-1, cr.bottom-1);
+	else {
+		o->oRectangle(cr.left, cr.top, cr.right-2, cr.bottom-2);
+		Line.color = DlgBGcolor;
+		o->SetLine(&Line);
+		pts[0].x = cr.left;					pts[0].y = pts[1].y = cr.bottom-1;
+		pts[1].x = pts[2].x = cr.right-1;	pts[2].y = cr.top-1;
+		o->oPolyline(pts, 3);				Line.color = 0x00ffffffL;
+		o->SetLine(&Line);					pts[0].x = pts[1].x = cr.left;
+		pts[0].y = cr.bottom -3;			pts[1].y = pts[2].y = cr.top;
+		pts[2].x = cr.right -2;				o->oPolyline(pts, 3);
+		}
+	Fill.color = Line.color = 0x00000000L;
+	o->SetLine(&Line);						o->SetFill(&Fill);
+	switch(direct) {
+	case 1:
+		pts[0].x = pts[3].x = cr.left+ix;	pts[0].y = pts[3].y = pts[1].y = cr.bottom-(iy<<1);
+		pts[1].x = cr.right-(ix<<1);		pts[2].x = (cr.right + cr.left)/2 -1;
+		pts[2].y = cr.top+iy;				o->oPolygon(pts, 4);
+		break;
+	case 2:
+		pts[0].x = pts[3].x = cr.left+ix;	pts[0].y = pts[3].y = pts[1].y = cr.top+iy;
+		pts[1].x = cr.right-(ix<<1);		pts[2].x = (cr.right + cr.left)/2 -1;
+		pts[2].y = cr.bottom-(iy<<1);		o->oPolygon(pts, 4);
+		break;
+	case 3:
+		pts[0].x = pts[3].x = cr.left+ix;	pts[0].y = pts[3].y = (cr.bottom + cr.top)/2-1;
+		pts[1].x = pts[2].x = cr.right-(ix<<1);
+		pts[1].y = cr.bottom-(iy<<1);		pts[2].y = cr.top+iy;
+		o->oPolygon(pts, 4);
+		break;
+	case 4:
+		pts[0].x = pts[3].x = cr.right-(ix<<1);
+		pts[0].y = pts[3].y = (cr.bottom + cr.top)/2-1;
+		pts[1].x = pts[2].x = cr.left+2;	pts[1].y = cr.bottom-(iy<<1);
+		pts[2].y = cr.top+iy;				o->oPolygon(pts, 4);
+		break;
+		}
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+ArrowButton::Select(int x, int y, anyOutput *o)
+{
+	if(IsInRect(&cr, x, y)) {
+		bLBdown = false;					DoPlot(o);
+		if(parent) parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
+		}
+	else if(bLBdown) {
+		bLBdown = false;					DoPlot(o);
+		}
+	return false;
+}
+
+
+ColorButton::ColorButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, DWORD *color)
+	:Dialog(par, desc, rec)
+{
+	col = color ? *color : 0x0L;
+}
+
+void
+ColorButton::DoPlot(anyOutput *o)
+{
+	POINT pts[5];
+
+	Fill.color = (col & 0x00ffffffL);
+	o->SetFill(&Fill);
+	if(flags & ISRADIO) {
+		Line.color = bChecked ? 0x00000000L : DlgBGcolor;
+		o->SetLine(&Line);
+		pts[0].x = pts[3].x = pts[4].x = cr.left;
+		pts[0].y = pts[1].y = pts[4].y = cr.top;
+		pts[1].x = pts[2].x = cr.right-1;
+		pts[2].y = pts[3].y = cr.bottom-1;
+		o->oPolyline(pts, 5);
+		Line.color = 0x00000000L;
+		o->SetLine(&Line);
+		o->oRectangle(cr.left+3, cr.top+3, cr.right-3, cr.bottom-3);
+		}
+	else {
+		Line.color = 0x00000000L;
+		o->SetLine(&Line);
+		o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
+		}
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+ColorButton::Select(int x, int y, anyOutput *o)
+{
+	if(!parent) return false;
+	if(IsInRect(&cr, x, y)) {
+		bChecked = true;
+		if((flags & OWNDIALOG)) {
+			parent->Command(CMD_CONTINUE, 0L, o);
+			col = GetNewColor(col);
+			}
+		if(flags & ISRADIO) parent->Command(CMD_RADIOBUTT, (void *)this, o);
+		if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		DoPlot(o);
+		return true;
+		}
+	return false;
+}
+
+FillButton::FillButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, FillDEF *fill)
+	:Dialog(par, desc, rec)
+{
+	CurrFill = fill;
+}
+
+void
+FillButton::DoPlot(anyOutput *o)
+{
+	Line.color = 0x00000000L;
+	o->SetLine(&Line);
+	o->SetFill(CurrFill);
+	o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+FillButton::Select(int x, int y, anyOutput *o)
+{
+	if(IsInRect(&cr, x, y)) {
+		if((flags & OWNDIALOG)) {
+			if(parent)parent->Command(CMD_CONTINUE, 0L, o);
+			GetNewFill(CurrFill);
+			}
+		if((flags & TOUCHEXIT) && parent) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		DoPlot(o);
+		return true;
+		}
+	return false;
+}
+
+Shade3D::Shade3D(tag_DlgObj *par, DlgInfo * desc, RECT rec, FillDEF *fill)
+	:Dialog(par, desc, rec)
+{
+	CurrFill = fill;
+}
+
+void
+Shade3D::DoPlot(anyOutput *o)
+{
+	POINT pts[5];
+	FillDEF fd;
+	int dx;
+
+	Line.color = 0x00000000L;
+	o->SetLine(&Line);
+	if(CurrFill->type & FILL_LIGHT3D) {
+		fd.color = fd.color2 = (CurrFill->color & 0x00ffffff);
+		fd.hatch = 0L;		fd.scale = 1.0;
+		fd.type = 0L;
+		dx = iround(((double)(cr.bottom - cr.top))*.26);
+		pts[0].x = pts[1].x = pts[4].x = ((cr.left + cr.right)>>1);
+		pts[0].y = pts[4].y = cr.bottom;
+		pts[1].y = ((cr.top + cr.bottom)>>1);
+		pts[2].x = pts[3].x = pts[0].x + (dx<<1);
+		pts[2].y = pts[1].y - dx;		pts[3].y = pts[0].y - dx;
+		o->SetFill(&fd);
+		o->oPolygon(pts, 5, 0L);
+		fd.color = fd.color2 = (IpolCol(CurrFill->color, CurrFill->color2, 0.4) & 0x00ffffffL);
+		pts[2].x = pts[3].x = pts[0].x - (dx<<1);
+		o->SetFill(&fd);
+		o->oPolygon(pts, 5, 0L);
+		fd.color = fd.color2 = (CurrFill->color2 & 0x00ffffffL);
+		pts[0].y = pts[4].y = pts[1].y;
+		pts[1].x = pts[2].x;			pts[1].y = pts[3].y = pts[2].y;
+		pts[2].x = pts[0].x;			pts[2].y -= dx;
+		pts[3].x = pts[0].x + (dx<<1);
+		o->SetFill(&fd);
+		o->oPolygon(pts, 5, 0L);
+		}
+	else {
+		o->SetFill(CurrFill);
+		o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
+		}
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+void ConfShade(FillDEF *oldfill);
+bool
+Shade3D::Select(int x, int y, anyOutput *o)
+{
+	if(IsInRect(&cr, x, y)) {
+		if((flags & OWNDIALOG)) {
+			if(parent)parent->Command(CMD_CONTINUE, 0L, o);
+			ConfShade(CurrFill);
+			}
+		if((flags & TOUCHEXIT) && parent) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		DoPlot(o);
+		return true;
+		}
+	return false;
+}
+
+LineButton::LineButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, LineDEF *line)
+	:Dialog(par, desc, rec)
+{
+	Line.color = 0x00000000L;
+	CurrLine = line;
+	Fill.color = defs.Color(COL_BG);
+	pts[0].x = cr.left+4;
+	pts[1].x = cr.right-5;
+	pts[0].y = pts[1].y = (cr.top + cr.bottom)/2;
+}
+
+void
+LineButton::DoPlot(anyOutput *o)
+{
+	o->SetLine(&Line);
+	o->SetFill(&Fill);
+	o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
+	o->SetLine(CurrLine);
+	o->oPolyline(pts, 2);
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+LineButton::Select(int x, int y, anyOutput *o)
+{
+	if(IsInRect(&cr, x, y)) {
+		if((flags & TOUCHEXIT) && parent) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		DoPlot(o);
+		return true;
+		}
+	return false;
+}
+
+SymButton::SymButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, Symbol **sym)
+	:Dialog(par, desc, rec)
+{
+	symbol = sym;
+	Line.color = 0x00000000L;
+	Fill.color = 0x00ffffffL;
+}
+
+void
+SymButton::DoPlot(anyOutput *o)
+{
+	Line.color = 0x00000000L;
+	o->SetLine(&Line);
+	o->SetFill(&Fill);
+	o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
+	if(*(symbol)) {		//center symbol in the rectangle
+		(*symbol)->SetSize(SIZE_XPOS, (cr.right+cr.left)/2.0);
+		(*symbol)->SetSize(SIZE_YPOS, (cr.bottom+cr.top)/2.0);
+		(*symbol)->DoPlot(o);
+		}
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+SymButton::Select(int x, int y, anyOutput *o)
+{
+	if(parent && IsInRect(&cr, x, y)) {
+		if((flags & OWNDIALOG) && (*(symbol))) {
+			parent->Command(CMD_CONTINUE, 0L, o);
+			(*symbol)->PropertyDlg();
+			}
+		if((flags & TOUCHEXIT)) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		DoPlot(o);
+		return true;
+		}
+	return false;
+}
+
+FillRadioButt::FillRadioButt(tag_DlgObj *par, DlgInfo * desc, RECT rec, unsigned int pattern)
+	:Dialog(par, desc, rec)
+{
+	Line.pattern = 0x00000000L;			Fill.type = pattern;
+	Fill.color = 0x00ffffffL;			Fill.hatch = &Line;
+	flags |= ISRADIO;
+}
+
+void
+FillRadioButt::DoPlot(anyOutput *o)
+{
+	POINT pts[5];
+
+	Line.color = bChecked ? 0x00000000L : DlgBGcolor;
+	o->SetLine(&Line);
+	pts[0].x = pts[3].x = pts[4].x = cr.left;
+	pts[0].y = pts[1].y = pts[4].y = cr.top;
+	pts[1].x = pts[2].x = cr.right-1;
+	pts[2].y = pts[3].y = cr.bottom-1;
+	o->oPolyline(pts, 5);
+	Line.color = 0x00000000L;
+	o->SetLine(&Line);
+	o->SetFill(&Fill);
+	o->oRectangle(cr.left+3, cr.top+3, cr.right-3, cr.bottom-3);
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+FillRadioButt::Select(int x, int y, anyOutput *o)
+{
+	if(IsInRect(&cr, x, y)) {
+		bChecked = true;
+		DoPlot(o);
+		if(parent) parent->Command(CMD_RADIOBUTT, (void *)this, o);
+		if((flags & TOUCHEXIT) && parent) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
+		}
+	return false;
+}
+
+SymRadioButt::SymRadioButt(tag_DlgObj *par, DlgInfo * desc, RECT rec, int* type)
+	:Dialog(par, desc, rec)
+{
+	double size;
+
+	if(type && (Sym = new Symbol(0L, 0L, ((double)(cr.right+cr.left))/2.0, 
+		((double)(cr.bottom+cr.top))/2.0, *type))){
+		size = (0.14*(double)(cr.bottom-cr.top))/Units[defs.cUnits].convert;
+		Sym->SetSize(SIZE_SYMBOL, size);			Sym->SetSize(SIZE_SYM_LINE, size/10.0);
+		Sym->SetColor(COL_SYM_LINE, 0x00000000L);	Sym->SetColor(COL_SYM_FILL, 0x00ffffffL);
+		}
+	Fill.color = DlgBGhigh;		flags |= ISRADIO;
+}
+
+SymRadioButt::~SymRadioButt()
+{
+	if (Sym) delete Sym;
+}
+
+void
+SymRadioButt::DoPlot(anyOutput *o)
+{
+	if(!Sym) return;
+	Line.color = bChecked ? 0x00000000L : (Fill.color & 0x00ffffff);
+	o->SetFill(&Fill);
+	o->SetLine(&Line);
+	o->oRectangle(cr.left+1, cr.top+1, cr.right-1, cr.bottom-1);
+	Sym->DoPlot(o);
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+SymRadioButt::Select(int x, int y, anyOutput *o)
+{
+	if(IsInRect(&cr, x, y)) {
+		bChecked = true;
+		DoPlot(o);
+		if(parent) parent->Command(CMD_RADIOBUTT, (void *)this, o);
+		if((flags & TOUCHEXIT) && parent) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
+		}
+	return false;
+}
+
+CheckBox::CheckBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
+	:Dialog(par, desc, rec)
+{
+	if(text && text[0])Text = (char*)memdup(text, (int)strlen(text)+1, 0);
+	else Text = 0L;
+	hcr.left = cr.left+2;		hcr.right = cr.left+14;
+	hcr.top = cr.top+3;			hcr.bottom = cr.top+15;
+}
+
+CheckBox::~CheckBox()
+{
+	if(Text) free (Text);
+}
+
+bool
+CheckBox::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch(cmd) {
+	case CMD_SETTEXT:
+		if(Text) free(Text);
+		if(tmpl && *((char*)tmpl)) Text = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1,0);
+		else Text = 0L;
+		return true;
+		}
+	return false;
+}
+
+void
+CheckBox::DoPlot(anyOutput *o)
+{
+	POINT pts[3];
+
+	if(flags & HIDDEN) return;
+	Line.color = 0x00000000L;
+	Line.width = 0.0;
+	Fill.color = bLBdown ? DlgBGcolor : 0x00ffffffL;
+	o->SetLine(&Line);
+	o->SetFill(&Fill);
+	o->oRectangle(cr.left+2, cr.top+3, cr.left+14, cr.top+15);
+	if(bChecked) {
+		Line.width = defs.GetSize(SIZE_SYM_LINE)*2.0;
+		o->SetLine(&Line);
+		pts[0].x = cr.left+4;			pts[0].y = cr.top+5;
+		pts[1].x = cr.left+11;			pts[1].y = cr.top+12;
+		o->oSolidLine(pts);
+		pts[0].x = cr.left+11;			pts[1].x = cr.left+4;
+		o->oSolidLine(pts);
+		}
+	if(Text) {
+		o->SetTextSpec(&TextDef);
+		o->oTextOut(cr.left + 20, cr.top + 1, Text, (int)strlen(Text));
+		}
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+CheckBox::Select(int x, int y, anyOutput *o)
+{
+	if(!(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
+		bChecked ^= 0x01;	//toggle selection
+		bLBdown = false;
+		DoPlot(o);
+		if((flags & ISRADIO) && parent)
+			parent->Command(CMD_RADIOBUTT, (void *)this, o);
+		if((flags & TOUCHEXIT) && parent) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
+		}
+	return false;
+}
+
+CheckPin::CheckPin(tag_DlgObj *par, DlgInfo * desc, RECT rec)
+	:Dialog(par, desc, rec)
+{
+	hcr.left = cr.left+2;		hcr.right = cr.right-2;
+	hcr.top = cr.top+2;			hcr.bottom = cr.bottom-2;
+}
+
+CheckPin::~CheckPin()
+{
+}
+
+bool
+CheckPin::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch(cmd) {
+	case CMD_UNLOCK:
+		bChecked = false;
+		return true;
+		}
+	return false;
+}
+
+void
+CheckPin::DoPlot(anyOutput *o)
+{
+	POINT pts[20];
+
+	if(!o || flags & HIDDEN) return;
+	Line.width = 0.0;
+	Line.color = DlgBGcolor;		Fill.color = DlgBGcolor;
+	o->SetLine(&Line);		o->SetFill(&Fill);
+	o->oRectangle(hcr.left+2, hcr.top, hcr.right, hcr.bottom);
+	Line.color = 0x00000000L;		Fill.color = bLBdown ? 0x00ffffff : 0x00e8e8e8L;
+	o->SetLine(&Line);		o->SetFill(&Fill);
+	if(bChecked) {
+		o->oCircle(hcr.left + 5, hcr.top + 1, hcr.left + 15, hcr.top + 12);
+		o->oCircle(hcr.left + 8, hcr.top + 3, hcr.left + 15, hcr.top + 10);
+		}
+	else {
+		pts[0].x = hcr.left + 8;	pts[0].y = hcr.top + 4;
+		pts[1].x = pts[0].x - 5;	pts[1].y = pts[0].y + 1;
+		pts[2].x = pts[0].x;		pts[2].y = pts[1].y + 1;
+		pts[3].x = pts[0].x;		pts[3].y = pts[0].y;
+		o->oPolygon(pts, 4);
+		pts[0].x = hcr.left + 8;	pts[0].y = hcr.top + 1;
+		pts[1].x = pts[0].x;		pts[1].y = pts[0].y + 9;
+		pts[2].x = pts[1].x + 3;	pts[2].y = pts[1].y - 3;
+		pts[3].x = pts[2].x + 4;	pts[3].y = pts[2].y;
+		pts[4].x = pts[3].x;		pts[4].y = pts[3].y - 3;
+		pts[5].x = pts[4].x - 4;	pts[5].y = pts[4].y;
+		pts[6].x = pts[0].x;		pts[6].y = pts[0].y;
+		o->oPolygon(pts, 7);
+		o->oCircle(pts[4].x-2, pts[0].y, pts[4].x+3, pts[1].y+1);
+		}
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+CheckPin::Select(int x, int y, anyOutput *o)
+{
+	if(!(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
+		bChecked ^= 0x01;	//toggle selection
+		bLBdown = false;	DoPlot(o);
+		if((flags & TOUCHEXIT) && parent) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		bLBdown=false;				DoPlot(o);
+		return true;
+		}
+	return false;
+}
+
+Trash::Trash(tag_DlgObj *par, DlgInfo * desc, RECT rec)
+	:Dialog(par, desc, rec)
+{
+	hcr.left = cr.left+2;		hcr.right = cr.right-2;
+	hcr.top = cr.top+2;			hcr.bottom = cr.bottom-2;
+}
+
+Trash::~Trash()
+{
+}
+
+bool
+Trash::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	return false;
+}
+
+void
+Trash::DoPlot(anyOutput *o)
+{
+	POINT pts[2];
+
+	if(!o || flags & HIDDEN) return;
+	Line.width = 0.0;
+	Line.color = DlgBGcolor;		Fill.color = DlgBGcolor;
+	o->SetLine(&Line);		o->SetFill(&Fill);
+	o->oRectangle(hcr.left+2, hcr.top, hcr.right, hcr.bottom);
+	Line.color = 0x00000000L;		Fill.color = bLBdown ? 0x00ffffff : 0x00e8e8e8L;
+	o->SetLine(&Line);		o->SetFill(&Fill);
+	o->oRectangle(cr.left+8, cr.top+10, cr.right-8, cr.bottom-3);
+	o->oRectangle(cr.left+6, cr.top+5, cr.right-6, cr.top+10);
+	o->oRectangle(cr.left+11, cr.top+2, cr.right-11, cr.top+5);
+	pts[0].y = cr.top + 12;						pts[1].y = cr.bottom - 5;
+	pts[0].x = pts[1].x = cr.left + 12;			o->oSolidLine(pts);
+	pts[0].x = pts[1].x = cr.right - 13;		o->oSolidLine(pts);
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+Trash::Select(int x, int y, anyOutput *o)
+{
+	if(!(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
+		if((flags & TOUCHEXIT) && parent) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		bLBdown=false;				DoPlot(o);
+		return true;
+		}
+	return false;
+}
+
+Config::Config(tag_DlgObj *par, DlgInfo * desc, RECT rec)
+	:Dialog(par, desc, rec)
+{
+	hcr.left = cr.left+2;		hcr.right = cr.right-2;
+	hcr.top = cr.top+2;			hcr.bottom = cr.bottom-2;
+}
+
+Config::~Config()
+{
+}
+
+bool
+Config::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	return false;
+}
+
+void
+Config::DoPlot(anyOutput *o)
+{
+	POINT pts[2];
+
+	if(!o || flags & HIDDEN) return;
+	Line.width = 0.0;
+	Line.color = DlgBGcolor;		Fill.color = DlgBGcolor;
+	o->SetLine(&Line);		o->SetFill(&Fill);
+	o->oRectangle(hcr.left+2, hcr.top, hcr.right, hcr.bottom);
+	Line.color = 0x00000000L;		Fill.color = bLBdown ? 0x00ffffff : 0x00e8e8e8L;
+	o->SetLine(&Line);		o->SetFill(&Fill);
+	o->oRectangle(cr.left+3, cr.top+3, cr.right-3, cr.bottom-3);
+	o->oRectangle(cr.left+8, cr.top+6, cr.left+11, cr.top+9);
+	o->oRectangle(cr.left+8, cr.top+10, cr.left+11, cr.top+13);
+	o->oRectangle(cr.left+8, cr.top+14, cr.left+11, cr.top+17);
+	o->oRectangle(cr.left+7, cr.top+18, cr.right-7, cr.bottom-5);
+	pts[0].x = cr.left + 14;					pts[1].x = cr.right - 8;
+	pts[0].y = pts[1].y = cr.top + 7;			o->oSolidLine(pts);
+	pts[0].y = pts[1].y = cr.top + 11;			o->oSolidLine(pts);
+	pts[0].y = pts[1].y = cr.top + 15;			o->oSolidLine(pts);
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+Config::Select(int x, int y, anyOutput *o)
+{
+	if(!(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
+		if((flags & TOUCHEXIT) && parent) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		bLBdown=false;				DoPlot(o);
+		return true;
+		}
+	return false;
+}
+
+RadioButton::RadioButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
+	:Dialog(par, desc, rec)
+{
+	if(text && text[0])Text = (char*)memdup(text, (int)strlen(text)+1, 0);
+	else Text = 0L;
+	switch(type) {
+	case RADIO0:
+		hcr.left = cr.left+4;		hcr.right = cr.left+13;
+		hcr.top = cr.top+4;			hcr.bottom = cr.top+13;
+		break;
+	case RADIO1:
+		hcr.left = cr.left+2;		hcr.right = cr.left+15;
+		hcr.top = cr.top+3;			hcr.bottom = cr.top+16;
+		break;
+	case RADIO2:
+		hcr.left = cr.left;			hcr.right = cr.left+15;
+		hcr.top = cr.top+1;			hcr.bottom = cr.top+16;
+		break;
+		}
+	flags |= ISRADIO;
+}
+
+RadioButton::~RadioButton()
+{
+	if(Text) free (Text);
+}
+
+bool
+RadioButton::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch(cmd) {
+	case CMD_SETTEXT:
+		if(Text) free(Text);
+		if(tmpl && *((char*)tmpl))Text = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0);
+		else Text = 0L;
+		return true;
+		}
+	return false;
+}
+
+void
+RadioButton::DoPlot(anyOutput *o)
+{
+	if(flags & HIDDEN) return;
+	Line.color = 0x00000000L;
+	Line.width = 0.0;
+	Fill.color = bLBdown ? DlgBGcolor : 0x00ffffffL;
+	if(bActive) {
+		o->SetLine(&Line);
+		o->SetFill(&Fill);
+		o->oCircle(hcr.left, hcr.top, hcr.right, hcr.bottom);
+		if(bChecked) {
+			Fill.color = 0x00000000L;		o->SetFill(&Fill);
+			switch (type) {
+			case RADIO0:
+				o->oCircle(cr.left+6, cr.top+6, cr.left+11, cr.top+11);
+				break;
+			case RADIO1:
+				o->oCircle(cr.left+5, cr.top+6, cr.left+12, cr.top+13);
+				break;
+			case RADIO2:
+				o->oCircle(cr.left+3, cr.top+4, cr.left+12, cr.top+13);
+				break;
+				}	
+			}
+		}
+	if(Text) {
+		o->SetTextSpec(&TextDef);
+		o->oTextOut(cr.left + 20, cr.top + 1, Text, (int)strlen(Text));
+		}
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+RadioButton::Select(int x, int y, anyOutput *o)
+{
+	if(bActive && !(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
+		bChecked = true;		bLBdown = false;
+		DoPlot(o);
+		if(parent) parent->Command(CMD_RADIOBUTT, (void *)this, o);
+		if((flags & TOUCHEXIT) && parent) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
+		}
+	return false;
+}
+
+void
+RadioButton::SetColor(int id, DWORD color)
+{
+	TextDef.ColTxt = (color & 0x00ffffffL);
+}
+
+Text::Text(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
+	:Dialog(par, desc, rec)
+{
+	if(text && text[0])txt = (char*)memdup(text, (int)strlen(text)+1, 0);
+	else txt = 0L;
+	switch (type) {
+	case RTEXT:		TextDef.Align = TXA_HRIGHT | TXA_VTOP;	break;
+	case CTEXT:		TextDef.Align = TXA_HCENTER | TXA_VTOP;	break;
+	default:		TextDef.Align = TXA_HLEFT | TXA_VTOP;	break;
+		}
+}
+
+Text::~Text()
+{
+	if(txt) free(txt);
+}
+
+bool
+Text::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch(cmd) {
+	case CMD_SETTEXT:
+		if(txt) free(txt);
+		if(tmpl && *((char*)tmpl))txt = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0);
+		else txt = 0L;
+		return true;
+		}
+	return false;
+}
+
+void
+Text::DoPlot(anyOutput *o)
+{
+	if(WWWbrowser && WWWbrowser[0] && (flags & HREF)) {
+		TextDef.ColTxt = 0x00ff0000L;
+		TextDef.Style |= TXS_UNDERLINE;
+		}
+	if(txt){
+		o->SetTextSpec(&TextDef);
+		switch(type) {
+		case RTEXT: o->oTextOut(cr.right - 2, cr.top + 1, txt, 0);				break;
+		case CTEXT: o->oTextOut((cr.left + cr.right)/2, cr.top + 1, txt, 0);	break;
+		default:	o->oTextOut(cr.left + 2, cr.top + 1, txt, 0);				break;
+			}
+		defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+		}
+}
+
+bool
+Text::Select(int x, int y, anyOutput *o)
+{
+	int i;
+
+	if(WWWbrowser && WWWbrowser[0] && txt && (flags & HREF) && IsInRect(&cr, x, y)) {
+		o->MouseCursor(MC_WAIT, false);
+		i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE-50, WWWbrowser);
+		TmpTxt[i++] = ' ';
+		rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, txt);
+		if(parent && (flags & TOUCHEXIT))parent->Command(CMD_ENDDIALOG, 0L, o);
+		else if(parent)parent->Command(CMD_CONTINUE, 0L, o);
+#ifdef _WINDOWS
+		WinExec(TmpTxt, SW_SHOWMAXIMIZED); 
+#else
+		strcat(TmpTxt, " &");
+		system(TmpTxt);
+#endif
+		o->MouseCursor(MC_ARROW, false);
+		return true;
+		}
+	return false;
+}
+
+void
+Text::SetColor(int id, DWORD color)
+{
+	TextDef.ColTxt = (color & 0x00ffffffL);
+}
+
+void
+Text::MBtrack(MouseEvent *mev, anyOutput *o)
+{
+	if((mev->StateFlags &1) && IsInRect(&hcr, mev->x, mev->y) && parent) parent->Command(CMD_MARKOBJ, this, o);
+}
+
+InputText::InputText(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
+	:Dialog(par, desc, rec)
+{
+	RECT rc;
+
+	rc.left = rec.left+1;				rc.top = rec.top+1;
+	rc.right = rec.right-1;				rc.bottom = rec.bottom-1;
+	if(type == INCDECVAL1) cr.right = rc.right = cr.right - 7*xbase;
+	Line.color = 0x00000000L;
+	if(Text = new EditText(0L, text, -1, -1)) Text->SetRec(&rc);
+	Disp = 0L;
+}
+
+InputText::~InputText()
+{
+	if(Text) delete (Text);		Text = 0L;
+}
+
+bool
+InputText::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	bool bRet;
+
+	switch(cmd) {
+	case CMD_CURRLEFT:	case CMD_CURRIGHT:	case CMD_DELETE:
+	case CMD_POS_FIRST:	case CMD_POS_LAST:	case CMD_SHIFTLEFT:
+	case CMD_SHIFTRIGHT:
+		if(Text && bActive && !(flags & NOEDIT)){
+			if(o) Undo.SetDisp(o);
+			o->SetTextSpec(&TextDef);
+			bRet = Text->Command(cmd, o, NULL);
+			if(bRet && (flags & TOUCHEXIT)) parent->Command(CMD_ENDDIALOG, (void *)this, o);
+			return bRet;
+			}
+		break;
+	case CMD_SETFONT:	case CMD_SETSTYLE:
+		if(Text) return Text->Command(cmd, o, tmpl);
+		return false;
+	case CMD_UPDATE:
+		if(Text && bActive && o) Text->Command(cmd, o, 0L);
+		break;
+	case CMD_COPY:		case CMD_PASTE:
+		if(Text && bActive && !(flags & NOEDIT)) return  Text->Command(cmd, o, NULL);
+		return false;
+	case CMD_ADDCHAR:
+		if(Text && bActive && !(flags & NOEDIT)){
+			if(o) Undo.SetDisp(o);
+			o->SetTextSpec(&TextDef);
+			switch(*((int*)tmpl)) {
+			case 8: return Text->Command(CMD_BACKSP, o, NULL);	//Backspace
+			default: return Text->AddChar(*((int*)tmpl), o, 0L);
+				}
+			if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, o);
+			}
+		return false;
+	case CMD_SETTEXT:
+		if(Text) return Text->SetText((char*)tmpl);
+		break;
+		}
+	return false;
+}
+
+void
+InputText::DoPlot(anyOutput *o)
+{
+	POINT pts[5];
+
+	if(!o) return;
+	Disp = o;
+	pts[0].x = pts[0].y = 0;		//that means no caret with update(5)
+	o->SetTextSpec(&TextDef);
+	if(Text) Text->Update(bActive ? 5 : 2, o, &pts[0]);
+	o->SetLine(&Line);
+	pts[0].x = pts[1].x = pts[4].x = cr.left;
+	pts[0].y = pts[3].y = pts[4].y = cr.top;
+	pts[1].y = pts[2].y = cr.bottom-1;
+	pts[2].x = pts[3].x = cr.right-1;
+	o->oPolyline(pts, 5);
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+	if(parent && bActive){
+		parent->Command(CMD_TABDLG, this, o);
+		if(type == RANGEINPUT)o->MouseCapture(false);
+		}
+}
+
+bool
+InputText::Select(int x, int y, anyOutput *o)
+{
+	POINT p1;
+
+	p1.x = x;		p1.y = y;
+	if(bActive && IsInRect(&cr, x, y) && !(flags & NOEDIT)) {
+		DialogFocus = DialogTabStop = this;
+		if(parent) parent->Command(CMD_FOCTXT, (void*)this, 0L);
+		o->SetTextSpec(&TextDef);
+		if(Text) Text->Update(1, o, &p1); 
+		if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
+		}
+	return false;
+}
+
+bool
+InputText::GetValue(int id, double *val)
+{
+	if(Text && val) {
+		Text->Update(20, NULL, 0L);			//convert string to value
+		return Text->GetValue(val);
+		}
+	return false;
+}
+
+bool
+InputText::GetInt(int id, int *val)
+{
+	if(type == EDTEXT) {
+		if(Text) *val = Text->Cursor();
+		else *val = 0;
+		return true;
+		}
+	if(Text && Text->text) {
+#ifdef USE_WIN_SECURE
+		sscanf_s(Text->text, "%d", val);
+#else
+		sscanf(Text->text, "%d", val);
+#endif
+		return true;
+		}
+	return false;
+}
+
+bool
+InputText::GetText(int id, char *txt, int size)
+{
+	if(Text && Text->text && Text->text[0]) {
+		rlp_strcpy(txt, size, Text->text);
+		return true;
+		}
+	return false;
+}
+
+void
+InputText::MBtrack(MouseEvent *mev, anyOutput *o)
+{
+	bool bLBstate;
+	int x, y;
+
+	x = mev->x;						y = mev->y;
+	if(mev->StateFlags &1) bLBstate = true;
+	else bLBstate = false;
+	if(bActive && IsInRect(&cr, x, y) && !(flags & NOEDIT) && 
+		bLBstate && Text && parent) {
+		DialogFocus = this;
+		parent->Command(CMD_FOCTXT, (void*)this, 0L);
+		o->SetTextSpec(&TextDef);
+		Text->Command(CMD_MOUSE_EVENT, o, (DataObj *)mev);
+		parent->Command(CMD_MARKOBJ, this, o);
+		}
+}
+
+void
+InputText::Activate(int id, bool activate)
+{
+	bActive = activate;
+	if(Text && Disp) {
+		Disp->SetTextSpec(&TextDef);
+		if(bActive){
+			Text->Update(1, Disp, 0L);
+			DialogFocus = DialogTabStop = this;
+			if(parent) parent->Command(CMD_FOCTXT, this, Disp);
+			}
+		else Text->Update(2, Disp, 0L);
+		}
+}
+
+RangeInput::RangeInput(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text, DataObj *d)
+	:InputText(par, desc, rec, text)
+{
+	data = d;
+}
+
+RangeInput::~RangeInput()
+{
+	if(data) data->Command(CMD_ETRACC, 0L, 0L);
+	if(Text) delete (Text);		Text = 0L;
+}
+
+bool
+RangeInput::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch(cmd) {
+	case CMD_SET_DATAOBJ:
+		data = (DataObj*)tmpl;
+		return true;
+		}
+	return InputText::Command(cmd, tmpl, o);
+}
+
+bool
+RangeInput::Select(int x, int y, anyOutput *o)
+{
+	bool bRes;
+
+	bRes = InputText::Select(x, y, o);
+	if(bRes && data) {
+		if(DialogFocus == this){
+			data->Command(CMD_ETRACC, Text, 0L);
+			if(Text) Text->Update(1, o, 0L); 
+			}
+		else data->Command(CMD_ETRACC, 0L, 0L);
+		}
+	return bRes;
+}
+
+void
+RangeInput::Activate(int id, bool activate)
+{
+	InputText::Activate(id, activate);
+	if(activate && data) data->Command(CMD_ETRACC, Text, 0L);
+}
+
+InputValue::InputValue(tag_DlgObj *par, DlgInfo * desc, RECT rec, double *value)
+	:InputText(par, desc, rec, 0L)
+{
+	if(value) {
+		WriteNatFloatToBuff(TmpTxt, *value);
+		if(Text) Text->text = (char*)memdup(TmpTxt+1, (int)strlen(TmpTxt+1)+1, 0);
+		}
+	else if(Text) Text->SetText("");
+}
+
+InputValue::~InputValue()
+{
+	if(Text) delete (Text);
+	Text = 0L;					//in fact the destructor of InputText is also
+								//  called. This prevents a double delete of Text.
+}
+
+IncDecValue::IncDecValue(tag_DlgObj *par, DlgInfo * desc, RECT rec, double *value)
+	:InputText(par, desc, rec, 0L)
+{
+	int ab1 = 1, ab2 = 2;
+	static DlgInfo ab[] = {
+		{1, 2, 0, 0x0L, ARROWBUTT, (void*)&ab1, 0, 0, 0, 0},
+		{2, 0, 0, LASTOBJ, ARROWBUTT, (void*)&ab2, 0, 0, 0, 0}};
+	RECT br;
+
+	WriteNatFloatToBuff(TmpTxt, *value);
+	if(Text) Text->text = (char*)memdup(TmpTxt+1, (int)strlen(TmpTxt+1)+1, 0);
+	br.left = cr.right+1;		br.right = br.left + 7*xbase;
+	br.top = cr.top+1;			br.bottom = br.top + 5*ybase;
+	butts[0] = new ArrowButton(this, &ab[0], br, &ab1);
+	br.top += 5*ybase;			br.bottom += 5*ybase;
+	butts[1] = new ArrowButton(this, &ab[1], br, &ab2);
+	hasMinMax = hasStep = false;
+	theMin = theMax = theStep = 0.0;	
+}
+
+IncDecValue::~IncDecValue()
+{
+	if(Text) delete (Text);
+	if(butts[0]) delete (butts[0]);
+	if(butts[1]) delete (butts[1]);
+	Text = 0L;
+}
+
+bool
+IncDecValue::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	double tmpVal;
+
+	switch(cmd) {
+	case CMD_MINMAX:
+		if(tmpl) {
+			hasMinMax = true;
+			theMin = ((double*)tmpl)[0];
+			theMax = ((double*)tmpl)[1];
+			}
+		else hasMinMax = false;
+		return true;
+	case CMD_STEP:
+		if(tmpl) {
+			hasStep = true;
+			theStep = *((double*)tmpl);
+			}
+		else hasStep = false;
+		return true;
+	case CMD_ENDDIALOG:
+		if(GetValue(Id, &tmpVal)) {
+			if(hasStep && theStep > 0.0) {
+				tmpVal = floor(tmpVal/theStep)*theStep;
+				if(((Dialog*)tmpl)->Id == 1) tmpVal += theStep;
+				else tmpVal -= theStep;
+				}
+			else {
+				if(((Dialog*)tmpl)->Id == 1) tmpVal *= 1.2;
+				else tmpVal /= 1.2;
+				tmpVal = NiceValue(tmpVal);
+				}
+			if(hasMinMax) {
+				if(tmpVal < theMin) tmpVal = theMin;
+				if(tmpVal > theMax) tmpVal = theMax;
+				}
+			else if(!hasStep && fabs(tmpVal) < 0.0001) switch(defs.cUnits) {
+				case 1:		tmpVal = 0.01;	break;
+				case 2:		tmpVal = 0.004;	break;
+				default:	tmpVal = 0.1;	break;
+				}
+			WriteNatFloatToBuff(TmpTxt, tmpVal);
+			if(Text) {
+				Text->SetText(TmpTxt+1);				DoPlot(o);
+				}
+			}
+		return true;
+		}
+	return InputText::Command(cmd, tmpl, o);
+}
+
+void
+IncDecValue::DoPlot(anyOutput *o)
+{
+	InputText::DoPlot(o);
+	if(butts[0]) butts[0]->DoPlot(o);
+	if(butts[1]) butts[1]->DoPlot(o);
+}
+
+bool
+IncDecValue::Select(int x, int y, anyOutput *o)
+{
+	bool bRet = false;
+
+	if(x > cr.right) {
+		if(butts[0]) bRet = butts[0]->Select(x, y, o);
+		if(!bRet && butts[1]) bRet = butts[1]->Select(x, y, o);
+		}
+	else return InputText::Select(x, y, o);
+	if(bRet && parent) {
+		DialogFocus = DialogTabStop = 0L;
+		if(parent) parent->Command(CMD_FOCTXT, (void*)0L, 0L);
+		if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		}
+	return bRet;
+}
+
+bool
+IncDecValue::GetValue(int id, double *val)
+{
+	if(Text && val) {
+		Text->Update(20, NULL, 0L);			//convert string to value
+		if(Text->GetValue(val)) {
+			if(hasMinMax) {
+				if(*val < theMin) *val = theMin;
+				if(*val > theMax) *val = theMax;
+				}
+			return true;
+			}
+		}
+	return false;
+}
+
+void
+IncDecValue::MBtrack(MouseEvent *mev, anyOutput *o)
+{
+	if(mev->x >cr.right) {
+		if(butts[0]) butts[0]->MBtrack(mev, o);
+		if(butts[1]) butts[1]->MBtrack(mev, o);
+		}
+	else InputText::MBtrack(mev, o);
+}
+
+TxtHSP::TxtHSP(tag_DlgObj *par, DlgInfo *desc, RECT rec, int *align)
+	:Dialog(par, desc, rec)
+{
+	int x1 = cr.left/xbase,			x2 = ((cr.left>>1) + (cr.right>>1))/xbase-4,
+		x3 = cr.right/xbase-8,		y1 = cr.top/ybase,
+		y2 = ((cr.top>>1) + (cr.bottom>>1))/ybase-4,	y3 = cr.bottom/ybase-8;
+	int i;
+	RECT br;
+	DlgInfo d1[9];
+
+	for(i = 0; i < 9; i++) {
+		d1[i].id = i+1;			d1[i].next = i+2;			d1[i].first = 0;
+		d1[i].flags = ISRADIO;	d1[i].type = RADIO0;		d1[i].ptype = 0L;
+		d1[i].w = d1[i].h = 8;
+		switch (i / 3) {
+			case 0:		d1[i].y = y1;	break;
+			case 1:		d1[i].y = y2;	break;
+			case 2:		d1[i].y = y3;	break;
+			}
+		switch (i % 3) {
+			case 0:		d1[i].x = x1;	break;
+			case 1:		d1[i].x = x2;	break;
+			case 2:		d1[i].x = x3;	break;
+			}
+		}
+	d1[8].next = 0;			d1[8].flags |= LASTOBJ;
+	txt.ColTxt = 0x00808080L;	txt.ColBg = 0x00ffffff;
+	txt.fSize = defs.GetSize(SIZE_TEXT)*2.0;		
+	txt.RotBL = txt.RotCHAR = 0.0;
+	txt.iSize = 0;			txt.Align = TXA_HCENTER | TXA_VCENTER;
+	txt.Style = TXS_NORMAL;	txt.Font = FONT_HELVETICA;
+	txt.Mode = TXM_TRANSPARENT;
+	if(txt.text = (char*)malloc(20*sizeof(char))) rlp_strcpy(txt.text, 20, "text");
+	if((d2 = (DlgInfo*)malloc(9*sizeof(DlgInfo)))){
+		memcpy(d2, &d1,9*sizeof(DlgInfo));
+		if(align) switch(*align) {
+		case 1:		d2[1].flags |= CHECKED;		break;
+		case 2:		d2[2].flags |= CHECKED;		break;
+		case 4:		d2[3].flags |= CHECKED;		break;
+		case 5:		d2[4].flags |= CHECKED;		break;
+		case 6:		d2[5].flags |= CHECKED;		break;
+		case 8:		d2[6].flags |= CHECKED;		break;
+		case 9:		d2[7].flags |= CHECKED;		break;
+		case 10:	d2[8].flags |= CHECKED;		break;
+		default:	d2[0].flags |= CHECKED;		break;
+			}
+		for(i = 0; i < 9; i++) {
+			br.left = d2[i].x*xbase;
+			br.right = br.left + d2[i].w*xbase;
+			br.top = d2[i].y*ybase;
+			br.bottom = br.top + d2[i].h*ybase;
+			butts[i] = new RadioButton(this, &d2[i], br, 0L);
+			}
+		}
+}
+
+TxtHSP::~TxtHSP()
+{
+	int i;
+
+	if(txt.text) free(txt.text);
+	if(d2){
+		for(i = 0; i < 9; i++) if(butts[i]) delete(butts[i]);
+		free(d2);
+		}
+}
+
+bool
+TxtHSP::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	Dialog *d;
+
+	switch(cmd) {
+	case CMD_RADIOBUTT:
+		d = (Dialog *)tmpl;
+		for(i = 0; i < 9; i++) 
+			if(butts[i] && butts[i] != d && butts[i]->type == d->type)
+				butts[i]->SetCheck(0, o, false);
+		break;
+		}
+	return false;
+}
+
+void
+TxtHSP::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(txt.text){
+		o->SetTextSpec(&txt);
+		o->oTextOut((cr.left + cr.right)>>1, (cr.top + cr.bottom)>>1, txt.text, 0);
+		}
+	for(i = 0; i < 9; i++) if(butts[i]) butts[i]->DoPlot(o);
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+TxtHSP::Select(int x, int y, anyOutput *o)
+{
+	int i;
+	bool bRet = false;
+
+	for(i = 0; i < 9; i++) if(butts[i] && butts[i]->Select(x, y, o)) bRet = true;
+	return bRet;
+}
+
+bool
+TxtHSP::GetInt(int id, int *val)
+{
+	int i;
+	bool bRet = false;
+
+	for(i = 0; i < 9; i++) {
+		if(butts[i] && butts[i]->GetCheck(i+1)) {
+			bRet = true;
+			switch (i) {
+			case 0:		*val = 0;	break;
+			case 1:		*val = 1;	break;
+			case 2:		*val = 2;	break;
+			case 3:		*val = 4;	break;
+			case 4:		*val = 5;	break;
+			case 5:		*val = 6;	break;
+			case 6:		*val = 8;	break;
+			case 7:		*val = 9;	break;
+			case 8:		*val = 10;	break;
+				}
+			}
+		}
+	return bRet;
+}
+
+void
+TxtHSP::MBtrack(MouseEvent *mev, anyOutput *o)
+{
+	int i;
+
+	for(i = 0; i < 9; i++) if(butts[i]) butts[i]->MBtrack(mev, o);
+}
+
+SlideRect::SlideRect(tag_DlgObj *par, DlgInfo *desc, RECT rec, bool isVert)
+	:Dialog(par, desc, rec)
+{
+	bV = isVert;
+	if(isVert) {
+		buttrc.left = cr.left+1;	buttrc.top = cr.top;
+		buttrc.right = cr.right;	buttrc.bottom = cr.top + (w = h = (cr.right-cr.left));
+		}
+	else {
+		buttrc.left = cr.left;		buttrc.top = cr.top+1;
+		buttrc.right = cr.left+ (w = h = (cr.bottom - cr.top));	buttrc.bottom = cr.bottom;
+		}
+	dx = w>>1;		dy = h>>1;
+	sLine = 1;
+	puSel = pdSel = false;
+
+}
+
+SlideRect::~SlideRect()
+{
+}
+
+bool
+SlideRect::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	double tmp;
+
+	switch(cmd) {
+	case CMD_SETSCROLL:
+		//DEBUG: replace width/height calulation by variables w and h
+		tmp = *((double*)tmpl);
+		if(tmp < 0.0) tmp = 0.0;	if(tmp > 1.0) tmp = 1.0;
+		if(bV) {
+			i = (int)((double)(cr.bottom -cr.top -7*ybase)*tmp);
+			buttrc.bottom -= buttrc.top;
+			buttrc.top = i + cr.top;		buttrc.bottom += (i+cr.top);
+			}
+		else {
+			i = (int)((double)(cr.right -cr.left -7*xbase)*tmp);
+			buttrc.right -= buttrc.left;
+			buttrc.left = i + cr.left;		buttrc.right += (i+cr.left);
+			}
+		if(o)DoPlot(o);
+		return true;
+	case CMD_LINEUP:
+		i = -sLine;
+	case CMD_LINEDOWN:
+		if(cmd == CMD_LINEDOWN) i = sLine;
+		if(bV) {
+			buttrc.top += i;
+			if(buttrc.top < cr.top) buttrc.top = cr.top;
+			if((buttrc.top + h) > cr.bottom) buttrc.top = cr.bottom -h;
+			buttrc.bottom = buttrc.top + h;
+			}
+		else {
+			buttrc.left += i;
+			if(buttrc.left < cr.left) buttrc.left = cr.left;
+			if((buttrc.left + w) > cr.right) buttrc.left = cr.right -w;
+			buttrc.right = buttrc.left +w;
+			}
+		return true;
+		}
+	return false;
+}
+
+
+void
+SlideRect::DoPlot(anyOutput *o)
+{
+	POINT pts[3];
+
+	Line.color = DlgBGcolor;	Fill.color = 0x00e0e0e0L;
+	o->SetLine(&Line);			o->SetFill(&Fill);
+	o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
+	puRC.top = cr.top;		puRC.left = cr.left;
+	pdRC.right = cr.right;	pdRC.bottom = cr.bottom;
+	if(bV) {
+		puRC.bottom = buttrc.top;	puRC.right = cr.right;
+		pdRC.top = buttrc.bottom;	pdRC.left = cr.left;
+		}
+	else {
+		puRC.bottom = cr.bottom;	puRC.right = buttrc.left;
+		pdRC.top = cr.top;			pdRC.left = buttrc.right;
+		}
+	Fill.color = DlgBGhigh;
+	o->SetFill(&Fill);
+	if(bLBdown){
+		Line.color = 0x00808080L;		o->SetLine(&Line);
+		o->oRectangle(buttrc.left, buttrc.top, buttrc.right, buttrc.bottom);
+		}
+	else {
+		o->oRectangle(buttrc.left, buttrc.top, buttrc.right-1, buttrc.bottom-1);
+		Line.color = 0x0L;
+		o->SetLine(&Line);
+		pts[0].x = buttrc.left;					pts[0].y = pts[1].y = buttrc.bottom-1;
+		pts[1].x = pts[2].x = buttrc.right-1;	pts[2].y = buttrc.top-1;
+		o->oPolyline(pts, 3);
+		Line.color = 0x00ffffffL;
+		o->SetLine(&Line);
+		pts[0].x = pts[1].x = buttrc.left;
+		pts[0].y = buttrc.bottom -3;
+		pts[1].y = pts[2].y = buttrc.top;
+		pts[2].x = buttrc.right -2;
+		o->oPolyline(pts, 3);
+		}
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+	puSel = pdSel = false;
+}
+
+bool
+SlideRect::Select(int x, int y, anyOutput *o)
+{
+	if(IsInRect(&buttrc, x, y)) {
+		bLBdown = false;
+		DoPlot(o);
+		return true;
+		}
+	else if(bLBdown || puSel || pdSel) {
+		bLBdown = false;
+		DoPlot(o);
+		}
+	if(IsInRect(&puRC, x, y) && parent) parent->Command(CMD_PAGEUP, 0L, o);
+	else if(IsInRect(&pdRC, x, y) && parent) parent->Command(CMD_PAGEDOWN, 0L, o);
+	return false;
+}
+
+bool
+SlideRect::GetValue(int id, double *val)
+{
+	double res;
+
+	if(bV) {
+		res = ((double)(buttrc.top - cr.top))/
+			((double)(cr.bottom-cr.top-(cr.right-cr.left)));
+		}
+	else {
+		res = ((double)(buttrc.left - cr.left))/
+			((double)(cr.right-cr.left-(cr.bottom-cr.top)));
+		}
+	*val = res;
+	return true;
+}
+
+void
+SlideRect::MBtrack(MouseEvent *mev, anyOutput *o)
+{
+	bool bLBstate = false;
+
+	if(mev->Action == MOUSE_LBDOUBLECLICK) {
+		bLBdown = false;
+		DoPlot(o);
+		return;
+		}
+	if(mev->StateFlags &1) bLBstate = true;
+	if(IsInRect(&buttrc, mev->x, mev->y)){
+		if(mev->Action == MOUSE_LBDOWN){
+			dx = mev->x-buttrc.left;	dy = mev->y - buttrc.top;
+			}
+		if(bLBstate && !bLBdown){
+			bLBdown = bLBstate;
+			DoPlot(o);
+			return;
+			}
+		}
+	if(IsInRect(&puRC, mev->x, mev->y)){
+		if(pdSel) DoPlot(o);
+		Line.color = Fill.color = bLBstate ? DlgBGcolor : 0x00e0e0e0L;
+		o->SetLine(&Line);			o->SetFill(&Fill);
+		o->oRectangle(puRC.left+3, puRC.top+3, puRC.right-3, puRC.bottom-3);
+		o->UpdateRect(&puRC, false);
+		puSel = true;
+		}
+	else if(puSel) DoPlot(o);
+	if(IsInRect(&pdRC, mev->x, mev->y)){
+		if(puSel) DoPlot(o);
+		Line.color = Fill.color = bLBstate ? DlgBGcolor : 0x00e0e0e0L;
+		o->SetLine(&Line);			o->SetFill(&Fill);
+		o->oRectangle(pdRC.left+3, pdRC.top+3, pdRC.right-3, pdRC.bottom-3);
+		o->UpdateRect(&pdRC, false);
+		pdSel = true;
+		}
+	else if(pdSel) DoPlot(o);
+	if(bLBdown && IsInRect(&cr, mev->x, mev->y)){
+		if(bV) {
+			buttrc.top = mev->y - dy;
+			if(buttrc.top < cr.top) buttrc.top = cr.top;
+			if((buttrc.top + h) > cr.bottom) buttrc.top = cr.bottom -h;
+			buttrc.bottom = buttrc.top + h;
+			}
+		else {
+			buttrc.left = mev->x - dx;
+			if(buttrc.left < cr.left) buttrc.left = cr.left;
+			if((buttrc.left + w) > cr.right) buttrc.left = cr.right -w;
+			buttrc.right = buttrc.left +w;
+			}
+		if(parent && ((Dialog*)parent)->parent && 
+			((Dialog*)parent)->parent->Command(CMD_REDRAW, 0L, o));
+		else DoPlot(o);
+		}
+	else if(bLBdown){
+		bLBdown = false;
+		DoPlot(o);
+		}
+}
+
+ScrollBar::ScrollBar(tag_DlgObj *par, DlgInfo *desc, RECT rec, bool isVert)
+	:Dialog(par, desc, rec)
+{
+	int ab1 = 1, ab2 = 2, ab3 = 3, ab4 = 4;
+	static DlgInfo ab[] = {
+		{1, 2, 0, 0x0L, ARROWBUTT, (void*)0L, 0, 0, 0, 0},
+		{2, 0, 0, 0x0L, ARROWBUTT, (void*)0L, 0, 0, 0, 0},
+		{3, 0, 0, LASTOBJ, NONE, 0L, 0, 0, 0, 0}};
+	RECT br, sr;
+
+	if(isVert) {
+		sr.left = cr.left;		sr.right = cr.right;
+		br.left = cr.left+1;	br.right = cr.right+1;
+		br.top = cr.top+1;		br.bottom = sr.top = br.top + 5*ybase + 1;
+		sr.top--;	hcr.top = sr.top;
+		butts[0] = new ArrowButton(this, &ab[0], br, &ab1);
+		br.top = cr.bottom-5*ybase;		br.bottom = cr.bottom+1;
+		butts[1] = new ArrowButton(this, &ab[1], br, &ab2);
+		br.bottom -= 5*ybase;	br.top -= 5*ybase;
+		hcr.bottom = sr.bottom = br.top;
+		butts[2] = new ArrowButton(this, &ab[0], br, &ab1);
+		}
+	else {
+		sr.top = cr.top;		sr.bottom = cr.bottom;
+		br.left = cr.left+1;	br.right = sr.left = br.left + 5*xbase;
+		sr.left--;	hcr.left = sr.left;
+		br.top = cr.top+1;		br.bottom = cr.bottom+1;
+		butts[0] = new ArrowButton(this, &ab[0], br, &ab3);
+		br.left = cr.right - 5*xbase;	br.right = cr.right+1;
+		butts[1] = new ArrowButton(this, &ab[1], br, &ab4);
+		br.left -= 5*xbase;		br.right -= 5*xbase;
+		hcr.right = sr.right = br.left;
+		butts[2] = new ArrowButton(this, &ab[0], br, &ab3);
+		}
+	slrc = new SlideRect(this, &ab[2], sr, isVert);
+	sLine = 1;		sPage = 8;
+}
+
+ScrollBar::~ScrollBar()
+{
+	int i;
+
+	for(i = 0; i < 3; i++) if(butts[i]) delete butts[i];
+	if(slrc) delete slrc;
+}
+
+bool
+ScrollBar::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+
+	switch(cmd) {
+	case CMD_ENDDIALOG:
+		if(!tmpl || !slrc) return false;
+		i = ((Dialog*)tmpl)->Id;
+		switch(i) {
+		case 1:	return slrc->Command(CMD_LINEUP, 0L, o);
+		case 2:	return slrc->Command(CMD_LINEDOWN, 0L, o);
+		default:	return false;
+			}
+	case CMD_PAGEUP:
+	case CMD_PAGEDOWN:
+		if(parent)return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_SETSCROLL:
+		if(slrc) return slrc->Command(cmd, tmpl, o);
+		break;
+		}
+	return false;
+}
+
+void
+ScrollBar::DoPlot(anyOutput *o)
+{
+	int i;
+
+	for(i = 0; i < 3; i++) if(butts[i]) butts[i]->DoPlot(o);
+	if(slrc){
+		slrc->sLine = sLine;
+		slrc->DoPlot(o);
+		}
+}
+
+bool
+ScrollBar::Select(int x, int y, anyOutput *o)
+{
+	int i;
+	bool bRet = false;
+
+	if(!IsInRect(&cr, x, y)) return false;
+	for(i = 0; i < 3; i++) if(butts[i] && butts[i]->Select(x, y, o)) bRet = true;
+	if(!bRet && slrc) bRet = slrc->Select(x, y, o);
+	if(bRet && parent) parent->Command(CMD_REDRAW, 0L, o);
+	return bRet;
+}
+
+bool
+ScrollBar::GetValue(int id, double *val)
+{
+	if(slrc) return slrc->GetValue(id, val);
+	return false;
+}
+
+void
+ScrollBar::MBtrack(MouseEvent *mev, anyOutput *o)
+{
+	int i;
+
+	for(i = 0; i < 3; i++) if(butts[i]) butts[i]->MBtrack(mev, o);
+	if(slrc) slrc->MBtrack(mev, o);
+}
+
+Icon::Icon(tag_DlgObj *par, DlgInfo * desc, RECT rec, int *ico)
+	:Dialog(par, desc, rec)
+{
+	icon = ico ? *ico : 0;
+}
+
+void
+Icon::DoPlot(anyOutput *o)
+{
+	o->oDrawIcon(icon, cr.left, cr.top);
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+Group::Group(tag_DlgObj *par, DlgInfo *desc, RECT rec)
+	:Dialog(par, desc, rec)
+{
+	numChildren = 5;
+	Children = (Dialog **)calloc(numChildren, sizeof(Dialog *));
+	TextFocus = 0L;
+}
+
+Group::~Group()
+{
+	//pointers to child objects are allocated and freed by parent:
+	//  pointers are copies and we do not care further;
+	if(Children) free(Children);
+}
+
+bool
+Group::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	Dialog *d, **tmp;
+
+	int i;
+
+	switch (cmd) {
+	case CMD_FLUSH:
+		if(Children) for(i = 0; i < numChildren; Children[i++] = 0L);
+		break;
+	case CMD_CONTINUE:		case CMD_ENDDIALOG:		case CMD_TABDLG:
+	case CMD_NOTABDLG:		case CMD_MARKOBJ:
+		if(parent && (flags & HIDDEN) != HIDDEN) return parent->Command(cmd, tmpl, o);
+		else return false;
+	case CMD_ADDCHILD:
+		if(Children && numChildren && (d = (Dialog*)tmpl)) {
+			d->parent = this;
+			if(!TextFocus &&(d->type == EDTEXT || d->type == RANGEINPUT ||
+				d->type == INCDECVAL1 || d->type == EDVAL1))
+				TextFocus = ((InputText*)tmpl)->bActive ? (InputText*)tmpl: 0L ;
+			for(i = 0; i < numChildren; i++) {
+				if(!Children[i] || Children[i] == d) {
+					Children[i] = d;
+					return true;
+					}
+				}
+			//if we come here the list for children is too short
+			tmp = (Dialog **)realloc(Children, (numChildren+1) * sizeof(Dialog *));
+			if(!tmp) return false;
+			Children = tmp;
+			Children[numChildren++] = d;
+			return true;
+			}
+		break;
+	case CMD_RADIOBUTT:
+		if(Children && numChildren) {
+			d = (Dialog *)tmpl;
+			for(i = 0; i < numChildren; i++) {
+				if(Children[i] && Children[i] != d && Children[i]->type == d->type &&
+					Children[i]->bChecked) {
+					Children[i]->bChecked = false;
+					Children[i]->DoPlot(o);
+					}
+				}
+			}
+		break;
+	case CMD_FOCTXT:
+		DialogTabStop = TextFocus = (InputText *)tmpl;
+		break;
+		}
+	return false;
+}
+
+void
+Group::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(flags & HIDDEN) return;
+	if(Children && numChildren && bChecked) {
+		for(i = 0; i < numChildren; i++) {
+			if(Children[i] && !(Children[i]->flags & HIDDEN)) {
+				Children[i]->DoPlot(o);
+				if(Children[i] == (Dialog*)TextFocus && DialogFocus != DialogTabStop)
+					TextFocus->Activate(TextFocus->Id, true);
+				}
+			}
+		}
+	else if(Children && numChildren && parent) {
+		for(i = 0; i < numChildren; i++) {
+			if(Children[i]) parent->Command(CMD_NOTABDLG, Children[i], o); 
+			}
+		}
+}
+
+bool
+Group::Select(int x, int y, anyOutput *o)
+{
+	int i;
+
+	if(Children && numChildren) {
+		for(i = 0; i < numChildren; i++) {
+			if(Children[i] && !(Children[i]->flags &HIDDEN) && Children[i]->Select(x, y, o)) 
+				return (bChecked = true);
+			}
+		}
+	return false;
+}
+
+void
+Group::MBtrack(MouseEvent *mev, anyOutput *o)
+{
+	int i;
+
+	if(bChecked && Children && numChildren) {
+		for(i = 0; i < numChildren; i++) {
+			if(Children[i]&& !(Children[i]->flags &HIDDEN)) Children[i]->MBtrack(mev, o);
+			}
+		}
+}
+
+GroupBox::GroupBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *txt)
+	:Group(par,desc, rec)
+{
+	if(txt && txt[0])Text = (char*)memdup(txt, (int)strlen(txt)+1, 0);
+	else Text = 0L;
+	Line.color = 0x00000000L;					Line.width = 0.0;
+	Fill.color = DlgBGhigh;						TextDef.ColBg = Fill.color;
+	TextDef.Align = TXA_HLEFT | TXA_VCENTER;	TextDef.Mode = TXM_OPAQUE;
+}
+
+GroupBox::~GroupBox()
+{
+	if(Text) free(Text);
+}
+
+void
+GroupBox::DoPlot(anyOutput *o)
+{
+	o->SetLine(&Line);
+	o->SetFill(&Fill);
+	o->oRectangle(cr.left, cr.top, cr.right-1, cr.bottom-1);
+	if(Text) {
+		o->SetTextSpec(&TextDef);
+		hcr.top = cr.top - TextDef.iSize;
+		o->oTextOut(cr.left+4, cr.top, Text, 0);
+		}
+	Group::DoPlot(o);
+	defs.UpdRect(o, hcr.left, hcr.top, hcr.right, hcr.bottom);
+}
+
+TabSheet::TabSheet(tag_DlgObj *par, DlgInfo * desc, RECT rec, TabSHEET * sh, DataObj *d)
+	:Group(par, desc, rec)
+{
+	if(sh->text && sh->text[0])Text = (char*)memdup(sh->text, (int)strlen(sh->text)+1, 0);
+	else Text = 0L;
+	rctab.left = cr.left + sh->x1 * xbase;			rctab.right = cr.left + sh->x2 * xbase;
+	rctab.top = cr.top;								rctab.bottom = cr.top + sh->height * ybase;
+	TextDef.Align = TXA_HRIGHT | TXA_VTOP;			flags |= ISRADIO;
+	data = d;
+}
+
+TabSheet::~TabSheet()
+{
+	if(Text) free (Text);
+}
+
+void
+TabSheet::DoPlot(anyOutput *o)
+{
+	POINT pts[6];
+
+	pts[0].x = pts[1].x = pts[5].x = rctab.left;
+	pts[0].y = pts[4].y = pts[5].y = rctab.bottom;
+	pts[1].y = (rctab.top + rctab.bottom)/2;
+	pts[2].x = rctab.left + (rctab.bottom - rctab.top)/2;
+	pts[2].y = pts[3].y = rctab.top;
+	pts[3].x = pts[4].x = rctab.right-1;
+	HideCopyMark();
+	Line.color = 0x0L;				Line.width = 0.0;
+	Fill.color = bChecked ? DlgBGhigh : DlgBGcolor;
+	o->SetLine(&Line);				o->SetFill(&Fill);
+	o->oPolygon(pts, 6);
+	if(bChecked) {
+		o->oRectangle(cr.left, rctab.bottom, cr.right, cr.bottom);
+		Line.color = DlgBGhigh;
+		o->SetLine(&Line);
+		pts[4].x--;
+		o->oSolidLine(pts+4);
+		}
+	o->SetTextSpec(&TextDef);
+#ifdef _WINDOWS
+	o->oTextOut(rctab.right - 6, rctab.top + 3, Text, 0);
+#else
+	o->oTextOut(rctab.right - 6, rctab.top + 5, Text, 0);
+	if(bChecked) {
+		Line.color = 0x0L;			o->SetLine(&Line);
+		pts[0].y++;					o->oSolidLine(pts);
+		}
+#endif
+	Group::DoPlot(o);
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+TabSheet::Select(int x, int y, anyOutput *o)
+{
+	if(IsInRect(&rctab, x, y)) {
+		if(data)data->Command(CMD_ETRACC, 0L, 0L);
+		if(parent) {
+			parent->Command(CMD_RADIOBUTT, (void *)this, o);
+			parent->Command(CMD_MARKOBJ, this, o);
+			}
+		bChecked = true;		DoPlot(o);
+		if((flags & TOUCHEXIT) && parent) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
+		}
+	if(bChecked) return Group::Select(x, y, o);
+	return false;
+}
+
+ODbutton::ODbutton(tag_DlgObj *par, DlgInfo * desc, RECT rec, void *proc)
+	:Dialog(par, desc, rec)
+{
+	ODexec = (void(*)(int, void *, RECT *, anyOutput *, void *, int))proc;
+	if(ODexec) (*ODexec)(OD_CREATE, parent, &cr, 0L, 0L, Id);
+}
+
+ODbutton::~ODbutton()
+{
+	if(ODexec) (*ODexec)(OD_DELETE, (void*)parent, &cr, 0L, 0L, Id);
+}
+
+void 
+ODbutton::DoPlot(anyOutput *o)
+{
+	if(ODexec) (*ODexec)(bChecked ? OD_DRAWSELECTED : OD_DRAWNORMAL, (void*)parent, &cr, o, 0L, Id);
+}
+
+bool
+ODbutton::Select(int x, int y, anyOutput *o)
+{
+	POINT p;
+
+	if(!(flags & HIDDEN) && IsInRect(&cr, x, y)) {
+		if(flags & ISRADIO) bChecked = true;
+		else bChecked = bChecked ? false : true;
+		if(flags & NOSELECT) bChecked = false;
+		bLBdown = false;
+		p.x = x;			p.y = y;
+		if(ODexec) {
+			(*ODexec)(OD_SELECT, (void*)parent, &cr, o, (void*)&p, Id);
+			if(!(flags & NOSELECT))DoPlot(o);
+			}
+		if((flags & ISRADIO) && parent)
+			parent->Command(CMD_RADIOBUTT, (void *)this, o);
+		if((flags & TOUCHEXIT) && parent) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
+		}
+	return false;
+}
+
+void
+ODbutton::MBtrack(MouseEvent *mev, anyOutput *o)
+{
+	int x = mev->x, y = mev->y;
+
+	if(mev->Action == MOUSE_LBDOUBLECLICK) {
+		Select(mev->x, mev->y, o);
+		return;
+		}
+	if(!(flags & HIDDEN) && IsInRect(&cr, x, y) && ODexec) 
+			(*ODexec)(OD_MBTRACK, (void*)parent, &cr, o, mev, Id);
+}
+
+Listbox::Listbox(tag_DlgObj *par, DlgInfo *des, RECT rec, char **list)
+	:Dialog(par, des, rec)
+{
+	static DlgInfo sbd[] = {
+		{1, 2, 0, 0x0L, VSCROLL, 0L, 0, 0, 0, 0}};
+	RECT sr;
+	int i;
+
+	sbd[0].x = (sr.left = (cr.right-7*xbase))/xbase;
+	sbd[0].y = (sr.top = cr.top)/ybase;		sr.right = cr.right;	sbd[0].w = 7;							
+	sbd[0].h = ((sr.bottom = cr.bottom) - cr.top)/ybase;
+	sb = new ScrollBar(this, &sbd[0], sr, true);
+	bmp = 0L;		cl = bmh = 0;		hcr.right -= 7*xbase;
+	Fill.color = 0x00ffffffL;			Line.color = 0x00000000L;
+	for(i = ns = 0; list && list[i]; i++);	//count lines
+	if(strings = (char **)calloc(i+1, sizeof(char*))){
+		for(ns = i, i = 0; i < ns; i++){
+			strings[i] = (char*)memdup(list[i], (int)strlen(list[i])+1, 0);
+			}
+		}
+}
+
+Listbox::~Listbox()
+{
+	int i;
+
+	if(sb) delete(sb);
+	if(bmp) DelBitmapClass(bmp);
+	if(strings) {
+		for(i = 0; i < ns; i++) if(strings[i])free(strings[i]);
+		free(strings);
+		}
+}
+
+bool
+Listbox::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	double ps;
+	char *txt;
+
+	ps = ((double)(cr.bottom - cr.top))/((double)(bmh+TextDef.iSize+_SBINC));
+	switch(cmd) {
+	case CMD_REDRAW:
+		DoPlot(o);
+		return true;
+	case CMD_PAGEUP:
+		ps *= -1.0;
+	case CMD_PAGEDOWN:
+		if(sb && sb->GetValue(1, &spos)){
+			spos += ps;
+			sb->Command(CMD_SETSCROLL, (void*)&spos, o);
+			DoPlot(o);
+			}
+		return true;
+	case CMD_SETSCROLL:
+		if(sb) return sb->Command(cmd, tmpl, o);
+		break;
+	case CMD_FINDTEXT:
+		txt = (char*)tmpl;
+		if(strings) for (i = 0; i < ns; i++) {
+			if(strings[i] && 0 == strcmp(txt, strings[i])){
+				cl = i;
+				return true;
+				}
+			}
+		return false;
+		}
+	return false;
+}
+
+void
+Listbox::DoPlot(anyOutput *o)
+{
+	RECT mrk;
+
+	if(!ns) return;
+	if(!bmp && !CreateBitMap(o))return;
+	startY = 0;
+	if(sb && sb->GetValue(1, &spos)) startY = (int)(spos*(double)bmh);
+	o->SetLine(&Line);			o->SetFill(&Fill);
+	o->oRectangle(hcr.left, hcr.top, hcr.right+1, hcr.bottom);
+	if(sb){
+		sb->sLine = 1+(sb->hcr.bottom-sb->hcr.top-7*ybase)/ns;
+		sb->DoPlot(o);
+		}
+	o->CopyBitmap(hcr.left+1, hcr.top+1, bmp, 0, startY, (hcr.right-hcr.left)-2, 
+		(hcr.bottom-hcr.top)-2, false); 
+	mrk.left = hcr.left+2;		mrk.top = (cl)*(TextDef.iSize+_SBINC) - startY + hcr.top;
+	mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
+	if(mrk.bottom > (hcr.top+1) && mrk.top < (hcr.bottom-1)){
+		if(mrk.top < (hcr.top+1)) mrk.top = hcr.top+1;
+		if(mrk.bottom > (hcr.bottom-1)) mrk.bottom = hcr.bottom-1;
+		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, cl*(TextDef.iSize+_SBINC), 
+			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true);
+		}
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+Listbox::Select(int x, int y, anyOutput *o)
+{
+	int il;
+	RECT mrk;
+
+	if(IsInRect(&hcr, x, y)) {
+		il = (y+startY-hcr.top)/(TextDef.iSize+_SBINC);
+		if(il >= ns || il < 0){
+			o->UpdateRect(&hcr, false);
+			return false;
+			}
+		cl = il;
+		mrk.left = hcr.left+2;		mrk.top = (il)*(TextDef.iSize+_SBINC) - startY + hcr.top;
+		mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
+		o->CopyBitmap(hcr.left+1, hcr.top+1, bmp, 0, startY, (hcr.right-hcr.left)-2, 
+			(hcr.bottom-hcr.top)-2, false); 
+		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, (cl)*(TextDef.iSize+_SBINC), 
+			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true); 
+		o->UpdateRect(&hcr, false);
+		if((flags & TOUCHEXIT) && parent) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
+		}
+	if(sb)return sb->Select(x, y, o);
+	return false;
+}
+
+bool
+Listbox::GetInt(int id, int *val)
+{
+	*val = cl;
+	return true;
+}
+
+bool
+Listbox::GetText(int id, char *txt, int size)
+{
+	if(strings && ns && cl >= 0 && cl < ns){
+		rlp_strcpy(txt, size, strings[cl]);
+		return true;
+		}
+	return false;
+}
+
+void
+Listbox::MBtrack(MouseEvent *mev, anyOutput *o)
+{
+	if(sb) sb->MBtrack(mev, o);
+}
+
+bool
+Listbox::CreateBitMap(anyOutput *tmpl)
+{
+	int i, w, h;
+
+	if(bmp) DelBitmapClass(bmp);
+	bmp = 0L;
+	if(tmpl && strings) {
+		h = (TextDef.iSize+_SBINC) * ns;		w = (hcr.right - hcr.left);
+		if(bmp = NewBitmapClass(w, h, tmpl->hres, tmpl->vres)){
+			bmp->Erase(0x00ffffffL);
+			bmp->SetTextSpec(&TextDef);
+			bmh = h - (TextDef.iSize+_SBINC);
+			for(i = 0; i < ns; i++) {
+				bmp->oTextOut(5, i*(TextDef.iSize+_SBINC), strings[i], 0);
+				}
+			return true;
+			}
+		}
+	return false;
+}
+
+Treeview::Treeview(tag_DlgObj *par, DlgInfo *des, RECT rec, GraphObj *g)
+	:Dialog(par, des, rec)
+{
+	static DlgInfo sbd[] = {
+		{1, 2, 0, 0x0L, VSCROLL, 0L, 0, 0, 0, 0}};
+	RECT sr;
+
+	sbd[0].x = (sr.left = (cr.right-7*xbase))/xbase;
+	sbd[0].y = (sr.top = cr.top)/ybase;		sr.right = cr.right;	sbd[0].w = 7;							
+	sbd[0].h = ((sr.bottom = cr.bottom) - cr.top)/ybase;
+	sb = new ScrollBar(this, &sbd[0], sr, true);
+	Fill.color = 0x00ffffffL;			Line.color = 0x00000000L;
+	bmp = 0L;		startY = cl = 0;	hcr.right -= 7*xbase;
+	bmh = hcr.bottom - hcr.top;			bmw = hcr.right - hcr.left;	
+	ot = new ObjTree(0L, 0L, go = g);
+	if(ot) ot->Command(CMD_TEXTDEF, &TextDef, 0L);
+}
+
+Treeview::~Treeview()
+{
+	if(sb) delete(sb);				sb = 0L;
+	if(ot) delete(ot);				ot = 0L;
+	if(bmp) DelBitmapClass(bmp);	bmp = 0L;
+}
+
+bool
+Treeview::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	double ps;
+
+	ps = ((double)(cr.bottom - cr.top))/((double)(bmh+TextDef.iSize+_SBINC));
+	switch(cmd) {
+	case CMD_LAYERS:
+		ot->Command(cmd, tmpl, o);
+	case CMD_UPDATE:
+		if(bmp) DelBitmapClass(bmp);
+		bmh = hcr.bottom - hcr.top;			bmw = hcr.right - hcr.left;	
+		bmp = ot->CreateBitmap(&bmw, &bmh, o);
+		return true;
+	case CMD_REDRAW:
+		DoPlot(o);
+		return true;
+	case CMD_OBJTREE:
+		if(tmpl) *((ObjTree**)tmpl) = ot;
+		return true;
+	case CMD_PAGEUP:
+		ps *= -1.0;
+	case CMD_PAGEDOWN:
+		if(sb && sb->GetValue(1, &spos)){
+			spos += ps;
+			sb->Command(CMD_SETSCROLL, (void*)&spos, o);
+			DoPlot(o);
+			}
+		return true;
+	case CMD_SETSCROLL:
+		if(sb) return sb->Command(cmd, tmpl, o);
+		break;
+		}
+	return false;
+}
+
+void
+Treeview::DoPlot(anyOutput *o)
+{
+	RECT mrk;
+
+	if(!o || !ot) return;
+	ot->Command(CMD_TEXTDEF, &TextDef, 0L);
+	ns = ot->count_lines();
+	if(bmp) ot->DoPlot(bmp);
+	else if(!(bmp = ot->CreateBitmap(&bmw, &bmh, o)))return;
+	startY = 0;
+	if(sb && sb->GetValue(1, &spos)) startY = (int)(spos*(double)bmh);
+	o->SetLine(&Line);			o->SetFill(&Fill);
+	o->oRectangle(hcr.left, hcr.top, hcr.right+1, hcr.bottom);
+	if(sb){
+		sb->sLine = 1+(sb->hcr.bottom-sb->hcr.top-7*ybase)/ns;
+		sb->DoPlot(o);
+		}
+	o->CopyBitmap(hcr.left+1, hcr.top+1, bmp, 0, startY, (hcr.right-hcr.left)-2, 
+		(hcr.bottom-hcr.top)-2, false); 
+	mrk.left = hcr.left+2;		mrk.top = (cl)*(TextDef.iSize+_SBINC) - startY + hcr.top;
+	mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
+	if(mrk.bottom > (hcr.top+1) && mrk.top < (hcr.bottom-1)){
+		if(mrk.top < (hcr.top+1)) mrk.top = hcr.top+1;
+		if(mrk.bottom > (hcr.bottom-1)) mrk.bottom = hcr.bottom-1;
+		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, cl*(TextDef.iSize+_SBINC), 
+			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true);
+		}
+	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
+}
+
+bool
+Treeview::Select(int x, int y, anyOutput *o)
+{
+	int il;
+	RECT mrk;
+
+	if(IsInRect(&hcr, x, y)) {
+		il = (y+startY-hcr.top)/(TextDef.iSize+_SBINC);
+		if(il >= ns || il < 0){
+			o->UpdateRect(&hcr, false);
+			return false;
+			}
+		cl = il;
+		mrk.left = hcr.left+2;		mrk.top = (il)*(TextDef.iSize+_SBINC) - startY + hcr.top;
+		mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
+		o->CopyBitmap(hcr.left+1, hcr.top+1, bmp, 0, startY, (hcr.right-hcr.left)-2, 
+			(hcr.bottom-hcr.top)-2, false); 
+		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, (cl)*(TextDef.iSize+_SBINC), 
+			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true); 
+		o->UpdateRect(&hcr, false);
+		if((flags & TOUCHEXIT) && parent) 
+			parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
+		}
+	if(sb)return sb->Select(x, y, o);
+	return false;
+}
+
+void
+Treeview::MBtrack(MouseEvent *mev, anyOutput *o)
+{
+	if(sb) sb->MBtrack(mev, o);
+}
+
+bool
+Treeview::GetInt(int id, int *val)
+{
+	*val = cl;
+	return true;
+}
+
+LinePat::LinePat(tag_DlgObj *par, DlgInfo * desc, RECT rec, LineDEF *line)
+	:Dialog(par, desc, rec)
+{
+	pPattern = &line->pattern;
+	Line.color = 0x00000000L;
+	bDraw = true;
+	cr.right = cr.left + (cr.bottom-cr.top)*32;
+}
+
+void
+LinePat::DoPlot(anyOutput *o)
+{
+	int i, h;
+	POINT ruler[2];
+	RECT gr;
+
+	h = cr.bottom-cr.top;
+	memcpy(&gr, &cr, sizeof(RECT));
+	o->SetLine(&Line);
+	for(i = 0; i < 32; i++) {
+		if(*pPattern &(1<<i)) Fill.color = 0x00ffffffL;
+		else Fill.color = 0x00808080L;
+		o->SetFill(&Fill);
+		o->oRectangle(cr.left+i*h, cr.top, cr.left+i*h+h, cr.top+h);
+		}
+	ruler[0].y = cr.top+h;
+	ruler[1].y = ruler[0].y + h/2;
+	for(i = 0; i <=4; i++) {
+		ruler[0].x = ruler[1].x = cr.left+i*8*h+(i ? -1:0);
+		o->oSolidLine(ruler);
+		}
+	gr.bottom += h;
+	defs.UpdRect(o, gr.left, gr.top, gr.right, gr.bottom);
+}
+
+void
+LinePat::MBtrack(MouseEvent *mev, anyOutput *o)
+{
+	int i, x, y;
+	DWORD mask, oldpat;
+
+	if(!(mev->StateFlags &1))return;	//draw with mouse left button
+	x = mev->x;						y = mev->y;
+	if(x > cr.left && x < cr.right && y > cr.top && y < cr.bottom){
+		i = (x-cr.left)/(cr.bottom-cr.top);
+		oldpat = *pPattern;
+		mask = (1<<i);
+		//use first hit (button down) to select color, true or false
+		bDraw = mev->Action == MOUSE_LBDOWN ? 0L != (*pPattern & mask) : bDraw;
+		if(bDraw) *pPattern &= ~mask;
+		else *pPattern |= mask;
+		if(oldpat != *pPattern) {
+			DoPlot(o);
+			if((flags & TOUCHEXIT) && parent) parent->Command(CMD_ENDDIALOG, (void *)this, o);
+			}
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Common code for multiple range dialogs
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int com_StackDlg(int res, DlgRoot *Dlg, AccRange **rX, int *nx, char ***rd, int *currYR,
+	AccRange **rY, bool *bContinue, int *ny, int *maxYR, bool *updateYR)
+{
+	char **tmprd;
+
+	switch (res) {
+		case 1:
+			if(rX && nx && Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0] && 
+				(*rX = new AccRange(TmpTxt))) *nx = rX[0]->CountItems();
+			else if(nx) *nx = 0;
+			if(Dlg->GetText(154, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0]) {
+				if(rd[0][*currYR]) free(rd[0][*currYR]);
+				rd[0][*currYR] = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
+				}
+			break;
+		case 155:
+			res = -1;
+			*ny = 0;
+			if(rX) {
+				if(!(*currYR) && Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0]) {
+					if(*rX = new AccRange(TmpTxt)){
+						*nx = rX[0]->CountItems();
+						delete *rX;						*rX = 0L;
+						}
+					}
+				if(!(*nx)) {
+					ErrorBox("X-range is empty\nor not valid!\n\nEnter a valid range\n"
+						"for common x-values.");
+					*bContinue = true;
+					break;
+					}
+				}
+			if(Dlg->GetText(154, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0]) {
+				if(*rY = new AccRange(TmpTxt)){
+					*ny = rY[0]->CountItems();
+					delete *rY;
+					*rY = 0L;
+					}
+				}
+			if(!(*ny)) {
+				ErrorBox("Y-range is empty\nor not valid!\n\nEnter a valid range\n"
+					"for y-values with the same\nsize as the x-range.");
+				*bContinue = true;
+				break;
+				}
+			if((*currYR)+1 > *maxYR) {
+				tmprd = (char**)realloc(*rd, sizeof(char*)*((*currYR)+2));
+				if(tmprd) *rd = tmprd;
+				else break;
+				*maxYR = (*currYR)+1;
+				rd[0][*currYR] = 0L;
+				rd[0][(*currYR)+1] = 0L;
+				}
+			if(rd[0][*currYR]) free(rd[0][*currYR]);
+			rd[0][*currYR] = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);	//store y-ranges
+			*updateYR = true;
+			(*currYR)++;
+			Dlg->SetText(154, rd[0][*currYR]);
+			Dlg->Activate(154, true);
+			break;
+		case 156:
+			if(Dlg->GetText(154, TmpTxt, TMP_TXT_SIZE)){
+				if(rd[0][*currYR]) free(rd[0][*currYR]);
+				rd[0][*currYR] = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);;
+				}
+			else if(*currYR == *maxYR) (*maxYR)--;
+			(*currYR)--;
+			Dlg->SetText(154, rd[0][*currYR]);
+			*updateYR = true;
+			res = -1;
+			break;
+		}
+	return res;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Dialog meta compiler
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *std_text[] = {"OK", "Cancel", "", "x", "y", "z", "-", "Next >>", "<< Prev.",
+	"%", "color", "x-value", "y-value", "z-value", TmpTxt, TmpTxt+100, TmpTxt+200,
+	TmpTxt+300, TmpTxt+400, "left", "right", "top", "bottom", "x-axis", "y-axis", "z-axis",
+	"select style:"};
+
+DlgInfo *CompileDialog(char* tmpl, void **ptypes)
+{
+	char **lines, **fields, **flags, error[80];
+	int i, j, nlines, nfields, nflags, last_id, last_next;
+	unsigned int hv;
+	DlgInfo *Dlg;
+
+	std_text[2] = Units[defs.cUnits].display;
+	lines = split(tmpl, '\n', &nlines);
+	if(!lines || nlines <1 ||(!(Dlg = (DlgInfo*)malloc(nlines*sizeof(DlgInfo))))) return 0L;
+	for(i = last_id = last_next = 0; i < nlines; i++) if(lines[i] && lines[i][0]){
+		if(fields = split(lines[i], ',', &nfields)) {
+			if(nfields == 10) {
+				Dlg[i].id = Dlg[i].next = Dlg[i].first = 0;
+				if(fields[0][0]) {
+					if(fields[0][0] == '.') Dlg[i].id = (last_id += 1);
+					else 
+#ifdef USE_WIN_SECURE
+						sscanf_s(fields[0], "%d", &Dlg[i].id);		last_id = Dlg[i].id;
+#else
+						sscanf(fields[0], "%d", &Dlg[i].id);		last_id = Dlg[i].id;
+#endif
+					}
+				if(fields[1][0]) {
+					if(fields[1][0] == '+') Dlg[i].next = (last_id + 1);
+					else if(fields[1][0] == '.') Dlg[i].next = (last_next += 1);
+					else 
+#ifdef USE_WIN_SECURE
+						sscanf_s(fields[1], "%d", &Dlg[i].next);	last_next = Dlg[i].next;
+#else
+						sscanf(fields[1], "%d", &Dlg[i].next);		last_next = Dlg[i].next;
+#endif
+					}
+#ifdef USE_WIN_SECURE
+				sscanf_s(fields[2], "%d", &Dlg[i].first);
+#else
+				sscanf(fields[2], "%d", &Dlg[i].first);
+#endif
+				Dlg[i].flags = 0L;
+				if(flags = split(fields[3], '|', &nflags)) {
+					for(j = 0; j < nflags; j++){
+						hv = HashValue((unsigned char *)str_trim(flags[j]));
+						switch(hv) {
+						case 196904:		Dlg[i].flags |= CHECKED;		break;
+						case 4444568:		Dlg[i].flags |= TOUCHEXIT;		break;
+						case 284491160:		Dlg[i].flags |= TOUCHSELEXIT;	break;
+						case 235859:		Dlg[i].flags |= ISRADIO;		break;
+						case 942268:		Dlg[i].flags |= ISPARENT;		break;
+						case 4220131:		Dlg[i].flags |= OWNDIALOG;		break;
+						case 198260:		Dlg[i].flags |= DEFAULT;		break;
+						case 54530:			Dlg[i].flags |= HIDDEN;			break;
+						case 1011472:		Dlg[i].flags |= NOSELECT;		break;
+						case 3546:			Dlg[i].flags |= HREF;			break;
+						case 62296:			Dlg[i].flags |= NOEDIT;			break;
+						case 231330:		Dlg[i].flags |= LASTOBJ;		break;
+						case 224595:		Dlg[i].flags |= EXRADIO;		break;
+						case 60824:			Dlg[i].flags |= ODEXIT;			break;
+							}
+						free(flags[j]);
+						}
+					free(flags);
+					}
+				hv = HashValue((unsigned char *)str_trim(fields[4]));
+				switch(hv) {
+				case 17108522:		Dlg[i].type = PUSHBUTTON;	break;
+				case 3252180:		Dlg[i].type = ARROWBUTT;	break;
+				case 206036:		Dlg[i].type = COLBUTT;		break;
+				case 13602346:		Dlg[i].type = FILLBUTTON;	break;
+				case 261312:		Dlg[i].type = SHADE3D;		break;
+				case 948692:		Dlg[i].type = LINEBUTT;		break;
+				case 282068:		Dlg[i].type = SYMBUTT;		break;
+				case 3403091:		Dlg[i].type = FILLRADIO;	break;
+				case 1130835:		Dlg[i].type = SYMRADIO;		break;
+				case 787668:		Dlg[i].type = CHECKBOX;		break;
+				case 62812:			Dlg[i].type = RADIO0;		break;
+				case 62813:			Dlg[i].type = RADIO1;		break;
+				case 62814:			Dlg[i].type = RADIO2;		break;
+				case 15460:			Dlg[i].type = LTEXT;		break;
+				case 16996:			Dlg[i].type = RTEXT;		break;
+				case 13156:			Dlg[i].type = CTEXT;		break;
+				case 51300:			Dlg[i].type = EDTEXT;		break;
+				case 16235656:		Dlg[i].type = RANGEINPUT;	break;
+				case 51281:			Dlg[i].type = EDVAL1;		break;
+				case 14534481:		Dlg[i].type = INCDECVAL1;	break;
+				case 229196:		Dlg[i].type = HSCROLL;		break;
+				case 286540:		Dlg[i].type = VSCROLL;		break;
+				case 71804:			Dlg[i].type = TXTHSP;		break;
+				case 3418:			Dlg[i].type = ICON;			break;
+				case 14196:			Dlg[i].type = GROUP;		break;
+				case 909332:		Dlg[i].type = GROUPBOX;		break;
+				case 16408:			Dlg[i].type = SHEET;		break;
+				case 970282:		Dlg[i].type = ODBUTTON;		break;
+				case 957537:		Dlg[i].type = LISTBOX1;		break;
+				case 1108443:		Dlg[i].type = TREEVIEW;		break;
+				case 237304:		Dlg[i].type = LINEPAT;		break;
+				case 269332:		Dlg[i].type = TEXTBOX;		break;
+				case 787858:		Dlg[i].type = CHECKPIN;		break;
+				case 17284:			Dlg[i].type = TRASH;		break;
+				case 51627:			Dlg[i].type = CONFIG;		break;
+				default:			Dlg[i].type = NONE;			break;
+					}
+#ifdef USE_WIN_SECURE
+				sscanf_s(fields[5], "%d", &j);
+#else
+				sscanf(fields[5], "%d", &j);
+#endif
+				if(j < 0) Dlg[i].ptype = (void*)std_text[(-j)-1];
+				else if(j > 0) Dlg[i].ptype = ptypes[j-1];
+				else Dlg[i].ptype = (void*)0L;
+#ifdef USE_WIN_SECURE
+				sscanf_s(fields[6], "%d", &Dlg[i].x);	sscanf_s(fields[7], "%d", &Dlg[i].y);
+				sscanf_s(fields[8], "%d", &Dlg[i].w);	sscanf_s(fields[9], "%d", &Dlg[i].h);
+#else
+				sscanf(fields[6], "%d", &Dlg[i].x);		sscanf(fields[7], "%d", &Dlg[i].y);
+				sscanf(fields[8], "%d", &Dlg[i].w);		sscanf(fields[9], "%d", &Dlg[i].h);
+#endif
+				}
+			else {
+#ifdef USE_WIN_SECURE
+				sprintf_s(error, 80, "Wrong number of arguments in template line %d", i);
+#else
+				sprintf(error,"Wrong number of arguments in template line %d\n\"%s\"\n", i, lines[i]);
+#endif
+				InfoBox(error);
+				Dlg[i].id = Dlg[i].next = Dlg[i].first = 0;
+				Dlg[i].flags = 0L;		Dlg[i].type = NONE;
+				Dlg[i].ptype = 0L;		Dlg[i].x = Dlg[i].y = Dlg[i].w = Dlg[i].h = 0;
+				}
+			for(j = 0; j < nfields; j++)free(fields[j]);
+			free(fields);
+			}
+		free(lines[i]);
+		}
+	free(lines);
+	return Dlg;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Use marked ranges as default for dialog ranges
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool UseRangeMark(DataObj *d, int type, char *r0, char *r1, char *r2, char *r3,
+	char *r4, char *r5, char *r6, char *r7, char *r8, char *r9, char *r10)
+{
+	char *dst[] = {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10};
+	char *mrk, **ra =0L;;
+	int i, j, ranges=0;
+	bool success = false, bErr=false;
+	RECT vrc, rrc;
+	AccRange *ar;
+	bool bRet = true;
+
+	for(i = 0; i < 11; i++) if(dst[i]) *dst[i] = 0;
+	if(d && type) {
+		d->ValueRec(&vrc);
+		if(d->Command(CMD_GETMARK, &mrk, 0L)) {
+			ra = split(mrk, ';', &ranges);
+			ar = new AccRange(mrk);			ar->BoundRec(&vrc);			delete ar;
+			}
+		switch(type) {
+		case 1:
+			if(ranges > 1) {
+				for(i = 0; i < 11; i++) if(dst[i]) {
+					rlp_strcpy(dst[i], 100, i < ranges ? ra[i] : mkRangeRef(vrc.top, vrc.left, vrc.bottom, vrc.left));
+					}
+				success = true;
+				}
+			else if(vrc.top == vrc.bottom && (vrc.right - vrc.left) >2) {
+				for(i = 0; i < 11; i++) if(dst[i]){
+					rlp_strcpy(dst[i], 100, mkRangeRef(vrc.top+i, vrc.left, vrc.top+i, vrc.right));
+					}
+				success = true;
+				}
+			else if(vrc.right == vrc.left && (vrc.bottom - vrc.top) >2) {
+				for(i = 0; i < 11; i++) if(dst[i]){
+					rlp_strcpy(dst[i], 100, mkRangeRef(vrc.top, vrc.left+i, vrc.bottom, vrc.left+i));
+					}
+				success = true;
+				}
+			break;
+		case 2:
+			for(i = 0; i < 11; i++) if(dst[i]) *(dst[i]) = 0;
+			if(ranges == 1) {
+				for(i = 0, j = vrc.left; i < 11 && j <= vrc.right; i++, j++) if(dst[i]){
+					rlp_strcpy(dst[i], 100, mkRangeRef(vrc.top, vrc.left+i, vrc.bottom, vrc.left+i));
+					}
+				}
+			else if(ranges > 1) {
+				ar = new AccRange(ra[0]);		j = ar->CountItems();
+				ar->BoundRec(&rrc);				delete ar;
+				if(rrc.left == rrc.right)for(i = 1; i < 11 && i < ranges && !bErr; i++){
+					ar = new AccRange(ra[i]);	ar->BoundRec(&rrc);
+					if(rrc.left != rrc.right) bErr = true;
+					delete ar;
+					}
+				else if(rrc.top == rrc.bottom)for(i = 1; i < 11 && i < ranges && !bErr; i++){
+					ar = new AccRange(ra[i]);	ar->BoundRec(&rrc);
+					if(rrc.top != rrc.bottom) bErr = true;
+					delete ar;
+					}
+				else for(i = 1; i < 11 && i < ranges && !bErr; i++){
+					ar = new AccRange(ra[i]);
+					if(j != ar->CountItems()) bErr = true;
+					delete ar;
+					}
+				if(i < 12 && !bErr) for(i = 0; i < 11 && i < ranges; i++){
+					if(dst[i]) rlp_strcpy(dst[i], 100, ra[i]);
+					}
+				}
+			if(bErr) {
+				InfoBox("Cannot resolve multiple ranges\nwith different sizes.\n\n"
+					"Please select ranges with equal size!");
+				i = 12;		bRet = false;
+				}
+			success = true;
+			}
+		}
+	else {
+		vrc.left = vrc.top = 0;		vrc.right = vrc.bottom = 9;
+		}
+	if(!success) for(i = 0; i < 11; i++) if(dst[i]){
+		rlp_strcpy(dst[i], 100, mkRangeRef(vrc.top, vrc.left+i, vrc.bottom, vrc.left+i));
+		}
+	if(ra) {
+		for(i = 0; i < ranges; i++) if(ra[i]) free(ra[i]);
+		free(ra);
+		}
+	return bRet;
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Select color out of predefined palette
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *NewColorTmpl =
+	"1,2,,DEFAULT,PUSHBUTTON,-1,200,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,200,25,45,12\n"
+	"3,7,4,CHECKED | ISPARENT,GROUPBOX,1,200,55,45,40\n"
+	"4,5,,,LTEXT,0,210,60,45,9\n"
+	"5,6,,,LTEXT,0,210,70,45,9\n"
+	"6,,,,LTEXT,0,210,80,45,9\n"
+	"7,+,,,RTEXT,2,5,130,79,9\n"
+	".,.,,,INCDECVAL1,3,85,130,33,10\n"
+	".,.,,,LTEXT,-10,122,130,10,9\n"
+	".,,11,CHECKED | ISPARENT,GROUP,0,0,0,0,0";
+
+DWORD GetNewColor(DWORD oldColor)
+{
+	double transp = (double)iround((double)((oldColor>>24) & 0xff)/2.55);
+	double use_step = 10.0, use_minmax[] = {0.0, 100.0};
+	void *ptypes[] = {(void*)" RGB ", (void*)"transparency", (void*)&transp};
+	DlgInfo *ColDlg = CompileDialog(NewColorTmpl, ptypes);
+	DWORD currcol, newcol, palette[230];
+	int ilevels[] = {0x0, 0x40, 0x80, 0xc0, 0xe0, 0xff};
+	int i, j, ir, ig, ib, col, row, res;
+	DlgRoot *Dlg;
+	void *hDlg;
+
+	if(!(ColDlg = (DlgInfo *)realloc(ColDlg, 240 * sizeof(DlgInfo))))return oldColor;
+	for(ir=row=col=0, i=10, j=0; ir<6; ir++) for(ig=0; ig<6; ig++) for(ib=0; ib<6; ib++) {
+		ColDlg[i].id = i+1;				ColDlg[i].next = i+2;
+		ColDlg[i].type = COLBUTT;			ColDlg[i].first = 0;
+		palette[j] = currcol = (ilevels[ib]<<16)|(ilevels[ig]<<8)|(ilevels[ir]);
+		ColDlg[i].flags = TOUCHEXIT | ISRADIO;
+		if(currcol == (oldColor & 0x00ffffff)) ColDlg[i].flags |= CHECKED;
+		ColDlg[i].ptype = (void *)&palette[j];
+		ColDlg[i].x = 5 + col*10;			ColDlg[i].y = 5 + row*10;
+		ColDlg[i].w = ColDlg[i].h = 10;
+		col++;
+		if(col >= 18) {
+			col = 0;			row++;
+			}
+		i++, j++;
+		}
+	j=j;
+	ColDlg[i-1].next = 0;
+	ColDlg[i-1].flags |= LASTOBJ;
+	newcol = currcol = oldColor;
+	Dlg = new DlgRoot(ColDlg, 0L);
+	Dlg->ItemCmd(8, CMD_STEP, (void*)&use_step);
+	Dlg->ItemCmd(8, CMD_MINMAX, (void*)&use_minmax);
+#ifdef USE_WIN_SECURE
+	sprintf_s(TmpTxt, 10, "R: 0x%02x", currcol & 0xff);				Dlg->SetText(4, TmpTxt);
+	sprintf_s(TmpTxt, 10, "G: 0x%02x", (currcol>>8) & 0xff);		Dlg->SetText(5, TmpTxt);
+	sprintf_s(TmpTxt, 10, "B: 0x%02x", (currcol>>16) & 0xff);		Dlg->SetText(6, TmpTxt);
+#else
+	sprintf(TmpTxt, "R: 0x%02x", currcol & 0xff);			Dlg->SetText(4, TmpTxt);
+	sprintf(TmpTxt, "G: 0x%02x", (currcol>>8) & 0xff);		Dlg->SetText(5, TmpTxt);
+	sprintf(TmpTxt, "B: 0x%02x", (currcol>>16) & 0xff);		Dlg->SetText(6, TmpTxt);
+#endif
+	hDlg = CreateDlgWnd("Select color", 50, 50, 510, 330, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:						//close window
+		case 2:						//cancel
+			currcol = oldColor;
+			break;
+		case 1:						//ok
+			currcol = newcol;
+			Dlg->GetValue(8, &transp);
+			currcol &= 0x00ffffff;
+			currcol |= ((((int)(transp*2.55))<<24)&0xff000000);
+			break;
+		default:
+			if(res > 9 && res < 227) {
+				currcol = newcol;
+				if(Dlg->GetColor(res, &newcol) && currcol != newcol) {
+					res = -1;
+#ifdef USE_WIN_SECURE
+					sprintf_s(TmpTxt, 10, "R: 0x%02x", newcol & 0xff);		Dlg->SetText(4, TmpTxt);
+					sprintf_s(TmpTxt, 10, "G: 0x%02x", (newcol>>8) & 0xff);		Dlg->SetText(5, TmpTxt);
+					sprintf_s(TmpTxt, 10, "B: 0x%02x", (newcol>>16) & 0xff);	Dlg->SetText(6, TmpTxt);
+#else
+					sprintf(TmpTxt, "R: 0x%02x", newcol & 0xff);			Dlg->SetText(4, TmpTxt);
+					sprintf(TmpTxt, "G: 0x%02x", (newcol>>8) & 0xff);		Dlg->SetText(5, TmpTxt);
+					sprintf(TmpTxt, "B: 0x%02x", (newcol>>16) & 0xff);		Dlg->SetText(6, TmpTxt);
+#endif
+					}
+				}
+			break;
+			}
+		}while (res < 0);
+	CloseDlgWnd(hDlg);		delete Dlg;
+	free(ColDlg);			return currcol;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Configure 3D shading
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *ConfShade_DlgTmpl = 
+	"1,2,,DEFAULT,PUSHBUTTON,-1,95,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,95,25,45,12\n"
+	"3,4,100,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
+	"4,5,,ODEXIT,COLBUTT,1,60,10,15,10\n"
+	"5,6,,ODEXIT,COLBUTT,2,60,37,15,10\n"
+	"6,7,,ODEXIT,COLBUTT,1,60,49,15,10\n"
+	"7,8,,,RTEXT,3,25,37,30,9\n"
+	"8,9,,,RTEXT,4,25,49,30,9\n"
+	"9,,,,SHADE3D,5,95,50,22,20\n"
+	"100,101,,TOUCHEXIT,RADIO1,6,10,10,50,9\n"
+	"101,,,TOUCHEXIT | LASTOBJ,RADIO1,7,10,25,60,9";
+
+void ConfShade(FillDEF *oldfill)
+{
+	FillDEF newFill;
+	void *dyndata[] = {(void*)&oldfill->color, (void*)&oldfill->color2, (void*)"HI-color:",
+		(void*)"LO-color:", (void*)&newFill, (void*)"fixed color:", (void*)"use light sorce:"};
+	DlgInfo *ShadeDlg = CompileDialog(ConfShade_DlgTmpl, dyndata);
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res;
+	bool bRedraw;
+	anyOutput *o;
+	RECT rc_prev;
+
+	if(!oldfill) return;
+	memcpy(&newFill, oldfill, sizeof(FillDEF));
+	newFill.type = FILL_LIGHT3D;
+	if(!(Dlg = new DlgRoot(ShadeDlg, 0L))) return;
+	if(oldfill->type & FILL_LIGHT3D) Dlg->SetCheck(101, 0L, true);
+	else {
+		Dlg->SetCheck(100, 0L, true);
+		newFill.color2 = newFill.color;
+		}
+	hDlg = CreateDlgWnd("Shade and Fill Color", 50, 50, 300, 180, Dlg, 0x4L);
+	bRedraw = true;
+	o = Dlg->GetOutputClass();			o->LightSource(32.0, 16.0);
+	rc_prev.left =240;					rc_prev.right = 280;
+	rc_prev.top = 100;					rc_prev.bottom = 140;
+	do {
+		if(bRedraw) {
+			Dlg->DoPlot(0L);			o->SetFill(&newFill);
+			o->oSphere(260, 120, 17, 0L, 0);
+			o->UpdateRect(&rc_prev, false);
+			bRedraw = false;
+			}
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 4:		case 100:
+			Dlg->GetColor(4, &newFill.color);
+			newFill.color2 = newFill.color;
+			Dlg->SetCheck(100, 0L, true);
+			bRedraw = true;
+			res = -1;
+			break;
+		case 5:		case 6:		case 101:
+			Dlg->GetColor(5, &newFill.color2);
+			Dlg->GetColor(6, &newFill.color);
+			Dlg->SetCheck(101, 0L, true);
+			bRedraw = true;
+			res = -1;
+			break;
+			}
+		}while (res < 0);
+	if(res == 1) {
+		memcpy(oldfill, &newFill, sizeof(FillDEF));
+		if(Dlg->GetCheck(100)) oldfill->type &= ~FILL_LIGHT3D;
+		else oldfill->type |= FILL_LIGHT3D;
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;			 free(ShadeDlg);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Select fill pattern
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *FillDlgBase_Tmpl = 
+	"1,2,,DEFAULT,PUSHBUTTON,-1,170,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,170,25,45,12\n"
+	"3,100,500,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
+	"100,+,,TOUCHEXIT,FILLBUTTON,1,170,75,45,45\n"
+	".,.,,,RTEXT,2,78,95,40,8\n"
+	".,.,,TOUCHEXIT,INCDECVAL1,3, 119, 95, 32, 10\n"
+	".,.,,,LTEXT,-3,152,95,15,8\n"
+	".,.,,,RTEXT,4,5,95,30,8\n"
+	".,.,,ODEXIT,COLBUTT,8,36,95,25,10\n"
+	".,.,,,RTEXT,5,78,110,40,8\n"
+	".,.,,TOUCHEXIT,INCDECVAL1,6, 119, 110, 32, 10\n"
+	".,.,,,LTEXT,-10,152,110,8,8\n"
+	".,.,,,RTEXT,7,5,110,30,8\n"
+	".,,,ODEXIT,COLBUTT,9,36,110,25,10";
+
+void GetNewFill(FillDEF *oldfill)
+{
+	LineDEF PrevLine;
+	FillDEF PrevFill;
+	unsigned int EnumFills[] = {FILL_NONE, FILL_HLINES, FILL_VLINES, FILL_HVCROSS, FILL_DLINEU, FILL_DLINED,
+		FILL_DCROSS, FILL_STIPPLE1, FILL_STIPPLE2, FILL_STIPPLE3, FILL_STIPPLE4, 
+		FILL_STIPPLE5, FILL_ZIGZAG, FILL_COMBS, FILL_BRICKH, FILL_BRICKV, FILL_BRICKDU, 
+		FILL_BRICKDD, FILL_TEXTURE1, FILL_TEXTURE2, FILL_WAVES1, FILL_SCALES, FILL_SHINGLES, 
+		FILL_WAVES2, FILL_HERRING, FILL_CIRCLES, FILL_GRASS, FILL_FOAM, FILL_RECS, FILL_HASH,
+		FILL_WATER};
+	double fscale;
+	void *dyndata[] = {(void *)&PrevFill, (void*)"line width", (void*)&PrevLine.width, (void*)"line color",
+		(void*)"pattern size", (void*)&fscale, (void*)"BG color", (void*)&PrevLine.color, (void*) &PrevFill.color};
+	DlgInfo *FillDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, res;
+	anyOutput *o;
+	bool bRedraw, bContinue = false;
+	double tmp, use_minmax[] = {10.0, 800.0};
+
+	PrevLine.width = (oldfill && oldfill->hatch)? oldfill->hatch->width : 0.2f;
+	PrevLine.patlength = 1.0f;
+	PrevLine.color = (oldfill && oldfill->hatch)? oldfill->hatch->color : 0x00000000L;
+	PrevLine.pattern = 0x00000000L;
+	PrevFill.type = oldfill ? oldfill->type : FILL_NONE;
+	PrevFill.color = oldfill ? oldfill->color : 0x00ffffffL;
+	PrevFill.scale = oldfill ? oldfill->scale : 1.0f;
+	PrevFill.hatch = &PrevLine;
+	PrevFill.color2 = oldfill ? oldfill->color2 : 0x00ffffffL;
+	fscale = PrevFill.scale *100.0f;
+	if(!(FillDlg =  CompileDialog(FillDlgBase_Tmpl, dyndata))) return;
+	if(!(FillDlg = (DlgInfo*)realloc(FillDlg, (NUM_FILLS + 15)*sizeof(DlgInfo)))) return;
+	for(i = 14, j = 0; j < NUM_FILLS; i++, j++) {
+		FillDlg[i].id = j+500;				FillDlg[i].next = j+501;
+		FillDlg[i].first = 0;				FillDlg[i].flags = TOUCHEXIT;
+		FillDlg[i].type = FILLRADIO;		FillDlg[i].ptype = (void*)(EnumFills+j);
+		FillDlg[i].x = (j &0x07)*20+5;		FillDlg[i].y = (j>>3)*20+5;
+		FillDlg[i].w = FillDlg[i].h = 20;
+		}
+	FillDlg[i-1].next = 0;
+	FillDlg[i-1].flags |= LASTOBJ;
+	Dlg = new DlgRoot(FillDlg, 0L);
+	Dlg->ItemCmd(107, CMD_MINMAX, (void*)use_minmax);
+	for(i = Dlg->FindIndex(500), j = 0; FillDlg[i].type == FILLRADIO; i++, j++)
+		if(*((int*)FillDlg[i].ptype) == PrevFill.type)Dlg->SetCheck(500+j, 0L, true);
+	hDlg = CreateDlgWnd("Fill patterns", 50, 50, 450, 290, Dlg, 0x4L);
+	bRedraw = true;
+	o = Dlg->GetOutputClass();
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		if (res >= 0) switch (res) {
+		case 0:								// focus lost
+			if(bContinue) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		default:					// test for pattern
+			i = Dlg->FindIndex(res);
+			if(FillDlg[i].type == FILLRADIO) {
+				bRedraw = bContinue = true;
+				i = *((int*)FillDlg[i].ptype);
+				if(i != PrevFill.type){		// process double click on pattern
+					PrevFill.type = i;		res = -1;
+					defs.Idle(CMD_UPDATE);
+					}
+				else res = 1;			// assume OK pressed
+				break;
+				}
+			else break;
+		case 110:					// fill color changed
+		case 102:					// line width changed
+		case 105:					// line color changed
+		case 107:					// scaling changed
+		case 100:					// update preview
+			bContinue = true;
+			res = -1;				// ...these buttons do not exit dialog
+		case 1:						// but do exit with OK
+			if((bRedraw = Dlg->GetValue(102, &tmp)))PrevLine.width = tmp;
+			if(bRedraw) bRedraw = Dlg->GetColor(105, &PrevLine.color);
+			if(bRedraw) bRedraw = Dlg->GetColor(110, &PrevFill.color);
+			if(bRedraw && (bRedraw = Dlg->GetValue(107, &tmp))) 
+				PrevFill.scale = tmp/100.0;
+			break;
+			}
+		if (o && bRedraw) {					// send update preview to dialog
+			Dlg->ForEach(CMD_DOPLOT, Dlg->FindIndex(100), o);
+			bRedraw = false;
+			}
+		}while (res < 0);
+	switch(res) {
+	case 1:									//OK button pressed
+		if(oldfill) {
+			oldfill->type = PrevFill.type;
+			oldfill->color = PrevFill.color;
+			oldfill->scale = PrevFill.scale;
+			if(oldfill->hatch) {
+				oldfill->hatch->width = PrevLine.width;
+				oldfill->hatch->patlength = PrevLine.patlength;
+				oldfill->hatch->color = PrevLine.color;
+				oldfill->hatch->pattern = PrevLine.pattern;
+				}
+			}
+		break;
+		}
+	CloseDlgWnd(hDlg);	delete Dlg;		free(FillDlg);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// execute layers dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char* LayerDlg_Tmpl =
+	"1,3,0,DEFAULT,PUSHBUTTON,-1,165,10,45,12\n"
+	"3,20,100,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"20,550,,,CHECKPIN,0,10,0,12,8\n"
+	"100,101,,TOUCHEXIT,TREEVIEW,1,10,15,100,70\n"
+	"101,102,,,EDTEXT,2, 120, 25, 90, 10\n"
+	"102,200,150,ISPARENT | CHECKED | HIDDEN,GROUP,0L,0,0,0,0\n"
+	"150,151,,TOUCHEXIT,RADIO1,3,120,35,40,9\n"
+	"151,,,TOUCHEXIT,RADIO1,4,160,35,40,9\n"
+	"200,500,,,LTEXT,5,10,5,110,9\n"
+	"500,501,600,ISPARENT | CHECKED | HIDDEN,GROUP,0,0,0,0,0\n"
+	"501,,,HIDDEN | TOUCHEXIT,TRASH,0,125,72,15,15\n"
+	"550,,,HIDDEN | TOUCHEXIT,CONFIG,0,140,72,15,15\n"
+	"600,601,,TOUCHEXIT, ODBUTTON,6,125,50,15,15\n"
+	"601,602,,TOUCHEXIT, ODBUTTON,6,145,50,15,15\n"
+	"602,603,,TOUCHEXIT, ODBUTTON,6,165,50,15,15\n"
+	"603,,,LASTOBJ | TOUCHEXIT, ODBUTTON,6,185,50,15,15";
+
+bool ShowLayers(GraphObj *root)
+{
+	char curr_name[50];
+	void *dyndata[] = {(void*)root, (void*)curr_name, (void*)"hidden", (void*)"visible",
+		(void*)"Layers:", (void*)OD_DrawOrder};
+	DlgInfo *LayerDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, cl;
+	bool bContinue = false;
+	ObjTree *ot = 0L;
+	GraphObj *cgo = 0L;
+
+	if(!root || !(LayerDlg = CompileDialog(LayerDlg_Tmpl, dyndata))) return false;
+	rlp_strcpy(curr_name, 50, "(root)");
+	if(!(Dlg = new DlgRoot(LayerDlg, 0L)))return false;
+	Dlg->ItemCmd(100, CMD_OBJTREE, &ot);
+	if(!ot) {
+		delete Dlg;			return false;
+		}
+	Dlg->SetText(101, ot->get_name(0));		Dlg->Activate(101, false);
+	Dlg->SetColor(150, 0x00000080L);		Dlg->SetColor(151, 0x00008000L);
+	if(root->Id == GO_GRAPH || root->Id == GO_PAGE) Dlg->ShowItem(550, true);
+	hDlg = CreateDlgWnd("Layer Control", 50, 50, 440, 210, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue || Dlg->GetCheck(20)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 550:
+			bContinue = true;
+		case 501:
+			if (res == 501) {
+				if(cgo && cgo->parent)	cgo->parent->Command(CMD_DELOBJ, cgo, 0L);
+				Dlg->ItemCmd(100, CMD_LAYERS, 0L);
+				}
+		case 100:
+			if(Dlg->GetInt(100, &cl)) Dlg->SetText(101, ot->get_name(cl));
+			switch(ot->get_vis(cl)) {
+			case 0:
+				Dlg->SetCheck(150, 0L, true);			Dlg->ShowItem(102, true);
+				break;
+			case 1:
+				Dlg->SetCheck(151, 0L, true);			Dlg->ShowItem(102, true);
+				break;
+			case 2:
+				Dlg->ShowItem(102, false);
+				break;
+				}
+			if((cgo = ot->get_obj(cl)) && cgo->parent) {
+				if(cgo->parent->Id == GO_STACKBAR || cgo->parent->Id == GO_STACKPG ||
+					cgo->parent->Id == GO_WATERFALL || cgo->parent->Id == GO_PLOT3D ||
+					cgo->parent->Id == GO_FUNC3D) {
+					Dlg->ShowItem(501, true);
+					}
+				else if(cgo->parent->Id == GO_GRAPH || cgo->parent->Id == GO_PAGE){ 
+					Dlg->ShowItem(500, cgo->parent->Command(CMD_HASSTACK, 0L, 0L));
+					Dlg->ShowItem(501, true);
+					}
+				else {
+					Dlg->ShowItem(500, false);			Dlg->ShowItem(501, false);
+					}
+				if(cgo->Id == GO_GRAPH || cgo->Id == GO_PAGE || cgo->Id == GO_POLARPLOT
+					|| cgo->Id == GO_GRID3D){
+					Dlg->ShowItem(550, true);
+					if(res == 550) if(cgo->Command(CMD_CONFIG, 0L, 0L))
+						cgo->Command(CMD_REDRAW, 0L, 0L);
+					}
+				else Dlg->ShowItem(550, false);
+				}
+			Dlg->Command(CMD_REDRAW, 0L, 0L);
+			res = -1;
+			break;
+		case 150:		case 151:
+			ot->set_vis(cl, res == 151);
+			Dlg->ItemCmd(100, CMD_UPDATE, 0L);			Dlg->Command(CMD_REDRAW, 0L, 0L);
+			res = -1;
+			break;
+		case 600:	case 601:	case 602:	case 603:
+			if(cgo && cgo->parent){
+				ExecDrawOrderButt(cgo->parent, cgo, res);
+				}
+			Dlg->ItemCmd(100, CMD_UPDATE, 0L);			Dlg->Command(CMD_REDRAW, 0L, 0L);
+			res = -1;
+			}
+		}while(res <0);
+	CloseDlgWnd(hDlg);		delete Dlg;			free(LayerDlg);
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// display a welcome banner
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void ShowBanner(bool show)
+{
+	int icon = ICO_RLPLOT;
+	static DlgInfo BannerDlg[] = {
+		{1, 2, 0, 0x0L, PUSHBUTTON, 0L, 0, 0, 112, 52},
+		{2, 3, 0, 0x0L, ICON, (void*)&icon, 10, 10, 20, 20},
+		{3, 4, 0, 0x0L, LTEXT, (void*)"RLPlot", 40, 15, 50, 20},
+		{4, 5, 0, 0x0L, LTEXT, (void*)SZ_VERSION, 13, 30, 35, 9},
+		{5, 0, 0, LASTOBJ, LTEXT, (void*)"... is loading", 50, 30, 50, 9}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	bool init = true;
+	int res, cnt = 5;
+
+	if(!show) return;
+	if(!(Dlg = new DlgRoot(BannerDlg, 0L)))return;
+#ifdef _WINDOWS
+	Dlg->TextSize(3, 36);
+#else
+	Dlg->TextSize(3, 24);
+#endif
+	Dlg->TextStyle(3, TXS_ITALIC | TXS_BOLD);
+	Dlg->TextFont(3, FONT_TIMES);
+	Dlg->SetColor(3, 0x00ff0000L);
+	hDlg = CreateDlgWnd("RLPlot", 50, 50, 220, 100, Dlg, 0xbL);
+	ShowDlgWnd(hDlg);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:						//loose focus: get it again
+			ShowDlgWnd(hDlg);
+			cnt = 5;		res = -1;
+			break;
+		case -2:					//Timer event
+			if(init) {
+				init = false;
+				FindBrowser();			SpreadMain(true);
+				ShowDlgWnd(hDlg);		cnt = 4;
+				}
+			if((cnt--) <=0) res = 1;
+			break;
+			}
+		}while(res < 0);
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// the RLPlot about dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void RLPlotInfo()
+{
+	int ico_rlp = ICO_RLPLOT;
+#ifdef _WINDOWS
+	DlgInfo AboutDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 40, 110, 40, 12},
+		{2, 3, 0, 0x0L, ICON, (void*)&ico_rlp, 10, 10, 16, 16},
+		{3, 4, 0, 0x0L, LTEXT, (void*)"RLPlot", 40, 10, 60, 18},
+		{4, 5, 0, 0x0L, CTEXT, (void*)"scientific plotting program", 10, 30, 100, 8},
+		{5, 6, 0, 0x0L, CTEXT, (void*)"version "SZ_VERSION, 10, 38, 100, 8},
+		{6, 7, 0, HREF, CTEXT, (void*)"http://rlplot.sourceforge.net/", 5, 46, 110, 8},
+		{7, 8, 0, 0x0L, CTEXT, (void*)"Copyright (C) 2002-2007 R. Lackner", 5, 54, 110, 8},
+		{8, 9, 0, 0x0L, CTEXT, (void*)"reinhard.lackner at uibk.ac.at", 5, 62, 110, 9},
+		{9, 10, 0, 0x0L, CTEXT, (void*)"This is free software published", 5, 80, 110, 8},
+		{10, 11, 0, 0x0L, CTEXT, (void*)"under the GNU general public.", 5, 88, 110, 8},
+		{11, 0, 0, LASTOBJ, CTEXT, (void*)"license (GPL).", 5, 96, 110, 9}};
+#else
+	int ico_qt = ICO_QT;
+	DlgInfo AboutDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 40, 128, 40, 12},
+		{2, 3, 0, 0x0L, ICON, (void*)&ico_rlp, 10, 10, 16, 16},
+		{3, 4, 0, 0x0L, LTEXT, (void*)"RLPlot", 40, 10, 60, 18},
+		{4, 5, 0, 0x0L, CTEXT, (void*)"scientific plotting program", 10, 30, 100, 8},
+		{5, 6, 0, 0x0L, CTEXT, (void*)"version "SZ_VERSION, 10, 38, 100, 8},
+		{6, 7, 0, HREF, CTEXT, (void*)"http://rlplot.sourceforge.net/", 5, 46, 110, 8},
+		{7, 8, 0, 0x0L, CTEXT, (void*)"Copyright (C) 2002-2007 R. Lackner", 5, 54, 110, 8},
+		{8, 9, 0, 0x0L, CTEXT, (void*)"reinhard.lackner at uibk.ac.at", 5, 62, 110, 9},
+		{9, 10, 0, 0x0L, LTEXT, (void*)"powered by Trolltech\'s Qt", 35, 72, 80, 8},
+		{10, 11, 0, HREF, LTEXT, (void*)"http://www.trolltech.com", 35, 80, 80, 9},
+		{11, 12, 0, TOUCHEXIT, ICON, (void*)&ico_qt, 5, 72, 25, 25},
+		{12, 13, 0, 0x0L, CTEXT, (void*)"This is free software published", 5, 100, 110, 8},
+		{13, 14, 0, 0x0L, CTEXT, (void*)"under the GNU general public.", 5, 108, 110, 8},
+		{14, 0, 0, LASTOBJ, CTEXT, (void*)"license (GPL).", 5, 116, 110, 9}};
+#endif // _WINDOWS
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res;
+
+	if((Dlg = new DlgRoot(AboutDlg, 0L))) {
+		Dlg->TextStyle(3, TXS_ITALIC | TXS_BOLD);
+		Dlg->TextFont(3, FONT_TIMES);
+#ifdef _WINDOWS
+		Dlg->TextSize(3, 36);
+#else
+		Dlg->TextSize(3, 32);
+#endif // _WINDOWS
+		Dlg->SetColor(3, 0x00ff0000L);
+		}
+	else return;
+#ifdef _WINDOWS
+	hDlg = CreateDlgWnd("About RLPlot", 50, 50, 240, 280, Dlg, 0x4L);
+#else
+	hDlg = CreateDlgWnd("About RLPlot", 50, 50, 240, 310, Dlg, 0x4L);
+#endif // _WINDOWS
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		if(res == 11) Qt_Box();
+		}while(res <0);
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// change spreadsheet settings
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char * SSDlg_Tmpl = 
+	"1,2,,DEFAULT,PUSHBUTTON,-1,129,10,40,12\n"
+	"2,3,,,PUSHBUTTON,-2,129,25,40,12\n"
+	"3,,5,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
+	"5,6,100,ISPARENT | CHECKED,SHEET,1,5,10,115,85\n"
+	"6,,200,ISPARENT,SHEET,2,5,10,115,85\n"
+	"100,+,,,LTEXT,3,15,25,60,8\n"
+	".,.,,,EDTEXT,-16,50,37,40,10\n"
+	".,.,,,LTEXT,4,15,52,60,8\n"
+	".,,,,EDTEXT,-17,50,64,40,10\n"
+	"200,+,,,RTEXT,5,10,29,45,8\n"
+	".,.,,,EDVAL1,6,57,29,25,10\n"
+	".,.,,,LTEXT,7,84,29,20,8\n"
+	".,.,,,RTEXT,8,10,41,45,8\n"
+	".,.,,,EDVAL1,9,57,41,25,10\n"
+	".,.,,,LTEXT,7,84,41,20,8\n"
+	".,.,,,RTEXT,10,10,53,45,8\n"
+	".,.,,,EDVAL1,11,57,53,25,10\n"
+	".,.,,,LTEXT,-3,84,53,20,8\n"
+	".,.,,,RTEXT,12,10,65,45,8\n"
+	".,.,,,INCDECVAL1,13,57,65,33,10\n"
+	".,.,,,LTEXT,-10,92,65,20,8\n"
+	".,.,,,RTEXT,14,10,77,45,8\n"
+	".,.,,,EDVAL1,15,57,77,25,10\n"
+	".,,,LASTOBJ,LTEXT,16,84,77,20,8";
+
+bool
+DoSpShSize(DataObj *dt, GraphObj *parent)
+{
+	TabSHEET tab1 = {0, 50, 10, "Dimensions"};
+	TabSHEET tab2 = {50, 115, 10, "Width and Height"};
+	double fw, cw, ch, th, mh;
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)"number of columns:", (void*)"number of rows:",
+		(void*)"row buttons", (void*)&fw, (void*)"[digits]", (void*)"column width", (void*)&cw,
+		(void*)"row height", (void*)&ch, (void*)"text size", (void*)&th, (void*)"menu height",
+		(void*)&mh, (void*)"pix"};
+	DlgInfo *SSDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int w1, w2, h1, h2, res, celldim[3], ith;
+	bool bRet = false;
+	double fw1, cw1, ch1, th1, mh1;
+
+	if(!dt || !dt->GetSize(&w1, &h1) || !parent) return false;
+	if(!(SSDlg = CompileDialog(SSDlg_Tmpl, dyndata))) return false;
+	dt->Command(CMD_GET_CELLDIMS, &celldim, 0L);
+	fw1 = fw = NiceValue(((double)celldim[0])/((double)(celldim[2]-2)/2.0));
+	cw1 = cw = NiceValue(((double)celldim[1])/((double)(celldim[2]-2)/2.0));
+	mh1 = mh = (double)(defs.iMenuHeight);
+	switch(defs.cUnits) {
+	case 1:		ch = NiceValue(((double)(celldim[2]-2))*0.0259183673);	break;
+	case 2:		ch = NiceValue(((double)(celldim[2]-2))/98.0);		break;
+	default:	ch = NiceValue(((double)(celldim[2]-2))*0.259183673);	break;
+		}
+	ch1 = ch;						th = th1 = defs.ss_txt*100.0;
+#ifdef USE_WIN_SECURE
+	sprintf_s(TmpTxt+100, 40, "%d", w1);		sprintf_s(TmpTxt+200, 40, "%d", h1);
+#else
+	sprintf(TmpTxt+100, "%d", w1);			sprintf(TmpTxt+200, "%d", h1);
+#endif
+	Dlg = new DlgRoot(SSDlg, dt);
+	hDlg = CreateDlgWnd("Change spread sheet settings", 50, 50, 354, 220, Dlg, 0x4L);
+	Dlg->GetValue(201, &fw1);	Dlg->GetValue(204, &cw1);	Dlg->GetValue(207, &ch1);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 1:					//OK pressed
+			if(Dlg->GetText(101, TmpTxt+100, 40) && Dlg->GetText(103, TmpTxt+200, 40)) {
+				w2 = atol(TmpTxt+100);		h2 = atol(TmpTxt+200);
+				w2 = w2 > 0 ? w2: w1;	h2 = h2 > 0 ? h2: h1;
+				}
+			else res = -1;
+			break;
+			}
+		}while(res <0);
+	if(res == 1){
+		if(Dlg->GetValue(207, &ch) && Dlg->GetValue(210, &th) && ch > 0.001) {
+			Undo.ValFloat(parent, &defs.ss_txt, 0L);
+			defs.ss_txt = th = th >= 10.0 && th <= 100 ? th/100.0 : 1.0; 
+			switch(defs.cUnits) {
+			case 1:		
+				celldim[2] = iround(ch/0.0259183673)+2;		ith = iround(th * ch/0.0259183673);
+				break;
+			case 2:		
+				celldim[2] = iround(ch*98.0)+2;				ith = iround(th * ch*98.0);
+				break;
+			default:	
+				celldim[2] = iround(ch/0.259183673)+2;		ith = iround(th * ch/0.259183673);
+				break;
+				}
+			bRet = dt->Command(CMD_TEXTSIZE, (void*)(& ith), 0L);
+			}
+		Dlg->GetValue(201, &fw);	Dlg->GetValue(204, &cw);
+		if(fw >0.001 && (fw != fw1 || ch != ch1))
+			celldim[0] = iround(fw * ((double)(celldim[2]-3)/2.0));
+		if(cw >0.001 && (cw != cw1 || ch != ch1))
+			celldim[1] = iround(cw * ((double)(celldim[2]-3)/2.0));
+		if(fw != fw1 || cw != cw1 || ch != ch1)
+			dt->Command(CMD_SET_CELLDIMS, &celldim, 0L);
+		if(!dt->ChangeSize(w2, h2, true))
+			ErrorBox("Failed to set new dimensions\nof Spreadsheet.");
+		else bRet = true;
+		Dlg->GetValue(213, &mh);
+		if(mh != mh1 && mh >= 0.0 && mh < 100) {
+			defs.iMenuHeight = (int)mh;
+			parent->Command(CMD_MENUHEIGHT, 0L, 0L);
+			}
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;			free(SSDlg);
+	return bRet;
+}
+
+
+bool FillSsRange(DataObj *d, char **range, GraphObj *msg_go)
+{
+	TabSHEET tab1 = {0, 37, 10, "fill range "};
+	char *ra = range ? *range:0L;
+	double startval = 1.0, stepval = 1.0;
+	static char *formula = (char*)malloc(500 * sizeof(char));
+	if(formula && CurrText && CurrText->isFormula()) {
+		if(CurrText->GetText(TmpTxt,TMP_TXT_SIZE, false)) rlp_strcpy(formula, 500, TmpTxt);
+		}
+	if(formula) rlp_strcpy(formula, 500, "=a1");
+	DlgInfo RangeDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 162, 5, 38, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 162, 20, 38, 12},
+		{3, 0, 5, CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{5, 0, 6, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 150, 75},
+		{6, 7, 0, 0x0L, RANGEINPUT, (void*)ra, 10, 25, 140, 10},
+		{7, 8, 0, CHECKED | TOUCHEXIT, RADIO1, (void*)"with values", 10, 37, 45, 8},
+		{8, 9, 0, TOUCHEXIT, RADIO1, (void*)"with formulas", 60, 37, 60, 8},
+		{9, 10, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
+		{10, 20, 11, CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{11, 12, 0, 0x0L, RTEXT, (void*)"start value=", 10, 60, 35, 8},
+		{12, 13, 0, 0x0L, EDVAL1, &startval, 45, 60, 30, 10},
+		{13, 14, 0, 0x0L, RTEXT, (void*)"increment by", 77, 60, 43, 8},
+		{14, 0, 0, 0x0L, EDVAL1, &stepval, 122, 60, 30, 10},
+		{20, 0, 21, CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
+		{21, 22, 0, 0x0L, RTEXT, (void*)"first cell formula:", 10, 52, 50, 8},
+		{22, 23, 0, 0x0L, EDTEXT, (void*)formula, 60, 52, 90, 10},
+		{23, 24, 0, 0x0L, LTEXT, (void*)"For a list with available funtions see:", 10, 65, 90, 8},
+		{24, 0, 0, HREF | LASTOBJ | TOUCHEXIT, LTEXT, (void*)"http://rlplot.sourceforge.net/Docs/parser.html", 10, 72, 90, 8}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, res, row, col, r1, c1;
+	bool bRet = false, bContinue = false;
+	AccRange *rF;
+	RECT rc_dest;
+	anyOutput *cdisp = 	Undo.cdisp;
+
+	if(!d || !range) return false;
+	if(!(Dlg = new DlgRoot(RangeDlg, d))) return false;
+#ifdef _WINDOWS
+	for(i = 23; i <= 24; i++) Dlg->TextSize(i, 12);
+#else
+	for(i = 23; i <= 24; i++) Dlg->TextSize(i, 10);
+#endif
+	hDlg = CreateDlgWnd("Fill Spreadsheet Range", 50, 50, 414, 206, Dlg, 0x4L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res){
+		case 0:
+			if(bContinue) {
+				bContinue = false;				res = -1;
+				}
+			else if(Dlg->GetCheck(9)) res = -1;
+			break;
+		case 1:
+			ra = 0L;
+			if(!Dlg->GetText(6, TmpTxt, TMP_TXT_SIZE)) {
+				InfoBox("Range not specified\nor not valid.");
+				bContinue = true;				res = -1;
+				}
+			else ra = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+			rc_dest.left = rc_dest.right = rc_dest.top = rc_dest.bottom = 0;
+			break;
+		case 7:
+			Dlg->ShowItem(10, true);			Dlg->ShowItem(20, false);
+			Dlg->Command(CMD_REDRAW, 0L, 0L);	res = -1;
+			break;
+		case 8:
+			Dlg->ShowItem(10, false);	Dlg->ShowItem(20, true);
+			Dlg->Command(CMD_REDRAW, 0L, 0L);	res = -1;
+			break;
+		case 24:
+			bContinue=true;
+			res = -1;
+			break;
+			}
+		}while(res <0);
+	Undo.SetDisp(cdisp);
+	if(res == 1 && ra) {
+		if(Dlg->GetCheck(7)) {
+			Dlg->GetValue(12, &startval);	Dlg->GetValue(14, &stepval);
+			if((rF = new AccRange(ra)) && rF->GetFirst(&col, &row)) {
+				rF->BoundRec(&rc_dest);
+				Undo.DataObject(msg_go, Undo.cdisp, d, &rc_dest, 0L);
+				for( ; rF->GetNext(&col, &row); startval += stepval) {
+					d->SetValue(row, col, startval);
+					}
+				delete rF;							bRet = true;
+				}
+			}
+		else if(Dlg->GetCheck(8)) {
+			if((rF = new AccRange(ra)) && rF->GetFirst(&col, &row) && 
+				Dlg->GetText(22, TmpTxt, TMP_TXT_SIZE) && formula){
+				rlp_strcpy(formula, 500, TmpTxt);
+				r1 = row;		c1 = col;
+				for( ; rF->GetNext(&col, &row); startval += stepval) {
+					if(formula[0] == '=') {
+						MoveFormula(d, formula, TmpTxt, TMP_TXT_SIZE, col-c1, row-r1, -1, -1);
+						d->SetText(row, col, TmpTxt);
+						}
+					else d->SetText(row, col, formula);
+					}
+				delete rF;							bRet = true;
+				}
+			}
+		free(ra);
+		}
+	CloseDlgWnd(hDlg);			delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Get resolution and size for exported bitmap
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char * ResDlg_Tmpl = 
+	"1,2,,DEFAULT,PUSHBUTTON,-1,125,10,35,12\n"
+	"2,100,,,PUSHBUTTON,-2,125,25,35,12\n"
+	"100,+,,,LTEXT,1,20,10,50,9\n"
+	".,.,,,RTEXT,2,20,22,35,9\n"
+	".,.,,,EDVAL1,3,57,22,30,10\n"
+	".,.,,,LTEXT,-3,89,22,20,8\n"
+	".,.,,,RTEXT,4,20,34,35,9\n"
+	".,.,,,EDVAL1,5,57,34,30,10\n"
+	".,.,,,LTEXT,-3,89,34,20,8\n"
+	".,.,,,RTEXT,6,20,46,35,9\n"
+	".,.,,,EDVAL1,7,57,46,30,10\n"
+	".,,,LASTOBJ,LTEXT,8,89,46,20,8";
+
+bool GetBitmapRes(double *dpi, double *width, double *height, char *header)
+{
+	void *dyndata[] = {(void*)"Image properties:", (void*)"width", (void*)width,
+		(void*)"height", (void*)height, (void*)"resolution", (void*)dpi,  (void *) "dpi"};
+	DlgInfo *ResDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	bool bRet = false;
+	int res;
+	
+	if(!(ResDlg = CompileDialog(ResDlg_Tmpl, dyndata))) return false;
+	if(!(Dlg = new DlgRoot(ResDlg, 0L))) return false;
+	if(!(hDlg = CreateDlgWnd(header, 50, 50, 340, 160, Dlg, 0x4L)))return false;
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		if(res==1) {
+			Dlg->GetValue(102, width);		Dlg->GetValue(105, height);
+			Dlg->GetValue(108, dpi);
+			}
+		}while (res < 0);
+	bRet = (res == 1);
+	CloseDlgWnd(hDlg);	delete Dlg;		free(ResDlg);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Execute schemes dialog as owner drawn button
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static LineDEF Sch0_LINE = {.1, 1.0, 0x0L, 0x0L};
+static LineDEF Sch3_LINE = {.1, 1.0, 0x0L, 0x0L};
+static FillDEF Scheme0 = {FILL_NONE, 0x00ffffffL, 1.0, &Sch0_LINE, 0x00ffffffL};
+static DWORD Scheme1[] = {0x000000ffL, 0x0000ff00L, 0x00ff0000L, 0x0000ffffL,
+	0x00ff00ffL, 0x00ffff00L, 0x00000000L, 0x00ffffffL};
+static DWORD Scheme2[] = {0x00ffffffL, 0x00e0e0e0L, 0x00c0c0c0L, 0x00808080L,
+	0x000000L, 0x00808080L, 0x00c0c0c0L, 0x00e0e0e0L};
+static FillDEF Scheme3[] = {
+	{FILL_HLINES, 0x00ffffffL, 1.0, &Sch3_LINE}, {FILL_VLINES, 0x00ffffffL, 1.0, &Sch3_LINE, 0x00ffffffL},
+	{FILL_HVCROSS, 0x00ffffffL, 1.0, &Sch3_LINE}, {FILL_DLINEU, 0x00ffffffL, 1.0, &Sch3_LINE, 0x00ffffffL},
+	{FILL_DLINED, 0x00ffffffL, 1.0, &Sch3_LINE}, {FILL_DCROSS, 0x00ffffffL, 1.0, &Sch3_LINE, 0x00ffffffL},
+	{FILL_BRICKH, 0x00ffffffL, 1.0, &Sch3_LINE}, {FILL_BRICKV, 0x00ffffffL, 1.0, &Sch3_LINE, 0x00ffffffL}};
+static DlgInfo SchBase[] = {
+	{0, 0, 5, CHECKED, GROUP, 0L, 0, 0, 0, 0},
+	{5, 6, 0, 0x0L, RADIO1, (void*)"constant fill", 0, 0, 50, 8},
+	{6, 7, 0, 0x0L, RADIO1, (void*)"colors 1", 0, 10, 50, 8},
+	{7, 8, 0, 0x0L, RADIO1, (void*)"colors 2", 0, 28, 50, 8},
+	{8, 10, 0, 0x0L, RADIO1, (void*)"hatches", 0, 46, 50, 8},
+	{10, 20, 0, OWNDIALOG | TOUCHEXIT, FILLBUTTON, (void*)&Scheme0, 56, 0, 20, 8}};
+
+static int CurrScheme = 1;
+		
+void OD_scheme(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	int x = rec ? rec->left/xbase :0, y = rec ? rec->top/ybase:0;
+	static DlgRoot *Dlg = 0L;
+	POINT *mpos;
+	MouseEvent mev;
+	int i, j, k, res;
+	static DlgInfo *ComSchDlg = 0L;
+
+	switch(cmd) {
+	case OD_CREATE:
+		Dlg = 0L;
+		//set line width: internationalization may be different
+		Sch0_LINE.width = Sch3_LINE.width = defs.GetSize(SIZE_HAIRLINE);
+		ComSchDlg = (DlgInfo*)calloc(40, sizeof(DlgInfo));
+		if(ComSchDlg) {
+			memcpy(ComSchDlg, SchBase, 6 * sizeof(DlgInfo));
+			for (i = 1; i < 6; i++) {
+				ComSchDlg[i].x += x;	ComSchDlg[i].y += y;
+				}
+			for (k = 20, j = 6; k < 50; k += 10) {
+				for(i = 0; i < 8; i++) {
+					ComSchDlg[j].id = i+k;				ComSchDlg[j].next = i+k+1;
+					ComSchDlg[j].type = k < 40 ? COLBUTT : FILLBUTTON;		
+					ComSchDlg[j].flags = OWNDIALOG | TOUCHEXIT;
+					switch(k) {
+					case 20:	ComSchDlg[j].ptype = (void *)&Scheme1[i];	break;
+					case 30:	ComSchDlg[j].ptype = (void *)&Scheme2[i];	break;
+					case 40:	ComSchDlg[j].ptype = (void *)&Scheme3[i];	break;
+						}
+					ComSchDlg[j].x = 5 + i*9 + x;		ComSchDlg[j].y = 18*(k-10)/10 + y;
+					ComSchDlg[j].w = ComSchDlg[j].h = 8;
+					j++;
+					}
+				ComSchDlg[j-1].next = k+10;
+				}
+			ComSchDlg[j-1].next = 0;	ComSchDlg[j-1].flags |= LASTOBJ;
+			Dlg = new DlgRoot(ComSchDlg, 0L);
+			}
+		if(Dlg){
+			if(CurrScheme < 4) Dlg->SetCheck(5+CurrScheme, 0L, true);
+			mev.x =  mev.y = 0;			//activate RootDlg !
+			mev.Action = MOUSE_LBDOWN;
+			mev.StateFlags = 0L;
+			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);	//fake
+			}
+		break;
+	case OD_DELETE:
+		if(Dlg) delete Dlg;
+		Dlg = 0L;
+		if(ComSchDlg) free(ComSchDlg);
+		ComSchDlg = 0L;
+		break;
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		if(Dlg && o) Dlg->DoPlot(o);
+		break;
+	case OD_SELECT:
+		mpos = (POINT*)data;
+		mev.x = mpos->x;			mev.y = mpos->y;
+		mev.Action = MOUSE_LBUP;
+		mev.StateFlags = 0L;
+		if(Dlg){
+			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
+			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
+			res = Dlg->GetResult();
+			if(res >= 10) Dlg->SetCheck(4 + res/10, 0L, true);
+			if(Dlg->GetCheck(5)) CurrScheme = 0;
+			else if(Dlg->GetCheck(7)) CurrScheme = 2;
+			else if(Dlg->GetCheck(8)) CurrScheme = 3;
+			else CurrScheme = 1;
+			for(i = 0; i < 8; i++) {
+				Dlg->GetColor(i+20, &Scheme1[i]);
+				Dlg->GetColor(i+30, &Scheme2[i]);
+				}
+			}
+		break;
+	case OD_MBTRACK:
+		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
+		break;
+		}
+}
+
+FillDEF *GetSchemeFill(int *i)
+{
+	FillDEF curfill = {FILL_NONE, 0x00c0c0c0L, 1.0, 0L};
+	static FillDEF RetFill;
+
+	switch(CurrScheme) {
+	case 0:
+		memcpy(&RetFill, &Scheme0, sizeof(FillDEF));
+		break;
+	default:
+	case 1:
+		curfill.color = Scheme1[*i&0x07];
+		memcpy(&RetFill, &curfill, sizeof(FillDEF));
+		break;
+	case 2:
+		curfill.color = Scheme2[*i&0x07];
+		memcpy(&RetFill, &curfill, sizeof(FillDEF));
+		break;
+	case 3:
+		memcpy(&RetFill, &Scheme3[*i&0x07], sizeof(FillDEF));
+		break;
+		}
+	*i += 1;
+	return &RetFill;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Execute common line properties as owner drawn dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static LineDEF EditLine = {0.0f, 6.0f, 0x0, 0x0}; 
+static DlgInfo LinePropBase[] = {
+	{100, 101, 0, 0x0L, RTEXT, (void*)"line width", 0, 0, 45, 8},
+	{101, 102, 0, TOUCHEXIT, INCDECVAL1, &EditLine.width, 50, 0, 32, 10},
+	{102, 103, 0, 0x0L, LTEXT, 0L, 84, 0, 20, 8},
+	{103, 104, 0, 0x0L, RTEXT, (void*)"line color", 0, 12, 45, 8},
+	{104, 105, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&EditLine.color, 50, 12, 25, 10},
+	{105, 106, 0, 0x0L, LTEXT, (void*)"pattern:", 0, 24, 25, 8},
+	{106, 107, 0, TOUCHEXIT, LINEPAT, (void *)&EditLine, 0, 34, 128, 4},
+	{107, 108, 0, 0x0L, RTEXT, (void*)"pattern length", 0, 47, 45, 8},
+	{108, 109, 0, TOUCHEXIT, INCDECVAL1, &EditLine.patlength, 50, 47, 32, 10},
+	{109, 110, 0, 0x0L, LTEXT, 0L, 84, 47, 20, 8},
+	{110, 0, 0, LASTOBJ | TOUCHEXIT, LINEBUTT, (void *)&EditLine, 0, 67, 128, 20}};
+
+void OD_linedef(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	int x = rec ? rec->left/xbase :0, y = rec ? rec->top/ybase:0;
+	int i, res;
+	POINT *mpos;
+	MouseEvent mev;
+	static DlgRoot *Dlg = 0L;
+	static DlgInfo *LinePropDlg= 0L;
+
+	switch(cmd) {
+	case OD_CREATE:
+		Dlg = 0L;
+		LinePropDlg = (DlgInfo*)calloc(40, sizeof(DlgInfo));
+		if(LinePropDlg) {
+			memcpy(LinePropDlg, LinePropBase, 11 * sizeof(DlgInfo));
+			LinePropDlg[2].ptype = LinePropDlg[9].ptype =
+				(void *)Units[defs.cUnits].display;
+			for (i = 0; i < 11; i++) {
+				LinePropDlg[i].x += x;	LinePropDlg[i].y += y;
+				}
+			Dlg = new DlgRoot(LinePropDlg, 0L);
+			}
+		if(Dlg){
+			Dlg->SetColor(104, EditLine.color);
+			mev.x =  mev.y = 0;			//activate RootDlg !
+			mev.Action = MOUSE_LBDOWN;
+			mev.StateFlags = 0L;
+			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);	//fake
+			}
+		break;
+	case OD_DELETE:
+		if(Dlg) delete Dlg;
+		Dlg = 0L;
+		if(LinePropDlg) free(LinePropDlg);
+		LinePropDlg = 0L;
+		break;
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		if(Dlg && o) Dlg->DoPlot(o);
+		break;
+	case OD_SELECT:
+		mpos = (POINT*)data;
+		mev.x = mpos->x;			mev.y = mpos->y;
+		mev.Action = MOUSE_LBUP;
+		mev.StateFlags = 0L;
+		if(Dlg){
+			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
+			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
+			res = Dlg->GetResult();
+			switch (res) {
+			case 101:						//line width changed
+			case 108:						//pattern length changed
+			case 110:						//preview button
+				Dlg->GetValue(101, &EditLine.width);
+				Dlg->GetValue(108, &EditLine.patlength);
+			case 104:						//color button
+				Dlg->GetColor(104, &EditLine.color);
+			case 106:						//line pattern
+				Dlg->DoPlot(0);
+				break;
+				}
+			}
+		break;
+	case OD_MBTRACK:
+		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
+		break;
+	case OD_SETLINE:
+		if(data) {
+			memcpy(&EditLine, data, sizeof(LineDEF));
+			if(Dlg && LinePropDlg) Dlg->DoPlot(0);
+			}
+		break;
+	case OD_GETLINE:
+		if(Dlg) {
+				Dlg->GetValue(101, &EditLine.width);
+				Dlg->GetValue(108, &EditLine.patlength);
+			}
+		if(data) memcpy(data, &EditLine, sizeof(LineDEF));
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Execute common FILL properties as owner drawn button
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static LineDEF ODLine = {0.0f, 6.0f, 0x0, 0x0}; 
+static LineDEF ODFillLine = {0.0f, 6.0f, 0x0, 0x0}; 
+static FillDEF ODFill = {FILL_NONE, 0x00ffffffL, 1.0f, &ODFillLine, 0x00ffffffL}; 
+static DlgInfo FillPropBase[] = {
+	{100, 101, 0, 0x0L, RTEXT, (void*)"outline width", 0, 0, 40, 8},
+	{101, 102, 0, 0x0L, INCDECVAL1, &ODLine.width, 42, 0, 32, 10},
+	{102, 103, 0, 0x0L, LTEXT, 0L, 76, 0, 20, 8},
+	{103, 104, 0, 0x0L, RTEXT, (void*)"outline color", 0, 12, 40, 8},
+	{104, 105, 0, OWNDIALOG, COLBUTT, (void *)&ODLine.color, 42, 12, 25, 10},
+	{105, 106, 0, 0x0L, RTEXT,(void*)"fill color" , 0, 24, 40, 8},
+	{106, 107, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&ODFill.color, 42, 24, 25, 10},
+	{107, 108, 0, 0x0L, RTEXT, (void*)"pattern", 0, 36, 40, 8},
+	{108, 0, 0, LASTOBJ | TOUCHEXIT | OWNDIALOG, FILLBUTTON, (void*)&ODFill, 42, 36, 25, 10}};
+
+void OD_filldef(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	int x = rec ? rec->left/xbase :0, y = rec ? rec->top/ybase:0;
+	int i, res;
+	POINT *mpos;
+	MouseEvent mev;
+	static DlgRoot *Dlg = 0L;
+	static DlgInfo *FillPropDlg= 0L;
+
+	switch(cmd) {
+	case OD_CREATE:
+		Dlg = 0L;
+		FillPropDlg = (DlgInfo*)calloc(9, sizeof(DlgInfo));
+		if(FillPropDlg) {
+			memcpy(FillPropDlg, FillPropBase, 9 * sizeof(DlgInfo));
+			FillPropDlg[2].ptype = (void *) Units[defs.cUnits].display;
+			for (i = 0; i < 9; i++) {
+				FillPropDlg[i].x += x;	FillPropDlg[i].y += y;
+				}
+			Dlg = new DlgRoot(FillPropDlg, 0L);
+			}
+		if(Dlg){
+			Dlg->SetColor(104, ODLine.color);
+			Dlg->SetColor(106, ODFill.color);
+			mev.x =  mev.y = 0;			//activate RootDlg !
+			mev.Action = MOUSE_LBDOWN;
+			mev.StateFlags = 0L;
+			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);	//fake
+			}
+		break;
+	case OD_DELETE:
+		if(Dlg) delete Dlg;
+		Dlg = 0L;
+		if(FillPropDlg) free(FillPropDlg);
+		FillPropDlg = 0L;
+		break;
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		if(Dlg && o) Dlg->DoPlot(o);
+		break;
+	case OD_SELECT:
+		mpos = (POINT*)data;
+		mev.x = mpos->x;			mev.y = mpos->y;
+		mev.Action = MOUSE_LBUP;
+		mev.StateFlags = 0L;
+		if(Dlg){
+			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
+			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
+			res = Dlg->GetResult();
+			switch (res) {
+			case 106:					//fill color changed
+				Dlg->GetColor(106, &ODFill.color);
+				Dlg->DoPlot(NULL);
+				break;
+			case 108:					//copy color from pattern dialog
+				Dlg->SetColor(106, ODFill.color);
+				break;
+				}
+			}
+		break;
+	case OD_MBTRACK:
+		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
+		break;
+	case OD_SETLINE:
+		if(data) {
+			memcpy(&ODLine, data, sizeof(LineDEF));
+			if(Dlg && FillPropDlg) Dlg->DoPlot(0);
+			}
+		break;
+	case OD_GETLINE:
+		if(Dlg) {
+			Dlg->GetValue(101, &ODLine.width);
+			Dlg->GetColor(104, &ODLine.color);
+			}
+		if(data) memcpy(data, &ODLine, sizeof(LineDEF));
+		break;
+	case OD_SETFILL:
+		if(data) {
+			memcpy(&ODFill, data, sizeof(FillDEF));
+			if(ODFill.hatch) memcpy(&ODFillLine, ((FillDEF*)data)->hatch, sizeof(LineDEF));
+			ODFill.hatch = &ODFillLine;
+			if(Dlg) Dlg->SetColor(106, ODFill.color);
+			}
+		break;
+	case OD_GETFILL:
+		Dlg->GetColor(106, &ODFill.color);
+		if(data) memcpy(data, &ODFill, sizeof(FillDEF));
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Execute paper size properiteis as owner drawn button
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+typedef struct{
+	char *name;
+	double mwidth;		double mheight;
+	double iwidth;		double iheight;
+}paper;
+
+static paper p_formats[] = {
+	{"A0", 841, 1189, 33.11, 46.81},	{"A1", 594, 841, 23.39, 33.11},
+	{"A2", 420, 594, 16.54, 23.39},		{"A3", 297, 420, 11.69, 16.54},
+	{"A4", 210, 297, 8.27, 11.69},		{"A5", 148, 210, 5.83, 8.27},
+	{"A6", 105, 148, 4.13, 5.83},		{"A7", 74, 105, 2.91, 4.13},
+	{"B0", 1030, 1456, 40.55, 57.32},	{"B1", 728, 1030, 28.66, 40.55},
+	{"B2", 515, 728, 20.28, 28.66},		{"B3", 364, 515, 14.33, 20.28},
+	{"B4", 257, 364, 10.12, 14.33},		{"B5", 182, 257, 7.17, 10.12},
+	{"B6", 128, 182, 5.04, 7.17},		{"B7", 91, 128, 3.58, 5.04},
+	{"Executive", 191, 254, 7.52, 10},	{"Folio", 210, 330, 8.27, 12.99},
+	{"Ledger", 432, 279, 17.01, 10.98},	{"Legal", 216, 356, 8.5, 14.02},
+	{"Letter", 216, 279, 8.5, 10.98},
+	{"Custom", 210, 297, 8.27, 11.69}};
+static int cpformats = sizeof(p_formats)/sizeof(paper);
+static double cu_width = 123.0, cu_height = 234.0;
+static int pg_sel = 4;
+
+static DlgInfo PaperDlg[] = {
+	{100, 110, 0, 0x0L, NONE, (void*)0L, 0, 0, 0, 0},
+	{110, 120, 0, TOUCHEXIT, LISTBOX1, (void*)0L, 0, 15, 100, 70},
+	{120, 130, 0, NOEDIT, EDTEXT, (void*)"n.a.", 0, 0, 100, 10},
+	{130, 0, 132, CHECKED, GROUP, 0, 0, 0, 0},
+	{132, 133, 0, 0x0L, EDVAL1, (void*)&cu_width, 27, 0, 25, 10},
+	{133, 134, 0, 0x0L, LTEXT, (void*)"x", 52, 0, 5, 8},
+	{134, 135, 0, 0x0L, EDVAL1, (void*)&cu_height, 58, 0, 25, 10},
+	{135, 0, 0, LASTOBJ, LTEXT, (void*)0L, 83, 0, 14, 8}};
+
+void OD_paperdef(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	int x = rec ? rec->left/xbase :0, y = rec ? rec->top/ybase:0;
+	int i, res;
+	POINT *mpos;
+	MouseEvent mev;
+	static DlgRoot *Dlg = 0L;
+	static DlgInfo *PaperPropDlg= 0L;
+	char **dispsize = 0L;
+	double dtmp;
+
+	switch(cmd) {
+	case OD_CREATE:
+		GetPaper(&cu_width, &cu_height);
+		Dlg = 0L;
+		if((PaperPropDlg = (DlgInfo*)calloc(8, sizeof(DlgInfo))) && 
+			(dispsize = (char**)calloc(cpformats+1, sizeof(char*)))) {
+			memcpy(PaperPropDlg, PaperDlg, 8 * sizeof(DlgInfo));
+			for (i = 0; i < cpformats; i++) {
+				if(i < cpformats -1) {
+					switch(defs.cUnits) {
+					case 1:
+#ifdef USE_WIN_SECURE
+						sprintf_s(TmpTxt, TMP_TXT_SIZE, " %s  (%.1lf x %.1lf cm)", p_formats[i].name, 
+							p_formats[i].mwidth/10.0, p_formats[i].mheight/10.0);
+#else
+						sprintf(TmpTxt, " %s  (%.1lf x %.1lf cm)", p_formats[i].name, 
+							p_formats[i].mwidth/10.0, p_formats[i].mheight/10.0);
+#endif
+						break;
+					case 2:
+#ifdef USE_WIN_SECURE
+						sprintf_s(TmpTxt, TMP_TXT_SIZE, " %s  (%.2lf x %.2lf inch)", p_formats[i].name, 
+							p_formats[i].iwidth, p_formats[i].iheight);
+#else
+						sprintf(TmpTxt, " %s  (%.2lf x %.2lf inch)", p_formats[i].name, 
+							p_formats[i].iwidth, p_formats[i].iheight);
+#endif
+						break;
+					default:
+#ifdef USE_WIN_SECURE
+						sprintf_s(TmpTxt, TMP_TXT_SIZE, " %s  (%.0lf x %.0lf mm)", p_formats[i].name, 
+							p_formats[i].mwidth, p_formats[i].mheight);
+#else
+						sprintf(TmpTxt, " %s  (%.0lf x %.0lf mm)", p_formats[i].name, 
+							p_formats[i].mwidth, p_formats[i].mheight);
+#endif
+						break;
+						}
+					dispsize[i] = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
+					}
+				else if(dispsize[i] = (char*)malloc(15*sizeof(char)))
+					rlp_strcpy(dispsize[i], 15, " Custom");
+				}
+			PaperPropDlg[1].ptype = (void*)dispsize;
+			PaperPropDlg[7].ptype = (void*)Units[defs.cUnits].display;
+			if(pg_sel <(cpformats -1))PaperPropDlg[3].flags |= HIDDEN;
+			for (i = 0; i < 8; i++) {
+				PaperPropDlg[i].x += x;	PaperPropDlg[i].y += y;
+				}
+			if(Dlg = new DlgRoot(PaperPropDlg, 0L)){
+				Dlg->Activate(120, false);
+				if(Dlg->ItemCmd(110, CMD_FINDTEXT, (void*)dispsize[pg_sel])){
+					if(pg_sel < (cpformats-1)) Dlg->SetText(120, dispsize[pg_sel]);
+					else Dlg->SetText(120, dispsize[pg_sel]+1);
+					dtmp = ((double)pg_sel)/((double) cpformats +10.0);
+					Dlg->ItemCmd(110, CMD_SETSCROLL, (void*)&dtmp);
+					}
+				}
+			}
+		break;
+	case OD_DELETE:
+		if(Dlg) delete Dlg;
+		Dlg = 0L;
+		if(PaperPropDlg) free(PaperPropDlg);
+		PaperPropDlg = 0L;
+		if(dispsize) {
+			for (i = 0; i < 20; i++) if(dispsize[i])free(dispsize[i]);
+			free(dispsize);
+			dispsize = 0L;
+			}
+		break;
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		if(Dlg && o) Dlg->DoPlot(o);
+		break;
+	case OD_SELECT:
+		mpos = (POINT*)data;
+		mev.x = mpos->x;			mev.y = mpos->y;
+		mev.Action = MOUSE_LBUP;
+		mev.StateFlags = 0L;
+		if(Dlg){
+			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
+			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
+			res = Dlg->GetResult();
+			if(res == 110 && Dlg->GetText(110, TmpTxt, TMP_TXT_SIZE)){
+				if(Dlg->GetInt(110, &i)) {
+					if(i == cpformats-1){
+						Dlg->ShowItem(130, true);
+						Dlg->SetText(120, TmpTxt+1);
+						}
+					else {
+						Dlg->ShowItem(130, false);
+						Dlg->SetText(120, TmpTxt);
+						}
+					}
+				Dlg->DoPlot(o);
+				}
+			}
+		break;
+	case OD_MBTRACK:
+		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
+		break;
+	case OD_ACCEPT:
+		if(Dlg) {
+			if(Dlg->GetInt(110, &pg_sel) && pg_sel == (i=cpformats-1)){
+				Dlg->GetValue(132, &cu_width);
+				Dlg->GetValue(134, &cu_height);
+				switch(defs.cUnits){
+				case 1:
+					p_formats[i].mwidth = cu_width*10.0;
+					p_formats[i].mheight = cu_height*10.0;
+					p_formats[i].iwidth = cu_width/2.54;
+					p_formats[i].iheight = cu_height/2.54;
+					break;
+				case 2:
+					p_formats[i].mwidth = cu_width*25.4;
+					p_formats[i].mheight = cu_height*25.4;
+					p_formats[i].iwidth = cu_width;
+					p_formats[i].iheight = cu_height;
+					break;
+				default:
+					p_formats[i].mwidth = cu_width;
+					p_formats[i].mheight = cu_height;
+					p_formats[i].iwidth = cu_width/25.4;
+					p_formats[i].iheight = cu_height/25.4;
+					break;
+					}
+				}
+			}
+		break;
+		}
+}
+
+//Find a suitable paper size with width w and height h
+void FindPaper(double w, double h, double tol)
+{
+	int i;
+	double lw, hw, lh, hh;
+
+	lw = w *(1.0-tol);		hw = w *(1.0+tol);
+	lh = h *(1.0-tol);		hh = h *(1.0+tol);
+	for(i = 0; i < cpformats; i++) {
+		switch(defs.cUnits) {
+		case 1:					//units are cm
+			if(p_formats[i].mwidth >= lw*10.0 && p_formats[i].mwidth <= hw*10.0 &&
+				p_formats[i].mheight >= lh*10.0 && p_formats[i].mheight <= hh*10.0){
+					pg_sel = i;
+					return;
+				}
+			break;
+		case 2:					//units are inch
+			if(p_formats[i].iwidth >= lw && p_formats[i].iwidth <= hw &&
+				p_formats[i].iheight >= lh && p_formats[i].iheight <= hh){
+					pg_sel = i;
+					return;
+				}
+			break;
+		default:				//units are mm
+			if(p_formats[i].mwidth >= lw && p_formats[i].mwidth <= hw &&
+				p_formats[i].mheight >= lh && p_formats[i].mheight <= hh){
+					pg_sel = i;
+					return;
+				}
+			break;
+			}
+		}
+	//The paper format is non standard
+	pg_sel = i = cpformats-1;
+	switch(defs.cUnits){
+	case 1:
+		p_formats[i].mwidth = w*10.0;		p_formats[i].mheight = h*10.0;
+		p_formats[i].iwidth = w/2.54;		p_formats[i].iheight = h/2.54;
+		break;
+	case 2:
+		p_formats[i].mwidth = w*25.4;		p_formats[i].mheight = h*25.4;
+		p_formats[i].iwidth = w;			p_formats[i].iheight = h;
+		break;
+	default:
+		p_formats[i].mwidth = w;			p_formats[i].mheight = h;
+		p_formats[i].iwidth = w/25.4;		p_formats[i].iheight = h/25.4;
+		break;
+		}
+}
+
+//Get (default) paper size
+bool GetPaper(double *w, double *h)
+{
+	switch(defs.cUnits) {
+		case 1:					//units are cm
+			*w = p_formats[pg_sel].mwidth/10.0;
+			*h = p_formats[pg_sel].mheight/10.0;
+			break;
+		case 2:					//units are inch
+			*w = p_formats[pg_sel].iwidth;
+			*h = p_formats[pg_sel].iheight;
+			break;
+		default:				//units are mm
+			*w = p_formats[pg_sel].mwidth;
+			*h = p_formats[pg_sel].mheight;
+			break;
+		}
+	return true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Select axis for plot as owner drawn button
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static DlgInfo PlotsDlg[] = {
+	{100, 110, 0, 0x0L, NONE, (void*)0L, 0, 0, 0, 0},
+	{110, 150, 0, TOUCHEXIT, LISTBOX1, (void*)0L, 20, 50, 100, 70},
+	{150, 0, 0, LASTOBJ, LTEXT, (void*)"Apply this axis to plot:", 20, 35, 50, 9}};
+static int axisplot_sel = 0;
+
+void OD_axisplot(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id)
+{
+	static DlgInfo *PlotsPropDlg= 0L;
+	static DlgRoot *Dlg = 0L;
+	int i, res;
+	POINT *mpos;
+	MouseEvent mev;
+	static char **names = 0L;
+
+	switch(cmd) {
+	case OD_CREATE:
+		Dlg = 0L;
+		if(PlotsPropDlg = (DlgInfo*)calloc(3, sizeof(DlgInfo))){
+			memcpy(PlotsPropDlg, PlotsDlg, 3 * sizeof(DlgInfo));
+			PlotsPropDlg[1].ptype = (void*)names;
+			Dlg = new DlgRoot(PlotsPropDlg, 0L);
+			}
+		axisplot_sel = 0;
+		break;
+	case OD_DELETE:
+		if(Dlg) delete Dlg;
+		Dlg = 0L;
+		if(PlotsPropDlg) free(PlotsPropDlg);
+		PlotsPropDlg = 0L;
+		break;
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		if(Dlg && o) Dlg->DoPlot(o);
+		break;
+	case OD_SELECT:
+		mpos = (POINT*)data;
+		mev.x = mpos->x;			mev.y = mpos->y;
+		mev.Action = MOUSE_LBUP;
+		mev.StateFlags = 0L;
+		if(Dlg){
+			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
+			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
+			res = Dlg->GetResult();
+			if(res == 110 && Dlg->GetText(110, TmpTxt, TMP_TXT_SIZE)){
+				if(Dlg->GetInt(110, &i)) {
+					//get selection
+					}
+				Dlg->DoPlot(o);
+				}
+			Dlg->GetInt(110, &axisplot_sel);
+			}
+		break;
+	case OD_MBTRACK:
+		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
+		break;
+	case OD_ACCEPT:
+		if(data) names = (char**)data;
+		else if(Dlg) Dlg->GetInt(110, &axisplot_sel);
+		if(o) *((int*)o) = axisplot_sel;
+		}
+}
diff --git a/TheDialog.h b/TheDialog.h
index 7c88fb8..3c6f0dc 100755
--- a/TheDialog.h
+++ b/TheDialog.h
@@ -1,557 +1,563 @@
-//TheDialog.h, Copyright (c) 2001-2007 R.Lackner
-//Definitions for TheDialog.cpp
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-// declarations relevant for user
-typedef struct {
-	unsigned int id;			//consecutive number
-	unsigned int next;		//id of next object
-	unsigned int first;		//number of first child
-	unsigned long flags;		//any status flag bits
-	unsigned int type;		//identifier of dialog item
-	void *ptype;				//pointer to information dependend on type
-	unsigned int x, y, w, h;	//start coordinates	
-} DlgInfo;
-
-//defining a tab
-typedef struct {
-	unsigned int x1, x2;		//relative position to left border
-	unsigned int height;		//the height
-	char *text;					//descriptor shown on tab
-} TabSHEET;
-
-//types of dialogs
-enum {NONE, PUSHBUTTON, ARROWBUTT, COLBUTT, FILLBUTTON, SHADE3D, LINEBUTT, SYMBUTT,
-	FILLRADIO, SYMRADIO, CHECKBOX, RADIO0, RADIO1, RADIO2, LTEXT, RTEXT, CTEXT, EDTEXT, 
-	RANGEINPUT, EDVAL1, INCDECVAL1, HSCROLL, VSCROLL, TXTHSP, ICON, GROUP, 
-	GROUPBOX, SHEET, ODBUTTON, LISTBOX1, TREEVIEW, LINEPAT, TEXTBOX, CHECKPIN, TRASH,
-	CONFIG};
-
-//flags
-#define CHECKED      0x00000001L
-#define TOUCHEXIT    0x00000002L
-#define TOUCHSELEXIT 0x00000004L
-#define ISRADIO      0x00000008L
-#define ISPARENT     0x00000010L
-#define OWNDIALOG    0x00000020L
-#define DEFAULT      0x00000040L
-#define HIDDEN       0x00000080L
-#define NOSELECT     0x00000100L
-#define HREF         0x00000200L
-#define NOEDIT       0x00000400L
-#define LASTOBJ      0x00100000L
-
-#define EXRADIO      TOUCHEXIT|ISRADIO
-#define ODEXIT       OWNDIALOG|TOUCHEXIT
-
-//owner draw button commands
-enum {OD_CREATE, OD_DELETE, OD_DRAWNORMAL, OD_DRAWSELECTED, OD_SELECT, OD_MBTRACK,
-	OD_SETLINE, OD_GETLINE, OD_SETFILL, OD_GETFILL, OD_ACCEPT};
-
-class tag_DlgObj{
-public:
-	RECT cr, hcr;
-	int Id, type;
-	unsigned long flags;
-	bool bChecked, bLBdown, bActive;
-
-	virtual bool Command(int cmd, void *tmpl, anyOutput *o){return false;};
-	virtual void DoPlot(anyOutput *o) {return;};
-	virtual bool Select(int x, int y, anyOutput *o) {return false;};
-	virtual bool GetColor(int id, DWORD *color) {return false;};
-	virtual void SetColor(int id, DWORD color) {return;};
-	virtual bool GetValue(int id, double *val) {return false;};
-	virtual bool GetInt(int id, int *val) {return false;};
-	virtual bool SetCheck(int id, anyOutput *o, bool state) {return false;};
-	virtual bool GetCheck(int Id) {return bChecked;};
-	virtual bool GetText(int id, char *txt, int size) {return false;};
-	virtual void MBtrack(MouseEvent *mev, anyOutput *o) {return;};
-	virtual void Activate(int id, bool active){return;};
-};
-
-class Dialog:public tag_DlgObj {
-public:
-	tag_DlgObj *parent;
-	LineDEF Line;
-	FillDEF Fill;
-	TextDEF TextDef;
-
-	Dialog(tag_DlgObj *par, DlgInfo * desc, RECT rec);
-	virtual bool Select(int x, int y, anyOutput *o);
-	bool SetCheck(int id, anyOutput *o, bool state);
-	void MBtrack(MouseEvent *mev, anyOutput *o);
-	virtual void Activate(int id, bool active);
-};
-
-typedef struct {
-	unsigned int id;			//consecutive number
-	unsigned int next;		//id of next object
-	unsigned int first;		//number of first child
-	unsigned long flags;		//any status flag bits
-	Dialog *dialog;				//pointer to dialog object
-} DlgTmpl;
-
-class DlgRoot:public tag_DlgObj {
-public:
-	anyOutput *CurrDisp;		//the dialog's output class
-	void *hDialog;				//handle to the dialog window/widget
-
-	DlgRoot(DlgInfo *tmpl, DataObj *d);
-	~DlgRoot();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool CurUpDown(int cmd);
-	bool GetColor(int id, DWORD *color);
-	void SetColor(int id, DWORD color);
-	bool GetValue(int id, double *val);
-	bool GetInt(int id, int *val);
-	bool SetCheck(int id, anyOutput *o, bool state);
-	bool GetCheck(int Id);
-	void Activate(int id, bool active);
-	bool GetText(int id, char *txt, int size);
-	bool SetText(int id, char *txt);
-	bool SetValue(int id, double val);
-	bool TextStyle(int id, int style);
-	bool TextFont(int id, int font);
-	bool TextSize(int id, int size);
-	bool ShowItem(int id, bool show);
-	int GetResult();
-	int FindIndex(unsigned short id);
-	void ForEach(int cmd, int start, anyOutput *o);
-	bool ItemCmd(int id, int cmd, void *tmpl);
-	anyOutput *GetOutputClass(){return CurrDisp;};
-
-private:
-	int res_q[256], res_put, res_get, cDlgs, cContinue;
-	anyOutput *ParentOut;
-	DataObj *data;
-	Dialog *oldFocus, *oldDefault, *oldTabStop;
-	bool bActive, bRedraw;
-	GraphObj *c_go;
-	Dialog **tabstops, *mrk_item;
-	DlgTmpl **dlg;
-	MouseEvent *mev;
-};
-
-class PushButton:public Dialog {
-public:
-	PushButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
-	~PushButton();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-
-private:
-	char *Text;
-};
-
-class TextBox:public Dialog {
-public:
-	TextBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
-	~TextBox();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	void MBtrack(MouseEvent *mev, anyOutput *o);
-	bool GetText(int id, char *txt, int size);
-
-private:
-	TextFrame *cont;
-};
-
-class ArrowButton:public Dialog {
-public:
-	ArrowButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, int *which);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-
-private:
-	int direct;
-};
-
-class ColorButton:public Dialog {
-public:
-	ColorButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, DWORD *color);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	bool GetColor(int id, DWORD *color) {*color = col; return true;};
-	void SetColor(int id, DWORD color) {col = color; return;};
-
-private:
-	unsigned long col;
-};
-
-class FillButton:public Dialog {
-public:
-	FillButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, FillDEF *fill);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	bool GetColor(int id, DWORD *color) {*color = CurrFill->color; return true;};
-
-private:
-	FillDEF *CurrFill;
-};
-
-class Shade3D:public Dialog {
-public:
-	Shade3D(tag_DlgObj *par, DlgInfo * desc, RECT rec, FillDEF *fill);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	bool GetColor(int id, DWORD *color) {*color = CurrFill->color; return true;};
-
-private:
-	FillDEF *CurrFill;
-};
-
-class LineButton:public Dialog {
-public:
-	LineButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, LineDEF *line);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-
-private:
-	LineDEF *CurrLine;
-	POINT pts[2];
-};
-
-class SymButton:public Dialog {
-public:
-	SymButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, Symbol **sym);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-
-private:
-	Symbol **symbol;
-};
-
-class FillRadioButt:public Dialog {
-public:
-	FillRadioButt(tag_DlgObj *par, DlgInfo * desc, RECT rec, unsigned int pattern);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-};
-
-class SymRadioButt:public Dialog {
-public:
-	SymRadioButt(tag_DlgObj *par, DlgInfo * desc, RECT rec, int *type);
-	~SymRadioButt();
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-
-private:
-	Symbol *Sym;
-};
-
-class CheckBox:public Dialog {
-public:
-	CheckBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
-	~CheckBox();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-
-private:
-	char *Text;
-};
-
-class CheckPin:public Dialog {
-public:
-	CheckPin(tag_DlgObj *par, DlgInfo * desc, RECT rec);
-	~CheckPin();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-};
-
-class Trash:public Dialog {
-public:
-	Trash(tag_DlgObj *par, DlgInfo * desc, RECT rec);
-	~Trash();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-};
-
-class Config:public Dialog {
-public:
-	Config(tag_DlgObj *par, DlgInfo * desc, RECT rec);
-	~Config();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-};
-
-class RadioButton:public Dialog {
-public:
-	RadioButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
-	~RadioButton();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	void SetColor(int id, DWORD color);
-
-private:
-	char *Text;
-};
-
-class Text:public Dialog {
-public:
-	Text(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
-	~Text();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	void SetColor(int id, DWORD color);
-	void MBtrack(MouseEvent *mev, anyOutput *o);
-
-private:
-	char *txt;
-};
-
-class InputText:public Dialog {
-public:
-	EditText *Text;
-
-	InputText(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
-	~InputText();
-	virtual bool Command(int cmd, void *tmpl, anyOutput *o);
-	virtual void DoPlot(anyOutput *o);
-	virtual bool Select(int x, int y, anyOutput *o);
-	bool GetValue(int id, double *val);
-	bool GetInt(int id, int *val);
-	bool GetText(int id, char *txt, int size);
-	virtual void MBtrack(MouseEvent *mev, anyOutput *o);
-	virtual void Activate(int id, bool active);
-
-private:
-	anyOutput *Disp;
-};
-
-class RangeInput:public InputText {
-public:
-	RangeInput(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text, DataObj *d);
-	~RangeInput();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	void Activate(int id, bool active);
-
-private:
-	DataObj *data;
-};
-
-class InputValue:public InputText {
-public:
-	InputValue(tag_DlgObj *par, DlgInfo * desc, RECT rec, double *value);
-	~InputValue();
-};
-
-class IncDecValue:public InputText {
-public:
-	IncDecValue(tag_DlgObj *par, DlgInfo * desc, RECT rec, double *value);
-	~IncDecValue();
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	void MBtrack(MouseEvent *mev, anyOutput *o);
-
-private:
-	ArrowButton *butts[2];
-};
-
-class TxtHSP:public Dialog {
-public:
-	TxtHSP(tag_DlgObj *par, DlgInfo *desc, RECT rec, int *align);
-	~TxtHSP();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	bool GetInt(int id, int *val);
-	void MBtrack(MouseEvent *mev, anyOutput *o);
-
-private:
-	TextDEF txt;
-	RadioButton *butts[9];
-	DlgInfo *d2;
-};
-
-class SlideRect:public Dialog{
-public:
-	int sLine;
-
-	SlideRect(tag_DlgObj *par, DlgInfo *desc, RECT rec, bool isVert);
-	~SlideRect();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	bool GetValue(int id, double *val);
-	void MBtrack(MouseEvent *mev, anyOutput *o);
-
-private:
-	int dx, dy, w, h;
-	bool bV, puSel, pdSel;
-	RECT buttrc, puRC, pdRC;
-};
-
-class ScrollBar:public Dialog{
-public:
-	int sLine, sPage;
-
-	ScrollBar(tag_DlgObj *par, DlgInfo *desc, RECT rec, bool isVert);
-	~ScrollBar();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	bool GetValue(int id, double *val);
-	void MBtrack(MouseEvent *mev, anyOutput *o);
-
-private:
-	ArrowButton *butts[3];
-	SlideRect *slrc;
-};
-
-class Icon:public Dialog {
-public:
-	Icon(tag_DlgObj *par, DlgInfo * desc, RECT rec, int *ico);
-	void DoPlot(anyOutput *o);
-
-private:
-	int icon;
-};
-
-class Group:public Dialog {
-public:
-	Group(tag_DlgObj *par, DlgInfo * desc, RECT rec);
-	~Group();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	void MBtrack(MouseEvent *mev, anyOutput *o);
-
-	InputText *TextFocus;
-	Dialog **Children;
-	int numChildren;
-};
-
-class GroupBox:public Group {
-public:
-	GroupBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *txt);
-	~GroupBox();
-	void DoPlot(anyOutput *o);
-
-private:
-	char *Text;
-};
-
-class TabSheet:public Group {
-public:
-	TabSheet(tag_DlgObj *par, DlgInfo * desc, RECT rec, TabSHEET *sh, DataObj *d);
-	~TabSheet();
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-
-private:
-	DataObj *data;
-	RECT rctab;
-	char *Text;
-};
-
-class ODbutton:public Dialog {
-public:
-	ODbutton(tag_DlgObj *par, DlgInfo *des, RECT rec, void*proc);
-	~ODbutton();
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	void MBtrack(MouseEvent *mev, anyOutput *o);
-
-private:
-	void (*ODexec)(int cmd, void *par, RECT *rec, anyOutput *o,
-		void *data, int id);
-};
-
-class Listbox:public Dialog {
-public:
-	Listbox(tag_DlgObj *par, DlgInfo *des, RECT rec, char **list);
-	~Listbox();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	bool GetInt(int id, int *val);
-	bool GetText(int id, char *txt, int size);
-	void MBtrack(MouseEvent *mev, anyOutput *o);
-
-private:
-	ScrollBar *sb;
-	anyOutput *bmp;
-	char **strings;
-	double spos;
-	int ns, bmh, startY, cl;
-
-	bool CreateBitMap(anyOutput *tmpl);
-};
-
-class Treeview:public Dialog {
-public:
-	Treeview(tag_DlgObj *par, DlgInfo *des, RECT rec, GraphObj *g);
-	~Treeview();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void DoPlot(anyOutput *o);
-	bool Select(int x, int y, anyOutput *o);
-	bool GetInt(int id, int *val);
-	void MBtrack(MouseEvent *mev, anyOutput *o);
-
-private:
-	ScrollBar *sb;
-	anyOutput *bmp;
-	double spos;
-	int ns, bmh, bmw, startY, cl;
-	GraphObj *go;
-	ObjTree *ot;
-};
-
-class LinePat:public Dialog {
-public:
-	LinePat(tag_DlgObj *par, DlgInfo *desc, RECT rec, LineDEF *Line);
-	void DoPlot(anyOutput *o);
-	void MBtrack(MouseEvent *mev, anyOutput *o);
-
-private:
-	bool bDraw;
-	DWORD *pPattern;
-};
-
-//prototypes TheDialog.cpp
-bool UseRangeMark(DataObj *d, int type, char* =0L, char* =0L, char* =0L, char* =0L,
-	char* =0L, char* =0L, char* =0L, char* =0L, char* =0L, char* =0L, char* =0L);
-int com_StackDlg(int,DlgRoot*,AccRange**,int*,char***,int*,AccRange**,bool*,int*,int*,bool*);
-DlgInfo *CompileDialog(char* tmpl, void **ptypes);
-
-//prototypes ODbutton.cpp
-void OD_DrawOrder(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
-int ExecDrawOrderButt(GraphObj *parent, GraphObj *obj, int id);
-void OD_LineStyleTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
-void OD_ErrBarTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
-void OD_WhiskerTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
-void OD_PolarTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
-void OD_PieTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
-void OD_AxisDesc3D(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
-void OD_BreakTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
-void OD_PlotTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
-void OD_AxisTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
-void OD_AxisTempl3D(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
-void OD_NewAxisTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
-void OD_NewAxisTempl3D(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+//TheDialog.h, Copyright (c) 2001-2008 R.Lackner
+//Definitions for TheDialog.cpp
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// declarations relevant for user
+typedef struct {
+	unsigned int id;		//consecutive number
+	unsigned int next;		//id of next object
+	unsigned int first;		//number of first child
+	unsigned long flags;		//any status flag bits
+	unsigned int type;		//identifier of dialog item
+	void *ptype;			//pointer to information dependend on type
+	int x, y, w, h;			//start coordinates, width, height	
+} DlgInfo;
+
+//defining a tab
+typedef struct {
+	unsigned int x1, x2;		//relative position to left border
+	unsigned int height;		//the height
+	char *text;					//descriptor shown on tab
+} TabSHEET;
+
+//types of dialogs
+enum {NONE, PUSHBUTTON, ARROWBUTT, COLBUTT, FILLBUTTON, SHADE3D, LINEBUTT, SYMBUTT,
+	FILLRADIO, SYMRADIO, CHECKBOX, RADIO0, RADIO1, RADIO2, LTEXT, RTEXT, CTEXT, EDTEXT, 
+	RANGEINPUT, EDVAL1, INCDECVAL1, HSCROLL, VSCROLL, TXTHSP, ICON, GROUP, 
+	GROUPBOX, SHEET, ODBUTTON, LISTBOX1, TREEVIEW, LINEPAT, TEXTBOX, CHECKPIN, TRASH,
+	CONFIG};
+
+//flags
+#define CHECKED      0x00000001L
+#define TOUCHEXIT    0x00000002L
+#define TOUCHSELEXIT 0x00000004L
+#define ISRADIO      0x00000008L
+#define ISPARENT     0x00000010L
+#define OWNDIALOG    0x00000020L
+#define DEFAULT      0x00000040L
+#define HIDDEN       0x00000080L
+#define NOSELECT     0x00000100L
+#define HREF         0x00000200L
+#define NOEDIT       0x00000400L
+#define LASTOBJ      0x00100000L
+
+#define EXRADIO      TOUCHEXIT|ISRADIO
+#define ODEXIT       OWNDIALOG|TOUCHEXIT
+
+//owner draw button commands
+enum {OD_CREATE, OD_DELETE, OD_DRAWNORMAL, OD_DRAWSELECTED, OD_SELECT, OD_MBTRACK,
+	OD_SETLINE, OD_GETLINE, OD_SETFILL, OD_GETFILL, OD_ACCEPT};
+
+class tag_DlgObj{
+public:
+	RECT cr, hcr;
+	int Id, type;
+	unsigned long flags;
+	bool bChecked, bLBdown, bActive, bModal;
+
+	virtual bool Command(int cmd, void *tmpl, anyOutput *o){return false;};
+	virtual void DoPlot(anyOutput *o) {return;};
+	virtual bool Select(int x, int y, anyOutput *o) {return false;};
+	virtual bool GetColor(int id, DWORD *color) {return false;};
+	virtual void SetColor(int id, DWORD color) {return;};
+	virtual bool GetValue(int id, double *val) {return false;};
+	virtual bool GetInt(int id, int *val) {return false;};
+	virtual bool SetCheck(int id, anyOutput *o, bool state) {return false;};
+	virtual bool GetCheck(int Id) {return bChecked;};
+	virtual bool GetText(int id, char *txt, int size) {return false;};
+	virtual void MBtrack(MouseEvent *mev, anyOutput *o) {return;};
+	virtual void Activate(int id, bool active){return;};
+};
+
+class Dialog:public tag_DlgObj {
+public:
+	tag_DlgObj *parent;
+	LineDEF Line;
+	FillDEF Fill;
+	TextDEF TextDef;
+
+	Dialog(tag_DlgObj *par, DlgInfo * desc, RECT rec);
+	virtual bool Select(int x, int y, anyOutput *o);
+	bool SetCheck(int id, anyOutput *o, bool state);
+	void MBtrack(MouseEvent *mev, anyOutput *o);
+	virtual void Activate(int id, bool active);
+};
+
+typedef struct {
+	unsigned int id;			//consecutive number
+	unsigned int next;		//id of next object
+	unsigned int first;		//number of first child
+	unsigned long flags;		//any status flag bits
+	Dialog *dialog;			//pointer to dialog object
+} DlgTmpl;
+
+class DlgRoot:public tag_DlgObj {
+public:
+	anyOutput *CurrDisp;		//the dialog's output class
+	void *hDialog;			//handle to the dialog window/widget
+
+	DlgRoot(DlgInfo *tmpl, DataObj *d);
+	~DlgRoot();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool CurUpDown(int cmd);
+	bool GetColor(int id, DWORD *color);
+	void SetColor(int id, DWORD color);
+	bool GetValue(int id, double *val);
+	bool GetInt(int id, int *val);
+	bool SetCheck(int id, anyOutput *o, bool state);
+	bool GetCheck(int Id);
+	void Activate(int id, bool active);
+	bool GetText(int id, char *txt, int size);
+	bool SetText(int id, char *txt);
+	bool SetValue(int id, double val);
+	bool TextStyle(int id, int style);
+	bool TextFont(int id, int font);
+	bool TextSize(int id, int size);
+	bool ShowItem(int id, bool show);
+	int GetResult();
+	int FindIndex(unsigned short id);
+	void ForEach(int cmd, int start, anyOutput *o);
+	bool ItemCmd(int id, int cmd, void *tmpl);
+	anyOutput *GetOutputClass(){return CurrDisp;};
+
+private:
+	int res_q[256], res_put, res_get, cDlgs, cContinue;
+	anyOutput *ParentOut;
+	DataObj *data;
+	Dialog *oldFocus, *oldDefault, *oldTabStop;
+	bool bActive, bRedraw;
+	GraphObj *c_go;
+	Dialog **tabstops, *mrk_item;
+	DlgTmpl **dlg;
+	MouseEvent *mev;
+};
+
+class PushButton:public Dialog {
+public:
+	PushButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
+	~PushButton();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+
+private:
+	char *Text;
+};
+
+class TextBox:public Dialog {
+public:
+	TextBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
+	~TextBox();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	void MBtrack(MouseEvent *mev, anyOutput *o);
+	bool GetText(int id, char *txt, int size);
+
+private:
+	TextFrame *cont;
+};
+
+class ArrowButton:public Dialog {
+public:
+	ArrowButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, int *which);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+
+private:
+	int direct;
+};
+
+class ColorButton:public Dialog {
+public:
+	ColorButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, DWORD *color);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	bool GetColor(int id, DWORD *color) {*color = col; return true;};
+	void SetColor(int id, DWORD color) {col = color; return;};
+
+private:
+	DWORD col;
+};
+
+class FillButton:public Dialog {
+public:
+	FillButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, FillDEF *fill);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	bool GetColor(int id, DWORD *color) {*color = CurrFill->color; return true;};
+
+private:
+	FillDEF *CurrFill;
+};
+
+class Shade3D:public Dialog {
+public:
+	Shade3D(tag_DlgObj *par, DlgInfo * desc, RECT rec, FillDEF *fill);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	bool GetColor(int id, DWORD *color) {*color = CurrFill->color; return true;};
+
+private:
+	FillDEF *CurrFill;
+};
+
+class LineButton:public Dialog {
+public:
+	LineButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, LineDEF *line);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+
+private:
+	LineDEF *CurrLine;
+	POINT pts[2];
+};
+
+class SymButton:public Dialog {
+public:
+	SymButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, Symbol **sym);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+
+private:
+	Symbol **symbol;
+};
+
+class FillRadioButt:public Dialog {
+public:
+	FillRadioButt(tag_DlgObj *par, DlgInfo * desc, RECT rec, unsigned int pattern);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+};
+
+class SymRadioButt:public Dialog {
+public:
+	SymRadioButt(tag_DlgObj *par, DlgInfo * desc, RECT rec, int *type);
+	~SymRadioButt();
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+
+private:
+	Symbol *Sym;
+};
+
+class CheckBox:public Dialog {
+public:
+	CheckBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
+	~CheckBox();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+
+private:
+	char *Text;
+};
+
+class CheckPin:public Dialog {
+public:
+	CheckPin(tag_DlgObj *par, DlgInfo * desc, RECT rec);
+	~CheckPin();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+};
+
+class Trash:public Dialog {
+public:
+	Trash(tag_DlgObj *par, DlgInfo * desc, RECT rec);
+	~Trash();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+};
+
+class Config:public Dialog {
+public:
+	Config(tag_DlgObj *par, DlgInfo * desc, RECT rec);
+	~Config();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+};
+
+class RadioButton:public Dialog {
+public:
+	RadioButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
+	~RadioButton();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	void SetColor(int id, DWORD color);
+
+private:
+	char *Text;
+};
+
+class Text:public Dialog {
+public:
+	Text(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
+	~Text();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	void SetColor(int id, DWORD color);
+	void MBtrack(MouseEvent *mev, anyOutput *o);
+
+private:
+	char *txt;
+};
+
+class InputText:public Dialog {
+public:
+	EditText *Text;
+
+	InputText(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
+	~InputText();
+	virtual bool Command(int cmd, void *tmpl, anyOutput *o);
+	virtual void DoPlot(anyOutput *o);
+	virtual bool Select(int x, int y, anyOutput *o);
+	virtual bool GetValue(int id, double *val);
+	bool GetInt(int id, int *val);
+	bool GetText(int id, char *txt, int size);
+	virtual void MBtrack(MouseEvent *mev, anyOutput *o);
+	virtual void Activate(int id, bool active);
+
+private:
+	anyOutput *Disp;
+};
+
+class RangeInput:public InputText {
+public:
+	RangeInput(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text, DataObj *d);
+	~RangeInput();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	void Activate(int id, bool active);
+
+private:
+	DataObj *data;
+};
+
+class InputValue:public InputText {
+public:
+	InputValue(tag_DlgObj *par, DlgInfo * desc, RECT rec, double *value);
+	~InputValue();
+};
+
+class IncDecValue:public InputText {
+public:
+	IncDecValue(tag_DlgObj *par, DlgInfo * desc, RECT rec, double *value);
+	~IncDecValue();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	bool GetValue(int id, double *val);
+	void MBtrack(MouseEvent *mev, anyOutput *o);
+
+private:
+	ArrowButton *butts[2];
+	bool hasMinMax, hasStep;
+	double theMin, theMax, theStep;
+};
+
+class TxtHSP:public Dialog {
+public:
+	TxtHSP(tag_DlgObj *par, DlgInfo *desc, RECT rec, int *align);
+	~TxtHSP();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	bool GetInt(int id, int *val);
+	void MBtrack(MouseEvent *mev, anyOutput *o);
+
+private:
+	TextDEF txt;
+	RadioButton *butts[9];
+	DlgInfo *d2;
+};
+
+class SlideRect:public Dialog{
+public:
+	int sLine;
+
+	SlideRect(tag_DlgObj *par, DlgInfo *desc, RECT rec, bool isVert);
+	~SlideRect();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	bool GetValue(int id, double *val);
+	void MBtrack(MouseEvent *mev, anyOutput *o);
+
+private:
+	int dx, dy, w, h;
+	bool bV, puSel, pdSel;
+	RECT buttrc, puRC, pdRC;
+};
+
+class ScrollBar:public Dialog{
+public:
+	int sLine, sPage;
+
+	ScrollBar(tag_DlgObj *par, DlgInfo *desc, RECT rec, bool isVert);
+	~ScrollBar();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	bool GetValue(int id, double *val);
+	void MBtrack(MouseEvent *mev, anyOutput *o);
+
+private:
+	ArrowButton *butts[3];
+	SlideRect *slrc;
+};
+
+class Icon:public Dialog {
+public:
+	Icon(tag_DlgObj *par, DlgInfo * desc, RECT rec, int *ico);
+	void DoPlot(anyOutput *o);
+
+private:
+	int icon;
+};
+
+class Group:public Dialog {
+public:
+	Group(tag_DlgObj *par, DlgInfo * desc, RECT rec);
+	~Group();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	void MBtrack(MouseEvent *mev, anyOutput *o);
+
+	InputText *TextFocus;
+	Dialog **Children;
+	int numChildren;
+};
+
+class GroupBox:public Group {
+public:
+	GroupBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *txt);
+	~GroupBox();
+	void DoPlot(anyOutput *o);
+
+private:
+	char *Text;
+};
+
+class TabSheet:public Group {
+public:
+	TabSheet(tag_DlgObj *par, DlgInfo * desc, RECT rec, TabSHEET *sh, DataObj *d);
+	~TabSheet();
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+
+private:
+	DataObj *data;
+	RECT rctab;
+	char *Text;
+};
+
+class ODbutton:public Dialog {
+public:
+	ODbutton(tag_DlgObj *par, DlgInfo *des, RECT rec, void*proc);
+	~ODbutton();
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	void MBtrack(MouseEvent *mev, anyOutput *o);
+
+private:
+	void (*ODexec)(int cmd, void *par, RECT *rec, anyOutput *o,
+		void *data, int id);
+};
+
+class Listbox:public Dialog {
+public:
+	Listbox(tag_DlgObj *par, DlgInfo *des, RECT rec, char **list);
+	~Listbox();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	bool GetInt(int id, int *val);
+	bool GetText(int id, char *txt, int size);
+	void MBtrack(MouseEvent *mev, anyOutput *o);
+
+private:
+	ScrollBar *sb;
+	anyOutput *bmp;
+	char **strings;
+	double spos;
+	int ns, bmh, startY, cl;
+
+	bool CreateBitMap(anyOutput *tmpl);
+};
+
+class Treeview:public Dialog {
+public:
+	Treeview(tag_DlgObj *par, DlgInfo *des, RECT rec, GraphObj *g);
+	~Treeview();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	bool GetInt(int id, int *val);
+	void MBtrack(MouseEvent *mev, anyOutput *o);
+
+private:
+	ScrollBar *sb;
+	anyOutput *bmp;
+	double spos;
+	int ns, bmh, bmw, startY, cl;
+	GraphObj *go;
+	ObjTree *ot;
+};
+
+class LinePat:public Dialog {
+public:
+	LinePat(tag_DlgObj *par, DlgInfo *desc, RECT rec, LineDEF *Line);
+	void DoPlot(anyOutput *o);
+	void MBtrack(MouseEvent *mev, anyOutput *o);
+
+private:
+	bool bDraw;
+	DWORD *pPattern;
+};
+
+//prototypes TheDialog.cpp
+bool UseRangeMark(DataObj *d, int type, char* =0L, char* =0L, char* =0L, char* =0L,
+	char* =0L, char* =0L, char* =0L, char* =0L, char* =0L, char* =0L, char* =0L);
+int com_StackDlg(int,DlgRoot*,AccRange**,int*,char***,int*,AccRange**,bool*,int*,int*,bool*);
+DlgInfo *CompileDialog(char* tmpl, void **ptypes);
+
+//prototypes ODbutton.cpp
+void OD_DrawOrder(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+int ExecDrawOrderButt(GraphObj *parent, GraphObj *obj, int id);
+void OD_PolygonStyleTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+void OD_LineStyleTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+void OD_BubbleTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+void OD_ErrBarTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+void OD_WhiskerTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+void OD_PolarTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+void OD_PieTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+void OD_AxisDesc3D(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+void OD_BreakTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+void OD_PlotTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+void OD_AxisTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+void OD_AxisTempl3D(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+void OD_NewAxisTempl(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
+void OD_NewAxisTempl3D(int cmd, void *par, RECT *rec, anyOutput *o, void *data, int id);
diff --git a/UtilObj.cpp b/UtilObj.cpp
index db976da..ac9c3ab 100755
--- a/UtilObj.cpp
+++ b/UtilObj.cpp
@@ -1,3997 +1,4118 @@
-//UtilObj.cpp, (c) 2000-2007 by R. Lackner
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-#include "rlplot.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <math.h>
-#include <ctype.h>
-
-#include <fcntl.h>				//file open flags
-#include <sys/stat.h>			//I/O flags
-#ifdef _WINDOWS
-	#include <io.h>					//for read/write
-#else
-	#define O_BINARY 0x0
-	#include <unistd.h>
-#endif
-
-Default defs;
-
-static LineDEF ETbgnn = {0.0, 1.0, 0x00e8e8e8L, 0L};
-static LineDEF ETbgna = {0.0, 1.0, 0x00ffffffL, 0L};
-static LineDEF ETbgmn = {0.0, 1.0, 0x00cbcbcbL, 0L};
-static LineDEF ETbgma = {0.0, 1.0, 0x00ffffc0L, 0L};
-static LineDEF yLine = {0.0, 1.0, 0x0000ffffL, 0L};
-extern const LineDEF BlackLine = {0.0, 1.0, 0x00000000L, 0L};
-extern const LineDEF GrayLine = {0.0, 1.0, 0x00c0c0c0L, 0L};
-
-static FillDEF ETfbnn = {FILL_NONE, 0x00e8e8e8L, 1.0, NULL, 0x00ffffffL};
-static FillDEF ETfbna = {FILL_NONE, 0x00ffffffL, 1.0, NULL, 0x00ffffffL};
-static FillDEF ETfbmn = {FILL_NONE, 0x00e8cbcbL, 1.0, NULL, 0x00ffffffL};
-static FillDEF ETfbma = {FILL_NONE, 0x00ffffc0L, 1.0, NULL, 0x00ffffffL};
-static FillDEF yFill = {FILL_NONE, 0x0000ffffL, 1.0, NULL, 0x0000ffffL};
-
-extern char TmpTxt[500];
-extern unsigned long cObsW;				//count objects written
-extern GraphObj *CurrGO, *TrackGO;		//Selected Graphic Objects
-extern dragHandle *CurrHandle;
-extern UndoObj Undo;
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Process fields with user input text
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-EditText *CurrText = 0L, *scroll_et = 0;
-int scroll_dist = 0;
-
-EditText::EditText(void *par, char *msg, int r, int c)
-{
-	loc.x = loc.y = crb.x = rb.x = crb.y = rb.y = 0;	Value = 0.0;
-	row = r;	col = c;	disp = 0L;		text = 0L;
-	CursorPos = length = Align = 0;		type = ET_UNKNOWN;		TextCol=0x00000000L;
-	bgLine = &ETbgnn;					bgFill = &ETfbnn;		parent = par;
-	if(msg && msg[0]) {
-		SetText(msg);		FindType();
-		}
-	else {
-		Align = TXA_VCENTER | TXA_HRIGHT;
-		type = ET_UNKNOWN;
-		}
-	m1 = m2 = -1;						//cursor positions track marks
-	ftext = 0L;							//store formula text result here
-}
-
-EditText::~EditText()
-{
-	HideCopyMark();
-	if(CurrText == this)	CurrText = 0L;
-	if(text) free(text);	text = 0L;
-//	if(ftext) free(ftext);	ftext = 0L;
-}
-
-bool
-EditText::AddChar(int ci, anyOutput *Out, void *data_obj)
-{
-	unsigned char byte1, byte2, c, *tmp;
-	POINT MyPos;
-	int i;
-
-	if(ci < 254 && ci > 31) c = (char)ci;
-	else if(ci == 27) {						//Esc
-		m1 = m2 = -1;		Redraw(Out, true);		return true;
-		}
-	else return false;
-	if(parent) {
-		((DataObj*)parent)->Command(CMD_MRK_DIRTY, 0L, 0L);
-		((DataObj*)parent)->Command(CMD_SAVEPOS, 0L, 0L);
-		}
-	Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
-	bgLine = &ETbgna; bgFill = &ETfbna; TextCol = 0x00000000L;
-	if(text)length = (int)strlen(text);
-	else length = 0;
-	if(text) tmp = (unsigned char *)realloc(text, length+2);
-	else tmp = (unsigned char *)calloc(2, sizeof(unsigned char));
-	if(!tmp) return false;
-	text = (char*)tmp;
-	byte1 = byte2 = 0;
-	//replace mark by character if mark exists
-	if(hasMark()) {			//delete marked part of text
-			if(m1 > m2) Swap(m1, m2);
-			if(m2 >= (short int)strlen(text)) text[m1] = 0;
-			else rlp_strcpy(text+m1, TMP_TXT_SIZE, text+m2);
-			CursorPos = m1;
-			m1 = m2 = -1;
-			}
-	byte1 = text[CursorPos];
-	i = CursorPos;
-	text[i++] = c;
-	while(byte1) {
-		byte2 = byte1;			byte1 = text[i];			text[i++] = byte2;
-		}
-	text[i] = byte1;			CursorPos++;				type = ET_UNKNOWN;
-	Redraw(Out, true);
-	MyPos.y = ((loc.y +crb.y)>>1);
-	MyPos.x = Align & TXA_HRIGHT ? crb.x - 2 : loc.x + 2;
-	if(Out)Out->TextCursor(text, MyPos, (POINT *) NULL, &CursorPos, 
-		scroll_et == this ? scroll_dist : scroll_dist=0);
-	set_etracc();
-	return true;
-}
-
-void
-EditText::Update(int select, anyOutput *Out, POINT *MousePos)
-{
-	POINT MyPos;
-
-	if(!parent && !disp) disp = Out;
-	if(select != 1 && select != 5) m1 = m2 = -1;		//no mark;
-	switch(select) {
-		case 0:							//just redraw with current settings
-			Redraw(Out, true);
-			break;
-		case 5:							//dialog control
-			if(!text)Align = TXA_VCENTER | TXA_HLEFT;
-		case 1:							//active spread sheet cell with cursor
-			if((type & 0xff) == ET_FORMULA) Align = TXA_VCENTER | TXA_HLEFT;
-			if(!text && !(text = (char *) calloc(10, sizeof(char))))return;
-			if(CursorPos > (int)strlen(text)) CursorPos = (int)strlen(text);
-			if(MousePos && (type & 0xff) == ET_TEXT && (text && text[0] == '\'' && (bgLine == &ETbgnn || bgLine == &ETbgmn))) {
-				MousePos->x += 4;
-				}
-			bgLine = &ETbgna; bgFill = &ETfbna; TextCol = 0x00000000L;
-			if(Out) {
-				Redraw(Out, true);
-				MyPos.y = ((loc.y +crb.y)>>1);
-				MyPos.x = Align & TXA_HRIGHT ? crb.x - 4 : loc.x + 4;
-				if(MousePos && MousePos->x && MousePos->y) {
-					Out->TextCursor(text, MyPos, MousePos,&CursorPos, 
-					scroll_et == this ? scroll_dist : scroll_dist=0);
-					}
-				else if(select ==1) Out->TextCursor(text, MyPos, NULL, &CursorPos, 
-					scroll_et == this ? scroll_dist : (scroll_dist=0)+2);
-				set_etracc();
-				}
-			break;
-		case 2:							//inactive spreadsheet cell
-			if(CurrText == this) {
-				FindType();
-				}
-			if(crb.x > rb.x) {
-				crb.x = rb.x;	crb.y = rb.y;
-				}
-			bgLine = &ETbgnn; bgFill = &ETfbnn; TextCol = 0x00000000L;
-			if(Out) Redraw(Out, true);
-			break;
-		case 10:						//value filled in by external app.
-			if(text && text[0]) {
-				type = ET_VALUE;
-				Align = TXA_VCENTER | TXA_HRIGHT;
-#ifdef USE_WIN_SECURE
-				sscanf_s(text, "%lf", &Value);
-#else
-				sscanf(text, "%lf", &Value);
-#endif
-				}
-			break;
-		case 20:						//update value only
-			FindType();
-			break;
-		}
-}
-
-bool
-EditText::Redraw(anyOutput *Out, bool display)
-{
-	RECT rc;
-	POINT MyPos;
-	char *txt, tmptxt[500];
-	int i, w, h, o_crbx;
-	bool b_clip = false;
-	anyOutput *opc;
-	anyResult cres;
-	POINT grid[3];
-
-	if(!parent && disp) Out = disp;
-	if((type & ET_NODRAW_EMPTY) == ET_NODRAW_EMPTY && CurrText != this){
-		type &= ~ET_NODRAW;
-		return true;
-		}
-	if(loc.x <1 || rb.x < 1 || loc.y <1 || rb.y <1) return false;
-	o_crbx = crb.x;			crb.x = rb.x;				crb.y = rb.y;
-	if (Out) {
-		if (m1 >m2) Swap(m1, m2);
-		if(((type & 0xff) == ET_UNKNOWN) && text && text[0] && (bgLine == &ETbgnn || bgLine == &ETbgmn)) FindType();
-		Out->TxtSet.Align = Align;		Out->TxtSet.ColTxt = TextCol;
-		Out->TxtSet.ColBg = bgLine->color;
-		if(text && text[0]) {
-			Out->oGetTextExtent(text, (int)strlen(text), &w, &h);
-			if(CurrText == this && parent && col >= 0) {
-				for(i = col+1; (crb.x - loc.x) < (w+(h>>1)); i++) {
-					crb.x += ((DataObj*)parent)->ri->GetWidth(i);
-					}
-				if(o_crbx > loc.x && o_crbx > crb.x && o_crbx < 4000) crb.x = o_crbx;
-				}
-			else if((crb.x - loc.x) < (w+(h>>1))) b_clip = true;
-			}
-		Out->SetFill(bgFill);		Out->SetLine(bgLine);
-		rc.left = loc.x;			rc.right = crb.x;
-		rc.top = loc.y+1;			rc.bottom = crb.y-2;
-		Out->oRectangle(loc.x, loc.y, crb.x-1, crb.y-1);
-		if(!text || !text[0]){
-			if((type & 0xff) == ET_VALUE){
-#ifdef USE_WIN_SECURE
-				sprintf_s(tmptxt, 500, "%g", Value);
-#else
-				sprintf(tmptxt, "%g", Value);
-#endif
-				}
-			else if((type & 0xff) == ET_BOOL) {
-#ifdef USE_WIN_SECURE
-				sprintf_s(tmptxt, 500, Value != 0.0 ? "true" : "false");
-#else
-				sprintf(tmptxt, Value != 0.0 ? "true" : "false");
-#endif
-				}
-			else tmptxt[0] = 0;
-			if(tmptxt[0] && (text = (char*)realloc(text, strlen(tmptxt)+2))) rlp_strcpy(text, 500, tmptxt);
-			CursorPos = 0;
-			}
-		if(ftext) free(ftext);		ftext = 0L;
-		if(text && text[0]){
-			if(bgLine == &ETbgnn || bgLine == &ETbgmn) {
-				Out->TxtSet.Align = TXA_HLEFT | TXA_VCENTER;
-				GetResult(&cres, false);				TranslateResult(&cres);
-				Value = cres.value;
-				switch (cres.type) {
-				case ET_VALUE:
-					Out->TxtSet.Align = TXA_HRIGHT | TXA_VCENTER;
-					b_clip = false;				rlp_strcpy(tmptxt, 500, cres.text);
-					fit_num_rect(Out, rb.x - loc.x, tmptxt);
-					Int2Nat(tmptxt);			break;
-				case ET_BOOL:	case ET_DATE:	case ET_TIME:	case ET_DATETIME:
-				case ET_TEXT:
-					Out->TxtSet.Align = cres.type == ET_TEXT ? 
-						TXA_HLEFT | TXA_VCENTER : TXA_HRIGHT | TXA_VCENTER;
-					i = (int)strlen(cres.text)+2;
-					if(ftext = (char*)realloc(ftext, i > 20 ? i : 20))rlp_strcpy(ftext, i, cres.text);
-					if(cres.text && i < sizeof(tmptxt)) 
-						rlp_strcpy(tmptxt, 500, cres.text[0] != '\'' ? cres.text : cres.text +1);
-					else if(cres.text)rlp_strcpy(tmptxt, 500, "#SIZE");
-					else tmptxt[0] = 0;			
-					Out->oGetTextExtent(tmptxt, (int)strlen(tmptxt), &w, &h);
-					b_clip = (crb.x - loc.x) < (w+(h>>1)) ? true : false;
-					break;
-				case ET_ERROR:
-					rlp_strcpy(tmptxt, 500, "#ERROR");	break;
-				default: 
-					rlp_strcpy(tmptxt, 500, "#VALUE");	break;
-					}
-				txt = tmptxt;
-				}
-			else txt = text;
-			if(b_clip && parent && col >= 0 && row >=0) {
-				Out->oGetTextExtent(txt, (int)strlen(txt), &w, &h);
-				for(i = col+1; (crb.x - loc.x) < (w+(h>>1)); i++) {
-					if(((DataObj*)parent)->isEmpty(row, i)) {
-						crb.x += ((DataObj*)parent)->ri->GetWidth(i);
-						((DataObj*)parent)->etRows[row][i]->type |= ET_NODRAW;
-						}
-					else break;
-					}
-				if((crb.x - loc.x) >= (w+(h>>1))) b_clip = false;
-				else rc.right = crb.x;
-				}
-			MyPos.y = (loc.y+rb.y)>>1;
-			if(Out->TxtSet.Align & TXA_HRIGHT) {	//right justified text
-				MyPos.x = crb.x-4;
-				}
-			else {									//left justified text
-				MyPos.x = loc.x+4;
-				}
-			if(b_clip && (opc = NewBitmapClass(w+22, rb.y-loc.y, Out->hres, Out->vres))){
-				if(scroll_et != this || parent) {
-					scroll_et = this;	scroll_dist = 0;
-					}
-				opc->Erase(bgFill->color);
-				opc->SetTextSpec(&Out->TxtSet);		opc->TxtSet.Align = TXA_HLEFT | TXA_VCENTER;
-				opc->oTextOut(4,(rb.y-loc.y)>>1, txt, (int)strlen(txt));
-				if(!parent && CursorPos) {
-					Out->oGetTextExtent(txt, CursorPos, &w, &h);
-					while((scroll_dist + w)>(rc.right-rc.left-10)) scroll_dist -=10;
-					while((scroll_dist + w)<12) scroll_dist +=10;
-					if(scroll_dist >0) scroll_dist=0;
-					}
-				else scroll_dist=0;
-				Out->CopyBitmap(rc.left+1, rc.top+1, opc, 1-scroll_dist, 1, 
-					rc.right-rc.left-4, rc.bottom-rc.top-2, false);
-				DelBitmapClass(opc);
-				}
-			else {
-				if(display && hasMark() && mx1 > loc.x && mx2 < crb.x) {
-					Out->SetFill(&yFill);		Out->SetLine(&yLine);
-					Out->oRectangle(mx1, rc.top, mx2, rc.bottom);
-					Out->SetFill(bgFill);		Out->SetLine(bgLine);
-					}
-				scroll_dist = 0;
-				Out->oTextOut(MyPos.x, MyPos.y, txt, 0);
-				if(display && hasMark() && mx1 > loc.x && mx2 < crb.x) {
-					rc.left = mx1;		rc.right = mx2;
-					Out->CopyBitmap(mx1, rc.top, Out, mx1, rc.top, mx2-mx1, rc.bottom-rc.top, true);
-					Out->MrkMode = MRK_NONE;
-					}
-				}
-			}
-		Out->SetLine((LineDEF*)&GrayLine);
-		grid[0].x = loc.x;					grid[0].y = grid[1].y = crb.y-1;
-		grid[1].x = grid[2].x = crb.x-1;	grid[2].y = loc.y-1;
-		Out->oPolyline(grid, 3, 0L);
-		if(display) {
-			if(!(Out->UpdateRect(&rc, false))) return false;
-			}
-		return true;
-	}
-	return false;
-}
-
-void
-EditText::Mark(anyOutput *Out, int mark)
-{
-	LineDEF *ol = bgLine;
-	FillDEF *of = bgFill;
-	DWORD ocol = TextCol;
-
-	m1 = m2 = -1;
-	if(!parent) return;
-	switch (mark){
-	case 0:				//normal not active
-		bgLine = &ETbgnn; bgFill = &ETfbnn; TextCol = 0x00000000L;
-		break;
-	case 1:				//normal active
-		bgLine = &ETbgna; bgFill = &ETfbna; TextCol = 0x00000000L;
-		break;
-	case 2:				//mark not active
-		bgLine = &ETbgmn; bgFill = &ETfbmn; TextCol = 0x00c00000L;
-		break;
-	case 3:				//mark active
-		bgLine = &ETbgma; bgFill = &ETfbma; TextCol = 0x00ff0000L;
-		break;
-		}
-	if(!mark || mark == 2) {
-		loc.y--;	rb.y++;
-		}
-	Redraw(Out, true);
-	if(!mark || mark == 2) {
-		loc.y++;	rb.y--;
-		}
-	bgLine = ol;	bgFill = of;	TextCol = ocol;
-}
-
-bool
-EditText::Command(int cmd, anyOutput *Out, void *data_obj)
-{
-	int i, j, k, w, h;
-	POINT MyPos;
-	MouseEvent *mev;
-	static RECT rMark;
-	bool bRet;
-	char *tag1, *tag2;
-	unsigned char *pt;
-
-	MyPos.y = ((loc.y+crb.y)>>1);
-	MyPos.x = Align & TXA_HRIGHT ? crb.x - 4 : loc.x + 4;
-	if(!(text)) return false;
-	if(!parent && disp) Out = disp;		//Dialog !
-	switch(cmd) {
-		case CMD_MRK_DIRTY:
-			type = ET_UNKNOWN;
-			if(CurrText == this) {
-				Command(CMD_REDRAW, Out, data_obj);
-				if(parent)((DataObj*)parent)->Command(CMD_MRK_DIRTY, Out, 0L);
-				}
-			else if(parent) {
-				((DataObj*)parent)->Command(CMD_REDRAW, Out, 0L);
-				((DataObj*)parent)->Command(CMD_MRK_DIRTY, Out, 0L);
-				}
-			else return Command(CMD_REDRAW, Out, data_obj);
-			return true;
-		case CMD_SETFONT:
-			if (!text || !text[0]) return false;
-			if(Out) Undo.SetDisp(Out);
-			type = ET_TEXT;
-			if(hasMark()) {
-				Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
-				switch (*((int*)data_obj)) {
-				case FONT_HELVETICA:
-					tag1 = (char*)"<face=helvetica>";		tag2 = (char*)"</face>";		break;
-				case FONT_TIMES:
-					tag1 = (char*)"<face=times>";			tag2 = (char*)"</face>";		break;
-				case FONT_COURIER:
-					tag1 = (char*)"<face=courier>";			tag2 = (char*)"</face>";		break;
-				case FONT_GREEK:
-					tag1 = (char*)"<face=greek>";			tag2 = (char*)"</face>";		break;
-				default:
-					return false;
-					}
-				if(m1 < m2) {
-					j = m1;	k = m2;
-					}
-				else if(m1 > m2) {
-					j = m2; k = m1;
-					}
-				else return false;			//empty mark !
-				for(i = 0; i < j; i++) TmpTxt[i] = text[i];
-				for(j = 0, w = i; tag1[j]; j++) TmpTxt[i++] = tag1[j];
-				for( ; w < k; w++) TmpTxt[i++] = text[w];
-				for(j = 0; tag2[j]; j++) TmpTxt[i++] = tag2[j];
-				for( ; TmpTxt[i++] = text[w]; w++);
-				m1 += (w = (int)strlen(tag1));	m2 += w;	CursorPos += w;
-				CleanTags(TmpTxt, &m1, &m2, &CursorPos);
-				if(text = (char*)realloc(text, strlen(TmpTxt)+2)) rlp_strcpy(text, TMP_TXT_SIZE, TmpTxt);
-				Command(CMD_REDRAW, Out, 0L);
-				return true;
-				}
-			return false;
-		case CMD_SETSTYLE:
-			if (!text || !text[0]) return false;
-			if(Out) Undo.SetDisp(Out);
-			type = ET_TEXT;
-			if(hasMark()) {
-				Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
-				switch (*((int*)data_obj)) {
-				case TXS_BOLD:
-					tag1 = (char*)"<b>";		tag2 = (char*)"</b>";		break;
-				case ~TXS_BOLD:
-					tag1 = (char*)"</b>";		tag2 = (char*)"<b>";		break;
-				case TXS_ITALIC:
-					tag1 = (char*)"<i>";		tag2 = (char*)"</i>";		break;
-				case ~TXS_ITALIC:
-					tag1 = (char*)"</i>";		tag2 = (char*)"<i>";		break;
-				case TXS_UNDERLINE:
-					tag1 = (char*)"<u>";		tag2 = (char*)"</u>";		break;
-				case ~TXS_UNDERLINE:
-					tag1 = (char*)"</u>";		tag2 = (char*)"<u>";		break;
-				case TXS_SUPER:
-					tag1 = (char*)"<sup>";		tag2 = (char*)"</sup>";		break;
-				case ~TXS_SUPER:
-					tag1 = (char*)"</sup>";		tag2 = (char*)"<sup>";		break;
-				case TXS_SUB:
-					tag1 = (char*)"<sub>";		tag2 = (char*)"</sub>";		break;
-				case ~TXS_SUB:
-					tag1 = (char*)"</sub>";		tag2 = (char*)"<sub>";		break;
-				default:
-					return false;
-					}
-				if(m1 < m2) {
-					j = m1;	k = m2;
-					}
-				else if(m1 > m2) {
-					j = m2; k = m1;
-					}
-				else return false;			//empty mark !
-				for(i = 0; i < j; i++) TmpTxt[i] = text[i];
-				for(j = 0, w = i; tag1[j]; j++) TmpTxt[i++] = tag1[j];
-				for( ; w < k; w++) TmpTxt[i++] = text[w];
-				for(j = 0; tag2[j]; j++) TmpTxt[i++] = tag2[j];
-				for( ; TmpTxt[i++] = text[w]; w++);
-				m1 += (w = (int)strlen(tag1));	m2 += w;	CursorPos += w;
-				CleanTags(TmpTxt, &m1, &m2, &CursorPos);
-				if(text = (char*)realloc(text, strlen(TmpTxt)+2)) rlp_strcpy(text, TMP_TXT_SIZE, TmpTxt);
-				Command(CMD_REDRAW, Out, 0L);
-				return true;
-				}
-			return false;
-		case CMD_ADDTXT:
-			if((tag1 = (char*)data_obj) && *tag1 && text && 
-				(type == ET_TEXT || type == ET_UNKNOWN || type == ET_FORMULA)){
-				if(hasMark()) Command(CMD_DELETE, 0L, 0L);
-				else Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
-				if(m1 > -1 && m2 > -1) Command(CMD_DELETE, 0L, 0L);
-				for(k = 0; tag1[k] && tag1[k] == text[k]; k++);
-				if(tag1[k]!=';') k = 0;
-				for(i = 0; i < CursorPos && text[i]; i++) TmpTxt[i] = text[i];
-				j = i + rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, tag1+k);
-				if(text[i]) j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, text+i);
-				if(text = (char*)realloc(text, j+2 )) rlp_strcpy(text, j+2, TmpTxt);
-				CursorPos += (int)strlen(tag1+k);
-				Out->Focus();					Update(1, Out, 0L);
-				set_etracc();
-				}
-			return true;
-		case CMD_BACKSP:
-			if(!text) return false;
-			if(CursorPos <=0){
-				Out->TextCursor(text, MyPos, (POINT *) NULL, &CursorPos, 
-					scroll_et == this ? scroll_dist : scroll_dist=0);
-				return false;
-				}
-			Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
-			CursorPos--;						//continue as if delete
-		case CMD_DELETE:
-			if(!text) return false;
-			if(cmd == CMD_DELETE) Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
-			if(parent) {
-				((DataObj*)parent)->Command(CMD_MRK_DIRTY, 0L, 0L);
-				((DataObj*)parent)->Command(CMD_SAVEPOS, 0L, 0L);
-				}
-			bRet = false;
-			if(!text || !text[0]) {
-				type = ET_UNKNOWN;	CursorPos = 0;
-				}
-			if(hasMark()) {			//delete marked part of text
-				if (!text || !text[0]) return false;
-				if(m1 > m2) Swap(m1, m2);
-				if(m2 >= (short int)strlen(text)) text[m1] = 0;
-				else rlp_strcpy(text+m1, (int)strlen(text)+m1, text+m2);
-				CursorPos = m1;						m1 = m2 = -1;
-				if(!text[0]) {
-					type = ET_UNKNOWN;	CursorPos = 0;
-					}
-				if(Out) Redraw(Out, (bRet = true));
-				}
-			else if(text[CursorPos]) {
-				rlp_strcpy(text + CursorPos, (int)strlen(text + CursorPos), text + CursorPos + 1);
-				if(!text || !text[0]) {
-					type = ET_UNKNOWN;	CursorPos = 0;
-					}
-				if(Out)Redraw(Out, (bRet = true));
-				}
-			set_etracc();
-			if(Out)Out->TextCursor(text, MyPos, (POINT *) NULL, &CursorPos,
-				scroll_et == this ? scroll_dist : scroll_dist=0);
-			return bRet;
-		case CMD_COPY:
-			if(text && text[0]) {
-				rMark.left = loc.x+2;		rMark.right = rb.x-2;
-				rMark.top = loc.y+1;		rMark.bottom = rb.y-2;
-				if(m1 != m2 && m1 >=0 && m2 >=0) {
-					if (m1 >m2) Swap(m1, m2);
-					rMark.left = mx1;		rMark.right = mx2;
-					if(Out) Out->UpdateRect(&rMark, false);
-					CopyText(text+m1, m2-m1);
-					if(Out) {
-						Out->MrkMode = MRK_NONE;
-						ShowCopyMark(Out, &rMark, 1);
-						Out->UpdateRect(&rMark, true);
-						}
-					return false;
-					}
-				CopyText(text, (int)strlen(text));
-				if(Out)Out->UpdateRect(&rMark, true);
-				return false;
-				}
-			return false;
-		case CMD_PASTE:
-			if(pt = PasteText()) {
-				Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
-				for(i = 0; pt[i] > 0x20 && i < 81; i++) this->AddChar(pt[i], 0L, 0L);
-				if(Out) Redraw(Out, true);				free(pt);
-				set_etracc();
-				if(i) return true;
-				}
-			return false;
-		case CMD_SHIFTRIGHT:
-			if(CursorPos == m1 && text[m1]) m1++;
-			else if(CursorPos == m2 && text[m2]) m2++;
-			else if(text[CursorPos]){
-				m1 = CursorPos;	m2 = CursorPos+1;
-				}
-			set_etracc();
-			if(text[CursorPos]) CursorPos++;
-			else return false;
-		case CMD_SHIFTLEFT:
-			set_etracc();
-			if (!(CursorPos)) return false;
-		case CMD_REDRAW:
-			if(cmd == CMD_SHIFTLEFT) {
-				if(CursorPos == m1 && m1 >0) m1--;
-				else if(CursorPos == m2 && m2 >0) m2--;
-				else if(CursorPos > 0){
-					m1 = CursorPos;	m2 = CursorPos-1;
-					}
-				if(CursorPos >0) CursorPos--;
-				}
-			if(m1 >=0 && m2 >= 0 && m1 != m2 && Out) {
-				if(m1 > m2) Swap(m1, m2);
-				w = h = 0;
-				if(Align & TXA_HRIGHT) {	//right justified text
-					Out->oGetTextExtent(text, 0, &w, &h);
-					mx1 = crb.x-4 - w;
-					}
-				else {						//left justified text
-					mx1 = loc.x +4;
-					}
-				Out->oGetTextExtent(text, m1, &w, &h);
-				mx1 += (m1 ? w : 0);
-				Out->oGetTextExtent(text+m1, m2-m1, &w, &h);
-				mx2 = mx1 + w;
-				}
-			HideTextCursor();				Redraw(Out, true);
-			Out->TextCursor(text, MyPos, (POINT *) NULL, &CursorPos,
-				scroll_et == this ? scroll_dist : scroll_dist=0);
-			return true;
-		case CMD_CURRLEFT:
-			m1 = m2 = -1;					set_etracc();
-			if(CursorPos >0) {
-				CursorPos--;
-				if(Redraw(Out, true) && Out->TextCursor(text, MyPos, (POINT *) NULL,
-					&CursorPos, scroll_et == this ? scroll_dist : scroll_dist=0)) return true;
-				else return false;
-				}
-			else if (data_obj) {
-				MyPos.x = loc.x-4;			MyPos.y = (rb.y+loc.y)/2;
-				if(((DataObj*)data_obj)->Select(&MyPos))return true;
-				MyPos.x = loc.x+4;
-				((DataObj*)data_obj)->Select(&MyPos);
-				}
-			return false;
-		case CMD_CURRIGHT:
-			m1 = m2 = -1;					set_etracc();
-			if(text[CursorPos]){
-				CursorPos++;
-				if(Redraw(Out, true) && Out->TextCursor(text, MyPos, (POINT *) NULL,
-					&CursorPos, scroll_et == this ? scroll_dist : scroll_dist=0)) return true;
-				else return false;
-				}
-			else if (data_obj) {
-				MyPos.x = rb.x+4;		MyPos.y = (rb.y+loc.y)/2;	crb.x = rb.x;
-				if(((DataObj*)data_obj)->Select(&MyPos)) return true;
-				MyPos.x = rb.x-4;
-				((DataObj*)data_obj)->Select(&MyPos);
-				}
-			return false;
-		case CMD_UPDATE:
-			m1 = m2 = -1;
-			Redraw(Out, true);
-			return true;
-		case CMD_POS_FIRST:		case CMD_POS_LAST:
-			CursorPos = (cmd == CMD_POS_LAST && text && text[0]) ? (int)strlen(text) : 0;
-			m1 = m2 = -1;		Redraw(Out, true);
-			Out->TextCursor(text, MyPos, (POINT *) NULL, &CursorPos,
-				scroll_et == this ? scroll_dist : scroll_dist=0);
-			set_etracc();
-			return true;
-		case CMD_CURRDOWN:		case CMD_CURRUP:
-			if (data_obj) {
-			//the following calculation of the cursor position is crude
-            //it is based on a aspect of 2:1 for digits
-				if(Align & TXA_HRIGHT)		//right justified text
-					MyPos.x = rb.x-4-((rb.y-loc.y-4)*((long)strlen(text)-CursorPos))/2;
-				else MyPos.x = loc.x+4+((rb.y-loc.y-4)*CursorPos)/2;
-				MyPos.y = (cmd == CMD_CURRUP) ? loc.y-2 : rb.y+2;
-				if(MyPos.x < loc.x) MyPos.x = loc.x +4;
-				if(MyPos.x > rb.x) MyPos.x = rb.x -4;
-				if(((DataObj*)data_obj)->Select(&MyPos))return true;
-				MyPos.y = rb.y;
-				((DataObj*)data_obj)->Select(&MyPos);
-				}
-			return false;
-		case CMD_MOUSE_EVENT:					//track left mouse button
-			mev = (MouseEvent*) data_obj;
-			if(!text || !text[0]) return false;
-			if(mev->x <loc.x || mev->x >crb.x || mev->y <loc.y || mev->y >rb.y)return false;
-			if(mev->Action == MOUSE_LBDOWN) {
-				m1 = m2 = -1;//					set_etracc();
-				return true;
-				}
-			if(mev->Action == MOUSE_LBDOUBLECLICK) {
-				rMark.top = loc.y+1;			rMark.bottom = rb.y-2;
-				if(!Out->oGetTextExtent(text, (int)strlen(text), &w, &h)) return false;
-				m1 = 0;							m2 = (int)strlen(text);
-				if(Align & TXA_HRIGHT) {		//right justfied text
-					rMark.right = crb.x -4;		rMark.left = crb.x - w - 4;
-					}
-				else {							//left justified text
-					rMark.left = loc.x +4;		rMark.right = rMark.left +w;
-					}
-				mx1 = rMark.left;				mx2 = rMark.right;
-				Redraw(Out, true);				set_etracc();
-				return true;
-				}
-			MyPos.x = Align & TXA_HRIGHT ? mev->x + 4 : mev->x - 4;
-			MyPos.y = mev->y;
-			Out->TxtSet.Align = Align;
-			j = Out->CalcCursorPos(text, Align & TXA_HRIGHT ? crb :loc, &MyPos);
-			if(j == m1 || j == m2) return true;
-			if(Align & TXA_HRIGHT) {			//right justfied text
-				if((i = (int)strlen(text)-j)){
-					if(!Out->oGetTextExtent(text+j, i, &w, &h)) return false;
-					w = crb.x - w - 4;
-					}
-				else w = crb.x-1;
-				}
-			else {								//left justified text
-				if(!j) w = 0;
-				else if(!Out->oGetTextExtent(text, j, &w, &h))return false;
-				w += (loc.x+4);
-				}
-			if(m1 == m2 && m1 == -1) {
-				mx1 = (short)(rMark.left = w);
-				m1 = j;
-				}
-			else if(j != m2){
-				m2 = j;
-				if(m2 >= 0)Out->UpdateRect(&rMark, false);
-				mx2 = (short)(rMark.right = w);
-				rMark.top = loc.y+1;				rMark.bottom = rb.y-2;
-				if(rMark.right < crb.x && rMark.right > loc.x &&
-					rMark.left > loc.x && rMark.left < crb.x)
-					Redraw(Out, true);
-				}
-			if(hasMark() && parent)
-				//remove range-mark of data 
-				((DataObj*)parent)->Command(CMD_UNLOCK, 0L, 0L);
-			return true;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// return the value (i.e. the floating point equivalent) of text
-bool
-EditText::GetValue(double *v)
-{
-	anyResult * res;
-
-	if(((type & 0xff) == ET_UNKNOWN) && (text)) FindType();
-	if(!text || !text[0]) {
-		if((type & 0xff) == ET_VALUE) {
-			*v = Value;		return true;
-			}
-		return false;
-		}
-	if(CurrText == this && !(type & ET_BUSY)) FindType();
-	if((type & 0xff) == ET_VALUE || (type & 0xff) == ET_BOOL || (type & 0xff) == ET_DATE 
-		|| (type & 0xff) == ET_TIME || (type & 0xff) == ET_DATETIME){
-		*v = Value;			return true;
-		}
-	if((type & 0xff) == ET_FORMULA && text && text[0]){
-		if(!(type & ET_BUSY)){
-			type |= ET_BUSY;
-			if(res = do_formula((DataObj*)parent, text+1)) {
-				if(res->type == ET_VALUE || res->type == ET_DATE || res->type == ET_TIME 
-					|| res->type == ET_DATETIME || res->type == ET_BOOL){
-					*v = Value = res->value;	type &= ~ET_BUSY;	return true;
-					}
-				}
-			*v = Value = 0.0;	type &= ~ET_BUSY;	return false;
-			}
-		else type |= ET_CIRCULAR;
-		*v = Value;
-		return true;
-		}
-	return false;
-}
-
-bool
-EditText::GetText(char *tx, int size, bool bTranslate)
-{
-	char *t = 0L;
-	static char tmp_txt[40];
-	anyResult res;
-
-	if((type & 0xff) == ET_TEXT && text && text[0]) {
-		if(text[0] =='\'' && text[1]) t = text + 1;
-		else t = text;
-		}
-	else if(bTranslate) {
-		GetResult(&res, false);						TranslateResult(&res);
-		switch (res.type) {
-		case ET_VALUE:
-			rlp_strcpy(tmp_txt, 40, res.text);		t = tmp_txt;
-			Int2Nat(tmp_txt);						break;
-		case ET_BOOL:		case ET_DATE:			case ET_TIME:	
-		case ET_DATETIME:	case ET_TEXT:
-			t = res.text;							break;
-		default:
-			t = 0L;									break;
-			}
-		}
-	else if(text && text[0]) t = (text[0] =='\'' && text[1]) ? text+1 : text;
-	if(t) {
-		rlp_strcpy(tx, size, t);
-		return true;
-		}
-	else if((type & 0xff) == ET_VALUE) {
-#ifdef USE_WIN_SECURE
-		if(text = (char*)realloc(text, 20)) sprintf_s(text, 20, "%g", Value);
-#else
-		if(text = (char*)realloc(text, 20)) sprintf(text, "%g", Value);
-#endif
-		if(text && text[0]) return(GetText(tx, size, false));
-		}
-	return false;
-}
-
-bool
-EditText::GetResult(anyResult *r, bool use_last)
-{
-	anyResult * res;
-
-	if(!text || !text[0]) {
-		r->text = 0L;
-		if((type & 0xff) == ET_VALUE) {
-			r->value = Value;		r->type = ET_VALUE;
-			}
-		else {
-			r->value = 0.0;			r->type = ET_UNKNOWN;
-			}
-		return true;
-		}
-    if((type & 0xff) == ET_UNKNOWN) FindType();
-	if((type & 0xff) == ET_VALUE || (type & 0xff) == ET_BOOL || (type & 0xff) == ET_DATE 
-		|| (type & 0xff) == ET_TIME || (type & 0xff) == ET_DATETIME){
-		r->text = 0L;	r->value = Value;		r->type = (type & 0xff);
-		return true;
-		}
-	if((type & 0xff) == ET_TEXT) {
-		r->text = text;	r->value = 0.0;			r->type = ET_TEXT;
-		return true;
-		}
-	if((type & 0xff) == ET_FORMULA){
-		if(use_last) {
-			if(ftext) {
-				r->text = ftext;	r->value = 0.0;			r->type = ET_TEXT;
-				}
-			else {
-				r->text = 0L;		r->value = Value;		r->type = ET_VALUE;
-				}
-			return true;
-			}
-		if(!(type & ET_BUSY)){
-			type |= ET_BUSY;
-			if(res = do_formula((DataObj*)parent, text+1)) {
-				if(res->type == ET_VALUE) Value = res->value;
-				else if(res->type == ET_ERROR) {
-					res->text = "#ERROR";	res->type = ET_TEXT;
-					}
-				else Value = 0.0;	type &= ~ET_BUSY;
-				memcpy(r, res, sizeof(anyResult));
-				return true;
-				}
-			type &= ~ET_BUSY;
-			return false;
-			}
-		else {
-			type |= ET_CIRCULAR;
-			r->text = "#CIRC.";	r->value = 0.0;			r->type = ET_TEXT;
-			return true;
-			}
-		return false;
-		}
-	return false;
-}
-
-bool
-EditText::SetValue(double v)
-{
-	if(text) text[0] = 0;
-	Value = v;	type = ET_VALUE;
-	return true;
-}
-
-bool
-EditText::SetText(char *t)
-{
-	int cb;
-
-	Value = 0.0;	type = ET_UNKNOWN;
-	bgLine = &ETbgnn; bgFill = &ETfbnn; TextCol = 0x00000000L;
-	if(t && t[0] && (text = (char*)realloc(text, cb = (int)(strlen(t)+2)))) rlp_strcpy(text, cb, t);
-	else if (text) text[0] = 0;
-	return false;
-}
-
-void
-EditText::SetRec(RECT *rc)
-{
-	loc.x = rc->left;				loc.y = rc->top;
-	crb.x = rb.x = rc->right;		crb.y = rb.y = rc->bottom;
-}
-
-	
-bool
-EditText::isValue()
-{
-	if((type & 0xff)==ET_UNKNOWN) FindType();
-	return (type == ET_VALUE || type == ET_FORMULA || type == ET_BOOL
-		|| type == ET_DATE || type == ET_TIME || type == ET_DATETIME);
-}
-
-bool
-EditText::isFormula()
-{
-	if((type & 0xff)==ET_UNKNOWN) FindType();
-	return (type == ET_FORMULA);
-}
-
-void
-EditText::FindType()
-{
-	int i, c1, c2, c3, c4, c5;
-
-	if(!text || !text[0]) {
-		Align = TXA_VCENTER | TXA_HRIGHT;
-		if ((type & 0xff) == ET_VALUE) type = ET_VALUE;
-		else type = ET_UNKNOWN | ET_EMPTY;
-		return;
-		}
-	if(text[0] == '=') {
-		Align = TXA_VCENTER | TXA_HRIGHT;
-		type = ET_FORMULA;
-		}
-	else if(text[0] > 31 && isdigit(text[0]) || ((text[0] == '-' ) || text[0] == '.' 
-		|| text[0] == defs.DecPoint[0]) && text[1]>31 && (isdigit(text[1]))) {
-		for(i = c1 = c2 = c3 = c4 = c5 = 0; text[i]; i++) {
-			switch(text[i]) {
-			case '.':		c1++;		break;
-			case '-':		c2++;		break;
-			case '/':		c3++;		break;
-			case ':':		c4++;		break;
-			case 'e':		break;
-			case 'E':		break;
-			default:		if(isalpha(text[i])) c5++;
-				}
-			}
-		if(c5 > 0){
-			Align = TXA_VCENTER | TXA_HLEFT;
-			type = ET_TEXT;
-			}
-		else if(c1 < 2 && c2 < 2 && !c3  && !c4 && Txt2Flt(text, &Value)) {
-			Align = TXA_VCENTER | TXA_HRIGHT;
-			type = ET_VALUE;
-			}
-		else if((c1 == 2 || c2 == 2 || c3 == 2) && date_value(text, 0L, &Value)) {
-			Align = TXA_VCENTER | TXA_HRIGHT;
-			type = c4 == 2 ? ET_DATETIME : ET_DATE;
-			}
-		else if((c4 == 1 || c4 == 2) && date_value(text, "H:M:S", &Value)) {
-			Align = TXA_VCENTER | TXA_HRIGHT;
-			type = ET_TIME;
-			}
-		else {
-			Align = TXA_VCENTER | TXA_HLEFT;
-			type = ET_TEXT;
-			}
-		}
-	else {
-		if(0 == strcmp(text, "true")) {
-			Value = 1.0;
-			Align = TXA_VCENTER | TXA_HRIGHT;
-			type = ET_BOOL;
-			}
-		else if(0 == strcmp(text, "false")) {
-			Value = 0.0;
-			Align = TXA_VCENTER | TXA_HRIGHT;
-			type = ET_BOOL;
-			}
-		else if(0 == strcmp(text, "inf")) {
-			Value = HUGE_VAL;
-			Align = TXA_VCENTER | TXA_HRIGHT;
-			type = ET_VALUE;
-			}
-		else if(0 == strcmp(text, "-inf")) {
-			Value = -HUGE_VAL;
-			Align = TXA_VCENTER | TXA_HRIGHT;
-			type = ET_VALUE;
-			}
-		else {
-			Align = TXA_VCENTER | TXA_HLEFT;
-			type = ET_TEXT;
-			}
-		}
-}
-
-void
-EditText::set_etracc()
-{
-	int i;
-	bool accept_range;
-	anyResult *res;
-
-	if(parent) {
-		if(hasMark()) i = m1 < m2 ? m1 : m2;
-		else i = CursorPos;
-		accept_range = (i && text && text[0] == '=' && text[i-1]!=')'
-			&& text[i-1] > 31 && !(isdigit(text[i-1]) || isalpha(text[i-1])));
-		if(accept_range) {
-			LockData(true, true);
-			res = do_formula((DataObj*)parent, text+1);
-			if(res->type != ET_ERROR) accept_range = false;
-			if(!accept_range) {
-				if(text[i-1] == '(' || text[i-1] == ',' || text[i-1] == ';')
-					accept_range = true;
-				}
-			((DataObj*)parent)->Command(CMD_CLEAR_ERROR, 0L, 0L);
-			LockData(false, false);
-			}
-		((DataObj*)parent)->Command(CMD_ETRACC, accept_range ? this : 0L, 0L);
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// output formated text - style and font changes
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-typedef struct _tag_info {
-	char *tag;
-	int and_style, or_style;
-	int font, op;
-}tag_info;
-
-#define UC_TAG 100
-
-static tag_info tags[] = {
-	{"<i>", ~TXS_NORMAL, TXS_ITALIC, -1, 0},
-	{"</i>", ~TXS_ITALIC, TXS_NORMAL, -1, 0},
-	{"<b>", ~TXS_NORMAL, TXS_BOLD, -1, 0},
-	{"</b>", ~TXS_BOLD, TXS_NORMAL, -1, 0},
-	{"<u>", ~TXS_NORMAL, TXS_UNDERLINE, -1, 0},
-	{"</u>", ~TXS_UNDERLINE, TXS_NORMAL, -1, 0},
-	{"<sup>", ~TXS_NORMAL, TXS_SUPER, -1, 0},
-	{"</sup>", ~TXS_SUPER, TXS_NORMAL, -1, 0},
-	{"<sub>", ~TXS_NORMAL, TXS_SUB, -1, 0},
-	{"</sub>", ~TXS_SUB, TXS_NORMAL, -1, 0},
-	{"<face=helvetica>", 0, 0, FONT_HELVETICA, 0},
-	{"<face=times>", 0, 0, FONT_TIMES, 0},
-	{"<face=courier>", 0, 0, FONT_COURIER, 0},
-	{"<face=greek>", 0, 0, FONT_GREEK, 0},
-	{"</face>", 0, 0, -2, 0},
-	{"<bullet1>", 0, 0, -1, 1},	{"<bullet2>", 0, 0, -1, 2},
-	{"<bullet3>", 0, 0, -1, 3},	{"<bullet4>", 0, 0, -1, 4},
-	{"<bullet5>", 0, 0, -1, 5},	{"<bullet6>", 0, 0, -1, 6},
-	{"<bullet7>", 0, 0, -1, 7},	{"<bullet8>", 0, 0, -1, 8},
-	{"<bullet9>", 0, 0, -1, 9},	{"<bullet10>", 0, 0, -1, 10},
-	{"<white>", 0, 0, -1, 100},	{"<black>", 0, 0, -1, 101},
-	{"<red>", 0, 0, -1, 102},	{"<blue>", 0, 0, -1, 103},
-	{"<green>", 0, 0, -1, 104},	{"<yellow>", 0, 0, -1, 105},
-	{"<cyan>", 0, 0, -1, 106},	{"<purple>", 0, 0, -1, 107},
-	{"<smaller>", 0, 0, -1, 200},	{"<larger>", 0, 0, -1, 201},
-	{0L, 0, 0, 0, 0}};
-
-static int font_buff[256];
-static unsigned font_idx=0;
-fmtText::fmtText()
-{
-	src=0L;		split_text=0L;		split_text_W = 0;
-	n_split = n_split_W = uc_state = 0;
-	pos.x = pos.y = 0;				flags =0x0;
-}
-
-fmtText::fmtText(anyOutput *o, int x, int y, char *txt)
-{
-	if(txt && txt[0]) src = (char*)memdup(txt, (int)strlen(txt)+1, 0);
-	else src = 0L;		split_text = 0L;	split_text_W = 0L;
-	n_split = n_split_W = uc_state = 0;		flags = 0x0;
-	pos.x = x;	pos.y = y;		if(src)Parse();
-	if(o) DrawText(o);
-}
-
-fmtText::~fmtText()
-{
-	SetText(0L, 0L, 0L, 0L);
-}
-
-bool
-fmtText::StyleAt(int idx, TextDEF *txt_def, int *style, int *font)
-{
-	TextDEF td;
-	int i, j, n;
-
-	if(!src || !split_text || (idx > (int)strlen(src))) return false;
-	memcpy(&td, txt_def, sizeof(TextDEF));
-	for(i = j = 0; i < n_split; i++) {
-		if((n=split_text[i].tag) >= 0 && n < UC_TAG && SetTextDef(&td, n)) j += (int)strlen(tags[n].tag);
-		if(j > idx) break;
-		if(split_text[i].txt && split_text[i].txt[0]) j += (int)strlen(split_text[i].txt);
-		if(j >= idx) break;
-		}
-	if(style) *style = td.Style;		if(font) *font = td.Font;
-	return true;
-}
-
-int
-fmtText::rightTag(char *txt, int cb)
-{
-	int i, j;
-
-	for(i = 0; tags[i].tag; i++) {
-		for(j=1; tags[i].tag[j] && txt[cb+j]; j++) if(tags[i].tag[j] != txt[cb+j]) break;
-		if(!tags[i].tag[j]) return i;
-		}
-	return -1;
-}
-
-int
-fmtText::leftTag(char *txt, int cb)
-{
-	int i, j, k;
-
-	for(i = 0; tags[i].tag; i++) {
-		for(j = 0, k=(int)strlen(tags[i].tag)-1; tags[i].tag[j] && k <=cb; j++, k--) {
-			if(tags[i].tag[j] != txt[cb-k]) break;
-			}
-		if(!tags[i].tag[j]) return i;
-		}
-	return -1;
-}
-
-int 
-fmtText::ucTag(char *txt, int cb, int *tl, int *ucc)
-{
-	int i, uc=0;
-
-	if(txt[cb] != '&' || txt[cb+1] != '#') return -1;
-	for(i = cb+2; txt[i] && txt[i] != ';'; i++) {
-		if(!isdigit(txt[i])) return -1;
-		}
-	if(txt[i] != ';') return -1;
-	i -= cb;
-#ifdef USE_WIN_SECURE
-	sscanf_s(txt+cb+2, "%d", &uc);
-#else
-	sscanf(txt+cb+2, "%d", &uc);
-#endif
-	if(tl) *tl = i+1;		if(ucc) *ucc = uc;
-	return UC_TAG;
-}
-
-int
-fmtText::ucLeft(char *txt, int cb, int *tl, int *ucc)
-{
-	int i, uc = 0;
-
-	for(i = 1; i < 6 && i < cb && txt[cb-i] != '#'; i++) {
-		if(!isdigit(txt[cb-i])) return -1;
-		}
-	if(txt[cb-i] != '#' || txt[cb-i-1] != '&') return -1;
-#ifdef USE_WIN_SECURE
-	sscanf_s(txt+cb-i+1, "%d", &uc);
-#else
-	sscanf(txt+cb-i+1, "%d", &uc);
-#endif
-	if(tl) *tl = i+1;			if(ucc) *ucc = uc;
-	return UC_TAG;
-}
-
-void
-fmtText::cur_right(int *pos)
-{
-	int n, tl;
-
-	if(!src || !pos || *pos >= (int)strlen(src) || !src[*pos]) return;
-	if(src[*pos] == '<' && (n=rightTag(src, *pos)) >= 0) {
-		*pos += (int)strlen(tags[n].tag);
-		cur_right(pos);
-		}
-	else if(src[*pos] == '&' && (ucTag(src, *pos, &tl, 0L)== UC_TAG)){
-		*pos += tl;
-		}
-	else (*pos)++;
-}
-
-void 
-fmtText::cur_left(int *pos)
-{
-	int n, tl;
-
-	if(!src || !pos || !(*pos)) return;
-	(*pos)--;
-	if(*pos >= (n=(int)strlen(src))){
-		*pos = n-1;		return;
-		}
-	if(src[*pos] == ';' && (n=ucLeft(src, *pos, &tl, 0L)) == UC_TAG) {
-		*pos -= tl;		return;
-		}
-	while (src[*pos] == '>' && (n=leftTag(src, *pos)) >= 0) {
-		*pos -= (int)strlen(tags[n].tag);
-		}
-}
-
-bool
-fmtText::oGetTextExtent(anyOutput *o, int *width, int *height, int cb)
-{
-	TextDEF td1, td2;
-	int i, n, l, l1, w, w1, h, h1;
-
-	if(!o || !width || !height) return false;
-	if(!src || !src[0]) return false;
-	if(!cb) cb = (int)strlen(src);
-	if(!split_text) return o->oGetTextExtent(src, cb, width, height);
-	memcpy(&td1, &o->TxtSet, sizeof(TextDEF));	memcpy(&td2, &o->TxtSet, sizeof(TextDEF));
-	for(i = w = h = l = l1 = 0; i < n_split; i++){
-		if(split_text[i].tag == UC_TAG) {
-			o->oGetTextExtentW((w_char*)(&split_text[i].uc), 1, &w1, &h1);
-			w += w1;		h = h1 > h ? h1 : h;
-			l += split_text[i].uc_len;
-			if (l >= cb) {
-				*width = w;		*height = h;	o->SetTextSpec(&td1);
-				return true;
-				}
-			}
-		else {
-			if((n=split_text[i].tag) >= 0 && SetTextDef(&td2, n)) {
-				o->SetTextSpec(&td2);
-				l += (int)strlen(tags[n].tag);
-				}
-			if(tags[n].font == -1 && tags[n].op > 0 && tags[n].op < 100) {
-				w += o->un2ix(td2.fSize/2.0);
-				}
-			}
-		if(split_text[i].txt && split_text[i].txt[0]){
-			l1 = l;		l += (int)strlen(split_text[i].txt);
-			if (l1 >= cb) break;
-			o->oGetTextExtent(split_text[i].txt, l >= cb ? cb-l1 : 0, &w1, &h1);
-			w += w1;	h = h1 > h ? h1 : h;
-			if (l >= cb) break;
-			}
-		}
-	*width = w;			*height = h;		o->SetTextSpec(&td1);
-	return true;
-}
-
-void
-fmtText::SetText(anyOutput *o, char *txt, int *px, int *py)
-{
-	int i;
-
-	if(px) pos.x = *px;			if(py) pos.y = *py;
-	if(src && txt && !strcmp(src, txt)) {
-		if(o) DrawText(o);
-		return;
-		}
-	if(src) free(src);			src = 0L;
-	if(split_text) {
-		for(i = 0; i < n_split; i++) if(split_text[i].txt) free(split_text[i].txt);
-		free(split_text);		split_text = 0L;	n_split = 0;
-		}
-	if(split_text_W) {
-		for(i = 0; i < n_split_W; i++) {
-			if(split_text_W[i].uc_txt) free((split_text_W[i].uc_txt));
-			}
-		free(split_text_W);		split_text_W = 0L;	n_split_W = 0;
-		}
-	if(txt && txt[0]) src = (char*)memdup(txt, (int)strlen(txt)+1, 0);
-	if(src)Parse();
-	if(o) DrawText(o);
-}
-
-void
-fmtText::DrawText(anyOutput *o)
-{
-	if(!o || !src) return;
-	if(split_text)DrawFormatted(o);
-	else o->oTextOut(pos.x, pos.y, src, 0);
-}
-
-bool
-fmtText::SetTextDef(TextDEF *td, int idx)
-{
-	if(tags[idx].and_style != tags[idx].or_style) {
-		td->Style &= tags[idx].and_style;			td->Style |= tags[idx].or_style;
-		}
-	else if(tags[idx].font >= 0) {
-		font_buff[font_idx & 0xff] = td->Font;	font_idx++;
-		td->Font = tags[idx].font;
-		}
-	else if(tags[idx].font == -2) {
-		font_idx--;			td->Font=font_buff[font_idx & 0xff];
-		}
-	else return false;
-	return true;
-}
-
-bool
-fmtText::Parse()
-{
-	int i, li, j, n, tl, uc;
-	char *tmp;
-
-	if((flags & 0x01) || !src || !(tmp = (char*)memdup(src, (int)strlen(src)+1, 0))) return false;
-	for(i = li = uc_state = 0; src[i]; i++) {
-		if(i-li == 1 && split_text) {
-			if(src[li] == '<' && (n=rightTag(src, li))>=0) i--;
-			else if(src[li] == '&' && (n=ucTag(src, li, &tl, &uc))>0) i--;
-			}
-		if(src[i] == '<' && (n=rightTag(src, i))>=0) {
-			if(tags[n].font == FONT_GREEK) uc_state |= 0x02;
-			if(tags[n].op) uc_state |= 0x04;
-			if(split_text) {				//more tags in text
-				if(!(split_text = (fmt_txt_info *)realloc(split_text, (n_split+1)*sizeof(fmt_txt_info)))){
-					free(tmp);					return false;
-					}
-				for(j = li; j < i; j++) tmp[j-li] = src[j];	tmp[j-li]=0;
-				split_text[n_split-1].txt = (char*)memdup(tmp, (int)strlen((char*)tmp)+1, 0);
-				i += (int)strlen(tags[n].tag);	split_text[n_split].tag = n;
-				split_text[n_split++].txt = 0L;
-				}
-			else {							//first tag of text
-				if(!(split_text = (fmt_txt_info *)calloc(2, sizeof(fmt_txt_info)))){
-					free(tmp);					return false;
-					}
-				for(j = 0; j < i; j++) tmp[j]= src[j];	tmp[j]=0;
-				split_text[0].tag = -1;		
-				split_text[0].txt = (char*)memdup(tmp, (int)strlen((char*)tmp)+1, 0);
-				i += (int)strlen(tags[n].tag);	split_text[1].tag = n;
-				n_split = 2;
-				}
-			li = i;							i--;
-			}
-		else if(src[i] == '&' && (n=ucTag(src, i, &tl, &uc))>0) {
-			uc_state |= 0x01;
-			if(split_text) {				//more tags in text
-				if(!(split_text = (fmt_txt_info *)realloc(split_text, (n_split+1)*sizeof(fmt_txt_info)))){
-					free(tmp);					return false;
-					}
-				for(j = li; j < i; j++) tmp[j-li] = src[j];	tmp[j-li]=0;
-				split_text[n_split-1].txt = (char*)memdup(tmp, (int)strlen((char*)tmp)+1, 0);
-				i += tl;						split_text[n_split].tag = n;
-				split_text[n_split].uc = uc;	split_text[n_split].uc_len = tl;
-				split_text[n_split++].txt = 0L;
-				}
-			else {							//first tag of text
-				if(!(split_text = (fmt_txt_info *)calloc(2, sizeof(fmt_txt_info)))){
-					free(tmp);					return false;
-					}
-				for(j = 0; j < i; j++) tmp[j]= src[j];	tmp[j]=0;
-				split_text[0].tag = -1;		
-				split_text[0].txt = (char*)memdup(tmp, (int)strlen((char*)tmp)+1, 0);
-				i += tl;					split_text[1].tag = n;
-				split_text[1].uc = uc;		n_split = 2;
-				split_text[1].uc_len = tl;
-				}
-			li = i;
-			}
-		}
-	if(split_text && n_split && li < i && src[li]) 
-		split_text[n_split-1].txt = (char*)memdup(src+li, (int)strlen((char*)src+li)+1,0);
-	free(tmp);
-	return true;
-}
-
-void
-fmtText::DrawBullet(anyOutput *o, int x, int y, int type, double size, DWORD lc, DWORD fc)
-{
-	int is;
-	POINT pts[5];
-	static FillDEF fd = {0, 0x00ffffff, 1.0, 0L, 0x00ffffff};
-	static LineDEF ld = {defs.GetSize(SIZE_SYM_LINE), 1.0, 0x00000000L, 0x00000000};
-
-	switch (type) {
-	case 3:		case 4:
-		is = o->un2ix(size/4.1);
-		break;
-	case 5:		case 6:		case 7:		case 8:
-		is = o->un2ix(size/3.7);
-		break;
-	default:
-		is = o->un2ix(size/4.0);
-		break;
-		}
-	fd.color = fc;				ld.color = lc;
-	switch(type) {
-	case 1:
-		o->SetLine(&ld);				o->SetFill(&fd);
-		o->oCircle(x-is, y-is, x+is, y+is);
-		break;
-	case 2:
-		fd.color = ld.color;
-		o->SetLine(&ld);				o->SetFill(&fd);
-		o->oCircle(x-is, y-is, x+is, y+is);
-		break;
-	case 3:
-		o->SetLine(&ld);				o->SetFill(&fd);
-		o->oRectangle(x-is, y-is, x+is, y+is);
-		break;
-	case 4:
-		fd.color = ld.color;
-		o->SetLine(&ld);				o->SetFill(&fd);
-		o->oRectangle(x-is, y-is, x+is, y+is);
-		break;
-	case 5:		case 6:		case 7:		case 8:		case 9:		case 10:
-		if(type == 6 || type == 8 || type == 10) 		fd.color = ld.color;
-		pts[0].x = pts[3].x = pts[4].x = x - is;
-		pts[1].x = x;		pts[2].x = x+is;
-		if(type == 5 || type == 6) {
-			pts[0].y = pts[2].y = pts[3].y = y+o->un2iy(size*0.19439);
-			pts[1].y = y-o->un2iy(size*0.38878);
-			}
-		else if(type == 9 || type ==10) {
-			pts[0].y = pts[2].y = pts[4].y = y;
-			pts[1].y = y - is;		pts[3].y = y + is;		pts[3].x = x;
-			}
-		else {
-			pts[0].y = pts[2].y = pts[3].y = y-o->un2iy(size*0.19439);
-			pts[1].y = y+o->un2iy(size*0.38878);
-			}
-		o->SetLine(&ld);				o->SetFill(&fd);
-		o->oPolygon(pts, type < 9 ? 4 : 5);
-		break;
-		}
-}
-
-static int char2uc(char*src, w_char* dest, bool isGreek)
-{
-	int i;
-
-	if(!src || !*src) return 0;
-	for(i = 0; ; i++){
-		if(isGreek && src[i] >= 'A' && src[i] <= 'Z') dest[i] = (src[i] - 'A' + 0x391);
-		else if(isGreek && src[i] >= 'a' && src[i] <= 'z') dest[i] = (src[i] - 'a' + 0x3B1);
-		else dest[i] = (src[i]);
-		if(!dest[i]) return i;
-		}
-}
-
-bool 
-fmtText::DrawFormattedW(anyOutput *o)
-{
-	int i, j, n, cb, x, y, x1, y1, w, h;
-	double si, csi, fx, fy;
-	TextDEF td1, td2;
-	bool bGreek = false;
-	
-	if(!o || !(split_text_W = (fmt_uc_info *)calloc(n_split, sizeof(fmt_uc_info))))return false;
-	memcpy(&td1, &o->TxtSet, sizeof(TextDEF));	memcpy(&td2, &o->TxtSet, sizeof(TextDEF));
-	bGreek = (td1.Font == FONT_GREEK);
-	for(i = n_split_W = 0; i < n_split; i++){
-		if(split_text[i].tag == UC_TAG) {
-			if(i) {
-				j = n_split_W ? n_split_W-1 : 0;
-				cb = split_text[i].txt ? (int)strlen(split_text[i].txt) : 0;
-				if(split_text_W[j].uc_txt = (w_char*)realloc(split_text_W[j].uc_txt, 
-					(10 + cb + split_text_W[j].cb) * sizeof(w_char))) {
-					split_text_W[j].uc_txt[split_text_W[j].cb++] = split_text[i].uc;
-					if(cb) split_text_W[j].cb += char2uc(split_text[i].txt, split_text_W[j].uc_txt+split_text_W[j].cb, bGreek);
-					split_text_W[j].uc_txt[split_text_W[j].cb] = 0;
-					}
-				}
-			else {
-				split_text_W[0].cb = 1;				split_text_W[0].tag = -1;
-				if(split_text_W[0].uc_txt = (w_char*)malloc(2*sizeof(w_char))) {
-					split_text_W[0].uc_txt[0] = split_text[0].uc;
-					split_text_W[0].uc_txt[0] = 0;
-					}
-				}
-			}
-		else if(split_text[i].tag >= 0 && tags[split_text[i].tag].font == FONT_GREEK) {
-			if(!i) return false;	bGreek = true;
-			j = n_split_W-1;		cb = split_text[i].txt ? (int)strlen(split_text[i].txt) : 0;
-			if(split_text_W[j].uc_txt = (w_char*)realloc(split_text_W[j].uc_txt, 
-				(2 + cb + split_text_W[j].cb) * sizeof(w_char))) {
-				if(cb) split_text_W[j].cb += char2uc(split_text[i].txt, split_text_W->uc_txt+split_text_W[j].cb, bGreek);
-				}
-			}
-		else if(bGreek && split_text[i].tag >= 0 && tags[split_text[i].tag].font == -2){
-			if(!i) return false;	bGreek = false;
-			j = n_split_W-1;		cb = split_text[i].txt ? (int)strlen(split_text[i].txt) : 0;
-			if(split_text_W[j].uc_txt = (w_char*)realloc(split_text_W[j].uc_txt, 
-				(2 + cb + split_text_W[j].cb) * sizeof(w_char))) {
-				if(cb) split_text_W[j].cb += char2uc(split_text[i].txt, split_text_W->uc_txt+split_text_W[j].cb, bGreek);
-				}
-			}
-		else {
-			if((n=split_text[i].tag) >= 0) SetTextDef(&td2, n);
-			bGreek = (td2.Font == FONT_GREEK);
-			split_text_W[n_split_W].tag = split_text[i].tag;
-			split_text_W[n_split_W].cb = split_text[i].txt ? (int)strlen(split_text[i].txt) : 0;
-			if(split_text_W[n_split_W].cb && (split_text_W[n_split_W].uc_txt = 
-				(w_char*)malloc((1 + split_text_W[n_split_W].cb) * sizeof(w_char)))) {
-				char2uc(split_text[i].txt, split_text_W[n_split_W].uc_txt, bGreek);
-				}
-			else split_text_W[n_split_W].uc_txt = 0L;
-			n_split_W++;
-			}
-		}
-	memcpy(&td2, &td1, sizeof(TextDEF));
-	si = sin(td1.RotBL *0.01745329252);	csi = cos(td1.RotBL *0.01745329252);
-	fx = pos.x;		fy = pos.y;	
-	oGetTextExtent(o, &w, &h, 0);
-	if(td2.Align & TXA_HRIGHT) {
-		fx -= w*csi;		fy += w*si;
-		}
-	else if(td2.Align & TXA_HCENTER){
-		fx -= (w>>1)*csi;	fy += (w>>1)*si;
-		}
-	x = iround(fx);			y = iround(fy);
-	td2.Align &= ~(TXA_HRIGHT | TXA_HCENTER);			o->SetTextSpec(&td2);
-	for(i = 0; i < n_split_W; i++) {
-		if((n=split_text_W[i].tag) >= 0 && SetTextDef(&td2, n)) o->SetTextSpec(&td2);
-		else if(n >= 0 && tags[n].op) {
-			x1 = x + iround(o->un2fix(td2.fSize*0.25)*csi);
-			y1 = y - iround(o->un2fiy(td2.fSize*0.25)*si);
-			if((td2.Align & TXA_VTOP) == TXA_VTOP){
-				y1 += iround(o->un2fiy(td2.fSize*0.5)*csi);
-				x1 += iround(o->un2fix(td2.fSize*0.5)*si);
-				}
-			if((td2.Align & TXA_VCENTER) == TXA_VCENTER){
-				y1 -= iround(o->un2fiy(td2.fSize*0.5)*csi);
-				x1 -= iround(o->un2fix(td2.fSize*0.5)*si);
-				}
-			if((td2.Align & TXA_VBOTTOM) == TXA_VBOTTOM){
-				y1 -= iround(o->un2fiy(td2.fSize)*csi);
-				x1 -= iround(o->un2fix(td2.fSize)*si);
-				}
-			switch (tags[n].op) {
-			case 1:	case 2: case 3:	case 4: case 5:	case 6:	case 7:	case 8:	case 9:	case 10:
-				DrawBullet(o, x1, y1, tags[n].op, td2.fSize, td2.ColTxt, 0x00ffffff);
-				w = o->un2ix(td2.fSize/2.0);
-				x = iround(fx += (w*csi));		y = iround(fy -= (w*si));
-				break;
-			case 100:	td2.ColTxt = 0x00ffffffL;	break;
-			case 101:	td2.ColTxt = 0x00000000L;	break;
-			case 102:	td2.ColTxt = 0x000000ffL;	break;
-			case 103:	td2.ColTxt = 0x00ff0000L;	break;
-			case 104:	td2.ColTxt = 0x0000ff00L;	break;
-			case 105:	td2.ColTxt = 0x0000ffffL;	break;
-			case 106:	td2.ColTxt = 0x00ffff00L;	break;
-			case 107:	td2.ColTxt = 0x00ff00ffL;	break;
-			case 200:	td2.fSize *= 0.81;	td2.iSize = 0;	break;
-			case 201:	td2.fSize /= 0.81;	td2.iSize = 0;	break;
-				}
-			o->SetTextSpec(&td2);
-			}
-		if(split_text_W[i].uc_txt && split_text_W[i].uc_txt[0]){
-			o->oTextOutW(x, y, split_text_W[i].uc_txt, 0);
-			o->oGetTextExtentW(split_text_W[i].uc_txt, 0, &w, &h);
-			x = iround(fx += (w*csi));		y = iround(fy -= (w*si));
-			}
-		}
-	return true;
-}
-
-void 
-fmtText::DrawFormatted(anyOutput *o)
-{
-	int i, n, x, y, w, h;
-	TextDEF td1, td2;
-	double si, csi, fx, fy;
-
-	if(!o || !split_text) return;
-	if(uc_state && DrawFormattedW(o)) return;
-	memcpy(&td1, &o->TxtSet, sizeof(TextDEF));	memcpy(&td2, &o->TxtSet, sizeof(TextDEF));
-	si = sin(td1.RotBL *0.01745329252);	csi = cos(td1.RotBL *0.01745329252);
-	fx = pos.x;		fy = pos.y;	
-	oGetTextExtent(o, &w, &h, 0);
-	if(td2.Align & TXA_HRIGHT) {
-		fx -= w*csi;		fy += w*si;
-		}
-	else if(td2.Align & TXA_HCENTER){
-		fx -= (w>>1)*csi;	fy += (w>>1)*si;
-		}
-	x = iround(fx);			y = iround(fy);
-	td2.Align &= ~(TXA_HRIGHT | TXA_HCENTER);			o->SetTextSpec(&td2);
-	for(i = 0; i < n_split; i++) if(split_text[i].txt || split_text[i].tag == UC_TAG) {
-		if((n=split_text[i].tag) >= 0 && SetTextDef(&td2, n)) o->SetTextSpec(&td2);
-		if(split_text[i].txt && split_text[i].txt[0]){
-			o->oTextOut(x, y, split_text[i].txt, 0);
-			o->oGetTextExtent(split_text[i].txt, 0, &w, &h);
-			x = iround(fx += (w*csi));		y = iround(fy -= (w*si));
-			}
-		}
-}
-
-void
-fmtText::EditMode(bool bEdit)
-{
-	if(bEdit) flags |= 0x01;
-	else flags &= ~(0x01);
-}
-#undef UC_TAG
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// assign a value to a string
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-TextValue::TextValue()
-{
-	items = 0L;		nitems = 0;		next = inc = 1.0;
-}
-
-TextValue::TextValue(TextValItem **tvi, int ntvi)
-{
-	int i;
-
-	items = 0L;		nitems = 0;		next = inc = 1.0;
-	if(ntvi && (items = (TextValItem **)malloc(ntvi*sizeof(TextValItem*)))){
-		for(i = 0, nitems = ntvi; i < ntvi; i++) {
-			if(items[i] = (TextValItem*)memdup(tvi[i], sizeof(TextValItem), 0)){
-				if(tvi[i]->text) items[i]->text = (char*)memdup(tvi[i]->text, (int)strlen(tvi[i]->text)+1, 0);
-				}
-			}
-		nitems = ntvi;	if(items[nitems-1]) next = items[nitems-1]->val + inc;
-		}
-}
-
-TextValue::~TextValue()
-{
-	Reset();
-}
-
-double
-TextValue::GetValue(char *txt)
-{
-	unsigned int hv1, hv2;
-	int i;
-
-	if(!txt || !txt[0]) return 0.0;
-	hv1 = HashValue((unsigned char*)txt);
-	hv2 = Hash2((unsigned char*)txt);
-	if(items && nitems) for(i = 0; i < nitems; i++) {
-		if(items[i] && items[i]->hv1 == hv1 && items[i]->hv2 == hv2) return items[i]->val;
-		}
-	else if(!items &&(items = (TextValItem **)malloc(sizeof(TextValItem*)))) {
-		if(items[0] = (TextValItem*)calloc(1, sizeof(TextValItem))) {
-			items[0]->hv1 = hv1;		items[0]->hv2 = hv2;
-			items[0]->text = (char*)memdup(txt, (int)strlen(txt)+1, 0);
-			items[0]->val = next;		next += inc;
-			nitems = 1;					return (next-inc);
-			}
-		return 0.0;
-		}
-	else return 0.0;
-	if(items = (TextValItem **)realloc(items, (nitems+1)*sizeof(TextValItem*))){
-		if(items[nitems] = (TextValItem*)calloc(1, sizeof(TextValItem))) {
-			items[nitems]->hv1 = hv1;		items[nitems]->hv2 = hv2;
-			items[nitems]->text = (char*)memdup(txt, (int)strlen(txt)+1, 0);
-			items[nitems]->val = next;		next += inc;	nitems++;
-			return (next-inc);
-			}
-		}
-	return 0.0;
-}
-
-bool
-TextValue::GetItem(int idx, char **text, double *value)
-{
-	if(items && idx >=0 && idx < nitems) {
-		if(text) *text = items[idx]->text;
-		if(value) *value = items[idx]->val;
-		return true;
-		}
-	return false;
-}
-
-void
-TextValue::Reset()
-{
-	int i;
-
-	if(items) for(i = 0; i < nitems; i++){
-		if(items[i]->text) free(items[i]->text);
-		free(items[i]);
-		}
-	if(items && nitems) free(items);
-	next = inc = 1.0;	items = 0L;		nitems = 0;
-}
-
-TextValue *
-TextValue::Copy()
-{
-	return new TextValue(items, nitems);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// manage formats and style of a spreadsheet range
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-RangeInfo::RangeInfo(int sel, int cw, int rh, int fw, RangeInfo *next)
-{
-	r_type = sel;		col_width = cw;		row_height = rh;	first_width = fw;
-	ri_next = next;		ar = 0L;
-}
-
-RangeInfo::RangeInfo(int sel, char *range, RangeInfo *next)
-{
-	r_type = sel;		ri_next = next;
-	if(range && range[0]) {
-		ar = new AccRange(range);
-		}
-	else ar = 0L;
-}
-
-RangeInfo::~RangeInfo()
-{
-	if(ar) delete ar;
-}
-
-int
-RangeInfo::SetWidth(int w)
-{
-	if(r_type == 0 || r_type == 1) return col_width = w;
-	else if (ri_next) return ri_next->SetWidth(w);
-	return w;
-}
-
-int
-RangeInfo::SetDefWidth(int w)
-{
-	if(r_type == 0) return col_width = w;
-	else if (ri_next) return ri_next->SetDefWidth(w);
-	return w;
-}
-
-int
-RangeInfo::SetHeight(int h)
-{
-	if(r_type == 0) return row_height = h;
-	else if (ri_next) return ri_next->SetHeight(h);
-	return h;
-}
-
-int
-RangeInfo::SetFirstWidth(int w)
-{
-	if(r_type == 0) return first_width = w;
-	else if (ri_next) return ri_next->SetFirstWidth(w);
-	return w;
-}
-
-int
-RangeInfo::GetWidth(int col)
-{
-	int r, c;
-
-	if(r_type == 0) return col_width;
-	else if (ar && r_type == 1){
-		ar->GetFirst(&c, &r);
-		while(ar->NextCol(&c)) {
-			if(c == col) return col_width;
-			}
-		}
-	if(ri_next) return ri_next->GetWidth(col);
-	return 0;
-}
-
-int
-RangeInfo::GetHeight(int row)
-{
-	if(r_type == 0) return row_height;
-	else if (ri_next) return ri_next->GetHeight(row);
-	return row_height;
-}
-
-int
-RangeInfo::GetFirstWidth()
-{
-	if(r_type == 0) return first_width;
-	else if (ri_next) return ri_next->GetFirstWidth();
-	return first_width;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// the basic data object
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-DataObj::DataObj()
-{
-	cRows = cCols = 0;
-	etRows = 0L;
-	ri = new RangeInfo(0, 76, 19, 32, 0L);
-}
-
-DataObj::~DataObj()
-{
-	FlushData();
-	delete ri;
-}
-
-bool
-DataObj::Init(int nR, int nC)
-{
-	int i, j;
-
-	if(etRows)FlushData();
-	if(!(etRows = (EditText ***)calloc (cRows = nR, sizeof(EditText **)))) return false;
-	for(i = 0, cCols = nC; i < cRows; i++) {
-		if(!(etRows[i] = (EditText **)calloc(cCols, sizeof(EditText *)))) {
-			FlushData();	return false;
-			}
-		if(etRows[i]) for(j = 0; j < cCols; j++) {
-			etRows[i][j] = new EditText(this, 0L, i, j);
-			}
-		}
-	return true;
-}
-
-bool
-DataObj::SetValue(int row, int col, double val)
-{
-	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
-	if(etRows[row][col]) return etRows[row][col]->SetValue(val);
-	return false;
-}
-
-bool
-DataObj::SetText(int row, int col, char *txt)
-{
-	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
-	if(etRows[row][col]) return etRows[row][col]->SetText(txt);
-	return false;
-}
-
-bool
-DataObj::GetValue(int row, int col, double *v)
-{
-	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
-	if(etRows[row][col]) return etRows[row][col]->GetValue(v);
-	return false;
-}
-
-bool
-DataObj::GetText(int row, int col, char *txt, int len, bool bTranslate)
-{
-	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
-	if(txt && etRows[row][col]) return etRows[row][col]->GetText(txt, len, bTranslate);
-	return false;
-}
-
-char ** 
-DataObj::GetTextPtr(int row, int col)
-{
-	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return 0L;
-	if(etRows[row][col]) return &etRows[row][col]->text;
-	return 0L;
-}
-
-bool
-DataObj::GetResult(anyResult *r, int row, int col, bool use_last)
-{
-	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
-	if(etRows[row][col]) return etRows[row][col]->GetResult(r, use_last);
-	return false;
-}
-
-bool
-DataObj::GetSize(int *width, int *height)
-{
-	if(width)*width = cCols;		if(height)*height = cRows;
-	return true;
-}
-
-bool
-DataObj::isEmpty(int row, int col)
-{
-	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
-	if(etRows[row][col]) {
-		if((etRows[row][col]->type & 0xff) == ET_UNKNOWN)etRows[row][col]->Update(20, 0L, 0L);
-		if((etRows[row][col]->type & ET_EMPTY) == ET_EMPTY) return true;
-		}
-	return false;
-}
-
-void
-DataObj::FlushData()
-{
-	int i, j;
-
-	if(etRows){
-		for(i = 0; i < cRows; i++) if(etRows[i]) {
-			for (j = 0; j< cCols; j++) if(etRows[i][j]) delete etRows[i][j];
-			free(etRows[i]);
-			}
-		free(etRows);
-		}
-	etRows = 0L;
-}
-
-bool
-DataObj::ValueRec(RECT *rc)
-{
-	int r, c;
-	double val;
-
-	if(etRows && rc){
-		rc->left = cCols;	rc->right = 0;
-		rc->bottom = 0;		rc->top = cRows;
-		for(r = 0; r < cRows; r++) if(etRows[r]) {
-			for (c = 0; c< cCols; c++) {
-				if(etRows[r][c] && etRows[r][c]->GetValue(&val)) {
-					if(c < rc->left) rc->left =  c;
-					if(c > rc->right) rc->right =  c;
-					else c = rc->right;
-					if(r > rc->bottom) rc->bottom =  r;
-					else c = rc->right;
-					if(r < rc->top) rc->top =  r;
-					}
-				}
-			}
-		if(rc->right < rc->left) rc->right = rc->left < cCols ? rc->left : rc->left = 0;
-		if(rc->bottom < rc->top) rc->bottom = rc->top < cRows ? rc->top : rc->top = 0;
-		if(!rc->bottom && !rc->top && !rc->right && !rc->left) {
-			rc->right = cCols-1;	rc->bottom = cRows-1;
-			}
-		return true;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Store Data Object as strings: less memory required than with DataObj
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-StrData::StrData(DataObj *par, RECT *rc)
-{
-	int r1, c1, r2, c2, w, h;
-	char **tx;
-
-	pw = ph = 0;		str_data = 0L;
-	drc.left = drc.right = drc.top = drc.bottom = 0;
-	if(!(src = par)) return;
-	src->GetSize(&pw, & ph);
-	if(rc) {
-		if(0>(h = (rc->bottom - rc->top)) || 0>(w = (rc->right - rc->left))) return;
-		if(!(str_data = (char***)calloc(h+1, sizeof(char**))))return;
-		drc.left = rc->left;				drc.right = rc->right;
-		drc.top = rc->top;					drc.bottom = rc->bottom;
-		for (r1 = 0, r2 = drc.top; r1 <= h; r1++, r2++) {
-			if(!(str_data[r1] = (char**)malloc((w+1) * sizeof(char*)))) break;
-			for(c1 = 0, c2= drc.left; c1 <= w; c1++, c2++) {
-				tx = src->GetTextPtr(r2, c2);
-				if(tx && *tx && *tx[0]) {
-					str_data[r1][c1] = (char*)memdup(*tx, (int)strlen(*tx)+1, 0);
-					}
-				else str_data[r1][c1] = 0L;
-				}
-			}
-		}
-	else {
-		if(!(str_data = (char***)calloc(ph, sizeof(char**))))return;
-		for (r1 = 0; r1 < ph; r1++) {
-			if(!(str_data[r1] = (char**)malloc(pw * sizeof(char*)))) break;
-			for(c1 = 0; c1 < pw; c1++) {
-				tx = src->GetTextPtr(r1, c1);
-				if(tx && *tx && *tx[0]) {
-					str_data[r1][c1] = (char*)memdup(*tx, (int)strlen(*tx)+1, 0);
-					}
-				else str_data[r1][c1] = 0L;
-				}
-			}
-		drc.right = pw-1;		drc.bottom = ph-1;
-		}
-}
-
-StrData::~StrData()
-{
-	int r, c, w, h;
-
-	w = drc.right-drc.left;			h = drc.bottom-drc.top;
-	if(str_data) for (r = 0; r <= h; r++) {
-		if(str_data[r]) {
-			for(c = 0; c <= w; c++) if(str_data[r][c]) free(str_data[r][c]);
-			free(str_data[r]);
-			}
-		}
-	if(str_data) free(str_data);
-}
-
-bool
-StrData::GetSize(int *uw, int *uh)
-{
-	if(uw) *uw = pw;			if(uh) *uh = ph;
-	return true;
-}
-
-void
-StrData::RestoreData(DataObj *dest)
-{
-	int r1, c1, r2, c2;
-
-	if(!dest || !str_data) return;
-	for (r1 = 0, r2 = drc.top; r2 <= drc.bottom; r1++, r2++) {
-		if(str_data[r1]) {
-			for(c1 = 0, c2 = drc.left; c2 <= drc.right; c1++, c2++) 
-				dest->SetText(r2, c2, str_data[r1][c1]);
-			}
-		}
-	return;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// The notary class handles different types of supervision and indexing
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-notary::notary()
-{
-	gObs = 0L;
-	goStack = 0L;
-	NextPopGO = NextPushGO = NextRegGO = 0L;
-}
-
-notary::~notary()
-{
-	FreeStack();
-}
-
-int
-notary::RegisterGO(GraphObj *go)
-{
-	int i, j;
-
-	if(!go) return 0L;
-	if(!gObs) {
-		gObs = (GraphObj ***)calloc(0x2000L, sizeof(GraphObj **));
-		gObs[0] = (GraphObj **)calloc(0x2000L, sizeof(GraphObj *));
-		if(gObs && gObs[0]) {
-			gObs[0][0] = go;
-			return 1L;
-			}
-		return 0L;
-		}
-	i = (int)(NextRegGO >> 13);
-	j = (int)(NextRegGO & 0x1fff)+1;
-	if(j >=0x2000){
-		i++;	j = 0;
-		}
-	if(gObs[i] && gObs[i][j] && gObs[i][j] == go) {
-		NextRegGO = ((i << 13) | j);
-		return i*0x2000+j+1;
-		}
-	if(gObs && gObs[0]) {
-		for(i = 0; i < 0x2000; i++) {
-			for(j = 0; j < 0x2000L; j++) {
-				if(gObs[i][j] == go) {
-					NextRegGO = ((i << 13) | j);
-					return i*0x2000+j+1;
-					}
-				if(!gObs[i][j]) {
-					gObs[i][j] = go;
-					NextRegGO = ((i << 13) | j);
-					return i*0x2000+j+1;
-					}
-				}
-			if(i < 0x1fffL && !gObs[i+1])
-				gObs[i+1] = (GraphObj **)calloc(0x2000L, sizeof(GraphObj *));
-			if(i < 0x1fff && !gObs[i+1]) return 0;
-			}
-		}
-	return 0;
-}
-
-void
-notary::AddRegGO(GraphObj *go)
-{
-	int i, j;
-
-	if(!go) return;
-	if(!gObs) {
-		gObs = (GraphObj ***)calloc(0x2000L, sizeof(GraphObj **));
-		gObs[0] = (GraphObj **)calloc(0x2000L, sizeof(GraphObj *));
-		if(gObs && gObs[0]) {
-			gObs[0][0] = go;
-			return;
-			}
-		return;
-		}
-	i = (int)(NextRegGO >> 13);
-	j = (int)(NextRegGO & 0x1fff)+1;
-	if(j >=0x2000){
-		i++;
-		j = 0;
-		}
-	if(!gObs[i]) gObs[i] = (GraphObj **)calloc(0x2000L, sizeof(GraphObj *));
-	if(gObs[i] && !gObs[i][j]) {
-		gObs[i][j] = go;
-		NextRegGO = ((i << 13) | j);
-		}
-	else RegisterGO(go);
-}
-
-bool
-notary::PushGO(unsigned int id, GraphObj *go)
-{
-	int i, j;
-
-	NextPopGO = 0L;
-	if(!go) return true;
-	go->Id = id;
-	if(!goStack) {
-		if(!(goStack = (GraphObj ***)calloc(8192, sizeof(GraphObj **))))return false;
-		goStack[0] = (GraphObj **)calloc(8192, sizeof(GraphObj *));
-		if(goStack && goStack[0]) {
-			goStack[0][0] = go;
-			return true;
-			}
-		return false;
-		}
-	i = (int)(NextPushGO >> 13);
-	j = (int)(NextPushGO & 0x1fff)+1;
-	if(j >=0x2000){
-		i++;
-		j = 0;
-		}
-	if(!goStack || !goStack[0]) return false;
-	if(goStack[i] && !goStack[i][j]) {
-		goStack[i][j] = go;
-		NextPushGO = ((i << 13) | j);
-		return true;
-		}
-	for(i = 0; i < 0x2000; i++) {
-		for(j = 0; j < 0x2000; j++) {
-			if(!goStack[i][j]) {
-				goStack[i][j] = go;
-				NextPushGO = ((i << 13) | j);
-				return true;
-				}
-			}
-		if(i < 0x1fff && !goStack[i+1] && !(goStack[i+1] =
-			(GraphObj **)calloc(0x2000, sizeof(GraphObj *)))) return false;
-		}
-	return false;
-}
-
-GraphObj *
-notary::PopGO(unsigned int id)
-{
-	int i, j;
-	GraphObj *go;
-
-	NextPushGO = 0L;
-	if(!id || !goStack || !goStack[0]) return 0L;
-	i = (int)(NextPopGO >> 13);
-	j = (int)(NextPopGO & 0x1fff)+1;
-	if(j >=0x2000){
-		i++;
-		j = 0;
-		}
-	if(goStack[i] && goStack[i][j] && goStack[i][j]->Id == id) {
-		go = goStack[i][j];
-		goStack[i][j] = 0L;
-		go->Id = 0L;
-		NextPopGO = ((i << 13) | j);
-		return go;
-		}
-	for(i = 0; i < 0x2000; i++) {
-		for(j = 0; j < 0x2000; j++) {
-			if(goStack[i][j] && goStack[i][j]->Id == id) {
-				go = goStack[i][j];
-				goStack[i][j] = 0L;
-				go->Id = 0L;
-				NextPopGO = ((i << 13) | j);
-				return go;
-				}
-			}
-		if(i < 0x1fff && !goStack[i+1]) return 0L;
-		}
-	return 0L;
-}
-
-void
-notary::FreeStack()
-{
-	int i, j, k;
-
-	if(gObs) {
-		for(i = 0; gObs[i] && i <8192; i++) free(gObs[i]);
-		free(gObs);
-		gObs = 0L;
-		}
-	if(goStack) {
-		for(i = 0; goStack[i] && i <8192; i++){
-			for(j = k = 0; j < 8192; j++){
-				if(goStack[i][j]) {
-					goStack[i][j]->Id = 0L;
-					DeleteGO(goStack[i][j]);
-					k++;
-					}
-				}
-			free(goStack[i]);
-			}
-		free(goStack);
-		if(k){
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "%d objects deleted\nby notary", k);
-#else
-			sprintf(TmpTxt,"%d objects deleted\nby notary", k);
-#endif
-			ErrorBox(TmpTxt);
-			}
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Calculate continuous index to a range given by an ASCII string
-// string examples include  "a1:a12"  or  "a1:a4;a12:a24"
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-AccRange::AccRange(char *asc)
-{
-	int i, j, l;
-
-	if(asc && *asc && (l=(int)strlen(asc)) >1){
-		txt = (char *)malloc(l+2);
-		for(i = j = 0; i< (int)strlen(asc); i++)
-			if(asc[i] > 32) txt[j++] = asc[i];
-		txt[j] = 0;
-		}
-	else txt = 0L;
-	x1 = y1 = x2 = y2 = 0;
-}
-
-AccRange::~AccRange()
-{
-	if(txt) free(txt);
-}
-
-int
-AccRange::CountItems()
-{
-	int RetVal, l;
-
-	RetVal = 0;
-	if(txt && txt[0] && Reset()){
-		l = (int)strlen(txt);
-		do {
-			RetVal += ((x2-x1+1)*(y2-y1+1));
-			} while((curridx < l) && Parse(curridx));
-		}
-	return RetVal;
-}
-
-bool
-AccRange::GetFirst(int *x, int *y)
-{
-	if(txt && Reset()) {
-		if(x && y) {*x = x1; *y = y1;}
-		return true;
-		}
-	return false;
-}
-
-bool
-AccRange::GetNext(int *x, int *y)
-{
-	if(txt && x && y) {
-		if(cx <= x2) {*x = cx; *y = cy; cx++; return true;}
-		else {
-			cx = x1; cy++;
-			if(cy <= y2) return GetNext(x, y);
-			else if(txt[curridx]){
-				if(Parse(curridx)) return GetNext(x, y);
-				return false;
-				}
-			}
-		}
-	return false;
-}
-
-bool
-AccRange::NextRow(int *y)
-{
-	if(cy <= y2) {
-		*y = cy;	cy++;	cx = x1;	return true;
-		}
-	else if(txt[curridx] && Parse(curridx)) return NextRow(y);
-	return false;
-}
-
-bool
-AccRange::NextCol(int *x)
-{
-	if(cx <= x2) {
-		*x = cx;	cx++;	return true;
-		}
-	else if(txt[curridx] && Parse(curridx)) return NextCol(x);
-	return false;
-}
-
-bool
-AccRange::IsInRange(int x, int y)
-{
-	if(txt && Reset())	do {
-		if(x >= x1 && x <= x2 && y >= y1 && y <= y2) return true;
-		} while((curridx < (int)strlen(txt)) && Parse(curridx));
-	return false;
-}
-
-bool
-AccRange::BoundRec(RECT *rec)
-{
-	if(txt && Reset()){
-		SetMinMaxRect(rec, x1, y1, x2, y2);
-		while((curridx < (int)strlen(txt)) && Parse(curridx)) {
-			UpdateMinMaxRect(rec, x1, y1);	UpdateMinMaxRect(rec, x2, y2);
-			}
-		Reset();
-		return true;
-		}
-	return false;
-}
-
-bool
-AccRange::Reset()
-{
-	curridx = 0;
-	return Parse(curridx);
-}
-
-bool
-AccRange::Parse(int start)
-{
-	int i, l, step, *v;
-
-	i = start;
-	if(!txt) return false;
-	while(txt[i] == ';' || txt[i] == ',') i++;
-	if(!txt[i]) return false;
-	step = x1 = y1 = x2 = y2 = 0;
-	v = &x1;
-	for (l=(int)strlen(txt)+1 ; i < l; i++) {
-		if(txt[i] == '$') i++;
-		switch(step) {
-		case 0:
-		case 2:
-			if((txt[i] >= 'a') && (txt[i] <= 'z')){
-				*v *= 26;
-				*v += (txt[i]-'a'+1);
-				}
-			else if((txt[i] >= 'A') && (txt[i] <= 'Z')){
-				*v *= 26;
-				*v += (txt[i]-'A'+1);
-				}
-			else if((txt[i] >= '0') && (txt[i] <= '9')){
-				v = step == 0 ? &y1 : &y2;
-				*v = txt[i]-'0';
-				step++;
-				}
-			else return false;
-			break;
-		case 1:
-		case 3:
-			if((txt[i] >= '0') && (txt[i] <= '9')){
-				*v *= 10;
-				*v += (txt[i]-'0');
-				}
-			else if((txt[i] >= 'a') && (txt[i] <= 'z') ||
-				(txt[i] >= 'A') && (txt[i] <= 'Z')){
-				if(step == 1) v =  &x2;
-				else return false;
-				*v = txt[i] >='a' && txt[i] <= 'z' ? 
-					txt[i]-'a' : txt[i]-'A';
-				step++;
-				}
-			else if(step == 1 && (txt[i] == ':')) {
-				v = &x2;
-				step++;
-				}
-			else if((txt[i] == ';') || (txt[i] == ',') || (txt[i] == 0)) {
-				if(step == 1) {		//one single cell selected
-					x2 = x1;	y2 = y1;
-					}
-				if(x2<x1) Swap(x1,x2);		if(y2<y1) Swap(y1,y2);
-				if(y1 >=0) y1--;			if(y2 >=0) y2--;
-				if(x1 >=0) x1--;			if(x2 >=0) x2--;
-				curridx = i;
-				cx = x1; cy = y1;
-				return true;
-				}
-			break;
-			}
-		}
-	return false;
-}
-
-//get a description for the current range from the data object
-char *
-AccRange::RangeDesc(void *d, int style)
-{
-	anyResult res, res1;
-	int cb;
-	char *desc;
-
-	if(!d || !txt || !Reset())return 0L;
-	if(!((DataObj*)d)->GetResult(&res, y1, x1, false))return 0L;
-	if(style == 3) {
-		if(x1 == x2 && y1 > 0 && res.type == ET_TEXT) {
-			if(((DataObj*)d)->GetResult(&res1, 0, x1, false) && res1.type == ET_TEXT)
-				return (char*)memdup(res1.text, (int)strlen(res1.text)+1, 0);
-			}
-		if(y1 == y2 && x1 > 0 && res.type == ET_TEXT) {
-			if(((DataObj*)d)->GetResult(&res1, y1, 0, false) && res1.type == ET_TEXT)
-				return (char*)memdup(res1.text, (int)strlen(res1.text)+1, 0);
-			}
-		}
-	switch(res.type) {
-	case ET_TEXT:
-		if(res.text && res.text[0])
-			return (char*)memdup(res.text, (int)strlen(res.text)+1, 0);
-		else return 0L;
-	case ET_VALUE:
-		if(style != 4) break;
-	case ET_DATE:	case ET_DATETIME:	case ET_TIME:
-		TranslateResult(&res);
-		if(res.text && res.text[0])
-			return (char*)memdup(res.text, (int)strlen(res.text)+1, 0);
-		else return 0L;
-		}
-	if(!(desc = (char*)malloc(40*sizeof(char))))return 0L;
-	if(x1 == x2) {
-		if(style == 1 || style == 3) cb = rlp_strcpy(desc, 40, "Col. ");
-		else if(style == 2) cb = rlp_strcpy(desc, 40, "Column ");
-		else goto rdesc_err;
-		rlp_strcpy(desc+cb, 40-cb, Int2ColLabel(x1, true));
-		return desc;
-		}
-	else if(y1 == y2) {
-#ifdef USE_WIN_SECURE
-		sprintf_s(desc, 40, "Row %d", y1+1);
-#else
-		sprintf(desc, "Row %d", y1+1);
-#endif
-		return desc;
-		}
-rdesc_err:
-	free(desc);
-	return 0L;
-}
-
-int 
-AccRange::DataTypes(void *d, int *numerical, int *strings, int *datetime)
-{
-	anyResult res;
-	int n, r, c, c_num, c_txt, c_dattim;
-
-	if(!d || !Reset()) return 0;
-	for(n = c_num = c_txt = c_dattim = 0, GetFirst(&c, &r); GetNext(&c, &r); n++) {
-		if(((DataObj*)d)->GetResult(&res, r, c, false)) {
-			switch(res.type) {
-			case ET_VALUE:		c_num++;		break;
-			case ET_TEXT:		c_txt++;		break;
-			case ET_DATE:		case ET_TIME:	case ET_DATETIME:		
-				c_dattim++;		break;
-				}
-			}
-		}
-	if(numerical) *numerical = c_num;
-	if(strings) *strings = c_txt;
-	if(datetime) *datetime = c_dattim;
-	return n;
-}
-
-//---------------------------------------------------------------------------
-// Use the Delauney triangulation to create a 3D mesh of dispersed data
-//---------------------------------------------------------------------------
-void
-Triangle::SetRect()
-{
-	int i, i2;
-	double dy1, dy2, dx, dy;
-	double m1, m2, mx1, mx2, my1, my2;
-
-	//setup bounding rectangle
-	rc.Xmin = rc.Xmax = pt[0].fx;	rc.Ymin = rc.Ymax = pt[0].fy;
-	for(i = 1; i < 3; i++) {
-		if(pt[i].fx < rc.Xmin) rc.Xmin = pt[i].fx;
-		if(pt[i].fx > rc.Xmax) rc.Xmax = pt[i].fx;
-		if(pt[i].fy < rc.Ymin) rc.Ymin = pt[i].fy;
-		if(pt[i].fy > rc.Ymax) rc.Ymax = pt[i].fy;
-		}
-	//get three line equations in 2D
-	for(i = 0; i < 3; i++) {
-		i2 = (i+1)%3;
-		ld[i].fx = pt[i].fy;
-		if(pt[i].fx != pt[i2].fx) {
-			ld[i].fy = (pt[i2].fy - pt[i].fy) / (pt[i2].fx - pt[i].fx);
-			}
-		else ld[i].fy = HUGE_VAL;
-		}
-	//close polygon
-	pt[3].fx = pt[0].fx;	pt[3].fy = pt[0].fy;	pt[3].fz = pt[0].fz;
-	//circumcricle
-	dy1 = fabs(pt[0].fy - pt[1].fy);			dy2 = fabs(pt[1].fy - pt[2].fy);
-	m1 = (pt[0].fx - pt[1].fx)/(pt[1].fy - pt[0].fy);
-	m2 = (pt[1].fx - pt[2].fx)/(pt[2].fy - pt[1].fy);
-	mx1 = (pt[0].fx + pt[1].fx)/2.0;			my1 = (pt[0].fy + pt[1].fy)/2.0;
-	mx2 = (pt[1].fx + pt[2].fx)/2.0;			my2 = (pt[1].fy + pt[2].fy)/2.0;
-	if(dy1 < 1.0e-16 && dy2 < 1.0e-16) {
-		cy = (pt[0].fy + pt[1].fy + pt[2].fy)/3.0;
-		cx = (pt[0].fx + pt[1].fx + pt[2].fx)/3.0;
-		r2 = 0.0;			return;
-		}
-	else if(dy1 < 1.0e-16) {
-		cx = (pt[0].fx + pt[1].fx)/2.0;			cy = m2 * (cx - mx2) + my2;
-		}
-	else if(dy2 < 1.0e-16) {
-		cx = (pt[2].fx + pt[1].fx)/2.0;			cy = m1 * (cx - mx1) + my1;
-		}
-	else {
-		cx = (m1*mx1-m2*mx2+my2-my1)/(m1-m2);	cy = m1*(cx - mx1) + my1;
-		}
-	dx = pt[1].fx - cx;	dy = pt[1].fy - cy;		r2 = dx * dx + dy * dy;
-}
-
-bool
-Triangle::TestVertex(double x, double y)
-{
-	double dx, dy;
-
-	dx = x-cx;		dx = dx * dx;		dy = y-cy;		dy = dy * dy;
-	return (dx+dy)<r2;
-}
-
-Triangulate::Triangulate(Triangle *t_list)
-{
-	trl = t_list;		edges = 0L;
-}
-
-bool
-Triangulate::AddEdge(fPOINT3D *p1, fPOINT3D *p2)
-{
-	edge *ce, *ne;
-
-	//if edge exists delete both the new and the existing edge
-	for(ce = edges, ne = 0L; (ce); ) {
-		if((ce->p1.fx == p1->fx && ce->p1.fy == p1->fy && ce->p1.fz == p1->fz
-			&& ce->p2.fx == p2->fx && ce->p2.fy == p2->fy && ce->p2.fz == p2->fz)
-			|| (ce->p2.fx == p1->fx && ce->p2.fy == p1->fy && ce->p2.fz == p1->fz
-			&& ce->p1.fx == p2->fx && ce->p1.fy == p2->fy && ce->p1.fz == p2->fz)) {
-			if(ne) ne->next = ce->next;
-			else edges = ce->next;
-			delete ce;					return true;
-			}
-		ne = ce;	ce = ce->next;
-		}
-	//come here for new edge
-	if(ne = new edge()) {
-		ne->p1.fx = p1->fx;		ne->p1.fy = p1->fy;		ne->p1.fz = p1->fz;
-		ne->p2.fx = p2->fx;		ne->p2.fy = p2->fy;		ne->p2.fz = p2->fz;
-		ne->next = edges;		edges = ne;
-		}
-	return false;
-}
-
-bool
-Triangulate::AddVertex(fPOINT3D *v)
-{
-	Triangle *trc, *trn, *tr1;
-	edge *ce, *ae;
-
-	for(trc = trl, trn = 0L, edges = 0L; (trc);) {
-		tr1 = trc->next;
-		//delete triangles whose circumcircle enclose the new vertex
-		if(trc->TestVertex(v->fx, v->fy)) {
-			AddEdge(&trc->pt[0], &trc->pt[1]);		AddEdge(&trc->pt[1], &trc->pt[2]);
-			AddEdge(&trc->pt[0], &trc->pt[2]);
-			if(trn) trn->next = trc->next;
-			else trl = trc->next;
-			if(trl == trc) trl = 0L;	
-			delete trc;
-			}
-		else trn = trc;
-		trc = tr1;
-		}
-	//create new triangles from those edges which where found only once
-	for(ce = edges; (ce); ) {
-		if(trn = new Triangle()) {
-			trn->pt[0].fx = ce->p1.fx;	trn->pt[0].fy = ce->p1.fy;	trn->pt[0].fz = ce->p1.fz;
-			trn->pt[1].fx = ce->p2.fx;	trn->pt[1].fy = ce->p2.fy;	trn->pt[1].fz = ce->p2.fz;
-			trn->pt[2].fx = v->fx;		trn->pt[2].fy = v->fy;		trn->pt[2].fz = v->fz;
-			trn->SetRect();				trn->next = trl;			trl = trn;
-			ae = ce->next;				delete(ce);					ce = ae;
-			}
-		}
-	return true;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Default data vault
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Default::Default()
-{
-	dUnits = cUnits = 0;
-	rlp_strcpy(DecPoint, 2,  ".");		rlp_strcpy(ColSep, 2, ",");
-	Line_0.width = .4;		Line_1.width = .04,		Line_2.width = 0.016;
-	Line_0.patlength = 6.0;	Line_1.patlength = 0.6;	Line_2.patlength = 0.24;
-	Line_0.color = Line_1.color = Line_2.color = 0x00000000L;	//black
-	Line_0.pattern = Line_1.pattern = Line_2.pattern = 0L;		//solid line
-	FillLine_0.width = FillLine_1.width = FillLine_2.width = 0.0;
-	FillLine_0.patlength = 6.0;	FillLine_1.patlength = 0.6;	FillLine_2.patlength = 0.24;
-	Line_0.color = Line_1.color = Line_2.color = 0x00000000L;	//black
-	Line_0.pattern = Line_1.pattern = Line_2.pattern = 0L;		//solid line
-	Fill_0.type = Fill_1.type = Fill_2.type = FILL_NONE;
-	Fill_0.color = Fill_1.color = Fill_2.color = 0x00ffffffL;	//white background
-	Fill_0.scale = Fill_1.scale = Fill_2.scale = 1.0;			//size = 100%
-	Fill_0.hatch = &FillLine_0;	Fill_1.hatch = &FillLine_1;	Fill_2.hatch = &FillLine_2;
-	Fill_0.color2 = Fill_1.color2 = Fill_2.color2 = 0x00ffffffL;	//white background
-	OutLine_0.width = .2;	OutLine_1.width = .02;	OutLine_2.width = 0.008;
-	OutLine_0.patlength = 6.0;	OutLine_1.patlength = 0.6;	OutLine_2.patlength = 0.24;
-	OutLine_0.color = OutLine_1.color = OutLine_2.color = 0x00000000L;
-	OutLine_0.pattern = OutLine_1.pattern = OutLine_2.pattern = 0L;
-	pl = pgl = 0L;	pg = 0L;	pg_fl = 0L;	rrect_rad = 0L;
-	cdisp = 0L;		min4log = 0.000001;		axis_color = 0x0L;
-	ss_txt = 0.9;
-	svgAttr = svgScript = currPath = IniFile = 0L;
-	File1 = File2 = File3 = File4 = File5 = File6 = 0L;
-	if(fmt_date = (char*)malloc(20)) rlp_strcpy(fmt_date, 20, "Z.V.Y");
-	if(fmt_time = (char*)malloc(20)) rlp_strcpy(fmt_time, 20, "H:M:S");
-	if(fmt_datetime = (char*)malloc(20)) rlp_strcpy(fmt_datetime, 20, "Z.V.Y H:M:S");
-}
-
-Default::~Default()
-{
-	if(svgAttr) free(svgAttr);		if(svgScript) free(svgScript);
-	if(currPath) free(currPath);	if(IniFile) free(IniFile);
-	svgAttr = svgScript = currPath = 0L;
-	if(pl) free(pl);				if(pgl) free(pgl);
-	if(pg_fl) free(pg_fl);			if(pg) free(pg);
-	if(rrect_rad) free(rrect_rad);
-	if(File1) free(File1);			if(File2) free(File2);
-	if(File3) free(File3);			if(File4) free(File4);
-	if(File5) free(File5);			if(File6) free(File6);
-	if(fmt_date) free(fmt_date);	if(fmt_time) free(fmt_time);
-	if(fmt_datetime) free(fmt_datetime);
-}
-
-void
-Default::SetDisp(anyOutput *o)
-{
-	if(o && o != cdisp) {
-		Undo.SetDisp(o);
-		cdisp = o;
-		}
-}
-
-double
-Default::GetSize(int select)
-{
-	double RetVal = 0.0;
-
-	switch (select){
-	case SIZE_SYMBOL:				RetVal = 3.0;		break;
-	case SIZE_SYM_LINE:				RetVal = 0.2;		break;
-	case SIZE_DATA_LINE:
-		switch(cUnits) {
-		case 1:		return Line_1.width;
-		case 2:		return Line_2.width;
-		default:	return Line_0.width;
-		}
-	case SIZE_TEXT:					RetVal = _TXH;		break;
-	case SIZE_GRECT_TOP:			RetVal = 0.0;		break;
-	case SIZE_GRECT_BOTTOM:			RetVal = 110.0;		break;
-	case SIZE_GRECT_LEFT:			RetVal = 0.0;		break;
-	case SIZE_GRECT_RIGHT:			RetVal = 160.0;		break;
-	case SIZE_DRECT_TOP:			RetVal = 10.0;		break;
-	case SIZE_DRECT_BOTTOM:			RetVal = 90.0;		break;
-	case SIZE_DRECT_LEFT:			RetVal = 25.0;		break;
-	case SIZE_DRECT_RIGHT:			RetVal = 140.0;		break;
-	case SIZE_PATLENGTH:
-	case SIZE_DATA_LINE_PAT:
-		switch(cUnits) {
-		case 1:		return Line_1.patlength;
-		case 2:		return Line_2.patlength;
-		default:	return Line_0.patlength;
-		}
-	case SIZE_AXIS_TICKS:			RetVal = 2.0;		break;
-	case SIZE_TICK_LABELS:			RetVal = _TXH;		break;
-	case SIZE_WHISKER:
-	case SIZE_ERRBAR:				RetVal = 3.0;		break;
-	case SIZE_WHISKER_LINE:
-	case SIZE_AXIS_LINE:
-	case SIZE_BAR_LINE:			
-	case SIZE_ERRBAR_LINE:
-		switch(cUnits) {
-		case 1:		return OutLine_1.width;
-		case 2:		return OutLine_2.width;
-		default:	return OutLine_0.width;
-		}
-	case SIZE_BOX:
-	case SIZE_BAR:					RetVal = 10.0;				break;
-	case SIZE_BUBBLE_LINE:			RetVal = 0.4;				break;
-	case SIZE_BUBBLE_HATCH_LINE:	RetVal = 0.1;				break;
-	case SIZE_ARROW_LINE:			RetVal = 0.4;				break;
-	case SIZE_ARROW_CAPWIDTH:		RetVal = 3.0;				break;
-	case SIZE_ARROW_CAPLENGTH:		RetVal = 4.0;				break;
-	case SIZE_HAIRLINE:				RetVal = 0.1;				break;
-	case SIZE_SEGLINE:				RetVal = 0.4;				break;
-	case SIZE_CELLWIDTH:			RetVal = 20.0;				break;
-	case SIZE_CELLTEXT:
-#ifdef _WINDOWS
-		RetVal = 4.5*ss_txt;		break;
-#else
-		RetVal = 4.5*ss_txt*0.7;	break;
-#endif
-	case SIZE_RRECT_RAD:			
-		return rrect_rad ? *rrect_rad : GetSize(SIZE_SYMBOL);
-	case SIZE_SCALE:				return 1.0;
-	default:	return 0.0;
-		}
-	switch(cUnits) {
-	case 1:	RetVal /= 10.0;	break;
-	case 2:	RetVal = NiceValue(RetVal/25.4);	break;
-		}
-	return RetVal;
-}
-
-DWORD
-Default::Color(int select)
-{
-	switch (select){
-	case COL_ERROR_LINE:
-	case COL_WHISKER:
-	case COL_SYM_LINE:			return 0x00000000L;
-	case COL_SYM_FILL:			return 0x00ffffffL;
-	case COL_ARROW:
-	case COL_DATA_LINE:			return Line_0.color;
-	case COL_TEXT:				return 0x00000000L;
-	case COL_BG:				return 0x00ffffffL;
-	case COL_AXIS:				return axis_color;
-	case COL_BAR_LINE:			return OutLine_0.color;
-	case COL_BAR_FILL:			return 0x00ffffffL;
-	case COL_BUBBLE_LINE:
-	case COL_BUBBLE_FILLLINE:	return 0x00ff0000L;
-	case COL_BUBBLE_FILL:		return 0x00ffc0c0L;
-	case COL_DRECT:				return 0x00ffffffL;
-	case COL_GRECT:				return 0x00ffffffL;
-	case COL_GRECTLINE:			return 0x00e0e0e0L;
-	case COL_POLYLINE:			return pl ? pl->color : OutLine_0.color;
-	case COL_POLYGON:			return pgl ? pgl->color : OutLine_0.color;
-	default:					return 0x00C0C0C0L;	//Error
-	}
-}
-
-LineDEF *
-Default::GetLine()
-{
-	switch (cUnits) {
-	case 1:		return &Line_1;
-	case 2:		return &Line_2;
-	default:	return &Line_0;
-		}
-}
-
-void
-Default::SetLine(int u, LineDEF *l, int which)
-{
-	double lw, pl;
-	LineDEF *l1, *l2, *l3;
-
-	switch (which) {
-	case 0:	l1 = &Line_0;		l2 = &Line_1;		l3 = &Line_2;		break;
-	case 1:	l1 = &FillLine_0;	l2 = &FillLine_1;	l3 = &FillLine_2;	break;
-	case 2:	l1 = &OutLine_0;	l2 = &OutLine_1;	l3 = &OutLine_2;	break;
-	default: return;
-		}
-	l1->color = l2->color = l3->color = l->color;
-	l1->pattern = l2->pattern = l3->pattern = l->pattern;
-	switch (u) {
-	case 1:
-		lw = l->width*10.0;	pl = l->patlength*10.0;
-		l1->width = lw;			l1->patlength = pl;
-		l2->width = l->width;	l2->patlength = l->patlength;
-		l3->patlength = NiceValue(pl/25.4);
-		l3->patlength = NiceValue(pl/25.4);
-		break;
-	case 2:
-		lw = NiceValue(l->width*25.4);	pl = NiceValue(l->patlength*25.4);
-		l1->width = lw;			l1->patlength = pl;
-		l2->width = lw/10.0;	l2->patlength = pl/10.0;
-		l3->width = l->width;	l3->patlength = l->patlength;
-		break;
-	default:
-		lw = l->width;		pl = l->patlength;
-		l1->width = l->width;	l1->patlength = l->patlength;
-		l2->width = lw/10.0;		l2->patlength = pl/10.0;
-		l3->patlength = NiceValue(pl/25.4);
-		l3->patlength = NiceValue(pl/25.4);
-		break;
-		}
-}
-
-FillDEF *
-Default::GetFill()
-{
-	switch (cUnits) {
-	case 1:		return &Fill_1;
-	case 2:		return &Fill_2;
-	default:	return &Fill_0;
-		}
-}
-
-void
-Default::SetFill(int u, FillDEF *fd)
-{
-	memcpy(&Fill_0, fd, sizeof(FillDEF));
-	memcpy(&Fill_1, fd, sizeof(FillDEF));
-	memcpy(&Fill_2, fd, sizeof(FillDEF));
-	if(fd->hatch) SetLine(u, fd->hatch, 1);
-	Fill_0.hatch = &FillLine_0;
-	Fill_1.hatch = &FillLine_1;
-	Fill_2.hatch = &FillLine_2;
-}
-
-LineDEF *
-Default::GetOutLine()
-{
-	switch (cUnits) {
-	case 1:		return &OutLine_1;
-	case 2:		return &OutLine_2;
-	default:	return &OutLine_0;
-		}
-}
-
-LineDEF *
-Default::plLineDEF(LineDEF *ld)
-{
-	if(ld) {
-		if(pl) free(pl);
-		if(pl = (LineDEF *)malloc(sizeof(LineDEF))) memcpy(pl, ld, sizeof(LineDEF));
-		}
-	if(pl) return pl;
-	else return GetOutLine();
-}
-
-LineDEF *
-Default::pgLineDEF(LineDEF *ol)
-{
-	if(ol) {
-		if(pgl) free(pgl);
-		if(pgl = (LineDEF *)malloc(sizeof(LineDEF))) memcpy(pgl, ol, sizeof(LineDEF));
-		}
-	if(pgl) return pgl;
-	else return GetOutLine();
-}
-
-FillDEF *
-Default::pgFillDEF(FillDEF *fd)
-{
-	if(fd) {
-		if(pg) free(pg);
-		if(pg = (FillDEF *)malloc(sizeof(FillDEF))){
-			memcpy(pg, fd, sizeof(FillDEF));
-			if(pg->hatch) {
-				if(pg_fl) free(pg_fl);
-				if(pg_fl = (LineDEF *)malloc(sizeof(LineDEF)))
-					memcpy(pg_fl, pg->hatch, sizeof(LineDEF));
-				pg->hatch = pg_fl;
-				}
-			}
-		}
-	if(pg) return pg;
-	else return GetFill();
-}
-
-double
-Default::rrectRad(double rad)
-{
-	if(!rrect_rad)rrect_rad=(double*)malloc(sizeof(double));
-	if(rrect_rad) return (*rrect_rad = rad);
-	return 0.0;
-}
-
-void
-Default::FileHistory(char *path)
-{
-	char *tmp_path = 0L, *tmp;
-	char **history[] = {&File1, &File2, &File3, &File4, &File5, &File6};
-	int i, j;
-
-	if(path && (tmp_path=(char*)malloc((i=(int)strlen(path))+10))){
-		rlp_strcpy(tmp_path, i+1, path);
-		for(j = i ; i > 0 && tmp_path[i] != '/' && tmp_path[i] != '\\'; i--);
-		tmp_path[i] = 0;
-		if(currPath) free(currPath);
-		if(tmp_path[0]) currPath = (char*)memdup(tmp_path, i+1, 0);
-		else currPath = 0L;
-		rlp_strcpy(tmp_path, j+1, path);
-		if(File1 && strcmp(File1, tmp_path)) {
-			for(i = 0; i < 6 && tmp_path; i++) {
-				if(i && *history[i] && !strcmp(File1, *history[i])){
-					free(*history[i]);
-					*history[i] = tmp_path;
-					tmp_path = 0L;
-					break;
-					}
-				if(*history[i]) {
-					if(strcmp(tmp_path, *history[i])){
-						tmp = *history[i];
-						*history[i] = tmp_path;
-						tmp_path = tmp;
-						}
-					else { 
-						free(tmp_path);
-						tmp_path = 0L;
-						}
-					}
-				else{
-					tmp = *history[i];
-					*history[i] = tmp_path;
-					tmp_path = tmp;
-					}
-				}
-			}
-		if(!(*history[0])) {
-			*history[0] = tmp_path;
-			tmp_path = 0L;
-			}
-		}
-	if(tmp_path) free(tmp_path);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Chache file input for read operations
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-#define CharCacheSize 1024
-ReadCache::ReadCache()
-{
-	Cache = 0L;		idx = max = 0;		eof = true;
-}
-
-ReadCache::~ReadCache()
-{
-	if(Cache) free(Cache);
-	Cache = 0L;
-}
-
-bool
-ReadCache::Open(char *name)
-{
-	idx = max = 0;
-	eof = true;
-	if(!name) iFile = -1;
-#ifdef USE_WIN_SECURE
-	else if(_sopen_s(&iFile, name, O_BINARY, 0x40, S_IWRITE) || iFile < 0) return false;
-#else
-	else if(-1 ==(iFile = open(name, O_BINARY))) return false;
-#endif
-	Cache = (unsigned char *)malloc((unsigned)(CharCacheSize + 1));
-	if(Cache) return true;
-	return false;
-}
-
-void
-ReadCache::Close()
-{
-#ifdef USE_WIN_SECURE
-	if(iFile >= 0) _close(iFile);
-#else
-	if(iFile >= 0) close(iFile);
-#endif
-	if(Cache) free(Cache);				Cache = 0L;
-}
-
-unsigned char
-ReadCache::Getc()
-{
-	if(Cache){
-		if(idx < max) return (last = Cache[idx++]);
-		else {
-			do {
-#ifdef USE_WIN_SECURE
-				max = _read(iFile, Cache, CharCacheSize);
-#else
-				max = read(iFile, Cache, CharCacheSize);
-#endif
-				if(max <=0) {
-					eof = true;
-					return 0;
-					}
-				else eof = false;
-				}while(max == 0);
-			idx = 1;
-			return(last = Cache[0]);
-			}
-		}
-	return 0;
-}
-
-unsigned char *
-ReadCache::GetField()
-{
-	int i;
-	static unsigned char *ret;
-
-	if(Cache && max) {
-		while(idx < max && Cache[idx] < 43) idx++;
-		if(idx == max){
-			if(max == CharCacheSize) {
-#ifdef USE_WIN_SECURE
-				max = _read(iFile, Cache, CharCacheSize);
-#else
-				max = read(iFile, Cache, CharCacheSize);
-#endif
-				idx = 0;
-				return GetField();
-				}
-			else return 0L;
-			}
-		i = idx;
-		while(i < max && Cache[i] > 32 && Cache[i] <= 'z') i++;
-		if(i == max) {
-			for(i = 0; (Line[i] = Getc()) >32 && Line[i] <= 'z' && i < 4096; i++);
-			Line[i] = 0;
-			return Line;
-			}
-		else {
-			ret = Cache+idx;
-			idx = i;
-			return ret;
-			}
-		}
-	return 0L;
-}
-
-void
-ReadCache::ReadLine(char *dest, int size)
-{
-	int i=0;
-	unsigned char c;
-
-	dest[0] = 0;
-	do {
-		c =  Getc();
-		if(c == 0x09) c = 0x32;			// tab to space
-		if(c > 31) dest[i++] = (char)c;
-		}while(c && c != 0x0a && i < size);
-	dest[i] = 0;
-}
-
-bool
-ReadCache::GetInt(long *in)
-{
-	unsigned char *field;
-
-	field = GetField();
-	if(field && field[0]) {
-		*in = atol((char*)field);
-		if(*in == 0 && field[0] == '}') return false;
-		return true;
-		}
-	return false;
-}
-
-bool
-ReadCache::GetFloat(double *fn)
-{
-	unsigned char *field;
-
-	field = GetField();
-	if(field && field[0]) {
-		*fn = atof((char*)field);
-		if(*fn == 0.0 && field[0] == '}') return false;
-		return true;
-		}
-	return false;
-}
-
-unsigned char
-ReadCache::Lastc()
-{
-	return last;
-}
-
-bool
-ReadCache::IsEOF()
-{
-	return eof;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Process memory block as if file input
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-MemCache::MemCache(unsigned char *ptr)
-:ReadCache()
-{
-	if(ptr) {
-		max = (int)strlen((char*)ptr);
-		Cache = (unsigned char*) memdup(ptr, max+1, 0);
-		eof = false;
-		}
-}
-
-MemCache::~MemCache()
-{
-	if(Cache) free(Cache);
-	Cache = 0L;
-}
-
-unsigned char
-MemCache::Getc()
-{
-	if(Cache){
-		if(idx < max) return (last = Cache[idx++]);
-		else {
-			eof = true;
-			return 0;
-			}
-		}
-	return 0;
-}
-
-unsigned char *
-MemCache::GetField()
-{
-	int i;
-	static unsigned char *ret;
-
-	if(Cache && max) {
-		while(idx < max && Cache[idx] < 43) idx++;
-		i = idx;
-		while(i < max && Cache[i] > 32 && Cache[i] <= 'z') i++;
-		ret = Cache+idx;
-		idx = i;
-		return ret;
-		}
-	return 0L;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Process Undo 
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-#define UNDO_RING_SIZE 0x100
-#define UNDO_IDX_MASK 0xff
-UndoObj::UndoObj()
-{
-	buff = buff0 = (UndoInfo**)calloc(UNDO_RING_SIZE, sizeof(UndoInfo*));
-	stub1 = ndisp = 0;					busy = false;
-	pcb = &stub1;	cdisp = ldisp = 0L;	buffers = 0L;
-}
-
-UndoObj::~UndoObj()
-{
-	Flush();
-	if(buff0) free(buff0);
-}
-
-//free all resources associated with undo operations
-void
-UndoObj::Flush()
-{
-	int i, j;
-
-	if(!buffers) return;
-	for(i = 0; i < ndisp; i++) if(buffers[i]) {
-		if(buffers[i]->buff) {
-			for(j = 0; j < UNDO_RING_SIZE; j++) {
-				if(buffers[i]->buff[j]) FreeInfo(&buffers[i]->buff[j]);
-				}
-			free(buffers[i]->buff);
-			}
-		free(buffers[i]);
-		}
-	free(buffers);
-	stub1 = ndisp = 0;
-	pcb = &stub1;	cdisp = 0L;	buffers = 0L;
-}
-
-bool
-UndoObj::isEmpty(anyOutput *o)
-{
-	int i;
-
-	if(!buffers) return true;
-	for (i = 0; i < ndisp; i++) if(buffers[i]){
-		if(o && buffers[i]->disp == o ) {
-			if(buffers[i]->count > 0) return false;
-			else return true;
-			}
-		else if(!o && buffers[i]->count > 0) return false;
-		}
-	return true;
-}
-
-//select buffers associated with the current window/widget,
-//create new buffers if called for the first time
-void
-UndoObj::SetDisp(anyOutput *o)
-{
-	int i;
-	void *ptmp;
-
-	if(o && o != cdisp) {
-		ldisp = cdisp;
-		if(buffers) {
-			for(i = 0; i < ndisp; i++) {
-				if(buffers[i] && buffers[i]->disp == o){
-					cdisp = o;
-					buff = buffers[i]->buff;	pcb = &buffers[i]->count;
-					return;
-					}
-				else if(!buffers[i] && (buffers[i] = (UndoBuff*)calloc(1, sizeof(UndoBuff)))) {
-					buffers[i]->buff = (UndoInfo**)calloc(UNDO_RING_SIZE, sizeof(UndoInfo*));
-					buffers[i]->disp = cdisp = o;
-					buff = buffers[i]->buff;	pcb = &buffers[i]->count;
-					return;
-					}
-				}
-			if(ptmp = memdup(buffers, sizeof(UndoBuff*) *(ndisp+1), 0)){
-				free(buffers);		buffers = (UndoBuff**)ptmp;
-				if(buffers[ndisp] = (UndoBuff*)calloc(1, sizeof(UndoBuff))) {
-					buffers[ndisp]->buff = (UndoInfo**)calloc(UNDO_RING_SIZE, sizeof(UndoInfo*));
-					buffers[ndisp]->disp = cdisp = o;
-					buff = buffers[ndisp]->buff;	pcb = &buffers[ndisp]->count;
-					ndisp++;
-					}
-				}
-			}
-		else if(buffers = (UndoBuff**)calloc(1, sizeof(UndoBuff*))){
-			if(buffers[0] = (UndoBuff*)calloc(1, sizeof(UndoBuff))) {
-				buffers[0]->buff = (UndoInfo**)calloc(UNDO_RING_SIZE, sizeof(UndoInfo*));
-				buffers[0]->disp = cdisp = o;
-				ndisp = 1;
-				buff = buffers[0]->buff;	pcb = &buffers[0]->count;
-				}
-			}
-		}
-}
-
-//free memory for a closed output
-void 
-UndoObj::KillDisp(anyOutput *o)
-{
-	int i, j, c_buf;
-
-	InvalidateOutput(o);			//kill text cursor and animated rectangle
-	if(o && buffers) {
-		for(i = 0, c_buf = -1; i < ndisp; i++) {
-			if(buffers[i] && buffers[i]->disp == o)	{
-				c_buf = i;	break;
-				}
-			}
-		if(c_buf < 0) return;
-		if(buffers[c_buf]->buff) {
-			for(j = 0; j < UNDO_RING_SIZE; j++) {
-				if(buffers[c_buf]->buff[j]) FreeInfo(&buffers[c_buf]->buff[j]);
-				}
-			free(buffers[c_buf]->buff);
-			}
-		free(buffers[c_buf]);	buffers[c_buf] = 0L;
-		}
-	if(ldisp && o == cdisp) SetDisp(ldisp);
-	else cdisp = 0L;
-}
-
-//remove references to an invalid (probabbly deleted) object
-void
-UndoObj::InvalidGO(GraphObj *go)
-{
-	int i, i1, i2;
-
-	if(*pcb >10) {
-		if(*pcb < UNDO_RING_SIZE){
-			i1 = 0;		i2 = *pcb;
-			}
-		else {
-			i1 = ((*pcb) & UNDO_IDX_MASK);
-			i2 = (((*pcb)-UNDO_RING_SIZE) & UNDO_IDX_MASK);
-			if(i1 > i2) Swap(i1, i2);
-			}
-		}
-	else {
-		i1 = 0;		i2 = *pcb;
-		}
-	for(i = i1; i < i2; i++) {
-		if(buff[i] && buff[i]->owner == go) FreeInfo(&buff[i]);
-		if(buff[i]) switch(buff[i]->cmd) {
-//		case UNDO_OBJCONF:
-		case UNDO_OBJCONF_1:
-			if(buff[i]->loc == (void**)go) FreeInfo(&buff[i]);
-			break;
-			}
-		}
-}
-
-void
-UndoObj::Pop(anyOutput *o)
-{
-	int idx;
-
-	if(o) {
-		SetDisp(o);
-		if(*pcb < 1) return;
-		idx = ((*pcb-1) & UNDO_IDX_MASK);
-		if(buff[idx]) FreeInfo(&buff[idx]);
-		(*pcb)--;
-		}
-}
-
-void
-UndoObj::Restore(bool redraw, anyOutput*o)
-{
-	int i, j, idx;
-	DWORD flags;
-	UndoList *ul;
-	GraphObj **gol;
-	unsigned char *savbuf, *target;
-
-	if(o) SetDisp(o);
-	else if(cdisp) o = cdisp;
-	CurrGO = 0L;
-	if(*pcb < 1){
-		InfoBox("The UNDO-cache is empty");
-		return;
-		}
-	do {
-		idx = ((*pcb-1) & UNDO_IDX_MASK);
-		if(!buff[idx] && *pcb > 0) (*pcb)--;
-		} while(!buff[idx] && *pcb > 0);
-	if(!buff[idx]) return;
-	if(buff[idx]->zd.org.fx != cdisp->VPorg.fx ||
-		buff[idx]->zd.org.fy != cdisp->VPorg.fy || buff[idx]->zd.scale != cdisp->VPscale){
-		cdisp->VPorg.fx = buff[idx]->zd.org.fx;			cdisp->VPorg.fy = buff[idx]->zd.org.fy;
-		cdisp->VPscale = buff[idx]->zd.scale;			HideCopyMark();
-		if(cdisp->VPscale > 100.0) cdisp->VPscale = 100.0;
-		if(cdisp->VPscale < 0.05) cdisp->VPscale = 0.05;
-		if(buff[idx]->owner)
-			if(buff[idx]->owner->Id < GO_PLOT) CurrGO = buff[idx]->owner;
-			if(!((buff[idx]->owner)->Command(CMD_SETSCROLL, 0L, cdisp)))
-				(buff[idx]->owner)->Command(CMD_REDRAW, 0L, cdisp);
-		return;
-		}
-	(*pcb)--;
-	busy = true;
-	if(buff[idx]) {
-		flags = buff[idx]->flags;
-		switch(buff[idx]->cmd) {
-		case UNDO_DATA:
-			RestoreData(buff[idx]);
-			break;
-		case UNDO_ET:
-			if(buff[idx]->loc && buff[idx]->data) {
-				if(((EtBuff*)buff[idx]->data)->DaO){
-					buff[idx]->loc = (void**)((EtBuff*)buff[idx]->data)->DaO->
-						etRows[((EtBuff*)buff[idx]->data)->row][((EtBuff*)buff[idx]->data)->col];
-					}
-				if(((EtBuff*)buff[idx]->data)->DaO) (((EtBuff*)buff[idx]->data)->DaO)->SetText(((EtBuff*)buff[idx]->data)->row,
-					((EtBuff*)buff[idx]->data)->col, ((EtBuff*)buff[idx]->data)->txt);
-				else ((EditText*)buff[idx]->loc)->SetText(((EtBuff*)buff[idx]->data)->txt);
-				if(((EtBuff*)buff[idx]->data)->txt) free(((EtBuff*)buff[idx]->data)->txt);
-				*(((EtBuff*)buff[idx]->data)->cur) = ((EtBuff*)buff[idx]->data)->vcur;
-				*(((EtBuff*)buff[idx]->data)->m1) = ((EtBuff*)buff[idx]->data)->vm1;
-				*(((EtBuff*)buff[idx]->data)->m2) = ((EtBuff*)buff[idx]->data)->vm2;
-				((EditText*)buff[idx]->loc)->Command(CMD_MRK_DIRTY, cdisp, 0L);
-				}
-			break;
-		case UNDO_TEXTBUF:
-			if(buff[idx]->loc && buff[idx]->data) {
-				target = *((TextBuff*)buff[idx]->data)->pbuff;
-				target = ((TextBuff*)buff[idx]->data)->buff;
-				if(*((TextBuff*)buff[idx]->data)->pbuff) free(*((TextBuff*)buff[idx]->data)->pbuff);
-				*(((TextBuff*)buff[idx]->data)->pbuff) = ((TextBuff*)buff[idx]->data)->buff;
-				*(((TextBuff*)buff[idx]->data)->psize) = ((TextBuff*)buff[idx]->data)->size;
-				*(((TextBuff*)buff[idx]->data)->ppos) = ((TextBuff*)buff[idx]->data)->pos;
-				}
-			break;
-		case UNDO_MUTATE:
-		case UNDO_DEL_GO:
-			((GraphObj*)(buff[idx]->data))->parent = buff[idx]->owner;
-			if(buff[idx]->cmd == UNDO_MUTATE && *(buff[idx]->loc))
-				::DeleteGO((GraphObj*)*(buff[idx]->loc));
-			else CurrGO = (GraphObj*) buff[idx]->data;
-			*(buff[idx]->loc) = buff[idx]->data;
-			(buff[idx]->owner)->Command(CMD_MRK_DIRTY, 0L, 0L);
-			break;
-		case UNDO_DROPGOLIST:
-			if((ul = (UndoList *)(buff[idx]->data)) && (ul->array)){
-				gol = (GraphObj**)(*(ul->loc_arr));
-				if(gol) for (i = 0; i < *(ul->loc_count); i++) if(gol[i]) ::DeleteGO(gol[i]);
-				*(ul->loc_count) = ul->count;				if(gol) free(gol);
-				*(ul->loc_arr) = ul->array;					free(ul);
-				}
-			break;
-		case UNDO_GOLIST:
-			if((ul = (UndoList *)(buff[idx]->data)) && (ul->array)){
-				memcpy(*(ul->loc_arr), ul->array, ul->count * sizeof(GraphObj*));
-				*(ul->loc_count) = ul->count;				free(ul->array);
-				free(ul);
-				}
-			break;
-		case UNDO_DROPMEM:
-			*(buff[idx]->loc) = buff[idx]->data;				break;
-		case UNDO_SAVVAR:
-			if(!(savbuf = (unsigned char *)buff[idx]->data))break;
-			for(i = 0; ; ) {
-				memcpy(&target, savbuf+i, sizeof(unsigned char*));	i += sizeof(unsigned char*);
-				memcpy(&j, savbuf+i, sizeof(int));					i += sizeof(int);
-				if(!target) break;
-				memcpy(target, savbuf+i, j);						i += j;
-				}
-			if(buff[idx]->owner)(buff[idx]->owner)->Command(CMD_MRK_DIRTY, 0L, 0L);
-			free(savbuf);
-			break;
-		case UNDO_VALDWORD:
-			*((DWORD*)(buff[idx]->loc)) = *((DWORD*)(buff[idx]->data));
-			free(buff[idx]->data);								break;
-		case UNDO_POINT:
-			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(POINT));
-			free(buff[idx]->data);								break;
-		case UNDO_VOIDPTR:
-			*buff[idx]->loc = buff[idx]->data;					break;
-		case UNDO_VALINT:
-			*((int*)(buff[idx]->loc)) = *((int*)(buff[idx]->data));
-			free(buff[idx]->data);								break;
-		case UNDO_VALLONG:
-			*((long*)(buff[idx]->loc)) = *((long*)(buff[idx]->data));
-			free(buff[idx]->data);								break;
-		case UNDO_OBJCONF_1:			//single object restore
-			UpdGOfromMem((GraphObj *)buff[idx]->loc, (unsigned char *)buff[idx]->data);
-			free(buff[idx]->data);								break;
-		case UNDO_OBJCONF:				//tree of objects to restore
-			RestoreConf(buff[idx]);
-			if(buff[idx] && buff[idx]->data) free(buff[idx]->data);								break;
-		case UNDO_LFP:
-			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(lfPOINT));
-			free(buff[idx]->data);								break;
-		case UNDO_MOVE:
-			(buff[idx]->owner)->Command(CMD_UNDO_MOVE, buff[idx]->data, 0L);
-			free(buff[idx]->data);								break;
-		case UNDO_RECT:
-			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(fRECT));
-			free(buff[idx]->data);								break;
-		case UNDO_STRING:
-			if(*(buff[idx]->loc) && buff[idx]->data)
-				rlp_strcpy ((char*)(*buff[idx]->loc), 100, (char*)(buff[idx]->data));
-			else if(*(buff[idx]->loc))((char*)*(buff[idx]->loc))[0] = 0;
-			if(buff[idx]->data) free(buff[idx]->data);
-			CurrGO = buff[idx]->owner;							break;
-		case UNDO_ROTDEF:
-			memcpy(*(buff[idx]->loc), buff[idx]->data, 6 * sizeof(double));
-			free(buff[idx]->data);								break;
-		case UNDO_SETGO:
-			::DeleteGO(*((GraphObj**)(buff[idx]->loc)));
-			*((GraphObj**)(buff[idx]->loc)) = 0L;				break;
-		case UNDO_LINEDEF:
-			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(LineDEF));
-			free(buff[idx]->data);								break;
-		case UNDO_FILLDEF:
-			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(FillDEF) - (sizeof(LineDEF*) + sizeof(DWORD)));
-			((FillDEF*)(buff[idx]->loc))->color2 = ((FillDEF*)(buff[idx]->data))->color2;
-			free(buff[idx]->data);								break;
-		case UNDO_AXISDEF:
-			if(((AxisDEF*)(buff[idx]->loc))->breaks) free(((AxisDEF*)(buff[idx]->loc))->breaks);
-			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(AxisDEF));
-			free(buff[idx]->data);								break;
-		case UNDO_TEXTDEF:
-			if(((TextDEF*)(buff[idx]->loc))->text) free(((TextDEF*)(buff[idx]->loc))->text);
-			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(TextDEF));
-			free(buff[idx]->data);								break;
-		case UNDO_LFP3D:
-			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(fPOINT3D));
-			free(buff[idx]->data);								break;
-		case UNDO_FLOAT:
-			*((double*)(buff[idx]->loc)) = *((double*)(buff[idx]->data));
-			free(buff[idx]->data);								break;
-		case UNDO_MEM:
-			if((ul = (UndoList *)(buff[idx]->data)) && (ul->array)){
-				memcpy(*(ul->loc_arr), ul->array, ul->size);
-				*(ul->loc_count) = ul->count;
-				free(ul->array);
-				if(buff[idx]->owner->Id < GO_PLOT) CurrGO = (GraphObj*) buff[idx]->owner;
-				if(buff[idx]->owner)(buff[idx]->owner)->Command(CMD_MRK_DIRTY, 0L, 0L);
-				}
-			break;
-			}
-		if(flags & UNDO_CONTINUE){
-			free(buff[idx]);	buff[idx] = 0L;
-			Restore(redraw, cdisp);
-			}
-		else {
-			if(o) o->MrkMode = MRK_NONE;
-			if(redraw && buff[idx] && buff[idx]->owner){
-				(buff[idx]->owner)->Command(CMD_MRK_DIRTY, 0L, 0L);
-				(buff[idx]->owner)->Command(CMD_REDRAW, 0L, 0L);
-				}
-			if(buff[idx]) free(buff[idx]);	buff[idx] = 0L;
-			}
-		}
-	else {
-		InfoBox("The UNDO-cache is empty");
-		}
-	busy = false;
-}
-
-void
-UndoObj::ListGOmoved(GraphObj **oldlist, GraphObj **newlist, long size)
-{
-	long i;
-	int c;
-
-	if(!oldlist || !newlist || oldlist == newlist) return;
-	for(i = 0; i < size; i++) if(oldlist[i] == newlist[i]) {
-		for(c = 0; c < UNDO_RING_SIZE; c++) {
-			if(buff[c]) switch(buff[c]->cmd) {
-			case UNDO_DEL_GO:
-			case UNDO_SETGO:
-			case UNDO_OBJCONF_1:
-			case UNDO_OBJCONF:
-				if(buff[c]->loc == (void**) &oldlist[i]){
-					buff[c]->loc = (void**) &newlist[i];
-					}
-				break;
-				}
-			}
-		}
-}
-
-void
-UndoObj::DeleteGO(GraphObj **go, DWORD flags, anyOutput *o)
-{
-	if(!go || !(*go)) return;
-	HideCopyMark();
-	if(o){
-		SetDisp(o);					 o->HideMark();
-		}
-	if(CurrGO == *go) CurrGO = 0L;
-	if((*go)->Id == GO_POLYLINE || (*go)->Id == GO_POLYGON || (*go)->Id == GO_BEZIER){
-		if(CurrHandle && CurrHandle->parent==*go) {
-			if((*go)->Command(CMD_DELOBJ, CurrHandle, 0l)) return;
-			}
-		}
-	NewItem(UNDO_DEL_GO, flags, (*(go))->parent, *(go), (void**)go);
-	(*(go))->parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-	(*(go))->parent = 0L;
-	*(go) = CurrGO = 0L;
-}
-
-void
-UndoObj::MutateGO(GraphObj **old, GraphObj *repl, DWORD flags, anyOutput *o)
-{
-	if(!old || !(*old)) return;
-	if(o){
-		SetDisp(o);					 o->HideMark();
-		}
-	if(!(*old))return;	//HideMark might delete object: should never happen
-	if(CurrGO == *old) CurrGO = 0L;
-	NewItem(UNDO_MUTATE, flags, (*(old))->parent, *(old), (void**)old);
-	repl->parent = (*(old))->parent;
-	(*(old))->parent = 0L;
-	*(old) = repl;
-	repl->parent->Command(CMD_REDRAW, 0L, o);
-}
-
-void
-UndoObj::StoreListGO(GraphObj *parent, GraphObj ***go, long *count, DWORD flags)
-{
-	UndoList *ul;
-
-	if(ul = (UndoList *)malloc(sizeof(UndoList))) {
-		if(ul->array = memdup(*go, *count * sizeof(GraphObj*), 0)){
-			ul->loc_arr = (void **)go;
-			ul->count = *count;
-			ul->loc_count = count;
-			if(0 > NewItem(UNDO_GOLIST, flags, parent, ul, 0L)) {
-				free(ul->array);			free(ul);
-				}
-			}
-		else free(ul);
-		}
-}
-
-void
-UndoObj::DropListGO(GraphObj *parent, GraphObj ***go, long *count, DWORD flags)
-{
-	UndoList *ul;
-
-	if(ul = (UndoList *)malloc(sizeof(UndoList))) {
-		if(ul->array = *go) {
-			ul->loc_arr = (void **)go;		*go = 0L;
-			ul->count = *count;				*count = 0;
-			ul->loc_count = count;
-			if(0 > NewItem(UNDO_DROPGOLIST, flags, parent, ul, 0L)) {
-				free(ul->array);			free(ul);
-				}
-			}
-		else free(ul);
-		}
-}
-
-void
-UndoObj::DropMemory(GraphObj *parent, void **mem, DWORD flags)
-{
-	NewItem(UNDO_DROPMEM, flags, parent, *(mem), mem);
-	*mem = 0L;
-}
-
-void
-UndoObj::SavVarBlock(GraphObj *parent, void **mem, DWORD flags)
-{
-	NewItem(UNDO_SAVVAR, flags, parent, *(mem), mem);
-	*mem = 0L;
-}
-
-void
-UndoObj::ValDword(GraphObj *parent, DWORD *val, DWORD flags)
-{
-	void *ptr;
-
-	if(!(ptr = memdup(val, sizeof(DWORD), 0))) return;
-	if(0 > NewItem(UNDO_VALDWORD, flags, parent, ptr, (void**)val)) free(ptr);
-}
-
-void 
-UndoObj::Point(GraphObj *parent, POINT *pt, anyOutput * o, DWORD flags)
-{
-	void *ptr;
-
-	if(o) SetDisp(o);
-	if(!(ptr = memdup(pt, sizeof(POINT), 0))) return;
-	if(0 > NewItem(UNDO_POINT, flags, parent, ptr, (void**)pt)) free(ptr);
-}
-
-void 
-UndoObj::VoidPtr(GraphObj *parent, void **pptr, void *ptr, anyOutput *o, DWORD flags)
-{
-	if(o) SetDisp(o);
-	NewItem(UNDO_VOIDPTR, flags, parent, ptr, pptr);
-}
-
-void
-UndoObj::ValInt(GraphObj *parent, int *val, DWORD flags)
-{
-	void *ptr;
-
-	if(!(ptr = memdup(val, sizeof(int), 0))) return;
-	if(0 > NewItem(UNDO_VALINT, flags, parent, ptr, (void**)val)) free(ptr);
-}
-
-void
-UndoObj::ValLong(GraphObj *parent, long *val, DWORD flags)
-{
-	void *ptr;
-
-	if(!(ptr = memdup(val, sizeof(long), 0))) return;
-	if(0 > NewItem(UNDO_VALINT, flags, parent, ptr, (void**)val)) free(ptr);
-}
-
-void
-UndoObj::ObjConf(GraphObj *go, DWORD flags)
-{
-	long sz;
-	int idx;
-	
-	InvalidGO(go);
-	if(0<=(idx = NewItem(UNDO_OBJCONF, flags, go->parent, GraphToMem(go, &sz), (void**)go))){
-		if(cObsW == 1) buff[idx]->cmd = UNDO_OBJCONF_1;
-		(buff[idx]->owner)->Command(CMD_MRK_DIRTY, 0L, 0L);
-		}
-}
-
-int
-UndoObj::SaveLFP(GraphObj *go, lfPOINT *lfp, DWORD flags)
-{
-	int idx;
-	void *ptr;
-
-	if(!(ptr = memdup(lfp, sizeof(lfPOINT), 0))) return -1;
-	if(0 > (idx = NewItem(UNDO_LFP, flags, go, ptr, (void**)lfp))) free(ptr);
-	return idx;
-}
-
-void
-UndoObj::MoveObj(GraphObj *go, lfPOINT *lfp, DWORD flags)
-{
-	int idx;
-	lfPOINT dsp;
-
-	if(!lfp) return;
-	dsp.fx = -1.0 * lfp->fx;		dsp.fy = -1.0 * lfp->fy;
-	if((idx = SaveLFP(go, &dsp, flags)) <0) return;
-	buff[idx]->cmd = UNDO_MOVE;
-}
-
-void
-UndoObj::ValRect(GraphObj *go, fRECT *rec, DWORD flags)
-{
-	void *ptr;
-
-	if(!(ptr = memdup(rec, sizeof(fRECT), 0))) return;
-	if(0 > NewItem(UNDO_RECT, flags, go, ptr, (void**)rec)) free(ptr);
-}
-
-int
-UndoObj::String(GraphObj *go, char **s, DWORD flags)
-{
-	char *ptr;
-	int iret;
-
-	if(s && *s &&  *(*s)){
-		iret = (int)strlen(*(s));
-		ptr = (char*)memdup(*(s), iret+1, 0);
-		}
-	else {
-		ptr = 0L;				iret = 0;
-		}
-	if(0 > NewItem(UNDO_STRING, flags, go, ptr, (void**)s)) if(ptr) free(ptr);
-	return iret;
-}
-
-void
-UndoObj::RotDef(GraphObj *go, double **d, DWORD flags)
-{
-	void *ptr;
-
-	if(!(ptr = memdup(*d, 6 * sizeof(double), 0))) return;
-	if(0 > NewItem(UNDO_ROTDEF, flags, go, ptr, (void**)d)) free(ptr);
-}
-
-void
-UndoObj::SetGO(GraphObj *parent, GraphObj **where, GraphObj *go, DWORD flags)
-{
-	*where = go;
-	NewItem(UNDO_SETGO, flags, parent, 0L, (void**)where);
-}
-
-void
-UndoObj::Line(GraphObj *go, LineDEF *ld, DWORD flags)
-{
-	void *ptr;
-
-	if(!(ptr = memdup(ld, sizeof(LineDEF), 0))) return;
-	if(0 > NewItem(UNDO_LINEDEF, flags, go, ptr, (void**)ld)) free(ptr);
-}
-
-void
-UndoObj::Fill(GraphObj *go, FillDEF *fd, DWORD flags)
-{
-	void *ptr;
-
-	if(!(ptr = memdup(fd, sizeof(FillDEF), 0))) return;
-	if(0 > NewItem(UNDO_FILLDEF, flags, go, ptr, (void**)fd)) free(ptr);
-}
-
-void
-UndoObj::AxisDef(GraphObj *go, AxisDEF *ad, DWORD flags)
-{
-	AxisDEF *ptr;
-
-	if(!(ptr = (AxisDEF*) memdup(ad, sizeof(AxisDEF), 0))) return;
-	if(ptr->nBreaks && ptr->breaks) ptr->breaks = 
-		(lfPOINT*)memdup(ad->breaks, ad->nBreaks * sizeof(lfPOINT), 0);
-	if(0 > NewItem(UNDO_AXISDEF, flags, go, ptr, (void**)ad)) free(ptr);
-}
-
-void
-UndoObj::TextDef(GraphObj *go, TextDEF *td, DWORD flags)
-{
-	TextDEF *ptr;
-
-	if(!(ptr = (TextDEF*) memdup(td, sizeof(TextDEF), 0))) return;
-	if(td->text && td->text[0]) ptr->text = (char*)memdup(td->text, (int)strlen(td->text)+1, 0);
-	if(0 > NewItem(UNDO_TEXTDEF, flags, go, ptr, (void**)td)){
-		if(ptr->text) free(ptr->text);		free(ptr);
-		}
-}
-
-void
-UndoObj::ValLFP3D(GraphObj *go, fPOINT3D *lfp, DWORD flags)
-{
-	void *ptr;
-
-	if(!(ptr = memdup(lfp, sizeof(fPOINT3D), 0))) return;
-	if(0 > NewItem(UNDO_LFP3D, flags, go, ptr, (void**)lfp)) free(ptr);
-}
-
-void
-UndoObj::ValFloat(GraphObj *parent, double *val, DWORD flags)
-{
-	void *ptr;
-
-	if(!(ptr = memdup(val, sizeof(double), 0))) return;
-	if(0 > NewItem(UNDO_FLOAT, flags, parent, ptr, (void**)val)) free(ptr);
-}
-
-void
-UndoObj::DataMem(GraphObj *go, void **mem, int size, long *count, DWORD flags)
-{
-	UndoList *ul;
-
-	if(ul = (UndoList *)malloc(sizeof(UndoList))) {
-		if(ul->array = memdup(*mem, size, 0)){
-			ul->loc_arr = (void **)mem;
-			ul->size = size;
-			ul->count = *count;
-			ul->loc_count = count;
-			if(0 > NewItem(UNDO_MEM, flags, go, ul, 0L)) {
-				free(ul->array);			free(ul);
-				}
-			}
-		else free(ul);
-		}
-}
-
-void
-UndoObj::DataObject(GraphObj *go, anyOutput *o, DataObj *d, RECT *rc, DWORD flags)
-{
-	StrData *save;
-
-	if(!go || !d) return;
-	if(o) SetDisp(o);
-	if(!(save = new StrData(d, rc))) return;
-	if(0 > NewItem(UNDO_DATA, flags, go, save, (void**)d)) if(save) delete(save);
-}
-
-void 
-UndoObj::TextCell(EditText *et, anyOutput *o, char *text, int *cur, int *m1, int *m2, void* DaO, DWORD flags)
-{
-	anyOutput *o_save;
-	EtBuff *ptr;
-	POINT cpos = {-1, -1};
-
-	if(o) {
-		o_save = cdisp;		SetDisp(o);
-		if(ptr = (EtBuff*) calloc(1, sizeof(EtBuff))) {
-			if(text && text[0]) ptr->txt = (char*)memdup(text, (int)strlen(text)+1, 0);
-			ptr->cur = cur;			ptr->m1 = m1;		ptr->m2 = m2;
-			ptr->vcur = *cur;		ptr->vm1 = *m1;		ptr->vm2 = *m2;
-			ptr->row = et->row;		ptr->col = et->col;
-			ptr->DaO = (DataObj*)et->parent;
-			if(0 > NewItem(UNDO_ET, flags, 0L, ptr, (void**)et)) {
-				if(ptr->txt)free (ptr->txt);			free(ptr);
-				}
-			}
-		SetDisp(o_save);
-		}
-}
-
-void
-UndoObj::TextBuffer(GraphObj *parent, int *psize, int *ppos, unsigned char **pbuff, DWORD flags, anyOutput *o)
-{
-	TextBuff *ptr;
-
-	if(o) SetDisp(o);
-	if(!parent || !psize || !ppos || !pbuff) return;
-	if(ptr = (TextBuff*)calloc(1, sizeof(TextBuff))) {
-		ptr->size = *psize;			ptr->psize = psize;
-		ptr->pos = *ppos;			ptr->ppos = ppos;
-		ptr->pbuff = pbuff;			ptr->buff = (unsigned char*)memdup(*pbuff, ptr->size, 0);
-		if(0 > NewItem(UNDO_TEXTBUF, flags, parent, ptr, (void**)pbuff)) {
-			if(ptr->buff) free(ptr->buff);			free(ptr);
-			}
-		}
-}
-
-int
-UndoObj::NewItem(int cmd, DWORD flags, GraphObj *owner, void *data, void **loc)
-{
-	UndoInfo *tmp;
-	int idx;
-
-	if(!buff || !cdisp) return -1;
-	if(!(tmp = (UndoInfo *)malloc(sizeof(UndoInfo))))return -1;
-	tmp->cmd = cmd;			tmp->flags = flags;
-	tmp->owner = owner;		tmp->data = data;
-	tmp->loc = loc;
-	tmp->zd.org.fx = cdisp->VPorg.fx;
-	tmp->zd.org.fy = cdisp->VPorg.fy;
-	tmp->zd.scale = cdisp->VPscale;
-	idx = (*pcb & UNDO_IDX_MASK);
-	if(buff[idx]) FreeInfo(&buff[idx]);
-	buff[idx] = tmp;
-	(*pcb)++;
-	return idx;
-}
-
-void
-UndoObj::FreeInfo(UndoInfo** inf)
-{
-	int i;
-	UndoList *ul;
-	GraphObj *go, **gol;
-
-	if(!inf || !(*inf)) return;
-	switch((*inf)->cmd) {
-	case UNDO_SETGO:
-		break;
-	case UNDO_DATA:
-		delete ((StrData*)((*inf)->data));
-		break;
-	case UNDO_ET:
-		if(((EtBuff*)((*inf)->data))->txt) free(((EtBuff*)((*inf)->data))->txt);
-		free((*inf)->data);
-		break;
-	case UNDO_TEXTBUF:
-		if(((TextBuff*)((*inf)->data))->buff) free(((TextBuff*)((*inf)->data))->buff);
-		free((*inf)->data);
-		break;
-	case UNDO_MUTATE:
-	case UNDO_DEL_GO:
-		go = (GraphObj*)((*inf)->data);
-		(*inf)->data = 0L;		::DeleteGO(go);
-		break;
-	case UNDO_DROPGOLIST:
-		if((ul = (UndoList *)((*inf)->data)) && (ul->array)) {
-			gol = (GraphObj**)(ul->array);
-			for (i = 0; i < ul->count; i++) if(gol[i]) ::DeleteGO(gol[i]);
-			free(ul->array);				free(ul);
-			}
-		break;
-	case UNDO_GOLIST:	case UNDO_MEM:
-		if((ul = (UndoList *)((*inf)->data)) && (ul->array)) {
-			free(ul->array);				free(ul);
-			}
-		break;
-	case UNDO_AXISDEF:
-		if(((AxisDEF*)(*inf)->data)->breaks) free(((AxisDEF*)(*inf)->data)->breaks);
-		free((*inf)->data);
-		break;
-	case UNDO_TEXTDEF:
-		if(((TextDEF*)(*inf)->data)->text) free(((TextDEF*)(*inf)->data)->text);
-		free((*inf)->data);
-		break;
-	case UNDO_DROPMEM:		case UNDO_VALDWORD:		case UNDO_VALINT:
-	case UNDO_OBJCONF:		case UNDO_OBJCONF_1:	case UNDO_LFP:
-	case UNDO_MOVE:			case UNDO_RECT:			case UNDO_STRING:
-	case UNDO_ROTDEF:		case UNDO_LINEDEF:		case UNDO_FILLDEF:
-	case UNDO_LFP3D:		case UNDO_FLOAT:		case UNDO_SAVVAR:
-	case UNDO_POINT:		case UNDO_VALLONG:
-		free((*inf)->data);
-		break;
-		}
-	free(*inf);
-	*inf = 0L;
-}
-
-class UndoUtil:public GraphObj {
-public:
-	GraphObj *res;
-
-	UndoUtil(GraphObj *p, GraphObj *old):GraphObj(0L, 0L){root = p; optr = old; res = 0L;};
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-
-private:
-	GraphObj *root, *optr;
-};
-
-bool
-UndoUtil::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	GraphObj *xch[2];
-
-	switch(cmd){
-	case CMD_DROP_GRAPH:
-		//we come here if conversion of undo-information is
-		//   successfully converted into a tree of graphic objects.
-		//   Now ask the parent object to replace the modified
-		//   object with a previous version (i.e. undo modifications).
-		xch[0] = optr;
-		xch[1] = res = (GraphObj*)tmpl;
-		if(root) return root->Command(CMD_REPL_GO, xch, o);
-		break;
-		}
-	return false;
-}
-
-void
-UndoObj::RestoreConf(UndoInfo *inf)
-{
-	UndoUtil *proc;
-	int i;
-
-	//Create a message object which will accept the translated graphic
-	//   object or tree of objects, and which will forward it to the parent
-	//   of the tree finalizing undo.
-	if(!inf->data) return;
-	if(!(proc = new UndoUtil(inf->owner, (GraphObj*)inf->loc))) return; 
-	OpenGraph(proc, 0L, (unsigned char *)inf->data, false);
-	if(proc->res) for(i = 0; i < UNDO_RING_SIZE; i++) {
-		if(buff[i] && buff[i]->owner == (GraphObj*)inf->loc) FreeInfo(&buff[i]);
-		if(buff[i] && buff[i]->cmd == UNDO_OBJCONF){
-			if(buff[i]->loc == inf->loc) buff[i]->loc = (void**)proc->res;
-			}
-		}
-	delete proc;
-}
-
-void
-UndoObj::RestoreData(UndoInfo *inf)
-{
-	DataObj *od;
-	StrData *nd;
-	int w, h;
-
-	if(!(nd = (StrData*)inf->data) || !(od = (DataObj*)inf->loc)){
-		if(nd) delete(nd);		return;
-		}
-	nd->GetSize(&w, &h);		od->ChangeSize(w, h, false);
-	nd->RestoreData(od);		delete(nd);
-}
-
-#undef UNDO_RING_SIZE
-#undef UNDO_IDX_MASK
+//UtilObj.cpp, (c) 2000-2008 by R. Lackner
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+#include "rlplot.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include <ctype.h>
+
+#include <fcntl.h>				//file open flags
+#include <sys/stat.h>			//I/O flags
+#ifdef _WINDOWS
+	#include <io.h>					//for read/write
+#else
+	#define O_BINARY 0x0
+	#include <unistd.h>
+#endif
+
+Default defs;
+
+static LineDEF ETbgnn = {0.0, 1.0, 0x00e8e8e8L, 0L};
+static LineDEF ETbgna = {0.0, 1.0, 0x00ffffffL, 0L};
+static LineDEF ETbgmn = {0.0, 1.0, 0x00cbcbcbL, 0L};
+static LineDEF ETbgma = {0.0, 1.0, 0x00ffffc0L, 0L};
+static LineDEF yLine = {0.0, 1.0, 0x0000ffffL, 0L};
+extern const LineDEF BlackLine = {0.0, 1.0, 0x00000000L, 0L};
+extern const LineDEF GrayLine = {0.0, 1.0, 0x00c0c0c0L, 0L};
+
+static FillDEF ETfbnn = {FILL_NONE, 0x00e8e8e8L, 1.0, NULL, 0x00ffffffL};
+static FillDEF ETfbna = {FILL_NONE, 0x00ffffffL, 1.0, NULL, 0x00ffffffL};
+static FillDEF ETfbmn = {FILL_NONE, 0x00e8cbcbL, 1.0, NULL, 0x00ffffffL};
+static FillDEF ETfbma = {FILL_NONE, 0x00ffffc0L, 1.0, NULL, 0x00ffffffL};
+static FillDEF yFill = {FILL_NONE, 0x0000ffffL, 1.0, NULL, 0x0000ffffL};
+
+extern char TmpTxt[500];
+extern unsigned long cObsW;				//count objects written
+extern GraphObj *CurrGO, *TrackGO;		//Selected Graphic Objects
+extern dragHandle *CurrHandle;
+extern UndoObj Undo;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Process fields with user input text
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+EditText *CurrText = 0L, *scroll_et = 0;
+int scroll_dist = 0;
+
+EditText::EditText(void *par, char *msg, int r, int c)
+{
+	loc.x = loc.y = crb.x = rb.x = crb.y = rb.y = 0;	Value = 0.0;
+	row = r;	col = c;	disp = 0L;		text = 0L;
+	CursorPos = length = Align = 0;		type = ET_UNKNOWN;		TextCol=0x00000000L;
+	bgLine = &ETbgnn;					bgFill = &ETfbnn;		parent = par;
+	if(msg && msg[0]) {
+		SetText(msg);		FindType();
+		}
+	else {
+		Align = TXA_VCENTER | TXA_HRIGHT;
+		type = ET_UNKNOWN;
+		}
+	m1 = m2 = -1;						//cursor positions track marks
+	ftext = 0L;							//store formula text result here
+}
+
+EditText::~EditText()
+{
+	HideCopyMark();
+	if(CurrText == this)	CurrText = 0L;
+	if(text) free(text);	text = 0L;
+//	if(ftext) free(ftext);	ftext = 0L;
+}
+
+bool
+EditText::AddChar(int ci, anyOutput *Out, void *data_obj)
+{
+	unsigned char byte1, byte2, c, *tmp;
+	POINT MyPos;
+	int i;
+
+	if(ci < 254 && ci > 31) c = (char)ci;
+	else if(ci == 27) {						//Esc
+		m1 = m2 = -1;		Redraw(Out, true);		return true;
+		}
+	else return false;
+	Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
+	if(parent) {
+		((DataObj*)parent)->Command(CMD_MRK_DIRTY, 0L, 0L);
+		((DataObj*)parent)->Command(CMD_SAVEPOS, 0L, 0L);
+		}
+	bgLine = &ETbgna; bgFill = &ETfbna; TextCol = 0x00000000L;
+	if(text)length = (int)strlen(text);
+	else length = 0;
+	if(text) tmp = (unsigned char *)realloc(text, length+2);
+	else tmp = (unsigned char *)calloc(2, sizeof(unsigned char));
+	if(!tmp) return false;
+	text = (char*)tmp;
+	byte1 = byte2 = 0;
+	//replace mark by character if mark exists
+	if(hasMark()) {			//delete marked part of text
+			if(m1 > m2) Swap(m1, m2);
+			if(m2 >= (short int)strlen(text)) text[m1] = 0;
+			else rlp_strcpy(text+m1, TMP_TXT_SIZE, text+m2);
+			CursorPos = m1;
+			m1 = m2 = -1;
+			}
+	byte1 = text[CursorPos];
+	i = CursorPos;
+	text[i++] = c;
+	while(byte1) {
+		byte2 = byte1;			byte1 = text[i];			text[i++] = byte2;
+		}
+	text[i] = byte1;			CursorPos++;				type = ET_UNKNOWN;
+	Redraw(Out, true);
+	MyPos.y = ((loc.y +crb.y)>>1);
+	MyPos.x = Align & TXA_HRIGHT ? crb.x - 2 : loc.x + 2;
+	if(Out)Out->TextCursor(text, MyPos, (POINT *) NULL, &CursorPos, 
+		scroll_et == this ? scroll_dist : scroll_dist=0);
+	set_etracc();
+	return true;
+}
+
+void
+EditText::Update(int select, anyOutput *Out, POINT *MousePos)
+{
+	POINT MyPos;
+
+	if(!parent && !disp) disp = Out;
+	if(select != 1 && select != 5) m1 = m2 = -1;		//no mark;
+	switch(select) {
+		case 0:							//just redraw with current settings
+			Redraw(Out, true);
+			break;
+		case 5:							//dialog control
+			if(!text)Align = TXA_VCENTER | TXA_HLEFT;
+		case 1:							//active spread sheet cell with cursor
+			if((type & 0xff) == ET_FORMULA) Align = TXA_VCENTER | TXA_HLEFT;
+			if(!text && !(text = (char *) calloc(10, sizeof(char))))return;
+			if(CursorPos > (int)strlen(text)) CursorPos = (int)strlen(text);
+			if(MousePos && (type & 0xff) == ET_TEXT && (text && text[0] == '\'' && (bgLine == &ETbgnn || bgLine == &ETbgmn))) {
+				MousePos->x += 4;
+				}
+			bgLine = &ETbgna; bgFill = &ETfbna; TextCol = 0x00000000L;
+			if(Out) {
+				Redraw(Out, true);
+				MyPos.y = ((loc.y +crb.y)>>1);
+				MyPos.x = Align & TXA_HRIGHT ? crb.x - 4 : loc.x + 4;
+				if(MousePos && MousePos->x && MousePos->y) {
+					Out->TextCursor(text, MyPos, MousePos,&CursorPos, 
+					scroll_et == this ? scroll_dist : scroll_dist=0);
+					}
+				else if(select ==1) Out->TextCursor(text, MyPos, NULL, &CursorPos, 
+					scroll_et == this ? scroll_dist : (scroll_dist=0)+2);
+				set_etracc();
+				}
+			break;
+		case 2:							//inactive spreadsheet cell
+			if(CurrText == this) {
+				FindType();
+				}
+			if(crb.x > rb.x) {
+				crb.x = rb.x;	crb.y = rb.y;
+				}
+			bgLine = &ETbgnn; bgFill = &ETfbnn; TextCol = 0x00000000L;
+			if(Out) Redraw(Out, true);
+			break;
+		case 10:						//value filled in by external app.
+			if(text && text[0]) {
+				type = ET_VALUE;
+				Align = TXA_VCENTER | TXA_HRIGHT;
+#ifdef USE_WIN_SECURE
+				sscanf_s(text, "%lf", &Value);
+#else
+				sscanf(text, "%lf", &Value);
+#endif
+				}
+			break;
+		case 20:						//update value only
+			FindType();
+			break;
+		}
+}
+
+bool
+EditText::Redraw(anyOutput *Out, bool display)
+{
+	RECT rc;
+	POINT MyPos;
+	char *txt, tmptxt[500];
+	int i, w, h, o_crbx;
+	bool b_clip = false;
+	anyOutput *opc;
+	anyResult cres;
+	POINT grid[3];
+
+	if(!parent && disp) Out = disp;
+	if((type & ET_NODRAW_EMPTY) == ET_NODRAW_EMPTY && CurrText != this){
+		type &= ~ET_NODRAW;
+		return true;
+		}
+	if(loc.x <1 || rb.x < 1 || loc.y <1 || rb.y <1) return false;
+	o_crbx = crb.x;			crb.x = rb.x;				crb.y = rb.y;
+	if (Out) {
+		if (m1 >m2) Swap(m1, m2);
+		if(((type & 0xff) == ET_UNKNOWN) && text && text[0] && (bgLine == &ETbgnn || bgLine == &ETbgmn)) FindType();
+		Out->TxtSet.Align = Align;		Out->TxtSet.ColTxt = TextCol;
+		Out->TxtSet.ColBg = bgLine->color;
+		if(text && text[0]) {
+			Out->oGetTextExtent(text, (int)strlen(text), &w, &h);
+			if(CurrText == this && parent && col >= 0) {
+				for(i = col+1; (crb.x - loc.x) < (w+(h>>1)); i++) {
+					crb.x += ((DataObj*)parent)->ri->GetWidth(i);
+					}
+				if(o_crbx > loc.x && o_crbx > crb.x && o_crbx < 4000) crb.x = o_crbx;
+				}
+			else if((crb.x - loc.x) < (w+(h>>1))) b_clip = true;
+			}
+		Out->SetFill(bgFill);		Out->SetLine(bgLine);
+		rc.left = loc.x;			rc.right = crb.x;
+		rc.top = loc.y+1;			rc.bottom = crb.y-2;
+		Out->oRectangle(loc.x, loc.y, crb.x-1, crb.y-1);
+		if(!text || !text[0]){
+			if((type & 0xff) == ET_VALUE){
+#ifdef USE_WIN_SECURE
+				sprintf_s(tmptxt, 500, "%g", Value);
+#else
+				sprintf(tmptxt, "%g", Value);
+#endif
+				}
+			else if((type & 0xff) == ET_BOOL) {
+#ifdef USE_WIN_SECURE
+				sprintf_s(tmptxt, 500, Value != 0.0 ? "true" : "false");
+#else
+				sprintf(tmptxt, Value != 0.0 ? "true" : "false");
+#endif
+				}
+			else tmptxt[0] = 0;
+			if(tmptxt[0] && (text = (char*)realloc(text, strlen(tmptxt)+2))) rlp_strcpy(text, 500, tmptxt);
+			CursorPos = 0;
+			}
+		if(ftext) free(ftext);		ftext = 0L;
+		if(text && text[0]){
+			if(bgLine == &ETbgnn || bgLine == &ETbgmn) {
+				Out->TxtSet.Align = TXA_HLEFT | TXA_VCENTER;
+				GetResult(&cres, false);				TranslateResult(&cres);
+				Value = cres.value;
+				switch (cres.type) {
+				case ET_VALUE:
+					Out->TxtSet.Align = TXA_HRIGHT | TXA_VCENTER;
+					b_clip = false;				rlp_strcpy(tmptxt, 500, cres.text);
+					fit_num_rect(Out, rb.x - loc.x, tmptxt);
+					Int2Nat(tmptxt);			break;
+				case ET_BOOL:	case ET_DATE:	case ET_TIME:	case ET_DATETIME:
+				case ET_TEXT:
+					Out->TxtSet.Align = cres.type == ET_TEXT ? 
+						TXA_HLEFT | TXA_VCENTER : TXA_HRIGHT | TXA_VCENTER;
+					i = (int)strlen(cres.text)+2;
+					if(ftext = (char*)realloc(ftext, i > 20 ? i : 20))rlp_strcpy(ftext, i, cres.text);
+					if(cres.text && i < sizeof(tmptxt)) 
+						rlp_strcpy(tmptxt, 500, cres.text[0] != '\'' ? cres.text : cres.text +1);
+					else if(cres.text)rlp_strcpy(tmptxt, 500, "#SIZE");
+					else tmptxt[0] = 0;			
+					Out->oGetTextExtent(tmptxt, (int)strlen(tmptxt), &w, &h);
+					b_clip = (crb.x - loc.x) < (w+(h>>1)) ? true : false;
+					break;
+				case ET_ERROR:
+					rlp_strcpy(tmptxt, 500, "#ERROR");	break;
+				default: 
+					rlp_strcpy(tmptxt, 500, "#VALUE");	break;
+					}
+				txt = tmptxt;
+				}
+			else txt = text;
+			if(b_clip && parent && col >= 0 && row >=0) {
+				Out->oGetTextExtent(txt, (int)strlen(txt), &w, &h);
+				for(i = col+1; (crb.x - loc.x) < (w+(h>>1)); i++) {
+					if(((DataObj*)parent)->isEmpty(row, i)) {
+						crb.x += ((DataObj*)parent)->ri->GetWidth(i);
+						((DataObj*)parent)->etRows[row][i]->type |= ET_NODRAW;
+						}
+					else break;
+					}
+				if((crb.x - loc.x) >= (w+(h>>1))) b_clip = false;
+				else rc.right = crb.x;
+				}
+			MyPos.y = (loc.y+rb.y)>>1;
+			if(Out->TxtSet.Align & TXA_HRIGHT) {	//right justified text
+				MyPos.x = crb.x-4;
+				}
+			else {									//left justified text
+				MyPos.x = loc.x+4;
+				}
+			if(b_clip && (opc = NewBitmapClass(w+22, rb.y-loc.y, Out->hres, Out->vres))){
+				if(scroll_et != this || parent) {
+					scroll_et = this;	scroll_dist = 0;
+					}
+				opc->Erase(bgFill->color);
+				opc->SetTextSpec(&Out->TxtSet);		opc->TxtSet.Align = TXA_HLEFT | TXA_VCENTER;
+				opc->oTextOut(4,(rb.y-loc.y)>>1, txt, (int)strlen(txt));
+				if(!parent && CursorPos) {
+					Out->oGetTextExtent(txt, CursorPos, &w, &h);
+					while((scroll_dist + w)>(rc.right-rc.left-10)) scroll_dist -=10;
+					while((scroll_dist + w)<12) scroll_dist +=10;
+					if(scroll_dist >0) scroll_dist=0;
+					}
+				else scroll_dist=0;
+				Out->CopyBitmap(rc.left+1, rc.top+1, opc, 1-scroll_dist, 1, 
+					rc.right-rc.left-4, rc.bottom-rc.top-2, false);
+				DelBitmapClass(opc);
+				}
+			else {
+				if(display && hasMark() && mx1 > loc.x && mx2 < crb.x) {
+					Out->SetFill(&yFill);		Out->SetLine(&yLine);
+					Out->oRectangle(mx1, rc.top, mx2, rc.bottom);
+					Out->SetFill(bgFill);		Out->SetLine(bgLine);
+					}
+				scroll_dist = 0;
+				Out->oTextOut(MyPos.x, MyPos.y, txt, 0);
+				if(display && hasMark() && mx1 > loc.x && mx2 < crb.x) {
+					rc.left = mx1;		rc.right = mx2;
+					Out->CopyBitmap(mx1, rc.top, Out, mx1, rc.top, mx2-mx1, rc.bottom-rc.top, true);
+					Out->MrkMode = MRK_NONE;
+					}
+				}
+			}
+		Out->SetLine((LineDEF*)&GrayLine);
+		grid[0].x = loc.x;					grid[0].y = grid[1].y = crb.y-1;
+		grid[1].x = grid[2].x = crb.x-1;	grid[2].y = loc.y-1;
+		Out->oPolyline(grid, 3, 0L);
+		if(display) {
+			if(!(Out->UpdateRect(&rc, false))) return false;
+			}
+		return true;
+	}
+	return false;
+}
+
+void
+EditText::Mark(anyOutput *Out, int mark)
+{
+	LineDEF *ol = bgLine;
+	FillDEF *of = bgFill;
+	DWORD ocol = TextCol;
+
+	m1 = m2 = -1;
+	if(!parent) return;
+	switch (mark){
+	case 0:				//normal not active
+		bgLine = &ETbgnn; bgFill = &ETfbnn; TextCol = 0x00000000L;
+		break;
+	case 1:				//normal active
+		bgLine = &ETbgna; bgFill = &ETfbna; TextCol = 0x00000000L;
+		break;
+	case 2:				//mark not active
+		bgLine = &ETbgmn; bgFill = &ETfbmn; TextCol = 0x00c00000L;
+		break;
+	case 3:				//mark active
+		bgLine = &ETbgma; bgFill = &ETfbma; TextCol = 0x00ff0000L;
+		break;
+		}
+	if(!mark || mark == 2) {
+		loc.y--;	rb.y++;
+		}
+	Redraw(Out, true);
+	if(!mark || mark == 2) {
+		loc.y++;	rb.y--;
+		}
+	bgLine = ol;	bgFill = of;	TextCol = ocol;
+}
+
+bool
+EditText::Command(int cmd, anyOutput *Out, void *data_obj)
+{
+	int i, j, k, w, h;
+	POINT MyPos;
+	MouseEvent *mev;
+	static RECT rMark;
+	bool bRet;
+	char *tag1, *tag2;
+	unsigned char *pt;
+
+	MyPos.y = ((loc.y+crb.y)>>1);
+	MyPos.x = Align & TXA_HRIGHT ? crb.x - 4 : loc.x + 4;
+	if(!(text)) return false;
+	if(!parent && disp) Out = disp;		//Dialog !
+	switch(cmd) {
+		case CMD_MRK_DIRTY:
+			type = ET_UNKNOWN;
+			if(CurrText == this) {
+				Command(CMD_REDRAW, Out, data_obj);
+				if(parent)((DataObj*)parent)->Command(CMD_MRK_DIRTY, Out, 0L);
+				}
+			else if(parent) {
+				((DataObj*)parent)->Command(CMD_REDRAW, Out, 0L);
+				((DataObj*)parent)->Command(CMD_MRK_DIRTY, Out, 0L);
+				}
+			else return Command(CMD_REDRAW, Out, data_obj);
+			return true;
+		case CMD_SETFONT:
+			if (!text || !text[0]) return false;
+			if(Out) Undo.SetDisp(Out);
+			type = ET_TEXT;
+			if(hasMark()) {
+				Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
+				switch (*((int*)data_obj)) {
+				case FONT_HELVETICA:
+					tag1 = (char*)"<face=helvetica>";		tag2 = (char*)"</face>";		break;
+				case FONT_TIMES:
+					tag1 = (char*)"<face=times>";			tag2 = (char*)"</face>";		break;
+				case FONT_COURIER:
+					tag1 = (char*)"<face=courier>";			tag2 = (char*)"</face>";		break;
+				case FONT_GREEK:
+					tag1 = (char*)"<face=greek>";			tag2 = (char*)"</face>";		break;
+				default:
+					return false;
+					}
+				if(m1 < m2) {
+					j = m1;	k = m2;
+					}
+				else if(m1 > m2) {
+					j = m2; k = m1;
+					}
+				else return false;			//empty mark !
+				for(i = 0; i < j; i++) TmpTxt[i] = text[i];
+				for(j = 0, w = i; tag1[j]; j++) TmpTxt[i++] = tag1[j];
+				for( ; w < k; w++) TmpTxt[i++] = text[w];
+				for(j = 0; tag2[j]; j++) TmpTxt[i++] = tag2[j];
+				for( ; TmpTxt[i++] = text[w]; w++);
+				m1 += (w = (int)strlen(tag1));	m2 += w;	CursorPos += w;
+				CleanTags(TmpTxt, &m1, &m2, &CursorPos);
+				if(text = (char*)realloc(text, strlen(TmpTxt)+2)) rlp_strcpy(text, TMP_TXT_SIZE, TmpTxt);
+				Command(CMD_REDRAW, Out, 0L);
+				return true;
+				}
+			return false;
+		case CMD_SETSTYLE:
+			if (!text || !text[0]) return false;
+			if(Out) Undo.SetDisp(Out);
+			type = ET_TEXT;
+			if(hasMark()) {
+				Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
+				switch (*((int*)data_obj)) {
+				case TXS_BOLD:
+					tag1 = (char*)"<b>";		tag2 = (char*)"</b>";		break;
+				case ~TXS_BOLD:
+					tag1 = (char*)"</b>";		tag2 = (char*)"<b>";		break;
+				case TXS_ITALIC:
+					tag1 = (char*)"<i>";		tag2 = (char*)"</i>";		break;
+				case ~TXS_ITALIC:
+					tag1 = (char*)"</i>";		tag2 = (char*)"<i>";		break;
+				case TXS_UNDERLINE:
+					tag1 = (char*)"<u>";		tag2 = (char*)"</u>";		break;
+				case ~TXS_UNDERLINE:
+					tag1 = (char*)"</u>";		tag2 = (char*)"<u>";		break;
+				case TXS_SUPER:
+					tag1 = (char*)"<sup>";		tag2 = (char*)"</sup>";		break;
+				case ~TXS_SUPER:
+					tag1 = (char*)"</sup>";		tag2 = (char*)"<sup>";		break;
+				case TXS_SUB:
+					tag1 = (char*)"<sub>";		tag2 = (char*)"</sub>";		break;
+				case ~TXS_SUB:
+					tag1 = (char*)"</sub>";		tag2 = (char*)"<sub>";		break;
+				default:
+					return false;
+					}
+				if(m1 < m2) {
+					j = m1;	k = m2;
+					}
+				else if(m1 > m2) {
+					j = m2; k = m1;
+					}
+				else return false;			//empty mark !
+				for(i = 0; i < j; i++) TmpTxt[i] = text[i];
+				for(j = 0, w = i; tag1[j]; j++) TmpTxt[i++] = tag1[j];
+				for( ; w < k; w++) TmpTxt[i++] = text[w];
+				for(j = 0; tag2[j]; j++) TmpTxt[i++] = tag2[j];
+				for( ; TmpTxt[i++] = text[w]; w++);
+				m1 += (w = (int)strlen(tag1));	m2 += w;	CursorPos += w;
+				CleanTags(TmpTxt, &m1, &m2, &CursorPos);
+				if(text = (char*)realloc(text, strlen(TmpTxt)+2)) rlp_strcpy(text, TMP_TXT_SIZE, TmpTxt);
+				Command(CMD_REDRAW, Out, 0L);
+				return true;
+				}
+			return false;
+		case CMD_ADDTXT:
+			if((tag1 = (char*)data_obj) && *tag1 && text && 
+				(type == ET_TEXT || type == ET_UNKNOWN || type == ET_FORMULA)){
+				if(hasMark()) Command(CMD_DELETE, 0L, 0L);
+				else Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
+				if(m1 > -1 && m2 > -1) Command(CMD_DELETE, 0L, 0L);
+				for(k = 0; tag1[k] && tag1[k] == text[k]; k++);
+				if(tag1[k]!=';') k = 0;
+				for(i = 0; i < CursorPos && text[i]; i++) TmpTxt[i] = text[i];
+				j = i + rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, tag1+k);
+				if(text[i]) j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, text+i);
+				if(text = (char*)realloc(text, j+2 )) rlp_strcpy(text, j+2, TmpTxt);
+				CursorPos += (int)strlen(tag1+k);
+				Out->Focus();					Update(1, Out, 0L);
+				set_etracc();
+				}
+			return true;
+		case CMD_BACKSP:
+			if(!text) return false;
+			if(CursorPos <=0){
+				Out->TextCursor(text, MyPos, (POINT *) NULL, &CursorPos, 
+					scroll_et == this ? scroll_dist : scroll_dist=0);
+				return false;
+				}
+			Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
+			CursorPos--;						//continue as if delete
+		case CMD_DELETE:
+			if(!text) return false;
+			if(cmd == CMD_DELETE) Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
+			if(parent) {
+				((DataObj*)parent)->Command(CMD_MRK_DIRTY, 0L, 0L);
+				((DataObj*)parent)->Command(CMD_SAVEPOS, 0L, 0L);
+				}
+			bRet = false;
+			if(!text || !text[0]) {
+				type = ET_UNKNOWN;	CursorPos = 0;
+				}
+			if(hasMark()) {			//delete marked part of text
+				if (!text || !text[0]) return false;
+				if(m1 > m2) Swap(m1, m2);
+				if(m2 >= (short int)strlen(text)) text[m1] = 0;
+				else rlp_strcpy(text+m1, (int)strlen(text)+m1, text+m2);
+				CursorPos = m1;						m1 = m2 = -1;
+				if(!text[0]) {
+					type = ET_UNKNOWN;	CursorPos = 0;
+					}
+				if(Out) Redraw(Out, (bRet = true));
+				}
+			else if(text[CursorPos]) {
+				rlp_strcpy(text + CursorPos, (int)strlen(text + CursorPos), text + CursorPos + 1);
+				if(!text || !text[0]) {
+					type = ET_UNKNOWN;	CursorPos = 0;
+					}
+				if(Out)Redraw(Out, (bRet = true));
+				}
+			set_etracc();
+			if(Out)Out->TextCursor(text, MyPos, (POINT *) NULL, &CursorPos,
+				scroll_et == this ? scroll_dist : scroll_dist=0);
+			return bRet;
+		case CMD_COPY:
+			if(text && text[0]) {
+				rMark.left = loc.x+2;		rMark.right = rb.x-2;
+				rMark.top = loc.y+1;		rMark.bottom = rb.y-2;
+				if(m1 != m2 && m1 >=0 && m2 >=0) {
+					if (m1 >m2) Swap(m1, m2);
+					rMark.left = mx1;		rMark.right = mx2;
+					if(Out) Out->UpdateRect(&rMark, false);
+					CopyText(text+m1, m2-m1);
+					if(Out) {
+						Out->MrkMode = MRK_NONE;
+						ShowCopyMark(Out, &rMark, 1);
+						Out->UpdateRect(&rMark, true);
+						}
+					return false;
+					}
+				CopyText(text, (int)strlen(text));
+				if(Out)Out->UpdateRect(&rMark, true);
+				return false;
+				}
+			return false;
+		case CMD_PASTE:
+			if(pt = PasteText()) {
+				Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
+				for(i = 0; pt[i] > 0x20 && i < 81; i++) this->AddChar(pt[i], 0L, 0L);
+				if(Out) Redraw(Out, true);				free(pt);
+				set_etracc();
+				if(i) return true;
+				}
+			return false;
+		case CMD_SHIFTRIGHT:
+			if(CursorPos == m1 && text[m1]) m1++;
+			else if(CursorPos == m2 && text[m2]) m2++;
+			else if(text[CursorPos]){
+				m1 = CursorPos;	m2 = CursorPos+1;
+				}
+			set_etracc();
+			if(text[CursorPos]) CursorPos++;
+			else return false;
+		case CMD_SHIFTLEFT:
+			set_etracc();
+			if (!(CursorPos)) return false;
+		case CMD_REDRAW:
+			if(cmd == CMD_SHIFTLEFT) {
+				if(CursorPos == m1 && m1 >0) m1--;
+				else if(CursorPos == m2 && m2 >0) m2--;
+				else if(CursorPos > 0){
+					m1 = CursorPos;	m2 = CursorPos-1;
+					}
+				if(CursorPos >0) CursorPos--;
+				}
+			if(m1 >=0 && m2 >= 0 && m1 != m2 && Out) {
+				if(m1 > m2) Swap(m1, m2);
+				w = h = 0;
+				if(Align & TXA_HRIGHT) {	//right justified text
+					Out->oGetTextExtent(text, 0, &w, &h);
+					mx1 = crb.x-4 - w;
+					}
+				else {						//left justified text
+					mx1 = loc.x +4;
+					}
+				Out->oGetTextExtent(text, m1, &w, &h);
+				mx1 += (m1 ? w : 0);
+				Out->oGetTextExtent(text+m1, m2-m1, &w, &h);
+				mx2 = mx1 + w;
+				}
+			HideTextCursor();				Redraw(Out, true);
+			Out->TextCursor(text, MyPos, (POINT *) NULL, &CursorPos,
+				scroll_et == this ? scroll_dist : scroll_dist=0);
+			return true;
+		case CMD_CURRLEFT:
+			m1 = m2 = -1;					set_etracc();
+			if(CursorPos >0) {
+				CursorPos--;
+				if(Redraw(Out, true) && Out->TextCursor(text, MyPos, (POINT *) NULL,
+					&CursorPos, scroll_et == this ? scroll_dist : scroll_dist=0)) return true;
+				else return false;
+				}
+			else if (data_obj) {
+				MyPos.x = loc.x-4;			MyPos.y = (rb.y+loc.y)/2;
+				if(((DataObj*)data_obj)->Select(&MyPos))return true;
+				MyPos.x = loc.x+4;
+				((DataObj*)data_obj)->Select(&MyPos);
+				}
+			return false;
+		case CMD_CURRIGHT:
+			m1 = m2 = -1;					set_etracc();
+			if(text[CursorPos]){
+				CursorPos++;
+				if(Redraw(Out, true) && Out->TextCursor(text, MyPos, (POINT *) NULL,
+					&CursorPos, scroll_et == this ? scroll_dist : scroll_dist=0)) return true;
+				else return false;
+				}
+			else if (data_obj) {
+				MyPos.x = rb.x+4;		MyPos.y = (rb.y+loc.y)/2;	crb.x = rb.x;
+				if(((DataObj*)data_obj)->Select(&MyPos)) return true;
+				MyPos.x = rb.x-4;
+				((DataObj*)data_obj)->Select(&MyPos);
+				}
+			return false;
+		case CMD_UPDATE:
+			m1 = m2 = -1;
+			Redraw(Out, true);
+			return true;
+		case CMD_POS_FIRST:		case CMD_POS_LAST:
+			CursorPos = (cmd == CMD_POS_LAST && text && text[0]) ? (int)strlen(text) : 0;
+			m1 = m2 = -1;		Redraw(Out, true);
+			Out->TextCursor(text, MyPos, (POINT *) NULL, &CursorPos,
+				scroll_et == this ? scroll_dist : scroll_dist=0);
+			set_etracc();
+			return true;
+		case CMD_CURRDOWN:		case CMD_CURRUP:
+			if (data_obj) {
+			//the following calculation of the cursor position is crude
+            //it is based on a aspect of 2:1 for digits
+				if(Align & TXA_HRIGHT)		//right justified text
+					MyPos.x = rb.x-4-((rb.y-loc.y-4)*((long)strlen(text)-CursorPos))/2;
+				else MyPos.x = loc.x+4+((rb.y-loc.y-4)*CursorPos)/2;
+				MyPos.y = (cmd == CMD_CURRUP) ? loc.y-2 : rb.y+2;
+				if(MyPos.x < loc.x) MyPos.x = loc.x +4;
+				if(MyPos.x > rb.x) MyPos.x = rb.x -4;
+				if(((DataObj*)data_obj)->Select(&MyPos))return true;
+				MyPos.y = rb.y;
+				((DataObj*)data_obj)->Select(&MyPos);
+				}
+			return false;
+		case CMD_MOUSE_EVENT:					//track left mouse button
+			mev = (MouseEvent*) data_obj;
+			if(!text || !text[0]) return false;
+			if(mev->x <loc.x || mev->x >crb.x || mev->y <loc.y || mev->y >rb.y)return false;
+			if(mev->Action == MOUSE_LBDOWN) {
+				m1 = m2 = -1;//					set_etracc();
+				return true;
+				}
+			if(mev->Action == MOUSE_LBDOUBLECLICK) {
+				rMark.top = loc.y+1;			rMark.bottom = rb.y-2;
+				if(!Out->oGetTextExtent(text, (int)strlen(text), &w, &h)) return false;
+				m1 = 0;							m2 = (int)strlen(text);
+				if(Align & TXA_HRIGHT) {		//right justfied text
+					rMark.right = crb.x -4;		rMark.left = crb.x - w - 4;
+					}
+				else {							//left justified text
+					rMark.left = loc.x +4;		rMark.right = rMark.left +w;
+					}
+				mx1 = rMark.left;				mx2 = rMark.right;
+				Redraw(Out, true);				set_etracc();
+				return true;
+				}
+			MyPos.x = Align & TXA_HRIGHT ? mev->x + 4 : mev->x - 4;
+			MyPos.y = mev->y;
+			Out->TxtSet.Align = Align;
+			j = Out->CalcCursorPos(text, Align & TXA_HRIGHT ? crb :loc, &MyPos);
+			if(j == m1 || j == m2) return true;
+			if(Align & TXA_HRIGHT) {			//right justfied text
+				if((i = (int)strlen(text)-j)){
+					if(!Out->oGetTextExtent(text+j, i, &w, &h)) return false;
+					w = crb.x - w - 4;
+					}
+				else w = crb.x-1;
+				}
+			else {								//left justified text
+				if(!j) w = 0;
+				else if(!Out->oGetTextExtent(text, j, &w, &h))return false;
+				w += (loc.x+4);
+				}
+			if(m1 == m2 && m1 == -1) {
+				mx1 = (short)(rMark.left = w);
+				m1 = j;
+				}
+			else if(j != m2){
+				m2 = j;
+				if(m2 >= 0)Out->UpdateRect(&rMark, false);
+				mx2 = (short)(rMark.right = w);
+				rMark.top = loc.y+1;				rMark.bottom = rb.y-2;
+				if(rMark.right < crb.x && rMark.right > loc.x &&
+					rMark.left > loc.x && rMark.left < crb.x)
+					Redraw(Out, true);
+				}
+			if(hasMark() && parent)
+				//remove range-mark of data 
+				((DataObj*)parent)->Command(CMD_UNLOCK, 0L, 0L);
+			return true;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// return the value (i.e. the floating point equivalent) of text
+bool
+EditText::GetValue(double *v)
+{
+	anyResult * res;
+
+	if(((type & 0xff) == ET_UNKNOWN) && (text)) FindType();
+	if(!text || !text[0]) {
+		if((type & 0xff) == ET_VALUE) {
+			*v = Value;		return true;
+			}
+		return false;
+		}
+	if(CurrText == this && !(type & ET_BUSY)) FindType();
+	if((type & 0xff) == ET_VALUE || (type & 0xff) == ET_BOOL || (type & 0xff) == ET_DATE 
+		|| (type & 0xff) == ET_TIME || (type & 0xff) == ET_DATETIME){
+		*v = Value;			return true;
+		}
+	if((type & 0xff) == ET_FORMULA && text && text[0]){
+		if(!(type & ET_BUSY)){
+			type |= ET_BUSY;
+			if(res = do_formula((DataObj*)parent, text+1)) {
+				if(res->type == ET_VALUE || res->type == ET_DATE || res->type == ET_TIME 
+					|| res->type == ET_DATETIME || res->type == ET_BOOL){
+					*v = Value = res->value;	type &= ~ET_BUSY;	return true;
+					}
+				}
+			*v = Value = 0.0;	type &= ~ET_BUSY;	return false;
+			}
+		else type |= ET_CIRCULAR;
+		*v = Value;
+		return true;
+		}
+	return false;
+}
+
+bool
+EditText::GetText(char *tx, int size, bool bTranslate)
+{
+	char *t = 0L;
+	static char tmp_txt[40];
+	anyResult res;
+
+	if((type & 0xff) == ET_TEXT && text && text[0]) {
+		if(text[0] =='\'' && text[1]) t = text + 1;
+		else t = text;
+		}
+	else if(bTranslate) {
+		GetResult(&res, false);						TranslateResult(&res);
+		switch (res.type) {
+		case ET_VALUE:
+			rlp_strcpy(tmp_txt, 40, res.text);		t = tmp_txt;
+			Int2Nat(tmp_txt);						break;
+		case ET_BOOL:		case ET_DATE:			case ET_TIME:	
+		case ET_DATETIME:	case ET_TEXT:
+			t = res.text;							break;
+		default:
+			t = 0L;									break;
+			}
+		}
+	else if(text && text[0]) t = (text[0] =='\'' && text[1]) ? text+1 : text;
+	if(t) {
+		rlp_strcpy(tx, size, t);
+		return true;
+		}
+	else if((type & 0xff) == ET_VALUE) {
+#ifdef USE_WIN_SECURE
+		if(text = (char*)realloc(text, 20)) sprintf_s(text, 20, "%g", Value);
+#else
+		if(text = (char*)realloc(text, 20)) sprintf(text, "%g", Value);
+#endif
+		if(text && text[0]) return(GetText(tx, size, false));
+		}
+	return false;
+}
+
+bool
+EditText::GetResult(anyResult *r, bool use_last)
+{
+	anyResult * res;
+
+	if(!text || !text[0]) {
+		r->text = 0L;
+		if((type & 0xff) == ET_VALUE) {
+			r->value = Value;		r->type = ET_VALUE;
+			}
+		else {
+			r->value = 0.0;			r->type = ET_UNKNOWN;
+			}
+		return true;
+		}
+    if((type & 0xff) == ET_UNKNOWN) FindType();
+	if((type & 0xff) == ET_VALUE || (type & 0xff) == ET_BOOL || (type & 0xff) == ET_DATE 
+		|| (type & 0xff) == ET_TIME || (type & 0xff) == ET_DATETIME){
+		r->text = 0L;	r->value = Value;		r->type = (type & 0xff);
+		return true;
+		}
+	if((type & 0xff) == ET_TEXT) {
+		r->text = text;	r->value = 0.0;			r->type = ET_TEXT;
+		return true;
+		}
+	if((type & 0xff) == ET_FORMULA){
+		if(use_last) {
+			if(ftext) {
+				r->text = ftext;	r->value = 0.0;			r->type = ET_TEXT;
+				}
+			else {
+				r->text = 0L;		r->value = Value;		r->type = ET_VALUE;
+				}
+			return true;
+			}
+		if(!(type & ET_BUSY)){
+			type |= ET_BUSY;
+			if(res = do_formula((DataObj*)parent, text+1)) {
+				if(res->type == ET_VALUE) Value = res->value;
+				else if(res->type == ET_ERROR) {
+					res->text = "#ERROR";	res->type = ET_TEXT;
+					}
+				else Value = 0.0;	type &= ~ET_BUSY;
+				memcpy(r, res, sizeof(anyResult));
+				return true;
+				}
+			type &= ~ET_BUSY;
+			return false;
+			}
+		else {
+			type |= ET_CIRCULAR;
+			r->text = "#CIRC.";	r->value = 0.0;			r->type = ET_TEXT;
+			return true;
+			}
+		return false;
+		}
+	return false;
+}
+
+bool
+EditText::SetValue(double v)
+{
+	if(text) text[0] = 0;
+	Value = v;	type = ET_VALUE;
+	return true;
+}
+
+bool
+EditText::SetText(char *t)
+{
+	int cb;
+
+	Value = 0.0;	type = ET_UNKNOWN;
+	bgLine = &ETbgnn; bgFill = &ETfbnn; TextCol = 0x00000000L;
+	if(t && t[0] && (text = (char*)realloc(text, cb = (int)(strlen(t)+2)))) rlp_strcpy(text, cb, t);
+	else if (text) text[0] = 0;
+	return false;
+}
+
+void
+EditText::SetRec(RECT *rc)
+{
+	loc.x = rc->left;				loc.y = rc->top;
+	crb.x = rb.x = rc->right;		crb.y = rb.y = rc->bottom;
+}
+
+	
+bool
+EditText::isValue()
+{
+	if((type & 0xff)==ET_UNKNOWN) FindType();
+	return (type == ET_VALUE || type == ET_FORMULA || type == ET_BOOL
+		|| type == ET_DATE || type == ET_TIME || type == ET_DATETIME);
+}
+
+bool
+EditText::isFormula()
+{
+	if((type & 0xff)==ET_UNKNOWN) FindType();
+	return (type == ET_FORMULA);
+}
+
+void
+EditText::FindType()
+{
+	int i, c1, c2, c3, c4, c5;
+
+	if(!text || !text[0]) {
+		Align = TXA_VCENTER | TXA_HRIGHT;
+		if ((type & 0xff) == ET_VALUE) type = ET_VALUE;
+		else type = ET_UNKNOWN | ET_EMPTY;
+		return;
+		}
+	if(text[0] == '=') {
+		Align = TXA_VCENTER | TXA_HRIGHT;
+		type = ET_FORMULA;
+		}
+	else if(text[0] > 31 && isdigit(text[0]) || ((text[0] == '-' ) || text[0] == '.' 
+		|| text[0] == defs.DecPoint[0]) && text[1]>31 && (isdigit(text[1]))) {
+		for(i = c1 = c2 = c3 = c4 = c5 = 0; text[i]; i++) {
+			switch(text[i]) {
+			case '.':		c1++;		break;
+			case '-':		c2++;		break;
+			case '/':		c3++;		break;
+			case ':':		c4++;		break;
+			case 'e':		break;
+			case 'E':		break;
+			default:		if(isalpha(text[i])) c5++;
+				}
+			}
+		if(c5 > 0){
+			Align = TXA_VCENTER | TXA_HLEFT;
+			type = ET_TEXT;
+			}
+		else if(c1 < 2 && c2 < 2 && !c3  && !c4 && Txt2Flt(text, &Value)) {
+			Align = TXA_VCENTER | TXA_HRIGHT;
+			type = ET_VALUE;
+			}
+		else if((c1 == 2 || c2 == 2 || c3 == 2) && date_value(text, 0L, &Value)) {
+			Align = TXA_VCENTER | TXA_HRIGHT;
+			type = c4 == 2 ? ET_DATETIME : ET_DATE;
+			}
+		else if((c4 == 1 || c4 == 2) && date_value(text, "H:M:S", &Value)) {
+			Align = TXA_VCENTER | TXA_HRIGHT;
+			type = ET_TIME;
+			}
+		else {
+			Align = TXA_VCENTER | TXA_HLEFT;
+			type = ET_TEXT;
+			}
+		}
+	else {
+		if(0 == strcmp(text, "true")) {
+			Value = 1.0;
+			Align = TXA_VCENTER | TXA_HRIGHT;
+			type = ET_BOOL;
+			}
+		else if(0 == strcmp(text, "false")) {
+			Value = 0.0;
+			Align = TXA_VCENTER | TXA_HRIGHT;
+			type = ET_BOOL;
+			}
+		else if(0 == strcmp(text, "inf")) {
+			Value = HUGE_VAL;
+			Align = TXA_VCENTER | TXA_HRIGHT;
+			type = ET_VALUE;
+			}
+		else if(0 == strcmp(text, "-inf")) {
+			Value = -HUGE_VAL;
+			Align = TXA_VCENTER | TXA_HRIGHT;
+			type = ET_VALUE;
+			}
+		else {
+			Align = TXA_VCENTER | TXA_HLEFT;
+			type = ET_TEXT;
+			}
+		}
+}
+
+void
+EditText::set_etracc()
+{
+	int i;
+	bool accept_range;
+	anyResult *res;
+
+	if(parent) {
+		if(hasMark()) i = m1 < m2 ? m1 : m2;
+		else i = CursorPos;
+		accept_range = (i && text && text[0] == '=' && text[i-1]!=')'
+			&& text[i-1] > 31 && !(isdigit(text[i-1]) || isalpha(text[i-1])));
+		if(accept_range) {
+			LockData(true, true);
+			res = do_formula((DataObj*)parent, text+1);
+			if(res->type != ET_ERROR) accept_range = false;
+			if(!accept_range) {
+				if(text[i-1] == '(' || text[i-1] == ',' || text[i-1] == ';')
+					accept_range = true;
+				}
+			((DataObj*)parent)->Command(CMD_CLEAR_ERROR, 0L, 0L);
+			LockData(false, false);
+			}
+		((DataObj*)parent)->Command(CMD_ETRACC, accept_range ? this : 0L, 0L);
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// output formated text - style and font changes
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+typedef struct _tag_info {
+	char *tag;
+	int and_style, or_style;
+	int font, op;
+}tag_info;
+
+#define UC_TAG 100
+
+static tag_info tags[] = {
+	{"<i>", ~TXS_NORMAL, TXS_ITALIC, -1, 0},
+	{"</i>", ~TXS_ITALIC, TXS_NORMAL, -1, 0},
+	{"<b>", ~TXS_NORMAL, TXS_BOLD, -1, 0},
+	{"</b>", ~TXS_BOLD, TXS_NORMAL, -1, 0},
+	{"<u>", ~TXS_NORMAL, TXS_UNDERLINE, -1, 0},
+	{"</u>", ~TXS_UNDERLINE, TXS_NORMAL, -1, 0},
+	{"<sup>", ~TXS_NORMAL, TXS_SUPER, -1, 0},
+	{"</sup>", ~TXS_SUPER, TXS_NORMAL, -1, 0},
+	{"<sub>", ~TXS_NORMAL, TXS_SUB, -1, 0},
+	{"</sub>", ~TXS_SUB, TXS_NORMAL, -1, 0},
+	{"<face=helvetica>", 0, 0, FONT_HELVETICA, 0},
+	{"<face=times>", 0, 0, FONT_TIMES, 0},
+	{"<face=courier>", 0, 0, FONT_COURIER, 0},
+	{"<face=greek>", 0, 0, FONT_GREEK, 0},
+	{"</face>", 0, 0, -2, 0},
+	{"<bullet1>", 0, 0, -1, 1},	{"<bullet2>", 0, 0, -1, 2},
+	{"<bullet3>", 0, 0, -1, 3},	{"<bullet4>", 0, 0, -1, 4},
+	{"<bullet5>", 0, 0, -1, 5},	{"<bullet6>", 0, 0, -1, 6},
+	{"<bullet7>", 0, 0, -1, 7},	{"<bullet8>", 0, 0, -1, 8},
+	{"<bullet9>", 0, 0, -1, 9},	{"<bullet10>", 0, 0, -1, 10},
+	{"<white>", 0, 0, -1, 100},	{"<black>", 0, 0, -1, 101},
+	{"<red>", 0, 0, -1, 102},	{"<blue>", 0, 0, -1, 103},
+	{"<green>", 0, 0, -1, 104},	{"<yellow>", 0, 0, -1, 105},
+	{"<cyan>", 0, 0, -1, 106},	{"<purple>", 0, 0, -1, 107},
+	{"<smaller>", 0, 0, -1, 200},	{"<larger>", 0, 0, -1, 201},
+	{0L, 0, 0, 0, 0}};
+
+static int font_buff[256];
+static unsigned font_idx=0;
+fmtText::fmtText()
+{
+	src=0L;		split_text=0L;		split_text_W = 0;
+	n_split = n_split_W = uc_state = 0;
+	pos.x = pos.y = 0;				flags =0x0;
+}
+
+fmtText::fmtText(anyOutput *o, int x, int y, char *txt)
+{
+	if(txt && txt[0]) src = (char*)memdup(txt, (int)strlen(txt)+1, 0);
+	else src = 0L;		split_text = 0L;	split_text_W = 0L;
+	n_split = n_split_W = uc_state = 0;		flags = 0x0;
+	pos.x = x;	pos.y = y;		if(src)Parse();
+	if(o) DrawText(o);
+}
+
+fmtText::~fmtText()
+{
+	SetText(0L, 0L, 0L, 0L);
+}
+
+bool
+fmtText::StyleAt(int idx, TextDEF *txt_def, int *style, int *font)
+{
+	TextDEF td;
+	int i, j, n;
+
+	if(!src || !split_text || (idx > (int)strlen(src))) return false;
+	memcpy(&td, txt_def, sizeof(TextDEF));
+	for(i = j = 0; i < n_split; i++) {
+		if((n=split_text[i].tag) >= 0 && n < UC_TAG && SetTextDef(&td, n)) j += (int)strlen(tags[n].tag);
+		if(j > idx) break;
+		if(split_text[i].txt && split_text[i].txt[0]) j += (int)strlen(split_text[i].txt);
+		if(j >= idx) break;
+		}
+	if(style) *style = td.Style;		if(font) *font = td.Font;
+	return true;
+}
+
+int
+fmtText::rightTag(char *txt, int cb)
+{
+	int i, j;
+
+	for(i = 0; tags[i].tag; i++) {
+		for(j=1; tags[i].tag[j] && txt[cb+j]; j++) if(tags[i].tag[j] != txt[cb+j]) break;
+		if(!tags[i].tag[j]) return i;
+		}
+	return -1;
+}
+
+int
+fmtText::leftTag(char *txt, int cb)
+{
+	int i, j, k;
+
+	for(i = 0; tags[i].tag; i++) {
+		for(j = 0, k=(int)strlen(tags[i].tag)-1; tags[i].tag[j] && k <=cb; j++, k--) {
+			if(tags[i].tag[j] != txt[cb-k]) break;
+			}
+		if(!tags[i].tag[j]) return i;
+		}
+	return -1;
+}
+
+int 
+fmtText::ucTag(char *txt, int cb, int *tl, int *ucc)
+{
+	int i, uc=0;
+
+	if(txt[cb] != '&' || txt[cb+1] != '#') return -1;
+	for(i = cb+2; txt[i] && txt[i] != ';'; i++) {
+		if(!isdigit(txt[i])) return -1;
+		}
+	if(txt[i] != ';') return -1;
+	i -= cb;
+#ifdef USE_WIN_SECURE
+	sscanf_s(txt+cb+2, "%d", &uc);
+#else
+	sscanf(txt+cb+2, "%d", &uc);
+#endif
+	if(tl) *tl = i+1;		if(ucc) *ucc = uc;
+	return UC_TAG;
+}
+
+int
+fmtText::ucLeft(char *txt, int cb, int *tl, int *ucc)
+{
+	int i, uc = 0;
+
+	for(i = 1; i < 6 && i < cb && txt[cb-i] != '#'; i++) {
+		if(!isdigit(txt[cb-i])) return -1;
+		}
+	if(txt[cb-i] != '#' || txt[cb-i-1] != '&') return -1;
+#ifdef USE_WIN_SECURE
+	sscanf_s(txt+cb-i+1, "%d", &uc);
+#else
+	sscanf(txt+cb-i+1, "%d", &uc);
+#endif
+	if(tl) *tl = i+1;			if(ucc) *ucc = uc;
+	return UC_TAG;
+}
+
+void
+fmtText::cur_right(int *pos)
+{
+	int n, tl;
+
+	if(!src || !pos || *pos >= (int)strlen(src) || !src[*pos]) return;
+	if(src[*pos] == '<' && (n=rightTag(src, *pos)) >= 0) {
+		*pos += (int)strlen(tags[n].tag);
+		cur_right(pos);
+		}
+	else if(src[*pos] == '&' && (ucTag(src, *pos, &tl, 0L)== UC_TAG)){
+		*pos += tl;
+		}
+	else (*pos)++;
+}
+
+void 
+fmtText::cur_left(int *pos)
+{
+	int n, tl;
+
+	if(!src || !pos || !(*pos)) return;
+	(*pos)--;
+	if(*pos >= (n=(int)strlen(src))){
+		*pos = n-1;		return;
+		}
+	if(src[*pos] == ';' && (n=ucLeft(src, *pos, &tl, 0L)) == UC_TAG) {
+		*pos -= tl;		return;
+		}
+	while (src[*pos] == '>' && (n=leftTag(src, *pos)) >= 0) {
+		*pos -= (int)strlen(tags[n].tag);
+		}
+}
+
+bool
+fmtText::oGetTextExtent(anyOutput *o, int *width, int *height, int cb)
+{
+	TextDEF td1, td2;
+	int i, n, l, l1, w, w1, h, h1;
+
+	if(!o || !width || !height) return false;
+	if(!src || !src[0]) return false;
+	if(!cb) cb = (int)strlen(src);
+	if(!split_text) return o->oGetTextExtent(src, cb, width, height);
+	memcpy(&td1, &o->TxtSet, sizeof(TextDEF));	memcpy(&td2, &o->TxtSet, sizeof(TextDEF));
+	for(i = w = h = l = l1 = 0; i < n_split; i++){
+		if(split_text[i].tag == UC_TAG) {
+			o->oGetTextExtentW((w_char*)(&split_text[i].uc), 1, &w1, &h1);
+			w += w1;		h = h1 > h ? h1 : h;
+			l += split_text[i].uc_len;
+			if (l >= cb) {
+				*width = w;		*height = h;	o->SetTextSpec(&td1);
+				return true;
+				}
+			}
+		else {
+			if((n=split_text[i].tag) >= 0 && SetTextDef(&td2, n)) {
+				o->SetTextSpec(&td2);
+				l += (int)strlen(tags[n].tag);
+				}
+			if(tags[n].font == -1 && tags[n].op > 0 && tags[n].op < 100) {
+				w += o->un2ix(td2.fSize/2.0);
+				}
+			}
+		if(split_text[i].txt && split_text[i].txt[0]){
+			l1 = l;		l += (int)strlen(split_text[i].txt);
+			if (l1 >= cb) break;
+			o->oGetTextExtent(split_text[i].txt, l >= cb ? cb-l1 : 0, &w1, &h1);
+			w += w1;	h = h1 > h ? h1 : h;
+			if (l >= cb) break;
+			}
+		}
+	*width = w;			*height = h;		o->SetTextSpec(&td1);
+	return true;
+}
+
+void
+fmtText::SetText(anyOutput *o, char *txt, int *px, int *py)
+{
+	int i;
+
+	if(px) pos.x = *px;			if(py) pos.y = *py;
+	if(src && txt && !strcmp(src, txt)) {
+		if(o) DrawText(o);
+		return;
+		}
+	if(src) free(src);			src = 0L;
+	if(split_text) {
+		for(i = 0; i < n_split; i++) if(split_text[i].txt) free(split_text[i].txt);
+		free(split_text);		split_text = 0L;	n_split = 0;
+		}
+	if(split_text_W) {
+		for(i = 0; i < n_split_W; i++) {
+			if(split_text_W[i].uc_txt) free((split_text_W[i].uc_txt));
+			}
+		free(split_text_W);		split_text_W = 0L;	n_split_W = 0;
+		}
+	if(txt && txt[0]) src = (char*)memdup(txt, (int)strlen(txt)+1, 0);
+	if(src)Parse();
+	if(o) DrawText(o);
+}
+
+void
+fmtText::DrawText(anyOutput *o)
+{
+	if(!o || !src) return;
+	if(split_text)DrawFormatted(o);
+	else o->oTextOut(pos.x, pos.y, src, 0);
+}
+
+bool
+fmtText::SetTextDef(TextDEF *td, int idx)
+{
+	if(tags[idx].and_style != tags[idx].or_style) {
+		td->Style &= tags[idx].and_style;			td->Style |= tags[idx].or_style;
+		}
+	else if(tags[idx].font >= 0) {
+		font_buff[font_idx & 0xff] = td->Font;	font_idx++;
+		td->Font = tags[idx].font;
+		}
+	else if(tags[idx].font == -2) {
+		font_idx--;			td->Font=font_buff[font_idx & 0xff];
+		}
+	else return false;
+	return true;
+}
+
+bool
+fmtText::Parse()
+{
+	int i, li, j, n, tl, uc;
+	char *tmp;
+
+	if((flags & 0x01) || !src || !(tmp = (char*)memdup(src, (int)strlen(src)+1, 0))) return false;
+	for(i = li = uc_state = 0; src[i]; i++) {
+		if(i-li == 1 && split_text) {
+			if(src[li] == '<' && (n=rightTag(src, li))>=0) i--;
+			else if(src[li] == '&' && (n=ucTag(src, li, &tl, &uc))>0) i--;
+			}
+		if(src[i] == '<' && (n=rightTag(src, i))>=0) {
+			if(tags[n].font == FONT_GREEK) uc_state |= 0x02;
+			if(tags[n].op) uc_state |= 0x04;
+			if(split_text) {				//more tags in text
+				if(!(split_text = (fmt_txt_info *)realloc(split_text, (n_split+1)*sizeof(fmt_txt_info)))){
+					free(tmp);					return false;
+					}
+				for(j = li; j < i; j++) tmp[j-li] = src[j];	tmp[j-li]=0;
+				split_text[n_split-1].txt = (char*)memdup(tmp, (int)strlen((char*)tmp)+1, 0);
+				i += (int)strlen(tags[n].tag);	split_text[n_split].tag = n;
+				split_text[n_split++].txt = 0L;
+				}
+			else {							//first tag of text
+				if(!(split_text = (fmt_txt_info *)calloc(2, sizeof(fmt_txt_info)))){
+					free(tmp);					return false;
+					}
+				for(j = 0; j < i; j++) tmp[j]= src[j];	tmp[j]=0;
+				split_text[0].tag = -1;		
+				split_text[0].txt = (char*)memdup(tmp, (int)strlen((char*)tmp)+1, 0);
+				i += (int)strlen(tags[n].tag);	split_text[1].tag = n;
+				n_split = 2;
+				}
+			li = i;							i--;
+			}
+		else if(src[i] == '&' && (n=ucTag(src, i, &tl, &uc))>0) {
+			uc_state |= 0x01;
+			if(split_text) {				//more tags in text
+				if(!(split_text = (fmt_txt_info *)realloc(split_text, (n_split+1)*sizeof(fmt_txt_info)))){
+					free(tmp);					return false;
+					}
+				for(j = li; j < i; j++) tmp[j-li] = src[j];	tmp[j-li]=0;
+				split_text[n_split-1].txt = (char*)memdup(tmp, (int)strlen((char*)tmp)+1, 0);
+				i += tl;						split_text[n_split].tag = n;
+				split_text[n_split].uc = uc;	split_text[n_split].uc_len = tl;
+				split_text[n_split++].txt = 0L;
+				}
+			else {							//first tag of text
+				if(!(split_text = (fmt_txt_info *)calloc(2, sizeof(fmt_txt_info)))){
+					free(tmp);					return false;
+					}
+				for(j = 0; j < i; j++) tmp[j]= src[j];	tmp[j]=0;
+				split_text[0].tag = -1;		
+				split_text[0].txt = (char*)memdup(tmp, (int)strlen((char*)tmp)+1, 0);
+				i += tl;					split_text[1].tag = n;
+				split_text[1].uc = uc;		n_split = 2;
+				split_text[1].uc_len = tl;
+				}
+			li = i;
+			}
+		}
+	if(split_text && n_split && li < i && src[li]) 
+		split_text[n_split-1].txt = (char*)memdup(src+li, (int)strlen((char*)src+li)+1,0);
+	free(tmp);
+	return true;
+}
+
+void
+fmtText::DrawBullet(anyOutput *o, int x, int y, int type, double size, DWORD lc, DWORD fc)
+{
+	int is;
+	POINT pts[5];
+	static FillDEF fd = {0, 0x00ffffff, 1.0, 0L, 0x00ffffff};
+	static LineDEF ld = {defs.GetSize(SIZE_SYM_LINE), 1.0, 0x00000000L, 0x00000000};
+
+	switch (type) {
+	case 3:		case 4:
+		is = o->un2ix(size/4.1);
+		break;
+	case 5:		case 6:		case 7:		case 8:
+		is = o->un2ix(size/3.7);
+		break;
+	default:
+		is = o->un2ix(size/4.0);
+		break;
+		}
+	fd.color = fc;				ld.color = lc;
+	switch(type) {
+	case 1:
+		o->SetLine(&ld);				o->SetFill(&fd);
+		o->oCircle(x-is, y-is, x+is, y+is);
+		break;
+	case 2:
+		fd.color = ld.color;
+		o->SetLine(&ld);				o->SetFill(&fd);
+		o->oCircle(x-is, y-is, x+is, y+is);
+		break;
+	case 3:
+		o->SetLine(&ld);				o->SetFill(&fd);
+		o->oRectangle(x-is, y-is, x+is, y+is);
+		break;
+	case 4:
+		fd.color = ld.color;
+		o->SetLine(&ld);				o->SetFill(&fd);
+		o->oRectangle(x-is, y-is, x+is, y+is);
+		break;
+	case 5:		case 6:		case 7:		case 8:		case 9:		case 10:
+		if(type == 6 || type == 8 || type == 10) 		fd.color = ld.color;
+		pts[0].x = pts[3].x = pts[4].x = x - is;
+		pts[1].x = x;		pts[2].x = x+is;
+		if(type == 5 || type == 6) {
+			pts[0].y = pts[2].y = pts[3].y = y+o->un2iy(size*0.19439);
+			pts[1].y = y-o->un2iy(size*0.38878);
+			}
+		else if(type == 9 || type ==10) {
+			pts[0].y = pts[2].y = pts[4].y = y;
+			pts[1].y = y - is;		pts[3].y = y + is;		pts[3].x = x;
+			}
+		else {
+			pts[0].y = pts[2].y = pts[3].y = y-o->un2iy(size*0.19439);
+			pts[1].y = y+o->un2iy(size*0.38878);
+			}
+		o->SetLine(&ld);				o->SetFill(&fd);
+		o->oPolygon(pts, type < 9 ? 4 : 5);
+		break;
+		}
+}
+
+static int char2uc(char*src, w_char* dest, bool isGreek)
+{
+	int i;
+
+	if(!src || !*src) return 0;
+	for(i = 0; ; i++){
+		if(isGreek && src[i] >= 'A' && src[i] <= 'Z') dest[i] = (src[i] - 'A' + 0x391);
+		else if(isGreek && src[i] >= 'a' && src[i] <= 'z') dest[i] = (src[i] - 'a' + 0x3B1);
+		else dest[i] = (src[i]);
+		if(!dest[i]) return i;
+		}
+}
+
+bool 
+fmtText::DrawFormattedW(anyOutput *o)
+{
+	int i, j, n, cb, x, y, x1, y1, w, h;
+	double si, csi, fx, fy;
+	TextDEF td1, td2;
+	bool bGreek = false;
+	
+	if(!o || !(split_text_W = (fmt_uc_info *)calloc(n_split, sizeof(fmt_uc_info))))return false;
+	memcpy(&td1, &o->TxtSet, sizeof(TextDEF));	memcpy(&td2, &o->TxtSet, sizeof(TextDEF));
+	bGreek = (td1.Font == FONT_GREEK);
+	for(i = n_split_W = 0; i < n_split; i++){
+		if(split_text[i].tag == UC_TAG) {
+			if(i) {
+				j = n_split_W ? n_split_W-1 : 0;
+				cb = split_text[i].txt ? (int)strlen(split_text[i].txt) : 0;
+				if(split_text_W[j].uc_txt = (w_char*)realloc(split_text_W[j].uc_txt, 
+					(10 + cb + split_text_W[j].cb) * sizeof(w_char))) {
+					split_text_W[j].uc_txt[split_text_W[j].cb++] = split_text[i].uc;
+					if(cb) split_text_W[j].cb += char2uc(split_text[i].txt, split_text_W[j].uc_txt+split_text_W[j].cb, bGreek);
+					split_text_W[j].uc_txt[split_text_W[j].cb] = 0;
+					}
+				}
+			else {
+				split_text_W[0].cb = 1;				split_text_W[0].tag = -1;
+				if(split_text_W[0].uc_txt = (w_char*)malloc(2*sizeof(w_char))) {
+					split_text_W[0].uc_txt[0] = split_text[0].uc;
+					split_text_W[0].uc_txt[0] = 0;
+					}
+				}
+			}
+		else if(split_text[i].tag >= 0 && tags[split_text[i].tag].font == FONT_GREEK) {
+			if(!i) return false;	bGreek = true;
+			j = n_split_W-1;		cb = split_text[i].txt ? (int)strlen(split_text[i].txt) : 0;
+			if(split_text_W[j].uc_txt = (w_char*)realloc(split_text_W[j].uc_txt, 
+				(2 + cb + split_text_W[j].cb) * sizeof(w_char))) {
+				if(cb) split_text_W[j].cb += char2uc(split_text[i].txt, split_text_W->uc_txt+split_text_W[j].cb, bGreek);
+				}
+			}
+		else if(bGreek && split_text[i].tag >= 0 && tags[split_text[i].tag].font == -2){
+			if(!i) return false;	bGreek = false;
+			j = n_split_W-1;		cb = split_text[i].txt ? (int)strlen(split_text[i].txt) : 0;
+			if(split_text_W[j].uc_txt = (w_char*)realloc(split_text_W[j].uc_txt, 
+				(2 + cb + split_text_W[j].cb) * sizeof(w_char))) {
+				if(cb) split_text_W[j].cb += char2uc(split_text[i].txt, split_text_W->uc_txt+split_text_W[j].cb, bGreek);
+				}
+			}
+		else {
+			if((n=split_text[i].tag) >= 0) SetTextDef(&td2, n);
+			bGreek = (td2.Font == FONT_GREEK);
+			split_text_W[n_split_W].tag = split_text[i].tag;
+			split_text_W[n_split_W].cb = split_text[i].txt ? (int)strlen(split_text[i].txt) : 0;
+			if(split_text_W[n_split_W].cb && (split_text_W[n_split_W].uc_txt = 
+				(w_char*)malloc((1 + split_text_W[n_split_W].cb) * sizeof(w_char)))) {
+				char2uc(split_text[i].txt, split_text_W[n_split_W].uc_txt, bGreek);
+				}
+			else split_text_W[n_split_W].uc_txt = 0L;
+			n_split_W++;
+			}
+		}
+	memcpy(&td2, &td1, sizeof(TextDEF));
+	si = sin(td1.RotBL *0.01745329252);	csi = cos(td1.RotBL *0.01745329252);
+	fx = pos.x;		fy = pos.y;	
+	oGetTextExtent(o, &w, &h, 0);
+	if(td2.Align & TXA_HRIGHT) {
+		fx -= w*csi;		fy += w*si;
+		}
+	else if(td2.Align & TXA_HCENTER){
+		fx -= (w>>1)*csi;	fy += (w>>1)*si;
+		}
+	x = iround(fx);			y = iround(fy);
+	td2.Align &= ~(TXA_HRIGHT | TXA_HCENTER);			o->SetTextSpec(&td2);
+	for(i = 0; i < n_split_W; i++) {
+		if((n=split_text_W[i].tag) >= 0 && SetTextDef(&td2, n)) o->SetTextSpec(&td2);
+		else if(n >= 0 && tags[n].op) {
+			x1 = x + iround(o->un2fix(td2.fSize*0.25)*csi);
+			y1 = y - iround(o->un2fiy(td2.fSize*0.25)*si);
+			if((td2.Align & TXA_VTOP) == TXA_VTOP){
+				y1 += iround(o->un2fiy(td2.fSize*0.5)*csi);
+				x1 += iround(o->un2fix(td2.fSize*0.5)*si);
+				}
+			if((td2.Align & TXA_VCENTER) == TXA_VCENTER){
+				y1 -= iround(o->un2fiy(td2.fSize*0.5)*csi);
+				x1 -= iround(o->un2fix(td2.fSize*0.5)*si);
+				}
+			if((td2.Align & TXA_VBOTTOM) == TXA_VBOTTOM){
+				y1 -= iround(o->un2fiy(td2.fSize)*csi);
+				x1 -= iround(o->un2fix(td2.fSize)*si);
+				}
+			switch (tags[n].op) {
+			case 1:	case 2: case 3:	case 4: case 5:	case 6:	case 7:	case 8:	case 9:	case 10:
+				DrawBullet(o, x1, y1, tags[n].op, td2.fSize, td2.ColTxt, 0x00ffffff);
+				w = o->un2ix(td2.fSize/2.0);
+				x = iround(fx += (w*csi));		y = iround(fy -= (w*si));
+				break;
+			case 100:	td2.ColTxt = 0x00ffffffL;	break;
+			case 101:	td2.ColTxt = 0x00000000L;	break;
+			case 102:	td2.ColTxt = 0x000000ffL;	break;
+			case 103:	td2.ColTxt = 0x00ff0000L;	break;
+			case 104:	td2.ColTxt = 0x0000ff00L;	break;
+			case 105:	td2.ColTxt = 0x0000ffffL;	break;
+			case 106:	td2.ColTxt = 0x00ffff00L;	break;
+			case 107:	td2.ColTxt = 0x00ff00ffL;	break;
+			case 200:	td2.fSize *= 0.81;	td2.iSize = 0;	break;
+			case 201:	td2.fSize /= 0.81;	td2.iSize = 0;	break;
+				}
+			o->SetTextSpec(&td2);
+			}
+		if(split_text_W[i].uc_txt && split_text_W[i].uc_txt[0]){
+			o->oTextOutW(x, y, split_text_W[i].uc_txt, 0);
+			o->oGetTextExtentW(split_text_W[i].uc_txt, 0, &w, &h);
+			x = iround(fx += (w*csi));		y = iround(fy -= (w*si));
+			}
+		}
+	return true;
+}
+
+void 
+fmtText::DrawFormatted(anyOutput *o)
+{
+	int i, n, x, y, w, h;
+	TextDEF td1, td2;
+	double si, csi, fx, fy;
+
+	if(!o || !split_text) return;
+	if(uc_state && DrawFormattedW(o)) return;
+	memcpy(&td1, &o->TxtSet, sizeof(TextDEF));	memcpy(&td2, &o->TxtSet, sizeof(TextDEF));
+	si = sin(td1.RotBL *0.01745329252);	csi = cos(td1.RotBL *0.01745329252);
+	fx = pos.x;		fy = pos.y;	
+	oGetTextExtent(o, &w, &h, 0);
+	if(td2.Align & TXA_HRIGHT) {
+		fx -= w*csi;		fy += w*si;
+		}
+	else if(td2.Align & TXA_HCENTER){
+		fx -= (w>>1)*csi;	fy += (w>>1)*si;
+		}
+	x = iround(fx);			y = iround(fy);
+	td2.Align &= ~(TXA_HRIGHT | TXA_HCENTER);			o->SetTextSpec(&td2);
+	for(i = 0; i < n_split; i++) if(split_text[i].txt || split_text[i].tag == UC_TAG) {
+		if((n=split_text[i].tag) >= 0 && SetTextDef(&td2, n)) o->SetTextSpec(&td2);
+		if(split_text[i].txt && split_text[i].txt[0]){
+			o->oTextOut(x, y, split_text[i].txt, 0);
+			o->oGetTextExtent(split_text[i].txt, 0, &w, &h);
+			x = iround(fx += (w*csi));		y = iround(fy -= (w*si));
+			}
+		}
+}
+
+void
+fmtText::EditMode(bool bEdit)
+{
+	if(bEdit) flags |= 0x01;
+	else flags &= ~(0x01);
+}
+#undef UC_TAG
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// assign a value to a string
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+TextValue::TextValue()
+{
+	items = 0L;		nitems = 0;		next = inc = 1.0;
+}
+
+TextValue::TextValue(TextValItem **tvi, int ntvi)
+{
+	int i;
+
+	items = 0L;		nitems = 0;		next = inc = 1.0;
+	if(ntvi && (items = (TextValItem **)malloc(ntvi*sizeof(TextValItem*)))){
+		for(i = 0, nitems = ntvi; i < ntvi; i++) {
+			if(items[i] = (TextValItem*)memdup(tvi[i], sizeof(TextValItem), 0)){
+				if(tvi[i]->text) items[i]->text = (char*)memdup(tvi[i]->text, (int)strlen(tvi[i]->text)+1, 0);
+				}
+			}
+		nitems = ntvi;	if(items[nitems-1]) next = items[nitems-1]->val + inc;
+		}
+}
+
+TextValue::~TextValue()
+{
+	Reset();
+}
+
+double
+TextValue::GetValue(char *txt)
+{
+	unsigned int hv1, hv2;
+	int i;
+
+	if(!txt || !txt[0]) return 0.0;
+	hv1 = HashValue((unsigned char*)txt);
+	hv2 = Hash2((unsigned char*)txt);
+	if(items && nitems) for(i = 0; i < nitems; i++) {
+		if(items[i] && items[i]->hv1 == hv1 && items[i]->hv2 == hv2) return items[i]->val;
+		}
+	else if(!items &&(items = (TextValItem **)malloc(sizeof(TextValItem*)))) {
+		if(items[0] = (TextValItem*)calloc(1, sizeof(TextValItem))) {
+			items[0]->hv1 = hv1;		items[0]->hv2 = hv2;
+			items[0]->text = (char*)memdup(txt, (int)strlen(txt)+1, 0);
+			items[0]->val = next;		next += inc;
+			nitems = 1;					return (next-inc);
+			}
+		return 0.0;
+		}
+	else return 0.0;
+	if(items = (TextValItem **)realloc(items, (nitems+1)*sizeof(TextValItem*))){
+		if(items[nitems] = (TextValItem*)calloc(1, sizeof(TextValItem))) {
+			items[nitems]->hv1 = hv1;		items[nitems]->hv2 = hv2;
+			items[nitems]->text = (char*)memdup(txt, (int)strlen(txt)+1, 0);
+			items[nitems]->val = next;		next += inc;	nitems++;
+			return (next-inc);
+			}
+		}
+	return 0.0;
+}
+
+bool
+TextValue::GetItem(int idx, char **text, double *value)
+{
+	if(items && idx >=0 && idx < nitems) {
+		if(text) *text = items[idx]->text;
+		if(value) *value = items[idx]->val;
+		return true;
+		}
+	return false;
+}
+
+void
+TextValue::Reset()
+{
+	int i;
+
+	if(items) for(i = 0; i < nitems; i++){
+		if(items[i]->text) free(items[i]->text);
+		free(items[i]);
+		}
+	if(items && nitems) free(items);
+	next = inc = 1.0;	items = 0L;		nitems = 0;
+}
+
+TextValue *
+TextValue::Copy()
+{
+	return new TextValue(items, nitems);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// manage formats and style of a spreadsheet range
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+RangeInfo::RangeInfo(int sel, int cw, int rh, int fw, RangeInfo *next)
+{
+	r_type = sel;		col_width = cw;		row_height = rh;	first_width = fw;
+	ri_next = next;		ar = 0L;
+}
+
+RangeInfo::RangeInfo(int sel, char *range, RangeInfo *next)
+{
+	r_type = sel;		ri_next = next;
+	if(range && range[0]) {
+		ar = new AccRange(range);
+		}
+	else ar = 0L;
+}
+
+RangeInfo::~RangeInfo()
+{
+	if(ar) delete ar;
+}
+
+int
+RangeInfo::SetWidth(int w)
+{
+	if(r_type == 0 || r_type == 1) return col_width = w;
+	else if (ri_next) return ri_next->SetWidth(w);
+	return w;
+}
+
+int
+RangeInfo::SetDefWidth(int w)
+{
+	if(r_type == 0) return col_width = w;
+	else if (ri_next) return ri_next->SetDefWidth(w);
+	return w;
+}
+
+int
+RangeInfo::SetHeight(int h)
+{
+	if(r_type == 0) return row_height = h;
+	else if (ri_next) return ri_next->SetHeight(h);
+	return h;
+}
+
+int
+RangeInfo::SetFirstWidth(int w)
+{
+	if(r_type == 0) return first_width = w;
+	else if (ri_next) return ri_next->SetFirstWidth(w);
+	return w;
+}
+
+int
+RangeInfo::GetWidth(int col)
+{
+	int r, c;
+
+	if(r_type == 0) return col_width;
+	else if (ar && r_type == 1){
+		ar->GetFirst(&c, &r);
+		while(ar->NextCol(&c)) {
+			if(c == col) return col_width;
+			}
+		}
+	if(ri_next) return ri_next->GetWidth(col);
+	return 0;
+}
+
+int
+RangeInfo::GetHeight(int row)
+{
+	if(r_type == 0) return row_height;
+	else if (ri_next) return ri_next->GetHeight(row);
+	return row_height;
+}
+
+int
+RangeInfo::GetFirstWidth()
+{
+	if(r_type == 0) return first_width;
+	else if (ri_next) return ri_next->GetFirstWidth();
+	return first_width;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// the basic data object
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+DataObj::DataObj()
+{
+	cRows = cCols = 0;
+	etRows = 0L;
+	ri = new RangeInfo(0, 76, 19, 32, 0L);
+}
+
+DataObj::~DataObj()
+{
+	FlushData();
+	delete ri;
+}
+
+bool
+DataObj::Init(int nR, int nC)
+{
+	int i, j;
+
+	if(etRows)FlushData();
+	if(!(etRows = (EditText ***)calloc (cRows = nR, sizeof(EditText **)))) return false;
+	for(i = 0, cCols = nC; i < cRows; i++) {
+		if(!(etRows[i] = (EditText **)calloc(cCols, sizeof(EditText *)))) {
+			FlushData();	return false;
+			}
+		if(etRows[i]) for(j = 0; j < cCols; j++) {
+			etRows[i][j] = new EditText(this, 0L, i, j);
+			}
+		}
+	return true;
+}
+
+bool
+DataObj::SetValue(int row, int col, double val)
+{
+	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
+	if(etRows[row][col]) return etRows[row][col]->SetValue(val);
+	return false;
+}
+
+bool
+DataObj::SetText(int row, int col, char *txt)
+{
+	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
+	if(etRows[row][col]) return etRows[row][col]->SetText(txt);
+	return false;
+}
+
+bool
+DataObj::GetValue(int row, int col, double *v)
+{
+	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
+	if(etRows[row][col]) return etRows[row][col]->GetValue(v);
+	return false;
+}
+
+bool
+DataObj::GetText(int row, int col, char *txt, int len, bool bTranslate)
+{
+	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
+	if(txt && etRows[row][col]) return etRows[row][col]->GetText(txt, len, bTranslate);
+	return false;
+}
+
+char ** 
+DataObj::GetTextPtr(int row, int col)
+{
+	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return 0L;
+	if(etRows[row][col]) return &etRows[row][col]->text;
+	return 0L;
+}
+
+bool
+DataObj::GetResult(anyResult *r, int row, int col, bool use_last)
+{
+	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
+	if(etRows[row][col]) return etRows[row][col]->GetResult(r, use_last);
+	return false;
+}
+
+bool
+DataObj::GetSize(int *width, int *height)
+{
+	if(width)*width = cCols;		if(height)*height = cRows;
+	return true;
+}
+
+bool
+DataObj::isEmpty(int row, int col)
+{
+	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
+	if(etRows[row][col]) {
+		if((etRows[row][col]->type & 0xff) == ET_UNKNOWN)etRows[row][col]->Update(20, 0L, 0L);
+		if((etRows[row][col]->type & ET_EMPTY) == ET_EMPTY) return true;
+		}
+	return false;
+}
+
+void
+DataObj::FlushData()
+{
+	int i, j;
+
+	if(etRows){
+		for(i = 0; i < cRows; i++) if(etRows[i]) {
+			for (j = 0; j< cCols; j++) if(etRows[i][j]) delete etRows[i][j];
+			free(etRows[i]);
+			}
+		free(etRows);
+		}
+	etRows = 0L;
+}
+
+bool
+DataObj::ValueRec(RECT *rc)
+{
+	int r, c;
+	double val;
+
+	if(etRows && rc){
+		rc->left = cCols;	rc->right = 0;
+		rc->bottom = 0;		rc->top = cRows;
+		for(r = 0; r < cRows; r++) if(etRows[r]) {
+			for (c = 0; c< cCols; c++) {
+				if(etRows[r][c] && etRows[r][c]->GetValue(&val)) {
+					if(c < rc->left) rc->left =  c;
+					if(c > rc->right) rc->right =  c;
+					else c = rc->right;
+					if(r > rc->bottom) rc->bottom =  r;
+					else c = rc->right;
+					if(r < rc->top) rc->top =  r;
+					}
+				}
+			}
+		if(rc->right < rc->left) rc->right = rc->left < cCols ? rc->left : rc->left = 0;
+		if(rc->bottom < rc->top) rc->bottom = rc->top < cRows ? rc->top : rc->top = 0;
+		if(!rc->bottom && !rc->top && !rc->right && !rc->left) {
+			rc->right = cCols-1;	rc->bottom = cRows-1;
+			}
+		return true;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Store Data Object as strings: less memory required than with DataObj
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+StrData::StrData(DataObj *par, RECT *rc)
+{
+	int r1, c1, r2, c2, w, h;
+	char **tx;
+
+	pw = ph = 0;		str_data = 0L;
+	drc.left = drc.right = drc.top = drc.bottom = 0;
+	if(!(src = par)) return;
+	src->GetSize(&pw, & ph);
+	if(rc) {
+		if(0>(h = (rc->bottom - rc->top)) || 0>(w = (rc->right - rc->left))) return;
+		if(!(str_data = (char***)calloc(h+1, sizeof(char**))))return;
+		drc.left = rc->left;				drc.right = rc->right;
+		drc.top = rc->top;					drc.bottom = rc->bottom;
+		for (r1 = 0, r2 = drc.top; r1 <= h; r1++, r2++) {
+			if(!(str_data[r1] = (char**)malloc((w+1) * sizeof(char*)))) break;
+			for(c1 = 0, c2= drc.left; c1 <= w; c1++, c2++) {
+				tx = src->GetTextPtr(r2, c2);
+				if(tx && *tx && *tx[0]) {
+					str_data[r1][c1] = (char*)memdup(*tx, (int)strlen(*tx)+1, 0);
+					}
+				else str_data[r1][c1] = 0L;
+				}
+			}
+		}
+	else {
+		if(!(str_data = (char***)calloc(ph, sizeof(char**))))return;
+		for (r1 = 0; r1 < ph; r1++) {
+			if(!(str_data[r1] = (char**)malloc(pw * sizeof(char*)))) break;
+			for(c1 = 0; c1 < pw; c1++) {
+				tx = src->GetTextPtr(r1, c1);
+				if(tx && *tx && *tx[0]) {
+					str_data[r1][c1] = (char*)memdup(*tx, (int)strlen(*tx)+1, 0);
+					}
+				else str_data[r1][c1] = 0L;
+				}
+			}
+		drc.right = pw-1;		drc.bottom = ph-1;
+		}
+}
+
+StrData::~StrData()
+{
+	int r, c, w, h;
+
+	w = drc.right-drc.left;			h = drc.bottom-drc.top;
+	if(str_data) for (r = 0; r <= h; r++) {
+		if(str_data[r]) {
+			for(c = 0; c <= w; c++) if(str_data[r][c]) free(str_data[r][c]);
+			free(str_data[r]);
+			}
+		}
+	if(str_data) free(str_data);
+}
+
+bool
+StrData::GetSize(int *uw, int *uh)
+{
+	if(uw) *uw = pw;			if(uh) *uh = ph;
+	return true;
+}
+
+void
+StrData::RestoreData(DataObj *dest)
+{
+	int r1, c1, r2, c2;
+
+	if(!dest || !str_data) return;
+	for (r1 = 0, r2 = drc.top; r2 <= drc.bottom; r1++, r2++) {
+		if(str_data[r1]) {
+			for(c1 = 0, c2 = drc.left; c2 <= drc.right; c1++, c2++) 
+				dest->SetText(r2, c2, str_data[r1][c1]);
+			}
+		}
+	return;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// The notary class handles different types of supervision and indexing
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+notary::notary()
+{
+	gObs = 0L;
+	goStack = 0L;
+	NextPopGO = NextPushGO = NextRegGO = 0L;
+}
+
+notary::~notary()
+{
+	FreeStack();
+}
+
+int
+notary::RegisterGO(GraphObj *go)
+{
+	int i, j;
+
+	if(!go) return 0L;
+	if(!gObs) {
+		gObs = (GraphObj ***)calloc(0x2000L, sizeof(GraphObj **));
+		gObs[0] = (GraphObj **)calloc(0x2000L, sizeof(GraphObj *));
+		if(gObs && gObs[0]) {
+			gObs[0][0] = go;
+			return 1L;
+			}
+		return 0L;
+		}
+	i = (int)(NextRegGO >> 13);
+	j = (int)(NextRegGO & 0x1fff)+1;
+	if(j >=0x2000){
+		i++;	j = 0;
+		}
+	if(gObs[i] && gObs[i][j] && gObs[i][j] == go) {
+		NextRegGO = ((i << 13) | j);
+		return i*0x2000+j+1;
+		}
+	if(gObs && gObs[0]) {
+		for(i = 0; i < 0x2000; i++) {
+			for(j = 0; j < 0x2000L; j++) {
+				if(gObs[i][j] == go) {
+					NextRegGO = ((i << 13) | j);
+					return i*0x2000+j+1;
+					}
+				if(!gObs[i][j]) {
+					gObs[i][j] = go;
+					NextRegGO = ((i << 13) | j);
+					return i*0x2000+j+1;
+					}
+				}
+			if(i < 0x1fffL && !gObs[i+1])
+				gObs[i+1] = (GraphObj **)calloc(0x2000L, sizeof(GraphObj *));
+			if(i < 0x1fff && !gObs[i+1]) return 0;
+			}
+		}
+	return 0;
+}
+
+void
+notary::AddRegGO(GraphObj *go)
+{
+	int i, j;
+
+	if(!go) return;
+	if(!gObs) {
+		gObs = (GraphObj ***)calloc(0x2000L, sizeof(GraphObj **));
+		gObs[0] = (GraphObj **)calloc(0x2000L, sizeof(GraphObj *));
+		if(gObs && gObs[0]) {
+			gObs[0][0] = go;
+			return;
+			}
+		return;
+		}
+	i = (int)(NextRegGO >> 13);
+	j = (int)(NextRegGO & 0x1fff)+1;
+	if(j >=0x2000){
+		i++;
+		j = 0;
+		}
+	if(!gObs[i]) gObs[i] = (GraphObj **)calloc(0x2000L, sizeof(GraphObj *));
+	if(gObs[i] && !gObs[i][j]) {
+		gObs[i][j] = go;
+		NextRegGO = ((i << 13) | j);
+		}
+	else RegisterGO(go);
+}
+
+bool
+notary::PushGO(unsigned int id, GraphObj *go)
+{
+	int i, j;
+
+	NextPopGO = 0L;
+	if(!go) return true;
+	go->Id = id;
+	if(!goStack) {
+		if(!(goStack = (GraphObj ***)calloc(8192, sizeof(GraphObj **))))return false;
+		goStack[0] = (GraphObj **)calloc(8192, sizeof(GraphObj *));
+		if(goStack && goStack[0]) {
+			goStack[0][0] = go;
+			return true;
+			}
+		return false;
+		}
+	i = (int)(NextPushGO >> 13);
+	j = (int)(NextPushGO & 0x1fff)+1;
+	if(j >=0x2000){
+		i++;
+		j = 0;
+		}
+	if(!goStack || !goStack[0]) return false;
+	if(goStack[i] && !goStack[i][j]) {
+		goStack[i][j] = go;
+		NextPushGO = ((i << 13) | j);
+		return true;
+		}
+	for(i = 0; i < 0x2000; i++) {
+		for(j = 0; j < 0x2000; j++) {
+			if(!goStack[i][j]) {
+				goStack[i][j] = go;
+				NextPushGO = ((i << 13) | j);
+				return true;
+				}
+			}
+		if(i < 0x1fff && !goStack[i+1] && !(goStack[i+1] =
+			(GraphObj **)calloc(0x2000, sizeof(GraphObj *)))) return false;
+		}
+	return false;
+}
+
+GraphObj *
+notary::PopGO(unsigned int id)
+{
+	int i, j;
+	GraphObj *go;
+
+	NextPushGO = 0L;
+	if(!id || !goStack || !goStack[0]) return 0L;
+	i = (int)(NextPopGO >> 13);
+	j = (int)(NextPopGO & 0x1fff)+1;
+	if(j >=0x2000){
+		i++;
+		j = 0;
+		}
+	if(goStack[i] && goStack[i][j] && goStack[i][j]->Id == id) {
+		go = goStack[i][j];
+		goStack[i][j] = 0L;
+		go->Id = 0L;
+		NextPopGO = ((i << 13) | j);
+		return go;
+		}
+	for(i = 0; i < 0x2000; i++) {
+		for(j = 0; j < 0x2000; j++) {
+			if(goStack[i][j] && goStack[i][j]->Id == id) {
+				go = goStack[i][j];
+				goStack[i][j] = 0L;
+				go->Id = 0L;
+				NextPopGO = ((i << 13) | j);
+				return go;
+				}
+			}
+		if(i < 0x1fff && !goStack[i+1]) return 0L;
+		}
+	return 0L;
+}
+
+void
+notary::FreeStack()
+{
+	int i, j, k;
+
+	if(gObs) {
+		for(i = 0; gObs[i] && i <8192; i++) free(gObs[i]);
+		free(gObs);
+		gObs = 0L;
+		}
+	if(goStack) {
+		for(i = k = 0; goStack[i] && i <8192; i++){
+			for(j = 0; j < 8192; j++){
+				if(goStack[i][j]) {
+					goStack[i][j]->Id = 0L;
+					DeleteGO(goStack[i][j]);
+					k++;
+					}
+				}
+			free(goStack[i]);
+			}
+		free(goStack);
+		if(k){
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "%d objects deleted\nby notary", k);
+#else
+			sprintf(TmpTxt,"%d objects deleted\nby notary", k);
+#endif
+			ErrorBox(TmpTxt);
+			}
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Calculate continuous index to a range given by an ASCII string
+// string examples include  "a1:a12"  or  "a1:a4;a12:a24"
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+AccRange::AccRange(char *asc)
+{
+	int i, j, l;
+
+	if(asc && *asc && (l=(int)strlen(asc)) >1){
+		txt = (char *)malloc(l+2);
+		for(i = j = 0; i< (int)strlen(asc); i++)
+			if(asc[i] > 32) txt[j++] = asc[i];
+		txt[j] = 0;
+		}
+	else txt = 0L;
+	x1 = y1 = x2 = y2 = 0;
+}
+
+AccRange::~AccRange()
+{
+	if(txt) free(txt);
+}
+
+int
+AccRange::CountItems()
+{
+	int RetVal, l;
+
+	RetVal = 0;
+	if(txt && txt[0] && Reset()){
+		l = (int)strlen(txt);
+		do {
+			RetVal += ((x2-x1+1)*(y2-y1+1));
+			} while((curridx < l) && Parse(curridx));
+		}
+	return RetVal;
+}
+
+bool
+AccRange::GetFirst(int *x, int *y)
+{
+	if(txt && Reset()) {
+		if(x && y) {*x = x1; *y = y1;}
+		return true;
+		}
+	return false;
+}
+
+bool
+AccRange::GetNext(int *x, int *y)
+{
+	if(txt && x && y) {
+		if(cx <= x2) {*x = cx; *y = cy; cx++; return true;}
+		else {
+			cx = x1; cy++;
+			if(cy <= y2) return GetNext(x, y);
+			else if(txt[curridx]){
+				if(Parse(curridx)) return GetNext(x, y);
+				return false;
+				}
+			}
+		}
+	return false;
+}
+
+bool
+AccRange::NextRow(int *y)
+{
+	if(cy <= y2) {
+		*y = cy;	cy++;	cx = x1;	return true;
+		}
+	else if(txt[curridx] && Parse(curridx)) return NextRow(y);
+	return false;
+}
+
+bool
+AccRange::NextCol(int *x)
+{
+	if(cx <= x2) {
+		*x = cx;	cx++;	return true;
+		}
+	else if(txt[curridx] && Parse(curridx)) return NextCol(x);
+	return false;
+}
+
+bool
+AccRange::IsInRange(int x, int y)
+{
+	if(txt && Reset())	do {
+		if(x >= x1 && x <= x2 && y >= y1 && y <= y2) return true;
+		} while((curridx < (int)strlen(txt)) && Parse(curridx));
+	return false;
+}
+
+bool
+AccRange::BoundRec(RECT *rec)
+{
+	if(txt && Reset()){
+		SetMinMaxRect(rec, x1, y1, x2, y2);
+		while((curridx < (int)strlen(txt)) && Parse(curridx)) {
+			UpdateMinMaxRect(rec, x1, y1);	UpdateMinMaxRect(rec, x2, y2);
+			}
+		Reset();
+		return true;
+		}
+	return false;
+}
+
+bool
+AccRange::Reset()
+{
+	curridx = 0;
+	return Parse(curridx);
+}
+
+bool
+AccRange::Parse(int start)
+{
+	int i, l, step, *v;
+
+	i = start;
+	if(!txt) return false;
+	while(txt[i] == ';' || txt[i] == ',') i++;
+	if(!txt[i]) return false;
+	step = x1 = y1 = x2 = y2 = 0;
+	v = &x1;
+	for (l=(int)strlen(txt)+1 ; i < l; i++) {
+		if(txt[i] == '$') i++;
+		switch(step) {
+		case 0:
+		case 2:
+			if((txt[i] >= 'a') && (txt[i] <= 'z')){
+				*v *= 26;
+				*v += (txt[i]-'a'+1);
+				}
+			else if((txt[i] >= 'A') && (txt[i] <= 'Z')){
+				*v *= 26;
+				*v += (txt[i]-'A'+1);
+				}
+			else if((txt[i] >= '0') && (txt[i] <= '9')){
+				v = step == 0 ? &y1 : &y2;
+				*v = txt[i]-'0';
+				step++;
+				}
+			else return false;
+			break;
+		case 1:
+		case 3:
+			if((txt[i] >= '0') && (txt[i] <= '9')){
+				*v *= 10;
+				*v += (txt[i]-'0');
+				}
+			else if((txt[i] >= 'a') && (txt[i] <= 'z') ||
+				(txt[i] >= 'A') && (txt[i] <= 'Z')){
+				if(step == 1) v =  &x2;
+				else return false;
+				*v = txt[i] >='a' && txt[i] <= 'z' ? 
+					txt[i]-'a' : txt[i]-'A';
+				step++;
+				}
+			else if(step == 1 && (txt[i] == ':')) {
+				v = &x2;
+				step++;
+				}
+			else if((txt[i] == ';') || (txt[i] == ',') || (txt[i] == 0)) {
+				if(step == 1) {		//one single cell selected
+					x2 = x1;	y2 = y1;
+					}
+				if(x2<x1) Swap(x1,x2);		if(y2<y1) Swap(y1,y2);
+				if(y1 >=0) y1--;			if(y2 >=0) y2--;
+				if(x1 >=0) x1--;			if(x2 >=0) x2--;
+				curridx = i;
+				cx = x1; cy = y1;
+				return true;
+				}
+			break;
+			}
+		}
+	return false;
+}
+
+//get a description for the current range from the data object
+char *
+AccRange::RangeDesc(void *d, int style)
+{
+	anyResult res, res1;
+	int cb;
+	char *desc;
+
+	if(!d || !txt || !Reset())return 0L;
+	if(!((DataObj*)d)->GetResult(&res, y1, x1, false))return 0L;
+	if(style == 3) {
+		if(x1 == x2 && y1 > 0 && res.type == ET_TEXT) {
+			if(((DataObj*)d)->GetResult(&res1, 0, x1, false) && res1.type == ET_TEXT)
+				return (char*)memdup(res1.text, (int)strlen(res1.text)+1, 0);
+			}
+		if(y1 == y2 && x1 > 0 && res.type == ET_TEXT) {
+			if(((DataObj*)d)->GetResult(&res1, y1, 0, false) && res1.type == ET_TEXT)
+				return (char*)memdup(res1.text, (int)strlen(res1.text)+1, 0);
+			}
+		}
+	switch(res.type) {
+	case ET_TEXT:
+		if(res.text && res.text[0])
+			return (char*)memdup(res.text, (int)strlen(res.text)+1, 0);
+		else return 0L;
+	case ET_VALUE:
+		if(style != 4) break;
+	case ET_DATE:	case ET_DATETIME:	case ET_TIME:
+		TranslateResult(&res);
+		if(res.text && res.text[0])
+			return (char*)memdup(res.text, (int)strlen(res.text)+1, 0);
+		else return 0L;
+		}
+	if(!(desc = (char*)malloc(40*sizeof(char))))return 0L;
+	if(x1 == x2) {
+		if(style == 1 || style == 3) cb = rlp_strcpy(desc, 40, "Col. ");
+		else if(style == 2) cb = rlp_strcpy(desc, 40, "Column ");
+		else goto rdesc_err;
+		rlp_strcpy(desc+cb, 40-cb, Int2ColLabel(x1, true));
+		return desc;
+		}
+	else if(y1 == y2) {
+#ifdef USE_WIN_SECURE
+		sprintf_s(desc, 40, "Row %d", y1+1);
+#else
+		sprintf(desc, "Row %d", y1+1);
+#endif
+		return desc;
+		}
+rdesc_err:
+	free(desc);
+	return 0L;
+}
+
+int 
+AccRange::DataTypes(void *d, int *numerical, int *strings, int *datetime)
+{
+	anyResult res;
+	int n, r, c, c_num, c_txt, c_dattim;
+
+	if(!d || !Reset()) return 0;
+	for(n = c_num = c_txt = c_dattim = 0, GetFirst(&c, &r); GetNext(&c, &r); n++) {
+		if(((DataObj*)d)->GetResult(&res, r, c, false)) {
+			switch(res.type) {
+			case ET_VALUE:		c_num++;		break;
+			case ET_TEXT:		c_txt++;		break;
+			case ET_DATE:		case ET_TIME:	case ET_DATETIME:		
+				c_dattim++;		break;
+				}
+			}
+		}
+	if(numerical) *numerical = c_num;
+	if(strings) *strings = c_txt;
+	if(datetime) *datetime = c_dattim;
+	return n;
+}
+
+//---------------------------------------------------------------------------
+// Use the Delauney triangulation to create a 3D mesh of dispersed data
+//---------------------------------------------------------------------------
+Triangle::Triangle()
+{
+	bSorted = false;
+}
+
+void
+Triangle::SetRect()
+{
+	int i, i2;
+	double dy1, dy2, dx, dy;
+	double m1, m2, mx1, mx2, my1, my2;
+
+	//setup bounding rectangle
+	rc.Xmin = rc.Xmax = pt[0].fx;	rc.Ymin = rc.Ymax = pt[0].fy;
+	for(i = 1; i < 3; i++) {
+		if(pt[i].fx < rc.Xmin) rc.Xmin = pt[i].fx;
+		if(pt[i].fx > rc.Xmax) rc.Xmax = pt[i].fx;
+		if(pt[i].fy < rc.Ymin) rc.Ymin = pt[i].fy;
+		if(pt[i].fy > rc.Ymax) rc.Ymax = pt[i].fy;
+		}
+	//get three line equations in 2D
+	for(i = 0; i < 3; i++) {
+		i2 = (i+1)%3;
+		ld[i].fx = pt[i].fy;
+		if(pt[i].fx != pt[i2].fx) {
+			ld[i].fy = (pt[i2].fy - pt[i].fy) / (pt[i2].fx - pt[i].fx);
+			}
+		else ld[i].fy = HUGE_VAL;
+		}
+	//close polygon
+	pt[3].fx = pt[0].fx;	pt[3].fy = pt[0].fy;	pt[3].fz = pt[0].fz;
+	//circumcricle
+	dy1 = fabs(pt[0].fy - pt[1].fy);			dy2 = fabs(pt[1].fy - pt[2].fy);
+	m1 = (pt[0].fx - pt[1].fx)/(pt[1].fy - pt[0].fy);
+	m2 = (pt[1].fx - pt[2].fx)/(pt[2].fy - pt[1].fy);
+	mx1 = (pt[0].fx + pt[1].fx)/2.0;			my1 = (pt[0].fy + pt[1].fy)/2.0;
+	mx2 = (pt[1].fx + pt[2].fx)/2.0;			my2 = (pt[1].fy + pt[2].fy)/2.0;
+	if(dy1 < 1.0e-16 && dy2 < 1.0e-16) {
+		cy = (pt[0].fy + pt[1].fy + pt[2].fy)/3.0;
+		cx = (pt[0].fx + pt[1].fx + pt[2].fx)/3.0;
+		r2 = 0.0;			return;
+		}
+	else if(dy1 < 1.0e-16) {
+		cx = (pt[0].fx + pt[1].fx)/2.0;			cy = m2 * (cx - mx2) + my2;
+		}
+	else if(dy2 < 1.0e-16) {
+		cx = (pt[2].fx + pt[1].fx)/2.0;			cy = m1 * (cx - mx1) + my1;
+		}
+	else {
+		cx = (m1*mx1-m2*mx2+my2-my1)/(m1-m2);	cy = m1*(cx - mx1) + my1;
+		}
+	dx = pt[1].fx - cx;	dy = pt[1].fy - cy;		r2 = dx * dx + dy * dy;
+}
+
+bool
+Triangle::TestVertex(double x, double y)
+{
+	double dx, dy;
+
+	dx = x-cx;		dx = dx * dx;		dy = y-cy;		dy = dy * dy;
+	return (dx+dy)<r2;
+}
+
+bool
+Triangle::Sort()
+{
+	if(pt[0].fz <= pt[1].fz) {
+		if(pt[1].fz <= pt[2].fz) {
+			order[0] = 0;	order[1] = 1;	order[2] = 2;
+			}
+		else if(pt[0].fz <= pt[2].fz) {
+			order[0] = 0;	order[1] = 2;	order[2] = 1;
+			}
+		else {
+			order[0] = 1;	order[1] = 2;	order[2] = 0;
+			}
+		}
+	else {
+		if(pt[1].fz >= pt[2].fz) {
+			order[0] = 2;	order[1] = 1;	order[2] = 0;
+			}
+		else if(pt[0].fz >= pt[2].fz) {
+			order[0] = 2;	order[1] = 0;	order[2] = 1;
+			}
+		else {
+			order[0] = 1;	order[1] = 0;	order[2] = 2;
+			}
+		}
+	return (bSorted = true);
+}
+	
+void
+Triangle::LinePoint(int i1, int i2, double z, lfPOINT *res)
+{
+	double dx, dy, dz, l, f;
+
+	dx = pt[i2].fx - pt[i1].fx;		dy = pt[i2].fy - pt[i1].fy;
+	dz = f = pt[i2].fz - pt[i1].fz;
+	l = sqrt(dx * dx + dy * dy + dz * dz);
+	dx /= l;	dy /= l;	dz /= l;
+	f = (z-pt[i1].fz) / dz;
+	res->fx = pt[i1].fx + dx * f;
+	res->fy = pt[i1].fy + dy * f;
+}
+
+bool
+Triangle::IsoLine(double z, void *dest)
+{
+	lfPOINT li[2];
+
+	if(!bSorted && !Sort())return false;
+	if(z < pt[order[0]].fz) return false;		//below lowest point
+	if(z == pt[order[0]].fz) {
+		if(pt[order[1]].fz > z) return true;	//crossing single lowest point
+		li[0].fx = pt[order[0]].fx;		li[0].fy = pt[order[0]].fy;
+		li[1].fx = pt[order[1]].fx;		li[1].fy = pt[order[1]].fy;
+		return ((GraphObj*)dest)->Command(CMD_ADDTOLINE, &li, 0L);
+		}
+	if(z < pt[order[1]].fz) {
+		LinePoint(order[0], order[1], z, &li[0]);
+		LinePoint(order[0], order[2], z, &li[1]);
+		return ((GraphObj*)dest)->Command(CMD_ADDTOLINE, &li, 0L);
+		}
+	if(z == pt[order[1]].fz && pt[order[1]].fz < pt[order[2]].fz) {
+		li[0].fx = pt[order[1]].fx;		li[0].fy = pt[order[1]].fy;
+		LinePoint(order[0], order[2], z, &li[1]);
+		return ((GraphObj*)dest)->Command(CMD_ADDTOLINE, &li, 0L);
+		}
+	if(z < pt[order[2]].fz) {
+		LinePoint(order[1], order[2], z, &li[0]);
+		LinePoint(order[0], order[2], z, &li[1]);
+		return ((GraphObj*)dest)->Command(CMD_ADDTOLINE, &li, 0L);
+		}
+	if(z == pt[order[1]].fz && z == pt[order[2]].fz) {
+		li[0].fx = pt[order[1]].fx;		li[0].fy = pt[order[1]].fy;
+		li[1].fx = pt[order[2]].fx;		li[1].fy = pt[order[2]].fy;
+		return ((GraphObj*)dest)->Command(CMD_ADDTOLINE, &li, 0L);
+		}
+	return false;
+}
+
+Triangulate::Triangulate(Triangle *t_list)
+{
+	trl = t_list;		edges = 0L;
+}
+
+bool
+Triangulate::AddEdge(fPOINT3D *p1, fPOINT3D *p2)
+{
+	edge *ce, *ne;
+
+	//if edge exists delete both the new and the existing edge
+	for(ce = edges, ne = 0L; (ce); ) {
+		if((ce->p1.fx == p1->fx && ce->p1.fy == p1->fy && ce->p1.fz == p1->fz
+			&& ce->p2.fx == p2->fx && ce->p2.fy == p2->fy && ce->p2.fz == p2->fz)
+			|| (ce->p2.fx == p1->fx && ce->p2.fy == p1->fy && ce->p2.fz == p1->fz
+			&& ce->p1.fx == p2->fx && ce->p1.fy == p2->fy && ce->p1.fz == p2->fz)) {
+			if(ne) ne->next = ce->next;
+			else edges = ce->next;
+			delete ce;					return true;
+			}
+		ne = ce;	ce = ce->next;
+		}
+	//come here for new edge
+	if(ne = new edge()) {
+		ne->p1.fx = p1->fx;		ne->p1.fy = p1->fy;		ne->p1.fz = p1->fz;
+		ne->p2.fx = p2->fx;		ne->p2.fy = p2->fy;		ne->p2.fz = p2->fz;
+		ne->next = edges;		edges = ne;
+		}
+	return false;
+}
+
+bool
+Triangulate::AddVertex(fPOINT3D *v)
+{
+	Triangle *trc, *trn, *tr1;
+	edge *ce, *ae;
+
+	for(trc = trl, trn = 0L, edges = 0L; (trc);) {
+		tr1 = trc->next;
+		//delete triangles whose circumcircle enclose the new vertex
+		if(trc->TestVertex(v->fx, v->fy)) {
+			AddEdge(&trc->pt[0], &trc->pt[1]);		AddEdge(&trc->pt[1], &trc->pt[2]);
+			AddEdge(&trc->pt[0], &trc->pt[2]);
+			if(trn) trn->next = trc->next;
+			else trl = trc->next;
+			if(trl == trc) trl = 0L;	
+			delete trc;
+			}
+		else trn = trc;
+		trc = tr1;
+		}
+	//create new triangles from those edges which where found only once
+	for(ce = edges; (ce); ) {
+		if(trn = new Triangle()) {
+			trn->pt[0].fx = ce->p1.fx;	trn->pt[0].fy = ce->p1.fy;	trn->pt[0].fz = ce->p1.fz;
+			trn->pt[1].fx = ce->p2.fx;	trn->pt[1].fy = ce->p2.fy;	trn->pt[1].fz = ce->p2.fz;
+			trn->pt[2].fx = v->fx;		trn->pt[2].fy = v->fy;		trn->pt[2].fz = v->fz;
+			trn->SetRect();				trn->next = trl;			trl = trn;
+			ae = ce->next;				delete(ce);					ce = ae;
+			}
+		}
+	return true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Default data vault
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Default::Default()
+{
+	dUnits = cUnits = 0;
+	rlp_strcpy(DecPoint, 2,  ".");		rlp_strcpy(ColSep, 2, ",");
+	Line_0.width = .4;		Line_1.width = .04,		Line_2.width = 0.016;
+	Line_0.patlength = 6.0;	Line_1.patlength = 0.6;	Line_2.patlength = 0.24;
+	Line_0.color = Line_1.color = Line_2.color = 0x00000000L;	//black
+	Line_0.pattern = Line_1.pattern = Line_2.pattern = 0L;		//solid line
+	FillLine_0.width = FillLine_1.width = FillLine_2.width = 0.0;
+	FillLine_0.patlength = 6.0;	FillLine_1.patlength = 0.6;	FillLine_2.patlength = 0.24;
+	Line_0.color = Line_1.color = Line_2.color = 0x00000000L;	//black
+	Line_0.pattern = Line_1.pattern = Line_2.pattern = 0L;		//solid line
+	Fill_0.type = Fill_1.type = Fill_2.type = FILL_NONE;
+	Fill_0.color = Fill_1.color = Fill_2.color = 0x00ffffffL;	//white background
+	Fill_0.scale = Fill_1.scale = Fill_2.scale = 1.0;			//size = 100%
+	Fill_0.hatch = &FillLine_0;	Fill_1.hatch = &FillLine_1;	Fill_2.hatch = &FillLine_2;
+	Fill_0.color2 = Fill_1.color2 = Fill_2.color2 = 0x00ffffffL;	//white background
+	OutLine_0.width = .2;	OutLine_1.width = .02;	OutLine_2.width = 0.008;
+	OutLine_0.patlength = 6.0;	OutLine_1.patlength = 0.6;	OutLine_2.patlength = 0.24;
+	OutLine_0.color = OutLine_1.color = OutLine_2.color = 0x00000000L;
+	OutLine_0.pattern = OutLine_1.pattern = OutLine_2.pattern = 0L;
+	pl = pgl = 0L;	pg = 0L;	pg_fl = 0L;	rrect_rad = 0L;
+	cdisp = 0L;		min4log = 0.000001;		axis_color = 0x0L;
+	ss_txt = 0.9;	out_upd = 0L;			can_upd = false;
+#ifdef _WINDOWS	
+	iMenuHeight = 0;
+#else
+	iMenuHeight = 25;
+#endif
+	svgAttr = svgScript = currPath = IniFile = 0L;
+	File1 = File2 = File3 = File4 = File5 = File6 = 0L;
+	if(fmt_date = (char*)malloc(20)) rlp_strcpy(fmt_date, 20, "Z.V.Y");
+	if(fmt_time = (char*)malloc(20)) rlp_strcpy(fmt_time, 20, "H:M:S");
+	if(fmt_datetime = (char*)malloc(20)) rlp_strcpy(fmt_datetime, 20, "Z.V.Y H:M:S");
+}
+
+Default::~Default()
+{
+	if(svgAttr) free(svgAttr);		if(svgScript) free(svgScript);
+	if(currPath) free(currPath);	if(IniFile) free(IniFile);
+	svgAttr = svgScript = currPath = 0L;
+	if(pl) free(pl);				if(pgl) free(pgl);
+	if(pg_fl) free(pg_fl);			if(pg) free(pg);
+	if(rrect_rad) free(rrect_rad);
+	if(File1) free(File1);			if(File2) free(File2);
+	if(File3) free(File3);			if(File4) free(File4);
+	if(File5) free(File5);			if(File6) free(File6);
+	if(fmt_date) free(fmt_date);	if(fmt_time) free(fmt_time);
+	if(fmt_datetime) free(fmt_datetime);
+}
+
+void
+Default::SetDisp(anyOutput *o)
+{
+	if(o && o != cdisp) {
+		Undo.SetDisp(o);
+		cdisp = o;
+		}
+}
+
+double
+Default::GetSize(int select)
+{
+	double RetVal = 0.0;
+
+	switch (select){
+	case SIZE_SYMBOL:				RetVal = 3.0;		break;
+	case SIZE_SYM_LINE:				RetVal = 0.2;		break;
+	case SIZE_DATA_LINE:
+		switch(cUnits) {
+		case 1:		return Line_1.width;
+		case 2:		return Line_2.width;
+		default:	return Line_0.width;
+		}
+	case SIZE_TEXT:					RetVal = _TXH;		break;
+	case SIZE_GRECT_TOP:			RetVal = 0.0;		break;
+	case SIZE_GRECT_BOTTOM:			RetVal = 110.0;		break;
+	case SIZE_GRECT_LEFT:			RetVal = 0.0;		break;
+	case SIZE_GRECT_RIGHT:			RetVal = 160.0;		break;
+	case SIZE_DRECT_TOP:			RetVal = 10.0;		break;
+	case SIZE_DRECT_BOTTOM:			RetVal = 90.0;		break;
+	case SIZE_DRECT_LEFT:			RetVal = 25.0;		break;
+	case SIZE_DRECT_RIGHT:			RetVal = 140.0;		break;
+	case SIZE_PATLENGTH:
+	case SIZE_DATA_LINE_PAT:
+		switch(cUnits) {
+		case 1:		return Line_1.patlength;
+		case 2:		return Line_2.patlength;
+		default:	return Line_0.patlength;
+		}
+	case SIZE_AXIS_TICKS:			RetVal = 2.0;		break;
+	case SIZE_TICK_LABELS:			RetVal = _TXH;		break;
+	case SIZE_WHISKER:
+	case SIZE_ERRBAR:				RetVal = 3.0;		break;
+	case SIZE_WHISKER_LINE:
+	case SIZE_AXIS_LINE:
+	case SIZE_BAR_LINE:			
+	case SIZE_ERRBAR_LINE:
+		switch(cUnits) {
+		case 1:		return OutLine_1.width;
+		case 2:		return OutLine_2.width;
+		default:	return OutLine_0.width;
+		}
+	case SIZE_BOX:
+	case SIZE_BAR:					RetVal = 10.0;				break;
+	case SIZE_BUBBLE_LINE:			RetVal = 0.4;				break;
+	case SIZE_BUBBLE_HATCH_LINE:	RetVal = 0.1;				break;
+	case SIZE_ARROW_LINE:			RetVal = 0.4;				break;
+	case SIZE_ARROW_CAPWIDTH:		RetVal = 3.0;				break;
+	case SIZE_ARROW_CAPLENGTH:		RetVal = 4.0;				break;
+	case SIZE_HAIRLINE:				RetVal = 0.1;				break;
+	case SIZE_SEGLINE:				RetVal = 0.4;				break;
+	case SIZE_CELLWIDTH:			RetVal = 20.0;				break;
+	case SIZE_CELLTEXT:
+#ifdef _WINDOWS
+		RetVal = 4.5*ss_txt;		break;
+#else
+		RetVal = 4.5*ss_txt*0.7;	break;
+#endif
+	case SIZE_RRECT_RAD:			
+		return rrect_rad ? *rrect_rad : GetSize(SIZE_SYMBOL);
+	case SIZE_SCALE:				return 1.0;
+	default:	return 0.0;
+		}
+	switch(cUnits) {
+	case 1:	RetVal /= 10.0;	break;
+	case 2:	RetVal = NiceValue(RetVal/25.4);	break;
+		}
+	return RetVal;
+}
+
+DWORD
+Default::Color(int select)
+{
+	switch (select){
+	case COL_ERROR_LINE:
+	case COL_WHISKER:
+	case COL_SYM_LINE:			return 0x00000000L;
+	case COL_SYM_FILL:			return 0x00ffffffL;
+	case COL_ARROW:
+	case COL_DATA_LINE:			return Line_0.color;
+	case COL_TEXT:				return 0x00000000L;
+	case COL_BG:				return 0x00ffffffL;
+	case COL_AXIS:				return axis_color;
+	case COL_BAR_LINE:			return OutLine_0.color;
+	case COL_BAR_FILL:			return 0x00ffffffL;
+	case COL_BUBBLE_LINE:
+	case COL_BUBBLE_FILLLINE:	return 0x00ff0000L;
+	case COL_BUBBLE_FILL:		return 0x00ffc0c0L;
+	case COL_DRECT:				return 0x00ffffffL;
+	case COL_GRECT:				return 0x00ffffffL;
+	case COL_GRECTLINE:			return 0x00e0e0e0L;
+	case COL_POLYLINE:			return pl ? pl->color : OutLine_0.color;
+	case COL_POLYGON:			return pgl ? pgl->color : OutLine_0.color;
+	default:					return 0x00C0C0C0L;	//Error
+	}
+}
+
+LineDEF *
+Default::GetLine()
+{
+	switch (cUnits) {
+	case 1:		return &Line_1;
+	case 2:		return &Line_2;
+	default:	return &Line_0;
+		}
+}
+
+void
+Default::SetLine(int u, LineDEF *l, int which)
+{
+	double lw, pl;
+	LineDEF *l1, *l2, *l3;
+
+	switch (which) {
+	case 0:	l1 = &Line_0;		l2 = &Line_1;		l3 = &Line_2;		break;
+	case 1:	l1 = &FillLine_0;	l2 = &FillLine_1;	l3 = &FillLine_2;	break;
+	case 2:	l1 = &OutLine_0;	l2 = &OutLine_1;	l3 = &OutLine_2;	break;
+	default: return;
+		}
+	l1->color = l2->color = l3->color = l->color;
+	l1->pattern = l2->pattern = l3->pattern = l->pattern;
+	switch (u) {
+	case 1:
+		lw = l->width*10.0;	pl = l->patlength*10.0;
+		l1->width = lw;			l1->patlength = pl;
+		l2->width = l->width;	l2->patlength = l->patlength;
+		l3->patlength = NiceValue(pl/25.4);
+		l3->patlength = NiceValue(pl/25.4);
+		break;
+	case 2:
+		lw = NiceValue(l->width*25.4);	pl = NiceValue(l->patlength*25.4);
+		l1->width = lw;			l1->patlength = pl;
+		l2->width = lw/10.0;	l2->patlength = pl/10.0;
+		l3->width = l->width;	l3->patlength = l->patlength;
+		break;
+	default:
+		lw = l->width;		pl = l->patlength;
+		l1->width = l->width;	l1->patlength = l->patlength;
+		l2->width = lw/10.0;		l2->patlength = pl/10.0;
+		l3->patlength = NiceValue(pl/25.4);
+		l3->patlength = NiceValue(pl/25.4);
+		break;
+		}
+}
+
+FillDEF *
+Default::GetFill()
+{
+	switch (cUnits) {
+	case 1:		return &Fill_1;
+	case 2:		return &Fill_2;
+	default:	return &Fill_0;
+		}
+}
+
+void
+Default::SetFill(int u, FillDEF *fd)
+{
+	memcpy(&Fill_0, fd, sizeof(FillDEF));
+	memcpy(&Fill_1, fd, sizeof(FillDEF));
+	memcpy(&Fill_2, fd, sizeof(FillDEF));
+	if(fd->hatch) SetLine(u, fd->hatch, 1);
+	Fill_0.hatch = &FillLine_0;
+	Fill_1.hatch = &FillLine_1;
+	Fill_2.hatch = &FillLine_2;
+}
+
+LineDEF *
+Default::GetOutLine()
+{
+	switch (cUnits) {
+	case 1:		return &OutLine_1;
+	case 2:		return &OutLine_2;
+	default:	return &OutLine_0;
+		}
+}
+
+LineDEF *
+Default::plLineDEF(LineDEF *ld)
+{
+	if(ld) {
+		if(pl) free(pl);
+		if(pl = (LineDEF *)malloc(sizeof(LineDEF))) memcpy(pl, ld, sizeof(LineDEF));
+		}
+	if(pl) return pl;
+	else return GetOutLine();
+}
+
+LineDEF *
+Default::pgLineDEF(LineDEF *ol)
+{
+	if(ol) {
+		if(pgl) free(pgl);
+		if(pgl = (LineDEF *)malloc(sizeof(LineDEF))) memcpy(pgl, ol, sizeof(LineDEF));
+		}
+	if(pgl) return pgl;
+	else return GetOutLine();
+}
+
+FillDEF *
+Default::pgFillDEF(FillDEF *fd)
+{
+	if(fd) {
+		if(pg) free(pg);
+		if(pg = (FillDEF *)malloc(sizeof(FillDEF))){
+			memcpy(pg, fd, sizeof(FillDEF));
+			if(pg->hatch) {
+				if(pg_fl) free(pg_fl);
+				if(pg_fl = (LineDEF *)malloc(sizeof(LineDEF)))
+					memcpy(pg_fl, pg->hatch, sizeof(LineDEF));
+				pg->hatch = pg_fl;
+				}
+			}
+		}
+	if(pg) return pg;
+	else return GetFill();
+}
+
+double
+Default::rrectRad(double rad)
+{
+	if(!rrect_rad)rrect_rad=(double*)malloc(sizeof(double));
+	if(rrect_rad) return (*rrect_rad = rad);
+	return 0.0;
+}
+
+void
+Default::FileHistory(char *path)
+{
+	char *tmp_path = 0L, *tmp;
+	char **history[] = {&File1, &File2, &File3, &File4, &File5, &File6};
+	int i, j;
+
+	if(path && (tmp_path=(char*)malloc((i=(int)strlen(path))+10))){
+		rlp_strcpy(tmp_path, i+1, path);
+		for(j = i ; i > 0 && tmp_path[i] != '/' && tmp_path[i] != '\\'; i--);
+		tmp_path[i] = 0;
+		if(currPath) free(currPath);
+		if(tmp_path[0]) currPath = (char*)memdup(tmp_path, i+1, 0);
+		else currPath = 0L;
+		rlp_strcpy(tmp_path, j+1, path);
+		if(File1 && strcmp(File1, tmp_path)) {
+			for(i = 0; i < 6 && tmp_path; i++) {
+				if(i && *history[i] && !strcmp(File1, *history[i])){
+					free(*history[i]);
+					*history[i] = tmp_path;
+					tmp_path = 0L;
+					break;
+					}
+				if(*history[i]) {
+					if(strcmp(tmp_path, *history[i])){
+						tmp = *history[i];
+						*history[i] = tmp_path;
+						tmp_path = tmp;
+						}
+					else { 
+						free(tmp_path);
+						tmp_path = 0L;
+						}
+					}
+				else{
+					tmp = *history[i];
+					*history[i] = tmp_path;
+					tmp_path = tmp;
+					}
+				}
+			}
+		if(!(*history[0])) {
+			*history[0] = tmp_path;
+			tmp_path = 0L;
+			}
+		}
+	if(tmp_path) free(tmp_path);
+}
+
+void
+Default::Idle(int cmd)
+{
+	if(cmd == CMD_UPDATE && can_upd && out_upd) {
+		IncrementMinMaxRect(&rec_upd, 6);
+		if(rec_upd.left < 0) rec_upd.left = 0;
+		if(rec_upd.top < 0) rec_upd.top = 0;
+		out_upd->UpdateRect(&rec_upd, false);
+		}
+	if(cmd == CMD_FLUSH) {
+		}
+	out_upd = 0L;	can_upd = false;
+}
+
+void
+Default::UpdRect(anyOutput *o, int x1, int y1, int x2, int y2)
+{
+	if(out_upd && o != out_upd) Idle(CMD_UPDATE);
+	out_upd = o;
+	if(can_upd){
+		UpdAdd(o, x1, y1);		UpdAdd(o, x2, y2);
+		}
+	else SetMinMaxRect(&rec_upd, x1, y1, x2, y2);
+	can_upd = true;
+}
+
+void
+Default::UpdAdd(anyOutput * o, int x, int y)
+{
+	if(o == out_upd && can_upd)
+		UpdateMinMaxRect(&rec_upd, x, y);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Chache file input for read operations
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#define CharCacheSize 1024
+ReadCache::ReadCache()
+{
+	Cache = 0L;		idx = max = 0;		eof = true;
+}
+
+ReadCache::~ReadCache()
+{
+	if(Cache) free(Cache);
+	Cache = 0L;
+}
+
+bool
+ReadCache::Open(char *name)
+{
+	idx = max = 0;
+	eof = true;
+	if(!name) iFile = -1;
+#ifdef USE_WIN_SECURE
+	else if(_sopen_s(&iFile, name, O_BINARY, 0x40, S_IWRITE) || iFile < 0) return false;
+#else
+	else if(-1 ==(iFile = open(name, O_BINARY))) return false;
+#endif
+	Cache = (unsigned char *)malloc((unsigned)(CharCacheSize + 1));
+	if(Cache) return true;
+	return false;
+}
+
+void
+ReadCache::Close()
+{
+#ifdef USE_WIN_SECURE
+	if(iFile >= 0) _close(iFile);
+#else
+	if(iFile >= 0) close(iFile);
+#endif
+	if(Cache) free(Cache);				Cache = 0L;
+}
+
+unsigned char
+ReadCache::Getc()
+{
+	if(Cache){
+		if(idx < max) return (last = Cache[idx++]);
+		else {
+			do {
+#ifdef USE_WIN_SECURE
+				max = _read(iFile, Cache, CharCacheSize);
+#else
+				max = read(iFile, Cache, CharCacheSize);
+#endif
+				if(max <=0) {
+					eof = true;
+					return 0;
+					}
+				else eof = false;
+				}while(max == 0);
+			idx = 1;
+			return(last = Cache[0]);
+			}
+		}
+	return 0;
+}
+
+unsigned char *
+ReadCache::GetField()
+{
+	int i;
+	static unsigned char *ret;
+
+	if(Cache && max) {
+		while(idx < max && Cache[idx] < 43) idx++;
+		if(idx == max){
+			if(max == CharCacheSize) {
+#ifdef USE_WIN_SECURE
+				max = _read(iFile, Cache, CharCacheSize);
+#else
+				max = read(iFile, Cache, CharCacheSize);
+#endif
+				idx = 0;
+				return GetField();
+				}
+			else return 0L;
+			}
+		i = idx;
+		while(i < max && Cache[i] > 32 && Cache[i] <= 'z') i++;
+		if(i == max) {
+			for(i = 0; (Line[i] = Getc()) >32 && Line[i] <= 'z' && i < 4096; i++);
+			Line[i] = 0;
+			return Line;
+			}
+		else {
+			ret = Cache+idx;
+			idx = i;
+			return ret;
+			}
+		}
+	return 0L;
+}
+
+void
+ReadCache::ReadLine(char *dest, int size)
+{
+	int i=0;
+	unsigned char c;
+
+	dest[0] = 0;
+	do {
+		c =  Getc();
+		if(c == 0x09) c = 0x32;			// tab to space
+		if(c > 31) dest[i++] = (char)c;
+		}while(c && c != 0x0a && i < size);
+	dest[i] = 0;
+}
+
+bool
+ReadCache::GetInt(long *in)
+{
+	unsigned char *field;
+
+	field = GetField();
+	if(field && field[0]) {
+		*in = atol((char*)field);
+		if(*in == 0 && field[0] == '}') return false;
+		return true;
+		}
+	return false;
+}
+
+bool
+ReadCache::GetFloat(double *fn)
+{
+	unsigned char *field;
+
+	field = GetField();
+	if(field && field[0]) {
+		*fn = atof((char*)field);
+		if(*fn == 0.0 && field[0] == '}') return false;
+		return true;
+		}
+	return false;
+}
+
+unsigned char
+ReadCache::Lastc()
+{
+	return last;
+}
+
+bool
+ReadCache::IsEOF()
+{
+	return eof;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Process memory block as if file input
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+MemCache::MemCache(unsigned char *ptr)
+:ReadCache()
+{
+	if(ptr) {
+		max = (int)strlen((char*)ptr);
+		Cache = (unsigned char*) memdup(ptr, max+1, 0);
+		eof = false;
+		}
+}
+
+MemCache::~MemCache()
+{
+	if(Cache) free(Cache);
+	Cache = 0L;
+}
+
+unsigned char
+MemCache::Getc()
+{
+	if(Cache){
+		if(idx < max) return (last = Cache[idx++]);
+		else {
+			eof = true;
+			return 0;
+			}
+		}
+	return 0;
+}
+
+unsigned char *
+MemCache::GetField()
+{
+	int i;
+	static unsigned char *ret;
+
+	if(Cache && max) {
+		while(idx < max && Cache[idx] < 43) idx++;
+		i = idx;
+		while(i < max && Cache[i] > 32 && Cache[i] <= 'z') i++;
+		ret = Cache+idx;
+		idx = i;
+		return ret;
+		}
+	return 0L;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Process Undo 
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#define UNDO_RING_SIZE 0x100
+#define UNDO_IDX_MASK 0xff
+UndoObj::UndoObj()
+{
+	buff = buff0 = (UndoInfo**)calloc(UNDO_RING_SIZE, sizeof(UndoInfo*));
+	stub1 = ndisp = 0;					busy = false;
+	pcb = &stub1;	cdisp = ldisp = 0L;	buffers = 0L;
+}
+
+UndoObj::~UndoObj()
+{
+	Flush();
+	if(buff0) free(buff0);
+}
+
+//free all resources associated with undo operations
+void
+UndoObj::Flush()
+{
+	int i, j;
+
+	if(!buffers) return;
+	for(i = 0; i < ndisp; i++) if(buffers[i]) {
+		if(buffers[i]->buff) {
+			for(j = 0; j < UNDO_RING_SIZE; j++) {
+				if(buffers[i]->buff[j]) FreeInfo(&buffers[i]->buff[j]);
+				}
+			free(buffers[i]->buff);
+			}
+		free(buffers[i]);
+		}
+	free(buffers);
+	stub1 = ndisp = 0;
+	pcb = &stub1;	cdisp = 0L;	buffers = 0L;
+}
+
+bool
+UndoObj::isEmpty(anyOutput *o)
+{
+	int i;
+
+	if(!buffers) return true;
+	for (i = 0; i < ndisp; i++) if(buffers[i]){
+		if(o && buffers[i]->disp == o ) {
+			if(buffers[i]->count > 0) return false;
+			else return true;
+			}
+		else if(!o && buffers[i]->count > 0) return false;
+		}
+	return true;
+}
+
+//select buffers associated with the current window/widget,
+//create new buffers if called for the first time
+void
+UndoObj::SetDisp(anyOutput *o)
+{
+	int i;
+	void *ptmp;
+
+	if(o && o != cdisp) {
+		ldisp = cdisp;
+		if(buffers) {
+			for(i = 0; i < ndisp; i++) {
+				if(buffers[i] && buffers[i]->disp == o){
+					cdisp = o;
+					buff = buffers[i]->buff;	pcb = &buffers[i]->count;
+					return;
+					}
+				else if(!buffers[i] && (buffers[i] = (UndoBuff*)calloc(1, sizeof(UndoBuff)))) {
+					buffers[i]->buff = (UndoInfo**)calloc(UNDO_RING_SIZE, sizeof(UndoInfo*));
+					buffers[i]->disp = cdisp = o;
+					buff = buffers[i]->buff;	pcb = &buffers[i]->count;
+					return;
+					}
+				}
+			if(ptmp = memdup(buffers, sizeof(UndoBuff*) *(ndisp+1), 0)){
+				free(buffers);		buffers = (UndoBuff**)ptmp;
+				if(buffers[ndisp] = (UndoBuff*)calloc(1, sizeof(UndoBuff))) {
+					buffers[ndisp]->buff = (UndoInfo**)calloc(UNDO_RING_SIZE, sizeof(UndoInfo*));
+					buffers[ndisp]->disp = cdisp = o;
+					buff = buffers[ndisp]->buff;	pcb = &buffers[ndisp]->count;
+					ndisp++;
+					}
+				}
+			}
+		else if(buffers = (UndoBuff**)calloc(1, sizeof(UndoBuff*))){
+			if(buffers[0] = (UndoBuff*)calloc(1, sizeof(UndoBuff))) {
+				buffers[0]->buff = (UndoInfo**)calloc(UNDO_RING_SIZE, sizeof(UndoInfo*));
+				buffers[0]->disp = cdisp = o;
+				ndisp = 1;
+				buff = buffers[0]->buff;	pcb = &buffers[0]->count;
+				}
+			}
+		}
+}
+
+//free memory for a closed output
+void 
+UndoObj::KillDisp(anyOutput *o)
+{
+	int i, j, c_buf;
+
+	InvalidateOutput(o);			//kill text cursor and animated rectangle
+	if(o && buffers) {
+		for(i = 0, c_buf = -1; i < ndisp; i++) {
+			if(buffers[i] && buffers[i]->disp == o)	{
+				c_buf = i;	break;
+				}
+			}
+		if(c_buf < 0) return;
+		if(buffers[c_buf]->buff) {
+			for(j = 0; j < UNDO_RING_SIZE; j++) {
+				if(buffers[c_buf]->buff[j]) FreeInfo(&buffers[c_buf]->buff[j]);
+				}
+			free(buffers[c_buf]->buff);
+			}
+		free(buffers[c_buf]);	buffers[c_buf] = 0L;
+		}
+	if(ldisp && o == cdisp) SetDisp(ldisp);
+	else cdisp = 0L;
+}
+
+//remove references to an invalid (probabbly deleted) object
+void
+UndoObj::InvalidGO(GraphObj *go)
+{
+	int i, i1, i2;
+
+	if(*pcb >10) {
+		if(*pcb < UNDO_RING_SIZE){
+			i1 = 0;		i2 = *pcb;
+			}
+		else {
+			i1 = ((*pcb) & UNDO_IDX_MASK);
+			i2 = (((*pcb)-UNDO_RING_SIZE) & UNDO_IDX_MASK);
+			if(i1 > i2) Swap(i1, i2);
+			}
+		}
+	else {
+		i1 = 0;		i2 = *pcb;
+		}
+	for(i = i1; i < i2; i++) {
+		if(buff[i] && buff[i]->owner == go) FreeInfo(&buff[i]);
+		if(buff[i]) switch(buff[i]->cmd) {
+//		case UNDO_OBJCONF:
+		case UNDO_OBJCONF_1:
+			if(buff[i]->loc == (void**)go) FreeInfo(&buff[i]);
+			break;
+			}
+		}
+}
+
+void
+UndoObj::Pop(anyOutput *o)
+{
+	int idx;
+
+	if(o) {
+		SetDisp(o);
+		if(*pcb < 1) return;
+		idx = ((*pcb-1) & UNDO_IDX_MASK);
+		if(buff[idx]) FreeInfo(&buff[idx]);
+		(*pcb)--;
+		}
+}
+
+void
+UndoObj::Restore(bool redraw, anyOutput*o)
+{
+	int i, j, idx;
+	DWORD flags;
+	UndoList *ul;
+	GraphObj **gol;
+	unsigned char *savbuf, *target;
+
+	if(o) SetDisp(o);
+	else if(cdisp) o = cdisp;
+	CurrGO = 0L;
+	if(*pcb < 1){
+		InfoBox("The UNDO-cache is empty");
+		return;
+		}
+	do {
+		idx = ((*pcb-1) & UNDO_IDX_MASK);
+		if(!buff[idx] && *pcb > 0) (*pcb)--;
+		} while(!buff[idx] && *pcb > 0);
+	if(!buff[idx]) return;
+	if(buff[idx]->zd.org.fx != cdisp->VPorg.fx ||
+		buff[idx]->zd.org.fy != cdisp->VPorg.fy || buff[idx]->zd.scale != cdisp->VPscale){
+		cdisp->VPorg.fx = buff[idx]->zd.org.fx;			cdisp->VPorg.fy = buff[idx]->zd.org.fy;
+		cdisp->VPscale = buff[idx]->zd.scale;			HideCopyMark();
+		if(cdisp->VPscale > 100.0) cdisp->VPscale = 100.0;
+		if(cdisp->VPscale < 0.05) cdisp->VPscale = 0.05;
+		if(buff[idx]->owner)
+			if(buff[idx]->owner->Id < GO_PLOT) CurrGO = buff[idx]->owner;
+			if(!((buff[idx]->owner)->Command(CMD_SETSCROLL, 0L, cdisp)))
+				(buff[idx]->owner)->Command(CMD_REDRAW, 0L, cdisp);
+		return;
+		}
+	(*pcb)--;
+	busy = true;
+	if(buff[idx]) {
+		flags = buff[idx]->flags;
+		switch(buff[idx]->cmd) {
+		case UNDO_DATA:
+			RestoreData(buff[idx]);
+			break;
+		case UNDO_ET:
+			if(buff[idx]->loc && buff[idx]->data) {
+				if(((EtBuff*)buff[idx]->data)->DaO){
+					buff[idx]->loc = (void**)((EtBuff*)buff[idx]->data)->DaO->
+						etRows[((EtBuff*)buff[idx]->data)->row][((EtBuff*)buff[idx]->data)->col];
+					}
+				if(((EtBuff*)buff[idx]->data)->DaO) (((EtBuff*)buff[idx]->data)->DaO)->SetText(((EtBuff*)buff[idx]->data)->row,
+					((EtBuff*)buff[idx]->data)->col, ((EtBuff*)buff[idx]->data)->txt);
+				else ((EditText*)buff[idx]->loc)->SetText(((EtBuff*)buff[idx]->data)->txt);
+				if(((EtBuff*)buff[idx]->data)->txt) free(((EtBuff*)buff[idx]->data)->txt);
+				*(((EtBuff*)buff[idx]->data)->cur) = ((EtBuff*)buff[idx]->data)->vcur;
+				*(((EtBuff*)buff[idx]->data)->m1) = ((EtBuff*)buff[idx]->data)->vm1;
+				*(((EtBuff*)buff[idx]->data)->m2) = ((EtBuff*)buff[idx]->data)->vm2;
+				((EditText*)buff[idx]->loc)->Command(CMD_MRK_DIRTY, cdisp, 0L);
+				}
+			break;
+		case UNDO_TEXTBUF:
+			if(buff[idx]->loc && buff[idx]->data) {
+				target = *((TextBuff*)buff[idx]->data)->pbuff;
+				target = ((TextBuff*)buff[idx]->data)->buff;
+				if(*((TextBuff*)buff[idx]->data)->pbuff) free(*((TextBuff*)buff[idx]->data)->pbuff);
+				*(((TextBuff*)buff[idx]->data)->pbuff) = ((TextBuff*)buff[idx]->data)->buff;
+				*(((TextBuff*)buff[idx]->data)->psize) = ((TextBuff*)buff[idx]->data)->size;
+				*(((TextBuff*)buff[idx]->data)->ppos) = ((TextBuff*)buff[idx]->data)->pos;
+				}
+			break;
+		case UNDO_MUTATE:
+		case UNDO_DEL_GO:
+			((GraphObj*)(buff[idx]->data))->parent = buff[idx]->owner;
+			if(buff[idx]->cmd == UNDO_MUTATE && *(buff[idx]->loc))
+				::DeleteGO((GraphObj*)*(buff[idx]->loc));
+			else CurrGO = (GraphObj*) buff[idx]->data;
+			*(buff[idx]->loc) = buff[idx]->data;
+			(buff[idx]->owner)->Command(CMD_MRK_DIRTY, 0L, 0L);
+			break;
+		case UNDO_DROPGOLIST:
+			if((ul = (UndoList *)(buff[idx]->data)) && (ul->array)){
+				gol = (GraphObj**)(*(ul->loc_arr));
+				if(gol) for (i = 0; i < *(ul->loc_count); i++) if(gol[i]) ::DeleteGO(gol[i]);
+				*(ul->loc_count) = ul->count;				if(gol) free(gol);
+				*(ul->loc_arr) = ul->array;					free(ul);
+				}
+			break;
+		case UNDO_GOLIST:
+			if((ul = (UndoList *)(buff[idx]->data)) && (ul->array)){
+				memcpy(*(ul->loc_arr), ul->array, ul->count * sizeof(GraphObj*));
+				*(ul->loc_count) = ul->count;				free(ul->array);
+				free(ul);
+				}
+			break;
+		case UNDO_DROPMEM:
+			*(buff[idx]->loc) = buff[idx]->data;				break;
+		case UNDO_SAVVAR:
+			if(!(savbuf = (unsigned char *)buff[idx]->data))break;
+			for(i = 0; ; ) {
+				memcpy(&target, savbuf+i, sizeof(unsigned char*));	i += sizeof(unsigned char*);
+				memcpy(&j, savbuf+i, sizeof(int));					i += sizeof(int);
+				if(!target) break;
+				memcpy(target, savbuf+i, j);						i += j;
+				}
+			if(buff[idx]->owner)(buff[idx]->owner)->Command(CMD_MRK_DIRTY, 0L, 0L);
+			free(savbuf);
+			break;
+		case UNDO_VALDWORD:
+			*((DWORD*)(buff[idx]->loc)) = *((DWORD*)(buff[idx]->data));
+			free(buff[idx]->data);								break;
+		case UNDO_POINT:
+			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(POINT));
+			free(buff[idx]->data);								break;
+		case UNDO_VOIDPTR:
+			*buff[idx]->loc = buff[idx]->data;					break;
+		case UNDO_VALINT:
+			*((int*)(buff[idx]->loc)) = *((int*)(buff[idx]->data));
+			free(buff[idx]->data);								break;
+		case UNDO_VALLONG:
+			*((long*)(buff[idx]->loc)) = *((long*)(buff[idx]->data));
+			free(buff[idx]->data);								break;
+		case UNDO_OBJCONF_1:			//single object restore
+			UpdGOfromMem((GraphObj *)buff[idx]->loc, (unsigned char *)buff[idx]->data);
+			free(buff[idx]->data);								break;
+		case UNDO_OBJCONF:				//tree of objects to restore
+			RestoreConf(buff[idx]);
+			if(buff[idx] && buff[idx]->data) free(buff[idx]->data);								break;
+		case UNDO_LFP:
+			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(lfPOINT));
+			free(buff[idx]->data);								break;
+		case UNDO_MOVE:
+			(buff[idx]->owner)->Command(CMD_UNDO_MOVE, buff[idx]->data, 0L);
+			free(buff[idx]->data);								break;
+		case UNDO_RECT:
+			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(fRECT));
+			free(buff[idx]->data);								break;
+		case UNDO_STRING:
+			if(*(buff[idx]->loc) && buff[idx]->data)
+				rlp_strcpy ((char*)(*buff[idx]->loc), 100, (char*)(buff[idx]->data));
+			else if(*(buff[idx]->loc))((char*)*(buff[idx]->loc))[0] = 0;
+			if(buff[idx]->data) free(buff[idx]->data);
+			CurrGO = buff[idx]->owner;							break;
+		case UNDO_ROTDEF:
+			memcpy(*(buff[idx]->loc), buff[idx]->data, 6 * sizeof(double));
+			free(buff[idx]->data);								break;
+		case UNDO_SETGO:
+			::DeleteGO(*((GraphObj**)(buff[idx]->loc)));
+			*((GraphObj**)(buff[idx]->loc)) = 0L;				break;
+		case UNDO_LINEDEF:
+			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(LineDEF));
+			free(buff[idx]->data);								break;
+		case UNDO_FILLDEF:
+			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(FillDEF) - (sizeof(LineDEF*) + sizeof(DWORD)));
+			((FillDEF*)(buff[idx]->loc))->color2 = ((FillDEF*)(buff[idx]->data))->color2;
+			free(buff[idx]->data);								break;
+		case UNDO_AXISDEF:
+			if(((AxisDEF*)(buff[idx]->loc))->breaks) free(((AxisDEF*)(buff[idx]->loc))->breaks);
+			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(AxisDEF));
+			free(buff[idx]->data);								break;
+		case UNDO_TEXTDEF:
+			if(((TextDEF*)(buff[idx]->loc))->text) free(((TextDEF*)(buff[idx]->loc))->text);
+			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(TextDEF));
+			free(buff[idx]->data);								break;
+		case UNDO_LFP3D:
+			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(fPOINT3D));
+			free(buff[idx]->data);								break;
+		case UNDO_FLOAT:
+			*((double*)(buff[idx]->loc)) = *((double*)(buff[idx]->data));
+			free(buff[idx]->data);								break;
+		case UNDO_MEM:
+			if((ul = (UndoList *)(buff[idx]->data)) && (ul->array)){
+				memcpy(*(ul->loc_arr), ul->array, ul->size);
+				*(ul->loc_count) = ul->count;
+				free(ul->array);
+				if(buff[idx]->owner->Id < GO_PLOT) CurrGO = (GraphObj*) buff[idx]->owner;
+				if(buff[idx]->owner)(buff[idx]->owner)->Command(CMD_MRK_DIRTY, 0L, 0L);
+				}
+			break;
+			}
+		if(flags & UNDO_CONTINUE){
+			free(buff[idx]);	buff[idx] = 0L;
+			Restore(redraw, cdisp);
+			}
+		else {
+			if(o) o->MrkMode = MRK_NONE;
+			if(redraw && buff[idx] && buff[idx]->owner){
+				(buff[idx]->owner)->Command(CMD_MRK_DIRTY, 0L, 0L);
+				(buff[idx]->owner)->Command(CMD_REDRAW, 0L, 0L);
+				}
+			if(buff[idx]) free(buff[idx]);	buff[idx] = 0L;
+			}
+		}
+	else {
+		InfoBox("The UNDO-cache is empty");
+		}
+	busy = false;
+}
+
+void
+UndoObj::ListGOmoved(GraphObj **oldlist, GraphObj **newlist, long size)
+{
+	long i;
+	int c;
+
+	if(!oldlist || !newlist || oldlist == newlist) return;
+	for(i = 0; i < size; i++) if(oldlist[i] == newlist[i]) {
+		for(c = 0; c < UNDO_RING_SIZE; c++) {
+			if(buff[c]) switch(buff[c]->cmd) {
+			case UNDO_DEL_GO:
+			case UNDO_SETGO:
+			case UNDO_OBJCONF_1:
+			case UNDO_OBJCONF:
+				if(buff[c]->loc == (void**) &oldlist[i]){
+					buff[c]->loc = (void**) &newlist[i];
+					}
+				break;
+				}
+			}
+		}
+}
+
+void
+UndoObj::DeleteGO(GraphObj **go, DWORD flags, anyOutput *o)
+{
+	if(!go || !(*go)) return;
+	HideCopyMark();
+	if(o){
+		SetDisp(o);					 o->HideMark();
+		}
+	if(CurrGO == *go) CurrGO = 0L;
+	if((*go)->Id == GO_POLYLINE || (*go)->Id == GO_POLYGON || (*go)->Id == GO_BEZIER){
+		if(CurrHandle && CurrHandle->parent==*go) {
+			if((*go)->Command(CMD_DELOBJ, CurrHandle, 0l)) return;
+			}
+		}
+	NewItem(UNDO_DEL_GO, flags, (*(go))->parent, *(go), (void**)go);
+	(*(go))->parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+	(*(go))->parent = 0L;
+	*(go) = CurrGO = 0L;
+}
+
+void
+UndoObj::MutateGO(GraphObj **old, GraphObj *repl, DWORD flags, anyOutput *o)
+{
+	if(!old || !(*old)) return;
+	if(o){
+		SetDisp(o);					 o->HideMark();
+		}
+	if(!(*old))return;	//HideMark might delete object: should never happen
+	if(CurrGO == *old) CurrGO = 0L;
+	NewItem(UNDO_MUTATE, flags, (*(old))->parent, *(old), (void**)old);
+	repl->parent = (*(old))->parent;
+	(*(old))->parent = 0L;
+	*(old) = repl;
+	repl->parent->Command(CMD_REDRAW, 0L, o);
+}
+
+void
+UndoObj::StoreListGO(GraphObj *parent, GraphObj ***go, long *count, DWORD flags)
+{
+	UndoList *ul;
+
+	if(ul = (UndoList *)malloc(sizeof(UndoList))) {
+		if(ul->array = memdup(*go, *count * sizeof(GraphObj*), 0)){
+			ul->loc_arr = (void **)go;
+			ul->count = *count;
+			ul->loc_count = count;
+			if(0 > NewItem(UNDO_GOLIST, flags, parent, ul, 0L)) {
+				free(ul->array);			free(ul);
+				}
+			}
+		else free(ul);
+		}
+}
+
+void
+UndoObj::DropListGO(GraphObj *parent, GraphObj ***go, long *count, DWORD flags)
+{
+	UndoList *ul;
+
+	if(ul = (UndoList *)malloc(sizeof(UndoList))) {
+		if(ul->array = *go) {
+			ul->loc_arr = (void **)go;		*go = 0L;
+			ul->count = *count;			*count = 0;
+			ul->loc_count = count;
+			if(0 > NewItem(UNDO_DROPGOLIST, flags, parent, ul, 0L)) {
+				free(ul->array);			free(ul);
+				}
+			}
+		else free(ul);
+		}
+}
+
+void
+UndoObj::DropMemory(GraphObj *parent, void **mem, DWORD flags)
+{
+	NewItem(UNDO_DROPMEM, flags, parent, *(mem), mem);
+	*mem = 0L;
+}
+
+void
+UndoObj::SavVarBlock(GraphObj *parent, void **mem, DWORD flags)
+{
+	NewItem(UNDO_SAVVAR, flags, parent, *(mem), mem);
+	*mem = 0L;
+}
+
+void
+UndoObj::ValDword(GraphObj *parent, DWORD *val, DWORD flags)
+{
+	void *ptr;
+
+	if(!(ptr = memdup(val, sizeof(DWORD), 0))) return;
+	if(0 > NewItem(UNDO_VALDWORD, flags, parent, ptr, (void**)val)) free(ptr);
+}
+
+void 
+UndoObj::Point(GraphObj *parent, POINT *pt, anyOutput * o, DWORD flags)
+{
+	void *ptr;
+
+	if(o) SetDisp(o);
+	if(!(ptr = memdup(pt, sizeof(POINT), 0))) return;
+	if(0 > NewItem(UNDO_POINT, flags, parent, ptr, (void**)pt)) free(ptr);
+}
+
+void 
+UndoObj::VoidPtr(GraphObj *parent, void **pptr, void *ptr, anyOutput *o, DWORD flags)
+{
+	if(o) SetDisp(o);
+	NewItem(UNDO_VOIDPTR, flags, parent, ptr, pptr);
+}
+
+void
+UndoObj::ValInt(GraphObj *parent, int *val, DWORD flags)
+{
+	void *ptr;
+
+	if(!(ptr = memdup(val, sizeof(int), 0))) return;
+	if(0 > NewItem(UNDO_VALINT, flags, parent, ptr, (void**)val)) free(ptr);
+}
+
+void
+UndoObj::ValLong(GraphObj *parent, long *val, DWORD flags)
+{
+	void *ptr;
+
+	if(!(ptr = memdup(val, sizeof(long), 0))) return;
+	if(0 > NewItem(UNDO_VALINT, flags, parent, ptr, (void**)val)) free(ptr);
+}
+
+void
+UndoObj::ObjConf(GraphObj *go, DWORD flags)
+{
+	long sz;
+	int idx;
+	
+	InvalidGO(go);
+	if(0<=(idx = NewItem(UNDO_OBJCONF, flags, go->parent, GraphToMem(go, &sz), (void**)go))){
+		if(cObsW == 1) buff[idx]->cmd = UNDO_OBJCONF_1;
+		(buff[idx]->owner)->Command(CMD_MRK_DIRTY, 0L, 0L);
+		}
+}
+
+int
+UndoObj::SaveLFP(GraphObj *go, lfPOINT *lfp, DWORD flags)
+{
+	int idx;
+	void *ptr;
+
+	if(!(ptr = memdup(lfp, sizeof(lfPOINT), 0))) return -1;
+	if(0 > (idx = NewItem(UNDO_LFP, flags, go, ptr, (void**)lfp))) free(ptr);
+	return idx;
+}
+
+void
+UndoObj::MoveObj(GraphObj *go, lfPOINT *lfp, DWORD flags)
+{
+	int idx;
+	lfPOINT dsp;
+
+	if(!lfp) return;
+	dsp.fx = -1.0 * lfp->fx;		dsp.fy = -1.0 * lfp->fy;
+	if((idx = SaveLFP(go, &dsp, flags)) <0) return;
+	buff[idx]->cmd = UNDO_MOVE;
+}
+
+void
+UndoObj::ValRect(GraphObj *go, fRECT *rec, DWORD flags)
+{
+	void *ptr;
+
+	if(!(ptr = memdup(rec, sizeof(fRECT), 0))) return;
+	if(0 > NewItem(UNDO_RECT, flags, go, ptr, (void**)rec)) free(ptr);
+}
+
+int
+UndoObj::String(GraphObj *go, char **s, DWORD flags)
+{
+	char *ptr;
+	int iret;
+
+	if(s && *s &&  *(*s)){
+		iret = (int)strlen(*(s));
+		ptr = (char*)memdup(*(s), iret+1, 0);
+		}
+	else {
+		ptr = 0L;				iret = 0;
+		}
+	if(0 > NewItem(UNDO_STRING, flags, go, ptr, (void**)s)) if(ptr) free(ptr);
+	return iret;
+}
+
+void
+UndoObj::RotDef(GraphObj *go, double **d, DWORD flags)
+{
+	void *ptr;
+
+	if(!(ptr = memdup(*d, 6 * sizeof(double), 0))) return;
+	if(0 > NewItem(UNDO_ROTDEF, flags, go, ptr, (void**)d)) free(ptr);
+}
+
+void
+UndoObj::SetGO(GraphObj *parent, GraphObj **where, GraphObj *go, DWORD flags)
+{
+	*where = go;
+	NewItem(UNDO_SETGO, flags, parent, 0L, (void**)where);
+}
+
+void
+UndoObj::Line(GraphObj *go, LineDEF *ld, DWORD flags)
+{
+	void *ptr;
+
+	if(!(ptr = memdup(ld, sizeof(LineDEF), 0))) return;
+	if(0 > NewItem(UNDO_LINEDEF, flags, go, ptr, (void**)ld)) free(ptr);
+}
+
+void
+UndoObj::Fill(GraphObj *go, FillDEF *fd, DWORD flags)
+{
+	void *ptr;
+
+	if(!(ptr = memdup(fd, sizeof(FillDEF), 0))) return;
+	if(0 > NewItem(UNDO_FILLDEF, flags, go, ptr, (void**)fd)) free(ptr);
+}
+
+void
+UndoObj::AxisDef(GraphObj *go, AxisDEF *ad, DWORD flags)
+{
+	AxisDEF *ptr;
+
+	if(!(ptr = (AxisDEF*) memdup(ad, sizeof(AxisDEF), 0))) return;
+	if(ptr->nBreaks && ptr->breaks) ptr->breaks = 
+		(lfPOINT*)memdup(ad->breaks, ad->nBreaks * sizeof(lfPOINT), 0);
+	if(0 > NewItem(UNDO_AXISDEF, flags, go, ptr, (void**)ad)) free(ptr);
+}
+
+void
+UndoObj::TextDef(GraphObj *go, TextDEF *td, DWORD flags)
+{
+	TextDEF *ptr;
+
+	if(!(ptr = (TextDEF*) memdup(td, sizeof(TextDEF), 0))) return;
+	if(td->text && td->text[0]) ptr->text = (char*)memdup(td->text, (int)strlen(td->text)+1, 0);
+	if(0 > NewItem(UNDO_TEXTDEF, flags, go, ptr, (void**)td)){
+		if(ptr->text) free(ptr->text);		free(ptr);
+		}
+}
+
+void
+UndoObj::ValLFP3D(GraphObj *go, fPOINT3D *lfp, DWORD flags)
+{
+	void *ptr;
+
+	if(!(ptr = memdup(lfp, sizeof(fPOINT3D), 0))) return;
+	if(0 > NewItem(UNDO_LFP3D, flags, go, ptr, (void**)lfp)) free(ptr);
+}
+
+void
+UndoObj::ValFloat(GraphObj *parent, double *val, DWORD flags)
+{
+	void *ptr;
+
+	if(!(ptr = memdup(val, sizeof(double), 0))) return;
+	if(0 > NewItem(UNDO_FLOAT, flags, parent, ptr, (void**)val)) free(ptr);
+}
+
+void
+UndoObj::DataMem(GraphObj *go, void **mem, int size, long *count, DWORD flags)
+{
+	UndoList *ul;
+
+	if(ul = (UndoList *)malloc(sizeof(UndoList))) {
+		if(ul->array = memdup(*mem, size, 0)){
+			ul->loc_arr = (void **)mem;
+			ul->size = size;
+			ul->count = *count;
+			ul->loc_count = count;
+			if(0 > NewItem(UNDO_MEM, flags, go, ul, 0L)) {
+				free(ul->array);			free(ul);
+				}
+			}
+		else free(ul);
+		}
+}
+
+void
+UndoObj::DataObject(GraphObj *go, anyOutput *o, DataObj *d, RECT *rc, DWORD flags)
+{
+	StrData *save;
+
+	if(!go || !d) return;
+	if(o) SetDisp(o);
+	if(!(save = new StrData(d, rc))) return;
+	if(0 > NewItem(UNDO_DATA, flags, go, save, (void**)d)) if(save) delete(save);
+}
+
+void 
+UndoObj::TextCell(EditText *et, anyOutput *o, char *text, int *cur, int *m1, int *m2, void* DaO, DWORD flags)
+{
+	anyOutput *o_save;
+	EtBuff *ptr;
+	POINT cpos = {-1, -1};
+
+	if(o) {
+		o_save = cdisp;		SetDisp(o);
+		if(ptr = (EtBuff*) calloc(1, sizeof(EtBuff))) {
+			if(text && text[0]) ptr->txt = (char*)memdup(text, (int)strlen(text)+1, 0);
+			ptr->cur = cur;			ptr->m1 = m1;		ptr->m2 = m2;
+			ptr->vcur = *cur;		ptr->vm1 = *m1;		ptr->vm2 = *m2;
+			ptr->row = et->row;		ptr->col = et->col;
+			ptr->DaO = (DataObj*)et->parent;
+			if(0 > NewItem(UNDO_ET, flags, 0L, ptr, (void**)et)) {
+				if(ptr->txt)free (ptr->txt);			free(ptr);
+				}
+			}
+		SetDisp(o_save);
+		}
+}
+
+void
+UndoObj::TextBuffer(GraphObj *parent, int *psize, int *ppos, unsigned char **pbuff, DWORD flags, anyOutput *o)
+{
+	TextBuff *ptr;
+
+	if(o) SetDisp(o);
+	if(!parent || !psize || !ppos || !pbuff) return;
+	if(ptr = (TextBuff*)calloc(1, sizeof(TextBuff))) {
+		ptr->size = *psize;			ptr->psize = psize;
+		ptr->pos = *ppos;			ptr->ppos = ppos;
+		ptr->pbuff = pbuff;			ptr->buff = (unsigned char*)memdup(*pbuff, ptr->size, 0);
+		if(0 > NewItem(UNDO_TEXTBUF, flags, parent, ptr, (void**)pbuff)) {
+			if(ptr->buff) free(ptr->buff);			free(ptr);
+			}
+		}
+}
+
+int
+UndoObj::NewItem(int cmd, DWORD flags, GraphObj *owner, void *data, void **loc)
+{
+	UndoInfo *tmp;
+	int idx;
+
+	if(!buff || !cdisp) return -1;
+	if(!(tmp = (UndoInfo *)malloc(sizeof(UndoInfo))))return -1;
+	tmp->cmd = cmd;			tmp->flags = flags;
+	tmp->owner = owner;		tmp->data = data;
+	tmp->loc = loc;
+	tmp->zd.org.fx = cdisp->VPorg.fx;
+	tmp->zd.org.fy = cdisp->VPorg.fy;
+	tmp->zd.scale = cdisp->VPscale;
+	idx = (*pcb & UNDO_IDX_MASK);
+	if(buff[idx]) FreeInfo(&buff[idx]);
+	buff[idx] = tmp;
+	(*pcb)++;
+	return idx;
+}
+
+void
+UndoObj::FreeInfo(UndoInfo** inf)
+{
+	int i;
+	UndoList *ul;
+	GraphObj *go, **gol;
+
+	if(!inf || !(*inf)) return;
+	switch((*inf)->cmd) {
+	case UNDO_SETGO:
+		break;
+	case UNDO_DATA:
+		delete ((StrData*)((*inf)->data));
+		break;
+	case UNDO_ET:
+		if(((EtBuff*)((*inf)->data))->txt) free(((EtBuff*)((*inf)->data))->txt);
+		free((*inf)->data);
+		break;
+	case UNDO_TEXTBUF:
+		if(((TextBuff*)((*inf)->data))->buff) free(((TextBuff*)((*inf)->data))->buff);
+		free((*inf)->data);
+		break;
+	case UNDO_MUTATE:
+	case UNDO_DEL_GO:
+		go = (GraphObj*)((*inf)->data);
+		(*inf)->data = 0L;		::DeleteGO(go);
+		break;
+	case UNDO_DROPGOLIST:
+		if((ul = (UndoList *)((*inf)->data)) && (ul->array)) {
+			gol = (GraphObj**)(ul->array);
+			for (i = 0; i < ul->count; i++) if(gol[i]) ::DeleteGO(gol[i]);
+			free(ul->array);				free(ul);
+			}
+		break;
+	case UNDO_GOLIST:	case UNDO_MEM:
+		if((ul = (UndoList *)((*inf)->data)) && (ul->array)) {
+			free(ul->array);				free(ul);
+			}
+		break;
+	case UNDO_AXISDEF:
+		if(((AxisDEF*)(*inf)->data)->breaks) free(((AxisDEF*)(*inf)->data)->breaks);
+		free((*inf)->data);
+		break;
+	case UNDO_TEXTDEF:
+		if(((TextDEF*)(*inf)->data)->text) free(((TextDEF*)(*inf)->data)->text);
+		free((*inf)->data);
+		break;
+	case UNDO_DROPMEM:		case UNDO_VALDWORD:		case UNDO_VALINT:
+	case UNDO_OBJCONF:		case UNDO_OBJCONF_1:	case UNDO_LFP:
+	case UNDO_MOVE:			case UNDO_RECT:			case UNDO_STRING:
+	case UNDO_ROTDEF:		case UNDO_LINEDEF:		case UNDO_FILLDEF:
+	case UNDO_LFP3D:		case UNDO_FLOAT:		case UNDO_SAVVAR:
+	case UNDO_POINT:		case UNDO_VALLONG:
+		free((*inf)->data);
+		break;
+		}
+	free(*inf);
+	*inf = 0L;
+}
+
+class UndoUtil:public GraphObj {
+public:
+	GraphObj *res;
+
+	UndoUtil(GraphObj *p, GraphObj *old):GraphObj(0L, 0L){root = p; optr = old; res = 0L;};
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+
+private:
+	GraphObj *root, *optr;
+};
+
+bool
+UndoUtil::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	GraphObj *xch[2];
+
+	switch(cmd){
+	case CMD_DROP_GRAPH:
+		//we come here if conversion of undo-information is
+		//   successfully converted into a tree of graphic objects.
+		//   Now ask the parent object to replace the modified
+		//   object with a previous version (i.e. undo modifications).
+		xch[0] = optr;
+		xch[1] = res = (GraphObj*)tmpl;
+		if(root) return root->Command(CMD_REPL_GO, xch, o);
+		break;
+		}
+	return false;
+}
+
+void
+UndoObj::RestoreConf(UndoInfo *inf)
+{
+	UndoUtil *proc;
+	int i;
+
+	//Create a message object which will accept the translated graphic
+	//   object or tree of objects, and which will forward it to the parent
+	//   of the tree finalizing undo.
+	if(!inf->data) return;
+	if(!(proc = new UndoUtil(inf->owner, (GraphObj*)inf->loc))) return; 
+	OpenGraph(proc, 0L, (unsigned char *)inf->data, false);
+	if(proc->res) for(i = 0; i < UNDO_RING_SIZE; i++) {
+		if(buff[i] && buff[i]->owner == (GraphObj*)inf->loc) FreeInfo(&buff[i]);
+		if(buff[i] && buff[i]->cmd == UNDO_OBJCONF){
+			if(buff[i]->loc == inf->loc) buff[i]->loc = (void**)proc->res;
+			}
+		}
+	delete proc;
+}
+
+void
+UndoObj::RestoreData(UndoInfo *inf)
+{
+	DataObj *od;
+	StrData *nd;
+	int w, h;
+
+	if(!(nd = (StrData*)inf->data) || !(od = (DataObj*)inf->loc)){
+		if(nd) delete(nd);		return;
+		}
+	nd->GetSize(&w, &h);		od->ChangeSize(w, h, false);
+	nd->RestoreData(od);		delete(nd);
+}
+
+#undef UNDO_RING_SIZE
+#undef UNDO_IDX_MASK
diff --git a/Utils.cpp b/Utils.cpp
index ca15ccd..f83236a 100755
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -1,2494 +1,2528 @@
-//Utils.cpp, Copyright (c) 2000-2007 R.Lackner
-//Collection of utility functions and classes for RLPlot
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-#include <math.h>
-#include <stdlib.h>
-#include <stdio.h>
-#include <string.h>
-#include <errno.h>
-#include "rlplot.h"
-
-extern GraphObj *CurrGO;			//Selected Graphic Objects
-extern Label *CurrLabel;
-extern Default defs;
-extern UndoObj Undo;
-
-char TmpTxt[TMP_TXT_SIZE];
-
-//----------------------------------------------------------------------------
-// Get the rectanpular part of bitmap. Used for screen updates
-//----------------------------------------------------------------------------
-anyOutput *GetRectBitmap(RECT *rc, anyOutput *src)
-{
-	RECT cr;
-	anyOutput *bm;
-
-	if(!rc || !src) return 0L;
-	src->ActualSize(&cr);
-	if(rc->left < cr.left) rc->left = cr.left;
-	if(rc->right > cr.right) rc->right = cr.right;
-	if(rc->top < cr.top) rc->top = cr.top;
-	if(rc->bottom > cr.bottom) rc->bottom = cr.bottom;
-	if(rc->left == rc->right) return 0L;
-	if(rc->top == rc->bottom) return 0L;
-	if(!(bm = NewBitmapClass(rc->right - rc->left, rc->bottom - rc->top, 
-		src->hres, src->vres)))return 0L;
-	bm->CopyBitmap(0, 0, src, rc->left, rc->top, rc->right - rc->left, 
-		rc->bottom - rc->top, false);
-	return bm;
-}
-
-void RestoreRectBitmap(anyOutput **pmo, RECT *mrc, anyOutput *o)
-{
-	if(pmo && *pmo && o && mrc) {
-		o->CopyBitmap(mrc->left, mrc->top, *pmo, 0, 0, mrc->right - mrc->left, 
-			mrc->bottom - mrc->top, false);
-		DelBitmapClass(*pmo);		*pmo = 0L;
-		o->UpdateRect(mrc, false);
-		}
-}
-
-//----------------------------------------------------------------------------
-// Axis utility functions
-//----------------------------------------------------------------------------
-void NiceAxis(AxisDEF *axis, int nTick)
-{
-	double diff, logStep, Step, Magn, LoVal, HiVal;
-	int i;
-
-	axis->Start = axis->min;
-	axis->Step = (axis->max - axis->min)/((double)nTick);
-	diff = axis->max - axis->min;
-	if(axis->breaks) for(i = 0; i < axis->nBreaks; i++) {
-		diff -= fabs(axis->breaks[i].fy - axis->breaks[i].fx);
-		}
-	if(diff <= 0.0) return;
-	logStep = log10(diff/(double)nTick);
-	Magn = floor(logStep);			logStep -= Magn;
-	if(logStep > 0.8) Step = 10.0;
-	else if(logStep > 0.5) Step = 5.0;
-	else if(logStep > 0.2) Step = 2.0; 
-	else Step = 1.0;
-	Step *= pow(10.0, Magn);		HiVal = LoVal = Step * floor(axis->min/Step);
-	axis->max += (diff * 0.05);
-	while(HiVal < axis->max) HiVal += Step;
-	if((axis->flags & AXIS_LOG) == AXIS_LOG) {
-		if (LoVal > defs.min4log) axis->min = LoVal;
-		if ((LoVal + Step) > defs.min4log && (LoVal + Step) < axis->min) axis->min = LoVal+Step;
-		}
-	else axis->min = LoVal;
-	axis->max = HiVal;		axis->Start = axis->min;		axis->Step = Step;
-}
-
-void NiceStep(AxisDEF *axis, int nTick)
-{
-	double diff, d, logStep, Step, Magn;
-	int i;
-
-	diff = axis->max - axis->min;		d = axis->Step != 0.0 ? diff/axis->Step : HUGE_VAL;
-	if((d - floor(d)) < 0.1 && axis->Step != 0.0 && diff/axis->Step < 12.0)return;
-	if(axis->breaks) for(i = 0; i < axis->nBreaks; i++) {
-		diff -= fabs(axis->breaks[i].fy - axis->breaks[i].fx);
-		}
-	if(diff <= 0.0) return;
-	logStep = log10(diff/(double)nTick);
-	Magn = floor(logStep);				logStep -= Magn;
-	if(logStep > 0.8) Step = 10.0;
-	else if(logStep > 0.5) Step = 5.0;
-	else if(logStep > 0.2) Step = 2.0; 
-	else Step = 1.0;
-	Step *= pow(10.0, Magn);			axis->Step = Step;
-}
-
-double base4log(AxisDEF *axis, int direc)
-{
-	double lv, Step = 1.0, Magn;
-	int cmd;
-
-	switch (direc) {
-	case 0:		cmd = SIZE_BOUNDS_XMIN;		break;
-	case 1:		cmd = SIZE_BOUNDS_YMIN;		break;
-	case 2:		cmd = SIZE_BOUNDS_ZMIN;		break;
-	default:	return 1.0;
-		}
-	lv = axis->min > defs.min4log ? axis->min : defs.min4log;
-	if(lv <= defs.min4log) return defs.min4log;
-	lv = log10(lv);
-	lv -= (Magn = floor(lv));
-	if(lv > 0.301) Step = 2.0; 
-	if(lv > 0.699) Step = 5.0;
-	Step *= pow(10.0, Magn);
-	return Step > defs.min4log ? Step : 1.0;
-}
-
-double TransformValue(AxisDEF *axis, double val, bool transform)
-{
-	int i;
-	double f1, f2, RetVal = val;
-
-	if(!axis) return val;
-	if(axis->breaks) {
-		for (i = 0; i < axis->nBreaks; i++) {
-			f1 = axis->breaks[i].fx;	f2 = axis->breaks[i].fy;
-			if(val > f2) RetVal -= (f2-f1);
-			else if(val > f1 && val <= f2) RetVal -= (val-f1);
-			}
-		}
-	else (axis->nBreaks = 0);
-	if(transform) {
-		switch(axis->flags & 0x7000L) {
-		case AXIS_LINEAR:	break;
-		case AXIS_LOG:
-			if(axis->flags & AXIS_RADIAL) RetVal = fabs(RetVal);
-			RetVal = RetVal > defs.min4log ? log10(RetVal): log10(defs.min4log);
-			break;
-		case AXIS_RECI:		RetVal = RetVal > defs.min4log || RetVal < - defs.min4log ?
-				1.0/RetVal : 0.0;		break;
-		case AXIS_SQR:		RetVal = RetVal >= 0.0 ? sqrt(RetVal) : 0.0;					break;
-			}
-		}
-	return RetVal;
-}
-
-void SortAxisBreaks(AxisDEF *axis)
-{
-	int i, j;
-	double ftmp;
-	bool sorted;
-
-	if(!axis || !axis->nBreaks || !axis->breaks) return;
-	//low values first
-	for(i = 0; i < axis->nBreaks; i++) {
-		if(axis->breaks[i].fy < axis->breaks[i].fx) {
-			ftmp = axis->breaks[i].fx;
-			axis->breaks[i].fx = axis->breaks[i].fy;
-			axis->breaks[i].fy = ftmp;
-			}
-		}
-	//a simple bubble sort should do
-	if(axis->nBreaks >1) do {
-		sorted = true;
-		for(i = 1; i < axis->nBreaks; i++) {
-			if(axis->breaks[i-1].fx > axis->breaks[i].fx) {
-				ftmp = axis->breaks[i-1].fx;
-				axis->breaks[i-1].fx = axis->breaks[i].fx;
-				axis->breaks[i].fx = ftmp;
-				ftmp = axis->breaks[i-1].fy;
-				axis->breaks[i-1].fy = axis->breaks[i].fy;
-				axis->breaks[i].fy = ftmp;
-				sorted = false;
-				}
-			}
-		}while(!sorted);
-	//combine overlapping ranges
-	if((j = axis->nBreaks) >1) for(i = j = 1; i < axis->nBreaks; i++) {
-		if(axis->breaks[i].fx > axis->breaks[j-1].fy) {
-			axis->breaks[j].fx = axis->breaks[i].fx;
-			axis->breaks[j].fy = axis->breaks[i].fy;
-			j++;
-			}
-		else {
-			j--;
-			axis->breaks[j++].fy = axis->breaks[i].fy;
-			}
-		}
-	axis->nBreaks = j;
-}
-
-double GetAxisFac(AxisDEF *axis, double delta, int direc)
-{
-	double da, v1, v2;
-
-	switch(axis->flags & 0x7000L) {
-	case AXIS_LOG:		
-		axis->max = axis->max > defs.min4log ? log10(axis->max): log10(defs.min4log);
-		axis->min = axis->min > defs.min4log ? log10(axis->min): 
-			log10(base4log(axis, direc));
-		if(axis->max <= axis->min) axis->max = axis->min +1.0;
-		break;
-	case AXIS_RECI:
-		v1 = fabs(axis->min) >defs.min4log ? axis->min :
-			base4log(axis, direc);
-		if(fabs(v1) > defs.min4log) v1 = 1.0/v1;
-		else v1 = 1.0e+34;
-		if(fabs(axis->max) >defs.min4log) v2 = 1.0/axis->max;
-		else v2 = 0.0;
-		if(fabs(v2) < fabs(v1/10.0)) v2 = 0.0;
-		axis->min = v2;		axis->max = v1;
-		break;
-	case AXIS_SQR:
-		axis->max = axis->max > defs.min4log ? sqrt(axis->max) : 0.0;
-		axis->min = axis->min > defs.min4log ? sqrt(axis->min) : 0.0;
-		break;
-		}
-	v2 = TransformValue(axis, axis->max, false);	//process breaks
-	v1 = TransformValue(axis, axis->min, false);
-	da = v2 != v1 ? v2 - v1 : 1.0;
-	return delta / da;
-}
-
-//----------------------------------------------------------------------------
-// Text utility functions: internationalization and formats
-//----------------------------------------------------------------------------
-//remove leading/trailing whitespace
-char *str_ltrim(char *str) {
-	int i, j;
-
-	if(!str || !str[0]) return str;
-	for(i = 0; str[i] && str[i] <= ' '; i++);
-	for(j = 0; str[i]; str[j++] = str[i++]);
-	str[j++] = '\0';	return str;
-	}
-
-char *str_rtrim(char *str) {
-	size_t i;
-
-	i = strlen(str);
-	while(i > 0 && str[i-1] <= ' ') str[--i] = '\0';
-	return str;
-}
-
-char *str_trim(char *str) {
-	str = str_ltrim(str);			return str_rtrim(str);
-}
-
-//remove leading and tailing quotatation
-void rmquot(char *str)
-{
-	size_t i, len;
-	char c;
-
-	if(str && str[0] && (*str == '"' || *str == '\'')) {
-		len = strlen(str);		c = *str;
-		if(str[len-1] == c) {
-			str[len-1] = 0;
-			for(i = 1; i < len; str[i-1] = str[i++]);
-			}
-		}
-}
-
-int strpos(char *needle, char *haystack)
-{
-	int i, j;
-
-	if(!needle || !needle[0] || !haystack || !haystack[0]) return -1;
-	for(i = j = 0; haystack[i]; i++, j=0) {
-		if(haystack[i] == needle[0]) for (j = 1; haystack[i+j]; j++) {
-			if(needle[j] != haystack[i+j]) break; 
-			}
-		if(j && !needle[j]) return i;
-		}
-	return -1;
-}
-
-char *strreplace(char *needle, char *replace, char *haystack)
-{
-	static char *result = 0L;
-	static size_t reslen = 0;
-	size_t i, j, k, l;
-
-	if(!needle || !needle[0] || !haystack || !haystack[0]) return result;
-	if(!result) result = (char*)malloc(reslen = 100);
-	result[0] = 0;		l = strlen(needle);
-	for(i = j = k = 0; haystack[i]; i++, j=0) {
-		if(haystack[i] == needle[0]) for (j = 1; haystack[i+j]; j++) {
-			if(needle[j] != haystack[i+j]) break; 
-			}
-		if(j && !needle[j]) {
-			if(replace && replace[0]) {
-				if(reslen < (i + (int)strlen(replace) + 10)) {
-					result = (char*)realloc(result, reslen += 100);
-					}
-				for(j = 0; replace[j]; j++) result[k++] = replace[j];
-				}
-			i += (l-1);
-			}
-		else result[k++] = haystack[i];
-		}
-	result[k++] = 0;
-	return result;
-}
-
-char *substr(char *text, int pos1, int pos2)
-{
-	static char *result = 0L;
-	static size_t reslen = 0;
-	int i, j;
-	size_t l;
-
-	if(!text || !text[0]) return 0L;
-	l = strlen(text);
-	if(pos1 < 0) pos1 = 0;			if(pos2 < pos1) pos2 = (int)(l+1);
-	if(!result) result = (char*)malloc(reslen = 100);
-	while (reslen < l) result = (char*) realloc(result, reslen += 100);
-	for(i = 0; i < pos1 && text[i]; i++);
-	for(j = 0; i <= pos2 && text[i]; result[j++] = text[i++]);
-	result[j] = 0;
-	return result;
-}
-
-//copy string in src to dest, returning the stringlength of src
-//   seek better solution for long strings
-int rlp_strcpy(char*dest, int size, char*src)
-{
-	int i;
-
-	if(dest && src) {
-		for(i = 0; i < size; i++) {
-			if(!(dest[i] = src[i])) return i;
-			}
-		dest[i-1] = 0;
-		return i-1;
-		}
-	return 0;
-}
-
-// restyle formula
-void ReshapeFormula(char **text)
-{
-	int i, j, l;
-
-	if(!text || !*text || !**text) return;
-	l = (int)strlen(*text);
-	for(i = j = 0; i < l; i++) {
-		if((*text)[i] == ';') {
-			if((*text)[i+1] == ';' || (*text)[i+1] == '\n') i++;
-			}
-		TmpTxt[j++] = (*text)[i];
-		}
-	TmpTxt[j] = 0;
-	if(j && j <= l && TmpTxt[0]) {
-		rlp_strcpy(*text, l+1, TmpTxt);
-		}
-}
-
-//translate anyResult to output format
-void TranslateResult(anyResult *res)
-{
-	static char tr_text[80];
-
-	switch (res->type) {
-	case ET_VALUE:
-		if(res->value == HUGE_VAL) rlp_strcpy(tr_text, 80, "inf");
-		else if(res->value == -HUGE_VAL) rlp_strcpy(tr_text, 80, "-inf");
-#ifdef USE_WIN_SECURE
-		else sprintf_s(tr_text, 80, "%g", res->value);
-#else
-		else sprintf(tr_text, "%g", res->value);
-#endif
-		res->text = tr_text;				return;
-	case ET_BOOL:
-		rlp_strcpy(tr_text, 80, ((int)res->value) ? (char*)"true" : (char*)"false"); 
-		res->text = tr_text;				return;
-	case ET_DATE:
-		rlp_strcpy(tr_text, 80, value_date(res->value, defs.fmt_date)); 
-		res->text = tr_text;				return;
-	case ET_TIME:
-		rlp_strcpy(tr_text, 80, value_date(res->value, defs.fmt_time)); 
-		res->text = tr_text;				return;
-	case ET_DATETIME:
-		rlp_strcpy(tr_text, 80, value_date(res->value, defs.fmt_datetime)); 
-		res->text = tr_text;				return;
-	case ET_TEXT:
-		if(res->text && res->text[0])		return;
-		}
-	if(!(res->text)) res->text="";
-}
-
-//remove invalid tag combinations from string
-void CleanTags(char *txt, int *i1, int *i2, int *i3)
-{
-	char *no_tags[] = {"<b></b>", "</b><b>", "<b><b>", "</b></b>",
-		"<i></i>", "</i><i>", "<i><i>", "</i></i>", "<u></u>",
-		"</u><u>", "<u><u>", "</u></u>", "<sub></sub>",
-		"</sub><sub>", "<sup></sup>", "</sup><sup>", 
-		0L};
-	int i, j, k, l, w;
-	bool na;
-
-	for(i = j = 0; txt[i]; i++) {
-		if(txt[i] != '<') txt[j++] = txt[i];
-		else {
-			for(k = 0, na = true; no_tags[k]; k++) {
-				for(l=1; no_tags[k][l] && txt[i+l]; l++) 
-					if(no_tags[k][l] != txt[i+l]) break;
-				if(!no_tags[k][l]){
-					na = false;
-					i += ((w=(int)strlen(no_tags[k]))-1);
-					if(i1 && *i1 > i) *i1 -= w;
-					if(i2 && *i2 > i) *i2 -= w;
-					if(i3 && *i3 > i) *i3 -= w;
-					break;
-					}
-				}
-			if(na) txt[j++] = txt[i];
-			}
-		}
-	txt[j++] = 0;
-}
-
-//replace one character in string
-void ChangeChar(char *text, char c1, char c2)	//replace one char in string
-{
-	int i;
-
-	for(i = 0; text[i]; i++) if (text[i] == c1) text[i] = c2;
-}
-
-char *Int2Nat(char *text)	//format ASCII number to locale format
-{
-	int i;
-
-	for(i = 0; text[i]; i++) if (text[i] == '.') text[i] = defs.DecPoint[0];
-	return text;
-}
-
-char *Nat2Int(char *text)	//format locale number to intranational notation
-{
-	int i;
-
-	for(i = 0; text[i]; i++) if (text[i] == defs.DecPoint[0]) text[i] = '.';
-	return text;
-}
-
-void WriteNatFloatToBuff(char *buff, double val)
-{
-#ifdef USE_WIN_SECURE
-	sprintf_s(buff, 20, " %g", val);
-#else
-	sprintf(buff, " %g", val);
-#endif
-	Int2Nat(buff);
-}
-
-bool Txt2Flt(char *txt, double *val)
-{
-	char *tmp = 0L;
-
-	if(txt && txt[0] && val) {
-		if(!txt[1] && (txt[0] == defs.DecPoint[0] || txt[0] < '0' ||
-			txt[0] > '9'))return false;
-		if(txt && txt[0] && (tmp = (char*)memdup(txt, (int)strlen(txt)+1, 0))){
-			Nat2Int(tmp);
-			//the return value of sscanf only roughly identifies a number
-#ifdef USE_WIN_SECURE
-			if(!sscanf_s(tmp,"%lf", val)){
-#else
-			if(!sscanf(tmp,"%lf", val)){
-#endif
-				free(tmp);
-				return false;
-				}
-			free(tmp);
-			return true;
-			}
-		}
-	return false;
-}
-
-void RmTrail(char *txt)
-{
-	int i;
-
-	i = (int)strlen(txt);
-	while(i >0 && (txt[i-1] == '0' || txt[i-1] < 32)) txt[--i] = 0;
-	if(i > 1 && txt[i-1] == '.') txt[i-1] = 0;
-}
-
-double NiceValue(double fv)
-{
-	double sign = fv > 0.0f ? 1.0 : -1.0;
-	double fa = fabs(fv);
-	double magn = floor(log10(fa));
-	int i = iround(fa/pow(10.0, magn-1.0));
-
-	return sign*pow(10.0, magn-1.0) *(double)i;
-}
-
-char *NiceTime(double val)
-{
-	rlp_datetime dt;
-
-	parse_datevalue(&dt, val);
-	if(dt.year > 1905) {
-		if(dt.hours) return date2text(&dt, defs.fmt_datetime);
-		else return date2text(&dt, defs.fmt_date);
-		}
-	else return date2text(&dt, defs.fmt_time);
-}
-
-char *Int2ColLabel(int nr1, bool uc)
-{
-	static char RetTxt[12];
-	int i, j, nr = nr1;
-	char base = uc ? 'A' : 'a';
-
-#ifdef USE_WIN_SECURE
-	sprintf_s(RetTxt+8, 4, "%c\0", base + (nr %26));
-#else
-	sprintf(RetTxt+8, "%c\0", base + (nr %26));
-#endif
-	nr /= 26;
-	for (i = 7; nr && i>=0; i--) {
-		j = nr %27;
-		RetTxt[i] = base + (j ? j-1 : j);
-		if (nr == 26) nr = 0;
-		else nr = nr/26;
-		}
-	return RetTxt+i+1;
-}
-
-char *mkCellRef(int row, int col)
-{
-	static char RetTxt[20];
-
-#ifdef USE_WIN_SECURE
-	sprintf_s(RetTxt, 20, "%s%d", Int2ColLabel(col, false), row+1);
-#else
-	sprintf(RetTxt, "%s%d", Int2ColLabel(col, false), row+1);
-#endif
-	return RetTxt;
-}
-
-char *mkRangeRef(int r1, int c1, int r2, int c2)
-{
-	static char RetTxt[40];
-	int cb;
-
-	cb = rlp_strcpy(RetTxt, 20, mkCellRef(r1, c1)); 
-	RetTxt[cb++] = ':';
-	rlp_strcpy(RetTxt+cb, 40-cb, mkCellRef(r2, c2));
-	return RetTxt;
-}
-//convert strings to XML specifications
-//this offers a limited support for special characters
-char *str2xml(char *str, bool bGreek)
-{
-	int i, j;
-	wchar_t wc;
-
-	if(!str) return 0L;
-	for(i = j = 0; str[i] && j < 4090; i++) {
-		switch(str[i]) {
-		case '"':
-			j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, """);
-			break;
-		case '&':
-			j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, "&");
-			break;
-		case '<':
-			j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, "<");
-			break;
-		case '>':
-			j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, ">");
-			break;
-		default:
-#ifdef USE_WIN_SECURE
-			if(bGreek && str[i] >= 'A' && str[i] <= 'Z') j += sprintf_s(TmpTxt+j, 20, "&#%d;", str[i] - 'A' + 0x391);
-			else if(bGreek && str[i] >= 'a' && str[i] <= 'z') j += sprintf_s(TmpTxt+j, 20, "&#%d;", str[i] - 'a' + 0x3B1);
-#else
-			if(bGreek && str[i] >= 'A' && str[i] <= 'Z') j += sprintf(TmpTxt+j, "&#%d;", str[i] - 'A' + 0x391);
-			else if(bGreek && str[i] >= 'a' && str[i] <= 'z') j += sprintf(TmpTxt+j, "&#%d;", str[i] - 'a' + 0x3B1);
-#endif
-			else if((unsigned char)str[i] <= 127) TmpTxt[j++]=str[i];
-			else {
-				if(mbtowc(&wc, str+i, 1) >0) 
-#ifdef USE_WIN_SECURE
-					j += sprintf_s(TmpTxt+j, TMP_TXT_SIZE-j, "&#%d;", ((unsigned short)wc));
-#else
-					j += sprintf(TmpTxt+j, "&#%d;", ((unsigned short)wc));
-#endif
-				}
-			}
-		}
-	TmpTxt[j] = 0;
-	return(TmpTxt);
-}
-
-// split string str into array of strings using sep as separator
-// return number of lines created in nl
-static char  *split_buf = 0L;
-static int split_buf_size, split_buf_pos;
-char **split(char *str, char sep, int *nl)
-{
-	int i, j, l, ns;
-	char **ptr;
-
-	if(!str || !str[0] || !sep) return 0L;
-	split_buf_pos = 0;
-	add_to_buff(&split_buf, &split_buf_pos, &split_buf_size, str, 0);
-	if(!split_buf || !split_buf_pos) return 0L;
-	for(i = ns = 0; i < split_buf_pos; i++) if(split_buf[i] == sep) ns++;
-	if(!(ptr = (char**)calloc(ns+2, sizeof(char*)))) return 0L;
-	for(i = j = l = 0; i < split_buf_pos; i++) {
-		if(split_buf[i] == sep) {
-			split_buf[i] = 0;
-			ptr[l++] = (char*)memdup(split_buf+j, (int)strlen(split_buf+j)+1, 0);
-			j = i+1;
-			}
-		}
-	ptr[l++] = (char*)memdup(split_buf+j, (int)strlen(split_buf+j)+1, 0);
-	if(nl) *nl = l;
-	return ptr;
-}
-
-char *fit_num_rect(anyOutput *o, int max_width, char *num_str)
-{
-	int i, j, k, w, h, slen;
-	char mant[30], expo[30], fmt[20];
-	double num;
-
-	o->oGetTextExtent(num_str, slen = (int)strlen(num_str), &w, &h);
-	if(w < (max_width-5)) return num_str;
-	//first try to remove leading zero from exponent
-	for(i = 0; i < slen; i++) if(num_str[i] == 'e') break;
-	if(num_str[i] == 'e') while (num_str[i+2] == '0') {
-		for(j = i+2; num_str[j]; j++) num_str[j] = num_str[j+1];
-		o->oGetTextExtent(num_str, slen = (int)strlen(num_str), &w, &h);
-		if(w < (max_width-5)) return num_str;
-		}
-	//no success: reduce the number of digit by rounding
-	for(i = k = 0; i <= slen; i++){
-		if(num_str[i] == '.') k = i;
-		if((mant[i] = num_str[i]) == 'e' || mant[i] == 0) break;
-		}
-	if(num_str[i] =='e') mant[i] = 0;						k = i - k-1;
-	for(j = 0; num_str[i]; j++, i++) expo[j] = num_str[i];		expo[j] = 0;
-#ifdef USE_WIN_SECURE
-	sscanf_s(mant, "%lf", &num);
-#else
-	sscanf(mant, "%lf", &num);	
-#endif
-	if(k >0) do {
-#ifdef USE_WIN_SECURE
-		sprintf_s(fmt, 20, "%%.%dlf%s", k, expo);
-		slen = sprintf_s(num_str, 60, fmt, num);		//num_str is much longer than 60
-#else
-		sprintf(fmt, "%%.%dlf%s", k, expo);
-		slen = sprintf(num_str, fmt, num);
-#endif
-		k--;
-		o->oGetTextExtent(num_str, slen, &w, &h);
-		if(w < (max_width-5)) return num_str;
-		}while (k >= 0);
-	//cannot fit: return hash marks instead
-	for(i = w = 0; w < (max_width-5) && i < 11; i++) {
-		rlp_strcpy(num_str, i+2, "##########");
-		o->oGetTextExtent(num_str, i+1, &w, &h);
-		}
-	num_str[i-1] = 0;		return num_str;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// utilities to add a line or number to a text buffer: memory file
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void add_to_buff(char** dest, int *pos, int *csize, char *txt, int len)
-{
-	if(!len)len = (int)strlen(txt);
-	if(!len) return;
-	if((*pos+len+1)>= *csize) {
-		*csize += 1000;
-		while(*csize < (*pos+len+1)) *csize += 1000;
-		*dest = (char*)realloc(*dest, *csize);
-		}
-	if(*dest) {
-		*pos += rlp_strcpy(*dest+*pos, len+1, txt);
-		}
-}
-
-void add_int_to_buff(char** dest, int *pos, int *csize, int value, bool lsp, int ndig)
-{
-	int len;
-	char txt[40];
-
-#ifdef USE_WIN_SECURE
-	len = sprintf_s(txt, 40, lsp ? " %d" : "%d", value);
-#else
-	len = sprintf(txt, lsp ? " %d" : "%d", value);
-#endif
-	add_to_buff(dest, pos, csize, txt, len);
-}
-
-void add_dbl_to_buff(char** dest, int *pos, int *csize, double value, bool lsp)
-{
-	int len;
-	char txt[40];
-
-#ifdef USE_WIN_SECURE
-	len = sprintf_s(txt, 40, lsp ? " %g" : "%g", value);
-#else
-	len = sprintf(txt, lsp ? " %g" : "%g", value);
-#endif
-	add_to_buff(dest, pos, csize, txt, len);
-}
-
-void add_hex_to_buff(char** dest, int *pos, int *csize, DWORD value, bool lsp)
-{
-	int len;
-	char txt[40];
-
-	if(value) {
-#ifdef USE_WIN_SECURE
-		len = sprintf_s(txt, 40, lsp ? " 0x%08x" : "0x%08x", value);
-#else
-		len = sprintf(txt, lsp ? " 0x%08x" : "0x%08x", value);
-#endif
-		add_to_buff(dest, pos, csize, txt, len);
-		}
-	else if(lsp) add_to_buff(dest, pos, csize, " 0x0", 4);
-	else add_to_buff(dest, pos, csize, "0x0", 3);
-}
-
-//----------------------------------------------------------------------------
-// bounding rectangle utilities
-//----------------------------------------------------------------------------
-void SetMinMaxRect(RECT *rc, int x1, int y1, int x2, int y2)
-{
-	if(x1 > x2) {
-		rc->left = x2;		rc->right = x1;
-		}
-	else {
-		rc->left = x1;		rc->right = x2;
-		}
-	if(y1 > y2) {
-		rc->top = y2;		rc->bottom = y1;
-		}
-	else {
-		rc->top = y1;		rc->bottom = y2;
-		}
-}
-
-void UpdateMinMaxRect(RECT *rc, int x, int y)
-{
-	if(x < rc->left) rc->left = x;
-	if(x > rc->right) rc->right = x;
-	if(y < rc->top) rc->top = y;
-	if(y > rc->bottom) rc->bottom = y;
-}
-
-void IncrementMinMaxRect(RECT *rc, int i)
-{
-	rc->left -= i;			rc->right += i;
-	rc->top -= i;			rc->bottom += i;
-}
-
-bool IsInRect(RECT *rc, int x, int y)
-{
-	if(x < rc->left || x > rc->right || 
-		y < rc->top || y > rc->bottom) return false;
-	return true;
-}
-
-//----------------------------------------------------------------------------
-// test if point (e.g. mouse position) is close to or iside a shape
-//----------------------------------------------------------------------------
-bool IsCloseToLine(POINT *p1, POINT *p2, int x, int y)
-{
-	bool bFound = false;
-	int tmp, dx, dy;
-
-	//do not process single point
-	if(p1->x == p2->x && p1->y == p2->y) return false;
-	//point must be bracketed by p1:p2
-	if((x-2) > p1->x && (x-2) > p2->x) return false;
-	if((x+2) < p1->x && (x+2) < p2->x) return false;
-	if((y-2) > p1->y && (y-2) > p2->y) return false;
-	if((y+2) < p1->y && (y+2) < p2->y) return false;
-	if(abs(dx = (p2->x - p1->x)) < abs(dy = (p2->y - p1->y))) {	//y dominant
-		tmp = (p1->x + ((y - p1->y) * dx)/dy);
-		if(x > (tmp-3) && x < (tmp+3)) bFound = true;
-		}
-	else {							// x dominant
-		tmp = (p1->y + ((x - p1->x) * dy)/dx);
-		if(y > (tmp-3) && y < (tmp+3)) bFound = true;
-		}
-	return bFound;
-}
-
-bool IsCloseToPL(POINT p, POINT *pts, int cp)
-{
-	int i;
-
-	for( i = 1; i < cp; i++) if(IsCloseToLine(pts+i-1, pts+i, p.x, p.y)) return true;
-	return false;
-}
-
-//test if poitn p is within the polygon pts of size cp
-//note: the last point of the polypon should be equal to the first point,
-//   i.e. the polygon must be closed
-int idx_point_on_line;
-bool IsInPolygon(POINT *p, POINT *pts, int cp)
-{
-	int tmp1 = 0, tmp2 = 0, tmp3, i;
-
-	for(i = 1; i < cp; i++) {
-		if((pts[i-1].x <= p->x && pts[i].x > p->x) || (pts[i-1].x > p->x && pts[i].x <= p->x)) {
-			tmp3 = ((p->x - pts[i-1].x)*(pts[i].y -pts[i-1].y))/(pts[i].x - pts[i-1].x);
-			tmp3 += pts[i-1].y;
-			if(p->y > tmp3) tmp1++;
-			else if(p->y < tmp3) tmp2++;
-			else return false;			//ignore points on the line
-			}
-		}
-	return((tmp1 & 0x1) && (tmp2 & 0x1));
-}
-
-bool IsInPolygon3D(double x, double y, POINT3D *pts, int cp, int *us, int *ls)
-{
-	int tmp1 = 0, tmp2 = 0, i, idx1, idx2;
-	double tmp3;
-
-	for(i = 1, idx1 = idx2 = -1; i < cp; i++) {
-		if((pts[i-1].x <= x && pts[i].x > x) || (pts[i-1].x > x && pts[i].x <= x)) {
-			tmp3 = ((x - pts[i-1].x)*(pts[i].y -pts[i-1].y))/(pts[i].x - pts[i-1].x);
-			tmp3 += pts[i-1].y;
-			if(y > tmp3){
-				tmp1++;				idx1 = i;
-				}
-			else if(y < tmp3) {
-				tmp2++;				idx2 = i;
-				}
-			else {			//points is on the line
-				idx1 = idx2 = idx_point_on_line = i;
-				if(us) *us = idx1;		if(ls) *ls = idx2;
-				return true;
-				}
-			}
-		}
-	//return an index to the bracketing sgments of the polygon
-	if(us) *us = idx1;			if(ls) *ls = idx2;
-	return((tmp1 & 0x1) && (tmp2 & 0x1));
-}
-
-//return true if two rectangles overlap
-bool OverlapRect(RECT *rc1, RECT *rc2)
-{
-	if((rc1->left < rc2->right && rc1->right > rc2->left) ||
-		(rc2->left < rc1->right && rc2->right > rc1->left)) {
-		if((rc1->top < rc2->bottom && rc1->bottom > rc2->top) ||
-			(rc2->top < rc1->bottom && rc2->bottom > rc1->top))
-			return true;
-		}
-	return false;
-}
-
-//----------------------------------------------------------------------------
-// collect points to a polygon
-// keep number of points low by extrapolation
-//----------------------------------------------------------------------------
-void AddToPolygon(long *cp, POINT *pts, POINT *np)
-{
-	int ix, iy;
-	long i = *cp;
-
-	if(i && pts[i-1].x == np->x && pts[i-1].y == np->y) return;
-	if(i < 2) {					//accept first points of polygon
-		pts[i].x = np->x;	pts[i].y = np->y;	*cp = i+1;
-		return;
-		}
-	if(pts[i-1].x == pts[i-2].x && pts[i-1].x == np->x) {
-		if(np->y == pts[i-1].y) return;
-		if((np->y > pts[i-1].y && pts[i-1].y > pts[i-2].y) ||
-			(np->y < pts[i-1].y && pts[i-1].y < pts[i-2].y)) {
-			pts[i-1].x = np->x;		pts[i-1].y = np->y;
-			return;
-			}
-		}
-	if(pts[i-1].y == pts[i-2].y && pts[i-1].y == np->y) {
-		if(np->x == pts[i-1].x) return;
-		if((np->x > pts[i-1].x && pts[i-1].x > pts[i-2].x) ||
-			(np->x < pts[i-1].x && pts[i-1].x < pts[i-2].x)) {
-			pts[i-1].x = np->x;		pts[i-1].y = np->y;
-			return;
-			}
-		}
-	//try linear extrapolation
-	if((pts[i-1].x > pts[i-2].x && np->x > pts[i-1].x) ||
-		(pts[i-1].x < pts[i-2].x && np->x < pts[i-1].x)) {
-		ix = (pts[i-1].y != pts[i-2].y) ? (pts[i-2].x + ((np->y - pts[i-2].y) * 
-			(pts[i-1].x - pts[i-2].x))/(pts[i-1].y - pts[i-2].y)) : 0;
-		iy = (pts[i-1].x != pts[i-2].x) ? (pts[i-2].y + ((np->x - pts[i-2].x) * 
-			(pts[i-1].y - pts[i-2].y))/(pts[i-1].x - pts[i-2].x)) : 0;
-		if((ix && ix == np->x) && (iy && iy == np->y)) {
-			pts[i-1].x = np->x;		pts[i-1].y = np->y;
-			return;
-			}
-		}
-	//not explained by extrapolation, accept new point
-	pts[i].x = np->x;				pts[i].y = np->y;
-	*cp = i+1;
-	return;
-}
-
-//----------------------------------------------------------------------------
-// create a Bezier polygon
-void DrawBezier(long *cp, POINT *pts, POINT p0, POINT p1, POINT p2, POINT p3, int depth)
-{
-#define MIN_SEG 11
-#define MAX_DEPTH 5
-	int i;
-	POINT np, p01, p12, p23, p012, p123, p0123;
-	POINT *ap[] = {&p0, &p1, &p2, &p3};
-
-	depth ++;
-	if(depth > MAX_DEPTH) {
-		for(i= 0; i < 4; i++) {
-			np.x = (*ap[i]).x >> 2;		np.y = (*ap[i]).y >> 2;
-			AddToPolygon(cp, pts, &np);
-			}
-		return;
-		}
-	else if(depth == 1) for(i=0; i < 4; i++) {
-		(*ap[i]).x <<= 2;			(*ap[i]).y <<= 2;
-		}
-	p01.x = (p0.x + p1.x) >> 1;				p01.y = (p0.y + p1.y) >> 1;
-	p12.x = (p1.x + p2.x) >> 1;				p12.y = (p1.y + p2.y) >> 1;
-	p23.x = (p2.x + p3.x) >> 1;				p23.y = (p2.y + p3.y) >> 1;
-	p012.x = (p01.x + p12.x) >> 1;			p012.y = (p01.y + p12.y) >> 1;
-	p123.x = (p12.x + p23.x) >> 1;			p123.y = (p12.y + p23.y) >> 1;
-	p0123.x = (p012.x + p123.x) >> 1;		p0123.y = (p012.y + p123.y) >> 1;
-	if(abs(p0.x - p0123.x)> MIN_SEG || abs(p0.y - p0123.y)> MIN_SEG) {
-		DrawBezier(cp, pts, p0, p01, p012, p0123, depth);		//recursion: refine
-		}
-	else {
-		DrawBezier(cp, pts, p0, p01, p012, p0123, MAX_DEPTH);	//recursion: store data
-		}
-	if(abs(p3.x - p0123.x)> MIN_SEG || abs(p3.y - p0123.y)> MIN_SEG) {
-		DrawBezier(cp, pts, p0123, p123, p23, p3, depth);		//recursion: refine
-		}
-	else {
-		DrawBezier(cp, pts, p0123, p123, p23, p3, MAX_DEPTH);	//recursion: store data
-		}
-#undef MAX_DEPTH
-#undef MIN_SEG
-}
-
-//----------------------------------------------------------------------------
-// create a Bezier spline through data points
-static void ipol_curve(lfPOINT *p1, lfPOINT *p2, lfPOINT *p3, lfPOINT *cp1, lfPOINT *cp2)
-{
-	double dx, dy, l;
-	lfPOINT tHat;
-
-	tHat.fx = p3->fx - p1->fx;		tHat.fy = p3->fy - p1->fy;
-	l = sqrt(tHat.fx * tHat.fx + tHat.fy * tHat.fy);
-	tHat.fx /= l;					tHat.fy /= l;
-	cp1->fx = cp2->fx = p2->fx;		cp1->fy = cp2->fy = p2->fy;
-	dx = p3->fx - p2->fx;			dy = p3->fy - p2->fy;		l = sqrt(dx*dx + dy*dy)/3.0;
-	cp2->fx += (tHat.fx * l);		cp2->fy += (tHat.fy *l);
-	dx = p2->fx - p1->fx;			dy = p2->fy - p1->fy;		l = sqrt(dx*dx + dy*dy)/3.0;
-	cp1->fx -= (tHat.fx * l);		cp1->fy -= (tHat.fy *l);
-}
-
-static void mirr_vecvec(lfPOINT *a0, lfPOINT *a1, lfPOINT *v1)
-{
-	double dx, dy, as, ac, vs, vc, s, c, l;
-	lfPOINT sol1, sol2;
-
-	//mirror vector a0 -> v1 by rotation around vector a0 -> a1
-	dx = a1->fx - a0->fx;					dy = a1->fy - a0->fy;
-	l = sqrt(dx*dx + dy*dy);	as = dy/l;			ac = dx/l;
-	dx = v1->fx - a0->fx;					dy = v1->fy - a0->fy;
-	l = sqrt(dx*dx + dy*dy);	vs = dy/l;			vc = dx/l;
-	//calculate cw and ccw solution
-	s = sin((asin(as)-asin(vs))*2.0);		c = cos((acos(ac)-acos(vc))*2.0);
-	sol1.fx = dx * c + dy * s + a0->fx;		sol1.fy = dy * c - dx * s + a0->fy;
-	s = sin((asin(as)-asin(-vs))*2.0);		c = cos((acos(ac)-acos(-vc))*2.0);
-	sol2.fx = dx * c + dy * s + a0->fx;		sol2.fy = dy * c - dx * s + a0->fy;
-	//get better solution
-	dx = sol1.fx - a1->fx;					dy = sol1.fy - a1->fy;
-	l = sqrt(dx*dx + dy*dy);
-	dx = sol2.fx - a1->fx;					dy = sol2.fy - a1->fy;
-	if( l < sqrt(dx*dx + dy*dy)) {
-		v1->fx = sol1.fx;					v1->fy = sol1.fy;
-		}
-	else {
-		v1->fx = sol2.fx;					v1->fy = sol2.fy;
-		}
-}
-
-int mkCurve(lfPOINT *src, int n1, lfPOINT **dst, bool bClosed)
-{
-	int i, j, iret;
-
-	if(!src || n1 < 3) return 0;
-	if(!(*dst = (lfPOINT*)malloc((n1*3+2) * sizeof(lfPOINT))))return 0;
-	for(i = j = iret = 0; i < n1; i++, j += 3) {
-		(*dst)[j].fx = src[i].fx;		(*dst)[j].fy = src[i].fy;
-		if(i && i < (n1-1)){
-			ipol_curve(&src[i-1], &src[i], &src[i+1], *dst+j-1, *dst+j+1);
-			}
-		else if(i) {
-			iret = j+1;					break;
-			}
-		}
-	if(bClosed && iret >2) {
-		if((*dst)[j].fx != (*dst)[0].fx || (*dst)[j].fy != (*dst)[0].fy) {
-			j += 3;								iret += 3;
-			(*dst)[j].fx = (*dst)[0].fx;		(*dst)[j].fy = (*dst)[0].fy;	
-			}
-		if(j < 6) {
-			free(*dst);		*dst = 0L;				return 0;
-			}
-		ipol_curve(*dst+j-3, *dst+j, *dst+3, *dst+j-1, *dst+1);
-		ipol_curve(*dst+j-6, *dst+j-3, *dst+j, *dst+j-4, *dst+j-2);
-		}
-	else if(!bClosed && iret>3) {
-		(*dst)[1].fx = (*dst)[0].fx + (*dst)[3].fx - (*dst)[2].fx;
-		(*dst)[1].fy = (*dst)[0].fy + (*dst)[3].fy - (*dst)[2].fy;
-		mirr_vecvec(*dst, *dst+3, *dst+1);
-		(*dst)[j-1].fx = (*dst)[j].fx + (*dst)[j-3].fx - (*dst)[j-2].fx;
-		(*dst)[j-1].fy = (*dst)[j].fy + (*dst)[j-3].fy - (*dst)[j-2].fy;
-		mirr_vecvec(*dst+j, *dst+j-3, *dst+j-1);
-		}
-	else {
-		free(*dst);			*dst = 0L;				return 0;
-		}
-	return iret;
-}
-
-//----------------------------------------------------------------------------
-// create a circular polygon
-//use circular Bresenham's algorithm to draw arcs
-//Ref: C. Montani, R. Scopigno (1990) "Speres-To-Voxel Conversion", in:
-//   Graphic Gems (A.S. Glassner ed.) Academic Press, Inc.; 
-//   ISBN 0-12-288165-5 
-//----------------------------------------------------------------------------
-POINT *MakeArc(int ix, int iy, int r, int qad, long *npts)
-{
-	int i, x, y, di, de, lim;
-	static POINT *pts, *rpts;
-	POINT np;
-
-	if(r < 1) return 0L;
-	if(!(pts = (POINT*) malloc((r+1)*8*sizeof(POINT))))return 0L;
-	for(i = *npts = 0; i < 4; i++) {
-		x = lim = 0;	y = r;	di = 2*(1-r);
-		if(qad & (1<<i))while (y >= lim){
-			if(di < 0) {
-				de = 2*di + 2*y -1;
-				if(de > 0) {
-					x++;	y--;	di += (2*x -2*y +2);
-					}
-				else {
-					x++;	di += (2*x +1);
-					}
-				}
-			else {
-				de = 2*di -2*x -1;
-				if(de > 0) {
-					y--;	di += (-2*y +1);
-					}
-				else {
-					x++;	y--;	di += (2*x -2*y +2);
-					}
-				}
-			switch(i) {
-			case 0:	np.x = ix-y;	np.y = iy+x;	break;
-			case 1: np.x = ix+x;	np.y = iy+y;	break;
-			case 2: np.x = ix+y;	np.y = iy-x;	break;
-			case 3:	np.x = ix-x;	np.y = iy-y;	break;
-				}
-			AddToPolygon(npts, pts, &np);
-			}
-		}
-	if(*npts < 3) return 0L;
-	if(rpts = (POINT*)realloc(pts, sizeof(POINT)*(*npts+4))) return rpts;
-	return pts;
-}
-
-//----------------------------------------------------------------------------
-// display a marked polygon by displaying it with complement colors
-//----------------------------------------------------------------------------
-void InvertPolygon(POINT *pts, int nPts, LineDEF *Line, FillDEF *Fill, RECT *rc, 
-	anyOutput *o, bool mark)
-{
-	LineDEF *FillLine;
-
-	if(!Line || !Fill || !rc || !o) return;
-	FillLine = Fill->hatch;
-	if(mark) {
-		if(FillLine)FillLine->color ^= 0x00ffffffL;
-		Fill->color ^= 0x00ffffffL;
-		Line->color ^= 0x00ffffffL;
-		o->SetLine(Line);
-		o->SetFill(Fill);
-		o->oPolygon(pts, nPts);
-		Fill->color ^= 0x00ffffffL;
-		if(FillLine)FillLine->color ^= 0x00ffffffL;
-		Line->color ^= 0x00ffffffL;
-		InvertLine(pts, nPts, Line, 0L, o, mark);
-		}
-	else {
-		InvertLine(pts, nPts, Line, 0L, o, mark);
-		o->SetLine(Line);
-		o->SetFill(Fill);
-		o->oPolygon(pts, nPts);
-		}
-	if(rc) o->UpdateRect(rc, false);
-}
-
-//----------------------------------------------------------------------------
-// display a marked line using complement colors
-//----------------------------------------------------------------------------
-void InvertLine(POINT *pts, int nPts, LineDEF *Line, RECT *rc, 
-	anyOutput *o, bool mark)
-{
-	int i;
-	LineDEF OldLine;
-
-	memcpy(&OldLine, Line, sizeof(LineDEF));
-	if(OldLine.width <= 0.0001) OldLine.width = 0.0001;
-	for(i = 0; o->un2fiy(OldLine.width) < 1.0 && i < 50; i++) OldLine.width *= 2.0;
-	OldLine.color = mark ? Line->color : 0x00ffffffL;
-	OldLine.width *= 3.0;				o->SetLine(&OldLine);
-	o->oPolyline(pts, nPts);			OldLine.width = Line->width;
-	OldLine.color = mark ? Line->color ^ 0x00ffffffL : Line->color;
-	o->SetLine(&OldLine);				o->oPolyline(pts, nPts);
-	if(rc) o->UpdateRect(rc, false);
-}
-
-//----------------------------------------------------------------------------
-// color utilitis
-//----------------------------------------------------------------------------
-// calculate distance between two colors
-unsigned int ColDiff(DWORD col1, DWORD col2)
-{
-	int ret = 0, d;
-
-	d = (col1 & 0xff) - (col2 & 0xff);
-	ret = isqr(d*d);
-	d = ((col1>>8) & 0xff) - ((col2>>8) & 0xff);
-	ret += isqr(d*d);
-	d = ((col1>>16) & 0xff) - ((col2>>16) & 0xff);
-	ret += isqr(d*d);
-	return ret;	
-}
-
-//----------------------------------------------------------------------------
-// interpolate between two colors
-DWORD IpolCol(DWORD color1, DWORD color2, double fact)
-{
-	DWORD col1, col2, col3, c1;
-	int i;
-
-	col1 = color1;	col2 = color2;	col3 = 0x0L;
-	for(i = 0; i < 3; i++) {
-		c1 = iround(fabs(((col1 & 0xff0000)>>16) * fact));
-		c1 += iround(fabs(((col2 & 0xff0000)>>16) *(1.0-fact)));
-		col3 |= c1 < 0xff ? c1 : 0xff;
-		col1 <<= 8;	col2 <<= 8; 
-		if(i < 2) col3 <<= 8;
-		}
-	return col3;
-}
-
-//----------------------------------------------------------------------------
-// Random number generator with low sequential correlations.
-// ran2 returns a number betwee 0.0f and 1.0f;
-// Ref: W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Veterling
-// Numerical Recipe in C, The Art of Scientific Computing
-// Cambridge University Press 1988, ISBN 0-521-35465-X
-//----------------------------------------------------------------------------
-#define M 714025
-#define IA 1366
-#define IC 150889
-double ran2(long *idum)
-{
-	static long iy, ir[98];
-	static int iff = 0;
-	int j;
-
-	if(*idum < 0 || iff == 0) {
-		iff = 1;
-		if((*idum = (IC-(*idum)) % M) < 0) *idum = -(*idum);
-		for(j = 1; j <= 97; j++) {
-			*idum = (IA*(*idum)+IC) % M;
-			ir[j] = (*idum);
-			}
-		*idum=(IA*(*idum)+IC) % M;
-		iy=(*idum);
-		}
-	j = 1+97 * iy/M;
-	if(j > 97 || j< 1) return 0.0f;		//impossible
-	iy = ir[j];
-	*idum = (IA*(*idum)+IC) % M;
-	ir[j] = (*idum);
-	return (float) iy/M;
-}
-#undef IC
-#undef IA
-#undef M
-
-//----------------------------------------------------------------------------
-// integer square root
-// calculate the largest number <= sqr(n)
-// Christopher J. Musial in Graphics Gems II, page 37ff, page 610
-// Academic Press, 1991
-// modified
-//----------------------------------------------------------------------------
-
-unsigned long int isqr(unsigned long int n)
-{
-	unsigned long int nextTrial, decrement;
-
-	if (nextTrial = n>>1) {
-		for ( ; ; ){
-			if(decrement = (nextTrial - n/nextTrial)>>1) nextTrial -= decrement;
-			else if(nextTrial * nextTrial > n) return nextTrial-1;
-			else return nextTrial;
-			}
-		}
-	return n;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// multiply two rotation matrices
-//  see: Graphic Gems, A.S. Glassner ed.; Academic Press Inc.
-//  ISBN 0-12-286165-5, p.640
-//
-bool MatMul(double a[3][3], double b[3][3], double c[3][3])
-{
-	int i, j, k;
-	bool success = true;
-
-	for(i = 0; i < 3; i++) {
-		for (j = 0; j < 3; j++) {
-			c[i][j] = 0.0;
-			for(k = 0; k < 3; k++) c[i][j] += a[i][k] * b[k][j];
-			if(c[i][j] < -.99999 || c[i][j] > .99999) success = false;
-			}
-		}
-	return success;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//Return a format string depending on the range
-//
-char *GetNumFormat(double Magn)
-{
-	if(Magn < -3.0) return("%0.2le");
-	if(Magn == -3.0) return("%0.4lf");
-	if(Magn == -2.0) return("%0.3lf");
-	if(Magn == -1.0) return("%0.2lf");
-	if(Magn == 0.0) return("%0.1lf");
-	if(Magn >= 5.0) return("%0.2le");
-	return("%0.0lf");
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//Delete a graphic object using the Id member of the class to select the 
-//   the proper destructor
-void DeleteGO(GraphObj *go)
-{
-	if(!go) return;
-	if(!go->Id) {
-		//this will also set the Id member of the class
-		go->Command(CMD_SET_DATAOBJ, 0L, 0L);
-		}
-	if(go == CurrGO) CurrGO = 0L;
-	switch(go->Id) {
-	case GO_AXIS:			delete((Axis*)go);			break;
-	case GO_TICK:			delete((Tick*)go);			break;
-	case GO_GRIDLINE:		delete((GridLine*)go);		break;
-	case GO_GRIDLINE3D:		delete((GridLine3D*)go);	break;
-	case GO_SYMBOL:			delete((Symbol*)go);		break;
-	case GO_BUBBLE:			delete((Bubble*)go);		break;
-	case GO_BAR:			delete((Bar*)go);			break;
-	case GO_ERRBAR:			delete((ErrorBar*)go);		break;
-	case GO_ARROW:			delete((Arrow*)go);			break;
-	case GO_BOX:			delete((Box*)go);			break;
-	case GO_WHISKER:		delete((Whisker*)go);		break;
-	case GO_DROPLINE:		delete((DropLine*)go);		break;
-	case GO_DATALINE:
-		//we call ~DataLine for ~DataPolygon because variables are
-		//   initialized in DataLine ??!!??!!
-		//   otherwise we would crash with ~DataPolygon.
-	case GO_DATAPOLYGON:	delete((DataLine*)go);		break;
-	case GO_SPHERE:			delete((Sphere*)go);		break;
-	case GO_PLANE:			delete((plane*)go);			break;
-	case GO_BRICK:			delete((Brick*)go);			break;
-	case GO_LINE3D:			delete((Line3D*)go);		break;
-	case GO_LABEL:			delete((Label*)go);			break;
-	case GO_MLABEL:			delete((mLabel*)go);		break;
-	case GO_SEGMENT:		delete((segment*)go);		break;
-	case GO_POLYGON:
-	case GO_POLYLINE:		delete((polyline*)go);		break;
-	case GO_REGLINE:		delete((RegLine*)go);		break;
-	case GO_SDELLIPSE:		delete((SDellipse*)go);		break;
-	case GO_ELLIPSE:		
-	case GO_ROUNDREC:
-	case GO_RECTANGLE:		delete((rectangle*)go);		break;
-	case GO_DRAGHANDLE:		delete((dragHandle*)go);	break;
-	case GO_DRAGRECT:		delete((dragRect*)go);		break;
-	case GO_DRAG3D:			delete((Drag3D*)go);		break;
-	case GO_FRAMERECT:		delete((FrmRect*)go);		break;
-	case GO_PLOT:			delete((Plot*)go);			break;
-	case GO_BARCHART:
-	case GO_PLOTSCATT:		delete((PlotScatt*)go);		break;
-	case GO_REGRESSION:		delete((Regression*)go);	break;
-	case GO_BUBBLEPLOT:		delete((BubblePlot*)go);	break;
-	case GO_BOXPLOT:		delete((BoxPlot*)go);		break;
-	case GO_DENSDISP:		delete((DensDisp*)go);		break;
-	case GO_STACKBAR:
-	case GO_WATERFALL:
-	case GO_STACKPG:		delete((StackBar*)go);		break;
-	case GO_POLARPLOT:		delete((PolarPlot*)go);		break;
-	case GO_RINGCHART:
-	case GO_PIECHART:		delete((PieChart*)go);		break;
-	case GO_GROUP:
-	case GO_STARCHART:		delete((GoGroup*)go);		break;
-	case GO_SCATT3D:		delete((Scatt3D*)go);		break;
-	case GO_PLOT3D:			delete((Plot3D*)go);		break;
-	case GO_PAGE:
-	case GO_GRAPH:			delete((Graph*)go);			break;
-	case GO_SVGOPTIONS:		delete((svgOptions*)go);	break;
-	case GO_DROPL3D:		delete((DropLine3D*)go);	break;
-	case GO_ARROW3D:		delete((Arrow3D*)go);		break;
-	case GO_LIMITS:			delete((Limits*)go);		break;
-	case GO_GRIDRADIAL:		delete((GridRadial*)go);	break;
-	case GO_DEFRW:			delete((DefsRW*)go);		break;
-	case GO_PLANE3D:		delete((Plane3D*)go);		break;
-	case GO_RIBBON:			delete((Ribbon*)go);		break;
-	case GO_FUNCTION:		delete((Function*)go);		break;
-	case GO_FITFUNC:		delete((FitFunc*)go);		break;
-	case GO_LEGITEM:		delete((LegItem*)go);		break;
-	case GO_LEGEND:			delete((Legend*)go);		break;
-	case GO_OBJTREE:		delete((ObjTree*)go);		break;
-	case GO_FREQDIST:		delete((FreqDist*)go);		break;
-	case GO_GRID3D:			delete((Grid3D*)go);		break;
-	case GO_FUNC3D:			delete((Func3D*)go);		break;
-	case GO_XYSTAT:			delete((xyStat*)go);		break;
-	case GO_FITFUNC3D:		delete((FitFunc3D*)go);		break;
-	case GO_BEZIER:			delete((Bezier*)go);		break;
-	case GO_TEXTFRAME:		delete((TextFrame*)go);		break;
-	case GO_NORMQUANT:		delete((NormQuant*)go);		break;
-	default:
-#ifdef USE_WIN_SECURE
-		sprintf_s(TmpTxt, TMP_TXT_SIZE, "Cannot delete Object\nwith Id %ld", go->Id);
-#else
-		sprintf(TmpTxt, "Cannot delete Object\nwith Id %ld", go->Id);
-#endif
-		ErrorBox(TmpTxt);
-		//we do not delete the object, probably we recover
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//Delete a graphic object from a list 
-bool DeleteGOL(GraphObj ***gol, long n, GraphObj *go, anyOutput *o)
-{
-	long i;
-	int c;
-	GraphObj **g, *p;
-
-	if(!gol || !(*gol) || !go || !n) return false;
-	for (i = 0, c = 0, g = *gol; i < n; i++, g++) {
-		if(*g) {
-			c++;
-			if(*g == go) {
-				p = (*g)->parent;
-				if(o) o->HideMark();
-				Undo.DeleteGO(g, 0L, o);
-				if(c == 1) {
-					for (g++, i++ ;i < n; i++, g++) {
-						if(*g) return true;
-						}
-					if(p) Undo.DropMemory(p, (void**) gol, UNDO_CONTINUE);
-					}
-				return true;
-				}
-			}
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//backup file before writing a new one
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool BackupFile(char *FileName)
-{
-	int i;
-	FILE *TheFile;
-	char TmpFileName[512], Name[512], ext[6];
-
-	//no backup necessary if file does not exist
-	if(!(FileExist(FileName))) return true;
-	rlp_strcpy(Name, 512,FileName);
-	i = (int)strlen(Name)-1;
-	if(Name[--i] == '.') Name[i] = 0;
-	else if(Name[--i] == '.') Name[i] = 0;
-	else if(Name[--i] == '.') Name[i] = 0;
-	else return false;
-	i = 1;
-	do {
-#ifdef USE_WIN_SECURE
-		sprintf_s(ext, 6, ".%03d", i);
-		sprintf_s(TmpFileName, 512, "%s%s", Name, ext);
-		if(!(fopen_s(&TheFile, TmpFileName, "r"))) {
-			fclose(TheFile);
-			}
-		else break;
-#else
-		sprintf(ext, ".%03d", i);
-		sprintf(TmpFileName, "%s%s", Name, ext);
-		if((TheFile = fopen(TmpFileName, "r"))) fclose(TheFile);
-#endif
-		i++;
-	} while (i < 999 && TheFile);
-	if(i >= 999) {								//too many backups exist already
-		ErrorBox("Too many backup\files exist already.");
-		return false;
-		}
-	if(-1 == rename(FileName, TmpFileName)) {
-		ErrorBox("Error accessing file.");
-		return false;
-		}
-	return true;
-}
-
-bool IsRlpFile(char *FileName)
-{
-	FILE *TheFile;
-	char Line[10];
-	bool bRet = false;
-
-#ifdef USE_WIN_SECURE
-	if(fopen_s(&TheFile, FileName, "r")) return false;
-#else
-	if(0L ==(TheFile = fopen(FileName, "r"))) return false;
-#endif
-	fread(Line, 1, 8, TheFile);
-	Line[5] = 0;
-	if(0 == strcmp(Line, ";RLP "))bRet = true;
-	fclose(TheFile);
-	return bRet;
-}
-
-bool IsXmlFile(char *FileName)
-{
-	FILE *TheFile;
-	char Line[10];
-	bool bRet = false;
-
-#ifdef USE_WIN_SECURE
-	if(fopen_s(&TheFile, FileName, "r")) return false;
-#else
-	if(0L ==(TheFile = fopen(FileName, "r"))) return false;
-#endif
-	fread(Line, 1, 8, TheFile);
-	Line[6] = 0;
-	if(0 == strcmp(Line, "<?xml "))bRet = true;
-	fclose(TheFile);
-	return bRet;
-}
-
-bool FileExist(char *FileName)
-{
-	FILE *TheFile;
-
-#ifdef USE_WIN_SECURE
-	if(ENOENT == fopen_s(&TheFile, FileName, "r")) return false;
-#else
-	if(0L ==(TheFile = fopen(FileName, "r"))) {
-		if(errno == ENOENT) return false;
-		return true;
-		}
-#endif
-	fclose(TheFile);
-	return true;
-}
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//check Object for certain properties
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool IsPlot3D(GraphObj *g)
-{
-	if(g && (g->Id == GO_PLOT3D || g->Id == GO_FUNC3D || g->Id == GO_FITFUNC3D)) return true;
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//duplicate a block of memory
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void *memdup(void *ptr, int cb_old, int cb_new)
-{
-	void *p;
-
-	if(cb_new > cb_old) {
-		if((p = calloc(cb_new, 1)) && ptr) memcpy(p, ptr, cb_old);
-		}
-	else {
-		if((p = malloc(cb_old)) && ptr) memcpy(p, ptr, cb_old);
-		}
-	return p;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//calculate angle from sin(angle)
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-double trig2deg(double si, double csi)
-{
-	double ang;
-
-	ang = acos(csi);
-	if(si < 0.0) ang *= -1.0;
-	return floor(ang * 57.29577951 +0.5);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//replace graphic object with new: typically used for undo
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool ReplaceGO(GraphObj **oldobj, GraphObj **newobj)
-{
-	newobj[1]->parent = newobj[0]->parent;
-	newobj[1]->Command(CMD_SET_DATAOBJ, newobj[0]->data, 0L);
-	*oldobj = newobj[1];
-	newobj[0]->parent = 0L;		//disable messaging
-	Undo.InvalidGO(newobj[0]);
-	DeleteGO(newobj[0]);
-	return true;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//calculate a 'unique' hash value for a string
-//Ref: Corman T.H., Leiserson C.E. & Rivest R.L. (1990) Hash Functions.
-//   in: Introduction to Algorithms (MIT Press & McGraw-Hill)
-//   ISBN 0-262-03141-8 and ISBN 0-07-013143-0, pp. 226ff
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-unsigned int HashValue(unsigned char *str)
-{
-	unsigned int i = 0, ret = 0;
-
-	if(!str || !str[0]) return 0;
-	do {
-		if(str[i] > 32) ret = ((str[i]-32) + (ret <<2));
-		i++;
-		}while(str[i]);
-	return ret;
-}
-
-unsigned int Hash2(unsigned char * str)
-{
-	unsigned int i = 0, ret = 0, c;
-
-	if(!str) return 0;
-	do {
-		c = str[i++];
-		ret = ((ret * c)<<2) | c;
-		}while(str[i]);
-	return ret;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//compare data structures: return true if changed or save undo info
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-bool cmpLineDEF(LineDEF *l1, LineDEF *l2)
-{
-	if(!l1 || !l2 || l1 == l2) return false;	//oh, oh !
-	if(l1->width != l2->width) return true;
-	if(l1->patlength != l2->patlength) return true;
-	if(l1->color != l2->color) return true;
-	if(l1->pattern != l2->pattern) return true;
-	return false;
-}
-
-bool cmpFillDEF(FillDEF *f1, FillDEF *f2)
-{
-	if(!f1 || !f2 || f1 == f2) return false;	//oh, oh!
-	if(f1->type != f2->type || f1->color != f2->color ||
-		f1->scale != f2->scale || f1->color2 != f2->color2) return true;
-	//the hatch line is subject to a separate call to cmpLineDEF
-	return false;
-}
-
-bool cmpAxisDEF(AxisDEF *a1, AxisDEF *a2)
-{
-	int i;
-
-	if(!a1 || !a2 || a1 == a2) return false;	//oh, oh!
-	if(a1->flags != a2->flags || a1->min != a2->min || a1->max != a2->max ||
-		a1->loc[0].fx != a2->loc[0].fx || a1->loc[0].fy != a2->loc[0].fy ||
-		a1->loc[0].fz != a2->loc[0].fz || a1->loc[1].fx != a2->loc[1].fx ||
-		a1->loc[1].fy != a2->loc[1].fy || a1->loc[1].fz != a2->loc[1].fz ||
-		a1->Start != a2->Start || a1->Step != a2->Step || a1->Radius != a2->Radius ||
-		a1->Center.fx != a2->Center.fx || a1->Center.fy != a2->Center.fy ||
-		a1->nBreaks != a2->nBreaks) return true;
-	for(i = 0; i < a1->nBreaks; i++) {
-		if(a1->breaks[i].fx != a2->breaks[i].fx ||
-			a1->breaks[i].fy != a2->breaks[i].fy) return true;
-		}
-	return false;
-}
-
-bool cmpTextDEF(TextDEF *t1, TextDEF *t2)
-{
-	if(!t1 || !t2) return false;
-	if(t1->ColTxt != t2->ColTxt || t1->ColBg != t2->ColBg || t1->fSize != t2->fSize ||
-		t1->RotBL != t2->RotBL || t1->RotCHAR != t2->RotCHAR || t1->iSize != t2->iSize ||
-		t1->Align != t2->Align || t1->Mode != t2->Mode || t1->Style != t2->Style ||
-		t1->Font != t2->Font) return true;
-	if((!(t1->text) && (t2->text)) || (!(t2->text) && (t1->text))) return true;
-	if(t1->text && t2->text && strcmp(t1->text, t2->text)) return true;
-	return false;
-}
-
-// Dialog Undo utilitites
-DWORD CheckNewFloat(double *loc, double old_v, double new_v, GraphObj *par, DWORD flags)
-{
-	if(loc && old_v != new_v) {
-		Undo.ValFloat(par, loc, flags);
-		*loc = new_v;						return (flags | UNDO_CONTINUE);
-		}
-	return flags;
-}
-
-DWORD CheckNewInt(int *loc, int old_v, int new_v, GraphObj *par, DWORD flags)
-{
-	if(loc && old_v != new_v) {
-		Undo.ValInt(par, loc, flags);
-		*loc = new_v;						return (flags | UNDO_CONTINUE);
-		}
-	return flags;
-}
-
-DWORD CheckNewDword(DWORD *loc, DWORD old_v, DWORD new_v, GraphObj *par, DWORD flags)
-{
-	if(loc && old_v != new_v) {
-		Undo.ValDword(par, loc, flags);
-		*loc = new_v;						return (flags | UNDO_CONTINUE);
-		}
-	return flags;
-}
-
-DWORD CheckNewLFPoint(lfPOINT *loc, lfPOINT *old_v, lfPOINT *new_v, GraphObj *par, DWORD flags)
-{
-	if(loc && old_v && new_v && (old_v->fx != new_v->fx || old_v->fy != new_v->fy)) {
-		Undo.SaveLFP(par, loc, flags);
-		loc->fx = new_v->fx;	loc->fy = new_v->fy;	return (flags | UNDO_CONTINUE);
-		}
-	return flags;
-}
-
-DWORD CheckNewString(char **loc, char *s_old, char *s_new, GraphObj *par, DWORD flags)
-{
-	int ocb, ncb, cb;
-
-	if(s_old && s_new) {
-		if(!strcmp(s_old, s_new)) return flags;
-		ocb = (int)strlen(s_old);		ncb = (int)strlen(s_new);
-		cb = ncb > ocb ? ncb : ocb;
-		if(cb > ocb) {
-			*loc = (char*)realloc(*loc, cb * sizeof(char)+1);
-			}
-		Undo.String(par, loc, flags);	flags |= UNDO_CONTINUE;
-		if(*loc) rlp_strcpy(*loc, cb+1, s_new); 
-		}
-	else if(!s_old && s_new && s_new[0]) {
-		Undo.String(par, loc, flags);	flags |= UNDO_CONTINUE;
-		*loc = (char *)memdup(s_new, (int)strlen(s_new) +1, 0);
-		}
-	else if(s_old && s_old[0] && !s_new) {
-		Undo.String(par, loc, flags);	flags |= UNDO_CONTINUE;
-		if(*loc) *loc[0] = 0;
-		}
-	return flags;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//execute clipping of 3D objects
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-typedef struct {			//structure required by 3D Bresenhan's algorithm
-	int d, a, s, *r, l;
-	}moveDEF;
-
-long sph_r2;
-int sph_x, sph_y, sph_z, nclp_pg, seg_x_seg;
-POINT3D *clp_pg, *sphlim1, *sphlim2;
-double *vclp_pg = 0L;
-
-int test_sphere(int x, int y, int z)
-{
-	int d;
-	long ld;
-	
-	ld = (d = x-sph_x) * d;			ld += (d = y-sph_y) * d;
-	ld += (d = z-sph_z) * d;
-	return (ld > sph_r2) ? 1 : 0;
-}
-
-//use a 3D Bresenham alorithm to find z coordinates where x == lx or y == ly
-bool line3D_z(POINT3D *p1, POINT3D *p2, int lx, int ly, int *cx, int *cy, int *cz)
-{
-	moveDEF mx, my, mz, *m1, *m2, *m3;
-	int x, y, z, d1, d2;
-
-	mx.d = p2->x - p1->x;		mx.a = mx.d >= 0 ? mx.d : -mx.d;
-	mx.s = mx.d >= 0 ? 1 : -1;	mx.r = &x;		mx.l = p2->x;	x = p1->x;
-	my.d = p2->y - p1->y;		my.a = my.d >= 0 ? my.d : -my.d;
-	my.s = my.d >= 0 ? 1 : -1;	my.r = &y;		my.l = p2->y;	y = p1->y;
-	mz.d = p2->z - p1->z;		mz.a = mz.d >= 0 ? mz.d : -mz.d;
-	mz.s = mz.d >= 0 ? 1 : -1;	mz.r = &z;		mz.l = p2->z;	z = p1->z;
-	if(mx.a > my.a) {
-		if(mz.a > mx.a) {
-			m1 = &mz;		m2 = &mx;	m3 = &my;
-			}
-		else if(mz.a > my.a) {
-			m1 = &mx;		m2 = &mz;	m3 = &my;
-			}
-		else {
-			m1 = &mx;		m2 = &my;	m3 = &mz;
-			}
-		}
-	else {
-		if(mz.a > my.a) {
-			m1 = &mz;		m2 = &my;	m3 = &mx;
-			}
-		else if(mz.a > mx.a) {
-			m1 = &my;		m2 = &mz;	m3 = &mx;
-			}
-		else {
-			m1 = &my;		m2 = &mx;	m3 = &mz;
-			}
-		}
-	d1 = m2->a - (m1->a >>1);		d2 = m3->a - (m1->a >>1);
-	for(; ;) {
-		//process point at (m1.r, m2.r, m3.r);
-		if(x == lx || y == ly) {
-			*cx = x;	*cy = y;	*cz = z;
-			return true;
-			}
-		if(*(m1->r) == m1->l) return false;
-		if(d1 >= 0) {
-			*(m2->r) += m2->s;	d1 -= m1->a;
-			}
-		if(d2 >= 0) {
-			*(m3->r) += m3->s;	d2 -= m1->a;
-			}
-		*(m1->r) += m1->s;	d1 += m2->a;		d2 += m3->a;
-		}
-}
-
-//test if point is 1) outside, 2) above, 3) on the line, or 0) hidden by a plane 
-int test_plane(int x, int y, int z)
-{
-	int us, ls, us1, ls1, x1, y1, z1, x2, y2, z2;
-	static int last = 0;
-	POINT3D p1, p2;
-
-	if(IsInPolygon3D(x, y, clp_pg, nclp_pg, &us, &ls)) {
-		if(us == ls){
-			seg_x_seg = us;
-			return last = 3;		//point is on line: visible
-			}
-		if(vclp_pg) {
-			if(iround((x * vclp_pg[0] + y * vclp_pg[1] - vclp_pg[3])/vclp_pg[2]) < z)
-				return last = 2;
-			else return last = 0;
-			}
-		if(us < 1) us1 = nclp_pg -1;	else us1 = us -1;
-		if(ls < 1) ls1 = nclp_pg -1;	else ls1 = ls -1;
-		if(z < clp_pg[us1].z && z < clp_pg[us].z && z < clp_pg[ls1].z &&
-			z < clp_pg[ls].z) return last = 0;		//far below plane
-		if(z > clp_pg[us1].z && z > clp_pg[us].z && z > clp_pg[ls1].z &&
-			z > clp_pg[ls].z) return last = 2;		//far above plane
-		if(line3D_z(&clp_pg[us1], &clp_pg[us], x, -1, &x1, &y1, &z1) &&
-			line3D_z(&clp_pg[ls1], &clp_pg[ls], x, -1, &x2, &y2, &z2)){
-			if(z1 < z && z2 < z) return last = 2;
-			if(z1 >= z && z2 >= z) return last = 0;
-			p1.x = x1;	p1.y = y1; p1.z = z1;	p2.x = x2;	p2.y = y2; p2.z = z2;
-			if(line3D_z(&p1, &p2, -1, y, &x1, &y1, &z1)) {
-				if(z > z1) return last = 2;
-				else return last = 0;
-				}
-			}
-		return last;
-		}
-	return last = 1;
-}
-
-int test_planeandline(int x, int y, int z)
-{
-	int ret;
-
-	ret = test_plane(x, y, z);
-	if(ret == 3 && clp_pg && nclp_pg && idx_point_on_line < nclp_pg) {
-		if(vclp_pg) {
-			if(iround((x * vclp_pg[0] + y * vclp_pg[1] - vclp_pg[3])/vclp_pg[2]) < z)
-				return 2;
-			else return 0;
-			}
-		else {
-			// no equation for plane available
-			}
-		}
-	return ret;
-}
-
-
-void proc_polygon(int vis, POINT3D *pnt, POINT3D * last);	//prototype
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//use a 3D Bresenham algorithm to clip lines
-void clip_line_3D(GraphObj *go, POINT3D *p1, POINT3D *p2, int(*proc)(int, int, int))
-{
-	moveDEF mx, my, mz, *m1, *m2, *m3;
-	int x, y, z, d1, d2, vis;
-	bool bVisible, bDrawLater = false;
-	POINT3D p, lp;
-
-	if(p1->x == p2->x && p1->y == p2->y && p1->z == p2->z) return;
-	mx.d = p2->x - p1->x;		mx.a = mx.d >= 0 ? mx.d : -mx.d;
-	mx.s = mx.d >= 0 ? 1 : -1;	mx.r = &x;		mx.l = p2->x;	x = p1->x;
-	my.d = p2->y - p1->y;		my.a = my.d >= 0 ? my.d : -my.d;
-	my.s = my.d >= 0 ? 1 : -1;	my.r = &y;		my.l = p2->y;	y = p1->y;
-	mz.d = p2->z - p1->z;		mz.a = mz.d >= 0 ? mz.d : -mz.d;
-	mz.s = mz.d >= 0 ? 1 : -1;	mz.r = &z;		mz.l = p2->z;	z = p1->z;
-	if(mx.a > my.a) {
-		if(mz.a > mx.a) {
-			m1 = &mz;		m2 = &mx;	m3 = &my;
-			}
-		else if(mz.a > my.a) {
-			m1 = &mx;		m2 = &mz;	m3 = &my;
-			}
-		else {
-			m1 = &mx;		m2 = &my;	m3 = &mz;
-			}
-		}
-	else {
-		if(mz.a > my.a) {
-			m1 = &mz;		m2 = &my;	m3 = &mx;
-			}
-		else if(mz.a > mx.a) {
-			m1 = &my;		m2 = &mz;	m3 = &mx;
-			}
-		else {
-			m1 = &my;		m2 = &mx;	m3 = &mz;
-			}
-		}
-	bVisible = (0 != (vis = (*proc)(x, y, z)));
-	if((bDrawLater = (vis == 2)) && go) go->Command(CMD_DRAW_LATER, 0L, 0L);
-	lp.x = p1->x;	lp.y = p1->y;	lp.z = p1->z;
-	if(!go && vis) proc_polygon(vis, p1, p1);
-	d1 = m2->a - (m1->a >>1);		d2 = m3->a - (m1->a >>1);
-	for(; ;) {
-		//process point at (m1.r, m2.r, m3.r);
-		vis = (*proc)(x, y, z);
-		if(!bDrawLater && vis == 2){
-			if(go) go->Command(CMD_DRAW_LATER, 0L, 0L);
-			bDrawLater = true;
-			}
-		if(bVisible) {
-			if(!vis) {
-				p.x = x;	p.y = y;	p.z = z;
-				if(go) go->Command(CMD_ADDTOLINE, &lp, 0L);
-				else proc_polygon(vis, &p, &lp);
-				bVisible = false;
-				}
-			}
-		else if(vis){
-			p.x = x;	p.y = y;	p.z = z;
-			if(go) go->Command(CMD_STARTLINE, &p, 0L);
-			else proc_polygon(vis, &p, &lp);
-			bVisible = true;
-			}
-		if(*(m1->r) == m1->l){
-			if(vis){
-				p.x = x;	p.y = y;	p.z = z;
-				if(go) go->Command(CMD_ADDTOLINE, &p, 0L);
-				else proc_polygon(vis, &p, &lp);
-				}
-			return;
-			}
-		lp.x = x;	lp.y = y;	lp.z = z;
-		if(d1 >= 0) {
-			*(m2->r) += m2->s;	d1 -= m1->a;
-			}
-		if(d2 >= 0) {
-			*(m3->r) += m3->s;	d2 -= m1->a;
-			}
-		*(m1->r) += m1->s;	d1 += m2->a;		d2 += m3->a;
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//use circular Bresenham's algorithm to clip a spherical scanline
-//Ref: C. Montani, R. Scopigno (1990) "Speres-To-Voxel Conversion", in:
-//   Graphic Gems (A.S. Glassner ed.) Academic Press, Inc.; 
-//   ISBN 0-12-288165-5 
-void clip_spher_line(GraphObj *go, POINT3D *cent, int r, bool bVert, int(*proc)(int, int, int))
-{
-	int x, y, q, di, de, lim;
-	int vis = 0, o_vis;
-	POINT3D cpos;
-
-	if(r < 1) return;
-	cpos.x = cent->x;	cpos.y = cent->y;	cpos.z = cent->z;
-	if(bVert) cpos.y -= r;
-	else cpos.x -= r;
-	for(q = 0; q < 2; q++) {
-		x = lim = 0;	y = r;	di = 2*(1-r);
-		while (y >= lim){
-			o_vis = vis;
-			if(bVert && (cpos.x < sphlim1->x || cpos.x > sphlim2->x)) vis = 0;
-			else if (cpos.y < sphlim1->y || cpos.y > sphlim2->y) vis = 0;
-			else if (cpos.x > 0 && cpos.y >0) vis = (*proc)(cpos.x, cpos.y, cpos.z);
-			if(vis != o_vis) {
-				if(vis) go->Command(CMD_STARTLINE, &cpos, 0L);
-				else go->Command(CMD_ADDTOLINE, &cpos, 0L);
-				}
-			if(di < 0) {
-				de = 2*di + 2*y -1;
-				if(de > 0) {
-					x++;	y--;	di += (2*x -2*y +2);
-					}
-				else {
-					x++;	di += (2*x +1);
-					}
-				}
-			else {
-				de = 2*di -2*x -1;
-				if(de > 0) {
-					y--;	di += (-2*y +1);
-					}
-				else {
-					x++;	y--;	di += (2*x -2*y +2);
-					}
-				}
-			switch(q) {
-			case 0:
-				if(bVert) cpos.y = cent->y - y;
-				else cpos.x = cent->x - y;
-				cpos.z = cent->z + x;
-				break;
-			case 1:
-				if(vis && y < lim) go->Command(CMD_ADDTOLINE, &cpos, 0L);
-				if(bVert) cpos.y = cent->y + x;
-				else cpos.x = cent->x + x;
-				cpos.z = cent->z + y;
-				break;
-				}
-			}
-		}
-	return;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//come here to process polygon clipping events
-int ppg_vis, ppg_level, ppg_reason, ppg_firstvis, ppg_nact, ppg_nmask;
-bool ppg_nowvis;
-POINT *ppg_mask;
-POINT3D ppg_first, *ppg_act;
-GraphObj *ppg_par;
-double *ppg_vec = 0L;
-
-// test if point is on line of 3D polygon
-//Ref: P.S. Heckbert (1990) "Digital Line Drawing", in: Graphic Gems
-//   (A.S. Glassner, ed.); Academic Press, Inc.,
-//   ISBN 0-12-286165-5
-bool IsOnLine(POINT *p1, POINT *p2, int x, int y)
-{
-	int d, ax, ay, sx, sy, dx, dy;
-	POINT tr;
-
-	dx = p2->x - p1->x;
-	if ( p2->x < p1->x) { 	ax = (-dx)<<1;		sx = -1;		}
-	else {					ax = dx <<1;		sx = 1;		}
-	dy = p2->y - p1->y;
-	if (p2->y < p1->y) {	ay = (-dy)<<1;		sy = -1;		}
-	else {					ay = dy<<1;			sy = 1;		}
-	tr.x = p1->x;			tr.y = p1->y;
-	if (ax > ay) {									// x dominant
-		d = ay - (ax >>1);
-		for ( ; ; ) {
-			if(tr.y == y && tr.x == x) return true;	//match
-			if(tr.x == p2->x) return false;
-			if (d >= 0) {tr.y += sy;		d -= ax;}
-			tr.x += sx;		d += ay;
-			}
-		}
-	else {											// y dominant
-		d = ax - (ay >>1);
-		for ( ; ; ) {
-			if(tr.x == x && tr.y == y) return true;	//match
-			if (tr.y == p2->y) return false;
-			if (d >= 0) {tr.x += sx;		d -= ay;}
-			tr.y += sy;		d += ax;
-			}
-		}
-}
-
-//use Bresenham's algorithm to complete a partially hidden polygon
-//   by the shadow of the above surface
-//Ref: P.S. Heckbert (1990) "Digital Line Drawing", in: Graphic Gems
-//   (A.S. Glassner, ed.); Academic Press, Inc.,
-//   ISBN 0-12-286165-5
-bool ShadowPolygon(POINT *p1, POINT *p2, POINT *tp, int ntp, POINT *pts, long *cp, POINT *lim)
-{
-	int d, ax, ay, sx, sy, dx, dy;
-	bool bPen, vis;
-	POINT tr, mp;
-	long d1, ndist, dist = 99999;
-
-	dx = p2->x - p1->x;
-	if ( p2->x < p1->x) { 	ax = (-dx)<<1;		sx = -1;		}
-	else {					ax = dx <<1;		sx = 1;		}
-	dy = p2->y - p1->y;
-	if (p2->y < p1->y) {	ay = (-dy)<<1;		sy = -1;		}
-	else {					ay = dy<<1;			sy = 1;		}
-	tr.x = p1->x;			tr.y = p1->y;
-	bPen = IsInPolygon(&tr, tp, ntp) || IsCloseToPL(tr, tp, ntp);
-	AddToPolygon(cp, pts, p1);				//first Point is always visible
-	if (ax > ay) {							// x dominant
-		d = ay - (ax >>1);
-		for ( ; ; ) {
-			if(abs(tr.y - lim->y) < 3 && abs(tr.x - lim->x) < 3) {
-				ndist = (d1=(tr.y - lim->y))*d1;
-				ndist += ((d1=(tr.x - lim->x))*d1);
-				if(ndist <= dist) {
-					mp.x = tr.x;	mp.y = tr.y;	dist = ndist;
-					}
-				else {
-					//mp is the closest point
-					AddToPolygon(cp, pts, &mp);
-					return false;
-					}
-				}
-			vis = IsInPolygon(&tr, tp, ntp) || IsCloseToPL(tr, tp, ntp);
-			if(bPen && !vis) {
-				AddToPolygon(cp, pts, &tr);
-				return false;				//now leaving the polygon
-				}
-			if(tr.x == p2->x) return true;	//still inside
-			if (d >= 0) {tr.y += sy;		d -= ax;}
-			bPen = vis;		tr.x += sx;		d += ay;
-			}
-		}
-	else {									// y dominant
-		d = ax - (ay >>1);
-		for ( ; ; ) {
-			if(abs(tr.x - lim->x) < 3 && abs(tr.y - lim->y) < 3) {
-				ndist = (d1=(tr.y - lim->y))*d1;
-				ndist += ((d1=(tr.x - lim->x))*d1);
-				if(ndist <= dist) {
-					mp.x = tr.x;	mp.y = tr.y;	dist = ndist;
-					}
-				else {
-					//mp is the closest point
-					AddToPolygon(cp, pts, &mp);
-					return false;
-					}
-				}
-			vis = IsInPolygon(&tr, tp, ntp) || IsCloseToPL(tr, tp, ntp);
-			if(bPen && !vis) {
-				AddToPolygon(cp, pts, &tr);
-				return false;				//now leaving the polygon
-				}
-			if (tr.y == p2->y) return true;	//still inside
-			if (d >= 0) {tr.x += sx;		d -= ay;}
-			bPen = vis;		tr.y += sy;		d += ax;
-			}
-		}
-}
-
-//find sepment which is closest to point
-int FindClosestSeg(POINT3D *pg, int npg, int x, int y, int start)
-{
-	int i, i1, j, tmp, idx = -2, dx, dy, d, dist = 10000;
-	POINT p1, p2;
-
-	if(start < 1) start = 1;
-	for(j = start + npg, i1 = start; i1 < j; i1++) {
-		i = ((i1-1)%npg)+1;
-		if( i == npg) {
-			p1.x = pg[i-1].x;	p1.y = pg[i-1].y;
-			p2.x = pg[0].x;		p2.y = pg[0].y;
-			}
-		else {
-			p1.x = pg[i-1].x;	p1.y = pg[i-1].y;
-			p2.x = pg[i].x;		p2.y = pg[i].y;
-			}
-		if(p1.x != p2.x || p1.y != p2.y) {
-			d = dist;
-			if(abs(dx = (p2.x - p1.x)) < abs(dy = (p2.y - p1.y))) {	//y dominant
-				if(dy && ((p1.y >= y && p2.y < y) || (p2.y > y && p1.y <= y))) {
-					tmp = (p1.x + ((y - p1.y) * dx)/dy);	d = abs(x-tmp);
-					}
-				}
-			else {								// x dominant
-				if(dx && ((p1.x >= x && p2.x < x) || (p2.x > x && p1.x <= x))) {
-					tmp = (p1.y + ((x - p1.x) * dy)/dx);	d = abs(y-tmp);
-					}
-				}
-			if(d < dist) {
-				dist = d;	idx = i;
-				}
-			}
-		if(dist < 3) break;
-		}
-	return idx;
-}
-
-//finish a partially visible 3D polygon by its shadow of the above
-//   3D polygon
-bool AddShadowPolygon(POINT3D *pnt, POINT3D *ep, int cidx) {
-	int us, ls, i, j, k, x1, x2, y1, y2, z1, z2, idx_clppg_line;
-	long cpg1 = 0, d, d1, d2, ntp=0, ntp1=0, ntp2=0;
-	POINT *pg1 = 0L, np, *tp=0L, *tp1, *tp2, lim;	
-	POINT3D nep;
-	bool bRet = false;
-
-	d = ((d1 = (ep->x - pnt->x))*d1);
-	d += ((d1 = (ep->y - pnt->y))*d1);
-	if(d < 4) {			//propably too close
-		if(d) ppg_par->Command(CMD_ADDTOLINE, ep, 0L);
-		return true;	//connect
-		}
-	lim.x = ep->x;		lim.y = ep->y;
-	idx_clppg_line = FindClosestSeg(clp_pg, nclp_pg, pnt->x, pnt->y, cidx);
-	//create track from hiding polygon
-	//the ppoint 'pnt' is expected to be on the line
-	//   clp_pg[idx_clpgg_line] and clp_pg[idx_clpgg_line - 1]
-	if(!(pg1 = (POINT*)calloc(nclp_pg +4, sizeof(POINT))))return true;
-	np.x = pnt->x;	np.y = pnt->y;	AddToPolygon(&cpg1, pg1, &np);
-	j = idx_clppg_line + nclp_pg;
-	for(i = idx_clppg_line; i < j; i++) {
-		np.x = clp_pg[k = (i%nclp_pg)].x;		np.y = clp_pg[k].y;
-		AddToPolygon(&cpg1, pg1, &np);
-		}
-	//close polygon
-	np.x = pnt->x;	np.y = pnt->y;	AddToPolygon(&cpg1, pg1, &np);
-	//calculate two possible solutions
-	tp1 = (POINT*)calloc(nclp_pg+4, sizeof(POINT));
-	tp2 = (POINT*)calloc(nclp_pg+4, sizeof(POINT));
-	if(!tp1 || !tp2) {			//memory allocation error
-		free(pg1);	free(ppg_mask);	return false;
-		}
-	ShadowPolygon(&pg1[0], &pg1[1], ppg_mask, (int)ppg_nmask, tp1, &ntp1, &lim);
-	if(ntp1 == 1){				//more than one segment
-		for(i = 2; i < cpg1; i++) {
-			if(!ShadowPolygon(&pg1[i-1], &pg1[i], ppg_mask, (int)ppg_nmask, tp1, &ntp1, &lim))
-				break;
-			}
-		}
-	if(i == cpg1) {				//last segment required
-		ShadowPolygon(&pg1[i-1], &pg1[0], ppg_mask, (int)ppg_nmask, tp1, &ntp1, &lim);
-		}
-	ShadowPolygon(&pg1[0], &pg1[cpg1-1], ppg_mask, (int)ppg_nmask, tp2, &ntp2, &lim);
-	if(ntp2 == 1){				//more than one segment
-		for(i = cpg1-1; i > 1; i--) {
-			if(!ShadowPolygon(&pg1[i], &pg1[i-1], ppg_mask, (int)ppg_nmask, tp2, &ntp2, &lim))
-				break;
-			}
-		}
-	//find better solution
-	d1 = ((d = (ep->x - tp1[ntp1-1].x))*d);		d1 += ((d = (ep->y - tp1[ntp1-1].y))*d);
-	d2 = ((d = (ep->x - tp2[ntp2-1].x))*d);		d2 += ((d = (ep->y - tp2[ntp2-1].y))*d);
-	if(d1 < d2 && d1 < 5) {			//use solution 1
-		tp = tp1;	ntp = ntp1;
-		}
-	else if(d2 < d1 && d2 < 5) {	//use solution 2
-		tp = tp2;	ntp = ntp2;
-		}
-	else if (d1 == d2 && d1 < 5) {			//ambiguous result: connect stright 
-		if(d) ppg_par->Command(CMD_ADDTOLINE, ep, 0L);
-		}
-	else {							//no valid solution;
-		if(cidx >= 0) return AddShadowPolygon(pnt, ep, -2);
-		bRet = false;
-		}
-	if(tp && ntp>1) {				//create shadow line
-		bRet = true;
-		for(i = 1; i < ntp; i++) {
-			if(i == ntp -1) {
-				d = ((d1 = tp[i].x - ep->x) * d1);
-				d += ((d1 = tp[i].y - ep->y) * d1);
-				if(d < 2){						//too close to end point
-					nep.x = ep->x = tp[i].x;	nep.y = ep->y = tp[i].y;
-					nep.z = ep->z;
-					break;
-					}
-				}
-			np.x = nep.x = tp[i].x;	np.y = nep.y = tp[i].y;
-			nep.z = pnt->z > ep->z ? ep->z : pnt->z;
-			if(IsInPolygon(&np, ppg_mask, ppg_nmask) || 
-				IsCloseToPL(np, ppg_mask, ppg_nmask)){
-				if(ppg_vec) {					//valid plane eqation
-					nep.z = iround((nep.x * ppg_vec[0] + nep.y * ppg_vec[1] - ppg_vec[3])/ppg_vec[2]);
-					ppg_par->Command(CMD_ADDTOLINE, &nep, 0L);
-					}
-				else if(IsInPolygon3D(nep.x, nep.y, ppg_act, ppg_nact, &us, &ls)) {
-					if(us == ls) if(ls){	//point is on the line
-						j = nep.z;
-						if(ppg_act[ls].x == ppg_act[ls-1].x && ppg_act[ls].y == ppg_act[ls-1].y){
-							nep.z = (ppg_act[ls].z + ppg_act[ls-1].z)>>1; //impropable
-							}
-						else if(abs(ppg_act[ls].x - ppg_act[ls-1].x) > 
-							abs(ppg_act[ls].y - ppg_act[ls-1].y)){	// x dominant
-							line3D_z(&ppg_act[ls-1], &ppg_act[ls], nep.x, -1, &k, &k, &j);
-							}
-						else {										// y dominant
-							line3D_z(&ppg_act[ls-1], &ppg_act[ls], -1, nep.y, &k, &k, &j);
-							}
-						nep.z = j;
-						}
-					else {
-						if(line3D_z(&ppg_act[ls-1], &ppg_act[ls], nep.x, -1, &x1, &y1, &z1) &&
-							line3D_z(&ppg_act[us-1], &ppg_act[us], nep.x, -1, &x2, &y2, &z2)){
-							nep.z = (z1 + z2)>>1;					//impropable
-							}
-						}
-					ppg_par->Command(CMD_ADDTOLINE, &nep, 0L);
-					}
-				else {
-					//point is inside by one algorithm but outside with another
-					//try without this point
-					}
-				}
-			}
-		if(nep.x != ep->x || nep.y != ep->y) ppg_par->Command(CMD_ADDTOLINE, ep, 0L);
-		}
-	free(pg1);	free(tp1);	free(tp2);
-	return bRet;
-}
-
-//calculate the clipping line between two planes
-bool CuttingEdge(POINT3D* pt, POINT3D* np)
-{
-	int i, j, us1, ls1, us2, ls2;
-	long d, di[2];
-	POINT3D res[2];
-	double v[3], s[2][3];
-
-	if(!vclp_pg || !ppg_vec) return false;
-	v[0] = vclp_pg[1]*ppg_vec[2] - vclp_pg[2]*ppg_vec[1];
-	v[1] = vclp_pg[2]*ppg_vec[0] - vclp_pg[0]*ppg_vec[2];
-	v[2] = vclp_pg[0]*ppg_vec[1] - vclp_pg[1]*ppg_vec[0];
-	if(fabs(v[0]) < 1e-5 || fabs(v[1]) < 1e-5 || fabs(v[2]) < 1e-5) return false;
-	v[0] *= (v[2]*2048.0);	v[1] *= (v[2]*2048.0);	v[2] *= (v[2]*2048.0);
-	//find two solutions +/- vector
-	for(i = 0; i < 2; i++) {
-		s[i][0] = (double)(pt->x);	s[i][1] = (double)(pt->y);	s[i][2] = (double)(pt->z);
-		for(j = 0; j < 5; j++) {
-			do {
-				s[i][0] += v[0];			s[i][1] += v[1];			s[i][2] += v[2];
-				}while(IsInPolygon3D(s[i][0], s[i][1], ppg_act, ppg_nact, &us1, &ls1) 
-						&& IsInPolygon3D(s[i][0], s[i][1], clp_pg, nclp_pg, &us2, &ls2));
-			s[i][0] -= v[0];			s[i][1] -= v[1];			s[i][2] -= v[2];
-			v[0] /= 4.0;	v[1] /= 4.0;	v[2] /= 4.0;
-			}
-		s[i][0] += v[0];			s[i][1] += v[1];			s[i][2] += v[2];
-		v[0] *= -1024.0;	v[1] *= -1024.0;	v[2] *= -1024.0;
-		res[i].x = iround(s[i][0]);	res[i].y = iround(s[i][1]);	res[i].z = iround(s[i][2]);
-		di[i] = (d=(res[i].x - pt->x))*d;		di[i] += ((d=(res[i].y - pt->y))*d);
-		}
-	if(di[0] > 1 && di[0] > di[1]) {
-		//first solution has longer projection
-		np->x = res[0].x;	np->y = res[0].y;	np->z = res[0].z;
-		return true;
-		}
-	if(di[1] > 1 && di[1] > di[0]) {
-		//second solution has longer projection
-		np->x = res[1].x;	np->y = res[1].y;	np->z = res[1].z;
-		return true;
-		}
-	return false;
-}
-
-//come here from clip_line_3D to process changes in visibility when
-//   clipping one polygon with another
-void proc_polygon(int vis, POINT3D *pnt, POINT3D *last)
-{
-	static POINT3D np, lp;
-	long d, d1;
-	bool spg_valid;
-
-	switch(ppg_level){
-	case 0:						//searching first visible point of polygon
-		if(vis == 3) vis = 1;						//on line is visible
-		if(!ppg_vis && vis && !ppg_nowvis){			//found it !
-			ppg_nowvis = true;			ppg_first.x = pnt->x;
-			ppg_first.y = pnt->y;		ppg_first.z = pnt->z;
-			ppg_reason = vis;
-			}
-		else if(!vis && ppg_nowvis) {	//check if too short
-			d = (d1 = pnt->x - ppg_first.x) * d1;
-			d += (d1 = pnt->y - ppg_first.y) * d1;
-			if(d < 3) ppg_nowvis = false;	//cancel first point
-			}
-		ppg_vis = vis;
-		break;
-	case 1:
-		if(vis == 3) vis = 1;						//on line: visible
-		if(ppg_first.x < 0 && ppg_first.y < 0 && vis) {
-			memcpy(&ppg_first, pnt, sizeof(POINT3D));
-			ppg_firstvis = vis;			lp.x = pnt->x;
-			lp.y = pnt->y;				lp.z = pnt->z;
-			ppg_par->Command(CMD_STARTLINE, pnt, 0L);
-			}
-		else if (ppg_vis) {			//leaving visibility or continue
-			spg_valid = false;
-			if(lp.x == pnt->x && lp.y == pnt->y && lp.z == pnt->z) break;
-			if(vis) ppg_par->Command(CMD_ADDTOLINE, pnt, 0L);
-			else ppg_par->Command(CMD_ADDTOLINE, last, 0L);
-			if(!vis){				//leaving visibility
-				ppg_vis = test_plane(last->x, last->y, last->z);
-				if(ppg_vis == 3) ppg_vis = 1;
-				if(ppg_firstvis == 1 && ppg_vis == 1) {
-					//from below surface to below surface
-					spg_valid = AddShadowPolygon(last, &ppg_first, -2);
-					}
-				else if(ppg_firstvis == 1 && ppg_vis == 2) {
-					//from below surface enter inside surface
-					if(CuttingEdge(last, &np)){
-						ppg_par->Command(CMD_ADDTOLINE, &np, 0L);
-						spg_valid = AddShadowPolygon(&np, &ppg_first, seg_x_seg);
-						}
-					else spg_valid = AddShadowPolygon(last, &ppg_first, seg_x_seg);
-					}
-				else if(ppg_firstvis == 2 && ppg_vis == 1) {
-					//from inside surface to below surface
-					if(CuttingEdge(&ppg_first, &np)){
-						if(!(spg_valid = AddShadowPolygon(last, &np, seg_x_seg)))
-							ppg_par->Command(CMD_REQ_POINT, &ppg_first, 0L);
-						ppg_par->Command(CMD_ADDTOLINE, &np, 0L);
-						}
-					else spg_valid = AddShadowPolygon(last, &ppg_first, seg_x_seg);
-					}
-				else if(ppg_firstvis == 2 && ppg_vis == 2) {
-					//from inside surface to inside surface
-					// nothing to do: connect straight
-					spg_valid = true;
-					}
-				//prepare for new polygon
-				if(spg_valid) {
-					ppg_first.x = ppg_first.y = ppg_first.z = -1;
-					}
-				else {
-					//we could not find a proper connection between the two points
-					//   probably due to high complexity of graph or shape.
-					//   We ignore part of the polygon and continue with the
-					//   started shape.
-					vis = ppg_vis;
-					}
-				}
-			}
-		ppg_vis = vis;				lp.x = pnt->x;
-		lp.y = pnt->y;				lp.z = pnt->z;
-		break;
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//test if line is available in 3D-polygons: determine shared edges
-bool LineInPolgon(POINT3D *p1, POINT3D *p2, POINT3D *tpg, int ntpg)
-{
-	int i;
-
-	for(i = 1; i < ntpg; i++) {
-		if(p2->x == tpg[i].x && p2->y == tpg[i].y && p2->z == tpg[i].z) {
-			if(p1->x == tpg[i-1].x && p1->y == tpg[i-1].y && p1->z == tpg[i-1].z) return true;
-			}
-		if(p1->x == tpg[i].x && p1->y == tpg[i].y && p1->z == tpg[i].z) {
-			if(p2->x == tpg[i-1].x && p2->y == tpg[i-1].y && p2->z == tpg[i-1].z) return true;
-			}
-		}
-	i = ntpg -1;
-	if(p1->x == tpg[i].x && p1->y == tpg[i].y && p1->z == tpg[i].z &&
-		p2->x == tpg[0].x && p2->y == tpg[0].y && p2->z == tpg[0].z) return true;
-	if(p2->x == tpg[i].x && p2->y == tpg[i].y && p2->z == tpg[i].z &&
-		p1->x == tpg[0].x && p1->y == tpg[0].y && p1->z == tpg[0].z) return true;
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//entry points for clipping requests
-
-void clip_line_sphere(GraphObj *par, POINT3D **li, int r, int cx, int cy, int cz)
-{
-	sph_r2 = r*r;	sph_x = cx;		sph_y = cy;		sph_z = cz;
-	if(test_sphere(li[0]->x, li[0]->y, li[0]->z)) par->Command(CMD_STARTLINE, li[0], 0L);
-	clip_line_3D(par, &li[0][0], &li[0][1], test_sphere);
-}
-
-void clip_line_plane(GraphObj *par, POINT3D **li, POINT3D *pg, int np, double *vec)
-{
-	nclp_pg = np;		clp_pg = pg;	vclp_pg = vec;
-	if(test_plane(li[0]->x, li[0]->y, li[0]->z)) par->Command(CMD_STARTLINE, li[0], 0L);
-	clip_line_3D(par, &li[0][0], &li[0][1], test_plane);
-	vclp_pg = 0L;
-}
-
-void clip_sphline_sphere(GraphObj *par, POINT3D *lim1, POINT3D *lim2, POINT3D *cent, 
-	int r1, int r2, int cx, int cy, int cz)
-{
-	sphlim1 = lim1;		sphlim2 = lim2;
-	sph_r2 = r2*r2;	sph_x = cx;		sph_y = cy;		sph_z = cz;	
-	clip_spher_line(par, cent, r1, lim1->x == lim2->x, test_sphere);
-}
-
-void clip_sphline_plane(GraphObj *par, POINT3D *lim1, POINT3D *lim2, POINT3D *cent, 
-	int r1, POINT3D *pg, int np, double *vec)
-{
-	sphlim1 = lim1;		sphlim2 = lim2;		nclp_pg = np;
-	clp_pg = pg;		vclp_pg = vec;
-	clip_spher_line(par, cent, r1, lim1->x == lim2->x, test_planeandline);
-	vclp_pg = 0L;
-}
-
-void clip_plane_plane(GraphObj *par, POINT3D *pg1, int n1, double *v1, POINT3D *pg2, 
-	int n2, double *v2, POINT *mask, int nmask)
-{
-	int i, j;
-	POINT3D sp;
-
-	nclp_pg = n2;	clp_pg = pg2;	ppg_level = 0;	ppg_nowvis = false;
-	ppg_reason = 0;	vclp_pg = v2;
-    ppg_vis = test_plane(pg1[0].x, pg1[0].y, pg1[0].z);
-	ppg_act = pg1;	ppg_nact = n1;	ppg_vec = v1;
-	for(i = 1; i < n1 && !ppg_nowvis; i++){		//assume first pnt == last pnt
-		if(!LineInPolgon(&pg1[i-1], &pg1[i], pg2, n2))
-			clip_line_3D(0L, &pg1[i-1], &pg1[i], test_plane);
-		}
-	if(!ppg_nowvis && !ppg_vis) {				//complete pg hidden
-		ppg_vec = vclp_pg = 0L;		return;
-		}
-	if(ppg_nowvis) {							//clip this polygon
-		ppg_mask = mask;		ppg_nmask = nmask;
-		ppg_level = 1;			ppg_par = par;
-		sp.x = ppg_first.x;		sp.y = ppg_first.y;
-		sp.z = ppg_first.z;		ppg_first.x = ppg_first.y = ppg_first.z = -1;
-		ppg_vis = test_plane(sp.x, sp.y, sp.z);
-		proc_polygon(ppg_vis, &sp, &sp);
-		clip_line_3D(0L, &sp, &pg1[i-1], test_plane);
-		for(j = i+n1-1; i < j; i++) {
-			seg_x_seg = -2;
-			clip_line_3D(0L, &pg1[(i-1)%n1], &pg1[i%n1], test_plane);
-			}
-		clip_line_3D(0L, &pg1[(i-1)%n1], &sp, test_plane);
-		}
-	else {										//all visible
-		par->Command(CMD_STARTLINE, pg1, 0L);
-		for(i = 1; i < n1; i++) {
-			par->Command(CMD_ADDTOLINE, &pg1[i], 0L);
-			}
-		}
-	ppg_vec = vclp_pg = 0L;
-}
+//Utils.cpp, Copyright (c) 2000-2008 R.Lackner
+//Collection of utility functions and classes for RLPlot
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+#include <math.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include "rlplot.h"
+
+extern GraphObj *CurrGO;			//Selected Graphic Objects
+extern Label *CurrLabel;
+extern Default defs;
+extern UndoObj Undo;
+
+char TmpTxt[TMP_TXT_SIZE];
+
+//----------------------------------------------------------------------------
+// Get the rectanpular part of bitmap. Used for screen updates
+//----------------------------------------------------------------------------
+anyOutput *GetRectBitmap(RECT *rc, anyOutput *src)
+{
+	RECT cr;
+	anyOutput *bm;
+
+	if(!rc || !src) return 0L;
+	src->ActualSize(&cr);
+	if(rc->left < cr.left) rc->left = cr.left;
+	if(rc->right > cr.right) rc->right = cr.right;
+	if(rc->top < cr.top) rc->top = cr.top;
+	if(rc->bottom > cr.bottom) rc->bottom = cr.bottom;
+	if(rc->left == rc->right) return 0L;
+	if(rc->top == rc->bottom) return 0L;
+	if(!(bm = NewBitmapClass(rc->right - rc->left, rc->bottom - rc->top, 
+		src->hres, src->vres)))return 0L;
+	bm->CopyBitmap(0, 0, src, rc->left, rc->top, rc->right - rc->left, 
+		rc->bottom - rc->top, false);
+	return bm;
+}
+
+void RestoreRectBitmap(anyOutput **pmo, RECT *mrc, anyOutput *o)
+{
+	if(pmo && *pmo && o && mrc) {
+		o->CopyBitmap(mrc->left, mrc->top, *pmo, 0, 0, mrc->right - mrc->left, 
+			mrc->bottom - mrc->top, false);
+		DelBitmapClass(*pmo);		*pmo = 0L;
+		o->UpdateRect(mrc, false);
+		}
+}
+
+//----------------------------------------------------------------------------
+// Axis utility functions
+//----------------------------------------------------------------------------
+void NiceAxis(AxisDEF *axis, int nTick)
+{
+	double diff, logStep, Step, Magn, LoVal, HiVal;
+	int i;
+
+	axis->Start = axis->min;
+	axis->Step = (axis->max - axis->min)/((double)nTick);
+	diff = axis->max - axis->min;
+	if(axis->breaks) for(i = 0; i < axis->nBreaks; i++) {
+		diff -= fabs(axis->breaks[i].fy - axis->breaks[i].fx);
+		}
+	if(diff <= 0.0) return;
+	logStep = log10(diff/(double)nTick);
+	Magn = floor(logStep);			logStep -= Magn;
+	if(logStep > 0.8) Step = 10.0;
+	else if(logStep > 0.5) Step = 5.0;
+	else if(logStep > 0.2) Step = 2.0; 
+	else Step = 1.0;
+	Step *= pow(10.0, Magn);		HiVal = LoVal = Step * floor(axis->min/Step);
+	axis->max += (diff * 0.05);
+	while(HiVal < axis->max) HiVal += Step;
+	if((axis->flags & AXIS_LOG) == AXIS_LOG) {
+		if (LoVal > defs.min4log) axis->min = LoVal;
+		if ((LoVal + Step) > defs.min4log && (LoVal + Step) < axis->min) axis->min = LoVal+Step;
+		}
+	else axis->min = LoVal;
+	axis->max = HiVal;		axis->Start = axis->min;		axis->Step = Step;
+}
+
+void NiceStep(AxisDEF *axis, int nTick)
+{
+	double diff, d, logStep, Step, Magn;
+	int i;
+
+	diff = axis->max - axis->min;		d = axis->Step != 0.0 ? diff/axis->Step : HUGE_VAL;
+	if((d - floor(d)) < 0.1 && axis->Step != 0.0 && diff/axis->Step < 12.0)return;
+	if(axis->breaks) for(i = 0; i < axis->nBreaks; i++) {
+		diff -= fabs(axis->breaks[i].fy - axis->breaks[i].fx);
+		}
+	if(diff <= 0.0) return;
+	logStep = log10(diff/(double)nTick);
+	Magn = floor(logStep);				logStep -= Magn;
+	if(logStep > 0.8) Step = 10.0;
+	else if(logStep > 0.5) Step = 5.0;
+	else if(logStep > 0.2) Step = 2.0; 
+	else Step = 1.0;
+	Step *= pow(10.0, Magn);			axis->Step = Step;
+}
+
+double base4log(AxisDEF *axis, int direc)
+{
+	double lv, Step = 1.0, Magn;
+	int cmd;
+
+	switch (direc) {
+	case 0:		cmd = SIZE_BOUNDS_XMIN;		break;
+	case 1:		cmd = SIZE_BOUNDS_YMIN;		break;
+	case 2:		cmd = SIZE_BOUNDS_ZMIN;		break;
+	default:	return 1.0;
+		}
+	lv = axis->min > defs.min4log ? axis->min : defs.min4log;
+	if(lv <= defs.min4log) return defs.min4log;
+	lv = log10(lv);
+	lv -= (Magn = floor(lv));
+	if(lv > 0.301) Step = 2.0; 
+	if(lv > 0.699) Step = 5.0;
+	Step *= pow(10.0, Magn);
+	return Step > defs.min4log ? Step : 1.0;
+}
+
+double TransformValue(AxisDEF *axis, double val, bool transform)
+{
+	int i;
+	double f1, f2, RetVal = val;
+
+	if(!axis) return val;
+	if(axis->breaks) {
+		for (i = 0; i < axis->nBreaks; i++) {
+			f1 = axis->breaks[i].fx;	f2 = axis->breaks[i].fy;
+			if(val > f2) RetVal -= (f2-f1);
+			else if(val > f1 && val <= f2) RetVal -= (val-f1);
+			}
+		}
+	else (axis->nBreaks = 0);
+	if(transform) {
+		switch(axis->flags & 0x7000L) {
+		case AXIS_LINEAR:	break;
+		case AXIS_LOG:
+			if(axis->flags & AXIS_RADIAL) RetVal = fabs(RetVal);
+			RetVal = RetVal > defs.min4log ? log10(RetVal): log10(defs.min4log);
+			break;
+		case AXIS_RECI:		RetVal = RetVal > defs.min4log || RetVal < - defs.min4log ?
+				1.0/RetVal : 0.0;		break;
+		case AXIS_SQR:		RetVal = RetVal >= 0.0 ? sqrt(RetVal) : 0.0;					break;
+			}
+		}
+	return RetVal;
+}
+
+void SortAxisBreaks(AxisDEF *axis)
+{
+	int i, j;
+	double ftmp;
+	bool sorted;
+
+	if(!axis || !axis->nBreaks || !axis->breaks) return;
+	//low values first
+	for(i = 0; i < axis->nBreaks; i++) {
+		if(axis->breaks[i].fy < axis->breaks[i].fx) {
+			ftmp = axis->breaks[i].fx;
+			axis->breaks[i].fx = axis->breaks[i].fy;
+			axis->breaks[i].fy = ftmp;
+			}
+		}
+	//a simple bubble sort should do
+	if(axis->nBreaks >1) do {
+		sorted = true;
+		for(i = 1; i < axis->nBreaks; i++) {
+			if(axis->breaks[i-1].fx > axis->breaks[i].fx) {
+				ftmp = axis->breaks[i-1].fx;
+				axis->breaks[i-1].fx = axis->breaks[i].fx;
+				axis->breaks[i].fx = ftmp;
+				ftmp = axis->breaks[i-1].fy;
+				axis->breaks[i-1].fy = axis->breaks[i].fy;
+				axis->breaks[i].fy = ftmp;
+				sorted = false;
+				}
+			}
+		}while(!sorted);
+	//combine overlapping ranges
+	if((j = axis->nBreaks) >1) for(i = j = 1; i < axis->nBreaks; i++) {
+		if(axis->breaks[i].fx > axis->breaks[j-1].fy) {
+			axis->breaks[j].fx = axis->breaks[i].fx;
+			axis->breaks[j].fy = axis->breaks[i].fy;
+			j++;
+			}
+		else {
+			j--;
+			axis->breaks[j++].fy = axis->breaks[i].fy;
+			}
+		}
+	axis->nBreaks = j;
+}
+
+double GetAxisFac(AxisDEF *axis, double delta, int direc)
+{
+	double da, v1, v2;
+
+	switch(axis->flags & 0x7000L) {
+	case AXIS_LOG:		
+		axis->max = axis->max > defs.min4log ? log10(axis->max): log10(defs.min4log);
+		axis->min = axis->min > defs.min4log ? log10(axis->min): 
+			log10(base4log(axis, direc));
+		if(axis->max <= axis->min) axis->max = axis->min +1.0;
+		break;
+	case AXIS_RECI:
+		v1 = fabs(axis->min) >defs.min4log ? axis->min :
+			base4log(axis, direc);
+		if(fabs(v1) > defs.min4log) v1 = 1.0/v1;
+		else v1 = 1.0e+34;
+		if(fabs(axis->max) >defs.min4log) v2 = 1.0/axis->max;
+		else v2 = 0.0;
+		if(fabs(v2) < fabs(v1/10.0)) v2 = 0.0;
+		axis->min = v2;		axis->max = v1;
+		break;
+	case AXIS_SQR:
+		axis->max = axis->max > defs.min4log ? sqrt(axis->max) : 0.0;
+		axis->min = axis->min > defs.min4log ? sqrt(axis->min) : 0.0;
+		break;
+		}
+	v2 = TransformValue(axis, axis->max, false);	//process breaks
+	v1 = TransformValue(axis, axis->min, false);
+	da = v2 != v1 ? v2 - v1 : 1.0;
+	return delta / da;
+}
+
+//----------------------------------------------------------------------------
+// Text utility functions: internationalization and formats
+//----------------------------------------------------------------------------
+//remove leading/trailing whitespace
+char *str_ltrim(char *str) {
+	int i, j;
+
+	if(!str || !str[0]) return str;
+	for(i = 0; str[i] && str[i] <= ' '; i++);
+	for(j = 0; str[i]; str[j++] = str[i++]);
+	str[j++] = '\0';	return str;
+	}
+
+char *str_rtrim(char *str) {
+	size_t i;
+
+	i = strlen(str);
+	while(i > 0 && str[i-1] <= ' ') str[--i] = '\0';
+	return str;
+}
+
+char *str_trim(char *str) {
+	str = str_ltrim(str);			return str_rtrim(str);
+}
+
+//remove leading and tailing quotatation
+void rmquot(char *str)
+{
+	size_t i, len;
+	char c;
+
+	if(str && str[0] && (*str == '"' || *str == '\'')) {
+		len = strlen(str);		c = *str;
+		if(str[len-1] == c) {
+			str[len-1] = 0;
+			for(i = 1; i < len; str[i-1] = str[i++]);
+			}
+		}
+}
+
+int strpos(char *needle, char *haystack)
+{
+	int i, j;
+
+	if(!needle || !needle[0] || !haystack || !haystack[0]) return -1;
+	for(i = j = 0; haystack[i]; i++, j=0) {
+		if(haystack[i] == needle[0]) for (j = 1; haystack[i+j]; j++) {
+			if(needle[j] != haystack[i+j]) break; 
+			}
+		if(j && !needle[j]) return i;
+		}
+	return -1;
+}
+
+char *strreplace(char *needle, char *replace, char *haystack)
+{
+	static char *result = 0L;
+	static size_t reslen = 0;
+	size_t i, j, k, l;
+
+	if(!needle || !needle[0] || !haystack || !haystack[0]) return result;
+	if(!result) result = (char*)malloc(reslen = 100);
+	result[0] = 0;		l = strlen(needle);
+	for(i = j = k = 0; haystack[i]; i++, j=0) {
+		if(haystack[i] == needle[0]) for (j = 1; haystack[i+j]; j++) {
+			if(needle[j] != haystack[i+j]) break; 
+			}
+		if(j && !needle[j]) {
+			if(replace && replace[0]) {
+				if(reslen < (i + (int)strlen(replace) + 10)) {
+					result = (char*)realloc(result, reslen += 100);
+					}
+				for(j = 0; replace[j]; j++) result[k++] = replace[j];
+				}
+			i += (l-1);
+			}
+		else result[k++] = haystack[i];
+		}
+	result[k++] = 0;
+	return result;
+}
+
+char *substr(char *text, int pos1, int pos2)
+{
+	static char *result = 0L;
+	static size_t reslen = 0;
+	int i, j;
+	size_t l;
+
+	if(!text || !text[0]) return 0L;
+	l = strlen(text);
+	if(pos1 < 0) pos1 = 0;			if(pos2 < pos1) pos2 = (int)(l+1);
+	if(!result) result = (char*)malloc(reslen = 100);
+	while (reslen < l) result = (char*) realloc(result, reslen += 100);
+	for(i = 0; i < pos1 && text[i]; i++);
+	for(j = 0; i <= pos2 && text[i]; result[j++] = text[i++]);
+	result[j] = 0;
+	return result;
+}
+
+//copy string in src to dest, returning the stringlength of src
+//   seek better solution for long strings
+int rlp_strcpy(char*dest, int size, char*src)
+{
+	int i;
+
+	if(dest && src) {
+		for(i = 0; i < size; i++) {
+			if(!(dest[i] = src[i])) return i;
+			}
+		dest[i-1] = 0;
+		return i-1;
+		}
+	return 0;
+}
+
+// restyle formula
+void ReshapeFormula(char **text)
+{
+	int i, j, l;
+
+	if(!text || !*text || !**text) return;
+	l = (int)strlen(*text);
+	for(i = j = 0; i < l; i++) {
+		if((*text)[i] == ';') {
+			if((*text)[i+1] == ';' || (*text)[i+1] == '\n') i++;
+			}
+		TmpTxt[j++] = (*text)[i];
+		}
+	TmpTxt[j] = 0;
+	if(j && j <= l && TmpTxt[0]) {
+		rlp_strcpy(*text, l+1, TmpTxt);
+		}
+}
+
+//translate anyResult to output format
+void TranslateResult(anyResult *res)
+{
+	static char tr_text[80];
+
+	switch (res->type) {
+	case ET_VALUE:
+		if(res->value == HUGE_VAL) rlp_strcpy(tr_text, 80, "inf");
+		else if(res->value == -HUGE_VAL) rlp_strcpy(tr_text, 80, "-inf");
+#ifdef USE_WIN_SECURE
+		else sprintf_s(tr_text, 80, "%g", res->value);
+#else
+		else sprintf(tr_text, "%g", res->value);
+#endif
+		res->text = tr_text;				return;
+	case ET_BOOL:
+		rlp_strcpy(tr_text, 80, ((int)res->value) ? (char*)"true" : (char*)"false"); 
+		res->text = tr_text;				return;
+	case ET_DATE:
+		rlp_strcpy(tr_text, 80, value_date(res->value, defs.fmt_date)); 
+		res->text = tr_text;				return;
+	case ET_TIME:
+		rlp_strcpy(tr_text, 80, value_date(res->value, defs.fmt_time)); 
+		res->text = tr_text;				return;
+	case ET_DATETIME:
+		rlp_strcpy(tr_text, 80, value_date(res->value, defs.fmt_datetime)); 
+		res->text = tr_text;				return;
+	case ET_TEXT:
+		if(res->text && res->text[0])		return;
+		}
+	if(!(res->text)) res->text="";
+}
+
+//remove invalid tag combinations from string
+void CleanTags(char *txt, int *i1, int *i2, int *i3)
+{
+	char *no_tags[] = {"<b></b>", "</b><b>", "<b><b>", "</b></b>",
+		"<i></i>", "</i><i>", "<i><i>", "</i></i>", "<u></u>",
+		"</u><u>", "<u><u>", "</u></u>", "<sub></sub>",
+		"</sub><sub>", "<sup></sup>", "</sup><sup>", 
+		0L};
+	int i, j, k, l, w;
+	bool na;
+
+	for(i = j = 0; txt[i]; i++) {
+		if(txt[i] != '<') txt[j++] = txt[i];
+		else {
+			for(k = 0, na = true; no_tags[k]; k++) {
+				for(l=1; no_tags[k][l] && txt[i+l]; l++) 
+					if(no_tags[k][l] != txt[i+l]) break;
+				if(!no_tags[k][l]){
+					na = false;
+					i += ((w=(int)strlen(no_tags[k]))-1);
+					if(i1 && *i1 > i) *i1 -= w;
+					if(i2 && *i2 > i) *i2 -= w;
+					if(i3 && *i3 > i) *i3 -= w;
+					break;
+					}
+				}
+			if(na) txt[j++] = txt[i];
+			}
+		}
+	txt[j++] = 0;
+}
+
+//replace one character in string
+void ChangeChar(char *text, char c1, char c2)	//replace one char in string
+{
+	int i;
+
+	for(i = 0; text[i]; i++) if (text[i] == c1) text[i] = c2;
+}
+
+char *Int2Nat(char *text)	//format ASCII number to locale format
+{
+	int i;
+
+	for(i = 0; text[i]; i++) if (text[i] == '.') text[i] = defs.DecPoint[0];
+	return text;
+}
+
+char *Nat2Int(char *text)	//format locale number to intranational notation
+{
+	int i;
+
+	for(i = 0; text[i]; i++) if (text[i] == defs.DecPoint[0]) text[i] = '.';
+	return text;
+}
+
+void WriteNatFloatToBuff(char *buff, double val)
+{
+#ifdef USE_WIN_SECURE
+	sprintf_s(buff, 20, " %g", val);
+#else
+	sprintf(buff, " %g", val);
+#endif
+	Int2Nat(buff);
+}
+
+bool Txt2Flt(char *txt, double *val)
+{
+	char *tmp = 0L;
+
+	if(txt && txt[0] && val) {
+		if(!txt[1] && (txt[0] == defs.DecPoint[0] || txt[0] < '0' ||
+			txt[0] > '9'))return false;
+		if(txt && txt[0] && (tmp = (char*)memdup(txt, (int)strlen(txt)+1, 0))){
+			Nat2Int(tmp);
+			//the return value of sscanf only roughly identifies a number
+#ifdef USE_WIN_SECURE
+			if(!sscanf_s(tmp,"%lf", val)){
+#else
+			if(!sscanf(tmp,"%lf", val)){
+#endif
+				free(tmp);
+				return false;
+				}
+			free(tmp);
+			return true;
+			}
+		}
+	return false;
+}
+
+void RmTrail(char *txt)
+{
+	int i;
+
+	i = (int)strlen(txt);
+	while(i >0 && (txt[i-1] == '0' || txt[i-1] < 32)) txt[--i] = 0;
+	if(i > 1 && txt[i-1] == '.') txt[i-1] = 0;
+}
+
+double NiceValue(double fv)
+{
+	double sign = fv > 0.0f ? 1.0 : -1.0;
+	double fa = fabs(fv);
+	double magn = floor(log10(fa));
+	int i = iround(fa/pow(10.0, magn-1.0));
+
+	return sign*pow(10.0, magn-1.0) *(double)i;
+}
+
+char *NiceTime(double val)
+{
+	rlp_datetime dt;
+
+	parse_datevalue(&dt, val);
+	if(dt.year > 1905) {
+		if(dt.hours) return date2text(&dt, defs.fmt_datetime);
+		else return date2text(&dt, defs.fmt_date);
+		}
+	else return date2text(&dt, defs.fmt_time);
+}
+
+char *Int2ColLabel(int nr1, bool uc)
+{
+	static char RetTxt[12];
+	int i, j, nr = nr1;
+	char base = uc ? 'A' : 'a';
+
+#ifdef USE_WIN_SECURE
+	sprintf_s(RetTxt+8, 4, "%c\0", base + (nr %26));
+#else
+	sprintf(RetTxt+8, "%c\0", base + (nr %26));
+#endif
+	nr /= 26;
+	for (i = 7; nr && i>=0; i--) {
+		j = nr %27;
+		RetTxt[i] = base + (j ? j-1 : j);
+		if (nr == 26) nr = 0;
+		else nr = nr/26;
+		}
+	return RetTxt+i+1;
+}
+
+char *mkCellRef(int row, int col)
+{
+	static char RetTxt[20];
+
+#ifdef USE_WIN_SECURE
+	sprintf_s(RetTxt, 20, "%s%d", Int2ColLabel(col, false), row+1);
+#else
+	sprintf(RetTxt, "%s%d", Int2ColLabel(col, false), row+1);
+#endif
+	return RetTxt;
+}
+
+char *mkRangeRef(int r1, int c1, int r2, int c2)
+{
+	static char RetTxt[40];
+	int cb;
+
+	cb = rlp_strcpy(RetTxt, 20, mkCellRef(r1, c1)); 
+	RetTxt[cb++] = ':';
+	rlp_strcpy(RetTxt+cb, 40-cb, mkCellRef(r2, c2));
+	return RetTxt;
+}
+//convert strings to XML specifications
+//this offers a limited support for special characters
+char *str2xml(char *str, bool bGreek)
+{
+	int i, j;
+	wchar_t wc;
+
+	if(!str) return 0L;
+	for(i = j = 0; str[i] && j < 4090; i++) {
+		switch(str[i]) {
+		case '"':
+			j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, """);
+			break;
+		case '&':
+			j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, "&");
+			break;
+		case '<':
+			j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, "<");
+			break;
+		case '>':
+			j += rlp_strcpy(TmpTxt+j, TMP_TXT_SIZE-j, ">");
+			break;
+		default:
+#ifdef USE_WIN_SECURE
+			if(bGreek && str[i] >= 'A' && str[i] <= 'Z') j += sprintf_s(TmpTxt+j, 20, "&#%d;", str[i] - 'A' + 0x391);
+			else if(bGreek && str[i] >= 'a' && str[i] <= 'z') j += sprintf_s(TmpTxt+j, 20, "&#%d;", str[i] - 'a' + 0x3B1);
+#else
+			if(bGreek && str[i] >= 'A' && str[i] <= 'Z') j += sprintf(TmpTxt+j, "&#%d;", str[i] - 'A' + 0x391);
+			else if(bGreek && str[i] >= 'a' && str[i] <= 'z') j += sprintf(TmpTxt+j, "&#%d;", str[i] - 'a' + 0x3B1);
+#endif
+			else if((unsigned char)str[i] <= 127) TmpTxt[j++]=str[i];
+			else {
+				if(mbtowc(&wc, str+i, 1) >0) 
+#ifdef USE_WIN_SECURE
+					j += sprintf_s(TmpTxt+j, TMP_TXT_SIZE-j, "&#%d;", ((unsigned short)wc));
+#else
+					j += sprintf(TmpTxt+j, "&#%d;", ((unsigned short)wc));
+#endif
+				}
+			}
+		}
+	TmpTxt[j] = 0;
+	return(TmpTxt);
+}
+
+// split string str into array of strings using sep as separator
+// return number of lines created in nl
+static char  *split_buf = 0L;
+static int split_buf_size, split_buf_pos;
+char **split(char *str, char sep, int *nl)
+{
+	int i, j, l, ns;
+	char **ptr;
+
+	if(!str || !str[0] || !sep) return 0L;
+	split_buf_pos = 0;
+	add_to_buff(&split_buf, &split_buf_pos, &split_buf_size, str, 0);
+	if(!split_buf || !split_buf_pos) return 0L;
+	for(i = ns = 0; i < split_buf_pos; i++) if(split_buf[i] == sep) ns++;
+	if(!(ptr = (char**)calloc(ns+2, sizeof(char*)))) return 0L;
+	for(i = j = l = 0; i < split_buf_pos; i++) {
+		if(split_buf[i] == sep) {
+			split_buf[i] = 0;
+			ptr[l++] = (char*)memdup(split_buf+j, (int)strlen(split_buf+j)+1, 0);
+			j = i+1;
+			}
+		}
+	ptr[l++] = (char*)memdup(split_buf+j, (int)strlen(split_buf+j)+1, 0);
+	if(nl) *nl = l;
+	return ptr;
+}
+
+char *fit_num_rect(anyOutput *o, int max_width, char *num_str)
+{
+	int i, j, k, w, h, slen;
+	char mant[30], expo[30], fmt[20];
+	double num;
+
+	o->oGetTextExtent(num_str, slen = (int)strlen(num_str), &w, &h);
+	if(w < (max_width-5)) return num_str;
+	//first try to remove leading zero from exponent
+	for(i = 0; i < slen; i++) if(num_str[i] == 'e') break;
+	if(num_str[i] == 'e') while (num_str[i+2] == '0') {
+		for(j = i+2; num_str[j]; j++) num_str[j] = num_str[j+1];
+		o->oGetTextExtent(num_str, slen = (int)strlen(num_str), &w, &h);
+		if(w < (max_width-5)) return num_str;
+		}
+	//no success: reduce the number of digit by rounding
+	for(i = k = 0; i <= slen; i++){
+		if(num_str[i] == '.') k = i;
+		if((mant[i] = num_str[i]) == 'e' || mant[i] == 0) break;
+		}
+	if(num_str[i] =='e') mant[i] = 0;						k = i - k-1;
+	for(j = 0; num_str[i]; j++, i++) expo[j] = num_str[i];		expo[j] = 0;
+#ifdef USE_WIN_SECURE
+	sscanf_s(mant, "%lf", &num);
+#else
+	sscanf(mant, "%lf", &num);	
+#endif
+	if(k >0) do {
+#ifdef USE_WIN_SECURE
+		sprintf_s(fmt, 20, "%%.%dlf%s", k, expo);
+		slen = sprintf_s(num_str, 60, fmt, num);		//num_str is much longer than 60
+#else
+		sprintf(fmt, "%%.%dlf%s", k, expo);
+		slen = sprintf(num_str, fmt, num);
+#endif
+		k--;
+		o->oGetTextExtent(num_str, slen, &w, &h);
+		if(w < (max_width-5)) return num_str;
+		}while (k >= 0);
+	//cannot fit: return hash marks instead
+	for(i = w = 0; w < (max_width-5) && i < 11; i++) {
+		rlp_strcpy(num_str, i+2, "##########");
+		o->oGetTextExtent(num_str, i+1, &w, &h);
+		}
+	num_str[i-1] = 0;		return num_str;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// utilities to add a line or number to a text buffer: memory file
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void add_to_buff(char** dest, int *pos, int *csize, char *txt, int len)
+{
+	if(!len)len = (int)strlen(txt);
+	if(!len) return;
+	if((*pos+len+1)>= *csize) {
+		*csize += 1000;
+		while(*csize < (*pos+len+1)) *csize += 1000;
+		*dest = (char*)realloc(*dest, *csize);
+		}
+	if(*dest) {
+		*pos += rlp_strcpy(*dest+*pos, len+1, txt);
+		}
+}
+
+void add_int_to_buff(char** dest, int *pos, int *csize, int value, bool lsp, int ndig)
+{
+	int len;
+	char txt[40];
+
+#ifdef USE_WIN_SECURE
+	len = sprintf_s(txt, 40, lsp ? " %d" : "%d", value);
+#else
+	len = sprintf(txt, lsp ? " %d" : "%d", value);
+#endif
+	add_to_buff(dest, pos, csize, txt, len);
+}
+
+void add_dbl_to_buff(char** dest, int *pos, int *csize, double value, bool lsp)
+{
+	int len;
+	char txt[40];
+
+#ifdef USE_WIN_SECURE
+	len = sprintf_s(txt, 40, lsp ? " %g" : "%g", value);
+#else
+	len = sprintf(txt, lsp ? " %g" : "%g", value);
+#endif
+	add_to_buff(dest, pos, csize, txt, len);
+}
+
+void add_hex_to_buff(char** dest, int *pos, int *csize, DWORD value, bool lsp)
+{
+	int len;
+	char txt[40];
+
+	if(value) {
+#ifdef USE_WIN_SECURE
+		len = sprintf_s(txt, 40, lsp ? " 0x%08x" : "0x%08x", value);
+#else
+		len = sprintf(txt, lsp ? " 0x%08x" : "0x%08x", value);
+#endif
+		add_to_buff(dest, pos, csize, txt, len);
+		}
+	else if(lsp) add_to_buff(dest, pos, csize, " 0x0", 4);
+	else add_to_buff(dest, pos, csize, "0x0", 3);
+}
+
+//----------------------------------------------------------------------------
+// bounding rectangle utilities
+//----------------------------------------------------------------------------
+void SetMinMaxRect(RECT *rc, int x1, int y1, int x2, int y2)
+{
+	if(x1 > x2) {
+		rc->left = x2;		rc->right = x1;
+		}
+	else {
+		rc->left = x1;		rc->right = x2;
+		}
+	if(y1 > y2) {
+		rc->top = y2;		rc->bottom = y1;
+		}
+	else {
+		rc->top = y1;		rc->bottom = y2;
+		}
+}
+
+void UpdateMinMaxRect(RECT *rc, int x, int y)
+{
+	if(x < rc->left) rc->left = x;
+	if(x > rc->right) rc->right = x;
+	if(y < rc->top) rc->top = y;
+	if(y > rc->bottom) rc->bottom = y;
+}
+
+void IncrementMinMaxRect(RECT *rc, int i)
+{
+	rc->left -= i;			rc->right += i;
+	rc->top -= i;			rc->bottom += i;
+}
+
+bool IsInRect(RECT *rc, int x, int y)
+{
+	if(x < rc->left || x > rc->right || 
+		y < rc->top || y > rc->bottom) return false;
+	return true;
+}
+
+//----------------------------------------------------------------------------
+// test if point (e.g. mouse position) is close to or iside a shape
+//----------------------------------------------------------------------------
+bool IsCloseToLine(POINT *p1, POINT *p2, int x, int y)
+{
+	bool bFound = false;
+	int tmp, dx, dy;
+
+	//do not process single point
+	if(p1->x == p2->x && p1->y == p2->y) return false;
+	//point must be bracketed by p1:p2
+	if((x-2) > p1->x && (x-2) > p2->x) return false;
+	if((x+2) < p1->x && (x+2) < p2->x) return false;
+	if((y-2) > p1->y && (y-2) > p2->y) return false;
+	if((y+2) < p1->y && (y+2) < p2->y) return false;
+	if(abs(dx = (p2->x - p1->x)) < abs(dy = (p2->y - p1->y))) {	//y dominant
+		tmp = (p1->x + ((y - p1->y) * dx)/dy);
+		if(x > (tmp-3) && x < (tmp+3)) bFound = true;
+		}
+	else {							// x dominant
+		tmp = (p1->y + ((x - p1->x) * dy)/dx);
+		if(y > (tmp-3) && y < (tmp+3)) bFound = true;
+		}
+	return bFound;
+}
+
+bool IsCloseToPL(POINT p, POINT *pts, int cp)
+{
+	int i;
+
+	for( i = 1; i < cp; i++) if(IsCloseToLine(pts+i-1, pts+i, p.x, p.y)) return true;
+	return false;
+}
+
+//test if poitn p is within the polygon pts of size cp
+//note: the last point of the polypon should be equal to the first point,
+//   i.e. the polygon must be closed
+int idx_point_on_line;
+bool IsInPolygon(POINT *p, POINT *pts, int cp)
+{
+	int tmp1 = 0, tmp2 = 0, tmp3, i;
+
+	for(i = 1; i < cp; i++) {
+		if((pts[i-1].x <= p->x && pts[i].x > p->x) || (pts[i-1].x > p->x && pts[i].x <= p->x)) {
+			tmp3 = ((p->x - pts[i-1].x)*(pts[i].y -pts[i-1].y))/(pts[i].x - pts[i-1].x);
+			tmp3 += pts[i-1].y;
+			if(p->y > tmp3) tmp1++;
+			else if(p->y < tmp3) tmp2++;
+			else return false;			//ignore points on the line
+			}
+		}
+	return((tmp1 & 0x1) && (tmp2 & 0x1));
+}
+
+bool IsInPolygon3D(double x, double y, POINT3D *pts, int cp, int *us, int *ls)
+{
+	int tmp1 = 0, tmp2 = 0, i, idx1, idx2;
+	double tmp3;
+
+	for(i = 1, idx1 = idx2 = -1; i < cp; i++) {
+		if((pts[i-1].x <= x && pts[i].x > x) || (pts[i-1].x > x && pts[i].x <= x)) {
+			tmp3 = ((x - pts[i-1].x)*(pts[i].y -pts[i-1].y))/(pts[i].x - pts[i-1].x);
+			tmp3 += pts[i-1].y;
+			if(y > tmp3){
+				tmp1++;				idx1 = i;
+				}
+			else if(y < tmp3) {
+				tmp2++;				idx2 = i;
+				}
+			else {			//points is on the line
+				idx1 = idx2 = idx_point_on_line = i;
+				if(us) *us = idx1;		if(ls) *ls = idx2;
+				return true;
+				}
+			}
+		}
+	//return an index to the bracketing segments of the polygon
+	if(us) *us = idx1;			if(ls) *ls = idx2;
+	return((tmp1 & 0x1) && (tmp2 & 0x1));
+}
+
+//return true if two rectangles overlap
+bool OverlapRect(RECT *rc1, RECT *rc2)
+{
+	if((rc1->left < rc2->right && rc1->right > rc2->left) ||
+		(rc2->left < rc1->right && rc2->right > rc1->left)) {
+		if((rc1->top < rc2->bottom && rc1->bottom > rc2->top) ||
+			(rc2->top < rc1->bottom && rc2->bottom > rc1->top))
+			return true;
+		}
+	return false;
+}
+
+//----------------------------------------------------------------------------
+// collect points to a polygon
+// keep number of points low by extrapolation
+//----------------------------------------------------------------------------
+void AddToPolygon(long *cp, POINT *pts, POINT *np)
+{
+	int ix, iy;
+	long i = *cp;
+
+	if(i && pts[i-1].x == np->x && pts[i-1].y == np->y) return;
+	if(i < 2) {					//accept first points of polygon
+		pts[i].x = np->x;	pts[i].y = np->y;	*cp = i+1;
+		return;
+		}
+	if(pts[i-1].x == pts[i-2].x && pts[i-1].x == np->x) {
+		if(np->y == pts[i-1].y) return;
+		if((np->y > pts[i-1].y && pts[i-1].y > pts[i-2].y) ||
+			(np->y < pts[i-1].y && pts[i-1].y < pts[i-2].y)) {
+			pts[i-1].x = np->x;		pts[i-1].y = np->y;
+			return;
+			}
+		}
+	if(pts[i-1].y == pts[i-2].y && pts[i-1].y == np->y) {
+		if(np->x == pts[i-1].x) return;
+		if((np->x > pts[i-1].x && pts[i-1].x > pts[i-2].x) ||
+			(np->x < pts[i-1].x && pts[i-1].x < pts[i-2].x)) {
+			pts[i-1].x = np->x;		pts[i-1].y = np->y;
+			return;
+			}
+		}
+	//try linear extrapolation
+	if((pts[i-1].x > pts[i-2].x && np->x > pts[i-1].x) ||
+		(pts[i-1].x < pts[i-2].x && np->x < pts[i-1].x)) {
+		ix = (pts[i-1].y != pts[i-2].y) ? (pts[i-2].x + ((np->y - pts[i-2].y) * 
+			(pts[i-1].x - pts[i-2].x))/(pts[i-1].y - pts[i-2].y)) : 0;
+		iy = (pts[i-1].x != pts[i-2].x) ? (pts[i-2].y + ((np->x - pts[i-2].x) * 
+			(pts[i-1].y - pts[i-2].y))/(pts[i-1].x - pts[i-2].x)) : 0;
+		if((ix && ix == np->x) && (iy && iy == np->y)) {
+			pts[i-1].x = np->x;		pts[i-1].y = np->y;
+			return;
+			}
+		}
+	//not explained by extrapolation, accept new point
+	pts[i].x = np->x;				pts[i].y = np->y;
+	*cp = i+1;
+	return;
+}
+
+//----------------------------------------------------------------------------
+// create a Bezier polygon
+#define MIN_SEG 11
+#define MAX_DEPTH 5
+void DrawBezier(long *cp, POINT *pts, POINT p0, POINT p1, POINT p2, POINT p3, int depth)
+{
+	int i;
+	POINT np, p01, p12, p23, p012, p123, p0123;
+	POINT *ap[] = {&p0, &p1, &p2, &p3};
+
+	depth ++;
+	if(depth > MAX_DEPTH) {
+		for(i= 0; i < 4; i++) {
+			np.x = (*ap[i]).x >> 2;		np.y = (*ap[i]).y >> 2;
+			AddToPolygon(cp, pts, &np);
+			}
+		return;
+		}
+	else if(depth == 1) for(i=0; i < 4; i++) {
+		(*ap[i]).x <<= 2;			(*ap[i]).y <<= 2;
+		}
+	p01.x = (p0.x + p1.x) >> 1;			p01.y = (p0.y + p1.y) >> 1;
+	p12.x = (p1.x + p2.x) >> 1;			p12.y = (p1.y + p2.y) >> 1;
+	p23.x = (p2.x + p3.x) >> 1;			p23.y = (p2.y + p3.y) >> 1;
+	p012.x = (p01.x + p12.x) >> 1;			p012.y = (p01.y + p12.y) >> 1;
+	p123.x = (p12.x + p23.x) >> 1;			p123.y = (p12.y + p23.y) >> 1;
+	p0123.x = (p012.x + p123.x) >> 1;		p0123.y = (p012.y + p123.y) >> 1;
+	if(abs(p0.x - p0123.x)> MIN_SEG || abs(p0.y - p0123.y)> MIN_SEG) {
+		DrawBezier(cp, pts, p0, p01, p012, p0123, depth);	//recursion: refine
+		}
+	else {
+		DrawBezier(cp, pts, p0, p01, p012, p0123, MAX_DEPTH);	//recursion: store data
+		}
+	if(abs(p3.x - p0123.x)> MIN_SEG || abs(p3.y - p0123.y)> MIN_SEG) {
+		DrawBezier(cp, pts, p0123, p123, p23, p3, depth);	//recursion: refine
+		}
+	else {
+		DrawBezier(cp, pts, p0123, p123, p23, p3, MAX_DEPTH);	//recursion: store data
+		}
+}
+#undef MAX_DEPTH
+#undef MIN_SEG
+
+// create a Bezier polygon clipped to rectangle
+static RECT BezClipRec;
+#define MIN_SEGCLP 5
+#define MAX_DEPTHCLP 6
+static void DrawBezierClip(long *cp, POINT *pts, POINT p0, POINT p1, POINT p2, POINT p3, int depth)
+{
+	int i;
+	POINT np, p01, p12, p23, p012, p123, p0123;
+	POINT *ap[] = {&p0, &p1, &p2, &p3};
+
+	depth++;
+	if(depth > MAX_DEPTHCLP) {
+		for(i= 0; i < 4; i++) {
+			np.x = (*ap[i]).x >> 2;		np.y = (*ap[i]).y >> 2;
+			if(np.x < BezClipRec.left) np.x = BezClipRec.left;
+			if(np.x > BezClipRec.right) np.x = BezClipRec.right;
+			if(np.y < BezClipRec.top) np.y = BezClipRec.top;
+			if(np.y > BezClipRec.bottom) np.y = BezClipRec.bottom;
+			AddToPolygon(cp, pts, &np);
+			}
+		return;
+		}
+	else if(depth == 1) for(i=0; i < 4; i++) {
+		(*ap[i]).x <<= 2;			(*ap[i]).y <<= 2;
+		}
+	p01.x = (p0.x + p1.x) >> 1;			p01.y = (p0.y + p1.y) >> 1;
+	p12.x = (p1.x + p2.x) >> 1;			p12.y = (p1.y + p2.y) >> 1;
+	p23.x = (p2.x + p3.x) >> 1;			p23.y = (p2.y + p3.y) >> 1;
+	p012.x = (p01.x + p12.x) >> 1;			p012.y = (p01.y + p12.y) >> 1;
+	p123.x = (p12.x + p23.x) >> 1;			p123.y = (p12.y + p23.y) >> 1;
+	p0123.x = (p012.x + p123.x) >> 1;		p0123.y = (p012.y + p123.y) >> 1;
+	if((abs(p0.x - p0123.x)> MIN_SEGCLP || abs(p0.y - p0123.y)> MIN_SEGCLP)) {
+		DrawBezierClip(cp, pts, p0, p01, p012, p0123, depth);		//recursion: refine
+		}
+	else {
+		DrawBezierClip(cp, pts, p0, p01, p012, p0123, MAX_DEPTHCLP);	//store data
+		}
+	if((abs(p3.x - p0123.x)> MIN_SEGCLP || abs(p3.y - p0123.y)> MIN_SEGCLP)) {
+		DrawBezierClip(cp, pts, p0123, p123, p23, p3, depth);		//recursion: refine
+		}
+	else {
+		DrawBezierClip(cp, pts, p0123, p123, p23, p3, MAX_DEPTHCLP);	//store data
+		}
+}
+
+void ClipBezier(long *cp, POINT *pts, POINT p0, POINT p1, POINT p2, POINT p3, POINT *clp1, POINT *clp2)
+{
+	if(clp1 && clp2){
+		SetMinMaxRect(&BezClipRec, clp1->x, clp1->y, clp2->x, clp2->y);
+		}
+	if(cp && pts) DrawBezierClip(cp, pts, p0, p1, p2, p3, 0);
+}
+#undef MIN_SEGCLP
+#undef MAX_DEPTHCLP
+
+//----------------------------------------------------------------------------
+// create a Bezier spline through data points
+static void ipol_curve(lfPOINT *p1, lfPOINT *p2, lfPOINT *p3, lfPOINT *cp1, lfPOINT *cp2)
+{
+	double dx, dy, l;
+	lfPOINT tHat;
+
+	tHat.fx = p3->fx - p1->fx;		tHat.fy = p3->fy - p1->fy;
+	l = sqrt(tHat.fx * tHat.fx + tHat.fy * tHat.fy);
+	tHat.fx /= l;					tHat.fy /= l;
+	cp1->fx = cp2->fx = p2->fx;		cp1->fy = cp2->fy = p2->fy;
+	dx = p3->fx - p2->fx;			dy = p3->fy - p2->fy;		l = sqrt(dx*dx + dy*dy)/3.0;
+	cp2->fx += (tHat.fx * l);		cp2->fy += (tHat.fy *l);
+	dx = p2->fx - p1->fx;			dy = p2->fy - p1->fy;		l = sqrt(dx*dx + dy*dy)/3.0;
+	cp1->fx -= (tHat.fx * l);		cp1->fy -= (tHat.fy *l);
+}
+
+static void mirr_vecvec(lfPOINT *a0, lfPOINT *a1, lfPOINT *v1)
+{
+	double dx, dy, as, ac, vs, vc, s, c, l;
+	lfPOINT sol1, sol2;
+
+	//mirror vector a0 -> v1 by rotation around vector a0 -> a1
+	dx = a1->fx - a0->fx;					dy = a1->fy - a0->fy;
+	l = sqrt(dx*dx + dy*dy);	as = dy/l;			ac = dx/l;
+	dx = v1->fx - a0->fx;					dy = v1->fy - a0->fy;
+	l = sqrt(dx*dx + dy*dy);	vs = dy/l;			vc = dx/l;
+	//calculate cw and ccw solution
+	s = sin((asin(as)-asin(vs))*2.0);		c = cos((acos(ac)-acos(vc))*2.0);
+	sol1.fx = dx * c + dy * s + a0->fx;		sol1.fy = dy * c - dx * s + a0->fy;
+	s = sin((asin(as)-asin(-vs))*2.0);		c = cos((acos(ac)-acos(-vc))*2.0);
+	sol2.fx = dx * c + dy * s + a0->fx;		sol2.fy = dy * c - dx * s + a0->fy;
+	//get better solution
+	dx = sol1.fx - a1->fx;					dy = sol1.fy - a1->fy;
+	l = sqrt(dx*dx + dy*dy);
+	dx = sol2.fx - a1->fx;					dy = sol2.fy - a1->fy;
+	if( l < sqrt(dx*dx + dy*dy)) {
+		v1->fx = sol1.fx;					v1->fy = sol1.fy;
+		}
+	else {
+		v1->fx = sol2.fx;					v1->fy = sol2.fy;
+		}
+}
+
+int mkCurve(lfPOINT *src, int n1, lfPOINT **dst, bool bClosed)
+{
+	int i, j, iret;
+
+	if(!src || n1 < 3) return 0;
+	if(!(*dst = (lfPOINT*)malloc((n1*3+2) * sizeof(lfPOINT))))return 0;
+	for(i = j = iret = 0; i < n1; i++, j += 3) {
+		(*dst)[j].fx = src[i].fx;		(*dst)[j].fy = src[i].fy;
+		if(i && i < (n1-1)){
+			ipol_curve(&src[i-1], &src[i], &src[i+1], *dst+j-1, *dst+j+1);
+			}
+		else if(i) {
+			iret = j+1;					break;
+			}
+		}
+	if(bClosed && iret >2) {
+		if((*dst)[j].fx != (*dst)[0].fx || (*dst)[j].fy != (*dst)[0].fy) {
+			j += 3;								iret += 3;
+			(*dst)[j].fx = (*dst)[0].fx;		(*dst)[j].fy = (*dst)[0].fy;	
+			}
+		if(j < 6) {
+			free(*dst);		*dst = 0L;				return 0;
+			}
+		ipol_curve(*dst+j-3, *dst+j, *dst+3, *dst+j-1, *dst+1);
+		ipol_curve(*dst+j-6, *dst+j-3, *dst+j, *dst+j-4, *dst+j-2);
+		}
+	else if(!bClosed && iret>3) {
+		(*dst)[1].fx = (*dst)[0].fx + (*dst)[3].fx - (*dst)[2].fx;
+		(*dst)[1].fy = (*dst)[0].fy + (*dst)[3].fy - (*dst)[2].fy;
+		mirr_vecvec(*dst, *dst+3, *dst+1);
+		(*dst)[j-1].fx = (*dst)[j].fx + (*dst)[j-3].fx - (*dst)[j-2].fx;
+		(*dst)[j-1].fy = (*dst)[j].fy + (*dst)[j-3].fy - (*dst)[j-2].fy;
+		mirr_vecvec(*dst+j, *dst+j-3, *dst+j-1);
+		}
+	else {
+		free(*dst);			*dst = 0L;				return 0;
+		}
+	return iret;
+}
+
+//----------------------------------------------------------------------------
+// create a circular polygon
+//use circular Bresenham's algorithm to draw arcs
+//Ref: C. Montani, R. Scopigno (1990) "Speres-To-Voxel Conversion", in:
+//   Graphic Gems (A.S. Glassner ed.) Academic Press, Inc.; 
+//   ISBN 0-12-288165-5 
+//----------------------------------------------------------------------------
+POINT *MakeArc(int ix, int iy, int r, int qad, long *npts)
+{
+	int i, x, y, di, de, lim;
+	static POINT *pts, *rpts;
+	POINT np;
+
+	if(r < 1) return 0L;
+	if(!(pts = (POINT*) malloc((r+1)*8*sizeof(POINT))))return 0L;
+	for(i = *npts = 0; i < 4; i++) {
+		x = lim = 0;	y = r;	di = 2*(1-r);
+		if(qad & (1<<i))while (y >= lim){
+			if(di < 0) {
+				de = 2*di + 2*y -1;
+				if(de > 0) {
+					x++;	y--;	di += (2*x -2*y +2);
+					}
+				else {
+					x++;	di += (2*x +1);
+					}
+				}
+			else {
+				de = 2*di -2*x -1;
+				if(de > 0) {
+					y--;	di += (-2*y +1);
+					}
+				else {
+					x++;	y--;	di += (2*x -2*y +2);
+					}
+				}
+			switch(i) {
+			case 0:	np.x = ix-y;	np.y = iy+x;	break;
+			case 1: np.x = ix+x;	np.y = iy+y;	break;
+			case 2: np.x = ix+y;	np.y = iy-x;	break;
+			case 3:	np.x = ix-x;	np.y = iy-y;	break;
+				}
+			AddToPolygon(npts, pts, &np);
+			}
+		}
+	if(*npts < 3) return 0L;
+	if(rpts = (POINT*)realloc(pts, sizeof(POINT)*(*npts+4))) return rpts;
+	return pts;
+}
+
+//----------------------------------------------------------------------------
+// display a marked line using complement colors
+//----------------------------------------------------------------------------
+void InvertLine(POINT *pts, int nPts, LineDEF *Line, RECT *rc, 
+	anyOutput *o, bool mark)
+{
+	int i;
+	LineDEF OldLine;
+
+	memcpy(&OldLine, Line, sizeof(LineDEF));
+	if(OldLine.width <= 0.0001) OldLine.width = 0.0001;
+	for(i = 0; o->un2fiy(OldLine.width) < 1.0 && i < 50; i++) OldLine.width *= 2.0;
+	OldLine.color = mark ? (Line->color & 0x00ffffffL) : 0x00ffffffL;
+	OldLine.width *= 3.0;				o->SetLine(&OldLine);
+	o->oPolyline(pts, nPts);			OldLine.width = Line->width;
+	OldLine.color = mark ? (Line->color & 0x00ffffffL) ^ 0x00ffffffL : (Line->color & 0x00ffffff);
+	o->SetLine(&OldLine);				o->oPolyline(pts, nPts);
+	if(rc) o->UpdateRect(rc, false);
+}
+
+//----------------------------------------------------------------------------
+// color utilitis
+//----------------------------------------------------------------------------
+// calculate distance between two colors
+unsigned int ColDiff(DWORD col1, DWORD col2)
+{
+	int ret = 0, d;
+
+	d = (col1 & 0xff) - (col2 & 0xff);
+	ret = isqr(d*d);
+	d = ((col1>>8) & 0xff) - ((col2>>8) & 0xff);
+	ret += isqr(d*d);
+	d = ((col1>>16) & 0xff) - ((col2>>16) & 0xff);
+	ret += isqr(d*d);
+	return ret;	
+}
+
+//----------------------------------------------------------------------------
+// interpolate between two colors
+DWORD IpolCol(DWORD color1, DWORD color2, double fact)
+{
+	DWORD col1, col2, col3, c1;
+	int i;
+
+	col1 = color1;	col2 = color2;	col3 = 0x0L;
+	for(i = 0; i < 4; i++) {
+		c1 = iround(fabs((((col1 & 0xff000000)>>24) & 0xff) * fact));
+		c1 += iround(fabs((((col2 & 0xff000000)>>24) & 0xff) *(1.0-fact)));
+		col3 |= c1 < 0xff ? c1 : 0xff;
+		col1 <<= 8;	col2 <<= 8; 
+		if(i < 3) col3 <<= 8;
+		}
+	return col3;
+}
+
+//----------------------------------------------------------------------------
+// Random number generator with low sequential correlations.
+// ran2 returns a number betwee 0.0f and 1.0f;
+// Ref: W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Veterling
+// Numerical Recipe in C, The Art of Scientific Computing
+// Cambridge University Press 1988, ISBN 0-521-35465-X
+//----------------------------------------------------------------------------
+#define M 714025
+#define IA 1366
+#define IC 150889
+double ran2(long *idum)
+{
+	static long iy, ir[98];
+	static int iff = 0;
+	int j;
+
+	if(*idum < 0 || iff == 0) {
+		iff = 1;
+		if((*idum = (IC-(*idum)) % M) < 0) *idum = -(*idum);
+		for(j = 1; j <= 97; j++) {
+			*idum = (IA*(*idum)+IC) % M;
+			ir[j] = (*idum);
+			}
+		*idum=(IA*(*idum)+IC) % M;
+		iy=(*idum);
+		}
+	j = 1+97 * iy/M;
+	if(j > 97 || j< 1) return 0.0f;		//impossible
+	iy = ir[j];
+	*idum = (IA*(*idum)+IC) % M;
+	ir[j] = (*idum);
+	return (float) iy/M;
+}
+#undef IC
+#undef IA
+#undef M
+
+//----------------------------------------------------------------------------
+// integer square root
+// calculate the largest number <= sqr(n)
+// Christopher J. Musial in Graphics Gems II, page 37ff, page 610
+// Academic Press, 1991
+// modified
+//----------------------------------------------------------------------------
+
+unsigned long int isqr(unsigned long int n)
+{
+	unsigned long int nextTrial, decrement;
+
+	if (nextTrial = n>>1) {
+		for ( ; ; ){
+			if(decrement = (nextTrial - n/nextTrial)>>1) nextTrial -= decrement;
+			else if(nextTrial * nextTrial > n) return nextTrial-1;
+			else return nextTrial;
+			}
+		}
+	return n;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// multiply two rotation matrices
+//  see: Graphic Gems, A.S. Glassner ed.; Academic Press Inc.
+//  ISBN 0-12-286165-5, p.640
+//
+bool MatMul(double a[3][3], double b[3][3], double c[3][3])
+{
+	int i, j, k;
+	bool success = true;
+
+	for(i = 0; i < 3; i++) {
+		for (j = 0; j < 3; j++) {
+			c[i][j] = 0.0;
+			for(k = 0; k < 3; k++) c[i][j] += a[i][k] * b[k][j];
+			if(c[i][j] < -.99999 || c[i][j] > .99999) success = false;
+			}
+		}
+	return success;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//Return a format string depending on the range
+//
+char *GetNumFormat(double Magn)
+{
+	if(Magn < -3.0) return("%0.2le");
+	if(Magn == -3.0) return("%0.4lf");
+	if(Magn == -2.0) return("%0.3lf");
+	if(Magn == -1.0) return("%0.2lf");
+	if(Magn == 0.0) return("%0.1lf");
+	if(Magn >= 5.0) return("%0.2le");
+	return("%0.0lf");
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//Delete a graphic object using the Id member of the class to select the 
+//   the proper destructor
+void DeleteGO(GraphObj *go)
+{
+	if(!go) return;
+	if(!go->Id) {
+		//this will also set the Id member of the class
+		go->Command(CMD_SET_DATAOBJ, 0L, 0L);
+		}
+	if(go == CurrGO) CurrGO = 0L;
+	switch(go->Id) {
+	case GO_AXIS:			delete((Axis*)go);			break;
+	case GO_TICK:			delete((Tick*)go);			break;
+	case GO_GRIDLINE:		delete((GridLine*)go);		break;
+	case GO_GRIDLINE3D:		delete((GridLine3D*)go);	break;
+	case GO_SYMBOL:			delete((Symbol*)go);		break;
+	case GO_BUBBLE:			delete((Bubble*)go);		break;
+	case GO_BAR:			delete((Bar*)go);			break;
+	case GO_ERRBAR:			delete((ErrorBar*)go);		break;
+	case GO_ARROW:			delete((Arrow*)go);			break;
+	case GO_BOX:			delete((Box*)go);			break;
+	case GO_WHISKER:		delete((Whisker*)go);		break;
+	case GO_DROPLINE:		delete((DropLine*)go);		break;
+	case GO_DATALINE:
+		//we call ~DataLine for ~DataPolygon because variables are
+		//   initialized in DataLine ??!!??!!
+		//   otherwise we would crash with ~DataPolygon.
+	case GO_DATAPOLYGON:	delete((DataLine*)go);		break;
+	case GO_SPHERE:			delete((Sphere*)go);		break;
+	case GO_PLANE:			delete((plane*)go);			break;
+	case GO_BRICK:			delete((Brick*)go);			break;
+	case GO_LINE3D:			delete((Line3D*)go);		break;
+	case GO_LABEL:			delete((Label*)go);			break;
+	case GO_MLABEL:			delete((mLabel*)go);		break;
+	case GO_SEGMENT:		delete((segment*)go);		break;
+	case GO_POLYGON:
+	case GO_POLYLINE:		delete((polyline*)go);		break;
+	case GO_REGLINE:		delete((RegLine*)go);		break;
+	case GO_SDELLIPSE:		delete((SDellipse*)go);		break;
+	case GO_ELLIPSE:		
+	case GO_ROUNDREC:
+	case GO_RECTANGLE:		delete((rectangle*)go);		break;
+	case GO_DRAGHANDLE:		delete((dragHandle*)go);	break;
+	case GO_DRAGRECT:		delete((dragRect*)go);		break;
+	case GO_DRAG3D:			delete((Drag3D*)go);		break;
+	case GO_FRAMERECT:		delete((FrmRect*)go);		break;
+	case GO_PLOT:			delete((Plot*)go);			break;
+	case GO_BARCHART:
+	case GO_PLOTSCATT:		delete((PlotScatt*)go);		break;
+	case GO_REGRESSION:		delete((Regression*)go);	break;
+	case GO_BUBBLEPLOT:		delete((BubblePlot*)go);	break;
+	case GO_BOXPLOT:		delete((BoxPlot*)go);		break;
+	case GO_DENSDISP:		delete((DensDisp*)go);		break;
+	case GO_STACKBAR:
+	case GO_WATERFALL:
+	case GO_STACKPG:		delete((StackBar*)go);		break;
+	case GO_POLARPLOT:		delete((PolarPlot*)go);		break;
+	case GO_RINGCHART:
+	case GO_PIECHART:		delete((PieChart*)go);		break;
+	case GO_GROUP:
+	case GO_STARCHART:		delete((GoGroup*)go);		break;
+	case GO_SCATT3D:		delete((Scatt3D*)go);		break;
+	case GO_PLOT3D:			delete((Plot3D*)go);		break;
+	case GO_PAGE:
+	case GO_GRAPH:			delete((Graph*)go);			break;
+	case GO_SVGOPTIONS:		delete((svgOptions*)go);	break;
+	case GO_DROPL3D:		delete((DropLine3D*)go);	break;
+	case GO_ARROW3D:		delete((Arrow3D*)go);		break;
+	case GO_LIMITS:			delete((Limits*)go);		break;
+	case GO_GRIDRADIAL:		delete((GridRadial*)go);	break;
+	case GO_DEFRW:			delete((DefsRW*)go);		break;
+	case GO_PLANE3D:		delete((Plane3D*)go);		break;
+	case GO_RIBBON:			delete((Ribbon*)go);		break;
+	case GO_FUNCTION:		delete((Function*)go);		break;
+	case GO_FITFUNC:		delete((FitFunc*)go);		break;
+	case GO_LEGITEM:		delete((LegItem*)go);		break;
+	case GO_LEGEND:			delete((Legend*)go);		break;
+	case GO_OBJTREE:		delete((ObjTree*)go);		break;
+	case GO_FREQDIST:		delete((FreqDist*)go);		break;
+	case GO_GRID3D:			delete((Grid3D*)go);		break;
+	case GO_FUNC3D:			delete((Func3D*)go);		break;
+	case GO_XYSTAT:			delete((xyStat*)go);		break;
+	case GO_FITFUNC3D:		delete((FitFunc3D*)go);		break;
+	case GO_BEZIER:			delete((Bezier*)go);		break;
+	case GO_TEXTFRAME:		delete((TextFrame*)go);		break;
+	case GO_NORMQUANT:		delete((NormQuant*)go);		break;
+	case GO_CONTOUR:		delete((ContourPlot*)go);	break;
+	default:
+#ifdef USE_WIN_SECURE
+		sprintf_s(TmpTxt, TMP_TXT_SIZE, "Cannot delete Object\nwith Id %ld", go->Id);
+#else
+		sprintf(TmpTxt, "Cannot delete Object\nwith Id %ld", go->Id);
+#endif
+		ErrorBox(TmpTxt);
+		//we do not delete the object, probably we recover
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//Delete a graphic object from a list 
+bool DeleteGOL(GraphObj ***gol, long n, GraphObj *go, anyOutput *o)
+{
+	long i;
+	int c;
+	GraphObj **g, *p;
+
+	if(!gol || !(*gol) || !go || !n) return false;
+	for (i = 0, c = 0, g = *gol; i < n; i++, g++) {
+		if(*g) {
+			c++;
+			if(*g == go) {
+				p = (*g)->parent;
+				if(o) o->HideMark();
+				Undo.DeleteGO(g, 0L, o);
+				if(c == 1) {
+					for (g++, i++ ;i < n; i++, g++) {
+						if(*g) return true;
+						}
+					if(p) Undo.DropMemory(p, (void**) gol, UNDO_CONTINUE);
+					}
+				return true;
+				}
+			}
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//backup file before writing a new one
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool BackupFile(char *FileName)
+{
+	int i;
+	FILE *TheFile;
+	char TmpFileName[512], Name[512], ext[6];
+
+	//no backup necessary if file does not exist
+	if(!(FileExist(FileName))) return true;
+	rlp_strcpy(Name, 512,FileName);
+	i = (int)strlen(Name)-1;
+	if(Name[--i] == '.') Name[i] = 0;
+	else if(Name[--i] == '.') Name[i] = 0;
+	else if(Name[--i] == '.') Name[i] = 0;
+	else return false;
+	i = 1;
+	do {
+#ifdef USE_WIN_SECURE
+		sprintf_s(ext, 6, ".%03d", i);
+		sprintf_s(TmpFileName, 512, "%s%s", Name, ext);
+		if(!(fopen_s(&TheFile, TmpFileName, "r"))) {
+			fclose(TheFile);
+			}
+		else break;
+#else
+		sprintf(ext, ".%03d", i);
+		sprintf(TmpFileName, "%s%s", Name, ext);
+		if((TheFile = fopen(TmpFileName, "r"))) fclose(TheFile);
+#endif
+		i++;
+	} while (i < 999 && TheFile);
+	if(i >= 999) {								//too many backups exist already
+		ErrorBox("Too many backup\files exist already.");
+		return false;
+		}
+	if(-1 == rename(FileName, TmpFileName)) {
+		ErrorBox("Error accessing file.");
+		return false;
+		}
+	return true;
+}
+
+bool IsRlpFile(char *FileName)
+{
+	FILE *TheFile;
+	char Line[10];
+	bool bRet = false;
+
+#ifdef USE_WIN_SECURE
+	if(fopen_s(&TheFile, FileName, "r")) return false;
+#else
+	if(0L ==(TheFile = fopen(FileName, "r"))) return false;
+#endif
+	fread(Line, 1, 8, TheFile);
+	Line[5] = 0;
+	if(0 == strcmp(Line, ";RLP "))bRet = true;
+	fclose(TheFile);
+	return bRet;
+}
+
+bool IsXmlFile(char *FileName)
+{
+	FILE *TheFile;
+	char Line[10];
+	bool bRet = false;
+
+#ifdef USE_WIN_SECURE
+	if(fopen_s(&TheFile, FileName, "r")) return false;
+#else
+	if(0L ==(TheFile = fopen(FileName, "r"))) return false;
+#endif
+	fread(Line, 1, 8, TheFile);
+	Line[6] = 0;
+	if(0 == strcmp(Line, "<?xml "))bRet = true;
+	fclose(TheFile);
+	return bRet;
+}
+
+bool FileExist(char *FileName)
+{
+	FILE *TheFile;
+
+#ifdef USE_WIN_SECURE
+	if(ENOENT == fopen_s(&TheFile, FileName, "r")) return false;
+#else
+	if(0L ==(TheFile = fopen(FileName, "r"))) {
+		if(errno == ENOENT) return false;
+		return true;
+		}
+#endif
+	fclose(TheFile);
+	return true;
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//check Object for certain properties
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool IsPlot3D(GraphObj *g)
+{
+	if(g && (g->Id == GO_PLOT3D || g->Id == GO_FUNC3D || g->Id == GO_FITFUNC3D)) return true;
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//duplicate a block of memory
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void *memdup(void *ptr, int cb_old, int cb_new)
+{
+	void *p;
+
+	if(cb_new > cb_old) {
+		if((p = calloc(cb_new, 1)) && ptr) memcpy(p, ptr, cb_old);
+		}
+	else {
+		if((p = malloc(cb_old)) && ptr) memcpy(p, ptr, cb_old);
+		}
+	return p;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//calculate angle from sin(angle)
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+double trig2deg(double si, double csi)
+{
+	double ang;
+
+	ang = acos(csi);
+	if(si < 0.0) ang *= -1.0;
+	return floor(ang * 57.29577951 +0.5);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//replace graphic object with new: typically used for undo
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool ReplaceGO(GraphObj **oldobj, GraphObj **newobj)
+{
+	newobj[1]->parent = newobj[0]->parent;
+	newobj[1]->Command(CMD_SET_DATAOBJ, newobj[0]->data, 0L);
+	*oldobj = newobj[1];
+	newobj[0]->parent = 0L;		//disable messaging
+	Undo.InvalidGO(newobj[0]);
+	DeleteGO(newobj[0]);
+	return true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//calculate a 'unique' hash value for a string
+//Ref: Corman T.H., Leiserson C.E. & Rivest R.L. (1990) Hash Functions.
+//   in: Introduction to Algorithms (MIT Press & McGraw-Hill)
+//   ISBN 0-262-03141-8 and ISBN 0-07-013143-0, pp. 226ff
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+unsigned int HashValue(unsigned char *str)
+{
+	unsigned int i = 0, ret = 0;
+
+	if(!str || !str[0]) return 0;
+	do {
+		if(str[i] > 32) ret = ((str[i]-32) + (ret <<2));
+		i++;
+		}while(str[i]);
+	return ret;
+}
+
+unsigned int Hash2(unsigned char * str)
+{
+	unsigned int i = 0, ret = 0, c;
+
+	if(!str) return 0;
+	do {
+		c = str[i++];
+		ret = ((ret * c)<<2) | c;
+		}while(str[i]);
+	return ret;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//compare data structures: return true if changed or save undo info
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool cmpLineDEF(LineDEF *l1, LineDEF *l2)
+{
+	if(!l1 || !l2 || l1 == l2) return false;	//oh, oh !
+	if(l1->width != l2->width) return true;
+	if(l1->patlength != l2->patlength) return true;
+	if(l1->color != l2->color) return true;
+	if(l1->pattern != l2->pattern) return true;
+	return false;
+}
+
+bool cmpFillDEF(FillDEF *f1, FillDEF *f2)
+{
+	if(!f1 || !f2 || f1 == f2) return false;	//oh, oh!
+	if(f1->type != f2->type || f1->color != f2->color ||
+		f1->scale != f2->scale || f1->color2 != f2->color2) return true;
+	//the hatch line is subject to a separate call to cmpLineDEF
+	return false;
+}
+
+bool cmpAxisDEF(AxisDEF *a1, AxisDEF *a2)
+{
+	int i;
+
+	if(!a1 || !a2 || a1 == a2) return false;	//oh, oh!
+	if(a1->flags != a2->flags || a1->min != a2->min || a1->max != a2->max ||
+		a1->loc[0].fx != a2->loc[0].fx || a1->loc[0].fy != a2->loc[0].fy ||
+		a1->loc[0].fz != a2->loc[0].fz || a1->loc[1].fx != a2->loc[1].fx ||
+		a1->loc[1].fy != a2->loc[1].fy || a1->loc[1].fz != a2->loc[1].fz ||
+		a1->Start != a2->Start || a1->Step != a2->Step || a1->Radius != a2->Radius ||
+		a1->Center.fx != a2->Center.fx || a1->Center.fy != a2->Center.fy ||
+		a1->nBreaks != a2->nBreaks) return true;
+	for(i = 0; i < a1->nBreaks; i++) {
+		if(a1->breaks[i].fx != a2->breaks[i].fx ||
+			a1->breaks[i].fy != a2->breaks[i].fy) return true;
+		}
+	return false;
+}
+
+bool cmpTextDEF(TextDEF *t1, TextDEF *t2)
+{
+	if(!t1 || !t2) return false;
+	if(t1->ColTxt != t2->ColTxt || t1->ColBg != t2->ColBg || t1->fSize != t2->fSize ||
+		t1->RotBL != t2->RotBL || t1->RotCHAR != t2->RotCHAR || t1->iSize != t2->iSize ||
+		t1->Align != t2->Align || t1->Mode != t2->Mode || t1->Style != t2->Style ||
+		t1->Font != t2->Font) return true;
+	if((!(t1->text) && (t2->text)) || (!(t2->text) && (t1->text))) return true;
+	if(t1->text && t2->text && strcmp(t1->text, t2->text)) return true;
+	return false;
+}
+
+// Dialog Undo utilitites
+DWORD CheckNewFloat(double *loc, double old_v, double new_v, GraphObj *par, DWORD flags)
+{
+	if(loc && old_v != new_v) {
+		Undo.ValFloat(par, loc, flags);
+		*loc = new_v;						return (flags | UNDO_CONTINUE);
+		}
+	return flags;
+}
+
+DWORD CheckNewInt(int *loc, int old_v, int new_v, GraphObj *par, DWORD flags)
+{
+	if(loc && old_v != new_v) {
+		Undo.ValInt(par, loc, flags);
+		*loc = new_v;						return (flags | UNDO_CONTINUE);
+		}
+	return flags;
+}
+
+DWORD CheckNewDword(DWORD *loc, DWORD old_v, DWORD new_v, GraphObj *par, DWORD flags)
+{
+	if(loc && old_v != new_v) {
+		Undo.ValDword(par, loc, flags);
+		*loc = new_v;						return (flags | UNDO_CONTINUE);
+		}
+	return flags;
+}
+
+DWORD CheckNewLFPoint(lfPOINT *loc, lfPOINT *old_v, lfPOINT *new_v, GraphObj *par, DWORD flags)
+{
+	if(loc && old_v && new_v && (old_v->fx != new_v->fx || old_v->fy != new_v->fy)) {
+		Undo.SaveLFP(par, loc, flags);
+		loc->fx = new_v->fx;	loc->fy = new_v->fy;	return (flags | UNDO_CONTINUE);
+		}
+	return flags;
+}
+
+DWORD CheckNewString(char **loc, char *s_old, char *s_new, GraphObj *par, DWORD flags)
+{
+	int ocb, ncb, cb;
+
+	if(s_old && s_new) {
+		if(!strcmp(s_old, s_new)) return flags;
+		ocb = (int)strlen(s_old);		ncb = (int)strlen(s_new);
+		cb = ncb > ocb ? ncb : ocb;
+		if(cb > ocb) {
+			*loc = (char*)realloc(*loc, cb * sizeof(char)+1);
+			}
+		Undo.String(par, loc, flags);	flags |= UNDO_CONTINUE;
+		if(*loc) rlp_strcpy(*loc, cb+1, s_new); 
+		}
+	else if(!s_old && s_new && s_new[0]) {
+		Undo.String(par, loc, flags);	flags |= UNDO_CONTINUE;
+		*loc = (char *)memdup(s_new, (int)strlen(s_new) +1, 0);
+		}
+	else if(s_old && s_old[0] && !s_new) {
+		Undo.String(par, loc, flags);	flags |= UNDO_CONTINUE;
+		if(*loc) *loc[0] = 0;
+		}
+	return flags;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//execute clipping of 3D objects
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+typedef struct {			//structure required by 3D Bresenhan's algorithm
+	int d, a, s, *r, l;
+	}moveDEF;
+
+long sph_r2;
+int sph_x, sph_y, sph_z, nclp_pg, seg_x_seg;
+POINT3D *clp_pg, *sphlim1, *sphlim2;
+double *vclp_pg = 0L;
+
+int test_sphere(int x, int y, int z)
+{
+	int d;
+	long ld;
+	
+	ld = (d = x-sph_x) * d;			ld += (d = y-sph_y) * d;
+	ld += (d = z-sph_z) * d;
+	return (ld > sph_r2) ? 1 : 0;
+}
+
+//use a 3D Bresenham alorithm to find z coordinates where x == lx or y == ly
+bool line3D_z(POINT3D *p1, POINT3D *p2, int lx, int ly, int *cx, int *cy, int *cz)
+{
+	moveDEF mx, my, mz, *m1, *m2, *m3;
+	int x, y, z, d1, d2;
+
+	mx.d = p2->x - p1->x;		mx.a = mx.d >= 0 ? mx.d : -mx.d;
+	mx.s = mx.d >= 0 ? 1 : -1;	mx.r = &x;		mx.l = p2->x;	x = p1->x;
+	my.d = p2->y - p1->y;		my.a = my.d >= 0 ? my.d : -my.d;
+	my.s = my.d >= 0 ? 1 : -1;	my.r = &y;		my.l = p2->y;	y = p1->y;
+	mz.d = p2->z - p1->z;		mz.a = mz.d >= 0 ? mz.d : -mz.d;
+	mz.s = mz.d >= 0 ? 1 : -1;	mz.r = &z;		mz.l = p2->z;	z = p1->z;
+	if(mx.a > my.a) {
+		if(mz.a > mx.a) {
+			m1 = &mz;		m2 = &mx;	m3 = &my;
+			}
+		else if(mz.a > my.a) {
+			m1 = &mx;		m2 = &mz;	m3 = &my;
+			}
+		else {
+			m1 = &mx;		m2 = &my;	m3 = &mz;
+			}
+		}
+	else {
+		if(mz.a > my.a) {
+			m1 = &mz;		m2 = &my;	m3 = &mx;
+			}
+		else if(mz.a > mx.a) {
+			m1 = &my;		m2 = &mz;	m3 = &mx;
+			}
+		else {
+			m1 = &my;		m2 = &mx;	m3 = &mz;
+			}
+		}
+	d1 = m2->a - (m1->a >>1);		d2 = m3->a - (m1->a >>1);
+	for(; ;) {
+		//process point at (m1.r, m2.r, m3.r);
+		if(x == lx || y == ly) {
+			*cx = x;	*cy = y;	*cz = z;
+			return true;
+			}
+		if(*(m1->r) == m1->l) return false;
+		if(d1 >= 0) {
+			*(m2->r) += m2->s;	d1 -= m1->a;
+			}
+		if(d2 >= 0) {
+			*(m3->r) += m3->s;	d2 -= m1->a;
+			}
+		*(m1->r) += m1->s;	d1 += m2->a;		d2 += m3->a;
+		}
+}
+
+//test if point is 1) outside, 2) above, 3) on the line, or 0) hidden by a plane 
+int test_plane(int x, int y, int z)
+{
+	int us, ls, us1, ls1, x1, y1, z1, x2, y2, z2;
+	static int last = 0;
+	POINT3D p1, p2;
+
+	if(IsInPolygon3D(x, y, clp_pg, nclp_pg, &us, &ls)) {
+		if(us == ls){
+			seg_x_seg = us;
+			return last = 3;		//point is on line: visible
+			}
+		if(vclp_pg) {
+			if(iround((x * vclp_pg[0] + y * vclp_pg[1] - vclp_pg[3])/vclp_pg[2]) < z)
+				return last = 2;
+			else return last = 0;
+			}
+		if(us < 1) us1 = nclp_pg -1;	else us1 = us -1;
+		if(ls < 1) ls1 = nclp_pg -1;	else ls1 = ls -1;
+		if(z < clp_pg[us1].z && z < clp_pg[us].z && z < clp_pg[ls1].z &&
+			z < clp_pg[ls].z) return last = 0;		//far below plane
+		if(z > clp_pg[us1].z && z > clp_pg[us].z && z > clp_pg[ls1].z &&
+			z > clp_pg[ls].z) return last = 2;		//far above plane
+		if(line3D_z(&clp_pg[us1], &clp_pg[us], x, -1, &x1, &y1, &z1) &&
+			line3D_z(&clp_pg[ls1], &clp_pg[ls], x, -1, &x2, &y2, &z2)){
+			if(z1 < z && z2 < z) return last = 2;
+			if(z1 >= z && z2 >= z) return last = 0;
+			p1.x = x1;	p1.y = y1; p1.z = z1;	p2.x = x2;	p2.y = y2; p2.z = z2;
+			if(line3D_z(&p1, &p2, -1, y, &x1, &y1, &z1)) {
+				if(z > z1) return last = 2;
+				else return last = 0;
+				}
+			}
+		return last;
+		}
+	return last = 1;
+}
+
+int test_planeandline(int x, int y, int z)
+{
+	int ret;
+
+	ret = test_plane(x, y, z);
+	if(ret == 3 && clp_pg && nclp_pg && idx_point_on_line < nclp_pg) {
+		if(vclp_pg) {
+			if(iround((x * vclp_pg[0] + y * vclp_pg[1] - vclp_pg[3])/vclp_pg[2]) < z)
+				return 2;
+			else return 0;
+			}
+		else {
+			// no equation for plane available
+			}
+		}
+	return ret;
+}
+
+
+void proc_polygon(int vis, POINT3D *pnt, POINT3D * last);	//prototype
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//use a 3D Bresenham algorithm to clip lines
+void clip_line_3D(GraphObj *go, POINT3D *p1, POINT3D *p2, int(*proc)(int, int, int))
+{
+	moveDEF mx, my, mz, *m1, *m2, *m3;
+	int x, y, z, d1, d2, vis;
+	bool bVisible, bDrawLater = false;
+	POINT3D p, lp;
+
+	if(p1->x == p2->x && p1->y == p2->y && p1->z == p2->z) return;
+	mx.d = p2->x - p1->x;		mx.a = mx.d >= 0 ? mx.d : -mx.d;
+	mx.s = mx.d >= 0 ? 1 : -1;	mx.r = &x;		mx.l = p2->x;	x = p1->x;
+	my.d = p2->y - p1->y;		my.a = my.d >= 0 ? my.d : -my.d;
+	my.s = my.d >= 0 ? 1 : -1;	my.r = &y;		my.l = p2->y;	y = p1->y;
+	mz.d = p2->z - p1->z;		mz.a = mz.d >= 0 ? mz.d : -mz.d;
+	mz.s = mz.d >= 0 ? 1 : -1;	mz.r = &z;		mz.l = p2->z;	z = p1->z;
+	if(mx.a > my.a) {
+		if(mz.a > mx.a) {
+			m1 = &mz;		m2 = &mx;	m3 = &my;
+			}
+		else if(mz.a > my.a) {
+			m1 = &mx;		m2 = &mz;	m3 = &my;
+			}
+		else {
+			m1 = &mx;		m2 = &my;	m3 = &mz;
+			}
+		}
+	else {
+		if(mz.a > my.a) {
+			m1 = &mz;		m2 = &my;	m3 = &mx;
+			}
+		else if(mz.a > mx.a) {
+			m1 = &my;		m2 = &mz;	m3 = &mx;
+			}
+		else {
+			m1 = &my;		m2 = &mx;	m3 = &mz;
+			}
+		}
+	bVisible = (0 != (vis = (*proc)(x, y, z)));
+	if((bDrawLater = (vis == 2)) && go) go->Command(CMD_DRAW_LATER, 0L, 0L);
+	lp.x = p1->x;	lp.y = p1->y;	lp.z = p1->z;
+	if(!go && vis) proc_polygon(vis, p1, p1);
+	d1 = m2->a - (m1->a >>1);		d2 = m3->a - (m1->a >>1);
+	for(; ;) {
+		//process point at (m1.r, m2.r, m3.r);
+		vis = (*proc)(x, y, z);
+		if(!bDrawLater && vis == 2){
+			if(go) go->Command(CMD_DRAW_LATER, 0L, 0L);
+			bDrawLater = true;
+			}
+		if(bVisible) {
+			if(!vis) {
+				p.x = x;	p.y = y;	p.z = z;
+				if(go) go->Command(CMD_ADDTOLINE, &lp, 0L);
+				else proc_polygon(vis, &p, &lp);
+				bVisible = false;
+				}
+			}
+		else if(vis){
+			p.x = x;	p.y = y;	p.z = z;
+			if(go) go->Command(CMD_STARTLINE, &p, 0L);
+			else proc_polygon(vis, &p, &lp);
+			bVisible = true;
+			}
+		if(*(m1->r) == m1->l){
+			if(vis){
+				p.x = x;	p.y = y;	p.z = z;
+				if(go) go->Command(CMD_ADDTOLINE, &p, 0L);
+				else proc_polygon(vis, &p, &lp);
+				}
+			return;
+			}
+		lp.x = x;	lp.y = y;	lp.z = z;
+		if(d1 >= 0) {
+			*(m2->r) += m2->s;	d1 -= m1->a;
+			}
+		if(d2 >= 0) {
+			*(m3->r) += m3->s;	d2 -= m1->a;
+			}
+		*(m1->r) += m1->s;	d1 += m2->a;		d2 += m3->a;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//use circular Bresenham's algorithm to clip a spherical scanline
+//Ref: C. Montani, R. Scopigno (1990) "Speres-To-Voxel Conversion", in:
+//   Graphic Gems (A.S. Glassner ed.) Academic Press, Inc.; 
+//   ISBN 0-12-288165-5 
+void clip_spher_line(GraphObj *go, POINT3D *cent, int r, bool bVert, int(*proc)(int, int, int))
+{
+	int x, y, q, di, de, lim;
+	int vis = 0, o_vis;
+	POINT3D cpos;
+
+	if(r < 1) return;
+	cpos.x = cent->x;	cpos.y = cent->y;	cpos.z = cent->z;
+	if(bVert) cpos.y -= r;
+	else cpos.x -= r;
+	for(q = 0; q < 2; q++) {
+		x = lim = 0;	y = r;	di = 2*(1-r);
+		while (y >= lim){
+			o_vis = vis;
+			if(bVert && (cpos.x < sphlim1->x || cpos.x > sphlim2->x)) vis = 0;
+			else if (cpos.y < sphlim1->y || cpos.y > sphlim2->y) vis = 0;
+			else if (cpos.x > 0 && cpos.y >0) vis = (*proc)(cpos.x, cpos.y, cpos.z);
+			if(vis != o_vis) {
+				if(vis) go->Command(CMD_STARTLINE, &cpos, 0L);
+				else go->Command(CMD_ADDTOLINE, &cpos, 0L);
+				}
+			if(di < 0) {
+				de = 2*di + 2*y -1;
+				if(de > 0) {
+					x++;	y--;	di += (2*x -2*y +2);
+					}
+				else {
+					x++;	di += (2*x +1);
+					}
+				}
+			else {
+				de = 2*di -2*x -1;
+				if(de > 0) {
+					y--;	di += (-2*y +1);
+					}
+				else {
+					x++;	y--;	di += (2*x -2*y +2);
+					}
+				}
+			switch(q) {
+			case 0:
+				if(bVert) cpos.y = cent->y - y;
+				else cpos.x = cent->x - y;
+				cpos.z = cent->z + x;
+				break;
+			case 1:
+				if(vis && y < lim) go->Command(CMD_ADDTOLINE, &cpos, 0L);
+				if(bVert) cpos.y = cent->y + x;
+				else cpos.x = cent->x + x;
+				cpos.z = cent->z + y;
+				break;
+				}
+			}
+		}
+	return;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//come here to process polygon clipping events
+int ppg_vis, ppg_level, ppg_reason, ppg_firstvis, ppg_nact, ppg_nmask;
+bool ppg_nowvis;
+POINT *ppg_mask;
+POINT3D ppg_first, *ppg_act;
+GraphObj *ppg_par;
+double *ppg_vec = 0L;
+
+// test if point is on line of 3D polygon
+//Ref: P.S. Heckbert (1990) "Digital Line Drawing", in: Graphic Gems
+//   (A.S. Glassner, ed.); Academic Press, Inc.,
+//   ISBN 0-12-286165-5
+bool IsOnLine(POINT *p1, POINT *p2, int x, int y)
+{
+	int d, ax, ay, sx, sy, dx, dy;
+	POINT tr;
+
+	dx = p2->x - p1->x;
+	if ( p2->x < p1->x) { 	ax = (-dx)<<1;		sx = -1;		}
+	else {					ax = dx <<1;		sx = 1;		}
+	dy = p2->y - p1->y;
+	if (p2->y < p1->y) {	ay = (-dy)<<1;		sy = -1;		}
+	else {					ay = dy<<1;			sy = 1;		}
+	tr.x = p1->x;			tr.y = p1->y;
+	if (ax > ay) {									// x dominant
+		d = ay - (ax >>1);
+		for ( ; ; ) {
+			if(tr.y == y && tr.x == x) return true;	//match
+			if(tr.x == p2->x) return false;
+			if (d >= 0) {tr.y += sy;		d -= ax;}
+			tr.x += sx;		d += ay;
+			}
+		}
+	else {											// y dominant
+		d = ax - (ay >>1);
+		for ( ; ; ) {
+			if(tr.x == x && tr.y == y) return true;	//match
+			if (tr.y == p2->y) return false;
+			if (d >= 0) {tr.x += sx;		d -= ay;}
+			tr.y += sy;		d += ax;
+			}
+		}
+}
+
+//use Bresenham's algorithm to complete a partially hidden polygon
+//   by the shadow of the above surface
+//Ref: P.S. Heckbert (1990) "Digital Line Drawing", in: Graphic Gems
+//   (A.S. Glassner, ed.); Academic Press, Inc.,
+//   ISBN 0-12-286165-5
+bool ShadowPolygon(POINT *p1, POINT *p2, POINT *tp, int ntp, POINT *pts, long *cp, POINT *lim)
+{
+	int d, ax, ay, sx, sy, dx, dy;
+	bool bPen, vis;
+	POINT tr, mp;
+	long d1, ndist, dist = 99999;
+
+	dx = p2->x - p1->x;
+	if ( p2->x < p1->x) { 	ax = (-dx)<<1;		sx = -1;		}
+	else {					ax = dx <<1;		sx = 1;		}
+	dy = p2->y - p1->y;
+	if (p2->y < p1->y) {	ay = (-dy)<<1;		sy = -1;		}
+	else {					ay = dy<<1;			sy = 1;		}
+	tr.x = p1->x;			tr.y = p1->y;
+	bPen = IsInPolygon(&tr, tp, ntp) || IsCloseToPL(tr, tp, ntp);
+	AddToPolygon(cp, pts, p1);				//first Point is always visible
+	if (ax > ay) {							// x dominant
+		d = ay - (ax >>1);
+		for ( ; ; ) {
+			if(abs(tr.y - lim->y) < 3 && abs(tr.x - lim->x) < 3) {
+				ndist = (d1=(tr.y - lim->y))*d1;
+				ndist += ((d1=(tr.x - lim->x))*d1);
+				if(ndist <= dist) {
+					mp.x = tr.x;	mp.y = tr.y;	dist = ndist;
+					}
+				else {
+					//mp is the closest point
+					AddToPolygon(cp, pts, &mp);
+					return false;
+					}
+				}
+			vis = IsInPolygon(&tr, tp, ntp) || IsCloseToPL(tr, tp, ntp);
+			if(bPen && !vis) {
+				AddToPolygon(cp, pts, &tr);
+				return false;				//now leaving the polygon
+				}
+			if(tr.x == p2->x) return true;	//still inside
+			if (d >= 0) {tr.y += sy;		d -= ax;}
+			bPen = vis;		tr.x += sx;		d += ay;
+			}
+		}
+	else {									// y dominant
+		d = ax - (ay >>1);
+		for ( ; ; ) {
+			if(abs(tr.x - lim->x) < 3 && abs(tr.y - lim->y) < 3) {
+				ndist = (d1=(tr.y - lim->y))*d1;
+				ndist += ((d1=(tr.x - lim->x))*d1);
+				if(ndist <= dist) {
+					mp.x = tr.x;	mp.y = tr.y;	dist = ndist;
+					}
+				else {
+					//mp is the closest point
+					AddToPolygon(cp, pts, &mp);
+					return false;
+					}
+				}
+			vis = IsInPolygon(&tr, tp, ntp) || IsCloseToPL(tr, tp, ntp);
+			if(bPen && !vis) {
+				AddToPolygon(cp, pts, &tr);
+				return false;				//now leaving the polygon
+				}
+			if (tr.y == p2->y) return true;	//still inside
+			if (d >= 0) {tr.x += sx;		d -= ay;}
+			bPen = vis;		tr.y += sy;		d += ax;
+			}
+		}
+}
+
+//find segment which is closest to point
+int FindClosestSeg(POINT3D *pg, int npg, int x, int y, int start)
+{
+	int i, i1, j, tmp, idx = -2, dx, dy, d, dist = 10000;
+	POINT p1, p2;
+
+	if(start < 1) start = 1;
+	for(j = start + npg, i1 = start; i1 < j; i1++) {
+		i = ((i1-1)%npg)+1;
+		if( i == npg) {
+			p1.x = pg[i-1].x;	p1.y = pg[i-1].y;
+			p2.x = pg[0].x;		p2.y = pg[0].y;
+			}
+		else {
+			p1.x = pg[i-1].x;	p1.y = pg[i-1].y;
+			p2.x = pg[i].x;		p2.y = pg[i].y;
+			}
+		if(p1.x != p2.x || p1.y != p2.y) {
+			d = dist;
+			if(abs(dx = (p2.x - p1.x)) < abs(dy = (p2.y - p1.y))) {	//y dominant
+				if(dy && ((p1.y >= y && p2.y < y) || (p2.y > y && p1.y <= y))) {
+					tmp = (p1.x + ((y - p1.y) * dx)/dy);	d = abs(x-tmp);
+					}
+				}
+			else {								// x dominant
+				if(dx && ((p1.x >= x && p2.x < x) || (p2.x > x && p1.x <= x))) {
+					tmp = (p1.y + ((x - p1.x) * dy)/dx);	d = abs(y-tmp);
+					}
+				}
+			if(d < dist) {
+				dist = d;	idx = i;
+				}
+			}
+		if(dist < 3) break;
+		}
+	return idx;
+}
+
+//finish a partially visible 3D polygon by its shadow of the above
+//   3D polygon
+bool AddShadowPolygon(POINT3D *pnt, POINT3D *ep, int cidx) {
+	int us, ls, i, j, k, x1, x2, y1, y2, z1, z2, idx_clppg_line;
+	long cpg1 = 0, d, d1, d2, ntp=0, ntp1=0, ntp2=0;
+	POINT *pg1 = 0L, np, *tp=0L, *tp1, *tp2, lim;	
+	POINT3D nep;
+	bool bRet = false;
+
+	d = ((d1 = (ep->x - pnt->x))*d1);
+	d += ((d1 = (ep->y - pnt->y))*d1);
+	if(d < 4) {			//propably too close
+		if(d && ppg_par) ppg_par->Command(CMD_ADDTOLINE, ep, 0L);
+		return true;	//connect
+		}
+	lim.x = ep->x;		lim.y = ep->y;
+	idx_clppg_line = FindClosestSeg(clp_pg, nclp_pg, pnt->x, pnt->y, cidx);
+	//create track from hiding polygon
+	//the ppoint 'pnt' is expected to be on the line
+	//   clp_pg[idx_clpgg_line] and clp_pg[idx_clpgg_line - 1]
+	if(!(pg1 = (POINT*)calloc(nclp_pg +4, sizeof(POINT))))return true;
+	np.x = pnt->x;	np.y = pnt->y;	AddToPolygon(&cpg1, pg1, &np);
+	j = idx_clppg_line + nclp_pg;
+	for(i = idx_clppg_line; i < j; i++) {
+		np.x = clp_pg[k = (i%nclp_pg)].x;		np.y = clp_pg[k].y;
+		AddToPolygon(&cpg1, pg1, &np);
+		}
+	//close polygon
+	np.x = pnt->x;	np.y = pnt->y;	AddToPolygon(&cpg1, pg1, &np);
+	//calculate two possible solutions
+	tp1 = (POINT*)calloc(nclp_pg+4, sizeof(POINT));
+	tp2 = (POINT*)calloc(nclp_pg+4, sizeof(POINT));
+	if(!tp1 || !tp2) {			//memory allocation error
+		free(pg1);	free(ppg_mask);	return false;
+		}
+	ShadowPolygon(&pg1[0], &pg1[1], ppg_mask, (int)ppg_nmask, tp1, &ntp1, &lim);
+	if(ntp1 == 1){				//more than one segment
+		for(i = 2; i < cpg1; i++) {
+			if(!ShadowPolygon(&pg1[i-1], &pg1[i], ppg_mask, (int)ppg_nmask, tp1, &ntp1, &lim))
+				break;
+			}
+		}
+	if(i == cpg1) {				//last segment required
+		ShadowPolygon(&pg1[i-1], &pg1[0], ppg_mask, (int)ppg_nmask, tp1, &ntp1, &lim);
+		}
+	ShadowPolygon(&pg1[0], &pg1[cpg1-1], ppg_mask, (int)ppg_nmask, tp2, &ntp2, &lim);
+	if(ntp2 == 1){				//more than one segment
+		for(i = cpg1-1; i > 1; i--) {
+			if(!ShadowPolygon(&pg1[i], &pg1[i-1], ppg_mask, (int)ppg_nmask, tp2, &ntp2, &lim))
+				break;
+			}
+		}
+	//find better solution
+	d1 = ((d = (ep->x - tp1[ntp1-1].x))*d);		d1 += ((d = (ep->y - tp1[ntp1-1].y))*d);
+	d2 = ((d = (ep->x - tp2[ntp2-1].x))*d);		d2 += ((d = (ep->y - tp2[ntp2-1].y))*d);
+	if(d1 < d2 && d1 < 5) {			//use solution 1
+		tp = tp1;	ntp = ntp1;
+		}
+	else if(d2 < d1 && d2 < 5) {	//use solution 2
+		tp = tp2;	ntp = ntp2;
+		}
+	else if (d1 == d2 && d1 < 5) {			//ambiguous result: connect stright 
+		if(d && ppg_par) ppg_par->Command(CMD_ADDTOLINE, ep, 0L);
+		}
+	else {							//no valid solution;
+		if(cidx >= 0) return AddShadowPolygon(pnt, ep, -2);
+		bRet = false;
+		}
+	if(tp && ntp>1) {				//create shadow line
+		bRet = true;
+		for(i = 1; i < ntp; i++) {
+			if(i == ntp -1) {
+				d = ((d1 = tp[i].x - ep->x) * d1);
+				d += ((d1 = tp[i].y - ep->y) * d1);
+				if(d < 2){						//too close to end point
+					nep.x = ep->x = tp[i].x;	nep.y = ep->y = tp[i].y;
+					nep.z = ep->z;
+					break;
+					}
+				}
+			np.x = nep.x = tp[i].x;	np.y = nep.y = tp[i].y;
+			nep.z = pnt->z > ep->z ? ep->z : pnt->z;
+			if(IsInPolygon(&np, ppg_mask, ppg_nmask) || 
+				IsCloseToPL(np, ppg_mask, ppg_nmask)){
+				if(ppg_vec) {					//valid plane eqation
+					nep.z = iround((nep.x * ppg_vec[0] + nep.y * ppg_vec[1] - ppg_vec[3])/ppg_vec[2]);
+					if(ppg_par) ppg_par->Command(CMD_ADDTOLINE, &nep, 0L);
+					}
+				else if(IsInPolygon3D(nep.x, nep.y, ppg_act, ppg_nact, &us, &ls)) {
+					if(us == ls) if(ls){	//point is on the line
+						j = nep.z;
+						if(ppg_act[ls].x == ppg_act[ls-1].x && ppg_act[ls].y == ppg_act[ls-1].y){
+							nep.z = (ppg_act[ls].z + ppg_act[ls-1].z)>>1; //impropable
+							}
+						else if(abs(ppg_act[ls].x - ppg_act[ls-1].x) > 
+							abs(ppg_act[ls].y - ppg_act[ls-1].y)){	// x dominant
+							line3D_z(&ppg_act[ls-1], &ppg_act[ls], nep.x, -1, &k, &k, &j);
+							}
+						else {										// y dominant
+							line3D_z(&ppg_act[ls-1], &ppg_act[ls], -1, nep.y, &k, &k, &j);
+							}
+						nep.z = j;
+						}
+					else {
+						if(line3D_z(&ppg_act[ls-1], &ppg_act[ls], nep.x, -1, &x1, &y1, &z1) &&
+							line3D_z(&ppg_act[us-1], &ppg_act[us], nep.x, -1, &x2, &y2, &z2)){
+							nep.z = (z1 + z2)>>1;					//impropable
+							}
+						}
+					if(ppg_par) ppg_par->Command(CMD_ADDTOLINE, &nep, 0L);
+					}
+				else {
+					//point is inside by one algorithm but outside with another
+					//try without this point
+					}
+				}
+			}
+		if(ppg_par && (nep.x != ep->x || nep.y != ep->y)) ppg_par->Command(CMD_ADDTOLINE, ep, 0L);
+		}
+	free(pg1);	free(tp1);	free(tp2);
+	return bRet;
+}
+
+//calculate the clipping line between two planes
+bool CuttingEdge(POINT3D* pt, POINT3D* np)
+{
+	int i, j, us1, ls1, us2, ls2;
+	long d, di[2];
+	POINT3D res[2];
+	double v[3], s[2][3];
+
+	if(!vclp_pg || !ppg_vec) return false;
+	v[0] = vclp_pg[1]*ppg_vec[2] - vclp_pg[2]*ppg_vec[1];
+	v[1] = vclp_pg[2]*ppg_vec[0] - vclp_pg[0]*ppg_vec[2];
+	v[2] = vclp_pg[0]*ppg_vec[1] - vclp_pg[1]*ppg_vec[0];
+	if(fabs(v[0]) < 1e-5 || fabs(v[1]) < 1e-5 || fabs(v[2]) < 1e-5) return false;
+	v[0] *= (v[2]*2048.0);	v[1] *= (v[2]*2048.0);	v[2] *= (v[2]*2048.0);
+	//find two solutions +/- vector
+	for(i = 0; i < 2; i++) {
+		s[i][0] = (double)(pt->x);	s[i][1] = (double)(pt->y);	s[i][2] = (double)(pt->z);
+		for(j = 0; j < 5; j++) {
+			do {
+				s[i][0] += v[0];			s[i][1] += v[1];			s[i][2] += v[2];
+				}while(IsInPolygon3D(s[i][0], s[i][1], ppg_act, ppg_nact, &us1, &ls1) 
+						&& IsInPolygon3D(s[i][0], s[i][1], clp_pg, nclp_pg, &us2, &ls2));
+			s[i][0] -= v[0];			s[i][1] -= v[1];			s[i][2] -= v[2];
+			v[0] /= 4.0;	v[1] /= 4.0;	v[2] /= 4.0;
+			}
+		s[i][0] += v[0];			s[i][1] += v[1];			s[i][2] += v[2];
+		v[0] *= -1024.0;	v[1] *= -1024.0;	v[2] *= -1024.0;
+		res[i].x = iround(s[i][0]);	res[i].y = iround(s[i][1]);	res[i].z = iround(s[i][2]);
+		di[i] = (d=(res[i].x - pt->x))*d;		di[i] += ((d=(res[i].y - pt->y))*d);
+		}
+	if(di[0] > 1 && di[0] > di[1]) {
+		//first solution has longer projection
+		np->x = res[0].x;	np->y = res[0].y;	np->z = res[0].z;
+		return true;
+		}
+	if(di[1] > 1 && di[1] > di[0]) {
+		//second solution has longer projection
+		np->x = res[1].x;	np->y = res[1].y;	np->z = res[1].z;
+		return true;
+		}
+	return false;
+}
+
+//come here from clip_line_3D to process changes in visibility when
+//   clipping one polygon with another
+void proc_polygon(int vis1, POINT3D *pnt, POINT3D *last)
+{
+	static POINT3D np, lp;
+	long d, d1;
+	int vis = vis1;
+	bool spg_valid;
+
+	switch(ppg_level){
+	case 0:						//searching first visible point of polygon
+		if(vis == 3) vis = 1;						//on line is visible
+		if(!ppg_vis && vis && !ppg_nowvis){			//found it !
+			if(ppg_par && (vis1 == 3 || ppg_vis == 3)) ppg_par->Command(CMD_SIGNAL_POL, pnt, 0L);
+			ppg_nowvis = true;			ppg_first.x = pnt->x;
+			ppg_first.y = pnt->y;		ppg_first.z = pnt->z;
+			ppg_reason = vis;
+			}
+		else if(!vis && ppg_nowvis) {	//check if too short
+			d = (d1 = pnt->x - ppg_first.x) * d1;
+			d += (d1 = pnt->y - ppg_first.y) * d1;
+			if(d < 3) ppg_nowvis = false;	//cancel first point
+			}
+		ppg_vis = vis;
+		break;
+	case 1:
+		if(vis == 3) {						//on line: visible
+			vis = 1;
+			}
+		if(ppg_first.x < 0 && ppg_first.y < 0 && vis) {
+			memcpy(&ppg_first, pnt, sizeof(POINT3D));
+			ppg_firstvis = vis;			lp.x = pnt->x;
+			lp.y = pnt->y;				lp.z = pnt->z;
+			if(ppg_par) ppg_par->Command(CMD_STARTLINE, pnt, 0L);
+			}
+		else if (ppg_vis) {			//leaving visibility or continue
+			spg_valid = false;
+			if(lp.x == pnt->x && lp.y == pnt->y && lp.z == pnt->z) break;
+			if(vis1 == 3 && ppg_par) ppg_par->Command(CMD_SIGNAL_POL, pnt, 0L);
+			if(vis && ppg_par) ppg_par->Command(CMD_ADDTOLINE, pnt, 0L);
+			else if(ppg_par) ppg_par->Command(CMD_ADDTOLINE, last, 0L);
+			if(!vis){				//leaving visibility
+				ppg_vis = test_plane(last->x, last->y, last->z);
+				if(ppg_vis == 3 && ppg_par) ppg_par->Command(CMD_SIGNAL_POL, pnt, 0L);
+				if(ppg_vis == 3) ppg_vis = 1;
+				if(ppg_firstvis == 1 && ppg_vis == 1) {
+					//from below surface to below surface
+					if(!(spg_valid = AddShadowPolygon(last, &ppg_first, -2)))
+						if(ppg_par) spg_valid = ppg_par->Command(CMD_ADDTOLINE, pnt, 0L);
+					}
+				else if(ppg_firstvis == 1 && ppg_vis == 2) {
+					//from below surface enter inside surface
+					if(CuttingEdge(last, &np)){
+						if(ppg_par)ppg_par->Command(CMD_ADDTOLINE, &np, 0L);
+						spg_valid = AddShadowPolygon(&np, &ppg_first, seg_x_seg);
+						}
+					else if(!(spg_valid = AddShadowPolygon(last, &ppg_first, seg_x_seg)))
+						if(ppg_par) spg_valid = ppg_par->Command(CMD_ADDTOLINE, pnt, 0L);
+					}
+				else if(ppg_firstvis == 2 && ppg_vis == 1 && ppg_par) {
+					//from inside surface to below surface
+					if(CuttingEdge(&ppg_first, &np)){
+						if(!(spg_valid = AddShadowPolygon(last, &np, seg_x_seg)))
+							spg_valid = ppg_par->Command(CMD_REQ_POINT, &ppg_first, 0L);
+						ppg_par->Command(CMD_ADDTOLINE, &np, 0L);
+						}
+					else if(!(spg_valid = AddShadowPolygon(last, &ppg_first, seg_x_seg)))
+						spg_valid = ppg_par->Command(CMD_ADDTOLINE, pnt, 0L);
+					}
+				else if(ppg_firstvis == 2 && ppg_vis == 2) {
+					//from inside surface to inside surface
+					// nothing to do: connect straight
+					spg_valid = true;
+					}
+				//prepare for new polygon
+				if(spg_valid) {
+					ppg_first.x = ppg_first.y = ppg_first.z = -1;
+					}
+				else {
+					//we could not find a proper connection between the two points
+					//   probably due to high complexity of graph or shape.
+					//   We ignore part of the polygon and continue with the
+					//   started shape.
+					vis = ppg_vis;
+					}
+				}
+			}
+		ppg_vis = vis;				lp.x = pnt->x;
+		lp.y = pnt->y;				lp.z = pnt->z;
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//test if line is available in 3D-polygons: determine shared edges
+bool LineInPolgon(POINT3D *p1, POINT3D *p2, POINT3D *tpg, int ntpg)
+{
+	int i;
+
+	for(i = 1; i < ntpg; i++) {
+		if(p2->x == tpg[i].x && p2->y == tpg[i].y && p2->z == tpg[i].z) {
+			if(p1->x == tpg[i-1].x && p1->y == tpg[i-1].y && p1->z == tpg[i-1].z) return true;
+			}
+		if(p1->x == tpg[i].x && p1->y == tpg[i].y && p1->z == tpg[i].z) {
+			if(p2->x == tpg[i-1].x && p2->y == tpg[i-1].y && p2->z == tpg[i-1].z) return true;
+			}
+		}
+	i = ntpg -1;
+	if(p1->x == tpg[i].x && p1->y == tpg[i].y && p1->z == tpg[i].z &&
+		p2->x == tpg[0].x && p2->y == tpg[0].y && p2->z == tpg[0].z) return true;
+	if(p2->x == tpg[i].x && p2->y == tpg[i].y && p2->z == tpg[i].z &&
+		p1->x == tpg[0].x && p1->y == tpg[0].y && p1->z == tpg[0].z) return true;
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//entry points for clipping requests
+
+void clip_line_sphere(GraphObj *par, POINT3D **li, int r, int cx, int cy, int cz)
+{
+	sph_r2 = r*r;	sph_x = cx;		sph_y = cy;		sph_z = cz;
+	if(test_sphere(li[0]->x, li[0]->y, li[0]->z)) par->Command(CMD_STARTLINE, li[0], 0L);
+	clip_line_3D(par, &li[0][0], &li[0][1], test_sphere);
+}
+
+void clip_line_plane(GraphObj *par, POINT3D **li, POINT3D *pg, int np, double *vec)
+{
+	nclp_pg = np;		clp_pg = pg;	vclp_pg = vec;
+	if(test_plane(li[0]->x, li[0]->y, li[0]->z)) par->Command(CMD_STARTLINE, li[0], 0L);
+	clip_line_3D(par, &li[0][0], &li[0][1], test_plane);
+	vclp_pg = 0L;
+}
+
+void clip_sphline_sphere(GraphObj *par, POINT3D *lim1, POINT3D *lim2, POINT3D *cent, 
+	int r1, int r2, int cx, int cy, int cz)
+{
+	sphlim1 = lim1;		sphlim2 = lim2;
+	sph_r2 = r2*r2;	sph_x = cx;		sph_y = cy;		sph_z = cz;	
+	clip_spher_line(par, cent, r1, lim1->x == lim2->x, test_sphere);
+}
+
+void clip_sphline_plane(GraphObj *par, POINT3D *lim1, POINT3D *lim2, POINT3D *cent, 
+	int r1, POINT3D *pg, int np, double *vec)
+{
+	sphlim1 = lim1;		sphlim2 = lim2;		nclp_pg = np;
+	clp_pg = pg;		vclp_pg = vec;
+	clip_spher_line(par, cent, r1, lim1->x == lim2->x, test_planeandline);
+	vclp_pg = 0L;
+}
+
+void clip_plane_plane(GraphObj *par, POINT3D *pg1, int n1, double *v1, POINT3D *pg2, 
+	int n2, double *v2, POINT *mask, int nmask)
+{
+	int i, j;
+	POINT3D sp;
+
+	nclp_pg = n2;	clp_pg = pg2;	ppg_level = 0;	ppg_nowvis = false;
+	ppg_reason = 0;	vclp_pg = v2;	ppg_par = par;
+    ppg_vis = test_plane(pg1[0].x, pg1[0].y, pg1[0].z);
+	ppg_act = pg1;	ppg_nact = n1;	ppg_vec = v1;
+	for(i = 1; i < n1 && !ppg_nowvis; i++){		//assume first pnt == last pnt
+		if(!LineInPolgon(&pg1[i-1], &pg1[i], pg2, n2))
+			clip_line_3D(0L, &pg1[i-1], &pg1[i], test_plane);
+		}
+	if(!ppg_nowvis && !ppg_vis) {				//complete pg hidden
+		ppg_vec = vclp_pg = 0L;		return;
+		}
+	if(ppg_nowvis) {							//clip this polygon
+		ppg_mask = mask;		ppg_nmask = nmask;
+		ppg_level = 1;
+		sp.x = ppg_first.x;		sp.y = ppg_first.y;
+		sp.z = ppg_first.z;		ppg_first.x = ppg_first.y = ppg_first.z = -1;
+		ppg_vis = test_plane(sp.x, sp.y, sp.z);
+		proc_polygon(ppg_vis, &sp, &sp);
+		clip_line_3D(0L, &sp, &pg1[i-1], test_plane);
+		for(j = i+n1-1; i < j; i++) {
+			seg_x_seg = -2;
+			clip_line_3D(0L, &pg1[(i-1)%n1], &pg1[i%n1], test_plane);
+			}
+		clip_line_3D(0L, &pg1[(i-1)%n1], &sp, test_plane);
+		}
+	else {										//all visible
+		par->Command(CMD_STARTLINE, pg1, 0L);
+		for(i = 1; i < n1; i++) {
+			par->Command(CMD_ADDTOLINE, &pg1[i], 0L);
+			}
+		}
+	ppg_vec = vclp_pg = 0L;
+}
diff --git a/Version.h b/Version.h
index 5b444bb..23a4b3b 100755
--- a/Version.h
+++ b/Version.h
@@ -1,4 +1,4 @@
-//RLPlot.h, Copyright (c) 2000-2007 R.Lackner
+//RLPlot.h, Copyright (c) 2000-2008 R.Lackner
 //
 //    This file is part of RLPlot.
 //
@@ -16,4 +16,4 @@
 //    along with RLPlot; if not, write to the Free Software
 //    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 //
-#define SZ_VERSION  "1.4"
+#define SZ_VERSION  "1.5"
diff --git a/WinSpec.cpp b/WinSpec.cpp
index 1b1bcb3..a9724bf 100755
--- a/WinSpec.cpp
+++ b/WinSpec.cpp
@@ -1,4 +1,4 @@
-//WinSpec.cpp, Copyright (c) 2000-2007 R.Lackner
+//WinSpec.cpp, Copyright (c) 2000-2008 R.Lackner
 //the entire code of this module is highly specific to Windows!
 //
 //    This file is part of RLPlot.
@@ -46,6 +46,7 @@ const char name[] = "RLPLOT1";
 
 static unsigned int cf_rlpobj = RegisterClipboardFormat("rlp_obj");
 static unsigned int cf_rlpxml = RegisterClipboardFormat("rlp_xml");
+static char *ShellCmd;
 
 long FAR PASCAL WndProc(HWND, UINT, UINT, LONG);
 PrintWin *Printer = 0L;
@@ -327,6 +328,7 @@ void ShowTextCursor(anyOutput *out, RECT *disp, DWORD color)
 {
 	HWND wnd = GetFocus();
 
+	if(!out || (out->OC_type & 0xff) != OC_BITMAP) return;
 	cTxtCur = color;			HideTextCursor();
 	oTxtCur = out;				bSuspend = false;
 	iTxtCurCount = -2;
@@ -354,6 +356,7 @@ void ShowCopyMark(anyOutput *out, RECT *mrk, int nRec)
 {
 	int i;
 
+	if(!out || (out->OC_type & 0xff) != OC_BITMAP) return;
 	HideCopyMark();			bSuspend = false;
 	if(!out || !mrk || !nRec) return;
 	oCopyMark = out;
@@ -369,7 +372,7 @@ void ShowCopyMark(anyOutput *out, RECT *mrk, int nRec)
 
 void InvalidateOutput(anyOutput *o)
 {
-	if(!o) return;
+	if(!o || (o->OC_type & 0xff) != OC_BITMAP) return;
 	if(o == oCopyMark) {
 		oCopyMark = 0L;
 		if(bmCopyMark) delete bmCopyMark;
@@ -382,7 +385,7 @@ void InvalidateOutput(anyOutput *o)
 
 void SuspendAnimation(anyOutput *o, bool bSusp)
 {
-	if(!o) return;
+	if(!o || (o->OC_type & 0xff) != OC_BITMAP) return;
 	if(!bSusp) bSuspend = false;
 	else {
 		if(o == oCopyMark) bSuspend = bSusp;
@@ -401,7 +404,7 @@ LRESULT FAR PASCAL TimerWndProc(HWND hwnd, UINT message, UINT wParam, LONG lPara
 	switch(message) {
 	case WM_TIMER:
 		if(bSuspend) return 0;
-		if(bmCopyMark && oCopyMark) {
+		if(bmCopyMark && oCopyMark && (oCopyMark->OC_type & 0xff) == OC_BITMAP) {
 			bmCopyMark->CopyBitmap(0, 0, oCopyMark, rCopyMark.left, rCopyMark.top, 
 				rCopyMark.right - rCopyMark.left, rCopyMark.bottom - rCopyMark.top, false);
 			bmCopyMark->SetLine(&liCopyMark1);
@@ -602,16 +605,52 @@ bool com_oTextOutW(int x, int y, w_char *txt, int cb, HFONT *hFont, HDC *dc,
 	TextDEF *td, anyOutput *o)
 {
 	XFORM xf;
-	int ix, iy, w, h, dtflags;
+	int ix, iy, w, h, dtflags, rx, ry;
+	double si, csi;
 	RECT dtrc;
+	fRECT rc;
+	TextDEF ttd;
 
 
 	if(!*hFont || !txt || !txt[0]) return false;
 	if(cb < 1) cb = (int)wcslen(txt);
-	SelectObject(*dc, *hFont);				SetTextColor(*dc, td->ColTxt);
-	SetBkColor(*dc, td->ColBg);				SetBkMode(*dc, td->Mode ? TRANSPARENT : OPAQUE);
-	ix = iy = 0;							SetTextAlign(*dc, TA_LEFT | TA_TOP);
-	if(o->OC_type == OC_HIMETRIC) {
+	//test for transparency
+	if(((td->ColTxt & 0xff000000L) || (td->ColBg & 0xff000000L)) && (o->OC_type & 0xff) == OC_BITMAP) {
+		o->oGetTextExtentW(txt, cb, &rx, &ry);
+		rx += 4;	rc.Xmin = -2.0;		rc.Ymin = 0.0;		rc.Xmax = rx;		rc.Ymax = ry;
+		si = sin(td->RotBL *0.01745329252);	csi = cos(td->RotBL *0.01745329252);
+		if(td->Align & TXA_HCENTER) {
+			rc.Xmin -= rx/2.0-1.0;		rc.Xmax -= rx/2.0-1.0;
+			}
+		else if(td->Align & TXA_HRIGHT) {
+			rc.Xmin -= rx-2.0;			rc.Xmax -= rx-2.0;
+			}
+		if(td->Align & TXA_VCENTER) {
+			rc.Ymin -= ry/2.0;			rc.Ymax -= ry/2.0;
+			}
+		else if(td->Align & TXA_VBOTTOM) {
+			rc.Ymin -= ry;				rc.Ymax -= ry;
+			}
+		SetMinMaxRect(&((BitMapWin*)o)->tr_rec, iround(rc.Xmin*csi + rc.Ymin*si)+x, iround(rc.Ymin*csi - rc.Xmin*si)+y,
+			iround(rc.Xmax*csi + rc.Ymin*si)+x, iround(rc.Ymin*csi - rc.Xmax*si)+y);
+		UpdateMinMaxRect(&((BitMapWin*)o)->tr_rec, iround(rc.Xmax*csi + rc.Ymax*si)+x, iround(rc.Ymax*csi - rc.Xmax*si)+y);
+		UpdateMinMaxRect(&((BitMapWin*)o)->tr_rec, iround(rc.Xmin*csi + rc.Ymax*si)+x, iround(rc.Ymax*csi - rc.Xmin*si)+y);
+		IncrementMinMaxRect(&((BitMapWin*)o)->tr_rec, (td->iSize>>1) +6);
+		((BitMapWin*)o)->tr_out = GetRectBitmap(&((BitMapWin*)o)->tr_rec, o);
+		((BitMapWin*)o)->tr_out->hres = ((BitMapWin*)o)->hres;
+		((BitMapWin*)o)->tr_out->vres = ((BitMapWin*)o)->vres;
+		memcpy(&ttd, td, sizeof(TextDEF));
+		ttd.ColTxt = td->ColTxt & 0x00ffffffL;	ttd.ColBg = td->ColBg & 0x00ffffffL;
+		((BitMapWin*)o)->tr_out->SetTextSpec(&ttd);
+		((BitMapWin*)o)->tr_out->oTextOutW(x-((BitMapWin*)o)->tr_rec.left, y-((BitMapWin*)o)->tr_rec.top,
+			txt, cb);
+		((BitMapWin*)o)->DoTransparency(td->ColTxt);
+		return true;
+		}
+	SelectObject(*dc, *hFont);					SetTextColor(*dc, (td->ColTxt)&0x00ffffffL);
+	SetBkColor(*dc, (td->ColBg)&0x00ffffffL);	SetBkMode(*dc, td->Mode ? TRANSPARENT : OPAQUE);
+	ix = iy = 0;								SetTextAlign(*dc, TA_LEFT | TA_TOP);
+	if((o->OC_type & 0xff) == OC_HIMETRIC) {
 		if(td->Style & TXS_SUB) {
 			if((td->Align & TXA_VCENTER) == TXA_VCENTER) iy -= o->un2iy(td->fSize*0.4);
 			else if((td->Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy -= o->un2iy(td->fSize*0.2);
@@ -657,7 +696,7 @@ bool com_oTextOutW(int x, int y, w_char *txt, int cb, HFONT *hFont, HDC *dc,
 	dtrc.top = iy + y;				dtrc.right = dtrc.left+w;		dtrc.bottom = dtrc.top+h;
 	if(fabs(td->RotBL) >.01 || fabs(td->RotCHAR) >.01) {
 		SetGraphicsMode(*dc, GM_ADVANCED);
-		if(o->OC_type == OC_HIMETRIC) {
+		if((o->OC_type &0xff) == OC_HIMETRIC) {
 			if((td->Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy -= (td->iSize<<1);
 			else if((td->Align & TXA_VCENTER) == TXA_VCENTER) iy -= (td->iSize);
 			xf.eM11 = (float)cos(td->RotBL *0.01745329252);
@@ -714,7 +753,7 @@ bool com_SetTextSpec(TextDEF *set, anyOutput *o, HFONT *hFont,	TextDEF *TxtSet,
 	LOGFONT FontRec;
 	HFONT newFont;
 
-	if(!set->iSize && set->fSize > 0.001f) set->iSize = o->un2iy(set->fSize);
+	if(!set->iSize && set->fSize > 0.001) set->iSize = o->un2iy(set->fSize);
 	if(!set->iSize) return false;
 	if(!*hFont || TxtSet->iSize != set->iSize || TxtSet->Style != set->Style ||
 		TxtSet->RotBL != set->RotBL || TxtSet->RotCHAR != set->RotCHAR ||
@@ -815,11 +854,10 @@ BitMapWin::BitMapWin(GraphObj *g, HWND hw):anyOutput()
 	Box1.Ymin = DeskRect.top;		Box1.Ymax = DeskRect.bottom;
 	scr = CreateCompatibleBitmap(dc, DeskRect.right, DeskRect.bottom);
 	memDC = CreateCompatibleDC(NULL);
-	SelectObject(memDC, scr);
-	ReleaseDC(hwndDesk, dc);
+	SelectObject(memDC, scr);		ReleaseDC(hwndDesk, dc);
 	hPen = CreatePen(PS_SOLID, 1, dLineCol = 0x00ffffffL);
 	hBrush = CreateSolidBrush(dFillCol =dBgCol = 0x00ffffffL);
-	dPattern = 0L;
+	dPattern = 0L;				minLW = 1;
 	if(memDC) {
 		oldPen = (HPEN)SelectObject(memDC, hPen);
 		oldBrush = (HBRUSH)SelectObject(memDC, hBrush);
@@ -828,7 +866,7 @@ BitMapWin::BitMapWin(GraphObj *g, HWND hw):anyOutput()
 		oldBrush = 0L;
 		oldPen = 0L;
 		}
-	hFont = 0L;
+	hFont = 0L;	OC_type = OC_BITMAP;
 }
 
 BitMapWin::BitMapWin(int w, int h, double hr, double vr)
@@ -837,7 +875,7 @@ BitMapWin::BitMapWin(int w, int h, double hr, double vr)
 	HWND hwndDesk;
 
 	memDC = 0L;		hgo = 0L;		go = 0L;
-	hres = hr;		vres = vr;
+	hres = hr;		vres = vr;		minLW = 1;
 	units = defs.cUnits;
 	DeskRect.right = w;				DeskRect.bottom = h;
 	DeskRect.left = DeskRect.top = 0;
@@ -854,7 +892,7 @@ BitMapWin::BitMapWin(int w, int h, double hr, double vr)
 		SelectObject(memDC, hPen);
 		SelectObject(memDC, hBrush);
 		}
-	hFont = 0L;
+	hFont = 0L;				OC_type = OC_BITMAP;
 }
 
 BitMapWin::BitMapWin(GraphObj *g):anyOutput()
@@ -864,8 +902,7 @@ BitMapWin::BitMapWin(GraphObj *g):anyOutput()
 
 	memDC = 0L;		hgo = 0L;		go = g;
 	dc = GetDC(hwndDesk = GetDesktopWindow());
-	hres = vres = 300.0;
-	units = defs.cUnits;
+	hres = vres = 300.0;	units = defs.cUnits;	minLW = 1;
 	DeskRect.right = un2ix(go->GetSize(SIZE_GRECT_RIGHT) - go->GetSize(SIZE_GRECT_LEFT));
 	DeskRect.bottom = un2iy(go->GetSize(SIZE_GRECT_BOTTOM) - go->GetSize(SIZE_GRECT_TOP));
 	DeskRect.top = DeskRect.left = 0;
@@ -882,19 +919,15 @@ BitMapWin::BitMapWin(GraphObj *g):anyOutput()
 		SelectObject(memDC, hPen);
 		SelectObject(memDC, hBrush);
 		}
-	hFont = 0L;
+	hFont = 0L;		OC_type = OC_BITMAP;
 }
 
 BitMapWin::~BitMapWin()
 {
-	Undo.KillDisp(this);
-	if(hgo) delete hgo;
-	if(hFont) DeleteObject(hFont);
-	if(scr) DeleteObject(scr);
-	SelectObject(memDC, oldPen);
-	SelectObject(memDC, oldBrush);
-	if(memDC) DeleteDC(memDC);
-	if(hPen) DeleteObject(hPen);
+	Undo.KillDisp(this);			if(hgo) delete hgo;
+	if(hFont) DeleteObject(hFont);		if(scr) DeleteObject(scr);
+	SelectObject(memDC, oldPen);		SelectObject(memDC, oldBrush);
+	if(memDC) DeleteDC(memDC);		if(hPen) DeleteObject(hPen);
 	if(hBrush) DeleteObject(hBrush);
 	hgo = 0L;	hFont = 0L;	scr = 0L;	
 	memDC = 0L;	hPen = 0L;	hBrush = 0L;
@@ -936,7 +969,7 @@ BitMapWin::SetFill(FillDEF *fill)
 		}
 	else {
 		if(hgo) delete hgo;
-		hgo = NULL;
+		hgo = 0L;
 		}
 	if(dFillCol != fill->color) {
 		newBrush = CreateSolidBrush(dFillCol = fill->color);
@@ -968,10 +1001,8 @@ BitMapWin::Erase(DWORD Color)
 			hOldBrush = (HBRUSH)SelectObject(memDC, hBGbrush);
 			hOldPen = (HPEN)SelectObject(memDC, hBGpen);
 			Rectangle(memDC, 0, 0, DeskRect.right, DeskRect.bottom);
-			SelectObject(memDC, hOldBrush);
-			SelectObject(memDC, hOldPen);
-			DeleteObject(hBGbrush);
-			DeleteObject(hBGpen);
+			SelectObject(memDC, hOldBrush);		SelectObject(memDC, hOldPen);
+			DeleteObject(hBGbrush);			DeleteObject(hBGpen);
 			return true;
 			}
 		if(hBGpen) DeleteObject(hBGpen);
@@ -1042,44 +1073,117 @@ bool
 BitMapWin::oCircle(int x1, int y1, int x2, int y2, char *nam)
 {
 	BOOL RetVal;
+	FillDEF tr_fill = {FILL_NONE, 0x0, 1.0, 0L, 0x0};
+	LineDEF tr_line = {0.0, 1.0, 0x0, 0x0};
+	bool bTrans = false;
+	HPEN newPen;
 	
-	RetVal = Ellipse(memDC, x1, y1, x2, y2);
-	if(RetVal && hgo) return hgo->oCircle(x1, y1, x2, y2);
-	else if(RetVal) return true;
-	return false;
+
+	if(dFillCol & 0xff000000) {
+		bTrans = true;
+		tr_fill.color = tr_fill.color2 = tr_line.color = (dFillCol & 0x00ffffffL);
+		SetMinMaxRect(&tr_rec, x1, y1, x2, y2);
+		IncrementMinMaxRect(&tr_rec, 6);
+		tr_out = GetRectBitmap(&tr_rec, this);
+		tr_out->hres = hres;			tr_out->vres = vres;
+		tr_out->SetLine(&tr_line);			tr_out->SetFill(&tr_fill);
+		RetVal = tr_out->oCircle(x1-tr_rec.left, y1-tr_rec.top, x2-tr_rec.left, y2-tr_rec.top, nam);
+		DoTransparency(dFillCol);
+		if(!(dLineCol & 0xff000000)) Arc(memDC, x1, y1,	x2, y2, 0, 0, 0, 0);
+		}
+	if(dLineCol & 0xff000000) {
+		if(!bTrans) {
+			newPen = CreatePen(PS_SOLID, 1, dFillCol);
+			SelectObject(memDC, newPen);			Ellipse(memDC, x1, y1, x2, y2);
+			if(hPen) {
+				SelectObject(memDC, hPen);			DeleteObject(newPen);
+				}
+			}
+		bTrans = true;
+		tr_line.color = (dLineCol & 0x00ffffffL);
+		tr_line.width = LineWidth;
+		tr_line.pattern = dPattern;
+		SetMinMaxRect(&tr_rec, x1, y1, x2, y2);
+		IncrementMinMaxRect(&tr_rec, 6 + un2ix(LineWidth*2.0));
+		tr_out = GetRectBitmap(&tr_rec, this);
+		tr_out->hres = hres;			tr_out->vres = vres;
+		tr_out->SetLine(&tr_line);
+		Arc(((BitMapWin*)tr_out)->memDC, x1-tr_rec.left, y1-tr_rec.top,
+			x2-tr_rec.left, y2-tr_rec.top, 0, 0, 0, 0);
+		DoTransparency(dLineCol);
+		}
+	if(!bTrans) {
+		RetVal = Ellipse(memDC, x1, y1, x2, y2);
+		if(RetVal && hgo) return hgo->oCircle(x1, y1, x2, y2);
+		else if(RetVal) return true;
+		}
+	return true;
 }
 
 bool
 BitMapWin::oPolyline(POINT * pts, int cp, char *nam)
 {
 	int i;
-
-	if(cp < 1) return false;
-	if (dPattern) {
-		for (i = 1; i < cp; i++) PatLine(pts[i-1], pts[i]);
-		return true;
+	BOOL RetVal;
+	POINT *newpts;
+	LineDEF tr_line = {0.0, 1.0, 0x0, 0x0};
+
+	if(!pts || cp < 1) return false;
+	if((dLineCol & 0xff000000) && (newpts = (POINT*)malloc(cp * sizeof(POINT)))) {
+		tr_line.color = (dLineCol & 0x00ffffffL);
+		tr_line.width = LineWidth;
+		tr_line.pattern = dPattern;
+		SetMinMaxRect(&tr_rec, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+		for(i = 2; i < cp; i++) {
+			UpdateMinMaxRect(&tr_rec, pts[i].x, pts[i].y);
+			}
+		IncrementMinMaxRect(&tr_rec, 6 + un2ix(LineWidth*2.0));
+		tr_out = GetRectBitmap(&tr_rec, this);
+		tr_out->RLP.finc = RLP.finc;	tr_out->RLP.fp = RLP.fp;
+		tr_out->hres = hres;			tr_out->vres = vres;
+		for(i = 0; i < cp; i++) {
+			newpts[i].x = pts[i].x - tr_rec.left;
+			newpts[i].y = pts[i].y - tr_rec.top;
+			}
+		tr_out->SetLine(&tr_line);
+		RetVal = tr_out->oPolyline(newpts, cp, 0L);
+		RLP.finc = tr_out->RLP.finc;	RLP.fp = tr_out->RLP.fp;
+		DoTransparency(dLineCol);		free(newpts);
+		return (RetVal != 0);
 		}
 	else {
-		if(Polyline(memDC, pts, cp))return true;
-		else return false;
+		if (dPattern) {
+			for (i = 1; i < cp; i++) PatLine(pts[i-1], pts[i]);
+			return true;
+			}
+		else RetVal = Polyline(memDC, pts, cp);
 		}
+	return false;
 }
 
 bool
-BitMapWin::oRectangle(int x1, int y1, int x2, int y2, char *name)
+BitMapWin::oRectangle(int x1, int y1, int x2, int y2, char *nam)
 {
-	BOOL RetVal;
+	POINT pts[5];
 
-	RetVal = Rectangle(memDC, x1, y1, x2, y2);
-	if(RetVal && hgo) return hgo->oRectangle(x1, y1, x2, y2, 0L);
-	else if (RetVal) return true;
-	return false;
+	pts[0].x = pts[3].x = pts[4].x = x1;	pts[0].y = pts[1].y = pts[4].y = y1;
+	pts[1].x = pts[2].x = x2;		pts[2].y = pts[3].y = y2;
+	return oPolygon(pts, 5, nam);
 }
 
 bool
 BitMapWin::oSolidLine(POINT *p)
 {
-	if(Polyline(memDC, p, 2)) return true;
+	DWORD dPat;
+
+	if(dLineCol & 0xff000000L) {
+		dPat = dPattern;		dPattern = 0L;
+		oPolyline(p, 2, 0L);		dPattern = dPat;
+		return true;
+		}
+	else {
+		if(Polyline(memDC, p, 2)) return true;
+		}
 	return false;
 }
 
@@ -1098,12 +1202,93 @@ BitMapWin::oTextOutW(int x, int y, w_char *txt, int cb)
 bool
 BitMapWin::oPolygon(POINT *pts, int cp, char *nam)
 {
+	int i;
 	BOOL RetVal;
+	POINT *newpts;
+	FillDEF tr_fill = {FILL_NONE, 0x0, 1.0, 0L, 0x0};
+	LineDEF tr_line = {0.0, 1.0, 0x0, 0x0};
+	HPEN newPen;
 
-	RetVal = Polygon(memDC, pts, cp);
+	if(!pts || cp < 2) return false;
+	if((dFillCol & 0xff000000) && (newpts = (POINT*)malloc(cp * sizeof(POINT)))) {
+		tr_fill.color = tr_fill.color2 = tr_line.color = (dFillCol & 0x00ffffffL);
+		SetMinMaxRect(&tr_rec, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+		for(i = 2; i < cp; i++) {
+			UpdateMinMaxRect(&tr_rec, pts[i].x, pts[i].y);
+			}
+		IncrementMinMaxRect(&tr_rec, 6);
+		tr_out = GetRectBitmap(&tr_rec, this);
+		tr_out->hres = hres;			tr_out->vres = vres;
+		for(i = 0; i < cp; i++) {
+			newpts[i].x = pts[i].x - tr_rec.left;
+			newpts[i].y = pts[i].y - tr_rec.top;
+			}
+		tr_out->SetLine(&tr_line);		tr_out->SetFill(&tr_fill);
+		RetVal = tr_out->oPolygon(newpts, cp, 0L);
+		DoTransparency(dFillCol);		free(newpts);
+		oPolyline(pts, cp, nam);
+		}
+	else if((dLineCol & 0xff000000) && (newpts = (POINT*)malloc(cp * sizeof(POINT)))) {
+		newPen = CreatePen(PS_SOLID, 1, dFillCol);
+		SelectObject(memDC, newPen);
+		RetVal = Polygon(memDC, pts, cp);
+		if(hPen) {
+			SelectObject(memDC, hPen);	oPolyline(pts, cp, nam);
+			DeleteObject(newPen);
+			}
+		}
+	else {
+		RetVal = Polygon(memDC, pts, cp);
+		}
 	if(RetVal && hgo) return hgo->oPolygon(pts, cp);
-	else if (RetVal) return true;
-	return false;
+	return RetVal != 0;
+}
+
+// The following code does alpha blending for transparent colors.
+//    The Windows AlphaBlend function requires msimg32.dll. To include
+//    this library add MSIMG32.LIB to the Project/Settings.../Link/Library_Moduls list.
+//    The code executed with USE_MSIMG32 undefined is very slow because of the GetPixel
+//    and SetPixel functions.
+//
+#define USE_MSIMG32
+void
+BitMapWin::DoTransparency(DWORD color)
+{
+#ifdef USE_MSIMG32
+	BLENDFUNCTION bf = {AC_SRC_OVER, 0, 127, 0};
+
+	bf.SourceConstantAlpha = (unsigned char)(255 - ((color >> 24) & 0xff));
+	AlphaBlend(memDC, tr_rec.left, tr_rec.top, tr_rec.right - tr_rec.left, tr_rec.bottom - tr_rec.top,
+		((BitMapWin*)tr_out)->memDC, 0, 0, tr_rec.right - tr_rec.left, tr_rec.bottom - tr_rec.top, bf);
+#else
+	int x1, y1, x2, y2, c, c1, c2;
+	DWORD col1, col2, col;
+	double f, f1;
+	
+	if(!tr_out) return;
+	f = ((color & 0xff000000L) >>24)/255.0;		f1 = 1.0 - f;
+	for(y1 = tr_rec.top, y2 = 0; y1 < tr_rec.bottom; y1++, y2++) {
+		for(x1 = tr_rec.left, x2 = 0; x1 < tr_rec.right; x1++, x2++) {
+			col1 = GetPixel(memDC, x1, y1);
+			col2 = GetPixel(((BitMapWin*)tr_out)->memDC, x2, y2);
+			if(col1 != col2) {
+				col = 0x0;
+				c1 = (col1 & 0x000000ffL);	c = c2 = (col2 & 0x000000ffL);
+				if(c1 != c2) c = (int)(c2 * f1 + c1 * f);
+				col |= (c < 256 ? c : 0xff);
+				c1 = ((col1 & 0x0000ff00L)>>8);	c = c2 = ((col2 & 0x0000ff00L)>>8);
+				if(c1 != c2) c = (int)(c2 * f1 + c1 * f);
+				col |= (c < 256 ? (c<<8) : 0x00ff00);
+				c1 = ((col1 & 0x00ff0000L)>>16);	c = c2 = ((col2 & 0x00ff0000L)>>16);
+				if(c1 != c2) c = (int)(c2 * f1 + c1 * f);
+				col |= (c < 256 ? (c<<16) : 0xff0000);
+				SetPixel(memDC, x1, y1, col);
+				}
+			}
+		}
+#endif	//USE_MSIMG32
+	DelBitmapClass(tr_out);			tr_out = 0L;
+	OC_type |= OC_TRANSPARENT;
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1111,7 +1296,7 @@ BitMapWin::oPolygon(POINT *pts, int cp, char *nam)
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 OutputWin::OutputWin(GraphObj *g, HWND hw):BitMapWin(g, hw)
 {
-	hdc = 0L;
+	hdc = 0L;	minLW = 1;
 	if(g) {
 		if(!hw) CreateNewWindow(g);
 		else hWnd = hw;
@@ -1138,9 +1323,14 @@ OutputWin::ActualSize(RECT *rc)
 }
 
 void
-OutputWin::Caption(char *txt)
+OutputWin::Caption(char *txt, bool bModified)
 {
-	SetWindowText(hWnd, txt);
+	char txt1[200];
+	int cb;
+
+	cb = rlp_strcpy(txt1, 180, txt);
+	if(bModified)rlp_strcpy(txt1+cb, 20, " [modified]");
+	SetWindowText(hWnd, txt1);
 }
 
 const static unsigned char hand_bits[] =	{	//hand cursor bitmap
@@ -1531,7 +1721,7 @@ WinCopyWMF::WinCopyWMF(GraphObj *g, char *file_wmf, char *file_emf)
 	hPen = CreatePen(PS_SOLID, 1, dLineCol = 0x00ff0000L);
 	hBrush = CreateSolidBrush(dFillCol =dBgCol = 0x00ffffffL);
 	dPattern = 0L;		go = g;			hgo = 0L;		bott_y = 0;
-	dc = GetDC(hwndDesk = GetDesktopWindow());
+	dc = GetDC(hwndDesk = GetDesktopWindow());	minLW = 1;
 	hres = (double)GetDeviceCaps(dc, LOGPIXELSX);	vres = (double)GetDeviceCaps(dc, LOGPIXELSY);
 	ReleaseDC(hwndDesk, dc);	wmf_file = file_wmf;	emf_file = file_emf;
 }
@@ -1771,7 +1961,7 @@ PrintWin::PrintWin()
 
 	PrintDriver = PrintDevice = PrintPort = 0L;
 	hPen = 0L;		hBrush = 0L;			hFont = 0L;		hDC = 0L;
-	hgo = 0L;		units = defs.cUnits;	i = j = 0;
+	hgo = 0L;		units = defs.cUnits;		i = j = 0;		minLW = 1;
 	GetProfileString("windows", "device", "", TmpTxt, 4096);
 	while(TmpTxt[i] && TmpTxt[i] != ',') i++;
 	TmpTxt[i] = 0;
@@ -1928,6 +2118,16 @@ PrintWin::Eject()
 }
 
 bool
+PrintWin::CopyBitmap(int x, int y, anyOutput* sr, int sx, int sy,
+	int sw, int sh, bool invert)
+{
+	BitMapWin *src = (BitMapWin*)sr;
+
+	return(0 != BitBlt(hDC, x, y, sw, sh, src->memDC, sx, sy, 
+		invert ? DSTINVERT : SRCCOPY));
+}
+
+bool
 PrintWin::oCircle(int x1, int y1, int x2, int y2, char* nam)
 {
 	BOOL RetVal;
@@ -1955,14 +2155,13 @@ PrintWin::oPolyline(POINT * pts, int cp, char *nam)
 }
 
 bool
-PrintWin::oRectangle(int x1, int y1, int x2, int y2, char *name)
+PrintWin::oRectangle(int x1, int y1, int x2, int y2, char *nam)
 {
-	BOOL RetVal;
+	POINT pts[5];
 
-	RetVal = Rectangle(hDC, x1, y1, x2, y2);
-	if(RetVal && hgo) return hgo->oRectangle(x1, y1, x2, y2, 0L);
-	else if (RetVal) return true;
-	return false;
+	pts[0].x = pts[3].x = pts[4].x = x1;	pts[0].y = pts[1].y = pts[4].y = y1;
+	pts[1].x = pts[2].x = x2;		pts[2].y = pts[3].y = y2;
+	return oPolygon(pts, 5, nam);
 }
 
 bool
@@ -2082,6 +2281,7 @@ int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInstance,
 		rmquot(TmpTxt);
 		if(TmpTxt[0]) LoadFile= _strdup(TmpTxt);
 		}
+	ShellCmd = GetCommandLine();
 	hInstance = hInst;
 	wndclass.style = CS_BYTEALIGNWINDOW | CS_DBLCLKS;
 	wndclass.lpfnWndProc = WndProc;
@@ -2251,6 +2451,7 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 	MouseEvent mev;
 	HDC dc;
 	int cc;
+	RECT rec;
 
 	g = (GraphObj *) reflptr(GetWindowLong(hwnd, 0));
 	w = (OutputWin *) reflptr(GetWindowLong(hwnd, GWL_USERDATA));
@@ -2394,6 +2595,9 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 				DestroyWindow(hwnd);
 				}
 			return 0;
+		case CM_NEWINST:
+			if(ShellCmd && ShellCmd[0])WinExec(ShellCmd, SW_SHOW);
+			return 0;
 		case CM_PASTE:
 			w->MouseCursor(MC_WAIT, true);
 			if(g->Id == GO_SPREADDATA) TestClipboard(g);
@@ -2423,7 +2627,7 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 				SetClipboardData(cf_rlpxml, NULL);
 				}
 			else if(g->Id == GO_PAGE) {
-				SetClipboardData(CF_ENHMETAFILE, NULL);
+				if(!g->hasTransp()) SetClipboardData(CF_ENHMETAFILE, NULL);
 				SetClipboardData(CF_BITMAP, NULL);
 				if(CurrGraph) {
 					CopyGraph(CurrGraph, cf_rlpobj, w);		copy_obj = CurrGraph;
@@ -2431,12 +2635,12 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 				}
 			else if (wParam == CM_CUT)return 0;
 			else if(CurrGraph && CurrGraph->Id == GO_GRAPH){
-				SetClipboardData(CF_ENHMETAFILE, NULL);
+				if(!g->hasTransp()) SetClipboardData(CF_ENHMETAFILE, NULL);
 				SetClipboardData(CF_BITMAP, NULL);
 				CopyGraph(CurrGraph, cf_rlpobj, w);			copy_obj = CurrGraph;
 				}
 			else if(g->Id == GO_GRAPH){
-				SetClipboardData(CF_ENHMETAFILE, NULL);
+				if(!g->hasTransp()) SetClipboardData(CF_ENHMETAFILE, NULL);
 				SetClipboardData(CF_BITMAP, NULL);
 				CopyGraph(g, cf_rlpobj, w);					copy_obj = g;
 				}
@@ -2499,11 +2703,11 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 		case CM_DELGRAPH:
 			g->Command(CMD_DELGRAPH, 0L, w);
 			return 0;
-		case CM_SAVEDATA:
-			g->Command(CMD_SAVEDATA, 0L, w);
+		case CM_SAVE:
+			g->Command(CMD_SAVE, 0L, w);
 			return 0;
-		case CM_SAVEDATAAS:
-			g->Command(CMD_SAVEDATAAS, 0L, w);
+		case CM_SAVEAS:
+			g->Command(CMD_SAVEAS, 0L, w);
 			return 0;
 		case CM_REDRAW:
 			if(w->Erase(defs.Color(COL_BG))) g->DoPlot(w);
@@ -2516,9 +2720,6 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 				}
 			else if(!CurrGO) InfoBox("No object selected!");
 			return 0;
-		case CM_SAVEGRAPHAS:
-			SaveGraphAs(g);
-			return 0;
 		case CM_EXPORT:
 			OpenExportName(g, 0L);
 			g->DoPlot(w);
@@ -2530,8 +2731,26 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 				}
 			if(Printer && Printer->StartPage()) {
 				SetCursor(LoadCursor(0L, IDC_WAIT));
-				g->DoPlot(Printer);
-				Printer->EndPage();
+				rec.left = Printer->un2ix(g->GetSize(SIZE_GRECT_LEFT));
+				rec.right = Printer->un2ix(g->GetSize(SIZE_GRECT_RIGHT));
+				rec.top = Printer->un2iy(g->GetSize(SIZE_GRECT_TOP));
+				rec.bottom = Printer->un2iy(g->GetSize(SIZE_GRECT_BOTTOM));
+				if(g->hasTransp()) {
+					if((CopyBMP = new BitMapWin(rec.right-rec.left, rec.bottom-rec.top,
+						Printer->hres, Printer->vres)) && CopyBMP->StartPage()) {
+						CopyBMP->VPorg.fy = -Printer->co2fiy(g->GetSize(SIZE_GRECT_TOP));
+						CopyBMP->VPorg.fx = -Printer->co2fix(g->GetSize(SIZE_GRECT_LEFT));
+						g->DoPlot(CopyBMP);			CopyBMP->EndPage();
+						Printer->CopyBitmap(rec.left, rec.top, CopyBMP, 0, 0, CopyBMP->DeskRect.right,
+							CopyBMP->DeskRect.bottom, false);
+						delete CopyBMP;						CopyBMP = NULL;
+						Printer->EndPage();
+						}
+					}
+				else {
+					g->DoPlot(Printer);
+					Printer->EndPage();
+					}
 				w->Erase(defs.Color(COL_BG));
 				g->DoPlot(w);
 				SetCursor(LoadCursor(0L, IDC_ARROW));
@@ -2633,6 +2852,9 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 		case CM_REPANOV:
 			if(g->data) rep_anova(g, g->data);
 			return 0;
+		case CM_REPBDANOV:
+			if(g->data) rep_bdanova(g, g->data);
+			return 0;
 		case CM_REPTWANR:
 			if(g->data) rep_twoway_anova(g, g->data);
 			return 0;
@@ -2827,7 +3049,7 @@ void *CreateDlgWnd(char *title, int x, int y, int width, int height, tag_DlgObj
 				(BoxRec.bottom- BoxRec.top)/2, BoxRec.right - BoxRec.left,
 				BoxRec.bottom - BoxRec.top, 0);
 			}
-		if(flags & 0x04)			SetTimer(hDlg, 1, 100, 0L);
+		if(flags & 0x08)			SetTimer(hDlg, 1, 100, 0L);
 		UpdateWindow(hDlg);			d->DoPlot(w);
 		ShowWindow(hDlg, SW_SHOW);
 		}
diff --git a/WinSpec.h b/WinSpec.h
index aaa60b8..fa83f1f 100755
--- a/WinSpec.h
+++ b/WinSpec.h
@@ -1,4 +1,4 @@
-//WinSpec.h, Copyright (c) 2000-2006 R.Lackner
+//WinSpec.h, Copyright (c) 2000-2008 R.Lackner
 //
 //    This file is part of RLPlot.
 //
@@ -25,6 +25,8 @@ public:
 	HBRUSH hBrush, oldBrush;
 	HFONT hFont;
 	HatchOut *hgo;
+	RECT tr_rec;			//rectangle for transparency
+	anyOutput *tr_out;		//transparency source class
 
 	BitMapWin(GraphObj *g, HWND hw);
 	BitMapWin(int w, int h, double hr, double vr);
@@ -48,6 +50,7 @@ public:
 	bool oTextOut(int x, int y, char *txt, int cb);
 	bool oTextOutW(int x, int y, w_char *txt, int cb);
 	bool oPolygon(POINT *pts, int cp, char *nam = 0L);
+	void DoTransparency(DWORD color);
 };
 
 class OutputWin:public BitMapWin{
@@ -59,7 +62,7 @@ public:
 	~OutputWin();
 	bool ActualSize(RECT *rc);
 	void Focus(){if(hWnd) SetFocus(hWnd);};
-	void Caption(char *txt);
+	void Caption(char *txt, bool bModified);
 	void MouseCursor(int cid, bool force);
 	bool SetScroll(bool isVert, int iMin, int iMax, int iPSize, int iPos);
 	bool Erase(DWORD Color);
@@ -119,6 +122,8 @@ public:
 	bool oGetTextExtentW(w_char *text, int cb, int *width, int *height);
 	bool StartPage();
 	bool EndPage();
+	bool CopyBitmap(int x, int y, anyOutput* src, int sx, int sy,
+		int sw, int sh, bool invert);
 	bool Eject();
 	bool oCircle(int x1, int y1, int x2, int y2, char* nam = 0L);
 	bool oPolyline(POINT * pts, int cp, char *nam = 0L);
diff --git a/exprlp.cpp b/exprlp.cpp
index 4b0a5e5..8da75b0 100755
--- a/exprlp.cpp
+++ b/exprlp.cpp
@@ -1,235 +1,253 @@
-//exprlp.cpp, Copyright (c) 2002-2006 R.Lackner
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <locale.h>
-#include <unistd.h>			//required for unlink()
-#include "Version.h"
-#include "rlplot.h"
-
-int file_fmt = FF_UNKNOWN;
-bool bQuiet = false, bSVGtype = false, bDelete = false;
-char *szFile1 = 0L, *szFile2 = 0L;
-int dlgtxtheight = 12;				//stub: not used
-char *name1, *name2;				//the filenames
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// direct messages to console
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void InfoBox(char *Msg)
-{
-	if(!bQuiet) fprintf(stderr, "exprlp INFO: %s\n", Msg);
-}
-
-void ErrorBox(char *Msg)
-{
-	if(!bQuiet) fprintf(stderr, "exprlp ERROR: %s\n", Msg);
-}
-
-bool YesNoBox(char *Msg)
-{
-return false;
-}
-
-int YesNoCancelBox(char *Msg)
-{
-	return 0;
-}
-
-void HideCopyMark()
-{
-}
-
-void ShowCopyMark(anyOutput *out, RECT *mrk, int nRec)
-{
-}
-
-void CopyText(char *txt, int len)
-{
-}
-
-unsigned char* PasteText()
-{
-	return 0L;
-}
-
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create a root object to handle I/O
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class ExpRoot:public GraphObj{
-public:
-	ExpRoot(char *file1, char *file2);
-	~ExpRoot();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-
-private:
-	GraphObj *go;
-};
-
-ExpRoot::ExpRoot(char *file1, char *file2):GraphObj(0L, 0L)
-{
-	if(file1 && strcmp("-", file1)) name1 = file1;
-	else name1 = 0L;
-	if(file2 && strcmp("-", file2)) name2 = file2;
-	else name2 = 0L;
-	go = 0L;
-	OpenGraph(this, name1, 0L, false);
-	if(bDelete && name1 && name1[0]) unlink(name1);
-}
-
-ExpRoot::~ExpRoot()
-{
-	if(go) {
-		DeleteGO(go);
-		if(!bQuiet)fprintf(stderr, "Object deleted after read\n");
-		}
-}
-
-bool
-ExpRoot::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-
-	switch(cmd) {
-	case CMD_DROP_GRAPH:
-		go = (GraphObj*)tmpl;
-		if(go) {
-			go->Command(CMD_SET_DATAOBJ, 0L, 0L);
-			switch(file_fmt){
-			case FF_SVG:
-				DoExportSvg(go, name2, bSVGtype ? 1L : 0L);
-				break;
-			case FF_WMF:
-				DoExportWmf(go, name2, 600.0f, 0L);
-				break;
-			case FF_EPS:
-				DoExportEps(go, name2, 0L);
-				break;
-			case FF_RLP:
-				SaveGraphAs(go);
-				break;
-			default:
-				ErrorBox("Unknown file extension or format of destination");
-				}
-			DeleteGO(go);
-			go = 0L;
-			}
-		break;
-		}
-	return true;
-}
-
-
-int Usage()
-{
-	printf("______________________________________________________________\n");
-	printf("\nexprlp: RLPlot export utility, version %s.\n", SZ_VERSION);
-	printf("Copyright (C) 2002-2005 R. Lackner\n");
-	printf("This is free software published under the GNU\n");
-	printf("general public licence (GPL).\n");
-	printf("\nUsage: exprlp [options] <input> [options] [<output>]\n");
-	printf("\nOptions:\n");
-	printf("   -      use stdin/stdout as input or output file; requires\n");
-	printf("             that file format is set by -e | -s | -w option\n");
-	printf("             not an option in the strict sense\n");
-	printf("   -h     print this information\n");
-	printf("   -d     delete input file after read\n");
-	printf("   -e     output Encapsulated PostScript, *.eps\n");
-	printf("   -s     output Scalable Vector Graphics, *.svg\n");
-	printf("   -S     like -s, start output with \"Content-Type: image/svg+xml\"\n");
-	printf("   -v     print RLPlot version\n");
-	printf("   -w     output Windows Meta File, *.wmf\n");
-	printf("   -q     quiet mode: suppress output to the console\n");
-	printf("\nExamples:\n");
-	printf("   exprlp foo.rlp foo.svg      ;exports Scalable Vector Graphics\n");
-	printf("   exprlp -q foo.rlp foo.eps   ;exports Encapsulated PostScript, no messages\n");
-	printf("   exprlp foo.rlp foo.wmf      ;exports Windows Meta File\n");
-	printf("   exprlp -sq foo.rlp -        ;exports SVG to the console, no messages\n");
-	printf("   exprlp -eq - -              ;converts inputfile from stdin to EPS\n");
-	printf("                                   on stdout: useful for pipes\n");
-	printf("\n switch character is either \'-\' or \'/\'\n");
-	printf("______________________________________________________________\n\n");
-	if(szFile1) free(szFile1);	if(szFile2) free(szFile2);
-	szFile1 = szFile2 = 0L;
-	return 1;
-}
-
-int main (int argc, char **argv)
-{
-	ExpRoot *base = 0L;
-	int i, j, k;
-
-	for (i = 1, j = 0; i < argc; i++) {
-		if(argv[i][0] == '-' || (argv[i][0] == '/' && strlen(argv[i]) < 5)) {
-			//check for switch
-			for(k = 1; argv[i][k-1]; k++) {
-				switch(argv[i][k]){
-				case 'h':	case 'H':	case '?':
-					return Usage();
-				case 'd':
-					bDelete = true;
-					break;
-				case 'S':
-					bSVGtype = true;
-				case 's':
-					file_fmt = FF_SVG;
-					break;
-				case 'e':	case 'E':
-					file_fmt = FF_EPS;
-					break;
-				case 'r':	case 'R':
-					file_fmt = FF_RLP;
-					break;
-				case 'w':	case 'W':
-					file_fmt = FF_WMF;
-					break;
-				case 'q':	case 'Q':
-					bQuiet = true;
-					break;
-				case 'v':	case 'V':
-					printf("RLPlot version %s\n", SZ_VERSION);
-					return 0;
-				case '\0':
-					if(k == 1) {
-						if(j == 0) szFile1 = strdup("-");
-						else if(j == 1) szFile2 = strdup("-");
-						j++;
-						}
-					break;
-					}
-				}
-			}
-		else switch(j) {
-		case 0:
-			szFile1 = strdup(argv[i]);
-			j++;
-			break;
-		case 1:
-			szFile2 = strdup(argv[i]);
-			j++;
-			}
-		}
-	if(file_fmt == FF_UNKNOWN && szFile2 && (i = strlen(szFile2)) > 4) {
-		if(0==strcmp(".svg", szFile2+i-4)) file_fmt = FF_SVG;
-		else if(0==strcmp(".wmf", szFile2+i-4)) file_fmt = FF_WMF;
-		else if(0==strcmp(".eps", szFile2+i-4)) file_fmt = FF_EPS;
-		else if(0==strcmp(".rlp", szFile2+i-4)) file_fmt = FF_RLP;
-		}
-	if(file_fmt == FF_UNKNOWN) {
-		if(szFile1)printf("\n**** Unknown file extension or format ****\n\n");
-		return Usage();
-		}
-	if(!bQuiet) {
-		fprintf(stderr,"Input file \"%s\"\n", szFile1);
-		fprintf(stderr,"Output file \"%s\"\n", szFile2);
-		}
-	setlocale(LC_ALL, "");
-	if(!szFile1) return Usage();
-	base = new ExpRoot(szFile1, szFile2);
-	if(base) {
-		delete base;
-		}
-	if(szFile1) free(szFile1);	if(szFile2) free(szFile2);
-	return 0;
-}
+//exprlp.cpp, Copyright (c) 2002-2008 R.Lackner
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// A console application to process *.rlp files
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <locale.h>
+#include <unistd.h>			//required for unlink()
+#include "Version.h"
+#include "rlplot.h"
+
+int file_fmt = FF_UNKNOWN;
+bool bQuiet = false, bSVGtype = false, bDelete = false;
+char *szFile1 = 0L, *szFile2 = 0L;
+int dlgtxtheight = 12;				//stub: not used
+char *name1, *name2;				//the filenames
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// direct messages to console
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void InfoBox(char *Msg)
+{
+	if(!bQuiet) fprintf(stderr, "exprlp INFO: %s\n", Msg);
+}
+
+void ErrorBox(char *Msg)
+{
+	if(!bQuiet) fprintf(stderr, "exprlp ERROR: %s\n", Msg);
+}
+
+bool YesNoBox(char *Msg)
+{
+return false;
+}
+
+int YesNoCancelBox(char *Msg)
+{
+	return 0;
+}
+
+void HideCopyMark()
+{
+}
+
+void ShowCopyMark(anyOutput *out, RECT *mrk, int nRec)
+{
+}
+
+void CopyText(char *txt, int len)
+{
+}
+
+unsigned char* PasteText()
+{
+	return 0L;
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create a root object to handle I/O
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class ExpRoot:public GraphObj{
+public:
+	ExpRoot(char *file1, char *file2);
+	~ExpRoot();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+
+private:
+	GraphObj *go;
+};
+
+ExpRoot::ExpRoot(char *file1, char *file2):GraphObj(0L, 0L)
+{
+	if(file1 && strcmp("-", file1)) name1 = file1;
+	else name1 = 0L;
+	if(file2 && strcmp("-", file2)) name2 = file2;
+	else name2 = 0L;
+	go = 0L;
+	OpenGraph(this, name1, 0L, false);
+	if(bDelete && name1 && name1[0]) unlink(name1);
+}
+
+ExpRoot::~ExpRoot()
+{
+	if(go) {
+		DeleteGO(go);
+		if(!bQuiet)fprintf(stderr, "Object deleted after read\n");
+		}
+}
+
+bool
+ExpRoot::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+
+	switch(cmd) {
+	case CMD_DROP_GRAPH:
+		go = (GraphObj*)tmpl;
+		if(go) {
+			go->Command(CMD_SET_DATAOBJ, 0L, 0L);
+			switch(file_fmt){
+			case FF_SVG:
+				DoExportSvg(go, name2, bSVGtype ? 1L : 0L);
+				break;
+			case FF_WMF:
+				DoExportWmf(go, name2, 600.0f, 0L);
+				break;
+			case FF_EPS:
+				DoExportEps(go, name2, 0L);
+				break;
+			case FF_RLP:
+				SaveGraphAs(go);
+				break;
+			default:
+				ErrorBox("Unknown file extension or format of destination");
+				}
+			DeleteGO(go);
+			go = 0L;
+			}
+		break;
+		}
+	return true;
+}
+
+
+int Usage()
+{
+	printf("______________________________________________________________\n");
+	printf("\nexprlp: RLPlot export utility, version %s.\n", SZ_VERSION);
+	printf("Copyright (C) 2002-2005 R. Lackner\n");
+	printf("This is free software published under the GNU\n");
+	printf("general public licence (GPL).\n");
+	printf("\nUsage: exprlp [options] <input> [options] [<output>]\n");
+	printf("\nOptions:\n");
+	printf("   -      use stdin/stdout as input or output file; requires\n");
+	printf("             that file format is set by -e | -s | -w option\n");
+	printf("             not an option in the strict sense\n");
+	printf("   -h     print this information\n");
+	printf("   -d     delete input file after read\n");
+	printf("   -e     output Encapsulated PostScript, *.eps\n");
+	printf("   -s     output Scalable Vector Graphics, *.svg\n");
+	printf("   -S     like -s, start output with \"Content-Type: image/svg+xml\"\n");
+	printf("   -v     print RLPlot version\n");
+	printf("   -w     output Windows Meta File, *.wmf\n");
+	printf("   -q     quiet mode: suppress output to the console\n");
+	printf("\nExamples:\n");
+	printf("   exprlp foo.rlp foo.svg      ;exports Scalable Vector Graphics\n");
+	printf("   exprlp -q foo.rlp foo.eps   ;exports Encapsulated PostScript, no messages\n");
+	printf("   exprlp foo.rlp foo.wmf      ;exports Windows Meta File\n");
+	printf("   exprlp -sq foo.rlp -        ;exports SVG to the console, no messages\n");
+	printf("   exprlp -eq - -              ;converts inputfile from stdin to EPS\n");
+	printf("                                   on stdout: useful for pipes\n");
+	printf("\n switch character is either \'-\' or \'/\'\n");
+	printf("______________________________________________________________\n\n");
+	if(szFile1) free(szFile1);	if(szFile2) free(szFile2);
+	szFile1 = szFile2 = 0L;
+	return 1;
+}
+
+int main (int argc, char **argv)
+{
+	ExpRoot *base = 0L;
+	int i, j, k;
+
+	for (i = 1, j = 0; i < argc; i++) {
+		if(argv[i][0] == '-' || (argv[i][0] == '/' && strlen(argv[i]) < 5)) {
+			//check for switch
+			for(k = 1; argv[i][k-1]; k++) {
+				switch(argv[i][k]){
+				case 'h':	case 'H':	case '?':
+					return Usage();
+				case 'd':
+					bDelete = true;
+					break;
+				case 'S':
+					bSVGtype = true;
+				case 's':
+					file_fmt = FF_SVG;
+					break;
+				case 'e':	case 'E':
+					file_fmt = FF_EPS;
+					break;
+				case 'r':	case 'R':
+					file_fmt = FF_RLP;
+					break;
+				case 'w':	case 'W':
+					file_fmt = FF_WMF;
+					break;
+				case 'q':	case 'Q':
+					bQuiet = true;
+					break;
+				case 'v':	case 'V':
+					printf("RLPlot version %s\n", SZ_VERSION);
+					return 0;
+				case '\0':
+					if(k == 1) {
+						if(j == 0) szFile1 = strdup("-");
+						else if(j == 1) szFile2 = strdup("-");
+						j++;
+						}
+					break;
+					}
+				}
+			}
+		else switch(j) {
+		case 0:
+			szFile1 = strdup(argv[i]);
+			j++;
+			break;
+		case 1:
+			szFile2 = strdup(argv[i]);
+			j++;
+			}
+		}
+	if(file_fmt == FF_UNKNOWN && szFile2 && (i = strlen(szFile2)) > 4) {
+		if(0==strcmp(".svg", szFile2+i-4)) file_fmt = FF_SVG;
+		else if(0==strcmp(".wmf", szFile2+i-4)) file_fmt = FF_WMF;
+		else if(0==strcmp(".eps", szFile2+i-4)) file_fmt = FF_EPS;
+		else if(0==strcmp(".rlp", szFile2+i-4)) file_fmt = FF_RLP;
+		}
+	if(file_fmt == FF_UNKNOWN) {
+		if(szFile1)printf("\n**** Unknown file extension or format ****\n\n");
+		return Usage();
+		}
+	if(!bQuiet) {
+		fprintf(stderr,"Input file \"%s\"\n", szFile1);
+		fprintf(stderr,"Output file \"%s\"\n", szFile2);
+		}
+	if(!szFile1) return Usage();
+	base = new ExpRoot(szFile1, szFile2);
+	if(base) {
+		delete base;
+		}
+	if(szFile1) free(szFile1);	if(szFile2) free(szFile2);
+	return 0;
+}
diff --git a/menu.h b/menu.h
index 00a49ed..88e8777 100755
--- a/menu.h
+++ b/menu.h
@@ -1,106 +1,107 @@
-//menu.h, (C) 2006, 2007 R.Lackner
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-// menu declarations
-//
-
-#define CM_OPEN        500
-#define CM_SAVEDATAAS  501
-#define CM_EXIT        502
-#define CM_NEWGRAPH    503
-#define CM_NEWPAGE     504
-#define CM_DELGRAPH    505
-#define CM_ADDPLOT     506
-#define CM_ABOUT       507
-#define CM_ADDROWCOL   508
-#define CM_COPYGRAPH   509
-#define CM_SAVEGRAPHAS 510
-#define CM_REDRAW      511
-#define CM_ZOOM25      512
-#define CM_ZOOM50      513
-#define CM_ZOOM100     514
-#define CM_ZOOM200     515
-#define CM_ZOOM400     516
-#define CM_PRINT       517
-#define CM_EXPORT      518
-#define CM_DELOBJ      519
-#define CM_DEFAULTS    522
-#define CM_COPY        523
-#define CM_PASTE       524
-#define CM_UPDATE      525
-#define CM_ADDAXIS     526
-#define CM_UNDO        527
-#define CM_ZOOMIN      528
-#define CM_ZOOMOUT     529
-#define CM_ZOOMFIT     530
-#define CM_FILE1       531
-#define CM_FILE2       532
-#define CM_FILE3       533
-#define CM_FILE4       534
-#define CM_FILE5       535
-#define CM_FILE6       536
-#define CM_FILLRANGE   537
-#define CM_CUT         538
-#define CM_LEGEND      539
-#define CM_LAYERS      540
-#define CM_INSROW      541
-#define CM_INSCOL      542
-#define CM_DELROW      543
-#define CM_DELCOL      544
-#define CM_SAVEDATA    545
-
-#define CM_T_STANDARD  550
-#define CM_T_DRAW      551
-#define CM_T_POLYLINE  552
-#define CM_T_POLYGON   553
-#define CM_T_RECTANGLE 554
-#define CM_T_ROUNDREC  555
-#define CM_T_ELLIPSE   556
-#define CM_T_ARROW     557
-#define CM_T_TEXT      558
-
-#define CM_DELKEY      600
-#define CM_LEFTARRKEY  601
-#define CM_RIGHTARRKEY 602
-#define CM_UPARRKEY    603
-#define CM_DOWNARRKEY  604
-#define CM_TAB         605
-#define CM_SHTAB       606
-#define CM_PGUP        607
-#define CM_PGDOWN      608
-#define CM_POS_FIRST   609
-#define CM_POS_LAST    610
-#define CM_SHLEFT      611
-#define CM_SHRIGHT     612
-#define CM_SHUP        613
-#define CM_SHDOWN      614
-#define CM_SHPGUP      615
-#define CM_SHPGDOWN    616
-
-#define CM_SMPLSTAT    650
-#define CM_REPCMEANS   651
-#define CM_REPANOV     652
-#define CM_REPTWANOV   653
-#define CM_REPFRIEDM   654
-#define CM_REPTWANR    655
-#define CM_REPKRUSKAL  656
-#define CM_REPREGR     657
-#define CM_ROBUSTLINE  658
-#define CM_CORRELM     659
-#define CM_CORRELT     660
-#define CM_REPTWOWAY   661
+//menu.h, (C) 2006-2008 R.Lackner
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// menu declarations
+//
+
+#define CM_OPEN        500
+#define CM_SAVE        501
+#define CM_EXIT        502
+#define CM_NEWGRAPH    503
+#define CM_NEWPAGE     504
+#define CM_DELGRAPH    505
+#define CM_ADDPLOT     506
+#define CM_ABOUT       507
+#define CM_ADDROWCOL   508
+#define CM_COPYGRAPH   509
+#define CM_REDRAW      510
+#define CM_ZOOM25      511
+#define CM_ZOOM50      512
+#define CM_ZOOM100     513
+#define CM_ZOOM200     514
+#define CM_ZOOM400     515
+#define CM_PRINT       516
+#define CM_EXPORT      517
+#define CM_DELOBJ      518
+#define CM_DEFAULTS    519
+#define CM_COPY        520
+#define CM_PASTE       521
+#define CM_UPDATE      522
+#define CM_ADDAXIS     523
+#define CM_UNDO        524
+#define CM_ZOOMIN      525
+#define CM_ZOOMOUT     526
+#define CM_ZOOMFIT     527
+#define CM_FILE1       528
+#define CM_FILE2       529
+#define CM_FILE3       530
+#define CM_FILE4       531
+#define CM_FILE5       532
+#define CM_FILE6       533
+#define CM_FILLRANGE   534
+#define CM_CUT         535
+#define CM_LEGEND      536
+#define CM_LAYERS      537
+#define CM_INSROW      538
+#define CM_INSCOL      539
+#define CM_DELROW      540
+#define CM_DELCOL      541
+#define CM_SAVEAS      542
+#define CM_NEWINST     543
+
+#define CM_T_STANDARD  550
+#define CM_T_DRAW      551
+#define CM_T_POLYLINE  552
+#define CM_T_POLYGON   553
+#define CM_T_RECTANGLE 554
+#define CM_T_ROUNDREC  555
+#define CM_T_ELLIPSE   556
+#define CM_T_ARROW     557
+#define CM_T_TEXT      558
+
+#define CM_DELKEY      600
+#define CM_LEFTARRKEY  601
+#define CM_RIGHTARRKEY 602
+#define CM_UPARRKEY    603
+#define CM_DOWNARRKEY  604
+#define CM_TAB         605
+#define CM_SHTAB       606
+#define CM_PGUP        607
+#define CM_PGDOWN      608
+#define CM_POS_FIRST   609
+#define CM_POS_LAST    610
+#define CM_SHLEFT      611
+#define CM_SHRIGHT     612
+#define CM_SHUP        613
+#define CM_SHDOWN      614
+#define CM_SHPGUP      615
+#define CM_SHPGDOWN    616
+
+#define CM_SMPLSTAT    650
+#define CM_REPCMEANS   651
+#define CM_REPANOV     652
+#define CM_REPTWANOV   653
+#define CM_REPFRIEDM   654
+#define CM_REPTWANR    655
+#define CM_REPKRUSKAL  656
+#define CM_REPREGR     657
+#define CM_ROBUSTLINE  658
+#define CM_CORRELM     659
+#define CM_CORRELT     660
+#define CM_REPTWOWAY   661
+#define CM_REPBDANOV   662
diff --git a/mfcalc.cpp b/mfcalc.cpp
index 3b55224..a1cf5de 100755
--- a/mfcalc.cpp
+++ b/mfcalc.cpp
@@ -66,7 +66,7 @@
 
 
 /*
- mfcalc.y, mfcalc.cpp, Copyright (c) 2004-2006 R.Lackner
+ mfcalc.y, mfcalc.cpp, Copyright (c) 2004-2008 R.Lackner
  parse string and simple math: based on the bison 'mfcalc' example
 
     This file is part of RLPlot.
@@ -142,6 +142,7 @@ static int block_res;			//result of eval()
 static symrec *putsym (unsigned int h_name, unsigned int h2_name, int sym_type);
 static symrec *getsym (unsigned int h_name, unsigned int h2_name, char *sym_name = 0L);
 static int push(YYSTYPE *res, YYSTYPE *val);
+static void yyCompare(YYSTYPE *res, YYSTYPE *arg1, YYSTYPE *arg2, int op);
 static void store_res(YYSTYPE *res);
 static char *PushString(char *text);
 static double *PushArray(double *arr);
@@ -157,7 +158,7 @@ static void yyerror(char *s);
 static void make_time(YYSTYPE *dst, double h, double m, double s);
 static int yylex(void);
 static double nop() {return 0.0;};
-static double for_loop(char *block1, char *block2);
+static int for_loop(char *block1, char *block2);
 
 static char res_txt[1000];
 static anyResult line_res = {ET_UNKNOWN, 0.0, res_txt, 0L, 0};
@@ -168,7 +169,7 @@ static char *last_err_desc = 0L;	//short error description
 static char *buffer = 0L;		//the current command buffer
 static int buff_pos = 0;
 static bool bRecent = false;		//rearrange functions
-static bool bNoWrite, bNoExec;		//while editing ...
+static bool bNoWrite, bNoExec, bNoSS;	//while editing ...
 static int parse_level = 0;		//count reentrances into parser
 #define MAX_PARSE 50			//maximum number of recursive reentances 
 #include <stdio.h>
@@ -181,23 +182,23 @@ static int parse_level = 0;		//count reentrances into parser
 
 
 
-#define	YYFINAL		243
+#define	YYFINAL		221
 #define	YYFLAG		-32768
 #define	YYNTBASE	77
 
-#define YYTRANSLATE(x) ((unsigned)(x) <= 315 ? yytranslate[x] : 86)
+#define YYTRANSLATE(x) ((unsigned)(x) <= 315 ? yytranslate[x] : 87)
 
 static const char yytranslate[] = {     0,
-     2,     2,     2,     2,     2,     2,     2,     2,     2,    70,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,    72,
      2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
      2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
      2,     2,     2,     2,     2,     2,     2,     2,     2,    73,
-    74,    60,    59,    72,    58,     2,    61,     2,     2,     2,
-     2,     2,     2,     2,     2,     2,     2,    76,    71,     2,
+    74,    60,    59,    71,    58,     2,    61,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,    76,    70,     2,
     40,     2,    49,     2,     2,     2,     2,     2,     2,     2,
      2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
      2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
-    62,     2,    75,    63,     2,     2,     2,     2,     2,     2,
+    63,     2,    75,    62,     2,     2,     2,     2,     2,     2,
      2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
      2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
      2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
@@ -224,88 +225,79 @@ static const char yytranslate[] = {     0,
 
 #if YYDEBUG != 0
 static const short yyprhs[] = {     0,
-     0,     1,     4,     6,     8,    10,    13,    16,    19,    22,
-    25,    29,    35,    39,    43,    46,    48,    51,    53,    57,
-    61,    65,    70,    77,    84,    91,    98,   100,   102,   104,
-   108,   112,   114,   118,   120,   125,   127,   129,   133,   137,
-   141,   145,   149,   153,   157,   161,   163,   165,   167,   169,
-   171,   173,   175,   177,   179,   181,   183,   185,   187,   191,
-   195,   199,   203,   207,   211,   216,   221,   228,   237,   242,
-   247,   254,   261,   268,   275,   282,   289,   296,   303,   314,
-   321,   328,   332,   337,   344,   349,   358,   365,   369,   373,
-   377,   381,   384,   387,   390,   393,   397,   400,   404,   410,
-   415,   422,   429,   436,   443,   450,   456,   460,   466,   472,
-   478
+     0,     1,     4,     6,     8,    10,    12,    14,    17,    20,
+    23,    26,    30,    36,    40,    44,    47,    49,    52,    54,
+    58,    62,    66,    71,    78,    80,    82,    84,    88,    92,
+    94,    98,   100,   105,   107,   109,   113,   117,   121,   125,
+   129,   133,   137,   141,   143,   145,   147,   149,   151,   153,
+   155,   157,   159,   161,   163,   165,   167,   171,   175,   179,
+   183,   187,   191,   196,   201,   208,   217,   222,   227,   234,
+   245,   252,   259,   263,   268,   275,   280,   289,   296,   300,
+   304,   308,   312,   315,   318,   321,   324,   328,   331,   335,
+   341,   346,   353,   360,   367,   374,   381,   387,   391,   397,
+   403,   409
 };
 
 static const short yyrhs[] = {    -1,
-    77,    78,     0,    70,     0,    71,     0,    72,     0,    85,
-    70,     0,    85,    71,     0,    85,    72,     0,    79,    70,
-     0,    79,    71,     0,    14,     8,    83,     0,    14,     8,
-    83,    15,    83,     0,    23,     8,    83,     0,    22,     8,
-    83,     0,    26,     9,     0,    27,     0,     1,    70,     0,
-     5,     0,    79,    59,    85,     0,    85,    59,    79,     0,
-    79,    59,    79,     0,    35,    73,    85,    74,     0,    35,
-    73,    85,    13,    79,    74,     0,    35,    73,    85,    13,
-    85,    74,     0,    35,    73,    85,    72,    79,    74,     0,
-    35,    73,    85,    72,    85,    74,     0,    79,     0,     6,
-     0,    85,     0,    81,    45,    81,     0,    81,    46,    85,
-     0,    80,     0,     3,    47,     3,     0,     4,     0,    30,
-    73,    85,    74,     0,    16,     0,    17,     0,    85,    50,
-    85,     0,    85,    51,    85,     0,    85,    52,    85,     0,
-    85,    53,    85,     0,    85,    54,    85,     0,    85,    55,
-    85,     0,    85,    56,    85,     0,    85,    57,    85,     0,
-     7,     0,     9,     0,    85,     0,    79,     0,     3,     0,
-    25,     0,    82,     0,    34,     0,    12,     0,    10,     0,
-    11,     0,    28,     0,    83,     0,    28,    40,    85,     0,
-    28,    40,    79,     0,    28,    41,    85,     0,    28,    42,
-    85,     0,    28,    43,    85,     0,    28,    44,    85,     0,
-    29,    73,    85,    74,     0,    31,    73,    81,    74,     0,
-    31,    73,    85,    13,    81,    74,     0,    31,    73,    85,
-    13,    85,    13,    85,    74,     0,    32,    73,    79,    74,
-     0,    32,    73,    85,    74,     0,    32,    73,    79,    13,
-    79,    74,     0,    32,    73,    85,    13,    79,    74,     0,
-    32,    73,    85,    13,    85,    74,     0,    32,    73,    79,
-    13,    85,    74,     0,    32,    73,    79,    72,    79,    74,
-     0,    32,    73,    85,    72,    79,    74,     0,    32,    73,
-    79,    72,    85,    74,     0,    32,    73,    85,    72,    85,
-    74,     0,    37,    73,    85,    13,    85,    13,    81,    13,
-    80,    74,     0,    33,    73,    81,    13,    80,    74,     0,
-    33,    73,    81,    13,    25,    74,     0,    36,    73,    74,
-     0,    36,    73,    81,    74,     0,    38,    73,    84,    13,
-    84,    74,     0,    38,    73,    84,    74,     0,    39,    73,
-    84,    13,    84,    13,    84,    74,     0,    39,    73,    84,
-    13,    84,    74,     0,    85,    59,    85,     0,    85,    58,
-    85,     0,    85,    60,    85,     0,    85,    61,    85,     0,
-    28,    65,     0,    28,    66,     0,    65,    28,     0,    66,
-    28,     0,    85,    63,    85,     0,    58,    85,     0,    73,
-    84,    74,     0,    21,    28,    62,    85,    75,     0,    85,
-    62,    85,    75,     0,    85,    62,    85,    75,    40,    85,
-     0,    85,    62,    85,    75,    41,    85,     0,    85,    62,
-    85,    75,    42,    85,     0,    85,    62,    85,    75,    43,
-    85,     0,    85,    62,    85,    75,    44,    85,     0,     3,
-    76,     3,    76,     3,     0,     3,    76,     3,     0,    85,
-    49,    85,    48,    85,     0,    85,    49,     5,    48,     5,
-     0,    85,    49,     5,    48,    85,     0,    85,    49,    85,
-    48,     5,     0
+    77,    79,     0,    70,     0,    71,     0,    72,     0,    70,
+     0,    71,     0,    86,    72,     0,    86,    78,     0,    80,
+    72,     0,    80,    70,     0,    14,     8,    84,     0,    14,
+     8,    84,    15,    84,     0,    23,     8,    84,     0,    22,
+     8,    84,     0,    26,     9,     0,    27,     0,     1,    72,
+     0,     5,     0,    80,    59,    86,     0,    86,    59,    80,
+     0,    80,    59,    80,     0,    35,    73,    86,    74,     0,
+    35,    73,    86,    78,    80,    74,     0,    80,     0,     6,
+     0,    86,     0,    82,    45,    82,     0,    82,    46,    86,
+     0,    81,     0,     3,    47,     3,     0,     4,     0,    30,
+    73,    86,    74,     0,    16,     0,    17,     0,    86,    50,
+    86,     0,    86,    51,    86,     0,    86,    52,    86,     0,
+    86,    53,    86,     0,    86,    54,    86,     0,    86,    55,
+    86,     0,    86,    56,    86,     0,    86,    57,    86,     0,
+     7,     0,     9,     0,    86,     0,    80,     0,     3,     0,
+    25,     0,    83,     0,    34,     0,    12,     0,    10,     0,
+    11,     0,    28,     0,    84,     0,    28,    40,    86,     0,
+    28,    40,    80,     0,    28,    41,    86,     0,    28,    42,
+    86,     0,    28,    43,    86,     0,    28,    44,    86,     0,
+    29,    73,    86,    74,     0,    31,    73,    82,    74,     0,
+    31,    73,    86,    13,    82,    74,     0,    31,    73,    86,
+    13,    86,    13,    86,    74,     0,    32,    73,    80,    74,
+     0,    32,    73,    86,    74,     0,    32,    73,    85,    78,
+    85,    74,     0,    37,    73,    86,    13,    86,    13,    82,
+    13,    81,    74,     0,    33,    73,    82,    13,    81,    74,
+     0,    33,    73,    82,    13,    25,    74,     0,    36,    73,
+    74,     0,    36,    73,    82,    74,     0,    38,    73,    85,
+    13,    85,    74,     0,    38,    73,    85,    74,     0,    39,
+    73,    85,    13,    85,    13,    85,    74,     0,    39,    73,
+    85,    13,    85,    74,     0,    86,    59,    86,     0,    86,
+    58,    86,     0,    86,    60,    86,     0,    86,    61,    86,
+     0,    28,    65,     0,    28,    66,     0,    65,    28,     0,
+    66,    28,     0,    86,    62,    86,     0,    58,    86,     0,
+    73,    85,    74,     0,    21,    28,    63,    86,    75,     0,
+    86,    63,    86,    75,     0,    86,    63,    86,    75,    40,
+    86,     0,    86,    63,    86,    75,    41,    86,     0,    86,
+    63,    86,    75,    42,    86,     0,    86,    63,    86,    75,
+    43,    86,     0,    86,    63,    86,    75,    44,    86,     0,
+     3,    76,     3,    76,     3,     0,     3,    76,     3,     0,
+    86,    49,    86,    48,    86,     0,    86,    49,     5,    48,
+     5,     0,    86,    49,     5,    48,    86,     0,    86,    49,
+    86,    48,     5,     0
 };
 
 #endif
 
 #if YYDEBUG != 0
 static const short yyrline[] = { 0,
-   136,   137,   140,   140,   140,   141,   142,   143,   144,   145,
-   146,   148,   151,   152,   154,   155,   156,   159,   161,   162,
-   163,   164,   165,   166,   167,   168,   171,   175,   176,   177,
-   178,   179,   180,   184,   185,   186,   187,   188,   189,   190,
-   191,   192,   193,   194,   195,   198,   198,   200,   200,   202,
-   203,   204,   205,   206,   207,   208,   209,   210,   211,   212,
-   213,   214,   215,   216,   218,   219,   220,   222,   225,   226,
-   227,   228,   229,   230,   231,   232,   233,   234,   235,   236,
-   237,   238,   239,   240,   241,   242,   243,   244,   248,   252,
-   253,   255,   256,   257,   258,   259,   260,   261,   262,   264,
-   266,   270,   274,   278,   282,   287,   288,   289,   290,   291,
-   292
+   137,   138,   141,   141,   143,   143,   143,   144,   145,   146,
+   147,   148,   150,   153,   154,   156,   157,   158,   161,   163,
+   164,   165,   166,   167,   170,   174,   175,   176,   177,   178,
+   179,   183,   184,   185,   186,   187,   188,   189,   190,   191,
+   192,   193,   194,   197,   197,   199,   199,   201,   202,   203,
+   204,   205,   206,   207,   208,   209,   210,   211,   212,   213,
+   214,   215,   217,   218,   219,   221,   224,   225,   226,   227,
+   228,   229,   230,   231,   232,   233,   234,   235,   236,   240,
+   244,   245,   247,   248,   249,   250,   251,   252,   253,   254,
+   256,   258,   262,   266,   270,   274,   279,   280,   281,   282,
+   283,   284
 };
 #endif
 
@@ -318,340 +310,296 @@ static const char * const yytname[] = {   "$","error","$undefined.","NUM","BOOLV
 "RETURN","BREAK","VAR","FNCT","BFNCT","AFNCT","SFNCT","FUNC1","TXT","SRFUNC",
 "YYFNC","FUNC4","YYFNC2","YYFNC3","'='","ADDEQ","SUBEQ","MULEQ","DIVEQ","LSEP",
 "CLAUSE","SER","COLC","'?'","AND","OR","EQ","NE","GT","GE","LT","LE","'-'","'+'",
-"'*'","'/'","'['","'^'","NEG","INC","DEC","PINC","PDEC","PDIM","'\\n'","';'",
-"','","'('","')'","']'","':'","input","line","str_exp","range","arr","bool",
+"'*'","'/'","'^'","'['","NEG","INC","DEC","PINC","PDEC","PDIM","';'","','","'\\n'",
+"'('","')'","']'","':'","input","anysep","line","str_exp","range","arr","bool",
 "block","anyarg","exp", NULL
 };
 #endif
 
 static const short yyr1[] = {     0,
-    77,    77,    78,    78,    78,    78,    78,    78,    78,    78,
-    78,    78,    78,    78,    78,    78,    78,    79,    79,    79,
-    79,    79,    79,    79,    79,    79,    80,    81,    81,    81,
-    81,    81,    81,    82,    82,    82,    82,    82,    82,    82,
-    82,    82,    82,    82,    82,    83,    83,    84,    84,    85,
-    85,    85,    85,    85,    85,    85,    85,    85,    85,    85,
-    85,    85,    85,    85,    85,    85,    85,    85,    85,    85,
-    85,    85,    85,    85,    85,    85,    85,    85,    85,    85,
-    85,    85,    85,    85,    85,    85,    85,    85,    85,    85,
-    85,    85,    85,    85,    85,    85,    85,    85,    85,    85,
-    85,    85,    85,    85,    85,    85,    85,    85,    85,    85,
-    85
+    77,    77,    78,    78,    79,    79,    79,    79,    79,    79,
+    79,    79,    79,    79,    79,    79,    79,    79,    80,    80,
+    80,    80,    80,    80,    81,    82,    82,    82,    82,    82,
+    82,    83,    83,    83,    83,    83,    83,    83,    83,    83,
+    83,    83,    83,    84,    84,    85,    85,    86,    86,    86,
+    86,    86,    86,    86,    86,    86,    86,    86,    86,    86,
+    86,    86,    86,    86,    86,    86,    86,    86,    86,    86,
+    86,    86,    86,    86,    86,    86,    86,    86,    86,    86,
+    86,    86,    86,    86,    86,    86,    86,    86,    86,    86,
+    86,    86,    86,    86,    86,    86,    86,    86,    86,    86,
+    86,    86
 };
 
 static const short yyr2[] = {     0,
-     0,     2,     1,     1,     1,     2,     2,     2,     2,     2,
-     3,     5,     3,     3,     2,     1,     2,     1,     3,     3,
-     3,     4,     6,     6,     6,     6,     1,     1,     1,     3,
-     3,     1,     3,     1,     4,     1,     1,     3,     3,     3,
-     3,     3,     3,     3,     3,     1,     1,     1,     1,     1,
-     1,     1,     1,     1,     1,     1,     1,     1,     3,     3,
-     3,     3,     3,     3,     4,     4,     6,     8,     4,     4,
-     6,     6,     6,     6,     6,     6,     6,     6,    10,     6,
-     6,     3,     4,     6,     4,     8,     6,     3,     3,     3,
-     3,     2,     2,     2,     2,     3,     2,     3,     5,     4,
-     6,     6,     6,     6,     6,     5,     3,     5,     5,     5,
-     5
+     0,     2,     1,     1,     1,     1,     1,     2,     2,     2,
+     2,     3,     5,     3,     3,     2,     1,     2,     1,     3,
+     3,     3,     4,     6,     1,     1,     1,     3,     3,     1,
+     3,     1,     4,     1,     1,     3,     3,     3,     3,     3,
+     3,     3,     3,     1,     1,     1,     1,     1,     1,     1,
+     1,     1,     1,     1,     1,     1,     3,     3,     3,     3,
+     3,     3,     4,     4,     6,     8,     4,     4,     6,    10,
+     6,     6,     3,     4,     6,     4,     8,     6,     3,     3,
+     3,     3,     2,     2,     2,     2,     3,     2,     3,     5,
+     4,     6,     6,     6,     6,     6,     5,     3,     5,     5,
+     5,     5
 };
 
 static const short yydefact[] = {     1,
-     0,     0,    50,    34,    18,    46,    47,    55,    56,    54,
-     0,    36,    37,     0,     0,     0,    51,     0,    16,    57,
-     0,     0,     0,     0,     0,    53,     0,     0,     0,     0,
-     0,     0,     0,     0,     3,     4,     5,     0,     2,     0,
-    52,    58,     0,    17,     0,     0,     0,     0,     0,    15,
-     0,     0,     0,     0,     0,    92,    93,     0,     0,     0,
-     0,     0,     0,     0,     0,     0,     0,    97,    94,    95,
-    49,     0,    48,     0,     9,    10,     0,     0,     0,     0,
+     0,     0,    48,    32,    19,    44,    45,    53,    54,    52,
+     0,    34,    35,     0,     0,     0,    49,     0,    17,    55,
+     0,     0,     0,     0,     0,    51,     0,     0,     0,     0,
+     0,     0,     0,     0,     6,     7,     5,     0,     2,     0,
+    50,    56,     0,    18,     0,     0,     0,     0,     0,    16,
+     0,     0,     0,     0,     0,    83,    84,     0,     0,     0,
+     0,     0,     0,     0,     0,     0,     0,    88,    85,    86,
+    47,     0,    46,     0,    11,    10,     0,     0,     0,     0,
      0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-     0,     6,     7,     8,   107,    11,     0,    14,    13,    60,
-    59,    61,    62,    63,    64,     0,     0,    50,    28,    27,
-    32,     0,    29,     0,     0,     0,    29,     0,    82,     0,
-     0,     0,     0,     0,    98,    21,    19,     0,     0,    38,
-    39,    40,    41,    42,    43,    44,    45,    89,    20,    88,
-    90,    91,     0,    96,     0,     0,     0,    65,    35,     0,
-     0,     0,    66,     0,     0,     0,    69,     0,     0,    70,
-     0,     0,     0,    22,    83,     0,     0,    85,     0,    88,
-     0,     0,   100,   106,    12,    99,    33,    30,    31,     0,
-    29,     0,     0,     0,     0,     0,     0,     0,     0,    51,
-     0,     0,     0,     0,     0,     0,     0,     0,     0,   109,
-   110,   111,   108,     0,     0,     0,     0,     0,    67,     0,
-    71,    74,    75,    77,    72,    73,    76,    78,    81,    80,
-    23,    24,    25,    26,     0,    84,     0,    87,   101,   102,
-   103,   104,   105,     0,     0,     0,    68,     0,    86,     0,
-    79,     0,     0
+     0,     3,     4,     8,     9,    98,    12,     0,    15,    14,
+    58,    57,    59,    60,    61,    62,     0,     0,    48,    26,
+    25,    30,     0,    27,    47,     0,    46,     0,    27,     0,
+    73,     0,     0,     0,     0,     0,    89,    22,    20,     0,
+     0,    36,    37,    38,    39,    40,    41,    42,    43,    80,
+    21,    79,    81,    82,    87,     0,     0,     0,     0,    63,
+    33,     0,     0,     0,    64,     0,    67,     0,    68,     0,
+    23,     0,    74,     0,     0,    76,     0,    79,     0,     0,
+    91,    97,    13,    90,    31,    28,    29,     0,    27,     0,
+    49,     0,     0,     0,     0,     0,     0,   100,   101,   102,
+    99,     0,     0,     0,     0,     0,    65,     0,    69,    72,
+    71,    24,     0,    75,     0,    78,    92,    93,    94,    95,
+    96,     0,     0,     0,    66,     0,    77,     0,    70,     0,
+     0
 };
 
 static const short yydefgoto[] = {     1,
-    39,   110,   111,   112,    41,    42,    72,    73
+    95,    39,   111,   112,   113,    41,    42,    72,    73
 };
 
 static const short yypact[] = {-32768,
-   218,   -41,   -44,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
-    27,-32768,-32768,     8,    34,    40,-32768,    58,-32768,    59,
-     1,     2,    24,    25,    37,-32768,    38,    48,    49,    53,
-    54,   746,    15,    44,-32768,-32768,-32768,   426,-32768,    35,
--32768,-32768,  1008,-32768,   104,    21,    46,    21,    21,-32768,
-   426,   746,   746,   746,   746,-32768,-32768,   746,   746,   361,
-   426,   361,   746,   289,   746,   426,   426,-32768,-32768,-32768,
-    69,    39,  1048,   426,-32768,-32768,   556,   746,   746,   746,
-   746,   746,   746,   746,   746,   746,   426,   746,   746,   746,
-   746,-32768,-32768,-32768,    68,   130,   746,-32768,-32768,    69,
-  1048,  1063,  1063,  1063,  1063,   680,   740,   -20,-32768,    69,
--32768,   -30,    80,    -4,   773,   -12,  1048,   800,-32768,   -27,
-   419,    -5,   134,   746,-32768,-32768,   137,   100,  1033,   131,
-   131,    57,    57,    57,    57,    57,    57,   137,-32768,   137,
-     3,     3,   353,-32768,   146,    21,   613,-32768,-32768,   149,
-   361,   746,-32768,   361,   426,   426,-32768,   426,   426,-32768,
-   491,   426,   426,-32768,-32768,   746,   426,-32768,   426,   137,
-   621,   686,   136,-32768,-32768,-32768,-32768,   110,  1063,   -25,
-   484,   -57,   826,   -37,   852,   -36,   878,   -35,   904,    89,
-    90,  1048,   -34,   930,   -33,   956,   549,    93,    -3,-32768,
-  1063,-32768,  1063,   746,   746,   746,   746,   746,-32768,   746,
--32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
--32768,-32768,-32768,-32768,   361,-32768,   426,-32768,  1063,  1063,
-  1063,  1063,  1063,   982,     0,    94,-32768,   426,-32768,    99,
--32768,   181,-32768
+   235,   -46,   -48,-32768,-32768,-32768,-32768,-32768,-32768,-32768,
+    22,-32768,-32768,     4,    29,    33,-32768,    35,-32768,   145,
+     1,     2,    18,    20,    21,-32768,    23,    34,    36,    37,
+    39,   763,    42,    44,-32768,-32768,-32768,   443,-32768,   -30,
+-32768,-32768,   814,-32768,    43,    13,    50,    13,    13,-32768,
+   443,   763,   763,   763,   763,-32768,-32768,   763,   763,   378,
+   443,   378,   763,   306,   763,   443,   443,-32768,-32768,-32768,
+     6,    40,   854,   443,-32768,-32768,   573,   763,   763,   763,
+   763,   763,   763,   763,   763,   763,   443,   763,   763,   763,
+   763,-32768,-32768,-32768,-32768,    46,   108,   763,-32768,-32768,
+     6,   854,   869,   869,   869,   869,   631,   697,   -20,-32768,
+     6,-32768,   -31,   121,   -36,   -22,   756,   -12,   854,   566,
+-32768,   -29,   228,    -8,   111,   763,-32768,-32768,    56,    78,
+   839,   170,   170,    77,    77,    77,    77,    77,    77,    56,
+-32768,    56,     5,     5,    64,   298,   126,    13,   370,-32768,
+-32768,   127,   378,   763,-32768,   378,-32768,   443,-32768,   508,
+-32768,   443,-32768,   763,   443,-32768,   443,    56,   638,   703,
+    60,-32768,-32768,-32768,-32768,    85,   869,   -27,   436,    58,
+    59,    68,   854,   -35,   501,    69,    -3,-32768,   869,-32768,
+   869,   763,   763,   763,   763,   763,-32768,   763,-32768,-32768,
+-32768,-32768,   378,-32768,   443,-32768,   869,   869,   869,   869,
+   869,   788,   -10,    70,-32768,   443,-32768,    73,-32768,   148,
+-32768
 };
 
 static const short yypgoto[] = {-32768,
--32768,   108,  -147,   -59,-32768,   -42,   -55,    -1
+   -95,-32768,    54,  -147,   -58,-32768,   -37,   -59,    -1
 };
 
 
-#define	YYLAST		1126
+#define	YYLAST		932
 
 
 static const short yytable[] = {    43,
-   161,    74,   116,    96,   120,    98,    99,   167,   155,   227,
-   122,   123,   238,   191,   151,   152,   211,   151,   152,   151,
-   152,    74,    74,    74,    74,    74,   150,     6,    44,     7,
-    68,    45,   151,   152,    46,    47,   213,   215,   217,   221,
-   223,    48,    69,   153,   151,   152,   165,    49,   209,   101,
-   102,   103,   104,   105,    74,    45,   106,   107,   113,   115,
-   117,   118,   117,   121,    90,    91,    50,   156,   168,   157,
-   228,    70,   127,    58,    59,   129,   130,   131,   132,   133,
-   134,   135,   136,   137,   138,   140,   141,   142,   143,   144,
-   240,   178,   154,    74,   180,   147,    60,    61,    51,    52,
-    53,    54,    55,   175,    75,    76,    95,    97,    40,    62,
-    63,   198,   125,   199,    86,   124,    88,    89,    90,    91,
-    64,    65,   170,    56,    57,    66,    67,    74,    77,    78,
-    79,    80,    81,    82,    83,    84,    85,    86,    87,    88,
-    89,    90,    91,   145,   146,    71,   169,   171,   174,   117,
-   179,   177,   181,   183,   185,   152,   187,   189,   100,   192,
-   194,   196,   219,   220,   197,   235,   226,   239,   114,   201,
-   203,   236,   241,    71,    71,   204,   205,   206,   207,   208,
-   243,   126,    80,    81,    82,    83,    84,    85,    86,   124,
-    88,    89,    90,    91,   139,     0,    88,    89,    90,    91,
-     0,     0,   229,   230,   231,   232,   233,     0,   234,     0,
-     0,     0,     0,     0,     0,     0,     0,   242,     2,     0,
-     3,     4,     5,   117,     6,     0,     7,     8,     9,    10,
-     0,    11,     0,    12,    13,     0,   192,     0,    14,    15,
-    16,     0,    17,    18,    19,    20,    21,    22,    23,    24,
-    25,    26,    27,    28,    29,    30,    31,     0,     0,     0,
-     0,     0,   182,   184,     0,   186,   188,     0,     0,   193,
-   195,     0,     0,     0,    71,    32,    71,     0,     0,     0,
-     0,     0,    33,    34,     0,     0,     0,    35,    36,    37,
-    38,   108,     4,     5,   109,     6,     0,     7,     8,     9,
-    10,     0,     0,     0,    12,    13,     0,     0,     0,    14,
-     0,     0,     0,    17,     0,     0,    20,    21,    22,    23,
-    24,    25,    26,    27,    28,    29,    30,    31,     0,     0,
-     0,     0,     0,     0,    71,     0,     0,     0,     0,     0,
-     0,     0,     0,     0,     0,     0,    32,     0,     0,     0,
-     0,     0,     0,    33,    34,     0,     0,     0,     0,     0,
-     0,    38,   119,   108,     4,     5,   109,     6,     0,     7,
-     8,     9,    10,     0,     0,     0,    12,    13,     0,     0,
-     0,    14,     0,     0,     0,    17,     0,     0,    20,    21,
-    22,    23,    24,    25,    26,    27,    28,    29,    30,    31,
-     0,    77,    78,    79,    80,    81,    82,    83,    84,    85,
-    86,   124,    88,    89,    90,    91,     0,     0,    32,     0,
-     0,     0,     0,     0,     0,    33,    34,   173,     3,     4,
-     5,   166,     6,    38,     7,     8,     9,    10,     0,     0,
+   160,   116,   216,   118,   165,   122,   124,   125,    97,   205,
+    99,   100,   182,   153,   154,   153,   154,   153,   154,     6,
+   158,     7,    74,    74,   162,    44,   152,    45,    74,    46,
+    68,    47,   153,   154,   153,   154,    48,   157,   202,    75,
+    49,    76,   155,    50,   163,    96,   197,    92,    93,   102,
+   103,   104,   105,   106,    40,    45,   107,   108,   114,   117,
+   119,   120,   119,   123,    74,   166,    90,    91,   218,    69,
+   206,    70,   129,    58,    59,   131,   132,   133,   134,   135,
+   136,   137,   138,   139,   140,   142,   143,   144,   145,   146,
+    60,    71,    61,    62,   176,    63,   149,   178,   180,   192,
+   193,   194,   195,   196,   101,   186,    64,   187,    65,    66,
+   173,    67,    98,   127,   115,    88,    89,    90,    91,    71,
+    71,   147,   148,   167,   168,   169,    91,   128,   172,   175,
+   154,   199,   200,   156,    86,   126,    88,    89,    90,    91,
+   141,   201,   204,   217,   213,   214,   219,   221,     0,     0,
+     0,   119,   177,     0,   179,     0,     0,     0,   183,     0,
+   183,     0,   185,     0,     0,     0,     0,   189,   191,    77,
+    78,    79,    80,    81,    82,    83,    84,    85,    86,    87,
+    88,    89,    90,    91,    51,    52,    53,    54,    55,     0,
+   207,   208,   209,   210,   211,     0,   212,     0,     0,     0,
+     0,   119,     0,     0,     0,     0,     0,     0,     0,    56,
+    57,    71,     0,     0,   183,   184,     0,     0,    71,     0,
+    71,    80,    81,    82,    83,    84,    85,    86,   126,    88,
+    89,    90,    91,     0,   220,     2,     0,     3,     4,     5,
+   164,     6,     0,     7,     8,     9,    10,     0,    11,     0,
+    12,    13,     0,     0,     0,    14,    15,    16,    71,    17,
+    18,    19,    20,    21,    22,    23,    24,    25,    26,    27,
+    28,    29,    30,    31,     0,     0,    77,    78,    79,    80,
+    81,    82,    83,    84,    85,    86,   126,    88,    89,    90,
+    91,     0,    32,     0,     0,     0,     0,     0,     0,    33,
+    34,     0,     0,     0,    35,    36,    37,    38,   109,     4,
+     5,   110,     6,     0,     7,     8,     9,    10,     0,     0,
      0,    12,    13,     0,     0,     0,    14,     0,     0,     0,
     17,     0,     0,    20,    21,    22,    23,    24,    25,    26,
-    27,    28,    29,    30,    31,     0,     0,    77,    78,    79,
-    80,    81,    82,    83,    84,    85,    86,   124,    88,    89,
-    90,    91,     0,    32,     0,     0,     0,     0,     0,     0,
-    33,    34,     0,     3,     4,     5,   210,     6,    38,     7,
-     8,     9,    10,     0,     0,     0,    12,    13,     0,     0,
-     0,    14,     0,     0,     0,   190,     0,     0,    20,    21,
-    22,    23,    24,    25,    26,    27,    28,    29,    30,    31,
-     0,     0,    77,    78,    79,    80,    81,    82,    83,    84,
-    85,    86,    87,    88,    89,    90,    91,     0,    32,     0,
-     0,     0,     0,     0,     0,    33,    34,     0,     3,     4,
-   128,   225,     6,    38,     7,     8,     9,    10,     0,     0,
-     0,    12,    13,     0,     0,     0,    14,     0,     0,     0,
-    17,     0,     0,    20,    21,    22,    23,    24,    25,    26,
-     0,    28,    29,    30,    31,     0,     0,    77,    78,    79,
-    80,    81,    82,    83,    84,    85,    86,   124,    88,    89,
-    90,    91,     0,    32,     0,     0,     0,     0,     0,     0,
-    33,    34,     0,     3,     4,   200,     0,     6,    38,     7,
-     8,     9,    10,     0,     0,     0,    12,    13,     0,     0,
-     0,    14,     0,     0,     0,    17,     0,     0,    20,    21,
-    22,    23,    24,    25,    26,     0,    28,    29,    30,    31,
-     0,    77,    78,    79,    80,    81,    82,    83,    84,    85,
-    86,   124,    88,    89,    90,    91,     0,     0,    32,     0,
-     0,     0,     0,     0,     0,    33,    34,   176,     3,     4,
-   202,     0,     6,    38,     7,     8,     9,    10,     0,     0,
-     0,    12,    13,     0,     0,     0,    14,     0,     0,     0,
-    17,     0,     0,    20,    21,    22,    23,    24,    25,    26,
-     0,    28,    29,    30,    31,     0,     0,     0,    77,    78,
-    79,    80,    81,    82,    83,    84,    85,    86,   124,    88,
-    89,    90,    91,    32,     0,     0,     0,     0,     3,     4,
-    33,    34,     6,   148,     7,     8,     9,    10,    38,     0,
-     0,    12,    13,     0,     0,     0,    14,     0,     0,     0,
-    17,     0,     0,    20,    21,    22,    23,    24,    25,    26,
-     0,    28,    29,    30,    31,   158,     0,     0,    77,    78,
-    79,    80,    81,    82,    83,    84,    85,    86,   124,    88,
-    89,    90,    91,    32,     0,     0,     0,     0,     0,     0,
-    33,    34,   162,   149,     0,     0,     0,     0,    38,     0,
-     0,    77,    78,    79,    80,    81,    82,    83,    84,    85,
-    86,    87,    88,    89,    90,    91,     0,     0,     0,     0,
-     0,     0,     0,     0,   159,     0,   160,     0,    77,    78,
-    79,    80,    81,    82,    83,    84,    85,    86,   124,    88,
-    89,    90,    91,     0,     0,     0,     0,     0,     0,     0,
-     0,   163,     0,   164,    77,    78,    79,    80,    81,    82,
+    27,    28,    29,    30,    31,     0,    77,    78,    79,    80,
+    81,    82,    83,    84,    85,    86,   126,    88,    89,    90,
+    91,     0,     0,    32,     0,     0,     0,     0,     0,     0,
+    33,    34,   171,     0,     0,     0,     0,     0,    38,   121,
+   109,     4,     5,   110,     6,     0,     7,     8,     9,    10,
+     0,     0,     0,    12,    13,     0,     0,     0,    14,     0,
+     0,     0,    17,     0,     0,    20,    21,    22,    23,    24,
+    25,    26,    27,    28,    29,    30,    31,     0,    77,    78,
+    79,    80,    81,    82,    83,    84,    85,    86,   126,    88,
+    89,    90,    91,     0,     0,    32,     0,     0,     0,     0,
+     0,     0,    33,    34,   174,     3,     4,     5,   198,     6,
+    38,     7,     8,     9,    10,     0,     0,     0,    12,    13,
+     0,     0,     0,    14,     0,     0,     0,    17,     0,     0,
+    20,    21,    22,    23,    24,    25,    26,    27,    28,    29,
+    30,    31,     0,     0,    77,    78,    79,    80,    81,    82,
+    83,    84,    85,    86,    87,    88,    89,    90,    91,     0,
+    32,     0,     0,     0,     0,     0,     0,    33,    34,     0,
+     3,     4,     5,   203,     6,    38,     7,     8,     9,    10,
+     0,     0,     0,    12,    13,     0,     0,     0,    14,     0,
+     0,     0,   181,     0,     0,    20,    21,    22,    23,    24,
+    25,    26,    27,    28,    29,    30,    31,     0,     0,    77,
+    78,    79,    80,    81,    82,    83,    84,    85,    86,   126,
+    88,    89,    90,    91,     0,    32,     0,     0,     0,     0,
+     0,     0,    33,    34,     0,     3,     4,   130,     0,     6,
+    38,     7,     8,     9,    10,     0,     0,     0,    12,    13,
+     0,     0,     0,    14,     0,     0,     0,    17,     0,     0,
+    20,    21,    22,    23,    24,    25,    26,     0,    28,    29,
+    30,    31,     0,     0,    77,    78,    79,    80,    81,    82,
+    83,    84,    85,    86,   126,    88,    89,    90,    91,     0,
+    32,     0,     0,     0,     0,    92,    93,    33,    34,   161,
+     3,     4,   188,     0,     6,    38,     7,     8,     9,    10,
+     0,     0,     0,    12,    13,     0,     0,     0,    14,     0,
+     0,     0,    17,     0,     0,    20,    21,    22,    23,    24,
+    25,    26,     0,    28,    29,    30,    31,     0,     0,    77,
+    78,    79,    80,    81,    82,    83,    84,    85,    86,   126,
+    88,    89,    90,    91,     0,    32,     0,     0,     0,     0,
+     0,     0,    33,    34,   150,     3,     4,   190,     0,     6,
+    38,     7,     8,     9,    10,     0,     0,     0,    12,    13,
+     0,     0,     0,    14,     0,     0,     0,    17,     0,     0,
+    20,    21,    22,    23,    24,    25,    26,     0,    28,    29,
+    30,    31,     0,     0,     0,    77,    78,    79,    80,    81,
+    82,    83,    84,    85,    86,   126,    88,    89,    90,    91,
+    32,     0,     0,     0,     0,     3,     4,    33,    34,     6,
+   151,     7,     8,     9,    10,    38,     0,     0,    12,    13,
+     0,     0,     0,    14,     0,     0,     0,    17,     0,     0,
+    20,    21,    22,    23,    24,    25,    26,     0,    28,    29,
+    30,    31,     0,     0,    77,    78,    79,    80,    81,    82,
     83,    84,    85,    86,    87,    88,    89,    90,    91,     0,
-     0,     0,     0,     0,     0,     0,     0,     0,     0,   212,
-    77,    78,    79,    80,    81,    82,    83,    84,    85,    86,
-    87,    88,    89,    90,    91,     0,     0,     0,     0,     0,
-     0,     0,     0,     0,     0,   214,    77,    78,    79,    80,
-    81,    82,    83,    84,    85,    86,    87,    88,    89,    90,
+    32,     0,     0,     0,     0,     0,     0,    33,    34,   159,
+     0,     0,     0,     0,     0,    38,    77,    78,    79,    80,
+    81,    82,    83,    84,    85,    86,   126,    88,    89,    90,
     91,     0,     0,     0,     0,     0,     0,     0,     0,     0,
-     0,   216,    77,    78,    79,    80,    81,    82,    83,    84,
+     0,   215,    77,    78,    79,    80,    81,    82,    83,    84,
     85,    86,    87,    88,    89,    90,    91,     0,     0,     0,
-     0,     0,     0,     0,     0,     0,     0,   218,    77,    78,
-    79,    80,    81,    82,    83,    84,    85,    86,    87,    88,
-    89,    90,    91,     0,     0,     0,     0,     0,     0,     0,
-     0,     0,     0,   222,    77,    78,    79,    80,    81,    82,
-    83,    84,    85,    86,    87,    88,    89,    90,    91,     0,
-     0,     0,     0,     0,     0,     0,     0,     0,     0,   224,
-    77,    78,    79,    80,    81,    82,    83,    84,    85,    86,
-   124,    88,    89,    90,    91,     0,     0,     0,     0,     0,
-     0,     0,     0,     0,     0,   237,    77,    78,    79,    80,
-    81,    82,    83,    84,    85,    86,    87,    88,    89,    90,
-    91,     0,     0,     0,     0,     0,     0,    92,    93,    94,
-   172,    77,    78,    79,    80,    81,    82,    83,    84,    85,
-    86,   124,    88,    89,    90,    91,    77,    78,    79,    80,
-    81,    82,    83,    84,    85,    86,    87,    88,    89,    90,
-    91,    77,    78,    79,    80,    81,    82,    83,    84,    85,
-    86,   124,    88,    89,    90,    91
+     0,     0,     0,    92,    93,    94,   170,    77,    78,    79,
+    80,    81,    82,    83,    84,    85,    86,   126,    88,    89,
+    90,    91,    77,    78,    79,    80,    81,    82,    83,    84,
+    85,    86,    87,    88,    89,    90,    91,    77,    78,    79,
+    80,    81,    82,    83,    84,    85,    86,   126,    88,    89,
+    90,    91
 };
 
 static const short yycheck[] = {     1,
-    13,    59,    62,    46,    64,    48,    49,    13,    13,    13,
-    66,    67,    13,   161,    45,    46,    74,    45,    46,    45,
-    46,    59,    59,    59,    59,    59,    47,     7,    70,     9,
-    32,    76,    45,    46,     8,    28,    74,    74,    74,    74,
-    74,     8,    28,    74,    45,    46,    74,     8,    74,    51,
-    52,    53,    54,    55,    59,    76,    58,    59,    60,    61,
-    62,    63,    64,    65,    62,    63,     9,    72,    74,    74,
+    13,    61,    13,    62,    13,    64,    66,    67,    46,    13,
+    48,    49,   160,    45,    46,    45,    46,    45,    46,     7,
+   116,     9,    59,    59,   120,    72,    47,    76,    59,     8,
+    32,    28,    45,    46,    45,    46,     8,    74,    74,    70,
+     8,    72,    74,     9,    74,     3,    74,    70,    71,    51,
+    52,    53,    54,    55,     1,    76,    58,    59,    60,    61,
+    62,    63,    64,    65,    59,    74,    62,    63,   216,    28,
     74,    28,    74,    73,    73,    77,    78,    79,    80,    81,
     82,    83,    84,    85,    86,    87,    88,    89,    90,    91,
-   238,   151,    13,    59,   154,    97,    73,    73,    40,    41,
-    42,    43,    44,   146,    70,    71,     3,    62,     1,    73,
-    73,   167,    74,   169,    58,    59,    60,    61,    62,    63,
-    73,    73,   124,    65,    66,    73,    73,    59,    49,    50,
-    51,    52,    53,    54,    55,    56,    57,    58,    59,    60,
-    61,    62,    63,    76,    15,    38,    13,    48,     3,   151,
-   152,     3,   154,   155,   156,    46,   158,   159,    51,   161,
-   162,   163,    74,    74,   166,   225,    74,    74,    61,   171,
-   172,   227,    74,    66,    67,    40,    41,    42,    43,    44,
-     0,    74,    52,    53,    54,    55,    56,    57,    58,    59,
-    60,    61,    62,    63,    87,    -1,    60,    61,    62,    63,
-    -1,    -1,   204,   205,   206,   207,   208,    -1,   210,    -1,
-    -1,    -1,    -1,    -1,    -1,    -1,    -1,     0,     1,    -1,
-     3,     4,     5,   225,     7,    -1,     9,    10,    11,    12,
-    -1,    14,    -1,    16,    17,    -1,   238,    -1,    21,    22,
-    23,    -1,    25,    26,    27,    28,    29,    30,    31,    32,
-    33,    34,    35,    36,    37,    38,    39,    -1,    -1,    -1,
-    -1,    -1,   155,   156,    -1,   158,   159,    -1,    -1,   162,
-   163,    -1,    -1,    -1,   167,    58,   169,    -1,    -1,    -1,
-    -1,    -1,    65,    66,    -1,    -1,    -1,    70,    71,    72,
-    73,     3,     4,     5,     6,     7,    -1,     9,    10,    11,
-    12,    -1,    -1,    -1,    16,    17,    -1,    -1,    -1,    21,
-    -1,    -1,    -1,    25,    -1,    -1,    28,    29,    30,    31,
-    32,    33,    34,    35,    36,    37,    38,    39,    -1,    -1,
-    -1,    -1,    -1,    -1,   227,    -1,    -1,    -1,    -1,    -1,
-    -1,    -1,    -1,    -1,    -1,    -1,    58,    -1,    -1,    -1,
-    -1,    -1,    -1,    65,    66,    -1,    -1,    -1,    -1,    -1,
-    -1,    73,    74,     3,     4,     5,     6,     7,    -1,     9,
-    10,    11,    12,    -1,    -1,    -1,    16,    17,    -1,    -1,
-    -1,    21,    -1,    -1,    -1,    25,    -1,    -1,    28,    29,
-    30,    31,    32,    33,    34,    35,    36,    37,    38,    39,
-    -1,    49,    50,    51,    52,    53,    54,    55,    56,    57,
-    58,    59,    60,    61,    62,    63,    -1,    -1,    58,    -1,
-    -1,    -1,    -1,    -1,    -1,    65,    66,    75,     3,     4,
-     5,    13,     7,    73,     9,    10,    11,    12,    -1,    -1,
-    -1,    16,    17,    -1,    -1,    -1,    21,    -1,    -1,    -1,
-    25,    -1,    -1,    28,    29,    30,    31,    32,    33,    34,
-    35,    36,    37,    38,    39,    -1,    -1,    49,    50,    51,
-    52,    53,    54,    55,    56,    57,    58,    59,    60,    61,
-    62,    63,    -1,    58,    -1,    -1,    -1,    -1,    -1,    -1,
-    65,    66,    -1,     3,     4,     5,    13,     7,    73,     9,
-    10,    11,    12,    -1,    -1,    -1,    16,    17,    -1,    -1,
-    -1,    21,    -1,    -1,    -1,    25,    -1,    -1,    28,    29,
-    30,    31,    32,    33,    34,    35,    36,    37,    38,    39,
-    -1,    -1,    49,    50,    51,    52,    53,    54,    55,    56,
-    57,    58,    59,    60,    61,    62,    63,    -1,    58,    -1,
-    -1,    -1,    -1,    -1,    -1,    65,    66,    -1,     3,     4,
-     5,    13,     7,    73,     9,    10,    11,    12,    -1,    -1,
-    -1,    16,    17,    -1,    -1,    -1,    21,    -1,    -1,    -1,
-    25,    -1,    -1,    28,    29,    30,    31,    32,    33,    34,
-    -1,    36,    37,    38,    39,    -1,    -1,    49,    50,    51,
-    52,    53,    54,    55,    56,    57,    58,    59,    60,    61,
-    62,    63,    -1,    58,    -1,    -1,    -1,    -1,    -1,    -1,
-    65,    66,    -1,     3,     4,     5,    -1,     7,    73,     9,
-    10,    11,    12,    -1,    -1,    -1,    16,    17,    -1,    -1,
-    -1,    21,    -1,    -1,    -1,    25,    -1,    -1,    28,    29,
-    30,    31,    32,    33,    34,    -1,    36,    37,    38,    39,
-    -1,    49,    50,    51,    52,    53,    54,    55,    56,    57,
-    58,    59,    60,    61,    62,    63,    -1,    -1,    58,    -1,
-    -1,    -1,    -1,    -1,    -1,    65,    66,    75,     3,     4,
-     5,    -1,     7,    73,     9,    10,    11,    12,    -1,    -1,
-    -1,    16,    17,    -1,    -1,    -1,    21,    -1,    -1,    -1,
-    25,    -1,    -1,    28,    29,    30,    31,    32,    33,    34,
-    -1,    36,    37,    38,    39,    -1,    -1,    -1,    49,    50,
-    51,    52,    53,    54,    55,    56,    57,    58,    59,    60,
-    61,    62,    63,    58,    -1,    -1,    -1,    -1,     3,     4,
-    65,    66,     7,    74,     9,    10,    11,    12,    73,    -1,
+    73,    38,    73,    73,   153,    73,    98,   156,   158,    40,
+    41,    42,    43,    44,    51,   165,    73,   167,    73,    73,
+   148,    73,    63,    74,    61,    60,    61,    62,    63,    66,
+    67,    76,    15,    13,   126,    48,    63,    74,     3,     3,
+    46,    74,    74,    13,    58,    59,    60,    61,    62,    63,
+    87,    74,    74,    74,   203,   205,    74,     0,    -1,    -1,
+    -1,   153,   154,    -1,   156,    -1,    -1,    -1,   160,    -1,
+   162,    -1,   164,    -1,    -1,    -1,    -1,   169,   170,    49,
+    50,    51,    52,    53,    54,    55,    56,    57,    58,    59,
+    60,    61,    62,    63,    40,    41,    42,    43,    44,    -1,
+   192,   193,   194,   195,   196,    -1,   198,    -1,    -1,    -1,
+    -1,   203,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    65,
+    66,   158,    -1,    -1,   216,   162,    -1,    -1,   165,    -1,
+   167,    52,    53,    54,    55,    56,    57,    58,    59,    60,
+    61,    62,    63,    -1,     0,     1,    -1,     3,     4,     5,
+    13,     7,    -1,     9,    10,    11,    12,    -1,    14,    -1,
+    16,    17,    -1,    -1,    -1,    21,    22,    23,   205,    25,
+    26,    27,    28,    29,    30,    31,    32,    33,    34,    35,
+    36,    37,    38,    39,    -1,    -1,    49,    50,    51,    52,
+    53,    54,    55,    56,    57,    58,    59,    60,    61,    62,
+    63,    -1,    58,    -1,    -1,    -1,    -1,    -1,    -1,    65,
+    66,    -1,    -1,    -1,    70,    71,    72,    73,     3,     4,
+     5,     6,     7,    -1,     9,    10,    11,    12,    -1,    -1,
     -1,    16,    17,    -1,    -1,    -1,    21,    -1,    -1,    -1,
     25,    -1,    -1,    28,    29,    30,    31,    32,    33,    34,
-    -1,    36,    37,    38,    39,    13,    -1,    -1,    49,    50,
-    51,    52,    53,    54,    55,    56,    57,    58,    59,    60,
-    61,    62,    63,    58,    -1,    -1,    -1,    -1,    -1,    -1,
-    65,    66,    13,    74,    -1,    -1,    -1,    -1,    73,    -1,
-    -1,    49,    50,    51,    52,    53,    54,    55,    56,    57,
-    58,    59,    60,    61,    62,    63,    -1,    -1,    -1,    -1,
-    -1,    -1,    -1,    -1,    72,    -1,    74,    -1,    49,    50,
+    35,    36,    37,    38,    39,    -1,    49,    50,    51,    52,
+    53,    54,    55,    56,    57,    58,    59,    60,    61,    62,
+    63,    -1,    -1,    58,    -1,    -1,    -1,    -1,    -1,    -1,
+    65,    66,    75,    -1,    -1,    -1,    -1,    -1,    73,    74,
+     3,     4,     5,     6,     7,    -1,     9,    10,    11,    12,
+    -1,    -1,    -1,    16,    17,    -1,    -1,    -1,    21,    -1,
+    -1,    -1,    25,    -1,    -1,    28,    29,    30,    31,    32,
+    33,    34,    35,    36,    37,    38,    39,    -1,    49,    50,
     51,    52,    53,    54,    55,    56,    57,    58,    59,    60,
-    61,    62,    63,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-    -1,    72,    -1,    74,    49,    50,    51,    52,    53,    54,
+    61,    62,    63,    -1,    -1,    58,    -1,    -1,    -1,    -1,
+    -1,    -1,    65,    66,    75,     3,     4,     5,    13,     7,
+    73,     9,    10,    11,    12,    -1,    -1,    -1,    16,    17,
+    -1,    -1,    -1,    21,    -1,    -1,    -1,    25,    -1,    -1,
+    28,    29,    30,    31,    32,    33,    34,    35,    36,    37,
+    38,    39,    -1,    -1,    49,    50,    51,    52,    53,    54,
     55,    56,    57,    58,    59,    60,    61,    62,    63,    -1,
-    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    74,
-    49,    50,    51,    52,    53,    54,    55,    56,    57,    58,
-    59,    60,    61,    62,    63,    -1,    -1,    -1,    -1,    -1,
-    -1,    -1,    -1,    -1,    -1,    74,    49,    50,    51,    52,
+    58,    -1,    -1,    -1,    -1,    -1,    -1,    65,    66,    -1,
+     3,     4,     5,    13,     7,    73,     9,    10,    11,    12,
+    -1,    -1,    -1,    16,    17,    -1,    -1,    -1,    21,    -1,
+    -1,    -1,    25,    -1,    -1,    28,    29,    30,    31,    32,
+    33,    34,    35,    36,    37,    38,    39,    -1,    -1,    49,
+    50,    51,    52,    53,    54,    55,    56,    57,    58,    59,
+    60,    61,    62,    63,    -1,    58,    -1,    -1,    -1,    -1,
+    -1,    -1,    65,    66,    -1,     3,     4,     5,    -1,     7,
+    73,     9,    10,    11,    12,    -1,    -1,    -1,    16,    17,
+    -1,    -1,    -1,    21,    -1,    -1,    -1,    25,    -1,    -1,
+    28,    29,    30,    31,    32,    33,    34,    -1,    36,    37,
+    38,    39,    -1,    -1,    49,    50,    51,    52,    53,    54,
+    55,    56,    57,    58,    59,    60,    61,    62,    63,    -1,
+    58,    -1,    -1,    -1,    -1,    70,    71,    65,    66,    74,
+     3,     4,     5,    -1,     7,    73,     9,    10,    11,    12,
+    -1,    -1,    -1,    16,    17,    -1,    -1,    -1,    21,    -1,
+    -1,    -1,    25,    -1,    -1,    28,    29,    30,    31,    32,
+    33,    34,    -1,    36,    37,    38,    39,    -1,    -1,    49,
+    50,    51,    52,    53,    54,    55,    56,    57,    58,    59,
+    60,    61,    62,    63,    -1,    58,    -1,    -1,    -1,    -1,
+    -1,    -1,    65,    66,    74,     3,     4,     5,    -1,     7,
+    73,     9,    10,    11,    12,    -1,    -1,    -1,    16,    17,
+    -1,    -1,    -1,    21,    -1,    -1,    -1,    25,    -1,    -1,
+    28,    29,    30,    31,    32,    33,    34,    -1,    36,    37,
+    38,    39,    -1,    -1,    -1,    49,    50,    51,    52,    53,
+    54,    55,    56,    57,    58,    59,    60,    61,    62,    63,
+    58,    -1,    -1,    -1,    -1,     3,     4,    65,    66,     7,
+    74,     9,    10,    11,    12,    73,    -1,    -1,    16,    17,
+    -1,    -1,    -1,    21,    -1,    -1,    -1,    25,    -1,    -1,
+    28,    29,    30,    31,    32,    33,    34,    -1,    36,    37,
+    38,    39,    -1,    -1,    49,    50,    51,    52,    53,    54,
+    55,    56,    57,    58,    59,    60,    61,    62,    63,    -1,
+    58,    -1,    -1,    -1,    -1,    -1,    -1,    65,    66,    74,
+    -1,    -1,    -1,    -1,    -1,    73,    49,    50,    51,    52,
     53,    54,    55,    56,    57,    58,    59,    60,    61,    62,
     63,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
     -1,    74,    49,    50,    51,    52,    53,    54,    55,    56,
     57,    58,    59,    60,    61,    62,    63,    -1,    -1,    -1,
-    -1,    -1,    -1,    -1,    -1,    -1,    -1,    74,    49,    50,
-    51,    52,    53,    54,    55,    56,    57,    58,    59,    60,
-    61,    62,    63,    -1,    -1,    -1,    -1,    -1,    -1,    -1,
-    -1,    -1,    -1,    74,    49,    50,    51,    52,    53,    54,
-    55,    56,    57,    58,    59,    60,    61,    62,    63,    -1,
-    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,    74,
-    49,    50,    51,    52,    53,    54,    55,    56,    57,    58,
-    59,    60,    61,    62,    63,    -1,    -1,    -1,    -1,    -1,
-    -1,    -1,    -1,    -1,    -1,    74,    49,    50,    51,    52,
-    53,    54,    55,    56,    57,    58,    59,    60,    61,    62,
-    63,    -1,    -1,    -1,    -1,    -1,    -1,    70,    71,    72,
-    48,    49,    50,    51,    52,    53,    54,    55,    56,    57,
-    58,    59,    60,    61,    62,    63,    49,    50,    51,    52,
-    53,    54,    55,    56,    57,    58,    59,    60,    61,    62,
-    63,    49,    50,    51,    52,    53,    54,    55,    56,    57,
-    58,    59,    60,    61,    62,    63
+    -1,    -1,    -1,    70,    71,    72,    48,    49,    50,    51,
+    52,    53,    54,    55,    56,    57,    58,    59,    60,    61,
+    62,    63,    49,    50,    51,    52,    53,    54,    55,    56,
+    57,    58,    59,    60,    61,    62,    63,    49,    50,    51,
+    52,    53,    54,    55,    56,    57,    58,    59,    60,    61,
+    62,    63
 };
 /* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */
 
@@ -1196,12 +1144,6 @@ yyreduce:
 
   switch (yyn) {
 
-case 6:
-{store_res(&yyvsp[-1]); return 0;;
-    break;}
-case 7:
-{store_res(&yyvsp[-1]); return 0;;
-    break;}
 case 8:
 {store_res(&yyvsp[-1]); return 0;;
     break;}
@@ -1212,322 +1154,295 @@ case 10:
 {store_res(&yyvsp[-1]); return 0;;
     break;}
 case 11:
+{store_res(&yyvsp[-1]); return 0;;
+    break;}
+case 12:
 {if(block_res = eval(&yyval, &yyvsp[-1]))return block_res; 
 				if(yyval.val != 0.0) if(block_res = eval(&yyval, &yyvsp[0]))return block_res;;
     break;}
-case 12:
+case 13:
 {if(block_res = eval(&yyval, &yyvsp[-3])) return block_res; 
 				if(yyval.val != 0.0) {if(block_res = eval(&yyval, &yyvsp[-2])) return block_res;} 
 				else if(block_res = eval(&yyval, &yyvsp[0])) return block_res;;
     break;}
-case 13:
-{for_loop(yyvsp[-1].text, yyvsp[0].text);;
-    break;}
 case 14:
-{for(int i=0; i< yy_maxiter; i++){ if(block_res = eval(&yyval, &yyvsp[-1]))return block_res; 
-					if(yyval.val != 0.0){if(block_res = eval(&yyval, &yyvsp[0]))return block_res;} else break;};
+{block_res = for_loop(yyvsp[-1].text, yyvsp[0].text); if(block_res == 1 || block_res == 2) return block_res;;
     break;}
 case 15:
-{if(block_res = eval(&yyval, &yyvsp[0]) == 1) return 1; else return 2;;
+{for(int i=0; i< yy_maxiter; i++){ if(block_res = eval(&yyval, &yyvsp[-1]))return block_res; 
+					if(yyval.val != 0.0){if(block_res = eval(&yyval, &yyvsp[0]))return block_res;} else break;};
     break;}
 case 16:
-{return 3;;
+{if(block_res = eval(&yyval, &yyvsp[0]) == 1) return 1; else return 2;;
     break;}
 case 17:
-{yyerrok;;
+{return 3;;
     break;}
 case 18:
-{;;
+{yyerrok;;
     break;}
 case 19:
-{yyval.text=add_strings(yyvsp[-2].text, string_value(&yyvsp[0])); yyval.type = STR;;
+{;;
     break;}
 case 20:
-{yyval.text=add_strings(string_value(&yyvsp[-2]), yyvsp[0].text); yyval.type = STR;;
+{yyval.text=add_strings(yyvsp[-2].text, string_value(&yyvsp[0])); yyval.type = STR;;
     break;}
 case 21:
-{yyval.text=add_strings(yyvsp[-2].text, yyvsp[0].text); yyval.type = STR;;
+{yyval.text=add_strings(string_value(&yyvsp[-2]), yyvsp[0].text); yyval.type = STR;;
     break;}
 case 22:
-{if(yyvsp[-3].tptr->fnctptr)((yyvsp[-3].tptr->fnctptr)(&yyval, &yyvsp[-1], 0L)); yyval.type = STR;;
+{yyval.text=add_strings(yyvsp[-2].text, yyvsp[0].text); yyval.type = STR;;
     break;}
 case 23:
-{if(yyvsp[-5].tptr->fnctptr)((yyvsp[-5].tptr->fnctptr)(&yyval, &yyvsp[-3], yyvsp[-1].text)); yyval.type = STR;;
+{if(yyvsp[-3].tptr->fnctptr)((yyvsp[-3].tptr->fnctptr)(&yyval, &yyvsp[-1], 0L)); yyval.type = STR;;
     break;}
 case 24:
 {if(yyvsp[-5].tptr->fnctptr)((yyvsp[-5].tptr->fnctptr)(&yyval, &yyvsp[-3], yyvsp[-1].text)); yyval.type = STR;;
     break;}
 case 25:
-{if(yyvsp[-5].tptr->fnctptr)((yyvsp[-5].tptr->fnctptr)(&yyval, &yyvsp[-3], yyvsp[-1].text)); yyval.type = STR;;
-    break;}
-case 26:
-{if(yyvsp[-5].tptr->fnctptr)((yyvsp[-5].tptr->fnctptr)(&yyval, &yyvsp[-3], yyvsp[-1].text)); yyval.type = STR;;
-    break;}
-case 27:
 {;;
     break;}
-case 28:
+case 26:
 {;;
     break;}
-case 29:
+case 27:
 {if(!yyval.a_data) {yyval.a_data = PushArray((double*)malloc(sizeof(double))); yyval.a_count = 1; yyval.a_data[0] = yyval.val;};
     break;}
-case 30:
+case 28:
 {push(&yyval, &yyvsp[0]);yyval.type = ARR;;
     break;}
-case 31:
+case 29:
 {exec_clause(&yyval);;
     break;}
-case 32:
+case 30:
 {range_array(&yyval, yyvsp[0].text);;
     break;}
-case 33:
+case 31:
 {if(yyvsp[-2].val < yyvsp[0].val && (yyval.a_data = PushArray((double*)malloc((int)(yyvsp[0].val-yyvsp[-2].val+2)*sizeof(double)))))
 					for(yyval.a_count=0; yyvsp[-2].val<=yyvsp[0].val; yyval.a_data[yyval.a_count++] = yyvsp[-2].val, yyvsp[-2].val += 1.0 ); yyval.type = ARR;;
     break;}
-case 35:
+case 33:
 {yyval.val = yyvsp[-3].tptr->fnctptr ? ((yyvsp[-3].tptr->fnctptr)(yyvsp[-1].val)): 0.0; yyval.type = BOOLVAL;;
     break;}
-case 36:
+case 34:
 {yyval.val = 1.0; yyval.type = BOOLVAL;;
     break;}
-case 37:
+case 35:
 {yyval.val = 0.0; yyval.type = BOOLVAL;;
     break;}
+case 36:
+{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], AND);;
+    break;}
+case 37:
+{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], OR);;
+    break;}
 case 38:
-{yyval.val = ((yyvsp[-2].val != 0) && (yyvsp[0].val != 0))? 1 : 0; yyval.type = BOOLVAL;;
+{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], EQ);;
     break;}
 case 39:
-{yyval.val = ((yyvsp[-2].val != 0) || (yyvsp[0].val != 0))? 1 : 0; yyval.type = BOOLVAL;;
+{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], NE);;
     break;}
 case 40:
-{yyval.val = (yyvsp[-2].val == yyvsp[0].val) ? 1 : 0; yyval.type = BOOLVAL;;
+{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], GT);;
     break;}
 case 41:
-{yyval.val = (yyvsp[-2].val != yyvsp[0].val) ? 1 : 0; yyval.type = BOOLVAL;;
+{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], GE);;
     break;}
 case 42:
-{yyval.val = (yyvsp[-2].val > yyvsp[0].val) ? 1 : 0; yyval.type = BOOLVAL;;
+{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], LT);;
     break;}
 case 43:
-{yyval.val = (yyvsp[-2].val >= yyvsp[0].val) ? 1 : 0; yyval.type = BOOLVAL;;
+{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], LE);;
     break;}
-case 44:
-{yyval.val = (yyvsp[-2].val < yyvsp[0].val) ? 1 : 0; yyval.type = BOOLVAL;;
-    break;}
-case 45:
-{yyval.val = (yyvsp[-2].val <= yyvsp[0].val) ? 1 : 0; yyval.type = BOOLVAL;;
-    break;}
-case 50:
+case 48:
 {yyval.val = yyvsp[0].val; yyval.type = NUM;;
     break;}
-case 52:
+case 50:
 {yyval.val = yyvsp[0].val; yyval.type = BOOLVAL;;
     break;}
-case 53:
+case 51:
 {yyval.val = 0.0;;
     break;}
-case 54:
+case 52:
 {yyval.val = syntax_level ? syntax_level->clval : 0.0;  yyval.type = NUM;;
     break;}
-case 55:
+case 53:
 {yyval.val = _PI; yyval.type = NUM;;
     break;}
-case 56:
+case 54:
 {yyval.val = 2.71828182845905; yyval.type = NUM;;
     break;}
-case 57:
+case 55:
 {if(yyvsp[0].tptr)yyvsp[0].tptr->GetValue(&yyval);;
     break;}
-case 58:
+case 56:
 {if(block_res = eval(&yyval, &yyvsp[0]))return block_res;;
     break;}
-case 59:
+case 57:
 {if(yyvsp[-2].tptr)yyvsp[-2].tptr->SetValue(&yyval, &yyvsp[0]);;
     break;}
-case 60:
+case 58:
 {if(yyvsp[-2].tptr)yyvsp[-2].tptr->SetValue(&yyval, &yyvsp[0]);;
     break;}
-case 61:
+case 59:
 {if(yyvsp[-2].tptr){yyvsp[-2].tptr->GetValue(&yyval); yyvsp[-2].tptr->SetValue(yyval.val + yyvsp[0].val);};
     break;}
-case 62:
+case 60:
 {if(yyvsp[-2].tptr){yyvsp[-2].tptr->GetValue(&yyval); yyvsp[-2].tptr->SetValue(yyval.val - yyvsp[0].val);};
     break;}
-case 63:
+case 61:
 {if(yyvsp[-2].tptr){yyvsp[-2].tptr->GetValue(&yyval); yyvsp[-2].tptr->SetValue(yyval.val * yyvsp[0].val);};
     break;}
-case 64:
+case 62:
 {if(yyvsp[-2].tptr){yyvsp[-2].tptr->GetValue(&yyval); yyvsp[-2].tptr->SetValue(yyvsp[0].val != 0.0 ? yyval.val / yyvsp[0].val :
 		(getsym(HashValue((unsigned char*)"zdiv"), Hash2((unsigned char*)"zdiv")))->GetValue());};
     break;}
-case 65:
+case 63:
 {yyval.val = yyvsp[-3].tptr->fnctptr ? ((yyvsp[-3].tptr->fnctptr)(yyvsp[-1].val)): 0.0; yyval.type = NUM;;
     break;}
-case 66:
+case 64:
 {yyval.val = yyvsp[-3].tptr->fnctptr ? ((yyvsp[-3].tptr->fnctptr)(proc_clause(&yyvsp[-1]))) : 0.0; yyval.type = NUM; yyval.a_data = 0L; yyval.a_count = 0; yyval.text = 0L;;
     break;}
-case 67:
+case 65:
 { if(!yyval.a_data){yyval.a_data=PushArray((double*)malloc(sizeof(double)));yyval.a_data[0]=yyvsp[-3].val;yyval.a_count=1;}
 		push(&yyval, &yyvsp[-1]);yyval.val = yyvsp[-5].tptr->fnctptr ?((yyvsp[-5].tptr->fnctptr)(&yyval)):0.0; yyval.type = NUM;;
     break;}
-case 68:
+case 66:
 { yyval.a_data = PushArray((double*)malloc(3*sizeof(double)));
 		yyval.a_count = 3; yyval.a_data[0] = yyvsp[-5].val; yyval.a_data[1] = yyvsp[-3].val; yyval.a_data[2] = yyvsp[-1].val;	
 		yyval.val = yyvsp[-7].tptr->fnctptr ? ((yyvsp[-7].tptr->fnctptr)(&yyval)) : 0.0; yyval.type = NUM;;
     break;}
-case 69:
+case 67:
 {yyval.type = NUM; yyval.val = yyvsp[-3].tptr->fnctptr ? ((yyvsp[-3].tptr->fnctptr)(&yyvsp[-1], &yyval, 0L)) : 0.0;;
     break;}
-case 70:
+case 68:
 {yyval.type = NUM; yyvsp[-1].text = string_value(&yyvsp[-1]); yyval.val = yyvsp[-3].tptr->fnctptr ? ((yyvsp[-3].tptr->fnctptr)(&yyvsp[-1], &yyval, 0L)) : 0.0;;
     break;}
-case 71:
-{yyval.val = yyvsp[-5].tptr->fnctptr ? ((yyvsp[-5].tptr->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;;
-    break;}
-case 72:
-{yyval.val = yyvsp[-5].tptr->fnctptr ?((yyvsp[-5].tptr->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;;
-    break;}
-case 73:
-{yyval.val = yyvsp[-5].tptr->fnctptr ? ((yyvsp[-5].tptr->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;;
-    break;}
-case 74:
-{yyval.val = yyvsp[-5].tptr->fnctptr ? ((yyvsp[-5].tptr->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;;
-    break;}
-case 75:
-{yyval.val = yyvsp[-5].tptr->fnctptr ? ((yyvsp[-5].tptr->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;;
-    break;}
-case 76:
-{yyval.val = yyvsp[-5].tptr->fnctptr ? ((yyvsp[-5].tptr->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;;
-    break;}
-case 77:
-{yyval.val = yyvsp[-5].tptr->fnctptr ? ((yyvsp[-5].tptr->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;;
-    break;}
-case 78:
+case 69:
 {yyval.val = yyvsp[-5].tptr->fnctptr ? ((yyvsp[-5].tptr->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;;
     break;}
-case 79:
+case 70:
 {proc_clause(&yyvsp[-3]); yyval.val=yyvsp[-9].tptr->fnctptr ? (*yyvsp[-9].tptr->fnctptr)(yyvsp[-7].val, yyvsp[-5].val, &yyvsp[-3], &yyvsp[-1]) : 0.0; yyval.type = NUM;;
     break;}
-case 80:
+case 71:
 {yyval.val = yyvsp[-5].tptr->fnctptr ? ((*yyvsp[-5].tptr->fnctptr)(proc_clause(&yyvsp[-3]), yyvsp[-1].text)) : 0.0;  yyval.type = NUM;;
     break;}
-case 81:
+case 72:
 {yyval.val = yyvsp[-5].tptr->fnctptr ? ((*yyvsp[-5].tptr->fnctptr)(proc_clause(&yyvsp[-3]), yyvsp[-1].text)) : 0.0;  yyval.type = NUM;;
     break;}
-case 82:
+case 73:
 {if(yyvsp[-2].tptr->fnctptr)(*yyvsp[-2].tptr->fnctptr)(&yyval, 0L);;
     break;}
-case 83:
+case 74:
 {if(yyvsp[-3].tptr->fnctptr)(*yyvsp[-3].tptr->fnctptr)(&yyval, &yyvsp[-1]);;
     break;}
-case 84:
+case 75:
 {if(yyvsp[-5].tptr->fnctptr)(*yyvsp[-5].tptr->fnctptr)(&yyval, &yyvsp[-3], &yyvsp[-1]);;
     break;}
-case 85:
+case 76:
 {if(yyvsp[-3].tptr->fnctptr)(*yyvsp[-3].tptr->fnctptr)(&yyval, &yyvsp[-1], 0L);;
     break;}
-case 86:
+case 77:
 {if(yyvsp[-7].tptr->fnctptr)(*yyvsp[-7].tptr->fnctptr)(&yyval, &yyvsp[-5], &yyvsp[-3], &yyvsp[-1]);;
     break;}
-case 87:
+case 78:
 {if(yyvsp[-5].tptr->fnctptr)(*yyvsp[-5].tptr->fnctptr)(&yyval, &yyvsp[-3], &yyvsp[-1], 0L);;
     break;}
-case 88:
+case 79:
 {yyval.val = yyvsp[-2].val + yyvsp[0].val; yyval.type = NUM;
 				if(yyvsp[0].type == DATE1 || yyvsp[-2].type == DATE1) yyval.type = DATE1;
 				else if(yyvsp[0].type == TIME1 || yyvsp[-2].type == TIME1) yyval.type = TIME1;
 				else if(yyvsp[0].type == DATETIME1 || yyvsp[-2].type == DATETIME1) yyval.type = DATETIME1;;
     break;}
-case 89:
+case 80:
 {yyval.val = yyvsp[-2].val - yyvsp[0].val; yyval.type = NUM;
 				if(yyvsp[0].type == DATE1 || yyvsp[-2].type == DATE1) yyval.type = DATE1;
 				else if(yyvsp[0].type == TIME1 || yyvsp[-2].type == TIME1) yyval.type = TIME1;
 				else if(yyvsp[0].type == DATETIME1 || yyvsp[-2].type == DATETIME1) yyval.type = DATETIME1;;
     break;}
-case 90:
+case 81:
 {yyval.val = yyvsp[-2].val * yyvsp[0].val; yyval.type = NUM;;
     break;}
-case 91:
+case 82:
 {yyval.type = NUM; if(yyvsp[0].val != 0.0) yyval.val = yyvsp[-2].val / yyvsp[0].val;
 					else yyval.val = (getsym(HashValue((unsigned char*)"zdiv"), Hash2((unsigned char*)"zdiv")))->GetValue(); ;
     break;}
-case 92:
+case 83:
 {yyval.val=yyvsp[-1].tptr->GetValue(); yyvsp[-1].tptr->SetValue(yyval.val+1.0); yyval.val -= 1.0; yyval.type = NUM;;
     break;}
-case 93:
+case 84:
 {yyval.val=yyvsp[-1].tptr->GetValue(); yyvsp[-1].tptr->SetValue(yyval.val-1.0); yyval.val += 1.0; yyval.type = NUM;;
     break;}
-case 94:
+case 85:
 {yyval.val=yyvsp[0].tptr->GetValue(); yyvsp[0].tptr->SetValue(yyval.val+1.0); yyval.type = NUM;;
     break;}
-case 95:
+case 86:
 {yyval.val=yyvsp[0].tptr->GetValue(); yyvsp[0].tptr->SetValue(yyval.val-1.0); yyval.type = NUM;;
     break;}
-case 96:
+case 87:
 {yyval.val = (yyvsp[0].val >0 && yyvsp[0].val/2.0 == floor(yyvsp[0].val/2.0)) ? fabs(pow(yyvsp[-2].val,yyvsp[0].val) ): pow(yyvsp[-2].val, yyvsp[0].val); yyval.type = NUM;;
     break;}
-case 97:
+case 88:
 {yyval.val = -yyvsp[0].val; yyval.type = NUM;;
     break;}
-case 98:
+case 89:
 {memcpy(&yyval, &yyvsp[-1], sizeof(YYSTYPE)); yyvsp[-1].a_data = 0L; yyvsp[-1].a_count = 0;;
     break;}
-case 99:
+case 90:
 {yyval.a_data = PushArray((double*)calloc((int)yyvsp[-1].val, sizeof(double))); yyval.a_count=(int)(yyvsp[-1].val); 
 					yyval.type = ARR; yyvsp[-3].tptr->SetValue(&yyval,&yyval);;
     break;}
-case 100:
+case 91:
 {if(yyvsp[-3].a_data && yyvsp[-1].val >= 0.0 && yyvsp[-1].val < yyvsp[-3].a_count) yyval.val = yyvsp[-3].a_data[(int)yyvsp[-1].val];
-				else {yyval.val = 0.0; last_err_desc = "#INDEX";} yyval.type = NUM;;
+				else {yyval.val = 0.0; last_err_desc = "#INDEX";} yyval.a_data = 0L; yyval.a_count = 0; yyval.type = NUM;;
     break;}
-case 101:
+case 92:
 {if(yyvsp[-5].a_data && yyvsp[-3].val >= 0.0 && yyvsp[-3].val < yyvsp[-5].a_count)
 				{yyval.val = yyvsp[-5].a_data[(int)yyvsp[-3].val] = yyvsp[0].val; 
 				if(yyvsp[-5].tptr && yyvsp[-5].tptr->type == VAR)yyvsp[-5].tptr->SetValue(0L, &yyvsp[-5]);}
 				else {yyval.val = 0.0; last_err_desc = "#INDEX";} yyval.type = NUM;;
     break;}
-case 102:
+case 93:
 {if(yyvsp[-5].a_data && yyvsp[-3].val >= 0.0 && yyvsp[-3].val < yyvsp[-5].a_count) 
 				{yyval.val = yyvsp[-5].a_data[(int)yyvsp[-3].val] += yyvsp[0].val;
 				if(yyvsp[-5].tptr && yyvsp[-5].tptr->type == VAR)yyvsp[-5].tptr->SetValue(0L, &yyvsp[-5]);}
 				else {yyval.val = 0.0; last_err_desc = "#INDEX";} yyval.type = NUM;;
     break;}
-case 103:
+case 94:
 {if(yyvsp[-5].a_data && yyvsp[-3].val >= 0.0 && yyvsp[-3].val < yyvsp[-5].a_count) 
 				{yyval.val = yyvsp[-5].a_data[(int)yyvsp[-3].val] -= yyvsp[0].val;
 				if(yyvsp[-5].tptr && yyvsp[-5].tptr->type == VAR)yyvsp[-5].tptr->SetValue(0L, &yyvsp[-5]);}
 				else {yyval.val = 0.0; last_err_desc = "#INDEX";} yyval.type = NUM;;
     break;}
-case 104:
+case 95:
 {if(yyvsp[-5].a_data && yyvsp[-3].val >= 0.0 && yyvsp[-3].val < yyvsp[-5].a_count) 
 				{yyval.val = yyvsp[-5].a_data[(int)yyvsp[-3].val] *= yyvsp[0].val;
 				if(yyvsp[-5].tptr && yyvsp[-5].tptr->type == VAR)yyvsp[-5].tptr->SetValue(0L, &yyvsp[-5]);}
 				else {yyval.val = 0.0; last_err_desc = "#INDEX";} yyval.type = NUM;;
     break;}
-case 105:
+case 96:
 {if(yyvsp[-5].a_data && yyvsp[-3].val >= 0.0 && yyvsp[-3].val < yyvsp[-5].a_count){ 
 				if(yyvsp[0].val != 0.0) {yyval.val = yyvsp[-5].a_data[(int)yyvsp[-3].val] /= yyvsp[0].val;
 				if(yyvsp[-5].tptr && yyvsp[-5].tptr->type == VAR)yyvsp[-5].tptr->SetValue(0L, &yyvsp[-5]);}
 				else {yyval.val = (getsym(HashValue((unsigned char*)"zdiv"), Hash2((unsigned char*)"zdiv")))->GetValue();}}
 				else {yyval.val = 0.0; last_err_desc = "#INDEX";} yyval.type = NUM;;
     break;}
-case 106:
+case 97:
 {make_time(&yyval, yyvsp[-4].val, yyvsp[-2].val, yyvsp[0].val+1.0e-10);;
     break;}
-case 107:
+case 98:
 {make_time(&yyval, yyvsp[-2].val, yyvsp[0].val, 1.0e-10);;
     break;}
-case 108:
+case 99:
 {memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
     break;}
-case 109:
+case 100:
 {memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
     break;}
-case 110:
+case 101:
 {memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
     break;}
-case 111:
+case 102:
 {memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
     break;}
 }
@@ -2044,7 +1959,7 @@ symrec::GetValue()
 {
 	anyResult ares;
 
-	if(isSSval) {
+	if(isSSval && !bNoSS) {
 		if(row < 0 && col < 0) InitSS();
 		//GetResult( , , ,true) inhibits reentrance into parser !
 		if(curr_data->GetResult(&ares, row, col, parse_level > MAX_PARSE)){
@@ -2062,9 +1977,10 @@ symrec::GetValue(void *re)
 {
 	anyResult ares;
 	YYSTYPE *res = (YYSTYPE*)re;
+	int i;
 
 	if(!res) return;
-	if(isSSval) {
+	if(isSSval && !bNoSS) {
 		if(row < 0 && col < 0) InitSS();
 		res->a_data = 0L;	res->a_count = 0;
 		//GetResult( , , ,true) inhibits reentrance into parser !
@@ -2088,9 +2004,21 @@ symrec::GetValue(void *re)
 				res->type = DATETIME1;	res->val = ares.value;
 				res->text = 0L;		break;
 			case ET_TEXT:
-				res->type = STR;	res->val = 0.0;
-				if(ares.text) res->text = PushString(text = (char*)memdup(ares.text, (int)strlen(ares.text)+1, 0));
-				else res->text = 0L;	break;
+				i = 0;			res->val = 0.0;
+				if(ares.text) for( ; ares.text && ares.text[i] != ':'; i++);
+				if(i > 1 && ares.text[i] 
+					&& (isalpha(ares.text[0]) || ares.text[0] =='$')
+					&& (isalpha(ares.text[i+1]) || ares.text[i+1] =='$')
+					&& isdigit(ares.text[i-1])) {
+					RangeData.GetData(ares.text, &res->a_data, &res->a_count, &res->text); 
+					res->type = RANGEARR;
+					}
+				else {
+					res->type = STR;
+					if(ares.text) res->text = PushString(text = (char*)memdup(ares.text, (int)strlen(ares.text)+1, 0));
+					else res->text = 0L;
+					}
+				break;
 			default:
 				res->type = NUM;	res->val = var;
 				res->text = 0L;		break;
@@ -2210,6 +2138,7 @@ void LockData(bool lockExec, bool lockWrite)
 	RangeData.Clear();
 	bNoWrite = lockWrite;
 	bNoExec = lockExec;
+	bNoSS = false;
 } 
  
 static void yyerror(char *s)
@@ -2287,6 +2216,69 @@ char  *yywarn(char *txt, bool bNew)
 	else return 0L;
 }
 
+static void yyCompare(YYSTYPE *res, YYSTYPE *arg1, YYSTYPE *arg2, int op)
+{
+	int cmp;
+
+	if(!res || !arg1 || !arg2) return;
+	if(arg1->type == STR && arg2->type == STR) {
+		if (arg1->text && arg1->text[0] && arg2->text
+			&& arg2->text[0]) cmp = strcmp(arg1->text, arg2->text);
+		else if(arg1->text && arg1->text[0]) cmp = 1;
+		else if(arg2->text && arg2->text[0]) cmp = -1;
+		else cmp = 0;
+		switch(op) {
+		case AND:
+			res->val = (arg1->text && arg1->text[0] && arg2->text
+			&& arg2->text[0]) ? 1.0 : 0.0;		break;
+		case OR:
+			res->val = ((arg1->text && arg1->text[0]) || (arg2->text
+			&& arg2->text[0])) ? 1.0 : 0.0;		break;
+		case EQ:
+			res->val = cmp ? 0.0 : 1.0;		break;
+		case NE:
+			res->val = cmp ? 1.0 : 0.0;		break;
+		case GT:
+			res->val = cmp > 0 ? 1.0 : 0.0;		break;
+		case GE:
+			res->val = cmp >= 0 ? 1.0 : 0.0;	break;
+		case LT:
+			res->val = cmp < 0 ? 1.0 : 0.0;		break;
+		case LE:
+			res->val = cmp <= 0 ? 1.0 : 0.0;	break;
+			}
+		}
+	else {
+		switch(op) {
+		case AND:
+			res->val = (arg1->val != 0.0 && arg2->val != 0.0)
+			? 1.0 : 0.0;				break;
+		case OR:
+			res->val = (arg1->val != 0.0 || arg2->val != 0.0)
+			? 1.0 : 0.0;				break;
+		case EQ:
+			res->val = (arg1->val ==  arg2->val) ? 1.0 : 0.0;
+			break;
+		case NE:
+			res->val = (arg1->val !=  arg2->val) ? 1.0 : 0.0;
+			break;
+		case GT:
+			res->val = (arg1->val >  arg2->val) ? 1.0 : 0.0;
+			break;
+		case GE:
+			res->val = (arg1->val >=  arg2->val) ? 1.0 : 0.0;
+			break;
+		case LT:
+			res->val = (arg1->val <  arg2->val) ? 1.0 : 0.0;
+			break;
+		case LE:
+			res->val = (arg1->val <=  arg2->val) ? 1.0 : 0.0;
+			break;
+			}
+		}
+	res->type = BOOLVAL;
+}
+
 static void store_res(YYSTYPE *res)
 {
 	if(last_err_desc) {
@@ -2332,7 +2324,8 @@ static void store_res(YYSTYPE *res)
 		line_res.value = 0.0;
 		line_res.a_data = res->a_data;
 		line_res.a_count = res->a_count;
-		rlp_strcpy(res_txt, 1000, "#ARRAY");
+		if(res->text) rlp_strcpy(res_txt, 1000, res->text);
+		else rlp_strcpy(res_txt, 1000, "#ARRAY");
 		}
 	else if(res->tptr && res->tptr->type == TXT) {
 		line_res.type = ET_TEXT;
@@ -2482,6 +2475,14 @@ static double srand(double v)
 	return v;
 }
 
+static double maxiter(YYSTYPE *dst, YYSTYPE *src)
+{
+	if(!dst) return 0.0;
+	if(src) yy_maxiter = (int)src->val;
+	dst->type = NUM;
+	return(dst->val = (double)yy_maxiter);
+}
+
 static double factorial(double v)
 {
 	return factrl((int)v);
@@ -3025,6 +3026,50 @@ static double weibinv(YYSTYPE *sr)
 	return sr->val;
 }
 
+static double geomfreq(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 2){
+		sr->val = geom_freq(sr->a_data[0], sr->a_data[1]);
+		}
+	else yyargcnterr("geomfreq(x, p)");
+	return sr->val;
+}
+
+static double geomdist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 2){
+		sr->val = geom_dist(sr->a_data[0], sr->a_data[1]);
+		}
+	else yyargcnterr("geomdist(x, p)");
+	return sr->val;
+}
+
+static double hyperfreq(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 4){
+		sr->val = hyper_freq(sr->a_data[0], sr->a_data[1], sr->a_data[2], sr->a_data[3]);
+		}
+	else yyargcnterr("hyperfreq(k, N, m, n)");
+	return sr->val;
+}
+
+static double hyperdist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 4){
+		sr->val = hyper_dist(sr->a_data[0], sr->a_data[1], sr->a_data[2], sr->a_data[3]);
+		}
+	else yyargcnterr("hyperdist(k, N, m, n)");
+	return sr->val;
+}
+
 static double cauchydist(YYSTYPE *sr)
 {
 	if(!sr) return 0.0;
@@ -3869,7 +3914,9 @@ void InitArithFuncs(DataObj *d)
 		double (*fnct)(double);
 		};
 	fdef fncts[] = {
-	INIT_SYM(YYFNC3, "mkarr", mkarr),
+	INIT_SYM(AFNCT, "geomfreq", geomfreq),		INIT_SYM(AFNCT, "geomdist", geomdist),
+	INIT_SYM(AFNCT, "hyperfreq", hyperfreq),	INIT_SYM(AFNCT, "hyperdist", hyperdist),
+	INIT_SYM(YYFNC3, "mkarr", mkarr),		INIT_SYM(YYFNC, "maxiter", maxiter),
 	INIT_SYM(YYFNC, "randarr", _randarr),		INIT_SYM(YYFNC, "resample", _resample),
 	INIT_SYM(AFNCT, "weibdist", weibdist),		INIT_SYM(AFNCT, "weibfreq", weibfreq),
 	INIT_SYM(AFNCT, "weibinv", weibinv),		INIT_SYM(AFNCT, "cauchydist", cauchydist),
@@ -4313,17 +4360,17 @@ static char *copy_block()
 }
 
 static symrec *curr_sym;
-static double for_loop(char *block1, char *block2)
+static int for_loop(char *block1, char *block2)
 {
 	char *last_buffer, *bb1, *bb2, *bb3;
-	int i, a_count, last_buff_pos, cb1;
+	int i, a_count, last_buff_pos, cb1, bres;
 	double *a_data;
 	YYSTYPE yyres, yysrc;
 	symrec *var;
 
-	if(!block1 || !block1[0] || bNoExec) return 0.0;
+	if(!block1 || !block1[0] || bNoExec) return 0;
 	bb1 = bb2 = bb3 = 0L;		parse_level++;
-	cb1 = (int)strlen(block1);
+	cb1 = (int)strlen(block1);	bres = 0;
 	last_buffer = buffer;		last_buff_pos = buff_pos;
 	buffer = block1;		buff_pos = 0;
 	//test for syntax 1
@@ -4331,21 +4378,21 @@ static double for_loop(char *block1, char *block2)
 	if(buff_pos < cb1) bb2 = copy_block();
 	if(buff_pos < cb1) bb3 = copy_block();
 	if(bb1 && bb2 && bb3) {		//syntax 1 found !
-		yysrc.text = bb1;	if(bb1[0]) eval(&yyres, &yysrc);
-		for(i = 0; i < yy_maxiter; i++) {
+		yysrc.text = bb1;	if(bb1[0]) bres = eval(&yyres, &yysrc);
+		for(i = 0; !bres && i < yy_maxiter; i++) {
 			yysrc.text = bb2;
 			if(bb2[0]) {
-				eval(&yyres, &yysrc);
+				bres = eval(&yyres, &yysrc);
 				if(yyres.type != NUM && yyres.type != BOOLVAL) yyres.val = 0.0;
 				}
 			else yyres.val = 1.0;
 			if(yyres.val != 0.0) {
 				if(block2 && block2[0]) {
 					yysrc.text = block2;
-					eval(&yyres, &yysrc);
+					bres = eval(&yyres, &yysrc);
 					}
 				yysrc.text = bb3;
-				eval(&yyres, &yysrc);
+				bres = eval(&yyres, &yysrc);
 				}
 			else break;		
 			}
@@ -4356,14 +4403,14 @@ static double for_loop(char *block1, char *block2)
 		buff_pos = 0;
 		if (VAR == yylex() && (var = curr_sym) && INARR == yylex() && buffer[buff_pos]){
 			yysrc.text = buffer + buff_pos;
-			eval(&yyres, &yysrc);
+			bres = eval(&yyres, &yysrc);
 			a_count = yyres.a_count;
 			a_data = yyres.a_data;
-			for(i = 0; i < a_count && i < yy_maxiter; i++) {
+			for(i = 0; !bres && i < a_count && i < yy_maxiter; i++) {
 				var->SetValue(a_data[i]);
 				if(block2 && block2[0]) {
 					yysrc.text = block2;
-					eval(&yyres, &yysrc);
+					bres = eval(&yyres, &yysrc);
 					}
 				}
 			last_error = 0;
@@ -4374,7 +4421,7 @@ static double for_loop(char *block1, char *block2)
 	if(bb1) free(bb1);	if(bb2) free(bb2);	if(bb3) free(bb3);
 	buffer = last_buffer;		buff_pos = last_buff_pos;
 	parse_level--;
-	return 0.0;
+	return bres;
 }
 
 static int yylex()
@@ -4447,7 +4494,7 @@ static int yylex()
 			tmp_txt[i] = 0;
 			RangeData.GetData(tmp_txt, &yylval.a_data, &yylval.a_count, &yylval.text); 
 			yylval.val = 0.0; 
-			return yylval.type = RANGEARR;
+			return yylval.type = (yylval.a_data && yylval.a_count) ? RANGEARR : STR;
 			}
 		tmp_txt[i] = 0;
 		h_nam = HashValue((unsigned char*)tmp_txt);
@@ -4722,7 +4769,7 @@ bool MoveFormula(DataObj *d, char *of, char *nf, int nfsize, int dx, int dy, int
 	char *res, desc1[2], desc2[2];
 
 	if(d) curr_data = d;
-	if(!curr_data || !of || !nf) return false;
+	if(!curr_data || !of || !of[0] || !nf) return false;
 	push_parser();		//make code reentrant
 	init_table();		length = (int)strlen(of);
 	if(!(buffer = (char*)malloc(length+2))){
@@ -4873,10 +4920,12 @@ bool MoveFormula(DataObj *d, char *of, char *nf, int nfsize, int dx, int dy, int
 				pos += rlp_strcpy(res+pos, length2-pos, " break");
 				break;
 			case RANGEARR:
-				for(i = 0; yylval.text[i]; i++) if(yylval.text[i] == ':') {
-					yylval.text[i] = ';';	break;
+				if(yylval.text && yylval.text[0]) {
+					for(i = 0; yylval.text[i]; i++) if(yylval.text[i] == ':') {
+						yylval.text[i] = ';';	break;
+						}
+					MoveFormula(d, yylval.text, res+pos, nfsize-pos-2, dx, dy, r0, c0);
 					}
-				MoveFormula(d, yylval.text, res+pos, nfsize-pos-2, dx, dy, r0, c0);
 				while(res[pos]) {
 					pos++;			if(res[pos] == ';') res[pos] = ':';
 					}
@@ -4925,7 +4974,7 @@ static void fcurve(double x, double z, double **a, double *y, double dyda[], int
 		}
 	//calc result
 	symx->SetValue(x);	symz->SetValue(z);	
-	buffer = txt_formula;
+	buffer = txt_formula;	bNoSS = true;
 	buff_pos = 0;		length = (int)strlen(txt_formula);
 	while(!(parse_res = yyparse()) && buff_pos < length);
 	if(sy = getsym(hn_y, h2_y)) *y = sy->GetValue();
@@ -4955,6 +5004,7 @@ static void fcurve(double x, double z, double **a, double *y, double dyda[], int
 	if(a != parval) for(i = 0; i < ma; i++) {
 		tmp = *parval[i];	*parval[i]  = *a[i];	*a[i] = tmp;
 		}
+	bNoSS = false;
 }
 
 int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr, double conv, int maxiter, double *chi_2)
@@ -5049,6 +5099,7 @@ int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr,
 		mrqmin(x, y, z, ndata, parval, nparam, lista, nparam, covar, alpha, &chisq, fcurve, &alamda);
 		Check_MRQerror();
 		}
+	bNoSS = true;
 	for(i = nparam-1, j = k = l = 0; i >= 0; l = 0, i--) {
 		if(k > 20) {
 			if(tmp_txt[j-1] == ' ') j--;
@@ -5085,8 +5136,7 @@ int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr,
 		d->Command(CMD_CLEAR_ERROR, 0L, 0L);
 		d->Command(CMD_REDRAW, 0L, 0L);
 		}
+	bNoSS = false;
 	return itst < maxiter ? itst+1 : maxiter;
 }
 
-
-
diff --git a/mfcalc.y b/mfcalc.y
index 69a4162..5fb3fd0 100755
--- a/mfcalc.y
+++ b/mfcalc.y
@@ -1,6 +1,6 @@
 %{
 /*
- mfcalc.y, mfcalc.cpp, Copyright (c) 2004-2006 R.Lackner
+ mfcalc.y, mfcalc.cpp, Copyright (c) 2004-2008 R.Lackner
  parse string and simple math: based on the bison 'mfcalc' example
 
     This file is part of RLPlot.
@@ -76,6 +76,7 @@ static int block_res;			//result of eval()
 static symrec *putsym (unsigned int h_name, unsigned int h2_name, int sym_type);
 static symrec *getsym (unsigned int h_name, unsigned int h2_name, char *sym_name = 0L);
 static int push(YYSTYPE *res, YYSTYPE *val);
+static void yyCompare(YYSTYPE *res, YYSTYPE *arg1, YYSTYPE *arg2, int op);
 static void store_res(YYSTYPE *res);
 static char *PushString(char *text);
 static double *PushArray(double *arr);
@@ -91,7 +92,7 @@ static void yyerror(char *s);
 static void make_time(YYSTYPE *dst, double h, double m, double s);
 static int yylex(void);
 static double nop() {return 0.0;};
-static double for_loop(char *block1, char *block2);
+static int for_loop(char *block1, char *block2);
 
 static char res_txt[1000];
 static anyResult line_res = {ET_UNKNOWN, 0.0, res_txt, 0L, 0};
@@ -102,7 +103,7 @@ static char *last_err_desc = 0L;	//short error description
 static char *buffer = 0L;		//the current command buffer
 static int buff_pos = 0;
 static bool bRecent = false;		//rearrange functions
-static bool bNoWrite, bNoExec;		//while editing ...
+static bool bNoWrite, bNoExec, bNoSS;	//while editing ...
 static int parse_level = 0;		//count reentrances into parser
 #define MAX_PARSE 50			//maximum number of recursive reentances 
 %}
@@ -124,8 +125,8 @@ static int parse_level = 0;		//count reentrances into parser
 %left   EQ NE GT GE LT LE
 %left   '-' '+'
 %left   '*' '/'
-%left	'['
 %left 	'^'	 	/* exponentiation       */
+%left	'['
 %left 	NEG 		/* negation-unary minus */
 %left	INC DEC		/* increment, decrement */
 %left	PINC PDEC	/* pre- increment, decrement */
@@ -137,10 +138,11 @@ input:    /* empty string */
 	| input line
 ;
 
+anysep:	';' | ',';
+
 line:	 '\n' | ';' | ','
 	| exp '\n'		{store_res(&yyvsp[-1]); return 0;}
-	| exp ';'		{store_res(&yyvsp[-1]); return 0;}
-	| exp ','		{store_res(&yyvsp[-1]); return 0;}
+	| exp anysep		{store_res(&yyvsp[-1]); return 0;}
 	| str_exp '\n'		{store_res(&yyvsp[-1]); return 0;}
 	| str_exp ';'		{store_res(&yyvsp[-1]); return 0;}
 	| IF PBLOCK block	{if(block_res = eval(&yyval, &yyvsp[-1]))return block_res; 
@@ -148,7 +150,7 @@ line:	 '\n' | ';' | ','
 	| IF PBLOCK block ELSE block {if(block_res = eval(&yyval, &yyvsp[-3])) return block_res; 
 				if(yyval.val != 0.0) {if(block_res = eval(&yyval, &yyvsp[-2])) return block_res;} 
 				else if(block_res = eval(&yyval, &yyvsp[0])) return block_res;}
-	| FOR PBLOCK block	{for_loop(yyvsp[-1].text, yyvsp[0].text);}
+	| FOR PBLOCK block	{block_res = for_loop(yyvsp[-1].text, yyvsp[0].text); if(block_res == 1 || block_res == 2) return block_res;}
 	| WHILE PBLOCK block	{for(int i=0; i< yy_maxiter; i++){ if(block_res = eval(&yyval, &yyvsp[-1]))return block_res; 
 					if(yyval.val != 0.0){if(block_res = eval(&yyval, &yyvsp[0]))return block_res;} else break;}}
 	| RETURN IBLOCK		{if(block_res = eval(&yyval, &yyvsp[0]) == 1) return 1; else return 2;}
@@ -162,10 +164,7 @@ str_exp:
 	|exp '+' str_exp	{yyval.text=add_strings(string_value(&yyvsp[-2]), yyvsp[0].text); yyval.type = STR;}
 	|str_exp '+' str_exp	{yyval.text=add_strings(yyvsp[-2].text, yyvsp[0].text); yyval.type = STR;}
 	|SRFUNC '(' exp ')'	{if($1->fnctptr)(($1->fnctptr)(&yyval, &yyvsp[-1], 0L)); yyval.type = STR;}
-	|SRFUNC '(' exp PSEP str_exp ')'	{if($1->fnctptr)(($1->fnctptr)(&yyval, &yyvsp[-3], yyvsp[-1].text)); yyval.type = STR;}
-	|SRFUNC '(' exp PSEP exp ')'		{if($1->fnctptr)(($1->fnctptr)(&yyval, &yyvsp[-3], yyvsp[-1].text)); yyval.type = STR;}
-	|SRFUNC '(' exp ',' str_exp ')'		{if($1->fnctptr)(($1->fnctptr)(&yyval, &yyvsp[-3], yyvsp[-1].text)); yyval.type = STR;}
-	|SRFUNC '(' exp ',' exp ')'		{if($1->fnctptr)(($1->fnctptr)(&yyval, &yyvsp[-3], yyvsp[-1].text)); yyval.type = STR;}
+	|SRFUNC '(' exp anysep str_exp ')'	{if($1->fnctptr)(($1->fnctptr)(&yyval, &yyvsp[-3], yyvsp[-1].text)); yyval.type = STR;}
 ;
 
 range:
@@ -185,14 +184,14 @@ bool:	BOOLVAL
 	|BFNCT '(' exp ')'	{$$ = $1->fnctptr ? (($1->fnctptr)($3)): 0.0; yyval.type = BOOLVAL;}
 	|BTRUE			{$$ = 1.0; yyval.type = BOOLVAL;}
 	|BFALSE			{$$ = 0.0; yyval.type = BOOLVAL;}
-	|exp AND exp		{$$ = (($1 != 0) && ($3 != 0))? 1 : 0; yyval.type = BOOLVAL;}
-	|exp OR exp		{$$ = (($1 != 0) || ($3 != 0))? 1 : 0; yyval.type = BOOLVAL;}
-	|exp EQ exp		{$$ = ($1 == $3) ? 1 : 0; yyval.type = BOOLVAL;}
-	|exp NE exp		{$$ = ($1 != $3) ? 1 : 0; yyval.type = BOOLVAL;}
-	|exp GT exp		{$$ = ($1 > $3) ? 1 : 0; yyval.type = BOOLVAL;}
-	|exp GE exp		{$$ = ($1 >= $3) ? 1 : 0; yyval.type = BOOLVAL;}
-	|exp LT exp		{$$ = ($1 < $3) ? 1 : 0; yyval.type = BOOLVAL;}
-	|exp LE exp		{$$ = ($1 <= $3) ? 1 : 0; yyval.type = BOOLVAL;}
+	|exp AND exp		{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], AND);}
+	|exp OR exp		{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], OR);}
+	|exp EQ exp		{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], EQ);}
+	|exp NE exp		{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], NE);}
+	|exp GT exp		{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], GT);}
+	|exp GE exp		{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], GE);}
+	|exp LT exp		{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], LT);}
+	|exp LE exp		{yyCompare(&yyval, &yyvsp[-2], &yyvsp[0], LE);}
 ;
 
 block:	BLOCK | IBLOCK;
@@ -224,14 +223,7 @@ exp:	NUM				{$$ = $1; yyval.type = NUM;}
 		$$ = $1->fnctptr ? (($1->fnctptr)(&yyval)) : 0.0; yyval.type = NUM;}
 	|SFNCT '(' str_exp ')'	{yyval.type = NUM; $$ = $1->fnctptr ? (($1->fnctptr)(&yyvsp[-1], &yyval, 0L)) : 0.0;}
 	|SFNCT '(' exp ')'	{yyval.type = NUM; yyvsp[-1].text = string_value(&yyvsp[-1]); $$ = $1->fnctptr ? (($1->fnctptr)(&yyvsp[-1], &yyval, 0L)) : 0.0;}
-	|SFNCT '(' str_exp PSEP str_exp ')' {$$ = $1->fnctptr ? (($1->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;}
-	|SFNCT '(' exp PSEP str_exp ')' {$$ = $1->fnctptr ?(($1->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;}
-	|SFNCT '(' exp PSEP exp ')' {$$ = $1->fnctptr ? (($1->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;}
-	|SFNCT '(' str_exp PSEP exp ')' {$$ = $1->fnctptr ? (($1->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;}
-	|SFNCT '(' str_exp ',' str_exp ')' {$$ = $1->fnctptr ? (($1->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;}
-	|SFNCT '(' exp ',' str_exp ')' {$$ = $1->fnctptr ? (($1->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;}
-	|SFNCT '(' str_exp ',' exp ')' {$$ = $1->fnctptr ? (($1->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;}
-	|SFNCT '(' exp ',' exp ')' {$$ = $1->fnctptr ? (($1->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;}
+	|SFNCT '(' anyarg anysep anyarg ')' {$$ = $1->fnctptr ? (($1->fnctptr)(&yyvsp[-3], &yyval, yyvsp[-1].text)) : 0.0; yyval.type = NUM;}
 	|FUNC4 '(' exp PSEP exp PSEP arr PSEP range ')' {proc_clause(&yyvsp[-3]); $$=$1->fnctptr ? (*$1->fnctptr)($3, $5, &yyvsp[-3], &yyvsp[-1]) : 0.0; yyval.type = NUM;}
 	|FUNC1 '(' arr PSEP range ')' {$$ = $1->fnctptr ? ((*$1->fnctptr)(proc_clause(&yyvsp[-3]), yyvsp[-1].text)) : 0.0;  yyval.type = NUM;}
 	|FUNC1 '(' arr PSEP RANGEARR ')' {$$ = $1->fnctptr ? ((*$1->fnctptr)(proc_clause(&yyvsp[-3]), yyvsp[-1].text)) : 0.0;  yyval.type = NUM;}
@@ -262,7 +254,7 @@ exp:	NUM				{$$ = $1; yyval.type = NUM;}
 	|DIM VAR %prec PDIM '[' exp ']'	{yyval.a_data = PushArray((double*)calloc((int)$4, sizeof(double))); yyval.a_count=(int)($4); 
 					yyval.type = ARR; $2->SetValue(&yyval,&yyval);}
 	|exp '[' exp ']'	{if(yyvsp[-3].a_data && yyvsp[-1].val >= 0.0 && yyvsp[-1].val < yyvsp[-3].a_count) $$ = yyvsp[-3].a_data[(int)yyvsp[-1].val];
-				else {$$ = 0.0; last_err_desc = "#INDEX";} yyval.type = NUM;}
+				else {$$ = 0.0; last_err_desc = "#INDEX";} yyval.a_data = 0L; yyval.a_count = 0; yyval.type = NUM;}
 	|exp '[' exp ']' '=' exp {if(yyvsp[-5].a_data && yyvsp[-3].val >= 0.0 && yyvsp[-3].val < yyvsp[-5].a_count)
 				{$$ = yyvsp[-5].a_data[(int)yyvsp[-3].val] = $6; 
 				if(yyvsp[-5].tptr && yyvsp[-5].tptr->type == VAR)yyvsp[-5].tptr->SetValue(0L, &yyvsp[-5]);}
@@ -583,7 +575,7 @@ symrec::GetValue()
 {
 	anyResult ares;
 
-	if(isSSval) {
+	if(isSSval && !bNoSS) {
 		if(row < 0 && col < 0) InitSS();
 		//GetResult( , , ,true) inhibits reentrance into parser !
 		if(curr_data->GetResult(&ares, row, col, parse_level > MAX_PARSE)){
@@ -601,9 +593,10 @@ symrec::GetValue(void *re)
 {
 	anyResult ares;
 	YYSTYPE *res = (YYSTYPE*)re;
+	int i;
 
 	if(!res) return;
-	if(isSSval) {
+	if(isSSval && !bNoSS) {
 		if(row < 0 && col < 0) InitSS();
 		res->a_data = 0L;	res->a_count = 0;
 		//GetResult( , , ,true) inhibits reentrance into parser !
@@ -627,9 +620,21 @@ symrec::GetValue(void *re)
 				res->type = DATETIME1;	res->val = ares.value;
 				res->text = 0L;		break;
 			case ET_TEXT:
-				res->type = STR;	res->val = 0.0;
-				if(ares.text) res->text = PushString(text = (char*)memdup(ares.text, (int)strlen(ares.text)+1, 0));
-				else res->text = 0L;	break;
+				i = 0;			res->val = 0.0;
+				if(ares.text) for( ; ares.text && ares.text[i] != ':'; i++);
+				if(i > 1 && ares.text[i] 
+					&& (isalpha(ares.text[0]) || ares.text[0] =='$')
+					&& (isalpha(ares.text[i+1]) || ares.text[i+1] =='$')
+					&& isdigit(ares.text[i-1])) {
+					RangeData.GetData(ares.text, &res->a_data, &res->a_count, &res->text); 
+					res->type = RANGEARR;
+					}
+				else {
+					res->type = STR;
+					if(ares.text) res->text = PushString(text = (char*)memdup(ares.text, (int)strlen(ares.text)+1, 0));
+					else res->text = 0L;
+					}
+				break;
 			default:
 				res->type = NUM;	res->val = var;
 				res->text = 0L;		break;
@@ -749,6 +754,7 @@ void LockData(bool lockExec, bool lockWrite)
 	RangeData.Clear();
 	bNoWrite = lockWrite;
 	bNoExec = lockExec;
+	bNoSS = false;
 } 
  
 static void yyerror(char *s)
@@ -826,6 +832,69 @@ char  *yywarn(char *txt, bool bNew)
 	else return 0L;
 }
 
+static void yyCompare(YYSTYPE *res, YYSTYPE *arg1, YYSTYPE *arg2, int op)
+{
+	int cmp;
+
+	if(!res || !arg1 || !arg2) return;
+	if(arg1->type == STR && arg2->type == STR) {
+		if (arg1->text && arg1->text[0] && arg2->text
+			&& arg2->text[0]) cmp = strcmp(arg1->text, arg2->text);
+		else if(arg1->text && arg1->text[0]) cmp = 1;
+		else if(arg2->text && arg2->text[0]) cmp = -1;
+		else cmp = 0;
+		switch(op) {
+		case AND:
+			res->val = (arg1->text && arg1->text[0] && arg2->text
+			&& arg2->text[0]) ? 1.0 : 0.0;		break;
+		case OR:
+			res->val = ((arg1->text && arg1->text[0]) || (arg2->text
+			&& arg2->text[0])) ? 1.0 : 0.0;		break;
+		case EQ:
+			res->val = cmp ? 0.0 : 1.0;		break;
+		case NE:
+			res->val = cmp ? 1.0 : 0.0;		break;
+		case GT:
+			res->val = cmp > 0 ? 1.0 : 0.0;		break;
+		case GE:
+			res->val = cmp >= 0 ? 1.0 : 0.0;	break;
+		case LT:
+			res->val = cmp < 0 ? 1.0 : 0.0;		break;
+		case LE:
+			res->val = cmp <= 0 ? 1.0 : 0.0;	break;
+			}
+		}
+	else {
+		switch(op) {
+		case AND:
+			res->val = (arg1->val != 0.0 && arg2->val != 0.0)
+			? 1.0 : 0.0;				break;
+		case OR:
+			res->val = (arg1->val != 0.0 || arg2->val != 0.0)
+			? 1.0 : 0.0;				break;
+		case EQ:
+			res->val = (arg1->val ==  arg2->val) ? 1.0 : 0.0;
+			break;
+		case NE:
+			res->val = (arg1->val !=  arg2->val) ? 1.0 : 0.0;
+			break;
+		case GT:
+			res->val = (arg1->val >  arg2->val) ? 1.0 : 0.0;
+			break;
+		case GE:
+			res->val = (arg1->val >=  arg2->val) ? 1.0 : 0.0;
+			break;
+		case LT:
+			res->val = (arg1->val <  arg2->val) ? 1.0 : 0.0;
+			break;
+		case LE:
+			res->val = (arg1->val <=  arg2->val) ? 1.0 : 0.0;
+			break;
+			}
+		}
+	res->type = BOOLVAL;
+}
+
 static void store_res(YYSTYPE *res)
 {
 	if(last_err_desc) {
@@ -871,7 +940,8 @@ static void store_res(YYSTYPE *res)
 		line_res.value = 0.0;
 		line_res.a_data = res->a_data;
 		line_res.a_count = res->a_count;
-		rlp_strcpy(res_txt, 1000, "#ARRAY");
+		if(res->text) rlp_strcpy(res_txt, 1000, res->text);
+		else rlp_strcpy(res_txt, 1000, "#ARRAY");
 		}
 	else if(res->tptr && res->tptr->type == TXT) {
 		line_res.type = ET_TEXT;
@@ -1021,6 +1091,14 @@ static double srand(double v)
 	return v;
 }
 
+static double maxiter(YYSTYPE *dst, YYSTYPE *src)
+{
+	if(!dst) return 0.0;
+	if(src) yy_maxiter = (int)src->val;
+	dst->type = NUM;
+	return(dst->val = (double)yy_maxiter);
+}
+
 static double factorial(double v)
 {
 	return factrl((int)v);
@@ -1564,6 +1642,50 @@ static double weibinv(YYSTYPE *sr)
 	return sr->val;
 }
 
+static double geomfreq(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 2){
+		sr->val = geom_freq(sr->a_data[0], sr->a_data[1]);
+		}
+	else yyargcnterr("geomfreq(x, p)");
+	return sr->val;
+}
+
+static double geomdist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 2){
+		sr->val = geom_dist(sr->a_data[0], sr->a_data[1]);
+		}
+	else yyargcnterr("geomdist(x, p)");
+	return sr->val;
+}
+
+static double hyperfreq(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 4){
+		sr->val = hyper_freq(sr->a_data[0], sr->a_data[1], sr->a_data[2], sr->a_data[3]);
+		}
+	else yyargcnterr("hyperfreq(k, N, m, n)");
+	return sr->val;
+}
+
+static double hyperdist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 4){
+		sr->val = hyper_dist(sr->a_data[0], sr->a_data[1], sr->a_data[2], sr->a_data[3]);
+		}
+	else yyargcnterr("hyperdist(k, N, m, n)");
+	return sr->val;
+}
+
 static double cauchydist(YYSTYPE *sr)
 {
 	if(!sr) return 0.0;
@@ -2408,7 +2530,9 @@ void InitArithFuncs(DataObj *d)
 		double (*fnct)(double);
 		};
 	fdef fncts[] = {
-	INIT_SYM(YYFNC3, "mkarr", mkarr),
+	INIT_SYM(AFNCT, "geomfreq", geomfreq),		INIT_SYM(AFNCT, "geomdist", geomdist),
+	INIT_SYM(AFNCT, "hyperfreq", hyperfreq),	INIT_SYM(AFNCT, "hyperdist", hyperdist),
+	INIT_SYM(YYFNC3, "mkarr", mkarr),		INIT_SYM(YYFNC, "maxiter", maxiter),
 	INIT_SYM(YYFNC, "randarr", _randarr),		INIT_SYM(YYFNC, "resample", _resample),
 	INIT_SYM(AFNCT, "weibdist", weibdist),		INIT_SYM(AFNCT, "weibfreq", weibfreq),
 	INIT_SYM(AFNCT, "weibinv", weibinv),		INIT_SYM(AFNCT, "cauchydist", cauchydist),
@@ -2852,17 +2976,17 @@ static char *copy_block()
 }
 
 static symrec *curr_sym;
-static double for_loop(char *block1, char *block2)
+static int for_loop(char *block1, char *block2)
 {
 	char *last_buffer, *bb1, *bb2, *bb3;
-	int i, a_count, last_buff_pos, cb1;
+	int i, a_count, last_buff_pos, cb1, bres;
 	double *a_data;
 	YYSTYPE yyres, yysrc;
 	symrec *var;
 
-	if(!block1 || !block1[0] || bNoExec) return 0.0;
+	if(!block1 || !block1[0] || bNoExec) return 0;
 	bb1 = bb2 = bb3 = 0L;		parse_level++;
-	cb1 = (int)strlen(block1);
+	cb1 = (int)strlen(block1);	bres = 0;
 	last_buffer = buffer;		last_buff_pos = buff_pos;
 	buffer = block1;		buff_pos = 0;
 	//test for syntax 1
@@ -2870,21 +2994,21 @@ static double for_loop(char *block1, char *block2)
 	if(buff_pos < cb1) bb2 = copy_block();
 	if(buff_pos < cb1) bb3 = copy_block();
 	if(bb1 && bb2 && bb3) {		//syntax 1 found !
-		yysrc.text = bb1;	if(bb1[0]) eval(&yyres, &yysrc);
-		for(i = 0; i < yy_maxiter; i++) {
+		yysrc.text = bb1;	if(bb1[0]) bres = eval(&yyres, &yysrc);
+		for(i = 0; !bres && i < yy_maxiter; i++) {
 			yysrc.text = bb2;
 			if(bb2[0]) {
-				eval(&yyres, &yysrc);
+				bres = eval(&yyres, &yysrc);
 				if(yyres.type != NUM && yyres.type != BOOLVAL) yyres.val = 0.0;
 				}
 			else yyres.val = 1.0;
 			if(yyres.val != 0.0) {
 				if(block2 && block2[0]) {
 					yysrc.text = block2;
-					eval(&yyres, &yysrc);
+					bres = eval(&yyres, &yysrc);
 					}
 				yysrc.text = bb3;
-				eval(&yyres, &yysrc);
+				bres = eval(&yyres, &yysrc);
 				}
 			else break;		
 			}
@@ -2895,14 +3019,14 @@ static double for_loop(char *block1, char *block2)
 		buff_pos = 0;
 		if (VAR == yylex() && (var = curr_sym) && INARR == yylex() && buffer[buff_pos]){
 			yysrc.text = buffer + buff_pos;
-			eval(&yyres, &yysrc);
+			bres = eval(&yyres, &yysrc);
 			a_count = yyres.a_count;
 			a_data = yyres.a_data;
-			for(i = 0; i < a_count && i < yy_maxiter; i++) {
+			for(i = 0; !bres && i < a_count && i < yy_maxiter; i++) {
 				var->SetValue(a_data[i]);
 				if(block2 && block2[0]) {
 					yysrc.text = block2;
-					eval(&yyres, &yysrc);
+					bres = eval(&yyres, &yysrc);
 					}
 				}
 			last_error = 0;
@@ -2913,7 +3037,7 @@ static double for_loop(char *block1, char *block2)
 	if(bb1) free(bb1);	if(bb2) free(bb2);	if(bb3) free(bb3);
 	buffer = last_buffer;		buff_pos = last_buff_pos;
 	parse_level--;
-	return 0.0;
+	return bres;
 }
 
 static int yylex()
@@ -2986,7 +3110,7 @@ static int yylex()
 			tmp_txt[i] = 0;
 			RangeData.GetData(tmp_txt, &yylval.a_data, &yylval.a_count, &yylval.text); 
 			yylval.val = 0.0; 
-			return yylval.type = RANGEARR;
+			return yylval.type = (yylval.a_data && yylval.a_count) ? RANGEARR : STR;
 			}
 		tmp_txt[i] = 0;
 		h_nam = HashValue((unsigned char*)tmp_txt);
@@ -3261,7 +3385,7 @@ bool MoveFormula(DataObj *d, char *of, char *nf, int nfsize, int dx, int dy, int
 	char *res, desc1[2], desc2[2];
 
 	if(d) curr_data = d;
-	if(!curr_data || !of || !nf) return false;
+	if(!curr_data || !of || !of[0] || !nf) return false;
 	push_parser();		//make code reentrant
 	init_table();		length = (int)strlen(of);
 	if(!(buffer = (char*)malloc(length+2))){
@@ -3412,10 +3536,12 @@ bool MoveFormula(DataObj *d, char *of, char *nf, int nfsize, int dx, int dy, int
 				pos += rlp_strcpy(res+pos, length2-pos, " break");
 				break;
 			case RANGEARR:
-				for(i = 0; yylval.text[i]; i++) if(yylval.text[i] == ':') {
-					yylval.text[i] = ';';	break;
+				if(yylval.text && yylval.text[0]) {
+					for(i = 0; yylval.text[i]; i++) if(yylval.text[i] == ':') {
+						yylval.text[i] = ';';	break;
+						}
+					MoveFormula(d, yylval.text, res+pos, nfsize-pos-2, dx, dy, r0, c0);
 					}
-				MoveFormula(d, yylval.text, res+pos, nfsize-pos-2, dx, dy, r0, c0);
 				while(res[pos]) {
 					pos++;			if(res[pos] == ';') res[pos] = ':';
 					}
@@ -3464,7 +3590,7 @@ static void fcurve(double x, double z, double **a, double *y, double dyda[], int
 		}
 	//calc result
 	symx->SetValue(x);	symz->SetValue(z);	
-	buffer = txt_formula;
+	buffer = txt_formula;	bNoSS = true;
 	buff_pos = 0;		length = (int)strlen(txt_formula);
 	while(!(parse_res = yyparse()) && buff_pos < length);
 	if(sy = getsym(hn_y, h2_y)) *y = sy->GetValue();
@@ -3494,6 +3620,7 @@ static void fcurve(double x, double z, double **a, double *y, double dyda[], int
 	if(a != parval) for(i = 0; i < ma; i++) {
 		tmp = *parval[i];	*parval[i]  = *a[i];	*a[i] = tmp;
 		}
+	bNoSS = false;
 }
 
 int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr, double conv, int maxiter, double *chi_2)
@@ -3588,6 +3715,7 @@ int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr,
 		mrqmin(x, y, z, ndata, parval, nparam, lista, nparam, covar, alpha, &chisq, fcurve, &alamda);
 		Check_MRQerror();
 		}
+	bNoSS = true;
 	for(i = nparam-1, j = k = l = 0; i >= 0; l = 0, i--) {
 		if(k > 20) {
 			if(tmp_txt[j-1] == ' ') j--;
@@ -3624,10 +3752,7 @@ int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr,
 		d->Command(CMD_CLEAR_ERROR, 0L, 0L);
 		d->Command(CMD_REDRAW, 0L, 0L);
 		}
+	bNoSS = false;
 	return itst < maxiter ? itst+1 : maxiter;
 }
 
-
-
-
-
diff --git a/no_gui.cpp b/no_gui.cpp
index 2195534..c03da54 100755
--- a/no_gui.cpp
+++ b/no_gui.cpp
@@ -1,585 +1,590 @@
-//no_gui.cpp, Copyright 2000-2007 R.Lackner
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// This modules contains code for builds with no graphical user interface.
-// Builds using the GUI are compiled with use_gui.cpp instead.
-//
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//
-#include "rlplot.h"
-
-extern char *name1, *name2;				//the filenames
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// STUBS: we do not need this objects or functions without GUI
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-dragHandle::dragHandle(GraphObj *par, int which):GraphObj(par, 0L)
-{
-}
-
-dragHandle::~dragHandle()
-{
-}
-
-void
-dragHandle::DoPlot(anyOutput *o)
-{
-}
-
-bool
-dragHandle::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	return false;
-}
-
-void *
-dragHandle::ObjThere(int x, int y)
-{
-	return 0L;
-}
-
-void
-dragHandle::Track(POINT *p, anyOutput *o)
-{
-}
-
-dragRect::dragRect(GraphObj *par, int which):GraphObj(par, 0L)
-{
-}
-
-dragRect::~dragRect()
-{
-}
-
-void
-dragRect::DoPlot(anyOutput *o)
-{
-}
-
-void *
-dragRect::ObjThere(int x, int y)
-{
-	return 0L;
-}
-
-bool
-dragRect::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	return false;
-}
-
-Drag3D::Drag3D(GraphObj *par):GraphObj(par, 0L)
-{
-}
-
-Drag3D::~Drag3D()
-{
-}
-
-void *
-Drag3D::ObjThere(int x, int y)
-{
-	return 0L;
-}
-
-void
-Drag3D::DoPlot(anyOutput *o)
-{
-}
-
-FrmRect::FrmRect(GraphObj *par, fRECT *lim, fRECT *c, fRECT *chld):GraphObj(par, 0L)
-{
-	Id = Id;
-}
-
-FrmRect::~FrmRect()
-{
-}
-
-double
-FrmRect::GetSize(int select)
-{
-	return 0.0;
-}
-
-bool
-FrmRect::SetSize(int select, double value)
-{
-	return false;
-}
-
-bool
-FrmRect::SetColor(int select, DWORD col)
-{
-	return false;
-}
-
-void
-FrmRect::DoMark(anyOutput *o, bool mark)
-{
-}
-
-void
-FrmRect::DoPlot(anyOutput *o)
-{
-}
-
-bool
-FrmRect::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	return false;
-}
-
-void *
-FrmRect::ObjThere(int x, int y)
-{
-	return 0L;
-}
-
-void
-FrmRect::Track(POINT *p, anyOutput *o)
-{
-}
-
-bool ShowLayers(GraphObj *root)
-{
-	return false;
-}
-
-bool GetBitmapRes(double *res, double *width, double *height, char *header)
-{
-	return false;
-}
-
-bool GetPaper(double *w, double *h)
-{
-	*w = *h = 1.0;
-	return true;
-}
-
-bool Symbol::PropertyDlg()
-{
-	return false;
-}
-
-bool Bubble::PropertyDlg()
-{
-	return false;
-}
-
-bool Bar::PropertyDlg()
-{
-	return false;
-}
-
-bool DataLine::PropertyDlg()
-{
-	return false;
-}
-
-bool DataPolygon::PropertyDlg()
-{
-	return false;
-}
-
-bool RegLine::PropertyDlg()
-{
-	return false;
-}
-
-bool SDellipse::PropertyDlg()
-{
-	return false;
-}
-
-bool ErrorBar::PropertyDlg()
-{
-	return false;
-}
-
-bool Arrow::PropertyDlg()
-{
-	return false;
-}
-
-void *
-Arrow::ObjThere(int x, int y)
-{
-	return 0L;
-}
-
-void
-Arrow::Track(POINT *p, anyOutput *o)
-{
-}
-
-bool Box::PropertyDlg()
-{
-	return false;
-}
-
-bool Whisker::PropertyDlg()
-{
-	return false;
-}
-
-bool DropLine::PropertyDlg()
-{
-	return false;
-}
-
-bool Sphere::PropertyDlg()
-{
-	return false;
-}
-
-bool Plane3D::PropertyDlg()
-{
-	return false;
-}
-
-bool Brick::PropertyDlg()
-{
-	return false;
-}
-
-bool DropLine3D::PropertyDlg()
-{
-	return false;
-}
-
-bool Arrow3D::PropertyDlg()
-{
-	return false;
-}
-
-bool Line3D::PropertyDlg()
-{
-	return false;
-}
-
-bool Label::PropertyDlg()
-{
-	return false;
-}
-
-void Label::ShowCursor(anyOutput *o)
-{
-}
-
-bool Label::AddChar(int ci, anyOutput *o)
-{
-	return true;
-}
-
-void Label::CalcCursorPos(int x, int y, anyOutput *o)
-{
-}
-
-bool TextFrame::PropertyDlg()
-{
-	return false;
-}
-
-void TextFrame::ShowCursor(anyOutput *o)
-{
-}
-
-void TextFrame::CalcCursorPos(int x, int y, anyOutput *o)
-{
-}
-
-bool segment::PropertyDlg()
-{
-	return false;
-}
-
-bool polyline::PropertyDlg()
-{
-	return false;
-}
-
-bool polygon::PropertyDlg()
-{
-	return false;
-}
-
-bool rectangle::PropertyDlg()
-{
-	return false;
-}
-
-bool PlotScatt::PropertyDlg()
-{
-	return false;
-}
-
-bool xyStat::PropertyDlg()
-{
-	return false;
-}
-
-bool Regression::PropertyDlg()
-{
-	return false;
-}
-
-bool FreqDist::PropertyDlg()
-{
-	return false;
-}
-
-bool BubblePlot::PropertyDlg()
-{
-	return false;
-}
-
-bool PolarPlot::PropertyDlg()
-{
-	return false;
-}
-
-bool PolarPlot::Config()
-{
-	return false;
-}
-
-bool BoxPlot::PropertyDlg()
-{
-	return false;
-}
-
-bool DensDisp::PropertyDlg()
-{
-	return false;
-}
-
-bool StackBar::PropertyDlg()
-{
-	return false;
-}
-
-bool Waterfall::PropertyDlg()
-{
-	return false;
-}
-
-bool MultiLines::PropertyDlg()
-{
-	return false;
-}
-
-bool PieChart::PropertyDlg()
-{
-	return false;
-}
-
-bool StarChart::PropertyDlg()
-{
-	return false;
-}
-
-bool Function::PropertyDlg()
-{
-	return false;
-}
-
-bool Grid3D::PropertyDlg()
-{
-	return false;
-}
-
-bool Grid3D::Configure()
-{
-	return false;
-}
-
-bool Scatt3D::PropertyDlg()
-{
-	return false;
-}
-
-bool FitFunc::PropertyDlg()
-{
-	return false;
-}
-
-bool NormQuant::PropertyDlg()
-{
-	return false;
-}
-
-bool Plot3D::AddAxis()
-{
-	return false;
-}
-
-bool Plot3D::PropertyDlg()
-{
-	return false;
-}
-
-bool Plot3D::AddPlot(int)
-{
-	return false;
-}
-
-bool Chart25D::PropertyDlg()
-{
-	return false;
-}
-
-bool Ribbon25D::PropertyDlg()
-{
-	return false;
-}
-
-bool Func3D::PropertyDlg()
-{
-	return false;
-}
-
-bool FitFunc3D::PropertyDlg()
-{
-	return false;
-}
-
-bool BubblePlot3D::PropertyDlg()
-{
-	return false;
-}
-
-bool GridLine::PropertyDlg()
-{
-	return false;
-}
-
-bool Tick::PropertyDlg()
-{
-	return false;
-}
-
-void
-Axis::DoMark(anyOutput *o, bool mark)
-{
-}
-
-bool Axis::PropertyDlg()
-{
-	return false;
-}
-
-bool Graph::AddPlot(int family)
-{
-	return false;
-}
-
-bool Graph::PropertyDlg()
-{
-	return false;
-}
-
-bool Graph::Configure()
-{
-	return false;
-}
-
-bool Graph::AddAxis()
-{
-	return false;
-}
-
-bool Graph::ExecTool(MouseEvent *mev)
-{
-	return false;
-}
-
-bool Graph::MoveObj(int cmd, GraphObj *g)
-{
-	return false;
-}
-
-bool Graph::DoZoom(char *z)
-{
-	return false;
-}
-
-bool Page::Configure()
-{
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// some more STUBS .....
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-char *SaveGraphAsName(char *oldname)
-{
-	return name2;
-}
-
-char *OpenGraphName(char *oldname)
-{
-	return 0L;
-}
-
-void HideTextCursor()
-{
-	return;
-}
-
-void HideTextCursorObj(anyOutput *out)
-{
-	return;
-}
-
-void ShowTextCursor(anyOutput *out, RECT *disp, DWORD color)
-{
-	return;
-}
-
-void InvalidateOutput(anyOutput *o)
-{
-	return;
-}
-
-void SuspendAnimation(anyOutput *o, bool bSusp)
-{
-	return;
-}
-
-anyOutput *NewDispClass(GraphObj *g)
-{
-	return 0L;
-}
-
-bool DelDispClass(anyOutput *w)
-{
-	return false;
-}
-
-anyOutput *NewBitmapClass(int w, int h, double hr, double vr)
-{
-	return 0L;
-}
-
-bool DelBitmapClass(anyOutput *w)
-{
-	return false;
-}
-
+//no_gui.cpp, Copyright 2000-2007 R.Lackner
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// This modules contains code for builds with no graphical user interface.
+// Builds using the GUI are compiled with use_gui.cpp instead.
+//
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+#include "rlplot.h"
+
+extern char *name1, *name2;				//the filenames
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// STUBS: we do not need this objects or functions without GUI
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+dragHandle::dragHandle(GraphObj *par, int which):GraphObj(par, 0L)
+{
+}
+
+dragHandle::~dragHandle()
+{
+}
+
+void
+dragHandle::DoPlot(anyOutput *o)
+{
+}
+
+bool
+dragHandle::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	return false;
+}
+
+void *
+dragHandle::ObjThere(int x, int y)
+{
+	return 0L;
+}
+
+void
+dragHandle::Track(POINT *p, anyOutput *o)
+{
+}
+
+dragRect::dragRect(GraphObj *par, int which):GraphObj(par, 0L)
+{
+}
+
+dragRect::~dragRect()
+{
+}
+
+void
+dragRect::DoPlot(anyOutput *o)
+{
+}
+
+void *
+dragRect::ObjThere(int x, int y)
+{
+	return 0L;
+}
+
+bool
+dragRect::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	return false;
+}
+
+Drag3D::Drag3D(GraphObj *par):GraphObj(par, 0L)
+{
+}
+
+Drag3D::~Drag3D()
+{
+}
+
+void *
+Drag3D::ObjThere(int x, int y)
+{
+	return 0L;
+}
+
+void
+Drag3D::DoPlot(anyOutput *o)
+{
+}
+
+FrmRect::FrmRect(GraphObj *par, fRECT *lim, fRECT *c, fRECT *chld):GraphObj(par, 0L)
+{
+	Id = Id;
+}
+
+FrmRect::~FrmRect()
+{
+}
+
+double
+FrmRect::GetSize(int select)
+{
+	return 0.0;
+}
+
+bool
+FrmRect::SetSize(int select, double value)
+{
+	return false;
+}
+
+bool
+FrmRect::SetColor(int select, DWORD col)
+{
+	return false;
+}
+
+void
+FrmRect::DoMark(anyOutput *o, bool mark)
+{
+}
+
+void
+FrmRect::DoPlot(anyOutput *o)
+{
+}
+
+bool
+FrmRect::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	return false;
+}
+
+void *
+FrmRect::ObjThere(int x, int y)
+{
+	return 0L;
+}
+
+void
+FrmRect::Track(POINT *p, anyOutput *o)
+{
+}
+
+bool ShowLayers(GraphObj *root)
+{
+	return false;
+}
+
+bool GetBitmapRes(double *res, double *width, double *height, char *header)
+{
+	return false;
+}
+
+bool GetPaper(double *w, double *h)
+{
+	*w = *h = 1.0;
+	return true;
+}
+
+bool Symbol::PropertyDlg()
+{
+	return false;
+}
+
+bool Bubble::PropertyDlg()
+{
+	return false;
+}
+
+bool Bar::PropertyDlg()
+{
+	return false;
+}
+
+bool DataLine::PropertyDlg()
+{
+	return false;
+}
+
+bool DataPolygon::PropertyDlg()
+{
+	return false;
+}
+
+bool RegLine::PropertyDlg()
+{
+	return false;
+}
+
+bool SDellipse::PropertyDlg()
+{
+	return false;
+}
+
+bool ErrorBar::PropertyDlg()
+{
+	return false;
+}
+
+bool Arrow::PropertyDlg()
+{
+	return false;
+}
+
+void *
+Arrow::ObjThere(int x, int y)
+{
+	return 0L;
+}
+
+void
+Arrow::Track(POINT *p, anyOutput *o)
+{
+}
+
+bool Box::PropertyDlg()
+{
+	return false;
+}
+
+bool Whisker::PropertyDlg()
+{
+	return false;
+}
+
+bool DropLine::PropertyDlg()
+{
+	return false;
+}
+
+bool Sphere::PropertyDlg()
+{
+	return false;
+}
+
+bool Plane3D::PropertyDlg()
+{
+	return false;
+}
+
+bool Brick::PropertyDlg()
+{
+	return false;
+}
+
+bool DropLine3D::PropertyDlg()
+{
+	return false;
+}
+
+bool Arrow3D::PropertyDlg()
+{
+	return false;
+}
+
+bool Line3D::PropertyDlg()
+{
+	return false;
+}
+
+bool Label::PropertyDlg()
+{
+	return false;
+}
+
+void Label::ShowCursor(anyOutput *o)
+{
+}
+
+bool Label::AddChar(int ci, anyOutput *o)
+{
+	return true;
+}
+
+void Label::CalcCursorPos(int x, int y, anyOutput *o)
+{
+}
+
+bool TextFrame::PropertyDlg()
+{
+	return false;
+}
+
+void TextFrame::ShowCursor(anyOutput *o)
+{
+}
+
+void TextFrame::CalcCursorPos(int x, int y, anyOutput *o)
+{
+}
+
+bool segment::PropertyDlg()
+{
+	return false;
+}
+
+bool polyline::PropertyDlg()
+{
+	return false;
+}
+
+bool polygon::PropertyDlg()
+{
+	return false;
+}
+
+bool rectangle::PropertyDlg()
+{
+	return false;
+}
+
+bool PlotScatt::PropertyDlg()
+{
+	return false;
+}
+
+bool xyStat::PropertyDlg()
+{
+	return false;
+}
+
+bool Regression::PropertyDlg()
+{
+	return false;
+}
+
+bool FreqDist::PropertyDlg()
+{
+	return false;
+}
+
+bool BubblePlot::PropertyDlg()
+{
+	return false;
+}
+
+bool PolarPlot::PropertyDlg()
+{
+	return false;
+}
+
+bool PolarPlot::Config()
+{
+	return false;
+}
+
+bool BoxPlot::PropertyDlg()
+{
+	return false;
+}
+
+bool DensDisp::PropertyDlg()
+{
+	return false;
+}
+
+bool StackBar::PropertyDlg()
+{
+	return false;
+}
+
+bool Waterfall::PropertyDlg()
+{
+	return false;
+}
+
+bool MultiLines::PropertyDlg()
+{
+	return false;
+}
+
+bool PieChart::PropertyDlg()
+{
+	return false;
+}
+
+bool StarChart::PropertyDlg()
+{
+	return false;
+}
+
+bool Function::PropertyDlg()
+{
+	return false;
+}
+
+bool Grid3D::PropertyDlg()
+{
+	return false;
+}
+
+bool Grid3D::Configure()
+{
+	return false;
+}
+
+bool Scatt3D::PropertyDlg()
+{
+	return false;
+}
+
+bool FitFunc::PropertyDlg()
+{
+	return false;
+}
+
+bool NormQuant::PropertyDlg()
+{
+	return false;
+}
+
+bool ContourPlot::PropertyDlg()
+{
+	return false;
+}
+
+bool Plot3D::AddAxis()
+{
+	return false;
+}
+
+bool Plot3D::PropertyDlg()
+{
+	return false;
+}
+
+bool Plot3D::AddPlot(int)
+{
+	return false;
+}
+
+bool Chart25D::PropertyDlg()
+{
+	return false;
+}
+
+bool Ribbon25D::PropertyDlg()
+{
+	return false;
+}
+
+bool Func3D::PropertyDlg()
+{
+	return false;
+}
+
+bool FitFunc3D::PropertyDlg()
+{
+	return false;
+}
+
+bool BubblePlot3D::PropertyDlg()
+{
+	return false;
+}
+
+bool GridLine::PropertyDlg()
+{
+	return false;
+}
+
+bool Tick::PropertyDlg()
+{
+	return false;
+}
+
+void
+Axis::DoMark(anyOutput *o, bool mark)
+{
+}
+
+bool Axis::PropertyDlg()
+{
+	return false;
+}
+
+bool Graph::AddPlot(int family)
+{
+	return false;
+}
+
+bool Graph::PropertyDlg()
+{
+	return false;
+}
+
+bool Graph::Configure()
+{
+	return false;
+}
+
+bool Graph::AddAxis()
+{
+	return false;
+}
+
+bool Graph::ExecTool(MouseEvent *mev)
+{
+	return false;
+}
+
+bool Graph::MoveObj(int cmd, GraphObj *g)
+{
+	return false;
+}
+
+bool Graph::DoZoom(char *z)
+{
+	return false;
+}
+
+bool Page::Configure()
+{
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// some more STUBS .....
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+char *SaveGraphAsName(char *oldname)
+{
+	return name2;
+}
+
+char *OpenGraphName(char *oldname)
+{
+	return 0L;
+}
+
+void HideTextCursor()
+{
+	return;
+}
+
+void HideTextCursorObj(anyOutput *out)
+{
+	return;
+}
+
+void ShowTextCursor(anyOutput *out, RECT *disp, DWORD color)
+{
+	return;
+}
+
+void InvalidateOutput(anyOutput *o)
+{
+	return;
+}
+
+void SuspendAnimation(anyOutput *o, bool bSusp)
+{
+	return;
+}
+
+anyOutput *NewDispClass(GraphObj *g)
+{
+	return 0L;
+}
+
+bool DelDispClass(anyOutput *w)
+{
+	return false;
+}
+
+anyOutput *NewBitmapClass(int w, int h, double hr, double vr)
+{
+	return 0L;
+}
+
+bool DelBitmapClass(anyOutput *w)
+{
+	return false;
+}
+
diff --git a/reports.cpp b/reports.cpp
index dcb9388..c522ea6 100755
--- a/reports.cpp
+++ b/reports.cpp
@@ -1,3078 +1,3245 @@
-//reports.cpp, Copyright (c) 2006, 2007 R.Lackner
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-// Create statistical reports
-//
-
-#include "rlplot.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <math.h>
-#include <ctype.h>
-#include "TheDialog.h"
-
-extern char TmpTxt[];
-extern Default defs;
-extern GraphObj *LastOpenGO;
-
-#define _PREC 1.0e-12
-
-//prototypes: WinSpec.cpp
-void *CreateDlgWnd(char *title, int x, int y, int width, int height, tag_DlgObj *d, DWORD flags);
-
-static int curr_id, cbSymLineStr;
-static fRECT dBounds;
-static TextDEF txtdef1, txtdef2;
-static double linsp1, linsp2;
-static char SymLineStr[40];
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// init report variables
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static void rep_init()
-{
-	curr_id = 1;		defs.cUnits = defs.dUnits;
-	txtdef1.ColTxt = txtdef2.ColTxt = 0x0L;
-	txtdef1.ColBg = txtdef2.ColBg = 0x00ffffffL;
-	txtdef1.fSize = defs.GetSize(SIZE_TEXT);
-	txtdef2.fSize = txtdef1.fSize *1.2;
-	txtdef1.RotBL = txtdef2.RotBL = 0.0;
-	txtdef1.RotCHAR = txtdef2.RotCHAR = 0.0;
-	txtdef1.iSize = txtdef2.iSize = 0;
-	txtdef1.Align = txtdef2.Align = TXA_HLEFT | TXA_VTOP;
-	txtdef1.Mode = txtdef2.Mode = TXM_TRANSPARENT;
-	txtdef1.Style = txtdef2.Style = TXS_NORMAL;
-	txtdef1.Font = txtdef2.Font = FONT_HELVETICA;
-	txtdef1.text = txtdef2.text = 0L;
-#ifdef _WINDOWS
-	linsp1 = txtdef1.fSize*1.2;	linsp2 = txtdef1.fSize*1.5;
-#else
-	linsp1 = txtdef1.fSize*1.7;	linsp2 = txtdef1.fSize*2.5;
-#endif
-#ifdef USE_WIN_SECURE
-	cbSymLineStr = sprintf_s(SymLineStr, 40, "Line= %g 1 0x0 0x0\n", defs.GetSize(SIZE_SYM_LINE)); 
-#else
-	cbSymLineStr = sprintf(SymLineStr, "Line= %g 1 0x0 0x0\n", defs.GetSize(SIZE_SYM_LINE)); 
-#endif
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create a text label for a report
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char* mk_label(double x, double y, bool moveable, int align, TextDEF *td, char*text)
-{
-	int csize, pos = 0;
-	char *res;
-
-	if(!(res = (char*)malloc(csize = 1000)))return 0L;
-	res[pos++] = '\n';				res[pos++] = '[';
-	add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
-	add_to_buff(&res, &pos, &csize, "=Label]\nPos=", 12);
-	add_dbl_to_buff(&res, &pos, &csize, x, true);
-	add_dbl_to_buff(&res, &pos, &csize, y, true);
-	res[pos++] = '\n';
-	if(moveable) add_to_buff(&res, &pos, &csize, "moveable= 1\n", 12);
-	add_to_buff(&res, &pos, &csize, "TxtDef= 0x0 0x00ffffff", 22);
-	add_dbl_to_buff(&res, &pos, &csize, td->fSize, true);
-	add_dbl_to_buff(&res, &pos, &csize, td->RotBL, true);
-	add_dbl_to_buff(&res, &pos, &csize, td->RotCHAR, true);
-	add_int_to_buff(&res, &pos, &csize, align, true, 0);
-	add_to_buff(&res, &pos, &csize, " 1 0 0 \"", 8);
-	add_to_buff(&res, &pos, &csize, text, 0);
-	add_to_buff(&res, &pos, &csize, "\"\n", 2);
-	return res;
-}
-static void rep_DrawText(GraphObj *parent, double x, double y, bool moveable, int align, TextDEF *td, char*text)
-{
-	char *txt_obj;
-
-	if(txt_obj = mk_label(x, y,	moveable, align, td, text)) {
-		OpenGraph(parent, 0L, (unsigned char*)txt_obj, false);
-		free(txt_obj);
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// draw a rectangle
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char* mk_rect(double x1, double y1, double x2, double y2, DWORD lcol, DWORD fcol)
-{
-	int csize, pos = 0;
-	char *res;
-
-	if(!(res = (char*)malloc(csize = 1000)))return 0L;
-	res[pos++] = '\n';				res[pos++] = '[';
-	add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
-	add_to_buff(&res, &pos, &csize, "=rectangle]\np1=", 0);
-	add_dbl_to_buff(&res, &pos, &csize, x1, true);
-	add_dbl_to_buff(&res, &pos, &csize, y1, true);
-	add_to_buff(&res, &pos, &csize, "\np2=", 0);
-	add_dbl_to_buff(&res, &pos, &csize, x2, true);
-	add_dbl_to_buff(&res, &pos, &csize, y2, true);
-	add_to_buff(&res, &pos, &csize, "\nLine= 0 1", 0);
-	add_hex_to_buff(&res, &pos, &csize, lcol, true);
-	add_to_buff(&res, &pos, &csize, " 0x0\nFillLine= 0 1 0x0 0x0\nFill= 0", 0);
-	add_hex_to_buff(&res, &pos, &csize, fcol, true);
-	add_to_buff(&res, &pos, &csize, " 1 0x0 0x00ffffff\n", 0);
-	return res;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// print values to string
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static int dbl_to_str1(char *dest, int size, char* fmt, double val)
-{
-#ifdef USE_WIN_SECURE
-	return sprintf_s(dest, size, fmt, val);
-#else
-	return sprintf(dest, fmt, val);
-#endif
-}
-
-static int dbl_to_str2(char *dest, int size, char* fmt, double val1, double val2)
-{
-#ifdef USE_WIN_SECURE
-	return sprintf_s(dest, size, fmt, val1, val2);
-#else
-	return sprintf(dest, fmt, val1, val2);
-#endif
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create general information on report page
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static void mk_header(Page *page, char* desc, DataObj *data)
-{
-	time_t ti = time(0L);
-	char label[80];
-	double rpos;
-	int cb;
-
-	if(!page) return;
-	rpos = page->GetSize(SIZE_GRECT_RIGHT) - txtdef1.fSize*5.0;
-	rep_DrawText(page, txtdef1.fSize*5.0, page->GetSize(SIZE_GRECT_TOP)+txtdef2.fSize*6.0,
-		false, TXA_HLEFT, &txtdef2, desc); 
-#ifdef USE_WIN_SECURE
-	ctime_s(label, 32, &ti);
-#else
-	rlp_strcpy(label, 25, ctime(&ti));
-#endif
-	label[24] = 0;
-	rep_DrawText(page, rpos, page->GetSize(SIZE_GRECT_TOP)+txtdef1.fSize*5.0,
-		false, TXA_HRIGHT, &txtdef1, label);
-	cb = rlp_strcpy(label, 80, "RLPlot ");		cb += rlp_strcpy(label+cb, 80-cb, SZ_VERSION);
-	rep_DrawText(page, rpos, page->GetSize(SIZE_GRECT_BOTTOM)-txtdef1.fSize*6.0,
-		false, TXA_HRIGHT, &txtdef1, label);
-	if(data && data->Command(CMD_GETFILENAME, TmpTxt, 0L)) {
-		rpos = page->GetSize(SIZE_GRECT_LEFT) + txtdef1.fSize*5.0;
-		rep_DrawText(page, rpos, page->GetSize(SIZE_GRECT_BOTTOM)-txtdef1.fSize*6.0,
-			false, TXA_HLEFT, &txtdef1, TmpTxt);
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create horizontal ruler
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static void mk_hr(GraphObj *parent, double x1, double x2, double y)
-{
-	int csize, pos = 0;
-	char *res;
-
-	if(!(res = (char*)malloc(csize = 100)))return;
-	res[pos++] = '\n';				res[pos++] = '[';
-	add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
-	add_to_buff(&res, &pos, &csize, "=polyline]\nData= (2){", 21);
-	add_dbl_to_buff(&res, &pos, &csize, x1, false);
-	add_dbl_to_buff(&res, &pos, &csize, y, true);
-	add_dbl_to_buff(&res, &pos, &csize, x2, true);
-	add_dbl_to_buff(&res, &pos, &csize, y, true);
-	add_to_buff(&res, &pos, &csize, "}\nLine=", 7);
-	add_dbl_to_buff(&res, &pos, &csize, txtdef1.fSize/20.0, true);
-	add_dbl_to_buff(&res, &pos, &csize, txtdef1.fSize, true);
-	add_to_buff(&res, &pos, &csize, " 0x0 0x0\n", 9);
-	OpenGraph(parent, 0L, (unsigned char*)res, false);
-	free(res);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create a means report
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static double mk_mean_report(GraphObj *parent, double x, double y, double *da, int n, double ci, char *name)
-{
-	static char *mean_fmts[] = {"Mean = %g", "Std.Dev. = %g", "N = %g", "Std.Err. = %g", 0L,
-		"Kurtosis = %g", "Skewness = %g"};
-	char desc[80];
-	int i, cb;
-	double v, t, res[10];
-
-	if(name && name[0]) {
-		cb = rlp_strcpy(desc, 40, "<b>");			cb += rlp_strcpy(desc+cb, 40-cb, name);
-		cb += rlp_strcpy(desc+cb, 40-cb, ":</b>");
-		rep_DrawText(parent, x, y, false, TXA_HLEFT, &txtdef1, desc);
-		y += linsp1;		x += (txtdef1.fSize*3.0);
-		}
-	cb = dbl_to_str1(desc, 80, "%g%%%% C.I. = %%g", ci*100.0);
-	mean_fmts[4] = (char*)malloc(cb+2);
-	rlp_strcpy(mean_fmts[4], cb+1, desc);			t = distinv(t_dist, n-1, 1, 1.0-ci, 2.0);
-	v = d_variance(n, da, &res[0], 0L);				res[2] = (double)n;
-	res[1] = sqrt(v);								res[3] = res[1] / sqrt(res[2]);
-	res[4] = res[3] *t;								res[5] = d_kurt(n, da);
-	res[6] = d_skew(n, da);
-	for(i = 0; i < 7; i++) {
-		dbl_to_str1(desc, 80, mean_fmts[i], res[i]);
-		rep_DrawText(parent, x, y, false, TXA_HLEFT, &txtdef1, desc);
-		y += (i==2 ? linsp1/0.9 : linsp1/1.2);
-		}
-	free(mean_fmts[4]);								mean_fmts[4] = 0L;
-	return y;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create a median report
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static double mk_median_report(GraphObj *parent, double x, double y, double *da, int n, double ci, char *name)
-{
-	static char *mean_fmts[] = {"Median = %g", "25%% = %g", "75%% = %g", "N = %g", "Min. = %g", "Max. = %g" };
-	char desc[80];
-	int i, cb;
-	double res[6];
-
-	if(!da || !parent || !n) return y;
-	if(name && name[0]) {
-		cb = rlp_strcpy(desc, 40, "<b>");			cb += rlp_strcpy(desc+cb, 40-cb, name);
-		cb += rlp_strcpy(desc+cb, 40-cb, ":</b>");
-		rep_DrawText(parent, x, y, false, TXA_HLEFT, &txtdef1, desc);
-		y += linsp1; x += (txtdef1.fSize*3.0);
-		}
-	d_quartile(n, da, &res[1], &res[0], &res[2]);
-	res[4] = res[5] = *da;
-	for(i = 1; i < n; i++) {
-		if(da[i] > res[5]) res[5] = da[i];			if(da[i] < res[4]) res[4] = da[i];
-		}
-	res[3] = (double)n;
-	for(i = 0; i < 6; i++) {
-		dbl_to_str1(desc, 80, mean_fmts[i], res[i]);
-		rep_DrawText(parent, x, y, false, TXA_HLEFT, &txtdef1, desc);
-		y += linsp1/1.2;
-		}
-	return y;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create report table for anova ...
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static double mk_table(GraphObj *parent, double x, double y, int type, double **dda)
-{
-	char *cheaders[] = {"<i>df</i>", "<i>SS</i>", "<i>MS</i>", "<i>F</i>", "<i>P</i>"};
-	char *rheaders1[] = {"Source of variation", type == 2 ? (char*)"Explained by regression":
-		(char*)"Among groups", type == 2 ? (char*)"Unexplained":(char*)"Within groups", "Total"};
-	char *rheaders2[] = {"Source of variation", "Between rows", "Between columns", "Interaction",
-		"Within subgroups (error)", "Total"};
-	char *rheaders3[] = {"Source of variation", "Between columns", "Between rows", "Error", "Total"};
-	char *cfmt[8], **rheaders;
-	int i, j, nl, nc[8];
-	double posc[8], cinc;
-
-#ifdef _WINDOWS
-	cinc = txtdef1.fSize;
-#else
-	cinc = txtdef1.fSize *1.3;
-#endif
-	cfmt[0] = "%.0lf";		cfmt[3] = "%0.3lf";			cfmt[4] = "%0.4lf";
-	switch(type) {
-	case 1:	case 2:
-		rheaders = rheaders1;
-		nl = 3;	nc[0] = 5;	nc[1] = 3;	nc[2] = 2;
-		posc[0] = x + cinc*14.0;		posc[1] = posc[0] + cinc*5.0;
-		posc[2] = posc[1] + cinc*6.0;	posc[3] = posc[2] + cinc*6.0;
-		posc[4] = posc[3] + cinc*6.0;
-		cfmt[1] = GetNumFormat(floor(log10(dda[2][1])-3.0));	
-		cfmt[2] = GetNumFormat(floor(log10(dda[0][2]+dda[1][2])-3.0));
-		break;
-	case 3:
-		rheaders = rheaders2;
-		nl = 5;	nc[0] = nc[1] = nc[2] = 5;	nc[3] = 3;	nc[4] = 2;
-		posc[0] = x + cinc*14.0;		posc[1] = posc[0] + cinc*5.0;
-		posc[2] = posc[1] + cinc*6.0;	posc[3] = posc[2] + cinc*6.0;
-		posc[4] = posc[3] + cinc*6.0;
-		cfmt[1] = GetNumFormat(floor(log10(dda[2][1])-3.0));	
-		cfmt[2] = GetNumFormat(floor(log10(dda[0][2]+dda[0][1])-3.0));
-		break;
-	case 4:
-		rheaders = rheaders3;
-		nl = 4;	nc[0] = nc[1] = 5;	nc[2] = 3;	nc[3] = 2;
-		posc[0] = x + cinc*14.0;		posc[1] = posc[0] + cinc*5.0;
-		posc[2] = posc[1] + cinc*6.0;	posc[3] = posc[2] + cinc*6.0;
-		posc[4] = posc[3] + cinc*6.0;
-		cfmt[1] = GetNumFormat(floor(log10(dda[3][1])-4.0));	
-		cfmt[2] = GetNumFormat(floor(log10(dda[0][2]+dda[1][2]+dda[2][2])-4.0));
-		break;
-	default: return y;
-		}
-	if(type == 1 || type == 2 || type == 3 || type == 4) {
-		rep_DrawText(parent, x, y, false, TXA_HLEFT, &txtdef1, rheaders[0]);
-		for(i = 0; i < 5; i++) {
-			rep_DrawText(parent, posc[i], y, false, TXA_HRIGHT, &txtdef1, cheaders[i]);
-			if(i) posc[i] += linsp1;
-			}
-		mk_hr(parent, x, posc[4], y + linsp1);			y += linsp2;
-		}
-	for(i = 0; i < nl; i++) {
-		rep_DrawText(parent, x, y, false, TXA_HLEFT, &txtdef1, rheaders[i+1]);
-		for(j = 0; j < nc[i]; j++) {
-			if(j == 4 && dda[i][j] > 0.0 && dda[i][j] < 0.0001) rlp_strcpy(TmpTxt, 10, "< 0.0001");
-#ifdef USE_WIN_SECURE
-			else sprintf_s(TmpTxt, 20, cfmt[j], dda[i][j]);
-#else
-			else sprintf(TmpTxt, cfmt[j], dda[i][j]);
-#endif
-			rep_DrawText(parent, posc[j], y, false, TXA_HRIGHT, &txtdef1, TmpTxt);
-			}
-		if(i < (nl-2)) y += linsp1;
-		else {
-			mk_hr(parent, x, posc[4], y + linsp1);		y += linsp2;
-			}
-		}
-	return y;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create a boxplot for a report
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char* mk_boxplot(int style, double *x, double *y, double *by1, double *by2, double *wy1, double *wy2, int *ny, int n, 
-	char *s_nam, char *b_nam, char *w_nam)
-{
-	int i, csize, pos, first_s, first_b, first_w, first_l;
-	char *res;
-	double size;
-
-	if(!(res = (char*)malloc(csize = 2000)))return 0L;
-	if(n < 20) size = defs.GetSize(SIZE_SYMBOL);
-	else size = defs.GetSize(SIZE_SYMBOL)/2.0 + 20.0 * defs.GetSize(SIZE_SYMBOL)/(2.0 * n);
-	first_b = curr_id;
-	for(i = pos = 0; i < n && res; i++) {
-		add_to_buff(&res, &pos, &csize, "\n[", 2);		add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
-		add_to_buff(&res, &pos, &csize, "=Box]\nType= 256\nHigh=", 21);
-		if(style == 1) {
-			add_dbl_to_buff(&res, &pos, &csize, by2[i], true);
-			add_dbl_to_buff(&res, &pos, &csize, y[i], true);
-			add_to_buff(&res, &pos, &csize,"\nLow=", 5);
-			add_dbl_to_buff(&res, &pos, &csize, by1[i], true);
-			add_dbl_to_buff(&res, &pos, &csize,y[i], true);
-			}
-		else {
-			add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
-			add_dbl_to_buff(&res, &pos, &csize, by2[i], true);
-			add_to_buff(&res, &pos, &csize,"\nLow=", 5);
-			add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
-			add_dbl_to_buff(&res, &pos, &csize, by1[i], true);
-			}
-		add_to_buff(&res, &pos, &csize,"\nSize= 60\nName= \"", 17);
-		add_to_buff(&res, &pos, &csize, b_nam, 0);
-		add_to_buff(&res, &pos, &csize, "\"\n", 2);
-		}
-	first_w = curr_id;
-	for(i = 0; i < n && res; i++) {
-		add_to_buff(&res, &pos, &csize, "\n[", 2);		add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
-		add_to_buff(&res, &pos, &csize, "=Whisker]\nHigh=", 15);
-		if(style == 1) {
-			add_dbl_to_buff(&res, &pos, &csize, wy2[i], true);
-			add_dbl_to_buff(&res, &pos, &csize, y[i], true);
-			add_to_buff(&res, &pos, &csize,"\nLow=", 5);
-			add_dbl_to_buff(&res, &pos, &csize, wy1[i], true);
-			add_dbl_to_buff(&res, &pos, &csize,y[i], true);
-			}
-		else {
-			add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
-			add_dbl_to_buff(&res, &pos, &csize, wy2[i], true);
-			add_to_buff(&res, &pos, &csize,"\nLow=", 5);
-			add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
-			add_dbl_to_buff(&res, &pos, &csize, wy1[i], true);
-			}
-		add_to_buff(&res, &pos, &csize, "\nDesc= \"", 8);
-		add_to_buff(&res, &pos, &csize, w_nam, 0);
-		add_to_buff(&res, &pos, &csize, "\"\n", 2);
-		}
-	first_s = curr_id;
-	for(i = 0; i < n && res; i++) {
-		add_to_buff(&res, &pos, &csize, "\n[", 2);		add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
-		add_to_buff(&res, &pos, &csize, "=Symbol]\nType= 10\nPos=", 22);
-		add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
-		add_dbl_to_buff(&res, &pos, &csize, y[i], true);
-		add_to_buff(&res, &pos, &csize, "\nSize=", 6);	add_dbl_to_buff(&res, &pos, &csize, size, true);
-		add_to_buff(&res, &pos, &csize, "\n", 1);
-		add_to_buff(&res, &pos, &csize, SymLineStr, cbSymLineStr);
-		add_to_buff(&res, &pos, &csize, "FillCol= 0x00ffffff\n", 20);
-		if(s_nam) {
-			add_to_buff(&res, &pos, &csize, "Name=\"", 6);
-			add_to_buff(&res, &pos, &csize, s_nam, 0);	add_to_buff(&res, &pos, &csize, "\"\n", 2);
-			}
-		}
-	first_l = curr_id;
-	for(i = 0; i < n && res; i++) {
-		add_to_buff(&res, &pos, &csize, "\n[", 2);
-		add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
-		add_to_buff(&res, &pos, &csize, "=Label]\nPos=", 12);
-		if(style == 1) {
-			add_dbl_to_buff(&res, &pos, &csize, wy2[i], true);
-			add_dbl_to_buff(&res, &pos, &csize, y[i], true);
-			add_to_buff(&res, &pos, &csize, "\nDist=", 6);
-			add_dbl_to_buff(&res, &pos, &csize, txtdef1.fSize/2.0, true);
-			add_to_buff(&res, &pos, &csize, " 0", 2);
-			}
-		else {
-			add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
-			add_dbl_to_buff(&res, &pos, &csize, wy2[i], true);
-			add_to_buff(&res, &pos, &csize, "\nDist= 0", 8);
-			add_dbl_to_buff(&res, &pos, &csize, -txtdef1.fSize/4.0, true);
-			}
-		add_to_buff(&res, &pos, &csize, "\nFlags= 0x00000011\nTxtDef= 0x00000000 0x00ffffff", 48);
-		add_dbl_to_buff(&res, &pos, &csize, txtdef1.fSize, true);
-		add_dbl_to_buff(&res, &pos, &csize, txtdef1.RotBL, true);
-		add_dbl_to_buff(&res, &pos, &csize, txtdef1.RotCHAR, true);
-		add_int_to_buff(&res, &pos, &csize, style == 1 ? (TXA_HLEFT | TXA_VCENTER):(TXA_HCENTER | TXA_VBOTTOM), true, 0);
-		add_to_buff(&res, &pos, &csize, " 1 0 0 \"", 8);
-		if(n < 7) add_to_buff(&res, &pos, &csize, "n = ", 4);
-		add_int_to_buff(&res, &pos, &csize, ny[i], false, 0);
-		add_to_buff(&res, &pos, &csize, "\"\n", 2);
-		}
-	add_to_buff(&res,&pos,&csize, "\n[", 2);				add_int_to_buff(&res,&pos,&csize, curr_id++, false, 0);
-	add_to_buff(&res, &pos, &csize, "=BoxPlot]\nBounds=", 17);
-	add_dbl_to_buff(&res,&pos,&csize, dBounds.Xmin, true);	add_dbl_to_buff(&res,&pos,&csize, dBounds.Ymax, true);
-	add_dbl_to_buff(&res,&pos,&csize, dBounds.Xmax, true);	add_dbl_to_buff(&res,&pos,&csize, dBounds.Ymin, true);
-
-	add_to_buff(&res,&pos,&csize, "\nBoxes=(", 0);			add_int_to_buff(&res,&pos,&csize, n, false, 0);
-	add_to_buff(&res,&pos,&csize, "){", 2);
-	for(i = 0; i < n; i++, first_b++) {
-		add_int_to_buff(&res,&pos,&csize, first_b, false, 0);	add_to_buff(&res,&pos,&csize, ",", 1);
-		if(i && (i%16)== 0 && first_b < (curr_id-2)) add_to_buff(&res, &pos, &csize, "\n   ", 4);
-		}
-	while(res[pos-1] == ',' || res[pos-1] < 33) pos --;		add_to_buff(&res, &pos, &csize, "}", 2);
-	add_to_buff(&res,&pos,&csize, "\nWhiskers=(", 0);		add_int_to_buff(&res,&pos,&csize, n, false, 0);
-	add_to_buff(&res,&pos,&csize, "){", 2);
-	for(i = 0; i < n; i++, first_w++) {
-		add_int_to_buff(&res,&pos,&csize, first_w, false, 0);	add_to_buff(&res,&pos,&csize, ",", 1);
-		if(i && (i%16)== 0 && first_b < (curr_id-2)) add_to_buff(&res, &pos, &csize, "\n   ", 4);
-		}
-	while(res[pos-1] == ',' || res[pos-1] < 33) pos --;		add_to_buff(&res, &pos, &csize, "}", 2);
-	add_to_buff(&res,&pos,&csize, "\nSymbols=(", 10);		add_int_to_buff(&res,&pos,&csize, n, false, 0);
-	add_to_buff(&res,&pos,&csize, "){", 2);
-	for(i = 0; i < n; i++, first_s++) {
-		add_int_to_buff(&res,&pos,&csize, first_s, false, 0);	add_to_buff(&res,&pos,&csize, ",", 1);
-		if(i && (i%16)== 0 && first_s < (curr_id-2)) add_to_buff(&res, &pos, &csize, "\n   ", 4);
-		}
-	while(res[pos-1] == ',' || res[pos-1] < 33) pos --;		add_to_buff(&res, &pos, &csize, "}", 2);
-	add_to_buff(&res,&pos,&csize, "\nLabels=(", 9);			add_int_to_buff(&res,&pos,&csize, n, false, 0);
-	add_to_buff(&res,&pos,&csize, "){", 2);
-	for(i = 0; i < n; i++, first_l++) {
-		add_int_to_buff(&res,&pos,&csize, first_l, false, 0);	add_to_buff(&res,&pos,&csize, ",", 1);
-		if(i && (i%16)== 0 && first_s < (curr_id-2)) add_to_buff(&res, &pos, &csize, "\n   ", 4);
-		}
-	while(res[pos-1] == ',' || res[pos-1] < 33) pos --;		add_to_buff(&res, &pos, &csize, "}\n", 2);
-	return res;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create a scatterplot for a report
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char* mk_scatt(int style, double *x, double *y, double *ss, int *ny, int n, char *s_nam, char *x_desc, char *y_desc)
-{
-	int i, csize, pos, first;
-	char *res;
-	double size, linew, tmp, val;
-
-	if(!(res = (char*)malloc(csize = 2000)))return 0L;
-	if(n < 20) size = defs.GetSize(SIZE_SYMBOL);
-	else size = defs.GetSize(SIZE_SYMBOL)/2.0 + 20.0 * defs.GetSize(SIZE_SYMBOL)/(2.0 * n);
-	linew = defs.GetSize(SIZE_SYM_LINE);
-	first = curr_id;
-	for(i = pos = 0; i < n && res; i++) {
-		add_to_buff(&res, &pos, &csize, "\n[", 2);		add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
-		add_to_buff(&res, &pos, &csize, "=Symbol]\nPos=", 13);
-		add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
-		add_dbl_to_buff(&res, &pos, &csize, y[i], true);
-		add_to_buff(&res, &pos, &csize, "\nSize=", 6);	add_dbl_to_buff(&res, &pos, &csize, size, true);
-		add_to_buff(&res, &pos, &csize, "\n", 1);
-		add_to_buff(&res, &pos, &csize, SymLineStr, cbSymLineStr);
-		add_to_buff(&res, &pos, &csize, "FillCol= 0x00ffffff\n", 20);
-		}
-	if(ss && ny) {
-		for(i = 0; i < n && res; i++) {
-			if(ny[i] > 1) tmp = sqrt(ss[i]/(ny[i]-1));
-			else tmp = 0.0;
-			add_to_buff(&res, &pos, &csize, "\n[", 2);
-			add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
-			add_to_buff(&res, &pos, &csize, "=ErrorBar]\nType=", 16);
-			add_int_to_buff(&res, &pos, &csize, style & 0x10 ? 3 : 0, true, 0);
-			add_to_buff(&res, &pos, &csize, "\nPos=", 5);
-			add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
-			add_dbl_to_buff(&res, &pos, &csize, y[i], true);
-			add_to_buff(&res, &pos, &csize, "\nErr=", 5);
-			add_dbl_to_buff(&res, &pos, &csize, tmp, true);
-			add_to_buff(&res, &pos, &csize, "\nDesc= \"Std. Dev.\"\n", 19);
-			}
-		for(i = 0; i < n && res; i++) {
-			if(ny[i] > 1) tmp = sqrt(ss[i]/(ny[i]-1));
-			else tmp = 0.0;
-			add_to_buff(&res, &pos, &csize, "\n[", 2);
-			add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
-			add_to_buff(&res, &pos, &csize, "=Label]\nPos=", 12);
-			if(style & 0x10) {
-				val = x ? x[i] : (double)(i+1);
-				if(dBounds.Xmin > (val-tmp)) dBounds.Xmin = val-tmp;
-				if(dBounds.Xmax < (val+tmp)) dBounds.Xmax = val+tmp;
-				add_dbl_to_buff(&res, &pos, &csize, val+tmp, true);
-				add_dbl_to_buff(&res, &pos, &csize, y[i], true);
-				add_to_buff(&res, &pos, &csize, "\nDist=", 6);
-				add_dbl_to_buff(&res, &pos, &csize, txtdef1.fSize/2.0, true);
-				add_to_buff(&res, &pos, &csize, " 0", 2);
-				}
-			else {
-				if(dBounds.Ymin > (y[i]-tmp)) dBounds.Ymin = y[i]-tmp;
-				if(dBounds.Ymax < (y[i]+tmp)) dBounds.Ymax = y[i]+tmp;
-				add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : ((double)(i+1)), true);
-				add_dbl_to_buff(&res, &pos, &csize, y[i] +tmp, true);
-				add_to_buff(&res, &pos, &csize, "\nDist= 0", 8);
-				add_dbl_to_buff(&res, &pos, &csize, -txtdef1.fSize/4.0, true);
-				}
-			add_to_buff(&res, &pos, &csize, "\nFlags= 0x00000011\nTxtDef= 0x00000000 0x00ffffff", 48);
-			add_dbl_to_buff(&res, &pos, &csize, txtdef1.fSize, true);
-			add_dbl_to_buff(&res, &pos, &csize, txtdef1.RotBL, true);
-			add_dbl_to_buff(&res, &pos, &csize, txtdef1.RotCHAR, true);
-			add_int_to_buff(&res, &pos, &csize, (style & 0x10)?(TXA_HLEFT | TXA_VCENTER) : (TXA_HCENTER | TXA_VBOTTOM), true, 0);
-			add_to_buff(&res, &pos, &csize, " 1 0 0 \"", 8);
-			if(n < 7) add_to_buff(&res, &pos, &csize, "n = ", 4);
-			add_int_to_buff(&res, &pos, &csize, ny[i], false, 0);
-			add_to_buff(&res, &pos, &csize, "\"\n", 2);
-			}
-		}
-	add_to_buff(&res,&pos,&csize, "\n[", 2);				add_int_to_buff(&res,&pos,&csize, curr_id++, false, 0);
-	add_to_buff(&res, &pos, &csize, "=PlotScatt]\nBounds=", 19);
-	add_dbl_to_buff(&res,&pos,&csize, dBounds.Xmin, true);	add_dbl_to_buff(&res,&pos,&csize, dBounds.Ymax, true);
-	add_dbl_to_buff(&res,&pos,&csize, dBounds.Xmax, true);	add_dbl_to_buff(&res,&pos,&csize, dBounds.Ymin, true);
-	add_to_buff(&res,&pos,&csize, "\nSymbols=(", 10);		add_int_to_buff(&res,&pos,&csize, n, false, 0);
-	add_to_buff(&res,&pos,&csize, "){", 2);
-	for(i = 0; i < n; i++, first++) {
-		add_int_to_buff(&res,&pos,&csize, first, false,0);	add_to_buff(&res,&pos,&csize, ",", 1);
-		if(i && (i%16)== 0 && first < (curr_id-2)) add_to_buff(&res, &pos, &csize, "\n   ", 4);
-		}
-	while(res[pos-1] == ',' || res[pos-1] < 33) pos --;		add_to_buff(&res, &pos, &csize, "}\n", 2);
-	if(ss && ny) {
-		add_to_buff(&res,&pos,&csize, "ErrBars=(", 9);		add_int_to_buff(&res,&pos,&csize, n, false, 0);
-		add_to_buff(&res,&pos,&csize, "){", 2);
-		for(i = 0; i < n; i++, first++) {
-			add_int_to_buff(&res,&pos,&csize, first,false,0);	add_to_buff(&res,&pos,&csize, ",", 1);
-			if(i && (i%16)== 0 && first < (curr_id-2)) add_to_buff(&res, &pos, &csize, "\n   ", 4);
-			}
-		while(res[pos-1] == ',' || res[pos-1] < 33) pos --;	add_to_buff(&res, &pos, &csize, "}\n", 2);
-		add_to_buff(&res,&pos,&csize, "Labels=(", 8);		add_int_to_buff(&res,&pos,&csize, n, false, 0);
-		add_to_buff(&res,&pos,&csize, "){", 2);
-		for(i = 0; i < n; i++, first++) {
-			add_int_to_buff(&res,&pos,&csize, first,false,0);	add_to_buff(&res,&pos,&csize, ",", 1);
-			if(i && (i%16)== 0 && first < (curr_id-2)) add_to_buff(&res, &pos, &csize, "\n   ", 4);
-			}
-		while(res[pos-1] == ',' || res[pos-1] < 33) pos --;	add_to_buff(&res, &pos, &csize, "}\n", 2);
-		}
-	if(x_desc && x_desc[0]){
-		add_to_buff(&res,&pos,&csize, "x_info= \"", 9);		add_to_buff(&res,&pos,&csize, x_desc, 0);
-		add_to_buff(&res,&pos,&csize, "\"\n", 2);
-		}
-	if(y_desc && y_desc[0]){
-		add_to_buff(&res,&pos,&csize, "y_info= \"", 9);		add_to_buff(&res,&pos,&csize, y_desc, 0);
-		add_to_buff(&res,&pos,&csize, "\"\n", 2);
-		}
-	if(s_nam && s_nam[0]) {
-		add_to_buff(&res, &pos, &csize, "DataDesc=\"", 10);
-		add_to_buff(&res, &pos, &csize, s_nam, 0);			add_to_buff(&res, &pos, &csize, "\"\n", 2);
-		}
-	return res;
-}
-
-static double contrasts_level = 95.0;
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create a contrasts report for one way anova
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static void mk_contrasts(GraphObj* par, int type, double dx, double dy, double *y, double *ss, int *ny, int n, 
-	char **names, double ci, double msw, double msdf)
-{
-	double tmp, tkd, pcorr, cx[10], *raw;
-	int i, j, k, l, c, df, *co, nco, cb;
-	char ctext[5], **contrasts;
-	char *headings[] = {"<i>Groups</i>", "<i>Mean</i>", "<i>Std. Dev.</i>", "<i>N</i>",
-		"<i>Contrasts</i><sup>1)</sup>"};
-
-	if(!par || !y || !ss || !ny || n < 2) return;
-	cx[0] = txtdef1.fSize*5.0;		cx[1] = cx[0] + linsp1*5.0;
-	cx[2] = cx[1] + linsp1*5.0;		cx[3] = cx[2] + linsp1*5.0;
-	cx[4] = cx[3] + linsp1*4.0;		cx[5] = cx[4] + linsp1*3.0;
-	cx[6] = cx[5] + linsp1*4.0;
-
-	rep_DrawText(par, dx, dy, false, TXA_HLEFT, &txtdef1, "<b>Summary:</b>");
-	for(i = 0, dy += linsp2; i < 5; i++) {				//column headers
-		c = (i == 4) ? TXA_HLEFT : TXA_HRIGHT;
-		rep_DrawText(par, cx[i+1], dy, false, c, &txtdef1, headings[i]);
-		}
-	mk_hr(par, cx[0], cx[6]+txtdef1.fSize*2.0, dy +linsp1);
-	if(type == 1 || type == 2) {	
-		if(!(co = (int*)malloc(n*sizeof(int)))) return;
-		if(!(contrasts = (char**)malloc(n*sizeof(char*)))) return;
-		rlp_strcpy(ctext, 5, ", a");
-		for(i = df = 0, nco = n; i < n; i++) {
-			if(ny[i] > 0) df += (ny[i]-1);
-			co[i] = i;
-			contrasts[i] = (char*)calloc(50, sizeof(char));
-			}
-		tkd = qtukey(1.0-ci, 1.0, (double) n, (double)df, 1, 0);
-		for(i = 0; nco; ) {
-			for(j = 0; j < n; j++) {
-				switch(type) {
-					case 1:					//Tukey-Kramer
-						tmp = tkd * sqrt((msw*(1.0/((double)ny[j]) + 1.0/((double)ny[co[i]])))/2.0);
-						break;
-					case 2:					//Tukey's HSD
-						tmp = tkd * sqrt(msw/(ny[j] <= ny[co[i]] ? ny[j] : ny[co[i]]));
-						break;
-					}
-				if(fabs(y[j]-y[co[i]]) < tmp) {
-					cb = (int)strlen(contrasts[j]);
-					rlp_strcpy((contrasts[j])+cb, 50-cb, ctext);
-					}
-				}
-			for(j = nco = 0; j < n; j++) {
-				if(!(contrasts[j][0])) co[nco++] = j;
-				}
-			ctext[2]++;
-			}
-		}
-	else if(type == 10) {
-		if(!(co = (int*)malloc(n*sizeof(int)))) return;
-		if(!(contrasts = (char**)malloc(n*sizeof(char*)))) return;
-		if(!(raw = (double*)malloc((n*n-1)*sizeof(double))))return;
-		rlp_strcpy(ctext, 5, ", a");
-		for(i = df = 0, nco = n; i < n; i++) {
-			if(ny[i] > 0) df += (ny[i]-1);
-			co[i] = i;
-			contrasts[i] = (char*)calloc(50, sizeof(char));
-			}
-		for(i = k = 0; i < (n-1); i++) for(j = i+1; j < n; j++) {
-			raw[k++] = t_dist(fabs(0.5*(y[i]-y[j])/sqrt(msw/(ny[i]+ny[j]))), msdf, 0.0);
-			}
-		SortArray(k, raw);
-		for(i = 0; nco; ) {
-			for(j = 0; j < n; j++) {
-				tmp = t_dist(fabs(0.5*(y[j]-y[co[i]])/sqrt(msw/(ny[j]+ny[co[i]]))), msdf, 0.0);
-				for(l = 0; l < k && tmp > raw[l]; l++);
-				switch(type) {
-				case 10:					//Dunn Sidak
-					pcorr = 1.0 - pow((1.0 - ci), 1.0 /(double(k-l)));
-					break;
-					}
-				if(tmp > pcorr || j == co[i]) {
-					cb = (int)strlen(contrasts[j]);
-					rlp_strcpy((contrasts[j])+cb, 50-cb, ctext);
-					}
-				}
-			for(j = nco = 0; j < n; j++) {
-				if(!(contrasts[j][0])) co[nco++] = j;
-				}
-			ctext[2]++;
-			}
-		free(raw);
-		}
-	else return;
-
-	for(i = 0, dy += linsp2; i < n; i++, dy +=linsp1) {
-		if(ny[i] > 1) tmp = sqrt(ss[i]/(ny[i]-1));
-		else tmp = 0.0;
-		rep_DrawText(par, cx[1], dy, false, TXA_HRIGHT, &txtdef1, names[i]);
-		dbl_to_str1(TmpTxt, 20, "%g", y[i]);
-		rep_DrawText(par, cx[2], dy, false, TXA_HRIGHT, &txtdef1, TmpTxt);
-		if(tmp > 0.0) {
-			dbl_to_str1(TmpTxt, 20, "%g", tmp);
-			rep_DrawText(par, cx[3], dy, false, TXA_HRIGHT, &txtdef1, TmpTxt);
-			}
-		if(ny[i] >1) {
-			dbl_to_str1(TmpTxt, 20, "%.0lf", (double)ny[i]);
-			rep_DrawText(par, cx[4], dy, false, TXA_HRIGHT, &txtdef1, TmpTxt);
-			}
-		rep_DrawText(par, cx[5], dy, false, TXA_HLEFT, &txtdef1, contrasts[i]+2);
-		}
-	mk_hr(par, cx[0], cx[6]+txtdef1.fSize*2.0, dy+txtdef1.fSize*0.2);
-	cb = dbl_to_str1(TmpTxt, 200, "<sup>1)</sup> Groups not sharing the same letter are different "
-		"on the %g%% level ", (1.0-ci)*100.0);
-	switch (type) {
-	case 1:						//Tukey-Kramer
-		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE - cb, "(Tukey-Kramer method)");
-		break;
-	case 2:						//Tukey's HSD
-		rep_DrawText(par, cx[0]+ txtdef1.fSize*2.0, dy + txtdef2.fSize + linsp1, false, 
-			TXA_HLEFT, &txtdef1, "(Tukey's honest significant difference)");
-		break;
-	case 10:					//Dunn-Sidak
-		rep_DrawText(par, cx[0]+ txtdef1.fSize*2.0, dy + txtdef2.fSize + linsp1, false, 
-			TXA_HLEFT, &txtdef1, "(sequential Dunn-Sidak method)");
-		break;
-		}
-	rep_DrawText(par, cx[0], dy += txtdef2.fSize , false, TXA_HLEFT, &txtdef1, TmpTxt);
-	for(i = 0; i < n; i++) free(contrasts[i]);
-	free(co);		free(contrasts);
-	return;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// create a homogeneity of variances report for one way anova
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static void mk_v_homogeneity(GraphObj* par, DataObj *data, double *dx, double *dy, double *y, double *ss,
-	int *ny, int n, double **vals)
-{
-	int i;
-	double tmp, *sd, f1, f2, p1, p2;
-	char *txt_obj;
-	scaleINFO scale = {{0.0, 0.8}, {0.0, 0.8}, {0.0, 0.8}};
-	Graph *graph;
-
-	if(!par || !y || !ss || !ny || n < 2) return;
-	if(!(sd = (double*)malloc(n*sizeof(double)))) return;
-	rep_DrawText(par, *dx, *dy, false, TXA_HLEFT, &txtdef1, "<b>Homogeneity of Variances:</b>");
-	for(i = 0; i < n; i++) {
-		if(ny[i] > 1) sd[i] = sqrt(ss[i]/(ny[i]-1));
-		else sd[i] = 0.0;
-		if(i) {
-			if(dBounds.Xmax < y[i]) dBounds.Xmax = y[i];
-			if(dBounds.Xmin > y[i]) dBounds.Xmin = y[i];
-			if(dBounds.Ymax < sd[i]) dBounds.Ymax = sd[i];
-			if(dBounds.Ymin > sd[i]) dBounds.Ymin = sd[i];
-			}
-		else {
-			dBounds.Xmax = dBounds.Xmin = y[0];
-			dBounds.Ymax = dBounds.Ymin = sd[0];
-			}
-		}
-	if((graph = new Graph(par, data, 0L, 0)) && (txt_obj = mk_scatt(0, y, sd, 0L, ny, n, 
-		"Variables", "Means", "Std.Dev."))){
-		graph->GRect.Xmax = defs.GetSize(SIZE_GRECT_BOTTOM)*0.8;
-		graph->GRect.Ymax *= 0.8;
-		graph->DRect.Xmin *= 0.8;				graph->DRect.Ymax *= 0.8;				
-		graph->DRect.Xmax = graph->GRect.Xmax - (txtdef1.fSize*2.0);
-		scale.sx.fx = par->GetSize(SIZE_GRECT_RIGHT) - txtdef1.fSize*5.0 - graph->GRect.Xmax*0.8 + graph->GRect.Xmin*0.8;		
-		scale.sy.fx = *dy;
-		OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
-		free(txt_obj);
-		graph->Command(CMD_SCALE, &scale, 0L);
-		if(!(par->Command(CMD_DROP_GRAPH, graph, 0L))) delete graph;
-		else graph->moveable = 0;
-		}
-	if(bartlett(n, ny, ss, &tmp)) {
-		rep_DrawText(par, *dx + txtdef1.fSize*2.0, *dy += (linsp2*1.5), false, TXA_HLEFT, &txtdef1, "Bartlett's test:");
-		i = dbl_to_str1(TmpTxt, TMP_TXT_SIZE, "Chi<sup>2</sup> = %.2lf, ", tmp);
-		tmp = chi_dist(tmp, n-1, 0);
-		dbl_to_str1(TmpTxt+i, TMP_TXT_SIZE-i, tmp < 0.0001 ? (char*)"P < 0.0001" : (char*)"P = %.4lf", tmp);
-		rep_DrawText(par, *dx + txtdef1.fSize*3.0, *dy += linsp1, false, TXA_HLEFT, &txtdef1, TmpTxt);
-		}
-	if(levene(1, n, ny, y, vals, &f1, &p1) && levene(2, n, ny, y, vals, &f2, &p2) ) {
-		rep_DrawText(par, *dx + txtdef1.fSize*2.0, *dy += (linsp2*1.5), false, TXA_HLEFT, &txtdef1, "Levene's test:");
-		i = dbl_to_str1(TmpTxt, TMP_TXT_SIZE, "using means: F = %.2lf, ", f1);
-		dbl_to_str1(TmpTxt+i, TMP_TXT_SIZE-i, tmp < 0.0001 ? (char*)"P < 0.0001" : (char*)"P = %.4lf", p1);
-		rep_DrawText(par, *dx + txtdef1.fSize*3.0, *dy += linsp1, false, TXA_HLEFT, &txtdef1, TmpTxt);
-		i = dbl_to_str1(TmpTxt, TMP_TXT_SIZE, "using medians: F = %.2lf, ", f2);
-		dbl_to_str1(TmpTxt+i, TMP_TXT_SIZE-i, tmp < 0.0001 ? (char*)"P < 0.0001" : (char*)"P = %.4lf", p2);
-		rep_DrawText(par, *dx + txtdef1.fSize*3.0, *dy += linsp1, false, TXA_HLEFT, &txtdef1, TmpTxt);
-		}
-	free(sd);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// one way anova
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *AnovaDlg_Tmpl =
-	"1,+,,DEFAULT,PUSHBUTTON,-1,158,10,45,12\n"
-	".,.,,,PUSHBUTTON,-2,158,25,45,12\n"
-	".,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"10,+,152,ISPARENT | CHECKED,SHEET,1,5,10,140,90\n"
-	".,20,100,ISPARENT,SHEET,2,5,10,140,90\n"
-	"20,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-	"100,104,,ISRADIO,CHECKBOX,8,20,25,100,9\n"
-	"104,+,,,LTEXT,4,15,37,100,9\n"
-	".,.,,ISRADIO,CHECKBOX,5,20,47,100,9\n"
-	".,.,,ISRADIO,CHECKBOX,9,20,57,100,9\n"
-	".,110,,ISRADIO,CHECKBOX,10,20,67,100,9\n"
-	"110,+,,,LTEXT,7,20,85,55,9\n"
-	".,.,,,EDVAL1,6,80,85,25,10\n"
-	".,,,,LTEXT,-10,107,85,10,9\n"
-	"152,+,,ISPARENT | CHECKED,GROUPBOX,3,12,30,128,65\n"
-	".,.,,,LTEXT,0,25,45,60,8\n"
-	".,.,,,RANGEINPUT,0,25,55,100,10\n"
-	".,.,0,,PUSHBUTTON,-8,95,70,30,12\n"
-	".,,,LASTOBJ,PUSHBUTTON,-9,60,70,35,12";
-
-void rep_anova(GraphObj *parent, DataObj *data)
-{
-	TabSHEET tab1 = {0, 45, 10, "Anova Input"};
-	TabSHEET tab2 = {45, 75, 10, "Tests"};
-	DlgInfo *AnovaDlg;
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)" select one range for every variable ",
-		(void*)"Contrasts:", (void*)" Tukey-Kramer method", (void*)&contrasts_level, (void*)"significance level:",
-		(void*)" Homogeneity of Variances", (void*)" Tukey's honest sig. difference", (void*)" Dunn-Sidak"};
-	DlgRoot *Dlg;
-	void *hDlg;
-	double **cols = 0L, *csums=0L, mtot, *css=0L, cx, cy;
-	double **res_tab = 0L, ci;
-	int i, j, n, c, r, res, nc, ntot, currYR = 0, maxYR=0, ny, *ncols = 0L;;
-	bool bContinue = false, updateYR = true;
-	anyResult ares;
-	AccRange *rD =0L;
-	char **rd = 0L, **names, *txt_obj;
-	Graph *graph;
-	Page *page;
-
-	if(!parent || !data) return;
-	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
-		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return;
-	if(!(AnovaDlg = CompileDialog(AnovaDlg_Tmpl, dyndata))) return;
-	if(TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
-		for(i=j=0; i <= 1000; i +=100) if(TmpTxt[i]) 
-			rd[j++] = (char*)memdup(TmpTxt+i, ((int)strlen(TmpTxt+i))+2, 0);	 maxYR = j-1;
-		}
-	if(!rd && !(rd = (char**)calloc(1, sizeof(char*))))return;
-	if(!(Dlg = new DlgRoot(AnovaDlg, data)))return;
-	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
-	hDlg = CreateDlgWnd("Single-Classification Anova", 50, 50, 420, 240, Dlg, 0x0L);
-	do {
-		if(updateYR) {
-			if(currYR >0) Dlg->ShowItem(156, true);
-			else Dlg->ShowItem(156, false);
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "variable # %d/%d", currYR+1, maxYR+1);
-#else
-			sprintf(TmpTxt,"variable # %d/%d", currYR+1, maxYR+1);
-#endif
-			Dlg->SetText(153, TmpTxt);
-			updateYR = false;
-			}
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 155:		case 156:
-			res = com_StackDlg(res, Dlg, 0L, 0L, &rd, &currYR,
-				&rD, &bContinue, &ny, &maxYR, &updateYR);
-			break;
-			}
-		}while (res < 0);
-	if(res == 1 && (res_tab = (double**)calloc(3, sizeof(double*)))
-		&& (res_tab[0] = (double*) malloc(5*sizeof(double)))
-		&& (res_tab[1] = (double*) malloc(5*sizeof(double)))
-		&& (res_tab[2] = (double*) malloc(5*sizeof(double)))
-		&& (cols = (double**)calloc(maxYR+1, sizeof(double*)))
-		&& (names = (char**)calloc(maxYR+1, sizeof(char*)))
-		&& (ncols = (int*)calloc(maxYR+1, sizeof(int)))) {
-		rep_init();		if(rD) delete rD;		rD = 0L;
-		if(Dlg->GetValue(111, &ci)) {
-			contrasts_level = ci;		ci = 1.0-(ci/100.0);
-			}
-		dBounds.Ymin = HUGE_VAL;		dBounds.Ymax = -HUGE_VAL;
-		// get data into two dimensional array
-		for(nc = maxYR+1, i = ntot = 0, mtot = 0.0; i < nc; i++) {
-			if((rD = new AccRange(rd[i])) && (n = rD->CountItems()) && (cols[i] = (double*)malloc(n*sizeof(double)))) {
-				names[i] = rD->RangeDesc(data, 1);
-				for(n = 0, rD->GetFirst(&c, &r); rD->GetNext(&c, &r); ) {
-					if(data->GetResult(&ares, r, c, false) && ares.type == ET_VALUE) {
-						if(ares.value < dBounds.Ymin) dBounds.Ymin = ares.value;
-						if(ares.value > dBounds.Ymax) dBounds.Ymax = ares.value;
-						cols[i][n++] = ares.value;
-						}
-					}
-				ncols[i] = n;		ntot += n;		//mtot += csums[i];
-				delete(rD);			rD = 0L;
-				}
-			if(!names[i] && (names[i] = (char*)malloc(20*sizeof(char)))){
-#ifdef USE_WIN_SECURE
-				sprintf_s(names[i], 20, "Group %d", i+1);
-#else
-				sprintf(names[i], "Group %d", i+1);
-#endif
-				}
-			}
-		// check for unique names
-		for(i = 0; i < (nc-1); i++) for(j = i+1; j < nc; j++) {
-			if(!strcmp(names[i], names[j])) {
-				names[i] = (char*) realloc(names[i], 20 *sizeof(char));
-				names[j] = (char*) realloc(names[j], 20 *sizeof(char));
-#ifdef USE_WIN_SECURE
-				sprintf_s(names[i], 20, "Group %d", i+1);	sprintf_s(names[j], 20, "Group %d", j+1);
-#else
-				sprintf(names[i], "Group %d", i+1);			sprintf(names[j], "Group %d", j+1);
-#endif
-				}
-			}
-
-		if(do_anova1(nc, ncols, cols, res_tab, &mtot, &csums, &css)){
-			dBounds.Xmin = 0.5;				dBounds.Xmax = ((double)nc)+0.5;
-			page = new Page(parent, data);
-			mk_header(page, "<b>Single-Classification ANOVA</b>", data);
-			if((graph = new Graph(parent, data, 0L, 0)) && (txt_obj = mk_scatt(0, 0L, csums, css, ncols, nc, "Mean", "Groups", "Means <u>+</u> S.D."))){
-				OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
-				if(LastOpenGO && LastOpenGO->Id == GO_PLOTSCATT) {
-					if(((PlotScatt*)LastOpenGO)->x_tv = new TextValue()){
-						for(i = 0; i < nc; i++) ((PlotScatt*)LastOpenGO)->x_tv->GetValue(names[i]);
-						}
-					}
-				free(txt_obj);								graph->moveable = 0;
-				graph->GRect.Xmin += (txtdef1.fSize*5.0);	graph->GRect.Xmax += (txtdef1.fSize*5.0);
-				graph->GRect.Ymin += (txtdef1.fSize*10.0);	graph->GRect.Ymax += (txtdef1.fSize*10.0);
-				page->Command(CMD_DROP_GRAPH, graph, 0L);
-				}
-			cx = graph->GRect.Xmin;		cy = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef2.fSize*2.0;
-			rep_DrawText(page, cx, cy, false, TXA_HLEFT, &txtdef1, "<b>Anova:</b>");
-			cy = mk_table(page, cx, cy+txtdef2.fSize, 1, res_tab)+txtdef2.fSize;
-			if(Dlg->GetCheck(100)) mk_v_homogeneity(page, data, &cx, &cy, csums, css, ncols, nc, cols);
-			else if(Dlg->GetCheck(105)) mk_contrasts(page, 1, cx, cy, csums, css, ncols, nc, names, ci, res_tab[1][2], res_tab[1][0]);
-			else if(Dlg->GetCheck(106)) mk_contrasts(page, 2, cx, cy, csums, css, ncols, nc, names, ci, res_tab[1][2], res_tab[1][0]);
-			else if(Dlg->GetCheck(107)) mk_contrasts(page, 10, cx, cy, csums, css, ncols, nc, names, ci, res_tab[1][2], res_tab[1][0]);
-			if(ntot > (nc<<1) && nc >1 && parent->Command(CMD_DROP_GRAPH, page, 0L));
-			else {
-				delete page;
-				InfoBox("No or insufficient\ndata for ANOVA\n");
-				}
-			}
-		for(i = 0; i < nc; i++){
-			if(cols[i]) free(cols[i]);		if(names[i]) free(names[i]);
-			}
-		for(i = 0; i < 3; i++) if(res_tab[i]) free(res_tab[i]);
-		free(cols);			free(ncols);			free(names);
-		free(res_tab);		if(css)free(css);		if(csums)free(csums);
-		}
-	if(rD) delete rD;		CloseDlgWnd(hDlg);
-	delete Dlg;				free(AnovaDlg);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Parametric two way anova
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *TwAnov_DlgTmpl = 
-	"1,2,,DEFAULT,PUSHBUTTON,-1,148,10,45,12\n"
-	"2,3,,,PUSHBUTTON,-2,148,25,45,12\n"
-	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"4,10,100,ISPARENT | CHECKED,SHEET,1,5,10,130,80\n"
-	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-	"100,+,,,LTEXT,2,10,30,60,8\n"
-	".,.,,,RANGEINPUT,-15,20,40,100,10\n"
-	".,,,LASTOBJ,CHECKBOX,3,20,60,100,9";
-
-void rep_twanova(GraphObj *parent, DataObj *data)
-{
-	TabSHEET tab1 = {0, 40, 10, "Input Data"};
-	DlgInfo *TwAnovDlg;
-	void *dyndata[] = {(void*)&tab1, (void*)"rectangular range for variables",
-		(void*)" column/row headers present"};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, hc, hr, mr, mc, nr, nc, c, r, res, *nvr=0L, *nvc=0L;
-	bool bContinue = false;
-	char *mrk, *txt_obj, **cnames = 0L, **rnames = 0L;
-	double gm, ssc, ssr, sse, tmp, cx, cy, dmin, dmax;
-	double **vals = 0L, *cs = 0L, *rs = 0L, **res_tab = 0L, *c_ss, *r_ss, *c_m, *r_m, *abc;
-	RECT rec;
-	scaleINFO scale = {{0.0, 0.7}, {0.0, 0.7}, {0.0, 0.7}};
-	AccRange *rD =0L, *rDesc;
-	anyResult ares;
-	Graph *graph;
-	Page *page;
-
-	if(!parent || !data) return;
-	if(!(TwAnovDlg = CompileDialog(TwAnov_DlgTmpl, dyndata))) return;
-	if(data->Command(CMD_GETMARK, &mrk, 0L))rlp_strcpy(TmpTxt, TMP_TXT_SIZE, mrk);
-	else {
-		data->ValueRec(&rec);
-		rlp_strcpy(TmpTxt, 100, mkRangeRef(rec.top, rec.left, rec.bottom, rec.right));
-		}
-	if(!(Dlg = new DlgRoot(TwAnovDlg, data)))return;
-	hDlg = CreateDlgWnd("Two-Way Anova", 50, 50, 420, 220, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 1:
-			if(Dlg->GetText(101, TmpTxt+200, TMP_TXT_SIZE-200) &&(rD = new AccRange(TmpTxt+200))&& rD->BoundRec(&rec)
-				&& (vals = (double**)calloc(rec.bottom - rec.top +1, sizeof(double*)))) {
-				nc = rec.right-rec.left+1;				nr = rec.bottom-rec.top+1;
-				nvc = (int*)calloc(nc, sizeof(int));	nvr = (int*)calloc(nr, sizeof(int));
-				dmin = HUGE_VAL;						dmax = -HUGE_VAL;
-				if(Dlg->GetCheck(102)) hr = hc = 1;
-				else hr = hc = 0;
-				for(i = rec.top+hr; i <= rec.bottom; i++) vals[i-rec.top] = (double*)calloc(nc, sizeof(double));
-				for(c = rec.left+hc; c <= rec.right; c++) for(r = rec.top; r <= rec.bottom; r++) {
-					if(data->GetResult(&ares, r, c,false) && ares.type == ET_VALUE){
-						nvc[c-rec.left]++;				nvr[r-rec.top]++;
-						if(vals[r-rec.top]) vals[r-rec.top][c-rec.left] = ares.value;
-						if(ares.value > dmax) dmax = ares.value;
-						if(ares.value < dmin) dmin = ares.value;
-						}
-					}
-				while(!nvc[nc-1] && nc > 1) nc--;
-				while(!nvr[nr-1] && nr > 1) nr--;
-				for(i = 1, mr = nvr[0]; i < nr; i++)if(nvr[i] > mr) mr = nvr[i];
-				for(i = 1, mc = nvc[0]; i < nc; i++)if(nvc[i] > mc) mc = nvc[i];
-				for( ; nvr[hr] < mr && hr < nr; hr++);
-				for( ; nvc[hc] < mc && hc < nc; hc++);
-				for(i = hr; i < nr; i++) if(nvr[i] < mr) res = -1;
-				for(i = hc; i < nc; i++) if(nvc[i] < mc) res = -1;
-				for(i = 0, mr = nc-hc; i < nr; i++) nvr[i] = mr;
-				for(i = 0, mc = nr-hr; i < nc; i++) nvc[i] = mc;
-				if(res < 0 || mr < 2 || mc < 2) {
-					InfoBox("There are missing data!");
-					for(i = 0; i < nc; i++) if(vals[i]) free(vals[i]);
-					free(vals);			free(nvr);			free(nvc);
-					nvr = nvc = 0L;		vals = 0L;
-					bContinue = true;
-					}
-				delete rD;
-				}
-			break;
-		default:
-			nr = nc = 0;		break;
-			}
-		}while (res < 0);
-	if(res == 1 && (vals) && (cs = (double*)calloc(nc, sizeof(double))) 
-		&& (rs = (double*)calloc(nr, sizeof(double)))
-		&& (c_ss = (double*)calloc(nc, sizeof(double)))
-		&& (r_ss = (double*)calloc(nr, sizeof(double)))
-		&& (c_m = (double*)calloc(nc, sizeof(double)))
-		&& (r_m = (double*)calloc(nr, sizeof(double)))
-		&& (abc = (double*)calloc(nr > nc ? nr : nc, sizeof(double)))
-		&& (cnames = (char**)calloc(rec.right-rec.left+1, sizeof(char*)))
-		&& (rnames = (char**)calloc(rec.bottom-rec.top+1, sizeof(char*)))
-		&& (res_tab = (double**)calloc(4, sizeof(double*)))
-		&& (res_tab[0] = (double*)calloc(5, sizeof(double)))
-		&& (res_tab[1] = (double*)calloc(5, sizeof(double)))
-		&& (res_tab[2] = (double*)calloc(5, sizeof(double)))
-		&& (res_tab[3] = (double*)calloc(5, sizeof(double)))){
-		//get column and row descriptors
-		for(c = hc; c < nc; c++) {
-			if(rDesc = new AccRange(mkRangeRef(rec.top, rec.left+c, rec.bottom, rec.left+c))) {
-				cnames[c-hc] = rDesc->RangeDesc(data, hr ? 4 : 1);
-				delete rDesc;
-				}
-			}
-		for(r = hr; r < nr; r++) {
-			if(rDesc = new AccRange(mkRangeRef(rec.top+r, rec.left, rec.top+r, rec.right))) {
-				rnames[r-hr] = rDesc->RangeDesc(data, hc ? 4 : 1);
-				delete rDesc;
-				}
-			}
-		//grand mean
-		for(c = hc, gm = 0.0; c < nc; c++) for(r = hr; r < nr; r++) {
-			gm += vals[r][c];	cs[c] += vals[r][c];	rs[r] += vals[r][c];
-			}
-		gm /= ((double)((nc-hc)*(nr-hr)));
-		//anova stats
-		for(c = hc; c < nc; c++) cs[c] /= ((double)nvc[c]);
-		for(c = hc, ssc = 0.0; c < nc; c++) ssc += ((tmp = cs[c]-gm)*tmp); 
-		for(r = hr; r < nr; r++) rs[r] /= ((double)nvr[r]);
-		for(r = hr, ssr = 0.0; r < nr; r++) ssr += ((tmp = rs[r]-gm)*tmp);
-		ssc *= ((double)(nr-hr));		ssr *= ((double)(nc-hc));
-		for(c = hc, sse = 0.0; c < nc; c++) for(r = hr; r < nr; r++) {
-			sse += ((tmp = vals[r][c]-cs[c]-rs[r]+gm)*tmp);
-			}
-		for(c = hc; c < nc; c++) for(r = hr; r < nr; r++) {
-			c_m[c-hc] += vals[r][c];		r_m[r-hr] += vals[r][c];
-			}
-		for(c = hc; c < nc; c++) c_m[c-hc] /= ((double)(nvc[c]));
-		for(r = hr; r < nr; r++) r_m[r-hr] /= ((double)(nvr[r]));
-		for(c = hc; c < nc; c++) for(r = hr; r < nr; r++) {
-			c_ss[c-hc] += ((tmp = vals[r][c]-c_m[c-hc])*tmp);
-			r_ss[r-hr] += ((tmp = vals[r][c]-r_m[r-hr])*tmp);
-			}
-		//prepare table for report
-		res_tab[0][0] = (double)(nc-hc-1);			res_tab[1][0] = (double)(nr-hr-1);
-		res_tab[2][0] = res_tab[0][0] * res_tab[1][0];
-		res_tab[3][0] = res_tab[0][0] + res_tab[1][0] + res_tab[2][0];
-		res_tab[0][1] = ssc;						res_tab[0][2] = ssc/res_tab[0][0];
-		res_tab[1][1] = ssr;						res_tab[1][2] = ssr/res_tab[1][0];
-		res_tab[2][1] = sse;						res_tab[2][2] = sse/res_tab[2][0];
-		res_tab[3][1] = ssc + ssr + sse;
-		res_tab[0][3] = res_tab[0][2] / res_tab[2][2];
-		res_tab[1][3] = res_tab[1][2] / res_tab[2][2];
-		res_tab[0][4] = f_dist(res_tab[0][3], res_tab[0][0], res_tab[2][0]);
-		res_tab[1][4] = f_dist(res_tab[1][3], res_tab[1][0], res_tab[2][0]);
-		rep_init();
-		page = new Page(parent, data);
-		mk_header(page, "<b>Two-Way ANOVA</b>", data);
-		cx = txtdef1.fSize*5.0;		cy = txtdef1.fSize*10.0;
-		dBounds.Xmin = 0.5;			dBounds.Xmax = ((double)(nc-hc))+0.5;
-		dBounds.Ymin = dmin;		dBounds.Ymax = dmax;
-		//plot column results
-		if((graph = new Graph(parent, data, 0L, 0)) && (txt_obj = mk_scatt(0, 0L, c_m, c_ss, nvc+hc, nc-hc, "Mean", "Columns", "Means <u>+</u> S.D."))){
-			OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
-			if(LastOpenGO && LastOpenGO->Id == GO_PLOTSCATT) {
-				if(((PlotScatt*)LastOpenGO)->x_tv = new TextValue()){
-					for(i = 0; i < (nc-hc); i++) ((PlotScatt*)LastOpenGO)->x_tv->GetValue(cnames[i]);
-					}
-				}
-			free(txt_obj);								graph->moveable = 0;
-			graph->DRect.Xmax = graph->DRect.Xmin + graph->DRect.Ymax - graph->DRect.Ymin;
-			graph->GRect.Xmax = graph->GRect.Xmin + graph->GRect.Ymax - graph->GRect.Ymin;
-			scale.sx.fx = txtdef1.fSize*5.0;			scale.sy.fx = txtdef1.fSize*10.0;
-			graph->Command(CMD_SCALE, &scale, 0L);
-			page->Command(CMD_DROP_GRAPH, graph, 0L);
-			cx = graph->GetSize(SIZE_GRECT_RIGHT)+txtdef1.fSize;
-			cy = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef2.fSize*2.0;
-			}
-		dBounds.Ymin = 0.5;			dBounds.Ymax = ((double)(nr-hr))+0.5;
-		dBounds.Xmin = dmin;		dBounds.Xmax = dmax;
-		for(r = 0, tmp = 1.0; r < (nr-hr); r++, tmp += 1.0) abc[r] = tmp;
-		//plot row results
-		if((graph = new Graph(parent, data, 0L, 0x10)) && (txt_obj = mk_scatt(0x10, r_m, abc, r_ss, nvr+hr, nr-hr, "Mean", "Means <u>+</u> S.D.", "Rows"))){
-			OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
-			if(LastOpenGO && LastOpenGO->Id == GO_PLOTSCATT) {
-				if(((PlotScatt*)LastOpenGO)->y_tv = new TextValue()){
-					for(i = 0; i < nr; i++) ((PlotScatt*)LastOpenGO)->y_tv->GetValue(rnames[i]);
-					}
-				}
-			free(txt_obj);								graph->moveable = 0;
-			graph->DRect.Xmax = graph->DRect.Xmin + graph->DRect.Ymax - graph->DRect.Ymin;
-			graph->GRect.Xmax = graph->GRect.Xmin + graph->GRect.Ymax - graph->GRect.Ymin;
-			scale.sx.fx = cx;							scale.sy.fx = txtdef1.fSize*10.0;
-			graph->Command(CMD_SCALE, &scale, 0L);
-			page->Command(CMD_DROP_GRAPH, graph, 0L);
-			}
-		cx = txtdef1.fSize*5.0;
-		//draw anova table and clean up
-		rep_DrawText(page, cx, cy, false, TXA_HLEFT, &txtdef1, "<b>Anova:</b>");
-		cy = mk_table(page, cx, cy+txtdef2.fSize, 4, res_tab)+txtdef2.fSize;
-		if(!(parent->Command(CMD_DROP_GRAPH, page, 0L))) delete page;
-		free(c_ss);			free(r_ss);
-		free(c_m);			free(r_m);
-		}
-	if(vals) {
-		for(i = 0; i < nc; i++) if(vals[i]) free(vals[i]);
-		free(vals);
-		}
-	if(res_tab) {
-		for(i = 0; i < 4; i++) if(res_tab[i]) free(res_tab[i]);
-		free(res_tab);
-		}
-	if (cnames) {
-		for(c = 0; c < (rec.right-rec.left+1); c++) if(cnames[c]) free(cnames[c]);
-		free(cnames);
-		}
-	if (rnames) {
-		for(r = 0; r < (rec.bottom-rec.top+1); r++) if(rnames[r]) free(rnames[r]);
-		free(rnames);
-		}
-	if(nvr) free(nvr);		if(nvc) free(nvc);
-	CloseDlgWnd(hDlg);		delete Dlg;		free(TwAnovDlg);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Friedman's non-parametric two way anova
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void rep_fmanova(GraphObj *parent, DataObj *data)
-{
-	TabSHEET tab1 = {0, 40, 10, "Input Data"};
-	DlgInfo *FmAnovDlg;
-	void *dyndata[] = {(void*)&tab1, (void*)"rectangular range for variables",
-		(void*)" column/row headers present"};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, hc, hr, mr, mc, nr, nc, c, r, res, *nvr=0L, *nvc=0L;
-	bool bContinue = false;
-	char *mrk, *txt_obj, **cnames = 0L, **rnames = 0L;
-	double tmp, cx, cy, dmin, dmax, cchi2, rchi2, prob;
-	double **vals = 0L, **rows = 0L, **cols = 0L, *idx, *cs = 0L, *rs = 0L;
-	double *m, *b1, *b2, *w1, *w2, *trc;
-	RECT rec;
-	scaleINFO scale = {{0.0, 0.7}, {0.0, 0.7}, {0.0, 0.7}};
-	AccRange *rD =0L, *rDesc;
-	anyResult ares;
-	Graph *graph;
-	Page *page;
-
-	if(!parent || !data) return;
-	if(!(FmAnovDlg = CompileDialog(TwAnov_DlgTmpl, dyndata))) return;
-	if(data->Command(CMD_GETMARK, &mrk, 0L))rlp_strcpy(TmpTxt, TMP_TXT_SIZE, mrk);
-	else {
-		data->ValueRec(&rec);
-		rlp_strcpy(TmpTxt, 100, mkRangeRef(rec.top, rec.left, rec.bottom, rec.right));
-		}
-	if(!(Dlg = new DlgRoot(FmAnovDlg, data)))return;
-	hDlg = CreateDlgWnd("Friedman's Non-Parametric Two-Way Anova", 50, 50, 420, 220, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 1:
-			if(Dlg->GetText(101, TmpTxt+200, TMP_TXT_SIZE-200) &&(rD = new AccRange(TmpTxt+200))&& rD->BoundRec(&rec)
-				&& (vals = (double**)calloc(rec.bottom - rec.top +1, sizeof(double*)))) {
-				nc = rec.right-rec.left+1;				nr = rec.bottom-rec.top+1;
-				nvc = (int*)calloc(nc, sizeof(int));	nvr = (int*)calloc(nr, sizeof(int));
-				dmin = HUGE_VAL;						dmax = -HUGE_VAL;
-				if(Dlg->GetCheck(102)) hr = hc = 1;
-				else hr = hc = 0;
-				for(i = rec.top+hr; i <= rec.bottom; i++) vals[i-rec.top] = (double*)calloc(nc, sizeof(double));
-				for(c = rec.left+hc; c <= rec.right; c++) for(r = rec.top; r <= rec.bottom; r++) {
-					if(data->GetResult(&ares, r, c,false) && ares.type == ET_VALUE){
-						nvc[c-rec.left]++;				nvr[r-rec.top]++;
-						if(vals[r-rec.top]) vals[r-rec.top][c-rec.left] = ares.value;
-						if(ares.value > dmax) dmax = ares.value;
-						if(ares.value < dmin) dmin = ares.value;
-						}
-					}
-				while(!nvc[nc-1] && nc > 1) nc--;
-				while(!nvr[nr-1] && nr > 1) nr--;
-				for(i = 1, mr = nvr[0]; i < nr; i++)if(nvr[i] > mr) mr = nvr[i];
-				for(i = 1, mc = nvc[0]; i < nc; i++)if(nvc[i] > mc) mc = nvc[i];
-				for( ; nvr[hr] < mr && hr < nr; hr++);
-				for( ; nvc[hc] < mc && hc < nc; hc++);
-				for(i = hr; i < nr; i++) if(nvr[i] < mr) res = -1;
-				for(i = hc; i < nc; i++) if(nvc[i] < mc) res = -1;
-				for(i = 0, mr = nc-hc; i < nr; i++) nvr[i] = mr;
-				for(i = 0, mc = nr-hr; i < nc; i++) nvc[i] = mc;
-				if(res < 0 || mr < 2 || mc < 2) {
-					InfoBox("There are missing data!");
-					for(i = 0; i < nc; i++) if(vals[i]) free(vals[i]);
-					free(vals);			free(nvr);			free(nvc);
-					nvr = nvc = 0L;		vals = 0L;
-					bContinue = true;
-					}
-				delete rD;
-				}
-			break;
-		default:
-			nr = nc = 0;		break;
-			}
-		}while (res < 0);
-	if(res == 1&& (vals) && (cs = (double*)calloc(nr, sizeof(double))) 
-		&& (rs = (double*)calloc(nc, sizeof(double)))
-		&& (cols = (double**)calloc(nc, sizeof(double*)))
-		&& (rows = (double**)calloc(nr, sizeof(double*)))
-		&& (cnames = (char**)calloc(rec.right-rec.left+1, sizeof(char*)))
-		&& (rnames = (char**)calloc(rec.bottom-rec.top+1, sizeof(char*)))
-		&& (idx = (double*)malloc((nr > nc ? nr:nc)*sizeof(double)))
-		&& (m = (double*)malloc((nr > nc ? nr:nc)*sizeof(double)))
-		&& (b1 = (double*)malloc((nr > nc ? nr:nc)*sizeof(double)))
-		&& (b2 = (double*)malloc((nr > nc ? nr:nc)*sizeof(double)))
-		&& (w1 = (double*)malloc((nr > nc ? nr:nc)*sizeof(double)))
-		&& (w2 = (double*)malloc((nr > nc ? nr:nc)*sizeof(double)))
-		&& (trc = (double*)malloc((nr > nc ? nr:nc)*sizeof(double)))) {
-		//get column and row descriptors
-		for(c = hc; c < nc; c++) {
-			if(rDesc = new AccRange(mkRangeRef(rec.top, rec.left+c, rec.bottom, rec.left+c))) {
-				cnames[c-hc] = rDesc->RangeDesc(data, hr ? 4 : 1);
-				delete rDesc;
-				}
-			}
-		for(r = hr; r < nr; r++) {
-			if(rDesc = new AccRange(mkRangeRef(rec.top+r, rec.left, rec.top+r, rec.right))) {
-				rnames[r-hr] = rDesc->RangeDesc(data, hc ? 4 : 1);
-				delete rDesc;
-				}
-			}
-		//create ranks
-		for(c = hc; c < nc; c++) if(cols[c-hc] = (double*)calloc(nr, sizeof(double))) {
-			for(r = hr; r < nr; r++) {
-				cols[c-hc][r-hr] = vals[r][c];		idx[r-hr] = (double)(r-hr);
-				}
-			SortArray2(nr-hr, cols[c-hc], idx);		crank(nr-hr, cols[c-hc], &tmp);
-			SortArray2(nr-hr, idx, cols[c-hc]);
-			}
-		for(r = hr; r < nr; r++) if(rows[r-hr] = (double*)calloc(nc, sizeof(double))) {
-			for(c = hc; c < nc; c++) {
-				rows[r-hr][c-hc] = vals[r][c];		idx[c-hc] = (double)(c-hc);
-				}
-			SortArray2(nc-hc, rows[r-hr], idx);		crank(nc-hc, rows[r-hr], &tmp);
-			SortArray2(nc-hc, idx, rows[r-hr]);	
-			}
-		for(r = 0; r < (nr-hr); r++) for(c = 0; c < (nc-hc); c++){
-			cs[r] += cols[c][r];	rs[c] += rows[r][c];
-			}
-		//rank sums and statistics
-		for(r = 0, cchi2 = 0.0; r < (nr-hr); r++) cchi2 += (cs[r]*cs[r]);
-		cchi2 = cchi2 * 12.0 / ((double)((nr-hr)*(nc-hc)*(nr-hr+1))) - 3.0*(nc-hc)*(nr-hr+1);
-		for(c = 0, rchi2 = 0.0; c < (nc-hc); c++) rchi2 += (rs[c]*rs[c]);
-		rchi2 = rchi2 * 12.0 / ((double)((nc-hc)*(nr-hr)*(nc-hc+1))) - 3.0*(nr-hr)*(nc-hc+1);
-		//create report page
-		rep_init();
-		page = new Page(parent, data);
-		mk_header(page, "<b>Friedman's non-parametric two-way ANOVA</b>", data);
-		cx = txtdef1.fSize*5.0;		cy = txtdef1.fSize*10.0;
-		//plot column results
-		for(c = hc; c < nc; c++) {
-			w1[c-hc] = HUGE_VAL;		w2[c-hc] = -HUGE_VAL;
-			for(r = hr; r < nr; r++) {
-				trc[r-hr] = vals[r][c];
-				if(vals[r][c] < w1[c-hc]) w1[c-hc] = vals[r][c];
-				if(vals[r][c] > w2[c-hc]) w2[c-hc] = vals[r][c];
-				}
-			d_quartile(r-hr, trc, b1+c-hc, m+c-hc, b2+c-hc);
-			}
-		dBounds.Xmin = 0.5;			dBounds.Xmax = ((double)(nc-hc))+0.5;
-		dBounds.Ymin = dmin;		dBounds.Ymax = dmax;
-		if((graph = new Graph(parent, data, 0L, 0)) && 
-			(txt_obj = mk_boxplot(0, 0L, m, b1, b2, w1, w2, nvc, nc-hc, "medians", "25-75%", "min/max"))){
-			OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
-			if(LastOpenGO && LastOpenGO->Id == GO_BOXPLOT) {
-				if(((BoxPlot*)LastOpenGO)->x_tv = new TextValue()){
-					for(i = 0; i < (nc-hc); i++) ((PlotScatt*)LastOpenGO)->x_tv->GetValue(cnames[i]);
-					}
-				if(((BoxPlot*)LastOpenGO)->x_info=(char*)malloc(20*sizeof(char)))
-					rlp_strcpy(((BoxPlot*)LastOpenGO)->x_info, 20, "Columns");
-				if(((BoxPlot*)LastOpenGO)->y_info=(char*)malloc(20*sizeof(char)))
-					rlp_strcpy(((BoxPlot*)LastOpenGO)->y_info, 20, "Location");
-				}
-			free(txt_obj);								graph->moveable = 0;
-			graph->DRect.Xmax = graph->DRect.Xmin + graph->DRect.Ymax - graph->DRect.Ymin;
-			graph->GRect.Xmax = graph->GRect.Xmin + graph->GRect.Ymax - graph->GRect.Ymin;
-			scale.sx.fx = txtdef1.fSize*5.0;			scale.sy.fx = txtdef1.fSize*10.0;
-			graph->Command(CMD_SCALE, &scale, 0L);
-			page->Command(CMD_DROP_GRAPH, graph, 0L);
-			cx = graph->GetSize(SIZE_GRECT_RIGHT)+txtdef1.fSize;
-			}
-		//plot row results
-		for(r = hr; r < nr; r++) {
-			w1[r-hr] = HUGE_VAL;		w2[r-hr] = -HUGE_VAL;
-			for(c = hc; c < nc; c++) {
-				trc[c-hc] = vals[r][c];
-				if(vals[r][c] < w1[r-hr]) w1[r-hr] = vals[r][c];
-				if(vals[r][c] > w2[r-hr]) w2[r-hr] = vals[r][c];
-				if(vals[r][c] < dBounds.Xmin) dBounds.Xmin = vals[r][c];
-				if(vals[r][c] > dBounds.Xmax) dBounds.Xmax = vals[r][c];
-				}
-			d_quartile(c-hc, trc, b1+r-hr, m+r-hr, b2+r-hr);
-			}
-		for(r = hr; r < nr; r++) trc[r-hr] = ((double)(r-hr+1));
-		dBounds.Ymin = 0.5;			dBounds.Ymax = ((double)(nr-hr))+0.5;
-		dBounds.Xmin = dmin;		dBounds.Xmax = dmax;
-		if((graph = new Graph(parent, data, 0L, 0x10)) && 
-			(txt_obj = mk_boxplot(1, m, trc, b1, b2, w1, w2, nvr, nr-hr, "medians", "25-75%", "min/max"))){
-			OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
-			if(LastOpenGO && LastOpenGO->Id == GO_BOXPLOT) {
-				if(((BoxPlot*)LastOpenGO)->y_tv = new TextValue()){
-					for(i = 0; i < (nr-hr); i++)((PlotScatt*)LastOpenGO)->y_tv->GetValue(rnames[i]);
-					}
-				if(((BoxPlot*)LastOpenGO)->y_info=(char*)malloc(20*sizeof(char)))
-					rlp_strcpy(((BoxPlot*)LastOpenGO)->y_info, 20, "Rows");
-				if(((BoxPlot*)LastOpenGO)->x_info=(char*)malloc(20*sizeof(char)))
-					rlp_strcpy(((BoxPlot*)LastOpenGO)->x_info, 20, "Location");
-				}
-			free(txt_obj);								graph->moveable = 0;
-			graph->DRect.Xmax = graph->DRect.Xmin + graph->DRect.Ymax - graph->DRect.Ymin;
-			graph->GRect.Xmax = graph->GRect.Xmin + graph->GRect.Ymax - graph->GRect.Ymin;
-			scale.sx.fx = cx;							scale.sy.fx = txtdef1.fSize*10.0;
-			graph->Command(CMD_SCALE, &scale, 0L);
-			page->Command(CMD_DROP_GRAPH, graph, 0L);
-			}
-		cx = txtdef1.fSize*5.0;			cy = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef2.fSize*2.0;
-		free(m);	free(b1);	free(b2);	free(w1);	free(w2);	free(trc);
-		rep_DrawText(page, cx, cy, false, TXA_HLEFT, &txtdef1, "<b>Between columns:</b>");
-		cy += txtdef1.fSize *1.5;
-		prob = chi_dist(fabs(rchi2), nc-hc-1, 0.0);
-#ifdef USE_WIN_SECURE
-		i = sprintf_s(TmpTxt, 60, "N = %d, Chi<sup>2</sup> = %.2lf, P ", nc-hc, rchi2);
-		if(prob > 0.001) sprintf_s(TmpTxt+i, 20, "= %.3lf", prob);
-#else
-		i = sprintf(TmpTxt, "N = %d, Chi<sup>2</sup> = %.2lf, P ", nc-hc, rchi2);
-		if(prob > 0.001) sprintf(TmpTxt+i, "= %.3lf", prob);
-#endif
-		else rlp_strcpy(TmpTxt+i, 40, "< 0.001");
-		rep_DrawText(page, cx+txtdef1.fSize*3.0, cy, false, TXA_HLEFT, &txtdef1, TmpTxt);
-		cy += txtdef1.fSize*2.0;
-		rep_DrawText(page, cx, cy, false, TXA_HLEFT, &txtdef1, "<b>Between rows:</b>");
-		cy += txtdef1.fSize *1.5;
-		prob = chi_dist(fabs(cchi2), nr-hr-1, 0.0);
-#ifdef USE_WIN_SECURE
-		i = sprintf_s(TmpTxt, 60, "N = %d, Chi<sup>2</sup> = %.2lf, P ", nr-hr, cchi2);
-		if(prob > 0.001) sprintf_s(TmpTxt+i, 20, "= %.3lf", prob);
-#else
-		i = sprintf(TmpTxt, "N = %d, Chi<sup>2</sup> = %.2lf, P ", nr-hr, cchi2);
-		if(prob > 0.001) sprintf(TmpTxt+i, "= %.3lf", prob);
-#endif
-		else rlp_strcpy(TmpTxt+i, 40, "< 0.001");
-		rep_DrawText(page, cx+txtdef1.fSize*3.0, cy, false, TXA_HLEFT, &txtdef1, TmpTxt);
-		if(!(parent->Command(CMD_DROP_GRAPH, page, 0L))) delete page;
-		free(rs);		free(cs);		free(idx);
-		}
-	if(cols) {
-		for(i = 0; i < nc; i++) if(cols[i]) free(cols[i]);
-		free(cols);
-		}
-	if(rows) {
-		for(i = 0; i < nr; i++) if(rows[i]) free(rows[i]);
-		free(rows);
-		}
-	if(vals) {
-		for(i = 0; i < nc; i++) if(vals[i]) free(vals[i]);
-		free(vals);
-		}
-	if (cnames) {
-		for(c = 0; c < (rec.right-rec.left+1); c++) if(cnames[c]) free(cnames[c]);
-		free(cnames);
-		}
-	if (rnames) {
-		for(r = 0; r < (rec.bottom-rec.top+1); r++) if(rnames[r]) free(rnames[r]);
-		free(rnames);
-		}
-	if(nvr) free(nvr);		if(nvc) free(nvc);
-	CloseDlgWnd(hDlg);		delete Dlg;		free(FmAnovDlg);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Two way anova with replica
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *TwAnovDlg_Tmpl = 
-	"1,2,,DEFAULT,PUSHBUTTON,-1,148,10,45,12\n"
-	"2,3,,,PUSHBUTTON,-2,148,25,45,12\n"
-	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"4,10,100,ISPARENT | CHECKED,SHEET,1,5,10,130,80\n"
-	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-	"100,101,,,LTEXT,2,10,30,60,8\n"
-	"101,102,,,RANGEINPUT,-15,20,40,100,10\n"
-	"102,103,,,LTEXT,3,20,55,53,8\n"
-	"103,,,LASTOBJ,EDVAL1,4,75,55,30,10";
-
-typedef struct _anov_group_info {
-	double *rmeans, *cmeans;
-	int *vpr, *vpc, nvals;
-	double mean;
-	}anov_group_info;
-
-void rep_twoway_anova(GraphObj *parent, DataObj *data)
-{
-	TabSHEET tab1 = {0, 40, 10, "Input Data"};
-	DlgInfo *TwAnovDlg;
-	double dlpr = 3.0, *cmeans;
-	void *dyndata[] = {(void*)&tab1, (void*)"rectangular range for variables", (void*)"lines per replica:",
-		(void*)&dlpr};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, j, k, res, ntot, lpr, ngr, r1, r2, c1, c2, iErr;
-	int *vpc, *vpr;
-	double tmp, dn, gMean, SSwithin, SSsubgr, SStotal, SSrows, SScols, SSinteract;
-	double **res_tab = 0L, cx, cy;
-	bool bContinue = false;
-	AccRange *rD =0L;
-	char *mrk;
-	RECT rec;
-	anyResult ares;
-	anov_group_info *agr;
-	Page *page;
-
-	if(!parent || !data) return;
-	if(!(TwAnovDlg = CompileDialog(TwAnovDlg_Tmpl, dyndata))) return;
-	if(data->Command(CMD_GETMARK, &mrk, 0L))rlp_strcpy(TmpTxt, TMP_TXT_SIZE, mrk);
-	else {
-		data->ValueRec(&rec);
-		rlp_strcpy(TmpTxt, 100, mkRangeRef(rec.top, rec.left, rec.bottom, rec.right));
-		}
-	if(!(Dlg = new DlgRoot(TwAnovDlg, data)))return;
-	hDlg = CreateDlgWnd("Two-Way Anova with Replica", 50, 50, 420, 220, Dlg, 0x0L);
-loop1:
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 1:
-			Dlg->GetValue(103, &dlpr);		lpr = (int)dlpr;
-			break;
-			}
-		}while (res < 0);
-	if(res == 1 && Dlg->GetText(101, TmpTxt+200, TMP_TXT_SIZE-200) &&(rD = new AccRange(TmpTxt+200))
-		&& rD->BoundRec(&rec) && (ntot = rD->CountItems())){
-		r1 = rec.top;		r2 = rec.bottom+1;			c1= rec.left;	c2 = rec.right+1;
-		vpc = (int*)calloc(c2-c1+1, sizeof(int));		vpr = (int*)calloc(r2-r1+1, sizeof(int));
-		for(k = iErr = 0; k < 2; k++) {
-			for(i = c1;	i < c2; i++) for(j = r1; j < r2; j++) {
-				data->GetResult(&ares, j, i, false);
-				if(ares.type == ET_VALUE) {
-					vpc[i-c1]++;					vpr[j-r1]++;
-					}
-				}
-			if(!k) {
-				for(i = 0; !vpc[i]; i++);			for(j = 0; !vpr[j]; j++);
-				memset(vpc,0,sizeof(int)*(c2-c1));	memset(vpr,0,sizeof(int)*(r2-r1));
-				c1 += i;		r1 += j;
-				}
-			}
-		while(c2 > c1 && !vpc[c2-c1-1]) c2--;		while(r2 > r1 && !vpr[r2-r1-1]) r2--;
-		ngr = (int)(((double)(r2-r1))/dlpr);
-		if(ngr * lpr < r2 -r1) iErr = 2;
-		agr = (anov_group_info *)calloc(ngr+1, sizeof(anov_group_info));
-		cmeans = (double *)calloc(c2-c1+1, sizeof(double));
-		for(i = 0; i <= ngr; i++) {
-			agr[i].cmeans = (double*)calloc(c2-c1+2, sizeof(double));
-			agr[i].vpc = (int*)calloc(c2-c1+2, sizeof(int));
-			agr[i].rmeans = (double*)calloc(lpr+2, sizeof(double));
-			agr[i].vpr = (int*)calloc(lpr+2, sizeof(int));
-			}
-		for(i = c1; i < c2; i++) for(j = r1; j < r2; j++) {
-			k = (j-r1)/lpr;		data->GetResult(&ares, j, i, false);
-			if(ares.type == ET_VALUE) {
-				agr[k].cmeans[i-c1] += ares.value;		agr[k].vpc[i-c1]++;
-				agr[k].rmeans[j-k*lpr] += ares.value;	agr[k].vpr[j-k*lpr]++;
-				agr[k].mean += ares.value;				agr[k].nvals++;
-				cmeans[i-c1] += ares.value;
-				}
-			else iErr = 1;
-			}
-		for(k = 0; k < ngr; k++) {
-			agr[k].mean /= ((double)(agr[k].nvals));
-			for(i = 0; i < (c2-c1); i++) {
-				agr[k].cmeans[i] /= ((double)(agr[k].vpc[i]));
-				}
-			}
-		for(i = c1, SSwithin = gMean = 0.0, dn = 1.0; i < c2; i++) for(j = r1; j < r2; j++) {
-			k = (j-r1)/lpr;		data->GetResult(&ares, j, i, false);
-			if(ares.type == ET_VALUE) {
-				SSwithin += ((tmp = ares.value-agr[k].cmeans[i-c1])*tmp);
-				gMean += ((ares.value - gMean)/dn);		dn	+=	1.0;
-				}
-			}
-		for(k = 0, SSsubgr = SSrows = 0.0; k < ngr; k++) {
-			for(i = 0; i < (c2-c1); i++) {
-				SSsubgr += (dlpr*((tmp = agr[k].cmeans[i] - gMean) * tmp));
-				}
-			SSrows += ((tmp = (agr[k].mean - gMean)) * tmp);
-			}
-		for(i = c1, SScols = 0.0; i < c2; i++) {
-			cmeans[i-c1] /= ((double)(r2-r1));
-			SScols += ((tmp = cmeans[i-c1]-gMean) * tmp);
-			}
-		SStotal = SSsubgr + SSwithin;		SSrows *= (dlpr *((double)(c2-c1)));
-		SScols *= (dlpr * ((double)ngr));	SSinteract = fabs(SSsubgr - SSrows - SScols);
-		if(!iErr&& (res_tab = (double**)calloc(5, sizeof(double*)))
-			&& (res_tab[0] = (double*)calloc(5, sizeof(double)))
-			&& (res_tab[1] = (double*)calloc(5, sizeof(double)))
-			&& (res_tab[2] = (double*)calloc(5, sizeof(double)))
-			&& (res_tab[3] = (double*)calloc(5, sizeof(double)))
-			&& (res_tab[4] = (double*)calloc(5, sizeof(double)))) {
-			res_tab[0][0] = (double)(ngr-1);				res_tab[1][0] = (double)(c2-c1-1);
-			res_tab[2][0] = res_tab[0][0]* res_tab[1][0];	res_tab[3][0] = (double)(ngr*(c2-c1)*(lpr-1));
-			res_tab[4][0] = res_tab[0][0] + res_tab[1][0] + res_tab[2][0] + res_tab[3][0];
-			res_tab[0][1] = SSrows;			res_tab[0][2] = res_tab[0][1] / res_tab[0][0];
-			res_tab[1][1] = SScols;			res_tab[1][2] = res_tab[1][1] / res_tab[1][0];
-			res_tab[2][1] = SSinteract;		res_tab[2][2] = res_tab[2][1] / res_tab[2][0];
-			res_tab[3][1] = SSwithin;		res_tab[3][2] = res_tab[3][1] / res_tab[3][0];
-			res_tab[4][1] = SStotal;		res_tab[0][3] = res_tab[0][2] / res_tab[3][2];
-			res_tab[1][3] = res_tab[1][2] / res_tab[3][2];
-			res_tab[2][3] = res_tab[2][2] / res_tab[3][2];
-			res_tab[0][4] = f_dist(res_tab[0][3], res_tab[0][0], res_tab[3][0]);
-			res_tab[1][4] = f_dist(res_tab[1][3], res_tab[1][0], res_tab[3][0]);
-			res_tab[2][4] = f_dist(res_tab[2][3], res_tab[2][0], res_tab[3][0]);
-
-
-			rep_init();
-//			dBounds.Xmin = 0.5;				dBounds.Xmax = ((double)nc)+0.5;
-			page = new Page(parent, data);
-			mk_header(page, "<b>Two-Way ANOVA with Replication</b>", data);
-
-			cx = txtdef1.fSize*5.0;		cy = txtdef1.fSize*10.0;
-
-			rep_DrawText(page, cx, cy, false, TXA_HLEFT, &txtdef1, "<b>Anova:</b>");
-			cy = mk_table(page, cx, cy+txtdef2.fSize, 3, res_tab)+txtdef2.fSize;
-			if(!(parent->Command(CMD_DROP_GRAPH, page, 0L))) delete page;
-			free(res_tab[0]);			free(res_tab[1]);			free(res_tab[2]);
-			free(res_tab[3]);			free(res_tab[4]);			free(res_tab);
-			}
-		for(i = 0; i <= ngr; i++) {
-			free(agr[i].cmeans);					free(agr[i].vpc);
-			free(agr[i].rmeans);					free(agr[i].vpr);
-			}
-		free(vpc);			free(vpr);		free(cmeans);
-		delete rD;
-		switch(iErr) {
-			case 1:		
-				ErrorBox("There are missing Data!");
-				goto loop1;
-			case 2:		
-				ErrorBox("The total number of lines\nmust be a multiple of\nlines per replica.");
-				goto loop1;
-			}
-		}
-		CloseDlgWnd(hDlg);		delete Dlg;		free(TwAnovDlg);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Kruskal-Wallis Test for Differences of Location
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *RepKruskal_DlgTmpl =
-	"1,2,,DEFAULT,PUSHBUTTON,-1,158,10,45,12\n"
-	"2,3,,,PUSHBUTTON,-2,158,25,45,12\n"
-	"3,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"10,20,152,ISPARENT | CHECKED, SHEET,1,5,10,140,70\n"
-	"20,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-	"152,153,,ISPARENT | CHECKED,GROUPBOX,2,12,30,128,45\n"
-	"153,154,,,LTEXT,0,25,35,60,8\n"
-	"154,155,,,RANGEINPUT,0,25,45,100,10\n"
-	"155,156,0,,PUSHBUTTON,-8,95,57,30,12\n"
-	"156,,,LASTOBJ,PUSHBUTTON,-9,60,57,35,12";
-
-void rep_kruskal(GraphObj *parent, DataObj *data)
-{
-	TabSHEET tab1 = {0, 25, 10, "Data"};
-	
-	void *dyndata[] = {(void*)&tab1, (void*)" select one range for every variable "};
-	DlgInfo *KruskalDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, j, n, c, r, nt, res, currYR = 0, maxYR = 0, ny, nr, *nvals;
-	bool updateYR = true, bContinue = false;
-	double h, h1, p, **vals, *x, *y, *by1, *by2, *wy1, *wy2, *ranks, *ridx, *rsums, th, cy, cx[10];
-	char **rd = 0L, **names, *txt_obj;
-	char *headings[] = {"<i>Groups</i>", "<i>N</i>", "<i>Median</i>", "<i>25% - 75%</i>",
-		"<i>Range</i>","<i>Rank Sums</i>"};
-	scaleINFO scale = {{0.0, 1.0}, {0.0, 1.0}, {0.0, 1.0}};
-	AccRange *rV1 = 0L;
-	anyResult ares;
-	Page *page;
-	Graph *graph;
-
-	if(!parent || !data) return;
-	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
-		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return;
-	if(!(KruskalDlg = CompileDialog(RepKruskal_DlgTmpl, dyndata))) return;
-	if(TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
-		for(i=j=0; i <= 1000; i +=100) if(TmpTxt[i]) 
-			rd[j++] = (char*)memdup(TmpTxt+i, ((int)strlen(TmpTxt+i))+2, 0);	 maxYR = j-1;
-		}
-	if(!rd && !(rd = (char**)calloc(1, sizeof(char*))))return;
-	if(!(Dlg = new DlgRoot(KruskalDlg, data))) return;
-	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
-	hDlg = CreateDlgWnd("Kruskal-Wallis Nonparametric Anova", 50, 50, 420, 200, Dlg, 0x0L);
-	do {
-		if(updateYR) {
-			if(currYR >0) Dlg->ShowItem(156, true);
-			else Dlg->ShowItem(156, false);
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "variable # %d/%d", currYR+1, maxYR+1);
-#else
-			sprintf(TmpTxt,"variable # %d/%d", currYR+1, maxYR+1);
-#endif
-			Dlg->SetText(153, TmpTxt);
-			updateYR = false;
-			}
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 0:
-			if(bContinue || Dlg->GetCheck(20)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 155:		case 156:
-			res = com_StackDlg(res, Dlg, 0L, 0L, &rd, &currYR,
-				&rV1, &bContinue, &ny, &maxYR, &updateYR);
-			break;
-			}
-		}while (res < 0);
-	if(res == 1 && (vals = (double**)calloc(sizeof(double*), maxYR+1)) && (nvals = (int*)calloc(sizeof(int), maxYR+1))
-		&& (names = (char**)calloc(maxYR+1, sizeof(char*))) && (x = (double*)calloc(maxYR+1, sizeof(double)))
-		&& (y = (double*)calloc(maxYR+1, sizeof(double))) && (by1 = (double*)calloc(maxYR+1, sizeof(double)))
-		&& (by2 = (double*)calloc(maxYR+1, sizeof(double))) && (wy1 = (double*)calloc(maxYR+1, sizeof(double)))
-		&& (wy2 = (double*)calloc(maxYR+1, sizeof(double)))
-		&& (rsums = (double*)calloc(maxYR+1, sizeof(double)))) {
-		maxYR++;	rep_init();		page = new Page(parent, data);
-		dBounds.Xmin = 0.5;			dBounds.Xmax = (double)maxYR+0.3;
-		if(rV1) delete rV1;			rV1 = 0L;	ranks = ridx = 0L;
-		cy = txtdef1.fSize*10.0;
-		mk_header(page, "<b>Kruskal-Wallis Test for Differences of Location</b>", data);
-		dBounds.Ymin = HUGE_VAL;	dBounds.Ymax = -HUGE_VAL;
-		// get data into two dimensional array
-		for(nr = maxYR, i = nt = 0; i < nr; i++) {
-			x [i] = y[i] = by1[i] = by2[i] = wy1[i] = wy2[i] = 0.0;		nvals[i] = 0;
-			if((rV1 = new AccRange(rd[i])) && (n = rV1->CountItems()) && (vals[i] = (double*)malloc(n*sizeof(double)))) {
-				names[i] = rV1->RangeDesc(data, 1);
-				for(n = 0, rV1->GetFirst(&c, &r); rV1->GetNext(&c, &r); ) {
-					if(data->GetResult(&ares, r, c, false) && ares.type == ET_VALUE) {
-						if(!n) wy1[i] = wy2[i] = ares.value;
-						else {
-							if(ares.value < wy1[i]) wy1[i] = ares.value;
-							if(ares.value > wy2[i]) wy2[i] = ares.value;
-							}
-						if(ares.value < dBounds.Ymin) dBounds.Ymin = ares.value;
-						if(ares.value > dBounds.Ymax) dBounds.Ymax = ares.value;
-						vals[i][n] = ares.value;		n++;
-						}
-					}
-				nvals[i] = n;	nt += n;	delete rV1;		rV1 = 0;
-				}
-			if(!names[i] && (names[i] = (char*)malloc(20*sizeof(char)))){
-#ifdef USE_WIN_SECURE
-				sprintf_s(names[i], 20, "Group %d", i+1);
-#else
-				sprintf(names[i], "Group %d", i+1);
-#endif
-				}
-			}
-		// rank sums
-		if(nt && (ranks=(double*)malloc(nt*sizeof(double))) && (ridx=(double*)malloc(nt*sizeof(double)))) {
-			for(i = n = 0; i < nr; i++) {
-				for(j = 0; j < nvals[i]; j++) {
-					ridx[n] = (double)(i);	ranks[n] = vals[i][j];	n++;
-					}
-				}
-			SortArray2(n, ranks, ridx);			crank(n, ranks, &th);
-			for(i = 0; i < n; i++) rsums[(int)ridx[i]] += ranks[i];
-			//statistics on range sums
-			for(i = 0, h = 0.0; i < nr; i++) h += rsums[i]*rsums[i]/((double)nvals[i]);
-			h = h * 12.0/(((double)n)*((double)(n+1))) - 3.0*((double)n+1);
-			h1 = h  / (1.0 - th/(((double)(n-1)) * ((double)n)* ((double)(n+1))));
-			}
-		else h = h1 = -1.0;
-		// check for unique names
-		for(i = 0; i < (nr-1); i++) for(j = i+1; j < nr; j++) {
-			if(!strcmp(names[i], names[j])) {
-				names[i] = (char*) realloc(names[i], 20 *sizeof(char));
-				names[j] = (char*) realloc(names[j], 20 *sizeof(char));
-#ifdef USE_WIN_SECURE
-				sprintf_s(names[i], 20, "Group %d", i+1);	sprintf_s(names[j], 20, "Group %d", j+1);
-#else
-				sprintf(names[i], "Group %d", i+1);			sprintf(names[j], "Group %d", j+1);
-#endif
-				}
-			}
-		// simple group statistics
-		for(i = 0; i < nr; i++) {
-			x[i] = (double)(i+1);		d_quartile(nvals[i], vals[i], by1+i, y+i, by2+i);
-			}
-		// create boxplot
-		if((graph = new Graph(parent, data, 0L, 0)) && (txt_obj = mk_boxplot(0, x, y, by1, by2, wy1, wy2,
-			nvals, nr,"Median","25-75%","Min./Max."))){
-			scale.sx.fx = (txtdef1.fSize*5.0);			scale.sy.fx = (txtdef1.fSize*10.0);
-			OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
-			if(LastOpenGO && LastOpenGO->Id == GO_BOXPLOT) {
-				if(((BoxPlot*)LastOpenGO)->x_tv = new TextValue()){
-					for(i = 0; i < nr; i++) ((BoxPlot*)LastOpenGO)->x_tv->GetValue(names[i]);
-					}
-				if(((BoxPlot*)LastOpenGO)->x_info = (char*)malloc(20*sizeof(char)))
-					rlp_strcpy(((BoxPlot*)LastOpenGO)->x_info, 20, "Groups");
-				if(((BoxPlot*)LastOpenGO)->y_info = (char*)malloc(20*sizeof(char)))
-					rlp_strcpy(((BoxPlot*)LastOpenGO)->y_info, 20, "Location");
-				}
-			free(txt_obj);		graph->Command(CMD_SCALE, &scale, 0L);
-			cy = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef1.fSize*2.0;
-			page->Command(CMD_DROP_GRAPH, graph, 0L);
-			}
-		parent->Command(CMD_DROP_GRAPH, page, 0L);
-		//report statistics
-		cx[0] = txtdef1.fSize*5.0;				cx[1] = cx[0] + linsp1*5.0;
-		cx[2] = cx[1] + linsp1*2.5;		cx[3] = cx[2] + linsp1*4.0;
-		cx[4] = cx[3] + linsp1*5.0;		cx[5] = cx[4] + linsp1*7.0;
-		cx[6] = cx[5] + linsp1*8.0;
-		rep_DrawText(page, cx[0], cy, false, TXA_HLEFT, &txtdef1, "<b>Test Statistics:</b>");
-		cy += linsp2;			p = chi_dist(h,(double)(nr-1), 0.0);
-		dbl_to_str2(TmpTxt, 100, p < 0.0001 ?(char*)"H = %.2lf, P < 0.0001" : (char*)"H = %.2lf, P = %.4lf", h, p);
-		rep_DrawText(page, cx[1], cy, false, TXA_HLEFT, &txtdef1, TmpTxt);
-		cy += linsp1;			p = chi_dist(h1,(double)(nr-1), 0.0);
-		dbl_to_str2(TmpTxt, 100, p < 0.0001 ?(char*)"H(corr.) = %.2lf, P < 0.0001" : (char*)"H(corr.) = %.2lf, P = %.4lf", h1, p);
-		if(th >= 1.0) {
-			rep_DrawText(page, cx[1], cy, false, TXA_HLEFT, &txtdef1, TmpTxt);
-			cy += linsp1;
-			}
-		cy += linsp2;
-		// create summary table
-		rep_DrawText(page, cx[0], cy, false, TXA_HLEFT, &txtdef1, "<b>Summary:</b>");
-		for(i = 0, cy += linsp2; i < 6; i++) {				//column headers
-			c = (i == 3 || i == 4) ? TXA_HCENTER : TXA_HRIGHT;
-			rep_DrawText(page, cx[i+1], cy, false, c, &txtdef1, headings[i]);
-			}
-		mk_hr(page, cx[0], cx[6]+txtdef1.fSize*2.0, cy +linsp1);
-		for(i = 0, cy += linsp2; i < nr; i++, cy += linsp1) {
-			for(j = 0; j < 6; j++) {
-				switch(j) {
-				default:	rlp_strcpy(TmpTxt, 20, names[i]);						break;
-				case 1:		dbl_to_str1(TmpTxt, 20, "%.0lf", (double)nvals[i]);		break;
-				case 2:		dbl_to_str1(TmpTxt, 20, "%g", y[i]);					break;
-				case 3:		dbl_to_str2(TmpTxt, 20, "%g - %g", by1[i], by2[i]);		break;
-				case 4:		dbl_to_str2(TmpTxt, 20, "%g - %g", wy1[i], wy2[i]);		break;
-				case 5:		dbl_to_str1(TmpTxt, 20, "%g", rsums[i]);				break;
-					}
-				c = (j == 3 || j == 4) ? TXA_HCENTER : TXA_HRIGHT;
-				rep_DrawText(page, cx[j+1], cy, false, c, &txtdef1,TmpTxt);
-				}
-			}
-		mk_hr(page, cx[0], cx[6]+txtdef1.fSize*2.0, cy+txtdef1.fSize*0.2);
-		for(i= 0; i< nr; i++) {
-			if(vals[i]) free(vals[i]);	if(names[i]) free(names[i]);
-			}
-		free(vals);				free(nvals);		free(names);
-		free(x);				free(y);			free(by1);
-		free(by2);				free(wy1);			free(wy2);
-		free(rsums);
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(rd) {
-		for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
-		free(rd);
-		}
-	if(ranks)free(ranks);	if(ridx)free(ridx);
-	if(rV1) delete rV1;		free(KruskalDlg);
-	return;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// simple sample statistics
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *SmplStatDlg_Tmpl = 
-	"1,2,,DEFAULT,PUSHBUTTON,-1,148,10,45,12\n"
-	"2,3,,,PUSHBUTTON,-2,148,25,45,12\n"
-	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"4,10,100,ISPARENT | CHECKED,SHEET,1,5,10,130,80\n"
-	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-	"100,101,,,LTEXT,2,10,30,60,8\n"
-	"101,,,LASTOBJ,RANGEINPUT,-15,20,40,100,10";
-void rep_samplestats(GraphObj *parent, DataObj *data)
-{
-	TabSHEET tab1 = {0, 40, 10, "Input Data"};
-	DlgInfo *SmplStatDlg;
-	void *dyndata[] = {(void*)&tab1, (void*)"rectangular range for variables"};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int res, nr, nc, ntot, cb;
-	double val, *src_data, cx, cy, ksprob, ksd, sww, swp, mean, sd;
-	bool bContinue = false;
-	AccRange *rD =0L;
-	char *mrk, *x_info, *y_info;
-	RECT rec;
-	Plot *plot;
-	Graph *graph;
-	Page *page;
-
-	if(!parent || !data) return;
-	if(!(SmplStatDlg = CompileDialog(SmplStatDlg_Tmpl, dyndata))) return;
-	if(data->Command(CMD_GETMARK, &mrk, 0L))rlp_strcpy(TmpTxt, TMP_TXT_SIZE, mrk);
-	else {
-		data->ValueRec(&rec);
-		rlp_strcpy(TmpTxt, 100, mkRangeRef(rec.top, rec.left, rec.bottom, rec.right));
-		}
-	if(!(Dlg = new DlgRoot(SmplStatDlg, data)))return;
-	hDlg = CreateDlgWnd("Sample Statistics", 50, 50, 420, 220, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-			}
-		}while (res < 0);
-	if(res == 1 && Dlg->GetText(101, TmpTxt+200, TMP_TXT_SIZE) &&(rD = new AccRange(TmpTxt+200))
-		&& rD->BoundRec(&rec) && (ntot = rD->CountItems()) && (src_data = (double*)malloc(ntot*sizeof(double)))){
-		rep_init();
-		x_info = rD->RangeDesc(data, 2);
-		if(y_info = (char*)malloc(20)) rlp_strcpy(y_info, 20, "Normal quantiles");
-		page = new Page(parent, data);
-		cb = rlp_strcpy(TmpTxt, 100, "<b>Sample Statistics for \"");
-		if(x_info && x_info[0])	cb += rlp_strcpy(TmpTxt+cb, 100-cb, x_info);
-		else cb += rlp_strcpy(TmpTxt+cb, 100-cb, TmpTxt+200);
-		rlp_strcpy(TmpTxt+cb,100-cb, "\"</b>");		mk_header(page, TmpTxt, data);
-		for(ntot = 0, rD->GetFirst(&nc, &nr); rD->GetNext(&nc, &nr); ) {
-			if(data->GetValue(nr, nc, &val)) src_data[ntot++] = val;
-			}
-		if(ntot > 5 && (graph = new Graph(page, data, 0L, 0))) {
-			if(plot = new NormQuant(page, data, src_data, ntot)) {
-				plot->x_info = x_info;		plot->y_info = y_info;
-				}
-			if(!(graph->Command(CMD_DROP_PLOT, (void *)plot, 0L))) {
-				delete plot;	plot =0L;
-				}
-			graph->moveable = 0;							graph->GRect.Xmin += (txtdef1.fSize*5.0);	
-			graph->GRect.Xmax += (txtdef1.fSize*5.0);		graph->GRect.Ymin += (txtdef1.fSize*10.0);
-			graph->GRect.Ymax += (txtdef1.fSize*10.0);		page->Command(CMD_DROP_GRAPH, graph, 0L);
-			}
-		cy = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef1.fSize*3;
-		rep_DrawText(page, graph->GRect.Xmin, cy, false, TXA_HLEFT, &txtdef1, "<b>Descriptive Statistics:</b>");
-		cy += txtdef1.fSize*1.5;			cx = graph->GetSize(SIZE_GRECT_LEFT)+txtdef1.fSize*25.0;
-		mk_median_report(page, cx, cy, src_data, ntot, .95, 0L);
-		cx = graph->GetSize(SIZE_GRECT_LEFT)+txtdef1.fSize*2.0;
-		cy = mk_mean_report(page, cx, cy, src_data, ntot, .95, 0L);
-		//data are sorted by the mk_median_report();
-		d_variance(ntot, src_data, &mean, &sd);
-		sd = sqrt(sd/((double)(ntot-1)));
-		KolSmir(ntot, src_data, norm_dist, mean, sd, true, &ksd, &ksprob);
-		cy += txtdef1.fSize*1.5;			cx = graph->GRect.Xmin;
-		rep_DrawText(page, cx , cy, false, TXA_HLEFT, &txtdef1, "<b>Test for Normal Distribution:</b>");
-		cy += linsp1;						cx += (txtdef1.fSize*2.0);
-		cb = dbl_to_str1(TmpTxt, 100, "Kolmogorov-Smirnov D = %.4lf, P ", ksd);
-		dbl_to_str1(TmpTxt+cb, 100-cb, ksprob >= 0.0001 ? (char*)"= %.4lf" : (char*)"< 0.0001", ksprob);
-		rep_DrawText(page, cx , cy, false, TXA_HLEFT, &txtdef1,TmpTxt);			cy += linsp1/1.2;
-		swilk1(ntot, src_data, norm_dist, 0.0, 1.0, true, &sww, &swp);
-		if(sww >= 0.0 && swp >= 0.0 && (cb = dbl_to_str1(TmpTxt, 100, "Shapiro-Wilk W = %.4lf, P ", sww))
-			&& (dbl_to_str1(TmpTxt+cb, 100-cb, swp >= 0.0001 ? (char*)"= %.4lf" : (char*)"< 0.0001", swp))){
-			rep_DrawText(page, cx , cy, false, TXA_HLEFT, &txtdef1,TmpTxt);		cy += linsp1/1.2;
-			}
-		parent->Command(CMD_DROP_GRAPH, page, 0L);
-		delete rD;	free(src_data);
-		}
-	CloseDlgWnd(hDlg);	delete Dlg;		free(SmplStatDlg);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// linear regression analysis
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static double mk_regr_summary(GraphObj *parent, double x, double y, double *dres, double ci, int n)
-{
-	char *fmts[] = {"slope = %g", "intercept = %g", "observations = %g", "r<sup> 2</sup> = %g", "r = %g"};
-	char *ci_fmt = "%g  -  %g";
-	char lbl[80];
-	double z, s;
-	double x1 = x + txtdef1.fSize*3.0;
-	double x2 = x + txtdef1.fSize*25.0;
-#ifdef _WINDOWS
-	double hrw = txtdef1.fSize*38.0;
-#else
-	double hrw = txtdef1.fSize*1.3*38.0;
-#endif
-
-	rep_DrawText(parent, x, y, false, TXA_HLEFT, &txtdef1, "<b>Regression:</b>");
-	dbl_to_str1(lbl, 80, "%g%% C.I.", ci);
-	rep_DrawText(parent, x2, y, false, TXA_HCENTER, &txtdef1, lbl);
-	mk_hr(parent, x, x+hrw, y+txtdef1.fSize*1.2);
-	y += linsp1*1.5;		dbl_to_str1(lbl, 80, fmts[0], dres[0]);
-	rep_DrawText(parent, x1, y, false, TXA_HLEFT, &txtdef1, lbl);
-	dbl_to_str2(lbl, 80, ci_fmt, dres[0]-dres[10], dres[0]+dres[10]);
-	rep_DrawText(parent, x2, y, false, TXA_HCENTER, &txtdef1, lbl);
-	y += linsp1;		dbl_to_str1(lbl, 80, fmts[1], dres[1]);
-	rep_DrawText(parent, x1, y, false, TXA_HLEFT, &txtdef1, lbl);
-	dbl_to_str2(lbl, 80, ci_fmt, dres[1]-dres[11], dres[1]+dres[11]);
-	rep_DrawText(parent, x2, y, false, TXA_HCENTER, &txtdef1, lbl);
-	y += linsp1;		dbl_to_str1(lbl, 80, fmts[2], (double)n);
-	rep_DrawText(parent, x1, y, false, TXA_HLEFT, &txtdef1, lbl);
-	y += linsp1;		dbl_to_str1(lbl, 80, fmts[3], dres[12]);
-	rep_DrawText(parent, x1, y, false, TXA_HLEFT, &txtdef1, lbl);
-	y += linsp1;		dbl_to_str1(lbl, 80, fmts[4], sqrt(dres[12]));
-	rep_DrawText(parent, x1, y, false, TXA_HLEFT, &txtdef1, lbl);
-	z = 0.5 * log((1.0+sqrt(dres[12])+_PREC)/(1.0-sqrt(dres[12])+_PREC));	//Fishers z-transform
-	s = distinv(t_dist, 1.0E+10, 1.0, (100-ci)/100.0, 2.0)/sqrt((double)(n-3));	
-	dbl_to_str2(lbl, 80, ci_fmt, tanh(z-s), tanh(z+s));
-	rep_DrawText(parent, x2, y, false, TXA_HCENTER, &txtdef1, lbl);
-	mk_hr(parent, x, x+hrw, y+txtdef1.fSize*1.2);
-	return y + linsp1*3.0;
-}
-
-static char *RegrDlg_Tmpl =
-	"1,+,,DEFAULT,PUSHBUTTON,-1,148,10,45,12\n"
-	".,.,,,PUSHBUTTON,-2,148,25,45,12\n"
-	".,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"4,10,100,ISPARENT | CHECKED,SHEET,1,5,10,130,100\n"
-	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-	"100,+,,,LTEXT,2,10,30,60,8\n"
-	".,.,,,RANGEINPUT,-15,20,40,100,10\n"
-	".,.,,,LTEXT,3,10,55,60,8\n"
-	".,.,,,RANGEINPUT,-16,20,65,100,10\n"
-	".,.,,,LTEXT,4,10,80,60,8\n"
-	".,.,,,EDVAL1,5,74,80,25,10\n"
-	".,.,,,LTEXT,-10,101,80,60,8\n"
-	".,,,LASTOBJ,CHECKBOX,6,10,95,100,8";
-
-void
-rep_regression(GraphObj *parent, DataObj *data)
-{
-	TabSHEET tab1 = {0, 60, 10, "Regression Input"};
-	double ci = 95.0;
-	DlgInfo *RegrDlg;
-	void *dyndata[] = {(void*)&tab1, (void*)"range for independent variable (x)",
-		(void*)"range for dependent variable (y)", (void*)"confidence interval:",
-		(void*)&ci, (void*)" include origin"};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, n, n1, rx, cx, ry, cy, res, align = 0;
-	int x_dtype, y_dtype, nVals, nTxt, nTime;
-	bool bContinue = false, bValid, bParZ;
-	AccRange *rX = 0L, *rY = 0L;
-	double *x = 0L, *y = 0L, **res_tab = 0L, c_x, c_y;
-	double sx, sy, dx, dy, sxy, sxx, syy, sdy, df, t, ts, ty;
-	double dres[14], ly[4];
-	char *txt_obj, *x_desc=0L, *y_desc=0L;
-	anyResult xRes, yRes;
-	TextValue *x_tv, *y_tv;
-	Graph *graph;
-	Page *page;
-
-	if(!parent || !data) return;
-	if(!(RegrDlg = CompileDialog(RegrDlg_Tmpl, dyndata))) return;
-	UseRangeMark(data, 1, TmpTxt, TmpTxt+100);
-	if(!(Dlg = new DlgRoot(RegrDlg, data)))return;
-	hDlg = CreateDlgWnd("Linear Regression", 50, 50, 420, 260, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 1:
-			Dlg->GetValue(105, &ci);			bParZ = Dlg->GetCheck(107);
-			if(rX) delete rX;					if(rY) delete rY;
-			rX = rY = 0L;
-			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) rX = new AccRange(TmpTxt);
-			n = rX ? rX->CountItems() : 0;
-			if(!n) {
-				ErrorBox("Range not specified\nor not valid.");
-				bContinue = true;
-				res = -1;
-				}
-			if(n && Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE) && (rY = new AccRange(TmpTxt))){
-				if(n != rY->CountItems()) {
-					ErrorBox("Both ranges must be given\nand must have the same size");
-					bContinue = true;
-					res = -1;
-					}
-				}
-			}
-		}while (res < 0);
-	n1 = n;
-	if(res == 1 && n >1 && rX && rY && (x = (double*)malloc(n*sizeof(double))) 
-		&& (y = (double*)malloc(n*sizeof(double)))
-		&& (res_tab = (double**)calloc(3, sizeof(double*)))
-		&& (res_tab[0] = (double*) malloc(5*sizeof(double)))
-		&& (res_tab[1] = (double*) malloc(5*sizeof(double)))
-		&& (res_tab[2] = (double*) malloc(5*sizeof(double)))) {
-		x_desc = rX->RangeDesc(data, 0);	y_desc = rY->RangeDesc(data, 0);
-		//analyse data types
-		x_dtype = y_dtype = 0;				x_tv = y_tv = 0L;
-		if(rX->DataTypes(data, &nVals, &nTxt, &nTime)){
-			if(!nVals && nTxt > 1 && nTxt > nTime) x_tv = new TextValue();
-			else if(!nVals && nTime > 1 && nTime > nTxt) x_dtype = ET_DATETIME;
-			}
-		if(rY->DataTypes(data, &nVals, &nTxt, &nTime)){
-			if(!nVals && nTxt > 1 && nTxt > nTime) y_tv = new TextValue();
-			else if(!nVals && nTime > 1 && nTime > nTxt) y_dtype = ET_DATETIME;
-			}
-		rX->GetFirst(&cx, &rx);				rY->GetFirst(&cy, &ry);
-		rep_init();
-		dBounds.Xmin = dBounds.Ymin = HUGE_VAL;		dBounds.Xmax = dBounds.Ymax = -HUGE_VAL;
-		//read data into x[] and y[]
-		for(i = n = 0; i < n1 && rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry); i++) {
-			bValid = false;
-			if(data->GetResult(&xRes, rx, cx, false) && data->GetResult(&yRes, ry, cy, false)) {
-				bValid = true;
-				if(x_tv) {
-					if(xRes.type == ET_TEXT) dx = x_tv->GetValue(xRes.text);
-					else bValid = false;
-					}
-				else if(x_dtype == ET_DATETIME) {
-					if(xRes.type == ET_DATE || xRes.type == ET_TIME  || xRes.type == ET_DATETIME) dx = xRes.value;
-					else bValid = false;
-					}
-				else {
-					if(xRes.type == ET_VALUE) dx = xRes.value;
-					else bValid = false;
-					}
-				if(y_tv) {
-					if(yRes.type == ET_TEXT) dy = y_tv->GetValue(yRes.text);
-					else bValid = false;
-					}
-				else if(y_dtype == ET_DATETIME) {
-					if(yRes.type == ET_DATE || yRes.type == ET_TIME  || yRes.type == ET_DATETIME) dy = yRes.value;
-					else bValid = false;
-					}
-				else {
-					if(yRes.type == ET_VALUE) dy = yRes.value;
-					else bValid = false;
-					}
-				}
-			if(bValid){
-				x[n] = dx;				y[n] = dy;
-				if(dBounds.Xmin > x[n]) dBounds.Xmin = x[n];
-				if(dBounds.Xmax < x[n]) dBounds.Xmax = x[n];
-				if(dBounds.Ymin > y[n]) dBounds.Ymin = y[n];
-				if(dBounds.Ymax < y[n]) dBounds.Ymax = y[n];
-				n++;
-				}
-			}
-		if(!bParZ) {
-			for(i = 0, 	sx = sy = 0.0; i < n; i++) {
-				sx += x[i];			sy += y[i];
-				}
-			dres[2] = sx /n;		dres[3] = sy/n;
-			}
-		else {
-			dres[2] = sx = dres[3] = sy = 0.0;
-			}
-		sxy = sxx = syy = 0.0;
-		for(i = 0; i < n; i++) {
-			dx = x[i]-dres[2];	dy = y[i]-dres[3];
-			sxx += (dx*dx);		syy += (dy*dy);		sxy += (dx*dy);
-			}
-		dres[0] = sxy / sxx;	dres[1] = dres[3] - dres[0] * dres[2];
-		for(i = 0, sdy = 0.0; i < n; i++) {
-			dy = y[i] - (dres[1] + x[i] *dres[0]);
-			sdy += (dy * dy);
-			}
-		df = bParZ ? (n-1) : (n-2);		sdy = sdy/df;					dres[4] = sqrt(sdy/sxx);
-		dres[5] = sxx/(n-1);			dres[6] = syy/(n-1);			dres[7] = sdy;
-		dres[8] = sxy/sdy*sxy/sxx;		dres[9] = f_dist(dres[8], 1.0, df);
-		t = distinv(t_dist, df, 1.0, (100.0-ci)/100.0, 2.0); 
-		dres[10] = t * sqrt(dres[7]/sxx);
-		dres[11] = t * sqrt(dres[7]*(dres[2]*dres[2]/sxx +1.0/(double)n));
-		if (n && (graph = new Graph(parent, data, 0L, 0))) {
-			if(txt_obj = mk_scatt(0, x, y, 0L, 0L, n, "Data", x_desc, y_desc)){
-				OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
-				free(txt_obj);
-				if(LastOpenGO && LastOpenGO->Id >= GO_PLOT && LastOpenGO ->Id < GO_GRAPH) {
-					((Plot*)LastOpenGO)->x_dtype = x_dtype;	((Plot*)LastOpenGO)->y_dtype = y_dtype;
-					((Plot*)LastOpenGO)->x_tv = x_tv;		((Plot*)LastOpenGO)->y_tv = y_tv;
-					}
-				}
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
-				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE));
-			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "f_xy=\"%g+x*%g\\n\"\n", dres[1], dres[0]);
-			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "Desc=\"Regression\"\n");
-			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
-				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE)*0.5);
-			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "f_xy=\"y=%g+x*%g;\\nts=sqrt((((x-(%g))^2)/%g+%g)*%g);\\ny=y+ts*%g\\n\"\n", 
-				dres[1], dres[0], dres[2], sxx, (1.0/(double)n), dres[7], t);
-			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "Desc=\"%g%% C.I.\"\n", ci);
-			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
-				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE)*0.5);
-			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "f_xy=\"y=%g+x*%g;\\nts=sqrt((((x-(%g))^2)/%g+%g)*%g);\\ny=y-ts*%g\\n\"\n", 
-				dres[1], dres[0], dres[2], sxx, (1.0/(double)n), dres[7], t);
-			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "Desc=\"%g%% C.I.\"\n", ci);
-			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
-#else
-			i = sprintf(TmpTxt, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
-				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE));
-			i += sprintf(TmpTxt+i, "f_xy=\"%g+x*%g\\n\"\n", dres[1], dres[0]);
-			i += sprintf(TmpTxt+i, "Desc=\"Regression\"\n");
-			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
-			i = sprintf(TmpTxt, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
-				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE)*0.5);
-			i += sprintf(TmpTxt+i, "f_xy=\"y=%g+x*%g;\\nts=sqrt((((x-(%g))^2)/%g+%g)*%g);\\ny=y+ts*%g\\n\"\n", 
-				dres[1], dres[0], dres[2], sxx, (1.0/(double)n), dres[7], t);
-			i += sprintf(TmpTxt+i, "Desc=\"%g%% C.I.\"\n", ci);
-			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
-			i = sprintf(TmpTxt, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
-				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE)*0.5);
-			i += sprintf(TmpTxt+i, "f_xy=\"y=%g+x*%g;\\nts=sqrt((((x-(%g))^2)/%g+%g)*%g);\\ny=y-ts*%g\\n\"\n", 
-				dres[1], dres[0], dres[2], sxx, (1.0/(double)n), dres[7], t);
-			i += sprintf(TmpTxt+i, "Desc=\"%g%% C.I.\"\n", ci);
-			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
-#endif
-			ts = t * sqrt(dres[7]*((dBounds.Xmax-dres[2])*(dBounds.Xmax-dres[2])/sxx +1.0/(double)n));
-			ty = dBounds.Xmax * dres[0] +dres[1];
-			ly[0] = ty +ts;		ly[1] = ty -ts;
-			ts = t * sqrt(dres[7]*((dBounds.Xmin-dres[2])*(dBounds.Xmin-dres[2])/sxx +1.0/(double)n));
-			ty = dBounds.Xmin * dres[0] +dres[1];
-			ly[2] = ty +ts;		ly[3] = ty -ts;
-			for(i = 0; i < 4; i++) if(ly[i] > -HUGE_VAL && ly[i] < HUGE_VAL) {
-				if(ly[i] < dBounds.Ymin) dBounds.Ymin = ly[i];
-				if(ly[i] > dBounds.Ymax) dBounds.Ymax = ly[i];
-				}
-#ifdef USE_WIN_SECURE
-			if(!bParZ) sprintf_s(TmpTxt, TMP_TXT_SIZE, "y = %g %c %g * x",dres[1],(dres[0] < 0.0 ? '-' : '+'), fabs(dres[0]));
-			else sprintf_s(TmpTxt, TMP_TXT_SIZE, "y = %g * x", fabs(dres[0]));
-#else
-			if(!bParZ) sprintf(TmpTxt, "y = %g %c %g * x",dres[1],(dres[0] < 0.0 ? '-' : '+'), fabs(dres[0]));
-			else sprintf(TmpTxt, "y = %g * x", fabs(dres[0]));
-#endif
-			rep_DrawText(graph, (graph->GetSize(SIZE_DRECT_LEFT) + graph->GetSize(SIZE_DRECT_RIGHT))/2.0,
-				graph->GetSize(SIZE_DRECT_TOP)+txtdef1.fSize/2.0, true, TXA_HCENTER, &txtdef1, TmpTxt);
-			page = new Page(parent, data);					page->Command(CMD_DROP_GRAPH, graph, 0L);
-			graph->moveable = 0;
-			graph->GRect.Xmin += (txtdef1.fSize*5.0);		graph->GRect.Xmax += (txtdef1.fSize*5.0);
-			graph->GRect.Ymin += (txtdef1.fSize*10.0);		graph->GRect.Ymax += (txtdef1.fSize*10.0);
-			mk_header(page, "<b>Linear Regression Analysis</b>", data);
-			res_tab[0][0] = 1;						res_tab[1][0] = df;
-			res_tab[2][0] = df+1.0;					res_tab[0][1] = sxy*sxy/sxx;
-			res_tab[1][1] = syy-res_tab[0][1];		res_tab[2][1] = syy;
-			res_tab[0][2] = res_tab[0][1];			res_tab[1][2] = res_tab[1][1]/df;
-			res_tab[0][3] = dres[8];				res_tab[0][4] = dres[9];
-			dres[12] = res_tab[0][1]/res_tab[2][1];
-			c_y = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef2.fSize*3.0;
-			c_x = graph->GRect.Xmin;
-			c_y = mk_regr_summary(page, c_x, c_y, dres, ci, n);
-			rep_DrawText(page, c_x, c_y, false, TXA_HLEFT, &txtdef1, "<b>Anova:</b>");
-			c_y += txtdef1.fSize*1.5;			mk_table(page, c_x, c_y, 2, res_tab);
-			parent->Command(CMD_DROP_GRAPH, page, 0L);
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;				if(res_tab) {
-		for(i = 0; i < 3; i++) if(res_tab[i]) free(res_tab[i]);
-		free(res_tab);
-		}
-	if(x_desc) free(x_desc);	if(y_desc)free(y_desc);
-	if(x) free(x);				if(y) free(y);
-	if(rX) delete rX;			if(rY) delete rY;		free(RegrDlg);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Kendall's robust line-fit
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *RobLineDlg_Tmpl =
-	"1,+,,DEFAULT,PUSHBUTTON,-1,148,10,45,12\n"
-	".,.,,,PUSHBUTTON,-2,148,25,45,12\n"
-	".,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"4,10,100,ISPARENT | CHECKED,SHEET,1,5,10,130,78\n"
-	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-	"100,+,,,LTEXT,2,10,30,60,8\n"
-	".,.,,,RANGEINPUT,-15,20,40,100,10\n"
-	".,.,,,LTEXT,3,10,55,60,8\n"
-	".,,,LASTOBJ,RANGEINPUT,-16,20,65,100,10";
-
-void
-rep_robustline(GraphObj *parent, DataObj *data)
-{
-	TabSHEET tab1 = {0, 90, 10, "Nonparametric Regression"};
-	DlgInfo *RegrDlg;
-	void *dyndata[] = {(void*)&tab1, (void*)"range for x-data", (void*)"range for y-data"};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, j, k, n, n1, rx, cx, ry, cy, res, align = 0;
-	int x_dtype, y_dtype, nVals, nTxt, nTime;
-	bool bContinue = false, bValid;
-	AccRange *rX = 0L, *rY = 0L;
-	double *x = 0L, *y = 0L, *a = 0L, *b = 0L, c_x, c_y;
-	double dx, dy, slope, intercept;
-	char *txt_obj, *x_desc=0L, *y_desc=0L;
-	scaleINFO scale = {{0.0, 0.45}, {0.0, 0.45}, {0.0, 0.45}};
-	anyResult xRes, yRes;
-	TextValue *x_tv, *y_tv;
-	Plot *plot;
-	Graph *graph;
-	Page *page;
-
-	if(!parent || !data) return;
-	if(!(RegrDlg = CompileDialog(RobLineDlg_Tmpl, dyndata))) return;
-	UseRangeMark(data, 1, TmpTxt, TmpTxt+100);
-	if(!(Dlg = new DlgRoot(RegrDlg, data)))return;
-	hDlg = CreateDlgWnd("Kendall's robust line-fit", 50, 50, 420, 220, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 1:
-			if(rX) delete rX;					if(rY) delete rY;
-			rX = rY = 0L;
-			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) rX = new AccRange(TmpTxt);
-			n = rX ? rX->CountItems() : 0;
-			if(!n) {
-				ErrorBox("Range not specified\nor not valid.");
-				bContinue = true;
-				res = -1;
-				}
-			if(n && Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE) && (rY = new AccRange(TmpTxt))){
-				if(n != rY->CountItems()) {
-					ErrorBox("Both ranges must be given\nand must have the same size");
-					bContinue = true;
-					res = -1;
-					}
-				}
-			}
-		}while (res < 0);
-	for(n1 = n, i = k = 1; i < n; i++) k += i;
-	if(res == 1 && n >1 && rX && rY && (x = (double*)malloc(n*sizeof(double))) 
-		&& (y = (double*)malloc(n*sizeof(double)))
-		&& (a = (double*)malloc(k*sizeof(double)))
-		&& (b = (double*)malloc(k*sizeof(double)))){
-		x_desc = rX->RangeDesc(data, 0);	y_desc = rY->RangeDesc(data, 0);
-		//analyse data types
-		x_dtype = y_dtype = 0;				x_tv = y_tv = 0L;
-		if(rX->DataTypes(data, &nVals, &nTxt, &nTime)){
-			if(!nVals && nTxt > 1 && nTxt > nTime) x_tv = new TextValue();
-			else if(!nVals && nTime > 1 && nTime > nTxt) x_dtype = ET_DATETIME;
-			}
-		if(rY->DataTypes(data, &nVals, &nTxt, &nTime)){
-			if(!nVals && nTxt > 1 && nTxt > nTime) y_tv = new TextValue();
-			else if(!nVals && nTime > 1 && nTime > nTxt) y_dtype = ET_DATETIME;
-			}
-		rX->GetFirst(&cx, &rx);				rY->GetFirst(&cy, &ry);
-		rep_init();
-		dBounds.Xmin = dBounds.Ymin = HUGE_VAL;		dBounds.Xmax = dBounds.Ymax = -HUGE_VAL;
-		//read data into x[] and y[]
-		for(i = n = 0; i < n1 && rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry); i++) {
-			bValid = false;
-			if(data->GetResult(&xRes, rx, cx, false) && data->GetResult(&yRes, ry, cy, false)) {
-				bValid = true;
-				if(x_tv) {
-					if(xRes.type == ET_TEXT) dx = x_tv->GetValue(xRes.text);
-					else bValid = false;
-					}
-				else if(x_dtype == ET_DATETIME) {
-					if(xRes.type == ET_DATE || xRes.type == ET_TIME  || xRes.type == ET_DATETIME) dx = xRes.value;
-					else bValid = false;
-					}
-				else {
-					if(xRes.type == ET_VALUE) dx = xRes.value;
-					else bValid = false;
-					}
-				if(y_tv) {
-					if(yRes.type == ET_TEXT) dy = y_tv->GetValue(yRes.text);
-					else bValid = false;
-					}
-				else if(y_dtype == ET_DATETIME) {
-					if(yRes.type == ET_DATE || yRes.type == ET_TIME  || yRes.type == ET_DATETIME) dy = yRes.value;
-					else bValid = false;
-					}
-				else {
-					if(yRes.type == ET_VALUE) dy = yRes.value;
-					else bValid = false;
-					}
-				}
-			if(bValid){
-				x[n] = dx;				y[n] = dy;
-				if(dBounds.Xmin > x[n]) dBounds.Xmin = x[n];
-				if(dBounds.Xmax < x[n]) dBounds.Xmax = x[n];
-				if(dBounds.Ymin > y[n]) dBounds.Ymin = y[n];
-				if(dBounds.Ymax < y[n]) dBounds.Ymax = y[n];
-				n++;
-				}
-			}
-		SortArray2(n, x, y);
-		for(i = k = 0; i < (n-1); i++) for(j = i+1; j < n; j++) {
-			if(x[i] != x[j]) {
-				b[k] = (y[j] - y[i])/(x[j] - x[i]);
-				a[k] = y[i] - b[k]*x[i];	k++;
-				}
-			}
-		d_quartile(k, b, 0L, &slope, 0L);		d_quartile(k, a, 0L, &intercept, 0L);
-		slope = slope;		intercept = intercept;
-		if (n && (graph = new Graph(parent, data, 0L, 0))) {
-			if(txt_obj = mk_scatt(0, x, y, 0L, 0L, n, "Data", x_desc, y_desc)){
-				OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
-				free(txt_obj);
-				if(LastOpenGO && LastOpenGO->Id >= GO_PLOT && LastOpenGO ->Id < GO_GRAPH) {
-					((Plot*)LastOpenGO)->x_dtype = x_dtype;	((Plot*)LastOpenGO)->y_dtype = y_dtype;
-					((Plot*)LastOpenGO)->x_tv = x_tv;		((Plot*)LastOpenGO)->y_tv = y_tv;
-					}
-				}
-#ifdef USE_WIN_SECURE
-			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
-				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE));
-			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "f_xy=\"%g+x*%g\\n\"\n", intercept, slope);
-			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "Desc=\"Fitted Line\"\n");
-			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "y = %g %c %g * x",intercept,(slope < 0.0 ? '-' : '+'), fabs(slope));
-#else
-			i = sprintf(TmpTxt, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
-				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE));
-			i += sprintf(TmpTxt+i, "f_xy=\"%g+x*%g\\n\"\n", intercept, slope);
-			i += sprintf(TmpTxt+i, "Desc=\"Fitted Line\"\n");
-			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
-			sprintf(TmpTxt, "y = %g %c %g * x", intercept, (slope < 0.0 ? '-' : '+'), fabs(slope));
-#endif
-			rep_DrawText(graph, (graph->GetSize(SIZE_DRECT_LEFT) + graph->GetSize(SIZE_DRECT_RIGHT))/2.0,
-				graph->GetSize(SIZE_DRECT_TOP)+txtdef1.fSize/2.0, true, TXA_HCENTER, &txtdef1, TmpTxt);
-			page = new Page(parent, data);					page->Command(CMD_DROP_GRAPH, graph, 0L);
-			graph->moveable = 0;
-			graph->GRect.Xmin += (txtdef1.fSize*5.0);		graph->GRect.Xmax += (txtdef1.fSize*5.0);
-			graph->GRect.Ymin += (txtdef1.fSize*9.0);		graph->GRect.Ymax += (txtdef1.fSize*9.0);
-			mk_header(page, "<b>Kendall's Robust Line-Fit</b>", data);
-			c_y = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef2.fSize*2.0;
-			c_x = graph->GRect.Xmin;
-			j = (int)isqr(k);				i = (int)((double)k/10.0);
-			if(j < 8) j = 8;
-			else if (j >40) j = 40;
-			scale.sx.fx = (graph->GRect.Xmin + graph->GRect.Xmax)/2.0;
-			scale.sy.fx = c_y;
-			graph = new Graph(parent, data, 0L, 0);
-			if(plot = new FreqDist(graph, data, b+(i>>1), k-i, j)){
-				if(plot->x_info = (char*)malloc(30)) rlp_strcpy(plot->x_info, 30, "90% of all slopes");
-				if(plot->y_info = (char*)malloc(30)) rlp_strcpy(plot->y_info, 30, "No. of observations");
-				if(!(graph->Command(CMD_DROP_PLOT, plot, 0L))) delete(plot);
-				}
-			graph->Command(CMD_SCALE, &scale, 0L);
-			page->Command(CMD_DROP_GRAPH, graph, 0L);
-			mk_median_report(page, c_x, c_y, b, k, .95, "Slope");
-			scale.sy.fx = c_y = graph->GRect.Ymax + txtdef1.fSize;
-			graph = new Graph(parent, data, 0L, 0);
-			if(plot = new FreqDist(graph, data, a+(i>>1), k-i, j)){
-				if(plot->x_info = (char*)malloc(30)) rlp_strcpy(plot->x_info, 30, "90% of all intercepts");
-				if(plot->y_info = (char*)malloc(30)) rlp_strcpy(plot->y_info, 30, "No. of observations");
-				if(!(graph->Command(CMD_DROP_PLOT, plot, 0L))) delete(plot);
-				}
-			graph->Command(CMD_SCALE, &scale, 0L);
-			page->Command(CMD_DROP_GRAPH, graph, 0L);
-			c_y = mk_median_report(page, c_x, c_y, a, k, .95, "Intercept");
-			parent->Command(CMD_DROP_GRAPH, page, 0L);
-			}
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(x_desc) free(x_desc);	if(y_desc)free(y_desc);
-	if(x) free(x);				if(y) free(y);
-	if(a) free(a);				if(b) free(b);
-	if(rX) delete rX;			if(rY) delete rY;		free(RegrDlg);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Correlation reports
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *RepCorrel_DlgTmpl =
-		"1,2,,DEFAULT,PUSHBUTTON,-1,158,10,45,12\n"
-		"2,3,,,PUSHBUTTON,-2,158,25,45,12\n"
-		"3,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-		"10,11,152,ISPARENT | CHECKED, SHEET,1,5,10,140,70\n"
-		"11,20,200,ISPARENT | TOUCHEXIT,SHEET,2,5,10,140,70\n"
-		"20,21,,CHECKED,CHECKPIN,0,5,0,12,8\n"
-		"21,22,,CHECKED,CHECKBOX,7,25,85,130,9\n"
-		"22,23,,,LTEXT,8,35,95,50,9\n"
-		"23,24,,,EDVAL1,9,87,95,30,10\n"
-		"24,,,,LTEXT,-10,120,95,10,9\n"
-		"152,153,,ISPARENT | CHECKED,GROUPBOX,3,12,30,128,45\n"
-		"153,154,,,LTEXT,0,25,35,60,8\n"
-		"154,155,,,RANGEINPUT,0,25,45,100,10\n"
-		"155,156,0,,PUSHBUTTON,-8,95,57,30,12\n"
-		"156,,,,PUSHBUTTON,-9,60,57,35,12\n"
-		"200,201,,TOUCHEXIT,RADIO1,4,25,30,60,8\n"
-		"201,202,,TOUCHEXIT,RADIO1,5,25,45,60,8\n"
-		"202,,,TOUCHEXIT,RADIO1,6,25,60,60,8\n"
-		"300,,,LASTOBJ,NONE,0,0,0,0,0";
-static int use_corr = 2;
-static double use_ci = 95.0;
-
-void rep_correl(GraphObj *parent, DataObj *data, int style)
-{
-	TabSHEET tab1 = {0, 25, 10, "Data"};
-	TabSHEET tab2 = {25, 57, 10, "Method"};
-	
-	void *dyndata[] = {(void*)&tab1, (void*)&tab2, 
-		(void*)" select one range for every variable ", (void*)" Pearsons product moment",
-		(void*)" Spearmans rank correlation", (void*)" Kendalls Tau", (void*)" highlight significant correlations,",
-		(void*)"sigificance level", (void*)&use_ci};
-	DlgInfo *CorrelDlg;
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, j, res, nr, currYR = 0, maxYR = 0, ny, corr;
-	int r1, c1, r2, c2, n, cb;
-	bool updateYR = true, bContinue = false, bMtab = false, bHiLite;
-	double lmarg, line_inc, *v1, *v2, val1, val2, cx, cy, r,dn, p, sf, ra[20], cl;
-	char **rd = 0L, *txt_obj, *info1, *info2;
-	scaleINFO scale = {{0.0, 0.14}, {0.0, 0.14}, {0.0, 0.14}};
-	AccRange *rV1 = 0L, *rV2 = 0L;
-	TextDEF mtext;
-	Plot *plot;
-	Graph *graph;
-	Page *page;
-
-	if(!parent || !data) return;
-	info1 = info2 = 0L;
-	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
-		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return;
-	if(!(CorrelDlg = CompileDialog(RepCorrel_DlgTmpl, dyndata))) return;
-	if(TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
-		for(i=j=0; i <= 1000; i +=100) if(TmpTxt[i]) 
-			rd[j++] = (char*)memdup(TmpTxt+i, ((int)strlen(TmpTxt+i))+2, 0);	 maxYR = j-1;
-		}
-	if(!rd && !(rd = (char**)calloc(1, sizeof(char*))))return;
-	if(!(Dlg = new DlgRoot(CorrelDlg, data))) return;
-	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
-	switch(use_corr) {
-	case 1:			Dlg->SetCheck(200, 0L, true);			corr = 1;			break;
-	default:		Dlg->SetCheck(201, 0L, true);			corr = 2;			break;
-	case 3:			Dlg->SetCheck(202, 0L, true);			corr = 3;			break;
-		}
-	hDlg = CreateDlgWnd(style? (char*)"Create Tiled Correlation Plots" : 
-		(char*)"Create a Correlation Matrix", 50, 50, 420, 252, Dlg, 0x0L);
-	do {
-		if(updateYR) {
-			if(currYR >0) Dlg->ShowItem(156, true);
-			else Dlg->ShowItem(156, false);
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "variable # %d/%d", currYR+1, maxYR+1);
-#else
-			sprintf(TmpTxt,"variable # %d/%d", currYR+1, maxYR+1);
-#endif
-			Dlg->SetText(153, TmpTxt);
-			updateYR = false;
-			}
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-		case 0:
-			if(bContinue || Dlg->GetCheck(20)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 11:					//select correlation method
-			bMtab = true;	res =-1;	break;
-		case 200:					//select pearson
-		case 201:					//select spearman
-		case 202:					//select kendall
-			corr = res-199;		res =-1;	break;
-		case 1:
-			if(!bMtab) {
-				Dlg->SetCheck(11, 0L, true);
-				bMtab = true;	res = -1;	
-				break;
-				}
-		case 155:		case 156:
-			res = com_StackDlg(res, Dlg, 0L, 0L, &rd, &currYR,
-				&rV1, &bContinue, &ny, &maxYR, &updateYR);
-			break;
-			}
-		}while (res < 0);
-
-	if(res ==1) {
-		if(bHiLite = Dlg->GetCheck(21)) {
-			Dlg->GetValue(23, &use_ci);		cl = 1.0 - use_ci/100.0;
-			}
-		maxYR++;	rep_init();		page = new Page(parent, data);
-		if(rV1) delete rV1;			rV1 = 0L;		use_corr = corr;
-		switch(corr) {
-		case 1:
-			mk_header(page, "<b>Pearsons product moment correlations</b>", data);
-			break;
-		case 2:
-			mk_header(page, "<b>Spearmans rank correlations</b>", data);
-			break;
-		case 3:
-			mk_header(page, "<b>Kendalls non parametric correlations</b>", data);
-			break;
-		default:
-			mk_header(page, "<b>### Correlation Error ###</b>", data);
-			break;
-			}
-		memcpy(&mtext, &txtdef1, sizeof(TextDEF));
-		if(style == 0) {
-			lmarg = txtdef1.fSize*12.0;
-			cy = txtdef1.fSize*13.0;	cx = txtdef1.fSize*6.0;
-			line_inc = linsp1;
-			sf = (page->GetSize(SIZE_GRECT_RIGHT)-lmarg-linsp1)/(cx *(double)maxYR);
-			if(sf< 1.0) {
-				cx *= sf;		line_inc *= sf;		lmarg *= sf;
-				mtext.fSize *= sf;		mtext.iSize = 0;
-				}
-			}
-		else {
-			lmarg = txtdef1.fSize*8.0;
-			cy = txtdef1.fSize*13.0;
-			cx = (page->GetSize(SIZE_GRECT_RIGHT)-txtdef1.fSize*3.0-lmarg)/maxYR;
-			switch(defs.cUnits) {
-			default:
-				scale.sx.fy = scale.sy.fy = scale.sz.fy = cx/165.0;			break;
-			case 1:
-				scale.sx.fy = scale.sy.fy = scale.sz.fy = cx/16.50;			break;
-			case 2:
-				scale.sx.fy = scale.sy.fy = scale.sz.fy = cx/6.49606;		break;
-				}
-			line_inc = cx/1.44;
-			}
-		for(nr = maxYR, i = 0; i < nr; i++) for(j = 0; j < nr; j++) {
-			if(i == 0 &&(rV1 = new AccRange(rd[j]))) {	//first row
-				if(info1 = rV1->RangeDesc(data, style == 0 || nr > 5 ? 1 : 2)) {
-					if(style == 0)
-						rep_DrawText(page, lmarg+cx*j, cy-txtdef1.fSize*2.0, false, TXA_HCENTER, &mtext, info1);
-					else if(style == 1)
-						rep_DrawText(page, lmarg+cx*j+cx/2.0, cy-txtdef1.fSize*2.0, false, TXA_HCENTER, &mtext, info1);
-					free(info1);	info1 = 0L;
-					}
-				delete rV1;			rV1 = 0L;
-				}
-			if(j == 0 &&(rV1 = new AccRange(rd[i]))) {	//first column
-				if(info1 = rV1->RangeDesc(data, 1)) {
-					if(style == 0) 
-						rep_DrawText(page, lmarg-cx/2.0-txtdef1.fSize, cy+line_inc, false, TXA_HRIGHT, &mtext, info1);
-					else if(style == 1)
-						rep_DrawText(page, lmarg-txtdef1.fSize, cy+line_inc/2.0-mtext.fSize/2.0, false, TXA_HRIGHT, &mtext, info1);
-					free(info1);	info1 = 0L;
-					}
-				delete rV1;			rV1 = 0L;
-				}
-			if(i == j) {					//self correlation: do something else ...
-				if(style == 0) {			//correlation matrix
-					rep_DrawText(page, lmarg+cx*j, cy+line_inc, false, TXA_HCENTER, &mtext, "---");
-					}
-				else if(style = 1) {		//tiled plots
-					graph = new Graph(parent, data, 0L, 0);
-					scale.sx.fx = lmarg+cx*j;			scale.sy.fx = cy;
-					if(plot = new FreqDist(graph, data, rd[i], true)){
-						if(rV1 = new AccRange(rd[i])){
-							plot->x_info = rV1->RangeDesc(data, 2);
-							delete rV1;		rV1 = 0L;
-							}
-						if(!(graph->Command(CMD_DROP_PLOT, plot, 0L))) delete(plot);
-						}
-					graph->Command(CMD_SCALE, &scale, 0L);
-					page->Command(CMD_DROP_GRAPH, graph, 0L);
-					}
-				}
-			else {
-				rV1 = new AccRange(rd[i]);	rV2 = new AccRange(rd[j]);
-				v1 = (double*)malloc((rV1->CountItems()+1) * sizeof(double));
-				v2 = (double*)malloc((rV2->CountItems()+1) * sizeof(double));
-				dBounds.Xmin = dBounds.Ymin = HUGE_VAL;		dBounds.Xmax = dBounds.Ymax = -HUGE_VAL;
-				rV1->GetFirst(&c1, &r1);	rV2->GetFirst(&c2, &r2);
-				//copy values into arrays
-				for(n = 0; rV1->GetNext(&c1, &r1) && rV2->GetNext(&c2, &r2); ) {
-					if(data->GetValue(r1, c1, &val1) && data->GetValue(r2, c2, &val2)) {
-						if(dBounds.Xmin > val1) dBounds.Xmin = val1;
-						if(dBounds.Xmax < val1) dBounds.Xmax = val1;
-						if(dBounds.Ymin > val2) dBounds.Ymin = val2;
-						if(dBounds.Ymax < val2) dBounds.Ymax = val2;
-						v1[n] = val1;	v2[n] = val2;	n++;
-						}
-					}
-				//do correlation
-				dn = n;			r = 0.0;
-				if(n) switch(corr) {
-				case 1:
-					d_pearson(v1, v2, n, 0L, 0L, ra);
-					r = ra[0];	p = ra[2];	dn = ra[3];
-					break;
-				case 2:
-					d_spearman(v1, v2, n, 0L, 0L, ra);
-					r = ra[3];	p = ra[4];	dn = ra[5];
-					break;
-				case 3:
-					d_kendall(v1, v2, n, 0L, 0L, ra);
-					r = ra[0];	p = ra[2];	dn = ra[3];
-					break;
-				default:
-					r = 0.0;	dn = 0.0;	p = 1.0;			break;
-					}
-				//process result
-				if(dn > 1.0 && style == 0) {			//correlation matrix
-					if(bHiLite && p < cl && (txt_obj = mk_rect(lmarg+cx*j-cx/2.1, cy-line_inc/4.0, lmarg+cx*j+cx/2.1, 
-						cy+line_inc*3.25, 0x0000ffffL, 0x0080ffffL))) {
-						OpenGraph(page, 0L, (unsigned char*)txt_obj, false);
-						free(txt_obj);
-						}
-					dbl_to_str1(TmpTxt, 80, "%g", r);
-					rep_DrawText(page, lmarg+cx*j, cy, false, TXA_HCENTER, &mtext, TmpTxt);
-					dbl_to_str1(TmpTxt, 80, "n = %g", dn);
-					rep_DrawText(page, lmarg+cx*j, cy+line_inc, false, TXA_HCENTER, &mtext, TmpTxt);
-					dbl_to_str1(TmpTxt, 80, p < 0.001 ? (char*)"P < 0.0001" : (char*)"P = %.4lf", p);
-					rep_DrawText(page, lmarg+cx*j, cy+line_inc*2.0, false, TXA_HCENTER, &mtext, TmpTxt);
-					if(j == (nr-1)) cy += (line_inc*4.0);
-					}
-				else if(style == 0) {					//corr. matrix but no data
-					if(j == (nr-1)) cy += (line_inc*4.0);
-					}
-				else if(style == 1) {		//tiled plots
-					graph = new Graph(parent, data, 0L, 0);
-					scale.sx.fx = lmarg+cx*j;			scale.sy.fx = cy;
-					info1 = rV1->RangeDesc(data, 2);	info2 = rV2->RangeDesc(data, 2);
-					if(txt_obj = mk_scatt(0, v1, v2, 0L, 0L, n, "Data", info1, info2)){
-						OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
-						free(txt_obj);
-						}
-					if(info1) free(info1);				if(info2) free(info2);
-					info1 = info2 = 0L;
-					if(bHiLite && p < cl) {
-						graph->SetColor(COL_GRECT, 0x0000ffffL);	graph->SetColor(COL_DRECT, 0x00c0ffffL);
-						}
-					switch(corr) {
-					case 1:
-						cb = dbl_to_str2(TmpTxt, 80, "r = %.4lf, n = %g, ", r, dn);				break;
-					case 2:
-						cb = dbl_to_str2(TmpTxt, 80, "r<sub>S</sub> = %.4lf, n = %g, ", r, dn);	break;
-					case 3:
-						cb = dbl_to_str2(TmpTxt, 80, "r<sub>K</sub> = %.4lf, n = %g, ", r, dn);	break;
-					default:
-						TmpTxt[0] = 0;		cb = 0;		break;
-						}
-					dbl_to_str1(TmpTxt+cb, 80, p < 0.001 ? (char*)"P < 0.0001" : (char*)"P = %.4lf", p);
-					rep_DrawText(graph, graph->GetSize(SIZE_DRECT_LEFT)+txtdef1.fSize, 
-						graph->GetSize(SIZE_DRECT_TOP)+txtdef1.fSize, false, TXA_HLEFT, &txtdef1, TmpTxt);
-					if(LastOpenGO)LastOpenGO->SetColor(COL_TEXT, 0x00cb0000L);
-					graph->Command(CMD_SCALE, &scale, 0L);
-					if(dn > 1.0) page->Command(CMD_DROP_GRAPH, graph, 0L);
-					if(j == (nr-1)) cy += line_inc;
-					}
-				free(v1);		free(v2);
-				if(rV1)delete rV1;		if(rV2)delete rV2;		rV1 = rV2 = 0L;
-				}
-			}
-		parent->Command(CMD_DROP_GRAPH, page, 0L);
-		}
-	CloseDlgWnd(hDlg);
-	delete Dlg;
-	if(rd) {
-		for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
-		free(rd);
-		}
-	if(rV2) delete rV2;		if(rV1) delete rV1;		free(CorrelDlg);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// 2x2 table
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-static char *twDlg_Tmpl =
-	"1,2,100,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"2,3,400,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"3,4,600,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
-	"4,5,,DEFAULT,PUSHBUTTON,-1,168,10,45,12\n"
-	"5,,,,PUSHBUTTON,2,168,25,45,12\n"
-	"100,101,,,CTEXT,1,35,10,40,8\n"
-	"101,102,,,EDTEXT,0,35,20,40,10\n"
-	"102,103,,,EDTEXT,0,77,20,40,10\n"
-	"103,104,,,EDTEXT,0,35,32,40,10\n"
-	"104,105,,,EDTEXT,0,77,32,40,10\n"
-	"105,106,,,LTEXT,3,10,20,40,8\n"
-	"106,107,,,LTEXT,4,10,32,40,8\n"
-	"107,,,,CTEXT,5,77,10,40,8\n"
-	"400,401,,,EDTEXT,0,119,20,40,10\n"
-	"401,402,,,EDTEXT,0,119,32,40,10\n"
-	"402,403,,,EDTEXT,0,35,44,40,10\n"
-	"403,404,,,EDTEXT,0,77,44,40,10\n"
-	"404,405,,,EDTEXT,0,119,44,40,10\n"
-	"405,406,,,CTEXT,6,119,10,40,8\n"
-	"406,407,,,LTEXT,7,10,44,40,8\n"
-	"407,408,,,LTEXT,2,35,59,40,8\n"
-	"408,409,,,LTEXT,2,35,69,40,8\n"
-	"409,410,,,LTEXT,8,119,59,60,8\n"
-	"410,,,,LTEXT,0,119,69,60,8\n"
-	"600,601,,DEFAULT,PUSHBUTTON,-1,128,10,45,12\n"
-	"601,,,LASTOBJ,PUSHBUTTON,-2,128,25,45,12";
-
-void rep_twowaytable(GraphObj *parent, DataObj *data)
-{
-	DlgInfo *twDlg;
-	void *dyndata[] = {(void*)"Group A", (void*)"Close", (void*)"Case 1", (void*)"Case 2",
-		(void*)"Group B", (void*)"A + B", (void*)"C1+C2", (void*)"Fisher's exact:"};
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, r, c, level, res, wcc;
-	int v_idx[] = {101,102,400,103,104,401,402,403,404};
-	double tmp, v[9], chi2, p, dn, pf, pfa[128];
-	char *rng;
-	AccRange *ar;
-
-	if(!parent || !data) return;
-	if(!(twDlg = CompileDialog(twDlg_Tmpl, dyndata))) return;
-	if(!(Dlg = new DlgRoot(twDlg, data)))return;
-	for(i = 400; i < 405; i++) Dlg->Activate(i, false);
-	if(data->Command(CMD_GETMARK, &rng, 0L) && rng && rng[0] && (ar = new AccRange(rng)) && ar->GetFirst(&c, &r)) {
-		for(i = 0; i < 4 && ar->GetNext(&c, &r); ) {
-			if(data->GetValue(r, c, &tmp)) {
-				Dlg->SetValue(101+i, tmp);				i++;
-				}
-			}
-		delete ar;
-		if(i == 4) Dlg->ItemCmd(600, CMD_ENDDIALOG, 0L);
-		}
-	level = wcc = 0;
-	Dlg->ShowItem(2, false);		Dlg->ShowItem(4, false);	
-	Dlg->ShowItem(5, false);	
-	hDlg = CreateDlgWnd("2x2 Table", 50, 50, 450, 200, Dlg, 0);
-	ResizeDlgWnd(hDlg, 370, 150);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch(res) {
-			case 0:
-				res = -1;
-				break;
-			case 600:			//level 0 OK
-				Dlg->ShowItem(2, true);			Dlg->ShowItem(4, true);	
-				Dlg->ShowItem(5, true);			Dlg->ShowItem(3, false);
-				ResizeDlgWnd(hDlg, 450, 200);	Dlg->Command(CMD_REDRAW, 0L, 0L);
-				level = 1;
-			case 4:				//level 1 OK
-				for(i = 0; i < 9; i++) {
-					v[i] = 0.0;					Dlg->GetValue(v_idx[i], &v[i]);
-					v[i] = fabs(floor(v[i]));
-					}
-				v[2] = v[0] + v[1];				v[5] = v[3] + v[4];
-				v[6] = v[0] + v[3];				v[7] = v[1] + v[4];
-				v[8] = v[6] + v[7];				chi2 = v[0]*v[4]-v[1]*v[3];
-				for(i = wcc = 0; i < 9; i++) {
-					dbl_to_str1(TmpTxt, TMP_TXT_SIZE, "%g", v[i]);
-					Dlg->SetText(v_idx[i], TmpTxt);
-					}
-				if(v[8] < 128.0) do {
-					pf = factrl((int)v[2])/factrl((int)v[0])*factrl((int)v[5])/factrl((int)v[1])
-						*factrl((int)v[6])/factrl((int)v[3])*factrl((int)v[7])/factrl((int)v[4]);
-					pf /= factrl((int)v[8]);
-					pfa[wcc++] = pf;
-					//worse case correction
-					//RR Sokal & FJ Rohlf: Biometry, 3rd ed., pp. 734 ff.
-					if((v[0]*v[4]- v[1]*v[3]) < 0.0) {
-						v[0]-=1.0;	v[4]-=1.0;	v[1]+=1.0;	v[3]+=1.0;
-						}
-					else if((v[0]*v[4]- v[1]*v[3]) > 0.0) {
-						v[0]+=1.0;	v[4]+=1.0;	v[1]-=1.0;	v[3]-=1.0;
-						}
-					else break;
-					}while(v[0]>=0.0 && v[1]>=0.0 && v[3]>=0.0 && v[4]>=0.0 && wcc < 128);
-				if(wcc){
-					for(i = 1, pf = pfa[0]; i < wcc; i++){
-						pf += pfa[i];
-						}
-					if(pf > 1.0) pf = 1.0;
-					dbl_to_str1(TmpTxt, TMP_TXT_SIZE, "P(one sided) = %.4lf", pf);
-					Dlg->SetText(410, pf >= 0.001 ? TmpTxt : (char*)"P < 0.001");
-					}
-				else Dlg->SetText(410, "- - -");
-				dn = (v[2]*v[5]*v[6]*v[7]);
-				chi2 = dn > 0.0  ? (chi2*chi2*v[8])/dn : 0.0;
-				p = chi_dist(chi2, 1.0, 1.0);
-				dbl_to_str1(TmpTxt, TMP_TXT_SIZE, "Chi2 = %g", chi2);
-				Dlg->SetText(407, TmpTxt);
-				if(p >= 0.001) {
-					dbl_to_str1(TmpTxt, TMP_TXT_SIZE, "P = %g", p);
-					Dlg->SetText(408, TmpTxt);
-					}
-				else Dlg->SetText(408, "P < 0.001");
-				Dlg->Command(CMD_REDRAW, 0L, 0L);
-				res= -1;
-				break;
-			}
-		}while (res < 0);
-	CloseDlgWnd(hDlg);
-	delete Dlg;		free(twDlg);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// compare means / medians of two groups
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void rep_compmeans(GraphObj *parent, DataObj *data)
-{
-	TabSHEET tab1 = {0, 42, 10, "Data Input"};
-	double ci = 95.0;
-	DlgInfo *MeanDlg;
-	void *dyndata[] = {(void*)&tab1, (void*)"range for first variable",
-		(void*)"range for second variable", (void*)"confidence interval:",
-		(void*)&ci, (void*)" "};
-	char *ttest[] = {"Student's t = %g", "P = %g", "P(corr.) = %g"};
-	char *utest[] = {"Mann-Whitney U = %g", "z = %g", "P = %g", "z(corr.) = %g", "P(corr.) = %g"};
-	char g1_nam[30], g2_nam[30], *c_name;
-	DlgRoot *Dlg;
-	void *hDlg;
-	int i, j, res, n1, n2, r, c, *ny;
-	bool bContinue = false;
-	double *d1, *d2, dtmp, *rs, cx, cy, min1,max1, min2, max2;
-	scaleINFO scale = {{0.0, 0.9}, {0.0, 0.9}, {0.0, 0.9}};
-	char *txt_obj;
-	anyResult ares;
-	AccRange *rD;
-	Graph *graph;
-	Page *page;
-
-	if(!parent || !data) return;
-	if(!(MeanDlg = CompileDialog(RegrDlg_Tmpl, dyndata))) return;
-	UseRangeMark(data, 1, TmpTxt, TmpTxt+100);
-	if(!(Dlg = new DlgRoot(MeanDlg, data)))return;
-	Dlg->ShowItem(107, false);
-	d1 = d2 = 0L;
-	hDlg = CreateDlgWnd("Compare Means", 50, 50, 420, 260, Dlg, 0x0L);
-	do {
-		LoopDlgWnd();
-		res = Dlg->GetResult();
-		switch (res) {
-		case 0:
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(10)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
-			break;
-		case 1:
-			if(d1) free(d1);		if(d2) free(d2);		d1 = d2 = 0L;
-			min1 = min2 = dBounds.Ymin = HUGE_VAL;		max1 = max2 = dBounds.Ymax = -HUGE_VAL;
-			if(Dlg->GetText(101,TmpTxt,TMP_TXT_SIZE)&&(rD=new AccRange(TmpTxt))&&(n1=rD->CountItems())&&(d1=(double*)malloc(n1*sizeof(double)))){
-				if(c_name = rD->RangeDesc(data, 2)) {
-					rlp_strcpy(g1_nam, 30, c_name);		g1_nam[0] = toupper(g1_nam[0]);
-					free(c_name);
-					}
-				else rlp_strcpy(g1_nam, 30, "Group 1");
-				for(n1 = 0, rD->GetFirst(&c, &r); rD->GetNext(&c, &r); ) {
-					if(data->GetResult(&ares, r, c, false) && ares.type == ET_VALUE){
-						if(dBounds.Ymin > ares.value) dBounds.Ymin = ares.value;
-						if(dBounds.Ymax < ares.value) dBounds.Ymax = ares.value;
-						if(min1 > ares.value) min1 = ares.value;		if(max1 < ares.value) max1 = ares.value;
-						d1[n1++] = ares.value;
-						}
-					}
-				delete rD;
-				}
-			if(Dlg->GetText(103,TmpTxt,TMP_TXT_SIZE)&&(rD=new AccRange(TmpTxt))&&(n2=rD->CountItems())&&(d2=(double*)malloc(n2*sizeof(double)))){
-				if(c_name = rD->RangeDesc(data, 2)) {
-					rlp_strcpy(g2_nam, 30, c_name);		g2_nam[0] = toupper(g2_nam[0]);
-					free(c_name);
-					}
-				else rlp_strcpy(g2_nam, 30, "Group 2");
-				for(n2 = 0, rD->GetFirst(&c, &r); rD->GetNext(&c, &r); ) {
-					if(data->GetResult(&ares, r, c, false) && ares.type == ET_VALUE){
-						if(dBounds.Ymin > ares.value) dBounds.Ymin = ares.value;
-						if(dBounds.Ymax < ares.value) dBounds.Ymax = ares.value;
-						if(min2 > ares.value) min2 = ares.value;		if(max2 < ares.value) max2 = ares.value;
-						d2[n2++] = ares.value;
-						}
-					}
-				delete rD;
-				}
-			if(g1_nam[0] && g2_nam[0] && 0==strcmp(g1_nam, g2_nam)) {
-				rlp_strcpy(g1_nam, 30, "Group 1");		rlp_strcpy(g2_nam, 30, "Group 2");
-				}
-			if(!d1 || !d2 || n1 < 2 || n2 < 2) {
-				InfoBox("Insufficient data to calculate means!");
-				bContinue = true;
-				res = -1;
-				}
-			Dlg->GetValue(105, &ci);
-			break;
-			}
-		}while (res < 0);
-	if(res == 1 && d1 && d2 && n1>1 && n2>1 && (rs = (double*)malloc(40*sizeof(double))) && (ny = (int*)malloc(2*sizeof(int)))) {
-		dBounds.Xmin = 0.5;		rs[0] = 1.0;		dBounds.Xmax = 2.3;		rs[1] = 2.0;
-		dtmp = d_variance(n1, d1, &rs[2], 0L);		rs[10] = sqrt(dtmp);
-		dtmp = d_variance(n2, d2, &rs[3], 0L);		rs[11] = sqrt(dtmp);
-		rs[12] = (double)n1;						rs[13] = (double)n2;
-		rs[6] = rs[10]/sqrt(rs[12]);				rs[7] = rs[11]/sqrt(rs[13]);
-		rs[4] = rs[2] - rs[6];						rs[5] = rs[3] - rs[7];
-		rs[6] += rs[2];								rs[7] += rs[3];
-		rs[8] = rs[2] - rs[10];						rs[9] = rs[3] - rs[11];
-		rs[10] += rs[2];							rs[11] += rs[3];
-		ny[0] = n1;									ny[1] = n2;
-		rep_init();									page = new Page(parent, data);
-		ci /= 100.0;
-		mk_header(page, "<b>Compare Means of Two Groups</b>", data);
-		if((graph = new Graph(parent, data, 0L, 0)) && (txt_obj = mk_boxplot(0, rs, rs+2, rs+4, rs+6, rs+8, rs+10,
-				ny, 2,"Mean","Std. Err.","Std. Dev."))){
-			scale.sx.fx = (txtdef1.fSize*5.0);			scale.sy.fx = (txtdef1.fSize*10.0);
-			graph->GRect.Xmax = defs.GetSize(SIZE_GRECT_BOTTOM);
-			graph->DRect.Xmin *= 0.8;					graph->moveable = 0;
-			graph->DRect.Xmax = graph->GRect.Xmax - (txtdef1.fSize*2.0);
-			OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
-			if(LastOpenGO && LastOpenGO->Id == GO_BOXPLOT) {
-				if(((BoxPlot*)LastOpenGO)->x_tv = new TextValue()){
-					((BoxPlot*)LastOpenGO)->x_tv->GetValue(g1_nam);
-					((BoxPlot*)LastOpenGO)->x_tv->GetValue(g2_nam);
-					}
-				}
-			free(txt_obj);						graph->Command(CMD_SCALE, &scale, 0L);
-			cx = graph->GetSize(SIZE_GRECT_RIGHT)+txtdef1.fSize*3.0;
-			cy = mk_mean_report(page, cx, graph->GetSize(SIZE_GRECT_TOP), d1, n1, ci, g1_nam);
-			cy = mk_mean_report(page, cx, cy + txtdef1.fSize, d2, n2, ci, g2_nam);
-			cy += linsp1;
-			rep_DrawText(page, graph->GetSize(SIZE_GRECT_RIGHT)+txtdef1.fSize*3.0, cy, false, TXA_HLEFT, &txtdef1, "<b>t-Test:</b>");
-			cy += linsp1;						d_ttest(d1, d2, n1, n2, 0L, 0L, rs+15);
-			for(i = 0; i < 3; i++) {
-				switch(i) {
-					case 0:			dtmp = rs[24];			break;
-					case 1:			dtmp = rs[21];			break;
-					case 2:			dtmp = rs[23];			break;
-					}
-#ifdef USE_WIN_SECURE
-				j = sprintf_s(TmpTxt, 80, ttest[i], dtmp);
-#else
-				j = sprintf(TmpTxt, ttest[i], dtmp);
-#endif
-				if(i && dtmp < 0.0001) {
-					while(TmpTxt[j] != '=' && j) j--;
-					rlp_strcpy(TmpTxt+j, 10, "< 0.0001");
-					}
-				rep_DrawText(page, cx+(txtdef1.fSize*3.0), cy, false, TXA_HLEFT, &txtdef1, TmpTxt);
-				cy += linsp1/1.2;
-				}
-			page->Command(CMD_DROP_GRAPH, graph, 0L);
-			}
-		d_quartile(n1, d1, &rs[6], &rs[2], &rs[4]);
-		d_quartile(n2, d2, &rs[7], &rs[3], &rs[5]);
-		rs[8] = min1;	rs[9] = min2;	rs[10] = max1;	rs[11] = max2;
-		cy = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef2.fSize*3.0;
-		if((graph = new Graph(parent, data, 0L, 0)) && (txt_obj = mk_boxplot(0, rs, rs+2, rs+4, rs+6, rs+8, rs+10,
-				ny, 2,"Median","25-75%","Min./Max."))){
-			scale.sy.fx = cy;
-			graph->GRect.Xmax = defs.GetSize(SIZE_GRECT_BOTTOM);
-			graph->DRect.Xmin *= 0.8;					graph->moveable = 0;
-			graph->DRect.Xmax = graph->GRect.Xmax - (txtdef1.fSize*2.0);
-			OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
-			if(LastOpenGO && LastOpenGO->Id == GO_BOXPLOT) {
-				if(((BoxPlot*)LastOpenGO)->x_tv = new TextValue()){
-					((BoxPlot*)LastOpenGO)->x_tv->GetValue(g1_nam);
-					((BoxPlot*)LastOpenGO)->x_tv->GetValue(g2_nam);
-					}
-				}
-			free(txt_obj);								graph->Command(CMD_SCALE, &scale, 0L);
-			cy = mk_median_report(page, cx, graph->GetSize(SIZE_GRECT_TOP), d1, n1, .95, g1_nam);
-			cy = mk_median_report(page, cx, cy + txtdef1.fSize, d2, n2, .95, g2_nam);
-			cy += linsp1;
-			rep_DrawText(page, graph->GetSize(SIZE_GRECT_RIGHT)+txtdef1.fSize*3.0, cy, false, TXA_HLEFT, &txtdef1, "<b>u-Test:</b>");
-			cy += linsp1;			d_utest(d1, d2, n1, n2, 0L, 0L, rs+15);
-			for(i = 0; i < 5; i++) {
-				switch(i) {
-					case 0:			dtmp = rs[17];			break;
-					case 1:			dtmp = rs[18];			break;
-					case 2:			dtmp = rs[21];			break;
-					case 3:			dtmp = rs[22];			break;
-					case 4:			dtmp = rs[23];			break;
-					}
-#ifdef USE_WIN_SECURE
-				j = sprintf_s(TmpTxt, 80, utest[i], dtmp);
-#else
-				j = sprintf(TmpTxt, utest[i], dtmp);
-#endif
-				if(i && dtmp < 0.0001) {
-					while(TmpTxt[j] != '=' && j) j--;
-					rlp_strcpy(TmpTxt+j, 10, "< 0.0001");
-					}
-				rep_DrawText(page, cx+(txtdef1.fSize*3.0), cy, false, TXA_HLEFT, &txtdef1, TmpTxt);
-				cy += linsp1/1.2;
-				}
-			page->Command(CMD_DROP_GRAPH, graph, 0L);
-			}
-		parent->Command(CMD_DROP_GRAPH, page, 0L);	
-		free(rs);			free(ny);
-		}
-	CloseDlgWnd(hDlg);		delete Dlg;				free(MeanDlg);
-	if(d1) free(d1);		if(d2) free(d2);
-}
+//reports.cpp, Copyright (c) 2006-2008 R.Lackner
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+// Create statistical reports
+//
+
+#include "rlplot.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+#include <ctype.h>
+#include "TheDialog.h"
+
+extern char TmpTxt[];
+extern Default defs;
+extern GraphObj *LastOpenGO;
+
+#define _PREC 1.0e-12
+
+//prototypes: WinSpec.cpp
+void *CreateDlgWnd(char *title, int x, int y, int width, int height, tag_DlgObj *d, DWORD flags);
+
+static int curr_id, cbSymLineStr;
+static fRECT dBounds;
+static TextDEF txtdef1, txtdef2;
+static double linsp1, linsp2;
+static char SymLineStr[40];
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// init report variables
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static void rep_init()
+{
+	curr_id = 1;		defs.cUnits = defs.dUnits;
+	txtdef1.ColTxt = txtdef2.ColTxt = 0x0L;
+	txtdef1.ColBg = txtdef2.ColBg = 0x00ffffffL;
+	txtdef1.fSize = defs.GetSize(SIZE_TEXT);
+	txtdef2.fSize = txtdef1.fSize *1.2;
+	txtdef1.RotBL = txtdef2.RotBL = 0.0;
+	txtdef1.RotCHAR = txtdef2.RotCHAR = 0.0;
+	txtdef1.iSize = txtdef2.iSize = 0;
+	txtdef1.Align = txtdef2.Align = TXA_HLEFT | TXA_VTOP;
+	txtdef1.Mode = txtdef2.Mode = TXM_TRANSPARENT;
+	txtdef1.Style = txtdef2.Style = TXS_NORMAL;
+	txtdef1.Font = txtdef2.Font = FONT_HELVETICA;
+	txtdef1.text = txtdef2.text = 0L;
+#ifdef _WINDOWS
+	linsp1 = txtdef1.fSize*1.2;	linsp2 = txtdef1.fSize*1.5;
+#else
+	linsp1 = txtdef1.fSize*1.7;	linsp2 = txtdef1.fSize*2.5;
+#endif
+#ifdef USE_WIN_SECURE
+	cbSymLineStr = sprintf_s(SymLineStr, 40, "Line= %g 1 0x0 0x0\n", defs.GetSize(SIZE_SYM_LINE)); 
+#else
+	cbSymLineStr = sprintf(SymLineStr, "Line= %g 1 0x0 0x0\n", defs.GetSize(SIZE_SYM_LINE)); 
+#endif
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create a text label for a report
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char* mk_label(double x, double y, bool moveable, int align, TextDEF *td, char*text)
+{
+	int csize, pos = 0;
+	char *res;
+
+	if(!(res = (char*)malloc(csize = 1000)))return 0L;
+	res[pos++] = '\n';				res[pos++] = '[';
+	add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
+	add_to_buff(&res, &pos, &csize, "=Label]\nPos=", 12);
+	add_dbl_to_buff(&res, &pos, &csize, x, true);
+	add_dbl_to_buff(&res, &pos, &csize, y, true);
+	res[pos++] = '\n';
+	if(moveable) add_to_buff(&res, &pos, &csize, "moveable= 1\n", 12);
+	add_to_buff(&res, &pos, &csize, "TxtDef= 0x0 0x00ffffff", 22);
+	add_dbl_to_buff(&res, &pos, &csize, td->fSize, true);
+	add_dbl_to_buff(&res, &pos, &csize, td->RotBL, true);
+	add_dbl_to_buff(&res, &pos, &csize, td->RotCHAR, true);
+	add_int_to_buff(&res, &pos, &csize, align, true, 0);
+	add_to_buff(&res, &pos, &csize, " 1 0 0 \"", 8);
+	add_to_buff(&res, &pos, &csize, text, 0);
+	add_to_buff(&res, &pos, &csize, "\"\n", 2);
+	return res;
+}
+static void rep_DrawText(GraphObj *parent, double x, double y, bool moveable, int align, TextDEF *td, char*text)
+{
+	char *txt_obj;
+
+	if(txt_obj = mk_label(x, y,	moveable, align, td, text)) {
+		OpenGraph(parent, 0L, (unsigned char*)txt_obj, false);
+		free(txt_obj);
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// draw a rectangle
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char* mk_rect(double x1, double y1, double x2, double y2, DWORD lcol, DWORD fcol)
+{
+	int csize, pos = 0;
+	char *res;
+
+	if(!(res = (char*)malloc(csize = 1000)))return 0L;
+	res[pos++] = '\n';				res[pos++] = '[';
+	add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
+	add_to_buff(&res, &pos, &csize, "=rectangle]\np1=", 0);
+	add_dbl_to_buff(&res, &pos, &csize, x1, true);
+	add_dbl_to_buff(&res, &pos, &csize, y1, true);
+	add_to_buff(&res, &pos, &csize, "\np2=", 0);
+	add_dbl_to_buff(&res, &pos, &csize, x2, true);
+	add_dbl_to_buff(&res, &pos, &csize, y2, true);
+	add_to_buff(&res, &pos, &csize, "\nLine= 0 1", 0);
+	add_hex_to_buff(&res, &pos, &csize, lcol, true);
+	add_to_buff(&res, &pos, &csize, " 0x0\nFillLine= 0 1 0x0 0x0\nFill= 0", 0);
+	add_hex_to_buff(&res, &pos, &csize, fcol, true);
+	add_to_buff(&res, &pos, &csize, " 1 0x0 0x00ffffff\n", 0);
+	return res;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// print values to string
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static int dbl_to_str1(char *dest, int size, char* fmt, double val)
+{
+#ifdef USE_WIN_SECURE
+	return sprintf_s(dest, size, fmt, val);
+#else
+	return sprintf(dest, fmt, val);
+#endif
+}
+
+static int dbl_to_str2(char *dest, int size, char* fmt, double val1, double val2)
+{
+#ifdef USE_WIN_SECURE
+	return sprintf_s(dest, size, fmt, val1, val2);
+#else
+	return sprintf(dest, fmt, val1, val2);
+#endif
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create general information on report page
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static void mk_header(Page *page, char* desc, DataObj *data)
+{
+	time_t ti = time(0L);
+	char label[80];
+	double rpos;
+	int cb;
+
+	if(!page) return;
+	rpos = page->GetSize(SIZE_GRECT_RIGHT) - txtdef1.fSize*5.0;
+	rep_DrawText(page, txtdef1.fSize*5.0, page->GetSize(SIZE_GRECT_TOP)+txtdef2.fSize*6.0,
+		false, TXA_HLEFT, &txtdef2, desc); 
+#ifdef USE_WIN_SECURE
+	ctime_s(label, 32, &ti);
+#else
+	rlp_strcpy(label, 25, ctime(&ti));
+#endif
+	label[24] = 0;
+	rep_DrawText(page, rpos, page->GetSize(SIZE_GRECT_TOP)+txtdef1.fSize*5.0,
+		false, TXA_HRIGHT, &txtdef1, label);
+	cb = rlp_strcpy(label, 80, "RLPlot ");		cb += rlp_strcpy(label+cb, 80-cb, SZ_VERSION);
+	rep_DrawText(page, rpos, page->GetSize(SIZE_GRECT_BOTTOM)-txtdef1.fSize*6.0,
+		false, TXA_HRIGHT, &txtdef1, label);
+	if(data && data->Command(CMD_GETFILENAME, TmpTxt, 0L)) {
+		rpos = page->GetSize(SIZE_GRECT_LEFT) + txtdef1.fSize*5.0;
+		rep_DrawText(page, rpos, page->GetSize(SIZE_GRECT_BOTTOM)-txtdef1.fSize*6.0,
+			false, TXA_HLEFT, &txtdef1, TmpTxt);
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create horizontal ruler
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static void mk_hr(GraphObj *parent, double x1, double x2, double y)
+{
+	int csize, pos = 0;
+	char *res;
+
+	if(!(res = (char*)malloc(csize = 100)))return;
+	res[pos++] = '\n';				res[pos++] = '[';
+	add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
+	add_to_buff(&res, &pos, &csize, "=polyline]\nData= (2){", 21);
+	add_dbl_to_buff(&res, &pos, &csize, x1, false);
+	add_dbl_to_buff(&res, &pos, &csize, y, true);
+	add_dbl_to_buff(&res, &pos, &csize, x2, true);
+	add_dbl_to_buff(&res, &pos, &csize, y, true);
+	add_to_buff(&res, &pos, &csize, "}\nLine=", 7);
+	add_dbl_to_buff(&res, &pos, &csize, txtdef1.fSize/20.0, true);
+	add_dbl_to_buff(&res, &pos, &csize, txtdef1.fSize, true);
+	add_to_buff(&res, &pos, &csize, " 0x0 0x0\n", 9);
+	OpenGraph(parent, 0L, (unsigned char*)res, false);
+	free(res);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create a means report
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static double mk_mean_report(GraphObj *parent, double x, double y, double *da, int n, double ci, char *name)
+{
+	static char *mean_fmts[] = {"Mean = %g", "Std.Dev. = %g", "N = %g", "Std.Err. = %g", 0L,
+		"Kurtosis = %g", "Skewness = %g"};
+	char desc[80];
+	int i, cb;
+	double v, t, res[10];
+
+	if(name && name[0]) {
+		cb = rlp_strcpy(desc, 40, "<b>");			cb += rlp_strcpy(desc+cb, 40-cb, name);
+		cb += rlp_strcpy(desc+cb, 40-cb, ":</b>");
+		rep_DrawText(parent, x, y, false, TXA_HLEFT, &txtdef1, desc);
+		y += linsp1;		x += (txtdef1.fSize*3.0);
+		}
+	cb = dbl_to_str1(desc, 80, "%g%%%% C.I. = %%g", ci*100.0);
+	mean_fmts[4] = (char*)malloc(cb+2);
+	rlp_strcpy(mean_fmts[4], cb+1, desc);			t = distinv(t_dist, n-1, 1, 1.0-ci, 2.0);
+	v = d_variance(n, da, &res[0], 0L);				res[2] = (double)n;
+	res[1] = sqrt(v);								res[3] = res[1] / sqrt(res[2]);
+	res[4] = res[3] *t;								res[5] = d_kurt(n, da);
+	res[6] = d_skew(n, da);
+	for(i = 0; i < 7; i++) {
+		dbl_to_str1(desc, 80, mean_fmts[i], res[i]);
+		rep_DrawText(parent, x, y, false, TXA_HLEFT, &txtdef1, desc);
+		y += (i==2 ? linsp1/0.9 : linsp1/1.2);
+		}
+	free(mean_fmts[4]);								mean_fmts[4] = 0L;
+	return y;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create a median report
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static double mk_median_report(GraphObj *parent, double x, double y, double *da, int n, double ci, char *name)
+{
+	static char *mean_fmts[] = {"Median = %g", "25%% = %g", "75%% = %g", "N = %g", "Min. = %g", "Max. = %g" };
+	char desc[80];
+	int i, cb;
+	double res[6];
+
+	if(!da || !parent || !n) return y;
+	if(name && name[0]) {
+		cb = rlp_strcpy(desc, 40, "<b>");			cb += rlp_strcpy(desc+cb, 40-cb, name);
+		cb += rlp_strcpy(desc+cb, 40-cb, ":</b>");
+		rep_DrawText(parent, x, y, false, TXA_HLEFT, &txtdef1, desc);
+		y += linsp1; x += (txtdef1.fSize*3.0);
+		}
+	d_quartile(n, da, &res[1], &res[0], &res[2]);
+	res[4] = res[5] = *da;
+	for(i = 1; i < n; i++) {
+		if(da[i] > res[5]) res[5] = da[i];			if(da[i] < res[4]) res[4] = da[i];
+		}
+	res[3] = (double)n;
+	for(i = 0; i < 6; i++) {
+		dbl_to_str1(desc, 80, mean_fmts[i], res[i]);
+		rep_DrawText(parent, x, y, false, TXA_HLEFT, &txtdef1, desc);
+		y += linsp1/1.2;
+		}
+	return y;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create report table for anova ...
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static double mk_table(GraphObj *parent, double x, double y, int type, double **dda)
+{
+	char *cheaders[] = {"<i>df</i>", "<i>SS</i>", "<i>MS</i>", "<i>F</i>", "<i>P</i>"};
+	char *rheaders1[] = {"Source of variation", type == 2 ? (char*)"Explained by regression":
+		(char*)"Among groups", type == 2 ? (char*)"Unexplained":(char*)"Within groups", "Total"};
+	char *rheaders2[] = {"Source of variation", "Between rows", "Between columns", "Interaction",
+		"Within subgroups (error)", "Total"};
+	char *rheaders3[] = {"Source of variation", "Between columns", "Between rows", "Error", "Total"};
+	char *cfmt[8], **rheaders;
+	int i, j, nl, nc[8];
+	double posc[8], cinc;
+
+#ifdef _WINDOWS
+	cinc = txtdef1.fSize;
+#else
+	cinc = txtdef1.fSize *1.3;
+#endif
+	cfmt[0] = "%.0lf";		cfmt[3] = "%0.3lf";			cfmt[4] = "%0.4lf";
+	switch(type) {
+	case 1:	case 2:
+		rheaders = rheaders1;
+		nl = 3;	nc[0] = 5;	nc[1] = 3;	nc[2] = 2;
+		posc[0] = x + cinc*14.0;		posc[1] = posc[0] + cinc*5.0;
+		posc[2] = posc[1] + cinc*6.0;	posc[3] = posc[2] + cinc*6.0;
+		posc[4] = posc[3] + cinc*6.0;
+		cfmt[1] = GetNumFormat(floor(log10(dda[2][1])-3.0));	
+		cfmt[2] = GetNumFormat(floor(log10(dda[0][2]+dda[1][2])-3.0));
+		break;
+	case 3:
+		rheaders = rheaders2;
+		nl = 5;	nc[0] = nc[1] = nc[2] = 5;	nc[3] = 3;	nc[4] = 2;
+		posc[0] = x + cinc*14.0;		posc[1] = posc[0] + cinc*5.0;
+		posc[2] = posc[1] + cinc*6.0;	posc[3] = posc[2] + cinc*6.0;
+		posc[4] = posc[3] + cinc*6.0;
+		cfmt[1] = GetNumFormat(floor(log10(dda[2][1])-3.0));	
+		cfmt[2] = GetNumFormat(floor(log10(dda[0][2]+dda[0][1])-3.0));
+		break;
+	case 4:
+		rheaders = rheaders3;
+		nl = 4;	nc[0] = nc[1] = 5;	nc[2] = 3;	nc[3] = 2;
+		posc[0] = x + cinc*14.0;		posc[1] = posc[0] + cinc*5.0;
+		posc[2] = posc[1] + cinc*6.0;	posc[3] = posc[2] + cinc*6.0;
+		posc[4] = posc[3] + cinc*6.0;
+		cfmt[1] = GetNumFormat(floor(log10(dda[3][1])-4.0));	
+		cfmt[2] = GetNumFormat(floor(log10(dda[0][2]+dda[1][2]+dda[2][2])-4.0));
+		break;
+	default: return y;
+		}
+	if(type == 1 || type == 2 || type == 3 || type == 4) {
+		rep_DrawText(parent, x, y, false, TXA_HLEFT, &txtdef1, rheaders[0]);
+		for(i = 0; i < 5; i++) {
+			rep_DrawText(parent, posc[i], y, false, TXA_HRIGHT, &txtdef1, cheaders[i]);
+			if(i) posc[i] += linsp1;
+			}
+		mk_hr(parent, x, posc[4], y + linsp1);			y += linsp2;
+		}
+	for(i = 0; i < nl; i++) {
+		rep_DrawText(parent, x, y, false, TXA_HLEFT, &txtdef1, rheaders[i+1]);
+		for(j = 0; j < nc[i]; j++) {
+			if(j == 4 && dda[i][j] > 0.0 && dda[i][j] < 0.0001) rlp_strcpy(TmpTxt, 10, "< 0.0001");
+#ifdef USE_WIN_SECURE
+			else sprintf_s(TmpTxt, 20, cfmt[j], dda[i][j]);
+#else
+			else sprintf(TmpTxt, cfmt[j], dda[i][j]);
+#endif
+			rep_DrawText(parent, posc[j], y, false, TXA_HRIGHT, &txtdef1, TmpTxt);
+			}
+		if(i < (nl-2)) y += linsp1;
+		else {
+			mk_hr(parent, x, posc[4], y + linsp1);		y += linsp2;
+			}
+		}
+	return y;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create a boxplot for a report
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char* mk_boxplot(int style, double *x, double *y, double *by1, double *by2, double *wy1, double *wy2, int *ny, int n, 
+	char *s_nam, char *b_nam, char *w_nam)
+{
+	int i, csize, pos, first_s, first_b, first_w, first_l;
+	char *res;
+	double size;
+
+	if(!(res = (char*)malloc(csize = 2000)))return 0L;
+	if(n < 20) size = defs.GetSize(SIZE_SYMBOL);
+	else size = defs.GetSize(SIZE_SYMBOL)/2.0 + 20.0 * defs.GetSize(SIZE_SYMBOL)/(2.0 * n);
+	first_b = curr_id;
+	for(i = pos = 0; i < n && res; i++) {
+		add_to_buff(&res, &pos, &csize, "\n[", 2);		add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
+		add_to_buff(&res, &pos, &csize, "=Box]\nType= 256\nHigh=", 21);
+		if(style == 1) {
+			add_dbl_to_buff(&res, &pos, &csize, by2[i], true);
+			add_dbl_to_buff(&res, &pos, &csize, y[i], true);
+			add_to_buff(&res, &pos, &csize,"\nLow=", 5);
+			add_dbl_to_buff(&res, &pos, &csize, by1[i], true);
+			add_dbl_to_buff(&res, &pos, &csize,y[i], true);
+			}
+		else {
+			add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
+			add_dbl_to_buff(&res, &pos, &csize, by2[i], true);
+			add_to_buff(&res, &pos, &csize,"\nLow=", 5);
+			add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
+			add_dbl_to_buff(&res, &pos, &csize, by1[i], true);
+			}
+		add_to_buff(&res, &pos, &csize,"\nSize= 60\nName= \"", 17);
+		add_to_buff(&res, &pos, &csize, b_nam, 0);
+		add_to_buff(&res, &pos, &csize, "\"\n", 2);
+		}
+	first_w = curr_id;
+	for(i = 0; i < n && res; i++) {
+		add_to_buff(&res, &pos, &csize, "\n[", 2);		add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
+		add_to_buff(&res, &pos, &csize, "=Whisker]\nHigh=", 15);
+		if(style == 1) {
+			add_dbl_to_buff(&res, &pos, &csize, wy2[i], true);
+			add_dbl_to_buff(&res, &pos, &csize, y[i], true);
+			add_to_buff(&res, &pos, &csize,"\nLow=", 5);
+			add_dbl_to_buff(&res, &pos, &csize, wy1[i], true);
+			add_dbl_to_buff(&res, &pos, &csize,y[i], true);
+			}
+		else {
+			add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
+			add_dbl_to_buff(&res, &pos, &csize, wy2[i], true);
+			add_to_buff(&res, &pos, &csize,"\nLow=", 5);
+			add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
+			add_dbl_to_buff(&res, &pos, &csize, wy1[i], true);
+			}
+		add_to_buff(&res, &pos, &csize, "\nDesc= \"", 8);
+		add_to_buff(&res, &pos, &csize, w_nam, 0);
+		add_to_buff(&res, &pos, &csize, "\"\n", 2);
+		}
+	first_s = curr_id;
+	for(i = 0; i < n && res; i++) {
+		add_to_buff(&res, &pos, &csize, "\n[", 2);		add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
+		add_to_buff(&res, &pos, &csize, "=Symbol]\nType= 10\nPos=", 22);
+		add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
+		add_dbl_to_buff(&res, &pos, &csize, y[i], true);
+		add_to_buff(&res, &pos, &csize, "\nSize=", 6);	add_dbl_to_buff(&res, &pos, &csize, size, true);
+		add_to_buff(&res, &pos, &csize, "\n", 1);
+		add_to_buff(&res, &pos, &csize, SymLineStr, cbSymLineStr);
+		add_to_buff(&res, &pos, &csize, "FillCol= 0x00ffffff\n", 20);
+		if(s_nam) {
+			add_to_buff(&res, &pos, &csize, "Name=\"", 6);
+			add_to_buff(&res, &pos, &csize, s_nam, 0);	add_to_buff(&res, &pos, &csize, "\"\n", 2);
+			}
+		}
+	first_l = curr_id;
+	for(i = 0; i < n && res; i++) {
+		add_to_buff(&res, &pos, &csize, "\n[", 2);
+		add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
+		add_to_buff(&res, &pos, &csize, "=Label]\nPos=", 12);
+		if(style == 1) {
+			add_dbl_to_buff(&res, &pos, &csize, wy2[i], true);
+			add_dbl_to_buff(&res, &pos, &csize, y[i], true);
+			add_to_buff(&res, &pos, &csize, "\nDist=", 6);
+			add_dbl_to_buff(&res, &pos, &csize, txtdef1.fSize/2.0, true);
+			add_to_buff(&res, &pos, &csize, " 0", 2);
+			}
+		else {
+			add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
+			add_dbl_to_buff(&res, &pos, &csize, wy2[i], true);
+			add_to_buff(&res, &pos, &csize, "\nDist= 0", 8);
+			add_dbl_to_buff(&res, &pos, &csize, -txtdef1.fSize/4.0, true);
+			}
+		add_to_buff(&res, &pos, &csize, "\nFlags= 0x00000011\nTxtDef= 0x00000000 0x00ffffff", 48);
+		add_dbl_to_buff(&res, &pos, &csize, txtdef1.fSize, true);
+		add_dbl_to_buff(&res, &pos, &csize, txtdef1.RotBL, true);
+		add_dbl_to_buff(&res, &pos, &csize, txtdef1.RotCHAR, true);
+		add_int_to_buff(&res, &pos, &csize, style == 1 ? (TXA_HLEFT | TXA_VCENTER):(TXA_HCENTER | TXA_VBOTTOM), true, 0);
+		add_to_buff(&res, &pos, &csize, " 1 0 0 \"", 8);
+		if(n < 7) add_to_buff(&res, &pos, &csize, "n = ", 4);
+		add_int_to_buff(&res, &pos, &csize, ny[i], false, 0);
+		add_to_buff(&res, &pos, &csize, "\"\n", 2);
+		}
+	add_to_buff(&res,&pos,&csize, "\n[", 2);				add_int_to_buff(&res,&pos,&csize, curr_id++, false, 0);
+	add_to_buff(&res, &pos, &csize, "=BoxPlot]\nBounds=", 17);
+	add_dbl_to_buff(&res,&pos,&csize, dBounds.Xmin, true);	add_dbl_to_buff(&res,&pos,&csize, dBounds.Ymax, true);
+	add_dbl_to_buff(&res,&pos,&csize, dBounds.Xmax, true);	add_dbl_to_buff(&res,&pos,&csize, dBounds.Ymin, true);
+
+	add_to_buff(&res,&pos,&csize, "\nBoxes=(", 0);			add_int_to_buff(&res,&pos,&csize, n, false, 0);
+	add_to_buff(&res,&pos,&csize, "){", 2);
+	for(i = 0; i < n; i++, first_b++) {
+		add_int_to_buff(&res,&pos,&csize, first_b, false, 0);	add_to_buff(&res,&pos,&csize, ",", 1);
+		if(i && (i%16)== 0 && first_b < (curr_id-2)) add_to_buff(&res, &pos, &csize, "\n   ", 4);
+		}
+	while(res[pos-1] == ',' || res[pos-1] < 33) pos --;		add_to_buff(&res, &pos, &csize, "}", 2);
+	add_to_buff(&res,&pos,&csize, "\nWhiskers=(", 0);		add_int_to_buff(&res,&pos,&csize, n, false, 0);
+	add_to_buff(&res,&pos,&csize, "){", 2);
+	for(i = 0; i < n; i++, first_w++) {
+		add_int_to_buff(&res,&pos,&csize, first_w, false, 0);	add_to_buff(&res,&pos,&csize, ",", 1);
+		if(i && (i%16)== 0 && first_b < (curr_id-2)) add_to_buff(&res, &pos, &csize, "\n   ", 4);
+		}
+	while(res[pos-1] == ',' || res[pos-1] < 33) pos --;		add_to_buff(&res, &pos, &csize, "}", 2);
+	add_to_buff(&res,&pos,&csize, "\nSymbols=(", 10);		add_int_to_buff(&res,&pos,&csize, n, false, 0);
+	add_to_buff(&res,&pos,&csize, "){", 2);
+	for(i = 0; i < n; i++, first_s++) {
+		add_int_to_buff(&res,&pos,&csize, first_s, false, 0);	add_to_buff(&res,&pos,&csize, ",", 1);
+		if(i && (i%16)== 0 && first_s < (curr_id-2)) add_to_buff(&res, &pos, &csize, "\n   ", 4);
+		}
+	while(res[pos-1] == ',' || res[pos-1] < 33) pos --;		add_to_buff(&res, &pos, &csize, "}", 2);
+	add_to_buff(&res,&pos,&csize, "\nLabels=(", 9);			add_int_to_buff(&res,&pos,&csize, n, false, 0);
+	add_to_buff(&res,&pos,&csize, "){", 2);
+	for(i = 0; i < n; i++, first_l++) {
+		add_int_to_buff(&res,&pos,&csize, first_l, false, 0);	add_to_buff(&res,&pos,&csize, ",", 1);
+		if(i && (i%16)== 0 && first_s < (curr_id-2)) add_to_buff(&res, &pos, &csize, "\n   ", 4);
+		}
+	while(res[pos-1] == ',' || res[pos-1] < 33) pos --;		add_to_buff(&res, &pos, &csize, "}\n", 2);
+	return res;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create a scatterplot for a report
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char* mk_scatt(int style, double *x, double *y, double *ss, int *ny, int n, char *s_nam, char *x_desc, char *y_desc)
+{
+	int i, csize, pos, first;
+	char *res;
+	double size, linew, tmp, val;
+
+	if(!(res = (char*)malloc(csize = 2000)))return 0L;
+	if(n < 20) size = defs.GetSize(SIZE_SYMBOL);
+	else size = defs.GetSize(SIZE_SYMBOL)/2.0 + 20.0 * defs.GetSize(SIZE_SYMBOL)/(2.0 * n);
+	linew = defs.GetSize(SIZE_SYM_LINE);
+	first = curr_id;
+	for(i = pos = 0; i < n && res; i++) {
+		add_to_buff(&res, &pos, &csize, "\n[", 2);		add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
+		add_to_buff(&res, &pos, &csize, "=Symbol]\nPos=", 13);
+		add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
+		add_dbl_to_buff(&res, &pos, &csize, y[i], true);
+		add_to_buff(&res, &pos, &csize, "\nSize=", 6);	add_dbl_to_buff(&res, &pos, &csize, size, true);
+		add_to_buff(&res, &pos, &csize, "\n", 1);
+		add_to_buff(&res, &pos, &csize, SymLineStr, cbSymLineStr);
+		add_to_buff(&res, &pos, &csize, "FillCol= 0x00ffffff\n", 20);
+		}
+	if(ss && ny) {
+		for(i = 0; i < n && res; i++) {
+			if(ny[i] > 1) tmp = sqrt(ss[i]/(ny[i]-1));
+			else tmp = 0.0;
+			add_to_buff(&res, &pos, &csize, "\n[", 2);
+			add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
+			add_to_buff(&res, &pos, &csize, "=ErrorBar]\nType=", 16);
+			add_int_to_buff(&res, &pos, &csize, style & 0x10 ? 3 : 0, true, 0);
+			add_to_buff(&res, &pos, &csize, "\nPos=", 5);
+			add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : (double)(i+1), true);
+			add_dbl_to_buff(&res, &pos, &csize, y[i], true);
+			add_to_buff(&res, &pos, &csize, "\nErr=", 5);
+			add_dbl_to_buff(&res, &pos, &csize, tmp, true);
+			add_to_buff(&res, &pos, &csize, "\nDesc= \"Std. Dev.\"\n", 19);
+			}
+		for(i = 0; i < n && res; i++) {
+			if(ny[i] > 1) tmp = sqrt(ss[i]/(ny[i]-1));
+			else tmp = 0.0;
+			add_to_buff(&res, &pos, &csize, "\n[", 2);
+			add_int_to_buff(&res, &pos, &csize, curr_id++, false, 0);
+			add_to_buff(&res, &pos, &csize, "=Label]\nPos=", 12);
+			if(style & 0x10) {
+				val = x ? x[i] : (double)(i+1);
+				if(dBounds.Xmin > (val-tmp)) dBounds.Xmin = val-tmp;
+				if(dBounds.Xmax < (val+tmp)) dBounds.Xmax = val+tmp;
+				add_dbl_to_buff(&res, &pos, &csize, val+tmp, true);
+				add_dbl_to_buff(&res, &pos, &csize, y[i], true);
+				add_to_buff(&res, &pos, &csize, "\nDist=", 6);
+				add_dbl_to_buff(&res, &pos, &csize, txtdef1.fSize/2.0, true);
+				add_to_buff(&res, &pos, &csize, " 0", 2);
+				}
+			else {
+				if(dBounds.Ymin > (y[i]-tmp)) dBounds.Ymin = y[i]-tmp;
+				if(dBounds.Ymax < (y[i]+tmp)) dBounds.Ymax = y[i]+tmp;
+				add_dbl_to_buff(&res, &pos, &csize, x ? x[i] : ((double)(i+1)), true);
+				add_dbl_to_buff(&res, &pos, &csize, y[i] +tmp, true);
+				add_to_buff(&res, &pos, &csize, "\nDist= 0", 8);
+				add_dbl_to_buff(&res, &pos, &csize, -txtdef1.fSize/4.0, true);
+				}
+			add_to_buff(&res, &pos, &csize, "\nFlags= 0x00000011\nTxtDef= 0x00000000 0x00ffffff", 48);
+			add_dbl_to_buff(&res, &pos, &csize, txtdef1.fSize, true);
+			add_dbl_to_buff(&res, &pos, &csize, txtdef1.RotBL, true);
+			add_dbl_to_buff(&res, &pos, &csize, txtdef1.RotCHAR, true);
+			add_int_to_buff(&res, &pos, &csize, (style & 0x10)?(TXA_HLEFT | TXA_VCENTER) : (TXA_HCENTER | TXA_VBOTTOM), true, 0);
+			add_to_buff(&res, &pos, &csize, " 1 0 0 \"", 8);
+			if(n < 7) add_to_buff(&res, &pos, &csize, "n = ", 4);
+			add_int_to_buff(&res, &pos, &csize, ny[i], false, 0);
+			add_to_buff(&res, &pos, &csize, "\"\n", 2);
+			}
+		}
+	add_to_buff(&res,&pos,&csize, "\n[", 2);				add_int_to_buff(&res,&pos,&csize, curr_id++, false, 0);
+	add_to_buff(&res, &pos, &csize, "=PlotScatt]\nBounds=", 19);
+	add_dbl_to_buff(&res,&pos,&csize, dBounds.Xmin, true);	add_dbl_to_buff(&res,&pos,&csize, dBounds.Ymax, true);
+	add_dbl_to_buff(&res,&pos,&csize, dBounds.Xmax, true);	add_dbl_to_buff(&res,&pos,&csize, dBounds.Ymin, true);
+	add_to_buff(&res,&pos,&csize, "\nSymbols=(", 10);		add_int_to_buff(&res,&pos,&csize, n, false, 0);
+	add_to_buff(&res,&pos,&csize, "){", 2);
+	for(i = 0; i < n; i++, first++) {
+		add_int_to_buff(&res,&pos,&csize, first, false,0);	add_to_buff(&res,&pos,&csize, ",", 1);
+		if(i && (i%16)== 0 && first < (curr_id-2)) add_to_buff(&res, &pos, &csize, "\n   ", 4);
+		}
+	while(res[pos-1] == ',' || res[pos-1] < 33) pos --;		add_to_buff(&res, &pos, &csize, "}\n", 2);
+	if(ss && ny) {
+		add_to_buff(&res,&pos,&csize, "ErrBars=(", 9);		add_int_to_buff(&res,&pos,&csize, n, false, 0);
+		add_to_buff(&res,&pos,&csize, "){", 2);
+		for(i = 0; i < n; i++, first++) {
+			add_int_to_buff(&res,&pos,&csize, first,false,0);	add_to_buff(&res,&pos,&csize, ",", 1);
+			if(i && (i%16)== 0 && first < (curr_id-2)) add_to_buff(&res, &pos, &csize, "\n   ", 4);
+			}
+		while(res[pos-1] == ',' || res[pos-1] < 33) pos --;	add_to_buff(&res, &pos, &csize, "}\n", 2);
+		add_to_buff(&res,&pos,&csize, "Labels=(", 8);		add_int_to_buff(&res,&pos,&csize, n, false, 0);
+		add_to_buff(&res,&pos,&csize, "){", 2);
+		for(i = 0; i < n; i++, first++) {
+			add_int_to_buff(&res,&pos,&csize, first,false,0);	add_to_buff(&res,&pos,&csize, ",", 1);
+			if(i && (i%16)== 0 && first < (curr_id-2)) add_to_buff(&res, &pos, &csize, "\n   ", 4);
+			}
+		while(res[pos-1] == ',' || res[pos-1] < 33) pos --;	add_to_buff(&res, &pos, &csize, "}\n", 2);
+		}
+	if(x_desc && x_desc[0]){
+		add_to_buff(&res,&pos,&csize, "x_info= \"", 9);		add_to_buff(&res,&pos,&csize, x_desc, 0);
+		add_to_buff(&res,&pos,&csize, "\"\n", 2);
+		}
+	if(y_desc && y_desc[0]){
+		add_to_buff(&res,&pos,&csize, "y_info= \"", 9);		add_to_buff(&res,&pos,&csize, y_desc, 0);
+		add_to_buff(&res,&pos,&csize, "\"\n", 2);
+		}
+	if(s_nam && s_nam[0]) {
+		add_to_buff(&res, &pos, &csize, "DataDesc=\"", 10);
+		add_to_buff(&res, &pos, &csize, s_nam, 0);			add_to_buff(&res, &pos, &csize, "\"\n", 2);
+		}
+	return res;
+}
+
+static double contrasts_level = 95.0;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create a contrasts report for one way anova
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static void mk_contrasts(GraphObj* par, int type, double dx, double dy, double *y, double *ss, int *ny, int n, 
+	char **names, double ci, double msw, double msdf)
+{
+	double tmp, tkd, pcorr, cx[10], *raw;
+	int i, j, k, l, c, df, *co, nco, cb;
+	char ctext[5], **contrasts;
+	char *headings[] = {"<i>Groups</i>", "<i>Mean</i>", "<i>Std. Dev.</i>", "<i>N</i>",
+		"<i>Contrasts</i><sup>1)</sup>"};
+
+	if(!par || !y || !ss || !ny || n < 2) return;
+	cx[0] = txtdef1.fSize*5.0;		cx[1] = cx[0] + linsp1*5.0;
+	cx[2] = cx[1] + linsp1*5.0;		cx[3] = cx[2] + linsp1*5.0;
+	cx[4] = cx[3] + linsp1*4.0;		cx[5] = cx[4] + linsp1*3.0;
+	cx[6] = cx[5] + linsp1*4.0;
+
+	rep_DrawText(par, dx, dy, false, TXA_HLEFT, &txtdef1, "<b>Summary:</b>");
+	for(i = 0, dy += linsp2; i < 5; i++) {				//column headers
+		c = (i == 4) ? TXA_HLEFT : TXA_HRIGHT;
+		rep_DrawText(par, cx[i+1], dy, false, c, &txtdef1, headings[i]);
+		}
+	mk_hr(par, cx[0], cx[6]+txtdef1.fSize*2.0, dy +linsp1);
+	if(type == 1 || type == 2) {	
+		if(!(co = (int*)malloc(n*sizeof(int)))) return;
+		if(!(contrasts = (char**)malloc(n*sizeof(char*)))) return;
+		rlp_strcpy(ctext, 5, ", a");
+		for(i = df = 0, nco = n; i < n; i++) {
+			if(ny[i] > 0) df += (ny[i]-1);
+			co[i] = i;
+			contrasts[i] = (char*)calloc(50, sizeof(char));
+			}
+		tkd = qtukey(1.0-ci, 1.0, (double) n, (double)df, 1, 0);
+		for(i = 0; nco; ) {
+			for(j = 0; j < n; j++) {
+				switch(type) {
+					case 1:					//Tukey-Kramer
+						tmp = tkd * sqrt((msw*(1.0/((double)ny[j]) + 1.0/((double)ny[co[i]])))/2.0);
+						break;
+					case 2:					//Tukey's HSD
+						tmp = tkd * sqrt(msw/(ny[j] <= ny[co[i]] ? ny[j] : ny[co[i]]));
+						break;
+					}
+				if(fabs(y[j]-y[co[i]]) < tmp) {
+					cb = (int)strlen(contrasts[j]);
+					rlp_strcpy((contrasts[j])+cb, 50-cb, ctext);
+					}
+				}
+			for(j = nco = 0; j < n; j++) {
+				if(!(contrasts[j][0])) co[nco++] = j;
+				}
+			ctext[2]++;
+			}
+		}
+	else if(type == 10) {
+		if(!(co = (int*)malloc(n*sizeof(int)))) return;
+		if(!(contrasts = (char**)malloc(n*sizeof(char*)))) return;
+		if(!(raw = (double*)malloc((n*n-1)*sizeof(double))))return;
+		rlp_strcpy(ctext, 5, ", a");
+		for(i = df = 0, nco = n; i < n; i++) {
+			if(ny[i] > 0) df += (ny[i]-1);
+			co[i] = i;
+			contrasts[i] = (char*)calloc(50, sizeof(char));
+			}
+		for(i = k = 0; i < (n-1); i++) for(j = i+1; j < n; j++) {
+			raw[k++] = t_dist(fabs(0.5*(y[i]-y[j])/sqrt(msw/(ny[i]+ny[j]))), msdf, 0.0);
+			}
+		SortArray(k, raw);
+		for(i = 0; nco; ) {
+			for(j = 0; j < n; j++) {
+				tmp = t_dist(fabs(0.5*(y[j]-y[co[i]])/sqrt(msw/(ny[j]+ny[co[i]]))), msdf, 0.0);
+				for(l = 0; l < k && tmp > raw[l]; l++);
+				switch(type) {
+				case 10:					//Dunn Sidak
+					pcorr = 1.0 - pow((1.0 - ci), 1.0 /(double(k-l)));
+					break;
+					}
+				if(tmp > pcorr || j == co[i]) {
+					cb = (int)strlen(contrasts[j]);
+					rlp_strcpy((contrasts[j])+cb, 50-cb, ctext);
+					}
+				}
+			for(j = nco = 0; j < n; j++) {
+				if(!(contrasts[j][0])) co[nco++] = j;
+				}
+			ctext[2]++;
+			}
+		free(raw);
+		}
+	else return;
+
+	for(i = 0, dy += linsp2; i < n; i++, dy +=linsp1) {
+		if(ny[i] > 1) tmp = sqrt(ss[i]/(ny[i]-1));
+		else tmp = 0.0;
+		rep_DrawText(par, cx[1], dy, false, TXA_HRIGHT, &txtdef1, names[i]);
+		dbl_to_str1(TmpTxt, 20, "%g", y[i]);
+		rep_DrawText(par, cx[2], dy, false, TXA_HRIGHT, &txtdef1, TmpTxt);
+		if(tmp > 0.0) {
+			dbl_to_str1(TmpTxt, 20, "%g", tmp);
+			rep_DrawText(par, cx[3], dy, false, TXA_HRIGHT, &txtdef1, TmpTxt);
+			}
+		if(ny[i] >1) {
+			dbl_to_str1(TmpTxt, 20, "%.0lf", (double)ny[i]);
+			rep_DrawText(par, cx[4], dy, false, TXA_HRIGHT, &txtdef1, TmpTxt);
+			}
+		rep_DrawText(par, cx[5], dy, false, TXA_HLEFT, &txtdef1, contrasts[i]+2);
+		}
+	mk_hr(par, cx[0], cx[6]+txtdef1.fSize*2.0, dy+txtdef1.fSize*0.2);
+	cb = dbl_to_str1(TmpTxt, 200, "<sup>1)</sup> Groups not sharing the same letter are different "
+		"on the %g%% level ", (1.0-ci)*100.0);
+	switch (type) {
+	case 1:						//Tukey-Kramer
+		rlp_strcpy(TmpTxt+cb, TMP_TXT_SIZE - cb, "(Tukey-Kramer method)");
+		break;
+	case 2:						//Tukey's HSD
+		rep_DrawText(par, cx[0]+ txtdef1.fSize*2.0, dy + txtdef2.fSize + linsp1, false, 
+			TXA_HLEFT, &txtdef1, "(Tukey's honest significant difference)");
+		break;
+	case 10:					//Dunn-Sidak
+		rep_DrawText(par, cx[0]+ txtdef1.fSize*2.0, dy + txtdef2.fSize + linsp1, false, 
+			TXA_HLEFT, &txtdef1, "(sequential Dunn-Sidak method)");
+		break;
+		}
+	rep_DrawText(par, cx[0], dy += txtdef2.fSize , false, TXA_HLEFT, &txtdef1, TmpTxt);
+	for(i = 0; i < n; i++) free(contrasts[i]);
+	free(co);		free(contrasts);
+	return;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create a homogeneity of variances report for one way anova
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static void mk_v_homogeneity(GraphObj* par, DataObj *data, double *dx, double *dy, double *y, double *ss,
+	int *ny, int n, double **vals)
+{
+	int i;
+	double tmp, *sd, f1, f2, p1, p2;
+	char *txt_obj;
+	scaleINFO scale = {{0.0, 0.8}, {0.0, 0.8}, {0.0, 0.8}};
+	Graph *graph;
+
+	if(!par || !y || !ss || !ny || n < 2) return;
+	if(!(sd = (double*)malloc(n*sizeof(double)))) return;
+	rep_DrawText(par, *dx, *dy, false, TXA_HLEFT, &txtdef1, "<b>Homogeneity of Variances:</b>");
+	for(i = 0; i < n; i++) {
+		if(ny[i] > 1) sd[i] = sqrt(ss[i]/(ny[i]-1));
+		else sd[i] = 0.0;
+		if(i) {
+			if(dBounds.Xmax < y[i]) dBounds.Xmax = y[i];
+			if(dBounds.Xmin > y[i]) dBounds.Xmin = y[i];
+			if(dBounds.Ymax < sd[i]) dBounds.Ymax = sd[i];
+			if(dBounds.Ymin > sd[i]) dBounds.Ymin = sd[i];
+			}
+		else {
+			dBounds.Xmax = dBounds.Xmin = y[0];
+			dBounds.Ymax = dBounds.Ymin = sd[0];
+			}
+		}
+	if((graph = new Graph(par, data, 0L, 0)) && (txt_obj = mk_scatt(0, y, sd, 0L, ny, n, 
+		"Variables", "Means", "Std.Dev."))){
+		graph->GRect.Xmax = defs.GetSize(SIZE_GRECT_BOTTOM)*0.8;
+		graph->GRect.Ymax *= 0.8;
+		graph->DRect.Xmin *= 0.8;				graph->DRect.Ymax *= 0.8;				
+		graph->DRect.Xmax = graph->GRect.Xmax - (txtdef1.fSize*2.0);
+		scale.sx.fx = par->GetSize(SIZE_GRECT_RIGHT) - txtdef1.fSize*5.0 - graph->GRect.Xmax*0.8 + graph->GRect.Xmin*0.8;		
+		scale.sy.fx = *dy;
+		OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
+		free(txt_obj);
+		graph->Command(CMD_SCALE, &scale, 0L);
+		if(!(par->Command(CMD_DROP_GRAPH, graph, 0L))) delete graph;
+		else graph->moveable = 0;
+		}
+	if(bartlett(n, ny, ss, &tmp)) {
+		rep_DrawText(par, *dx + txtdef1.fSize*2.0, *dy += (linsp2*1.5), false, TXA_HLEFT, &txtdef1, "Bartlett's test:");
+		i = dbl_to_str1(TmpTxt, TMP_TXT_SIZE, "Chi<sup>2</sup> = %.2lf, ", tmp);
+		tmp = chi_dist(tmp, n-1, 0);
+		dbl_to_str1(TmpTxt+i, TMP_TXT_SIZE-i, tmp < 0.0001 ? (char*)"P < 0.0001" : (char*)"P = %.4lf", tmp);
+		rep_DrawText(par, *dx + txtdef1.fSize*3.0, *dy += linsp1, false, TXA_HLEFT, &txtdef1, TmpTxt);
+		}
+	if(levene(1, n, ny, y, vals, &f1, &p1) && levene(2, n, ny, y, vals, &f2, &p2) ) {
+		rep_DrawText(par, *dx + txtdef1.fSize*2.0, *dy += (linsp2*1.5), false, TXA_HLEFT, &txtdef1, "Levene's test:");
+		i = dbl_to_str1(TmpTxt, TMP_TXT_SIZE, "using means: F = %.2lf, ", f1);
+		dbl_to_str1(TmpTxt+i, TMP_TXT_SIZE-i, tmp < 0.0001 ? (char*)"P < 0.0001" : (char*)"P = %.4lf", p1);
+		rep_DrawText(par, *dx + txtdef1.fSize*3.0, *dy += linsp1, false, TXA_HLEFT, &txtdef1, TmpTxt);
+		i = dbl_to_str1(TmpTxt, TMP_TXT_SIZE, "using medians: F = %.2lf, ", f2);
+		dbl_to_str1(TmpTxt+i, TMP_TXT_SIZE-i, tmp < 0.0001 ? (char*)"P < 0.0001" : (char*)"P = %.4lf", p2);
+		rep_DrawText(par, *dx + txtdef1.fSize*3.0, *dy += linsp1, false, TXA_HLEFT, &txtdef1, TmpTxt);
+		}
+	free(sd);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// one way anova
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *AnovaDlg_Tmpl =
+	"1,+,,DEFAULT,PUSHBUTTON,-1,158,10,45,12\n"
+	".,.,,,PUSHBUTTON,-2,158,25,45,12\n"
+	".,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"10,+,152,ISPARENT | CHECKED,SHEET,1,5,10,140,90\n"
+	".,20,100,ISPARENT,SHEET,2,5,10,140,90\n"
+	"20,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,104,,ISRADIO,CHECKBOX,8,20,25,100,9\n"
+	"104,+,,,LTEXT,4,15,37,100,9\n"
+	".,.,,ISRADIO,CHECKBOX,5,20,47,100,9\n"
+	".,.,,ISRADIO,CHECKBOX,9,20,57,100,9\n"
+	".,110,,ISRADIO,CHECKBOX,10,20,67,100,9\n"
+	"110,+,,,LTEXT,7,20,85,55,9\n"
+	".,.,,,EDVAL1,6,80,85,25,10\n"
+	".,,,,LTEXT,-10,107,85,10,9\n"
+	"152,+,,ISPARENT | CHECKED,GROUPBOX,3,12,30,128,65\n"
+	".,.,,,LTEXT,0,25,45,60,8\n"
+	".,.,,,RANGEINPUT,0,25,55,100,10\n"
+	".,.,0,,PUSHBUTTON,-8,95,70,30,12\n"
+	".,,,LASTOBJ,PUSHBUTTON,-9,60,70,35,12";
+
+void rep_anova(GraphObj *parent, DataObj *data)
+{
+	TabSHEET tab1 = {0, 45, 10, "Anova Input"};
+	TabSHEET tab2 = {45, 75, 10, "Tests"};
+	DlgInfo *AnovaDlg;
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)" select one range for every variable ",
+		(void*)"Contrasts:", (void*)" Tukey-Kramer method", (void*)&contrasts_level, (void*)"significance level:",
+		(void*)" Homogeneity of Variances", (void*)" Tukey's honest sig. difference", (void*)" Dunn-Sidak"};
+	DlgRoot *Dlg;
+	void *hDlg;
+	double **cols = 0L, *csums=0L, mtot, *css=0L, cx, cy;
+	double **res_tab = 0L, ci;
+	int i, j, n, c, r, res, nc, ntot, currYR = 0, maxYR=0, ny, *ncols = 0L;;
+	bool bContinue = false, updateYR = true;
+	anyResult ares;
+	AccRange *rD =0L;
+	char **rd = 0L, **names, *txt_obj;
+	Graph *graph;
+	Page *page;
+
+	if(!parent || !data) return;
+	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
+		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return;
+	if(!(AnovaDlg = CompileDialog(AnovaDlg_Tmpl, dyndata))) return;
+	if(TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
+		for(i=j=0; i <= 1000; i +=100) if(TmpTxt[i]) 
+			rd[j++] = (char*)memdup(TmpTxt+i, ((int)strlen(TmpTxt+i))+2, 0);	 maxYR = j-1;
+		}
+	if(!rd && !(rd = (char**)calloc(1, sizeof(char*))))return;
+	if(!(Dlg = new DlgRoot(AnovaDlg, data)))return;
+	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
+	hDlg = CreateDlgWnd("Single-Classification Anova", 50, 50, 420, 240, Dlg, 0x4L);
+	do {
+		if(updateYR) {
+			if(currYR >0) Dlg->ShowItem(156, true);
+			else Dlg->ShowItem(156, false);
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "variable # %d/%d", currYR+1, maxYR+1);
+#else
+			sprintf(TmpTxt,"variable # %d/%d", currYR+1, maxYR+1);
+#endif
+			Dlg->SetText(153, TmpTxt);
+			updateYR = false;
+			}
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 155:		case 156:
+			res = com_StackDlg(res, Dlg, 0L, 0L, &rd, &currYR,
+				&rD, &bContinue, &ny, &maxYR, &updateYR);
+			break;
+			}
+		}while (res < 0);
+	if(res == 1 && (res_tab = (double**)calloc(3, sizeof(double*)))
+		&& (res_tab[0] = (double*) malloc(5*sizeof(double)))
+		&& (res_tab[1] = (double*) malloc(5*sizeof(double)))
+		&& (res_tab[2] = (double*) malloc(5*sizeof(double)))
+		&& (cols = (double**)calloc(maxYR+1, sizeof(double*)))
+		&& (names = (char**)calloc(maxYR+1, sizeof(char*)))
+		&& (ncols = (int*)calloc(maxYR+1, sizeof(int)))) {
+		rep_init();		if(rD) delete rD;		rD = 0L;
+		if(Dlg->GetValue(111, &ci)) {
+			contrasts_level = ci;		ci = 1.0-(ci/100.0);
+			}
+		dBounds.Ymin = HUGE_VAL;		dBounds.Ymax = -HUGE_VAL;
+		// get data into two dimensional array
+		for(nc = maxYR+1, i = ntot = 0, mtot = 0.0; i < nc; i++) {
+			if((rD = new AccRange(rd[i])) && (n = rD->CountItems()) && (cols[i] = (double*)malloc(n*sizeof(double)))) {
+				names[i] = rD->RangeDesc(data, 1);
+				for(n = 0, rD->GetFirst(&c, &r); rD->GetNext(&c, &r); ) {
+					if(data->GetResult(&ares, r, c, false) && ares.type == ET_VALUE) {
+						if(ares.value < dBounds.Ymin) dBounds.Ymin = ares.value;
+						if(ares.value > dBounds.Ymax) dBounds.Ymax = ares.value;
+						cols[i][n++] = ares.value;
+						}
+					}
+				ncols[i] = n;			ntot += n;
+				delete(rD);			rD = 0L;
+				}
+			if(!names[i] && (names[i] = (char*)malloc(20*sizeof(char)))){
+#ifdef USE_WIN_SECURE
+				sprintf_s(names[i], 20, "Group %d", i+1);
+#else
+				sprintf(names[i], "Group %d", i+1);
+#endif
+				}
+			}
+		// check for unique names
+		for(i = 0; i < (nc-1); i++) for(j = i+1; j < nc; j++) {
+			if(!strcmp(names[i], names[j])) {
+				names[i] = (char*) realloc(names[i], 20 *sizeof(char));
+				names[j] = (char*) realloc(names[j], 20 *sizeof(char));
+#ifdef USE_WIN_SECURE
+				sprintf_s(names[i], 20, "Group %d", i+1);	sprintf_s(names[j], 20, "Group %d", j+1);
+#else
+				sprintf(names[i], "Group %d", i+1);			sprintf(names[j], "Group %d", j+1);
+#endif
+				}
+			}
+
+		if(do_anova1(nc, ncols, cols, res_tab, &mtot, &csums, &css)){
+			dBounds.Xmin = 0.5;				dBounds.Xmax = ((double)nc)+0.5;
+			page = new Page(parent, data);
+			mk_header(page, "<b>Single-Classification ANOVA</b>", data);
+			if((graph = new Graph(parent, data, 0L, 0)) && (txt_obj = mk_scatt(0, 0L, csums, css, ncols, nc, "Mean", "Groups", "Means <u>+</u> S.D."))){
+				OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
+				if(LastOpenGO && LastOpenGO->Id == GO_PLOTSCATT) {
+					if(((PlotScatt*)LastOpenGO)->x_tv = new TextValue()){
+						for(i = 0; i < nc; i++) ((PlotScatt*)LastOpenGO)->x_tv->GetValue(names[i]);
+						}
+					}
+				free(txt_obj);								graph->moveable = 0;
+				graph->GRect.Xmin += (txtdef1.fSize*5.0);	graph->GRect.Xmax += (txtdef1.fSize*5.0);
+				graph->GRect.Ymin += (txtdef1.fSize*10.0);	graph->GRect.Ymax += (txtdef1.fSize*10.0);
+				page->Command(CMD_DROP_GRAPH, graph, 0L);
+				}
+			cx = graph->GRect.Xmin;		cy = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef2.fSize*2.0;
+			rep_DrawText(page, cx, cy, false, TXA_HLEFT, &txtdef1, "<b>Anova:</b>");
+			cy = mk_table(page, cx, cy+txtdef2.fSize, 1, res_tab)+txtdef2.fSize;
+			if(Dlg->GetCheck(100)) mk_v_homogeneity(page, data, &cx, &cy, csums, css, ncols, nc, cols);
+			else if(Dlg->GetCheck(105)) mk_contrasts(page, 1, cx, cy, csums, css, ncols, nc, names, ci, res_tab[1][2], res_tab[1][0]);
+			else if(Dlg->GetCheck(106)) mk_contrasts(page, 2, cx, cy, csums, css, ncols, nc, names, ci, res_tab[1][2], res_tab[1][0]);
+			else if(Dlg->GetCheck(107)) mk_contrasts(page, 10, cx, cy, csums, css, ncols, nc, names, ci, res_tab[1][2], res_tab[1][0]);
+			if(ntot > (nc<<1) && nc >1 && parent->Command(CMD_DROP_GRAPH, page, 0L));
+			else {
+				delete page;
+				InfoBox("No or insufficient\ndata for ANOVA\n");
+				}
+			}
+		for(i = 0; i < nc; i++){
+			if(cols[i]) free(cols[i]);		if(names[i]) free(names[i]);
+			}
+		for(i = 0; i < 3; i++) if(res_tab[i]) free(res_tab[i]);
+		free(cols);			free(ncols);			free(names);
+		free(res_tab);			if(css)free(css);		if(csums)free(csums);
+		}
+	if(rD) delete rD;		CloseDlgWnd(hDlg);
+	delete Dlg;				free(AnovaDlg);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Breakdown One Way Anova
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *BdAnovDlg_Tmpl =
+	"1,+,,DEFAULT,PUSHBUTTON,-1,158,10,45,12\n"
+	".,.,,,PUSHBUTTON,-2,158,25,45,12\n"
+	".,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"10,+,150,ISPARENT | CHECKED,SHEET,1,5,10,140,90\n"
+	".,20,100,ISPARENT,SHEET,2,5,10,140,90\n"
+	"20,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,104,,ISRADIO,CHECKBOX,8,20,25,100,9\n"
+	"104,+,,,LTEXT,4,15,37,100,9\n"
+	".,.,,ISRADIO,CHECKBOX,5,20,47,100,9\n"
+	".,.,,ISRADIO,CHECKBOX,9,20,57,100,9\n"
+	".,110,,ISRADIO,CHECKBOX,10,20,67,100,9\n"
+	"110,+,,,LTEXT,7,20,85,55,9\n"
+	".,.,,,EDVAL1,6,80,85,25,10\n"
+	".,,,,LTEXT,-10,107,85,10,9\n"
+	"150,+,,,LTEXT,3,20,32,100,9\n"
+	".,.,,,RANGEINPUT,-16,20,44,110,10\n"
+	".,.,,,LTEXT,11,20,60,100,9\n"
+	".,,,LASTOBJ,RANGEINPUT,-17,20,72,110,10";
+
+void rep_bdanova(GraphObj *parent, DataObj *data)
+{
+	TabSHEET tab1 = {0, 45, 10, "Anova Input"};
+	TabSHEET tab2 = {45, 75, 10, "Tests"};
+	DlgInfo *AnovaDlg;
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)"range for grouping variable",
+		(void*)"Contrasts:", (void*)" Tukey-Kramer method", (void*)&contrasts_level, (void*)"significance level:",
+		(void*)" Homogeneity of Variances", (void*)" Tukey's honest sig. difference", (void*)" Dunn-Sidak",
+		(void*)"range for values"};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, l, nc, nv, res, gr, gc, dr, dc;
+	int *ncols = 0L;
+	double cv, mv, ci, **cols = 0L, *csums=0L, mtot, *css=0L;
+	double **res_tab = 0L, cx, cy;
+	anyResult gres, dres;
+	AccRange *rG = 0L, *rD = 0L;
+	TextValue *tv = 0L;
+	char *txt_obj, **names;
+	bool bContinue = false;
+	Graph *graph;
+	Page *page;
+
+	if(!UseRangeMark(data, 2, TmpTxt+100, TmpTxt+200, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L, 0L)) return;
+	if(!(AnovaDlg = CompileDialog(BdAnovDlg_Tmpl, dyndata))) return;
+	if(!(Dlg = new DlgRoot(AnovaDlg, data)))return;
+	hDlg = CreateDlgWnd("Breakdown One Way Anova", 50, 50, 420, 250, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 1:
+			if(!Dlg->GetText(151, TmpTxt+100, 100) || !Dlg->GetText(153, TmpTxt+200, 100)) {
+				ErrorBox("Invalid Ranges!\nBoth ranges must be defined\nand must be of equal size.\n");
+				res = -1;	bContinue = true;
+				}
+			else if(!(rG = new AccRange(TmpTxt+100)) || !(rD = new AccRange(TmpTxt+200)) 
+				|| (l = rG->CountItems()) < 3 || (l = rD->CountItems()) < 3) {
+				ErrorBox("Insufficient Data!\nCheck data ranges.\n");
+				res = -1;	bContinue = true;
+				}
+			else for(l = nv = 0, mv = 0.0; l < 2 && rG->GetFirst(&gc, &gr) && rD->GetFirst(&dc, &dr); l++) {
+				if(l) {
+					dBounds.Ymin = HUGE_VAL;		dBounds.Ymax = -HUGE_VAL;
+					nc = (int)(mv);
+					cols = (double**)calloc(nc, sizeof(double*));
+					ncols = (int*)calloc(nc, sizeof(int));
+					if(cols && ncols) for(i = 0; i < nc; i++) cols[i] = (double*) malloc(nv * sizeof(double));
+					while(rG->GetNext(&gc, &gr) && rD->GetNext(&dc, &dr)) {
+						if(data->GetResult(&gres, gr, gc, false) && data->GetResult(&dres, dr, dc, false)
+							&& dres.type == ET_VALUE) {
+							switch (gres.type) {
+							case ET_TEXT:
+								cv = tv->GetValue(gres.text);		break;
+							default:
+								TranslateResult(&gres);
+								cv = tv->GetValue(gres.text);		break;
+								}
+							i = (int)(cv);
+							if(dres.value < dBounds.Ymin) dBounds.Ymin = dres.value;
+							if(dres.value > dBounds.Ymax) dBounds.Ymax = dres.value;
+							if(cols && ncols && cols[i-1]) cols[i-1][ncols[i-1]++] = dres.value;
+							}
+						}
+					}
+				else if(tv = new TextValue()) {
+					while(rG->GetNext(&gc, &gr) && rD->GetNext(&dc, &dr)) {
+						if(data->GetResult(&gres, gr, gc, false) && data->GetResult(&dres, dr, dc, false)
+							&& dres.type == ET_VALUE) {
+							switch (gres.type) {
+							case ET_TEXT:
+								cv = tv->GetValue(gres.text);		break;
+							default:
+								TranslateResult(&gres);
+								cv = tv->GetValue(gres.text);		break;
+								}
+							if(mv < cv) mv = cv;	nv++;
+							}
+						}
+					}
+				}
+			}
+		}while (res < 0);
+	if(res == 1 && tv && cols && ncols && (res_tab = (double**)calloc(3, sizeof(double*)))
+		&& (res_tab[0] = (double*) malloc(5*sizeof(double)))
+		&& (res_tab[1] = (double*) malloc(5*sizeof(double)))
+		&& (res_tab[2] = (double*) malloc(5*sizeof(double)))
+		&& (names = (char**)malloc(nc*sizeof(char*)))) {
+		if(Dlg->GetValue(111, &ci)) {
+			contrasts_level = ci;		ci = 1.0-(ci/100.0);
+			}
+		else ci = 0.05;					rep_init();
+		for(i = 0; i < nc; i++) tv->GetItem(i, &names[i], &cv);
+		if(rD) delete rD;				rD = 0L;
+		if(do_anova1(nc, ncols, cols, res_tab, &mtot, &csums, &css)){
+			dBounds.Xmin = 0.5;				dBounds.Xmax = ((double)nc)+0.5;
+			page = new Page(parent, data);
+			mk_header(page, "<b>Breakdown and Single-Classification ANOVA</b>", data);
+			if((graph = new Graph(parent, data, 0L, 0)) && (txt_obj = mk_scatt(0, 0L, csums, css, ncols, nc, "Mean", "Groups", "Means <u>+</u> S.D."))){
+				OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
+				if(LastOpenGO && LastOpenGO->Id == GO_PLOTSCATT) {
+					((PlotScatt*)LastOpenGO)->x_tv = tv;
+					}
+				else delete tv;
+				free(txt_obj);								graph->moveable = 0;
+				graph->GRect.Xmin += (txtdef1.fSize*5.0);	graph->GRect.Xmax += (txtdef1.fSize*5.0);
+				graph->GRect.Ymin += (txtdef1.fSize*10.0);	graph->GRect.Ymax += (txtdef1.fSize*10.0);
+				page->Command(CMD_DROP_GRAPH, graph, 0L);
+				}
+			cx = graph->GRect.Xmin;		cy = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef2.fSize*2.0;
+			rep_DrawText(page, cx, cy, false, TXA_HLEFT, &txtdef1, "<b>Anova:</b>");
+			cy = mk_table(page, cx, cy+txtdef2.fSize, 1, res_tab)+txtdef2.fSize;
+			if(Dlg->GetCheck(100)) mk_v_homogeneity(page, data, &cx, &cy, csums, css, ncols, nc, cols);
+			else if(Dlg->GetCheck(105)) mk_contrasts(page, 1, cx, cy, csums, css, ncols, nc, names, ci, res_tab[1][2], res_tab[1][0]);
+			else if(Dlg->GetCheck(106)) mk_contrasts(page, 2, cx, cy, csums, css, ncols, nc, names, ci, res_tab[1][2], res_tab[1][0]);
+			else if(Dlg->GetCheck(107)) mk_contrasts(page, 10, cx, cy, csums, css, ncols, nc, names, ci, res_tab[1][2], res_tab[1][0]);
+			if(nv > (nc<<1) && nc >1 && parent->Command(CMD_DROP_GRAPH, page, 0L));
+			else {
+				delete page;
+				InfoBox("No or insufficient\ndata for ANOVA\n");
+				}
+			}
+		for(i = 0; i < nc; i++)	if(cols[i]) free(cols[i]);
+		for(i = 0; i < 3; i++) if(res_tab[i]) free(res_tab[i]);
+		free(cols);		free(ncols);			free(names);
+		free(res_tab);		if(css)free(css);		if(csums)free(csums);
+		}
+
+	if(rD) delete rD;		if(rG) delete rG;
+	CloseDlgWnd(hDlg);
+	delete Dlg;			free(AnovaDlg);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Parametric two way anova
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *TwAnov_DlgTmpl = 
+	"1,2,,DEFAULT,PUSHBUTTON,-1,148,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,148,25,45,12\n"
+	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,10,100,ISPARENT | CHECKED,SHEET,1,5,10,130,80\n"
+	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,+,,,LTEXT,2,10,30,60,8\n"
+	".,.,,,RANGEINPUT,-15,20,40,100,10\n"
+	".,,,LASTOBJ,CHECKBOX,3,20,60,100,9";
+
+void rep_twanova(GraphObj *parent, DataObj *data)
+{
+	TabSHEET tab1 = {0, 40, 10, "Input Data"};
+	DlgInfo *TwAnovDlg;
+	void *dyndata[] = {(void*)&tab1, (void*)"rectangular range for variables",
+		(void*)" column/row headers present"};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, hc, hr, mr, mc, nr, nc, c, r, res, *nvr=0L, *nvc=0L;
+	bool bContinue = false;
+	char *mrk, *txt_obj, **cnames = 0L, **rnames = 0L;
+	double gm, ssc, ssr, sse, tmp, cx, cy, dmin, dmax;
+	double **vals = 0L, *cs = 0L, *rs = 0L, **res_tab = 0L, *c_ss, *r_ss, *c_m, *r_m, *abc;
+	RECT rec;
+	scaleINFO scale = {{0.0, 0.7}, {0.0, 0.7}, {0.0, 0.7}};
+	AccRange *rD =0L, *rDesc;
+	anyResult ares;
+	Graph *graph;
+	Page *page;
+
+	if(!parent || !data) return;
+	if(!(TwAnovDlg = CompileDialog(TwAnov_DlgTmpl, dyndata))) return;
+	if(data->Command(CMD_GETMARK, &mrk, 0L))rlp_strcpy(TmpTxt, TMP_TXT_SIZE, mrk);
+	else {
+		data->ValueRec(&rec);
+		rlp_strcpy(TmpTxt, 100, mkRangeRef(rec.top, rec.left, rec.bottom, rec.right));
+		}
+	if(!(Dlg = new DlgRoot(TwAnovDlg, data)))return;
+	hDlg = CreateDlgWnd("Two-Way Anova", 50, 50, 420, 220, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 1:
+			if(Dlg->GetText(101, TmpTxt+200, TMP_TXT_SIZE-200) &&(rD = new AccRange(TmpTxt+200))&& rD->BoundRec(&rec)
+				&& (vals = (double**)calloc(rec.bottom - rec.top +1, sizeof(double*)))) {
+				nc = rec.right-rec.left+1;				nr = rec.bottom-rec.top+1;
+				nvc = (int*)calloc(nc, sizeof(int));	nvr = (int*)calloc(nr, sizeof(int));
+				dmin = HUGE_VAL;						dmax = -HUGE_VAL;
+				if(Dlg->GetCheck(102)) hr = hc = 1;
+				else hr = hc = 0;
+				for(i = rec.top+hr; i <= rec.bottom; i++) vals[i-rec.top] = (double*)calloc(nc, sizeof(double));
+				for(c = rec.left+hc; c <= rec.right; c++) for(r = rec.top; r <= rec.bottom; r++) {
+					if(data->GetResult(&ares, r, c,false) && ares.type == ET_VALUE){
+						nvc[c-rec.left]++;				nvr[r-rec.top]++;
+						if(vals[r-rec.top]) vals[r-rec.top][c-rec.left] = ares.value;
+						if(ares.value > dmax) dmax = ares.value;
+						if(ares.value < dmin) dmin = ares.value;
+						}
+					}
+				while(!nvc[nc-1] && nc > 1) nc--;
+				while(!nvr[nr-1] && nr > 1) nr--;
+				for(i = 1, mr = nvr[0]; i < nr; i++)if(nvr[i] > mr) mr = nvr[i];
+				for(i = 1, mc = nvc[0]; i < nc; i++)if(nvc[i] > mc) mc = nvc[i];
+				for( ; nvr[hr] < mr && hr < nr; hr++);
+				for( ; nvc[hc] < mc && hc < nc; hc++);
+				for(i = hr; i < nr; i++) if(nvr[i] < mr) res = -1;
+				for(i = hc; i < nc; i++) if(nvc[i] < mc) res = -1;
+				for(i = 0, mr = nc-hc; i < nr; i++) nvr[i] = mr;
+				for(i = 0, mc = nr-hr; i < nc; i++) nvc[i] = mc;
+				if(res < 0 || mr < 2 || mc < 2) {
+					InfoBox("There are missing data!");
+					for(i = 0; i < nc; i++) if(vals[i]) free(vals[i]);
+					free(vals);			free(nvr);			free(nvc);
+					nvr = nvc = 0L;		vals = 0L;
+					bContinue = true;
+					}
+				delete rD;
+				}
+			break;
+		default:
+			nr = nc = 0;		break;
+			}
+		}while (res < 0);
+	if(res == 1 && (vals) && (cs = (double*)calloc(nc, sizeof(double))) 
+		&& (rs = (double*)calloc(nr, sizeof(double)))
+		&& (c_ss = (double*)calloc(nc, sizeof(double)))
+		&& (r_ss = (double*)calloc(nr, sizeof(double)))
+		&& (c_m = (double*)calloc(nc, sizeof(double)))
+		&& (r_m = (double*)calloc(nr, sizeof(double)))
+		&& (abc = (double*)calloc(nr > nc ? nr : nc, sizeof(double)))
+		&& (cnames = (char**)calloc(rec.right-rec.left+1, sizeof(char*)))
+		&& (rnames = (char**)calloc(rec.bottom-rec.top+1, sizeof(char*)))
+		&& (res_tab = (double**)calloc(4, sizeof(double*)))
+		&& (res_tab[0] = (double*)calloc(5, sizeof(double)))
+		&& (res_tab[1] = (double*)calloc(5, sizeof(double)))
+		&& (res_tab[2] = (double*)calloc(5, sizeof(double)))
+		&& (res_tab[3] = (double*)calloc(5, sizeof(double)))){
+		//get column and row descriptors
+		for(c = hc; c < nc; c++) {
+			if(rDesc = new AccRange(mkRangeRef(rec.top, rec.left+c, rec.bottom, rec.left+c))) {
+				cnames[c-hc] = rDesc->RangeDesc(data, hr ? 4 : 1);
+				delete rDesc;
+				}
+			}
+		for(r = hr; r < nr; r++) {
+			if(rDesc = new AccRange(mkRangeRef(rec.top+r, rec.left, rec.top+r, rec.right))) {
+				rnames[r-hr] = rDesc->RangeDesc(data, hc ? 4 : 1);
+				delete rDesc;
+				}
+			}
+		//grand mean
+		for(c = hc, gm = 0.0; c < nc; c++) for(r = hr; r < nr; r++) {
+			gm += vals[r][c];	cs[c] += vals[r][c];	rs[r] += vals[r][c];
+			}
+		gm /= ((double)((nc-hc)*(nr-hr)));
+		//anova stats
+		for(c = hc; c < nc; c++) cs[c] /= ((double)nvc[c]);
+		for(c = hc, ssc = 0.0; c < nc; c++) ssc += ((tmp = cs[c]-gm)*tmp); 
+		for(r = hr; r < nr; r++) rs[r] /= ((double)nvr[r]);
+		for(r = hr, ssr = 0.0; r < nr; r++) ssr += ((tmp = rs[r]-gm)*tmp);
+		ssc *= ((double)(nr-hr));		ssr *= ((double)(nc-hc));
+		for(c = hc, sse = 0.0; c < nc; c++) for(r = hr; r < nr; r++) {
+			sse += ((tmp = vals[r][c]-cs[c]-rs[r]+gm)*tmp);
+			}
+		for(c = hc; c < nc; c++) for(r = hr; r < nr; r++) {
+			c_m[c-hc] += vals[r][c];		r_m[r-hr] += vals[r][c];
+			}
+		for(c = hc; c < nc; c++) c_m[c-hc] /= ((double)(nvc[c]));
+		for(r = hr; r < nr; r++) r_m[r-hr] /= ((double)(nvr[r]));
+		for(c = hc; c < nc; c++) for(r = hr; r < nr; r++) {
+			c_ss[c-hc] += ((tmp = vals[r][c]-c_m[c-hc])*tmp);
+			r_ss[r-hr] += ((tmp = vals[r][c]-r_m[r-hr])*tmp);
+			}
+		//prepare table for report
+		res_tab[0][0] = (double)(nc-hc-1);			res_tab[1][0] = (double)(nr-hr-1);
+		res_tab[2][0] = res_tab[0][0] * res_tab[1][0];
+		res_tab[3][0] = res_tab[0][0] + res_tab[1][0] + res_tab[2][0];
+		res_tab[0][1] = ssc;						res_tab[0][2] = ssc/res_tab[0][0];
+		res_tab[1][1] = ssr;						res_tab[1][2] = ssr/res_tab[1][0];
+		res_tab[2][1] = sse;						res_tab[2][2] = sse/res_tab[2][0];
+		res_tab[3][1] = ssc + ssr + sse;
+		res_tab[0][3] = res_tab[0][2] / res_tab[2][2];
+		res_tab[1][3] = res_tab[1][2] / res_tab[2][2];
+		res_tab[0][4] = f_dist(res_tab[0][3], res_tab[0][0], res_tab[2][0]);
+		res_tab[1][4] = f_dist(res_tab[1][3], res_tab[1][0], res_tab[2][0]);
+		rep_init();
+		page = new Page(parent, data);
+		mk_header(page, "<b>Two-Way ANOVA</b>", data);
+		cx = txtdef1.fSize*5.0;		cy = txtdef1.fSize*10.0;
+		dBounds.Xmin = 0.5;			dBounds.Xmax = ((double)(nc-hc))+0.5;
+		dBounds.Ymin = dmin;		dBounds.Ymax = dmax;
+		//plot column results
+		if((graph = new Graph(parent, data, 0L, 0)) && (txt_obj = mk_scatt(0, 0L, c_m, c_ss, nvc+hc, nc-hc, "Mean", "Columns", "Means <u>+</u> S.D."))){
+			OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
+			if(LastOpenGO && LastOpenGO->Id == GO_PLOTSCATT) {
+				if(((PlotScatt*)LastOpenGO)->x_tv = new TextValue()){
+					for(i = 0; i < (nc-hc); i++) ((PlotScatt*)LastOpenGO)->x_tv->GetValue(cnames[i]);
+					}
+				}
+			free(txt_obj);								graph->moveable = 0;
+			graph->DRect.Xmax = graph->DRect.Xmin + graph->DRect.Ymax - graph->DRect.Ymin;
+			graph->GRect.Xmax = graph->GRect.Xmin + graph->GRect.Ymax - graph->GRect.Ymin;
+			scale.sx.fx = txtdef1.fSize*5.0;			scale.sy.fx = txtdef1.fSize*10.0;
+			graph->Command(CMD_SCALE, &scale, 0L);
+			page->Command(CMD_DROP_GRAPH, graph, 0L);
+			cx = graph->GetSize(SIZE_GRECT_RIGHT)+txtdef1.fSize;
+			cy = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef2.fSize*2.0;
+			}
+		dBounds.Ymin = 0.5;			dBounds.Ymax = ((double)(nr-hr))+0.5;
+		dBounds.Xmin = dmin;		dBounds.Xmax = dmax;
+		for(r = 0, tmp = 1.0; r < (nr-hr); r++, tmp += 1.0) abc[r] = tmp;
+		//plot row results
+		if((graph = new Graph(parent, data, 0L, 0x10)) && (txt_obj = mk_scatt(0x10, r_m, abc, r_ss, nvr+hr, nr-hr, "Mean", "Means <u>+</u> S.D.", "Rows"))){
+			OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
+			if(LastOpenGO && LastOpenGO->Id == GO_PLOTSCATT) {
+				if(((PlotScatt*)LastOpenGO)->y_tv = new TextValue()){
+					for(i = 0; i < nr; i++) ((PlotScatt*)LastOpenGO)->y_tv->GetValue(rnames[i]);
+					}
+				}
+			free(txt_obj);								graph->moveable = 0;
+			graph->DRect.Xmax = graph->DRect.Xmin + graph->DRect.Ymax - graph->DRect.Ymin;
+			graph->GRect.Xmax = graph->GRect.Xmin + graph->GRect.Ymax - graph->GRect.Ymin;
+			scale.sx.fx = cx;							scale.sy.fx = txtdef1.fSize*10.0;
+			graph->Command(CMD_SCALE, &scale, 0L);
+			page->Command(CMD_DROP_GRAPH, graph, 0L);
+			}
+		cx = txtdef1.fSize*5.0;
+		//draw anova table and clean up
+		rep_DrawText(page, cx, cy, false, TXA_HLEFT, &txtdef1, "<b>Anova:</b>");
+		cy = mk_table(page, cx, cy+txtdef2.fSize, 4, res_tab)+txtdef2.fSize;
+		if(!(parent->Command(CMD_DROP_GRAPH, page, 0L))) delete page;
+		free(c_ss);			free(r_ss);
+		free(c_m);			free(r_m);
+		}
+	if(vals) {
+		for(i = 0; i < nc; i++) if(vals[i]) free(vals[i]);
+		free(vals);
+		}
+	if(res_tab) {
+		for(i = 0; i < 4; i++) if(res_tab[i]) free(res_tab[i]);
+		free(res_tab);
+		}
+	if (cnames) {
+		for(c = 0; c < (rec.right-rec.left+1); c++) if(cnames[c]) free(cnames[c]);
+		free(cnames);
+		}
+	if (rnames) {
+		for(r = 0; r < (rec.bottom-rec.top+1); r++) if(rnames[r]) free(rnames[r]);
+		free(rnames);
+		}
+	if(nvr) free(nvr);		if(nvc) free(nvc);
+	CloseDlgWnd(hDlg);		delete Dlg;		free(TwAnovDlg);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Friedman's non-parametric two way anova
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void rep_fmanova(GraphObj *parent, DataObj *data)
+{
+	TabSHEET tab1 = {0, 40, 10, "Input Data"};
+	DlgInfo *FmAnovDlg;
+	void *dyndata[] = {(void*)&tab1, (void*)"rectangular range for variables",
+		(void*)" column/row headers present"};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, hc, hr, mr, mc, nr, nc, c, r, res, *nvr=0L, *nvc=0L;
+	bool bContinue = false;
+	char *mrk, *txt_obj, **cnames = 0L, **rnames = 0L;
+	double tmp, cx, cy, dmin, dmax, cchi2, rchi2, prob;
+	double **vals = 0L, **rows = 0L, **cols = 0L, *idx, *cs = 0L, *rs = 0L;
+	double *m, *b1, *b2, *w1, *w2, *trc;
+	RECT rec;
+	scaleINFO scale = {{0.0, 0.7}, {0.0, 0.7}, {0.0, 0.7}};
+	AccRange *rD =0L, *rDesc;
+	anyResult ares;
+	Graph *graph;
+	Page *page;
+
+	if(!parent || !data) return;
+	if(!(FmAnovDlg = CompileDialog(TwAnov_DlgTmpl, dyndata))) return;
+	if(data->Command(CMD_GETMARK, &mrk, 0L))rlp_strcpy(TmpTxt, TMP_TXT_SIZE, mrk);
+	else {
+		data->ValueRec(&rec);
+		rlp_strcpy(TmpTxt, 100, mkRangeRef(rec.top, rec.left, rec.bottom, rec.right));
+		}
+	if(!(Dlg = new DlgRoot(FmAnovDlg, data)))return;
+	hDlg = CreateDlgWnd("Friedman's Non-Parametric Two-Way Anova", 50, 50, 420, 220, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 1:
+			if(Dlg->GetText(101, TmpTxt+200, TMP_TXT_SIZE-200) &&(rD = new AccRange(TmpTxt+200))&& rD->BoundRec(&rec)
+				&& (vals = (double**)calloc(rec.bottom - rec.top +1, sizeof(double*)))) {
+				nc = rec.right-rec.left+1;				nr = rec.bottom-rec.top+1;
+				nvc = (int*)calloc(nc, sizeof(int));	nvr = (int*)calloc(nr, sizeof(int));
+				dmin = HUGE_VAL;						dmax = -HUGE_VAL;
+				if(Dlg->GetCheck(102)) hr = hc = 1;
+				else hr = hc = 0;
+				for(i = rec.top+hr; i <= rec.bottom; i++) vals[i-rec.top] = (double*)calloc(nc, sizeof(double));
+				for(c = rec.left+hc; c <= rec.right; c++) for(r = rec.top; r <= rec.bottom; r++) {
+					if(data->GetResult(&ares, r, c,false) && ares.type == ET_VALUE){
+						nvc[c-rec.left]++;				nvr[r-rec.top]++;
+						if(vals[r-rec.top]) vals[r-rec.top][c-rec.left] = ares.value;
+						if(ares.value > dmax) dmax = ares.value;
+						if(ares.value < dmin) dmin = ares.value;
+						}
+					}
+				while(!nvc[nc-1] && nc > 1) nc--;
+				while(!nvr[nr-1] && nr > 1) nr--;
+				for(i = 1, mr = nvr[0]; i < nr; i++)if(nvr[i] > mr) mr = nvr[i];
+				for(i = 1, mc = nvc[0]; i < nc; i++)if(nvc[i] > mc) mc = nvc[i];
+				for( ; nvr[hr] < mr && hr < nr; hr++);
+				for( ; nvc[hc] < mc && hc < nc; hc++);
+				for(i = hr; i < nr; i++) if(nvr[i] < mr) res = -1;
+				for(i = hc; i < nc; i++) if(nvc[i] < mc) res = -1;
+				for(i = 0, mr = nc-hc; i < nr; i++) nvr[i] = mr;
+				for(i = 0, mc = nr-hr; i < nc; i++) nvc[i] = mc;
+				if(res < 0 || mr < 2 || mc < 2) {
+					InfoBox("There are missing data!");
+					for(i = 0; i < nc; i++) if(vals[i]) free(vals[i]);
+					free(vals);			free(nvr);			free(nvc);
+					nvr = nvc = 0L;		vals = 0L;
+					bContinue = true;
+					}
+				delete rD;
+				}
+			break;
+		default:
+			nr = nc = 0;		break;
+			}
+		}while (res < 0);
+	if(res == 1&& (vals) && (cs = (double*)calloc(nr, sizeof(double))) 
+		&& (rs = (double*)calloc(nc, sizeof(double)))
+		&& (cols = (double**)calloc(nc, sizeof(double*)))
+		&& (rows = (double**)calloc(nr, sizeof(double*)))
+		&& (cnames = (char**)calloc(rec.right-rec.left+1, sizeof(char*)))
+		&& (rnames = (char**)calloc(rec.bottom-rec.top+1, sizeof(char*)))
+		&& (idx = (double*)malloc((nr > nc ? nr:nc)*sizeof(double)))
+		&& (m = (double*)malloc((nr > nc ? nr:nc)*sizeof(double)))
+		&& (b1 = (double*)malloc((nr > nc ? nr:nc)*sizeof(double)))
+		&& (b2 = (double*)malloc((nr > nc ? nr:nc)*sizeof(double)))
+		&& (w1 = (double*)malloc((nr > nc ? nr:nc)*sizeof(double)))
+		&& (w2 = (double*)malloc((nr > nc ? nr:nc)*sizeof(double)))
+		&& (trc = (double*)malloc((nr > nc ? nr:nc)*sizeof(double)))) {
+		//get column and row descriptors
+		for(c = hc; c < nc; c++) {
+			if(rDesc = new AccRange(mkRangeRef(rec.top, rec.left+c, rec.bottom, rec.left+c))) {
+				cnames[c-hc] = rDesc->RangeDesc(data, hr ? 4 : 1);
+				delete rDesc;
+				}
+			}
+		for(r = hr; r < nr; r++) {
+			if(rDesc = new AccRange(mkRangeRef(rec.top+r, rec.left, rec.top+r, rec.right))) {
+				rnames[r-hr] = rDesc->RangeDesc(data, hc ? 4 : 1);
+				delete rDesc;
+				}
+			}
+		//create ranks
+		for(c = hc; c < nc; c++) if(cols[c-hc] = (double*)calloc(nr, sizeof(double))) {
+			for(r = hr; r < nr; r++) {
+				cols[c-hc][r-hr] = vals[r][c];		idx[r-hr] = (double)(r-hr);
+				}
+			SortArray2(nr-hr, cols[c-hc], idx);		crank(nr-hr, cols[c-hc], &tmp);
+			SortArray2(nr-hr, idx, cols[c-hc]);
+			}
+		for(r = hr; r < nr; r++) if(rows[r-hr] = (double*)calloc(nc, sizeof(double))) {
+			for(c = hc; c < nc; c++) {
+				rows[r-hr][c-hc] = vals[r][c];		idx[c-hc] = (double)(c-hc);
+				}
+			SortArray2(nc-hc, rows[r-hr], idx);		crank(nc-hc, rows[r-hr], &tmp);
+			SortArray2(nc-hc, idx, rows[r-hr]);	
+			}
+		for(r = 0; r < (nr-hr); r++) for(c = 0; c < (nc-hc); c++){
+			cs[r] += cols[c][r];	rs[c] += rows[r][c];
+			}
+		//rank sums and statistics
+		for(r = 0, cchi2 = 0.0; r < (nr-hr); r++) cchi2 += (cs[r]*cs[r]);
+		cchi2 = cchi2 * 12.0 / ((double)((nr-hr)*(nc-hc)*(nr-hr+1))) - 3.0*(nc-hc)*(nr-hr+1);
+		for(c = 0, rchi2 = 0.0; c < (nc-hc); c++) rchi2 += (rs[c]*rs[c]);
+		rchi2 = rchi2 * 12.0 / ((double)((nc-hc)*(nr-hr)*(nc-hc+1))) - 3.0*(nr-hr)*(nc-hc+1);
+		//create report page
+		rep_init();
+		page = new Page(parent, data);
+		mk_header(page, "<b>Friedman's non-parametric two-way ANOVA</b>", data);
+		cx = txtdef1.fSize*5.0;		cy = txtdef1.fSize*10.0;
+		//plot column results
+		for(c = hc; c < nc; c++) {
+			w1[c-hc] = HUGE_VAL;		w2[c-hc] = -HUGE_VAL;
+			for(r = hr; r < nr; r++) {
+				trc[r-hr] = vals[r][c];
+				if(vals[r][c] < w1[c-hc]) w1[c-hc] = vals[r][c];
+				if(vals[r][c] > w2[c-hc]) w2[c-hc] = vals[r][c];
+				}
+			d_quartile(r-hr, trc, b1+c-hc, m+c-hc, b2+c-hc);
+			}
+		dBounds.Xmin = 0.5;			dBounds.Xmax = ((double)(nc-hc))+0.5;
+		dBounds.Ymin = dmin;		dBounds.Ymax = dmax;
+		if((graph = new Graph(parent, data, 0L, 0)) && 
+			(txt_obj = mk_boxplot(0, 0L, m, b1, b2, w1, w2, nvc, nc-hc, "medians", "25-75%", "min/max"))){
+			OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
+			if(LastOpenGO && LastOpenGO->Id == GO_BOXPLOT) {
+				if(((BoxPlot*)LastOpenGO)->x_tv = new TextValue()){
+					for(i = 0; i < (nc-hc); i++) ((PlotScatt*)LastOpenGO)->x_tv->GetValue(cnames[i]);
+					}
+				if(((BoxPlot*)LastOpenGO)->x_info=(char*)malloc(20*sizeof(char)))
+					rlp_strcpy(((BoxPlot*)LastOpenGO)->x_info, 20, "Columns");
+				if(((BoxPlot*)LastOpenGO)->y_info=(char*)malloc(20*sizeof(char)))
+					rlp_strcpy(((BoxPlot*)LastOpenGO)->y_info, 20, "Location");
+				}
+			free(txt_obj);								graph->moveable = 0;
+			graph->DRect.Xmax = graph->DRect.Xmin + graph->DRect.Ymax - graph->DRect.Ymin;
+			graph->GRect.Xmax = graph->GRect.Xmin + graph->GRect.Ymax - graph->GRect.Ymin;
+			scale.sx.fx = txtdef1.fSize*5.0;			scale.sy.fx = txtdef1.fSize*10.0;
+			graph->Command(CMD_SCALE, &scale, 0L);
+			page->Command(CMD_DROP_GRAPH, graph, 0L);
+			cx = graph->GetSize(SIZE_GRECT_RIGHT)+txtdef1.fSize;
+			}
+		//plot row results
+		for(r = hr; r < nr; r++) {
+			w1[r-hr] = HUGE_VAL;		w2[r-hr] = -HUGE_VAL;
+			for(c = hc; c < nc; c++) {
+				trc[c-hc] = vals[r][c];
+				if(vals[r][c] < w1[r-hr]) w1[r-hr] = vals[r][c];
+				if(vals[r][c] > w2[r-hr]) w2[r-hr] = vals[r][c];
+				if(vals[r][c] < dBounds.Xmin) dBounds.Xmin = vals[r][c];
+				if(vals[r][c] > dBounds.Xmax) dBounds.Xmax = vals[r][c];
+				}
+			d_quartile(c-hc, trc, b1+r-hr, m+r-hr, b2+r-hr);
+			}
+		for(r = hr; r < nr; r++) trc[r-hr] = ((double)(r-hr+1));
+		dBounds.Ymin = 0.5;			dBounds.Ymax = ((double)(nr-hr))+0.5;
+		dBounds.Xmin = dmin;		dBounds.Xmax = dmax;
+		if((graph = new Graph(parent, data, 0L, 0x10)) && 
+			(txt_obj = mk_boxplot(1, m, trc, b1, b2, w1, w2, nvr, nr-hr, "medians", "25-75%", "min/max"))){
+			OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
+			if(LastOpenGO && LastOpenGO->Id == GO_BOXPLOT) {
+				if(((BoxPlot*)LastOpenGO)->y_tv = new TextValue()){
+					for(i = 0; i < (nr-hr); i++)((PlotScatt*)LastOpenGO)->y_tv->GetValue(rnames[i]);
+					}
+				if(((BoxPlot*)LastOpenGO)->y_info=(char*)malloc(20*sizeof(char)))
+					rlp_strcpy(((BoxPlot*)LastOpenGO)->y_info, 20, "Rows");
+				if(((BoxPlot*)LastOpenGO)->x_info=(char*)malloc(20*sizeof(char)))
+					rlp_strcpy(((BoxPlot*)LastOpenGO)->x_info, 20, "Location");
+				}
+			free(txt_obj);								graph->moveable = 0;
+			graph->DRect.Xmax = graph->DRect.Xmin + graph->DRect.Ymax - graph->DRect.Ymin;
+			graph->GRect.Xmax = graph->GRect.Xmin + graph->GRect.Ymax - graph->GRect.Ymin;
+			scale.sx.fx = cx;							scale.sy.fx = txtdef1.fSize*10.0;
+			graph->Command(CMD_SCALE, &scale, 0L);
+			page->Command(CMD_DROP_GRAPH, graph, 0L);
+			}
+		cx = txtdef1.fSize*5.0;			cy = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef2.fSize*2.0;
+		free(m);	free(b1);	free(b2);	free(w1);	free(w2);	free(trc);
+		rep_DrawText(page, cx, cy, false, TXA_HLEFT, &txtdef1, "<b>Between columns:</b>");
+		cy += txtdef1.fSize *1.5;
+		prob = chi_dist(fabs(rchi2), nc-hc-1, 0.0);
+#ifdef USE_WIN_SECURE
+		i = sprintf_s(TmpTxt, 60, "N = %d, Chi<sup>2</sup> = %.2lf, P ", nc-hc, rchi2);
+		if(prob > 0.001) sprintf_s(TmpTxt+i, 20, "= %.3lf", prob);
+#else
+		i = sprintf(TmpTxt, "N = %d, Chi<sup>2</sup> = %.2lf, P ", nc-hc, rchi2);
+		if(prob > 0.001) sprintf(TmpTxt+i, "= %.3lf", prob);
+#endif
+		else rlp_strcpy(TmpTxt+i, 40, "< 0.001");
+		rep_DrawText(page, cx+txtdef1.fSize*3.0, cy, false, TXA_HLEFT, &txtdef1, TmpTxt);
+		cy += txtdef1.fSize*2.0;
+		rep_DrawText(page, cx, cy, false, TXA_HLEFT, &txtdef1, "<b>Between rows:</b>");
+		cy += txtdef1.fSize *1.5;
+		prob = chi_dist(fabs(cchi2), nr-hr-1, 0.0);
+#ifdef USE_WIN_SECURE
+		i = sprintf_s(TmpTxt, 60, "N = %d, Chi<sup>2</sup> = %.2lf, P ", nr-hr, cchi2);
+		if(prob > 0.001) sprintf_s(TmpTxt+i, 20, "= %.3lf", prob);
+#else
+		i = sprintf(TmpTxt, "N = %d, Chi<sup>2</sup> = %.2lf, P ", nr-hr, cchi2);
+		if(prob > 0.001) sprintf(TmpTxt+i, "= %.3lf", prob);
+#endif
+		else rlp_strcpy(TmpTxt+i, 40, "< 0.001");
+		rep_DrawText(page, cx+txtdef1.fSize*3.0, cy, false, TXA_HLEFT, &txtdef1, TmpTxt);
+		if(!(parent->Command(CMD_DROP_GRAPH, page, 0L))) delete page;
+		free(rs);		free(cs);		free(idx);
+		}
+	if(cols) {
+		for(i = 0; i < nc; i++) if(cols[i]) free(cols[i]);
+		free(cols);
+		}
+	if(rows) {
+		for(i = 0; i < nr; i++) if(rows[i]) free(rows[i]);
+		free(rows);
+		}
+	if(vals) {
+		for(i = 0; i < nc; i++) if(vals[i]) free(vals[i]);
+		free(vals);
+		}
+	if (cnames) {
+		for(c = 0; c < (rec.right-rec.left+1); c++) if(cnames[c]) free(cnames[c]);
+		free(cnames);
+		}
+	if (rnames) {
+		for(r = 0; r < (rec.bottom-rec.top+1); r++) if(rnames[r]) free(rnames[r]);
+		free(rnames);
+		}
+	if(nvr) free(nvr);		if(nvc) free(nvc);
+	CloseDlgWnd(hDlg);		delete Dlg;		free(FmAnovDlg);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Two way anova with replica
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *TwAnovDlg_Tmpl = 
+	"1,2,,DEFAULT,PUSHBUTTON,-1,148,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,148,25,45,12\n"
+	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,10,100,ISPARENT | CHECKED,SHEET,1,5,10,130,80\n"
+	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,101,,,LTEXT,2,10,30,60,8\n"
+	"101,102,,,RANGEINPUT,-15,20,40,100,10\n"
+	"102,103,,,LTEXT,3,20,55,53,8\n"
+	"103,,,LASTOBJ,EDVAL1,4,75,55,30,10";
+
+typedef struct _anov_group_info {
+	double *rmeans, *cmeans;
+	int *vpr, *vpc, nvals;
+	double mean;
+	}anov_group_info;
+
+void rep_twoway_anova(GraphObj *parent, DataObj *data)
+{
+	TabSHEET tab1 = {0, 40, 10, "Input Data"};
+	DlgInfo *TwAnovDlg;
+	double dlpr = 3.0, *cmeans;
+	void *dyndata[] = {(void*)&tab1, (void*)"rectangular range for variables", (void*)"lines per replica:",
+		(void*)&dlpr};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, k, res, ntot, lpr, ngr, r1, r2, c1, c2, iErr;
+	int *vpc, *vpr;
+	double tmp, dn, gMean, SSwithin, SSsubgr, SStotal, SSrows, SScols, SSinteract;
+	double **res_tab = 0L, cx, cy;
+	bool bContinue = false;
+	AccRange *rD =0L;
+	char *mrk;
+	RECT rec;
+	anyResult ares;
+	anov_group_info *agr;
+	Page *page;
+
+	if(!parent || !data) return;
+	if(!(TwAnovDlg = CompileDialog(TwAnovDlg_Tmpl, dyndata))) return;
+	if(data->Command(CMD_GETMARK, &mrk, 0L))rlp_strcpy(TmpTxt, TMP_TXT_SIZE, mrk);
+	else {
+		data->ValueRec(&rec);
+		rlp_strcpy(TmpTxt, 100, mkRangeRef(rec.top, rec.left, rec.bottom, rec.right));
+		}
+	if(!(Dlg = new DlgRoot(TwAnovDlg, data)))return;
+	hDlg = CreateDlgWnd("Two-Way Anova with Replica", 50, 50, 420, 220, Dlg, 0x4L);
+loop1:
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 1:
+			Dlg->GetValue(103, &dlpr);		lpr = (int)dlpr;
+			break;
+			}
+		}while (res < 0);
+	if(res == 1 && Dlg->GetText(101, TmpTxt+200, TMP_TXT_SIZE-200) &&(rD = new AccRange(TmpTxt+200))
+		&& rD->BoundRec(&rec) && (ntot = rD->CountItems())){
+		r1 = rec.top;		r2 = rec.bottom+1;			c1= rec.left;	c2 = rec.right+1;
+		vpc = (int*)calloc(c2-c1+1, sizeof(int));		vpr = (int*)calloc(r2-r1+1, sizeof(int));
+		for(k = iErr = 0; k < 2; k++) {
+			for(i = c1;	i < c2; i++) for(j = r1; j < r2; j++) {
+				data->GetResult(&ares, j, i, false);
+				if(ares.type == ET_VALUE) {
+					vpc[i-c1]++;					vpr[j-r1]++;
+					}
+				}
+			if(!k) {
+				for(i = 0; !vpc[i]; i++);			for(j = 0; !vpr[j]; j++);
+				memset(vpc,0,sizeof(int)*(c2-c1));	memset(vpr,0,sizeof(int)*(r2-r1));
+				c1 += i;		r1 += j;
+				}
+			}
+		while(c2 > c1 && !vpc[c2-c1-1]) c2--;		while(r2 > r1 && !vpr[r2-r1-1]) r2--;
+		ngr = (int)(((double)(r2-r1))/dlpr);
+		if(ngr * lpr < r2 -r1) iErr = 2;
+		agr = (anov_group_info *)calloc(ngr+1, sizeof(anov_group_info));
+		cmeans = (double *)calloc(c2-c1+1, sizeof(double));
+		for(i = 0; i <= ngr; i++) {
+			agr[i].cmeans = (double*)calloc(c2-c1+2, sizeof(double));
+			agr[i].vpc = (int*)calloc(c2-c1+2, sizeof(int));
+			agr[i].rmeans = (double*)calloc(lpr+2, sizeof(double));
+			agr[i].vpr = (int*)calloc(lpr+2, sizeof(int));
+			}
+		for(i = c1; i < c2; i++) for(j = r1; j < r2; j++) {
+			k = (j-r1)/lpr;		data->GetResult(&ares, j, i, false);
+			if(ares.type == ET_VALUE) {
+				agr[k].cmeans[i-c1] += ares.value;		agr[k].vpc[i-c1]++;
+				agr[k].rmeans[j-k*lpr] += ares.value;	agr[k].vpr[j-k*lpr]++;
+				agr[k].mean += ares.value;				agr[k].nvals++;
+				cmeans[i-c1] += ares.value;
+				}
+			else iErr = 1;
+			}
+		for(k = 0; k < ngr; k++) {
+			agr[k].mean /= ((double)(agr[k].nvals));
+			for(i = 0; i < (c2-c1); i++) {
+				agr[k].cmeans[i] /= ((double)(agr[k].vpc[i]));
+				}
+			}
+		for(i = c1, SSwithin = gMean = 0.0, dn = 1.0; i < c2; i++) for(j = r1; j < r2; j++) {
+			k = (j-r1)/lpr;		data->GetResult(&ares, j, i, false);
+			if(ares.type == ET_VALUE) {
+				SSwithin += ((tmp = ares.value-agr[k].cmeans[i-c1])*tmp);
+				gMean += ((ares.value - gMean)/dn);		dn	+=	1.0;
+				}
+			}
+		for(k = 0, SSsubgr = SSrows = 0.0; k < ngr; k++) {
+			for(i = 0; i < (c2-c1); i++) {
+				SSsubgr += (dlpr*((tmp = agr[k].cmeans[i] - gMean) * tmp));
+				}
+			SSrows += ((tmp = (agr[k].mean - gMean)) * tmp);
+			}
+		for(i = c1, SScols = 0.0; i < c2; i++) {
+			cmeans[i-c1] /= ((double)(r2-r1));
+			SScols += ((tmp = cmeans[i-c1]-gMean) * tmp);
+			}
+		SStotal = SSsubgr + SSwithin;		SSrows *= (dlpr *((double)(c2-c1)));
+		SScols *= (dlpr * ((double)ngr));	SSinteract = fabs(SSsubgr - SSrows - SScols);
+		if(!iErr&& (res_tab = (double**)calloc(5, sizeof(double*)))
+			&& (res_tab[0] = (double*)calloc(5, sizeof(double)))
+			&& (res_tab[1] = (double*)calloc(5, sizeof(double)))
+			&& (res_tab[2] = (double*)calloc(5, sizeof(double)))
+			&& (res_tab[3] = (double*)calloc(5, sizeof(double)))
+			&& (res_tab[4] = (double*)calloc(5, sizeof(double)))) {
+			res_tab[0][0] = (double)(ngr-1);				res_tab[1][0] = (double)(c2-c1-1);
+			res_tab[2][0] = res_tab[0][0]* res_tab[1][0];	res_tab[3][0] = (double)(ngr*(c2-c1)*(lpr-1));
+			res_tab[4][0] = res_tab[0][0] + res_tab[1][0] + res_tab[2][0] + res_tab[3][0];
+			res_tab[0][1] = SSrows;			res_tab[0][2] = res_tab[0][1] / res_tab[0][0];
+			res_tab[1][1] = SScols;			res_tab[1][2] = res_tab[1][1] / res_tab[1][0];
+			res_tab[2][1] = SSinteract;		res_tab[2][2] = res_tab[2][1] / res_tab[2][0];
+			res_tab[3][1] = SSwithin;		res_tab[3][2] = res_tab[3][1] / res_tab[3][0];
+			res_tab[4][1] = SStotal;		res_tab[0][3] = res_tab[0][2] / res_tab[3][2];
+			res_tab[1][3] = res_tab[1][2] / res_tab[3][2];
+			res_tab[2][3] = res_tab[2][2] / res_tab[3][2];
+			res_tab[0][4] = f_dist(res_tab[0][3], res_tab[0][0], res_tab[3][0]);
+			res_tab[1][4] = f_dist(res_tab[1][3], res_tab[1][0], res_tab[3][0]);
+			res_tab[2][4] = f_dist(res_tab[2][3], res_tab[2][0], res_tab[3][0]);
+
+
+			rep_init();
+//			dBounds.Xmin = 0.5;				dBounds.Xmax = ((double)nc)+0.5;
+			page = new Page(parent, data);
+			mk_header(page, "<b>Two-Way ANOVA with Replication</b>", data);
+
+			cx = txtdef1.fSize*5.0;		cy = txtdef1.fSize*10.0;
+
+			rep_DrawText(page, cx, cy, false, TXA_HLEFT, &txtdef1, "<b>Anova:</b>");
+			cy = mk_table(page, cx, cy+txtdef2.fSize, 3, res_tab)+txtdef2.fSize;
+			if(!(parent->Command(CMD_DROP_GRAPH, page, 0L))) delete page;
+			free(res_tab[0]);			free(res_tab[1]);			free(res_tab[2]);
+			free(res_tab[3]);			free(res_tab[4]);			free(res_tab);
+			}
+		for(i = 0; i <= ngr; i++) {
+			free(agr[i].cmeans);					free(agr[i].vpc);
+			free(agr[i].rmeans);					free(agr[i].vpr);
+			}
+		free(vpc);			free(vpr);		free(cmeans);
+		delete rD;
+		switch(iErr) {
+			case 1:		
+				ErrorBox("There are missing Data!");
+				goto loop1;
+			case 2:		
+				ErrorBox("The total number of lines\nmust be a multiple of\nlines per replica.");
+				goto loop1;
+			}
+		}
+		CloseDlgWnd(hDlg);		delete Dlg;		free(TwAnovDlg);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Kruskal-Wallis Test for Differences of Location
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *RepKruskal_DlgTmpl =
+	"1,2,,DEFAULT,PUSHBUTTON,-1,158,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,158,25,45,12\n"
+	"3,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"10,20,152,ISPARENT | CHECKED, SHEET,1,5,10,140,70\n"
+	"20,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"152,153,,ISPARENT | CHECKED,GROUPBOX,2,12,30,128,45\n"
+	"153,154,,,LTEXT,0,25,35,60,8\n"
+	"154,155,,,RANGEINPUT,0,25,45,100,10\n"
+	"155,156,0,,PUSHBUTTON,-8,95,57,30,12\n"
+	"156,,,LASTOBJ,PUSHBUTTON,-9,60,57,35,12";
+
+void rep_kruskal(GraphObj *parent, DataObj *data)
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	
+	void *dyndata[] = {(void*)&tab1, (void*)" select one range for every variable "};
+	DlgInfo *KruskalDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, n, c, r, nt, res, currYR = 0, maxYR = 0, ny, nr, *nvals;
+	bool updateYR = true, bContinue = false;
+	double h, h1, p, **vals, *x, *y, *by1, *by2, *wy1, *wy2, *ranks, *ridx, *rsums, th, cy, cx[10];
+	char **rd = 0L, **names, *txt_obj;
+	char *headings[] = {"<i>Groups</i>", "<i>N</i>", "<i>Median</i>", "<i>25% - 75%</i>",
+		"<i>Range</i>","<i>Rank Sums</i>"};
+	scaleINFO scale = {{0.0, 1.0}, {0.0, 1.0}, {0.0, 1.0}};
+	AccRange *rV1 = 0L;
+	anyResult ares;
+	Page *page;
+	Graph *graph;
+
+	if(!parent || !data) return;
+	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
+		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return;
+	if(!(KruskalDlg = CompileDialog(RepKruskal_DlgTmpl, dyndata))) return;
+	if(TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
+		for(i=j=0; i <= 1000; i +=100) if(TmpTxt[i]) 
+			rd[j++] = (char*)memdup(TmpTxt+i, ((int)strlen(TmpTxt+i))+2, 0);	 maxYR = j-1;
+		}
+	if(!rd && !(rd = (char**)calloc(1, sizeof(char*))))return;
+	if(!(Dlg = new DlgRoot(KruskalDlg, data))) return;
+	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
+	hDlg = CreateDlgWnd("Kruskal-Wallis Nonparametric Anova", 50, 50, 420, 200, Dlg, 0x4L);
+	do {
+		if(updateYR) {
+			if(currYR >0) Dlg->ShowItem(156, true);
+			else Dlg->ShowItem(156, false);
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "variable # %d/%d", currYR+1, maxYR+1);
+#else
+			sprintf(TmpTxt,"variable # %d/%d", currYR+1, maxYR+1);
+#endif
+			Dlg->SetText(153, TmpTxt);
+			updateYR = false;
+			}
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 0:
+			if(bContinue || Dlg->GetCheck(20)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 155:		case 156:
+			res = com_StackDlg(res, Dlg, 0L, 0L, &rd, &currYR,
+				&rV1, &bContinue, &ny, &maxYR, &updateYR);
+			break;
+			}
+		}while (res < 0);
+	if(res == 1 && (vals = (double**)calloc(sizeof(double*), maxYR+1)) && (nvals = (int*)calloc(sizeof(int), maxYR+1))
+		&& (names = (char**)calloc(maxYR+1, sizeof(char*))) && (x = (double*)calloc(maxYR+1, sizeof(double)))
+		&& (y = (double*)calloc(maxYR+1, sizeof(double))) && (by1 = (double*)calloc(maxYR+1, sizeof(double)))
+		&& (by2 = (double*)calloc(maxYR+1, sizeof(double))) && (wy1 = (double*)calloc(maxYR+1, sizeof(double)))
+		&& (wy2 = (double*)calloc(maxYR+1, sizeof(double)))
+		&& (rsums = (double*)calloc(maxYR+1, sizeof(double)))) {
+		maxYR++;	rep_init();		page = new Page(parent, data);
+		dBounds.Xmin = 0.5;			dBounds.Xmax = (double)maxYR+0.3;
+		if(rV1) delete rV1;			rV1 = 0L;	ranks = ridx = 0L;
+		cy = txtdef1.fSize*10.0;
+		mk_header(page, "<b>Kruskal-Wallis Test for Differences of Location</b>", data);
+		dBounds.Ymin = HUGE_VAL;	dBounds.Ymax = -HUGE_VAL;
+		// get data into two dimensional array
+		for(nr = maxYR, i = nt = 0; i < nr; i++) {
+			x [i] = y[i] = by1[i] = by2[i] = wy1[i] = wy2[i] = 0.0;		nvals[i] = 0;
+			if((rV1 = new AccRange(rd[i])) && (n = rV1->CountItems()) && (vals[i] = (double*)malloc(n*sizeof(double)))) {
+				names[i] = rV1->RangeDesc(data, 1);
+				for(n = 0, rV1->GetFirst(&c, &r); rV1->GetNext(&c, &r); ) {
+					if(data->GetResult(&ares, r, c, false) && ares.type == ET_VALUE) {
+						if(!n) wy1[i] = wy2[i] = ares.value;
+						else {
+							if(ares.value < wy1[i]) wy1[i] = ares.value;
+							if(ares.value > wy2[i]) wy2[i] = ares.value;
+							}
+						if(ares.value < dBounds.Ymin) dBounds.Ymin = ares.value;
+						if(ares.value > dBounds.Ymax) dBounds.Ymax = ares.value;
+						vals[i][n] = ares.value;		n++;
+						}
+					}
+				nvals[i] = n;	nt += n;	delete rV1;		rV1 = 0;
+				}
+			if(!names[i] && (names[i] = (char*)malloc(20*sizeof(char)))){
+#ifdef USE_WIN_SECURE
+				sprintf_s(names[i], 20, "Group %d", i+1);
+#else
+				sprintf(names[i], "Group %d", i+1);
+#endif
+				}
+			}
+		// rank sums
+		if(nt && (ranks=(double*)malloc(nt*sizeof(double))) && (ridx=(double*)malloc(nt*sizeof(double)))) {
+			for(i = n = 0; i < nr; i++) {
+				for(j = 0; j < nvals[i]; j++) {
+					ridx[n] = (double)(i);	ranks[n] = vals[i][j];	n++;
+					}
+				}
+			SortArray2(n, ranks, ridx);			crank(n, ranks, &th);
+			for(i = 0; i < n; i++) rsums[(int)ridx[i]] += ranks[i];
+			//statistics on range sums
+			for(i = 0, h = 0.0; i < nr; i++) h += rsums[i]*rsums[i]/((double)nvals[i]);
+			h = h * 12.0/(((double)n)*((double)(n+1))) - 3.0*((double)n+1);
+			h1 = h  / (1.0 - th/(((double)(n-1)) * ((double)n)* ((double)(n+1))));
+			}
+		else h = h1 = -1.0;
+		// check for unique names
+		for(i = 0; i < (nr-1); i++) for(j = i+1; j < nr; j++) {
+			if(!strcmp(names[i], names[j])) {
+				names[i] = (char*) realloc(names[i], 20 *sizeof(char));
+				names[j] = (char*) realloc(names[j], 20 *sizeof(char));
+#ifdef USE_WIN_SECURE
+				sprintf_s(names[i], 20, "Group %d", i+1);	sprintf_s(names[j], 20, "Group %d", j+1);
+#else
+				sprintf(names[i], "Group %d", i+1);			sprintf(names[j], "Group %d", j+1);
+#endif
+				}
+			}
+		// simple group statistics
+		for(i = 0; i < nr; i++) {
+			x[i] = (double)(i+1);		d_quartile(nvals[i], vals[i], by1+i, y+i, by2+i);
+			}
+		// create boxplot
+		if((graph = new Graph(parent, data, 0L, 0)) && (txt_obj = mk_boxplot(0, x, y, by1, by2, wy1, wy2,
+			nvals, nr,"Median","25-75%","Min./Max."))){
+			scale.sx.fx = (txtdef1.fSize*5.0);			scale.sy.fx = (txtdef1.fSize*10.0);
+			OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
+			if(LastOpenGO && LastOpenGO->Id == GO_BOXPLOT) {
+				if(((BoxPlot*)LastOpenGO)->x_tv = new TextValue()){
+					for(i = 0; i < nr; i++) ((BoxPlot*)LastOpenGO)->x_tv->GetValue(names[i]);
+					}
+				if(((BoxPlot*)LastOpenGO)->x_info = (char*)malloc(20*sizeof(char)))
+					rlp_strcpy(((BoxPlot*)LastOpenGO)->x_info, 20, "Groups");
+				if(((BoxPlot*)LastOpenGO)->y_info = (char*)malloc(20*sizeof(char)))
+					rlp_strcpy(((BoxPlot*)LastOpenGO)->y_info, 20, "Location");
+				}
+			free(txt_obj);		graph->Command(CMD_SCALE, &scale, 0L);
+			cy = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef1.fSize*2.0;
+			page->Command(CMD_DROP_GRAPH, graph, 0L);
+			}
+		parent->Command(CMD_DROP_GRAPH, page, 0L);
+		//report statistics
+		cx[0] = txtdef1.fSize*5.0;				cx[1] = cx[0] + linsp1*5.0;
+		cx[2] = cx[1] + linsp1*2.5;		cx[3] = cx[2] + linsp1*4.0;
+		cx[4] = cx[3] + linsp1*5.0;		cx[5] = cx[4] + linsp1*7.0;
+		cx[6] = cx[5] + linsp1*8.0;
+		rep_DrawText(page, cx[0], cy, false, TXA_HLEFT, &txtdef1, "<b>Test Statistics:</b>");
+		cy += linsp2;			p = chi_dist(h,(double)(nr-1), 0.0);
+		dbl_to_str2(TmpTxt, 100, p < 0.0001 ?(char*)"H = %.2lf, P < 0.0001" : (char*)"H = %.2lf, P = %.4lf", h, p);
+		rep_DrawText(page, cx[1], cy, false, TXA_HLEFT, &txtdef1, TmpTxt);
+		cy += linsp1;			p = chi_dist(h1,(double)(nr-1), 0.0);
+		dbl_to_str2(TmpTxt, 100, p < 0.0001 ?(char*)"H(corr.) = %.2lf, P < 0.0001" : (char*)"H(corr.) = %.2lf, P = %.4lf", h1, p);
+		if(th >= 1.0) {
+			rep_DrawText(page, cx[1], cy, false, TXA_HLEFT, &txtdef1, TmpTxt);
+			cy += linsp1;
+			}
+		cy += linsp2;
+		// create summary table
+		rep_DrawText(page, cx[0], cy, false, TXA_HLEFT, &txtdef1, "<b>Summary:</b>");
+		for(i = 0, cy += linsp2; i < 6; i++) {				//column headers
+			c = (i == 3 || i == 4) ? TXA_HCENTER : TXA_HRIGHT;
+			rep_DrawText(page, cx[i+1], cy, false, c, &txtdef1, headings[i]);
+			}
+		mk_hr(page, cx[0], cx[6]+txtdef1.fSize*2.0, cy +linsp1);
+		for(i = 0, cy += linsp2; i < nr; i++, cy += linsp1) {
+			for(j = 0; j < 6; j++) {
+				switch(j) {
+				default:	rlp_strcpy(TmpTxt, 20, names[i]);						break;
+				case 1:		dbl_to_str1(TmpTxt, 20, "%.0lf", (double)nvals[i]);		break;
+				case 2:		dbl_to_str1(TmpTxt, 20, "%g", y[i]);					break;
+				case 3:		dbl_to_str2(TmpTxt, 20, "%g - %g", by1[i], by2[i]);		break;
+				case 4:		dbl_to_str2(TmpTxt, 20, "%g - %g", wy1[i], wy2[i]);		break;
+				case 5:		dbl_to_str1(TmpTxt, 20, "%g", rsums[i]);				break;
+					}
+				c = (j == 3 || j == 4) ? TXA_HCENTER : TXA_HRIGHT;
+				rep_DrawText(page, cx[j+1], cy, false, c, &txtdef1,TmpTxt);
+				}
+			}
+		mk_hr(page, cx[0], cx[6]+txtdef1.fSize*2.0, cy+txtdef1.fSize*0.2);
+		for(i= 0; i< nr; i++) {
+			if(vals[i]) free(vals[i]);	if(names[i]) free(names[i]);
+			}
+		free(vals);				free(nvals);		free(names);
+		free(x);				free(y);			free(by1);
+		free(by2);				free(wy1);			free(wy2);
+		free(rsums);
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rd) {
+		for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
+		free(rd);
+		}
+	if(ranks)free(ranks);	if(ridx)free(ridx);
+	if(rV1) delete rV1;		free(KruskalDlg);
+	return;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// simple sample statistics
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *SmplStatDlg_Tmpl = 
+	"1,2,,DEFAULT,PUSHBUTTON,-1,148,10,45,12\n"
+	"2,3,,,PUSHBUTTON,-2,148,25,45,12\n"
+	"3,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,10,100,ISPARENT | CHECKED,SHEET,1,5,10,130,80\n"
+	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,101,,,LTEXT,2,10,30,60,8\n"
+	"101,,,LASTOBJ,RANGEINPUT,-15,20,40,100,10";
+void rep_samplestats(GraphObj *parent, DataObj *data)
+{
+	TabSHEET tab1 = {0, 40, 10, "Input Data"};
+	DlgInfo *SmplStatDlg;
+	void *dyndata[] = {(void*)&tab1, (void*)"rectangular range for variables"};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, nr, nc, ntot, cb;
+	double val, *src_data, cx, cy, ksprob, ksd, sww, swp, mean, sd;
+	bool bContinue = false;
+	AccRange *rD =0L;
+	char *mrk, *x_info, *y_info;
+	RECT rec;
+	Plot *plot;
+	Graph *graph;
+	Page *page;
+
+	if(!parent || !data) return;
+	if(!(SmplStatDlg = CompileDialog(SmplStatDlg_Tmpl, dyndata))) return;
+	if(data->Command(CMD_GETMARK, &mrk, 0L))rlp_strcpy(TmpTxt, TMP_TXT_SIZE, mrk);
+	else {
+		data->ValueRec(&rec);
+		rlp_strcpy(TmpTxt, 100, mkRangeRef(rec.top, rec.left, rec.bottom, rec.right));
+		}
+	if(!(Dlg = new DlgRoot(SmplStatDlg, data)))return;
+	hDlg = CreateDlgWnd("Sample Statistics", 50, 50, 420, 220, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+			}
+		}while (res < 0);
+	if(res == 1 && Dlg->GetText(101, TmpTxt+200, TMP_TXT_SIZE) &&(rD = new AccRange(TmpTxt+200))
+		&& rD->BoundRec(&rec) && (ntot = rD->CountItems()) && (src_data = (double*)malloc(ntot*sizeof(double)))){
+		rep_init();
+		x_info = rD->RangeDesc(data, 2);
+		if(y_info = (char*)malloc(20)) rlp_strcpy(y_info, 20, "Normal quantiles");
+		page = new Page(parent, data);
+		cb = rlp_strcpy(TmpTxt, 100, "<b>Sample Statistics for \"");
+		if(x_info && x_info[0])	cb += rlp_strcpy(TmpTxt+cb, 100-cb, x_info);
+		else cb += rlp_strcpy(TmpTxt+cb, 100-cb, TmpTxt+200);
+		rlp_strcpy(TmpTxt+cb,100-cb, "\"</b>");		mk_header(page, TmpTxt, data);
+		for(ntot = 0, rD->GetFirst(&nc, &nr); rD->GetNext(&nc, &nr); ) {
+			if(data->GetValue(nr, nc, &val)) src_data[ntot++] = val;
+			}
+		if(ntot > 2 && (graph = new Graph(page, data, 0L, 0))) {
+			if(plot = new NormQuant(page, data, src_data, ntot)) {
+				plot->x_info = x_info;		plot->y_info = y_info;
+				}
+			if(!(graph->Command(CMD_DROP_PLOT, (void *)plot, 0L))) {
+				delete plot;	plot =0L;
+				}
+			graph->moveable = 0;					graph->GRect.Xmin += (txtdef1.fSize*5.0);	
+			graph->GRect.Xmax += (txtdef1.fSize*5.0);		graph->GRect.Ymin += (txtdef1.fSize*10.0);
+			graph->GRect.Ymax += (txtdef1.fSize*10.0);		page->Command(CMD_DROP_GRAPH, graph, 0L);
+			cy = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef1.fSize*3;
+			rep_DrawText(page, graph->GRect.Xmin, cy, false, TXA_HLEFT, &txtdef1, "<b>Descriptive Statistics:</b>");
+			cy += txtdef1.fSize*1.5;			cx = graph->GetSize(SIZE_GRECT_LEFT)+txtdef1.fSize*25.0;
+			mk_median_report(page, cx, cy, src_data, ntot, .95, 0L);
+			cx = graph->GetSize(SIZE_GRECT_LEFT)+txtdef1.fSize*2.0;
+			cy = mk_mean_report(page, cx, cy, src_data, ntot, .95, 0L);
+			//data are sorted by the mk_median_report();
+			d_variance(ntot, src_data, &mean, &sd);
+			sd = sqrt(sd/((double)(ntot-1)));
+			KolSmir(ntot, src_data, norm_dist, mean, sd, true, &ksd, &ksprob);
+			cy += txtdef1.fSize*1.5;			cx = graph->GRect.Xmin;
+			rep_DrawText(page, cx , cy, false, TXA_HLEFT, &txtdef1, "<b>Test for Normal Distribution:</b>");
+			cy += linsp1;						cx += (txtdef1.fSize*2.0);
+			cb = dbl_to_str1(TmpTxt, 100, "Kolmogorov-Smirnov D = %.4lf, P ", ksd);
+			dbl_to_str1(TmpTxt+cb, 100-cb, ksprob >= 0.0001 ? (char*)"= %.4lf" : (char*)"< 0.0001", ksprob);
+			rep_DrawText(page, cx , cy, false, TXA_HLEFT, &txtdef1,TmpTxt);			cy += linsp1/1.2;
+			swilk1(ntot, src_data, norm_dist, 0.0, 1.0, true, &sww, &swp);
+			if(sww >= 0.0 && swp >= 0.0 && (cb = dbl_to_str1(TmpTxt, 100, "Shapiro-Wilk W = %.4lf, P ", sww))
+				&& (dbl_to_str1(TmpTxt+cb, 100-cb, swp >= 0.0001 ? (char*)"= %.4lf" : (char*)"< 0.0001", swp))){
+				rep_DrawText(page, cx , cy, false, TXA_HLEFT, &txtdef1,TmpTxt);		cy += linsp1/1.2;
+				}
+			parent->Command(CMD_DROP_GRAPH, page, 0L);
+			}
+		else {
+			ErrorBox("Insuficient data to do stats\n");
+			delete page;
+			}
+		delete rD;	free(src_data);
+		}
+	CloseDlgWnd(hDlg);	delete Dlg;		free(SmplStatDlg);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// linear regression analysis
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static double mk_regr_summary(GraphObj *parent, double x, double y, double *dres, double ci, int n)
+{
+	char *fmts[] = {"slope = %g", "intercept = %g", "observations = %g", "r<sup> 2</sup> = %g", "r = %g"};
+	char *ci_fmt = "%g  -  %g";
+	char lbl[80];
+	double z, s;
+	double x1 = x + txtdef1.fSize*3.0;
+	double x2 = x + txtdef1.fSize*25.0;
+#ifdef _WINDOWS
+	double hrw = txtdef1.fSize*38.0;
+#else
+	double hrw = txtdef1.fSize*1.3*38.0;
+#endif
+
+	rep_DrawText(parent, x, y, false, TXA_HLEFT, &txtdef1, "<b>Regression:</b>");
+	dbl_to_str1(lbl, 80, "%g%% C.I.", ci);
+	rep_DrawText(parent, x2, y, false, TXA_HCENTER, &txtdef1, lbl);
+	mk_hr(parent, x, x+hrw, y+txtdef1.fSize*1.2);
+	y += linsp1*1.5;		dbl_to_str1(lbl, 80, fmts[0], dres[0]);
+	rep_DrawText(parent, x1, y, false, TXA_HLEFT, &txtdef1, lbl);
+	dbl_to_str2(lbl, 80, ci_fmt, dres[0]-dres[10], dres[0]+dres[10]);
+	rep_DrawText(parent, x2, y, false, TXA_HCENTER, &txtdef1, lbl);
+	y += linsp1;		dbl_to_str1(lbl, 80, fmts[1], dres[1]);
+	rep_DrawText(parent, x1, y, false, TXA_HLEFT, &txtdef1, lbl);
+	dbl_to_str2(lbl, 80, ci_fmt, dres[1]-dres[11], dres[1]+dres[11]);
+	rep_DrawText(parent, x2, y, false, TXA_HCENTER, &txtdef1, lbl);
+	y += linsp1;		dbl_to_str1(lbl, 80, fmts[2], (double)n);
+	rep_DrawText(parent, x1, y, false, TXA_HLEFT, &txtdef1, lbl);
+	y += linsp1;		dbl_to_str1(lbl, 80, fmts[3], dres[12]);
+	rep_DrawText(parent, x1, y, false, TXA_HLEFT, &txtdef1, lbl);
+	y += linsp1;		dbl_to_str1(lbl, 80, fmts[4], sqrt(dres[12]));
+	rep_DrawText(parent, x1, y, false, TXA_HLEFT, &txtdef1, lbl);
+	z = 0.5 * log((1.0+sqrt(dres[12])+_PREC)/(1.0-sqrt(dres[12])+_PREC));	//Fishers z-transform
+	s = distinv(t_dist, 1.0E+10, 1.0, (100-ci)/100.0, 2.0)/sqrt((double)(n-3));	
+	dbl_to_str2(lbl, 80, ci_fmt, tanh(z-s), tanh(z+s));
+	rep_DrawText(parent, x2, y, false, TXA_HCENTER, &txtdef1, lbl);
+	mk_hr(parent, x, x+hrw, y+txtdef1.fSize*1.2);
+	return y + linsp1*3.0;
+}
+
+static char *RegrDlg_Tmpl =
+	"1,+,,DEFAULT,PUSHBUTTON,-1,148,10,45,12\n"
+	".,.,,,PUSHBUTTON,-2,148,25,45,12\n"
+	".,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,10,100,ISPARENT | CHECKED,SHEET,1,5,10,130,100\n"
+	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,+,,,LTEXT,2,10,30,60,8\n"
+	".,.,,,RANGEINPUT,-15,20,40,100,10\n"
+	".,.,,,LTEXT,3,10,55,60,8\n"
+	".,.,,,RANGEINPUT,-16,20,65,100,10\n"
+	".,.,,,LTEXT,4,10,80,60,8\n"
+	".,.,,,EDVAL1,5,74,80,25,10\n"
+	".,.,,,LTEXT,-10,101,80,60,8\n"
+	".,,,LASTOBJ,CHECKBOX,6,10,95,100,8";
+
+void
+rep_regression(GraphObj *parent, DataObj *data)
+{
+	TabSHEET tab1 = {0, 60, 10, "Regression Input"};
+	double ci = 95.0;
+	DlgInfo *RegrDlg;
+	void *dyndata[] = {(void*)&tab1, (void*)"range for independent variable (x)",
+		(void*)"range for dependent variable (y)", (void*)"confidence interval:",
+		(void*)&ci, (void*)" include origin"};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, n, n1, rx, cx, ry, cy, res, align = 0;
+	int x_dtype, y_dtype, nVals, nTxt, nTime;
+	bool bContinue = false, bValid, bParZ;
+	AccRange *rX = 0L, *rY = 0L;
+	double *x = 0L, *y = 0L, **res_tab = 0L, c_x, c_y;
+	double sx, sy, dx, dy, sxy, sxx, syy, sdy, df, t, ts, ty;
+	double dres[14], ly[4];
+	char *txt_obj, *x_desc=0L, *y_desc=0L;
+	anyResult xRes, yRes;
+	TextValue *x_tv, *y_tv;
+	Graph *graph;
+	Page *page;
+
+	if(!parent || !data) return;
+	if(!(RegrDlg = CompileDialog(RegrDlg_Tmpl, dyndata))) return;
+	UseRangeMark(data, 1, TmpTxt, TmpTxt+100);
+	if(!(Dlg = new DlgRoot(RegrDlg, data)))return;
+	hDlg = CreateDlgWnd("Linear Regression", 50, 50, 420, 260, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 1:
+			Dlg->GetValue(105, &ci);			bParZ = Dlg->GetCheck(107);
+			if(rX) delete rX;					if(rY) delete rY;
+			rX = rY = 0L;
+			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) rX = new AccRange(TmpTxt);
+			n = rX ? rX->CountItems() : 0;
+			if(!n) {
+				ErrorBox("Range not specified\nor not valid.");
+				bContinue = true;
+				res = -1;
+				}
+			if(n && Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE) && (rY = new AccRange(TmpTxt))){
+				if(n != rY->CountItems()) {
+					ErrorBox("Both ranges must be given\nand must have the same size");
+					bContinue = true;
+					res = -1;
+					}
+				}
+			}
+		}while (res < 0);
+	n1 = n;
+	if(res == 1 && n >1 && rX && rY && (x = (double*)malloc(n*sizeof(double))) 
+		&& (y = (double*)malloc(n*sizeof(double)))
+		&& (res_tab = (double**)calloc(3, sizeof(double*)))
+		&& (res_tab[0] = (double*) malloc(5*sizeof(double)))
+		&& (res_tab[1] = (double*) malloc(5*sizeof(double)))
+		&& (res_tab[2] = (double*) malloc(5*sizeof(double)))) {
+		x_desc = rX->RangeDesc(data, 0);	y_desc = rY->RangeDesc(data, 0);
+		//analyse data types
+		x_dtype = y_dtype = 0;				x_tv = y_tv = 0L;
+		if(rX->DataTypes(data, &nVals, &nTxt, &nTime)){
+			if(!nVals && nTxt > 1 && nTxt > nTime) x_tv = new TextValue();
+			else if(!nVals && nTime > 1 && nTime > nTxt) x_dtype = ET_DATETIME;
+			}
+		if(rY->DataTypes(data, &nVals, &nTxt, &nTime)){
+			if(!nVals && nTxt > 1 && nTxt > nTime) y_tv = new TextValue();
+			else if(!nVals && nTime > 1 && nTime > nTxt) y_dtype = ET_DATETIME;
+			}
+		rX->GetFirst(&cx, &rx);				rY->GetFirst(&cy, &ry);
+		rep_init();
+		dBounds.Xmin = dBounds.Ymin = HUGE_VAL;		dBounds.Xmax = dBounds.Ymax = -HUGE_VAL;
+		//read data into x[] and y[]
+		for(i = n = 0; i < n1 && rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry); i++) {
+			bValid = false;
+			if(data->GetResult(&xRes, rx, cx, false) && data->GetResult(&yRes, ry, cy, false)) {
+				bValid = true;
+				if(x_tv) {
+					if(xRes.type == ET_TEXT) dx = x_tv->GetValue(xRes.text);
+					else bValid = false;
+					}
+				else if(x_dtype == ET_DATETIME) {
+					if(xRes.type == ET_DATE || xRes.type == ET_TIME  || xRes.type == ET_DATETIME) dx = xRes.value;
+					else bValid = false;
+					}
+				else {
+					if(xRes.type == ET_VALUE) dx = xRes.value;
+					else bValid = false;
+					}
+				if(y_tv) {
+					if(yRes.type == ET_TEXT) dy = y_tv->GetValue(yRes.text);
+					else bValid = false;
+					}
+				else if(y_dtype == ET_DATETIME) {
+					if(yRes.type == ET_DATE || yRes.type == ET_TIME  || yRes.type == ET_DATETIME) dy = yRes.value;
+					else bValid = false;
+					}
+				else {
+					if(yRes.type == ET_VALUE) dy = yRes.value;
+					else bValid = false;
+					}
+				}
+			if(bValid){
+				x[n] = dx;				y[n] = dy;
+				if(dBounds.Xmin > x[n]) dBounds.Xmin = x[n];
+				if(dBounds.Xmax < x[n]) dBounds.Xmax = x[n];
+				if(dBounds.Ymin > y[n]) dBounds.Ymin = y[n];
+				if(dBounds.Ymax < y[n]) dBounds.Ymax = y[n];
+				n++;
+				}
+			}
+		if(!bParZ) {
+			for(i = 0, 	sx = sy = 0.0; i < n; i++) {
+				sx += x[i];			sy += y[i];
+				}
+			dres[2] = sx /n;		dres[3] = sy/n;
+			}
+		else {
+			dres[2] = sx = dres[3] = sy = 0.0;
+			}
+		sxy = sxx = syy = 0.0;
+		for(i = 0; i < n; i++) {
+			dx = x[i]-dres[2];	dy = y[i]-dres[3];
+			sxx += (dx*dx);		syy += (dy*dy);		sxy += (dx*dy);
+			}
+		dres[0] = sxy / sxx;	dres[1] = dres[3] - dres[0] * dres[2];
+		for(i = 0, sdy = 0.0; i < n; i++) {
+			dy = y[i] - (dres[1] + x[i] *dres[0]);
+			sdy += (dy * dy);
+			}
+		df = bParZ ? (n-1) : (n-2);		sdy = sdy/df;					dres[4] = sqrt(sdy/sxx);
+		dres[5] = sxx/(n-1);			dres[6] = syy/(n-1);			dres[7] = sdy;
+		dres[8] = sxy/sdy*sxy/sxx;		dres[9] = f_dist(dres[8], 1.0, df);
+		t = distinv(t_dist, df, 1.0, (100.0-ci)/100.0, 2.0); 
+		dres[10] = t * sqrt(dres[7]/sxx);
+		dres[11] = t * sqrt(dres[7]*(dres[2]*dres[2]/sxx +1.0/(double)n));
+		if (n && (graph = new Graph(parent, data, 0L, 0))) {
+			if(txt_obj = mk_scatt(0, x, y, 0L, 0L, n, "Data", x_desc, y_desc)){
+				OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
+				free(txt_obj);
+				if(LastOpenGO && LastOpenGO->Id >= GO_PLOT && LastOpenGO ->Id < GO_GRAPH) {
+					((Plot*)LastOpenGO)->x_dtype = x_dtype;	((Plot*)LastOpenGO)->y_dtype = y_dtype;
+					((Plot*)LastOpenGO)->x_tv = x_tv;		((Plot*)LastOpenGO)->y_tv = y_tv;
+					}
+				}
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
+				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE));
+			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "f_xy=\"%g+x*%g\\n\"\n", dres[1], dres[0]);
+			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "Desc=\"Regression\"\n");
+			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
+				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE)*0.5);
+			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "f_xy=\"y=%g+x*%g;\\nts=sqrt((((x-(%g))^2)/%g+%g)*%g);\\ny=y+ts*%g\\n\"\n", 
+				dres[1], dres[0], dres[2], sxx, (1.0/(double)n), dres[7], t);
+			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "Desc=\"%g%% C.I.\"\n", ci);
+			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
+				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE)*0.5);
+			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "f_xy=\"y=%g+x*%g;\\nts=sqrt((((x-(%g))^2)/%g+%g)*%g);\\ny=y-ts*%g\\n\"\n", 
+				dres[1], dres[0], dres[2], sxx, (1.0/(double)n), dres[7], t);
+			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "Desc=\"%g%% C.I.\"\n", ci);
+			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
+#else
+			i = sprintf(TmpTxt, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
+				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE));
+			i += sprintf(TmpTxt+i, "f_xy=\"%g+x*%g\\n\"\n", dres[1], dres[0]);
+			i += sprintf(TmpTxt+i, "Desc=\"Regression\"\n");
+			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
+			i = sprintf(TmpTxt, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
+				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE)*0.5);
+			i += sprintf(TmpTxt+i, "f_xy=\"y=%g+x*%g;\\nts=sqrt((((x-(%g))^2)/%g+%g)*%g);\\ny=y+ts*%g\\n\"\n", 
+				dres[1], dres[0], dres[2], sxx, (1.0/(double)n), dres[7], t);
+			i += sprintf(TmpTxt+i, "Desc=\"%g%% C.I.\"\n", ci);
+			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
+			i = sprintf(TmpTxt, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
+				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE)*0.5);
+			i += sprintf(TmpTxt+i, "f_xy=\"y=%g+x*%g;\\nts=sqrt((((x-(%g))^2)/%g+%g)*%g);\\ny=y-ts*%g\\n\"\n", 
+				dres[1], dres[0], dres[2], sxx, (1.0/(double)n), dres[7], t);
+			i += sprintf(TmpTxt+i, "Desc=\"%g%% C.I.\"\n", ci);
+			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
+#endif
+			ts = t * sqrt(dres[7]*((dBounds.Xmax-dres[2])*(dBounds.Xmax-dres[2])/sxx +1.0/(double)n));
+			ty = dBounds.Xmax * dres[0] +dres[1];
+			ly[0] = ty +ts;		ly[1] = ty -ts;
+			ts = t * sqrt(dres[7]*((dBounds.Xmin-dres[2])*(dBounds.Xmin-dres[2])/sxx +1.0/(double)n));
+			ty = dBounds.Xmin * dres[0] +dres[1];
+			ly[2] = ty +ts;		ly[3] = ty -ts;
+			for(i = 0; i < 4; i++) if(ly[i] > -HUGE_VAL && ly[i] < HUGE_VAL) {
+				if(ly[i] < dBounds.Ymin) dBounds.Ymin = ly[i];
+				if(ly[i] > dBounds.Ymax) dBounds.Ymax = ly[i];
+				}
+#ifdef USE_WIN_SECURE
+			if(!bParZ) sprintf_s(TmpTxt, TMP_TXT_SIZE, "y = %g %c %g * x",dres[1],(dres[0] < 0.0 ? '-' : '+'), fabs(dres[0]));
+			else sprintf_s(TmpTxt, TMP_TXT_SIZE, "y = %g * x", fabs(dres[0]));
+#else
+			if(!bParZ) sprintf(TmpTxt, "y = %g %c %g * x",dres[1],(dres[0] < 0.0 ? '-' : '+'), fabs(dres[0]));
+			else sprintf(TmpTxt, "y = %g * x", fabs(dres[0]));
+#endif
+			rep_DrawText(graph, (graph->GetSize(SIZE_DRECT_LEFT) + graph->GetSize(SIZE_DRECT_RIGHT))/2.0,
+				graph->GetSize(SIZE_DRECT_TOP)+txtdef1.fSize/2.0, true, TXA_HCENTER, &txtdef1, TmpTxt);
+			page = new Page(parent, data);					page->Command(CMD_DROP_GRAPH, graph, 0L);
+			graph->moveable = 0;
+			graph->GRect.Xmin += (txtdef1.fSize*5.0);		graph->GRect.Xmax += (txtdef1.fSize*5.0);
+			graph->GRect.Ymin += (txtdef1.fSize*10.0);		graph->GRect.Ymax += (txtdef1.fSize*10.0);
+			mk_header(page, "<b>Linear Regression Analysis</b>", data);
+			res_tab[0][0] = 1;						res_tab[1][0] = df;
+			res_tab[2][0] = df+1.0;					res_tab[0][1] = sxy*sxy/sxx;
+			res_tab[1][1] = syy-res_tab[0][1];		res_tab[2][1] = syy;
+			res_tab[0][2] = res_tab[0][1];			res_tab[1][2] = res_tab[1][1]/df;
+			res_tab[0][3] = dres[8];				res_tab[0][4] = dres[9];
+			dres[12] = res_tab[0][1]/res_tab[2][1];
+			c_y = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef2.fSize*3.0;
+			c_x = graph->GRect.Xmin;
+			c_y = mk_regr_summary(page, c_x, c_y, dres, ci, n);
+			rep_DrawText(page, c_x, c_y, false, TXA_HLEFT, &txtdef1, "<b>Anova:</b>");
+			c_y += txtdef1.fSize*1.5;			mk_table(page, c_x, c_y, 2, res_tab);
+			parent->Command(CMD_DROP_GRAPH, page, 0L);
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;				if(res_tab) {
+		for(i = 0; i < 3; i++) if(res_tab[i]) free(res_tab[i]);
+		free(res_tab);
+		}
+	if(x_desc) free(x_desc);	if(y_desc)free(y_desc);
+	if(x) free(x);				if(y) free(y);
+	if(rX) delete rX;			if(rY) delete rY;		free(RegrDlg);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Kendall's robust line-fit
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *RobLineDlg_Tmpl =
+	"1,+,,DEFAULT,PUSHBUTTON,-1,148,10,45,12\n"
+	".,.,,,PUSHBUTTON,-2,148,25,45,12\n"
+	".,,4,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,10,100,ISPARENT | CHECKED,SHEET,1,5,10,130,78\n"
+	"10,,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+	"100,+,,,LTEXT,2,10,30,60,8\n"
+	".,.,,,RANGEINPUT,-15,20,40,100,10\n"
+	".,.,,,LTEXT,3,10,55,60,8\n"
+	".,,,LASTOBJ,RANGEINPUT,-16,20,65,100,10";
+
+void
+rep_robustline(GraphObj *parent, DataObj *data)
+{
+	TabSHEET tab1 = {0, 90, 10, "Nonparametric Regression"};
+	DlgInfo *RegrDlg;
+	void *dyndata[] = {(void*)&tab1, (void*)"range for x-data", (void*)"range for y-data"};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, k, n, n1, rx, cx, ry, cy, res, align = 0;
+	int x_dtype, y_dtype, nVals, nTxt, nTime;
+	bool bContinue = false, bValid;
+	AccRange *rX = 0L, *rY = 0L;
+	double *x = 0L, *y = 0L, *a = 0L, *b = 0L, c_x, c_y;
+	double dx, dy, slope, intercept;
+	char *txt_obj, *x_desc=0L, *y_desc=0L;
+	scaleINFO scale = {{0.0, 0.45}, {0.0, 0.45}, {0.0, 0.45}};
+	anyResult xRes, yRes;
+	TextValue *x_tv, *y_tv;
+	Plot *plot;
+	Graph *graph;
+	Page *page;
+
+	if(!parent || !data) return;
+	if(!(RegrDlg = CompileDialog(RobLineDlg_Tmpl, dyndata))) return;
+	UseRangeMark(data, 1, TmpTxt, TmpTxt+100);
+	if(!(Dlg = new DlgRoot(RegrDlg, data)))return;
+	hDlg = CreateDlgWnd("Kendall's robust line-fit", 50, 50, 420, 220, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 1:
+			if(rX) delete rX;					if(rY) delete rY;
+			rX = rY = 0L;
+			if(Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE)) rX = new AccRange(TmpTxt);
+			n = rX ? rX->CountItems() : 0;
+			if(!n) {
+				ErrorBox("Range not specified\nor not valid.");
+				bContinue = true;
+				res = -1;
+				}
+			if(n && Dlg->GetText(103, TmpTxt, TMP_TXT_SIZE) && (rY = new AccRange(TmpTxt))){
+				if(n != rY->CountItems()) {
+					ErrorBox("Both ranges must be given\nand must have the same size");
+					bContinue = true;
+					res = -1;
+					}
+				}
+			}
+		}while (res < 0);
+	for(n1 = n, i = k = 1; i < n; i++) k += i;
+	if(res == 1 && n >1 && rX && rY && (x = (double*)malloc(n*sizeof(double))) 
+		&& (y = (double*)malloc(n*sizeof(double)))
+		&& (a = (double*)malloc(k*sizeof(double)))
+		&& (b = (double*)malloc(k*sizeof(double)))){
+		x_desc = rX->RangeDesc(data, 0);	y_desc = rY->RangeDesc(data, 0);
+		//analyse data types
+		x_dtype = y_dtype = 0;				x_tv = y_tv = 0L;
+		if(rX->DataTypes(data, &nVals, &nTxt, &nTime)){
+			if(!nVals && nTxt > 1 && nTxt > nTime) x_tv = new TextValue();
+			else if(!nVals && nTime > 1 && nTime > nTxt) x_dtype = ET_DATETIME;
+			}
+		if(rY->DataTypes(data, &nVals, &nTxt, &nTime)){
+			if(!nVals && nTxt > 1 && nTxt > nTime) y_tv = new TextValue();
+			else if(!nVals && nTime > 1 && nTime > nTxt) y_dtype = ET_DATETIME;
+			}
+		rX->GetFirst(&cx, &rx);				rY->GetFirst(&cy, &ry);
+		rep_init();
+		dBounds.Xmin = dBounds.Ymin = HUGE_VAL;		dBounds.Xmax = dBounds.Ymax = -HUGE_VAL;
+		//read data into x[] and y[]
+		for(i = n = 0; i < n1 && rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry); i++) {
+			bValid = false;
+			if(data->GetResult(&xRes, rx, cx, false) && data->GetResult(&yRes, ry, cy, false)) {
+				bValid = true;
+				if(x_tv) {
+					if(xRes.type == ET_TEXT) dx = x_tv->GetValue(xRes.text);
+					else bValid = false;
+					}
+				else if(x_dtype == ET_DATETIME) {
+					if(xRes.type == ET_DATE || xRes.type == ET_TIME  || xRes.type == ET_DATETIME) dx = xRes.value;
+					else bValid = false;
+					}
+				else {
+					if(xRes.type == ET_VALUE) dx = xRes.value;
+					else bValid = false;
+					}
+				if(y_tv) {
+					if(yRes.type == ET_TEXT) dy = y_tv->GetValue(yRes.text);
+					else bValid = false;
+					}
+				else if(y_dtype == ET_DATETIME) {
+					if(yRes.type == ET_DATE || yRes.type == ET_TIME  || yRes.type == ET_DATETIME) dy = yRes.value;
+					else bValid = false;
+					}
+				else {
+					if(yRes.type == ET_VALUE) dy = yRes.value;
+					else bValid = false;
+					}
+				}
+			if(bValid){
+				x[n] = dx;				y[n] = dy;
+				if(dBounds.Xmin > x[n]) dBounds.Xmin = x[n];
+				if(dBounds.Xmax < x[n]) dBounds.Xmax = x[n];
+				if(dBounds.Ymin > y[n]) dBounds.Ymin = y[n];
+				if(dBounds.Ymax < y[n]) dBounds.Ymax = y[n];
+				n++;
+				}
+			}
+		SortArray2(n, x, y);
+		for(i = k = 0; i < (n-1); i++) for(j = i+1; j < n; j++) {
+			if(x[i] != x[j]) {
+				b[k] = (y[j] - y[i])/(x[j] - x[i]);
+				a[k] = y[i] - b[k]*x[i];	k++;
+				}
+			}
+		d_quartile(k, b, 0L, &slope, 0L);		d_quartile(k, a, 0L, &intercept, 0L);
+		slope = slope;		intercept = intercept;
+		if (n && (graph = new Graph(parent, data, 0L, 0))) {
+			if(txt_obj = mk_scatt(0, x, y, 0L, 0L, n, "Data", x_desc, y_desc)){
+				OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
+				free(txt_obj);
+				if(LastOpenGO && LastOpenGO->Id >= GO_PLOT && LastOpenGO ->Id < GO_GRAPH) {
+					((Plot*)LastOpenGO)->x_dtype = x_dtype;	((Plot*)LastOpenGO)->y_dtype = y_dtype;
+					((Plot*)LastOpenGO)->x_tv = x_tv;		((Plot*)LastOpenGO)->y_tv = y_tv;
+					}
+				}
+#ifdef USE_WIN_SECURE
+			i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
+				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE));
+			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "f_xy=\"%g+x*%g\\n\"\n", intercept, slope);
+			i += sprintf_s(TmpTxt+i, TMP_TXT_SIZE-i, "Desc=\"Fitted Line\"\n");
+			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "y = %g %c %g * x",intercept,(slope < 0.0 ? '-' : '+'), fabs(slope));
+#else
+			i = sprintf(TmpTxt, "[1=Function]\nx1= %g\nx2= %g\nxstep= %g\nLine= %g 1 0x000000ff 0x0\n", 
+				dBounds.Xmin, dBounds.Xmax, (dBounds.Xmax -dBounds.Xmin)/100.0, defs.GetSize(SIZE_DATA_LINE));
+			i += sprintf(TmpTxt+i, "f_xy=\"%g+x*%g\\n\"\n", intercept, slope);
+			i += sprintf(TmpTxt+i, "Desc=\"Fitted Line\"\n");
+			OpenGraph(graph, 0L, (unsigned char*)TmpTxt, false);
+			sprintf(TmpTxt, "y = %g %c %g * x", intercept, (slope < 0.0 ? '-' : '+'), fabs(slope));
+#endif
+			rep_DrawText(graph, (graph->GetSize(SIZE_DRECT_LEFT) + graph->GetSize(SIZE_DRECT_RIGHT))/2.0,
+				graph->GetSize(SIZE_DRECT_TOP)+txtdef1.fSize/2.0, true, TXA_HCENTER, &txtdef1, TmpTxt);
+			page = new Page(parent, data);					page->Command(CMD_DROP_GRAPH, graph, 0L);
+			graph->moveable = 0;
+			graph->GRect.Xmin += (txtdef1.fSize*5.0);		graph->GRect.Xmax += (txtdef1.fSize*5.0);
+			graph->GRect.Ymin += (txtdef1.fSize*9.0);		graph->GRect.Ymax += (txtdef1.fSize*9.0);
+			mk_header(page, "<b>Kendall's Robust Line-Fit</b>", data);
+			c_y = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef2.fSize*2.0;
+			c_x = graph->GRect.Xmin;
+			j = (int)isqr(k);				i = (int)((double)k/10.0);
+			if(j < 8) j = 8;
+			else if (j >40) j = 40;
+			scale.sx.fx = (graph->GRect.Xmin + graph->GRect.Xmax)/2.0;
+			scale.sy.fx = c_y;
+			graph = new Graph(parent, data, 0L, 0);
+			if(plot = new FreqDist(graph, data, b+(i>>1), k-i, j)){
+				if(plot->x_info = (char*)malloc(30)) rlp_strcpy(plot->x_info, 30, "90% of all slopes");
+				if(plot->y_info = (char*)malloc(30)) rlp_strcpy(plot->y_info, 30, "No. of observations");
+				if(!(graph->Command(CMD_DROP_PLOT, plot, 0L))) delete(plot);
+				}
+			graph->Command(CMD_SCALE, &scale, 0L);
+			page->Command(CMD_DROP_GRAPH, graph, 0L);
+			mk_median_report(page, c_x, c_y, b, k, .95, "Slope");
+			scale.sy.fx = c_y = graph->GRect.Ymax + txtdef1.fSize;
+			graph = new Graph(parent, data, 0L, 0);
+			if(plot = new FreqDist(graph, data, a+(i>>1), k-i, j)){
+				if(plot->x_info = (char*)malloc(30)) rlp_strcpy(plot->x_info, 30, "90% of all intercepts");
+				if(plot->y_info = (char*)malloc(30)) rlp_strcpy(plot->y_info, 30, "No. of observations");
+				if(!(graph->Command(CMD_DROP_PLOT, plot, 0L))) delete(plot);
+				}
+			graph->Command(CMD_SCALE, &scale, 0L);
+			page->Command(CMD_DROP_GRAPH, graph, 0L);
+			c_y = mk_median_report(page, c_x, c_y, a, k, .95, "Intercept");
+			parent->Command(CMD_DROP_GRAPH, page, 0L);
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(x_desc) free(x_desc);	if(y_desc)free(y_desc);
+	if(x) free(x);				if(y) free(y);
+	if(a) free(a);				if(b) free(b);
+	if(rX) delete rX;			if(rY) delete rY;		free(RegrDlg);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Correlation reports
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *RepCorrel_DlgTmpl =
+		"1,2,,DEFAULT,PUSHBUTTON,-1,158,10,45,12\n"
+		"2,3,,,PUSHBUTTON,-2,158,25,45,12\n"
+		"3,,10,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+		"10,11,152,ISPARENT | CHECKED, SHEET,1,5,10,140,70\n"
+		"11,20,200,ISPARENT | TOUCHEXIT,SHEET,2,5,10,140,70\n"
+		"20,21,,CHECKED,CHECKPIN,0,5,0,12,8\n"
+		"21,22,,CHECKED,CHECKBOX,7,25,85,130,9\n"
+		"22,23,,,LTEXT,8,35,95,50,9\n"
+		"23,24,,,EDVAL1,9,87,95,30,10\n"
+		"24,,,,LTEXT,-10,120,95,10,9\n"
+		"152,153,,ISPARENT | CHECKED,GROUPBOX,3,12,30,128,45\n"
+		"153,154,,,LTEXT,0,25,35,60,8\n"
+		"154,155,,,RANGEINPUT,0,25,45,100,10\n"
+		"155,156,0,,PUSHBUTTON,-8,95,57,30,12\n"
+		"156,,,,PUSHBUTTON,-9,60,57,35,12\n"
+		"200,201,,TOUCHEXIT,RADIO1,4,25,30,60,8\n"
+		"201,202,,TOUCHEXIT,RADIO1,5,25,45,60,8\n"
+		"202,,,TOUCHEXIT,RADIO1,6,25,60,60,8\n"
+		"300,,,LASTOBJ,NONE,0,0,0,0,0";
+static int use_corr = 2;
+static double use_ci = 95.0;
+
+void rep_correl(GraphObj *parent, DataObj *data, int style)
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 57, 10, "Method"};
+	
+	void *dyndata[] = {(void*)&tab1, (void*)&tab2, 
+		(void*)" select one range for every variable ", (void*)" Pearsons product moment",
+		(void*)" Spearmans rank correlation", (void*)" Kendalls Tau", (void*)" highlight significant correlations,",
+		(void*)"sigificance level", (void*)&use_ci};
+	DlgInfo *CorrelDlg;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, res, nr, currYR = 0, maxYR = 0, ny, corr;
+	int r1, c1, r2, c2, n, cb;
+	bool updateYR = true, bContinue = false, bMtab = false, bHiLite;
+	double lmarg, line_inc, *v1, *v2, val1, val2, cx, cy, r,dn, p, sf, ra[20], cl;
+	char **rd = 0L, *txt_obj, *info1, *info2;
+	scaleINFO scale = {{0.0, 0.14}, {0.0, 0.14}, {0.0, 0.14}};
+	AccRange *rV1 = 0L, *rV2 = 0L;
+	TextDEF mtext;
+	Plot *plot;
+	Graph *graph;
+	Page *page;
+
+	if(!parent || !data) return;
+	info1 = info2 = 0L;
+	if(!UseRangeMark(data, 2, TmpTxt, TmpTxt+100, TmpTxt+200, TmpTxt+300, TmpTxt+400,
+		TmpTxt+500, TmpTxt+600, TmpTxt+700, TmpTxt+800, TmpTxt+900, TmpTxt+1000)) return;
+	if(!(CorrelDlg = CompileDialog(RepCorrel_DlgTmpl, dyndata))) return;
+	if(TmpTxt[0] && TmpTxt[100] && (rd = (char**)calloc(12, sizeof(char*)))) {
+		for(i=j=0; i <= 1000; i +=100) if(TmpTxt[i]) 
+			rd[j++] = (char*)memdup(TmpTxt+i, ((int)strlen(TmpTxt+i))+2, 0);	 maxYR = j-1;
+		}
+	if(!rd && !(rd = (char**)calloc(1, sizeof(char*))))return;
+	if(!(Dlg = new DlgRoot(CorrelDlg, data))) return;
+	if(rd && rd[currYR] &&  *(rd[currYR])) Dlg->SetText(154, rd[currYR]);
+	switch(use_corr) {
+	case 1:			Dlg->SetCheck(200, 0L, true);			corr = 1;			break;
+	default:		Dlg->SetCheck(201, 0L, true);			corr = 2;			break;
+	case 3:			Dlg->SetCheck(202, 0L, true);			corr = 3;			break;
+		}
+	hDlg = CreateDlgWnd(style? (char*)"Create Tiled Correlation Plots" : 
+		(char*)"Create a Correlation Matrix", 50, 50, 420, 252, Dlg, 0x4L);
+	do {
+		if(updateYR) {
+			if(currYR >0) Dlg->ShowItem(156, true);
+			else Dlg->ShowItem(156, false);
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "variable # %d/%d", currYR+1, maxYR+1);
+#else
+			sprintf(TmpTxt,"variable # %d/%d", currYR+1, maxYR+1);
+#endif
+			Dlg->SetText(153, TmpTxt);
+			updateYR = false;
+			}
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 0:
+			if(bContinue || Dlg->GetCheck(20)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 11:					//select correlation method
+			bMtab = true;	res =-1;	break;
+		case 200:					//select pearson
+		case 201:					//select spearman
+		case 202:					//select kendall
+			corr = res-199;		res =-1;	break;
+		case 1:
+			if(!bMtab) {
+				Dlg->SetCheck(11, 0L, true);
+				bMtab = true;	res = -1;	
+				break;
+				}
+		case 155:		case 156:
+			res = com_StackDlg(res, Dlg, 0L, 0L, &rd, &currYR,
+				&rV1, &bContinue, &ny, &maxYR, &updateYR);
+			break;
+			}
+		}while (res < 0);
+
+	if(res ==1) {
+		if(bHiLite = Dlg->GetCheck(21)) {
+			Dlg->GetValue(23, &use_ci);		cl = 1.0 - use_ci/100.0;
+			}
+		maxYR++;	rep_init();		page = new Page(parent, data);
+		if(rV1) delete rV1;			rV1 = 0L;		use_corr = corr;
+		switch(corr) {
+		case 1:
+			mk_header(page, "<b>Pearsons product moment correlations</b>", data);
+			break;
+		case 2:
+			mk_header(page, "<b>Spearmans rank correlations</b>", data);
+			break;
+		case 3:
+			mk_header(page, "<b>Kendalls non parametric correlations</b>", data);
+			break;
+		default:
+			mk_header(page, "<b>### Correlation Error ###</b>", data);
+			break;
+			}
+		memcpy(&mtext, &txtdef1, sizeof(TextDEF));
+		if(style == 0) {
+			lmarg = txtdef1.fSize*12.0;
+			cy = txtdef1.fSize*13.0;	cx = txtdef1.fSize*6.0;
+			line_inc = linsp1;
+			sf = (page->GetSize(SIZE_GRECT_RIGHT)-lmarg-linsp1)/(cx *(double)maxYR);
+			if(sf< 1.0) {
+				cx *= sf;		line_inc *= sf;		lmarg *= sf;
+				mtext.fSize *= sf;		mtext.iSize = 0;
+				}
+			}
+		else {
+			lmarg = txtdef1.fSize*8.0;
+			cy = txtdef1.fSize*13.0;
+			cx = (page->GetSize(SIZE_GRECT_RIGHT)-txtdef1.fSize*3.0-lmarg)/maxYR;
+			switch(defs.cUnits) {
+			default:
+				scale.sx.fy = scale.sy.fy = scale.sz.fy = cx/165.0;			break;
+			case 1:
+				scale.sx.fy = scale.sy.fy = scale.sz.fy = cx/16.50;			break;
+			case 2:
+				scale.sx.fy = scale.sy.fy = scale.sz.fy = cx/6.49606;		break;
+				}
+			line_inc = cx/1.44;
+			}
+		for(nr = maxYR, i = 0; i < nr; i++) for(j = 0; j < nr; j++) {
+			if(i == 0 &&(rV1 = new AccRange(rd[j]))) {	//first row
+				if(info1 = rV1->RangeDesc(data, style == 0 || nr > 5 ? 1 : 2)) {
+					if(style == 0)
+						rep_DrawText(page, lmarg+cx*j, cy-txtdef1.fSize*2.0, false, TXA_HCENTER, &mtext, info1);
+					else if(style == 1)
+						rep_DrawText(page, lmarg+cx*j+cx/2.0, cy-txtdef1.fSize*2.0, false, TXA_HCENTER, &mtext, info1);
+					free(info1);	info1 = 0L;
+					}
+				delete rV1;			rV1 = 0L;
+				}
+			if(j == 0 &&(rV1 = new AccRange(rd[i]))) {	//first column
+				if(info1 = rV1->RangeDesc(data, 1)) {
+					if(style == 0) 
+						rep_DrawText(page, lmarg-cx/2.0-txtdef1.fSize, cy+line_inc, false, TXA_HRIGHT, &mtext, info1);
+					else if(style == 1)
+						rep_DrawText(page, lmarg-txtdef1.fSize, cy+line_inc/2.0-mtext.fSize/2.0, false, TXA_HRIGHT, &mtext, info1);
+					free(info1);	info1 = 0L;
+					}
+				delete rV1;			rV1 = 0L;
+				}
+			if(i == j) {					//self correlation: do something else ...
+				if(style == 0) {			//correlation matrix
+					rep_DrawText(page, lmarg+cx*j, cy+line_inc, false, TXA_HCENTER, &mtext, "---");
+					}
+				else if(style = 1) {		//tiled plots
+					graph = new Graph(parent, data, 0L, 0);
+					scale.sx.fx = lmarg+cx*j;			scale.sy.fx = cy;
+					if(plot = new FreqDist(graph, data, rd[i], true)){
+						if(rV1 = new AccRange(rd[i])){
+							plot->x_info = rV1->RangeDesc(data, 2);
+							delete rV1;		rV1 = 0L;
+							}
+						if(!(graph->Command(CMD_DROP_PLOT, plot, 0L))) delete(plot);
+						}
+					graph->Command(CMD_SCALE, &scale, 0L);
+					page->Command(CMD_DROP_GRAPH, graph, 0L);
+					}
+				}
+			else {
+				rV1 = new AccRange(rd[i]);	rV2 = new AccRange(rd[j]);
+				v1 = (double*)malloc((rV1->CountItems()+1) * sizeof(double));
+				v2 = (double*)malloc((rV2->CountItems()+1) * sizeof(double));
+				dBounds.Xmin = dBounds.Ymin = HUGE_VAL;		dBounds.Xmax = dBounds.Ymax = -HUGE_VAL;
+				rV1->GetFirst(&c1, &r1);	rV2->GetFirst(&c2, &r2);
+				//copy values into arrays
+				for(n = 0; rV1->GetNext(&c1, &r1) && rV2->GetNext(&c2, &r2); ) {
+					if(data->GetValue(r1, c1, &val1) && data->GetValue(r2, c2, &val2)) {
+						if(dBounds.Xmin > val1) dBounds.Xmin = val1;
+						if(dBounds.Xmax < val1) dBounds.Xmax = val1;
+						if(dBounds.Ymin > val2) dBounds.Ymin = val2;
+						if(dBounds.Ymax < val2) dBounds.Ymax = val2;
+						v1[n] = val1;	v2[n] = val2;	n++;
+						}
+					}
+				//do correlation
+				dn = n;			r = 0.0;
+				if(n) switch(corr) {
+				case 1:
+					d_pearson(v1, v2, n, 0L, 0L, ra);
+					r = ra[0];	p = ra[2];	dn = ra[3];
+					break;
+				case 2:
+					d_spearman(v1, v2, n, 0L, 0L, ra);
+					r = ra[3];	p = ra[4];	dn = ra[5];
+					break;
+				case 3:
+					d_kendall(v1, v2, n, 0L, 0L, ra);
+					r = ra[0];	p = ra[2];	dn = ra[3];
+					break;
+				default:
+					r = 0.0;	dn = 0.0;	p = 1.0;			break;
+					}
+				//process result
+				if(dn > 1.0 && style == 0) {			//correlation matrix
+					if(bHiLite && p < cl && (txt_obj = mk_rect(lmarg+cx*j-cx/2.1, cy-line_inc/4.0, lmarg+cx*j+cx/2.1, 
+						cy+line_inc*3.25, 0x0000ffffL, 0x0080ffffL))) {
+						OpenGraph(page, 0L, (unsigned char*)txt_obj, false);
+						free(txt_obj);
+						}
+					dbl_to_str1(TmpTxt, 80, "%g", r);
+					rep_DrawText(page, lmarg+cx*j, cy, false, TXA_HCENTER, &mtext, TmpTxt);
+					dbl_to_str1(TmpTxt, 80, "n = %g", dn);
+					rep_DrawText(page, lmarg+cx*j, cy+line_inc, false, TXA_HCENTER, &mtext, TmpTxt);
+					dbl_to_str1(TmpTxt, 80, p < 0.001 ? (char*)"P < 0.0001" : (char*)"P = %.4lf", p);
+					rep_DrawText(page, lmarg+cx*j, cy+line_inc*2.0, false, TXA_HCENTER, &mtext, TmpTxt);
+					if(j == (nr-1)) cy += (line_inc*4.0);
+					}
+				else if(style == 0) {					//corr. matrix but no data
+					if(j == (nr-1)) cy += (line_inc*4.0);
+					}
+				else if(style == 1) {		//tiled plots
+					graph = new Graph(parent, data, 0L, 0);
+					scale.sx.fx = lmarg+cx*j;			scale.sy.fx = cy;
+					info1 = rV1->RangeDesc(data, 2);	info2 = rV2->RangeDesc(data, 2);
+					if(txt_obj = mk_scatt(0, v1, v2, 0L, 0L, n, "Data", info1, info2)){
+						OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
+						free(txt_obj);
+						}
+					if(info1) free(info1);				if(info2) free(info2);
+					info1 = info2 = 0L;
+					if(bHiLite && p < cl) {
+						graph->SetColor(COL_GRECT, 0x0000ffffL);	graph->SetColor(COL_DRECT, 0x00c0ffffL);
+						}
+					switch(corr) {
+					case 1:
+						cb = dbl_to_str2(TmpTxt, 80, "r = %.4lf, n = %g, ", r, dn);				break;
+					case 2:
+						cb = dbl_to_str2(TmpTxt, 80, "r<sub>S</sub> = %.4lf, n = %g, ", r, dn);	break;
+					case 3:
+						cb = dbl_to_str2(TmpTxt, 80, "r<sub>K</sub> = %.4lf, n = %g, ", r, dn);	break;
+					default:
+						TmpTxt[0] = 0;		cb = 0;		break;
+						}
+					dbl_to_str1(TmpTxt+cb, 80, p < 0.001 ? (char*)"P < 0.0001" : (char*)"P = %.4lf", p);
+					rep_DrawText(graph, graph->GetSize(SIZE_DRECT_LEFT)+txtdef1.fSize, 
+						graph->GetSize(SIZE_DRECT_TOP)+txtdef1.fSize, false, TXA_HLEFT, &txtdef1, TmpTxt);
+					if(LastOpenGO)LastOpenGO->SetColor(COL_TEXT, 0x00cb0000L);
+					graph->Command(CMD_SCALE, &scale, 0L);
+					if(dn > 1.0) page->Command(CMD_DROP_GRAPH, graph, 0L);
+					if(j == (nr-1)) cy += line_inc;
+					}
+				free(v1);		free(v2);
+				if(rV1)delete rV1;		if(rV2)delete rV2;		rV1 = rV2 = 0L;
+				}
+			}
+		parent->Command(CMD_DROP_GRAPH, page, 0L);
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rd) {
+		for (i = 0; i < maxYR; i++)	if(rd[i]) free(rd[i]);
+		free(rd);
+		}
+	if(rV2) delete rV2;		if(rV1) delete rV1;		free(CorrelDlg);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 2x2 table
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char *twDlg_Tmpl =
+	"1,2,100,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"2,3,400,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"3,4,600,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
+	"4,5,,DEFAULT,PUSHBUTTON,-1,168,10,45,12\n"
+	"5,,,,PUSHBUTTON,2,168,25,45,12\n"
+	"100,101,,,CTEXT,1,35,10,40,8\n"
+	"101,102,,,EDTEXT,0,35,20,40,10\n"
+	"102,103,,,EDTEXT,0,77,20,40,10\n"
+	"103,104,,,EDTEXT,0,35,32,40,10\n"
+	"104,105,,,EDTEXT,0,77,32,40,10\n"
+	"105,106,,,LTEXT,3,10,20,40,8\n"
+	"106,107,,,LTEXT,4,10,32,40,8\n"
+	"107,,,,CTEXT,5,77,10,40,8\n"
+	"400,401,,,EDTEXT,0,119,20,40,10\n"
+	"401,402,,,EDTEXT,0,119,32,40,10\n"
+	"402,403,,,EDTEXT,0,35,44,40,10\n"
+	"403,404,,,EDTEXT,0,77,44,40,10\n"
+	"404,405,,,EDTEXT,0,119,44,40,10\n"
+	"405,406,,,CTEXT,6,119,10,40,8\n"
+	"406,407,,,LTEXT,7,10,44,40,8\n"
+	"407,408,,,LTEXT,2,35,59,40,8\n"
+	"408,409,,,LTEXT,2,35,69,40,8\n"
+	"409,410,,,LTEXT,8,119,59,60,8\n"
+	"410,,,,LTEXT,0,119,69,60,8\n"
+	"600,601,,DEFAULT,PUSHBUTTON,-1,128,10,45,12\n"
+	"601,,,LASTOBJ,PUSHBUTTON,-2,128,25,45,12";
+
+void rep_twowaytable(GraphObj *parent, DataObj *data)
+{
+	DlgInfo *twDlg;
+	void *dyndata[] = {(void*)"Group A", (void*)"Close", (void*)"Case 1", (void*)"Case 2",
+		(void*)"Group B", (void*)"A + B", (void*)"C1+C2", (void*)"Fisher's exact:"};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, r, c, level, res, wcc;
+	int v_idx[] = {101,102,400,103,104,401,402,403,404};
+	double tmp, v[9], chi2, p, dn, pf, pfa[128];
+	char *rng;
+	AccRange *ar;
+
+	if(!parent || !data) return;
+	if(!(twDlg = CompileDialog(twDlg_Tmpl, dyndata))) return;
+	if(!(Dlg = new DlgRoot(twDlg, data)))return;
+	for(i = 400; i < 405; i++) Dlg->Activate(i, false);
+	if(data->Command(CMD_GETMARK, &rng, 0L) && rng && rng[0] && (ar = new AccRange(rng)) && ar->GetFirst(&c, &r)) {
+		for(i = 0; i < 4 && ar->GetNext(&c, &r); ) {
+			if(data->GetValue(r, c, &tmp)) {
+				Dlg->SetValue(101+i, tmp);				i++;
+				}
+			}
+		delete ar;
+		if(i == 4) Dlg->ItemCmd(600, CMD_ENDDIALOG, 0L);
+		}
+	level = wcc = 0;
+	Dlg->ShowItem(2, false);		Dlg->ShowItem(4, false);	
+	Dlg->ShowItem(5, false);	
+	hDlg = CreateDlgWnd("2x2 Table", 50, 50, 450, 200, Dlg, 0x4L);
+	ResizeDlgWnd(hDlg, 370, 150);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+			case 0:
+				res = -1;
+				break;
+			case 600:			//level 0 OK
+				Dlg->ShowItem(2, true);			Dlg->ShowItem(4, true);	
+				Dlg->ShowItem(5, true);			Dlg->ShowItem(3, false);
+				ResizeDlgWnd(hDlg, 450, 200);	Dlg->Command(CMD_REDRAW, 0L, 0L);
+				level = 1;
+			case 4:				//level 1 OK
+				for(i = 0; i < 9; i++) {
+					v[i] = 0.0;					Dlg->GetValue(v_idx[i], &v[i]);
+					v[i] = fabs(floor(v[i]));
+					}
+				v[2] = v[0] + v[1];				v[5] = v[3] + v[4];
+				v[6] = v[0] + v[3];				v[7] = v[1] + v[4];
+				v[8] = v[6] + v[7];				chi2 = v[0]*v[4]-v[1]*v[3];
+				for(i = wcc = 0; i < 9; i++) {
+					dbl_to_str1(TmpTxt, TMP_TXT_SIZE, "%g", v[i]);
+					Dlg->SetText(v_idx[i], TmpTxt);
+					}
+				if(v[8] < 128.0) do {
+					pf = factrl((int)v[2])/factrl((int)v[0])*factrl((int)v[5])/factrl((int)v[1])
+						*factrl((int)v[6])/factrl((int)v[3])*factrl((int)v[7])/factrl((int)v[4]);
+					pf /= factrl((int)v[8]);
+					pfa[wcc++] = pf;
+					//worse case correction
+					//RR Sokal & FJ Rohlf: Biometry, 3rd ed., pp. 734 ff.
+					if((v[0]*v[4]- v[1]*v[3]) < 0.0) {
+						v[0]-=1.0;	v[4]-=1.0;	v[1]+=1.0;	v[3]+=1.0;
+						}
+					else if((v[0]*v[4]- v[1]*v[3]) > 0.0) {
+						v[0]+=1.0;	v[4]+=1.0;	v[1]-=1.0;	v[3]-=1.0;
+						}
+					else break;
+					}while(v[0]>=0.0 && v[1]>=0.0 && v[3]>=0.0 && v[4]>=0.0 && wcc < 128);
+				if(wcc){
+					for(i = 1, pf = pfa[0]; i < wcc; i++){
+						pf += pfa[i];
+						}
+					if(pf > 1.0) pf = 1.0;
+					dbl_to_str1(TmpTxt, TMP_TXT_SIZE, "P(one sided) = %.4lf", pf);
+					Dlg->SetText(410, pf >= 0.001 ? TmpTxt : (char*)"P < 0.001");
+					}
+				else Dlg->SetText(410, "- - -");
+				dn = (v[2]*v[5]*v[6]*v[7]);
+				chi2 = dn > 0.0  ? (chi2*chi2*v[8])/dn : 0.0;
+				p = chi_dist(chi2, 1.0, 1.0);
+				dbl_to_str1(TmpTxt, TMP_TXT_SIZE, "Chi2 = %g", chi2);
+				Dlg->SetText(407, TmpTxt);
+				if(p >= 0.001) {
+					dbl_to_str1(TmpTxt, TMP_TXT_SIZE, "P = %g", p);
+					Dlg->SetText(408, TmpTxt);
+					}
+				else Dlg->SetText(408, "P < 0.001");
+				Dlg->Command(CMD_REDRAW, 0L, 0L);
+				res= -1;
+				break;
+			}
+		}while (res < 0);
+	CloseDlgWnd(hDlg);
+	delete Dlg;		free(twDlg);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// compare means / medians of two groups
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void rep_compmeans(GraphObj *parent, DataObj *data)
+{
+	TabSHEET tab1 = {0, 42, 10, "Data Input"};
+	double ci = 95.0;
+	DlgInfo *MeanDlg;
+	void *dyndata[] = {(void*)&tab1, (void*)"range for first variable",
+		(void*)"range for second variable", (void*)"confidence interval:",
+		(void*)&ci, (void*)" "};
+	char *ttest[] = {"Student's t = %g", "P = %g", "P(corr.) = %g"};
+	char *utest[] = {"Mann-Whitney U = %g", "z = %g", "P = %g", "z(corr.) = %g", "P(corr.) = %g"};
+	char g1_nam[30], g2_nam[30], *c_name;
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, res, n1, n2, r, c, *ny;
+	bool bContinue = false;
+	double *d1, *d2, dtmp, *rs, cx, cy, min1,max1, min2, max2;
+	scaleINFO scale = {{0.0, 0.9}, {0.0, 0.9}, {0.0, 0.9}};
+	char *txt_obj;
+	anyResult ares;
+	AccRange *rD;
+	Graph *graph;
+	Page *page;
+
+	if(!parent || !data) return;
+	if(!(MeanDlg = CompileDialog(RegrDlg_Tmpl, dyndata))) return;
+	UseRangeMark(data, 1, TmpTxt, TmpTxt+100);
+	if(!(Dlg = new DlgRoot(MeanDlg, data)))return;
+	Dlg->ShowItem(107, false);
+	d1 = d2 = 0L;
+	hDlg = CreateDlgWnd("Compare Means", 50, 50, 420, 260, Dlg, 0x4L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 1:
+			if(d1) free(d1);		if(d2) free(d2);		d1 = d2 = 0L;
+			min1 = min2 = dBounds.Ymin = HUGE_VAL;		max1 = max2 = dBounds.Ymax = -HUGE_VAL;
+			if(Dlg->GetText(101,TmpTxt,TMP_TXT_SIZE)&&(rD=new AccRange(TmpTxt))&&(n1=rD->CountItems())&&(d1=(double*)malloc(n1*sizeof(double)))){
+				if(c_name = rD->RangeDesc(data, 2)) {
+					rlp_strcpy(g1_nam, 30, c_name);		g1_nam[0] = toupper(g1_nam[0]);
+					free(c_name);
+					}
+				else rlp_strcpy(g1_nam, 30, "Group 1");
+				for(n1 = 0, rD->GetFirst(&c, &r); rD->GetNext(&c, &r); ) {
+					if(data->GetResult(&ares, r, c, false) && ares.type == ET_VALUE){
+						if(dBounds.Ymin > ares.value) dBounds.Ymin = ares.value;
+						if(dBounds.Ymax < ares.value) dBounds.Ymax = ares.value;
+						if(min1 > ares.value) min1 = ares.value;		if(max1 < ares.value) max1 = ares.value;
+						d1[n1++] = ares.value;
+						}
+					}
+				delete rD;
+				}
+			if(Dlg->GetText(103,TmpTxt,TMP_TXT_SIZE)&&(rD=new AccRange(TmpTxt))&&(n2=rD->CountItems())&&(d2=(double*)malloc(n2*sizeof(double)))){
+				if(c_name = rD->RangeDesc(data, 2)) {
+					rlp_strcpy(g2_nam, 30, c_name);		g2_nam[0] = toupper(g2_nam[0]);
+					free(c_name);
+					}
+				else rlp_strcpy(g2_nam, 30, "Group 2");
+				for(n2 = 0, rD->GetFirst(&c, &r); rD->GetNext(&c, &r); ) {
+					if(data->GetResult(&ares, r, c, false) && ares.type == ET_VALUE){
+						if(dBounds.Ymin > ares.value) dBounds.Ymin = ares.value;
+						if(dBounds.Ymax < ares.value) dBounds.Ymax = ares.value;
+						if(min2 > ares.value) min2 = ares.value;		if(max2 < ares.value) max2 = ares.value;
+						d2[n2++] = ares.value;
+						}
+					}
+				delete rD;
+				}
+			if(g1_nam[0] && g2_nam[0] && 0==strcmp(g1_nam, g2_nam)) {
+				rlp_strcpy(g1_nam, 30, "Group 1");		rlp_strcpy(g2_nam, 30, "Group 2");
+				}
+			if(!d1 || !d2 || n1 < 2 || n2 < 2) {
+				InfoBox("Insufficient data to calculate means!");
+				bContinue = true;
+				res = -1;
+				}
+			Dlg->GetValue(105, &ci);
+			break;
+			}
+		}while (res < 0);
+	if(res == 1 && d1 && d2 && n1>1 && n2>1 && (rs = (double*)malloc(40*sizeof(double))) && (ny = (int*)malloc(2*sizeof(int)))) {
+		dBounds.Xmin = 0.5;		rs[0] = 1.0;		dBounds.Xmax = 2.3;		rs[1] = 2.0;
+		dtmp = d_variance(n1, d1, &rs[2], 0L);		rs[10] = sqrt(dtmp);
+		dtmp = d_variance(n2, d2, &rs[3], 0L);		rs[11] = sqrt(dtmp);
+		rs[12] = (double)n1;						rs[13] = (double)n2;
+		rs[6] = rs[10]/sqrt(rs[12]);				rs[7] = rs[11]/sqrt(rs[13]);
+		rs[4] = rs[2] - rs[6];						rs[5] = rs[3] - rs[7];
+		rs[6] += rs[2];								rs[7] += rs[3];
+		rs[8] = rs[2] - rs[10];						rs[9] = rs[3] - rs[11];
+		rs[10] += rs[2];							rs[11] += rs[3];
+		ny[0] = n1;									ny[1] = n2;
+		rep_init();									page = new Page(parent, data);
+		ci /= 100.0;
+		mk_header(page, "<b>Compare Means of Two Groups</b>", data);
+		if((graph = new Graph(parent, data, 0L, 0)) && (txt_obj = mk_boxplot(0, rs, rs+2, rs+4, rs+6, rs+8, rs+10,
+				ny, 2,"Mean","Std. Err.","Std. Dev."))){
+			scale.sx.fx = (txtdef1.fSize*5.0);			scale.sy.fx = (txtdef1.fSize*10.0);
+			graph->GRect.Xmax = defs.GetSize(SIZE_GRECT_BOTTOM);
+			graph->DRect.Xmin *= 0.8;					graph->moveable = 0;
+			graph->DRect.Xmax = graph->GRect.Xmax - (txtdef1.fSize*2.0);
+			OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
+			if(LastOpenGO && LastOpenGO->Id == GO_BOXPLOT) {
+				if(((BoxPlot*)LastOpenGO)->x_tv = new TextValue()){
+					((BoxPlot*)LastOpenGO)->x_tv->GetValue(g1_nam);
+					((BoxPlot*)LastOpenGO)->x_tv->GetValue(g2_nam);
+					}
+				}
+			free(txt_obj);						graph->Command(CMD_SCALE, &scale, 0L);
+			cx = graph->GetSize(SIZE_GRECT_RIGHT)+txtdef1.fSize*3.0;
+			cy = mk_mean_report(page, cx, graph->GetSize(SIZE_GRECT_TOP), d1, n1, ci, g1_nam);
+			cy = mk_mean_report(page, cx, cy + txtdef1.fSize, d2, n2, ci, g2_nam);
+			cy += linsp1;
+			rep_DrawText(page, graph->GetSize(SIZE_GRECT_RIGHT)+txtdef1.fSize*3.0, cy, false, TXA_HLEFT, &txtdef1, "<b>t-Test:</b>");
+			cy += linsp1;						d_ttest(d1, d2, n1, n2, 0L, 0L, rs+15);
+			for(i = 0; i < 3; i++) {
+				switch(i) {
+					case 0:			dtmp = rs[24];			break;
+					case 1:			dtmp = rs[21];			break;
+					case 2:			dtmp = rs[23];			break;
+					}
+#ifdef USE_WIN_SECURE
+				j = sprintf_s(TmpTxt, 80, ttest[i], dtmp);
+#else
+				j = sprintf(TmpTxt, ttest[i], dtmp);
+#endif
+				if(i && dtmp < 0.0001) {
+					while(TmpTxt[j] != '=' && j) j--;
+					rlp_strcpy(TmpTxt+j, 10, "< 0.0001");
+					}
+				rep_DrawText(page, cx+(txtdef1.fSize*3.0), cy, false, TXA_HLEFT, &txtdef1, TmpTxt);
+				cy += linsp1/1.2;
+				}
+			page->Command(CMD_DROP_GRAPH, graph, 0L);
+			}
+		d_quartile(n1, d1, &rs[6], &rs[2], &rs[4]);
+		d_quartile(n2, d2, &rs[7], &rs[3], &rs[5]);
+		rs[8] = min1;	rs[9] = min2;	rs[10] = max1;	rs[11] = max2;
+		cy = graph->GetSize(SIZE_GRECT_BOTTOM)+txtdef2.fSize*3.0;
+		if((graph = new Graph(parent, data, 0L, 0)) && (txt_obj = mk_boxplot(0, rs, rs+2, rs+4, rs+6, rs+8, rs+10,
+				ny, 2,"Median","25-75%","Min./Max."))){
+			scale.sy.fx = cy;
+			graph->GRect.Xmax = defs.GetSize(SIZE_GRECT_BOTTOM);
+			graph->DRect.Xmin *= 0.8;					graph->moveable = 0;
+			graph->DRect.Xmax = graph->GRect.Xmax - (txtdef1.fSize*2.0);
+			OpenGraph(graph, 0L, (unsigned char*)txt_obj, false);
+			if(LastOpenGO && LastOpenGO->Id == GO_BOXPLOT) {
+				if(((BoxPlot*)LastOpenGO)->x_tv = new TextValue()){
+					((BoxPlot*)LastOpenGO)->x_tv->GetValue(g1_nam);
+					((BoxPlot*)LastOpenGO)->x_tv->GetValue(g2_nam);
+					}
+				}
+			free(txt_obj);								graph->Command(CMD_SCALE, &scale, 0L);
+			cy = mk_median_report(page, cx, graph->GetSize(SIZE_GRECT_TOP), d1, n1, .95, g1_nam);
+			cy = mk_median_report(page, cx, cy + txtdef1.fSize, d2, n2, .95, g2_nam);
+			cy += linsp1;
+			rep_DrawText(page, graph->GetSize(SIZE_GRECT_RIGHT)+txtdef1.fSize*3.0, cy, false, TXA_HLEFT, &txtdef1, "<b>u-Test:</b>");
+			cy += linsp1;			d_utest(d1, d2, n1, n2, 0L, 0L, rs+15);
+			for(i = 0; i < 5; i++) {
+				switch(i) {
+					case 0:			dtmp = rs[17];			break;
+					case 1:			dtmp = rs[18];			break;
+					case 2:			dtmp = rs[21];			break;
+					case 3:			dtmp = rs[22];			break;
+					case 4:			dtmp = rs[23];			break;
+					}
+#ifdef USE_WIN_SECURE
+				j = sprintf_s(TmpTxt, 80, utest[i], dtmp);
+#else
+				j = sprintf(TmpTxt, utest[i], dtmp);
+#endif
+				if(i && dtmp < 0.0001) {
+					while(TmpTxt[j] != '=' && j) j--;
+					rlp_strcpy(TmpTxt+j, 10, "< 0.0001");
+					}
+				rep_DrawText(page, cx+(txtdef1.fSize*3.0), cy, false, TXA_HLEFT, &txtdef1, TmpTxt);
+				cy += linsp1/1.2;
+				}
+			page->Command(CMD_DROP_GRAPH, graph, 0L);
+			}
+		parent->Command(CMD_DROP_GRAPH, page, 0L);	
+		free(rs);			free(ny);
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;				free(MeanDlg);
+	if(d1) free(d1);		if(d2) free(d2);
+}
diff --git a/rlp_math.cpp b/rlp_math.cpp
index ab5cf56..0b109f1 100755
--- a/rlp_math.cpp
+++ b/rlp_math.cpp
@@ -1,2604 +1,2631 @@
-//rlp_math.cpp, Copyright (c) 2004-2007 R.Lackner
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-#include "rlplot.h"
-#include <math.h>
-#include <stdlib.h>
-#include <ctype.h>
-#include <string.h>
-#include <time.h>
-
-#define SWAP(a,b) {double temp=(a);(a)=(b);(b)=temp;}
-#define _PREC 1.0e-12
-
-extern Default defs;
-
-static char *MRQ_error = 0L;
-static double sqrt2pi = sqrt(_PI*2.0);
-
-//---------------------------------------------------------------------------
-//utilitity functions for memory allocation
-double **dmatrix(int nrl, int nrh, int ncl, int nch)
-{
-	int i;
-	double **m;
-
-	m = (double **)malloc(nrh * sizeof(double*));
-	//Allocate rows and set pointers to them
-	for(i = 0; i < nrh; i++) {
-		m[i] = (double *)malloc(nrh * sizeof(double));
-		}
-	return m;
-}
-void free_dmatrix(double **m, int nrl, int nrh, int ncl, int)
-{
-	int i;
-
-	for(i = 0; i < nrh; i++) free(m[i]);
-	free(m);
-}
-
-//---------------------------------------------------------------------------
-//The routine gaussj solves linear equations by Gauss-Jordan elimination
-bool gaussj(double **a, int n, double **b, int m)
-{
-	int *indxc, *indxr, *ipiv;
-	int i, icol, irow, j, k, l, ll;
-	double big, dum, pivinv;
-
-	indxc = (int*)malloc(n*sizeof(int*));
-	indxr = (int*)malloc(n*sizeof(int*));
-	ipiv = (int*)malloc(n*sizeof(int*));
-	for (j = 0; j < n; j++) ipiv[j] = 0;
-	for (i = 0; i < n; i++) {				//This is the main loop over the
-		big = 0.0;							//    columns to be reduced
-		for(j = 0; j < n; j ++)				//This is the outer loop of the search
-			if(ipiv[j] != 1)				//    for a pivot element
-				for(k = 0; k < n; k ++) {
-					if (ipiv[k] == 0) {
-						if(fabs(a[j][k]) >= big) {
-							big = fabs(a[j][k]);
-							irow = j;				icol = k;
-							}
-						}
-					else if(ipiv[k] > 1) {
-						MRQ_error = "Singular Matrix (1)";
-						free(ipiv);		free(indxr);	free(indxc);
-						return false;
-						}
-				}
-		++(ipiv[icol]);
-		//We now have the pivot element, so we interchange rows, if needed,
-		// to put the pivot element on the diagonal.
-		if(irow != icol) {
-			for(l = 0; l < n; l++) SWAP(a[irow][l], a[icol][l])
-			for(l = 0; l < m; l++) SWAP(b[irow][l], b[icol][l])
-			}
-		indxr[i] = irow;		indxc[i] = icol;
-		if(a[icol][icol] == 0.0) {
-			MRQ_error = "Singular Matrix (2)";
-			free(ipiv);		free(indxr);	free(indxc);
-			return false;
-			}
-		pivinv = 1.0/a[icol][icol];
-		a[icol][icol] = 1.0;
-		for(l = 0; l < n; l++) a[icol][l] *= pivinv;
-		for(l = 0; l < m; l++) b[icol][l] *= pivinv;
-		for(ll = 0; ll <  n; ll++)
-			if(ll != icol) { 							//Next, we reduce the rows
-				dum = a[ll][icol];
-				a[ll][icol] = 0.0;
-				for(l = 0; l < n; l++) a[ll][l] -= a[icol][l]*dum;
-				for(l = 0; l < m; l++) b[ll][l] -= b[icol][l]*dum;
-				}
-		}											// This is the end of the main loop
-	for (l = n; l > 0; l--) {						//   over columns of the reduction.
-		if(indxr[l] != indxc[l]) 					//   Unscramble the solution
-			for(k = 0; k < n; k++) SWAP (a[k][indxr[l]], a[k][indxc[l]]);
-		}											//And we are done.
-	free(ipiv);		free(indxr);	free(indxc);
-	return true;
-}
-
-//---------------------------------------------------------------------------
-//The routine mrqcof is called by mrqmin to evaluate the linearized fitting
-// matrix alpha and vector beta
-void mrqcof(double x[], double y[], double z[], int ndata, double **a, int ma,
-	int lista[], int mfit, double **alpha, double beta[], double *chisq,
-	void (*funcs)(double, double, double **, double *, double *, int))
-{
-	int k, j, i;
-	double ymod, wt, dy;
-	double *dyda;
-
-	dyda = (double*)malloc(ma*sizeof(double));
-	for(j = 0; j < mfit; j++) {					//Initialize (symmetric) alpha, beta
-		for(k = 0; k <= j; k++) alpha[j][k] = 0.0;
-		beta[j] = 0.0;
-		}
-	*chisq = 0.0;
-	for (i = 0; i < ndata; i++) {		 		//Summation loop over all data
-		(*funcs)(x[i], z ? z[i] : 0.0, a, &ymod, dyda, ma);
-		if(ymod != 0.0) dy = y[i]-ymod;			//functions = 0.0 if out of range
-		else dy = 0.0;
-		for(j = 0; j < mfit; j++) {
-			wt = dyda[lista[j]];
-			for (k = 0; k <= j; k++){
-				alpha[j][k] += wt*dyda[lista[k]];
-				}
-			beta[j] += dy*wt;
-			}
-		(*chisq) += dy*dy; 							//And find X^2 if function o.k.
-		}
-	for(j = 0; j < mfit; j++)						//Fill the symmetric side
-		for(k = 0; k <= j; k++) alpha[k][j]=alpha[j][k];
-	free(dyda);
-}
-
-//---------------------------------------------------------------------------
-//The routine mrqmin performs one iteration of Marquart's method for nonlinear
-// parameter estimation
-bool mrqmin(double *x, double *y, double *z, int ndata, double **a, int ma,
-	int *lista, int mfit, double **covar, double **alpha, double *chisq,
-	void (*funcs)(double, double, double **, double *, double *, int), double *alamda)
-{
-	int k, kk, j, ihit;
-	static double *da, *atry, *beta, ochisq;
-	static double **oneda, **atryref;
-
-	if (*alamda < 0.0) {								//Initialization
-		MRQ_error = 0L;
-		oneda = dmatrix(1, mfit, 1, 1);
-		atry = (double *)malloc(ma * sizeof(double));
-		atryref = (double**)malloc(ma * sizeof(double*));
-		for(j=0; j < ma; atryref[j++] = &atry[j]);
-		da = (double*)malloc(ma *sizeof(double));
-		beta = (double*)malloc(ma *sizeof(double));
-		kk = mfit+1;
-		for(j = 0; j < ma; j++) { 						//Does lista contain a proper
-			ihit = 0;									//   permutation of the
-			for(k = 0; k < mfit; k++)					//   coefficients ?
-				if(lista[k] == j) ihit++;
-			if(ihit == 0)
-				lista[kk++] = j;
-			else if (ihit >1) ErrorBox("Bad LISTA permutations in MRQMIN-1");
-			}
-		if(kk != ma+1) ErrorBox("Bad LISTA permutations in MRQMIN-2");
-		*alamda = 0.001;
-		mrqcof(x, y, z, ndata, a, ma, lista, mfit, alpha, beta, chisq, funcs);
-		ochisq=(*chisq);
-		}
-	for (j = 0; j < mfit; j++) {						//Alter linearized fitting matrix
-		for(k = 0; k < mfit; k++) covar[j][k] = alpha[j][k];	// by augmenting
-		covar[j][j] = alpha[j][j]*(1.0+(*alamda));		// diagaonal elements
-		oneda[j][0] = beta[j];
-		}
-	if (!gaussj(covar, mfit, oneda, 1)) return false;	//Matrix solution ?
-	for(j = 0; j < mfit; j++) da[j] = oneda[j][0];
-	if(*alamda == 0.0) {								//Once converged evaluate
-														//  covariance matrix with
-		free(beta);										//  alamda = 0.
-		free(da);
-		free(atry);
-		free(atryref);
-		free_dmatrix(oneda, 1, mfit, 1, 1);
-		return true;
-		}
-	for(j = 0; j < ma; j++) atry[j] = *a[j];
-	for(j = 0; j < mfit; j++)							//Did the trial succeed ?
-		atry[lista[j]] = *a[lista[j]] + da[j];
-	mrqcof(x, y, z, ndata, atryref, ma, lista, mfit, covar, da, chisq, funcs);
-	if(*chisq < ochisq) {								//Success, accept the new solution
-		*alamda *= 0.1;
-		ochisq=(*chisq);
-		for(j = 0; j < mfit; j++) {
-			for(k = 0; k < mfit; k++) alpha[j][k] = covar[j][k];
-			beta[j] = da[j];
-			*a[lista[j]] = atry[lista[j]];
-			}
-		}
-	else {												//Failure, increase almda and
-		*alamda *= 10.0;								//    return.
-		*chisq = ochisq;
-		}
-	return true;
-}
-
-bool Check_MRQerror()
-{
-	bool bRet;
-
-	if(bRet = MRQ_error != 0L) ErrorBox(MRQ_error);
-	MRQ_error = 0L;
-	return bRet;
-}
-
-//---------------------------------------------------------------------------
-//Use heap sort to sort elements of an float array
-//W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1988/1989)
-//Numerical Recipes in C, Cambridge University Press, ISBN 0-521-35465-X
-// p. 245
-void SortArray(int n, double *vals)
-{
-	int l, j, ir, i;
-	double rra, *ra = vals-1;
-
-	if(n < 2 || !vals) return;
-	l=(n >> 1) + 1;				ir = n;
-	for( ; ; ) {
-		if(l > 1) rra = ra[--l];
-		else {
-			rra = ra[ir];		ra[ir] = ra[1];
-			if(--ir == 1) {
-				ra[1] = rra;	return;
-				}
-			}
-		i = l;					j = l << 1;
-		while (j <= ir) {
-			if (j < ir && ra[j] < ra[j+1]) ++j;
-			if (rra < ra[j]) {
-				ra[i] = ra[j];	j += (i=j);
-				}
-			else j = ir + 1;
-			}
-		ra[i] = rra;
-		}
-}
-
-//sorts array v1 making the corresponding rearrangement of v2
-void SortArray2(int n, double *v1, double *v2)
-{
-	int l, j, ir, i;
-	double rra, rrb, *ra = v1-1, *rb = v2-1;
-
-	if(n < 2 || !v1 || !v2) return;
-	l=(n >> 1) + 1;				ir = n;
-	for( ; ; ) {
-		if(l > 1) {
-			rra = ra[--l];		rrb = rb[l];
-			}
-		else {
-			rra = ra[ir];		rrb = rb[ir];
-			ra[ir] = ra[1];		rb[ir] = rb[1];
-			if(--ir == 1) {
-				ra[1] = rra;	rb[1] = rrb;
-				return;
-				}
-			}
-		i = l;					j = l << 1;
-		while (j <= ir) {
-			if (j < ir && ra[j] < ra[j+1]) ++j;
-			if (rra < ra[j]) {
-				ra[i] = ra[j];	rb[i] = rb[j];
-				j += (i=j);
-				}
-			else j = ir + 1;
-			}
-		ra[i] = rra;			rb[i] = rrb;
-		}
-}
-
-//Use heap sort to sort elements of an xy array
-void SortFpArray(int n, lfPOINT *vals)
-{
-	int l, j, ir, i;
-	lfPOINT rra, *ra = vals-1;
-
-	if(n < 2) return;
-	l=(n >> 1) + 1;					ir = n;
-	for( ; ; ) {
-		if(l > 1) {
-			rra.fx = ra[--l].fx; rra.fy = ra[l].fy;
-			}
-		else {
-			rra.fx = ra[ir].fx;		rra.fy = ra[ir].fy;
-			ra[ir].fx = ra[1].fx;	ra[ir].fy = ra[1].fy;	
-			if(--ir == 1) {
-				ra[1].fx = rra.fx;	ra[1].fy = rra.fy;
-				return;
-				}
-			}
-		i = l;					j = l << 1;
-		while (j <= ir) {
-			if (j < ir && ra[j].fx < ra[j+1].fx) ++j;
-			if (rra.fx < ra[j].fx) {
-				ra[i].fx = ra[j].fx;	ra[i].fy = ra[j].fy;
-				j += (i=j);
-				}
-			else j = ir + 1;
-			}
-		ra[i].fx = rra.fx;				ra[i].fy = rra.fy;
-		}
-}
-
-//randomize array
-double *randarr(double *v0, int n, long *seed)
-{
-	double r, *v, *v_tmp;
-	int i, j, l;
-
-	if(!(v = (double*)malloc(n *sizeof(double)))) return 0L;
-	if(!(v_tmp = (double*)memdup(v0, n *sizeof(double),0))) return 0L;
-	for(l = n, i = 0; i < n; ) {
-		r = ran2(seed);			j = (int)(r *((double)l));
-		if(j < l) {
-			v[i++] = v_tmp[j];
-			if(j < l)memcpy(v_tmp+j, v_tmp+j+1, (l-j)*sizeof(double));
-			l--;
-			}
-		}
-	return v;
-}
-
-//resample array
-double *resample(double *v0, int n, long *seed)
-{
-	double r, *v;
-	int i, j;
-
-	if(!(v = (double*)malloc(n *sizeof(double)))) return 0L;
-	for(i = 0; i < n; ) {
-		r = ran2(seed);			j = (int)(r *((double)n));
-		if(j < n) v[i++] = v0[j];
-		}
-	return v;
-}
-
-//---------------------------------------------------------------------------
-// Cubic Spline Interpolation
-// Ref.: W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1989), 
-//    Numerical Rcipies in C. The Art of Scientific Computing, 
-//    Cambridge University Press, ISBN 0-521-35465, pp. 96 ff.
-void spline(lfPOINT *v, int n, double *y2)
-{
-	int i, k;
-	double p, qn, sig, un, *u;
-
-	u = (double *)malloc(n * sizeof(double));
-	y2[0] = u[0] = 0.0;
-	for(i = 1; i < (n-1); i++) {
-		sig = (v[i].fx-v[i-1].fx)/(v[i+1].fx-v[i-1].fx);
-		p = sig*y2[i-1]+2.0;			y2[i]=(sig-1.0)/p;
-		u[i]=(v[i+1].fy-v[i].fy)/(v[i+1].fx-v[i].fx)-(v[i].fy-v[i-1].fy)/(v[i].fx-v[i-1].fx);
-		u[i]=(6.0*u[i]/(v[i+1].fx-v[i-1].fx)-sig*u[i-1])/p;
-		}
-	qn = un = 0.0;
-	y2[n-1] = (un - qn * u[n-2])/(qn*y2[n-2]+1.0);
-	for(k = n-2; k >= 0; k--) {
-		y2[k] = y2[k]*y2[k+1]+u[k];
-		}
-	free(u);
-}
-
-//---------------------------------------------------------------------------
-// The Gamma Function: return the ln(G(xx)) for xx > 0
-// Ref: B.W. Brown, J. Lovato, K. Russel (1994)
-//    DCDFLIB.C, Library of C Routinesfor Cumulative Distribution Functions,
-//    Inverses, and other Parameters.
-
-double devlpl(double a[], int n, double x)
-{
-	double term;
-	int i;
-
-	for(term = a[n-1], i= n-2; i>=0; i--) term = a[i] + term * x;
-	return term;
-}
-
-
-double gammln(double x)
-{
-	static double coef[] = {0.83333333333333023564e-1,-0.27777777768818808e-2, 
-	0.79365006754279e-3, -0.594997310889e-3, 0.8065880899e-3};
-static double scoefd[] = {0.62003838007126989331e2, 0.9822521104713994894e1,
-	-0.8906016659497461257e1, 0.1000000000000000000e1};
-static double scoefn[] = {0.62003838007127258804e2, 0.36036772530024836321e2,
-	0.20782472531792126786e2, 0.6338067999387272343e1,0.215994312846059073e1,
-	0.3980671310203570498e0, 0.1093115956710439502e0,0.92381945590275995e-2,
-	0.29737866448101651e-2};
-	double offset, prod, xx;
-	int i,n;
-
-    if(x < 6.0) {
-		prod = 1.0e0;	    xx = x;
-		while(xx > 3.0) {
-			xx -= 1.0;			prod *= xx;
-			}
-		if(x <= 2.0) while(xx < 2.0) {
-			prod /= xx;			xx += 1.0;
-			}
-		// compute rational approximation to gamma(x)
-		return log(devlpl(scoefn, 9, xx-2.0) / devlpl(scoefd, 4, xx-2.0) * prod);
-		}
-	else {
-		offset = 0.91893853320467274178;	// hln2pi
-		// if necessary make x at least 12 and carry correction in offset
-		if(n = 13.0 >= x ? (int)(12.0 - x) : 0) xx = x;
-		else {
-			for(i=1, prod = 1.0; i<= n; i++) prod *= (x+(double)(i-1));
-			offset -= log(prod);			xx = x+(double)n;
-			}
-		// compute power series
-		return devlpl(coef, 5, 1.0/(xx*xx)) / xx + (offset+(xx-0.5)*log(xx)-xx);
-		}
-}
-
-//---------------------------------------------------------------------------
-// Special Functions
-// Ref.: W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1989), 
-//    Numerical Rcipies in C. The Art of Scientific Computing, 
-//    Cambridge University Press, ISBN 0-521-35465, pp. 166 ff.
-
-//The Factorial Function: return n!
-double factrl(int n)
-{
-	static int ntop = 4;
-	static double a[33]={1.0, 1.0, 2.0, 6.0, 24.0};
-	int j;
-
-	if(n < 0) return 0.0;		//error: no factorial for negative numbers
-	if(n > 32) return exp(gammln(n+1.0));
-	while(ntop < n) {			//fill in table up to desired value
-		j = ntop++;		a[ntop]=a[j] * ntop;
-		}
-	return a[n];
-}
-
-//returns the incomplete gamma function evaluated by its series representation
-void gser(double *gamser, double a, double x, double *gln)
-{
-	int n;
-	double sum, del, ap;
-
-	*gln = gammln(a);
-	if(x <= 0) {
-		*gamser = 0.0;			return;
-		}
-	else {
-		ap = a;					del = sum = 1.0/a;
-		for(n = 1; n <= 100; n++) {
-			ap += 1.0;			del *= x/ap;		sum += del;
-			if(fabs(del) <= fabs(sum) * _PREC) {
-				*gamser = sum * exp(-x + a * log(x)-(*gln));
-				return;
-				}
-			}
-		// maximum number of iterations exceeded
-		*gamser = sum * exp(-x + a * log(x)-(*gln));
-		}
-
-}
-
-//returns the incomplete gamma function evaluated by its continued fraction representation
-void gcf(double *gammcf, double a, double x, double *gln)
-{
-	int n;
-	double gold=0.0, g, fac=1.0, b1=1.0, b0=0.0, anf, ana, an, a1, a0=1.0;
-
-	*gln=gammln(a);		a1=x;
-	for(n=1; n <= 100; n++) {
-		an = (double)n;			ana = an -a;		a0 = (a1 + a0 * ana) * fac;
-		b0 = (b1 + b0 * ana) *fac;					anf = an * fac;
-		a1 = x * a0 + anf * a1;						b1 = x * b0 + anf * b1;
-		if(a1) {
-			fac = 1.0 / a1;							g = b1 * fac;
-			if(fabs((g-gold)/g) <= _PREC) {
-				*gammcf = exp(-x + a * log(x) -(*gln)) * g;
-				return;
-				}
-			gold = g;
-			}
-		}
-	// maximum number of iterations exceeded
-	*gammcf = exp(-x + a * log(x) -(*gln)) * gold;
-}
-
-//returns the incomplete gamma function P(a,x)
-double gammp(double a, double x)
-{
-	double gamser, gammcf, gln;
-
-	if(x < 0.0 || a <= 0.0) return 0.0;
-	if(x < (a+1.0)) {
-		gser(&gamser, a, x, &gln);			return gamser;
-		}
-	else {
-		gcf(&gammcf, a, x, &gln);			return 1.0-gammcf;
-		}
-	return 0.0;
-}
-
-//returns the complementary incomplete gamma function Q(a,x)
-double gammq(double a, double x)
-{
-	double gamser, gammcf, gln;
-
-	if(x < 0.0 || a <= 0.0) return 0.0;
-	if(x < (a+1.0)) {
-		gser(&gamser, a, x, &gln);			return 1.0-gamser;
-		}
-	else {
-		gcf(&gammcf, a, x, &gln);			return gammcf;
-		}
-	return 0.0;
-}
-
-//continued fraction for incomplete beta function, used by betai()
-double betacf(double a, double b, double x)
-{
-	double qap, qam, qab, em, tem, d, bz, bm = 1.0, bp, bpp, az = 1.0, am = 1.0, ap, app, aold;
-	int m;
-
-	qab = a+b;		qap = a+1.0;		qam = a-1.0;	bz = 1.0-qab*x/qap;
-	for(m = 1; m <= 100; m++) {
-		em = (double)m;			tem = em+em;
-		d = em*(b-em)*x/((qam+tem)*(a+tem));
-		ap = az + d * am;		bp = bz + d *bm;
-		d = -(a+em)*(qab+em)*x/((qap+tem)*(a+tem));
-		app = ap + d * az;		bpp = bp + d * bz;
-		aold = az;				am = ap/bpp;
-		bm = bp/bpp;			az = app/bpp;
-		bz = 1.0;
-		if(fabs(az-aold) <= (_PREC * fabs(az))) return az;	//success: return
-		}
-	return az;												//fail: iterations exceeded
-}
-
-//The incomplete beta function Ix(a,b) for 0 <= x <= 1
-double betai(double a, double b, double x)
-{
-	double bt;
-
-	if(x < 0.0 || x > 1.0) return 0.0;		//range !
-	if(x == 0.0 || x == 1.0) bt = 0.0;
-	else
-		bt = exp(gammln(a+b)-gammln(a)-gammln(b)+a*log(x)+b*log(1.0-x));
-	if(x < (a+1.0)/(a+b+2.0)) return bt * betacf(a, b, x)/a;
-	else return 1.0 - bt * betacf(b, a, 1.0 - x)/b;
-}
-
-//The following relations are obviously based on:
-//  Abramowitz, M. & Stegun I.A. (1964): Hanbook of Mathematical Functions.
-//    Applied Mathematics Series, vol. 55 (Washington: National Bureau
-//    of Standards).
-
-//the binomial coefficient
-double bincof(double n, double k)
-{
-	if(n<0 || k<0 || k > n) return 0.0;
-	return exp(gammln(n+1.0) - gammln(k+1.0) - gammln(n-k+1.0));
-}
-
-//the cumulative binomial distribution
-double binomdistf(double k, double n, double p)
-{
-	if(k > n || n < 0.0 || p < 0.0 || p >1.0) return 0.0;
-	return betai(n-k, k+1, p);
-}
-
-//the beta function
-double betaf(double z, double w)
-{
-	return exp(gammln(z)+gammln(w)-gammln(z+w));
-}
-
-//the error function: not all compilers have a built in erf()
-double errf(double x)
-{
-	return x < 0.0 ? -gammp(0.5, x*x) : gammp(0.5, x*x);
-}
-
-//the complementary error function
-double  errfc(double x)
-{
-//	return x < 0.0 ? 2.0 - gammq(0.5, x*x) : gammq(0.5, x*x);
-	return x < 0.0 ? 1.0 + gammp(0.5, x*x) : gammq(0.5, x*x);
-}
-
-//cumulative normal distribution
-double norm_dist(double x, double m, double s)
-{
-	return 0.5 + errf((x - m)/(s * _SQRT2))/2.0;
-}
-
-//normal distribution
-double norm_freq(double x, double m, double s)
-{
-	double ex;
-
-	ex = (x-m)/s;	ex = exp(-0.5*ex*ex);
-	return ex/(s*sqrt2pi);
-}
-
-//cumulative exponential distribution
-double exp_dist(double x, double l, double s)
-{
-	if(x >= 0.0 && l > 0.0) return 1.0-exp(-x*l);
-	else return 0.0;
-}
-
-//inverse exponential distribution
-double exp_inv(double p, double l, double s)
-{
-	if(p >= 1.0) return HUGE_VAL;
-	if(l <= 0.0) return 0.0;
-	return -log(1.0-p)/l;
-}
-
-//exponential distribution
-double exp_freq(double x, double l, double s)
-{
-	if(x >= 0.0 && l > 0.0) return l*exp(-x*l);
-	else return fabs(l);
-}
-
-//cumulative lognormal distribution
-double lognorm_dist(double x, double m, double s)
-{
-	return 0.5 + errf((log(x) - m)/(s * _SQRT2))/2.0;
-}
-
-//lognormal distribution
-double lognorm_freq(double x, double m, double s)
-{
-	double tmp;
-
-	if(x > 0.0 && m > 0.0 && s > 0.0) {
-		tmp = (log(x)-m)/s;
-		return exp(-0.5*tmp*tmp)/(x*s*sqrt2pi);
-		}
-	return 0.0;
-}
-
-//chi square distribution
-double chi_dist(double x, double df, double)
-{
-	if(x <= 0.0) return 1.0;
-	return gammq(df/2.0, x/2.0);
-}
-
-double chi_freq(double x, double df)
-{
-	if(x < 0.0 || df <= 0.0) return 0.0;
-	if(x < 1.0e-32) x = 1.0e-32;
-//formula by Wikipedia 
-//	return exp(log(2.0)*(1.0-df/2.0)+log(x)*(df-1.0)+x*x/-2.0-gammln(df/2.0));
-//formula by StatSoft's STATISTICA documentation
-	return exp(-x/2.0+log(x)*(df/2.0-1.0)-log(2.0)*df/2.0-gammln(df/2.0));
-}
-
-//t-distribution
-double t_dist(double t, double df, double)
-{
-	return betai(df/2.0, 0.5, (df/(df+t*t)));
-}
-
-double t_freq(double t, double df)
-{
-	double a, b, c, d;
- 
-	a = gammln((df+1.0)/2.0);		b = log(sqrt(df * _PI));
-	c = gammln(df/2.0);				d = log(1.0+t*t/df) * (df+1)/2.0;
-	return exp(a-b-c-d);
-}
-
-//poisson distribution
-double pois_dist(double x, double m, double)
-{
-	return gammq(x+1.0, m);
-}
-
-//f-distribution
-double f_dist(double f, double df1, double df2)
-{
-	return f > 0.0 ? betai(df2/2.0, df1/2.0, df2/(df2+df1*f)): 1.0;
-}
-
-double f_freq(double x, double df1, double df2)
-{
-	double a, b, c, d;
-
-	a = gammln((df1+df2)/2.0);		b = gammln(df1/2.0) + gammln(df2/2.0);
-	c = log(df1/df2) * df1/2.0 + log(x) * (df1/2.0-1.0);
-	d = log(1+(df1/df2)*x) * (-(df1+df2)/2.0);
-	return exp(a-b+c+d);
-}
-
-//---------------------------------------------------------------------------
-// The Weibull distribution
-//---------------------------------------------------------------------------
-double weib_dist(double x, double shape, double scale)
-{
-	double dn=1.0, sum, term, tmp;
-
-	if(shape <= 0.0 || scale <= 0.0) return HUGE_VAL;
-	if(x <= 0.0) return 0.0;
-	term = -pow(x/scale, shape);		tmp = fabs(term);
-	if(tmp < 2.22e-16) return tmp;
-	if (tmp > 0.697) return -exp(term)+1.0;
-	x = sum = term;
-	do {				//do taylor series
-		dn += 1.0 ;		term *= x/dn;		sum += term;
-		}while (fabs(term) > fabs(sum) * 2.22e-16) ;
-	return -sum;
-}
-
-double weib_freq(double x, double shape, double scale)
-{
-	double tmp1, tmp2;
-
-	if (shape <= 0.0 || scale <= 0.0) return HUGE_VAL;
-	if (x < 0) return 0.0;
-	if(x > -HUGE_VAL && x < HUGE_VAL) {
-		if(x == 0.0 && shape < 1.0) return HUGE_VAL;
-		tmp1 = pow(x / scale, shape - 1.0);
-		tmp2 = tmp1 * (x / scale);
-		return shape * tmp1 * exp(-tmp2) / scale;
-		}
-	return HUGE_VAL;
-}
-
-//---------------------------------------------------------------------------
-// The Cauchy (Lorentz) distribution
-//---------------------------------------------------------------------------
-double cauch_dist(double x, double loc, double scale)
-{
-	double y;
-
-	if(scale < 0.0) return HUGE_VAL;
-	x = (x - loc) / scale;
-	if(x > -HUGE_VAL && x < HUGE_VAL) {
-		if (fabs(x) > 1.0) {
-			y = atan(1.0/x)/_PI;		return (x > 0) ? 1.0-y : -y;
-			} 
-		else return 0.5 + atan(x)/_PI;
-		}
-	return HUGE_VAL;
-}
-
-double cauch_freq(double x, double loc, double scale)
-{
-	double y;
-
-	if(scale < 0.0) return HUGE_VAL;
-	if(x > -HUGE_VAL && x < HUGE_VAL) {
-		y = (x - loc) / scale;
-		return 1.0 / (_PI * scale * (1.0 + y*y));
-		}
-	return HUGE_VAL;
-}
-
-//---------------------------------------------------------------------------
-// The Logistic distribution
-//---------------------------------------------------------------------------
-double logis_dist(double x, double loc, double scale)
-{
-	if(scale < 0.0) return HUGE_VAL;
-	x = exp(-(x - loc) / scale);
-	if(x > -HUGE_VAL && x < HUGE_VAL) {
-		return 1.0/(1.0 + x);
-		}
-	return HUGE_VAL;
-}
-
-double logis_freq(double x, double loc, double scale)
-{
-	double e, f;
-
-	x = fabs((x - loc) / scale);
-	if(x > -HUGE_VAL && x < HUGE_VAL) {
-		e = exp(-x);     f = 1.0 + e;	
-		return  e / (scale * f*f);
-		}
-	return HUGE_VAL;
-}
-
-//---------------------------------------------------------------------------
-// Shapiro-Wilk W test and its significance level
-// Algorithm AS 394, 1995, Appl. Statist. 44(4), 547-551
-//
-static int do_swilk(double (*func)(double, double, double), double p1, double p2, 
-	double *x, int n, int n1, int n2, double *a, double *w, double *pw)
-{
-
-//initialized data
-const static double z90 = 1.2816;		//tinv(0.2, inf)
-const static double z95 = 1.6449;		//tinv(0.1, inf)
-const static double z99 = 2.3263;		//tinv(.05, inf)
-const static double zm = 1.7509;		//(z90 + z95 + z99)/3
-const static double zss = 0.56268;
-const static double bf1 = 0.8378;
-const static double xx90 = 0.556;
-const static double xx95 = 0.622;
-const static double sqrth = 0.70711;	//sqrt(0.5)
-const static double smal = 1.0e-19;		//small value
-const static double pi6 = 1.909859;
-const static double stqr = 1.047198;	//pi / 3
-
-//polynomial coefficients
-static double g[2] = {-2.273, 0.459};
-static double c1[6] = {0.0, 0.221157, -0.147981, -2.07119, 4.434685, -2.706056};
-static double c2[6] = {0.0, 0.042981, -0.293762, -1.752461, 5.682633, -3.582633};
-static double c3[4] = {0.544, -0.39978, 0.025054, -6.714e-4};
-static double c4[4] = {1.3822, -0.77857, 0.062767, -0.0020322};
-static double c5[4] = {-1.5861, -0.31082, -0.083751, 0.0038915};
-static double c6[3] = {-0.4803, -0.082676, 0.0030302};
-static double c7[2] = {0.164, 0.533};
-static double c8[2] = {0.1736, 0.315};
-static double c9[2] = {0.256, -0.00635};
-
-	//local variables
-	int i, j, ncens, i1, nn2;
-	double zbar, ssassx, summ2, ssumm2, gamma, delta, range;
-	double a1, a2, an, bf, ld, m, s, sa, xi, sx, xx, y, w1;
-	double fac, asa, an25, ssa, z90f, sax, zfm, z95f, zsd, z99f, rsn, ssx, xsx;
-
-	//parameter adjustment
-	--a;
-
-	*pw = 1.0;
-	if(*w >= 0.0) *w = 1.0;
-	an = (double)(n);			nn2 = n>>1;
-	if(n2 < nn2) return 3;
-	if(n < 3) return 1;
-	// calculate coefficients a[]
-	if(true) {
-		if(n == 3) a[1] = sqrth;
-		else {
-			for(i = 1, summ2 = 0.0, an25 = an + 0.25; i <= n2; ++i) {
-				a[i] = distinv(func, p1, p2, (i-0.375)/an25, 0);
-				summ2 += (a[i] * a[i]);
-				}
-			summ2 *= 2.0;			ssumm2 = sqrt(summ2);
-			rsn = 1.0 / sqrt(an);	a1 = devlpl(c1, 6, rsn) -a[1]/ssumm2;
-			//normalize a[]
-			if(n > 5) {
-				i1 = 3;
-				a2 = -a[2] / ssumm2 + devlpl(c2, 6, rsn);
-				fac = sqrt((summ2 - 2.0*a[1]*a[1] - 2.0*a[2]*a[2])
-					/ (1.0 - 2.0*a1*a1 - 2.0*a2*a2));
-				a[2] = a2;
-				}
-			else {
-				i1 = 2;
-				fac = sqrt((summ2 -2.0*a[1]*a[1]) / (1.0 - 2.0*a1*a1));
-				}
-			a[1] = a1;
-			for(i = i1; i <= nn2; ++i) a[i] /= -fac;
-			} 
-		}
-	if(n1 < 3) return 1;
-	ncens = n - n1;
-	if(ncens < 0 || (ncens > 0 && n < 20)) return 4;
-	delta = (double)ncens / an;
-	if(delta > 0.8) return 5;
-	//if w input as negative, calculate significance level of -w
-	if(*w < 0.0) { 
-		w1 = 1.0 + *w;
-		goto sw_prob;
-		}
-	//check for zero range
-	if((range = x[n1-1] -x[0]) < smal) return 6;
-	//check for sort order
-	xx = x[0]/range;	sx = xx;	sa = -a[1];		j = n -1;
-	for(i = 1; i < n1; --j) {
-		xi = x[i] / range;			sx += xi;			++i;
-		if(i != j) sa += i > j ? a[i < j ? i : j] : -a[i < j ? i : j];
-		xx = xi;
-		}
-	//calculate w statistic as squared correlation between data and coefficients
-	sa /= n1;		sx /= n1;		ssa = ssx = sax = 0.0;		j = n -1;
-	for(i = 0; i < n1; ++i, --j) {
-		if(i > j) asa = a[1+j] - sa;
-		else if(i < j) asa = -a[1+i] - sa;
-		else asa = -sa;
-		xsx = x[i] / range - sx;		ssa += asa * asa;
-		ssx += xsx * xsx;				sax += asa * xsx;
-		}
-	ssassx = sqrt(ssa * ssx);
-	w1 = (ssassx - sax) * (ssassx + sax) / (ssa * ssx);
-sw_prob:
-	*w = 1.0 - w1;			//reduce rounding errors
-	if(n == 3) {
-		*pw = pi6 * (asin(sqrt(*w)) - stqr);
-		return 0;
-		}
-	y = log(w1);
-	xx = log(an);
-	if(n <= 11) {
-		gamma = devlpl(g, 2, an);
-		if(y >= gamma) {
-			*pw = smal;		return 0;
-			}
-		y = -log(gamma - y);		m = devlpl(c3, 4, an);
-		s = exp(devlpl(c4, 4, an));
-		}
-	else {					//n >= 12
-		m = devlpl(c5, 4, xx);		s = exp(devlpl(c6, 3, xx));
-		}
-	//Censoring by proportion  NCENS/N
-	if(ncens > 0) {
-		ld = -log(delta);			bf = 1.0 + xx * bf1;
-		z90f = z90 + bf * pow(devlpl(c7, 2, pow(xx90, xx)), ld);
-		z95f = z95 + bf * pow(devlpl(c8, 2, pow(xx95, xx)), ld);
-		z99f = z99 + bf * pow(devlpl(c9, 2, xx), ld);
-		//Regress z90f ... z99f on normal deviates z90 ... z99
-		//   to get pseudo-mean and pseudo-sd of z as the slope and intercept 
-		zfm = (z90f + z95f + z99f)/3.0;
-		zsd = (z90 * (z90f - zfm) + z95 * (z95f - zfm) + z99 * (z99f - zfm)) / zss;
-		zbar = zfm - zsd * zm;		m += zbar * s;		s *= zsd;
-		}
-	*pw = 1.0 - norm_dist(y, m, s);
-	return 0;
-}
-
-void swilk1(int n, double *v0, double (*func)(double, double, double), double p1, double p2, 
-	bool bsorted, double *w, double *p)
-{
-	double *v, *a;
-
-	if(!n || !w || !p) return;			*w = *p = 1.0;
-	if(!(a = (double*)malloc(n *sizeof(double)))) return;
-	if(!bsorted && (v = (double*)memdup(v0, n*sizeof(double), 0)))SortArray(n, v);
-	else if(bsorted) v = v0;
-	else return;
-	if(do_swilk(func, p1, p2, v, n, n, n>>1, a, w, p)){
-		//an error occured
-		*w = *p = -1.0;
-		}
-	free(a);	if(v != v0) free(v);
-}
-
-//Kolmogorov-Smirnov's test and distribution of D
-// (1) Miller L. (1956) Journal of the American Statistical Association.  51: 111-121
-// (2) Mises R. (1964) Mathematical Theory of Probability and Statistics (New York: Academic Press)
-//     Chapters IX(C) and IX(E)
-// (3) Press W.H., Flannery B.P.,Teukolsky S.A., Vetterling W.T. (1988/1989)
-//     Numerical Recipes in C, Cambridge University Press, ISBN 0-521-35465-X, pp. 490 ff.
-//
-double ks_dist(int n, double d)
-{
-	double j, jn, sum, las, q, r, s, dn = (double)n;
-
-	las = floor(dn - dn * d);
-	for (j = sum = 0.0; j <= las; j += 1.0) {
-		jn = j / dn;							q = gammln(dn+1) - gammln(j+1) - gammln(dn-j+1.0);
-		r = (dn - j) * log( 1 - d - jn );		s = (j - 1.0) * log( d + jn );
-		sum += exp(q + r + s);
-		}
-	return(d*sum);
-}
-
-void KolSmir(int n, double *v0, double (*func)(double, double, double), double p1, double p2, 
-	bool bsorted, double *d, double *p)
-{
-	int i;
-	double *v, *dev, *x, ff, dt, dt1, dt2;
-	double dn = (double)n, f0 = 0.0;
-
-	if(!n || !d || !p) return;			*d = *p = 0.0;
-	if(!(dev = (double*)malloc(n*sizeof(double)))) return;
-	if(!(x = (double*)malloc(n*sizeof(double)))){
-		free(dev);						return;
-		}
-	if(!bsorted && (v = (double*)memdup(v0, n*sizeof(double), 0)))SortArray(n, v);
-	else if(bsorted) v = v0;
-	else return;
-	for(i = 0, *d = 0.0; i < n; i++) {
-		x[i] = (double)(i+1)/dn;		ff = (*func)(v[i], p1, p2); 
-		dt1 = fabs(f0-ff);				dt2 = fabs(dev[i] = (f0 = x[i])-ff);
-		dt = dt1 > dt2 ? dt1 : dt2;		if(dt > *d) *d = dt;
-		}
-	free(dev);	free(x);
-	*p = ks_dist(n, *d);
-	if(v != v0) free(v);
-}
-
-//---------------------------------------------------------------------------
-// Inverse of statitistical functions:
-// funcd supplies the function value fn and the derivative df of the function sf at x
-void funcd(double x, double *fn, double *df, double (*sf)(double, double, double), 
-		   double df1, double df2, double p)
-{
-	double y1, y2;
-
-	*fn = (sf)(x, df1, df2);
-	if(sf == norm_dist) *df = norm_freq(x, df1,df2);
-	else if(sf == chi_dist) *df = -chi_freq(x, df1);
-	else if(sf == t_dist) *df = -2.0 * t_freq(x, df1);
-	else if(sf == f_dist) *df = -1.0 * f_freq(x, df1, df2);
-	else if(sf == lognorm_dist) *df = lognorm_freq(x, df1, df2);
-	else if(sf == weib_dist) *df = weib_freq(x, df1, df2);
-	else if(sf == cauch_dist) *df = cauch_freq(x, df1, df2);
-	else if(sf == logis_dist) *df = logis_freq(x, df1, df2);
-	else {		//numerical differentiation
-		y1 = (sf)(x * 0.995, df1, df2);		y2 = (sf)(x * 1.005, df1, df2);
-		*df = (y2-y1)*100.0/x;
-		}
-	*fn = *fn - p;
-}
-
-//distinv does actual Newton-Raphson root finding
-double distinv(double (*sf)(double, double, double), double df1, double df2, double p, double x0)
-{
-	int i, j;
-	double df, df0, adf, dx, f, rtn;
-
-	for(j = 0, rtn = dx = x0; j < 200; j++) {
-		for(i = 0, df0 = 0.0; i < 20; i++) {
-			funcd(rtn, &f, &df, sf, df1, df2, p);
-			if((adf=fabs(df)) > 1.0e-12 || df0 > adf) break;
-			rtn += (dx = dx/2.0);				df0 = adf;
-			if(i >= 19) return HUGE_VAL;
-			}
-		dx = f/df*(0.01*(double)(100-j));		rtn -= dx;
-		if(fabs(dx) < _PREC && j > 3)return rtn; 
-		}
-	return HUGE_VAL;
-}
-
-//---------------------------------------------------------------------------
-//some statistical basics
-//do quartiles, median of data
-void d_quartile(int n, double *v, double *q1, double *q2, double *q3)
-{
-	int n2, n3;
-	double f1, f2;
-
-	if(!v || n<2) return;
-	SortArray(n, v);			n2 = n >> 1;
-	if(q1) {
-		n3 = n2 >> 1;
-		switch(n%4) {
-		case 3:		n3 ++;		f1 = 2.0;		f2 = 2.0;		break;
-		case 2:		n3 ++;		f1 = 3.0;		f2 = 1.0;		break;
-		case 1:		n3 ++;		f1 = 4.0;		f2 = 0.0;		break;
-		default:	f1 = 1.0;	f2 = 3.0;						break;
-			}
-		*q1 = (f1*v[n3-1] + f2*v[n3])/4.0;
-		}
-	if(q2) {
-		if(n & 1) *q2 = v[n2];
-		else *q2 = (v[n2-1] + v[n2])/2.0;
-		}
-	if(q3) {
-		n3 = n2 >> 1;
-		switch(n%4) {
-		case 3:		n3++;		f1 = 2.0;		f2 = 2.0;	break;
-		case 2:		f1 = 3.0;	f2 = 1.0;					break;
-		case 1:		f1 = 4.0;	f2 = 0.0;					break;
-		default:	f1 = 1.0;	f2 = 3.0;					break;
-			}
-		n3 += n2;
-		*q3 = (f2*v[n3-1] + f1*v[n3])/4.0;
-		}
-}
-
-// statistical basics partly based on
-// Davies, J. and Gogh, B. (2000), GSL-1.7 - The GNU scientific library
-//
-//do variance
-double d_variance(int n, double *v, double *mean, double *ss)
-{
-	int i;
-	double d, m, va, e;
-
-	for(i = 0, m = 0.0, d = 1.0; i < n; i++, d += 1.0) {
-		m += (v[i] - m)/d;
-		}
-	if (mean) *mean = m;
-	for(i = 0, va = 0.0, d = 1.0; i < n; i++, d += 1.0) {
-		e = v[i] - m;		va += (e * e - va)/d;
-		}
-	if (ss) *ss = va * (double)n;
-	return va * ((double)n/((double)(n-1)));
-}
-
-//do arithmethic mean
-double d_amean(int n, double *v)
-{
-	int i;
-	double d, mean;
-
-	for(i = 0, mean = 0.0, d = 1.0; i < n; i++, d += 1.0) {
-		mean += (v[i] - mean)/d;
-		}
-	return mean;
-}
-
-
-//do geometric mean
-double d_gmean(int n, double *v)
-{
-	int i;
-	double sum;
-
-	for(i = 0, sum = 0.0; i < n; i++) {
-		if(v[i] <= 0.0) return 0.0;
-		sum += log(v[i]);
-		}
-	return exp(sum/n);
-}
-
-//do harmonic mean
-double d_hmean(int n, double *v)
-{
-	int i;
-
-	double sum;
-
-	for(i = 0, sum = 0.0; i < n; i++) {
-		if(v[i] == 0.0) return 0.0;
-		sum += 1.0/(v[i]);
-		}
-	return (n/sum);
-}
-
-//kurtosis
-double d_kurt(int n, double *v)
-{
-	double sum, avg, sd, tmp, dn = n;
-	int i;
-
-	for(i = 0, sum = 0.0; i < n; i++) sum += v[i];
-	for(i = 0, avg = sum/dn, sum = 0.0; i < n; i++) sum += (tmp = v[i]-avg) * tmp;
-	for(i = 0, sd = sqrt(sum/(dn-1.0)), sum=0.0; i < n; i++) sum += ((tmp = (v[i]-avg)/sd)*tmp*tmp*tmp);
-	sum *= ((dn*(dn+1.0))/((dn-1.0)*(dn-2.0)*(dn-3.0)));
-	tmp = (3.0 * (dn-1.0) * (dn-1.0))/((dn-2.0)*(dn-3.0));
-	return sum - tmp;
-}
-
-//skewness
-double d_skew(int n, double *v)
-{
-	double sum, avg, sd, tmp, dn = n;
-	int i;
-
-	for(i = 0, avg = 0.0; i < n; i++) avg += ((v[i]-avg)/((double)(i+1)));
-	for(i = 0, sum = 0.0; i < n; i++) sum += (tmp = v[i]-avg) * tmp;
-	for(i = 0, sd = sqrt(sum/(dn-1.0)), sum=0.0; i < n; i++) sum += ((tmp = (v[i]-avg)/sd)*tmp*tmp);
-	return sum * dn/((dn-1.0)*(dn-2.0));
-}
-
-//---------------------------------------------------------------------------
-// Create a frequency distribution by counting the elements which may be 
-// assigned to a bin
-double d_classes(DataObj *d, double start, double step, double *v, int nv, char *range)
-{
-	int i, j, r, c, nc, *f;
-	AccRange *ar;
-
-	if(!range || !nv || !v || step <= 0.0 || !(ar = new AccRange(range))) return 0.0;
-	if(!(nc = ar->CountItems()) || !ar->GetFirst(&c, &r) || !(f=(int*)calloc(nc, sizeof(int)))) {
-		delete ar;				return 0.0;
-		}
-	for(i = 0; i < nv; i++) {
-		j = (int)(floor((v[i] - start)/step));
-		if(j < 0) j = 0;		if(j >= nc) j = (nc-1);
-		f[j]++;
-		}
-	for( ; nc > 0 && !(f[nc-1]); nc--);
-	for(i = 0; ar->GetNext(&c, &r) && i < nc; i++) {
-		d->SetValue(r, c, (double)f[i]);
-		}
-	free(f);					return ((double)nv);
-}
-
-//---------------------------------------------------------------------------
-// Pearsons linear correlation
-// (1) W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1989), 
-//    Numerical Rcipies in C. The Art of Scientific Computing, 
-//    Cambridge University Press, ISBN 0-521-35465, pp. 503 ff.
-// (2) B. Gough (2000), linear.c, gsl-1.7 the GNU scientific library
-double d_pearson(double *x, double *y, int n, char *dest, DataObj *data, double *ra)
-{
-	int j, r, c;
-	double yt, xt, t, df, res[4];
-	double syy=0.0, sxy=0.0, sxx=0.0, ay=0.0, ax=0.0;
-	AccRange *rD;
-
-
-	for(j = 0;	j < n; j++) {				// find means
-		ax += (x[j] - ax) / (j+1);			ay += (y[j] - ay) / (j+1);
-		}
-	for(j = 0; j < n; j++) {				// correlation
-		xt = x[j] - ax;						yt = y[j] - ay;
-		sxx += (xt*xt-sxx) / (j+1);			syy += (yt*yt-syy) / (j+1);
-		sxy += (xt*yt-sxy) / (j+1);
-		}
-	res[0] = sxy/sqrt(sxx*syy);				//pearsons r
-	if(dest || ra) {
-		res[1] = 0.5 * log((1.0+res[0]+_PREC)/(1.0-res[0]+_PREC));	//Fishers z-transform
-		df = n-2;
-		t = res[0]*sqrt(df/((1.0-res[0]+_PREC)*(1.0+res[0]+_PREC)));	//Student's t
-		res[2] = betai(0.5*df, 0.5, df/(df+t*t));					//probability
-		res[3] = n;
-		}
-	if((dest) && (data) && (rD = new AccRange(dest))) {
-		rD->GetFirst(&c, &r);
-		for(j = 0; j < 4 && rD->GetNext(&c, &r); j++) {
-			data->SetValue(r, c, res[j]);
-			}
-		data->Command(CMD_UPDATE, 0L, 0L);
-		delete rD;
-		}
-	if (ra){
-		memcpy(ra, res, 4 * sizeof(double));
-		}
-	return res[0];
-}
-
-//---------------------------------------------------------------------------
-// Given an array w, rank returns the rank of v1 in v
-// if v1 is not found in v 0 is returned
-double d_rank(int n, double *v, double v1)
-{
-	double *sv;
-	int i, j;
-
-	if(!n || !v) return 0.0;		if(n < 2) return 1.0;
-	if(!(sv = (double*)memdup(v, n * sizeof(double), 0))) return 0.0;
-	SortArray(n, sv);
-	for(i = j = 0; i < n; i++) {
-		if(v1 == sv[i]) {
-			for( ;(i+j)<n; j++) if(sv[i+j] > v1) break;
-			free(sv);				return (double)i + 1.0 + (((double)j-1.0)/2.0);
-			}
-		}
-	free(sv);						return 0.0;
-}
-
-//---------------------------------------------------------------------------
-// Spearman rank-order correlation
-// Ref.: W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1989), 
-//    Numerical Recipies in C. The Art of Scientific Computing, 
-//    Cambridge University Press, ISBN 0-521-35465, pp. 507 ff.
-
-//Given a sorted array w, crank replaces the elements by their rank
-void crank(int n, double *w0, double *s)
-{
-	int j=1, ji, jt;
-	double t, rank, *w = w0-1;
-
-	*s = 0.0;
-	while (j < n) {
-		if(w[j+1] != w[j]) {
-			w[j] = j;		++j;
-			}
-		else {
-			for(jt = j+1; jt <= n; jt++) if(w[jt] != w[j]) break;
-			rank = 0.5 * (j+jt-1);
-			for(ji = j; ji <= (jt-1); ji++) w[ji] = rank;
-			t = jt -j;		*s += t*t*t -t;				j = jt;
-			}
-		}
-	if(j == n) w[n] = n;
-}
-
-//the actual rank correlation
-double d_spearman(double *sx, double *sy, int n, char *dest, DataObj *data, double *ra)
-{
-	int j, r, c;
-	double *x, *y, vard, t, sg, sf, fac, en3n, en, df, aved, tmp;
-	double res[6];
-	AccRange *rD;
-
-	if(!(x = (double*)memdup(sx, n*sizeof(double), 0)) 
-		|| !(y = (double*)memdup(sy, n*sizeof(double), 0)))return 0.0;
-	SortArray2(n, x, y);			crank(n, x, &sf);
-	SortArray2(n, y, x);			crank(n, y, &sg);
-	for(j = 0, res[0] = 0.0; j < n; j++) res[0] += ((tmp = (x[j]-y[j]))*tmp);
-	en = n;						en3n = en*en*en -en;
-	aved = en3n/6.0 - (sf+sg)/12.0;
-	fac = (1.0-sf/en3n)*(1.0-sg/en3n);
-	vard = ((en-1.0)*en*en*((tmp = (en+1.0))*tmp)/36.0)*fac;
-	res[1] = (res[0]-aved)/sqrt(vard);
-	res[2] = errfc(fabs(res[1])/_SQRT2);
-	res[3] = (1.0-(6.0/en3n)*(res[0]+0.5*(sf+sg)))/fac;
-	t = res[3]*sqrt((en-2.0)/((res[3]+1.0)*(1.0-res[3])));
-	df = en-2.0;	res[5] = (double)n;
-    res[4] = betai(0.5*df, 0.5, df/(df+t*t));
-	if((dest) && (data) && (rD = new AccRange(dest))) {
-		rD->GetFirst(&c, &r);
-		for(j = 0; j < 6 && rD->GetNext(&c, &r); j++) {
-			data->SetValue(r, c, res[j]);
-			}
-		data->Command(CMD_UPDATE, 0L, 0L);
-		delete rD;
-		}
-	if(ra) {
-		memcpy(ra, res, 6 * sizeof(double));
-		}
-	free(x);						free(y);
-	return res[3];
-}
-
-//---------------------------------------------------------------------------
-// Kendal's non-parametric correlation
-// Ref.: W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1989), 
-//    Numerical Recipies in C. The Art of Scientific Computing, 
-//    Cambridge University Press, ISBN 0-521-35465, pp. 510 ff.
-
-double d_kendall(double *x, double *y, int n, char *dest, DataObj *data, double *ra)
-{
-	int j, k, n1, n2, is, r, c;
-	double aa, a1, a2, sv, res[4];
-	AccRange *rD;
-
-	for (j = n1 = n2 = is = 0; j < (n-1); j++) {
-		for(k = j+1; k < n; k++) {
-			a1 = x[j] - x[k];		a2 = y[j] - y[k];		aa = a1*a2;
-			if(aa != 0.0) {
-				n1++;				n2++;
-				if (aa > 0.0) is++;
-				else is--;
-				}
-			else {
-				if(a1 != 0.0) n1++;	if(a2 != 0.0) n2++;
-				}
-			}
-		}
-	res[0] = ((double)is)/(sqrt((double)n1) * sqrt((double)n2));
-	sv = (4.0 * ((double)n) + 10.0)/(9.0*((double)n)*((double)(n-1)));
-	res[1] = res[0]/sqrt(sv);	res[2] = errfc(fabs(res[1])/_SQRT2);
-	res[3] = n;			
-	if((dest) && (data) && (rD = new AccRange(dest))) {
-		rD->GetFirst(&c, &r);
-		for(j = 0; j < 4 && rD->GetNext(&c, &r); j++) {
-			data->SetValue(r, c, res[j]);
-			}
-		data->Command(CMD_UPDATE, 0L, 0L);
-		delete rD;
-		}
-	if (ra){
-		memcpy(ra, res, 4 * sizeof(double));
-		}
-	return res[0];
-}
-
-
-//linear regression
-double d_regression(double *x, double *y, int n, char *dest, DataObj *data, double *ra)
-{
-	double sx, sy, dx, dy, sxy, sxx, syy, sdy, df;
-	double res[10];		// slope, intercept, mean x, mean y, SE of slope, 
-						//   variance(x), variance(y), variance(fit), F of regression, significance
-	int i, j, r, c;
-	AccRange *rD;
-
-	if(n < 2) return 0.0;
-	for(i = 0, 	sx = sy = 0.0; i < n; i++) {
-		sx += x[i];			sy += y[i];
-		}
-	res[2] = sx /n;			res[3] = sy/n;
-	sxy = sxx = syy = 0.0;
-	for(i = 0; i < n; i++) {
-		dx = x[i]-res[2];	dy = y[i]-res[3];
-		sxx += (dx*dx);		syy += (dy*dy);		sxy += (dx*dy);
-		}
-	res[0] = sxy / sxx;		res[1] = res[3] - res[0] * res[2];
-	for(i = 0, sdy = 0.0; i < n; i++) {
-		dy = y[i] - (res[1] + x[i] *res[0]);
-		sdy += (dy * dy);
-		}
-	sdy = sdy/(n-2);		res[4] = sqrt(sdy/sxx);		df = (n-2);
-	res[5] = sxx/(n-1);		res[6] = syy/(n-1);			res[7] = sdy;
-	res[8] = sxy/sdy*sxy/sxx;
-	res[9] = betai(df/2.0, 0.5, df/(df+res[8]));
-	if((dest) && (data) && (rD = new AccRange(dest))) {
-		rD->GetFirst(&c, &r);
-		for(j = 0; j < 10 && rD->GetNext(&c, &r); j++) {
-			data->SetValue(r, c, res[j]);
-			}
-		data->Command(CMD_UPDATE, 0L, 0L);
-		delete rD;
-		}
-	if (ra)	memcpy(ra, res, 10 * sizeof(double));
-	return n;
-}
-
-//covariance
-double d_covar(double *x, double *y, int n, char *dest, DataObj *data)
-{
-	int i;
-	double sx, sy, dx, dy, sxy;
-
-	if(n < 2) return 0.0;
-	for(i = 0, 	sx = sy = 0.0; i < n; i++) {
-		sx += x[i];			sy += y[i];
-		}
-	sx /= n;		sy /= n;		sxy = 0.0;
-	for(i = 0; i < n; i++) {
-		dx = x[i]-sx;		dy = y[i]-sy;
-		sxy += (dx*dy - sxy) / (i+1);
-		}
-	return sxy;
-}
-
-//Mann-Whitney U Test
-double d_utest(double *x, double *y, int n1, int n2, char *dest, DataObj *data, double *ra)
-{
-	double *da, *ta, u1, u2, su, su1, ts, dn1 = n1, dn2 = n2;
-	double res[9];
-	AccRange *rD;
-	int i, j, n, r, c;
-
-	if(!x || !y || n1 < 2 || n2 < 2) return 0.0;
-	da = (double*)malloc((n = (n1+n2)) * sizeof(double));
-	ta = (double*)malloc(n * sizeof(double));
-	if(!da || !ta) {
-		if(da) free(da);	if(ta) free(ta); return 0.0;
-		}
-	for(i = 0; i < n1; i++) {
-		da[i] = x[i];		ta[i] = 1.0;
-		}
-	for(j = 0; j < n2; j++) {
-		da[i] = y[j];		ta[i++] = 2.0;
-		}
-	SortArray2(n, da, ta);	crank(n, da, &ts);
-	for(i = 0, res[0] = res[1] = 0.0; i < n; i++) {
-		if(ta[i] == 1.0) res[0] += da[i];
-		else res[1] += da[i];
-		}
-	free(da);										free(ta);
-	u1 = (dn1*dn2 + (dn1*(dn1+1))/2.0) - res[0];	u2 = (dn1*dn2 + ((dn2+1)*dn2)/2.0) - res[1];
-	su = sqrt((dn1*dn2*(dn1+dn2+1))/12.0);			res[2] = u2 > u1 ? u2 : u1;
-	su1 = ((dn1*dn2)/((dn1+dn2)*(dn1+dn2-1))) * (((dn1+dn2)*(dn1+dn2)*(dn1+dn2)-(dn1+dn2)-ts)/12.0);
-	su1 = sqrt(su1);
-	res[3] = (res[2] - (n1*n2)/2.0)/su;			res[6] = errfc(res[3]/_SQRT2);
-	res[4] = n1;								res[5] = n2;
-	res[7] = (res[2] - (n1*n2)/2.0)/su1;		res[8] = errfc(res[7]/_SQRT2);
-	if((dest) && (data) && (rD = new AccRange(dest))) {
-		rD->GetFirst(&c, &r);
-		for(i = 0; i < 9 && rD->GetNext(&c, &r); i++) {
-			data->SetValue(r, c, res[i]);
-			}
-		data->Command(CMD_UPDATE, 0L, 0L);
-		delete rD;
-		}
-	if (ra)	memcpy(ra, res, 9 * sizeof(double));
-	return res[8];
-}
-
-//t-test
-double d_ttest(double *x, double *y, int n1, int n2, char *dest, DataObj *data, double *results)
-{
-	int i, r, c;
-	double sx, sy, mx, my, d, df, p;
-	double res[9];			// mean1, SD1, n1, mean2, SD2, n2, p if variances equal,
-	AccRange *rD;			//    corrected df, corrected p
-
-	d_variance(n1, x, &mx, &sx);		d_variance(n2, y, &my, &sy);
-	d = ((sx+sy)/(n1+n2-2)) * ((double)(n1+n2)/(double)(n1*n2));
-	d = (mx-my)/sqrt(d);	//Student's t
-
-	//Welch's correction for differences in variance
-	df = (sx/(double)n1)*(sx/(double)n1)/(double)(n1+1)+(sy/(double)n2)*(sy/(double)n2)/(double)(n2+1);
-	df = (sx/(double)n1+sy/(double)n2)*(sx/(double)n1+sy/(double)n2)/df;
-	df -= 2.0;		df = floor(df);
-
-//	an alternative formula for correction
-//	p = (sx/(double)n1)*(sx/(double)n1)/(double)(n1-1) + (sy/(double)n2)*(sy/(double)n2)/(double)(n2-1);
-//	df = (sx/(double)n1 + sy/(double)n2) * (sx/(double)n1 + sy/(double)n2) / p;
-
-	p = betai(df/2.0, 0.5, (df/(df+d*d)));
-	if((dest) && (data) && (rD = new AccRange(dest))) {
-		res[0] = mx;	res[1] = sqrt(sx/(double)(n1-1));	res[2] = n1;
-		res[3] = my;	res[4] = sqrt(sy/(double)(n2-1));	res[5] = n2;
-		res[7] = df;	df = (n1-1) + (n2-1);	res[6] = betai(df/2.0, 0.5, (df/(df+d*d)));
-		res[8] = p;
-		rD->GetFirst(&c, &r);
-		for(i = 0; i < 9 && rD->GetNext(&c, &r); i++) {
-			data->SetValue(r, c, res[i]);
-			}
-		data->Command(CMD_UPDATE, 0L, 0L);
-		delete rD;
-		}
-	if(results) {
-		results[0] = mx;	results[1] = sqrt(sx/(double)(n1-1));	results[2] = n1;
-		results[3] = my;	results[4] = sqrt(sy/(double)(n2-1));	results[5] = n2;
-		results[7] = df;	df = (n1-1) + (n2-1);	results[6] = betai(df/2.0, 0.5, (df/(df+d*d)));
-		results[8] = p;		results[9] = d;
-		}
-	return p;
-}
-
-//t-test for paired samples
-double d_ttest2(double *x, double *y, int n, char *dest, DataObj *data, double *ra)
-{
-	double sx, sy, mx, my, df, cov, sd, t, p;
-	int i, r, c;
-	double res[6];			// mean1, SD1, mean2, SD2, n, p 
-	AccRange *rD;
-
-	d_variance(n, x, &mx, &sx);		d_variance(n, y, &my, &sy);
-	sx = d_variance(n, x, &mx);		sy = d_variance(n, y, &my);
-	cov = d_covar(x, y, n, 0L, 0L) * ((double)n/(double)(n-1));
-	sd = sqrt((sx+sy-2*cov)/n);
-	t = (mx-my)/sd;					df = (n-1);
-	p = betai(0.5*df, 0.5, df/(df+t*t));
-	res[0] = mx;	res[1] = sqrt(sx);	res[5] = p;
-	res[2] = my;	res[3] = sqrt(sy);	res[4] = n;
-	if((dest) && (data) && (rD = new AccRange(dest))) {
-		rD->GetFirst(&c, &r);
-		for(i = 0; i < 6 && rD->GetNext(&c, &r); i++) {
-			data->SetValue(r, c, res[i]);
-			}
-		data->Command(CMD_UPDATE, 0L, 0L);
-		delete rD;
-		}
-	if (ra)	memcpy(ra, res, 6 * sizeof(double));
-	return p;
-}
-
-//f-test
-double d_ftest(double *x, double *y, int n1, int n2, char *dest, DataObj *data, double *ra)
-{
-	int i, r, c;
-	double sx, sy, mx, my, d, df1, df2, p;
-	double res[6];			// mean1, SD1, n1, mean2, SD2, n2
-	AccRange *rD;
-
-	for(i=0, sx = 0.0; i < n1; sx += x[i], i++);				mx = sx/n1;
-	for(i=0, sy = 0.0; i < n2; sy += y[i], i++);				my = sy/n2;
-	for(i=0, sx = 0.0; i < n1; sx += ((d=(x[i]-mx))*d), i++);	sx /= (n1-1);
-	for(i=0, sy = 0.0; i < n2; sy += ((d=(y[i]-my))*d), i++);	sy /= (n2-1);
-	if(sx > sy) {
-		d = sx/sy;		df1 = n1-1;		df2 = n2-1;
-		}
-	else {
-		d = sy/sx;		df1 = n2-1;		df2 = n1-1;
-		}
-	p = 2.0 * betai(df2/2.0, df1/2.0, df2/(df2+df1*d));
-	if(p > 1.0) p = 2.0-p;
-	res[0] = mx;	res[1] = sqrt(sx);	res[2] = n1;
-	res[3] = my;	res[4] = sqrt(sy);	res[5] = n2;
-	if((dest) && (data) && (rD = new AccRange(dest))) {
-		rD->GetFirst(&c, &r);
-		for(i = 0; i < 6 && rD->GetNext(&c, &r); i++) {
-			data->SetValue(r, c, res[i]);
-			}
-		data->Command(CMD_UPDATE, 0L, 0L);
-		delete rD;
-		}
-	if (ra)	memcpy(ra, res, 6 * sizeof(double));
-	return p;
-}
-//---------------------------------------------------------------------------
-// Simple one way anova
-//---------------------------------------------------------------------------
-bool do_anova1(int n, int *nv, double **vals, double **res_tab, double *gm, double **means, double **ss)
-{
-	int i, j, ntot;
-	double tmp, *csums, *css, ssa, ssw, sst, mtot, d;
-
-	if(!(csums = (double*)calloc(n+1, sizeof(double)))
-		|| !(css = (double*)calloc(n+1, sizeof(double)))) return false;
-
-	for(i = ntot = 0, mtot = 0.0, d = 1.0; i< n; i++){
-		for(j = 0, csums[i] = 0.0, tmp = 1.0; j < nv[i]; j++, d+=1.0, tmp +=1.0) {
-			mtot += (vals[i][j] - mtot)/d;		
-			csums[i] += (vals[i][j] -csums[i])/tmp;
-			}
-		ntot += nv[i];
-		}
-	for(i = 0; i < n; i++) {
-		for(j = 0, css[i] = 0.0; j < nv[i]; j++) {
-			tmp = vals[i][j] - csums[i];	css[i] += (tmp*tmp);
-			}
-		}
-	for(i = 0, ssa = ssw = sst = 0.0;  i < n; i++) {
-		tmp =(csums[i] - mtot);		ssa += (tmp*tmp) * ((double)nv[i]);
-		ssw += css[i];
-		}
-	sst = ssa + ssw;
-	res_tab[0][0] = n - 1;				res_tab[1][0] = ntot - n;
-	res_tab[2][0] = ntot -1;			res_tab[0][1] = ssa;
-	res_tab[1][1] = ssw;				res_tab[2][1] = sst;
-	res_tab[0][2] = ssa/res_tab[0][0];	res_tab[1][2] = ssw/res_tab[1][0];
-	res_tab[0][3] = res_tab[0][2]/res_tab[1][2];
-	res_tab[0][4] = f_dist(res_tab[0][3], res_tab[0][0], res_tab[1][0]);
-	if(gm) *gm = mtot;
-	if(means) *means = csums;			else free(csums);
-	if(ss) *ss = css;					else free(css);
-	return true;
-}
-
-//---------------------------------------------------------------------------
-// Bartlett's Test for homogeneity of variances
-// RR Sokal & FJ Rohlf: Biometry, 3rd ed., pp. 398 ff.
-//---------------------------------------------------------------------------
-bool bartlett(int n, int *nc, double *ss, double *chi2)
-{
-	int i, sdf, df;
-	double mss, mlss, *lnss, cf;
-
-	if(!n || !nc || !ss || !chi2) return false;
-	if(!(lnss = (double*)malloc(n * sizeof(double))))return false;
-	for(i = sdf = 0, mss = mlss = cf = 0.0; i < n; i++) {
-		sdf += (df = nc[i]-1);				lnss[i] = log(ss[i]);
-		mss += (ss[i] * ((double)df));		mlss += (lnss[i] * ((double)df)); 
-		cf += (1.0/((double)df));
-		}
-	*chi2 = ((double)sdf) * log(mss/((double)sdf)) - mlss;
-	cf -= (1.0/((double)sdf));				cf = 1.0 + cf/(3.0 * ((double)(n-1)));
-	*chi2 /= cf;
-	// P = chi_dist(*chi2, n-1, 0);
-	free(lnss);		return true;
-}
-//---------------------------------------------------------------------------
-// Leven's Test for homogeneity of variances
-//---------------------------------------------------------------------------
-bool levene(int type, int n, int *nv, double *means, double **vals, double *F, double *P)
-{
-	int i, j;
-	bool bRet = false;
-	double cm, **res_tab, **cols;
-	
-	if(!n || !nv || !means || !vals) return false;
-	//setup matrix for results
-	if((res_tab = (double**)calloc(3, sizeof(double*)))
-		&& (res_tab[0] = (double*) malloc(5*sizeof(double)))
-		&& (res_tab[1] = (double*) malloc(5*sizeof(double)))
-		&& (res_tab[2] = (double*) malloc(5*sizeof(double)))
-		&& (cols = (double**)calloc(n+1, sizeof(double*)))) bRet = true;
-	//allocate mem for data
-	for(i = 0; bRet && i<n; i++) {
-		if(!(cols[i]=(double*)malloc((nv[i]+1)*sizeof(double)))) bRet = false;
-		}
-	//data are absolute differences to mean ...
-	for(i = 0, cm = 0.0; bRet && i < n; i++) {
-		switch(type) {
-			case 1:			//use means
-				cm = means[i];								break;
-			case 2:			//use medians
-				d_quartile(nv[i], vals[i], 0L, &cm, 0L);	break;
-			}
-		for(j = 0; j < nv[i]; j++) {
-			cols[i][j] = vals[i][j] > cm ? vals[i][j] - cm : cm - vals[i][j];
-			}
-		}
-	//Levene's test statistic is based on ANOVA of the differences
-	if(bRet && (bRet = do_anova1(n, nv, cols, res_tab, 0L, 0L, 0L))){
-		if(F) *F = res_tab[0][3];				if(P) *P = res_tab[0][4];
-		}
-	//clean up
-	if(bRet) {
-		for(i = 0; i < n; i++) if(cols[i]) free(cols[i]);
-		for(i = 0; i < 3; i++) if(res_tab[i]) free(res_tab[i]);
-		free(cols);								free(res_tab);	
-		}
-	return bRet;
-}
-
-//---------------------------------------------------------------------------
-// Modules from the R-project
-//
-//---------------------------------------------------------------------------
-#define M_1_SQRT_2PI	0.398942280401432677939946059934	/* 1/sqrt(2pi) */
-/*
- *  Copyright (C) 1998       Ross Ihaka
- *  Copyright (C) 2000--2005 The R Development Core Team
- *
- *  This program 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.
- *  This program 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- *  DESCRIPTION
- *    Computes the probability that the maximum of rr studentized
- *    ranges, each based on cc means and with df degrees of freedom
- *    for the standard error, is less than q.
- *    The algorithm is based on that of the reference.
- *
- *  REFERENCE
- *    Copenhaver, Margaret Diponzio & Holland, Burt S.
- *    Multiple comparisons of simple effects in
- *    the two-way analysis of variance with fixed effects.
- *    Journal of Statistical Computation and Simulation,
- *    Vol.30, pp.1-15, 1988.
- */
-
-double wprob(double w, double rr, double cc)
-{
-/*  wprob() :
-
-	This function calculates probability integral of Hartley's
-	form of the range.
-
-	w     = value of range
-	rr    = no. of rows or groups
-	cc    = no. of columns or treatments
-	ir    = error flag = 1 if pr_w probability > 1
-	pr_w = returned probability integral from (0, w)
-
-	program will not terminate if ir is raised.
-
-	bb = upper limit of legendre integration
-	iMax = maximum acceptable value of integral
-	nleg = order of legendre quadrature
-	ihalf = int ((nleg + 1) / 2)
-	wlar = value of range above which wincr1 intervals are used to
-	       calculate second part of integral,
-	       else wincr2 intervals are used.
-	C1, C2, C3 = values which are used as cutoffs for terminating
-           or modifying a calculation.
-	xleg = legendre 12-point nodes
-	aleg = legendre 12-point coefficients
- */
-#define nleg	12
-#define ihalf	6
-
-    /* looks like this is suboptimal for double precision.
-       (see how C1-C3 are used) <MM> */
-    /* const double iMax  = 1.; not used if = 1*/
-    const static double C1 = -30.0, C2 = -50.0, C3 = 60.;
-    const static double bb = 8.0, wlar = 3.0, wincr1 = 2.0, wincr2 = 3.;
-    const static double xleg[ihalf] = {	0.981560634246719250690549090149,
-	0.904117256370474856678465866119,	0.769902674194304687036893833213,
-	0.587317954286617447296702418941,	0.367831498998180193752691536644,
-	0.125233408511468915472441369464};
-    const static double aleg[ihalf] = {	0.047175336386511827194615961485,
-	0.106939325995318430960254718194,	0.160078328543346226334652529543,
-	0.203167426723065921749064455810,	0.233492536538354808760849898925,
-	0.249147045813402785000562436043};
-    double a, ac, pr_w, b, binc, blb, bub, c, cc1, einsum, elsum,
-		pminus, pplus, qexpo, qsqz, rinsum, wi, wincr, xx;
-    int j, jj;
-
-    qsqz = w * 0.5;
-
-    // if w >= 16 then the integral lower bound (occurs for c=20)
-    // is 0.99999999999995 so return a value of 1
-	if (qsqz >= bb)	return 1.0;
-
-	// find (f(w/2) - 1) ^ cc
-    // (first term in integral of hartley's form). 
-	pr_w = 2.0 * norm_dist(qsqz, 0.0, 1.0) -1.0;
-    // if pr_w ^ cc < 2e-22 then set pr_w = 0 
-    if (pr_w >= exp(C2 / cc)) pr_w = pow(pr_w, cc);
-    else pr_w = 0.0;
-    // if w is large then the second component of the
-    // integral is small, so fewer intervals are needed.
-    if (w > wlar) wincr = wincr1;
-    else wincr = wincr2;
-
-    /* find the integral of second term of hartley's form */
-    /* for the integral of the range for equal-length */
-    /* intervals using legendre quadrature.  limits of */
-    /* integration are from (w/2, 8).  two or three */
-    /* equal-length intervals are used. */
-    /* blb and bub are lower and upper limits of integration. */
-    blb = qsqz;			    binc = (bb - qsqz) / wincr;
-    bub = blb + binc;	    einsum = 0.0;
-
-    // integrate over each interval
-    cc1 = cc - 1.0;
-    for (wi = 1; wi <= wincr; wi++) {
-		elsum = 0.0;		a = 0.5 * (bub + blb);
-		// legendre quadrature with order = nleg
-		b = 0.5 * (bub - blb);
-		for (jj = 1; jj <= nleg; jj++) {
-			if (ihalf < jj) {
-				j = (nleg - jj) + 1;		xx = xleg[j-1];
-				}
-			else {
-				j = jj;						xx = -xleg[j-1];
-				}
-			c = b * xx;					    ac = a + c;
-			// if exp(-qexpo/2) < 9e-14, then doesn't contribute to integral
-			if ((qexpo = ac * ac) > C3) break;
-			pplus = 2.0 * norm_dist(ac, 0.0, 1.0);    pminus= 2.0 * norm_dist(ac, w, 1.0);
-			// if rinsum ^ (cc-1) < 9e-14, then doesn't contribute to integral
-			rinsum = (pplus * 0.5) - (pminus * 0.5);
-			if (rinsum >= exp(C1 / cc1)) {
-				rinsum = (aleg[j-1] * exp(-(0.5 * qexpo))) * pow(rinsum, cc1);
-				elsum += rinsum;
-				}
-			}
-		elsum *= (((2.0 * b) * cc) * M_1_SQRT_2PI);
-		einsum += elsum;		blb = bub;			bub += binc;
-		}
-	// if pr_w ^ rr < 9e-14, then return 0 */
-	pr_w = einsum + pr_w;
-	if (pr_w <= exp(C1 / rr))return 0.;
-    pr_w = pow(pr_w, rr);
- 	return pr_w < 1.0 ? pr_w : 1.0;
-}
-
-double ptukey(double q, double rr, double cc, double df, int lower_tail, int log_p)
-{
-/* 	q = value of studentized range
-	rr = no. of rows or groups
-	cc = no. of columns or treatments
-	df = degrees of freedom of error term
-	ir[0] = error flag = 1 if wprob probability > 1
-	ir[1] = error flag = 1 if qprob probability > 1
-
-	All references in wprob to Abramowitz and Stegun
-	are from the following reference:
-		Abramowitz, Milton and Stegun, Irene A.
-		Handbook of Mathematical Functions.
-		New York:  Dover publications, Inc. (1970).
-	All constants taken from this text are given to 25 significant digits.
-
-	nlegq = order of legendre quadrature
-	ihalfq = int ((nlegq + 1) / 2)
-	eps = max. allowable value of integral
-	eps1 & eps2 = values below which there is no contribution to integral.
-
-	d.f. <= dhaf:	integral is divided into ulen1 length intervals.  else
-	d.f. <= dquar:	integral is divided into ulen2 length intervals.  else
-	d.f. <= deigh:	integral is divided into ulen3 length intervals.  else
-	d.f. <= dlarg:	integral is divided into ulen4 length intervals.
-
-	d.f. > dlarg:	the range is used to calculate integral.
-
-	xlegq = legendre 16-point nodes
-	alegq = legendre 16-point coefficients
-
-	The coefficients and nodes for the legendre quadrature used in
-	qprob and wprob were calculated using the algorithms found in:
-		Stroud, A. H. and Secrest, D.,	Gaussian Quadrature Formulas.
-		Englewood Cliffs, New Jersey:  Prentice-Hall, Inc, 1966.
-
-	All values matched the tables (provided in same reference)
-	to 30 significant digits.
-
-	f(x) = .5 + erf(x / sqrt(2)) / 2      for x > 0
-	f(x) = erfc( -x / sqrt(2)) / 2	      for x < 0
-	where f(x) is standard normal c. d. f.
-
-	if degrees of freedom large, approximate integral with range distribution.
- */
-#define nlegq	16
-#define ihalfq	8
-
-/*  const double eps = 1.0; not used if = 1 */
-    const static double eps1 = -30.0, eps2 = 1.0e-14;
-    const static double dhaf  = 100.0, dquar = 800.0, deigh = 5000.0, dlarg = 25000.0;
-    const static double ulen1 = 1.0, ulen2 = 0.5, ulen3 = 0.25, ulen4 = 0.125;
-    const static double xlegq[ihalfq] = { 0.989400934991649932596154173450,
-	0.944575023073232576077988415535, 0.865631202387831743880467897712,
-	0.755404408355003033895101194847, 0.617876244402643748446671764049,
-	0.458016777657227386342419442984, 0.281603550779258913230460501460,
-	0.950125098376374401853193354250e-1};
-    const static double alegq[ihalfq] = {0.271524594117540948517805724560e-1,
-	0.622535239386478928628438369944e-1, 0.951585116824927848099251076022e-1,
-	0.124628971255533872052476282192, 0.149595988816576732081501730547,
-	0.169156519395002538189312079030, 0.182603415044923588866763667969,
-	0.189450610455068496285396723208};
-    double ans, f2, f21, f2lf, ff4, otsum, qsqz, rotsum, t1, twa1, ulen, wprb;
-    int i, j, jj;
-
-    if (df > dlarg)	return wprob(q, rr, cc);
-    f2 = df * 0.5;								// calculate leading constant
-    f2lf = ((f2 * log(df)) - (df * log(2.0))) - gammln(f2);
-    f21 = f2 - 1.0;
-    // integral is divided into unit, half-unit, quarter-unit, or eighth-unit length intervals 
-	//    depending on the value of the degrees of freedom.
-    ff4 = df * 0.25;
-    if	    (df <= dhaf)	ulen = ulen1;
-    else if (df <= dquar)	ulen = ulen2;
-    else if (df <= deigh)	ulen = ulen3;
-    else					ulen = ulen4;
-    f2lf += log(ulen);
-    for (i = 1, ans = 0.0; i <= 50; i++) {		// integrate over each subinterval
-		otsum = 0.0;
-		// legendre quadrature with order = nlegq, nodes (stored in xlegq) are symmetric around zero.
-		twa1 = (2 * i - 1) * ulen;
-		for (jj = 1; jj <= nlegq; jj++) {
-			if (ihalfq < jj) {
-				j = jj - ihalfq - 1;
-				t1 = (f2lf + (f21 * log(twa1 + (xlegq[j] * ulen)))) - (((xlegq[j] * ulen) + twa1) * ff4);
-				} 
-			else {
-				j = jj - 1;
-				t1 = (f2lf + (f21 * log(twa1 - (xlegq[j] * ulen)))) + (((xlegq[j] * ulen) - twa1) * ff4);
-				}
-			if (t1 >= eps1) {			// if exp(t1) < 9e-14, then doesn't contribute to integral 
-				if (ihalfq < jj) qsqz = q * sqrt(((xlegq[j] * ulen) + twa1) * 0.5);
-				else qsqz = q * sqrt(((-(xlegq[j] * ulen)) + twa1) * 0.5);
-				wprb = wprob(qsqz, rr, cc);		// call wprob to find integral of range portion
-				rotsum = (wprb * alegq[j]) * exp(t1);			otsum += rotsum;
-				}
-			}									// end legendre integral for interval i
-		// If integral for interval i < 1e-14, then stop. However, in order to avoid small area 
-		//    under left tail, at least  1 / ulen  intervals are calculated.
-		if (i * ulen >= 1.0 && otsum <= eps2) break;
-		ans += otsum;							//end of interval i 
-		}
-	return ans > 1.0 ? 1.0 : ans;
- }
-
- /*
- *  Copyright (C) 1998 	     Ross Ihaka
- *  Copyright (C) 2000--2005 The R Development Core Team
- *  based in part on AS70 (C) 1974 Royal Statistical Society
- *
- *  This program 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.
- *  This program 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- *  DESCRIPTION
- *	Computes the quantiles of the maximum of rr studentized
- *	ranges, each based on cc means and with df degrees of freedom
- *	for the standard error, is less than q.
- *	The algorithm is based on that of the reference.
- *
- *  REFERENCE
- *	Copenhaver, Margaret Diponzio & Holland, Burt S., Multiple comparisons of simple
- *	effects in the two-way analysis of variance with fixed effects.
- *	Journal of Statistical Computation and Simulation, Vol.30, pp.1-15, 1988.
- */
-
-/* qinv() :
- *	this function finds percentage point of the studentized range
- *	which is used as initial estimate for the secant method.
- *	function is adapted from portion of algorithm as 70
- *	from applied statistics (1974) ,vol. 23, no. 1
- *	by odeh, r. e. and evans, j. o.
- *	  p = percentage point
- *	  c = no. of columns or treatments
- *	  v = degrees of freedom
- *	  qinv = returned initial estimate
- *	vmax is cutoff above which degrees of freedom
- *	is treated as infinity.
- */
-
-static double qinv(double p, double c, double v)
-{
-    const static double p0 = 0.322232421088, q0 = 0.993484626060e-01;
-    const static double p1 = -1.0, q1 = 0.588581570495;
-    const static double p2 = -0.342242088547, q2 = 0.531103462366;
-    const static double p3 = -0.204231210125, q3 = 0.103537752850;
-    const static double p4 = -0.453642210148e-04, q4 = 0.38560700634e-02;
-    const static double c1 = 0.8832, c2 = 0.2368, c3 = 1.214, c4 = 1.208, c5 = 1.4142;
-    const static double vmax = 120.0;
-    double ps, q, t, yi;
-
-    ps = 0.5 - 0.5 * p;
-    yi = sqrt (log (1.0 / (ps * ps)));
-    t = yi + (((( yi * p4 + p3) * yi + p2) * yi + p1) * yi + p0)
-	   / (((( yi * q4 + q3) * yi + q2) * yi + q1) * yi + q0);
-    if (v < vmax) t += (t * t * t + t) / v / 4.0;
-    q = c1 - c2 * t;
-    if (v < vmax) q += -c3 / v + c4 * t / v;
-    return t * (q * log (c - 1.0) + c5);
-}
-
-/*
- *  Copenhaver, Margaret Diponzio & Holland, Burt S.
- *  Multiple comparisons of simple effects in
- *  the two-way analysis of variance with fixed effects.
- *  Journal of Statistical Computation and Simulation,
- *  Vol.30, pp.1-15, 1988.
- *
- *  Uses the secant method to find critical values.
- *
- *  p = confidence level (1 - alpha)
- *  rr = no. of rows or groups
- *  cc = no. of columns or treatments
- *  df = degrees of freedom of error term
- *
- */
-double qtukey(double p, double rr, double cc, double df, int lower_tail, int log_p)
-{
-    const int maxiter = 50;
-    double ans = HUGE_VAL, valx0, valx1, x0, x1;
-    int iter;
-
-    // df must be > 1 ; there must be at least two values 
-	if(p >= 1.0 || df < 2 || rr < 1 || cc < 2) return HUGE_VAL;
-	if(p < 0.0) p = 0.0;
-    x0 = qinv(p, cc, df);									// Initial value
-    valx0 = ptukey(x0, rr, cc, df, true, false) - p;		// Find prob(value < x0)
-    // Find the second iterate and prob(value < x1). If the first iterate has probability value 
-    // exceeding p then second iterate is 1 less than first iterate; otherwise it is 1 greater.
-	x1 = valx0 > 0.0 ? (x1 = x0 > 1.0 ? x0-1.0 : 0.0) : (x0 + 1.0);
-    valx1 = ptukey(x1, rr, cc, df, true, false) - p;
-    for(iter=1; iter < maxiter ; iter++) {					// Iterate
-		ans = x1 - ((valx1 * (x1 - x0)) / (valx1 - valx0));
-		valx0 = valx1;		x0 = x1;
-		if (ans < 0.0) {									// New iterate must be >= 0
-			ans = 0.0;			valx1 = -p;
-			}
-		valx1 = ptukey(ans, rr, cc, df, true, false) - p;	//  Find prob(value < new iterate)
-		x1 = ans;
-		if (fabs(x1 - x0) < _PREC)	return ans;				// Convergence ?
-		}
-    //The process did not converge in 'maxiter' iterations 
-    return ans;
-}
-//---------------------------------------------------------------------------
-// END Modules from the R-project
-
-
-//---------------------------------------------------------------------------
-// Calendar, Date- and Time functions
-// The following characters are used as format specifiers in a format string,
-//    all other characters are either ignored or copyied to the output
-//
-//    Y   four digits year               y    two digits year
-//    X   month's full name              x    three character month name
-//    Z   two digits day of month        z    same as Z but no leading zero
-//    V   two digit month number         v    number of month
-//    W   single letter month
-//    D   full name of day               d    three characters for day name
-//    E   two digits weekday             e    one or two digits weekday
-//    F   single character day name
-//    H   two digits for hours           h    hours with no leading zero
-//    M   two digits for minutes         m    minutes with no leading zero
-//    S   two digits for seconds         s    seconds with no leading zero
-//    T   two digits seconds, two dec.   t    same as T but no leading zero
-//    U   full precision seconds
-
-static char *dt_month[] = {"January", "February", "March", "April", "May", "June",
-	"July", "August", "September", "October", "November", "December"};
-
-static char *dt_months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
-	"Sep", "Oct", "Nov", "Dec"};
-
-static int dt_monthl[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
-
-static char *dt_day[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
-	"Friday", "Saturday"};
-
-static char *dt_days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
-
-static bool leapyear(int year) {
-	return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
-}
-
-int year2aday(int y)
-{
-	int aday, y1;
-
-	y1 = y - 1900;
-	aday = y1 * 365;
-	aday += ((y1-1) >> 2 );
-	aday -= (y1 / 100);
-	aday += ((y/400)-4);
-	return aday;
-}
-
-static void set_dow(rlp_datetime *dt)
-{
-	dt->dow = (dt->aday %7)+1;
-}
-
-void add_date(rlp_datetime *base, rlp_datetime *inc)
-{
-	int i, dom;
-
-	if(base) {
-		if(base->month < 1) base->month = 1;
-		if(inc) {
-			base->seconds += inc->seconds;
-			if(base->seconds >= 60.0) {
-				base->minutes++;		base->seconds -= 60.0;
-				}
-			base->minutes += inc->minutes;
-			if(base->minutes >= 60) {
-				base->hours++;			base->minutes -= 60;
-				}
-			base->hours += inc->hours;
-			if(base->hours >= 24) {
-				base->dom++;			base->hours -= 24;
-				}
-			base->year += inc->year;	base->dom += inc->dom;
-			base->month += inc->month;
-			}
-		dom = dt_monthl[base->month-1];
-		if(leapyear(base->year) && base->month == 2) dom = 29;
-		if(base->dom > dom) {
-			base->month++;			base->dom -= dom;
-			}
-		if(base->month > 12) {
-			base->year++;			base->month -= 12;
-			}
-		base->aday = year2aday(base->year);
-		for(i = base->doy = 0; i < (base->month-1); i++) {
-			dom = dt_monthl[i];
-			if(i == 1 && leapyear(base->year)) dom = 29;
-			base->doy += dom;
-			}
-		base->doy += base->dom;
-		base->aday += base->doy;	set_dow(base);
-		}
-}
-
-static int parse_date (rlp_datetime *dt, char *src, char *fmt)
-{
-	int i, j, k;
-	char tmp_str[10];
-
-	if(!src || !src[0] || !fmt || !fmt[0]) return 0;
-	if(*src == '\'') src++;
-	for(i = j = 0; fmt[i] && src[j]; i++) {
-		switch (fmt[i]) {
-		case 'Y':		case 'y':			// year is numeric
-			if(j && src[j] == '-' || src[j] == '/' || src[j] == '.') j++;
-#ifdef USE_WIN_SECURE
-			if(sscanf_s(src+j, "%d", &dt->year)) {
-#else
-			if(sscanf(src+j, "%d", &dt->year)) {
-#endif
-				if(dt->year < 0) return 0;
-				while(isdigit(src[j])) j++;
-				if(dt->year<60) dt->year += 2000;
-				else if(dt->year <99) dt->year += 1900;
-				}
-			else return 0;
-			break;
-		case 'X':		case 'x':			// month can be text
-			if(j && src[j] == '-' || src[j] == '/' || src[j] == '.') j++;
-			tmp_str[0] = toupper(src[j]);
-			tmp_str[1] = tolower(src[j+1]);
-			tmp_str[2] = tolower(src[j+2]);
-			tmp_str[3] = 0;
-			for(k = dt->month = 0; k < 12; k++) {
-				if(0 == strcmp(tmp_str,dt_months[k])) {
-					dt->month = k+1;			break;
-					}
-				}
-			if(dt->month) while(isalpha(src[j])) j++;
-			else return 0;
-			break;
-		case 'V':		case 'v':			//    or numeric
-			if(j && src[j] == '-' || src[j] == '/' || src[j] == '.') j++;
-#ifdef USE_WIN_SECURE
-			if(sscanf_s(src+j, "%d", &dt->month)) {
-#else
-			if(sscanf(src+j, "%d", &dt->month)) {
-#endif
-				if(dt->month <= 0 || dt->month > 12) return 0;
-				j++;				if(isdigit(src[j])) j++;
-				}
-			else return 0;
-			break;
-		case 'Z':		case 'z':			// day of month is numeric
-			if(j && src[j] == '-' || src[j] == '/' || src[j] == '.') j++;
-#ifdef USE_WIN_SECURE
-			if(sscanf_s(src+j, "%d", &dt->dom)) {
-#else
-			if(sscanf(src+j, "%d", &dt->dom)) {
-#endif
-				if(dt->dom <= 0 || dt->dom > 31) return 0;
-				j++;				if(isdigit(src[j])) j++;
-				}
-			else return 0;
-			break;
-		case 'H':		case 'h':			// hours are numeric
-#ifdef USE_WIN_SECURE
-			if(sscanf_s(src+j, "%2d", &dt->hours)) {
-#else
-			if(sscanf(src+j, "%2d", &dt->hours)) {
-#endif
-				if(dt->hours < 0 || dt->hours > 23) return 0;
-				j++;				if(isdigit(src[j])) j++;
-				}
-			else return 0;
-			break;
-		case 'M':		case 'm':			// minutes are numeric
-			if(j && src[j] == ' ' || src[j] == ':') j++;
-#ifdef USE_WIN_SECURE
-			if(sscanf_s(src+j, "%2d", &dt->minutes)) {
-#else
-			if(sscanf(src+j, "%2d", &dt->minutes)) {
-#endif
-				if(dt->minutes < 0 || dt->minutes >= 60) return 0;
-				j++;				if(isdigit(src[j])) j++;
-				}
-			else return 0;
-			break;
-		case 'S':		case 's':			// seconds are numeric
-		case 'T':		case 't':
-			if(j && src[j] == ' ' || src[j] == ':') j++;
-#ifdef USE_WIN_SECURE
-			if(sscanf_s(src+j, "%lf", &dt->seconds)) {
-#else
-			if(sscanf(src+j, "%lf", &dt->seconds)) {
-#endif
-				if(dt->seconds < 0.0 || dt->seconds >= 60.0) return 0;
-				while(isdigit(src[j]) || src[j] == '.') j++;
-				}
-			else return 0;
-			dt->seconds += 1.0e-12;
-			break;
-		default:
-			if(fmt[i] && fmt[i] == src[j]) j++;
-			}
-		}
-	if(dt->year && dt->month && dt->dom) {
-		for(dt->doy = 0, i = dt->month-2; i >= 0; i--) {
-			if(i == 1) dt->doy += leapyear(dt->year) ? 29 : 28;
-			else dt->doy += dt_monthl[i]; 
-			}
-		dt->doy += dt->dom;
-		if(dt->year >= 1900) dt->aday = year2aday(dt->year);
-		dt->aday += dt->doy;
-		}
-	return j;
-}
-
-char *date2text(rlp_datetime *dt, char *fmt)
-{
-	static char res[80];
-	int i, pos;
-	double secs;
-
-	res[0] = 0;
-	if(!fmt || !fmt[0] || !dt) return res;
-	set_dow(dt);
-	secs = dt->seconds;
-	if (secs > 59.4999) secs = 59.4999;
-	for(pos = i = 0; fmt[i] && pos < 70; i++) {
-#ifdef USE_WIN_SECURE
-		switch(fmt[i]) {
-		case 'Y':
-			if(dt->year) pos+=sprintf_s(res+pos, 80-pos, "%4d", dt->year);
-			else pos += sprintf_s(res+pos, 80-pos, "####");			break;
-		case 'y':
-			if(dt->year) pos+=sprintf_s(res+pos, 80-pos, "%02d", (dt->year %100));
-			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
-		case 'Z':
-			if(dt->dom) pos+=sprintf_s(res+pos, 80-pos, "%02d", dt->dom);
-			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
-		case 'z':
-			if(dt->dom) pos+=sprintf_s(res+pos, 80-pos, "%d", dt->dom);
-			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
-		case 'X':
-			if(dt->month >0 && dt->month < 13) pos+=sprintf_s(res+pos, 80-pos, "%s", dt_month[dt->month-1]);
-			else pos += sprintf_s(res+pos, 80-pos, "###");			break;
-		case 'x':
-			if(dt->month >0 && dt->month < 13) pos+=sprintf_s(res+pos, 80-pos, "%s", dt_months[dt->month-1]);
-			else pos += sprintf_s(res+pos, 80-pos, "###");			break;
-		case 'V':
-			if(dt->month >0 && dt->month < 13) pos+=sprintf_s(res+pos, 80-pos, "%02d", dt->month);
-			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
-		case 'v':
-			if(dt->month >0 && dt->month < 13) pos+=sprintf_s(res+pos, 80-pos, "%d", dt->month);
-			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
-		case 'W':
-			if(dt->month >0 && dt->month < 13) pos+=sprintf_s(res+pos, 80-pos, "%c", dt_month[dt->month-1][0]);
-			else pos += sprintf_s(res+pos, 80-pos, "#");			break;
-		case 'D':
-			if(dt->dow >0 && dt->dow < 8) pos+=sprintf_s(res+pos, 80-pos, "%s", dt_day[dt->dow-1]);
-			else pos += sprintf_s(res+pos, 80-pos, "###");			break;
-		case 'd':
-			if(dt->dow >0 && dt->dow < 8) pos+=sprintf_s(res+pos, 80-pos, "%s", dt_days[dt->dow-1]);
-			else pos += sprintf_s(res+pos, 80-pos, "###");			break;
-		case 'E':
-			if(dt->dow >0 && dt->dow < 8) pos+=sprintf_s(res+pos, 80-pos, "%02d", dt->dow);
-			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
-		case 'e':
-			if(dt->dow >0 && dt->dow < 8) pos+=sprintf_s(res+pos, 80-pos, "%d", dt->dow);
-			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
-		case 'F':
-			if(dt->dow >0 && dt->dow < 8) pos+=sprintf_s(res+pos, 80-pos, "%c", dt_day[dt->dow-1][0]);
-			else pos += sprintf_s(res+pos, 80-pos, "#");			break;
-		case 'H':
-			if(dt->hours >=0 && dt->hours < 24) pos+=sprintf_s(res+pos, 80-pos, "%02d", dt->hours);
-			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
-		case 'h':
-			if(dt->hours >=0 && dt->hours < 24) pos+=sprintf_s(res+pos, 80-pos, "%d", dt->hours);
-			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
-		case 'M':
-			if(dt->minutes >=0 && dt->minutes < 60) pos+=sprintf_s(res+pos, 80-pos, "%02d", dt->minutes);
-			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
-		case 'm':
-			if(dt->minutes >=0 && dt->minutes < 60) pos+=sprintf_s(res+pos, 80-pos, "%d", dt->minutes);
-			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
-		case 'S':
-			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf_s(res+pos, 80-pos, "%02d", iround(secs));
-			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
-		case 's':
-			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf_s(res+pos, 80-pos, "%d", iround(secs));
-			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
-		case 'T':
-			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf_s(res+pos, 80-pos, "%02.2lf", dt->seconds);
-			else pos += sprintf_s(res+pos, 80-pos, "##.##");		break;
-		case 't':
-			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf_s(res+pos, 80-pos, "%.2lf", dt->seconds);
-			else pos += sprintf_s(res+pos, 80-pos, "##.##");		break;
-		default:
-			pos += sprintf_s(res+pos, 80-pos, "%c", fmt[i]);		break;
-			}
-#else
-		switch(fmt[i]) {
-		case 'Y':
-			if(dt->year) pos+=sprintf(res+pos, "%4d", dt->year);
-			else pos += sprintf(res+pos, "####");		break;
-		case 'y':
-			if(dt->year) pos+=sprintf(res+pos, "%02d", (dt->year %100));
-			else pos += sprintf(res+pos, "##");			break;
-		case 'Z':
-			if(dt->dom) pos+=sprintf(res+pos, "%02d", dt->dom);
-			else pos += sprintf(res+pos, "##");			break;
-		case 'z':
-			if(dt->dom) pos+=sprintf(res+pos, "%d", dt->dom);
-			else pos += sprintf(res+pos, "##");			break;
-		case 'X':
-			if(dt->month >0 && dt->month < 13) pos+=sprintf(res+pos, "%s", dt_month[dt->month-1]);
-			else pos += sprintf(res+pos, "###");		break;
-		case 'x':
-			if(dt->month >0 && dt->month < 13) pos+=sprintf(res+pos, "%s", dt_months[dt->month-1]);
-			else pos += sprintf(res+pos, "###");		break;
-		case 'V':
-			if(dt->month >0 && dt->month < 13) pos+=sprintf(res+pos, "%02d", dt->month);
-			else pos += sprintf(res+pos, "##");			break;
-		case 'v':
-			if(dt->month >0 && dt->month < 13) pos+=sprintf(res+pos, "%d", dt->month);
-			else pos += sprintf(res+pos, "##");			break;
-		case 'W':
-			if(dt->month >0 && dt->month < 13) pos+=sprintf(res+pos, "%c", dt_month[dt->month-1][0]);
-			else pos += sprintf(res+pos, "#");			break;
-		case 'D':
-			if(dt->dow >0 && dt->dow < 8) pos+=sprintf(res+pos, "%s", dt_day[dt->dow-1]);
-			else pos += sprintf(res+pos, "###");		break;
-		case 'd':
-			if(dt->dow >0 && dt->dow < 8) pos+=sprintf(res+pos, "%s", dt_days[dt->dow-1]);
-			else pos += sprintf(res+pos, "###");		break;
-		case 'E':
-			if(dt->dow >0 && dt->dow < 8) pos+=sprintf(res+pos, "%02d", dt->dow);
-			else pos += sprintf(res+pos, "##");			break;
-		case 'e':
-			if(dt->dow >0 && dt->dow < 8) pos+=sprintf(res+pos, "%d", dt->dow);
-			else pos += sprintf(res+pos, "##");			break;
-		case 'F':
-			if(dt->dow >0 && dt->dow < 8) pos+=sprintf(res+pos, "%c", dt_day[dt->dow-1][0]);
-			else pos += sprintf(res+pos, "#");			break;
-		case 'H':
-			if(dt->hours >=0 && dt->hours < 24) pos+=sprintf(res+pos, "%02d", dt->hours);
-			else pos += sprintf(res+pos, "##");			break;
-		case 'h':
-			if(dt->hours >=0 && dt->hours < 24) pos+=sprintf(res+pos, "%d", dt->hours);
-			else pos += sprintf(res+pos, "##");			break;
-		case 'M':
-			if(dt->minutes >=0 && dt->minutes < 60) pos+=sprintf(res+pos, "%02d", dt->minutes);
-			else pos += sprintf(res+pos, "##");			break;
-		case 'm':
-			if(dt->minutes >=0 && dt->minutes < 60) pos+=sprintf(res+pos, "%d", dt->minutes);
-			else pos += sprintf(res+pos, "##");			break;
-		case 'S':
-			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf(res+pos, "%02d", iround(secs));
-			else pos += sprintf(res+pos, "##");			break;
-		case 's':
-			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf(res+pos, "%d", iround(secs));
-			else pos += sprintf(res+pos, "##");			break;
-		case 'T':
-			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf(res+pos, "%02.2lf", dt->seconds);
-			else pos += sprintf(res+pos, "##.##");		break;
-		case 't':
-			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf(res+pos, "%.2lf", dt->seconds);
-			else pos += sprintf(res+pos, "##.##");		break;
-		default:
-			pos += sprintf(res+pos, "%c", fmt[i]);		break;
-			}
-#endif
-	}
-	res[pos] = 0;
-	return res;
-}
-
-double date2value(rlp_datetime *dt)
-{
-	double res;
-
-	if(!dt) return 0.0;
-
-	res = dt->seconds/60.0 + (double)dt->minutes;
-	res = res/60.0 + (double)dt->hours;
-	res = res/24.0 + (double)dt->aday;
-	return res;
-}
-
-void parse_datevalue(rlp_datetime *dt, double dv)
-{
-	int i, j, d;
-
-	if(!dt || dv < 0.0) return;
-	if(dv > 1.0) {
-		dt->aday = (int)floor(dv);
-		dt->year = (int)(dv/365.2425);
-		d = (int)floor(dv);
-		do {
-			dt->doy = d - 365*dt->year;
-			dt->doy -= ((dt->year-1)>>2);
-			dt->doy += ((dt->year)/100);
-			dt->doy -= ((dt->year+300)/400);
-			if(dt->doy < 1) dt->year--;
-			}while(dt->doy < 1);
-		dt->year += 1900;
-		for(i = dt->month = 0, d = dt->doy; i < 12 && d > 0; i++) {
-			if(i == 1 && d > (j = (leapyear(dt->year)) ? 29 : 28)) d -= j;
-			else if(i != 1 && d > dt_monthl[i]) d -= dt_monthl[i];
-			else break;
-			}
-		dt->month = i+1;				dt->dom = d;
-		}
-	dv -= floor(dv);				dv *= 24.0;
-	dt->hours = (int)floor(dv);		dv -= floor(dv);
-	dv *= 60.0;						dt->minutes = (int)floor(dv); 
-	dv -= floor(dv);				dt->seconds = dv *60.0 + 1.0e-12; 
-	if(dt->seconds > 59.9999) {
-		dt->seconds = 0.0;			dt->minutes++;
-		if(dt->minutes == 60) {
-			dt->hours++;			dt->minutes = 0;
-			}
-		}
-}
-
-static char *dt_popfmt[] = {"Z.V.Y H:M:S", "Z/V/Y H:M:S", "Z-V-Y H:M:S", "Z.X.Y H:M:S",
-	"Y.V.Z H:M:S", "Y-X-Z H:M:S", "H:M:S", 0L};
-
-bool date_value(char *desc, char *fmt, double *value)
-{
-	int i;
-	rlp_datetime dt;
-
-	dt.year = dt.aday = dt.doy = dt.month = dt.dom = dt.dow = dt.hours = dt.minutes = 0;
-	dt.seconds = 0.0;
-	if(!value || !desc || !desc[0]) return false;
-	if(fmt && fmt[0]) {
-		if(parse_date(&dt, desc, fmt)) {
-			*value = date2value(&dt);	return true;
-			}
-		}
-	else {
-		if(parse_date(&dt, desc, defs.fmt_datetime)) {
-			*value = date2value(&dt);	return true;
-			}
-		}
-	for(i=0; dt_popfmt[i]; i++) {
-		if(parse_date(&dt, desc, dt_popfmt[i])) {
-			*value = date2value(&dt);	return true;
-			}
-		}
-	return false;
-}
-
-char *value_date(double dv, char *fmt)
-{
-	rlp_datetime dt;
-
-	parse_datevalue(&dt, dv);
-	return date2text(&dt, fmt ? fmt : defs.fmt_date);
-}
-
-double now_today()
-{
-	double res = 0.0;
-	time_t ti = time(0L);
-#ifdef USE_WIN_SECURE
-	char dtbuff[80];
-
-	ctime_s(dtbuff, 80, &ti);
-	date_value(dtbuff+4, "x z H:M:S Y", &res);
-#else
-	date_value(ctime(&ti)+4, "x z H:M:S Y", &res);
-#endif
-	return res;
-}
-
-void split_date(double dv, int *y, int *mo, int *dom, int *dow, int *doy, int *h, int *m, double *s)
-{
-	rlp_datetime dt;
-
-	parse_datevalue(&dt, dv);
-	set_dow(&dt);
-	if(y) *y = dt.year;				if(mo) *mo = dt.month;
-	if(dom) *dom = dt.dom;			if(dow) *dow = dt.dow;
-	if(doy) *doy = dt.doy;			if(h) *h = dt.hours;
-	if(m) *m = dt.minutes;			if(s) *s = dt.seconds;
-}
-
-//---------------------------------------------------------------------------
-// Use the Delauney triangulation to create a 3D mesh of dispersed data
-//
-Triangle* Triangulate1(char *xr, char *yr, char *zr, DataObj *data)
-{
-	AccRange *rX, *rY, *rZ;
-	int i, j, n, rx, cx, ry, cy, rz, cz;
-	double zMin;
-	fPOINT3D *da;
-	fRECT lim;
-	Triangle *trl, *trn;
-	Triangulate *tria;
-
-	rX = rY = rZ = 0L;				trl = trn  = 0L;
-	if((rX = new AccRange(xr)) && (rY = new AccRange(yr)) && (rZ = new AccRange(zr))
-		&& rX->GetFirst(&cx, &rx) && rY->GetFirst(&cy, &ry) && rZ->GetFirst(&cz, &rz)
-		&& (n = rX->CountItems()) && (da = (fPOINT3D*)malloc(n * sizeof(fPOINT3D)))
-		&& (trl = new Triangle()) && (trn = new Triangle())) {
-		//get minima and maxima
-		for(i = j = 0; i < n; i++) {
-			if(rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry) && rZ->GetNext(&cz, &rz)) {
-				data->GetValue(rx, cx, &da[j].fx);	data->GetValue(ry, cy, &da[j].fy);
-				data->GetValue(rz, cz, &da[j].fz);	j++;
-				}
-			}
-		if(!j) {
-			free(da); delete rX;	delete rY;	delete rZ;	return trl;
-			}
-		for(i = 0, j = n; i < n; i++) {
-			if(i) {
-				if(da[i].fx < lim.Xmin) lim.Xmin = da[i].fx;	if(da[i].fx > lim.Xmax) lim.Xmax = da[i].fx;
-				if(da[i].fy < lim.Ymin) lim.Ymin = da[i].fy;	if(da[i].fy > lim.Ymax) lim.Ymax = da[i].fy;
-				if(da[i].fz < zMin) zMin = da[i].fz;
-				}
-			else {
-				lim.Xmax = lim.Xmin = da[i].fx;		lim.Ymax = lim.Ymin = da[i].fy;		zMin = da[i].fz;
-				}
-			}
-		//setup two super triangles
-		trl->pt[0].fz = trl->pt[1].fz = trl->pt[2].fz = zMin;
-		trn->pt[0].fz = trn->pt[1].fz = trn->pt[2].fz = zMin;
-		trl->pt[0].fx = trn->pt[0].fx = trl->pt[2].fx = lim.Xmin;
-		trl->pt[0].fy = trn->pt[0].fy = trn->pt[1].fy = lim.Ymin;
-		trl->pt[1].fx = trn->pt[2].fx = trn->pt[1].fx = lim.Xmax;
-		trl->pt[1].fy = trn->pt[2].fy = trl->pt[2].fy = lim.Ymax;
-		trl->SetRect();			trn->SetRect();
-		trl->next = trn;		trn->next = 0L;
-		//do triangulation
-		tria = new Triangulate(trl);
-		for(i = 0; i < n; i++) {
-			tria->AddVertex(&da[i]);
-			}
-		free(da);
-		}
-	if(rX) delete rX;	if(rY) delete rY;	if(rZ) delete rZ;
-	trl = tria->trl;	delete tria;		return trl;
-}
-
-Ribbon *SurfTria(GraphObj *parent, DataObj *data, char *xr, char *yr, char *zr)
-{
-	Triangle *trl, *trc, *trn;
-	int i, j, n, npl;
-	double tmp;
-	Plane3D **planes;
-
-	trl = Triangulate1(xr, zr, yr, data);
-	for(i = 0, trc = trl; trc; i++) trc = trc->next;
-	if((n = i) && (planes = (Plane3D**)malloc(n*sizeof(Plane3D*)))) 
-		for(i = npl = 0, trc = trl; trc && i < n; i++) {
-		for(j = 0; j < 4; j++) {	//swap y and z values;
-			tmp = trc->pt[j].fz;	trc->pt[j].fz = trc->pt[j].fy;	trc->pt[j].fy = tmp;
-			}
-		planes[npl++] = new Plane3D(0L, data, trc->pt, 4);
-		trn = trc->next;	delete trc;		trc = trn;
-		}
-	if(npl) return new Ribbon(parent, data, (GraphObj**)planes, npl);
-	return 0L;
-}
+//rlp_math.cpp, Copyright (c) 2004-2008 R.Lackner
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+#include "rlplot.h"
+#include <math.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <string.h>
+#include <time.h>
+
+#define SWAP(a,b) {double temp=(a);(a)=(b);(b)=temp;}
+#define _PREC 1.0e-12
+
+extern Default defs;
+
+static char *MRQ_error = 0L;
+static double sqrt2pi = sqrt(_PI*2.0);
+
+//---------------------------------------------------------------------------
+//utilitity functions for memory allocation
+double **dmatrix(int nrl, int nrh, int ncl, int nch)
+{
+	int i;
+	double **m;
+
+	m = (double **)malloc(nrh * sizeof(double*));
+	//Allocate rows and set pointers to them
+	for(i = 0; i < nrh; i++) {
+		m[i] = (double *)malloc(nrh * sizeof(double));
+		}
+	return m;
+}
+void free_dmatrix(double **m, int nrl, int nrh, int ncl, int)
+{
+	int i;
+
+	for(i = 0; i < nrh; i++) free(m[i]);
+	free(m);
+}
+
+//---------------------------------------------------------------------------
+//The routine gaussj solves linear equations by Gauss-Jordan elimination
+bool gaussj(double **a, int n, double **b, int m)
+{
+	int *indxc, *indxr, *ipiv;
+	int i, icol, irow, j, k, l, ll;
+	double big, dum, pivinv;
+
+	indxc = (int*)malloc(n*sizeof(int*));
+	indxr = (int*)malloc(n*sizeof(int*));
+	ipiv = (int*)malloc(n*sizeof(int*));
+	for (j = 0; j < n; j++) ipiv[j] = 0;
+	for (i = 0; i < n; i++) {				//This is the main loop over the
+		big = 0.0;							//    columns to be reduced
+		for(j = 0; j < n; j ++)				//This is the outer loop of the search
+			if(ipiv[j] != 1)				//    for a pivot element
+				for(k = 0; k < n; k ++) {
+					if (ipiv[k] == 0) {
+						if(fabs(a[j][k]) >= big) {
+							big = fabs(a[j][k]);
+							irow = j;				icol = k;
+							}
+						}
+					else if(ipiv[k] > 1) {
+						MRQ_error = "Singular Matrix (1)";
+						free(ipiv);		free(indxr);	free(indxc);
+						return false;
+						}
+				}
+		++(ipiv[icol]);
+		//We now have the pivot element, so we interchange rows, if needed,
+		// to put the pivot element on the diagonal.
+		if(irow != icol) {
+			for(l = 0; l < n; l++) SWAP(a[irow][l], a[icol][l])
+			for(l = 0; l < m; l++) SWAP(b[irow][l], b[icol][l])
+			}
+		indxr[i] = irow;		indxc[i] = icol;
+		if(a[icol][icol] == 0.0) {
+			MRQ_error = "Singular Matrix (2)";
+			free(ipiv);		free(indxr);	free(indxc);
+			return false;
+			}
+		pivinv = 1.0/a[icol][icol];
+		a[icol][icol] = 1.0;
+		for(l = 0; l < n; l++) a[icol][l] *= pivinv;
+		for(l = 0; l < m; l++) b[icol][l] *= pivinv;
+		for(ll = 0; ll <  n; ll++)
+			if(ll != icol) { 							//Next, we reduce the rows
+				dum = a[ll][icol];
+				a[ll][icol] = 0.0;
+				for(l = 0; l < n; l++) a[ll][l] -= a[icol][l]*dum;
+				for(l = 0; l < m; l++) b[ll][l] -= b[icol][l]*dum;
+				}
+		}											// This is the end of the main loop
+	for (l = n; l > 0; l--) {						//   over columns of the reduction.
+		if(indxr[l] != indxc[l]) 					//   Unscramble the solution
+			for(k = 0; k < n; k++) SWAP (a[k][indxr[l]], a[k][indxc[l]]);
+		}											//And we are done.
+	free(ipiv);		free(indxr);	free(indxc);
+	return true;
+}
+
+//---------------------------------------------------------------------------
+//The routine mrqcof is called by mrqmin to evaluate the linearized fitting
+// matrix alpha and vector beta
+void mrqcof(double x[], double y[], double z[], int ndata, double **a, int ma,
+	int lista[], int mfit, double **alpha, double beta[], double *chisq,
+	void (*funcs)(double, double, double **, double *, double *, int))
+{
+	int k, j, i;
+	double ymod, wt, dy;
+	double *dyda;
+
+	dyda = (double*)malloc(ma*sizeof(double));
+	for(j = 0; j < mfit; j++) {					//Initialize (symmetric) alpha, beta
+		for(k = 0; k <= j; k++) alpha[j][k] = 0.0;
+		beta[j] = 0.0;
+		}
+	*chisq = 0.0;
+	for (i = 0; i < ndata; i++) {		 		//Summation loop over all data
+		(*funcs)(x[i], z ? z[i] : 0.0, a, &ymod, dyda, ma);
+		if(ymod != 0.0) dy = y[i]-ymod;			//functions = 0.0 if out of range
+		else dy = 0.0;
+		for(j = 0; j < mfit; j++) {
+			wt = dyda[lista[j]];
+			for (k = 0; k <= j; k++){
+				alpha[j][k] += wt*dyda[lista[k]];
+				}
+			beta[j] += dy*wt;
+			}
+		(*chisq) += dy*dy; 							//And find X^2 if function o.k.
+		}
+	for(j = 0; j < mfit; j++)						//Fill the symmetric side
+		for(k = 0; k <= j; k++) alpha[k][j]=alpha[j][k];
+	free(dyda);
+}
+
+//---------------------------------------------------------------------------
+//The routine mrqmin performs one iteration of Marquart's method for nonlinear
+// parameter estimation
+bool mrqmin(double *x, double *y, double *z, int ndata, double **a, int ma,
+	int *lista, int mfit, double **covar, double **alpha, double *chisq,
+	void (*funcs)(double, double, double **, double *, double *, int), double *alamda)
+{
+	int k, kk, j, ihit;
+	static double *da, *atry, *beta, ochisq;
+	static double **oneda, **atryref;
+
+	if (*alamda < 0.0) {								//Initialization
+		MRQ_error = 0L;
+		oneda = dmatrix(1, mfit, 1, 1);
+		atry = (double *)malloc(ma * sizeof(double));
+		atryref = (double**)malloc(ma * sizeof(double*));
+		for(j=0; j < ma; atryref[j++] = &atry[j]);
+		da = (double*)malloc(ma *sizeof(double));
+		beta = (double*)malloc(ma *sizeof(double));
+		kk = mfit+1;
+		for(j = 0; j < ma; j++) { 						//Does lista contain a proper
+			ihit = 0;									//   permutation of the
+			for(k = 0; k < mfit; k++)					//   coefficients ?
+				if(lista[k] == j) ihit++;
+			if(ihit == 0)
+				lista[kk++] = j;
+			else if (ihit >1) ErrorBox("Bad LISTA permutations in MRQMIN-1");
+			}
+		if(kk != ma+1) ErrorBox("Bad LISTA permutations in MRQMIN-2");
+		*alamda = 0.001;
+		mrqcof(x, y, z, ndata, a, ma, lista, mfit, alpha, beta, chisq, funcs);
+		ochisq=(*chisq);
+		}
+	for (j = 0; j < mfit; j++) {						//Alter linearized fitting matrix
+		for(k = 0; k < mfit; k++) covar[j][k] = alpha[j][k];	// by augmenting
+		covar[j][j] = alpha[j][j]*(1.0+(*alamda));		// diagaonal elements
+		oneda[j][0] = beta[j];
+		}
+	if (!gaussj(covar, mfit, oneda, 1)) return false;	//Matrix solution ?
+	for(j = 0; j < mfit; j++) da[j] = oneda[j][0];
+	if(*alamda == 0.0) {								//Once converged evaluate
+														//  covariance matrix with
+		free(beta);										//  alamda = 0.
+		free(da);
+		free(atry);
+		free(atryref);
+		free_dmatrix(oneda, 1, mfit, 1, 1);
+		return true;
+		}
+	for(j = 0; j < ma; j++) atry[j] = *a[j];
+	for(j = 0; j < mfit; j++)							//Did the trial succeed ?
+		atry[lista[j]] = *a[lista[j]] + da[j];
+	mrqcof(x, y, z, ndata, atryref, ma, lista, mfit, covar, da, chisq, funcs);
+	if(*chisq < ochisq) {								//Success, accept the new solution
+		*alamda *= 0.1;
+		ochisq=(*chisq);
+		for(j = 0; j < mfit; j++) {
+			for(k = 0; k < mfit; k++) alpha[j][k] = covar[j][k];
+			beta[j] = da[j];
+			*a[lista[j]] = atry[lista[j]];
+			}
+		}
+	else {												//Failure, increase almda and
+		*alamda *= 10.0;								//    return.
+		*chisq = ochisq;
+		}
+	return true;
+}
+
+bool Check_MRQerror()
+{
+	bool bRet;
+
+	if(bRet = MRQ_error != 0L) ErrorBox(MRQ_error);
+	MRQ_error = 0L;
+	return bRet;
+}
+
+//---------------------------------------------------------------------------
+//Use heap sort to sort elements of an float array
+//W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1988/1989)
+//Numerical Recipes in C, Cambridge University Press, ISBN 0-521-35465-X
+// p. 245
+void SortArray(int n, double *vals)
+{
+	int l, j, ir, i;
+	double rra, *ra = vals-1;
+
+	if(n < 2 || !vals) return;
+	l=(n >> 1) + 1;				ir = n;
+	for( ; ; ) {
+		if(l > 1) rra = ra[--l];
+		else {
+			rra = ra[ir];		ra[ir] = ra[1];
+			if(--ir == 1) {
+				ra[1] = rra;	return;
+				}
+			}
+		i = l;					j = l << 1;
+		while (j <= ir) {
+			if (j < ir && ra[j] < ra[j+1]) ++j;
+			if (rra < ra[j]) {
+				ra[i] = ra[j];	j += (i=j);
+				}
+			else j = ir + 1;
+			}
+		ra[i] = rra;
+		}
+}
+
+//sorts array v1 making the corresponding rearrangement of v2
+void SortArray2(int n, double *v1, double *v2)
+{
+	int l, j, ir, i;
+	double rra, rrb, *ra = v1-1, *rb = v2-1;
+
+	if(n < 2 || !v1 || !v2) return;
+	l=(n >> 1) + 1;				ir = n;
+	for( ; ; ) {
+		if(l > 1) {
+			rra = ra[--l];		rrb = rb[l];
+			}
+		else {
+			rra = ra[ir];		rrb = rb[ir];
+			ra[ir] = ra[1];		rb[ir] = rb[1];
+			if(--ir == 1) {
+				ra[1] = rra;	rb[1] = rrb;
+				return;
+				}
+			}
+		i = l;					j = l << 1;
+		while (j <= ir) {
+			if (j < ir && ra[j] < ra[j+1]) ++j;
+			if (rra < ra[j]) {
+				ra[i] = ra[j];	rb[i] = rb[j];
+				j += (i=j);
+				}
+			else j = ir + 1;
+			}
+		ra[i] = rra;			rb[i] = rrb;
+		}
+}
+
+//Use heap sort to sort elements of an xy array
+void SortFpArray(int n, lfPOINT *vals)
+{
+	int l, j, ir, i;
+	lfPOINT rra, *ra = vals-1;
+
+	if(n < 2) return;
+	l=(n >> 1) + 1;					ir = n;
+	for( ; ; ) {
+		if(l > 1) {
+			rra.fx = ra[--l].fx; rra.fy = ra[l].fy;
+			}
+		else {
+			rra.fx = ra[ir].fx;		rra.fy = ra[ir].fy;
+			ra[ir].fx = ra[1].fx;	ra[ir].fy = ra[1].fy;	
+			if(--ir == 1) {
+				ra[1].fx = rra.fx;	ra[1].fy = rra.fy;
+				return;
+				}
+			}
+		i = l;					j = l << 1;
+		while (j <= ir) {
+			if (j < ir && ra[j].fx < ra[j+1].fx) ++j;
+			if (rra.fx < ra[j].fx) {
+				ra[i].fx = ra[j].fx;	ra[i].fy = ra[j].fy;
+				j += (i=j);
+				}
+			else j = ir + 1;
+			}
+		ra[i].fx = rra.fx;				ra[i].fy = rra.fy;
+		}
+}
+
+//randomize array
+double *randarr(double *v0, int n, long *seed)
+{
+	double r, *v, *v_tmp;
+	int i, j, l;
+
+	if(!(v = (double*)malloc(n *sizeof(double)))) return 0L;
+	if(!(v_tmp = (double*)memdup(v0, n *sizeof(double),0))) return 0L;
+	for(l = n, i = 0; i < n; ) {
+		r = ran2(seed);			j = (int)(r *((double)l));
+		if(j < l) {
+			v[i++] = v_tmp[j];
+			if(j < l)memcpy(v_tmp+j, v_tmp+j+1, (l-j)*sizeof(double));
+			l--;
+			}
+		}
+	return v;
+}
+
+//resample array
+double *resample(double *v0, int n, long *seed)
+{
+	double r, *v;
+	int i, j;
+
+	if(!(v = (double*)malloc(n *sizeof(double)))) return 0L;
+	for(i = 0; i < n; ) {
+		r = ran2(seed);			j = (int)(r *((double)n));
+		if(j < n) v[i++] = v0[j];
+		}
+	return v;
+}
+
+//---------------------------------------------------------------------------
+// Cubic Spline Interpolation
+// Ref.: W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1989), 
+//    Numerical Rcipies in C. The Art of Scientific Computing, 
+//    Cambridge University Press, ISBN 0-521-35465, pp. 96 ff.
+void spline(lfPOINT *v, int n, double *y2)
+{
+	int i, k;
+	double p, qn, sig, un, *u;
+
+	u = (double *)malloc(n * sizeof(double));
+	y2[0] = u[0] = 0.0;
+	for(i = 1; i < (n-1); i++) {
+		sig = (v[i].fx-v[i-1].fx)/(v[i+1].fx-v[i-1].fx);
+		p = sig*y2[i-1]+2.0;			y2[i]=(sig-1.0)/p;
+		u[i]=(v[i+1].fy-v[i].fy)/(v[i+1].fx-v[i].fx)-(v[i].fy-v[i-1].fy)/(v[i].fx-v[i-1].fx);
+		u[i]=(6.0*u[i]/(v[i+1].fx-v[i-1].fx)-sig*u[i-1])/p;
+		}
+	qn = un = 0.0;
+	y2[n-1] = (un - qn * u[n-2])/(qn*y2[n-2]+1.0);
+	for(k = n-2; k >= 0; k--) {
+		y2[k] = y2[k]*y2[k+1]+u[k];
+		}
+	free(u);
+}
+
+//---------------------------------------------------------------------------
+// The Gamma Function: return the ln(G(xx)) for xx > 0
+// Ref: B.W. Brown, J. Lovato, K. Russel (1994)
+//    DCDFLIB.C, Library of C Routinesfor Cumulative Distribution Functions,
+//    Inverses, and other Parameters.
+
+double devlpl(double a[], int n, double x)
+{
+	double term;
+	int i;
+
+	for(term = a[n-1], i= n-2; i>=0; i--) term = a[i] + term * x;
+	return term;
+}
+
+
+double gammln(double x)
+{
+	static double coef[] = {0.83333333333333023564e-1,-0.27777777768818808e-2, 
+	0.79365006754279e-3, -0.594997310889e-3, 0.8065880899e-3};
+static double scoefd[] = {0.62003838007126989331e2, 0.9822521104713994894e1,
+	-0.8906016659497461257e1, 0.1000000000000000000e1};
+static double scoefn[] = {0.62003838007127258804e2, 0.36036772530024836321e2,
+	0.20782472531792126786e2, 0.6338067999387272343e1,0.215994312846059073e1,
+	0.3980671310203570498e0, 0.1093115956710439502e0,0.92381945590275995e-2,
+	0.29737866448101651e-2};
+	double offset, prod, xx;
+	int i,n;
+
+    if(x < 6.0) {
+		prod = 1.0e0;	    xx = x;
+		while(xx > 3.0) {
+			xx -= 1.0;			prod *= xx;
+			}
+		if(x <= 2.0) while(xx < 2.0) {
+			prod /= xx;			xx += 1.0;
+			}
+		// compute rational approximation to gamma(x)
+		return log(devlpl(scoefn, 9, xx-2.0) / devlpl(scoefd, 4, xx-2.0) * prod);
+		}
+	else {
+		offset = 0.91893853320467274178;	// hln2pi
+		// if necessary make x at least 12 and carry correction in offset
+		if(n = 13.0 >= x ? (int)(12.0 - x) : 0) xx = x;
+		else {
+			for(i=1, prod = 1.0; i<= n; i++) prod *= (x+(double)(i-1));
+			offset -= log(prod);			xx = x+(double)n;
+			}
+		// compute power series
+		return devlpl(coef, 5, 1.0/(xx*xx)) / xx + (offset+(xx-0.5)*log(xx)-xx);
+		}
+}
+
+//---------------------------------------------------------------------------
+// Special Functions
+// Ref.: W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1989), 
+//    Numerical Rcipies in C. The Art of Scientific Computing, 
+//    Cambridge University Press, ISBN 0-521-35465, pp. 166 ff.
+
+//The Factorial Function: return n!
+double factrl(int n)
+{
+	static int ntop = 4;
+	static double a[33]={1.0, 1.0, 2.0, 6.0, 24.0};
+	int j;
+
+	if(n < 0) return 0.0;		//error: no factorial for negative numbers
+	if(n > 32) return exp(gammln(n+1.0));
+	while(ntop < n) {			//fill in table up to desired value
+		j = ntop++;		a[ntop]=a[j] * ntop;
+		}
+	return a[n];
+}
+
+//returns the incomplete gamma function evaluated by its series representation
+void gser(double *gamser, double a, double x, double *gln)
+{
+	int n;
+	double sum, del, ap;
+
+	*gln = gammln(a);
+	if(x <= 0) {
+		*gamser = 0.0;			return;
+		}
+	else {
+		ap = a;					del = sum = 1.0/a;
+		for(n = 1; n <= 100; n++) {
+			ap += 1.0;			del *= x/ap;		sum += del;
+			if(fabs(del) <= fabs(sum) * _PREC) {
+				*gamser = sum * exp(-x + a * log(x)-(*gln));
+				return;
+				}
+			}
+		// maximum number of iterations exceeded
+		*gamser = sum * exp(-x + a * log(x)-(*gln));
+		}
+
+}
+
+//returns the incomplete gamma function evaluated by its continued fraction representation
+void gcf(double *gammcf, double a, double x, double *gln)
+{
+	int n;
+	double gold=0.0, g, fac=1.0, b1=1.0, b0=0.0, anf, ana, an, a1, a0=1.0;
+
+	*gln=gammln(a);		a1=x;
+	for(n=1; n <= 100; n++) {
+		an = (double)n;			ana = an -a;		a0 = (a1 + a0 * ana) * fac;
+		b0 = (b1 + b0 * ana) *fac;					anf = an * fac;
+		a1 = x * a0 + anf * a1;						b1 = x * b0 + anf * b1;
+		if(a1) {
+			fac = 1.0 / a1;							g = b1 * fac;
+			if(fabs((g-gold)/g) <= _PREC) {
+				*gammcf = exp(-x + a * log(x) -(*gln)) * g;
+				return;
+				}
+			gold = g;
+			}
+		}
+	// maximum number of iterations exceeded
+	*gammcf = exp(-x + a * log(x) -(*gln)) * gold;
+}
+
+//returns the incomplete gamma function P(a,x)
+double gammp(double a, double x)
+{
+	double gamser, gammcf, gln;
+
+	if(x < 0.0 || a <= 0.0) return 0.0;
+	if(x < (a+1.0)) {
+		gser(&gamser, a, x, &gln);			return gamser;
+		}
+	else {
+		gcf(&gammcf, a, x, &gln);			return 1.0-gammcf;
+		}
+	return 0.0;
+}
+
+//returns the complementary incomplete gamma function Q(a,x)
+double gammq(double a, double x)
+{
+	double gamser, gammcf, gln;
+
+	if(x < 0.0 || a <= 0.0) return 0.0;
+	if(x < (a+1.0)) {
+		gser(&gamser, a, x, &gln);			return 1.0-gamser;
+		}
+	else {
+		gcf(&gammcf, a, x, &gln);			return gammcf;
+		}
+	return 0.0;
+}
+
+//continued fraction for incomplete beta function, used by betai()
+double betacf(double a, double b, double x)
+{
+	double qap, qam, qab, em, tem, d, bz, bm = 1.0, bp, bpp, az = 1.0, am = 1.0, ap, app, aold;
+	int m;
+
+	qab = a+b;		qap = a+1.0;		qam = a-1.0;	bz = 1.0-qab*x/qap;
+	for(m = 1; m <= 100; m++) {
+		em = (double)m;			tem = em+em;
+		d = em*(b-em)*x/((qam+tem)*(a+tem));
+		ap = az + d * am;		bp = bz + d *bm;
+		d = -(a+em)*(qab+em)*x/((qap+tem)*(a+tem));
+		app = ap + d * az;		bpp = bp + d * bz;
+		aold = az;				am = ap/bpp;
+		bm = bp/bpp;			az = app/bpp;
+		bz = 1.0;
+		if(fabs(az-aold) <= (_PREC * fabs(az))) return az;	//success: return
+		}
+	return az;												//fail: iterations exceeded
+}
+
+//The incomplete beta function Ix(a,b) for 0 <= x <= 1
+double betai(double a, double b, double x)
+{
+	double bt;
+
+	if(x < 0.0 || x > 1.0) return 0.0;		//range !
+	if(x == 0.0 || x == 1.0) bt = 0.0;
+	else
+		bt = exp(gammln(a+b)-gammln(a)-gammln(b)+a*log(x)+b*log(1.0-x));
+	if(x < (a+1.0)/(a+b+2.0)) return bt * betacf(a, b, x)/a;
+	else return 1.0 - bt * betacf(b, a, 1.0 - x)/b;
+}
+
+//The following relations are obviously based on:
+//  Abramowitz, M. & Stegun I.A. (1964): Hanbook of Mathematical Functions.
+//    Applied Mathematics Series, vol. 55 (Washington: National Bureau
+//    of Standards).
+
+//the binomial coefficient
+double bincof(double n, double k)
+{
+	if(n<0 || k<0 || k > n) return 0.0;
+	return exp(gammln(n+1.0) - gammln(k+1.0) - gammln(n-k+1.0));
+}
+
+//the cumulative binomial distribution
+double binomdistf(double k, double n, double p)
+{
+	if(k > n || n < 0.0 || p < 0.0 || p >1.0) return 0.0;
+	return betai(n-k, k+1, p);
+}
+
+//the beta function
+double betaf(double z, double w)
+{
+	return exp(gammln(z)+gammln(w)-gammln(z+w));
+}
+
+//the error function: not all compilers have a built in erf()
+double errf(double x)
+{
+	return x < 0.0 ? -gammp(0.5, x*x) : gammp(0.5, x*x);
+}
+
+//the complementary error function
+double  errfc(double x)
+{
+//	return x < 0.0 ? 2.0 - gammq(0.5, x*x) : gammq(0.5, x*x);
+	return x < 0.0 ? 1.0 + gammp(0.5, x*x) : gammq(0.5, x*x);
+}
+
+//cumulative normal distribution
+double norm_dist(double x, double m, double s)
+{
+	return 0.5 + errf((x - m)/(s * _SQRT2))/2.0;
+}
+
+//normal distribution
+double norm_freq(double x, double m, double s)
+{
+	double ex;
+
+	ex = (x-m)/s;	ex = exp(-0.5*ex*ex);
+	return ex/(s*sqrt2pi);
+}
+
+//cumulative exponential distribution
+double exp_dist(double x, double l, double s)
+{
+	if(x >= 0.0 && l > 0.0) return 1.0-exp(-x*l);
+	else return 0.0;
+}
+
+//inverse exponential distribution
+double exp_inv(double p, double l, double s)
+{
+	if(p >= 1.0) return HUGE_VAL;
+	if(l <= 0.0) return 0.0;
+	return -log(1.0-p)/l;
+}
+
+//exponential distribution
+double exp_freq(double x, double l, double s)
+{
+	if(x >= 0.0 && l > 0.0) return l*exp(-x*l);
+	else return fabs(l);
+}
+
+//cumulative lognormal distribution
+double lognorm_dist(double x, double m, double s)
+{
+	return 0.5 + errf((log(x) - m)/(s * _SQRT2))/2.0;
+}
+
+//lognormal distribution
+double lognorm_freq(double x, double m, double s)
+{
+	double tmp;
+
+	if(x > 0.0 && m > 0.0 && s > 0.0) {
+		tmp = (log(x)-m)/s;
+		return exp(-0.5*tmp*tmp)/(x*s*sqrt2pi);
+		}
+	return 0.0;
+}
+
+//chi square distribution
+double chi_dist(double x, double df, double)
+{
+	if(x <= 0.0) return 1.0;
+	return gammq(df/2.0, x/2.0);
+}
+
+double chi_freq(double x, double df)
+{
+	if(x < 0.0 || df <= 0.0) return 0.0;
+	if(x < 1.0e-32) x = 1.0e-32;
+//formula by Wikipedia 
+//	return exp(log(2.0)*(1.0-df/2.0)+log(x)*(df-1.0)+x*x/-2.0-gammln(df/2.0));
+//formula by StatSoft's STATISTICA documentation
+	return exp(-x/2.0+log(x)*(df/2.0-1.0)-log(2.0)*df/2.0-gammln(df/2.0));
+}
+
+//t-distribution
+double t_dist(double t, double df, double)
+{
+	return betai(df/2.0, 0.5, (df/(df+t*t)));
+}
+
+double t_freq(double t, double df)
+{
+	double a, b, c, d;
+ 
+	a = gammln((df+1.0)/2.0);		b = log(sqrt(df * _PI));
+	c = gammln(df/2.0);				d = log(1.0+t*t/df) * (df+1)/2.0;
+	return exp(a-b-c-d);
+}
+
+//poisson distribution
+double pois_dist(double x, double m, double)
+{
+	return gammq(x+1.0, m);
+}
+
+//f-distribution
+double f_dist(double f, double df1, double df2)
+{
+	return f > 0.0 ? betai(df2/2.0, df1/2.0, df2/(df2+df1*f)): 1.0;
+}
+
+double f_freq(double x, double df1, double df2)
+{
+	double a, b, c, d;
+
+	a = gammln((df1+df2)/2.0);		b = gammln(df1/2.0) + gammln(df2/2.0);
+	c = log(df1/df2) * df1/2.0 + log(x) * (df1/2.0-1.0);
+	d = log(1+(df1/df2)*x) * (-(df1+df2)/2.0);
+	return exp(a-b+c+d);
+}
+
+//---------------------------------------------------------------------------
+// The Weibull distribution
+//---------------------------------------------------------------------------
+double weib_dist(double x, double shape, double scale)
+{
+	double dn=1.0, sum, term, tmp;
+
+	if(shape <= 0.0 || scale <= 0.0) return HUGE_VAL;
+	if(x <= 0.0) return 0.0;
+	term = -pow(x/scale, shape);		tmp = fabs(term);
+	if(tmp < 2.22e-16) return tmp;
+	if (tmp > 0.697) return -exp(term)+1.0;
+	x = sum = term;
+	do {				//do taylor series
+		dn += 1.0 ;		term *= x/dn;		sum += term;
+		}while (fabs(term) > fabs(sum) * 2.22e-16) ;
+	return -sum;
+}
+
+double weib_freq(double x, double shape, double scale)
+{
+	double tmp1, tmp2;
+
+	if (shape <= 0.0 || scale <= 0.0) return HUGE_VAL;
+	if (x < 0) return 0.0;
+	if(x > -HUGE_VAL && x < HUGE_VAL) {
+		if(x == 0.0 && shape < 1.0) return HUGE_VAL;
+		tmp1 = pow(x / scale, shape - 1.0);
+		tmp2 = tmp1 * (x / scale);
+		return shape * tmp1 * exp(-tmp2) / scale;
+		}
+	return HUGE_VAL;
+}
+
+//---------------------------------------------------------------------------
+// The geometric distribution
+//---------------------------------------------------------------------------
+double geom_freq(double x, double p)
+{ 
+    if (p <= 0 || p > 1 || x < 0.0) return HUGE_VAL;
+	x = floor(x + 1.0e-16);
+	return pow(1.0 - p, x) * p;
+}
+
+double geom_dist(double x, double p)
+{
+	double sum, x1;
+
+	for(x1 = sum = 0.0; x1 <= x; sum += geom_freq(x1, p), x1 += 1.0);
+	return sum;
+}
+
+//---------------------------------------------------------------------------
+// The hypergeometric distribution
+//---------------------------------------------------------------------------
+double hyper_freq(double k, double n0, double m, double n1)
+{
+	double pr;
+
+	if(k < 0.0 || m < 0.0 || n1 < 0.0 || n1 > n0+m) return HUGE_VAL;
+	k = floor(k + 1.0e-16);		n0 = floor(n0 + 1.0e-16);
+	m = floor(m + 1.0e-16);		n1 = floor(n1 + 1.0e-16);
+
+	pr = gammln(m+1.0) - gammln(k+1.0) - gammln(m-k+1.0)
+		+ gammln(n0-m+1.0) - gammln(n1-k+1.0) - gammln(n0-m-n1+k+1.0)
+		- gammln(n0+1.0) + gammln(n1+1.0) + gammln(n0-n1+1.0);
+	return exp(pr);
+}
+
+double hyper_dist(double k, double n0, double m, double n1)
+{
+	double sum, x1;
+
+	for(x1 = sum = 0.0; x1 <= k; sum += hyper_freq(x1, n0, m, n1), x1 += 1.0);
+	return sum;
+}
+
+//---------------------------------------------------------------------------
+// The Cauchy (Lorentz) distribution
+//---------------------------------------------------------------------------
+double cauch_dist(double x, double loc, double scale)
+{
+	double y;
+
+	if(scale < 0.0) return HUGE_VAL;
+	x = (x - loc) / scale;
+	if(x > -HUGE_VAL && x < HUGE_VAL) {
+		if (fabs(x) > 1.0) {
+			y = atan(1.0/x)/_PI;		return (x > 0) ? 1.0-y : -y;
+			} 
+		else return 0.5 + atan(x)/_PI;
+		}
+	return HUGE_VAL;
+}
+
+double cauch_freq(double x, double loc, double scale)
+{
+	double y;
+
+	if(scale < 0.0) return HUGE_VAL;
+	if(x > -HUGE_VAL && x < HUGE_VAL) {
+		y = (x - loc) / scale;
+		return 1.0 / (_PI * scale * (1.0 + y*y));
+		}
+	return HUGE_VAL;
+}
+
+//---------------------------------------------------------------------------
+// The Logistic distribution
+//---------------------------------------------------------------------------
+double logis_dist(double x, double loc, double scale)
+{
+	if(scale < 0.0) return HUGE_VAL;
+	x = exp(-(x - loc) / scale);
+	if(x > -HUGE_VAL && x < HUGE_VAL) {
+		return 1.0/(1.0 + x);
+		}
+	return HUGE_VAL;
+}
+
+double logis_freq(double x, double loc, double scale)
+{
+	double e, f;
+
+	x = fabs((x - loc) / scale);
+	if(x > -HUGE_VAL && x < HUGE_VAL) {
+		e = exp(-x);     f = 1.0 + e;	
+		return  e / (scale * f*f);
+		}
+	return HUGE_VAL;
+}
+
+//---------------------------------------------------------------------------
+// Shapiro-Wilk W test and its significance level
+// Algorithm AS 394, 1995, Appl. Statist. 44(4), 547-551
+//
+static int do_swilk(double (*func)(double, double, double), double p1, double p2, 
+	double *x, int n, int n1, int n2, double *a, double *w, double *pw)
+{
+
+//initialized data
+const static double z90 = 1.2816;		//tinv(0.2, inf)
+const static double z95 = 1.6449;		//tinv(0.1, inf)
+const static double z99 = 2.3263;		//tinv(.05, inf)
+const static double zm = 1.7509;		//(z90 + z95 + z99)/3
+const static double zss = 0.56268;
+const static double bf1 = 0.8378;
+const static double xx90 = 0.556;
+const static double xx95 = 0.622;
+const static double sqrth = 0.70711;	//sqrt(0.5)
+const static double smal = 1.0e-19;		//small value
+const static double pi6 = 1.909859;
+const static double stqr = 1.047198;	//pi / 3
+
+//polynomial coefficients
+static double g[2] = {-2.273, 0.459};
+static double c1[6] = {0.0, 0.221157, -0.147981, -2.07119, 4.434685, -2.706056};
+static double c2[6] = {0.0, 0.042981, -0.293762, -1.752461, 5.682633, -3.582633};
+static double c3[4] = {0.544, -0.39978, 0.025054, -6.714e-4};
+static double c4[4] = {1.3822, -0.77857, 0.062767, -0.0020322};
+static double c5[4] = {-1.5861, -0.31082, -0.083751, 0.0038915};
+static double c6[3] = {-0.4803, -0.082676, 0.0030302};
+static double c7[2] = {0.164, 0.533};
+static double c8[2] = {0.1736, 0.315};
+static double c9[2] = {0.256, -0.00635};
+
+	//local variables
+	int i, j, ncens, i1, nn2;
+	double zbar, ssassx, summ2, ssumm2, gamma, delta, range;
+	double a1, a2, an, bf, ld, m, s, sa, xi, sx, xx, y, w1;
+	double fac, asa, an25, ssa, z90f, sax, zfm, z95f, zsd, z99f, rsn, ssx, xsx;
+
+	//parameter adjustment
+	--a;
+
+	*pw = 1.0;
+	if(*w >= 0.0) *w = 1.0;
+	an = (double)(n);			nn2 = n>>1;
+	if(n2 < nn2) return 3;
+	if(n < 3) return 1;
+	// calculate coefficients a[]
+	if(true) {
+		if(n == 3) a[1] = sqrth;
+		else {
+			for(i = 1, summ2 = 0.0, an25 = an + 0.25; i <= n2; ++i) {
+				a[i] = distinv(func, p1, p2, (i-0.375)/an25, 0);
+				summ2 += (a[i] * a[i]);
+				}
+			summ2 *= 2.0;			ssumm2 = sqrt(summ2);
+			rsn = 1.0 / sqrt(an);	a1 = devlpl(c1, 6, rsn) -a[1]/ssumm2;
+			//normalize a[]
+			if(n > 5) {
+				i1 = 3;
+				a2 = -a[2] / ssumm2 + devlpl(c2, 6, rsn);
+				fac = sqrt((summ2 - 2.0*a[1]*a[1] - 2.0*a[2]*a[2])
+					/ (1.0 - 2.0*a1*a1 - 2.0*a2*a2));
+				a[2] = a2;
+				}
+			else {
+				i1 = 2;
+				fac = sqrt((summ2 -2.0*a[1]*a[1]) / (1.0 - 2.0*a1*a1));
+				}
+			a[1] = a1;
+			for(i = i1; i <= nn2; ++i) a[i] /= -fac;
+			} 
+		}
+	if(n1 < 3) return 1;
+	ncens = n - n1;
+	if(ncens < 0 || (ncens > 0 && n < 20)) return 4;
+	delta = (double)ncens / an;
+	if(delta > 0.8) return 5;
+	//if w input as negative, calculate significance level of -w
+	if(*w < 0.0) { 
+		w1 = 1.0 + *w;
+		goto sw_prob;
+		}
+	//check for zero range
+	if((range = x[n1-1] -x[0]) < smal) return 6;
+	//check for sort order
+	xx = x[0]/range;	sx = xx;	sa = -a[1];		j = n -1;
+	for(i = 1; i < n1; --j) {
+		xi = x[i] / range;			sx += xi;			++i;
+		if(i != j) sa += i > j ? a[i < j ? i : j] : -a[i < j ? i : j];
+		xx = xi;
+		}
+	//calculate w statistic as squared correlation between data and coefficients
+	sa /= n1;		sx /= n1;		ssa = ssx = sax = 0.0;		j = n -1;
+	for(i = 0; i < n1; ++i, --j) {
+		if(i > j) asa = a[1+j] - sa;
+		else if(i < j) asa = -a[1+i] - sa;
+		else asa = -sa;
+		xsx = x[i] / range - sx;		ssa += asa * asa;
+		ssx += xsx * xsx;				sax += asa * xsx;
+		}
+	ssassx = sqrt(ssa * ssx);
+	w1 = (ssassx - sax) * (ssassx + sax) / (ssa * ssx);
+sw_prob:
+	*w = 1.0 - w1;			//reduce rounding errors
+	if(n == 3) {
+		*pw = pi6 * (asin(sqrt(*w)) - stqr);
+		return 0;
+		}
+	y = log(w1);
+	xx = log(an);
+	if(n <= 11) {
+		gamma = devlpl(g, 2, an);
+		if(y >= gamma) {
+			*pw = smal;		return 0;
+			}
+		y = -log(gamma - y);		m = devlpl(c3, 4, an);
+		s = exp(devlpl(c4, 4, an));
+		}
+	else {					//n >= 12
+		m = devlpl(c5, 4, xx);		s = exp(devlpl(c6, 3, xx));
+		}
+	//Censoring by proportion  NCENS/N
+	if(ncens > 0) {
+		ld = -log(delta);			bf = 1.0 + xx * bf1;
+		z90f = z90 + bf * pow(devlpl(c7, 2, pow(xx90, xx)), ld);
+		z95f = z95 + bf * pow(devlpl(c8, 2, pow(xx95, xx)), ld);
+		z99f = z99 + bf * pow(devlpl(c9, 2, xx), ld);
+		//Regress z90f ... z99f on normal deviates z90 ... z99
+		//   to get pseudo-mean and pseudo-sd of z as the slope and intercept 
+		zfm = (z90f + z95f + z99f)/3.0;
+		zsd = (z90 * (z90f - zfm) + z95 * (z95f - zfm) + z99 * (z99f - zfm)) / zss;
+		zbar = zfm - zsd * zm;		m += zbar * s;		s *= zsd;
+		}
+	*pw = 1.0 - norm_dist(y, m, s);
+	return 0;
+}
+
+void swilk1(int n, double *v0, double (*func)(double, double, double), double p1, double p2, 
+	bool bsorted, double *w, double *p)
+{
+	double *v, *a;
+
+	if(!n || !w || !p) return;			*w = *p = 1.0;
+	if(!(a = (double*)malloc(n *sizeof(double)))) return;
+	if(!bsorted && (v = (double*)memdup(v0, n*sizeof(double), 0)))SortArray(n, v);
+	else if(bsorted) v = v0;
+	else return;
+	if(do_swilk(func, p1, p2, v, n, n, n>>1, a, w, p)){
+		//an error occured
+		*w = *p = -1.0;
+		}
+	free(a);	if(v != v0) free(v);
+}
+
+//Kolmogorov-Smirnov's test and distribution of D
+// (1) Miller L. (1956) Journal of the American Statistical Association.  51: 111-121
+// (2) Mises R. (1964) Mathematical Theory of Probability and Statistics (New York: Academic Press)
+//     Chapters IX(C) and IX(E)
+// (3) Press W.H., Flannery B.P.,Teukolsky S.A., Vetterling W.T. (1988/1989)
+//     Numerical Recipes in C, Cambridge University Press, ISBN 0-521-35465-X, pp. 490 ff.
+//
+double ks_dist(int n, double d)
+{
+	double j, jn, sum, las, q, r, s, dn = (double)n;
+
+	las = floor(dn - dn * d);
+	for (j = sum = 0.0; j <= las; j += 1.0) {
+		jn = j / dn;							q = gammln(dn+1) - gammln(j+1) - gammln(dn-j+1.0);
+		r = (dn - j) * log( 1 - d - jn );		s = (j - 1.0) * log( d + jn );
+		sum += exp(q + r + s);
+		}
+	return(d*sum);
+}
+
+void KolSmir(int n, double *v0, double (*func)(double, double, double), double p1, double p2, 
+	bool bsorted, double *d, double *p)
+{
+	int i;
+	double *v, *dev, *x, ff, dt, dt1, dt2;
+	double dn = (double)n, f0 = 0.0;
+
+	if(!n || !d || !p) return;			*d = *p = 0.0;
+	if(!(dev = (double*)malloc(n*sizeof(double)))) return;
+	if(!(x = (double*)malloc(n*sizeof(double)))){
+		free(dev);						return;
+		}
+	if(!bsorted && (v = (double*)memdup(v0, n*sizeof(double), 0)))SortArray(n, v);
+	else if(bsorted) v = v0;
+	else return;
+	for(i = 0, *d = 0.0; i < n; i++) {
+		x[i] = (double)(i+1)/dn;		ff = (*func)(v[i], p1, p2); 
+		dt1 = fabs(f0-ff);				dt2 = fabs(dev[i] = (f0 = x[i])-ff);
+		dt = dt1 > dt2 ? dt1 : dt2;		if(dt > *d) *d = dt;
+		}
+	free(dev);	free(x);
+	*p = ks_dist(n, *d);
+	if(v != v0) free(v);
+}
+
+//---------------------------------------------------------------------------
+// Inverse of statitistical functions:
+// funcd supplies the function value fn and the derivative df of the function sf at x
+void funcd(double x, double *fn, double *df, double (*sf)(double, double, double), 
+		   double df1, double df2, double p)
+{
+	double y1, y2;
+
+	*fn = (sf)(x, df1, df2);
+	if(sf == norm_dist) *df = norm_freq(x, df1,df2);
+	else if(sf == chi_dist) *df = -chi_freq(x, df1);
+	else if(sf == t_dist) *df = -2.0 * t_freq(x, df1);
+	else if(sf == f_dist) *df = -1.0 * f_freq(x, df1, df2);
+	else if(sf == lognorm_dist) *df = lognorm_freq(x, df1, df2);
+	else if(sf == weib_dist) *df = weib_freq(x, df1, df2);
+	else if(sf == cauch_dist) *df = cauch_freq(x, df1, df2);
+	else if(sf == logis_dist) *df = logis_freq(x, df1, df2);
+	else {		//numerical differentiation
+		y1 = (sf)(x * 0.995, df1, df2);		y2 = (sf)(x * 1.005, df1, df2);
+		*df = (y2-y1)*100.0/x;
+		}
+	*fn = *fn - p;
+}
+
+//distinv does actual Newton-Raphson root finding
+double distinv(double (*sf)(double, double, double), double df1, double df2, double p, double x0)
+{
+	int i, j;
+	double df, df0, adf, dx, f, rtn;
+
+	for(j = 0, rtn = dx = x0; j < 200; j++) {
+		for(i = 0, df0 = 0.0; i < 20; i++) {
+			funcd(rtn, &f, &df, sf, df1, df2, p);
+			if((adf=fabs(df)) > 1.0e-12 || df0 > adf) break;
+			rtn += (dx = dx/2.0);				df0 = adf;
+			if(i >= 19) return HUGE_VAL;
+			}
+		dx = f/df*(0.01*(double)(100-j));		rtn -= dx;
+		if(fabs(dx) < _PREC && j > 3)return rtn; 
+		}
+	return HUGE_VAL;
+}
+
+//---------------------------------------------------------------------------
+//some statistical basics
+//do quartiles, median of data
+void d_quartile(int n, double *v, double *q1, double *q2, double *q3)
+{
+	int n2, n3;
+	double f1, f2;
+
+	if(!v || n<2) return;
+	SortArray(n, v);			n2 = n >> 1;
+	if(q1) {
+		n3 = n2 >> 1;
+		switch(n%4) {
+		case 3:		n3 ++;		f1 = 2.0;		f2 = 2.0;		break;
+		case 2:		n3 ++;		f1 = 3.0;		f2 = 1.0;		break;
+		case 1:		n3 ++;		f1 = 4.0;		f2 = 0.0;		break;
+		default:	f1 = 1.0;	f2 = 3.0;						break;
+			}
+		*q1 = (f1*v[n3-1] + f2*v[n3])/4.0;
+		}
+	if(q2) {
+		if(n & 1) *q2 = v[n2];
+		else *q2 = (v[n2-1] + v[n2])/2.0;
+		}
+	if(q3) {
+		n3 = n2 >> 1;
+		switch(n%4) {
+		case 3:		n3++;		f1 = 2.0;		f2 = 2.0;	break;
+		case 2:		f1 = 3.0;	f2 = 1.0;					break;
+		case 1:		f1 = 4.0;	f2 = 0.0;					break;
+		default:	f1 = 1.0;	f2 = 3.0;					break;
+			}
+		n3 += n2;
+		*q3 = (f2*v[n3-1] + f1*v[n3])/4.0;
+		}
+}
+
+// statistical basics partly based on
+// Davies, J. and Gogh, B. (2000), GSL-1.7 - The GNU scientific library
+//
+//do variance
+double d_variance(int n, double *v, double *mean, double *ss)
+{
+	int i;
+	double d, m, va, e;
+
+	for(i = 0, m = 0.0, d = 1.0; i < n; i++, d += 1.0) {
+		m += (v[i] - m)/d;
+		}
+	if (mean) *mean = m;
+	for(i = 0, va = 0.0, d = 1.0; i < n; i++, d += 1.0) {
+		e = v[i] - m;		va += (e * e - va)/d;
+		}
+	if (ss) *ss = va * (double)n;
+	return va * ((double)n/((double)(n-1)));
+}
+
+//do arithmethic mean
+double d_amean(int n, double *v)
+{
+	int i;
+	double d, mean;
+
+	for(i = 0, mean = 0.0, d = 1.0; i < n; i++, d += 1.0) {
+		mean += (v[i] - mean)/d;
+		}
+	return mean;
+}
+
+
+//do geometric mean
+double d_gmean(int n, double *v)
+{
+	int i;
+	double sum;
+
+	for(i = 0, sum = 0.0; i < n; i++) {
+		if(v[i] <= 0.0) return 0.0;
+		sum += log(v[i]);
+		}
+	return exp(sum/n);
+}
+
+//do harmonic mean
+double d_hmean(int n, double *v)
+{
+	int i;
+
+	double sum;
+
+	for(i = 0, sum = 0.0; i < n; i++) {
+		if(v[i] == 0.0) return 0.0;
+		sum += 1.0/(v[i]);
+		}
+	return (n/sum);
+}
+
+//kurtosis
+double d_kurt(int n, double *v)
+{
+	double sum, avg, sd, tmp, dn = n;
+	int i;
+
+	for(i = 0, sum = 0.0; i < n; i++) sum += v[i];
+	for(i = 0, avg = sum/dn, sum = 0.0; i < n; i++) sum += (tmp = v[i]-avg) * tmp;
+	for(i = 0, sd = sqrt(sum/(dn-1.0)), sum=0.0; i < n; i++) sum += ((tmp = (v[i]-avg)/sd)*tmp*tmp*tmp);
+	sum *= ((dn*(dn+1.0))/((dn-1.0)*(dn-2.0)*(dn-3.0)));
+	tmp = (3.0 * (dn-1.0) * (dn-1.0))/((dn-2.0)*(dn-3.0));
+	return sum - tmp;
+}
+
+//skewness
+double d_skew(int n, double *v)
+{
+	double sum, avg, sd, tmp, dn = n;
+	int i;
+
+	for(i = 0, avg = 0.0; i < n; i++) avg += ((v[i]-avg)/((double)(i+1)));
+	for(i = 0, sum = 0.0; i < n; i++) sum += (tmp = v[i]-avg) * tmp;
+	for(i = 0, sd = sqrt(sum/(dn-1.0)), sum=0.0; i < n; i++) sum += ((tmp = (v[i]-avg)/sd)*tmp*tmp);
+	return sum * dn/((dn-1.0)*(dn-2.0));
+}
+
+//---------------------------------------------------------------------------
+// Create a frequency distribution by counting the elements which may be 
+// assigned to a bin
+double d_classes(DataObj *d, double start, double step, double *v, int nv, char *range)
+{
+	int i, j, r, c, nc, *f;
+	AccRange *ar;
+
+	if(!range || !nv || !v || step <= 0.0 || !(ar = new AccRange(range))) return 0.0;
+	if(!(nc = ar->CountItems()) || !ar->GetFirst(&c, &r) || !(f=(int*)calloc(nc, sizeof(int)))) {
+		delete ar;				return 0.0;
+		}
+	for(i = 0; i < nv; i++) {
+		j = (int)(floor((v[i] - start)/step));
+		if(j < 0) j = 0;		if(j >= nc) j = (nc-1);
+		f[j]++;
+		}
+	for( ; nc > 0 && !(f[nc-1]); nc--);
+	for(i = 0; ar->GetNext(&c, &r) && i < nc; i++) {
+		d->SetValue(r, c, (double)f[i]);
+		}
+	free(f);					return ((double)nv);
+}
+
+//---------------------------------------------------------------------------
+// Pearsons linear correlation
+// (1) W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1989), 
+//    Numerical Rcipies in C. The Art of Scientific Computing, 
+//    Cambridge University Press, ISBN 0-521-35465, pp. 503 ff.
+// (2) B. Gough (2000), linear.c, gsl-1.7 the GNU scientific library
+double d_pearson(double *x, double *y, int n, char *dest, DataObj *data, double *ra)
+{
+	int j, r, c;
+	double yt, xt, t, df, res[4];
+	double syy=0.0, sxy=0.0, sxx=0.0, ay=0.0, ax=0.0;
+	AccRange *rD;
+
+
+	for(j = 0;	j < n; j++) {				// find means
+		ax += (x[j] - ax) / (j+1);			ay += (y[j] - ay) / (j+1);
+		}
+	for(j = 0; j < n; j++) {				// correlation
+		xt = x[j] - ax;						yt = y[j] - ay;
+		sxx += (xt*xt-sxx) / (j+1);			syy += (yt*yt-syy) / (j+1);
+		sxy += (xt*yt-sxy) / (j+1);
+		}
+	res[0] = sxy/sqrt(sxx*syy);				//pearsons r
+	if(dest || ra) {
+		res[1] = 0.5 * log((1.0+res[0]+_PREC)/(1.0-res[0]+_PREC));	//Fishers z-transform
+		df = n-2;
+		t = res[0]*sqrt(df/((1.0-res[0]+_PREC)*(1.0+res[0]+_PREC)));	//Student's t
+		res[2] = betai(0.5*df, 0.5, df/(df+t*t));					//probability
+		res[3] = n;
+		}
+	if((dest) && (data) && (rD = new AccRange(dest))) {
+		rD->GetFirst(&c, &r);
+		for(j = 0; j < 4 && rD->GetNext(&c, &r); j++) {
+			data->SetValue(r, c, res[j]);
+			}
+		data->Command(CMD_UPDATE, 0L, 0L);
+		delete rD;
+		}
+	if (ra){
+		memcpy(ra, res, 4 * sizeof(double));
+		}
+	return res[0];
+}
+
+//---------------------------------------------------------------------------
+// Given an array w, rank returns the rank of v1 in v
+// if v1 is not found in v 0 is returned
+double d_rank(int n, double *v, double v1)
+{
+	double *sv;
+	int i, j;
+
+	if(!n || !v) return 0.0;		if(n < 2) return 1.0;
+	if(!(sv = (double*)memdup(v, n * sizeof(double), 0))) return 0.0;
+	SortArray(n, sv);
+	for(i = j = 0; i < n; i++) {
+		if(v1 == sv[i]) {
+			for( ;(i+j)<n; j++) if(sv[i+j] > v1) break;
+			free(sv);				return (double)i + 1.0 + (((double)j-1.0)/2.0);
+			}
+		}
+	free(sv);						return 0.0;
+}
+
+//---------------------------------------------------------------------------
+// Spearman rank-order correlation
+// Ref.: W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1989), 
+//    Numerical Recipies in C. The Art of Scientific Computing, 
+//    Cambridge University Press, ISBN 0-521-35465, pp. 507 ff.
+
+//Given a sorted array w, crank replaces the elements by their rank
+void crank(int n, double *w0, double *s)
+{
+	int j=1, ji, jt;
+	double t, rank, *w = w0-1;
+
+	*s = 0.0;
+	while (j < n) {
+		if(w[j+1] != w[j]) {
+			w[j] = j;		++j;
+			}
+		else {
+			for(jt = j+1; jt <= n; jt++) if(w[jt] != w[j]) break;
+			rank = 0.5 * (j+jt-1);
+			for(ji = j; ji <= (jt-1); ji++) w[ji] = rank;
+			t = jt -j;		*s += t*t*t -t;				j = jt;
+			}
+		}
+	if(j == n) w[n] = n;
+}
+
+//the actual rank correlation
+double d_spearman(double *sx, double *sy, int n, char *dest, DataObj *data, double *ra)
+{
+	int j, r, c;
+	double *x, *y, vard, t, sg, sf, fac, en3n, en, df, aved, tmp;
+	double res[6];
+	AccRange *rD;
+
+	if(!(x = (double*)memdup(sx, n*sizeof(double), 0)) 
+		|| !(y = (double*)memdup(sy, n*sizeof(double), 0)))return 0.0;
+	SortArray2(n, x, y);			crank(n, x, &sf);
+	SortArray2(n, y, x);			crank(n, y, &sg);
+	for(j = 0, res[0] = 0.0; j < n; j++){
+		res[0] += ((tmp = (x[j]-y[j]))*tmp);
+		}
+	en = n;						en3n = en*en*en -en;
+	aved = en3n/6.0 - (sf+sg)/12.0;
+	fac = (1.0-sf/en3n)*(1.0-sg/en3n);
+	vard = ((en-1.0)*en*en*((tmp = (en+1.0))*tmp)/36.0)*fac;
+	res[1] = (res[0]-aved)/sqrt(vard);
+	res[2] = errfc(fabs(res[1])/_SQRT2);
+	res[3] = (1.0-(6.0/en3n)*(res[0]+0.5*(sf+sg)))/fac;
+	t = res[3]*sqrt((en-2.0)/((res[3]+1.0)*(1.0-res[3])));
+	df = en-2.0;	res[5] = (double)n;
+    res[4] = betai(0.5*df, 0.5, df/(df+t*t));
+	if((dest) && (data) && (rD = new AccRange(dest))) {
+		rD->GetFirst(&c, &r);
+		for(j = 0; j < 6 && rD->GetNext(&c, &r); j++) {
+			data->SetValue(r, c, res[j]);
+			}
+		data->Command(CMD_UPDATE, 0L, 0L);
+		delete rD;
+		}
+	if(ra) {
+		memcpy(ra, res, 6 * sizeof(double));
+		}
+	free(x);						free(y);
+	return res[3];
+}
+
+//---------------------------------------------------------------------------
+// Kendal's non-parametric correlation
+// Ref.: W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1989), 
+//    Numerical Recipies in C. The Art of Scientific Computing, 
+//    Cambridge University Press, ISBN 0-521-35465, pp. 510 ff.
+
+double d_kendall(double *x, double *y, int n, char *dest, DataObj *data, double *ra)
+{
+	int j, k, n1, n2, is, r, c;
+	double aa, a1, a2, sv, res[4];
+	AccRange *rD;
+
+	for (j = n1 = n2 = is = 0; j < (n-1); j++) {
+		for(k = j+1; k < n; k++) {
+			a1 = x[j] - x[k];		a2 = y[j] - y[k];		aa = a1*a2;
+			if(aa != 0.0) {
+				n1++;				n2++;
+				if (aa > 0.0) is++;
+				else is--;
+				}
+			else {
+				if(a1 != 0.0) n1++;	if(a2 != 0.0) n2++;
+				}
+			}
+		}
+	res[0] = ((double)is)/(sqrt((double)n1) * sqrt((double)n2));
+	sv = (4.0 * ((double)n) + 10.0)/(9.0*((double)n)*((double)(n-1)));
+	res[1] = res[0]/sqrt(sv);	res[2] = errfc(fabs(res[1])/_SQRT2);
+	res[3] = n;			
+	if((dest) && (data) && (rD = new AccRange(dest))) {
+		rD->GetFirst(&c, &r);
+		for(j = 0; j < 4 && rD->GetNext(&c, &r); j++) {
+			data->SetValue(r, c, res[j]);
+			}
+		data->Command(CMD_UPDATE, 0L, 0L);
+		delete rD;
+		}
+	if (ra){
+		memcpy(ra, res, 4 * sizeof(double));
+		}
+	return res[0];
+}
+
+
+//linear regression
+double d_regression(double *x, double *y, int n, char *dest, DataObj *data, double *ra)
+{
+	double sx, sy, dx, dy, sxy, sxx, syy, sdy, df;
+	double res[10];		// slope, intercept, mean x, mean y, SE of slope, 
+						//   variance(x), variance(y), variance(fit), F of regression, significance
+	int i, j, r, c;
+	AccRange *rD;
+
+	if(n < 2) return 0.0;
+	for(i = 0, 	sx = sy = 0.0; i < n; i++) {
+		sx += x[i];			sy += y[i];
+		}
+	res[2] = sx /n;			res[3] = sy/n;
+	sxy = sxx = syy = 0.0;
+	for(i = 0; i < n; i++) {
+		dx = x[i]-res[2];	dy = y[i]-res[3];
+		sxx += (dx*dx);		syy += (dy*dy);		sxy += (dx*dy);
+		}
+	res[0] = sxy / sxx;		res[1] = res[3] - res[0] * res[2];
+	for(i = 0, sdy = 0.0; i < n; i++) {
+		dy = y[i] - (res[1] + x[i] *res[0]);
+		sdy += (dy * dy);
+		}
+	sdy = sdy/(n-2);		res[4] = sqrt(sdy/sxx);		df = (n-2);
+	res[5] = sxx/(n-1);		res[6] = syy/(n-1);			res[7] = sdy;
+	res[8] = sxy/sdy*sxy/sxx;
+	res[9] = betai(df/2.0, 0.5, df/(df+res[8]));
+	if((dest) && (data) && (rD = new AccRange(dest))) {
+		rD->GetFirst(&c, &r);
+		for(j = 0; j < 10 && rD->GetNext(&c, &r); j++) {
+			data->SetValue(r, c, res[j]);
+			}
+		data->Command(CMD_UPDATE, 0L, 0L);
+		delete rD;
+		}
+	if (ra)	memcpy(ra, res, 10 * sizeof(double));
+	return n;
+}
+
+//covariance
+double d_covar(double *x, double *y, int n, char *dest, DataObj *data)
+{
+	int i;
+	double sx, sy, dx, dy, sxy;
+
+	if(n < 2) return 0.0;
+	for(i = 0, 	sx = sy = 0.0; i < n; i++) {
+		sx += x[i];			sy += y[i];
+		}
+	sx /= n;		sy /= n;		sxy = 0.0;
+	for(i = 0; i < n; i++) {
+		dx = x[i]-sx;		dy = y[i]-sy;
+		sxy += (dx*dy - sxy) / (i+1);
+		}
+	return sxy;
+}
+
+//Mann-Whitney U Test
+double d_utest(double *x, double *y, int n1, int n2, char *dest, DataObj *data, double *ra)
+{
+	double *da, *ta, u1, u2, su, su1, ts, dn1 = n1, dn2 = n2;
+	double res[9];
+	AccRange *rD;
+	int i, j, n, r, c;
+
+	if(!x || !y || n1 < 2 || n2 < 2) return 0.0;
+	da = (double*)malloc((n = (n1+n2)) * sizeof(double));
+	ta = (double*)malloc(n * sizeof(double));
+	if(!da || !ta) {
+		if(da) free(da);	if(ta) free(ta); return 0.0;
+		}
+	for(i = 0; i < n1; i++) {
+		da[i] = x[i];		ta[i] = 1.0;
+		}
+	for(j = 0; j < n2; j++) {
+		da[i] = y[j];		ta[i++] = 2.0;
+		}
+	SortArray2(n, da, ta);	crank(n, da, &ts);
+	for(i = 0, res[0] = res[1] = 0.0; i < n; i++) {
+		if(ta[i] == 1.0) res[0] += da[i];
+		else res[1] += da[i];
+		}
+	free(da);										free(ta);
+	u1 = (dn1*dn2 + (dn1*(dn1+1))/2.0) - res[0];	u2 = (dn1*dn2 + ((dn2+1)*dn2)/2.0) - res[1];
+	su = sqrt((dn1*dn2*(dn1+dn2+1))/12.0);			res[2] = u2 > u1 ? u2 : u1;
+	su1 = ((dn1*dn2)/((dn1+dn2)*(dn1+dn2-1))) * (((dn1+dn2)*(dn1+dn2)*(dn1+dn2)-(dn1+dn2)-ts)/12.0);
+	su1 = sqrt(su1);
+	res[3] = (res[2] - (n1*n2)/2.0)/su;			res[6] = errfc(res[3]/_SQRT2);
+	res[4] = n1;								res[5] = n2;
+	res[7] = (res[2] - (n1*n2)/2.0)/su1;		res[8] = errfc(res[7]/_SQRT2);
+	if((dest) && (data) && (rD = new AccRange(dest))) {
+		rD->GetFirst(&c, &r);
+		for(i = 0; i < 9 && rD->GetNext(&c, &r); i++) {
+			data->SetValue(r, c, res[i]);
+			}
+		data->Command(CMD_UPDATE, 0L, 0L);
+		delete rD;
+		}
+	if (ra)	memcpy(ra, res, 9 * sizeof(double));
+	return res[8];
+}
+
+//t-test
+double d_ttest(double *x, double *y, int n1, int n2, char *dest, DataObj *data, double *results)
+{
+	int i, r, c;
+	double sx, sy, mx, my, d, df, p;
+	double res[9];			// mean1, SD1, n1, mean2, SD2, n2, p if variances equal,
+	AccRange *rD;			//    corrected df, corrected p
+
+	d_variance(n1, x, &mx, &sx);		d_variance(n2, y, &my, &sy);
+	d = ((sx+sy)/(n1+n2-2)) * ((double)(n1+n2)/(double)(n1*n2));
+	d = (mx-my)/sqrt(d);	//Student's t
+
+	//Welch's correction for differences in variance
+	df = (sx/(double)n1)*(sx/(double)n1)/(double)(n1+1)+(sy/(double)n2)*(sy/(double)n2)/(double)(n2+1);
+	df = (sx/(double)n1+sy/(double)n2)*(sx/(double)n1+sy/(double)n2)/df;
+	df -= 2.0;		df = floor(df);
+
+//	an alternative formula for correction
+//	p = (sx/(double)n1)*(sx/(double)n1)/(double)(n1-1) + (sy/(double)n2)*(sy/(double)n2)/(double)(n2-1);
+//	df = (sx/(double)n1 + sy/(double)n2) * (sx/(double)n1 + sy/(double)n2) / p;
+
+	p = betai(df/2.0, 0.5, (df/(df+d*d)));
+	if((dest) && (data) && (rD = new AccRange(dest))) {
+		res[0] = mx;	res[1] = sqrt(sx/(double)(n1-1));	res[2] = n1;
+		res[3] = my;	res[4] = sqrt(sy/(double)(n2-1));	res[5] = n2;
+		res[7] = df;	df = (n1-1) + (n2-1);	res[6] = betai(df/2.0, 0.5, (df/(df+d*d)));
+		res[8] = p;
+		rD->GetFirst(&c, &r);
+		for(i = 0; i < 9 && rD->GetNext(&c, &r); i++) {
+			data->SetValue(r, c, res[i]);
+			}
+		data->Command(CMD_UPDATE, 0L, 0L);
+		delete rD;
+		}
+	if(results) {
+		results[0] = mx;	results[1] = sqrt(sx/(double)(n1-1));	results[2] = n1;
+		results[3] = my;	results[4] = sqrt(sy/(double)(n2-1));	results[5] = n2;
+		results[7] = df;	df = (n1-1) + (n2-1);	results[6] = betai(df/2.0, 0.5, (df/(df+d*d)));
+		results[8] = p;		results[9] = d;
+		}
+	return p;
+}
+
+//t-test for paired samples
+double d_ttest2(double *x, double *y, int n, char *dest, DataObj *data, double *ra)
+{
+	double sx, sy, mx, my, df, cov, sd, t, p;
+	int i, r, c;
+	double res[6];			// mean1, SD1, mean2, SD2, n, p 
+	AccRange *rD;
+
+	d_variance(n, x, &mx, &sx);		d_variance(n, y, &my, &sy);
+	sx = d_variance(n, x, &mx);		sy = d_variance(n, y, &my);
+	cov = d_covar(x, y, n, 0L, 0L) * ((double)n/(double)(n-1));
+	sd = sqrt((sx+sy-2*cov)/n);
+	t = (mx-my)/sd;					df = (n-1);
+	p = betai(0.5*df, 0.5, df/(df+t*t));
+	res[0] = mx;	res[1] = sqrt(sx);	res[5] = p;
+	res[2] = my;	res[3] = sqrt(sy);	res[4] = n;
+	if((dest) && (data) && (rD = new AccRange(dest))) {
+		rD->GetFirst(&c, &r);
+		for(i = 0; i < 6 && rD->GetNext(&c, &r); i++) {
+			data->SetValue(r, c, res[i]);
+			}
+		data->Command(CMD_UPDATE, 0L, 0L);
+		delete rD;
+		}
+	if (ra)	memcpy(ra, res, 6 * sizeof(double));
+	return p;
+}
+
+//f-test
+double d_ftest(double *x, double *y, int n1, int n2, char *dest, DataObj *data, double *ra)
+{
+	int i, r, c;
+	double sx, sy, mx, my, d, df1, df2, p;
+	double res[6];			// mean1, SD1, n1, mean2, SD2, n2
+	AccRange *rD;
+
+	for(i=0, sx = 0.0; i < n1; sx += x[i], i++);				mx = sx/n1;
+	for(i=0, sy = 0.0; i < n2; sy += y[i], i++);				my = sy/n2;
+	for(i=0, sx = 0.0; i < n1; sx += ((d=(x[i]-mx))*d), i++);	sx /= (n1-1);
+	for(i=0, sy = 0.0; i < n2; sy += ((d=(y[i]-my))*d), i++);	sy /= (n2-1);
+	if(sx > sy) {
+		d = sx/sy;		df1 = n1-1;		df2 = n2-1;
+		}
+	else {
+		d = sy/sx;		df1 = n2-1;		df2 = n1-1;
+		}
+	p = 2.0 * betai(df2/2.0, df1/2.0, df2/(df2+df1*d));
+	if(p > 1.0) p = 2.0-p;
+	res[0] = mx;	res[1] = sqrt(sx);	res[2] = n1;
+	res[3] = my;	res[4] = sqrt(sy);	res[5] = n2;
+	if((dest) && (data) && (rD = new AccRange(dest))) {
+		rD->GetFirst(&c, &r);
+		for(i = 0; i < 6 && rD->GetNext(&c, &r); i++) {
+			data->SetValue(r, c, res[i]);
+			}
+		data->Command(CMD_UPDATE, 0L, 0L);
+		delete rD;
+		}
+	if (ra)	memcpy(ra, res, 6 * sizeof(double));
+	return p;
+}
+//---------------------------------------------------------------------------
+// Simple one way anova
+//---------------------------------------------------------------------------
+bool do_anova1(int n, int *nv, double **vals, double **res_tab, double *gm, double **means, double **ss)
+{
+	int i, j, ntot;
+	double tmp, *csums, *css, ssa, ssw, sst, mtot, d;
+
+	if(!(csums = (double*)calloc(n+1, sizeof(double)))
+		|| !(css = (double*)calloc(n+1, sizeof(double)))) return false;
+
+	for(i = ntot = 0, mtot = 0.0, d = 1.0; i< n; i++){
+		for(j = 0, csums[i] = 0.0, tmp = 1.0; j < nv[i]; j++, d+=1.0, tmp +=1.0) {
+			mtot += (vals[i][j] - mtot)/d;		
+			csums[i] += (vals[i][j] -csums[i])/tmp;
+			}
+		ntot += nv[i];
+		}
+	for(i = 0; i < n; i++) {
+		for(j = 0, css[i] = 0.0; j < nv[i]; j++) {
+			tmp = vals[i][j] - csums[i];	css[i] += (tmp*tmp);
+			}
+		}
+	for(i = 0, ssa = ssw = sst = 0.0;  i < n; i++) {
+		tmp =(csums[i] - mtot);		ssa += (tmp*tmp) * ((double)nv[i]);
+		ssw += css[i];
+		}
+	sst = ssa + ssw;
+	res_tab[0][0] = n - 1;				res_tab[1][0] = ntot - n;
+	res_tab[2][0] = ntot -1;			res_tab[0][1] = ssa;
+	res_tab[1][1] = ssw;				res_tab[2][1] = sst;
+	res_tab[0][2] = ssa/res_tab[0][0];	res_tab[1][2] = ssw/res_tab[1][0];
+	res_tab[0][3] = res_tab[0][2]/res_tab[1][2];
+	res_tab[0][4] = f_dist(res_tab[0][3], res_tab[0][0], res_tab[1][0]);
+	if(gm) *gm = mtot;
+	if(means) *means = csums;			else free(csums);
+	if(ss) *ss = css;					else free(css);
+	return true;
+}
+
+//---------------------------------------------------------------------------
+// Bartlett's Test for homogeneity of variances
+// RR Sokal & FJ Rohlf: Biometry, 3rd ed., pp. 398 ff.
+//---------------------------------------------------------------------------
+bool bartlett(int n, int *nc, double *ss, double *chi2)
+{
+	int i, sdf, df;
+	double mss, mlss, *lnss, cf;
+
+	if(!n || !nc || !ss || !chi2) return false;
+	if(!(lnss = (double*)malloc(n * sizeof(double))))return false;
+	for(i = sdf = 0, mss = mlss = cf = 0.0; i < n; i++) {
+		sdf += (df = nc[i]-1);				lnss[i] = log(ss[i]);
+		mss += (ss[i] * ((double)df));		mlss += (lnss[i] * ((double)df)); 
+		cf += (1.0/((double)df));
+		}
+	*chi2 = ((double)sdf) * log(mss/((double)sdf)) - mlss;
+	cf -= (1.0/((double)sdf));				cf = 1.0 + cf/(3.0 * ((double)(n-1)));
+	*chi2 /= cf;
+	// P = chi_dist(*chi2, n-1, 0);
+	free(lnss);		return true;
+}
+//---------------------------------------------------------------------------
+// Leven's Test for homogeneity of variances
+//---------------------------------------------------------------------------
+bool levene(int type, int n, int *nv, double *means, double **vals, double *F, double *P)
+{
+	int i, j;
+	bool bRet = false;
+	double cm, **res_tab, **cols;
+	
+	if(!n || !nv || !means || !vals) return false;
+	//setup matrix for results
+	if((res_tab = (double**)calloc(3, sizeof(double*)))
+		&& (res_tab[0] = (double*) malloc(5*sizeof(double)))
+		&& (res_tab[1] = (double*) malloc(5*sizeof(double)))
+		&& (res_tab[2] = (double*) malloc(5*sizeof(double)))
+		&& (cols = (double**)calloc(n+1, sizeof(double*)))) bRet = true;
+	//allocate mem for data
+	for(i = 0; bRet && i<n; i++) {
+		if(!(cols[i]=(double*)malloc((nv[i]+1)*sizeof(double)))) bRet = false;
+		}
+	//data are absolute differences to mean ...
+	for(i = 0, cm = 0.0; bRet && i < n; i++) {
+		switch(type) {
+			case 1:			//use means
+				cm = means[i];								break;
+			case 2:			//use medians
+				d_quartile(nv[i], vals[i], 0L, &cm, 0L);	break;
+			}
+		for(j = 0; j < nv[i]; j++) {
+			cols[i][j] = vals[i][j] > cm ? vals[i][j] - cm : cm - vals[i][j];
+			}
+		}
+	//Levene's test statistic is based on ANOVA of the differences
+	if(bRet && (bRet = do_anova1(n, nv, cols, res_tab, 0L, 0L, 0L))){
+		if(F) *F = res_tab[0][3];				if(P) *P = res_tab[0][4];
+		}
+	//clean up
+	if(bRet) {
+		for(i = 0; i < n; i++) if(cols[i]) free(cols[i]);
+		for(i = 0; i < 3; i++) if(res_tab[i]) free(res_tab[i]);
+		free(cols);								free(res_tab);	
+		}
+	return bRet;
+}
+
+//---------------------------------------------------------------------------
+// Modules from the R-project
+//
+//---------------------------------------------------------------------------
+#define M_1_SQRT_2PI	0.398942280401432677939946059934	/* 1/sqrt(2pi) */
+/*
+ *  Copyright (C) 1998       Ross Ihaka
+ *  Copyright (C) 2000--2005 The R Development Core Team
+ *
+ *  This program 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.
+ *  This program 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *  DESCRIPTION
+ *    Computes the probability that the maximum of rr studentized
+ *    ranges, each based on cc means and with df degrees of freedom
+ *    for the standard error, is less than q.
+ *    The algorithm is based on that of the reference.
+ *
+ *  REFERENCE
+ *    Copenhaver, Margaret Diponzio & Holland, Burt S.
+ *    Multiple comparisons of simple effects in
+ *    the two-way analysis of variance with fixed effects.
+ *    Journal of Statistical Computation and Simulation,
+ *    Vol.30, pp.1-15, 1988.
+ */
+
+double wprob(double w, double rr, double cc)
+{
+/*  wprob() :
+
+	This function calculates probability integral of Hartley's
+	form of the range.
+
+	w     = value of range
+	rr    = no. of rows or groups
+	cc    = no. of columns or treatments
+	ir    = error flag = 1 if pr_w probability > 1
+	pr_w = returned probability integral from (0, w)
+
+	program will not terminate if ir is raised.
+
+	bb = upper limit of legendre integration
+	iMax = maximum acceptable value of integral
+	nleg = order of legendre quadrature
+	ihalf = int ((nleg + 1) / 2)
+	wlar = value of range above which wincr1 intervals are used to
+	       calculate second part of integral,
+	       else wincr2 intervals are used.
+	C1, C2, C3 = values which are used as cutoffs for terminating
+           or modifying a calculation.
+	xleg = legendre 12-point nodes
+	aleg = legendre 12-point coefficients
+ */
+#define nleg	12
+#define ihalf	6
+
+    /* looks like this is suboptimal for double precision.
+       (see how C1-C3 are used) <MM> */
+    /* const double iMax  = 1.; not used if = 1*/
+    const static double C1 = -30.0, C2 = -50.0, C3 = 60.;
+    const static double bb = 8.0, wlar = 3.0, wincr1 = 2.0, wincr2 = 3.;
+    const static double xleg[ihalf] = {	0.981560634246719250690549090149,
+	0.904117256370474856678465866119,	0.769902674194304687036893833213,
+	0.587317954286617447296702418941,	0.367831498998180193752691536644,
+	0.125233408511468915472441369464};
+    const static double aleg[ihalf] = {	0.047175336386511827194615961485,
+	0.106939325995318430960254718194,	0.160078328543346226334652529543,
+	0.203167426723065921749064455810,	0.233492536538354808760849898925,
+	0.249147045813402785000562436043};
+    double a, ac, pr_w, b, binc, blb, bub, c, cc1, einsum, elsum,
+		pminus, pplus, qexpo, qsqz, rinsum, wi, wincr, xx;
+    int j, jj;
+
+    qsqz = w * 0.5;
+
+    // if w >= 16 then the integral lower bound (occurs for c=20)
+    // is 0.99999999999995 so return a value of 1
+	if (qsqz >= bb)	return 1.0;
+
+	// find (f(w/2) - 1) ^ cc
+    // (first term in integral of hartley's form). 
+	pr_w = 2.0 * norm_dist(qsqz, 0.0, 1.0) -1.0;
+    // if pr_w ^ cc < 2e-22 then set pr_w = 0 
+    if (pr_w >= exp(C2 / cc)) pr_w = pow(pr_w, cc);
+    else pr_w = 0.0;
+    // if w is large then the second component of the
+    // integral is small, so fewer intervals are needed.
+    if (w > wlar) wincr = wincr1;
+    else wincr = wincr2;
+
+    /* find the integral of second term of hartley's form */
+    /* for the integral of the range for equal-length */
+    /* intervals using legendre quadrature.  limits of */
+    /* integration are from (w/2, 8).  two or three */
+    /* equal-length intervals are used. */
+    /* blb and bub are lower and upper limits of integration. */
+    blb = qsqz;			    binc = (bb - qsqz) / wincr;
+    bub = blb + binc;	    einsum = 0.0;
+
+    // integrate over each interval
+    cc1 = cc - 1.0;
+    for (wi = 1; wi <= wincr; wi++) {
+		elsum = 0.0;		a = 0.5 * (bub + blb);
+		// legendre quadrature with order = nleg
+		b = 0.5 * (bub - blb);
+		for (jj = 1; jj <= nleg; jj++) {
+			if (ihalf < jj) {
+				j = (nleg - jj) + 1;		xx = xleg[j-1];
+				}
+			else {
+				j = jj;						xx = -xleg[j-1];
+				}
+			c = b * xx;					    ac = a + c;
+			// if exp(-qexpo/2) < 9e-14, then doesn't contribute to integral
+			if ((qexpo = ac * ac) > C3) break;
+			pplus = 2.0 * norm_dist(ac, 0.0, 1.0);    pminus= 2.0 * norm_dist(ac, w, 1.0);
+			// if rinsum ^ (cc-1) < 9e-14, then doesn't contribute to integral
+			rinsum = (pplus * 0.5) - (pminus * 0.5);
+			if (rinsum >= exp(C1 / cc1)) {
+				rinsum = (aleg[j-1] * exp(-(0.5 * qexpo))) * pow(rinsum, cc1);
+				elsum += rinsum;
+				}
+			}
+		elsum *= (((2.0 * b) * cc) * M_1_SQRT_2PI);
+		einsum += elsum;		blb = bub;			bub += binc;
+		}
+	// if pr_w ^ rr < 9e-14, then return 0 */
+	pr_w = einsum + pr_w;
+	if (pr_w <= exp(C1 / rr))return 0.;
+    pr_w = pow(pr_w, rr);
+ 	return pr_w < 1.0 ? pr_w : 1.0;
+}
+
+double ptukey(double q, double rr, double cc, double df, int lower_tail, int log_p)
+{
+/* 	q = value of studentized range
+	rr = no. of rows or groups
+	cc = no. of columns or treatments
+	df = degrees of freedom of error term
+	ir[0] = error flag = 1 if wprob probability > 1
+	ir[1] = error flag = 1 if qprob probability > 1
+
+	All references in wprob to Abramowitz and Stegun
+	are from the following reference:
+		Abramowitz, Milton and Stegun, Irene A.
+		Handbook of Mathematical Functions.
+		New York:  Dover publications, Inc. (1970).
+	All constants taken from this text are given to 25 significant digits.
+
+	nlegq = order of legendre quadrature
+	ihalfq = int ((nlegq + 1) / 2)
+	eps = max. allowable value of integral
+	eps1 & eps2 = values below which there is no contribution to integral.
+
+	d.f. <= dhaf:	integral is divided into ulen1 length intervals.  else
+	d.f. <= dquar:	integral is divided into ulen2 length intervals.  else
+	d.f. <= deigh:	integral is divided into ulen3 length intervals.  else
+	d.f. <= dlarg:	integral is divided into ulen4 length intervals.
+
+	d.f. > dlarg:	the range is used to calculate integral.
+
+	xlegq = legendre 16-point nodes
+	alegq = legendre 16-point coefficients
+
+	The coefficients and nodes for the legendre quadrature used in
+	qprob and wprob were calculated using the algorithms found in:
+		Stroud, A. H. and Secrest, D.,	Gaussian Quadrature Formulas.
+		Englewood Cliffs, New Jersey:  Prentice-Hall, Inc, 1966.
+
+	All values matched the tables (provided in same reference)
+	to 30 significant digits.
+
+	f(x) = .5 + erf(x / sqrt(2)) / 2      for x > 0
+	f(x) = erfc( -x / sqrt(2)) / 2	      for x < 0
+	where f(x) is standard normal c. d. f.
+
+	if degrees of freedom large, approximate integral with range distribution.
+ */
+#define nlegq	16
+#define ihalfq	8
+
+/*  const double eps = 1.0; not used if = 1 */
+    const static double eps1 = -30.0, eps2 = 1.0e-14;
+    const static double dhaf  = 100.0, dquar = 800.0, deigh = 5000.0, dlarg = 25000.0;
+    const static double ulen1 = 1.0, ulen2 = 0.5, ulen3 = 0.25, ulen4 = 0.125;
+    const static double xlegq[ihalfq] = { 0.989400934991649932596154173450,
+	0.944575023073232576077988415535, 0.865631202387831743880467897712,
+	0.755404408355003033895101194847, 0.617876244402643748446671764049,
+	0.458016777657227386342419442984, 0.281603550779258913230460501460,
+	0.950125098376374401853193354250e-1};
+    const static double alegq[ihalfq] = {0.271524594117540948517805724560e-1,
+	0.622535239386478928628438369944e-1, 0.951585116824927848099251076022e-1,
+	0.124628971255533872052476282192, 0.149595988816576732081501730547,
+	0.169156519395002538189312079030, 0.182603415044923588866763667969,
+	0.189450610455068496285396723208};
+    double ans, f2, f21, f2lf, ff4, otsum, qsqz, rotsum, t1, twa1, ulen, wprb;
+    int i, j, jj;
+
+    if (df > dlarg)	return wprob(q, rr, cc);
+    f2 = df * 0.5;								// calculate leading constant
+    f2lf = ((f2 * log(df)) - (df * log(2.0))) - gammln(f2);
+    f21 = f2 - 1.0;
+    // integral is divided into unit, half-unit, quarter-unit, or eighth-unit length intervals 
+	//    depending on the value of the degrees of freedom.
+    ff4 = df * 0.25;
+    if	    (df <= dhaf)	ulen = ulen1;
+    else if (df <= dquar)	ulen = ulen2;
+    else if (df <= deigh)	ulen = ulen3;
+    else					ulen = ulen4;
+    f2lf += log(ulen);
+    for (i = 1, ans = 0.0; i <= 50; i++) {		// integrate over each subinterval
+		otsum = 0.0;
+		// legendre quadrature with order = nlegq, nodes (stored in xlegq) are symmetric around zero.
+		twa1 = (2 * i - 1) * ulen;
+		for (jj = 1; jj <= nlegq; jj++) {
+			if (ihalfq < jj) {
+				j = jj - ihalfq - 1;
+				t1 = (f2lf + (f21 * log(twa1 + (xlegq[j] * ulen)))) - (((xlegq[j] * ulen) + twa1) * ff4);
+				} 
+			else {
+				j = jj - 1;
+				t1 = (f2lf + (f21 * log(twa1 - (xlegq[j] * ulen)))) + (((xlegq[j] * ulen) - twa1) * ff4);
+				}
+			if (t1 >= eps1) {			// if exp(t1) < 9e-14, then doesn't contribute to integral 
+				if (ihalfq < jj) qsqz = q * sqrt(((xlegq[j] * ulen) + twa1) * 0.5);
+				else qsqz = q * sqrt(((-(xlegq[j] * ulen)) + twa1) * 0.5);
+				wprb = wprob(qsqz, rr, cc);		// call wprob to find integral of range portion
+				rotsum = (wprb * alegq[j]) * exp(t1);			otsum += rotsum;
+				}
+			}									// end legendre integral for interval i
+		// If integral for interval i < 1e-14, then stop. However, in order to avoid small area 
+		//    under left tail, at least  1 / ulen  intervals are calculated.
+		if (i * ulen >= 1.0 && otsum <= eps2) break;
+		ans += otsum;							//end of interval i 
+		}
+	return ans > 1.0 ? 1.0 : ans;
+ }
+
+ /*
+ *  Copyright (C) 1998 	     Ross Ihaka
+ *  Copyright (C) 2000--2005 The R Development Core Team
+ *  based in part on AS70 (C) 1974 Royal Statistical Society
+ *
+ *  This program 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.
+ *  This program 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ *  DESCRIPTION
+ *	Computes the quantiles of the maximum of rr studentized
+ *	ranges, each based on cc means and with df degrees of freedom
+ *	for the standard error, is less than q.
+ *	The algorithm is based on that of the reference.
+ *
+ *  REFERENCE
+ *	Copenhaver, Margaret Diponzio & Holland, Burt S., Multiple comparisons of simple
+ *	effects in the two-way analysis of variance with fixed effects.
+ *	Journal of Statistical Computation and Simulation, Vol.30, pp.1-15, 1988.
+ */
+
+/* qinv() :
+ *	this function finds percentage point of the studentized range
+ *	which is used as initial estimate for the secant method.
+ *	function is adapted from portion of algorithm as 70
+ *	from applied statistics (1974) ,vol. 23, no. 1
+ *	by odeh, r. e. and evans, j. o.
+ *	  p = percentage point
+ *	  c = no. of columns or treatments
+ *	  v = degrees of freedom
+ *	  qinv = returned initial estimate
+ *	vmax is cutoff above which degrees of freedom
+ *	is treated as infinity.
+ */
+
+static double qinv(double p, double c, double v)
+{
+    const static double p0 = 0.322232421088, q0 = 0.993484626060e-01;
+    const static double p1 = -1.0, q1 = 0.588581570495;
+    const static double p2 = -0.342242088547, q2 = 0.531103462366;
+    const static double p3 = -0.204231210125, q3 = 0.103537752850;
+    const static double p4 = -0.453642210148e-04, q4 = 0.38560700634e-02;
+    const static double c1 = 0.8832, c2 = 0.2368, c3 = 1.214, c4 = 1.208, c5 = 1.4142;
+    const static double vmax = 120.0;
+    double ps, q, t, yi;
+
+    ps = 0.5 - 0.5 * p;
+    yi = sqrt (log (1.0 / (ps * ps)));
+    t = yi + (((( yi * p4 + p3) * yi + p2) * yi + p1) * yi + p0)
+	   / (((( yi * q4 + q3) * yi + q2) * yi + q1) * yi + q0);
+    if (v < vmax) t += (t * t * t + t) / v / 4.0;
+    q = c1 - c2 * t;
+    if (v < vmax) q += -c3 / v + c4 * t / v;
+    return t * (q * log (c - 1.0) + c5);
+}
+
+/*
+ *  Copenhaver, Margaret Diponzio & Holland, Burt S.
+ *  Multiple comparisons of simple effects in
+ *  the two-way analysis of variance with fixed effects.
+ *  Journal of Statistical Computation and Simulation,
+ *  Vol.30, pp.1-15, 1988.
+ *
+ *  Uses the secant method to find critical values.
+ *
+ *  p = confidence level (1 - alpha)
+ *  rr = no. of rows or groups
+ *  cc = no. of columns or treatments
+ *  df = degrees of freedom of error term
+ *
+ */
+double qtukey(double p, double rr, double cc, double df, int lower_tail, int log_p)
+{
+    const int maxiter = 50;
+    double ans = HUGE_VAL, valx0, valx1, x0, x1;
+    int iter;
+
+    // df must be > 1 ; there must be at least two values 
+	if(p >= 1.0 || df < 2 || rr < 1 || cc < 2) return HUGE_VAL;
+	if(p < 0.0) p = 0.0;
+    x0 = qinv(p, cc, df);									// Initial value
+    valx0 = ptukey(x0, rr, cc, df, true, false) - p;		// Find prob(value < x0)
+    // Find the second iterate and prob(value < x1). If the first iterate has probability value 
+    // exceeding p then second iterate is 1 less than first iterate; otherwise it is 1 greater.
+	x1 = valx0 > 0.0 ? (x1 = x0 > 1.0 ? x0-1.0 : 0.0) : (x0 + 1.0);
+    valx1 = ptukey(x1, rr, cc, df, true, false) - p;
+    for(iter=1; iter < maxiter ; iter++) {					// Iterate
+		ans = x1 - ((valx1 * (x1 - x0)) / (valx1 - valx0));
+		valx0 = valx1;		x0 = x1;
+		if (ans < 0.0) {									// New iterate must be >= 0
+			ans = 0.0;			valx1 = -p;
+			}
+		valx1 = ptukey(ans, rr, cc, df, true, false) - p;	//  Find prob(value < new iterate)
+		x1 = ans;
+		if (fabs(x1 - x0) < _PREC)	return ans;				// Convergence ?
+		}
+    //The process did not converge in 'maxiter' iterations 
+    return ans;
+}
+//---------------------------------------------------------------------------
+// END Modules from the R-project
+
+
+//---------------------------------------------------------------------------
+// Calendar, Date- and Time functions
+// The following characters are used as format specifiers in a format string,
+//    all other characters are either ignored or copyied to the output
+//
+//    Y   four digits year               y    two digits year
+//    X   month's full name              x    three character month name
+//    Z   two digits day of month        z    same as Z but no leading zero
+//    V   two digit month number         v    number of month
+//    W   single letter month
+//    D   full name of day               d    three characters for day name
+//    E   two digits weekday             e    one or two digits weekday
+//    F   single character day name
+//    H   two digits for hours           h    hours with no leading zero
+//    M   two digits for minutes         m    minutes with no leading zero
+//    S   two digits for seconds         s    seconds with no leading zero
+//    T   two digits seconds, two dec.   t    same as T but no leading zero
+//    U   full precision seconds
+
+static char *dt_month[] = {"January", "February", "March", "April", "May", "June",
+	"July", "August", "September", "October", "November", "December"};
+
+static char *dt_months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
+	"Sep", "Oct", "Nov", "Dec"};
+
+static int dt_monthl[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+static char *dt_day[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday",
+	"Friday", "Saturday"};
+
+static char *dt_days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"};
+
+static bool leapyear(int year) {
+	return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
+}
+
+int year2aday(int y)
+{
+	int aday, y1;
+
+	y1 = y - 1900;
+	aday = y1 * 365;
+	aday += ((y1-1) >> 2 );
+	aday -= (y1 / 100);
+	aday += ((y/400)-4);
+	return aday;
+}
+
+static void set_dow(rlp_datetime *dt)
+{
+	dt->dow = (dt->aday %7)+1;
+}
+
+void add_date(rlp_datetime *base, rlp_datetime *inc)
+{
+	int i, dom;
+
+	if(base) {
+		if(base->month < 1) base->month = 1;
+		if(inc) {
+			base->seconds += inc->seconds;
+			if(base->seconds >= 60.0) {
+				base->minutes++;		base->seconds -= 60.0;
+				}
+			base->minutes += inc->minutes;
+			if(base->minutes >= 60) {
+				base->hours++;			base->minutes -= 60;
+				}
+			base->hours += inc->hours;
+			if(base->hours >= 24) {
+				base->dom++;			base->hours -= 24;
+				}
+			base->year += inc->year;	base->dom += inc->dom;
+			base->month += inc->month;
+			}
+		dom = dt_monthl[base->month-1];
+		if(leapyear(base->year) && base->month == 2) dom = 29;
+		if(base->dom > dom) {
+			base->month++;			base->dom -= dom;
+			}
+		if(base->month > 12) {
+			base->year++;			base->month -= 12;
+			}
+		base->aday = year2aday(base->year);
+		for(i = base->doy = 0; i < (base->month-1); i++) {
+			dom = dt_monthl[i];
+			if(i == 1 && leapyear(base->year)) dom = 29;
+			base->doy += dom;
+			}
+		base->doy += base->dom;
+		base->aday += base->doy;	set_dow(base);
+		}
+}
+
+static int parse_date (rlp_datetime *dt, char *src, char *fmt)
+{
+	int i, j, k;
+	char tmp_str[10];
+
+	if(!src || !src[0] || !fmt || !fmt[0]) return 0;
+	if(*src == '\'') src++;
+	for(i = j = 0; fmt[i] && src[j]; i++) {
+		switch (fmt[i]) {
+		case 'Y':		case 'y':			// year is numeric
+			if(j && src[j] == '-' || src[j] == '/' || src[j] == '.') j++;
+#ifdef USE_WIN_SECURE
+			if(sscanf_s(src+j, "%d", &dt->year)) {
+#else
+			if(sscanf(src+j, "%d", &dt->year)) {
+#endif
+				if(dt->year < 0) return 0;
+				while(isdigit(src[j])) j++;
+				if(dt->year<60) dt->year += 2000;
+				else if(dt->year <99) dt->year += 1900;
+				}
+			else return 0;
+			break;
+		case 'X':		case 'x':			// month can be text
+			if(j && src[j] == '-' || src[j] == '/' || src[j] == '.') j++;
+			tmp_str[0] = toupper(src[j]);
+			tmp_str[1] = tolower(src[j+1]);
+			tmp_str[2] = tolower(src[j+2]);
+			tmp_str[3] = 0;
+			for(k = dt->month = 0; k < 12; k++) {
+				if(0 == strcmp(tmp_str,dt_months[k])) {
+					dt->month = k+1;			break;
+					}
+				}
+			if(dt->month) while(isalpha(src[j])) j++;
+			else return 0;
+			break;
+		case 'V':		case 'v':			//    or numeric
+			if(j && src[j] == '-' || src[j] == '/' || src[j] == '.') j++;
+#ifdef USE_WIN_SECURE
+			if(sscanf_s(src+j, "%d", &dt->month)) {
+#else
+			if(sscanf(src+j, "%d", &dt->month)) {
+#endif
+				if(dt->month <= 0 || dt->month > 12) return 0;
+				j++;				if(isdigit(src[j])) j++;
+				}
+			else return 0;
+			break;
+		case 'Z':		case 'z':			// day of month is numeric
+			if(j && src[j] == '-' || src[j] == '/' || src[j] == '.') j++;
+#ifdef USE_WIN_SECURE
+			if(sscanf_s(src+j, "%d", &dt->dom)) {
+#else
+			if(sscanf(src+j, "%d", &dt->dom)) {
+#endif
+				if(dt->dom <= 0 || dt->dom > 31) return 0;
+				j++;				if(isdigit(src[j])) j++;
+				}
+			else return 0;
+			break;
+		case 'H':		case 'h':			// hours are numeric
+#ifdef USE_WIN_SECURE
+			if(sscanf_s(src+j, "%2d", &dt->hours)) {
+#else
+			if(sscanf(src+j, "%2d", &dt->hours)) {
+#endif
+				if(dt->hours < 0 || dt->hours > 23) return 0;
+				j++;				if(isdigit(src[j])) j++;
+				}
+			else return 0;
+			break;
+		case 'M':		case 'm':			// minutes are numeric
+			if(j && src[j] == ' ' || src[j] == ':') j++;
+#ifdef USE_WIN_SECURE
+			if(sscanf_s(src+j, "%2d", &dt->minutes)) {
+#else
+			if(sscanf(src+j, "%2d", &dt->minutes)) {
+#endif
+				if(dt->minutes < 0 || dt->minutes >= 60) return 0;
+				j++;				if(isdigit(src[j])) j++;
+				}
+			else return 0;
+			break;
+		case 'S':		case 's':			// seconds are numeric
+		case 'T':		case 't':
+			if(j && src[j] == ' ' || src[j] == ':') j++;
+#ifdef USE_WIN_SECURE
+			if(sscanf_s(src+j, "%lf", &dt->seconds)) {
+#else
+			if(sscanf(src+j, "%lf", &dt->seconds)) {
+#endif
+				if(dt->seconds < 0.0 || dt->seconds >= 60.0) return 0;
+				while(isdigit(src[j]) || src[j] == '.') j++;
+				}
+			else return 0;
+			dt->seconds += 1.0e-12;
+			break;
+		default:
+			if(fmt[i] && fmt[i] == src[j]) j++;
+			}
+		}
+	if(dt->year && dt->month && dt->dom) {
+		for(dt->doy = 0, i = dt->month-2; i >= 0; i--) {
+			if(i == 1) dt->doy += leapyear(dt->year) ? 29 : 28;
+			else dt->doy += dt_monthl[i]; 
+			}
+		dt->doy += dt->dom;
+		if(dt->year >= 1900) dt->aday = year2aday(dt->year);
+		dt->aday += dt->doy;
+		}
+	return j;
+}
+
+char *date2text(rlp_datetime *dt, char *fmt)
+{
+	static char res[80];
+	int i, pos;
+	double secs;
+
+	res[0] = 0;
+	if(!fmt || !fmt[0] || !dt) return res;
+	set_dow(dt);
+	secs = dt->seconds;
+	if (secs > 59.4999) secs = 59.4999;
+	for(pos = i = 0; fmt[i] && pos < 70; i++) {
+#ifdef USE_WIN_SECURE
+		switch(fmt[i]) {
+		case 'Y':
+			if(dt->year) pos+=sprintf_s(res+pos, 80-pos, "%4d", dt->year);
+			else pos += sprintf_s(res+pos, 80-pos, "####");			break;
+		case 'y':
+			if(dt->year) pos+=sprintf_s(res+pos, 80-pos, "%02d", (dt->year %100));
+			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
+		case 'Z':
+			if(dt->dom) pos+=sprintf_s(res+pos, 80-pos, "%02d", dt->dom);
+			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
+		case 'z':
+			if(dt->dom) pos+=sprintf_s(res+pos, 80-pos, "%d", dt->dom);
+			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
+		case 'X':
+			if(dt->month >0 && dt->month < 13) pos+=sprintf_s(res+pos, 80-pos, "%s", dt_month[dt->month-1]);
+			else pos += sprintf_s(res+pos, 80-pos, "###");			break;
+		case 'x':
+			if(dt->month >0 && dt->month < 13) pos+=sprintf_s(res+pos, 80-pos, "%s", dt_months[dt->month-1]);
+			else pos += sprintf_s(res+pos, 80-pos, "###");			break;
+		case 'V':
+			if(dt->month >0 && dt->month < 13) pos+=sprintf_s(res+pos, 80-pos, "%02d", dt->month);
+			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
+		case 'v':
+			if(dt->month >0 && dt->month < 13) pos+=sprintf_s(res+pos, 80-pos, "%d", dt->month);
+			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
+		case 'W':
+			if(dt->month >0 && dt->month < 13) pos+=sprintf_s(res+pos, 80-pos, "%c", dt_month[dt->month-1][0]);
+			else pos += sprintf_s(res+pos, 80-pos, "#");			break;
+		case 'D':
+			if(dt->dow >0 && dt->dow < 8) pos+=sprintf_s(res+pos, 80-pos, "%s", dt_day[dt->dow-1]);
+			else pos += sprintf_s(res+pos, 80-pos, "###");			break;
+		case 'd':
+			if(dt->dow >0 && dt->dow < 8) pos+=sprintf_s(res+pos, 80-pos, "%s", dt_days[dt->dow-1]);
+			else pos += sprintf_s(res+pos, 80-pos, "###");			break;
+		case 'E':
+			if(dt->dow >0 && dt->dow < 8) pos+=sprintf_s(res+pos, 80-pos, "%02d", dt->dow);
+			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
+		case 'e':
+			if(dt->dow >0 && dt->dow < 8) pos+=sprintf_s(res+pos, 80-pos, "%d", dt->dow);
+			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
+		case 'F':
+			if(dt->dow >0 && dt->dow < 8) pos+=sprintf_s(res+pos, 80-pos, "%c", dt_day[dt->dow-1][0]);
+			else pos += sprintf_s(res+pos, 80-pos, "#");			break;
+		case 'H':
+			if(dt->hours >=0 && dt->hours < 24) pos+=sprintf_s(res+pos, 80-pos, "%02d", dt->hours);
+			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
+		case 'h':
+			if(dt->hours >=0 && dt->hours < 24) pos+=sprintf_s(res+pos, 80-pos, "%d", dt->hours);
+			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
+		case 'M':
+			if(dt->minutes >=0 && dt->minutes < 60) pos+=sprintf_s(res+pos, 80-pos, "%02d", dt->minutes);
+			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
+		case 'm':
+			if(dt->minutes >=0 && dt->minutes < 60) pos+=sprintf_s(res+pos, 80-pos, "%d", dt->minutes);
+			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
+		case 'S':
+			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf_s(res+pos, 80-pos, "%02d", iround(secs));
+			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
+		case 's':
+			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf_s(res+pos, 80-pos, "%d", iround(secs));
+			else pos += sprintf_s(res+pos, 80-pos, "##");			break;
+		case 'T':
+			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf_s(res+pos, 80-pos, "%02.2lf", dt->seconds);
+			else pos += sprintf_s(res+pos, 80-pos, "##.##");		break;
+		case 't':
+			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf_s(res+pos, 80-pos, "%.2lf", dt->seconds);
+			else pos += sprintf_s(res+pos, 80-pos, "##.##");		break;
+		default:
+			pos += sprintf_s(res+pos, 80-pos, "%c", fmt[i]);		break;
+			}
+#else
+		switch(fmt[i]) {
+		case 'Y':
+			if(dt->year) pos+=sprintf(res+pos, "%4d", dt->year);
+			else pos += sprintf(res+pos, "####");		break;
+		case 'y':
+			if(dt->year) pos+=sprintf(res+pos, "%02d", (dt->year %100));
+			else pos += sprintf(res+pos, "##");			break;
+		case 'Z':
+			if(dt->dom) pos+=sprintf(res+pos, "%02d", dt->dom);
+			else pos += sprintf(res+pos, "##");			break;
+		case 'z':
+			if(dt->dom) pos+=sprintf(res+pos, "%d", dt->dom);
+			else pos += sprintf(res+pos, "##");			break;
+		case 'X':
+			if(dt->month >0 && dt->month < 13) pos+=sprintf(res+pos, "%s", dt_month[dt->month-1]);
+			else pos += sprintf(res+pos, "###");		break;
+		case 'x':
+			if(dt->month >0 && dt->month < 13) pos+=sprintf(res+pos, "%s", dt_months[dt->month-1]);
+			else pos += sprintf(res+pos, "###");		break;
+		case 'V':
+			if(dt->month >0 && dt->month < 13) pos+=sprintf(res+pos, "%02d", dt->month);
+			else pos += sprintf(res+pos, "##");			break;
+		case 'v':
+			if(dt->month >0 && dt->month < 13) pos+=sprintf(res+pos, "%d", dt->month);
+			else pos += sprintf(res+pos, "##");			break;
+		case 'W':
+			if(dt->month >0 && dt->month < 13) pos+=sprintf(res+pos, "%c", dt_month[dt->month-1][0]);
+			else pos += sprintf(res+pos, "#");			break;
+		case 'D':
+			if(dt->dow >0 && dt->dow < 8) pos+=sprintf(res+pos, "%s", dt_day[dt->dow-1]);
+			else pos += sprintf(res+pos, "###");		break;
+		case 'd':
+			if(dt->dow >0 && dt->dow < 8) pos+=sprintf(res+pos, "%s", dt_days[dt->dow-1]);
+			else pos += sprintf(res+pos, "###");		break;
+		case 'E':
+			if(dt->dow >0 && dt->dow < 8) pos+=sprintf(res+pos, "%02d", dt->dow);
+			else pos += sprintf(res+pos, "##");			break;
+		case 'e':
+			if(dt->dow >0 && dt->dow < 8) pos+=sprintf(res+pos, "%d", dt->dow);
+			else pos += sprintf(res+pos, "##");			break;
+		case 'F':
+			if(dt->dow >0 && dt->dow < 8) pos+=sprintf(res+pos, "%c", dt_day[dt->dow-1][0]);
+			else pos += sprintf(res+pos, "#");			break;
+		case 'H':
+			if(dt->hours >=0 && dt->hours < 24) pos+=sprintf(res+pos, "%02d", dt->hours);
+			else pos += sprintf(res+pos, "##");			break;
+		case 'h':
+			if(dt->hours >=0 && dt->hours < 24) pos+=sprintf(res+pos, "%d", dt->hours);
+			else pos += sprintf(res+pos, "##");			break;
+		case 'M':
+			if(dt->minutes >=0 && dt->minutes < 60) pos+=sprintf(res+pos, "%02d", dt->minutes);
+			else pos += sprintf(res+pos, "##");			break;
+		case 'm':
+			if(dt->minutes >=0 && dt->minutes < 60) pos+=sprintf(res+pos, "%d", dt->minutes);
+			else pos += sprintf(res+pos, "##");			break;
+		case 'S':
+			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf(res+pos, "%02d", iround(secs));
+			else pos += sprintf(res+pos, "##");			break;
+		case 's':
+			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf(res+pos, "%d", iround(secs));
+			else pos += sprintf(res+pos, "##");			break;
+		case 'T':
+			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf(res+pos, "%02.2lf", dt->seconds);
+			else pos += sprintf(res+pos, "##.##");		break;
+		case 't':
+			if(dt->seconds >=0.0 && dt->seconds < 60.0) pos+=sprintf(res+pos, "%.2lf", dt->seconds);
+			else pos += sprintf(res+pos, "##.##");		break;
+		default:
+			pos += sprintf(res+pos, "%c", fmt[i]);		break;
+			}
+#endif
+	}
+	res[pos] = 0;
+	return res;
+}
+
+double date2value(rlp_datetime *dt)
+{
+	double res;
+
+	if(!dt) return 0.0;
+
+	res = dt->seconds/60.0 + (double)dt->minutes;
+	res = res/60.0 + (double)dt->hours;
+	res = res/24.0 + (double)dt->aday;
+	return res;
+}
+
+void parse_datevalue(rlp_datetime *dt, double dv)
+{
+	int i, j, d;
+
+	if(!dt || dv < 0.0) return;
+	if(dv > 1.0) {
+		dt->aday = (int)floor(dv);
+		dt->year = (int)(dv/365.2425);
+		d = (int)floor(dv);
+		do {
+			dt->doy = d - 365*dt->year;
+			dt->doy -= ((dt->year-1)>>2);
+			dt->doy += ((dt->year)/100);
+			dt->doy -= ((dt->year+300)/400);
+			if(dt->doy < 1) dt->year--;
+			}while(dt->doy < 1);
+		dt->year += 1900;
+		for(i = dt->month = 0, d = dt->doy; i < 12 && d > 0; i++) {
+			if(i == 1 && d > (j = (leapyear(dt->year)) ? 29 : 28)) d -= j;
+			else if(i != 1 && d > dt_monthl[i]) d -= dt_monthl[i];
+			else break;
+			}
+		dt->month = i+1;				dt->dom = d;
+		}
+	else {
+		dt->aday = dt->year = dt->doy = dt->dom = dt->month = 0;
+		}
+	dv -= floor(dv);				dv *= 24.0;
+	dt->hours = (int)floor(dv);		dv -= floor(dv);
+	dv *= 60.0;						dt->minutes = (int)floor(dv); 
+	dv -= floor(dv);				dt->seconds = dv *60.0 + 1.0e-12; 
+	if(dt->seconds > 59.9999) {
+		dt->seconds = 0.0;			dt->minutes++;
+		if(dt->minutes == 60) {
+			dt->hours++;			dt->minutes = 0;
+			}
+		}
+}
+
+static char *dt_popfmt[] = {"Z.V.Y H:M:S", "Z/V/Y H:M:S", "Z-V-Y H:M:S", "Z.X.Y H:M:S",
+	"Y.V.Z H:M:S", "Y-X-Z H:M:S", "H:M:S", 0L};
+
+bool date_value(char *desc, char *fmt, double *value)
+{
+	int i;
+	rlp_datetime dt;
+
+	dt.year = dt.aday = dt.doy = dt.month = dt.dom = dt.dow = dt.hours = dt.minutes = 0;
+	dt.seconds = 0.0;
+	if(!value || !desc || !desc[0]) return false;
+	if(fmt && fmt[0]) {
+		if(parse_date(&dt, desc, fmt)) {
+			*value = date2value(&dt);	return true;
+			}
+		}
+	else {
+		if(parse_date(&dt, desc, defs.fmt_datetime)) {
+			*value = date2value(&dt);	return true;
+			}
+		}
+	for(i=0; dt_popfmt[i]; i++) {
+		if(parse_date(&dt, desc, dt_popfmt[i])) {
+			*value = date2value(&dt);	return true;
+			}
+		}
+	return false;
+}
+
+char *value_date(double dv, char *fmt)
+{
+	rlp_datetime dt;
+
+	parse_datevalue(&dt, dv);
+	return date2text(&dt, fmt ? fmt : defs.fmt_date);
+}
+
+double now_today()
+{
+	double res = 0.0;
+	time_t ti = time(0L);
+#ifdef USE_WIN_SECURE
+	char dtbuff[80];
+
+	ctime_s(dtbuff, 80, &ti);
+	date_value(dtbuff+4, "x z H:M:S Y", &res);
+#else
+	date_value(ctime(&ti)+4, "x z H:M:S Y", &res);
+#endif
+	return res;
+}
+
+void split_date(double dv, int *y, int *mo, int *dom, int *dow, int *doy, int *h, int *m, double *s)
+{
+	rlp_datetime dt;
+
+	parse_datevalue(&dt, dv);
+	set_dow(&dt);
+	if(y) *y = dt.year;				if(mo) *mo = dt.month;
+	if(dom) *dom = dt.dom;			if(dow) *dow = dt.dow;
+	if(doy) *doy = dt.doy;			if(h) *h = dt.hours;
+	if(m) *m = dt.minutes;			if(s) *s = dt.seconds;
+}
+
+//---------------------------------------------------------------------------
+// Use the Delauney triangulation to create a 3D mesh of dispersed data
+//
+Triangle* Triangulate1(char *xr, char *yr, char *zr, DataObj *data)
+{
+	AccRange *rX, *rY, *rZ;
+	int i, j, n, rx, cx, ry, cy, rz, cz;
+	double zMin;
+	fPOINT3D *da;
+	fRECT lim;
+	Triangle *trl, *trn;
+	Triangulate *tria;
+
+	rX = rY = rZ = 0L;				trl = trn  = 0L;
+	if((rX = new AccRange(xr)) && (rY = new AccRange(yr)) && (rZ = new AccRange(zr))
+		&& rX->GetFirst(&cx, &rx) && rY->GetFirst(&cy, &ry) && rZ->GetFirst(&cz, &rz)
+		&& (n = rX->CountItems()) && (da = (fPOINT3D*)malloc(n * sizeof(fPOINT3D)))
+		&& (trl = new Triangle()) && (trn = new Triangle())) {
+		//get minima and maxima
+		for(i = j = 0; i < n; i++) {
+			if(rX->GetNext(&cx, &rx) && rY->GetNext(&cy, &ry) && rZ->GetNext(&cz, &rz)) {
+				data->GetValue(rx, cx, &da[j].fx);	data->GetValue(ry, cy, &da[j].fy);
+				data->GetValue(rz, cz, &da[j].fz);	j++;
+				}
+			}
+		if(!j) {
+			free(da); delete rX;	delete rY;	delete rZ;	return trl;
+			}
+		for(i = 0, j = n; i < n; i++) {
+			if(i) {
+				if(da[i].fx < lim.Xmin) lim.Xmin = da[i].fx;	if(da[i].fx > lim.Xmax) lim.Xmax = da[i].fx;
+				if(da[i].fy < lim.Ymin) lim.Ymin = da[i].fy;	if(da[i].fy > lim.Ymax) lim.Ymax = da[i].fy;
+				if(da[i].fz < zMin) zMin = da[i].fz;
+				}
+			else {
+				lim.Xmax = lim.Xmin = da[i].fx;		lim.Ymax = lim.Ymin = da[i].fy;		zMin = da[i].fz;
+				}
+			}
+		//setup two super triangles
+		trl->pt[0].fz = trl->pt[1].fz = trl->pt[2].fz = zMin;
+		trn->pt[0].fz = trn->pt[1].fz = trn->pt[2].fz = zMin;
+		trl->pt[0].fx = trn->pt[0].fx = trl->pt[2].fx = lim.Xmin;
+		trl->pt[0].fy = trn->pt[0].fy = trn->pt[1].fy = lim.Ymin;
+		trl->pt[1].fx = trn->pt[2].fx = trn->pt[1].fx = lim.Xmax;
+		trl->pt[1].fy = trn->pt[2].fy = trl->pt[2].fy = lim.Ymax;
+		trl->SetRect();			trn->SetRect();
+		trl->next = trn;		trn->next = 0L;
+		//do triangulation
+		tria = new Triangulate(trl);
+		for(i = 0; i < n; i++) {
+			tria->AddVertex(&da[i]);
+			}
+		free(da);
+		}
+	if(rX) delete rX;	if(rY) delete rY;	if(rZ) delete rZ;
+	trl = tria->trl;	delete tria;		return trl;
+}
diff --git a/rlplot.cpp b/rlplot.cpp
index 22cfa15..6d37cc7 100755
--- a/rlplot.cpp
+++ b/rlplot.cpp
@@ -1,10204 +1,10279 @@
-//rlplot.cpp, Copyright 2000-2006 R.Lackner
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-#include "rlplot.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <math.h>
-#include <string.h>
-
-extern tag_Units Units[];
-extern char TmpTxt[];
-extern Default defs;
-
-GraphObj *CurrGO, *TrackGO;			//Selected Graphic Objects
-Label *CurrLabel = 0L;
-Graph *CurrGraph = 0L;
-Axis **CurrAxes = 0L;
-dragHandle *CurrHandle = 0L;
-UndoObj Undo;
-fmtText DrawFmtText;
-
-int cGraphs = 0;
-int cPlots = 0;
-int cPages = 0;
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// grapic objects
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-GraphObj::GraphObj(GraphObj *par, DataObj *d)
-{
-	parent = par;	data = d;	Id = GO_UNKNOWN;
-	type = moveable = 0;		name = 0L;
-	rDims.left = rDims.right = rDims.top = rDims.bottom = 0;
-}
-
-GraphObj::~GraphObj()
-{
-	if(name)free(name);		name = 0L;
-	if(CurrGO == this)		CurrGO = 0L;
-	if(TrackGO == this)	 TrackGO = 0L;
-}
-
-double
-GraphObj::GetSize(int select)
-{
-	if(parent) return parent->GetSize(select);
-	else return defs.GetSize(select);
-}
-
-DWORD 
-GraphObj::GetColor(int select){
-	return defs.Color(select);
-}
-
-void
-GraphObj::RegGO(void *n)
-{
-	((notary*)n)->AddRegGO(this);
-}
-
-void *
-GraphObj::ObjThere(int x, int y)
-{
-	if(IsInRect(&rDims, x, y)) return this;
-	else return 0L;
-}
-
-void
-GraphObj::Track(POINT *p, anyOutput *o)
-{
-}
-
-double
-GraphObj::DefSize(int select)
-{
-	if(parent) return parent->DefSize(select);
-	else return defs.GetSize(select);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// This is a special object to read certain svg-settings from a *.rlp file
-svgOptions::svgOptions(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(defs.svgScript) free(defs.svgScript);
-	if(defs.svgAttr) free(defs.svgAttr);
-	defs.svgScript = defs.svgAttr = 0L;
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		if(script) defs.svgScript = script;
-		if(svgattr) defs.svgAttr = svgattr;
-		script = svgattr = 0L;
-		}
-	Id=GO_SVGOPTIONS;
-}
-
-svgOptions::~svgOptions()
-{
-	if(script)free(script);
-	script = 0L;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Symbols are graphic objects
-Symbol::Symbol(GraphObj *par, DataObj *d, double x, double y, int which,
-		int xc, int xr, int yc, int yr):GraphObj(par, d)
-{
-	//Symbols with no parent are part of a dialog
-	FileIO(INIT_VARS);
-	fPos.fx = x;
-	fPos.fy = y;
-	type = which;
-	Id = GO_SYMBOL;
-	if(xc >= 0 && xr >= 0 && yc >= 0 && yr >= 0) {
-		if(ssRef = (POINT*)malloc(sizeof(POINT)*2)) {
-			ssRef[0].x = xc;	ssRef[0].y = xr;
-			ssRef[1].x = yc;	ssRef[1].y = yr;
-			cssRef = 2;
-			}
-		}
-}
-
-Symbol::Symbol(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		SymFill.hatch = (LineDEF *) NULL;
-		}
-}
-
-Symbol::~Symbol()
-{
-	Command(CMD_FLUSH, 0L, 0L);
-}
-
-double
-Symbol::GetSize(int select)
-{
-	switch(select) {
-	case SIZE_MINE:		case SIZE_SYMBOL:
-		return size;
-	case SIZE_SYM_LINE:
-		return SymLine.width;
-	case SIZE_XPOS:
-		return fPos.fx;
-	case SIZE_YPOS:
-		return fPos.fy;
-	default:
-		return DefSize(select);
-		}
-}
-
-bool
-Symbol::SetSize(int select, double value)
-{
-	switch(select & 0xfff){
-	case SIZE_MINE:		case SIZE_SYMBOL:
-		size = value;
-		return true;
-	case SIZE_SYM_LINE:
-		SymLine.width = value;
-		return true;
-	case SIZE_XPOS:
-		fPos.fx = value;
-		return true;
-	case SIZE_YPOS:
-		fPos.fy = value;
-		return true;
-	}
-	return false;
-}
-
-DWORD
-Symbol::GetColor(int select)
-{
-	switch(select) {
-	case COL_SYM_LINE:
-		return SymLine.color;
-	case COL_SYM_FILL:
-		return SymFill.color;
-	default:
-		return parent ? parent->GetColor(select) : defs.Color(select);
-		}
-}
-
-bool
-Symbol::SetColor(int select, DWORD col)
-{
-	switch(select & 0xfff) {
-	case COL_SYM_LINE:
-		SymLine.color = col;
-		if(SymTxt) SymTxt->ColTxt = col;
-		return true;
-	case COL_SYM_FILL:
-		SymFill.color = col;
-		return true;
-	default:
-		return false;
-		}
-}
-
-void
-Symbol::DoPlot(anyOutput *target)
-{
-	int ix, iy, rx, ry, crx, cry, atype;
-	double sc;
-	long ncpts;
-	lfPOINT fip;
-	POINT pts[14], *cpts;
-	FillDEF cf;
-
-	atype = (type  & 0xfff);
-	memcpy(&cf, &SymFill, sizeof(FillDEF));
-	if(atype == SYM_CIRCLEF || atype == SYM_RECTF || atype == SYM_TRIAUF ||
-		atype == SYM_TRIADF || atype == SYM_DIAMONDF || atype == SYM_4STARF ||
-		atype == SYM_5GONF || atype == SYM_5STARF || atype == SYM_6STARF) cf.color = SymLine.color;
-	if(type & SYM_POS_PARENT) {
-		if(!parent) return;
-		fip.fx = parent->GetSize(SIZE_XCENTER);
-		fip.fy = parent->GetSize(SIZE_YCENTER);
-		}
-	else if(!target->fp2fip(&fPos, &fip)) return;
-	ix = iround(fip.fx);		iy = iround(fip.fy);
-	target->SetLine(&SymLine);
-	switch(atype){
-	default:
-	case SYM_CIRCLE:		//circle
-	case SYM_CIRCLEF:		//filled circle
-	case SYM_CIRCLEC:		//circle with center point
-	case SYM_1QUAD:		case SYM_2QUAD:		case SYM_3QUAD:
-		rx = target->un2ix(size/2.0);		ry = target->un2iy(size/2.0);
-		if(rx < 5) rx = 1;					if(ry < 5) ry = 1;
-		target->SetFill(&cf);
-		target->oCircle(ix-rx, iy-ry, ix+rx+1, iy+ry+1, name);
-		if(atype == SYM_CIRCLEC) {
-			crx = target->un2ix(size/5.0);	cry = target->un2iy(size/5.0);
-			cf.color = SymLine.color;		target->SetFill(&cf);
-			target->oCircle(ix-crx, iy-cry, ix+crx+1, iy+cry+1, name);
-			}
-		else if(atype == SYM_1QUAD || atype == SYM_2QUAD || atype == SYM_3QUAD) {
-			ncpts = 0L;			cf.color = SymLine.color;		target->SetFill(&cf);
-			if(atype == SYM_1QUAD) {
-				if(!(cpts = MakeArc(ix, iy, rx, 0x04, &ncpts)) || !ncpts) return;
-				cpts[0].x = ix + rx;		cpts[0].y = iy;
-				}
-			else if(atype == SYM_2QUAD) {
-				if(!(cpts = MakeArc(ix, iy, rx, 0x06, &ncpts)) || !ncpts) return;
-				cpts[0].x = ix;				cpts[0].y = iy+rx;
-				}
-			else if(atype == SYM_3QUAD) {
-				if(!(cpts = MakeArc(ix, iy, rx, 0x07, &ncpts)) || !ncpts) return;
-				cpts[0].x = ix-rx;			cpts[0].y = iy;
-				}
-			cpts[ncpts-1].x = ix;			cpts[ncpts-1].y = iy-rx;
-			cpts[ncpts].x = ix;				cpts[ncpts].y = iy;				ncpts++;
-			cpts[ncpts].x = cpts[0].x;		cpts[ncpts].y = cpts[0].y;		ncpts++;
-			target->oPolygon(cpts, ncpts);	free(cpts);
-			}
-		rx--;ry--;			//smaller marking rectangle
-		break;
-	case SYM_RECT:			//rectange (square)
-	case SYM_RECTF:			//filled rectangle
-	case SYM_RECTC:			//square with center point
-		rx = target->un2ix(size/2.25676);	ry = target->un2iy(size/2.25676);
-		if(rx < 5) rx = 1;					if(ry < 5) ry = 1;
-		target->SetFill(&cf);
-		target->oRectangle(ix-rx, iy-ry, ix+rx+1, iy+ry+1, name);
-		if(atype == SYM_RECTC) {
-			crx = target->un2ix(size/6.0);	cry = target->un2iy(size/6.0);
-			cf.color = SymLine.color;		target->SetFill(&cf);
-			target->oCircle(ix-crx, iy-cry, ix+crx+1, iy+cry+1, name);
-			}
-		break;
-	case SYM_TRIAU:			//triangles up and down, open or closed
-	case SYM_TRIAUF:	case SYM_TRIAD:		case SYM_TRIADF:		case SYM_TRIADC:
-	case SYM_TRIAUC:	case SYM_TRIAUL:	case SYM_TRIAUR:		case SYM_TRIADL:
-	case SYM_TRIADR:
-		rx = target->un2ix(size/1.48503);	ry = target->un2iy(size/1.48503);
-		if(rx < 5) rx = 1;					if(ry < 5) ry = 1;
-		target->SetFill(&cf);
-		pts[0].x = pts[3].x = ix - rx;		pts[1].x = ix;		pts[2].x = ix+rx;
-		//patch by anonymous
-		if(atype == SYM_TRIAU || atype == SYM_TRIAUF || atype == SYM_TRIAUL 
-			|| atype == SYM_TRIAUR || atype == SYM_TRIAUC) {
-			pts[0].y = pts[2].y = pts[3].y = iy+target->un2iy(size*0.38878f);
-			pts[1].y = iy-target->un2iy(size*0.77756f);
-			}
-		else {
-			pts[0].y = pts[2].y = pts[3].y = iy-target->un2iy(size*0.38878f);
-			pts[1].y = iy+target->un2iy(size*0.77756f);
-			}
-		target->oPolygon(pts, 4);
-		if(atype == SYM_TRIAUC || atype == SYM_TRIADC) {
-			crx = target->un2ix(size/6.0);	cry = target->un2iy(size/6.0);
-			cf.color = SymLine.color;		target->SetFill(&cf);
-			target->oCircle(ix-crx, iy-cry, ix+crx+1, iy+cry+1, name);
-			}
-		else if(atype == SYM_TRIAUL || atype == SYM_TRIADL) {
-			cf.color = SymLine.color;		target->SetFill(&cf);
-			pts[2].x = pts[1].x;			target->oPolygon(pts, 4);
-			}
-		else if(atype == SYM_TRIAUR || atype == SYM_TRIADR) {
-			cf.color = SymLine.color;		target->SetFill(&cf);
-			pts[0].x = pts[3].x = pts[1].x;	target->oPolygon(pts, 4);
-			}
-		rx--; ry--;
-		break;
-	case SYM_DIAMOND:	case SYM_DIAMONDF:		case SYM_DIAMONDC:
-		rx = target->un2ix(size/1.59588);	ry = target->un2iy(size/1.59588);
-		if(rx < 5) rx = 1;					if(ry < 5) ry = 1;
-		target->SetFill(&cf);
-		pts[0].x = pts[2].x = pts[4].x = ix;		
-		pts[0].y = pts[4].y = iy -ry;
-		pts[1].x = ix +rx;					pts[1].y = pts[3].y = iy;
-		pts[2].y = iy +ry;					pts[3].x = ix-rx;
-		target->oPolygon(pts, 5);
-		if(atype == SYM_DIAMONDC) {
-			crx = target->un2ix(size/6.0);	cry = target->un2iy(size/6.0);
-			cf.color = SymLine.color;		target->SetFill(&cf);
-			target->oCircle(ix-crx, iy-cry, ix+crx+1, iy+cry+1, name);
-			}
-		rx--;									ry--;
-		break;
-	case SYM_4STAR:		case SYM_4STARF:
-		rx = target->un2ix(size/1.4);	ry = target->un2iy(size/1.4);
-		crx = target->un2ix(size/6.0);	cry = target->un2iy(size/6.0);
-		pts[0].x = pts[8].x = ix-rx;	pts[0].y = pts[4].y = pts[8].y = iy;
-		pts[1].x = pts[7].x = ix-crx;	pts[1].y = pts[3].y = iy - cry;
-		pts[2].x = pts[6].x = ix;		pts[2].y = iy - ry;
-		pts[3].x = pts[5].x = ix+crx;	pts[4].x = ix + rx;
-		pts[5].y = pts[7].y = iy+cry;	pts[6].y = iy + ry;
-		target->SetFill(&cf);			target->oPolygon(pts, 9);
-		break;
-	case SYM_5GON:		case SYM_5GONF:		case SYM_5GONC:
-		sc = 1.4;
-		rx = target->un2ix(size/sc);	ry = target->un2iy(size/sc);
-		crx = target->un2ix(size/sc * 0.951057);	
-		cry = target->un2iy(size/sc * 0.309017);
-		pts[0].x = ix-crx;		pts[0].y = pts[2].y = iy-cry;	pts[1].x = ix;
-		pts[1].y = iy-ry;		pts[2].x = ix+crx;
-		crx = target->un2ix(size/sc * 0.587785);	
-		cry = target->un2iy(size/sc * 0.809017);
-		pts[3].x = ix + crx;	pts[4].x = ix - crx;
-		pts[3].y = pts[4].y = iy+cry;
-		pts[5].x = pts[0].x;	pts[5].y = pts[0].y;
-		target->SetFill(&cf);			target->oPolygon(pts, 6);
-		if(atype == SYM_5GONC) {
-			crx = target->un2ix(size/6.0);	cry = target->un2iy(size/6.0);
-			cf.color = SymLine.color;		target->SetFill(&cf);
-			target->oCircle(ix-crx, iy-cry, ix+crx+1, iy+cry+1, name);
-			}
-		break;
-	case SYM_5STAR:		case SYM_5STARF:
-		sc = 1.4;
-		rx = target->un2ix(size/sc);	ry = target->un2iy(size/sc);
-		crx = target->un2ix(size/sc * 0.951057);	
-		cry = target->un2iy(size/sc * 0.309017);
-		pts[0].x = ix-crx;		pts[0].y = pts[1].y = pts[3].y = pts[4].y = iy-cry;
-		pts[2].x = pts[7].x = ix;		pts[2].y = iy-ry;		pts[4].x = ix+crx;
-		crx = target->un2ix(size/sc * 0.23);
-		pts[1].x = ix - crx;	pts[3].x = ix + crx;
-		crx =  target->un2ix(size/sc * 0.36);
-		cry = target->un2iy(size/sc * 0.11);
-		pts[5].x = ix + crx;	pts[5].y = pts[9].y = iy +	cry;	pts[9].x = ix - crx;
-		pts[7].y = iy + target->un2iy(size/sc * 0.38);
-		crx = target->un2ix(size/sc * 0.587785);	
-		cry = target->un2iy(size/sc * 0.809017);
-		pts[6].x = ix + crx;	pts[8].x = ix - crx;
-		pts[6].y = pts[8].y = iy+cry;
-		pts[10].x = pts[0].x;	pts[10].y = pts[0].y;
-		target->SetFill(&cf);			target->oPolygon(pts, 11);
-		break;
-	case SYM_6STAR:		case SYM_6STARF:
-		sc = 1.4 / 0.86;				rx = target->un2ix(size/sc);
-		sc = 1.4 / 0.5;					ry = target->un2iy(size/sc);
-		pts[0].x = pts[10].x = pts[12].x = ix - rx;
-		pts[4].x = pts[6].x = ix + rx;	pts[2].x = pts[8].x = ix;
-		pts[0].y = pts[1].y = pts[3].y = pts[4].y = pts[12].y = iy - ry;
-		pts[6].y = pts[7].y = pts[9].y = pts[10].y = iy + ry;	
-		sc = 1.4 / 0.29;				rx = target->un2ix(size/sc);
-		pts[1].x = pts[9].x = ix - rx;	pts[3].x = pts[7].x = ix + rx;
-		sc = 1.4 / 0.52;				rx = target->un2ix(size/sc);
-		pts[5].x = ix +rx;	pts[11].x = ix - rx;	pts[5].y = pts[11].y = iy;
-		sc = 1.4;
-		rx = target->un2ix(size/sc);	ry = target->un2iy(size/sc);
-		pts[2].y = iy - ry;				pts[8].y = iy + ry;
-		target->SetFill(&cf);			target->oPolygon(pts, 13);
-		break;
-	case SYM_STAR:			//star is a combination of + and x symbols
-	case SYM_PLUS:			//draw a + sign
-	case SYM_HLINE:		case SYM_VLINE:
-		rx = target->un2ix(size/2.0f);		ry = target->un2iy(size/2.0f);
-		if(rx < 5) rx = 1;					if(ry < 5) ry = 1;
-		pts[0].x = pts[1].x = ix;
-		pts[0].y = iy - ry;					pts[1].y = iy + ry +1;
-		if(type != SYM_HLINE) target->oPolyline(pts, 2);
-		pts[0].x = ix -rx;					pts[1].x = ix + rx +1;
-		pts[0].y = pts[1].y = iy;
-		if(atype != SYM_VLINE) target->oPolyline(pts, 2);
-		if(atype == SYM_VLINE){ rx = 2; break;}
-		if(atype == SYM_HLINE){ ry = 2; break;}
-		if(atype == SYM_PLUS) break;		//continue with x symbol for star
-	case SYM_CROSS:			//draw a x symbol
-		rx = target->un2ix(size/2.5);		ry = target->un2iy(size/2.5);
-		if(rx < 5) rx = 1;					if(ry < 5) ry = 1;
-		pts[0].x = ix - rx - 2;				pts[1].x = ix + rx + 3;
-		pts[0].y = iy - ry - 2;				pts[1].y = iy + ry + 3;
-		target->oPolyline(pts, 2);			Swap(pts[0].y, pts[1].y);
-		pts[1].y -= 1;						pts[0].y -= 1;
-		target->oPolyline(pts, 2);
-		break;
-	case SYM_TEXT:
-		if(!SymTxt) Command(CMD_SETTEXT, (void *)"text", target);
-		if(!SymTxt || !SymTxt->text || !SymTxt->text[0])return;
-		SymTxt->iSize = target->un2iy(SymTxt->fSize = size *1.5);
-		target->SetTextSpec(SymTxt);
-		DrawFmtText.SetText(target, SymTxt->text, &ix, &iy);
-		if (target->oGetTextExtent(SymTxt->text, 0, &rx, &ry)){
-			rx >>= 1;		ry >>= 1;
-			}
-		else rx = ry = 10;
-		}
-	rDims.left = ix-rx-1;				rDims.right = ix+rx+2;
-	rDims.top = iy-ry-1;				rDims.bottom = iy+ry+2;
-}
-
-bool 
-Symbol::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	char *tmptxt;
-	AccRange *ac;
-	int i, r, c;
-
-	switch (cmd) {
-	case CMD_SCALE:
-		if(!tmpl) return false;
-		size *= ((scaleINFO*)tmpl)->sy.fy;
-		SymLine.width *= ((scaleINFO*)tmpl)->sy.fy;
-		if(SymTxt) {
-			SymTxt->fSize *= ((scaleINFO*)tmpl)->sy.fy;
-			SymTxt->iSize = 0;
-			}
-		return true;
-	case CMD_FLUSH:
-		if(SymTxt) {
-			if(SymTxt->text) free(SymTxt->text);
-			free(SymTxt);
-			}
-		if(ssRef) free(ssRef);	ssRef = 0L;
-		if(name)free(name);		name = 0L;
-		return true;
-	case CMD_REDRAW:
-		//if we come here its most likely the result of Undo
-		if(parent && parent->Id==GO_REGRESSION)
-			return parent->Command(CMD_MRK_DIRTY, 0L, o);
-		return false;
-	case CMD_GETTEXT:
-		if(SymTxt && SymTxt->text && tmpl) {
-			rlp_strcpy((char*)tmpl, 50, SymTxt->text);
-			return true;
-			}
-		return false;
-	case CMD_SYMTEXT_UNDO:
-		if(SymTxt && SymTxt->text){
-			c = Undo.String(this, &SymTxt->text, UNDO_CONTINUE);
-			i = (int)strlen((char*)tmpl);		i = i > c ? i+2 : c+2;
-			if(tmpl) {
-				if(SymTxt->text = (char*)realloc(SymTxt->text, i)) rlp_strcpy(SymTxt->text, i, (char*)tmpl);
-				}
-			else if(SymTxt->text) SymTxt->text[0] = 0;
-			return true;
-			}
-		//fall through if its new
-	case CMD_SYMTEXT:		case CMD_SETTEXT:
-		if(!SymTxt && (SymTxt = (TextDEF *) calloc(1, sizeof(TextDEF)))) {
-			SymTxt->ColTxt = SymLine.color;			SymTxt->fSize = size*1.5;
-			SymTxt->ColBg = parent ? parent->GetColor(COL_BG) : 0x00ffffffL;
-			SymTxt->Align = TXA_VCENTER | TXA_HCENTER;
-			SymTxt->Style = TXS_NORMAL;				SymTxt->Mode = TXM_TRANSPARENT;
-			SymTxt->Font = FONT_HELVETICA;			SymTxt->text = 0L;
-			}
-		if(!SymTxt) return false;
-		if(tmpl) {
-			i = (int) strlen((char*)tmpl) + 2;
-			if(SymTxt->text = (char*)realloc(SymTxt->text, i)) rlp_strcpy(SymTxt->text, i, (char*)tmpl);
-			}
-		else if(SymTxt->text) SymTxt->text[0] = 0;
-		return true;
-	case CMD_SYM_TYPE:
-		if(tmpl)type = *((int*)tmpl);
-		return true;
-	case CMD_GETTEXTDEF:
-		if(!SymTxt || !tmpl) return false;
-		memcpy(tmpl, SymTxt, sizeof(TextDEF));
-		return true;
-	case CMD_SYMTEXTDEF:		case CMD_SETTEXTDEF:
-		if(!tmpl)return false;
-		if(SymTxt) tmptxt = SymTxt->text;
-		else tmptxt = 0L;
-		if(!SymTxt && !(SymTxt = (TextDEF *) calloc(1, sizeof(TextDEF)))) return false;
-		memcpy(SymTxt, tmpl, sizeof(TextDEF));
-		SymTxt->text = tmptxt;
-		return true;
-	case CMD_SYM_RANGETEXT:		case CMD_RANGETEXT:
-		if(!data || !tmpl) return false;
-		if(!(tmptxt = (char*)malloc(500)))return false;
-		if((ac = new AccRange((char*)tmpl)) && ac->GetFirst(&c, &r)) {
-			for(i = 0, tmptxt[0] = 0; i <= idx; i++) ac->GetNext(&c, &r);
-			data->GetText(r, c, tmptxt, 500);
-			delete(ac);
-			}
-		Command(CMD_SETTEXT, tmptxt, 0L);
-		free(tmptxt);
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = GO_SYMBOL;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
-				o->ShowMark(&rDims, MRK_INVERT);
-				CurrGO = this;
-				return true;
-				}
-			break;
-			}
-		break;
-	case CMD_UPDATE:
-		if(ssRef && cssRef >1 && data) {
-			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
-			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
-			return true;
-			}
-		return false;
-	case CMD_AUTOSCALE:
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
-			return true;
-			}
-		break;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Bubbles are graphic objects
-Bubble::Bubble(GraphObj *par, DataObj *d, double x, double y, double s, int which, 
-	FillDEF *fill, LineDEF *outline, int xc, int xr, int yc, int yr, int sc,
-	int sr):GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	fPos.fx = x;	fPos.fy = y;	fs = s;
-	type = which;
-	if(fill) {
-		memcpy(&BubbleFill,fill, sizeof(FillDEF));
-		if(BubbleFill.hatch) memcpy(&BubbleFillLine, BubbleFill.hatch, sizeof(LineDEF));
-		}
-	BubbleFill.hatch = &BubbleFillLine;
-	if(outline)memcpy(&BubbleLine, outline, sizeof(LineDEF));
-	Id = GO_BUBBLE;
-	if(xc >= 0 || xr >= 0 || yc >= 0 || yr >= 0 || sc >= 0 || sr >= 0) {
-		if(ssRef = (POINT*)malloc(sizeof(POINT)*3)) {
-			ssRef[0].x = xc;	ssRef[0].y = xr;
-			ssRef[1].x = yc;	ssRef[1].y = yr;
-			ssRef[2].x = sc;	ssRef[2].y = sr;
-			cssRef = 3;
-			}
-		}
-}
-
-Bubble::Bubble(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-Bubble::~Bubble()
-{
-	Command(CMD_FLUSH, 0L, 0L);
-}
-
-void
-Bubble::DoPlot(anyOutput *o)
-{
-	int x1, y1, x2, y2, ix, iy, tmp;
-	double fix, fiy;
-
-	o->SetLine(&BubbleLine);
-	o->SetFill(&BubbleFill);
-	switch(type & 0x0f0) {
-	case BUBBLE_UNITS:
-		fix = o->un2fix(fs);		fiy = o->un2fiy(fs);
-		break;
-	case BUBBLE_XAXIS:
-		fix = (o->fx2fix(fPos.fx+fs) - o->fx2fix(fPos.fx-fs))/2.0;
-		fiy = fix * (o->un2fiy(10.0f)/o->un2fix(10.0f));	//x and y resolution different ?
-		break;
-	case BUBBLE_YAXIS:
-		fix = (o->fy2fiy(fPos.fy-fs) - o->fy2fiy(fPos.fy+fs))/2.0;
-		fiy = fix * (o->un2fiy(10.0f)/o->un2fix(10.0f));	//x and y resolution different ?
-		break;
-		}
-	fix = fix < 0.0 ? -fix : fix;							//sign must be positive
-	fiy = fiy < 0.0 ? -fiy : fiy;
-	rDims.left = rDims.right = iround(o->fx2fix(fPos.fx));
-	rDims.top = rDims.bottom = iround(o->fy2fiy(fPos.fy));
-	switch(type & 0x00f) {
-	case BUBBLE_CIRCLE:
-		ix = (int)(fix/2.0);			iy = (int)(fiy/2.0);
-		tmp = iround(o->fx2fix(fPos.fx));		x1 = tmp - ix;		x2 = tmp + ix;
-		tmp = iround(o->fy2fiy(fPos.fy));		y1 = tmp - iy;		y2 = tmp + iy;
-		o->oCircle(x1, y1, x2, y2, name);
-		UpdateMinMaxRect(&rDims, x1, y1);	UpdateMinMaxRect(&rDims, x2, y2);
-		break;
-	case BUBBLE_SQUARE:
-		if((type & 0xf00) == BUBBLE_CIRCUM) {
-			ix = iround(fix*.392699081);		iy = iround(fiy*.392699081);
-			}
-		else if((type & 0xf00) == BUBBLE_AREA) {
-			ix = iround(fix*.443113462);		iy = iround(fiy*.443113462);
-			}
-		else {
-			ix = iround(fix*.353553391);		iy = iround(fiy*.353553391);
-			}
-		tmp = iround(o->fx2fix(fPos.fx));		x1 = tmp - ix;		x2 = tmp + ix;
-		tmp = iround(o->fy2fiy(fPos.fy));		y1 = tmp - iy;		y2 = tmp + iy;
-		o->oRectangle(x1, y1, x2, y2, name);
-		UpdateMinMaxRect(&rDims, x1, y1);	UpdateMinMaxRect(&rDims, x2, y2);
-		break;
-	case BUBBLE_UPTRIA:
-	case BUBBLE_DOWNTRIA:
-		if((type & 0xf00) == BUBBLE_CIRCUM) {
-			fix *= .523598775;		fiy *= .523598775;
-			}
-		else if((type & 0xf00) == BUBBLE_AREA) {
-			fix *= .673386843;		fiy *= .673386843;
-			}
-		else {
-			fix *=.433012702;		fiy *= .433012702;
-			}
-		ix =  iround(fix);		iy = iround(fiy*.57735);
-		tmp = iround(o->fx2fix(fPos.fx));
-		pts[0].x = pts[3].x = tmp - ix;		pts[1].x = tmp + ix;		pts[2].x = tmp;
-		tmp = iround(o->fy2fiy(fPos.fy));
-		if((type & 0x00f) == BUBBLE_UPTRIA) {
-			pts[0].y = pts[1].y = pts[3].y = tmp + iy;
-			pts[2].y = tmp - iround(fiy*1.1547);
-			}
-		else {
-			pts[0].y = pts[1].y = pts[3].y = tmp - iy;
-			pts[2].y = tmp + iround(fiy*1.1547);
-			}
-		o->oPolygon(pts, 4);
-		UpdateMinMaxRect(&rDims, pts[0].x, pts[0].y);
-		UpdateMinMaxRect(&rDims, pts[1].x, pts[2].y);
-		break;
-		}
-}
-
-void
-Bubble::DoMark(anyOutput *o, bool mark)
-{
-	if(mark) {
-		BubbleFillLine.color ^= 0x00ffffffL;
-		BubbleFill.color ^= 0x00ffffffL;
-		DoPlot(o);
-		BubbleFill.color ^= 0x00ffffffL;
-		BubbleFillLine.color ^= 0x00ffffffL;
-		}
-	else {
-		if(parent) parent->DoPlot(o);
-		else DoPlot(o);
-		}
-	o->UpdateRect(&rDims, false);
-}
-
-bool 
-Bubble::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	bool bSelected = false;
-	unsigned long n, s;
-	POINT p;
-
-	switch (cmd) {
-	case CMD_FLUSH:
-		if(ssRef) free(ssRef);	ssRef = 0L;
-		if(name)free(name);		name = 0L;
-		return true;
-	case CMD_SCALE:
-		if(!tmpl) return false;
-		if((type & 0x0f0)== BUBBLE_UNITS) fs *= ((scaleINFO*)tmpl)->sy.fy;
-		BubbleLine.width *= ((scaleINFO*)tmpl)->sy.fy;
-		BubbleLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		BubbleFillLine.width *= ((scaleINFO*)tmpl)->sy.fy;
-		BubbleFillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		BubbleFill.scale *= ((scaleINFO*)tmpl)->sy.fy;
-		return true;
-	case CMD_LEGEND:
-		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
-		((Legend*)tmpl)->HasFill(&BubbleLine, &BubbleFill, 0L);
-		break;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, p.x = mev->x, p.y = mev->y) && !CurrGO) {
-				switch(type & 0x00f) {
-				case BUBBLE_CIRCLE:
-					n = s = p.x - ((rDims.right+rDims.left)>>1);
-					s *= n;
-					n = p.y - ((rDims.bottom+rDims.top)>>1);
-					n = isqr(s += n*n) -2;
-					bSelected = ((unsigned)((rDims.right-rDims.left)>>1) > n);
-					break;
-				case BUBBLE_SQUARE:
-					bSelected = true;
-					break;
-				case BUBBLE_UPTRIA:
-				case BUBBLE_DOWNTRIA:
-					if(!(bSelected = IsInPolygon(&p, pts, 4)))
-						bSelected = IsCloseToPL(p, pts, 4);
-					break;
-					}
-				if(bSelected) o->ShowMark(this, MRK_GODRAW);
-				return bSelected;
-				}
-			break;
-			}
-		break;
-	case CMD_SET_DATAOBJ:
-		Id = GO_BUBBLE;
-		data = (DataObj*)tmpl;
-		return true;
-	case CMD_UPDATE:
-		if(ssRef && cssRef >2 && data) {
-			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
-			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
-			data->GetValue(ssRef[2].y, ssRef[2].x, &fs);
-			return true;
-			}
-		return false;
-	case CMD_BUBBLE_ATTRIB:
-		if(tmpl) {
-			type &= ~0xff0;
-			type |= (*((int*)tmpl) & 0xff0);
-			return true;
-			}
-		return false;
-	case CMD_BUBBLE_TYPE:
-		if(tmpl) {
-			type &= ~0x00f;
-			type |= (*((int*)tmpl) & 0x00f);
-			return true;
-			}
-		return false;
-	case CMD_BUBBLE_FILL:
-		if(tmpl) {
-			BubbleFill.type = ((FillDEF*)tmpl)->type;
-			BubbleFill.color = ((FillDEF*)tmpl)->color;
-			BubbleFill.scale = ((FillDEF*)tmpl)->scale;
-			if(((FillDEF*)tmpl)->hatch)
-				memcpy(&BubbleFillLine, ((FillDEF*)tmpl)->hatch, sizeof(LineDEF));
-			}
-		return true;
-	case CMD_BUBBLE_LINE:
-		if(tmpl) memcpy(&BubbleLine, tmpl, sizeof(LineDEF));
-		return true;
-	case CMD_AUTOSCALE:
-		return DoAutoscale(o);
-		break;
-		}
-	return false;
-}
-
-bool
-Bubble::DoAutoscale(anyOutput *o) 
-{
-	double dx, dy;
-
-	switch(type & 0x0f0) {
-	case BUBBLE_XAXIS:			case BUBBLE_YAXIS:
-		dx = dy = fs/2.0;		break;
-	case BUBBLE_UNITS:
-		dx = fPos.fx/20;		dy = fPos.fy/20;		break;
-		}
-	((Plot*)parent)->CheckBounds(fPos.fx+dx, fPos.fy-dy);
-	((Plot*)parent)->CheckBounds(fPos.fx-dx, fPos.fy+dy);
-	return true;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Bars are graphic objects
-Bar::Bar(GraphObj *par, DataObj *d, double x, double y, int which,int xc, int xr,
-		int yc, int yr, char *desc):GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	fPos.fx = x;			fPos.fy = y;			type = which;
-	if(type & BAR_RELWIDTH) size = 60.0;			Id = GO_BAR;
-	if(xc >= 0 || xr >= 0 || yc >= 0 || yr >= 0) {
-		if(ssRef = (POINT*)malloc(sizeof(POINT)*2)) {
-			ssRef[0].x = xc;	ssRef[0].y = xr;
-			ssRef[1].x = yc;	ssRef[1].y = yr;
-			cssRef = 2;
-			}
-		}
-	if(desc && desc[0]) name = (char*)memdup(desc, (int)strlen(desc)+1, 0);
-}
-
-Bar::Bar(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-Bar::~Bar()
-{
-	if(mo) DelBitmapClass(mo);	mo = 0L;
-	Command(CMD_FLUSH, 0L, 0L);
-}
-
-double
-Bar::GetSize(int select)
-{
-	switch(select){
-	case SIZE_XPOS:				return fPos.fx;
-	case SIZE_YPOS:				return fPos.fy;
-		}
-	return 0.0;
-}
-
-bool
-Bar::SetSize(int select, double value)
-{
-	switch(select & 0xfff) {
-	case SIZE_BAR: 
-		size = value;
-		return true;
-	case SIZE_BAR_LINE:
-		BarLine.width = value;
-		return true;
-	case SIZE_XBASE:
-		BarBase.fx = value;
-		return true;
-	case SIZE_YBASE:
-		BarBase.fy = value;
-		return true;
-		}
-	return false;
-}
-
-bool
-Bar::SetColor(int select, DWORD col)
-{
-	switch(select & 0xfff) {
-	case COL_BAR_LINE:
-		BarLine.color = col;
-		return true;
-	case COL_BAR_FILL:
-		BarFill.color = col;
-		return true;
-		}
-	return false;
-}
-
-void
-Bar::DoPlot(anyOutput *target)
-{
-	int w;
-	double fBase, rsize;
-	POINT pts[2];
-
-	if(!parent || size <= 0.001) return;
-	target->SetLine(&BarLine);				target->SetFill(&BarFill);
-	switch(type & 0xff) {
-	case BAR_VERTU:		case BAR_VERTT:		case BAR_VERTB:
-		switch(type & 0xff) {
-		case BAR_VERTB:
-			fBase = parent->GetSize(SIZE_BOUNDS_BOTTOM);
-			break;
-		case BAR_VERTT:
-			fBase = parent->GetSize(SIZE_BOUNDS_TOP);
-			break;
-		case BAR_VERTU:
-			fBase = BarBase.fy;
-			break;
-			}
-		if(type & BAR_RELWIDTH) {
-			rsize = size * parent->GetSize(SIZE_BARMINX)/100.0;
-			pts[0].x = iround(target->fx2fix(fPos.fx - rsize/2.0));
-			pts[1].x = iround(target->fx2fix(fPos.fx + rsize/2.0));
-			}
-		else {
-			w = target->un2ix(size);
-			pts[0].x = iround(target->fx2fix(fPos.fx)) - (w>>1);
-			pts[1].x = pts[0].x + w;
-			}
-		if(type & BAR_CENTERED) {
-			pts[0].y = iround(target->fy2fiy(fBase - (fPos.fy - fBase)));
-			pts[1].y = iround(target->fy2fiy(fBase + (fPos.fy - fBase)));
-			}
-		else {
-			pts[0].y = iround(target->fy2fiy(fBase));
-			pts[1].y = iround(target->fy2fiy(fPos.fy));
-			}
-		break;
-	case BAR_HORU:		case BAR_HORR:		case BAR_HORL:
-		switch(type & 0xff) {
-		case BAR_HORL:
-			fBase = parent->GetSize(SIZE_BOUNDS_LEFT);
-			break;
-		case BAR_HORR:
-			fBase = parent->GetSize(SIZE_BOUNDS_RIGHT);
-			break;
-		case BAR_HORU:
-			fBase = BarBase.fx;
-			break;
-			}
-		if(type & BAR_RELWIDTH) {
-			rsize = size * parent->GetSize(SIZE_BARMINY)/100.0;
-			pts[0].y = iround(target->fy2fiy(fPos.fy - rsize/2.0));
-			pts[1].y = iround(target->fy2fiy(fPos.fy + rsize/2.0));
-			}
-		else {
-			w = target->un2iy(size);
-			pts[0].y = target->fy2iy(fPos.fy) - w/2;
-			pts[1].y = pts[0].y+w;
-			}
-		if(type & BAR_CENTERED) {
-			pts[0].x = target->fx2ix(fBase - (fPos.fx - fBase));
-			pts[1].x = target->fx2ix(fBase + (fPos.fx - fBase));
-			}
-		else {
-			pts[0].x = target->fx2ix(fBase);
-			pts[1].x = target->fx2ix(fPos.fx);
-			}
-		break;
-	default:
-		return;
-		}
-	if(pts[0].x == pts[1].x || pts[0].y == pts[1].y) {
-		target->oSolidLine(pts);
-		}
-	else target->oRectangle(pts[0].x, pts[0].y, pts[1].x, pts[1].y, name);
-	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
-}
-
-void
-Bar::DoMark(anyOutput *o, bool mark)
-{
-	int i;
-
-	if(mark){
-		if(mo) DelBitmapClass(mo);					mo = 0L;
-		memcpy(&mrc, &rDims, sizeof(RECT));
-		i = 2*o->un2ix(BarLine.width);		//increase size of rectangle for marks
-		IncrementMinMaxRect(&mrc, i);
-		mo = GetRectBitmap(&mrc, o);
-		o->CopyBitmap(mrc.left, mrc.top, mo, 0, 0, mrc.right-mrc.left, mrc.bottom - mrc.top, true);
-		o->UpdateRect(&mrc, false);
-		}
-	else RestoreRectBitmap(&mo, &mrc, o);
-}
-
-bool 
-Bar::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	FillDEF *TmpFill;
-	lfPOINT bl;
-
-	switch (cmd) {
-	case CMD_FLUSH:
-		if(ssRef) free(ssRef);		ssRef = 0L;
-		if(name)free(name);			name = 0L;
-		return true;
-	case CMD_SCALE:
-		if(!tmpl) return false;
-		if(!(type & BAR_RELWIDTH)) size *= ((scaleINFO*)tmpl)->sy.fy;
-		BarLine.width *= ((scaleINFO*)tmpl)->sy.fy;
-		BarLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		HatchLine.width *= ((scaleINFO*)tmpl)->sy.fy;
-		HatchLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		BarFill.scale *= ((scaleINFO*)tmpl)->sy.fy;
-		return true;
-	case CMD_LEGEND:
-		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
-		((Legend*)tmpl)->HasFill(&BarLine, &BarFill, name);
-		break;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
-				o->ShowMark(CurrGO = this, MRK_GODRAW);
-				return true;
-				}
-			break;
-			}
-		return false;
-	case CMD_BAR_FILL:
-		TmpFill = (FillDEF *)tmpl;
-		if(TmpFill) {
-			BarFill.type = TmpFill->type;
-			BarFill.color = TmpFill->color;
-			BarFill.scale = TmpFill->scale;
-			if(TmpFill->hatch) memcpy(&HatchLine, TmpFill->hatch, sizeof(LineDEF));
-			}
-		return true;
-	case CMD_BAR_TYPE:
-		if(tmpl) type = *((int*)tmpl);
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = GO_BAR;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_UPDATE:
-		if(ssRef && cssRef >1 && data) {
-			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
-			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
-			return true;
-			}
-		return false;
-	case CMD_AUTOSCALE:
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
-			switch(type & 0xff) {
-			case BAR_VERTU:
-			case BAR_VERTT:
-			case BAR_VERTB:
-				bl.fx = fPos.fx;
-				switch (type & 0xff) {
-				case BAR_VERTU:
-					bl.fy = BarBase.fy;
-					break;
-				case BAR_VERTT:
-				case BAR_VERTB:
-					bl.fy = 0.0f;		//cannot resolve
-					break;
-					}
-				if(type & BAR_CENTERED) bl.fy -= fPos.fy;
-				break;
-			case BAR_HORU:
-			case BAR_HORR:
-			case BAR_HORL:
-				bl.fy = fPos.fy;
-				switch(type & 0xff) {
-				case BAR_HORU:
-					bl.fx = BarBase.fx;
-				case BAR_HORR:
-				case BAR_HORL:
-					bl.fx = 0.0f;		//cannot resolve
-					}
-				if(type & BAR_CENTERED) bl.fx -= fPos.fx;
-				break;
-				}
-			((Plot*)parent)->CheckBounds(bl.fx, bl.fy);
-			return true;
-			}
-		break;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Data line is a graphic object
-DataLine::DataLine(GraphObj *par, DataObj *d, char *xrange, char *yrange, char *nam):GraphObj(par, d)
-{
-	size_t cb;
-
-	FileIO(INIT_VARS);
-	Id = GO_DATALINE;
-	if(xrange && xrange[0]) {
-		cb = strlen(xrange) +2;		ssXref = (char*)malloc(cb);		rlp_strcpy(ssXref, (int)cb, xrange);
-		}
-	if(yrange && yrange[0]) {
-		cb = strlen(yrange) +2;		ssYref = (char*)malloc(cb);		rlp_strcpy(ssYref, (int)cb, yrange);
-		}
-	if(nam && nam[0]) name = (char*)memdup(nam, (int)strlen(nam)+1, 0);
-	SetValues();
-}
-	
-DataLine::DataLine(GraphObj *par, DataObj *d, lfPOINT *val, long nval, char *na):GraphObj(par, d)
-{
-	int cb;
-
-	FileIO(INIT_VARS);
-	Values = val;			nPnt = nval;	nPntSet = nPnt-1;
-	if(na && na[0] && (name = (char*)malloc((cb = (int)strlen(na))+2))) {
-		rlp_strcpy(name, cb+1, na);
-		}
-	Id = GO_DATALINE;
-}
-
-DataLine::DataLine(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-DataLine::~DataLine()
-{
-	if(Values)free(Values);		Values = 0L;
-	if(pts) free(pts);			pts = 0L;
-	if(ssXref) free(ssXref);	ssXref = 0L;
-	if(ssYref) free(ssYref);	ssYref = 0L;
-	if(mo) DelBitmapClass(mo);	mo = 0L;
-	if(name) free(name);		name = 0L;
-	if(parent)parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-}
-
-bool
-DataLine::SetColor(int select, DWORD col)
-{
-	switch(select & 0xfff) {
-	case COL_DATA_LINE:
-		LineDef.color = col;
-		return true;
-		}
-	return false;
-}
-
-void
-DataLine::DoPlot(anyOutput *target)
-{
-	int i;
-	lfPOINT fip;
-	POINT pn, *tmppts;
-
-	if(!Values || nPntSet < 1) return;
-	if (nPntSet >= nPnt) nPntSet = nPnt-1;
-	if(mo) DelBitmapClass(mo);		mo = 0L;
-	if(pts) free(pts);				pts = 0L;
-	if((type & 0xff) == 9 || (type & 0xff) == 10)	//splines
-		pts = (POINT *)malloc(sizeof(POINT)*1000);
-	else if((type & 0xff) == 11)					// curve
-		pts = (POINT *) malloc(sizeof(POINT)* (nPntSet+2)*192);
-	else if(type & 0xff) pts = (POINT *)malloc(sizeof(POINT)*(nPntSet+2)*2);
-	else pts = (POINT *)malloc(sizeof(POINT)*(nPntSet+2));
-	if(!pts) return;
-	if(max.fx > min.fx && max.fy > min.fy) dirty = false;
-	else if(dirty) Command(CMD_AUTOSCALE, 0L, target);
-	cp = 0;
-	switch(type & 0x0f) {
-	case 0:		default:
-		for (i = 0; i <= nPntSet; i++){
-			target->fp2fip(Values+i, &fip);
-			pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
-			AddToPolygon(&cp, pts, &pn);
-			}
-		break;
-	case 5:
-		target->fp2fip(Values, &fip);
-		pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
-		target->fp2fip(Values+1, &fip);
-		pn.y += (pn.y -iround(fip.fy))>>1;
-		AddToPolygon(&cp, pts, &pn);
-	case 1:
-		target->fp2fip(Values, &fip);
-		pn.x = iround(fip.fx);		pn.y = iround(+fip.fy);
-		for (i = 0; i <= nPntSet; i++){
-			target->fp2fip(Values+i, &fip);
-			pn.x = iround(fip.fx);			AddToPolygon(&cp, pts, &pn);
-			pn.y = iround(fip.fy);			AddToPolygon(&cp, pts, &pn);
-			}
-		if((type &0xf) == 5) {
-			target->fp2fip(Values+i-2, &fip);
-			pn.x += (pn.x - iround(fip.fx))>>1;
-			AddToPolygon(&cp, pts, &pn);
-			}
-		break;
-	case 6:
-		target->fp2fip(Values, &fip);
-		pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
-		target->fp2fip(Values+1, &fip);
-		pn.x += (pn.x - iround(fip.fx))>>1;
-		AddToPolygon(&cp, pts, &pn);
-	case 2:
-		target->fp2fip(Values, &fip);
-		pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
-		for (i = 0; i <= nPntSet; i++){
-			target->fp2fip(Values+i, &fip);
-			pn.y = iround(fip.fy);			AddToPolygon(&cp, pts, &pn);
-			pn.x = iround(fip.fx);			AddToPolygon(&cp, pts, &pn);
-			}
-		if((type &0xf) == 6) {
-			target->fp2fip(Values+i-2, &fip);
-			pn.y += (pn.y - iround(fip.fy))>>1;
-			AddToPolygon(&cp, pts, &pn);
-			}
-		break;
-	case 7:
-		target->fp2fip(Values, &fip);
-		pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
-		target->fp2fip(Values+1, &fip);
-		pn.x += (pn.x - iround(fip.fx))>>1;
-		AddToPolygon(&cp, pts, &pn);
-	case 3:
-		target->fp2fip(Values, &fip);
-		pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
-		for (i = 0; i <= nPntSet; i++){
-			target->fp2fip(Values+i, &fip);
-			pn.x = (pn.x + iround(fip.fx))>>1;	AddToPolygon(&cp, pts, &pn);
-			pn.y = iround(fip.fy);				AddToPolygon(&cp, pts, &pn);
-			pn.x = iround(fip.fx);
-			}
-		AddToPolygon(&cp, pts, &pn);
-		if((type &0xf) == 7) {
-			target->fp2fip(Values+i-2, &fip);
-			pn.x += (pn.x - iround(fip.fx))>>1;
-			AddToPolygon(&cp, pts, &pn);
-			}
-		break;
-	case 8:
-		target->fp2fip(Values, &fip);
-		pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
-		target->fp2fip(Values+1, &fip);
-		pn.y += (pn.y - iround(fip.fy))>>1;
-		AddToPolygon(&cp, pts, &pn);
-	case 4:
-		target->fp2fip(Values, &fip);
-		pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
-		for (i = 0; i <= nPntSet; i++){
-			target->fp2fip(Values+i, &fip);
-			pn.y = (pn.y + iround(fip.fy))>>1;	AddToPolygon(&cp, pts, &pn);
-			pn.x = iround(fip.fx);				AddToPolygon(&cp, pts, &pn);
-			pn.y = iround(fip.fy);
-			}
-		AddToPolygon(&cp, pts, &pn);
-		if((type &0xf) == 8) {
-			target->fp2fip(Values+i-2, &fip);
-			pn.y += (pn.y - iround(fip.fy))>>1;
-			AddToPolygon(&cp, pts, &pn);
-			}
-		break;
-	case 9:		case 10:
-		DrawSpline(target);
-		break;
-	case 11:
-		DrawCurve(target);
-		break;
-		}
-	if(cp < 2) return;
-	if(isPolygon) {			//for mark polygon only !!
-		AddToPolygon(&cp, pts, pts);
-		}
-	else{
-		target->SetLine(&LineDef);		target->oPolyline(pts, cp);
-		}
-	if(tmppts = (POINT*)realloc(pts, cp *sizeof(POINT))) pts = tmppts;
-	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
-	for(i = 2; i < cp; i++) UpdateMinMaxRect(&rDims, pts[i].x, pts[i].y);
-	i = 2*target->un2ix(LineDef.width);		//increase size of rectangle for marks
-	IncrementMinMaxRect(&rDims, i);
-}
-
-void
-DataLine::DoMark(anyOutput *o, bool mark)
-{
-	if(pts && cp && o){
-		if(mark){
-			if(mo) DelBitmapClass(mo);				mo = 0L;
-			memcpy(&mrc, &rDims, sizeof(RECT));
-			IncrementMinMaxRect(&mrc, 6 + o->un2ix(LineDef.width));
-			mo = GetRectBitmap(&mrc, o);
-			InvertLine(pts, cp, &LineDef, &mrc, o, mark);
-			}
-		else if(mo) RestoreRectBitmap(&mo, &mrc, o);
-		}
-}
-
-bool
-DataLine::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	bool bFound = false;
-	POINT p1;
-	int i;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(!IsInRect(&rDims, p1.x= mev->x, p1.y= mev->y) || CurrGO || !o || nPntSet <1)
-				return false; 
-			if(isPolygon && IsInPolygon(&p1, pts, cp)) bFound = true;
-			if(bFound || IsCloseToPL(p1,pts,cp)) 
-				return o->ShowMark(this, MRK_GODRAW);
-			}
-		break;
-	case CMD_SCALE:
-		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;		LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		break;
-	case CMD_SET_DATAOBJ:
-		Id = isPolygon ? GO_DATAPOLYGON : GO_DATALINE;
-		data = (DataObj*)tmpl;
-		return true;
-	case CMD_MRK_DIRTY:
-		dirty= true;
-	case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, 0L);
-		return false;
-	case CMD_LEGEND:
-		if(tmpl && ((GraphObj*)tmpl)->Id == GO_LEGEND) {
-			if(Id == GO_DATALINE) ((Legend*)tmpl)->HasFill(&LineDef, 0L, name);
-			}
-		break;
-	case CMD_SET_LINE:
-		if(tmpl) memcpy(&LineDef, tmpl, sizeof(LineDEF));
-		return true;
-	case CMD_UPDATE:
-		Undo.DataMem(this, (void**)&Values, nPnt * sizeof(lfPOINT), &nPnt, UNDO_CONTINUE);
-		Undo.ValLong(this, &nPntSet, UNDO_CONTINUE);
-		SetValues();
-		return true;
-	case CMD_AUTOSCALE:
-		if(nPntSet < 1 || !Values) return false;
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			if(dirty) {
-				min.fx = max.fx = Values[0].fx;	min.fy = max.fy = Values[0].fy;
-				for (i = 1; i <= nPntSet; i++){
-					min.fx = Values[i].fx < min.fx ? Values[i].fx : min.fx;
-					max.fx = Values[i].fx > max.fx ? Values[i].fx : max.fx;
-					min.fy = Values[i].fy < min.fy ? Values[i].fy : min.fy;
-					max.fy = Values[i].fy > max.fy ? Values[i].fy : max.fy;
-					}
-				}
-			((Plot*)parent)->CheckBounds(min.fx, min.fy);
-			((Plot*)parent)->CheckBounds(max.fx, max.fy);
-			dirty = false;
-			return true;
-			}
-		return false;
-		}
-	return false;
-}
-
-void
-DataLine::SetValues()
-{
-	AccRange *rX, *rY1=0L, *rY2=0L;
-	int i, j, k, l, m, n;
-	double x, y;
-	char yref1[500], yref2[500];
-	lfPOINT *tmpValues = Values;
-
-	if(!ssXref || !ssYref) return;
-	if(!rlp_strcpy(yref1, 500, ssYref)) return;
-	for(i = 0, yref2[0] = 0; yref1[i]; i++) {
-		if(yref1[i] == ';') {
-			yref1[i++] = 0;
-			while(yref1[i] && yref1[i] < 33) i++;
-			rlp_strcpy(yref2, 500, yref1+i);
-			}
-		}
-	nPnt = nPntSet = 0;
-	min.fx = min.fy = HUGE_VAL;		max.fx = max.fy = -HUGE_VAL;
-	rX = new AccRange(ssXref);		rY1 = new AccRange(yref1);
-	if(!rX || !rY1){
-		if(rX) delete(rX);	if(rY1) delete(rY1);
-		return;
-		}
-	if(!name) name = rY1->RangeDesc(data, 1);
-	if(yref2[0] &&((nPnt = rX->CountItems()) == (rY1->CountItems()))) {
-		if(!(Values = (lfPOINT *)realloc(Values, ((nPnt*2+2) * sizeof(lfPOINT))))) return; 
-		if(!(rY2 = new AccRange(yref2))) {
-			if(yref1) free(yref1);		if(yref2) free(yref2);
-			if(rX) delete(rX);	if(rY1) delete(rY1);
-			return;
-			}
-		if(rX->GetFirst(&i, &j) && rY1->GetFirst(&k, &l) && 
-			rX->GetNext(&i, &j) && rY1->GetNext(&k, &l) &&
-			rY2->GetFirst(&m, &n) && rY2->GetNext(&m, &n)) do {
-			if(data->GetValue(j, i, &x)){
-				if(data->GetValue(l, k, &y)){
-					Values[nPntSet].fx = x;			Values[nPntSet++].fy = y;
-					}
-				if(data->GetValue(n, m, &y)){
-					Values[nPntSet].fx = x;			Values[nPntSet++].fy = y;
-					}
-				}
-			}while(rX->GetNext(&i, &j) && rY1->GetNext(&k, &l) && rY2->GetNext(&m, &n));
-		}
-	else {
-		if((nPnt = rX->CountItems()) != (rY1->CountItems())) return;
-		if(!(Values = (lfPOINT *)realloc(Values, (nPnt+2) * sizeof(lfPOINT)))) return; 
-		if(rX->GetFirst(&i, &j) && rY1->GetFirst(&k, &l) && 
-			rX->GetNext(&i, &j) && rY1->GetNext(&k, &l)) do {
-			if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y)){
-				Values[nPntSet].fx = x;				Values[nPntSet++].fy = y;
-				}
-			}while(rX->GetNext(&i, &j) && rY1->GetNext(&k, &l));
-		}
-	nPnt = nPntSet;		nPntSet--;	dirty = true;
-	Command(CMD_AUTOSCALE, 0L, 0L);
-	if(tmpValues && Values != tmpValues) Undo.InvalidGO(this);
-	if(rX) delete(rX);	if(rY1) delete(rY1);	if(rY2) delete(rY2);
-}
-
-void
-DataLine::LineData(lfPOINT *val, long nval)
-{
-	lfPOINT *ov = Values;
-
-	if(!val || nval <2) return;
-	if(nval > nPnt && nPnt > 1 && Values){
-		if(!(Values = (lfPOINT *)realloc(Values, ((nval*2+2) * sizeof(lfPOINT))))) return; 
-		if(ov != Values) Undo.InvalidGO(this);
-		}
-	else if(!Undo.busy) Undo.DataMem(this, (void**)&Values, nPnt * sizeof(lfPOINT), &nPnt, UNDO_CONTINUE);
-	memcpy(Values, val, nval * sizeof(lfPOINT));
-	if(pts) free(pts);			pts = 0L;					dirty = true;			
-	free(val);					nPnt = nval;				nPntSet = nPnt-1;
-}
-
-void
-DataLine::DrawCurve(anyOutput *target)
-{
-	lfPOINT *sdata, *bdata;
-	POINT *tmppts;
-	int i, j, n;
-
-	if(!(sdata = (lfPOINT *)malloc(nPnt * sizeof(lfPOINT))))return;
-	sdata[0].fx = Values[0].fx;				sdata[0].fy = Values[0].fy;
-	for(i = j = 1; i < nPnt; i++) {
-		if(Values[i].fx != sdata[j-1].fx || Values[i].fy != sdata[j-1].fy) {
-			sdata[j].fx = Values[i].fx;		sdata[j++].fy = Values[i].fy;
-			}
-		}
-	n = mkCurve(sdata, j, &bdata, false);
-	if(!(tmppts = (POINT*)malloc((n+2)*sizeof(POINT))))return;
-	for(i = 0; i < n; i++){
-		tmppts[i].x = target->fx2ix(bdata[i].fx);	tmppts[i].y = target->fy2iy(bdata[i].fy);
-		}
-	for(i = cp = 0; i< (n-2); i += 3) {
-		DrawBezier(&cp, pts, tmppts[i], tmppts[i+1], tmppts[i+2], tmppts[i+3], 0);
-		}
-	if(bdata) free(bdata);	free(sdata);
-}
-
-void
-DataLine::DrawSpline(anyOutput *target)
-{
-	int i, j, k, klo, khi, ptsize = 1000;
-	double *y2, min, max, x, y, h, b, a;
-	POINT pn;
-	lfPOINT *scvals;
-	
-	if(!(y2 = (double*)malloc(sizeof(double)*(nPnt)))) return;
-	if(!(scvals = (lfPOINT*)malloc(sizeof(lfPOINT)*(nPnt)))){
-		free(y2);
-		return;
-		}
-	if((type & 0x0f) == 9 || (type & 0x0f) == 10) {
-		if((type & 0x0f) == 9) for(i = 0; i < nPnt; i++) {
-			scvals[i].fx = target->fx2fix(Values[i].fx);
-			scvals[i].fy = target->fy2fiy(Values[i].fy);
-			}
-		else for(i = 0; i < nPnt; i++) {
-			scvals[i].fy = target->fx2fix(Values[i].fx);
-			scvals[i].fx = target->fy2fiy(Values[i].fy);
-			}
-		SortFpArray(nPnt, scvals);
-		min = scvals[0].fx;			max = scvals[nPnt-1].fx;
-		for(i = j = 0; i < (nPnt-1); i++, j++) {
-			y = scvals[i].fy;			scvals[j].fx = scvals[i].fx;
-			for(k = 1; scvals[i+1].fx == scvals[i].fx; k++) {
-				y += scvals[i+1].fy;		i++;
-				}
-			scvals[j].fy = y/((double)k);
-			}
-		if(scvals[i].fx > scvals[i-1].fx) {
-			scvals[j].fx = scvals[i].fx;	scvals[j].fy = scvals[i].fy;
-			j++;
-			}
-		spline(scvals, j, y2);
-		h = scvals[1].fx - scvals[0].fx;	// klo and khi bracket the input value of x
-		for(x = min, klo = 0, i = khi = 1; x < max && i < j; x += 1.0) {
-			while(x > scvals[i].fx) {
-				klo++;		khi++;	i++;
-				h = scvals[khi].fx - scvals[klo].fx;
-				}
-			a = (scvals[khi].fx - x) / h;		b = (x - scvals[klo].fx) / h;
-			y = a * scvals[klo].fy + b * scvals[khi].fy + ((a*a*a - a) * y2[klo] + (b*b*b - b) * y2[khi]) * (h*h)/6.0;
-			if((type & 0x0f) == 9) {
-				pn.x = iround(x);		pn.y = iround(y);
-				}
-			else {
-				pn.x = iround(y);		pn.y = iround(x);
-				}
-			if(cp >= ptsize) {
-				ptsize += 1000;
-				pts = (POINT*)realloc(pts, sizeof(POINT)*ptsize); 
-				}
-			AddToPolygon(&cp, pts, &pn);
-			}
-		}
-	free(y2);	free(scvals);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// DataPolygon is a graphic object based on DataLine
-DataPolygon::DataPolygon(GraphObj *par, DataObj *d, char *xrange, char *yrange, char *nam):
-	DataLine(par, d, xrange, yrange, nam)
-{
-	lfPOINT *fp = Values;
-	char *rx = ssXref;
-	char *ry = ssYref;
-
-	FileIO(INIT_VARS);
-	Values = fp;				//FileIO will just set Values to 0L !
-	ssXref = rx;			ssYref = ry;
-	Id = GO_DATAPOLYGON;
-}
-
-DataPolygon::DataPolygon(int src):DataLine(0L, 0)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-DataPolygon::~DataPolygon()
-{
-	if(Values)free(Values);		Values =0L;
-	if(pts) free (pts);			pts = 0L;
-	if(ssXref) free(ssXref);	ssXref = 0L;
-	if(ssYref) free(ssYref);	ssYref = 0L;
-	if(mo) DelBitmapClass(mo);	mo = 0L;
-	if(name) free(name);		name = 0;
-	if(parent)parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-}
-
-void
-DataPolygon::DoPlot(anyOutput *target)
-{
-	if(!Values || nPntSet < 2) return;
-	if(mo) DelBitmapClass(mo);	mo = 0L;
-	DataLine::DoPlot(target);	//no drawing but fill pts only
-	target->SetLine(&LineDef);	target->SetFill(&pgFill);
-	target->oPolygon(pts, cp);
-}
-
-void
-DataPolygon::DoMark(anyOutput *o, bool mark)
-{
-	if(pts && cp && o){
-		if(mark){
-			if(mo) DelBitmapClass(mo);				mo = 0L;
-			memcpy(&mrc, &rDims, sizeof(RECT));
-			IncrementMinMaxRect(&mrc, 6 + o->un2ix(LineDef.width));
-			mo = GetRectBitmap(&mrc, o);
-			InvertPolygon(pts, cp, &LineDef, &pgFill, &mrc, o, mark);
-			}
-		else RestoreRectBitmap(&mo, &mrc, o);
-		}
-}
-
-bool
-DataPolygon::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch (cmd) {
-	case CMD_PG_FILL:
-		if(tmpl) {
-			memcpy((void*)&pgFill, tmpl, sizeof(FillDEF));
-			if(pgFill.hatch) memcpy((void*)&pgFillLine, (void*)pgFill.hatch, sizeof(LineDEF));
-			pgFill.hatch = (LineDEF*)&pgFillLine;
-			}
-		return true;
-	case CMD_SCALE:
-		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;			LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		pgFillLine.width *= ((scaleINFO*)tmpl)->sy.fy;		pgFillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		pgFill.scale *= ((scaleINFO*)tmpl)->sy.fy;
-		break;
-	case CMD_LEGEND:
-		if(tmpl && ((GraphObj*)tmpl)->Id == GO_LEGEND) {
-			if(Id == GO_DATAPOLYGON) ((Legend*)tmpl)->HasFill(&LineDef, &pgFill, name);
-			}
-		break;
-	default:
-		return DataLine::Command(cmd, tmpl, o);
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Calculate and display a regression line
-// Ref.: "Biometry" third edition 1995 (ed. R.R. Sokal and F.J. Rohlf),
-// W.H. Freeman and Company, New York; ISBN 0-7167-2411-1; pp. 451ff
-RegLine::RegLine(GraphObj *par, DataObj *d, lfPOINT *values, long n, int sel):
-	GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	type = sel;
-	Id = GO_REGLINE;
-	uclip.Xmin = uclip.Ymin = lim.Xmin = lim.Ymin = -1.0;
-	uclip.Xmax = uclip.Ymax = lim.Xmax = lim.Ymax = 1.0;
-	Recalc(values, n);
-}
-
-RegLine::RegLine(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) FileIO(FILE_READ);
-}
-
-RegLine::~RegLine()
-{
-	if(pts) free(pts);		pts = 0L;
-	if(parent)parent->Command(CMD_MRK_DIRTY, 0L, 0L);
-}
-
-double
-RegLine::GetSize(int select)
-{
-	double a, b;
-
-	switch(select) {
-	case SIZE_MX:		return mx;
-	case SIZE_MY:		return my;
-	case SIZE_A:
-	case SIZE_B:
-		switch(type & 0x07) {
-		case 1:		a = l2.fx;	b = l2.fy;	break;
-		case 2:		a = l3.fx;	b = l3.fy;	break;
-		case 3:		a = l4.fx;	b = l4.fy;	break;
-		case 4:		a = l5.fx;	b = l5.fy;	break;
-		default:	a = l1.fx;	b = l1.fy;	break;
-			}
-		if(select == SIZE_A) return a;
-		else return b;
-		}
-	return 0.0;
-}
-
-void
-RegLine::DoPlot(anyOutput *o)
-{
-	int i;
-	POINT pn;
-	double x, x1, y, d, a, b;
-	fRECT cliprc;
-	bool dValid;
-
-	switch (type & 0x70) {
-	case 0x20:	memcpy(&cliprc, &uclip, sizeof(fRECT));		break;
-	case 0x10:
-		if(parent) {
-			cliprc.Xmin = parent->GetSize(SIZE_BOUNDS_LEFT);
-			cliprc.Xmax = parent->GetSize(SIZE_BOUNDS_RIGHT);
-			cliprc.Ymin = parent->GetSize(SIZE_BOUNDS_BOTTOM);
-			cliprc.Ymax = parent->GetSize(SIZE_BOUNDS_TOP);
-			break;
-			}
-		//no parent: use default
-	default:	memcpy(&cliprc, &lim, sizeof(fRECT));		break;
-		}
-	if(cliprc.Xmax < cliprc.Xmin) {
-		x = cliprc.Xmax;	cliprc.Xmax = cliprc.Xmin;	cliprc.Xmin = x;
-		}
-	if(cliprc.Ymax < cliprc.Ymin) {
-		y = cliprc.Ymax;	cliprc.Ymax = cliprc.Ymin;	cliprc.Ymin = y;
-		}
-	if(cliprc.Xmin == cliprc.Xmax || cliprc.Ymin == cliprc.Ymax) return;
-	if((!pts) && (!(pts = (POINT *)malloc(sizeof(POINT)*202))))return;
-	switch(type & 0x07) {
-	case 1:		a = l2.fx;	b = l2.fy;	break;
-	case 2:		a = l3.fx;	b = l3.fy;	break;
-	case 3:		a = l4.fx;	b = l4.fy;	break;
-	case 4:		a = l5.fx;	b = l5.fy;	break;
-	default:	a = l1.fx;	b = l1.fy;	break;
-		}
-	x = cliprc.Xmin;	d = (cliprc.Xmax - cliprc.Xmin)/200.0;
-	for (cp = i = 0; i <= 200; i++){
-		dValid = true;
-		switch(type & 0x700) {
-		case 0x100:						//logarithmic x
-			if(dValid = x > defs.min4log) x1 = log10(x);
-			break;
-		case 0x200:						//reciprocal x
-			if(dValid = fabs(x) > defs.min4log) x1 = 1.0/x;
-			break;
-		case 0x300:						//square root x
-			if(dValid = fabs(x) > defs.min4log) x1 = sqrt(x);
-			break;
-		default:	x1 = x;	break;		//linear x
-			}
-		if(dValid) {
-			y = a + b*x1;
-			switch(type & 0x7000) {
-			case 0x1000:				//logarithmic y
-				y = pow(10.0, y);
-				break;
-			case 0x2000:				//reciprocal y
-				if(dValid = fabs(y) >0.0001) y = 1.0/y;
-				break;
-			case 0x3000:				//square root y
-				if(dValid = fabs(y) >0.0001) y = y*y;
-				break;
-				}
-			if(y >= cliprc.Ymin && y <= cliprc.Ymax) {
-				pn.x = o->fx2ix(x);	pn.y = o->fy2iy(y);
-				AddToPolygon(&cp, pts, &pn);
-				}
-			}
-		x += d;
-		}
-	if(cp < 2) return;
-	o->SetLine(&LineDef);			o->oPolyline(pts, cp);
-	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
-	for(i = 2; i < cp; i++) UpdateMinMaxRect(&rDims, pts[i].x, pts[i].y);
-	i = 2*o->un2ix(LineDef.width);		//increase size of rectangle for marks
-	IncrementMinMaxRect(&rDims, i);
-}
-
-void
-RegLine::DoMark(anyOutput *o, bool mark)
-{
-	if(pts && cp && o){
-		if(mark)InvertLine(pts, cp, &LineDef, &rDims, o, mark);
-		else if(parent) parent->Command(CMD_REDRAW, 0L, o);
-		}
-}
-
-bool
-RegLine::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	POINT p1;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(!IsInRect(&rDims, p1.x= mev->x, p1.y= mev->y) || CurrGO || !o || nPoints <2)
-				return false; 
-			if(IsCloseToPL(p1,pts,cp)) return o->ShowMark(CurrGO= this, MRK_GODRAW);
-			}
-		break;
-	case CMD_SCALE:
-		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;		LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		break;
-	case CMD_SET_DATAOBJ:
-		Id = GO_REGLINE;
-		return true;
-	case CMD_BOUNDS:
-		if(tmpl) {
-			memcpy(&lim, tmpl, sizeof(fRECT));
-			memcpy(&uclip, tmpl, sizeof(fRECT));
-			}
-		return true;
-	case CMD_AUTOSCALE:
-		if(nPoints < 2) return false;
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			((Plot*)parent)->CheckBounds(lim.Xmin, lim.Ymin);
-			((Plot*)parent)->CheckBounds(lim.Xmax, lim.Ymax);
-			return true;
-			}
-		return false;
-		}
-	return false;
-}
-
-void
-RegLine::Recalc(lfPOINT *values, long n)
-{
-	double sx, sy, dx, dy, sxy, sxx, syy;
-	double a, b, k;
-	long ic;
-
-	sx = sy = 0.0;
-	if((nPoints = n)<2) return;
-	for(ic = 0; ic < n; ic++) {
-		sx += values[ic].fx;		sy += values[ic].fy;
-		}
-	mx = sx /((double)nPoints);	my = sy/((double)nPoints);
-	sxy = sxx = syy = 0.0;
-	for(ic = 0; ic < n; ic++) {
-		dx = mx - values[ic].fx;	dy = my - values[ic].fy;
-		sxx += (dx*dx);	syy += (dy*dy);	sxy += (dx*dy);
-		}
-	l1.fy = sxy / sxx;			l1.fx = my - (sxy / sxx) * mx;
-	b = sxy / syy;				a = mx - (sxy / syy) * my;
-	l2.fy = 1.0/b;				l2.fx = -a / b;
-	l3.fy = (l1.fy+l2.fy)/2.0;	l3.fx = (l1.fx+l2.fx)/2.0;
-	l4.fy = sy/sx;				l4.fx = 0.0;
-	if(l5.fx == 0.0 && l5.fx == 0.0){
-		l5.fy = l1.fy;				l5.fx = l1.fx;
-		}
-	//calculate distance point from line algorithm
-	//Ref: K. Thompson, 1990: Vertical Distance from a Point to a Line. In:
-	//   Graphic Gems (Andrew S. Glassner, ed.), Academic Press,
-	//   pp. 47-48; ISBN 0-12-286165-5
-	k = (sqrt(1.0/(1.0+l1.fy*l1.fy))+sqrt(1.0/(1.0+l2.fy*l2.fy)))/2.0;
-	b = sqrt(1.0/(k*k) -1.0);
-	l3.fy = l3.fy > 0.0 ? b : -b;
-	l3.fx = my - mx * l3.fy;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Calculate and display a statnard deviation (SD-) ellipse
-SDellipse::SDellipse(GraphObj *par, DataObj *d, lfPOINT *values, long n, int sel):
-	GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	type = sel;
-	Id = GO_SDELLIPSE;
-	if(val = (lfPOINT*)malloc(n * sizeof(lfPOINT))){
-		memcpy(val, values, (nPoints = n)*sizeof(lfPOINT));
-		rl = new RegLine(this, data, values, n, type);
-		}
-}
-
-SDellipse::SDellipse(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) FileIO(FILE_READ);
-}
-
-SDellipse::~SDellipse()
-{
-	if(val) free(val);
-	if(pts) free(pts);
-	if(!(type & 0x10000) && parent && rl && 
-		parent->Command(CMD_DROP_OBJECT, rl, 0L)) return;
-	if(rl) DeleteGO(rl);
-}
-
-void
-SDellipse::DoPlot(anyOutput *o)
-{
-	int i;
-	double a1, b1, a2, b2, fv, k1, k2, ss1, ss2, np, x, dx, si, csi, fac, fac2;
-	lfPOINT fp, fip;
-	POINT p1, *tmppts;
-
-	if(!rl) return;
-	if(pts) free(pts);
-	if(!(pts = (POINT *)malloc(sizeof(POINT)*420)))return;
-	//get line data from regression line object
-	mx = rl->GetSize(SIZE_MX);		my = rl->GetSize(SIZE_MY);
-	a1 = rl->GetSize(SIZE_A);		b1 = rl->GetSize(SIZE_B);
-	b2 = -1.0/b1;	a2 = my - b2 * mx;
-	//calculate sine and cosine for back rotation
-	fv = sqrt(1.0+b1*b1);			si = b1/fv;			csi = 1.0/fv;
-	//calculate distance from line for each point and squared sum of distances
-	//Ref: K. Thompson, 1990: Vertical Distance from a Point to a Line. In:
-	//   Graphic Gems (Andrew S. Glassner, ed.), Academic Press,
-	//   pp. 47-48; ISBN 0-12-286165-5
-	k1 = sqrt(1.0/(1.0+b1*b1));			k2 = sqrt(1.0/(1.0+b2*b2));
-	// y = a + b*x;
-	ss1 = ss2 = 0.0;
-	for(i = 0; i < nPoints; i++) {
-		fv = (a1 + b1 * val[i].fx - val[i].fy) * k1;	ss1 += (fv*fv);
-		fv = (a2 + b2 * val[i].fx - val[i].fy) * k2;	ss2 += (fv*fv);
-		}
-	np = ((double)(nPoints-1));
-	//SD perpendicular and in direction of regression line
-	sd1 = sqrt(ss1 /= np);		sd2 = sqrt(ss2 /= np);
-	switch(type & 0x60000) {
-		case 0x20000:		fac = 2.0;		break;
-		case 0x40000:		fac = 3.0;		break;
-		default:			fac = 1.0;		break;
-		}
-	fac2 = fac*fac;			dx = sd2/100.0*fac;
-	for(i = 0, cp = 0, x = -(sd2*fac); i < 2; i++) {
-		do {
-			fv = (x*x)/(ss2*fac2);
-			fv = fv < 0.99999 ? sqrt((1.0-fv)*ss1*fac2) : 0.0;
-			fv = i ? fv : -fv;
-			fp.fx = mx + x * csi - fv * si;
-			fp.fy = my + x * si + fv * csi;
-			switch(type & 0x700) {
-			case 0x100:					//logarithmic x
-				fp.fx = pow(10.0, fp.fx);
-				break;
-			case 0x200:					//reciprocal x
-				if(fabs(fp.fx) > defs.min4log) fp.fx = 1.0/fp.fx;
-				else fp.fx = 0.0;
-				break;
-			case 0x300:					//square root x
-				if(fabs(fp.fx) > defs.min4log) fp.fx = fp.fx*fp.fx;
-				else fp.fx = 0.0;
-				break;
-				}
-			switch(type & 0x7000) {
-			case 0x1000:				//logarithmic y
-				fp.fy = pow(10.0, fp.fy);
-				break;
-			case 0x2000:				//reciprocal y
-				if(fabs(fp.fy) > defs.min4log) fp.fy = 1.0/fp.fy;
-				else fp.fy = 0.0;
-				break;
-			case 0x3000:				//square root y
-				if(fabs(fp.fy) > defs.min4log) fp.fy = fp.fy*fp.fy;
-				else fp.fy = 0.0;
-				break;
-				}
-			o->fp2fip(&fp, &fip);	p1.x = iround(fip.fx);		p1.y = iround(fip.fy);
-			AddToPolygon(&cp, pts, &p1);
-			}while((x += dx) < (sd2*fac) && x > (-sd2*fac));
-		x = sd2*fac;
-		dx *= -1.0;
-		}
-	o->SetLine(&LineDef);
-	if(cp > 2) {
-		AddToPolygon(&cp, pts, pts);		//close polygon
-		if(tmppts = (POINT*)realloc(pts, cp *sizeof(POINT))) pts = tmppts;
-		SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
-		for(i = 2; i < cp; i++) 
-			UpdateMinMaxRect(&rDims, pts[i].x, pts[i].y);
-		i = 3*o->un2ix(LineDef.width);		//increase size of rectangle for marks
-		IncrementMinMaxRect(&rDims, i);
-		o->oPolyline(pts, cp);
-		}
-	else {
-		free(pts);
-		cp = 0;
-		}
-	if(!(type & 0x10000))rl->DoPlot(o);
-}
-
-void
-SDellipse::DoMark(anyOutput *o, bool mark)
-{
-	if(pts && cp && o){
-		if(mark)InvertLine(pts, cp, &LineDef, &rDims, o, mark);
-		else if(parent) parent->Command(CMD_REDRAW, 0L, o);
-		}
-}
-
-bool
-SDellipse::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	POINT p1;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(!(type & 0x10000) && rl && rl->Command(cmd, tmpl, o)) return true;
-			if(!IsInRect(&rDims, p1.x= mev->x, p1.y= mev->y) || CurrGO || !o || nPoints <2)
-				return false; 
-			if(IsCloseToPL(p1,pts,cp)) return o->ShowMark(CurrGO= this, MRK_GODRAW);
-			}
-		break;
-	case CMD_SCALE:
-		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;		LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		if(rl) rl->Command(cmd, tmpl, o);
-		break;
-	case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_RMU:
-		if(!(type & 0x10000) && parent && rl && parent->Command(CMD_DROP_OBJECT, rl, o)){
-			rl = 0L;
-			return true;
-			}
-		return false;
-	case CMD_INIT:
-		if(rl) return rl->PropertyDlg();
-		break;
-	case CMD_DROP_OBJECT:
-		if(tmpl && ((GraphObj*)tmpl)->Id == GO_REGLINE && !rl) {
-			rl = (RegLine *)tmpl;
-			rl->parent = this;
-			return true;
-			}
-		return false;
-	case CMD_SET_DATAOBJ:
-		Id = GO_SDELLIPSE;
-		if(rl) rl->Command(cmd, tmpl, o);
-		return true;
-	case CMD_BOUNDS:
-		if(tmpl) {
-			if(rl) rl->Command(cmd, tmpl, o);
-			memcpy(&lim, tmpl, sizeof(fRECT));
-			}
-		return true;
-	case CMD_DELOBJ:
-		if(tmpl && tmpl == (void*)rl) {
-			Undo.ValInt(parent, &type, 0L);
-			type |= 0x10000;
-			if(parent) parent->Command(CMD_REDRAW, 0L, o);
-			return true;
-			}
-		break;
-	case CMD_AUTOSCALE:
-		if(nPoints < 2) return false;
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			((Plot*)parent)->CheckBounds(lim.Xmin, lim.Ymin);
-			((Plot*)parent)->CheckBounds(lim.Xmax, lim.Ymax);
-			return true;
-			}
-		break;
-		}
-	return false;
-}
-
-void
-SDellipse::Recalc(lfPOINT *values, long n)
-{
-	if(val) free(val);
-	if(val = (lfPOINT*)malloc(n * sizeof(lfPOINT)))
-		memcpy(val, values, (nPoints = n)*sizeof(lfPOINT));
-	if(rl) rl->Recalc(values, n);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Error bars are simple graphic objects
-ErrorBar::ErrorBar(GraphObj *par, DataObj *d, double x, double y, double err, int which,
-	int xc, int xr, int yc, int yr, int ec, int er):GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	fPos.fx = x;		fPos.fy = y;
-	ferr = err;			type = which;
-	Id = GO_ERRBAR;		data = d;
-	if(xc >= 0 || xr >= 0 || yc >= 0 || yr >= 0 || ec >= 0 || er >= 0) {
-		if(ssRef = (POINT*)malloc(sizeof(POINT)*3)) {
-			ssRef[0].x = xc;	ssRef[0].y = xr;
-			ssRef[1].x = yc;	ssRef[1].y = yr;
-			ssRef[2].x = ec;	ssRef[2].y = er;
-			cssRef = 3;
-			}
-		}
-	Command(CMD_AUTOSCALE, 0L, 0L);
-}
-
-ErrorBar::ErrorBar(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	type = ERRBAR_VSYM;
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-ErrorBar::~ErrorBar()
-{
-	if(mo) DelBitmapClass(mo);	mo = 0L;
-	if(ssRef) free(ssRef);		ssRef = 0L;
-	if(name) free(name);		name = 0L;
-}
-
-bool
-ErrorBar::SetSize(int select, double value)
-{
-	switch(select & 0xfff) {
-	case SIZE_ERRBAR: 
-		SizeBar = value;
-		return true;
-	case SIZE_ERRBAR_LINE:
-		ErrLine.width = value;
-		return true;
-		}
-	return false;
-}
-
-bool
-ErrorBar::SetColor(int select, DWORD col)
-{
-	switch(select & 0xfff) {
-	case COL_ERROR_LINE:
-		ErrLine.color = col;
-		return true;
-		}
-	return false;
-}
-
-void
-ErrorBar::DoPlot(anyOutput *target)
-{
-	int ie;
-
-	switch (type & 0x0ff) {
-	case ERRBAR_VSYM:
-	case ERRBAR_VUP:
-	case ERRBAR_VDOWN:
-		ie = target->un2ix(SizeBar/2.0);
-		break;
-	default:
-		ie = target->un2iy(SizeBar/2.0);
-		break;
-		}
-	target->SetLine(&ErrLine);
-	switch(type) {
-	case ERRBAR_VSYM:
-		ebpts[0].x = ebpts[1].x = target->fx2ix(fPos.fx);
-		ebpts[0].y = target->fy2iy(fPos.fy-ferr);
-		ebpts[4].y = ebpts[5].y = ebpts[1].y = target->fy2iy(fPos.fy+ferr);
-		if(ebpts[1].y != ebpts[0].y) target->oSolidLine(ebpts);
-		ebpts[4].x = ebpts[2].x = ebpts[0].x - ie;
-		ebpts[5].x = ebpts[3].x = ebpts[1].x + ie+1;
-		ebpts[2].y = ebpts[3].y = ebpts[0].y;
-		if(ebpts[3].x > ebpts[2].x) {
-			target->oSolidLine(ebpts+2);
-			target->oSolidLine(ebpts+4);
-			}
-		rDims.left =  ebpts[2].x;		rDims.right = ebpts[3].x;
-		rDims.top = ebpts[0].y;			rDims.bottom = ebpts[1].y;
-		break;
-	case ERRBAR_VUP:
-	case ERRBAR_VDOWN:
-		ebpts[0].x = ebpts[1].x = target->fx2ix(fPos.fx);
-		ebpts[0].y = target->fy2iy(fPos.fy);
-		ebpts[2].y = ebpts[3].y = ebpts[1].y = 
-			target->fy2iy(type == ERRBAR_VUP ? fPos.fy+ferr : fPos.fy-ferr);
-		if(ebpts[1].y != ebpts[0].y) target->oSolidLine(ebpts);
-		ebpts[2].x = ebpts[0].x - ie;
-		ebpts[3].x = ebpts[1].x + ie+1;
-		if(ebpts[3].x > ebpts[2].x) target->oSolidLine(ebpts+2);
-		rDims.left =  ebpts[2].x;		rDims.right = ebpts[3].x;
-		rDims.top = ebpts[0].y;			rDims.bottom = ebpts[1].y;
-		break;
-	case ERRBAR_HSYM:
-		ebpts[2].x = ebpts[3].x = ebpts[0].x = target->fx2ix(fPos.fx-ferr);
-		ebpts[4].x = ebpts[5].x = ebpts[1].x = target->fx2ix(fPos.fx+ferr);
-		ebpts[0].y = ebpts[1].y = target->fy2iy(fPos.fy);
-		if(ebpts[1].x != ebpts[0].x) target->oSolidLine(ebpts);
-		ebpts[2].y = ebpts[4].y = ebpts[0].y - ie;
-		ebpts[3].y = ebpts[5].y = ebpts[1].y + ie+1;
-		if(ebpts[3].y >ebpts[2].y) {
-			target->oSolidLine(ebpts+2);
-			target->oSolidLine(ebpts+4);
-			}
-		rDims.left =  ebpts[0].x;		rDims.right = ebpts[1].x;
-		rDims.top = ebpts[2].y;			rDims.bottom = ebpts[3].y;
-		break;
-	case ERRBAR_HLEFT:
-	case ERRBAR_HRIGHT:
-		ebpts[0].x = target->fx2ix(fPos.fx);
-		ebpts[0].y = ebpts[1].y = target->fy2iy(fPos.fy);
-		ebpts[2].x = ebpts[3].x = ebpts[1].x = 
-			target->fx2ix(type == ERRBAR_HRIGHT ? fPos.fx+ferr : fPos.fx-ferr);
-		if(ebpts[1].x != ebpts[0].x) target->oSolidLine(ebpts);
-		ebpts[2].y = ebpts[0].y - ie;
-		ebpts[3].y = ebpts[1].y + ie+1;
-		if(ebpts[3].y > ebpts[2].y) target->oSolidLine(ebpts+2);
-		rDims.left =  ebpts[0].x;		rDims.right = ebpts[1].x;
-		rDims.top = ebpts[2].y;			rDims.bottom = ebpts[3].y;
-		break;
-		}
-	if(rDims.left > rDims.right) Swap(rDims.left, rDims.right);
-	if(rDims.top > rDims.bottom) Swap(rDims.top, rDims.bottom);
-	IncrementMinMaxRect(&rDims, 2);
-}
-
-void
-ErrorBar::DoMark(anyOutput *o, bool mark)
-{
-	int i;
-	LineDEF OldLine;
-
-	if(mark){
-		if(mo) DelBitmapClass(mo);			mo = 0L;
-		memcpy(&mrc, &rDims, sizeof(RECT));
-		memcpy(&OldLine, &ErrLine, sizeof(LineDEF));
-		i = 3*o->un2ix(ErrLine.width);		//increase size of rectangle for marks
-		IncrementMinMaxRect(&mrc, i);		mo = GetRectBitmap(&mrc, o);
-		ErrLine.width *= 5.0;				DoPlot(o);
-		ErrLine.width = OldLine.width;		ErrLine.color = OldLine.color ^ 0x00ffffffL;
-		DoPlot(o);							o->UpdateRect(&mrc, false);
-		memcpy(&ErrLine, &OldLine, sizeof(LineDEF));
-		}
-	else if(mo) RestoreRectBitmap(&mo, &mrc, o);
-}
-
-bool
-ErrorBar::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	bool bFound;
-	int cb;
-
-	switch (cmd) {
-	case CMD_SCALE:
-		ErrLine.width *= ((scaleINFO*)tmpl)->sy.fy;		ErrLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		SizeBar *= ((scaleINFO*)tmpl)->sy.fy;
-		return true;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		bFound = false;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(!IsInRect(&rDims, mev->x, mev->y) || CurrGO) return false; 
-			switch (type) {
-			case ERRBAR_HSYM:		case ERRBAR_HLEFT:		case ERRBAR_HRIGHT:
-				if(mev->y >= (ebpts[0].y-2) && mev->y <= (ebpts[1].y+2)) bFound = true;
-				else if(mev->x >= (ebpts[2].x-2) && mev->x <= (ebpts[2].x+2)) bFound = true;
-				else if(type == ERRBAR_HSYM && mev->x >= (ebpts[4].x-2) &&
-					mev->x <= (ebpts[4].x + 2)) bFound = true;
-				break;
-			case ERRBAR_VSYM:		case ERRBAR_VUP:		case ERRBAR_VDOWN:
-				if(mev->x >= (ebpts[0].x-2) && mev->x <= (ebpts[1].x+2)) bFound = true;
-				else if(mev->y >= (ebpts[2].y-2) && mev->y <= (ebpts[2].y+2)) bFound = true;
-				else if(type == ERRBAR_VSYM && mev->y >= (ebpts[4].y-2) &&
-					mev->y <= (ebpts[4].y + 2)) bFound = true;
-				break;
-				}
-			if(bFound) o->ShowMark(this, MRK_GODRAW);
-			}
-		return false;
-	case CMD_SET_DATAOBJ:
-		Id = GO_ERRBAR;
-		data = (DataObj *) tmpl;
-		return true;
-	case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_UPDATE:
-		if(ssRef && cssRef >2 && data) {
-			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
-			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
-			data->GetValue(ssRef[2].y, ssRef[2].x, &ferr);
-			return true;
-			}
-		return false;
-	case CMD_LEGEND:
-		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
-		switch(type) {
-			case ERRBAR_VSYM:		case ERRBAR_VUP:		case ERRBAR_VDOWN:
-				((Legend*)tmpl)->HasErr(&ErrLine, 1, name);
-				break;
-			case ERRBAR_HSYM:		case ERRBAR_HLEFT:		case ERRBAR_HRIGHT:
-				((Legend*)tmpl)->HasErr(&ErrLine, 2, name);
-				break;
-			}
-		break;
-	case CMD_ERRDESC:
-		if(tmpl && *((char *)tmpl)){
-			cb = (int)strlen((char*)tmpl)+2;
-			if(name = (char*)realloc(name, cb)) rlp_strcpy(name, cb, (char*)tmpl);
-			return true;
-			}
-		return false;
-	case CMD_ERR_TYPE:
-		if(tmpl) type = *((int*)tmpl);
-		return true;
-	case CMD_AUTOSCALE:
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			switch(type) {
-			case ERRBAR_VSYM:	
-				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy+ferr);
-				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy-ferr);		break;
-			case ERRBAR_VUP:	
-				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
-				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy+ferr);		break;
-			case ERRBAR_VDOWN:
-				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
-				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy-ferr);		break;
-			case ERRBAR_HSYM:
-				((Plot*)parent)->CheckBounds(fPos.fx+ferr, fPos.fy);
-				((Plot*)parent)->CheckBounds(fPos.fx-ferr, fPos.fy);		break;
-			case ERRBAR_HLEFT:
-				((Plot*)parent)->CheckBounds(fPos.fx-ferr, fPos.fy);
-				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);				break;
-			case ERRBAR_HRIGHT:
-				((Plot*)parent)->CheckBounds(fPos.fx+ferr, fPos.fy);
-				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);				break;
-				}
-			return true;
-			}
-		break;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// arrows to data points or with absolute coordinates
-Arrow::Arrow(GraphObj * par, DataObj *d, lfPOINT fp1, lfPOINT fp2, int which,
-	int xc1, int xr1, int yc1, int yr1, int xc2, int xr2, int yc2, int yr2):
-	GraphObj(par, d)
-{
-	double dx, dy;
-
-	FileIO(INIT_VARS);
-	memcpy(&pos1, &fp1, sizeof(lfPOINT));
-	memcpy(&pos2, &fp2, sizeof(lfPOINT));
-	type = which;
-	if(type & ARROW_UNITS) {
-		dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
-		pos1.fx -= dx;	pos1.fy -= dy;			pos2.fx -= dx;	pos2.fy -= dy;
-		}
-	if(xc1 >= 0 || xr1 >= 0 || yc1 >= 0 || yr1 >= 0 || xc2 >= 0 || xr2 >= 0 || 
-		yc2 >= 0 || yr2 >= 0) {
-		if(ssRef = (POINT*)malloc(sizeof(POINT)*4)) {
-			ssRef[0].x = xc1;	ssRef[0].y = xr1;
-			ssRef[1].x = yc1;	ssRef[1].y = yr1;
-			ssRef[2].x = xc2;	ssRef[2].y = xr2;
-			ssRef[3].x = yc2;	ssRef[3].y = yr2;
-			cssRef = 4;
-			}
-		}
-	bModified = false;
-}
-
-Arrow::Arrow(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	bModified = false;
-}
-
-Arrow::~Arrow()
-{
-	Command(CMD_FLUSH, 0L, 0L);
-	if(bModified) Undo.InvalidGO(this);
-}
-
-double
-Arrow::GetSize(int select)
-{
-	switch(select) {
-	case SIZE_XPOS:		return pos1.fx;
-	case SIZE_XPOS+1:	return pos2.fx;
-	case SIZE_YPOS:		return pos1.fy;
-	case SIZE_YPOS+1:	return pos2.fy;
-	case SIZE_GRECT_LEFT:	case SIZE_GRECT_TOP:
-	case SIZE_GRECT_RIGHT:	case SIZE_GRECT_BOTTOM:
-		if(parent) return parent->GetSize(select);
-		break;
-		}
-	return 0.0;
-}
-
-bool
-Arrow::SetSize(int select, double value)
-{
-	switch(select & 0xfff) {
-	case SIZE_ARROW_LINE:		LineDef.width = value;		return true;
-	case SIZE_ARROW_CAPWIDTH:	cw = value;					return true;
-	case SIZE_ARROW_CAPLENGTH:	cl = value;					return true;
-	case SIZE_XPOS:				pos1.fx = value;			return true;
-	case SIZE_XPOS+1:			pos2.fx = value;			return true;
-	case SIZE_YPOS:				pos1.fy = value;			return true;
-	case SIZE_YPOS+1:			pos2.fy = value;			return true;
-		}
-	return false;
-}
-
-bool
-Arrow::SetColor(int select, DWORD col)
-{
-	switch(select & 0xfff) {
-	case COL_ARROW:
-		LineDef.color = col;
-		return true;
-		}
-	return false;
-}
-
-void
-Arrow::DoPlot(anyOutput *o)
-{
-	double si, csi, tmp, fix1, fiy1, fix2, fiy2, dx, dy;
-
-	if(!o || !parent) return;
-	if(type & ARROW_UNITS) {
-		dx = parent->GetSize(SIZE_GRECT_LEFT);		dy = parent->GetSize(SIZE_GRECT_TOP);
-		fix1 = o->co2fix(pos1.fx+dx);	fix2 = o->co2fix(pos2.fx+dx);
-		fiy1 = o->co2fiy(pos1.fy+dy);	fiy2 = o->co2fiy(pos2.fy+dy);
-		}
-	else {
-		fix1 = o->fx2fix(pos1.fx);		fix2 = o->fx2fix(pos2.fx);
-		fiy1 = o->fy2fiy(pos1.fy);		fiy2 = o->fy2fiy(pos2.fy);
-		}
-	if(fix1 == fix2 && fiy1 == fiy2) return;	//zero length
-	//draw arrow line
-	pts[0].x = iround(fix1);		pts[1].x = iround(fix2);
-	pts[0].y = iround(fiy1);		pts[1].y = iround(fiy2);
-	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
-	//calculate sine and cosine for cap
-	si = fiy1-fiy2;
-	tmp = fix2 - fix1;
-	si = si/sqrt(si*si + tmp*tmp);
-	csi = fix2-fix1;
-	tmp = fiy2 - fiy1;
-	csi = csi/sqrt(csi*csi + tmp*tmp);
-	//draw cap
-	pts[2].x = pts[1].x - o->un2ix(csi*cl + si*cw/2.0);
-	pts[2].y = pts[1].y + o->un2iy(si*cl - csi*cw/2.0);
-	pts[3].x = pts[1].x;		pts[3].y = pts[1].y;
-	pts[4].x = pts[1].x - o->un2ix(csi*cl - si*cw/2.0);
-	pts[4].y = pts[1].y + o->un2iy(si*cl + csi*cw/2.0);
-	switch(type & 0xff) {
-	case ARROW_NOCAP:
-		pts[2].x = pts[3].x = pts[4].x = pts[1].x;
-		pts[2].y = pts[3].y = pts[4].y = pts[1].y;
-		break;
-		}
-	UpdateMinMaxRect(&rDims, pts[2].x, pts[2].y);
-	UpdateMinMaxRect(&rDims, pts[4].x, pts[4].y);
-	IncrementMinMaxRect(&rDims, 3*o->un2ix(LineDef.width)+3);
-	Redraw(o);
-}
-
-void
-Arrow::DoMark(anyOutput *o, bool mark)
-{
-	LineDEF OldLine;
-
-	if(type & ARROW_UNITS) {
-		if(!dh1) dh1 = new dragHandle(this, DH_12);
-		if(!dh2) dh2 = new dragHandle(this, DH_22);
-		}
-	else {
-		if (dh1) DeleteGO(dh1);		if (dh2) DeleteGO(dh2);
-		dh1 = dh2 = 0L;
-		}
-	if(mark) {
-		if(mo) DelBitmapClass(mo);					mo = 0L;
-		if(dh1 && dh2) {
-			memcpy(&mrc, &rDims, sizeof(RECT));		memcpy(&OldLine, &LineDef, sizeof(LineDEF));
-			mo = GetRectBitmap(&mrc, o);			Redraw(o);
-			dh1->DoPlot(o);		dh2->DoPlot(o);
-			}
-		else {
-			memcpy(&mrc, &rDims, sizeof(RECT));		memcpy(&OldLine, &LineDef, sizeof(LineDEF));
-			mo = GetRectBitmap(&mrc, o);			LineDef.color = 0x00000000L;
-			LineDef.width = OldLine.width *3.0;		Redraw(o);
-			LineDef.width = OldLine.width;			LineDef.color = OldLine.color ^ 0x00ffffffL;
-			Redraw(o);								o->UpdateRect(&mrc, false);
-			memcpy(&LineDef, &OldLine, sizeof(LineDEF));
-			}
-		}
-	else if(mo) RestoreRectBitmap(&mo, &mrc, o);
-}
-
-bool
-Arrow::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-
-	switch (cmd) {
-	case CMD_SAVEPOS:
-		bModified = true;
-		Undo.SaveLFP(this, &pos1, 0L);
-		Undo.SaveLFP(this, &pos2, UNDO_CONTINUE);
-		return true;
-	case CMD_SCALE:
-		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;		LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		cw *= ((scaleINFO*)tmpl)->sy.fy;				cl *= ((scaleINFO*)tmpl)->sy.fy;
-		if(type & ARROW_UNITS) {
-			pos1.fx = ((scaleINFO*)tmpl)->sx.fx + pos1.fx * ((scaleINFO*)tmpl)->sx.fy;
-			pos1.fy = ((scaleINFO*)tmpl)->sy.fx + pos1.fy * ((scaleINFO*)tmpl)->sy.fy;
-			pos2.fx = ((scaleINFO*)tmpl)->sx.fx + pos2.fx * ((scaleINFO*)tmpl)->sx.fy;
-			pos2.fy = ((scaleINFO*)tmpl)->sy.fx + pos2.fy * ((scaleINFO*)tmpl)->sy.fy;
-			}
-		return true;
-	case CMD_FLUSH:
-		if (dh1) DeleteGO(dh1);			dh1 = 0L;
-		if (dh2) DeleteGO(dh2);			dh2 = 0L;
-		if(mo) DelBitmapClass(mo);		mo = 0L;
-		if(ssRef) free(ssRef);			ssRef = 0L;
-		if(name) free(name);			name = 0L;
-		return true;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-		if(!CurrGO && ObjThere(mev->x, mev->y)){
-			o->ShowMark(this, MRK_GODRAW);
-			return true;
-			}
-		}
-		break;
-	case CMD_ARROW_ORG:
-		memcpy(&pos1, tmpl, sizeof(lfPOINT));
-		if(ssRef && cssRef >3) 
-			ssRef[0].x = ssRef[0].y = ssRef[1].x = ssRef[1].y = -1;
-		return true;
-	case CMD_ARROW_TYPE:
-		if(tmpl) {
-			type &= ~0xff;		type |= (*((int*)tmpl));
-			return true;
-			}
-		return false;
-	case CMD_SET_DATAOBJ:
-		Id = GO_ARROW;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_UPDATE:
-		if(ssRef && cssRef >3 && data) {
-			data->GetValue(ssRef[0].y, ssRef[0].x, &pos1.fx);
-			data->GetValue(ssRef[1].y, ssRef[1].x, &pos1.fy);
-			data->GetValue(ssRef[2].y, ssRef[2].x, &pos2.fx);
-			data->GetValue(ssRef[3].y, ssRef[3].x, &pos2.fy);
-			return true;
-			}
-		return false;
-	case CMD_AUTOSCALE:
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			((Plot*)parent)->CheckBounds(pos1.fx, pos1.fy);
-			((Plot*)parent)->CheckBounds(pos2.fx, pos2.fy);
-			return true;
-			}
-		break;
-	case CMD_MRK_DIRTY:			//from Undo ?
-	case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_MOVE:
-		bModified = true;
-	case CMD_UNDO_MOVE:
-		if(type & ARROW_UNITS) {
-			if(cmd == CMD_MOVE) Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
-			pos1.fx += ((lfPOINT*)tmpl)[0].fx;	pos1.fy += ((lfPOINT*)tmpl)[0].fy;
-			pos2.fx += ((lfPOINT*)tmpl)[0].fx;	pos2.fy += ((lfPOINT*)tmpl)[0].fy;
-			if(o){
-				o->StartPage();		parent->DoPlot(o);		o->EndPage();
-				}
-			return true;
-			}
-		break;
-		}
-	return false;
-}
-
-void
-Arrow::Redraw(anyOutput *o)
-{
-	FillDEF FillCap;
-
-	o->SetLine(&LineDef);
-	o->oSolidLine(pts);
-	switch(type & 0xff) {
-	case ARROW_NOCAP:
-		break;
-	case ARROW_LINE:
-		o->oSolidLine(pts+2);
-		o->oSolidLine(pts+3);
-		break;
-	case ARROW_TRIANGLE:
-		FillCap.type = FILL_NONE;
-		FillCap.color = LineDef.color;
-		FillCap.scale = 1.0f;
-		FillCap.hatch = 0L;
-		o->SetFill(&FillCap);
-		o->oPolygon(pts+2, 3);
-		break;
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// universal boxes
-Box::Box(GraphObj * par, DataObj *d, lfPOINT fp1, lfPOINT fp2, int which,
-	int xc1, int xr1, int yc1, int yr1, int xc2, int xr2,
-		int yc2, int yr2):GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	memcpy(&pos1, &fp1, sizeof(lfPOINT));
-	memcpy(&pos2, &fp2, sizeof(lfPOINT));
-	type = which;
-	Id = GO_BOX;
-	if(xc1 >= 0 || xr1 >= 0 || yc1 >= 0 || yr1 >= 0 || xc2 >= 0 || xr2 >= 0 || 
-		yc2 >= 0 || yr2 >= 0) {
-		if(ssRef = (POINT*)malloc(sizeof(POINT)*4)) {
-			ssRef[0].x = xc1;	ssRef[0].y = xr1;
-			ssRef[1].x = yc1;	ssRef[1].y = yr1;
-			ssRef[2].x = xc2;	ssRef[2].y = xr2;
-			ssRef[3].x = yc2;	ssRef[3].y = yr2;
-			cssRef = 4;
-			}
-		}
-}
-
-Box::Box(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-Box::~Box()
-{
-	Command(CMD_FLUSH, 0L, 0L);
-}
-
-double
-Box::GetSize(int select)
-{
-	switch(select) {
-	case SIZE_XPOS:
-		return (pos1.fx + pos2.fx)/2.0;
-	case SIZE_YPOS:
-		return (pos1.fy + pos2.fy)/2.0;
-		}
-	return 1.0;
-}
-
-bool
-Box::SetSize(int select, double value)
-{
-	switch(select & 0xfff) {
-	case SIZE_BOX_LINE:		Outline.width = value;		return true;
-	case SIZE_BOX:			size = value;				return true;
-	case SIZE_XPOS:			pos1.fx = value;			return true;
-	case SIZE_XPOS+1:		pos2.fx = value;			return true;
-	case SIZE_YPOS:			pos1.fy = value;			return true;
-	case SIZE_YPOS+1:		pos2.fy = value;			return true;
-		}
-	return false;
-}
-
-bool
-Box::SetColor(int select, DWORD col)
-{
-	switch(select & 0xfff) {
-	case COL_BOX_LINE:
-		Outline.color = col;
-		return true;
-		}
-	return false;
-}
-
-void
-Box::DoPlot(anyOutput *o)
-{
-	double si, csi, tmp, fix1, fiy1, fix2, fiy2, fsize, dx, dy;
-
-	if(!parent || !o || size <= 0.001) return;
-	o->SetLine(&Outline);
-	o->SetFill(&Fill);
-	//calculate coordinates
-	fix1 = o->fx2fix(pos1.fx);	fix2 = o->fx2fix(pos2.fx);
-	fiy1 = o->fy2fiy(pos1.fy);	fiy2 = o->fy2fiy(pos2.fy);
-	//calculate sine and cosine
-	si = fiy1-fiy2;
-	tmp = fix2 - fix1;
-	si = si/sqrt(si*si + tmp*tmp);
-	csi = fix2-fix1;
-	tmp = fiy2 - fiy1;
-	csi = csi/sqrt(csi*csi + tmp*tmp);
-	if(type & BAR_WIDTHDATA) {			//use e.g. for density distribution
-		dx = si * size;					dy = csi * size;
-		pts[0].x = o->fx2ix(pos1.fx + dx);	pts[1].x = o->fx2ix(pos2.fx + dx);
-		pts[2].x = o->fx2ix(pos2.fx - dx);	pts[3].x = o->fx2ix(pos1.fx - dx);
-		pts[0].y = o->fy2iy(pos1.fy + dy);	pts[1].y = o->fy2iy(pos2.fy + dy);
-		pts[2].y = o->fy2iy(pos2.fy - dy);	pts[3].y = o->fy2iy(pos1.fy - dy);
-		}
-	else if(type & BAR_RELWIDTH) {
-		if(!parent || (pos1.fy == pos2.fy && pos1.fx == pos2.fx)) return;
-		fsize = parent->GetSize(pos1.fy == pos2.fy ? SIZE_BOXMINY : SIZE_BOXMINX);
-		fsize = fsize * size /200.0;
-		dx = si * fsize;					dy = csi * fsize;
-		pts[0].x = o->fx2ix(pos1.fx + dx);	pts[1].x = o->fx2ix(pos2.fx + dx);
-		pts[2].x = o->fx2ix(pos2.fx - dx);	pts[3].x = o->fx2ix(pos1.fx - dx);
-		pts[0].y = o->fy2iy(pos1.fy + dy);	pts[1].y = o->fy2iy(pos2.fy + dy);
-		pts[2].y = o->fy2iy(pos2.fy - dy);	pts[3].y = o->fy2iy(pos1.fy - dy);
-		}
-	else {
-		dx = o->un2fix(si*size/2.0);		dy = o->un2fiy(csi*size/2.0);
-		pts[0].x = iround(fix1 + dx);	pts[1].x = iround(fix2 + dx);
-		pts[2].x = iround(fix2 - dx);	pts[3].x = iround(fix1 - dx);
-		pts[0].y = iround(fiy1 + dy);	pts[1].y = iround(fiy2 + dy);
-		pts[2].y = iround(fiy2 - dy);	pts[3].y = iround(fiy1 - dy);
-		}
-	pts[4].x = pts[0].x;		pts[4].y = pts[0].y;	//close polygon
-	if(pts[0].x == pts[1].x){
-		o->oRectangle(pts[3].x, pts[3].y, pts[1].x, pts[1].y, name);
-		}
-	else {
-		o->oPolygon(pts, 5);
-		}
-	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[2].x, pts[2].y);
-	UpdateMinMaxRect(&rDims, pts[1].x, pts[1].y);
-	UpdateMinMaxRect(&rDims, pts[3].x, pts[3].y);
-}
-
-void
-Box::DoMark(anyOutput *o, bool mark)
-{
-	InvertPolygon(pts, 5, &Outline, &Fill, &rDims, o, mark);
-}
-
-bool
-Box::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	POINT p;
-
-	switch (cmd) {
-	case CMD_SCALE:
-		Outline.width *= ((scaleINFO*)tmpl)->sy.fy;
-		Hatchline.width *= ((scaleINFO*)tmpl)->sy.fy;
-		Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;
-		if(!(type & BAR_RELWIDTH) && parent->Id!= GO_DENSDISP)size *= ((scaleINFO*)tmpl)->sy.fy;
-		return true;
-	case CMD_FLUSH:
-		if(ssRef) free(ssRef);		ssRef = 0L;
-		if(name)free(name);			name = 0L;
-		return true;
-	case CMD_LEGEND:
-		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
-		((Legend*)tmpl)->HasFill(&Outline, &Fill, name);
-		break;
-	case CMD_MRK_DIRTY:				case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
-				if(pts[0].x == pts[1].x || pts[0].y == pts[1].y) {
-					o->ShowMark(CurrGO = this, MRK_GODRAW);
-					return true;
-					}
-				else {
-					p.x = mev->x;		p.y = mev->y;
-					if(IsInPolygon(&p, pts, 5)) {
-						o->ShowMark(CurrGO = this, MRK_GODRAW);
-						return true;
-						}
-					}
-				}
-			break;
-			}
-		return false;
-	case CMD_SET_DATAOBJ:
-		Id = GO_BOX;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_UPDATE:
-		if(ssRef && cssRef >3 && data) {
-			data->GetValue(ssRef[0].y, ssRef[0].x, &pos1.fx);
-			data->GetValue(ssRef[1].y, ssRef[1].x, &pos1.fy);
-			data->GetValue(ssRef[2].y, ssRef[2].x, &pos2.fx);
-			data->GetValue(ssRef[3].y, ssRef[3].x, &pos2.fy);
-			return true;
-			}
-		return false;
-	case CMD_BOX_TYPE:
-		if(tmpl)type = *((int*)tmpl);
-		return true;
-	case CMD_BOX_FILL:
-		if(tmpl) {
-			memcpy(&Fill, tmpl, sizeof(FillDEF));
-			if(Fill.hatch) memcpy(&Hatchline, Fill.hatch, sizeof(LineDEF));
-			Fill.hatch = &Hatchline;
-			return true;
-			}
-		break;
-	case CMD_AUTOSCALE:
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			if(type & BAR_WIDTHDATA) {
-				if(pos1.fy != pos2.fy) {
-					((Plot*)parent)->CheckBounds(pos1.fx+size, pos1.fy);
-					((Plot*)parent)->CheckBounds(pos2.fx-size, pos2.fy);
-					}
-				else {
-					((Plot*)parent)->CheckBounds(pos1.fx, pos1.fy+size);
-					((Plot*)parent)->CheckBounds(pos2.fx, pos2.fy-size);
-					}
-				}
-			else {
-				((Plot*)parent)->CheckBounds(pos1.fx, pos1.fy);
-				((Plot*)parent)->CheckBounds(pos2.fx, pos2.fy);
-				if(parent && (type & BAR_RELWIDTH)) {
-					if(pos1.fy == pos2.fy) {
-						((Plot*)parent)->CheckBounds(pos2.fx, pos2.fy + parent->GetSize(SIZE_BOXMINY));
-						((Plot*)parent)->CheckBounds(pos2.fx, pos2.fy - parent->GetSize(SIZE_BOXMINY));
-						}
-					else if(pos1.fx == pos2.fx) {
-						((Plot*)parent)->CheckBounds(pos2.fx + parent->GetSize(SIZE_BOXMINX), pos2.fy);
-						((Plot*)parent)->CheckBounds(pos2.fx - parent->GetSize(SIZE_BOXMINX), pos2.fy);
-						}
-					}
-				}
-			return true;
-			}
-		break;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// whisker 
-Whisker::Whisker(GraphObj *par, DataObj *d, lfPOINT fp1, lfPOINT fp2, int which,
-	int xc1, int xr1, int yc1, int yr1, int xc2, int xr2,
-	int yc2, int yr2):GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	memcpy(&pos1, &fp1, sizeof(lfPOINT));
-	memcpy(&pos2, &fp2, sizeof(lfPOINT));
-	type = which;
-	Id = GO_WHISKER;
-	if(xc1 >= 0 || xr1 >= 0 || yc1 >= 0 || yr1 >= 0 || xc2 >= 0 || xr2 >= 0 || 
-		yc2 >= 0 || yr2 >= 0) {
-		if(ssRef = (POINT*)malloc(sizeof(POINT)*4)) {
-			ssRef[0].x = xc1;	ssRef[0].y = xr1;
-			ssRef[1].x = yc1;	ssRef[1].y = yr1;
-			ssRef[2].x = xc2;	ssRef[2].y = xr2;
-			ssRef[3].x = yc2;	ssRef[3].y = yr2;
-			cssRef = 4;
-			}
-		}
-	Command(CMD_AUTOSCALE, 0L, 0L);
-}
-
-Whisker::Whisker(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-Whisker::~Whisker()
-{
-	if(mo) DelBitmapClass(mo);	mo = 0L;
-	if(ssRef) free(ssRef);		ssRef = 0L;
-	if(name) free(name);		name = 0L;
-}
-
-bool
-Whisker::SetSize(int select, double value)
-{
-	switch(select & 0xfff) {
-	case SIZE_WHISKER: 
-		size = value;
-		return true;
-	case SIZE_WHISKER_LINE:
-		LineDef.width = value;
-		return true;
-		}
-	return false;
-}
-
-bool
-Whisker::SetColor(int select, DWORD col)
-{
-	switch(select & 0xfff) {
-	case COL_WHISKER:
-		LineDef.color = col;
-		return true;
-		}
-	return false;
-}
-
-void
-Whisker::DoPlot(anyOutput *o)
-{
-	double si, csi, tmp, fix1, fiy1, fix2, fiy2, dx, dy;
-	int i;
-
-	fix1 = o->fx2fix(pos1.fx);	fix2 = o->fx2fix(pos2.fx);
-	fiy1 = o->fy2fiy(pos1.fy);	fiy2 = o->fy2fiy(pos2.fy);
-	if(fix1 == fix2 && fiy1 == fiy2) return;	//zero length
-	pts[2].x = iround(fix1);		pts[3].x = iround(fix2);
-	pts[2].y = iround(fiy1);		pts[3].y = iround(fiy2);
-	//calculate sine and cosine
-	si = fiy1-fiy2;
-	tmp = fix2 - fix1;
-	si = si/sqrt(si*si + tmp*tmp);
-	csi = fix2-fix1;
-	tmp = fiy2 - fiy1;
-	csi = csi/sqrt(csi*csi + tmp*tmp);
-	dx = o->un2fix(si*size/2.0);
-	dy = o->un2fiy(csi*size/2.0);
-	//calc cap
-	pts[0].x = iround(fix1 - dx);	pts[4].x = iround(fix2 - dx);
-	pts[0].y = iround(fiy1 - dy);	pts[4].y = iround(fiy2 - dy);
-	pts[1].x = iround(fix1 + dx);	pts[5].x = iround(fix2 + dx);
-	pts[1].y = iround(fiy1 + dy);	pts[5].y = iround(fiy2 + dy);
-	//draw whisker
-	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
-	UpdateMinMaxRect(&rDims, pts[4].x, pts[4].y);
-	UpdateMinMaxRect(&rDims, pts[5].x, pts[5].y);
-	IncrementMinMaxRect(&rDims, 3+(o->un2ix(LineDef.width)<<1));
-	o->SetLine(&LineDef);
-	switch(type & 0x0f) {
-	case 2:
-		pts[4].x = pts[3].x;	pts[4].y = pts[3].y;
-		pts[1].x = pts[2].x;	pts[1].y = pts[2].y;
-	case 3:
-		if((type & 0x0f) == 3){
-			pts[5].x = pts[3].x;	pts[5].y = pts[3].y;
-			pts[0].x = pts[2].x;	pts[0].y = pts[2].y;
-			}
-	case 0:
-		for(i = 0; i < 5; i+=2) o->oSolidLine(pts+i);
-		break;
-	case 1:
-		pts[4].x = pts[5].x = pts[3].x;		pts[4].y = pts[5].y = pts[3].y;
-		pts[1].x = pts[0].x = pts[2].x;		pts[1].y = pts[0].y = pts[2].y;
-		o->oSolidLine(pts+2);
-		break;
-		}
-}
-
-void
-Whisker::DoMark(anyOutput *o, bool mark)
-{
-	int i;
-	LineDEF OldLine;
-
-	if(mark){
-		if(mo) DelBitmapClass(mo);					mo = 0L;
-		memcpy(&mrc, &rDims, sizeof(RECT));
-		memcpy(&OldLine, &LineDef, sizeof(LineDEF));
-		i = 3*o->un2ix(LineDef.width);		//increase size of rectangle for marks
-		IncrementMinMaxRect(&mrc, i);		mo = GetRectBitmap(&mrc, o);
-		LineDef.width *= 5.0;				DoPlot(o);
-		LineDef.width = OldLine.width;		LineDef.color = OldLine.color ^ 0x00ffffffL;
-		DoPlot(o);							o->UpdateRect(&mrc, false);
-		memcpy(&LineDef, &OldLine, sizeof(LineDEF));
-		}
-	else RestoreRectBitmap(&mo, &mrc, o);
-}
-
-bool
-Whisker::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	int cb;
-
-	switch (cmd) {
-	case CMD_SCALE:
-		size *= ((scaleINFO*)tmpl)->sy.fy;
-		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;
-		LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-		return true;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
-				if(IsCloseToLine(&pts[2], &pts[3], mev->x, mev->y) ||
-					IsCloseToLine(&pts[0], &pts[1], mev->x, mev->y) ||
-					IsCloseToLine(&pts[4], &pts[5], mev->x, mev->y)) {
-						o->ShowMark(this, MRK_GODRAW);
-						return true;
-						}
-				}
-			break;
-			}
-		return false;
-	case CMD_ERRDESC:
-		if(tmpl && *((char*)tmpl)) {
-			cb = (int)strlen((char*)tmpl)+2;
-			if(name = (char*)realloc(name, cb)) rlp_strcpy(name, cb, (char*)tmpl);
-			return true;
-			}
-		return false;
-	case CMD_LEGEND:
-		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
-		switch(type & 0x0f) {
-		case 1:		((Legend*)tmpl)->HasErr(&LineDef, 5, name);		break;
-		case 2:		((Legend*)tmpl)->HasErr(&LineDef, 4, name);		break;
-		case 3:		((Legend*)tmpl)->HasErr(&LineDef, 3, name);		break;
-		default:
-			if((rDims.right - rDims.left) < (rDims.bottom - rDims.top))
-				((Legend*)tmpl)->HasErr(&LineDef, 1, name);
-			else ((Legend*)tmpl)->HasErr(&LineDef, 2, name);
-			break;
-			}
-		break;
-	case CMD_SET_DATAOBJ:
-		Id = GO_WHISKER;		data = (DataObj *)tmpl;
-		return true;
-	case CMD_UPDATE:
-		if(ssRef && cssRef >3 && data) {
-			data->GetValue(ssRef[0].y, ssRef[0].x, &pos1.fx);
-			data->GetValue(ssRef[1].y, ssRef[1].x, &pos1.fy);
-			data->GetValue(ssRef[2].y, ssRef[2].x, &pos2.fx);
-			data->GetValue(ssRef[3].y, ssRef[3].x, &pos2.fy);
-			return true;
-			}
-		return false;
-	case CMD_AUTOSCALE:
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			((Plot*)parent)->CheckBounds(pos1.fx, pos1.fy);
-			((Plot*)parent)->CheckBounds(pos2.fx, pos2.fy);
-			return true;
-			}
-		break;
-	case CMD_WHISKER_STYLE:
-		if(tmpl) type = *((int*)tmpl);
-		return true;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// drop line 
-DropLine::DropLine(GraphObj *par, DataObj *d, double x, double y, int which, int xc, 
-	int xr, int yc, int yr):GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	fPos.fx = x;
-	fPos.fy = y;
-	type = which;
-	Id = GO_DROPLINE;
-	if(xc >= 0 && xr >= 0 && yc >= 0 && yr >= 0) {
-		if(ssRef = (POINT*)malloc(sizeof(POINT)*2)) {
-			ssRef[0].x = xc;	ssRef[0].y = xr;
-			ssRef[1].x = yc;	ssRef[1].y = yr;
-			cssRef = 2;
-			}
-		}
-	bModified = false;
-}
-
-DropLine::DropLine(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	bModified = false;
-}
-
-DropLine::~DropLine()
-{
-	if(bModified) Undo.InvalidGO(this);
-	if(ssRef) free(ssRef);		ssRef = 0L;
-}
-
-void
-DropLine::DoPlot(anyOutput *o)
-{
-	int tmp;
-
-	o->RLP.fp = 0.0f;		//reset line pattern start
-	if(parent) {
-		pts[0].x = pts[1].x = pts[2].x = pts[3].x = o->fx2ix(fPos.fx);
-		pts[0].y = pts[1].y = pts[2].y = pts[3].y = o->fy2iy(fPos.fy);
-		if(type & DL_LEFT) {
-			tmp = o->fx2ix(parent->GetSize(SIZE_BOUNDS_LEFT));
-			if(tmp < pts[0].x) pts[0].x = tmp;
-			if(tmp > pts[1].x) pts[1].x = tmp;
-			}
-		if(type & DL_RIGHT) {
-			tmp = o->fx2ix(parent->GetSize(SIZE_BOUNDS_RIGHT));
-			if(tmp < pts[0].x) pts[0].x = tmp;
-			if(tmp > pts[1].x) pts[1].x = tmp;
-			}
-		if(type & DL_YAXIS) {
-			tmp = iround(parent->GetSize(SIZE_YAXISX));
-			if(tmp < pts[0].x) pts[0].x = tmp;
-			if(tmp > pts[1].x) pts[1].x = tmp;
-			}
-		if(type & DL_TOP) {
-			tmp = o->fy2iy(parent->GetSize(SIZE_BOUNDS_TOP));
-			if(tmp < pts[2].y) pts[2].y = tmp;
-			if(tmp > pts[3].y) pts[3].y = tmp;
-			}
-		if(type & DL_BOTTOM) {
-			tmp = o->fy2iy(parent->GetSize(SIZE_BOUNDS_BOTTOM));
-			if(tmp < pts[2].y) pts[2].y = tmp;
-			if(tmp > pts[3].y) pts[3].y = tmp;
-			}
-		if(type & DL_XAXIS) {
-			tmp = iround(parent->GetSize(SIZE_XAXISY));
-			if(tmp < pts[2].y) pts[2].y = tmp;
-			if(tmp > pts[3].y) pts[3].y = tmp;
-			}
-		SetMinMaxRect(&rDims, pts[0].x, pts[2].y, pts[1].x, pts[3].y);
-		IncrementMinMaxRect(&rDims, 3);
-		o->SetLine(&LineDef);
-		o->oPolyline(pts, 2);
-		o->oPolyline(pts+2, 2);
-		}
-}
-
-void
-DropLine::DoMark(anyOutput *o, bool mark)
-{
-
-	InvertLine(pts, 2, &LineDef, 0L, o, mark);
-	InvertLine(pts+2, 2, &LineDef, &rDims, o, mark);
-}
-
-bool
-DropLine::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
-				if(IsCloseToLine(&pts[0], &pts[1], mev->x, mev->y) ||
-					IsCloseToLine(&pts[2], &pts[3], mev->x, mev->y)) {
-					o->ShowMark(this, MRK_GODRAW);
-					return true;
-					}
-				}
-			break;
-			}
-		return false;
-	case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_DL_LINE:
-		if(tmpl) memcpy(&LineDef, tmpl, sizeof(LineDEF));
-		return true;
-	case CMD_DL_TYPE:
-		if(tmpl)type = *((int*)tmpl);
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = GO_DROPLINE;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_UPDATE:
-		if(ssRef && cssRef >1 && data) {
-			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
-			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
-			return true;
-			}
-		return false;
-	case CMD_AUTOSCALE:
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
-			return true;
-			}
-		break;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// define a spherical scanline used for clipping spheres
-sph_scanline::sph_scanline(POINT3D *center, int radius, bool bVert):GraphObj(0, 0)
-{
-	Id = GO_SPHSCANL;
-	rad = radius >= 0 ? radius : -radius;
-	memcpy(&p1, center, sizeof(POINT3D));	memcpy(&p2, center, sizeof(POINT3D));
-	memcpy(&cent, center, sizeof(POINT3D));
-	if(vert = bVert) {
-		p1.y -= rad;		p2.y += rad;
-		}
-	else {
-		p1.x -= rad;		p2.x += rad;
-		}
-	if(p1.x < 1) p1.x = 1;			if(p1.y < 1) p1.y = 1;
-	if(p2.x < p1.x) p2.x = p1.x;	if(p2.y < p1.y) p2.y = p1.y;
-	bValid1 = bValid2 = true;
-}
-
-bool
-sph_scanline::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch(cmd) {
-	case CMD_ADDTOLINE:
-		if(bValid1 && tmpl){
-			memcpy(&p2, tmpl, sizeof(POINT3D));
-			bValid2 = true;
-			return true;
-			}
-		break;
-	case CMD_STARTLINE:
-		if(!bValid1 && tmpl){
-			memcpy(&p1, tmpl, sizeof(POINT3D));
-			bValid1 = true;
-			}
-		break;
-		return true;
-		}
-	return false;
-}
-
-void
-sph_scanline::DoClip(GraphObj *co)
-{
-	POINT3D *pla;
-	int np, i;
-
-	if(!bValid1 || !bValid2) return;
-	switch(co->Id){
-	case GO_SPHERE:
-		bValid1 = bValid2 = false;
-		clip_sphline_sphere(this, &p1, &p2, &cent, rad, iround(co->GetSize(SIZE_RADIUS1)), 
-			iround(co->GetSize(SIZE_XPOS)), iround(co->GetSize(SIZE_YPOS)), 
-			iround(co->GetSize(SIZE_ZPOS)));
-		break;
-	case GO_PLANE:
-		for(i=0; ((plane*)co)->GetPolygon(&pla, &np, i); i++) {
-			bValid1 = bValid2 = false;
-			clip_sphline_plane(this, &p1, &p2, &cent, rad, pla, np, ((plane*)co)->GetVec());
-			}
-		break;
-		}
-}
-	
-bool
-sph_scanline::GetPoint(POINT *p, int sel)
-{
-	if(!bValid1 || !bValid2) {
-		p->x = p->y = 0;
-		return false;
-		}
-	switch(sel) {
-	case 1:
-		p->x = p1.x;	p->y = p1.y;
-		return true;
-	case 2:
-		p->x = p2.x;	p->y = p2.y;
-		return true;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Sphere: a symbol in three dimensional space
-Sphere::Sphere(GraphObj *par, DataObj *d, int sel, double x, double y, double z, 
-	double r, int xc, int xr, int yc, int yr, int zc, int zr, int rc, int rr)
-	:GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	Id = GO_SPHERE;
-	fPos.fx = x;	fPos.fy = y;	fPos.fz = z;	size = r;
-	if(xc >=0 || xr >=0 || yc >=0 || yr >=0 || zc >=0 || zr >=0 || rc >=0 || rr >=0) {
-		if(ssRef = (POINT*)malloc(sizeof(POINT)*4)) {
-			ssRef[0].x = xc;	ssRef[0].y = xr;
-			ssRef[1].x = yc;	ssRef[1].y = yr;
-			ssRef[2].x = zc;	ssRef[2].y = zr;
-			ssRef[3].x = rc;	ssRef[3].y = rr;
-			cssRef = 4;
-			}
-		}
-	type = sel;
-	bModified = false;
-}
-
-Sphere::Sphere(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	bModified = false;
-}
-
-Sphere::~Sphere()
-{
-	if(bModified) Undo.InvalidGO(this);
-	Command(CMD_FLUSH, 0L, 0L);
-}
-
-double
-Sphere::GetSize(int select)
-{
-	switch(select) {
-	case SIZE_MIN_X:		return fip.fx - (double)rx;
-	case SIZE_MAX_X:		return fip.fx + (double)rx;
-	case SIZE_MIN_Y:		return fip.fy - (double)ry;
-	case SIZE_MAX_Y:		return fip.fy + (double)ry;
-	case SIZE_MIN_Z:		return fip.fz - (double)rx;
-	case SIZE_MAX_Z:		return fip.fz + (double)rx;
-	case SIZE_XPOS:			return fip.fx;
-	case SIZE_YPOS:			return fip.fy;
-	case SIZE_ZPOS:			return fip.fz;
-	case SIZE_RADIUS1:	case SIZE_RADIUS2:	return (double)rx;
-	case SIZE_XCENT:		return fPos.fx;
-	case SIZE_YCENT:		return fPos.fy;
-	case SIZE_ZCENT:		return fPos.fz;
-	case SIZE_DRADIUS:		return size;
-	case SIZE_SYMBOL:
-		if(!type) return size;
-		else return DefSize(SIZE_SYMBOL);
-	case SIZE_SYM_LINE:		return Line.width;
-		}
-	return 0.0;
-}
-
-bool
-Sphere::SetSize(int select, double value)
-{
-	switch(select & 0xfff) {
-	case SIZE_SYMBOL:		size = value;			break;
-	case SIZE_SYM_LINE:		Line.width = value;		break;
-		}
-	return true;
-}
-
-DWORD
-Sphere::GetColor(int select)
-{
-	switch(select) {
-	case COL_SYM_LINE:		return Line.color;
-	case COL_SYM_FILL:		return Fill.color;
-	default: return defs.Color(select);
-		}
-}
-
-bool
-Sphere::SetColor(int select, DWORD col)
-{
-	switch(select & 0xfff) {
-	case COL_SYM_LINE:		Line.color = col;		break;
-	case COL_SYM_FILL:		Fill.color = col;		break;
-		}
-	return true;
-}
-
-void
-Sphere::DoPlot(anyOutput *o)
-{
-	int i;
-
-	if(size <= 0.001 || !o) return;
-	if(!o->fvec2ivec(&fPos, &fip)) return;
-	bDrawDone = false;
-	if(scl){
-		for(i = 0; i < nscl; i++) if(scl[i]) delete(scl[i]);
-		free(scl);
-		scl = 0L;	nscl = 0;
-		}
-	ix = iround(fip.fx);			iy = iround(fip.fy);
-	switch (type) {
-	case 1:
-		rx = ry = iround(size * 0.5 * o->ddx);		break;
-	case 2:
-		rx = ry = iround(size * 0.5 * o->ddy);		break;
-	case 3:
-		rx = ry = iround(size * 0.5 * o->ddz);		break;
-	default:
-		type = 0;
-	case 5:
-		rx = o->un2ix(size/2.0);	ry = o->un2iy(size/2.0);
-		break;
-		}
-	rDims.left = ix - rx;			rDims.right = ix + rx;
-	rDims.top = iy - ry;			rDims.bottom = iy + ry;
-	if(o->VPscale > 1.5 && (
-		(rDims.right < defs.clipRC.left) || (rDims.left > defs.clipRC.right) ||
-		(rDims.bottom < defs.clipRC.top) || (rDims.top > defs.clipRC.bottom))){
-		bDrawDone = true;		return;
-		}
-	if(parent && parent->Command(CMD_SET_GO3D, this, o)) return;
-	Command(CMD_REDRAW, 0L, o);
-}
-
-bool
-Sphere::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	int i;
-
-	switch (cmd) {
-	case CMD_FLUSH:
-		if(ssRef) free(ssRef);		ssRef = 0L;
-		if(name) free(name);		name = 0L;
-		if(scl){
-			for(i = 0; i < nscl; i++) if(scl[i]) delete(scl[i]);
-			free(scl);
-			scl = 0L;	nscl = 0;
-			}
-		return true;
-	case CMD_SCALE:
-		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
-		Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;
-		if(!type || type == 5)size *= ((scaleINFO*)tmpl)->sy.fy;;
-		return true;
-	case CMD_LEGEND:
-		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
-		((Legend*)tmpl)->HasFill(&Line, &Fill, 0L);
-		break;
-	case CMD_SYM_FILL:
-		if(tmpl) memcpy(&Fill, tmpl, sizeof(FillDEF));
-		return true;
-	case CMD_MRK_DIRTY:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_SET_DATAOBJ:
-		Id = GO_SPHERE;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_REDRAW:
-		//Note: this command is issued either by Undo (no output given) or
-		//  by Plot3D::DoPlot after sorting all objects (output specified)
-		if(!parent) return false;
-		if(!o) return parent->Command(cmd, tmpl, o);
-		if(bDrawDone) return false;
-		bDrawDone = true;
-		if(scl) DrawPG(o, 0);
-		else {
-			o->SetLine(&Line);				o->SetFill(&Fill);
-			if(Fill.type & FILL_LIGHT3D) o->oSphere(ix, iy, rx, 0L, 0, 0L);
-			else o->oCircle(ix-rx, iy-ry, ix+rx+1, iy+ry+1, name);
-			}
-		rx--;ry--;			//smaller marking rectangle
-		rDims.left = ix-rx-1;				rDims.right = ix+rx+1;
-		rDims.top = iy-ry-1;				rDims.bottom = iy+ry+1;
-		return true;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
-				o->ShowMark(&rDims, MRK_INVERT);
-				CurrGO = this;
-				return true;
-				}
-			break;
-			}
-		break;
-	case CMD_UPDATE:
-		if(ssRef && cssRef >1 && data) {
-			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
-			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
-			data->GetValue(ssRef[2].y, ssRef[2].x, &fPos.fz);
-			if(cssRef >3) data->GetValue(ssRef[3].y, ssRef[3].x, &size);
-			return true;
-			}
-		return false;
-	case CMD_CLIP:
-		if(co = (GraphObj*)tmpl){
-			switch(co->Id) {
-			case GO_PLANE:
-			case GO_SPHERE:
-				DoClip(co);
-				break;
-				}
-			}
-		return false;
-	case CMD_AUTOSCALE:
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			((Plot*)parent)->CheckBounds3D(fPos.fx, fPos.fy, fPos.fz);
-			return true;
-			}
-		break;
-		}
-	return false;
-}
-
-void
-Sphere::DoClip(GraphObj *co)
-{
-	RECT cliprc;
-	double d, d1;
-	POINT3D cscl;
-	int x, y, q, di, de, lim, cx, cy;
-	int i;
-
-	if(co && co->Id == GO_SPHERE) {
-		if(co->GetSize(SIZE_ZPOS) > fip.fz) return;
-		d1 = (d = co->GetSize(SIZE_XPOS) - fip.fx) * d;
-		d1 += (d = co->GetSize(SIZE_YPOS) - fip.fy) * d;
-		d1 = sqrt(d1 + (d = co->GetSize(SIZE_ZPOS) - fip.fz) * d);
-		if(d1 >= (co->GetSize(SIZE_RADIUS1) + rx))return;
-		}
-	else {
-		cliprc.left = iround(co->GetSize(SIZE_MIN_X));
-		cliprc.right = iround(co->GetSize(SIZE_MAX_X));
-		cliprc.top = iround(co->GetSize(SIZE_MIN_Y));
-		cliprc.bottom = iround(co->GetSize(SIZE_MAX_Y));
-		if(!OverlapRect(&rDims, &cliprc))return;
-		}
-	//use a list of horizontal scanlines created by a circular Bresenham's algorithm
-	//Ref: C. Montani, R. Scopigno (1990) "Speres-To-Voxel Conversion", in:
-	//   Graphic Gems (A.S. Glassner ed.) Academic Press, Inc.; 
-	//   ISBN 0-12-288165-5 
-	if(!scl && (scl = (sph_scanline**)calloc(rx*7+2, sizeof(sph_scanline*)))) {
-		cscl.z = iround(fip.fz);			nscl = 0;
-		for(q = 0; q < 2; q++) {
-			x = lim = 0;	y = rx;		di = 2*(1-rx);
-			while( y >= lim) {
-				if(di < 0) {
-					de = 2*di + 2*y -1;
-					if(de > 0) {
-						x++;	y--;	di += (2*x -2*y +2);
-						}
-					else {
-						x++;	di += (2*x +1);
-						}
-					}
-				else {
-					de = 2*di -2*x -1;
-					if(de > 0) {
-						y--;	di += (-2*y +1);
-						}
-					else {
-						x++;	y--;	di += (2*x -2*y +2);
-						}
-					}
-				switch(q) {
-				case 0:
-					cy = rx - y;			cx = x;
-					break;
-				case 1:
-					cy = rx + x;			cx = y; 
-					break;
-					}
-				cscl.y = iround(fip.fy);		cscl.x = iround(fip.fx);
-				cscl.y = cscl.y - rx + cy;
-				if(cx > 1) scl[nscl++] = new sph_scanline(&cscl, cx, false);
-				}
-			}
-		}
-	if(!scl) return;
-	//do clip for every scanline
-	for(i = 0; i < nscl; i++) if(scl[i]) scl[i]->DoClip(co);
-}
-
-void
-Sphere::DrawPG(anyOutput *o, int start)
-{
-	POINT *pts, np;
-	long cp = 0;
-	int i = start, step = 1;
-
-	if(!o || !scl ||!nscl) return;
-	if((pts = (POINT*)calloc(nscl*2, sizeof(POINT)))) {
-		do {
-			if(scl[i]) {
-				if(step > 0) scl[i]->GetPoint(&np, 1);
-				else scl[i]->GetPoint(&np, 2);
-				if(np.x && np.y){
-					AddToPolygon(&cp, pts, &np);
-					}
-				else if(cp){
-					if(step > 0) DrawPG(o, i);			//split sphere
-					step = -1;
-					}
-				}
-			if(i == nscl && step > 0) step = -1;
-			else i += step;
-			}while(i > start);
-		}
-	o->SetLine(&Line);				o->SetFill(&Fill);
-	if(cp) o->oSphere(ix, iy, rx, pts, cp, name);
-	free(pts);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// plane: utility object to draw a flat plane in 3D space
-plane::plane(GraphObj *par, DataObj *d, fPOINT3D *pts, int nPts, LineDEF *line, 
-	  FillDEF *fill):GraphObj(par, d)
-{
-	int i;
-	long area;
-	double vlength, v1[3], v2[3], vp[3], area1;
-	bool v_valid = false;
-	
-	nli = n_ipts = n_lines = 0;
-	nldata = 0L;  ldata = 0L;	co = 0L;	lines = 0L;		PlaneVec = 0L;
-	Id = GO_PLANE;	totalArea = 0;
-	memcpy(&Line, line, sizeof(LineDEF));
-	memcpy(&Fill, fill, sizeof(FillDEF));
-	rDims.left = rDims.right = rDims.top = rDims.bottom = 0;
-	if(nPts > 2 && (ldata =(POINT3D**)calloc(1, sizeof(POINT3D*))) &&
-		(ldata[0] = (POINT3D *)calloc(nPts+1, sizeof(POINT3D))) &&
-		(nldata = (int*)calloc(1, sizeof(int)))	&&
-		(ipts = (POINT*)calloc(nPts, sizeof(POINT)))){
-		for(i = 0; i < nPts; i++) {
-			ipts[i].x = ldata[0][i].x = iround(pts[i].fx);
-			ipts[i].y = ldata[0][i].y = iround(pts[i].fy);
-			ldata[0][i].z = iround(pts[i].fz);
-			}
-		nldata[0] = nPts;		nli = 1;	n_ipts = nPts;
-		xBounds.fx = xBounds.fy = pts[0].fx;	yBounds.fx = yBounds.fy = pts[0].fy;
-		zBounds.fx = zBounds.fy = pts[0].fz;
-		rDims.left = rDims.right = ipts[0].x;	rDims.top = rDims.bottom = ipts[0].y;
-		for(i = 1; i < nPts; i++){
-			UpdateMinMaxRect(&rDims, ipts[i].x, ipts[i].y);
-			if(pts[i].fx < xBounds.fx) xBounds.fx = pts[i].fx;
-			if(pts[i].fx > xBounds.fy) xBounds.fy = pts[i].fx;
-			if(pts[i].fy < yBounds.fx) yBounds.fx = pts[i].fy;
-			if(pts[i].fy > yBounds.fy) yBounds.fy = pts[i].fy;
-			if(pts[i].fz < zBounds.fx) zBounds.fx = pts[i].fz;
-			if(pts[i].fz > zBounds.fy) zBounds.fy = pts[i].fz;
-			}
-		//test if plane vertical
-		area1 = (xBounds.fx - xBounds.fy) * (yBounds.fx - yBounds.fy);
-		for(area = 0, i = 1; i < nPts; i++) {
-			area += (ipts[i].x - ipts[i-1].x) * ((ipts[i].y + ipts[i-1].y)>>1);
-			}
-		totalArea= area = abs(area);
-		area1 = area ? fabs(area1/area) : 101.0;
-		if(area < 100 && area1 > 100.0) {			//its small or vertical !
-			if(lines=(line_segment**)calloc(nPts, sizeof(line_segment*))){
-				for(i = 1; i < nPts; i++) {
-					lines[i-1] = new line_segment(par, d, line, &ldata[0][i-1], &ldata[0][i]);
-					}
-				n_lines = nPts-1;
-				}
-			}
-		else {					//for a visible plane get vector perpendicular to plane
-			for (i = 1; i < (nPts-1); i++) {
-				v1[0] = pts[i].fx - pts[i-1].fx;	v1[1] = pts[i].fy - pts[i-1].fy;
-				v1[2] = pts[i].fz - pts[i-1].fz;	v2[0] = pts[i+1].fx - pts[i].fx;
-				v2[1] = pts[i+1].fy - pts[i].fy;	v2[2] = pts[i+1].fz - pts[i].fz;
-				vp[0] = v1[1]*v2[2] - v1[2]*v2[1];	vp[1] = v1[2]*v2[0] - v1[0]*v2[2];
-				vp[2] = v1[0]*v2[1] - v1[1]*v2[0];
-				vlength = sqrt(vp[0]*vp[0]+vp[1]*vp[1]+vp[2]*vp[2]);
-				if(v_valid = (vlength > 100.0)) break;
-				}
-			if(v_valid && (PlaneVec = (double*)malloc(4 * sizeof(double)))) {
-				PlaneVec[0] = vp[0]/vlength;		PlaneVec[1] = vp[1]/vlength;
-				PlaneVec[2] = -vp[2]/vlength;
-				PlaneVec[3] = PlaneVec[0] * pts[i].fx + PlaneVec[1] * pts[i].fy - PlaneVec[2] * pts[i].fz;
-				}
-			}
-		}
-}
-
-plane::~plane() 
-{
-	int i;
-
-	if(ldata) {
-		for(i = 0; i < nli; i++) if(ldata[i]) free(ldata[i]);
-		free(ldata);	ldata = 0L;		nli = 0;
-		}
-	if(lines) {
-		for(i = 0; i < n_lines; i++) if(lines[i]) delete(lines[i]);
-		free(lines);	lines = 0L;		n_lines = 0;
-		}
-	if(nldata) free(nldata);		nldata = 0L;
-	if(ipts) free(ipts);			ipts = 0L;
-	if(PlaneVec) free(PlaneVec);	PlaneVec = 0L;
-}
-
-double
-plane::GetSize(int select)
-{
-	switch(select) {
-	case SIZE_MIN_X:		return xBounds.fx;
-	case SIZE_MAX_X:		return xBounds.fy;
-	case SIZE_MIN_Y:		return yBounds.fx;
-	case SIZE_MAX_Y:		return yBounds.fy;
-	case SIZE_MIN_Z:		return zBounds.fx;
-	case SIZE_MAX_Z:		return zBounds.fy;
-		}
-	return 0.0;
-}
-
-void
-plane::DoPlot(anyOutput *o)
-{
-	int i;
-
-	bDrawDone = bReqPoint = false;
-	if(Fill.type & FILL_LIGHT3D){
-		Fill.color = o->VecColor(PlaneVec, Fill.color2, Fill.color);
-		Fill.type &= ~FILL_LIGHT3D;
-		}
-	if(o->VPscale > 1.5 && (
-		//ignore objects outside the display ara
-		(rDims.right < defs.clipRC.left) || (rDims.left > defs.clipRC.right) ||
-		(rDims.bottom < defs.clipRC.top) || (rDims.top > defs.clipRC.bottom))){
-		bDrawDone = true;		return;
-		}
-	if(lines) {
-		if(Line.width == 0.0) return;
-		//draw line segments for vertical plane
-		for(i = 0; i < n_lines; i++) if(lines[i]) lines[i]->DoPlot(o);
-		bDrawDone = true;		return;
-		}
-	if(parent && parent->Command(CMD_SET_GO3D, this, o)) return;
-	Command(CMD_REDRAW, 0L, o);
-}
-
-void
-plane::DoMark(anyOutput *o, bool mark)
-{
-	FillDEF tmpfill;
-	LineDEF tmpline;
-
-	memcpy(&tmpfill, &Fill, sizeof(FillDEF));
-	memcpy(&tmpline, &Line, sizeof(LineDEF));
-	if(mark){
-		tmpfill.color ^= 0x00ffffffL;		tmpline.color ^= 0x00ffffffL;
-		}
-	o->SetLine(&tmpline);					o->SetFill(&tmpfill);
-	o->oPolygon(ipts, n_ipts);
-}
-
-bool 
-plane::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	POINT *pt;
-	POINT3D *ap;
-	int i, j;
-	
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_REDRAW:
-		if(bDrawDone) return false;
-		bDrawDone = true;
-		if(o && nldata){
-			if(Line.width == 0.0) Line.color = Fill.color;
-			o->SetLine(&Line);			o->SetFill(&Fill);
-			for(i = 0; i < nli; i++){
-				if(nldata[i] > 2 && (pt = (POINT*)malloc(nldata[i]*sizeof(POINT)))){
-					for(j = 0; j < nldata[i]; j++) {
-						pt[j].x = ldata[i][j].x;	pt[j].y = ldata[i][j].y;
-						}
-					o->oPolygon(pt, nldata[i]);
-					free(pt);
-					}
-				}
-			}
-		return true;
-	case CMD_STARTLINE:
-		if(ap = (POINT3D*)tmpl) {
-			if(ldata && nldata && nli) {
-				if(bReqPoint) {
-					Command(CMD_ADDTOLINE, &ReqPoint, o);
-					bReqPoint = false;
-					}
-				i = nli-1;			j = nldata[i]-1;
-				if(ldata[i][j].x == ap->x && ldata[i][j].y == ap->y && ldata[i][j].z == ap->z){
-					return true;
-					}
-				if(IsValidPG(ldata[i], nldata[i])) {
-					//close previous polygon first
-					if(ldata[i][0].x != ldata[i][j].x || ldata[i][0].y != ldata[i][j].y ||
-						ldata[i][0].z != ldata[i][j].z){
-						j++;
-						ldata[i][j].x = ldata[i][0].x;	ldata[i][j].y = ldata[i][0].y;
-						ldata[i][j].z = ldata[i][0].z;	nldata[i]++;
-						}
-					ldata = (POINT3D**)realloc(ldata, sizeof(POINT3D*) * (nli+1));
-					ldata[nli] = (POINT3D*)malloc(sizeof(POINT3D)*2);
-					nldata = (int*)realloc(nldata, sizeof(int) * (nli+1));
-					}
-				else {					//drop incomplete or invalid polygon
-					nli--;
-					}
-				}
-			else {
-				ldata = (POINT3D**)calloc(1, sizeof(POINT3D*));
-				ldata[nli = 0] = (POINT3D*)malloc(sizeof(POINT3D));
-				nldata = (int*)calloc(1, sizeof(int));
-				bReqPoint = false;
-				}
-			if(ldata && nldata) {
-				ldata[nli][0].x = ap->x;	ldata[nli][0].y = ap->y;
-				ldata[nli][0].z = ap->z;	nldata[nli++] = 1;
-				return true;
-				}
-			}
-		break;
-	case CMD_REQ_POINT:
-		if(ap = (POINT3D*)tmpl) {
-			ReqPoint.x = ap->x;		ReqPoint.y = ap->y;		ReqPoint.z = ap->z;
-			bReqPoint = true;
-			}
-		return true;
-	case CMD_ADDTOLINE:
-		if((ap = (POINT3D*)tmpl) && ldata && nldata && nli) {
-			i= nli-1;	j=nldata[i]-1;
-			if((ldata[i][j].x == ap->x && ldata[i][j].y == ap->y) ||
-				(ldata[i][j].y == ap->y && ldata[i][j].z == ap->z)){
-				//probably nothing to add
-				ldata[i][j].x = ap->x;			ldata[i][j].y = ap->y;
-				return j>0 ? true : false;
-				}
-			ldata[i] = (POINT3D*)realloc(ldata[i], ((j=nldata[i])+2) * sizeof(POINT3D));
-			ldata[i][j].x = ap->x;			ldata[i][j].y = ap->y;
-			ldata[i][j].z = ap->z;			nldata[i]++;
-			return true;
-			}
-	case CMD_CLIP:
-		if(co = (GraphObj*)tmpl){
-			switch(co->Id) {
-			case GO_PLANE:
-				//Clip only planes which are drawn later
-				if(GetSize(SIZE_MIN_Z) < co->GetSize(SIZE_MIN_Z)) return false;
-				if(nli){
-					DoClip(co);
-					if(nli && ldata) {
-						i = nli-1;			j = nldata[i]-1;
-						//is last part valid ?
-						if(j < 2) {
-							free(ldata[i]);		ldata[i] = 0L;
-							nldata[i] = 0;
-							nli--;
-							}
-						//close last polygon
-						else if(ldata[i][0].x != ldata[i][j].x ||
-							ldata[i][0].y != ldata[i][j].y ||
-							ldata[i][0].z != ldata[i][j].z){
-							j++;
-							ldata[i][j].x = ldata[i][0].x;
-							ldata[i][j].y = ldata[i][0].y;
-							ldata[i][j].z = ldata[i][0].z;
-							nldata[i]++;
-							}
-						}
-					}
-				else xBounds.fx=xBounds.fy=yBounds.fx=yBounds.fy=zBounds.fx=zBounds.fy=0.0;
-				break;
-				}
-			}
-		break;
-		}
-	return false;
-}
-
-void * 
-plane::ObjThere(int x, int y)
-{
-	POINT p1;
-
-	if(bDrawDone && IsInRect(&rDims, p1.x = x, p1.y = y) &&
-		(IsInPolygon(&p1, ipts, n_ipts) || IsCloseToPL(p1, ipts, n_ipts))) return this;
-	return 0L;
-}
-
-bool
-plane::GetPolygon(POINT3D **pla, int *npt, int n)
-{
-	if(n < nli && ldata && ldata[n]) {
-		*pla = ldata[n];	*npt = nldata[n];
-		return true;
-		}
-	return false;
-}
-
-void
-plane::DoClip(GraphObj *co)
-{
-	RECT cliprc;
-	int o_nli, *o_nldata = 0L;
-	POINT3D **o_ldata = 0L;
-	POINT3D *tpg;
-	int i, j, tnpt;
-	bool is_valid = false;
-
-	//if two planes have the same parent it means they are part of one object
-	// do not clip!
-	if(co->parent == parent && co->Id == GO_PLANE) return;
-	if(co->Id == GO_PLANE && (parent->parent->Id == GO_GRID3D || parent->parent->Id == GO_RIBBON)
-		&& co->parent->parent == parent->parent) return;
-	cliprc.left = iround(co->GetSize(SIZE_MIN_X));
-	cliprc.right = iround(co->GetSize(SIZE_MAX_X));
-	cliprc.top = iround(co->GetSize(SIZE_MIN_Y));
-	cliprc.bottom = iround(co->GetSize(SIZE_MAX_Y));
-	if(OverlapRect(&rDims, &cliprc) && co != this) {
-		o_nli = nli;		nli = 0;
-		o_nldata = nldata;	nldata = 0L;
-		o_ldata = ldata;	ldata = 0L;
-		switch(co->Id) {
-		case GO_PLANE:
-			//clip all parts of this plane with all from another plane
-			for(i = 0; ((plane*)co)->GetPolygon(&tpg, &tnpt, i); i++){
-				for(j = 0; j < o_nli; j++) {
-					if(o_nldata[j] >2) clip_plane_plane(this, o_ldata[j], o_nldata[j], PlaneVec, 
-						tpg, tnpt, ((plane*)co)->GetVec(), ipts, n_ipts );
-					}
-				if(bReqPoint){
-					Command(CMD_ADDTOLINE, &ReqPoint, 0L);
-					bReqPoint = false;
-					}
-				if(nli) is_valid = true;
-				if(o_ldata) {
-					for(j = 0; j < o_nli; j++) if(o_ldata[j]) free(o_ldata[j]);
-					free(o_ldata);
-					}
-				if(o_nldata) free(o_nldata);
-				o_nli = nli;		nli = 0;
-				o_nldata = nldata;	nldata = 0L;
-				o_ldata = ldata;	ldata = 0L;
-				if(!o_nli) {					//plane is completly hidden
-					return;
-					}
-				}
-			if(is_valid || i==0){
-				nli = o_nli;		o_nli = 0;
-				nldata = o_nldata;	o_nldata = 0L;
-				ldata = o_ldata;	o_ldata = 0L;
-				}
-			if(nli > 1) for(i = 1; i < nli; i++) {
-				if(nldata[i] > 3 && ldata[i][nldata[i]-1].x == ldata[i-1][0].x
-					&& ldata[i][nldata[i]-1].y == ldata[i-1][0].y) {
-					nldata[i]--;	//bad vis: ignore last point
-					}
-				}
-			break;
-		default:
-			nli = o_nli;		o_nli = 0;
-			nldata = o_nldata;	o_nldata = 0L;
-			ldata = o_ldata;	o_ldata = 0L;
-			break;
-			}
-		//check shape and recalc some values
-		if(is_valid && nli) {
-			xBounds.fx = xBounds.fy = ldata[0][0].x;	yBounds.fx = yBounds.fy = ldata[0][0].y;
-			zBounds.fx = zBounds.fy = ldata[0][0].z;
-			rDims.left = rDims.right = ldata[0][0].x;	rDims.top = rDims.bottom = ldata[0][0].y;
-			for(i = 0; i < nli; i++) if(nldata[i] > 2){
-				if(ldata[i][0].x != ldata[i][nldata[i]-1].x || ldata[i][0].y != ldata[i][nldata[i]-1].y
-					|| ldata[i][0].z != ldata[i][nldata[i]-1].z) {
-					ldata[i][nldata[i]].x = ldata[i][0].x;	ldata[i][nldata[i]].y = ldata[i][0].y;
-					ldata[i][nldata[i]].z = ldata[i][0].z;	nldata[i]++;
-					}
-				for(j = 0; j < (nldata[i]-1); j++) {
-					UpdateMinMaxRect(&rDims, ldata[i][j].x, ldata[i][j].y);
-					if(ldata[i][j].x < xBounds.fx) xBounds.fx = ldata[i][j].x;
-					if(ldata[i][j].x > xBounds.fy) xBounds.fy = ldata[i][j].x;
-					if(ldata[i][j].y < yBounds.fx) yBounds.fx = ldata[i][j].y;
-					if(ldata[i][j].y > yBounds.fy) yBounds.fy = ldata[i][j].y;
-					if(ldata[i][j].z < zBounds.fx) zBounds.fx = ldata[i][j].z;
-					if(ldata[i][j].z > zBounds.fy) zBounds.fy = ldata[i][j].z;
-					}
-				}
-			}
-		}
-	if(o_ldata) {
-		for(i = 0; i < o_nli; i++) if(o_ldata[i]) free(o_ldata[i]);
-		free(o_ldata);
-		}
-	if(o_nldata) free(o_nldata);
-}
-
-bool
-plane::IsValidPG(POINT3D *pg, int npg)
-{
-	int i;
-	long area;
-
-	//a polygon must have more than 3 Points
-	if(npg < 3) return false;
-	//check for a reasonable size
-	for(area = 0, i = 1; i < npg; i++) {
-		area += (pg[i].x - pg[i-1].x) * ((pg[i].y + pg[i-1].y)>>1);
-		}
-	area += (pg[0].x - pg[i-1].x) * ((pg[0].y + pg[i-1].y)>>1);
-	area = abs(area);
-	if(area < 20) return false;
-	if(totalArea/area > 100) return false;
-	return true;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// a simple plane in three dimensional space
-Plane3D::Plane3D(GraphObj *par, DataObj *da, fPOINT3D *pt, long npt)
-	:GraphObj(par, da) 
-{
-	FileIO(INIT_VARS);
-	Id = GO_PLANE3D;
-	dt = (fPOINT3D*) memdup(pt, sizeof(fPOINT3D)*npt, 0L);
-	ndt = npt;
-}
-
-Plane3D::Plane3D(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-Plane3D::~Plane3D()
-{
-	if(dt) free(dt);	if(pts) free(pts);
-	dt = 0L;		ndt = 0L;
-	if(ipl) delete (ipl);	ipl = 0L;
-}
-
-bool
-Plane3D::SetSize(int select, double value)
-{
-	int i;
-
-	if((select & 0xfff) >= SIZE_XPOS && (select & 0xfff) <=  SIZE_XPOS_LAST){
-		if((i = select-SIZE_XPOS) >=0 && i <= 200 && i < (int)ndt){
-			dt[i].fx = value;			return true;
-			}
-		}
-	else if((select & 0xfff) >= SIZE_YPOS && (select & 0xfff) <= SIZE_YPOS_LAST){
-		if((i = select-SIZE_YPOS) >=0 && i <= 200 && i < (int)ndt){
-			dt[i].fy = value;			return true;
-			}
-		}
-	else if((select & 0xfff) >= SIZE_ZPOS && (select & 0xfff) <= SIZE_ZPOS_LAST){
-		if((i = select-SIZE_ZPOS) >=0 && i <= 200 && i < (int)ndt){
-			dt[i].fz = value;			return true;
-			}
-		}
-	else switch(select) {
-	case SIZE_SYM_LINE:
-		Line.width = value;
-		}
-	return false;
-}
-
-bool
-Plane3D::SetColor(int select, DWORD col)
-{
-	switch(select) {
-	case COL_POLYLINE:		Line.color = col;		return true;
-	case COL_POLYGON:		Fill.color = col;		return true;
-		}
-	return false;
-}
-
-void
-Plane3D::DoPlot(anyOutput *o)
-{
-	int i;
-
-	if(ipl) delete ipl;		ipl = 0L;
-	if(pts) free(pts);		pts = 0L;
-	if(!(o->ActualSize(&rDims)))return;
-	rDims.left = rDims.right;	rDims.top = rDims.bottom;
-	rDims.right = rDims.bottom = 0;
-	if((pts = (fPOINT3D*)malloc(sizeof(fPOINT3D)*ndt))){
-		for(i = 0; i < ndt; i++) {
-			if(!o->fvec2ivec(&dt[i], &pts[i])){
-				free(pts);	pts = 0L; return;
-				}
-			UpdateMinMaxRect(&rDims, iround(pts[i].fx), iround(pts[i].fy));
-			}
-		if(ipl = new plane(this, data, pts, i, &Line, &Fill))ipl->DoPlot(o);
-		}
-	IncrementMinMaxRect(&rDims, o->un2ix(Line.width)+1);
-}
-
-void
-Plane3D::DoMark(anyOutput *o, bool mark)
-{
-	if(mark){
-		if(pts && ipl)ipl->DoMark(o, mark);
-		o->UpdateRect(&rDims, false);
-		}
-	else if(parent) parent->Command(CMD_REDRAW, 0L, o);
-}
-
-bool
-Plane3D::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-	MouseEvent *mev;
-
-	switch (cmd) {
-	case CMD_SET_DATAOBJ:
-		Id = GO_PLANE3D;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_SCALE:
-		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
-		Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;
-		return true;
-	case CMD_LEGEND:
-		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
-		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH) {
-			((Legend*)tmpl)->HasFill(&Line, &Fill, ((Plot*)parent)->data_desc);
-			}
-		else ((Legend*)tmpl)->HasFill(&Line, &Fill, 0L);
-		break;
-	case CMD_MRK_DIRTY:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_SYM_FILL:
-		if(tmpl) memcpy(&Fill, tmpl, sizeof(FillDEF));
-		return true;
-	case CMD_REDRAW:
-		//Note: this command is issued either by Undo (no output given) or
-		//  by Plot3D::DoPlot after sorting all objects (output specified)
-		if(!parent) return false;
-		if(!o) return parent->Command(cmd, tmpl, o);
-		return true;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
-				if(ipl && ipl->ObjThere(mev->x, mev->y)){
-					o->ShowMark(CurrGO=this, MRK_GODRAW);
-					return true;
-					}
-				}
-			break;
-			}
-		break;
-	case CMD_PG_FILL:
-		if(tmpl) {
-			memcpy((void*)&Fill, tmpl, sizeof(FillDEF));
-			Fill.hatch = 0L;
-			}
-		break;
-	case CMD_AUTOSCALE:
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH && dt) {
-			for(i = 0; i < ndt; i++)
-				((Plot*)parent)->CheckBounds3D(dt[i].fx, dt[i].fy, dt[i].fz);
-			return true;
-			}
-		break;
-	case CMD_SET_GO3D:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Brick: a bar in three dimensional space
-Brick::Brick(GraphObj *par, DataObj *da, double x, double y, double z, 
-		double d, double w, double h, DWORD flg, int xc, int xr, int yc,
-		int yr, int zc, int zr, int dc, int dr, int wc, int wr, int hc,
-		int hr):GraphObj(par, da)
-{
-	FileIO(INIT_VARS);
-	Id = GO_BRICK;
-	fPos.fx = x;	fPos.fy = y;	fPos.fz = z;
-	depth = d;		width = w;		height = h;
-	flags = flg;
-	if(xc >= 0 || xr >= 0 || yc >= 0 || yr >= 0 || zc >= 0 || zr >= 0 ||
-		dc >= 0 || dr >= 0 || wc >= 0 || wr >= 0 || hc >= 0 || hr >= 0) {
-		if(ssRef = (POINT*)malloc(sizeof(POINT)*6)) {
-			ssRef[0].x = xc;	ssRef[0].y = xr;
-			ssRef[1].x = yc;	ssRef[1].y = yr;
-			ssRef[2].x = zc;	ssRef[2].y = zr;
-			ssRef[3].x = dc;	ssRef[3].y = dr;
-			ssRef[4].x = wc;	ssRef[4].y = wr;
-			ssRef[5].x = hc;	ssRef[5].y = hr;
-			cssRef = 6;
-			}
-		}
-	bModified = false;
-}
-
-Brick::Brick(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	bModified = false;
-}
-	
-Brick::~Brick()
-{
-	int i;
-
-	if(faces) {
-		for(i = 0; i < 6; i++) if(faces[i]) delete(faces[i]);
-		free(faces);
-		}
-	faces = 0L;
-	if(mo) DelBitmapClass(mo);		mo = 0L;
-	Command(CMD_FLUSH, 0L, 0L);
-	if(bModified) Undo.InvalidGO(this);
-}
-
-bool
-Brick::SetSize(int select, double value)
-{
-	switch(select & 0xfff) {
-	case SIZE_BAR_LINE:		Line.width = value;		return true;
-	case SIZE_BAR_BASE:		fPos.fy = value;		return true;
-	case SIZE_BAR:			width = value;			return true;
-	case SIZE_BAR_DEPTH:	depth = value;			return true;
-		}
-	return false;
-}
-
-bool
-Brick::SetColor(int select, DWORD col)
-{
-	switch(select & 0xfff) {
-	case COL_BAR_LINE:		Line.color = col;		return true;
-	case COL_BAR_FILL:		Fill.color = col;		return true;
-		}
-	return false;
-}
-
-void
-Brick::DoPlot(anyOutput *o)
-{
-	fPOINT3D cpt[8], fip1, fip2, tmp, itmp, *pg;
-	plane *npl;
-	double dtmp;
-	int i;
-
-	if(mo) DelBitmapClass(mo);		mo = 0L;
-	if(!faces || !o || !o->fvec2ivec(&fPos, &fip1)) return;
-	if(!(pg = (fPOINT3D *)malloc(5*sizeof(fPOINT3D)))) return;
-	for(i = 0; i < 6; i++) {
-		if(faces[i]) delete(faces[i]);
-		faces[i] = 0L;
-		}
-	if(flags & 0x800L) {		//height is data
-		tmp.fx = fPos.fx;			tmp.fy = height;
-		tmp.fz = fPos.fz;			o->fvec2ivec(&tmp, &fip2);
-		}
-	else {						//height is units
-		tmp.fx = tmp.fz = 0.0;		tmp.fy = height;
-		o->uvec2ivec(&tmp, &fip2);
-		fip2.fx += fip1.fx;			fip2.fy += fip1.fy;
-		fip2.fz += fip1.fz;
-		}
-	//calc output-device coordinates of cubic brick: 8 corners
-	tmp.fx = -(width/2.0);			tmp.fz = (depth/2.0);	tmp.fy = 0.0;
-	o->uvec2ivec(&tmp, &itmp);
-	cpt[0].fx= fip1.fx+itmp.fx;	cpt[0].fy= fip1.fy+itmp.fy;	cpt[0].fz= fip1.fz+itmp.fz;
-	cpt[2].fx= fip1.fx-itmp.fx;	cpt[2].fy= fip1.fy-itmp.fy;	cpt[2].fz= fip1.fz-itmp.fz;
-	cpt[4].fx= fip2.fx+itmp.fx;	cpt[4].fy= fip2.fy+itmp.fy;	cpt[4].fz= fip2.fz+itmp.fz;
-	cpt[6].fx= fip2.fx-itmp.fx;	cpt[6].fy= fip2.fy-itmp.fy;	cpt[6].fz= fip2.fz-itmp.fz;
-	tmp.fx = (width/2.0);			tmp.fz = (depth/2.0);	tmp.fy = 0.0;
-	o->uvec2ivec(&tmp, &itmp);
-	cpt[1].fx= fip1.fx+itmp.fx;	cpt[1].fy= fip1.fy+itmp.fy;	cpt[1].fz= fip1.fz+itmp.fz;
-	cpt[3].fx= fip1.fx-itmp.fx;	cpt[3].fy= fip1.fy-itmp.fy;	cpt[3].fz= fip1.fz-itmp.fz;
-	cpt[5].fx= fip2.fx+itmp.fx;	cpt[5].fy= fip2.fy+itmp.fy;	cpt[5].fz= fip2.fz+itmp.fz;
-	cpt[7].fx= fip2.fx-itmp.fx;	cpt[7].fy= fip2.fy-itmp.fy;	cpt[7].fz= fip2.fz-itmp.fz;
-	//set up 6 faces
-	pg[0].fx = pg[4].fx = cpt[0].fx;	pg[1].fx = cpt[1].fx;	
-	pg[2].fx = cpt[2].fx;				pg[3].fx = cpt[3].fx;
-	pg[0].fy = pg[4].fy = cpt[0].fy;	pg[1].fy = cpt[1].fy;	
-	pg[2].fy = cpt[2].fy;				pg[3].fy = cpt[3].fy;
-	pg[0].fz = pg[4].fz = cpt[0].fz;	pg[1].fz = cpt[1].fz;	
-	pg[2].fz = cpt[2].fz;				pg[3].fz = cpt[3].fz;
-	faces[0] = new plane(this, data, pg, 5, &Line, &Fill);
-	pg[2].fx = cpt[5].fx;				pg[3].fx = cpt[4].fx;
-	pg[2].fy = cpt[5].fy;				pg[3].fy = cpt[4].fy;
-	pg[2].fz = cpt[5].fz;				pg[3].fz = cpt[4].fz;
-	npl = new plane(this, data, pg, 5, &Line, &Fill);
-	if(npl->GetSize(SIZE_MAX_Z) > faces[0]->GetSize(SIZE_MAX_Z)) faces[1] = npl;
-	else {
-		faces[1] = faces[0];		faces[0] = npl;		
-		}
-	pg[0].fx = pg[4].fx = cpt[2].fx;	pg[1].fx = cpt[6].fx;	
-	pg[2].fx = cpt[5].fx;				pg[3].fx = cpt[1].fx;
-	pg[0].fy = pg[4].fy = cpt[2].fy;	pg[1].fy = cpt[6].fy;	
-	pg[2].fy = cpt[5].fy;				pg[3].fy = cpt[1].fy;
-	pg[0].fz = pg[4].fz = cpt[2].fz;	pg[1].fz = cpt[6].fz;	
-	pg[2].fz = cpt[5].fz;				pg[3].fz = cpt[1].fz;
-	npl = new plane(this, data, pg, 5, &Line, &Fill);
-	if((dtmp = npl->GetSize(SIZE_MAX_Z)) > faces[1]->GetSize(SIZE_MAX_Z)) faces[2] = npl;
-	else {
-		faces[2] = faces[1];
-		if(dtmp > faces[0]->GetSize(SIZE_MAX_Z)) faces[1] = npl;
-		else {
-			faces[1] = faces[0];	faces[0] = npl;
-			}
-		}
-	pg[2].fx = cpt[7].fx;				pg[3].fx = cpt[3].fx;
-	pg[2].fy = cpt[7].fy;				pg[3].fy = cpt[3].fy;
-	pg[2].fz = cpt[7].fz;				pg[3].fz = cpt[3].fz;
-	npl = new plane(this, data, pg, 5, &Line, &Fill);
-	dtmp = npl->GetSize(SIZE_MAX_Z);
-	for (i = 3; i; i--) {
-		if(dtmp >faces[i-1]->GetSize(SIZE_MAX_Z)) {
-			faces[i] = npl;			break;
-			}
-		else faces[i] = faces[i-1];
-		}
-	if(!i) faces[0] = npl;
-	pg[0].fx = pg[4].fx = cpt[4].fx;	pg[1].fx = cpt[7].fx;	
-	pg[2].fx = cpt[3].fx;				pg[3].fx = cpt[0].fx;
-	pg[0].fy = pg[4].fy = cpt[4].fy;	pg[1].fy = cpt[7].fy;	
-	pg[2].fy = cpt[3].fy;				pg[3].fy = cpt[0].fy;
-	pg[0].fz = pg[4].fz = cpt[4].fz;	pg[1].fz = cpt[7].fz;	
-	pg[2].fz = cpt[3].fz;				pg[3].fz = cpt[0].fz;
-	npl = new plane(this, data, pg, 5, &Line, &Fill);
-	dtmp = npl->GetSize(SIZE_MAX_Z);
-	for (i = 4; i; i--) {
-		if(dtmp >faces[i-1]->GetSize(SIZE_MAX_Z)) {
-			faces[i] = npl;			break;
-			}
-		else faces[i] = faces[i-1];
-		}
-	if(!i) faces[0] = npl;
-	pg[2].fx = cpt[6].fx;				pg[3].fx = cpt[5].fx;
-	pg[2].fy = cpt[6].fy;				pg[3].fy = cpt[5].fy;
-	pg[2].fz = cpt[6].fz;				pg[3].fz = cpt[5].fz;
-	npl = new plane(this, data, pg, 5, &Line, &Fill);
-	dtmp = npl->GetSize(SIZE_MAX_Z);
-	for (i = 5; i; i--) {
-		if(dtmp >faces[i-1]->GetSize(SIZE_MAX_Z)) {
-			faces[i] = npl;			break;
-			}
-		else faces[i] = faces[i-1];
-		}
-	if(!i) faces[0] = npl;
-	rDims.left = rDims.right = (int)pg[0].fx;
-	rDims.top = rDims.bottom = (int)pg[0].fy;
-	for (i= 3; i < 6; i++) if(faces[i]) {
-		faces[i]->DoPlot(o);
-		UpdateMinMaxRect(&rDims, faces[i]->rDims.left, faces[i]->rDims.top);
-		UpdateMinMaxRect(&rDims, faces[i]->rDims.right, faces[i]->rDims.bottom);
-		}
-	free(pg);
-}
-
-void
-Brick::DoMark(anyOutput *o, bool mark)
-{
-	int i;
-
-	if(mark){
-		if(mo) DelBitmapClass(mo);					mo = 0L;
-		memcpy(&mrc, &rDims, sizeof(RECT));
-		IncrementMinMaxRect(&mrc, 6 + o->un2ix(Line.width));
-		mo = GetRectBitmap(&mrc, o);
-		if(faces) for(i = 3; i < 6; i++)
-			if(faces[i]) faces[i]->DoMark(o, mark);
-		o->UpdateRect(&rDims, false);
-		}
-	else if(mo)	RestoreRectBitmap(&mo, &mrc, o);
-}
-
-bool
-Brick::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-
-	switch (cmd) {
-	case CMD_FLUSH:
-		if(ssRef) free(ssRef);		ssRef = 0L;
-		if(name) free(name);		name = 0L;
-		return true;
-	case CMD_SCALE:
-		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
-		Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;			depth *= ((scaleINFO*)tmpl)->sz.fy;
-		width *= ((scaleINFO*)tmpl)->sx.fy;		if(!(flags & 0x800L)) height *=  ((scaleINFO*)tmpl)->sx.fy;
-		return true;
-	case CMD_LEGEND:
-		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
-		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH) {
-			((Legend*)tmpl)->HasFill(&Line, &Fill, ((Plot*)parent)->data_desc);
-			}
-		else ((Legend*)tmpl)->HasFill(&Line, &Fill, 0L);
-		break;
-	case CMD_BAR_FILL:
-		if(tmpl) {
-			memcpy(&Fill, tmpl, sizeof(FillDEF));
-			Fill.hatch = 0L;
-			return true;
-			}
-		break;
-	case CMD_MRK_DIRTY:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_SET_DATAOBJ:
-		Id = GO_BRICK;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_REDRAW:
-		//Note: this command is issued either by Undo (no output given) or
-		//  by Plot3D::DoPlot after sorting all objects (output specified)
-		if(!parent) return false;
-		if(!o) return parent->Command(cmd, tmpl, o);
-		//Should we ever come here ?
-		return true;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
-				if(faces && faces[3] && faces[4] && faces[5] &&
-					(faces[3]->ObjThere(mev->x, mev->y) || 
-					faces[4]->ObjThere(mev->x, mev->y) ||
-					faces[5]->ObjThere(mev->x, mev->y))){
-						o->ShowMark(CurrGO=this, MRK_GODRAW);
-						return true;
-						}
-				}
-			break;
-			}
-		break;
-	case CMD_UPDATE:
-		if(ssRef && cssRef > 5 && data) {
-			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
-			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
-			data->GetValue(ssRef[2].y, ssRef[2].x, &fPos.fz);
-			data->GetValue(ssRef[3].y, ssRef[3].x, &depth);
-			data->GetValue(ssRef[4].y, ssRef[4].x, &width);
-			data->GetValue(ssRef[5].y, ssRef[5].x, &height);
-			return true;
-			}
-		return false;
-	case CMD_AUTOSCALE:
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			((Plot*)parent)->CheckBounds3D(fPos.fx, fPos.fy, fPos.fz);
-			if(flags & 0x800L) {		//height is data
-				((Plot*)parent)->CheckBounds3D(fPos.fx, height, fPos.fz);
-				}
-			return true;
-			}
-		break;
-	case CMD_SET_GO3D:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// line_segment: utility object to draw a piece of a polyline in 3D space
-line_segment::line_segment(GraphObj *par, DataObj *d, LineDEF *ld, POINT3D *p1, POINT3D *p2)
-	:GraphObj(par, d)
-{
-	double tmp, tmp1, tmp2;
-
-	nli = 0;    nldata = 0L;	  ldata = 0L;	prop = 1.0;		df_go = 0L;
-	fmin.fx = fmax.fx = fmin.fy = fmax.fy = fmin.fz = fmax.fz = 0.0;
-	ndf_go = 0;
-	if(ld) memcpy(&Line, ld, sizeof(LineDEF));
-	if(p1 && p2 &&(ldata =(POINT3D**)calloc(1, sizeof(POINT3D*))) &&
-		(ldata[0] = (POINT3D*)malloc(2 * sizeof(POINT3D))) &&
-		(nldata = (int*)calloc(1, sizeof(int)))){
-			if(Line.pattern) {
-				tmp1 = (tmp = (p2->x - p1->x)) * tmp;
-				tmp2 = (tmp1 += (tmp = (p2->y - p1->y)) * tmp);
-				tmp1 += (tmp = (p2->z - p1->z)) * tmp;
-				if(tmp1 > 1.0) prop = sqrt(tmp2)/sqrt(tmp1);
-				}
-			memcpy(&ldata[0][0], p1, sizeof(POINT3D));
-			memcpy(&ldata[0][1], p2, sizeof(POINT3D));
-			nldata[0] = 2;		nli = 1;
-			rDims.left = rDims.right = p1->x;	rDims.top = rDims.bottom = p1->y;
-			UpdateMinMaxRect(&rDims, p2->x, p2->y);
-			fmin.fx = (double)rDims.left;	fmin.fy = (double)rDims.top;
-			fmax.fx = (double)rDims.right;	fmax.fy = (double)rDims.bottom;
-			if(p2->z > p1->z) {
-				fmin.fz = (double)p1->z;	fmax.fz = (double)p2->z;
-				}
-			else {
-				fmin.fz = (double)p2->z;	fmax.fz = (double)p1->z;
-				}
-		}
-	Id = GO_LINESEG;
-}
-
-line_segment::~line_segment()
-{
-	int i;
-
-	if(ldata) {
-		for(i = 0; i < nli; i++) if(ldata[i]) free(ldata[i]);
-		free(ldata);
-		}
-	if(nldata) free(nldata);		nldata = 0L;
-}
-
-double
-line_segment::GetSize(int select)
-{
-	switch(select) {
-	case SIZE_MIN_Z:
-		return fmin.fz;
-	case SIZE_MAX_Z:
-		return fmax.fz;
-		}
-	return 0.0;
-}
-
-void
-line_segment::DoPlot(anyOutput *o)
-{
-	bDrawDone = false;		co = 0L;
-	if(df_go) free(df_go);
-	df_go = 0L;				ndf_go = 0;
-	if(o->VPscale > 1.5 && (
-		(rDims.right < defs.clipRC.left) || (rDims.left > defs.clipRC.right) ||
-		(rDims.bottom < defs.clipRC.top) || (rDims.top > defs.clipRC.bottom))){
-		bDrawDone = true;		return;
-		}
-	if(parent && parent->Command(CMD_SET_GO3D, this, o)) return;
-	Command(CMD_REDRAW, 0L, o);
-}
-
-bool
-line_segment::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i, j;
-	POINT pts[2];
-	LineDEF cLine;
-	POINT3D *ap;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_DRAW_LATER:
-		if(!co) return false;
-		if(df_go){
-			for(i = 0; i < ndf_go; i++) if(df_go[i] == co) return true;
-			if(df_go = (GraphObj**)realloc(df_go, (ndf_go +1) * sizeof(GraphObj*))) {
-				df_go[ndf_go++] = co;
-				}
-			}
-		else {
-			if(df_go = (GraphObj**)malloc(sizeof(GraphObj*))){
-				df_go[0] = co;	ndf_go = 1;
-				}
-			}
-		return true;
-	case CMD_REDRAW:
-		if(bDrawDone) return false;
-		bDrawDone = true;
-		if(!nli) return false;
-		if(df_go) {
-			for(i = 0; i < ndf_go; i++) if(df_go[i]) df_go[i]->Command(cmd, tmpl, o);
-			free(df_go);	df_go = 0L;			ndf_go = 0L;
-			}
-		if(o && ldata && nldata){
-			memcpy(&cLine, &Line, sizeof(LineDEF));
-			cLine.patlength *= prop;
-			o->SetLine(&cLine);
-			for(i = 0; i < nli; i++) for(j = 0; j < (nldata[i]-1); j++) {
-				pts[0].x = ldata[i][j].x;	pts[0].y = ldata[i][j].y;
-				pts[1].x = ldata[i][j+1].x;	pts[1].y = ldata[i][j+1].y;
-				if(pts[0].x != pts[1].x || pts[0].y != pts[1].y) o->oPolyline(pts, 2);
-				}
-			}
-		return true;
-	case CMD_CLIP:
-		if(co = (GraphObj*)tmpl){
-			switch(co->Id) {
-			case GO_PLANE:
-			case GO_SPHERE:
-				DoClip(co);
-				break;
-				}
-			}
-		return false;
-	case CMD_STARTLINE:
-		if(tmpl) {
-			if(ldata && nldata) {
-				ldata = (POINT3D**)realloc(ldata, sizeof(POINT3D*) * (nli+1));
-				ldata[nli] = (POINT3D*)malloc(2 * sizeof(POINT3D));
-				nldata = (int*)realloc(nldata, sizeof(int)*(nli+1));
-				}
-			else {
-				ldata = (POINT3D**)calloc(1, sizeof(POINT3D*));
-				ldata[nli = 0] = (POINT3D*)malloc(2 * sizeof(POINT3D));
-				nldata = (int*)calloc(1, sizeof(int));
-				}
-			if(ldata && nldata) {
-				memcpy(&ldata[nli][0], tmpl, sizeof(POINT3D));
-				memcpy(&ldata[nli][1], tmpl, sizeof(POINT3D));
-				nldata[nli++] = 1;
-				return true;
-				}
-			}
-		break;
-	case CMD_ADDTOLINE:
-		if((ap = (POINT3D*)tmpl) && ldata && nldata && nli && nldata[i =(nli-1)]) {
-			j = nldata[i];
-			ldata[i] = (POINT3D*)realloc(ldata[i], (nldata[i]+2) * sizeof(POINT3D));
-			if(j && ldata[i][j-1].x == ap->x && ldata[i][j-1].y == ap->y &&
-				ldata[i][j-1].z == ap->z) return true;
-			else {
-				j = (nldata[i]++);
-				}
-			memcpy(&ldata[i][j-1], tmpl, sizeof(POINT3D));
-			return true;
-			}
-		break;
-		}
-	return false;
-}
-
-void *
-line_segment::ObjThere(int x, int y)
-{
-	int i, j;
-	POINT pts[2];
-
-	if(ldata && nldata){
-		for(i = 0; i < nli; i++) for(j = 0; j < nldata[i]; j +=2) {
-			pts[0].x = ldata[i][j].x;	pts[0].y = ldata[i][j].y;
-			pts[1].x = ldata[i][j+1].x;	pts[1].y = ldata[i][j+1].y;
-			if(IsCloseToLine(&pts[0], &pts[1], x, y))return this;
-			}
-		}
-	return 0L;
-}
-
-void
-line_segment::DoClip(GraphObj *co)
-{
-	RECT cliprc;
-	int o_nli, *o_nldata = 0L;
-	POINT3D **o_ldata = 0L, *pts = 0L, *pla;
-	int i, j, k, np, r, cx, cy, cz;
-	bool is_valid = false;
-
-	cliprc.left = iround(co->GetSize(SIZE_MIN_X));
-	cliprc.right = iround(co->GetSize(SIZE_MAX_X));
-	cliprc.top = iround(co->GetSize(SIZE_MIN_Y));
-	cliprc.bottom = iround(co->GetSize(SIZE_MAX_Y));
-	if(OverlapRect(&rDims, &cliprc)) {
-		if(!(pts = (POINT3D*)calloc(2, sizeof(POINT3D))))return;
-		o_nli = nli;		nli = 0;
-		o_nldata = nldata;	nldata = 0L;
-		o_ldata = ldata;	ldata = 0L;
-		switch(co->Id) {
-		case GO_SPHERE:
-			cx = iround(co->GetSize(SIZE_XPOS));
-			cy = iround(co->GetSize(SIZE_YPOS));
-			cz = iround(co->GetSize(SIZE_ZPOS));
-			r = iround(co->GetSize(SIZE_RADIUS1));
-			for(i = 0; i < o_nli; i++) for(j = 0; j < (o_nldata[i]-1); j ++) {
-				pts[0].x = o_ldata[i][j].x;			pts[0].y = o_ldata[i][j].y;
-				pts[0].z = o_ldata[i][j].z;			pts[1].x = o_ldata[i][j+1].x;
-				pts[1].y = o_ldata[i][j+1].y;		pts[1].z = o_ldata[i][j+1].z;
-				clip_line_sphere(this, &pts, r, cx, cy, cz);
-				}
-			break;
-		case GO_PLANE:
-			for(i = 0; ((plane*)co)->GetPolygon(&pla, &np, i); i++){
-				for(j = 0; j < o_nli; j++) {
-					if(o_nldata[j] >1) for(k = 0; k < (o_nldata[j]-1); k++){
-						pts[0].x = o_ldata[j][k].x;			pts[0].y = o_ldata[j][k].y;
-						pts[0].z = o_ldata[j][k].z;			pts[1].x = o_ldata[j][k+1].x;
-						pts[1].y = o_ldata[j][k+1].y;		pts[1].z = o_ldata[j][k+1].z;
-						if(pts[0].x != pts[1].x || pts[0].y != pts[1].y || pts[0].z != pts[1].z)
-							clip_line_plane(this, &pts, pla, np, ((plane*)co)->GetVec());
-						}
-					}
-				if(nli) is_valid = true;
-				if(o_ldata) {
-					for(j = 0; j < o_nli; j++) if(o_ldata[j]) free(o_ldata[j]);
-					free(o_ldata);
-					}
-				if(o_nldata) free(o_nldata);
-				o_nli = nli;		nli = 0;
-				o_nldata = nldata;	nldata = 0L;
-				o_ldata = ldata;	ldata = 0L;
-				if(!o_nli) return;				//line is completly hidden
-				}
-			if(is_valid || i==0){
-				nli = o_nli;		o_nli = 0;
-				nldata = o_nldata;	o_nldata = 0L;
-				ldata = o_ldata;	o_ldata = 0L;
-				}
-			break;
-		default:
-			nli = o_nli;		o_nli = 0;
-			nldata = o_nldata;	o_nldata = 0L;
-			ldata = o_ldata;	o_ldata = 0L;
-			break;
-			}
-		if(pts) free(pts);
-		}
-	if(o_ldata) {
-		for(i = 0; i < o_nli; i++) if(o_ldata[i]) free(o_ldata[i]);
-		free(o_ldata);
-		}
-	if(o_nldata) free(o_nldata);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// define a drop line in 3D space
-DropLine3D::DropLine3D(GraphObj *par, DataObj *d, fPOINT3D *p1, int xc,
-		int xr, int yc, int yr, int zc, int zr):GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	Id = GO_DROPL3D;
-	memcpy(&fPos, p1, sizeof(fPOINT3D));
-	if(xc >= 0 || xr >= 0 || yc >= 0 || yr >= 0 || zc >= 0 || zr >= 0) {
-		if(ssRef = (POINT*)malloc(sizeof(POINT)*3)) {
-			ssRef[0].x = xc;	ssRef[0].y = xr;
-			ssRef[1].x = yc;	ssRef[1].y = yr;
-			ssRef[2].x = zc;	ssRef[2].y = zr;
-			cssRef = 3;
-			}
-		}
-	bModified = false;
-	type = 0x01;
-}
-
-DropLine3D::DropLine3D(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	bModified = false;
-}
-
-DropLine3D::~DropLine3D()
-{
-	if(bModified) Undo.InvalidGO(this);
-	if(mo) DelBitmapClass(mo);		mo = 0L;
-	Command(CMD_FLUSH, 0L, 0L);
-}
-
-void
-DropLine3D::DoPlot(anyOutput *o)
-{
-	fPOINT3D fip, fp, fp1;
-	POINT3D p1, p2;
-	int i;
-
-	if(!parent || !o || !o->fvec2ivec(&fPos, &fip)) return;
-	if(mo) DelBitmapClass(mo);		mo = 0L;
-	for(i = 0; i < 6; i++){
-		if(ls[i]) delete(ls[i]);
-		ls[i] = 0L;
-		}
-	p1.x = iround(fip.fx);	p1.y = iround(fip.fy);	p1.z = iround(fip.fz);
-	rDims.left = rDims.right = p1.x;	rDims.top = rDims.bottom = p1.y;
-	for(i = 0; i < 6; i++) {
-		fp.fx = fPos.fx;	fp.fy = fPos.fy;	fp.fz = fPos.fz;
-		if(type & (1 << i)){
-			switch (i) {
-			case 0:	fp.fy = parent->GetSize(SIZE_BOUNDS_YMIN);	break;
-			case 1:	fp.fy = parent->GetSize(SIZE_BOUNDS_YMAX);	break;
-			case 2:	fp.fz = parent->GetSize(SIZE_BOUNDS_ZMIN);	break;
-			case 3:	fp.fz = parent->GetSize(SIZE_BOUNDS_ZMAX);	break;
-			case 4:	fp.fx = parent->GetSize(SIZE_BOUNDS_XMIN);	break;
-			case 5:	fp.fx = parent->GetSize(SIZE_BOUNDS_XMAX);	break;
-				}
-			o->fvec2ivec(&fp, &fp1);		p2.x = iround(fp1.fx);
-			p2.y = iround(fp1.fy);			p2.z = iround(fp1.fz);
-			UpdateMinMaxRect(&rDims, p2.x, p2.y);
-			if(ls[i] = new line_segment(this, data, &Line, &p1, &p2)) ls[i]->DoPlot(o);
-			mpts[i][0].x = p1.x;	mpts[i][0].y = p1.y;
-			mpts[i][1].x = p2.x;	mpts[i][1].y = p2.y;
-			}
-		}
-	IncrementMinMaxRect(&rDims, 5);
-}
-
-void
-DropLine3D::DoMark(anyOutput *o, bool mark)
-{
-	int i;
-
-	if(mark) {
-		if(mo) DelBitmapClass(mo);					mo = 0L;
-		memcpy(&mrc, &rDims, sizeof(RECT));
-		IncrementMinMaxRect(&mrc, 6 + o->un2ix(Line.width));
-		mo = GetRectBitmap(&mrc, o);
-		for(i = 0; i < 6; i++) {
-			if(type & (1 << i)){
-				InvertLine(mpts[i], 2, &Line, 0L, o, mark);
-				}
-			}
-		o->UpdateRect(&mrc, false);
-		}
-	else RestoreRectBitmap(&mo, &mrc, o);
-}
-
-bool
-DropLine3D::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	int i;
-
-	switch (cmd) {
-	case CMD_FLUSH:
-		for(i = 0; i < 6; i++){
-			if(ls[i]) delete(ls[i]);
-			ls[i] = 0L;
-			}
-		if(ssRef) free(ssRef);		ssRef = 0L;
-		if(name) free(name);		name = 0L;
-		return true;
-	case CMD_SCALE:
-		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
-		return true;
-	case CMD_DL_TYPE:
-		if(tmpl && *((int*)tmpl)) type = *((int*)tmpl);
-		return true;
-	case CMD_DL_LINE:
-		if(tmpl) memcpy(&Line, tmpl, sizeof(LineDEF));
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = GO_DROPL3D;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_REDRAW:
-		//Note: this command is issued either by Undo (no output given) or
-		//  by Plot3D::DoPlot after sorting all objects (output specified)
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
-				for(i = 0; i < 6; i++) {
-					if(ls[i] && ls[i]->ObjThere(mev->x, mev->y)){
-						o->ShowMark(this, MRK_GODRAW);
-						return true;
-						}
-					}
-				}
-			break;
-			}
-		break;
-	case CMD_UPDATE:
-		if(ssRef && cssRef >2 && data) {
-			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
-			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
-			data->GetValue(ssRef[2].y, ssRef[2].x, &fPos.fz);
-			return true;
-			}
-		return false;
-	case CMD_AUTOSCALE:
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			((Plot*)parent)->CheckBounds3D(fPos.fx, fPos.fy, fPos.fz);
-			return true;
-			}
-		break;
-	case CMD_SET_GO3D:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// define an arrow in 3D space
-Arrow3D::Arrow3D(GraphObj *par, DataObj *d, fPOINT3D *p1, fPOINT3D *p2, int xc1,
-		int xr1, int yc1, int yr1, int zc1, int zr1, int xc2, int xr2, int yc2, 
-		int yr2, int zc2, int zr2):GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	Id = GO_ARROW3D;
-	memcpy(&fPos1, p1, sizeof(fPOINT3D));	memcpy(&fPos2, p2, sizeof(fPOINT3D));
-	if(xc1 >= 0 || xr1 >= 0 || yc1 >= 0 || yr1 >= 0 || zc1 >= 0 || zr1 >= 0 || 
-		xc2 >= 0 || xr2 >= 0 || yc2 >= 0 || yr2 >= 0 || zc2 >= 0 || zr2 >= 0) {
-		if(ssRef = (POINT*)malloc(sizeof(POINT)*6)) {
-			ssRef[0].x = xc1;	ssRef[0].y = xr1;
-			ssRef[1].x = yc1;	ssRef[1].y = yr1;
-			ssRef[2].x = zc1;	ssRef[2].y = zr1;
-			ssRef[3].x = xc2;	ssRef[3].y = xr2;
-			ssRef[4].x = yc2;	ssRef[4].y = yr2;
-			ssRef[5].x = zc2;	ssRef[5].y = zr2;
-			cssRef = 6;
-			}
-		}
-	bModified = false;
-}
-
-Arrow3D::Arrow3D(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	bModified = false;
-}
-
-Arrow3D::~Arrow3D()
-{
-	if(bModified) Undo.InvalidGO(this);
-	Command(CMD_FLUSH, 0L, 0L);
-}
-
-bool
-Arrow3D::SetSize(int select, double value)
-{
-	switch(select & 0xfff) {
-	case SIZE_ARROW_LINE:
-		Line.width = value;
-		return true;
-	case SIZE_ARROW_CAPWIDTH:
-		cw = value;
-		return true;
-	case SIZE_ARROW_CAPLENGTH:
-		cl = value;
-		return true;
-		}
-	return false;
-}
-
-bool
-Arrow3D::SetColor(int select, DWORD col)
-{
-	switch(select & 0xfff) {
-	case COL_ARROW:
-		Line.color = col;
-		return true;
-		}
-	return false;
-}
-
-void
-Arrow3D::DoPlot(anyOutput *o)
-{
-	double si, csi, tmp, cwr, clr, d, d1, d2;
-	fPOINT3D fip1, fip2, tria[3];
-	POINT3D p1, p2, cp1, cp2;
-	FillDEF fill;
-	int i;
-
-	if(!parent || !o || !o->fvec2ivec(&fPos1, &fip1) ||!o->fvec2ivec(&fPos2, &fip2)) return;
-	for(i = 0; i < 3; i++){
-		if(ls[i]) delete(ls[i]);
-		ls[i] = 0L;
-		}
-	p1.x = iround(fip1.fx);	p1.y = iround(fip1.fy);	p1.z = iround(fip1.fz);
-	p2.x = iround(fip2.fx);	p2.y = iround(fip2.fy);	p2.z = iround(fip2.fz);
-	if(p1.x == p2.x && p1.y == p2.y && p1.z == p2.z) return;	//zero length arrow
-	rDims.left = rDims.right = p1.x;	rDims.top = rDims.bottom = p1.y;
-	UpdateMinMaxRect(&rDims, p2.x, p2.y);
-	IncrementMinMaxRect(&rDims, 5);
-	if(ls[0] = new line_segment(this, data, &Line, &p1, &p2)) ls[0]->DoPlot(o);
-	mpts[0][0].x = p1.x;	mpts[0][0].y = p1.y;	mpts[0][1].x = p2.x;	mpts[0][1].y = p2.y;
-	if(p1.x == p2.x && p1.y == p2.y) return;			//zero length in 2D
-	if((type & 0xff) == ARROW_NOCAP) return;			//no cap;
-	//calculate sine and cosine for cap
-	si = fip1.fy-fip2.fy;
-	tmp = fip2.fx - fip1.fx;
-	si = si/sqrt(si*si + tmp*tmp);
-	csi = fip2.fx-fip1.fx;
-	tmp = fip2.fy - fip1.fy;
-	csi = csi/sqrt(csi*csi + tmp*tmp);
-	//cap corners
-	d1 = (d = fip2.fx - fip1.fx) * d;
-	d1 += ((d = fip2.fy - fip1.fy) * d);
-	d2 = d1 + ((d = fip2.fz - fip1.fz) * d);
-	d1 = sqrt(d1);	d2 = sqrt(d2);	d = d1/d2;
-	cwr = cw;	clr = cl*d;
-	cp1.x = p2.x - o->un2ix(csi*clr + si*cwr/2.0);
-	cp1.y = p2.y + o->un2iy(si*clr - csi*cwr/2.0);
-	cp2.x = p2.x - o->un2ix(csi*clr - si*cwr/2.0);
-	cp2.y = p2.y + o->un2iy(si*clr + csi*cwr/2.0);
-	cp1.z = cp2.z = p2.z;
-	mpts[1][0].x = p2.x;	mpts[1][0].y = p2.y;	mpts[1][1].x = cp1.x;	mpts[1][1].y = cp1.y;
-	mpts[2][0].x = p2.x;	mpts[2][0].y = p2.y;	mpts[2][1].x = cp2.x;	mpts[2][1].y = cp2.y;
-	if((type & 0xff) == ARROW_LINE) {
-		if(ls[1] = new line_segment(this, data, &Line, &p2, &cp1)) ls[1]->DoPlot(o);
-		if(ls[2] = new line_segment(this, data, &Line, &p2, &cp2)) ls[2]->DoPlot(o);
-		}
-	else if((type & 0xff) == ARROW_TRIANGLE) {
-		fill.type = FILL_NONE;	fill.color = Line.color;
-		fill.scale = 1.0;		fill.hatch = 0L;
-		tria[0].fz = tria[1].fz = tria[2].fz = fip2.fz;
-		tria[0].fx = cp1.x;		tria[0].fy = cp1.y;
-		tria[1].fx = fip2.fx;	tria[1].fy = fip2.fy;
-		tria[2].fx = cp2.x;		tria[2].fy = cp2.y;
-		if(cap = new plane(this, data, tria, 3, &Line, &fill))cap->DoPlot(o);
-	}
-}
-
-void
-Arrow3D::DoMark(anyOutput *o, bool mark)
-{
-	int i;
-
-	if(mark) {
-		for(i = 0; i < 3; i++) {
-			if(ls[i]){
-				InvertLine(mpts[i], 2, &Line, 0L, o, mark);
-				}
-			}
-		}
-	else if(parent) parent->Command(CMD_REDRAW, 0L, o);
-}
-
-bool
-Arrow3D::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	int i;
-
-	switch (cmd) {
-	case CMD_FLUSH:
-		for(i = 0; i < 3; i++){
-			if(ls[i]) delete(ls[i]);
-			ls[i] = 0L;
-			}
-		if(cap) delete(cap);	cap = 0L;
-		if(ssRef) free(ssRef);		ssRef = 0L;
-		if(name) free(name);		name = 0L;
-		return true;
-	case CMD_SCALE:
-		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
-		 cw *= ((scaleINFO*)tmpl)->sy.fy;					cl *= ((scaleINFO*)tmpl)->sy.fy;
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = GO_ARROW3D;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_MRK_DIRTY:			//from Undo ?
-	case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
-				for(i = 0; i < 3; i++) {
-					if(ls[i] && ls[i]->ObjThere(mev->x, mev->y)){
-						o->ShowMark(this, MRK_GODRAW);
-						return true;
-						}
-					}
-				}
-			break;
-			}
-		break;
-	case CMD_ARROW_ORG3D:
-		if(tmpl) memcpy(&fPos1, tmpl, sizeof(fPOINT3D));
-		return true;
-	case CMD_ARROW_TYPE:
-		if(tmpl) type = *((int*)tmpl);
-		return true;
-	case CMD_UPDATE:
-		if(ssRef && cssRef >5 && data) {
-			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos2.fx);
-			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos2.fy);
-			data->GetValue(ssRef[2].y, ssRef[2].x, &fPos2.fz);
-			data->GetValue(ssRef[3].y, ssRef[3].x, &fPos1.fx);
-			data->GetValue(ssRef[4].y, ssRef[4].x, &fPos1.fy);
-			data->GetValue(ssRef[5].y, ssRef[5].x, &fPos1.fz);
-			return true;
-			}
-		return false;
-	case CMD_AUTOSCALE:
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
-			((Plot*)parent)->CheckBounds3D(fPos1.fx, fPos1.fy, fPos1.fz);
-			((Plot*)parent)->CheckBounds3D(fPos2.fx, fPos2.fy, fPos2.fz);
-			return true;
-			}
-		break;
-	case CMD_SET_GO3D:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// a data line in 3D space
-Line3D::Line3D(GraphObj *par, DataObj *d, char *rx, char *ry, char *rz)
-	:GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	if(rx && rx[0]) x_range = (char*)memdup(rx, (int)strlen(rx)+2, 0L);
-	if(ry && ry[0]) y_range = (char*)memdup(ry, (int)strlen(ry)+2, 0L);
-	if(rz && rz[0]) z_range = (char*)memdup(rz, (int)strlen(rz)+2, 0L);
-	DoUpdate();
-	Id = GO_LINE3D;
-	bModified = false;
-}
-
-Line3D::Line3D(GraphObj *par, DataObj *d, fPOINT3D *pt, int n_pt, int xc1, int xr1, int yc1, int yr1,
-		int zc1, int zr1, int xc2, int xr2, int yc2, int yr2, int zc2, int zr2)
-	:GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	if(pt && n_pt) {
-		values = (fPOINT3D*)memdup(pt, n_pt * sizeof(fPOINT3D), 0L);
-		nPts = n_pt;
-		ls = (line_segment **)calloc(nPts-1, sizeof(line_segment*));
-		}
-	if(xc1 >= 0 || xr1 >= 0 || yc1 >= 0 || yr1 >= 0 || zc1 >= 0 || zr1 >= 0 || 
-		xc2 >= 0 || xr2 >= 0 || yc2 >= 0 || yr2 >= 0 || zc2 >= 0 || zr2 >= 0) {
-		if(ssRef = (POINT*)malloc(sizeof(POINT)*6)) {
-			ssRef[0].x = xc1;	ssRef[0].y = xr1;
-			ssRef[1].x = yc1;	ssRef[1].y = yr1;
-			ssRef[2].x = zc1;	ssRef[2].y = zr1;
-			ssRef[3].x = xc2;	ssRef[3].y = xr2;
-			ssRef[4].x = yc2;	ssRef[4].y = yr2;
-			ssRef[5].x = zc2;	ssRef[5].y = zr2;
-			cssRef = 6;
-			}
-		}
-	Id = GO_LINE3D;
-	bModified = false;
-}
-
-Line3D::Line3D(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	bModified = false;
-}
-
-Line3D::~Line3D()
-{
-	int i;
-
-	if(bModified) Undo.InvalidGO(this);
-	if(ls){
-		for(i = 0; i < (nPts-1); i++) if(ls[i]) delete(ls[i]);
-		free(ls);
-		}
-	if(pts && npts) free(pts);		pts = 0L;		npts = 0;	cssRef = 0;
-	if(x_range) free(x_range);		x_range = 0L;
-	if(y_range) free(y_range);		y_range = 0L;
-	if(z_range) free(z_range);		z_range = 0L;
-	if(values) free(values);		values = 0L;
-	if(ssRef) free(ssRef);			ssRef = 0L;
-	if(mo) DelBitmapClass(mo);		mo = 0L;
-}
-
-void
-Line3D::DoPlot(anyOutput *o)
-{
-	int i, j;
-	fPOINT3D fip;
-	POINT3D p1, p2;
-	POINT np;
-
-
-	if(mo) DelBitmapClass(mo);		mo = 0L;
-	if(ls) {
-		if(pts && npts) free(pts);
-		npts = 0;	if(!(pts = (POINT*)calloc(nPts, sizeof(POINT))))return;
-		for(i = 0; i< nPts; i++) {
-			if(!o->fvec2ivec(&values[i], &fip)) return;
-			p2.x = iround(fip.fx);	p2.y = iround(fip.fy);	p2.z = iround(fip.fz);
-			np.x = p2.x;			np.y = p2.y;
-			AddToPolygon(&npts, pts, &np);
-			if(i) {
-				UpdateMinMaxRect(&rDims, np.x, np.y);
-				j = i-1;
-				if(ls[j]) delete(ls[j]);
-				if(ls[j] = new line_segment(this, data, &Line, &p1, &p2)) {
-					ls[j]->DoPlot(o);
-					}
-				}
-			else {
-				rDims.left = rDims.right = p2.x;
-				rDims.top = rDims.bottom = p2.y;
-				}
-			p1.x = p2.x;	p1.y = p2.y;	p1.z = p2.z;
-			}
-		IncrementMinMaxRect(&rDims, 6);
-		}
-}
-
-void
-Line3D::DoMark(anyOutput *o, bool mark)
-{
-	if(mark) {
-		if(mo) DelBitmapClass(mo);					mo = 0L;
-		memcpy(&mrc, &rDims, sizeof(RECT));
-		IncrementMinMaxRect(&mrc, 6 + o->un2ix(Line.width));
-		mo = GetRectBitmap(&mrc, o);
-		InvertLine(pts, npts, &Line, &rDims, o, true);
-		}
-	else RestoreRectBitmap(&mo, &mrc, o);
-}
-
-bool
-Line3D::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	POINT p1;
-	int i;
-
-	switch (cmd) {
-	case CMD_FLUSH:
-		if(name) free(name);		name = 0L;
-		return true;
-	case CMD_SCALE:
-		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
-		return true;
-	case CMD_SET_LINE:
-		if(tmpl) {
-			memcpy(&Line, tmpl, sizeof(LineDEF));
-			return true;
-			}
-		return false;
-	case CMD_SET_DATAOBJ:
-		Id = GO_LINE3D;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_REDRAW:
-		//Note: this command is issued  by Undo (no output given)
-		if(!parent) return false;
-		if(!o) return parent->Command(cmd, tmpl, o);
-		//Should we ever come here ?
-		return true;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(!IsInRect(&rDims, p1.x= mev->x, p1.y=mev->y)|| CurrGO || !o || nPts <2 ||
-				!IsCloseToPL(p1, pts, npts))return false;
-			o->ShowMark(CurrGO=this, MRK_GODRAW);
-			return true;
-			}
-		return false;
-	case CMD_UPDATE:
-		if(parent && parent->Id != GO_GRID3D) {
-			Undo.DataMem(this, (void**)&values, nPts * sizeof(fPOINT3D), &nPts, UNDO_CONTINUE);
-			}
-		if(ssRef && cssRef >5 && data && nPts == 2) {
-			data->GetValue(ssRef[0].y, ssRef[0].x, &values[0].fx);
-			data->GetValue(ssRef[1].y, ssRef[1].x, &values[0].fy);
-			data->GetValue(ssRef[2].y, ssRef[2].x, &values[0].fz);
-			data->GetValue(ssRef[3].y, ssRef[3].x, &values[1].fx);
-			data->GetValue(ssRef[4].y, ssRef[4].x, &values[1].fy);
-			data->GetValue(ssRef[5].y, ssRef[5].x, &values[1].fz);
-			return true;
-			}
-		else DoUpdate();
-	case CMD_AUTOSCALE:
-		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH){
-			if(min.fx == max.fx || min.fy == max.fy){	//z's may be equal !
-				min.fx = min.fy = min.fz = HUGE_VAL;
-				max.fx = max.fy = max.fz = -HUGE_VAL;
-				for(i = 0; i < nPts; i++) {
-					if(values[i].fx < min.fx) min.fx = values[i].fx;
-					if(values[i].fy < min.fy) min.fy = values[i].fy;
-					if(values[i].fz < min.fz) min.fz = values[i].fz;
-					if(values[i].fx > max.fx) max.fx = values[i].fx;
-					if(values[i].fy > max.fy) max.fy = values[i].fy;
-					if(values[i].fz > max.fz) max.fz = values[i].fz;
-					}
-				}
-			((Plot*)parent)->CheckBounds3D(min.fx, min.fy, min.fz);
-			((Plot*)parent)->CheckBounds3D(max.fx, max.fy, max.fz);
-			return true;
-			}
-		return false;
-	case CMD_SET_GO3D:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-		}
-	return false;
-}
-
-void
-Line3D::DoUpdate()
-{
-	int n1 = 0, ic = 0, i, j, k, l, m, n;
-	double x, y, z;
-	AccRange *rX=0L, *rY=0L, *rZ=0L;
-
-	if(!x_range || !y_range || !z_range) return;
-	if(values) free(values);	values = 0L;
-	if(ls) free(ls);			ls = 0L;
-	rX = new AccRange(x_range);
-	rY = new AccRange(y_range);
-	rZ = new AccRange(z_range);
-	min.fx = min.fy = min.fz = HUGE_VAL;	max.fx = max.fy = max.fz = -HUGE_VAL;
-	if(rX) n1 = rX->CountItems();
-	if(n1 && rY && rZ && (values = (fPOINT3D*)malloc(n1 * sizeof(fPOINT3D)))){
-		rX->GetFirst(&i, &j);		rX->GetNext(&i, &j);
-		rY->GetFirst(&k, &l);		rY->GetNext(&k, &l);
-		rZ->GetFirst(&m, &n);		rZ->GetNext(&m, &n);
-		do {
-			if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y) && 
-				data->GetValue(n, m, &z)){
-				values[ic].fx = x;	values[ic].fy = y;	values[ic].fz = z;
-				if(x < min.fx) min.fx = x;	if(x > max.fx) max.fx = x;
-				if(y < min.fy) min.fy = y;	if(y > max.fy) max.fy = y;
-				if(z < min.fz) min.fz = z;	if(z > max.fz) max.fz = z;
-				ic++;
-				}
-			}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l) && rZ->GetNext(&m, &n));
-		nPts = ic;
-		if(ic > 1) ls = (line_segment **)calloc(ic-1, sizeof(line_segment*));
-		}
-	if(rX) delete(rX);	if(rY) delete(rY); if(rZ) delete(rZ);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// the text label class 
-Label::Label(GraphObj *par, DataObj *d, double x, double y, TextDEF *td, DWORD flg,
-	 int xc, int xr, int yc, int yr, int tc, int tr):GraphObj(par, d)
-{
-	int cb;
-
-	FileIO(INIT_VARS);
-	fPos.fx = x;		fPos.fy = y;		flags = flg;
-	if(parent){
-		fDist.fx = parent->GetSize(SIZE_LB_XDIST);
-		fDist.fy = parent->GetSize(SIZE_LB_YDIST);
-		}
-	Id = GO_LABEL;
-	if(td){
-		memcpy(&TextDef, td, sizeof(TextDEF));
-		if(td->text && td->text[0]) {
-			cb = (int)strlen(td->text)+1;			if(cb < 20) cb = 20;
-			TextDef.text = (char*)malloc(cb *sizeof(char));
-			rlp_strcpy(TextDef.text, cb, td->text);
-			}
-		}
-	if(xc >= 0 || xr >= 0 || yc >= 0 || yr >= 0 || tc >= 0 || tr >= 0) {
-		if(ssRef = (POINT*)malloc(sizeof(POINT)*3)) {
-			ssRef[0].x = xc;	ssRef[0].y = xr;
-			ssRef[1].x = yc;	ssRef[1].y = yr;
-			ssRef[2].x = tc;	ssRef[2].y = tr;
-			cssRef = 3;
-			}
-		}
-}
-
-Label::Label(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-Label::~Label()
-{
-	HideTextCursor();
-	Command(CMD_FLUSH, 0L, 0L);
-	if(bModified)Undo.InvalidGO(this);
-}
-
-double
-Label::GetSize(int select)
-{
-	switch(select){
-	case SIZE_CURSORPOS:
-		return (double)CursorPos;
-	case SIZE_CURSOR_XMIN:
-		return (double) (Cursor.right > Cursor.left ? Cursor.left : Cursor.right);
-	case SIZE_CURSOR_XMAX:
-		return (double) (Cursor.right > Cursor.left ? Cursor.right : Cursor.left);
-	case SIZE_CURSOR_YMIN:
-		return (double) (Cursor.bottom > Cursor.top ? Cursor.top : Cursor.bottom);
-	case SIZE_CURSOR_YMAX:
-		return (double) (Cursor.bottom > Cursor.top ? Cursor.bottom : Cursor.top);
-	case SIZE_MIN_Z:	case SIZE_MAX_Z:	case SIZE_ZPOS:
-		return curr_z;
-		}
-	return 0.0;
-}
-
-bool
-Label::SetSize(int select, double value)
-{
-	switch(select & 0xfff) {
-	case SIZE_LB_XDIST:			fDist.fx = value;		return true;
-	case SIZE_LB_YDIST:			fDist.fy = value;		return true;
-	case SIZE_XPOS:				fPos.fx = value;		return true;
-	case SIZE_YPOS:				fPos.fy = value;		return true;
-	case SIZE_ZPOS:				curr_z = value;			return is3D = true;
-		}
-	return false;
-}
-
-bool
-Label::SetColor(int select, DWORD col)
-{
-	switch(select & 0xfff) {
-	case COL_TEXT:
-		if(select & UNDO_STORESET) {
-			Undo.ValDword(this, &TextDef.ColTxt, UNDO_CONTINUE);
-			bModified = true;
-			}
-		TextDef.ColTxt = col;			bBGvalid = false;
-		return true;
-	case COL_BG:
-		bgcolor = col;	bBGvalid = true;
-		return true;
-		}
-	return false;
-}
-
-void
-Label::DoPlot(anyOutput *o)
-{
-	if(this != CurrGO && m1 >=0 && m2 >=0) {
-		m1 = m2 = -1;
-		}
-	if(is3D && parent && parent->Command(CMD_SET_GO3D, this, o)) return;
-	DoPlotText(o);
-}
-
-void
-Label::DoMark(anyOutput *o, bool mark)
-{
-	DWORD bgpix[16];
-	int i, d, d1, ix, iy, dx, dy, n;
-	RECT mrc;
-	anyOutput *mo;
-
-	if(mark) {
-		//find color with maximum contrast to text
-		if(!bBGvalid) {
-			mrc.left = rDims.left;		mrc.right = rDims.right;
-			mrc.top = rDims.top;		mrc.bottom = rDims.bottom;
-			dx = mrc.right - mrc.left;	dy = mrc.bottom - mrc.top;
-			if(dx <= 0 || dy <= 0) return;
-			if(mo = GetRectBitmap(&mrc, o)) {
-				for(i = 0,  ix = 1; ix < dx; ix += dx<<2) {
-					for(iy = 1; iy < dy; dy += dy<<2) {
-						if(!(mo->oGetPix(pts[ix].x, pts[iy].y, &bgpix[i]))) bgpix[i] = 0x00ffffff;
-						i++;
-						}
-					}
-				DelBitmapClass(mo);		n = i;
-				}
-			bgcolor = bgpix[0];
-			d = ColDiff(bgcolor, TextDef.ColTxt);
-			for(i = 1; i < n; i++) {
-				if(d < (d1 = ColDiff(bgpix[i], TextDef.ColTxt))) {
-					d = d1;		bgcolor = bgpix[i];
-					}
-				}
-			if(!d) bgcolor = TextDef.ColTxt ^ 0x00ffffffL;
-			bBGvalid = true;
-			}
-		//in dialogs parent has no parent
-		if(parent && parent->parent) o->ShowLine(pts, 5, TextDef.ColTxt);
-		ShowCursor(o);	CurrGO = this;		CurrLabel = this;
-		}
-	else if(m1 >= 0 && m2 >= 0) {
-		m1 = m2 = -1;
-		parent->Command(CMD_REDRAW, 0L, o);
-		}
-	else {
-		HideTextCursor();				m1 = m2 = -1;
-		bgLine.color = bgcolor;			o->SetLine(&bgLine);
-		//in dialogs parent has no parent
-		if(parent && parent->parent) o->oPolyline(pts, 5);
-		IncrementMinMaxRect(&rDims, 3);
-		o->UpdateRect(&rDims, false);	IncrementMinMaxRect(&rDims, -3);
-		if(CurrLabel == this) CurrLabel = 0L;
-		if(parent && parent->Id != GO_MLABEL && (!TextDef.text || !TextDef.text[0]))
-			parent->Command(CMD_DELOBJ, (void*)this, o);
-		}
-}
-
-bool
-Label::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	scaleINFO *scale;
-	int i, cb;
-
-	if(cmd != CMD_SET_DATAOBJ && !parent) return false;
-	switch (cmd) {
-	case CMD_SCALE:
-		scale = (scaleINFO*)tmpl;
-		if((flags & 0x03)!= LB_X_DATA) fPos.fx *= scale->sx.fy;
-		if((flags & 0x30)!= LB_Y_DATA) fPos.fy *= scale->sy.fy;
-		fDist.fx *= scale->sx.fy;				fDist.fy *= scale->sy.fy;
-		TextDef.fSize *= scale->sx.fy;			TextDef.iSize = 0;
-		return true;
-	case CMD_HIDEMARK:
-		if(m1 >=0 && m2 >=0 && m1 != m2) {
-			m1 = m2 = -1;						return true;
-			}
-		return false;
-	case CMD_FLUSH:
-		if(CurrLabel == this) CurrLabel = 0L;
-		if(TextDef.text) free(TextDef.text);	TextDef.text = 0L;
-		if(ssRef) free(ssRef);					ssRef = 0L;
-		return true;
-	case CMD_POS_FIRST:		case CMD_POS_LAST:
-		Undo.ValInt(this, &CursorPos, 0L);
-		bModified = true;
-		if(o && TextDef.text) {
-			CursorPos = (cmd == CMD_POS_LAST) ? (int)strlen(TextDef.text) : 0;
-			ShowCursor(o);
-			return true;
-			}
-		return false;
-	case CMD_SHIFTLEFT:		case CMD_CURRLEFT:
-		if(o && CursorPos >0 && TextDef.text) {
-			Undo.ValInt(this, &CursorPos, 0L);
-			DrawFmtText.SetText(0L, TextDef.text, 0L, 0L);
-			bModified = true;
-			if(cmd == CMD_SHIFTLEFT) {
-				if(CursorPos && CursorPos == m1) {
-					DrawFmtText.cur_left(&CursorPos);
-					m1 = CursorPos;		ShowCursor(o);
-					}
-				else if(CursorPos && CursorPos == m2) {
-					DrawFmtText.cur_left(&CursorPos);
-					m2 = CursorPos;		ShowCursor(o);
-					if(parent) parent->Command(CMD_REDRAW, 0L, o); 
-					}
-				else if(CursorPos){
-					m2 = CursorPos;
-					DrawFmtText.cur_left(&CursorPos);
-					m1 = CursorPos;		ShowCursor(o);
-					}
-				}
-			else {
-				if(m1 >= 0 && m2 >= 0 && parent) {
-					m1 = m2 = -1;						parent->Command(CMD_REDRAW, 0L, o);
-					}
-				DrawFmtText.cur_left(&CursorPos);		ShowCursor(o);
-				}
-			if(m1 >=0 && m2 >=0 && m1 != m2) DoPlot(o);
-			return true;
-			}
-		return false;
-	case CMD_SHIFTRIGHT:	case CMD_CURRIGHT:
-		if(o && TextDef.text && CursorPos < (int)strlen(TextDef.text)) {
-			Undo.ValInt(this, &CursorPos, 0L);
-			DrawFmtText.SetText(0L, TextDef.text, 0L, 0L);
-			bModified = true;
-			if(cmd == CMD_SHIFTRIGHT) {
-				if(CursorPos == m1 && TextDef.text[m1]) {
-					DrawFmtText.cur_right(&CursorPos);
-					m2 = CursorPos;		ShowCursor(o);
-					memcpy(&rm2, &Cursor, sizeof(RECT));
-					if(parent) parent->Command(CMD_REDRAW, 0L, o); 
-					}
-				else if(CursorPos == m2 && TextDef.text[m2]) {
-					DrawFmtText.cur_right(&CursorPos);
-					m2 = CursorPos;		ShowCursor(o);
-					memcpy(&rm2, &Cursor, sizeof(RECT));
-					}
-				else if(TextDef.text[CursorPos]){
-					if(m1 < 0) {
-						m1 = CursorPos;		ShowCursor(o);
-						}
-					DrawFmtText.cur_right(&CursorPos);
-					m2 = CursorPos;		ShowCursor(o);
-					}
-				else return false;
-				}
-			else {
-				if(m1 >= 0 && m2 >= 0 && parent) {
-					m1 = m2 = -1;						parent->Command(CMD_REDRAW, 0L, o);
-					}
-				DrawFmtText.cur_right(&CursorPos);		ShowCursor(o);
-				}
-			if(m1 >=0 && m2 >=0 && m1 != m2) DoPlot(o);
-			return true;
-			}
-		return false;
-	case CMD_ADDCHAR:	case CMD_ADDCHARW:
-		SetModified();
-		if(tmpl && 8 != *((int*)tmpl)) return AddChar(*((int*)tmpl), o);
-		//value 8 == backspace
-	case CMD_BACKSP:
-		SetModified();
-		if(CursorPos <=0 && o) {
-			if(parent && parent->Id == GO_MLABEL) {
-				parent->Command(CMD_SETFOCUS, this, o);
-				return parent->Command(CMD_BACKSP, tmpl, o);
-				}
-			RedrawEdit(o);
-			return true;
-			}
-		DrawFmtText.SetText(0L, TextDef.text, 0L, 0L);
-		DrawFmtText.cur_left(&CursorPos);					//continue as if delete
-	case CMD_DELETE:
-		SetModified();
-		if(TextDef.text && TextDef.text[CursorPos]) {
-			Undo.String(this, &TextDef.text, 0L);
-			if(cmd == CMD_DELETE) Undo.ValInt(this, &CursorPos, UNDO_CONTINUE);
-			if(CheckMark() && m2 < (cb = (int) strlen(TextDef.text))) {
-				Undo.ValInt(this, &m1, UNDO_CONTINUE);
-				Undo.ValInt(this, &m2, UNDO_CONTINUE);
-				rlp_strcpy(TextDef.text + m1, cb, TextDef.text + m2);
-				CursorPos = m1;			m1 = m2 = -1;
-				}
-			else {
-				DrawFmtText.SetText(0L, TextDef.text, 0L, 0L);
-				cb = CursorPos;		DrawFmtText.cur_right(&cb);
-				cb -= CursorPos;	if(cb < 1) cb = 1;
-				rlp_strcpy(TextDef.text + CursorPos, TMP_TXT_SIZE, TextDef.text + CursorPos + cb);
-				}
-			if(o) {
-				RedrawEdit(o);			ShowCursor(o);
-				}
-			}
-		else if(TextDef.text && parent->Id == GO_MLABEL) {
-			parent->Command(CMD_SETFOCUS, this, o);
-			return parent->Command(CMD_DELETE, tmpl, o);
-			}
-		else o->HideMark();
-		break;
-	case CMD_GETTEXT:
-		if(TextDef.text && TextDef.text[0] && tmpl) {
-			rlp_strcpy((char*)tmpl, TMP_TXT_SIZE, TextDef.text);
-			return true;
-			}
-		return false;
-	case CMD_SETTEXT:
-		if(TextDef.text) free(TextDef.text);		TextDef.text = 0L;
-		if(tmpl && *((char*)tmpl)) {
-			TextDef.text = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0);
-			}
-		return true;
-	case CMD_GETTEXTDEF:
-		if(!tmpl) return false;
-		memcpy(tmpl, &TextDef, sizeof(TextDEF));
-		return true;
-	case CMD_SETTEXTDEF:
-		if(!tmpl)return false;
-		memcpy(&TextDef, tmpl, sizeof(TextDEF)-sizeof(char*));
-		if(((TextDEF*)tmpl)->text) Command(CMD_SETTEXT, tmpl, o);
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = GO_LABEL;
-		data = (DataObj*)tmpl;
-		return true;
-	case CMD_UPDATE:
-		if(ssRef && cssRef >2 && data) {
-			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
-			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
-			if(data->GetText(ssRef[2].y, ssRef[2].x, TmpTxt, TMP_TXT_SIZE)) {
-				Undo.String(this, &TextDef.text, UNDO_CONTINUE);
-				TextDef.text = (char*)realloc(TextDef.text, cb = (int)strlen(TmpTxt)+2);
-				if(TmpTxt[0]) rlp_strcpy(TextDef.text, cb, TmpTxt);
-				else TextDef.text[0] = 0;
-				}
-			return true;
-			}
-		return false;
-	case CMD_SELECT:
-		if(!o || !tmpl) return false;
-		CalcCursorPos(((POINT*)tmpl)->x, ((POINT*)tmpl)->y, o);
-		o->ShowMark(this, MRK_GODRAW);
-		return true;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_MOVE:
-			if((mev->StateFlags & 0x01) && ObjThere(mev->x, mev->y)) {
-				i = CursorPos;				CalcCursorPos(mev->x, mev->y, o);
-				if(CurrLabel && CurrLabel != this) {
-					CurrLabel->Command(CMD_HIDEMARK, tmpl, o);
-					}
-				CurrGO = CurrLabel = this;
-				if(i == CursorPos) return true;
-				if(CursorPos > m1 && CursorPos < m2) {
-					if(m1 < 0) m1 = CursorPos;
-					else if(m2 != CursorPos)m2 = CursorPos;
-					parent->Command(CMD_REDRAW, 0L, o);
-					}
-				else {
-					if(m1 < 0) m1 = CursorPos;
-					else if(m2 != CursorPos)m2 = CursorPos;
-					if(m1 >=0 && m2 >=0 && m1 != m2) DoPlot(o);
-					}
-				return true;
-				}
-			break;
-		case MOUSE_LBUP:
-			if(ObjThere(mev->x, mev->y)) {
-				if(parent && parent->Id == GO_MLABEL) parent->Command(CMD_SETFOCUS, this, o);
-				CalcCursorPos(mev->x, mev->y, o);		
-				if(o->MrkRect && (void*)o->MrkRect == (void*)this) o->MrkMode = MRK_NONE;
-				if(m1 < 0) m1 = CursorPos;			ShowCursor(o);
-				o->ShowMark(this, MRK_GODRAW);
-				}
-			else if(m1 >= 0 && m2 >= 0) {
-				m1 = m2 = -1;		DoPlot(o);
-				}
-			break;
-			}
-		break;
-	case CMD_TEXTTHERE:
-		if(ObjThere(((MouseEvent *)tmpl)->x, ((MouseEvent *)tmpl)->y)) {
-			CalcCursorPos(((MouseEvent *)tmpl)->x, ((MouseEvent *)tmpl)->y, o);
-			CalcRect(o);
-			m1 = m2 = CursorPos;						CurrGO = this;								
-			return true;
-			}
-		m1 = m2 = -1;
-		return false;
-	case CMD_AUTOSCALE:
-		if(parent->Id >= GO_PLOT && parent->Id < GO_GRAPH
-			&& (flags & LB_X_DATA) && (flags & LB_Y_DATA)) {
-			((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
-			return true;
-			}
-		break;
-	case CMD_REDRAW:
-		if(is3D && o) {
-			DoPlotText(o);
-			is3D = false;										//enable edit
-			}
-		else if(CurrGO == this) {
-			if(parent && parent->parent) RedrawEdit(defDisp);	//not a dialog
-			else ShowCursor(defDisp);							//dialog !
-			}
-		else return parent->Command(cmd, tmpl, o);
-		return true;
-	case CMD_MOVE:
-		if(parent && (parent->Id == GO_MLABEL || parent->Id == GO_LEGITEM))
-			return parent->Command(cmd, tmpl, o);
-		Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
-	case CMD_UNDO_MOVE:
-		if(!(flags & 0x03)) fPos.fx += ((lfPOINT*)tmpl)[0].fx;
-		if(!(flags & 0x30)) fPos.fy += ((lfPOINT*)tmpl)[0].fy;
-		if(o){
-			o->StartPage();		parent->DoPlot(o);		o->EndPage();
-			}
-		return bModified = true;
-		}
-	return false;
-}
-
-void *
-Label::ObjThere(int x, int y)
-{
-	POINT p1;
-
-	if(IsInRect(&rDims, p1.x = x, p1.y = y) && IsInPolygon(&p1, pts, 5))
-		return this;
-	return 0L;
-}
-
-void
-Label::Track(POINT *p, anyOutput *o)
-{
-	POINT *tpts;
-	RECT old_rc;
-	int i;
-
-	if(!parent) return;
-	m1 = m2 = -1;
-	if(parent->Id == GO_MLABEL || parent->Id == GO_LEGITEM){
-		parent->Track(p, o);
-		return;
-		}
-	if(o && (tpts = (POINT*)malloc(5*sizeof(POINT)))){
-		memcpy(&old_rc, &rDims, sizeof(rDims));
-		//note: mLabel set Id to zero upon trackking
-		//   thus rectangle is not updated if parent is a mLabel
-		if(parent->Id) o->UpdateRect(&rDims, false);
-		for(i = 0; i < 5; i++) {
-			tpts[i].x = pts[i].x+p->x;	tpts[i].y = pts[i].y+p->y;
-			UpdateMinMaxRect(&rDims, tpts[i].x, tpts[i].y);
-			}
-		o->ShowLine(tpts, 5, TextDef.ColTxt);
-		if(old_rc.left != rDims.left || old_rc.right != rDims.right || old_rc.top !=
-			rDims.top || old_rc.bottom != rDims.bottom)IncrementMinMaxRect(&rDims, 3);
-		free(tpts);
-		}
-}
-
-bool
-Label::CalcRect(anyOutput *o)
-{
-	int rx1, rx, ry;
-	fRECT rc, rcc;
-
-	if(parent && parent->Id != GO_MLABEL) o->SetTextSpec(&TextDef);
-	DrawFmtText.SetText(0L, TextDef.text, &ix, &iy);
-	if(TextDef.text && TextDef.text[0]) {
-		if(!(DrawFmtText.oGetTextExtent(o, &rx, &ry, 0))) return false;
-		rx++;
-		}
-	else {
-		if(!(o->oGetTextExtent("A", 1, &rx, &ry))) return false;
-		rx = 1;
-		}
-	rx += 4;	rc.Xmin = -2.0f;	rc.Ymin = 0.0f;		rc.Xmax = rx;		rc.Ymax = ry;
-	si = sin(TextDef.RotBL *0.01745329252);	csi = cos(TextDef.RotBL *0.01745329252);
-	if(TextDef.Align & TXA_HCENTER) {
-		rc.Xmin -= rx/2.0-1.0;		rc.Xmax -= rx/2.0-1.0;
-		}
-	else if(TextDef.Align & TXA_HRIGHT) {
-		rc.Xmin -= rx-2.0;			rc.Xmax -= rx-2.0;
-		}
-	if(TextDef.Align & TXA_VCENTER) {
-		rc.Ymin -= ry/2.0;			rc.Ymax -= ry/2.0;
-		}
-	else if(TextDef.Align & TXA_VBOTTOM) {
-		rc.Ymin -= ry;				rc.Ymax -= ry;
-		}
-	if(DrawFmtText.oGetTextExtent(o, &rx1, &ry, CursorPos)){
-		rx = CursorPos ? (int)rx1 : 0;
-		}
-	else rx = 0;
-	rcc.Xmax = rc.Xmin + (double)rx+2.0;	rcc.Ymin = rc.Ymin+2.0;
-	rcc.Xmin = rc.Xmin;						rcc.Ymax = rc.Ymax-2.0;
-	pts[0].x = iround(rc.Xmin*csi + rc.Ymin*si)+ix;
-	pts[0].y = iround(rc.Ymin*csi - rc.Xmin*si)+iy;
-	pts[1].x = iround(rc.Xmax*csi + rc.Ymin*si)+ix;
-	pts[1].y = iround(rc.Ymin*csi - rc.Xmax*si)+iy;
-	pts[2].x = iround(rc.Xmax*csi + rc.Ymax*si)+ix;
-	pts[2].y = iround(rc.Ymax*csi - rc.Xmax*si)+iy;
-	pts[3].x = iround(rc.Xmin*csi + rc.Ymax*si)+ix;
-	pts[3].y = iround(rc.Ymax*csi - rc.Xmin*si)+iy;
-	pts[4].x = pts[0].x;	pts[4].y = pts[0].y;
-	Cursor.left = iround(rcc.Xmax*csi + rcc.Ymin*si)+ix;
-	Cursor.top = iround(rcc.Ymin*csi - rcc.Xmax*si)+iy;
-	Cursor.right = iround(rcc.Xmax*csi + rcc.Ymax*si)+ix;
-	Cursor.bottom = iround(rcc.Ymax*csi - rcc.Xmax*si)+iy;
-	return true;
-}
-
-void 
-Label::RedrawEdit(anyOutput *o)
-{
-	FillDEF bgFill = {FILL_NONE, bgcolor, 1.0, 0L, bgcolor};
-
-	if(!o || !parent) return;
-	bgLine.color = bgcolor;		o->SetLine(&bgLine);	o->SetFill(&bgFill);
-	o->oPolygon(pts, 5);		IncrementMinMaxRect(&rDims, 3);		
-	o->UpdateRect(&rDims, false);
-	CalcRect(o);				bgLine.color ^= 0x00ffffffL;
-	o->SetLine(&bgLine);		o->oPolygon(pts, 5);
-	if(parent->Id == GO_MLABEL) {
-		if(parent->parent && parent->parent->Id == GO_LEGITEM && parent->parent->parent)
-			parent->parent->parent->DoPlot(o);
-		else parent->DoPlot(o);
-		}
-	else if(parent->Id == GO_LEGITEM && parent->parent) parent->parent->DoPlot(o);
-	else DoPlot(o);
-	o->UpdateRect(&rDims, false);
-	DoMark(o, true);			ShowCursor(o);
-}
-
-void
-Label::SetModified()
-{
-	AxisDEF *adef;
-
-	bModified = true;
-	if(parent && parent->Id==GO_TICK && parent->parent && parent->parent->Id==GO_AXIS){
-	adef = ((Axis*)(parent->parent))->GetAxis();
-	adef->flags &= ~AXIS_AUTOSCALE;
-	}
-}
-
-void
-Label::DoPlotText(anyOutput *o)
-{
-	static LineDEF yLine = {0.0, 1.0, 0x0000ffff, 0x0};
-	static FillDEF yFill = {0, 0x0000ffff, 1.0, 0L, 0x0000ffff};
-	POINT mpts[5];
-	int i;
-
-	if(!parent || !o) return;
-	if(m1 >= 0 && m2 >= 0 && m1 != m2 && CurrGO == this) {
-		i = CursorPos;							CursorPos = m1;
-		CalcRect(o);							memcpy(&rm1, &Cursor, sizeof(RECT));
-		CursorPos = m2;							CalcRect(o);
-		memcpy(&rm2, &Cursor, sizeof(RECT));	CursorPos = i;
-		if(CurrGO == this) ShowCursor(o);
-		else CalcRect(o);
-		if(m2 > m1) {
-			mpts[0].x = mpts[4].x = rm1.left;	mpts[1].x = rm1.right;
-			mpts[0].y = mpts[4].y = rm1.top;	mpts[1].y = rm1.bottom;
-			mpts[2].x = rm2.right;				mpts[2].y = rm2.bottom;
-			mpts[3].x = rm2.left;				mpts[3].y = rm2.top;
-			}
-		else {
-			mpts[0].x = mpts[4].x = rm2.left;	mpts[1].x = rm2.right;
-			mpts[0].y = mpts[4].y = rm2.top;	mpts[1].y = rm2.bottom;
-			mpts[2].x = rm1.right;				mpts[2].y = rm1.bottom;
-			mpts[3].x = rm1.left;				mpts[3].y = rm1.top;
-			}
-		o->SetLine(&yLine);						o->SetFill(&yFill);
-		o->oPolygon(mpts, 5, 0L);
-		}
-	else {
-		m1 = m2 = -1;
-		if(CurrGO == this) ShowCursor(o);
-		}
-	defDisp = o;
-	if(parent && parent->Id == GO_MLABEL) parent->Command(CMD_SETFOCUS, this, o);
-	switch(flags & 0x03) {
-	case LB_X_DATA:	ix = o->fx2ix(fPos.fx);		break;
-	case LB_X_PARENT: 
-		ix = iround(parent->GetSize(SIZE_LB_XPOS));
-		break;
-	default:
-		ix = o->co2ix(fPos.fx + parent->GetSize(SIZE_GRECT_LEFT));
-		break;
-		}
-	switch(flags & 0x30) {
-	case LB_Y_DATA:	iy = o->fy2iy(fPos.fy);		break;
-	case LB_Y_PARENT: 
-		iy = iround(parent->GetSize(SIZE_LB_YPOS));
-		break;
-	default:
-		iy = o->co2iy(fPos.fy +parent->GetSize(SIZE_GRECT_TOP));
-		break;
-		}
-	ix += o->un2ix(fDist.fx);		iy += o->un2iy(fDist.fy);
-	TextDef.iSize = o->un2iy(TextDef.fSize);
-	o->SetTextSpec(&TextDef);
-	if(TextDef.text && TextDef.text[0]){
-		DrawFmtText.SetText(o, TextDef.text, &ix, &iy);
-		}
-	if(!(CalcRect(o))) return;
-	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
-	UpdateMinMaxRect(&rDims, pts[2].x, pts[2].y);
-	UpdateMinMaxRect(&rDims, pts[3].x, pts[3].y);
-	if(m1 >= 0 && m2 >= 0 && m1 != m2 && CurrGO == this && fabs(TextDef.RotBL) < 0.01) {
-		o->CopyBitmap(mpts[0].x, mpts[0].y, o, mpts[0].x, mpts[0].y,
-			mpts[2].x - mpts[0].x, mpts[2].y - mpts[0].y, true);
-		}
-	if(m1 >= 0 && m2 >= 0) o->UpdateRect(&rDims, false);
-}
-
-bool
-Label::CheckMark()
-{
-	int m;
-
-	if(m1 < 0 || m2 < 0 || m1 == m2) return false;
-	if(m1 < m2) return true;
-	//come here on right to left mark: swap m1 and m2
-	m = m1;		m1 = m2;	m2 = m;
-	return	true;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// a multiline label consists of several Label objects
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-mLabel::mLabel(GraphObj *par, DataObj *d, double x, double y, TextDEF *td, char *txt, 
-	int cp, DWORD flg):GraphObj(par, d)
-{
-	int i;
-
-	memcpy(&TextDef, td, sizeof(TextDEF));
-	TextDef.text = 0L;		fPos.fx = x;		fPos.fy = y;
-	Lines = 0L;				flags = flg;		lspc = 1.0;
-	fDist.fx = 0.0;			fDist.fy = 0.0;
-	CurrGO = CurrLabel = 0L;
-	if(txt && (Lines = (Label**)calloc(2, sizeof(Label*)))) {
-		for(i = 0; i < cp && txt[i]; i ++) TmpTxt[i] = txt[i];
-		TmpTxt[i] = 0;
-		if(Lines[0] = new 	Label(this, d, x, y, &TextDef, LB_X_PARENT | LB_Y_PARENT))
-			Lines[0]->Command(CMD_SETTEXT, TmpTxt, 0L);
-		if(Lines[1] = new 	Label(this, d, x, y, &TextDef, LB_X_PARENT | LB_Y_PARENT)){
-			Lines[1]->Command(CMD_SETTEXT, txt+cp, 0L);
-			CurrGO = CurrLabel = Lines[1];
-			}
-		nLines = 2;		cli = 1;
-		}
-	Id = GO_MLABEL;
-}
-
-mLabel::mLabel(GraphObj *par, DataObj *d, double x, double y, TextDEF *td, char *txt)
-	:GraphObj(par, d)
-{
-	int i, nll;
-	char **llist;
-
-	memcpy(&TextDef, td, sizeof(TextDEF));
-	TextDef.text = 0L;		fPos.fx = x;		fPos.fy = y;
-	Lines = 0L;				flags = 0L;			Id = GO_MLABEL;
-	fDist.fx = 0.0;			fDist.fy = 0.0;		lspc = 1.0;
-	CurrGO = CurrLabel = 0L;
-	if(txt){
-		if((llist=split(txt,'\n',&nll)) && nll && (Lines=(Label**)calloc(nll, sizeof(Label*)))){
-			for(i = 0; i < nll; i++) {
-				if(llist[i]){
-					Lines[i] = new 	Label(this, d, x, y, &TextDef, LB_X_PARENT | LB_Y_PARENT);
-					Lines[i]->Command(CMD_SETTEXT, llist[i], 0L);
-					free(llist[i]);
-					}
-				}
-			free(llist);	nLines = nll;		cli = 0;
-			}
-		}
-}
-
-mLabel::mLabel(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-mLabel::~mLabel()
-{
-	int i;
-	
-	Undo.InvalidGO(this);
-	if(Lines){
-		for(i = 0; i < nLines; i++) if(Lines[i]) DeleteGO(Lines[i]);
-		free(Lines);
-		}
-}
-
-double
-mLabel::GetSize(int select)
-{
-	switch(select){
-	case SIZE_LB_XPOS:	return cPos1.fx;
-	case SIZE_LB_YPOS:	return cPos1.fy;
-	case SIZE_GRECT_TOP:	
-		if (parent) return parent->GetSize(select);
-		break;
-	case SIZE_MIN_Z:	case SIZE_MAX_Z:	case SIZE_ZPOS:
-		return curr_z;
-	case SIZE_LSPC:
-		return lspc;
-		}
-	return 0.0;
-}
-
-bool
-mLabel::SetSize(int select, double value)
-{
-	int i;
-
-	switch(select & 0xfff) {
-	case SIZE_LB_XDIST:
-		undo_flags = CheckNewFloat(&fDist.fx, fDist.fx, value, this, undo_flags);
-		return true;
-	case SIZE_LB_YDIST:
-		undo_flags = CheckNewFloat(&fDist.fy, fDist.fy, value, this, undo_flags);
-		return true;
-	case SIZE_XPOS:
-		undo_flags = CheckNewFloat(&fPos.fx, fPos.fx, value, this, undo_flags);
-		return true;
-	case SIZE_YPOS:
-		undo_flags = CheckNewFloat(&fPos.fy, fPos.fy, value, this, undo_flags);
-		return true;
-	case SIZE_ZPOS:
-		curr_z = value;
-		if(Lines) for(i = 0; i < nLines; i++) if(Lines[i]){
-			Lines[i]->SetSize(select, value);
-			}
-		return is3D = true;
-	case SIZE_LSPC:
-		undo_flags = CheckNewFloat(&lspc, lspc, value, this, undo_flags);
-		return true;
-		}
-	return false;
-}
-
-void
-mLabel::DoPlot(anyOutput *o)
-{
-	int i;
-	double dh, dx, dy;
-
-	if(!o || !Lines) return;
-	undo_flags = 0L;
-	if(parent){		//if this object is part of a dialog we dont have a parent
-		dx = parent->GetSize(SIZE_GRECT_LEFT);		dy = parent->GetSize(SIZE_GRECT_TOP);
-		}
-	else dx = dy = 0.0;
-	cPos.fx = cPos.fy = 0.0;
-	TextDef.iSize = o->un2iy(TextDef.fSize);
-	switch(flags & 0x03) {
-	case LB_X_DATA:		cPos.fx = o->fx2fix(fPos.fx);				break;
-	case LB_X_PARENT:	if(parent) cPos.fx = parent->GetSize(SIZE_LB_XPOS);	break;
-	default:
-		//if no parent its a dialog
-		cPos.fx = parent ? o->co2fix(fPos.fx + dx) : fPos.fx;
-		break;
-		}
-	switch(flags & 0x30) {
-	case LB_Y_DATA:		cPos.fy = o->fy2fiy(fPos.fy);				break;
-	case LB_Y_PARENT:	if(parent) cPos.fy = parent->GetSize(SIZE_LB_YPOS);	break;
-	default:	
-		//if no parent its a dialog
-		cPos.fy = parent ? o->co2fiy(fPos.fy + dy) : fPos.fy;
-		break;
-		}
-	si = sin(TextDef.RotBL *0.01745329252f);	csi = cos(TextDef.RotBL *0.01745329252f);
-	if(TextDef.Align & TXA_VBOTTOM) dh = (double)(nLines-1);
-	else if(TextDef.Align & TXA_VCENTER) dh = ((double)(nLines-1))/2.0;
-	else dh = 0.0;						dh *= TextDef.fSize;
-	cPos.fx -= o->un2fix(dh*si);		cPos.fy -= o->un2fiy(dh*csi);
-	memcpy(&cPos1, &cPos, sizeof(lfPOINT));
-	if(lspc < 0.5 || lspc > 5) lspc = 1.0;
-#ifdef _WINDOWS
-	dist.fx = floor(o->un2fix(TextDef.fSize * si * lspc));
-	dist.fy = floor(o->un2fiy(TextDef.fSize * csi * lspc));
-#else
-	dist.fx = floor(o->un2fix(TextDef.fSize * si * 1.2 * lspc));
-	dist.fy = floor(o->un2fiy(TextDef.fSize * csi * 1.2 * lspc));
-#endif
-	o->SetTextSpec(&TextDef);
-	rDims.left = rDims.top = rDims.right = rDims.bottom = 0;
-	for(i = 0; i < nLines; i++)	if(Lines[i]){
-		Lines[i]->Command(CMD_SETTEXTDEF, &TextDef, o);
-		Lines[i]->SetSize(SIZE_LB_XDIST, fDist.fx);	Lines[i]->SetSize(SIZE_LB_YDIST, fDist.fy);
-		Lines[i]->SetSize(SIZE_XPOS, fPos.fx);		Lines[i]->SetSize(SIZE_YPOS, fPos.fy);
-		Lines[i]->moveable = moveable;				Lines[i]->DoPlot(o);
-		UpdateMinMaxRect(&rDims, Lines[i]->rDims.left, Lines[i]->rDims.top);
-		UpdateMinMaxRect(&rDims, Lines[i]->rDims.right, Lines[i]->rDims.bottom);
-		}
-	if(CurrLabel && o && Command(CMD_SETFOCUS, CurrLabel, o))
-		Lines[cli]->ShowCursor(o);
-}
-
-bool
-mLabel::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i, j, k;
-	fRECT t_cur;
-	MouseEvent mev;
-	scaleINFO *scale;
-
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:		case CMD_TEXTTHERE:
-		if(Lines && tmpl && IsInRect(&rDims, ((MouseEvent*)tmpl)->x, ((MouseEvent*)tmpl)->y)) 
-			for(i = 0; i<nLines; i++) if(Lines[i] && Lines[i]->Command(cmd, tmpl, o)) return true;
-		break;
-	case CMD_ADDCHAR:
-		if(!tmpl || 13 != *((int*)tmpl)) return false;
-		if(!Lines[cli] || !Lines[cli]->Command(CMD_GETTEXT, &TmpTxt, o)) return false;
-		k = iround(Lines[cli]->GetSize(SIZE_CURSORPOS));
-		if(parent)Undo.ObjConf(this, 0L);
-		if(tmpl && Lines && (Lines = (Label**)realloc(Lines, (nLines+1) * sizeof(Label*)))) {
-			for(i = nLines-1, j = nLines; i >= cli; i--, j-- ) {
-				Lines[j] = Lines[i];
-				}
-			i++, j++;
-			if(Lines[j]) DeleteGO(Lines[j]);	Lines[i] = Lines[j] = 0L;	nLines++;
-			if(Lines[j] = new 	Label(this, data, fPos.fx, fPos.fy, &TextDef, LB_X_PARENT | LB_Y_PARENT)){
-				Lines[j]->Command(CMD_SETTEXT, TmpTxt+k, o);
-				Lines[j]->Command(CMD_POS_FIRST, 0L, o);
-				CurrGO = CurrLabel = Lines[j];
-				TmpTxt[k] = 0;
-				}
-			if(Lines[i] = new 	Label(this, data, fPos.fx, fPos.fy, &TextDef, LB_X_PARENT | LB_Y_PARENT))
-				Lines[i]->Command(CMD_SETTEXT, TmpTxt, 0L);
-			Command(CMD_SETFOCUS, CurrGO, o);
-			if(parent) return parent->Command(CMD_REDRAW, 0L, o);
-			else if(o) DoPlot(o);
-			}
-		break;
-	case CMD_SETFOCUS:
-		for(i = 0; i< nLines; i++) if(Lines[i] == (Label*)tmpl) {
-			cli = i;
-			cPos1.fx = cPos.fx + dist.fx*i;
-			cPos1.fy = cPos.fy + dist.fy*i;
-			return true;
-			}
-		break;
-	case CMD_GETTEXT:
-		if(tmpl && Lines && nLines && Lines[0]) return Lines[0]->Command(CMD_GETTEXT, tmpl, o);
-		break;
-	case CMD_ALLTEXT:							//used e.g from dialog text boxes
-		if(tmpl && Lines && nLines){
-			for(i = j = 0; i < nLines; i++) {
-				if(Lines[i] && Lines[i]->Command(CMD_GETTEXT, TmpTxt+500, o) && TmpTxt[500]){
-					j += rlp_strcpy((char*)tmpl+j, 500-j, TmpTxt+500);
-					((char*)tmpl)[j++] = '\n';
-					}
-				((char*)tmpl)[j] = 0;
-				}
-			if(j >2) return true;
-			}
-		return false;
-	case CMD_DELETE:
-		cli++;
-		//fall through
-	case CMD_BACKSP:
-		if(cli > 0 && cli < nLines && Lines && Lines[cli] && Lines[cli-1]) {
-			Lines[cli-1]->Command(CMD_POS_LAST, 0L, o);
-			TmpTxt[0] = 0;
-			Lines[cli-1]->Command(CMD_GETTEXT, TmpTxt, o);
-			Lines[cli]->Command(CMD_GETTEXT, TmpTxt+strlen(TmpTxt), o);
-			Lines[cli-1]->Command(CMD_SETTEXT, TmpTxt, o);
-			DeleteGO(Lines[cli]);	Lines[cli] = 0L;
-			for(i = cli+1; i < nLines; i++){
-				Lines[i-1] = Lines[i], Lines[i]= 0L;
-				}
-			nLines--;
-			CurrGO = CurrLabel = Lines[cli-1];
-			if(parent) parent->Command(CMD_REDRAW, 0L, o);
-			if(CurrLabel) CurrLabel->RedrawEdit(o);
-			return true;
-			}
-		return false;
-	case CMD_GETTEXTDEF:
-		if(!tmpl) return false;
-		memcpy(tmpl, &TextDef, sizeof(TextDEF));
-		return true;
-	case CMD_SETTEXTDEF:
-		if(!tmpl)return false;
-		if(parent)Undo.TextDef(this, &TextDef, undo_flags);
-		undo_flags |= UNDO_CONTINUE;
-		memcpy(&TextDef, tmpl, sizeof(TextDEF));
-		TextDef.text = 0L;
-		return true;
-	case CMD_SET_DATAOBJ:
-		if(Lines) {
-			for(i = 0; i< nLines; i++) if(Lines[i]) Lines[i]->Command(cmd, tmpl, o);
-			}
-		Id = GO_MLABEL;
-		data = (DataObj*)tmpl;
-		return true;
-	case CMD_AUTOSCALE:
-		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH
-			&& (flags & LB_X_DATA) && (flags & LB_Y_DATA)) {
-			((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
-			return true;
-			}
-		break;
-	case CMD_CURRUP:		case CMD_CURRDOWN:
-		if(!o) return false;
-		o->SetTextSpec(&TextDef);
-		Command(CMD_SETFOCUS, CurrGO, o);
-		if(cli >= 0 && cli < nLines && Lines && Lines[cli]) {
-			t_cur.Xmin = Lines[cli]->GetSize(SIZE_CURSOR_XMIN);
-			t_cur.Xmax = Lines[cli]->GetSize(SIZE_CURSOR_XMAX);
-			t_cur.Ymin = Lines[cli]->GetSize(SIZE_CURSOR_YMIN);
-			t_cur.Ymax = Lines[cli]->GetSize(SIZE_CURSOR_YMAX);
-			mev.StateFlags = 0;		mev.Action = MOUSE_LBUP;
-			mev.x = iround((t_cur.Xmax+t_cur.Xmin)/2.0);
-			mev.y = iround((t_cur.Ymax+t_cur.Ymin)/2.0);
-			i = o->un2ix(TextDef.fSize*si);		j = o->un2iy(TextDef.fSize*csi);
-			if(cmd == CMD_CURRUP && cli > 0 && Lines[cli-1]) {
-				Lines[cli-1]->CalcCursorPos(mev.x -= i, mev.y -= j, o);
-				o->ShowMark(CurrGO = CurrLabel = Lines[cli-=1], MRK_GODRAW);
-				return Command(CMD_SETFOCUS, Lines[cli], o);
-				}
-			if(cmd == CMD_CURRDOWN && cli < (nLines-1) && Lines[cli+1]) {
-				Lines[cli+1]->CalcCursorPos(mev.x += i, mev.y += j, o);
-				o->ShowMark(CurrGO = CurrLabel = Lines[cli+=1], MRK_GODRAW);
-				return Command(CMD_SETFOCUS, Lines[cli], o);
-				}
-			}
-		else return false;
-		break;
-	case CMD_SCALE:
-		scale = (scaleINFO*)tmpl;
-		if((flags & 0x03)!= LB_X_DATA) fPos.fx *= scale->sx.fy;
-		if((flags & 0x30)!= LB_Y_DATA) fPos.fy *= scale->sy.fy;
-		fDist.fx *= scale->sx.fy;				fDist.fy *= scale->sy.fy;
-		TextDef.fSize *= scale->sx.fy;			TextDef.iSize = 0;
-		return true;
-	case CMD_SELECT:
-		if(o && tmpl) for(i = 0; i < nLines; i++){
-			o->SetTextSpec(&TextDef);
-			if(Lines[i] && ((POINT*)tmpl)->y > Lines[i]->rDims.top && ((POINT*)tmpl)->y < 
-				Lines[i]->rDims.bottom) return Lines[i]->Command(cmd, tmpl, o);
-			}
-		break;
-	case CMD_DELOBJ:
-		if(parent && Lines) for(i = 0; i< nLines; i++) 
-			if(Lines[i] && Lines[i] == (Label*)tmpl) return parent->Command(cmd, this, o);
-		break;
-	case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		else if(o) DoPlot(o);
-		break;
-	case CMD_MOVE:
-		if(parent && parent->Id == GO_LEGITEM) return parent->Command(cmd, tmpl, o);
-		Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
-	case CMD_UNDO_MOVE:
-		if(!(flags & 0x03)) fPos.fx += ((lfPOINT*)tmpl)[0].fx;
-		if(!(flags & 0x30)) fPos.fy += ((lfPOINT*)tmpl)[0].fy;
-		if(o && parent){
-			o->StartPage();		parent->DoPlot(o);		o->EndPage();
-			}
-		return true;
-		}
-	return false;
-}
-
-void
-mLabel::Track(POINT *p, anyOutput *o)
-{
-	int i;
-
-	if(!parent) return;
-	if(parent->Id == GO_LEGITEM){
-		parent->Track(p, o);
-		return;
-		}
-	for(i = 0; i < nLines; i++)	if(Lines[i]){
-		if(!i) memcpy(&rDims, &Lines[i]->rDims, sizeof(RECT));
-		else {
-			UpdateMinMaxRect(&rDims, Lines[i]->rDims.left, Lines[i]->rDims.top);
-			UpdateMinMaxRect(&rDims, Lines[i]->rDims.right, Lines[i]->rDims.bottom);
-			}
-		}
-	o->UpdateRect(&rDims, false);
-	Id = 0L;			//disable reentrance from Label objects
-	for(i = 0; i < nLines; i++)	if(Lines[i]) Lines[i]->Track(p, o);
-	Id = GO_MLABEL;		//enable entrance from Label objects
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// a rectangular range to accept any text
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-TextFrame::TextFrame(GraphObj *parent, DataObj *data, lfPOINT *p1, lfPOINT *p2, char *txt)
-	:GraphObj(parent, data)
-{
-	FileIO(INIT_VARS);					lspc = 1.0;
-	pos1.fx = p1->fx;	pos1.fy = p1->fy;	pos2.fx = p2->fx;	pos2.fy = p2->fy;
-	if(txt && txt[0]) {
-		text = (unsigned char*)memdup(txt, (int)strlen(txt)+1, 0);
-		}
-	else if(lines = (unsigned char**)malloc(sizeof(char*))){
-		if(lines[0] = (unsigned char*)malloc(TF_MAXLINE))lines[0][0] = 0;
-		nlines = 1;
-		}
-	Id = GO_TEXTFRAME;
-}
-
-TextFrame::TextFrame(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	moveable = 1;				bModified = false;
-}
-
-TextFrame::~TextFrame()
-{
-	int i;
-
-	if(text)free(text);			text = 0L;
-	if(drc) delete(drc);		drc = 0L;
-	if(tm_rec) free(tm_rec);	tm_rec = 0L;
-	if(lines) {
-		for(i = 0; i < nlines; i++)	if(lines[i]) free(lines[i]);
-		free(lines);			lines = 0L;
-		}
-	HideTextCursor();
-}
-double
-TextFrame::GetSize(int select)
-{
-	switch(select) {
-	case SIZE_XPOS:		return pos1.fx;
-	case SIZE_XPOS+1:	return pos2.fx;
-	case SIZE_YPOS:		return pos1.fy;
-	case SIZE_YPOS+1:	return pos2.fy;
-	case SIZE_GRECT_LEFT:	case SIZE_GRECT_TOP:
-	case SIZE_GRECT_RIGHT:	case SIZE_GRECT_BOTTOM:
-		if(parent) return parent->GetSize(select);
-		break;
-		}
-	return 0.0;
-}
-
-bool
-TextFrame::SetSize(int select, double value)
-{
-	switch(select & 0xfff) {
-	case SIZE_XPOS:				pos1.fx = value;			return bResize = true;
-	case SIZE_XPOS+1:			pos2.fx = value;			return bResize = true;
-	case SIZE_YPOS:				pos1.fy = value;			return bResize = true;
-	case SIZE_YPOS+1:			pos2.fy = value;			return bResize = true;
-		}
-	return false;
-}
-
-void 
-TextFrame::DoMark(anyOutput *o, bool mark)
-{
-	RECT upd;
-
-	if(has_m1 && has_m2) TextMark(o, 3);
-	has_m1 = has_m2 = false;
-	if(!drc) drc = new dragRect(this, 0);
-	memcpy(&upd, &rDims, sizeof(RECT));
-	if(mark){
-		if(drc) drc->DoPlot(o);								ShowCursor(o);
-		}
-	else if(parent)	parent->DoPlot(o);
-	IncrementMinMaxRect(&upd, 6);
-	o->UpdateRect(&upd, false);
-}
-
-void
-TextFrame::DoPlot(anyOutput *o)
-{
-	int i, j, x1, y1, x2, y2;
-	double tmp, dx, dy;
-
-	if(!o) return;
-	if(parent){		//if this object is part of a dialog we dont have a parent
-		dx = parent->GetSize(SIZE_GRECT_LEFT);			dy = parent->GetSize(SIZE_GRECT_TOP);
-		x1 = o->co2ix(pos1.fx+dx);						y1 = o->co2iy(pos1.fy+dy);
-		x2 = o->co2ix(pos2.fx+dx);						y2 = o->co2iy(pos2.fy+dy);
-		ipad.left = o->un2ix(pad.Xmin);					ipad.right = o->un2ix(pad.Xmax);
-		ipad.top = o->un2iy(pad.Ymin);					ipad.bottom = o->un2iy(pad.Ymax);
-		}
-	else {
-		dx = dy = 0.0;
-		x1 = (int)pos1.fx;		y1 = (int)pos1.fy;		x2 = (int)pos2.fx;		y2 = (int)pos2.fy;
-		ipad.left = ipad.right = ipad.top = ipad.bottom = 4;
-		fmt_txt.EditMode(true);
-		}
-	if(pos1.fx > pos2.fx) {
-		tmp = pos2.fx;	pos2.fx = pos1.fx;	pos1.fx = tmp;
-		}
-	if(pos1.fy > pos2.fy) {
-		tmp = pos2.fy;	pos2.fy = pos1.fy;	pos1.fy = tmp;
-		}
-	o->SetLine(&Line);						o->SetFill(&Fill);
-	o->oRectangle(x1, y1, x2, y2, name);
-	SetMinMaxRect(&rDims, x1, y1, x2, y2);
-	x1 += ipad.left;						y1 += ipad.top;
-	TextDef.iSize = o->un2iy(TextDef.fSize);
-#ifdef _WINDOWS
-	linc = o->un2iy(TextDef.fSize*lspc);
-#else
-	linc = o->un2iy(TextDef.fSize*lspc*1.2);
-#endif
-	o->SetTextSpec(&TextDef);				y1 += linc;
-	if(text && text[0] && !(lines)) text2lines(o);
-	else if(bResize && lines) {
-		c_char = lines[cur_pos.y][cur_pos.x];			lines[cur_pos.y][cur_pos.x] = 0x01;
-		if(!c_char) lines[cur_pos.y][cur_pos.x+1] = 0x00; 
-		lines2text();				text2lines(o);		bResize = false;
-		o->ShowMark(this, MRK_GODRAW);
-		return;
-		}
-	if(has_m1 && has_m2) TextMark(o, 1);
-	for(i = 0; i < nlines; i++) {
-		if(lines[i] && lines[i][0]){
-			j = (int)strlen((char*)lines[i]);
-			if(lines[i][j-1] == '\n') {
-				lines[i][j-1] = 0;
-				fmt_txt.SetText(o, (char*)lines[i], &x1, &y1);
-				lines[i][j-1] = '\n';
-				}
-			else fmt_txt.SetText(o, (char*)lines[i], &x1, &y1);
-			}
-		y1 += linc;
-		}
-	if(has_m1 && has_m2) TextMark(o, 2);
-	bModified = bResize = false;
-}
-
-bool
-TextFrame::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	int i;
-
-	switch (cmd) {
-	case CMD_SELECT:
-		if(!o || !tmpl) return false;
-		CalcCursorPos(((POINT*)tmpl)->x, ((POINT*)tmpl)->y, o);
-		if(parent)o->ShowMark(this, MRK_GODRAW);	ShowCursor(o);
-		return true;
-	case CMD_COPY:
-		return CopyText(o, false);
-	case CMD_SAVEPOS:
-		bModified = true;
-		Undo.SaveLFP(this, &pos1, 0L);
-		Undo.SaveLFP(this, &pos2, UNDO_CONTINUE);
-		return true;
-	case CMD_SCALE:
-		if(tmpl) {
-			pos1.fx = ((scaleINFO*)tmpl)->sx.fx + pos1.fx * ((scaleINFO*)tmpl)->sx.fy;
-			pos1.fy = ((scaleINFO*)tmpl)->sy.fx + pos1.fy * ((scaleINFO*)tmpl)->sy.fy;
-			pos2.fx = ((scaleINFO*)tmpl)->sx.fx + pos2.fx * ((scaleINFO*)tmpl)->sx.fy;
-			pos2.fy = ((scaleINFO*)tmpl)->sy.fx + pos2.fy * ((scaleINFO*)tmpl)->sy.fy;
-			TextDef.fSize *= ((scaleINFO*)tmpl)->sy.fy;		TextDef.iSize = 0;
-			pad.Xmax *= ((scaleINFO*)tmpl)->sx.fy;			pad.Xmin *= ((scaleINFO*)tmpl)->sx.fy;
-			pad.Ymax *= ((scaleINFO*)tmpl)->sy.fy;			pad.Ymin *= ((scaleINFO*)tmpl)->sy.fy;
-			Line.width *= ((scaleINFO*)tmpl)->sy.fy;		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-			FillLine.width *= ((scaleINFO*)tmpl)->sy.fy;	FillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
-			Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;
-			}
-		return true;
-	case CMD_GETTEXT:	case CMD_ALLTEXT:
-		if(lines && lines[0] && lines[0][0] && nlines) {
-			lines2text();	i = rlp_strcpy((char*)tmpl, TMP_TXT_SIZE-2, (char*)text);
-			while(i && ((char*)tmpl)[i-1] == '\n') i--; 
-			((char*)tmpl)[i++] = '\n';						((char*)tmpl)[i] = 0;
-			return true;
-			}
-		return false;
-	case CMD_ADDCHARW:	case CMD_ADDCHAR:
-		if(tmpl && o) AddChar(o, *((int *)tmpl));
-		return true;
-	case CMD_DELETE:
-		if(o) DelChar(o);
-		return true;
-	case CMD_SHIFTLEFT:
-		if(!has_m1) {
-			m1_pos.x = cur_pos.x;		m1_pos.y = cur_pos.y;
-			}
-		has_m1 = has_m2 = true;
-	case CMD_CURRLEFT:
-		if(!(lines[cur_pos.y]))return false;
-		if(cmd == CMD_CURRLEFT && has_m1 && has_m2) TextMark(o, 3);
-		if(cur_pos.x > 0) {
-			i = 0;					fmt_txt.SetText(0L,(char*)lines[cur_pos.y], &i, &i);
-			i = cur_pos.x;			fmt_txt.cur_left(&i);
-			cur_pos.x = i;
-			}
-		else if(cur_pos.y) {
-			cur_pos.y--;			cur_pos.x = (int)strlen((char*)lines[cur_pos.y]);
-			}
-		if(cmd == CMD_SHIFTLEFT){
-			m2_pos.x = cur_pos.x;		m2_pos.y = cur_pos.y;
-			DoPlot(o);					o->UpdateRect(&rDims, false);
-			}
-		ShowCursor(o);
-		return false;
-	case CMD_SHIFTRIGHT:
-		if(!has_m1) {
-			m1_pos.x = cur_pos.x;		m1_pos.y = cur_pos.y;
-			}
-		has_m1 = has_m2 = true;
-	case CMD_CURRIGHT:
-		if(!(lines[cur_pos.y]))return false;
-		if(cmd == CMD_CURRIGHT && has_m1 && has_m2) TextMark(o, 3);
-		if(cur_pos.x >= (int)strlen((char*)lines[cur_pos.y])) {
-			if(cur_pos.y < (nlines-1)) {
-				cur_pos.y++;		cur_pos.x = 0;
-				}
-			else if(cur_pos.y == (nlines-1) && lines[cur_pos.y][0]) {
-				if(!(lines = (unsigned char**)realloc(lines, (nlines+1)*sizeof(char*))))return false;
-				if(!(lines[cur_pos.y+1] = (unsigned char*)malloc(TF_MAXLINE))) return false;
-				cur_pos.y++;		cur_pos.x = 0;		nlines++;
-				lines[cur_pos.y][0] = 0;
-				}
-			}
-		else {
-			i = 0;						fmt_txt.SetText(0L,(char*)lines[cur_pos.y], &i, &i);
-			i = cur_pos.x;				fmt_txt.cur_right(&i);
-			cur_pos.x = i;
-			}
-		if(cmd == CMD_SHIFTRIGHT){
-			m2_pos.x = cur_pos.x;		m2_pos.y = cur_pos.y;
-			DoPlot(o);					o->UpdateRect(&rDims, false);
-			}
-		ShowCursor(o);
-		return false;
-	case CMD_SHIFTUP:
-		if(!has_m1) {
-			m1_pos.x = cur_pos.x;		m1_pos.y = cur_pos.y;
-			}
-		has_m1 = has_m2 = true;
-	case CMD_CURRUP:
-		if(cmd == CMD_CURRUP && has_m1 && has_m2) TextMark(o, 3);
-		if(cur_pos.y && o) {
-			i = ((Cursor.bottom + Cursor.top)>>1)-linc;
-			CalcCursorPos(Cursor.left, i, o);
-			if(cmd == CMD_SHIFTUP){
-				m2_pos.x = cur_pos.x;	m2_pos.y = cur_pos.y;
-				DoPlot(o);				o->UpdateRect(&rDims, false);
-				}
-			ShowCursor(o);
-			return true;
-			}
-		return false;
-	case CMD_SHIFTDOWN:
-		if(!has_m1) {
-			m1_pos.x = cur_pos.x;		m1_pos.y = cur_pos.y;
-			}
-		has_m1 = has_m2 = true;
-	case CMD_CURRDOWN:
-		if(cmd == CMD_CURRDOWN && has_m1 && has_m2) TextMark(o, 3);
-		if(cur_pos.y < (nlines-1) && o && lines[cur_pos.y][0]) {
-			i = ((Cursor.bottom + Cursor.top)>>1)+linc;
-			if(i >= (rDims.bottom-ipad.bottom)) return false;
-			CalcCursorPos(Cursor.left, i, o);
-			if(cmd == CMD_SHIFTDOWN){
-				m2_pos.x = cur_pos.x;	m2_pos.y = cur_pos.y;
-				DoPlot(o);				o->UpdateRect(&rDims, false);
-				}
-			ShowCursor(o);
-			return true;
-			}
-		return false;
-	case CMD_POS_FIRST:
-		if(has_m1 && has_m2) TextMark(o, 3);
-		cur_pos.x = 0;									ShowCursor(o);
-		return true;
-	case CMD_POS_LAST:
-		if(has_m1 && has_m2) TextMark(o, 3);
-		cur_pos.x = (int)strlen((char*)lines[cur_pos.y]);
-		ShowCursor(o);
-		return true;
-	case CMD_TEXTTHERE:
-		if(IsInRect(&rDims, ((MouseEvent *)tmpl)->x, ((MouseEvent *)tmpl)->y)) return true;
-		return false;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, mev->x, mev->y) && (!(CurrGO) || !has_m2) && o){
-				CalcCursorPos(mev->x, mev->y, o);
-				if(has_m1 && has_m2) {
-					if(o)DoPlot(o);						o->UpdateRect(&rDims, false);
-					CurrGO = this;						ShowCursor(o);
-					return true;
-					}
-				return o->ShowMark(this, MRK_GODRAW);
-				}
-			else if(CurrGO == this && o){
-				ShowCursor(o);
-				return IsInRect(&rDims, mev->x, mev->y);
-				}
-			break;
-		case MOUSE_LBDOWN:
-			if(has_m1 && has_m2) {
-				has_m1 = has_m2 = false;
-				if(o)DoPlot(o);							o->UpdateRect(&rDims, false);
-				}
-			has_m1 = has_m2 = false;
-		case MOUSE_MOVE:
-			if(!(mev->StateFlags & 0x1)) return false;
-			if(!IsInRect(&rDims, mev->x, mev->y))return false;
-			if(!CurrGO) CurrGO = this;
-			CalcCursorPos(mev->x, mev->y, o);
-			if(!has_m1) {
-				m1_pos.x = m2_pos.x = cur_pos.x;		m1_pos.y = m2_pos.y = cur_pos.y;
-				return has_m1 = true;
-				}
-			else if(cur_pos.x != m2_pos.x || cur_pos.y != m2_pos.y) {
-				m2_pos.x = cur_pos.x;					m2_pos.y = cur_pos.y;
-				has_m2 = true;
-				if(o)DoPlot(o);							o->UpdateRect(&rDims, false);
-				return true;
-				}
-			return true;
-			}
-		return false;
-	case CMD_SET_DATAOBJ:
-		Id = GO_TEXTFRAME;
-		return true;
-	case CMD_PASTE:
-		return DoPaste(o);
-	case CMD_MOVE:
-		bModified = true;
-		Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
-	case CMD_UNDO_MOVE:
-		pos1.fx += ((lfPOINT*)tmpl)[0].fx;	pos1.fy += ((lfPOINT*)tmpl)[0].fy;
-		pos2.fx += ((lfPOINT*)tmpl)[0].fx;	pos2.fy += ((lfPOINT*)tmpl)[0].fy;
-		CurrGO = this;
-	case CMD_REDRAW:
-		if(parent){
-			if(o && cmd == CMD_REDRAW) DoPlot(o);		//trickle down ?
-			if(!o && cmd == CMD_REDRAW) {
-				//coming from Undo
-				bModified = true;
-				if(lines) {
-					for(i = 0; i < nlines; i++) if(lines[i]) free(lines[i]);
-					free(lines);			lines = 0L;
-					}
-				HideTextCursor();			cur_pos.x = cur_pos.y = 0;
-				parent->Command(CMD_REDRAW, tmpl, o);
-				}
-			else parent->Command(CMD_REDRAW, tmpl, o);
-			}
-		return true;
-	case CMD_SETSCROLL:
-		if(parent) return parent->Command(cmd, tmpl, o);
-	case CMD_GETTEXTDEF:
-		if(!tmpl) return false;
-		memcpy(tmpl, &TextDef, sizeof(TextDEF));
-		return true;
-	case CMD_SETTEXTDEF:
-		if(!tmpl)return false;
-		memcpy(&TextDef, tmpl, sizeof(TextDEF));
-		TextDef.text = 0L;
-		return true;
-	case CMD_SETTEXT:
-		if(lines) {
-			for(i = 0; i < nlines; i++) if(lines[i]) free(lines[i]);
-			free(lines);			lines = 0L;
-			}
-		if(text) free(text);		text = 0L;
-		if(tmpl && *((char*)tmpl)) text = (unsigned char*)memdup(tmpl, (int)strlen(((char*)tmpl))+1, 0);
-		return true;
-		}
-	return false;
-}
-
-void
-TextFrame::Track(POINT *p, anyOutput *o)
-{
-	POINT tpts[5];
-	RECT old_rc;
-	double dx, dy;
-
-	if(o && parent){
-		dx = parent->GetSize(SIZE_GRECT_LEFT);		dy = parent->GetSize(SIZE_GRECT_TOP);
-		memcpy(&old_rc, &rDims, sizeof(rDims));
-		o->UpdateRect(&rDims, false);
-		tpts[0].x = tpts[1].x = tpts[4].x = o->co2ix(pos1.fx+dx)+p->x;		
-		tpts[0].y = tpts[3].y = tpts[4].y = o->co2iy(pos1.fy+dy)+p->y;
-		tpts[1].y = tpts[2].y = o->co2iy(pos2.fy+dy)+p->y;
-		tpts[2].x = tpts[3].x = o->co2ix(pos2.fx+dx)+p->x;
-		UpdateMinMaxRect(&rDims, tpts[0].x, tpts[0].y);
-		UpdateMinMaxRect(&rDims, tpts[2].x, tpts[2].y);	
-		if(old_rc.left != rDims.left || old_rc.right != rDims.right || old_rc.top !=
-			rDims.top || old_rc.bottom != rDims.bottom)IncrementMinMaxRect(&rDims, 3);
-		o->ShowLine(tpts, 5, Line.color);
-		}
-}
-
-void *
-TextFrame::ObjThere(int x, int y)
-{
-	if(drc) return drc->ObjThere(x, y);
-	return 0L;
-}
-
-void
-TextFrame::text2lines(anyOutput *o)
-{
-	int i, j, w, h, cl, maxlines, maxw;
-	char tmp_line[TF_MAXLINE];
-	bool hasMark = false;
-
-	if(lines) {
-		for(i = 0; i < nlines; i++) if(lines[i]) free(lines[i]);
-		free(lines);			lines = 0L;
-		}
-	has_m1 = has_m2 = false;
-	nlines = 0;
-	if(!text || !text[0]) return;
-	maxlines = (rDims.bottom -rDims.top)/linc +1;
-	maxw = rDims.right - rDims.left - ipad.left - ipad.right;
-	lines = (unsigned char**)calloc(maxlines, sizeof(char*));
-	for(cl = cpos = w = h = 0; cl < maxlines && text[cpos]; cl++) {
-		if(!(lines[cl] = (unsigned char*)malloc(TF_MAXLINE))) return;
-		for(i = 0; text[cpos] && i < TF_MAXLINE; i++) {
-			tmp_line[i] = text[cpos++];				tmp_line[i+1] = 0;
-			fmt_txt.SetText(0L, tmp_line, &w, &h);
-			if(!(fmt_txt.oGetTextExtent(o, &w, &h, i))) break;
-			if(tmp_line[i] == '\n'){
-				break;													//new line character found
-				}
-			if(tmp_line[i] < ' ') switch(tmp_line[i]) {
-				case 0x01:
-					if(c_char == 0 && text[cpos]) c_char = text[cpos++];	//cursor at end of line
-					hasMark = true;				break;
-				case 0x02:	case 0x03:
-					hasMark = true;				break;
-				}
-			else if(w >= maxw){
-				for(j = i; j > (i>>1); j--) {
-					if(tmp_line[j] == ' ' || tmp_line[j] == '-' || tmp_line[j] < ' ') break;
-					}
-				if(j == (i>>1)) {
-					cpos--;							tmp_line[i] = 0;
-					}
-				else {
-					for(tmp_line[j+1] = 0; j < i; j++, cpos--);
-					}
-				break;
-				}
-			}
-		if(i || tmp_line[i] == '\n') rlp_strcpy((char*)lines[cl], TF_MAXLINE, tmp_line);
-		else lines[cl][0] = 0;
-		}
-	nlines = cl;
-	if(hasMark)procTokens();
-}
-
-void
-TextFrame::lines2text()
-{
-	int i;
-
-	if(text) free(text);		cpos = 0;
-	text = (unsigned char*)malloc(csize = 1000);
-	for(i = 0; i < nlines; i++) {
-		if(lines[i] && lines[i][0]) add_to_buff((char**)&text, &cpos, &csize, (char*)lines[i], 0);
-		}
-}
-
-void
-TextFrame::AddChar(anyOutput *o, int c)
-{
-	int i, j, h, w, maxw;
-	bool brd;
-	char *txt1;
-
-	if(cur_pos.y >= nlines) return;
-	if(!lines || !lines[cur_pos.y]) return;
-	if(c == '\r') c = '\n';
-	if(has_m1 && has_m2){
-		TmpTxt[0] = c;		TmpTxt[1] = 0;
-		if(c == 8) ReplMark(o, "");
-		else if(c >= 32 || c == '\n') ReplMark(o, TmpTxt);
-		return;
-		}
-	else if(!text) {
-		lines2text();
-		Undo.TextBuffer(this, &csize, &cpos, &text, 0L, o);
-		}
-	maxw = rDims.right - rDims.left - ipad.left - ipad.right;
-	i = j = (int)strlen((char*)lines[cur_pos.y])+1;
-	has_m1 = has_m2 = false;
-	if(c >= 32 || c == '\n') {
-		if(c > 254 && (txt1 = (char*)malloc(10))) {
-#ifdef USE_WIN_SECURE
-			w = sprintf_s(txt1, 10, "&#%d;", c);
-#else
-			w = sprintf(txt1, "&#%d;", c);
-#endif
-			for(j = j+w; j>0; j--) {
-				lines[cur_pos.y][j] = lines[cur_pos.y][j-w];
-				if((j-w) == cur_pos.x){
-					for(i = 0; i < w; i++) lines[cur_pos.y][j-w+i] = txt1[i];
-					j = 0;					cur_pos.x += w;
-					}
-				}
-			free(txt1);
-			}
-		else while(j) {
-			lines[cur_pos.y][j] = lines[cur_pos.y][j-1];	j--;
-			if(j == cur_pos.x){
-				lines[cur_pos.y][j] = c;					j = 0;
-				cur_pos.x++;
-				}
-			}
-		fmt_txt.SetText(0L, (char*)lines[cur_pos.y], &j, &j);
-		fmt_txt.oGetTextExtent(o, &w, &h, i);				brd = false;
-		if(cur_pos.x > 2 && (c == '>' || c == ';')) {
-			if(c == '>' && fmt_txt.leftTag((char*)lines[cur_pos.y],cur_pos.x-1) >= 0) brd =true;
-			if(c == ';' && fmt_txt.ucLeft((char*)lines[cur_pos.y],cur_pos.x-1, 0L, 0L) > 0) brd =true;
-			}
-		if(brd || w >= maxw || c == '\n' || (c == ' ' && w >=(maxw>>1))) {
-			c_char = lines[cur_pos.y][cur_pos.x];			lines[cur_pos.y][cur_pos.x] = 0x01;
-			if(!c_char) lines[cur_pos.y][cur_pos.x+1] = 0x00; 
-			lines2text();	Undo.TextBuffer(this, &csize, &cpos, &text, 0L, o);		text2lines(o);
-			}
-		}
-	else if(c == 8 && (cur_pos.x || cur_pos.y)) {			//Backspace
-		if(!cur_pos.x) Command(CMD_CURRLEFT, 0L, o);
-		Command(CMD_CURRLEFT, 0L, o);						DelChar(o);
-		return;
-		}
-	else return;
-	DoPlot(o);		o->UpdateRect(&rDims, false);			ShowCursor(o);
-}
-
-void
-TextFrame::DelChar(anyOutput *o)
-{
-	int i, cb, x;
-
-	if(has_m1 && has_m2){
-		ReplMark(o, "");	return;
-		}
-	if(lines[cur_pos.y][cur_pos.x]) {
-		lines2text();			Undo.TextBuffer(this, &csize, &cpos, &text, 0L, o);
-		cb = x = cur_pos.x;			fmt_txt.cur_right(&x);
-		cb = x - cb;	if(cb < 1) cb = 1;
-		for(i = cur_pos.x; lines[cur_pos.y][i]; i++) {
-			if(!(lines[cur_pos.y][i] = lines[cur_pos.y][i+cb])) break;
-			}
-		c_char = lines[cur_pos.y][cur_pos.x];			lines[cur_pos.y][cur_pos.x] = 0x01;
-		if(!c_char) lines[cur_pos.y][cur_pos.x+1] = 0x00; 
-		lines2text();			text2lines(o);
-		DoPlot(o);				o->UpdateRect(&rDims, false);
-		ShowCursor(o);			return;
-		}
-	else if(cur_pos.y < (nlines-1)){
-		cur_pos.y++;			cur_pos.x = 0;
-		DelChar(o);				return;
-		}
-}
-
-void
-TextFrame::ReplMark(anyOutput *o, char *ntext)
-{
-	int i, j;
-
-	if(!has_m1 || !has_m2 || !o || !ntext) return;
-	lines2text();
-	Undo.TextBuffer(this, &csize, &cpos, &text, 0L, o);
-	for(i = cpos = 0; i < nlines && i < m1_cpos.y; i++) {
-		if(lines[i] && lines[i][0]) add_to_buff((char**)&text, &cpos, &csize, (char*)lines[i], 0);
-		}
-	if(m1_cpos.x)add_to_buff((char**)&text, &cpos, &csize, (char*)lines[i], m1_cpos.x);
-	add_to_buff((char**)&text, &cpos, &csize, ntext, 0);			j = cpos;
-	if(m1_cpos.y == m2_cpos.y)add_to_buff((char**)&text, &cpos, &csize, (char*)(lines[i]+m2_cpos.x), 0);
-	for( ; i < nlines && i < m2_cpos.y; i++);
-	if(i == m2_cpos.y && m2_cpos.y > m1_cpos.y)add_to_buff((char**)&text, &cpos, &csize, (char*)(lines[i]+m2_cpos.x), 0);
-	for(i++; i < nlines; i++) {
-		if(lines[i] && lines[i][0]) add_to_buff((char**)&text, &cpos, &csize, (char*)lines[i], 0);
-		}
-	if(tm_rec)free(tm_rec);			tm_rec = 0L;		has_m1 = has_m2 = false;
-	if(text[j]) {
-		c_char = text[j];						text[j] = 0x01;
-		}
-	else if(i < (nlines-1) && lines[i+1] && lines[i+1][0]) {
-		c_char = lines[i+1][0];					lines[i+1][0] = 0x01;
-		}
-	else if(j == cpos){
-		c_char = 0;		text[cpos++] = 0x01;		text[cpos] = 0;
-		}
-	else cur_pos.x = cur_pos.y = 0;
-	text2lines(o);		DoPlot(o);	o->UpdateRect(&rDims, false);
-	ShowCursor(o);
-}
-
-void
-TextFrame::procTokens()
-{
-	int i, j;
-
-	for(i = 0; i < nlines; i++) {
-		if(lines[i] && lines[i][0]){
-			for(j = 0; lines[i][j]; j++) switch(lines[i][j]) {
-				case 0x01:
-					cur_pos.y = i;				cur_pos.x = j;
-					lines[i][j] = c_char;		c_char = '?';
-					break;
-				case 0x02:
-					m1_pos.y = i;				m1_pos.x = j;
-					if(m1_char == 0x01) {
-						lines[i][j] = c_char;	c_char = '?';
-						cur_pos.y = i;			cur_pos.x = j;
-						}
-					else lines[i][j] = m1_char;
-					has_m1 = true;				m1_char = '?';
-					break;
-				case 0x03:
-					m2_pos.y = i;				m2_pos.x = j;
-					if(m2_char == 0x01) {
-						lines[i][j] = c_char;	c_char = '?';
-						cur_pos.y = i;			cur_pos.x = j;
-						}
-					else lines[i][j] = m2_char;
-					has_m2 = true;				m2_char = '?';
-					break;
-					}
-			}
-		}
-}
-
-bool
-TextFrame::DoPaste(anyOutput *o)
-{
-	int i, j, k;
-	char *ntxt;
-	unsigned char *ptxt;
-
-	if((ptxt = PasteText()) && ptxt[0]) {
-		lines2text();	Undo.TextBuffer(this, &csize, &cpos, &text, 0L, o);
-		if(!(ntxt = (char*)malloc(strlen((char*)ptxt)+1))) return false;
-		for(i = j = cpos = 0; ptxt[i]; i++) {
-			if(ptxt[i] >= ' ' || ptxt[i] == '\n') ntxt[j++] = ptxt[i];
-			else if(ptxt[i] == 9)ntxt[j++] = ' ';		//convert tab->space 
-			}
-		ntxt[j] = 0;
-		if(!ntxt[0]) {
-			free(ntxt);				return false;
-			}
-		if(has_m1 && has_m2) {
-			ReplMark(o, ntxt);		free(ntxt);
-			return true;
-			}
-		m1_char = ntxt[0];			ntxt[0] = 0x02;
-		ntxt[j] = 0;				if(text) free(text);
-		if(!(text = (unsigned char*)malloc(csize = 1000)))return false;
-		for(i = k = 0, text[0] = 0; i < nlines; i++) {
-			if(lines[i] && lines[i][0]){
-				if(i == cur_pos.y) {
-					if(cur_pos.x) add_to_buff((char**)&text, &cpos, &csize, (char*)lines[i], cur_pos.x);
-					add_to_buff((char**)&text, &cpos, &csize, ntxt, 0);	k = cpos;
-					add_to_buff((char**)&text, &cpos, &csize, (char*)lines[i]+cur_pos.x, 0);
-					free(ntxt);		ntxt = 0L;
-					}
-				else add_to_buff((char**)&text, &cpos, &csize, (char*)lines[i], 0);
-				}
-			}
-		if(ntxt) {
-			add_to_buff((char**)&text, &cpos, &csize, ntxt, 0);			k = cpos;
-			}
-		m2_char = 0x01;
-		if(text[k]) {
-			c_char = text[k];						text[k] = 0x03;
-			}
-		else if(i < (nlines-1) && lines[i+1] && lines[i+1][0]) {
-			c_char = lines[i+1][0];					lines[i+1][0] = 0x03;
-			}
-		else if(k == cpos){
-			c_char = 0;		text[cpos++] = 0x03;	text[cpos] = 0;
-			}
-		text2lines(o);						DoPlot(o);
-		o->UpdateRect(&rDims, false);
-		ShowCursor(o);						if(ntxt) free(ntxt);
-		}
-	return false;
-}
-void
-TextFrame::TextMark(anyOutput *o, int mode)
-{
-	int i, j, w, h;
-	LineDEF ld;
-	FillDEF fd;
-
-	if(m1_pos.y > m2_pos.y) {
-		m1_cpos.y = m2_pos.y;				m1_cpos.x = m2_pos.x;
-		m2_cpos.y = m1_pos.y;				m2_cpos.x = m1_pos.x;
-		}
-	else if(m1_pos.y == m2_pos.y  && m1_pos.x > m2_pos.x) {
-		m1_cpos.x = m2_pos.x;				m2_cpos.x = m1_pos.x;
-		m1_cpos.y = m2_cpos.y = m1_pos.y;
-		}
-	else {
-		m1_cpos.y = m1_pos.y;				m1_cpos.x = m1_pos.x;
-		m2_cpos.y = m2_pos.y;				m2_cpos.x = m2_pos.x;
-		}
-	if(!has_m1 || !has_m2 || !o) return;
-	if(m1_pos.y == m2_pos.y && m1_pos.x == m2_pos.x) return;
-	if(mode == 1){							//create background mark
-		if(tm_rec)free(tm_rec);				tm_rec = 0L;
-		if((tm_c = m2_cpos.y - m1_cpos.y +1)<1) return;
-		if(!(tm_rec = (RECT*)malloc(tm_c * sizeof(RECT))))return;
-		for(i = w = 0, j = m1_cpos.y; j <= m2_cpos.y; i++, j++) {
-			h = TextDef.iSize;
-			if(j == m1_cpos.y) {
-				fmt_txt.SetText(0L, (char*)lines[j], 0L, 0L);
-				if(m1_cpos.x) fmt_txt.oGetTextExtent(o, &w, &h, m1_cpos.x);
-				else w = 0;
-				tm_rec[0].left = w + rDims.left + ipad.left + 1;			
-				fmt_txt.SetText(0L, (char*)(lines[j]+m1_cpos.x), 0L, 0L);
-				fmt_txt.oGetTextExtent(o, &w, &h, 0);
-				tm_rec[0].right = tm_rec[0].left + w;
-				}
-			if(j == m2_cpos.y) {
-				fmt_txt.SetText(0L, (char*)lines[j], 0L, 0L);
-				if(m2_cpos.x) fmt_txt.oGetTextExtent(o, &w, &h, m2_cpos.x);
-				else w = 0;
-				tm_rec[i].right = w + rDims.left + ipad.left - 1;			
-				if(m2_cpos.y > m1_cpos.y) {
-					tm_rec[i].left = rDims.left + ipad.left;
-					}
-				}
-			else if(j < m2_cpos.y && j > m1_cpos.y) {
-				tm_rec[i].left = rDims.left + ipad.left;
-				tm_rec[i].top = j * linc + rDims.top + ipad.top;
-				fmt_txt.SetText(0L, (char*)lines[j], 0L, 0L);
-				fmt_txt.oGetTextExtent(o, &w, &h, 0);
-				tm_rec[i].right = w + rDims.left + ipad.left;			
-				}
-			tm_rec[i].top = j * linc + rDims.top + ipad.top;
-			tm_rec[i].bottom = tm_rec[i].top + linc;
-			}
-		ld.color = 0x0000ffff;				ld.patlength = 1.0;
-		ld.pattern = 0x0L;					ld.width = 0;
-		fd.color = fd.color2 = ld.color;	fd.hatch = 0L;
-		fd.scale = 1.0;						fd.type = 0;
-		o->SetLine(&ld);					o->SetFill(&fd);
-		for(i = 0; i < tm_c; i++) {
-			o->oRectangle(tm_rec[i].left, tm_rec[i].top, tm_rec[i].right, tm_rec[i].bottom, 0L);
-			}
-		}
-	if(mode == 2){							//invert rectangles
-		if(tm_rec) for(i = 0; i < tm_c; i++) {
-			o->CopyBitmap(tm_rec[i].left, tm_rec[i].top, o, tm_rec[i].left, tm_rec[i].top,
-				tm_rec[i].right - tm_rec[i].left, tm_rec[i].bottom - tm_rec[i].top, true);
-			}
-		}
-	if(mode == 3){							//clear mark
-		if(tm_rec)free(tm_rec);			tm_rec = 0L;
-		tm_c = 0;						has_m1 = has_m2 = false;
-		DoPlot(o);						o->UpdateRect(&rDims, false);
-		}
-}
-
-bool
-TextFrame::CopyText(anyOutput *o, bool b_cut)
-{
-	int i, csize, pos = 0;
-	char *ntxt;
-
-	if(!lines || !lines[0][0] || !o) return false;
-	if(!has_m1 || !has_m2) {
-		m1_pos.x = m1_pos.y = 0;		m2_pos.y = nlines-1;
-		if(lines[nlines-1]) m2_pos.x = (int)strlen((char*)lines[nlines-1]);
-		else m2_pos.x = 0;				has_m1 = has_m2 = true;
-		DoPlot(o);						o->UpdateRect(&rDims, false);
-		return CopyText(o, false);
-		}
-	if(!(ntxt = (char*)malloc(csize = 1000))) return false;
-	if(m1_cpos.y == m2_cpos.y) {
-		add_to_buff(&ntxt, &pos, &csize, (char*)(lines[m1_cpos.y]) + m1_cpos.x, m2_cpos.x - m1_cpos.x);
-		::CopyText(ntxt, pos);			free(ntxt);
-		}
-	else if(m1_cpos.y < m2_cpos.y){
-		add_to_buff(&ntxt, &pos, &csize, (char*)(lines[m1_cpos.y]) + m1_cpos.x, 0);
-		for(i = m1_cpos.y; i < m2_cpos.y; i++) {
-			add_to_buff(&ntxt, &pos, &csize, (char*)(lines[i]), 0);
-			}
-		add_to_buff(&ntxt, &pos, &csize, (char*)(lines[i]), m2_pos.x);
-		::CopyText(ntxt, pos);			free(ntxt);
-		}
-	else {
-		free(ntxt);						return false;
-		}
-	if(b_cut) ReplMark(o, "");
-	return true;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// The segment object is either a pie slice or a ring segment
-//
-segment::segment(GraphObj *par, DataObj *d, lfPOINT *c, double r1, double r2,
-				 double a1, double a2):GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	segFill.hatch = &segFillLine;
-	segFill.color = 0x00c0c0c0L;
-	fCent.fx = c->fx;		fCent.fy = c->fy;
-	radius1 = r1;			radius2 = r2;
-	angle1 = a1;			angle2 = a2;
-	Id = GO_SEGMENT;
-	bModified = false;
-}
-
-segment::segment(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	bModified = false;
-}
-
-segment::~segment()
-{
-	if(pts && nPts) free(pts);
-	if(bModified) Undo.InvalidGO(this);
-}
-	
-bool 
-segment::SetSize(int select, double value)
-{
-	switch(select & 0xfff) {
-	case SIZE_XPOS:		fCent.fx = value;		break;
-	case SIZE_YPOS:		fCent.fy = value;		break;
-	case SIZE_RADIUS1:	radius1 = value;		break;
-	case SIZE_RADIUS2:	radius2 = value;		break;
-	case SIZE_ANGLE1:	angle1 = value;			break;
-	case SIZE_ANGLE2:	angle2 = value;			break;
-	default:			return false;
-		}
-	return true;
-}
-
-void
-segment::DoPlot(anyOutput *o)
-{
-	double dsize, dpt, frx1, frx2, dtmp, da, dda, sia, csia;
-	int i, n, npt = 12;
-	POINT np, of, cp,  *tmppts;
-
-	if(!o || angle1 == angle2) return;
-	dsize = angle1 > angle2 ? angle1 -angle2 : (angle1+360.0)-angle2;
-	dpt = dsize*0.01745329252;		//degrees to rad
-	frx1 = (double)o->un2fix(radius1);
-	frx2 = (double)o->un2fix(radius2);
-	dtmp = frx1*dpt;
-	npt += (int)(dtmp < dsize ? dtmp : dsize);
-	dtmp = frx2*dpt;
-	npt += (int)(dtmp < dsize ? dtmp : dsize);
-	if(!(pts = (POINT*)malloc(npt*sizeof(POINT))))return;
-	nPts = 0;
-	n = (dtmp < dsize) ? (int)dtmp : (int)dsize;
-	while (n<2) n++;
-	da = angle1*0.01745329252;
-	dda = (dsize*0.01745329252)/(double)n;
-	sia = sin(0.5*((angle2 < angle1 ? angle1 : angle1 +360) + angle2) * 0.01745329252);
-	csia = cos(0.5*((angle2 < angle1 ? angle1 : angle1 +360) + angle2) * 0.01745329252);
-	of.x = o->un2ix(shift * csia);
-	of.y = - (o->un2iy(shift * sia));
-	cp.x = o->co2ix(fCent.fx + (parent ? parent->GetSize(SIZE_GRECT_LEFT): 0.0));
-	cp.y = o->co2iy(fCent.fy + (parent ? parent->GetSize(SIZE_GRECT_TOP): 0.0));
-	for(i = 0; i < n; i++) {
-		sia = sin(da);					csia = cos(da);
-		np.x = of.x + cp.x;				np.y = of.y + cp.y;
-		np.x += o->un2ix(csia*radius2);	np.y -= o->un2iy(sia*radius2);
-		AddToPolygon(&nPts, pts, &np);
-		da -= dda;
-		}
-	sia = sin(angle2 *0.01745329252);
-	csia = cos(angle2 *0.01745329252);
-	np.x = of.x + cp.x;					np.y = of.y + cp.y;
-	np.x += o->un2ix(csia*radius2);		np.y -= o->un2iy(sia*radius2);
-	AddToPolygon(&nPts, pts, &np);
-	dtmp = frx1*dpt;
-	n = dtmp < dsize ? (int)dtmp : (int)dsize;
-	da = angle2*0.01745329252;
-	if(n>1)dda = (dsize*0.01745329252)/(double)n;
-	else dda = 0.0;
-	for(i = 0; i < n; i++) {
-		sia = sin(da);					csia = cos(da);
-		np.x = of.x + cp.x;				np.y = of.y + cp.y;
-		np.x += o->un2ix(csia*radius1);	np.y -= o->un2iy(sia*radius1);
-		AddToPolygon(&nPts, pts, &np);
-		da += dda;
-		}
-	sia = sin(angle1 *0.01745329252);	csia = cos(angle1 *0.01745329252);
-	np.x = of.x + cp.x;					np.y = of.y + cp.y;
-	np.x += o->un2ix(csia*radius1);		np.y -= o->un2iy(sia*radius1);
-	AddToPolygon(&nPts, pts, &np);
-	if(nPts <3) return;
-	AddToPolygon(&nPts, pts, &pts[0]);	//close polygon
-	o->SetLine(&segLine);				o->SetFill(&segFill);
-	o->oPolygon(pts, nPts);
-	if(tmppts = (POINT*)realloc(pts, nPts *sizeof(POINT))) pts = tmppts;
-	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
-	for(i = 2; i < nPts; i++) UpdateMinMaxRect(&rDims, pts[i].x, pts[i].y);
-	i = 3*o->un2ix(segLine.width);		//increase size of rectangle for marks
-	IncrementMinMaxRect(&rDims, i+6);
-}
-
-void
-segment::DoMark(anyOutput *o, bool mark)
-{
-	if(mark)InvertPolygon(pts, nPts, &segLine, &segFill, &rDims, o, mark);
-	else if(parent) {
-		parent->DoPlot(o);
-		o->UpdateRect(&rDims, false);
-		}
-}
-
-bool
-segment::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	FillDEF *TmpFill;
-	LegItem *leg;
-
-	switch (cmd) {
-	case CMD_SCALE:
-		fCent.fx *= ((scaleINFO*)tmpl)->sx.fy;			fCent.fy *= ((scaleINFO*)tmpl)->sx.fy;
-		radius1 *= ((scaleINFO*)tmpl)->sx.fy;			radius2 *= ((scaleINFO*)tmpl)->sx.fy;
-		segLine.width *= ((scaleINFO*)tmpl)->sx.fy;		segLine.patlength *= ((scaleINFO*)tmpl)->sx.fy;
-		segFillLine.width *= ((scaleINFO*)tmpl)->sx.fy;	segFillLine.patlength *= ((scaleINFO*)tmpl)->sx.fy;
-		segFill.scale *= ((scaleINFO*)tmpl)->sx.fy;		shift *= ((scaleINFO*)tmpl)->sx.fy;
-		return true;
-	case CMD_LEGEND:
-		if(tmpl) {
-			leg = new LegItem(this, data, 0L, &segLine, &segFill, 0L);
-			if(!((Legend*)tmpl)->Command(CMD_DROP_OBJECT, leg, o)) DeleteGO(leg);
-			}
-		return true;
-	case CMD_MOUSE_EVENT:
-		switch (((MouseEvent*)tmpl)->Action) {
-		case MOUSE_LBUP:
-			if(ObjThere(((MouseEvent*)tmpl)->x, ((MouseEvent*)tmpl)->y)) 
-				return o->ShowMark(CurrGO=this, MRK_GODRAW);
-			}
-		return false;
-	case CMD_SET_DATAOBJ:
-		Id = GO_SEGMENT;
-		return true;
-	case CMD_REDRAW:
-		return parent ? parent->Command(cmd, tmpl, o) : false;
-	case CMD_SEG_FILL:
-		TmpFill = (FillDEF *)tmpl;
-		if(TmpFill) {
-			segFill.type = TmpFill->type;			segFill.color = TmpFill->color;
-			segFill.scale = TmpFill->scale;
-			if(TmpFill->hatch) memcpy(&segFillLine, TmpFill->hatch, sizeof(LineDEF));
-			}
-		return true;
-	case CMD_SEG_LINE:
-		if(tmpl) memcpy(&segLine, tmpl, sizeof(LineDEF));
-		return true;
-	case CMD_SEG_MOVEABLE:
-		if(tmpl) moveable = *((int*)tmpl);
-		return true;
-	case CMD_SHIFT_OUT:
-		if(tmpl) shift = *((double*)tmpl);
-		return true;
-	case CMD_MOVE:
-		bModified = true;
-		Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
-	case CMD_UNDO_MOVE:
-		fCent.fx += ((lfPOINT*)tmpl)[0].fx;		fCent.fy += ((lfPOINT*)tmpl)[0].fy;
-		if(parent)parent->Command(CMD_REDRAW, 0L, o);
-		return true;
-		}
-	return false;
-}
-
-void *
-segment::ObjThere(int x, int y)
-{
-	bool bFound = false;
-	POINT p1;
-
-	if(IsInRect(&rDims, p1.x = x, p1.y = y)) {
-		bFound = IsInPolygon(&p1, pts, nPts);
-		if(bFound || IsCloseToPL(p1, pts, nPts)) return this;
-		}
-	return 0L;
-}
-
-void
-segment::Track(POINT *p, anyOutput *o)
-{
-	POINT *tpts;
-	RECT old_rc;
-	int i;
-
-	if(o && (tpts = (POINT*)malloc(nPts*sizeof(POINT)))){
-		memcpy(&old_rc, &rDims, sizeof(rDims));
-		o->UpdateRect(&rDims, false);
-		for(i = 0; i < nPts; i++) {
-			tpts[i].x = pts[i].x+p->x;			tpts[i].y = pts[i].y+p->y;
-			UpdateMinMaxRect(&rDims, tpts[i].x, tpts[i].y);
-			}
-		o->ShowLine(tpts, nPts, segLine.color);
-		if(old_rc.left != rDims.left || old_rc.right != rDims.right || old_rc.top !=
-			rDims.top || old_rc.bottom != rDims.bottom)IncrementMinMaxRect(&rDims, 3);
-		free(tpts);
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// the polyline object
-//
-polyline::polyline(GraphObj *par, DataObj *d, lfPOINT *fpts, int cpts):
-	GraphObj(par, d)
-{
-	double dx = 0.0, dy = 0.0;
-	int i;
-
-	FileIO(INIT_VARS);
-	if(parent){
-		dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
-		}
-	if(cpts && (Values = (lfPOINT*)malloc((nPoints = cpts)* sizeof(lfPOINT)))){
-		memcpy(Values, fpts, cpts*sizeof(lfPOINT));
-		for(i = 0; i < cpts; i++) {
-			Values[i].fx -= dx;		Values[i].fy -= dy;
-			}
-		}
-	Id = GO_POLYLINE;
-	bModified = false;
-}
-
-polyline::polyline(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	bModified = false;
-}
-
-polyline::~polyline()
-{
-	Command(CMD_FLUSH, 0L, 0L);
-	if(this == CurrGO) CurrGO = 0L;
-	if(bModified) Undo.InvalidGO(this);
-}
-
-double
-polyline::GetSize(int select)
-{
-	int i;
-
-	if(select >= SIZE_XPOS && select <=  SIZE_XPOS_LAST){
-		if((i = select-SIZE_XPOS) >=0 && i <= 200)
-			return i < nPoints ? Values[i].fx : Values[nPoints-2].fx;
-		}
-	if(select >= SIZE_YPOS && select <= SIZE_YPOS_LAST){
-		if((i = select-SIZE_YPOS) >=0 && i <= 200)
-			return i < nPoints ? Values[i].fy : Values[nPoints-2].fy;
-		}
-	return parent ? parent->GetSize(select) : 0.0;
-}
-
-DWORD
-polyline::GetColor(int select)
-{
-	return pgLine.color;
-}
-
-bool
-polyline::SetSize(int select, double value)
-{
-	int i;
-
-	if((select & 0xfff) >= SIZE_XPOS && (select & 0xfff) <=  SIZE_XPOS_LAST){
-		if((i = select-SIZE_XPOS) >=0 && i <= 200 && i < (int)nPoints){
-			Values[i].fx = value;
-			return true;
-			}
-		}
-	if((select & 0xfff) >= SIZE_YPOS && (select & 0xfff) <= SIZE_YPOS_LAST){
-		if((i = select-SIZE_YPOS) >=0 && i <= 200 && i < (int)nPoints){
-			Values[i].fy = value;
-			return true;
-			}
-		}
-	return false;
-}
-
-void
-polyline::DoPlot(anyOutput *o)
-{
-	POINT np, *tmppts;
-	double dx, dy;
-	int i;
-
-	if(!Values || !nPoints || !o || !parent) return;
-	if(pts) free(pts);
-	dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
-	if(!(pts = (POINT*)malloc((nPoints+2)*sizeof(POINT))))return;
-	for(i = nPts = 0; i < nPoints; i++){
-		np.x = o->co2ix(Values[i].fx + dx);		np.y = o->co2iy(Values[i].fy + dy);
-		AddToPolygon(&nPts, pts, &np);
-		}
-	if(type == 1) AddToPolygon(&nPts, pts, &pts[0]);	//close polygon
-	if(tmppts = (POINT*)realloc(pts, nPts *sizeof(POINT))) pts = tmppts;
-	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
-	for(i = 2; i < nPts; i++) UpdateMinMaxRect(&rDims, pts[i].x, pts[i].y);
-	i = 3*o->un2ix(pgLine.width)+3;		//increase size of rectangle for marks
-	IncrementMinMaxRect(&rDims, i);
-	if(this == CurrGO) o->ShowMark(this, MRK_GODRAW);
-	else switch(type) {
-	case 0:				//line
-		o->SetLine(&pgLine);							o->oPolyline(pts, nPts);
-		break;
-	case 1:				//polygon
-		o->SetLine(&pgLine);	o->SetFill(&pgFill);	o->oPolygon(pts, nPts);
-		break;
-		}
-}
-
-void
-polyline::DoMark(anyOutput *o, bool mark)
-{
-	RECT upd;
-
-	memcpy(&upd, &rDims, sizeof(RECT));
-	IncrementMinMaxRect(&upd, 6 + o->un2ix(pgLine.width)*4);
-	if(mark) {
-		o->SetLine(&pgLine);
-		if(nPoints < 200){
-			switch(type) {
-			case 0:				//line
-				o->oPolyline(pts, nPts);
-			break;
-			case 1:				//polygon
-				o->SetFill(&pgFill);		o->oPolygon(pts, nPts);
-				break;
-				}
-			ShowPoints(o);
-			}
-		else InvertLine(pts, nPts, &pgLine, &upd, o, true);;
-		o->UpdateRect(&upd, false);
- 		}
-	else {
-		if(parent)	parent->DoPlot(o);
-		}
-}
-
-bool
-polyline::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	POINT p1;
-	bool bFound = false;
-	int i;
-
-	switch (cmd) {
-	case CMD_MRK_DIRTY:			//issued by Undo
-		CurrGO = this;
-		bModified = true;
-	case CMD_FLUSH:
-		if(pHandles) {
-			for(i = 0; i < nPoints; i++) if(pHandles[i]) delete(pHandles[i]);
-			free(pHandles);		pHandles = 0L;
-			}
-		if(cmd == CMD_FLUSH && Values && nPoints){
-			free(Values);
-			Values = 0L;	nPoints = 0;
-			}
-		if(pts && nPts) free(pts);		pts = 0L;		nPts = 0;
-		return true;
-	case CMD_SCALE:
-		if(Values) for(i = 0; i < nPoints; i++){
-			Values[i].fx = ((scaleINFO*)tmpl)->sx.fx + Values[i].fx * ((scaleINFO*)tmpl)->sx.fy;
-			Values[i].fy = ((scaleINFO*)tmpl)->sy.fx + Values[i].fy * ((scaleINFO*)tmpl)->sy.fy;
-			}
-		pgLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;		pgLine.width *= ((scaleINFO*)tmpl)->sy.fy;
-		pgFillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;	pgFillLine.width *= ((scaleINFO*)tmpl)->sy.fy;
-		pgFill.scale *= ((scaleINFO*)tmpl)->sy.fy;
-		return true;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(!ObjThere(p1.x= mev->x, p1.y=mev->y)|| CurrGO || !o || nPoints <2)return false;
-			return o->ShowMark(CurrGO=this, MRK_GODRAW);
-			}
-		return false;
-	case CMD_DELOBJ:
-		if(pHandles && tmpl && tmpl == (void*)CurrHandle) {
-			for(i = 0; i < nPoints; i++) if(pHandles[i] == CurrHandle) {
-				Undo.DataMem(this, (void**)&Values, nPoints * sizeof(lfPOINT), &nPoints, 0L);
-				for( ; i < nPoints-1; i++) {
-					Values[i].fx = Values[i+1].fx;	Values[i].fy = Values[i+1].fy;
-					}
-				nPoints--;
-				if(pHandles[nPoints])delete(pHandles[nPoints]);
-				pHandles[nPoints] = 0L;				CurrHandle = 0L;
-				CurrGO = this;						bModified = true;
-				return true;
-				}
-			}
-		return false;
-	case CMD_SAVEPOS:
-		if(tmpl && Values) {
-			bModified = true;
-			i = *(int*)tmpl;
-			if(i >= 0 && i < nPoints) Undo.SaveLFP(this, Values + i, 0L);
-			}
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = type == 1 ? GO_POLYGON : GO_POLYLINE;
-		return true;
-	case CMD_SETSCROLL:		case CMD_REDRAW:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		return false;
-	case CMD_MOVE:
-		bModified = true;
-		Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
-	case CMD_UNDO_MOVE:
-		for(i = 0; i < nPoints; i++) {
-			Values[i].fx += ((lfPOINT*)tmpl)[0].fx;
-			Values[i].fy += ((lfPOINT*)tmpl)[0].fy;
-			}
-		if(o) {
-			o->StartPage();		parent->DoPlot(o);		o->EndPage();
-			}
-		return true;
-		}
-	return false;
-}
-
-void * 
-polyline::ObjThere(int x, int y)
-{
-	bool bFound = false;
-	POINT p1;
-	int i;
-	void *ret;
-
-	if(IsInRect(&rDims, p1.x = x, p1.y = y)) {
-		if(CurrGO == this && pHandles) for(i = nPoints-1; i >= 0; i--) 
-			if((pHandles[i]) && (ret = pHandles[i]->ObjThere(x, y))) return ret;
-		if(type == 1) bFound = IsInPolygon(&p1, pts, nPts);
-		if(bFound || IsCloseToPL(p1,pts,nPts)) return this;
-		}
-	return 0L;
-}
-
-void
-polyline::Track(POINT *p, anyOutput *o)
-{
-	POINT *tpts;
-	RECT old_rc;
-	int i;
-
-	if(o && (tpts = (POINT*)malloc(nPts*sizeof(POINT)))){
-		memcpy(&old_rc, &rDims, sizeof(rDims));
-		o->UpdateRect(&rDims, false);
-		for(i = 0; i < nPts; i++) {
-			tpts[i].x = pts[i].x+p->x;
-			tpts[i].y = pts[i].y+p->y;
-			UpdateMinMaxRect(&rDims, tpts[i].x, tpts[i].y);
-			}
-		o->ShowLine(tpts, nPts, pgLine.color);
-		if(old_rc.left != rDims.left || old_rc.right != rDims.right || old_rc.top !=
-			rDims.top || old_rc.bottom != rDims.bottom)IncrementMinMaxRect(&rDims, 3);
-		free(tpts);
-		}
-}
-
-void
-polyline::ShowPoints(anyOutput *o)
-{
-	int i;
-	double dx, dy;
-	POINT hpts[3];
-	LineDEF gl = {0.0, 1.0, 0x00c0c0c0, 0};
-
-	if(nPoints >= 200 || !o) return;
-	if(!pHandles && (pHandles = (dragHandle**)calloc(nPoints+4, sizeof(dragHandle*)))){
-		for(i = 0; i < nPoints; i++) pHandles[i] = new dragHandle(this, DH_DATA+i);
-		}
-	if(!pHandles) return;
-	if(Id == GO_BEZIER && parent && nPoints > 3) {
-		dx = parent->GetSize(SIZE_GRECT_LEFT);			dy = parent->GetSize(SIZE_GRECT_TOP);
-		o->SetLine(&gl);
-		hpts[0].x = o->co2ix(Values[0].fx+dx);			hpts[0].y = o->co2iy(Values[0].fy+dy);
-		hpts[1].x = o->co2ix(Values[1].fx+dx);			hpts[1].y = o->co2iy(Values[1].fy+dy);
-		o->oPolyline(hpts, 2);
-		for(i = 3; i < (nPoints-2); i += 3) {
-			hpts[0].x = o->co2ix(Values[i-1].fx+dx);	hpts[0].y = o->co2iy(Values[i-1].fy+dy);
-			hpts[1].x = o->co2ix(Values[i].fx+dx);		hpts[1].y = o->co2iy(Values[i].fy+dy);
-			hpts[2].x = o->co2ix(Values[i+1].fx+dx);	hpts[2].y = o->co2iy(Values[i+1].fy+dy);
-			o->oPolyline(hpts, 3);
-			}
-		hpts[0].x = o->co2ix(Values[i-1].fx+dx);		hpts[0].y = o->co2iy(Values[i-1].fy+dy);
-		hpts[1].x = o->co2ix(Values[i].fx+dx);			hpts[1].y = o->co2iy(Values[i].fy+dy);
-		o->oPolyline(hpts, 2);
-		}
-	for(i = 0; i < nPoints; i++) if(pHandles[i]) pHandles[i]->DoPlot(o);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Beziers are based on the polyline object
-Bezier::Bezier(GraphObj *par, DataObj *d, lfPOINT *fpts, int cpts, int mode, double res):
-	polyline(par, d, 0L, 0)
-{
-	double dx, dy, merr;
-	int i;
-
-	type = mode;
-	Id = GO_BEZIER;
-
-	if(!parent) return;
-	dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
-	if(type == 0 && (Values = (lfPOINT*)malloc(4 * cpts * sizeof(lfPOINT)))) {
-		merr = 0.01 * res / Units[defs.cUnits].convert;
-		FitCurve(fpts, cpts, merr);
-		Values[nPoints].fx = fpts[cpts-1].fx;	Values[nPoints].fy = fpts[cpts-1].fy;
-		for(i = 0; i < nPoints; i++) {
-			Values[i].fx -= dx;		Values[i].fy -= dy;
-			}
-		nPoints ++;
-		}
-	return;
-}
-
-Bezier::Bezier(int src):polyline(0L, 0L, 0L, 0)
-{
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-}
-
-void
-Bezier::DoPlot(anyOutput *o)
-{
-	POINT *tmppts;
-	double dx, dy;
-	int i;
-
-	if(!Values || !nPoints || !o || !parent) return;
-	if(pts) free(pts);		pts = 0L;
-	dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
-	if(!(tmppts = (POINT*)malloc((nPoints+2)*sizeof(POINT))))return;
-	for(i = 0; i < nPoints; i++){
-		tmppts[i].x = o->co2ix(Values[i].fx + dx);	tmppts[i].y = o->co2iy(Values[i].fy + dy);
-		}
-	rDims.left = rDims.right = tmppts[0].x;		rDims.top = rDims.bottom = tmppts[0].y;
-	for(i = 1; i < nPoints; i++) {
-		if(tmppts[i].x < rDims.left) rDims.left = tmppts[i].x;
-		else if(tmppts[i].x > rDims.right) rDims.right = tmppts[i].x;
-		if(tmppts[i].y < rDims.top) rDims.top = tmppts[i].y;
-		else if(tmppts[i].y > rDims.bottom) rDims.bottom = tmppts[i].y;
-		}
-	//DrawBezier returns not more than 2^MAXDEPTH points
-	pts = (POINT*)malloc(nPoints * 64 * sizeof(POINT));
-	for(i= nPts = 0; i< (nPoints-2); i += 3) {
-		DrawBezier(&nPts, pts, tmppts[i], tmppts[i+1], tmppts[i+2], tmppts[i+3], 0);
-		}
-	IncrementMinMaxRect(&rDims, 3);
-	o->ShowLine(pts, nPts, 0x00c0c0c0L);
-	if(this == CurrGO) o->ShowMark(this, MRK_GODRAW);
-	else {
-		o->SetLine(&pgLine);			o->oPolyline(pts, nPts);
-		}
-}
-
-bool
-Bezier::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i, i1, i2;
-
-	switch (cmd) {
-	case CMD_MRK_DIRTY:			//issued by Undo
-		CurrGO = this;
-		bModified = true;
-	case CMD_FLUSH:
-		if(pHandles) {
-			for(i = 0; i < nPoints; i++) if(pHandles[i]) delete(pHandles[i]);
-			free(pHandles);		pHandles = 0L;
-			}
-		if(cmd == CMD_FLUSH && Values && nPoints){
-			free(Values);
-			Values = 0L;	nPoints = 0;
-			}
-		if(pts && nPts) free(pts);		pts = 0L;		nPts = 0;
-		return true;
-	case CMD_DELOBJ:
-		if(pHandles && tmpl && tmpl == (void*)CurrHandle) {
-			i = CurrHandle->type - DH_DATA;
-			if (i >= 0 && i < nPoints) {
-				i = CurrHandle->type - DH_DATA;
-				Undo.DataMem(this, (void**)&Values, nPoints * sizeof(lfPOINT), &nPoints, 0L);
-				if (i < 2) {
-					i1 = 0;					i2 = 3;
-					}
-				else if (i > (nPoints-3)) {
-					i1 = nPoints -3;		i2 = nPoints;
-					}
-				else {
-					i -= (((i-2) % 3)-1);	i1 = i -1;		i2 = i +2;
-					RemovePoint(Values, i1+1);
-					Values[i2].fx = Values[i1].fx;	Values[i2].fy = Values[i1].fy;
-					}
-				for(i = 0; i < nPoints; i++) if(pHandles[i]) delete(pHandles[i]);
-				free(pHandles);		pHandles = 0L;		CurrHandle = 0L;
-				i = i2 - i1;
-				for( ; i2 < nPoints; i1++, i2++) {
-					Values[i1].fx = Values[i2].fx;	Values[i1].fy = Values[i2].fy;
-					}
-				nPoints -= i;	CurrGO = this;		bModified = true;
-				return true;
-				}
-			}
-		return false;
-	case CMD_SET_DATAOBJ:
-		Id = GO_BEZIER;
-		return true;
-		}
-	return polyline::Command(cmd, tmpl, o);
-}
-
-void
-Bezier::AddPoints(int n, lfPOINT *p)
-{
-	int i;
-
-	for(i = 0; i< n; i++) {
-		Values[nPoints].fx = p[i].fx;	Values[nPoints].fy = p[i].fy;
-		nPoints ++;
-		}
-}
-
-//Fitting of a Bezier curve to digitized points is based on:
-//   P.J. Schneider (1990) An Algorithm for Automatically Fitting Digitized
-//   Curves. In: Graphics Gems (Ed. A. Glassner, Academic Press Inc., 
-//   ISBN 0-12-286165-5) pp. 612ff
-void
-Bezier::FitCurve(lfPOINT *d, int npt, double error)
-{
-	double len;
-	lfPOINT tHat1, tHat2;
-
-	tHat1.fx = d[1].fx - d[0].fx;		tHat1.fy = d[1].fy - d[0].fy;
-	if(0.0 != (len = sqrt((tHat1.fx * tHat1.fx) + (tHat1.fy * tHat1.fy)))) {
-		tHat1.fx /= len;					tHat1.fy /= len;
-		}
-	tHat2.fx = d[npt-2].fx - d[npt-1].fx;		tHat2.fy = d[npt-2].fy - d[npt-1].fy;
-	if(0.0 != (len = sqrt((tHat2.fx * tHat2.fx) + (tHat2.fy * tHat2.fy)))) {
-		tHat2.fx /= len;					tHat2.fy /= len;
-		}
-	FitCubic(d, 0, npt -1, tHat1, tHat2, error);
-}
-
-void
-Bezier::RemovePoint(lfPOINT *d, int sel)
-{
-	lfPOINT tHat1, tHat2;
-
-	tHat1.fx = d[sel-2].fx - d[sel-3].fx;	tHat1.fy = d[sel-2].fy - d[sel-3].fy;
-	tHat2.fx = d[sel+2].fx - d[sel+3].fx;	tHat2.fy = d[sel+2].fy - d[sel+3].fy;
-	d[sel] = d[sel+3];
-	IpolBez(d+sel-3, tHat1, tHat2);
-}
-
-//heuristic interpolation: other methods failed
-void
-Bezier::IpolBez(lfPOINT *d, lfPOINT tHat1, lfPOINT tHat2)
-{
-	double b0, b1, b2;						//temp variables
-
-	b1 = d[3].fx - d[0].fx;					b2 = d[3].fy - d[0].fy;
-	b0 = sqrt(b1 * b1 + b2 * b2)/3.0;
-	b1 = b0/sqrt(tHat1.fx * tHat1.fx + tHat1.fy * tHat1.fy);
-	b2 = b0/sqrt(tHat2.fx * tHat2.fx + tHat2.fy * tHat2.fy);
-	d[1].fx = d[0].fx + tHat1.fx * b1;		d[1].fy = d[0].fy + tHat1.fy * b1;
-	d[2].fx = d[3].fx + tHat2.fx * b2;		d[2].fy = d[3].fy + tHat2.fy * b2;
-}
-
-//Fit a Bezier curve to a (sub)set of digitized points
-void
-Bezier::FitCubic(lfPOINT *d, int first, int last, lfPOINT tHat1, lfPOINT tHat2, double error)
-{
-	lfPOINT bezCurve[4];
-	double *u, *uPrime, maxError, iterationError, len;
-	int i, splitPoint, npt;
-	lfPOINT tHatCenter;
-	int maxIterations = 8;
-
-	iterationError = error * error;
-	npt = last - first +1;
-	if(npt == 2) {
-		bezCurve[0] = d[first];					bezCurve[3] = d[last];
-		IpolBez(bezCurve, tHat1, tHat2);
-		AddPoints(3, bezCurve);
-		return;
-		}
-	u = ChordLengthParameterize(d, first, last);
-	GenerateBezier(d, first, last, u, tHat1, tHat2, bezCurve);
-	maxError = ComputeMaxError(d, first, last, bezCurve, u, &splitPoint);
-	if(maxError < error) {
-		if(maxError < 0.0) {					//Failure fitting curve
-			IpolBez(bezCurve, tHat1, tHat2);
-			}
-		AddPoints(3, bezCurve);					return;
-		}
-	if(maxError < iterationError) {
-		for (i = 0; i < maxIterations; i++) {
-			uPrime = Reparameterize(d, first, last, u, bezCurve);
-			GenerateBezier(d, first, last, uPrime, tHat1, tHat2, bezCurve);
-			maxError = ComputeMaxError(d, first, last, bezCurve, uPrime, &splitPoint);
-			if (maxError < error) {
-				if(maxError < 0.0) IpolBez(bezCurve, tHat1, tHat2);
-				AddPoints(3, bezCurve);			return;
-				}
-			free(u);							u = uPrime;
-			}
-		}
-	//Fitting failed: split at max error point and recurse
-	tHatCenter.fx = d[splitPoint-1].fx - d[splitPoint+1].fx;
-	tHatCenter.fy = d[splitPoint-1].fy - d[splitPoint+1].fy;
-	if(0.0 != (len = sqrt((tHatCenter.fx * tHatCenter.fx) + (tHatCenter.fy * tHatCenter.fy)))) {
-		tHatCenter.fx /= len;					tHatCenter.fy /= len;
-		}
-	FitCubic(d, first, splitPoint, tHat1, tHatCenter, error * _SQRT2);
-	tHatCenter.fx = -tHatCenter.fx;				tHatCenter.fy = -tHatCenter.fy;
-	FitCubic(d, splitPoint, last, tHatCenter, tHat2, error * _SQRT2);
-}
-
-//Use least-squares method to find Bezier control points for region.
-void
-Bezier::GenerateBezier(lfPOINT *d, int first, int last, double *uPrime, 
-	lfPOINT tHat1, lfPOINT tHat2, lfPOINT *bezCurve)
-{
-	int i, npt;
-	lfPOINT *A0, *A1, tmp, v1, v2;
-	double C[2][2], X[2];
-	double det_C0_C1, det_C0_X, det_X_C1, alpha_l, alpha_r;
-	double b0, b1, b2, b3;		//temp variables
-
-	npt = last - first +1;
-	A0 = (lfPOINT*) malloc(npt * sizeof(lfPOINT));
-	A1 = (lfPOINT*) malloc(npt * sizeof(lfPOINT));
-	for (i = 0; i < npt; i++) {
-		v1 = tHat1;								v2 = tHat2;
-		b2 = 1.0 - uPrime[i];
-		b1 = 3.0 * uPrime[i] * b2 * b2;			b2 = 3.0 * uPrime[i] * uPrime[i] * b2;
-		if(0.0 != (b0 = sqrt((v1.fx * v1.fx) + (v1.fy * v1.fy)))) {
-			v1.fx *= b1/b0;						v1.fy *= b1/b0;
-			}
-		if(0.0 != (b0 = sqrt((v2.fx * v2.fx) + (v2.fy * v2.fy)))) {
-			v2.fx *= b2/b0;						v2.fy *= b2/b0;
-			}
-		A0[i] = v1;								A1[i] = v2;
-		}
-	C[0][0] = C[0][1] = C[1][0] = C[1][1] = X[0] = X[1] = 0.0;
-	for (i = 0; i < npt; i++) {
-		C[0][0] += ((A0[i].fx * A0[i].fx) + (A0[i].fy * A0[i].fy));
-		C[0][1] += ((A0[i].fx * A1[i].fx) + (A0[i].fy * A1[i].fy));
-		C[1][0] = C[0][1];
-		C[1][1] += ((A1[i].fx * A1[i].fx) + (A1[i].fy * A1[i].fy));
-		b2 = 1.0 - uPrime[i];					b0 = b2 * b2 * b2;
-		b1 = 3.0 * uPrime[i] * b2 * b2;			b2 = 3.0 * uPrime[i] * uPrime[i] * b2;
-		b3 = uPrime[i] * uPrime[i] * uPrime[i];
-		tmp.fx = d[last].fx * b2 + d[last].fx * b3;
-		tmp.fy = d[last].fy * b2 + d[last].fy * b3;
-		tmp.fx += d[first].fx * b1;				tmp.fy += d[first].fy * b1;
-		tmp.fx += d[first].fx * b0;				tmp.fy += d[first].fy * b0;
-		tmp.fx = d[first+i].fx - tmp.fx;		tmp.fy = d[first+i].fy - tmp.fy;
-		X[0] += (A0[i].fx * tmp.fx + A0[i].fy * tmp.fy);
-		X[1] += (A1[i].fx * tmp.fx + A1[i].fy * tmp.fy);
-		}
-	det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1];
-	det_C0_X = C[0][0] * X[1] - C[0][1] * X[0];
-	det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1];
-	if(det_C0_C1 == 0.0) det_C0_C1 = (C[0][0] * C[1][1]) * 10e-12;
-	alpha_l = det_X_C1 / det_C0_C1;				alpha_r = det_C0_X / det_C0_C1;
-	bezCurve[0] = d[first];						bezCurve[3] = d[last];
-	if(alpha_l < 0.0 || alpha_r < 0.0) {
-		//use Wu/Barsky heuristic
-		b1 = d[last].fx - d[first].fx;			b2 = d[last].fy - d[first].fy;
-		b0 = sqrt(b1 * b1 + b2 * b2)/3.0;
-		b1 = b0/sqrt(tHat1.fx * tHat1.fx + tHat1.fy * tHat1.fy);
-		b2 = b0/sqrt(tHat2.fx * tHat2.fx + tHat2.fy * tHat2.fy);
-		}
-	else {
-		b1 = alpha_l/sqrt(tHat1.fx * tHat1.fx + tHat1.fy * tHat1.fy);
-		b2 = alpha_r/sqrt(tHat2.fx * tHat2.fx + tHat2.fy * tHat2.fy);
-		}
-	bezCurve[1].fx = bezCurve[0].fx + tHat1.fx * b1;
-	bezCurve[1].fy = bezCurve[0].fy + tHat1.fy * b1;
-	bezCurve[2].fx = bezCurve[3].fx + tHat2.fx * b2;
-	bezCurve[2].fy = bezCurve[3].fy + tHat2.fy * b2;
-	free(A0);									free(A1);
-}
-
-//Given set of points and their parameterization, try to find
-//  a better parameterization.
-double *
-Bezier::Reparameterize(lfPOINT *d, int first, int last, double *u, lfPOINT *bezCurve)
-{
-	int i, j, k, npt = last-first+1;
-	double *uPrime, num, den, *pl, *ph, *pq;
-	lfPOINT Q1[3], Q2[2], Q_u, Q1_u, Q2_u;
-
-	uPrime = (double*)malloc(npt * sizeof(double));
-	//Use Newton-Raphson iteration to find better root
-	for (i = first, j = 0; i <= last; i++, j++) {
-		for(pl=(double*)bezCurve, ph=pl+2, pq=(double*)Q1, k=0; k <= 4; pl++, ph++, pq++, k++) {
-			*pq = (*ph - *pl ) * 3.0;
-			}
-		for(pl=(double*)Q1, ph=pl+2, pq=(double*)Q2, k=0; k <= 2; pl++, ph++, pq++, k++) {
-			*pq = (*ph - *pl ) * 2.0;
-			}
-		Q_u = fBezier(3, bezCurve, u[j]);
-		Q1_u = fBezier(2, Q1, u[j]);	
-		Q2_u = fBezier(1, Q2, u[j]);
-		num = (Q_u.fx - d[i].fx) * (Q1_u.fx) + (Q_u.fy - d[i].fy) * (Q1_u.fy);
-		den = (Q1_u.fx) * (Q1_u.fx) + (Q1_u.fy) * (Q1_u.fy) + 
-			(Q_u.fx - d[i].fx) * (Q2_u.fx) + (Q_u.fy - d[i].fy) * (Q2_u.fy);
-		uPrime[j] = u[j] - (num/den);
-		}
-	return uPrime;
-}
-
-//evaluate a Bezier curve at a particular parameter value
-lfPOINT
-Bezier::fBezier(int degree, lfPOINT *V, double t)
-{
-	int i, j;
-	lfPOINT Q;
-	lfPOINT *Vtemp;
-
-	Vtemp = (lfPOINT *)malloc((degree+1) * sizeof(lfPOINT));
-	for (i = 0; i <= degree; i++) {
-		Vtemp[i] = V[i];
-		}
-	for (i = 1; i <= degree; i++) {
-		for (j = 0; j <= degree-i; j++) {
-			Vtemp[j].fx = (1.0 -t) * Vtemp[j].fx + t * Vtemp[j+1].fx;
-			Vtemp[j].fy = (1.0 -t) * Vtemp[j].fy + t * Vtemp[j+1].fy;
-			}
-		}
-	Q = Vtemp[0];
-	free(Vtemp);
-	return Q;
-}
-
-double *
-Bezier::ChordLengthParameterize(lfPOINT *d, int first, int last)
-{ 
-	int i;
-	double tmp, *u;
-
-	u = (double*)malloc((last-first+1) * sizeof(double));
-	u[0] = 0.0;
-	for(i = first+1; i <= last; i++) {
-		u[i-first] = u[i-first-1] + 
-			sqrt(((tmp=(d[i].fx - d[i-1].fx))*tmp) + ((tmp=(d[i].fy - d[i-1].fy))*tmp));
-		}
-	for(i = first +1; i <= last; i++) {
-		u[i-first] = u[i-first] / u[last-first];
-		}
-	return u;
-}
-
-double
-Bezier::ComputeMaxError(lfPOINT *d, int first, int last, lfPOINT *bezCurve, double *u, int *splitPoint)
-{
-	int i;
-	double maxDist, dist;
-	lfPOINT P, v;
-
-	*splitPoint = (last - first + 1)>>1;
-	maxDist = -1.0;
-	for(i = first +1; i < last; i++) {
-		P = fBezier(3, bezCurve, u[i-first]);
-		v.fx = P.fx - d[i].fx;			v.fy = P.fy - d[i].fy;
-		dist = v.fx * v.fx + v.fy * v.fy;
-		if(dist >= maxDist) {
-			maxDist = dist;		*splitPoint = i;
-			}
-		}
-	return maxDist;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// polygons are based on the polyline object
-polygon::polygon(GraphObj *par, DataObj *d, lfPOINT *fpts, int cpts):
-	polyline(par, d, fpts, cpts)
-{
-	Id = GO_POLYGON;
-	memcpy(&pgLine, defs.pgLineDEF(0L), sizeof(LineDEF));
-	type = 1;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// rectangle with absolute coordinates
-rectangle::rectangle(GraphObj *par, DataObj *d, lfPOINT *p1, lfPOINT *p2):
-	GraphObj(par, d)
-{
-	double dx = 0.0, dy = 0.0;
-
-	FileIO(INIT_VARS);
-	if(parent){
-		dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
-		}
-	memcpy(&fp1, p1, sizeof(lfPOINT));	memcpy(&fp2, p2, sizeof(lfPOINT));
-	fp1.fx -= dx;	fp1.fy -= dy;		fp2.fx -= dx;	fp2.fy -= dy;
-	type = 0;
-	Id = GO_RECTANGLE;
-	bModified = false;
-}
-
-rectangle::rectangle(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	type = 0;
-	bModified = false;
-}
-
-rectangle::~rectangle()
-{
-	Command(CMD_FLUSH, 0L, 0L);
-	if(bModified) Undo.InvalidGO(this);
-	if(drc) delete(drc);					drc = 0L;
-}
-
-double
-rectangle::GetSize(int select)
-{
-	if(parent && parent->Id== GO_GROUP){
-	switch(select) {
-		case SIZE_XPOS:		return fp1.fx + parent->GetSize(SIZE_XPOS);
-		case SIZE_XPOS+1:	return fp2.fx + parent->GetSize(SIZE_XPOS);
-		case SIZE_YPOS:		return fp1.fy + parent->GetSize(SIZE_YPOS);
-		case SIZE_YPOS+1:	return fp2.fy + parent->GetSize(SIZE_YPOS);
-		case SIZE_GRECT_LEFT:	case SIZE_GRECT_TOP:
-		case SIZE_GRECT_RIGHT:	case SIZE_GRECT_BOTTOM:
-			return parent->GetSize(select);
-			}
-		return 0.0;
-		}
-	switch(select) {
-	case SIZE_XPOS:		return fp1.fx;
-	case SIZE_XPOS+1:	return fp2.fx;
-	case SIZE_YPOS:		return fp1.fy;
-	case SIZE_YPOS+1:	return fp2.fy;
-	case SIZE_GRECT_LEFT:	case SIZE_GRECT_TOP:
-	case SIZE_GRECT_RIGHT:	case SIZE_GRECT_BOTTOM:
-		if(parent) return parent->GetSize(select);
-		break;
-		}
-	return 0.0;
-}
-
-bool
-rectangle::SetSize(int select, double value)
-{
-	switch(select & 0xfff) {
-	case SIZE_XPOS:				fp1.fx = value;			return true;
-	case SIZE_XPOS+1:			fp2.fx = value;			return true;
-	case SIZE_YPOS:				fp1.fy = value;			return true;
-	case SIZE_YPOS+1:			fp2.fy = value;			return true;
-		}
-	return false;
-}
-
-void 
-rectangle::DoMark(anyOutput *o, bool mark)
-{
-	RECT upd;
-
-	if(!drc) drc = new dragRect(this, 0);
-	memcpy(&upd, &rDims, sizeof(RECT));
-	if(mark){
-		if(drc) drc->DoPlot(o);
-		}
-	else if(parent)	parent->DoPlot(o);
-	IncrementMinMaxRect(&upd, 6);
-	o->UpdateRect(&upd, false);
-}
-
-void
-rectangle::DoPlot(anyOutput *o)
-{
-	int x1, y1, x2, y2;
-	double tmp, dx, dy;
-
-	if(!parent || !o) return;
-	dx = parent->GetSize(SIZE_GRECT_LEFT);		dy = parent->GetSize(SIZE_GRECT_TOP);
-	if(fp1.fx > fp2.fx) {
-		tmp = fp2.fx;	fp2.fx = fp1.fx;	fp1.fx = tmp;
-		}
-	if(fp1.fy > fp2.fy) {
-		tmp = fp2.fy;	fp2.fy = fp1.fy;	fp1.fy = tmp;
-		}
-	if(type == 2) PlotRoundRect(o);
-	else {
-		x1 = o->co2ix(fp1.fx+dx);			y1 = o->co2iy(fp1.fy+dy);
-		x2 = o->co2ix(fp2.fx+dx);			y2 = o->co2iy(fp2.fy+dy);
-		o->SetLine(&Line);				o->SetFill(&Fill);
-		if(type == 1) o->oCircle(x1, y1, x2, y2, name);
-		else o->oRectangle(x1, y1, x2, y2, name);
-		SetMinMaxRect(&rDims, x1, y1, x2, y2);
-		}
-	o->MrkMode = MRK_NONE;
-	if(CurrGO == this) o->ShowMark(this, MRK_GODRAW);
-}
-
-bool
-rectangle::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-
-	switch (cmd) {
-	case CMD_FLUSH:
-		if(pts) free(pts);		pts = 0L;
-		if(name) free(name);	name = 0L;
-		return true;
-	case CMD_SCALE:
-		fp1.fx = ((scaleINFO*)tmpl)->sx.fx + fp1.fx * ((scaleINFO*)tmpl)->sx.fy;
-		fp2.fx = ((scaleINFO*)tmpl)->sx.fx + fp2.fx * ((scaleINFO*)tmpl)->sx.fy;
-		fp1.fy = ((scaleINFO*)tmpl)->sy.fx + fp1.fy * ((scaleINFO*)tmpl)->sy.fy;
-		fp2.fy = ((scaleINFO*)tmpl)->sy.fx + fp2.fy * ((scaleINFO*)tmpl)->sy.fy;
-		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
-		FillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;	FillLine.width *= ((scaleINFO*)tmpl)->sy.fy;
-		Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;			rad *= ((scaleINFO*)tmpl)->sy.fy;
-		return true;
-	case CMD_SAVEPOS:
-		bModified = true;
-		Undo.SaveLFP(this, &fp1, 0L);
-		Undo.SaveLFP(this, &fp2, UNDO_CONTINUE);
-		return true;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, mev->x, mev->y) && !(CurrGO) && (o)){
-				return o->ShowMark(this, MRK_GODRAW);
-				}
-			}
-		return false;
-	case CMD_SET_DATAOBJ:
-		switch (type) {
-		case 1:		Id = GO_ELLIPSE;		break;
-		case 2:		Id = GO_ROUNDREC;		break;
-		default:	Id = GO_RECTANGLE;		break;
-			}
-		return true;
-	case CMD_MOVE:
-		bModified = true;
-		Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
-	case CMD_UNDO_MOVE:
-		fp1.fx += ((lfPOINT*)tmpl)[0].fx;	fp1.fy += ((lfPOINT*)tmpl)[0].fy;
-		fp2.fx += ((lfPOINT*)tmpl)[0].fx;	fp2.fy += ((lfPOINT*)tmpl)[0].fy;
-		CurrGO = this;
-	case CMD_REDRAW:
-		if(parent && cmd != CMD_UNDO_MOVE){
-			parent->Command(CMD_REDRAW, tmpl, o);
-			}
-		return true;
-	case CMD_SETSCROLL:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		}
-	return false;
-}
-
-void
-rectangle::Track(POINT *p, anyOutput *o)
-{
-	POINT tpts[5];
-	RECT old_rc;
-	double dx, dy;
-
-	if(o && parent){
-		dx = parent->GetSize(SIZE_GRECT_LEFT);		dy = parent->GetSize(SIZE_GRECT_TOP);
-		memcpy(&old_rc, &rDims, sizeof(rDims));
-		o->UpdateRect(&rDims, false);
-		tpts[0].x = tpts[1].x = tpts[4].x = o->co2ix(fp1.fx+dx)+p->x;		
-		tpts[0].y = tpts[3].y = tpts[4].y = o->co2iy(fp1.fy+dy)+p->y;
-		tpts[1].y = tpts[2].y = o->co2iy(fp2.fy+dy)+p->y;
-		tpts[2].x = tpts[3].x = o->co2ix(fp2.fx+dx)+p->x;
-		UpdateMinMaxRect(&rDims, tpts[0].x, tpts[0].y);
-		UpdateMinMaxRect(&rDims, tpts[2].x, tpts[2].y);	
-		if(old_rc.left != rDims.left || old_rc.right != rDims.right || old_rc.top !=
-			rDims.top || old_rc.bottom != rDims.bottom)IncrementMinMaxRect(&rDims, 3);
-		o->ShowLine(tpts, 5, Line.color);
-		if(type == 1) o->ShowEllipse(tpts[0], tpts[2], Line.color);
-		}
-}
-
-void *
-rectangle::ObjThere(int x, int y)
-{
-	if(drc) return drc->ObjThere(x, y);
-	return 0L;
-}
-
-//use circular Bresenham's algorithm to draw rounded rectangles
-//Ref: C. Montani, R. Scopigno (1990) "Speres-To-Voxel Conversion", in:
-//   Graphic Gems (A.S. Glassner ed.) Academic Press, Inc.; 
-//   ISBN 0-12-288165-5 
-void
-rectangle::PlotRoundRect(anyOutput *o)
-{
-	int i, m, x, y, di, de, lim, ir, x1, x2, y1, y2;
-	double dx, dy;
-	POINT np;
-
-	dx = parent->GetSize(SIZE_GRECT_LEFT);		dy = parent->GetSize(SIZE_GRECT_TOP);
-	ir = o->un2ix(rad);
-	x1 = o->co2ix(fp1.fx+dx);			y1 = o->co2iy(fp1.fy+dy);
-	x2 = o->co2ix(fp2.fx+dx);			y2 = o->co2iy(fp2.fy+dy);
-	if (x1 > x2) Swap(x1, x2);		if(y1 > y2) Swap(y1, y2);
-	if(pts) free(pts);				nPts = 0;
-	m = ir*4+10;
-	if(!(pts = (POINT*)malloc(m*sizeof(POINT))))return;
-	for(i = 0; i < 4; i++) {
-		x = lim = 0;	y = ir;	di = 2*(1-ir);
-		while (y >= lim){
-			if(di < 0) {
-				de = 2*di + 2*y -1;
-				if(de > 0) {
-					x++;	y--;	di += (2*x -2*y +2);
-					}
-				else {
-					x++;	di += (2*x +1);
-					}
-				}
-			else {
-				de = 2*di -2*x -1;
-				if(de > 0) {
-					y--;	di += (-2*y +1);
-					}
-				else {
-					x++;	y--;	di += (2*x -2*y +2);
-					}
-				}
-			switch(i) {
-			case 0:
-				np.x = x2-ir+x;		np.y = y2-ir+y;
-				break;
-			case 1:
-				np.x = x2-ir+y;		np.y = y1+ir-x;
-				break;
-			case 2: 
-				np.x = x1+ir-x;		np.y = y1+ir-y;
-				break;
-			case 3:
-				np.x = x1+ir-y;		np.y = y2-ir+x;
-				break;
-				}
-			AddToPolygon(&nPts, pts, &np);
-			}
-		}
-	AddToPolygon(&nPts, pts, pts);	//close polygon
-	o->SetLine(&Line);				o->SetFill(&Fill);
-	o->oPolygon(pts, nPts, name);
-	SetMinMaxRect(&rDims, x1, y1, x2, y2);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// ellipse with absolute coordinates
-ellipse::ellipse(GraphObj *par, DataObj *d, lfPOINT *p1, lfPOINT *p2)
-	:rectangle(par, d, p1, p2)
-{
-	type = 1;
-	Id = GO_ELLIPSE;
-}
-
-ellipse::ellipse(int src)
-	:rectangle(src)
-{
-	type = 1;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// rounded rectangle
-roundrec::roundrec(GraphObj *par, DataObj *d, lfPOINT *p1, lfPOINT *p2)
-	:rectangle(par, d, p1, p2)
-{
-	type = 2;
-	Id = GO_ROUNDREC;
-}
-
-roundrec::roundrec(int src)
-	:rectangle(src)
-{
-	type = 2;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Add a legend to the graph
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-LegItem::LegItem(GraphObj *par, DataObj *d, LineDEF *ld, LineDEF *lf, FillDEF *fd, char *desc)
-	:GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	if(!ld && !fd && lf) ld = lf;
-	if(ld) {
-		memcpy(&DataLine, ld, sizeof(LineDEF));
-		flags |= 0x01;
-		}
-	if(lf) memcpy(&OutLine, lf, sizeof(LineDEF));
-	if(fd) {
-		if(fd->hatch) memcpy(&HatchLine, fd->hatch, sizeof(LineDEF));
-		memcpy(&Fill, fd, sizeof(FillDEF));
-		Fill.hatch = &HatchLine;
-		flags |= 0x02;
-		}
-	DefDesc(desc);		Id = GO_LEGITEM;		moveable = 1;
-}
-
-LegItem::LegItem(GraphObj *par, DataObj *d, LineDEF *ld, Symbol *sy)
-	:GraphObj(par, d)
-{
-	FileIO(INIT_VARS);
-	if(ld) {
-		memcpy(&DataLine, ld, sizeof(LineDEF));		flags |= 0x01;
-		}
-	if(sy) {
-		Sym = sy;		Sym->parent = this;			flags |= 0x04;
-		}
-	DefDesc(sy ? sy->name : 0L);		Id = GO_LEGITEM;		moveable = 1;
-}
-
-LegItem::LegItem(GraphObj *par, DataObj *d, LineDEF *ld, int err, char *desc)
-	:GraphObj(par, d)
-{
-	FileIO(INIT_VARS);		flags |= (0x80 | (err & 0x7f));
-	if(ld) {
-		memcpy(&OutLine, ld, sizeof(LineDEF));
-		}
-	DefDesc(desc);		Id = GO_LEGITEM;		moveable = 1;
-}
-
-LegItem::LegItem(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	moveable = 1;
-}
-
-LegItem::~LegItem()
-{
-	if(Sym) DeleteGO(Sym);		Sym = 0L;
-	if(Desc) DeleteGO(Desc);	Desc = 0L;
-}
-
-double
-LegItem::GetSize(int select)
-{
-	switch(select) {
-	case SIZE_XCENTER:
-		return (parent->GetSize((flags & 0x01) ? SIZE_XPOS+2 : SIZE_XPOS) + 
-			parent->GetSize((flags & 0x01) ? SIZE_XPOS+3 : SIZE_XPOS+1))/2.0;
-	case SIZE_YCENTER:
-		return (parent->GetSize(SIZE_YPOS) + parent->GetSize(SIZE_YPOS+1))/2.0;
-	case SIZE_LB_XPOS:		case SIZE_LB_YPOS:
-	case SIZE_GRECT_TOP:	case SIZE_GRECT_LEFT:
-	default:
-		if(parent) return parent->GetSize(select);
-		break;
-		}
-	return 0.0;
-}
-
-void
-LegItem::DoPlot(anyOutput *o)
-{
-	POINT pts[3];
-	int ie, cy;
-
-	if(!parent || !o) return;
-	hcr.top = iround(parent->GetSize(SIZE_YPOS));
-	hcr.bottom = iround(parent->GetSize(SIZE_YPOS+1));
-	if(flags & 0x80) {
-		hcr.left = iround(parent->GetSize((flags & 0x40) ? SIZE_XPOS+2 : SIZE_XPOS));
-		hcr.right = iround(parent->GetSize((flags & 0x40) ? SIZE_XPOS+3 :SIZE_XPOS+1));
-		ie = o->un2ix(DefSize(SIZE_ERRBAR)/2.0);
-		o->SetLine(&OutLine);						cy = (hcr.top + hcr.bottom)>>1;
-		if((flags & 0x3f) == 0x01) {
-			pts[0].x = pts[1].x = (hcr.right + hcr.left)>>1;
-			pts[0].y = hcr.top;		pts[1].y = hcr.bottom;
-			o->oPolyline(pts, 2, 0L);		
-			pts[0].x -= (ie-1);		pts[1].x += ie;		pts[0].y = pts[1].y = hcr.top;
-			o->oPolyline(pts, 2, 0L);					pts[0].y = pts[1].y = hcr.bottom;
-			o->oPolyline(pts, 2, 0L);
-			}
-		else if((flags & 0x3f) == 0x02) {
-			pts[0].x = pts[1].x = ((hcr.right + hcr.left)>>1);
-			pts[0].y = pts[1].y = cy;			cy = ((hcr.bottom - hcr.top)>>1);
-			pts[0].x -= cy;						pts[2].x = (pts[1].x += cy);
-			o->oPolyline(pts, 2, 0L);			pts[1].x = pts[0].x;
-			pts[0].y -= ie;						pts[1].y += ie;
-			o->oPolyline(pts, 2, 0L);
-			pts[0].x = pts[1].x = pts[2].x;		o->oPolyline(pts, 2, 0L);
-			}
-		else if((flags & 0x3f) == 0x03) {
-			pts[0].x = pts[1].x = (hcr.right + hcr.left)>>1;
-			pts[0].y = hcr.top;		pts[1].y = hcr.bottom;
-			o->oPolyline(pts, 2, 0L);			pts[0].x -= (ie-1);
-			pts[0].y = pts[1].y = hcr.top;		o->oPolyline(pts, 2, 0L);
-			pts[0].y = pts[1].y = hcr.bottom;	pts[0].x += (ie + ie -1);
-			o->oPolyline(pts, 2, 0L);
-			}
-		else if((flags & 0x3f) == 0x04) {
-			pts[0].x = pts[1].x = (hcr.right + hcr.left)>>1;
-			pts[0].y = hcr.top;		pts[1].y = hcr.bottom;
-			o->oPolyline(pts, 2, 0L);			pts[0].x += ie;
-			pts[0].y = pts[1].y = hcr.top;		o->oPolyline(pts, 2, 0L);
-			pts[0].y = pts[1].y = hcr.bottom;	pts[0].x -= (ie + ie -1);
-			o->oPolyline(pts, 2, 0L);
-			}
-		else if((flags & 0x3f) == 0x05) {
-			pts[0].x = pts[1].x = (hcr.right + hcr.left)>>1;
-			pts[0].y = hcr.top;		pts[1].y = hcr.bottom;
-			o->oPolyline(pts, 2, 0L);
-			}
-		}
-	else {
-		hcr.left = iround(parent->GetSize((flags & 0x01) ? SIZE_XPOS+2 : SIZE_XPOS));
-		hcr.right = iround(parent->GetSize((flags & 0x01) ? SIZE_XPOS+3 :SIZE_XPOS+1));
-		if(flags & 0x02){
-			o->SetLine(&OutLine);	o->SetFill(&Fill);
-			o->oRectangle(hcr.left, hcr.top, hcr.right, hcr.bottom, name);
-			}
-		if(flags & 0x01){
-			pts[0].x = hcr.left;	pts[1].x = hcr.right;
-			pts[0].y = pts[1].y = iround(GetSize(SIZE_YCENTER))+1;
-			o->SetLine(&DataLine);	o->oPolyline(pts, 2, 0L);
-			}
-		if(flags & 0x04){
-			if(Sym) Sym->DoPlot(o);
-			}
-		}
-	SetMinMaxRect(&rDims, hcr.left, hcr.top, hcr.right, hcr.bottom);
-	if(Desc) {
-		Desc->moveable = 1;			Desc->DoPlot(o);
-		if(Desc->rDims.bottom > rDims.bottom){
-			parent->SetSize(SIZE_YPOS+1, (double)Desc->rDims.bottom);
-			}
-		UpdateMinMaxRect(&rDims, Desc->rDims.left, Desc->rDims.top);
-		UpdateMinMaxRect(&rDims, Desc->rDims.right, Desc->rDims.bottom);
-		}
-}
-
-void
-LegItem::DoMark(anyOutput *o, bool mark)
-{
-	RECT cr;
-	LineDEF ld = {0.0, 1.0, 0x00000000L, 0x00000000L};
-	POINT pts[5];
-
-	if(!parent || !o) return;
-	cr.left = hcr.left-5;			cr.right = hcr.right+3;
-	cr.top = hcr.top-3;				cr.bottom = hcr.bottom+1;
-	ld.color = mark ? 0x00000000L : 0x00ffffffL;	o->SetLine(&ld);
-	pts[0].x = pts[3].x = pts[4].x = cr.left;	pts[0].y = pts[1].y = pts[4].y = cr.top;
-	pts[1].x = pts[2].x = cr.right;				pts[2].y = pts[3].y = cr.bottom;
-	o->oPolyline(pts, 5, name);					IncrementMinMaxRect(&cr, 3);
-	o->UpdateRect(&cr, false);
-}
-
-bool
-LegItem::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	GraphObj **tmpPlots;
-
-	switch(cmd){
-	case CMD_TEXTTHERE:
-		if(Desc && Desc->Command(cmd, tmpl, o)) return true;
-		return false;
-	case CMD_MOUSE_EVENT:
-		if(tmpl && IsInRect(&rDims, ((MouseEvent*)tmpl)->x, ((MouseEvent*)tmpl)->y) && o) {
-			if(Desc && Desc->Command(cmd, tmpl, o)) return true;
-			if(!CurrGO) o->ShowMark(CurrGO=this, MRK_GODRAW);
-			}
-		break;
-	case CMD_SCALE:
-		DataLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;		DataLine.width *= ((scaleINFO*)tmpl)->sy.fy;
-		OutLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;			OutLine.width *= ((scaleINFO*)tmpl)->sy.fy;
-		HatchLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;		HatchLine.width *= ((scaleINFO*)tmpl)->sy.fy;
-		Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;	
-		if(Sym) Sym->Command(cmd, tmpl, o);
-		if(Desc) Desc->Command(cmd, tmpl, o);
-		break;
-	case CMD_REDRAW:	case CMD_MOVE:
-		if(parent) return parent->Command(cmd, tmpl, o);
-		break;
-	case CMD_MUTATE:
-		if(!parent || !(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
-		if(Desc == tmpPlots[0]) {
-			Undo.MutateGO((GraphObj**)&Desc, tmpPlots[1], 0L, o);
-			return true;
-			}
-		break;
-	case CMD_SET_DATAOBJ:
-		if(Desc) Desc->Command(cmd, tmpl, o);
-		Id = GO_LEGITEM;
-		data = (DataObj *)tmpl;
-		return true;
-		}
-	return false;
-}
-
-void
-LegItem::Track(POINT *p, anyOutput *o)
-{
-	if(parent) parent->Track(p, o);
-}
-
-bool
-LegItem::HasFill(LineDEF *ld, FillDEF *fd, char *desc)
-{
-	if(ld && cmpLineDEF(ld, &OutLine)) return false;
-	if(fd && cmpFillDEF(fd, &Fill)) return false;
-	if(fd && fd->hatch && cmpLineDEF(fd->hatch, &HatchLine)) return false;
-	return true;
-}
-
-bool
-LegItem::HasSym(LineDEF *ld, GraphObj *sy)
-{
-	if(sy && !Sym) return false;
-	if(sy->Id == GO_SYMBOL && (sy->type & 0xfff) != (Sym->type & 0xfff)) return false;
-	if(sy && Sym) {
-		if(Sym->GetSize(SIZE_SYMBOL) != sy->GetSize(SIZE_SYMBOL)) return false;
-		if(Sym->GetSize(SIZE_SYM_LINE) != sy->GetSize(SIZE_SYM_LINE)) return false;
-		if(Sym->GetColor(COL_SYM_LINE) != sy->GetColor(COL_SYM_LINE)) return false;
-		if(Sym->GetColor(COL_SYM_FILL) != sy->GetColor(COL_SYM_FILL)) return false;
-		}
-	if(ld && cmpLineDEF(ld, &DataLine)) return false;
-	return true;
-}
-
-bool
-LegItem::HasErr(LineDEF *ld, int err)
-{
-	if((((DWORD)err & 0x1f) | 0x80) != (flags & 0x9f)) return false;
-	if(ld && cmpLineDEF(ld, &OutLine)) return false;
-	return true;
-}
-
-void
-LegItem::DefDesc(char *txt)
-{
-	TextDEF td;
-	int cb;
-
-	cb = txt && *txt ? (int)strlen(txt) : 20;
-	if(cb < 20) cb = 20;
-	td.ColTxt = 0x00000000L;		td.ColBg = 0x00ffffffL;
-	td.fSize = DefSize(SIZE_TICK_LABELS);
-	td.RotBL = td.RotCHAR = 0.0;
-	td.iSize = 0;					td.Align = TXA_VTOP | TXA_HLEFT;
-	td.Mode = TXM_TRANSPARENT;		td.Style = TXS_NORMAL;
-	td.Font = FONT_HELVETICA;		td.text = (char*)malloc(cb+2);
-	rlp_strcpy(td.text, cb+2, txt ? txt : (char*)"text");
-	Desc = new Label(this, data, 0, 0, &td, LB_X_PARENT | LB_Y_PARENT);
-}
-
-Legend::Legend(GraphObj *par, DataObj *d):GraphObj(par, d)
-{
-	FileIO(INIT_VARS);		Id = GO_LEGEND;		moveable = true;
-}
-
-Legend::Legend(int src):GraphObj(0L, 0L)
-{
-	FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		}
-	moveable = true;
-}
-
-Legend::~Legend()
-{
-	int i;
-
-	if(Items) {
-		for(i = 0; i< nItems; i++) if(Items[i]) DeleteGO(Items[i]);
-		free(Items);	Items = 0L;
-		}
-	if(to) DelBitmapClass(to);		to = 0L;
-}
-double
-Legend::GetSize(int select)
-{
-	switch(select) {
-	case SIZE_XPOS:		return C_Rect.Xmin;
-	case SIZE_XPOS+1:	return C_Rect.Xmax;
-	case SIZE_XPOS+2:	return E_Rect.Xmin;
-	case SIZE_XPOS+3:	return E_Rect.Xmax;
-	case SIZE_YPOS:		return C_Rect.Ymin;
-	case SIZE_YPOS+1:	return C_Rect.Ymax;
-	case SIZE_LB_XPOS:	return lb_pos.fx;
-	case SIZE_LB_YPOS:	return lb_pos.fy;
-	case SIZE_GRECT_TOP:	case SIZE_GRECT_LEFT:
-		if(parent) return parent->GetSize(select);
-		break;
-		}
-	return 0.0;
-}
-
-bool
-Legend::SetSize(int select, double value)
-{
-	double tmp;
-
-	switch (select & 0xfff){
-	case SIZE_XPOS:		pos.fx = value;		return true;
-	case SIZE_YPOS:		pos.fy = value;		return true;
-	case SIZE_YPOS+1:
-		tmp = value - C_Rect.Ymax;
-		C_Rect.Ymin += tmp;		C_Rect.Ymax += tmp;		lb_pos.fy +=tmp;
-		}
-	return false;
-}
-
-void
-Legend::DoPlot(anyOutput *o)
-{
-	int i;
-	double y_inc, dx, dy;
-
-	if(!o || !Items) return;
-	if(to) DelBitmapClass(to);		to = 0L;
-	dx = parent->GetSize(SIZE_GRECT_LEFT);		dy = parent->GetSize(SIZE_GRECT_TOP);
-	C_Rect.Xmin = o->co2fix(pos.fx + B_Rect.Xmin + D_Rect.Xmin + dx);
-	C_Rect.Ymin = o->co2fiy(pos.fy + B_Rect.Ymin + D_Rect.Ymin + dy);
-	C_Rect.Xmax = C_Rect.Xmin + o->un2fix(D_Rect.Xmax - D_Rect.Xmin);
-	C_Rect.Ymax = C_Rect.Ymin + o->un2fix(D_Rect.Ymax - D_Rect.Ymin);
-	E_Rect.Ymin = C_Rect.Ymin;	E_Rect.Ymax = C_Rect.Ymax;
-	E_Rect.Xmin = o->co2fix(pos.fx + B_Rect.Xmin + F_Rect.Xmin + dx);
-	E_Rect.Xmax = E_Rect.Xmin + o->un2fix(F_Rect.Xmax - F_Rect.Xmin);
-	y_inc = floor(0.5+o->un2fiy(B_Rect.Ymax - B_Rect.Ymin));
-	rDims.top = iround(C_Rect.Ymin); rDims.bottom = iround(C_Rect.Ymax);
-	rDims.left = rDims.right = iround(C_Rect.Xmin);	
-	lb_pos.fx = o->co2fix(pos.fx + B_Rect.Xmax + dx);	lb_pos.fy = C_Rect.Ymin;
-	//draw all items
-	for(i = 0; i < nItems; i++) {
-		if(Items[i]){
-			if((Items[i]->flags & 0x11) == 0x01) hasLine = true;
-			Items[i]->DoPlot(o);
-			UpdateMinMaxRect(&rDims, Items[i]->rDims.left, Items[i]->rDims.top);
-			UpdateMinMaxRect(&rDims, Items[i]->rDims.right, Items[i]->rDims.bottom);
-			C_Rect.Ymin += y_inc;		C_Rect.Ymax += y_inc;
-			lb_pos.fy += y_inc;
-			}
-		}
-	IncrementMinMaxRect(&rDims, 6);
-}
-
-void
-Legend::DoMark(anyOutput *o, bool mark)
-{
-	RECT cr;
-	LineDEF ld = {0.0, 1.0, 0x00c0c0c0L, 0x00000000L};
-	POINT pts[5];
-
-	if(!parent || !o) return;
-	cr.left = rDims.left;			cr.right = rDims.right;
-	cr.top = rDims.top;				cr.bottom = rDims.bottom;
-	ld.color = mark ? 0x00c0c0c0L : 0x00ffffffL;	o->SetLine(&ld);
-	pts[0].x = pts[3].x = pts[4].x = cr.left;	pts[0].y = pts[1].y = pts[4].y = cr.top;
-	pts[1].x = pts[2].x = cr.right;				pts[2].y = pts[3].y = cr.bottom;
-	o->oPolyline(pts, 5, name);					IncrementMinMaxRect(&cr, 3);
-	o->UpdateRect(&cr, false);
-}
-
-bool
-Legend::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-
-	switch(cmd){
-	case CMD_TEXTTHERE:
-		if(Items) for(i = 0; i< nItems; i++) 
-			if(Items[i] && Items[i]->Command(cmd, tmpl, o)) return true;
-		return false;
-	case CMD_MOUSE_EVENT:
-		if(o && tmpl && IsInRect(&rDims, ((MouseEvent*)tmpl)->x, ((MouseEvent*)tmpl)->y)) {
-			if(Items) for(i = 0; i< nItems; i++) 
-				if(Items[i] && Items[i]->Command(cmd, tmpl, o)) return true;
-			if(!CurrGO) o->ShowMark(CurrGO = this, MRK_GODRAW);
-			}
-		break;
-	case CMD_SET_DATAOBJ:
-		if(Items) for(i = 0; i < nItems; i++) if(Items[i]) Items[i]->Command(cmd, tmpl, o);
-		Id = GO_LEGEND;
-		data = (DataObj *)tmpl;
-		return true;
-	case CMD_SCALE:
-		pos.fx *= ((scaleINFO*)tmpl)->sx.fy;			pos.fy *= ((scaleINFO*)tmpl)->sy.fy;
-		lb_pos.fx *= ((scaleINFO*)tmpl)->sx.fy;			lb_pos.fy *= ((scaleINFO*)tmpl)->sy.fy;
-		B_Rect.Xmax *= ((scaleINFO*)tmpl)->sx.fy;		B_Rect.Xmin *= ((scaleINFO*)tmpl)->sx.fy;
-		B_Rect.Ymax *= ((scaleINFO*)tmpl)->sy.fy;		B_Rect.Ymin *= ((scaleINFO*)tmpl)->sy.fy;
-		C_Rect.Xmax *= ((scaleINFO*)tmpl)->sx.fy;		C_Rect.Xmin *= ((scaleINFO*)tmpl)->sx.fy;
-		C_Rect.Ymax *= ((scaleINFO*)tmpl)->sy.fy;		C_Rect.Ymin *= ((scaleINFO*)tmpl)->sy.fy;
-		D_Rect.Xmax *= ((scaleINFO*)tmpl)->sx.fy;		D_Rect.Xmin *= ((scaleINFO*)tmpl)->sx.fy;
-		D_Rect.Ymax *= ((scaleINFO*)tmpl)->sy.fy;		D_Rect.Ymin *= ((scaleINFO*)tmpl)->sy.fy;
-		E_Rect.Xmax *= ((scaleINFO*)tmpl)->sx.fy;		E_Rect.Xmin *= ((scaleINFO*)tmpl)->sx.fy;
-		E_Rect.Ymax *= ((scaleINFO*)tmpl)->sy.fy;		E_Rect.Ymin *= ((scaleINFO*)tmpl)->sy.fy;
-		F_Rect.Xmax *= ((scaleINFO*)tmpl)->sx.fy;		F_Rect.Xmin *= ((scaleINFO*)tmpl)->sx.fy;
-		F_Rect.Ymax *= ((scaleINFO*)tmpl)->sy.fy;		F_Rect.Ymin *= ((scaleINFO*)tmpl)->sy.fy;
-		if(Items) for(i = 0; i < nItems; i++) if(Items[i]) Items[i]->Command(cmd, tmpl, o);
-		return true;
-	case CMD_DELOBJ:
-		o->HideMark();
-		if(Items && parent) for(i = 0; i < nItems; i++) {
-			if(Items[i] && tmpl == (void *)Items[i]) {
-				Undo.DeleteGO((GraphObj**)(&Items[i]), 0L, o);
-				parent->Command(CMD_REDRAW, NULL, o);
-				return true;
-				}
-			}
-		break;
-	case CMD_MOVE:
-		Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
-	case CMD_UNDO_MOVE:
-		pos.fx += ((lfPOINT*)tmpl)[0].fx;	pos.fy += ((lfPOINT*)tmpl)[0].fy;
-		CurrGO = this;
-	case CMD_REDRAW:
-		if(parent && cmd != CMD_UNDO_MOVE){
-			parent->Command(CMD_REDRAW, tmpl, o);
-			}
-		return true;
-	case CMD_DROP_OBJECT:
-		if(!tmpl) return false;
-		if(!(Items = (LegItem**)realloc(Items, (2+nItems)*sizeof(LegItem*))))return false;
-		Items[nItems++] = (LegItem*)tmpl;
-		Items[nItems-1]->parent = this;
-		return true;
-		}
-	return false;
-}
-
-void
-Legend::Track(POINT *p, anyOutput *o)
-{
-	POINT pts[5];
-	LineDEF tld = {0.0, 1.0, 0x00c0c0c0, 0x0L};
-
-	if(!p || !o) return;
-	if(to) {
-		o->CopyBitmap(trc.left, trc.top, to, 0, 0, trc.right - trc.left, 
-			trc.bottom - trc.top, false);
-		DelBitmapClass(to);		to = 0L;
-		o->UpdateRect(&trc, false);
-		}
-	trc.left = pts[0].x = pts[1].x = pts[4].x = rDims.left + p->x;		
-	trc.top = pts[0].y = pts[3].y = pts[4].y = rDims.top + p->y;
-	trc.bottom = pts[1].y = pts[2].y = rDims.bottom + p->y;
-	trc.right = pts[2].x = pts[3].x = rDims.right + p->x;
-	IncrementMinMaxRect(&trc, 3);	to = GetRectBitmap(&trc, o);
-	o->SetLine(&tld);				o->oPolyline(pts, 5, 0L);
-	o->UpdateRect(&trc, false);
-} 
-
-bool
-Legend::HasFill(LineDEF *ld, FillDEF *fd, char *desc)
-{
-	int i;
-	LegItem *li;
-
-	if(Items) for(i = 0; i < nItems; i++) {
-		if(Items[i] && Items[i]->HasFill(ld, fd, desc)) return true;
-		}
-	if(li = new LegItem(this, data, 0L, ld, fd, desc)){
-		if(!(Command(CMD_DROP_OBJECT, li, 0L))) DeleteGO(li);
-		}
-	return false;
-}
-
-bool
-Legend::HasSym(LineDEF *ld, GraphObj *sy, char *desc)
-{
-	int i, sym;
-	Symbol *ns;
-	LegItem *li;
-
-	if(!parent || !sy) return true;
-	if(ld) hasLine = true;
-	if(Items) for(i = 0; i < nItems; i++) {
-		if(Items[i] && Items[i]->HasSym(ld, sy)) return true;
-		}
-	sym = sy->Id == GO_SYMBOL ? (sy->type & 0xff) : 0;
-	if(!(ns = new Symbol(this, data, 0.0, 0.0, sym | SYM_POS_PARENT))) return true;
-	ns->SetSize(SIZE_SYMBOL, sy->GetSize(SIZE_SYMBOL));
-	ns->SetSize(SIZE_SYM_LINE, sy->GetSize(SIZE_SYM_LINE));
-	ns->SetColor(COL_SYM_LINE, sy->GetColor(COL_SYM_LINE));
-	ns->SetColor(COL_SYM_FILL, sy->GetColor(COL_SYM_FILL));
-	if(desc && desc[0]) {
-		ns->name = (char*)memdup(desc,(int)strlen(desc)+1, 0); 
-		}
-	else if(sy->name && sy->name[0]) {
-		ns->name = (char*)memdup(sy->name,(int)strlen(sy->name)+1, 0); 
-		}
-	else if(sy->parent && sy->parent->Id < GO_GRAPH && sy->parent->Id >= GO_PLOT) {
-		if(((Plot*)(sy->parent))->data_desc) ns->name = (char*)memdup(((Plot*)(sy->parent))->data_desc,
-			(int)strlen(((Plot*)(sy->parent))->data_desc)+1, 0);
-		}
-	else if(sy->parent && sy->parent->name && sy->parent->name[0]) {
-		ns->name = (char*)memdup(sy->parent->name,(int)strlen(sy->parent->name)+1, 0); 
-		}
-	if(li = new LegItem(this, data, ld, ns)){
-		if(!(Command(CMD_DROP_OBJECT, li, 0L))) DeleteGO(li);
-		}
-	return false;
-}
-
-bool
-Legend::HasErr(LineDEF *ld, int err, char *desc)
-{
-	int i;
-	LegItem *li;
-
-	if(Items) for(i = 0; i < nItems; i++) {
-		if(Items[i] && Items[i]->HasErr(ld, err)) return true;
-		}
-	if(li = new LegItem(this, data, ld, err | (hasLine ? 0x40 : 0), desc)){
-		if(!(Command(CMD_DROP_OBJECT, li, 0L))) DeleteGO(li);
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Graphs are graphic objects containing plots, axes, and drawn objects
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Graph::Graph(GraphObj *par, DataObj *d, anyOutput *o, int style):GraphObj(par, d)
-{
-	Graph::FileIO(INIT_VARS);
-	Disp = o;		Id = GO_GRAPH;		cGraphs++;	bModified = true;
-	if(style & 0x10) y_axis.flags |= AXIS_INVERT;
-	if (!d && parent) parent->Command(CMD_DELOBJ, this, NULL);
-}
-
-Graph::Graph(int src):GraphObj(0L, 0L)
-{
-	int i;
-
-	Graph::FileIO(INIT_VARS);
-	if(src == FILE_READ) {
-		FileIO(FILE_READ);
-		x_axis.owner = y_axis.owner = (void *)this;
-		//do all axes
-		for(i = 0; Axes && i< NumAxes; i++) if(Axes[i]) Axes[i]->parent = this;
-		//do all plots
-		for(i = 0; Plots && i< NumPlots; i++) if(Plots[i]) Plots[i]->parent = this;
-		if(x_axis.max > x_axis.min && y_axis.max > y_axis.min &&
-			Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin) dirty = false;
-		}
-	cGraphs++;		bModified = false;
-}
-
-Graph::~Graph()
-{
-	int i;
-
-	if(!parent) return;			parent = 0L;
-	Undo.InvalidGO(this);		DoZoom("reset");
-	if(CurrGraph == this) CurrGraph = 0L;
-	if(Plots) {
-		for(i = 0; i< NumPlots; i++) if(Plots[i]) DeleteGO(Plots[i]);
-		free(Plots);		Plots = 0L;		NumPlots = 0;
-		}
-	if(Axes) {
-		for(i = 0; i< NumAxes; i++) if(Axes[i]) DeleteGO(Axes[i]);
-		free(Axes);			Axes = 0L;		NumAxes = 0;
-		}
-	if(OwnDisp && Disp) DelDispClass(Disp);		OwnDisp = false;	Disp = 0L;
-	if(frm_g) DeleteGO(frm_g);		if(frm_d) DeleteGO(frm_d);
-	if(x_axis.breaks && x_axis.owner == this) free(x_axis.breaks);
-	if(y_axis.breaks && y_axis.owner == this) free(y_axis.breaks);
-	if(tl_pts) free(tl_pts);
-	if(nscp > 0 && nscp <= NumPlots && Sc_Plots) free(Sc_Plots);
-	nscp = 0;			Sc_Plots = 0L;
-	if(name) free(name);		name = 0L;
-	if(filename) free(filename);	filename= 0L;
-}
-
-double
-Graph::GetSize(int select)
-{
-	return Graph::DefSize(select);
-}
-
-bool
-Graph::SetSize(int select, double val)
-{
-	switch(select & 0xfff) {
-	case SIZE_GRECT_TOP:	GRect.Ymin = val;	return true;
-	case SIZE_GRECT_BOTTOM:	GRect.Ymax = val;	return true;
-	case SIZE_GRECT_LEFT:	GRect.Xmin = val;	return true;
-	case SIZE_GRECT_RIGHT:	GRect.Xmax = val;	return true;
-	case SIZE_DRECT_TOP:	DRect.Ymin = val;	return true;
-	case SIZE_DRECT_BOTTOM:	DRect.Ymax = val;	return true;
-	case SIZE_DRECT_LEFT:	DRect.Xmin = val;	return true;
-	case SIZE_DRECT_RIGHT:	DRect.Xmax = val;	return true;
-	default: return false;
-		}
-}
-
-DWORD
-Graph::GetColor(int select)
-{
-	switch(select & 0xfff) {
-	case COL_AXIS:		return ColAX;
-	case COL_BG:		return ColDR;
-	case COL_DRECT:		return ColDR;
-	case COL_GRECT:		return ColGR;
-		}
-	if(parent) return parent->GetColor(select);
-	else return defs.Color(select);
-}
-
-bool
-Graph::SetColor(int select, DWORD col)
-{
-	switch(select) {
-	case COL_DRECT:
-		ColDR = col;		return true;
-	case COL_GRECT:
-		ColGR = col;		return true;
-		}
-	return false;
-}
-
-void
-Graph::DoPlot(anyOutput *target)
-{
-	int i, cb;
-	AxisDEF *ax;
-
-	if(nscp > 0 && nscp <= NumPlots && Sc_Plots) free(Sc_Plots);
-	nscp = 0;		Sc_Plots = 0L;
-	rc_mrk.left = rc_mrk.right = rc_mrk.top = rc_mrk.bottom = -1;
-	CurrAxes = Axes;
-	if(data) do_formula(data, 0L);			//init mfcalc
-	//every graph needs axes!
-	if(type == GT_STANDARD && !NumAxes) CreateAxes(AxisTempl);
-	//verify ownership of axes
-	if(Axes) for (i = 0; i < NumAxes; i++){
-		if(Axes[i] && (ax = Axes[i]->GetAxis()))
-			if(ax == &x_axis || ax == &y_axis) ax->owner = this;
-		}
-	if(!name){
-#ifdef USE_WIN_SECURE
-		cb = sprintf_s(TmpTxt, TMP_TXT_SIZE, "Graph %d", cGraphs) + 2;
-#else
-		cb = sprintf(TmpTxt, "Graph %d", cGraphs) + 2;
-#endif
-		name = (char*)realloc(name, cb);		rlp_strcpy(name, cb, TmpTxt);
-		}
-	if(!target && !Disp) {
-		Disp = NewDispClass(this);
-		Disp->SetMenu(MENU_GRAPH);
-		if(name) Disp->Caption(name);
-		Disp->VPorg.fy = (double)Disp->MenuHeight;
-		Disp->CheckMenu(ToolMode, true);
-		OwnDisp = true;						defs.SetDisp(Disp);
-		bModified = false;			//first graph is not modified!
-		Undo.SetDisp(Disp);
-		}
-	//the first output class is the display class
-	if(!Disp && (!(Disp = target))) return;
-	Disp->ActualSize(&defs.clipRC);
-	if(OwnDisp) Disp->Erase(Disp->dFillCol = defs.Color(COL_BG));
-	CurrDisp = target ? target : Disp;
-	CurrDisp->MrkMode = MRK_NONE;
-	CurrRect.Xmin=GRect.Xmin + DRect.Xmin;	CurrRect.Xmax=GRect.Xmin + DRect.Xmax;
-	CurrRect.Ymin=GRect.Ymin + DRect.Ymax;	CurrRect.Ymax=GRect.Ymin + DRect.Ymin;
-	if(dirty) DoAutoscale();
-	CurrDisp->SetRect(CurrRect, units, &x_axis, &y_axis);
-	CurrDisp->disp_x = CurrDisp->un2fix(GRect.Xmin);
-	CurrDisp->disp_y = CurrDisp->un2fiy(GRect.Ymin);
-	if(!frm_g && !(frm_g = new FrmRect(this, 0L, &GRect, 0L))) return;
-	frm_g->SetColor(COL_GRECT, ColGR);	frm_g->SetColor(COL_GRECTLINE, ColGRL);
-	frm_g->DoPlot(CurrDisp);
-	if(type == GT_STANDARD) {
-		if(!frm_d && !(frm_d = new FrmRect(this, &GRect, &DRect, 0L))) return;
-		SetMinMaxRect(&rDims, CurrDisp->co2ix(CurrRect.Xmin), CurrDisp->co2iy(CurrRect.Ymax), 
-			CurrDisp->co2ix(CurrRect.Xmax), CurrDisp->co2iy(CurrRect.Ymin));
-		frm_g->Command(CMD_MINRC, &rDims, CurrDisp);
-		SetMinMaxRect(&rDims, CurrDisp->co2ix(GRect.Xmin), CurrDisp->co2iy(GRect.Ymax), 
-			CurrDisp->co2ix(GRect.Xmax), CurrDisp->co2iy(GRect.Ymin));
-		frm_d->Command(CMD_MAXRC, &rDims, CurrDisp);
-		frm_d->SetColor(COL_DRECT, ColDR);		frm_d->DoPlot(CurrDisp);
-		frm_g->Command(CMD_SETCHILD, &DRect, CurrDisp);
-		}
-	//do all axes
-	if(Axes) for(i = 0; i< NumAxes; i++) if(Axes[i]){
-		Axes[i]->SetColor(COL_BG, ColGR);
-		Axes[i]->DoPlot(CurrDisp);
-		}
-	//do all plots
-	if(Plots) for(i = 0; i < NumPlots; i++) if(Plots[i]) {
-		if(Plots[i]->Id >= GO_PLOT && Plots[i]->Id < GO_GRAPH) {
-			if(((Plot*)Plots[i])->hidden == 0) Plots[i]->DoPlot(CurrDisp);
-			}
-		else {
-			Plots[i]->DoPlot(CurrDisp);
-			}
-		}
-	if(bModified) data->Command(CMD_MRK_DIRTY, 0L, 0L);
-	if(parent && parent->Id == GO_GRAPH) {
-		parent->Command(CMD_AXIS, 0L, 0L);
-		}
-	if(target && ToolMode == TM_STANDARD) target->MouseCursor(MC_ARROW, false);
-}
-
-bool
-Graph::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-	GraphObj **tmpPlots;
-	char *f_name;
-	RECT rc;
-	int i, j;
-	DWORD delflg = 0L;
-
-	if(!o) o = Disp ? Disp : CurrDisp;
-	switch (cmd){
-    case CMD_CAN_CLOSE:
-		HideTextCursor();
-		if(bModified) {
-			if (Undo.isEmpty(CurrDisp)) return true;
-			bModified = false;
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "%s has not been saved.\nDo you want to save it now?", name);
-#else
-			sprintf(TmpTxt, "%s has not been saved.\nDo you want to save it now?", name);
-#endif
-			i = YesNoCancelBox(TmpTxt);
-			if(i == 2) return false;
-			else if(i == 1) if(! SaveGraphAs(this)) return false;
-			}
-		return true;
-	case CMD_SCALE:
-		return DoScale((scaleINFO*)tmpl, o);
-	case CMD_LAYERS:
-		Undo.SetDisp(o ? o : CurrDisp);
-		return ShowLayers(this);
-	case CMD_HASSTACK:
-		return(NumPlots > 1);
-	case CMD_SAVEPOS:
-		Undo.ValRect(this, &GRect, 0L);
-		Undo.ValRect(this, &DRect, UNDO_CONTINUE);
-		return true;
-	case CMD_LEGEND:
-		Undo.SetDisp(o ? o : CurrDisp);
-		if(Id == GO_PAGE) {
-			if(CurrGraph && CurrGraph->parent == this) return CurrGraph->Command(cmd, tmpl, o);
-			if(!CurrGraph && NumPlots == 1 && Plots[0] && Plots[0]->Id == GO_GRAPH){
-				CurrGraph = (Graph*)Plots[0];
-				return CurrGraph->Command(cmd, tmpl, o);
-				}
-			InfoBox("No graph selected!\nCreate a new graph first or select\n"
-				"a graph before you can add a legend.");
-			return false;
-			}
-		if(Id == GO_GRAPH && !tmpl && Plots) {
-			for(i = 0; i< NumPlots; i++){
-				if(Plots[i] && Plots[i]->Id == GO_LEGEND) {
-					tmpl = (void*)Plots[i];
-					Undo.ObjConf(Plots[i], 0L);
-					break;
-					}
-				}
-			if(!tmpl) {
-				if(!(tmpl = (void*) new Legend(this, data)))return false;
-				if(Disp) {
-					Undo.SetDisp(Disp);
-					tmpPlots = (GraphObj**)memdup(Plots, sizeof(GraphObj*) * (NumPlots+2), 0);
-					if(!tmpPlots) return false;
-					Undo.ListGOmoved(Plots, tmpPlots, NumPlots);
-					Undo.SetGO(this, &tmpPlots[NumPlots++], (Plot *)tmpl, 0L);
-					free(Plots);		Plots = tmpPlots;	bModified = true;
-					}
-				else if(Plots = (GraphObj**)realloc(Plots, sizeof(GraphObj*) * (NumPlots+2))){
-					Plots[NumPlots++] = (GraphObj*)tmpl;	Plots[NumPlots] = 0L;
-					}
-				else return false;
-				}
-			if(type == GT_CIRCCHART)((Legend*)tmpl)->SetSize(SIZE_XPOS, DRect.Xmin*3.0);
-			for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->Command(cmd, tmpl,o);
-			if(Disp) Command(CMD_REDRAW, 0L, CurrDisp);
-			}
-		else if(Id == GO_GRAPH) {
-			for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->Command(cmd, tmpl,o);
-			}
-		return true;
-	case CMD_REPL_GO:
-		if(!(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
-		if(Axes) for(i = 0; i < NumAxes; i++) if(Axes[i] && Axes[i] == tmpPlots[0]){
-			tmpPlots[1]->parent = this;
-			tmpPlots[1]->Command(CMD_SET_DATAOBJ, data, o);
-			Axes[i] = (Axis *)tmpPlots[1];
-			tmpPlots[0]->parent = 0L;		//disable messaging
-			//check for default axes
-			if(((Axis*)tmpPlots[0])->GetAxis() == &x_axis) {
-				if(x_axis.breaks) free(x_axis.breaks);
-				memcpy(&x_axis, Axes[i]->axis, sizeof(AxisDEF));
-				if(x_axis.owner == Axes[i]) free(Axes[i]->axis);
-				Axes[i]->axis = &x_axis;			x_axis.owner = this;
-				}
-			else if(((Axis*)tmpPlots[0])->GetAxis() == &y_axis) {
-				if(y_axis.breaks) free(y_axis.breaks);
-				memcpy(&y_axis, Axes[i]->axis, sizeof(AxisDEF));
-				if(y_axis.owner == Axes[i]) free(Axes[i]->axis);
-				Axes[i]->axis = &y_axis;			y_axis.owner = this;
-				}
-			DeleteGO(tmpPlots[0]);
-			return bModified = dirty = true;
-			}
-		if(Plots) for(i = 0; i < NumPlots; i++) if(Plots[i] && Plots[i] == tmpPlots[0]) { 
-			return bModified = dirty = ReplaceGO((GraphObj**)&Plots[i], tmpPlots);
-			}
-		return false;
-	case CMD_MUTATE:
-		if(!parent || !(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
-		if(Plots) for (i = 0; i < NumPlots; i++) if(Plots[i] && Plots[i] == tmpPlots[0]) {
-			Undo.MutateGO((GraphObj**)&Plots[i], tmpPlots[1], 0L, o);
-			if(ToolMode) Command(CMD_TOOLMODE, 0L, o);
-			return bModified = true;
-			}
-		break;
-	case CMD_REDRAW:
-		if(!CurrDisp) {
-			DoPlot(CurrDisp);			return true;
-			}
-		if(Disp)CurrDisp = Disp;		bDialogOpen = false;
-		if(parent && (parent->Id == GO_PAGE || parent->Id == GO_GRAPH)) return parent->Command(cmd, tmpl, o);
-		if(CurrDisp && CurrDisp->Erase(ColBG)) CurrDisp->StartPage();
-		CurrDisp->MrkMode = MRK_NONE;			DoPlot(CurrDisp);
-		if(CurrDisp) CurrDisp->EndPage();		if(CurrGO == this) CurrGO = 0L;
-		if(CurrGO && CurrGO == CurrLabel && CurrLabel->Id == GO_LABEL)
-			CurrDisp->ShowMark(CurrLabel, MRK_GODRAW);
-		return true;
-	case CMD_ZOOM:
-		return DoZoom((char*)tmpl);
-	case CMD_MOUSECURSOR:
-		if(o)o->MouseCursor(PasteObj ? MC_PASTE : MC_ARROW, false);	
-		return true;
-	case CMD_AXIS:			//one of the plots has changed scaling: reset
-		CurrAxes = Axes;
-		if(o)o->SetRect(CurrRect, units, &x_axis, &y_axis);
-		return true;
-	case CMD_REG_AXISPLOT:	//notification: plot can handle its own axes
-		if(nscp > 0 && nscp <= NumPlots && Sc_Plots)  {
-			for(i = 0; i < nscp; i++)
-				if(Sc_Plots[i] == (GraphObj*)tmpl) return true;
-			if(tmpPlots = (GraphObj**)realloc(Sc_Plots, (nscp+1)*sizeof(GraphObj*))){
-				tmpPlots[nscp++] = (GraphObj *)tmpl;
-				Sc_Plots = tmpPlots;
-				}
-			else {		//memory allocation error
-				nscp = 0;
-				Sc_Plots = 0L;
-				}
-			}
-		else {
-			if(Sc_Plots = (GraphObj **)calloc(1, sizeof(GraphObj*))){
-				Sc_Plots[0] = (GraphObj *)tmpl;
-				nscp = 1;
-				}
-			else nscp = 0;
-			}
-		return true;
-	case CMD_BUSY:
-		if(Disp) Disp->MouseCursor(MC_WAIT, true);
-		break;
-	case CMD_UNDO:
-		Command(CMD_TOOLMODE, 0L, o);
-		if(CurrDisp) CurrDisp->MouseCursor(MC_WAIT, true);
-		Undo.Restore(true, CurrDisp);
-		if(CurrDisp) CurrDisp->MouseCursor(MC_ARROW, true);
-		return true;
-	case CMD_ADDAXIS:
-		Undo.SetDisp(o ? o : CurrDisp);		Command(CMD_TOOLMODE, 0L, o);
-		if(Id == GO_PAGE) {
-			if(CurrGraph && CurrGraph->parent == this) return CurrGraph->Command(cmd, tmpl, o);
-			if(!CurrGraph && NumPlots == 1 && Plots[0] && Plots[0]->Id == GO_GRAPH){
-				CurrGraph = (Graph*)Plots[0];
-				return CurrGraph->Command(cmd, tmpl, o);
-				}
-			InfoBox("No graph selected!\nCreate a new graph first or select\n"
-				"a graph before you can add an axis.");
-			return false;
-			}
-		else {
-			if(type == GT_3D && Plots){
-				for(i = 0; i < NumPlots; i++)
-					if(Plots[i] && (Plots[i]->Id == GO_PLOT3D || Plots[i]->Id == GO_FUNC3D
-						|| Plots[i]->Id == GO_FITFUNC3D))
-						return Plots[i]->Command(cmd, tmpl, o); 
-				}
-			else if(AddAxis()) {
-				Command(CMD_REDRAW, tmpl, o);
-				return true;
-				}
-			}
-		return false;
-	case CMD_CONFIG:
-		Command(CMD_TOOLMODE, 0L, o);		if(CurrGO == this) CurrGO = 0L;
-		return Configure();
-	case CMD_FILENAME:
-		if(tmpl) {
-			i = (int)strlen((char*)tmpl)+2;
-			filename = (char*)realloc(filename, i );
-			rlp_strcpy(filename, i, (char*)tmpl);
-			}
-		break;
-	case CMD_SETNAME:
-		if(OwnDisp && CurrDisp && tmpl && *((char*)tmpl)){
-			CurrDisp->Caption((char*)tmpl);
-			i = (int)strlen((char*)tmpl);
-			j = name && name[0] ? (int)strlen(name) : i;
-			name = (char*)realloc(name, i > j ? i+2 : j+2);
-			rlp_strcpy(name, i+2, (char*)tmpl);
-			}
-		else return false;
-		return true;
-	case CMD_SET_DATAOBJ:
-		Id = GO_GRAPH;
-		data = (DataObj *)tmpl;
-		//do axes
-		if(Axes) for(i = 0; i< NumAxes; i++) if(Axes[i]) Axes[i]->Command(cmd, tmpl, o);
-		//do all plots
-		if(Plots) for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->Command(cmd, tmpl,o);
-		return true;
-	case CMD_OPEN:
-		Command(CMD_TOOLMODE, 0L, o);
-		if(!parent) return false;
-		f_name = OpenGraphName(filename);
-		if(f_name && f_name[0]) {
-			if(data) data->Command(CMD_UPDHISTORY, 0L, 0L);
-			return OpenGraph(Id == GO_PAGE ? this : parent, f_name, 0L, false);
-			}
-		return true;
-	case CMD_UPDHISTORY:
-		if(data) data->Command(CMD_UPDHISTORY, 0L, 0L);
-		return true;
-	case CMD_UPDATE:
-		Command(CMD_TOOLMODE, 0L, o);
-		Undo.SetDisp(CurrDisp);
-		if(parent && parent->Id != GO_PAGE && parent->Id != GO_GRAPH){
-			Undo.ValInt(this, &ToolMode, 0L);		//stub, all plots have UNDO_CONTINUE
-			}
-		if(CurrDisp) CurrDisp->MouseCursor(MC_WAIT, true);
-		for(i = 0; i < NumAxes; i++) if(Axes[i]) Axes[i]->Command(cmd, tmpl, o);
-		if(CurrDisp) CurrDisp->MouseCursor(MC_WAIT, false);
-		for(i = 0; Plots && i < NumPlots; i++) 
-			if(Plots[i]) Plots[i]->Command(cmd, tmpl, o);
-		dirty = bModified = true;		CurrDisp->StartPage();
-		if(CurrDisp) CurrDisp->MouseCursor(MC_WAIT, false);
-		DoPlot(CurrDisp);		CurrDisp->EndPage();		CurrGO = 0L;
-		return true;
-	case CMD_DELOBJ_CONT:
-		delflg = UNDO_CONTINUE;
-	case CMD_DELOBJ:
-		Command(CMD_TOOLMODE, 0L, o);
-		bModified = true;
-		if(!tmpl) return false;
-		for(i = 0; i < NumAxes; i++) if(Axes[i] && (void*)Axes[i] == tmpl){
-			if(Axes[i]->Command(CMD_CAN_CLOSE, 0L, o)) {
-				Undo.DeleteGO((GraphObj**)(&Axes[i]), delflg, o);
-				return Command(CMD_REDRAW, 0L, o);
-				}
-			else {
-				InfoBox("Axes used for scaling\ncan not be deleted.");
-				return false;
-				}
-			}
-		for(i = 0; Plots && i < NumPlots; i++) if(Plots[i] && (void*)Plots[i] == tmpl) {
-			Undo.DeleteGO(&Plots[i], delflg, o);			HideTextCursor();
-			Undo.StoreListGO(this, &Plots, &NumPlots, UNDO_CONTINUE);
-			for(i = j = 0; i < NumPlots; i++) if(Plots[i]) Plots[j++] = Plots[i];
-			NumPlots = j;			//compress list of objects
-			return Command(CMD_REDRAW, NULL, o);
-			}
-		if(tmpl == (void*)frm_g && parent && (parent->Id == GO_PAGE || parent->Id == GO_GRAPH)) 
-			return parent->Command(CMD_DELOBJ_CONT, (void*)this, o);
-		return false;
-	case CMD_TOOLMODE:
-		if(o && tmpl) {
-			Undo.SetDisp(o);
-			o->CheckMenu(ToolMode & 0x0f, false);
-			o->CheckMenu(ToolMode = tmpl ? (*((int*)tmpl)) & 0x0f : TM_STANDARD, true);
-			}
-		if(tl_pts && tl_nPts) free(tl_pts);
-		tl_pts = 0L;	tl_nPts = 0;
-		if(CurrDisp) CurrDisp->MrkMode = MRK_NONE;
-		if(o) switch(ToolMode & 0x0f) {
-		case TM_TEXT:	
-			o->MouseCursor(MC_TEXT, false);			break;
-		case TM_DRAW:		case TM_POLYLINE:		case TM_POLYGON:
-			o->MouseCursor(MC_DRAWPEN, false);		break;
-		case TM_RECTANGLE:	
-			o->MouseCursor(MC_DRAWREC, false);		break;
-		case TM_ELLIPSE:		
-			o->MouseCursor(MC_DRAWELLY, false);		break;
-		case TM_ROUNDREC:
-			o->MouseCursor(MC_DRAWRREC, false);		break;
-		case TM_ARROW:
-			o->MouseCursor(MC_CROSS, false);		break;
-		default:
-			o->MouseCursor(MC_ARROW, true);			break;
-			}
-		if((ToolMode & 0x0f) != TM_TEXT) HideTextCursor();
-		return Command(CMD_REDRAW, 0L, CurrDisp);
-	case CMD_ADDPLOT:
-		Undo.SetDisp(o ? o : CurrDisp);
-		if(Id == GO_PAGE) {
-			if(CurrGraph && CurrGraph->parent == this) return CurrGraph->Command(cmd, tmpl, o);
-			if(!CurrGraph && NumPlots == 1 && Plots[0] && Plots[0]->Id == GO_GRAPH){
-				CurrGraph = (Graph*)Plots[0];
-				return CurrGraph->Command(cmd, tmpl, o);
-				}
-			InfoBox("No graph selected!\nCreate a new graph first or select\n"
-				"a graph before you can add a plot.");
-			return false;
-			}
-		else if(Id == GO_GRAPH) {
-			if(type == GT_3D && Plots){
-				for(i = 0; i < NumPlots; i++) { 
-					if(Plots[i] && (Plots[i]->Id == GO_PLOT3D || Plots[i]->Id == GO_FUNC3D
-						|| Plots[i]->Id == GO_FITFUNC3D)) {
-						if(Plots[i]->Command(cmd, tmpl, CurrDisp)) return Command(CMD_REDRAW, 0L, o);
-						else return false;
-						}
-					}
-				return false;
-				}
-			else return AddPlot(0x0);
-			}
-		return false;
-	case CMD_MRK_DIRTY:
-		return (dirty = true);
-	case CMD_CURRLEFT:	case CMD_CURRIGHT:	case CMD_ADDCHAR:	case CMD_ADDCHARW:
-	case CMD_BACKSP:	case CMD_POS_FIRST:	case CMD_POS_LAST:
-		defs.SetDisp(o);
-		if(tmpl && *((int*)tmpl) == 27) {			//Escape
-			HideCopyMark();
-			if(CurrGO && CurrGO->Id == GO_TEXTFRAME) {
-				CurrGO->DoMark(o, false);				o->MrkMode = MRK_NONE;
-				CurrGO = 0L;
-				}
-			else o->HideMark();
-			CurrLabel = 0L;								HideTextCursor();
-			}
-		if(CurrLabel) return CurrLabel->Command(cmd, tmpl, o);
-		if(CurrGO && CurrGO->Id == GO_TEXTFRAME) return CurrGO->Command(cmd, tmpl, o);
-		else if(CurrGO && CurrGO->Id != GO_LABEL && tmpl && *((int*)tmpl) == 13) {
-			CurrGO->PropertyDlg();
-			}
-	case CMD_CURRUP:	case CMD_CURRDOWN:
-		if(CurrLabel && CurrLabel == CurrGO){
-			if(CurrLabel->parent && CurrLabel->parent->Id == GO_MLABEL)
-				return CurrLabel->parent->Command(cmd, tmpl, o);
-			return true;
-			}
-		else if(CurrGO && CurrGO->Id == GO_TEXTFRAME) return CurrGO->Command(cmd, tmpl, o);
-	case CMD_SHIFTLEFT:	case CMD_SHIFTRIGHT:	case CMD_SHIFTUP:	case CMD_SHIFTDOWN:
-		if(Id == GO_PAGE || (CurrGraph && CurrGraph->parent == this)) {
-			if(CurrGraph && CurrGraph->parent == this) return CurrGraph->Command(cmd, tmpl, o);
-			else if(CurrGO && CurrGO->Id == GO_TEXTFRAME) return CurrGO->Command(cmd, tmpl, o);
-			else if(CurrGO && CurrGO->Id == GO_LABEL) return CurrGO->Command(cmd, tmpl, o);
-			}
-		else {
-			if(type == GT_3D && Plots) {
-				for(i = 0; i < NumPlots; i++) {
-					if(Plots[i] && (Plots[i]->Id == GO_PLOT3D || Plots[i]->Id == GO_FUNC3D
-						|| Plots[i]->Id == GO_FITFUNC3D))
-						return Plots[i]->Command(cmd, tmpl, CurrDisp);
-					}
-				}
-			else if(CurrGO && CurrGO->Id == GO_TEXTFRAME) return CurrGO->Command(cmd, tmpl, o);
-			else if(CurrGO && CurrGO->Id == GO_LABEL) return CurrGO->Command(cmd, tmpl, o);
-			}
-		return false;
-	case CMD_MOVE_TOP:	case CMD_MOVE_UP:
-	case CMD_MOVE_DOWN:	case CMD_MOVE_BOTTOM:
-		Undo.StoreListGO(this, &Plots, &NumPlots, 0L);
-		if(MoveObj(cmd, (GraphObj *)tmpl)){
-			bModified = true;
-			CurrDisp->StartPage();			DoPlot(CurrDisp);
-			CurrDisp->EndPage();			return true;
-			}
-		return false;
-	case CMD_DELETE:
-		if(!CurrGO) return false;			bModified = true;
-		if(CurrGO->Id == GO_TEXTFRAME) return CurrGO->Command(cmd, tmpl, o);
-		if(CurrGO == CurrLabel) return CurrLabel->Command(cmd, tmpl, o);
-		if(CurrGO->Id == GO_FRAMERECT) if(!(CurrGO = CurrGO->parent))return false;
-		if(CurrGO->parent == this) return Command(CMD_DELOBJ, (void*)CurrGO, o);
-		if(CurrGO->parent)return CurrGO->parent->Command(CMD_DELOBJ, (void*)CurrGO, o);
-		return false;
-	case CMD_DROP_GRAPH:
-		if(Disp) {
-			if(!(tmpPlots = (GraphObj**)memdup(Plots, (NumPlots+2)*sizeof(GraphObj*), 0))) 
-				return false;
-			Undo.ListGOmoved(Plots, tmpPlots, NumPlots);
-			free(Plots);			Plots = tmpPlots;	Plots[NumPlots] = Plots[NumPlots+1] = 0L;
-			Undo.SetGO(this, &Plots[NumPlots], (GraphObj*)tmpl, 0L);
-			}
-		else if(Plots = (GraphObj**)realloc(Plots, sizeof(GraphObj*) * (NumPlots+2))){
-			Plots[NumPlots] = (GraphObj*)tmpl;	Plots[NumPlots+1] = 0L;
-			}
-		else return false;
-		if(Plots[NumPlots]){
-			Plots[NumPlots]->parent = this;
-			Plots[NumPlots]->Command(CMD_SET_DATAOBJ, data, 0L);
-			if(Plots[NumPlots]->Id == GO_GRAPH) CurrGraph = (Graph*)Plots[NumPlots];
-			Plots[NumPlots]->moveable = 1;		//all page items should be freely
-			}									//   moveable by user
-		NumPlots++;
-		if(CurrDisp) {
-			CurrDisp->StartPage();		DoPlot(CurrDisp);		CurrDisp->EndPage();
-			}
-		return true;
-	case CMD_DROP_PLOT:
-		if(!tmpl) return false;
-		((GraphObj*)tmpl)->parent = this;
-		if(((GraphObj*)tmpl)->Id < GO_PLOT) {
-			if(!NumPlots && Id != GO_PAGE) return false;
-			Plots = (GraphObj**)realloc(Plots, (NumPlots+2)*sizeof(GraphObj*));
-			Plots[NumPlots] = (GraphObj*)tmpl;
-			NumPlots++;
-			if(CurrDisp) {
-				CurrDisp->StartPage();
-				DoPlot(CurrDisp);
-				CurrDisp->EndPage();
-				}
-			return true;
-			}
-		if(Id == GO_GRAPH) CurrGraph = this;	bModified =true;
-		if(!NumPlots) {
-			Plots = (GraphObj**)calloc(2, sizeof(GraphObj*));
-			if(Plots) {
-				Plots[0] = (Plot *)tmpl;	Plots[0]->parent = this;
-				switch(Plots[0]->Id) {
-				case GO_PIECHART:		case GO_RINGCHART:	case GO_STARCHART:
-					type = GT_CIRCCHART;
-					break;
-				case GO_POLARPLOT:
-					type = GT_POLARPLOT;
-					break;
-				case GO_SCATT3D:		case GO_PLOT3D:		case GO_FUNC3D:
-				case GO_FITFUNC3D:
-					type = GT_3D;
-					break;
-				default:
-					type = GT_STANDARD;
-					break;
-					}
-				Bounds.Xmin = x_axis.min = ((Plot*)Plots[0])->Bounds.Xmin;
-				Bounds.Xmax = x_axis.max = ((Plot*)Plots[0])->Bounds.Xmax;
-				Bounds.Ymin = y_axis.min = ((Plot*)Plots[0])->Bounds.Ymin;
-				Bounds.Ymax = y_axis.max = ((Plot*)Plots[0])->Bounds.Ymax;
-				if(Bounds.Ymax == Bounds.Ymin) {
-					if(Bounds.Ymax != 0.0f) {
-						Bounds.Ymax = y_axis.max = Bounds.Ymax + (Bounds.Ymax)/10.0f;
-						Bounds.Ymin = y_axis.min = Bounds.Ymin - (Bounds.Ymax)/10.0f;
-						}
-					else {
-						Bounds.Ymax = y_axis.max = 1.0f;
-						Bounds.Ymin = y_axis.min = -1.0f;
-						}
-					}
-				if(Bounds.Xmax == Bounds.Xmin) {
-					if(Bounds.Xmax != 0.0f) {
-						Bounds.Xmax = x_axis.max = Bounds.Xmax + (Bounds.Xmax)/10.0f;
-						Bounds.Xmin = x_axis.min = Bounds.Xmin - (Bounds.Xmax)/10.0f;
-						}
-					else {
-						Bounds.Xmax = 1.0f;
-						Bounds.Xmin = -1.0f;
-						}
-					}
-				NiceAxis(&x_axis, 4);				NiceAxis(&y_axis, 4);
-				NumPlots = 1;
-				dirty = false;
-				return true;
-				}
-			return false;
-			}
-		else {
-			if(Disp) {
-				Undo.SetDisp(Disp);
-				tmpPlots = (GraphObj**)memdup(Plots, sizeof(GraphObj*) * (NumPlots+2), 0);
-				Undo.ListGOmoved(Plots, tmpPlots, NumPlots);
-				Undo.SetGO(this, &tmpPlots[NumPlots++], (Plot *)tmpl, 0L);
-				free(Plots);			Plots = tmpPlots;
-				}
-			else if(Plots = (GraphObj**)realloc(Plots, sizeof(GraphObj*) * (NumPlots+2))){
-				Plots[NumPlots++] = (Plot*)tmpl;	Plots[NumPlots] = 0L;
-				}
-			else return false;
-			if(type == GT_STANDARD && ((x_axis.flags & AXIS_AUTOSCALE) || 
-				(y_axis.flags & AXIS_AUTOSCALE))) {
-				if(x_axis.flags & AXIS_AUTOSCALE) {
-					Bounds.Xmin = x_axis.min = ((Plot *)tmpl)->Bounds.Xmin < Bounds.Xmin ?
-						((Plot *)tmpl)->Bounds.Xmin : Bounds.Xmin;
-					Bounds.Xmax = x_axis.max = ((Plot *)tmpl)->Bounds.Xmax > Bounds.Xmax ?
-						((Plot *)tmpl)->Bounds.Xmax : Bounds.Xmax;
-					NiceAxis(&x_axis, 4);
-					if(Axes)for(i = 0; i < NumAxes; i++) 
-						if(Axes[i]) Axes[i]->Command(CMD_AUTOSCALE, &x_axis, o);
-					}
-				if(y_axis.flags & AXIS_AUTOSCALE) {
-					Bounds.Ymin = y_axis.min = ((Plot *)tmpl)->Bounds.Ymin < Bounds.Ymin ? 
-						((Plot *)tmpl)->Bounds.Ymin : Bounds.Ymin;
-					Bounds.Ymax = y_axis.max = ((Plot *)tmpl)->Bounds.Ymax > Bounds.Ymax ?
-						((Plot *)tmpl)->Bounds.Ymax : Bounds.Ymax;
-					NiceAxis(&y_axis, 4);
-					if(Axes)for(i = 0; i < NumAxes; i++) 
-						if(Axes[i]) Axes[i]->Command(CMD_AUTOSCALE, &y_axis, o);
-					}
-				}
-			dirty = false;
-			//Redraw graph to show all plots
-			if(CurrDisp) {
-				CurrDisp->StartPage();
-				DoPlot(CurrDisp);
-				CurrDisp->EndPage();
-				}
-			return true;
-			}
-		break;
-	case CMD_PASTE_OBJ:
-		if(!tmpl) return false;
-		Undo.SetDisp(o ? o : CurrDisp);
-		PasteObj = (GraphObj*)tmpl;
-		if(PasteObj->Id == GO_GRAPH || PasteObj->Id == GO_POLYLINE || PasteObj->Id == GO_POLYGON 
-			|| PasteObj->Id == GO_RECTANGLE || PasteObj->Id == GO_ROUNDREC || PasteObj->Id == GO_ELLIPSE
-			|| PasteObj->Id == GO_BEZIER) {
-			ToolMode = TM_PASTE;			o->MouseCursor(MC_PASTE, false);
-			return true;
-			}
-		PasteObj = 0L;
-		return false;
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *)tmpl;		defs.SetDisp(o);
-		if(CurrGO && CurrGO->moveable && mev->Action == MOUSE_LBDOWN &&
-			ToolMode == TM_STANDARD && (mev->StateFlags & 24) == 0 &&
-			(TrackGO = (GraphObj*)CurrGO->ObjThere(mev->x, mev->y))){
-			ToolMode |= TM_MOVE;
-			}
-		else if(mev->Action == MOUSE_LBDOWN){
-			if(CurrGO && (CurrGO->Id == GO_TEXTFRAME || CurrGO->Id == GO_LABEL) && CurrGO->Command(cmd, tmpl, o)) return true;
-			CurrGO = 0L;		rc_mrk.left = mev->x;		rc_mrk.top = mev->y;
-			if((ToolMode == TM_TEXT || ToolMode == TM_STANDARD) && Command(CMD_TEXTTHERE, tmpl, o)) {
-				o->CheckMenu(TM_TEXT, false);		o->CheckMenu(TM_STANDARD, true);
-				ToolMode = TM_STANDARD;				return true;
-				}
-			SuspendAnimation(o, true);
-			}
-		if(ToolMode != TM_STANDARD && ExecTool(mev)) return true;
-		switch(mev->Action) {
-		case MOUSE_RBUP:
-			i = ToolMode;							ToolMode = TM_STANDARD;
-			mev->Action = MOUSE_LBUP;				//fake select
-			CurrGO = 0L;		Command(cmd, tmpl, o);		ToolMode = i;
-			if(!CurrGO) CurrGO = this;
-			//the default behaviour for right button click is the same as for
-			//   double click: execute properties dialog, just continue.
-		case MOUSE_LBDOUBLECLICK:
-			Undo.SetDisp(o);							bDialogOpen = true;
-			if(!CurrGO){
-				mev->Action = MOUSE_LBUP;
-				Command(CMD_MOUSE_EVENT, mev, CurrDisp);
-				mev->Action = MOUSE_LBDOUBLECLICK;
-				if(!CurrGO) CurrGO = this;
-				if(CurrGO->Command(CMD_CONFIG, 0L, o))	return Command(CMD_REDRAW, 0L, o);
-				}
-			else if(CurrGO->Id < GO_PLOT) {
-				if(CurrGO->PropertyDlg()) {
-					bModified = true;					return Command(CMD_REDRAW, 0L, o);
-					}
-				}
-			else if(CurrGO->Command(CMD_CONFIG, 0L, o)) return Command(CMD_REDRAW, 0L, o);
-			else o->HideMark();
-			TrackGO = 0L;	CurrLabel = 0L;			bDialogOpen = false;
-			if(CurrGO == this) CurrGO = 0L;
-			return false;
-		case MOUSE_LBUP:
-			if(bDialogOpen) {
-				bDialogOpen = false;					return false;
-				}
-			Undo.SetDisp(o);		SuspendAnimation(o, false);
-			if(Id == GO_GRAPH && parent && parent->Id == GO_SPREADDATA){
-				CurrGO = TrackGO = 0L;
-				CurrGraph = 0L;
-				}
-		case MOUSE_MOVE:
-			if(mev->Action == MOUSE_MOVE && !(mev->StateFlags & 0x01)) return false;
-			//do all axes
-			for(i = 0; Axes && i< NumAxes; i++)
-				if(Axes[i] && Axes[i]->Command(cmd, tmpl,o)) return true;
-			//do all plots
-			if(Plots && NumPlots > 0) for(i = NumPlots-1; i>=0; i--){
-				if(Plots[i] && Plots[i]->Command(cmd, tmpl, o)){
-					if(Plots[i]->Id != GO_GRAPH && Id == GO_GRAPH) CurrGraph = this;
-					return true;
-					}
-				}
-			if(frm_d && frm_d->Command(cmd, tmpl, o)) return true;
-			if(frm_g && frm_g->Command(cmd, tmpl, o)) return true;
-			if(mev->Action == MOUSE_MOVE && ToolMode == TM_STANDARD && 
-				rc_mrk.left >=0 && rc_mrk.top >=0) ToolMode = TM_MARK;
-			if(!CurrGO) CurrGraph = 0L;
-			return false;
-			}
-		break;
-	case CMD_TEXTTHERE:
-		//do all axes
-		for(i = 0; Axes && i< NumAxes; i++)
-			if(Axes[i] && Axes[i]->Command(cmd, tmpl,o)) return true;
-		//do all plots
-		if(Plots)for(i = NumPlots-1; i>=0; i--)
-			if(Plots[i] && Plots[i]->Command(cmd, tmpl,o)) return true;
-		break;
-	case CMD_SETSCROLL:
-		if(o) {
-			if(!(o->ActualSize(&rc)))return false;
-			i = o->un2iy(GRect.Ymax);
-			o->SetScroll(true, -(i>>3), i+(i>>3), (rc.bottom -rc.top)>>1, - iround(o->VPorg.fy));
-			i = o->un2ix(GRect.Xmax);
-			o->SetScroll(false, -(i>>3), i+(i>>3), (rc.right -rc.left)>>1, - iround(o->VPorg.fx));
-			if(CurrDisp && o->Erase(ColBG)) Command(CMD_REDRAW, 0L, o);
-			return true;
-			}
-		return false;
-	case CMD_SETHPOS:
-		if(o && tmpl) o->VPorg.fx = - (double)(*((int*)tmpl));
-		return Command(CMD_SETSCROLL, tmpl, o);
-	case CMD_SETVPOS:
-		if(o && tmpl) o->VPorg.fy = - (double)(*((int*)tmpl));
-		return Command(CMD_SETSCROLL, tmpl, o);
-	case CMD_OBJTREE:
-		for(i = 0; Plots && i < NumPlots; i++) if(Plots[i]) {
-			((ObjTree*)tmpl)->Command(CMD_UPDATE, Plots[i], 0L);
-			if(Plots[i]->Id == GO_STACKBAR || Plots[i]->Id == GO_GRAPH || 
-				Plots[i]->Id == GO_PLOT3D || Plots[i]->Id == GO_POLARPLOT ||
-				Plots[i]->Id == GO_FUNC3D || Plots[i]->Id == GO_FITFUNC3D)
-				Plots[i]->Command(cmd, tmpl, o);
-			}
-		return true;
-		}
-	return false;
-}
-
-double 
-Graph::DefSize(int select)
-{
-	switch(select) {
-	case SIZE_LB_XDIST:
-	case SIZE_LB_YDIST:			return 0.0f;
-	case SIZE_GRECT_TOP:		return GRect.Ymin;
-	case SIZE_GRECT_BOTTOM:		return GRect.Ymax;
-	case SIZE_GRECT_LEFT:		return GRect.Xmin;
-	case SIZE_GRECT_RIGHT:		return GRect.Xmax;
-	case SIZE_DRECT_TOP:		return DRect.Ymin;
-	case SIZE_DRECT_BOTTOM:		return DRect.Ymax;
-	case SIZE_DRECT_LEFT:		return DRect.Xmin;
-	case SIZE_DRECT_RIGHT:		return DRect.Xmax;
-	case SIZE_BOUNDS_XMIN:		return Bounds.Xmin;
-	case SIZE_BOUNDS_XMAX:		return Bounds.Xmax;
-	case SIZE_BOUNDS_YMIN:		return Bounds.Ymin;
-	case SIZE_BOUNDS_YMAX:		return Bounds.Ymax;
-	case SIZE_SCALE:			return scale > 0.0 ? scale : 1.0;
-	case SIZE_BOUNDS_LEFT:		return x_axis.flags & AXIS_INVERT ? x_axis.max : x_axis.min;
-	case SIZE_BOUNDS_RIGHT:		return x_axis.flags & AXIS_INVERT ? x_axis.min : x_axis.max;
-	case SIZE_BOUNDS_TOP:		return y_axis.flags & AXIS_INVERT ? y_axis.min : y_axis.max;
-	case SIZE_BOUNDS_BOTTOM:	return y_axis.flags & AXIS_INVERT ? y_axis.max : y_axis.min;
-	case SIZE_YAXISX:
-		if(y_axis.flags & AXIS_X_DATA) return CurrDisp->fx2fix(y_axis.loc[0].fx);
-		else return CurrDisp->co2fix(y_axis.loc[0].fx);
-	case SIZE_XAXISY:
-		if(x_axis.flags & AXIS_Y_DATA) return CurrDisp->fy2fiy(x_axis.loc[0].fy);
-		else return CurrDisp->co2fiy(x_axis.loc[0].fy);
-		}
-	if(scale > 0.0) return scale * defs.GetSize(select);
-	else return defs.GetSize(select);
-}
-
-void
-Graph::DoAutoscale()
-{
-	int i;
-	fRECT oB;
-
-	memcpy(&oB, &Bounds, sizeof(fRECT));
-	if(type == GT_STANDARD && ((x_axis.flags & AXIS_AUTOSCALE) ||
-		(y_axis.flags & AXIS_AUTOSCALE))) {
-		for(i = 0; i < NumPlots; i++) {
-			if(Plots[i] && (Plots[i]->Id == GO_PLOTSCATT || Plots[i]->Id == GO_BUBBLEPLOT ||
-				Plots[i]->Id == GO_BOXPLOT || Plots[i]->Id == GO_STACKBAR ||
-				Plots[i]->Id == GO_STACKPG || Plots[i]->Id == GO_DENSDISP ||
-				Plots[i]->Id == GO_LIMITS || Plots[i]->Id == GO_FUNCTION ||
-				Plots[i]->Id == GO_FITFUNC || Plots[i]->Id== GO_FREQDIST)) {
-				bModified = true;
-				if(dirty) {
-					if(x_axis.flags & AXIS_AUTOSCALE) {
-						Bounds.Xmin = HUGE_VAL;			Bounds.Xmax = -HUGE_VAL;
-						}
-					if(y_axis.flags & AXIS_AUTOSCALE) {
-						Bounds.Ymin = HUGE_VAL;			Bounds.Ymax = -HUGE_VAL;
-						}
-					dirty = false;
-					}
-				Plots[i]->Command(CMD_AUTOSCALE, 0L, CurrDisp);
-				if(!((Plot*)Plots[i])->hidden) {
-					if(x_axis.flags & AXIS_AUTOSCALE) {
-						Bounds.Xmin = ((Plot*)Plots[i])->Bounds.Xmin < Bounds.Xmin ?
-							((Plot*)Plots[i])->Bounds.Xmin : Bounds.Xmin;
-						Bounds.Xmax = ((Plot*)Plots[i])->Bounds.Xmax > Bounds.Xmax ?
-							((Plot*)Plots[i])->Bounds.Xmax : Bounds.Xmax;
-						}
-					if(y_axis.flags & AXIS_AUTOSCALE) {
-						Bounds.Ymin = ((Plot*)Plots[i])->Bounds.Ymin < Bounds.Ymin ?
-							((Plot*)Plots[i])->Bounds.Ymin : Bounds.Ymin;
-						Bounds.Ymax = ((Plot*)Plots[i])->Bounds.Ymax > Bounds.Ymax ?
-							((Plot*)Plots[i])->Bounds.Ymax : Bounds.Ymax;
-						}
-					}
-				}
-			}
-		if(Bounds.Xmax <= Bounds.Xmin) {
-			Bounds.Xmax = oB.Xmax > oB.Xmin ? oB.Xmax : oB.Xmax + 1.0;
-			Bounds.Xmin = oB.Xmin < oB.Xmax ? oB.Xmin : oB.Xmin - 1.0;
-			}
-		if(Bounds.Ymax <= Bounds.Ymin) {
-			Bounds.Ymax = oB.Ymax > oB.Ymin ? oB.Ymax : oB.Ymax + 1.0;
-			Bounds.Ymin = oB.Ymin < oB.Ymax ? oB.Ymin : oB.Ymin - 1.0;
-			}
-		if(x_axis.flags & AXIS_AUTOSCALE){
-			x_axis.min = Bounds.Xmin;	x_axis.max = Bounds.Xmax;
-			NiceAxis(&x_axis, 4);
-			if(x_axis.min <= 0.0 && ((x_axis.flags & 0xf000) == AXIS_LOG ||
-				(x_axis.flags & 0xf000) == AXIS_RECI)) {
-				x_axis.min = base4log(&x_axis, 0);
-				}
-			if(Axes)for(i = 0; i < NumAxes; i++)
-				if(Axes[i]) Axes[i]->Command(CMD_AUTOSCALE, &x_axis, 0L);
-			}
-		if(y_axis.flags & AXIS_AUTOSCALE){
-			y_axis.min = Bounds.Ymin;	y_axis.max = Bounds.Ymax;
-			NiceAxis(&y_axis, 4);
-			if(y_axis.min <= 0.0 && ((y_axis.flags & 0xf000) == AXIS_LOG ||
-				(y_axis.flags & 0xf000) == AXIS_RECI)) {
-				y_axis.min = base4log(&y_axis, 1);
-				}
-			if(Axes)for(i = 0; i < NumAxes; i++)
-				if(Axes[i]) Axes[i]->Command(CMD_AUTOSCALE, &y_axis, 0L);
-			}
-		}
-	dirty = false;
-}
-
-void
-Graph::CreateAxes(int templ)
-{
-	AxisDEF tmp_axis;
-	TextDEF label_def, tlbdef;
-	char label_text[500];
-	Label *label;
-	double ts, lb_ydist, lb_xdist, tlb_dist;
-	DWORD ptick, ntick, utick;
-	char xa_desc[50], ya_desc[50];
-
-	if(Axes && NumAxes) return;
-	label_def.ColTxt = defs.Color(COL_AXIS);				label_def.ColBg = 0x00ffffffL;
-	label_def.fSize = DefSize(SIZE_TICK_LABELS)*1.2;		label_def.RotBL = label_def.RotCHAR = 0.0;
-	label_def.iSize = 0;									label_def.Align = TXA_VTOP | TXA_HCENTER;
-	label_def.Mode = TXM_TRANSPARENT;						label_def.Style = TXS_NORMAL;
-	label_def.Font = FONT_HELVETICA;						label_def.text = label_text;
-	tlbdef.ColTxt = defs.Color(COL_AXIS);					tlbdef.ColBg = 0x00ffffffL;
-	tlbdef.RotBL = tlbdef.RotCHAR = 0.0;					tlbdef.iSize = 0;
-	tlbdef.fSize = DefSize(SIZE_TICK_LABELS);				tlbdef.Align = TXA_VCENTER | TXA_HCENTER;
-	tlbdef.Style = TXS_NORMAL;								tlbdef.Mode = TXM_TRANSPARENT;
-	tlbdef.Font = FONT_HELVETICA;							tlbdef.text = 0L;
-	ts = DefSize(SIZE_AXIS_TICKS);
-	rlp_strcpy(xa_desc, 50, (char*)"x-axis");				rlp_strcpy(ya_desc, 50, (char*)"y-axis");
-	if(Plots && NumPlots && Plots[0] && Plots[0]->Id >= GO_PLOT && Plots[0]->Id < GO_GRAPH) {
-		if(((Plot*)Plots[0])->x_info) rlp_strcpy(xa_desc, 50,((Plot*)Plots[0])->x_info); 
-		if(((Plot*)Plots[0])->y_info) rlp_strcpy(ya_desc, 50,((Plot*)Plots[0])->y_info); 
-		}
-	switch (tickstyle & 0x07){
-	case 1:						//ticks inside
-		ntick = AXIS_POSTICKS;		ptick = AXIS_POSTICKS;	utick = AXIS_NEGTICKS;
-		ts *= 0.5;
-		break;
-	case 2:						//centered, symetrical
-		ptick = ntick = utick = AXIS_SYMTICKS;
-		ts *= 0.75;
-		break;
-	default:					//ticks outside
-		ptick = AXIS_NEGTICKS;		ntick = AXIS_NEGTICKS; utick = AXIS_POSTICKS;
-		break;
-		}
-	tlb_dist = NiceValue(ts * 2.0);
-	lb_ydist = NiceValue((ts+DefSize(SIZE_AXIS_TICKS))*2.0);
-	lb_xdist = NiceValue((ts+DefSize(SIZE_AXIS_TICKS))*3.0);
-	switch(templ) {
-	case 0:
-		Axes = (Axis**)calloc(5, sizeof(Axis *));
-		x_axis.loc[0].fx = GRect.Xmin + DRect.Xmin;
-		x_axis.loc[1].fx = GRect.Xmin + DRect.Xmax;
-		x_axis.loc[0].fy = x_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
-		y_axis.loc[0].fy = GRect.Ymin + DRect.Ymin;
-		y_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
-		y_axis.loc[0].fx = y_axis.loc[1].fx = GRect.Xmin + DRect.Xmin;;
-		if((Axes[0] = new Axis(this, data, &x_axis, AXIS_BOTTOM | ptick |	
-			AXIS_AUTOTICK | AXIS_AUTOSCALE | ((tickstyle & 0x100) ? AXIS_GRIDLINE : 0)))){
-			Axes[0]->SetSize(SIZE_LB_YDIST, lb_ydist);
-			Axes[0]->SetSize(SIZE_TLB_YDIST, tlb_dist);
-			rlp_strcpy(label_text, 500, xa_desc);
-			label = new Label(Axes[0], data, (DRect.Xmin+DRect.Xmax)/2.0, 
-				GRect.Ymin+DRect.Ymax+DefSize(SIZE_AXIS_TICKS)*4.0f, &label_def, LB_Y_PARENT);
-			if(label && Axes[0]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
-			else if(label) DeleteGO(label);
-			tlbdef.Align = TXA_VTOP | TXA_HCENTER;
-			Axes[0]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		if((Axes[1] = new Axis(this, data, &y_axis, y_axis.flags | AXIS_LEFT | ntick | AXIS_AUTOTICK |
-			AXIS_AUTOSCALE | ((tickstyle & 0x200) ? AXIS_GRIDLINE : 0)))){
-			Axes[1]->SetSize(SIZE_LB_XDIST, -lb_xdist); 
-			Axes[1]->SetSize(SIZE_TLB_XDIST, -tlb_dist); 
-			rlp_strcpy(label_text, 500, ya_desc);
-			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
-			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - DefSize(SIZE_AXIS_TICKS)*6.0, 
-				(DRect.Ymax+DRect.Ymin)/2.0, &label_def, LB_X_PARENT);
-			if(label && Axes[1]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
-			else if(label) DeleteGO(label);
-			tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
-			Axes[1]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		label = 0L;
-		memcpy(&tmp_axis, &x_axis, sizeof(AxisDEF));
-		tmp_axis.owner = NULL;
-		tmp_axis.loc[0].fy = tmp_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
-		if((Axes[2] = new Axis(this, data, &tmp_axis, AXIS_TOP | AXIS_NOTICKS | AXIS_AUTOTICK))){
-			Axes[2]->SetSize(SIZE_LB_YDIST, -lb_ydist); 
-			Axes[2]->SetSize(SIZE_TLB_YDIST, -tlb_dist); 
-			tlbdef.Align = TXA_VBOTTOM | TXA_HCENTER;
-			Axes[2]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		memcpy(&tmp_axis, &y_axis, sizeof(AxisDEF));
-		tmp_axis.owner = NULL;
-		tmp_axis.loc[0].fx = tmp_axis.loc[1].fx = GRect.Xmin + DRect.Xmax;
-		if((Axes[3] = new Axis(this, data, &tmp_axis, AXIS_RIGHT | AXIS_NOTICKS | AXIS_AUTOTICK))){
-			Axes[3]->SetSize(SIZE_LB_XDIST, lb_xdist); 
-			Axes[3]->SetSize(SIZE_TLB_XDIST, tlb_dist); 
-			tlbdef.Align = TXA_VCENTER | TXA_HLEFT;
-			Axes[3]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		NumAxes = 4;
-		break;
-	case 1:
-		Axes = (Axis**)calloc(7, sizeof(Axis *));
-		if(x_axis.Start >= 0.0f) x_axis.min = x_axis.Start = -x_axis.Step;
-		if(y_axis.Start >= 0.0f) y_axis.min = y_axis.Start = -y_axis.Step;
-		x_axis.loc[0].fx = GRect.Xmin + DRect.Xmin;
-		x_axis.loc[1].fx = GRect.Xmin + DRect.Xmax;
-		x_axis.loc[0].fy = x_axis.loc[1].fy = 0.0f;
-		y_axis.loc[0].fy = GRect.Ymin + DRect.Ymin;
-		y_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
-		y_axis.loc[0].fx = y_axis.loc[1].fx = 0.0f;
-		if((Axes[0] = new Axis(this, data, &x_axis, ptick | AXIS_Y_DATA |	
-			AXIS_AUTOTICK | AXIS_AUTOSCALE | ((tickstyle & 0x100) ? AXIS_GRIDLINE : 0)))){
-			Axes[0]->SetSize(SIZE_LB_YDIST, lb_ydist);
-			Axes[0]->SetSize(SIZE_TLB_YDIST, tlb_dist);
-			rlp_strcpy(label_text, 500, xa_desc);
-			label_def.Align = TXA_VTOP | TXA_HRIGHT;
-			label = new Label(Axes[0], data, GRect.Xmin + DRect.Xmax - DefSize(SIZE_AXIS_TICKS)*2.0, 
-				GRect.Ymin+DRect.Ymax+DefSize(SIZE_AXIS_TICKS)*4.0f, &label_def, LB_Y_PARENT);
-			if(label && Axes[0]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
-			else if(label) DeleteGO(label);
-			tlbdef.Align = TXA_VTOP | TXA_HCENTER;
-			Axes[0]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		if((Axes[1] = new Axis(this, data, &y_axis, ntick | AXIS_AUTOTICK | AXIS_X_DATA |
-			AXIS_AUTOSCALE | ((tickstyle & 0x200) ? AXIS_GRIDLINE : 0)))){
-			Axes[1]->SetSize(SIZE_LB_XDIST, -lb_xdist); 
-			Axes[1]->SetSize(SIZE_TLB_XDIST, -tlb_dist);
-			rlp_strcpy(label_text, 500, ya_desc);
-			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HRIGHT;
-			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - DefSize(SIZE_AXIS_TICKS)*6.0, 
-				GRect.Ymin + DRect.Ymin + DefSize(SIZE_AXIS_TICKS)*2.0, 
-				&label_def, LB_X_PARENT);
-			if(label && Axes[1]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
-			else if(label) DeleteGO(label);
-			tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
-			Axes[1]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		memcpy(&tmp_axis, &x_axis, sizeof(AxisDEF));
-		tmp_axis.owner = NULL;
-		tmp_axis.loc[0].fy = tmp_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
-		if(Axes[2] = new Axis(this, data, &tmp_axis, AXIS_TOP | AXIS_NOTICKS | AXIS_AUTOTICK)){
-			Axes[2]->SetSize(SIZE_LB_YDIST, -lb_ydist); 
-			Axes[2]->SetSize(SIZE_TLB_YDIST, -tlb_dist);
-			tlbdef.Align = TXA_VBOTTOM | TXA_HCENTER;
-			Axes[2]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		tmp_axis.loc[0].fy = tmp_axis.loc[1].fy = GRect.Ymin + DRect.Ymin;
-		if(Axes[3] = new Axis(this, data, &tmp_axis, AXIS_BOTTOM | AXIS_NOTICKS | AXIS_AUTOTICK)){
-			Axes[3]->SetSize(SIZE_LB_YDIST, lb_xdist); 
-			Axes[3]->SetSize(SIZE_TLB_YDIST, tlb_dist); 
-			tlbdef.Align = TXA_VTOP | TXA_HCENTER;
-			Axes[3]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		memcpy(&tmp_axis, &y_axis, sizeof(AxisDEF));
-		tmp_axis.owner = NULL;
-		tmp_axis.loc[0].fx = tmp_axis.loc[1].fx = GRect.Xmin + DRect.Xmin;
-		if(Axes[4] = new Axis(this, data, &tmp_axis, AXIS_LEFT | AXIS_NOTICKS | AXIS_AUTOTICK)){
-			Axes[4]->SetSize(SIZE_LB_XDIST, -lb_xdist); 
-			Axes[4]->SetSize(SIZE_TLB_XDIST, -tlb_dist); 
-			tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
-			Axes[4]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		tmp_axis.loc[0].fx = tmp_axis.loc[1].fx = GRect.Xmin + DRect.Xmax;
-		if(Axes[5] = new Axis(this, data, &tmp_axis, AXIS_RIGHT | AXIS_NOTICKS | AXIS_AUTOTICK)){
-			Axes[5]->SetSize(SIZE_LB_XDIST, lb_xdist); 
-			Axes[5]->SetSize(SIZE_TLB_XDIST, tlb_dist); 
-			tlbdef.Align = TXA_VCENTER | TXA_HLEFT;
-			Axes[5]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		NumAxes = 6;
-		break;
-	case 2:
-		Axes = (Axis**)calloc(3, sizeof(Axis *));
-		x_axis.loc[0].fx = GRect.Xmin + DRect.Xmin;
-		x_axis.loc[1].fx = GRect.Xmin + DRect.Xmax;
-		x_axis.loc[0].fy = x_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
-		y_axis.loc[0].fy = GRect.Ymin + DRect.Ymin;
-		y_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
-		y_axis.loc[0].fx = y_axis.loc[1].fx = GRect.Xmin + DRect.Xmin;
-		if((Axes[0] = new Axis(this, data, &x_axis, AXIS_BOTTOM | ptick |	
-			AXIS_AUTOTICK | AXIS_AUTOSCALE | ((tickstyle & 0x100) ? AXIS_GRIDLINE : 0)))){
-			Axes[0]->SetSize(SIZE_LB_YDIST, lb_ydist);
-			Axes[0]->SetSize(SIZE_TLB_YDIST, tlb_dist);
-			rlp_strcpy(label_text, 500, xa_desc);
-			label = new Label(Axes[0], data, GRect.Xmin + (DRect.Xmin+DRect.Xmax)/2.0f, 
-				GRect.Ymin+DRect.Ymax+DefSize(SIZE_AXIS_TICKS)*4.0f, &label_def, LB_Y_PARENT);
-			if(label && Axes[0]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
-			else if(label) DeleteGO(label);
-			tlbdef.Align = TXA_VTOP | TXA_HCENTER;
-			Axes[0]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		if((Axes[1] = new Axis(this, data, &y_axis, AXIS_LEFT | ntick | AXIS_AUTOTICK |
-			AXIS_AUTOSCALE | ((tickstyle & 0x200) ? AXIS_GRIDLINE : 0)))){
-			Axes[1]->SetSize(SIZE_LB_XDIST, -lb_xdist); 
-			Axes[1]->SetSize(SIZE_TLB_XDIST, -tlb_dist); 
-			rlp_strcpy(label_text, 500, ya_desc);
-			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
-			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - DefSize(SIZE_AXIS_TICKS)*6.0, 
-				GRect.Ymin+(DRect.Ymax+DRect.Ymin)/2.0, &label_def, LB_X_PARENT);
-			if(label && Axes[1]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
-			else if(label) DeleteGO(label);
-			tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
-			Axes[1]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		label = 0L;
-		NumAxes = 2;
-		break;
-	case 3:
-		label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
-		Axes = (Axis**)calloc(3, sizeof(Axis *));
-		x_axis.loc[0].fx = GRect.Xmin + DRect.Xmin;
-		x_axis.loc[1].fx = GRect.Xmin + DRect.Xmax;
-		x_axis.loc[0].fy = x_axis.loc[1].fy = GRect.Ymin + DRect.Ymin;
-		y_axis.loc[0].fy = GRect.Ymin + DRect.Ymin;
-		y_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
-		y_axis.loc[0].fx = y_axis.loc[1].fx = GRect.Xmin + DRect.Xmin;
-		if((Axes[0] = new Axis(this, data, &x_axis, AXIS_TOP | utick |	
-			AXIS_AUTOTICK | AXIS_AUTOSCALE | ((tickstyle & 0x100) ? AXIS_GRIDLINE : 0)))){
-			Axes[0]->SetSize(SIZE_LB_YDIST, -lb_ydist);
-			Axes[0]->SetSize(SIZE_TLB_YDIST, -tlb_dist);
-			rlp_strcpy(label_text, 500, xa_desc);
-			label = new Label(Axes[0], data, GRect.Xmin + (DRect.Xmin+DRect.Xmax)/2.0f, 
-				GRect.Ymin+DRect.Ymax+DefSize(SIZE_AXIS_TICKS)*4.0f, &label_def, LB_Y_PARENT);
-			if(label && Axes[0]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
-			else if(label) DeleteGO(label);
-			tlbdef.Align = TXA_VBOTTOM | TXA_HCENTER;
-			Axes[0]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		if((Axes[1] = new Axis(this, data, &y_axis, AXIS_LEFT | ntick | AXIS_AUTOTICK |
-			AXIS_AUTOSCALE | AXIS_INVERT | ((tickstyle & 0x200) ? AXIS_GRIDLINE : 0)))){
-			Axes[1]->SetSize(SIZE_LB_XDIST, -lb_xdist); 
-			Axes[1]->SetSize(SIZE_TLB_XDIST, -tlb_dist); 
-			rlp_strcpy(label_text, 500, ya_desc);
-			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
-			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - DefSize(SIZE_AXIS_TICKS)*6.0, 
-				GRect.Ymin+(DRect.Ymax+DRect.Ymin)/2.0, &label_def, LB_X_PARENT);
-			if(label && Axes[1]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
-			else if(label) DeleteGO(label);
-			tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
-			Axes[1]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		label = 0L;
-		NumAxes = 2;
-		break;
-	case 4:
-		Axes = (Axis**)calloc(3, sizeof(Axis *));
-		if(x_axis.Start >= 0.0f) x_axis.min = -x_axis.Step;
-		x_axis.loc[0].fx = GRect.Xmin + DRect.Xmin;
-		x_axis.loc[1].fx = GRect.Xmin + DRect.Xmax;
-		x_axis.loc[0].fy = x_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
-		y_axis.loc[0].fy = GRect.Ymin + DRect.Ymin;
-		y_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
-		y_axis.loc[0].fx = y_axis.loc[1].fx = 0.0f;
-		if((Axes[0] = new Axis(this, data, &x_axis, AXIS_BOTTOM | ptick |	
-			AXIS_AUTOTICK | AXIS_AUTOSCALE | ((tickstyle & 0x100) ? AXIS_GRIDLINE : 0)))){
-			Axes[0]->SetSize(SIZE_LB_YDIST, lb_ydist);
-			Axes[0]->SetSize(SIZE_TLB_YDIST, tlb_dist);
-			rlp_strcpy(label_text, 500, xa_desc);
-			label = new Label(Axes[0], data, GRect.Xmin + (DRect.Xmin+DRect.Xmax)/2.0f, 
-				GRect.Ymin+DRect.Ymax+DefSize(SIZE_AXIS_TICKS)*4.0f, &label_def, LB_Y_PARENT);
-			if(label && Axes[0]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
-			else if(label) DeleteGO(label);
-			tlbdef.Align = TXA_VTOP | TXA_HCENTER;
-			Axes[0]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		if((Axes[1] = new Axis(this, data, &y_axis, ntick | AXIS_AUTOTICK | AXIS_X_DATA |
-			AXIS_AUTOSCALE | ((tickstyle & 0x200) ? AXIS_GRIDLINE : 0)))){
-			Axes[1]->SetSize(SIZE_LB_XDIST, -lb_xdist); 
-			Axes[1]->SetSize(SIZE_TLB_XDIST, -tlb_dist); 
-			rlp_strcpy(label_text, 500, ya_desc);
-			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
-			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - DefSize(SIZE_AXIS_TICKS)*6.0, 
-				GRect.Ymin+(DRect.Ymax+DRect.Ymin)/2.0, &label_def, LB_X_PARENT);
-			if(label && Axes[1]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
-			else if(label) DeleteGO(label);
-			tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
-			Axes[1]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
-			}
-		label = 0L;
-		NumAxes = 2;
-		break;
-		}
-	if(Plots[0] && Plots[0]->Id >= GO_PLOT && Plots[0]->Id < GO_GRAPH && NumAxes > 1) {
-		if(((Plot*)Plots[0])->x_tv && Axes[0])Axes[0]->atv = ((Plot*)Plots[0])->x_tv->Copy();
-		else if(((Plot*)Plots[0])->x_dtype == ET_DATETIME)x_axis.flags |= AXIS_DATETIME;
-		if(((Plot*)Plots[0])->y_tv && Axes[1])Axes[1]->atv = ((Plot*)Plots[0])->y_tv->Copy();
-		else if(((Plot*)Plots[0])->y_dtype == ET_DATETIME)y_axis.flags |= AXIS_DATETIME;
-		}
-}
-
-bool
-Graph::DoScale(scaleINFO* sc, anyOutput *o)
-{
-	int i;
-	scaleINFO sc0;
-
-	if(sc->sy.fy <= 0.0) return false;
-	GRect.Xmax = sc->sx.fx + GRect.Xmax* sc->sx.fy;
-	GRect.Xmin = sc->sx.fx + GRect.Xmin * sc->sx.fy;
-	GRect.Ymax = sc->sy.fx + GRect.Ymax * sc->sy.fy;
-	GRect.Ymin = sc->sy.fx + GRect.Ymin * sc->sy.fy;
-	DRect.Xmax *= sc->sx.fy;	DRect.Xmin *= sc->sx.fy;
-	DRect.Ymax *= sc->sy.fy;	DRect.Ymin *= sc->sy.fy;
-	if(sc->sx.fy == 1.0 && sc->sx.fy  == sc->sy.fy && sc->sx.fy == sc->sz.fy) return true;
-	memcpy(&sc0, sc, sizeof(scaleINFO));
-	sc0.sx.fx = sc0.sy.fx = sc0.sz.fx = 0.0;	sc0.sx.fy = sc0.sz.fy = sc->sy.fy;
-	if(Axes) for(i = 0; i< NumAxes; i++) if(Axes[i]) Axes[i]->Command(CMD_SCALE, &sc0, o);
-	if(Plots) for(i = 0; i < NumPlots; i++) if(Plots[i]){
-		if(Plots[i]->Id == GO_GRAPH || Plots[i]->Id == GO_PAGE) Plots[i]->Command(CMD_SCALE, sc, o);
-		else Plots[i]->Command(CMD_SCALE, &sc0, o);
-		}
-	scale = scale > 0.0 ? scale * sc->sy.fy : sc->sy.fy;
-	return true;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Pages are graphic objects containing graphs and drawn objects
-Page::Page(GraphObj *par, DataObj *d):Graph(par, d, 0L, 0)
-{
-	FileIO(INIT_VARS);
-	cGraphs--;		cPages++;		Id = GO_PAGE;	bModified = true;
-}
-
-Page::Page(int src):Graph(src)
-{
-	int i;
-
-	//most of the object is read by Graph::FileIO()
-	ColBG = 0x00e8e8e8L;
-	LineDef.width = 0.0;	LineDef.patlength = 1.0;
-	LineDef.color = LineDef.pattern = 0x0L;
-	FillDef.type = FILL_NONE;
-	FillDef.color = 0x00ffffffL;	//use white paper
-	FillDef.scale = 1.0;
-	FillDef.hatch = 0L;
-	cGraphs--;		cPages++;	bModified = false;
-	if(Plots) for(i = 0; i < NumPlots; i++) if(Plots[i]) Plots[i]->moveable = 1;
-}
-
-void
-Page::DoPlot(anyOutput *o)
-{
-	int i;
-	POINT pts[3];
-
-	if(!o && !Disp) {
-		Disp = NewDispClass(this);
-		Disp->SetMenu(MENU_PAGE);
-#ifdef USE_WIN_SECURE
-		i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "Page %d", cPages);
-#else
-		i = sprintf(TmpTxt, "Page %d", cPages);
-#endif
-		if(!name && (name = (char*)malloc(i += 2)))rlp_strcpy(name, i, TmpTxt);
-		Disp->Caption(TmpTxt);
-		Disp->VPorg.fy = iround(Disp->MenuHeight);
-		Disp->VPscale = 0.5;
-		Disp->CheckMenu(ToolMode, true);
-		OwnDisp = true;
-		}
-	//the first output class is the display class
-	if(!Disp && (!(Disp = o))) return;
-	CurrDisp = o ? o : Disp;
-	CurrDisp->Erase(CurrDisp->dFillCol = ColBG);
-	if(OwnDisp && CurrDisp == Disp)LineDef.color = 0x0L;
-	else LineDef.color = FillDef.color;
-	CurrDisp->SetLine(&LineDef);
-	CurrDisp->SetFill(&FillDef);
-	CurrDisp->oRectangle(rDims.left = CurrDisp->co2ix(GRect.Xmin), 
-		rDims.top = CurrDisp->co2iy(GRect.Ymin), rDims.right = CurrDisp->co2ix(GRect.Xmax),
-		rDims.bottom = CurrDisp->co2iy(GRect.Ymax));
-	pts[0].x = rDims.left+7;				pts[0].y = pts[1].y = rDims.bottom;
-	pts[1].x = pts[2].x = rDims.right;		pts[2].y = rDims.top +3;
-	CurrDisp->oPolyline(pts, 3);
-	//do all plots
-	if(Plots) for(i = 0; i < NumPlots; i++) if(Plots[i]) Plots[i]->DoPlot(CurrDisp);
-	if(PasteObj) {
-		ToolMode = TM_PASTE;	CurrDisp->MouseCursor(MC_PASTE, false);
-		}
-}
-
-bool
-Page::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	Graph *g;
-
-	switch(cmd) {
-	case CMD_MOUSE_EVENT:
-		return Graph::Command(cmd, tmpl, o);
-	case CMD_REDRAW:
-		if(Disp) {
-			Disp->StartPage();		DoPlot(Disp);		Disp->EndPage();
-			}
-		return true;
-	case CMD_CONFIG:
-		return Configure();
-	case CMD_NEWGRAPH:
-		if((g = new Graph(this, data, Disp, 0)) && g->PropertyDlg() && 
-			Command(CMD_DROP_GRAPH, g, o))return true;
-		else if(g) DeleteGO(g);
-		return false;
-	case CMD_SET_DATAOBJ:
-		Graph::Command(cmd, tmpl, o);
-		Id = GO_PAGE;
-		return true;
-	case CMD_SCALE:
-		return true;
-	default:
-		return Graph::Command(cmd, tmpl, o);
-	}
-}
-
-double 
-Page::DefSize(int select)
-{
-	return defs.GetSize(select);
-}
-
+//rlplot.cpp, Copyright 2000-2008 R.Lackner
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+#include "rlplot.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <string.h>
+
+extern tag_Units Units[];
+extern char TmpTxt[];
+extern Default defs;
+
+GraphObj *CurrGO, *TrackGO;			//Selected Graphic Objects
+Label *CurrLabel = 0L;
+Graph *CurrGraph = 0L;
+Axis **CurrAxes = 0L;
+dragHandle *CurrHandle = 0L;
+UndoObj Undo;
+fmtText DrawFmtText;
+
+int cGraphs = 0;
+int cPlots = 0;
+int cPages = 0;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// grapic objects
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+GraphObj::GraphObj(GraphObj *par, DataObj *d)
+{
+	parent = par;	data = d;	Id = GO_UNKNOWN;
+	type = moveable = 0;		name = 0L;
+	rDims.left = rDims.right = rDims.top = rDims.bottom = 0;
+}
+
+GraphObj::~GraphObj()
+{
+	if(name)free(name);		name = 0L;
+	if(CurrGO == this)		CurrGO = 0L;
+	if(TrackGO == this)	 TrackGO = 0L;
+}
+
+double
+GraphObj::GetSize(int select)
+{
+	if(parent) return parent->GetSize(select);
+	else return defs.GetSize(select);
+}
+
+DWORD 
+GraphObj::GetColor(int select){
+	return defs.Color(select);
+}
+
+void
+GraphObj::RegGO(void *n)
+{
+	((notary*)n)->AddRegGO(this);
+}
+
+void *
+GraphObj::ObjThere(int x, int y)
+{
+	if(IsInRect(&rDims, x, y)) return this;
+	else return 0L;
+}
+
+void
+GraphObj::Track(POINT *p, anyOutput *o)
+{
+}
+
+double
+GraphObj::DefSize(int select)
+{
+	if(parent) return parent->DefSize(select);
+	else return defs.GetSize(select);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// This is a special object to read certain svg-settings from a *.rlp file
+svgOptions::svgOptions(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(defs.svgScript) free(defs.svgScript);
+	if(defs.svgAttr) free(defs.svgAttr);
+	defs.svgScript = defs.svgAttr = 0L;
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		if(script) defs.svgScript = script;
+		if(svgattr) defs.svgAttr = svgattr;
+		script = svgattr = 0L;
+		}
+	Id=GO_SVGOPTIONS;
+}
+
+svgOptions::~svgOptions()
+{
+	if(script)free(script);
+	script = 0L;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Symbols are graphic objects
+Symbol::Symbol(GraphObj *par, DataObj *d, double x, double y, int which,
+		int xc, int xr, int yc, int yr):GraphObj(par, d)
+{
+	//Symbols with no parent are part of a dialog
+	FileIO(INIT_VARS);
+	fPos.fx = x;
+	fPos.fy = y;
+	type = which;
+	Id = GO_SYMBOL;
+	if(xc >= 0 && xr >= 0 && yc >= 0 && yr >= 0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*2)) {
+			ssRef[0].x = xc;	ssRef[0].y = xr;
+			ssRef[1].x = yc;	ssRef[1].y = yr;
+			cssRef = 2;
+			}
+		}
+}
+
+Symbol::Symbol(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		SymFill.hatch = (LineDEF *) NULL;
+		}
+}
+
+Symbol::~Symbol()
+{
+	Command(CMD_FLUSH, 0L, 0L);
+}
+
+double
+Symbol::GetSize(int select)
+{
+	switch(select) {
+	case SIZE_MINE:		case SIZE_SYMBOL:
+		return size;
+	case SIZE_SYM_LINE:
+		return SymLine.width;
+	case SIZE_XPOS:
+		return fPos.fx;
+	case SIZE_YPOS:
+		return fPos.fy;
+	default:
+		return DefSize(select);
+		}
+}
+
+bool
+Symbol::SetSize(int select, double value)
+{
+	switch(select & 0xfff){
+	case SIZE_MINE:		case SIZE_SYMBOL:
+		size = value;
+		return true;
+	case SIZE_SYM_LINE:
+		SymLine.width = value;
+		return true;
+	case SIZE_XPOS:
+		fPos.fx = value;
+		return true;
+	case SIZE_YPOS:
+		fPos.fy = value;
+		return true;
+	}
+	return false;
+}
+
+DWORD
+Symbol::GetColor(int select)
+{
+	switch(select) {
+	case COL_SYM_LINE:
+		return SymLine.color;
+	case COL_SYM_FILL:
+		return SymFill.color;
+	default:
+		return parent ? parent->GetColor(select) : defs.Color(select);
+		}
+}
+
+bool
+Symbol::SetColor(int select, DWORD col)
+{
+	switch(select & 0xfff) {
+	case COL_SYM_LINE:
+		SymLine.color = col;
+		if(SymTxt) SymTxt->ColTxt = col;
+		return true;
+	case COL_SYM_FILL:
+		SymFill.color = col;
+		return true;
+	default:
+		return false;
+		}
+}
+
+void
+Symbol::DoPlot(anyOutput *target)
+{
+	int ix, iy, rx, ry, crx, cry, atype;
+	double sc;
+	long ncpts;
+	lfPOINT fip;
+	POINT pts[14], *cpts;
+	FillDEF cf;
+
+	atype = (type  & 0xfff);
+	memcpy(&cf, &SymFill, sizeof(FillDEF));
+	if(atype == SYM_CIRCLEF || atype == SYM_RECTF || atype == SYM_TRIAUF ||
+		atype == SYM_TRIADF || atype == SYM_DIAMONDF || atype == SYM_4STARF ||
+		atype == SYM_5GONF || atype == SYM_5STARF || atype == SYM_6STARF) cf.color = SymLine.color;
+	if(type & SYM_POS_PARENT) {
+		if(!parent) return;
+		fip.fx = parent->GetSize(SIZE_XCENTER);
+		fip.fy = parent->GetSize(SIZE_YCENTER);
+		}
+	else if(!target->fp2fip(&fPos, &fip)) return;
+	ix = iround(fip.fx);		iy = iround(fip.fy);
+	target->SetLine(&SymLine);
+	switch(atype){
+	default:
+	case SYM_CIRCLE:		//circle
+	case SYM_CIRCLEF:		//filled circle
+	case SYM_CIRCLEC:		//circle with center point
+	case SYM_1QUAD:		case SYM_2QUAD:		case SYM_3QUAD:
+		rx = target->un2ix(size/2.0);		ry = target->un2iy(size/2.0);
+		if(rx < 5) rx = 1;					if(ry < 5) ry = 1;
+		target->SetFill(&cf);
+		target->oCircle(ix-rx, iy-ry, ix+rx+1, iy+ry+1, name);
+		if(atype == SYM_CIRCLEC) {
+			crx = target->un2ix(size/5.0);	cry = target->un2iy(size/5.0);
+			cf.color = SymLine.color;		target->SetFill(&cf);
+			target->oCircle(ix-crx, iy-cry, ix+crx+1, iy+cry+1, name);
+			}
+		else if(atype == SYM_1QUAD || atype == SYM_2QUAD || atype == SYM_3QUAD) {
+			ncpts = 0L;			cf.color = SymLine.color;		target->SetFill(&cf);
+			if(atype == SYM_1QUAD) {
+				if(!(cpts = MakeArc(ix, iy, rx, 0x04, &ncpts)) || !ncpts) return;
+				cpts[0].x = ix + rx;		cpts[0].y = iy;
+				}
+			else if(atype == SYM_2QUAD) {
+				if(!(cpts = MakeArc(ix, iy, rx, 0x06, &ncpts)) || !ncpts) return;
+				cpts[0].x = ix;				cpts[0].y = iy+rx;
+				}
+			else if(atype == SYM_3QUAD) {
+				if(!(cpts = MakeArc(ix, iy, rx, 0x07, &ncpts)) || !ncpts) return;
+				cpts[0].x = ix-rx;			cpts[0].y = iy;
+				}
+			cpts[ncpts-1].x = ix;			cpts[ncpts-1].y = iy-rx;
+			cpts[ncpts].x = ix;				cpts[ncpts].y = iy;				ncpts++;
+			cpts[ncpts].x = cpts[0].x;		cpts[ncpts].y = cpts[0].y;		ncpts++;
+			target->oPolygon(cpts, ncpts);	free(cpts);
+			}
+		rx--;ry--;			//smaller marking rectangle
+		break;
+	case SYM_RECT:			//rectange (square)
+	case SYM_RECTF:			//filled rectangle
+	case SYM_RECTC:			//square with center point
+		rx = target->un2ix(size/2.25676);	ry = target->un2iy(size/2.25676);
+		if(rx < 5) rx = 1;					if(ry < 5) ry = 1;
+		target->SetFill(&cf);
+		target->oRectangle(ix-rx, iy-ry, ix+rx+1, iy+ry+1, name);
+		if(atype == SYM_RECTC) {
+			crx = target->un2ix(size/6.0);	cry = target->un2iy(size/6.0);
+			cf.color = SymLine.color;		target->SetFill(&cf);
+			target->oCircle(ix-crx, iy-cry, ix+crx+1, iy+cry+1, name);
+			}
+		break;
+	case SYM_TRIAU:			//triangles up and down, open or closed
+	case SYM_TRIAUF:	case SYM_TRIAD:		case SYM_TRIADF:		case SYM_TRIADC:
+	case SYM_TRIAUC:	case SYM_TRIAUL:	case SYM_TRIAUR:		case SYM_TRIADL:
+	case SYM_TRIADR:
+		rx = target->un2ix(size/1.48503);	ry = target->un2iy(size/1.48503);
+		if(rx < 5) rx = 1;					if(ry < 5) ry = 1;
+		target->SetFill(&cf);
+		pts[0].x = pts[3].x = ix - rx;		pts[1].x = ix;		pts[2].x = ix+rx;
+		//patch by anonymous
+		if(atype == SYM_TRIAU || atype == SYM_TRIAUF || atype == SYM_TRIAUL 
+			|| atype == SYM_TRIAUR || atype == SYM_TRIAUC) {
+			pts[0].y = pts[2].y = pts[3].y = iy+target->un2iy(size*0.38878f);
+			pts[1].y = iy-target->un2iy(size*0.77756f);
+			}
+		else {
+			pts[0].y = pts[2].y = pts[3].y = iy-target->un2iy(size*0.38878f);
+			pts[1].y = iy+target->un2iy(size*0.77756f);
+			}
+		target->oPolygon(pts, 4);
+		if(atype == SYM_TRIAUC || atype == SYM_TRIADC) {
+			crx = target->un2ix(size/6.0);	cry = target->un2iy(size/6.0);
+			cf.color = SymLine.color;		target->SetFill(&cf);
+			target->oCircle(ix-crx, iy-cry, ix+crx+1, iy+cry+1, name);
+			}
+		else if(atype == SYM_TRIAUL || atype == SYM_TRIADL) {
+			cf.color = SymLine.color;		target->SetFill(&cf);
+			pts[2].x = pts[1].x;			target->oPolygon(pts, 4);
+			}
+		else if(atype == SYM_TRIAUR || atype == SYM_TRIADR) {
+			cf.color = SymLine.color;		target->SetFill(&cf);
+			pts[0].x = pts[3].x = pts[1].x;	target->oPolygon(pts, 4);
+			}
+		rx--; ry--;
+		break;
+	case SYM_DIAMOND:	case SYM_DIAMONDF:		case SYM_DIAMONDC:
+		rx = target->un2ix(size/1.59588);	ry = target->un2iy(size/1.59588);
+		if(rx < 5) rx = 1;					if(ry < 5) ry = 1;
+		target->SetFill(&cf);
+		pts[0].x = pts[2].x = pts[4].x = ix;		
+		pts[0].y = pts[4].y = iy -ry;
+		pts[1].x = ix +rx;					pts[1].y = pts[3].y = iy;
+		pts[2].y = iy +ry;					pts[3].x = ix-rx;
+		target->oPolygon(pts, 5);
+		if(atype == SYM_DIAMONDC) {
+			crx = target->un2ix(size/6.0);	cry = target->un2iy(size/6.0);
+			cf.color = SymLine.color;		target->SetFill(&cf);
+			target->oCircle(ix-crx, iy-cry, ix+crx+1, iy+cry+1, name);
+			}
+		rx--;									ry--;
+		break;
+	case SYM_4STAR:		case SYM_4STARF:
+		rx = target->un2ix(size/1.4);	ry = target->un2iy(size/1.4);
+		crx = target->un2ix(size/6.0);	cry = target->un2iy(size/6.0);
+		pts[0].x = pts[8].x = ix-rx;	pts[0].y = pts[4].y = pts[8].y = iy;
+		pts[1].x = pts[7].x = ix-crx;	pts[1].y = pts[3].y = iy - cry;
+		pts[2].x = pts[6].x = ix;		pts[2].y = iy - ry;
+		pts[3].x = pts[5].x = ix+crx;	pts[4].x = ix + rx;
+		pts[5].y = pts[7].y = iy+cry;	pts[6].y = iy + ry;
+		target->SetFill(&cf);			target->oPolygon(pts, 9);
+		break;
+	case SYM_5GON:		case SYM_5GONF:		case SYM_5GONC:
+		sc = 1.4;
+		rx = target->un2ix(size/sc);	ry = target->un2iy(size/sc);
+		crx = target->un2ix(size/sc * 0.951057);	
+		cry = target->un2iy(size/sc * 0.309017);
+		pts[0].x = ix-crx;		pts[0].y = pts[2].y = iy-cry;	pts[1].x = ix;
+		pts[1].y = iy-ry;		pts[2].x = ix+crx;
+		crx = target->un2ix(size/sc * 0.587785);	
+		cry = target->un2iy(size/sc * 0.809017);
+		pts[3].x = ix + crx;	pts[4].x = ix - crx;
+		pts[3].y = pts[4].y = iy+cry;
+		pts[5].x = pts[0].x;	pts[5].y = pts[0].y;
+		target->SetFill(&cf);			target->oPolygon(pts, 6);
+		if(atype == SYM_5GONC) {
+			crx = target->un2ix(size/6.0);	cry = target->un2iy(size/6.0);
+			cf.color = SymLine.color;		target->SetFill(&cf);
+			target->oCircle(ix-crx, iy-cry, ix+crx+1, iy+cry+1, name);
+			}
+		break;
+	case SYM_5STAR:		case SYM_5STARF:
+		sc = 1.4;
+		rx = target->un2ix(size/sc);	ry = target->un2iy(size/sc);
+		crx = target->un2ix(size/sc * 0.951057);	
+		cry = target->un2iy(size/sc * 0.309017);
+		pts[0].x = ix-crx;		pts[0].y = pts[1].y = pts[3].y = pts[4].y = iy-cry;
+		pts[2].x = pts[7].x = ix;		pts[2].y = iy-ry;		pts[4].x = ix+crx;
+		crx = target->un2ix(size/sc * 0.23);
+		pts[1].x = ix - crx;	pts[3].x = ix + crx;
+		crx =  target->un2ix(size/sc * 0.36);
+		cry = target->un2iy(size/sc * 0.11);
+		pts[5].x = ix + crx;	pts[5].y = pts[9].y = iy +	cry;	pts[9].x = ix - crx;
+		pts[7].y = iy + target->un2iy(size/sc * 0.38);
+		crx = target->un2ix(size/sc * 0.587785);	
+		cry = target->un2iy(size/sc * 0.809017);
+		pts[6].x = ix + crx;	pts[8].x = ix - crx;
+		pts[6].y = pts[8].y = iy+cry;
+		pts[10].x = pts[0].x;	pts[10].y = pts[0].y;
+		target->SetFill(&cf);			target->oPolygon(pts, 11);
+		break;
+	case SYM_6STAR:		case SYM_6STARF:
+		sc = 1.4 / 0.86;				rx = target->un2ix(size/sc);
+		sc = 1.4 / 0.5;					ry = target->un2iy(size/sc);
+		pts[0].x = pts[10].x = pts[12].x = ix - rx;
+		pts[4].x = pts[6].x = ix + rx;	pts[2].x = pts[8].x = ix;
+		pts[0].y = pts[1].y = pts[3].y = pts[4].y = pts[12].y = iy - ry;
+		pts[6].y = pts[7].y = pts[9].y = pts[10].y = iy + ry;	
+		sc = 1.4 / 0.29;				rx = target->un2ix(size/sc);
+		pts[1].x = pts[9].x = ix - rx;	pts[3].x = pts[7].x = ix + rx;
+		sc = 1.4 / 0.52;				rx = target->un2ix(size/sc);
+		pts[5].x = ix +rx;	pts[11].x = ix - rx;	pts[5].y = pts[11].y = iy;
+		sc = 1.4;
+		rx = target->un2ix(size/sc);	ry = target->un2iy(size/sc);
+		pts[2].y = iy - ry;				pts[8].y = iy + ry;
+		target->SetFill(&cf);			target->oPolygon(pts, 13);
+		break;
+	case SYM_STAR:			//star is a combination of + and x symbols
+	case SYM_PLUS:			//draw a + sign
+	case SYM_HLINE:		case SYM_VLINE:
+		rx = target->un2ix(size/2.0f);		ry = target->un2iy(size/2.0f);
+		if(rx < 5) rx = 1;					if(ry < 5) ry = 1;
+		pts[0].x = pts[1].x = ix;
+		pts[0].y = iy - ry;					pts[1].y = iy + ry +1;
+		if(type != SYM_HLINE) target->oPolyline(pts, 2);
+		pts[0].x = ix -rx;					pts[1].x = ix + rx +1;
+		pts[0].y = pts[1].y = iy;
+		if(atype != SYM_VLINE) target->oPolyline(pts, 2);
+		if(atype == SYM_VLINE){ rx = 2; break;}
+		if(atype == SYM_HLINE){ ry = 2; break;}
+		if(atype == SYM_PLUS) break;		//continue with x symbol for star
+	case SYM_CROSS:			//draw a x symbol
+		rx = target->un2ix(size/2.5);		ry = target->un2iy(size/2.5);
+		if(rx < 5) rx = 1;					if(ry < 5) ry = 1;
+		pts[0].x = ix - rx - 2;				pts[1].x = ix + rx + 3;
+		pts[0].y = iy - ry - 2;				pts[1].y = iy + ry + 3;
+		target->oPolyline(pts, 2);			Swap(pts[0].y, pts[1].y);
+		pts[1].y -= 1;						pts[0].y -= 1;
+		target->oPolyline(pts, 2);
+		break;
+	case SYM_TEXT:
+		if(!SymTxt) Command(CMD_SETTEXT, (void *)"text", target);
+		if(!SymTxt || !SymTxt->text || !SymTxt->text[0])return;
+		SymTxt->iSize = target->un2iy(SymTxt->fSize = size *1.5);
+		target->SetTextSpec(SymTxt);
+		DrawFmtText.SetText(target, SymTxt->text, &ix, &iy);
+		if (target->oGetTextExtent(SymTxt->text, 0, &rx, &ry)){
+			rx >>= 1;		ry >>= 1;
+			}
+		else rx = ry = 10;
+		}
+	rDims.left = ix-rx-1;				rDims.right = ix+rx+2;
+	rDims.top = iy-ry-1;				rDims.bottom = iy+ry+2;
+}
+
+bool 
+Symbol::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	char *tmptxt;
+	AccRange *ac;
+	int i, r, c;
+
+	switch (cmd) {
+	case CMD_SCALE:
+		if(!tmpl) return false;
+		size *= ((scaleINFO*)tmpl)->sy.fy;
+		SymLine.width *= ((scaleINFO*)tmpl)->sy.fy;
+		if(SymTxt) {
+			SymTxt->fSize *= ((scaleINFO*)tmpl)->sy.fy;
+			SymTxt->iSize = 0;
+			}
+		return true;
+	case CMD_FLUSH:
+		if(SymTxt) {
+			if(SymTxt->text) free(SymTxt->text);
+			free(SymTxt);
+			}
+		if(ssRef) free(ssRef);	ssRef = 0L;
+		if(name)free(name);		name = 0L;
+		return true;
+	case CMD_REDRAW:
+		//if we come here its most likely the result of Undo
+		if(parent && parent->Id==GO_REGRESSION)
+			return parent->Command(CMD_MRK_DIRTY, 0L, o);
+		return false;
+	case CMD_GETTEXT:
+		if(SymTxt && SymTxt->text && tmpl) {
+			rlp_strcpy((char*)tmpl, 50, SymTxt->text);
+			return true;
+			}
+		return false;
+	case CMD_SYMTEXT_UNDO:
+		if(SymTxt && SymTxt->text){
+			c = Undo.String(this, &SymTxt->text, UNDO_CONTINUE);
+			i = (int)strlen((char*)tmpl);		i = i > c ? i+2 : c+2;
+			if(tmpl) {
+				if(SymTxt->text = (char*)realloc(SymTxt->text, i)) rlp_strcpy(SymTxt->text, i, (char*)tmpl);
+				}
+			else if(SymTxt->text) SymTxt->text[0] = 0;
+			return true;
+			}
+		//fall through if its new
+	case CMD_SYMTEXT:		case CMD_SETTEXT:
+		if(!SymTxt && (SymTxt = (TextDEF *) calloc(1, sizeof(TextDEF)))) {
+			SymTxt->ColTxt = SymLine.color;			SymTxt->fSize = size*1.5;
+			SymTxt->ColBg = parent ? parent->GetColor(COL_BG) : 0x00ffffffL;
+			SymTxt->Align = TXA_VCENTER | TXA_HCENTER;
+			SymTxt->Style = TXS_NORMAL;				SymTxt->Mode = TXM_TRANSPARENT;
+			SymTxt->Font = FONT_HELVETICA;			SymTxt->text = 0L;
+			}
+		if(!SymTxt) return false;
+		if(tmpl) {
+			i = (int) strlen((char*)tmpl) + 2;
+			if(SymTxt->text = (char*)realloc(SymTxt->text, i)) rlp_strcpy(SymTxt->text, i, (char*)tmpl);
+			}
+		else if(SymTxt->text) SymTxt->text[0] = 0;
+		return true;
+	case CMD_SYM_TYPE:
+		if(tmpl)type = *((int*)tmpl);
+		return true;
+	case CMD_GETTEXTDEF:
+		if(!SymTxt || !tmpl) return false;
+		memcpy(tmpl, SymTxt, sizeof(TextDEF));
+		return true;
+	case CMD_SYMTEXTDEF:		case CMD_SETTEXTDEF:
+		if(!tmpl)return false;
+		if(SymTxt) tmptxt = SymTxt->text;
+		else tmptxt = 0L;
+		if(!SymTxt && !(SymTxt = (TextDEF *) calloc(1, sizeof(TextDEF)))) return false;
+		memcpy(SymTxt, tmpl, sizeof(TextDEF));
+		SymTxt->text = tmptxt;
+		return true;
+	case CMD_SYM_RANGETEXT:		case CMD_RANGETEXT:
+		if(!data || !tmpl) return false;
+		if(!(tmptxt = (char*)malloc(500)))return false;
+		if((ac = new AccRange((char*)tmpl)) && ac->GetFirst(&c, &r)) {
+			for(i = 0, tmptxt[0] = 0; i <= idx; i++) ac->GetNext(&c, &r);
+			data->GetText(r, c, tmptxt, 500);
+			delete(ac);
+			}
+		Command(CMD_SETTEXT, tmptxt, 0L);
+		free(tmptxt);
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_SYMBOL;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
+				o->ShowMark(&rDims, MRK_INVERT);
+				CurrGO = this;
+				return true;
+				}
+			break;
+			}
+		break;
+	case CMD_UPDATE:
+		if(ssRef && cssRef >1 && data) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
+			return true;
+			}
+		return false;
+	case CMD_AUTOSCALE:
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
+			return true;
+			}
+		break;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Bubbles are graphic objects
+Bubble::Bubble(GraphObj *par, DataObj *d, double x, double y, double s, int which, 
+	FillDEF *fill, LineDEF *outline, int xc, int xr, int yc, int yr, int sc,
+	int sr):GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	fPos.fx = x;	fPos.fy = y;	fs = s;
+	type = which;
+	if(fill) {
+		memcpy(&BubbleFill,fill, sizeof(FillDEF));
+		if(BubbleFill.hatch) memcpy(&BubbleFillLine, BubbleFill.hatch, sizeof(LineDEF));
+		}
+	BubbleFill.hatch = &BubbleFillLine;
+	if(outline)memcpy(&BubbleLine, outline, sizeof(LineDEF));
+	Id = GO_BUBBLE;
+	if(xc >= 0 || xr >= 0 || yc >= 0 || yr >= 0 || sc >= 0 || sr >= 0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*3)) {
+			ssRef[0].x = xc;	ssRef[0].y = xr;
+			ssRef[1].x = yc;	ssRef[1].y = yr;
+			ssRef[2].x = sc;	ssRef[2].y = sr;
+			cssRef = 3;
+			}
+		}
+}
+
+Bubble::Bubble(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+Bubble::~Bubble()
+{
+	Command(CMD_FLUSH, 0L, 0L);
+}
+
+void
+Bubble::DoPlot(anyOutput *o)
+{
+	int x1, y1, x2, y2, ix, iy, tmp;
+	double fix, fiy;
+
+	o->SetLine(&BubbleLine);		o->SetFill(&BubbleFill);
+//	if(mo) DelBitmapClass(mo);		mo = 0L;
+	switch(type & 0x0f0) {
+	case BUBBLE_UNITS:
+		fix = o->un2fix(fs);		fiy = o->un2fiy(fs);
+		break;
+	case BUBBLE_XAXIS:
+		fix = (o->fx2fix(fPos.fx+fs) - o->fx2fix(fPos.fx-fs))/2.0;
+		fiy = fix * (o->un2fiy(10.0f)/o->un2fix(10.0f));	//x and y resolution different ?
+		break;
+	case BUBBLE_YAXIS:
+		fix = (o->fy2fiy(fPos.fy-fs) - o->fy2fiy(fPos.fy+fs))/2.0;
+		fiy = fix * (o->un2fiy(10.0f)/o->un2fix(10.0f));	//x and y resolution different ?
+		break;
+		}
+	fix = fix < 0.0 ? -fix : fix;							//sign must be positive
+	fiy = fiy < 0.0 ? -fiy : fiy;
+	rDims.left = rDims.right = iround(o->fx2fix(fPos.fx));
+	rDims.top = rDims.bottom = iround(o->fy2fiy(fPos.fy));
+	switch(type & 0x00f) {
+	case BUBBLE_CIRCLE:
+		ix = (int)(fix/2.0);			iy = (int)(fiy/2.0);
+		tmp = iround(o->fx2fix(fPos.fx));		x1 = tmp - ix;		x2 = tmp + ix;
+		tmp = iround(o->fy2fiy(fPos.fy));		y1 = tmp - iy;		y2 = tmp + iy;
+		o->oCircle(x1, y1, x2, y2, name);
+		UpdateMinMaxRect(&rDims, x1, y1);	UpdateMinMaxRect(&rDims, x2, y2);
+		break;
+	case BUBBLE_SQUARE:
+		if((type & 0xf00) == BUBBLE_CIRCUM) {
+			ix = iround(fix*.392699081);		iy = iround(fiy*.392699081);
+			}
+		else if((type & 0xf00) == BUBBLE_AREA) {
+			ix = iround(fix*.443113462);		iy = iround(fiy*.443113462);
+			}
+		else {
+			ix = iround(fix*.353553391);		iy = iround(fiy*.353553391);
+			}
+		tmp = iround(o->fx2fix(fPos.fx));		x1 = tmp - ix;		x2 = tmp + ix;
+		tmp = iround(o->fy2fiy(fPos.fy));		y1 = tmp - iy;		y2 = tmp + iy;
+		o->oRectangle(x1, y1, x2, y2, name);
+		UpdateMinMaxRect(&rDims, x1, y1);	UpdateMinMaxRect(&rDims, x2, y2);
+		break;
+	case BUBBLE_UPTRIA:
+	case BUBBLE_DOWNTRIA:
+		if((type & 0xf00) == BUBBLE_CIRCUM) {
+			fix *= .523598775;		fiy *= .523598775;
+			}
+		else if((type & 0xf00) == BUBBLE_AREA) {
+			fix *= .673386843;		fiy *= .673386843;
+			}
+		else {
+			fix *=.433012702;		fiy *= .433012702;
+			}
+		ix =  iround(fix);		iy = iround(fiy*.57735);
+		tmp = iround(o->fx2fix(fPos.fx));
+		pts[0].x = pts[3].x = tmp - ix;		pts[1].x = tmp + ix;		pts[2].x = tmp;
+		tmp = iround(o->fy2fiy(fPos.fy));
+		if((type & 0x00f) == BUBBLE_UPTRIA) {
+			pts[0].y = pts[1].y = pts[3].y = tmp + iy;
+			pts[2].y = tmp - iround(fiy*1.1547);
+			}
+		else {
+			pts[0].y = pts[1].y = pts[3].y = tmp - iy;
+			pts[2].y = tmp + iround(fiy*1.1547);
+			}
+		o->oPolygon(pts, 4);
+		UpdateMinMaxRect(&rDims, pts[0].x, pts[0].y);
+		UpdateMinMaxRect(&rDims, pts[1].x, pts[2].y);
+		break;
+		}
+}
+
+void
+Bubble::DoMark(anyOutput *o, bool mark)
+{
+	if(mark) {
+		BubbleFillLine.color ^= 0x00ffffffL;
+		BubbleFill.color ^= 0x00ffffffL;
+		DoPlot(o);
+		BubbleFill.color ^= 0x00ffffffL;
+		BubbleFillLine.color ^= 0x00ffffffL;
+		}
+	else {
+		if(parent) parent->DoPlot(o);
+		else DoPlot(o);
+		}
+	o->UpdateRect(&rDims, false);
+}
+
+bool 
+Bubble::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	bool bSelected = false;
+	unsigned long n, s;
+	POINT p;
+
+	switch (cmd) {
+	case CMD_FLUSH:
+		if(ssRef) free(ssRef);	ssRef = 0L;
+		if(name)free(name);		name = 0L;
+		return true;
+	case CMD_SCALE:
+		if(!tmpl) return false;
+		if((type & 0x0f0)== BUBBLE_UNITS) fs *= ((scaleINFO*)tmpl)->sy.fy;
+		BubbleLine.width *= ((scaleINFO*)tmpl)->sy.fy;
+		BubbleLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		BubbleFillLine.width *= ((scaleINFO*)tmpl)->sy.fy;
+		BubbleFillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		BubbleFill.scale *= ((scaleINFO*)tmpl)->sy.fy;
+		return true;
+	case CMD_LEGEND:
+		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		((Legend*)tmpl)->HasFill(&BubbleLine, &BubbleFill, 0L);
+		break;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, p.x = mev->x, p.y = mev->y) && !CurrGO) {
+				switch(type & 0x00f) {
+				case BUBBLE_CIRCLE:
+					n = s = p.x - ((rDims.right+rDims.left)>>1);
+					s *= n;
+					n = p.y - ((rDims.bottom+rDims.top)>>1);
+					n = isqr(s += n*n) -2;
+					bSelected = ((unsigned)((rDims.right-rDims.left)>>1) > n);
+					break;
+				case BUBBLE_SQUARE:
+					bSelected = true;
+					break;
+				case BUBBLE_UPTRIA:
+				case BUBBLE_DOWNTRIA:
+					if(!(bSelected = IsInPolygon(&p, pts, 4)))
+						bSelected = IsCloseToPL(p, pts, 4);
+					break;
+					}
+				if(bSelected) o->ShowMark(this, MRK_GODRAW);
+				return bSelected;
+				}
+			break;
+			}
+		break;
+	case CMD_SET_DATAOBJ:
+		Id = GO_BUBBLE;
+		data = (DataObj*)tmpl;
+		return true;
+	case CMD_UPDATE:
+		if(ssRef && cssRef >2 && data) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
+			data->GetValue(ssRef[2].y, ssRef[2].x, &fs);
+			return true;
+			}
+		return false;
+	case CMD_BUBBLE_ATTRIB:
+		if(tmpl) {
+			type &= ~0xff0;
+			type |= (*((int*)tmpl) & 0xff0);
+			return true;
+			}
+		return false;
+	case CMD_BUBBLE_TYPE:
+		if(tmpl) {
+			type &= ~0x00f;
+			type |= (*((int*)tmpl) & 0x00f);
+			return true;
+			}
+		return false;
+	case CMD_BUBBLE_FILL:
+		if(tmpl) {
+			BubbleFill.type = ((FillDEF*)tmpl)->type;
+			BubbleFill.color = ((FillDEF*)tmpl)->color;
+			BubbleFill.scale = ((FillDEF*)tmpl)->scale;
+			if(((FillDEF*)tmpl)->hatch)
+				memcpy(&BubbleFillLine, ((FillDEF*)tmpl)->hatch, sizeof(LineDEF));
+			}
+		return true;
+	case CMD_BUBBLE_LINE:
+		if(tmpl) memcpy(&BubbleLine, tmpl, sizeof(LineDEF));
+		return true;
+	case CMD_AUTOSCALE:
+		return DoAutoscale(o);
+		break;
+		}
+	return false;
+}
+
+bool
+Bubble::DoAutoscale(anyOutput *o) 
+{
+	double dx, dy;
+
+	switch(type & 0x0f0) {
+	case BUBBLE_XAXIS:			case BUBBLE_YAXIS:
+		dx = dy = fs/2.0;		break;
+	case BUBBLE_UNITS:
+		dx = fPos.fx/20;		dy = fPos.fy/20;		break;
+		}
+	((Plot*)parent)->CheckBounds(fPos.fx+dx, fPos.fy-dy);
+	((Plot*)parent)->CheckBounds(fPos.fx-dx, fPos.fy+dy);
+	return true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Bars are graphic objects
+Bar::Bar(GraphObj *par, DataObj *d, double x, double y, int which,int xc, int xr,
+		int yc, int yr, char *desc):GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	fPos.fx = x;			fPos.fy = y;			type = which;
+	if(type & BAR_RELWIDTH) size = 60.0;			Id = GO_BAR;
+	if(xc >= 0 || xr >= 0 || yc >= 0 || yr >= 0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*2)) {
+			ssRef[0].x = xc;	ssRef[0].y = xr;
+			ssRef[1].x = yc;	ssRef[1].y = yr;
+			cssRef = 2;
+			}
+		}
+	if(desc && desc[0]) name = (char*)memdup(desc, (int)strlen(desc)+1, 0);
+}
+
+Bar::Bar(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+Bar::~Bar()
+{
+	if(mo) DelBitmapClass(mo);	mo = 0L;
+	Command(CMD_FLUSH, 0L, 0L);
+}
+
+double
+Bar::GetSize(int select)
+{
+	switch(select){
+	case SIZE_XPOS:				return fPos.fx;
+	case SIZE_YPOS:				return fPos.fy;
+		}
+	return 0.0;
+}
+
+bool
+Bar::SetSize(int select, double value)
+{
+	switch(select & 0xfff) {
+	case SIZE_BAR: 
+		size = value;
+		return true;
+	case SIZE_BAR_LINE:
+		BarLine.width = value;
+		return true;
+	case SIZE_XBASE:
+		BarBase.fx = value;
+		return true;
+	case SIZE_YBASE:
+		BarBase.fy = value;
+		return true;
+		}
+	return false;
+}
+
+bool
+Bar::SetColor(int select, DWORD col)
+{
+	switch(select & 0xfff) {
+	case COL_BAR_LINE:
+		BarLine.color = col;
+		return true;
+	case COL_BAR_FILL:
+		BarFill.color = col;
+		return true;
+		}
+	return false;
+}
+
+void
+Bar::DoPlot(anyOutput *target)
+{
+	int w;
+	double fBase, rsize;
+	POINT pts[2];
+
+	if(!parent || size <= 0.001) return;
+	target->SetLine(&BarLine);			target->SetFill(&BarFill);
+	if(mo) DelBitmapClass(mo);			mo = 0L;
+	switch(type & 0xff) {
+	case BAR_VERTU:		case BAR_VERTT:		case BAR_VERTB:
+		switch(type & 0xff) {
+		case BAR_VERTB:
+			fBase = parent->GetSize(SIZE_BOUNDS_BOTTOM);
+			break;
+		case BAR_VERTT:
+			fBase = parent->GetSize(SIZE_BOUNDS_TOP);
+			break;
+		case BAR_VERTU:
+			fBase = BarBase.fy;
+			break;
+			}
+		if(type & BAR_RELWIDTH) {
+			rsize = size * parent->GetSize(SIZE_BARMINX)/100.0;
+			pts[0].x = iround(target->fx2fix(fPos.fx - rsize/2.0));
+			pts[1].x = iround(target->fx2fix(fPos.fx + rsize/2.0));
+			}
+		else {
+			w = target->un2ix(size);
+			pts[0].x = iround(target->fx2fix(fPos.fx)) - (w>>1);
+			pts[1].x = pts[0].x + w;
+			}
+		if(type & BAR_CENTERED) {
+			pts[0].y = iround(target->fy2fiy(fBase - (fPos.fy - fBase)));
+			pts[1].y = iround(target->fy2fiy(fBase + (fPos.fy - fBase)));
+			}
+		else {
+			pts[0].y = iround(target->fy2fiy(fBase));
+			pts[1].y = iround(target->fy2fiy(fPos.fy));
+			}
+		break;
+	case BAR_HORU:		case BAR_HORR:		case BAR_HORL:
+		switch(type & 0xff) {
+		case BAR_HORL:
+			fBase = parent->GetSize(SIZE_BOUNDS_LEFT);
+			break;
+		case BAR_HORR:
+			fBase = parent->GetSize(SIZE_BOUNDS_RIGHT);
+			break;
+		case BAR_HORU:
+			fBase = BarBase.fx;
+			break;
+			}
+		if(type & BAR_RELWIDTH) {
+			rsize = size * parent->GetSize(SIZE_BARMINY)/100.0;
+			pts[0].y = iround(target->fy2fiy(fPos.fy - rsize/2.0));
+			pts[1].y = iround(target->fy2fiy(fPos.fy + rsize/2.0));
+			}
+		else {
+			w = target->un2iy(size);
+			pts[0].y = target->fy2iy(fPos.fy) - w/2;
+			pts[1].y = pts[0].y+w;
+			}
+		if(type & BAR_CENTERED) {
+			pts[0].x = target->fx2ix(fBase - (fPos.fx - fBase));
+			pts[1].x = target->fx2ix(fBase + (fPos.fx - fBase));
+			}
+		else {
+			pts[0].x = target->fx2ix(fBase);
+			pts[1].x = target->fx2ix(fPos.fx);
+			}
+		break;
+	default:
+		return;
+		}
+	if(pts[0].x == pts[1].x || pts[0].y == pts[1].y) {
+		target->oSolidLine(pts);
+		}
+	else target->oRectangle(pts[0].x, pts[0].y, pts[1].x, pts[1].y, name);
+	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+}
+
+void
+Bar::DoMark(anyOutput *o, bool mark)
+{
+	POINT mpts[5];
+
+	if(mark){
+		if(mo) DelBitmapClass(mo);			mo = 0L;
+		mpts[0].x = mpts[4].x = mpts[3].x = rDims.left;
+		mpts[0].y = mpts[4].y = mpts[1].y = rDims.bottom;
+		mpts[1].x = mpts[2].x = rDims.right;	mpts[2].y = mpts[3].y = rDims.top;
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		IncrementMinMaxRect(&mrc, 3*o->un2ix(BarLine.width)+2);
+		mo = GetRectBitmap(&mrc, o);
+		InvertLine(mpts, 5, &BarLine, &mrc, o, mark);
+		}
+	else RestoreRectBitmap(&mo, &mrc, o);
+}
+
+bool 
+Bar::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	FillDEF *TmpFill;
+	lfPOINT bl;
+
+	switch (cmd) {
+	case CMD_FLUSH:
+		if(ssRef) free(ssRef);		ssRef = 0L;
+		if(name)free(name);			name = 0L;
+		return true;
+	case CMD_SCALE:
+		if(!tmpl) return false;
+		if(!(type & BAR_RELWIDTH)) size *= ((scaleINFO*)tmpl)->sy.fy;
+		BarLine.width *= ((scaleINFO*)tmpl)->sy.fy;
+		BarLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		HatchLine.width *= ((scaleINFO*)tmpl)->sy.fy;
+		HatchLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		BarFill.scale *= ((scaleINFO*)tmpl)->sy.fy;
+		return true;
+	case CMD_LEGEND:
+		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		((Legend*)tmpl)->HasFill(&BarLine, &BarFill, name);
+		break;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
+				o->ShowMark(CurrGO = this, MRK_GODRAW);
+				return true;
+				}
+			break;
+			}
+		return false;
+	case CMD_BAR_FILL:
+		TmpFill = (FillDEF *)tmpl;
+		if(TmpFill) {
+			BarFill.type = TmpFill->type;
+			BarFill.color = TmpFill->color;
+			BarFill.scale = TmpFill->scale;
+			if(TmpFill->hatch) memcpy(&HatchLine, TmpFill->hatch, sizeof(LineDEF));
+			}
+		return true;
+	case CMD_BAR_TYPE:
+		if(tmpl) type = *((int*)tmpl);
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_BAR;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_UPDATE:
+		if(ssRef && cssRef >1 && data) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
+			return true;
+			}
+		return false;
+	case CMD_AUTOSCALE:
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
+			switch(type & 0xff) {
+			case BAR_VERTU:
+			case BAR_VERTT:
+			case BAR_VERTB:
+				bl.fx = fPos.fx;
+				switch (type & 0xff) {
+				case BAR_VERTU:
+					bl.fy = BarBase.fy;
+					break;
+				case BAR_VERTT:
+				case BAR_VERTB:
+					bl.fy = 0.0f;		//cannot resolve
+					break;
+					}
+				if(type & BAR_CENTERED) bl.fy -= fPos.fy;
+				break;
+			case BAR_HORU:
+			case BAR_HORR:
+			case BAR_HORL:
+				bl.fy = fPos.fy;
+				switch(type & 0xff) {
+				case BAR_HORU:
+					bl.fx = BarBase.fx;
+				case BAR_HORR:
+				case BAR_HORL:
+					bl.fx = 0.0f;		//cannot resolve
+					}
+				if(type & BAR_CENTERED) bl.fx -= fPos.fx;
+				break;
+				}
+			((Plot*)parent)->CheckBounds(bl.fx, bl.fy);
+			return true;
+			}
+		break;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Data line is a graphic object
+DataLine::DataLine(GraphObj *par, DataObj *d, char *xrange, char *yrange, char *nam):GraphObj(par, d)
+{
+	size_t cb;
+
+	FileIO(INIT_VARS);
+	Id = GO_DATALINE;
+	if(xrange && xrange[0]) {
+		cb = strlen(xrange) +2;		ssXref = (char*)malloc(cb);		rlp_strcpy(ssXref, (int)cb, xrange);
+		}
+	if(yrange && yrange[0]) {
+		cb = strlen(yrange) +2;		ssYref = (char*)malloc(cb);		rlp_strcpy(ssYref, (int)cb, yrange);
+		}
+	if(nam && nam[0]) name = (char*)memdup(nam, (int)strlen(nam)+1, 0);
+	SetValues();
+}
+	
+DataLine::DataLine(GraphObj *par, DataObj *d, lfPOINT *val, long nval, char *na):GraphObj(par, d)
+{
+	int cb;
+
+	FileIO(INIT_VARS);
+	Values = val;			nPnt = nval;	nPntSet = nPnt-1;
+	if(na && na[0] && (name = (char*)malloc((cb = (int)strlen(na))+2))) {
+		rlp_strcpy(name, cb+1, na);
+		}
+	Id = GO_DATALINE;
+}
+
+DataLine::DataLine(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+DataLine::~DataLine()
+{
+	if(Values)free(Values);		Values = 0L;
+	if(pts) free(pts);			pts = 0L;
+	if(ssXref) free(ssXref);	ssXref = 0L;
+	if(ssYref) free(ssYref);	ssYref = 0L;
+	if(mo) DelBitmapClass(mo);	mo = 0L;
+	if(name) free(name);		name = 0L;
+	if(parent)parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+}
+
+bool
+DataLine::SetColor(int select, DWORD col)
+{
+	switch(select & 0xfff) {
+	case COL_DATA_LINE:
+		LineDef.color = col;		return true;
+	case COL_POLYGON:
+		pgFill.color = col;
+		LineDef.color = ((col & 0x00fefefeL)>>1);
+		return true;
+		}
+	return false;
+}
+
+void
+DataLine::DoPlot(anyOutput *target)
+{
+	int i;
+	lfPOINT fip;
+	POINT pn, *tmppts;
+
+	if(!Values || nPntSet < 1) return;
+	if (nPntSet >= nPnt) nPntSet = nPnt-1;
+	if(mo) DelBitmapClass(mo);		mo = 0L;
+	if(pts) free(pts);				pts = 0L;
+	if((type & 0xff) == 9 || (type & 0xff) == 10)	//splines
+		pts = (POINT *)malloc(sizeof(POINT)*1000);
+	else if((type & 0xff) == 11 || (type & 0xff) == 12)			// curve
+		pts = (POINT *) malloc(sizeof(POINT)* (nPntSet+2)*192);
+	else if(type & 0xff) pts = (POINT *)malloc(sizeof(POINT)*(nPntSet+2)*2);
+	else pts = (POINT *)malloc(sizeof(POINT)*(nPntSet+2));
+	if(!pts) return;
+	if(max.fx > min.fx && max.fy > min.fy) dirty = false;
+	else if(dirty) Command(CMD_AUTOSCALE, 0L, target);
+	cp = 0;
+	switch(type & 0x0f) {
+	case 0:		default:
+		for (i = 0; i <= nPntSet; i++){
+			target->fp2fip(Values+i, &fip);
+			pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
+			AddToPolygon(&cp, pts, &pn);
+			}
+		break;
+	case 5:
+		target->fp2fip(Values, &fip);
+		pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
+		target->fp2fip(Values+1, &fip);
+		pn.y += (pn.y -iround(fip.fy))>>1;
+		AddToPolygon(&cp, pts, &pn);
+	case 1:
+		target->fp2fip(Values, &fip);
+		pn.x = iround(fip.fx);		pn.y = iround(+fip.fy);
+		for (i = 0; i <= nPntSet; i++){
+			target->fp2fip(Values+i, &fip);
+			pn.x = iround(fip.fx);			AddToPolygon(&cp, pts, &pn);
+			pn.y = iround(fip.fy);			AddToPolygon(&cp, pts, &pn);
+			}
+		if((type &0xf) == 5) {
+			target->fp2fip(Values+i-2, &fip);
+			pn.x += (pn.x - iround(fip.fx))>>1;
+			AddToPolygon(&cp, pts, &pn);
+			}
+		break;
+	case 6:
+		target->fp2fip(Values, &fip);
+		pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
+		target->fp2fip(Values+1, &fip);
+		pn.x += (pn.x - iround(fip.fx))>>1;
+		AddToPolygon(&cp, pts, &pn);
+	case 2:
+		target->fp2fip(Values, &fip);
+		pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
+		for (i = 0; i <= nPntSet; i++){
+			target->fp2fip(Values+i, &fip);
+			pn.y = iround(fip.fy);			AddToPolygon(&cp, pts, &pn);
+			pn.x = iround(fip.fx);			AddToPolygon(&cp, pts, &pn);
+			}
+		if((type &0xf) == 6) {
+			target->fp2fip(Values+i-2, &fip);
+			pn.y += (pn.y - iround(fip.fy))>>1;
+			AddToPolygon(&cp, pts, &pn);
+			}
+		break;
+	case 7:
+		target->fp2fip(Values, &fip);
+		pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
+		target->fp2fip(Values+1, &fip);
+		pn.x += (pn.x - iround(fip.fx))>>1;
+		AddToPolygon(&cp, pts, &pn);
+	case 3:
+		target->fp2fip(Values, &fip);
+		pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
+		for (i = 0; i <= nPntSet; i++){
+			target->fp2fip(Values+i, &fip);
+			pn.x = (pn.x + iround(fip.fx))>>1;		AddToPolygon(&cp, pts, &pn);
+			pn.y = iround(fip.fy);				AddToPolygon(&cp, pts, &pn);
+			pn.x = iround(fip.fx);
+			}
+		AddToPolygon(&cp, pts, &pn);
+		if((type &0xf) == 7) {
+			target->fp2fip(Values+i-2, &fip);
+			pn.x += (pn.x - iround(fip.fx))>>1;
+			AddToPolygon(&cp, pts, &pn);
+			}
+		break;
+	case 8:
+		target->fp2fip(Values, &fip);
+		pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
+		target->fp2fip(Values+1, &fip);
+		pn.y += (pn.y - iround(fip.fy))>>1;
+		AddToPolygon(&cp, pts, &pn);
+	case 4:
+		target->fp2fip(Values, &fip);
+		pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
+		for (i = 0; i <= nPntSet; i++){
+			target->fp2fip(Values+i, &fip);
+			pn.y = (pn.y + iround(fip.fy))>>1;		AddToPolygon(&cp, pts, &pn);
+			pn.x = iround(fip.fx);				AddToPolygon(&cp, pts, &pn);
+			pn.y = iround(fip.fy);
+			}
+		AddToPolygon(&cp, pts, &pn);
+		if((type &0xf) == 8) {
+			target->fp2fip(Values+i-2, &fip);
+			pn.y += (pn.y - iround(fip.fy))>>1;
+			AddToPolygon(&cp, pts, &pn);
+			}
+		break;
+	case 9:		case 10:
+		DrawSpline(target);
+		break;
+	case 11:	case 12:
+		DrawCurve(target);
+		break;
+		}
+	if(cp < 2) return;
+	if(isPolygon) {			//for mark polygon only !!
+		AddToPolygon(&cp, pts, pts);
+		}
+	else{
+		target->SetLine(&LineDef);		target->oPolyline(pts, cp);
+		}
+	if(tmppts = (POINT*)realloc(pts, cp *sizeof(POINT))) pts = tmppts;
+	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+	for(i = 2; i < cp; i++) UpdateMinMaxRect(&rDims, pts[i].x, pts[i].y);
+	i = 2*target->un2ix(LineDef.width);		//increase size of rectangle for marks
+	IncrementMinMaxRect(&rDims, i);
+}
+
+void
+DataLine::DoMark(anyOutput *o, bool mark)
+{
+	if(pts && cp && o){
+		if(mark){
+			if(mo) DelBitmapClass(mo);				mo = 0L;
+			memcpy(&mrc, &rDims, sizeof(RECT));
+			IncrementMinMaxRect(&mrc, 6 + o->un2ix(LineDef.width));
+			mo = GetRectBitmap(&mrc, o);
+			InvertLine(pts, cp, &LineDef, &mrc, o, mark);
+			}
+		else if(mo) RestoreRectBitmap(&mo, &mrc, o);
+		}
+}
+
+bool
+DataLine::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	bool bFound = false;
+	POINT p1;
+	int i;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(!IsInRect(&rDims, p1.x= mev->x, p1.y= mev->y) || CurrGO || !o || nPntSet <1)
+				return false; 
+			if(isPolygon && IsInPolygon(&p1, pts, cp)) bFound = true;
+			if(bFound || IsCloseToPL(p1,pts,cp)) 
+				return o->ShowMark(this, MRK_GODRAW);
+			}
+		break;
+	case CMD_SCALE:
+		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;		LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		break;
+	case CMD_SET_DATAOBJ:
+		Id = isPolygon ? GO_DATAPOLYGON : GO_DATALINE;
+		data = (DataObj*)tmpl;
+		return true;
+	case CMD_MRK_DIRTY:
+		dirty= true;
+	case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, 0L);
+		return false;
+	case CMD_LEGEND:
+		if(tmpl && ((GraphObj*)tmpl)->Id == GO_LEGEND) {
+			if(Id == GO_DATALINE) ((Legend*)tmpl)->HasFill(&LineDef, 0L, name);
+			}
+		break;
+	case CMD_SET_LINE:
+		if(tmpl) memcpy(&LineDef, tmpl, sizeof(LineDEF));
+		return true;
+	case CMD_UPDATE:
+		Undo.DataMem(this, (void**)&Values, nPnt * sizeof(lfPOINT), &nPnt, UNDO_CONTINUE);
+		Undo.ValLong(this, &nPntSet, UNDO_CONTINUE);
+		SetValues();
+		return true;
+	case CMD_AUTOSCALE:
+		if(nPntSet < 1 || !Values) return false;
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			if(dirty) {
+				min.fx = max.fx = Values[0].fx;	min.fy = max.fy = Values[0].fy;
+				for (i = 1; i <= nPntSet; i++){
+					min.fx = Values[i].fx < min.fx ? Values[i].fx : min.fx;
+					max.fx = Values[i].fx > max.fx ? Values[i].fx : max.fx;
+					min.fy = Values[i].fy < min.fy ? Values[i].fy : min.fy;
+					max.fy = Values[i].fy > max.fy ? Values[i].fy : max.fy;
+					}
+				}
+			((Plot*)parent)->CheckBounds(min.fx, min.fy);
+			((Plot*)parent)->CheckBounds(max.fx, max.fy);
+			dirty = false;
+			return true;
+			}
+		return false;
+		}
+	return false;
+}
+
+void
+DataLine::SetValues()
+{
+	AccRange *rX, *rY1=0L, *rY2=0L;
+	int i, j, k, l, m, n;
+	double x, y;
+	char yref1[500], yref2[500];
+	lfPOINT *tmpValues = Values;
+
+	if(!ssXref || !ssYref) return;
+	if(!rlp_strcpy(yref1, 500, ssYref)) return;
+	for(i = 0, yref2[0] = 0; yref1[i]; i++) {
+		if(yref1[i] == ';') {
+			yref1[i++] = 0;
+			while(yref1[i] && yref1[i] < 33) i++;
+			rlp_strcpy(yref2, 500, yref1+i);
+			}
+		}
+	nPnt = nPntSet = 0;
+	min.fx = min.fy = HUGE_VAL;		max.fx = max.fy = -HUGE_VAL;
+	rX = new AccRange(ssXref);		rY1 = new AccRange(yref1);
+	if(!rX || !rY1){
+		if(rX) delete(rX);	if(rY1) delete(rY1);
+		return;
+		}
+	if(!name) name = rY1->RangeDesc(data, 1);
+	if(yref2[0] &&((nPnt = rX->CountItems()) == (rY1->CountItems()))) {
+		if(!(Values = (lfPOINT *)realloc(Values, ((nPnt*2+2) * sizeof(lfPOINT))))) return; 
+		if(!(rY2 = new AccRange(yref2))) {
+			if(yref1) free(yref1);		if(yref2) free(yref2);
+			if(rX) delete(rX);	if(rY1) delete(rY1);
+			return;
+			}
+		if(rX->GetFirst(&i, &j) && rY1->GetFirst(&k, &l) && 
+			rX->GetNext(&i, &j) && rY1->GetNext(&k, &l) &&
+			rY2->GetFirst(&m, &n) && rY2->GetNext(&m, &n)) do {
+			if(data->GetValue(j, i, &x)){
+				if(data->GetValue(l, k, &y)){
+					Values[nPntSet].fx = x;			Values[nPntSet++].fy = y;
+					}
+				if(data->GetValue(n, m, &y)){
+					Values[nPntSet].fx = x;			Values[nPntSet++].fy = y;
+					}
+				}
+			}while(rX->GetNext(&i, &j) && rY1->GetNext(&k, &l) && rY2->GetNext(&m, &n));
+		}
+	else {
+		if((nPnt = rX->CountItems()) != (rY1->CountItems())) return;
+		if(!(Values = (lfPOINT *)realloc(Values, (nPnt+2) * sizeof(lfPOINT)))) return; 
+		if(rX->GetFirst(&i, &j) && rY1->GetFirst(&k, &l) && 
+			rX->GetNext(&i, &j) && rY1->GetNext(&k, &l)) do {
+			if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y)){
+				Values[nPntSet].fx = x;				Values[nPntSet++].fy = y;
+				}
+			}while(rX->GetNext(&i, &j) && rY1->GetNext(&k, &l));
+		}
+	nPnt = nPntSet;		nPntSet--;	dirty = true;
+	Command(CMD_AUTOSCALE, 0L, 0L);
+	if(tmpValues && Values != tmpValues) Undo.InvalidGO(this);
+	if(rX) delete(rX);	if(rY1) delete(rY1);	if(rY2) delete(rY2);
+}
+
+void
+DataLine::LineData(lfPOINT *val, long nval)
+{
+	lfPOINT *ov = Values;
+
+	if(!val || nval <2) return;
+	if(nval > nPnt && nPnt > 1 && Values){
+		if(!(Values = (lfPOINT *)realloc(Values, ((nval*2+2) * sizeof(lfPOINT))))) return; 
+		if(ov != Values) Undo.InvalidGO(this);
+		}
+	else if(!Undo.busy) Undo.DataMem(this, (void**)&Values, nPnt * sizeof(lfPOINT), &nPnt, UNDO_CONTINUE);
+	memcpy(Values, val, nval * sizeof(lfPOINT));
+	if(pts) free(pts);			pts = 0L;					dirty = true;			
+	free(val);					nPnt = nval;				nPntSet = nPnt-1;
+}
+
+void
+DataLine::DrawCurve(anyOutput *target)
+{
+	lfPOINT *sdata, *bdata;
+	POINT *tmppts;
+	int i, j, n;
+
+	if(!(sdata = (lfPOINT *)malloc(nPnt * sizeof(lfPOINT))))return;
+	sdata[0].fx = Values[0].fx;				sdata[0].fy = Values[0].fy;
+	for(i = j = 1; i < nPnt; i++) {
+		if(Values[i].fx != sdata[j-1].fx || Values[i].fy != sdata[j-1].fy) {
+			sdata[j].fx = Values[i].fx;		sdata[j++].fy = Values[i].fy;
+			}
+		}
+	n = mkCurve(sdata, j, &bdata, (type&0x0f) != 11);
+//	if(!(tmppts = (POINT*)malloc((n+2)*sizeof(POINT))))return;
+	if(!(tmppts = (POINT*)malloc((n*64+2)*sizeof(POINT))))return;
+	for(i = 0; i < n; i++){
+		tmppts[i].x = target->fx2ix(bdata[i].fx);	tmppts[i].y = target->fy2iy(bdata[i].fy);
+		}
+	for(i = cp = 0; i< (n-2); i += 3) {
+		if(parent->Id == GO_TICK) ClipBezier(&cp, pts, tmppts[i], tmppts[i+1], tmppts[i+2], tmppts[i+3], 0L, 0L);
+		else DrawBezier(&cp, pts, tmppts[i], tmppts[i+1], tmppts[i+2], tmppts[i+3], 0);
+		}
+	if(bdata) free(bdata);	free(sdata);
+}
+
+void
+DataLine::DrawSpline(anyOutput *target)
+{
+	int i, j, k, klo, khi, ptsize = 1000;
+	double *y2, min, max, x, y, h, b, a;
+	POINT pn;
+	lfPOINT *scvals;
+	
+	if(!(y2 = (double*)malloc(sizeof(double)*(nPnt)))) return;
+	if(!(scvals = (lfPOINT*)malloc(sizeof(lfPOINT)*(nPnt)))){
+		free(y2);
+		return;
+		}
+	if((type & 0x0f) == 9 || (type & 0x0f) == 10) {
+		if((type & 0x0f) == 9) for(i = 0; i < nPnt; i++) {
+			scvals[i].fx = target->fx2fix(Values[i].fx);
+			scvals[i].fy = target->fy2fiy(Values[i].fy);
+			}
+		else for(i = 0; i < nPnt; i++) {
+			scvals[i].fy = target->fx2fix(Values[i].fx);
+			scvals[i].fx = target->fy2fiy(Values[i].fy);
+			}
+		SortFpArray(nPnt, scvals);
+		min = scvals[0].fx;			max = scvals[nPnt-1].fx;
+		for(i = j = 0; i < (nPnt-1); i++, j++) {
+			y = scvals[i].fy;			scvals[j].fx = scvals[i].fx;
+			for(k = 1; scvals[i+1].fx == scvals[i].fx; k++) {
+				y += scvals[i+1].fy;		i++;
+				}
+			scvals[j].fy = y/((double)k);
+			}
+		if(scvals[i].fx > scvals[i-1].fx) {
+			scvals[j].fx = scvals[i].fx;	scvals[j].fy = scvals[i].fy;
+			j++;
+			}
+		spline(scvals, j, y2);
+		h = scvals[1].fx - scvals[0].fx;	// klo and khi bracket the input value of x
+		for(x = min, klo = 0, i = khi = 1; x < max && i < j; x += 1.0) {
+			while(x > scvals[i].fx) {
+				klo++;		khi++;	i++;
+				h = scvals[khi].fx - scvals[klo].fx;
+				}
+			a = (scvals[khi].fx - x) / h;		b = (x - scvals[klo].fx) / h;
+			y = a * scvals[klo].fy + b * scvals[khi].fy + ((a*a*a - a) * y2[klo] + (b*b*b - b) * y2[khi]) * (h*h)/6.0;
+			if((type & 0x0f) == 9) {
+				pn.x = iround(x);		pn.y = iround(y);
+				}
+			else {
+				pn.x = iround(y);		pn.y = iround(x);
+				}
+			if(cp >= ptsize) {
+				ptsize += 1000;
+				pts = (POINT*)realloc(pts, sizeof(POINT)*ptsize); 
+				}
+			AddToPolygon(&cp, pts, &pn);
+			}
+		}
+	free(y2);	free(scvals);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// DataPolygon is a graphic object based on DataLine
+DataPolygon::DataPolygon(GraphObj *par, DataObj *d, char *xrange, char *yrange, char *nam):
+	DataLine(par, d, xrange, yrange, nam)
+{
+	lfPOINT *fp = Values;
+	char *rx = ssXref;
+	char *ry = ssYref;
+	long np = nPnt;
+
+	FileIO(INIT_VARS);
+	Values = fp;			//FileIO will just set Values to 0L !
+	ssXref = rx;			ssYref = ry;
+	nPnt = np;
+	Id = GO_DATAPOLYGON;
+}
+
+DataPolygon::DataPolygon(GraphObj *par, DataObj *d, lfPOINT *val, long nval, char *na):
+	DataLine(par, d, val, nval, 0L)
+{
+	int cb;
+
+	FileIO(INIT_VARS);
+	Values = val;			nPnt = nval;	nPntSet = nPnt-1;
+	if(na && na[0] && (name = (char*)malloc((cb = (int)strlen(na))+2))) {
+		rlp_strcpy(name, cb+1, na);
+		}
+	Id = GO_DATAPOLYGON;
+}
+
+DataPolygon::DataPolygon(int src):DataLine(0L, 0)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+DataPolygon::~DataPolygon()
+{
+	if(Values)free(Values);		Values =0L;
+	if(pts) free (pts);		pts = 0L;
+	if(ssXref) free(ssXref);	ssXref = 0L;
+	if(ssYref) free(ssYref);	ssYref = 0L;
+	if(mo) DelBitmapClass(mo);	mo = 0L;
+	if(name) free(name);		name = 0;
+	if(parent)parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+}
+
+void
+DataPolygon::DoPlot(anyOutput *o)
+{
+	LineDEF currLine;
+
+	if(!Values || !o || nPntSet < 2) return;
+	if(mo) DelBitmapClass(mo);	mo = 0L;
+	if((type & 0x0f) != 12 && (type & 0x0f)) {		//close polygon if necessary
+		if(Values[nPntSet].fx != Values[0].fx || Values[nPntSet].fy != Values[0].fy) {
+			Values = (lfPOINT*)realloc(Values, (nPntSet+2) * sizeof(lfPOINT));
+			Values[nPntSet+1].fx = Values[0].fx;	Values[nPntSet+1].fy = Values[0].fy;
+			nPntSet++;	nPnt++;
+			}
+		}
+	DataLine::DoPlot(o);			//no drawing but fill pts only
+	memcpy(&currLine, &LineDef, sizeof(LineDEF));
+	if(currLine.width < 1.0e-10) currLine.color = pgFill.color;
+	o->SetLine(&currLine);		o->SetFill(&pgFill);
+	o->oPolygon(pts, cp);
+}
+
+void
+DataPolygon::DoMark(anyOutput *o, bool mark)
+{
+	if(pts && cp && o){
+		if(mark){
+			if(mo) DelBitmapClass(mo);			mo = 0L;
+			memcpy(&mrc, &rDims, sizeof(RECT));
+			IncrementMinMaxRect(&mrc, 6 + o->un2ix(LineDef.width));
+			mo = GetRectBitmap(&mrc, o);
+			InvertLine(pts, cp, &LineDef, &mrc, o, mark);
+			}
+		else RestoreRectBitmap(&mo, &mrc, o);
+		}
+}
+
+bool
+DataPolygon::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch (cmd) {
+	case CMD_PG_FILL:
+		if(tmpl) {
+			memcpy((void*)&pgFill, tmpl, sizeof(FillDEF));
+			if(pgFill.hatch) memcpy((void*)&pgFillLine, (void*)pgFill.hatch, sizeof(LineDEF));
+			pgFill.hatch = (LineDEF*)&pgFillLine;
+			}
+		return true;
+	case CMD_SCALE:
+		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;			LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		pgFillLine.width *= ((scaleINFO*)tmpl)->sy.fy;		pgFillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		pgFill.scale *= ((scaleINFO*)tmpl)->sy.fy;
+		break;
+	case CMD_LEGEND:
+		if(tmpl && ((GraphObj*)tmpl)->Id == GO_LEGEND) {
+			if(Id == GO_DATAPOLYGON) ((Legend*)tmpl)->HasFill(&LineDef, &pgFill, name);
+			}
+		break;
+	default:
+		return DataLine::Command(cmd, tmpl, o);
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Calculate and display a regression line
+// Ref.: "Biometry" third edition 1995 (ed. R.R. Sokal and F.J. Rohlf),
+// W.H. Freeman and Company, New York; ISBN 0-7167-2411-1; pp. 451ff
+RegLine::RegLine(GraphObj *par, DataObj *d, lfPOINT *values, long n, int sel):
+	GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	type = sel;
+	Id = GO_REGLINE;
+	uclip.Xmin = uclip.Ymin = lim.Xmin = lim.Ymin = -1.0;
+	uclip.Xmax = uclip.Ymax = lim.Xmax = lim.Ymax = 1.0;
+	Recalc(values, n);
+}
+
+RegLine::RegLine(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) FileIO(FILE_READ);
+}
+
+RegLine::~RegLine()
+{
+	if(pts) free(pts);		pts = 0L;
+	if(parent)parent->Command(CMD_MRK_DIRTY, 0L, 0L);
+}
+
+double
+RegLine::GetSize(int select)
+{
+	double a, b;
+
+	switch(select) {
+	case SIZE_MX:		return mx;
+	case SIZE_MY:		return my;
+	case SIZE_A:
+	case SIZE_B:
+		switch(type & 0x07) {
+		case 1:		a = l2.fx;	b = l2.fy;	break;
+		case 2:		a = l3.fx;	b = l3.fy;	break;
+		case 3:		a = l4.fx;	b = l4.fy;	break;
+		case 4:		a = l5.fx;	b = l5.fy;	break;
+		default:	a = l1.fx;	b = l1.fy;	break;
+			}
+		if(select == SIZE_A) return a;
+		else return b;
+		}
+	return 0.0;
+}
+
+void
+RegLine::DoPlot(anyOutput *o)
+{
+	int i;
+	POINT pn;
+	double x, x1, y, d, a, b;
+	fRECT cliprc;
+	bool dValid;
+
+	switch (type & 0x70) {
+	case 0x20:	memcpy(&cliprc, &uclip, sizeof(fRECT));		break;
+	case 0x10:
+		if(parent) {
+			cliprc.Xmin = parent->GetSize(SIZE_BOUNDS_LEFT);
+			cliprc.Xmax = parent->GetSize(SIZE_BOUNDS_RIGHT);
+			cliprc.Ymin = parent->GetSize(SIZE_BOUNDS_BOTTOM);
+			cliprc.Ymax = parent->GetSize(SIZE_BOUNDS_TOP);
+			break;
+			}
+		//no parent: use default
+	default:	memcpy(&cliprc, &lim, sizeof(fRECT));		break;
+		}
+	if(cliprc.Xmax < cliprc.Xmin) {
+		x = cliprc.Xmax;	cliprc.Xmax = cliprc.Xmin;	cliprc.Xmin = x;
+		}
+	if(cliprc.Ymax < cliprc.Ymin) {
+		y = cliprc.Ymax;	cliprc.Ymax = cliprc.Ymin;	cliprc.Ymin = y;
+		}
+	if(cliprc.Xmin == cliprc.Xmax || cliprc.Ymin == cliprc.Ymax) return;
+	if((!pts) && (!(pts = (POINT *)malloc(sizeof(POINT)*202))))return;
+	switch(type & 0x07) {
+	case 1:		a = l2.fx;	b = l2.fy;	break;
+	case 2:		a = l3.fx;	b = l3.fy;	break;
+	case 3:		a = l4.fx;	b = l4.fy;	break;
+	case 4:		a = l5.fx;	b = l5.fy;	break;
+	default:	a = l1.fx;	b = l1.fy;	break;
+		}
+	x = cliprc.Xmin;	d = (cliprc.Xmax - cliprc.Xmin)/200.0;
+	for (cp = i = 0; i <= 200; i++){
+		dValid = true;
+		switch(type & 0x700) {
+		case 0x100:						//logarithmic x
+			if(dValid = x > defs.min4log) x1 = log10(x);
+			break;
+		case 0x200:						//reciprocal x
+			if(dValid = fabs(x) > defs.min4log) x1 = 1.0/x;
+			break;
+		case 0x300:						//square root x
+			if(dValid = fabs(x) > defs.min4log) x1 = sqrt(x);
+			break;
+		default:	x1 = x;	break;		//linear x
+			}
+		if(dValid) {
+			y = a + b*x1;
+			switch(type & 0x7000) {
+			case 0x1000:				//logarithmic y
+				y = pow(10.0, y);
+				break;
+			case 0x2000:				//reciprocal y
+				if(dValid = fabs(y) >0.0001) y = 1.0/y;
+				break;
+			case 0x3000:				//square root y
+				if(dValid = fabs(y) >0.0001) y = y*y;
+				break;
+				}
+			if(y >= cliprc.Ymin && y <= cliprc.Ymax) {
+				pn.x = o->fx2ix(x);	pn.y = o->fy2iy(y);
+				AddToPolygon(&cp, pts, &pn);
+				}
+			}
+		x += d;
+		}
+	if(cp < 2) return;
+	o->SetLine(&LineDef);			o->oPolyline(pts, cp);
+	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+	for(i = 2; i < cp; i++) UpdateMinMaxRect(&rDims, pts[i].x, pts[i].y);
+	i = 2*o->un2ix(LineDef.width);		//increase size of rectangle for marks
+	IncrementMinMaxRect(&rDims, i);
+}
+
+void
+RegLine::DoMark(anyOutput *o, bool mark)
+{
+	if(pts && cp && o){
+		if(mark)InvertLine(pts, cp, &LineDef, &rDims, o, mark);
+		else if(parent) parent->Command(CMD_REDRAW, 0L, o);
+		}
+}
+
+bool
+RegLine::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	POINT p1;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(!IsInRect(&rDims, p1.x= mev->x, p1.y= mev->y) || CurrGO || !o || nPoints <2)
+				return false; 
+			if(IsCloseToPL(p1,pts,cp)) return o->ShowMark(CurrGO= this, MRK_GODRAW);
+			}
+		break;
+	case CMD_SCALE:
+		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;		LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		break;
+	case CMD_SET_DATAOBJ:
+		Id = GO_REGLINE;
+		return true;
+	case CMD_BOUNDS:
+		if(tmpl) {
+			memcpy(&lim, tmpl, sizeof(fRECT));
+			memcpy(&uclip, tmpl, sizeof(fRECT));
+			}
+		return true;
+	case CMD_AUTOSCALE:
+		if(nPoints < 2) return false;
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			((Plot*)parent)->CheckBounds(lim.Xmin, lim.Ymin);
+			((Plot*)parent)->CheckBounds(lim.Xmax, lim.Ymax);
+			return true;
+			}
+		return false;
+		}
+	return false;
+}
+
+void
+RegLine::Recalc(lfPOINT *values, long n)
+{
+	double sx, sy, dx, dy, sxy, sxx, syy;
+	double a, b, k;
+	long ic;
+
+	sx = sy = 0.0;
+	if((nPoints = n)<2) return;
+	for(ic = 0; ic < n; ic++) {
+		sx += values[ic].fx;		sy += values[ic].fy;
+		}
+	mx = sx /((double)nPoints);	my = sy/((double)nPoints);
+	sxy = sxx = syy = 0.0;
+	for(ic = 0; ic < n; ic++) {
+		dx = mx - values[ic].fx;	dy = my - values[ic].fy;
+		sxx += (dx*dx);	syy += (dy*dy);	sxy += (dx*dy);
+		}
+	l1.fy = sxy / sxx;			l1.fx = my - (sxy / sxx) * mx;
+	b = sxy / syy;				a = mx - (sxy / syy) * my;
+	l2.fy = 1.0/b;				l2.fx = -a / b;
+	l3.fy = (l1.fy+l2.fy)/2.0;	l3.fx = (l1.fx+l2.fx)/2.0;
+	l4.fy = sy/sx;				l4.fx = 0.0;
+	if(l5.fx == 0.0 && l5.fx == 0.0){
+		l5.fy = l1.fy;				l5.fx = l1.fx;
+		}
+	//calculate distance point from line algorithm
+	//Ref: K. Thompson, 1990: Vertical Distance from a Point to a Line. In:
+	//   Graphic Gems (Andrew S. Glassner, ed.), Academic Press,
+	//   pp. 47-48; ISBN 0-12-286165-5
+	k = (sqrt(1.0/(1.0+l1.fy*l1.fy))+sqrt(1.0/(1.0+l2.fy*l2.fy)))/2.0;
+	b = sqrt(1.0/(k*k) -1.0);
+	l3.fy = l3.fy > 0.0 ? b : -b;
+	l3.fx = my - mx * l3.fy;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Calculate and display a statnard deviation (SD-) ellipse
+SDellipse::SDellipse(GraphObj *par, DataObj *d, lfPOINT *values, long n, int sel):
+	GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	type = sel;
+	Id = GO_SDELLIPSE;
+	if(val = (lfPOINT*)malloc(n * sizeof(lfPOINT))){
+		memcpy(val, values, (nPoints = n)*sizeof(lfPOINT));
+		rl = new RegLine(this, data, values, n, type);
+		}
+}
+
+SDellipse::SDellipse(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) FileIO(FILE_READ);
+}
+
+SDellipse::~SDellipse()
+{
+	if(val) free(val);
+	if(pts) free(pts);
+	if(!(type & 0x10000) && parent && rl && 
+		parent->Command(CMD_DROP_OBJECT, rl, 0L)) return;
+	if(rl) DeleteGO(rl);
+}
+
+void
+SDellipse::DoPlot(anyOutput *o)
+{
+	int i;
+	double a1, b1, a2, b2, fv, k1, k2, ss1, ss2, np, x, dx, si, csi, fac, fac2;
+	lfPOINT fp, fip;
+	POINT p1, *tmppts;
+
+	if(!rl) return;
+	if(pts) free(pts);
+	if(!(pts = (POINT *)malloc(sizeof(POINT)*420)))return;
+	//get line data from regression line object
+	mx = rl->GetSize(SIZE_MX);		my = rl->GetSize(SIZE_MY);
+	a1 = rl->GetSize(SIZE_A);		b1 = rl->GetSize(SIZE_B);
+	b2 = -1.0/b1;	a2 = my - b2 * mx;
+	//calculate sine and cosine for back rotation
+	fv = sqrt(1.0+b1*b1);			si = b1/fv;			csi = 1.0/fv;
+	//calculate distance from line for each point and squared sum of distances
+	//Ref: K. Thompson, 1990: Vertical Distance from a Point to a Line. In:
+	//   Graphic Gems (Andrew S. Glassner, ed.), Academic Press,
+	//   pp. 47-48; ISBN 0-12-286165-5
+	k1 = sqrt(1.0/(1.0+b1*b1));			k2 = sqrt(1.0/(1.0+b2*b2));
+	// y = a + b*x;
+	ss1 = ss2 = 0.0;
+	for(i = 0; i < nPoints; i++) {
+		fv = (a1 + b1 * val[i].fx - val[i].fy) * k1;	ss1 += (fv*fv);
+		fv = (a2 + b2 * val[i].fx - val[i].fy) * k2;	ss2 += (fv*fv);
+		}
+	np = ((double)(nPoints-1));
+	//SD perpendicular and in direction of regression line
+	sd1 = sqrt(ss1 /= np);		sd2 = sqrt(ss2 /= np);
+	switch(type & 0x60000) {
+		case 0x20000:		fac = 2.0;		break;
+		case 0x40000:		fac = 3.0;		break;
+		default:			fac = 1.0;		break;
+		}
+	fac2 = fac*fac;			dx = sd2/100.0*fac;
+	for(i = 0, cp = 0, x = -(sd2*fac); i < 2; i++) {
+		do {
+			fv = (x*x)/(ss2*fac2);
+			fv = fv < 0.99999 ? sqrt((1.0-fv)*ss1*fac2) : 0.0;
+			fv = i ? fv : -fv;
+			fp.fx = mx + x * csi - fv * si;
+			fp.fy = my + x * si + fv * csi;
+			switch(type & 0x700) {
+			case 0x100:					//logarithmic x
+				fp.fx = pow(10.0, fp.fx);
+				break;
+			case 0x200:					//reciprocal x
+				if(fabs(fp.fx) > defs.min4log) fp.fx = 1.0/fp.fx;
+				else fp.fx = 0.0;
+				break;
+			case 0x300:					//square root x
+				if(fabs(fp.fx) > defs.min4log) fp.fx = fp.fx*fp.fx;
+				else fp.fx = 0.0;
+				break;
+				}
+			switch(type & 0x7000) {
+			case 0x1000:				//logarithmic y
+				fp.fy = pow(10.0, fp.fy);
+				break;
+			case 0x2000:				//reciprocal y
+				if(fabs(fp.fy) > defs.min4log) fp.fy = 1.0/fp.fy;
+				else fp.fy = 0.0;
+				break;
+			case 0x3000:				//square root y
+				if(fabs(fp.fy) > defs.min4log) fp.fy = fp.fy*fp.fy;
+				else fp.fy = 0.0;
+				break;
+				}
+			o->fp2fip(&fp, &fip);	p1.x = iround(fip.fx);		p1.y = iround(fip.fy);
+			AddToPolygon(&cp, pts, &p1);
+			}while((x += dx) < (sd2*fac) && x > (-sd2*fac));
+		x = sd2*fac;
+		dx *= -1.0;
+		}
+	o->SetLine(&LineDef);
+	if(cp > 2) {
+		AddToPolygon(&cp, pts, pts);		//close polygon
+		if(tmppts = (POINT*)realloc(pts, cp *sizeof(POINT))) pts = tmppts;
+		SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+		for(i = 2; i < cp; i++) 
+			UpdateMinMaxRect(&rDims, pts[i].x, pts[i].y);
+		i = 3*o->un2ix(LineDef.width);		//increase size of rectangle for marks
+		IncrementMinMaxRect(&rDims, i);
+		o->oPolyline(pts, cp);
+		}
+	else {
+		free(pts);
+		cp = 0;
+		}
+	if(!(type & 0x10000))rl->DoPlot(o);
+}
+
+void
+SDellipse::DoMark(anyOutput *o, bool mark)
+{
+	if(pts && cp && o){
+		if(mark)InvertLine(pts, cp, &LineDef, &rDims, o, mark);
+		else if(parent) parent->Command(CMD_REDRAW, 0L, o);
+		}
+}
+
+bool
+SDellipse::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	POINT p1;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(!(type & 0x10000) && rl && rl->Command(cmd, tmpl, o)) return true;
+			if(!IsInRect(&rDims, p1.x= mev->x, p1.y= mev->y) || CurrGO || !o || nPoints <2)
+				return false; 
+			if(IsCloseToPL(p1,pts,cp)) return o->ShowMark(CurrGO= this, MRK_GODRAW);
+			}
+		break;
+	case CMD_SCALE:
+		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;		LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		if(rl) rl->Command(cmd, tmpl, o);
+		break;
+	case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_RMU:
+		if(!(type & 0x10000) && parent && rl && parent->Command(CMD_DROP_OBJECT, rl, o)){
+			rl = 0L;
+			return true;
+			}
+		return false;
+	case CMD_INIT:
+		if(rl) return rl->PropertyDlg();
+		break;
+	case CMD_DROP_OBJECT:
+		if(tmpl && ((GraphObj*)tmpl)->Id == GO_REGLINE && !rl) {
+			rl = (RegLine *)tmpl;
+			rl->parent = this;
+			return true;
+			}
+		return false;
+	case CMD_SET_DATAOBJ:
+		Id = GO_SDELLIPSE;
+		if(rl) rl->Command(cmd, tmpl, o);
+		return true;
+	case CMD_BOUNDS:
+		if(tmpl) {
+			if(rl) rl->Command(cmd, tmpl, o);
+			memcpy(&lim, tmpl, sizeof(fRECT));
+			}
+		return true;
+	case CMD_DELOBJ:
+		if(tmpl && tmpl == (void*)rl) {
+			Undo.ValInt(parent, &type, 0L);
+			type |= 0x10000;
+			if(parent) parent->Command(CMD_REDRAW, 0L, o);
+			return true;
+			}
+		break;
+	case CMD_AUTOSCALE:
+		if(nPoints < 2) return false;
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			((Plot*)parent)->CheckBounds(lim.Xmin, lim.Ymin);
+			((Plot*)parent)->CheckBounds(lim.Xmax, lim.Ymax);
+			return true;
+			}
+		break;
+		}
+	return false;
+}
+
+void
+SDellipse::Recalc(lfPOINT *values, long n)
+{
+	if(val) free(val);
+	if(val = (lfPOINT*)malloc(n * sizeof(lfPOINT)))
+		memcpy(val, values, (nPoints = n)*sizeof(lfPOINT));
+	if(rl) rl->Recalc(values, n);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Error bars are simple graphic objects
+ErrorBar::ErrorBar(GraphObj *par, DataObj *d, double x, double y, double err, int which,
+	int xc, int xr, int yc, int yr, int ec, int er):GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	fPos.fx = x;		fPos.fy = y;
+	ferr = err;			type = which;
+	Id = GO_ERRBAR;		data = d;
+	if(xc >= 0 || xr >= 0 || yc >= 0 || yr >= 0 || ec >= 0 || er >= 0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*3)) {
+			ssRef[0].x = xc;	ssRef[0].y = xr;
+			ssRef[1].x = yc;	ssRef[1].y = yr;
+			ssRef[2].x = ec;	ssRef[2].y = er;
+			cssRef = 3;
+			}
+		}
+	Command(CMD_AUTOSCALE, 0L, 0L);
+}
+
+ErrorBar::ErrorBar(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	type = ERRBAR_VSYM;
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+ErrorBar::~ErrorBar()
+{
+	if(mo) DelBitmapClass(mo);	mo = 0L;
+	if(ssRef) free(ssRef);		ssRef = 0L;
+	if(name) free(name);		name = 0L;
+}
+
+bool
+ErrorBar::SetSize(int select, double value)
+{
+	switch(select & 0xfff) {
+	case SIZE_ERRBAR: 
+		SizeBar = value;
+		return true;
+	case SIZE_ERRBAR_LINE:
+		ErrLine.width = value;
+		return true;
+		}
+	return false;
+}
+
+bool
+ErrorBar::SetColor(int select, DWORD col)
+{
+	switch(select & 0xfff) {
+	case COL_ERROR_LINE:
+		ErrLine.color = col;
+		return true;
+		}
+	return false;
+}
+
+void
+ErrorBar::DoPlot(anyOutput *target)
+{
+	int ie;
+
+	switch (type & 0x0ff) {
+	case ERRBAR_VSYM:
+	case ERRBAR_VUP:
+	case ERRBAR_VDOWN:
+		ie = target->un2ix(SizeBar/2.0);
+		break;
+	default:
+		ie = target->un2iy(SizeBar/2.0);
+		break;
+		}
+	target->SetLine(&ErrLine);
+	switch(type) {
+	case ERRBAR_VSYM:
+		ebpts[0].x = ebpts[1].x = target->fx2ix(fPos.fx);
+		ebpts[0].y = target->fy2iy(fPos.fy-ferr);
+		ebpts[4].y = ebpts[5].y = ebpts[1].y = target->fy2iy(fPos.fy+ferr);
+		if(ebpts[1].y != ebpts[0].y) target->oSolidLine(ebpts);
+		ebpts[4].x = ebpts[2].x = ebpts[0].x - ie;
+		ebpts[5].x = ebpts[3].x = ebpts[1].x + ie+1;
+		ebpts[2].y = ebpts[3].y = ebpts[0].y;
+		if(ebpts[3].x > ebpts[2].x) {
+			target->oSolidLine(ebpts+2);
+			target->oSolidLine(ebpts+4);
+			}
+		rDims.left =  ebpts[2].x;		rDims.right = ebpts[3].x;
+		rDims.top = ebpts[0].y;			rDims.bottom = ebpts[1].y;
+		break;
+	case ERRBAR_VUP:
+	case ERRBAR_VDOWN:
+		ebpts[0].x = ebpts[1].x = target->fx2ix(fPos.fx);
+		ebpts[0].y = target->fy2iy(fPos.fy);
+		ebpts[2].y = ebpts[3].y = ebpts[1].y = 
+			target->fy2iy(type == ERRBAR_VUP ? fPos.fy+ferr : fPos.fy-ferr);
+		if(ebpts[1].y != ebpts[0].y) target->oSolidLine(ebpts);
+		ebpts[2].x = ebpts[0].x - ie;
+		ebpts[3].x = ebpts[1].x + ie+1;
+		if(ebpts[3].x > ebpts[2].x) target->oSolidLine(ebpts+2);
+		rDims.left =  ebpts[2].x;		rDims.right = ebpts[3].x;
+		rDims.top = ebpts[0].y;			rDims.bottom = ebpts[1].y;
+		break;
+	case ERRBAR_HSYM:
+		ebpts[2].x = ebpts[3].x = ebpts[0].x = target->fx2ix(fPos.fx-ferr);
+		ebpts[4].x = ebpts[5].x = ebpts[1].x = target->fx2ix(fPos.fx+ferr);
+		ebpts[0].y = ebpts[1].y = target->fy2iy(fPos.fy);
+		if(ebpts[1].x != ebpts[0].x) target->oSolidLine(ebpts);
+		ebpts[2].y = ebpts[4].y = ebpts[0].y - ie;
+		ebpts[3].y = ebpts[5].y = ebpts[1].y + ie+1;
+		if(ebpts[3].y >ebpts[2].y) {
+			target->oSolidLine(ebpts+2);
+			target->oSolidLine(ebpts+4);
+			}
+		rDims.left =  ebpts[0].x;		rDims.right = ebpts[1].x;
+		rDims.top = ebpts[2].y;			rDims.bottom = ebpts[3].y;
+		break;
+	case ERRBAR_HLEFT:
+	case ERRBAR_HRIGHT:
+		ebpts[0].x = target->fx2ix(fPos.fx);
+		ebpts[0].y = ebpts[1].y = target->fy2iy(fPos.fy);
+		ebpts[2].x = ebpts[3].x = ebpts[1].x = 
+			target->fx2ix(type == ERRBAR_HRIGHT ? fPos.fx+ferr : fPos.fx-ferr);
+		if(ebpts[1].x != ebpts[0].x) target->oSolidLine(ebpts);
+		ebpts[2].y = ebpts[0].y - ie;
+		ebpts[3].y = ebpts[1].y + ie+1;
+		if(ebpts[3].y > ebpts[2].y) target->oSolidLine(ebpts+2);
+		rDims.left =  ebpts[0].x;		rDims.right = ebpts[1].x;
+		rDims.top = ebpts[2].y;			rDims.bottom = ebpts[3].y;
+		break;
+		}
+	if(rDims.left > rDims.right) Swap(rDims.left, rDims.right);
+	if(rDims.top > rDims.bottom) Swap(rDims.top, rDims.bottom);
+	IncrementMinMaxRect(&rDims, 2);
+}
+
+void
+ErrorBar::DoMark(anyOutput *o, bool mark)
+{
+	int i;
+	LineDEF OldLine;
+
+	if(mark){
+		if(mo) DelBitmapClass(mo);			mo = 0L;
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		memcpy(&OldLine, &ErrLine, sizeof(LineDEF));
+		i = 3*o->un2ix(ErrLine.width);		//increase size of rectangle for marks
+		IncrementMinMaxRect(&mrc, i);		mo = GetRectBitmap(&mrc, o);
+		ErrLine.width *= 5.0;				DoPlot(o);
+		ErrLine.width = OldLine.width;		ErrLine.color = OldLine.color ^ 0x00ffffffL;
+		DoPlot(o);							o->UpdateRect(&mrc, false);
+		memcpy(&ErrLine, &OldLine, sizeof(LineDEF));
+		}
+	else if(mo) RestoreRectBitmap(&mo, &mrc, o);
+}
+
+bool
+ErrorBar::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	bool bFound;
+	int cb;
+
+	switch (cmd) {
+	case CMD_SCALE:
+		ErrLine.width *= ((scaleINFO*)tmpl)->sy.fy;		ErrLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		SizeBar *= ((scaleINFO*)tmpl)->sy.fy;
+		return true;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		bFound = false;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(!IsInRect(&rDims, mev->x, mev->y) || CurrGO) return false; 
+			switch (type) {
+			case ERRBAR_HSYM:		case ERRBAR_HLEFT:		case ERRBAR_HRIGHT:
+				if(mev->y >= (ebpts[0].y-2) && mev->y <= (ebpts[1].y+2)) bFound = true;
+				else if(mev->x >= (ebpts[2].x-2) && mev->x <= (ebpts[2].x+2)) bFound = true;
+				else if(type == ERRBAR_HSYM && mev->x >= (ebpts[4].x-2) &&
+					mev->x <= (ebpts[4].x + 2)) bFound = true;
+				break;
+			case ERRBAR_VSYM:		case ERRBAR_VUP:		case ERRBAR_VDOWN:
+				if(mev->x >= (ebpts[0].x-2) && mev->x <= (ebpts[1].x+2)) bFound = true;
+				else if(mev->y >= (ebpts[2].y-2) && mev->y <= (ebpts[2].y+2)) bFound = true;
+				else if(type == ERRBAR_VSYM && mev->y >= (ebpts[4].y-2) &&
+					mev->y <= (ebpts[4].y + 2)) bFound = true;
+				break;
+				}
+			if(bFound) o->ShowMark(this, MRK_GODRAW);
+			}
+		return false;
+	case CMD_SET_DATAOBJ:
+		Id = GO_ERRBAR;
+		data = (DataObj *) tmpl;
+		return true;
+	case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_UPDATE:
+		if(ssRef && cssRef >2 && data) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
+			data->GetValue(ssRef[2].y, ssRef[2].x, &ferr);
+			return true;
+			}
+		return false;
+	case CMD_LEGEND:
+		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		switch(type) {
+			case ERRBAR_VSYM:		case ERRBAR_VUP:		case ERRBAR_VDOWN:
+				((Legend*)tmpl)->HasErr(&ErrLine, 1, name);
+				break;
+			case ERRBAR_HSYM:		case ERRBAR_HLEFT:		case ERRBAR_HRIGHT:
+				((Legend*)tmpl)->HasErr(&ErrLine, 2, name);
+				break;
+			}
+		break;
+	case CMD_ERRDESC:
+		if(tmpl && *((char *)tmpl)){
+			cb = (int)strlen((char*)tmpl)+2;
+			if(name = (char*)realloc(name, cb)) rlp_strcpy(name, cb, (char*)tmpl);
+			return true;
+			}
+		return false;
+	case CMD_ERR_TYPE:
+		if(tmpl) type = *((int*)tmpl);
+		return true;
+	case CMD_AUTOSCALE:
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			switch(type) {
+			case ERRBAR_VSYM:	
+				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy+ferr);
+				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy-ferr);		break;
+			case ERRBAR_VUP:	
+				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
+				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy+ferr);		break;
+			case ERRBAR_VDOWN:
+				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
+				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy-ferr);		break;
+			case ERRBAR_HSYM:
+				((Plot*)parent)->CheckBounds(fPos.fx+ferr, fPos.fy);
+				((Plot*)parent)->CheckBounds(fPos.fx-ferr, fPos.fy);		break;
+			case ERRBAR_HLEFT:
+				((Plot*)parent)->CheckBounds(fPos.fx-ferr, fPos.fy);
+				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);				break;
+			case ERRBAR_HRIGHT:
+				((Plot*)parent)->CheckBounds(fPos.fx+ferr, fPos.fy);
+				((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);				break;
+				}
+			return true;
+			}
+		break;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// arrows to data points or with absolute coordinates
+Arrow::Arrow(GraphObj * par, DataObj *d, lfPOINT fp1, lfPOINT fp2, int which,
+	int xc1, int xr1, int yc1, int yr1, int xc2, int xr2, int yc2, int yr2):
+	GraphObj(par, d)
+{
+	double dx, dy;
+
+	FileIO(INIT_VARS);
+	memcpy(&pos1, &fp1, sizeof(lfPOINT));
+	memcpy(&pos2, &fp2, sizeof(lfPOINT));
+	type = which;
+	if(type & ARROW_UNITS) {
+		dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
+		pos1.fx -= dx;	pos1.fy -= dy;			pos2.fx -= dx;	pos2.fy -= dy;
+		}
+	if(xc1 >= 0 || xr1 >= 0 || yc1 >= 0 || yr1 >= 0 || xc2 >= 0 || xr2 >= 0 || 
+		yc2 >= 0 || yr2 >= 0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*4)) {
+			ssRef[0].x = xc1;	ssRef[0].y = xr1;
+			ssRef[1].x = yc1;	ssRef[1].y = yr1;
+			ssRef[2].x = xc2;	ssRef[2].y = xr2;
+			ssRef[3].x = yc2;	ssRef[3].y = yr2;
+			cssRef = 4;
+			}
+		}
+	bModified = false;
+}
+
+Arrow::Arrow(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	bModified = false;
+}
+
+Arrow::~Arrow()
+{
+	Command(CMD_FLUSH, 0L, 0L);
+	if(bModified) Undo.InvalidGO(this);
+}
+
+double
+Arrow::GetSize(int select)
+{
+	switch(select) {
+	case SIZE_XPOS:		return pos1.fx;
+	case SIZE_XPOS+1:	return pos2.fx;
+	case SIZE_YPOS:		return pos1.fy;
+	case SIZE_YPOS+1:	return pos2.fy;
+	case SIZE_GRECT_LEFT:	case SIZE_GRECT_TOP:
+	case SIZE_GRECT_RIGHT:	case SIZE_GRECT_BOTTOM:
+		if(parent) return parent->GetSize(select);
+		break;
+		}
+	return 0.0;
+}
+
+bool
+Arrow::SetSize(int select, double value)
+{
+	switch(select & 0xfff) {
+	case SIZE_ARROW_LINE:		LineDef.width = value;		return true;
+	case SIZE_ARROW_CAPWIDTH:	cw = value;					return true;
+	case SIZE_ARROW_CAPLENGTH:	cl = value;					return true;
+	case SIZE_XPOS:				pos1.fx = value;			return true;
+	case SIZE_XPOS+1:			pos2.fx = value;			return true;
+	case SIZE_YPOS:				pos1.fy = value;			return true;
+	case SIZE_YPOS+1:			pos2.fy = value;			return true;
+		}
+	return false;
+}
+
+bool
+Arrow::SetColor(int select, DWORD col)
+{
+	switch(select & 0xfff) {
+	case COL_ARROW:
+		LineDef.color = col;
+		return true;
+		}
+	return false;
+}
+
+void
+Arrow::DoPlot(anyOutput *o)
+{
+	double si, csi, tmp, fix1, fiy1, fix2, fiy2, dx, dy;
+
+	if(!o || !parent) return;
+	if(type & ARROW_UNITS) {
+		dx = parent->GetSize(SIZE_GRECT_LEFT);		dy = parent->GetSize(SIZE_GRECT_TOP);
+		fix1 = o->co2fix(pos1.fx+dx);	fix2 = o->co2fix(pos2.fx+dx);
+		fiy1 = o->co2fiy(pos1.fy+dy);	fiy2 = o->co2fiy(pos2.fy+dy);
+		}
+	else {
+		fix1 = o->fx2fix(pos1.fx);		fix2 = o->fx2fix(pos2.fx);
+		fiy1 = o->fy2fiy(pos1.fy);		fiy2 = o->fy2fiy(pos2.fy);
+		}
+	if(fix1 == fix2 && fiy1 == fiy2) return;	//zero length
+	//draw arrow line
+	pts[0].x = iround(fix1);		pts[1].x = iround(fix2);
+	pts[0].y = iround(fiy1);		pts[1].y = iround(fiy2);
+	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+	//calculate sine and cosine for cap
+	si = fiy1-fiy2;
+	tmp = fix2 - fix1;
+	si = si/sqrt(si*si + tmp*tmp);
+	csi = fix2-fix1;
+	tmp = fiy2 - fiy1;
+	csi = csi/sqrt(csi*csi + tmp*tmp);
+	//draw cap
+	pts[2].x = pts[1].x - o->un2ix(csi*cl + si*cw/2.0);
+	pts[2].y = pts[1].y + o->un2iy(si*cl - csi*cw/2.0);
+	pts[3].x = pts[1].x;		pts[3].y = pts[1].y;
+	pts[4].x = pts[1].x - o->un2ix(csi*cl - si*cw/2.0);
+	pts[4].y = pts[1].y + o->un2iy(si*cl + csi*cw/2.0);
+	switch(type & 0xff) {
+	case ARROW_NOCAP:
+		pts[2].x = pts[3].x = pts[4].x = pts[1].x;
+		pts[2].y = pts[3].y = pts[4].y = pts[1].y;
+		break;
+		}
+	UpdateMinMaxRect(&rDims, pts[2].x, pts[2].y);
+	UpdateMinMaxRect(&rDims, pts[4].x, pts[4].y);
+	IncrementMinMaxRect(&rDims, 3*o->un2ix(LineDef.width)+3);
+	Redraw(o);
+}
+
+void
+Arrow::DoMark(anyOutput *o, bool mark)
+{
+	LineDEF OldLine;
+
+	if(type & ARROW_UNITS) {
+		if(!dh1) dh1 = new dragHandle(this, DH_12);
+		if(!dh2) dh2 = new dragHandle(this, DH_22);
+		}
+	else {
+		if (dh1) DeleteGO(dh1);		if (dh2) DeleteGO(dh2);
+		dh1 = dh2 = 0L;
+		}
+	if(mark) {
+		if(mo) DelBitmapClass(mo);					mo = 0L;
+		if(dh1 && dh2) {
+			memcpy(&mrc, &rDims, sizeof(RECT));		memcpy(&OldLine, &LineDef, sizeof(LineDEF));
+			mo = GetRectBitmap(&mrc, o);			Redraw(o);
+			dh1->DoPlot(o);		dh2->DoPlot(o);
+			}
+		else {
+			memcpy(&mrc, &rDims, sizeof(RECT));		memcpy(&OldLine, &LineDef, sizeof(LineDEF));
+			mo = GetRectBitmap(&mrc, o);			LineDef.color = 0x00000000L;
+			LineDef.width = OldLine.width *3.0;		Redraw(o);
+			LineDef.width = OldLine.width;			LineDef.color = OldLine.color ^ 0x00ffffffL;
+			Redraw(o);								o->UpdateRect(&mrc, false);
+			memcpy(&LineDef, &OldLine, sizeof(LineDEF));
+			}
+		}
+	else if(mo) RestoreRectBitmap(&mo, &mrc, o);
+}
+
+bool
+Arrow::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+
+	switch (cmd) {
+	case CMD_SAVEPOS:
+		bModified = true;
+		Undo.SaveLFP(this, &pos1, 0L);
+		Undo.SaveLFP(this, &pos2, UNDO_CONTINUE);
+		return true;
+	case CMD_SCALE:
+		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;		LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		cw *= ((scaleINFO*)tmpl)->sy.fy;				cl *= ((scaleINFO*)tmpl)->sy.fy;
+		if(type & ARROW_UNITS) {
+			pos1.fx = ((scaleINFO*)tmpl)->sx.fx + pos1.fx * ((scaleINFO*)tmpl)->sx.fy;
+			pos1.fy = ((scaleINFO*)tmpl)->sy.fx + pos1.fy * ((scaleINFO*)tmpl)->sy.fy;
+			pos2.fx = ((scaleINFO*)tmpl)->sx.fx + pos2.fx * ((scaleINFO*)tmpl)->sx.fy;
+			pos2.fy = ((scaleINFO*)tmpl)->sy.fx + pos2.fy * ((scaleINFO*)tmpl)->sy.fy;
+			}
+		return true;
+	case CMD_FLUSH:
+		if (dh1) DeleteGO(dh1);			dh1 = 0L;
+		if (dh2) DeleteGO(dh2);			dh2 = 0L;
+		if(mo) DelBitmapClass(mo);		mo = 0L;
+		if(ssRef) free(ssRef);			ssRef = 0L;
+		if(name) free(name);			name = 0L;
+		return true;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+		if(!CurrGO && ObjThere(mev->x, mev->y)){
+			o->ShowMark(this, MRK_GODRAW);
+			return true;
+			}
+		}
+		break;
+	case CMD_ARROW_ORG:
+		memcpy(&pos1, tmpl, sizeof(lfPOINT));
+		if(ssRef && cssRef >3) 
+			ssRef[0].x = ssRef[0].y = ssRef[1].x = ssRef[1].y = -1;
+		return true;
+	case CMD_ARROW_TYPE:
+		if(tmpl) {
+			type &= ~0xff;		type |= (*((int*)tmpl));
+			return true;
+			}
+		return false;
+	case CMD_SET_DATAOBJ:
+		Id = GO_ARROW;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_UPDATE:
+		if(ssRef && cssRef >3 && data) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &pos1.fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &pos1.fy);
+			data->GetValue(ssRef[2].y, ssRef[2].x, &pos2.fx);
+			data->GetValue(ssRef[3].y, ssRef[3].x, &pos2.fy);
+			return true;
+			}
+		return false;
+	case CMD_AUTOSCALE:
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			((Plot*)parent)->CheckBounds(pos1.fx, pos1.fy);
+			((Plot*)parent)->CheckBounds(pos2.fx, pos2.fy);
+			return true;
+			}
+		break;
+	case CMD_MRK_DIRTY:			//from Undo ?
+	case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_MOVE:
+		bModified = true;
+	case CMD_UNDO_MOVE:
+		if(type & ARROW_UNITS) {
+			if(cmd == CMD_MOVE) Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
+			pos1.fx += ((lfPOINT*)tmpl)[0].fx;	pos1.fy += ((lfPOINT*)tmpl)[0].fy;
+			pos2.fx += ((lfPOINT*)tmpl)[0].fx;	pos2.fy += ((lfPOINT*)tmpl)[0].fy;
+			if(o){
+				o->StartPage();		parent->DoPlot(o);		o->EndPage();
+				}
+			return true;
+			}
+		break;
+		}
+	return false;
+}
+
+void
+Arrow::Redraw(anyOutput *o)
+{
+	FillDEF FillCap;
+
+	o->SetLine(&LineDef);
+	o->oSolidLine(pts);
+	switch(type & 0xff) {
+	case ARROW_NOCAP:
+		break;
+	case ARROW_LINE:
+		o->oSolidLine(pts+2);
+		o->oSolidLine(pts+3);
+		break;
+	case ARROW_TRIANGLE:
+		FillCap.type = FILL_NONE;
+		FillCap.color = LineDef.color;
+		FillCap.scale = 1.0f;
+		FillCap.hatch = 0L;
+		o->SetFill(&FillCap);
+		o->oPolygon(pts+2, 3);
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// universal boxes
+Box::Box(GraphObj * par, DataObj *d, lfPOINT fp1, lfPOINT fp2, int which,
+	int xc1, int xr1, int yc1, int yr1, int xc2, int xr2,
+		int yc2, int yr2):GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	memcpy(&pos1, &fp1, sizeof(lfPOINT));
+	memcpy(&pos2, &fp2, sizeof(lfPOINT));
+	type = which;
+	Id = GO_BOX;
+	if(xc1 >= 0 || xr1 >= 0 || yc1 >= 0 || yr1 >= 0 || xc2 >= 0 || xr2 >= 0 || 
+		yc2 >= 0 || yr2 >= 0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*4)) {
+			ssRef[0].x = xc1;	ssRef[0].y = xr1;
+			ssRef[1].x = yc1;	ssRef[1].y = yr1;
+			ssRef[2].x = xc2;	ssRef[2].y = xr2;
+			ssRef[3].x = yc2;	ssRef[3].y = yr2;
+			cssRef = 4;
+			}
+		}
+}
+
+Box::Box(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+Box::~Box()
+{
+	Command(CMD_FLUSH, 0L, 0L);
+}
+
+double
+Box::GetSize(int select)
+{
+	switch(select) {
+	case SIZE_XPOS:
+		return (pos1.fx + pos2.fx)/2.0;
+	case SIZE_YPOS:
+		return (pos1.fy + pos2.fy)/2.0;
+		}
+	return 1.0;
+}
+
+bool
+Box::SetSize(int select, double value)
+{
+	switch(select & 0xfff) {
+	case SIZE_BOX_LINE:		Outline.width = value;		return true;
+	case SIZE_BOX:			size = value;				return true;
+	case SIZE_XPOS:			pos1.fx = value;			return true;
+	case SIZE_XPOS+1:		pos2.fx = value;			return true;
+	case SIZE_YPOS:			pos1.fy = value;			return true;
+	case SIZE_YPOS+1:		pos2.fy = value;			return true;
+		}
+	return false;
+}
+
+bool
+Box::SetColor(int select, DWORD col)
+{
+	switch(select & 0xfff) {
+	case COL_BOX_LINE:
+		Outline.color = col;
+		return true;
+		}
+	return false;
+}
+
+void
+Box::DoPlot(anyOutput *o)
+{
+	double si, csi, tmp, fix1, fiy1, fix2, fiy2, fsize, dx, dy;
+
+	if(!parent || !o || size <= 0.001) return;
+	o->SetLine(&Outline);		o->SetFill(&Fill);
+	if(mo) DelBitmapClass(mo);	mo = 0L;
+	//calculate coordinates
+	fix1 = o->fx2fix(pos1.fx);	fix2 = o->fx2fix(pos2.fx);
+	fiy1 = o->fy2fiy(pos1.fy);	fiy2 = o->fy2fiy(pos2.fy);
+	//calculate sine and cosine
+	si = fiy1-fiy2;
+	tmp = fix2 - fix1;
+	si = si/sqrt(si*si + tmp*tmp);
+	csi = fix2-fix1;
+	tmp = fiy2 - fiy1;
+	csi = csi/sqrt(csi*csi + tmp*tmp);
+	if(type & BAR_WIDTHDATA) {			//use e.g. for density distribution
+		dx = si * size;					dy = csi * size;
+		pts[0].x = o->fx2ix(pos1.fx + dx);	pts[1].x = o->fx2ix(pos2.fx + dx);
+		pts[2].x = o->fx2ix(pos2.fx - dx);	pts[3].x = o->fx2ix(pos1.fx - dx);
+		pts[0].y = o->fy2iy(pos1.fy + dy);	pts[1].y = o->fy2iy(pos2.fy + dy);
+		pts[2].y = o->fy2iy(pos2.fy - dy);	pts[3].y = o->fy2iy(pos1.fy - dy);
+		}
+	else if(type & BAR_RELWIDTH) {
+		if(!parent || (pos1.fy == pos2.fy && pos1.fx == pos2.fx)) return;
+		fsize = parent->GetSize(pos1.fy == pos2.fy ? SIZE_BOXMINY : SIZE_BOXMINX);
+		fsize = fsize * size /200.0;
+		dx = si * fsize;					dy = csi * fsize;
+		pts[0].x = o->fx2ix(pos1.fx + dx);	pts[1].x = o->fx2ix(pos2.fx + dx);
+		pts[2].x = o->fx2ix(pos2.fx - dx);	pts[3].x = o->fx2ix(pos1.fx - dx);
+		pts[0].y = o->fy2iy(pos1.fy + dy);	pts[1].y = o->fy2iy(pos2.fy + dy);
+		pts[2].y = o->fy2iy(pos2.fy - dy);	pts[3].y = o->fy2iy(pos1.fy - dy);
+		}
+	else {
+		dx = o->un2fix(si*size/2.0);		dy = o->un2fiy(csi*size/2.0);
+		pts[0].x = iround(fix1 + dx);	pts[1].x = iround(fix2 + dx);
+		pts[2].x = iround(fix2 - dx);	pts[3].x = iround(fix1 - dx);
+		pts[0].y = iround(fiy1 + dy);	pts[1].y = iround(fiy2 + dy);
+		pts[2].y = iround(fiy2 - dy);	pts[3].y = iround(fiy1 - dy);
+		}
+	pts[4].x = pts[0].x;		pts[4].y = pts[0].y;	//close polygon
+	if(pts[0].x == pts[1].x){
+		o->oRectangle(pts[3].x, pts[3].y, pts[1].x, pts[1].y, name);
+		}
+	else {
+		o->oPolygon(pts, 5);
+		}
+	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[2].x, pts[2].y);
+	UpdateMinMaxRect(&rDims, pts[1].x, pts[1].y);
+	UpdateMinMaxRect(&rDims, pts[3].x, pts[3].y);
+	IncrementMinMaxRect(&rDims, o->un2ix(Outline.width*6)+3);
+}
+
+void
+Box::DoMark(anyOutput *o, bool mark)
+{
+	if(mark){
+		if(mo) DelBitmapClass(mo);			mo = 0L;
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		IncrementMinMaxRect(&mrc, o->un2ix(Outline.width*6)+3);
+		mo = GetRectBitmap(&mrc, o);
+		InvertLine(pts, 5, &Outline, &rDims, o, mark);
+		}
+	else if(mo) RestoreRectBitmap(&mo, &mrc, o);
+}
+
+bool
+Box::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	POINT p;
+
+	switch (cmd) {
+	case CMD_SCALE:
+		Outline.width *= ((scaleINFO*)tmpl)->sy.fy;
+		Hatchline.width *= ((scaleINFO*)tmpl)->sy.fy;
+		Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;
+		if(!(type & BAR_RELWIDTH) && parent->Id!= GO_DENSDISP)size *= ((scaleINFO*)tmpl)->sy.fy;
+		return true;
+	case CMD_FLUSH:
+		if(ssRef) free(ssRef);		ssRef = 0L;
+		if(name)free(name);			name = 0L;
+		if(mo) DelBitmapClass(mo);		mo = 0L;
+		return true;
+	case CMD_LEGEND:
+		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		((Legend*)tmpl)->HasFill(&Outline, &Fill, name);
+		break;
+	case CMD_MRK_DIRTY:				case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, p.x = mev->x, p.y = mev->y) && !CurrGO) {
+				if(IsInPolygon(&p, pts, 5)) {
+					o->ShowMark(CurrGO = this, MRK_GODRAW);
+					return true;
+					}
+				}
+			break;
+			}
+		return false;
+	case CMD_SET_DATAOBJ:
+		Id = GO_BOX;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_UPDATE:
+		if(ssRef && cssRef >3 && data) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &pos1.fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &pos1.fy);
+			data->GetValue(ssRef[2].y, ssRef[2].x, &pos2.fx);
+			data->GetValue(ssRef[3].y, ssRef[3].x, &pos2.fy);
+			return true;
+			}
+		return false;
+	case CMD_BOX_TYPE:
+		if(tmpl)type = *((int*)tmpl);
+		return true;
+	case CMD_BOX_FILL:
+		if(tmpl) {
+			memcpy(&Fill, tmpl, sizeof(FillDEF));
+			if(Fill.hatch) memcpy(&Hatchline, Fill.hatch, sizeof(LineDEF));
+			Fill.hatch = &Hatchline;
+			return true;
+			}
+		break;
+	case CMD_AUTOSCALE:
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			if(type & BAR_WIDTHDATA) {
+				if(pos1.fy != pos2.fy) {
+					((Plot*)parent)->CheckBounds(pos1.fx+size, pos1.fy);
+					((Plot*)parent)->CheckBounds(pos2.fx-size, pos2.fy);
+					}
+				else {
+					((Plot*)parent)->CheckBounds(pos1.fx, pos1.fy+size);
+					((Plot*)parent)->CheckBounds(pos2.fx, pos2.fy-size);
+					}
+				}
+			else {
+				((Plot*)parent)->CheckBounds(pos1.fx, pos1.fy);
+				((Plot*)parent)->CheckBounds(pos2.fx, pos2.fy);
+				if(parent && (type & BAR_RELWIDTH)) {
+					if(pos1.fy == pos2.fy) {
+						((Plot*)parent)->CheckBounds(pos2.fx, pos2.fy + parent->GetSize(SIZE_BOXMINY));
+						((Plot*)parent)->CheckBounds(pos2.fx, pos2.fy - parent->GetSize(SIZE_BOXMINY));
+						}
+					else if(pos1.fx == pos2.fx) {
+						((Plot*)parent)->CheckBounds(pos2.fx + parent->GetSize(SIZE_BOXMINX), pos2.fy);
+						((Plot*)parent)->CheckBounds(pos2.fx - parent->GetSize(SIZE_BOXMINX), pos2.fy);
+						}
+					}
+				}
+			return true;
+			}
+		break;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// whisker 
+Whisker::Whisker(GraphObj *par, DataObj *d, lfPOINT fp1, lfPOINT fp2, int which,
+	int xc1, int xr1, int yc1, int yr1, int xc2, int xr2,
+	int yc2, int yr2):GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	memcpy(&pos1, &fp1, sizeof(lfPOINT));
+	memcpy(&pos2, &fp2, sizeof(lfPOINT));
+	type = which;
+	Id = GO_WHISKER;
+	if(xc1 >= 0 || xr1 >= 0 || yc1 >= 0 || yr1 >= 0 || xc2 >= 0 || xr2 >= 0 || 
+		yc2 >= 0 || yr2 >= 0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*4)) {
+			ssRef[0].x = xc1;	ssRef[0].y = xr1;
+			ssRef[1].x = yc1;	ssRef[1].y = yr1;
+			ssRef[2].x = xc2;	ssRef[2].y = xr2;
+			ssRef[3].x = yc2;	ssRef[3].y = yr2;
+			cssRef = 4;
+			}
+		}
+	Command(CMD_AUTOSCALE, 0L, 0L);
+}
+
+Whisker::Whisker(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+Whisker::~Whisker()
+{
+	if(mo) DelBitmapClass(mo);	mo = 0L;
+	if(ssRef) free(ssRef);		ssRef = 0L;
+	if(name) free(name);		name = 0L;
+}
+
+bool
+Whisker::SetSize(int select, double value)
+{
+	switch(select & 0xfff) {
+	case SIZE_WHISKER: 
+		size = value;
+		return true;
+	case SIZE_WHISKER_LINE:
+		LineDef.width = value;
+		return true;
+		}
+	return false;
+}
+
+bool
+Whisker::SetColor(int select, DWORD col)
+{
+	switch(select & 0xfff) {
+	case COL_WHISKER:
+		LineDef.color = col;
+		return true;
+		}
+	return false;
+}
+
+void
+Whisker::DoPlot(anyOutput *o)
+{
+	double si, csi, tmp, fix1, fiy1, fix2, fiy2, dx, dy;
+	int i;
+
+	fix1 = o->fx2fix(pos1.fx);	fix2 = o->fx2fix(pos2.fx);
+	fiy1 = o->fy2fiy(pos1.fy);	fiy2 = o->fy2fiy(pos2.fy);
+	if(fix1 == fix2 && fiy1 == fiy2) return;	//zero length
+	pts[2].x = iround(fix1);		pts[3].x = iround(fix2);
+	pts[2].y = iround(fiy1);		pts[3].y = iround(fiy2);
+	//calculate sine and cosine
+	si = fiy1-fiy2;
+	tmp = fix2 - fix1;
+	si = si/sqrt(si*si + tmp*tmp);
+	csi = fix2-fix1;
+	tmp = fiy2 - fiy1;
+	csi = csi/sqrt(csi*csi + tmp*tmp);
+	dx = o->un2fix(si*size/2.0);
+	dy = o->un2fiy(csi*size/2.0);
+	//calc cap
+	pts[0].x = iround(fix1 - dx);	pts[4].x = iround(fix2 - dx);
+	pts[0].y = iround(fiy1 - dy);	pts[4].y = iround(fiy2 - dy);
+	pts[1].x = iround(fix1 + dx);	pts[5].x = iround(fix2 + dx);
+	pts[1].y = iround(fiy1 + dy);	pts[5].y = iround(fiy2 + dy);
+	//draw whisker
+	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+	UpdateMinMaxRect(&rDims, pts[4].x, pts[4].y);
+	UpdateMinMaxRect(&rDims, pts[5].x, pts[5].y);
+	IncrementMinMaxRect(&rDims, 3+(o->un2ix(LineDef.width)<<1));
+	o->SetLine(&LineDef);
+	switch(type & 0x0f) {
+	case 2:
+		pts[4].x = pts[3].x;	pts[4].y = pts[3].y;
+		pts[1].x = pts[2].x;	pts[1].y = pts[2].y;
+	case 3:
+		if((type & 0x0f) == 3){
+			pts[5].x = pts[3].x;	pts[5].y = pts[3].y;
+			pts[0].x = pts[2].x;	pts[0].y = pts[2].y;
+			}
+	case 0:
+		for(i = 0; i < 5; i+=2) o->oSolidLine(pts+i);
+		break;
+	case 1:
+		pts[4].x = pts[5].x = pts[3].x;		pts[4].y = pts[5].y = pts[3].y;
+		pts[1].x = pts[0].x = pts[2].x;		pts[1].y = pts[0].y = pts[2].y;
+		o->oSolidLine(pts+2);
+		break;
+		}
+}
+
+void
+Whisker::DoMark(anyOutput *o, bool mark)
+{
+	int i;
+	LineDEF OldLine;
+
+	if(mark){
+		if(mo) DelBitmapClass(mo);					mo = 0L;
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		memcpy(&OldLine, &LineDef, sizeof(LineDEF));
+		i = 3*o->un2ix(LineDef.width);		//increase size of rectangle for marks
+		IncrementMinMaxRect(&mrc, i);		mo = GetRectBitmap(&mrc, o);
+		LineDef.width *= 5.0;				DoPlot(o);
+		LineDef.width = OldLine.width;		LineDef.color = OldLine.color ^ 0x00ffffffL;
+		DoPlot(o);							o->UpdateRect(&mrc, false);
+		memcpy(&LineDef, &OldLine, sizeof(LineDEF));
+		}
+	else RestoreRectBitmap(&mo, &mrc, o);
+}
+
+bool
+Whisker::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	int cb;
+
+	switch (cmd) {
+	case CMD_SCALE:
+		size *= ((scaleINFO*)tmpl)->sy.fy;
+		LineDef.width *= ((scaleINFO*)tmpl)->sy.fy;
+		LineDef.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+		return true;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
+				if(IsCloseToLine(&pts[2], &pts[3], mev->x, mev->y) ||
+					IsCloseToLine(&pts[0], &pts[1], mev->x, mev->y) ||
+					IsCloseToLine(&pts[4], &pts[5], mev->x, mev->y)) {
+						o->ShowMark(this, MRK_GODRAW);
+						return true;
+						}
+				}
+			break;
+			}
+		return false;
+	case CMD_ERRDESC:
+		if(tmpl && *((char*)tmpl)) {
+			cb = (int)strlen((char*)tmpl)+2;
+			if(name = (char*)realloc(name, cb)) rlp_strcpy(name, cb, (char*)tmpl);
+			return true;
+			}
+		return false;
+	case CMD_LEGEND:
+		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		switch(type & 0x0f) {
+		case 1:		((Legend*)tmpl)->HasErr(&LineDef, 5, name);		break;
+		case 2:		((Legend*)tmpl)->HasErr(&LineDef, 4, name);		break;
+		case 3:		((Legend*)tmpl)->HasErr(&LineDef, 3, name);		break;
+		default:
+			if((rDims.right - rDims.left) < (rDims.bottom - rDims.top))
+				((Legend*)tmpl)->HasErr(&LineDef, 1, name);
+			else ((Legend*)tmpl)->HasErr(&LineDef, 2, name);
+			break;
+			}
+		break;
+	case CMD_SET_DATAOBJ:
+		Id = GO_WHISKER;		data = (DataObj *)tmpl;
+		return true;
+	case CMD_UPDATE:
+		if(ssRef && cssRef >3 && data) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &pos1.fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &pos1.fy);
+			data->GetValue(ssRef[2].y, ssRef[2].x, &pos2.fx);
+			data->GetValue(ssRef[3].y, ssRef[3].x, &pos2.fy);
+			return true;
+			}
+		return false;
+	case CMD_AUTOSCALE:
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			((Plot*)parent)->CheckBounds(pos1.fx, pos1.fy);
+			((Plot*)parent)->CheckBounds(pos2.fx, pos2.fy);
+			return true;
+			}
+		break;
+	case CMD_WHISKER_STYLE:
+		if(tmpl) type = *((int*)tmpl);
+		return true;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// drop line 
+DropLine::DropLine(GraphObj *par, DataObj *d, double x, double y, int which, int xc, 
+	int xr, int yc, int yr):GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	fPos.fx = x;
+	fPos.fy = y;
+	type = which;
+	Id = GO_DROPLINE;
+	if(xc >= 0 && xr >= 0 && yc >= 0 && yr >= 0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*2)) {
+			ssRef[0].x = xc;	ssRef[0].y = xr;
+			ssRef[1].x = yc;	ssRef[1].y = yr;
+			cssRef = 2;
+			}
+		}
+	bModified = false;
+}
+
+DropLine::DropLine(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	bModified = false;
+}
+
+DropLine::~DropLine()
+{
+	if(bModified) Undo.InvalidGO(this);
+	if(ssRef) free(ssRef);		ssRef = 0L;
+}
+
+void
+DropLine::DoPlot(anyOutput *o)
+{
+	int tmp;
+
+	o->RLP.fp = 0.0f;		//reset line pattern start
+	if(parent) {
+		pts[0].x = pts[1].x = pts[2].x = pts[3].x = o->fx2ix(fPos.fx);
+		pts[0].y = pts[1].y = pts[2].y = pts[3].y = o->fy2iy(fPos.fy);
+		if(type & DL_LEFT) {
+			tmp = o->fx2ix(parent->GetSize(SIZE_BOUNDS_LEFT));
+			if(tmp < pts[0].x) pts[0].x = tmp;
+			if(tmp > pts[1].x) pts[1].x = tmp;
+			}
+		if(type & DL_RIGHT) {
+			tmp = o->fx2ix(parent->GetSize(SIZE_BOUNDS_RIGHT));
+			if(tmp < pts[0].x) pts[0].x = tmp;
+			if(tmp > pts[1].x) pts[1].x = tmp;
+			}
+		if(type & DL_YAXIS) {
+			tmp = iround(parent->GetSize(SIZE_YAXISX));
+			if(tmp < pts[0].x) pts[0].x = tmp;
+			if(tmp > pts[1].x) pts[1].x = tmp;
+			}
+		if(type & DL_TOP) {
+			tmp = o->fy2iy(parent->GetSize(SIZE_BOUNDS_TOP));
+			if(tmp < pts[2].y) pts[2].y = tmp;
+			if(tmp > pts[3].y) pts[3].y = tmp;
+			}
+		if(type & DL_BOTTOM) {
+			tmp = o->fy2iy(parent->GetSize(SIZE_BOUNDS_BOTTOM));
+			if(tmp < pts[2].y) pts[2].y = tmp;
+			if(tmp > pts[3].y) pts[3].y = tmp;
+			}
+		if(type & DL_XAXIS) {
+			tmp = iround(parent->GetSize(SIZE_XAXISY));
+			if(tmp < pts[2].y) pts[2].y = tmp;
+			if(tmp > pts[3].y) pts[3].y = tmp;
+			}
+		SetMinMaxRect(&rDims, pts[0].x, pts[2].y, pts[1].x, pts[3].y);
+		IncrementMinMaxRect(&rDims, 3);
+		o->SetLine(&LineDef);
+		o->oPolyline(pts, 2);
+		o->oPolyline(pts+2, 2);
+		}
+}
+
+void
+DropLine::DoMark(anyOutput *o, bool mark)
+{
+
+	InvertLine(pts, 2, &LineDef, 0L, o, mark);
+	InvertLine(pts+2, 2, &LineDef, &rDims, o, mark);
+}
+
+bool
+DropLine::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
+				if(IsCloseToLine(&pts[0], &pts[1], mev->x, mev->y) ||
+					IsCloseToLine(&pts[2], &pts[3], mev->x, mev->y)) {
+					o->ShowMark(this, MRK_GODRAW);
+					return true;
+					}
+				}
+			break;
+			}
+		return false;
+	case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_DL_LINE:
+		if(tmpl) memcpy(&LineDef, tmpl, sizeof(LineDEF));
+		return true;
+	case CMD_DL_TYPE:
+		if(tmpl)type = *((int*)tmpl);
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_DROPLINE;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_UPDATE:
+		if(ssRef && cssRef >1 && data) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
+			return true;
+			}
+		return false;
+	case CMD_AUTOSCALE:
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
+			return true;
+			}
+		break;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// define a spherical scanline used for clipping spheres
+sph_scanline::sph_scanline(POINT3D *center, int radius, bool bVert):GraphObj(0, 0)
+{
+	Id = GO_SPHSCANL;
+	rad = radius >= 0 ? radius : -radius;
+	memcpy(&p1, center, sizeof(POINT3D));	memcpy(&p2, center, sizeof(POINT3D));
+	memcpy(&cent, center, sizeof(POINT3D));
+	if(vert = bVert) {
+		p1.y -= rad;		p2.y += rad;
+		}
+	else {
+		p1.x -= rad;		p2.x += rad;
+		}
+	if(p1.x < 1) p1.x = 1;			if(p1.y < 1) p1.y = 1;
+	if(p2.x < p1.x) p2.x = p1.x;	if(p2.y < p1.y) p2.y = p1.y;
+	bValid1 = bValid2 = true;
+}
+
+bool
+sph_scanline::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch(cmd) {
+	case CMD_ADDTOLINE:
+		if(bValid1 && tmpl){
+			memcpy(&p2, tmpl, sizeof(POINT3D));
+			bValid2 = true;
+			return true;
+			}
+		break;
+	case CMD_STARTLINE:
+		if(!bValid1 && tmpl){
+			memcpy(&p1, tmpl, sizeof(POINT3D));
+			bValid1 = true;
+			}
+		break;
+		return true;
+		}
+	return false;
+}
+
+void
+sph_scanline::DoClip(GraphObj *co)
+{
+	POINT3D *pla;
+	int np, i;
+
+	if(!bValid1 || !bValid2) return;
+	switch(co->Id){
+	case GO_SPHERE:
+		bValid1 = bValid2 = false;
+		clip_sphline_sphere(this, &p1, &p2, &cent, rad, iround(co->GetSize(SIZE_RADIUS1)), 
+			iround(co->GetSize(SIZE_XPOS)), iround(co->GetSize(SIZE_YPOS)), 
+			iround(co->GetSize(SIZE_ZPOS)));
+		break;
+	case GO_PLANE:
+		for(i=0; ((plane*)co)->GetPolygon(&pla, &np, i); i++) {
+			bValid1 = bValid2 = false;
+			clip_sphline_plane(this, &p1, &p2, &cent, rad, pla, np, ((plane*)co)->GetVec());
+			}
+		break;
+		}
+}
+	
+bool
+sph_scanline::GetPoint(POINT *p, int sel)
+{
+	if(!bValid1 || !bValid2) {
+		p->x = p->y = 0;
+		return false;
+		}
+	switch(sel) {
+	case 1:
+		p->x = p1.x;	p->y = p1.y;
+		return true;
+	case 2:
+		p->x = p2.x;	p->y = p2.y;
+		return true;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Sphere: a symbol in three dimensional space
+Sphere::Sphere(GraphObj *par, DataObj *d, int sel, double x, double y, double z, 
+	double r, int xc, int xr, int yc, int yr, int zc, int zr, int rc, int rr)
+	:GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	Id = GO_SPHERE;
+	fPos.fx = x;	fPos.fy = y;	fPos.fz = z;	size = r;
+	if(xc >=0 || xr >=0 || yc >=0 || yr >=0 || zc >=0 || zr >=0 || rc >=0 || rr >=0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*4)) {
+			ssRef[0].x = xc;	ssRef[0].y = xr;
+			ssRef[1].x = yc;	ssRef[1].y = yr;
+			ssRef[2].x = zc;	ssRef[2].y = zr;
+			ssRef[3].x = rc;	ssRef[3].y = rr;
+			cssRef = 4;
+			}
+		}
+	type = sel;
+	bModified = false;
+}
+
+Sphere::Sphere(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	bModified = false;
+}
+
+Sphere::~Sphere()
+{
+	if(bModified) Undo.InvalidGO(this);
+	Command(CMD_FLUSH, 0L, 0L);
+}
+
+double
+Sphere::GetSize(int select)
+{
+	switch(select) {
+	case SIZE_MIN_X:		return fip.fx - (double)rx;
+	case SIZE_MAX_X:		return fip.fx + (double)rx;
+	case SIZE_MIN_Y:		return fip.fy - (double)ry;
+	case SIZE_MAX_Y:		return fip.fy + (double)ry;
+	case SIZE_MIN_Z:		return fip.fz - (double)rx;
+	case SIZE_MAX_Z:		return fip.fz + (double)rx;
+	case SIZE_XPOS:			return fip.fx;
+	case SIZE_YPOS:			return fip.fy;
+	case SIZE_ZPOS:			return fip.fz;
+	case SIZE_RADIUS1:	case SIZE_RADIUS2:	return (double)rx;
+	case SIZE_XCENT:		return fPos.fx;
+	case SIZE_YCENT:		return fPos.fy;
+	case SIZE_ZCENT:		return fPos.fz;
+	case SIZE_DRADIUS:		return size;
+	case SIZE_SYMBOL:
+		if(!type) return size;
+		else return DefSize(SIZE_SYMBOL);
+	case SIZE_SYM_LINE:		return Line.width;
+		}
+	return 0.0;
+}
+
+bool
+Sphere::SetSize(int select, double value)
+{
+	switch(select & 0xfff) {
+	case SIZE_SYMBOL:		size = value;			break;
+	case SIZE_SYM_LINE:		Line.width = value;		break;
+		}
+	return true;
+}
+
+DWORD
+Sphere::GetColor(int select)
+{
+	switch(select) {
+	case COL_SYM_LINE:		return Line.color;
+	case COL_SYM_FILL:		return Fill.color;
+	default: return defs.Color(select);
+		}
+}
+
+bool
+Sphere::SetColor(int select, DWORD col)
+{
+	switch(select & 0xfff) {
+	case COL_SYM_LINE:		Line.color = col;		break;
+	case COL_SYM_FILL:		Fill.color = col;		break;
+		}
+	return true;
+}
+
+void
+Sphere::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(size <= 0.001 || !o) return;
+	if(!o->fvec2ivec(&fPos, &fip)) return;
+	bDrawDone = false;
+	if(scl){
+		for(i = 0; i < nscl; i++) if(scl[i]) delete(scl[i]);
+		free(scl);
+		scl = 0L;	nscl = 0;
+		}
+	ix = iround(fip.fx);			iy = iround(fip.fy);
+	switch (type) {
+	case 1:
+		rx = ry = iround(size * 0.5 * o->ddx);		break;
+	case 2:
+		rx = ry = iround(size * 0.5 * o->ddy);		break;
+	case 3:
+		rx = ry = iround(size * 0.5 * o->ddz);		break;
+	default:
+		type = 0;
+	case 5:
+		rx = o->un2ix(size/2.0);	ry = o->un2iy(size/2.0);
+		break;
+		}
+	rDims.left = ix - rx;			rDims.right = ix + rx;
+	rDims.top = iy - ry;			rDims.bottom = iy + ry;
+	if(o->VPscale > 1.5 && (
+		(rDims.right < defs.clipRC.left) || (rDims.left > defs.clipRC.right) ||
+		(rDims.bottom < defs.clipRC.top) || (rDims.top > defs.clipRC.bottom))){
+		bDrawDone = true;		return;
+		}
+	if(parent && parent->Command(CMD_SET_GO3D, this, o)) return;
+	Command(CMD_REDRAW, 0L, o);
+}
+
+bool
+Sphere::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	int i;
+
+	switch (cmd) {
+	case CMD_FLUSH:
+		if(ssRef) free(ssRef);		ssRef = 0L;
+		if(name) free(name);		name = 0L;
+		if(scl){
+			for(i = 0; i < nscl; i++) if(scl[i]) delete(scl[i]);
+			free(scl);
+			scl = 0L;	nscl = 0;
+			}
+		return true;
+	case CMD_SCALE:
+		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
+		Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;
+		if(!type || type == 5)size *= ((scaleINFO*)tmpl)->sy.fy;;
+		return true;
+	case CMD_LEGEND:
+		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		((Legend*)tmpl)->HasFill(&Line, &Fill, 0L);
+		break;
+	case CMD_SYM_FILL:
+		if(tmpl) memcpy(&Fill, tmpl, sizeof(FillDEF));
+		return true;
+	case CMD_MRK_DIRTY:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_SET_DATAOBJ:
+		Id = GO_SPHERE;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_REDRAW:
+		//Note: this command is issued either by Undo (no output given) or
+		//  by Plot3D::DoPlot after sorting all objects (output specified)
+		if(!parent) return false;
+		if(!o) return parent->Command(cmd, tmpl, o);
+		if(bDrawDone) return false;
+		bDrawDone = true;
+		if(scl) DrawPG(o, 0);
+		else {
+			o->SetLine(&Line);				o->SetFill(&Fill);
+			if(Fill.type & FILL_LIGHT3D) o->oSphere(ix, iy, rx, 0L, 0, 0L);
+			else o->oCircle(ix-rx, iy-ry, ix+rx+1, iy+ry+1, name);
+			}
+		rx--;ry--;			//smaller marking rectangle
+		rDims.left = ix-rx-1;				rDims.right = ix+rx+1;
+		rDims.top = iy-ry-1;				rDims.bottom = iy+ry+1;
+		return true;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
+				o->ShowMark(&rDims, MRK_INVERT);
+				CurrGO = this;
+				return true;
+				}
+			break;
+			}
+		break;
+	case CMD_UPDATE:
+		if(ssRef && cssRef >1 && data) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
+			data->GetValue(ssRef[2].y, ssRef[2].x, &fPos.fz);
+			if(cssRef >3) data->GetValue(ssRef[3].y, ssRef[3].x, &size);
+			return true;
+			}
+		return false;
+	case CMD_CLIP:
+		if(co = (GraphObj*)tmpl){
+			switch(co->Id) {
+			case GO_PLANE:
+			case GO_SPHERE:
+				DoClip(co);
+				break;
+				}
+			}
+		return false;
+	case CMD_AUTOSCALE:
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			((Plot*)parent)->CheckBounds3D(fPos.fx, fPos.fy, fPos.fz);
+			return true;
+			}
+		break;
+		}
+	return false;
+}
+
+void
+Sphere::DoClip(GraphObj *co)
+{
+	RECT cliprc;
+	double d, d1;
+	POINT3D cscl;
+	int x, y, q, di, de, lim, cx, cy;
+	int i;
+
+	if(co && co->Id == GO_SPHERE) {
+		if(co->GetSize(SIZE_ZPOS) > fip.fz) return;
+		d1 = (d = co->GetSize(SIZE_XPOS) - fip.fx) * d;
+		d1 += (d = co->GetSize(SIZE_YPOS) - fip.fy) * d;
+		d1 = sqrt(d1 + (d = co->GetSize(SIZE_ZPOS) - fip.fz) * d);
+		if(d1 >= (co->GetSize(SIZE_RADIUS1) + rx))return;
+		}
+	else {
+		cliprc.left = iround(co->GetSize(SIZE_MIN_X));
+		cliprc.right = iround(co->GetSize(SIZE_MAX_X));
+		cliprc.top = iround(co->GetSize(SIZE_MIN_Y));
+		cliprc.bottom = iround(co->GetSize(SIZE_MAX_Y));
+		if(!OverlapRect(&rDims, &cliprc))return;
+		}
+	//use a list of horizontal scanlines created by a circular Bresenham's algorithm
+	//Ref: C. Montani, R. Scopigno (1990) "Speres-To-Voxel Conversion", in:
+	//   Graphic Gems (A.S. Glassner ed.) Academic Press, Inc.; 
+	//   ISBN 0-12-288165-5 
+	if(!scl && (scl = (sph_scanline**)calloc(rx*7+2, sizeof(sph_scanline*)))) {
+		cscl.z = iround(fip.fz);			nscl = 0;
+		for(q = 0; q < 2; q++) {
+			x = lim = 0;	y = rx;		di = 2*(1-rx);
+			while( y >= lim) {
+				if(di < 0) {
+					de = 2*di + 2*y -1;
+					if(de > 0) {
+						x++;	y--;	di += (2*x -2*y +2);
+						}
+					else {
+						x++;	di += (2*x +1);
+						}
+					}
+				else {
+					de = 2*di -2*x -1;
+					if(de > 0) {
+						y--;	di += (-2*y +1);
+						}
+					else {
+						x++;	y--;	di += (2*x -2*y +2);
+						}
+					}
+				switch(q) {
+				case 0:
+					cy = rx - y;			cx = x;
+					break;
+				case 1:
+					cy = rx + x;			cx = y; 
+					break;
+					}
+				cscl.y = iround(fip.fy);		cscl.x = iround(fip.fx);
+				cscl.y = cscl.y - rx + cy;
+				if(cx > 1) scl[nscl++] = new sph_scanline(&cscl, cx, false);
+				}
+			}
+		}
+	if(!scl) return;
+	//do clip for every scanline
+	for(i = 0; i < nscl; i++) if(scl[i]) scl[i]->DoClip(co);
+}
+
+void
+Sphere::DrawPG(anyOutput *o, int start)
+{
+	POINT *pts, np;
+	long cp = 0;
+	int i = start, step = 1;
+
+	if(!o || !scl ||!nscl) return;
+	if((pts = (POINT*)calloc(nscl*2, sizeof(POINT)))) {
+		do {
+			if(scl[i]) {
+				if(step > 0) scl[i]->GetPoint(&np, 1);
+				else scl[i]->GetPoint(&np, 2);
+				if(np.x && np.y){
+					AddToPolygon(&cp, pts, &np);
+					}
+				else if(cp){
+					if(step > 0) DrawPG(o, i);			//split sphere
+					step = -1;
+					}
+				}
+			if(i == nscl && step > 0) step = -1;
+			else i += step;
+			}while(i > start);
+		}
+	o->SetLine(&Line);				o->SetFill(&Fill);
+	if(cp) o->oSphere(ix, iy, rx, pts, cp, name);
+	free(pts);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// plane: utility object to draw a flat plane in 3D space
+plane::plane(GraphObj *par, DataObj *d, fPOINT3D *pts, int nPts, LineDEF *line, 
+	  FillDEF *fill):GraphObj(par, d)
+{
+	int i;
+	long area;
+	double vlength, v1[3], v2[3], vp[3], area1;
+	bool v_valid = false;
+	
+	nli = n_ipts = n_lines = 0;
+	nldata = 0L;  ldata = 0L;	co = 0L;	lines = 0L;		PlaneVec = 0L;
+	Id = GO_PLANE;	totalArea = 0;	bSigPol = false;
+	memcpy(&Line, line, sizeof(LineDEF));
+	memcpy(&Fill, fill, sizeof(FillDEF));
+	rDims.left = rDims.right = rDims.top = rDims.bottom = 0;
+	if(nPts > 2 && (ldata =(POINT3D**)calloc(1, sizeof(POINT3D*))) &&
+		(ldata[0] = (POINT3D *)calloc(nPts+1, sizeof(POINT3D))) &&
+		(nldata = (int*)calloc(1, sizeof(int)))	&&
+		(ipts = (POINT*)calloc(nPts, sizeof(POINT)))){
+		for(i = 0; i < nPts; i++) {
+			ipts[i].x = ldata[0][i].x = iround(pts[i].fx);
+			ipts[i].y = ldata[0][i].y = iround(pts[i].fy);
+			ldata[0][i].z = iround(pts[i].fz);
+			}
+		nldata[0] = nPts;		nli = 1;	n_ipts = nPts;
+		xBounds.fx = xBounds.fy = pts[0].fx;	yBounds.fx = yBounds.fy = pts[0].fy;
+		zBounds.fx = zBounds.fy = pts[0].fz;
+		rDims.left = rDims.right = ipts[0].x;	rDims.top = rDims.bottom = ipts[0].y;
+		for(i = 1; i < nPts; i++){
+			UpdateMinMaxRect(&rDims, ipts[i].x, ipts[i].y);
+			if(pts[i].fx < xBounds.fx) xBounds.fx = pts[i].fx;
+			if(pts[i].fx > xBounds.fy) xBounds.fy = pts[i].fx;
+			if(pts[i].fy < yBounds.fx) yBounds.fx = pts[i].fy;
+			if(pts[i].fy > yBounds.fy) yBounds.fy = pts[i].fy;
+			if(pts[i].fz < zBounds.fx) zBounds.fx = pts[i].fz;
+			if(pts[i].fz > zBounds.fy) zBounds.fy = pts[i].fz;
+			}
+		//test if plane vertical
+		area1 = (xBounds.fx - xBounds.fy) * (yBounds.fx - yBounds.fy);
+		for(area = 0, i = 1; i < nPts; i++) {
+			area += (ipts[i].x - ipts[i-1].x) * ((ipts[i].y + ipts[i-1].y)>>1);
+			}
+		totalArea= area = abs(area);
+		area1 = area ? fabs(area1/area) : 101.0;
+		if(area < 100 && area1 > 100.0) {			//its small or vertical !
+			if(lines=(line_segment**)calloc(nPts, sizeof(line_segment*))){
+				for(i = 1; i < nPts; i++) {
+					lines[i-1] = new line_segment(par, d, line, &ldata[0][i-1], &ldata[0][i]);
+					}
+				n_lines = nPts-1;
+				}
+			}
+		else {					//for a visible plane get vector perpendicular to plane
+			for (i = 1; i < (nPts-1); i++) {
+				v1[0] = pts[i].fx - pts[i-1].fx;	v1[1] = pts[i].fy - pts[i-1].fy;
+				v1[2] = pts[i].fz - pts[i-1].fz;	v2[0] = pts[i+1].fx - pts[i].fx;
+				v2[1] = pts[i+1].fy - pts[i].fy;	v2[2] = pts[i+1].fz - pts[i].fz;
+				vp[0] = v1[1]*v2[2] - v1[2]*v2[1];	vp[1] = v1[2]*v2[0] - v1[0]*v2[2];
+				vp[2] = v1[0]*v2[1] - v1[1]*v2[0];
+				vlength = sqrt(vp[0]*vp[0]+vp[1]*vp[1]+vp[2]*vp[2]);
+				if(v_valid = (vlength > 100.0)) break;
+				}
+			if(v_valid && (PlaneVec = (double*)malloc(4 * sizeof(double)))) {
+				PlaneVec[0] = vp[0]/vlength;		PlaneVec[1] = vp[1]/vlength;
+				PlaneVec[2] = -vp[2]/vlength;
+				PlaneVec[3] = PlaneVec[0] * pts[i].fx + PlaneVec[1] * pts[i].fy - PlaneVec[2] * pts[i].fz;
+				}
+			}
+		}
+}
+
+plane::~plane() 
+{
+	int i;
+
+	if(ldata) {
+		for(i = 0; i < nli; i++) if(ldata[i]) free(ldata[i]);
+		free(ldata);	ldata = 0L;		nli = 0;
+		}
+	if(lines) {
+		for(i = 0; i < n_lines; i++) if(lines[i]) delete(lines[i]);
+		free(lines);	lines = 0L;		n_lines = 0;
+		}
+	if(nldata) free(nldata);		nldata = 0L;
+	if(ipts) free(ipts);			ipts = 0L;
+	if(PlaneVec) free(PlaneVec);	PlaneVec = 0L;
+}
+
+double
+plane::GetSize(int select)
+{
+	switch(select) {
+	case SIZE_MIN_X:		return xBounds.fx;
+	case SIZE_MAX_X:		return xBounds.fy;
+	case SIZE_MIN_Y:		return yBounds.fx;
+	case SIZE_MAX_Y:		return yBounds.fy;
+	case SIZE_MIN_Z:		return zBounds.fx;
+	case SIZE_MAX_Z:		return zBounds.fy;
+		}
+	return 0.0;
+}
+
+void
+plane::DoPlot(anyOutput *o)
+{
+	int i;
+
+	bDrawDone = bReqPoint = false;
+	if(Fill.type & FILL_LIGHT3D){
+		Fill.color = o->VecColor(PlaneVec, Fill.color2, Fill.color);
+		Fill.type &= ~FILL_LIGHT3D;
+		}
+	if(o->VPscale > 1.5 && (
+		//ignore objects outside the display ara
+		(rDims.right < defs.clipRC.left) || (rDims.left > defs.clipRC.right) ||
+		(rDims.bottom < defs.clipRC.top) || (rDims.top > defs.clipRC.bottom))){
+		bDrawDone = true;		return;
+		}
+	if(lines) {
+		if(Line.width == 0.0) return;
+		//draw line segments for vertical plane
+		for(i = 0; i < n_lines; i++) if(lines[i]) lines[i]->DoPlot(o);
+		bDrawDone = true;		return;
+		}
+	if(parent && parent->Command(CMD_SET_GO3D, this, o)) return;
+	Command(CMD_REDRAW, 0L, o);
+}
+
+void
+plane::DoMark(anyOutput *o, bool mark)
+{
+	FillDEF tmpfill;
+	LineDEF tmpline;
+
+	memcpy(&tmpfill, &Fill, sizeof(FillDEF));
+	memcpy(&tmpline, &Line, sizeof(LineDEF));
+	if(mark){
+		tmpfill.color ^= 0x00ffffffL;		tmpline.color ^= 0x00ffffffL;
+		}
+	o->SetLine(&tmpline);					o->SetFill(&tmpfill);
+	o->oPolygon(ipts, n_ipts);
+}
+
+bool 
+plane::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	POINT *pt;
+	POINT3D *ap;
+	int i, j;
+	
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_REDRAW:
+		if(bDrawDone) return false;
+		bDrawDone = true;
+		if(o && nldata){
+			if(Line.width == 0.0) Line.color = Fill.color;
+			o->SetLine(&Line);			o->SetFill(&Fill);
+
+//FILE *dbg;
+//dbg = fopen("c:/test2.txt", "a");
+//fprintf(dbg, "\nplane with %d parts\n", nli);
+			for(i = 0; i < nli; i++){
+//fprintf(dbg, "\n");
+				if(nldata[i] > 2 && (pt = (POINT*)malloc(nldata[i]*sizeof(POINT)))){
+					for(j = 0; j < nldata[i]; j++) {
+//fprintf(dbg, "  %d %d\n", ldata[i][j].x, ldata[i][j].y);
+						pt[j].x = ldata[i][j].x;	pt[j].y = ldata[i][j].y;
+						}
+					o->oPolygon(pt, nldata[i]);
+					free(pt);
+					}
+				}
+//fclose(dbg);
+			}
+		return true;
+	case CMD_STARTLINE:
+		if(ap = (POINT3D*)tmpl) {
+			if(ldata && nldata && nli) {
+				if(bReqPoint) {
+					Command(CMD_ADDTOLINE, &ReqPoint, o);
+					bReqPoint = false;
+					}
+				i = nli-1;			j = nldata[i]-1;
+				if(ldata[i][j].x == ap->x && ldata[i][j].y == ap->y && ldata[i][j].z == ap->z){
+					return true;
+					}
+				if(IsValidPG(ldata[i], nldata[i])) {
+					//close previous polygon first
+					if(ldata[i][0].x != ldata[i][j].x || ldata[i][0].y != ldata[i][j].y ||
+						ldata[i][0].z != ldata[i][j].z){
+						j++;
+						ldata[i][j].x = ldata[i][0].x;	ldata[i][j].y = ldata[i][0].y;
+						ldata[i][j].z = ldata[i][0].z;	nldata[i]++;
+						}
+					ldata = (POINT3D**)realloc(ldata, sizeof(POINT3D*) * (nli+1));
+					ldata[nli] = (POINT3D*)malloc(sizeof(POINT3D)*2);
+					nldata = (int*)realloc(nldata, sizeof(int) * (nli+1));
+					}
+				else {					//drop incomplete or invalid polygon
+					nli--;
+					}
+				}
+			else {
+				ldata = (POINT3D**)calloc(1, sizeof(POINT3D*));
+				ldata[nli = 0] = (POINT3D*)malloc(sizeof(POINT3D));
+				nldata = (int*)calloc(1, sizeof(int));
+				bReqPoint = false;
+				}
+			if(bSigPol) {
+				n_linept = 1;	bSigPol = false;
+				}
+//			else n_linept = 0;
+			if(ldata && nldata) {
+				ldata[nli][0].x = ap->x;	ldata[nli][0].y = ap->y;
+				ldata[nli][0].z = ap->z;	nldata[nli++] = 1;
+				return true;
+				}
+			}
+		break;
+	case CMD_REQ_POINT:
+		if(ap = (POINT3D*)tmpl) {
+			ReqPoint.x = ap->x;		ReqPoint.y = ap->y;		ReqPoint.z = ap->z;
+			bReqPoint = true;
+			}
+		return true;
+	case CMD_SIGNAL_POL:			//signal: next point is on outline
+		bSigPol = true;
+		return true;
+	case CMD_ADDTOLINE:
+		if((ap = (POINT3D*)tmpl) && ldata && nldata && nli) {
+			i= nli-1;	j=nldata[i]-1;
+			if(ldata[i][j].x == ap->x && ldata[i][j].y == ap->y && ldata[i][j].z == ap->z){
+				//probably nothing to add
+				return j>0 ? true : false;
+				}
+			ldata[i] = (POINT3D*)realloc(ldata[i], ((j=nldata[i])+2) * sizeof(POINT3D));
+			ldata[i][j].x = ap->x;			ldata[i][j].y = ap->y;
+			ldata[i][j].z = ap->z;			nldata[i]++;
+			if(bSigPol) n_linept++;
+			bSigPol = false;
+			return true;
+			}
+	case CMD_CLIP:
+		bSigPol = false;
+		if(co = (GraphObj*)tmpl){
+			switch(co->Id) {
+			case GO_PLANE:
+				if(nli){
+					DoClip(co);
+					if(nli && ldata) {
+						i = nli-1;			j = nldata[i]-1;
+						//is last part valid ?
+						if(j < 2) {
+							free(ldata[i]);		ldata[i] = 0L;
+							nldata[i] = 0;
+							nli--;
+							}
+						//close last polygon
+						else if(ldata[i][0].x != ldata[i][j].x ||
+							ldata[i][0].y != ldata[i][j].y ||
+							ldata[i][0].z != ldata[i][j].z){
+							if(bSigPol) {
+								nldata[i]--;
+								}
+							else j++;
+							ldata[i][j].x = ldata[i][0].x;
+							ldata[i][j].y = ldata[i][0].y;
+							ldata[i][j].z = ldata[i][0].z;
+							nldata[i]++;
+							}
+						}
+					}
+				else xBounds.fx=xBounds.fy=yBounds.fx=yBounds.fy=zBounds.fx=zBounds.fy=0.0;
+				break;
+				}
+			}
+		break;
+		}
+	return false;
+}
+
+void * 
+plane::ObjThere(int x, int y)
+{
+	POINT p1;
+
+	if(bDrawDone && IsInRect(&rDims, p1.x = x, p1.y = y) &&
+		(IsInPolygon(&p1, ipts, n_ipts) || IsCloseToPL(p1, ipts, n_ipts))) return this;
+	return 0L;
+}
+
+bool
+plane::GetPolygon(POINT3D **pla, int *npt, int n)
+{
+	if(n < nli && ldata && ldata[n]) {
+		*pla = ldata[n];	*npt = nldata[n];
+		return true;
+		}
+	return false;
+}
+
+void
+plane::DoClip(GraphObj *co)
+{
+	RECT cliprc;
+	int o_nli, *o_nldata = 0L;
+	POINT3D **o_ldata = 0L;
+	POINT3D *tpg;
+	int i, j, tnpt;
+	bool is_valid = false;
+	double *co_vec, d;
+
+	//if two planes have the same parent it means they are part of one object
+	// do not clip!
+	if(co->parent == parent && co->Id == GO_PLANE) return;
+	//test if two planes are part of the same superplane
+#define TOL3D 1.0e-12
+	if(co->Id == GO_PLANE && (co_vec = ((plane*)co)->GetVec()) && PlaneVec && fabs(co_vec[0] - PlaneVec[0]) < TOL3D
+		&& fabs(co_vec[1]-PlaneVec[1]) < TOL3D && fabs(co_vec[2]-PlaneVec[2]) < TOL3D && ldata && nldata) {
+		d = (ldata[0][0].x * co_vec[0] + ldata[0][0].y * co_vec[1] - co_vec[3])/co_vec[2] - ldata[0][0].z;
+		if(fabs(d) < 2) return;
+		}
+#undef TOL3D
+	cliprc.left = iround(co->GetSize(SIZE_MIN_X));
+	cliprc.right = iround(co->GetSize(SIZE_MAX_X));
+	cliprc.top = iround(co->GetSize(SIZE_MIN_Y));
+	cliprc.bottom = iround(co->GetSize(SIZE_MAX_Y));
+	if(OverlapRect(&rDims, &cliprc) && co != this) {
+		o_nli = nli;		nli = 0;
+		o_nldata = nldata;	nldata = 0L;
+		o_ldata = ldata;	ldata = 0L;
+		switch(co->Id) {
+		case GO_PLANE:
+			//clip all parts of this plane with all from another plane
+			for(i = 0; ((plane*)co)->GetPolygon(&tpg, &tnpt, i); i++){
+				for(j = 0; j < o_nli; j++) {
+					n_linept = 0;
+					if(o_nldata[j] >2) clip_plane_plane(this, o_ldata[j], o_nldata[j], PlaneVec, 
+						tpg, tnpt, ((plane*)co)->GetVec(), ipts, n_ipts );
+					}
+				if(bReqPoint){
+					if(bSigPol) nldata[nli-1]--;
+					Command(CMD_ADDTOLINE, &ReqPoint, 0L);
+					bReqPoint = false;
+					}
+				if(nli) is_valid = true;
+				if(o_ldata) {
+					for(j = 0; j < o_nli; j++) if(o_ldata[j]) free(o_ldata[j]);
+					free(o_ldata);
+					}
+				if(o_nldata) free(o_nldata);
+				o_nli = nli;		nli = 0;
+				o_nldata = nldata;	nldata = 0L;
+				o_ldata = ldata;	ldata = 0L;
+				if(!o_nli) {					//plane is completly hidden
+					return;
+					}
+				}
+			if(is_valid || i==0){
+				nli = o_nli;		o_nli = 0;
+				nldata = o_nldata;	o_nldata = 0L;
+				ldata = o_ldata;	o_ldata = 0L;
+				}
+			if(nli > 1) for(i = 1; i < nli; i++) {
+				if(nldata[i] > 3 && ldata[i][nldata[i]-1].x == ldata[i-1][0].x
+					&& ldata[i][nldata[i]-1].y == ldata[i-1][0].y) {
+					nldata[i]--;	//bad vis: ignore last point
+					}
+				}
+			break;
+		default:
+			nli = o_nli;		o_nli = 0;
+			nldata = o_nldata;	o_nldata = 0L;
+			ldata = o_ldata;	o_ldata = 0L;
+			break;
+			}
+		//check shape and recalc some values
+		if(is_valid && nli) {
+			xBounds.fx = xBounds.fy = ldata[0][0].x;	yBounds.fx = yBounds.fy = ldata[0][0].y;
+			zBounds.fx = zBounds.fy = ldata[0][0].z;
+			rDims.left = rDims.right = ldata[0][0].x;	rDims.top = rDims.bottom = ldata[0][0].y;
+			for(i = 0; i < nli; i++) if(nldata[i] > 2){
+				if(ldata[i][0].x != ldata[i][nldata[i]-1].x || ldata[i][0].y != ldata[i][nldata[i]-1].y
+					|| ldata[i][0].z != ldata[i][nldata[i]-1].z) {
+					ldata[i][nldata[i]].x = ldata[i][0].x;	ldata[i][nldata[i]].y = ldata[i][0].y;
+					ldata[i][nldata[i]].z = ldata[i][0].z;	nldata[i]++;
+					}
+				for(j = 0; j < (nldata[i]-1); j++) {
+					UpdateMinMaxRect(&rDims, ldata[i][j].x, ldata[i][j].y);
+					if(ldata[i][j].x < xBounds.fx) xBounds.fx = ldata[i][j].x;
+					if(ldata[i][j].x > xBounds.fy) xBounds.fy = ldata[i][j].x;
+					if(ldata[i][j].y < yBounds.fx) yBounds.fx = ldata[i][j].y;
+					if(ldata[i][j].y > yBounds.fy) yBounds.fy = ldata[i][j].y;
+					if(ldata[i][j].z < zBounds.fx) zBounds.fx = ldata[i][j].z;
+					if(ldata[i][j].z > zBounds.fy) zBounds.fy = ldata[i][j].z;
+					}
+				}
+			}
+		}
+	if(o_ldata) {
+		for(i = 0; i < o_nli; i++) if(o_ldata[i]) free(o_ldata[i]);
+		free(o_ldata);
+		}
+	if(o_nldata) free(o_nldata);
+}
+
+bool
+plane::IsValidPG(POINT3D *pg, int npg)
+{
+	int i;
+	long area, ratio;
+
+	//if all points on an edge: not valid
+	if(n_linept >= npg) return false;
+	//a polygon must have more than 3 Points
+	if(npg < 3) return false;
+	//check for a reasonable size
+	for(area = 0, i = 1; i < npg; i++) {
+		area += (pg[i].x - pg[i-1].x) * ((pg[i].y + pg[i-1].y)>>1);
+		}
+	area += (pg[0].x - pg[i-1].x) * ((pg[0].y + pg[i-1].y)>>1);
+	area = abs(area);					if(area < 25) return false;
+	ratio = totalArea/area;
+	if(ratio > 100) return false;
+	return true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// a simple plane in three dimensional space
+Plane3D::Plane3D(GraphObj *par, DataObj *da, fPOINT3D *pt, long npt)
+	:GraphObj(par, da) 
+{
+	FileIO(INIT_VARS);
+	Id = GO_PLANE3D;
+	dt = (fPOINT3D*) memdup(pt, sizeof(fPOINT3D)*npt, 0L);
+	ndt = npt;
+}
+
+Plane3D::Plane3D(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+Plane3D::~Plane3D()
+{
+	if(dt) free(dt);	if(pts) free(pts);
+	dt = 0L;		ndt = 0L;
+	if(ipl) delete (ipl);	ipl = 0L;
+}
+
+bool
+Plane3D::SetSize(int select, double value)
+{
+	int i;
+
+	if((select & 0xfff) >= SIZE_XPOS && (select & 0xfff) <=  SIZE_XPOS_LAST){
+		if((i = select-SIZE_XPOS) >=0 && i <= 200 && i < (int)ndt){
+			dt[i].fx = value;			return true;
+			}
+		}
+	else if((select & 0xfff) >= SIZE_YPOS && (select & 0xfff) <= SIZE_YPOS_LAST){
+		if((i = select-SIZE_YPOS) >=0 && i <= 200 && i < (int)ndt){
+			dt[i].fy = value;			return true;
+			}
+		}
+	else if((select & 0xfff) >= SIZE_ZPOS && (select & 0xfff) <= SIZE_ZPOS_LAST){
+		if((i = select-SIZE_ZPOS) >=0 && i <= 200 && i < (int)ndt){
+			dt[i].fz = value;			return true;
+			}
+		}
+	else switch(select) {
+	case SIZE_SYM_LINE:
+		Line.width = value;
+		}
+	return false;
+}
+
+bool
+Plane3D::SetColor(int select, DWORD col)
+{
+	switch(select) {
+	case COL_POLYLINE:		Line.color = col;		return true;
+	case COL_POLYGON:		Fill.color = col;		return true;
+		}
+	return false;
+}
+
+void
+Plane3D::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(ipl) delete ipl;		ipl = 0L;
+	if(pts) free(pts);		pts = 0L;
+	if(!(o->ActualSize(&rDims)))return;
+	rDims.left = rDims.right;	rDims.top = rDims.bottom;
+	rDims.right = rDims.bottom = 0;
+	if((pts = (fPOINT3D*)malloc(sizeof(fPOINT3D)*ndt))){
+		for(i = 0; i < ndt; i++) {
+			if(!o->fvec2ivec(&dt[i], &pts[i])){
+				free(pts);	pts = 0L; return;
+				}
+			UpdateMinMaxRect(&rDims, iround(pts[i].fx), iround(pts[i].fy));
+			}
+		if(ipl = new plane(this, data, pts, i, &Line, &Fill))ipl->DoPlot(o);
+		}
+	IncrementMinMaxRect(&rDims, o->un2ix(Line.width)+1);
+}
+
+void
+Plane3D::DoMark(anyOutput *o, bool mark)
+{
+	if(mark){
+		if(pts && ipl)ipl->DoMark(o, mark);
+		o->UpdateRect(&rDims, false);
+		}
+	else if(parent) parent->Command(CMD_REDRAW, 0L, o);
+}
+
+bool
+Plane3D::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	MouseEvent *mev;
+
+	switch (cmd) {
+	case CMD_SET_DATAOBJ:
+		Id = GO_PLANE3D;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_SCALE:
+		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
+		Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;
+		return true;
+	case CMD_LEGEND:
+		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH) {
+			((Legend*)tmpl)->HasFill(&Line, &Fill, ((Plot*)parent)->data_desc);
+			}
+		else ((Legend*)tmpl)->HasFill(&Line, &Fill, 0L);
+		break;
+	case CMD_MRK_DIRTY:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_SYM_FILL:
+		if(tmpl) memcpy(&Fill, tmpl, sizeof(FillDEF));
+		return true;
+	case CMD_REDRAW:
+		//Note: this command is issued either by Undo (no output given) or
+		//  by Plot3D::DoPlot after sorting all objects (output specified)
+		if(!parent) return false;
+		if(!o) return parent->Command(cmd, tmpl, o);
+		return true;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
+				if(ipl && ipl->ObjThere(mev->x, mev->y)){
+					o->ShowMark(CurrGO=this, MRK_GODRAW);
+					return true;
+					}
+				}
+			break;
+			}
+		break;
+	case CMD_PG_FILL:
+		if(tmpl) {
+			memcpy((void*)&Fill, tmpl, sizeof(FillDEF));
+			Fill.hatch = 0L;
+			}
+		break;
+	case CMD_AUTOSCALE:
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH && dt) {
+			for(i = 0; i < ndt; i++)
+				((Plot*)parent)->CheckBounds3D(dt[i].fx, dt[i].fy, dt[i].fz);
+			return true;
+			}
+		break;
+	case CMD_SET_GO3D:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Brick: a bar in three dimensional space
+Brick::Brick(GraphObj *par, DataObj *da, double x, double y, double z, 
+		double d, double w, double h, DWORD flg, int xc, int xr, int yc,
+		int yr, int zc, int zr, int dc, int dr, int wc, int wr, int hc,
+		int hr):GraphObj(par, da)
+{
+	FileIO(INIT_VARS);
+	Id = GO_BRICK;
+	fPos.fx = x;	fPos.fy = y;	fPos.fz = z;
+	depth = d;		width = w;		height = h;
+	flags = flg;
+	if(xc >= 0 || xr >= 0 || yc >= 0 || yr >= 0 || zc >= 0 || zr >= 0 ||
+		dc >= 0 || dr >= 0 || wc >= 0 || wr >= 0 || hc >= 0 || hr >= 0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*6)) {
+			ssRef[0].x = xc;	ssRef[0].y = xr;
+			ssRef[1].x = yc;	ssRef[1].y = yr;
+			ssRef[2].x = zc;	ssRef[2].y = zr;
+			ssRef[3].x = dc;	ssRef[3].y = dr;
+			ssRef[4].x = wc;	ssRef[4].y = wr;
+			ssRef[5].x = hc;	ssRef[5].y = hr;
+			cssRef = 6;
+			}
+		}
+	bModified = false;
+}
+
+Brick::Brick(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	bModified = false;
+}
+	
+Brick::~Brick()
+{
+	int i;
+
+	if(faces) {
+		for(i = 0; i < 6; i++) if(faces[i]) delete(faces[i]);
+		free(faces);
+		}
+	faces = 0L;
+	if(mo) DelBitmapClass(mo);		mo = 0L;
+	Command(CMD_FLUSH, 0L, 0L);
+	if(bModified) Undo.InvalidGO(this);
+}
+
+bool
+Brick::SetSize(int select, double value)
+{
+	switch(select & 0xfff) {
+	case SIZE_BAR_LINE:		Line.width = value;		return true;
+	case SIZE_BAR_BASE:		fPos.fy = value;		return true;
+	case SIZE_BAR:			width = value;			return true;
+	case SIZE_BAR_DEPTH:	depth = value;			return true;
+		}
+	return false;
+}
+
+bool
+Brick::SetColor(int select, DWORD col)
+{
+	switch(select & 0xfff) {
+	case COL_BAR_LINE:		Line.color = col;		return true;
+	case COL_BAR_FILL:		Fill.color = col;		return true;
+		}
+	return false;
+}
+
+void
+Brick::DoPlot(anyOutput *o)
+{
+	fPOINT3D cpt[8], fip1, fip2, tmp, itmp, *pg;
+	plane *npl;
+	double dtmp;
+	int i;
+
+	if(mo) DelBitmapClass(mo);		mo = 0L;
+	if(!faces || !o || !o->fvec2ivec(&fPos, &fip1)) return;
+	if(!(pg = (fPOINT3D *)malloc(5*sizeof(fPOINT3D)))) return;
+	for(i = 0; i < 6; i++) {
+		if(faces[i]) delete(faces[i]);
+		faces[i] = 0L;
+		}
+	if(flags & 0x800L) {		//height is data
+		tmp.fx = fPos.fx;			tmp.fy = height;
+		tmp.fz = fPos.fz;			o->fvec2ivec(&tmp, &fip2);
+		}
+	else {						//height is units
+		tmp.fx = tmp.fz = 0.0;		tmp.fy = height;
+		o->uvec2ivec(&tmp, &fip2);
+		fip2.fx += fip1.fx;			fip2.fy += fip1.fy;
+		fip2.fz += fip1.fz;
+		}
+	//calc output-device coordinates of cubic brick: 8 corners
+	tmp.fx = -(width/2.0);			tmp.fz = (depth/2.0);	tmp.fy = 0.0;
+	o->uvec2ivec(&tmp, &itmp);
+	cpt[0].fx= fip1.fx+itmp.fx;	cpt[0].fy= fip1.fy+itmp.fy;	cpt[0].fz= fip1.fz+itmp.fz;
+	cpt[2].fx= fip1.fx-itmp.fx;	cpt[2].fy= fip1.fy-itmp.fy;	cpt[2].fz= fip1.fz-itmp.fz;
+	cpt[4].fx= fip2.fx+itmp.fx;	cpt[4].fy= fip2.fy+itmp.fy;	cpt[4].fz= fip2.fz+itmp.fz;
+	cpt[6].fx= fip2.fx-itmp.fx;	cpt[6].fy= fip2.fy-itmp.fy;	cpt[6].fz= fip2.fz-itmp.fz;
+	tmp.fx = (width/2.0);			tmp.fz = (depth/2.0);	tmp.fy = 0.0;
+	o->uvec2ivec(&tmp, &itmp);
+	cpt[1].fx= fip1.fx+itmp.fx;	cpt[1].fy= fip1.fy+itmp.fy;	cpt[1].fz= fip1.fz+itmp.fz;
+	cpt[3].fx= fip1.fx-itmp.fx;	cpt[3].fy= fip1.fy-itmp.fy;	cpt[3].fz= fip1.fz-itmp.fz;
+	cpt[5].fx= fip2.fx+itmp.fx;	cpt[5].fy= fip2.fy+itmp.fy;	cpt[5].fz= fip2.fz+itmp.fz;
+	cpt[7].fx= fip2.fx-itmp.fx;	cpt[7].fy= fip2.fy-itmp.fy;	cpt[7].fz= fip2.fz-itmp.fz;
+	//set up 6 faces
+	pg[0].fx = pg[4].fx = cpt[0].fx;	pg[1].fx = cpt[1].fx;	
+	pg[2].fx = cpt[2].fx;				pg[3].fx = cpt[3].fx;
+	pg[0].fy = pg[4].fy = cpt[0].fy;	pg[1].fy = cpt[1].fy;	
+	pg[2].fy = cpt[2].fy;				pg[3].fy = cpt[3].fy;
+	pg[0].fz = pg[4].fz = cpt[0].fz;	pg[1].fz = cpt[1].fz;	
+	pg[2].fz = cpt[2].fz;				pg[3].fz = cpt[3].fz;
+	faces[0] = new plane(this, data, pg, 5, &Line, &Fill);
+	pg[2].fx = cpt[5].fx;				pg[3].fx = cpt[4].fx;
+	pg[2].fy = cpt[5].fy;				pg[3].fy = cpt[4].fy;
+	pg[2].fz = cpt[5].fz;				pg[3].fz = cpt[4].fz;
+	npl = new plane(this, data, pg, 5, &Line, &Fill);
+	if(npl->GetSize(SIZE_MAX_Z) > faces[0]->GetSize(SIZE_MAX_Z)) faces[1] = npl;
+	else {
+		faces[1] = faces[0];		faces[0] = npl;		
+		}
+	pg[0].fx = pg[4].fx = cpt[2].fx;	pg[1].fx = cpt[6].fx;	
+	pg[2].fx = cpt[5].fx;				pg[3].fx = cpt[1].fx;
+	pg[0].fy = pg[4].fy = cpt[2].fy;	pg[1].fy = cpt[6].fy;	
+	pg[2].fy = cpt[5].fy;				pg[3].fy = cpt[1].fy;
+	pg[0].fz = pg[4].fz = cpt[2].fz;	pg[1].fz = cpt[6].fz;	
+	pg[2].fz = cpt[5].fz;				pg[3].fz = cpt[1].fz;
+	npl = new plane(this, data, pg, 5, &Line, &Fill);
+	if((dtmp = npl->GetSize(SIZE_MAX_Z)) > faces[1]->GetSize(SIZE_MAX_Z)) faces[2] = npl;
+	else {
+		faces[2] = faces[1];
+		if(dtmp > faces[0]->GetSize(SIZE_MAX_Z)) faces[1] = npl;
+		else {
+			faces[1] = faces[0];	faces[0] = npl;
+			}
+		}
+	pg[2].fx = cpt[7].fx;				pg[3].fx = cpt[3].fx;
+	pg[2].fy = cpt[7].fy;				pg[3].fy = cpt[3].fy;
+	pg[2].fz = cpt[7].fz;				pg[3].fz = cpt[3].fz;
+	npl = new plane(this, data, pg, 5, &Line, &Fill);
+	dtmp = npl->GetSize(SIZE_MAX_Z);
+	for (i = 3; i; i--) {
+		if(dtmp >faces[i-1]->GetSize(SIZE_MAX_Z)) {
+			faces[i] = npl;			break;
+			}
+		else faces[i] = faces[i-1];
+		}
+	if(!i) faces[0] = npl;
+	pg[0].fx = pg[4].fx = cpt[4].fx;	pg[1].fx = cpt[7].fx;	
+	pg[2].fx = cpt[3].fx;				pg[3].fx = cpt[0].fx;
+	pg[0].fy = pg[4].fy = cpt[4].fy;	pg[1].fy = cpt[7].fy;	
+	pg[2].fy = cpt[3].fy;				pg[3].fy = cpt[0].fy;
+	pg[0].fz = pg[4].fz = cpt[4].fz;	pg[1].fz = cpt[7].fz;	
+	pg[2].fz = cpt[3].fz;				pg[3].fz = cpt[0].fz;
+	npl = new plane(this, data, pg, 5, &Line, &Fill);
+	dtmp = npl->GetSize(SIZE_MAX_Z);
+	for (i = 4; i; i--) {
+		if(dtmp >faces[i-1]->GetSize(SIZE_MAX_Z)) {
+			faces[i] = npl;			break;
+			}
+		else faces[i] = faces[i-1];
+		}
+	if(!i) faces[0] = npl;
+	pg[2].fx = cpt[6].fx;				pg[3].fx = cpt[5].fx;
+	pg[2].fy = cpt[6].fy;				pg[3].fy = cpt[5].fy;
+	pg[2].fz = cpt[6].fz;				pg[3].fz = cpt[5].fz;
+	npl = new plane(this, data, pg, 5, &Line, &Fill);
+	dtmp = npl->GetSize(SIZE_MAX_Z);
+	for (i = 5; i; i--) {
+		if(dtmp >faces[i-1]->GetSize(SIZE_MAX_Z)) {
+			faces[i] = npl;			break;
+			}
+		else faces[i] = faces[i-1];
+		}
+	if(!i) faces[0] = npl;
+	rDims.left = rDims.right = (int)pg[0].fx;
+	rDims.top = rDims.bottom = (int)pg[0].fy;
+	for (i= 3; i < 6; i++) if(faces[i]) {
+		faces[i]->DoPlot(o);
+		UpdateMinMaxRect(&rDims, faces[i]->rDims.left, faces[i]->rDims.top);
+		UpdateMinMaxRect(&rDims, faces[i]->rDims.right, faces[i]->rDims.bottom);
+		}
+	free(pg);
+}
+
+void
+Brick::DoMark(anyOutput *o, bool mark)
+{
+	int i;
+
+	if(mark){
+		if(mo) DelBitmapClass(mo);					mo = 0L;
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		IncrementMinMaxRect(&mrc, 6 + o->un2ix(Line.width));
+		mo = GetRectBitmap(&mrc, o);
+		if(faces) for(i = 3; i < 6; i++)
+			if(faces[i]) faces[i]->DoMark(o, mark);
+		o->UpdateRect(&rDims, false);
+		}
+	else if(mo)	RestoreRectBitmap(&mo, &mrc, o);
+}
+
+bool
+Brick::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+
+	switch (cmd) {
+	case CMD_FLUSH:
+		if(ssRef) free(ssRef);		ssRef = 0L;
+		if(name) free(name);		name = 0L;
+		return true;
+	case CMD_SCALE:
+		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
+		Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;			depth *= ((scaleINFO*)tmpl)->sz.fy;
+		width *= ((scaleINFO*)tmpl)->sx.fy;		if(!(flags & 0x800L)) height *=  ((scaleINFO*)tmpl)->sx.fy;
+		return true;
+	case CMD_LEGEND:
+		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH) {
+			((Legend*)tmpl)->HasFill(&Line, &Fill, ((Plot*)parent)->data_desc);
+			}
+		else ((Legend*)tmpl)->HasFill(&Line, &Fill, 0L);
+		break;
+	case CMD_BAR_FILL:
+		if(tmpl) {
+			memcpy(&Fill, tmpl, sizeof(FillDEF));
+			Fill.hatch = 0L;
+			return true;
+			}
+		break;
+	case CMD_MRK_DIRTY:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_SET_DATAOBJ:
+		Id = GO_BRICK;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_REDRAW:
+		//Note: this command is issued either by Undo (no output given) or
+		//  by Plot3D::DoPlot after sorting all objects (output specified)
+		if(!parent) return false;
+		if(!o) return parent->Command(cmd, tmpl, o);
+		//Should we ever come here ?
+		return true;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
+				if(faces && faces[3] && faces[4] && faces[5] &&
+					(faces[3]->ObjThere(mev->x, mev->y) || 
+					faces[4]->ObjThere(mev->x, mev->y) ||
+					faces[5]->ObjThere(mev->x, mev->y))){
+						o->ShowMark(CurrGO=this, MRK_GODRAW);
+						return true;
+						}
+				}
+			break;
+			}
+		break;
+	case CMD_UPDATE:
+		if(ssRef && cssRef > 5 && data) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
+			data->GetValue(ssRef[2].y, ssRef[2].x, &fPos.fz);
+			data->GetValue(ssRef[3].y, ssRef[3].x, &depth);
+			data->GetValue(ssRef[4].y, ssRef[4].x, &width);
+			data->GetValue(ssRef[5].y, ssRef[5].x, &height);
+			return true;
+			}
+		return false;
+	case CMD_AUTOSCALE:
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			((Plot*)parent)->CheckBounds3D(fPos.fx, fPos.fy, fPos.fz);
+			if(flags & 0x800L) {		//height is data
+				((Plot*)parent)->CheckBounds3D(fPos.fx, height, fPos.fz);
+				}
+			return true;
+			}
+		break;
+	case CMD_SET_GO3D:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// line_segment: utility object to draw a piece of a polyline in 3D space
+line_segment::line_segment(GraphObj *par, DataObj *d, LineDEF *ld, POINT3D *p1, POINT3D *p2)
+	:GraphObj(par, d)
+{
+	double tmp, tmp1, tmp2;
+
+	nli = 0;    nldata = 0L;	  ldata = 0L;	prop = 1.0;		df_go = 0L;
+	fmin.fx = fmax.fx = fmin.fy = fmax.fy = fmin.fz = fmax.fz = 0.0;
+	ndf_go = 0;
+	if(ld) memcpy(&Line, ld, sizeof(LineDEF));
+	if(p1 && p2 &&(ldata =(POINT3D**)calloc(1, sizeof(POINT3D*))) &&
+		(ldata[0] = (POINT3D*)malloc(2 * sizeof(POINT3D))) &&
+		(nldata = (int*)calloc(1, sizeof(int)))){
+			if(Line.pattern) {
+				tmp1 = (tmp = (p2->x - p1->x)) * tmp;
+				tmp2 = (tmp1 += (tmp = (p2->y - p1->y)) * tmp);
+				tmp1 += (tmp = (p2->z - p1->z)) * tmp;
+				if(tmp1 > 1.0) prop = sqrt(tmp2)/sqrt(tmp1);
+				}
+			memcpy(&ldata[0][0], p1, sizeof(POINT3D));
+			memcpy(&ldata[0][1], p2, sizeof(POINT3D));
+			nldata[0] = 2;		nli = 1;
+			rDims.left = rDims.right = p1->x;	rDims.top = rDims.bottom = p1->y;
+			UpdateMinMaxRect(&rDims, p2->x, p2->y);
+			fmin.fx = (double)rDims.left;	fmin.fy = (double)rDims.top;
+			fmax.fx = (double)rDims.right;	fmax.fy = (double)rDims.bottom;
+			if(p2->z > p1->z) {
+				fmin.fz = (double)p1->z;	fmax.fz = (double)p2->z;
+				}
+			else {
+				fmin.fz = (double)p2->z;	fmax.fz = (double)p1->z;
+				}
+		}
+	Id = GO_LINESEG;
+}
+
+line_segment::~line_segment()
+{
+	int i;
+
+	if(ldata) {
+		for(i = 0; i < nli; i++) if(ldata[i]) free(ldata[i]);
+		free(ldata);
+		}
+	if(nldata) free(nldata);		nldata = 0L;
+}
+
+double
+line_segment::GetSize(int select)
+{
+	switch(select) {
+	case SIZE_MIN_Z:
+		return fmin.fz;
+	case SIZE_MAX_Z:
+		return fmax.fz;
+		}
+	return 0.0;
+}
+
+void
+line_segment::DoPlot(anyOutput *o)
+{
+	bDrawDone = false;		co = 0L;
+	if(df_go) free(df_go);
+	df_go = 0L;				ndf_go = 0;
+	if(o->VPscale > 1.5 && (
+		(rDims.right < defs.clipRC.left) || (rDims.left > defs.clipRC.right) ||
+		(rDims.bottom < defs.clipRC.top) || (rDims.top > defs.clipRC.bottom))){
+		bDrawDone = true;		return;
+		}
+	if(parent && parent->Command(CMD_SET_GO3D, this, o)) return;
+	Command(CMD_REDRAW, 0L, o);
+}
+
+bool
+line_segment::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i, j;
+	POINT pts[2];
+	LineDEF cLine;
+	POINT3D *ap;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_DRAW_LATER:
+		if(!co) return false;
+		if(df_go){
+			for(i = 0; i < ndf_go; i++) if(df_go[i] == co) return true;
+			if(df_go = (GraphObj**)realloc(df_go, (ndf_go +1) * sizeof(GraphObj*))) {
+				df_go[ndf_go++] = co;
+				}
+			}
+		else {
+			if(df_go = (GraphObj**)malloc(sizeof(GraphObj*))){
+				df_go[0] = co;	ndf_go = 1;
+				}
+			}
+		return true;
+	case CMD_REDRAW:
+		if(bDrawDone) return false;
+		bDrawDone = true;
+		if(!nli) return false;
+		if(df_go) {
+			for(i = 0; i < ndf_go; i++) if(df_go[i]) df_go[i]->Command(cmd, tmpl, o);
+			free(df_go);	df_go = 0L;			ndf_go = 0L;
+			}
+		if(o && ldata && nldata){
+			memcpy(&cLine, &Line, sizeof(LineDEF));
+			cLine.patlength *= prop;
+			o->SetLine(&cLine);
+			for(i = 0; i < nli; i++) for(j = 0; j < (nldata[i]-1); j++) {
+				pts[0].x = ldata[i][j].x;	pts[0].y = ldata[i][j].y;
+				pts[1].x = ldata[i][j+1].x;	pts[1].y = ldata[i][j+1].y;
+				if(pts[0].x != pts[1].x || pts[0].y != pts[1].y) o->oPolyline(pts, 2);
+				}
+			}
+		return true;
+	case CMD_CLIP:
+		if(co = (GraphObj*)tmpl){
+			switch(co->Id) {
+			case GO_PLANE:
+			case GO_SPHERE:
+				DoClip(co);
+				break;
+				}
+			}
+		return false;
+	case CMD_STARTLINE:
+		if(tmpl) {
+			if(ldata && nldata) {
+				ldata = (POINT3D**)realloc(ldata, sizeof(POINT3D*) * (nli+1));
+				ldata[nli] = (POINT3D*)malloc(2 * sizeof(POINT3D));
+				nldata = (int*)realloc(nldata, sizeof(int)*(nli+1));
+				}
+			else {
+				ldata = (POINT3D**)calloc(1, sizeof(POINT3D*));
+				ldata[nli = 0] = (POINT3D*)malloc(2 * sizeof(POINT3D));
+				nldata = (int*)calloc(1, sizeof(int));
+				}
+			if(ldata && nldata) {
+				memcpy(&ldata[nli][0], tmpl, sizeof(POINT3D));
+				memcpy(&ldata[nli][1], tmpl, sizeof(POINT3D));
+				nldata[nli++] = 1;
+				return true;
+				}
+			}
+		break;
+	case CMD_ADDTOLINE:
+		if((ap = (POINT3D*)tmpl) && ldata && nldata && nli && nldata[i =(nli-1)]) {
+			j = nldata[i];
+			ldata[i] = (POINT3D*)realloc(ldata[i], (nldata[i]+2) * sizeof(POINT3D));
+			if(j && ldata[i][j-1].x == ap->x && ldata[i][j-1].y == ap->y &&
+				ldata[i][j-1].z == ap->z) return true;
+			else {
+				j = (nldata[i]++);
+				}
+			memcpy(&ldata[i][j-1], tmpl, sizeof(POINT3D));
+			return true;
+			}
+		break;
+		}
+	return false;
+}
+
+void *
+line_segment::ObjThere(int x, int y)
+{
+	int i, j;
+	POINT pts[2];
+
+	if(ldata && nldata){
+		for(i = 0; i < nli; i++) for(j = 0; j < nldata[i]; j +=2) {
+			pts[0].x = ldata[i][j].x;	pts[0].y = ldata[i][j].y;
+			pts[1].x = ldata[i][j+1].x;	pts[1].y = ldata[i][j+1].y;
+			if(IsCloseToLine(&pts[0], &pts[1], x, y))return this;
+			}
+		}
+	return 0L;
+}
+
+void
+line_segment::DoClip(GraphObj *co)
+{
+	RECT cliprc;
+	int o_nli, *o_nldata = 0L;
+	POINT3D **o_ldata = 0L, *pts = 0L, *pla;
+	int i, j, k, np, r, cx, cy, cz;
+	bool is_valid = false;
+
+	cliprc.left = iround(co->GetSize(SIZE_MIN_X));
+	cliprc.right = iround(co->GetSize(SIZE_MAX_X));
+	cliprc.top = iround(co->GetSize(SIZE_MIN_Y));
+	cliprc.bottom = iround(co->GetSize(SIZE_MAX_Y));
+	if(OverlapRect(&rDims, &cliprc)) {
+		if(!(pts = (POINT3D*)calloc(2, sizeof(POINT3D))))return;
+		o_nli = nli;		nli = 0;
+		o_nldata = nldata;	nldata = 0L;
+		o_ldata = ldata;	ldata = 0L;
+		switch(co->Id) {
+		case GO_SPHERE:
+			cx = iround(co->GetSize(SIZE_XPOS));
+			cy = iround(co->GetSize(SIZE_YPOS));
+			cz = iround(co->GetSize(SIZE_ZPOS));
+			r = iround(co->GetSize(SIZE_RADIUS1));
+			for(i = 0; i < o_nli; i++) for(j = 0; j < (o_nldata[i]-1); j ++) {
+				pts[0].x = o_ldata[i][j].x;			pts[0].y = o_ldata[i][j].y;
+				pts[0].z = o_ldata[i][j].z;			pts[1].x = o_ldata[i][j+1].x;
+				pts[1].y = o_ldata[i][j+1].y;		pts[1].z = o_ldata[i][j+1].z;
+				clip_line_sphere(this, &pts, r, cx, cy, cz);
+				}
+			break;
+		case GO_PLANE:
+			for(i = 0; ((plane*)co)->GetPolygon(&pla, &np, i); i++){
+				for(j = 0; j < o_nli; j++) {
+					if(o_nldata[j] >1) for(k = 0; k < (o_nldata[j]-1); k++){
+						pts[0].x = o_ldata[j][k].x;			pts[0].y = o_ldata[j][k].y;
+						pts[0].z = o_ldata[j][k].z;			pts[1].x = o_ldata[j][k+1].x;
+						pts[1].y = o_ldata[j][k+1].y;		pts[1].z = o_ldata[j][k+1].z;
+						if(pts[0].x != pts[1].x || pts[0].y != pts[1].y || pts[0].z != pts[1].z)
+							clip_line_plane(this, &pts, pla, np, ((plane*)co)->GetVec());
+						}
+					}
+				if(nli) is_valid = true;
+				if(o_ldata) {
+					for(j = 0; j < o_nli; j++) if(o_ldata[j]) free(o_ldata[j]);
+					free(o_ldata);
+					}
+				if(o_nldata) free(o_nldata);
+				o_nli = nli;		nli = 0;
+				o_nldata = nldata;	nldata = 0L;
+				o_ldata = ldata;	ldata = 0L;
+				if(!o_nli) return;				//line is completly hidden
+				}
+			if(is_valid || i==0){
+				nli = o_nli;		o_nli = 0;
+				nldata = o_nldata;	o_nldata = 0L;
+				ldata = o_ldata;	o_ldata = 0L;
+				}
+			break;
+		default:
+			nli = o_nli;		o_nli = 0;
+			nldata = o_nldata;	o_nldata = 0L;
+			ldata = o_ldata;	o_ldata = 0L;
+			break;
+			}
+		if(pts) free(pts);
+		}
+	if(o_ldata) {
+		for(i = 0; i < o_nli; i++) if(o_ldata[i]) free(o_ldata[i]);
+		free(o_ldata);
+		}
+	if(o_nldata) free(o_nldata);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// define a drop line in 3D space
+DropLine3D::DropLine3D(GraphObj *par, DataObj *d, fPOINT3D *p1, int xc,
+		int xr, int yc, int yr, int zc, int zr):GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	Id = GO_DROPL3D;
+	memcpy(&fPos, p1, sizeof(fPOINT3D));
+	if(xc >= 0 || xr >= 0 || yc >= 0 || yr >= 0 || zc >= 0 || zr >= 0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*3)) {
+			ssRef[0].x = xc;	ssRef[0].y = xr;
+			ssRef[1].x = yc;	ssRef[1].y = yr;
+			ssRef[2].x = zc;	ssRef[2].y = zr;
+			cssRef = 3;
+			}
+		}
+	bModified = false;
+	type = 0x01;
+}
+
+DropLine3D::DropLine3D(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	bModified = false;
+}
+
+DropLine3D::~DropLine3D()
+{
+	if(bModified) Undo.InvalidGO(this);
+	if(mo) DelBitmapClass(mo);		mo = 0L;
+	Command(CMD_FLUSH, 0L, 0L);
+}
+
+void
+DropLine3D::DoPlot(anyOutput *o)
+{
+	fPOINT3D fip, fp, fp1;
+	POINT3D p1, p2;
+	int i;
+
+	if(!parent || !o || !o->fvec2ivec(&fPos, &fip)) return;
+	if(mo) DelBitmapClass(mo);		mo = 0L;
+	for(i = 0; i < 6; i++){
+		if(ls[i]) delete(ls[i]);
+		ls[i] = 0L;
+		}
+	p1.x = iround(fip.fx);	p1.y = iround(fip.fy);	p1.z = iround(fip.fz);
+	rDims.left = rDims.right = p1.x;	rDims.top = rDims.bottom = p1.y;
+	for(i = 0; i < 6; i++) {
+		fp.fx = fPos.fx;	fp.fy = fPos.fy;	fp.fz = fPos.fz;
+		if(type & (1 << i)){
+			switch (i) {
+			case 0:	fp.fy = parent->GetSize(SIZE_BOUNDS_YMIN);	break;
+			case 1:	fp.fy = parent->GetSize(SIZE_BOUNDS_YMAX);	break;
+			case 2:	fp.fz = parent->GetSize(SIZE_BOUNDS_ZMIN);	break;
+			case 3:	fp.fz = parent->GetSize(SIZE_BOUNDS_ZMAX);	break;
+			case 4:	fp.fx = parent->GetSize(SIZE_BOUNDS_XMIN);	break;
+			case 5:	fp.fx = parent->GetSize(SIZE_BOUNDS_XMAX);	break;
+				}
+			o->fvec2ivec(&fp, &fp1);		p2.x = iround(fp1.fx);
+			p2.y = iround(fp1.fy);			p2.z = iround(fp1.fz);
+			UpdateMinMaxRect(&rDims, p2.x, p2.y);
+			if(ls[i] = new line_segment(this, data, &Line, &p1, &p2)) ls[i]->DoPlot(o);
+			mpts[i][0].x = p1.x;	mpts[i][0].y = p1.y;
+			mpts[i][1].x = p2.x;	mpts[i][1].y = p2.y;
+			}
+		}
+	IncrementMinMaxRect(&rDims, 5);
+}
+
+void
+DropLine3D::DoMark(anyOutput *o, bool mark)
+{
+	int i;
+
+	if(mark) {
+		if(mo) DelBitmapClass(mo);					mo = 0L;
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		IncrementMinMaxRect(&mrc, 6 + o->un2ix(Line.width));
+		mo = GetRectBitmap(&mrc, o);
+		for(i = 0; i < 6; i++) {
+			if(type & (1 << i)){
+				InvertLine(mpts[i], 2, &Line, 0L, o, mark);
+				}
+			}
+		o->UpdateRect(&mrc, false);
+		}
+	else RestoreRectBitmap(&mo, &mrc, o);
+}
+
+bool
+DropLine3D::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	int i;
+
+	switch (cmd) {
+	case CMD_FLUSH:
+		for(i = 0; i < 6; i++){
+			if(ls[i]) delete(ls[i]);
+			ls[i] = 0L;
+			}
+		if(ssRef) free(ssRef);		ssRef = 0L;
+		if(name) free(name);		name = 0L;
+		return true;
+	case CMD_SCALE:
+		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
+		return true;
+	case CMD_DL_TYPE:
+		if(tmpl && *((int*)tmpl)) type = *((int*)tmpl);
+		return true;
+	case CMD_DL_LINE:
+		if(tmpl) memcpy(&Line, tmpl, sizeof(LineDEF));
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_DROPL3D;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_REDRAW:
+		//Note: this command is issued either by Undo (no output given) or
+		//  by Plot3D::DoPlot after sorting all objects (output specified)
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
+				for(i = 0; i < 6; i++) {
+					if(ls[i] && ls[i]->ObjThere(mev->x, mev->y)){
+						o->ShowMark(this, MRK_GODRAW);
+						return true;
+						}
+					}
+				}
+			break;
+			}
+		break;
+	case CMD_UPDATE:
+		if(ssRef && cssRef >2 && data) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
+			data->GetValue(ssRef[2].y, ssRef[2].x, &fPos.fz);
+			return true;
+			}
+		return false;
+	case CMD_AUTOSCALE:
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			((Plot*)parent)->CheckBounds3D(fPos.fx, fPos.fy, fPos.fz);
+			return true;
+			}
+		break;
+	case CMD_SET_GO3D:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// define an arrow in 3D space
+Arrow3D::Arrow3D(GraphObj *par, DataObj *d, fPOINT3D *p1, fPOINT3D *p2, int xc1,
+		int xr1, int yc1, int yr1, int zc1, int zr1, int xc2, int xr2, int yc2, 
+		int yr2, int zc2, int zr2):GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	Id = GO_ARROW3D;
+	memcpy(&fPos1, p1, sizeof(fPOINT3D));	memcpy(&fPos2, p2, sizeof(fPOINT3D));
+	if(xc1 >= 0 || xr1 >= 0 || yc1 >= 0 || yr1 >= 0 || zc1 >= 0 || zr1 >= 0 || 
+		xc2 >= 0 || xr2 >= 0 || yc2 >= 0 || yr2 >= 0 || zc2 >= 0 || zr2 >= 0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*6)) {
+			ssRef[0].x = xc1;	ssRef[0].y = xr1;
+			ssRef[1].x = yc1;	ssRef[1].y = yr1;
+			ssRef[2].x = zc1;	ssRef[2].y = zr1;
+			ssRef[3].x = xc2;	ssRef[3].y = xr2;
+			ssRef[4].x = yc2;	ssRef[4].y = yr2;
+			ssRef[5].x = zc2;	ssRef[5].y = zr2;
+			cssRef = 6;
+			}
+		}
+	bModified = false;
+}
+
+Arrow3D::Arrow3D(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	bModified = false;
+}
+
+Arrow3D::~Arrow3D()
+{
+	if(bModified) Undo.InvalidGO(this);
+	Command(CMD_FLUSH, 0L, 0L);
+}
+
+bool
+Arrow3D::SetSize(int select, double value)
+{
+	switch(select & 0xfff) {
+	case SIZE_ARROW_LINE:
+		Line.width = value;
+		return true;
+	case SIZE_ARROW_CAPWIDTH:
+		cw = value;
+		return true;
+	case SIZE_ARROW_CAPLENGTH:
+		cl = value;
+		return true;
+		}
+	return false;
+}
+
+bool
+Arrow3D::SetColor(int select, DWORD col)
+{
+	switch(select & 0xfff) {
+	case COL_ARROW:
+		Line.color = col;
+		return true;
+		}
+	return false;
+}
+
+void
+Arrow3D::DoPlot(anyOutput *o)
+{
+	double si, csi, tmp, cwr, clr, d, d1, d2;
+	fPOINT3D fip1, fip2, tria[3];
+	POINT3D p1, p2, cp1, cp2;
+	FillDEF fill;
+	int i;
+
+	if(!parent || !o || !o->fvec2ivec(&fPos1, &fip1) ||!o->fvec2ivec(&fPos2, &fip2)) return;
+	for(i = 0; i < 3; i++){
+		if(ls[i]) delete(ls[i]);
+		ls[i] = 0L;
+		}
+	p1.x = iround(fip1.fx);	p1.y = iround(fip1.fy);	p1.z = iround(fip1.fz);
+	p2.x = iround(fip2.fx);	p2.y = iround(fip2.fy);	p2.z = iround(fip2.fz);
+	if(p1.x == p2.x && p1.y == p2.y && p1.z == p2.z) return;	//zero length arrow
+	rDims.left = rDims.right = p1.x;	rDims.top = rDims.bottom = p1.y;
+	UpdateMinMaxRect(&rDims, p2.x, p2.y);
+	IncrementMinMaxRect(&rDims, 5);
+	if(ls[0] = new line_segment(this, data, &Line, &p1, &p2)) ls[0]->DoPlot(o);
+	mpts[0][0].x = p1.x;	mpts[0][0].y = p1.y;	mpts[0][1].x = p2.x;	mpts[0][1].y = p2.y;
+	if(p1.x == p2.x && p1.y == p2.y) return;			//zero length in 2D
+	if((type & 0xff) == ARROW_NOCAP) return;			//no cap;
+	//calculate sine and cosine for cap
+	si = fip1.fy-fip2.fy;
+	tmp = fip2.fx - fip1.fx;
+	si = si/sqrt(si*si + tmp*tmp);
+	csi = fip2.fx-fip1.fx;
+	tmp = fip2.fy - fip1.fy;
+	csi = csi/sqrt(csi*csi + tmp*tmp);
+	//cap corners
+	d1 = (d = fip2.fx - fip1.fx) * d;
+	d1 += ((d = fip2.fy - fip1.fy) * d);
+	d2 = d1 + ((d = fip2.fz - fip1.fz) * d);
+	d1 = sqrt(d1);	d2 = sqrt(d2);	d = d1/d2;
+	cwr = cw;	clr = cl*d;
+	cp1.x = p2.x - o->un2ix(csi*clr + si*cwr/2.0);
+	cp1.y = p2.y + o->un2iy(si*clr - csi*cwr/2.0);
+	cp2.x = p2.x - o->un2ix(csi*clr - si*cwr/2.0);
+	cp2.y = p2.y + o->un2iy(si*clr + csi*cwr/2.0);
+	cp1.z = cp2.z = p2.z;
+	mpts[1][0].x = p2.x;	mpts[1][0].y = p2.y;	mpts[1][1].x = cp1.x;	mpts[1][1].y = cp1.y;
+	mpts[2][0].x = p2.x;	mpts[2][0].y = p2.y;	mpts[2][1].x = cp2.x;	mpts[2][1].y = cp2.y;
+	if((type & 0xff) == ARROW_LINE) {
+		if(ls[1] = new line_segment(this, data, &Line, &p2, &cp1)) ls[1]->DoPlot(o);
+		if(ls[2] = new line_segment(this, data, &Line, &p2, &cp2)) ls[2]->DoPlot(o);
+		}
+	else if((type & 0xff) == ARROW_TRIANGLE) {
+		fill.type = FILL_NONE;	fill.color = Line.color;
+		fill.scale = 1.0;		fill.hatch = 0L;
+		tria[0].fz = tria[1].fz = tria[2].fz = fip2.fz;
+		tria[0].fx = cp1.x;		tria[0].fy = cp1.y;
+		tria[1].fx = fip2.fx;	tria[1].fy = fip2.fy;
+		tria[2].fx = cp2.x;		tria[2].fy = cp2.y;
+		if(cap = new plane(this, data, tria, 3, &Line, &fill))cap->DoPlot(o);
+	}
+}
+
+void
+Arrow3D::DoMark(anyOutput *o, bool mark)
+{
+	int i;
+
+	if(mark) {
+		for(i = 0; i < 3; i++) {
+			if(ls[i]){
+				InvertLine(mpts[i], 2, &Line, 0L, o, mark);
+				}
+			}
+		}
+	else if(parent) parent->Command(CMD_REDRAW, 0L, o);
+}
+
+bool
+Arrow3D::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	int i;
+
+	switch (cmd) {
+	case CMD_FLUSH:
+		for(i = 0; i < 3; i++){
+			if(ls[i]) delete(ls[i]);
+			ls[i] = 0L;
+			}
+		if(cap) delete(cap);	cap = 0L;
+		if(ssRef) free(ssRef);		ssRef = 0L;
+		if(name) free(name);		name = 0L;
+		return true;
+	case CMD_SCALE:
+		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
+		 cw *= ((scaleINFO*)tmpl)->sy.fy;					cl *= ((scaleINFO*)tmpl)->sy.fy;
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_ARROW3D;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_MRK_DIRTY:			//from Undo ?
+	case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
+				for(i = 0; i < 3; i++) {
+					if(ls[i] && ls[i]->ObjThere(mev->x, mev->y)){
+						o->ShowMark(this, MRK_GODRAW);
+						return true;
+						}
+					}
+				}
+			break;
+			}
+		break;
+	case CMD_ARROW_ORG3D:
+		if(tmpl) memcpy(&fPos1, tmpl, sizeof(fPOINT3D));
+		return true;
+	case CMD_ARROW_TYPE:
+		if(tmpl) type = *((int*)tmpl);
+		return true;
+	case CMD_UPDATE:
+		if(ssRef && cssRef >5 && data) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos2.fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos2.fy);
+			data->GetValue(ssRef[2].y, ssRef[2].x, &fPos2.fz);
+			data->GetValue(ssRef[3].y, ssRef[3].x, &fPos1.fx);
+			data->GetValue(ssRef[4].y, ssRef[4].x, &fPos1.fy);
+			data->GetValue(ssRef[5].y, ssRef[5].x, &fPos1.fz);
+			return true;
+			}
+		return false;
+	case CMD_AUTOSCALE:
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			((Plot*)parent)->CheckBounds3D(fPos1.fx, fPos1.fy, fPos1.fz);
+			((Plot*)parent)->CheckBounds3D(fPos2.fx, fPos2.fy, fPos2.fz);
+			return true;
+			}
+		break;
+	case CMD_SET_GO3D:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// a data line in 3D space
+Line3D::Line3D(GraphObj *par, DataObj *d, char *rx, char *ry, char *rz)
+	:GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	if(rx && rx[0]) x_range = (char*)memdup(rx, (int)strlen(rx)+2, 0L);
+	if(ry && ry[0]) y_range = (char*)memdup(ry, (int)strlen(ry)+2, 0L);
+	if(rz && rz[0]) z_range = (char*)memdup(rz, (int)strlen(rz)+2, 0L);
+	DoUpdate();
+	Id = GO_LINE3D;
+	bModified = false;
+}
+
+Line3D::Line3D(GraphObj *par, DataObj *d, fPOINT3D *pt, int n_pt, int xc1, int xr1, int yc1, int yr1,
+		int zc1, int zr1, int xc2, int xr2, int yc2, int yr2, int zc2, int zr2)
+	:GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	if(pt && n_pt) {
+		values = (fPOINT3D*)memdup(pt, n_pt * sizeof(fPOINT3D), 0L);
+		nPts = n_pt;
+		ls = (line_segment **)calloc(nPts-1, sizeof(line_segment*));
+		}
+	if(xc1 >= 0 || xr1 >= 0 || yc1 >= 0 || yr1 >= 0 || zc1 >= 0 || zr1 >= 0 || 
+		xc2 >= 0 || xr2 >= 0 || yc2 >= 0 || yr2 >= 0 || zc2 >= 0 || zr2 >= 0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*6)) {
+			ssRef[0].x = xc1;	ssRef[0].y = xr1;
+			ssRef[1].x = yc1;	ssRef[1].y = yr1;
+			ssRef[2].x = zc1;	ssRef[2].y = zr1;
+			ssRef[3].x = xc2;	ssRef[3].y = xr2;
+			ssRef[4].x = yc2;	ssRef[4].y = yr2;
+			ssRef[5].x = zc2;	ssRef[5].y = zr2;
+			cssRef = 6;
+			}
+		}
+	Id = GO_LINE3D;
+	bModified = false;
+}
+
+Line3D::Line3D(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	bModified = false;
+}
+
+Line3D::~Line3D()
+{
+	int i;
+
+	if(bModified) Undo.InvalidGO(this);
+	if(ls){
+		for(i = 0; i < (nPts-1); i++) if(ls[i]) delete(ls[i]);
+		free(ls);
+		}
+	if(pts && npts) free(pts);		pts = 0L;		npts = 0;	cssRef = 0;
+	if(x_range) free(x_range);		x_range = 0L;
+	if(y_range) free(y_range);		y_range = 0L;
+	if(z_range) free(z_range);		z_range = 0L;
+	if(values) free(values);		values = 0L;
+	if(ssRef) free(ssRef);			ssRef = 0L;
+	if(mo) DelBitmapClass(mo);		mo = 0L;
+}
+
+void
+Line3D::DoPlot(anyOutput *o)
+{
+	int i, j;
+	fPOINT3D fip;
+	POINT3D p1, p2;
+	POINT np;
+
+
+	if(mo) DelBitmapClass(mo);		mo = 0L;
+	if(ls) {
+		if(pts && npts) free(pts);
+		npts = 0;	if(!(pts = (POINT*)calloc(nPts, sizeof(POINT))))return;
+		for(i = 0; i< nPts; i++) {
+			if(!o->fvec2ivec(&values[i], &fip)) return;
+			p2.x = iround(fip.fx);	p2.y = iround(fip.fy);	p2.z = iround(fip.fz);
+			np.x = p2.x;			np.y = p2.y;
+			AddToPolygon(&npts, pts, &np);
+			if(i) {
+				UpdateMinMaxRect(&rDims, np.x, np.y);
+				j = i-1;
+				if(ls[j]) delete(ls[j]);
+				if(ls[j] = new line_segment(this, data, &Line, &p1, &p2)) {
+					ls[j]->DoPlot(o);
+					}
+				}
+			else {
+				rDims.left = rDims.right = p2.x;
+				rDims.top = rDims.bottom = p2.y;
+				}
+			p1.x = p2.x;	p1.y = p2.y;	p1.z = p2.z;
+			}
+		IncrementMinMaxRect(&rDims, 6);
+		}
+}
+
+void
+Line3D::DoMark(anyOutput *o, bool mark)
+{
+	if(mark) {
+		if(mo) DelBitmapClass(mo);					mo = 0L;
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		IncrementMinMaxRect(&mrc, 6 + o->un2ix(Line.width));
+		mo = GetRectBitmap(&mrc, o);
+		InvertLine(pts, npts, &Line, &rDims, o, true);
+		}
+	else RestoreRectBitmap(&mo, &mrc, o);
+}
+
+bool
+Line3D::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	POINT p1;
+	int i;
+
+	switch (cmd) {
+	case CMD_FLUSH:
+		if(name) free(name);		name = 0L;
+		return true;
+	case CMD_SCALE:
+		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
+		return true;
+	case CMD_SET_LINE:
+		if(tmpl) {
+			memcpy(&Line, tmpl, sizeof(LineDEF));
+			return true;
+			}
+		return false;
+	case CMD_SET_DATAOBJ:
+		Id = GO_LINE3D;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_REDRAW:
+		//Note: this command is issued  by Undo (no output given)
+		if(!parent) return false;
+		if(!o) return parent->Command(cmd, tmpl, o);
+		//Should we ever come here ?
+		return true;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(!IsInRect(&rDims, p1.x= mev->x, p1.y=mev->y)|| CurrGO || !o || nPts <2 ||
+				!IsCloseToPL(p1, pts, npts))return false;
+			o->ShowMark(CurrGO=this, MRK_GODRAW);
+			return true;
+			}
+		return false;
+	case CMD_UPDATE:
+		if(parent && parent->Id != GO_GRID3D) {
+			Undo.DataMem(this, (void**)&values, nPts * sizeof(fPOINT3D), &nPts, UNDO_CONTINUE);
+			}
+		if(ssRef && cssRef >5 && data && nPts == 2) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &values[0].fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &values[0].fy);
+			data->GetValue(ssRef[2].y, ssRef[2].x, &values[0].fz);
+			data->GetValue(ssRef[3].y, ssRef[3].x, &values[1].fx);
+			data->GetValue(ssRef[4].y, ssRef[4].x, &values[1].fy);
+			data->GetValue(ssRef[5].y, ssRef[5].x, &values[1].fz);
+			return true;
+			}
+		else DoUpdate();
+	case CMD_AUTOSCALE:
+		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH){
+			if(min.fx == max.fx || min.fy == max.fy){	//z's may be equal !
+				min.fx = min.fy = min.fz = HUGE_VAL;
+				max.fx = max.fy = max.fz = -HUGE_VAL;
+				for(i = 0; i < nPts; i++) {
+					if(values[i].fx < min.fx) min.fx = values[i].fx;
+					if(values[i].fy < min.fy) min.fy = values[i].fy;
+					if(values[i].fz < min.fz) min.fz = values[i].fz;
+					if(values[i].fx > max.fx) max.fx = values[i].fx;
+					if(values[i].fy > max.fy) max.fy = values[i].fy;
+					if(values[i].fz > max.fz) max.fz = values[i].fz;
+					}
+				}
+			((Plot*)parent)->CheckBounds3D(min.fx, min.fy, min.fz);
+			((Plot*)parent)->CheckBounds3D(max.fx, max.fy, max.fz);
+			return true;
+			}
+		return false;
+	case CMD_SET_GO3D:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+		}
+	return false;
+}
+
+void
+Line3D::DoUpdate()
+{
+	int n1 = 0, ic = 0, i, j, k, l, m, n;
+	double x, y, z;
+	AccRange *rX=0L, *rY=0L, *rZ=0L;
+
+	if(!x_range || !y_range || !z_range) return;
+	if(values) free(values);	values = 0L;
+	if(ls) free(ls);			ls = 0L;
+	rX = new AccRange(x_range);
+	rY = new AccRange(y_range);
+	rZ = new AccRange(z_range);
+	min.fx = min.fy = min.fz = HUGE_VAL;	max.fx = max.fy = max.fz = -HUGE_VAL;
+	if(rX) n1 = rX->CountItems();
+	if(n1 && rY && rZ && (values = (fPOINT3D*)malloc(n1 * sizeof(fPOINT3D)))){
+		rX->GetFirst(&i, &j);		rX->GetNext(&i, &j);
+		rY->GetFirst(&k, &l);		rY->GetNext(&k, &l);
+		rZ->GetFirst(&m, &n);		rZ->GetNext(&m, &n);
+		do {
+			if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y) && 
+				data->GetValue(n, m, &z)){
+				values[ic].fx = x;	values[ic].fy = y;	values[ic].fz = z;
+				if(x < min.fx) min.fx = x;	if(x > max.fx) max.fx = x;
+				if(y < min.fy) min.fy = y;	if(y > max.fy) max.fy = y;
+				if(z < min.fz) min.fz = z;	if(z > max.fz) max.fz = z;
+				ic++;
+				}
+			}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l) && rZ->GetNext(&m, &n));
+		nPts = ic;
+		if(ic > 1) ls = (line_segment **)calloc(ic-1, sizeof(line_segment*));
+		}
+	if(rX) delete(rX);	if(rY) delete(rY); if(rZ) delete(rZ);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// the text label class 
+Label::Label(GraphObj *par, DataObj *d, double x, double y, TextDEF *td, DWORD flg,
+	 int xc, int xr, int yc, int yr, int tc, int tr):GraphObj(par, d)
+{
+	int cb;
+
+	FileIO(INIT_VARS);
+	fPos.fx = x;		fPos.fy = y;		flags = flg;
+	if(parent){
+		fDist.fx = parent->GetSize(SIZE_LB_XDIST);
+		fDist.fy = parent->GetSize(SIZE_LB_YDIST);
+		}
+	Id = GO_LABEL;
+	if(td){
+		memcpy(&TextDef, td, sizeof(TextDEF));
+		if(td->text && td->text[0]) {
+			cb = (int)strlen(td->text)+1;			if(cb < 20) cb = 20;
+			TextDef.text = (char*)malloc(cb *sizeof(char));
+			rlp_strcpy(TextDef.text, cb, td->text);
+			}
+		}
+	if(xc >= 0 || xr >= 0 || yc >= 0 || yr >= 0 || tc >= 0 || tr >= 0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*3)) {
+			ssRef[0].x = xc;	ssRef[0].y = xr;
+			ssRef[1].x = yc;	ssRef[1].y = yr;
+			ssRef[2].x = tc;	ssRef[2].y = tr;
+			cssRef = 3;
+			}
+		}
+}
+
+Label::Label(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+Label::~Label()
+{
+	HideTextCursor();
+	Command(CMD_FLUSH, 0L, 0L);
+	if(bModified)Undo.InvalidGO(this);
+}
+
+double
+Label::GetSize(int select)
+{
+	switch(select){
+	case SIZE_CURSORPOS:
+		return (double)CursorPos;
+	case SIZE_CURSOR_XMIN:
+		return (double) (Cursor.right > Cursor.left ? Cursor.left : Cursor.right);
+	case SIZE_CURSOR_XMAX:
+		return (double) (Cursor.right > Cursor.left ? Cursor.right : Cursor.left);
+	case SIZE_CURSOR_YMIN:
+		return (double) (Cursor.bottom > Cursor.top ? Cursor.top : Cursor.bottom);
+	case SIZE_CURSOR_YMAX:
+		return (double) (Cursor.bottom > Cursor.top ? Cursor.bottom : Cursor.top);
+	case SIZE_MIN_Z:	case SIZE_MAX_Z:	case SIZE_ZPOS:
+		return curr_z;
+		}
+	return 0.0;
+}
+
+bool
+Label::SetSize(int select, double value)
+{
+	switch(select & 0xfff) {
+	case SIZE_LB_XDIST:			fDist.fx = value;		return true;
+	case SIZE_LB_YDIST:			fDist.fy = value;		return true;
+	case SIZE_XPOS:				fPos.fx = value;		return true;
+	case SIZE_YPOS:				fPos.fy = value;		return true;
+	case SIZE_ZPOS:				curr_z = value;			return is3D = true;
+	case SIZE_TEXT:				TextDef.fSize = value;		return true;
+		}
+	return false;
+}
+
+bool
+Label::SetColor(int select, DWORD col)
+{
+	switch(select & 0xfff) {
+	case COL_TEXT:
+		if(select & UNDO_STORESET) {
+			Undo.ValDword(this, &TextDef.ColTxt, UNDO_CONTINUE);
+			bModified = true;
+			}
+		TextDef.ColTxt = col;			bBGvalid = false;
+		return true;
+	case COL_BG:
+		bgcolor = col;	bBGvalid = true;
+		return true;
+		}
+	return false;
+}
+
+void
+Label::DoPlot(anyOutput *o)
+{
+	if(this != CurrGO && m1 >=0 && m2 >=0) {
+		m1 = m2 = -1;
+		}
+	if(is3D && parent && parent->Command(CMD_SET_GO3D, this, o)) return;
+	DoPlotText(o);
+}
+
+void
+Label::DoMark(anyOutput *o, bool mark)
+{
+	DWORD bgpix[16];
+	int i, d, d1, ix, iy, dx, dy, n;
+	RECT mrc;
+	anyOutput *mo;
+
+	if(mark) {
+		//find color with maximum contrast to text
+		if(!bBGvalid) {
+			mrc.left = rDims.left;		mrc.right = rDims.right;
+			mrc.top = rDims.top;		mrc.bottom = rDims.bottom;
+			dx = mrc.right - mrc.left;	dy = mrc.bottom - mrc.top;
+			if(dx <= 0 || dy <= 0) return;
+			if(mo = GetRectBitmap(&mrc, o)) {
+				for(i = 0,  ix = 1; ix < dx; ix += dx<<2) {
+					for(iy = 1; iy < dy; dy += dy<<2) {
+						if(!(mo->oGetPix(pts[ix].x, pts[iy].y, &bgpix[i]))) bgpix[i] = 0x00ffffff;
+						i++;
+						}
+					}
+				DelBitmapClass(mo);		n = i;
+				}
+			bgcolor = bgpix[0];
+			d = ColDiff(bgcolor, TextDef.ColTxt);
+			for(i = 1; i < n; i++) {
+				if(d < (d1 = ColDiff(bgpix[i], TextDef.ColTxt))) {
+					d = d1;		bgcolor = bgpix[i];
+					}
+				}
+			if(!d) bgcolor = TextDef.ColTxt ^ 0x00ffffffL;
+			bBGvalid = true;
+			}
+		//in dialogs parent has no parent
+		if(parent && parent->parent) o->ShowLine(pts, 5, TextDef.ColTxt);
+		ShowCursor(o);	CurrGO = this;		CurrLabel = this;
+		}
+	else if(m1 >= 0 && m2 >= 0) {
+		m1 = m2 = -1;
+		parent->Command(CMD_REDRAW, 0L, o);
+		}
+	else {
+		HideTextCursor();				m1 = m2 = -1;
+		bgLine.color = bgcolor;			o->SetLine(&bgLine);
+		//in dialogs parent has no parent
+		if(parent && parent->parent) o->oPolyline(pts, 5);
+		IncrementMinMaxRect(&rDims, 3);
+		o->UpdateRect(&rDims, false);	IncrementMinMaxRect(&rDims, -3);
+		if(CurrLabel == this) CurrLabel = 0L;
+		if(parent && parent->Id != GO_MLABEL && (!TextDef.text || !TextDef.text[0]))
+			parent->Command(CMD_DELOBJ, (void*)this, o);
+		}
+}
+
+bool
+Label::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	scaleINFO *scale;
+	int i, cb;
+
+	if(cmd != CMD_SET_DATAOBJ && !parent) return false;
+	switch (cmd) {
+	case CMD_SCALE:
+		scale = (scaleINFO*)tmpl;
+		if((flags & 0x03)!= LB_X_DATA) fPos.fx *= scale->sx.fy;
+		if((flags & 0x30)!= LB_Y_DATA) fPos.fy *= scale->sy.fy;
+		fDist.fx *= scale->sx.fy;				fDist.fy *= scale->sy.fy;
+		TextDef.fSize *= scale->sx.fy;			TextDef.iSize = 0;
+		return true;
+	case CMD_HIDEMARK:
+		if(m1 >=0 && m2 >=0 && m1 != m2) {
+			m1 = m2 = -1;						return true;
+			}
+		return false;
+	case CMD_FLUSH:
+		if(CurrLabel == this) CurrLabel = 0L;
+		if(TextDef.text) free(TextDef.text);	TextDef.text = 0L;
+		if(ssRef) free(ssRef);					ssRef = 0L;
+		return true;
+	case CMD_POS_FIRST:		case CMD_POS_LAST:
+		Undo.ValInt(this, &CursorPos, 0L);
+		bModified = true;
+		if(o && TextDef.text) {
+			CursorPos = (cmd == CMD_POS_LAST) ? (int)strlen(TextDef.text) : 0;
+			ShowCursor(o);
+			return true;
+			}
+		return false;
+	case CMD_SHIFTLEFT:		case CMD_CURRLEFT:
+		if(o && CursorPos >0 && TextDef.text) {
+			Undo.ValInt(this, &CursorPos, 0L);
+			DrawFmtText.SetText(0L, TextDef.text, 0L, 0L);
+			bModified = true;
+			if(cmd == CMD_SHIFTLEFT) {
+				if(CursorPos && CursorPos == m1) {
+					DrawFmtText.cur_left(&CursorPos);
+					m1 = CursorPos;		ShowCursor(o);
+					}
+				else if(CursorPos && CursorPos == m2) {
+					DrawFmtText.cur_left(&CursorPos);
+					m2 = CursorPos;		ShowCursor(o);
+					if(parent) parent->Command(CMD_REDRAW, 0L, o); 
+					}
+				else if(CursorPos){
+					m2 = CursorPos;
+					DrawFmtText.cur_left(&CursorPos);
+					m1 = CursorPos;		ShowCursor(o);
+					}
+				}
+			else {
+				if(m1 >= 0 && m2 >= 0 && parent) {
+					m1 = m2 = -1;						parent->Command(CMD_REDRAW, 0L, o);
+					}
+				DrawFmtText.cur_left(&CursorPos);		ShowCursor(o);
+				}
+			if(m1 >=0 && m2 >=0 && m1 != m2) DoPlot(o);
+			return true;
+			}
+		return false;
+	case CMD_SHIFTRIGHT:	case CMD_CURRIGHT:
+		if(o && TextDef.text && CursorPos < (int)strlen(TextDef.text)) {
+			Undo.ValInt(this, &CursorPos, 0L);
+			DrawFmtText.SetText(0L, TextDef.text, 0L, 0L);
+			bModified = true;
+			if(cmd == CMD_SHIFTRIGHT) {
+				if(CursorPos == m1 && TextDef.text[m1]) {
+					DrawFmtText.cur_right(&CursorPos);
+					m2 = CursorPos;		ShowCursor(o);
+					memcpy(&rm2, &Cursor, sizeof(RECT));
+					if(parent) parent->Command(CMD_REDRAW, 0L, o); 
+					}
+				else if(CursorPos == m2 && TextDef.text[m2]) {
+					DrawFmtText.cur_right(&CursorPos);
+					m2 = CursorPos;		ShowCursor(o);
+					memcpy(&rm2, &Cursor, sizeof(RECT));
+					}
+				else if(TextDef.text[CursorPos]){
+					if(m1 < 0) {
+						m1 = CursorPos;		ShowCursor(o);
+						}
+					DrawFmtText.cur_right(&CursorPos);
+					m2 = CursorPos;		ShowCursor(o);
+					}
+				else return false;
+				}
+			else {
+				if(m1 >= 0 && m2 >= 0 && parent) {
+					m1 = m2 = -1;						parent->Command(CMD_REDRAW, 0L, o);
+					}
+				DrawFmtText.cur_right(&CursorPos);		ShowCursor(o);
+				}
+			if(m1 >=0 && m2 >=0 && m1 != m2) DoPlot(o);
+			return true;
+			}
+		return false;
+	case CMD_ADDCHAR:	case CMD_ADDCHARW:
+		SetModified();
+		if(tmpl && 8 != *((int*)tmpl)) return AddChar(*((int*)tmpl), o);
+		//value 8 == backspace
+	case CMD_BACKSP:
+		SetModified();
+		if(CursorPos <=0 && o) {
+			if(parent && parent->Id == GO_MLABEL) {
+				parent->Command(CMD_SETFOCUS, this, o);
+				return parent->Command(CMD_BACKSP, tmpl, o);
+				}
+			RedrawEdit(o);
+			return true;
+			}
+		DrawFmtText.SetText(0L, TextDef.text, 0L, 0L);
+		DrawFmtText.cur_left(&CursorPos);					//continue as if delete
+	case CMD_DELETE:
+		SetModified();
+		if(TextDef.text && TextDef.text[CursorPos]) {
+			Undo.String(this, &TextDef.text, 0L);
+			if(cmd == CMD_DELETE) Undo.ValInt(this, &CursorPos, UNDO_CONTINUE);
+			if(CheckMark() && m2 < (cb = (int) strlen(TextDef.text))) {
+				Undo.ValInt(this, &m1, UNDO_CONTINUE);
+				Undo.ValInt(this, &m2, UNDO_CONTINUE);
+				rlp_strcpy(TextDef.text + m1, cb, TextDef.text + m2);
+				CursorPos = m1;			m1 = m2 = -1;
+				}
+			else {
+				DrawFmtText.SetText(0L, TextDef.text, 0L, 0L);
+				cb = CursorPos;		DrawFmtText.cur_right(&cb);
+				cb -= CursorPos;	if(cb < 1) cb = 1;
+				rlp_strcpy(TextDef.text + CursorPos, TMP_TXT_SIZE, TextDef.text + CursorPos + cb);
+				}
+			if(o) {
+				RedrawEdit(o);		ShowCursor(o);
+				}
+			}
+		else if(TextDef.text && parent->Id == GO_MLABEL) {
+			parent->Command(CMD_SETFOCUS, this, o);
+			return parent->Command(CMD_DELETE, tmpl, o);
+			}
+		else o->HideMark();
+		break;
+	case CMD_GETTEXT:
+		if(TextDef.text && TextDef.text[0] && tmpl) {
+			rlp_strcpy((char*)tmpl, TMP_TXT_SIZE, TextDef.text);
+			return true;
+			}
+		return false;
+	case CMD_SETTEXT:
+		if(TextDef.text) free(TextDef.text);		TextDef.text = 0L;
+		if(tmpl && *((char*)tmpl)) {
+			TextDef.text = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0);
+			}
+		return true;
+	case CMD_GETTEXTDEF:
+		if(!tmpl) return false;
+		memcpy(tmpl, &TextDef, sizeof(TextDEF));
+		return true;
+	case CMD_SETTEXTDEF:
+		if(!tmpl)return false;
+		memcpy(&TextDef, tmpl, sizeof(TextDEF)-sizeof(char*));
+		if(((TextDEF*)tmpl)->text) Command(CMD_SETTEXT, tmpl, o);
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_LABEL;
+		data = (DataObj*)tmpl;
+		return true;
+	case CMD_UPDATE:
+		if(ssRef && cssRef >2 && data) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &fPos.fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &fPos.fy);
+			if(data->GetText(ssRef[2].y, ssRef[2].x, TmpTxt, TMP_TXT_SIZE)) {
+				Undo.String(this, &TextDef.text, UNDO_CONTINUE);
+				TextDef.text = (char*)realloc(TextDef.text, cb = (int)strlen(TmpTxt)+2);
+				if(TmpTxt[0]) rlp_strcpy(TextDef.text, cb, TmpTxt);
+				else TextDef.text[0] = 0;
+				}
+			return true;
+			}
+		return false;
+	case CMD_SELECT:
+		if(!o || !tmpl) return false;
+		CalcCursorPos(((POINT*)tmpl)->x, ((POINT*)tmpl)->y, o);
+		o->ShowMark(this, MRK_GODRAW);
+		return true;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_MOVE:
+			if((mev->StateFlags & 0x01) && ObjThere(mev->x, mev->y)) {
+				i = CursorPos;				CalcCursorPos(mev->x, mev->y, o);
+				if(CurrLabel && CurrLabel != this) {
+					CurrLabel->Command(CMD_HIDEMARK, tmpl, o);
+					}
+				CurrGO = CurrLabel = this;
+				if(i == CursorPos) return true;
+				if(CursorPos > m1 && CursorPos < m2) {
+					if(m1 < 0) m1 = CursorPos;
+					else if(m2 != CursorPos)m2 = CursorPos;
+					parent->Command(CMD_REDRAW, 0L, o);
+					}
+				else {
+					if(m1 < 0) m1 = CursorPos;
+					else if(m2 != CursorPos)m2 = CursorPos;
+					if(m1 >=0 && m2 >=0 && m1 != m2) DoPlot(o);
+					}
+				return true;
+				}
+			break;
+		case MOUSE_LBUP:
+			if(ObjThere(mev->x, mev->y)) {
+				if(parent && parent->Id == GO_MLABEL) parent->Command(CMD_SETFOCUS, this, o);
+				CalcCursorPos(mev->x, mev->y, o);		
+				if(o->MrkRect && (void*)o->MrkRect == (void*)this) o->MrkMode = MRK_NONE;
+				if(m1 < 0) m1 = CursorPos;			ShowCursor(o);
+				o->ShowMark(this, MRK_GODRAW);
+				}
+			else if(m1 >= 0 && m2 >= 0) {
+				m1 = m2 = -1;		DoPlot(o);
+				}
+			break;
+			}
+		break;
+	case CMD_TEXTTHERE:
+		if(ObjThere(((MouseEvent *)tmpl)->x, ((MouseEvent *)tmpl)->y)) {
+			CalcCursorPos(((MouseEvent *)tmpl)->x, ((MouseEvent *)tmpl)->y, o);
+			CalcRect(o);
+			m1 = m2 = CursorPos;						CurrGO = this;								
+			return true;
+			}
+		m1 = m2 = -1;
+		return false;
+	case CMD_AUTOSCALE:
+		if(parent->Id >= GO_PLOT && parent->Id < GO_GRAPH
+			&& (flags & LB_X_DATA) && (flags & LB_Y_DATA)) {
+			((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
+			return true;
+			}
+		break;
+	case CMD_REDRAW:
+		if(is3D && o) {
+			DoPlotText(o);
+			is3D = false;										//enable edit
+			}
+		else if(CurrGO == this) {
+			if(parent && parent->parent) RedrawEdit(defDisp);	//not a dialog
+			else ShowCursor(defDisp);							//dialog !
+			}
+		else return parent->Command(cmd, tmpl, o);
+		return true;
+	case CMD_MOVE:
+		if(parent && (parent->Id == GO_MLABEL || parent->Id == GO_LEGITEM))
+			return parent->Command(cmd, tmpl, o);
+		Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
+	case CMD_UNDO_MOVE:
+		if(!(flags & 0x03)) fPos.fx += ((lfPOINT*)tmpl)[0].fx;
+		if(!(flags & 0x30)) fPos.fy += ((lfPOINT*)tmpl)[0].fy;
+		if(o){
+			o->StartPage();		parent->DoPlot(o);		o->EndPage();
+			}
+		return bModified = true;
+		}
+	return false;
+}
+
+void *
+Label::ObjThere(int x, int y)
+{
+	POINT p1;
+
+	if(IsInRect(&rDims, p1.x = x, p1.y = y) && IsInPolygon(&p1, pts, 5))
+		return this;
+	return 0L;
+}
+
+void
+Label::Track(POINT *p, anyOutput *o)
+{
+	POINT *tpts;
+	RECT old_rc;
+	int i;
+
+	if(!parent || !TextDef.text || !TextDef.text[0]) return;
+	m1 = m2 = -1;
+	if(parent->Id == GO_MLABEL || parent->Id == GO_LEGITEM){
+		parent->Track(p, o);
+		return;
+		}
+	if(o && (tpts = (POINT*)malloc(5*sizeof(POINT)))){
+		memcpy(&old_rc, &rDims, sizeof(rDims));
+		defs.UpdRect(o, rDims.left, rDims.top, rDims.right, rDims.bottom);
+		for(i = 0; i < 5; i++) {
+			tpts[i].x = pts[i].x+p->x;	tpts[i].y = pts[i].y+p->y;
+			defs.UpdAdd(o, tpts[i].x, tpts[i].y);
+			}
+		o->ShowLine(tpts, 5, TextDef.ColTxt);
+		free(tpts);
+		}
+}
+
+bool
+Label::CalcRect(anyOutput *o)
+{
+	int rx1, rx, ry;
+	fRECT rc, rcc;
+
+	if(parent && parent->Id != GO_MLABEL) o->SetTextSpec(&TextDef);
+	DrawFmtText.SetText(0L, TextDef.text, &ix, &iy);
+	if(TextDef.text && TextDef.text[0]) {
+		if(!(DrawFmtText.oGetTextExtent(o, &rx, &ry, 0))) return false;
+		rx++;
+		}
+	else {
+		if(!(o->oGetTextExtent("A", 1, &rx, &ry))) return false;
+		rx = 1;
+		}
+	rx += 4;	rc.Xmin = -2.0f;	rc.Ymin = 0.0f;		rc.Xmax = rx;		rc.Ymax = ry;
+	si = sin(TextDef.RotBL *0.01745329252);	csi = cos(TextDef.RotBL *0.01745329252);
+	if(TextDef.Align & TXA_HCENTER) {
+		rc.Xmin -= rx/2.0-1.0;		rc.Xmax -= rx/2.0-1.0;
+		}
+	else if(TextDef.Align & TXA_HRIGHT) {
+		rc.Xmin -= rx-2.0;			rc.Xmax -= rx-2.0;
+		}
+	if(TextDef.Align & TXA_VCENTER) {
+		rc.Ymin -= ry/2.0;			rc.Ymax -= ry/2.0;
+		}
+	else if(TextDef.Align & TXA_VBOTTOM) {
+		rc.Ymin -= ry;				rc.Ymax -= ry;
+		}
+	if(DrawFmtText.oGetTextExtent(o, &rx1, &ry, CursorPos)){
+		rx = CursorPos ? (int)rx1 : 0;
+		}
+	else rx = 0;
+	rcc.Xmax = rc.Xmin + (double)rx+2.0;	rcc.Ymin = rc.Ymin+2.0;
+	rcc.Xmin = rc.Xmin;						rcc.Ymax = rc.Ymax-2.0;
+	pts[0].x = iround(rc.Xmin*csi + rc.Ymin*si)+ix;
+	pts[0].y = iround(rc.Ymin*csi - rc.Xmin*si)+iy;
+	pts[1].x = iround(rc.Xmax*csi + rc.Ymin*si)+ix;
+	pts[1].y = iround(rc.Ymin*csi - rc.Xmax*si)+iy;
+	pts[2].x = iround(rc.Xmax*csi + rc.Ymax*si)+ix;
+	pts[2].y = iround(rc.Ymax*csi - rc.Xmax*si)+iy;
+	pts[3].x = iround(rc.Xmin*csi + rc.Ymax*si)+ix;
+	pts[3].y = iround(rc.Ymax*csi - rc.Xmin*si)+iy;
+	pts[4].x = pts[0].x;	pts[4].y = pts[0].y;
+	Cursor.left = iround(rcc.Xmax*csi + rcc.Ymin*si)+ix;
+	Cursor.top = iround(rcc.Ymin*csi - rcc.Xmax*si)+iy;
+	Cursor.right = iround(rcc.Xmax*csi + rcc.Ymax*si)+ix;
+	Cursor.bottom = iround(rcc.Ymax*csi - rcc.Xmax*si)+iy;
+	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+	UpdateMinMaxRect(&rDims, pts[2].x, pts[2].y);
+	UpdateMinMaxRect(&rDims, pts[3].x, pts[3].y);
+	UpdateMinMaxRect(&rDims, pts[4].x, pts[4].y);
+	return true;
+}
+
+void 
+Label::RedrawEdit(anyOutput *o)
+{
+	FillDEF bgFill = {FILL_NONE, bgcolor, 1.0, 0L, bgcolor};
+
+	if(!o || !parent) return;
+	bgLine.color = bgcolor;		o->SetLine(&bgLine);	o->SetFill(&bgFill);
+	o->oPolygon(pts, 5);		IncrementMinMaxRect(&rDims, 3);		
+	o->UpdateRect(&rDims, false);
+	CalcRect(o);			bgLine.color ^= 0x00ffffffL;
+	o->SetLine(&bgLine);		o->oPolygon(pts, 5);
+	if(parent->Id == GO_MLABEL) {
+		if(parent->parent && parent->parent->Id == GO_LEGITEM && parent->parent->parent)
+			parent->parent->parent->DoPlot(o);
+		else parent->DoPlot(o);
+		}
+	else if(parent->Id == GO_LEGITEM && parent->parent) parent->parent->DoPlot(o);
+	else DoPlot(o);
+	o->UpdateRect(&rDims, false);
+	DoMark(o, true);			ShowCursor(o);
+}
+
+void
+Label::SetModified()
+{
+	AxisDEF *adef;
+
+	bModified = true;
+	if(parent && parent->Id==GO_TICK && parent->parent && parent->parent->Id==GO_AXIS){
+	adef = ((Axis*)(parent->parent))->GetAxis();
+	adef->flags &= ~AXIS_AUTOSCALE;
+	}
+}
+
+void
+Label::DoPlotText(anyOutput *o)
+{
+	static LineDEF yLine = {0.0, 1.0, 0x0000ffff, 0x0};
+	static FillDEF yFill = {0, 0x0000ffff, 1.0, 0L, 0x0000ffff};
+	POINT mpts[5];
+	int i;
+
+	if(!parent || !o) return;
+	if(m1 >= 0 && m2 >= 0 && m1 != m2 && CurrGO == this) {
+		i = CursorPos;							CursorPos = m1;
+		CalcRect(o);							memcpy(&rm1, &Cursor, sizeof(RECT));
+		CursorPos = m2;							CalcRect(o);
+		memcpy(&rm2, &Cursor, sizeof(RECT));	CursorPos = i;
+		if(CurrGO == this) ShowCursor(o);
+		else CalcRect(o);
+		if(m2 > m1) {
+			mpts[0].x = mpts[4].x = rm1.left;	mpts[1].x = rm1.right;
+			mpts[0].y = mpts[4].y = rm1.top;	mpts[1].y = rm1.bottom;
+			mpts[2].x = rm2.right;				mpts[2].y = rm2.bottom;
+			mpts[3].x = rm2.left;				mpts[3].y = rm2.top;
+			}
+		else {
+			mpts[0].x = mpts[4].x = rm2.left;	mpts[1].x = rm2.right;
+			mpts[0].y = mpts[4].y = rm2.top;	mpts[1].y = rm2.bottom;
+			mpts[2].x = rm1.right;				mpts[2].y = rm1.bottom;
+			mpts[3].x = rm1.left;				mpts[3].y = rm1.top;
+			}
+		o->SetLine(&yLine);						o->SetFill(&yFill);
+		o->oPolygon(mpts, 5, 0L);
+		}
+	else {
+		m1 = m2 = -1;
+		if(CurrGO == this) ShowCursor(o);
+		}
+	defDisp = o;
+	if(parent && parent->Id == GO_MLABEL) parent->Command(CMD_SETFOCUS, this, o);
+	switch(flags & 0x03) {
+	case LB_X_DATA:	ix = o->fx2ix(fPos.fx);		break;
+	case LB_X_PARENT: 
+		ix = iround(parent->GetSize(SIZE_LB_XPOS));
+		break;
+	default:
+		ix = o->co2ix(fPos.fx + parent->GetSize(SIZE_GRECT_LEFT));
+		break;
+		}
+	switch(flags & 0x30) {
+	case LB_Y_DATA:	iy = o->fy2iy(fPos.fy);		break;
+	case LB_Y_PARENT: 
+		iy = iround(parent->GetSize(SIZE_LB_YPOS));
+		break;
+	default:
+		iy = o->co2iy(fPos.fy +parent->GetSize(SIZE_GRECT_TOP));
+		break;
+		}
+	ix += o->un2ix(fDist.fx);		iy += o->un2iy(fDist.fy);
+	TextDef.iSize = o->un2iy(TextDef.fSize);
+	o->SetTextSpec(&TextDef);
+	if(TextDef.text && TextDef.text[0]){
+		DrawFmtText.SetText(o, TextDef.text, &ix, &iy);
+		}
+	if(!(CalcRect(o))) return;
+	if(m1 >= 0 && m2 >= 0 && m1 != m2 && CurrGO == this && fabs(TextDef.RotBL) < 0.01) {
+		o->CopyBitmap(mpts[0].x, mpts[0].y, o, mpts[0].x, mpts[0].y,
+			mpts[2].x - mpts[0].x, mpts[2].y - mpts[0].y, true);
+		}
+	if(m1 >= 0 && m2 >= 0) o->UpdateRect(&rDims, false);
+}
+
+bool
+Label::CheckMark()
+{
+	int m;
+
+	if(m1 < 0 || m2 < 0 || m1 == m2) return false;
+	if(m1 < m2) return true;
+	//come here on right to left mark: swap m1 and m2
+	m = m1;		m1 = m2;	m2 = m;
+	return	true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// a multiline label consists of several Label objects
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+mLabel::mLabel(GraphObj *par, DataObj *d, double x, double y, TextDEF *td, char *txt, 
+	int cp, DWORD flg):GraphObj(par, d)
+{
+	int i;
+
+	memcpy(&TextDef, td, sizeof(TextDEF));
+	TextDef.text = 0L;		fPos.fx = x;		fPos.fy = y;
+	Lines = 0L;				flags = flg;		lspc = 1.0;
+	fDist.fx = 0.0;			fDist.fy = 0.0;
+	CurrGO = CurrLabel = 0L;
+	if(txt && (Lines = (Label**)calloc(2, sizeof(Label*)))) {
+		for(i = 0; i < cp && txt[i]; i ++) TmpTxt[i] = txt[i];
+		TmpTxt[i] = 0;
+		if(Lines[0] = new 	Label(this, d, x, y, &TextDef, LB_X_PARENT | LB_Y_PARENT))
+			Lines[0]->Command(CMD_SETTEXT, TmpTxt, 0L);
+		if(Lines[1] = new 	Label(this, d, x, y, &TextDef, LB_X_PARENT | LB_Y_PARENT)){
+			Lines[1]->Command(CMD_SETTEXT, txt+cp, 0L);
+			CurrGO = CurrLabel = Lines[1];
+			}
+		nLines = 2;		cli = 1;
+		}
+	Id = GO_MLABEL;
+}
+
+mLabel::mLabel(GraphObj *par, DataObj *d, double x, double y, TextDEF *td, char *txt)
+	:GraphObj(par, d)
+{
+	int i, nll;
+	char **llist;
+
+	memcpy(&TextDef, td, sizeof(TextDEF));
+	TextDef.text = 0L;		fPos.fx = x;		fPos.fy = y;
+	Lines = 0L;				flags = 0L;			Id = GO_MLABEL;
+	fDist.fx = 0.0;			fDist.fy = 0.0;		lspc = 1.0;
+	CurrGO = CurrLabel = 0L;
+	if(txt){
+		if((llist=split(txt,'\n',&nll)) && nll && (Lines=(Label**)calloc(nll, sizeof(Label*)))){
+			for(i = 0; i < nll; i++) {
+				if(llist[i]){
+					Lines[i] = new 	Label(this, d, x, y, &TextDef, LB_X_PARENT | LB_Y_PARENT);
+					Lines[i]->Command(CMD_SETTEXT, llist[i], 0L);
+					free(llist[i]);
+					}
+				}
+			free(llist);	nLines = nll;		cli = 0;
+			}
+		}
+}
+
+mLabel::mLabel(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+mLabel::~mLabel()
+{
+	int i;
+	
+	Undo.InvalidGO(this);
+	if(Lines){
+		for(i = 0; i < nLines; i++) if(Lines[i]) DeleteGO(Lines[i]);
+		free(Lines);
+		}
+}
+
+double
+mLabel::GetSize(int select)
+{
+	switch(select){
+	case SIZE_LB_XPOS:	return cPos1.fx;
+	case SIZE_LB_YPOS:	return cPos1.fy;
+	case SIZE_GRECT_TOP:	
+		if (parent) return parent->GetSize(select);
+		break;
+	case SIZE_MIN_Z:	case SIZE_MAX_Z:	case SIZE_ZPOS:
+		return curr_z;
+	case SIZE_LSPC:
+		return lspc;
+		}
+	return 0.0;
+}
+
+bool
+mLabel::SetSize(int select, double value)
+{
+	int i;
+
+	switch(select & 0xfff) {
+	case SIZE_LB_XDIST:
+		undo_flags = CheckNewFloat(&fDist.fx, fDist.fx, value, this, undo_flags);
+		return true;
+	case SIZE_LB_YDIST:
+		undo_flags = CheckNewFloat(&fDist.fy, fDist.fy, value, this, undo_flags);
+		return true;
+	case SIZE_XPOS:
+		undo_flags = CheckNewFloat(&fPos.fx, fPos.fx, value, this, undo_flags);
+		return true;
+	case SIZE_YPOS:
+		undo_flags = CheckNewFloat(&fPos.fy, fPos.fy, value, this, undo_flags);
+		return true;
+	case SIZE_ZPOS:
+		curr_z = value;
+		if(Lines) for(i = 0; i < nLines; i++) if(Lines[i]){
+			Lines[i]->SetSize(select, value);
+			}
+		return is3D = true;
+	case SIZE_LSPC:
+		undo_flags = CheckNewFloat(&lspc, lspc, value, this, undo_flags);
+		return true;
+		}
+	return false;
+}
+
+void
+mLabel::DoPlot(anyOutput *o)
+{
+	int i;
+	double dh, dx, dy;
+
+	if(!o || !Lines) return;
+	undo_flags = 0L;
+	if(parent){		//if this object is part of a dialog we dont have a parent
+		dx = parent->GetSize(SIZE_GRECT_LEFT);		dy = parent->GetSize(SIZE_GRECT_TOP);
+		}
+	else dx = dy = 0.0;
+	cPos.fx = cPos.fy = 0.0;
+	TextDef.iSize = o->un2iy(TextDef.fSize);
+	switch(flags & 0x03) {
+	case LB_X_DATA:		cPos.fx = o->fx2fix(fPos.fx);				break;
+	case LB_X_PARENT:	if(parent) cPos.fx = parent->GetSize(SIZE_LB_XPOS);	break;
+	default:
+		//if no parent its a dialog
+		cPos.fx = parent ? o->co2fix(fPos.fx + dx) : fPos.fx;
+		break;
+		}
+	switch(flags & 0x30) {
+	case LB_Y_DATA:		cPos.fy = o->fy2fiy(fPos.fy);				break;
+	case LB_Y_PARENT:	if(parent) cPos.fy = parent->GetSize(SIZE_LB_YPOS);	break;
+	default:	
+		//if no parent its a dialog
+		cPos.fy = parent ? o->co2fiy(fPos.fy + dy) : fPos.fy;
+		break;
+		}
+	si = sin(TextDef.RotBL *0.01745329252f);	csi = cos(TextDef.RotBL *0.01745329252f);
+	if(TextDef.Align & TXA_VBOTTOM) dh = (double)(nLines-1);
+	else if(TextDef.Align & TXA_VCENTER) dh = ((double)(nLines-1))/2.0;
+	else dh = 0.0;						dh *= TextDef.fSize;
+	cPos.fx -= o->un2fix(dh*si);		cPos.fy -= o->un2fiy(dh*csi);
+	memcpy(&cPos1, &cPos, sizeof(lfPOINT));
+	if(lspc < 0.5 || lspc > 5) lspc = 1.0;
+#ifdef _WINDOWS
+	dist.fx = floor(o->un2fix(TextDef.fSize * si * lspc));
+	dist.fy = floor(o->un2fiy(TextDef.fSize * csi * lspc));
+#else
+	dist.fx = floor(o->un2fix(TextDef.fSize * si * 1.2 * lspc));
+	dist.fy = floor(o->un2fiy(TextDef.fSize * csi * 1.2 * lspc));
+#endif
+	o->SetTextSpec(&TextDef);
+	rDims.left = rDims.top = rDims.right = rDims.bottom = 0;
+	for(i = 0; i < nLines; i++)	if(Lines[i]){
+		Lines[i]->Command(CMD_SETTEXTDEF, &TextDef, o);
+		Lines[i]->SetSize(SIZE_LB_XDIST, fDist.fx);	Lines[i]->SetSize(SIZE_LB_YDIST, fDist.fy);
+		Lines[i]->SetSize(SIZE_XPOS, fPos.fx);		Lines[i]->SetSize(SIZE_YPOS, fPos.fy);
+		Lines[i]->moveable = moveable;				Lines[i]->DoPlot(o);
+		UpdateMinMaxRect(&rDims, Lines[i]->rDims.left, Lines[i]->rDims.top);
+		UpdateMinMaxRect(&rDims, Lines[i]->rDims.right, Lines[i]->rDims.bottom);
+		}
+	if(CurrLabel && o && Command(CMD_SETFOCUS, CurrLabel, o))
+		Lines[cli]->ShowCursor(o);
+}
+
+bool
+mLabel::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i, j, k;
+	fRECT t_cur;
+	MouseEvent mev;
+	scaleINFO *scale;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:		case CMD_TEXTTHERE:
+		if(Lines && tmpl && IsInRect(&rDims, ((MouseEvent*)tmpl)->x, ((MouseEvent*)tmpl)->y)) 
+			for(i = 0; i<nLines; i++) if(Lines[i] && Lines[i]->Command(cmd, tmpl, o)) return true;
+		break;
+	case CMD_ADDCHAR:
+		if(!tmpl || 13 != *((int*)tmpl)) return false;
+		if(!Lines[cli] || !Lines[cli]->Command(CMD_GETTEXT, &TmpTxt, o)) return false;
+		k = iround(Lines[cli]->GetSize(SIZE_CURSORPOS));
+		if(parent)Undo.ObjConf(this, 0L);
+		if(tmpl && Lines && (Lines = (Label**)realloc(Lines, (nLines+1) * sizeof(Label*)))) {
+			for(i = nLines-1, j = nLines; i >= cli; i--, j-- ) {
+				Lines[j] = Lines[i];
+				}
+			i++, j++;
+			if(Lines[j]) DeleteGO(Lines[j]);	Lines[i] = Lines[j] = 0L;	nLines++;
+			if(Lines[j] = new 	Label(this, data, fPos.fx, fPos.fy, &TextDef, LB_X_PARENT | LB_Y_PARENT)){
+				Lines[j]->Command(CMD_SETTEXT, TmpTxt+k, o);
+				Lines[j]->Command(CMD_POS_FIRST, 0L, o);
+				CurrGO = CurrLabel = Lines[j];
+				TmpTxt[k] = 0;
+				}
+			if(Lines[i] = new 	Label(this, data, fPos.fx, fPos.fy, &TextDef, LB_X_PARENT | LB_Y_PARENT))
+				Lines[i]->Command(CMD_SETTEXT, TmpTxt, 0L);
+			Command(CMD_SETFOCUS, CurrGO, o);
+			if(parent) return parent->Command(CMD_REDRAW, 0L, o);
+			else if(o) DoPlot(o);
+			}
+		break;
+	case CMD_SETFOCUS:
+		for(i = 0; i< nLines; i++) if(Lines[i] == (Label*)tmpl) {
+			cli = i;
+			cPos1.fx = cPos.fx + dist.fx*i;
+			cPos1.fy = cPos.fy + dist.fy*i;
+			return true;
+			}
+		break;
+	case CMD_GETTEXT:
+		if(tmpl && Lines && nLines && Lines[0]) return Lines[0]->Command(CMD_GETTEXT, tmpl, o);
+		break;
+	case CMD_ALLTEXT:							//used e.g from dialog text boxes
+		if(tmpl && Lines && nLines){
+			for(i = j = 0; i < nLines; i++) {
+				if(Lines[i] && Lines[i]->Command(CMD_GETTEXT, TmpTxt+500, o) && TmpTxt[500]){
+					j += rlp_strcpy((char*)tmpl+j, 500-j, TmpTxt+500);
+					((char*)tmpl)[j++] = '\n';
+					}
+				((char*)tmpl)[j] = 0;
+				}
+			if(j >2) return true;
+			}
+		return false;
+	case CMD_DELETE:
+		cli++;
+		//fall through
+	case CMD_BACKSP:
+		if(cli > 0 && cli < nLines && Lines && Lines[cli] && Lines[cli-1]) {
+			Lines[cli-1]->Command(CMD_POS_LAST, 0L, o);
+			TmpTxt[0] = 0;
+			Lines[cli-1]->Command(CMD_GETTEXT, TmpTxt, o);
+			Lines[cli]->Command(CMD_GETTEXT, TmpTxt+strlen(TmpTxt), o);
+			Lines[cli-1]->Command(CMD_SETTEXT, TmpTxt, o);
+			DeleteGO(Lines[cli]);	Lines[cli] = 0L;
+			for(i = cli+1; i < nLines; i++){
+				Lines[i-1] = Lines[i], Lines[i]= 0L;
+				}
+			nLines--;
+			CurrGO = CurrLabel = Lines[cli-1];
+			if(parent) parent->Command(CMD_REDRAW, 0L, o);
+			if(CurrLabel) CurrLabel->RedrawEdit(o);
+			return true;
+			}
+		return false;
+	case CMD_GETTEXTDEF:
+		if(!tmpl) return false;
+		memcpy(tmpl, &TextDef, sizeof(TextDEF));
+		return true;
+	case CMD_SETTEXTDEF:
+		if(!tmpl)return false;
+		if(parent)Undo.TextDef(this, &TextDef, undo_flags);
+		undo_flags |= UNDO_CONTINUE;
+		memcpy(&TextDef, tmpl, sizeof(TextDEF));
+		TextDef.text = 0L;
+		return true;
+	case CMD_SET_DATAOBJ:
+		if(Lines) {
+			for(i = 0; i< nLines; i++) if(Lines[i]) Lines[i]->Command(cmd, tmpl, o);
+			}
+		Id = GO_MLABEL;
+		data = (DataObj*)tmpl;
+		return true;
+	case CMD_AUTOSCALE:
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH
+			&& (flags & LB_X_DATA) && (flags & LB_Y_DATA)) {
+			((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
+			return true;
+			}
+		break;
+	case CMD_CURRUP:		case CMD_CURRDOWN:
+		if(!o) return false;
+		o->SetTextSpec(&TextDef);
+		Command(CMD_SETFOCUS, CurrGO, o);
+		if(cli >= 0 && cli < nLines && Lines && Lines[cli]) {
+			t_cur.Xmin = Lines[cli]->GetSize(SIZE_CURSOR_XMIN);
+			t_cur.Xmax = Lines[cli]->GetSize(SIZE_CURSOR_XMAX);
+			t_cur.Ymin = Lines[cli]->GetSize(SIZE_CURSOR_YMIN);
+			t_cur.Ymax = Lines[cli]->GetSize(SIZE_CURSOR_YMAX);
+			mev.StateFlags = 0;		mev.Action = MOUSE_LBUP;
+			mev.x = iround((t_cur.Xmax+t_cur.Xmin)/2.0);
+			mev.y = iround((t_cur.Ymax+t_cur.Ymin)/2.0);
+			i = o->un2ix(TextDef.fSize*si);		j = o->un2iy(TextDef.fSize*csi);
+			if(cmd == CMD_CURRUP && cli > 0 && Lines[cli-1]) {
+				Lines[cli-1]->CalcCursorPos(mev.x -= i, mev.y -= j, o);
+				o->ShowMark(CurrGO = CurrLabel = Lines[cli-=1], MRK_GODRAW);
+				return Command(CMD_SETFOCUS, Lines[cli], o);
+				}
+			if(cmd == CMD_CURRDOWN && cli < (nLines-1) && Lines[cli+1]) {
+				Lines[cli+1]->CalcCursorPos(mev.x += i, mev.y += j, o);
+				o->ShowMark(CurrGO = CurrLabel = Lines[cli+=1], MRK_GODRAW);
+				return Command(CMD_SETFOCUS, Lines[cli], o);
+				}
+			}
+		else return false;
+		break;
+	case CMD_SCALE:
+		scale = (scaleINFO*)tmpl;
+		if((flags & 0x03)!= LB_X_DATA) fPos.fx *= scale->sx.fy;
+		if((flags & 0x30)!= LB_Y_DATA) fPos.fy *= scale->sy.fy;
+		fDist.fx *= scale->sx.fy;				fDist.fy *= scale->sy.fy;
+		TextDef.fSize *= scale->sx.fy;			TextDef.iSize = 0;
+		return true;
+	case CMD_SELECT:
+		if(o && tmpl) for(i = 0; i < nLines; i++){
+			o->SetTextSpec(&TextDef);
+			if(Lines[i] && ((POINT*)tmpl)->y > Lines[i]->rDims.top && ((POINT*)tmpl)->y < 
+				Lines[i]->rDims.bottom) return Lines[i]->Command(cmd, tmpl, o);
+			}
+		break;
+	case CMD_DELOBJ:
+		if(parent && Lines) for(i = 0; i< nLines; i++) 
+			if(Lines[i] && Lines[i] == (Label*)tmpl) return parent->Command(cmd, this, o);
+		break;
+	case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		else if(o) DoPlot(o);
+		break;
+	case CMD_MOVE:
+		if(parent && parent->Id == GO_LEGITEM) return parent->Command(cmd, tmpl, o);
+		Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
+	case CMD_UNDO_MOVE:
+		if(!(flags & 0x03)) fPos.fx += ((lfPOINT*)tmpl)[0].fx;
+		if(!(flags & 0x30)) fPos.fy += ((lfPOINT*)tmpl)[0].fy;
+		if(o && parent){
+			o->StartPage();		parent->DoPlot(o);		o->EndPage();
+			}
+		return true;
+		}
+	return false;
+}
+
+void
+mLabel::Track(POINT *p, anyOutput *o)
+{
+	int i;
+
+	if(!parent) return;
+	if(parent->Id == GO_LEGITEM){
+		parent->Track(p, o);
+		return;
+		}
+	for(i = 0; i < nLines; i++)	if(Lines[i]){
+		if(!i) memcpy(&rDims, &Lines[i]->rDims, sizeof(RECT));
+		else {
+			UpdateMinMaxRect(&rDims, Lines[i]->rDims.left, Lines[i]->rDims.top);
+			UpdateMinMaxRect(&rDims, Lines[i]->rDims.right, Lines[i]->rDims.bottom);
+			}
+		}
+	defs.UpdRect(o, rDims.left, rDims.top, rDims.right, rDims.bottom);
+	Id = 0L;			//disable reentrance from Label objects
+	for(i = 0; i < nLines; i++)	if(Lines[i]) Lines[i]->Track(p, o);
+	Id = GO_MLABEL;		//enable entrance from Label objects
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// a rectangular range to accept any text
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+TextFrame::TextFrame(GraphObj *parent, DataObj *data, lfPOINT *p1, lfPOINT *p2, char *txt)
+	:GraphObj(parent, data)
+{
+	FileIO(INIT_VARS);					lspc = 1.0;
+	pos1.fx = p1->fx;	pos1.fy = p1->fy;	pos2.fx = p2->fx;	pos2.fy = p2->fy;
+	if(txt && txt[0]) {
+		text = (unsigned char*)memdup(txt, (int)strlen(txt)+1, 0);
+		}
+	else if(lines = (unsigned char**)malloc(sizeof(char*))){
+		if(lines[0] = (unsigned char*)malloc(TF_MAXLINE))lines[0][0] = 0;
+		nlines = 1;
+		}
+	Id = GO_TEXTFRAME;
+}
+
+TextFrame::TextFrame(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	moveable = 1;				bModified = false;
+}
+
+TextFrame::~TextFrame()
+{
+	int i;
+
+	if(text)free(text);			text = 0L;
+	if(drc) delete(drc);		drc = 0L;
+	if(tm_rec) free(tm_rec);	tm_rec = 0L;
+	if(lines) {
+		for(i = 0; i < nlines; i++)	if(lines[i]) free(lines[i]);
+		free(lines);			lines = 0L;
+		}
+	HideTextCursor();
+}
+double
+TextFrame::GetSize(int select)
+{
+	switch(select) {
+	case SIZE_XPOS:		return pos1.fx;
+	case SIZE_XPOS+1:	return pos2.fx;
+	case SIZE_YPOS:		return pos1.fy;
+	case SIZE_YPOS+1:	return pos2.fy;
+	case SIZE_GRECT_LEFT:	case SIZE_GRECT_TOP:
+	case SIZE_GRECT_RIGHT:	case SIZE_GRECT_BOTTOM:
+		if(parent) return parent->GetSize(select);
+		break;
+		}
+	return 0.0;
+}
+
+bool
+TextFrame::SetSize(int select, double value)
+{
+	switch(select & 0xfff) {
+	case SIZE_XPOS:				pos1.fx = value;			return bResize = true;
+	case SIZE_XPOS+1:			pos2.fx = value;			return bResize = true;
+	case SIZE_YPOS:				pos1.fy = value;			return bResize = true;
+	case SIZE_YPOS+1:			pos2.fy = value;			return bResize = true;
+		}
+	return false;
+}
+
+void 
+TextFrame::DoMark(anyOutput *o, bool mark)
+{
+	RECT upd;
+
+	if(has_m1 && has_m2) TextMark(o, 3);
+	has_m1 = has_m2 = false;
+	if(!drc) drc = new dragRect(this, 0);
+	memcpy(&upd, &rDims, sizeof(RECT));
+	if(mark){
+		if(drc) drc->DoPlot(o);								ShowCursor(o);
+		}
+	else if(parent)	parent->DoPlot(o);
+	IncrementMinMaxRect(&upd, 6);
+	o->UpdateRect(&upd, false);
+}
+
+void
+TextFrame::DoPlot(anyOutput *o)
+{
+	int i, j, x1, y1, x2, y2;
+	double tmp, dx, dy;
+
+	if(!o) return;
+	if(parent){		//if this object is part of a dialog we dont have a parent
+		dx = parent->GetSize(SIZE_GRECT_LEFT);			dy = parent->GetSize(SIZE_GRECT_TOP);
+		x1 = o->co2ix(pos1.fx+dx);						y1 = o->co2iy(pos1.fy+dy);
+		x2 = o->co2ix(pos2.fx+dx);						y2 = o->co2iy(pos2.fy+dy);
+		ipad.left = o->un2ix(pad.Xmin);					ipad.right = o->un2ix(pad.Xmax);
+		ipad.top = o->un2iy(pad.Ymin);					ipad.bottom = o->un2iy(pad.Ymax);
+		}
+	else {
+		dx = dy = 0.0;
+		x1 = (int)pos1.fx;		y1 = (int)pos1.fy;		x2 = (int)pos2.fx;		y2 = (int)pos2.fy;
+		ipad.left = ipad.right = ipad.top = ipad.bottom = 4;
+		fmt_txt.EditMode(true);
+		}
+	if(pos1.fx > pos2.fx) {
+		tmp = pos2.fx;	pos2.fx = pos1.fx;	pos1.fx = tmp;
+		}
+	if(pos1.fy > pos2.fy) {
+		tmp = pos2.fy;	pos2.fy = pos1.fy;	pos1.fy = tmp;
+		}
+	o->SetLine(&Line);						o->SetFill(&Fill);
+	o->oRectangle(x1, y1, x2, y2, name);
+	SetMinMaxRect(&rDims, x1, y1, x2, y2);
+	x1 += ipad.left;						y1 += ipad.top;
+	TextDef.iSize = o->un2iy(TextDef.fSize);
+#ifdef _WINDOWS
+	linc = o->un2iy(TextDef.fSize*lspc);
+#else
+	linc = o->un2iy(TextDef.fSize*lspc*1.2);
+#endif
+	o->SetTextSpec(&TextDef);				y1 += linc;
+	if(text && text[0] && !(lines)) text2lines(o);
+	else if(bResize && lines) {
+		c_char = lines[cur_pos.y][cur_pos.x];			lines[cur_pos.y][cur_pos.x] = 0x01;
+		if(!c_char) lines[cur_pos.y][cur_pos.x+1] = 0x00; 
+		lines2text();				text2lines(o);		bResize = false;
+		o->ShowMark(this, MRK_GODRAW);
+		return;
+		}
+	if(has_m1 && has_m2) TextMark(o, 1);
+	for(i = 0; i < nlines; i++) {
+		if(lines[i] && lines[i][0]){
+			j = (int)strlen((char*)lines[i]);
+			if(lines[i][j-1] == '\n') {
+				lines[i][j-1] = 0;
+				fmt_txt.SetText(o, (char*)lines[i], &x1, &y1);
+				lines[i][j-1] = '\n';
+				}
+			else fmt_txt.SetText(o, (char*)lines[i], &x1, &y1);
+			}
+		y1 += linc;
+		}
+	if(has_m1 && has_m2) TextMark(o, 2);
+	bModified = bResize = false;
+}
+
+bool
+TextFrame::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	int i;
+
+	switch (cmd) {
+	case CMD_SELECT:
+		if(!o || !tmpl) return false;
+		CalcCursorPos(((POINT*)tmpl)->x, ((POINT*)tmpl)->y, o);
+		if(parent)o->ShowMark(this, MRK_GODRAW);	ShowCursor(o);
+		return true;
+	case CMD_COPY:
+		return CopyText(o, false);
+	case CMD_SAVEPOS:
+		bModified = true;
+		Undo.SaveLFP(this, &pos1, 0L);
+		Undo.SaveLFP(this, &pos2, UNDO_CONTINUE);
+		return true;
+	case CMD_SCALE:
+		if(tmpl) {
+			pos1.fx = ((scaleINFO*)tmpl)->sx.fx + pos1.fx * ((scaleINFO*)tmpl)->sx.fy;
+			pos1.fy = ((scaleINFO*)tmpl)->sy.fx + pos1.fy * ((scaleINFO*)tmpl)->sy.fy;
+			pos2.fx = ((scaleINFO*)tmpl)->sx.fx + pos2.fx * ((scaleINFO*)tmpl)->sx.fy;
+			pos2.fy = ((scaleINFO*)tmpl)->sy.fx + pos2.fy * ((scaleINFO*)tmpl)->sy.fy;
+			TextDef.fSize *= ((scaleINFO*)tmpl)->sy.fy;		TextDef.iSize = 0;
+			pad.Xmax *= ((scaleINFO*)tmpl)->sx.fy;			pad.Xmin *= ((scaleINFO*)tmpl)->sx.fy;
+			pad.Ymax *= ((scaleINFO*)tmpl)->sy.fy;			pad.Ymin *= ((scaleINFO*)tmpl)->sy.fy;
+			Line.width *= ((scaleINFO*)tmpl)->sy.fy;		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+			FillLine.width *= ((scaleINFO*)tmpl)->sy.fy;	FillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;
+			Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;
+			}
+		return true;
+	case CMD_GETTEXT:	case CMD_ALLTEXT:
+		if(lines && lines[0] && lines[0][0] && nlines) {
+			lines2text();	i = rlp_strcpy((char*)tmpl, TMP_TXT_SIZE-2, (char*)text);
+			while(i && ((char*)tmpl)[i-1] == '\n') i--; 
+			((char*)tmpl)[i++] = '\n';						((char*)tmpl)[i] = 0;
+			return true;
+			}
+		return false;
+	case CMD_ADDCHARW:	case CMD_ADDCHAR:
+		if(tmpl && o) AddChar(o, *((int *)tmpl));
+		return true;
+	case CMD_DELETE:
+		if(o) DelChar(o);
+		return true;
+	case CMD_SHIFTLEFT:
+		if(!has_m1) {
+			m1_pos.x = cur_pos.x;		m1_pos.y = cur_pos.y;
+			}
+		has_m1 = has_m2 = true;
+	case CMD_CURRLEFT:
+		if(!(lines[cur_pos.y]))return false;
+		if(cmd == CMD_CURRLEFT && has_m1 && has_m2) TextMark(o, 3);
+		if(cur_pos.x > 0) {
+			i = 0;					fmt_txt.SetText(0L,(char*)lines[cur_pos.y], &i, &i);
+			i = cur_pos.x;			fmt_txt.cur_left(&i);
+			cur_pos.x = i;
+			}
+		else if(cur_pos.y) {
+			cur_pos.y--;			cur_pos.x = (int)strlen((char*)lines[cur_pos.y]);
+			}
+		if(cmd == CMD_SHIFTLEFT){
+			m2_pos.x = cur_pos.x;		m2_pos.y = cur_pos.y;
+			DoPlot(o);					o->UpdateRect(&rDims, false);
+			}
+		ShowCursor(o);
+		return false;
+	case CMD_SHIFTRIGHT:
+		if(!has_m1) {
+			m1_pos.x = cur_pos.x;		m1_pos.y = cur_pos.y;
+			}
+		has_m1 = has_m2 = true;
+	case CMD_CURRIGHT:
+		if(!(lines[cur_pos.y]))return false;
+		if(cmd == CMD_CURRIGHT && has_m1 && has_m2) TextMark(o, 3);
+		if(cur_pos.x >= (int)strlen((char*)lines[cur_pos.y])) {
+			if(cur_pos.y < (nlines-1)) {
+				cur_pos.y++;		cur_pos.x = 0;
+				}
+			else if(cur_pos.y == (nlines-1) && lines[cur_pos.y][0]) {
+				if(!(lines = (unsigned char**)realloc(lines, (nlines+1)*sizeof(char*))))return false;
+				if(!(lines[cur_pos.y+1] = (unsigned char*)malloc(TF_MAXLINE))) return false;
+				cur_pos.y++;		cur_pos.x = 0;		nlines++;
+				lines[cur_pos.y][0] = 0;
+				}
+			}
+		else {
+			i = 0;						fmt_txt.SetText(0L,(char*)lines[cur_pos.y], &i, &i);
+			i = cur_pos.x;				fmt_txt.cur_right(&i);
+			cur_pos.x = i;
+			}
+		if(cmd == CMD_SHIFTRIGHT){
+			m2_pos.x = cur_pos.x;		m2_pos.y = cur_pos.y;
+			DoPlot(o);					o->UpdateRect(&rDims, false);
+			}
+		ShowCursor(o);
+		return false;
+	case CMD_SHIFTUP:
+		if(!has_m1) {
+			m1_pos.x = cur_pos.x;		m1_pos.y = cur_pos.y;
+			}
+		has_m1 = has_m2 = true;
+	case CMD_CURRUP:
+		if(cmd == CMD_CURRUP && has_m1 && has_m2) TextMark(o, 3);
+		if(cur_pos.y && o) {
+			i = ((Cursor.bottom + Cursor.top)>>1)-linc;
+			CalcCursorPos(Cursor.left, i, o);
+			if(cmd == CMD_SHIFTUP){
+				m2_pos.x = cur_pos.x;	m2_pos.y = cur_pos.y;
+				DoPlot(o);				o->UpdateRect(&rDims, false);
+				}
+			ShowCursor(o);
+			return true;
+			}
+		return false;
+	case CMD_SHIFTDOWN:
+		if(!has_m1) {
+			m1_pos.x = cur_pos.x;		m1_pos.y = cur_pos.y;
+			}
+		has_m1 = has_m2 = true;
+	case CMD_CURRDOWN:
+		if(cmd == CMD_CURRDOWN && has_m1 && has_m2) TextMark(o, 3);
+		if(cur_pos.y < (nlines-1) && o && lines[cur_pos.y][0]) {
+			i = ((Cursor.bottom + Cursor.top)>>1)+linc;
+			if(i >= (rDims.bottom-ipad.bottom)) return false;
+			CalcCursorPos(Cursor.left, i, o);
+			if(cmd == CMD_SHIFTDOWN){
+				m2_pos.x = cur_pos.x;	m2_pos.y = cur_pos.y;
+				DoPlot(o);				o->UpdateRect(&rDims, false);
+				}
+			ShowCursor(o);
+			return true;
+			}
+		return false;
+	case CMD_POS_FIRST:
+		if(has_m1 && has_m2) TextMark(o, 3);
+		cur_pos.x = 0;									ShowCursor(o);
+		return true;
+	case CMD_POS_LAST:
+		if(has_m1 && has_m2) TextMark(o, 3);
+		cur_pos.x = (int)strlen((char*)lines[cur_pos.y]);
+		ShowCursor(o);
+		return true;
+	case CMD_TEXTTHERE:
+		if(IsInRect(&rDims, ((MouseEvent *)tmpl)->x, ((MouseEvent *)tmpl)->y)) return true;
+		return false;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, mev->x, mev->y) && (!(CurrGO) || !has_m2) && o){
+				CalcCursorPos(mev->x, mev->y, o);
+				if(has_m1 && has_m2) {
+					if(o)DoPlot(o);						o->UpdateRect(&rDims, false);
+					CurrGO = this;						ShowCursor(o);
+					return true;
+					}
+				return o->ShowMark(this, MRK_GODRAW);
+				}
+			else if(CurrGO == this && o){
+				ShowCursor(o);
+				return IsInRect(&rDims, mev->x, mev->y);
+				}
+			break;
+		case MOUSE_LBDOWN:
+			if(has_m1 && has_m2) {
+				has_m1 = has_m2 = false;
+				if(o)DoPlot(o);							o->UpdateRect(&rDims, false);
+				}
+			has_m1 = has_m2 = false;
+		case MOUSE_MOVE:
+			if(!(mev->StateFlags & 0x1)) return false;
+			if(!IsInRect(&rDims, mev->x, mev->y))return false;
+			if(!CurrGO) CurrGO = this;
+			CalcCursorPos(mev->x, mev->y, o);
+			if(!has_m1) {
+				m1_pos.x = m2_pos.x = cur_pos.x;		m1_pos.y = m2_pos.y = cur_pos.y;
+				return has_m1 = true;
+				}
+			else if(cur_pos.x != m2_pos.x || cur_pos.y != m2_pos.y) {
+				m2_pos.x = cur_pos.x;					m2_pos.y = cur_pos.y;
+				has_m2 = true;
+				if(o)DoPlot(o);							o->UpdateRect(&rDims, false);
+				return true;
+				}
+			return true;
+			}
+		return false;
+	case CMD_SET_DATAOBJ:
+		Id = GO_TEXTFRAME;
+		return true;
+	case CMD_PASTE:
+		return DoPaste(o);
+	case CMD_MOVE:
+		bModified = true;
+		Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
+	case CMD_UNDO_MOVE:
+		pos1.fx += ((lfPOINT*)tmpl)[0].fx;	pos1.fy += ((lfPOINT*)tmpl)[0].fy;
+		pos2.fx += ((lfPOINT*)tmpl)[0].fx;	pos2.fy += ((lfPOINT*)tmpl)[0].fy;
+		CurrGO = this;
+	case CMD_REDRAW:
+		if(parent){
+			if(o && cmd == CMD_REDRAW) DoPlot(o);		//trickle down ?
+			if(!o && cmd == CMD_REDRAW) {
+				//coming from Undo
+				bModified = true;
+				if(lines) {
+					for(i = 0; i < nlines; i++) if(lines[i]) free(lines[i]);
+					free(lines);			lines = 0L;
+					}
+				HideTextCursor();			cur_pos.x = cur_pos.y = 0;
+				parent->Command(CMD_REDRAW, tmpl, o);
+				}
+			else parent->Command(CMD_REDRAW, tmpl, o);
+			}
+		return true;
+	case CMD_SETSCROLL:
+		if(parent) return parent->Command(cmd, tmpl, o);
+	case CMD_GETTEXTDEF:
+		if(!tmpl) return false;
+		memcpy(tmpl, &TextDef, sizeof(TextDEF));
+		return true;
+	case CMD_SETTEXTDEF:
+		if(!tmpl)return false;
+		memcpy(&TextDef, tmpl, sizeof(TextDEF));
+		TextDef.text = 0L;
+		return true;
+	case CMD_SETTEXT:
+		if(lines) {
+			for(i = 0; i < nlines; i++) if(lines[i]) free(lines[i]);
+			free(lines);			lines = 0L;
+			}
+		if(text) free(text);		text = 0L;
+		if(tmpl && *((char*)tmpl)) text = (unsigned char*)memdup(tmpl, (int)strlen(((char*)tmpl))+1, 0);
+		return true;
+		}
+	return false;
+}
+
+void
+TextFrame::Track(POINT *p, anyOutput *o)
+{
+	POINT tpts[5];
+	RECT old_rc;
+	double dx, dy;
+
+	if(o && parent){
+		dx = parent->GetSize(SIZE_GRECT_LEFT);		dy = parent->GetSize(SIZE_GRECT_TOP);
+		memcpy(&old_rc, &rDims, sizeof(rDims));
+		o->UpdateRect(&rDims, false);
+		tpts[0].x = tpts[1].x = tpts[4].x = o->co2ix(pos1.fx+dx)+p->x;		
+		tpts[0].y = tpts[3].y = tpts[4].y = o->co2iy(pos1.fy+dy)+p->y;
+		tpts[1].y = tpts[2].y = o->co2iy(pos2.fy+dy)+p->y;
+		tpts[2].x = tpts[3].x = o->co2ix(pos2.fx+dx)+p->x;
+		UpdateMinMaxRect(&rDims, tpts[0].x, tpts[0].y);
+		UpdateMinMaxRect(&rDims, tpts[2].x, tpts[2].y);	
+		if(old_rc.left != rDims.left || old_rc.right != rDims.right || old_rc.top !=
+			rDims.top || old_rc.bottom != rDims.bottom)IncrementMinMaxRect(&rDims, 3);
+		o->ShowLine(tpts, 5, Line.color);
+		}
+}
+
+void *
+TextFrame::ObjThere(int x, int y)
+{
+	if(drc) return drc->ObjThere(x, y);
+	return 0L;
+}
+
+void
+TextFrame::text2lines(anyOutput *o)
+{
+	int i, j, w, h, cl, maxlines, maxw;
+	char tmp_line[TF_MAXLINE];
+	bool hasMark = false;
+
+	if(lines) {
+		for(i = 0; i < nlines; i++) if(lines[i]) free(lines[i]);
+		free(lines);			lines = 0L;
+		}
+	has_m1 = has_m2 = false;
+	nlines = 0;
+	if(!text || !text[0]) return;
+	maxlines = (rDims.bottom -rDims.top)/linc +1;
+	maxw = rDims.right - rDims.left - ipad.left - ipad.right;
+	lines = (unsigned char**)calloc(maxlines, sizeof(char*));
+	for(cl = cpos = w = h = 0; cl < maxlines && text[cpos]; cl++) {
+		if(!(lines[cl] = (unsigned char*)malloc(TF_MAXLINE))) return;
+		for(i = 0; text[cpos] && i < TF_MAXLINE; i++) {
+			tmp_line[i] = text[cpos++];				tmp_line[i+1] = 0;
+			fmt_txt.SetText(0L, tmp_line, &w, &h);
+			if(!(fmt_txt.oGetTextExtent(o, &w, &h, i))) break;
+			if(tmp_line[i] == '\n'){
+				break;													//new line character found
+				}
+			if(tmp_line[i] < ' ') switch(tmp_line[i]) {
+				case 0x01:
+					if(c_char == 0 && text[cpos]) c_char = text[cpos++];	//cursor at end of line
+					hasMark = true;				break;
+				case 0x02:	case 0x03:
+					hasMark = true;				break;
+				}
+			else if(w >= maxw){
+				for(j = i; j > (i>>1); j--) {
+					if(tmp_line[j] == ' ' || tmp_line[j] == '-' || tmp_line[j] < ' ') break;
+					}
+				if(j == (i>>1)) {
+					cpos--;							tmp_line[i] = 0;
+					}
+				else {
+					for(tmp_line[j+1] = 0; j < i; j++, cpos--);
+					}
+				break;
+				}
+			}
+		if(i || tmp_line[i] == '\n') rlp_strcpy((char*)lines[cl], TF_MAXLINE, tmp_line);
+		else lines[cl][0] = 0;
+		}
+	nlines = cl;
+	if(hasMark)procTokens();
+}
+
+void
+TextFrame::lines2text()
+{
+	int i;
+
+	if(text) free(text);		cpos = 0;
+	text = (unsigned char*)malloc(csize = 1000);
+	for(i = 0; i < nlines; i++) {
+		if(lines[i] && lines[i][0]) add_to_buff((char**)&text, &cpos, &csize, (char*)lines[i], 0);
+		}
+}
+
+void
+TextFrame::AddChar(anyOutput *o, int c)
+{
+	int i, j, h, w, maxw;
+	bool brd;
+	char *txt1;
+
+	if(cur_pos.y >= nlines) return;
+	if(!lines || !lines[cur_pos.y]) return;
+	if(c == '\r') c = '\n';
+	if(has_m1 && has_m2){
+		TmpTxt[0] = c;		TmpTxt[1] = 0;
+		if(c == 8) ReplMark(o, "");
+		else if(c >= 32 || c == '\n') ReplMark(o, TmpTxt);
+		return;
+		}
+	else if(!text) {
+		lines2text();
+		Undo.TextBuffer(this, &csize, &cpos, &text, 0L, o);
+		}
+	maxw = rDims.right - rDims.left - ipad.left - ipad.right;
+	i = j = (int)strlen((char*)lines[cur_pos.y])+1;
+	has_m1 = has_m2 = false;
+	if(c >= 32 || c == '\n') {
+		if(c > 254 && (txt1 = (char*)malloc(10))) {
+#ifdef USE_WIN_SECURE
+			w = sprintf_s(txt1, 10, "&#%d;", c);
+#else
+			w = sprintf(txt1, "&#%d;", c);
+#endif
+			for(j = j+w; j>0; j--) {
+				lines[cur_pos.y][j] = lines[cur_pos.y][j-w];
+				if((j-w) == cur_pos.x){
+					for(i = 0; i < w; i++) lines[cur_pos.y][j-w+i] = txt1[i];
+					j = 0;					cur_pos.x += w;
+					}
+				}
+			free(txt1);
+			}
+		else while(j) {
+			lines[cur_pos.y][j] = lines[cur_pos.y][j-1];	j--;
+			if(j == cur_pos.x){
+				lines[cur_pos.y][j] = c;					j = 0;
+				cur_pos.x++;
+				}
+			}
+		fmt_txt.SetText(0L, (char*)lines[cur_pos.y], &j, &j);
+		fmt_txt.oGetTextExtent(o, &w, &h, i);				brd = false;
+		if(cur_pos.x > 2 && (c == '>' || c == ';')) {
+			if(c == '>' && fmt_txt.leftTag((char*)lines[cur_pos.y],cur_pos.x-1) >= 0) brd =true;
+			if(c == ';' && fmt_txt.ucLeft((char*)lines[cur_pos.y],cur_pos.x-1, 0L, 0L) > 0) brd =true;
+			}
+		if(brd || w >= maxw || c == '\n' || (c == ' ' && w >=(maxw>>1))) {
+			c_char = lines[cur_pos.y][cur_pos.x];			lines[cur_pos.y][cur_pos.x] = 0x01;
+			if(!c_char) lines[cur_pos.y][cur_pos.x+1] = 0x00; 
+			lines2text();	Undo.TextBuffer(this, &csize, &cpos, &text, 0L, o);		text2lines(o);
+			}
+		}
+	else if(c == 8 && (cur_pos.x || cur_pos.y)) {			//Backspace
+		if(!cur_pos.x) Command(CMD_CURRLEFT, 0L, o);
+		Command(CMD_CURRLEFT, 0L, o);						DelChar(o);
+		return;
+		}
+	else return;
+	DoPlot(o);		o->UpdateRect(&rDims, false);			ShowCursor(o);
+}
+
+void
+TextFrame::DelChar(anyOutput *o)
+{
+	int i, cb, x;
+
+	if(has_m1 && has_m2){
+		ReplMark(o, "");	return;
+		}
+	if(lines[cur_pos.y][cur_pos.x]) {
+		lines2text();			Undo.TextBuffer(this, &csize, &cpos, &text, 0L, o);
+		cb = x = cur_pos.x;			fmt_txt.cur_right(&x);
+		cb = x - cb;	if(cb < 1) cb = 1;
+		for(i = cur_pos.x; lines[cur_pos.y][i]; i++) {
+			if(!(lines[cur_pos.y][i] = lines[cur_pos.y][i+cb])) break;
+			}
+		c_char = lines[cur_pos.y][cur_pos.x];			lines[cur_pos.y][cur_pos.x] = 0x01;
+		if(!c_char) lines[cur_pos.y][cur_pos.x+1] = 0x00; 
+		lines2text();			text2lines(o);
+		DoPlot(o);				o->UpdateRect(&rDims, false);
+		ShowCursor(o);			return;
+		}
+	else if(cur_pos.y < (nlines-1)){
+		cur_pos.y++;			cur_pos.x = 0;
+		DelChar(o);				return;
+		}
+}
+
+void
+TextFrame::ReplMark(anyOutput *o, char *ntext)
+{
+	int i, j;
+
+	if(!has_m1 || !has_m2 || !o || !ntext) return;
+	lines2text();
+	Undo.TextBuffer(this, &csize, &cpos, &text, 0L, o);
+	for(i = cpos = 0; i < nlines && i < m1_cpos.y; i++) {
+		if(lines[i] && lines[i][0]) add_to_buff((char**)&text, &cpos, &csize, (char*)lines[i], 0);
+		}
+	if(m1_cpos.x)add_to_buff((char**)&text, &cpos, &csize, (char*)lines[i], m1_cpos.x);
+	add_to_buff((char**)&text, &cpos, &csize, ntext, 0);			j = cpos;
+	if(m1_cpos.y == m2_cpos.y)add_to_buff((char**)&text, &cpos, &csize, (char*)(lines[i]+m2_cpos.x), 0);
+	for( ; i < nlines && i < m2_cpos.y; i++);
+	if(i == m2_cpos.y && m2_cpos.y > m1_cpos.y)add_to_buff((char**)&text, &cpos, &csize, (char*)(lines[i]+m2_cpos.x), 0);
+	for(i++; i < nlines; i++) {
+		if(lines[i] && lines[i][0]) add_to_buff((char**)&text, &cpos, &csize, (char*)lines[i], 0);
+		}
+	if(tm_rec)free(tm_rec);			tm_rec = 0L;		has_m1 = has_m2 = false;
+	if(text[j]) {
+		c_char = text[j];						text[j] = 0x01;
+		}
+	else if(i < (nlines-1) && lines[i+1] && lines[i+1][0]) {
+		c_char = lines[i+1][0];					lines[i+1][0] = 0x01;
+		}
+	else if(j == cpos){
+		c_char = 0;		text[cpos++] = 0x01;		text[cpos] = 0;
+		}
+	else cur_pos.x = cur_pos.y = 0;
+	text2lines(o);		DoPlot(o);	o->UpdateRect(&rDims, false);
+	ShowCursor(o);
+}
+
+void
+TextFrame::procTokens()
+{
+	int i, j;
+
+	for(i = 0; i < nlines; i++) {
+		if(lines[i] && lines[i][0]){
+			for(j = 0; lines[i][j]; j++) switch(lines[i][j]) {
+				case 0x01:
+					cur_pos.y = i;				cur_pos.x = j;
+					lines[i][j] = c_char;		c_char = '?';
+					break;
+				case 0x02:
+					m1_pos.y = i;				m1_pos.x = j;
+					if(m1_char == 0x01) {
+						lines[i][j] = c_char;	c_char = '?';
+						cur_pos.y = i;			cur_pos.x = j;
+						}
+					else lines[i][j] = m1_char;
+					has_m1 = true;				m1_char = '?';
+					break;
+				case 0x03:
+					m2_pos.y = i;				m2_pos.x = j;
+					if(m2_char == 0x01) {
+						lines[i][j] = c_char;	c_char = '?';
+						cur_pos.y = i;			cur_pos.x = j;
+						}
+					else lines[i][j] = m2_char;
+					has_m2 = true;				m2_char = '?';
+					break;
+					}
+			}
+		}
+}
+
+bool
+TextFrame::DoPaste(anyOutput *o)
+{
+	int i, j, k;
+	char *ntxt;
+	unsigned char *ptxt;
+
+	if((ptxt = PasteText()) && ptxt[0]) {
+		lines2text();	Undo.TextBuffer(this, &csize, &cpos, &text, 0L, o);
+		if(!(ntxt = (char*)malloc(strlen((char*)ptxt)+1))) return false;
+		for(i = j = cpos = 0; ptxt[i]; i++) {
+			if(ptxt[i] >= ' ' || ptxt[i] == '\n') ntxt[j++] = ptxt[i];
+			else if(ptxt[i] == 9)ntxt[j++] = ' ';		//convert tab->space 
+			}
+		ntxt[j] = 0;
+		if(!ntxt[0]) {
+			free(ntxt);				return false;
+			}
+		if(has_m1 && has_m2) {
+			ReplMark(o, ntxt);		free(ntxt);
+			return true;
+			}
+		m1_char = ntxt[0];			ntxt[0] = 0x02;
+		ntxt[j] = 0;				if(text) free(text);
+		if(!(text = (unsigned char*)malloc(csize = 1000)))return false;
+		for(i = k = 0, text[0] = 0; i < nlines; i++) {
+			if(lines[i] && lines[i][0]){
+				if(i == cur_pos.y) {
+					if(cur_pos.x) add_to_buff((char**)&text, &cpos, &csize, (char*)lines[i], cur_pos.x);
+					add_to_buff((char**)&text, &cpos, &csize, ntxt, 0);	k = cpos;
+					add_to_buff((char**)&text, &cpos, &csize, (char*)lines[i]+cur_pos.x, 0);
+					free(ntxt);		ntxt = 0L;
+					}
+				else add_to_buff((char**)&text, &cpos, &csize, (char*)lines[i], 0);
+				}
+			}
+		if(ntxt) {
+			add_to_buff((char**)&text, &cpos, &csize, ntxt, 0);			k = cpos;
+			}
+		m2_char = 0x01;
+		if(text[k]) {
+			c_char = text[k];						text[k] = 0x03;
+			}
+		else if(i < (nlines-1) && lines[i+1] && lines[i+1][0]) {
+			c_char = lines[i+1][0];					lines[i+1][0] = 0x03;
+			}
+		else if(k == cpos){
+			c_char = 0;		text[cpos++] = 0x03;	text[cpos] = 0;
+			}
+		text2lines(o);						DoPlot(o);
+		o->UpdateRect(&rDims, false);
+		ShowCursor(o);						if(ntxt) free(ntxt);
+		}
+	return false;
+}
+void
+TextFrame::TextMark(anyOutput *o, int mode)
+{
+	int i, j, w, h;
+	LineDEF ld;
+	FillDEF fd;
+
+	if(m1_pos.y > m2_pos.y) {
+		m1_cpos.y = m2_pos.y;				m1_cpos.x = m2_pos.x;
+		m2_cpos.y = m1_pos.y;				m2_cpos.x = m1_pos.x;
+		}
+	else if(m1_pos.y == m2_pos.y  && m1_pos.x > m2_pos.x) {
+		m1_cpos.x = m2_pos.x;				m2_cpos.x = m1_pos.x;
+		m1_cpos.y = m2_cpos.y = m1_pos.y;
+		}
+	else {
+		m1_cpos.y = m1_pos.y;				m1_cpos.x = m1_pos.x;
+		m2_cpos.y = m2_pos.y;				m2_cpos.x = m2_pos.x;
+		}
+	if(!has_m1 || !has_m2 || !o) return;
+	if(m1_pos.y == m2_pos.y && m1_pos.x == m2_pos.x) return;
+	if(mode == 1){							//create background mark
+		if(tm_rec)free(tm_rec);				tm_rec = 0L;
+		if((tm_c = m2_cpos.y - m1_cpos.y +1)<1) return;
+		if(!(tm_rec = (RECT*)malloc(tm_c * sizeof(RECT))))return;
+		for(i = w = 0, j = m1_cpos.y; j <= m2_cpos.y; i++, j++) {
+			h = TextDef.iSize;
+			if(j == m1_cpos.y) {
+				fmt_txt.SetText(0L, (char*)lines[j], 0L, 0L);
+				if(m1_cpos.x) fmt_txt.oGetTextExtent(o, &w, &h, m1_cpos.x);
+				else w = 0;
+				tm_rec[0].left = w + rDims.left + ipad.left + 1;			
+				fmt_txt.SetText(0L, (char*)(lines[j]+m1_cpos.x), 0L, 0L);
+				fmt_txt.oGetTextExtent(o, &w, &h, 0);
+				tm_rec[0].right = tm_rec[0].left + w;
+				}
+			if(j == m2_cpos.y) {
+				fmt_txt.SetText(0L, (char*)lines[j], 0L, 0L);
+				if(m2_cpos.x) fmt_txt.oGetTextExtent(o, &w, &h, m2_cpos.x);
+				else w = 0;
+				tm_rec[i].right = w + rDims.left + ipad.left - 1;			
+				if(m2_cpos.y > m1_cpos.y) {
+					tm_rec[i].left = rDims.left + ipad.left;
+					}
+				}
+			else if(j < m2_cpos.y && j > m1_cpos.y) {
+				tm_rec[i].left = rDims.left + ipad.left;
+				tm_rec[i].top = j * linc + rDims.top + ipad.top;
+				fmt_txt.SetText(0L, (char*)lines[j], 0L, 0L);
+				fmt_txt.oGetTextExtent(o, &w, &h, 0);
+				tm_rec[i].right = w + rDims.left + ipad.left;			
+				}
+			tm_rec[i].top = j * linc + rDims.top + ipad.top;
+			tm_rec[i].bottom = tm_rec[i].top + linc;
+			}
+		ld.color = 0x0000ffff;				ld.patlength = 1.0;
+		ld.pattern = 0x0L;					ld.width = 0;
+		fd.color = fd.color2 = ld.color;	fd.hatch = 0L;
+		fd.scale = 1.0;						fd.type = 0;
+		o->SetLine(&ld);					o->SetFill(&fd);
+		for(i = 0; i < tm_c; i++) {
+			o->oRectangle(tm_rec[i].left, tm_rec[i].top, tm_rec[i].right, tm_rec[i].bottom, 0L);
+			}
+		}
+	if(mode == 2){							//invert rectangles
+		if(tm_rec) for(i = 0; i < tm_c; i++) {
+			o->CopyBitmap(tm_rec[i].left, tm_rec[i].top, o, tm_rec[i].left, tm_rec[i].top,
+				tm_rec[i].right - tm_rec[i].left, tm_rec[i].bottom - tm_rec[i].top, true);
+			}
+		}
+	if(mode == 3){							//clear mark
+		if(tm_rec)free(tm_rec);			tm_rec = 0L;
+		tm_c = 0;						has_m1 = has_m2 = false;
+		DoPlot(o);						o->UpdateRect(&rDims, false);
+		}
+}
+
+bool
+TextFrame::CopyText(anyOutput *o, bool b_cut)
+{
+	int i, csize, pos = 0;
+	char *ntxt;
+
+	if(!lines || !lines[0][0] || !o) return false;
+	if(!has_m1 || !has_m2) {
+		m1_pos.x = m1_pos.y = 0;		m2_pos.y = nlines-1;
+		if(lines[nlines-1]) m2_pos.x = (int)strlen((char*)lines[nlines-1]);
+		else m2_pos.x = 0;				has_m1 = has_m2 = true;
+		DoPlot(o);						o->UpdateRect(&rDims, false);
+		return CopyText(o, false);
+		}
+	if(!(ntxt = (char*)malloc(csize = 1000))) return false;
+	if(m1_cpos.y == m2_cpos.y) {
+		add_to_buff(&ntxt, &pos, &csize, (char*)(lines[m1_cpos.y]) + m1_cpos.x, m2_cpos.x - m1_cpos.x);
+		::CopyText(ntxt, pos);			free(ntxt);
+		}
+	else if(m1_cpos.y < m2_cpos.y){
+		add_to_buff(&ntxt, &pos, &csize, (char*)(lines[m1_cpos.y]) + m1_cpos.x, 0);
+		for(i = m1_cpos.y; i < m2_cpos.y; i++) {
+			add_to_buff(&ntxt, &pos, &csize, (char*)(lines[i]), 0);
+			}
+		add_to_buff(&ntxt, &pos, &csize, (char*)(lines[i]), m2_pos.x);
+		::CopyText(ntxt, pos);			free(ntxt);
+		}
+	else {
+		free(ntxt);						return false;
+		}
+	if(b_cut) ReplMark(o, "");
+	return true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// The segment object is either a pie slice or a ring segment
+//
+segment::segment(GraphObj *par, DataObj *d, lfPOINT *c, double r1, double r2,
+				 double a1, double a2):GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	segFill.hatch = &segFillLine;
+	segFill.color = 0x00c0c0c0L;
+	fCent.fx = c->fx;		fCent.fy = c->fy;
+	radius1 = r1;			radius2 = r2;
+	angle1 = a1;			angle2 = a2;
+	Id = GO_SEGMENT;
+	bModified = false;
+}
+
+segment::segment(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	bModified = false;
+}
+
+segment::~segment()
+{
+	if(pts && nPts) free(pts);			pts = 0L;
+	if(bModified) Undo.InvalidGO(this);
+	if(mo) DelBitmapClass(mo);			mo = 0L;
+}
+	
+bool 
+segment::SetSize(int select, double value)
+{
+	switch(select & 0xfff) {
+	case SIZE_XPOS:		fCent.fx = value;		break;
+	case SIZE_YPOS:		fCent.fy = value;		break;
+	case SIZE_RADIUS1:	radius1 = value;		break;
+	case SIZE_RADIUS2:	radius2 = value;		break;
+	case SIZE_ANGLE1:	angle1 = value;			break;
+	case SIZE_ANGLE2:	angle2 = value;			break;
+	default:			return false;
+		}
+	return true;
+}
+
+void
+segment::DoPlot(anyOutput *o)
+{
+	double dsize, dpt, frx1, frx2, dtmp, da, dda, sia, csia;
+	int i, n, npt = 12;
+	POINT np, of, cp,  *tmppts;
+
+	if(!o || angle1 == angle2) return;
+	if(mo) DelBitmapClass(mo);		mo = 0L;
+	dsize = angle1 > angle2 ? angle1 -angle2 : (angle1+360.0)-angle2;
+	dpt = dsize*0.01745329252;		//degrees to rad
+	frx1 = (double)o->un2fix(radius1);
+	frx2 = (double)o->un2fix(radius2);
+	dtmp = frx1*dpt;
+	npt += (int)(dtmp < dsize ? dtmp : dsize);
+	dtmp = frx2*dpt;
+	npt += (int)(dtmp < dsize ? dtmp : dsize);
+	if(!(pts = (POINT*)malloc(npt*sizeof(POINT))))return;
+	nPts = 0;
+	n = (dtmp < dsize) ? (int)dtmp : (int)dsize;
+	while (n<2) n++;
+	da = angle1*0.01745329252;
+	dda = (dsize*0.01745329252)/(double)n;
+	sia = sin(0.5*((angle2 < angle1 ? angle1 : angle1 +360) + angle2) * 0.01745329252);
+	csia = cos(0.5*((angle2 < angle1 ? angle1 : angle1 +360) + angle2) * 0.01745329252);
+	of.x = o->un2ix(shift * csia);
+	of.y = - (o->un2iy(shift * sia));
+	cp.x = o->co2ix(fCent.fx + (parent ? parent->GetSize(SIZE_GRECT_LEFT): 0.0));
+	cp.y = o->co2iy(fCent.fy + (parent ? parent->GetSize(SIZE_GRECT_TOP): 0.0));
+	for(i = 0; i < n; i++) {
+		sia = sin(da);					csia = cos(da);
+		np.x = of.x + cp.x;				np.y = of.y + cp.y;
+		np.x += o->un2ix(csia*radius2);	np.y -= o->un2iy(sia*radius2);
+		AddToPolygon(&nPts, pts, &np);
+		da -= dda;
+		}
+	sia = sin(angle2 *0.01745329252);
+	csia = cos(angle2 *0.01745329252);
+	np.x = of.x + cp.x;					np.y = of.y + cp.y;
+	np.x += o->un2ix(csia*radius2);		np.y -= o->un2iy(sia*radius2);
+	AddToPolygon(&nPts, pts, &np);
+	dtmp = frx1*dpt;
+	n = dtmp < dsize ? (int)dtmp : (int)dsize;
+	da = angle2*0.01745329252;
+	if(n>1)dda = (dsize*0.01745329252)/(double)n;
+	else dda = 0.0;
+	for(i = 0; i < n; i++) {
+		sia = sin(da);					csia = cos(da);
+		np.x = of.x + cp.x;				np.y = of.y + cp.y;
+		np.x += o->un2ix(csia*radius1);	np.y -= o->un2iy(sia*radius1);
+		AddToPolygon(&nPts, pts, &np);
+		da += dda;
+		}
+	sia = sin(angle1 *0.01745329252);	csia = cos(angle1 *0.01745329252);
+	np.x = of.x + cp.x;					np.y = of.y + cp.y;
+	np.x += o->un2ix(csia*radius1);		np.y -= o->un2iy(sia*radius1);
+	AddToPolygon(&nPts, pts, &np);
+	if(nPts <3) return;
+	AddToPolygon(&nPts, pts, &pts[0]);	//close polygon
+	o->SetLine(&segLine);				o->SetFill(&segFill);
+	o->oPolygon(pts, nPts);
+	if(tmppts = (POINT*)realloc(pts, nPts *sizeof(POINT))) pts = tmppts;
+	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+	for(i = 2; i < nPts; i++) UpdateMinMaxRect(&rDims, pts[i].x, pts[i].y);
+	i = 3*o->un2ix(segLine.width);		//increase size of rectangle for marks
+	IncrementMinMaxRect(&rDims, i+6);
+}
+
+void
+segment::DoMark(anyOutput *o, bool mark)
+{
+	if(mark){
+		if(mo) DelBitmapClass(mo);			mo = 0L;
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		mo = GetRectBitmap(&mrc, o);
+		InvertLine(pts, nPts, &segLine, &rDims, o, mark);
+		}
+	else if(mo) RestoreRectBitmap(&mo, &mrc, o);
+}
+
+bool
+segment::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	FillDEF *TmpFill;
+	LegItem *leg;
+
+	switch (cmd) {
+	case CMD_SCALE:
+		fCent.fx *= ((scaleINFO*)tmpl)->sx.fy;			fCent.fy *= ((scaleINFO*)tmpl)->sx.fy;
+		radius1 *= ((scaleINFO*)tmpl)->sx.fy;			radius2 *= ((scaleINFO*)tmpl)->sx.fy;
+		segLine.width *= ((scaleINFO*)tmpl)->sx.fy;		segLine.patlength *= ((scaleINFO*)tmpl)->sx.fy;
+		segFillLine.width *= ((scaleINFO*)tmpl)->sx.fy;	segFillLine.patlength *= ((scaleINFO*)tmpl)->sx.fy;
+		segFill.scale *= ((scaleINFO*)tmpl)->sx.fy;		shift *= ((scaleINFO*)tmpl)->sx.fy;
+		return true;
+	case CMD_LEGEND:
+		if(tmpl) {
+			leg = new LegItem(this, data, 0L, &segLine, &segFill, 0L);
+			if(!((Legend*)tmpl)->Command(CMD_DROP_OBJECT, leg, o)) DeleteGO(leg);
+			}
+		return true;
+	case CMD_MOUSE_EVENT:
+		switch (((MouseEvent*)tmpl)->Action) {
+		case MOUSE_LBUP:
+			if(ObjThere(((MouseEvent*)tmpl)->x, ((MouseEvent*)tmpl)->y)) 
+				return o->ShowMark(CurrGO=this, MRK_GODRAW);
+			}
+		return false;
+	case CMD_SET_DATAOBJ:
+		Id = GO_SEGMENT;
+		return true;
+	case CMD_REDRAW:
+		return parent ? parent->Command(cmd, tmpl, o) : false;
+	case CMD_SEG_FILL:
+		TmpFill = (FillDEF *)tmpl;
+		if(TmpFill) {
+			segFill.type = TmpFill->type;			segFill.color = TmpFill->color;
+			segFill.scale = TmpFill->scale;
+			if(TmpFill->hatch) memcpy(&segFillLine, TmpFill->hatch, sizeof(LineDEF));
+			}
+		return true;
+	case CMD_SEG_LINE:
+		if(tmpl) memcpy(&segLine, tmpl, sizeof(LineDEF));
+		return true;
+	case CMD_SEG_MOVEABLE:
+		if(tmpl) moveable = *((int*)tmpl);
+		return true;
+	case CMD_SHIFT_OUT:
+		if(tmpl) shift = *((double*)tmpl);
+		return true;
+	case CMD_MOVE:
+		bModified = true;
+		Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
+	case CMD_UNDO_MOVE:
+		fCent.fx += ((lfPOINT*)tmpl)[0].fx;		fCent.fy += ((lfPOINT*)tmpl)[0].fy;
+		if(parent)parent->Command(CMD_REDRAW, 0L, o);
+		return true;
+		}
+	return false;
+}
+
+void *
+segment::ObjThere(int x, int y)
+{
+	bool bFound = false;
+	POINT p1;
+
+	if(IsInRect(&rDims, p1.x = x, p1.y = y)) {
+		bFound = IsInPolygon(&p1, pts, nPts);
+		if(bFound || IsCloseToPL(p1, pts, nPts)) return this;
+		}
+	return 0L;
+}
+
+void
+segment::Track(POINT *p, anyOutput *o)
+{
+	POINT *tpts;
+	RECT old_rc;
+	int i;
+
+	if(o && (tpts = (POINT*)malloc(nPts*sizeof(POINT)))){
+		memcpy(&old_rc, &rDims, sizeof(rDims));
+		o->UpdateRect(&rDims, false);
+		for(i = 0; i < nPts; i++) {
+			tpts[i].x = pts[i].x+p->x;			tpts[i].y = pts[i].y+p->y;
+			UpdateMinMaxRect(&rDims, tpts[i].x, tpts[i].y);
+			}
+		o->ShowLine(tpts, nPts, segLine.color);
+		if(old_rc.left != rDims.left || old_rc.right != rDims.right || old_rc.top !=
+			rDims.top || old_rc.bottom != rDims.bottom)IncrementMinMaxRect(&rDims, 3);
+		free(tpts);
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// the polyline object
+//
+polyline::polyline(GraphObj *par, DataObj *d, lfPOINT *fpts, int cpts):
+	GraphObj(par, d)
+{
+	double dx = 0.0, dy = 0.0;
+	int i;
+
+	FileIO(INIT_VARS);
+	if(parent){
+		dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
+		}
+	if(cpts && (Values = (lfPOINT*)malloc((nPoints = cpts)* sizeof(lfPOINT)))){
+		memcpy(Values, fpts, cpts*sizeof(lfPOINT));
+		for(i = 0; i < cpts; i++) {
+			Values[i].fx -= dx;		Values[i].fy -= dy;
+			}
+		}
+	Id = GO_POLYLINE;
+	bModified = false;
+}
+
+polyline::polyline(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	bModified = false;
+}
+
+polyline::~polyline()
+{
+	Command(CMD_FLUSH, 0L, 0L);
+	if(this == CurrGO) CurrGO = 0L;
+	if(bModified) Undo.InvalidGO(this);
+}
+
+double
+polyline::GetSize(int select)
+{
+	int i;
+
+	if(select >= SIZE_XPOS && select <=  SIZE_XPOS_LAST){
+		if((i = select-SIZE_XPOS) >=0 && i <= 200)
+			return i < nPoints ? Values[i].fx : Values[nPoints-2].fx;
+		}
+	if(select >= SIZE_YPOS && select <= SIZE_YPOS_LAST){
+		if((i = select-SIZE_YPOS) >=0 && i <= 200)
+			return i < nPoints ? Values[i].fy : Values[nPoints-2].fy;
+		}
+	return parent ? parent->GetSize(select) : 0.0;
+}
+
+DWORD
+polyline::GetColor(int select)
+{
+	return pgLine.color;
+}
+
+bool
+polyline::SetSize(int select, double value)
+{
+	int i;
+
+	if((select & 0xfff) >= SIZE_XPOS && (select & 0xfff) <=  SIZE_XPOS_LAST){
+		if((i = select-SIZE_XPOS) >=0 && i <= 200 && i < (int)nPoints){
+			Values[i].fx = value;
+			return true;
+			}
+		}
+	if((select & 0xfff) >= SIZE_YPOS && (select & 0xfff) <= SIZE_YPOS_LAST){
+		if((i = select-SIZE_YPOS) >=0 && i <= 200 && i < (int)nPoints){
+			Values[i].fy = value;
+			return true;
+			}
+		}
+	return false;
+}
+
+void
+polyline::DoPlot(anyOutput *o)
+{
+	POINT np, *tmppts;
+	double dx, dy;
+	int i;
+
+	if(!Values || !nPoints || !o || !parent) return;
+	if(pts) free(pts);
+	dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
+	if(!(pts = (POINT*)malloc((nPoints+2)*sizeof(POINT))))return;
+	for(i = nPts = 0; i < nPoints; i++){
+		np.x = o->co2ix(Values[i].fx + dx);		np.y = o->co2iy(Values[i].fy + dy);
+		AddToPolygon(&nPts, pts, &np);
+		}
+	if(type == 1) AddToPolygon(&nPts, pts, &pts[0]);	//close polygon
+	if(tmppts = (POINT*)realloc(pts, nPts *sizeof(POINT))) pts = tmppts;
+	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
+	for(i = 2; i < nPts; i++) UpdateMinMaxRect(&rDims, pts[i].x, pts[i].y);
+	i = 3*o->un2ix(pgLine.width)+3;		//increase size of rectangle for marks
+	IncrementMinMaxRect(&rDims, i);
+	if(this == CurrGO) o->ShowMark(this, MRK_GODRAW);
+	else switch(type) {
+	case 0:				//line
+		o->SetLine(&pgLine);							o->oPolyline(pts, nPts);
+		break;
+	case 1:				//polygon
+		o->SetLine(&pgLine);	o->SetFill(&pgFill);	o->oPolygon(pts, nPts);
+		break;
+		}
+}
+
+void
+polyline::DoMark(anyOutput *o, bool mark)
+{
+	RECT upd;
+
+	memcpy(&upd, &rDims, sizeof(RECT));
+	IncrementMinMaxRect(&upd, 6 + o->un2ix(pgLine.width)*4);
+	if(mark) {
+		o->SetLine(&pgLine);
+		if(nPoints < 200){
+			switch(type) {
+			case 0:				//line
+				o->oPolyline(pts, nPts);
+			break;
+			case 1:				//polygon
+				o->SetFill(&pgFill);		o->oPolygon(pts, nPts);
+				break;
+				}
+			ShowPoints(o);
+			}
+		else InvertLine(pts, nPts, &pgLine, &upd, o, true);;
+		o->UpdateRect(&upd, false);
+ 		}
+	else {
+		if(parent)	parent->DoPlot(o);
+		}
+}
+
+bool
+polyline::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	POINT p1;
+	bool bFound = false;
+	int i;
+
+	switch (cmd) {
+	case CMD_MRK_DIRTY:			//issued by Undo
+		CurrGO = this;
+		bModified = true;
+	case CMD_FLUSH:
+		if(pHandles) {
+			for(i = 0; i < nPoints; i++) if(pHandles[i]) delete(pHandles[i]);
+			free(pHandles);		pHandles = 0L;
+			}
+		if(cmd == CMD_FLUSH && Values && nPoints){
+			free(Values);
+			Values = 0L;	nPoints = 0;
+			}
+		if(pts && nPts) free(pts);		pts = 0L;		nPts = 0;
+		return true;
+	case CMD_SCALE:
+		if(Values) for(i = 0; i < nPoints; i++){
+			Values[i].fx = ((scaleINFO*)tmpl)->sx.fx + Values[i].fx * ((scaleINFO*)tmpl)->sx.fy;
+			Values[i].fy = ((scaleINFO*)tmpl)->sy.fx + Values[i].fy * ((scaleINFO*)tmpl)->sy.fy;
+			}
+		pgLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;		pgLine.width *= ((scaleINFO*)tmpl)->sy.fy;
+		pgFillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;	pgFillLine.width *= ((scaleINFO*)tmpl)->sy.fy;
+		pgFill.scale *= ((scaleINFO*)tmpl)->sy.fy;
+		return true;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(!ObjThere(p1.x= mev->x, p1.y=mev->y)|| CurrGO || !o || nPoints <2)return false;
+			return o->ShowMark(CurrGO=this, MRK_GODRAW);
+			}
+		return false;
+	case CMD_DELOBJ:
+		if(pHandles && tmpl && tmpl == (void*)CurrHandle) {
+			for(i = 0; i < nPoints; i++) if(pHandles[i] == CurrHandle) {
+				Undo.DataMem(this, (void**)&Values, nPoints * sizeof(lfPOINT), &nPoints, 0L);
+				for( ; i < nPoints-1; i++) {
+					Values[i].fx = Values[i+1].fx;	Values[i].fy = Values[i+1].fy;
+					}
+				nPoints--;
+				if(pHandles[nPoints])delete(pHandles[nPoints]);
+				pHandles[nPoints] = 0L;				CurrHandle = 0L;
+				CurrGO = this;						bModified = true;
+				return true;
+				}
+			}
+		return false;
+	case CMD_SAVEPOS:
+		if(tmpl && Values) {
+			bModified = true;
+			i = *(int*)tmpl;
+			if(i >= 0 && i < nPoints) Undo.SaveLFP(this, Values + i, 0L);
+			}
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = type == 1 ? GO_POLYGON : GO_POLYLINE;
+		return true;
+	case CMD_SETSCROLL:		case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		return false;
+	case CMD_MOVE:
+		bModified = true;
+		Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
+	case CMD_UNDO_MOVE:
+		for(i = 0; i < nPoints; i++) {
+			Values[i].fx += ((lfPOINT*)tmpl)[0].fx;
+			Values[i].fy += ((lfPOINT*)tmpl)[0].fy;
+			}
+		if(o) {
+			o->StartPage();		parent->DoPlot(o);		o->EndPage();
+			}
+		return true;
+		}
+	return false;
+}
+
+void * 
+polyline::ObjThere(int x, int y)
+{
+	bool bFound = false;
+	POINT p1;
+	int i;
+	void *ret;
+
+	if(IsInRect(&rDims, p1.x = x, p1.y = y)) {
+		if(CurrGO == this && pHandles) for(i = nPoints-1; i >= 0; i--) 
+			if((pHandles[i]) && (ret = pHandles[i]->ObjThere(x, y))) return ret;
+		if(type == 1) bFound = IsInPolygon(&p1, pts, nPts);
+		if(bFound || IsCloseToPL(p1,pts,nPts)) return this;
+		}
+	return 0L;
+}
+
+void
+polyline::Track(POINT *p, anyOutput *o)
+{
+	POINT *tpts;
+	RECT old_rc;
+	int i;
+
+	if(o && (tpts = (POINT*)malloc(nPts*sizeof(POINT)))){
+		memcpy(&old_rc, &rDims, sizeof(rDims));
+		o->UpdateRect(&rDims, false);
+		for(i = 0; i < nPts; i++) {
+			tpts[i].x = pts[i].x+p->x;
+			tpts[i].y = pts[i].y+p->y;
+			UpdateMinMaxRect(&rDims, tpts[i].x, tpts[i].y);
+			}
+		o->ShowLine(tpts, nPts, pgLine.color);
+		if(old_rc.left != rDims.left || old_rc.right != rDims.right || old_rc.top !=
+			rDims.top || old_rc.bottom != rDims.bottom)IncrementMinMaxRect(&rDims, 3);
+		free(tpts);
+		}
+}
+
+void
+polyline::ShowPoints(anyOutput *o)
+{
+	int i;
+	double dx, dy;
+	POINT hpts[3];
+	LineDEF gl = {0.0, 1.0, 0x00c0c0c0, 0};
+
+	if(nPoints >= 200 || !o) return;
+	if(!pHandles && (pHandles = (dragHandle**)calloc(nPoints+4, sizeof(dragHandle*)))){
+		for(i = 0; i < nPoints; i++) pHandles[i] = new dragHandle(this, DH_DATA+i);
+		}
+	if(!pHandles) return;
+	if(Id == GO_BEZIER && parent && nPoints > 3) {
+		dx = parent->GetSize(SIZE_GRECT_LEFT);			dy = parent->GetSize(SIZE_GRECT_TOP);
+		o->SetLine(&gl);
+		hpts[0].x = o->co2ix(Values[0].fx+dx);			hpts[0].y = o->co2iy(Values[0].fy+dy);
+		hpts[1].x = o->co2ix(Values[1].fx+dx);			hpts[1].y = o->co2iy(Values[1].fy+dy);
+		o->oPolyline(hpts, 2);
+		for(i = 3; i < (nPoints-2); i += 3) {
+			hpts[0].x = o->co2ix(Values[i-1].fx+dx);	hpts[0].y = o->co2iy(Values[i-1].fy+dy);
+			hpts[1].x = o->co2ix(Values[i].fx+dx);		hpts[1].y = o->co2iy(Values[i].fy+dy);
+			hpts[2].x = o->co2ix(Values[i+1].fx+dx);	hpts[2].y = o->co2iy(Values[i+1].fy+dy);
+			o->oPolyline(hpts, 3);
+			}
+		hpts[0].x = o->co2ix(Values[i-1].fx+dx);		hpts[0].y = o->co2iy(Values[i-1].fy+dy);
+		hpts[1].x = o->co2ix(Values[i].fx+dx);			hpts[1].y = o->co2iy(Values[i].fy+dy);
+		o->oPolyline(hpts, 2);
+		}
+	for(i = 0; i < nPoints; i++) if(pHandles[i]) pHandles[i]->DoPlot(o);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Beziers are based on the polyline object
+Bezier::Bezier(GraphObj *par, DataObj *d, lfPOINT *fpts, int cpts, int mode, double res):
+	polyline(par, d, 0L, 0)
+{
+	double dx, dy, merr;
+	int i;
+
+	type = mode;
+	Id = GO_BEZIER;
+
+	if(!parent) return;
+	dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
+	if(type == 0 && (Values = (lfPOINT*)malloc(4 * cpts * sizeof(lfPOINT)))) {
+		merr = 0.01 * res / Units[defs.cUnits].convert;
+		FitCurve(fpts, cpts, merr);
+		Values[nPoints].fx = fpts[cpts-1].fx;	Values[nPoints].fy = fpts[cpts-1].fy;
+		for(i = 0; i < nPoints; i++) {
+			Values[i].fx -= dx;		Values[i].fy -= dy;
+			}
+		nPoints ++;
+		}
+	return;
+}
+
+Bezier::Bezier(int src):polyline(0L, 0L, 0L, 0)
+{
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+void
+Bezier::DoPlot(anyOutput *o)
+{
+	POINT *tmppts;
+	double dx, dy;
+	int i;
+
+	if(!Values || !nPoints || !o || !parent) return;
+	if(pts) free(pts);		pts = 0L;
+	dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
+	if(!(tmppts = (POINT*)malloc((nPoints+2)*sizeof(POINT))))return;
+	for(i = 0; i < nPoints; i++){
+		tmppts[i].x = o->co2ix(Values[i].fx + dx);	tmppts[i].y = o->co2iy(Values[i].fy + dy);
+		}
+	rDims.left = rDims.right = tmppts[0].x;		rDims.top = rDims.bottom = tmppts[0].y;
+	for(i = 1; i < nPoints; i++) {
+		if(tmppts[i].x < rDims.left) rDims.left = tmppts[i].x;
+		else if(tmppts[i].x > rDims.right) rDims.right = tmppts[i].x;
+		if(tmppts[i].y < rDims.top) rDims.top = tmppts[i].y;
+		else if(tmppts[i].y > rDims.bottom) rDims.bottom = tmppts[i].y;
+		}
+	//DrawBezier returns not more than 2^MAXDEPTH points
+	pts = (POINT*)malloc(nPoints * 64 * sizeof(POINT));
+	for(i= nPts = 0; i< (nPoints-2); i += 3) {
+		DrawBezier(&nPts, pts, tmppts[i], tmppts[i+1], tmppts[i+2], tmppts[i+3], 0);
+		}
+	IncrementMinMaxRect(&rDims, 3);
+	o->ShowLine(pts, nPts, 0x00c0c0c0L);
+	if(this == CurrGO) o->ShowMark(this, MRK_GODRAW);
+	else {
+		o->SetLine(&pgLine);			o->oPolyline(pts, nPts);
+		}
+}
+
+bool
+Bezier::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i, i1, i2;
+
+	switch (cmd) {
+	case CMD_MRK_DIRTY:			//issued by Undo
+		CurrGO = this;
+		bModified = true;
+	case CMD_FLUSH:
+		if(pHandles) {
+			for(i = 0; i < nPoints; i++) if(pHandles[i]) delete(pHandles[i]);
+			free(pHandles);		pHandles = 0L;
+			}
+		if(cmd == CMD_FLUSH && Values && nPoints){
+			free(Values);
+			Values = 0L;	nPoints = 0;
+			}
+		if(pts && nPts) free(pts);		pts = 0L;		nPts = 0;
+		return true;
+	case CMD_DELOBJ:
+		if(pHandles && tmpl && tmpl == (void*)CurrHandle) {
+			i = CurrHandle->type - DH_DATA;
+			if (i >= 0 && i < nPoints) {
+				i = CurrHandle->type - DH_DATA;
+				Undo.DataMem(this, (void**)&Values, nPoints * sizeof(lfPOINT), &nPoints, 0L);
+				if (i < 2) {
+					i1 = 0;					i2 = 3;
+					}
+				else if (i > (nPoints-3)) {
+					i1 = nPoints -3;		i2 = nPoints;
+					}
+				else {
+					i -= (((i-2) % 3)-1);	i1 = i -1;		i2 = i +2;
+					RemovePoint(Values, i1+1);
+					Values[i2].fx = Values[i1].fx;	Values[i2].fy = Values[i1].fy;
+					}
+				for(i = 0; i < nPoints; i++) if(pHandles[i]) delete(pHandles[i]);
+				free(pHandles);		pHandles = 0L;		CurrHandle = 0L;
+				i = i2 - i1;
+				for( ; i2 < nPoints; i1++, i2++) {
+					Values[i1].fx = Values[i2].fx;	Values[i1].fy = Values[i2].fy;
+					}
+				nPoints -= i;	CurrGO = this;		bModified = true;
+				return true;
+				}
+			}
+		return false;
+	case CMD_SET_DATAOBJ:
+		Id = GO_BEZIER;
+		return true;
+		}
+	return polyline::Command(cmd, tmpl, o);
+}
+
+void
+Bezier::AddPoints(int n, lfPOINT *p)
+{
+	int i;
+
+	for(i = 0; i< n; i++) {
+		Values[nPoints].fx = p[i].fx;	Values[nPoints].fy = p[i].fy;
+		nPoints ++;
+		}
+}
+
+//Fitting of a Bezier curve to digitized points is based on:
+//   P.J. Schneider (1990) An Algorithm for Automatically Fitting Digitized
+//   Curves. In: Graphics Gems (Ed. A. Glassner, Academic Press Inc., 
+//   ISBN 0-12-286165-5) pp. 612ff
+void
+Bezier::FitCurve(lfPOINT *d, int npt, double error)
+{
+	double len;
+	lfPOINT tHat1, tHat2;
+
+	tHat1.fx = d[1].fx - d[0].fx;		tHat1.fy = d[1].fy - d[0].fy;
+	if(0.0 != (len = sqrt((tHat1.fx * tHat1.fx) + (tHat1.fy * tHat1.fy)))) {
+		tHat1.fx /= len;					tHat1.fy /= len;
+		}
+	tHat2.fx = d[npt-2].fx - d[npt-1].fx;		tHat2.fy = d[npt-2].fy - d[npt-1].fy;
+	if(0.0 != (len = sqrt((tHat2.fx * tHat2.fx) + (tHat2.fy * tHat2.fy)))) {
+		tHat2.fx /= len;					tHat2.fy /= len;
+		}
+	FitCubic(d, 0, npt -1, tHat1, tHat2, error);
+}
+
+void
+Bezier::RemovePoint(lfPOINT *d, int sel)
+{
+	lfPOINT tHat1, tHat2;
+
+	tHat1.fx = d[sel-2].fx - d[sel-3].fx;	tHat1.fy = d[sel-2].fy - d[sel-3].fy;
+	tHat2.fx = d[sel+2].fx - d[sel+3].fx;	tHat2.fy = d[sel+2].fy - d[sel+3].fy;
+	d[sel] = d[sel+3];
+	IpolBez(d+sel-3, tHat1, tHat2);
+}
+
+//heuristic interpolation: other methods failed
+void
+Bezier::IpolBez(lfPOINT *d, lfPOINT tHat1, lfPOINT tHat2)
+{
+	double b0, b1, b2;						//temp variables
+
+	b1 = d[3].fx - d[0].fx;					b2 = d[3].fy - d[0].fy;
+	b0 = sqrt(b1 * b1 + b2 * b2)/3.0;
+	b1 = b0/sqrt(tHat1.fx * tHat1.fx + tHat1.fy * tHat1.fy);
+	b2 = b0/sqrt(tHat2.fx * tHat2.fx + tHat2.fy * tHat2.fy);
+	d[1].fx = d[0].fx + tHat1.fx * b1;		d[1].fy = d[0].fy + tHat1.fy * b1;
+	d[2].fx = d[3].fx + tHat2.fx * b2;		d[2].fy = d[3].fy + tHat2.fy * b2;
+}
+
+//Fit a Bezier curve to a (sub)set of digitized points
+void
+Bezier::FitCubic(lfPOINT *d, int first, int last, lfPOINT tHat1, lfPOINT tHat2, double error)
+{
+	lfPOINT bezCurve[4];
+	double *u, *uPrime, maxError, iterationError, len;
+	int i, splitPoint, npt;
+	lfPOINT tHatCenter;
+	int maxIterations = 8;
+
+	iterationError = error * error;
+	npt = last - first +1;
+	if(npt == 2) {
+		bezCurve[0] = d[first];					bezCurve[3] = d[last];
+		IpolBez(bezCurve, tHat1, tHat2);
+		AddPoints(3, bezCurve);
+		return;
+		}
+	u = ChordLengthParameterize(d, first, last);
+	GenerateBezier(d, first, last, u, tHat1, tHat2, bezCurve);
+	maxError = ComputeMaxError(d, first, last, bezCurve, u, &splitPoint);
+	if(maxError < error) {
+		if(maxError < 0.0) {					//Failure fitting curve
+			IpolBez(bezCurve, tHat1, tHat2);
+			}
+		AddPoints(3, bezCurve);					return;
+		}
+	if(maxError < iterationError) {
+		for (i = 0; i < maxIterations; i++) {
+			uPrime = Reparameterize(d, first, last, u, bezCurve);
+			GenerateBezier(d, first, last, uPrime, tHat1, tHat2, bezCurve);
+			maxError = ComputeMaxError(d, first, last, bezCurve, uPrime, &splitPoint);
+			if (maxError < error) {
+				if(maxError < 0.0) IpolBez(bezCurve, tHat1, tHat2);
+				AddPoints(3, bezCurve);			return;
+				}
+			free(u);							u = uPrime;
+			}
+		}
+	//Fitting failed: split at max error point and recurse
+	tHatCenter.fx = d[splitPoint-1].fx - d[splitPoint+1].fx;
+	tHatCenter.fy = d[splitPoint-1].fy - d[splitPoint+1].fy;
+	if(0.0 != (len = sqrt((tHatCenter.fx * tHatCenter.fx) + (tHatCenter.fy * tHatCenter.fy)))) {
+		tHatCenter.fx /= len;					tHatCenter.fy /= len;
+		}
+	FitCubic(d, first, splitPoint, tHat1, tHatCenter, error * _SQRT2);
+	tHatCenter.fx = -tHatCenter.fx;				tHatCenter.fy = -tHatCenter.fy;
+	FitCubic(d, splitPoint, last, tHatCenter, tHat2, error * _SQRT2);
+}
+
+//Use least-squares method to find Bezier control points for region.
+void
+Bezier::GenerateBezier(lfPOINT *d, int first, int last, double *uPrime, 
+	lfPOINT tHat1, lfPOINT tHat2, lfPOINT *bezCurve)
+{
+	int i, npt;
+	lfPOINT *A0, *A1, tmp, v1, v2;
+	double C[2][2], X[2];
+	double det_C0_C1, det_C0_X, det_X_C1, alpha_l, alpha_r;
+	double b0, b1, b2, b3;		//temp variables
+
+	npt = last - first +1;
+	A0 = (lfPOINT*) malloc(npt * sizeof(lfPOINT));
+	A1 = (lfPOINT*) malloc(npt * sizeof(lfPOINT));
+	for (i = 0; i < npt; i++) {
+		v1 = tHat1;								v2 = tHat2;
+		b2 = 1.0 - uPrime[i];
+		b1 = 3.0 * uPrime[i] * b2 * b2;			b2 = 3.0 * uPrime[i] * uPrime[i] * b2;
+		if(0.0 != (b0 = sqrt((v1.fx * v1.fx) + (v1.fy * v1.fy)))) {
+			v1.fx *= b1/b0;						v1.fy *= b1/b0;
+			}
+		if(0.0 != (b0 = sqrt((v2.fx * v2.fx) + (v2.fy * v2.fy)))) {
+			v2.fx *= b2/b0;						v2.fy *= b2/b0;
+			}
+		A0[i] = v1;								A1[i] = v2;
+		}
+	C[0][0] = C[0][1] = C[1][0] = C[1][1] = X[0] = X[1] = 0.0;
+	for (i = 0; i < npt; i++) {
+		C[0][0] += ((A0[i].fx * A0[i].fx) + (A0[i].fy * A0[i].fy));
+		C[0][1] += ((A0[i].fx * A1[i].fx) + (A0[i].fy * A1[i].fy));
+		C[1][0] = C[0][1];
+		C[1][1] += ((A1[i].fx * A1[i].fx) + (A1[i].fy * A1[i].fy));
+		b2 = 1.0 - uPrime[i];					b0 = b2 * b2 * b2;
+		b1 = 3.0 * uPrime[i] * b2 * b2;			b2 = 3.0 * uPrime[i] * uPrime[i] * b2;
+		b3 = uPrime[i] * uPrime[i] * uPrime[i];
+		tmp.fx = d[last].fx * b2 + d[last].fx * b3;
+		tmp.fy = d[last].fy * b2 + d[last].fy * b3;
+		tmp.fx += d[first].fx * b1;				tmp.fy += d[first].fy * b1;
+		tmp.fx += d[first].fx * b0;				tmp.fy += d[first].fy * b0;
+		tmp.fx = d[first+i].fx - tmp.fx;		tmp.fy = d[first+i].fy - tmp.fy;
+		X[0] += (A0[i].fx * tmp.fx + A0[i].fy * tmp.fy);
+		X[1] += (A1[i].fx * tmp.fx + A1[i].fy * tmp.fy);
+		}
+	det_C0_C1 = C[0][0] * C[1][1] - C[1][0] * C[0][1];
+	det_C0_X = C[0][0] * X[1] - C[0][1] * X[0];
+	det_X_C1 = X[0] * C[1][1] - X[1] * C[0][1];
+	if(det_C0_C1 == 0.0) det_C0_C1 = (C[0][0] * C[1][1]) * 10e-12;
+	alpha_l = det_X_C1 / det_C0_C1;				alpha_r = det_C0_X / det_C0_C1;
+	bezCurve[0] = d[first];						bezCurve[3] = d[last];
+	if(alpha_l < 0.0 || alpha_r < 0.0) {
+		//use Wu/Barsky heuristic
+		b1 = d[last].fx - d[first].fx;			b2 = d[last].fy - d[first].fy;
+		b0 = sqrt(b1 * b1 + b2 * b2)/3.0;
+		b1 = b0/sqrt(tHat1.fx * tHat1.fx + tHat1.fy * tHat1.fy);
+		b2 = b0/sqrt(tHat2.fx * tHat2.fx + tHat2.fy * tHat2.fy);
+		}
+	else {
+		b1 = alpha_l/sqrt(tHat1.fx * tHat1.fx + tHat1.fy * tHat1.fy);
+		b2 = alpha_r/sqrt(tHat2.fx * tHat2.fx + tHat2.fy * tHat2.fy);
+		}
+	bezCurve[1].fx = bezCurve[0].fx + tHat1.fx * b1;
+	bezCurve[1].fy = bezCurve[0].fy + tHat1.fy * b1;
+	bezCurve[2].fx = bezCurve[3].fx + tHat2.fx * b2;
+	bezCurve[2].fy = bezCurve[3].fy + tHat2.fy * b2;
+	free(A0);									free(A1);
+}
+
+//Given set of points and their parameterization, try to find
+//  a better parameterization.
+double *
+Bezier::Reparameterize(lfPOINT *d, int first, int last, double *u, lfPOINT *bezCurve)
+{
+	int i, j, k, npt = last-first+1;
+	double *uPrime, num, den, *pl, *ph, *pq;
+	lfPOINT Q1[3], Q2[2], Q_u, Q1_u, Q2_u;
+
+	uPrime = (double*)malloc(npt * sizeof(double));
+	//Use Newton-Raphson iteration to find better root
+	for (i = first, j = 0; i <= last; i++, j++) {
+		for(pl=(double*)bezCurve, ph=pl+2, pq=(double*)Q1, k=0; k <= 4; pl++, ph++, pq++, k++) {
+			*pq = (*ph - *pl ) * 3.0;
+			}
+		for(pl=(double*)Q1, ph=pl+2, pq=(double*)Q2, k=0; k <= 2; pl++, ph++, pq++, k++) {
+			*pq = (*ph - *pl ) * 2.0;
+			}
+		Q_u = fBezier(3, bezCurve, u[j]);
+		Q1_u = fBezier(2, Q1, u[j]);	
+		Q2_u = fBezier(1, Q2, u[j]);
+		num = (Q_u.fx - d[i].fx) * (Q1_u.fx) + (Q_u.fy - d[i].fy) * (Q1_u.fy);
+		den = (Q1_u.fx) * (Q1_u.fx) + (Q1_u.fy) * (Q1_u.fy) + 
+			(Q_u.fx - d[i].fx) * (Q2_u.fx) + (Q_u.fy - d[i].fy) * (Q2_u.fy);
+		uPrime[j] = u[j] - (num/den);
+		}
+	return uPrime;
+}
+
+//evaluate a Bezier curve at a particular parameter value
+lfPOINT
+Bezier::fBezier(int degree, lfPOINT *V, double t)
+{
+	int i, j;
+	lfPOINT Q;
+	lfPOINT *Vtemp;
+
+	Vtemp = (lfPOINT *)malloc((degree+1) * sizeof(lfPOINT));
+	for (i = 0; i <= degree; i++) {
+		Vtemp[i] = V[i];
+		}
+	for (i = 1; i <= degree; i++) {
+		for (j = 0; j <= degree-i; j++) {
+			Vtemp[j].fx = (1.0 -t) * Vtemp[j].fx + t * Vtemp[j+1].fx;
+			Vtemp[j].fy = (1.0 -t) * Vtemp[j].fy + t * Vtemp[j+1].fy;
+			}
+		}
+	Q = Vtemp[0];
+	free(Vtemp);
+	return Q;
+}
+
+double *
+Bezier::ChordLengthParameterize(lfPOINT *d, int first, int last)
+{ 
+	int i;
+	double tmp, *u;
+
+	u = (double*)malloc((last-first+1) * sizeof(double));
+	u[0] = 0.0;
+	for(i = first+1; i <= last; i++) {
+		u[i-first] = u[i-first-1] + 
+			sqrt(((tmp=(d[i].fx - d[i-1].fx))*tmp) + ((tmp=(d[i].fy - d[i-1].fy))*tmp));
+		}
+	for(i = first +1; i <= last; i++) {
+		u[i-first] = u[i-first] / u[last-first];
+		}
+	return u;
+}
+
+double
+Bezier::ComputeMaxError(lfPOINT *d, int first, int last, lfPOINT *bezCurve, double *u, int *splitPoint)
+{
+	int i;
+	double maxDist, dist;
+	lfPOINT P, v;
+
+	*splitPoint = (last - first + 1)>>1;
+	maxDist = -1.0;
+	for(i = first +1; i < last; i++) {
+		P = fBezier(3, bezCurve, u[i-first]);
+		v.fx = P.fx - d[i].fx;			v.fy = P.fy - d[i].fy;
+		dist = v.fx * v.fx + v.fy * v.fy;
+		if(dist >= maxDist) {
+			maxDist = dist;		*splitPoint = i;
+			}
+		}
+	return maxDist;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// polygons are based on the polyline object
+polygon::polygon(GraphObj *par, DataObj *d, lfPOINT *fpts, int cpts):
+	polyline(par, d, fpts, cpts)
+{
+	Id = GO_POLYGON;
+	memcpy(&pgLine, defs.pgLineDEF(0L), sizeof(LineDEF));
+	type = 1;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// rectangle with absolute coordinates
+rectangle::rectangle(GraphObj *par, DataObj *d, lfPOINT *p1, lfPOINT *p2):
+	GraphObj(par, d)
+{
+	double dx = 0.0, dy = 0.0;
+
+	FileIO(INIT_VARS);
+	if(parent){
+		dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
+		}
+	memcpy(&fp1, p1, sizeof(lfPOINT));	memcpy(&fp2, p2, sizeof(lfPOINT));
+	fp1.fx -= dx;	fp1.fy -= dy;		fp2.fx -= dx;	fp2.fy -= dy;
+	type = 0;
+	Id = GO_RECTANGLE;
+	bModified = false;
+}
+
+rectangle::rectangle(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	type = 0;
+	bModified = false;
+}
+
+rectangle::~rectangle()
+{
+	Command(CMD_FLUSH, 0L, 0L);
+	if(bModified) Undo.InvalidGO(this);
+	if(drc) delete(drc);					drc = 0L;
+}
+
+double
+rectangle::GetSize(int select)
+{
+	if(parent && parent->Id== GO_GROUP){
+	switch(select) {
+		case SIZE_XPOS:		return fp1.fx + parent->GetSize(SIZE_XPOS);
+		case SIZE_XPOS+1:	return fp2.fx + parent->GetSize(SIZE_XPOS);
+		case SIZE_YPOS:		return fp1.fy + parent->GetSize(SIZE_YPOS);
+		case SIZE_YPOS+1:	return fp2.fy + parent->GetSize(SIZE_YPOS);
+		case SIZE_GRECT_LEFT:	case SIZE_GRECT_TOP:
+		case SIZE_GRECT_RIGHT:	case SIZE_GRECT_BOTTOM:
+			return parent->GetSize(select);
+			}
+		return 0.0;
+		}
+	switch(select) {
+	case SIZE_XPOS:		return fp1.fx;
+	case SIZE_XPOS+1:	return fp2.fx;
+	case SIZE_YPOS:		return fp1.fy;
+	case SIZE_YPOS+1:	return fp2.fy;
+	case SIZE_GRECT_LEFT:	case SIZE_GRECT_TOP:
+	case SIZE_GRECT_RIGHT:	case SIZE_GRECT_BOTTOM:
+		if(parent) return parent->GetSize(select);
+		break;
+		}
+	return 0.0;
+}
+
+bool
+rectangle::SetSize(int select, double value)
+{
+	switch(select & 0xfff) {
+	case SIZE_XPOS:				fp1.fx = value;			return true;
+	case SIZE_XPOS+1:			fp2.fx = value;			return true;
+	case SIZE_YPOS:				fp1.fy = value;			return true;
+	case SIZE_YPOS+1:			fp2.fy = value;			return true;
+		}
+	return false;
+}
+
+void 
+rectangle::DoMark(anyOutput *o, bool mark)
+{
+	RECT upd;
+
+	if(!drc) drc = new dragRect(this, 0);
+	memcpy(&upd, &rDims, sizeof(RECT));
+	if(mark){
+		if(drc) drc->DoPlot(o);
+		}
+	else if(parent)	parent->DoPlot(o);
+	IncrementMinMaxRect(&upd, 6);
+	o->UpdateRect(&upd, false);
+}
+
+void
+rectangle::DoPlot(anyOutput *o)
+{
+	int x1, y1, x2, y2;
+	double tmp, dx, dy;
+
+	if(!parent || !o) return;
+	dx = parent->GetSize(SIZE_GRECT_LEFT);		dy = parent->GetSize(SIZE_GRECT_TOP);
+	if(fp1.fx > fp2.fx) {
+		tmp = fp2.fx;	fp2.fx = fp1.fx;	fp1.fx = tmp;
+		}
+	if(fp1.fy > fp2.fy) {
+		tmp = fp2.fy;	fp2.fy = fp1.fy;	fp1.fy = tmp;
+		}
+	if(type == 2) PlotRoundRect(o);
+	else {
+		x1 = o->co2ix(fp1.fx+dx);			y1 = o->co2iy(fp1.fy+dy);
+		x2 = o->co2ix(fp2.fx+dx);			y2 = o->co2iy(fp2.fy+dy);
+		o->SetLine(&Line);				o->SetFill(&Fill);
+		if(type == 1) o->oCircle(x1, y1, x2, y2, name);
+		else o->oRectangle(x1, y1, x2, y2, name);
+		SetMinMaxRect(&rDims, x1, y1, x2, y2);
+		}
+	o->MrkMode = MRK_NONE;
+	if(CurrGO == this) o->ShowMark(this, MRK_GODRAW);
+}
+
+bool
+rectangle::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+
+	switch (cmd) {
+	case CMD_FLUSH:
+		if(pts) free(pts);		pts = 0L;
+		if(name) free(name);	name = 0L;
+		return true;
+	case CMD_SCALE:
+		fp1.fx = ((scaleINFO*)tmpl)->sx.fx + fp1.fx * ((scaleINFO*)tmpl)->sx.fy;
+		fp2.fx = ((scaleINFO*)tmpl)->sx.fx + fp2.fx * ((scaleINFO*)tmpl)->sx.fy;
+		fp1.fy = ((scaleINFO*)tmpl)->sy.fx + fp1.fy * ((scaleINFO*)tmpl)->sy.fy;
+		fp2.fy = ((scaleINFO*)tmpl)->sy.fx + fp2.fy * ((scaleINFO*)tmpl)->sy.fy;
+		Line.patlength *= ((scaleINFO*)tmpl)->sy.fy;		Line.width *= ((scaleINFO*)tmpl)->sy.fy;
+		FillLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;	FillLine.width *= ((scaleINFO*)tmpl)->sy.fy;
+		Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;			rad *= ((scaleINFO*)tmpl)->sy.fy;
+		return true;
+	case CMD_SAVEPOS:
+		bModified = true;
+		Undo.SaveLFP(this, &fp1, 0L);
+		Undo.SaveLFP(this, &fp2, UNDO_CONTINUE);
+		return true;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, mev->x, mev->y) && !(CurrGO) && (o)){
+				return o->ShowMark(this, MRK_GODRAW);
+				}
+			}
+		return false;
+	case CMD_SET_DATAOBJ:
+		switch (type) {
+		case 1:		Id = GO_ELLIPSE;		break;
+		case 2:		Id = GO_ROUNDREC;		break;
+		default:	Id = GO_RECTANGLE;		break;
+			}
+		return true;
+	case CMD_MOVE:
+		bModified = true;
+		Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
+	case CMD_UNDO_MOVE:
+		fp1.fx += ((lfPOINT*)tmpl)[0].fx;	fp1.fy += ((lfPOINT*)tmpl)[0].fy;
+		fp2.fx += ((lfPOINT*)tmpl)[0].fx;	fp2.fy += ((lfPOINT*)tmpl)[0].fy;
+		CurrGO = this;
+	case CMD_REDRAW:
+		if(parent && cmd != CMD_UNDO_MOVE){
+			parent->Command(CMD_REDRAW, tmpl, o);
+			}
+		return true;
+	case CMD_SETSCROLL:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		}
+	return false;
+}
+
+void
+rectangle::Track(POINT *p, anyOutput *o)
+{
+	POINT tpts[5];
+	RECT old_rc;
+	double dx, dy;
+
+	if(o && parent){
+		dx = parent->GetSize(SIZE_GRECT_LEFT);		dy = parent->GetSize(SIZE_GRECT_TOP);
+		memcpy(&old_rc, &rDims, sizeof(rDims));
+		o->UpdateRect(&rDims, false);
+		tpts[0].x = tpts[1].x = tpts[4].x = o->co2ix(fp1.fx+dx)+p->x;		
+		tpts[0].y = tpts[3].y = tpts[4].y = o->co2iy(fp1.fy+dy)+p->y;
+		tpts[1].y = tpts[2].y = o->co2iy(fp2.fy+dy)+p->y;
+		tpts[2].x = tpts[3].x = o->co2ix(fp2.fx+dx)+p->x;
+		UpdateMinMaxRect(&rDims, tpts[0].x, tpts[0].y);
+		UpdateMinMaxRect(&rDims, tpts[2].x, tpts[2].y);	
+		if(old_rc.left != rDims.left || old_rc.right != rDims.right || old_rc.top !=
+			rDims.top || old_rc.bottom != rDims.bottom)IncrementMinMaxRect(&rDims, 3);
+		o->ShowLine(tpts, 5, Line.color);
+		if(type == 1) o->ShowEllipse(tpts[0], tpts[2], Line.color);
+		}
+}
+
+void *
+rectangle::ObjThere(int x, int y)
+{
+	if(drc) return drc->ObjThere(x, y);
+	return 0L;
+}
+
+//use circular Bresenham's algorithm to draw rounded rectangles
+//Ref: C. Montani, R. Scopigno (1990) "Speres-To-Voxel Conversion", in:
+//   Graphic Gems (A.S. Glassner ed.) Academic Press, Inc.; 
+//   ISBN 0-12-288165-5 
+void
+rectangle::PlotRoundRect(anyOutput *o)
+{
+	int i, m, x, y, di, de, lim, ir, x1, x2, y1, y2;
+	double dx, dy;
+	POINT np;
+
+	dx = parent->GetSize(SIZE_GRECT_LEFT);		dy = parent->GetSize(SIZE_GRECT_TOP);
+	ir = o->un2ix(rad);
+	x1 = o->co2ix(fp1.fx+dx);			y1 = o->co2iy(fp1.fy+dy);
+	x2 = o->co2ix(fp2.fx+dx);			y2 = o->co2iy(fp2.fy+dy);
+	if (x1 > x2) Swap(x1, x2);		if(y1 > y2) Swap(y1, y2);
+	if(pts) free(pts);				nPts = 0;
+	m = ir*4+10;
+	if(!(pts = (POINT*)malloc(m*sizeof(POINT))))return;
+	for(i = 0; i < 4; i++) {
+		x = lim = 0;	y = ir;	di = 2*(1-ir);
+		while (y >= lim){
+			if(di < 0) {
+				de = 2*di + 2*y -1;
+				if(de > 0) {
+					x++;	y--;	di += (2*x -2*y +2);
+					}
+				else {
+					x++;	di += (2*x +1);
+					}
+				}
+			else {
+				de = 2*di -2*x -1;
+				if(de > 0) {
+					y--;	di += (-2*y +1);
+					}
+				else {
+					x++;	y--;	di += (2*x -2*y +2);
+					}
+				}
+			switch(i) {
+			case 0:
+				np.x = x2-ir+x;		np.y = y2-ir+y;
+				break;
+			case 1:
+				np.x = x2-ir+y;		np.y = y1+ir-x;
+				break;
+			case 2: 
+				np.x = x1+ir-x;		np.y = y1+ir-y;
+				break;
+			case 3:
+				np.x = x1+ir-y;		np.y = y2-ir+x;
+				break;
+				}
+			AddToPolygon(&nPts, pts, &np);
+			}
+		}
+	AddToPolygon(&nPts, pts, pts);	//close polygon
+	o->SetLine(&Line);				o->SetFill(&Fill);
+	o->oPolygon(pts, nPts, name);
+	SetMinMaxRect(&rDims, x1, y1, x2, y2);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// ellipse with absolute coordinates
+ellipse::ellipse(GraphObj *par, DataObj *d, lfPOINT *p1, lfPOINT *p2)
+	:rectangle(par, d, p1, p2)
+{
+	type = 1;
+	Id = GO_ELLIPSE;
+}
+
+ellipse::ellipse(int src)
+	:rectangle(src)
+{
+	type = 1;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// rounded rectangle
+roundrec::roundrec(GraphObj *par, DataObj *d, lfPOINT *p1, lfPOINT *p2)
+	:rectangle(par, d, p1, p2)
+{
+	type = 2;
+	Id = GO_ROUNDREC;
+}
+
+roundrec::roundrec(int src)
+	:rectangle(src)
+{
+	type = 2;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Add a legend to the graph
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LegItem::LegItem(GraphObj *par, DataObj *d, LineDEF *ld, LineDEF *lf, FillDEF *fd, char *desc)
+	:GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	if(!ld && !fd && lf) ld = lf;
+	if(ld) {
+		memcpy(&DataLine, ld, sizeof(LineDEF));
+		flags |= 0x01;
+		}
+	if(lf) memcpy(&OutLine, lf, sizeof(LineDEF));
+	if(fd) {
+		if(fd->hatch) memcpy(&HatchLine, fd->hatch, sizeof(LineDEF));
+		memcpy(&Fill, fd, sizeof(FillDEF));
+		Fill.hatch = &HatchLine;
+		flags |= 0x02;
+		}
+	DefDesc(desc);		Id = GO_LEGITEM;		moveable = 1;
+}
+
+LegItem::LegItem(GraphObj *par, DataObj *d, LineDEF *ld, Symbol *sy)
+	:GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	if(ld) {
+		memcpy(&DataLine, ld, sizeof(LineDEF));		flags |= 0x01;
+		}
+	if(sy) {
+		Sym = sy;		Sym->parent = this;			flags |= 0x04;
+		}
+	DefDesc(sy ? sy->name : 0L);		Id = GO_LEGITEM;		moveable = 1;
+}
+
+LegItem::LegItem(GraphObj *par, DataObj *d, LineDEF *ld, int err, char *desc)
+	:GraphObj(par, d)
+{
+	FileIO(INIT_VARS);		flags |= (0x80 | (err & 0x7f));
+	if(ld) {
+		memcpy(&OutLine, ld, sizeof(LineDEF));
+		}
+	DefDesc(desc);		Id = GO_LEGITEM;		moveable = 1;
+}
+
+LegItem::LegItem(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	moveable = 1;
+}
+
+LegItem::~LegItem()
+{
+	if(Sym) DeleteGO(Sym);		Sym = 0L;
+	if(Desc) DeleteGO(Desc);	Desc = 0L;
+}
+
+double
+LegItem::GetSize(int select)
+{
+	switch(select) {
+	case SIZE_XCENTER:
+		return (parent->GetSize((flags & 0x01) ? SIZE_XPOS+2 : SIZE_XPOS) + 
+			parent->GetSize((flags & 0x01) ? SIZE_XPOS+3 : SIZE_XPOS+1))/2.0;
+	case SIZE_YCENTER:
+		return (parent->GetSize(SIZE_YPOS) + parent->GetSize(SIZE_YPOS+1))/2.0;
+	case SIZE_LB_XPOS:		case SIZE_LB_YPOS:
+	case SIZE_GRECT_TOP:	case SIZE_GRECT_LEFT:
+	default:
+		if(parent) return parent->GetSize(select);
+		break;
+		}
+	return 0.0;
+}
+
+void
+LegItem::DoPlot(anyOutput *o)
+{
+	POINT pts[3];
+	int ie, cy;
+
+	if(!parent || !o) return;
+	hcr.top = iround(parent->GetSize(SIZE_YPOS));
+	hcr.bottom = iround(parent->GetSize(SIZE_YPOS+1));
+	if(flags & 0x80) {
+		hcr.left = iround(parent->GetSize((flags & 0x40) ? SIZE_XPOS+2 : SIZE_XPOS));
+		hcr.right = iround(parent->GetSize((flags & 0x40) ? SIZE_XPOS+3 :SIZE_XPOS+1));
+		ie = o->un2ix(DefSize(SIZE_ERRBAR)/2.0);
+		o->SetLine(&OutLine);						cy = (hcr.top + hcr.bottom)>>1;
+		if((flags & 0x3f) == 0x01) {
+			pts[0].x = pts[1].x = (hcr.right + hcr.left)>>1;
+			pts[0].y = hcr.top;		pts[1].y = hcr.bottom;
+			o->oPolyline(pts, 2, 0L);		
+			pts[0].x -= (ie-1);		pts[1].x += ie;		pts[0].y = pts[1].y = hcr.top;
+			o->oPolyline(pts, 2, 0L);					pts[0].y = pts[1].y = hcr.bottom;
+			o->oPolyline(pts, 2, 0L);
+			}
+		else if((flags & 0x3f) == 0x02) {
+			pts[0].x = pts[1].x = ((hcr.right + hcr.left)>>1);
+			pts[0].y = pts[1].y = cy;			cy = ((hcr.bottom - hcr.top)>>1);
+			pts[0].x -= cy;						pts[2].x = (pts[1].x += cy);
+			o->oPolyline(pts, 2, 0L);			pts[1].x = pts[0].x;
+			pts[0].y -= ie;						pts[1].y += ie;
+			o->oPolyline(pts, 2, 0L);
+			pts[0].x = pts[1].x = pts[2].x;		o->oPolyline(pts, 2, 0L);
+			}
+		else if((flags & 0x3f) == 0x03) {
+			pts[0].x = pts[1].x = (hcr.right + hcr.left)>>1;
+			pts[0].y = hcr.top;		pts[1].y = hcr.bottom;
+			o->oPolyline(pts, 2, 0L);			pts[0].x -= (ie-1);
+			pts[0].y = pts[1].y = hcr.top;		o->oPolyline(pts, 2, 0L);
+			pts[0].y = pts[1].y = hcr.bottom;	pts[0].x += (ie + ie -1);
+			o->oPolyline(pts, 2, 0L);
+			}
+		else if((flags & 0x3f) == 0x04) {
+			pts[0].x = pts[1].x = (hcr.right + hcr.left)>>1;
+			pts[0].y = hcr.top;		pts[1].y = hcr.bottom;
+			o->oPolyline(pts, 2, 0L);			pts[0].x += ie;
+			pts[0].y = pts[1].y = hcr.top;		o->oPolyline(pts, 2, 0L);
+			pts[0].y = pts[1].y = hcr.bottom;	pts[0].x -= (ie + ie -1);
+			o->oPolyline(pts, 2, 0L);
+			}
+		else if((flags & 0x3f) == 0x05) {
+			pts[0].x = pts[1].x = (hcr.right + hcr.left)>>1;
+			pts[0].y = hcr.top;		pts[1].y = hcr.bottom;
+			o->oPolyline(pts, 2, 0L);
+			}
+		}
+	else {
+		hcr.left = iround(parent->GetSize((flags & 0x01) ? SIZE_XPOS+2 : SIZE_XPOS));
+		hcr.right = iround(parent->GetSize((flags & 0x01) ? SIZE_XPOS+3 :SIZE_XPOS+1));
+		if(flags & 0x02){
+			o->SetLine(&OutLine);	o->SetFill(&Fill);
+			o->oRectangle(hcr.left, hcr.top, hcr.right, hcr.bottom, name);
+			}
+		if(flags & 0x01){
+			pts[0].x = hcr.left;	pts[1].x = hcr.right;
+			pts[0].y = pts[1].y = iround(GetSize(SIZE_YCENTER))+1;
+			o->SetLine(&DataLine);	o->oPolyline(pts, 2, 0L);
+			}
+		if(flags & 0x04){
+			if(Sym) Sym->DoPlot(o);
+			}
+		}
+	SetMinMaxRect(&rDims, hcr.left, hcr.top, hcr.right, hcr.bottom);
+	if(Desc) {
+		Desc->moveable = 1;			Desc->DoPlot(o);
+		if(Desc->rDims.bottom > rDims.bottom){
+			parent->SetSize(SIZE_YPOS+1, (double)Desc->rDims.bottom);
+			}
+		UpdateMinMaxRect(&rDims, Desc->rDims.left, Desc->rDims.top);
+		UpdateMinMaxRect(&rDims, Desc->rDims.right, Desc->rDims.bottom);
+		}
+}
+
+void
+LegItem::DoMark(anyOutput *o, bool mark)
+{
+	RECT cr;
+	LineDEF ld = {0.0, 1.0, 0x00000000L, 0x00000000L};
+	POINT pts[5];
+
+	if(!parent || !o) return;
+	cr.left = hcr.left-5;			cr.right = hcr.right+3;
+	cr.top = hcr.top-3;				cr.bottom = hcr.bottom+1;
+	ld.color = mark ? 0x00000000L : 0x00ffffffL;	o->SetLine(&ld);
+	pts[0].x = pts[3].x = pts[4].x = cr.left;	pts[0].y = pts[1].y = pts[4].y = cr.top;
+	pts[1].x = pts[2].x = cr.right;				pts[2].y = pts[3].y = cr.bottom;
+	o->oPolyline(pts, 5, name);					IncrementMinMaxRect(&cr, 3);
+	o->UpdateRect(&cr, false);
+}
+
+bool
+LegItem::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	GraphObj **tmpPlots;
+
+	switch(cmd){
+	case CMD_TEXTTHERE:
+		if(Desc && Desc->Command(cmd, tmpl, o)) return true;
+		return false;
+	case CMD_MOUSE_EVENT:
+		if(tmpl && IsInRect(&rDims, ((MouseEvent*)tmpl)->x, ((MouseEvent*)tmpl)->y) && o) {
+			if(Desc && Desc->Command(cmd, tmpl, o)) return true;
+			if(!CurrGO) o->ShowMark(CurrGO=this, MRK_GODRAW);
+			}
+		break;
+	case CMD_SCALE:
+		DataLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;		DataLine.width *= ((scaleINFO*)tmpl)->sy.fy;
+		OutLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;			OutLine.width *= ((scaleINFO*)tmpl)->sy.fy;
+		HatchLine.patlength *= ((scaleINFO*)tmpl)->sy.fy;		HatchLine.width *= ((scaleINFO*)tmpl)->sy.fy;
+		Fill.scale *= ((scaleINFO*)tmpl)->sy.fy;	
+		if(Sym) Sym->Command(cmd, tmpl, o);
+		if(Desc) Desc->Command(cmd, tmpl, o);
+		break;
+	case CMD_REDRAW:	case CMD_MOVE:
+		if(parent) return parent->Command(cmd, tmpl, o);
+		break;
+	case CMD_MUTATE:
+		if(!parent || !(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
+		if(Desc == tmpPlots[0]) {
+			Undo.MutateGO((GraphObj**)&Desc, tmpPlots[1], 0L, o);
+			return true;
+			}
+		break;
+	case CMD_SET_DATAOBJ:
+		if(Desc) Desc->Command(cmd, tmpl, o);
+		Id = GO_LEGITEM;
+		data = (DataObj *)tmpl;
+		return true;
+		}
+	return false;
+}
+
+void
+LegItem::Track(POINT *p, anyOutput *o)
+{
+	if(parent) parent->Track(p, o);
+}
+
+bool
+LegItem::HasFill(LineDEF *ld, FillDEF *fd, char *desc)
+{
+	if(ld && cmpLineDEF(ld, &OutLine)) return false;
+	if(fd && cmpFillDEF(fd, &Fill)) return false;
+	if(fd && fd->hatch && cmpLineDEF(fd->hatch, &HatchLine)) return false;
+	return true;
+}
+
+bool
+LegItem::HasSym(LineDEF *ld, GraphObj *sy)
+{
+	if(sy && !Sym) return false;
+	if(sy->Id == GO_SYMBOL && (sy->type & 0xfff) != (Sym->type & 0xfff)) return false;
+	if(sy && Sym) {
+		if(Sym->GetSize(SIZE_SYMBOL) != sy->GetSize(SIZE_SYMBOL)) return false;
+		if(Sym->GetSize(SIZE_SYM_LINE) != sy->GetSize(SIZE_SYM_LINE)) return false;
+		if(Sym->GetColor(COL_SYM_LINE) != sy->GetColor(COL_SYM_LINE)) return false;
+		if(Sym->GetColor(COL_SYM_FILL) != sy->GetColor(COL_SYM_FILL)) return false;
+		}
+	if(ld && cmpLineDEF(ld, &DataLine)) return false;
+	return true;
+}
+
+bool
+LegItem::HasErr(LineDEF *ld, int err)
+{
+	if((((DWORD)err & 0x1f) | 0x80) != (flags & 0x9f)) return false;
+	if(ld && cmpLineDEF(ld, &OutLine)) return false;
+	return true;
+}
+
+void
+LegItem::DefDesc(char *txt)
+{
+	TextDEF td;
+	int cb;
+
+	cb = txt && *txt ? (int)strlen(txt) : 20;
+	if(cb < 20) cb = 20;
+	td.ColTxt = 0x00000000L;		td.ColBg = 0x00ffffffL;
+	td.fSize = DefSize(SIZE_TICK_LABELS);
+	td.RotBL = td.RotCHAR = 0.0;
+	td.iSize = 0;					td.Align = TXA_VTOP | TXA_HLEFT;
+	td.Mode = TXM_TRANSPARENT;		td.Style = TXS_NORMAL;
+	td.Font = FONT_HELVETICA;		td.text = (char*)malloc(cb+2);
+	rlp_strcpy(td.text, cb+2, txt ? txt : (char*)"text");
+	Desc = new Label(this, data, 0, 0, &td, LB_X_PARENT | LB_Y_PARENT);
+}
+
+Legend::Legend(GraphObj *par, DataObj *d):GraphObj(par, d)
+{
+	FileIO(INIT_VARS);		Id = GO_LEGEND;		moveable = true;
+}
+
+Legend::Legend(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	moveable = true;
+}
+
+Legend::~Legend()
+{
+	int i;
+
+	if(Items) {
+		for(i = 0; i< nItems; i++) if(Items[i]) DeleteGO(Items[i]);
+		free(Items);	Items = 0L;
+		}
+	if(to) DelBitmapClass(to);		to = 0L;
+}
+double
+Legend::GetSize(int select)
+{
+	switch(select) {
+	case SIZE_XPOS:		return C_Rect.Xmin;
+	case SIZE_XPOS+1:	return C_Rect.Xmax;
+	case SIZE_XPOS+2:	return E_Rect.Xmin;
+	case SIZE_XPOS+3:	return E_Rect.Xmax;
+	case SIZE_YPOS:		return C_Rect.Ymin;
+	case SIZE_YPOS+1:	return C_Rect.Ymax;
+	case SIZE_LB_XPOS:	return lb_pos.fx;
+	case SIZE_LB_YPOS:	return lb_pos.fy;
+	case SIZE_GRECT_TOP:	case SIZE_GRECT_LEFT:
+		if(parent) return parent->GetSize(select);
+		break;
+		}
+	return 0.0;
+}
+
+bool
+Legend::SetSize(int select, double value)
+{
+	double tmp;
+
+	switch (select & 0xfff){
+	case SIZE_XPOS:		pos.fx = value;		return true;
+	case SIZE_YPOS:		pos.fy = value;		return true;
+	case SIZE_YPOS+1:
+		tmp = value - C_Rect.Ymax;
+		C_Rect.Ymin += tmp;		C_Rect.Ymax += tmp;		lb_pos.fy +=tmp;
+		}
+	return false;
+}
+
+void
+Legend::DoPlot(anyOutput *o)
+{
+	int i;
+	double y_inc, dx, dy;
+
+	if(!o || !Items) return;
+	if(to) DelBitmapClass(to);		to = 0L;
+	dx = parent->GetSize(SIZE_GRECT_LEFT);		dy = parent->GetSize(SIZE_GRECT_TOP);
+	C_Rect.Xmin = o->co2fix(pos.fx + B_Rect.Xmin + D_Rect.Xmin + dx);
+	C_Rect.Ymin = o->co2fiy(pos.fy + B_Rect.Ymin + D_Rect.Ymin + dy);
+	C_Rect.Xmax = C_Rect.Xmin + o->un2fix(D_Rect.Xmax - D_Rect.Xmin);
+	C_Rect.Ymax = C_Rect.Ymin + o->un2fix(D_Rect.Ymax - D_Rect.Ymin);
+	E_Rect.Ymin = C_Rect.Ymin;	E_Rect.Ymax = C_Rect.Ymax;
+	E_Rect.Xmin = o->co2fix(pos.fx + B_Rect.Xmin + F_Rect.Xmin + dx);
+	E_Rect.Xmax = E_Rect.Xmin + o->un2fix(F_Rect.Xmax - F_Rect.Xmin);
+	y_inc = floor(0.5+o->un2fiy(B_Rect.Ymax - B_Rect.Ymin));
+	rDims.top = iround(C_Rect.Ymin); rDims.bottom = iround(C_Rect.Ymax);
+	rDims.left = rDims.right = iround(C_Rect.Xmin);	
+	lb_pos.fx = o->co2fix(pos.fx + B_Rect.Xmax + dx);	lb_pos.fy = C_Rect.Ymin;
+	//draw all items
+	for(i = 0; i < nItems; i++) {
+		if(Items[i]){
+			if((Items[i]->flags & 0x11) == 0x01) hasLine = true;
+			Items[i]->DoPlot(o);
+			UpdateMinMaxRect(&rDims, Items[i]->rDims.left, Items[i]->rDims.top);
+			UpdateMinMaxRect(&rDims, Items[i]->rDims.right, Items[i]->rDims.bottom);
+			C_Rect.Ymin += y_inc;		C_Rect.Ymax += y_inc;
+			lb_pos.fy += y_inc;
+			}
+		}
+	IncrementMinMaxRect(&rDims, 6);
+}
+
+void
+Legend::DoMark(anyOutput *o, bool mark)
+{
+	RECT cr;
+	LineDEF ld = {0.0, 1.0, 0x00c0c0c0L, 0x00000000L};
+	POINT pts[5];
+
+	if(!parent || !o) return;
+	cr.left = rDims.left;			cr.right = rDims.right;
+	cr.top = rDims.top;				cr.bottom = rDims.bottom;
+	ld.color = mark ? 0x00c0c0c0L : 0x00ffffffL;	o->SetLine(&ld);
+	pts[0].x = pts[3].x = pts[4].x = cr.left;	pts[0].y = pts[1].y = pts[4].y = cr.top;
+	pts[1].x = pts[2].x = cr.right;				pts[2].y = pts[3].y = cr.bottom;
+	o->oPolyline(pts, 5, name);					IncrementMinMaxRect(&cr, 3);
+	o->UpdateRect(&cr, false);
+}
+
+bool
+Legend::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+
+	switch(cmd){
+	case CMD_TEXTTHERE:
+		if(Items) for(i = 0; i< nItems; i++) 
+			if(Items[i] && Items[i]->Command(cmd, tmpl, o)) return true;
+		return false;
+	case CMD_MOUSE_EVENT:
+		if(o && tmpl && IsInRect(&rDims, ((MouseEvent*)tmpl)->x, ((MouseEvent*)tmpl)->y)) {
+			if(Items) for(i = 0; i< nItems; i++) 
+				if(Items[i] && Items[i]->Command(cmd, tmpl, o)) return true;
+			if(!CurrGO) o->ShowMark(CurrGO = this, MRK_GODRAW);
+			}
+		break;
+	case CMD_SET_DATAOBJ:
+		if(Items) for(i = 0; i < nItems; i++) if(Items[i]) Items[i]->Command(cmd, tmpl, o);
+		Id = GO_LEGEND;
+		data = (DataObj *)tmpl;
+		return true;
+	case CMD_SCALE:
+		pos.fx *= ((scaleINFO*)tmpl)->sx.fy;			pos.fy *= ((scaleINFO*)tmpl)->sy.fy;
+		lb_pos.fx *= ((scaleINFO*)tmpl)->sx.fy;			lb_pos.fy *= ((scaleINFO*)tmpl)->sy.fy;
+		B_Rect.Xmax *= ((scaleINFO*)tmpl)->sx.fy;		B_Rect.Xmin *= ((scaleINFO*)tmpl)->sx.fy;
+		B_Rect.Ymax *= ((scaleINFO*)tmpl)->sy.fy;		B_Rect.Ymin *= ((scaleINFO*)tmpl)->sy.fy;
+		C_Rect.Xmax *= ((scaleINFO*)tmpl)->sx.fy;		C_Rect.Xmin *= ((scaleINFO*)tmpl)->sx.fy;
+		C_Rect.Ymax *= ((scaleINFO*)tmpl)->sy.fy;		C_Rect.Ymin *= ((scaleINFO*)tmpl)->sy.fy;
+		D_Rect.Xmax *= ((scaleINFO*)tmpl)->sx.fy;		D_Rect.Xmin *= ((scaleINFO*)tmpl)->sx.fy;
+		D_Rect.Ymax *= ((scaleINFO*)tmpl)->sy.fy;		D_Rect.Ymin *= ((scaleINFO*)tmpl)->sy.fy;
+		E_Rect.Xmax *= ((scaleINFO*)tmpl)->sx.fy;		E_Rect.Xmin *= ((scaleINFO*)tmpl)->sx.fy;
+		E_Rect.Ymax *= ((scaleINFO*)tmpl)->sy.fy;		E_Rect.Ymin *= ((scaleINFO*)tmpl)->sy.fy;
+		F_Rect.Xmax *= ((scaleINFO*)tmpl)->sx.fy;		F_Rect.Xmin *= ((scaleINFO*)tmpl)->sx.fy;
+		F_Rect.Ymax *= ((scaleINFO*)tmpl)->sy.fy;		F_Rect.Ymin *= ((scaleINFO*)tmpl)->sy.fy;
+		if(Items) for(i = 0; i < nItems; i++) if(Items[i]) Items[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_DELOBJ:
+		o->HideMark();
+		if(Items && parent) for(i = 0; i < nItems; i++) {
+			if(Items[i] && tmpl == (void *)Items[i]) {
+				Undo.DeleteGO((GraphObj**)(&Items[i]), 0L, o);
+				parent->Command(CMD_REDRAW, NULL, o);
+				return true;
+				}
+			}
+		break;
+	case CMD_MOVE:
+		Undo.MoveObj(this, (lfPOINT*)tmpl, 0L);
+	case CMD_UNDO_MOVE:
+		pos.fx += ((lfPOINT*)tmpl)[0].fx;	pos.fy += ((lfPOINT*)tmpl)[0].fy;
+		CurrGO = this;
+	case CMD_REDRAW:
+		if(parent && cmd != CMD_UNDO_MOVE){
+			parent->Command(CMD_REDRAW, tmpl, o);
+			}
+		return true;
+	case CMD_DROP_OBJECT:
+		if(!tmpl) return false;
+		if(!(Items = (LegItem**)realloc(Items, (2+nItems)*sizeof(LegItem*))))return false;
+		Items[nItems++] = (LegItem*)tmpl;
+		Items[nItems-1]->parent = this;
+		return true;
+		}
+	return false;
+}
+
+void
+Legend::Track(POINT *p, anyOutput *o)
+{
+	POINT pts[5];
+	LineDEF tld = {0.0, 1.0, 0x00c0c0c0, 0x0L};
+
+	if(!p || !o) return;
+	if(to) {
+		o->CopyBitmap(trc.left, trc.top, to, 0, 0, trc.right - trc.left, 
+			trc.bottom - trc.top, false);
+		DelBitmapClass(to);		to = 0L;
+		o->UpdateRect(&trc, false);
+		}
+	trc.left = pts[0].x = pts[1].x = pts[4].x = rDims.left + p->x;		
+	trc.top = pts[0].y = pts[3].y = pts[4].y = rDims.top + p->y;
+	trc.bottom = pts[1].y = pts[2].y = rDims.bottom + p->y;
+	trc.right = pts[2].x = pts[3].x = rDims.right + p->x;
+	IncrementMinMaxRect(&trc, 3);	to = GetRectBitmap(&trc, o);
+	o->SetLine(&tld);				o->oPolyline(pts, 5, 0L);
+	o->UpdateRect(&trc, false);
+} 
+
+bool
+Legend::HasFill(LineDEF *ld, FillDEF *fd, char *desc)
+{
+	int i;
+	LegItem *li;
+
+	if(Items) for(i = 0; i < nItems; i++) {
+		if(Items[i] && Items[i]->HasFill(ld, fd, desc)) return true;
+		}
+	if(li = new LegItem(this, data, 0L, ld, fd, desc)){
+		if(!(Command(CMD_DROP_OBJECT, li, 0L))) DeleteGO(li);
+		}
+	return false;
+}
+
+bool
+Legend::HasSym(LineDEF *ld, GraphObj *sy, char *desc)
+{
+	int i, sym;
+	Symbol *ns;
+	LegItem *li;
+
+	if(!parent || !sy) return true;
+	if(ld) hasLine = true;
+	if(Items) for(i = 0; i < nItems; i++) {
+		if(Items[i] && Items[i]->HasSym(ld, sy)) return true;
+		}
+	sym = sy->Id == GO_SYMBOL ? (sy->type & 0xff) : 0;
+	if(!(ns = new Symbol(this, data, 0.0, 0.0, sym | SYM_POS_PARENT))) return true;
+	ns->SetSize(SIZE_SYMBOL, sy->GetSize(SIZE_SYMBOL));
+	ns->SetSize(SIZE_SYM_LINE, sy->GetSize(SIZE_SYM_LINE));
+	ns->SetColor(COL_SYM_LINE, sy->GetColor(COL_SYM_LINE));
+	ns->SetColor(COL_SYM_FILL, sy->GetColor(COL_SYM_FILL));
+	if(desc && desc[0]) {
+		ns->name = (char*)memdup(desc,(int)strlen(desc)+1, 0); 
+		}
+	else if(sy->name && sy->name[0]) {
+		ns->name = (char*)memdup(sy->name,(int)strlen(sy->name)+1, 0); 
+		}
+	else if(sy->parent && sy->parent->Id < GO_GRAPH && sy->parent->Id >= GO_PLOT) {
+		if(((Plot*)(sy->parent))->data_desc) ns->name = (char*)memdup(((Plot*)(sy->parent))->data_desc,
+			(int)strlen(((Plot*)(sy->parent))->data_desc)+1, 0);
+		}
+	else if(sy->parent && sy->parent->name && sy->parent->name[0]) {
+		ns->name = (char*)memdup(sy->parent->name,(int)strlen(sy->parent->name)+1, 0); 
+		}
+	if(li = new LegItem(this, data, ld, ns)){
+		if(!(Command(CMD_DROP_OBJECT, li, 0L))) DeleteGO(li);
+		}
+	return false;
+}
+
+bool
+Legend::HasErr(LineDEF *ld, int err, char *desc)
+{
+	int i;
+	LegItem *li;
+
+	if(Items) for(i = 0; i < nItems; i++) {
+		if(Items[i] && Items[i]->HasErr(ld, err)) return true;
+		}
+	if(li = new LegItem(this, data, ld, err | (hasLine ? 0x40 : 0), desc)){
+		if(!(Command(CMD_DROP_OBJECT, li, 0L))) DeleteGO(li);
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Graphs are graphic objects containing plots, axes, and drawn objects
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Graph::Graph(GraphObj *par, DataObj *d, anyOutput *o, int style):GraphObj(par, d)
+{
+	Graph::FileIO(INIT_VARS);
+	Disp = o;		Id = GO_GRAPH;		cGraphs++;	bModified = true;
+	if(style & 0x10) y_axis.flags |= AXIS_INVERT;
+	if (!d && parent) parent->Command(CMD_DELOBJ, this, NULL);
+}
+
+Graph::Graph(int src):GraphObj(0L, 0L)
+{
+	int i;
+
+	Graph::FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		x_axis.owner = y_axis.owner = (void *)this;
+		//do all axes
+		for(i = 0; Axes && i< NumAxes; i++) if(Axes[i]) Axes[i]->parent = this;
+		//do all plots
+		for(i = 0; Plots && i< NumPlots; i++) if(Plots[i]) Plots[i]->parent = this;
+		if(x_axis.max > x_axis.min && y_axis.max > y_axis.min &&
+			Bounds.Xmax > Bounds.Xmin && Bounds.Ymax > Bounds.Ymin) dirty = false;
+		}
+	cGraphs++;		bModified = false;
+}
+
+Graph::~Graph()
+{
+	int i;
+
+	if(!parent) return;			parent = 0L;
+	Undo.InvalidGO(this);		DoZoom("reset");
+	if(CurrGraph == this) CurrGraph = 0L;
+	if(Plots) {
+		for(i = 0; i< NumPlots; i++) if(Plots[i]) DeleteGO(Plots[i]);
+		free(Plots);		Plots = 0L;		NumPlots = 0;
+		}
+	if(Axes) {
+		for(i = 0; i< NumAxes; i++) if(Axes[i]) DeleteGO(Axes[i]);
+		free(Axes);			Axes = 0L;		NumAxes = 0;
+		}
+	if(OwnDisp && Disp) DelDispClass(Disp);		OwnDisp = false;	Disp = 0L;
+	if(frm_g) DeleteGO(frm_g);		if(frm_d) DeleteGO(frm_d);
+	if(x_axis.breaks && x_axis.owner == this) free(x_axis.breaks);
+	if(y_axis.breaks && y_axis.owner == this) free(y_axis.breaks);
+	if(tl_pts) free(tl_pts);
+	if(nscp > 0 && nscp <= NumPlots && Sc_Plots) free(Sc_Plots);
+	nscp = 0;			Sc_Plots = 0L;
+	if(name) free(name);		name = 0L;
+	if(filename) free(filename);	filename= 0L;
+}
+
+double
+Graph::GetSize(int select)
+{
+	return Graph::DefSize(select);
+}
+
+bool
+Graph::SetSize(int select, double val)
+{
+	switch(select & 0xfff) {
+	case SIZE_GRECT_TOP:	GRect.Ymin = val;	return true;
+	case SIZE_GRECT_BOTTOM:	GRect.Ymax = val;	return true;
+	case SIZE_GRECT_LEFT:	GRect.Xmin = val;	return true;
+	case SIZE_GRECT_RIGHT:	GRect.Xmax = val;	return true;
+	case SIZE_DRECT_TOP:	DRect.Ymin = val;	return true;
+	case SIZE_DRECT_BOTTOM:	DRect.Ymax = val;	return true;
+	case SIZE_DRECT_LEFT:	DRect.Xmin = val;	return true;
+	case SIZE_DRECT_RIGHT:	DRect.Xmax = val;	return true;
+	default: return false;
+		}
+}
+
+DWORD
+Graph::GetColor(int select)
+{
+	switch(select & 0xfff) {
+	case COL_AXIS:		return ColAX;
+	case COL_BG:		return ColDR;
+	case COL_DRECT:		return ColDR;
+	case COL_GRECT:		return ColGR;
+		}
+	if(parent) return parent->GetColor(select);
+	else return defs.Color(select);
+}
+
+bool
+Graph::SetColor(int select, DWORD col)
+{
+	switch(select) {
+	case COL_DRECT:
+		ColDR = col;		return true;
+	case COL_GRECT:
+		ColGR = col;		return true;
+		}
+	return false;
+}
+
+void
+Graph::DoPlot(anyOutput *target)
+{
+	int i, cb;
+	AxisDEF *ax;
+
+	if(nscp > 0 && nscp <= NumPlots && Sc_Plots) free(Sc_Plots);
+	nscp = 0;		Sc_Plots = 0L;
+	if(target && parent && parent->Id == GO_SPREADDATA) target->OC_type &= ~OC_TRANSPARENT;
+	rc_mrk.left = rc_mrk.right = rc_mrk.top = rc_mrk.bottom = -1;
+	CurrAxes = Axes;						defs.Idle(CMD_FLUSH);
+	if(data) do_formula(data, 0L);			//init mfcalc
+	//every graph needs axes!
+	if(type == GT_STANDARD && !NumAxes) CreateAxes(AxisTempl);
+	//verify ownership of axes
+	if(Axes) for (i = 0; i < NumAxes; i++){
+		if(Axes[i] && (ax = Axes[i]->GetAxis()))
+			if(ax == &x_axis || ax == &y_axis) ax->owner = this;
+		}
+	if(!name){
+#ifdef USE_WIN_SECURE
+		cb = sprintf_s(TmpTxt, TMP_TXT_SIZE, "Graph %d", cGraphs) + 2;
+#else
+		cb = sprintf(TmpTxt, "Graph %d", cGraphs) + 2;
+#endif
+		name = (char*)realloc(name, cb);		rlp_strcpy(name, cb, TmpTxt);
+		}
+	if(!target && !Disp) {
+		Disp = NewDispClass(this);
+		Disp->SetMenu(MENU_GRAPH);
+		if(name) Disp->Caption(name, false);
+		Disp->VPorg.fy = (double)Disp->MenuHeight;
+		Disp->CheckMenu(ToolMode, true);
+		OwnDisp = true;						defs.SetDisp(Disp);
+		bModified = false;			//first graph is not modified!
+		Undo.SetDisp(Disp);
+		}
+	//the first output class is the display class
+	if(!Disp && (!(Disp = target))) return;
+	Disp->ActualSize(&defs.clipRC);
+	if(OwnDisp) Disp->Erase(Disp->dFillCol = defs.Color(COL_BG));
+	CurrDisp = target ? target : Disp;
+	CurrDisp->MrkMode = MRK_NONE;
+	CurrRect.Xmin=GRect.Xmin + DRect.Xmin;	CurrRect.Xmax=GRect.Xmin + DRect.Xmax;
+	CurrRect.Ymin=GRect.Ymin + DRect.Ymax;	CurrRect.Ymax=GRect.Ymin + DRect.Ymin;
+	if(dirty) DoAutoscale();
+	CurrDisp->SetRect(CurrRect, units, &x_axis, &y_axis);
+	CurrDisp->disp_x = CurrDisp->un2fix(GRect.Xmin);
+	CurrDisp->disp_y = CurrDisp->un2fiy(GRect.Ymin);
+	if(!frm_g && !(frm_g = new FrmRect(this, 0L, &GRect, 0L))) return;
+	frm_g->SetColor(COL_GRECT, ColGR);	frm_g->SetColor(COL_GRECTLINE, ColGRL);
+	frm_g->DoPlot(CurrDisp);
+	if(type == GT_STANDARD) {
+		if(!frm_d && !(frm_d = new FrmRect(this, &GRect, &DRect, 0L))) return;
+		SetMinMaxRect(&rDims, CurrDisp->co2ix(CurrRect.Xmin), CurrDisp->co2iy(CurrRect.Ymax), 
+			CurrDisp->co2ix(CurrRect.Xmax), CurrDisp->co2iy(CurrRect.Ymin));
+		frm_g->Command(CMD_MINRC, &rDims, CurrDisp);
+		SetMinMaxRect(&rDims, CurrDisp->co2ix(GRect.Xmin), CurrDisp->co2iy(GRect.Ymax), 
+			CurrDisp->co2ix(GRect.Xmax), CurrDisp->co2iy(GRect.Ymin));
+		frm_d->Command(CMD_MAXRC, &rDims, CurrDisp);
+		frm_d->SetColor(COL_DRECT, ColDR);		frm_d->DoPlot(CurrDisp);
+		frm_g->Command(CMD_SETCHILD, &DRect, CurrDisp);
+		}
+	//do all axes
+	if(Axes) for(i = 0; i< NumAxes; i++) if(Axes[i]){
+		Axes[i]->SetColor(COL_BG, ColGR);
+		Axes[i]->DoPlot(CurrDisp);
+		}
+	//do all plots
+	if(Plots) for(i = 0; i < NumPlots; i++) if(Plots[i]) {
+		if(Plots[i]->Id >= GO_PLOT && Plots[i]->Id < GO_GRAPH) {
+			if(((Plot*)Plots[i])->hidden == 0) Plots[i]->DoPlot(CurrDisp);
+			}
+		else {
+			Plots[i]->DoPlot(CurrDisp);
+			}
+		}
+	if(bModified && data) data->Command(CMD_MRK_DIRTY, 0L, 0L);
+	if(parent && parent->Id == GO_GRAPH) {
+		parent->Command(CMD_AXIS, 0L, 0L);
+		}
+	if(target && ToolMode == TM_STANDARD) target->MouseCursor(MC_ARROW, false);
+}
+
+bool
+Graph::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	GraphObj **tmpPlots;
+	char *f_name;
+	RECT rc;
+	int i, j;
+	DWORD delflg = 0L;
+
+	if(!o) o = Disp ? Disp : CurrDisp;
+	switch (cmd){
+    case CMD_CAN_CLOSE:
+		HideTextCursor();
+		if(bModified) {
+			if (Undo.isEmpty(CurrDisp)) return true;
+			bModified = false;
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "%s has not been saved.\nDo you want to save it now?", name);
+#else
+			sprintf(TmpTxt, "%s has not been saved.\nDo you want to save it now?", name);
+#endif
+			i = YesNoCancelBox(TmpTxt);
+			if(i == 2) return false;
+			else if(i == 1) if(! SaveGraphAs(this)) return false;
+			}
+		return true;
+	case CMD_SAVEAS:
+		return SaveGraphAs(this);
+	case CMD_SCALE:
+		return DoScale((scaleINFO*)tmpl, o);
+	case CMD_LAYERS:
+		Undo.SetDisp(o ? o : CurrDisp);
+		return ShowLayers(this);
+	case CMD_HASSTACK:
+		return(NumPlots > 1);
+	case CMD_SAVEPOS:
+		Undo.ValRect(this, &GRect, 0L);
+		Undo.ValRect(this, &DRect, UNDO_CONTINUE);
+		return true;
+	case CMD_LEGEND:
+		Undo.SetDisp(o ? o : CurrDisp);
+		if(Id == GO_PAGE) {
+			if(CurrGraph && CurrGraph->parent == this) return CurrGraph->Command(cmd, tmpl, o);
+			if(!CurrGraph && NumPlots == 1 && Plots[0] && Plots[0]->Id == GO_GRAPH){
+				CurrGraph = (Graph*)Plots[0];
+				return CurrGraph->Command(cmd, tmpl, o);
+				}
+			InfoBox("No graph selected!\nCreate a new graph first or select\n"
+				"a graph before you can add a legend.");
+			return false;
+			}
+		if(Id == GO_GRAPH && !tmpl && Plots) {
+			for(i = 0; i< NumPlots; i++){
+				if(Plots[i] && Plots[i]->Id == GO_LEGEND) {
+					tmpl = (void*)Plots[i];
+					Undo.ObjConf(Plots[i], 0L);
+					break;
+					}
+				}
+			if(!tmpl) {
+				if(!(tmpl = (void*) new Legend(this, data)))return false;
+				if(Disp) {
+					Undo.SetDisp(Disp);
+					tmpPlots = (GraphObj**)memdup(Plots, sizeof(GraphObj*) * (NumPlots+2), 0);
+					if(!tmpPlots) return false;
+					Undo.ListGOmoved(Plots, tmpPlots, NumPlots);
+					Undo.SetGO(this, &tmpPlots[NumPlots++], (Plot *)tmpl, 0L);
+					free(Plots);		Plots = tmpPlots;	bModified = true;
+					}
+				else if(Plots = (GraphObj**)realloc(Plots, sizeof(GraphObj*) * (NumPlots+2))){
+					Plots[NumPlots++] = (GraphObj*)tmpl;	Plots[NumPlots] = 0L;
+					}
+				else return false;
+				}
+			if(type == GT_CIRCCHART)((Legend*)tmpl)->SetSize(SIZE_XPOS, DRect.Xmin*3.0);
+			for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->Command(cmd, tmpl,o);
+			if(Disp) Command(CMD_REDRAW, 0L, CurrDisp);
+			}
+		else if(Id == GO_GRAPH) {
+			for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->Command(cmd, tmpl,o);
+			}
+		return true;
+	case CMD_REPL_GO:
+		if(!(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
+		if(Axes) for(i = 0; i < NumAxes; i++) if(Axes[i] && Axes[i] == tmpPlots[0]){
+			tmpPlots[1]->parent = this;
+			tmpPlots[1]->Command(CMD_SET_DATAOBJ, data, o);
+			Axes[i] = (Axis *)tmpPlots[1];
+			tmpPlots[0]->parent = 0L;		//disable messaging
+			//check for default axes
+			if(((Axis*)tmpPlots[0])->GetAxis() == &x_axis) {
+				if(x_axis.breaks) free(x_axis.breaks);
+				memcpy(&x_axis, Axes[i]->axis, sizeof(AxisDEF));
+				if(x_axis.owner == Axes[i]) free(Axes[i]->axis);
+				Axes[i]->axis = &x_axis;			x_axis.owner = this;
+				}
+			else if(((Axis*)tmpPlots[0])->GetAxis() == &y_axis) {
+				if(y_axis.breaks) free(y_axis.breaks);
+				memcpy(&y_axis, Axes[i]->axis, sizeof(AxisDEF));
+				if(y_axis.owner == Axes[i]) free(Axes[i]->axis);
+				Axes[i]->axis = &y_axis;			y_axis.owner = this;
+				}
+			DeleteGO(tmpPlots[0]);
+			return bModified = dirty = true;
+			}
+		if(Plots) for(i = 0; i < NumPlots; i++) if(Plots[i] && Plots[i] == tmpPlots[0]) { 
+			return bModified = dirty = ReplaceGO((GraphObj**)&Plots[i], tmpPlots);
+			}
+		return false;
+	case CMD_MUTATE:
+		if(!parent || !(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
+		if(Plots) for (i = 0; i < NumPlots; i++) if(Plots[i] && Plots[i] == tmpPlots[0]) {
+			Undo.MutateGO((GraphObj**)&Plots[i], tmpPlots[1], 0L, o);
+			if(ToolMode) Command(CMD_TOOLMODE, 0L, o);
+			return bModified = true;
+			}
+		break;
+	case CMD_REDRAW:
+		if(!CurrDisp) {
+			DoPlot(CurrDisp);			return true;
+			}
+		if(Disp)CurrDisp = Disp;		bDialogOpen = false;
+		if(parent && (parent->Id == GO_PAGE || parent->Id == GO_GRAPH)) return parent->Command(cmd, tmpl, o);
+		if(CurrDisp && CurrDisp->Erase(ColBG)) CurrDisp->StartPage();
+		CurrDisp->MrkMode = MRK_NONE;			DoPlot(CurrDisp);
+		if(CurrDisp) CurrDisp->EndPage();		if(CurrGO == this) CurrGO = 0L;
+		if(CurrGO && CurrGO == CurrLabel && CurrLabel->Id == GO_LABEL)
+			CurrDisp->ShowMark(CurrLabel, MRK_GODRAW);
+		return true;
+	case CMD_ZOOM:
+		return DoZoom((char*)tmpl);
+	case CMD_MOUSECURSOR:
+		if(o)o->MouseCursor(PasteObj ? MC_PASTE : MC_ARROW, false);	
+		return true;
+	case CMD_AXIS:			//one of the plots has changed scaling: reset
+		CurrAxes = Axes;
+		if(o)o->SetRect(CurrRect, units, &x_axis, &y_axis);
+		return true;
+	case CMD_REG_AXISPLOT:	//notification: plot can handle its own axes
+		if(nscp > 0 && nscp <= NumPlots && Sc_Plots)  {
+			for(i = 0; i < nscp; i++)
+				if(Sc_Plots[i] == (GraphObj*)tmpl) return true;
+			if(tmpPlots = (GraphObj**)realloc(Sc_Plots, (nscp+1)*sizeof(GraphObj*))){
+				tmpPlots[nscp++] = (GraphObj *)tmpl;
+				Sc_Plots = tmpPlots;
+				}
+			else {		//memory allocation error
+				nscp = 0;
+				Sc_Plots = 0L;
+				}
+			}
+		else {
+			if(Sc_Plots = (GraphObj **)calloc(1, sizeof(GraphObj*))){
+				Sc_Plots[0] = (GraphObj *)tmpl;
+				nscp = 1;
+				}
+			else nscp = 0;
+			}
+		return true;
+	case CMD_BUSY:
+		if(Disp) Disp->MouseCursor(MC_WAIT, true);
+		break;
+	case CMD_UNDO:
+		Command(CMD_TOOLMODE, 0L, o);
+		if(CurrDisp) CurrDisp->MouseCursor(MC_WAIT, true);
+		Undo.Restore(true, CurrDisp);
+		if(CurrDisp) CurrDisp->MouseCursor(MC_ARROW, true);
+		return true;
+	case CMD_ADDAXIS:
+		Undo.SetDisp(o ? o : CurrDisp);		Command(CMD_TOOLMODE, 0L, o);
+		if(Id == GO_PAGE) {
+			if(CurrGraph && CurrGraph->parent == this) return CurrGraph->Command(cmd, tmpl, o);
+			if(!CurrGraph && NumPlots == 1 && Plots[0] && Plots[0]->Id == GO_GRAPH){
+				CurrGraph = (Graph*)Plots[0];
+				return CurrGraph->Command(cmd, tmpl, o);
+				}
+			InfoBox("No graph selected!\nCreate a new graph first or select\n"
+				"a graph before you can add an axis.");
+			return false;
+			}
+		else {
+			if(type == GT_3D && Plots){
+				for(i = 0; i < NumPlots; i++)
+					if(Plots[i] && (Plots[i]->Id == GO_PLOT3D || Plots[i]->Id == GO_FUNC3D
+						|| Plots[i]->Id == GO_FITFUNC3D))
+						return Plots[i]->Command(cmd, tmpl, o); 
+				}
+			else if(AddAxis()) {
+				Command(CMD_REDRAW, tmpl, o);
+				return true;
+				}
+			}
+		return false;
+	case CMD_CONFIG:
+		Command(CMD_TOOLMODE, 0L, o);		if(CurrGO == this) CurrGO = 0L;
+		return Configure();
+	case CMD_FILENAME:
+		if(tmpl) {
+			i = (int)strlen((char*)tmpl)+2;
+			filename = (char*)realloc(filename, i );
+			rlp_strcpy(filename, i, (char*)tmpl);
+			}
+		break;
+	case CMD_SETNAME:
+		if(OwnDisp && CurrDisp && tmpl && *((char*)tmpl)){
+			CurrDisp->Caption((char*)tmpl, false);
+			i = (int)strlen((char*)tmpl);
+			j = name && name[0] ? (int)strlen(name) : i;
+			name = (char*)realloc(name, i > j ? i+2 : j+2);
+			rlp_strcpy(name, i+2, (char*)tmpl);
+			}
+		else return false;
+		return true;
+	case CMD_SET_DATAOBJ:
+		Id = GO_GRAPH;
+		data = (DataObj *)tmpl;
+		//do axes
+		if(Axes) for(i = 0; i< NumAxes; i++) if(Axes[i]) Axes[i]->Command(cmd, tmpl, o);
+		//do all plots
+		if(Plots) for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->Command(cmd, tmpl,o);
+		return true;
+	case CMD_OPEN:
+		Command(CMD_TOOLMODE, 0L, o);
+		if(!parent) return false;
+		f_name = OpenGraphName(filename);
+		if(f_name && f_name[0]) {
+			if(data) data->Command(CMD_UPDHISTORY, 0L, 0L);
+			return OpenGraph(Id == GO_PAGE ? this : parent, f_name, 0L, false);
+			}
+		return true;
+	case CMD_UPDHISTORY:
+		if(data) data->Command(CMD_UPDHISTORY, 0L, 0L);
+		return true;
+	case CMD_UPDATE:
+		Command(CMD_TOOLMODE, 0L, o);
+		Undo.SetDisp(CurrDisp);
+		if(parent && parent->Id != GO_PAGE && parent->Id != GO_GRAPH){
+			Undo.ValInt(this, &ToolMode, 0L);		//stub, all plots have UNDO_CONTINUE
+			}
+		if(CurrDisp) CurrDisp->MouseCursor(MC_WAIT, true);
+		for(i = 0; i < NumAxes; i++) if(Axes[i]) Axes[i]->Command(cmd, tmpl, o);
+		if(CurrDisp) CurrDisp->MouseCursor(MC_WAIT, false);
+		for(i = 0; Plots && i < NumPlots; i++) 
+			if(Plots[i]) Plots[i]->Command(cmd, tmpl, o);
+		dirty = bModified = true;		CurrDisp->StartPage();
+		if(CurrDisp) CurrDisp->MouseCursor(MC_WAIT, false);
+		DoPlot(CurrDisp);		CurrDisp->EndPage();		CurrGO = 0L;
+		return true;
+	case CMD_DELOBJ_CONT:
+		delflg = UNDO_CONTINUE;
+	case CMD_DELOBJ:
+		Command(CMD_TOOLMODE, 0L, o);
+		bModified = true;
+		if(!tmpl) return false;
+		for(i = 0; i < NumAxes; i++) if(Axes[i] && (void*)Axes[i] == tmpl){
+			if(Axes[i]->Command(CMD_CAN_CLOSE, 0L, o)) {
+				Undo.DeleteGO((GraphObj**)(&Axes[i]), delflg, o);
+				return Command(CMD_REDRAW, 0L, o);
+				}
+			else {
+				InfoBox("Axes used for scaling\ncan not be deleted.");
+				return false;
+				}
+			}
+		for(i = 0; Plots && i < NumPlots; i++) if(Plots[i] && (void*)Plots[i] == tmpl) {
+			Undo.DeleteGO(&Plots[i], delflg, o);			HideTextCursor();
+			Undo.StoreListGO(this, &Plots, &NumPlots, UNDO_CONTINUE);
+			for(i = j = 0; i < NumPlots; i++) if(Plots[i]) Plots[j++] = Plots[i];
+			NumPlots = j;			//compress list of objects
+			return Command(CMD_REDRAW, NULL, o);
+			}
+		if(tmpl == (void*)frm_g && parent && (parent->Id == GO_PAGE || parent->Id == GO_GRAPH)) 
+			return parent->Command(CMD_DELOBJ_CONT, (void*)this, o);
+		return false;
+	case CMD_TOOLMODE:
+		if(o && tmpl) {
+			Undo.SetDisp(o);
+			o->CheckMenu(ToolMode & 0x0f, false);
+			o->CheckMenu(ToolMode = tmpl ? (*((int*)tmpl)) & 0x0f : TM_STANDARD, true);
+			}
+		if(tl_pts && tl_nPts) free(tl_pts);
+		tl_pts = 0L;	tl_nPts = 0;
+		if(CurrDisp) CurrDisp->MrkMode = MRK_NONE;
+		if(o) switch(ToolMode & 0x0f) {
+		case TM_TEXT:	
+			o->MouseCursor(MC_TEXT, false);			break;
+		case TM_DRAW:		case TM_POLYLINE:		case TM_POLYGON:
+			o->MouseCursor(MC_DRAWPEN, false);		break;
+		case TM_RECTANGLE:	
+			o->MouseCursor(MC_DRAWREC, false);		break;
+		case TM_ELLIPSE:		
+			o->MouseCursor(MC_DRAWELLY, false);		break;
+		case TM_ROUNDREC:
+			o->MouseCursor(MC_DRAWRREC, false);		break;
+		case TM_ARROW:
+			o->MouseCursor(MC_CROSS, false);		break;
+		default:
+			o->MouseCursor(MC_ARROW, true);			break;
+			}
+		if((ToolMode & 0x0f) != TM_TEXT) HideTextCursor();
+		return Command(CMD_REDRAW, 0L, CurrDisp);
+	case CMD_ADDPLOT:
+		Undo.SetDisp(o ? o : CurrDisp);
+		if(Id == GO_PAGE) {
+			if(CurrGraph && CurrGraph->parent == this) return CurrGraph->Command(cmd, tmpl, o);
+			if(!CurrGraph && NumPlots == 1 && Plots[0] && Plots[0]->Id == GO_GRAPH){
+				CurrGraph = (Graph*)Plots[0];
+				return CurrGraph->Command(cmd, tmpl, o);
+				}
+			InfoBox("No graph selected!\nCreate a new graph first or select\n"
+				"a graph before you can add a plot.");
+			return false;
+			}
+		else if(Id == GO_GRAPH) {
+			if(type == GT_3D && Plots){
+				for(i = 0; i < NumPlots; i++) { 
+					if(Plots[i] && (Plots[i]->Id == GO_PLOT3D || Plots[i]->Id == GO_FUNC3D
+						|| Plots[i]->Id == GO_FITFUNC3D)) {
+						if(Plots[i]->Command(cmd, tmpl, CurrDisp)) return Command(CMD_REDRAW, 0L, o);
+						else return false;
+						}
+					}
+				return false;
+				}
+			else return AddPlot(0x0);
+			}
+		return false;
+	case CMD_MRK_DIRTY:
+		return (dirty = true);
+	case CMD_CURRLEFT:	case CMD_CURRIGHT:	case CMD_ADDCHAR:	case CMD_ADDCHARW:
+	case CMD_BACKSP:	case CMD_POS_FIRST:	case CMD_POS_LAST:
+		defs.SetDisp(o);
+		if(tmpl && *((int*)tmpl) == 27) {			//Escape
+			HideCopyMark();
+			if(CurrGO && CurrGO->Id == GO_TEXTFRAME) {
+				CurrGO->DoMark(o, false);				o->MrkMode = MRK_NONE;
+				CurrGO = 0L;
+				}
+			else o->HideMark();
+			CurrLabel = 0L;								HideTextCursor();
+			}
+		if(CurrLabel) return CurrLabel->Command(cmd, tmpl, o);
+		if(CurrGO && CurrGO->Id == GO_TEXTFRAME) return CurrGO->Command(cmd, tmpl, o);
+		else if(CurrGO && CurrGO->Id != GO_LABEL && tmpl && *((int*)tmpl) == 13) {
+			CurrGO->PropertyDlg();
+			}
+	case CMD_CURRUP:	case CMD_CURRDOWN:
+		if(CurrLabel && CurrLabel == CurrGO){
+			if(CurrLabel->parent && CurrLabel->parent->Id == GO_MLABEL)
+				return CurrLabel->parent->Command(cmd, tmpl, o);
+			return true;
+			}
+		else if(CurrGO && CurrGO->Id == GO_TEXTFRAME) return CurrGO->Command(cmd, tmpl, o);
+	case CMD_SHIFTLEFT:	case CMD_SHIFTRIGHT:	case CMD_SHIFTUP:	case CMD_SHIFTDOWN:
+		if(Id == GO_PAGE || (CurrGraph && CurrGraph->parent == this)) {
+			if(CurrGraph && CurrGraph->parent == this) return CurrGraph->Command(cmd, tmpl, o);
+			else if(CurrGO && CurrGO->Id == GO_TEXTFRAME) return CurrGO->Command(cmd, tmpl, o);
+			else if(CurrGO && CurrGO->Id == GO_LABEL) return CurrGO->Command(cmd, tmpl, o);
+			}
+		else {
+			if(type == GT_3D && Plots) {
+				for(i = 0; i < NumPlots; i++) {
+					if(Plots[i] && (Plots[i]->Id == GO_PLOT3D || Plots[i]->Id == GO_FUNC3D
+						|| Plots[i]->Id == GO_FITFUNC3D))
+						return Plots[i]->Command(cmd, tmpl, CurrDisp);
+					}
+				}
+			else if(CurrGO && CurrGO->Id == GO_TEXTFRAME) return CurrGO->Command(cmd, tmpl, o);
+			else if(CurrGO && CurrGO->Id == GO_LABEL) return CurrGO->Command(cmd, tmpl, o);
+			}
+		return false;
+	case CMD_MOVE_TOP:	case CMD_MOVE_UP:
+	case CMD_MOVE_DOWN:	case CMD_MOVE_BOTTOM:
+		Undo.StoreListGO(this, &Plots, &NumPlots, 0L);
+		if(MoveObj(cmd, (GraphObj *)tmpl)){
+			bModified = true;
+			CurrDisp->StartPage();			DoPlot(CurrDisp);
+			CurrDisp->EndPage();			return true;
+			}
+		return false;
+	case CMD_DELETE:
+		if(!CurrGO) return false;			bModified = true;
+		if(CurrGO->Id == GO_TEXTFRAME) return CurrGO->Command(cmd, tmpl, o);
+		if(CurrGO == CurrLabel) return CurrLabel->Command(cmd, tmpl, o);
+		if(CurrGO->Id == GO_FRAMERECT) if(!(CurrGO = CurrGO->parent))return false;
+		if(CurrGO->parent == this) return Command(CMD_DELOBJ, (void*)CurrGO, o);
+		if(CurrGO->parent)return CurrGO->parent->Command(CMD_DELOBJ, (void*)CurrGO, o);
+		return false;
+	case CMD_DROP_GRAPH:
+		if(Disp) {
+			if(!(tmpPlots = (GraphObj**)memdup(Plots, (NumPlots+2)*sizeof(GraphObj*), 0))) 
+				return false;
+			Undo.ListGOmoved(Plots, tmpPlots, NumPlots);
+			free(Plots);			Plots = tmpPlots;	Plots[NumPlots] = Plots[NumPlots+1] = 0L;
+			Undo.SetGO(this, &Plots[NumPlots], (GraphObj*)tmpl, 0L);
+			}
+		else if(Plots = (GraphObj**)realloc(Plots, sizeof(GraphObj*) * (NumPlots+2))){
+			Plots[NumPlots] = (GraphObj*)tmpl;	Plots[NumPlots+1] = 0L;
+			}
+		else return false;
+		if(Plots[NumPlots]){
+			Plots[NumPlots]->parent = this;
+			Plots[NumPlots]->Command(CMD_SET_DATAOBJ, data, 0L);
+			if(Plots[NumPlots]->Id == GO_GRAPH) CurrGraph = (Graph*)Plots[NumPlots];
+			Plots[NumPlots]->moveable = 1;		//all page items should be freely
+			}									//   moveable by user
+		NumPlots++;
+		if(CurrDisp) {
+			CurrDisp->StartPage();		DoPlot(CurrDisp);		CurrDisp->EndPage();
+			}
+		return true;
+	case CMD_DROP_PLOT:
+		if(!tmpl) return false;
+		((GraphObj*)tmpl)->parent = this;
+		if(((GraphObj*)tmpl)->Id < GO_PLOT) {
+			if(!NumPlots && Id != GO_PAGE) return false;
+			Plots = (GraphObj**)realloc(Plots, (NumPlots+2)*sizeof(GraphObj*));
+			Plots[NumPlots] = (GraphObj*)tmpl;
+			NumPlots++;
+			if(CurrDisp) {
+				CurrDisp->StartPage();
+				DoPlot(CurrDisp);
+				CurrDisp->EndPage();
+				}
+			return true;
+			}
+		if(Id == GO_GRAPH) CurrGraph = this;	bModified =true;
+		if(!NumPlots) {
+			Plots = (GraphObj**)calloc(2, sizeof(GraphObj*));
+			if(Plots) {
+				Plots[0] = (Plot *)tmpl;	Plots[0]->parent = this;
+				switch(Plots[0]->Id) {
+				case GO_PIECHART:		case GO_RINGCHART:	case GO_STARCHART:
+					type = GT_CIRCCHART;
+					break;
+				case GO_POLARPLOT:
+					type = GT_POLARPLOT;
+					break;
+				case GO_SCATT3D:		case GO_PLOT3D:		case GO_FUNC3D:
+				case GO_FITFUNC3D:
+					type = GT_3D;
+					break;
+				default:
+					type = GT_STANDARD;
+					break;
+					}
+				Bounds.Xmin = x_axis.min = ((Plot*)Plots[0])->Bounds.Xmin;
+				Bounds.Xmax = x_axis.max = ((Plot*)Plots[0])->Bounds.Xmax;
+				Bounds.Ymin = y_axis.min = ((Plot*)Plots[0])->Bounds.Ymin;
+				Bounds.Ymax = y_axis.max = ((Plot*)Plots[0])->Bounds.Ymax;
+				if(Bounds.Ymax == Bounds.Ymin) {
+					if(Bounds.Ymax != 0.0f) {
+						Bounds.Ymax = y_axis.max = Bounds.Ymax + (Bounds.Ymax)/10.0f;
+						Bounds.Ymin = y_axis.min = Bounds.Ymin - (Bounds.Ymax)/10.0f;
+						}
+					else {
+						Bounds.Ymax = y_axis.max = 1.0f;
+						Bounds.Ymin = y_axis.min = -1.0f;
+						}
+					}
+				if(Bounds.Xmax == Bounds.Xmin) {
+					if(Bounds.Xmax != 0.0f) {
+						Bounds.Xmax = x_axis.max = Bounds.Xmax + (Bounds.Xmax)/10.0f;
+						Bounds.Xmin = x_axis.min = Bounds.Xmin - (Bounds.Xmax)/10.0f;
+						}
+					else {
+						Bounds.Xmax = 1.0f;
+						Bounds.Xmin = -1.0f;
+						}
+					}
+				NiceAxis(&x_axis, 4);				NiceAxis(&y_axis, 4);
+				NumPlots = 1;
+				dirty = false;
+				return true;
+				}
+			return false;
+			}
+		else {
+			if(Disp) {
+				Undo.SetDisp(Disp);
+				tmpPlots = (GraphObj**)memdup(Plots, sizeof(GraphObj*) * (NumPlots+2), 0);
+				Undo.ListGOmoved(Plots, tmpPlots, NumPlots);
+				Undo.SetGO(this, &tmpPlots[NumPlots++], (Plot *)tmpl, 0L);
+				free(Plots);			Plots = tmpPlots;
+				}
+			else if(Plots = (GraphObj**)realloc(Plots, sizeof(GraphObj*) * (NumPlots+2))){
+				Plots[NumPlots++] = (Plot*)tmpl;	Plots[NumPlots] = 0L;
+				}
+			else return false;
+			if(type == GT_STANDARD && ((x_axis.flags & AXIS_AUTOSCALE) || 
+				(y_axis.flags & AXIS_AUTOSCALE))) {
+				if(x_axis.flags & AXIS_AUTOSCALE) {
+					Bounds.Xmin = x_axis.min = ((Plot *)tmpl)->Bounds.Xmin < Bounds.Xmin ?
+						((Plot *)tmpl)->Bounds.Xmin : Bounds.Xmin;
+					Bounds.Xmax = x_axis.max = ((Plot *)tmpl)->Bounds.Xmax > Bounds.Xmax ?
+						((Plot *)tmpl)->Bounds.Xmax : Bounds.Xmax;
+					NiceAxis(&x_axis, 4);
+					if(Axes)for(i = 0; i < NumAxes; i++) 
+						if(Axes[i]) Axes[i]->Command(CMD_AUTOSCALE, &x_axis, o);
+					}
+				if(y_axis.flags & AXIS_AUTOSCALE) {
+					Bounds.Ymin = y_axis.min = ((Plot *)tmpl)->Bounds.Ymin < Bounds.Ymin ? 
+						((Plot *)tmpl)->Bounds.Ymin : Bounds.Ymin;
+					Bounds.Ymax = y_axis.max = ((Plot *)tmpl)->Bounds.Ymax > Bounds.Ymax ?
+						((Plot *)tmpl)->Bounds.Ymax : Bounds.Ymax;
+					NiceAxis(&y_axis, 4);
+					if(Axes)for(i = 0; i < NumAxes; i++) 
+						if(Axes[i]) Axes[i]->Command(CMD_AUTOSCALE, &y_axis, o);
+					}
+				}
+			dirty = false;
+			//Redraw graph to show all plots
+			if(CurrDisp) {
+				CurrDisp->StartPage();
+				DoPlot(CurrDisp);
+				CurrDisp->EndPage();
+				}
+			return true;
+			}
+		break;
+	case CMD_PASTE_OBJ:
+		if(!tmpl) return false;
+		Undo.SetDisp(o ? o : CurrDisp);
+		PasteObj = (GraphObj*)tmpl;
+		if(PasteObj->Id == GO_GRAPH || PasteObj->Id == GO_POLYLINE || PasteObj->Id == GO_POLYGON 
+			|| PasteObj->Id == GO_RECTANGLE || PasteObj->Id == GO_ROUNDREC || PasteObj->Id == GO_ELLIPSE
+			|| PasteObj->Id == GO_BEZIER) {
+			ToolMode = TM_PASTE;			o->MouseCursor(MC_PASTE, false);
+			return true;
+			}
+		PasteObj = 0L;
+		return false;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *)tmpl;		defs.SetDisp(o);
+		if(CurrGO && CurrGO->moveable && mev->Action == MOUSE_LBDOWN &&
+			ToolMode == TM_STANDARD && (mev->StateFlags & 24) == 0 &&
+			(TrackGO = (GraphObj*)CurrGO->ObjThere(mev->x, mev->y))){
+			ToolMode |= TM_MOVE;
+			}
+		else if(mev->Action == MOUSE_LBDOWN){
+			if(CurrGO && (CurrGO->Id == GO_TEXTFRAME || CurrGO->Id == GO_LABEL) && CurrGO->Command(cmd, tmpl, o)) return true;
+			CurrGO = 0L;		rc_mrk.left = mev->x;		rc_mrk.top = mev->y;
+			if((ToolMode == TM_TEXT || ToolMode == TM_STANDARD) && Command(CMD_TEXTTHERE, tmpl, o)) {
+				o->CheckMenu(TM_TEXT, false);		o->CheckMenu(TM_STANDARD, true);
+				ToolMode = TM_STANDARD;				return true;
+				}
+			SuspendAnimation(o, true);
+			}
+		if(ToolMode != TM_STANDARD && ExecTool(mev)) return true;
+		switch(mev->Action) {
+		case MOUSE_RBUP:
+			i = ToolMode;							ToolMode = TM_STANDARD;
+			mev->Action = MOUSE_LBUP;				//fake select
+			CurrGO = 0L;		Command(cmd, tmpl, o);		ToolMode = i;
+			if(!CurrGO) CurrGO = this;
+			//the default behaviour for right button click is the same as for
+			//   double click: execute properties dialog, just continue.
+		case MOUSE_LBDOUBLECLICK:
+			Undo.SetDisp(o);							bDialogOpen = true;
+			if(!CurrGO){
+				mev->Action = MOUSE_LBUP;
+				Command(CMD_MOUSE_EVENT, mev, CurrDisp);
+				mev->Action = MOUSE_LBDOUBLECLICK;
+				if(!CurrGO) CurrGO = this;
+				if(CurrGO->Command(CMD_CONFIG, 0L, o))	return Command(CMD_REDRAW, 0L, o);
+				}
+			else if(CurrGO->Id < GO_PLOT) {
+				if(CurrGO->PropertyDlg()) {
+					bModified = true;					return Command(CMD_REDRAW, 0L, o);
+					}
+				}
+			else if(CurrGO->Command(CMD_CONFIG, 0L, o)) return Command(CMD_REDRAW, 0L, o);
+			else o->HideMark();
+			TrackGO = 0L;	CurrLabel = 0L;			bDialogOpen = false;
+			if(CurrGO == this) CurrGO = 0L;
+			return false;
+		case MOUSE_LBUP:
+			if(bDialogOpen) {
+				bDialogOpen = false;					return false;
+				}
+			Undo.SetDisp(o);		SuspendAnimation(o, false);
+			if(Id == GO_GRAPH && parent && parent->Id == GO_SPREADDATA){
+				CurrGO = TrackGO = 0L;
+				CurrGraph = 0L;
+				}
+		case MOUSE_MOVE:
+			if(mev->Action == MOUSE_MOVE && !(mev->StateFlags & 0x01)) return false;
+			//do all axes
+			for(i = 0; Axes && i< NumAxes; i++)
+				if(Axes[i] && Axes[i]->Command(cmd, tmpl,o)) return true;
+			//do all plots
+			if(Plots && NumPlots > 0) for(i = NumPlots-1; i>=0; i--){
+				if(Plots[i] && Plots[i]->Command(cmd, tmpl, o)){
+					if(Plots[i]->Id != GO_GRAPH && Id == GO_GRAPH) CurrGraph = this;
+					return true;
+					}
+				}
+			if(frm_d && frm_d->Command(cmd, tmpl, o) || frm_g && frm_g->Command(cmd, tmpl, o)) {
+				if(Id == GO_GRAPH) CurrGraph = this;
+				return true;
+				}
+			if(mev->Action == MOUSE_MOVE && ToolMode == TM_STANDARD && 
+				rc_mrk.left >=0 && rc_mrk.top >=0) ToolMode = TM_MARK;
+			if(!CurrGO) CurrGraph = 0L;
+			return false;
+			}
+		break;
+	case CMD_TEXTTHERE:
+		//do all axes
+		for(i = 0; Axes && i< NumAxes; i++)
+			if(Axes[i] && Axes[i]->Command(cmd, tmpl,o)) return true;
+		//do all plots
+		if(Plots)for(i = NumPlots-1; i>=0; i--)
+			if(Plots[i] && Plots[i]->Command(cmd, tmpl,o)) return true;
+		break;
+	case CMD_SETSCROLL:
+		if(o) {
+			if(!(o->ActualSize(&rc)))return false;
+			i = o->un2iy(GRect.Ymax);
+			o->SetScroll(true, -(i>>3), i+(i>>3), (rc.bottom -rc.top)>>1, - iround(o->VPorg.fy));
+			i = o->un2ix(GRect.Xmax);
+			o->SetScroll(false, -(i>>3), i+(i>>3), (rc.right -rc.left)>>1, - iround(o->VPorg.fx));
+			if(CurrDisp && o->Erase(ColBG)) Command(CMD_REDRAW, 0L, o);
+			return true;
+			}
+		return false;
+	case CMD_SETHPOS:
+		if(o && tmpl) o->VPorg.fx = - (double)(*((int*)tmpl));
+		return Command(CMD_SETSCROLL, tmpl, o);
+	case CMD_SETVPOS:
+		if(o && tmpl) o->VPorg.fy = - (double)(*((int*)tmpl));
+		return Command(CMD_SETSCROLL, tmpl, o);
+	case CMD_OBJTREE:
+		for(i = 0; Plots && i < NumPlots; i++) if(Plots[i]) {
+			((ObjTree*)tmpl)->Command(CMD_UPDATE, Plots[i], 0L);
+			if(Plots[i]->Id == GO_STACKBAR || Plots[i]->Id == GO_GRAPH || 
+				Plots[i]->Id == GO_PLOT3D || Plots[i]->Id == GO_POLARPLOT ||
+				Plots[i]->Id == GO_FUNC3D || Plots[i]->Id == GO_FITFUNC3D)
+				Plots[i]->Command(cmd, tmpl, o);
+			}
+		return true;
+		}
+	return false;
+}
+
+double 
+Graph::DefSize(int select)
+{
+	switch(select) {
+	case SIZE_LB_XDIST:
+	case SIZE_LB_YDIST:			return 0.0f;
+	case SIZE_GRECT_TOP:		return GRect.Ymin;
+	case SIZE_GRECT_BOTTOM:		return GRect.Ymax;
+	case SIZE_GRECT_LEFT:		return GRect.Xmin;
+	case SIZE_GRECT_RIGHT:		return GRect.Xmax;
+	case SIZE_DRECT_TOP:		return DRect.Ymin;
+	case SIZE_DRECT_BOTTOM:		return DRect.Ymax;
+	case SIZE_DRECT_LEFT:		return DRect.Xmin;
+	case SIZE_DRECT_RIGHT:		return DRect.Xmax;
+	case SIZE_BOUNDS_XMIN:		return Bounds.Xmin;
+	case SIZE_BOUNDS_XMAX:		return Bounds.Xmax;
+	case SIZE_BOUNDS_YMIN:		return Bounds.Ymin;
+	case SIZE_BOUNDS_YMAX:		return Bounds.Ymax;
+	case SIZE_SCALE:			return scale > 0.0 ? scale : 1.0;
+	case SIZE_BOUNDS_LEFT:		return x_axis.flags & AXIS_INVERT ? x_axis.max : x_axis.min;
+	case SIZE_BOUNDS_RIGHT:		return x_axis.flags & AXIS_INVERT ? x_axis.min : x_axis.max;
+	case SIZE_BOUNDS_TOP:		return y_axis.flags & AXIS_INVERT ? y_axis.min : y_axis.max;
+	case SIZE_BOUNDS_BOTTOM:	return y_axis.flags & AXIS_INVERT ? y_axis.max : y_axis.min;
+	case SIZE_YAXISX:
+		if(y_axis.flags & AXIS_X_DATA) return CurrDisp->fx2fix(y_axis.loc[0].fx);
+		else return CurrDisp->co2fix(y_axis.loc[0].fx);
+	case SIZE_XAXISY:
+		if(x_axis.flags & AXIS_Y_DATA) return CurrDisp->fy2fiy(x_axis.loc[0].fy);
+		else return CurrDisp->co2fiy(x_axis.loc[0].fy);
+		}
+	if(scale > 0.0) return scale * defs.GetSize(select);
+	else return defs.GetSize(select);
+}
+bool
+Graph::hasTransp()
+{
+	if(OwnDisp && Disp && (Disp->OC_type & OC_TRANSPARENT)) return true;
+	return false;
+}
+
+void
+Graph::DoAutoscale()
+{
+	int i;
+	fRECT oB;
+
+	memcpy(&oB, &Bounds, sizeof(fRECT));
+	if(type == GT_STANDARD && ((x_axis.flags & AXIS_AUTOSCALE) ||
+		(y_axis.flags & AXIS_AUTOSCALE))) {
+		for(i = 0; i < NumPlots; i++) {
+			if(Plots[i] && (Plots[i]->Id == GO_PLOTSCATT || Plots[i]->Id == GO_BUBBLEPLOT ||
+				Plots[i]->Id == GO_BOXPLOT || Plots[i]->Id == GO_STACKBAR ||
+				Plots[i]->Id == GO_STACKPG || Plots[i]->Id == GO_DENSDISP ||
+				Plots[i]->Id == GO_LIMITS || Plots[i]->Id == GO_FUNCTION ||
+				Plots[i]->Id == GO_FITFUNC || Plots[i]->Id== GO_FREQDIST)) {
+				bModified = true;
+				if(dirty) {
+					if(x_axis.flags & AXIS_AUTOSCALE) {
+						Bounds.Xmin = HUGE_VAL;			Bounds.Xmax = -HUGE_VAL;
+						}
+					if(y_axis.flags & AXIS_AUTOSCALE) {
+						Bounds.Ymin = HUGE_VAL;			Bounds.Ymax = -HUGE_VAL;
+						}
+					dirty = false;
+					}
+				Plots[i]->Command(CMD_AUTOSCALE, 0L, CurrDisp);
+				if(!((Plot*)Plots[i])->hidden) {
+					if(x_axis.flags & AXIS_AUTOSCALE) {
+						Bounds.Xmin = ((Plot*)Plots[i])->Bounds.Xmin < Bounds.Xmin ?
+							((Plot*)Plots[i])->Bounds.Xmin : Bounds.Xmin;
+						Bounds.Xmax = ((Plot*)Plots[i])->Bounds.Xmax > Bounds.Xmax ?
+							((Plot*)Plots[i])->Bounds.Xmax : Bounds.Xmax;
+						}
+					if(y_axis.flags & AXIS_AUTOSCALE) {
+						Bounds.Ymin = ((Plot*)Plots[i])->Bounds.Ymin < Bounds.Ymin ?
+							((Plot*)Plots[i])->Bounds.Ymin : Bounds.Ymin;
+						Bounds.Ymax = ((Plot*)Plots[i])->Bounds.Ymax > Bounds.Ymax ?
+							((Plot*)Plots[i])->Bounds.Ymax : Bounds.Ymax;
+						}
+					}
+				}
+			}
+		if(Bounds.Xmax <= Bounds.Xmin) {
+			Bounds.Xmax = oB.Xmax > oB.Xmin ? oB.Xmax : oB.Xmax + 1.0;
+			Bounds.Xmin = oB.Xmin < oB.Xmax ? oB.Xmin : oB.Xmin - 1.0;
+			}
+		if(Bounds.Ymax <= Bounds.Ymin) {
+			Bounds.Ymax = oB.Ymax > oB.Ymin ? oB.Ymax : oB.Ymax + 1.0;
+			Bounds.Ymin = oB.Ymin < oB.Ymax ? oB.Ymin : oB.Ymin - 1.0;
+			}
+		if(x_axis.flags & AXIS_AUTOSCALE){
+			x_axis.min = Bounds.Xmin;	x_axis.max = Bounds.Xmax;
+			NiceAxis(&x_axis, 4);
+			if(x_axis.min <= 0.0 && ((x_axis.flags & 0xf000) == AXIS_LOG ||
+				(x_axis.flags & 0xf000) == AXIS_RECI)) {
+				x_axis.min = base4log(&x_axis, 0);
+				}
+			if(Axes)for(i = 0; i < NumAxes; i++)
+				if(Axes[i]) Axes[i]->Command(CMD_AUTOSCALE, &x_axis, 0L);
+			}
+		if(y_axis.flags & AXIS_AUTOSCALE){
+			y_axis.min = Bounds.Ymin;	y_axis.max = Bounds.Ymax;
+			NiceAxis(&y_axis, 4);
+			if(y_axis.min <= 0.0 && ((y_axis.flags & 0xf000) == AXIS_LOG ||
+				(y_axis.flags & 0xf000) == AXIS_RECI)) {
+				y_axis.min = base4log(&y_axis, 1);
+				}
+			if(Axes)for(i = 0; i < NumAxes; i++)
+				if(Axes[i]) Axes[i]->Command(CMD_AUTOSCALE, &y_axis, 0L);
+			}
+		}
+	dirty = false;
+}
+
+void
+Graph::CreateAxes(int templ)
+{
+	AxisDEF tmp_axis;
+	TextDEF label_def, tlbdef;
+	char label_text[500];
+	Label *label;
+	double ts, lb_ydist, lb_xdist, tlb_dist;
+	DWORD ptick, ntick, utick;
+	char xa_desc[50], ya_desc[50];
+
+	if(Axes && NumAxes) return;
+	label_def.ColTxt = defs.Color(COL_AXIS);				label_def.ColBg = 0x00ffffffL;
+	label_def.fSize = DefSize(SIZE_TICK_LABELS)*1.2;		label_def.RotBL = label_def.RotCHAR = 0.0;
+	label_def.iSize = 0;									label_def.Align = TXA_VTOP | TXA_HCENTER;
+	label_def.Mode = TXM_TRANSPARENT;						label_def.Style = TXS_NORMAL;
+	label_def.Font = FONT_HELVETICA;						label_def.text = label_text;
+	tlbdef.ColTxt = defs.Color(COL_AXIS);					tlbdef.ColBg = 0x00ffffffL;
+	tlbdef.RotBL = tlbdef.RotCHAR = 0.0;					tlbdef.iSize = 0;
+	tlbdef.fSize = DefSize(SIZE_TICK_LABELS);				tlbdef.Align = TXA_VCENTER | TXA_HCENTER;
+	tlbdef.Style = TXS_NORMAL;								tlbdef.Mode = TXM_TRANSPARENT;
+	tlbdef.Font = FONT_HELVETICA;							tlbdef.text = 0L;
+	ts = DefSize(SIZE_AXIS_TICKS);
+	rlp_strcpy(xa_desc, 50, (char*)"x-axis");				rlp_strcpy(ya_desc, 50, (char*)"y-axis");
+	if(Plots && NumPlots && Plots[0] && Plots[0]->Id >= GO_PLOT && Plots[0]->Id < GO_GRAPH) {
+		if(((Plot*)Plots[0])->x_info) rlp_strcpy(xa_desc, 50,((Plot*)Plots[0])->x_info); 
+		if(((Plot*)Plots[0])->y_info) rlp_strcpy(ya_desc, 50,((Plot*)Plots[0])->y_info); 
+		}
+	switch (tickstyle & 0x07){
+	case 1:						//ticks inside
+		ntick = AXIS_POSTICKS;		ptick = AXIS_POSTICKS;	utick = AXIS_NEGTICKS;
+		ts *= 0.5;
+		break;
+	case 2:						//centered, symetrical
+		ptick = ntick = utick = AXIS_SYMTICKS;
+		ts *= 0.75;
+		break;
+	default:					//ticks outside
+		ptick = AXIS_NEGTICKS;		ntick = AXIS_NEGTICKS; utick = AXIS_POSTICKS;
+		break;
+		}
+	tlb_dist = NiceValue(ts * 2.0);
+	lb_ydist = NiceValue((ts+DefSize(SIZE_AXIS_TICKS))*2.0);
+	lb_xdist = NiceValue((ts+DefSize(SIZE_AXIS_TICKS))*3.0);
+	switch(templ) {
+	case 0:
+		Axes = (Axis**)calloc(5, sizeof(Axis *));
+		x_axis.loc[0].fx = GRect.Xmin + DRect.Xmin;
+		x_axis.loc[1].fx = GRect.Xmin + DRect.Xmax;
+		x_axis.loc[0].fy = x_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
+		y_axis.loc[0].fy = GRect.Ymin + DRect.Ymin;
+		y_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
+		y_axis.loc[0].fx = y_axis.loc[1].fx = GRect.Xmin + DRect.Xmin;;
+		if((Axes[0] = new Axis(this, data, &x_axis, AXIS_BOTTOM | ptick |	
+			AXIS_AUTOTICK | AXIS_AUTOSCALE | ((tickstyle & 0x100) ? AXIS_GRIDLINE : 0)))){
+			Axes[0]->SetSize(SIZE_LB_YDIST, lb_ydist);
+			Axes[0]->SetSize(SIZE_TLB_YDIST, tlb_dist);
+			rlp_strcpy(label_text, 500, xa_desc);
+			label = new Label(Axes[0], data, (DRect.Xmin+DRect.Xmax)/2.0, 
+				GRect.Ymin+DRect.Ymax+DefSize(SIZE_AXIS_TICKS)*4.0f, &label_def, LB_Y_PARENT);
+			if(label && Axes[0]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
+			else if(label) DeleteGO(label);
+			tlbdef.Align = TXA_VTOP | TXA_HCENTER;
+			Axes[0]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		if((Axes[1] = new Axis(this, data, &y_axis, y_axis.flags | AXIS_LEFT | ntick | AXIS_AUTOTICK |
+			AXIS_AUTOSCALE | ((tickstyle & 0x200) ? AXIS_GRIDLINE : 0)))){
+			Axes[1]->SetSize(SIZE_LB_XDIST, -lb_xdist); 
+			Axes[1]->SetSize(SIZE_TLB_XDIST, -tlb_dist); 
+			rlp_strcpy(label_text, 500, ya_desc);
+			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
+			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - DefSize(SIZE_AXIS_TICKS)*6.0, 
+				(DRect.Ymax+DRect.Ymin)/2.0, &label_def, LB_X_PARENT);
+			if(label && Axes[1]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
+			else if(label) DeleteGO(label);
+			tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
+			Axes[1]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		label = 0L;
+		memcpy(&tmp_axis, &x_axis, sizeof(AxisDEF));
+		tmp_axis.owner = NULL;
+		tmp_axis.loc[0].fy = tmp_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
+		if((Axes[2] = new Axis(this, data, &tmp_axis, AXIS_TOP | AXIS_NOTICKS | AXIS_AUTOTICK))){
+			Axes[2]->SetSize(SIZE_LB_YDIST, -lb_ydist); 
+			Axes[2]->SetSize(SIZE_TLB_YDIST, -tlb_dist); 
+			tlbdef.Align = TXA_VBOTTOM | TXA_HCENTER;
+			Axes[2]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		memcpy(&tmp_axis, &y_axis, sizeof(AxisDEF));
+		tmp_axis.owner = NULL;
+		tmp_axis.loc[0].fx = tmp_axis.loc[1].fx = GRect.Xmin + DRect.Xmax;
+		if((Axes[3] = new Axis(this, data, &tmp_axis, AXIS_RIGHT | AXIS_NOTICKS | AXIS_AUTOTICK))){
+			Axes[3]->SetSize(SIZE_LB_XDIST, lb_xdist); 
+			Axes[3]->SetSize(SIZE_TLB_XDIST, tlb_dist); 
+			tlbdef.Align = TXA_VCENTER | TXA_HLEFT;
+			Axes[3]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		NumAxes = 4;
+		break;
+	case 1:
+		Axes = (Axis**)calloc(7, sizeof(Axis *));
+		if(x_axis.Start >= 0.0f) x_axis.min = x_axis.Start = -x_axis.Step;
+		if(y_axis.Start >= 0.0f) y_axis.min = y_axis.Start = -y_axis.Step;
+		x_axis.loc[0].fx = GRect.Xmin + DRect.Xmin;
+		x_axis.loc[1].fx = GRect.Xmin + DRect.Xmax;
+		x_axis.loc[0].fy = x_axis.loc[1].fy = 0.0f;
+		y_axis.loc[0].fy = GRect.Ymin + DRect.Ymin;
+		y_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
+		y_axis.loc[0].fx = y_axis.loc[1].fx = 0.0f;
+		if((Axes[0] = new Axis(this, data, &x_axis, ptick | AXIS_Y_DATA |	
+			AXIS_AUTOTICK | AXIS_AUTOSCALE | ((tickstyle & 0x100) ? AXIS_GRIDLINE : 0)))){
+			Axes[0]->SetSize(SIZE_LB_YDIST, lb_ydist);
+			Axes[0]->SetSize(SIZE_TLB_YDIST, tlb_dist);
+			rlp_strcpy(label_text, 500, xa_desc);
+			label_def.Align = TXA_VTOP | TXA_HRIGHT;
+			label = new Label(Axes[0], data, GRect.Xmin + DRect.Xmax - DefSize(SIZE_AXIS_TICKS)*2.0, 
+				GRect.Ymin+DRect.Ymax+DefSize(SIZE_AXIS_TICKS)*4.0f, &label_def, LB_Y_PARENT);
+			if(label && Axes[0]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
+			else if(label) DeleteGO(label);
+			tlbdef.Align = TXA_VTOP | TXA_HCENTER;
+			Axes[0]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		if((Axes[1] = new Axis(this, data, &y_axis, ntick | AXIS_AUTOTICK | AXIS_X_DATA |
+			AXIS_AUTOSCALE | ((tickstyle & 0x200) ? AXIS_GRIDLINE : 0)))){
+			Axes[1]->SetSize(SIZE_LB_XDIST, -lb_xdist); 
+			Axes[1]->SetSize(SIZE_TLB_XDIST, -tlb_dist);
+			rlp_strcpy(label_text, 500, ya_desc);
+			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HRIGHT;
+			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - DefSize(SIZE_AXIS_TICKS)*6.0, 
+				GRect.Ymin + DRect.Ymin + DefSize(SIZE_AXIS_TICKS)*2.0, 
+				&label_def, LB_X_PARENT);
+			if(label && Axes[1]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
+			else if(label) DeleteGO(label);
+			tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
+			Axes[1]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		memcpy(&tmp_axis, &x_axis, sizeof(AxisDEF));
+		tmp_axis.owner = NULL;
+		tmp_axis.loc[0].fy = tmp_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
+		if(Axes[2] = new Axis(this, data, &tmp_axis, AXIS_TOP | AXIS_NOTICKS | AXIS_AUTOTICK)){
+			Axes[2]->SetSize(SIZE_LB_YDIST, -lb_ydist); 
+			Axes[2]->SetSize(SIZE_TLB_YDIST, -tlb_dist);
+			tlbdef.Align = TXA_VBOTTOM | TXA_HCENTER;
+			Axes[2]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		tmp_axis.loc[0].fy = tmp_axis.loc[1].fy = GRect.Ymin + DRect.Ymin;
+		if(Axes[3] = new Axis(this, data, &tmp_axis, AXIS_BOTTOM | AXIS_NOTICKS | AXIS_AUTOTICK)){
+			Axes[3]->SetSize(SIZE_LB_YDIST, lb_xdist); 
+			Axes[3]->SetSize(SIZE_TLB_YDIST, tlb_dist); 
+			tlbdef.Align = TXA_VTOP | TXA_HCENTER;
+			Axes[3]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		memcpy(&tmp_axis, &y_axis, sizeof(AxisDEF));
+		tmp_axis.owner = NULL;
+		tmp_axis.loc[0].fx = tmp_axis.loc[1].fx = GRect.Xmin + DRect.Xmin;
+		if(Axes[4] = new Axis(this, data, &tmp_axis, AXIS_LEFT | AXIS_NOTICKS | AXIS_AUTOTICK)){
+			Axes[4]->SetSize(SIZE_LB_XDIST, -lb_xdist); 
+			Axes[4]->SetSize(SIZE_TLB_XDIST, -tlb_dist); 
+			tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
+			Axes[4]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		tmp_axis.loc[0].fx = tmp_axis.loc[1].fx = GRect.Xmin + DRect.Xmax;
+		if(Axes[5] = new Axis(this, data, &tmp_axis, AXIS_RIGHT | AXIS_NOTICKS | AXIS_AUTOTICK)){
+			Axes[5]->SetSize(SIZE_LB_XDIST, lb_xdist); 
+			Axes[5]->SetSize(SIZE_TLB_XDIST, tlb_dist); 
+			tlbdef.Align = TXA_VCENTER | TXA_HLEFT;
+			Axes[5]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		NumAxes = 6;
+		break;
+	case 2:
+		Axes = (Axis**)calloc(3, sizeof(Axis *));
+		x_axis.loc[0].fx = GRect.Xmin + DRect.Xmin;
+		x_axis.loc[1].fx = GRect.Xmin + DRect.Xmax;
+		x_axis.loc[0].fy = x_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
+		y_axis.loc[0].fy = GRect.Ymin + DRect.Ymin;
+		y_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
+		y_axis.loc[0].fx = y_axis.loc[1].fx = GRect.Xmin + DRect.Xmin;
+		if((Axes[0] = new Axis(this, data, &x_axis, AXIS_BOTTOM | ptick |	
+			AXIS_AUTOTICK | AXIS_AUTOSCALE | ((tickstyle & 0x100) ? AXIS_GRIDLINE : 0)))){
+			Axes[0]->SetSize(SIZE_LB_YDIST, lb_ydist);
+			Axes[0]->SetSize(SIZE_TLB_YDIST, tlb_dist);
+			rlp_strcpy(label_text, 500, xa_desc);
+			label = new Label(Axes[0], data, GRect.Xmin + (DRect.Xmin+DRect.Xmax)/2.0f, 
+				GRect.Ymin+DRect.Ymax+DefSize(SIZE_AXIS_TICKS)*4.0f, &label_def, LB_Y_PARENT);
+			if(label && Axes[0]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
+			else if(label) DeleteGO(label);
+			tlbdef.Align = TXA_VTOP | TXA_HCENTER;
+			Axes[0]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		if((Axes[1] = new Axis(this, data, &y_axis, AXIS_LEFT | ntick | AXIS_AUTOTICK |
+			AXIS_AUTOSCALE | ((tickstyle & 0x200) ? AXIS_GRIDLINE : 0)))){
+			Axes[1]->SetSize(SIZE_LB_XDIST, -lb_xdist); 
+			Axes[1]->SetSize(SIZE_TLB_XDIST, -tlb_dist); 
+			rlp_strcpy(label_text, 500, ya_desc);
+			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
+			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - DefSize(SIZE_AXIS_TICKS)*6.0, 
+				GRect.Ymin+(DRect.Ymax+DRect.Ymin)/2.0, &label_def, LB_X_PARENT);
+			if(label && Axes[1]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
+			else if(label) DeleteGO(label);
+			tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
+			Axes[1]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		label = 0L;
+		NumAxes = 2;
+		break;
+	case 3:
+		label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
+		Axes = (Axis**)calloc(3, sizeof(Axis *));
+		x_axis.loc[0].fx = GRect.Xmin + DRect.Xmin;
+		x_axis.loc[1].fx = GRect.Xmin + DRect.Xmax;
+		x_axis.loc[0].fy = x_axis.loc[1].fy = GRect.Ymin + DRect.Ymin;
+		y_axis.loc[0].fy = GRect.Ymin + DRect.Ymin;
+		y_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
+		y_axis.loc[0].fx = y_axis.loc[1].fx = GRect.Xmin + DRect.Xmin;
+		if((Axes[0] = new Axis(this, data, &x_axis, AXIS_TOP | utick |	
+			AXIS_AUTOTICK | AXIS_AUTOSCALE | ((tickstyle & 0x100) ? AXIS_GRIDLINE : 0)))){
+			Axes[0]->SetSize(SIZE_LB_YDIST, -lb_ydist);
+			Axes[0]->SetSize(SIZE_TLB_YDIST, -tlb_dist);
+			rlp_strcpy(label_text, 500, xa_desc);
+			label = new Label(Axes[0], data, GRect.Xmin + (DRect.Xmin+DRect.Xmax)/2.0f, 
+				GRect.Ymin+DRect.Ymax+DefSize(SIZE_AXIS_TICKS)*4.0f, &label_def, LB_Y_PARENT);
+			if(label && Axes[0]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
+			else if(label) DeleteGO(label);
+			tlbdef.Align = TXA_VBOTTOM | TXA_HCENTER;
+			Axes[0]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		if((Axes[1] = new Axis(this, data, &y_axis, AXIS_LEFT | ntick | AXIS_AUTOTICK |
+			AXIS_AUTOSCALE | AXIS_INVERT | ((tickstyle & 0x200) ? AXIS_GRIDLINE : 0)))){
+			Axes[1]->SetSize(SIZE_LB_XDIST, -lb_xdist); 
+			Axes[1]->SetSize(SIZE_TLB_XDIST, -tlb_dist); 
+			rlp_strcpy(label_text, 500, ya_desc);
+			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
+			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - DefSize(SIZE_AXIS_TICKS)*6.0, 
+				GRect.Ymin+(DRect.Ymax+DRect.Ymin)/2.0, &label_def, LB_X_PARENT);
+			if(label && Axes[1]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
+			else if(label) DeleteGO(label);
+			tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
+			Axes[1]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		label = 0L;
+		NumAxes = 2;
+		break;
+	case 4:
+		Axes = (Axis**)calloc(3, sizeof(Axis *));
+		if(x_axis.Start >= 0.0f) x_axis.min = -x_axis.Step;
+		x_axis.loc[0].fx = GRect.Xmin + DRect.Xmin;
+		x_axis.loc[1].fx = GRect.Xmin + DRect.Xmax;
+		x_axis.loc[0].fy = x_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
+		y_axis.loc[0].fy = GRect.Ymin + DRect.Ymin;
+		y_axis.loc[1].fy = GRect.Ymin + DRect.Ymax;
+		y_axis.loc[0].fx = y_axis.loc[1].fx = 0.0f;
+		if((Axes[0] = new Axis(this, data, &x_axis, AXIS_BOTTOM | ptick |	
+			AXIS_AUTOTICK | AXIS_AUTOSCALE | ((tickstyle & 0x100) ? AXIS_GRIDLINE : 0)))){
+			Axes[0]->SetSize(SIZE_LB_YDIST, lb_ydist);
+			Axes[0]->SetSize(SIZE_TLB_YDIST, tlb_dist);
+			rlp_strcpy(label_text, 500, xa_desc);
+			label = new Label(Axes[0], data, GRect.Xmin + (DRect.Xmin+DRect.Xmax)/2.0f, 
+				GRect.Ymin+DRect.Ymax+DefSize(SIZE_AXIS_TICKS)*4.0f, &label_def, LB_Y_PARENT);
+			if(label && Axes[0]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
+			else if(label) DeleteGO(label);
+			tlbdef.Align = TXA_VTOP | TXA_HCENTER;
+			Axes[0]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		if((Axes[1] = new Axis(this, data, &y_axis, ntick | AXIS_AUTOTICK | AXIS_X_DATA |
+			AXIS_AUTOSCALE | ((tickstyle & 0x200) ? AXIS_GRIDLINE : 0)))){
+			Axes[1]->SetSize(SIZE_LB_XDIST, -lb_xdist); 
+			Axes[1]->SetSize(SIZE_TLB_XDIST, -tlb_dist); 
+			rlp_strcpy(label_text, 500, ya_desc);
+			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
+			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - DefSize(SIZE_AXIS_TICKS)*6.0, 
+				GRect.Ymin+(DRect.Ymax+DRect.Ymin)/2.0, &label_def, LB_X_PARENT);
+			if(label && Axes[1]->Command(CMD_DROP_LABEL, (void*)label, 0L)) label = 0L;
+			else if(label) DeleteGO(label);
+			tlbdef.Align = TXA_VCENTER | TXA_HRIGHT;
+			Axes[1]->Command(CMD_TLB_TXTDEF, (void*)&tlbdef, 0L);
+			}
+		label = 0L;
+		NumAxes = 2;
+		break;
+		}
+	if(Plots[0] && Plots[0]->Id >= GO_PLOT && Plots[0]->Id < GO_GRAPH && NumAxes > 1) {
+		if(((Plot*)Plots[0])->x_tv && Axes[0])Axes[0]->atv = ((Plot*)Plots[0])->x_tv->Copy();
+		else if(((Plot*)Plots[0])->x_dtype == ET_DATETIME)x_axis.flags |= AXIS_DATETIME;
+		if(((Plot*)Plots[0])->y_tv && Axes[1])Axes[1]->atv = ((Plot*)Plots[0])->y_tv->Copy();
+		else if(((Plot*)Plots[0])->y_dtype == ET_DATETIME)y_axis.flags |= AXIS_DATETIME;
+		}
+}
+
+bool
+Graph::DoScale(scaleINFO* sc, anyOutput *o)
+{
+	int i;
+	scaleINFO sc0;
+
+	if(sc->sy.fy <= 0.0) return false;
+	GRect.Xmax = sc->sx.fx + GRect.Xmax* sc->sx.fy;
+	GRect.Xmin = sc->sx.fx + GRect.Xmin * sc->sx.fy;
+	GRect.Ymax = sc->sy.fx + GRect.Ymax * sc->sy.fy;
+	GRect.Ymin = sc->sy.fx + GRect.Ymin * sc->sy.fy;
+	DRect.Xmax *= sc->sx.fy;	DRect.Xmin *= sc->sx.fy;
+	DRect.Ymax *= sc->sy.fy;	DRect.Ymin *= sc->sy.fy;
+	if(sc->sx.fy == 1.0 && sc->sx.fy  == sc->sy.fy && sc->sx.fy == sc->sz.fy) return true;
+	memcpy(&sc0, sc, sizeof(scaleINFO));
+	sc0.sx.fx = sc0.sy.fx = sc0.sz.fx = 0.0;	sc0.sx.fy = sc0.sz.fy = sc->sy.fy;
+	if(Axes) for(i = 0; i< NumAxes; i++) if(Axes[i]) Axes[i]->Command(CMD_SCALE, &sc0, o);
+	if(Plots) for(i = 0; i < NumPlots; i++) if(Plots[i]){
+		if(Plots[i]->Id == GO_GRAPH || Plots[i]->Id == GO_PAGE) Plots[i]->Command(CMD_SCALE, sc, o);
+		else Plots[i]->Command(CMD_SCALE, &sc0, o);
+		}
+	scale = scale > 0.0 ? scale * sc->sy.fy : sc->sy.fy;
+	return true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Pages are graphic objects containing graphs and drawn objects
+Page::Page(GraphObj *par, DataObj *d):Graph(par, d, 0L, 0)
+{
+	FileIO(INIT_VARS);
+	cGraphs--;		cPages++;		Id = GO_PAGE;	bModified = true;
+}
+
+Page::Page(int src):Graph(src)
+{
+	int i;
+
+	//most of the object is read by Graph::FileIO()
+	ColBG = 0x00e8e8e8L;
+	LineDef.width = 0.0;	LineDef.patlength = 1.0;
+	LineDef.color = LineDef.pattern = 0x0L;
+	FillDef.type = FILL_NONE;
+	FillDef.color = 0x00ffffffL;	//use white paper
+	FillDef.scale = 1.0;
+	FillDef.hatch = 0L;
+	cGraphs--;		cPages++;	bModified = false;
+	if(Plots) for(i = 0; i < NumPlots; i++) if(Plots[i]) Plots[i]->moveable = 1;
+}
+
+void
+Page::DoPlot(anyOutput *o)
+{
+	int i;
+	POINT pts[3];
+
+	if(!o && !Disp) {
+		Disp = NewDispClass(this);
+		Disp->SetMenu(MENU_PAGE);
+#ifdef USE_WIN_SECURE
+		i = sprintf_s(TmpTxt, TMP_TXT_SIZE, "Page %d", cPages);
+#else
+		i = sprintf(TmpTxt, "Page %d", cPages);
+#endif
+		if(!name && (name = (char*)malloc(i += 2)))rlp_strcpy(name, i, TmpTxt);
+		Disp->Caption(TmpTxt, false);
+		Disp->VPorg.fy = iround(Disp->MenuHeight);
+		Disp->VPscale = 0.5;
+		Disp->CheckMenu(ToolMode, true);
+		OwnDisp = true;
+		}
+	//the first output class is the display class
+	if(!Disp && (!(Disp = o))) return;
+	CurrDisp = o ? o : Disp;
+	CurrDisp->Erase(CurrDisp->dFillCol = ColBG);
+	if(OwnDisp && CurrDisp == Disp)LineDef.color = 0x0L;
+	else LineDef.color = FillDef.color;
+	CurrDisp->SetLine(&LineDef);
+	CurrDisp->SetFill(&FillDef);
+	CurrDisp->oRectangle(rDims.left = CurrDisp->co2ix(GRect.Xmin), 
+		rDims.top = CurrDisp->co2iy(GRect.Ymin), rDims.right = CurrDisp->co2ix(GRect.Xmax),
+		rDims.bottom = CurrDisp->co2iy(GRect.Ymax));
+	pts[0].x = rDims.left+7;				pts[0].y = pts[1].y = rDims.bottom;
+	pts[1].x = pts[2].x = rDims.right;		pts[2].y = rDims.top +3;
+	CurrDisp->oPolyline(pts, 3);
+	//do all plots
+	if(Plots) for(i = 0; i < NumPlots; i++) if(Plots[i]) Plots[i]->DoPlot(CurrDisp);
+	if(PasteObj) {
+		ToolMode = TM_PASTE;	CurrDisp->MouseCursor(MC_PASTE, false);
+		}
+}
+
+bool
+Page::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	Graph *g;
+
+	switch(cmd) {
+	case CMD_MOUSE_EVENT:
+		return Graph::Command(cmd, tmpl, o);
+	case CMD_REDRAW:
+		if(Disp) {
+			Disp->StartPage();		DoPlot(Disp);		Disp->EndPage();
+			}
+		return true;
+	case CMD_CONFIG:
+		return Configure();
+	case CMD_NEWGRAPH:
+		if((g = new Graph(this, data, Disp, 0)) && g->PropertyDlg() && 
+			Command(CMD_DROP_GRAPH, g, o))return true;
+		else if(g) DeleteGO(g);
+		return false;
+	case CMD_SET_DATAOBJ:
+		Graph::Command(cmd, tmpl, o);
+		Id = GO_PAGE;
+		return true;
+	case CMD_SCALE:
+		return true;
+	default:
+		return Graph::Command(cmd, tmpl, o);
+	}
+}
+
+double 
+Page::DefSize(int select)
+{
+	return defs.GetSize(select);
+}
+
diff --git a/rlplot.h b/rlplot.h
index db6fa4a..3be5468 100755
--- a/rlplot.h
+++ b/rlplot.h
@@ -1,2973 +1,3045 @@
-//RLPlot.h, Copyright (c) 2000-2007 R.Lackner
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-#define NUM_UNITS  3
-#define TMP_TXT_SIZE 4096
-
-#include <stdio.h>
-#include "Version.h"
-
-#define Swap(a,b) {a^=b;b^=a;a^=b;}
-inline int iround(double a) {return a < 0.0 ?(int)(a-0.499) : (int)(a+0.499);}
-
-#define _PI		3.1415926535897932384626433832795028841971693993751
-#define _SQRT2	1.4142135623730950488016887242096980785696718753769
-
-#ifdef _WINDOWS					//platform is windows
-#include <windows.h>
-#if _MSC_VER >= 1400
-#define USE_WIN_SECURE
-#endif
-#define w_char WCHAR
-#define _SBINC	1				//scrollbox extra space/line
-#define _TXH	4.0				//graph text default size in mm
-
-#else							//platform is *nix
-#include <sys/types.h>
-#define DWORD u_int32_t
-#define w_char wchar_t
-#define _SBINC	8				//scrollbox extra space/line
-#define _TXH	3.0				//graph text default size in mm
-
-typedef struct tagPOINT { // pt 
-    long x; 
-    long y; 
-} POINT; 
-
-typedef struct _RECT {    // rc 
-    long left; 
-    long top; 
-    long right; 
-    long bottom; 
-} RECT; 
-
-#endif	//_WINDOWS
-
-enum {SIZE_MINE, SIZE_SYMBOL, SIZE_SYM_LINE, SIZE_DATA_LINE, SIZE_TEXT,
-	SIZE_GRECT_TOP, SIZE_GRECT_BOTTOM, SIZE_GRECT_LEFT, SIZE_GRECT_RIGHT,
-	SIZE_DRECT_LEFT, SIZE_DRECT_RIGHT, SIZE_DRECT_TOP, SIZE_DRECT_BOTTOM,
-	SIZE_DATA_LINE_PAT, SIZE_XPOS, SIZE_XPOS_LAST = SIZE_XPOS+200, 
-	SIZE_YPOS, SIZE_YPOS_LAST = SIZE_YPOS+200, SIZE_ZPOS, 
-	SIZE_ZPOS_LAST = SIZE_ZPOS+200, SIZE_BOUNDS_XMIN,
-	SIZE_BOUNDS_XMAX, SIZE_BOUNDS_YMIN, SIZE_BOUNDS_YMAX, SIZE_BOUNDS_ZMIN,
-	SIZE_BOUNDS_ZMAX, SIZE_BOUNDS_LEFT, SIZE_BAR_BASE,
-	SIZE_BOUNDS_RIGHT, SIZE_BOUNDS_TOP, SIZE_BOUNDS_BOTTOM, SIZE_XAXISY,
-	SIZE_YAXISX, SIZE_AXIS_LINE, SIZE_PATLENGTH, SIZE_BAR_DEPTH,
-	SIZE_AXIS_TICKS, SIZE_TICK_LABELS, SIZE_ERRBAR, SIZE_ERRBAR_LINE, 
-	SIZE_BAR_LINE, SIZE_BAR, SIZE_XBASE, SIZE_YBASE, SIZE_ZBASE, SIZE_BUBBLE_LINE,
-	SIZE_BUBBLE_HATCH_LINE, SIZE_BARMINX, SIZE_BARMINY, SIZE_ARROW_LINE,
-	SIZE_ARROW_CAPWIDTH, SIZE_ARROW_CAPLENGTH, SIZE_HAIRLINE, SIZE_WHISKER,
-	SIZE_WHISKER_LINE, SIZE_BOX_LINE, SIZE_BOXMINX, SIZE_BOXMINY, SIZE_BOX,
-	SIZE_RADIUS1, SIZE_RADIUS2, SIZE_SEGLINE, SIZE_LB_XPOS, SIZE_LB_YPOS,
-	SIZE_LB_XDIST, SIZE_LB_YDIST, SIZE_TLB_XDIST, SIZE_TLB_YDIST, SIZE_ANGLE1,
-	SIZE_ANGLE2, SIZE_XCENTER, SIZE_YCENTER, SIZE_ZCENTER, SIZE_CELLWIDTH, SIZE_CELLTEXT,
-	SIZE_A, SIZE_B, SIZE_MX, SIZE_MY, SIZE_MIN_Z, SIZE_MAX_Z, SIZE_MIN_X, SIZE_MAX_X,
-	SIZE_MIN_Y, SIZE_MAX_Y, SIZE_TICK_ANGLE, SIZE_RRECT_RAD, SIZE_XCENT, SIZE_YCENT,
-	SIZE_ZCENT, SIZE_DRADIUS, SIZE_CURSORPOS, SIZE_CURSOR_XMIN, SIZE_CURSOR_YMIN,
-	SIZE_CURSOR_XMAX, SIZE_CURSOR_YMAX, SIZE_XSTEP, SIZE_LSPC, SIZE_SCALE};
-enum {COL_SYM_LINE, COL_SYM_FILL, COL_DATA_LINE, COL_TEXT, COL_BG,
-	COL_AXIS, COL_BAR_LINE, COL_BAR_FILL, COL_ERROR_LINE, COL_BUBBLE_LINE, 
-	COL_BUBBLE_FILLLINE, COL_BUBBLE_FILL, COL_ARROW, COL_WHISKER, COL_BOX_LINE,
-	COL_DRECT, COL_GRECT, COL_GRECTLINE, COL_POLYLINE, COL_POLYGON};
-enum {MRK_NONE, MRK_INVERT, MRK_GODRAW, MRK_SSB_DRAW};
-enum {CMD_NONE, CMD_ADDCHAR, CMD_ADDCHARW, CMD_SETFOCUS, CMD_KILLFOCUS, CMD_DELETE,
-	CMD_BACKSP, CMD_CURRLEFT, CMD_CURRIGHT, CMD_CURRUP, CMD_CURRDOWN,
-	CMD_SHIFTLEFT, CMD_SHIFTRIGHT, CMD_SHIFTUP, CMD_SHIFTDOWN,
-	CMD_NEWGRAPH, CMD_DELGRAPH, CMD_DELOBJ, CMD_DROP_PLOT, CMD_DROP_GRAPH, CMD_OPEN,
-	CMD_SAVEDATAAS, CMD_ADDROWCOL, CMD_MOUSE_EVENT, CMD_REDRAW, CMD_DOPLOT,
-	CMD_LBUP, CMD_ENDDIALOG, CMD_RADIOBUTT, CMD_ADDCHILD, CMD_BAR_TYPE,
-	CMD_BAR_FILL, CMD_SET_AXDEF, CMD_SET_DATAOBJ, CMD_SETTEXT, CMD_GETTEXT,
-	CMD_SETTEXTDEF, CMD_GETTEXTDEF, CMD_ADDPLOT, CMD_SYM_TYPE, CMD_SYMTEXT, 
-	CMD_SYMTEXTDEF, CMD_RANGETEXT, CMD_SYM_RANGETEXT, CMD_FOCTXT, CMD_CONTINUE,
-	CMD_ERR_TYPE, CMD_ARROW_ORG, CMD_ARROW_TYPE, CMD_FLUSH, CMD_BOX_FILL,
-	CMD_TABDLG, CMD_NOTABDLG, CMD_TAB, CMD_SHTAB, CMD_BOX_TYPE, CMD_BUBBLE_TYPE,
-	CMD_BUBBLE_ATTRIB, CMD_BUBBLE_FILL, CMD_BUBBLE_LINE, CMD_DL_LINE, 
-	CMD_DL_TYPE, CMD_SEG_FILL, CMD_SEG_LINE, CMD_SELECT, CMD_MOVE, CMD_CUT,
-	CMD_SETSCROLL, CMD_SETHPOS, CMD_SETVPOS, CMD_PG_FILL, CMD_BOUNDS,
-	CMD_SHIFT_OUT, CMD_CAN_CLOSE, CMD_RESET_LINE, CMD_SET_TICKSTYLE,
-	CMD_GET_GRIDLINE, CMD_SET_GRIDLINE, CMD_SET_GRIDTYPE, CMD_TLB_TXTDEF,
-	CMD_DROP_LABEL, CMD_DROP_OBJECT, CMD_PAGEUP, CMD_PAGEDOWN, CMD_AUTOSCALE,
-	CMD_MRK_DIRTY, CMD_SETNAME, CMD_TOOLMODE, CMD_DROPFILE, CMD_AXIS, CMD_INIT,
-	CMD_GET_CELLDIMS, CMD_SET_CELLDIMS, CMD_TEXTSIZE, CMD_PASTE_TSV, CMD_PASTE_XML,
-	CMD_PASTE_SSV, CMD_PASTE_CSV, CMD_COPY_TSV, CMD_COPY_XML, CMD_COPY_SYLK, CMD_QUERY_COPY,
-	CMD_MOUSECURSOR, CMD_NEWPAGE, CMD_MINRC, CMD_MAXRC,CMD_SETCHILD, CMD_SYM_FILL,
-	CMD_LINEUP, CMD_LINEDOWN, CMD_CONFIG, CMD_FINDTEXT, CMD_MOVE_TOP, CMD_MOVE_UP,
-	CMD_MOVE_DOWN, CMD_MOVE_BOTTOM, CMD_UPDATE, CMD_CURRPOS, CMD_POS_FIRST, CMD_POS_LAST,
-	CMD_ADDAXIS, CMD_REG_AXISPLOT, CMD_USEAXIS, CMD_SET_GO3D, CMD_UNDO, CMD_DELOBJ_CONT,
-	CMD_RMU, CMD_REPL_GO, CMD_UNDO_MOVE, CMD_SAVEPOS, CMD_WHISKER_STYLE, CMD_TICK_TYPE,
-	CMD_ZOOM, CMD_CLIP, CMD_STARTLINE, CMD_ADDTOLINE, CMD_REQ_POINT, CMD_DRAW_LATER,
-	CMD_SEG_MOVEABLE, CMD_ARROW_ORG3D, CMD_MUTATE, CMD_PRINT, CMD_UPDHISTORY, CMD_ALLTEXT,
-	CMD_SET_LINE, CMD_SAVE_SYMBOLS, CMD_SAVE_TICKS, CMD_SAVE_BARS, CMD_SAVE_BARS_CONT,
-	CMD_SAVE_ERRS, CMD_SAVE_ARROWS, CMD_SAVE_DROPLINES, CMD_SAVE_LABELS, CMD_UNLOCK, CMD_SYMTEXT_UNDO,
-	CMD_FILLRANGE, CMD_BUSY, CMD_ERROR, CMD_CLEAR_ERROR, CMD_SETPARAM, CMD_SETFUNC,
-	CMD_LEGEND, CMD_FILENAME, CMD_LAYERS, CMD_OBJTREE, CMD_TEXTDEF,
-	CMD_HASSTACK, CMD_WRITE_GRAPHS, CMD_SETFONT, CMD_SETSTYLE, CMD_COPY, CMD_PASTE,
-	CMD_INSROW, CMD_INSCOL, CMD_DELROW, CMD_DELCOL, CMD_ADDTXT, CMD_ETRACC, CMD_SHPGUP, 
-	CMD_SHPGDOWN, CMD_ERRDESC, CMD_SAVEDATA, CMD_GETMARK, CMD_PASTE_OBJ, CMD_COL_MOUSE,
-	CMD_MARKOBJ, CMD_SCALE, CMD_GETFILENAME, CMD_TEXTTHERE, CMD_HIDEMARK};
-enum {SYM_CIRCLE, SYM_CIRCLEF, SYM_RECT, SYM_RECTF, SYM_TRIAU, SYM_TRIAUF,
-	SYM_TRIAD, SYM_TRIADF, SYM_DIAMOND, SYM_DIAMONDF, SYM_PLUS, SYM_CROSS,
-	SYM_STAR, SYM_HLINE, SYM_VLINE, SYM_CIRCLEC, SYM_RECTC, SYM_TRIAUC, SYM_TRIAUL, SYM_TRIAUR,
-	SYM_TRIADC, SYM_TRIADL, SYM_TRIADR, SYM_DIAMONDC, SYM_4STAR, SYM_4STARF, SYM_5GON, SYM_5GONF, 
-	SYM_5GONC, SYM_5STAR, SYM_5STARF, SYM_6STAR, SYM_6STARF, SYM_1QUAD, SYM_2QUAD, SYM_3QUAD,
-	SYM_TEXT = 0x004f, SYM_POS_PARENT = 0x1000};
-//types of graphic objects: stored in Id and used for proper destruction of objects
-//  and retrieving information.
-enum {GO_UNKNOWN, GO_AXIS, GO_TICK, GO_GRIDLINE, GO_SYMBOL, GO_BUBBLE, GO_BAR, 
-	GO_ERRBAR, GO_ARROW, GO_BOX, GO_LABEL, GO_MLABEL, GO_WHISKER, GO_DROPLINE, 
-	GO_DATALINE, GO_DATAPOLYGON, GO_REGLINE, GO_SDELLIPSE, GO_SEGMENT, 
-	GO_POLYLINE, GO_POLYGON, GO_RECTANGLE, GO_ELLIPSE, GO_ROUNDREC,
-	GO_DRAGHANDLE, GO_DRAGRECT, GO_DRAG3D, GO_FRAMERECT, GO_SPHERE, GO_SVGOPTIONS,
-	GO_PLANE, GO_BRICK, GO_LINESEG, GO_LINE3D, GO_GRIDLINE3D, GO_GRIDRADIAL,
-	GO_SPHSCANL, GO_DROPL3D, GO_ARROW3D, GO_PLANE3D, GO_LEGITEM, GO_LEGEND,
-	GO_OBJTREE, GO_BEZIER, GO_TEXTFRAME,
-	GO_PLOT = 0x100, GO_PLOTSCATT, GO_REGRESSION, GO_BARCHART, GO_BUBBLEPLOT, 
-	GO_BOXPLOT, GO_DENSDISP, GO_STACKBAR, GO_STACKPG, GO_WATERFALL, GO_POLARPLOT,
-	GO_PIECHART, GO_RINGCHART, GO_GROUP, GO_STARCHART, GO_SCATT3D, GO_PLOT3D,
-	GO_RIBBON, GO_LIMITS, GO_FUNCTION, GO_FITFUNC, GO_FREQDIST, GO_GRID3D, GO_FUNC3D,
-	GO_XYSTAT, GO_FITFUNC3D, GO_NORMQUANT,
-	GO_GRAPH = 0x200, GO_PAGE, GO_SPREADDATA = 0x300, GO_DEFRW};
-enum {OC_UNKNOWN, OC_HIMETRIC};
-enum {FILL_NONE, FILL_HLINES, FILL_VLINES, FILL_HVCROSS, FILL_DLINEU, FILL_DLINED,
-	FILL_DCROSS, FILL_STIPPLE1, FILL_STIPPLE2, FILL_STIPPLE3, FILL_STIPPLE4, 
-	FILL_STIPPLE5, FILL_ZIGZAG, FILL_COMBS, FILL_BRICKH, FILL_BRICKV, FILL_BRICKDU, 
-	FILL_BRICKDD, FILL_TEXTURE1, FILL_TEXTURE2, FILL_WAVES1, FILL_SCALES, FILL_SHINGLES, 
-	FILL_WAVES2, FILL_HERRING, FILL_CIRCLES, FILL_GRASS, FILL_FOAM, FILL_RECS, 
-	FILL_HASH, FILL_WATER, NUM_FILLS, FILL_LIGHT3D = 0x100};
-enum {ERRBAR_VSYM, ERRBAR_VUP, ERRBAR_VDOWN, ERRBAR_HSYM, ERRBAR_HLEFT,
-	ERRBAR_HRIGHT};
-enum {BAR_NONE, BAR_VERTB, BAR_VERTT, BAR_VERTU, BAR_HORL, BAR_HORR, BAR_HORU, 
-	BAR_RELWIDTH = 0x100, BAR_CENTERED = 0x200, BAR_WIDTHDATA = 0x400};
-enum {TM_STANDARD, TM_DRAW, TM_POLYLINE, TM_POLYGON, TM_RECTANGLE, TM_ELLIPSE,
-	TM_ROUNDREC, TM_ARROW, TM_TEXT, TM_MARK, TM_ZOOMIN, TM_MOVE = 0x100, 
-	TM_PASTE=0x200};
-enum {MC_LAST, MC_ARROW, MC_CROSS, MC_TEXT, MC_WAIT, MC_MOVE, MC_NORTH,
-	MC_NE, MC_EAST, MC_SE, MC_SALL, MC_ZOOM, MC_PASTE, MC_DRAWPEN, MC_DRAWREC, 
-	MC_DRAWRREC, MC_DRAWELLY, MC_TXTFRM, MC_COLWIDTH};
-enum {FILE_ERROR, FILE_READ, FILE_WRITE, INIT_VARS, SAVE_VARS};
-enum {ARROW_NOCAP, ARROW_LINE, ARROW_TRIANGLE, ARROW_UNITS = 0x100};
-enum {MENU_NONE, MENU_SPREAD, MENU_GRAPH, MENU_PAGE};
-enum {GT_UNKNOWN, GT_STANDARD, GT_CIRCCHART, GT_POLARPLOT, GT_3D};
-enum {ICO_NONE, ICO_INFO, ICO_ERROR, ICO_RLPLOT, ICO_QT};
-enum {FF_UNKNOWN, FF_CSV, FF_TSV, FF_XML, FF_SYLK, FF_RLP, FF_SVG, FF_EPS,
-	FF_WMF, FF_RLW, FF_SSV};
-enum {LB_X_DATA = 0x01, LB_X_PARENT = 0x02, LB_Y_DATA = 0x10, LB_Y_PARENT = 0x20};
-enum {BUBBLE_CIRCLE = 0x000, BUBBLE_SQUARE = 0x001, BUBBLE_UPTRIA = 0x002,
-	BUBBLE_DOWNTRIA = 0x003, BUBBLE_UNITS = 0x000, BUBBLE_XAXIS = 0x010,
-	BUBBLE_YAXIS = 0x020, BUBBLE_DIAMET = 0x000, BUBBLE_CIRCUM = 0x100,
-	BUBBLE_AREA = 0x200};
-enum {DH_UNKNOWN, DH_12, DH_22, DH_19, DH_29, DH_39, DH_49, DH_59, DH_69, DH_79,
-	DH_89, DH_99, DH_18, DH_28, DH_38, DH_48, DH_58, DH_68, DH_78, DH_88, DH_DATA};
-enum {FE_NONE = 0x1000, FE_PARENT, FE_PLOT, FE_FLUSH, FE_DELOBJ, FE_REPLGO, FE_MUTATE};
-
-//drop line styles
-#define DL_LEFT          0x001
-#define DL_RIGHT         0x002
-#define DL_YAXIS         0x004
-#define DL_TOP           0x010
-#define DL_BOTTOM        0x020
-#define DL_XAXIS         0x040
-#define DL_CIRCULAR      0x100
-
-typedef struct {
-	int num;
-	char* display;
-	float convert;			//multiply to get mm
-	}tag_Units;
-
-typedef struct {
-	int x, y, z;
-	}POINT3D;
-
-typedef struct {
-	double Xmin;
-	double Ymax;
-	double Xmax;
-	double Ymin;
-	}fRECT;
-
-typedef struct {
-	double fx;
-	double fy;
-	double fz;
-	}fPOINT3D;
-
-typedef struct {
-	double fx;
-	double fy;
-	}lfPOINT;
-
-typedef struct {
-	lfPOINT sx, sy, sz;
-	}scaleINFO;
-
-typedef struct {
-	double finc, fp;
-	}RunLinePat;				//used for line patterns
-
-typedef struct {
-	double width, patlength;
-	DWORD color, pattern;
-	}LineDEF;
-
-typedef struct {
-	int type;					//pattern
-	DWORD color;
-	double scale;
-	LineDEF *hatch;
-	DWORD color2;
-	}FillDEF;
-
-typedef struct {
-	lfPOINT org;				//zoom origin
-	double scale;				//zoom factor
-	}ZoomDEF;
-
-typedef struct rlp_datetime {
-	int aday, year, doy, month, dom, dow, hours, minutes;
-	double seconds;
-}rlp_datetime;
-
-// Axis definitions are stored in the following structure
-// not to be confused with the Axis class grapic object
-// bits stored in flags havethe following meaning
-#define AXIS_NOTICKS      0x0			// no ticks at all
-#define AXIS_POSTICKS     0x1			// positive direction of ticks
-#define AXIS_NEGTICKS     0x2			// negative direction 
-#define AXIS_SYMTICKS     0x3			// ticks are symmetrical
-#define AXIS_GRIDLINE     0x4			// ticks control a gridline
-#define AXIS_MINORTICK    0x8			// its a small tick only
-#define AXIS_USER         0x00			// axis placement by user
-#define AXIS_LEFT         0x10			// left of plot
-#define AXIS_RIGHT        0x20			// right  -"-
-#define AXIS_TOP          0x30			// top    -"-
-#define AXIS_BOTTOM       0x40			// bottom -"-
-#define AXIS_AUTOSCALE    0x80			// rescale upon editing
-#define AXIS_INVERT       0x100			// axis top->bottom, right to left
-#define AXIS_AUTOTICK     0x200			// select tick s automatically
-#define AXIS_DEFRECT      0x400			// use axis to define drawing rectangle
-#define AXIS_LINEAR       0x0000		// transforms ...
-#define AXIS_LOG          0x1000
-#define AXIS_RECI         0x2000
-#define AXIS_SQR          0x3000
-#define AXIS_DATETIME     0x4000		// its a date- or time axis
-#define AXIS_X_DATA       0x10000		// loc.x is data coordinates
-#define AXIS_Y_DATA       0x20000       // loc.y is data coordinates
-#define AXIS_Z_DATA       0x40000       // loc.z is data coordinates
-#define AXIS_ANGULAR      0x100000      // angular (circular) axis
-#define AXIS_RADIAL       0x200000		// radial axis
-#define AXIS_3D           0x400000		// three dimensional axis
-
-typedef struct {
-	void *owner;				//owners are usually graph, output or axis classes
-	DWORD flags;
-	double min, max;
-	fPOINT3D loc[2];			//placement of axis coordinates
-	double Start, Step;			//used for linear axis
-	lfPOINT Center;				//of polar plot
-	double Radius;				//   -"-
-	int nBreaks;				//axis break ...
-	lfPOINT *breaks;
-	}AxisDEF;
-
-//Attributes for text properties
-//Text fonts
-enum {FONT_HELVETICA, FONT_TIMES, FONT_COURIER, FONT_GREEK};
-//Text styles
-#define TXA_VTOP        0
-#define TXA_VCENTER     4
-#define TXA_VBOTTOM     8
-#define TXA_HLEFT       0
-#define TXA_HCENTER     1
-#define TXA_HRIGHT      2
-#define TXM_OPAQUE      0
-#define TXM_TRANSPARENT 1
-#define TXS_NORMAL      0
-#define TXS_ITALIC      1
-#define TXS_BOLD        2
-#define TXS_UNDERLINE   4
-#define TXS_SUPER       8
-#define TXS_SUB			16
-typedef struct {
-	DWORD ColTxt, ColBg;				//colors ..
-	double fSize;						//Text size in units
-	double RotBL, RotCHAR;				//Rotation in degrees
-	int iSize;							//Text size is given in iSize as pix
-	int Align, Mode, Style;				//Text Alignement 0   1   2
-										//                4   5   6
-										//                8   9  10
-										//Mode 0 = opaque, 1 = transparent
-	int Font;
-	char *text;
-	}TextDEF;
-
-// Store mouse events in the following structure
-// Action defines the type of event:
-enum {MOUSE_LBDOWN, MOUSE_LBUP, MOUSE_LBDOUBLECLICK, MOUSE_MBDOWN, MOUSE_MBUP, 
-	MOUSE_MBDOUBLECLICK, MOUSE_RBDOWN, MOUSE_RBUP, MOUSE_RBDOUBLECLICK,
-	MOUSE_MOVE};
-typedef struct {
-	unsigned short StateFlags;	//  1 Mouse left button down
-								//  2       middle button down
-								//  4       right button down
-								//  8       shift pressed
-								// 16       control pressed
-	unsigned short Action;
-	int x, y;
-	} MouseEvent;
-
-//use this structure if type of data is not known
-typedef struct {
-	int type;
-	double value;
-	char *text;
-	double *a_data;
-	int a_count;
-	} anyResult;
-
-//the AccRange class allows to access data objects with a 'a1:b1' style
-class AccRange{
-public:
-	AccRange(char *asc);
-	~AccRange();
-	int CountItems();
-	bool GetFirst(int *x, int *y);
-	bool GetNext(int *x, int *y);
-	bool NextRow(int *y);
-	bool NextCol(int *x);
-	bool IsInRange(int x, int y);
-	bool BoundRec(RECT *rec);
-	char *RangeDesc(void *d, int style);	//d points to a DataObj class
-	int DataTypes(void *d, int *numerical, int *strings, int *datetime);
-
-private:
-	char *txt;
-	int x1, y1, x2, y2, cx, cy, curridx;
-
-	bool Reset();
-	bool Parse(int start);
-};
-
-class Triangle {
-public:
-	Triangle *next;
-	fPOINT3D pt[4];
-
-	void SetRect();
-	bool TestVertex(double x, double y);
-
-private:
-	double cx, cy, r2;				//circumcircle
-	fRECT rc;						//bounding rectangle
-	lfPOINT ld[3];					//line eqations
-};
-
-class Triangulate {
-public:
-	Triangle *trl;
-
-	Triangulate(Triangle *t_list);
-	bool AddEdge(fPOINT3D *p1, fPOINT3D *p2);
-	bool AddVertex(fPOINT3D *v);
-
-private:
-	typedef struct edge {
-		edge *next;
-		fPOINT3D p1, p2;
-	};
-	edge *edges;
-};
-
-class anyOutput{
-public:
-	int units;							//use units mm, inch ...
-	int MrkMode;						//existing mark on screen
-	int OC_type;						//specify display, printer clipboard ...
-	void *MrkRect;						//pointer to e.g. the marked rectangle
-	fRECT Box1;							//User drawing rectangle
-	lfPOINT Box1z;						// add 3D to Box1
-	RECT DeskRect;						//this is maximum size Rectangle
-	double ddx, ddy, ddz;				//convert to device coordinates
-	double hres, vres;					//resolution in dpi
-	double LineWidth;					//line width in units
-	int iLine;							//current width of line in pixels
-	DWORD dLineCol;						//current color of line;
-	DWORD dBgCol;						//color of background
-	DWORD dFillCol, dFillCol2;
-	DWORD dPattern;						//current line bit-pattern
-	TextDEF TxtSet;						//store current text settings here
-	RunLinePat RLP;						//continuous setings of pattern line
-	AxisDEF xAxis, yAxis, zAxis;		//axis and transform definitions
-	lfPOINT VPorg;						//zoom origin
-	double VPscale;						//zoom factor
-	int MenuHeight;						//ofset due to pull down menus
-	int cCursor;						//mouse coursor identifier
-	double rotM[3][3];					//rotation matrix for 3D
-	fPOINT3D rotC;						//rotation center
-	bool hasHistMenu;					//File History List
-	int HistMenuSize;					//     -"-
-	lfPOINT light_source;				//angles for shading
-	double light_vec[3][3];				//     -"-
-	double disp_x, disp_y;				//displacement on page
-
-	anyOutput();
-	void SetRect(fRECT rec, int units, AxisDEF *x_ax, AxisDEF *y_ax);
-	void UseAxis(AxisDEF *ax, int type);
-	void SetSpace(fPOINT3D*,fPOINT3D*,int,double*,fPOINT3D*,AxisDEF*,AxisDEF*,AxisDEF*);
-	void LightSource(double x, double y);
-	DWORD VecColor(double *plane_vec, DWORD col1, DWORD col2);
-	bool GetSize(RECT *rc);
-	virtual bool ActualSize(RECT *rc) {return GetSize(rc);};
-	double fx2fix(double x);
-	int fx2ix(double x){return (int)(0.5 + fx2fix(x));};
-	double fy2fiy(double y);
-	int fy2iy(double y){return (int)(0.5 + fy2fiy(y));};
-	bool fp2fip(lfPOINT *fdp, lfPOINT *fip);
-	bool fvec2ivec(fPOINT3D *v, fPOINT3D *iv);
-	bool cvec2ivec(fPOINT3D *v, fPOINT3D *iv);
-	bool uvec2ivec(fPOINT3D *v, fPOINT3D *iv);
-	double un2fix(double x);
-	int un2ix(double x) {return (int)(0.5 + un2fix(x));};
-	int co2ix(double x) {return un2ix(x) + iround(VPorg.fx);};
-	double co2fix(double x) {return un2fix(x) + VPorg.fx;};
-	double un2fiy(double y);
-	int un2iy(double y) {return (int)(0.5 + un2fiy(y));};
-	int co2iy(double y) {return un2iy(y) + iround(VPorg.fy);};
-	double co2fiy(double y) {return un2fiy(y) + VPorg.fy;};
-	double un2fiz(double z);
-	double fz2fiz(double z);
-	double fix2un(double fix);
-	double fiy2un(double fiy);
-	virtual bool SetLine(LineDEF *lDef){return false;};
-	bool GetLine(LineDEF *lDef);
-	virtual void Focus() {return;};
-	virtual void Caption(char *txt){return;};
-	virtual void MouseCursor(int cid, bool force){return;};
-	virtual bool SetScroll(bool, int, int, int, int){return false;};
-	virtual bool SetFill(FillDEF *fill){return false;};
-	virtual bool SetTextSpec(TextDEF *set);
-	virtual bool Erase(DWORD Color){return false;};
-	virtual bool StartPage(){return false;};
-	virtual bool EndPage(){return false;};
-	virtual bool Eject() {return false;};		//printers only
-	virtual bool UpdateRect(RECT *rc, bool invert){return false;};
-	virtual bool CopyBitmap(int x, int y, anyOutput* src, int sx, int sy,
-		int sw, int sh, bool invert){return false;};
-	virtual void ShowBitmap(int x, int y, anyOutput* src){return;};
-	bool ShowMark(void *rc, int Mode);
-	bool HideMark();
-	int CalcCursorPos(char *txt, POINT p, POINT *fit);
-	bool TextCursor(char *txt, POINT p, POINT *fit, int *pos, int disp);
-	bool PatLine(POINT p1, POINT p2);
-	virtual void ShowLine(POINT * pts, int cp, DWORD color) {return;};
-	virtual void ShowEllipse(POINT p1, POINT p2, DWORD color){return;}; 
-	virtual bool SetMenu(int type){return false;};
-	virtual void CheckMenu(int mid, bool check){return;};
-	virtual void FileHistory(){return;};
-	virtual bool oGetPix(int x, int y, DWORD *col){return false;};
-	virtual bool oDrawIcon(int type, int x, int y) {return false;};
-	virtual bool oGetTextExtent(char *text, int cb, int *width, int *height);
-	virtual bool oGetTextExtentW(w_char *text, int cb, int *width, int *height);
-	virtual bool oCircle(int x1, int y1, int x2, int y2, char *nam = 0L){return false;};
-	virtual bool oSphere(int cx, int cy, int r, POINT *pts, int cp, char *nam = 0L);
-	virtual bool oPolyline(POINT * pts, int cp, char *nam = 0L){return false;};
-	virtual bool oRectangle(int x1, int y1, int x2, int y2, char *nam = 0L){return false;};
-	virtual bool oSolidLine(POINT *p){return false;};
-	virtual bool oTextOut(int x, int y, char *txt, int cb){return false;};
-	virtual bool oTextOutW(int x, int y, w_char *txt, int cb){return false;};
-	virtual bool oPolygon(POINT *pts, int cp, char *nam = 0L){return false;};
-};
-
-enum {ET_UNKNOWN, ET_VALUE, ET_TEXT, ET_FORMULA, ET_ERROR, ET_BOOL, 
-	ET_DATE, ET_TIME, ET_DATETIME, ET_BUSY=0x100, ET_CIRCULAR=0x200, ET_EMPTY=0x400, 
-	ET_NODRAW=0x800, ET_NODRAW_EMPTY=0xc00};
-
-class EditText {
-public:
-	char *text, *ftext;
-	int Align, type, row, col;
-	void *parent;				//points to a data object: defined below
-	anyOutput *disp;
-	double Value;
-
-	EditText(void *par, char *msg, int r, int c);
-	~EditText();
-	bool AddChar(int c, anyOutput *Out, void *data_obj);
-	void Update(int select, anyOutput *Out, POINT *MousePos);
-	bool Redraw(anyOutput *Out, bool display);
-	void Mark(anyOutput *Out, int mark);
-	bool Command(int cmd, anyOutput *Out, void *data_obj);
-	bool GetValue(double *v);
-	bool GetText(char *t, int size, bool bTranslate);
-	bool GetResult(anyResult *r, bool use_last = false);
-	bool SetValue(double v);
-	bool SetText(char *t);
-	void SetRec(RECT *rc);
-	int GetX() {return loc.x;};
-	int GetY() {return loc.y;};
-	int GetRX() {return crb.x;};
-	int GetRY() {return crb.y;};
-	int Cursor() {return CursorPos;};
-	bool isValue();
-	bool isFormula();
-	bool isInRect(POINT *p) {return (p->x>loc.x && p->x<crb.x && p->y>loc.y && p->y<rb.y);};
-	bool hasMark() {return (m1 != m2 && m1 >= 0 && m2 >= 0);};
-
-private:
-	LineDEF *bgLine;
-	FillDEF *bgFill;
-	int length, CursorPos, m1, m2, mx1, mx2;
-	POINT loc, rb, crb;
-	DWORD TextCol;
-
-	void FindType();
-	void set_etracc();
-};
-
-class fmtText {
-
-typedef struct _fmt_txt_info {
-	int tag, uc, uc_len;
-	char *txt;
-}fmt_txt_info;
-
-typedef struct _fmt_uc_info {
-	int tag, cb;
-	w_char *uc_txt;
-}fmt_uc_info;
-
-public:
-	fmtText();
-	fmtText(anyOutput *o, int x, int y, char *txt);
-	~fmtText();
-	bool StyleAt(int idx,  TextDEF *txt_def, int *style, int *font);
-	int rightTag(char *txt, int cb);
-	int leftTag(char *txt, int cb);
-	int ucTag(char *txt, int cb, int *tl, int *ucc);
-	int ucLeft(char *txt, int cb, int *tl, int *ucc);
-	void cur_right(int *pos);
-	void cur_left(int *pos);
-	bool oGetTextExtent(anyOutput *o, int *width, int *height, int cb);
-	void SetText(anyOutput *o, char *txt, int *px, int *py);
-	void DrawText(anyOutput *o);
-	void EditMode(bool bEdit);
-
-private:
-	bool SetTextDef(TextDEF *td, int idx);
-	bool Parse();
-	void DrawBullet(anyOutput *o, int x, int y, int type, double size, DWORD lc, DWORD fc);
-	bool DrawFormattedW(anyOutput *o);
-	void DrawFormatted(anyOutput *o);
-
-	char *src;
-	POINT pos;
-	int n_split, n_split_W, uc_state;
-	DWORD flags;
-	fmt_txt_info *split_text;
-	fmt_uc_info *split_text_W;
-};
-
-class TextValue {
-
-typedef struct _TxtValItem {
-	unsigned int hv1, hv2;
-	char *text;
-	double val;
-}TextValItem;
-
-public:
-	TextValue();
-	TextValue(TextValItem **tvi, int ntvi);
-	~TextValue();
-	double GetValue(char* txt);
-	bool GetItem(int idx, char **text, double *value);
-	void Reset();
-	TextValue *Copy();
-	int Count() {return nitems;};
-
-private:
-	TextValItem **items;
-	int nitems;
-	double next, inc;
-};
-
-class RangeInfo {
-public:
-	int col_width, row_height, first_width;
-	RangeInfo(int sel, int cw, int rh, int fw, RangeInfo *next);
-	RangeInfo(int sel, char *range, RangeInfo *next);
-	~RangeInfo();
-	int SetWidth(int w);
-	int SetDefWidth(int w);
-	int SetHeight(int h);
-	int SetFirstWidth(int w);
-	int GetWidth(int col);
-	int GetHeight(int row);
-	int GetFirstWidth();
-	int Type(){return r_type;};
-	RangeInfo *Next(){return ri_next;};
-
-private:
-	int r_type;		// 0 default
-					// 1 column info
-					// 2 row info
-	RangeInfo *ri_next;
-	AccRange *ar;
-};
-
-class DataObj{
-public:
-	int cRows, cCols, c_disp, r_disp;
-	RangeInfo *ri;
-	EditText ***etRows;
-
-	DataObj();
-	~DataObj();
-	virtual bool Init(int nRows, int nCols);
-	virtual bool mpos2dpos(POINT *mp, POINT *dp, bool can_scroll){return false;};
-	virtual bool SetValue(int row, int col, double val);
-	virtual bool SetText(int row, int col, char *txt);
-	virtual bool GetValue(int row, int col, double *v);
-	virtual bool GetText(int row, int col, char *txt, int len, bool bTranslate = true);
-	char **GetTextPtr(int row, int col);
-	virtual bool GetResult(anyResult *r, int row, int col, bool use_last = false);
-	virtual bool GetSize(int *width, int *height);
-	virtual bool isEmpty(int row, int col);
-	virtual bool Select(POINT *p){return false;};
-	virtual bool WriteData(char *FileName) {return false;};
-	virtual bool AddCols(int nCols){return false;};
-	virtual bool AddRows(int nRows){return false;};
-	virtual bool ChangeSize(int nCols, int nRows, bool bUndo){return false;};
-	virtual bool Command(int cmd, void *tmpl, anyOutput *o){return false;};
-	virtual bool ReadData(char *, unsigned char *, int){return false;};
-	virtual void FlushData();
-	bool ValueRec(RECT *rc);
-};
-
-class StrData {
-public:
-	StrData(DataObj *par, RECT *rc = 0L);
-	~StrData();
-	bool GetSize(int *w, int *h);
-	void RestoreData(DataObj *dest);
-
-private:
-	int pw, ph;
-	RECT drc;
-	DataObj *src;
-	char ***str_data;
-};
-
-class HatchOut:public anyOutput {
-public:
-	HatchOut(anyOutput *Parent);
-	~HatchOut();
-	bool SetFill(FillDEF *fill);
-	bool StartPage();
-	bool oCircle(int x1, int y1, int x2, int y2, char *nam = 0L);
-	bool oSphere(int cx, int cy, int r, POINT *pts, int cp, char *nam = 0L);
-	bool oRectangle(int x1, int y1, int x2, int y2, char *nam = 0L);
-	bool oPolygon(POINT *pts, int cp, char *nam = 0L);
-
-private:
-	anyOutput *out;
-	double xbase, ybase;
-	LineDEF ParLineDef, MyLineDef;
-	bool ParInit;
-	int ho, ht;
-	DWORD ParLinPat;
-	RECT UseRect;
-	struct {
-		int cx, cy, r;
-		}circ_grad;
-
-	bool PrepareParent(bool Restore);
-	bool DoHatch();
-	bool MkPolyLine(POINT *p, anyOutput *o);
-	bool HatchLine(POINT p1, POINT p2);
-	bool HatchArc(int x, int y, int r, int qad, bool start);
-	bool IsInside(POINT p);
-	void Lines000();
-	void Lines090();
-	void Lines045();
-	void Lines315();
-	void Stipple(int type);
-	void Zigzag();
-	void Combs();
-	void BricksH();
-	void BricksV();
-	void Bricks045();
-	void Bricks315();
-	void Texture1();
-	void Texture2();
-	void Arcs(int type);
-	void Waves2(int type);
-	void Herringbone();
-	void Circles();
-	void Grass();
-	void Foam();
-	void Recs();
-	void Hash();
-	void CircGrad();
-};
-
-class GraphObj {
-public:
-	unsigned long Id;			//accepts an identifier during read from file
-								//  it is set to an object identifier after
-								//  construction
-	GraphObj *parent;
-	DataObj *data;
-	int type, moveable;
-	RECT rDims;
-	char *name;
-
-	GraphObj(GraphObj *par, DataObj *d);
-	virtual ~GraphObj();
-	virtual double GetSize(int select);
-	virtual bool SetSize(int select, double value){return false;};
-	virtual DWORD GetColor(int select);
-	virtual bool SetColor(int select, DWORD col) {return false;};
-	virtual void DoPlot(anyOutput *target){return;};
-	virtual void DoMark(anyOutput *target, bool mark){return;};
-	virtual bool Command(int cmd, void *tmpl, anyOutput *o){return false;};
-	virtual bool PropertyDlg(){return false;};
-	virtual void RegGO(void *n);
-	virtual bool FileIO(int rw) {return false;};
-	virtual void * ObjThere(int x, int y);
-	virtual void Track(POINT *p, anyOutput *o);
-	virtual double DefSize(int select);
-};
-
-class ssButton:public GraphObj {
-public:
-
-	ssButton(GraphObj *par, int x, int y, int w, int h);
-	~ssButton();
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *target, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-
-private:
-	bool bLBdown, bSelected, bMarked;
-	TextDEF TextDef;
-	LineDEF Line;
-	FillDEF Fill;
-};
-
-class dragHandle:public GraphObj {
-public:
-
-	dragHandle(GraphObj *par, int type);
-	~dragHandle();
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void * ObjThere(int x, int y);
-	void Track(POINT *p, anyOutput *o);
-
-private:
-	RECT upd, drec, *minRC, *maxRC;
-	LineDEF LineDef;
-	FillDEF FillDef;
-};
-
-class dragRect:public GraphObj {
-public:
-
-	dragRect(GraphObj *par, int type);
-	~dragRect();
-	double GetSize(int select){return parent->GetSize(select);};
-	DWORD GetColor(int select){return parent->GetColor(select);};
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void*tmpl, anyOutput *o);
-	void * ObjThere(int x, int y);
-
-private:
-	dragHandle **handles;
-};
-
-class Drag3D:public GraphObj {
-public:
-	Drag3D(GraphObj *par);
-	~Drag3D();
-	double GetSize(int select){return parent->GetSize(select);};
-	void DoPlot(anyOutput *o);
-	void * ObjThere(int x, int y);
-
-private:
-	dragHandle **handles;
-};
-
-class FrmRect:public GraphObj {
-public:
-
-	FrmRect(GraphObj *par, fRECT *limRC, fRECT *cRC, fRECT *chld);
-	~FrmRect();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoMark(anyOutput *o, bool mark);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg(){return parent ? parent->Command(CMD_CONFIG, 0L, 0L) : false;};
-	void * ObjThere(int x, int y);
-	void Track(POINT *p, anyOutput *o);
-
-private:
-	RECT *minRC, *maxRC;
-	anyOutput *mo;
-	RECT mrc;
-	dragRect *drag;
-	bool swapX, swapY;
-	fRECT *limRC, *cRC, *chldRC, CurrRect;
-	LineDEF Line, FillLine;
-	FillDEF Fill;
-};
-
-class svgOptions:public GraphObj{
-public:
-	svgOptions(int src);
-	~svgOptions();
-	bool FileIO(int rw);
-
-private:
-	char *script, *svgattr;
-};
-
-class Symbol:public GraphObj{
-public:
-	int idx;
-
-	Symbol(GraphObj *par, DataObj *d, double x, double y, int which, 
-		int xc = -1, int xr = -1, int yc = -1, int yr = -1);
-	Symbol(int src);
-	~Symbol();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	DWORD GetColor(int select);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *target);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	lfPOINT fPos;
-	POINT *ssRef;
-	long cssRef;
-	double size;
-	LineDEF SymLine;
-	FillDEF SymFill;
-	TextDEF *SymTxt;
-};
-
-class Bubble:public GraphObj{
-public:
-	Bubble(GraphObj *par, DataObj *d, double x, double y, double s, 
-		int which, FillDEF *fill, LineDEF *outline, int xc = -1, 
-		int xr = -1, int yc = -1, int yr = -1, int sc = -1, int sr = -1);
-	Bubble(int src);
-	~Bubble();
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	bool DoAutoscale(anyOutput *o);
-
-	lfPOINT fPos;
-	double fs;
-	LineDEF BubbleLine, BubbleFillLine;
-	FillDEF BubbleFill;
-	POINT pts[5];
-	POINT *ssRef;
-	long cssRef;
-};
-
-class Bar:public GraphObj {
-public:
-	Bar(GraphObj *par, DataObj *d, double x, double y, int which, 
-		int xc = -1, int xr = -1, int yc = -1, int yr = -1, char *desc = 0L);
-	Bar(int src);
-	~Bar();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *target);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	anyOutput *mo;
-	RECT mrc;
-	double size;
-	LineDEF BarLine, HatchLine;
-	FillDEF BarFill;
-	lfPOINT fPos, BarBase;
-	POINT *ssRef;
-	long cssRef;
-};
-
-class DataLine:public GraphObj{
-public:
-	bool isPolygon, dirty;
-	lfPOINT *Values;
-	lfPOINT min, max;
-	LineDEF LineDef;
-	long nPnt, nPntSet, cp;
-	DWORD BgColor;
-	POINT *pts;
-	char *ssXref, *ssYref;
-	anyOutput *mo;
-	RECT mrc;
-
-	DataLine(GraphObj *par, DataObj *d, char *xrange=0L, char *yrange=0L, char *name=0L);
-	DataLine(GraphObj *par, DataObj *d, lfPOINT *val, long nval, char *name);
-	DataLine(int src);
-	~DataLine();
-	bool SetColor(int select, DWORD col);
-	virtual void DoPlot(anyOutput *target);
-	virtual void DoMark(anyOutput *o, bool mark);
-	virtual bool Command(int cmd, void *tmpl, anyOutput *o);
-	virtual bool PropertyDlg();
-	virtual bool FileIO(int rw);
-
-	void FileValues(char *name, int type, double start, double step);
-	void SetValues();
-	void LineData(lfPOINT *val, long nval);
-	void DrawCurve(anyOutput *target);
-	void DrawSpline(anyOutput *target);
-};
-
-class DataPolygon:public DataLine{
-public:
-	DataPolygon(GraphObj *par, DataObj *d, char *xrange=0L, char *yrange=0L, char *name=0L);
-	DataPolygon(int src);
-	~DataPolygon();
-	void DoPlot(anyOutput *target);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	FillDEF pgFill;
-	LineDEF pgFillLine;
-};
-
-class RegLine:public GraphObj{
-public:
-	RegLine(GraphObj *par, DataObj *d, lfPOINT *values, long n, int type);
-	RegLine(int src);
-	~RegLine();
-	double GetSize(int select);
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-	void Recalc(lfPOINT *values, long n);
-	LineDEF *GetLine(){return &LineDef;};
-
-private:
-	long nPoints, cp;
-	double mx, my;
-	LineDEF LineDef;
-	fRECT lim, uclip;
-	lfPOINT l1, l2, l3, l4, l5;
-	DWORD BgColor;
-	POINT *pts;
-};
-
-class SDellipse:public GraphObj{
-public:
-	SDellipse(GraphObj *par, DataObj *d, lfPOINT *values, long n, int sel);
-	SDellipse(int src);
-	~SDellipse();
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-	void Recalc(lfPOINT *values, long n);
-
-private:
-	long nPoints, cp;
-	double sd1, sd2, mx, my;
-	POINT *pts;
-	LineDEF LineDef;
-	fRECT lim;
-	lfPOINT *val;
-	RegLine *rl;
-};
-
-class ErrorBar:public GraphObj{
-public:
-	ErrorBar(GraphObj *par, DataObj *d, double x, double y, double err, int type,
-		int xc=-1, int xr=-1, int yc=-1, int yr=-1, int ec=-1, int er=-1);
-	ErrorBar(int src);
-	~ErrorBar();
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *target);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	anyOutput *mo;
-	RECT mrc;
-	lfPOINT fPos;
-	double ferr, SizeBar;
-	POINT ebpts[6];
-	LineDEF ErrLine;
-	POINT *ssRef;
-	long cssRef;
-};
-
-class Arrow:public GraphObj {
-public:
-	Arrow(GraphObj *par, DataObj *d, lfPOINT fp1, lfPOINT fp2, int which = 0,
-		int xc1=-1, int xr1=-1, int yc1=-1, int yr1=-1, int xc2=-1, int xr2=-1,
-		int yc2=-1, int yr2=-1);
-	Arrow(int src);
-	~Arrow();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	DWORD GetColor(int select){return LineDef.color;};
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-	void * ObjThere(int x, int y);
-	void Track(POINT *p, anyOutput *o);
-
-private:
-	anyOutput *mo;
-	RECT mrc;
-	dragHandle *dh1, *dh2;
-	POINT pts[5];
-	lfPOINT pos1, pos2;
-	double cw, cl;
-	LineDEF LineDef;
-	POINT *ssRef;
-	long cssRef;
-	bool bModified;
-
-	void Redraw(anyOutput *o);
-};
-
-class Box:public GraphObj {
-public:
-	Box(GraphObj *par, DataObj *d, lfPOINT fp1, lfPOINT fp2, int which, 
-		int xc1=-1, int xr1=-1, int yc1=-1, int yr1=-1, int xc2=-1, int xr2=-1,
-		int yc2=-1, int yr2=-1);
-	Box(int src);
-	~Box();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	double size;
-	lfPOINT pos1, pos2;
-	POINT pts[5];
-	LineDEF Outline, Hatchline;
-	FillDEF Fill;
-	POINT *ssRef;
-	long cssRef;
-};
-
-class Whisker:public GraphObj {
-public:
-	Whisker(GraphObj *par, DataObj *d, lfPOINT fp1, lfPOINT fp2, int which,
-		int xc1=-1, int xr1=-1, int yc1=-1, int yr1=-1, int xc2=-1, int xr2=-1,
-		int yc2=-1, int yr2=-1);
-	Whisker(int src);
-	~Whisker();
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	anyOutput *mo;
-	RECT mrc;
-	double size;
-	POINT pts[6];
-	lfPOINT pos1, pos2;
-	LineDEF LineDef;
-	POINT *ssRef;
-	long cssRef;
-};
-
-class DropLine:public GraphObj{
-public:
-	DropLine(GraphObj *par, DataObj *d, double x, double y, int which, int xc = -1, 
-		int xr = -1, int yc = -1, int yr = -1);
-	DropLine(int src);
-	~DropLine();
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	lfPOINT fPos;
-	POINT pts[4];
-	LineDEF LineDef;
-	POINT *ssRef;
-	long cssRef;
-	bool bModified;
-};
-
-class line_segment:public GraphObj{
-public:
-	line_segment(GraphObj *par, DataObj *d, LineDEF *ld, POINT3D *p1, POINT3D *p2);
-	~line_segment();
-	double GetSize(int select);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void * ObjThere(int x, int y);
-
-private:
-	LineDEF Line;
-	POINT3D **ldata;
-	fPOINT3D fmin, fmax;
-	int *nldata, nli, ndf_go;
-	double prop;
-	bool bDrawDone;
-	GraphObj *co, **df_go;
-
-	void DoClip(GraphObj *co);
-};
-
-class sph_scanline:public GraphObj{
-public:
-	int rad;
-	bool vert;
-
-	sph_scanline(POINT3D *center, int radius, bool bVert);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-
-	void DoClip(GraphObj *co);
-	bool GetPoint(POINT *p, int sel);
-
-private:
-	POINT3D p1, p2, cent;
-	bool bValid1, bValid2;
-};
-
-class Sphere:public GraphObj{
-public:
-	Sphere(GraphObj *par, DataObj *d, int sel, double x, double y, double z, double r,
-		int xc = -1, int xr = -1, int yc = -1, int yr = -1, int zc = -1, int zr = -1,
-		int rc = -1, int rr = -1);
-	Sphere(int src);
-	~Sphere();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	DWORD GetColor(int select);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	int ix, iy, rx, ry, nscl;
-	LineDEF Line;
-	FillDEF Fill;
-	fPOINT3D fPos, fip;
-	double size;
-	POINT *ssRef;
-	long cssRef;
-	GraphObj *co;
-	bool bModified, bDrawDone;
-	sph_scanline **scl;
-
-	void DoClip(GraphObj *co);
-	void DrawPG(anyOutput *o, int start);
-};
-
-class plane:public GraphObj{
-public:
-	plane(GraphObj *par, DataObj *d, fPOINT3D *pts, int nPts, LineDEF *line, 
-		FillDEF *fill);
-	~plane();
-	double GetSize(int select);
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void * ObjThere(int x, int y);
-
-	bool GetPolygon(POINT3D **pla, int *npt, int sel);
-	double *GetVec() {return PlaneVec;};
-
-private:
-	LineDEF Line;
-	FillDEF Fill;
-	double *PlaneVec;
-	POINT3D **ldata, ReqPoint;
-	int *nldata, nli, n_ipts, n_lines;
-	POINT *ipts;
-	lfPOINT xBounds, yBounds, zBounds;
-	bool bDrawDone, bReqPoint;
-	GraphObj *co;
-	line_segment **lines;
-	long totalArea;
-
-	void DoClip(GraphObj *co);
-	bool IsValidPG(POINT3D *pg, int npg);
-};
-
-class Plane3D:public GraphObj {
-public:
-	Plane3D(GraphObj *par, DataObj *da, fPOINT3D *pt, long npt);
-	Plane3D(int src);
-	~Plane3D();
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	plane *ipl;
-	fPOINT3D *dt, *pts;
-	long ndt;
-	LineDEF Line;
-	FillDEF Fill;
-};
-
-class Brick:public GraphObj{
-public:
-	Brick(GraphObj *par, DataObj *da, double x, double y, double z, 
-		double d, double w, double h, DWORD flags, int xc = -1,
-		int xr = -1, int yc = -1, int yr = -1, int zc = -1, int zr = -1,
-		int dc = -1, int dr = -1, int wc = -1, int wr = -1, int hc = -1,
-		int hr = -1);
-	Brick(int src);
-	~Brick();
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	LineDEF Line;
-	FillDEF Fill;
-	fPOINT3D fPos;
-	double depth, width, height;
-	DWORD flags;
-	POINT *ssRef;
-	long cssRef;
-	plane **faces;
-	anyOutput *mo;
-	RECT mrc;
-	bool bModified;
-};
-
-class DropLine3D:public GraphObj{
-public:
-	DropLine3D(GraphObj *par, DataObj *d, fPOINT3D *p1, int xc = -1,
-		int xr = -1, int yc = -1, int yr = -1, int zc = -1, int zr = -1);
-	DropLine3D(int src);
-	~DropLine3D();
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	line_segment *ls[6];
-	POINT mpts[6][2];
-	LineDEF Line;
-	fPOINT3D fPos;
-	POINT *ssRef;
-	long cssRef;
-	bool bModified;
-	anyOutput *mo;
-	RECT mrc;
-};
-
-class Arrow3D:public GraphObj{
-public:
-	Arrow3D(GraphObj *par, DataObj *d, fPOINT3D *p1, fPOINT3D *p2, int xc1 = -1,
-		int xr1 = -1, int yc1 = -1, int yr1 = -1, int zc1 = -1, int zr1 = -1, 
-		int xc2 = -1, int xr2 = -1, int yc2 = -1, int yr2 = -1, int zc2 = -1, 
-		int zr2 = -1);
-	Arrow3D(int src);
-	~Arrow3D();
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	line_segment *ls[3];
-	plane *cap;
-	double cw, cl;
-	POINT mpts[3][2];
-	LineDEF Line;
-	fPOINT3D fPos1, fPos2;
-	POINT *ssRef;
-	long cssRef;
-	bool bModified;
-};
-
-class Line3D:public GraphObj{
-public:
-	LineDEF Line;
-
-	Line3D(GraphObj *par, DataObj *d, char *rx, char *ry, char *rz);
-	Line3D(GraphObj *par, DataObj *d, fPOINT3D *pt, int n_pt, int xc1 = -1,
-		int xr1 = -1, int yc1 = -1, int yr1 = -1, int zc1 = -1, int zr1 = -1, 
-		int xc2 = -1, int xr2 = -1, int yc2 = -1, int yr2 = -1, int zc2 = -1, 
-		int zr2 = -1);
-	Line3D(int src);
-	~Line3D();
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	line_segment **ls;
-	fPOINT3D *values, min, max;
-	POINT *pts;
-	long nPts, npts;
-	char *x_range, *y_range, *z_range;
-	POINT *ssRef;
-	long cssRef;
-	bool bModified;
-	anyOutput *mo;
-	RECT mrc;
-
-	void DoUpdate();
-};
-
-class Label:public GraphObj{
-public:
-	Label(GraphObj *par, DataObj *d, double x, double y, TextDEF *td, DWORD flg,
-		int xc = -1, int xr = -1, int yc = -1, int yr = -1, int tc = -1, int tr = -1);
-	Label(int src);
-	~Label();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-	void * ObjThere(int x, int y);
-	void Track(POINT *p, anyOutput *o);
-
-	bool CalcRect(anyOutput *o);
-	void RedrawEdit(anyOutput *o);
-	void ShowCursor(anyOutput *o);
-	bool AddChar(int ci, anyOutput *o);
-	void CalcCursorPos(int x, int y, anyOutput *o);
-	void SetModified();
-	void DoPlotText(anyOutput *o);
-	bool CheckMark();
-	TextDEF *GetTextDef(){return &TextDef;};
-
-private:
-	lfPOINT fPos, fDist;
-	double si, csi, curr_z;
-	DWORD flags, bgcolor;
-	TextDEF TextDef;
-	LineDEF bgLine;
-	int ix, iy, m1, m2, CursorPos;
-	bool is3D;
-	RECT Cursor, rm1, rm2;
-	POINT pts[5];
-	POINT *ssRef;
-	long cssRef;
-	anyOutput *defDisp;
-	bool bModified, bBGvalid;
-};
-
-class mLabel:public GraphObj{
-public:
-	mLabel(GraphObj *, DataObj *, double, double, TextDEF *, char *, int, DWORD);
-	mLabel(GraphObj *, DataObj *, double, double, TextDEF *, char *);
-	mLabel(int src);
-	~mLabel();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void RegGO(void *n);
-	bool FileIO(int rw);
-	void Track(POINT *p, anyOutput *o);
-
-private:
-	lfPOINT fPos, fDist, cPos, cPos1, dist;
-	double si, csi, curr_z, lspc;
-	DWORD flags, undo_flags;
-	TextDEF TextDef;
-	long nLines;
-	int cli;
-	bool is3D;
-	Label **Lines;
-};
-
-class TextFrame:public GraphObj{
-	enum {TF_MAXLINE=120};
-public:
-	TextFrame(GraphObj *parent, DataObj *data, lfPOINT *p1, lfPOINT *p2, char *txt);
-	TextFrame(int src);
-	~TextFrame();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	void DoMark(anyOutput *o, bool mark);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-	void * ObjThere(int x, int y);
-	void Track(POINT *p, anyOutput *o);
-
-private:
-	fRECT pad;
-	RECT ipad, Cursor, *tm_rec;
-	fmtText fmt_txt;
-	int nlines, linc, tm_c, csize, cpos;
-	bool bModified, bResize, has_m1, has_m2;
-	unsigned char c_char, m1_char, m2_char;
-	double lspc;
-	lfPOINT pos1, pos2;
-	POINT cur_pos, m1_pos, m2_pos, m1_cpos, m2_cpos;
-	TextDEF TextDef;
-	dragRect *drc;
-	unsigned char *text, **lines;
-	LineDEF Line, FillLine;
-	FillDEF Fill;
-
-	void text2lines(anyOutput *o);
-	void lines2text();
-	void ShowCursor(anyOutput *o);
-	void AddChar(anyOutput *o, int c);
-	void DelChar(anyOutput *o);
-	void CalcCursorPos(int x, int y, anyOutput *o);
-	void ReplMark(anyOutput *o, char *ntext);
-	void procTokens();
-	bool DoPaste(anyOutput *o);
-	void TextMark(anyOutput *o, int mode);
-	bool CopyText(anyOutput *o, bool b_cut);
-};
-
-class segment:public GraphObj{
-public:
-	segment(GraphObj *par, DataObj *d, lfPOINT *c, double r1, double r2, double a1, double a2);
-	segment(int src);
-	~segment();
-	bool SetSize(int select, double value);
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-	void * ObjThere(int x, int y);
-	void Track(POINT *p, anyOutput *o);
-
-private:
-	lfPOINT fCent;
-	long nPts;
-	double radius1, radius2, angle1, angle2, shift;
-	LineDEF segLine, segFillLine;
-	FillDEF segFill;
-	POINT *pts;
-	bool bModified;
-};
-
-class polyline:public GraphObj {
-public:
-	dragHandle **pHandles;
-	lfPOINT *Values;
-	long nPoints, nPts;
-	POINT *pts;
-	LineDEF pgLine, pgFillLine;
-	FillDEF pgFill;
-	bool bModified;
-
-	polyline(GraphObj *par, DataObj *d, lfPOINT *fpts, int cpts);
-	polyline(int src);
-	~polyline();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	DWORD GetColor(int select);
-	virtual void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	virtual bool PropertyDlg();
-	virtual bool FileIO(int rw);
-	void * ObjThere(int x, int y);
-	void Track(POINT *p, anyOutput *o);
-
-private:
-	void ShowPoints(anyOutput *o);
-};
-
-class Bezier:public polyline {
-public:
-	Bezier(GraphObj *par, DataObj *d, lfPOINT *fpts, int cpts, int mode, double res);
-	Bezier(int src);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool FileIO(int rw);
-
-private:
-	void AddPoints(int n, lfPOINT *p);
-
-	void FitCurve(lfPOINT *d, int npt, double error);
-	void IpolBez(lfPOINT *d, lfPOINT tHat1, lfPOINT tHat2);
-	void FitCubic(lfPOINT *d, int first, int last, lfPOINT tHat1, lfPOINT tHat2, double error);
-	void RemovePoint(lfPOINT *d, int sel);
-	void GenerateBezier(lfPOINT *d, int first, int last, double * uPrime, lfPOINT tHat1, lfPOINT tHat2, lfPOINT *bezCurve);
-	double *Reparameterize(lfPOINT *d, int first, int last, double *u, lfPOINT *bezCurve);
-	lfPOINT fBezier(int degree, lfPOINT *V, double t);
-	double *ChordLengthParameterize(lfPOINT *d, int first, int last);
-	double ComputeMaxError(lfPOINT *d, int first, int last, lfPOINT *bezCurve, double *u, int *splitPoint);
-};
-
-class polygon:public polyline {
-public:
-	polygon(GraphObj *par, DataObj *d, lfPOINT *fpts, int cpts);
-	polygon(int src):polyline(src){};
-	bool PropertyDlg();
-};
-
-class rectangle:public GraphObj {
-public:
-	lfPOINT fp1, fp2;
-	LineDEF Line, FillLine;
-	FillDEF Fill;
-	double rad;
-	bool bModified;
-
-	rectangle(GraphObj *par, DataObj *d, lfPOINT *p1, lfPOINT *p2);
-	rectangle(int src);
-	~rectangle();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	DWORD GetColor(int select){return Line.color;};
-	void DoMark(anyOutput *o, bool mark);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-	void * ObjThere(int x, int y);
-	void Track(POINT *p, anyOutput *o);
-
-private:
-	POINT *pts;
-	long nPts;
-	dragRect *drc;
-	void PlotRoundRect(anyOutput *o);
-};
-
-class ellipse:public rectangle {
-public:
-	ellipse(GraphObj *par, DataObj *d, lfPOINT *p1, lfPOINT *p2);
-	ellipse(int src);
-};
-
-class roundrec:public rectangle {
-public:
-	roundrec(GraphObj *par, DataObj *d, lfPOINT *p1, lfPOINT *p2);
-	roundrec(int src);
-};
-
-class LegItem:public GraphObj{
-public:
-	DWORD flags;
-
-	LegItem(GraphObj *par, DataObj *d, LineDEF *ld, LineDEF *lf, FillDEF *fill, char *desc);
-	LegItem(GraphObj *par, DataObj *d, LineDEF *ld, Symbol *sy);
-	LegItem(GraphObj *par, DataObj *d, LineDEF *ld, int err, char *desc);
-	LegItem(int src);
-	~LegItem();
-	double GetSize(int select);
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void RegGO(void *n);
-	bool FileIO(int rw);
-	void Track(POINT *p, anyOutput *o);
-
-	bool HasFill(LineDEF *ld, FillDEF *fd, char *desc);
-	bool HasSym(LineDEF *ld, GraphObj *sy);
-	bool HasErr(LineDEF *ld, int err);
-
-private:
-	LineDEF DataLine, OutLine, HatchLine;
-	FillDEF Fill;
-	Symbol *Sym;
-	Label *Desc;
-	RECT hcr;
-
-	void DefDesc(char *txt);
-};
-
-class Legend:public GraphObj{
-public:
-	Legend(GraphObj *par, DataObj *d);
-	Legend(int src);
-	~Legend();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void RegGO(void *n);
-	bool FileIO(int rw);
-	void Track(POINT *p, anyOutput *o);
-
-	bool HasFill(LineDEF *ld, FillDEF *fd, char *desc);
-	bool HasSym(LineDEF *ld, GraphObj *sy, char *desc);
-	bool HasErr(LineDEF *ld, int err, char *desc);
-
-private:
-	lfPOINT pos, lb_pos;
-	RECT trc;
-	anyOutput *to;
-	fRECT B_Rect, C_Rect, D_Rect, E_Rect, F_Rect;
-	long nItems;
-	bool hasLine;
-	LegItem **Items;
-};
-
-class Plot:public GraphObj{
-public:
-	fRECT Bounds;					//contains minima and maxima for x and y
-	bool dirty;						//rescale before redraw;
-	int use_xaxis, use_yaxis, use_zaxis;		//this plot uses its own axes
-	lfPOINT xBounds, yBounds, zBounds;	//like Bounds but in 3D space
-	int hidden;						//plot (layer) is not visible
-	int x_dtype, y_dtype, z_dtype;	//data types
-	char *x_info, *y_info, *z_info;	//descriptor used e.g. for axis label or legend
-	char *data_desc;				//descriptor for data, used for legend etc
-	TextValue *x_tv, *y_tv;			//TextValue object for ordinal axes
-
-	Plot(GraphObj *par, DataObj *d);
-	virtual double GetSize(int select);
-	virtual bool SetSize(int select, double value){return false;};
-	virtual DWORD GetColor(int select);
-	virtual bool SetColor(int select, DWORD col){return false;};
-	virtual void DoPlot(anyOutput *o){return;};
-	virtual bool Command(int cmd, void *tmpl, anyOutput *o){return false;};
-	virtual bool PropertyDlg(){return false;};
-
-	void CheckBounds(double x, double y);
-	bool UseAxis(int idx);
-	void ApplyAxes(anyOutput *o);
-	void CheckBounds3D(double x, double y, double z);
-	bool SavVarObs(GraphObj **gol, long ngo, DWORD flags);
-	DataObj *CreaCumData(char *xr, char *yr, int mode, double base);
-};
-
-class PlotScatt:public Plot{
-public:
-	char *xRange, *yRange;
-	long nPoints;
-	int DefSym;
-	lfPOINT BarDist;
-	Bar **Bars;
-	Symbol **Symbols;
-	DataLine *TheLine;
-	ErrorBar **Errors;
-	Label **Labels;
-
-	PlotScatt(GraphObj *par, DataObj *d, DWORD presel);
-	PlotScatt(GraphObj *par, DataObj *d, int cBars, Bar **bars, ErrorBar **errs);
-	PlotScatt(GraphObj *par, DataObj *d, int nPts, Symbol **sym, DataLine *lin);
-	PlotScatt(int src);
-	~PlotScatt();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *target);
-	virtual bool Command(int cmd, void *tmpl, anyOutput *o);
-	virtual bool PropertyDlg();
-	void RegGO(void *n);
-	virtual bool FileIO(int rw);
-
-	bool ForEach(int cmd, void *tmp, anyOutput *o);
-
-private:
-	DWORD DefSel;
-	char *ErrRange, *LbRange;
-	Arrow **Arrows;
-	DropLine **DropLines;
-
-	bool CreateBarChart();
-};
-
-class xyStat:public PlotScatt{
-public:
-	xyStat(GraphObj *par, DataObj *d);
-	xyStat(int src);
-	~xyStat();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	virtual bool FileIO(int rw);
-
-	void CreateData();
-
-private:
-	double ci;
-	DataObj *curr_data;
-	char *case_prefix;
-
-};
-
-class BarChart:public PlotScatt{
-public:
-	BarChart(GraphObj *par, DataObj *d);
-};
-
-class FreqDist:public Plot {
-public:
-	FreqDist(GraphObj *par, DataObj *d);
-	FreqDist(GraphObj *par, DataObj *d, char* range, bool bOnce);
-	FreqDist(GraphObj *par, DataObj *d, double *vals, int nvals, int nclasses);
-	FreqDist(int src);
-	~FreqDist();
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-private:
-	double dmin, dmax, start, step;
-	long nPlots;
-	char *ssRef;
-	LineDEF BarLine, HatchLine;
-	FillDEF BarFill;
-	DataObj *curr_data;
-	GraphObj **plots;
-
-	void ProcData(int sel);
-};
-
-class Regression:public Plot{
-public:
-	Regression(GraphObj *par, DataObj *d);
-	Regression(int src);
-	~Regression();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *target);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-private:
-	long nPoints;
-	char *xRange, *yRange;
-	Symbol **Symbols;
-	RegLine *rLine;
-	SDellipse *sde;
-
-	void Recalc();
-};
-
-class BubblePlot:public Plot{
-public:
-	BubblePlot(GraphObj *par, DataObj *d);
-	BubblePlot(int src);
-	~BubblePlot();
-	void DoPlot(anyOutput *target);
-	DWORD GetColor(int select);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-private:
-	long nPoints;
-	LineDEF BubbleLine, BubbleFillLine;
-	FillDEF BubbleFill;
-	Bubble **Bubbles;
-};
-
-class PolarPlot:public Plot{
-public:
-	PolarPlot(GraphObj *par, DataObj *d);
-	PolarPlot(int src);
-	~PolarPlot();
-	double GetSize(int select);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-	bool AddPlot();
-	bool Config();
-
-private:
-	int nPlots, nAxes;
-	double offs;
-	anyOutput *CurrDisp;
-	fRECT CurrRect;
-	LineDEF FillLine;
-	FillDEF Fill;
-	Plot **Plots;
-	GraphObj **Axes;			//Axis not yet defined
-};
-
-class BoxPlot:public Plot {
-public:
-	BoxPlot(GraphObj *par, DataObj *d);
-	BoxPlot(int src);
-	BoxPlot(GraphObj *par, DataObj *dt, int mode, int c1, int c2, int c3, char *box_name);
-	~BoxPlot();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-	bool ForEach(int cmd, void *tmp, anyOutput *o);
-	void CreateData();
-
-private:
-	char *xRange, *yRange, *case_prefix;
-	long nPoints;
-	double ci_box, ci_err;
-	DataObj *curr_data;
-	lfPOINT BoxDist;
-	Box **Boxes;
-	Whisker **Whiskers;
-	Symbol **Symbols;
-	Label **Labels;
-	DataLine *TheLine;
-};
-
-class DensDisp:public Plot {
-public:
-	DensDisp(GraphObj *par, DataObj *d);
-	DensDisp(int src);
-	~DensDisp();
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-private:
-	LineDEF DefLine, DefFillLine;
-	FillDEF DefFill;
-	long nPoints;
-	char *xRange, *yRange;
-	Box **Boxes;
-
-	void DoUpdate();
-
-};
-
-class StackBar:public Plot{
-public:
-	int numPlots, numXY, numPG, numPL;
-	BoxPlot **Boxes;
-	PlotScatt **xyPlots;
-	DataPolygon **Polygons;
-	DataLine **Lines;
-	lfPOINT dspm;
-
-	StackBar(GraphObj *par, DataObj *d);
-	StackBar(int src);
-	~StackBar();
-	bool SetSize(int select, double value);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-private:
-	char *ssXrange, *ssYrange;
-	double StartVal;
-	int cum_data_mode;
-	DataObj *CumData;
-};
-
-class StackPG:public StackBar{
-public:
-	StackPG(GraphObj *par, DataObj *d);
-};
-
-class GroupBars:public StackBar{
-public:
-	GroupBars(GraphObj *par, DataObj *d, int type):StackBar(par, d){mode = type;};
-	bool PropertyDlg();
-
-private:
-	int mode;
-};
-
-class Waterfall:public StackBar{
-public:
-	Waterfall(GraphObj *par, DataObj *d);
-	bool PropertyDlg();
-};
-
-class MultiLines:public StackBar{
-public:
-	MultiLines(GraphObj *par, DataObj *d);
-	bool PropertyDlg();
-};
-
-class PieChart:public Plot {
-public:
-	PieChart(GraphObj *par, DataObj *d);
-	PieChart(int src);
-	~PieChart();
-	bool SetSize(int select, double value);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-	void DoUpdate();
-
-private:
-	long nPts;
-	char *ssRefA, *ssRefR;
-	lfPOINT CtDef;
-	double FacRad;
-	segment **Segments;
-};
-
-class RingChart:public PieChart {
-public:
-	RingChart(GraphObj *par, DataObj *d);
-};
-
-class GoGroup:public Plot {
-public:
-	GraphObj **Objects;
-	int nObs;
-	lfPOINT fPos;
-
-	GoGroup(GraphObj *par, DataObj *d);
-	GoGroup(int src);
-	~GoGroup();
-	double GetSize(int select);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void RegGO(void *n);
-	bool FileIO(int rw);
-};
-
-class StarChart:public GoGroup {
-public:
-	StarChart(GraphObj *par, DataObj *d);
-	bool PropertyDlg();
-
-private:
-};
-
-class Ribbon:public Plot {
-public:
-	Ribbon(GraphObj *par, DataObj *d, double z, double width, char *xr, char *yr);
-	Ribbon(GraphObj *par, DataObj *d, int which, char *xr, char *yr, char *zr);
-	Ribbon(GraphObj *par, DataObj *d, GraphObj **go, int ngo);
-	Ribbon(int src);
-	~Ribbon();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-private:
-	long nPlanes, nVal;
-	double z_value, z_width, relwidth;
-	char *ssRefX, *ssRefY, *ssRefZ;
-	FillDEF Fill;
-	LineDEF Line;
-	fPOINT3D *values;
-	Plane3D **planes;
-
-	void CreateObs();
-	void UpdateObs(bool bNewData);
-};
-
-class Grid3D:public Plot {
-public:
-	Grid3D(GraphObj *par, DataObj *d, int sel, double x1=0.0, double xstep=1.0, double z1=0.0, double zstep=1.0);
-	Grid3D(int src);
-	~Grid3D();
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-	void CreateObs(bool set_undo);
-
-private:
-	long nLines, nPlanes;
-	fPOINT3D start, step;
-	LineDEF Line;
-	FillDEF Fill;
-	Line3D **lines;
-	Plane3D **planes;
-
-	bool Configure();
-};
-
-class Scatt3D:public Plot{
-public:
-	Scatt3D(GraphObj *par, DataObj *d, DWORD flags);
-	Scatt3D(GraphObj *par, DataObj *d, Brick **cols, long nob);
-	Scatt3D(GraphObj *par, DataObj *d, Sphere **ba, long nob);
-	Scatt3D(int src);
-	~Scatt3D();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-private:
-	long nBalls, nColumns, nDropLines, nArrows;
-	DWORD c_flags;
-	char *ssRefX, *ssRefY, *ssRefZ;
-	Line3D *Line;
-	Sphere **Balls;
-	Brick **Columns;
-	DropLine3D **DropLines;
-	Arrow3D **Arrows;
-	Ribbon *rib;
-};
-
-class Limits:public Plot {
-public:
-	Limits(int src);
-	~Limits();
-	double GetSize(int select);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool FileIO(int rw);
-};
-
-class Function:public Plot{
-public:
-	Function(GraphObj *par, DataObj *d, char *desc);
-	Function(int src);
-	~Function();
-	bool SetSize(int select, double value);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-	bool Update(anyOutput *o, DWORD flags);
-	LineDEF *GetLine() {return &Line;};
-
-private:
-	double x1,x2, xstep;
-	LineDEF Line;
-	char *param;
-	char *cmdxy;
-	DataLine *dl;
-};
-
-class FitFunc:public Plot{
-public:
-	FitFunc(GraphObj *par, DataObj *d);
-	FitFunc(int src);
-	~FitFunc();
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-private:
-	double x1, x2, xstep, conv, chi2;
-	char *ssXref, *ssYref;
-	long nPoints;
-	int maxiter;
-	LineDEF Line;
-	Symbol **Symbols;
-	char *cmdxy, *parxy;
-	Function *dl;
-};
-
-class NormQuant:public Plot{
-public:
-	NormQuant(GraphObj *par, DataObj *d, char* range);
-	NormQuant(GraphObj *par, DataObj *d, double *data, int ndata);
-	NormQuant(int src);
-	~NormQuant();
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-
-private:
-	char *ssRef;
-	int nData, nValidData;
-	double *x_vals, *y_vals, *src_data;
-	Symbol *sy;
-
-	bool ProcessData();
-};
-
-	
-class GridLine:public GraphObj{
-public:
-	DWORD flags;
-	long ncpts;
-	POINT pts[6], *cpts;
-	LineDEF LineDef;
-	POINT3D *gl1, *gl2, *gl3;
-	line_segment **ls;
-	bool bModified;
-	anyOutput *mo;
-	RECT mrc;
-
-	GridLine(GraphObj *par, DataObj *d, int type, DWORD df);
-	GridLine(int src);
-	~GridLine();
-	virtual void DoPlot(anyOutput *o);
-	virtual void DoMark(anyOutput *o, bool mark);
-	virtual bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	bool FileIO(int rw);
-};
-
-class GridLine3D:public GridLine {
-public:
-	GridLine3D(GraphObj *par, DataObj *d, int type, DWORD df);
-	GridLine3D(int src);
-	~GridLine3D();
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-};
-
-class GridRadial:public GridLine {
-public:
-	GridRadial(GraphObj *par, DataObj *d, int type, DWORD df);
-	GridRadial(int src);
-	~GridRadial();
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-};
-
-class Tick:public GraphObj{
-public:
-	Tick(GraphObj *par, DataObj *d, double val, DWORD Flags);
-	Tick(int src);
-	~Tick();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	bool SetColor(int select, DWORD col);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-	void DoPlot(double six, double csx, anyOutput *o);
-	
-private:
-	double value, size, fix, fiy, fiz, lsi, lcsi, angle, lbx, lby;
-	bool bModified;
-	anyOutput *mo;
-	RECT mrc;
-	int gl_type;
-	GridLine *Grid;
-	Label *label;
-	DWORD flags;
-	POINT pts[2];
-	line_segment *ls;
-};
-
-class Axis:public GraphObj{
-public:
-	AxisDEF *axis;
-	LineDEF axline;
-	TextValue *atv;
-
-	Axis(GraphObj *par, DataObj *d, AxisDEF *ax, DWORD flags);
-	Axis(int src);
-	~Axis();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	DWORD GetColor(int select);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-	AxisDEF *GetAxis() {return axis;};
-	bool GetValuePos(double val, double *fix, double *fiy, double *fiz, anyOutput *o);
-	void TickFile(char *name);
-	void BreakSymbol(POINT3D *p1, double dsi, double dcsi, bool connect, anyOutput *o);
-	void DrawBreaks(anyOutput *o);
-
-private:
-	double sizAxLine, sizAxTick, sizAxTickLabel;
-	double si, csi;
-	double brksymsize, brkgap, tick_angle;
-	int brksym, nl_segs, gl_type, tick_type;
-	bool bModified;
-	DWORD colAxis;
-	LineDEF GridLine;
-	Tick **Ticks;
-	GraphObj *axisLabel;
-	long NumTicks;
-	POINT pts[2];
-	POINT3D pts3D[2];
-	fPOINT3D flim[2];
-	lfPOINT lbdist, tlbdist;
-	TextDEF tlbdef;
-	anyOutput *drawOut, *scaleOut;
-	line_segment **l_segs;
-	char *ssMATval, *ssMATlbl, *ssMITval; 
-	anyOutput *mo;
-	RECT mrc;
-
-	void SetTick(long idx, double val, DWORD flags, char *txt);
-	void mkTimeAxis();
-	void CreateTicks();
-	void ManuTicks(double sa, double st, int n, DWORD flags);
-	void UpdateTicks();
-	bool ssTicks();
-
-};
-
-class Plot3D:public Plot{
-	typedef struct {
-		double Zmin, Zmax;
-		GraphObj *go;
-		}obj_desc;
-public:
-	long nPlots, nAxes;
-	Axis **Axes;
-	GraphObj **plots, **Sc_Plots;
-	double *RotDef;
-	fPOINT3D cub1, cub2, rotC;
-	int nscp;
-
-	Plot3D(GraphObj *par, DataObj *d, DWORD flags);
-	Plot3D(int src);
-	~Plot3D();
-	double GetSize(int select);
-	bool SetColor(int select, DWORD col);
-	void DoPlot(anyOutput *o);
-	void DoMark(anyOutput *o, bool mark);
-	virtual bool Command(int cmd, void *tmpl, anyOutput *o);
-	virtual bool PropertyDlg();
-	virtual void RegGO(void *n);
-	virtual bool FileIO(int rw);
-
-	void * ObjThere(int x, int y);
-	void Track(POINT *p, anyOutput *o);
-	void CreateAxes();
-	void DoAutoscale();
-	void CalcRotation(double dx, double dy, anyOutput *o, bool accept);
-	bool AcceptObj(GraphObj *go);
-	void SortObj();
-	bool Rotate(double dx, double dy, double dz, anyOutput *o, bool accept);
-	bool AddAxis();
-
-private:
-	long nObs, nmaxObs;
-	DWORD crea_flags;
-	Drag3D *drag;
-	fPOINT3D cu1, cu2, rc;
-	obj_desc **dispObs;
-
-	bool AddPlot(int family);
-};
-
-class Chart25D:public Plot3D {
-public:
-	Chart25D(GraphObj *par, DataObj *d, DWORD flags);
-	~Chart25D();
-	bool PropertyDlg();
-
-private:
-	fPOINT3D dspm;
-};
-
-class Ribbon25D:public Plot3D {
-public:
-	Ribbon25D(GraphObj *par, DataObj *d, DWORD flags);
-	~Ribbon25D();
-	bool PropertyDlg();
-
-private:
-	fPOINT3D dspm;
-};
-
-class BubblePlot3D:public Plot3D {
-public:
-	BubblePlot3D(GraphObj *par, DataObj *d);
-	~BubblePlot3D();
-	bool PropertyDlg();
-};
-
-class Func3D:public Plot3D {
-public:
-	Func3D(GraphObj *par, DataObj *d);
-	Func3D(int src);
-	~Func3D();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-private:
-	bool Update();
-
-	double x1, x2, xstep, z1, z2, zstep;
-	LineDEF Line;
-	FillDEF Fill;
-	char *param, *cmdxy;
-	DataObj *gda;
-	Grid3D  *gob;
-};
-
-class FitFunc3D:public Plot3D {
-public:
-	FitFunc3D(GraphObj *par, DataObj *d);
-	FitFunc3D(int src);
-	~FitFunc3D();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	bool FileIO(int rw);
-
-private:
-	bool Update();
-
-	double x1, x2, xstep, z1, z2, zstep, conv, chi2;
-	char *ssXref, *ssYref, *ssZref;
-	int maxiter;
-	LineDEF Line;
-	FillDEF Fill;
-	char *param, *cmdxy;
-	DataObj *gda;
-	Grid3D  *gob;
-};
-
-class Graph:public GraphObj{
-public:
-	long NumPlots;
-	int ToolMode, units, nscp;
-	anyOutput *Disp, *CurrDisp;
-	fRECT GRect, DRect, Bounds;
-	bool OwnDisp, bModified;
-	fRECT CurrRect;
-	GraphObj **Plots, *PasteObj;
-	DWORD ColBG, ColAX;
-	GraphObj **Sc_Plots;
-	Axis **Axes;
-	char *filename;
-
-	Graph(GraphObj *par, DataObj *d, anyOutput *o, int style);
-	Graph(int src);
-	~Graph();
-	double GetSize(int select);
-	bool SetSize(int select, double value);
-	DWORD GetColor(int select);
-	bool SetColor(int select, DWORD col);
-	virtual void DoPlot(anyOutput *o);
-	virtual bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool PropertyDlg();
-	void RegGO(void *n);
-	virtual bool FileIO(int rw);
-	virtual double DefSize(int select);
-
-private:
-	int NumAxes, AxisTempl, tickstyle, zoom_level;
-	double scale;
-	RECT rcDim, rcUpd, rc_mrk;
-	DWORD ColDR, ColGR, ColGRL;
-	AxisDEF x_axis, y_axis;
-	FrmRect *frm_g, *frm_d;
-	bool dirty, bDialogOpen;
-	POINT *tl_pts;
-	long tl_nPts;
-	ZoomDEF *zoom_def;
-
-	bool AddPlot(int family);
-	void DoAutoscale();
-	void CreateAxes(int templ);
-	bool ExecTool(MouseEvent *mev);
-	bool Configure();
-	bool AddAxis();
-	bool MoveObj(int cmd, GraphObj *g); 
-	bool DoZoom(char *z);
-	bool DoScale(scaleINFO *sc, anyOutput *o);
-};
-
-class Page:public Graph{
-public:
-	Page(GraphObj *par, DataObj *d);
-	Page(int src);
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	void RegGO(void *n);
-	bool FileIO(int rw);
-	double DefSize(int select);
-
-private:
-	LineDEF LineDef;
-	FillDEF FillDef;
-
-	bool Configure();
-};
-
-class ObjTree:public GraphObj {
-public:
-	ObjTree(GraphObj *par, DataObj *d, GraphObj *root);
-	~ObjTree();
-	void DoPlot(anyOutput *o);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-
-	anyOutput *CreateBitmap(int *w, int *h, anyOutput *tmpl);
-	int count_lines(){ return count;};
-	char *get_name(int line);
-	int get_vis(int line);
-	bool set_vis(int line, bool vis);
-	GraphObj *get_obj(int line);
-
-private:
-	GraphObj **list, *base;
-	int count, maxcount;
-	TextDEF TextDef;
-};
-
-class notary{
-public:
-	notary();
-	~notary();
-	int RegisterGO(GraphObj *go);
-	void AddRegGO(GraphObj *go);
-	bool PushGO(unsigned int id, GraphObj *go);
-	GraphObj *PopGO(unsigned int id);
-	void FreeStack();
-
-private:
-	unsigned int NextPopGO, NextPushGO, NextRegGO;
-	GraphObj ***gObs;
-	GraphObj ***goStack;
-};
-
-class Default{
-public:
-	int dUnits, cUnits;
-	char DecPoint[2], ColSep[2];
-	char *svgAttr, *svgScript, *currPath, *IniFile;
-	char *File1, *File2, *File3, *File4, *File5, *File6;
-	char *fmt_date, *fmt_time, *fmt_datetime;
-	double min4log, ss_txt;
-	RECT clipRC;
-
-	Default();
-	~Default();
-	void SetDisp(anyOutput *o);
-	double GetSize(int select);
-	DWORD Color(int select);
-	LineDEF *GetLine();
-	void SetLine(int u, LineDEF *l, int which);
-	FillDEF *GetFill();
-	void SetFill(int u, FillDEF *fd);
-	LineDEF *GetOutLine();
-	bool PropertyDlg();
-	LineDEF *plLineDEF(LineDEF *ld);
-	LineDEF *pgLineDEF(LineDEF *ol);
-	FillDEF *pgFillDEF(FillDEF *fd);
-	double rrectRad(double rad);
-	void FileHistory(char *path);
-
-private:
-	LineDEF Line_0, Line_1, Line_2, *pl, *pgl;
-	FillDEF Fill_0, Fill_1, Fill_2, *pg;
-	LineDEF FillLine_0, FillLine_1, FillLine_2, *pg_fl;
-	LineDEF OutLine_0, OutLine_1, OutLine_2;
-	double *rrect_rad;
-	anyOutput *cdisp;
-	DWORD axis_color;
-};
-
-class DefsRW:public GraphObj{
-public:
-	DefsRW():GraphObj(0, 0){Id = 0; return;};
-	DefsRW(int rw):GraphObj(0,0){FileIO(rw);Id=GO_DEFRW;return;};
-	~DefsRW() {return;};
-	bool FileIO(int rw);
-};
-
-class ReadCache{
-public:
-	unsigned char last, *Cache, Line[4096];
-	int iFile, idx, max;
-	bool eof;
-
-	ReadCache();
-	~ReadCache();
-	virtual bool Open(char *name);
-	virtual void Close();
-	virtual unsigned char Getc();
-	virtual unsigned char *GetField();
-	void ReadLine(char *dest, int size);
-	bool GetInt(long *in);
-	bool GetFloat(double *fn);
-	unsigned char Lastc();
-	bool IsEOF();
-};
-
-class MemCache:public ReadCache{
-public:
-	MemCache(unsigned char *ptr);
-	~MemCache();
-	bool Open(char *name){return false;};
-	void Close(){return;};
-	unsigned char Getc();
-	unsigned char *GetField();
-};
-
-#define UNDO_CONTINUE 0x01
-#define UNDO_STORESET 0x1000
-class UndoObj {
-	enum {UNDO_UNDEFINED, UNDO_DEL_GO, UNDO_GOLIST, UNDO_DROPMEM,
-		UNDO_VALDWORD, UNDO_VALINT, UNDO_VALLONG, UNDO_OBJCONF, UNDO_OBJCONF_1,
-		UNDO_LFP, UNDO_POINT, UNDO_VOIDPTR, UNDO_MOVE, UNDO_RECT,
-		UNDO_STRING, UNDO_ROTDEF, UNDO_SETGO, UNDO_LINEDEF, UNDO_FILLDEF,
-		UNDO_AXISDEF, UNDO_LFP3D, UNDO_FLOAT, UNDO_MEM, UNDO_MUTATE, 
-		UNDO_DROPGOLIST, UNDO_TEXTDEF, UNDO_SAVVAR, UNDO_DATA, UNDO_ET, UNDO_TEXTBUF};
-	typedef struct _UndoInfo {
-		int cmd;
-		DWORD flags;
-		GraphObj *owner;
-		void *data;
-		void **loc;
-		ZoomDEF zd;
-		}UndoInfo;
-
-	typedef struct _UndoList {
-		void *array;
-		void **loc_arr;
-		long count, size;
-		long *loc_count;
-		}UndoList;
-
-	typedef struct _UndoBuff {
-		int count;
-		UndoInfo **buff;
-		anyOutput *disp;
-		}UndoBuff;
-
-	typedef struct _EtBuff {
-		char *txt;
-		DataObj *DaO;
-		int *cur, *m1, *m2, vcur, vm1, vm2, row, col;
-		}EtBuff;
-
-	typedef struct _TextBuff {
-		int *psize, size, *ppos, pos;
-		unsigned char **pbuff, *buff;
-		}TextBuff;
-
-public:
-	int *pcb;
-	anyOutput *cdisp, *ldisp;
-	bool busy;
-
-	UndoObj();
-	~UndoObj();
-	void Flush();
-	bool isEmpty(anyOutput *o);
-	void SetDisp(anyOutput *o);
-	void KillDisp(anyOutput *o);
-	void InvalidGO(GraphObj *go);
-	void Pop(anyOutput *o);
-	void Restore(bool redraw, anyOutput *o);
-	void ListGOmoved(GraphObj **oldlist, GraphObj **newlist, long size);
-	void DeleteGO(GraphObj **go, DWORD flags, anyOutput *o);
-	void MutateGO(GraphObj **old, GraphObj *repl, DWORD flags, anyOutput *o);
-	void StoreListGO(GraphObj *parent, GraphObj ***go, long *count, DWORD flags);
-	void DropListGO(GraphObj *parent, GraphObj ***go, long *count, DWORD flags);
-	void DropMemory(GraphObj *parent, void **mem, DWORD flags);
-	void SavVarBlock(GraphObj *parent, void **mem, DWORD flags);
-	void ValDword(GraphObj *parent, DWORD *val, DWORD flags);
-	void Point(GraphObj *parent, POINT *pt, anyOutput * o, DWORD flags);
-	void VoidPtr(GraphObj *parent, void **pptr, void *ptr, anyOutput * o, DWORD flags);
-	void ValInt(GraphObj *parent, int *val, DWORD flags);
-	void ValLong(GraphObj *parent, long *val, DWORD flags);
-	void ObjConf(GraphObj *go, DWORD flags);
-	int SaveLFP(GraphObj *go, lfPOINT *lfp, DWORD flags);
-	void MoveObj(GraphObj *go, lfPOINT *lfp, DWORD flags);
-	void ValRect(GraphObj *go, fRECT *rec, DWORD flags);
-	int String(GraphObj *go, char **s, DWORD flags);
-	void RotDef(GraphObj *go, double **d, DWORD flags);
-	void SetGO(GraphObj *parent, GraphObj **where, GraphObj *go, DWORD flags);
-	void Line(GraphObj *go, LineDEF *ld, DWORD flags);
-	void Fill(GraphObj *go, FillDEF *fd, DWORD flags);
-	void AxisDef(GraphObj *go, AxisDEF *ad, DWORD flags);
-	void TextDef(GraphObj *go, TextDEF *td, DWORD flags);
-	void ValLFP3D(GraphObj *go, fPOINT3D *lfp, DWORD flags);
-	void ValFloat(GraphObj *parent, double *val, DWORD flags);
-	void DataMem(GraphObj *go, void **mem, int size, long *count, DWORD flags);
-	void DataObject(GraphObj *go, anyOutput *o, DataObj *d, RECT *rc, DWORD flags);
-	void TextCell(EditText *et, anyOutput *o, char *text, int *cur, int *m1, int *m2, void* DaO, DWORD flags);
-	void TextBuffer(GraphObj *parent, int *psize, int *ppos, unsigned char **pbuff, DWORD flags, anyOutput *o); 
-
-private:
-	UndoInfo **buff, **buff0;
-	int stub1, ndisp;
-	UndoBuff **buffers;
-
-	int NewItem(int cmd, DWORD flags, GraphObj *owner, void *data, void **loc);
-	void FreeInfo(UndoInfo** inf);
-	void RestoreConf(UndoInfo *inf);
-	void RestoreData(UndoInfo *inf);
-};
-
-//prototypes: spreadwi.cpp
-int ProcMemData(GraphObj *g, unsigned char *ptr, bool dispatch);
-void SpreadMain(bool show);
-
-//prototypes: WinSpec.cpp or QT_Spec.cpp
-char *SaveDataAsName(char *oldname);
-char *SaveGraphAsName(char *oldname);
-char *OpenGraphName(char *oldname);
-char *OpenDataName(char *oldname);
-void InfoBox(char *Msg);
-void ErrorBox(char *Msg);
-bool YesNoBox(char *Msg);
-int YesNoCancelBox(char *Msg);
-void Qt_Box();
-void HideTextCursor();
-void HideTextCursorObj(anyOutput *out);
-void ShowTextCursor(anyOutput *out, RECT *disp, DWORD color);
-void HideCopyMark();
-void ShowCopyMark(anyOutput *out, RECT *mrk, int nRec);
-void InvalidateOutput(anyOutput *o);
-void SuspendAnimation(anyOutput *o, bool bSusp);
-void InitTextCursor(bool init);
-void EmptyClip();
-void CopyText(char *txt, int len);
-unsigned char* PasteText();
-void GetDesktopSize(int *width, int *height);
-void FindBrowser();
-void LoopDlgWnd();
-void CloseDlgWnd(void *hDlg);
-void ShowDlgWnd(void *hDlg);
-void ResizeDlgWnd(void *hDlg, int w, int h);
-anyOutput *NewDispClass(GraphObj *g);
-bool DelDispClass(anyOutput *w);
-anyOutput *NewBitmapClass(int w, int h, double hr, double vr);
-bool DelBitmapClass(anyOutput *w);
-
-//prototypes: FileIO.cpp
-bool SaveGraphAs(GraphObj *g);
-char *GraphToMem(GraphObj *g, long *size);
-void UpdGOfromMem(GraphObj *go, unsigned char *buff);
-bool OpenGraph(GraphObj *root, char *name, unsigned char *mem, bool bPaste);
-void SavVarInit(long len);
-void *SavVarFetch();
-
-//prototypes:TheDialog.cpp
-DWORD GetNewColor(DWORD oldcol);
-bool ShowLayers(GraphObj *root);
-void GetNewFill(FillDEF *oldfill);
-void ShowBanner(bool show);
-void RLPlotInfo();
-bool DoSpShSize(DataObj *dt, GraphObj *parent);
-bool FillSsRange(DataObj *d, char **range, GraphObj *msg_go);
-bool GetBitmapRes(double *res, double *width, double *height, char *header);
-void OD_scheme(int, void *, RECT *, anyOutput *, void *, int);
-FillDEF *GetSchemeFill(int *i);
-void OD_linedef(int, void *, RECT *, anyOutput *o, void *, int);
-void OD_filldef(int, void *, RECT *, anyOutput *o, void *, int);
-void OD_paperdef(int, void *, RECT *, anyOutput *o, void *, int);
-void FindPaper(double w, double h, double tol);
-bool GetPaper(double *w, double *h);
-void OD_axisplot(int, void *, RECT *, anyOutput *o, void *, int);
-
-//prototypes: Utils.cpp
-anyOutput *GetRectBitmap(RECT *rc, anyOutput *src);
-void RestoreRectBitmap(anyOutput **pmo, RECT *mrc, anyOutput *o);
-void NiceAxis(AxisDEF *axis, int nTick);
-void NiceStep(AxisDEF *axis, int nTick);
-double base4log(AxisDEF *axis, int direc);
-double TransformValue(AxisDEF *axis, double val, bool transform);
-void SortAxisBreaks(AxisDEF *axis);
-double GetAxisFac(AxisDEF *axis, double delta, int direc);
-char *str_ltrim(char *str);
-char *str_rtrim(char *str);
-char *str_trim(char *str);
-void rmquot(char *str);
-int strpos(char *needle, char *haystack);
-char *strreplace(char *needle, char *replace, char *haystack);
-char *substr(char *text, int pos1, int pos2);
-int rlp_strcpy(char*dest, int size, char*src);
-void ReshapeFormula(char **text);
-void TranslateResult(anyResult *res);
-void CleanTags(char *txt, int *i1, int *i2, int *i3);
-void ChangeChar(char *text, char c1, char c2);
-char *Int2Nat(char *Text);
-char *Nat2Int(char *Text);
-void WriteNatFloatToBuff(char *buff, double val);
-bool Txt2Flt(char *txt, double *val);
-void RmTrail(char *txt);
-double NiceValue(double fv);
-char *NiceTime(double val);
-char *Int2ColLabel(int nr, bool uc);
-char *mkCellRef(int row, int col);
-char *mkRangeRef(int r1, int c1, int r2, int c2);
-char *str2xml(char *str, bool bGreek);
-char **split(char *str, char sep, int *nl);
-char *fit_num_rect(anyOutput *o, int max_width, char *num_str);
-void add_to_buff(char** dest, int *pos, int *csize, char *txt, int len);
-void add_int_to_buff(char** dest, int *pos, int *csize, int value, bool lsp, int ndig);
-void add_dbl_to_buff(char** dest, int *pos, int *csize, double value, bool lsp);
-void add_hex_to_buff(char** dest, int *pos, int *csize, DWORD value, bool lsp);
-void SetMinMaxRect(RECT *rc, int x1, int y1, int x2, int y2);
-void UpdateMinMaxRect(RECT *rc, int x, int y);
-void IncrementMinMaxRect(RECT *rc, int i);
-bool IsInRect(RECT *rc, int x, int y);
-bool IsCloseToLine(POINT *p1, POINT *p2, int x, int y);
-bool IsCloseToPL(POINT p, POINT *pts, int cp);
-bool IsInPolygon(POINT *p, POINT *pts, int cp);
-bool OverlapRect(RECT *rc1, RECT *rc2);
-void AddToPolygon(long *cp, POINT *pts, POINT *np);
-void DrawBezier(long *cp, POINT *pts, POINT p0, POINT p1, POINT p2, POINT p3, int depth=0);
-int mkCurve(lfPOINT *src, int n1, lfPOINT **dst, bool bClosed);
-POINT *MakeArc(int ix, int iy, int r, int qad, long *npts);
-void InvertPolygon(POINT*, int, LineDEF*, FillDEF*, RECT*, anyOutput*, bool);
-void InvertLine(POINT*, int, LineDEF*, RECT*, anyOutput*, bool);
-unsigned int ColDiff(DWORD col1, DWORD col2);
-DWORD IpolCol(DWORD color1, DWORD color2, double fact);
-double ran2(long *idum);
-unsigned long isqr(unsigned long n);
-bool MatMul(double a[3][3], double b[3][3], double c[3][3]);
-char *GetNumFormat(double Magn);
-void DeleteGO(GraphObj *go);
-bool DeleteGOL(GraphObj ***gol, long n, GraphObj *go, anyOutput *o);
-bool BackupFile(char *FileName);
-bool IsRlpFile(char *FileName);
-bool IsXmlFile(char *FileName);
-bool FileExist(char *FileName);
-bool IsPlot3D(GraphObj *g);
-void *memdup(void *ptr, int cb_old, int cb_new);
-double sininv(double val);
-double trig2deg(double si, double csi);
-bool ReplaceGO(GraphObj **oldobj, GraphObj **newobj);
-unsigned int HashValue(unsigned char *str);
-unsigned int Hash2(unsigned char * str);
-bool cmpLineDEF(LineDEF *l1, LineDEF *l2);
-bool cmpFillDEF(FillDEF *f1, FillDEF *f2);
-bool cmpAxisDEF(AxisDEF *a1, AxisDEF *a2);
-bool cmpTextDEF(TextDEF *t1, TextDEF *t2);
-DWORD CheckNewFloat(double *loc, double old_v, double new_v, GraphObj *par, DWORD flags);
-DWORD CheckNewInt(int *loc, int old_v, int new_v, GraphObj *par, DWORD flags);
-DWORD CheckNewDword(DWORD *loc, DWORD old_v, DWORD new_v, GraphObj *par, DWORD flags);
-DWORD CheckNewLFPoint(lfPOINT *loc, lfPOINT *old_v, lfPOINT *new_v, GraphObj *par, DWORD flags);
-DWORD CheckNewString(char **loc, char *s_old, char *s_new, GraphObj *par, DWORD flags);
-void clip_line_sphere(GraphObj *par, POINT3D **li, int r, int cx, int cy, int cz);
-void clip_line_plane(GraphObj *par, POINT3D **li, POINT3D *pg, int np, double *vec);
-void clip_sphline_sphere(GraphObj *par, POINT3D *lim1, POINT3D *lim2, POINT3D *cent, 
-	int r1, int r2, int cx, int cy, int cz);
-void clip_sphline_plane(GraphObj *par, POINT3D *lim1, POINT3D *lim2, POINT3D *cent, 
-	int r1, POINT3D *pg, int np, double *vec);
-void clip_plane_plane(GraphObj *par, POINT3D *pg1, int n1, double *v1, POINT3D *pg2, 
-	int n2, double *v2, POINT *m, int nm);
-
-//prototypes Export.cpp
-void DoExportWmf(GraphObj *g, char *FileName, float res, DWORD flags);
-void DoExportSvg(GraphObj *g, char *FileName, DWORD flags);
-void DoExportEps(GraphObj *g, char *FileName, DWORD flags);
-
-//prototypes Output.cpp
-void DoExportTif(GraphObj *g, char *FileName, DWORD flags);
-
-//prototypes mfcalc.cpp
-void LockData(bool lockExec, bool lockWrite);
-char  *yywarn(char *txt, bool bNew);
-bool do_xyfunc(DataObj *, double, double, double, char *, lfPOINT **, long *, char *);
-bool do_func3D(DataObj *d, double x1, double x2, double xstep, double z1, double z2, double zstep, 
-	char *expr, char *param);
-anyResult *do_formula(DataObj *, char *);
-bool MoveFormula(DataObj *d, char *of, char *nf, int nfsize, int dx, int dy, int r0, int c0);
-int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr, 
-	double conv, int maxiter, double *chi_2);
-
-//prototypes rlp_math.cp
-double **dmatrix(int nrl, int nrh, int ncl, int nch);
-void free_dmatrix(double **m, int nrl, int nrh, int ncl, int);
-bool mrqmin(double *, double *, double *, int, double **, int, int *, int, double **, double **, double *,
-	void (*funcs)(double, double, double **, double *, double *, int), double *);
-bool Check_MRQerror();
-void SortArray(int n, double *vals);
-void SortArray2(int n, double *a1, double *a2);
-void SortFpArray(int n, lfPOINT *vals);
-double *randarr(double *v0, int n, long *seed);
-double *resample(double *v0, int n, long *seed);
-void spline(lfPOINT *v, int n, double *y2);
-double gammln(double x);
-double factrl(int n);
-double gammp(double a, double x);
-double gammq(double a, double x);
-double betai(double a, double b, double x);
-double bincof(double n, double k);
-double binomdistf(double k, double n, double p);
-double betaf(double z, double w);
-double errf(double x);
-double errfc(double x);
-double norm_dist(double x, double m, double s);
-double norm_freq(double x, double m, double s);
-double exp_dist(double x, double l, double s);
-double exp_inv(double p, double l, double s);
-double exp_freq(double x, double l, double s);
-double lognorm_dist(double x, double m, double s);
-double lognorm_freq(double x, double m, double s);
-double chi_dist(double x, double df, double);
-double chi_freq(double x, double df);
-double t_dist(double t, double df, double);
-double t_freq(double t, double df);
-double pois_dist(double x, double m, double);
-double f_dist(double f, double df1, double df2);
-double f_freq(double f, double df1, double df2);
-double weib_dist(double x, double shape, double scale);
-double weib_freq(double x, double shape, double scale);
-double cauch_dist(double x, double loc, double scale);
-double cauch_freq(double x, double loc, double scale);
-double logis_dist(double x, double loc, double scale);
-double logis_freq(double x, double loc, double scale);
-double ks_dist(int n, double d);
-void swilk1(int n, double *v0, double (*func)(double, double, double), double p1, double p2, 
-	bool bsorted, double *w, double *p);
-void KolSmir(int n, double *v0, double (*func)(double, double, double),
-	double p1, double p2, bool bsorted, double *d, double *p);
-double distinv(double (*sf)(double, double, double), double df1, double df2, double p, double x0);
-void d_quartile(int n, double *v, double *q1, double *q2, double *q3);
-double d_variance(int n, double *v, double *mean = 0L, double *ss = 0L);
-double d_amean(int n, double *v);
-double d_kurt(int n, double *v);
-double d_skew(int n, double *v);
-double d_gmean(int n, double *v);
-double d_hmean(int n, double *v);
-double d_classes(DataObj *d, double start, double step, double *v, int nv, char *range);
-double d_pearson(double *x, double *y, int n, char *dest, DataObj *data, double *ra);
-double d_rank(int n, double *v, double v1);
-void crank(int n, double *w0, double *s);
-double d_spearman(double *x, double *y, int n, char *dest, DataObj *data, double *ra);
-double d_kendall(double *x, double *y, int n, char *dest, DataObj *data, double *ra);
-double d_regression(double *x, double *y, int n, char *dest, DataObj *data, double *ra);
-double d_covar(double *x, double *y, int n, char *dest, DataObj *data);
-double d_utest(double *x, double *y, int n1, int n2, char *dest, DataObj *data, double *results);
-double d_ttest(double *x, double *y, int n1, int n2, char *dest, DataObj *data, double *results);
-double d_ttest2(double *x, double *y, int n, char *dest, DataObj *data, double *results);
-double d_ftest(double *x, double *y, int n1, int n2, char *dest, DataObj *data, double *results);
-bool do_anova1(int n, int *nv, double **vals, double **res_tab, double *gm, double **means, double **ss);
-bool bartlett(int n, int *nc, double *ss, double *chi2);
-bool levene(int type, int n, int *nv, double *means, double **vals, double *F, double *P);
-double wprob(double w, double rr, double cc);
-double ptukey(double q, double rr, double cc, double df, int lower_tail, int log_p);
-double qtukey(double p, double rr, double cc, double df, int lower_tail, int log_p);
-int year2aday(int y);
-void add_date(rlp_datetime *base, rlp_datetime *inc);
-char *date2text(rlp_datetime *dt, char *fmt);
-double date2value(rlp_datetime *dt);
-void parse_datevalue(rlp_datetime *dt, double dv);
-bool date_value(char *desc, char *fmt, double *value);
-char *value_date(double dv, char *fmt);
-double now_today();
-void split_date(double dv, int *y, int *mo, int *dom, int *dow, int *doy, int *h, int *m, double *s);
-Triangle* Triangulate1(char *xr, char *yr, char *zr, DataObj *data);
-Ribbon *SurfTria(GraphObj *parent, DataObj *data, char *text1, char *text2, char *text3);
-
-//prototypes reports.cpp
-void rep_anova(GraphObj *parent, DataObj *data);
-void rep_twanova(GraphObj *parent, DataObj *data);
-void rep_fmanova(GraphObj *parent, DataObj *data);
-void rep_twoway_anova(GraphObj *parent, DataObj *data);
-void rep_kruskal(GraphObj *parent, DataObj *data);
-void rep_samplestats(GraphObj *parent, DataObj *data);
-void rep_regression(GraphObj *parent, DataObj *data);
-void rep_robustline(GraphObj *parent, DataObj *data);
-void rep_twowaytable(GraphObj *parent, DataObj *data);
-void rep_compmeans(GraphObj *parent, DataObj *data);
-void rep_correl(GraphObj *parent, DataObj *data, int style);
-
+//RLPlot.h, Copyright (c) 2000-2008 R.Lackner
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+#ifndef _RLPLOT_H
+#define _RLPLOT_H
+#define NUM_UNITS  3
+#define TMP_TXT_SIZE 4096
+
+#include <stdio.h>
+#include "Version.h"
+
+#define Swap(a,b) {a^=b;b^=a;a^=b;}
+inline int iround(double a) {return a < 0.0 ?(int)(a-0.499) : (int)(a+0.499);}
+
+#define _PI	3.1415926535897932384626433832795028841971693993751
+#define _SQRT2	1.4142135623730950488016887242096980785696718753769
+
+#ifdef _WINDOWS					//platform is windows
+#include <windows.h>
+#if _MSC_VER >= 1400
+#define USE_WIN_SECURE
+#endif
+#define w_char WCHAR
+#define _SBINC	1				//scrollbox extra space/line
+#define _TXH	4.0				//graph text default size in mm
+
+#else							//platform is *nix
+#include <sys/types.h>
+#define DWORD u_int32_t
+#define w_char wchar_t
+#define _SBINC	8				//scrollbox extra space/line
+#define _TXH	3.0				//graph text default size in mm
+
+typedef struct tagPOINT { // pt 
+    long x; 
+    long y; 
+} POINT; 
+
+typedef struct _RECT {    // rc 
+    long left; 
+    long top; 
+    long right; 
+    long bottom; 
+} RECT; 
+
+#endif	//_WINDOWS
+
+enum {SIZE_MINE, SIZE_SYMBOL, SIZE_SYM_LINE, SIZE_DATA_LINE, SIZE_TEXT,
+	SIZE_GRECT_TOP, SIZE_GRECT_BOTTOM, SIZE_GRECT_LEFT, SIZE_GRECT_RIGHT,
+	SIZE_DRECT_LEFT, SIZE_DRECT_RIGHT, SIZE_DRECT_TOP, SIZE_DRECT_BOTTOM,
+	SIZE_DATA_LINE_PAT, SIZE_XPOS, SIZE_XPOS_LAST = SIZE_XPOS+200, 
+	SIZE_YPOS, SIZE_YPOS_LAST = SIZE_YPOS+200, SIZE_ZPOS, 
+	SIZE_ZPOS_LAST = SIZE_ZPOS+200, SIZE_BOUNDS_XMIN,
+	SIZE_BOUNDS_XMAX, SIZE_BOUNDS_YMIN, SIZE_BOUNDS_YMAX, SIZE_BOUNDS_ZMIN,
+	SIZE_BOUNDS_ZMAX, SIZE_BOUNDS_LEFT, SIZE_BAR_BASE,
+	SIZE_BOUNDS_RIGHT, SIZE_BOUNDS_TOP, SIZE_BOUNDS_BOTTOM, SIZE_XAXISY,
+	SIZE_YAXISX, SIZE_AXIS_LINE, SIZE_PATLENGTH, SIZE_BAR_DEPTH,
+	SIZE_AXIS_TICKS, SIZE_TICK_LABELS, SIZE_ERRBAR, SIZE_ERRBAR_LINE, 
+	SIZE_BAR_LINE, SIZE_BAR, SIZE_XBASE, SIZE_YBASE, SIZE_ZBASE, SIZE_BUBBLE_LINE,
+	SIZE_BUBBLE_HATCH_LINE, SIZE_BARMINX, SIZE_BARMINY, SIZE_ARROW_LINE,
+	SIZE_ARROW_CAPWIDTH, SIZE_ARROW_CAPLENGTH, SIZE_HAIRLINE, SIZE_WHISKER,
+	SIZE_WHISKER_LINE, SIZE_BOX_LINE, SIZE_BOXMINX, SIZE_BOXMINY, SIZE_BOX,
+	SIZE_RADIUS1, SIZE_RADIUS2, SIZE_SEGLINE, SIZE_LB_XPOS, SIZE_LB_YPOS,
+	SIZE_LB_XDIST, SIZE_LB_YDIST, SIZE_TLB_XDIST, SIZE_TLB_YDIST, SIZE_ANGLE1,
+	SIZE_ANGLE2, SIZE_XCENTER, SIZE_YCENTER, SIZE_ZCENTER, SIZE_CELLWIDTH, SIZE_CELLTEXT,
+	SIZE_A, SIZE_B, SIZE_MX, SIZE_MY, SIZE_MIN_Z, SIZE_MAX_Z, SIZE_MIN_X, SIZE_MAX_X,
+	SIZE_MIN_Y, SIZE_MAX_Y, SIZE_TICK_ANGLE, SIZE_RRECT_RAD, SIZE_XCENT, SIZE_YCENT,
+	SIZE_ZCENT, SIZE_DRADIUS, SIZE_CURSORPOS, SIZE_CURSOR_XMIN, SIZE_CURSOR_YMIN,
+	SIZE_CURSOR_XMAX, SIZE_CURSOR_YMAX, SIZE_XSTEP, SIZE_LSPC, SIZE_SCALE};
+enum {COL_SYM_LINE, COL_SYM_FILL, COL_DATA_LINE, COL_TEXT, COL_BG,
+	COL_AXIS, COL_BAR_LINE, COL_BAR_FILL, COL_ERROR_LINE, COL_BUBBLE_LINE, 
+	COL_BUBBLE_FILLLINE, COL_BUBBLE_FILL, COL_ARROW, COL_WHISKER, COL_BOX_LINE,
+	COL_DRECT, COL_GRECT, COL_GRECTLINE, COL_POLYLINE, COL_POLYGON};
+enum {MRK_NONE, MRK_INVERT, MRK_GODRAW, MRK_SSB_DRAW};
+enum {CMD_NONE, CMD_ADDCHAR, CMD_ADDCHARW, CMD_SETFOCUS, CMD_KILLFOCUS, CMD_DELETE,
+	CMD_BACKSP, CMD_CURRLEFT, CMD_CURRIGHT, CMD_CURRUP, CMD_CURRDOWN,
+	CMD_SHIFTLEFT, CMD_SHIFTRIGHT, CMD_SHIFTUP, CMD_SHIFTDOWN,
+	CMD_NEWGRAPH, CMD_DELGRAPH, CMD_DELOBJ, CMD_DROP_PLOT, CMD_DROP_GRAPH, CMD_OPEN,
+	CMD_SAVEAS, CMD_ADDROWCOL, CMD_MOUSE_EVENT, CMD_REDRAW, CMD_DOPLOT,
+	CMD_LBUP, CMD_ENDDIALOG, CMD_RADIOBUTT, CMD_ADDCHILD, CMD_BAR_TYPE,
+	CMD_BAR_FILL, CMD_SET_AXDEF, CMD_SET_DATAOBJ, CMD_SETTEXT, CMD_GETTEXT,
+	CMD_SETTEXTDEF, CMD_GETTEXTDEF, CMD_ADDPLOT, CMD_SYM_TYPE, CMD_SYMTEXT, 
+	CMD_SYMTEXTDEF, CMD_RANGETEXT, CMD_SYM_RANGETEXT, CMD_FOCTXT, CMD_CONTINUE,
+	CMD_ERR_TYPE, CMD_ARROW_ORG, CMD_ARROW_TYPE, CMD_FLUSH, CMD_BOX_FILL,
+	CMD_TABDLG, CMD_NOTABDLG, CMD_TAB, CMD_SHTAB, CMD_BOX_TYPE, CMD_BUBBLE_TYPE,
+	CMD_BUBBLE_ATTRIB, CMD_BUBBLE_FILL, CMD_BUBBLE_LINE, CMD_DL_LINE, 
+	CMD_DL_TYPE, CMD_SEG_FILL, CMD_SEG_LINE, CMD_SELECT, CMD_MOVE, CMD_CUT,
+	CMD_SETSCROLL, CMD_SETHPOS, CMD_SETVPOS, CMD_PG_FILL, CMD_BOUNDS,
+	CMD_SHIFT_OUT, CMD_CAN_CLOSE, CMD_RESET_LINE, CMD_SET_TICKSTYLE,
+	CMD_GET_GRIDLINE, CMD_SET_GRIDLINE, CMD_SET_GRIDTYPE, CMD_TLB_TXTDEF,
+	CMD_DROP_LABEL, CMD_DROP_OBJECT, CMD_PAGEUP, CMD_PAGEDOWN, CMD_AUTOSCALE,
+	CMD_MRK_DIRTY, CMD_SETNAME, CMD_TOOLMODE, CMD_DROPFILE, CMD_AXIS, CMD_INIT,
+	CMD_GET_CELLDIMS, CMD_SET_CELLDIMS, CMD_TEXTSIZE, CMD_PASTE_TSV, CMD_PASTE_XML,
+	CMD_PASTE_SSV, CMD_PASTE_CSV, CMD_COPY_TSV, CMD_COPY_XML, CMD_COPY_SYLK, CMD_QUERY_COPY,
+	CMD_MOUSECURSOR, CMD_NEWPAGE, CMD_MINRC, CMD_MAXRC,CMD_SETCHILD, CMD_SYM_FILL,
+	CMD_LINEUP, CMD_LINEDOWN, CMD_CONFIG, CMD_FINDTEXT, CMD_MOVE_TOP, CMD_MOVE_UP,
+	CMD_MOVE_DOWN, CMD_MOVE_BOTTOM, CMD_UPDATE, CMD_CURRPOS, CMD_POS_FIRST, CMD_POS_LAST,
+	CMD_ADDAXIS, CMD_REG_AXISPLOT, CMD_USEAXIS, CMD_SET_GO3D, CMD_UNDO, CMD_DELOBJ_CONT,
+	CMD_RMU, CMD_REPL_GO, CMD_UNDO_MOVE, CMD_SAVEPOS, CMD_WHISKER_STYLE, CMD_TICK_TYPE,
+	CMD_ZOOM, CMD_CLIP, CMD_STARTLINE, CMD_ADDTOLINE, CMD_REQ_POINT, CMD_DRAW_LATER,
+	CMD_SEG_MOVEABLE, CMD_ARROW_ORG3D, CMD_MUTATE, CMD_PRINT, CMD_UPDHISTORY, CMD_ALLTEXT,
+	CMD_SET_LINE, CMD_SAVE_SYMBOLS, CMD_SAVE_TICKS, CMD_SAVE_BARS, CMD_SAVE_BARS_CONT,
+	CMD_SAVE_ERRS, CMD_SAVE_ARROWS, CMD_SAVE_DROPLINES, CMD_SAVE_LABELS, CMD_UNLOCK, CMD_SYMTEXT_UNDO,
+	CMD_FILLRANGE, CMD_BUSY, CMD_ERROR, CMD_CLEAR_ERROR, CMD_SETPARAM, CMD_SETFUNC,
+	CMD_LEGEND, CMD_FILENAME, CMD_LAYERS, CMD_OBJTREE, CMD_TEXTDEF,
+	CMD_HASSTACK, CMD_WRITE_GRAPHS, CMD_SETFONT, CMD_SETSTYLE, CMD_COPY, CMD_PASTE,
+	CMD_INSROW, CMD_INSCOL, CMD_DELROW, CMD_DELCOL, CMD_ADDTXT, CMD_ETRACC, CMD_SHPGUP, 
+	CMD_SHPGDOWN, CMD_ERRDESC, CMD_SAVE, CMD_GETMARK, CMD_PASTE_OBJ, CMD_COL_MOUSE,
+	CMD_MARKOBJ, CMD_SCALE, CMD_GETFILENAME, CMD_TEXTTHERE, CMD_HIDEMARK, CMD_MENUHEIGHT,
+	CMD_RECALC, CMD_DRAWPG, CMD_UPDPG, CMD_MINMAX, CMD_STEP, CMD_SIGNAL_POL};
+enum {SYM_CIRCLE, SYM_CIRCLEF, SYM_RECT, SYM_RECTF, SYM_TRIAU, SYM_TRIAUF,
+	SYM_TRIAD, SYM_TRIADF, SYM_DIAMOND, SYM_DIAMONDF, SYM_PLUS, SYM_CROSS,
+	SYM_STAR, SYM_HLINE, SYM_VLINE, SYM_CIRCLEC, SYM_RECTC, SYM_TRIAUC, SYM_TRIAUL, SYM_TRIAUR,
+	SYM_TRIADC, SYM_TRIADL, SYM_TRIADR, SYM_DIAMONDC, SYM_4STAR, SYM_4STARF, SYM_5GON, SYM_5GONF, 
+	SYM_5GONC, SYM_5STAR, SYM_5STARF, SYM_6STAR, SYM_6STARF, SYM_1QUAD, SYM_2QUAD, SYM_3QUAD,
+	SYM_TEXT = 0x004f, SYM_POS_PARENT = 0x1000};
+//types of graphic objects: stored in Id and used for proper destruction of objects
+//  and retrieving information.
+enum {GO_UNKNOWN, GO_AXIS, GO_TICK, GO_GRIDLINE, GO_SYMBOL, GO_BUBBLE, GO_BAR, 
+	GO_ERRBAR, GO_ARROW, GO_BOX, GO_LABEL, GO_MLABEL, GO_WHISKER, GO_DROPLINE, 
+	GO_DATALINE, GO_DATAPOLYGON, GO_REGLINE, GO_SDELLIPSE, GO_SEGMENT, 
+	GO_POLYLINE, GO_POLYGON, GO_RECTANGLE, GO_ELLIPSE, GO_ROUNDREC,
+	GO_DRAGHANDLE, GO_DRAGRECT, GO_DRAG3D, GO_FRAMERECT, GO_SPHERE, GO_SVGOPTIONS,
+	GO_PLANE, GO_BRICK, GO_LINESEG, GO_LINE3D, GO_GRIDLINE3D, GO_GRIDRADIAL,
+	GO_SPHSCANL, GO_DROPL3D, GO_ARROW3D, GO_PLANE3D, GO_LEGITEM, GO_LEGEND,
+	GO_OBJTREE, GO_BEZIER, GO_TEXTFRAME,
+	GO_PLOT = 0x100, GO_PLOTSCATT, GO_REGRESSION, GO_BARCHART, GO_BUBBLEPLOT, 
+	GO_BOXPLOT, GO_DENSDISP, GO_STACKBAR, GO_STACKPG, GO_WATERFALL, GO_POLARPLOT,
+	GO_PIECHART, GO_RINGCHART, GO_GROUP, GO_STARCHART, GO_SCATT3D, GO_PLOT3D,
+	GO_RIBBON, GO_LIMITS, GO_FUNCTION, GO_FITFUNC, GO_FREQDIST, GO_GRID3D, GO_FUNC3D,
+	GO_XYSTAT, GO_FITFUNC3D, GO_NORMQUANT, GO_CONTOUR,
+	GO_GRAPH = 0x200, GO_PAGE, GO_SPREADDATA = 0x300, GO_DEFRW};
+enum {OC_UNKNOWN, OC_BITMAP, OC_HIMETRIC, OC_TRANSPARENT = 0x100};
+enum {FILL_NONE, FILL_HLINES, FILL_VLINES, FILL_HVCROSS, FILL_DLINEU, FILL_DLINED,
+	FILL_DCROSS, FILL_STIPPLE1, FILL_STIPPLE2, FILL_STIPPLE3, FILL_STIPPLE4, 
+	FILL_STIPPLE5, FILL_ZIGZAG, FILL_COMBS, FILL_BRICKH, FILL_BRICKV, FILL_BRICKDU, 
+	FILL_BRICKDD, FILL_TEXTURE1, FILL_TEXTURE2, FILL_WAVES1, FILL_SCALES, FILL_SHINGLES, 
+	FILL_WAVES2, FILL_HERRING, FILL_CIRCLES, FILL_GRASS, FILL_FOAM, FILL_RECS, 
+	FILL_HASH, FILL_WATER, NUM_FILLS, FILL_LIGHT3D = 0x100};
+enum {ERRBAR_VSYM, ERRBAR_VUP, ERRBAR_VDOWN, ERRBAR_HSYM, ERRBAR_HLEFT,
+	ERRBAR_HRIGHT};
+enum {BAR_NONE, BAR_VERTB, BAR_VERTT, BAR_VERTU, BAR_HORL, BAR_HORR, BAR_HORU, 
+	BAR_RELWIDTH = 0x100, BAR_CENTERED = 0x200, BAR_WIDTHDATA = 0x400};
+enum {TM_STANDARD, TM_DRAW, TM_POLYLINE, TM_POLYGON, TM_RECTANGLE, TM_ELLIPSE,
+	TM_ROUNDREC, TM_ARROW, TM_TEXT, TM_MARK, TM_ZOOMIN, TM_MOVE = 0x100, 
+	TM_PASTE=0x200};
+enum {MC_LAST, MC_ARROW, MC_CROSS, MC_TEXT, MC_WAIT, MC_MOVE, MC_NORTH,
+	MC_NE, MC_EAST, MC_SE, MC_SALL, MC_ZOOM, MC_PASTE, MC_DRAWPEN, MC_DRAWREC, 
+	MC_DRAWRREC, MC_DRAWELLY, MC_TXTFRM, MC_COLWIDTH};
+enum {FILE_ERROR, FILE_READ, FILE_WRITE, INIT_VARS, SAVE_VARS};
+enum {ARROW_NOCAP, ARROW_LINE, ARROW_TRIANGLE, ARROW_UNITS = 0x100};
+enum {MENU_NONE, MENU_SPREAD, MENU_GRAPH, MENU_PAGE};
+enum {GT_UNKNOWN, GT_STANDARD, GT_CIRCCHART, GT_POLARPLOT, GT_3D};
+enum {ICO_NONE, ICO_INFO, ICO_ERROR, ICO_RLPLOT, ICO_QT};
+enum {FF_UNKNOWN, FF_CSV, FF_TSV, FF_XML, FF_SYLK, FF_RLP, FF_SVG, FF_EPS,
+	FF_WMF, FF_RLW, FF_SSV};
+enum {LB_X_DATA = 0x01, LB_X_PARENT = 0x02, LB_Y_DATA = 0x10, LB_Y_PARENT = 0x20};
+enum {BUBBLE_CIRCLE = 0x000, BUBBLE_SQUARE = 0x001, BUBBLE_UPTRIA = 0x002,
+	BUBBLE_DOWNTRIA = 0x003, BUBBLE_UNITS = 0x000, BUBBLE_XAXIS = 0x010,
+	BUBBLE_YAXIS = 0x020, BUBBLE_DIAMET = 0x000, BUBBLE_CIRCUM = 0x100,
+	BUBBLE_AREA = 0x200};
+enum {DH_UNKNOWN, DH_12, DH_22, DH_19, DH_29, DH_39, DH_49, DH_59, DH_69, DH_79,
+	DH_89, DH_99, DH_18, DH_28, DH_38, DH_48, DH_58, DH_68, DH_78, DH_88, DH_DATA};
+enum {FE_NONE = 0x1000, FE_PARENT, FE_PLOT, FE_FLUSH, FE_DELOBJ, FE_REPLGO, FE_MUTATE};
+
+//drop line styles
+#define DL_LEFT          0x001
+#define DL_RIGHT         0x002
+#define DL_YAXIS         0x004
+#define DL_TOP           0x010
+#define DL_BOTTOM        0x020
+#define DL_XAXIS         0x040
+#define DL_CIRCULAR      0x100
+
+typedef struct {
+	int num;
+	char* display;
+	float convert;			//multiply to get mm
+	}tag_Units;
+
+typedef struct {
+	int x, y, z;
+	}POINT3D;
+
+typedef struct {
+	double Xmin;
+	double Ymax;
+	double Xmax;
+	double Ymin;
+	}fRECT;
+
+typedef struct {
+	double fx;
+	double fy;
+	double fz;
+	}fPOINT3D;
+
+typedef struct {
+	double fx;
+	double fy;
+	}lfPOINT;
+
+typedef struct {
+	lfPOINT sx, sy, sz;
+	}scaleINFO;
+
+typedef struct {
+	double finc, fp;
+	}RunLinePat;				//used for line patterns
+
+typedef struct {
+	double width, patlength;
+	DWORD color, pattern;
+	}LineDEF;
+
+typedef struct {
+	int type;					//pattern
+	DWORD color;
+	double scale;
+	LineDEF *hatch;
+	DWORD color2;
+	}FillDEF;
+
+typedef struct {
+	lfPOINT org;				//zoom origin
+	double scale;				//zoom factor
+	}ZoomDEF;
+
+typedef struct rlp_datetime {
+	int aday, year, doy, month, dom, dow, hours, minutes;
+	double seconds;
+}rlp_datetime;
+
+// Axis definitions are stored in the following structure
+// not to be confused with the Axis class grapic object
+// bits stored in flags havethe following meaning
+#define AXIS_NOTICKS      0x0			// no ticks at all
+#define AXIS_POSTICKS     0x1			// positive direction of ticks
+#define AXIS_NEGTICKS     0x2			// negative direction 
+#define AXIS_SYMTICKS     0x3			// ticks are symmetrical
+#define AXIS_GRIDLINE     0x4			// ticks control a gridline
+#define AXIS_MINORTICK    0x8			// its a small tick only
+#define AXIS_USER         0x00			// axis placement by user
+#define AXIS_LEFT         0x10			// left of plot
+#define AXIS_RIGHT        0x20			// right  -"-
+#define AXIS_TOP          0x30			// top    -"-
+#define AXIS_BOTTOM       0x40			// bottom -"-
+#define AXIS_AUTOSCALE    0x80			// rescale upon editing
+#define AXIS_INVERT       0x100			// axis top->bottom, right to left
+#define AXIS_AUTOTICK     0x200			// select tick s automatically
+#define AXIS_DEFRECT      0x400			// use axis to define drawing rectangle
+#define AXIS_LINEAR       0x0000		// transforms ...
+#define AXIS_LOG          0x1000
+#define AXIS_RECI         0x2000
+#define AXIS_SQR          0x3000
+#define AXIS_DATETIME     0x4000		// its a date- or time axis
+#define AXIS_X_DATA       0x10000		// loc.x is data coordinates
+#define AXIS_Y_DATA       0x20000       // loc.y is data coordinates
+#define AXIS_Z_DATA       0x40000       // loc.z is data coordinates
+#define AXIS_ANGULAR      0x100000      // angular (circular) axis
+#define AXIS_RADIAL       0x200000		// radial axis
+#define AXIS_3D           0x400000		// three dimensional axis
+
+typedef struct {
+	void *owner;				//owners are usually graph, output or axis classes
+	DWORD flags;
+	double min, max;
+	fPOINT3D loc[2];			//placement of axis coordinates
+	double Start, Step;			//used for linear axis
+	lfPOINT Center;				//of polar plot
+	double Radius;				//   -"-
+	int nBreaks;				//axis break ...
+	lfPOINT *breaks;
+	}AxisDEF;
+
+//Attributes for text properties
+//Text fonts
+enum {FONT_HELVETICA, FONT_TIMES, FONT_COURIER, FONT_GREEK};
+//Text styles
+#define TXA_VTOP        0
+#define TXA_VCENTER     4
+#define TXA_VBOTTOM     8
+#define TXA_HLEFT       0
+#define TXA_HCENTER     1
+#define TXA_HRIGHT      2
+#define TXM_OPAQUE      0
+#define TXM_TRANSPARENT 1
+#define TXS_NORMAL      0
+#define TXS_ITALIC      1
+#define TXS_BOLD        2
+#define TXS_UNDERLINE   4
+#define TXS_SUPER       8
+#define TXS_SUB			16
+typedef struct {
+	DWORD ColTxt, ColBg;				//colors ..
+	double fSize;						//Text size in units
+	double RotBL, RotCHAR;				//Rotation in degrees
+	int iSize;							//Text size is given in iSize as pix
+	int Align, Mode, Style;				//Text Alignement 0   1   2
+										//                4   5   6
+										//                8   9  10
+										//Mode 0 = opaque, 1 = transparent
+	int Font;
+	char *text;
+	}TextDEF;
+
+// Store mouse events in the following structure
+// Action defines the type of event:
+enum {MOUSE_LBDOWN, MOUSE_LBUP, MOUSE_LBDOUBLECLICK, MOUSE_MBDOWN, MOUSE_MBUP, 
+	MOUSE_MBDOUBLECLICK, MOUSE_RBDOWN, MOUSE_RBUP, MOUSE_RBDOUBLECLICK,
+	MOUSE_MOVE};
+typedef struct {
+	unsigned short StateFlags;	//  1 Mouse left button down
+								//  2       middle button down
+								//  4       right button down
+								//  8       shift pressed
+								// 16       control pressed
+	unsigned short Action;
+	int x, y;
+	} MouseEvent;
+
+//use this structure if type of data is not known
+typedef struct {
+	int type;
+	double value;
+	char *text;
+	double *a_data;
+	int a_count;
+	} anyResult;
+
+//the AccRange class allows to access data objects with a 'a1:b1' style
+class AccRange{
+public:
+	AccRange(char *asc);
+	~AccRange();
+	int CountItems();
+	bool GetFirst(int *x, int *y);
+	bool GetNext(int *x, int *y);
+	bool NextRow(int *y);
+	bool NextCol(int *x);
+	bool IsInRange(int x, int y);
+	bool BoundRec(RECT *rec);
+	char *RangeDesc(void *d, int style);	//d points to a DataObj class
+	int DataTypes(void *d, int *numerical, int *strings, int *datetime);
+
+private:
+	char *txt;
+	int x1, y1, x2, y2, cx, cy, curridx;
+
+	bool Reset();
+	bool Parse(int start);
+};
+
+class Triangle {
+public:
+	Triangle *next;
+	fPOINT3D pt[4];
+	int order[3];					//sort order
+
+	Triangle();
+	void SetRect();
+	bool TestVertex(double x, double y);
+	bool Sort();
+	void LinePoint(int i1, int i2, double z, lfPOINT *res);
+	bool IsoLine(double z, void *dest);
+
+private:
+	double cx, cy, r2;				//circumcircle
+	fRECT rc;					//bounding rectangle
+	lfPOINT ld[3];					//line eqations
+	bool bSorted;					//vertices are sorted
+};
+
+class Triangulate {
+public:
+	Triangle *trl;
+
+	Triangulate(Triangle *t_list);
+	bool AddEdge(fPOINT3D *p1, fPOINT3D *p2);
+	bool AddVertex(fPOINT3D *v);
+
+private:
+	typedef struct edge {
+		edge *next;
+		fPOINT3D p1, p2;
+	};
+	edge *edges;
+};
+
+class anyOutput{
+public:
+	int units;						//use units mm, inch ...
+	int minLW;						//minimum line width in pix
+	int MrkMode;						//existing mark on screen
+	int OC_type;						//specify display, printer clipboard ...
+	void *MrkRect;						//pointer to e.g. the marked rectangle
+	fRECT Box1;						//User drawing rectangle
+	lfPOINT Box1z;						// add 3D to Box1
+	RECT DeskRect;						//this is maximum size Rectangle
+	double ddx, ddy, ddz;					//convert to device coordinates
+	double hres, vres;					//resolution in dpi
+	double LineWidth;					//line width in units
+	int iLine;						//current width of line in pixels
+	DWORD dLineCol;						//current color of line;
+	DWORD dBgCol;						//color of background
+	DWORD dFillCol, dFillCol2;
+	DWORD dPattern;						//current line bit-pattern
+	TextDEF TxtSet;						//store current text settings here
+	RunLinePat RLP;						//continuous setings of pattern line
+	AxisDEF xAxis, yAxis, zAxis;				//axis and transform definitions
+	lfPOINT VPorg;						//zoom origin
+	double VPscale;						//zoom factor
+	int MenuHeight;						//ofset due to pull down menus
+	int cCursor;						//mouse coursor identifier
+	double rotM[3][3];					//rotation matrix for 3D
+	fPOINT3D rotC;						//rotation center
+	bool hasHistMenu;					//File History List
+	int HistMenuSize;					//     -"-
+	lfPOINT light_source;					//angles for shading
+	double light_vec[3][3];					//     -"-
+	double disp_x, disp_y;					//displacement on page
+
+	anyOutput();
+	void SetRect(fRECT rec, int units, AxisDEF *x_ax, AxisDEF *y_ax);
+	void UseAxis(AxisDEF *ax, int type);
+	void SetSpace(fPOINT3D*,fPOINT3D*,int,double*,fPOINT3D*,AxisDEF*,AxisDEF*,AxisDEF*);
+	void LightSource(double x, double y);
+	DWORD VecColor(double *plane_vec, DWORD col1, DWORD col2);
+	bool GetSize(RECT *rc);
+	virtual bool ActualSize(RECT *rc) {return GetSize(rc);};
+	double fx2fix(double x);
+	int fx2ix(double x){return (int)(0.5 + fx2fix(x));};
+	double fy2fiy(double y);
+	int fy2iy(double y){return (int)(0.5 + fy2fiy(y));};
+	bool fp2fip(lfPOINT *fdp, lfPOINT *fip);
+	bool fvec2ivec(fPOINT3D *v, fPOINT3D *iv);
+	bool cvec2ivec(fPOINT3D *v, fPOINT3D *iv);
+	bool uvec2ivec(fPOINT3D *v, fPOINT3D *iv);
+	double un2fix(double x);
+	int un2ix(double x) {return (int)(0.5 + un2fix(x));};
+	int co2ix(double x) {return un2ix(x) + iround(VPorg.fx);};
+	double co2fix(double x) {return un2fix(x) + VPorg.fx;};
+	double un2fiy(double y);
+	int un2iy(double y) {return (int)(0.5 + un2fiy(y));};
+	int co2iy(double y) {return un2iy(y) + iround(VPorg.fy);};
+	double co2fiy(double y) {return un2fiy(y) + VPorg.fy;};
+	double un2fiz(double z);
+	double fz2fiz(double z);
+	double fix2un(double fix);
+	double fiy2un(double fiy);
+	virtual bool SetLine(LineDEF *lDef){return false;};
+	bool GetLine(LineDEF *lDef);
+	virtual void Focus() {return;};
+	virtual void Caption(char *txt, bool bModified){return;};
+	virtual void MouseCursor(int cid, bool force){return;};
+	virtual bool SetScroll(bool, int, int, int, int){return false;};
+	virtual bool SetFill(FillDEF *fill){return false;};
+	virtual bool SetTextSpec(TextDEF *set);
+	virtual bool Erase(DWORD Color){return false;};
+	virtual bool StartPage(){return false;};
+	virtual bool EndPage(){return false;};
+	virtual bool Eject() {return false;};		//printers only
+	virtual bool UpdateRect(RECT *rc, bool invert){return false;};
+	virtual bool CopyBitmap(int x, int y, anyOutput* src, int sx, int sy,
+		int sw, int sh, bool invert){return false;};
+	virtual void ShowBitmap(int x, int y, anyOutput* src){return;};
+	bool ShowMark(void *rc, int Mode);
+	bool HideMark();
+	virtual void MouseCapture(bool bgrab){return;};
+	int CalcCursorPos(char *txt, POINT p, POINT *fit);
+	bool TextCursor(char *txt, POINT p, POINT *fit, int *pos, int disp);
+	bool PatLine(POINT p1, POINT p2);
+	virtual void ShowLine(POINT * pts, int cp, DWORD color) {return;};
+	virtual void ShowEllipse(POINT p1, POINT p2, DWORD color){return;}; 
+	virtual void ShowInvert(RECT *rec){return;};
+	virtual bool SetMenu(int type){return false;};
+	virtual void CheckMenu(int mid, bool check){return;};
+	virtual void FileHistory(){return;};
+	virtual bool oGetPix(int x, int y, DWORD *col){return false;};
+	virtual bool oDrawIcon(int type, int x, int y) {return false;};
+	virtual bool oGetTextExtent(char *text, int cb, int *width, int *height);
+	virtual bool oGetTextExtentW(w_char *text, int cb, int *width, int *height);
+	virtual bool oCircle(int x1, int y1, int x2, int y2, char *nam = 0L){return false;};
+	virtual bool oSphere(int cx, int cy, int r, POINT *pts, int cp, char *nam = 0L);
+	virtual bool oPolyline(POINT * pts, int cp, char *nam = 0L){return false;};
+	virtual bool oRectangle(int x1, int y1, int x2, int y2, char *nam = 0L){return false;};
+	virtual bool oSolidLine(POINT *p){return false;};
+	virtual bool oTextOut(int x, int y, char *txt, int cb){return false;};
+	virtual bool oTextOutW(int x, int y, w_char *txt, int cb){return false;};
+	virtual bool oPolygon(POINT *pts, int cp, char *nam = 0L){return false;};
+};
+
+enum {ET_UNKNOWN, ET_VALUE, ET_TEXT, ET_FORMULA, ET_ERROR, ET_BOOL, 
+	ET_DATE, ET_TIME, ET_DATETIME, ET_BUSY=0x100, ET_CIRCULAR=0x200, ET_EMPTY=0x400, 
+	ET_NODRAW=0x800, ET_NODRAW_EMPTY=0xc00};
+
+class EditText {
+public:
+	char *text, *ftext;
+	int Align, type, row, col;
+	void *parent;				//points to a data object: defined below
+	anyOutput *disp;
+	double Value;
+
+	EditText(void *par, char *msg, int r, int c);
+	~EditText();
+	bool AddChar(int c, anyOutput *Out, void *data_obj);
+	void Update(int select, anyOutput *Out, POINT *MousePos);
+	bool Redraw(anyOutput *Out, bool display);
+	void Mark(anyOutput *Out, int mark);
+	bool Command(int cmd, anyOutput *Out, void *data_obj);
+	bool GetValue(double *v);
+	bool GetText(char *t, int size, bool bTranslate);
+	bool GetResult(anyResult *r, bool use_last = false);
+	bool SetValue(double v);
+	bool SetText(char *t);
+	void SetRec(RECT *rc);
+	int GetX() {return loc.x;};
+	int GetY() {return loc.y;};
+	int GetRX() {return crb.x;};
+	int GetRY() {return crb.y;};
+	int Cursor() {return CursorPos;};
+	bool isValue();
+	bool isFormula();
+	bool isInRect(POINT *p) {return (p->x>loc.x && p->x<crb.x && p->y>loc.y && p->y<rb.y);};
+	bool hasMark() {return (m1 != m2 && m1 >= 0 && m2 >= 0);};
+
+private:
+	LineDEF *bgLine;
+	FillDEF *bgFill;
+	int length, CursorPos, m1, m2, mx1, mx2;
+	POINT loc, rb, crb;
+	DWORD TextCol;
+
+	void FindType();
+	void set_etracc();
+};
+
+class fmtText {
+
+typedef struct _fmt_txt_info {
+	int tag, uc, uc_len;
+	char *txt;
+}fmt_txt_info;
+
+typedef struct _fmt_uc_info {
+	int tag, cb;
+	w_char *uc_txt;
+}fmt_uc_info;
+
+public:
+	fmtText();
+	fmtText(anyOutput *o, int x, int y, char *txt);
+	~fmtText();
+	bool StyleAt(int idx,  TextDEF *txt_def, int *style, int *font);
+	int rightTag(char *txt, int cb);
+	int leftTag(char *txt, int cb);
+	int ucTag(char *txt, int cb, int *tl, int *ucc);
+	int ucLeft(char *txt, int cb, int *tl, int *ucc);
+	void cur_right(int *pos);
+	void cur_left(int *pos);
+	bool oGetTextExtent(anyOutput *o, int *width, int *height, int cb);
+	void SetText(anyOutput *o, char *txt, int *px, int *py);
+	void DrawText(anyOutput *o);
+	void EditMode(bool bEdit);
+
+private:
+	bool SetTextDef(TextDEF *td, int idx);
+	bool Parse();
+	void DrawBullet(anyOutput *o, int x, int y, int type, double size, DWORD lc, DWORD fc);
+	bool DrawFormattedW(anyOutput *o);
+	void DrawFormatted(anyOutput *o);
+
+	char *src;
+	POINT pos;
+	int n_split, n_split_W, uc_state;
+	DWORD flags;
+	fmt_txt_info *split_text;
+	fmt_uc_info *split_text_W;
+};
+
+class TextValue {
+
+typedef struct _TxtValItem {
+	unsigned int hv1, hv2;
+	char *text;
+	double val;
+}TextValItem;
+
+public:
+	TextValue();
+	TextValue(TextValItem **tvi, int ntvi);
+	~TextValue();
+	double GetValue(char* txt);
+	bool GetItem(int idx, char **text, double *value);
+	void Reset();
+	TextValue *Copy();
+	int Count() {return nitems;};
+
+private:
+	TextValItem **items;
+	int nitems;
+	double next, inc;
+};
+
+class RangeInfo {
+public:
+	int col_width, row_height, first_width;
+	RangeInfo(int sel, int cw, int rh, int fw, RangeInfo *next);
+	RangeInfo(int sel, char *range, RangeInfo *next);
+	~RangeInfo();
+	int SetWidth(int w);
+	int SetDefWidth(int w);
+	int SetHeight(int h);
+	int SetFirstWidth(int w);
+	int GetWidth(int col);
+	int GetHeight(int row);
+	int GetFirstWidth();
+	int Type(){return r_type;};
+	RangeInfo *Next(){return ri_next;};
+
+private:
+	int r_type;		// 0 default
+					// 1 column info
+					// 2 row info
+	RangeInfo *ri_next;
+	AccRange *ar;
+};
+
+class DataObj{
+public:
+	int cRows, cCols, c_disp, r_disp;
+	RangeInfo *ri;
+	EditText ***etRows;
+
+	DataObj();
+	~DataObj();
+	virtual bool Init(int nRows, int nCols);
+	virtual bool mpos2dpos(POINT *mp, POINT *dp, bool can_scroll){return false;};
+	virtual bool SetValue(int row, int col, double val);
+	virtual bool SetText(int row, int col, char *txt);
+	virtual bool GetValue(int row, int col, double *v);
+	virtual bool GetText(int row, int col, char *txt, int len, bool bTranslate = true);
+	char **GetTextPtr(int row, int col);
+	virtual bool GetResult(anyResult *r, int row, int col, bool use_last = false);
+	virtual bool GetSize(int *width, int *height);
+	virtual bool isEmpty(int row, int col);
+	virtual bool Select(POINT *p){return false;};
+	virtual bool WriteData(char *FileName) {return false;};
+	virtual bool AddCols(int nCols){return false;};
+	virtual bool AddRows(int nRows){return false;};
+	virtual bool ChangeSize(int nCols, int nRows, bool bUndo){return false;};
+	virtual bool Command(int cmd, void *tmpl, anyOutput *o){return false;};
+	virtual bool ReadData(char *, unsigned char *, int){return false;};
+	virtual void FlushData();
+	bool ValueRec(RECT *rc);
+};
+
+class StrData {
+public:
+	StrData(DataObj *par, RECT *rc = 0L);
+	~StrData();
+	bool GetSize(int *w, int *h);
+	void RestoreData(DataObj *dest);
+
+private:
+	int pw, ph;
+	RECT drc;
+	DataObj *src;
+	char ***str_data;
+};
+
+class HatchOut:public anyOutput {
+public:
+	HatchOut(anyOutput *Parent);
+	~HatchOut();
+	bool SetFill(FillDEF *fill);
+	bool StartPage();
+	bool oCircle(int x1, int y1, int x2, int y2, char *nam = 0L);
+	bool oSphere(int cx, int cy, int r, POINT *pts, int cp, char *nam = 0L);
+	bool oRectangle(int x1, int y1, int x2, int y2, char *nam = 0L);
+	bool oPolygon(POINT *pts, int cp, char *nam = 0L);
+
+private:
+	anyOutput *out;
+	double xbase, ybase;
+	LineDEF ParLineDef, MyLineDef;
+	bool ParInit;
+	int ho, ht;
+	DWORD ParLinPat;
+	RECT UseRect;
+	struct {
+		int cx, cy, r;
+		}circ_grad;
+
+	bool PrepareParent(bool Restore);
+	bool DoHatch();
+	bool MkPolyLine(POINT *p, anyOutput *o);
+	bool HatchLine(POINT p1, POINT p2);
+	bool HatchArc(int x, int y, int r, int qad, bool start);
+	bool IsInside(POINT p);
+	void Lines000();
+	void Lines090();
+	void Lines045();
+	void Lines315();
+	void Stipple(int type);
+	void Zigzag();
+	void Combs();
+	void BricksH();
+	void BricksV();
+	void Bricks045();
+	void Bricks315();
+	void Texture1();
+	void Texture2();
+	void Arcs(int type);
+	void Waves2(int type);
+	void Herringbone();
+	void Circles();
+	void Grass();
+	void Foam();
+	void Recs();
+	void Hash();
+	void CircGrad();
+};
+
+class GraphObj {
+public:
+	unsigned long Id;			//accepts an identifier during read from file
+								//  it is set to an object identifier after
+								//  construction
+	GraphObj *parent;
+	DataObj *data;
+	int type, moveable;
+	RECT rDims;
+	char *name;
+
+	GraphObj(GraphObj *par, DataObj *d);
+	virtual ~GraphObj();
+	virtual double GetSize(int select);
+	virtual bool SetSize(int select, double value){return false;};
+	virtual DWORD GetColor(int select);
+	virtual bool SetColor(int select, DWORD col) {return false;};
+	virtual void DoPlot(anyOutput *target){return;};
+	virtual void DoMark(anyOutput *target, bool mark){return;};
+	virtual bool Command(int cmd, void *tmpl, anyOutput *o){return false;};
+	virtual bool PropertyDlg(){return false;};
+	virtual void RegGO(void *n);
+	virtual bool FileIO(int rw) {return false;};
+	virtual void * ObjThere(int x, int y);
+	virtual void Track(POINT *p, anyOutput *o);
+	virtual double DefSize(int select);
+	virtual bool hasTransp(){return false;};
+};
+
+class ssButton:public GraphObj {
+public:
+
+	ssButton(GraphObj *par, int x, int y, int w, int h);
+	~ssButton();
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *target, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+
+private:
+	bool bLBdown, bSelected, bMarked;
+	TextDEF TextDef;
+	LineDEF Line;
+	FillDEF Fill;
+};
+
+class dragHandle:public GraphObj {
+public:
+
+	dragHandle(GraphObj *par, int type);
+	~dragHandle();
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void * ObjThere(int x, int y);
+	void Track(POINT *p, anyOutput *o);
+
+private:
+	RECT upd, drec, *minRC, *maxRC;
+	LineDEF LineDef;
+	FillDEF FillDef;
+};
+
+class dragRect:public GraphObj {
+public:
+
+	dragRect(GraphObj *par, int type);
+	~dragRect();
+	double GetSize(int select){return parent->GetSize(select);};
+	DWORD GetColor(int select){return parent->GetColor(select);};
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void*tmpl, anyOutput *o);
+	void * ObjThere(int x, int y);
+
+private:
+	dragHandle **handles;
+};
+
+class Drag3D:public GraphObj {
+public:
+	Drag3D(GraphObj *par);
+	~Drag3D();
+	double GetSize(int select){return parent->GetSize(select);};
+	void DoPlot(anyOutput *o);
+	void * ObjThere(int x, int y);
+
+private:
+	dragHandle **handles;
+};
+
+class FrmRect:public GraphObj {
+public:
+
+	FrmRect(GraphObj *par, fRECT *limRC, fRECT *cRC, fRECT *chld);
+	~FrmRect();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoMark(anyOutput *o, bool mark);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg(){return parent ? parent->Command(CMD_CONFIG, 0L, 0L) : false;};
+	void * ObjThere(int x, int y);
+	void Track(POINT *p, anyOutput *o);
+
+private:
+	RECT *minRC, *maxRC;
+	anyOutput *mo;
+	RECT mrc;
+	dragRect *drag;
+	bool swapX, swapY;
+	fRECT *limRC, *cRC, *chldRC, CurrRect;
+	LineDEF Line, FillLine;
+	FillDEF Fill;
+};
+
+class svgOptions:public GraphObj{
+public:
+	svgOptions(int src);
+	~svgOptions();
+	bool FileIO(int rw);
+
+private:
+	char *script, *svgattr;
+};
+
+class Symbol:public GraphObj{
+public:
+	int idx;
+
+	Symbol(GraphObj *par, DataObj *d, double x, double y, int which, 
+		int xc = -1, int xr = -1, int yc = -1, int yr = -1);
+	Symbol(int src);
+	~Symbol();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	DWORD GetColor(int select);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *target);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+private:
+	lfPOINT fPos;
+	POINT *ssRef;
+	long cssRef;
+	double size;
+	LineDEF SymLine;
+	FillDEF SymFill;
+	TextDEF *SymTxt;
+};
+
+class Bubble:public GraphObj{
+public:
+	Bubble(GraphObj *par, DataObj *d, double x, double y, double s, 
+		int which, FillDEF *fill, LineDEF *outline, int xc = -1, 
+		int xr = -1, int yc = -1, int yr = -1, int sc = -1, int sr = -1);
+	Bubble(int src);
+	~Bubble();
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+private:
+	bool DoAutoscale(anyOutput *o);
+
+	lfPOINT fPos;
+	double fs;
+	LineDEF BubbleLine, BubbleFillLine;
+	FillDEF BubbleFill;
+	POINT pts[5];
+	POINT *ssRef;
+	long cssRef;
+};
+
+class Bar:public GraphObj {
+public:
+	Bar(GraphObj *par, DataObj *d, double x, double y, int which, 
+		int xc = -1, int xr = -1, int yc = -1, int yr = -1, char *desc = 0L);
+	Bar(int src);
+	~Bar();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *target);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+private:
+	anyOutput *mo;
+	RECT mrc;
+	double size;
+	LineDEF BarLine, HatchLine;
+	FillDEF BarFill;
+	lfPOINT fPos, BarBase;
+	POINT *ssRef;
+	long cssRef;
+};
+
+class DataLine:public GraphObj{
+public:
+	bool isPolygon, dirty;
+	lfPOINT *Values;
+	lfPOINT min, max;
+	LineDEF LineDef;
+	FillDEF pgFill;
+	LineDEF pgFillLine;
+	long nPnt, nPntSet, cp;
+	DWORD BgColor;
+	POINT *pts;
+	char *ssXref, *ssYref;
+	anyOutput *mo;
+	RECT mrc;
+
+	DataLine(GraphObj *par, DataObj *d, char *xrange=0L, char *yrange=0L, char *name=0L);
+	DataLine(GraphObj *par, DataObj *d, lfPOINT *val, long nval, char *name);
+	DataLine(int src);
+	virtual ~DataLine();
+	bool SetColor(int select, DWORD col);
+	virtual void DoPlot(anyOutput *target);
+	virtual void DoMark(anyOutput *o, bool mark);
+	virtual bool Command(int cmd, void *tmpl, anyOutput *o);
+	virtual bool PropertyDlg();
+	virtual bool FileIO(int rw);
+
+	void FileValues(char *name, int type, double start, double step);
+	void SetValues();
+	void LineData(lfPOINT *val, long nval);
+	void DrawCurve(anyOutput *target);
+	void DrawSpline(anyOutput *target);
+};
+
+class DataPolygon:public DataLine{
+public:
+	DataPolygon(GraphObj *par, DataObj *d, char *xrange=0L, char *yrange=0L, char *name=0L);
+	DataPolygon(GraphObj *par, DataObj *d, lfPOINT *val, long nval, char *name = 0L);
+	DataPolygon(int src);
+	~DataPolygon();
+	void DoPlot(anyOutput *target);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+};
+
+class RegLine:public GraphObj{
+public:
+	RegLine(GraphObj *par, DataObj *d, lfPOINT *values, long n, int type);
+	RegLine(int src);
+	~RegLine();
+	double GetSize(int select);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+	void Recalc(lfPOINT *values, long n);
+	LineDEF *GetLine(){return &LineDef;};
+
+private:
+	long nPoints, cp;
+	double mx, my;
+	LineDEF LineDef;
+	fRECT lim, uclip;
+	lfPOINT l1, l2, l3, l4, l5;
+	DWORD BgColor;
+	POINT *pts;
+};
+
+class SDellipse:public GraphObj{
+public:
+	SDellipse(GraphObj *par, DataObj *d, lfPOINT *values, long n, int sel);
+	SDellipse(int src);
+	~SDellipse();
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+	void Recalc(lfPOINT *values, long n);
+
+private:
+	long nPoints, cp;
+	double sd1, sd2, mx, my;
+	POINT *pts;
+	LineDEF LineDef;
+	fRECT lim;
+	lfPOINT *val;
+	RegLine *rl;
+};
+
+class ErrorBar:public GraphObj{
+public:
+	ErrorBar(GraphObj *par, DataObj *d, double x, double y, double err, int type,
+		int xc=-1, int xr=-1, int yc=-1, int yr=-1, int ec=-1, int er=-1);
+	ErrorBar(int src);
+	~ErrorBar();
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *target);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+private:
+	anyOutput *mo;
+	RECT mrc;
+	lfPOINT fPos;
+	double ferr, SizeBar;
+	POINT ebpts[6];
+	LineDEF ErrLine;
+	POINT *ssRef;
+	long cssRef;
+};
+
+class Arrow:public GraphObj {
+public:
+	Arrow(GraphObj *par, DataObj *d, lfPOINT fp1, lfPOINT fp2, int which = 0,
+		int xc1=-1, int xr1=-1, int yc1=-1, int yr1=-1, int xc2=-1, int xr2=-1,
+		int yc2=-1, int yr2=-1);
+	Arrow(int src);
+	~Arrow();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	DWORD GetColor(int select){return LineDef.color;};
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+	void * ObjThere(int x, int y);
+	void Track(POINT *p, anyOutput *o);
+
+private:
+	anyOutput *mo;
+	RECT mrc;
+	dragHandle *dh1, *dh2;
+	POINT pts[5];
+	lfPOINT pos1, pos2;
+	double cw, cl;
+	LineDEF LineDef;
+	POINT *ssRef;
+	long cssRef;
+	bool bModified;
+
+	void Redraw(anyOutput *o);
+};
+
+class Box:public GraphObj {
+public:
+	Box(GraphObj *par, DataObj *d, lfPOINT fp1, lfPOINT fp2, int which, 
+		int xc1=-1, int xr1=-1, int yc1=-1, int yr1=-1, int xc2=-1, int xr2=-1,
+		int yc2=-1, int yr2=-1);
+	Box(int src);
+	~Box();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+private:
+	anyOutput *mo;
+	RECT mrc;
+	double size;
+	lfPOINT pos1, pos2;
+	POINT pts[5];
+	LineDEF Outline, Hatchline;
+	FillDEF Fill;
+	POINT *ssRef;
+	long cssRef;
+};
+
+class Whisker:public GraphObj {
+public:
+	Whisker(GraphObj *par, DataObj *d, lfPOINT fp1, lfPOINT fp2, int which,
+		int xc1=-1, int xr1=-1, int yc1=-1, int yr1=-1, int xc2=-1, int xr2=-1,
+		int yc2=-1, int yr2=-1);
+	Whisker(int src);
+	~Whisker();
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+private:
+	anyOutput *mo;
+	RECT mrc;
+	double size;
+	POINT pts[6];
+	lfPOINT pos1, pos2;
+	LineDEF LineDef;
+	POINT *ssRef;
+	long cssRef;
+};
+
+class DropLine:public GraphObj{
+public:
+	DropLine(GraphObj *par, DataObj *d, double x, double y, int which, int xc = -1, 
+		int xr = -1, int yc = -1, int yr = -1);
+	DropLine(int src);
+	~DropLine();
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+private:
+	lfPOINT fPos;
+	POINT pts[4];
+	LineDEF LineDef;
+	POINT *ssRef;
+	long cssRef;
+	bool bModified;
+};
+
+class line_segment:public GraphObj{
+public:
+	line_segment(GraphObj *par, DataObj *d, LineDEF *ld, POINT3D *p1, POINT3D *p2);
+	~line_segment();
+	double GetSize(int select);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void * ObjThere(int x, int y);
+
+private:
+	LineDEF Line;
+	POINT3D **ldata;
+	fPOINT3D fmin, fmax;
+	int *nldata, nli, ndf_go;
+	double prop;
+	bool bDrawDone;
+	GraphObj *co, **df_go;
+
+	void DoClip(GraphObj *co);
+};
+
+class sph_scanline:public GraphObj{
+public:
+	int rad;
+	bool vert;
+
+	sph_scanline(POINT3D *center, int radius, bool bVert);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+
+	void DoClip(GraphObj *co);
+	bool GetPoint(POINT *p, int sel);
+
+private:
+	POINT3D p1, p2, cent;
+	bool bValid1, bValid2;
+};
+
+class Sphere:public GraphObj{
+public:
+	Sphere(GraphObj *par, DataObj *d, int sel, double x, double y, double z, double r,
+		int xc = -1, int xr = -1, int yc = -1, int yr = -1, int zc = -1, int zr = -1,
+		int rc = -1, int rr = -1);
+	Sphere(int src);
+	~Sphere();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	DWORD GetColor(int select);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+private:
+	int ix, iy, rx, ry, nscl;
+	LineDEF Line;
+	FillDEF Fill;
+	fPOINT3D fPos, fip;
+	double size;
+	POINT *ssRef;
+	long cssRef;
+	GraphObj *co;
+	bool bModified, bDrawDone;
+	sph_scanline **scl;
+
+	void DoClip(GraphObj *co);
+	void DrawPG(anyOutput *o, int start);
+};
+
+class plane:public GraphObj{
+public:
+	plane(GraphObj *par, DataObj *d, fPOINT3D *pts, int nPts, LineDEF *line, 
+		FillDEF *fill);
+	~plane();
+	double GetSize(int select);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void * ObjThere(int x, int y);
+
+	bool GetPolygon(POINT3D **pla, int *npt, int sel);
+	double *GetVec() {return PlaneVec;};
+
+private:
+	LineDEF Line;
+	FillDEF Fill;
+	double *PlaneVec;
+	POINT3D **ldata, ReqPoint;
+	int *nldata, nli, n_ipts, n_lines, n_linept;
+	POINT *ipts;
+	lfPOINT xBounds, yBounds, zBounds;
+	bool bDrawDone, bReqPoint, bSigPol;
+	GraphObj *co;
+	line_segment **lines;
+	long totalArea;
+
+	void DoClip(GraphObj *co);
+	bool IsValidPG(POINT3D *pg, int npg);
+};
+
+class Plane3D:public GraphObj {
+public:
+	Plane3D(GraphObj *par, DataObj *da, fPOINT3D *pt, long npt);
+	Plane3D(int src);
+	~Plane3D();
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+private:
+	plane *ipl;
+	fPOINT3D *dt, *pts;
+	long ndt;
+	LineDEF Line;
+	FillDEF Fill;
+};
+
+class Brick:public GraphObj{
+public:
+	Brick(GraphObj *par, DataObj *da, double x, double y, double z, 
+		double d, double w, double h, DWORD flags, int xc = -1,
+		int xr = -1, int yc = -1, int yr = -1, int zc = -1, int zr = -1,
+		int dc = -1, int dr = -1, int wc = -1, int wr = -1, int hc = -1,
+		int hr = -1);
+	Brick(int src);
+	~Brick();
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+private:
+	LineDEF Line;
+	FillDEF Fill;
+	fPOINT3D fPos;
+	double depth, width, height;
+	DWORD flags;
+	POINT *ssRef;
+	long cssRef;
+	plane **faces;
+	anyOutput *mo;
+	RECT mrc;
+	bool bModified;
+};
+
+class DropLine3D:public GraphObj{
+public:
+	DropLine3D(GraphObj *par, DataObj *d, fPOINT3D *p1, int xc = -1,
+		int xr = -1, int yc = -1, int yr = -1, int zc = -1, int zr = -1);
+	DropLine3D(int src);
+	~DropLine3D();
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+private:
+	line_segment *ls[6];
+	POINT mpts[6][2];
+	LineDEF Line;
+	fPOINT3D fPos;
+	POINT *ssRef;
+	long cssRef;
+	bool bModified;
+	anyOutput *mo;
+	RECT mrc;
+};
+
+class Arrow3D:public GraphObj{
+public:
+	Arrow3D(GraphObj *par, DataObj *d, fPOINT3D *p1, fPOINT3D *p2, int xc1 = -1,
+		int xr1 = -1, int yc1 = -1, int yr1 = -1, int zc1 = -1, int zr1 = -1, 
+		int xc2 = -1, int xr2 = -1, int yc2 = -1, int yr2 = -1, int zc2 = -1, 
+		int zr2 = -1);
+	Arrow3D(int src);
+	~Arrow3D();
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+private:
+	line_segment *ls[3];
+	plane *cap;
+	double cw, cl;
+	POINT mpts[3][2];
+	LineDEF Line;
+	fPOINT3D fPos1, fPos2;
+	POINT *ssRef;
+	long cssRef;
+	bool bModified;
+};
+
+class Line3D:public GraphObj{
+public:
+	LineDEF Line;
+
+	Line3D(GraphObj *par, DataObj *d, char *rx, char *ry, char *rz);
+	Line3D(GraphObj *par, DataObj *d, fPOINT3D *pt, int n_pt, int xc1 = -1,
+		int xr1 = -1, int yc1 = -1, int yr1 = -1, int zc1 = -1, int zr1 = -1, 
+		int xc2 = -1, int xr2 = -1, int yc2 = -1, int yr2 = -1, int zc2 = -1, 
+		int zr2 = -1);
+	Line3D(int src);
+	~Line3D();
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+private:
+	line_segment **ls;
+	fPOINT3D *values, min, max;
+	POINT *pts;
+	long nPts, npts;
+	char *x_range, *y_range, *z_range;
+	POINT *ssRef;
+	long cssRef;
+	bool bModified;
+	anyOutput *mo;
+	RECT mrc;
+
+	void DoUpdate();
+};
+
+class Label:public GraphObj{
+public:
+	Label(GraphObj *par, DataObj *d, double x, double y, TextDEF *td, DWORD flg,
+		int xc = -1, int xr = -1, int yc = -1, int yr = -1, int tc = -1, int tr = -1);
+	Label(int src);
+	~Label();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+	void * ObjThere(int x, int y);
+	void Track(POINT *p, anyOutput *o);
+
+	bool CalcRect(anyOutput *o);
+	void RedrawEdit(anyOutput *o);
+	void ShowCursor(anyOutput *o);
+	bool AddChar(int ci, anyOutput *o);
+	void CalcCursorPos(int x, int y, anyOutput *o);
+	void SetModified();
+	void DoPlotText(anyOutput *o);
+	bool CheckMark();
+	TextDEF *GetTextDef(){return &TextDef;};
+
+private:
+	lfPOINT fPos, fDist;
+	double si, csi, curr_z;
+	DWORD flags, bgcolor;
+	TextDEF TextDef;
+	LineDEF bgLine;
+	int ix, iy, m1, m2, CursorPos;
+	bool is3D;
+	RECT Cursor, rm1, rm2;
+	POINT pts[5];
+	POINT *ssRef;
+	long cssRef;
+	anyOutput *defDisp;
+	bool bModified, bBGvalid;
+};
+
+class mLabel:public GraphObj{
+public:
+	mLabel(GraphObj *, DataObj *, double, double, TextDEF *, char *, int, DWORD);
+	mLabel(GraphObj *, DataObj *, double, double, TextDEF *, char *);
+	mLabel(int src);
+	~mLabel();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void RegGO(void *n);
+	bool FileIO(int rw);
+	void Track(POINT *p, anyOutput *o);
+
+private:
+	lfPOINT fPos, fDist, cPos, cPos1, dist;
+	double si, csi, curr_z, lspc;
+	DWORD flags, undo_flags;
+	TextDEF TextDef;
+	long nLines;
+	int cli;
+	bool is3D;
+	Label **Lines;
+};
+
+class TextFrame:public GraphObj{
+	enum {TF_MAXLINE=120};
+public:
+	TextFrame(GraphObj *parent, DataObj *data, lfPOINT *p1, lfPOINT *p2, char *txt);
+	TextFrame(int src);
+	~TextFrame();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	void DoMark(anyOutput *o, bool mark);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+	void * ObjThere(int x, int y);
+	void Track(POINT *p, anyOutput *o);
+
+private:
+	fRECT pad;
+	RECT ipad, Cursor, *tm_rec;
+	fmtText fmt_txt;
+	int nlines, linc, tm_c, csize, cpos;
+	bool bModified, bResize, has_m1, has_m2;
+	unsigned char c_char, m1_char, m2_char;
+	double lspc;
+	lfPOINT pos1, pos2;
+	POINT cur_pos, m1_pos, m2_pos, m1_cpos, m2_cpos;
+	TextDEF TextDef;
+	dragRect *drc;
+	unsigned char *text, **lines;
+	LineDEF Line, FillLine;
+	FillDEF Fill;
+
+	void text2lines(anyOutput *o);
+	void lines2text();
+	void ShowCursor(anyOutput *o);
+	void AddChar(anyOutput *o, int c);
+	void DelChar(anyOutput *o);
+	void CalcCursorPos(int x, int y, anyOutput *o);
+	void ReplMark(anyOutput *o, char *ntext);
+	void procTokens();
+	bool DoPaste(anyOutput *o);
+	void TextMark(anyOutput *o, int mode);
+	bool CopyText(anyOutput *o, bool b_cut);
+};
+
+class segment:public GraphObj{
+public:
+	segment(GraphObj *par, DataObj *d, lfPOINT *c, double r1, double r2, double a1, double a2);
+	segment(int src);
+	~segment();
+	bool SetSize(int select, double value);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+	void * ObjThere(int x, int y);
+	void Track(POINT *p, anyOutput *o);
+
+private:
+	lfPOINT fCent;
+	long nPts;
+	double radius1, radius2, angle1, angle2, shift;
+	LineDEF segLine, segFillLine;
+	FillDEF segFill;
+	POINT *pts;
+	bool bModified;
+	anyOutput *mo;
+	RECT mrc;
+};
+
+class polyline:public GraphObj {
+public:
+	dragHandle **pHandles;
+	lfPOINT *Values;
+	long nPoints, nPts;
+	POINT *pts;
+	LineDEF pgLine, pgFillLine;
+	FillDEF pgFill;
+	bool bModified;
+
+	polyline(GraphObj *par, DataObj *d, lfPOINT *fpts, int cpts);
+	polyline(int src);
+	virtual ~polyline();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	DWORD GetColor(int select);
+	virtual void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	virtual bool PropertyDlg();
+	virtual bool FileIO(int rw);
+	void * ObjThere(int x, int y);
+	void Track(POINT *p, anyOutput *o);
+
+private:
+	void ShowPoints(anyOutput *o);
+};
+
+class Bezier:public polyline {
+public:
+	Bezier(GraphObj *par, DataObj *d, lfPOINT *fpts, int cpts, int mode, double res);
+	Bezier(int src);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool FileIO(int rw);
+
+private:
+	void AddPoints(int n, lfPOINT *p);
+
+	void FitCurve(lfPOINT *d, int npt, double error);
+	void IpolBez(lfPOINT *d, lfPOINT tHat1, lfPOINT tHat2);
+	void FitCubic(lfPOINT *d, int first, int last, lfPOINT tHat1, lfPOINT tHat2, double error);
+	void RemovePoint(lfPOINT *d, int sel);
+	void GenerateBezier(lfPOINT *d, int first, int last, double * uPrime, lfPOINT tHat1, lfPOINT tHat2, lfPOINT *bezCurve);
+	double *Reparameterize(lfPOINT *d, int first, int last, double *u, lfPOINT *bezCurve);
+	lfPOINT fBezier(int degree, lfPOINT *V, double t);
+	double *ChordLengthParameterize(lfPOINT *d, int first, int last);
+	double ComputeMaxError(lfPOINT *d, int first, int last, lfPOINT *bezCurve, double *u, int *splitPoint);
+};
+
+class polygon:public polyline {
+public:
+	polygon(GraphObj *par, DataObj *d, lfPOINT *fpts, int cpts);
+	polygon(int src):polyline(src){};
+	bool PropertyDlg();
+};
+
+class rectangle:public GraphObj {
+public:
+	lfPOINT fp1, fp2;
+	LineDEF Line, FillLine;
+	FillDEF Fill;
+	double rad;
+	bool bModified;
+
+	rectangle(GraphObj *par, DataObj *d, lfPOINT *p1, lfPOINT *p2);
+	rectangle(int src);
+	virtual ~rectangle();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	DWORD GetColor(int select){return Line.color;};
+	void DoMark(anyOutput *o, bool mark);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+	void * ObjThere(int x, int y);
+	void Track(POINT *p, anyOutput *o);
+
+private:
+	POINT *pts;
+	long nPts;
+	dragRect *drc;
+	void PlotRoundRect(anyOutput *o);
+};
+
+class ellipse:public rectangle {
+public:
+	ellipse(GraphObj *par, DataObj *d, lfPOINT *p1, lfPOINT *p2);
+	ellipse(int src);
+};
+
+class roundrec:public rectangle {
+public:
+	roundrec(GraphObj *par, DataObj *d, lfPOINT *p1, lfPOINT *p2);
+	roundrec(int src);
+};
+
+class LegItem:public GraphObj{
+public:
+	DWORD flags;
+
+	LegItem(GraphObj *par, DataObj *d, LineDEF *ld, LineDEF *lf, FillDEF *fill, char *desc);
+	LegItem(GraphObj *par, DataObj *d, LineDEF *ld, Symbol *sy);
+	LegItem(GraphObj *par, DataObj *d, LineDEF *ld, int err, char *desc);
+	LegItem(int src);
+	~LegItem();
+	double GetSize(int select);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void RegGO(void *n);
+	bool FileIO(int rw);
+	void Track(POINT *p, anyOutput *o);
+
+	bool HasFill(LineDEF *ld, FillDEF *fd, char *desc);
+	bool HasSym(LineDEF *ld, GraphObj *sy);
+	bool HasErr(LineDEF *ld, int err);
+
+private:
+	LineDEF DataLine, OutLine, HatchLine;
+	FillDEF Fill;
+	Symbol *Sym;
+	Label *Desc;
+	RECT hcr;
+
+	void DefDesc(char *txt);
+};
+
+class Legend:public GraphObj{
+public:
+	Legend(GraphObj *par, DataObj *d);
+	Legend(int src);
+	~Legend();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void RegGO(void *n);
+	bool FileIO(int rw);
+	void Track(POINT *p, anyOutput *o);
+
+	bool HasFill(LineDEF *ld, FillDEF *fd, char *desc);
+	bool HasSym(LineDEF *ld, GraphObj *sy, char *desc);
+	bool HasErr(LineDEF *ld, int err, char *desc);
+
+private:
+	lfPOINT pos, lb_pos;
+	RECT trc;
+	anyOutput *to;
+	fRECT B_Rect, C_Rect, D_Rect, E_Rect, F_Rect;
+	long nItems;
+	bool hasLine;
+	LegItem **Items;
+};
+
+class Plot:public GraphObj{
+public:
+	fRECT Bounds;					//contains minima and maxima for x and y
+	bool dirty;						//rescale before redraw;
+	int use_xaxis, use_yaxis, use_zaxis;		//this plot uses its own axes
+	lfPOINT xBounds, yBounds, zBounds;	//like Bounds but in 3D space
+	int hidden;						//plot (layer) is not visible
+	int x_dtype, y_dtype, z_dtype;	//data types
+	char *x_info, *y_info, *z_info;	//descriptor used e.g. for axis label or legend
+	char *data_desc;				//descriptor for data, used for legend etc
+	TextValue *x_tv, *y_tv;			//TextValue object for ordinal axes
+
+	Plot(GraphObj *par, DataObj *d);
+	~Plot(){return;};
+	virtual double GetSize(int select);
+	virtual bool SetSize(int select, double value){return false;};
+	virtual DWORD GetColor(int select);
+	virtual bool SetColor(int select, DWORD col){return false;};
+	virtual void DoPlot(anyOutput *o){return;};
+	virtual bool Command(int cmd, void *tmpl, anyOutput *o){return false;};
+	virtual bool PropertyDlg(){return false;};
+	virtual void RegGO(void *n){return;};
+	virtual bool FileIO(int rw) {return false;};
+
+	void CheckBounds(double x, double y);
+	bool UseAxis(int idx);
+	void ApplyAxes(anyOutput *o);
+	void CheckBounds3D(double x, double y, double z);
+	bool SavVarObs(GraphObj **gol, long ngo, DWORD flags);
+	DataObj *CreaCumData(char *xr, char *yr, int mode, double base);
+};
+
+class PlotScatt:public Plot{
+public:
+	char *xRange, *yRange;
+	long nPoints;
+	int DefSym;
+	lfPOINT BarDist;
+	Bar **Bars;
+	Symbol **Symbols;
+	DataLine *TheLine;
+	ErrorBar **Errors;
+	Label **Labels;
+
+	PlotScatt(GraphObj *par, DataObj *d, DWORD presel);
+	PlotScatt(GraphObj *par, DataObj *d, int cBars, Bar **bars, ErrorBar **errs);
+	PlotScatt(GraphObj *par, DataObj *d, int nPts, Symbol **sym, DataLine *lin);
+	PlotScatt(int src);
+	virtual ~PlotScatt();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *target);
+	virtual bool Command(int cmd, void *tmpl, anyOutput *o);
+	virtual bool PropertyDlg();
+	void RegGO(void *n);
+	virtual bool FileIO(int rw);
+
+	bool ForEach(int cmd, void *tmp, anyOutput *o);
+
+private:
+	DWORD DefSel;
+	char *ErrRange, *LbRange;
+	Arrow **Arrows;
+	DropLine **DropLines;
+
+	bool CreateBarChart();
+};
+
+class xyStat:public PlotScatt{
+public:
+	xyStat(GraphObj *par, DataObj *d);
+	xyStat(int src);
+	~xyStat();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	virtual bool FileIO(int rw);
+
+	void CreateData();
+
+private:
+	double ci;
+	DataObj *curr_data;
+	char *case_prefix;
+
+};
+
+class BarChart:public PlotScatt{
+public:
+	BarChart(GraphObj *par, DataObj *d);
+};
+
+class FreqDist:public Plot {
+public:
+	FreqDist(GraphObj *par, DataObj *d);
+	FreqDist(GraphObj *par, DataObj *d, char* range, bool bOnce);
+	FreqDist(GraphObj *par, DataObj *d, double *vals, int nvals, int nclasses);
+	FreqDist(int src);
+	~FreqDist();
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+private:
+	double dmin, dmax, start, step;
+	long nPlots;
+	char *ssRef;
+	LineDEF BarLine, HatchLine;
+	FillDEF BarFill;
+	DataObj *curr_data;
+	GraphObj **plots;
+
+	void ProcData(int sel);
+};
+
+class Regression:public Plot{
+public:
+	Regression(GraphObj *par, DataObj *d);
+	Regression(int src);
+	~Regression();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *target);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+private:
+	long nPoints;
+	char *xRange, *yRange;
+	Symbol **Symbols;
+	RegLine *rLine;
+	SDellipse *sde;
+
+	void Recalc();
+};
+
+class BubblePlot:public Plot{
+public:
+	BubblePlot(GraphObj *par, DataObj *d);
+	BubblePlot(int src);
+	~BubblePlot();
+	void DoPlot(anyOutput *target);
+	DWORD GetColor(int select);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+private:
+	long nPoints;
+	LineDEF BubbleLine, BubbleFillLine;
+	FillDEF BubbleFill;
+	Bubble **Bubbles;
+};
+
+class PolarPlot:public Plot{
+public:
+	PolarPlot(GraphObj *par, DataObj *d);
+	PolarPlot(int src);
+	~PolarPlot();
+	double GetSize(int select);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+	bool AddPlot();
+	bool Config();
+
+private:
+	int nPlots, nAxes;
+	double offs;
+	anyOutput *CurrDisp;
+	fRECT CurrRect;
+	LineDEF FillLine;
+	FillDEF Fill;
+	Plot **Plots;
+	GraphObj **Axes;			//Axis not yet defined
+};
+
+class BoxPlot:public Plot {
+public:
+	BoxPlot(GraphObj *par, DataObj *d);
+	BoxPlot(int src);
+	BoxPlot(GraphObj *par, DataObj *dt, int mode, int c1, int c2, int c3, char *box_name);
+	~BoxPlot();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+	bool ForEach(int cmd, void *tmp, anyOutput *o);
+	void CreateData();
+
+private:
+	char *xRange, *yRange, *case_prefix;
+	long nPoints;
+	double ci_box, ci_err;
+	DataObj *curr_data;
+	lfPOINT BoxDist;
+	Box **Boxes;
+	Whisker **Whiskers;
+	Symbol **Symbols;
+	Label **Labels;
+	DataLine *TheLine;
+};
+
+class DensDisp:public Plot {
+public:
+	DensDisp(GraphObj *par, DataObj *d);
+	DensDisp(int src);
+	~DensDisp();
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+private:
+	LineDEF DefLine, DefFillLine;
+	FillDEF DefFill;
+	long nPoints;
+	char *xRange, *yRange;
+	Box **Boxes;
+
+	void DoUpdate();
+
+};
+
+class StackBar:public Plot{
+public:
+	int numPlots, numXY, numPG, numPL;
+	BoxPlot **Boxes;
+	PlotScatt **xyPlots;
+	DataPolygon **Polygons;
+	DataLine **Lines;
+	lfPOINT dspm;
+
+	StackBar(GraphObj *par, DataObj *d);
+	StackBar(int src);
+	virtual ~StackBar();
+	bool SetSize(int select, double value);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+private:
+	char *ssXrange, *ssYrange;
+	double StartVal;
+	int cum_data_mode;
+	DataObj *CumData;
+};
+
+class StackPG:public StackBar{
+public:
+	StackPG(GraphObj *par, DataObj *d);
+};
+
+class GroupBars:public StackBar{
+public:
+	GroupBars(GraphObj *par, DataObj *d, int type):StackBar(par, d){mode = type;};
+	bool PropertyDlg();
+
+private:
+	int mode;
+};
+
+class Waterfall:public StackBar{
+public:
+	Waterfall(GraphObj *par, DataObj *d);
+	bool PropertyDlg();
+};
+
+class MultiLines:public StackBar{
+public:
+	MultiLines(GraphObj *par, DataObj *d);
+	bool PropertyDlg();
+};
+
+class PieChart:public Plot {
+public:
+	PieChart(GraphObj *par, DataObj *d);
+	PieChart(int src);
+	virtual ~PieChart();
+	bool SetSize(int select, double value);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+	void DoUpdate();
+
+private:
+	long nPts;
+	char *ssRefA, *ssRefR;
+	lfPOINT CtDef;
+	double FacRad;
+	segment **Segments;
+};
+
+class RingChart:public PieChart {
+public:
+	RingChart(GraphObj *par, DataObj *d);
+};
+
+class GoGroup:public Plot {
+public:
+	GraphObj **Objects;
+	int nObs;
+	lfPOINT fPos;
+
+	GoGroup(GraphObj *par, DataObj *d);
+	GoGroup(int src);
+	virtual ~GoGroup();
+	double GetSize(int select);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void RegGO(void *n);
+	bool FileIO(int rw);
+};
+
+class StarChart:public GoGroup {
+public:
+	StarChart(GraphObj *par, DataObj *d);
+	bool PropertyDlg();
+
+private:
+};
+
+class Ribbon:public Plot {
+public:
+	Ribbon(GraphObj *par, DataObj *d, double z, double width, char *xr, char *yr);
+	Ribbon(GraphObj *par, DataObj *d, int which, char *xr, char *yr, char *zr);
+	Ribbon(GraphObj *par, DataObj *d, GraphObj **go, int ngo);
+	Ribbon(int src);
+	~Ribbon();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+private:
+	long nPlanes, nVal;
+	double z_value, z_width, relwidth;
+	char *ssRefX, *ssRefY, *ssRefZ;
+	FillDEF Fill;
+	LineDEF Line;
+	fPOINT3D *values;
+	Plane3D **planes;
+
+	void CreateObs();
+	void UpdateObs(bool bNewData);
+};
+
+class Grid3D:public Plot {
+public:
+	Grid3D(GraphObj *par, DataObj *d, int sel, double x1=0.0, double xstep=1.0, double z1=0.0, double zstep=1.0);
+	Grid3D(int src);
+	~Grid3D();
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+	void CreateObs(bool set_undo);
+
+private:
+	long nLines, nPlanes;
+	fPOINT3D start, step;
+	LineDEF Line;
+	FillDEF Fill;
+	Line3D **lines;
+	Plane3D **planes;
+
+	bool Configure();
+};
+
+class Scatt3D:public Plot{
+public:
+	Scatt3D(GraphObj *par, DataObj *d, DWORD flags);
+	Scatt3D(GraphObj *par, DataObj *d, Brick **cols, long nob);
+	Scatt3D(GraphObj *par, DataObj *d, Sphere **ba, long nob);
+	Scatt3D(int src);
+	~Scatt3D();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+private:
+	long nBalls, nColumns, nDropLines, nArrows;
+	DWORD c_flags;
+	char *ssRefX, *ssRefY, *ssRefZ;
+	Line3D *Line;
+	Sphere **Balls;
+	Brick **Columns;
+	DropLine3D **DropLines;
+	Arrow3D **Arrows;
+	Ribbon *rib;
+};
+
+class Limits:public Plot {
+public:
+	Limits(int src);
+	~Limits();
+	double GetSize(int select);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool FileIO(int rw);
+};
+
+class Function:public Plot{
+public:
+	Function(GraphObj *par, DataObj *d, char *desc);
+	Function(int src);
+	~Function();
+	bool SetSize(int select, double value);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+	bool Update(anyOutput *o, DWORD flags);
+	LineDEF *GetLine() {return &Line;};
+
+private:
+	double x1,x2, xstep;
+	LineDEF Line;
+	char *param;
+	char *cmdxy;
+	DataLine *dl;
+};
+
+class FitFunc:public Plot{
+public:
+	FitFunc(GraphObj *par, DataObj *d);
+	FitFunc(int src);
+	~FitFunc();
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+private:
+	double x1, x2, xstep, conv, chi2;
+	char *ssXref, *ssYref;
+	long nPoints;
+	int maxiter;
+	LineDEF Line;
+	Symbol **Symbols;
+	char *cmdxy, *parxy;
+	Function *dl;
+};
+
+class NormQuant:public Plot{
+public:
+	NormQuant(GraphObj *par, DataObj *d, char* range);
+	NormQuant(GraphObj *par, DataObj *d, double *data, int ndata);
+	NormQuant(int src);
+	~NormQuant();
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+
+private:
+	char *ssRef;
+	int nData, nValidData;
+	double *x_vals, *y_vals, *src_data;
+	Symbol *sy;
+
+	bool ProcessData();
+};
+
+	
+class GridLine:public GraphObj{
+public:
+	DWORD flags;
+	long ncpts;
+	POINT pts[6], *cpts;
+	LineDEF LineDef;
+	POINT3D *gl1, *gl2, *gl3;
+	line_segment **ls;
+	bool bModified;
+	anyOutput *mo;
+	RECT mrc;
+
+	GridLine(GraphObj *par, DataObj *d, int type, DWORD df);
+	GridLine(int src);
+	virtual ~GridLine();
+	virtual void DoPlot(anyOutput *o);
+	virtual void DoMark(anyOutput *o, bool mark);
+	virtual bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	bool FileIO(int rw);
+};
+
+class GridLine3D:public GridLine {
+public:
+	GridLine3D(GraphObj *par, DataObj *d, int type, DWORD df);
+	GridLine3D(int src);
+	~GridLine3D();
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+};
+
+class GridRadial:public GridLine {
+public:
+	GridRadial(GraphObj *par, DataObj *d, int type, DWORD df);
+	GridRadial(int src);
+	~GridRadial();
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+};
+
+class Tick:public GraphObj{
+public:
+	Tick(GraphObj *par, DataObj *d, double val, DWORD Flags);
+	Tick(int src);
+	~Tick();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+	void Track(POINT *p, anyOutput *o);
+
+	void DoPlot(double six, double csx, anyOutput *o);
+	bool ProcSeg();
+	
+private:
+	double value, size, fix, fiy, fiz, lsi, lcsi, angle, lbx, lby;
+	int n_seg, s_seg;
+	double *seg;
+	bool bModified, bValidTick;
+	long numPG;
+	anyOutput *mo;
+	RECT mrc;
+	int gl_type;
+	GridLine *Grid;
+	Label *label;
+	DataPolygon **Polygons;
+	DWORD flags;
+	POINT pts[2];
+	line_segment *ls;
+
+	bool CmpPoints(double x1, double y1, double x2, double y2);
+	bool StoreSeg(lfPOINT *line);
+};
+
+class Axis:public GraphObj{
+public:
+	AxisDEF *axis;
+	LineDEF axline;
+	TextValue *atv;
+	long NumTicks;
+	Tick **Ticks;
+
+	Axis(GraphObj *par, DataObj *d, AxisDEF *ax, DWORD flags);
+	Axis(int src);
+	~Axis();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	DWORD GetColor(int select);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+	void *ObjThere(int x, int y);
+	void Track(POINT *p, anyOutput *o);
+
+	AxisDEF *GetAxis() {return axis;};
+	bool GetValuePos(double val, double *fix, double *fiy, double *fiz, anyOutput *o);
+	void TickFile(char *name);
+	void BreakSymbol(POINT3D *p1, double dsi, double dcsi, bool connect, anyOutput *o);
+	void DrawBreaks(anyOutput *o);
+	void CreateTicks();
+	DWORD GradColor(double value);
+
+private:
+	double sizAxLine, sizAxTick, sizAxTickLabel;
+	double si, csi;
+	double brksymsize, brkgap, tick_angle;
+	int brksym, nl_segs, gl_type, tick_type, grad_type;
+	bool bModified;
+	DWORD colAxis, gCol_0, gCol_1, gCol_2, gTrans;
+	LineDEF GridLine;
+	GraphObj *axisLabel;
+	POINT pts[2], gradient_box[5];
+	POINT3D pts3D[2];
+	fPOINT3D flim[2];
+	lfPOINT lbdist, tlbdist;
+	TextDEF tlbdef;
+	anyOutput *drawOut, *scaleOut;
+	line_segment **l_segs;
+	char *ssMATval, *ssMATlbl, *ssMITval; 
+	anyOutput *mo;
+	RECT mrc;
+
+	void SetTick(long idx, double val, DWORD flags, char *txt);
+	void mkTimeAxis();
+	void ManuTicks(double sa, double st, int n, DWORD flags);
+	void UpdateTicks();
+	bool ssTicks();
+	void GradientBar(anyOutput *o);
+};
+
+class ContourPlot:public Plot {
+public:
+	ContourPlot(GraphObj *par, DataObj *d);
+	ContourPlot(int src);
+	~ContourPlot();
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+private:
+	char *ssRefX, *ssRefY, *ssRefZ;
+	Symbol **Symbols;
+	Label **Labels;
+	long nval, nSym, nLab;
+	DWORD flags;
+	double sr_zval;
+	fPOINT3D *val;
+	AxisDEF z_axis;
+	Axis *zAxis;
+
+	bool LoadData(char *xref, char *yref, char *zref);
+	bool DoTriangulate();
+	bool DoAxis(anyOutput *o);
+	void DoSymbols(Triangle *trl);
+};
+
+class Plot3D:public Plot{
+	typedef struct {
+		double Zmin, Zmax;
+		GraphObj *go;
+		}obj_desc;
+public:
+	long nPlots, nAxes;
+	Axis **Axes;
+	GraphObj **plots, **Sc_Plots;
+	double *RotDef;
+	fPOINT3D cub1, cub2, rotC;
+	int nscp;
+
+	Plot3D(GraphObj *par, DataObj *d, DWORD flags);
+	Plot3D(int src);
+	virtual ~Plot3D();
+	double GetSize(int select);
+	bool SetColor(int select, DWORD col);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	virtual bool Command(int cmd, void *tmpl, anyOutput *o);
+	virtual bool PropertyDlg();
+	virtual void RegGO(void *n);
+	virtual bool FileIO(int rw);
+
+	void * ObjThere(int x, int y);
+	void Track(POINT *p, anyOutput *o);
+	void CreateAxes();
+	void DoAutoscale();
+	void CalcRotation(double dx, double dy, anyOutput *o, bool accept);
+	bool AcceptObj(GraphObj *go);
+	void SortObj();
+	bool Rotate(double dx, double dy, double dz, anyOutput *o, bool accept);
+	bool AddAxis();
+
+private:
+	long nObs, nmaxObs;
+	DWORD crea_flags;
+	Drag3D *drag;
+	fPOINT3D cu1, cu2, rc;
+	obj_desc **dispObs;
+
+	bool AddPlot(int family);
+	int cmp_obj_desc(obj_desc *obj1, obj_desc *obj2);
+};
+
+class Chart25D:public Plot3D {
+public:
+	Chart25D(GraphObj *par, DataObj *d, DWORD flags);
+	~Chart25D();
+	bool PropertyDlg();
+
+private:
+	fPOINT3D dspm;
+};
+
+class Ribbon25D:public Plot3D {
+public:
+	Ribbon25D(GraphObj *par, DataObj *d, DWORD flags);
+	~Ribbon25D();
+	bool PropertyDlg();
+
+private:
+	fPOINT3D dspm;
+};
+
+class BubblePlot3D:public Plot3D {
+public:
+	BubblePlot3D(GraphObj *par, DataObj *d);
+	~BubblePlot3D();
+	bool PropertyDlg();
+};
+
+class Func3D:public Plot3D {
+public:
+	Func3D(GraphObj *par, DataObj *d);
+	Func3D(int src);
+	~Func3D();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+private:
+	bool Update();
+
+	double x1, x2, xstep, z1, z2, zstep;
+	LineDEF Line;
+	FillDEF Fill;
+	char *param, *cmdxy;
+	DataObj *gda;
+	Grid3D  *gob;
+};
+
+class FitFunc3D:public Plot3D {
+public:
+	FitFunc3D(GraphObj *par, DataObj *d);
+	FitFunc3D(int src);
+	~FitFunc3D();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+private:
+	bool Update();
+
+	double x1, x2, xstep, z1, z2, zstep, conv, chi2;
+	char *ssXref, *ssYref, *ssZref;
+	int maxiter;
+	LineDEF Line;
+	FillDEF Fill;
+	char *param, *cmdxy;
+	DataObj *gda;
+	Grid3D  *gob;
+};
+
+class Graph:public GraphObj{
+public:
+	long NumPlots;
+	int ToolMode, units, nscp;
+	anyOutput *Disp, *CurrDisp;
+	fRECT GRect, DRect, Bounds;
+	bool OwnDisp, bModified;
+	fRECT CurrRect;
+	GraphObj **Plots, *PasteObj;
+	DWORD ColBG, ColAX;
+	GraphObj **Sc_Plots;
+	Axis **Axes;
+	char *filename;
+
+	Graph(GraphObj *par, DataObj *d, anyOutput *o, int style);
+	Graph(int src);
+	virtual ~Graph();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	DWORD GetColor(int select);
+	bool SetColor(int select, DWORD col);
+	virtual void DoPlot(anyOutput *o);
+	virtual bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	virtual bool FileIO(int rw);
+	virtual double DefSize(int select);
+	bool hasTransp();
+
+private:
+	int NumAxes, AxisTempl, tickstyle, zoom_level;
+	double scale;
+	RECT rcDim, rcUpd, rc_mrk;
+	DWORD ColDR, ColGR, ColGRL;
+	AxisDEF x_axis, y_axis;
+	FrmRect *frm_g, *frm_d;
+	bool dirty, bDialogOpen;
+	POINT *tl_pts;
+	long tl_nPts;
+	ZoomDEF *zoom_def;
+
+	bool AddPlot(int family);
+	void DoAutoscale();
+	void CreateAxes(int templ);
+	bool ExecTool(MouseEvent *mev);
+	bool Configure();
+	bool AddAxis();
+	bool MoveObj(int cmd, GraphObj *g); 
+	bool DoZoom(char *z);
+	bool DoScale(scaleINFO *sc, anyOutput *o);
+};
+
+class Page:public Graph{
+public:
+	Page(GraphObj *par, DataObj *d);
+	Page(int src);
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void RegGO(void *n);
+	bool FileIO(int rw);
+	double DefSize(int select);
+
+private:
+	LineDEF LineDef;
+	FillDEF FillDef;
+
+	bool Configure();
+};
+
+class ObjTree:public GraphObj {
+public:
+	ObjTree(GraphObj *par, DataObj *d, GraphObj *root);
+	~ObjTree();
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+
+	anyOutput *CreateBitmap(int *w, int *h, anyOutput *tmpl);
+	int count_lines(){ return count;};
+	char *get_name(int line);
+	int get_vis(int line);
+	bool set_vis(int line, bool vis);
+	GraphObj *get_obj(int line);
+
+private:
+	GraphObj **list, *base;
+	int count, maxcount;
+	TextDEF TextDef;
+};
+
+class notary{
+public:
+	notary();
+	~notary();
+	int RegisterGO(GraphObj *go);
+	void AddRegGO(GraphObj *go);
+	bool PushGO(unsigned int id, GraphObj *go);
+	GraphObj *PopGO(unsigned int id);
+	void FreeStack();
+
+private:
+	unsigned int NextPopGO, NextPushGO, NextRegGO;
+	GraphObj ***gObs;
+	GraphObj ***goStack;
+};
+
+class Default{
+public:
+	int dUnits, cUnits, iMenuHeight;
+	char DecPoint[2], ColSep[2];
+	char *svgAttr, *svgScript, *currPath, *IniFile;
+	char *File1, *File2, *File3, *File4, *File5, *File6;
+	char *fmt_date, *fmt_time, *fmt_datetime;
+	double min4log, ss_txt;
+	RECT clipRC;
+
+	Default();
+	~Default();
+	void SetDisp(anyOutput *o);
+	double GetSize(int select);
+	DWORD Color(int select);
+	LineDEF *GetLine();
+	void SetLine(int u, LineDEF *l, int which);
+	FillDEF *GetFill();
+	void SetFill(int u, FillDEF *fd);
+	LineDEF *GetOutLine();
+	bool PropertyDlg();
+	LineDEF *plLineDEF(LineDEF *ld);
+	LineDEF *pgLineDEF(LineDEF *ol);
+	FillDEF *pgFillDEF(FillDEF *fd);
+	double rrectRad(double rad);
+	void FileHistory(char *path);
+	void Idle(int cmd);
+	void UpdRect(anyOutput *o, int x1, int y1, int x2, int y2);
+	void UpdAdd(anyOutput * o, int x, int y);
+
+private:
+	LineDEF Line_0, Line_1, Line_2, *pl, *pgl;
+	FillDEF Fill_0, Fill_1, Fill_2, *pg;
+	LineDEF FillLine_0, FillLine_1, FillLine_2, *pg_fl;
+	LineDEF OutLine_0, OutLine_1, OutLine_2;
+	double *rrect_rad;
+	anyOutput *cdisp, *out_upd;
+	RECT rec_upd;
+	bool can_upd;
+	DWORD axis_color;
+};
+
+class DefsRW:public GraphObj{
+public:
+	DefsRW():GraphObj(0, 0){Id = 0; return;};
+	DefsRW(int rw):GraphObj(0,0){FileIO(rw);Id=GO_DEFRW;return;};
+	~DefsRW() {return;};
+	bool FileIO(int rw);
+};
+
+class ReadCache{
+public:
+	unsigned char last, *Cache, Line[4096];
+	int iFile, idx, max;
+	bool eof;
+
+	ReadCache();
+	~ReadCache();
+	virtual bool Open(char *name);
+	virtual void Close();
+	virtual unsigned char Getc();
+	virtual unsigned char *GetField();
+	void ReadLine(char *dest, int size);
+	bool GetInt(long *in);
+	bool GetFloat(double *fn);
+	unsigned char Lastc();
+	bool IsEOF();
+};
+
+class MemCache:public ReadCache{
+public:
+	MemCache(unsigned char *ptr);
+	~MemCache();
+	bool Open(char *name){return false;};
+	void Close(){return;};
+	unsigned char Getc();
+	unsigned char *GetField();
+};
+
+#define UNDO_CONTINUE 0x01
+#define UNDO_STORESET 0x1000
+class UndoObj {
+	enum {UNDO_UNDEFINED, UNDO_DEL_GO, UNDO_GOLIST, UNDO_DROPMEM,
+		UNDO_VALDWORD, UNDO_VALINT, UNDO_VALLONG, UNDO_OBJCONF, UNDO_OBJCONF_1,
+		UNDO_LFP, UNDO_POINT, UNDO_VOIDPTR, UNDO_MOVE, UNDO_RECT,
+		UNDO_STRING, UNDO_ROTDEF, UNDO_SETGO, UNDO_LINEDEF, UNDO_FILLDEF,
+		UNDO_AXISDEF, UNDO_LFP3D, UNDO_FLOAT, UNDO_MEM, UNDO_MUTATE, 
+		UNDO_DROPGOLIST, UNDO_TEXTDEF, UNDO_SAVVAR, UNDO_DATA, UNDO_ET, UNDO_TEXTBUF};
+	typedef struct _UndoInfo {
+		int cmd;
+		DWORD flags;
+		GraphObj *owner;
+		void *data;
+		void **loc;
+		ZoomDEF zd;
+		}UndoInfo;
+
+	typedef struct _UndoList {
+		void *array;
+		void **loc_arr;
+		long count, size;
+		long *loc_count;
+		}UndoList;
+
+	typedef struct _UndoBuff {
+		int count;
+		UndoInfo **buff;
+		anyOutput *disp;
+		}UndoBuff;
+
+	typedef struct _EtBuff {
+		char *txt;
+		DataObj *DaO;
+		int *cur, *m1, *m2, vcur, vm1, vm2, row, col;
+		}EtBuff;
+
+	typedef struct _TextBuff {
+		int *psize, size, *ppos, pos;
+		unsigned char **pbuff, *buff;
+		}TextBuff;
+
+public:
+	int *pcb;
+	anyOutput *cdisp, *ldisp;
+	bool busy;
+
+	UndoObj();
+	~UndoObj();
+	void Flush();
+	bool isEmpty(anyOutput *o);
+	void SetDisp(anyOutput *o);
+	void KillDisp(anyOutput *o);
+	void InvalidGO(GraphObj *go);
+	void Pop(anyOutput *o);
+	void Restore(bool redraw, anyOutput *o);
+	void ListGOmoved(GraphObj **oldlist, GraphObj **newlist, long size);
+	void DeleteGO(GraphObj **go, DWORD flags, anyOutput *o);
+	void MutateGO(GraphObj **old, GraphObj *repl, DWORD flags, anyOutput *o);
+	void StoreListGO(GraphObj *parent, GraphObj ***go, long *count, DWORD flags);
+	void DropListGO(GraphObj *parent, GraphObj ***go, long *count, DWORD flags);
+	void DropMemory(GraphObj *parent, void **mem, DWORD flags);
+	void SavVarBlock(GraphObj *parent, void **mem, DWORD flags);
+	void ValDword(GraphObj *parent, DWORD *val, DWORD flags);
+	void Point(GraphObj *parent, POINT *pt, anyOutput * o, DWORD flags);
+	void VoidPtr(GraphObj *parent, void **pptr, void *ptr, anyOutput * o, DWORD flags);
+	void ValInt(GraphObj *parent, int *val, DWORD flags);
+	void ValLong(GraphObj *parent, long *val, DWORD flags);
+	void ObjConf(GraphObj *go, DWORD flags);
+	int SaveLFP(GraphObj *go, lfPOINT *lfp, DWORD flags);
+	void MoveObj(GraphObj *go, lfPOINT *lfp, DWORD flags);
+	void ValRect(GraphObj *go, fRECT *rec, DWORD flags);
+	int String(GraphObj *go, char **s, DWORD flags);
+	void RotDef(GraphObj *go, double **d, DWORD flags);
+	void SetGO(GraphObj *parent, GraphObj **where, GraphObj *go, DWORD flags);
+	void Line(GraphObj *go, LineDEF *ld, DWORD flags);
+	void Fill(GraphObj *go, FillDEF *fd, DWORD flags);
+	void AxisDef(GraphObj *go, AxisDEF *ad, DWORD flags);
+	void TextDef(GraphObj *go, TextDEF *td, DWORD flags);
+	void ValLFP3D(GraphObj *go, fPOINT3D *lfp, DWORD flags);
+	void ValFloat(GraphObj *parent, double *val, DWORD flags);
+	void DataMem(GraphObj *go, void **mem, int size, long *count, DWORD flags);
+	void DataObject(GraphObj *go, anyOutput *o, DataObj *d, RECT *rc, DWORD flags);
+	void TextCell(EditText *et, anyOutput *o, char *text, int *cur, int *m1, int *m2, void* DaO, DWORD flags);
+	void TextBuffer(GraphObj *parent, int *psize, int *ppos, unsigned char **pbuff, DWORD flags, anyOutput *o); 
+
+private:
+	UndoInfo **buff, **buff0;
+	int stub1, ndisp;
+	UndoBuff **buffers;
+
+	int NewItem(int cmd, DWORD flags, GraphObj *owner, void *data, void **loc);
+	void FreeInfo(UndoInfo** inf);
+	void RestoreConf(UndoInfo *inf);
+	void RestoreData(UndoInfo *inf);
+};
+
+//prototypes: spreadwi.cpp
+int ProcMemData(GraphObj *g, unsigned char *ptr, bool dispatch);
+void SpreadMain(bool show);
+
+//prototypes: WinSpec.cpp or QT_Spec.cpp
+char *SaveDataAsName(char *oldname);
+char *SaveGraphAsName(char *oldname);
+char *OpenGraphName(char *oldname);
+char *OpenDataName(char *oldname);
+void InfoBox(char *Msg);
+void ErrorBox(char *Msg);
+bool YesNoBox(char *Msg);
+int YesNoCancelBox(char *Msg);
+void Qt_Box();
+void HideTextCursor();
+void HideTextCursorObj(anyOutput *out);
+void ShowTextCursor(anyOutput *out, RECT *disp, DWORD color);
+void HideCopyMark();
+void ShowCopyMark(anyOutput *out, RECT *mrk, int nRec);
+void InvalidateOutput(anyOutput *o);
+void SuspendAnimation(anyOutput *o, bool bSusp);
+void InitTextCursor(bool init);
+void EmptyClip();
+void CopyText(char *txt, int len);
+unsigned char* PasteText();
+void GetDesktopSize(int *width, int *height);
+void FindBrowser();
+void LoopDlgWnd();
+void CloseDlgWnd(void *hDlg);
+void ShowDlgWnd(void *hDlg);
+void ResizeDlgWnd(void *hDlg, int w, int h);
+anyOutput *NewDispClass(GraphObj *g);
+bool DelDispClass(anyOutput *w);
+anyOutput *NewBitmapClass(int w, int h, double hr, double vr);
+bool DelBitmapClass(anyOutput *w);
+
+//prototypes: FileIO.cpp
+bool SaveGraphAs(GraphObj *g);
+char *GraphToMem(GraphObj *g, long *size);
+void UpdGOfromMem(GraphObj *go, unsigned char *buff);
+bool OpenGraph(GraphObj *root, char *name, unsigned char *mem, bool bPaste);
+void SavVarInit(long len);
+void *SavVarFetch();
+
+//prototypes:TheDialog.cpp
+DWORD GetNewColor(DWORD oldcol);
+bool ShowLayers(GraphObj *root);
+void GetNewFill(FillDEF *oldfill);
+void ShowBanner(bool show);
+void RLPlotInfo();
+bool DoSpShSize(DataObj *dt, GraphObj *parent);
+bool FillSsRange(DataObj *d, char **range, GraphObj *msg_go);
+bool GetBitmapRes(double *res, double *width, double *height, char *header);
+void OD_scheme(int, void *, RECT *, anyOutput *, void *, int);
+FillDEF *GetSchemeFill(int *i);
+void OD_linedef(int, void *, RECT *, anyOutput *o, void *, int);
+void OD_filldef(int, void *, RECT *, anyOutput *o, void *, int);
+void OD_paperdef(int, void *, RECT *, anyOutput *o, void *, int);
+void FindPaper(double w, double h, double tol);
+bool GetPaper(double *w, double *h);
+void OD_axisplot(int, void *, RECT *, anyOutput *o, void *, int);
+
+//prototypes: Utils.cpp
+anyOutput *GetRectBitmap(RECT *rc, anyOutput *src);
+void RestoreRectBitmap(anyOutput **pmo, RECT *mrc, anyOutput *o);
+void NiceAxis(AxisDEF *axis, int nTick);
+void NiceStep(AxisDEF *axis, int nTick);
+double base4log(AxisDEF *axis, int direc);
+double TransformValue(AxisDEF *axis, double val, bool transform);
+void SortAxisBreaks(AxisDEF *axis);
+double GetAxisFac(AxisDEF *axis, double delta, int direc);
+char *str_ltrim(char *str);
+char *str_rtrim(char *str);
+char *str_trim(char *str);
+void rmquot(char *str);
+int strpos(char *needle, char *haystack);
+char *strreplace(char *needle, char *replace, char *haystack);
+char *substr(char *text, int pos1, int pos2);
+int rlp_strcpy(char*dest, int size, char*src);
+void ReshapeFormula(char **text);
+void TranslateResult(anyResult *res);
+void CleanTags(char *txt, int *i1, int *i2, int *i3);
+void ChangeChar(char *text, char c1, char c2);
+char *Int2Nat(char *Text);
+char *Nat2Int(char *Text);
+void WriteNatFloatToBuff(char *buff, double val);
+bool Txt2Flt(char *txt, double *val);
+void RmTrail(char *txt);
+double NiceValue(double fv);
+char *NiceTime(double val);
+char *Int2ColLabel(int nr, bool uc);
+char *mkCellRef(int row, int col);
+char *mkRangeRef(int r1, int c1, int r2, int c2);
+char *str2xml(char *str, bool bGreek);
+char **split(char *str, char sep, int *nl);
+char *fit_num_rect(anyOutput *o, int max_width, char *num_str);
+void add_to_buff(char** dest, int *pos, int *csize, char *txt, int len);
+void add_int_to_buff(char** dest, int *pos, int *csize, int value, bool lsp, int ndig);
+void add_dbl_to_buff(char** dest, int *pos, int *csize, double value, bool lsp);
+void add_hex_to_buff(char** dest, int *pos, int *csize, DWORD value, bool lsp);
+void SetMinMaxRect(RECT *rc, int x1, int y1, int x2, int y2);
+void UpdateMinMaxRect(RECT *rc, int x, int y);
+void IncrementMinMaxRect(RECT *rc, int i);
+bool IsInRect(RECT *rc, int x, int y);
+bool IsCloseToLine(POINT *p1, POINT *p2, int x, int y);
+bool IsCloseToPL(POINT p, POINT *pts, int cp);
+bool IsInPolygon(POINT *p, POINT *pts, int cp);
+bool OverlapRect(RECT *rc1, RECT *rc2);
+void AddToPolygon(long *cp, POINT *pts, POINT *np);
+void DrawBezier(long *cp, POINT *pts, POINT p0, POINT p1, POINT p2, POINT p3, int depth=0);
+void ClipBezier(long *cp, POINT *pts, POINT p0, POINT p1, POINT p2, POINT p3, POINT *clp1, POINT *clp2);
+int mkCurve(lfPOINT *src, int n1, lfPOINT **dst, bool bClosed);
+POINT *MakeArc(int ix, int iy, int r, int qad, long *npts);
+void InvertLine(POINT*, int, LineDEF*, RECT*, anyOutput*, bool);
+unsigned int ColDiff(DWORD col1, DWORD col2);
+DWORD IpolCol(DWORD color1, DWORD color2, double fact);
+double ran2(long *idum);
+unsigned long isqr(unsigned long n);
+bool MatMul(double a[3][3], double b[3][3], double c[3][3]);
+char *GetNumFormat(double Magn);
+void DeleteGO(GraphObj *go);
+bool DeleteGOL(GraphObj ***gol, long n, GraphObj *go, anyOutput *o);
+bool BackupFile(char *FileName);
+bool IsRlpFile(char *FileName);
+bool IsXmlFile(char *FileName);
+bool FileExist(char *FileName);
+bool IsPlot3D(GraphObj *g);
+void *memdup(void *ptr, int cb_old, int cb_new);
+double sininv(double val);
+double trig2deg(double si, double csi);
+bool ReplaceGO(GraphObj **oldobj, GraphObj **newobj);
+unsigned int HashValue(unsigned char *str);
+unsigned int Hash2(unsigned char * str);
+bool cmpLineDEF(LineDEF *l1, LineDEF *l2);
+bool cmpFillDEF(FillDEF *f1, FillDEF *f2);
+bool cmpAxisDEF(AxisDEF *a1, AxisDEF *a2);
+bool cmpTextDEF(TextDEF *t1, TextDEF *t2);
+DWORD CheckNewFloat(double *loc, double old_v, double new_v, GraphObj *par, DWORD flags);
+DWORD CheckNewInt(int *loc, int old_v, int new_v, GraphObj *par, DWORD flags);
+DWORD CheckNewDword(DWORD *loc, DWORD old_v, DWORD new_v, GraphObj *par, DWORD flags);
+DWORD CheckNewLFPoint(lfPOINT *loc, lfPOINT *old_v, lfPOINT *new_v, GraphObj *par, DWORD flags);
+DWORD CheckNewString(char **loc, char *s_old, char *s_new, GraphObj *par, DWORD flags);
+void clip_line_sphere(GraphObj *par, POINT3D **li, int r, int cx, int cy, int cz);
+void clip_line_plane(GraphObj *par, POINT3D **li, POINT3D *pg, int np, double *vec);
+void clip_sphline_sphere(GraphObj *par, POINT3D *lim1, POINT3D *lim2, POINT3D *cent, 
+	int r1, int r2, int cx, int cy, int cz);
+void clip_sphline_plane(GraphObj *par, POINT3D *lim1, POINT3D *lim2, POINT3D *cent, 
+	int r1, POINT3D *pg, int np, double *vec);
+void clip_plane_plane(GraphObj *par, POINT3D *pg1, int n1, double *v1, POINT3D *pg2, 
+	int n2, double *v2, POINT *m, int nm);
+
+//prototypes Export.cpp
+void DoExportWmf(GraphObj *g, char *FileName, float res, DWORD flags);
+void DoExportSvg(GraphObj *g, char *FileName, DWORD flags);
+void DoExportEps(GraphObj *g, char *FileName, DWORD flags);
+
+//prototypes Output.cpp
+void DoExportTif(GraphObj *g, char *FileName, DWORD flags);
+
+//prototypes mfcalc.cpp
+void LockData(bool lockExec, bool lockWrite);
+char  *yywarn(char *txt, bool bNew);
+bool do_xyfunc(DataObj *, double, double, double, char *, lfPOINT **, long *, char *);
+bool do_func3D(DataObj *d, double x1, double x2, double xstep, double z1, double z2, double zstep, 
+	char *expr, char *param);
+anyResult *do_formula(DataObj *, char *);
+bool MoveFormula(DataObj *d, char *of, char *nf, int nfsize, int dx, int dy, int r0, int c0);
+int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr, 
+	double conv, int maxiter, double *chi_2);
+
+//prototypes rlp_math.cp
+double **dmatrix(int nrl, int nrh, int ncl, int nch);
+void free_dmatrix(double **m, int nrl, int nrh, int ncl, int);
+bool mrqmin(double *, double *, double *, int, double **, int, int *, int, double **, double **, double *,
+	void (*funcs)(double, double, double **, double *, double *, int), double *);
+bool Check_MRQerror();
+void SortArray(int n, double *vals);
+void SortArray2(int n, double *a1, double *a2);
+void SortFpArray(int n, lfPOINT *vals);
+double *randarr(double *v0, int n, long *seed);
+double *resample(double *v0, int n, long *seed);
+void spline(lfPOINT *v, int n, double *y2);
+double gammln(double x);
+double factrl(int n);
+double gammp(double a, double x);
+double gammq(double a, double x);
+double betai(double a, double b, double x);
+double bincof(double n, double k);
+double binomdistf(double k, double n, double p);
+double betaf(double z, double w);
+double errf(double x);
+double errfc(double x);
+double norm_dist(double x, double m, double s);
+double norm_freq(double x, double m, double s);
+double exp_dist(double x, double l, double s);
+double exp_inv(double p, double l, double s);
+double exp_freq(double x, double l, double s);
+double lognorm_dist(double x, double m, double s);
+double lognorm_freq(double x, double m, double s);
+double chi_dist(double x, double df, double);
+double chi_freq(double x, double df);
+double t_dist(double t, double df, double);
+double t_freq(double t, double df);
+double pois_dist(double x, double m, double);
+double f_dist(double f, double df1, double df2);
+double f_freq(double f, double df1, double df2);
+double weib_dist(double x, double shape, double scale);
+double weib_freq(double x, double shape, double scale);
+double geom_freq(double x, double p);
+double geom_dist(double x, double p);
+double hyper_freq(double k, double n0, double m, double n1);
+double hyper_dist(double k, double n0, double m, double n1);
+double cauch_dist(double x, double loc, double scale);
+double cauch_freq(double x, double loc, double scale);
+double logis_dist(double x, double loc, double scale);
+double logis_freq(double x, double loc, double scale);
+double ks_dist(int n, double d);
+void swilk1(int n, double *v0, double (*func)(double, double, double), double p1, double p2, 
+	bool bsorted, double *w, double *p);
+void KolSmir(int n, double *v0, double (*func)(double, double, double),
+	double p1, double p2, bool bsorted, double *d, double *p);
+double distinv(double (*sf)(double, double, double), double df1, double df2, double p, double x0);
+void d_quartile(int n, double *v, double *q1, double *q2, double *q3);
+double d_variance(int n, double *v, double *mean = 0L, double *ss = 0L);
+double d_amean(int n, double *v);
+double d_kurt(int n, double *v);
+double d_skew(int n, double *v);
+double d_gmean(int n, double *v);
+double d_hmean(int n, double *v);
+double d_classes(DataObj *d, double start, double step, double *v, int nv, char *range);
+double d_pearson(double *x, double *y, int n, char *dest, DataObj *data, double *ra);
+double d_rank(int n, double *v, double v1);
+void crank(int n, double *w0, double *s);
+double d_spearman(double *x, double *y, int n, char *dest, DataObj *data, double *ra);
+double d_kendall(double *x, double *y, int n, char *dest, DataObj *data, double *ra);
+double d_regression(double *x, double *y, int n, char *dest, DataObj *data, double *ra);
+double d_covar(double *x, double *y, int n, char *dest, DataObj *data);
+double d_utest(double *x, double *y, int n1, int n2, char *dest, DataObj *data, double *results);
+double d_ttest(double *x, double *y, int n1, int n2, char *dest, DataObj *data, double *results);
+double d_ttest2(double *x, double *y, int n, char *dest, DataObj *data, double *results);
+double d_ftest(double *x, double *y, int n1, int n2, char *dest, DataObj *data, double *results);
+bool do_anova1(int n, int *nv, double **vals, double **res_tab, double *gm, double **means, double **ss);
+bool bartlett(int n, int *nc, double *ss, double *chi2);
+bool levene(int type, int n, int *nv, double *means, double **vals, double *F, double *P);
+double wprob(double w, double rr, double cc);
+double ptukey(double q, double rr, double cc, double df, int lower_tail, int log_p);
+double qtukey(double p, double rr, double cc, double df, int lower_tail, int log_p);
+int year2aday(int y);
+void add_date(rlp_datetime *base, rlp_datetime *inc);
+char *date2text(rlp_datetime *dt, char *fmt);
+double date2value(rlp_datetime *dt);
+void parse_datevalue(rlp_datetime *dt, double dv);
+bool date_value(char *desc, char *fmt, double *value);
+char *value_date(double dv, char *fmt);
+double now_today();
+void split_date(double dv, int *y, int *mo, int *dom, int *dow, int *doy, int *h, int *m, double *s);
+Triangle* Triangulate1(char *xr, char *yr, char *zr, DataObj *data);
+
+//prototypes reports.cpp
+void rep_anova(GraphObj *parent, DataObj *data);
+void rep_bdanova(GraphObj *parent, DataObj *data);
+void rep_twanova(GraphObj *parent, DataObj *data);
+void rep_fmanova(GraphObj *parent, DataObj *data);
+void rep_twoway_anova(GraphObj *parent, DataObj *data);
+void rep_kruskal(GraphObj *parent, DataObj *data);
+void rep_samplestats(GraphObj *parent, DataObj *data);
+void rep_regression(GraphObj *parent, DataObj *data);
+void rep_robustline(GraphObj *parent, DataObj *data);
+void rep_twowaytable(GraphObj *parent, DataObj *data);
+void rep_compmeans(GraphObj *parent, DataObj *data);
+void rep_correl(GraphObj *parent, DataObj *data, int style);
+#endif //_RLPLOT_H
diff --git a/rlplot.spec b/rlplot.spec
index b1d823b..9d29338 100755
--- a/rlplot.spec
+++ b/rlplot.spec
@@ -1,5 +1,5 @@
 Name:      rlplot
-Version:   1.4
+Version:   1.5
 Release:   1
 Summary:   A plotting program to create high quality graphs from data.
 License:   GPL
@@ -41,6 +41,9 @@ rm -rf "$RPM_BUILD_ROOT"
 %{_bindir}/exprlp
 
 %changelog
+* Fri May 2 2008 Reinhard Lackner
+- release 1.5
+
 * Fri Sep 14 2007 Reinhard Lackner
 - release 1.4
 
diff --git a/rlplot.spec~ b/rlplot.spec~
deleted file mode 100644
index b63355e..0000000
--- a/rlplot.spec~
+++ /dev/null
@@ -1,70 +0,0 @@
-Name:      rlplot
-Version:   1.3
-Release:   1
-Summary:   A plotting program to create high quality graphs from data.
-License:   GPL
-URL:       http://rlplot.sourceforge.net
-Group:     Applications/Engineering
-Prefix:    %{_prefix}
-BuildRoot: %{_tmppath}/%{name}-buildroot
-Source:    %{name}_%{version}.tar.gz
-
-%description
-RLPlot is is a plotting program to create high quality graphs from data.
-Based on values stored in a spreadsheet several menus help you to create
-graphs of your choice. The Graphs are displayed as you get them (WYSIWIG).
-Double click any element of the graph (or a single click with the right
-mouse button) to modify its properties. RLPlot is a cross platform
-development for Linux and Windows. Exported file formats include
-Scalable Vector Graphics (SVG), Encapsulated
-Postscript (EPS). 
-
-%prep
-%setup -q -n %{name}
-
-%build
-make -e
-
-%install
-rm -rf $RPM_BUILD_ROOT
-mkdir -p ${RPM_BUILD_ROOT}%{_bindir}
-install -m 755 %{name} $RPM_BUILD_ROOT%{_prefix}/bin
-install -m 755 exprlp $RPM_BUILD_ROOT%{_prefix}/bin
-
-%clean
-rm -rf "$RPM_BUILD_ROOT"
-
-%files
-%defattr(-,root,root)
-%doc README RLPlot.bmp RLPLOT.ICO RLPlot.xpm gpl.txt
-%{_bindir}/rlplot
-%{_bindir}/exprlp
-
-%changelog
-* Sun Feb 25 2007 Reinhard Lackner
-- release 1.3
-
-* Thu Oct 19 2006 Reinhard Lackner
-- release 1.2
-
-* Fri Feb 24 2006 Reinhard Lackner
-- release 1.1
-
-* Wed Sep 07 2005 Reinhard Lackner
-- release 1.0
-
-* Tue Dec 21 2004 Reinhard Lackner
-- release canditate 2, version 0.99.12b
-
-* Sat May 31 2003 Reinhard Lackner
-- modified for RLPlot 0.97b
-
-* Sun Dec 29 2002 Reinhard Lackner
-- modified for RLPlot 0.96b
-
-* Mon Nov 25 2002 Guido Gonzato
-- initial
-
-
-
-
diff --git a/spreadwi.cpp b/spreadwi.cpp
index c2f8921..ac47e08 100755
--- a/spreadwi.cpp
+++ b/spreadwi.cpp
@@ -1,2637 +1,2656 @@
-//spreadwin.cpp, (c)2000-2006 by R. Lackner
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-#include "rlplot.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <math.h>
-
-#include <fcntl.h>				//file open flags
-#include <sys/stat.h>			//I/O flags
-
-#ifdef _WINDOWS
-	#include <io.h>					//for read/write
-#else
-	#define O_BINARY 0x0
-	#include <unistd.h>
-#endif
-
-extern const LineDEF GrayLine;
-extern GraphObj *CurrGO, *TrackGO;			//Selected Graphic Objects
-extern EditText *CurrText;
-extern char *LoadFile;
-extern char TmpTxt[];
-extern Default defs;
-extern UndoObj Undo;
-
-static ReadCache *Cache = 0L;
-static TextDEF ssText;
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Get item from *.csv file
-bool GetItemCSV(char *Text, int cbText)
-{
-	char c;
-	int i;
-
-	for (i = 0,	*Text = 0; i < cbText; ) {
-		c = Cache->Getc();
-		switch(c) {
-		case ',':			//column separator
-			Text[i] = 0;
-			return true;
-		case 0x0a:			//end of line: mark by false return but text o.k.
-			Text[i] = 0;
-			return false;
-		default:
-			if(c > 0x20) Text[i++] = c;	//printable character
-			else if(i >0 && c == 0x20) Text[i++] = c;
-			else if(!c && Cache->IsEOF()) {
-				Text[i] = 0;
-				return false;
-				}
-			else Text[i] = 0;	//ignore non printing characters
-			}
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// process a memory block (i.e. clipboard data) as if file input
-int ProcMemData(GraphObj *g, unsigned char *ptr, bool dispatch)
-{
-	int i, RetVal = FF_UNKNOWN, nt, nl, nc, ns;
-
-	if(ptr) {
-		for(i = nt = nl = nc = ns = 0; ptr[i] && nl<100; i++) {
-			switch(ptr[i]) {
-			case 0x09:				//tab
-				nt++;
-				break;
-			case 0x0a:				//LF
-				nl++;
-				break;
-			case ',':
-				nc++;
-				break;
-			case ' ':
-				ns++;
-				break;
-				}
-			}
-		if(dispatch && i && !nt && !nl) {
-			if(CurrText){
-				g->Command(CMD_SETFOCUS, 0L, 0L);
-				for(i = 0; ptr[i]; i++)	CurrText->AddChar(ptr[i], i? 0L : Undo.cdisp, 0L);
-				g->Command(CMD_REDRAW, 0L, 0L);
-				}
-			}
-		else if(nt) RetVal = FF_TSV;
-		else if(nl && ptr[0] == '<') RetVal = FF_XML;
-		else if(nc == nl && defs.DecPoint[0] == ',') RetVal = FF_TSV;
-		else if(nl && nc && 0 == (nc % nl)) RetVal = FF_CSV;
-		else if(nl && ns && 0 == (ns % nl)) RetVal = FF_SSV;
-		else if(nl) RetVal = FF_TSV;
-		if(dispatch) switch(RetVal) {
-		case FF_CSV:	g->Command(CMD_PASTE_CSV, ptr, 0L);	break;
-		case FF_TSV:	g->Command(CMD_PASTE_TSV, ptr, 0L);	break;
-		case FF_SSV:	g->Command(CMD_PASTE_SSV, ptr, 0L);	break;
-		case FF_XML:	g->Command(CMD_PASTE_XML, ptr, 0L); break;
-			}
-		}
-	return RetVal;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// This graphic object displays a spreadsheet
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class SpreadWin:public GraphObj{
-public:
-	anyOutput *w;
-	POINT ssOrg;
-	RECT currRC;
-
-	SpreadWin(GraphObj *par, DataObj *Data);
-	~SpreadWin();
-	void DoPlot(anyOutput *target);
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-
-	bool ShowGrid(int CellWidth, int CellHeight, int FirstWidth, POINT *cpos);
-	void MarkButtons(char *rng, POINT *cp = 0L);
-	bool PrintData(anyOutput *o);
-	void WriteGraphXML(unsigned char **ptr, long *cbd);
-
-private:
-	bool is_modified, bDoColWidth;
-	char *filename;
-	ssButton **cButtons, **rButtons;
-	ssButton *aButton;
-	POINT cpos;
-	DataObj *d;
-	int NumGraphs, CurrCol;
-	Graph **g;
-	RECT rc_line;
-	POINT line[2];
-};
-
-SpreadWin::SpreadWin(GraphObj *par, DataObj *Data):GraphObj(par, Data)
-{
-	d = Data;	g = 0L;		ssOrg.x =  ssOrg.y = 0;		NumGraphs = 0;
-	filename=0L;	aButton = 0L;
-	w = 0L;		cButtons = rButtons = 0L;
-	if(w = NewDispClass(this)){
-		w->hasHistMenu = true;
-		ssText.RotBL = ssText.RotCHAR = 0.0;
-		ssText.fSize = 0.0f;
-		ssText.iSize = w->un2iy(defs.GetSize(SIZE_CELLTEXT));
-		ssText.Align = TXA_VCENTER | TXA_HLEFT;		ssText.Mode = TXM_TRANSPARENT;
-		ssText.Style = TXS_NORMAL;					ssText.ColBg = 0x00e8e8e8L;
-		ssText.ColTxt = 0x00000000L;				ssText.text = 0L;
-		ssText.Font = FONT_HELVETICA;				w->SetTextSpec(&ssText);
-		w->SetMenu(MENU_SPREAD);					w->FileHistory();
-		w->Erase(0x00e8e8e8L);						w->Caption("RLPlot data");
-		d->ri->SetDefWidth(w->un2ix(defs.GetSize(SIZE_CELLWIDTH)));
-		d->ri->SetHeight(w->un2iy(defs.GetSize(SIZE_CELLTEXT)/defs.ss_txt) + 2);
-		d->ri->SetFirstWidth(32);
-		}
-	else if(d &&  d->ri) {
-		d->ri->SetHeight(19);	d->ri->SetDefWidth(76);	d->ri->SetFirstWidth(32);
-		}
-	Id = GO_SPREADDATA;
-	is_modified = bDoColWidth = false;
-}
-
-SpreadWin::~SpreadWin()
-{
-	int i;
-
-	if(parent) {
-		if(cButtons) {
-			for(i = 0; cButtons[i]; i++) if(cButtons[i]) delete(cButtons[i]);
-			free(cButtons);
-			}
-		if(rButtons) {
-			for(i = 0; rButtons[i]; i++) if(rButtons[i]) delete(rButtons[i]);
-			free(rButtons);
-			}
-		if (aButton) delete(aButton);
-		if (w) delete w;
-		if (g && NumGraphs) {
-			for(i = 0; i < NumGraphs; i++) if(g[i]) delete(g[i]);
-			free (g);
-			}
-		if(filename) free(filename);	filename=0L;
-		}
-}
-
-void
-SpreadWin::DoPlot(anyOutput *o)
-{
-	if(!(o->ActualSize(&currRC)))return;
-	o->StartPage();
-	d->Command(CMD_DOPLOT, (void*)this, o);
-	o->EndPage();
-}
-
-bool
-SpreadWin::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	char *Name;
-	Graph *g2;
-	int i, j, k;
-	MouseEvent *mev;
-	POINT p1, p2;
-
-	if(d) {
-		if(!o) o = w;
-		switch(cmd) {
-		case CMD_CURRPOS:
-			if(tmpl && cButtons && rButtons) {
-				int ac = 1, na =  0;
-				RECT urc;
-				if(((POINT*)tmpl)->x != cpos.x) {
-					for(cpos.x = ((POINT*)tmpl)->x, i = 0; cButtons[i]; i++) {
-						cButtons[i]->Command(CMD_SELECT, (cpos.x == (i+ssOrg.x)) ? &ac : &na, w);
-						}
-					urc.left = cButtons[0]->rDims.left;		urc.bottom = cButtons[0]->rDims.bottom;
-					urc.top = cButtons[0]->rDims.top;		urc.right = urc.left + d->ri->GetWidth(i+ssOrg.x) * (i-1);
-					w->UpdateRect(&urc, false);
-					}
-				if(((POINT*)tmpl)->y != cpos.y) {
-					for(cpos.y = ((POINT*)tmpl)->y, i = 0; rButtons[i]; i++) {
-						rButtons[i]->Command(CMD_SELECT, (cpos.y == (i+ssOrg.y)) ? &ac : &na, w);
-						}
-					urc.left = rButtons[0]->rDims.left;		urc.right = rButtons[0]->rDims.right;
-					urc.top = rButtons[0]->rDims.top;		urc.bottom = urc.top + d->ri->GetHeight(i+ssOrg.y) * (i-1);
-					w->UpdateRect(&urc, false);
-					}
-				}
-			else return false;
-			return true;
-		case CMD_CAN_CLOSE:
-			HideTextCursor();
-			if(is_modified == true) {
-				is_modified=false;
-				if(Undo.isEmpty(0L)) return true;
-				i = YesNoCancelBox("The spreadsheet or a graph has been modified!\n\nDo you want to save it now?");
-				if(i == 2) return false;
-				else if(i == 1) return Command(CMD_SAVEDATAAS, tmpl, o);
-				}
-			return true;
-		case CMD_MRK_DIRTY:
-			return is_modified = true;
-		case CMD_WRITE_GRAPHS:
-			if (g && NumGraphs) WriteGraphXML((unsigned char**)tmpl, (long*)o);
-			return true;
-		case CMD_DROP_GRAPH:
-			if(!tmpl) return false;				if(o) o->FileHistory();
-			if(g && NumGraphs) {
-				if(g = (Graph**)realloc(g, (NumGraphs+2) * sizeof(Graph*)))
-					g[NumGraphs++] = (Graph *)tmpl;
-				else return false;
-				}
-			else {
-				if(g = (Graph **)calloc(2, sizeof(Graph*))){
-					g[0] = (Graph *)tmpl;		NumGraphs = 1;
-					}
-				}
-			for(i = j = 0; i < NumGraphs; i++) {
-				if(g[i]) {
-					g[j] = g[i];			g[j]->parent = this;
-					g[j]->Command(CMD_SET_DATAOBJ, (void*)d, 0L);
-					j++;
-					}
-				}
-			NumGraphs = j;	g[j-1]->DoPlot(0L);
-			return true;
-		case CMD_NEWGRAPH:
-			if((g2 = new Graph(this, d, 0L, 0)) && g2->PropertyDlg() && 
-				Command(CMD_DROP_GRAPH, g2, o))return Command(CMD_REDRAW, 0L, o);
-			else if(g2) DeleteGO(g2);
-			Undo.SetDisp(w);
-			return false;
-		case CMD_NEWPAGE:
-			if((g2 = new Page(this, d)) && 
-				Command(CMD_DROP_GRAPH, g2, o))return Command(CMD_REDRAW, 0L, o);
-			else if(g2) DeleteGO(g2);
-			Undo.SetDisp(w);
-			return false;
-		case CMD_DELGRAPH:
-			if (g && NumGraphs) {
-				for(i = 0; i < NumGraphs; i++) if(g[i]) DeleteGO(g[i]);
-				free (g);
-				}
-			g = 0L;			NumGraphs = 0;		Undo.Flush();
-			return true;
-		case CMD_DELOBJ:
-			i = j = 0;
-			if(g && tmpl) for(; i < NumGraphs; i++) {
-				if(g[i] == (Graph*) tmpl) {
-					DeleteGO(g[i]);
-					}
-				else (g[j++] = g[i]);
-				}
-			if(g && j < i) g[j] = 0L;			NumGraphs = j;
-			return true;
-		case CMD_SAVEDATA:
-			if(o) o->MouseCursor(MC_WAIT, false);
-			if(d->WriteData(0L)) {
-				is_modified=false;
-				if(o) o->MouseCursor(MC_ARROW, false);
-				return true;
-				}
-			if(o) o->MouseCursor(MC_ARROW, true);
-		case CMD_SAVEDATAAS:
-			is_modified=false;
-			if((Name = SaveDataAsName(filename)) && Name[0]){
-				if(o) o->FileHistory();
-				if(Name && d->WriteData(Name)) {
-					if(filename) free(filename);
-					filename = (char*)memdup(Name, (int)strlen(Name)+1, 0);
-					}
-				else return false;
-				}
-			else return false;
-			return true;
-		case CMD_DROPFILE:
-			if(!Command(CMD_CAN_CLOSE, 0L, o)) return false;
-			if(IsRlpFile((char*)tmpl)) return OpenGraph(this, (char*)tmpl, 0L, false);
-			else if(d->ReadData((char*)tmpl, 0L, FF_UNKNOWN)){
-				if(filename) free(filename);
-				filename = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0);
-				return Command(CMD_SETSCROLL, 0L, w);
-				}
-			else ErrorBox("The selected file is not valid\nor not accessible!\n");
-			return false;
-		case CMD_OPEN:
-			if(!Command(CMD_CAN_CLOSE, 0L, o)) return false;
-			Undo.KillDisp(o);		Undo.SetDisp(o);
-			if((Name = OpenDataName(filename)) && Name[0]){
-				if(o) o->FileHistory();
-				if(IsRlpFile(Name)) return OpenGraph(this, Name, 0L, false);
-				else if(d->ReadData(Name, 0L, FF_UNKNOWN)){
-					if(filename) free(filename);
-					filename = (char*)memdup(Name, (int)strlen(Name)+1, 0);
-					return Command(CMD_SETSCROLL, 0L, w);
-					}
-				}
-			return false;
-		case CMD_ADDROWCOL:
-			if(DoSpShSize(d, this)) DoPlot(o);
-			return true;
-		case CMD_COL_MOUSE:
-			if(o && cButtons && rButtons && (mev = (MouseEvent*)tmpl) && mev->y > o->MenuHeight) {
-				for(i = 0; cButtons[i]; i++){
-					if(bDoColWidth) {
-						o->MouseCursor(MC_COLWIDTH, false);
-						}
-					else if(mev->x > cButtons[i]->rDims.left && mev->x < cButtons[i]->rDims.right+2){
-						if(mev->x > cButtons[i]->rDims.right-4) {
-							switch(mev->Action) {
-							case MOUSE_LBDOWN:
-								CurrCol = i;					line[0].x = line[1].x = mev->x;
-								line[0].y = o->MenuHeight;		line[1].y = currRC.bottom;
-								d->Command(CMD_TOOLMODE, tmpl, o);
-								o->MouseCursor(MC_COLWIDTH, false);
-								return bDoColWidth = true;
-								}
-							o->MouseCursor(MC_COLWIDTH, false);
-							}
-						else o->MouseCursor(MC_ARROW, false);
-						return false;
-						}
-					else o->MouseCursor(MC_ARROW, false);
-					if(mev->Action == MOUSE_MOVE && bDoColWidth && (mev->StateFlags & 0x01)) {
-						rc_line.left = line[0].x - 2;		rc_line.right = line[1].x + 2;
-						rc_line.top = line[0].y - 2;		rc_line.bottom = line[1].y +2;
-						o->UpdateRect(&rc_line, false);
-						if(mev->x < (cButtons[CurrCol]->rDims.left +20))mev->x = cButtons[CurrCol]->rDims.left +20;
-						k = mev->x - cButtons[CurrCol]->rDims.right;
-						for(j = CurrCol; cButtons[j] && cButtons[j]->rDims.left < (currRC.right-k); j++) {
-							cButtons[j]->rDims.right += k;	cButtons[j]->rDims.left += j > CurrCol ? k : 0;
-							cButtons[j]->DoPlot(o);			o->UpdateRect(&cButtons[j]->rDims, false);
-							cButtons[j]->rDims.right -= k;	cButtons[j]->rDims.left -= j > CurrCol ? k : 0;
-							}
-						line[0].x = line[1].x = mev->x;
-						line[0].y = o->MenuHeight;			line[1].y = currRC.bottom;
-						o->ShowLine(line, 2, 0x00a0a0a0);
-						return true;
-						}
-					if(mev->Action == MOUSE_LBUP && bDoColWidth) {
-						bDoColWidth = false;
-						k = line[0].x - cButtons[CurrCol]->rDims.left;
-						if(abs(k - cButtons[CurrCol]->rDims.right + cButtons[CurrCol]->rDims.left)>3) {
-							rlp_strcpy(TmpTxt, 40, mkRangeRef(0, CurrCol + ssOrg.x, 0, CurrCol + ssOrg.x));
-							d->ri = new RangeInfo(1, TmpTxt, d->ri);
-							d->ri->SetWidth(k >=20 ? k : 20);
-							}
-						d->Command(CMD_REDRAW, 0L, o);
-						return true;
-						}
-					else bDoColWidth = false;
-					}
-				}
-			else o->MouseCursor(MC_ARROW, false);
-			return false;
-		case CMD_MOUSE_EVENT:
-			if(!(mev =(MouseEvent*)tmpl)) return false;
-			if(bDoColWidth)return Command(CMD_COL_MOUSE, tmpl, o);
-			p1.x = mev->x;		p1.y = mev->y;
-			if(mev->y < o->MenuHeight) o->MouseCursor(MC_ARROW, false);
-			else if(o && cButtons && rButtons) {
-				if(mev->x < d->ri->GetFirstWidth() && mev->y < (o->MenuHeight + d->ri->GetHeight(-1)) && aButton) {
-					aButton->Command(cmd, tmpl, o);
-					}
-				else if(mev->x < d->ri->GetFirstWidth() && rButtons) {
-					if(!(d->mpos2dpos(&p1, &p2, false)))return false;
-					p2.x -= ssOrg.x;		p2.y -= ssOrg.y;
-					if (p2.y < 0 || p2.y >= (d->r_disp-1)) return false;
-					if(rButtons[p2.y]) rButtons[p2.y]->Command(cmd, tmpl, o);
-					}
-				else if(mev->y < (o->MenuHeight + d->ri->GetHeight(-1)) && cButtons) {
-					if(!(d->mpos2dpos(&p1, &p2, false)))return false;
-					p2.x -= ssOrg.x;		p2.y -= ssOrg.y;
-					if (p2.x < 0 || p2.x >= (d->c_disp-1)) return false;
-					if(cButtons[p2.x]) cButtons[p2.x]->Command(cmd, tmpl, o);
-					}
-				else if(o->MrkMode == MRK_SSB_DRAW) o->HideMark();
-				}
-			return	d->Command(cmd, tmpl, o);
-		case CMD_PASTE_TSV:		case CMD_PASTE_CSV:		case CMD_PASTE_SSV:
-			Undo.DataObject(this, w, d, 0L, 0L);
-		case CMD_COPY_SYLK:		case CMD_ADDCHAR:		case CMD_SHIFTUP:
-		case CMD_COPY_TSV:		case CMD_COPY_XML:		case CMD_QUERY_COPY:
-		case CMD_TAB:			case CMD_SHTAB:			case CMD_SHIFTDOWN:
-		case CMD_CURRLEFT:		case CMD_CURRIGHT:		case CMD_CURRUP:
-		case CMD_CURRDOWN:		case CMD_SHIFTRIGHT:	case CMD_POS_FIRST:
-		case CMD_POS_LAST:		case CMD_SHIFTLEFT:		case CMD_DELETE:
-		case CMD_TOOLMODE:		case CMD_FILLRANGE:		case CMD_CUT:
-		case CMD_PASTE_XML:		case CMD_DELROW:		case CMD_INSROW:
-		case CMD_INSCOL:		case CMD_DELCOL:		case CMD_UNDO:
-		case CMD_SHPGUP:		case CMD_SHPGDOWN:		case CMD_HIDEMARK:
-			bDoColWidth = false;
-			return d->Command(cmd, tmpl, o);
-		case CMD_REDRAW:
-			Undo.SetDisp(w);						d->Command(cmd, tmpl, o);
-			return true;
-		case CMD_MOUSECURSOR:
-			if(o)o->MouseCursor(MC_ARROW, false);	
-			return true;
-		case CMD_SETSCROLL:
-			HideTextCursor();						if(!(o->ActualSize(&currRC)))return false;
-			k = (currRC.bottom-currRC.top)/d->ri->GetHeight(-1);
-			d->GetSize(&i, &j);			o->MouseCursor(MC_WAIT, true);
-			o->SetScroll(true, 0, j, k, ssOrg.y);	k = (currRC.right-currRC.left)/d->ri->GetWidth(-1);
-			o->SetScroll(false, 0, i, k, ssOrg.x);	DoPlot(o);
-			o->MouseCursor(MC_ARROW, true);
-			return true;
-		case CMD_PAGEUP:		case CMD_PAGEDOWN:
-			k = (currRC.bottom-currRC.top)/d->ri->GetHeight(-1);
-			k = k > 3 ? k-2 : 1;					p1.x = d->ri->GetFirstWidth() + 2;
-			p1.y = d->ri->GetHeight(-1) + 2;
-			if(CurrText){
-				p1.x = CurrText->GetX()+2;	p1.y = CurrText->GetY()+12;
-				}
-			d->GetSize(&i, &j);
-			if(cmd == CMD_PAGEUP) ssOrg.y = ssOrg.y > k ? ssOrg.y-k : 0;
-			else ssOrg.y = ssOrg.y < j-k*2 ? ssOrg.y+k : j > k ? j-k : 0;
-			Command(CMD_SETSCROLL, tmpl, o);
-			CurrText = 0L;		d->Select(&p1);
-			return true;
-		case CMD_SETHPOS:
-			ssOrg.x = *((int*)tmpl) >= 0 ? *((long*)tmpl) : 0L;
-			return Command(CMD_SETSCROLL, tmpl, o);
-		case CMD_SETVPOS:
-			ssOrg.y = *((int*)tmpl) >= 0 ? *((long*)tmpl) : 0L;
-			return Command(CMD_SETSCROLL, tmpl, o);
-		case CMD_SETFOCUS:
-			Undo.SetDisp(w);
-			return true;
-		case CMD_KILLFOCUS:
-			return true;
-		case CMD_TEXTSIZE:
-			if(tmpl && *((int*)tmpl) != ssText.iSize) {
-				Undo.ValInt(this, &ssText.iSize, UNDO_CONTINUE);
-				ssText.iSize = o->un2iy(defs.GetSize(SIZE_CELLTEXT));
-				}
-			return true;
-		case CMD_CONFIG:
-			if(defs.PropertyDlg()) return Command(CMD_REDRAW, 0L, o);
-			return false;
-		case CMD_NONE:
-			return true;
-		case CMD_PRINT:
-			return PrintData(o);
-			}
-		}
-	return false;
-}
-
-bool
-SpreadWin::ShowGrid(int CellWidth, int CellHeight, int FirstWidth, POINT *cp)
-{
-	int i, c, nr, nc, cx, cw, ac = 1, na = 0;
-	RECT rc;
-	char text[20];
-	TextDEF ButtText;
-	bool redim = bDoColWidth;
-
-	w->HideMark();
-	cpos.x = cp->x;			cpos.y = cp->y;
-	if(d->ri->GetHeight(-1) != CellHeight || d->ri->GetWidth(-1) != CellWidth || d->ri->GetFirstWidth() != FirstWidth) redim = true;
-	if(redim){
-		d->ri->SetHeight(CellHeight);		d->ri->SetDefWidth(CellWidth);		d->ri->SetFirstWidth(FirstWidth);
-		if(cButtons) {
-			for(i = 0; cButtons[i]; i++) if(cButtons[i]) delete(cButtons[i]);
-			free(cButtons);
-			}
-		if(rButtons) {
-			for(i = 0; rButtons[i]; i++) if(rButtons[i]) delete(rButtons[i]);
-			free(rButtons);
-			}
-		if(aButton) delete(aButton);		aButton = 0L;
-		cButtons = rButtons = 0L;
-		}
-	if(!aButton) aButton = new ssButton(this, 0, w->MenuHeight, FirstWidth, CellHeight);
-	memcpy(&ButtText, &ssText, sizeof(TextDEF));
-	ButtText.Align = TXA_HCENTER | TXA_VCENTER;
-	w->GetSize(&rc);
-	if(!cButtons) {
-		c = d->c_disp;		if(c < 40 || c > 200) d->c_disp = c = 40;
-		cButtons = (ssButton **)calloc(c, sizeof(ssButton*));
-		for(i = 0, cx = FirstWidth; i < (c-1); i++) {
-			cw = d->ri->GetWidth(i+ssOrg.x);
-			cButtons[i] = new ssButton(this, cx, w->MenuHeight, cw+1, CellHeight);
-			cx += cw;
-			}
-		}
-	else {
-		for(i = 0, cx = FirstWidth; cButtons[i]; i++) {
-			cw = d->ri->GetWidth(i+ssOrg.x);
-			cButtons[i]->rDims.left = cx;		cButtons[i]->rDims.right = cx + cw + 1;
-			cx += cw;
-			}
-		}
-	if(!rButtons) {
- 		c = d->r_disp;			if(c < 60 || c > 300) d->c_disp = c = 60;
-		rButtons = (ssButton**)calloc(c, sizeof(ssButton*));
-		for(i = 0; i < (c-1); i++) rButtons[i] = 
-			new ssButton(this, 0, i*CellHeight+CellHeight+w->MenuHeight, 
-			FirstWidth, CellHeight);
-		}
-	d->GetSize(&nc, &nr);
-	if(rButtons) for(i = 0; rButtons[i]; i++) {
-#ifdef USE_WIN_SECURE
-		sprintf_s(text, 20, "%d", i+1+ssOrg.y);
-#else
-		sprintf(text, "%d", i+1+ssOrg.y);
-#endif
-		if(rButtons[i]) {
-			rButtons[i]->Command(CMD_SETTEXTDEF, &ButtText, w);
-			rButtons[i]->Command(CMD_SETTEXT, text, w);
-			rButtons[i]->Command(CMD_SELECT, (cpos.y == (i+ssOrg.y)) ? &ac : &na, w);
-			}
-		}
-	if(cButtons) for(i = 0; cButtons[i]; i++) {
-		if(cButtons[i]) {
-			cButtons[i]->Command(CMD_SETTEXTDEF, &ButtText, w);
-			cButtons[i]->Command(CMD_SETTEXT, Int2ColLabel(i+ssOrg.x, true), w);
-			cButtons[i]->Command(CMD_SELECT, (cpos.x == (i+ssOrg.x)) ? &ac : &na, w);
-			}
-		}
-	w->SetTextSpec(&ssText);
-	if(aButton) aButton->DoPlot(w);
-	return true;
-}
-
-void
-SpreadWin::MarkButtons(char *rng, POINT *cp)
-{
-	int i, r, c, ncb, nrb, *r_idx, *c_idx;
-	AccRange *mr;
-
-	if(!rButtons || !cButtons) return;
-	for(ncb = 0; cButtons[ncb]; ncb++);		ncb--;
-	for(nrb = 0; rButtons[nrb]; nrb++);		nrb--;
-	if(!nrb || !ncb || !(r_idx=(int*)calloc(nrb,sizeof(int))) || !(c_idx=(int*)calloc(ncb,sizeof(int)))) return;
-	if(rng && rng[0] && (mr = new AccRange(rng)) && mr->GetFirst(&c, &r)) {
-		mr->NextCol(&c);
-		do {
-			if((i = c - ssOrg.x) >= 0 && i < ncb) c_idx[i] = 1; 
-			}while(mr->NextCol(&c));
-		mr->GetFirst(&c, &r);			mr->NextRow(&r);
-		do {
-			if((i = r - ssOrg.y) >= 0 && i < nrb) r_idx[i] = 1; 
-			}while(mr->NextRow(&r));
-		delete mr;
-		}
-	for(i = 0; i < ncb; i++) cButtons[i]->Command(CMD_SETSTYLE, &c_idx[i], w); 
-	for(i = 0; i < nrb; i++) rButtons[i]->Command(CMD_SETSTYLE, &r_idx[i], w);
-	if(cp) {
-		c_idx[0] = r_idx[0] = 0;	c_idx[1] = r_idx[1] = 1;
-		r = (cp->y - ssOrg.y);		c = cp->x -ssOrg.x;
-		for(i = 0; i < ncb; i++) cButtons[i]->Command(CMD_SELECT, c == i ? &c_idx[1] : &c_idx[0], w); 
-		for(i = 0; i < nrb; i++) rButtons[i]->Command(CMD_SELECT, r == i ? &r_idx[1] : &r_idx[0], w);
-		}
-	free(r_idx);		free(c_idx);
-}
-
-bool
-SpreadWin::PrintData(anyOutput *o)
-{
-	int i, j, k, l, pgw, pfw, pcw, pch, rpp, cpp, nc, nr, ix, iy, cpages;
-	int row, col, width, height, ipad, mode;
-	double scale;
-	RECT rc, margin, margin_first;
-	POINT pp_pos, ss_pos, grid[3];
-	LineDEF Line1, Line2;
-	TextDEF td, tdp;
-	bool bContinue;
-	time_t ti = time(0L);
-	anyResult res;
-
-	Line1.patlength = Line2.patlength = 1.0;
-	Line1.color = Line2.color = 0x00808080;		//gray gridlines
-	Line1.pattern = Line2.pattern = 0x0;		//solid lines
-	switch(defs.cUnits) {
-	case 1:		Line1.width = 0.01;			break;
-	case 2:		Line1.width = 0.003937;		break;
-	default:	Line1.width = 0.1;			break;
-		}
-#ifdef _WINDOWS
-	scale = 1.0/_SQRT2;
-#else
-	scale = 0.9;
-#endif
-	Line2.width = Line1.width * 3.0;
-	d->GetSize(&nc, &nr);
-	if(!(o->StartPage())) return false;
-	pfw = iround(o->hres * ((double)d->ri->GetFirstWidth())/w->hres * scale);
-	pcw = iround(o->hres * ((double)d->ri->GetWidth(-1))/w->hres * scale);
-	pch = iround((o->vres * ((double)d->ri->GetHeight(-1))/w->vres) * scale + o->vres/20.0);
-	o->ActualSize(&rc);
-	tdp.ColTxt = 0x0;	tdp.ColBg = 0x00ffffffL; 
-	tdp.fSize = tdp.RotBL = tdp.RotCHAR = 0.0;	tdp.Align = TXA_HRIGHT | TXA_VCENTER;
-	tdp.Mode = TXM_TRANSPARENT;
-	tdp.Style = TXS_NORMAL;				tdp.Font = FONT_HELVETICA;	tdp.text = 0L;
-	memcpy(&td, &ssText, sizeof(TextDEF));	
-	td.Align = TXA_HCENTER | TXA_VCENTER;
-	td.Style = TXS_NORMAL;	td.iSize = 0;
-#ifdef _WINDOWS
-	tdp.iSize = iround(o->hres/6.0);
-	td.fSize = defs.GetSize(SIZE_CELLTEXT);
-#else
-	tdp.iSize = iround(o->hres/7.5);
-	td.fSize = defs.GetSize(SIZE_CELLTEXT)*.8;
-#endif
-	margin.left = iround(o->hres);	margin.right = iround(o->hres/2.0);
-	margin.top = margin.bottom = iround(o->hres);
-	memcpy(&margin_first, &margin, sizeof(RECT));
-	pp_pos.x = margin.left;		pp_pos.y = margin.top;
-	ss_pos.x = 0;				ss_pos.y = 0;		cpages = 1;
-	rpp = (rc.bottom - margin.top - margin.bottom - pch)/pch;
-	mode = 0;
-	if((nr * pch) < ((rc.bottom - rc.top - margin.top - margin.bottom)>>1)) mode = 1;
-	do {
-		k = (rc.right - margin.left - margin.right - pfw);
-		for(i = pgw = 0; pgw < k && (i+ss_pos.x) < nc; i++ ){
-			pgw += (pcw = iround(o->hres * ((double)d->ri->GetWidth(i+ss_pos.x))/w->hres * scale));
-			if(i >=(nc-1)) break;
-			}
-		if(pgw < k) {
-			cpp = i+1;
-			if(!mode && !ss_pos.x && pgw < (k>>1)) mode = 2;
-			}
-		else {
-			cpp = i-1;		pgw -= pcw;
-			}
-		mode = mode;
-		pp_pos.x = margin.left;		pp_pos.y = margin.top;
-		k = (ss_pos.x + cpp) > nc ? nc-ss_pos.x : cpp;
-		l = (ss_pos.y + rpp) > nr ? nr-ss_pos.y : rpp;
-		grid[0].y = margin.top +pch; grid[1].y = grid[0].y + l * pch;
-		o->SetLine(&Line2);
-		grid[0].x = grid[1].x = pp_pos.x;		o->oSolidLine(grid);
-		grid[0].x = grid[1].x = pp_pos.x+pfw;	o->oSolidLine(grid);
-		grid[0].x = margin.left+pfw;	grid[1].x = grid[0].x + pgw;
-		o->SetLine(&Line2);
-		grid[0].y = grid[1].y = pp_pos.y;		o->oSolidLine(grid);
-		grid[0].y = grid[1].y = pp_pos.y+pch;	o->oSolidLine(grid);
-		o->SetLine(&Line2);
-		td.Align = TXA_HCENTER | TXA_VCENTER;	o->SetTextSpec(&td);
-		grid[0].y = margin.top;	 grid[1].y = grid[0].y + pch;
-		iy = margin.top + (pch >>1);	grid[0].x = grid[1].x = pp_pos.x + pfw;
-		for(i = 0, ix = pp_pos.x + pfw; i <= k; i++) {			//column headers
-			pcw = iround(o->hres * ((double)d->ri->GetWidth(i+ss_pos.x))/w->hres * scale);
-			o->oSolidLine(grid);		grid[0].x = grid[1].x = ix + pcw;
-			if(i < k) o->oTextOut(ix + (pcw >>1), iy, Int2ColLabel(i+ss_pos.x, true), 0);
-			ix += pcw;
-			}
-		td.Align = TXA_HRIGHT | TXA_VCENTER;	
-		o->SetTextSpec(&td);	ix = margin.left + pfw - iround(o->hres/20.0);
-		grid[0].x = margin.left;		grid[1].x = grid[0].x + pfw;
-		for(i = 0; i <= l; i++) {			//row labels
-			grid[0].y = grid[1].y = pp_pos.y + pch + i * pch;
-			o->oSolidLine(grid);		iy = grid[0].y + (pch >>1);
-#ifdef USE_WIN_SECURE
-			sprintf_s(TmpTxt, TMP_TXT_SIZE, "%d", i+1+ss_pos.y);
-#else
-			sprintf(TmpTxt, "%d", i+1+ss_pos.y);
-#endif
-			if(i < l) o->oTextOut(ix, iy, TmpTxt, 0);
-			}
-		ipad = iround(o->hres/30.0);
-		grid[0].x = margin.left+pfw + ipad;
-		for(i = 0; i < k; i++, grid[0].x += pcw) {			//spreadsheet data
-			pcw = iround(o->hres * ((double)d->ri->GetWidth(i+ss_pos.x))/w->hres * scale);
-			for (j = 0; j < l; j++) {
-				row = j+ss_pos.y;		col = i+ss_pos.x;
-				if(row >= 0 && row < d->cRows && col >= 0  && col < d->cCols && d->etRows[row][col]) {
-					d->etRows[row][col]->GetResult(&res, false);
-					TranslateResult(&res);
-					td.Align = TXA_HLEFT | TXA_VCENTER;
-					ix = grid[0].x;
-					switch (res.type) {
-					case ET_VALUE:
-						ix = ix + pcw - ( ipad<<1 );
-						td.Align = TXA_HRIGHT | TXA_VCENTER;
-						rlp_strcpy(TmpTxt, TMP_TXT_SIZE, res.text);
-						fit_num_rect(o, pcw - ipad, TmpTxt);
-						Int2Nat(TmpTxt);			break;
-					case ET_BOOL:	case ET_DATE:	case ET_TIME:	case ET_DATETIME:
-						ix = ix + pcw - ( ipad<<1 );
-						td.Align = TXA_HRIGHT | TXA_VCENTER;
-					case ET_TEXT:	case ET_UNKNOWN:
-						if(res.text&& strlen(res.text) < 40) 
-							rlp_strcpy(TmpTxt, TMP_TXT_SIZE, res.text[0] != '\'' ? res.text : res.text +1);
-						else if(res.text) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "#SIZE");
-						else TmpTxt[0] = 0;	
-						do {
-							o->oGetTextExtent(TmpTxt, (int)strlen(TmpTxt), &width, &height);
-							if(width > (pcw + iround(o->hres/20.0))) TmpTxt[strlen(TmpTxt)-1] = 0;
-							}while(width > (pcw + ipad));
-						break;
-					case ET_ERROR:
-						rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "#ERROR");	break;
-					default:
-						rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "#VALUE");	break;
-						}
-					iy = pp_pos.y + pch + (pch>>1) + j * pch;
-					o->SetTextSpec(&td);			o->oTextOut(ix, iy, TmpTxt, 0);
-					grid[0].y = grid[1].y = iy+(pch>>1);	grid[2].y = grid[1].y - pch;
-					grid[0].x -= ipad;		grid[1].x = grid[2].x = grid[0].x + pcw;
-					o->SetLine(&Line1);		o->oPolyline(grid, 3, 0L);
-					grid[0].x += ipad;
-					}
-				}
-			}
-		//prepare for next table
-		ss_pos.x += k;			bContinue = false;
-		if(ss_pos.x >= nc) {ss_pos.x = 0; ss_pos.y += l; }
-		if(ss_pos.y < nr) {
-			ix = pfw + pgw + iround(o->hres/3.5);
-			iy = (l+2)*pch;
-			if(mode == 2 && (margin.left + ix + pfw + pgw) < (rc.right-margin.right)) {
-				margin.left += pfw + k*pcw + iround(o->hres/3.5);
-				bContinue = true;
-				}
-			else if(mode == 1 && (margin.top + iy + pch + l*pch) < (rc.bottom - margin.bottom)) {
-				margin.top += iy;	margin.left = margin_first.left;
-				bContinue = true;
-				}
-			else {
-				tdp.Align = TXA_HRIGHT | TXA_VCENTER; o->SetTextSpec(&tdp);
-#ifdef USE_WIN_SECURE
-				sprintf_s(TmpTxt, TMP_TXT_SIZE, "page %d", cpages++);
-#else
-				sprintf(TmpTxt, "page %d", cpages++);
-#endif
-				o->oTextOut(rc.right-margin.right, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
-				tdp.Align = TXA_HCENTER | TXA_VCENTER; o->SetTextSpec(&tdp);
-#ifdef USE_WIN_SECURE
-				ctime_s(TmpTxt, 50, &ti);	TmpTxt[24] = 0;
-#else
-				sprintf(TmpTxt, "%s", ctime(&ti));	TmpTxt[24] = 0;
-#endif
-				o->oTextOut((rc.right-rc.left)>>1, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
-				tdp.Align = TXA_HLEFT | TXA_VCENTER; o->SetTextSpec(&tdp);
-				i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "RLPlot ");
-				rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, SZ_VERSION);
-				o->oTextOut(margin_first.left, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
-				memcpy(&margin, &margin_first, sizeof(RECT));
-				bContinue = true;
-				o->Eject();
-				}
-			}
-		}while(bContinue);
-	tdp.Align = TXA_HRIGHT | TXA_VCENTER; o->SetTextSpec(&tdp);
-#ifdef USE_WIN_SECURE
-	sprintf_s(TmpTxt, TMP_TXT_SIZE, "page %d", cpages++);
-#else
-	sprintf(TmpTxt, "page %d", cpages++);
-#endif
-	o->oTextOut(rc.right-margin.right, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
-	tdp.Align = TXA_HCENTER | TXA_VCENTER; o->SetTextSpec(&tdp);
-#ifdef USE_WIN_SECURE
-	ctime_s(TmpTxt, 50, &ti);	TmpTxt[24] = 0;
-#else
-	sprintf(TmpTxt, "%s", ctime(&ti));	TmpTxt[24] = 0;
-#endif
-	o->oTextOut((rc.right-rc.left)>>1, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
-	tdp.Align = TXA_HLEFT | TXA_VCENTER; o->SetTextSpec(&tdp);
-	i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "RLPlot ");
-	rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, SZ_VERSION);
-	o->oTextOut(margin_first.left, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
-	o->EndPage();
-	return true;
-}
-
-void 
-SpreadWin::WriteGraphXML(unsigned char **ptr, long *cbd)
-{
-	unsigned char *pg;
-	long cb = 0, size = 0, newsize;
-	int i;
-
-	for(i = 0; i < NumGraphs; i++) if(g[i]) {
-		if((pg = (unsigned char*)GraphToMem(g[i], &cb)) && cb) {
-			newsize = *cbd + cb + 100;
-			*ptr = (unsigned char*)realloc(*ptr, newsize);
-			*cbd += rlp_strcpy((char*)(*ptr)+*cbd, 20, "<Graph><![CDATA[\n");
-			memcpy(*ptr+ *cbd, pg, cb);		*cbd += cb;
-			*cbd += rlp_strcpy((char*)(*ptr)+*cbd, 20, "]]>\n</Graph>\n");
-			if(pg) free(pg);		pg = 0L;			cb = 0;
-			}
-		}
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// This data object is a spreadsheet
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-class SpreadData:public DataObj{
-typedef struct _pos_info {
-	POINT ssOrg, currpos;
-	void *CurrText;};
-
-public:
-	SpreadData(GraphObj *par);
-	~SpreadData();
-	bool Init(int nRows, int nCols);
-	bool mpos2dpos(POINT *mp, POINT *dp, bool can_scroll);
-	bool Select(POINT *p);
-	void MarkRange(char *range, POINT *cp = 0L);
-	void HideMark(bool cclp);
-	bool WriteData(char *FileName);
-	bool AddCols(int nCols);
-	bool AddRows(int nRows);
-	bool ChangeSize(int nCols, int nRows, bool bUndo);
-	bool DeleteCols();
-	bool InsertCols();
-	bool DeleteRows();
-	bool InsertRows();
-	void DoPlot(anyOutput *o);
-	bool DelRange();
-	bool PasteRange(int cmd, char *txt);
-	bool InitCopy(int cmd, void *tmpl, anyOutput *o);
-	bool SavePos();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
-	bool ReadData(char *FileName, unsigned char *buffer, int type);
-
-	bool ReadXML(char *file, unsigned char *buffer, int type, DWORD undo_flags = 0L);
-	bool ReadTSV(char *file, unsigned char *buffer, int type);
-	bool MemList(unsigned char **ptr, int type);
-
-private:
-	int CellHeight, CellWidth, FirstWidth, mrk_offs;
-	RECT cp_src_rec;			//bounding rectangle for copy range
-	bool bActive, new_mark, bCopyCut, bUpdate, isRowMark, isColMark;
-	POINT currpos, currpos2;
-	anyOutput *w;
-	SpreadWin *Disp;
-	GraphObj *g_parent;
-	EditText *et_racc;
-	char *m_range, *c_range;	//mark and copy ranges
-	char *err_msg, *last_err;	//error message
-	char *rlw_file;				//use this name for save
-	_pos_info pos_info;			//save position settings
-};
-
-SpreadData::SpreadData(GraphObj *par)
-{
-	Disp = 0L;	m_range = 0L;	c_range = 0L;	w = 0L;		err_msg=last_err=rlw_file =  0L;
-	g_parent = par;		CellWidth = 76;		CellHeight = 19;	FirstWidth = 32;
-	et_racc = 0L;	currpos.x = currpos.y = mrk_offs = 0;
-	r_disp = 60;	c_disp = 40;
-	bActive = bCopyCut = bUpdate = isRowMark = isColMark = false;
-	cp_src_rec.left = cp_src_rec.right = cp_src_rec.top = cp_src_rec.bottom = 0;
-	if(defs.IniFile && FileExist(defs.IniFile)) {
-		OpenGraph(0L, defs.IniFile, 0L, false);
-		}
-	pos_info.currpos.x = currpos.x;		pos_info.currpos.y = currpos.y;
-	pos_info.CurrText = CurrText;
-}
-
-SpreadData::~SpreadData()
-{
-	FlushData();
-	if(Disp) delete Disp;			Disp = 0L;
-	if(m_range) free(m_range);		m_range = 0L;
-	if(c_range) free(c_range);		c_range = 0L;
-	if(rlw_file) free(rlw_file);	rlw_file = 0L;
-}
-
-bool
-SpreadData::Init(int nRows, int nCols)
-{
-	int i, j;
-	RECT rc;
-	RangeInfo *o_ri;
-
-	cRows = nRows;	cCols = nCols;				currpos.x = currpos.y = 0;
-	new_mark = bCopyCut = false;				if(w && m_range) HideMark(true);
-	while(ri->Type()) {
-		o_ri = ri;		ri = ri->Next();		delete(o_ri);
-		}
-	if(!Disp) {
-		Disp = new SpreadWin(g_parent, this);
-		w = Disp->w;
-		if(w) {
-			CellWidth = w->un2ix(defs.GetSize(SIZE_CELLWIDTH));
-#ifdef _WINDOWS
-			CellHeight = w->un2iy(defs.GetSize(SIZE_CELLTEXT)/defs.ss_txt) + 2;
-#else
-			CellHeight = w->un2iy(defs.GetSize(SIZE_CELLTEXT)/(defs.ss_txt *0.7)) + 2;
-#endif
-			if(CellHeight < 12)CellHeight = 19;		if(CellWidth < 40)CellWidth = 76;
-			FirstWidth = 32;						w->GetSize(&rc);
-			r_disp = (rc.bottom-rc.top)/CellHeight;
-			c_disp = (rc.right-rc.left)/CellWidth+1;
-			}
-		else return false;
-		pos_info.ssOrg.x = Disp->ssOrg.x;		pos_info.ssOrg.y = Disp->ssOrg.y;
-		pos_info.CurrText = CurrText;
-		}
-	if(etRows)FlushData();
-	etRows = (EditText ***)calloc (cRows, sizeof(EditText **));
-	if(etRows) for(i = 0; i < cRows; i++) {
-		etRows[i] = (EditText **)calloc(cCols, sizeof(EditText *));
-		if(etRows[i]) for(j = 0; j < cCols; j++) {
-#ifdef _DEBUG
-			char text[20];
-#ifdef USE_WIN_SECURE
-			sprintf_s (text, 20, "%.2f", i*10.0 + j);
-#else
-			sprintf (text, "%.2f", i*10.0 + j);
-#endif
-			etRows[i][j] = new EditText(this, text, i, j);
-#else
-			etRows[i][j] = new EditText(this, 0L, i, j);
-#endif
-			}
-		}
-	if (LoadFile) {
-		rlp_strcpy(TmpTxt, TMP_TXT_SIZE, LoadFile);	//we will reenter by recursion !
-		free(LoadFile);
-		LoadFile = 0L;
-		Disp->Command(CMD_DROPFILE, TmpTxt, w);
-		}
-	else DoPlot(w);
-	return true;
-}
-
-bool
-SpreadData::mpos2dpos(POINT *mp, POINT *dp, bool can_scroll)
-{
-	int mx;
-
-	CurrGO = 0L;
-	dp->y = (mp->y - w->MenuHeight - CellHeight)/CellHeight + Disp->ssOrg.y;
-	dp->x = Disp->ssOrg.x;
-	for(mx = mp->x - ri->GetFirstWidth() - ri->GetWidth(dp->x); mx > 0; dp->x++, mx -= ri->GetWidth(dp->x));
-	if(can_scroll) {
-		if(dp->x < cCols && mp->x < (ri->GetFirstWidth()+10) && mp->x > ri->GetFirstWidth() && Disp->ssOrg.x >0) {
-			Disp->ssOrg.x -= 1;
-			if(CurrText) CurrText->Update(2, w, mp);		CurrText = 0L;
-			Disp->Command(CMD_SETSCROLL, 0L, w);
-			}
-		if(mp->y < (w->MenuHeight + CellHeight+9) && mp->y > (w->MenuHeight + CellHeight) && Disp->ssOrg.y >0) {
-			Disp->ssOrg.y -= 1;
-			if(CurrText) CurrText->Update(2, w, mp);		CurrText = 0L;
-			Disp->Command(CMD_SETSCROLL, 0L, w);
-			}
-		if(mp->x > (Disp->currRC.right-w->MenuHeight-10) && Disp->ssOrg.x < (cCols-2)) {
-			Disp->ssOrg.x += 1;
-			if(CurrText) CurrText->Update(2, w, mp);		CurrText = 0L;
-			Disp->Command(CMD_SETSCROLL, 0L, w);
-			}
-		if(mp->y > (Disp->currRC.bottom-10) && Disp->ssOrg.y < (cRows-5)) {
-			Disp->ssOrg.y += 1;
-			do {
-				mp->y -= CellHeight;
-			} while(mp->y > (Disp->currRC.bottom-CellHeight));
-			if(CurrText) CurrText->Update(2, w, mp);		CurrText = 0L;
-			Disp->Command(CMD_SETSCROLL, 0L, w);			CurrText = 0L;
-			}
-		}
-	if(dp->y >= cRows) dp->y = cRows-1;					if(dp->x >= cCols) dp->x = cCols-1;
-	return true;
-}
-
-bool
-SpreadData::Select(POINT *p)
-{
-	if(CurrText && CurrText->isInRect(p)){
-		CurrText->Update(1, w, p);
-		Disp->Command(CMD_CURRPOS, &currpos, w);
-		if(CurrText->isFormula() && CurrText->hasMark()) et_racc = CurrText;
-		return true;
-		}
-	if(!(mpos2dpos(p, &currpos, false)))return false;
-	if(currpos.y < cRows && currpos.x < cCols && currpos.y >= 0 && currpos.x >= 0) {
-		if(etRows[currpos.y][currpos.x]) {
-			if(etRows[currpos.y][currpos.x] == CurrText) CurrText->Update(1, w, p);
-			else {
-				if(CurrText) CurrText->Update(2, w, p);
-				CurrText = etRows[currpos.y][currpos.x];
-				DoPlot(w);
-				CurrText->Update(1, w, p);
-				}
-			Disp->Command(CMD_CURRPOS, &currpos, w);
-			return true;
-			}
-		}
-	if(CurrText) CurrText->Update(2, w, p);		CurrText = 0L;
-	return false;
-}
-
-void
-SpreadData::MarkRange(char *range, POINT *cp)
-{
-	AccRange *nr, *oldr;
-	int r, c, cb;
-
-	oldr = nr = 0L;
-	if(m_range && range && !strcmp(m_range, range)) return;		//no change
-	if(m_range) oldr = new AccRange(m_range);
-	if(range) nr = new AccRange(range);
-	if(oldr && nr && oldr->GetFirst(&c, &r)) {
-		for( ; oldr->GetNext(&c, &r); ) {
-			if(r >= Disp->ssOrg.y && r < (r_disp +Disp->ssOrg.y) && r < cRows && c >= Disp->ssOrg.x 
-				&& c < (c_disp + Disp->ssOrg.x) && c < cCols && !nr->IsInRange(c, r) && etRows[r] && etRows[r][c]){
-				etRows[r][c]->Mark(w, etRows[r][c] != CurrText ? 0 : 1);
-				}
-			}
-		}
-	if(nr && nr->GetFirst(&c, &r)) {
-		for( ; nr->GetNext(&c, &r); ) {
-			if(r >= Disp->ssOrg.y && r < (r_disp +Disp->ssOrg.y) && c >= Disp->ssOrg.x 
-				&& c < (c_disp + Disp->ssOrg.x) && r < cRows && c < cCols)
-				etRows[r][c]->Mark(w, etRows[r][c] != CurrText ? 2 : 3);
-			}
-		}
-	if(range && (m_range = (char*)realloc(m_range, cb = ((int)strlen(range)+6)))) rlp_strcpy(m_range, cb, range);
-	else if (m_range) m_range[0] = 0;
-	if(oldr) delete(oldr);	if(nr) delete(nr);
-	new_mark = true;		Disp->MarkButtons(m_range, cp);
-}
-
-void
-SpreadData::HideMark(bool cclp)
-{
-	if(cclp && c_range && c_range != m_range){
-		free(c_range);	c_range = 0L;
-		}
-	if(m_range){
-		free(m_range);	m_range = 0L;
-		DoPlot(w);
-		}
-	if(cclp) EmptyClip();		new_mark = false;
-	Disp->MarkButtons(m_range, 0L);
-}
-
-bool
-SpreadData::WriteData(char *FileName)
-{
-	FILE *File;
-	int i, j;
-	unsigned char *buff = 0L;
-	anyResult res;
-	bool bErr = false;
-
-	if(!cRows || !cCols || !etRows) return false;
-	if(!FileName) FileName = rlw_file;
-	if(FileName && FileName[0]) BackupFile(FileName);
-	else return false;
-#ifdef USE_WIN_SECURE
-	if(fopen_s(&File, FileName, "w")) {
-#else
-	if(!(File = fopen(FileName, "w"))) {
-#endif
-		ErrorBox("An error occured during write,\n\nplease try again!");
-		return false;
-		}
-	HideMark(true);
-	i = (int)strlen(FileName);
-	//test for xml extension
-	if(!strcmp(".xml", FileName+i-4) || !strcmp(".XML", FileName+i-4)) {
-		MemList(&buff, FF_XML);
-		if(buff){
-			if(fprintf(File, "%s", buff) <= 0) bErr = true;
-			free(buff);					fclose(File);
-			return true;
-			}
-		return false;
-		}
-	//test for tsv extension
-	if(!strcmp(".tsv", FileName+i-4) || !strcmp(".TSV", FileName+i-4)) {
-		MemList(&buff, FF_TSV);
-		if(buff){
-			if(fprintf(File, "%s", buff) <= 0) bErr = true;
-			free(buff);					fclose(File);
-			return true;
-			}
-		return false;
-		}
-	//test for rlw extension
-	if(!strcmp(".rlw", FileName+i-4) || !strcmp(".RLW", FileName+i-4)) {
-		MemList(&buff, FF_RLW);
-		if(buff){
-			if(rlw_file != FileName) {
-				if(rlw_file) free(rlw_file);
-				rlw_file = (char*)memdup(FileName, (int)strlen(FileName)+1, 0);
-				}
-			if(fprintf(File, "%s", buff) <= 0) bErr = true;
-			free(buff);					fclose(File);
-			return true;
-			}
-		return false;
-		}
-	//test for slk extension
-	if(!strcmp(".slk", FileName+i-4) || !strcmp(".SLK", FileName+i-4)) {
-		MemList(&buff, FF_SYLK);
-		if(buff){
-			if(fprintf(File, "%s", buff) <= 0) bErr = true;
-			free(buff);					fclose(File);
-			return true;
-			}
-		return false;
-		}
-	//else write csv
-	for(i = 0; i < cRows; i++) {
-		for(j = 0; j < cCols; j++) {
-			if(etRows[i][j]) {
-				etRows[i][j]->GetResult(&res, false);		TranslateResult(&res);
-				switch(res.type) {
-				case ET_TEXT:
-					fprintf(File, "\"%s\"", res.text);
-					break;
-				case ET_VALUE:		case ET_DATE:	case ET_TIME:
-				case ET_DATETIME:	case ET_BOOL:
-					fprintf(File, "%s", res.text);
-					break;
-					}
-				}
-			if(j < (cCols-1)) fprintf(File, ", ");
-			}
-		if(fprintf(File, "\n") <= 0) bErr = true;
-		}
-	fclose(File);
-	if(bErr) ErrorBox("An error occured during write,\n\nplease try again!");
-	return true;
-}
-
-bool
-SpreadData::ReadData(char *FileName, unsigned char *buffer, int type)
-{
-	int i, j;
-	char ItemText[20];
-	bool success;
-
-	if(FileName) {		//read disk file
-		if(!Disp->Command(CMD_CAN_CLOSE, 0L, 0L))return false;
-		if(0 == strcmp(".xml", FileName+strlen(FileName)-4) ||
-			0 == strcmp(".XML", FileName+strlen(FileName)-4) ||
-			0 == strcmp(".rlw", FileName+strlen(FileName)-4) ||
-			0 == strcmp(".RLW", FileName+strlen(FileName)-4) ||
-			IsXmlFile(FileName)){
-			Disp->Command(CMD_DELGRAPH, 0L, w);
-			if(ReadXML(FileName, buffer, type, 0L)){
-				if(0 == strcmp(".rlw", FileName+strlen(FileName)-4) ||
-					0 == strcmp(".RLW", FileName+strlen(FileName)-4)) {
-					if(rlw_file) free(rlw_file);
-					rlw_file = (char*)memdup(FileName, (int)strlen(FileName)+1, 0);
-					}
-				return true;
-				}
-			return false;
-			}
-		if(0 == strcmp(".tsv", FileName+strlen(FileName)-4) ||
-			0 == strcmp(".TSV", FileName+strlen(FileName)-4)){
-			return ReadTSV(FileName, buffer, type);
-			}
-		if(!(Cache = new ReadCache())) return false;
-		if(! Cache->Open(FileName)) {
-			delete Cache;
-			i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Error open data file\n\"");
-			i += rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, FileName);
-			i += rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, "\"\n");
-			ErrorBox(TmpTxt);
-			return false;
-			}
-		if(!Init(1, 1)) goto ReadError;
-		}
-	else if(buffer) {	//read memory buffer
-		switch(type) {
-		case FF_TSV:
-			return ReadTSV(FileName, buffer, type);
-		case FF_XML:
-			return ReadXML(FileName, buffer, type, 0L);
-		case FF_CSV:
-			if(!(Cache = new MemCache(buffer))) return false;
-			break;
-		default:
-			ErrorBox("Read from buffer with\nunknown format failed.");
-			return false;
-			}
-		}
-	else return false;
-	i = j = 0;
-	do {
-		if((success = GetItemCSV(ItemText, sizeof(ItemText)-1)) || ItemText[0]){
-			if(j >= cCols && !AddCols(j+1)) goto ReadError;
-			if(i >= cRows && !AddRows(i+1)) goto ReadError;
-			if(!etRows[i][j]) goto ReadError;
-			if(ItemText[0] == '"') {
-				rmquot(ItemText);
-				etRows[i][j]->SetText(ItemText);
-				etRows[i][j]->Update(20, 0L, 0L);
-				}
-			else if(ItemText[0]){
-				etRows[i][j]->SetText(ItemText);
-				etRows[i][j]->Update(10, 0L, 0L);
-				}
-			else etRows[i][j]->SetText("");
-			j++;
-			}
-		if(!success && !Cache->IsEOF()) {i++; j = 0;}	//eol
-		}while (ItemText[0] || !Cache->IsEOF());		//eof
-	Cache->Close();		delete Cache;		Cache = 0L;
-	Disp->ssOrg.x = Disp->ssOrg.y = 0;
-	return true;
-
-ReadError:
-	Cache->Close();		delete Cache;		Cache = 0L;
-	return false;
-
-}
-
-bool
-SpreadData::AddCols(int nCols)
-{
-	EditText **NewRow;
-	int i, j;
-
-	if (nCols <= cCols || !etRows || !etRows[0] || !etRows[0][cCols-1]) return false;
-	for(i = 0; i < cRows; i++) {
-		if(NewRow = (EditText **)realloc(etRows[i], nCols * sizeof(EditText*))){
-			for(j = cCols; j < nCols; j++) {
-				NewRow[j] = new EditText(this, 0L, i, j);
-				}
-			etRows[i] = NewRow;
-			}
-		else return false;						//memory allocation error
-		}
-	cCols = nCols;
-	return true;
-}
-
-bool
-SpreadData::AddRows(int nRows)
-{
-	int i, j;
-	EditText ***NewRows;
-
-	if (nRows <= cRows || !etRows || !etRows[cRows-1] || !etRows[cRows-1][0] ) return false;
-	NewRows = (EditText ***)realloc(etRows, nRows * sizeof(EditText **));
-	if(NewRows) etRows = NewRows;
-	else return false;					//memory allocation error
-	for(i = cRows; i < nRows; i++){
-		etRows[i] = (EditText **)calloc(cCols, sizeof(EditText *));
-		if(etRows[i]) {
-			for(j = 0; j < cCols; j++) etRows[i][j] = new EditText(this, 0L, i, j);
-			}
-		else {							//memory allocation error
-			cRows = i-1;
-			return false;
-			}
-		}
-	cRows = nRows;
-	return true;
-}
-
-bool
-SpreadData::ChangeSize(int nCols, int nRows, bool bUndo)
-{
-	int i, j;
-	bool RetVal = true;
-	
-	if(nCols == cCols && nRows == cRows) return true;
-	if(c_range){
-		free(c_range);	c_range = 0L;	EmptyClip();
-		}
-	if(bUndo) Undo.DataObject(Disp, w, this, 0L, 0L);
-	if(nRows && nRows < cRows) {
-		for (i = nRows; i < cRows; i++) {
-			if(etRows[i]) for (j = 0; j < cCols; j++) {
-				if(etRows[i][j]) delete etRows[i][j];
-				etRows[i][j] = NULL;
-				}
-			free(etRows[i]);
-			etRows[i] = NULL;
-			}
-		cRows = nRows;
-		}
-	if(nCols && nCols < cCols) {
-		for (i = 0; i < cRows; i++) {
-			for (j = nCols; j < cCols; j++) {
-				if(etRows[i][j]) delete etRows[i][j];
-				etRows[i][j] = NULL;
-				}
-			}
-		cCols = nCols;
-		}
-	if(nCols > cCols) if(!AddCols(nCols))RetVal = false;
-	if(RetVal && nRows > cRows) if(!AddRows(nRows))RetVal = false;
-	if(w) {
-#ifdef USE_WIN_SECURE
-		sprintf_s(TmpTxt, TMP_TXT_SIZE, "%d00", nRows);
-#else
-		sprintf(TmpTxt, "%d00", nRows);
-#endif
-		w->oGetTextExtent(TmpTxt, 0, &i, &j);
-		if(i > FirstWidth) FirstWidth = i;
-		Disp->Command(CMD_SETSCROLL, 0L, w);
-		}
-	return RetVal;
-}
-
-bool
-SpreadData::DeleteCols()
-{
-	int i, j, r0, c0, c1, c2, cn, dc, nc;
-	RECT rc;
-	lfPOINT *cs;
-
-	AccRange * ar;
-	if(!isColMark || !m_range || !m_range[0]){
-		InfoBox("No columns selected!");
-		return false;
-		}
-	if(c_range){
-		free(c_range);	c_range = 0L;	EmptyClip();
-		}
-	Undo.DataObject(Disp, w, this, 0L, 0L);
-	Undo.String(0L, &m_range, UNDO_CONTINUE);
-	if(!(ar = new AccRange(m_range))) return false;
-	ar->BoundRec(&rc);
-	if(!(cs = (lfPOINT*)malloc((rc.right-rc.left+2)*sizeof(lfPOINT)))) {
-		delete ar;			return false;
-		}
-	if(!(ar->GetFirst(&c0, &r0))) {
-		free(cs);			delete ar;			return false;
-		}
-	ar->NextCol(&c1);		cn = c1;			nc = 0;
-	do {
-		for(c0 = c1 = c2 = cn; ar->NextCol(&c2); ) {
-			if(c2 > c1+1 || c2 < c0) break;
-			c1 = c2;
-			}
-		dc = c1-c0+1;		cn = c2;
-		cs[nc].fx = c0;		cs[nc].fy = c1;		nc++;
-		}while(c2!=c1);
-	SortFpArray(nc, cs);	nc--;
-	do {
-		c0 = (int)cs[nc].fx;	c1 = (int)cs[nc].fy;	dc = c1-c0+1;
-		for(i = 0; i < cRows; i++) {
-			for(j = c0+dc; j <cCols; j++) {
-				if(etRows && etRows[i] && etRows[i][j] && etRows[i][j-dc]) {
-					switch(etRows[i][j]->type) {
-					default:
-						etRows[i][j-dc]->SetText(etRows[i][j]->text);
-						break;
-					case ET_VALUE:
-						etRows[i][j-dc]->SetText(etRows[i][j]->text);
-						etRows[i][j-dc]->Value = etRows[i][j]->Value; 
-						etRows[i][j-dc]->type = ET_VALUE;
-						break;
-					case ET_FORMULA:
-						MoveFormula(this, etRows[i][j]->text, TmpTxt, TMP_TXT_SIZE, -dc, 0, -1, c0);
-						etRows[i][j-dc]->SetText(TmpTxt);	etRows[i][j]->type = ET_FORMULA;
-						break;
-						}
-					}
-				}
-			}
-		for(i = 0; i < cRows; i++) {
-			if(etRows[i]){
-				for(j = cCols-dc; j < cCols; j++) if(etRows[i][j]) {
-					delete(etRows[i][j]);
-					etRows[i][j] = 0L;
-					}
-				}
-			}
-		cCols -= dc;
-		MoveFormula(this, m_range, TmpTxt, TMP_TXT_SIZE, -dc, 0, -1, c0+1);		free(m_range);
-		m_range = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);	nc--;
-		for(i = 0; i < c0; i++) for(j = 0; j < c0; j++) {
-			if(etRows && etRows[i] && etRows[i][j] && etRows[i][j]->type == ET_FORMULA) {
-				MoveFormula(this, etRows[i][j]->text, TmpTxt, TMP_TXT_SIZE, -dc, 0, -1, c0);
-				etRows[i][j]->SetText(TmpTxt);			etRows[i][j]->type = ET_FORMULA;
-				}
-			}
-		}while(nc >= 0);
-	delete ar;
-	Disp->Command(CMD_SETSCROLL, 0L, w);		Disp->Command(CMD_MRK_DIRTY, 0L, w);
-	return true;
-}
-
-bool
-SpreadData::InsertCols()
-{
-	int i, j, r0, c0, c1, c2, cn, dc, nc;
-	RECT rc;
-	lfPOINT *cs;
-
-	AccRange * ar;
-	if(!isColMark || !m_range || !m_range[0]){
-		InfoBox("No columns selected!");
-		return false;
-		}
-	if(c_range){
-		free(c_range);	c_range = 0L;	EmptyClip();
-		}
-	Undo.DataObject(Disp, w, this, 0L, 0L);
-	Undo.String(0L, &m_range, UNDO_CONTINUE);
-	if(!(ar = new AccRange(m_range))) return false;
-	ar->BoundRec(&rc);
-	if(!(cs = (lfPOINT*)malloc((rc.right-rc.left+2)*sizeof(lfPOINT)))) {
-		delete ar;			return false;
-		}
-	if(!(ar->GetFirst(&c0, &r0))) {
-		free(cs);			delete ar;			return false;
-		}
-	ar->NextCol(&c1);		cn = c1;			nc = 0;
-	do {
-		for(c0 = c1 = c2 = cn; ar->NextCol(&c2); ) {
-			if(c2 > c1+1 || c2 < c0) break;
-			c1 = c2;
-			}
-		dc = c1-c0+1;		cn = c2;
-		cs[nc].fx = c0;		cs[nc].fy = c1;		nc++;
-		}while(c2!=c1);
-	SortFpArray(nc, cs);	nc--;
-	do {
-		c0 = (int)cs[nc].fx;	c1 = (int)cs[nc].fy;	dc = c1-c0+1;
-		if(AddCols(cCols + dc)) for(i = 0; i < cRows; i++) {
-			for(j = cCols-1; j >= c0+dc; j--) {
-				if(etRows && etRows[i] && etRows[i][j] && etRows[i][j-dc]) {
-					switch(etRows[i][j-dc]->type) {
-					default:
-						etRows[i][j]->SetText(etRows[i][j-dc]->text);
-						break;
-					case ET_VALUE:
-						etRows[i][j]->SetText(etRows[i][j-dc]->text);
-						etRows[i][j]->Value = etRows[i][j-dc]->Value; 
-						etRows[i][j]->type = ET_VALUE;
-						break;
-					case ET_FORMULA:
-						MoveFormula(this, etRows[i][j-dc]->text, TmpTxt, TMP_TXT_SIZE, dc, 0, -1, c0);
-						etRows[i][j]->SetText(TmpTxt);	etRows[i][j]->type = ET_FORMULA;
-						break;
-						}
-					etRows[i][j-dc]->SetText("");
-					}
-				}
-			}
-		MoveFormula(this, m_range, TmpTxt, TMP_TXT_SIZE, dc, 0, -1, c0);		free(m_range);
-		m_range = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);	nc--;
-		for(i = 0; i < c0; i++) for(j = 0; j < c0; j++) {
-			if(etRows && etRows[i] && etRows[i][j] && etRows[i][j]->type == ET_FORMULA) {
-				MoveFormula(this, etRows[i][j]->text, TmpTxt, TMP_TXT_SIZE, dc, 0, -1, c0);
-				etRows[i][j]->SetText(TmpTxt);			etRows[i][j]->type = ET_FORMULA;
-				}
-			}
-		}while(nc >= 0);
-	delete ar;
-	Disp->Command(CMD_SETSCROLL, 0L, w);		Disp->Command(CMD_MRK_DIRTY, 0L, w);
-	return true;
-}
-
-bool 
-SpreadData::DeleteRows()
-{
-	int i, j, r0, c0, r1, r2, rn, dr, nr;
-	AccRange * ar;
-	RECT rc;
-	lfPOINT *rs;
-
-	if(!isRowMark || !m_range || !m_range[0]){
-		InfoBox("No rows selected!");
-		return false;
-		}
-	if(c_range){
-		free(c_range);	c_range = 0L;	EmptyClip();
-		}
-	Undo.DataObject(Disp, w, this, 0L, 0L);
-	Undo.String(0L, &m_range, UNDO_CONTINUE);
-	if(!(ar = new AccRange(m_range))) return false;
-	ar->BoundRec(&rc);
-	if(!(rs = (lfPOINT*)malloc((rc.bottom-rc.top+2)*sizeof(lfPOINT)))) {
-		delete ar;			return false;
-		}
-	if(!(ar->GetFirst(&c0, &r0))) {
-		free(rs);			delete ar;			return false;
-		}
-	ar->NextRow(&r1);		rn = r1;			nr = 0;
-	do {
-		for(r0 = r1 = r2 = rn;ar->NextRow(&r2); ) {
-			if(r2 > r1+1 || r2 < r0) break;
-			r1 = r2;
-			}
-		dr = r1-r0+1;		rn = r2;
-		rs[nr].fx = r0;		rs[nr].fy = r1;		nr++;
-		}while(r2!=r1);
-	SortFpArray(nr, rs);	nr--;
-	do {
-		r0 = (int)rs[nr].fx;	r1 = (int)rs[nr].fy;	dr = r1-r0+1;
-		for(i = r0+dr; i < cRows; i++) {
-			for(j = 0; j < cCols; j++) {
-				if(etRows && etRows[i-dr] && etRows[i-dr][j] && etRows[i] && etRows[i][j]) {
-					switch(etRows[i][j]->type) {
-					default:
-						etRows[i-dr][j]->SetText(etRows[i][j]->text);
-						break;
-					case ET_VALUE:
-						etRows[i-dr][j]->SetText(etRows[i][j]->text);
-						etRows[i-dr][j]->Value = etRows[i][j]->Value; 
-						etRows[i-dr][j]->type = ET_VALUE;
-						break;
-					case ET_FORMULA:
-						MoveFormula(this, etRows[i][j]->text, TmpTxt, TMP_TXT_SIZE, 0, -dr, r0, -1);
-						etRows[i-dr][j]->SetText(TmpTxt);	etRows[i-dr][j]->type = ET_FORMULA;
-						break;
-						}
-					}
-				}
-			}
-		for(i = cRows-1; i >= cRows-dr; i--) {
-			if(etRows[i]){
-				for(j = 0; j < cCols; j++) if(etRows[i][j]) delete(etRows[i][j]);
-				free(etRows[i]);
-				}
-			}
-		cRows -= dr;
-		MoveFormula(this, m_range, TmpTxt, TMP_TXT_SIZE, 0, -dr, r0+1, -1);		free(m_range);
-		m_range = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);	nr--;
-		for(i = 0; i < r0; i++) for(j = 0; j < cCols; j++) {
-			if(etRows && etRows[i] && etRows[i][j] && etRows[i][j]->type == ET_FORMULA) {
-				MoveFormula(this, etRows[i][j]->text, TmpTxt, TMP_TXT_SIZE, 0, -dr, r0, -1);
-				etRows[i][j]->SetText(TmpTxt);			etRows[i][j]->type = ET_FORMULA;
-				}
-			}
-		}while(nr >= 0);
-	delete ar;
-	if(w) {
-		Disp->Command(CMD_SETSCROLL, 0L, w);			Disp->Command(CMD_MRK_DIRTY, 0L, w);
-		}
-	return true;
-}
-
-bool
-SpreadData::InsertRows()
-{
-	int i, j, r0, c0, r1, r2, rn, dr, nr;
-	AccRange * ar;
-	RECT rc;
-	lfPOINT *rs;
-
-	if(!isRowMark || !m_range || !m_range[0]){
-		InfoBox("No rows selected!");
-		return false;
-		}
-	if(c_range){
-		free(c_range);	c_range = 0L;	EmptyClip();
-		}
-	Undo.DataObject(Disp, w, this, 0L, 0L);
-	Undo.String(0L, &m_range, UNDO_CONTINUE);
-	if(!(ar = new AccRange(m_range))) return false;
-	ar->BoundRec(&rc);
-	if(!(rs = (lfPOINT*)malloc((rc.bottom-rc.top+2)*sizeof(lfPOINT)))) {
-		delete ar;			return false;
-		}
-	if(!(ar->GetFirst(&c0, &r0))) {
-		free(rs);			delete ar;			return false;
-		}
-	ar->NextRow(&r1);		rn = r1;			nr = 0;
-	do {
-		for(r0 = r1 = r2 = rn;ar->NextRow(&r2); ) {
-			if(r2 > r1+1 || r2 < r0) break;
-			r1 = r2;
-			}
-		dr = r1-r0+1;		rn = r2;
-		rs[nr].fx = r0;		rs[nr].fy = r1;		nr++;
-		}while(r2!=r1);
-	SortFpArray(nr, rs);	nr--;
-	do {
-		r0 = (int)rs[nr].fx;	r1 = (int)rs[nr].fy;	dr = r1-r0+1;
-		if(AddRows(cRows + dr)) for(i = cRows-1; i >= r0+dr; i--) {
-			for(j = 0; j < cCols; j++) {
-				if(etRows && etRows[i-dr] && etRows[i-dr][j] && etRows[i] && etRows[i][j]) {
-					switch(etRows[i-dr][j]->type) {
-					default:
-						etRows[i][j]->SetText(etRows[i-dr][j]->text);
-						break;
-					case ET_VALUE:
-						etRows[i][j]->SetText(etRows[i-dr][j]->text);
-						etRows[i][j]->Value = etRows[i-dr][j]->Value; 
-						etRows[i][j]->type = ET_VALUE;
-						break;
-					case ET_FORMULA:
-						MoveFormula(this, etRows[i-dr][j]->text, TmpTxt, TMP_TXT_SIZE, 0, dr, r0, -1);
-						etRows[i][j]->SetText(TmpTxt);	etRows[i][j]->type = ET_FORMULA;
-						break;
-						}
-					etRows[i-dr][j]->SetText("");
-					}
-				}
-			}
-		MoveFormula(this, m_range, TmpTxt, TMP_TXT_SIZE, 0, dr, r0, -1);		free(m_range);
-		m_range = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);	nr--;
-		for(i = 0; i < r0; i++) for(j = 0; j < cCols; j++) {
-			if(etRows && etRows[i] && etRows[i][j] && etRows[i][j]->type == ET_FORMULA) {
-				MoveFormula(this, etRows[i][j]->text, TmpTxt, TMP_TXT_SIZE, 0, dr, r0, -1);
-				etRows[i][j]->SetText(TmpTxt);			etRows[i][j]->type = ET_FORMULA;
-				}
-			}
-		}while(nr >= 0);
-	delete ar;
-	if(w) {
-#ifdef USE_WIN_SECURE
-		sprintf_s(TmpTxt, TMP_TXT_SIZE, "%d00", cRows);
-#else
-		sprintf(TmpTxt, "%d00", cRows);
-#endif
-		w->oGetTextExtent(TmpTxt, 0, &i, &j);
-		if(i > FirstWidth) FirstWidth = i;
-		Disp->Command(CMD_SETSCROLL, 0L, w);			Disp->Command(CMD_MRK_DIRTY, 0L, w);
-		}
-	return true;
-}
-
-void
-SpreadData::DoPlot(anyOutput *o)
-{
-	RECT rc;
-	int i, j, r, c;
-	AccRange *ar;
-
-	if(!w || !Disp) return;
-	w->Erase(0x00e8e8e8L);					if(!(w->ActualSize(&rc)))return;
-	w->SetTextSpec(&ssText);				et_racc = 0L;
-	r_disp = (rc.bottom-rc.top)/CellHeight;
-	for(c = Disp->ssOrg.x, j=rc.left, c_disp = 1; c < cCols && j <= (rc.right-rc.left) && c_disp < cCols; c++, c_disp++) {
-		j += ri->GetWidth(c);
-		}
-	if(j >= (rc.right-rc.left))c_disp--;
-	Disp->ShowGrid(CellWidth, CellHeight, FirstWidth, &currpos);
-	rc.top = w->MenuHeight;					rc.bottom = rc.top + CellHeight;
-	LockData(false, false);
-	for(i = 0; i <= (cRows - Disp->ssOrg.y) && i <= r_disp; i++) {
-		rc.left = ri->GetFirstWidth();		rc.right = rc.left + ri->GetWidth(Disp->ssOrg.x);
-		rc.top += CellHeight;				rc.bottom += CellHeight;
-		for(j = 0; j <= (cCols - Disp->ssOrg.x) && j <= c_disp; j++) {
-			r = i + Disp->ssOrg.y;	c = j + Disp->ssOrg.x; 
-			if(r < cRows && r >= 0 && c < cCols && c >=0 && etRows[r][c]){
-				etRows[r][c]->SetRec(&rc);	etRows[r][c]->Update(2, 0L, 0L);
-				etRows[r][c]->Redraw(w, false);
-				}
-			rc.left += ri->GetWidth(Disp->ssOrg.x+j);
-			rc.right += ri->GetWidth(Disp->ssOrg.x+j+1);
-			}
-		}
-	if(bUpdate) {
-		for(i = 0; i <= (cRows - Disp->ssOrg.y) && i <= r_disp; i++) {
-			for(j = 0; j <= (cCols - Disp->ssOrg.x) && j <= c_disp; j++) {
-				r = i + Disp->ssOrg.y;	c = j + Disp->ssOrg.x; 
-				if(r < cRows && r >= 0 && c < cCols && c >=0 && etRows[r][c]){
-					etRows[r][c]->Redraw(w, false);
-					}
-				}
-			}
-		}
-	if(CurrText && CurrText->row >= Disp->ssOrg.y && CurrText->row < (Disp->ssOrg.y + r_disp)
-			&& CurrText->col >= Disp->ssOrg.x && CurrText->col < (Disp->ssOrg.x +c_disp))
-			CurrText->Update(1, w, 0L);
-	bUpdate = false;
-	if(m_range && (ar = new AccRange(m_range)) && ar->GetFirst(&c, &r)) {
-		for( ; ar->GetNext(&c, &r); ) {
-			if(r >= Disp->ssOrg.y && r < (r_disp + Disp->ssOrg.y) && c >= Disp->ssOrg.x 
-				&& r < cRows && c < cCols
-				&& c < (c_disp + Disp->ssOrg.x) && etRows[r] && etRows[r][c])
-				etRows[r][c]->Mark(w, etRows[r][c] != CurrText ? 2 : 3);
-			}
-		delete (ar);
-		}
-	LockData(false, false);
-	if(c_range) InitCopy(0, 0L, w);			//move animated rectangle
-	if(!(w->ActualSize(&rc))) return;		
-	rc.bottom += CellHeight;		w->UpdateRect(&rc, false);
-	if(err_msg && (!last_err || strcmp(err_msg,last_err))) {
-		ErrorBox(last_err = err_msg);
-		}
-}
-
-bool
-SpreadData::DelRange()
-{
-	AccRange *ar;
-	int r, c;
-	RECT rec;
-
-	if(m_range && (ar = new AccRange(m_range)) && ar->GetFirst(&c, &r)) {
-		ar->BoundRec(&rec);
-		Undo.DataObject(Disp, w, this, &rec, 0L);
-		while(ar->GetNext(&c, &r)) {
-			if(r >= 0 && r < cRows && c >= 0 && c < cCols){
-				if(etRows[r][c] && etRows[r][c]->text) etRows[r][c]->SetText("");
-				}
-			}
-		delete (ar);	HideTextCursor();
-		}
-	HideMark(false);
-	if(CurrText) CurrText->Update(1, w, 0L);
-	bCopyCut = false;
-	return true;
-}
-
-bool
-SpreadData::PasteRange(int cmd, char *txt)
-{
-	AccRange *cr;
-	int i, r, c;
-	RECT mrk_range;
-
-	if(new_mark && m_range && (cr = new AccRange(m_range)) && cr->GetFirst(&c, &r)) {
-		cr->BoundRec(&mrk_range);
-		for(i = 0 ; cr->GetNext(&c, &r); i++) if(c >= 0 && c < cCols && r >= 0 && r < cRows){
-			currpos.x = c;	currpos.y = r;
-			switch(cmd){
-			case CMD_PASTE_TSV: ReadTSV(0L, (unsigned char*)txt, FF_TSV);		break;
-			case CMD_PASTE_SSV: ReadTSV(0L, (unsigned char*)txt, FF_SSV);		break;
-			case CMD_PASTE_XML: ReadXML(0L, (unsigned char*)txt, FF_XML, i ? UNDO_CONTINUE : 0L); break;
-			case CMD_PASTE_CSV: ReadData(0L, (unsigned char*)txt, FF_CSV);		break;
-				}
-			if((mrk_range.right - mrk_range.left) == (cp_src_rec.right - cp_src_rec.left) &&
-				(mrk_range.bottom - mrk_range.top) == (cp_src_rec.bottom - cp_src_rec.top)) break;
-			if((mrk_range.right - mrk_range.left) == 1 && (cp_src_rec.right - cp_src_rec.left) > 1) break;
-			if((mrk_range.bottom - mrk_range.top) == 1 && (cp_src_rec.bottom - cp_src_rec.top) > 1) break;
-			}
-		delete cr;		return true;
-		}
-	else switch(cmd){
-		case CMD_PASTE_TSV:
-			return ReadTSV(0L, (unsigned char*)txt, FF_TSV);
-		case CMD_PASTE_SSV:
-			return ReadTSV(0L, (unsigned char*)txt, FF_SSV);
-		case CMD_PASTE_XML:
-			return ReadXML(0L, (unsigned char*)txt, FF_XML, 0L);
-		case CMD_PASTE_CSV:
-			return ReadData(0L, (unsigned char*)txt, FF_CSV);
-		}
-	return bCopyCut = false;
-}
-
-bool
-SpreadData::InitCopy(int cmd, void *tmpl, anyOutput *o)
-{
-	int r, c;
-	AccRange *ar;
-	RECT rc_band, rcCopy;
-	bool bRet = false;
-
-	rcCopy.left = rcCopy.top = 0;
-	rcCopy.bottom = cRows-1;		rcCopy.right = cCols-1;
-	if(cmd) {
-		bCopyCut = (cmd == CMD_CUT);
-		new_mark = false;
-		if(m_range && m_range[0]) {
-			if(c_range) free(c_range);
-			c_range = (char*)memdup(m_range, (int)strlen(m_range)+1, 0);
-			if(c_range && (ar = new AccRange(c_range))) {
-				ar->BoundRec(&rcCopy);
-				delete ar;			bRet = true;;
-				}
-			}
-		else if (CurrText) {
-			if(CurrText->hasMark()) return CurrText->Command(CMD_COPY, o, 0L);
-			else {
-				if(m_range = (char*)realloc(m_range, 40*sizeof(char)))
-					rlp_strcpy(m_range, 40, mkRangeRef(currpos.y, currpos.x, currpos.y, currpos.x));
-				DoPlot(o);
-				return InitCopy(cmd, tmpl, o);
-				}
-			}
-		}
-	if(bRet || !cmd) {		//calculate animated mark
-		if(c_range && (ar = new AccRange(c_range))) {
-			ar->BoundRec(&rcCopy);			delete ar;
-			}
-		if(rcCopy.right < Disp->ssOrg.x || rcCopy.bottom < Disp->ssOrg.y) {
-			HideCopyMark();			return bRet;
-			}
-		c = rcCopy.left >= Disp->ssOrg.x ? rcCopy.left : Disp->ssOrg.x;
-		r = rcCopy.top >= Disp->ssOrg.y ? rcCopy.top : Disp->ssOrg.y;
-		while(!etRows[r] && r) r--;			while(!etRows[r][c] && c) c--;
-		if(etRows[r][c]){
-			rc_band.left = etRows[r][c]->GetX()-1;	rc_band.top = etRows[r][c]->GetY()-1;
-			}
-		else return bRet;
-		c = rcCopy.right < (Disp->ssOrg.x + c_disp) ? rcCopy.right : Disp->ssOrg.x + c_disp;
-		r = rcCopy.bottom < (Disp->ssOrg.y + r_disp)? rcCopy.bottom : Disp->ssOrg.y + r_disp;
-		if(r >= cRows) r = cRows-1;			if(c >= cCols) c = cRows -1;
-		if(etRows[r][c]){
-			rc_band.right = etRows[r][c]->GetX()+ri->GetWidth(c);
-			rc_band.bottom = etRows[r][c]->GetY()+CellHeight;
-			if(rc_band.left >= rc_band.right) rc_band.right = rc_band.left + CellWidth;
-			if(rc_band.top >= rc_band.bottom) rc_band.bottom = rc_band.top + CellHeight;
-			ShowCopyMark(o, &rc_band, 1);
-			}
-		}
-	return bRet;
-}
-
-bool
-SpreadData::SavePos()
-{
-	bool bRet = false;
-
-	if(pos_info.currpos.x != currpos.x || pos_info.currpos.y != pos_info.currpos.y
-		|| CurrText != pos_info.CurrText) {
-		Undo.Point(Disp, &currpos, w, UNDO_CONTINUE);
-		Undo.VoidPtr(Disp, (void**)&CurrText, 0L, 0L, UNDO_CONTINUE);
-		}
-	if(pos_info.ssOrg.x != Disp->ssOrg.x || pos_info.ssOrg.y != Disp->ssOrg.y) {
-		Swap(pos_info.ssOrg.x, Disp->ssOrg.x );		Swap(pos_info.ssOrg.y, Disp->ssOrg.y );
-		Undo.Point(Disp, &Disp->ssOrg, w, UNDO_CONTINUE);
-		Swap(pos_info.ssOrg.x, Disp->ssOrg.x );		Swap(pos_info.ssOrg.y, Disp->ssOrg.y );
-		}
-	pos_info.currpos.x = currpos.x;		pos_info.currpos.y = currpos.y;
-	pos_info.ssOrg.x = Disp->ssOrg.x;		pos_info.ssOrg.y = Disp->ssOrg.y;
-	pos_info.CurrText = CurrText;
-	return bRet;
-}
-
-bool
-SpreadData::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-	static int move_cr = CMD_CURRDOWN;
-	MouseEvent *mev;
-	POINT p, cp;
-	RECT rc;
-	
-	if(!o) o = w;
-	switch(cmd) {
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		p.x = mev->x;				p.y = mev->y;
-		if((mev->StateFlags & 1) || mev->Action == MOUSE_LBUP || p.y < (w->MenuHeight+CellHeight)) {
-			if(!(mpos2dpos(&p, &cp, (mev->StateFlags & 1) == 1))) return false;
-			if(cp.y >= cRows || cp.x >= cCols || cp.y < 0 || cp.x < 0) return false;
-			}
-		else cp.x = cp.y = 0;
-		switch (mev->Action) {
-		case MOUSE_LBDOWN:
-			if(m_range && (mev->StateFlags & 0x18)) mrk_offs = (int)strlen(m_range);
-			else mrk_offs = 0;
-			bActive = true;		new_mark = false;
-			if(!et_racc && CurrText && !CurrText->isInRect(&p)){
-				CurrText->Update(2, w, &p);			 CurrText = 0L;	
-				DoPlot(w);
-				}
-			if(p.x < FirstWidth) cp.x = 0;
-			if(p.y < (w->MenuHeight+CellHeight)) cp.y = 0;
-			currpos.y = currpos2.y = cp.y;
-			currpos.x = currpos2.x = cp.x;
-		case MOUSE_MOVE:
-			if(p.y < (w->MenuHeight+CellHeight)) {
-				if(Disp->Command(CMD_COL_MOUSE, tmpl, w)) return true;
-				}
-			else w->MouseCursor(MC_ARROW, false);
-		case MOUSE_LBDOUBLECLICK:
-			if(!bActive) return false;
-			if(!m_range && !CurrText && cp.y < cRows && cp.x < cCols && cp.y >= 0 && cp.x >= 0)
-				CurrText = etRows[cp.y][cp.x];
-			if(mev->Action == MOUSE_MOVE && (mev->StateFlags & 1) 
-				&& !(CurrText && CurrText->isInRect(&p))) {
-				//mark rectangular range
-				if(!et_racc && !m_range && CurrText) {
-					CurrText->Update(2, w, &p);		 CurrText = 0L;	
-					}
-				if(mrk_offs && m_range && m_range[0]){
-					mrk_offs = rlp_strcpy(TmpTxt, mrk_offs+1, m_range);
-					}
-				else mrk_offs = 0;
-				if(p.x < FirstWidth || p.y < (w->MenuHeight+CellHeight)) {
-					if(mrk_offs && TmpTxt[mrk_offs-1] != ';')TmpTxt[mrk_offs++] = ';';
-					rlp_strcpy(TmpTxt+mrk_offs, TMP_TXT_SIZE - mrk_offs, mkRangeRef(
-						p.y < (w->MenuHeight+CellHeight) ? 0 : currpos.y, p.x < FirstWidth ? 0 : currpos.x,
-						p.y < (w->MenuHeight+CellHeight) ? cRows : cp.y, p.x < FirstWidth ? cCols-1 : cp.x));
-					}
-				else {
-					if(mrk_offs && TmpTxt[mrk_offs-1] != ';')TmpTxt[mrk_offs++] = ';';
-					rlp_strcpy(TmpTxt+mrk_offs, TMP_TXT_SIZE - mrk_offs, mkRangeRef(currpos.y, currpos.x, cp.y, cp.x));
-					}
-				if(!CurrText || et_racc)MarkRange(TmpTxt, &cp);
-				return true;
-				}
-			if(mev->Action == MOUSE_LBDOUBLECLICK) bActive = false;
-			if(!(mev->StateFlags & 1)) return false;
-			if(CurrText && CurrText->isInRect(&p)) {
-				return CurrText->Command(CMD_MOUSE_EVENT, o, (DataObj *)mev);
-				}
-			if(etRows[cp.y][cp.x]) 
-				return etRows[cp.y][cp.x]->Command(CMD_MOUSE_EVENT, o, (DataObj *)mev);
-			return false;
-		case MOUSE_LBUP:
-			if(bActive){
-				if(p.y < (w->MenuHeight+CellHeight)) {
-					if(Disp->Command(CMD_COL_MOUSE, tmpl, w)) return true;
-					}
-				isRowMark = p.x < FirstWidth;
-				isColMark = p.y < (w->MenuHeight+CellHeight);
-				if(isRowMark || isColMark) {
-					if(p.x < FirstWidth) {
-						currpos.x = 0;	cp.x = cCols-1;
-						}
-					if(p.y < (w->MenuHeight+CellHeight)) {
-						currpos.y = 0;	cp.y = cRows-1;
-						}
-					if(mrk_offs && m_range && m_range[0]){
-						mrk_offs = rlp_strcpy(TmpTxt, mrk_offs+1, m_range);
-						}
-					else mrk_offs = 0;
-					if(mrk_offs && TmpTxt[mrk_offs-1] != ';')TmpTxt[mrk_offs++] = ';';
-					rlp_strcpy(TmpTxt+mrk_offs, TMP_TXT_SIZE - mrk_offs, mkRangeRef(currpos.y, currpos.x, cp.y, cp.x));
-					MarkRange(TmpTxt);			currpos2.x = cp.x;		currpos2.y = cp.y + 1;
-					}
-				else if((m_range) && !new_mark) HideMark(false);
-				if(et_racc && !et_racc->isInRect(&p)) {
-					CurrText = et_racc;
-					if(m_range && m_range[0] && (currpos.x != cp.x || currpos.y != cp.y)) {
-						CurrText->Command(CMD_ADDTXT, o, (DataObj*) m_range);
-						}
-					else {
-						m_range = (char*)realloc(m_range, 40*sizeof(char));
-						rlp_strcpy(m_range, 40, mkCellRef(currpos.y, currpos.x));
-						CurrText->Command(CMD_ADDTXT, o, (DataObj*) m_range);
-						}
-					}
-				else if(m_range) {
-					CurrText = etRows[currpos.y][currpos.x];	CurrText->Update(1, w, &p);
-					DoPlot(w);
-					currpos2.x = cp.x;		currpos2.y = cp.y;
-					}
-				else return Select(&p);
-				}
-			}
-		break;
-	case CMD_PASTE_TSV:		case CMD_PASTE_XML:	case CMD_PASTE_CSV:	case CMD_PASTE_SSV:
-		if(PasteRange(cmd, (char*)tmpl)) 
-			return Disp->Command(CMD_SETSCROLL, 0L, w);
-		return false;
-	case CMD_HIDEMARK:
-		if(c_range){
-			free(c_range);	c_range = 0L;	return true;
-			}
-		return false;
-	case CMD_GETFILENAME:
-		if(!tmpl) return false;
-		if(rlw_file && rlw_file[0]) {
-			if(rlp_strcpy((char*)tmpl, TMP_TXT_SIZE, rlw_file))return true;
-			else return false;
-			}
-		return false;
-	case CMD_ETRACC:
-		if(et_racc && tmpl && ((EditText*)tmpl)->parent != this) HideMark(false);
-		et_racc = (EditText*) tmpl;
-		if(CurrText && CurrText->parent != this) CurrText = 0L;
-		return true;
-	case CMD_CURRPOS:
-		if(tmpl) {
-			if(((POINT*)tmpl)->x < 0 && ((POINT*)tmpl)->y < 0) {
-				((POINT*)tmpl)->x = currpos.x;		((POINT*)tmpl)->y = currpos.y;
-				return true;
-				}
-			}
-		return false;
-	case CMD_UNLOCK:
-		HideMark(false);
-		currpos2.x = currpos.x;		currpos2.y = currpos.y;
-		break;
-	case CMD_ERROR:
-		if(err_msg && err_msg[0] && tmpl && *((char*)tmpl)) {
-			if(!(strcmp((char*)tmpl, "parse error")))return false;	//parse error has lowest priority
-			}
-		if(!tmpl && err_msg && err_msg[0] && strcmp(err_msg, "parse error")) return false;
-		err_msg = (char*)tmpl;
-		break;
-	case CMD_UPDATE:
-		bUpdate = true;
-		break;
-	case CMD_SAVEPOS:
-		return SavePos();
-	case CMD_CLEAR_ERROR:
-		err_msg = last_err = 0L;
-		break;
-	case CMD_TOOLMODE:						//ESC pressed
-		HideMark(true);
-		if(CurrText){
-			CurrText->Update(2, w, 0L);		CurrText->Update(1, w, 0L);
-			}
-		et_racc = 0L;						w->MouseCursor(MC_ARROW, true);
-		break;
-	case CMD_UPDHISTORY:
-		if(w) w->FileHistory();
-		break;
-	case CMD_MRK_DIRTY:
-		move_cr = CMD_CURRDOWN;
-		err_msg = last_err = 0L;
-		if(Disp) return Disp->Command(cmd, tmpl, o);
-		return false;
-	case CMD_ADDCHAR:
-		if(CurrText){
-			if(currpos.y < Disp->ssOrg.y) {
-				Disp->ssOrg.y = currpos.y;					Disp->Command(CMD_SETSCROLL, 0L, w);
-				}
-			else if(currpos.y > (Disp->ssOrg.y + r_disp -3)) {
-				Disp->ssOrg.y = currpos.y - (r_disp-3);
-				if(Disp->ssOrg.y < 0) Disp->ssOrg.y = 0;	Disp->Command(CMD_SETSCROLL, 0L, w);
-				}
-			if(currpos.x < Disp->ssOrg.x) {
-				Disp->ssOrg.x = currpos.x;					Disp->Command(CMD_SETSCROLL, 0L, w);
-				}
-			else if(currpos.x > (Disp->ssOrg.x + c_disp -3) && (CurrText->GetRX()+ 10) > Disp->currRC.right ) {
-				Disp->ssOrg.x = currpos.x - (c_disp-3);
-				if(Disp->ssOrg.x < 0) Disp->ssOrg.x = 0;	Disp->Command(CMD_SETSCROLL, 0L, w);
-				}
-			currpos2.x = currpos.x;		currpos2.y = currpos.y;
-			i = *((int*)tmpl);
-			Disp->Command(CMD_CURRPOS, &currpos, w);
-			if(i == 27) return Command(CMD_TOOLMODE, tmpl, o);
-			if(i !=3 && i != 22 && i != 24 && i != 26) HideMark(true);	//Do not hide upon ^C, ^V, ^X, ^Z
-			if(i == 13) return CurrText->Command(move_cr, w, this);
-			switch(i) {
-			case 8: return CurrText->Command(CMD_BACKSP, w, this);	//Backspace
-			default: return CurrText->AddChar(*((int*)tmpl), w, Disp);
-				}
-			}
-		else {
-			currpos.x = currpos2.x = Disp->ssOrg.x;			currpos.y = currpos2.y = Disp->ssOrg.y;
-			if(etRows[currpos.x] && (CurrText = etRows[currpos.x][currpos.y]))
-				CurrText->Update(1, w, &p);;
-			}
-		Disp->Command(CMD_CURRPOS, &currpos, w);
-		break;
-	case CMD_SHIFTUP:
-		if(Disp->ssOrg.y && currpos2.y <= Disp->ssOrg.y) {
-			Disp->ssOrg.y --;						Disp->Command(CMD_SETSCROLL, 0L, w);
-			}
-		currpos2.y -= 2;
-	case CMD_SHIFTDOWN:
-		if(cmd == CMD_SHIFTDOWN && r_disp > 3 && (currpos2.y-Disp->ssOrg.y) >= (r_disp-3)) {
-			Disp->ssOrg.y ++;						Disp->Command(CMD_SETSCROLL, 0L, w);
-			}
-		currpos2.y ++;
-		if(currpos2.y >= cRows) currpos2.y --;		if(currpos2.y < 0) currpos2.y ++;
-		//mark rectangular range
-		rlp_strcpy(TmpTxt, 40, mkRangeRef(currpos.y, currpos.x, currpos2.y, currpos2.x));
-		MarkRange(TmpTxt);							HideTextCursor();
-		break;
-	case CMD_SHIFTRIGHT:	case CMD_SHIFTLEFT:
-		if(!m_range && CurrText && CurrText->Command(cmd, w, this)) break;
-		if(cmd == CMD_SHIFTLEFT && c_disp > 3 && Disp->ssOrg.x && (currpos2.x-Disp->ssOrg.x) < 1) {
-			Disp->ssOrg.x --;						Disp->Command(CMD_SETSCROLL, 0L, w);
-			}
-		if(cmd == CMD_SHIFTRIGHT && c_disp > 3 && (currpos2.x-Disp->ssOrg.x) >= (c_disp-3)) {
-			Disp->ssOrg.x ++;						Disp->Command(CMD_SETSCROLL, 0L, w);
-			}
-		if(cmd == CMD_SHIFTLEFT) currpos2.x --;
-		else currpos2.x ++;
-		if(currpos2.x >= cCols) currpos2.x --;		if(currpos2.x < 0) currpos2.x ++;
-		//mark rectangular range
-		rlp_strcpy(TmpTxt, 40, mkRangeRef(currpos.y, currpos.x, currpos2.y, currpos2.x));
-		MarkRange(TmpTxt);							HideTextCursor();
-		break;
-	case CMD_SHPGUP:
-		if(Disp->ssOrg.y >0) {
-			Disp->ssOrg.y -= (r_disp-2);
-			if(Disp->ssOrg.y < 0) Disp->ssOrg.y = 0;
-			Disp->Command(CMD_SETSCROLL, 0L, w);
-			currpos2.y -= r_disp;					if(currpos2.y < 0) currpos2.y = 0;
-			rlp_strcpy(TmpTxt, 40, mkRangeRef(currpos.y, currpos.x, currpos2.y, currpos2.x));
-			MarkRange(TmpTxt);							HideTextCursor();
-			}
-		break;
-	case CMD_SHPGDOWN:
-		i = (Disp->ssOrg.y + 2*r_disp) < cRows ? r_disp-2 : cRows-r_disp - Disp->ssOrg.y+3;
-		if(i > 0) {
-			Disp->ssOrg.y += i;						Disp->Command(CMD_SETSCROLL, 0L, w);
-			}
-		if(currpos2.y < (cRows-1)) {
-			currpos2.y += r_disp;					if(currpos2.y >= cRows) currpos2.y = cRows-1;
-			//mark rectangular range
-			rlp_strcpy(TmpTxt, 40, mkRangeRef(currpos.y, currpos.x, currpos2.y, currpos2.x));
-			MarkRange(TmpTxt);							HideTextCursor();
-			}
-		break;
-	case CMD_CURRIGHT:		case CMD_CURRDOWN:
-		move_cr = cmd;		w->ActualSize(&rc);
-	case CMD_CURRLEFT:		case CMD_CURRUP:	case CMD_POS_FIRST:		case CMD_POS_LAST:
-		if(cmd == CMD_CURRUP && Disp->ssOrg.y && CurrText && currpos.y == Disp->ssOrg.y) {
-			Disp->ssOrg.y --;		currpos.y --;
-			DoPlot(o);
-			}
-		if(cmd == CMD_CURRLEFT && Disp->ssOrg.x && CurrText && (!CurrText->Cursor()) && (currpos.x == Disp->ssOrg.x)) {
-			Disp->ssOrg.x --;		currpos.x --;	
-			CurrText->Update(2, o, 0L);
-			Disp->Command(CMD_CURRPOS, &currpos, w);
-			if(etRows && etRows[currpos.y] && etRows[currpos.y][currpos.x]) 
-				CurrText = etRows[currpos.y][currpos.x];
-			if(CurrText){
-				CurrText->Command(CMD_POS_LAST, o, this);		CurrText->Update(1, o, 0L);
-				}
-			DoPlot(o);		return false;
-			}
-		if(CurrText && cmd == CMD_CURRIGHT && c_disp > 3 && ((currpos.x-Disp->ssOrg.x) >= (c_disp-1) || CurrText->GetRX() >= (rc.right-rc.left-10))) {
-			Disp->ssOrg.x ++;		currpos.y ++;			DoPlot(o);
-			CurrText->Command(CMD_POS_FIRST, o, this);
-			}
-		if(cmd == CMD_CURRDOWN && r_disp > 3 && (currpos.y-Disp->ssOrg.y) >= (r_disp-3)) {
-			Disp->ssOrg.y ++;		currpos.y ++;			DoPlot(o);
-			}
-		Disp->Command(CMD_CURRPOS, &currpos, w);
-		HideMark(false);
-		currpos2.x = currpos.x;								currpos2.y = currpos.y;
-		if(CurrText)return CurrText->Command(cmd, w, this);
-		else {
-			currpos.x = currpos2.x = Disp->ssOrg.x;			currpos.y = currpos2.y = Disp->ssOrg.y;
-			if(etRows[currpos.y] && (CurrText = etRows[currpos.y][currpos.x]))
-				CurrText->Update(1, w, &p);;
-			}
-		return false;
-	case CMD_SHTAB:
-		if(currpos.y >= cRows) currpos.y = cRows-1;
-		if(currpos.x == Disp->ssOrg.x && Disp->ssOrg.x > 0) {
-			Disp->ssOrg.x -= 1;
-			Disp->Command(CMD_SETSCROLL, 0L, w);
-			}
-		if(currpos.x > Disp->ssOrg.x && etRows[currpos.y][currpos.x]) {
-			currpos.x -=1;
-			}
-	case CMD_TAB:
-		if(currpos.y >= cRows) currpos.y = cRows-1;
-		if(cmd == CMD_TAB && currpos.x < (cCols-1) && etRows[currpos.y][currpos.x]) {
-			if((FirstWidth+(currpos.x - Disp->ssOrg.x +2)*CellWidth) > Disp->currRC.right){
-				Disp->ssOrg.x += 1;
-				Disp->Command(CMD_SETSCROLL, 0L, w);
-				}
-			currpos.x +=1;
-			}
-		if(CurrText) CurrText->Update(2, w, &p);
-		CurrText = etRows[currpos.y][currpos.x];
-		if(CurrText){
-			CurrText->Update(1, w, &p);
-			CurrText->Command(cmd == CMD_TAB ? CMD_POS_FIRST : CMD_POS_LAST, o, this);
-			}
-		Disp->Command(CMD_CURRPOS, &currpos, w);
-		return true;
-	case CMD_UNDO:
-		if(w) {
-			w->MouseCursor(MC_WAIT, true);
-			Undo.Restore(true, w);
-			w->MouseCursor(MC_ARROW, true);
-			if(et_racc) {
-				CurrText = et_racc;
-				CurrText->Update(1, w, 0L);
-				}
-			}
-		return true;
-	case CMD_DELETE:
-		if(m_range) DelRange();
-		else if(CurrText) return CurrText->Command(cmd, o, this);
-		Disp->Command(CMD_CURRPOS, &currpos, w);
-		return true;
-	case CMD_QUERY_COPY:		case CMD_CUT:
-		et_racc = 0L;
-		return InitCopy(cmd, tmpl, w);
-	case CMD_GET_CELLDIMS:
-		if(tmpl) {
-			((int*)tmpl)[0] = FirstWidth;	((int*)tmpl)[1] = CellWidth;
-			((int*)tmpl)[2] = CellHeight;
-			}
-		break;
-	case CMD_SET_CELLDIMS:
-		if(tmpl && (FirstWidth != ((int*)tmpl)[0] || CellWidth != ((int*)tmpl)[1] || CellHeight != ((int*)tmpl)[2])) {
-			Undo.ValInt(Disp, &FirstWidth, UNDO_CONTINUE);	Undo.ValInt(Disp, &CellWidth, UNDO_CONTINUE);
-			Undo.ValInt(Disp, &CellHeight, UNDO_CONTINUE);	FirstWidth = ((int*)tmpl)[0];
-			CellWidth = ((int*)tmpl)[1];					CellHeight = ((int*)tmpl)[2];
-			}
-		break;
-	case CMD_TEXTSIZE:
-		if(tmpl) return Disp->Command(cmd, tmpl, o);
-		return false;
-	case CMD_COPY_TSV:
-		return MemList((unsigned char**)tmpl, FF_TSV);
-	case CMD_COPY_SYLK:
-		return MemList((unsigned char**)tmpl, FF_SYLK);
-	case CMD_COPY_XML:
-		return MemList((unsigned char**)tmpl, FF_XML);
-	case CMD_DOPLOT:	case CMD_REDRAW:
-		if(CurrText) CurrText->Update(2, 0L, 0L);
-		if(etRows && etRows[currpos.y] && currpos.y < cRows && currpos.x < cCols) 
-			if(CurrText = etRows[currpos.y][currpos.x]) CurrText->Update(1,0L, 0L);
-		DoPlot(o);
-		break;
-	case CMD_FILLRANGE:
-		Undo.SetDisp(w);
-		FillSsRange(this, &m_range, Disp);
-		DoPlot(o);
-		Undo.SetDisp(w);
-		break;
-	case CMD_GETMARK:
-		if(tmpl && m_range && m_range[0]) {
-			*((char**)tmpl) = m_range;
-			return true;
-			}
-		return false;
-	case CMD_INSROW:
-		return InsertRows();
-	case CMD_INSCOL:
-		return InsertCols();
-	case CMD_DELROW:
-		return DeleteRows();
-	case CMD_DELCOL:
-		return DeleteCols();
-	}
-	return true;
-}
-
-bool
-SpreadData::ReadXML(char *file, unsigned char *buffer, int type, DWORD undo_flags)
-{
-	int i, row, col, tag, cpgr, spgr, ufl = 0;
-	bool bContinue, bRet = false, bUndo_done = false;
-	ReadCache *XMLcache;
-	POINT pt, mov;
-	char TmpTxt[1024], *tmp_range;
-	unsigned char *pgr = 0L;
-	RECT rc_undo;
-
-	if(file) {
-		if(!(XMLcache = new ReadCache())) return false;
-		if(! XMLcache->Open(file)) {
-			delete XMLcache;
-			i = rlp_strcpy(TmpTxt, 40, "Error open file\n\"");		i = rlp_strcpy(TmpTxt+i, 200, file);
-			rlp_strcpy(TmpTxt+i, 2, "\"");							ErrorBox(TmpTxt);
-			return false;
-			}
-		bUndo_done = true;
-		if(!Init(1, 1)) goto XMLError;
-		etRows[0][0]->SetText("");
-		}
-	else if(buffer && type == FF_XML){
-		if(!(XMLcache = new MemCache(buffer))) return false;
-		if(buffer && bCopyCut) {
-			tmp_range = m_range;	m_range = c_range;
-			c_range = 0L;			DelRange();
-			m_range = tmp_range;
-			}
-		}
-	else return false;
-	pt.x = pt.y = mov.x = mov.y = 0;
-	cp_src_rec.left = cp_src_rec.right = cp_src_rec.top = cp_src_rec.bottom = 0;
-	do {
-		row = col = 0;
-		do {
-			TmpTxt[0] = XMLcache->Getc();
-			}while(TmpTxt[0] && TmpTxt[0] != '<');
-		for(i = 1; i < 5; TmpTxt[i++] = XMLcache->Getc());
-		TmpTxt[i] = 0;
-		if(!strcmp("<cell", TmpTxt)){
-			if(!bUndo_done) {
-				rc_undo.left = currpos.x;
-				rc_undo.right = cp_src_rec.right - cp_src_rec.left + currpos.x;
-				rc_undo.top = currpos.y;
-				rc_undo.bottom = cp_src_rec.bottom - cp_src_rec.top + currpos.y;
-				if(ufl == 3) Undo.DataObject(Disp, w, this, &rc_undo, undo_flags);
-				bUndo_done = true;
-				}
- 			tag = 1;
-			}
-		else if(!strcmp("<pos1", TmpTxt)) tag = 2;
-		else if(!strcmp("<pos2", TmpTxt)) tag = 3;
-		else if(!strcmp("<RLPl", TmpTxt)) {
-			do {
-				TmpTxt[i++] = XMLcache->Getc();
-				}while(TmpTxt[i-1] > 31 && TmpTxt[i-1] != '>');
-			TmpTxt[i] = 0;
-			row = col = 0;
-			if(TmpTxt[17] == '=' && TmpTxt[13] == 'r') {
-#ifdef USE_WIN_SECURE
-				sscanf_s(TmpTxt+19, "%d", &row);
-#else
-				sscanf(TmpTxt+19, "%d", &row);
-#endif
-				}
-			else break;
-			for(i = 20; TmpTxt[i] && TmpTxt[i] != '='; i++);
-#ifdef USE_WIN_SECURE
-			sscanf_s(TmpTxt+i+2, "%d", &col);
-#else
-			sscanf(TmpTxt+i+2, "%d", &col);
-#endif
-			if(row && col) {
-				AddCols(col);		AddRows(row);
-				}
-			row = col = -1;
-			}
-		else if(!strcmp("<Grap", TmpTxt)){
-			while(XMLcache->Getc() != '[');
-			pgr = (unsigned char*)malloc(spgr = 1000);
-			pgr[0] = '[';	cpgr = 1;
-			do {
-				pgr[cpgr++] = XMLcache->Getc();
-				if(cpgr >= spgr) pgr = (unsigned char*)realloc(pgr, spgr +=1000);
-				}while(!(pgr[cpgr-1] == '<' && pgr[cpgr-2] == 0x0a));
-			pgr[cpgr-2] = 0;
-			while(XMLcache->Getc() != 0x0a);
-			if(cpgr >20)OpenGraph(Disp, 0L, pgr, false);
-			free(pgr);			tag = 0;
-			}
-		else tag = 0;
-		if(tag) {
-			do {
-				TmpTxt[0] = XMLcache->Getc();
-				}while(TmpTxt[0] && TmpTxt[0] != '"');
-			if (TmpTxt[0]) for (i =0; i <10 && ('"' != (TmpTxt[i] =XMLcache->Getc())); i++){
-				TmpTxt[i+1] = 0;
-				row = (int)atoi(TmpTxt);
-				}
-			do {
-				TmpTxt[0] = XMLcache->Getc();
-				}while(TmpTxt[0] && TmpTxt[0] != '"');
-			if (TmpTxt[0]) for (i =0; i <10 && ('"' != (TmpTxt[i] =XMLcache->Getc())); i++){
-				TmpTxt[i+1] = 0;
-				col = (int)atoi(TmpTxt);
-				}
-			if(tag ==2) {
-				mov.x = col;				mov.y = row;
-				cp_src_rec.left = col;		cp_src_rec.top = row;
-				ufl |= 1;
-				}
-			else if(tag ==3) {
-				cp_src_rec.right = col;		cp_src_rec.bottom = row;
-				ufl |= 2;
-				}
-			else if(row && col) do {
-				do {
-					TmpTxt[0] = XMLcache->Getc();
-					}while(TmpTxt[0] && TmpTxt[0] != '<');
-				for(i = 1; i < 6; TmpTxt[i++] = XMLcache->Getc());
-				TmpTxt[i] = 0;
-				if(bContinue =(0 == strcmp("<text>", TmpTxt))) {
-					for(i = 0; i < 1023; i++) {
-						if(!(TmpTxt[i] =XMLcache->Getc())) break;
-						if(i >1 && TmpTxt[i-2] == '<' && TmpTxt[i-1] == '/' && TmpTxt[i] == 't') break;
-						}
-					i -=2;		TmpTxt[i] = 0;
-					//xml indices start at 1:1 !
-					row += currpos.y-1;	col += currpos.x-1;
-					if(row >= cRows)AddRows(row+1);
-					if(col >= cCols)AddCols(col+1);
-					if(i && etRows[row] && etRows[row][col]) {
-						if(TmpTxt[0] == '=') {
-							MoveFormula(this, TmpTxt, TmpTxt, TMP_TXT_SIZE, currpos.x-mov.x, currpos.y-mov.y, -1, -1);
-							}
-						etRows[row][col]->SetText(TmpTxt);
-						etRows[row][col]->Update(20, 0L, &pt);
-						}
-					}
-				}while(!bContinue);
-			}
-		}while(!XMLcache->IsEOF());
-	bRet = true;
-XMLError:
-	XMLcache->Close();
-	delete XMLcache;
-	bCopyCut = false;
-	return bRet;
-}
-
-bool
-SpreadData::ReadTSV(char *file, unsigned char *buffer, int type)
-{
-	int i, row, col;
-	char c;
-	bool bRet = false;
-	ReadCache *TSVcache;
-	POINT pt;
-
-	if(file) {
-		if(!(TSVcache = new ReadCache())) return false;
-		if(! TSVcache->Open(file)) {
-			delete TSVcache;
-			i = rlp_strcpy(TmpTxt, 200, "Error open file\n\"");
-			i += rlp_strcpy(TmpTxt+i, 200-i, file);
-			i += rlp_strcpy(TmpTxt+i, 200-i, "\"\n");
-			ErrorBox(TmpTxt);
-			return false;
-			}
-		if(!Init(1, 1)) goto TSVError;
-		etRows[0][0]->SetText("");
-		}
-	else if(buffer && (type == FF_TSV || type == FF_SSV)) {
-		if(!(TSVcache = new MemCache(buffer))) return false;
-		}
-	else return false;
-	row = currpos.y;	col = currpos.x;
-	pt.x = pt.y = 0;
-	do {
-		do {
-			TmpTxt[0] = TSVcache->Getc();
-			switch(TmpTxt[0]) {
-			case 0x0d:					//CR
-			case 0x0a:					//LF
-				if(col == currpos.x) break;
-				row ++;			col = currpos.x;		break;
-			case 0x09:					//tab
-				col ++;			break;
-			case ' ':
-				if(type == FF_SSV) col ++;				break;
-				}
-			}while(TmpTxt[0] && ((unsigned char)TmpTxt[0] < 33));
-		for(i = 1; ((unsigned char)(TmpTxt[i] = c = TSVcache->Getc()))>= (type == FF_SSV ? 33 : 32); i++)
-		if(i >= 4094) i = 4094;
-		if(TmpTxt[0] && row >= cRows)AddRows(row+1);
-		if(TmpTxt[0] && col >= cCols)AddCols(col+1);
-		TmpTxt[i] = 0;
-		if(TmpTxt[0] && etRows[row] && etRows[row][col]) {
-			etRows[row][col]->SetText(TmpTxt);
-			etRows[row][col]->Update(20, 0L, &pt);
-			switch(c) {
-			case 0x0d:					//CR
-			case 0x0a:					//LF
-				row ++;			col = currpos.x;		break;
-			case 0x09:					//tab
-				col ++;			break;
-			case ' ':
-				if(type == FF_SSV) col ++;				break;
-				}
-			}
-		}while(!TSVcache->IsEOF());
-	bRet = true;
-TSVError:
-	TSVcache->Close();
-	delete TSVcache;
-	return bRet;
-}
-
-static void sylk_cell_ref(char** ptr, int *cbd, int *size, char *first, char* trail, int row, int col)
-{
-	if(first && first[0]) add_to_buff(ptr, cbd, size, first, 0);
-	add_to_buff(ptr, cbd, size, "Y", 1);	add_int_to_buff(ptr, cbd, size, row+1, false, 0);
-	add_to_buff(ptr, cbd, size, ";X", 2);	add_int_to_buff(ptr, cbd, size, col+1, false, 0);
-	if(trail && trail[0]) add_to_buff(ptr, cbd, size, trail, 0);
-}
-
-bool 
-SpreadData::MemList(unsigned char **ptr, int type)
-{
-	int i, j, nc, nl; 
-	int cbd = 0, size;
-	bool bLimit = true;
-	AccRange *ar;
-	anyResult res;
-	RECT rcCopy;
-
-	if(!(*ptr = (unsigned char *)malloc(size = 10000)))return false;
-	if (c_range &&  c_range[0]) {
-		ar = new AccRange(c_range);			ar->BoundRec(&rcCopy);
-		if(bCopyCut) Undo.DataObject(Disp, w, this, &rcCopy, 0L);
-		}
-	else {
-		ar = 0L;	rcCopy.left = rcCopy.top = 0;
-		rcCopy.right = cCols-1;				rcCopy.bottom = cRows-1;
-		}
-	if(rcCopy.left < 0) rcCopy.left = 0;	if(rcCopy.right >= cCols) rcCopy.right = cCols-1;
-	if(rcCopy.top < 0) rcCopy.top = 0;		if(rcCopy.bottom >= cRows) rcCopy.bottom = cRows-1;
-	if(type == FF_SYLK) cbd = rlp_strcpy((char*)*ptr, size, "ID;PWXL;N;E\n"
-		"P;Pdd/mm/yyyy\nP;Phh:mm:ss\nP;Pdd/mm/yyyy hh:mm:ss\n");
-	else if(type == FF_XML) {
-		cbd = rlp_strcpy((char*)*ptr, 100, "<?xml version=\"1.0\"?><!DOCTYPE spreadsheet-snippet>"
-			"<spreadsheet-snippet rows=\"");
-		add_int_to_buff((char**)ptr, &cbd, &size, cRows, false, 0);
-		add_to_buff((char**)ptr, &cbd, &size, "\" columns=\"", 11);
-		add_int_to_buff((char**)ptr, &cbd, &size, cCols, false, 0);
-		add_to_buff((char**)ptr, &cbd, &size, "\">\n", 3);
-		if(rcCopy.left || rcCopy.top || rcCopy.bottom || rcCopy.right){
-			add_to_buff((char**)ptr, &cbd, &size, " <pos1 row=\"", 12);
-			add_int_to_buff((char**)ptr, &cbd, &size, rcCopy.top, false, 0);
-			add_to_buff((char**)ptr, &cbd, &size, "\" column=\"", 10);
-			add_int_to_buff((char**)ptr, &cbd, &size, rcCopy.left, false, 0);
-			add_to_buff((char**)ptr, &cbd, &size, "\"></pos1>\n <pos2 row=\"", 22);
-			add_int_to_buff((char**)ptr, &cbd, &size, rcCopy.bottom, false, 0);
-			add_to_buff((char**)ptr, &cbd, &size, "\" column=\"", 10);
-			add_int_to_buff((char**)ptr, &cbd, &size, rcCopy.right, false, 0);
-			add_to_buff((char**)ptr, &cbd, &size, "\"></pos2>\n", 10);
-			}
-		}
-	else if(type == FF_RLW) {
-		cbd = rlp_strcpy((char*)*ptr, size, "<?xml version=\"1.0\"?><!DOCTYPE RLPlot-workbook>\n<RLPlot-data rows=\"");
-		add_int_to_buff((char**)ptr, &cbd, &size, cRows, false, 0);
-		add_to_buff((char**)ptr, &cbd, &size, "\" columns=\"", 11);
-		add_int_to_buff((char**)ptr, &cbd, &size, cCols, false, 0);
-		add_to_buff((char**)ptr, &cbd, &size, "\">\n", 3);
-		}
-	for(nl =0, i = rcCopy.top; i <= rcCopy.bottom; i++, nl++) {
-		for(nc = 0, j = rcCopy.left; j <= rcCopy.right; j++, nc++) {
-			switch (type) {
-			case FF_TSV:
-				if(nl || nc) add_to_buff((char**)ptr, &cbd, &size, nc ? (char*)"\t" : (char*)"\n", 1);
-				if(etRows[i] && etRows[i][j]){
-					if((ar && ar->IsInRange(j,i)) || !ar) {
-						etRows[i][j]->GetResult(&res, false);
-						TranslateResult(&res);
-						add_to_buff((char**)ptr, &cbd, &size, res.text, 0);
-						}
-					}
-				break;
-			case FF_SYLK:
-				if(etRows[i] && etRows[i][j] && etRows[i][j]->text && ((ar && ar->IsInRange(j,i)) || !ar)){
-					etRows[i][j]->GetResult(&res, false);
-					TranslateResult(&res);
-					switch(res.type) {
-					case ET_VALUE:	case ET_BOOL:
-						sylk_cell_ref((char**) ptr, &cbd, &size, "C;", ";K", nl, nc);
-						add_to_buff((char**)ptr, &cbd, &size, res.text, 0);
-						break;
-					case ET_DATE:
-						sylk_cell_ref((char**) ptr, &cbd, &size, "F;P0;FG0G;", "\nC;K", nl, nc);
-						add_dbl_to_buff((char**)ptr, &cbd, &size, res.value+1, false);
-						break;
-					case ET_DATETIME:
-						sylk_cell_ref((char**) ptr, &cbd, &size, "F;P2;FG0G;", "\nC;K", nl, nc);
-						add_dbl_to_buff((char**)ptr, &cbd, &size, res.value+1, false);
-						break;
-					case ET_TIME:
-						sylk_cell_ref((char**) ptr, &cbd, &size, "F;P1;FG0G;", "\nC;K", nl, nc);
-						add_dbl_to_buff((char**)ptr, &cbd, &size, res.value, false);
-						break;
-					case ET_TEXT:
-						sylk_cell_ref((char**) ptr, &cbd, &size, "C;", ";K\"", nl, nc);
-						add_to_buff((char**)ptr, &cbd, &size, res.text, 0);
-						add_to_buff((char**)ptr, &cbd, &size, "\"", 1);
-						break;
-						}
-					add_to_buff((char**)ptr, &cbd, &size, "\n", 1);
-					if(bCopyCut) etRows[i][j]->SetText("");
-					}
-				break;
-			case FF_RLW:	case FF_XML:
-				if(etRows[i] && etRows[i][j] && etRows[i][j]->text && ((ar && ar->IsInRange(j,i)) || !ar)
-					&& etRows[i][j]->text[0]){
-					add_to_buff((char**)ptr, &cbd, &size, " <cell row=\"", 12);
-					add_int_to_buff((char**)ptr, &cbd, &size, nl+1, false, 0);
-					add_to_buff((char**)ptr, &cbd, &size, "\" column=\"", 10);
-					add_int_to_buff((char**)ptr, &cbd, &size, nc+1, false, 0);
-					add_to_buff((char**)ptr, &cbd, &size, "\">\n  <text>", 11);
-					add_to_buff((char**)ptr, &cbd, &size, etRows[i][j]->text, 0);
-					add_to_buff((char**)ptr, &cbd, &size, "</text>\n </cell>\n", 17);
-					if(bCopyCut) etRows[i][j]->SetText("");
-					}
-				break;
-				}
-			}
-		}
-	if(type == FF_SYLK) {
-		if(!bLimit) {
-			add_to_buff((char**)ptr, &cbd, &size, "C;Y", 3);
-			add_int_to_buff((char**)ptr, &cbd, &size, i+2, false, 0);
-			add_to_buff((char**)ptr, &cbd, &size, ";X1;K\"long strings were"
-			" truncated to 256 characters due to limitations of the SYLK format!\"\n", 92);
-			}
-		add_to_buff((char**)ptr, &cbd, &size, "E\n", 2);
-		}
-	else if(type == FF_TSV) add_to_buff((char**)ptr, &cbd, &size, "\n", 1);
-	else if(type == FF_XML) add_to_buff((char**)ptr, &cbd, &size, "</spreadsheet-snippet>\n", 23);
-	else if(type == FF_RLW){
-		Disp->Command(CMD_WRITE_GRAPHS, ptr, (anyOutput*)&cbd);
-		//note: cbd may be greater than size !
-		add_to_buff((char**)ptr, &cbd, &size, "</RLPlot-data>\n", 15);
-		}
-	if(ar) delete ar;
-	if(bCopyCut && type == FF_XML || type == FF_SYLK || type == FF_RLW){
-		bCopyCut = false;
-		DoPlot(w);
-		}
-	return true;
-}
-
-void SpreadMain(bool show)
-{
-	static SpreadData *w = 0L;
-
-	if(show) {
-		if(w = new SpreadData(0L)) w->Init(50, 10);
-		do_formula(w, 0L);			//init mfcalc
-		}
-	else if (w) delete(w);
-}
+//spreadwin.cpp, (c)2000-2008 by R. Lackner
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+#include "rlplot.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <math.h>
+
+#include <fcntl.h>				//file open flags
+#include <sys/stat.h>			//I/O flags
+
+#ifdef _WINDOWS
+	#include <io.h>					//for read/write
+#else
+	#define O_BINARY 0x0
+	#include <unistd.h>
+#endif
+
+extern const LineDEF GrayLine;
+extern GraphObj *CurrGO, *TrackGO;			//Selected Graphic Objects
+extern EditText *CurrText;
+extern char *LoadFile;
+extern char TmpTxt[];
+extern Default defs;
+extern UndoObj Undo;
+
+static ReadCache *Cache = 0L;
+static TextDEF ssText;
+static char *szRlpData = "RLPlot data";
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Get item from *.csv file
+bool GetItemCSV(char *Text, int cbText)
+{
+	char c;
+	int i;
+
+	for (i = 0,	*Text = 0; i < cbText; ) {
+		c = Cache->Getc();
+		switch(c) {
+		case ',':			//column separator
+			Text[i] = 0;
+			return true;
+		case 0x0a:			//end of line: mark by false return but text o.k.
+			Text[i] = 0;
+			return false;
+		default:
+			if(c > 0x20) Text[i++] = c;	//printable character
+			else if(i >0 && c == 0x20) Text[i++] = c;
+			else if(!c && Cache->IsEOF()) {
+				Text[i] = 0;
+				return false;
+				}
+			else Text[i] = 0;	//ignore non printing characters
+			}
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// process a memory block (i.e. clipboard data) as if file input
+int ProcMemData(GraphObj *g, unsigned char *ptr, bool dispatch)
+{
+	int i, RetVal = FF_UNKNOWN, nt, nl, nc, ns;
+
+	if(ptr) {
+		for(i = nt = nl = nc = ns = 0; ptr[i] && nl<100; i++) {
+			switch(ptr[i]) {
+			case 0x09:				//tab
+				nt++;
+				break;
+			case 0x0a:				//LF
+				nl++;
+				break;
+			case ',':
+				nc++;
+				break;
+			case ' ':
+				ns++;
+				break;
+				}
+			}
+		if(dispatch && i && !nt && !nl) {
+			if(CurrText){
+				g->Command(CMD_SETFOCUS, 0L, 0L);
+				for(i = 0; ptr[i]; i++)	CurrText->AddChar(ptr[i], i? 0L : Undo.cdisp, 0L);
+				g->Command(CMD_REDRAW, 0L, 0L);
+				}
+			}
+		else if(nt) RetVal = FF_TSV;
+		else if(nl && ptr[0] == '<') RetVal = FF_XML;
+		else if(nc == nl && defs.DecPoint[0] == ',') RetVal = FF_TSV;
+		else if(nl && nc && 0 == (nc % nl)) RetVal = FF_CSV;
+		else if(nl && ns && 0 == (ns % nl)) RetVal = FF_SSV;
+		else if(nl) RetVal = FF_TSV;
+		if(dispatch) switch(RetVal) {
+		case FF_CSV:	g->Command(CMD_PASTE_CSV, ptr, 0L);	break;
+		case FF_TSV:	g->Command(CMD_PASTE_TSV, ptr, 0L);	break;
+		case FF_SSV:	g->Command(CMD_PASTE_SSV, ptr, 0L);	break;
+		case FF_XML:	g->Command(CMD_PASTE_XML, ptr, 0L); break;
+			}
+		}
+	return RetVal;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// This graphic object displays a spreadsheet
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class SpreadWin:public GraphObj{
+public:
+	anyOutput *w;
+	POINT ssOrg;
+	RECT currRC;
+
+	SpreadWin(GraphObj *par, DataObj *Data);
+	~SpreadWin();
+	void DoPlot(anyOutput *target);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+
+	bool ShowGrid(int CellWidth, int CellHeight, int FirstWidth, POINT *cpos);
+	void MarkButtons(char *rng, POINT *cp = 0L);
+	bool PrintData(anyOutput *o);
+	void WriteGraphXML(unsigned char **ptr, long *cbd);
+
+private:
+	bool is_modified, bDoColWidth;
+	char *filename;
+	ssButton **cButtons, **rButtons;
+	ssButton *aButton;
+	POINT cpos;
+	DataObj *d;
+	int NumGraphs, CurrCol;
+	Graph **g;
+	RECT rc_line;
+	POINT line[2];
+};
+
+SpreadWin::SpreadWin(GraphObj *par, DataObj *Data):GraphObj(par, Data)
+{
+	d = Data;	g = 0L;		ssOrg.x =  ssOrg.y = 0;		NumGraphs = 0;
+	filename=0L;	aButton = 0L;
+	w = 0L;		cButtons = rButtons = 0L;
+	if(w = NewDispClass(this)){
+		w->hasHistMenu = true;
+		ssText.RotBL = ssText.RotCHAR = 0.0;
+		ssText.fSize = 0.0f;
+		ssText.iSize = w->un2iy(defs.GetSize(SIZE_CELLTEXT));
+		ssText.Align = TXA_VCENTER | TXA_HLEFT;		ssText.Mode = TXM_TRANSPARENT;
+		ssText.Style = TXS_NORMAL;					ssText.ColBg = 0x00e8e8e8L;
+		ssText.ColTxt = 0x00000000L;				ssText.text = 0L;
+		ssText.Font = FONT_HELVETICA;				w->SetTextSpec(&ssText);
+		w->SetMenu(MENU_SPREAD);					w->FileHistory();
+		w->Erase(0x00e8e8e8L);						w->Caption(szRlpData, false);
+		d->ri->SetDefWidth(w->un2ix(defs.GetSize(SIZE_CELLWIDTH)));
+		d->ri->SetHeight(w->un2iy(defs.GetSize(SIZE_CELLTEXT)/defs.ss_txt) + 2);
+		d->ri->SetFirstWidth(32);
+		}
+	else if(d &&  d->ri) {
+		d->ri->SetHeight(19);	d->ri->SetDefWidth(76);	d->ri->SetFirstWidth(32);
+		}
+	Id = GO_SPREADDATA;
+	is_modified = bDoColWidth = false;
+}
+
+SpreadWin::~SpreadWin()
+{
+	int i;
+
+	if(parent) {
+		if(cButtons) {
+			for(i = 0; cButtons[i]; i++) if(cButtons[i]) delete(cButtons[i]);
+			free(cButtons);
+			}
+		if(rButtons) {
+			for(i = 0; rButtons[i]; i++) if(rButtons[i]) delete(rButtons[i]);
+			free(rButtons);
+			}
+		if (aButton) delete(aButton);
+		if (w) delete w;
+		if (g && NumGraphs) {
+			for(i = 0; i < NumGraphs; i++) if(g[i]) delete(g[i]);
+			free (g);
+			}
+		if(filename) free(filename);	filename=0L;
+		}
+}
+
+void
+SpreadWin::DoPlot(anyOutput *o)
+{
+	if(!(o->ActualSize(&currRC)))return;
+	o->StartPage();
+	d->Command(CMD_DOPLOT, (void*)this, o);
+	o->EndPage();
+}
+
+bool
+SpreadWin::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	char *Name;
+	Graph *g2;
+	int i, j, k;
+	bool bRet;
+	MouseEvent *mev;
+	POINT p1, p2;
+
+	if(d) {
+		if(!o) o = w;
+		switch(cmd) {
+		case CMD_CURRPOS:
+			if(tmpl && cButtons && rButtons) {
+				int ac = 1, na =  0;
+				RECT urc;
+				if(((POINT*)tmpl)->x != cpos.x) {
+					for(cpos.x = ((POINT*)tmpl)->x, i = 0; cButtons[i]; i++) {
+						cButtons[i]->Command(CMD_SELECT, (cpos.x == (i+ssOrg.x)) ? &ac : &na, w);
+						}
+					urc.left = cButtons[0]->rDims.left;		urc.bottom = cButtons[0]->rDims.bottom;
+					urc.top = cButtons[0]->rDims.top;		urc.right = urc.left + d->ri->GetWidth(i+ssOrg.x) * (i-1);
+					w->UpdateRect(&urc, false);
+					}
+				if(((POINT*)tmpl)->y != cpos.y) {
+					for(cpos.y = ((POINT*)tmpl)->y, i = 0; rButtons[i]; i++) {
+						rButtons[i]->Command(CMD_SELECT, (cpos.y == (i+ssOrg.y)) ? &ac : &na, w);
+						}
+					urc.left = rButtons[0]->rDims.left;		urc.right = rButtons[0]->rDims.right;
+					urc.top = rButtons[0]->rDims.top;		urc.bottom = urc.top + d->ri->GetHeight(i+ssOrg.y) * (i-1);
+					w->UpdateRect(&urc, false);
+					}
+				}
+			else return false;
+			return true;
+		case CMD_CAN_CLOSE:
+			HideTextCursor();
+			if(is_modified == true) {
+				is_modified=false;
+				if(Undo.isEmpty(0L)) return true;
+				i = YesNoCancelBox("The spreadsheet or a graph has been modified!\n\nDo you want to save it now?");
+				if(i == 2) return false;
+				else if(i == 1) return Command(CMD_SAVEAS, tmpl, o);
+				}
+			return true;
+		case CMD_MRK_DIRTY:
+			if(!is_modified) {
+				o->Caption(filename && filename[0]?filename : szRlpData, true);
+				}
+			return is_modified = true;
+		case CMD_WRITE_GRAPHS:
+			if (g && NumGraphs) WriteGraphXML((unsigned char**)tmpl, (long*)o);
+			return true;
+		case CMD_DROP_GRAPH:
+			if(!tmpl) return false;				if(o) o->FileHistory();
+			if(g && NumGraphs) {
+				if(g = (Graph**)realloc(g, (NumGraphs+2) * sizeof(Graph*)))
+					g[NumGraphs++] = (Graph *)tmpl;
+				else return false;
+				}
+			else {
+				if(g = (Graph **)calloc(2, sizeof(Graph*))){
+					g[0] = (Graph *)tmpl;		NumGraphs = 1;
+					}
+				}
+			for(i = j = 0; i < NumGraphs; i++) {
+				if(g[i]) {
+					g[j] = g[i];			g[j]->parent = this;
+					g[j]->Command(CMD_SET_DATAOBJ, (void*)d, 0L);
+					j++;
+					}
+				}
+			NumGraphs = j;	g[j-1]->DoPlot(0L);
+			return true;
+		case CMD_NEWGRAPH:
+			if((g2 = new Graph(this, d, 0L, 0)) && g2->PropertyDlg() && 
+				Command(CMD_DROP_GRAPH, g2, o))return Command(CMD_REDRAW, 0L, o);
+			else if(g2) DeleteGO(g2);
+			Undo.SetDisp(w);
+			return false;
+		case CMD_NEWPAGE:
+			if((g2 = new Page(this, d)) && 
+				Command(CMD_DROP_GRAPH, g2, o))return Command(CMD_REDRAW, 0L, o);
+			else if(g2) DeleteGO(g2);
+			Undo.SetDisp(w);
+			return false;
+		case CMD_DELGRAPH:
+			if (g && NumGraphs) {
+				for(i = 0; i < NumGraphs; i++) if(g[i]) DeleteGO(g[i]);
+				free (g);
+				}
+			g = 0L;			NumGraphs = 0;		Undo.Flush();
+			return true;
+		case CMD_DELOBJ:
+			i = j = 0;
+			if(g && tmpl) for(; i < NumGraphs; i++) {
+				if(g[i] == (Graph*) tmpl) {
+					DeleteGO(g[i]);
+					}
+				else (g[j++] = g[i]);
+				}
+			if(g && j < i) g[j] = 0L;			NumGraphs = j;
+			return true;
+		case CMD_SAVE:
+			if(o) o->MouseCursor(MC_WAIT, false);
+			if(d->WriteData(0L)) {
+				o->Caption(filename && filename[0]?filename : szRlpData, false);
+				is_modified=false;
+				if(o) o->MouseCursor(MC_ARROW, false);
+				return true;
+				}
+			if(o) o->MouseCursor(MC_ARROW, true);
+		case CMD_SAVEAS:
+			is_modified=false;
+			if((Name = SaveDataAsName(filename)) && Name[0]){
+				if(o) o->FileHistory();
+				if(Name && d->WriteData(Name)) {
+					o->Caption(filename && filename[0]?filename : szRlpData, false);
+					if(filename) free(filename);
+					filename = (char*)memdup(Name, (int)strlen(Name)+1, 0);
+					}
+				else return false;
+				}
+			else return false;
+			return true;
+		case CMD_DROPFILE:
+			if(!Command(CMD_CAN_CLOSE, 0L, o)) return false;
+			if(IsRlpFile((char*)tmpl)) return OpenGraph(this, (char*)tmpl, 0L, false);
+			else if(d->ReadData((char*)tmpl, 0L, FF_UNKNOWN)){
+				if(filename) free(filename);
+				filename = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0);
+				o->Caption(filename && filename[0]?filename : szRlpData, is_modified = false);
+				return Command(CMD_SETSCROLL, 0L, w);
+				}
+			else ErrorBox("The selected file is not valid\nor not accessible!\n");
+			return false;
+		case CMD_OPEN:
+			if(!Command(CMD_CAN_CLOSE, 0L, o)) return false;
+			Undo.KillDisp(o);		Undo.SetDisp(o);
+			if((Name = OpenDataName(filename)) && Name[0]){
+				if(o) o->FileHistory();
+				if(IsRlpFile(Name)) return OpenGraph(this, Name, 0L, false);
+				else if(d->ReadData(Name, 0L, FF_UNKNOWN)){
+					if(filename) free(filename);
+					filename = (char*)memdup(Name, (int)strlen(Name)+1, 0);
+					return Command(CMD_SETSCROLL, 0L, w);
+					}
+				}
+			return false;
+		case CMD_ADDROWCOL:
+			if(DoSpShSize(d, this)) DoPlot(o);
+			return true;
+		case CMD_COL_MOUSE:
+			if(o && cButtons && rButtons && (mev = (MouseEvent*)tmpl) && mev->y > o->MenuHeight) {
+				for(i = 0; cButtons[i]; i++){
+					if(bDoColWidth) {
+						o->MouseCursor(MC_COLWIDTH, false);
+						}
+					else if(mev->x > cButtons[i]->rDims.left && mev->x < cButtons[i]->rDims.right+2){
+						if(mev->x > cButtons[i]->rDims.right-4) {
+							switch(mev->Action) {
+							case MOUSE_LBDOWN:
+								CurrCol = i;					line[0].x = line[1].x = mev->x;
+								line[0].y = o->MenuHeight;		line[1].y = currRC.bottom;
+								d->Command(CMD_TOOLMODE, tmpl, o);
+								o->MouseCursor(MC_COLWIDTH, false);
+								return bDoColWidth = true;
+								}
+							o->MouseCursor(MC_COLWIDTH, false);
+							}
+						else o->MouseCursor(MC_ARROW, false);
+						return false;
+						}
+					else o->MouseCursor(MC_ARROW, false);
+					if(mev->Action == MOUSE_MOVE && bDoColWidth && (mev->StateFlags & 0x01)) {
+						rc_line.left = line[0].x - 2;		rc_line.right = line[1].x + 2;
+						rc_line.top = line[0].y - 2;		rc_line.bottom = line[1].y +2;
+						o->UpdateRect(&rc_line, false);
+						if(mev->x < (cButtons[CurrCol]->rDims.left +20))mev->x = cButtons[CurrCol]->rDims.left +20;
+						k = mev->x - cButtons[CurrCol]->rDims.right;
+						for(j = CurrCol; cButtons[j] && cButtons[j]->rDims.left < (currRC.right-k); j++) {
+							cButtons[j]->rDims.right += k;	cButtons[j]->rDims.left += j > CurrCol ? k : 0;
+							cButtons[j]->DoPlot(o);			o->UpdateRect(&cButtons[j]->rDims, false);
+							cButtons[j]->rDims.right -= k;	cButtons[j]->rDims.left -= j > CurrCol ? k : 0;
+							}
+						line[0].x = line[1].x = mev->x;
+						line[0].y = o->MenuHeight;			line[1].y = currRC.bottom;
+						o->ShowLine(line, 2, 0x00a0a0a0);
+						return true;
+						}
+					if(mev->Action == MOUSE_LBUP && bDoColWidth) {
+						bDoColWidth = false;
+						k = line[0].x - cButtons[CurrCol]->rDims.left;
+						if(abs(k - cButtons[CurrCol]->rDims.right + cButtons[CurrCol]->rDims.left)>3) {
+							rlp_strcpy(TmpTxt, 40, mkRangeRef(0, CurrCol + ssOrg.x, 0, CurrCol + ssOrg.x));
+							d->ri = new RangeInfo(1, TmpTxt, d->ri);
+							d->ri->SetWidth(k >=20 ? k : 20);
+							}
+						d->Command(CMD_REDRAW, 0L, o);
+						return true;
+						}
+					else bDoColWidth = false;
+					}
+				}
+			else o->MouseCursor(MC_ARROW, false);
+			return false;
+		case CMD_MOUSE_EVENT:
+			if(!(mev =(MouseEvent*)tmpl)) return false;
+			if(bDoColWidth)return Command(CMD_COL_MOUSE, tmpl, o);
+			p1.x = mev->x;		p1.y = mev->y;
+			if(mev->y < o->MenuHeight) o->MouseCursor(MC_ARROW, false);
+			else if(o && cButtons && rButtons) {
+				if(mev->x < d->ri->GetFirstWidth() && mev->y < (o->MenuHeight + d->ri->GetHeight(-1)) && aButton) {
+					aButton->Command(cmd, tmpl, o);
+					}
+				else if(mev->x < d->ri->GetFirstWidth() && rButtons) {
+					if(!(d->mpos2dpos(&p1, &p2, false)))return false;
+					p2.x -= ssOrg.x;		p2.y -= ssOrg.y;
+					if (p2.y < 0 || p2.y >= (d->r_disp-1)) return false;
+					if(rButtons[p2.y]) rButtons[p2.y]->Command(cmd, tmpl, o);
+					}
+				else if(mev->y < (o->MenuHeight + d->ri->GetHeight(-1)) && cButtons) {
+					if(!(d->mpos2dpos(&p1, &p2, false)))return false;
+					p2.x -= ssOrg.x;		p2.y -= ssOrg.y;
+					if (p2.x < 0 || p2.x >= d->c_disp) return false;
+					if(cButtons[p2.x]) cButtons[p2.x]->Command(cmd, tmpl, o);
+					}
+				else if(o->MrkMode == MRK_SSB_DRAW) o->HideMark();
+				}
+			return	d->Command(cmd, tmpl, o);
+		case CMD_PASTE_TSV:		case CMD_PASTE_CSV:		case CMD_PASTE_SSV:
+			Undo.DataObject(this, w, d, 0L, 0L);
+		case CMD_COPY_SYLK:		case CMD_ADDCHAR:		case CMD_SHIFTUP:
+		case CMD_COPY_TSV:		case CMD_COPY_XML:		case CMD_QUERY_COPY:
+		case CMD_TAB:			case CMD_SHTAB:			case CMD_SHIFTDOWN:
+		case CMD_CURRLEFT:		case CMD_CURRIGHT:		case CMD_CURRUP:
+		case CMD_CURRDOWN:		case CMD_SHIFTRIGHT:		case CMD_POS_FIRST:
+		case CMD_POS_LAST:		case CMD_SHIFTLEFT:		case CMD_DELETE:
+		case CMD_TOOLMODE:		case CMD_FILLRANGE:		case CMD_CUT:
+		case CMD_PASTE_XML:		case CMD_DELROW:		case CMD_INSROW:
+		case CMD_INSCOL:		case CMD_DELCOL:		case CMD_UNDO:
+		case CMD_SHPGUP:		case CMD_SHPGDOWN:		case CMD_HIDEMARK:
+			bDoColWidth = false;	bRet = d->Command(cmd, tmpl, o);
+			if(cmd == CMD_UNDO && is_modified && Undo.isEmpty(0L)) {
+				w->Caption(filename && filename[0]?filename : szRlpData, is_modified = false);
+				}
+			return bRet;
+		case CMD_MENUHEIGHT:
+			if(w) w->MenuHeight = defs.iMenuHeight;
+			bDoColWidth = true;
+		case CMD_REDRAW:
+			Undo.SetDisp(w);					d->Command(cmd, tmpl, o);
+			return true;
+		case CMD_MOUSECURSOR:
+			if(o)o->MouseCursor(MC_ARROW, false);	
+			return true;
+		case CMD_SETSCROLL:
+			HideTextCursor();					if(!(o->ActualSize(&currRC)))return false;
+			k = (currRC.bottom-currRC.top)/d->ri->GetHeight(-1);
+			d->GetSize(&i, &j);			o->MouseCursor(MC_WAIT, true);
+			o->SetScroll(true, 0, j, k, ssOrg.y);	k = (currRC.right-currRC.left)/d->ri->GetWidth(-1);
+			o->SetScroll(false, 0, i, k, ssOrg.x);	DoPlot(o);
+			o->MouseCursor(MC_ARROW, true);
+			return true;
+		case CMD_PAGEUP:		case CMD_PAGEDOWN:
+			k = (currRC.bottom-currRC.top)/d->ri->GetHeight(-1);
+			k = k > 3 ? k-2 : 1;					p1.x = d->ri->GetFirstWidth() + 2;
+			p1.y = d->ri->GetHeight(-1) + 2;
+			if(CurrText){
+				p1.x = CurrText->GetX()+2;	p1.y = CurrText->GetY()+12;
+				}
+			d->GetSize(&i, &j);
+			if(cmd == CMD_PAGEUP) ssOrg.y = ssOrg.y > k ? ssOrg.y-k : 0;
+			else ssOrg.y = ssOrg.y < j-k*2 ? ssOrg.y+k : j > k ? j-k : 0;
+			Command(CMD_SETSCROLL, tmpl, o);
+			CurrText = 0L;		d->Select(&p1);
+			return true;
+		case CMD_SETHPOS:
+			ssOrg.x = *((int*)tmpl) >= 0 ? *((long*)tmpl) : 0L;
+			return Command(CMD_SETSCROLL, tmpl, o);
+		case CMD_SETVPOS:
+			ssOrg.y = *((int*)tmpl) >= 0 ? *((long*)tmpl) : 0L;
+			return Command(CMD_SETSCROLL, tmpl, o);
+		case CMD_SETFOCUS:
+			Undo.SetDisp(w);
+			return true;
+		case CMD_KILLFOCUS:
+			return true;
+		case CMD_TEXTSIZE:
+			if(tmpl && *((int*)tmpl) != ssText.iSize) {
+				Undo.ValInt(this, &ssText.iSize, UNDO_CONTINUE);
+				ssText.iSize = o->un2iy(defs.GetSize(SIZE_CELLTEXT));
+				}
+			return true;
+		case CMD_CONFIG:
+			if(defs.PropertyDlg()) return Command(CMD_REDRAW, 0L, o);
+			return false;
+		case CMD_NONE:
+			return true;
+		case CMD_PRINT:
+			return PrintData(o);
+			}
+		}
+	return false;
+}
+
+bool
+SpreadWin::ShowGrid(int CellWidth, int CellHeight, int FirstWidth, POINT *cp)
+{
+	int i, c, nr, nc, cx, cw, ac = 1, na = 0;
+	RECT rc;
+	char text[20];
+	TextDEF ButtText;
+	bool redim = bDoColWidth;
+
+	w->HideMark();
+	cpos.x = cp->x;			cpos.y = cp->y;
+	if(d->ri->GetHeight(-1) != CellHeight || d->ri->GetWidth(-1) != CellWidth || d->ri->GetFirstWidth() != FirstWidth) redim = true;
+	if(redim){
+		d->ri->SetHeight(CellHeight);		d->ri->SetDefWidth(CellWidth);		d->ri->SetFirstWidth(FirstWidth);
+		if(cButtons) {
+			for(i = 0; cButtons[i]; i++) if(cButtons[i]) delete(cButtons[i]);
+			free(cButtons);
+			}
+		if(rButtons) {
+			for(i = 0; rButtons[i]; i++) if(rButtons[i]) delete(rButtons[i]);
+			free(rButtons);
+			}
+		if(aButton) delete(aButton);		aButton = 0L;
+		cButtons = rButtons = 0L;
+		}
+	if(!aButton) aButton = new ssButton(this, 0, w->MenuHeight, FirstWidth, CellHeight);
+	memcpy(&ButtText, &ssText, sizeof(TextDEF));
+	ButtText.Align = TXA_HCENTER | TXA_VCENTER;
+	w->GetSize(&rc);
+	if(!cButtons) {
+		c = d->c_disp;		if(c < 40 || c > 200) d->c_disp = c = 40;
+		cButtons = (ssButton **)calloc(c, sizeof(ssButton*));
+		for(i = 0, cx = FirstWidth; i < (c-1); i++) {
+			cw = d->ri->GetWidth(i+ssOrg.x);
+			cButtons[i] = new ssButton(this, cx, w->MenuHeight, cw+1, CellHeight);
+			cx += cw;
+			}
+		}
+	else {
+		for(i = 0, cx = FirstWidth; cButtons[i]; i++) {
+			cw = d->ri->GetWidth(i+ssOrg.x);
+			cButtons[i]->rDims.left = cx;		cButtons[i]->rDims.right = cx + cw + 1;
+			cx += cw;
+			}
+		}
+	if(!rButtons) {
+ 		c = d->r_disp;			if(c < 60 || c > 300) d->c_disp = c = 60;
+		rButtons = (ssButton**)calloc(c, sizeof(ssButton*));
+		for(i = 0; i < (c-1); i++) rButtons[i] = 
+			new ssButton(this, 0, i*CellHeight+CellHeight+w->MenuHeight, 
+			FirstWidth, CellHeight);
+		}
+	d->GetSize(&nc, &nr);
+	if(rButtons) for(i = 0; rButtons[i]; i++) {
+#ifdef USE_WIN_SECURE
+		sprintf_s(text, 20, "%d", i+1+ssOrg.y);
+#else
+		sprintf(text, "%d", i+1+ssOrg.y);
+#endif
+		if(rButtons[i]) {
+			rButtons[i]->Command(CMD_SETTEXTDEF, &ButtText, w);
+			rButtons[i]->Command(CMD_SETTEXT, text, w);
+			rButtons[i]->Command(CMD_SELECT, (cpos.y == (i+ssOrg.y)) ? &ac : &na, w);
+			}
+		}
+	if(cButtons) for(i = 0; cButtons[i]; i++) {
+		if(cButtons[i]) {
+			cButtons[i]->Command(CMD_SETTEXTDEF, &ButtText, w);
+			cButtons[i]->Command(CMD_SETTEXT, Int2ColLabel(i+ssOrg.x, true), w);
+			cButtons[i]->Command(CMD_SELECT, (cpos.x == (i+ssOrg.x)) ? &ac : &na, w);
+			}
+		}
+	w->SetTextSpec(&ssText);
+	if(aButton) aButton->DoPlot(w);
+	return true;
+}
+
+void
+SpreadWin::MarkButtons(char *rng, POINT *cp)
+{
+	int i, r, c, ncb, nrb, *r_idx, *c_idx;
+	AccRange *mr;
+
+	if(!rButtons || !cButtons) return;
+	for(ncb = 0; cButtons[ncb]; ncb++);		ncb--;
+	for(nrb = 0; rButtons[nrb]; nrb++);		nrb--;
+	if(!nrb || !ncb || !(r_idx=(int*)calloc(nrb,sizeof(int))) || !(c_idx=(int*)calloc(ncb,sizeof(int)))) return;
+	if(rng && rng[0] && (mr = new AccRange(rng)) && mr->GetFirst(&c, &r)) {
+		mr->NextCol(&c);
+		do {
+			if((i = c - ssOrg.x) >= 0 && i < ncb) c_idx[i] = 1; 
+			}while(mr->NextCol(&c));
+		mr->GetFirst(&c, &r);			mr->NextRow(&r);
+		do {
+			if((i = r - ssOrg.y) >= 0 && i < nrb) r_idx[i] = 1; 
+			}while(mr->NextRow(&r));
+		delete mr;
+		}
+	for(i = 0; i < ncb; i++) cButtons[i]->Command(CMD_SETSTYLE, &c_idx[i], w); 
+	for(i = 0; i < nrb; i++) rButtons[i]->Command(CMD_SETSTYLE, &r_idx[i], w);
+	if(cp) {
+		c_idx[0] = r_idx[0] = 0;	c_idx[1] = r_idx[1] = 1;
+		r = (cp->y - ssOrg.y);		c = cp->x -ssOrg.x;
+		for(i = 0; i < ncb; i++) cButtons[i]->Command(CMD_SELECT, c == i ? &c_idx[1] : &c_idx[0], w); 
+		for(i = 0; i < nrb; i++) rButtons[i]->Command(CMD_SELECT, r == i ? &r_idx[1] : &r_idx[0], w);
+		}
+	free(r_idx);		free(c_idx);
+}
+
+bool
+SpreadWin::PrintData(anyOutput *o)
+{
+	int i, j, k, l, pgw, pfw, pcw, pch, rpp, cpp, nc, nr, ix, iy, cpages;
+	int row, col, width, height, ipad, mode;
+	double scale;
+	RECT rc, margin, margin_first;
+	POINT pp_pos, ss_pos, grid[3];
+	LineDEF Line1, Line2;
+	TextDEF td, tdp;
+	bool bContinue;
+	time_t ti = time(0L);
+	anyResult res;
+
+	Line1.patlength = Line2.patlength = 1.0;
+	Line1.color = Line2.color = 0x00808080;		//gray gridlines
+	Line1.pattern = Line2.pattern = 0x0;		//solid lines
+	switch(defs.cUnits) {
+	case 1:		Line1.width = 0.01;			break;
+	case 2:		Line1.width = 0.003937;		break;
+	default:	Line1.width = 0.1;			break;
+		}
+#ifdef _WINDOWS
+	scale = 1.0/_SQRT2;
+#else
+	scale = 0.9;
+#endif
+	Line2.width = Line1.width * 3.0;
+	d->GetSize(&nc, &nr);
+	if(!(o->StartPage())) return false;
+	pfw = iround(o->hres * ((double)d->ri->GetFirstWidth())/w->hres * scale);
+	pcw = iround(o->hres * ((double)d->ri->GetWidth(-1))/w->hres * scale);
+	pch = iround((o->vres * ((double)d->ri->GetHeight(-1))/w->vres) * scale + o->vres/20.0);
+	o->ActualSize(&rc);
+	tdp.ColTxt = 0x0;	tdp.ColBg = 0x00ffffffL; 
+	tdp.fSize = tdp.RotBL = tdp.RotCHAR = 0.0;	tdp.Align = TXA_HRIGHT | TXA_VCENTER;
+	tdp.Mode = TXM_TRANSPARENT;
+	tdp.Style = TXS_NORMAL;				tdp.Font = FONT_HELVETICA;	tdp.text = 0L;
+	memcpy(&td, &ssText, sizeof(TextDEF));	
+	td.Align = TXA_HCENTER | TXA_VCENTER;
+	td.Style = TXS_NORMAL;	td.iSize = 0;
+#ifdef _WINDOWS
+	tdp.iSize = iround(o->hres/6.0);
+	td.fSize = defs.GetSize(SIZE_CELLTEXT);
+#else
+	tdp.iSize = iround(o->hres/7.5);
+	td.fSize = defs.GetSize(SIZE_CELLTEXT)*.8;
+#endif
+	margin.left = iround(o->hres);	margin.right = iround(o->hres/2.0);
+	margin.top = margin.bottom = iround(o->hres);
+	memcpy(&margin_first, &margin, sizeof(RECT));
+	pp_pos.x = margin.left;		pp_pos.y = margin.top;
+	ss_pos.x = 0;				ss_pos.y = 0;		cpages = 1;
+	rpp = (rc.bottom - margin.top - margin.bottom - pch)/pch;
+	mode = 0;
+	if((nr * pch) < ((rc.bottom - rc.top - margin.top - margin.bottom)>>1)) mode = 1;
+	do {
+		k = (rc.right - margin.left - margin.right - pfw);
+		for(i = pgw = 0; pgw < k && (i+ss_pos.x) < nc; i++ ){
+			pgw += (pcw = iround(o->hres * ((double)d->ri->GetWidth(i+ss_pos.x))/w->hres * scale));
+			if(i >=(nc-1)) break;
+			}
+		if(pgw < k) {
+			cpp = i+1;
+			if(!mode && !ss_pos.x && pgw < (k>>1)) mode = 2;
+			}
+		else {
+			cpp = i-1;		pgw -= pcw;
+			}
+		mode = mode;
+		pp_pos.x = margin.left;		pp_pos.y = margin.top;
+		k = (ss_pos.x + cpp) > nc ? nc-ss_pos.x : cpp;
+		l = (ss_pos.y + rpp) > nr ? nr-ss_pos.y : rpp;
+		grid[0].y = margin.top +pch; grid[1].y = grid[0].y + l * pch;
+		o->SetLine(&Line2);
+		grid[0].x = grid[1].x = pp_pos.x;		o->oSolidLine(grid);
+		grid[0].x = grid[1].x = pp_pos.x+pfw;	o->oSolidLine(grid);
+		grid[0].x = margin.left+pfw;	grid[1].x = grid[0].x + pgw;
+		o->SetLine(&Line2);
+		grid[0].y = grid[1].y = pp_pos.y;		o->oSolidLine(grid);
+		grid[0].y = grid[1].y = pp_pos.y+pch;	o->oSolidLine(grid);
+		o->SetLine(&Line2);
+		td.Align = TXA_HCENTER | TXA_VCENTER;	o->SetTextSpec(&td);
+		grid[0].y = margin.top;	 grid[1].y = grid[0].y + pch;
+		iy = margin.top + (pch >>1);	grid[0].x = grid[1].x = pp_pos.x + pfw;
+		for(i = 0, ix = pp_pos.x + pfw; i <= k; i++) {			//column headers
+			pcw = iround(o->hres * ((double)d->ri->GetWidth(i+ss_pos.x))/w->hres * scale);
+			o->oSolidLine(grid);		grid[0].x = grid[1].x = ix + pcw;
+			if(i < k) o->oTextOut(ix + (pcw >>1), iy, Int2ColLabel(i+ss_pos.x, true), 0);
+			ix += pcw;
+			}
+		td.Align = TXA_HRIGHT | TXA_VCENTER;	
+		o->SetTextSpec(&td);	ix = margin.left + pfw - iround(o->hres/20.0);
+		grid[0].x = margin.left;		grid[1].x = grid[0].x + pfw;
+		for(i = 0; i <= l; i++) {			//row labels
+			grid[0].y = grid[1].y = pp_pos.y + pch + i * pch;
+			o->oSolidLine(grid);		iy = grid[0].y + (pch >>1);
+#ifdef USE_WIN_SECURE
+			sprintf_s(TmpTxt, TMP_TXT_SIZE, "%d", i+1+ss_pos.y);
+#else
+			sprintf(TmpTxt, "%d", i+1+ss_pos.y);
+#endif
+			if(i < l) o->oTextOut(ix, iy, TmpTxt, 0);
+			}
+		ipad = iround(o->hres/30.0);
+		grid[0].x = margin.left+pfw + ipad;
+		for(i = 0; i < k; i++, grid[0].x += pcw) {			//spreadsheet data
+			pcw = iround(o->hres * ((double)d->ri->GetWidth(i+ss_pos.x))/w->hres * scale);
+			for (j = 0; j < l; j++) {
+				row = j+ss_pos.y;		col = i+ss_pos.x;
+				if(row >= 0 && row < d->cRows && col >= 0  && col < d->cCols && d->etRows[row][col]) {
+					d->etRows[row][col]->GetResult(&res, false);
+					TranslateResult(&res);
+					td.Align = TXA_HLEFT | TXA_VCENTER;
+					ix = grid[0].x;
+					switch (res.type) {
+					case ET_VALUE:
+						ix = ix + pcw - ( ipad<<1 );
+						td.Align = TXA_HRIGHT | TXA_VCENTER;
+						rlp_strcpy(TmpTxt, TMP_TXT_SIZE, res.text);
+						fit_num_rect(o, pcw - ipad, TmpTxt);
+						Int2Nat(TmpTxt);			break;
+					case ET_BOOL:	case ET_DATE:	case ET_TIME:	case ET_DATETIME:
+						ix = ix + pcw - ( ipad<<1 );
+						td.Align = TXA_HRIGHT | TXA_VCENTER;
+					case ET_TEXT:	case ET_UNKNOWN:
+						if(res.text&& strlen(res.text) < 40) 
+							rlp_strcpy(TmpTxt, TMP_TXT_SIZE, res.text[0] != '\'' ? res.text : res.text +1);
+						else if(res.text) rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "#SIZE");
+						else TmpTxt[0] = 0;	
+						do {
+							o->oGetTextExtent(TmpTxt, (int)strlen(TmpTxt), &width, &height);
+							if(width > (pcw + iround(o->hres/20.0))) TmpTxt[strlen(TmpTxt)-1] = 0;
+							}while(width > (pcw + ipad));
+						break;
+					case ET_ERROR:
+						rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "#ERROR");	break;
+					default:
+						rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "#VALUE");	break;
+						}
+					iy = pp_pos.y + pch + (pch>>1) + j * pch;
+					o->SetTextSpec(&td);			o->oTextOut(ix, iy, TmpTxt, 0);
+					grid[0].y = grid[1].y = iy+(pch>>1);	grid[2].y = grid[1].y - pch;
+					grid[0].x -= ipad;		grid[1].x = grid[2].x = grid[0].x + pcw;
+					o->SetLine(&Line1);		o->oPolyline(grid, 3, 0L);
+					grid[0].x += ipad;
+					}
+				}
+			}
+		//prepare for next table
+		ss_pos.x += k;			bContinue = false;
+		if(ss_pos.x >= nc) {ss_pos.x = 0; ss_pos.y += l; }
+		if(ss_pos.y < nr) {
+			ix = pfw + pgw + iround(o->hres/3.5);
+			iy = (l+2)*pch;
+			if(mode == 2 && (margin.left + ix + pfw + pgw) < (rc.right-margin.right)) {
+				margin.left += pfw + k*pcw + iround(o->hres/3.5);
+				bContinue = true;
+				}
+			else if(mode == 1 && (margin.top + iy + pch + l*pch) < (rc.bottom - margin.bottom)) {
+				margin.top += iy;	margin.left = margin_first.left;
+				bContinue = true;
+				}
+			else {
+				tdp.Align = TXA_HRIGHT | TXA_VCENTER; o->SetTextSpec(&tdp);
+#ifdef USE_WIN_SECURE
+				sprintf_s(TmpTxt, TMP_TXT_SIZE, "page %d", cpages++);
+#else
+				sprintf(TmpTxt, "page %d", cpages++);
+#endif
+				o->oTextOut(rc.right-margin.right, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
+				tdp.Align = TXA_HCENTER | TXA_VCENTER; o->SetTextSpec(&tdp);
+#ifdef USE_WIN_SECURE
+				ctime_s(TmpTxt, 50, &ti);	TmpTxt[24] = 0;
+#else
+				sprintf(TmpTxt, "%s", ctime(&ti));	TmpTxt[24] = 0;
+#endif
+				o->oTextOut((rc.right-rc.left)>>1, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
+				tdp.Align = TXA_HLEFT | TXA_VCENTER; o->SetTextSpec(&tdp);
+				i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "RLPlot ");
+				rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, SZ_VERSION);
+				o->oTextOut(margin_first.left, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
+				memcpy(&margin, &margin_first, sizeof(RECT));
+				bContinue = true;
+				o->Eject();
+				}
+			}
+		}while(bContinue);
+	tdp.Align = TXA_HRIGHT | TXA_VCENTER; o->SetTextSpec(&tdp);
+#ifdef USE_WIN_SECURE
+	sprintf_s(TmpTxt, TMP_TXT_SIZE, "page %d", cpages++);
+#else
+	sprintf(TmpTxt, "page %d", cpages++);
+#endif
+	o->oTextOut(rc.right-margin.right, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
+	tdp.Align = TXA_HCENTER | TXA_VCENTER; o->SetTextSpec(&tdp);
+#ifdef USE_WIN_SECURE
+	ctime_s(TmpTxt, 50, &ti);	TmpTxt[24] = 0;
+#else
+	sprintf(TmpTxt, "%s", ctime(&ti));	TmpTxt[24] = 0;
+#endif
+	o->oTextOut((rc.right-rc.left)>>1, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
+	tdp.Align = TXA_HLEFT | TXA_VCENTER; o->SetTextSpec(&tdp);
+	i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "RLPlot ");
+	rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, SZ_VERSION);
+	o->oTextOut(margin_first.left, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
+	o->EndPage();
+	return true;
+}
+
+void 
+SpreadWin::WriteGraphXML(unsigned char **ptr, long *cbd)
+{
+	unsigned char *pg;
+	long cb = 0, size = 0, newsize;
+	int i;
+
+	for(i = 0; i < NumGraphs; i++) if(g[i]) {
+		if((pg = (unsigned char*)GraphToMem(g[i], &cb)) && cb) {
+			newsize = *cbd + cb + 100;
+			*ptr = (unsigned char*)realloc(*ptr, newsize);
+			*cbd += rlp_strcpy((char*)(*ptr)+*cbd, 20, "<Graph><![CDATA[\n");
+			memcpy(*ptr+ *cbd, pg, cb);		*cbd += cb;
+			*cbd += rlp_strcpy((char*)(*ptr)+*cbd, 20, "]]>\n</Graph>\n");
+			if(pg) free(pg);		pg = 0L;			cb = 0;
+			}
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// This data object is a spreadsheet
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class SpreadData:public DataObj{
+typedef struct _pos_info {
+	POINT ssOrg, currpos;
+	void *CurrText;};
+
+public:
+	SpreadData(GraphObj *par);
+	~SpreadData();
+	bool Init(int nRows, int nCols);
+	bool mpos2dpos(POINT *mp, POINT *dp, bool can_scroll);
+	bool Select(POINT *p);
+	void MarkRange(char *range, POINT *cp = 0L);
+	void HideMark(bool cclp);
+	bool WriteData(char *FileName);
+	bool AddCols(int nCols);
+	bool AddRows(int nRows);
+	bool ChangeSize(int nCols, int nRows, bool bUndo);
+	bool DeleteCols();
+	bool InsertCols();
+	bool DeleteRows();
+	bool InsertRows();
+	void DoPlot(anyOutput *o);
+	bool DelRange();
+	bool PasteRange(int cmd, char *txt);
+	bool InitCopy(int cmd, void *tmpl, anyOutput *o);
+	bool SavePos();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool ReadData(char *FileName, unsigned char *buffer, int type);
+
+	bool ReadXML(char *file, unsigned char *buffer, int type, DWORD undo_flags = 0L);
+	bool ReadTSV(char *file, unsigned char *buffer, int type);
+	bool MemList(unsigned char **ptr, int type);
+
+private:
+	int CellHeight, CellWidth, FirstWidth, mrk_offs;
+	RECT cp_src_rec;			//bounding rectangle for copy range
+	bool bActive, new_mark, bCopyCut, bUpdate, isRowMark, isColMark;
+	POINT currpos, currpos2;
+	anyOutput *w;
+	SpreadWin *Disp;
+	GraphObj *g_parent;
+	EditText *et_racc;
+	char *m_range, *c_range;	//mark and copy ranges
+	char *err_msg, *last_err;	//error message
+	char *rlw_file;				//use this name for save
+	_pos_info pos_info;			//save position settings
+};
+
+SpreadData::SpreadData(GraphObj *par)
+{
+	Disp = 0L;	m_range = 0L;	c_range = 0L;	w = 0L;		err_msg=last_err=rlw_file =  0L;
+	g_parent = par;		CellWidth = 76;		CellHeight = 19;	FirstWidth = 32;
+	et_racc = 0L;	currpos.x = currpos.y = mrk_offs = 0;
+	r_disp = 60;	c_disp = 40;
+	bActive = bCopyCut = bUpdate = isRowMark = isColMark = false;
+	cp_src_rec.left = cp_src_rec.right = cp_src_rec.top = cp_src_rec.bottom = 0;
+	if(defs.IniFile && FileExist(defs.IniFile)) {
+		OpenGraph(0L, defs.IniFile, 0L, false);
+		}
+	pos_info.currpos.x = currpos.x;		pos_info.currpos.y = currpos.y;
+	pos_info.CurrText = CurrText;
+}
+
+SpreadData::~SpreadData()
+{
+	FlushData();
+	if(Disp) delete Disp;			Disp = 0L;
+	if(m_range) free(m_range);		m_range = 0L;
+	if(c_range) free(c_range);		c_range = 0L;
+	if(rlw_file) free(rlw_file);	rlw_file = 0L;
+}
+
+bool
+SpreadData::Init(int nRows, int nCols)
+{
+	int i, j;
+	RECT rc;
+	RangeInfo *o_ri;
+
+	cRows = nRows;	cCols = nCols;				currpos.x = currpos.y = 0;
+	new_mark = bCopyCut = false;				if(w && m_range) HideMark(true);
+	bUpdate = true;
+	while(ri->Type()) {
+		o_ri = ri;		ri = ri->Next();		delete(o_ri);
+		}
+	if(!Disp) {
+		Disp = new SpreadWin(g_parent, this);
+		w = Disp->w;
+		if(w) {
+			CellWidth = w->un2ix(defs.GetSize(SIZE_CELLWIDTH));
+#ifdef _WINDOWS
+			CellHeight = w->un2iy(defs.GetSize(SIZE_CELLTEXT)/defs.ss_txt) + 2;
+#else
+			CellHeight = w->un2iy(defs.GetSize(SIZE_CELLTEXT)/(defs.ss_txt *0.7)) + 2;
+#endif
+			if(CellHeight < 12)CellHeight = 19;		if(CellWidth < 40)CellWidth = 76;
+			FirstWidth = 32;						w->GetSize(&rc);
+			r_disp = (rc.bottom-rc.top)/CellHeight;
+			c_disp = (rc.right-rc.left)/CellWidth+1;
+			}
+		else return false;
+		pos_info.ssOrg.x = Disp->ssOrg.x;		pos_info.ssOrg.y = Disp->ssOrg.y;
+		pos_info.CurrText = CurrText;
+		}
+	if(etRows)FlushData();
+	etRows = (EditText ***)calloc (cRows, sizeof(EditText **));
+	if(etRows) for(i = 0; i < cRows; i++) {
+		etRows[i] = (EditText **)calloc(cCols, sizeof(EditText *));
+		if(etRows[i]) for(j = 0; j < cCols; j++) {
+#ifdef _DEBUG
+			char text[20];
+#ifdef USE_WIN_SECURE
+			sprintf_s (text, 20, "%.2f", i*10.0 + j);
+#else
+			sprintf (text, "%.2f", i*10.0 + j);
+#endif
+			etRows[i][j] = new EditText(this, text, i, j);
+#else
+			etRows[i][j] = new EditText(this, 0L, i, j);
+#endif
+			}
+		}
+	if (LoadFile) {
+		rlp_strcpy(TmpTxt, TMP_TXT_SIZE, LoadFile);	//we will reenter by recursion !
+		free(LoadFile);
+		LoadFile = 0L;
+		Disp->Command(CMD_DROPFILE, TmpTxt, w);
+		}
+	else DoPlot(w);
+	return true;
+}
+
+bool
+SpreadData::mpos2dpos(POINT *mp, POINT *dp, bool can_scroll)
+{
+	int mx;
+
+	CurrGO = 0L;
+	dp->y = (mp->y - w->MenuHeight - CellHeight)/CellHeight + Disp->ssOrg.y;
+	dp->x = Disp->ssOrg.x;
+	for(mx = mp->x - ri->GetFirstWidth() - ri->GetWidth(dp->x); mx > 0; dp->x++, mx -= ri->GetWidth(dp->x));
+	if(can_scroll) {
+		if(dp->x < cCols && mp->x < (ri->GetFirstWidth()+10) && mp->x > ri->GetFirstWidth() && Disp->ssOrg.x >0) {
+			Disp->ssOrg.x -= 1;
+			if(CurrText) CurrText->Update(2, w, mp);		CurrText = 0L;
+			Disp->Command(CMD_SETSCROLL, 0L, w);
+			}
+		if(mp->y < (w->MenuHeight + CellHeight+9) && mp->y > (w->MenuHeight + CellHeight) && Disp->ssOrg.y >0) {
+			Disp->ssOrg.y -= 1;
+			if(CurrText) CurrText->Update(2, w, mp);		CurrText = 0L;
+			Disp->Command(CMD_SETSCROLL, 0L, w);
+			}
+		if(mp->x > (Disp->currRC.right-w->MenuHeight-10) && Disp->ssOrg.x < (cCols-2)) {
+			Disp->ssOrg.x += 1;
+			if(CurrText) CurrText->Update(2, w, mp);		CurrText = 0L;
+			Disp->Command(CMD_SETSCROLL, 0L, w);
+			}
+		if(mp->y > (Disp->currRC.bottom-10) && Disp->ssOrg.y < (cRows-5)) {
+			Disp->ssOrg.y += 1;
+			do {
+				mp->y -= CellHeight;
+			} while(mp->y > (Disp->currRC.bottom-CellHeight));
+			if(CurrText) CurrText->Update(2, w, mp);		CurrText = 0L;
+			Disp->Command(CMD_SETSCROLL, 0L, w);			CurrText = 0L;
+			}
+		}
+	if(dp->y >= cRows) dp->y = cRows-1;					if(dp->x >= cCols) dp->x = cCols-1;
+	return true;
+}
+
+bool
+SpreadData::Select(POINT *p)
+{
+	if(CurrText && CurrText->isInRect(p)){
+		CurrText->Update(1, w, p);
+		Disp->Command(CMD_CURRPOS, &currpos, w);
+		if(CurrText->isFormula() && CurrText->hasMark()) et_racc = CurrText;
+		return true;
+		}
+	if(!(mpos2dpos(p, &currpos, false)))return false;
+	if(currpos.y < cRows && currpos.x < cCols && currpos.y >= 0 && currpos.x >= 0) {
+		if(etRows[currpos.y][currpos.x]) {
+			if(etRows[currpos.y][currpos.x] == CurrText) CurrText->Update(1, w, p);
+			else {
+				if(CurrText) CurrText->Update(2, w, p);
+				CurrText = etRows[currpos.y][currpos.x];
+				DoPlot(w);
+				CurrText->Update(1, w, p);
+				}
+			Disp->Command(CMD_CURRPOS, &currpos, w);
+			return true;
+			}
+		}
+	if(CurrText) CurrText->Update(2, w, p);		CurrText = 0L;
+	return false;
+}
+
+void
+SpreadData::MarkRange(char *range, POINT *cp)
+{
+	AccRange *nr, *oldr;
+	int r, c, cb;
+
+	oldr = nr = 0L;
+	if(m_range && range && !strcmp(m_range, range)) return;		//no change
+	if(m_range) oldr = new AccRange(m_range);
+	if(range) nr = new AccRange(range);
+	if(oldr && nr && oldr->GetFirst(&c, &r)) {
+		for( ; oldr->GetNext(&c, &r); ) {
+			if(r >= Disp->ssOrg.y && r < (r_disp +Disp->ssOrg.y) && r < cRows && c >= Disp->ssOrg.x 
+				&& c < (c_disp + Disp->ssOrg.x) && c < cCols && !nr->IsInRange(c, r) && etRows[r] && etRows[r][c]){
+				etRows[r][c]->Mark(w, etRows[r][c] != CurrText ? 0 : 1);
+				}
+			}
+		}
+	if(nr && nr->GetFirst(&c, &r)) {
+		for( ; nr->GetNext(&c, &r); ) {
+			if(r >= Disp->ssOrg.y && r < (r_disp +Disp->ssOrg.y) && c >= Disp->ssOrg.x 
+				&& c < (c_disp + Disp->ssOrg.x) && r < cRows && c < cCols)
+				etRows[r][c]->Mark(w, etRows[r][c] != CurrText ? 2 : 3);
+			}
+		}
+	if(range && (m_range = (char*)realloc(m_range, cb = ((int)strlen(range)+6)))) rlp_strcpy(m_range, cb, range);
+	else if (m_range) m_range[0] = 0;
+	if(oldr) delete(oldr);	if(nr) delete(nr);
+	new_mark = true;		Disp->MarkButtons(m_range, cp);
+}
+
+void
+SpreadData::HideMark(bool cclp)
+{
+	if(cclp && c_range && c_range != m_range){
+		free(c_range);	c_range = 0L;
+		}
+	if(m_range){
+		free(m_range);	m_range = 0L;
+		DoPlot(w);
+		}
+	if(cclp) EmptyClip();		new_mark = false;
+	Disp->MarkButtons(m_range, 0L);
+}
+
+bool
+SpreadData::WriteData(char *FileName)
+{
+	FILE *File;
+	int i, j;
+	unsigned char *buff = 0L;
+	anyResult res;
+	bool bErr = false;
+
+	if(!cRows || !cCols || !etRows) return false;
+	if(!FileName) FileName = rlw_file;
+	if(FileName && FileName[0]) BackupFile(FileName);
+	else return false;
+#ifdef USE_WIN_SECURE
+	if(fopen_s(&File, FileName, "w")) {
+#else
+	if(!(File = fopen(FileName, "w"))) {
+#endif
+		ErrorBox("An error occured during write,\n\nplease try again!");
+		return false;
+		}
+	HideMark(true);
+	i = (int)strlen(FileName);
+	//test for xml extension
+	if(!strcmp(".xml", FileName+i-4) || !strcmp(".XML", FileName+i-4)) {
+		MemList(&buff, FF_XML);
+		if(buff){
+			if(fprintf(File, "%s", buff) <= 0) bErr = true;
+			free(buff);					fclose(File);
+			return true;
+			}
+		return false;
+		}
+	//test for tsv extension
+	if(!strcmp(".tsv", FileName+i-4) || !strcmp(".TSV", FileName+i-4)) {
+		MemList(&buff, FF_TSV);
+		if(buff){
+			if(fprintf(File, "%s", buff) <= 0) bErr = true;
+			free(buff);					fclose(File);
+			return true;
+			}
+		return false;
+		}
+	//test for rlw extension
+	if(!strcmp(".rlw", FileName+i-4) || !strcmp(".RLW", FileName+i-4)) {
+		MemList(&buff, FF_RLW);
+		if(buff){
+			if(rlw_file != FileName) {
+				if(rlw_file) free(rlw_file);
+				rlw_file = (char*)memdup(FileName, (int)strlen(FileName)+1, 0);
+				}
+			if(fprintf(File, "%s", buff) <= 0) bErr = true;
+			free(buff);					fclose(File);
+			return true;
+			}
+		return false;
+		}
+	//test for slk extension
+	if(!strcmp(".slk", FileName+i-4) || !strcmp(".SLK", FileName+i-4)) {
+		MemList(&buff, FF_SYLK);
+		if(buff){
+			if(fprintf(File, "%s", buff) <= 0) bErr = true;
+			free(buff);					fclose(File);
+			return true;
+			}
+		return false;
+		}
+	//else write csv
+	for(i = 0; i < cRows; i++) {
+		for(j = 0; j < cCols; j++) {
+			if(etRows[i][j]) {
+				etRows[i][j]->GetResult(&res, false);		TranslateResult(&res);
+				switch(res.type) {
+				case ET_TEXT:
+					fprintf(File, "\"%s\"", res.text);
+					break;
+				case ET_VALUE:		case ET_DATE:	case ET_TIME:
+				case ET_DATETIME:	case ET_BOOL:
+					fprintf(File, "%s", res.text);
+					break;
+					}
+				}
+			if(j < (cCols-1)) fprintf(File, ", ");
+			}
+		if(fprintf(File, "\n") <= 0) bErr = true;
+		}
+	fclose(File);
+	if(bErr) ErrorBox("An error occured during write,\n\nplease try again!");
+	return true;
+}
+
+bool
+SpreadData::ReadData(char *FileName, unsigned char *buffer, int type)
+{
+	int i, j;
+	char ItemText[20];
+	bool success;
+
+	if(FileName) {		//read disk file
+		if(!Disp->Command(CMD_CAN_CLOSE, 0L, 0L))return false;
+		if(0 == strcmp(".xml", FileName+strlen(FileName)-4) ||
+			0 == strcmp(".XML", FileName+strlen(FileName)-4) ||
+			0 == strcmp(".rlw", FileName+strlen(FileName)-4) ||
+			0 == strcmp(".RLW", FileName+strlen(FileName)-4) ||
+			IsXmlFile(FileName)){
+			Disp->Command(CMD_DELGRAPH, 0L, w);
+			if(ReadXML(FileName, buffer, type, 0L)){
+				if(0 == strcmp(".rlw", FileName+strlen(FileName)-4) ||
+					0 == strcmp(".RLW", FileName+strlen(FileName)-4)) {
+					if(rlw_file) free(rlw_file);
+					rlw_file = (char*)memdup(FileName, (int)strlen(FileName)+1, 0);
+					}
+				return true;
+				}
+			return false;
+			}
+		if(0 == strcmp(".tsv", FileName+strlen(FileName)-4) ||
+			0 == strcmp(".TSV", FileName+strlen(FileName)-4)){
+			return ReadTSV(FileName, buffer, type);
+			}
+		if(!(Cache = new ReadCache())) return false;
+		if(! Cache->Open(FileName)) {
+			delete Cache;
+			i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE, "Error open data file\n\"");
+			i += rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, FileName);
+			i += rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, "\"\n");
+			ErrorBox(TmpTxt);
+			return false;
+			}
+		if(!Init(1, 1)) goto ReadError;
+		}
+	else if(buffer) {	//read memory buffer
+		switch(type) {
+		case FF_TSV:
+			return ReadTSV(FileName, buffer, type);
+		case FF_XML:
+			return ReadXML(FileName, buffer, type, 0L);
+		case FF_CSV:
+			if(!(Cache = new MemCache(buffer))) return false;
+			break;
+		default:
+			ErrorBox("Read from buffer with\nunknown format failed.");
+			return false;
+			}
+		}
+	else return false;
+	i = j = 0;
+	do {
+		if((success = GetItemCSV(ItemText, sizeof(ItemText)-1)) || ItemText[0]){
+			if(j >= cCols && !AddCols(j+1)) goto ReadError;
+			if(i >= cRows && !AddRows(i+1)) goto ReadError;
+			if(!etRows[i][j]) goto ReadError;
+			if(ItemText[0] == '"') {
+				rmquot(ItemText);
+				etRows[i][j]->SetText(ItemText);
+				etRows[i][j]->Update(20, 0L, 0L);
+				}
+			else if(ItemText[0]){
+				etRows[i][j]->SetText(ItemText);
+				etRows[i][j]->Update(10, 0L, 0L);
+				}
+			else etRows[i][j]->SetText("");
+			j++;
+			}
+		if(!success && !Cache->IsEOF()) {i++; j = 0;}	//eol
+		}while (ItemText[0] || !Cache->IsEOF());		//eof
+	Cache->Close();		delete Cache;		Cache = 0L;
+	Disp->ssOrg.x = Disp->ssOrg.y = 0;
+	bUpdate = true;
+	return true;
+
+ReadError:
+	Cache->Close();		delete Cache;		Cache = 0L;
+	return false;
+
+}
+
+bool
+SpreadData::AddCols(int nCols)
+{
+	EditText **NewRow;
+	int i, j;
+
+	if (nCols <= cCols || !etRows || !etRows[0] || !etRows[0][cCols-1]) return false;
+	for(i = 0; i < cRows; i++) {
+		if(NewRow = (EditText **)realloc(etRows[i], nCols * sizeof(EditText*))){
+			for(j = cCols; j < nCols; j++) {
+				NewRow[j] = new EditText(this, 0L, i, j);
+				}
+			etRows[i] = NewRow;
+			}
+		else return false;						//memory allocation error
+		}
+	cCols = nCols;
+	return true;
+}
+
+bool
+SpreadData::AddRows(int nRows)
+{
+	int i, j;
+	EditText ***NewRows;
+
+	if (nRows <= cRows || !etRows || !etRows[cRows-1] || !etRows[cRows-1][0] ) return false;
+	NewRows = (EditText ***)realloc(etRows, nRows * sizeof(EditText **));
+	if(NewRows) etRows = NewRows;
+	else return false;					//memory allocation error
+	for(i = cRows; i < nRows; i++){
+		etRows[i] = (EditText **)calloc(cCols, sizeof(EditText *));
+		if(etRows[i]) {
+			for(j = 0; j < cCols; j++) etRows[i][j] = new EditText(this, 0L, i, j);
+			}
+		else {							//memory allocation error
+			cRows = i-1;
+			return false;
+			}
+		}
+	cRows = nRows;
+	return true;
+}
+
+bool
+SpreadData::ChangeSize(int nCols, int nRows, bool bUndo)
+{
+	int i, j;
+	bool RetVal = true;
+	
+	if(nCols == cCols && nRows == cRows) return true;
+	if(c_range){
+		free(c_range);	c_range = 0L;	EmptyClip();
+		}
+	if(bUndo) Undo.DataObject(Disp, w, this, 0L, 0L);
+	if(nRows && nRows < cRows) {
+		for (i = nRows; i < cRows; i++) {
+			if(etRows[i]) for (j = 0; j < cCols; j++) {
+				if(etRows[i][j]) delete etRows[i][j];
+				etRows[i][j] = NULL;
+				}
+			free(etRows[i]);
+			etRows[i] = NULL;
+			}
+		cRows = nRows;
+		}
+	if(nCols && nCols < cCols) {
+		for (i = 0; i < cRows; i++) {
+			for (j = nCols; j < cCols; j++) {
+				if(etRows[i][j]) delete etRows[i][j];
+				etRows[i][j] = NULL;
+				}
+			}
+		cCols = nCols;
+		}
+	if(nCols > cCols) if(!AddCols(nCols))RetVal = false;
+	if(RetVal && nRows > cRows) if(!AddRows(nRows))RetVal = false;
+	if(w) {
+#ifdef USE_WIN_SECURE
+		sprintf_s(TmpTxt, TMP_TXT_SIZE, "%d00", nRows);
+#else
+		sprintf(TmpTxt, "%d00", nRows);
+#endif
+		w->oGetTextExtent(TmpTxt, 0, &i, &j);
+		if(i > FirstWidth) FirstWidth = i;
+		Disp->Command(CMD_SETSCROLL, 0L, w);
+		}
+	return RetVal;
+}
+
+bool
+SpreadData::DeleteCols()
+{
+	int i, j, r0, c0, c1, c2, cn, dc, nc;
+	RECT rc;
+	lfPOINT *cs;
+
+	AccRange * ar;
+	if(!isColMark || !m_range || !m_range[0]){
+		InfoBox("No columns selected!");
+		return false;
+		}
+	if(c_range){
+		free(c_range);	c_range = 0L;	EmptyClip();
+		}
+	Undo.DataObject(Disp, w, this, 0L, 0L);
+	Undo.String(0L, &m_range, UNDO_CONTINUE);
+	if(!(ar = new AccRange(m_range))) return false;
+	ar->BoundRec(&rc);
+	if(!(cs = (lfPOINT*)malloc((rc.right-rc.left+2)*sizeof(lfPOINT)))) {
+		delete ar;			return false;
+		}
+	if(!(ar->GetFirst(&c0, &r0))) {
+		free(cs);			delete ar;			return false;
+		}
+	ar->NextCol(&c1);		cn = c1;			nc = 0;
+	Disp->Command(CMD_MRK_DIRTY, 0L, 0L);
+	do {
+		for(c0 = c1 = c2 = cn; ar->NextCol(&c2); ) {
+			if(c2 > c1+1 || c2 < c0) break;
+			c1 = c2;
+			}
+		dc = c1-c0+1;		cn = c2;
+		cs[nc].fx = c0;		cs[nc].fy = c1;		nc++;
+		}while(c2!=c1);
+	SortFpArray(nc, cs);	nc--;
+	do {
+		c0 = (int)cs[nc].fx;	c1 = (int)cs[nc].fy;	dc = c1-c0+1;
+		for(i = 0; i < cRows; i++) {
+			for(j = c0+dc; j <cCols; j++) {
+				if(etRows && etRows[i] && etRows[i][j] && etRows[i][j-dc]) {
+					switch(etRows[i][j]->type) {
+					default:
+						etRows[i][j-dc]->SetText(etRows[i][j]->text);
+						break;
+					case ET_VALUE:
+						etRows[i][j-dc]->SetText(etRows[i][j]->text);
+						etRows[i][j-dc]->Value = etRows[i][j]->Value; 
+						etRows[i][j-dc]->type = ET_VALUE;
+						break;
+					case ET_FORMULA:
+						MoveFormula(this, etRows[i][j]->text, TmpTxt, TMP_TXT_SIZE, -dc, 0, -1, c0);
+						etRows[i][j-dc]->SetText(TmpTxt);	etRows[i][j]->type = ET_FORMULA;
+						break;
+						}
+					}
+				}
+			}
+		for(i = 0; i < cRows; i++) {
+			if(etRows[i]){
+				for(j = cCols-dc; j < cCols; j++) if(etRows[i][j]) {
+					delete(etRows[i][j]);
+					etRows[i][j] = 0L;
+					}
+				}
+			}
+		cCols -= dc;
+		MoveFormula(this, m_range, TmpTxt, TMP_TXT_SIZE, -dc, 0, -1, c0+1);		free(m_range);
+		m_range = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);	nc--;
+		for(i = 0; i < c0; i++) for(j = 0; j < c0; j++) {
+			if(etRows && etRows[i] && etRows[i][j] && etRows[i][j]->type == ET_FORMULA) {
+				MoveFormula(this, etRows[i][j]->text, TmpTxt, TMP_TXT_SIZE, -dc, 0, -1, c0);
+				etRows[i][j]->SetText(TmpTxt);			etRows[i][j]->type = ET_FORMULA;
+				}
+			}
+		}while(nc >= 0);
+	delete ar;
+	Disp->Command(CMD_SETSCROLL, 0L, w);		Disp->Command(CMD_MRK_DIRTY, 0L, w);
+	return true;
+}
+
+bool
+SpreadData::InsertCols()
+{
+	int i, j, r0, c0, c1, c2, cn, dc, nc;
+	RECT rc;
+	lfPOINT *cs;
+
+	AccRange * ar;
+	if(!isColMark || !m_range || !m_range[0]){
+		InfoBox("No columns selected!");
+		return false;
+		}
+	if(c_range){
+		free(c_range);	c_range = 0L;	EmptyClip();
+		}
+	Undo.DataObject(Disp, w, this, 0L, 0L);
+	Undo.String(0L, &m_range, UNDO_CONTINUE);
+	if(!(ar = new AccRange(m_range))) return false;
+	ar->BoundRec(&rc);
+	if(!(cs = (lfPOINT*)malloc((rc.right-rc.left+2)*sizeof(lfPOINT)))) {
+		delete ar;			return false;
+		}
+	if(!(ar->GetFirst(&c0, &r0))) {
+		free(cs);			delete ar;			return false;
+		}
+	ar->NextCol(&c1);		cn = c1;			nc = 0;
+	do {
+		for(c0 = c1 = c2 = cn; ar->NextCol(&c2); ) {
+			if(c2 > c1+1 || c2 < c0) break;
+			c1 = c2;
+			}
+		dc = c1-c0+1;		cn = c2;
+		cs[nc].fx = c0;		cs[nc].fy = c1;		nc++;
+		}while(c2!=c1);
+	SortFpArray(nc, cs);	nc--;	Disp->Command(CMD_MRK_DIRTY, 0L, w);
+	do {
+		c0 = (int)cs[nc].fx;	c1 = (int)cs[nc].fy;	dc = c1-c0+1;
+		if(AddCols(cCols + dc)) for(i = 0; i < cRows; i++) {
+			for(j = cCols-1; j >= c0+dc; j--) {
+				if(etRows && etRows[i] && etRows[i][j] && etRows[i][j-dc]) {
+					switch(etRows[i][j-dc]->type) {
+					default:
+						etRows[i][j]->SetText(etRows[i][j-dc]->text);
+						break;
+					case ET_VALUE:
+						etRows[i][j]->SetText(etRows[i][j-dc]->text);
+						etRows[i][j]->Value = etRows[i][j-dc]->Value; 
+						etRows[i][j]->type = ET_VALUE;
+						break;
+					case ET_FORMULA:
+						MoveFormula(this, etRows[i][j-dc]->text, TmpTxt, TMP_TXT_SIZE, dc, 0, -1, c0);
+						etRows[i][j]->SetText(TmpTxt);	etRows[i][j]->type = ET_FORMULA;
+						break;
+						}
+					etRows[i][j-dc]->SetText("");
+					}
+				}
+			}
+		MoveFormula(this, m_range, TmpTxt, TMP_TXT_SIZE, dc, 0, -1, c0);		free(m_range);
+		m_range = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);	nc--;
+		for(i = 0; i < c0; i++) for(j = 0; j < c0; j++) {
+			if(etRows && etRows[i] && etRows[i][j] && etRows[i][j]->type == ET_FORMULA) {
+				MoveFormula(this, etRows[i][j]->text, TmpTxt, TMP_TXT_SIZE, dc, 0, -1, c0);
+				etRows[i][j]->SetText(TmpTxt);			etRows[i][j]->type = ET_FORMULA;
+				}
+			}
+		}while(nc >= 0);
+	delete ar;
+	Disp->Command(CMD_SETSCROLL, 0L, w);		Disp->Command(CMD_MRK_DIRTY, 0L, w);
+	return true;
+}
+
+bool 
+SpreadData::DeleteRows()
+{
+	int i, j, r0, c0, r1, r2, rn, dr, nr;
+	AccRange * ar;
+	RECT rc;
+	lfPOINT *rs;
+
+	if(!isRowMark || !m_range || !m_range[0]){
+		InfoBox("No rows selected!");
+		return false;
+		}
+	if(c_range){
+		free(c_range);	c_range = 0L;	EmptyClip();
+		}
+	Undo.DataObject(Disp, w, this, 0L, 0L);
+	Undo.String(0L, &m_range, UNDO_CONTINUE);
+	if(!(ar = new AccRange(m_range))) return false;
+	ar->BoundRec(&rc);
+	if(!(rs = (lfPOINT*)malloc((rc.bottom-rc.top+2)*sizeof(lfPOINT)))) {
+		delete ar;			return false;
+		}
+	if(!(ar->GetFirst(&c0, &r0))) {
+		free(rs);			delete ar;			return false;
+		}
+	ar->NextRow(&r1);		rn = r1;			nr = 0;
+	Disp->Command(CMD_MRK_DIRTY, 0L, w);
+	do {
+		for(r0 = r1 = r2 = rn;ar->NextRow(&r2); ) {
+			if(r2 > r1+1 || r2 < r0) break;
+			r1 = r2;
+			}
+		dr = r1-r0+1;		rn = r2;
+		rs[nr].fx = r0;		rs[nr].fy = r1;		nr++;
+		}while(r2!=r1);
+	SortFpArray(nr, rs);	nr--;
+	do {
+		r0 = (int)rs[nr].fx;	r1 = (int)rs[nr].fy;	dr = r1-r0+1;
+		for(i = r0+dr; i < cRows; i++) {
+			for(j = 0; j < cCols; j++) {
+				if(etRows && etRows[i-dr] && etRows[i-dr][j] && etRows[i] && etRows[i][j]) {
+					switch(etRows[i][j]->type) {
+					default:
+						etRows[i-dr][j]->SetText(etRows[i][j]->text);
+						break;
+					case ET_VALUE:
+						etRows[i-dr][j]->SetText(etRows[i][j]->text);
+						etRows[i-dr][j]->Value = etRows[i][j]->Value; 
+						etRows[i-dr][j]->type = ET_VALUE;
+						break;
+					case ET_FORMULA:
+						MoveFormula(this, etRows[i][j]->text, TmpTxt, TMP_TXT_SIZE, 0, -dr, r0, -1);
+						etRows[i-dr][j]->SetText(TmpTxt);	etRows[i-dr][j]->type = ET_FORMULA;
+						break;
+						}
+					}
+				}
+			}
+		for(i = cRows-1; i >= cRows-dr; i--) {
+			if(etRows[i]){
+				for(j = 0; j < cCols; j++) if(etRows[i][j]) delete(etRows[i][j]);
+				free(etRows[i]);
+				}
+			}
+		cRows -= dr;
+		MoveFormula(this, m_range, TmpTxt, TMP_TXT_SIZE, 0, -dr, r0+1, -1);		free(m_range);
+		m_range = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);	nr--;
+		for(i = 0; i < r0; i++) for(j = 0; j < cCols; j++) {
+			if(etRows && etRows[i] && etRows[i][j] && etRows[i][j]->type == ET_FORMULA) {
+				MoveFormula(this, etRows[i][j]->text, TmpTxt, TMP_TXT_SIZE, 0, -dr, r0, -1);
+				etRows[i][j]->SetText(TmpTxt);			etRows[i][j]->type = ET_FORMULA;
+				}
+			}
+		}while(nr >= 0);
+	delete ar;
+	if(w) {
+		Disp->Command(CMD_SETSCROLL, 0L, w);			Disp->Command(CMD_MRK_DIRTY, 0L, w);
+		}
+	return true;
+}
+
+bool
+SpreadData::InsertRows()
+{
+	int i, j, r0, c0, r1, r2, rn, dr, nr;
+	AccRange * ar;
+	RECT rc;
+	lfPOINT *rs;
+
+	if(!isRowMark || !m_range || !m_range[0]){
+		InfoBox("No rows selected!");
+		return false;
+		}
+	if(c_range){
+		free(c_range);	c_range = 0L;	EmptyClip();
+		}
+	Undo.DataObject(Disp, w, this, 0L, 0L);
+	Undo.String(0L, &m_range, UNDO_CONTINUE);
+	if(!(ar = new AccRange(m_range))) return false;
+	ar->BoundRec(&rc);
+	if(!(rs = (lfPOINT*)malloc((rc.bottom-rc.top+2)*sizeof(lfPOINT)))) {
+		delete ar;			return false;
+		}
+	if(!(ar->GetFirst(&c0, &r0))) {
+		free(rs);			delete ar;			return false;
+		}
+	ar->NextRow(&r1);		rn = r1;			nr = 0;
+	Disp->Command(CMD_MRK_DIRTY, 0L, w);
+	do {
+		for(r0 = r1 = r2 = rn;ar->NextRow(&r2); ) {
+			if(r2 > r1+1 || r2 < r0) break;
+			r1 = r2;
+			}
+		dr = r1-r0+1;		rn = r2;
+		rs[nr].fx = r0;		rs[nr].fy = r1;		nr++;
+		}while(r2!=r1);
+	SortFpArray(nr, rs);	nr--;
+	do {
+		r0 = (int)rs[nr].fx;	r1 = (int)rs[nr].fy;	dr = r1-r0+1;
+		if(AddRows(cRows + dr)) for(i = cRows-1; i >= r0+dr; i--) {
+			for(j = 0; j < cCols; j++) {
+				if(etRows && etRows[i-dr] && etRows[i-dr][j] && etRows[i] && etRows[i][j]) {
+					switch(etRows[i-dr][j]->type) {
+					default:
+						etRows[i][j]->SetText(etRows[i-dr][j]->text);
+						break;
+					case ET_VALUE:
+						etRows[i][j]->SetText(etRows[i-dr][j]->text);
+						etRows[i][j]->Value = etRows[i-dr][j]->Value; 
+						etRows[i][j]->type = ET_VALUE;
+						break;
+					case ET_FORMULA:
+						MoveFormula(this, etRows[i-dr][j]->text, TmpTxt, TMP_TXT_SIZE, 0, dr, r0, -1);
+						etRows[i][j]->SetText(TmpTxt);	etRows[i][j]->type = ET_FORMULA;
+						break;
+						}
+					etRows[i-dr][j]->SetText("");
+					}
+				}
+			}
+		MoveFormula(this, m_range, TmpTxt, TMP_TXT_SIZE, 0, dr, r0, -1);		free(m_range);
+		m_range = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);	nr--;
+		for(i = 0; i < r0; i++) for(j = 0; j < cCols; j++) {
+			if(etRows && etRows[i] && etRows[i][j] && etRows[i][j]->type == ET_FORMULA) {
+				MoveFormula(this, etRows[i][j]->text, TmpTxt, TMP_TXT_SIZE, 0, dr, r0, -1);
+				etRows[i][j]->SetText(TmpTxt);			etRows[i][j]->type = ET_FORMULA;
+				}
+			}
+		}while(nr >= 0);
+	delete ar;
+	if(w) {
+#ifdef USE_WIN_SECURE
+		sprintf_s(TmpTxt, TMP_TXT_SIZE, "%d00", cRows);
+#else
+		sprintf(TmpTxt, "%d00", cRows);
+#endif
+		w->oGetTextExtent(TmpTxt, 0, &i, &j);
+		if(i > FirstWidth) FirstWidth = i;
+		Disp->Command(CMD_SETSCROLL, 0L, w);			Disp->Command(CMD_MRK_DIRTY, 0L, w);
+		}
+	return true;
+}
+
+void
+SpreadData::DoPlot(anyOutput *o)
+{
+	RECT rc;
+	int i, j, r, c;
+	AccRange *ar;
+	double v;
+
+	if(!w || !Disp) return;
+	w->Erase(0x00e8e8e8L);					if(!(w->ActualSize(&rc)))return;
+	w->SetTextSpec(&ssText);				et_racc = 0L;
+	r_disp = (rc.bottom-rc.top)/CellHeight;
+	for(c = Disp->ssOrg.x, j=rc.left, c_disp = 1; c < cCols && j <= (rc.right-rc.left) && c_disp < cCols; c++, c_disp++) {
+		j += ri->GetWidth(c);
+		}
+	if(j >= (rc.right-rc.left))c_disp--;
+	Disp->ShowGrid(CellWidth, CellHeight, FirstWidth, &currpos);
+	rc.top = w->MenuHeight;					rc.bottom = rc.top + CellHeight;
+	LockData(false, false);
+	if(bUpdate) {
+		for(i = cRows-1; i >= 0; i--) {
+			for(j = cCols-1; j >= 0; j--) etRows[i][j]->GetValue(&v);
+			}
+		bUpdate = false;
+		}
+	for(i = 0; i <= (cRows - Disp->ssOrg.y) && i <= r_disp; i++) {
+		rc.left = ri->GetFirstWidth();		rc.right = rc.left + ri->GetWidth(Disp->ssOrg.x);
+		rc.top += CellHeight;				rc.bottom += CellHeight;
+		for(j = 0; j <= (cCols - Disp->ssOrg.x) && j <= c_disp; j++) {
+			r = i + Disp->ssOrg.y;	c = j + Disp->ssOrg.x; 
+			if(r < cRows && r >= 0 && c < cCols && c >=0 && etRows[r][c]){
+				etRows[r][c]->SetRec(&rc);	etRows[r][c]->Update(2, 0L, 0L);
+				etRows[r][c]->Redraw(w, false);
+				}
+			rc.left += ri->GetWidth(Disp->ssOrg.x+j);
+			rc.right += ri->GetWidth(Disp->ssOrg.x+j+1);
+			}
+		}
+	if(CurrText && CurrText->row >= Disp->ssOrg.y && CurrText->row < (Disp->ssOrg.y + r_disp)
+			&& CurrText->col >= Disp->ssOrg.x && CurrText->col < (Disp->ssOrg.x +c_disp))
+			CurrText->Update(1, w, 0L);
+	if(m_range && (ar = new AccRange(m_range)) && ar->GetFirst(&c, &r)) {
+		for( ; ar->GetNext(&c, &r); ) {
+			if(r >= Disp->ssOrg.y && r < (r_disp + Disp->ssOrg.y) && c >= Disp->ssOrg.x 
+				&& r < cRows && c < cCols
+				&& c < (c_disp + Disp->ssOrg.x) && etRows[r] && etRows[r][c])
+				etRows[r][c]->Mark(w, etRows[r][c] != CurrText ? 2 : 3);
+			}
+		delete (ar);
+		}
+	LockData(false, false);
+	if(c_range) InitCopy(0, 0L, w);			//move animated rectangle
+	if(!(w->ActualSize(&rc))) return;		
+	rc.bottom += CellHeight;		w->UpdateRect(&rc, false);
+	if(err_msg && (!last_err || strcmp(err_msg,last_err))) {
+		ErrorBox(last_err = err_msg);
+		}
+}
+
+bool
+SpreadData::DelRange()
+{
+	AccRange *ar;
+	int r, c;
+	RECT rec;
+
+	if(m_range && (ar = new AccRange(m_range)) && ar->GetFirst(&c, &r)) {
+		Disp->Command(CMD_MRK_DIRTY, 0L, w);
+		ar->BoundRec(&rec);
+		Undo.DataObject(Disp, w, this, &rec, 0L);
+		while(ar->GetNext(&c, &r)) {
+			if(r >= 0 && r < cRows && c >= 0 && c < cCols){
+				if(etRows[r][c] && etRows[r][c]->text) etRows[r][c]->SetText("");
+				}
+			}
+		delete (ar);	HideTextCursor();
+		}
+	HideMark(false);
+	if(CurrText) CurrText->Update(1, w, 0L);
+	bCopyCut = false;
+	return true;
+}
+
+bool
+SpreadData::PasteRange(int cmd, char *txt)
+{
+	AccRange *cr;
+	int i, r, c;
+	RECT mrk_range;
+
+	if(new_mark && m_range && (cr = new AccRange(m_range)) && cr->GetFirst(&c, &r)) {
+		cr->BoundRec(&mrk_range);	Disp->Command(CMD_MRK_DIRTY, 0L, w);
+		for(i = 0 ; cr->GetNext(&c, &r); i++) if(c >= 0 && c < cCols && r >= 0 && r < cRows){
+			currpos.x = c;	currpos.y = r;
+			switch(cmd){
+			case CMD_PASTE_TSV: ReadTSV(0L, (unsigned char*)txt, FF_TSV);		break;
+			case CMD_PASTE_SSV: ReadTSV(0L, (unsigned char*)txt, FF_SSV);		break;
+			case CMD_PASTE_XML: ReadXML(0L, (unsigned char*)txt, FF_XML, i ? UNDO_CONTINUE : 0L); break;
+			case CMD_PASTE_CSV: ReadData(0L, (unsigned char*)txt, FF_CSV);		break;
+				}
+			if((mrk_range.right - mrk_range.left) == (cp_src_rec.right - cp_src_rec.left) &&
+				(mrk_range.bottom - mrk_range.top) == (cp_src_rec.bottom - cp_src_rec.top)) break;
+			if((mrk_range.right - mrk_range.left) == 1 && (cp_src_rec.right - cp_src_rec.left) > 1) break;
+			if((mrk_range.bottom - mrk_range.top) == 1 && (cp_src_rec.bottom - cp_src_rec.top) > 1) break;
+			}
+		delete cr;		return true;
+		}
+	else switch(cmd){
+		case CMD_PASTE_TSV:
+			return ReadTSV(0L, (unsigned char*)txt, FF_TSV);
+		case CMD_PASTE_SSV:
+			return ReadTSV(0L, (unsigned char*)txt, FF_SSV);
+		case CMD_PASTE_XML:
+			return ReadXML(0L, (unsigned char*)txt, FF_XML, 0L);
+		case CMD_PASTE_CSV:
+			return ReadData(0L, (unsigned char*)txt, FF_CSV);
+		}
+	return bCopyCut = false;
+}
+
+bool
+SpreadData::InitCopy(int cmd, void *tmpl, anyOutput *o)
+{
+	int r, c;
+	AccRange *ar;
+	RECT rc_band, rcCopy;
+	bool bRet = false;
+
+	rcCopy.left = rcCopy.top = 0;
+	rcCopy.bottom = cRows-1;		rcCopy.right = cCols-1;
+	if(cmd) {
+		bCopyCut = (cmd == CMD_CUT);
+		new_mark = false;
+		if(m_range && m_range[0]) {
+			if(c_range) free(c_range);
+			c_range = (char*)memdup(m_range, (int)strlen(m_range)+1, 0);
+			if(c_range && (ar = new AccRange(c_range))) {
+				ar->BoundRec(&rcCopy);
+				delete ar;			bRet = true;;
+				}
+			}
+		else if (CurrText) {
+			if(CurrText->hasMark()) return CurrText->Command(CMD_COPY, o, 0L);
+			else {
+				if(m_range = (char*)realloc(m_range, 40*sizeof(char)))
+					rlp_strcpy(m_range, 40, mkRangeRef(currpos.y, currpos.x, currpos.y, currpos.x));
+				DoPlot(o);
+				return InitCopy(cmd, tmpl, o);
+				}
+			}
+		}
+	if(bRet || !cmd) {		//calculate animated mark
+		if(c_range && (ar = new AccRange(c_range))) {
+			ar->BoundRec(&rcCopy);			delete ar;
+			}
+		if(rcCopy.right < Disp->ssOrg.x || rcCopy.bottom < Disp->ssOrg.y) {
+			HideCopyMark();			return bRet;
+			}
+		c = rcCopy.left >= Disp->ssOrg.x ? rcCopy.left : Disp->ssOrg.x;
+		r = rcCopy.top >= Disp->ssOrg.y ? rcCopy.top : Disp->ssOrg.y;
+		while(!etRows[r] && r) r--;			while(!etRows[r][c] && c) c--;
+		if(etRows[r][c]){
+			rc_band.left = etRows[r][c]->GetX()-1;	rc_band.top = etRows[r][c]->GetY()-1;
+			}
+		else return bRet;
+		c = rcCopy.right < (Disp->ssOrg.x + c_disp) ? rcCopy.right : Disp->ssOrg.x + c_disp;
+		r = rcCopy.bottom < (Disp->ssOrg.y + r_disp)? rcCopy.bottom : Disp->ssOrg.y + r_disp;
+		if(r >= cRows) r = cRows-1;			if(c >= cCols) c = cRows -1;
+		if(etRows[r][c]){
+			rc_band.right = etRows[r][c]->GetX()+ri->GetWidth(c);
+			rc_band.bottom = etRows[r][c]->GetY()+CellHeight;
+			if(rc_band.left >= rc_band.right) rc_band.right = rc_band.left + CellWidth;
+			if(rc_band.top >= rc_band.bottom) rc_band.bottom = rc_band.top + CellHeight;
+			ShowCopyMark(o, &rc_band, 1);
+			}
+		}
+	return bRet;
+}
+
+bool
+SpreadData::SavePos()
+{
+	bool bRet = false;
+
+	if(pos_info.currpos.x != currpos.x || pos_info.currpos.y != pos_info.currpos.y
+		|| CurrText != pos_info.CurrText) {
+		Undo.Point(Disp, &currpos, w, UNDO_CONTINUE);
+		Undo.VoidPtr(Disp, (void**)&CurrText, 0L, 0L, UNDO_CONTINUE);
+		}
+	if(pos_info.ssOrg.x != Disp->ssOrg.x || pos_info.ssOrg.y != Disp->ssOrg.y) {
+		Swap(pos_info.ssOrg.x, Disp->ssOrg.x );		Swap(pos_info.ssOrg.y, Disp->ssOrg.y );
+		Undo.Point(Disp, &Disp->ssOrg, w, UNDO_CONTINUE);
+		Swap(pos_info.ssOrg.x, Disp->ssOrg.x );		Swap(pos_info.ssOrg.y, Disp->ssOrg.y );
+		}
+	pos_info.currpos.x = currpos.x;		pos_info.currpos.y = currpos.y;
+	pos_info.ssOrg.x = Disp->ssOrg.x;		pos_info.ssOrg.y = Disp->ssOrg.y;
+	pos_info.CurrText = CurrText;
+	return bRet;
+}
+
+bool
+SpreadData::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	static int move_cr = CMD_CURRDOWN;
+	MouseEvent *mev;
+	POINT p, cp;
+	RECT rc;
+	
+	if(!o) o = w;
+	switch(cmd) {
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		p.x = mev->x;				p.y = mev->y;
+		if((mev->StateFlags & 1) || mev->Action == MOUSE_LBUP || p.y < (w->MenuHeight+CellHeight)) {
+			if(!(mpos2dpos(&p, &cp, (mev->StateFlags & 1) == 1))) return false;
+			if(cp.y >= cRows || cp.x >= cCols || cp.y < 0 || cp.x < 0) return false;
+			}
+		else cp.x = cp.y = 0;
+		switch (mev->Action) {
+		case MOUSE_LBDOWN:
+			if(m_range && (mev->StateFlags & 0x18)) mrk_offs = (int)strlen(m_range);
+			else mrk_offs = 0;
+			bActive = true;		new_mark = false;
+			if(!et_racc && CurrText && !CurrText->isInRect(&p)){
+				CurrText->Update(2, w, &p);			 CurrText = 0L;	
+				DoPlot(w);
+				}
+			if(p.x < FirstWidth) cp.x = 0;
+			if(p.y < (w->MenuHeight+CellHeight)) cp.y = 0;
+			currpos.y = currpos2.y = cp.y;
+			currpos.x = currpos2.x = cp.x;
+		case MOUSE_MOVE:
+			if(p.y < (w->MenuHeight+CellHeight)) {
+				if(Disp->Command(CMD_COL_MOUSE, tmpl, w)) return true;
+				}
+			else w->MouseCursor(MC_ARROW, false);
+		case MOUSE_LBDOUBLECLICK:
+			if(!bActive) return false;
+			if(!m_range && !CurrText && cp.y < cRows && cp.x < cCols && cp.y >= 0 && cp.x >= 0)
+				CurrText = etRows[cp.y][cp.x];
+			if(mev->Action == MOUSE_MOVE && (mev->StateFlags & 1) 
+				&& !(CurrText && CurrText->isInRect(&p))) {
+				//mark rectangular range
+				if(!et_racc && !m_range && CurrText) {
+					CurrText->Update(2, w, &p);		 CurrText = 0L;	
+					}
+				if(mrk_offs && m_range && m_range[0]){
+					mrk_offs = rlp_strcpy(TmpTxt, mrk_offs+1, m_range);
+					}
+				else mrk_offs = 0;
+				if(p.x < FirstWidth || p.y < (w->MenuHeight+CellHeight)) {
+					if(mrk_offs && TmpTxt[mrk_offs-1] != ';')TmpTxt[mrk_offs++] = ';';
+					rlp_strcpy(TmpTxt+mrk_offs, TMP_TXT_SIZE - mrk_offs, mkRangeRef(
+						p.y < (w->MenuHeight+CellHeight) ? 0 : currpos.y, p.x < FirstWidth ? 0 : currpos.x,
+						p.y < (w->MenuHeight+CellHeight) ? cRows : cp.y, p.x < FirstWidth ? cCols-1 : cp.x));
+					}
+				else {
+					if(mrk_offs && TmpTxt[mrk_offs-1] != ';')TmpTxt[mrk_offs++] = ';';
+					rlp_strcpy(TmpTxt+mrk_offs, TMP_TXT_SIZE - mrk_offs, mkRangeRef(currpos.y, currpos.x, cp.y, cp.x));
+					}
+				if(!CurrText || et_racc)MarkRange(TmpTxt, &cp);
+				return true;
+				}
+			if(mev->Action == MOUSE_LBDOUBLECLICK) bActive = false;
+			if(!(mev->StateFlags & 1)) return false;
+			if(CurrText && CurrText->isInRect(&p)) {
+				return CurrText->Command(CMD_MOUSE_EVENT, o, (DataObj *)mev);
+				}
+			if(etRows[cp.y][cp.x]) 
+				return etRows[cp.y][cp.x]->Command(CMD_MOUSE_EVENT, o, (DataObj *)mev);
+			return false;
+		case MOUSE_LBUP:
+			if(bActive){
+				if(p.y < (w->MenuHeight+CellHeight)) {
+					if(Disp->Command(CMD_COL_MOUSE, tmpl, w)) return true;
+					}
+				isRowMark = p.x < FirstWidth;
+				isColMark = p.y < (w->MenuHeight+CellHeight);
+				if(isRowMark || isColMark) {
+					if(p.x < FirstWidth) {
+						currpos.x = 0;	cp.x = cCols-1;
+						}
+					if(p.y < (w->MenuHeight+CellHeight)) {
+						currpos.y = 0;	cp.y = cRows-1;
+						}
+					if(mrk_offs && m_range && m_range[0]){
+						mrk_offs = rlp_strcpy(TmpTxt, mrk_offs+1, m_range);
+						}
+					else mrk_offs = 0;
+					if(mrk_offs && TmpTxt[mrk_offs-1] != ';')TmpTxt[mrk_offs++] = ';';
+					rlp_strcpy(TmpTxt+mrk_offs, TMP_TXT_SIZE - mrk_offs, mkRangeRef(currpos.y, currpos.x, cp.y, cp.x));
+					MarkRange(TmpTxt);			currpos2.x = cp.x;		currpos2.y = cp.y + 1;
+					}
+				else if((m_range) && !new_mark) HideMark(false);
+				if(et_racc && !et_racc->isInRect(&p)) {
+					CurrText = et_racc;
+					if(m_range && m_range[0] && (currpos.x != cp.x || currpos.y != cp.y)) {
+						CurrText->Command(CMD_ADDTXT, o, (DataObj*) m_range);
+						}
+					else {
+						m_range = (char*)realloc(m_range, 40*sizeof(char));
+						rlp_strcpy(m_range, 40, mkCellRef(currpos.y, currpos.x));
+						CurrText->Command(CMD_ADDTXT, o, (DataObj*) m_range);
+						}
+					}
+				else if(m_range) {
+					CurrText = etRows[currpos.y][currpos.x];	CurrText->Update(1, w, &p);
+					DoPlot(w);
+					currpos2.x = cp.x;		currpos2.y = cp.y;
+					}
+				else return Select(&p);
+				}
+			}
+		break;
+	case CMD_PASTE_TSV:		case CMD_PASTE_XML:	case CMD_PASTE_CSV:	case CMD_PASTE_SSV:
+		bUpdate = true;		Disp->Command(CMD_MRK_DIRTY, 0L, 0L);
+		if(PasteRange(cmd, (char*)tmpl)) 
+			return Disp->Command(CMD_SETSCROLL, 0L, w);
+		return false;
+	case CMD_HIDEMARK:
+		if(c_range){
+			free(c_range);	c_range = 0L;	return true;
+			}
+		return false;
+	case CMD_GETFILENAME:
+		if(!tmpl) return false;
+		if(rlw_file && rlw_file[0]) {
+			if(rlp_strcpy((char*)tmpl, TMP_TXT_SIZE, rlw_file))return true;
+			else return false;
+			}
+		return false;
+	case CMD_ETRACC:
+		if(et_racc && tmpl && ((EditText*)tmpl)->parent != this) HideMark(false);
+		et_racc = (EditText*) tmpl;
+		if(CurrText && CurrText->parent != this) CurrText = 0L;
+		return true;
+	case CMD_CURRPOS:
+		if(tmpl) {
+			if(((POINT*)tmpl)->x < 0 && ((POINT*)tmpl)->y < 0) {
+				((POINT*)tmpl)->x = currpos.x;		((POINT*)tmpl)->y = currpos.y;
+				return true;
+				}
+			}
+		return false;
+	case CMD_UNLOCK:
+		HideMark(false);
+		currpos2.x = currpos.x;		currpos2.y = currpos.y;
+		break;
+	case CMD_ERROR:
+		if(err_msg && err_msg[0] && tmpl && *((char*)tmpl)) {
+			if(!(strcmp((char*)tmpl, "parse error")))return false;	//parse error has lowest priority
+			}
+		if(!tmpl && err_msg && err_msg[0] && strcmp(err_msg, "parse error")) return false;
+		err_msg = (char*)tmpl;
+		break;
+	case CMD_UPDATE:
+		bUpdate = true;
+		break;
+	case CMD_SAVEPOS:
+		return SavePos();
+	case CMD_CLEAR_ERROR:
+		err_msg = last_err = 0L;
+		break;
+	case CMD_TOOLMODE:						//ESC pressed
+		HideMark(true);
+		if(CurrText){
+			CurrText->Update(2, w, 0L);		CurrText->Update(1, w, 0L);
+			}
+		et_racc = 0L;						w->MouseCursor(MC_ARROW, true);
+		break;
+	case CMD_UPDHISTORY:
+		if(w) w->FileHistory();
+		break;
+	case CMD_MRK_DIRTY:
+		move_cr = CMD_CURRDOWN;
+		err_msg = last_err = 0L;
+		if(Disp) return Disp->Command(cmd, tmpl, o);
+		return false;
+	case CMD_ADDCHAR:
+		if(CurrText){
+			if(currpos.y < Disp->ssOrg.y) {
+				Disp->ssOrg.y = currpos.y;					Disp->Command(CMD_SETSCROLL, 0L, w);
+				}
+			else if(currpos.y > (Disp->ssOrg.y + r_disp -3)) {
+				Disp->ssOrg.y = currpos.y - (r_disp-3);
+				if(Disp->ssOrg.y < 0) Disp->ssOrg.y = 0;	Disp->Command(CMD_SETSCROLL, 0L, w);
+				}
+			if(currpos.x < Disp->ssOrg.x) {
+				Disp->ssOrg.x = currpos.x;					Disp->Command(CMD_SETSCROLL, 0L, w);
+				}
+			else if(currpos.x > (Disp->ssOrg.x + c_disp -3) && (CurrText->GetRX()+ 10) > Disp->currRC.right ) {
+				Disp->ssOrg.x = currpos.x - (c_disp-3);
+				if(Disp->ssOrg.x < 0) Disp->ssOrg.x = 0;	Disp->Command(CMD_SETSCROLL, 0L, w);
+				}
+			currpos2.x = currpos.x;		currpos2.y = currpos.y;
+			i = *((int*)tmpl);
+			Disp->Command(CMD_CURRPOS, &currpos, w);
+			if(i == 27) return Command(CMD_TOOLMODE, tmpl, o);
+			if(i !=3 && i != 22 && i != 24 && i != 26) HideMark(true);	//Do not hide upon ^C, ^V, ^X, ^Z
+			if(i == 13) return CurrText->Command(move_cr, w, this);
+			switch(i) {
+			case 8: return CurrText->Command(CMD_BACKSP, w, this);	//Backspace
+			default: return CurrText->AddChar(*((int*)tmpl), w, Disp);
+				}
+			}
+		else {
+			currpos.x = currpos2.x = Disp->ssOrg.x;			currpos.y = currpos2.y = Disp->ssOrg.y;
+			if(etRows[currpos.x] && (CurrText = etRows[currpos.x][currpos.y]))
+				CurrText->Update(1, w, &p);;
+			}
+		Disp->Command(CMD_CURRPOS, &currpos, w);
+		break;
+	case CMD_SHIFTUP:
+		if(Disp->ssOrg.y && currpos2.y <= Disp->ssOrg.y) {
+			Disp->ssOrg.y --;						Disp->Command(CMD_SETSCROLL, 0L, w);
+			}
+		currpos2.y -= 2;
+	case CMD_SHIFTDOWN:
+		if(cmd == CMD_SHIFTDOWN && r_disp > 3 && (currpos2.y-Disp->ssOrg.y) >= (r_disp-3)) {
+			Disp->ssOrg.y ++;						Disp->Command(CMD_SETSCROLL, 0L, w);
+			}
+		currpos2.y ++;
+		if(currpos2.y >= cRows) currpos2.y --;		if(currpos2.y < 0) currpos2.y ++;
+		//mark rectangular range
+		rlp_strcpy(TmpTxt, 40, mkRangeRef(currpos.y, currpos.x, currpos2.y, currpos2.x));
+		MarkRange(TmpTxt);							HideTextCursor();
+		break;
+	case CMD_SHIFTRIGHT:	case CMD_SHIFTLEFT:
+		if(!m_range && CurrText && CurrText->Command(cmd, w, this)) break;
+		if(cmd == CMD_SHIFTLEFT && c_disp > 3 && Disp->ssOrg.x && (currpos2.x-Disp->ssOrg.x) < 1) {
+			Disp->ssOrg.x --;						Disp->Command(CMD_SETSCROLL, 0L, w);
+			}
+		if(cmd == CMD_SHIFTRIGHT && c_disp > 3 && (currpos2.x-Disp->ssOrg.x) >= (c_disp-3)) {
+			Disp->ssOrg.x ++;						Disp->Command(CMD_SETSCROLL, 0L, w);
+			}
+		if(cmd == CMD_SHIFTLEFT) currpos2.x --;
+		else currpos2.x ++;
+		if(currpos2.x >= cCols) currpos2.x --;		if(currpos2.x < 0) currpos2.x ++;
+		//mark rectangular range
+		rlp_strcpy(TmpTxt, 40, mkRangeRef(currpos.y, currpos.x, currpos2.y, currpos2.x));
+		MarkRange(TmpTxt);							HideTextCursor();
+		break;
+	case CMD_SHPGUP:
+		if(Disp->ssOrg.y >0) {
+			Disp->ssOrg.y -= (r_disp-2);
+			if(Disp->ssOrg.y < 0) Disp->ssOrg.y = 0;
+			Disp->Command(CMD_SETSCROLL, 0L, w);
+			currpos2.y -= r_disp;					if(currpos2.y < 0) currpos2.y = 0;
+			rlp_strcpy(TmpTxt, 40, mkRangeRef(currpos.y, currpos.x, currpos2.y, currpos2.x));
+			MarkRange(TmpTxt);							HideTextCursor();
+			}
+		break;
+	case CMD_SHPGDOWN:
+		i = (Disp->ssOrg.y + 2*r_disp) < cRows ? r_disp-2 : cRows-r_disp - Disp->ssOrg.y+3;
+		if(i > 0) {
+			Disp->ssOrg.y += i;						Disp->Command(CMD_SETSCROLL, 0L, w);
+			}
+		if(currpos2.y < (cRows-1)) {
+			currpos2.y += r_disp;					if(currpos2.y >= cRows) currpos2.y = cRows-1;
+			//mark rectangular range
+			rlp_strcpy(TmpTxt, 40, mkRangeRef(currpos.y, currpos.x, currpos2.y, currpos2.x));
+			MarkRange(TmpTxt);							HideTextCursor();
+			}
+		break;
+	case CMD_CURRIGHT:		case CMD_CURRDOWN:
+		move_cr = cmd;		w->ActualSize(&rc);
+	case CMD_CURRLEFT:		case CMD_CURRUP:	case CMD_POS_FIRST:		case CMD_POS_LAST:
+		if(cmd == CMD_CURRUP && Disp->ssOrg.y && CurrText && currpos.y == Disp->ssOrg.y) {
+			Disp->ssOrg.y --;		currpos.y --;
+			DoPlot(o);
+			}
+		if(cmd == CMD_CURRLEFT && Disp->ssOrg.x && CurrText && (!CurrText->Cursor()) && (currpos.x == Disp->ssOrg.x)) {
+			Disp->ssOrg.x --;		currpos.x --;	
+			CurrText->Update(2, o, 0L);
+			Disp->Command(CMD_CURRPOS, &currpos, w);
+			if(etRows && etRows[currpos.y] && etRows[currpos.y][currpos.x]) 
+				CurrText = etRows[currpos.y][currpos.x];
+			if(CurrText){
+				CurrText->Command(CMD_POS_LAST, o, this);		CurrText->Update(1, o, 0L);
+				}
+			DoPlot(o);		return false;
+			}
+		if(CurrText && cmd == CMD_CURRIGHT && c_disp > 3 && ((currpos.x-Disp->ssOrg.x) >= (c_disp-1) || CurrText->GetRX() >= (rc.right-rc.left-10))) {
+			Disp->ssOrg.x ++;		currpos.y ++;			DoPlot(o);
+			CurrText->Command(CMD_POS_FIRST, o, this);
+			}
+		if(cmd == CMD_CURRDOWN && r_disp > 3 && (currpos.y-Disp->ssOrg.y) >= (r_disp-3)) {
+			Disp->ssOrg.y ++;		currpos.y ++;			DoPlot(o);
+			}
+		Disp->Command(CMD_CURRPOS, &currpos, w);
+		HideMark(false);
+		currpos2.x = currpos.x;								currpos2.y = currpos.y;
+		if(CurrText)return CurrText->Command(cmd, w, this);
+		else {
+			currpos.x = currpos2.x = Disp->ssOrg.x;			currpos.y = currpos2.y = Disp->ssOrg.y;
+			if(etRows[currpos.y] && (CurrText = etRows[currpos.y][currpos.x]))
+				CurrText->Update(1, w, &p);;
+			}
+		return false;
+	case CMD_SHTAB:
+		if(currpos.y >= cRows) currpos.y = cRows-1;
+		if(currpos.x == Disp->ssOrg.x && Disp->ssOrg.x > 0) {
+			Disp->ssOrg.x -= 1;
+			Disp->Command(CMD_SETSCROLL, 0L, w);
+			}
+		if(currpos.x > Disp->ssOrg.x && etRows[currpos.y][currpos.x]) {
+			currpos.x -=1;
+			}
+	case CMD_TAB:
+		if(currpos.y >= cRows) currpos.y = cRows-1;
+		if(cmd == CMD_TAB && currpos.x < (cCols-1) && etRows[currpos.y][currpos.x]) {
+			if((FirstWidth+(currpos.x - Disp->ssOrg.x +2)*CellWidth) > Disp->currRC.right){
+				Disp->ssOrg.x += 1;
+				Disp->Command(CMD_SETSCROLL, 0L, w);
+				}
+			currpos.x +=1;
+			}
+		if(CurrText) CurrText->Update(2, w, &p);
+		CurrText = etRows[currpos.y][currpos.x];
+		if(CurrText){
+			CurrText->Update(1, w, &p);
+			CurrText->Command(cmd == CMD_TAB ? CMD_POS_FIRST : CMD_POS_LAST, o, this);
+			}
+		Disp->Command(CMD_CURRPOS, &currpos, w);
+		return true;
+	case CMD_UNDO:
+		if(w) {
+			w->MouseCursor(MC_WAIT, true);
+			Undo.Restore(true, w);
+			w->MouseCursor(MC_ARROW, true);
+			if(et_racc) {
+				CurrText = et_racc;
+				CurrText->Update(1, w, 0L);
+				}
+			}
+		
+		return true;
+	case CMD_DELETE:
+		if(m_range) DelRange();
+		else if(CurrText) return CurrText->Command(cmd, o, this);
+		Disp->Command(CMD_CURRPOS, &currpos, w);
+		return true;
+	case CMD_QUERY_COPY:		case CMD_CUT:
+		et_racc = 0L;
+		return InitCopy(cmd, tmpl, w);
+	case CMD_GET_CELLDIMS:
+		if(tmpl) {
+			((int*)tmpl)[0] = FirstWidth;	((int*)tmpl)[1] = CellWidth;
+			((int*)tmpl)[2] = CellHeight;
+			}
+		break;
+	case CMD_SET_CELLDIMS:
+		if(tmpl && (FirstWidth != ((int*)tmpl)[0] || CellWidth != ((int*)tmpl)[1] || CellHeight != ((int*)tmpl)[2])) {
+			Undo.ValInt(Disp, &FirstWidth, UNDO_CONTINUE);	Undo.ValInt(Disp, &CellWidth, UNDO_CONTINUE);
+			Undo.ValInt(Disp, &CellHeight, UNDO_CONTINUE);	FirstWidth = ((int*)tmpl)[0];
+			CellWidth = ((int*)tmpl)[1];					CellHeight = ((int*)tmpl)[2];
+			}
+		break;
+	case CMD_TEXTSIZE:
+		if(tmpl) return Disp->Command(cmd, tmpl, o);
+		return false;
+	case CMD_COPY_TSV:
+		return MemList((unsigned char**)tmpl, FF_TSV);
+	case CMD_COPY_SYLK:
+		return MemList((unsigned char**)tmpl, FF_SYLK);
+	case CMD_COPY_XML:
+		return MemList((unsigned char**)tmpl, FF_XML);
+	case CMD_MENUHEIGHT:
+	case CMD_DOPLOT:	case CMD_REDRAW:
+		if(CurrText) CurrText->Update(2, 0L, 0L);
+		if(etRows && etRows[currpos.y] && currpos.y < cRows && currpos.x < cCols) 
+			if(CurrText = etRows[currpos.y][currpos.x]) CurrText->Update(1,0L, 0L);
+		DoPlot(o);
+		break;
+	case CMD_FILLRANGE:
+		Undo.SetDisp(w);
+		if(FillSsRange(this, &m_range, Disp)) Disp->Command(CMD_MRK_DIRTY, 0L, o);
+		DoPlot(o);
+		Undo.SetDisp(w);
+		break;
+	case CMD_GETMARK:
+		if(tmpl && m_range && m_range[0]) {
+			*((char**)tmpl) = m_range;
+			return true;
+			}
+		return false;
+	case CMD_INSROW:
+		return InsertRows();
+	case CMD_INSCOL:
+		return InsertCols();
+	case CMD_DELROW:
+		return DeleteRows();
+	case CMD_DELCOL:
+		return DeleteCols();
+	}
+	return true;
+}
+
+bool
+SpreadData::ReadXML(char *file, unsigned char *buffer, int type, DWORD undo_flags)
+{
+	int i, row, col, tag, cpgr, spgr, ufl = 0;
+	bool bContinue, bRet = false, bUndo_done = false;
+	ReadCache *XMLcache;
+	POINT pt, mov;
+	char TmpTxt[1024], *tmp_range;
+	unsigned char *pgr = 0L;
+	RECT rc_undo;
+
+	if(file) {
+		if(!(XMLcache = new ReadCache())) return false;
+		if(! XMLcache->Open(file)) {
+			delete XMLcache;
+			i = rlp_strcpy(TmpTxt, 40, "Error open file\n\"");		i = rlp_strcpy(TmpTxt+i, 200, file);
+			rlp_strcpy(TmpTxt+i, 2, "\"");							ErrorBox(TmpTxt);
+			return false;
+			}
+		bUndo_done = true;
+		if(!Init(1, 1)) goto XMLError;
+		etRows[0][0]->SetText("");
+		}
+	else if(buffer && type == FF_XML){
+		if(!(XMLcache = new MemCache(buffer))) return false;
+		if(buffer && bCopyCut) {
+			tmp_range = m_range;	m_range = c_range;
+			c_range = 0L;			DelRange();
+			m_range = tmp_range;
+			}
+		}
+	else return false;
+	pt.x = pt.y = mov.x = mov.y = 0;
+	cp_src_rec.left = cp_src_rec.right = cp_src_rec.top = cp_src_rec.bottom = 0;
+	do {
+		row = col = 0;
+		do {
+			TmpTxt[0] = XMLcache->Getc();
+			}while(TmpTxt[0] && TmpTxt[0] != '<');
+		for(i = 1; i < 5; TmpTxt[i++] = XMLcache->Getc());
+		TmpTxt[i] = 0;
+		if(!strcmp("<cell", TmpTxt)){
+			if(!bUndo_done) {
+				rc_undo.left = currpos.x;
+				rc_undo.right = cp_src_rec.right - cp_src_rec.left + currpos.x;
+				rc_undo.top = currpos.y;
+				rc_undo.bottom = cp_src_rec.bottom - cp_src_rec.top + currpos.y;
+				if(ufl == 3) Undo.DataObject(Disp, w, this, &rc_undo, undo_flags);
+				bUndo_done = true;
+				}
+ 			tag = 1;
+			}
+		else if(!strcmp("<pos1", TmpTxt)) tag = 2;
+		else if(!strcmp("<pos2", TmpTxt)) tag = 3;
+		else if(!strcmp("<RLPl", TmpTxt)) {
+			do {
+				TmpTxt[i++] = XMLcache->Getc();
+				}while(TmpTxt[i-1] > 31 && TmpTxt[i-1] != '>');
+			TmpTxt[i] = 0;
+			row = col = 0;
+			if(TmpTxt[17] == '=' && TmpTxt[13] == 'r') {
+#ifdef USE_WIN_SECURE
+				sscanf_s(TmpTxt+19, "%d", &row);
+#else
+				sscanf(TmpTxt+19, "%d", &row);
+#endif
+				}
+			else break;
+			for(i = 20; TmpTxt[i] && TmpTxt[i] != '='; i++);
+#ifdef USE_WIN_SECURE
+			sscanf_s(TmpTxt+i+2, "%d", &col);
+#else
+			sscanf(TmpTxt+i+2, "%d", &col);
+#endif
+			if(row && col) {
+				AddCols(col);		AddRows(row);
+				}
+			row = col = -1;
+			}
+		else if(!strcmp("<Grap", TmpTxt)){
+			while(XMLcache->Getc() != '[');
+			pgr = (unsigned char*)malloc(spgr = 1000);
+			pgr[0] = '[';	cpgr = 1;
+			do {
+				pgr[cpgr++] = XMLcache->Getc();
+				if(cpgr >= spgr) pgr = (unsigned char*)realloc(pgr, spgr +=1000);
+				}while(!(pgr[cpgr-1] == '<' && pgr[cpgr-2] == 0x0a));
+			pgr[cpgr-2] = 0;
+			while(XMLcache->Getc() != 0x0a);
+			if(cpgr >20)OpenGraph(Disp, 0L, pgr, false);
+			free(pgr);			tag = 0;
+			}
+		else tag = 0;
+		if(tag) {
+			do {
+				TmpTxt[0] = XMLcache->Getc();
+				}while(TmpTxt[0] && TmpTxt[0] != '"');
+			if (TmpTxt[0]) for (i =0; i <10 && ('"' != (TmpTxt[i] =XMLcache->Getc())); i++){
+				TmpTxt[i+1] = 0;
+				row = (int)atoi(TmpTxt);
+				}
+			do {
+				TmpTxt[0] = XMLcache->Getc();
+				}while(TmpTxt[0] && TmpTxt[0] != '"');
+			if (TmpTxt[0]) for (i =0; i <10 && ('"' != (TmpTxt[i] =XMLcache->Getc())); i++){
+				TmpTxt[i+1] = 0;
+				col = (int)atoi(TmpTxt);
+				}
+			if(tag ==2) {
+				mov.x = col;				mov.y = row;
+				cp_src_rec.left = col;		cp_src_rec.top = row;
+				ufl |= 1;
+				}
+			else if(tag ==3) {
+				cp_src_rec.right = col;		cp_src_rec.bottom = row;
+				ufl |= 2;
+				}
+			else if(row && col) do {
+				do {
+					TmpTxt[0] = XMLcache->Getc();
+					}while(TmpTxt[0] && TmpTxt[0] != '<');
+				for(i = 1; i < 6; TmpTxt[i++] = XMLcache->Getc());
+				TmpTxt[i] = 0;
+				if(bContinue =(0 == strcmp("<text>", TmpTxt))) {
+					for(i = 0; i < 1023; i++) {
+						if(!(TmpTxt[i] =XMLcache->Getc())) break;
+						if(i >1 && TmpTxt[i-2] == '<' && TmpTxt[i-1] == '/' && TmpTxt[i] == 't') break;
+						}
+					i -=2;		TmpTxt[i] = 0;
+					//xml indices start at 1:1 !
+					row += currpos.y-1;	col += currpos.x-1;
+					if(row >= cRows)AddRows(row+1);
+					if(col >= cCols)AddCols(col+1);
+					if(i && etRows[row] && etRows[row][col]) {
+						if(TmpTxt[0] == '=' && (currpos.x || currpos.y || mov.x || mov.y)) {
+							MoveFormula(this, TmpTxt, TmpTxt, TMP_TXT_SIZE, currpos.x-mov.x, currpos.y-mov.y, -1, -1);
+							}
+						etRows[row][col]->SetText(TmpTxt);
+						etRows[row][col]->Update(20, 0L, &pt);
+						}
+					}
+				}while(!bContinue);
+			}
+		}while(!XMLcache->IsEOF());
+	bRet = true;
+XMLError:
+	XMLcache->Close();
+	delete XMLcache;
+	bCopyCut = false;
+	return bRet;
+}
+
+bool
+SpreadData::ReadTSV(char *file, unsigned char *buffer, int type)
+{
+	int i, row, col;
+	char c;
+	bool bRet = false;
+	ReadCache *TSVcache;
+	POINT pt;
+
+	if(file) {
+		if(!(TSVcache = new ReadCache())) return false;
+		if(! TSVcache->Open(file)) {
+			delete TSVcache;
+			i = rlp_strcpy(TmpTxt, 200, "Error open file\n\"");
+			i += rlp_strcpy(TmpTxt+i, 200-i, file);
+			i += rlp_strcpy(TmpTxt+i, 200-i, "\"\n");
+			ErrorBox(TmpTxt);
+			return false;
+			}
+		if(!Init(1, 1)) goto TSVError;
+		etRows[0][0]->SetText("");
+		}
+	else if(buffer && (type == FF_TSV || type == FF_SSV)) {
+		if(!(TSVcache = new MemCache(buffer))) return false;
+		}
+	else return false;
+	row = currpos.y;	col = currpos.x;
+	pt.x = pt.y = 0;
+	do {
+		do {
+			TmpTxt[0] = TSVcache->Getc();
+			switch(TmpTxt[0]) {
+			case 0x0d:					//CR
+			case 0x0a:					//LF
+				if(col == currpos.x) break;
+				row ++;			col = currpos.x;		break;
+			case 0x09:					//tab
+				col ++;			break;
+			case ' ':
+				if(type == FF_SSV) col ++;				break;
+				}
+			}while(TmpTxt[0] && ((unsigned char)TmpTxt[0] < 33));
+		for(i = 1; ((unsigned char)(TmpTxt[i] = c = TSVcache->Getc()))>= (type == FF_SSV ? 33 : 32); i++)
+		if(i >= 4094) i = 4094;
+		if(TmpTxt[0] && row >= cRows)AddRows(row+1);
+		if(TmpTxt[0] && col >= cCols)AddCols(col+1);
+		TmpTxt[i] = 0;
+		if(TmpTxt[0] && etRows[row] && etRows[row][col]) {
+			etRows[row][col]->SetText(TmpTxt);
+			etRows[row][col]->Update(20, 0L, &pt);
+			switch(c) {
+			case 0x0d:					//CR
+			case 0x0a:					//LF
+				row ++;			col = currpos.x;		break;
+			case 0x09:					//tab
+				col ++;			break;
+			case ' ':
+				if(type == FF_SSV) col ++;				break;
+				}
+			}
+		}while(!TSVcache->IsEOF());
+	bRet = true;
+TSVError:
+	TSVcache->Close();
+	delete TSVcache;
+	return bRet;
+}
+
+static void sylk_cell_ref(char** ptr, int *cbd, int *size, char *first, char* trail, int row, int col)
+{
+	if(first && first[0]) add_to_buff(ptr, cbd, size, first, 0);
+	add_to_buff(ptr, cbd, size, "Y", 1);	add_int_to_buff(ptr, cbd, size, row+1, false, 0);
+	add_to_buff(ptr, cbd, size, ";X", 2);	add_int_to_buff(ptr, cbd, size, col+1, false, 0);
+	if(trail && trail[0]) add_to_buff(ptr, cbd, size, trail, 0);
+}
+
+bool 
+SpreadData::MemList(unsigned char **ptr, int type)
+{
+	int i, j, nc, nl; 
+	int cbd = 0, size;
+	bool bLimit = true;
+	AccRange *ar;
+	anyResult res;
+	RECT rcCopy;
+
+	if(!(*ptr = (unsigned char *)malloc(size = 10000)))return false;
+	if (c_range &&  c_range[0]) {
+		ar = new AccRange(c_range);			ar->BoundRec(&rcCopy);
+		if(bCopyCut) Undo.DataObject(Disp, w, this, &rcCopy, 0L);
+		}
+	else {
+		ar = 0L;	rcCopy.left = rcCopy.top = 0;
+		rcCopy.right = cCols-1;				rcCopy.bottom = cRows-1;
+		}
+	if(rcCopy.left < 0) rcCopy.left = 0;	if(rcCopy.right >= cCols) rcCopy.right = cCols-1;
+	if(rcCopy.top < 0) rcCopy.top = 0;		if(rcCopy.bottom >= cRows) rcCopy.bottom = cRows-1;
+	if(type == FF_SYLK) cbd = rlp_strcpy((char*)*ptr, size, "ID;PWXL;N;E\n"
+		"P;Pdd/mm/yyyy\nP;Phh:mm:ss\nP;Pdd/mm/yyyy hh:mm:ss\n");
+	else if(type == FF_XML) {
+		cbd = rlp_strcpy((char*)*ptr, 100, "<?xml version=\"1.0\"?><!DOCTYPE spreadsheet-snippet>"
+			"<spreadsheet-snippet rows=\"");
+		add_int_to_buff((char**)ptr, &cbd, &size, cRows, false, 0);
+		add_to_buff((char**)ptr, &cbd, &size, "\" columns=\"", 11);
+		add_int_to_buff((char**)ptr, &cbd, &size, cCols, false, 0);
+		add_to_buff((char**)ptr, &cbd, &size, "\">\n", 3);
+		if(rcCopy.left || rcCopy.top || rcCopy.bottom || rcCopy.right){
+			add_to_buff((char**)ptr, &cbd, &size, " <pos1 row=\"", 12);
+			add_int_to_buff((char**)ptr, &cbd, &size, rcCopy.top, false, 0);
+			add_to_buff((char**)ptr, &cbd, &size, "\" column=\"", 10);
+			add_int_to_buff((char**)ptr, &cbd, &size, rcCopy.left, false, 0);
+			add_to_buff((char**)ptr, &cbd, &size, "\"></pos1>\n <pos2 row=\"", 22);
+			add_int_to_buff((char**)ptr, &cbd, &size, rcCopy.bottom, false, 0);
+			add_to_buff((char**)ptr, &cbd, &size, "\" column=\"", 10);
+			add_int_to_buff((char**)ptr, &cbd, &size, rcCopy.right, false, 0);
+			add_to_buff((char**)ptr, &cbd, &size, "\"></pos2>\n", 10);
+			}
+		}
+	else if(type == FF_RLW) {
+		cbd = rlp_strcpy((char*)*ptr, size, "<?xml version=\"1.0\"?><!DOCTYPE RLPlot-workbook>\n<RLPlot-data rows=\"");
+		add_int_to_buff((char**)ptr, &cbd, &size, cRows, false, 0);
+		add_to_buff((char**)ptr, &cbd, &size, "\" columns=\"", 11);
+		add_int_to_buff((char**)ptr, &cbd, &size, cCols, false, 0);
+		add_to_buff((char**)ptr, &cbd, &size, "\">\n", 3);
+		}
+	for(nl =0, i = rcCopy.top; i <= rcCopy.bottom; i++, nl++) {
+		for(nc = 0, j = rcCopy.left; j <= rcCopy.right; j++, nc++) {
+			switch (type) {
+			case FF_TSV:
+				if(nl || nc) add_to_buff((char**)ptr, &cbd, &size, nc ? (char*)"\t" : (char*)"\n", 1);
+				if(etRows[i] && etRows[i][j]){
+					if((ar && ar->IsInRange(j,i)) || !ar) {
+						etRows[i][j]->GetResult(&res, false);
+						TranslateResult(&res);
+						add_to_buff((char**)ptr, &cbd, &size, res.text, 0);
+						}
+					}
+				break;
+			case FF_SYLK:
+				if(etRows[i] && etRows[i][j] && etRows[i][j]->text && ((ar && ar->IsInRange(j,i)) || !ar)){
+					etRows[i][j]->GetResult(&res, false);
+					TranslateResult(&res);
+					switch(res.type) {
+					case ET_VALUE:	case ET_BOOL:
+						sylk_cell_ref((char**) ptr, &cbd, &size, "C;", ";K", nl, nc);
+						add_to_buff((char**)ptr, &cbd, &size, res.text, 0);
+						break;
+					case ET_DATE:
+						sylk_cell_ref((char**) ptr, &cbd, &size, "F;P0;FG0G;", "\nC;K", nl, nc);
+						add_dbl_to_buff((char**)ptr, &cbd, &size, res.value+1, false);
+						break;
+					case ET_DATETIME:
+						sylk_cell_ref((char**) ptr, &cbd, &size, "F;P2;FG0G;", "\nC;K", nl, nc);
+						add_dbl_to_buff((char**)ptr, &cbd, &size, res.value+1, false);
+						break;
+					case ET_TIME:
+						sylk_cell_ref((char**) ptr, &cbd, &size, "F;P1;FG0G;", "\nC;K", nl, nc);
+						add_dbl_to_buff((char**)ptr, &cbd, &size, res.value, false);
+						break;
+					case ET_TEXT:
+						sylk_cell_ref((char**) ptr, &cbd, &size, "C;", ";K\"", nl, nc);
+						add_to_buff((char**)ptr, &cbd, &size, res.text, 0);
+						add_to_buff((char**)ptr, &cbd, &size, "\"", 1);
+						break;
+						}
+					add_to_buff((char**)ptr, &cbd, &size, "\n", 1);
+					if(bCopyCut) etRows[i][j]->SetText("");
+					}
+				break;
+			case FF_RLW:	case FF_XML:
+				if(etRows[i] && etRows[i][j] && etRows[i][j]->text && ((ar && ar->IsInRange(j,i)) || !ar)
+					&& etRows[i][j]->text[0]){
+					add_to_buff((char**)ptr, &cbd, &size, " <cell row=\"", 12);
+					add_int_to_buff((char**)ptr, &cbd, &size, nl+1, false, 0);
+					add_to_buff((char**)ptr, &cbd, &size, "\" column=\"", 10);
+					add_int_to_buff((char**)ptr, &cbd, &size, nc+1, false, 0);
+					add_to_buff((char**)ptr, &cbd, &size, "\">\n  <text>", 11);
+					add_to_buff((char**)ptr, &cbd, &size, etRows[i][j]->text, 0);
+					add_to_buff((char**)ptr, &cbd, &size, "</text>\n </cell>\n", 17);
+					if(bCopyCut) etRows[i][j]->SetText("");
+					}
+				break;
+				}
+			}
+		}
+	if(type == FF_SYLK) {
+		if(!bLimit) {
+			add_to_buff((char**)ptr, &cbd, &size, "C;Y", 3);
+			add_int_to_buff((char**)ptr, &cbd, &size, i+2, false, 0);
+			add_to_buff((char**)ptr, &cbd, &size, ";X1;K\"long strings were"
+			" truncated to 256 characters due to limitations of the SYLK format!\"\n", 92);
+			}
+		add_to_buff((char**)ptr, &cbd, &size, "E\n", 2);
+		}
+	else if(type == FF_TSV) add_to_buff((char**)ptr, &cbd, &size, "\n", 1);
+	else if(type == FF_XML) add_to_buff((char**)ptr, &cbd, &size, "</spreadsheet-snippet>\n", 23);
+	else if(type == FF_RLW){
+		Disp->Command(CMD_WRITE_GRAPHS, ptr, (anyOutput*)&cbd);
+		//note: cbd may be greater than size !
+		add_to_buff((char**)ptr, &cbd, &size, "</RLPlot-data>\n", 15);
+		}
+	if(ar) delete ar;
+	if(bCopyCut && type == FF_XML || type == FF_SYLK || type == FF_RLW){
+		bCopyCut = false;
+		DoPlot(w);
+		}
+	return true;
+}
+
+void SpreadMain(bool show)
+{
+	static SpreadData *w = 0L;
+
+	if(show) {
+		if(w = new SpreadData(0L)) w->Init(50, 10);
+		do_formula(w, 0L);			//init mfcalc
+		}
+	else if (w) delete(w);
+}
diff --git a/use_gui.cpp b/use_gui.cpp
index 2c45985..18a0f36 100755
--- a/use_gui.cpp
+++ b/use_gui.cpp
@@ -1,1930 +1,1940 @@
-//use_gui.cpp, Copyright 2000-2007 R.Lackner
-//
-//    This file is part of RLPlot.
-//
-//    RLPlot 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.
-//
-//    RLPlot 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 RLPlot; if not, write to the Free Software
-//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-//
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// This modules contains code for builds using a graphical user interface.
-// Builds running without GUI use no_gui.cpp instead.
-//
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-//
-#include "rlplot.h"
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-
-extern char TmpTxt[];
-extern Default defs;
-extern GraphObj *CurrGO, *TrackGO;			//Selected Graphic Objects
-extern Graph *CurrGraph;
-extern dragHandle *CurrHandle;
-extern UndoObj Undo;
-extern fmtText DrawFmtText;
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Spread sheet buttons used for rows and columns
-ssButton::ssButton(GraphObj *par, int x, int y, int w, int h):GraphObj(par, 0L)
-{
-	bLBdown = bSelected = bMarked = false;
-	SetMinMaxRect(&rDims, x, y, x+w, y+h);
-	Line.width = 0.0f;				Line.patlength = 1.0f;
-	Line.color = 0x00000000L;		Line.pattern = 0x00000000L;
-	Fill.type = FILL_NONE;			Fill.color = 0x00e8e8e8L;
-	Fill.scale = 1.0;				Fill.hatch = NULL;
-	TextDef.ColTxt = 0x00000000L;	TextDef.ColBg = 0x00ffffffL;
-	TextDef.fSize = 4.0;			TextDef.RotBL = TextDef.RotCHAR = 0.0;
-	TextDef.iSize = 0;				TextDef.Align = TXA_HLEFT | TXA_VTOP;
-	TextDef.Mode = TXM_TRANSPARENT;	TextDef.Style = TXS_NORMAL;
-	TextDef.Font = FONT_HELVETICA;	TextDef.text = 0L;
-}
-
-ssButton::~ssButton()
-{
-	if(TextDef.text) free(TextDef.text);
-}
-
-void
-ssButton::DoPlot(anyOutput *o)
-{
-	POINT pts[3];
-
-	Line.color = 0x00000000L;
-	if(bSelected) {
-		Fill.color = bMarked ? 0x00ffffe0L : 0x00ffffffL;
-		}
-	else {
-		Fill.color = bMarked ? 0x00ffe0e0L : 0x00e8e8e8L;
-		}
-	o->SetLine(&Line);				o->SetFill(&Fill);
-	if(bLBdown){
-		o->oRectangle(rDims.left, rDims.top, rDims.right, rDims.bottom);
-		}
-	else {
-		o->oRectangle(rDims.left, rDims.top, rDims.right-1, rDims.bottom-1);
-		Line.color = 0x00000000L;
-		o->SetLine(&Line);
-		pts[0].x = rDims.left;					pts[0].y = pts[1].y = rDims.bottom-1;
-		pts[1].x = pts[2].x = rDims.right-1;	pts[2].y = rDims.top-1;
-		o->oPolyline(pts, 3);
-		Line.color = 0x00ffffffL;
-		o->SetLine(&Line);
-		pts[0].x = pts[1].x = rDims.left;		pts[0].y = rDims.bottom -3;
-		pts[1].y = pts[2].y = rDims.top;		pts[2].x = rDims.right -2;
-		o->oPolyline(pts, 3);
-		}
-	if(TextDef.text) {
-		o->SetTextSpec(&TextDef);
-#ifdef _WINDOWS
-		o->oTextOut((rDims.left + rDims.right)/2-2, (rDims.top + rDims.bottom)/2-1, TextDef.text, 0);
-#else
-		o->oTextOut((rDims.left + rDims.right)/2-2, (rDims.top + rDims.bottom)/2+1, TextDef.text, 0);
-#endif
-		}
-
-}
-void
-ssButton::DoMark(anyOutput *o, bool mark)
-{
-	bLBdown = mark;
-	DoPlot(o);
-	o->UpdateRect(&rDims, false);
-}
-
-bool
-ssButton::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	char *tmptxt;
-	MouseEvent *mev;
-
-	switch (cmd) {
-	case CMD_GETTEXT:
-		if(TextDef.text && tmpl) {
-			rlp_strcpy((char*)tmpl, 10, TextDef.text);
-			return true;
-			}
-		return false;
-	case CMD_SELECT:
-		if(tmpl && *((int*)tmpl)) bSelected = true;
-		else bSelected = false;
-		if(o) {
-			DoPlot(o);
-			o->UpdateRect(&rDims, false);
-			}
-		return true;
-	case CMD_SETSTYLE:
-		if(tmpl && *((int*)tmpl)) bMarked = true;
-		else bMarked = false;
-		if(o) {
-			DoPlot(o);		o->UpdateRect(&rDims, false);
-			}
-		return true;
-	case CMD_SETTEXT:
-		if(tmpl) {
-			if(! TextDef.text) TextDef.text = (char*)malloc(10*sizeof(char));
-			rlp_strcpy(TextDef.text, 10, (char*)tmpl);
-			}
-		return true;
-	case CMD_GETTEXTDEF:
-		if(!tmpl) return false;
-		memcpy(tmpl, &TextDef, sizeof(TextDEF));
-		return true;
-	case CMD_SETTEXTDEF:
-		if(!tmpl)return false;
-		tmptxt = TextDef.text;
-		memcpy(&TextDef, tmpl, sizeof(TextDEF));
-		TextDef.text = tmptxt;
-		return true;
-	case CMD_MOUSE_EVENT:
-		if(!o || !(mev = (MouseEvent *) tmpl)) break;
-		if(IsInRect(&rDims, mev->x, mev->y)) {
-			if(bLBdown) {
-				if(!(mev->StateFlags & 0x01)) {
-					o->HideMark();
-					if(bLBdown) {
-						bLBdown = false; DoPlot(o);
-						}
-					}
-				}
-			else if(mev->StateFlags & 0x01) {
-				o->ShowMark(this, MRK_SSB_DRAW);
-				}
-			return true;
-			}
-		break;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// drag handles are graphic objects which allow the user to interactively
-//   change the size of an object
-dragHandle::dragHandle(GraphObj *par, int which):GraphObj(par, 0L)
-{
-	type = which;
-	Id = GO_DRAGHANDLE;
-	LineDef.width = LineDef.patlength = 0.0;
-	LineDef.color = LineDef.pattern = 0L;
-	FillDef.type = FILL_NONE;
-	FillDef.color = 0x00ffffffL;
-	FillDef.scale = 1.0;
-	FillDef.hatch = 0L;
-	minRC = maxRC = 0L;
-}
-
-dragHandle::~dragHandle()
-{
-	if(minRC) free(minRC);
-	if(maxRC) free(maxRC);
-	if(CurrHandle == this) CurrHandle = 0L;
-}
-
-void
-dragHandle::DoPlot(anyOutput *o)
-{
-	double fx, fy, dx, dy;
-	int ix, iy, idx;
-	fPOINT3D fp3d, ifp3d;
-	bool is3D = false;
-
-	if(!o || !parent) return;
-	dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
-	SetMinMaxRect(&drec, o->co2ix(parent->GetSize(SIZE_XPOS)+dx), 
-		o->co2iy(parent->GetSize(SIZE_YPOS)+dy), o->co2ix(parent->GetSize(SIZE_XPOS+1)+dx),
-		o->co2iy(parent->GetSize(SIZE_YPOS+1)+dy));
-	switch(type) {
-	case DH_19:		case DH_12:
-		fx = parent->GetSize(SIZE_XPOS);	fy = parent->GetSize(SIZE_YPOS);
-		break;
-	case DH_99:		case DH_22:
-		fx = parent->GetSize(SIZE_XPOS+1);	fy = parent->GetSize(SIZE_YPOS+1);
-		break;
-	case DH_29:
-		fx = (parent->GetSize(SIZE_XPOS) + parent->GetSize(SIZE_XPOS+1))/2.0;
-		fy = parent->GetSize(SIZE_YPOS);
-		break;
-	case DH_39:
-		fx = parent->GetSize(SIZE_XPOS+1);	fy = parent->GetSize(SIZE_YPOS);
-		break;
-	case DH_49:
-		fx = parent->GetSize(SIZE_XPOS);
-		fy = (parent->GetSize(SIZE_YPOS) + parent->GetSize(SIZE_YPOS+1))/2.0;
-		break;
-	case DH_59:
-		fx = (parent->GetSize(SIZE_XPOS) + parent->GetSize(SIZE_XPOS+1))/2.0;
-		fy = (parent->GetSize(SIZE_YPOS) + parent->GetSize(SIZE_YPOS+1))/2.0;
-		break;
-	case DH_69:
-		fx = parent->GetSize(SIZE_XPOS+1);
-		fy = (parent->GetSize(SIZE_YPOS) + parent->GetSize(SIZE_YPOS+1))/2.0;
-		break;
-	case DH_79:
-		fx = parent->GetSize(SIZE_XPOS);
-		fy = parent->GetSize(SIZE_YPOS+1);
-		break;
-	case DH_89:
-		fx = (parent->GetSize(SIZE_XPOS) + parent->GetSize(SIZE_XPOS+1))/2.0;
-		fy = parent->GetSize(SIZE_YPOS+1);
-		break;
-	case DH_18:	case DH_28:	case DH_38:	case DH_48:
-	case DH_58:	case DH_68:	case DH_78:	case DH_88:
-		fp3d.fx = parent->GetSize(SIZE_XPOS + type - DH_18);
-		fp3d.fy = parent->GetSize(SIZE_YPOS + type - DH_18);
-		fp3d.fz = parent->GetSize(SIZE_ZPOS + type - DH_18);
-		is3D = true;
-		break;
-	default:
-		if(type >= DH_DATA) {
-			fx = parent->GetSize(SIZE_XPOS + type - DH_DATA);
-			fy = parent->GetSize(SIZE_YPOS + type - DH_DATA);
-			FillDef.color = (this == CurrHandle) ? 0x0L : 0x00ffffffL;
-			}
-		else return;
-		}
-	if(is3D) {
-		o->cvec2ivec(&fp3d, &ifp3d);
-		ix = iround(ifp3d.fx);		iy = iround(ifp3d.fy);
-		}
-	else {
-		ix = o->co2ix(fx+dx);	iy = o->co2iy(fy+dy);
-		}
-	SetMinMaxRect(&rDims, ix-4, iy-4, ix+4, iy+4);
-	memcpy(&upd, &rDims, sizeof(RECT));
-	switch(type) {
-	case DH_12:	case DH_22:	case DH_19:	case DH_29:	case DH_39:
-	case DH_49:	case DH_69:	case DH_79:	case DH_89:	case DH_99:
-		o->SetLine(&LineDef);		o->SetFill(&FillDef);
-		o->oRectangle(rDims.left, rDims.top, rDims.right, rDims.bottom);
-		o->UpdateRect(&rDims, false);
-		break;
-	case DH_59:
-		o->SetLine(&LineDef);		o->SetFill(&FillDef);
-		IncrementMinMaxRect(&rDims, 2);
-		o->oCircle(rDims.left, rDims.top, rDims.right, rDims.bottom);
-		IncrementMinMaxRect(&rDims, 1);
-		o->UpdateRect(&rDims, false);
-		break;
-	default:
-		if(type >= DH_DATA) {
-			idx = (type - DH_DATA);
-			o->SetLine(&LineDef);		o->SetFill(&FillDef);
-			if(parent->Id == GO_BEZIER && (idx %3)) {
-				IncrementMinMaxRect(&rDims, -1);
-				o->oRectangle(rDims.left, rDims.top, rDims.right, rDims.bottom);
-				}
-			else if(parent->Id == GO_POLYLINE || parent->Id == GO_BEZIER) {
-				o->oCircle(rDims.left, rDims.top, rDims.right, rDims.bottom);
-				}
-			else {
-				o->oRectangle(rDims.left, rDims.top, rDims.right, rDims.bottom);
-				}
-			o->UpdateRect(&rDims, false);
-			}
-		else {
-			IncrementMinMaxRect(&rDims, -1);
-			o->UpdateRect(&rDims, true);
-			}
-		break;
-		}
-}
-
-bool
-dragHandle::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	lfPOINT pos;
-	int idx;
-
-	if(!parent) return false;
-	switch (cmd) {
-	case CMD_MOUSECURSOR:
-		if(o) switch(type) {
-		case DH_19:	case DH_99:
-			o->MouseCursor(MC_SE, false);							break;
-		case DH_29:	case DH_89:
-			o->MouseCursor(MC_NORTH, false);						break;
-		case DH_39:	case DH_79:
-			o->MouseCursor(MC_NE, false);							break;
-		case DH_49:	case DH_69:
-			o->MouseCursor(MC_EAST, false);							break;
-		default:
-			if(type >= DH_DATA) o->MouseCursor(MC_SALL, false);
-			else o->MouseCursor(MC_MOVE, false);					break;
-			}
-		return true;
-	case CMD_MINRC:
-		if(!(minRC)) minRC = (RECT*)calloc(1, sizeof(RECT));
-		if(tmpl && minRC) SetMinMaxRect(minRC, ((RECT*)tmpl)->left, ((RECT*)tmpl)->top, 
-			((RECT*)tmpl)->right, ((RECT*)tmpl)->bottom);
-		return true;
-	case CMD_MAXRC:
-		if(!(maxRC)) maxRC = (RECT*)calloc(1, sizeof(RECT));
-		if(tmpl && maxRC) SetMinMaxRect(maxRC, ((RECT*)tmpl)->left, ((RECT*)tmpl)->top, 
-			((RECT*)tmpl)->right, ((RECT*)tmpl)->bottom);
-		return true;
-	case CMD_MOVE:
-		idx = type >= DH_DATA ? type - DH_DATA : 0;
-		pos.fx = NiceValue(((lfPOINT*)tmpl)[0].fx);
-		pos.fy = NiceValue(((lfPOINT*)tmpl)[0].fy);
-		if(pos.fx == 0.0 && pos.fy == 0.0) return false;
-		parent->Command(CMD_SAVEPOS, &idx, o);
-		switch(type) {
-		case DH_12:
-			parent->SetSize(SIZE_XPOS, pos.fx + parent->GetSize(SIZE_XPOS));
-			parent->SetSize(SIZE_YPOS, pos.fy + parent->GetSize(SIZE_YPOS));
-			break;
-		case DH_22:
-			parent->SetSize(SIZE_XPOS+1, pos.fx + parent->GetSize(SIZE_XPOS+1));
-			parent->SetSize(SIZE_YPOS+1, pos.fy + parent->GetSize(SIZE_YPOS+1));
-			break;
-		case DH_19: parent->parent->SetSize(SIZE_XPOS, 
-						pos.fx + parent->GetSize(SIZE_XPOS));
-		case DH_29: parent->parent->SetSize(SIZE_YPOS,
-						pos.fy + parent->GetSize(SIZE_YPOS));
-			break;
-		case DH_39: parent->parent->SetSize(SIZE_YPOS,
-						pos.fy + parent->GetSize(SIZE_YPOS));
-		case DH_69: parent->parent->SetSize(SIZE_XPOS+1,
-						pos.fx + parent->GetSize(SIZE_XPOS+1));
-			break;
-		case DH_99: parent->parent->SetSize(SIZE_XPOS+1, 
-						pos.fx + parent->GetSize(SIZE_XPOS+1));
-		case DH_89: parent->parent->SetSize(SIZE_YPOS+1,
-						pos.fy + parent->GetSize(SIZE_YPOS+1));
-			break;
-		case DH_79: parent->parent->SetSize(SIZE_YPOS+1,
-						pos.fy + parent->GetSize(SIZE_YPOS+1));
-		case DH_49:
-			parent->parent->SetSize(SIZE_XPOS, 
-						pos.fx + parent->GetSize(SIZE_XPOS));
-			break;
-		case DH_18:	case DH_28:	case DH_38:	case DH_48:
-		case DH_58:	case DH_68:	case DH_78:	case DH_88:
-		case DH_59:
-			return parent->parent->Command(cmd, tmpl, o);
-		default:
-			if (type >= DH_DATA) {
-				parent->SetSize(SIZE_XPOS + idx, pos.fx + parent->GetSize(SIZE_XPOS + idx));
-				parent->SetSize(SIZE_YPOS + idx, pos.fy + parent->GetSize(SIZE_YPOS + idx));
-				CurrGO = parent;
-				}
-			break;
-			}
-		return parent->Command(CMD_REDRAW, 0L, o);
-		break;
-		}
-	return false;
-}
-
-void *
-dragHandle::ObjThere(int x, int y)
-{
-	if(IsInRect(&rDims, x, y)) return (CurrHandle = this);
-	else return 0L;
-}
-
-
-void
-dragHandle::Track(POINT *p, anyOutput *o)
-{
-	POINT p1, p2, pts[5], bez[256];
-	double dx, dy;
-	int i, first=0, last = 0, idx;
-	long nbez;
-	DWORD color;
-
-	if(!parent || !o) return;
-	Command(CMD_MOUSECURSOR, 0L, o);
-	if(upd.right < upd.left) Swap(upd.right, upd.left);
-	if(upd.bottom < upd.top) Swap(upd.bottom, upd.top);
-	dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
-	IncrementMinMaxRect(&upd, 2);
-	o->UpdateRect(&upd, false);
-	if(type >= DH_19 && type <= DH_99) memcpy(&upd, &drec, sizeof(RECT));
-	color = parent->GetColor(COL_DATA_LINE);
-	switch(type) {
-	case DH_12:
-	case DH_22:
-		pts[0].x = o->co2ix(parent->GetSize(SIZE_XPOS)+dx);
-		pts[0].y = o->co2iy(parent->GetSize(SIZE_YPOS)+dy);
-		pts[1].x = o->co2ix(parent->GetSize(SIZE_XPOS+1)+dx);
-		pts[1].y = o->co2iy(parent->GetSize(SIZE_YPOS+1)+dy);
-		if(type == DH_12) {
-			pts[0].x += p->x;			pts[0].y += p->y;
-			}
-		else if(type == DH_22) {
-			pts[1].x += p->x;			pts[1].y += p->y;
-			}
-		UpdateMinMaxRect(&upd, pts[0].x, pts[0].y);
-		UpdateMinMaxRect(&upd, pts[1].x, pts[1].y);
-		last = 2;
-		break;
-	case DH_19:
-		if(minRC && IsInRect(minRC, upd.left+p->x, (upd.top+upd.bottom)>>1))
-			upd.left = minRC->left;
-		else if(maxRC && !IsInRect(maxRC, upd.left+p->x, (upd.top+upd.bottom)>>1))
-			upd.left = upd.left+p->x >= maxRC->right ? maxRC->right : maxRC->left;
-		else upd.left += p->x;
-	case DH_29:
-		if(minRC && IsInRect(minRC, (upd.left + upd.right)>>1, upd.top+p->y))
-			upd.top = minRC->top;
-		else if(maxRC && !IsInRect(maxRC, (upd.left + upd.right)>>1, upd.top+p->y))
-			upd.top = upd.top +p->y >= maxRC->bottom? maxRC->bottom : maxRC->top;
-		else upd.top += p->y;
-		break;
-	case DH_39:
-		if(minRC && IsInRect(minRC, (upd.left + upd.right)>>1, upd.top+p->y))
-			upd.top = minRC->top;
-		else if(maxRC && !IsInRect(maxRC, (upd.left + upd.right)>>1, upd.top+p->y))
-			upd.top = upd.top+p->y >= maxRC->bottom ? maxRC->bottom : maxRC->top;
-		else upd.top += p->y;
-	case DH_69:
-		if(minRC && IsInRect(minRC, upd.right+p->x, (upd.top+upd.bottom)>>1))
-			upd.right = minRC->right;
-		else if(maxRC && !IsInRect(maxRC, upd.right+p->x, (upd.top+upd.bottom)>>1))
-			upd.right = upd.right+p->x <= maxRC->left ? maxRC->left : maxRC->right;
-		else upd.right += p->x;
-		break;
-	case DH_99:
-		if(minRC && IsInRect(minRC, upd.right+p->x, (upd.top+upd.bottom)>>1))
-			upd.right = minRC->right;
-		else if(maxRC && !IsInRect(maxRC, upd.right+p->x, (upd.top+upd.bottom)>>1))
-			upd.right = upd.right+p->x <= maxRC->left ? maxRC->left : maxRC->right;
-		else upd.right += p->x;
-	case DH_89:
-		if(minRC && IsInRect(minRC, (upd.left + upd.right)>>1, upd.bottom+p->y))
-			upd.bottom = minRC->bottom;
-		else if(maxRC && !IsInRect(maxRC, (upd.left + upd.right)>>1, upd.bottom+p->y))
-			upd.bottom = upd.bottom+p->y <= maxRC->top? maxRC->top : maxRC->bottom;
-		else upd.bottom += p->y;
-		break;
-	case DH_79:
-		if(minRC && IsInRect(minRC, (upd.left + upd.right)>>1, upd.bottom+p->y))
-			upd.bottom = minRC->bottom;
-		else if(maxRC && !IsInRect(maxRC, (upd.left + upd.right)>>1, upd.bottom+p->y))
-			upd.bottom = upd.bottom+p->y <= maxRC->top? maxRC->top : maxRC->bottom;
-		else upd.bottom += p->y;
-	case DH_49:
-		if(minRC && IsInRect(minRC, upd.left+p->x, (upd.top+upd.bottom)>>1))
-			upd.left = minRC->left;
-		else if(maxRC && !IsInRect(maxRC, upd.left+p->x, (upd.top+upd.bottom)>>1))
-			upd.left = upd.left+p->x >= maxRC->right ? maxRC->right : maxRC->left;
-		else upd.left += p->x;
-		break;
-	case DH_18:	case DH_28:	case DH_38:	case DH_48:
-	case DH_58:	case DH_68:	case DH_78:	case DH_88:
-		CurrGO = this;
-	case DH_59:
-		parent->parent->Track(p, o);
-		return;
-	default:
-		if(type >= DH_DATA) {
-			idx = type - DH_DATA;
-			pts[1].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx)+dx);
-			pts[1].y = o->co2iy(parent->GetSize(SIZE_YPOS + idx)+dy);
-			pts[1].x += p->x;				pts[1].y += p->y;
-			if(type > DH_DATA) {
-				pts[0].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx -1)+dx);
-				pts[0].y = o->co2iy(parent->GetSize(SIZE_YPOS + idx -1)+dy);
-				}
-			else {
-				pts[0].x = pts[1].x;		pts[0].y = pts[1].y;
-				}
-			pts[3].x = pts[2].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx +1)+dx);
-			pts[3].y = pts[2].y = o->co2iy(parent->GetSize(SIZE_YPOS + idx +1)+dy);
-			UpdateMinMaxRect(&upd, pts[0].x, pts[0].y);
-			UpdateMinMaxRect(&upd, pts[1].x, pts[1].y);
-			UpdateMinMaxRect(&upd, pts[2].x, pts[2].y);
-			if(parent ->Id == GO_BEZIER) {
-				switch(idx % 3) {
-				case 0:
-					o->ShowLine(pts, 3, 0x00c0c0c0L);
-					pts[0].x = pts[1].x;		pts[0].y = pts[1].y;
-					for(nbez = 0, i = 1; i < 4; i++) {
-						pts[i].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx +i)+dx);
-						pts[i].y = o->co2iy(parent->GetSize(SIZE_YPOS + idx +i)+dy);
-						}
-					DrawBezier(&nbez, bez, pts[0], pts[1], pts[2], pts[3], 0);
-					o->ShowLine(bez, nbez, color);
-					if(idx < 3) return;
-					pts[3].x = pts[0].x;		pts[3].y = pts[0].y;
-					for(i = nbez = 0, idx -= 3; i < 3; i++) {
-						pts[i].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx +i)+dx);
-						pts[i].y = o->co2iy(parent->GetSize(SIZE_YPOS + idx +i)+dy);
-						}
-					DrawBezier(&nbez, bez, pts[0], pts[1], pts[2], pts[3], 0);
-					o->ShowLine(bez, nbez, color);
-					return;
-				case 1:		
-					pts[3].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx +2)+dx);
-					pts[3].y = o->co2iy(parent->GetSize(SIZE_YPOS + idx +2)+dy);
-					last = 2;				break;
-				case 2:	
-					pts[2].x = pts[1].x;	pts[2].y = pts[1].y;
-					pts[1].x = pts[0].x;	pts[1].y = pts[0].y;
-					pts[0].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx -2)+dx);
-					pts[0].y = o->co2iy(parent->GetSize(SIZE_YPOS + idx -2)+dy);
-					first = 2;	last = 4;	break;
-					}
-				if(idx %3) {
-					nbez = 0;
-					DrawBezier(&nbez, bez, pts[0], pts[1], pts[2], pts[3], 0);
-					o->ShowLine(bez, nbez, color);
-					}
-				color = 0x00c0c0c0L;
-				}
-			else last = 3;
-			if(color == 0x0L || color == 0x00ffffffL) color = 0x00c0c0c0L;
-			}
-		else return;
-		}
-	if(type >= DH_19 && type <= DH_99) {
-		pts[0].x = pts[4].x = pts[3].x = p1.x = upd.left;	
-		pts[0].y = pts[1].y = pts[4].y = p1.y = upd.top;
-		pts[1].x = pts[2].x = p2.x = upd.right;	
-		pts[2].y = pts[3].y = p2.y = upd.bottom;
-		last = 5;
-		if(parent->parent->Id == GO_ELLIPSE) o->ShowEllipse(p1, p2, color);
-		}
-	o->ShowLine(pts+first, last-first, color);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// the dragRect object uses nine dragHandles to create a user interface
-//   for modification of rectangular shapes
-dragRect::dragRect(GraphObj *par, int which):GraphObj(par, 0L)
-{
-	int i;
-
-	type = which;
-	Id = GO_DRAGRECT;
-	if(handles = (dragHandle**)calloc(9, sizeof(dragHandle*)))
-		for(i = 0; i < 9; i++){
-			if(i == 4 && type == 0) handles[i] = new dragHandle(this, DH_19 + i);
-			else if(i != 4) handles[i] = new dragHandle(this, DH_19 + i);
-			}
-}
-
-dragRect::~dragRect()
-{
-	int i;
-
-	if(handles) for(i = 0; i < 9; i++) if(handles[i]) DeleteGO(handles[i]);
-}
-
-void
-dragRect::DoPlot(anyOutput *o)
-{
-	int i;
-
-	if(handles) for(i = 0; i < 9; i++) if(handles[i]) handles[i]->DoPlot(o);
-}
-
-bool
-dragRect::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	int i;
-
-	if(!parent) return false;
-	switch (cmd) {
-	case CMD_MINRC:
-	case CMD_MAXRC:
-		if(handles) for(i = 0; i < 9; i++) {
-			if(handles[i]) handles[i]->Command(cmd, tmpl, o);
-			}
-		break;
-	case CMD_SAVEPOS:
-	case CMD_REDRAW:
-		return parent->Command(cmd, tmpl, o);
-		}
-	return false;
-}
-
-void *
-dragRect::ObjThere(int x, int y)
-{
-	int i;
-	void *go;
-
-	if(handles)	for(i = 0; i < 9; i++) 
-		if(handles[i] && (go = (handles[i])->ObjThere(x, y))) return go;
-	return 0L;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// implement some kind of virtual trackball for 3D plots
-Drag3D::Drag3D(GraphObj *par):GraphObj(par, 0L)
-{
-	int i;
-
-	Id = GO_DRAG3D;
-	if(handles = (dragHandle**)calloc(8, sizeof(dragHandle*)))
-		for(i = 0; i < 8; i++){
-			handles[i] = new dragHandle(this, DH_18 + i);
-			}
-}
-
-Drag3D::~Drag3D()
-{
-	int i;
-
-	if(handles) for(i = 0; i < 8; i++) if(handles[i]) DeleteGO(handles[i]);
-}
-
-void
-Drag3D::DoPlot(anyOutput *o)
-{
-	int i;
-
-	if(handles) for(i = 0; i < 8; i++) if(handles[i]) handles[i]->DoPlot(o);
-}
-
-void *
-Drag3D::ObjThere(int x, int y)
-{
-	int i;
-	void *go;
-
-	if(handles)	for(i = 0; i < 8; i++) 
-		if(handles[i] && (go = (handles[i])->ObjThere(x, y))) return go;
-	return 0L;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// frame rectangle
-FrmRect::FrmRect(GraphObj *par, fRECT *lim, fRECT *c, fRECT *chld):GraphObj(par, 0L)
-{
-	parent = par;
-	limRC = lim;	cRC = c;	chldRC = chld;
-	drag = 0L;		mo = 0L;
-	Id = GO_FRAMERECT;
-	moveable = true;
-	Fill.type = FILL_NONE;
-	Line.color = FillLine.color = Fill.color = 0x00ffffffL;
-	Line.width = FillLine.width = 0.0;
-	Line.patlength = FillLine.patlength = Fill.scale = 1.0;
-	Line.pattern = FillLine.pattern = 0x0L;
-	Fill.hatch = &FillLine;
-	minRC = maxRC = 0L;
-}
-
-FrmRect::~FrmRect()
-{
-	if(drag) DeleteGO(drag);		drag = 0L;
-	if(minRC) free(minRC);			minRC = 0L;	
-	if(maxRC) free(maxRC);			maxRC = 0L;
-	if(mo) DelBitmapClass(mo);		mo = 0L;
-}
-
-double
-FrmRect::GetSize(int select)
-{
-	switch(select) {
-	case SIZE_XPOS:		return CurrRect.Xmin;
-	case SIZE_XPOS+1:	return CurrRect.Xmax;
-	case SIZE_YPOS:		return CurrRect.Ymin;
-	case SIZE_YPOS+1:	return CurrRect.Ymax;
-		}
-	return 0.0;
-}
-
-bool
-FrmRect::SetSize(int select, double value)
-{
-	double tmp, o_left, o_top;
-
-	o_left = cRC->Xmin;		o_top = cRC->Ymin;
-	switch (select & 0xfff) {
-	case SIZE_XPOS:
-		if(limRC) value -= limRC->Xmin;
-		if(swapX) cRC->Xmax = value;
-		else cRC->Xmin = value;
-		break;
-	case SIZE_XPOS+1:
-		if(limRC) value -= limRC->Xmin;
-		if(swapX) cRC->Xmin = value;
-		else cRC->Xmax = value;
-		break;
-	case SIZE_YPOS:
-		if(limRC) value -= limRC->Ymin;
-		if(swapY) cRC->Ymin = value;
-		else cRC->Ymax = value;
-		break;
-	case SIZE_YPOS+1:
-		if(limRC) value -= limRC->Ymin;
-		if(swapY) cRC->Ymax = value;
-		else cRC->Ymin = value;
-		break;
-	default: return false;
-		}
-	if((swapX && cRC->Xmin < cRC->Xmax) || (!swapX && cRC->Xmin > cRC->Xmax)) {
-		tmp = cRC->Xmin;	cRC->Xmin = cRC->Xmax;	cRC->Xmax = tmp;
-		}
-	if((swapY && cRC->Ymin > cRC->Ymax) || (!swapY && cRC->Ymin < cRC->Ymax)) {
-		tmp = cRC->Ymin;	cRC->Ymin = cRC->Ymax;	cRC->Ymax = tmp;
-		}
-	if(chldRC) {		//check if new rectangle is not inside child rectangle
-		if(cRC->Xmin > o_left+ chldRC->Xmin) cRC->Xmin = o_left + chldRC->Xmin;
-		if(cRC->Xmax < o_left+ chldRC->Xmax) cRC->Xmax = o_left + chldRC->Xmax;
-		if(cRC->Ymin > o_top+ chldRC->Ymin) cRC->Ymin = o_top + chldRC->Ymin;
-		if(cRC->Ymax < o_top+ chldRC->Ymax) cRC->Ymax = o_top + chldRC->Ymax;
-		}
-	if(chldRC && (o_left != cRC->Xmin || o_top != cRC->Ymin)) {
-		chldRC->Xmin -= (tmp = cRC->Xmin - o_left);		chldRC->Xmax -= tmp;
-		chldRC->Ymin -= (tmp = cRC->Ymin - o_top);		chldRC->Ymax -= tmp;
-		}
-	return true;
-}
-
-bool
-FrmRect::SetColor(int select, DWORD col)
-{
-	switch(select & 0xfff){
-	case COL_DRECT:
-		Line.color = col;
-	case COL_BG:
-		Fill.color = col;		return true;
-	case COL_GRECT:
-		Fill.color = col;		return true;
-	case COL_GRECTLINE:
-		Line.color = col;		return true;
-		}
-	return false;
-}
-
-void 
-FrmRect::DoMark(anyOutput *o, bool mark)
-{
-	if(!parent || !o) return;
-	if(!drag && (drag = new dragRect(this, (!limRC && parent->moveable) ? 0 : 1))){
-		if(minRC) drag->Command(CMD_MINRC, minRC, o);
-		if(maxRC) drag->Command(CMD_MAXRC, maxRC, o);
-		}
-	if(mark && drag){
-		if(mo) DelBitmapClass(mo);					mo = 0L;
-		memcpy(&mrc, &rDims, sizeof(RECT));
-		IncrementMinMaxRect(&mrc, 6);
-		mo = GetRectBitmap(&mrc, o);
-		drag->DoPlot(o);
-		o->UpdateRect(&mrc, false);
-		}
-	else RestoreRectBitmap(&mo, &mrc, o);
-}
-
-void
-FrmRect::DoPlot(anyOutput *o)
-{
-	int x1, y1, x2, y2;
-	double tmp;
-
-	if(!(cRC) || !o) return;
-	o->dFillCol = Fill.color ^ 0x00ffffff;	//force new brush
-	o->dLineCol = Line.color ^ 0x00ffffff;	//force new pen
-	o->SetLine(&Line);						o->SetFill(&Fill);
-	CurrRect.Xmin = cRC->Xmin;				CurrRect.Xmax = cRC->Xmax;
-	CurrRect.Ymin = cRC->Ymax;				CurrRect.Ymax = cRC->Ymin;
-	if(limRC) {
-		CurrRect.Xmin += limRC->Xmin;		CurrRect.Xmax += limRC->Xmin;
-		CurrRect.Ymin += limRC->Ymin;		CurrRect.Ymax += limRC->Ymin;
-		}
-	if(swapX = (CurrRect.Xmin > CurrRect.Xmax)) {
-		tmp = CurrRect.Xmin;	CurrRect.Xmin = CurrRect.Xmax;	CurrRect.Xmax = tmp;
-		}
-	if(swapY = (CurrRect.Ymin > CurrRect.Ymax)) {
-		tmp = CurrRect.Ymin;	CurrRect.Ymin = CurrRect.Ymax;	CurrRect.Ymax = tmp;
-		}
-	o->oRectangle(x1 = o->co2ix(CurrRect.Xmin), y1 = o->co2iy(CurrRect.Ymin), 
-		x2 = o->co2ix(CurrRect.Xmax), y2 = o->co2iy(CurrRect.Ymax));
-	SetMinMaxRect(&rDims, x1, y1, x2, y2);
-}
-
-bool
-FrmRect::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	MouseEvent *mev;
-
-	if(!parent) return false;
-	switch (cmd) {
-	case CMD_MOUSE_EVENT:
-		mev = (MouseEvent *) tmpl;
-		switch (mev->Action) {
-		case MOUSE_LBUP:
-			if(IsInRect(&rDims, mev->x, mev->y) && !(CurrGO) && (o)){
-				o->ShowMark(this, MRK_GODRAW);
-				if(!CurrGraph && parent && parent->Id == GO_GRAPH) CurrGraph = (Graph*)parent;
-				return true;
-				}
-			}
-		return false;
-	case CMD_MINRC:
-		if(!(minRC)) minRC = (RECT*)calloc(1, sizeof(RECT));
-		if(minRC && tmpl) SetMinMaxRect(minRC, ((RECT*)tmpl)->left, ((RECT*)tmpl)->top, 
-			((RECT*)tmpl)->right, ((RECT*)tmpl)->bottom);
-		if(drag) drag->Command(cmd, tmpl, o);
-		return true;
-	case CMD_MAXRC:
-		if(!(maxRC)) maxRC = (RECT*)calloc(1, sizeof(RECT));
-		if(maxRC && tmpl) SetMinMaxRect(maxRC, ((RECT*)tmpl)->left, ((RECT*)tmpl)->top, 
-			((RECT*)tmpl)->right, ((RECT*)tmpl)->bottom);
-		if(drag) drag->Command(cmd, tmpl, o);
-		return true;
-	case CMD_MOVE:
-		cRC->Xmin += NiceValue(((lfPOINT*)tmpl)[0].fx);
-		cRC->Ymin += NiceValue(((lfPOINT*)tmpl)[0].fy);
-		cRC->Xmax += NiceValue(((lfPOINT*)tmpl)[0].fx);
-		cRC->Ymax += NiceValue(((lfPOINT*)tmpl)[0].fy);
-	case CMD_REDRAW:
-		return parent->Command(CMD_REDRAW, 0L, o);
-	case CMD_SAVEPOS:
-		return parent->Command(cmd, tmpl, o);
-	case CMD_SETCHILD:
-		chldRC = (fRECT*)tmpl;
-		return true;
-		}
-	return false;
-}
-
-void *
-FrmRect::ObjThere(int x, int y)
-{
-	if(drag) return drag->ObjThere(x, y);
-	return 0L;
-}
-
-void
-FrmRect::Track(POINT *p, anyOutput *o)
-{
-	POINT tpts[5];
-	RECT old_rc;
-
-	if(o){
-		memcpy(&old_rc, &rDims, sizeof(rDims));
-		o->UpdateRect(&rDims, false);
-		tpts[0].x = tpts[1].x = tpts[4].x = o->co2ix(cRC->Xmin)+p->x;		
-		tpts[0].y = tpts[3].y = tpts[4].y = o->co2iy(cRC->Ymin)+p->y;
-		tpts[1].y = tpts[2].y = o->co2iy(cRC->Ymax)+p->y;
-		tpts[2].x = tpts[3].x = o->co2ix(cRC->Xmax)+p->x;
-		UpdateMinMaxRect(&rDims, tpts[0].x, tpts[0].y);
-		UpdateMinMaxRect(&rDims, tpts[2].x, tpts[2].y);	
-		if(old_rc.left != rDims.left || old_rc.right != rDims.right || old_rc.top !=
-			rDims.top || old_rc.bottom != rDims.bottom)IncrementMinMaxRect(&rDims, 3);
-		o->ShowLine(tpts, 5, Fill.color ^ 0x00ffffffL);
-		}
-
-}
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-void * 
-Arrow::ObjThere(int x, int y)
-{
-	if(!IsInRect(&rDims, x, y)) return 0L;
-	if(IsCloseToLine(&pts[0], &pts[1], x, y) ||
-		IsCloseToLine(&pts[2], &pts[3], x, y) ||
-		IsCloseToLine(&pts[3], &pts[4], x, y)){
-		if(dh1 && dh1->ObjThere(x, y)) return dh1;
-		if(dh2 && dh2->ObjThere(x, y)) return dh2;
-		return this;
-		}
-	return 0L;
-}
-
-void
-Arrow::Track(POINT *p, anyOutput *o)
-{
-	POINT *tpts;
-	RECT old_rc;
-	int i;
-
-	if(o && (tpts = (POINT*)malloc(6*sizeof(POINT)))){
-		memcpy(&old_rc, &rDims, sizeof(rDims));
-		o->UpdateRect(&rDims, false);
-		for(i = 0; i < 5; i++) {
-			tpts[i].x = pts[i].x+p->x;
-			tpts[i].y = pts[i].y+p->y;
-			UpdateMinMaxRect(&rDims, tpts[i].x, tpts[i].y);
-			}
-		switch(type & 0xff) {
-		case ARROW_LINE:
-			o->ShowLine(tpts+2, 3, LineDef.color);
-		case ARROW_NOCAP:
-			o->ShowLine(tpts, 2, LineDef.color);
-			break;
-		case ARROW_TRIANGLE:
-			tpts[5].x = tpts[2].x;		tpts[5].y = tpts[2].y;
-			o->ShowLine(tpts+2, 4, LineDef.color);
-			tpts[1].x = (tpts[2].x + tpts[4].x)>>1;
-			tpts[1].y = (tpts[2].y + tpts[4].y)>>1;
-			o->ShowLine(tpts, 2, LineDef.color);
-			break;
-			}
-		if(old_rc.left != rDims.left || old_rc.right != rDims.right || old_rc.top !=
-			rDims.top || old_rc.bottom != rDims.bottom)IncrementMinMaxRect(&rDims, 3);
-		free(tpts);
-		}
-}
-
-void
-Label::ShowCursor(anyOutput *o)
-{
-	CalcRect(o);
-	ShowTextCursor(o, &Cursor, TextDef.ColTxt);
-}
-
-bool
-Label::AddChar(int ci, anyOutput *o)
-{
-	char c, *txt1 = 0L;
-	int i, j, k;
-	GraphObj *golist[2];
-	DWORD flags;
-
-	if(!o || (ci != 13 && ci < 32)) return false;
-	if(CheckMark()) {
-		Undo.String(this, &TextDef.text, 0L);
-		Undo.ValInt(this, &m1, UNDO_CONTINUE);
-		Undo.ValInt(this, &m2, UNDO_CONTINUE);
-		rlp_strcpy(TextDef.text+m1, (int)strlen(TextDef.text+m1)+1, TextDef.text+m2);
-		CursorPos = m1;		flags = UNDO_CONTINUE;
-		}
-	else flags = 0L;
-	m1 = m2 = -1;
-	if(ci == 13 && parent){		//CR
-		if(parent->Id == GO_MLABEL){
-			parent->Command(CMD_SETFOCUS, this, o);
-			return parent->Command(CMD_ADDCHAR, &ci, o);
-			}
-		if(golist[1] = new mLabel(parent, data, fPos.fx, fPos.fy, &TextDef, 
-			TextDef.text, CursorPos, flags)){
-			golist[1]->moveable = moveable;
-			golist[1]->SetSize(SIZE_LB_XDIST, fDist.fx);
-			golist[1]->SetSize(SIZE_LB_YDIST, fDist.fy);
-			}
-		golist[0] = this;
-		if(!parent->Command(CMD_MUTATE, golist, o)) DeleteGO(golist[1]);
-		return false;
-		}
-	if(TextDef.text && TextDef.text[0]) i = (int)strlen(TextDef.text);
-	else i = 0;		if(CursorPos > i)CursorPos = i;
-	if(ci > 254) {
-		Undo.String(this, &TextDef.text, flags);
-		Undo.ValInt(this, &CursorPos, flags = UNDO_CONTINUE);
-		txt1 = (char*)malloc((i+12)*sizeof(char));
-		if(txt1) {
-			if(j=CursorPos) k = rlp_strcpy(txt1, CursorPos+1, TextDef.text);
-			else k = 0;
-#ifdef USE_WIN_SECURE
-			k += sprintf_s(txt1+k, i+12-k, "&#%d;",ci);
-#else
-			k += sprintf(txt1+k, "&#%d;",ci);
-#endif
-			CursorPos = k;
-			k += rlp_strcpy(txt1+k, i+12-k, TextDef.text+j);
-			if(TextDef.text) free(TextDef.text);
-			TextDef.text = txt1;			RedrawEdit(o);
-			}
-		return true;
-		}
-	else if( ci > 31) c = (char)ci;
-	else return false;
-	if(!TextDef.text || CursorPos < 10 || c == ' ') {
-		Undo.String(this, &TextDef.text, flags);
-		Undo.ValInt(this, &CursorPos, flags = UNDO_CONTINUE);
-		}
-	txt1 = (char*)malloc((i+4)* sizeof(char));
-	if(txt1) {
-		if(j=CursorPos) k = rlp_strcpy(txt1, CursorPos+1, TextDef.text);
-		else k = 0;			txt1[k++] = c;
-		k += rlp_strcpy(txt1+k, i+12-k, TextDef.text+j);
-		if(TextDef.text) free(TextDef.text);
-		TextDef.text = txt1;	txt1[k] = 0;
-		CursorPos++;			RedrawEdit(o);
-		}
-	return true;
-}
-
-void
-Label::CalcCursorPos(int x, int y, anyOutput *o)
-{
-	int i, j, ix, x1, y1, x2, h;
-
-	if(!o || !TextDef.text) return;
-	TextDef.iSize = o->un2iy(TextDef.fSize);
-	if(parent && parent->Id != GO_MLABEL) o->SetTextSpec(&TextDef);
-	x1 = ((pts[3].x + pts[4].x)>>1);	y1 = ((pts[3].y + pts[4].y)>>1);
-	ix = ((j=(x1-x))*j);	ix += ((j=(y1-y))*j);	ix = isqr(ix);
-	for(i = 1,  x1 = 0; TextDef.text[i-1]; i++) {
-		DrawFmtText.SetText(0L, TextDef.text, 0L, 0L);
-		DrawFmtText.oGetTextExtent(o, &x2, &h, i);
-		if(x1 <= ix && x2 >= ix) {
-			if((ix-x1) < (x2-ix)) CursorPos = i-1;
-			else CursorPos = i;
-			return;
-			}
-		else if(ix < x2){
-			CursorPos = i-1;
-			return;
-			}
-		x1 = x2;
-		}
-	if(pts[3].x < pts[2].x && x < pts[3].x) CursorPos = 0;
-	else CursorPos = (int)strlen(TextDef.text);
-}
-
-void 
-TextFrame::ShowCursor(anyOutput *o)
-{
-	int cx, cy, w, h;
-
-	if(!o) return;
-	TextDef.iSize = o->un2iy(TextDef.fSize);
-#ifdef _WINDOWS
-	linc = o->un2iy(TextDef.fSize*lspc);
-#else
-	linc = o->un2iy(TextDef.fSize*lspc*1.2);
-#endif
-	o->SetTextSpec(&TextDef);
-	cy = rDims.top + linc * cur_pos.y;			cx = rDims.left;
-	cx += ipad.left;							cy += ipad.top;
-	if(cur_pos.y < nlines) {
-		if(lines[cur_pos.y] && lines[cur_pos.y][0] && cur_pos.x) {
-			fmt_txt.SetText(0L, (char*)lines[cur_pos.y], &cx, &cy);
-			if(!fmt_txt.oGetTextExtent(o, &w, &h, cur_pos.x))return;
-			}
-		else {
-			if(!o->oGetTextExtent("A", 1, &w, &h))return;	w = 0;
-			}
-		Cursor.left = cx+w;		Cursor.right = cx+w;
-		Cursor.top = cy;		Cursor.bottom = cy+linc;
-		ShowTextCursor(o, &Cursor, TextDef.ColTxt);
-		}
-}
-
-void
-TextFrame::CalcCursorPos(int x, int y, anyOutput *o)
-{
-	int i, iy, ix, x1, x2, h;
-
-	if(!o) return;
-	TextDef.iSize = o->un2iy(TextDef.fSize);
-	o->SetTextSpec(&TextDef);
-	iy = (y-rDims.top-ipad.top)/linc;		x1 = x2 = 0;
-	if(iy >= nlines) iy = nlines-1;			cur_pos.y = iy;
-	x2 = rDims.right - rDims.left -ipad.left - ipad.right;
-	x = x - rDims.left -ipad.left;
-	fmt_txt.SetText(0L, (char*)lines[iy], &x1, &x2);
-	for(i = 0,  ix = x1 = 0; lines[iy][i]; i++) {
-		if(i) fmt_txt.oGetTextExtent(o, &x1, &h, i);
-		x1 -= (TextDef.iSize >> 2);
-		if(i && x1 <= x2 && x1 >= x) {
-			if(i >1) fmt_txt.oGetTextExtent(o, &x1, &h, i-1);
-			else x1 = 0;
-			fmt_txt.oGetTextExtent(o, &x2, &h, i);
-			ix = (abs(x1-x)<abs(x2-x)) ? i = (i-1) : i;
-			break;
-			}
-		}
-	if(ix) cur_pos.x = ix;
-	else cur_pos.x = i;
-}
-
-void
-Axis::DoMark(anyOutput *o, bool mark)
-{
-	LineDEF mrkLine;
-	double minw = DefSize(SIZE_DATA_LINE);
-	POINT *arc;
-	long narc = 0;
-
-	if(axis->flags & AXIS_ANGULAR) {
-		if(mark) {
-			if(mo) DelBitmapClass(mo);					mo = 0L;
-			o->GetSize(&mrc);						mo = GetRectBitmap(&mrc, o);
-			memcpy(&mrkLine, &axline, sizeof(LineDEF));
-			mrkLine.width = axline.width < minw ? minw * 3.0 : axline.width *3.0;
-			o->SetLine(&mrkLine);
-			if(arc = MakeArc(pts[0].x, pts[0].y, pts[1].x, 0x0f, &narc)) {
-				o->oPolyline(arc, (int)narc, 0L);
-				mrkLine.color ^= 0x00ffffffL;		mrkLine.width = axline.width;
-				o->SetLine(&mrkLine);				o->oPolyline(arc, (int)narc, 0L);
-				free(arc);
-				}
-			o->UpdateRect(&mrc, false);
-			}
-		else RestoreRectBitmap(&mo, &mrc, o);
-		return;
-		}
-	if(mark){
-		if(mo) DelBitmapClass(mo);					mo = 0L;
-		memcpy(&mrc, &rDims, sizeof(RECT));
-		IncrementMinMaxRect(&mrc, 6 + o->un2ix(axline.width));
-		mo = GetRectBitmap(&mrc, o);
-		InvertLine(pts, 2, &axline, &rDims, o, mark);
-		}
-	else RestoreRectBitmap(&mo, &mrc, o);
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Graphs are graphic objects containing plots, axes, and drawn objects
-bool
-Graph::ExecTool(MouseEvent *mev)
-{
-	static POINT pl = {0, 0}, pc = {0, 0};
-	static DWORD color = 0L;
-	scaleINFO scale;
-	POINT line[5];
-	GraphObj **tmpPlots, *new_go;
-	TextDEF *td;
-	lfPOINT *lfp, lf;
-	int i, j;
-	double x, y;
-
-	if(!mev || !CurrDisp) return false;
-	td = 0L;	if(mev->Action == MOUSE_LBUP) SuspendAnimation(CurrDisp, false);
-	if(ToolMode & TM_MOVE) switch (mev->Action) {
-		case MOUSE_LBDOWN:
-			pl.x = pc.x = mev->x;				pl.y = pc.y = mev->y;
-			if(TrackGO && TrackGO->Command(CMD_MOUSECURSOR, 0L, CurrDisp)) return true;
-			CurrDisp->HideMark();			CurrDisp->MouseCursor(MC_MOVE, false);
-			return true;
-		case MOUSE_MOVE:
-			if(TrackGO) {
-				if(TrackGO->Id == GO_DRAGHANDLE && TrackGO->type >= DH_18 &&
-					TrackGO->type <= DH_88) {
-					lf.fx = CurrDisp->fix2un((double)(mev->x-pl.x));	
-					lf.fy = CurrDisp->fiy2un((double)(mev->y-pl.y));
-					TrackGO->Track((POINT*)&lf, CurrDisp);
-					}
-				else {
-					pc.x = mev->x-pl.x;				pc.y = mev->y-pl.y;
-					TrackGO->Track(&pc, CurrDisp);
-					}
-				}
-			return true;
-		case MOUSE_LBUP:
-			if(TrackGO) {
-				ToolMode &= ~TM_MOVE;
-				pc.x = mev->x-pl.x;				pc.y = mev->y-pl.y;
-				if(!pc.x && !pc.y){
-					Command(CMD_TOOLMODE, (void*)(& ToolMode), CurrDisp);
-					return false;
-					}
-				TrackGO->Track(&pc, CurrDisp);
-				lf.fx = CurrDisp->fix2un((double)(mev->x-pl.x));	
-				lf.fy = CurrDisp->fiy2un((double)(mev->y-pl.y));
-				TrackGO->Command(CMD_MOVE, &lf, CurrDisp);
-				bModified = true;
-				}
-			Command(CMD_TOOLMODE, (void*)(& ToolMode), CurrDisp);
-			return true;
-		default:
-			return true;
-		}
-	if(ToolMode == TM_MARK || ToolMode == TM_ZOOMIN) {
-		switch (mev->Action) {
-		case MOUSE_LBDOWN:			//we should not receive that !
-			rc_mrk.left = mev->x;	rc_mrk.top = mev->y;
-			return false;
-		case MOUSE_LBUP:
-			j = (i = rc_mrk.left - mev->x)*i;
-			j += ((i = rc_mrk.top - mev->y)*i);
-			if(j < 20) {						//glitch
-				rc_mrk.left = rc_mrk.right = rc_mrk.top = rc_mrk.bottom = 0;
-				ToolMode = TM_STANDARD;		CurrDisp->MouseCursor(MC_ARROW, false);
-				return false;
-				}
-			if(ToolMode == TM_ZOOMIN) {
-				rc_mrk.right = mev->x;	rc_mrk.bottom = mev->y;
-				ToolMode = TM_STANDARD;
-				return Command(CMD_ZOOM, (void*) &"+", 0L);
-				}
-		default:
-			ToolMode = TM_STANDARD;
-		case MOUSE_MOVE:
-			if((mev->StateFlags &1) && rc_mrk.left >= 0 && rc_mrk.top >= 0) {
-				if(rcUpd.left != rcUpd.right && rcUpd.top != rcUpd.bottom)
-					CurrDisp->UpdateRect(&rcUpd, false);
-				line[0].x = line[3].x = line[4].x = rc_mrk.left;
-				line[0].y = line[1].y = line[4].y = rc_mrk.top;
-				line[1].x = line[2].x = mev->x;
-				line[2].y = line[3].y = mev->y;
-				CurrDisp->ShowLine(line, 5, 0x00c0c0c0L);
-				rcUpd.left = rcUpd.right = rc_mrk.left;
-				rcUpd.top = rcUpd.bottom = rc_mrk.top;
-				UpdateMinMaxRect(&rcUpd, rc_mrk.right = mev->x, rc_mrk.bottom = mev->y);
-				IncrementMinMaxRect(&rcUpd,2);
-				}
-			return true;
-			}
-		}
-	if(ToolMode == TM_PASTE) {
-		switch (mev->Action) {
-		case MOUSE_LBDOWN:
-			CurrGO = 0L;
-			CurrDisp->MrkMode = MRK_NONE;
-			pl.x = pc.x = mev->x;		pl.y = pc.y = mev->y;
-			rcDim.left = rcDim.right = rcUpd.left = rcUpd.right = mev->x;
-			rcDim.top = rcDim.bottom = rcUpd.top = rcUpd.bottom = mev->y;
-			return true;
-		case MOUSE_MOVE:
-			if(mev->StateFlags &1) {
-				if(rcUpd.left != rcUpd.right && rcUpd.top != rcUpd.bottom)
-					CurrDisp->UpdateRect(&rcUpd, false);
-				line[0].x = line[4].x = pl.x;	line[0].y = line[4].y = pl.y;
-				line[1].x = pc.x = mev->x;		line[1].y = pl.y;
-				line[2].x = mev->x;				line[2].y = pc.y = mev->y;
-				line[3].x = pl.x;				line[3].y = mev->y;
-				CurrDisp->ShowLine(line, 5, 0x00c0c0c0L);
-				memcpy(&rcUpd, &rcDim, sizeof(RECT));
-				UpdateMinMaxRect(&rcUpd, mev->x, mev->y);
-				IncrementMinMaxRect(&rcUpd, 2);
-				return true;
-				}
-			break;
-		case MOUSE_LBUP:
-			if(!PasteObj) return false;
-			pc.x = mev->x;			pc.y = mev->y;
-			if((lfp = (lfPOINT*)malloc(2 * sizeof(lfPOINT)))){
-				lfp[0].fx = CurrDisp->fix2un(pl.x - CurrDisp->VPorg.fx);
-				lfp[0].fy = CurrDisp->fiy2un(pl.y - CurrDisp->VPorg.fy);
-				lfp[1].fx = CurrDisp->fix2un(pc.x - CurrDisp->VPorg.fx);
-				lfp[1].fy = CurrDisp->fiy2un(pc.y - CurrDisp->VPorg.fy);
-				if(lfp[0].fx > lfp[1].fx) {
-					x = lfp[0].fx;		lfp[0].fx = lfp[1].fx;		lfp[1].fx = x;
-					}
-				if(lfp[0].fy > lfp[1].fy) {
-					y = lfp[0].fy;		lfp[0].fy = lfp[1].fy;		lfp[1].fy = y;
-					}
-				}
-			else {
-				DeleteGO(PasteObj);			PasteObj = 0L;
-				ToolMode = TM_STANDARD;		CurrDisp->MouseCursor(MC_ARROW, false);
-				return true;
-				}
-			scale.sx.fx = lfp[0].fx;	scale.sy.fx = lfp[0].fy;	scale.sz.fx = 0.0;
-			scale.sx.fy = scale.sy.fy = scale.sz.fy = 1.0;
-			if(PasteObj->Id == GO_GRAPH) {
-				if(abs(pc.x -pl.x) > 20 && abs(pc.y -pl.y) >20) {
-					x = ((Graph*)PasteObj)->GRect.Xmax - ((Graph*)PasteObj)->GRect.Xmin;
-					y = ((Graph*)PasteObj)->GRect.Ymax - ((Graph*)PasteObj)->GRect.Ymin;
-					scale.sx.fy = (lfp[1].fx - lfp[0].fx)/x;	scale.sy.fy = (lfp[1].fy - lfp[0].fy)/y;
-					scale.sx.fy = (scale.sx.fy + scale.sy.fy) /2.0;		//preserve aspect ratio
-					scale.sy.fy = scale.sx.fy;
-					}
-				((Graph*)PasteObj)->GRect.Xmax -= ((Graph*)PasteObj)->GRect.Xmin;
-				((Graph*)PasteObj)->GRect.Ymax -= ((Graph*)PasteObj)->GRect.Ymin;
-				((Graph*)PasteObj)->GRect.Ymin = ((Graph*)PasteObj)->GRect.Xmin = 0.0;
-				PasteObj->Command(CMD_SCALE, &scale, 0L);
-				Command(CMD_DROP_GRAPH, (void*)PasteObj, 0L);
-				}
-			else {
-				anyOutput *ScaleOut;
-				
-				if(ScaleOut = NewBitmapClass(10, 10, CurrDisp->hres, CurrDisp->vres)) {
-					PasteObj->parent = this;	PasteObj->DoPlot(ScaleOut);
-					switch(PasteObj->Id){
-					case GO_POLYLINE:		case GO_POLYGON:
-						IncrementMinMaxRect(&PasteObj->rDims, -(3*CurrDisp->un2ix(((polyline*)PasteObj)->pgLine.width)+3));
-						break;
-					case GO_BEZIER:
-						IncrementMinMaxRect(&PasteObj->rDims, 3);
-						break;
-						}
-					scale.sx.fx = CurrDisp->fix2un(-PasteObj->rDims.left);
-					scale.sy.fx = CurrDisp->fiy2un(-PasteObj->rDims.top);
-					PasteObj->Command(CMD_SCALE, &scale, 0L);
-					scale.sx.fx = lfp[0].fx;	scale.sy.fx = lfp[0].fy;	scale.sz.fx = 0.0;
-					if(abs(pc.x -pl.x) > 8 && abs(pc.y -pl.y) > 8) {
-						x = CurrDisp->fix2un(PasteObj->rDims.right - PasteObj->rDims.left);
-						y = CurrDisp->fiy2un(PasteObj->rDims.bottom - PasteObj->rDims.top);
-						scale.sx.fy = (lfp[1].fx - lfp[0].fx)/x;	
-						scale.sy.fy = (lfp[1].fy - lfp[0].fy)/y;
-						}
-					PasteObj->Command(CMD_SCALE, &scale, 0L);
-					delete ScaleOut;
-					}
-				Command(CMD_DROP_GRAPH, (void*)PasteObj, 0L);
-				}
-			PasteObj = 0L;
-			ToolMode = TM_STANDARD;			CurrDisp->MouseCursor(MC_ARROW, false);
-			break;
-			}
-		return true;
-		}
-	if(NumPlots && !Plots[NumPlots-1]) {
-		Undo.StoreListGO(this, &Plots, &NumPlots, UNDO_CONTINUE);
-		for(i = j = 0; i < NumPlots; i++) if(Plots[i]) Plots[j++] = Plots[i];
-		NumPlots = j;
-		}
-	else if(!Plots && !(Plots = (GraphObj**)calloc(2, sizeof(GraphObj*))))return false;
-	else if(Plots[NumPlots]){
-		if(tmpPlots = (GraphObj**)memdup(Plots, (NumPlots+2) * sizeof(GraphObj*), 0L)){
-			Undo.ListGOmoved(Plots, tmpPlots, NumPlots);
-			free(Plots);	Plots = tmpPlots;
-			Plots[NumPlots] = Plots[NumPlots+1] = 0L;
-			}
-		else return false;
-		}
-	switch(ToolMode & 0x0f) {
-	case TM_DRAW:
-		switch (mev->Action) {
-		case MOUSE_LBDOWN:
-			CurrGO = 0L;
-			CurrDisp->MrkMode = MRK_NONE;
-			Command(CMD_REDRAW, 0L, CurrDisp);
-			color = defs.Color(COL_POLYLINE);
-			pl.x = pc.x = mev->x;		pl.y = pc.y = mev->y;
-			if(tl_pts) free(tl_pts);
-			tl_nPts = 0;
-			if(tl_pts = (POINT*)malloc(4096 * sizeof(POINT)))
-				AddToPolygon(&tl_nPts, tl_pts, &pc);
-			return true;
-		case MOUSE_LBUP:
-			pl.x = mev->x;				pl.y = mev->y;
-			if(tl_pts) AddToPolygon(&tl_nPts, tl_pts, &pc);
-			// create line object
-			if(tl_pts && tl_nPts >1) {			//DEBUG: check for plausibility
-				if(lfp = (lfPOINT*)malloc(tl_nPts * sizeof(lfPOINT))){
-					for(i = 0; i < tl_nPts; i++) {
-						lfp[i].fx = CurrDisp->fix2un(tl_pts[i].x - CurrDisp->VPorg.fx);	
-						lfp[i].fy = CurrDisp->fiy2un(tl_pts[i].y - CurrDisp->VPorg.fy);
-						}
-					if(Plots[NumPlots]) i = NumPlots+1;
-					else i = NumPlots;
-//					Undo.SetGO(this, &Plots[i], new polyline(this, data, lfp, (int)tl_nPts), 0L);
-					Undo.SetGO(this, &Plots[i], new Bezier(this, data, lfp, (int)tl_nPts, 0,
-							(CurrDisp->hres/CurrDisp->VPscale+CurrDisp->vres/CurrDisp->VPscale)/2.0), 0L);
-					if(Plots[i]){
-						NumPlots = i+1;
-						Plots[i]->moveable = 1;
-						Plots[i]->DoPlot(CurrDisp);							//init
-						CurrDisp->ShowMark(CurrGO = Plots[i], MRK_GODRAW);	//edit
-						bModified = true;
-						}
-					free(lfp);
-					}
-				if(tl_pts) free(tl_pts);	tl_pts = 0L;	tl_nPts = 0;
-				return true;
-				}
-			if(tl_pts) free(tl_pts);	tl_pts = 0L;	tl_nPts = 0;
-			return false;
-		case MOUSE_MOVE:
-			if((mev->StateFlags &1) && tl_pts && tl_nPts < 4095) {
-				memcpy(&pl, &pc, sizeof(POINT));
-				line[1].x = pc.x = mev->x;		line[1].y = pc.y = mev->y;
-				line[0].x = pl.x;				line[0].y = pl.y;
-				AddToPolygon(&tl_nPts, tl_pts, &pc);
-#ifdef _WINDOWS
-				CurrDisp->ShowLine(line, 2, color);
-#else
-				CurrDisp->ShowLine(tl_pts, tl_nPts, color);
-#endif
-				return true;
-				}
-			break;
-			}
-		break;
-	case TM_POLYLINE:	case TM_POLYGON:
-		switch (mev->Action) {
-		case MOUSE_LBDOWN:
-			if(!tl_pts) {
-				CurrGO = 0L;
-				CurrDisp->MrkMode = MRK_NONE;
-				Command(CMD_REDRAW, 0L, CurrDisp);
-				color = defs.Color((ToolMode & 0x0f) == TM_POLYLINE ? COL_POLYLINE : COL_POLYGON);
-				pl.x = pc.x = mev->x;		pl.y = pc.y = mev->y;
-				tl_nPts = 0;
-				if(tl_pts = (POINT*)malloc(4096 * sizeof(POINT)))
-				AddToPolygon(&tl_nPts, tl_pts, &pc);
-				rcDim.left = rcDim.right = rcUpd.left = rcUpd.right = mev->x;
-				rcDim.top = rcDim.bottom = rcUpd.top = rcUpd.bottom = mev->y;
-				}
-			return true;
-		case MOUSE_MOVE:
-			if(tl_pts && tl_nPts) {
-				if(rcUpd.left != rcUpd.right && rcUpd.top != rcUpd.bottom)
-					CurrDisp->UpdateRect(&rcUpd, false);
-				CurrDisp->ShowLine(tl_pts, tl_nPts, color);
-				line[1].x = mev->x;				line[1].y = mev->y;
-				line[0].x = pc.x;				line[0].y = pc.y;
-				CurrDisp->ShowLine(line, 2, color);
-				UpdateMinMaxRect(&rcUpd, mev->x, mev->y);
-				memcpy(&rcUpd, &rcDim, sizeof(RECT));
-				UpdateMinMaxRect(&rcUpd, mev->x, mev->y);
-				IncrementMinMaxRect(&rcUpd, 2);
-				return true;
-				}
-			break;
-		case MOUSE_LBUP:
-			if(tl_pts && tl_nPts) {
-				memcpy(&pl, &pc, sizeof(POINT));
-				pc.x = mev->x;				pc.y = mev->y;
-				UpdateMinMaxRect(&rcDim, mev->x, mev->y);
-				AddToPolygon(&tl_nPts, tl_pts, &pc);
-				}
-			return true;
-		default:			// create line or polygon object
-			if(tl_pts && tl_nPts >0) {			//DEBUG: check for plausibility
-				pc.x = mev->x;				pc.y = mev->y;
-				AddToPolygon(&tl_nPts, tl_pts, &pc);
-				if(lfp = (lfPOINT*)malloc(tl_nPts * sizeof(lfPOINT))){
-					for(i = 0; i < tl_nPts; i++) {
-						lfp[i].fx = CurrDisp->fix2un(tl_pts[i].x-CurrDisp->VPorg.fx);	
-						lfp[i].fy = CurrDisp->fiy2un(tl_pts[i].y-CurrDisp->VPorg.fy);
-						}
-					if(Plots[NumPlots]) i = NumPlots+1;
-					else i = NumPlots;
-					Undo.SetGO(this, &Plots[i], ToolMode == TM_POLYLINE ?
-						new polyline(this, data, lfp, (int)tl_nPts) :
-						(GraphObj*) new polygon(this, data, lfp, (int)tl_nPts), 0L);
-					if(Plots[i]){
-						NumPlots = i+1;
-						Plots[i]->moveable = 1;
-						Plots[i]->DoPlot(CurrDisp);							//init
-						CurrDisp->ShowMark(CurrGO = Plots[i], MRK_GODRAW);	//edit
-						bModified = true;
-						}
-					free(lfp);
-					}
-				free(tl_pts);			tl_pts = 0L;		tl_nPts = 0;
-				return true;
-				}
-			}
-		if(mev->x == pc.x && mev->y == pc.y) return true;	//rebounce
-		break;
-	case TM_RECTANGLE:	case TM_ELLIPSE:	case TM_ROUNDREC:	case TM_ARROW:
-		switch (mev->Action) {
-		case MOUSE_LBDOWN:
-			CurrGO = 0L;
-			CurrDisp->MrkMode = MRK_NONE;
-			Command(CMD_REDRAW, 0L, CurrDisp);
-			color = defs.Color((ToolMode & 0x0f) != TM_ARROW ? COL_POLYGON : COL_DATA_LINE);
-			pl.x = pc.x = mev->x;		pl.y = pc.y = mev->y;
-			rcDim.left = rcDim.right = rcUpd.left = rcUpd.right = mev->x;
-			rcDim.top = rcDim.bottom = rcUpd.top = rcUpd.bottom = mev->y;
-			return true;
-		case MOUSE_MOVE:
-			if(mev->StateFlags &1) {
-				if(rcUpd.left != rcUpd.right && rcUpd.top != rcUpd.bottom)
-					CurrDisp->UpdateRect(&rcUpd, false);
-				line[0].x = line[4].x = pl.x;	line[0].y = line[4].y = pl.y;
-				if((ToolMode & 0x0f)==TM_ARROW) {
-					line[1].x = pc.x = mev->x;		line[1].y = pc.y = mev->y;
-					CurrDisp->ShowLine(line, 2, color);
-					}
-				else {
-					line[1].x = pc.x = mev->x;		line[1].y = pl.y;
-					line[2].x = mev->x;				line[2].y = pc.y = mev->y;
-					line[3].x = pl.x;				line[3].y = mev->y;
-					CurrDisp->ShowLine(line, 5, color);
-					if((ToolMode & 0x0f) == TM_ELLIPSE) 
-						CurrDisp->ShowEllipse(pc, pl, color);
-					}
-				memcpy(&rcUpd, &rcDim, sizeof(RECT));
-				UpdateMinMaxRect(&rcUpd, mev->x, mev->y);
-				IncrementMinMaxRect(&rcUpd, 2);
-				return true;
-				}
-			break;
-		case MOUSE_LBUP:
-			pc.x = mev->x;			pc.y = mev->y;
-			if(((ToolMode & 0x0f)==TM_ARROW || (abs(pc.x-pl.x) >5 && abs(pc.y-pl.y) >5)) && 
-				(lfp = (lfPOINT*)malloc(2 * sizeof(lfPOINT)))){
-				lfp[0].fx = CurrDisp->fix2un(pl.x - CurrDisp->VPorg.fx);
-				lfp[0].fy = CurrDisp->fiy2un(pl.y - CurrDisp->VPorg.fy);
-				lfp[1].fx = CurrDisp->fix2un(pc.x - CurrDisp->VPorg.fx);
-				lfp[1].fy = CurrDisp->fiy2un(pc.y - CurrDisp->VPorg.fy);
-				if(Plots[NumPlots]) i = NumPlots+1;
-				else i = NumPlots;
-				if(ToolMode == TM_RECTANGLE) new_go = new rectangle(this, data,
-					&lfp[0], &lfp[1]);
-				else if(ToolMode == TM_ELLIPSE) new_go = new ellipse(this, data,
-					&lfp[0], &lfp[1]);
-				else if(ToolMode == TM_ROUNDREC) new_go = new roundrec(this, data,
-					&lfp[0], &lfp[1]);
-				else if(ToolMode == TM_ARROW) new_go = new Arrow(this, data,
-					lfp[0], lfp[1], ARROW_UNITS | ARROW_LINE);
-				else new_go = 0L;
-				if(new_go) Undo.SetGO(this, &Plots[i], new_go, 0L);
-				if(Plots[i]){
-					NumPlots = i+1;				CurrDisp->HideMark();
-					Plots[i]->DoPlot(CurrDisp);								//init
-					CurrDisp->ShowMark(CurrGO = Plots[i], MRK_GODRAW);		//edit
-					Plots[i]->moveable = 1;
-					bModified = true;
-					}
-				free(lfp);
-				}
-			else if(rcUpd.left != rcUpd.right && rcUpd.top != rcUpd.bottom) {
-				CurrDisp->UpdateRect(&rcUpd, false);
-				}
-			}
-		break;
-	case TM_TEXT:
-		if(Plots[NumPlots]) i = NumPlots+1;
-		else i = NumPlots;
-		switch(mev->Action) {
-		case MOUSE_LBDOWN:
-			CurrGO = 0L;
-			CurrDisp->MrkMode = MRK_NONE;
-			Command(CMD_REDRAW, 0L, CurrDisp);
-			color = 0x00cbcbcb;
-			pl.x = pc.x = mev->x;		pl.y = pc.y = mev->y;
-			rcDim.left = rcDim.right = rcUpd.left = rcUpd.right = mev->x;
-			rcDim.top = rcDim.bottom = rcUpd.top = rcUpd.bottom = mev->y;
-			return true;
-		case MOUSE_MOVE:
-			if(mev->StateFlags &1) {
-				CurrDisp->MouseCursor(MC_TXTFRM, false);
-				if(rcUpd.left != rcUpd.right && rcUpd.top != rcUpd.bottom)
-					CurrDisp->UpdateRect(&rcUpd, false);
-				line[0].x = line[4].x = pl.x;	line[0].y = line[4].y = pl.y;
-				if((ToolMode & 0x0f)==TM_ARROW) {
-					line[1].x = pc.x = mev->x;		line[1].y = pc.y = mev->y;
-					CurrDisp->ShowLine(line, 2, color);
-					}
-				else {
-					line[1].x = pc.x = mev->x;		line[1].y = pl.y;
-					line[2].x = mev->x;				line[2].y = pc.y = mev->y;
-					line[3].x = pl.x;				line[3].y = mev->y;
-					CurrDisp->ShowLine(line, 5, color);
-					}
-				memcpy(&rcUpd, &rcDim, sizeof(RECT));
-				UpdateMinMaxRect(&rcUpd, mev->x, mev->y);
-				IncrementMinMaxRect(&rcUpd, 2);
-				return true;
-				}
-			break;
-		case MOUSE_LBUP:
-			pc.x = mev->x;			pc.y = mev->y;
-			if(((abs(pc.x-pl.x) >20 && abs(pc.y-pl.y) >20)) && 
-				(lfp = (lfPOINT*)malloc(2 * sizeof(lfPOINT)))){
-				lfp[0].fx = CurrDisp->fix2un(pl.x - CurrDisp->VPorg.fx);
-				lfp[0].fy = CurrDisp->fiy2un(pl.y - CurrDisp->VPorg.fy);
-				lfp[1].fx = CurrDisp->fix2un(pc.x - CurrDisp->VPorg.fx);
-				lfp[1].fy = CurrDisp->fiy2un(pc.y - CurrDisp->VPorg.fy);
-				if(Plots[NumPlots]) i = NumPlots+1;
-				else i = NumPlots;
-				new_go = new TextFrame(this, data, &lfp[0], &lfp[1], 0L);
-				if(new_go) Undo.SetGO(this, &Plots[i], new_go, 0L);
-				if(Plots[i]){
-					NumPlots = i+1;
-					Plots[i]->DoPlot(CurrDisp);							//init
-					CurrDisp->ShowMark(CurrGO = Plots[i], MRK_GODRAW);				//edit
-					Plots[i]->moveable = 1;
-					bModified = true;
-					}
-				free(lfp);
-				CurrDisp->CheckMenu(ToolMode & 0x0f, false);
-				CurrDisp->CheckMenu(ToolMode = TM_STANDARD, true);
-				CurrDisp->MouseCursor(MC_ARROW, false);
-				}
-			else {
-				CurrDisp->MouseCursor(MC_TEXT, false);
-				if(!(td = (TextDEF *)calloc(1, sizeof(TextDEF))))return false;
-				x = CurrDisp->fix2un(mev->x-CurrDisp->VPorg.fx);
-				y = CurrDisp->fiy2un(mev->y-CurrDisp->VPorg.fy);
-				td->ColTxt = defs.Color(COL_TEXT);		td->ColBg = defs.Color(COL_BG);
-				td->RotBL = td->RotCHAR = 0.0f;			td->fSize = DefSize(SIZE_TEXT);
-				td->Align = TXA_VTOP | TXA_HLEFT;		td->Style = TXS_NORMAL;
-				td->Mode = TXM_TRANSPARENT;				td->Font = FONT_HELVETICA;
-				td->text = 0L;
-				CurrGO = 0L;
-				CurrDisp->MrkMode = MRK_NONE;
-				Command(CMD_REDRAW, 0L, CurrDisp);
-				y -= td->fSize/2.0;
-				Undo.SetGO(this, &Plots[i], new Label(this, data, x, y, td, 0L), 0L);
-				if(Plots[i]){
-					NumPlots = i+1;
-					Plots[i]->DoPlot(CurrDisp);							//init
-					CurrDisp->ShowMark(CurrGO = Plots[i], MRK_GODRAW);	//edit
-					Plots[i]->moveable = 1;
-					}
-				free(td);
-				}
-			return true;
-			}
-		break;
-		}
-	return false;
-}
-
-bool
-Graph::MoveObj(int cmd, GraphObj *g)
-{
-	int i, j;
-	GraphObj *g1 = 0;
-
-	if(!g || NumPlots <2 || g->parent != this) return false;
-	switch(cmd) {
-	case CMD_MOVE_TOP:
-		for(i = j = 0; i <NumPlots; i++){
-			if(g == Plots[i]) g1 = Plots[i];
-			else Plots[j++] = Plots[i];
-			}
-		if(g1) {
-			Plots[j++] = g1;
-			return true;
-			}
-		break;
-	case CMD_MOVE_UP:
-		for(i = 0; i<NumPlots-1; i++){
-			if(g == Plots[i]){
-				g1 = Plots[i];	Plots[i] = Plots[i+1];	Plots[i+1] = g1;
-				return true;
-				}
-			}
-		break;
-	case CMD_MOVE_DOWN:
-		for(i = 1; i<NumPlots; i++){
-			if(g == Plots[i]){
-				g1 = Plots[i];	Plots[i] = Plots[i-1];	Plots[i-1] = g1;
-				return true;
-				}
-			}
-		break;
-	case CMD_MOVE_BOTTOM:
-		if(Plots[0] == g) return false;
-		for(i =  j = NumPlots-1; i >= 0; i--) {
-			if(g == Plots[i]) g1 = Plots[i];
-			else Plots[j--] = Plots[i];
-			}
-		if(g1) {
-			Plots[j--] = g1;
-			return true;
-			}
-		break;
-		}
-	return false;
-}
-
-bool
-Graph::DoZoom(char *z)
-{
-	RECT cw;
-	double fac, f1, f2;
-	ZoomDEF *tz;
-	Graph *cg;
-
-	if(!z) return false;
-	HideCopyMark();
-	if(0==strcmp("fit", z)) {
-		if(CurrGraph) cg = CurrGraph;
-		else cg = this;
-		rc_mrk.left = CurrDisp->un2ix(cg->GetSize(SIZE_GRECT_LEFT))-4;
-		rc_mrk.right = CurrDisp->un2ix(cg->GetSize(SIZE_GRECT_RIGHT))+4
-			+ iround(CurrDisp->MenuHeight);
-		rc_mrk.top = CurrDisp->un2ix(cg->GetSize(SIZE_GRECT_TOP))-4 
-			- iround(CurrDisp->MenuHeight);
-		rc_mrk.bottom = CurrDisp->un2ix(cg->GetSize(SIZE_GRECT_BOTTOM))+4;
-		if(!(CurrDisp->ActualSize(&cw)))return false;
-		f1 = (double)(cw.bottom - cw.top)/(double)(rc_mrk.bottom - rc_mrk.top);
-		f2 = ((double)(cw.right - cw.left)/(double)(rc_mrk.right - rc_mrk.left));
-		fac = f1 < f2 ? f1 : f2;
-		if((CurrDisp->VPscale * fac) > 100.0) fac = 100.0/CurrDisp->VPscale;
-		if((CurrDisp->VPscale * fac) < 0.05) fac = 0.05/CurrDisp->VPscale;
-		if(fac == 1.0) return false;
-		if(tz = (ZoomDEF*)memdup(zoom_def, sizeof(ZoomDEF)*(zoom_level+1), 0)){
-			if(zoom_def) free(zoom_def);
-			zoom_def = tz;
-			zoom_def[zoom_level].org.fx = CurrDisp->VPorg.fx;
-			zoom_def[zoom_level].org.fy = CurrDisp->VPorg.fy;
-			zoom_def[zoom_level].scale = CurrDisp->VPscale;
-			zoom_level++;
-			}
-		CurrDisp->VPscale *= fac;
-		if(CurrDisp->VPscale < 0.05) CurrDisp->VPscale = 0.05; 
-		if(CurrDisp->VPscale > 100.0) CurrDisp->VPscale = 100.0; 
-		CurrDisp->VPorg.fx = -rc_mrk.left * fac;
-		CurrDisp->VPorg.fy = -rc_mrk.top * fac;
-		HideTextCursor();
-		Command(CMD_SETSCROLL, 0L, CurrDisp);
-		return true;
-		}
-	else if(0==strcmp("+", z)) {
-		if(rc_mrk.left >= 0 && rc_mrk.right >= 0 && rc_mrk.top >= 0 && rc_mrk.bottom >= 0) {
-			if(rc_mrk.left > rc_mrk.right) Swap(rc_mrk.left, rc_mrk.right);
-			if(rc_mrk.top > rc_mrk.bottom) Swap(rc_mrk.top, rc_mrk.bottom);
-			if(5 > (rc_mrk.right - rc_mrk.left) || 5 > (rc_mrk.bottom - rc_mrk.top)) {
-				rc_mrk.left = rc_mrk.left = rc_mrk.left = rc_mrk.left = -1;
-				ToolMode = TM_STANDARD;		Command(CMD_TOOLMODE, &ToolMode, CurrDisp);
-				return false;
-				}
-			if(!(CurrDisp->ActualSize(&cw)))return false;
-			fac = (double)(cw.bottom - cw.top)/(double)(rc_mrk.bottom - rc_mrk.top);
-			fac += ((double)(cw.right - cw.left)/(double)(rc_mrk.right - rc_mrk.left));
-			fac /= 2.0;
-			if((CurrDisp->VPscale * fac) > 100.0) fac = 100.0/CurrDisp->VPscale;
-			if((CurrDisp->VPscale * fac) < 0.05) fac = 0.05/CurrDisp->VPscale;
-			if(fac == 1.0) return false;
-			if(tz = (ZoomDEF*)memdup(zoom_def, sizeof(ZoomDEF)*(zoom_level+1), 0)){
-				if(zoom_def) free(zoom_def);
-				zoom_def = tz;
-				zoom_def[zoom_level].org.fx = CurrDisp->VPorg.fx;
-				zoom_def[zoom_level].org.fy = CurrDisp->VPorg.fy;
-				zoom_def[zoom_level].scale = CurrDisp->VPscale;
-				zoom_level++;
-				}
-			CurrDisp->VPscale *= fac;
-			if(CurrDisp->VPscale < 0.05) CurrDisp->VPscale = 0.05; 
-			if(CurrDisp->VPscale > 100.0) CurrDisp->VPscale = 100.0; 
-			CurrDisp->VPorg.fx = CurrDisp->VPorg.fx * fac - rc_mrk.left * fac;
-			CurrDisp->VPorg.fy = CurrDisp->VPorg.fy * fac - rc_mrk.top * fac;
-			HideTextCursor();
-			Command(CMD_SETSCROLL, 0L, CurrDisp);
-			CurrDisp->MouseCursor(MC_ARROW, false);
-			rc_mrk.left = rc_mrk.left = rc_mrk.left = rc_mrk.left = -1;
-			ToolMode = TM_STANDARD;		Command(CMD_TOOLMODE, &ToolMode, CurrDisp);
-			return true;
-			}
-		else {
-			ToolMode = TM_ZOOMIN;			CurrDisp->MouseCursor(MC_ZOOM, true);
-			}
-		}
-	else if(0==strcmp("-", z)) {
-		HideTextCursor();
-		if(zoom_def && zoom_level > 0) {
-			zoom_level--;
-			if(CurrDisp->VPscale == zoom_def[zoom_level].scale &&
-				CurrDisp->VPorg.fx == zoom_def[zoom_level].org.fx &&
-				CurrDisp->VPorg.fy == zoom_def[zoom_level].org.fy) {
-				DoZoom(z);
-				}
-			else {
-				CurrDisp->VPscale = zoom_def[zoom_level].scale;
-				CurrDisp->VPorg.fx = zoom_def[zoom_level].org.fx;
-				CurrDisp->VPorg.fy = zoom_def[zoom_level].org.fy;
-				}
-			}
-		else {
-			CurrDisp->VPorg.fx = CurrDisp->VPorg.fy = 0.0;
-			CurrDisp->VPscale = Id == GO_PAGE ? 0.5 : 1.0;
-			}
-		Command(CMD_SETSCROLL, 0L, CurrDisp);
-		return true;
-		}
-	else if(0==strcmp("25", z)){
-		CurrDisp->VPscale = 0.25;		return DoZoom("org");
-		}
-	else if(0==strcmp("50", z)){
-		CurrDisp->VPscale = 0.50;		return DoZoom("org");
-		}
-	else if(0==strcmp("100", z)){
-		CurrDisp->VPscale = 1.00;		return DoZoom("org");
-		}
-	else if(0==strcmp("200", z)){
-		CurrDisp->VPscale = 2.00;		return DoZoom("org");
-		}
-	else if(0==strcmp("400", z)){
-		CurrDisp->VPscale = 4.00;		return DoZoom("org");
-		}
-	else if(0==strcmp("org", z)){
-		CurrDisp->VPorg.fx = 0.0;		CurrDisp->VPorg.fy = iround(CurrDisp->MenuHeight);
-		HideTextCursor();
-		Command(CMD_SETSCROLL, 0L, CurrDisp);
-		return DoZoom("reset");
-		}
-	else if(0==strcmp("reset", z)){
-		if(zoom_def && zoom_level > 0) free(zoom_def);
-		zoom_def = 0L;			zoom_level = 0;
-		}
-	return false;
-}
-
-//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Create a tree structure of all interesting objects
-//   This object is used e.g. for layer control
-ObjTree::ObjTree(GraphObj *par, DataObj *d, GraphObj *root):GraphObj(par, d)
-{
-	Id = GO_OBJTREE;					base = root;		list=0L;
-	TextDef.ColTxt = 0x00000000L;		TextDef.ColBg = 0x00ffffffL;
-	TextDef.fSize = 4.0;				TextDef.RotBL = TextDef.RotCHAR = 0.0;
-	TextDef.iSize = 0;					TextDef.Align = TXA_HLEFT | TXA_VTOP;
-	TextDef.Mode = TXM_TRANSPARENT;		TextDef.Style = TXS_NORMAL;
-	TextDef.Font = FONT_HELVETICA;		TextDef.text = 0L;
-	Command(CMD_LAYERS, 0L, 0L);
-}
-
-ObjTree::~ObjTree()
-{
-	if(list) free(list);		list = 0L;
-}
-
-void
-ObjTree::DoPlot(anyOutput *o)
-{
-	int i, n, ix, iy;
-	GraphObj *curr_obj;
-
-	if(!o || !list) return;
-	o->Erase(0x00ffffffL);
-	ix = 10;	iy = 0;
-	for(i = 0; i < count; i++, iy += (TextDef.iSize+_SBINC)) {
-		if(list[i]) {
-			curr_obj = list[i];			n = 0;
-			if(i) while(curr_obj && curr_obj != list[0]) {
-				if(curr_obj != list[0]) n += rlp_strcpy(TmpTxt+n, TMP_TXT_SIZE -n, " -");
-				if(curr_obj) curr_obj = curr_obj->parent; 
-				}
-			if(n) TmpTxt[n++] = ' ';
-			n += rlp_strcpy(TmpTxt+n, TMP_TXT_SIZE -n, get_name(i));
-			if(list[i]->Id >= GO_PLOT && list[i]->Id < GO_GRAPH) {
-				TextDef.ColTxt = ((Plot*)list[i])->hidden ? 0x00000080L : 0x00008000L;
-				}
-			else TextDef.ColTxt = 0x00000000L;
-			o->SetTextSpec(&TextDef);
-			o->oTextOut(ix, iy, TmpTxt, 0);
-			}
-		}
-}
-
-bool
-ObjTree::Command(int cmd, void *tmpl, anyOutput *o)
-{
-	switch(cmd){
-	case CMD_LAYERS:
-		if(list) free(list);
-		count = 0;		maxcount = 100;
-		if(base) {
-			if(list = (GraphObj**) malloc(100 * sizeof(GraphObj*))) {
-				list[count++] = base;
-				}
-			base->Command(CMD_OBJTREE, this, 0L);
-			}
-		break;
-	case CMD_SET_DATAOBJ:
-		Id = GO_OBJTREE;
-		return true;
-	case CMD_UPDATE:
-		if(tmpl && (((GraphObj*)tmpl)->Id >= GO_PLOT && ((GraphObj*)tmpl)->Id < GO_SPREADDATA)
-			|| ((GraphObj*)tmpl)->Id == GO_LEGEND) {
-			if(count >= maxcount) {
-				maxcount += 100;
-				list = (GraphObj**) realloc(list, maxcount);
-				}
-			if(list) list[count++] = (GraphObj*)tmpl;
-			}
-		return true;
-	case CMD_TEXTDEF:
-		if(tmpl) memcpy(&TextDef, tmpl, sizeof(TextDEF));
-		TextDef.text = 0L;
-		return true;
-		}
-	return false;
-}
-
-anyOutput *
-ObjTree::CreateBitmap(int *bw, int *bh, anyOutput *tmpl)
-{
-	anyOutput *bmp = 0L;
-	int h;
-	
-	h = (tmpl->un2iy(TextDef.fSize)+_SBINC) * (count+1);
-	if(h > *bh) *bh = h;
-	if(bmp = NewBitmapClass(*bw, *bh, tmpl->hres, tmpl->vres)) DoPlot(bmp);
-	return bmp;
-}
-
-char *
-ObjTree::get_name(int li)
-{
-	if(li < count && list[li] && list[li]->name) return list[li]->name;
-	else return "(unknown)";
-}
-
-int
-ObjTree::get_vis(int li)
-{
-	if(li < count && list[li] && list[li]->Id >= GO_PLOT && list[li]->Id < GO_GRAPH)
-		return ((Plot*)list[li])->hidden ? 0 : 1;
-	return 2;
-}
-
-bool
-ObjTree::set_vis(int li, bool vis)
-{
-	if(li < count && list[li] && list[li]->Id >= GO_PLOT && list[li]->Id < GO_GRAPH) {
-		((Plot*)list[li])->hidden = vis ? 0 : 1;
-		list[li]->Command(CMD_MRK_DIRTY, 0L, 0L);
-		list[li]->Command(CMD_REDRAW, 0L, 0L);
-		}
-	return false;
-}
-
-GraphObj*
-ObjTree::get_obj(int li)
-{
-	if(li < count && list[li]) return list[li];
-	return 0L;
-}
-
+//use_gui.cpp, Copyright 2000-2008 R.Lackner
+//
+//    This file is part of RLPlot.
+//
+//    RLPlot 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.
+//
+//    RLPlot 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 RLPlot; if not, write to the Free Software
+//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// This modules contains code for builds using a graphical user interface.
+// Builds running without GUI use no_gui.cpp instead.
+//
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//
+#include "rlplot.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+extern char TmpTxt[];
+extern Default defs;
+extern GraphObj *CurrGO, *TrackGO;			//Selected Graphic Objects
+extern Graph *CurrGraph;
+extern dragHandle *CurrHandle;
+extern UndoObj Undo;
+extern fmtText DrawFmtText;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Spread sheet buttons used for rows and columns
+ssButton::ssButton(GraphObj *par, int x, int y, int w, int h):GraphObj(par, 0L)
+{
+	bLBdown = bSelected = bMarked = false;
+	SetMinMaxRect(&rDims, x, y, x+w, y+h);
+	Line.width = 0.0f;				Line.patlength = 1.0f;
+	Line.color = 0x00000000L;		Line.pattern = 0x00000000L;
+	Fill.type = FILL_NONE;			Fill.color = 0x00e8e8e8L;
+	Fill.scale = 1.0;				Fill.hatch = NULL;
+	TextDef.ColTxt = 0x00000000L;	TextDef.ColBg = 0x00ffffffL;
+	TextDef.fSize = 4.0;			TextDef.RotBL = TextDef.RotCHAR = 0.0;
+	TextDef.iSize = 0;				TextDef.Align = TXA_HLEFT | TXA_VTOP;
+	TextDef.Mode = TXM_TRANSPARENT;	TextDef.Style = TXS_NORMAL;
+	TextDef.Font = FONT_HELVETICA;	TextDef.text = 0L;
+}
+
+ssButton::~ssButton()
+{
+	if(TextDef.text) free(TextDef.text);
+}
+
+void
+ssButton::DoPlot(anyOutput *o)
+{
+	POINT pts[3];
+
+	Line.color = 0x00000000L;
+	if(bSelected) {
+		Fill.color = bMarked ? 0x00ffffe0L : 0x00ffffffL;
+		}
+	else {
+		Fill.color = bMarked ? 0x00ffe0e0L : 0x00e8e8e8L;
+		}
+	o->SetLine(&Line);				o->SetFill(&Fill);
+	if(bLBdown){
+		o->oRectangle(rDims.left, rDims.top, rDims.right-1, rDims.bottom-1);
+		}
+	else {
+		o->oRectangle(rDims.left, rDims.top, rDims.right-2, rDims.bottom-2);
+		Line.color = 0x00000000L;
+		o->SetLine(&Line);
+		pts[0].x = rDims.left;					pts[0].y = pts[1].y = rDims.bottom-1;
+		pts[1].x = pts[2].x = rDims.right-1;	pts[2].y = rDims.top-1;
+		o->oPolyline(pts, 3);
+		Line.color = 0x00ffffffL;
+		o->SetLine(&Line);
+		pts[0].x = pts[1].x = rDims.left;		pts[0].y = rDims.bottom -3;
+		pts[1].y = pts[2].y = rDims.top;		pts[2].x = rDims.right -2;
+		o->oPolyline(pts, 3);
+		}
+	if(TextDef.text) {
+		o->SetTextSpec(&TextDef);
+#ifdef _WINDOWS
+		o->oTextOut((rDims.left + rDims.right)/2-2, (rDims.top + rDims.bottom)/2-1, TextDef.text, 0);
+#else
+		o->oTextOut((rDims.left + rDims.right)/2-2, (rDims.top + rDims.bottom)/2+1, TextDef.text, 0);
+#endif
+		}
+
+}
+void
+ssButton::DoMark(anyOutput *o, bool mark)
+{
+	bLBdown = mark;
+	DoPlot(o);
+	o->UpdateRect(&rDims, false);
+}
+
+bool
+ssButton::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	char *tmptxt;
+	MouseEvent *mev;
+
+	switch (cmd) {
+	case CMD_GETTEXT:
+		if(TextDef.text && tmpl) {
+			rlp_strcpy((char*)tmpl, 10, TextDef.text);
+			return true;
+			}
+		return false;
+	case CMD_SELECT:
+		if(tmpl && *((int*)tmpl)) bSelected = true;
+		else bSelected = false;
+		if(o) {
+			DoPlot(o);
+			o->UpdateRect(&rDims, false);
+			}
+		return true;
+	case CMD_SETSTYLE:
+		if(tmpl && *((int*)tmpl)) bMarked = true;
+		else bMarked = false;
+		if(o) {
+			DoPlot(o);		o->UpdateRect(&rDims, false);
+			}
+		return true;
+	case CMD_SETTEXT:
+		if(tmpl) {
+			if(! TextDef.text) TextDef.text = (char*)malloc(10*sizeof(char));
+			rlp_strcpy(TextDef.text, 10, (char*)tmpl);
+			}
+		return true;
+	case CMD_GETTEXTDEF:
+		if(!tmpl) return false;
+		memcpy(tmpl, &TextDef, sizeof(TextDEF));
+		return true;
+	case CMD_SETTEXTDEF:
+		if(!tmpl)return false;
+		tmptxt = TextDef.text;
+		memcpy(&TextDef, tmpl, sizeof(TextDEF));
+		TextDef.text = tmptxt;
+		return true;
+	case CMD_MOUSE_EVENT:
+		if(!o || !(mev = (MouseEvent *) tmpl)) break;
+		if(IsInRect(&rDims, mev->x, mev->y)) {
+			if(bLBdown) {
+				if(!(mev->StateFlags & 0x01)) {
+					o->HideMark();
+					if(bLBdown) {
+						bLBdown = false; DoPlot(o);
+						}
+					}
+				}
+			else if(mev->StateFlags & 0x01) {
+				o->ShowMark(this, MRK_SSB_DRAW);
+				}
+			return true;
+			}
+		break;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// drag handles are graphic objects which allow the user to interactively
+//   change the size of an object
+dragHandle::dragHandle(GraphObj *par, int which):GraphObj(par, 0L)
+{
+	type = which;
+	Id = GO_DRAGHANDLE;
+	LineDef.width = LineDef.patlength = 0.0;
+	LineDef.color = LineDef.pattern = 0L;
+	FillDef.type = FILL_NONE;
+	FillDef.color = 0x00ffffffL;
+	FillDef.scale = 1.0;
+	FillDef.hatch = 0L;
+	minRC = maxRC = 0L;
+}
+
+dragHandle::~dragHandle()
+{
+	if(minRC) free(minRC);
+	if(maxRC) free(maxRC);
+	if(CurrHandle == this) CurrHandle = 0L;
+}
+
+void
+dragHandle::DoPlot(anyOutput *o)
+{
+	double fx, fy, dx, dy;
+	int ix, iy, idx;
+	fPOINT3D fp3d, ifp3d;
+	bool is3D = false;
+
+	if(!o || !parent) return;
+	dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
+	SetMinMaxRect(&drec, o->co2ix(parent->GetSize(SIZE_XPOS)+dx), 
+		o->co2iy(parent->GetSize(SIZE_YPOS)+dy), o->co2ix(parent->GetSize(SIZE_XPOS+1)+dx),
+		o->co2iy(parent->GetSize(SIZE_YPOS+1)+dy));
+	switch(type) {
+	case DH_19:		case DH_12:
+		fx = parent->GetSize(SIZE_XPOS);	fy = parent->GetSize(SIZE_YPOS);
+		break;
+	case DH_99:		case DH_22:
+		fx = parent->GetSize(SIZE_XPOS+1);	fy = parent->GetSize(SIZE_YPOS+1);
+		break;
+	case DH_29:
+		fx = (parent->GetSize(SIZE_XPOS) + parent->GetSize(SIZE_XPOS+1))/2.0;
+		fy = parent->GetSize(SIZE_YPOS);
+		break;
+	case DH_39:
+		fx = parent->GetSize(SIZE_XPOS+1);	fy = parent->GetSize(SIZE_YPOS);
+		break;
+	case DH_49:
+		fx = parent->GetSize(SIZE_XPOS);
+		fy = (parent->GetSize(SIZE_YPOS) + parent->GetSize(SIZE_YPOS+1))/2.0;
+		break;
+	case DH_59:
+		fx = (parent->GetSize(SIZE_XPOS) + parent->GetSize(SIZE_XPOS+1))/2.0;
+		fy = (parent->GetSize(SIZE_YPOS) + parent->GetSize(SIZE_YPOS+1))/2.0;
+		break;
+	case DH_69:
+		fx = parent->GetSize(SIZE_XPOS+1);
+		fy = (parent->GetSize(SIZE_YPOS) + parent->GetSize(SIZE_YPOS+1))/2.0;
+		break;
+	case DH_79:
+		fx = parent->GetSize(SIZE_XPOS);
+		fy = parent->GetSize(SIZE_YPOS+1);
+		break;
+	case DH_89:
+		fx = (parent->GetSize(SIZE_XPOS) + parent->GetSize(SIZE_XPOS+1))/2.0;
+		fy = parent->GetSize(SIZE_YPOS+1);
+		break;
+	case DH_18:	case DH_28:	case DH_38:	case DH_48:
+	case DH_58:	case DH_68:	case DH_78:	case DH_88:
+		fp3d.fx = parent->GetSize(SIZE_XPOS + type - DH_18);
+		fp3d.fy = parent->GetSize(SIZE_YPOS + type - DH_18);
+		fp3d.fz = parent->GetSize(SIZE_ZPOS + type - DH_18);
+		is3D = true;
+		break;
+	default:
+		if(type >= DH_DATA) {
+			fx = parent->GetSize(SIZE_XPOS + type - DH_DATA);
+			fy = parent->GetSize(SIZE_YPOS + type - DH_DATA);
+			FillDef.color = (this == CurrHandle) ? 0x0L : 0x00ffffffL;
+			}
+		else return;
+		}
+	if(is3D) {
+		o->cvec2ivec(&fp3d, &ifp3d);
+		ix = iround(ifp3d.fx);		iy = iround(ifp3d.fy);
+		}
+	else {
+		ix = o->co2ix(fx+dx);	iy = o->co2iy(fy+dy);
+		}
+	SetMinMaxRect(&rDims, ix-4, iy-4, ix+4, iy+4);
+	memcpy(&upd, &rDims, sizeof(RECT));
+	switch(type) {
+	case DH_12:	case DH_22:	case DH_19:	case DH_29:	case DH_39:
+	case DH_49:	case DH_69:	case DH_79:	case DH_89:	case DH_99:
+		o->SetLine(&LineDef);		o->SetFill(&FillDef);
+		o->oRectangle(rDims.left, rDims.top, rDims.right, rDims.bottom);
+		o->UpdateRect(&rDims, false);
+		break;
+	case DH_59:
+		o->SetLine(&LineDef);		o->SetFill(&FillDef);
+		IncrementMinMaxRect(&rDims, 2);
+		o->oCircle(rDims.left, rDims.top, rDims.right, rDims.bottom);
+		IncrementMinMaxRect(&rDims, 1);
+		o->UpdateRect(&rDims, false);
+		break;
+	default:
+		if(type >= DH_DATA) {
+			idx = (type - DH_DATA);
+			o->SetLine(&LineDef);		o->SetFill(&FillDef);
+			if(parent->Id == GO_BEZIER && (idx %3)) {
+				IncrementMinMaxRect(&rDims, -1);
+				o->oRectangle(rDims.left, rDims.top, rDims.right, rDims.bottom);
+				}
+			else if(parent->Id == GO_POLYLINE || parent->Id == GO_BEZIER) {
+				o->oCircle(rDims.left, rDims.top, rDims.right, rDims.bottom);
+				}
+			else {
+				o->oRectangle(rDims.left, rDims.top, rDims.right, rDims.bottom);
+				}
+			o->UpdateRect(&rDims, false);
+			}
+		else {
+			IncrementMinMaxRect(&rDims, -1);
+#ifdef _WINDOWS
+			o->UpdateRect(&rDims, true);
+#else
+			o->ShowInvert(&rDims);
+#endif
+			}
+		break;
+		}
+}
+
+bool
+dragHandle::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	lfPOINT pos;
+	int idx;
+
+	if(!parent) return false;
+	switch (cmd) {
+	case CMD_MOUSECURSOR:
+		if(o) switch(type) {
+		case DH_19:	case DH_99:
+			o->MouseCursor(MC_SE, false);							break;
+		case DH_29:	case DH_89:
+			o->MouseCursor(MC_NORTH, false);						break;
+		case DH_39:	case DH_79:
+			o->MouseCursor(MC_NE, false);							break;
+		case DH_49:	case DH_69:
+			o->MouseCursor(MC_EAST, false);							break;
+		default:
+			if(type >= DH_DATA) o->MouseCursor(MC_SALL, false);
+			else o->MouseCursor(MC_MOVE, false);					break;
+			}
+		return true;
+	case CMD_MINRC:
+		if(!(minRC)) minRC = (RECT*)calloc(1, sizeof(RECT));
+		if(tmpl && minRC) SetMinMaxRect(minRC, ((RECT*)tmpl)->left, ((RECT*)tmpl)->top, 
+			((RECT*)tmpl)->right, ((RECT*)tmpl)->bottom);
+		return true;
+	case CMD_MAXRC:
+		if(!(maxRC)) maxRC = (RECT*)calloc(1, sizeof(RECT));
+		if(tmpl && maxRC) SetMinMaxRect(maxRC, ((RECT*)tmpl)->left, ((RECT*)tmpl)->top, 
+			((RECT*)tmpl)->right, ((RECT*)tmpl)->bottom);
+		return true;
+	case CMD_MOVE:
+		idx = type >= DH_DATA ? type - DH_DATA : 0;
+		pos.fx = NiceValue(((lfPOINT*)tmpl)[0].fx);
+		pos.fy = NiceValue(((lfPOINT*)tmpl)[0].fy);
+		if(pos.fx == 0.0 && pos.fy == 0.0) return false;
+		parent->Command(CMD_SAVEPOS, &idx, o);
+		switch(type) {
+		case DH_12:
+			parent->SetSize(SIZE_XPOS, pos.fx + parent->GetSize(SIZE_XPOS));
+			parent->SetSize(SIZE_YPOS, pos.fy + parent->GetSize(SIZE_YPOS));
+			break;
+		case DH_22:
+			parent->SetSize(SIZE_XPOS+1, pos.fx + parent->GetSize(SIZE_XPOS+1));
+			parent->SetSize(SIZE_YPOS+1, pos.fy + parent->GetSize(SIZE_YPOS+1));
+			break;
+		case DH_19: parent->parent->SetSize(SIZE_XPOS, 
+						pos.fx + parent->GetSize(SIZE_XPOS));
+		case DH_29: parent->parent->SetSize(SIZE_YPOS,
+						pos.fy + parent->GetSize(SIZE_YPOS));
+			break;
+		case DH_39: parent->parent->SetSize(SIZE_YPOS,
+						pos.fy + parent->GetSize(SIZE_YPOS));
+		case DH_69: parent->parent->SetSize(SIZE_XPOS+1,
+						pos.fx + parent->GetSize(SIZE_XPOS+1));
+			break;
+		case DH_99: parent->parent->SetSize(SIZE_XPOS+1, 
+						pos.fx + parent->GetSize(SIZE_XPOS+1));
+		case DH_89: parent->parent->SetSize(SIZE_YPOS+1,
+						pos.fy + parent->GetSize(SIZE_YPOS+1));
+			break;
+		case DH_79: parent->parent->SetSize(SIZE_YPOS+1,
+						pos.fy + parent->GetSize(SIZE_YPOS+1));
+		case DH_49:
+			parent->parent->SetSize(SIZE_XPOS, 
+						pos.fx + parent->GetSize(SIZE_XPOS));
+			break;
+		case DH_18:	case DH_28:	case DH_38:	case DH_48:
+		case DH_58:	case DH_68:	case DH_78:	case DH_88:
+		case DH_59:
+			return parent->parent->Command(cmd, tmpl, o);
+		default:
+			if (type >= DH_DATA) {
+				parent->SetSize(SIZE_XPOS + idx, pos.fx + parent->GetSize(SIZE_XPOS + idx));
+				parent->SetSize(SIZE_YPOS + idx, pos.fy + parent->GetSize(SIZE_YPOS + idx));
+				CurrGO = parent;
+				}
+			break;
+			}
+		return parent->Command(CMD_REDRAW, 0L, o);
+		break;
+		}
+	return false;
+}
+
+void *
+dragHandle::ObjThere(int x, int y)
+{
+	if(IsInRect(&rDims, x, y)) return (CurrHandle = this);
+	else return 0L;
+}
+
+
+void
+dragHandle::Track(POINT *p, anyOutput *o)
+{
+	POINT p1, p2, pts[5], bez[256];
+	double dx, dy;
+	int i, first=0, last = 0, idx;
+	long nbez;
+	DWORD color;
+
+	if(!parent || !o) return;
+	Command(CMD_MOUSECURSOR, 0L, o);
+	if(upd.right < upd.left) Swap(upd.right, upd.left);
+	if(upd.bottom < upd.top) Swap(upd.bottom, upd.top);
+	dx = parent->GetSize(SIZE_GRECT_LEFT);	dy = parent->GetSize(SIZE_GRECT_TOP);
+	IncrementMinMaxRect(&upd, 2);
+	o->UpdateRect(&upd, false);
+	if(type >= DH_19 && type <= DH_99) memcpy(&upd, &drec, sizeof(RECT));
+	color = parent->GetColor(COL_DATA_LINE);
+	switch(type) {
+	case DH_12:
+	case DH_22:
+		pts[0].x = o->co2ix(parent->GetSize(SIZE_XPOS)+dx);
+		pts[0].y = o->co2iy(parent->GetSize(SIZE_YPOS)+dy);
+		pts[1].x = o->co2ix(parent->GetSize(SIZE_XPOS+1)+dx);
+		pts[1].y = o->co2iy(parent->GetSize(SIZE_YPOS+1)+dy);
+		if(type == DH_12) {
+			pts[0].x += p->x;			pts[0].y += p->y;
+			}
+		else if(type == DH_22) {
+			pts[1].x += p->x;			pts[1].y += p->y;
+			}
+		UpdateMinMaxRect(&upd, pts[0].x, pts[0].y);
+		UpdateMinMaxRect(&upd, pts[1].x, pts[1].y);
+		last = 2;
+		break;
+	case DH_19:
+		if(minRC && IsInRect(minRC, upd.left+p->x, (upd.top+upd.bottom)>>1))
+			upd.left = minRC->left;
+		else if(maxRC && !IsInRect(maxRC, upd.left+p->x, (upd.top+upd.bottom)>>1))
+			upd.left = upd.left+p->x >= maxRC->right ? maxRC->right : maxRC->left;
+		else upd.left += p->x;
+	case DH_29:
+		if(minRC && IsInRect(minRC, (upd.left + upd.right)>>1, upd.top+p->y))
+			upd.top = minRC->top;
+		else if(maxRC && !IsInRect(maxRC, (upd.left + upd.right)>>1, upd.top+p->y))
+			upd.top = upd.top +p->y >= maxRC->bottom? maxRC->bottom : maxRC->top;
+		else upd.top += p->y;
+		break;
+	case DH_39:
+		if(minRC && IsInRect(minRC, (upd.left + upd.right)>>1, upd.top+p->y))
+			upd.top = minRC->top;
+		else if(maxRC && !IsInRect(maxRC, (upd.left + upd.right)>>1, upd.top+p->y))
+			upd.top = upd.top+p->y >= maxRC->bottom ? maxRC->bottom : maxRC->top;
+		else upd.top += p->y;
+	case DH_69:
+		if(minRC && IsInRect(minRC, upd.right+p->x, (upd.top+upd.bottom)>>1))
+			upd.right = minRC->right;
+		else if(maxRC && !IsInRect(maxRC, upd.right+p->x, (upd.top+upd.bottom)>>1))
+			upd.right = upd.right+p->x <= maxRC->left ? maxRC->left : maxRC->right;
+		else upd.right += p->x;
+		break;
+	case DH_99:
+		if(minRC && IsInRect(minRC, upd.right+p->x, (upd.top+upd.bottom)>>1))
+			upd.right = minRC->right;
+		else if(maxRC && !IsInRect(maxRC, upd.right+p->x, (upd.top+upd.bottom)>>1))
+			upd.right = upd.right+p->x <= maxRC->left ? maxRC->left : maxRC->right;
+		else upd.right += p->x;
+	case DH_89:
+		if(minRC && IsInRect(minRC, (upd.left + upd.right)>>1, upd.bottom+p->y))
+			upd.bottom = minRC->bottom;
+		else if(maxRC && !IsInRect(maxRC, (upd.left + upd.right)>>1, upd.bottom+p->y))
+			upd.bottom = upd.bottom+p->y <= maxRC->top? maxRC->top : maxRC->bottom;
+		else upd.bottom += p->y;
+		break;
+	case DH_79:
+		if(minRC && IsInRect(minRC, (upd.left + upd.right)>>1, upd.bottom+p->y))
+			upd.bottom = minRC->bottom;
+		else if(maxRC && !IsInRect(maxRC, (upd.left + upd.right)>>1, upd.bottom+p->y))
+			upd.bottom = upd.bottom+p->y <= maxRC->top? maxRC->top : maxRC->bottom;
+		else upd.bottom += p->y;
+	case DH_49:
+		if(minRC && IsInRect(minRC, upd.left+p->x, (upd.top+upd.bottom)>>1))
+			upd.left = minRC->left;
+		else if(maxRC && !IsInRect(maxRC, upd.left+p->x, (upd.top+upd.bottom)>>1))
+			upd.left = upd.left+p->x >= maxRC->right ? maxRC->right : maxRC->left;
+		else upd.left += p->x;
+		break;
+	case DH_18:	case DH_28:	case DH_38:	case DH_48:
+	case DH_58:	case DH_68:	case DH_78:	case DH_88:
+		CurrGO = this;
+	case DH_59:
+		parent->parent->Track(p, o);
+		return;
+	default:
+		if(type >= DH_DATA) {
+			idx = type - DH_DATA;
+			pts[1].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx)+dx);
+			pts[1].y = o->co2iy(parent->GetSize(SIZE_YPOS + idx)+dy);
+			pts[1].x += p->x;				pts[1].y += p->y;
+			if(type > DH_DATA) {
+				pts[0].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx -1)+dx);
+				pts[0].y = o->co2iy(parent->GetSize(SIZE_YPOS + idx -1)+dy);
+				}
+			else {
+				pts[0].x = pts[1].x;		pts[0].y = pts[1].y;
+				}
+			pts[3].x = pts[2].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx +1)+dx);
+			pts[3].y = pts[2].y = o->co2iy(parent->GetSize(SIZE_YPOS + idx +1)+dy);
+			UpdateMinMaxRect(&upd, pts[0].x, pts[0].y);
+			UpdateMinMaxRect(&upd, pts[1].x, pts[1].y);
+			UpdateMinMaxRect(&upd, pts[2].x, pts[2].y);
+			if(parent ->Id == GO_BEZIER) {
+				switch(idx % 3) {
+				case 0:
+					o->ShowLine(pts, 3, 0x00c0c0c0L);
+					pts[0].x = pts[1].x;		pts[0].y = pts[1].y;
+					for(nbez = 0, i = 1; i < 4; i++) {
+						pts[i].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx +i)+dx);
+						pts[i].y = o->co2iy(parent->GetSize(SIZE_YPOS + idx +i)+dy);
+						}
+					DrawBezier(&nbez, bez, pts[0], pts[1], pts[2], pts[3], 0);
+					o->ShowLine(bez, nbez, color);
+					if(idx < 3) return;
+					pts[3].x = pts[0].x;		pts[3].y = pts[0].y;
+					for(i = nbez = 0, idx -= 3; i < 3; i++) {
+						pts[i].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx +i)+dx);
+						pts[i].y = o->co2iy(parent->GetSize(SIZE_YPOS + idx +i)+dy);
+						}
+					DrawBezier(&nbez, bez, pts[0], pts[1], pts[2], pts[3], 0);
+					o->ShowLine(bez, nbez, color);
+					return;
+				case 1:		
+					pts[3].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx +2)+dx);
+					pts[3].y = o->co2iy(parent->GetSize(SIZE_YPOS + idx +2)+dy);
+					last = 2;				break;
+				case 2:	
+					pts[2].x = pts[1].x;	pts[2].y = pts[1].y;
+					pts[1].x = pts[0].x;	pts[1].y = pts[0].y;
+					pts[0].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx -2)+dx);
+					pts[0].y = o->co2iy(parent->GetSize(SIZE_YPOS + idx -2)+dy);
+					first = 2;	last = 4;	break;
+					}
+				if(idx %3) {
+					nbez = 0;
+					DrawBezier(&nbez, bez, pts[0], pts[1], pts[2], pts[3], 0);
+					o->ShowLine(bez, nbez, color);
+					}
+				color = 0x00c0c0c0L;
+				}
+			else last = 3;
+			if(color == 0x0L || color == 0x00ffffffL) color = 0x00c0c0c0L;
+			}
+		else return;
+		}
+	if(type >= DH_19 && type <= DH_99) {
+		pts[0].x = pts[4].x = pts[3].x = p1.x = upd.left;	
+		pts[0].y = pts[1].y = pts[4].y = p1.y = upd.top;
+		pts[1].x = pts[2].x = p2.x = upd.right;	
+		pts[2].y = pts[3].y = p2.y = upd.bottom;
+		last = 5;
+		if(parent->parent->Id == GO_ELLIPSE) o->ShowEllipse(p1, p2, color);
+		}
+	o->ShowLine(pts+first, last-first, color);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// the dragRect object uses nine dragHandles to create a user interface
+//   for modification of rectangular shapes
+dragRect::dragRect(GraphObj *par, int which):GraphObj(par, 0L)
+{
+	int i;
+
+	type = which;
+	Id = GO_DRAGRECT;
+	if(handles = (dragHandle**)calloc(9, sizeof(dragHandle*)))
+		for(i = 0; i < 9; i++){
+			if(i == 4 && type == 0) handles[i] = new dragHandle(this, DH_19 + i);
+			else if(i != 4) handles[i] = new dragHandle(this, DH_19 + i);
+			}
+}
+
+dragRect::~dragRect()
+{
+	int i;
+
+	if(handles) for(i = 0; i < 9; i++) if(handles[i]) DeleteGO(handles[i]);
+}
+
+void
+dragRect::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(handles) for(i = 0; i < 9; i++) if(handles[i]) handles[i]->DoPlot(o);
+}
+
+bool
+dragRect::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+
+	if(!parent) return false;
+	switch (cmd) {
+	case CMD_MINRC:
+	case CMD_MAXRC:
+		if(handles) for(i = 0; i < 9; i++) {
+			if(handles[i]) handles[i]->Command(cmd, tmpl, o);
+			}
+		break;
+	case CMD_SAVEPOS:
+	case CMD_REDRAW:
+		return parent->Command(cmd, tmpl, o);
+		}
+	return false;
+}
+
+void *
+dragRect::ObjThere(int x, int y)
+{
+	int i;
+	void *go;
+
+	if(handles)	for(i = 0; i < 9; i++) 
+		if(handles[i] && (go = (handles[i])->ObjThere(x, y))) return go;
+	return 0L;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// implement some kind of virtual trackball for 3D plots
+Drag3D::Drag3D(GraphObj *par):GraphObj(par, 0L)
+{
+	int i;
+
+	Id = GO_DRAG3D;
+	if(handles = (dragHandle**)calloc(8, sizeof(dragHandle*)))
+		for(i = 0; i < 8; i++){
+			handles[i] = new dragHandle(this, DH_18 + i);
+			}
+}
+
+Drag3D::~Drag3D()
+{
+	int i;
+
+	if(handles) for(i = 0; i < 8; i++) if(handles[i]) DeleteGO(handles[i]);
+}
+
+void
+Drag3D::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(handles) for(i = 0; i < 8; i++) if(handles[i]) handles[i]->DoPlot(o);
+}
+
+void *
+Drag3D::ObjThere(int x, int y)
+{
+	int i;
+	void *go;
+
+	if(handles)	for(i = 0; i < 8; i++) 
+		if(handles[i] && (go = (handles[i])->ObjThere(x, y))) return go;
+	return 0L;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// frame rectangle
+FrmRect::FrmRect(GraphObj *par, fRECT *lim, fRECT *c, fRECT *chld):GraphObj(par, 0L)
+{
+	parent = par;
+	limRC = lim;	cRC = c;	chldRC = chld;
+	drag = 0L;		mo = 0L;
+	Id = GO_FRAMERECT;
+	moveable = true;
+	Fill.type = FILL_NONE;
+	Line.color = FillLine.color = Fill.color = 0x00ffffffL;
+	Line.width = FillLine.width = 0.0;
+	Line.patlength = FillLine.patlength = Fill.scale = 1.0;
+	Line.pattern = FillLine.pattern = 0x0L;
+	Fill.hatch = &FillLine;
+	minRC = maxRC = 0L;
+}
+
+FrmRect::~FrmRect()
+{
+	if(drag) DeleteGO(drag);		drag = 0L;
+	if(minRC) free(minRC);			minRC = 0L;	
+	if(maxRC) free(maxRC);			maxRC = 0L;
+	if(mo) DelBitmapClass(mo);		mo = 0L;
+}
+
+double
+FrmRect::GetSize(int select)
+{
+	switch(select) {
+	case SIZE_XPOS:		return CurrRect.Xmin;
+	case SIZE_XPOS+1:	return CurrRect.Xmax;
+	case SIZE_YPOS:		return CurrRect.Ymin;
+	case SIZE_YPOS+1:	return CurrRect.Ymax;
+		}
+	return 0.0;
+}
+
+bool
+FrmRect::SetSize(int select, double value)
+{
+	double tmp, o_left, o_top;
+
+	o_left = cRC->Xmin;		o_top = cRC->Ymin;
+	switch (select & 0xfff) {
+	case SIZE_XPOS:
+		if(limRC) value -= limRC->Xmin;
+		if(swapX) cRC->Xmax = value;
+		else cRC->Xmin = value;
+		break;
+	case SIZE_XPOS+1:
+		if(limRC) value -= limRC->Xmin;
+		if(swapX) cRC->Xmin = value;
+		else cRC->Xmax = value;
+		break;
+	case SIZE_YPOS:
+		if(limRC) value -= limRC->Ymin;
+		if(swapY) cRC->Ymin = value;
+		else cRC->Ymax = value;
+		break;
+	case SIZE_YPOS+1:
+		if(limRC) value -= limRC->Ymin;
+		if(swapY) cRC->Ymax = value;
+		else cRC->Ymin = value;
+		break;
+	default: return false;
+		}
+	if((swapX && cRC->Xmin < cRC->Xmax) || (!swapX && cRC->Xmin > cRC->Xmax)) {
+		tmp = cRC->Xmin;	cRC->Xmin = cRC->Xmax;	cRC->Xmax = tmp;
+		}
+	if((swapY && cRC->Ymin > cRC->Ymax) || (!swapY && cRC->Ymin < cRC->Ymax)) {
+		tmp = cRC->Ymin;	cRC->Ymin = cRC->Ymax;	cRC->Ymax = tmp;
+		}
+	if(chldRC) {		//check if new rectangle is not inside child rectangle
+		if(cRC->Xmin > o_left+ chldRC->Xmin) cRC->Xmin = o_left + chldRC->Xmin;
+		if(cRC->Xmax < o_left+ chldRC->Xmax) cRC->Xmax = o_left + chldRC->Xmax;
+		if(cRC->Ymin > o_top+ chldRC->Ymin) cRC->Ymin = o_top + chldRC->Ymin;
+		if(cRC->Ymax < o_top+ chldRC->Ymax) cRC->Ymax = o_top + chldRC->Ymax;
+		}
+	if(chldRC && (o_left != cRC->Xmin || o_top != cRC->Ymin)) {
+		chldRC->Xmin -= (tmp = cRC->Xmin - o_left);		chldRC->Xmax -= tmp;
+		chldRC->Ymin -= (tmp = cRC->Ymin - o_top);		chldRC->Ymax -= tmp;
+		}
+	return true;
+}
+
+bool
+FrmRect::SetColor(int select, DWORD col)
+{
+	switch(select & 0xfff){
+	case COL_DRECT:
+		Line.color = col;
+	case COL_BG:
+		Fill.color = col;		return true;
+	case COL_GRECT:
+		Fill.color = col;		return true;
+	case COL_GRECTLINE:
+		Line.color = col;		return true;
+		}
+	return false;
+}
+
+void 
+FrmRect::DoMark(anyOutput *o, bool mark)
+{
+	if(!parent || !o) return;
+	if(!drag && (drag = new dragRect(this, (!limRC && parent->moveable) ? 0 : 1))){
+		if(minRC) drag->Command(CMD_MINRC, minRC, o);
+		if(maxRC) drag->Command(CMD_MAXRC, maxRC, o);
+		}
+	if(mark && drag){
+		if(mo) DelBitmapClass(mo);					mo = 0L;
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		IncrementMinMaxRect(&mrc, 6);
+		mo = GetRectBitmap(&mrc, o);
+		drag->DoPlot(o);
+		o->UpdateRect(&mrc, false);
+		}
+	else RestoreRectBitmap(&mo, &mrc, o);
+}
+
+void
+FrmRect::DoPlot(anyOutput *o)
+{
+	int x1, y1, x2, y2;
+	double tmp;
+
+	if(!(cRC) || !o) return;
+	o->dFillCol = Fill.color ^ 0x00ffffff;	//force new brush
+	o->dLineCol = Line.color ^ 0x00ffffff;	//force new pen
+	o->SetLine(&Line);						o->SetFill(&Fill);
+	CurrRect.Xmin = cRC->Xmin;				CurrRect.Xmax = cRC->Xmax;
+	CurrRect.Ymin = cRC->Ymax;				CurrRect.Ymax = cRC->Ymin;
+	if(limRC) {
+		CurrRect.Xmin += limRC->Xmin;		CurrRect.Xmax += limRC->Xmin;
+		CurrRect.Ymin += limRC->Ymin;		CurrRect.Ymax += limRC->Ymin;
+		}
+	if(swapX = (CurrRect.Xmin > CurrRect.Xmax)) {
+		tmp = CurrRect.Xmin;	CurrRect.Xmin = CurrRect.Xmax;	CurrRect.Xmax = tmp;
+		}
+	if(swapY = (CurrRect.Ymin > CurrRect.Ymax)) {
+		tmp = CurrRect.Ymin;	CurrRect.Ymin = CurrRect.Ymax;	CurrRect.Ymax = tmp;
+		}
+	o->oRectangle(x1 = o->co2ix(CurrRect.Xmin), y1 = o->co2iy(CurrRect.Ymin), 
+		x2 = o->co2ix(CurrRect.Xmax), y2 = o->co2iy(CurrRect.Ymax));
+	SetMinMaxRect(&rDims, x1, y1, x2, y2);
+}
+
+bool
+FrmRect::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+
+	if(!parent) return false;
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *) tmpl;
+		switch (mev->Action) {
+		case MOUSE_LBUP:
+			if(IsInRect(&rDims, mev->x, mev->y) && !(CurrGO) && (o)){
+				o->ShowMark(this, MRK_GODRAW);
+				if(!CurrGraph && parent && parent->Id == GO_GRAPH) CurrGraph = (Graph*)parent;
+				return true;
+				}
+			}
+		return false;
+	case CMD_MINRC:
+		if(!(minRC)) minRC = (RECT*)calloc(1, sizeof(RECT));
+		if(minRC && tmpl) SetMinMaxRect(minRC, ((RECT*)tmpl)->left, ((RECT*)tmpl)->top, 
+			((RECT*)tmpl)->right, ((RECT*)tmpl)->bottom);
+		if(drag) drag->Command(cmd, tmpl, o);
+		return true;
+	case CMD_MAXRC:
+		if(!(maxRC)) maxRC = (RECT*)calloc(1, sizeof(RECT));
+		if(maxRC && tmpl) SetMinMaxRect(maxRC, ((RECT*)tmpl)->left, ((RECT*)tmpl)->top, 
+			((RECT*)tmpl)->right, ((RECT*)tmpl)->bottom);
+		if(drag) drag->Command(cmd, tmpl, o);
+		return true;
+	case CMD_MOVE:
+		cRC->Xmin += NiceValue(((lfPOINT*)tmpl)[0].fx);
+		cRC->Ymin += NiceValue(((lfPOINT*)tmpl)[0].fy);
+		cRC->Xmax += NiceValue(((lfPOINT*)tmpl)[0].fx);
+		cRC->Ymax += NiceValue(((lfPOINT*)tmpl)[0].fy);
+	case CMD_REDRAW:
+		return parent->Command(CMD_REDRAW, 0L, o);
+	case CMD_SAVEPOS:
+		return parent->Command(cmd, tmpl, o);
+	case CMD_SETCHILD:
+		chldRC = (fRECT*)tmpl;
+		return true;
+		}
+	return false;
+}
+
+void *
+FrmRect::ObjThere(int x, int y)
+{
+	if(drag) return drag->ObjThere(x, y);
+	return 0L;
+}
+
+void
+FrmRect::Track(POINT *p, anyOutput *o)
+{
+	POINT tpts[5];
+	RECT old_rc;
+
+	if(o){
+		memcpy(&old_rc, &rDims, sizeof(rDims));
+		o->UpdateRect(&rDims, false);
+		tpts[0].x = tpts[1].x = tpts[4].x = o->co2ix(cRC->Xmin)+p->x;		
+		tpts[0].y = tpts[3].y = tpts[4].y = o->co2iy(cRC->Ymin)+p->y;
+		tpts[1].y = tpts[2].y = o->co2iy(cRC->Ymax)+p->y;
+		tpts[2].x = tpts[3].x = o->co2ix(cRC->Xmax)+p->x;
+		UpdateMinMaxRect(&rDims, tpts[0].x, tpts[0].y);
+		UpdateMinMaxRect(&rDims, tpts[2].x, tpts[2].y);	
+		if(old_rc.left != rDims.left || old_rc.right != rDims.right || old_rc.top !=
+			rDims.top || old_rc.bottom != rDims.bottom)IncrementMinMaxRect(&rDims, 3);
+		o->ShowLine(tpts, 5, Fill.color ^ 0x00ffffffL);
+		}
+
+}
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void * 
+Arrow::ObjThere(int x, int y)
+{
+	if(!IsInRect(&rDims, x, y)) return 0L;
+	if(IsCloseToLine(&pts[0], &pts[1], x, y) ||
+		IsCloseToLine(&pts[2], &pts[3], x, y) ||
+		IsCloseToLine(&pts[3], &pts[4], x, y)){
+		if(dh1 && dh1->ObjThere(x, y)) return dh1;
+		if(dh2 && dh2->ObjThere(x, y)) return dh2;
+		return this;
+		}
+	return 0L;
+}
+
+void
+Arrow::Track(POINT *p, anyOutput *o)
+{
+	POINT *tpts;
+	RECT old_rc;
+	int i;
+
+	if(o && (tpts = (POINT*)malloc(6*sizeof(POINT)))){
+		memcpy(&old_rc, &rDims, sizeof(rDims));
+		o->UpdateRect(&rDims, false);
+		for(i = 0; i < 5; i++) {
+			tpts[i].x = pts[i].x+p->x;
+			tpts[i].y = pts[i].y+p->y;
+			UpdateMinMaxRect(&rDims, tpts[i].x, tpts[i].y);
+			}
+		switch(type & 0xff) {
+		case ARROW_LINE:
+			o->ShowLine(tpts+2, 3, LineDef.color);
+		case ARROW_NOCAP:
+			o->ShowLine(tpts, 2, LineDef.color);
+			break;
+		case ARROW_TRIANGLE:
+			tpts[5].x = tpts[2].x;		tpts[5].y = tpts[2].y;
+			o->ShowLine(tpts+2, 4, LineDef.color);
+			tpts[1].x = (tpts[2].x + tpts[4].x)>>1;
+			tpts[1].y = (tpts[2].y + tpts[4].y)>>1;
+			o->ShowLine(tpts, 2, LineDef.color);
+			break;
+			}
+		if(old_rc.left != rDims.left || old_rc.right != rDims.right || old_rc.top !=
+			rDims.top || old_rc.bottom != rDims.bottom)IncrementMinMaxRect(&rDims, 3);
+		free(tpts);
+		}
+}
+
+void
+Label::ShowCursor(anyOutput *o)
+{
+	if(o) {
+		CalcRect(o);
+		if((o->OC_type & 0xff) == OC_BITMAP) ShowTextCursor(o, &Cursor, TextDef.ColTxt);
+		}
+}
+
+bool
+Label::AddChar(int ci, anyOutput *o)
+{
+	char c, *txt1 = 0L;
+	int i, j, k;
+	GraphObj *golist[2];
+	DWORD flags;
+
+	if(!o || (ci != 13 && ci < 32)) return false;
+	if(CheckMark()) {
+		Undo.String(this, &TextDef.text, 0L);
+		Undo.ValInt(this, &m1, UNDO_CONTINUE);
+		Undo.ValInt(this, &m2, UNDO_CONTINUE);
+		rlp_strcpy(TextDef.text+m1, (int)strlen(TextDef.text+m1)+1, TextDef.text+m2);
+		CursorPos = m1;		flags = UNDO_CONTINUE;
+		}
+	else flags = 0L;
+	m1 = m2 = -1;
+	if(ci == 13 && parent){		//CR
+		if(parent->Id == GO_MLABEL){
+			parent->Command(CMD_SETFOCUS, this, o);
+			return parent->Command(CMD_ADDCHAR, &ci, o);
+			}
+		if(golist[1] = new mLabel(parent, data, fPos.fx, fPos.fy, &TextDef, 
+			TextDef.text, CursorPos, flags)){
+			golist[1]->moveable = moveable;
+			golist[1]->SetSize(SIZE_LB_XDIST, fDist.fx);
+			golist[1]->SetSize(SIZE_LB_YDIST, fDist.fy);
+			}
+		golist[0] = this;
+		if(!parent->Command(CMD_MUTATE, golist, o)) DeleteGO(golist[1]);
+		return false;
+		}
+	if(TextDef.text && TextDef.text[0]) i = (int)strlen(TextDef.text);
+	else i = 0;		if(CursorPos > i)CursorPos = i;
+	if(ci > 254) {
+		Undo.String(this, &TextDef.text, flags);
+		Undo.ValInt(this, &CursorPos, flags = UNDO_CONTINUE);
+		txt1 = (char*)malloc((i+12)*sizeof(char));
+		if(txt1) {
+			if(j=CursorPos) k = rlp_strcpy(txt1, CursorPos+1, TextDef.text);
+			else k = 0;
+#ifdef USE_WIN_SECURE
+			k += sprintf_s(txt1+k, i+12-k, "&#%d;",ci);
+#else
+			k += sprintf(txt1+k, "&#%d;",ci);
+#endif
+			CursorPos = k;
+			k += rlp_strcpy(txt1+k, i+12-k, TextDef.text+j);
+			if(TextDef.text) free(TextDef.text);
+			TextDef.text = txt1;			RedrawEdit(o);
+			}
+		return true;
+		}
+	else if( ci > 31) c = (char)ci;
+	else return false;
+	if(!TextDef.text || CursorPos < 10 || c == ' ') {
+		Undo.String(this, &TextDef.text, flags);
+		Undo.ValInt(this, &CursorPos, flags = UNDO_CONTINUE);
+		}
+	txt1 = (char*)malloc((i+4)* sizeof(char));
+	if(txt1) {
+		if(j=CursorPos) k = rlp_strcpy(txt1, CursorPos+1, TextDef.text);
+		else k = 0;			txt1[k++] = c;
+		k += rlp_strcpy(txt1+k, i+12-k, TextDef.text+j);
+		if(TextDef.text) free(TextDef.text);
+		TextDef.text = txt1;	txt1[k] = 0;
+		CursorPos++;			RedrawEdit(o);
+		}
+	return true;
+}
+
+void
+Label::CalcCursorPos(int x, int y, anyOutput *o)
+{
+	int i, j, ix, x1, y1, x2, h;
+
+	if(!o || !TextDef.text) return;
+	TextDef.iSize = o->un2iy(TextDef.fSize);
+	if(parent && parent->Id != GO_MLABEL) o->SetTextSpec(&TextDef);
+	x1 = ((pts[3].x + pts[4].x)>>1);	y1 = ((pts[3].y + pts[4].y)>>1);
+	ix = ((j=(x1-x))*j);	ix += ((j=(y1-y))*j);	ix = isqr(ix);
+	for(i = 1,  x1 = 0; TextDef.text[i-1]; i++) {
+		DrawFmtText.SetText(0L, TextDef.text, 0L, 0L);
+		DrawFmtText.oGetTextExtent(o, &x2, &h, i);
+		if(x1 <= ix && x2 >= ix) {
+			if((ix-x1) < (x2-ix)) CursorPos = i-1;
+			else CursorPos = i;
+			return;
+			}
+		else if(ix < x2){
+			CursorPos = i-1;
+			return;
+			}
+		x1 = x2;
+		}
+	if(pts[3].x < pts[2].x && x < pts[3].x) CursorPos = 0;
+	else CursorPos = (int)strlen(TextDef.text);
+}
+
+void 
+TextFrame::ShowCursor(anyOutput *o)
+{
+	int cx, cy, w, h;
+
+	if(!o || (o->OC_type & 0xff) != OC_BITMAP) return;
+	TextDef.iSize = o->un2iy(TextDef.fSize);
+#ifdef _WINDOWS
+	linc = o->un2iy(TextDef.fSize*lspc);
+#else
+	linc = o->un2iy(TextDef.fSize*lspc*1.2);
+#endif
+	o->SetTextSpec(&TextDef);
+	cy = rDims.top + linc * cur_pos.y;			cx = rDims.left;
+	cx += ipad.left;							cy += ipad.top;
+	if(cur_pos.y < nlines) {
+		if(lines[cur_pos.y] && lines[cur_pos.y][0] && cur_pos.x) {
+			fmt_txt.SetText(0L, (char*)lines[cur_pos.y], &cx, &cy);
+			if(!fmt_txt.oGetTextExtent(o, &w, &h, cur_pos.x))return;
+			}
+		else {
+			if(!o->oGetTextExtent("A", 1, &w, &h))return;	w = 0;
+			}
+		Cursor.left = cx+w;		Cursor.right = cx+w;
+		Cursor.top = cy;		Cursor.bottom = cy+linc;
+		ShowTextCursor(o, &Cursor, TextDef.ColTxt);
+		}
+}
+
+void
+TextFrame::CalcCursorPos(int x, int y, anyOutput *o)
+{
+	int i, iy, ix, x1, x2, h;
+
+	if(!o) return;
+	TextDef.iSize = o->un2iy(TextDef.fSize);
+	o->SetTextSpec(&TextDef);
+	iy = (y-rDims.top-ipad.top)/linc;		x1 = x2 = 0;
+	if(iy >= nlines) iy = nlines-1;			cur_pos.y = iy;
+	x2 = rDims.right - rDims.left -ipad.left - ipad.right;
+	x = x - rDims.left -ipad.left;
+	fmt_txt.SetText(0L, (char*)lines[iy], &x1, &x2);
+	for(i = 0,  ix = x1 = 0; lines[iy][i]; i++) {
+		if(i) fmt_txt.oGetTextExtent(o, &x1, &h, i);
+		x1 -= (TextDef.iSize >> 2);
+		if(i && x1 <= x2 && x1 >= x) {
+			if(i >1) fmt_txt.oGetTextExtent(o, &x1, &h, i-1);
+			else x1 = 0;
+			fmt_txt.oGetTextExtent(o, &x2, &h, i);
+			ix = (abs(x1-x)<abs(x2-x)) ? i = (i-1) : i;
+			break;
+			}
+		}
+	if(ix) cur_pos.x = ix;
+	else cur_pos.x = i;
+}
+
+void
+Axis::DoMark(anyOutput *o, bool mark)
+{
+	LineDEF mrkLine;
+	double minw = DefSize(SIZE_DATA_LINE);
+	POINT *arc;
+	long narc = 0;
+
+	if(axis->flags & AXIS_ANGULAR) {
+		if(mark) {
+			if(mo) DelBitmapClass(mo);					mo = 0L;
+			o->GetSize(&mrc);						mo = GetRectBitmap(&mrc, o);
+			memcpy(&mrkLine, &axline, sizeof(LineDEF));
+			mrkLine.width = axline.width < minw ? minw * 3.0 : axline.width *3.0;
+			o->SetLine(&mrkLine);
+			if(arc = MakeArc(pts[0].x, pts[0].y, pts[1].x, 0x0f, &narc)) {
+				o->oPolyline(arc, (int)narc, 0L);
+				mrkLine.color ^= 0x00ffffffL;		mrkLine.width = axline.width;
+				o->SetLine(&mrkLine);				o->oPolyline(arc, (int)narc, 0L);
+				free(arc);
+				}
+			o->UpdateRect(&mrc, false);
+			}
+		else RestoreRectBitmap(&mo, &mrc, o);
+		return;
+		}
+	if(mark){
+		if(mo) DelBitmapClass(mo);					mo = 0L;
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		IncrementMinMaxRect(&mrc, 6 + o->un2ix(axline.width));
+		mo = GetRectBitmap(&mrc, o);
+		if(type & 0x04) InvertLine(gradient_box, 5, &axline, &rDims, o, mark);
+		else InvertLine(pts, 2, &axline, &rDims, o, mark);
+		o->UpdateRect(&mrc, false);
+		}
+	else RestoreRectBitmap(&mo, &mrc, o);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Graphs are graphic objects containing plots, axes, and drawn objects
+bool
+Graph::ExecTool(MouseEvent *mev)
+{
+	static POINT pl = {0, 0}, pc = {0, 0};
+	static DWORD color = 0L;
+	scaleINFO scale;
+	POINT line[5];
+	GraphObj **tmpPlots, *new_go;
+	TextDEF *td;
+	lfPOINT *lfp, lf;
+	int i, j;
+	double x, y;
+
+	if(!mev || !CurrDisp) return false;
+	td = 0L;	if(mev->Action == MOUSE_LBUP) SuspendAnimation(CurrDisp, false);
+	if(ToolMode & TM_MOVE) switch (mev->Action) {
+		case MOUSE_LBDOWN:
+			pl.x = pc.x = mev->x;				pl.y = pc.y = mev->y;
+			if(TrackGO && TrackGO->Command(CMD_MOUSECURSOR, 0L, CurrDisp)) return true;
+			CurrDisp->HideMark();			CurrDisp->MouseCursor(MC_MOVE, false);
+			return true;
+		case MOUSE_MOVE:
+			defs.Idle(CMD_UPDATE);
+			if(TrackGO) {
+				if(TrackGO->Id == GO_DRAGHANDLE && TrackGO->type >= DH_18 &&
+					TrackGO->type <= DH_88) {
+					lf.fx = CurrDisp->fix2un((double)(mev->x-pl.x));	
+					lf.fy = CurrDisp->fiy2un((double)(mev->y-pl.y));
+					TrackGO->Track((POINT*)&lf, CurrDisp);
+					}
+				else {
+					pc.x = mev->x-pl.x;				pc.y = mev->y-pl.y;
+					TrackGO->Track(&pc, CurrDisp);
+					}
+				}
+			return true;
+		case MOUSE_LBUP:
+			if(TrackGO) {
+				ToolMode &= ~TM_MOVE;
+				pc.x = mev->x-pl.x;				pc.y = mev->y-pl.y;
+				if(!pc.x && !pc.y){
+					Command(CMD_TOOLMODE, (void*)(& ToolMode), CurrDisp);
+					return false;
+					}
+				TrackGO->Track(&pc, CurrDisp);
+				lf.fx = CurrDisp->fix2un((double)(mev->x-pl.x));	
+				lf.fy = CurrDisp->fiy2un((double)(mev->y-pl.y));
+				TrackGO->Command(CMD_MOVE, &lf, CurrDisp);
+				bModified = true;
+				}
+			Command(CMD_TOOLMODE, (void*)(& ToolMode), CurrDisp);
+			return true;
+		default:
+			return true;
+		}
+	if(ToolMode == TM_MARK || ToolMode == TM_ZOOMIN) {
+		switch (mev->Action) {
+		case MOUSE_LBDOWN:			//we should not receive that !
+			rc_mrk.left = mev->x;	rc_mrk.top = mev->y;
+			return false;
+		case MOUSE_LBUP:
+			j = (i = rc_mrk.left - mev->x)*i;
+			j += ((i = rc_mrk.top - mev->y)*i);
+			if(j < 20) {						//glitch
+				rc_mrk.left = rc_mrk.right = rc_mrk.top = rc_mrk.bottom = 0;
+				ToolMode = TM_STANDARD;		CurrDisp->MouseCursor(MC_ARROW, false);
+				return false;
+				}
+			if(ToolMode == TM_ZOOMIN) {
+				rc_mrk.right = mev->x;	rc_mrk.bottom = mev->y;
+				ToolMode = TM_STANDARD;
+				return Command(CMD_ZOOM, (void*) &"+", 0L);
+				}
+		default:
+			ToolMode = TM_STANDARD;
+		case MOUSE_MOVE:
+			if((mev->StateFlags &1) && rc_mrk.left >= 0 && rc_mrk.top >= 0) {
+				if(rcUpd.left != rcUpd.right && rcUpd.top != rcUpd.bottom)
+					CurrDisp->UpdateRect(&rcUpd, false);
+				line[0].x = line[3].x = line[4].x = rc_mrk.left;
+				line[0].y = line[1].y = line[4].y = rc_mrk.top;
+				line[1].x = line[2].x = mev->x;
+				line[2].y = line[3].y = mev->y;
+				CurrDisp->ShowLine(line, 5, 0x00c0c0c0L);
+				rcUpd.left = rcUpd.right = rc_mrk.left;
+				rcUpd.top = rcUpd.bottom = rc_mrk.top;
+				UpdateMinMaxRect(&rcUpd, rc_mrk.right = mev->x, rc_mrk.bottom = mev->y);
+				IncrementMinMaxRect(&rcUpd,2);
+				}
+			return true;
+			}
+		}
+	if(ToolMode == TM_PASTE) {
+		switch (mev->Action) {
+		case MOUSE_LBDOWN:
+			CurrGO = 0L;
+			CurrDisp->MrkMode = MRK_NONE;
+			pl.x = pc.x = mev->x;		pl.y = pc.y = mev->y;
+			rcDim.left = rcDim.right = rcUpd.left = rcUpd.right = mev->x;
+			rcDim.top = rcDim.bottom = rcUpd.top = rcUpd.bottom = mev->y;
+			return true;
+		case MOUSE_MOVE:
+			if(mev->StateFlags &1) {
+				if(rcUpd.left != rcUpd.right && rcUpd.top != rcUpd.bottom)
+					CurrDisp->UpdateRect(&rcUpd, false);
+				line[0].x = line[4].x = pl.x;	line[0].y = line[4].y = pl.y;
+				line[1].x = pc.x = mev->x;		line[1].y = pl.y;
+				line[2].x = mev->x;				line[2].y = pc.y = mev->y;
+				line[3].x = pl.x;				line[3].y = mev->y;
+				CurrDisp->ShowLine(line, 5, 0x00c0c0c0L);
+				memcpy(&rcUpd, &rcDim, sizeof(RECT));
+				UpdateMinMaxRect(&rcUpd, mev->x, mev->y);
+				IncrementMinMaxRect(&rcUpd, 2);
+				return true;
+				}
+			break;
+		case MOUSE_LBUP:
+			if(!PasteObj) return false;
+			pc.x = mev->x;			pc.y = mev->y;
+			if((lfp = (lfPOINT*)malloc(2 * sizeof(lfPOINT)))){
+				lfp[0].fx = CurrDisp->fix2un(pl.x - CurrDisp->VPorg.fx);
+				lfp[0].fy = CurrDisp->fiy2un(pl.y - CurrDisp->VPorg.fy);
+				lfp[1].fx = CurrDisp->fix2un(pc.x - CurrDisp->VPorg.fx);
+				lfp[1].fy = CurrDisp->fiy2un(pc.y - CurrDisp->VPorg.fy);
+				if(lfp[0].fx > lfp[1].fx) {
+					x = lfp[0].fx;		lfp[0].fx = lfp[1].fx;		lfp[1].fx = x;
+					}
+				if(lfp[0].fy > lfp[1].fy) {
+					y = lfp[0].fy;		lfp[0].fy = lfp[1].fy;		lfp[1].fy = y;
+					}
+				}
+			else {
+				DeleteGO(PasteObj);			PasteObj = 0L;
+				ToolMode = TM_STANDARD;		CurrDisp->MouseCursor(MC_ARROW, false);
+				return true;
+				}
+			scale.sx.fx = lfp[0].fx;	scale.sy.fx = lfp[0].fy;	scale.sz.fx = 0.0;
+			scale.sx.fy = scale.sy.fy = scale.sz.fy = 1.0;
+			if(PasteObj->Id == GO_GRAPH) {
+				if(abs(pc.x -pl.x) > 20 && abs(pc.y -pl.y) >20) {
+					x = ((Graph*)PasteObj)->GRect.Xmax - ((Graph*)PasteObj)->GRect.Xmin;
+					y = ((Graph*)PasteObj)->GRect.Ymax - ((Graph*)PasteObj)->GRect.Ymin;
+					scale.sx.fy = (lfp[1].fx - lfp[0].fx)/x;	scale.sy.fy = (lfp[1].fy - lfp[0].fy)/y;
+					scale.sx.fy = (scale.sx.fy + scale.sy.fy) /2.0;		//preserve aspect ratio
+					scale.sy.fy = scale.sx.fy;
+					}
+				((Graph*)PasteObj)->GRect.Xmax -= ((Graph*)PasteObj)->GRect.Xmin;
+				((Graph*)PasteObj)->GRect.Ymax -= ((Graph*)PasteObj)->GRect.Ymin;
+				((Graph*)PasteObj)->GRect.Ymin = ((Graph*)PasteObj)->GRect.Xmin = 0.0;
+				PasteObj->Command(CMD_SCALE, &scale, 0L);
+				PasteObj->moveable = 1;
+				Command(CMD_DROP_GRAPH, (void*)PasteObj, 0L);
+				}
+			else {
+				anyOutput *ScaleOut;
+				
+				if(ScaleOut = NewBitmapClass(10, 10, CurrDisp->hres, CurrDisp->vres)) {
+					PasteObj->parent = this;	PasteObj->DoPlot(ScaleOut);
+					switch(PasteObj->Id){
+					case GO_POLYLINE:		case GO_POLYGON:
+						IncrementMinMaxRect(&PasteObj->rDims, -(3*CurrDisp->un2ix(((polyline*)PasteObj)->pgLine.width)+3));
+						break;
+					case GO_BEZIER:
+						IncrementMinMaxRect(&PasteObj->rDims, 3);
+						break;
+						}
+					scale.sx.fx = CurrDisp->fix2un(-PasteObj->rDims.left);
+					scale.sy.fx = CurrDisp->fiy2un(-PasteObj->rDims.top);
+					PasteObj->Command(CMD_SCALE, &scale, 0L);
+					scale.sx.fx = lfp[0].fx;	scale.sy.fx = lfp[0].fy;	scale.sz.fx = 0.0;
+					if(abs(pc.x -pl.x) > 8 && abs(pc.y -pl.y) > 8) {
+						x = CurrDisp->fix2un(PasteObj->rDims.right - PasteObj->rDims.left);
+						y = CurrDisp->fiy2un(PasteObj->rDims.bottom - PasteObj->rDims.top);
+						scale.sx.fy = (lfp[1].fx - lfp[0].fx)/x;	
+						scale.sy.fy = (lfp[1].fy - lfp[0].fy)/y;
+						}
+					PasteObj->Command(CMD_SCALE, &scale, 0L);
+					delete ScaleOut;
+					}
+				Command(CMD_DROP_GRAPH, (void*)PasteObj, 0L);
+				}
+			PasteObj = 0L;
+			ToolMode = TM_STANDARD;			CurrDisp->MouseCursor(MC_ARROW, false);
+			break;
+			}
+		return true;
+		}
+	if(NumPlots && !Plots[NumPlots-1]) {
+		Undo.StoreListGO(this, &Plots, &NumPlots, UNDO_CONTINUE);
+		for(i = j = 0; i < NumPlots; i++) if(Plots[i]) Plots[j++] = Plots[i];
+		NumPlots = j;
+		}
+	else if(!Plots && !(Plots = (GraphObj**)calloc(2, sizeof(GraphObj*))))return false;
+	else if(Plots[NumPlots]){
+		if(tmpPlots = (GraphObj**)memdup(Plots, (NumPlots+2) * sizeof(GraphObj*), 0L)){
+			Undo.ListGOmoved(Plots, tmpPlots, NumPlots);
+			free(Plots);	Plots = tmpPlots;
+			Plots[NumPlots] = Plots[NumPlots+1] = 0L;
+			}
+		else return false;
+		}
+	switch(ToolMode & 0x0f) {
+	case TM_DRAW:
+		switch (mev->Action) {
+		case MOUSE_LBDOWN:
+			CurrGO = 0L;
+			CurrDisp->MrkMode = MRK_NONE;
+			Command(CMD_REDRAW, 0L, CurrDisp);
+			color = defs.Color(COL_POLYLINE);
+			pl.x = pc.x = mev->x;		pl.y = pc.y = mev->y;
+			if(tl_pts) free(tl_pts);
+			tl_nPts = 0;
+			if(tl_pts = (POINT*)malloc(4096 * sizeof(POINT)))
+				AddToPolygon(&tl_nPts, tl_pts, &pc);
+			return true;
+		case MOUSE_LBUP:
+			pl.x = mev->x;				pl.y = mev->y;
+			if(tl_pts) AddToPolygon(&tl_nPts, tl_pts, &pc);
+			// create line object
+			if(tl_pts && tl_nPts >1) {			//DEBUG: check for plausibility
+				if(lfp = (lfPOINT*)malloc(tl_nPts * sizeof(lfPOINT))){
+					for(i = 0; i < tl_nPts; i++) {
+						lfp[i].fx = CurrDisp->fix2un(tl_pts[i].x - CurrDisp->VPorg.fx);	
+						lfp[i].fy = CurrDisp->fiy2un(tl_pts[i].y - CurrDisp->VPorg.fy);
+						}
+					if(Plots[NumPlots]) i = NumPlots+1;
+					else i = NumPlots;
+//					Undo.SetGO(this, &Plots[i], new polyline(this, data, lfp, (int)tl_nPts), 0L);
+					Undo.SetGO(this, &Plots[i], new Bezier(this, data, lfp, (int)tl_nPts, 0,
+							(CurrDisp->hres/CurrDisp->VPscale+CurrDisp->vres/CurrDisp->VPscale)/2.0), 0L);
+					if(Plots[i]){
+						NumPlots = i+1;
+						Plots[i]->moveable = 1;
+						Plots[i]->DoPlot(CurrDisp);							//init
+						CurrDisp->ShowMark(CurrGO = Plots[i], MRK_GODRAW);	//edit
+						bModified = true;
+						}
+					free(lfp);
+					}
+				if(tl_pts) free(tl_pts);	tl_pts = 0L;	tl_nPts = 0;
+				return true;
+				}
+			if(tl_pts) free(tl_pts);	tl_pts = 0L;	tl_nPts = 0;
+			return false;
+		case MOUSE_MOVE:
+			if((mev->StateFlags &1) && tl_pts && tl_nPts < 4095) {
+				memcpy(&pl, &pc, sizeof(POINT));
+				line[1].x = pc.x = mev->x;		line[1].y = pc.y = mev->y;
+				line[0].x = pl.x;				line[0].y = pl.y;
+				AddToPolygon(&tl_nPts, tl_pts, &pc);
+#ifdef _WINDOWS
+				CurrDisp->ShowLine(line, 2, color);
+#else
+				CurrDisp->ShowLine(tl_pts, tl_nPts, color);
+#endif
+				return true;
+				}
+			break;
+			}
+		break;
+	case TM_POLYLINE:	case TM_POLYGON:
+		switch (mev->Action) {
+		case MOUSE_LBDOWN:
+			if(!tl_pts) {
+				CurrGO = 0L;
+				CurrDisp->MrkMode = MRK_NONE;
+				Command(CMD_REDRAW, 0L, CurrDisp);
+				color = defs.Color((ToolMode & 0x0f) == TM_POLYLINE ? COL_POLYLINE : COL_POLYGON);
+				pl.x = pc.x = mev->x;		pl.y = pc.y = mev->y;
+				tl_nPts = 0;
+				if(tl_pts = (POINT*)malloc(4096 * sizeof(POINT)))
+				AddToPolygon(&tl_nPts, tl_pts, &pc);
+				rcDim.left = rcDim.right = rcUpd.left = rcUpd.right = mev->x;
+				rcDim.top = rcDim.bottom = rcUpd.top = rcUpd.bottom = mev->y;
+				}
+			return true;
+		case MOUSE_MOVE:
+			if(tl_pts && tl_nPts) {
+				if(rcUpd.left != rcUpd.right && rcUpd.top != rcUpd.bottom)
+					CurrDisp->UpdateRect(&rcUpd, false);
+				CurrDisp->ShowLine(tl_pts, tl_nPts, color);
+				line[1].x = mev->x;				line[1].y = mev->y;
+				line[0].x = pc.x;				line[0].y = pc.y;
+				CurrDisp->ShowLine(line, 2, color);
+				UpdateMinMaxRect(&rcUpd, mev->x, mev->y);
+				memcpy(&rcUpd, &rcDim, sizeof(RECT));
+				UpdateMinMaxRect(&rcUpd, mev->x, mev->y);
+				IncrementMinMaxRect(&rcUpd, 2);
+				return true;
+				}
+			break;
+		case MOUSE_LBUP:
+			if(tl_pts && tl_nPts) {
+				memcpy(&pl, &pc, sizeof(POINT));
+				pc.x = mev->x;				pc.y = mev->y;
+				UpdateMinMaxRect(&rcDim, mev->x, mev->y);
+				AddToPolygon(&tl_nPts, tl_pts, &pc);
+				}
+			return true;
+		default:			// create line or polygon object
+			if(tl_pts && tl_nPts >0) {			//DEBUG: check for plausibility
+				pc.x = mev->x;				pc.y = mev->y;
+				AddToPolygon(&tl_nPts, tl_pts, &pc);
+				if(lfp = (lfPOINT*)malloc(tl_nPts * sizeof(lfPOINT))){
+					for(i = 0; i < tl_nPts; i++) {
+						lfp[i].fx = CurrDisp->fix2un(tl_pts[i].x-CurrDisp->VPorg.fx);	
+						lfp[i].fy = CurrDisp->fiy2un(tl_pts[i].y-CurrDisp->VPorg.fy);
+						}
+					if(Plots[NumPlots]) i = NumPlots+1;
+					else i = NumPlots;
+					Undo.SetGO(this, &Plots[i], ToolMode == TM_POLYLINE ?
+						new polyline(this, data, lfp, (int)tl_nPts) :
+						(GraphObj*) new polygon(this, data, lfp, (int)tl_nPts), 0L);
+					if(Plots[i]){
+						NumPlots = i+1;
+						Plots[i]->moveable = 1;
+						Plots[i]->DoPlot(CurrDisp);							//init
+						CurrDisp->ShowMark(CurrGO = Plots[i], MRK_GODRAW);	//edit
+						bModified = true;
+						}
+					free(lfp);
+					}
+				free(tl_pts);			tl_pts = 0L;		tl_nPts = 0;
+				return true;
+				}
+			}
+		if(mev->x == pc.x && mev->y == pc.y) return true;	//rebounce
+		break;
+	case TM_RECTANGLE:	case TM_ELLIPSE:	case TM_ROUNDREC:	case TM_ARROW:
+		switch (mev->Action) {
+		case MOUSE_LBDOWN:
+			CurrGO = 0L;
+			CurrDisp->MrkMode = MRK_NONE;
+			Command(CMD_REDRAW, 0L, CurrDisp);
+			color = defs.Color((ToolMode & 0x0f) != TM_ARROW ? COL_POLYGON : COL_DATA_LINE);
+			pl.x = pc.x = mev->x;		pl.y = pc.y = mev->y;
+			rcDim.left = rcDim.right = rcUpd.left = rcUpd.right = mev->x;
+			rcDim.top = rcDim.bottom = rcUpd.top = rcUpd.bottom = mev->y;
+			return true;
+		case MOUSE_MOVE:
+			if(mev->StateFlags &1) {
+				if(rcUpd.left != rcUpd.right && rcUpd.top != rcUpd.bottom)
+					CurrDisp->UpdateRect(&rcUpd, false);
+				line[0].x = line[4].x = pl.x;	line[0].y = line[4].y = pl.y;
+				if((ToolMode & 0x0f)==TM_ARROW) {
+					line[1].x = pc.x = mev->x;		line[1].y = pc.y = mev->y;
+					CurrDisp->ShowLine(line, 2, color);
+					}
+				else {
+					line[1].x = pc.x = mev->x;		line[1].y = pl.y;
+					line[2].x = mev->x;				line[2].y = pc.y = mev->y;
+					line[3].x = pl.x;				line[3].y = mev->y;
+					CurrDisp->ShowLine(line, 5, color);
+					if((ToolMode & 0x0f) == TM_ELLIPSE) 
+						CurrDisp->ShowEllipse(pc, pl, color);
+					}
+				memcpy(&rcUpd, &rcDim, sizeof(RECT));
+				UpdateMinMaxRect(&rcUpd, mev->x, mev->y);
+				IncrementMinMaxRect(&rcUpd, 2);
+				return true;
+				}
+			break;
+		case MOUSE_LBUP:
+			pc.x = mev->x;			pc.y = mev->y;
+			if(((ToolMode & 0x0f)==TM_ARROW || (abs(pc.x-pl.x) >5 && abs(pc.y-pl.y) >5)) && 
+				(lfp = (lfPOINT*)malloc(2 * sizeof(lfPOINT)))){
+				lfp[0].fx = CurrDisp->fix2un(pl.x - CurrDisp->VPorg.fx);
+				lfp[0].fy = CurrDisp->fiy2un(pl.y - CurrDisp->VPorg.fy);
+				lfp[1].fx = CurrDisp->fix2un(pc.x - CurrDisp->VPorg.fx);
+				lfp[1].fy = CurrDisp->fiy2un(pc.y - CurrDisp->VPorg.fy);
+				if(Plots[NumPlots]) i = NumPlots+1;
+				else i = NumPlots;
+				if(ToolMode == TM_RECTANGLE) new_go = new rectangle(this, data,
+					&lfp[0], &lfp[1]);
+				else if(ToolMode == TM_ELLIPSE) new_go = new ellipse(this, data,
+					&lfp[0], &lfp[1]);
+				else if(ToolMode == TM_ROUNDREC) new_go = new roundrec(this, data,
+					&lfp[0], &lfp[1]);
+				else if(ToolMode == TM_ARROW) new_go = new Arrow(this, data,
+					lfp[0], lfp[1], ARROW_UNITS | ARROW_LINE);
+				else new_go = 0L;
+				if(new_go) Undo.SetGO(this, &Plots[i], new_go, 0L);
+				if(Plots[i]){
+					NumPlots = i+1;				CurrDisp->HideMark();
+					Plots[i]->DoPlot(CurrDisp);								//init
+					CurrDisp->ShowMark(CurrGO = Plots[i], MRK_GODRAW);		//edit
+					Plots[i]->moveable = 1;
+					bModified = true;
+					}
+				free(lfp);
+				}
+			else if(rcUpd.left != rcUpd.right && rcUpd.top != rcUpd.bottom) {
+				CurrDisp->UpdateRect(&rcUpd, false);
+				}
+			}
+		break;
+	case TM_TEXT:
+		if(Plots[NumPlots]) i = NumPlots+1;
+		else i = NumPlots;
+		switch(mev->Action) {
+		case MOUSE_LBDOWN:
+			CurrGO = 0L;
+			CurrDisp->MrkMode = MRK_NONE;
+			Command(CMD_REDRAW, 0L, CurrDisp);
+			color = 0x00cbcbcb;
+			pl.x = pc.x = mev->x;		pl.y = pc.y = mev->y;
+			rcDim.left = rcDim.right = rcUpd.left = rcUpd.right = mev->x;
+			rcDim.top = rcDim.bottom = rcUpd.top = rcUpd.bottom = mev->y;
+			return true;
+		case MOUSE_MOVE:
+			if(mev->StateFlags &1) {
+				CurrDisp->MouseCursor(MC_TXTFRM, false);
+				if(rcUpd.left != rcUpd.right && rcUpd.top != rcUpd.bottom)
+					CurrDisp->UpdateRect(&rcUpd, false);
+				line[0].x = line[4].x = pl.x;	line[0].y = line[4].y = pl.y;
+				if((ToolMode & 0x0f)==TM_ARROW) {
+					line[1].x = pc.x = mev->x;		line[1].y = pc.y = mev->y;
+					CurrDisp->ShowLine(line, 2, color);
+					}
+				else {
+					line[1].x = pc.x = mev->x;		line[1].y = pl.y;
+					line[2].x = mev->x;				line[2].y = pc.y = mev->y;
+					line[3].x = pl.x;				line[3].y = mev->y;
+					CurrDisp->ShowLine(line, 5, color);
+					}
+				memcpy(&rcUpd, &rcDim, sizeof(RECT));
+				UpdateMinMaxRect(&rcUpd, mev->x, mev->y);
+				IncrementMinMaxRect(&rcUpd, 2);
+				return true;
+				}
+			break;
+		case MOUSE_LBUP:
+			pc.x = mev->x;			pc.y = mev->y;
+			if(((abs(pc.x-pl.x) >20 && abs(pc.y-pl.y) >20)) && 
+				(lfp = (lfPOINT*)malloc(2 * sizeof(lfPOINT)))){
+				lfp[0].fx = CurrDisp->fix2un(pl.x - CurrDisp->VPorg.fx);
+				lfp[0].fy = CurrDisp->fiy2un(pl.y - CurrDisp->VPorg.fy);
+				lfp[1].fx = CurrDisp->fix2un(pc.x - CurrDisp->VPorg.fx);
+				lfp[1].fy = CurrDisp->fiy2un(pc.y - CurrDisp->VPorg.fy);
+				if(Plots[NumPlots]) i = NumPlots+1;
+				else i = NumPlots;
+				new_go = new TextFrame(this, data, &lfp[0], &lfp[1], 0L);
+				if(new_go) Undo.SetGO(this, &Plots[i], new_go, 0L);
+				if(Plots[i]){
+					NumPlots = i+1;
+					Plots[i]->DoPlot(CurrDisp);							//init
+					CurrDisp->ShowMark(CurrGO = Plots[i], MRK_GODRAW);				//edit
+					Plots[i]->moveable = 1;
+					bModified = true;
+					}
+				free(lfp);
+				CurrDisp->CheckMenu(ToolMode & 0x0f, false);
+				CurrDisp->CheckMenu(ToolMode = TM_STANDARD, true);
+				CurrDisp->MouseCursor(MC_ARROW, false);
+				}
+			else {
+				CurrDisp->MouseCursor(MC_TEXT, false);
+				if(!(td = (TextDEF *)calloc(1, sizeof(TextDEF))))return false;
+				x = CurrDisp->fix2un(mev->x-CurrDisp->VPorg.fx);
+				y = CurrDisp->fiy2un(mev->y-CurrDisp->VPorg.fy);
+				td->ColTxt = defs.Color(COL_TEXT);		td->ColBg = defs.Color(COL_BG);
+				td->RotBL = td->RotCHAR = 0.0f;			td->fSize = DefSize(SIZE_TEXT);
+				td->Align = TXA_VTOP | TXA_HLEFT;		td->Style = TXS_NORMAL;
+				td->Mode = TXM_TRANSPARENT;				td->Font = FONT_HELVETICA;
+				td->text = 0L;
+				CurrGO = 0L;
+				CurrDisp->MrkMode = MRK_NONE;
+				Command(CMD_REDRAW, 0L, CurrDisp);
+				y -= td->fSize/2.0;
+				Undo.SetGO(this, &Plots[i], new Label(this, data, x, y, td, 0L), 0L);
+				if(Plots[i]){
+					NumPlots = i+1;
+					Plots[i]->DoPlot(CurrDisp);							//init
+					CurrDisp->ShowMark(CurrGO = Plots[i], MRK_GODRAW);	//edit
+					Plots[i]->moveable = 1;
+					}
+				free(td);
+				}
+			return true;
+			}
+		break;
+		}
+	return false;
+}
+
+bool
+Graph::MoveObj(int cmd, GraphObj *g)
+{
+	int i, j;
+	GraphObj *g1 = 0;
+
+	if(!g || NumPlots <2 || g->parent != this) return false;
+	switch(cmd) {
+	case CMD_MOVE_TOP:
+		for(i = j = 0; i <NumPlots; i++){
+			if(g == Plots[i]) g1 = Plots[i];
+			else Plots[j++] = Plots[i];
+			}
+		if(g1) {
+			Plots[j++] = g1;
+			return true;
+			}
+		break;
+	case CMD_MOVE_UP:
+		for(i = 0; i<NumPlots-1; i++){
+			if(g == Plots[i]){
+				g1 = Plots[i];	Plots[i] = Plots[i+1];	Plots[i+1] = g1;
+				return true;
+				}
+			}
+		break;
+	case CMD_MOVE_DOWN:
+		for(i = 1; i<NumPlots; i++){
+			if(g == Plots[i]){
+				g1 = Plots[i];	Plots[i] = Plots[i-1];	Plots[i-1] = g1;
+				return true;
+				}
+			}
+		break;
+	case CMD_MOVE_BOTTOM:
+		if(Plots[0] == g) return false;
+		for(i =  j = NumPlots-1; i >= 0; i--) {
+			if(g == Plots[i]) g1 = Plots[i];
+			else Plots[j--] = Plots[i];
+			}
+		if(g1) {
+			Plots[j--] = g1;
+			return true;
+			}
+		break;
+		}
+	return false;
+}
+
+bool
+Graph::DoZoom(char *z)
+{
+	RECT cw;
+	double fac, f1, f2;
+	ZoomDEF *tz;
+	Graph *cg;
+
+	if(!z) return false;
+	HideCopyMark();
+	if(0==strcmp("fit", z)) {
+		if(CurrGraph) cg = CurrGraph;
+		else cg = this;
+		rc_mrk.left = CurrDisp->un2ix(cg->GetSize(SIZE_GRECT_LEFT))-4;
+		rc_mrk.right = CurrDisp->un2ix(cg->GetSize(SIZE_GRECT_RIGHT))+4
+			+ iround(CurrDisp->MenuHeight);
+		rc_mrk.top = CurrDisp->un2ix(cg->GetSize(SIZE_GRECT_TOP))-4 
+			- iround(CurrDisp->MenuHeight);
+		rc_mrk.bottom = CurrDisp->un2ix(cg->GetSize(SIZE_GRECT_BOTTOM))+4;
+		if(!(CurrDisp->ActualSize(&cw)))return false;
+		f1 = (double)(cw.bottom - cw.top)/(double)(rc_mrk.bottom - rc_mrk.top);
+		f2 = ((double)(cw.right - cw.left)/(double)(rc_mrk.right - rc_mrk.left));
+		fac = f1 < f2 ? f1 : f2;
+		if((CurrDisp->VPscale * fac) > 100.0) fac = 100.0/CurrDisp->VPscale;
+		if((CurrDisp->VPscale * fac) < 0.05) fac = 0.05/CurrDisp->VPscale;
+		if(fac == 1.0) return false;
+		if(tz = (ZoomDEF*)memdup(zoom_def, sizeof(ZoomDEF)*(zoom_level+1), 0)){
+			if(zoom_def) free(zoom_def);
+			zoom_def = tz;
+			zoom_def[zoom_level].org.fx = CurrDisp->VPorg.fx;
+			zoom_def[zoom_level].org.fy = CurrDisp->VPorg.fy;
+			zoom_def[zoom_level].scale = CurrDisp->VPscale;
+			zoom_level++;
+			}
+		CurrDisp->VPscale *= fac;
+		if(CurrDisp->VPscale < 0.05) CurrDisp->VPscale = 0.05; 
+		if(CurrDisp->VPscale > 100.0) CurrDisp->VPscale = 100.0; 
+		CurrDisp->VPorg.fx = -rc_mrk.left * fac;
+		CurrDisp->VPorg.fy = -rc_mrk.top * fac;
+		HideTextCursor();
+		Command(CMD_SETSCROLL, 0L, CurrDisp);
+		return true;
+		}
+	else if(0==strcmp("+", z)) {
+		if(rc_mrk.left >= 0 && rc_mrk.right >= 0 && rc_mrk.top >= 0 && rc_mrk.bottom >= 0) {
+			if(rc_mrk.left > rc_mrk.right) Swap(rc_mrk.left, rc_mrk.right);
+			if(rc_mrk.top > rc_mrk.bottom) Swap(rc_mrk.top, rc_mrk.bottom);
+			if(5 > (rc_mrk.right - rc_mrk.left) || 5 > (rc_mrk.bottom - rc_mrk.top)) {
+				rc_mrk.left = rc_mrk.left = rc_mrk.left = rc_mrk.left = -1;
+				ToolMode = TM_STANDARD;		Command(CMD_TOOLMODE, &ToolMode, CurrDisp);
+				return false;
+				}
+			if(!(CurrDisp->ActualSize(&cw)))return false;
+			fac = (double)(cw.bottom - cw.top)/(double)(rc_mrk.bottom - rc_mrk.top);
+			fac += ((double)(cw.right - cw.left)/(double)(rc_mrk.right - rc_mrk.left));
+			fac /= 2.0;
+			if((CurrDisp->VPscale * fac) > 100.0) fac = 100.0/CurrDisp->VPscale;
+			if((CurrDisp->VPscale * fac) < 0.05) fac = 0.05/CurrDisp->VPscale;
+			if(fac == 1.0) return false;
+			if(tz = (ZoomDEF*)memdup(zoom_def, sizeof(ZoomDEF)*(zoom_level+1), 0)){
+				if(zoom_def) free(zoom_def);
+				zoom_def = tz;
+				zoom_def[zoom_level].org.fx = CurrDisp->VPorg.fx;
+				zoom_def[zoom_level].org.fy = CurrDisp->VPorg.fy;
+				zoom_def[zoom_level].scale = CurrDisp->VPscale;
+				zoom_level++;
+				}
+			CurrDisp->VPscale *= fac;
+			if(CurrDisp->VPscale < 0.05) CurrDisp->VPscale = 0.05; 
+			if(CurrDisp->VPscale > 100.0) CurrDisp->VPscale = 100.0; 
+			CurrDisp->VPorg.fx = CurrDisp->VPorg.fx * fac - rc_mrk.left * fac;
+			CurrDisp->VPorg.fy = CurrDisp->VPorg.fy * fac - rc_mrk.top * fac;
+			HideTextCursor();
+			Command(CMD_SETSCROLL, 0L, CurrDisp);
+			CurrDisp->MouseCursor(MC_ARROW, false);
+			rc_mrk.left = rc_mrk.left = rc_mrk.left = rc_mrk.left = -1;
+			ToolMode = TM_STANDARD;		Command(CMD_TOOLMODE, &ToolMode, CurrDisp);
+			return true;
+			}
+		else {
+			ToolMode = TM_ZOOMIN;			CurrDisp->MouseCursor(MC_ZOOM, true);
+			}
+		}
+	else if(0==strcmp("-", z)) {
+		HideTextCursor();
+		if(zoom_def && zoom_level > 0) {
+			zoom_level--;
+			if(CurrDisp->VPscale == zoom_def[zoom_level].scale &&
+				CurrDisp->VPorg.fx == zoom_def[zoom_level].org.fx &&
+				CurrDisp->VPorg.fy == zoom_def[zoom_level].org.fy) {
+				DoZoom(z);
+				}
+			else {
+				CurrDisp->VPscale = zoom_def[zoom_level].scale;
+				CurrDisp->VPorg.fx = zoom_def[zoom_level].org.fx;
+				CurrDisp->VPorg.fy = zoom_def[zoom_level].org.fy;
+				}
+			}
+		else {
+			CurrDisp->VPorg.fx = CurrDisp->VPorg.fy = 0.0;
+			CurrDisp->VPscale = Id == GO_PAGE ? 0.5 : 1.0;
+			}
+		Command(CMD_SETSCROLL, 0L, CurrDisp);
+		return true;
+		}
+	else if(0==strcmp("25", z)){
+		CurrDisp->VPscale = 0.25;		return DoZoom("org");
+		}
+	else if(0==strcmp("50", z)){
+		CurrDisp->VPscale = 0.50;		return DoZoom("org");
+		}
+	else if(0==strcmp("100", z)){
+		CurrDisp->VPscale = 1.00;		return DoZoom("org");
+		}
+	else if(0==strcmp("200", z)){
+		CurrDisp->VPscale = 2.00;		return DoZoom("org");
+		}
+	else if(0==strcmp("400", z)){
+		CurrDisp->VPscale = 4.00;		return DoZoom("org");
+		}
+	else if(0==strcmp("org", z)){
+		CurrDisp->VPorg.fx = 0.0;		CurrDisp->VPorg.fy = iround(CurrDisp->MenuHeight);
+		HideTextCursor();
+		Command(CMD_SETSCROLL, 0L, CurrDisp);
+		return DoZoom("reset");
+		}
+	else if(0==strcmp("reset", z)){
+		if(zoom_def && zoom_level > 0) free(zoom_def);
+		zoom_def = 0L;			zoom_level = 0;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Create a tree structure of all interesting objects
+//   This object is used e.g. for layer control
+ObjTree::ObjTree(GraphObj *par, DataObj *d, GraphObj *root):GraphObj(par, d)
+{
+	Id = GO_OBJTREE;					base = root;		list=0L;
+	TextDef.ColTxt = 0x00000000L;		TextDef.ColBg = 0x00ffffffL;
+	TextDef.fSize = 4.0;				TextDef.RotBL = TextDef.RotCHAR = 0.0;
+	TextDef.iSize = 0;					TextDef.Align = TXA_HLEFT | TXA_VTOP;
+	TextDef.Mode = TXM_TRANSPARENT;		TextDef.Style = TXS_NORMAL;
+	TextDef.Font = FONT_HELVETICA;		TextDef.text = 0L;
+	Command(CMD_LAYERS, 0L, 0L);
+}
+
+ObjTree::~ObjTree()
+{
+	if(list) free(list);		list = 0L;
+}
+
+void
+ObjTree::DoPlot(anyOutput *o)
+{
+	int i, n, ix, iy;
+	GraphObj *curr_obj;
+
+	if(!o || !list) return;
+	o->Erase(0x00ffffffL);
+	ix = 10;	iy = 0;
+	for(i = 0; i < count; i++, iy += (TextDef.iSize+_SBINC)) {
+		if(list[i]) {
+			curr_obj = list[i];			n = 0;
+			if(i) while(curr_obj && curr_obj != list[0]) {
+				if(curr_obj != list[0]) n += rlp_strcpy(TmpTxt+n, TMP_TXT_SIZE -n, " -");
+				if(curr_obj) curr_obj = curr_obj->parent; 
+				}
+			if(n) TmpTxt[n++] = ' ';
+			n += rlp_strcpy(TmpTxt+n, TMP_TXT_SIZE -n, get_name(i));
+			if(list[i]->Id >= GO_PLOT && list[i]->Id < GO_GRAPH) {
+				TextDef.ColTxt = ((Plot*)list[i])->hidden ? 0x00000080L : 0x00008000L;
+				}
+			else TextDef.ColTxt = 0x00000000L;
+			o->SetTextSpec(&TextDef);
+			o->oTextOut(ix, iy, TmpTxt, 0);
+			}
+		}
+}
+
+bool
+ObjTree::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch(cmd){
+	case CMD_LAYERS:
+		if(list) free(list);
+		count = 0;		maxcount = 100;
+		if(base) {
+			if(list = (GraphObj**) malloc(100 * sizeof(GraphObj*))) {
+				list[count++] = base;
+				}
+			base->Command(CMD_OBJTREE, this, 0L);
+			}
+		break;
+	case CMD_SET_DATAOBJ:
+		Id = GO_OBJTREE;
+		return true;
+	case CMD_UPDATE:
+		if(tmpl && (((GraphObj*)tmpl)->Id >= GO_PLOT && ((GraphObj*)tmpl)->Id < GO_SPREADDATA)
+			|| ((GraphObj*)tmpl)->Id == GO_LEGEND) {
+			if(count >= maxcount) {
+				maxcount += 100;
+				list = (GraphObj**) realloc(list, maxcount);
+				}
+			if(list) list[count++] = (GraphObj*)tmpl;
+			}
+		return true;
+	case CMD_TEXTDEF:
+		if(tmpl) memcpy(&TextDef, tmpl, sizeof(TextDEF));
+		TextDef.text = 0L;
+		return true;
+		}
+	return false;
+}
+
+anyOutput *
+ObjTree::CreateBitmap(int *bw, int *bh, anyOutput *tmpl)
+{
+	anyOutput *bmp = 0L;
+	int h;
+	
+	h = (tmpl->un2iy(TextDef.fSize)+_SBINC) * (count+1);
+	if(h > *bh) *bh = h;
+	if(bmp = NewBitmapClass(*bw, *bh, tmpl->hres, tmpl->vres)) DoPlot(bmp);
+	return bmp;
+}
+
+char *
+ObjTree::get_name(int li)
+{
+	if(li < count && list[li] && list[li]->name) return list[li]->name;
+	else return "(unknown)";
+}
+
+int
+ObjTree::get_vis(int li)
+{
+	if(li < count && list[li] && list[li]->Id >= GO_PLOT && list[li]->Id < GO_GRAPH)
+		return ((Plot*)list[li])->hidden ? 0 : 1;
+	return 2;
+}
+
+bool
+ObjTree::set_vis(int li, bool vis)
+{
+	if(li < count && list[li] && list[li]->Id >= GO_PLOT && list[li]->Id < GO_GRAPH) {
+		((Plot*)list[li])->hidden = vis ? 0 : 1;
+		list[li]->Command(CMD_MRK_DIRTY, 0L, 0L);
+		list[li]->Command(CMD_REDRAW, 0L, 0L);
+		}
+	return false;
+}
+
+GraphObj*
+ObjTree::get_obj(int li)
+{
+	if(li < count && list[li]) return list[li];
+	return 0L;
+}
+

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-science/packages/rlplot.git



More information about the debian-science-commits mailing list