[rlplot] 01/23: Imported Upstream version 0.99.14

Andreas Tille tille at debian.org
Wed Jun 29 09:50:54 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 31a7665dc2f1a80608c8ba489bc2dd303411100b
Author: Andreas Tille <tille at debian.org>
Date:   Wed Jun 29 11:43:53 2016 +0200

    Imported Upstream version 0.99.14
---
 Axes.cpp        | 1741 ++++++++++
 Export.cpp      | 1325 ++++++++
 Fileio.cpp      | 3353 +++++++++++++++++++
 Makefile        |  118 +
 Makefile.win    |   97 +
 ODbuttons.cpp   | 1273 ++++++++
 Output.cpp      | 1829 +++++++++++
 PlotObs.cpp     | 4427 +++++++++++++++++++++++++
 PropertyDlg.cpp | 7777 ++++++++++++++++++++++++++++++++++++++++++++
 QT_Spec.cpp     | 2667 +++++++++++++++
 QT_Spec.h       |  284 ++
 README          |  131 +
 RLPLOT.ICO      |  Bin 0 -> 766 bytes
 RLPLOT.RC       |  337 ++
 RLPlot.bmp      |  Bin 0 -> 630 bytes
 RLPlot.xpm      |   47 +
 TheDialog.cpp   | 4479 ++++++++++++++++++++++++++
 TheDialog.h     |  527 +++
 UtilObj.cpp     | 2950 +++++++++++++++++
 Utils.cpp       | 1946 +++++++++++
 Version.h       |   20 +
 WinSpec.cpp     | 2422 ++++++++++++++
 WinSpec.h       |  130 +
 exprlp.cpp      |  532 +++
 gpl.txt         |  340 ++
 mfcalc.cpp      | 2299 +++++++++++++
 mfcalc.y        | 1247 ++++++++
 resource.h      |   86 +
 rlp_math.cpp    |  411 +++
 rlplot.1        |  105 +
 rlplot.cpp      | 9618 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 rlplot.h        | 2495 +++++++++++++++
 rlplot.spec     |   62 +
 spreadwi.cpp    | 1833 +++++++++++
 34 files changed, 56908 insertions(+)

diff --git a/Axes.cpp b/Axes.cpp
new file mode 100755
index 0000000..320ff5b
--- /dev/null
+++ b/Axes.cpp
@@ -0,0 +1,1741 @@
+//Axes.cpp, Copyright 2000, 2001, 2002, 2003, 2004 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;
+			}
+		SetMinMaxRect(&rDims, pts[0].x, pts[2].y, pts[1].x, pts[3].y);
+		IncrementMinMaxRect(&rDims, 3);
+		if(pts[0].x != pts[1].x || pts[0].y != pts[1].y) o->oPolyline(pts, 2);
+		if(pts[2].x != pts[3].x || pts[2].y != pts[3].y) o->oPolyline(pts+2, 2);
+		}
+}
+
+void
+GridLine::DoMark(anyOutput *o, bool mark)
+{
+	if(cpts && mark){
+		InvertLine(cpts, ncpts, &LineDef, &rDims, o, mark);
+		}
+	else if(cpts) {
+		InvertLine(cpts, ncpts, &LineDef, &rDims, o, mark);
+		}
+
+}
+
+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_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(this, MRK_GODRAW);
+					return true;
+					}
+				else if(!(type & DL_CIRCULAR)){
+					o->ShowMark(&rDims, MRK_INVERT);
+					CurrGO = this;
+					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(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*));
+	o->ActualSize(&rDims);
+	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) {
+		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){
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		IncrementMinMaxRect(&mrc, 6);
+		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;
+
+	switch(cmd){
+	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_HIDE_MARK:
+		if(!tmpl || !o) return false;
+		if(tmpl == (void*)label){
+			label->DoMark(o, false);
+			return true;
+			}
+		else if(tmpl == (void*)Grid){
+			Grid->DoMark(o, false);
+			return true;
+			}
+		return false;
+	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_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)) {
+				if(pts[0].x == pts[1].x || pts[0].y == pts[1].y) {
+					o->ShowMark(&rDims, MRK_INVERT);
+					CurrGO = this;
+					}
+				else 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) : defs.GetSize(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) ? strdup((char*)tmpl) : 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;
+	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(defs.GetSize(SIZE_AXIS_TICKS)*3.0*six));
+		lby -= (o->un2fiy(defs.GetSize(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;
+}
+
+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;
+		}
+	//DEBUG: we should return a reasonable value for SIZE_BOUNDS_...
+	//    if the axis is not scaling (i.e. parent == this).
+	if(parent) return parent->GetSize(select);
+	else return defs.GetSize(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(parent->Id == GO_PLOT3D && (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);
+}
+
+void
+Axis::DoMark(anyOutput *o, bool mark)
+{
+	LineDEF mrkLine;
+	int x1, y1, x2, y2;
+	double minw = defs.GetSize(SIZE_DATA_LINE);
+
+	if(axis->flags & AXIS_ANGULAR) {
+		if(mark) {
+			memcpy(&mrc, &rDims, sizeof(RECT));
+			IncrementMinMaxRect(&mrc, 6 + o->un2ix(axline.width));
+			mo = GetRectBitmap(&mrc, o);
+			memcpy(&mrkLine, &axline, sizeof(LineDEF));
+			mrkLine.width = axline.width < minw ? minw * 3.0 : axline.width *3.0;;
+			x1 = o->co2ix(axis->Center.fx) - o->un2ix(axis->Radius);
+			y1 = o->co2iy(axis->Center.fy) - o->un2iy(axis->Radius);
+			x2 = o->co2ix(axis->Center.fx) + o->un2ix(axis->Radius);
+			y2 = o->co2iy(axis->Center.fy) + o->un2iy(axis->Radius);
+			o->SetLine(&mrkLine);
+			o->oArc(x1, y1, x2, y2, 4);
+			mrkLine.color ^= 0x00ffffffL;
+			mrkLine.width = axline.width;
+			o->SetLine(&mrkLine);
+			o->oArc(x1, y1, x2, y2, 4);
+			o->UpdateRect(&mrc, false);
+			}
+		else RestoreRectBitmap(&mo, &mrc, o);
+		return;
+		}
+	if(mark){
+		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);
+}
+
+bool
+Axis::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	GraphObj **tmpPlots;
+	void *sv_ptr;
+	int i;
+
+	switch (cmd) {
+	case CMD_HIDE_MARK:
+		if(!tmpl || !o) return false;
+		if(tmpl == (void*)axisLabel){
+			axisLabel->DoMark(o, false);
+			return true;
+			}
+		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++) {
+			if(Ticks[i]) {
+				if(tmpl == (void*)Ticks[i]) {
+					Ticks[i]->DoMark(o, false);
+					return true;
+					}	
+				else if(Ticks[i]->Command(cmd, tmpl, o)) return true;
+				}
+			}
+		if(axisLabel && axisLabel->Id == GO_MLABEL) 
+			if(axisLabel->Command(cmd, tmpl, o)) return true;
+		return false;
+	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)) {
+				if(pts[0].x == pts[1].x || pts[0].y == pts[1].y){ 
+					o->ShowMark(&rDims, MRK_INVERT);
+					CurrGO = this;
+					}
+				else 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_SET_AXDEF:
+		if(tmpl) {
+			if(axis && axis->owner == (void*)this) {
+				if(axis->breaks) free(axis->breaks);
+				free(axis);
+				}
+			}
+		axis = (AxisDEF*)tmpl;
+		if(axis) return true;
+		return false;
+	case CMD_CAN_DELETE:
+		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(Ticks && NumTicks) for(i = 0; i < NumTicks; i++){ 
+			if(Ticks[i] == tmpl) {
+				Undo.DeleteGO((GraphObj**)(&Ticks[i]), 0L, o);
+				return parent->Command(CMD_REDRAW, 0L, o);
+				}
+			else if(Ticks[i] && Ticks[i]->Command(cmd, 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);
+			}
+		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;
+		axis->Start = axis->min;
+		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 = strdup(TmpTxt+1);
+		}
+	else l = strdup(txt);
+	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::CreateTicks()
+{
+	int i, n, nstep;
+	char *format;
+	double fVal, tmp;
+	DWORD flags;
+
+	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_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) {
+			sprintf(TmpTxt, format, fVal);
+			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++) {
+		sprintf(TmpTxt, format, fVal);
+		SetTick(NumTicks, fVal, flags, TmpTxt);
+		for(j = 0; j < n; j++) {
+			mival = fVal+mist*(double)j +mist;
+			if(mival < axis->max) {
+				sprintf(TmpTxt, format, mival);
+				NumTicks++;
+				SetTick(NumTicks, mival, flags | AXIS_MINORTICK, TmpTxt);
+				}
+			}
+		fVal += st;
+		}
+}
+
+bool
+Axis::GetValuePos(double val, double *fix, double *fiy, double *fiz, anyOutput *op)
+{
+	double tmp1;
+	int i;
+	bool bRet = true;
+	anyOutput *o;
+	fPOINT3D p1, p2;
+	lfPOINT fdp, fip;
+
+	*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) {
+		p1.fx = GetSize(SIZE_XPOS);	p1.fy = GetSize(SIZE_YPOS);	p1.fz = GetSize(SIZE_ZPOS);
+		tmp1 = defs.cUnits == 1 ? 2.54 : defs.cUnits == 2 ? 1.0 : 25.4;
+		tmp1 /= op->VPscale;
+		switch(type) {
+		case 1: p1.fx += (tmp1 * (op->fx2fix(val)- op->Box1.Xmin)/op->hres);	break;
+		case 2: p1.fy += (tmp1 * (op->fy2fiy(val)- op->Box1.Ymax)/op->vres);	break;
+		case 3: p1.fz += (tmp1 * (op->fz2fiz(val)- op->Box1z.fx)/op->hres);	break;
+		default:	return false;
+			}
+		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 < -.005f || 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);
+}
diff --git a/Export.cpp b/Export.cpp
new file mode 100755
index 0000000..4470367
--- /dev/null
+++ b/Export.cpp
@@ -0,0 +1,1325 @@
+//Export.cpp, Copyright (c) 2002, 2003, 2004 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 _WINDOWS
+	#include <io.h>					//for read/write
+#else
+	#define O_BINARY 0x0
+	#include <unistd.h>
+#endif
+
+extern Default defs;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// export to *.wmf file (windows meta file)
+// this code is based on information from the following books
+// G. Born, 'Referenzhandbuch Dateiformate', 
+//     Addison-Wesley ISBN 3-89319-815-6
+// T. Hogan, 'Die PC-referenz f�r Programmierer',
+//     Microsoft Press: Systema Verlag GmbH ISBN 3-89390-250-3
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+typedef struct {
+	unsigned short mtType, mtHeader, mtVersion, mtSize0, mtSize1, mtNoObj;
+	unsigned mtMaxRec;
+	unsigned short mtnoPar;
+}wmf_header;
+
+class ExportWMF:public anyOutput {
+public:
+	HatchOut *hgo;
+
+	ExportWMF(GraphObj *g, char *FileName, float res, DWORD flags);
+	~ExportWMF();
+	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:
+	int oFile;
+	unsigned file_size, rec_size;
+	wmf_header header;
+	unsigned short currGDIobj, maxGDIobj;
+	unsigned short hPen, hBrush, hFont;
+	GraphObj *go;
+	char *name;
+
+	unsigned short wmfCreateSolidBrush(DWORD color);
+	unsigned short wmfCreateFontIndirect(short, short, short, short, short, unsigned char,
+		unsigned char, unsigned char, unsigned char, unsigned char, unsigned char,
+		unsigned char, unsigned char, char *);
+	unsigned short wmfCreateSolidPen(DWORD color, unsigned short width);
+	void wmfDeleteObject(unsigned short obj);
+	void wmfEllipse(unsigned short, unsigned short, unsigned short, unsigned short);
+	void wmfPolyline(POINT *pts, unsigned short cp);
+	void wmfPolygon(POINT *pts, unsigned short cp);
+	void wmfRectangle(unsigned short, unsigned short, unsigned short, unsigned short);
+	void wmfSelectObject(unsigned short o);
+	void wmfSetBkColor(DWORD col);
+	void wmfSetBkMode(unsigned m);
+	void wmfSetTextAlign(unsigned a);
+	void wmfSetTextColor(DWORD col);
+	void wmfTextOut(unsigned short, unsigned short, char *, unsigned short);
+};
+
+ExportWMF::ExportWMF(GraphObj *g, char *FileName, float res, DWORD flags)
+{
+	currGDIobj = maxGDIobj = 0;
+	hgo =0L;
+	DeskRect.left = DeskRect.top = 0;
+	DeskRect.right = DeskRect.bottom = 0x4fffffff;
+	dFillCol = 0xffffffffL;
+	hPen = 0xffff, hBrush = 0xffff, hFont = 0xffff;
+	hres = vres = res;
+	go = g;
+	if(FileName)name = strdup(FileName);
+	else name = 0L;
+	oFile = 0;
+	rec_size = 28;
+	header.mtType = 1;
+	header.mtHeader = 9;
+	header.mtVersion = 0x300;
+	header.mtSize0 = 1000;
+	header.mtSize1 = 0;
+	header.mtNoObj = 64;
+	header.mtMaxRec = 100;
+	header.mtnoPar = 0;
+}
+
+ExportWMF::~ExportWMF()
+{
+	if(hgo) delete hgo;
+	if(name) free(name);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//overloaded functions from the anyOutput class
+bool
+ExportWMF::SetLine(LineDEF *lDef)
+{
+	unsigned short iw;
+
+	if(hPen == 0xffff || lDef->width != LineWidth || lDef->width != LineWidth || 
+		lDef->pattern != dPattern || lDef->color != dLineCol) {
+		LineWidth = lDef->width;
+		iw = (unsigned short)(0.5 + un2fix(lDef->width));
+		dPattern = lDef->pattern;
+		RLP.finc = 256.0/((float)(un2fix(lDef->patlength*8.0)));
+		RLP.fp = 0.0;
+		if(iLine == iw && dLineCol == lDef->color && hPen != 0xffff) return true;
+		iLine = iw;
+		dLineCol = lDef->color;
+		if(hPen != 0xffff) wmfDeleteObject(hPen);
+		iw = iw > 0 ? iw : 1;
+		hPen = wmfCreateSolidPen(dLineCol, iw);
+		wmfSelectObject(hPen);
+		}
+	return true;
+}
+
+bool
+ExportWMF::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;
+		}
+	if(dFillCol != fill->color) {
+		if(hBrush != 0xffff) wmfDeleteObject(hBrush);
+		hBrush = wmfCreateSolidBrush(dFillCol = fill->color);
+		wmfSelectObject(hBrush);
+		}
+	dFillCol2 = fill->color2;
+	return true;
+}
+
+bool
+ExportWMF::SetTextSpec(TextDEF *set)
+{
+	unsigned char lfcs, lfpaf;
+	char *face;
+	bool IsModified, RetVal;
+
+	switch(set->Font){
+	case FONT_HELVETICA:
+	default:
+		lfcs = 0;
+		lfpaf = 2 | 2<<4;
+		face = "Arial";
+		break;
+	case FONT_TIMES:
+		lfcs = 0;
+		lfpaf = 2 | 1<<4;
+		face = "Times New Roman";
+		break;
+	case FONT_COURIER:
+		lfcs = 2;
+		lfpaf = 1 | 3<<4;
+		face = "Courier New";
+		break;
+		}
+	if(!set->iSize && set->fSize > 0.001f) set->iSize = un2iy(set->fSize);
+	if(!set->iSize) return false;
+	if(hFont == 0xffff || TxtSet.iSize != set->iSize || TxtSet.Style != set->Style ||
+		TxtSet.RotBL != set->RotBL || TxtSet.RotCHAR != set->RotCHAR ||
+		TxtSet.Font != set->Font || TxtSet.fSize != set->fSize) IsModified = true;
+	else IsModified = false;
+	RetVal = anyOutput::SetTextSpec(set);
+	if (IsModified && RetVal) {
+		hFont = wmfCreateFontIndirect(TxtSet.iSize, 0, 
+			iround(TxtSet.RotBL*10), iround(TxtSet.RotBL*10), 
+			(TxtSet.Style & TXS_BOLD) ? 700 : 400,
+			(TxtSet.Style & TXS_ITALIC) ? 1 : 0,
+			(TxtSet.Style & TXS_UNDERLINE) ? 1 : 0,
+			0, lfcs, 0, 0, 2, lfpaf, face);
+		}
+	if(hFont != 0xffff) wmfSelectObject(hFont);
+	return true;
+}
+
+bool 
+ExportWMF::StartPage()
+{
+	if(!go) return false;
+	if(name) {
+		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;
+			}
+		}
+	else {
+		oFile = 2;		//stdout
+		}
+	VPorg.fy = -un2fiy(go->GetSize(SIZE_GRECT_TOP));
+	VPorg.fx = -un2fix(go->GetSize(SIZE_GRECT_LEFT));
+	write(oFile, &header, 18);
+	return true;
+}
+
+bool
+ExportWMF::EndPage()
+{
+	unsigned short end_token[3] = {3, 0, 0};
+
+	write(oFile, &end_token, 6);
+//	file_size = tell(oFile);
+	file_size = lseek(oFile, 0L, SEEK_CUR);
+	lseek(oFile, 0L, SEEK_SET);
+	header.mtSize0 = (file_size>>1)&0xffff;
+	header.mtSize1 = file_size>>17;
+	header.mtNoObj = maxGDIobj;
+	header.mtMaxRec = rec_size;
+	write(oFile, &header, 18);
+	oFile = close(oFile);
+	return true;
+}
+
+bool
+ExportWMF::oCircle(int x1, int y1, int x2, int y2, char* nam)
+{
+	wmfEllipse(x1, y1, x2, y2);
+	if(hgo) return hgo->oCircle(x1, y1, x2, y2);
+	return true;
+}
+
+bool
+ExportWMF::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]);
+	else wmfPolyline(pts, cp);
+	return true;
+}
+
+bool
+ExportWMF::oRectangle(int x1, int y1, int x2, int y2, char *nam)
+{
+	wmfRectangle(x1, y1, x2, y2);
+	if(hgo) return hgo->oRectangle(x1, y1, x2, y2, 0L);
+	return true;
+}
+
+bool
+ExportWMF::oSolidLine(POINT *p)
+{
+	wmfPolyline(p, 2);
+	return true;
+}
+
+bool
+ExportWMF::oTextOut(int x, int y, char *txt, int cb)
+{
+	if(!txt || !txt[0]) return false;
+	if(hFont != 0xffff) wmfSelectObject(hFont);
+	wmfSetTextColor(TxtSet.ColTxt);
+	wmfSetBkColor(TxtSet.ColBg);
+	wmfSetTextAlign(((TxtSet.Align & TXA_HRIGHT) ? 2 : (TxtSet.Align &
+		TXA_HCENTER) ? 6 : 0) | ((TxtSet.Align & TXA_VBOTTOM) ?	8 : 0));
+	wmfSetBkMode(TxtSet.Mode ? 1 : 2);
+	wmfTextOut(x, (TxtSet.Align & TXA_VCENTER) ? y - TxtSet.iSize/2 : y, txt, 
+		(cb > 0) ? cb : strlen(txt));
+	return true;
+}
+
+bool
+ExportWMF::oPolygon(POINT *pts, int cp, char *nam)
+{
+	wmfPolygon(pts, cp);
+	if(hgo) return hgo->oPolygon(pts, cp);
+	return true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//templates for wmf file records
+typedef struct{
+	unsigned Size;
+	unsigned short Id;
+	unsigned short Value;
+}wmfObjShort;
+
+typedef struct{
+	unsigned Size;
+	unsigned short Id;
+}wmfObjCol;
+
+typedef struct{
+	unsigned Size;
+	unsigned short Id;
+	unsigned short Style;
+	DWORD Col;
+	unsigned short Hatch;
+}wmfLogBrush;
+
+typedef struct{
+	unsigned Size;
+	unsigned short Id;
+	short lfh, lfw, lfesc, lfori, lfwei;
+	unsigned char lfita, lfund, lfsto, lfcse, lfopre, lfclp, lfqua, lfpaqu;
+	char face[32];
+}wmfLogFont;
+
+typedef struct{
+	unsigned Size;
+	unsigned short Id;
+	unsigned short Style;
+	unsigned Width;
+	DWORD col;
+}wmfLogPen;
+
+typedef struct{
+	unsigned Size;
+	unsigned short Id;
+	unsigned short x1, y1, x2, y2;
+}wmfRect;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//the following routines resemble the corresponding GDI calls
+unsigned short
+ExportWMF::wmfCreateSolidBrush(DWORD color)
+{
+	wmfLogBrush lb = {7, 0x2fc, 0, color, 0};
+
+	write(oFile, &lb, 14);
+	currGDIobj++;
+	maxGDIobj = currGDIobj > maxGDIobj ? currGDIobj : maxGDIobj;
+	return currGDIobj-1;
+}
+
+unsigned short
+ExportWMF::wmfCreateFontIndirect(short lfHeight, short lfWidth, short lfEscapement,
+	short lfOrientation, short lfWeight, unsigned char lfItalic, unsigned char lfUnderline,
+	unsigned char lfStrikeOut, unsigned char lfCharSet, unsigned char lfOutPrecision, 
+	unsigned char lfClipPrecision, unsigned char lfQuality, unsigned char lfPitchAndFamily,
+	char *FaceName)
+{
+	wmfLogFont lf = {28, 0x2fb, lfHeight, lfWidth, lfEscapement, lfOrientation, lfWeight,
+		lfItalic, lfUnderline, lfStrikeOut, lfCharSet, lfOutPrecision, lfClipPrecision,
+		lfQuality, lfPitchAndFamily, "Arial"};
+
+	if(FaceName && FaceName[0]) strcpy(lf.face, FaceName);
+	write(oFile, &lf, 56);
+	currGDIobj++;
+	maxGDIobj = currGDIobj > maxGDIobj ? currGDIobj : maxGDIobj;
+	return currGDIobj-1;
+}
+
+unsigned short
+ExportWMF::wmfCreateSolidPen(DWORD color, unsigned short width)
+{
+	wmfLogPen lp = {8, 0x2fa, 0, width, color};
+
+	write(oFile, &lp, 16);
+	currGDIobj++;
+	maxGDIobj = currGDIobj > maxGDIobj ? currGDIobj : maxGDIobj;
+	return currGDIobj-1;
+}
+
+void
+ExportWMF::wmfDeleteObject(unsigned short obj)
+{
+	wmfObjShort wo = {4, 0x1f0, obj};
+
+	if(currGDIobj == obj+1) {
+//		write(oFile, &wo, 8);
+//		currGDIobj--;
+		}
+}
+
+void
+ExportWMF::wmfEllipse(unsigned short ix1, unsigned short iy1, unsigned short ix2, unsigned short iy2)
+{
+	wmfRect rc = {7, 0x418, iy2, ix2, iy1, ix1};
+	
+	write(oFile, &rc, 14);
+}
+
+void
+ExportWMF::wmfPolyline(POINT *pts, unsigned short cp)
+{
+	wmfObjShort pl = {4+cp*2, 0x325, cp};
+	unsigned short v[2];
+	int i;
+
+	write(oFile, &pl, 8);
+	for(i = 0; i < cp; i++) {
+		v[0] = (unsigned short)pts[i].x;
+		v[1] = (unsigned short)pts[i].y;
+		write(oFile, &v, 4);
+		}
+	if(pl.Size > rec_size) rec_size = pl.Size;
+}
+
+void
+ExportWMF::wmfPolygon(POINT *pts, unsigned short cp)
+{
+	wmfObjShort pl = {4+cp*2, 0x324, cp};
+	unsigned short v[2];
+	int i;
+
+	write(oFile, &pl, 8);
+	for(i = 0; i < cp; i++) {
+		v[0] = (unsigned short)pts[i].x;
+		v[1] = (unsigned short)pts[i].y;
+		write(oFile, &v, 4);
+		}
+	if(pl.Size > rec_size) rec_size = pl.Size;
+}
+
+void
+ExportWMF::wmfRectangle(unsigned short ix1, unsigned short iy1, unsigned short ix2, unsigned short iy2)
+{
+	wmfRect rc = {7, 0x41B, iy2, ix2, iy1, ix1};
+	
+	write(oFile, &rc, 14);
+}
+
+void
+ExportWMF::wmfSelectObject(unsigned short o)
+{
+	wmfObjShort so  = {4, 0x12D, o};
+
+	write(oFile, &so, 8);
+}
+
+void
+ExportWMF::wmfSetBkColor(DWORD col)
+{
+	wmfObjCol co = {5, 0x201};
+
+	write(oFile, &co, 6);
+	write(oFile, &col, 4);
+}
+
+void
+ExportWMF::wmfSetBkMode(unsigned m)
+{
+	wmfObjShort mo = {5, 0x102, m & 0xffff};
+	unsigned short p;
+
+	write(oFile, &mo, 8);
+	p = m>>16;
+	write(oFile, &p, 2);
+}
+
+//cmSetMapMode()
+//cmSetPolyFillMode()
+
+void
+ExportWMF::wmfSetTextAlign(unsigned a)
+{
+	wmfObjShort ao = {5, 0x12E, a & 0xffff};
+	unsigned short p;
+
+	write(oFile, &ao, 8);
+	p = a>>16;
+	write(oFile, &p, 2);
+}
+
+void
+ExportWMF::wmfSetTextColor(DWORD col)
+{
+	wmfObjCol tc = {5, 0x209};
+
+	write(oFile, &tc, 6);
+	write(oFile, &col, 4);
+}
+
+//cmSetWindowExt()
+//cmSetWindowOrg()
+
+void
+ExportWMF::wmfTextOut(unsigned short ix1, unsigned short iy1, char *txt, unsigned short cb)
+{
+	wmfObjShort to  = {6, 0x521, cb};
+	unsigned short le, v[2] = {iy1, ix1};
+
+	le = cb &1 ? cb+1 : cb;
+	to.Size += le>>1;
+	write(oFile, &to, 8);
+	write(oFile, txt, le);
+	write(oFile, &v, 4);	
+	if(to.Size > rec_size) rec_size = to.Size;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Entry point to export graph to Windows Meta File
+void DoExportWmf(GraphObj *g, char *FileName, float res, DWORD flags)
+{
+	ExportWMF *ex;
+	
+	ex = new ExportWMF(g, FileName, res, flags);
+	if(ex->StartPage()) {
+		g->DoPlot(ex);
+		ex->EndPage();
+		}
+	delete(ex);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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 oPolygon(POINT *pts, int cp, char * nam = 0L);
+
+private:
+	int iLineWidth;
+	bool bUseGroupLine, bOutputPending;
+	GraphObj *go;
+	char *name, indent[80], output[120], tHatchStyle[80];
+	FILE *oFile;
+	DWORD flags;
+
+	void Indent(bool ind);
+	void AddToOutput(char *txt);
+	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 = strdup(FileName);
+	else name = 0L;
+	oFile = 0L;
+	flags = flg;
+	strcpy(indent, "   ");
+	strcpy(tHatchStyle, "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;
+			sprintf(tHatchStyle, "style=\"fill:none; stroke:%s; stroke-width:%d\"",
+				ColName(fill->hatch->color), iL);
+			}
+		}
+	else {
+		if(hgo) delete hgo;
+		hgo = 0L;
+		}
+	dFillCol = fill->color;
+	dFillCol2 = fill->color2;
+	return true;
+}
+
+bool
+ExportSVG::SetTextSpec(TextDEF *set)
+{
+	if(!set->iSize && set->fSize > 0.0f) 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(name) {
+		oFile = fopen(name, "w");
+		if(!oFile) {
+			ErrorBox("Could not open\noutput file!");
+			return false;
+			}
+		}
+	else oFile = stdout;
+	if(flags & 0x01) fprintf(oFile, "Content-Type: image/svg+xml\n\n");
+	VPorg.fy = -un2fiy(go->GetSize(SIZE_GRECT_TOP));
+	VPorg.fx = -un2fix(go->GetSize(SIZE_GRECT_LEFT));
+	fprintf(oFile, "<?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");
+	fprintf(oFile, "<svg %s width=\"%d\" height=\"%d\" style=\"stroke-linecap:round\">\n", 
+		defs.svgAttr ? defs.svgAttr : "", w, h);
+	if(defs.svgScript) {
+		fprintf(oFile, "<defs>\n<script type=\"text/ecmascript\"><![CDATA[\n");
+		fprintf(oFile, "\n%s\n", defs.svgScript);
+		fprintf(oFile, "\n]]></script>\n</defs>\n\n");
+		}
+	fprintf(oFile, "<g transform=\"scale(0.1)\" style=\"font-family:Helvetica\">\n");
+	return true;
+}
+
+bool
+ExportSVG::EndPage()
+{
+	fprintf(oFile, "</g>\n</svg>\n");
+	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){
+		fprintf(oFile, "%s<g>  <!-- %s with pattern -->\n", indent, 
+			(x2-x1) == (y2-y1) ? "circle" : "ellipse");
+		Indent(true);
+		}
+	fprintf(oFile, "%s<%s%s%s%s cx=\"%d\" cy=\"%d\" ", indent,
+		(x2-x1) == (y2-y1) ? "circle" : "ellipse", nam? " name=\"" : "", 
+		nam ? nam : "", nam ? "\"" : "", (x1+x2)/2, (y1+y2)/2);
+	if((x2-x1) == (y2-y1)) fprintf(oFile, "r=\"%d\"", (x2-x1)/2);
+	else fprintf(oFile, "rx=\"%d\" ry=\"%d\"", (x2-x1)/2, (y2-y1)/2);
+	fprintf(oFile, " style=\"fill:%s; stroke:%s; stroke-width:%d\"/>\n",
+		ColName(dFillCol), ColName(dLineCol), iLineWidth);
+	if(hgo) {
+		fprintf(oFile, "%s<g %s>  <!-- hatch -->\n", indent, tHatchStyle);
+		Indent(true);
+		bUseGroupLine = true;
+		hgo->oCircle(x1, y1, x2, y2);
+		Indent(false);
+		bUseGroupLine = false;
+		fprintf(oFile, "%s</g>\n", indent);
+		Indent(false);
+		fprintf(oFile, "%s</g>\n", indent);
+		}
+	return true;
+}
+
+bool
+ExportSVG::oPolyline(POINT *pts, int cp, char *nam)
+{
+	int i;
+	char tmptxt[40];
+
+	if(cp < 2) return false;
+	if (dPattern){
+		fprintf(oFile, "%s<g style=\"stroke:%s; stroke-width:%d; stroke-linecap:round\">"
+			"<!-- pattern line -->\n", indent, ColName(dLineCol), iLineWidth);
+		Indent(true);
+		bUseGroupLine = true;
+		for (i = 1; i < cp; i++) PatLine(pts[i-1], pts[i]);
+		Indent(false);
+		fprintf(oFile, "%s</g>\n", indent);
+		bUseGroupLine = false;
+		}
+	else {
+		if(cp == 2) return oSolidLine(pts);
+		bOutputPending = false;
+		sprintf(output, "<polyline points=\""); 
+		for(i = 0; i < cp; i++) {
+			sprintf(tmptxt, "%d %d ", pts[i].x, pts[i].y);
+			AddToOutput(tmptxt);
+			}
+		i = strlen(output);
+		if(i) output[i-1] = 0;
+		strcat(output, "\"");
+		if(!bUseGroupLine) {
+			strcpy(tmptxt, " style = \"fill:none; ");
+			AddToOutput(tmptxt);
+			sprintf(tmptxt, "; stroke:%s; ", ColName(dLineCol));
+			AddToOutput(tmptxt);
+			sprintf(tmptxt, "stroke-width:%d\"/>",iLineWidth);
+			AddToOutput(tmptxt);
+			}
+		else strcat(output, "/>");
+		fprintf(oFile, "%s%s\n", indent, output);
+		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){
+		fprintf(oFile, "%s<g>  <!-- rectangle with pattern -->\n", indent);
+		Indent(true);
+		}
+	fprintf(oFile, "%s<rect%s%s%s x=\"%d\" y=\"%d\" width=\"%d\" height=\"%d\" "
+		"style=\"fill:%s; stroke:%s; stroke-width:%d\"/>\n",
+		indent, nam? " name=\"" : "", nam ? nam : "", nam ? "\"" : "",
+		x1, y1, x2-x1-1, y2-y1-1, ColName(dFillCol), ColName(dLineCol), iLineWidth);
+	if(hgo) {
+		fprintf(oFile, "%s<g %s>  <!-- hatch -->\n", indent, tHatchStyle);
+		Indent(true);
+		bUseGroupLine = true;
+		hgo->oRectangle(x1, y1, x2, y2, 0L);
+		Indent(false);
+		bUseGroupLine = false;
+		fprintf(oFile, "%s</g>\n", indent);
+		Indent(false);
+		fprintf(oFile, "%s</g>\n", indent);
+		}
+	return true;
+}
+
+bool
+ExportSVG::oSolidLine(POINT *p)
+{
+	if(bUseGroupLine) fprintf(oFile, "%s<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\"/>\n",
+		indent, p[0].x, p[0].y, p[1].x, p[1].y);
+	else fprintf(oFile, "%s<line x1=\"%d\" y1=\"%d\" x2=\"%d\" y2=\"%d\" "
+		"style=\"stroke:%s; stroke-width:%d\"/>\n",
+		indent, p[0].x, p[0].y, p[1].x, p[1].y, ColName(dLineCol), iLineWidth);
+	return true;
+}
+
+bool
+ExportSVG::oTextOut(int x, int y, char *txt, int cb)
+{
+	int h, ix, iy;
+	char tmptxt[140], *nt;
+
+	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*.8f);
+	ix = x;
+	sprintf(output, "<text x=\"%d\" y=\"%d\" ", ix, iy);
+	if(fabs(TxtSet.RotBL) >.01 || fabs(TxtSet.RotCHAR) >.01) {
+		sprintf(tmptxt,"transform=\"rotate(%.0f,%d,%d)\" ", -TxtSet.RotBL, ix, iy);
+		strcat(output, tmptxt);
+		}
+	strcpy(tmptxt, "style=\"font-family:");
+	switch(TxtSet.Font) {
+	case FONT_TIMES:	strcat(tmptxt, "Times;");		break;
+	case FONT_COURIER:	strcat(tmptxt, "Courier;");		break;
+	default:			strcat(tmptxt, "Helvetica;");	break;
+		}
+	if(TxtSet.Style & TXS_ITALIC) strcat(tmptxt, " font-style:italic;");
+	if(TxtSet.Style & TXS_BOLD) strcat(tmptxt, " font-weight:bold;");
+	if(TxtSet.Style & TXS_UNDERLINE) strcat(tmptxt, " text-decoration:underline;");
+	AddToOutput(tmptxt);
+	sprintf(tmptxt, " fill:%s; stroke:%s; ", ColName(TxtSet.ColTxt),
+		ColName(TxtSet.ColTxt));
+	AddToOutput(tmptxt);
+	sprintf(tmptxt, "font-size:%d; text-anchor:%s \">", TxtSet.iSize, 
+		(TxtSet.Align & TXA_HRIGHT) ? "end" : (TxtSet.Align & TXA_HCENTER) ? "middle":"start");
+	AddToOutput(tmptxt);
+	nt = str2xml(txt);
+	if((strlen(indent)+strlen(nt)+strlen(output)) <110) strcat(output, nt);
+	else {
+		fprintf(oFile, "%s%s\n", indent, output);
+		strcpy(output, nt);
+		}
+	if((strlen(indent) + strlen(output)) <104) 
+		fprintf(oFile, "%s%s</text>\n", indent, output);
+	else {
+		fprintf(oFile, "%s%s\n", indent, output);
+		fprintf(oFile, "</text>\n");
+		}
+	return true;
+}
+
+bool
+ExportSVG::oPolygon(POINT *pts, int cp, char *nam)
+{
+	int i;
+	char tmptxt[40];
+
+	if(cp <3) return false;
+	if(hgo){
+		fprintf(oFile, "%s<g>  <!-- polygon with pattern -->\n", indent);
+		Indent(true);
+		}
+	bOutputPending = false;
+	sprintf(output, "<polygon%s%s%s points=\"",
+		nam? " name=\"" : "", nam ? nam : "", nam ? "\"" : ""); 
+	for(i = 0; i < cp; i++) {
+		sprintf(tmptxt, "%d %d ", pts[i].x, pts[i].y);
+		AddToOutput(tmptxt);
+		}
+	i = strlen(output);
+	if(i) output[i-1] = 0;
+	strcat(output, "\" ");
+	sprintf(tmptxt, "style=\"fill:%s; ", ColName(dFillCol));
+	AddToOutput(tmptxt);
+	sprintf(tmptxt, "stroke:%s; ", ColName(dLineCol));
+	AddToOutput(tmptxt);
+	sprintf(tmptxt, "stroke-width:%d\"/>",iLineWidth);
+	AddToOutput(tmptxt);
+	if(output)fprintf(oFile, "%s%s\n", indent, output);
+	if(bOutputPending)Indent(false);
+	if(hgo) {
+		fprintf(oFile, "%s<g %s>  <!-- hatch -->\n", indent, tHatchStyle);
+		Indent(true);
+		bUseGroupLine = true;
+		hgo->oPolygon(pts, cp);
+		Indent(false);
+		bUseGroupLine = false;
+		fprintf(oFile, "%s</g>\n", indent);
+		Indent(false);
+		fprintf(oFile, "%s</g>\n", indent);
+		}
+	return true;
+}
+
+void
+ExportSVG::Indent(bool ind)
+{
+	int i = strlen(indent);
+
+	if(i > 20 && ind) return;
+	if(ind) strcat(indent, "   ");
+	else if(i>2) indent[i-3] = 0;
+}
+
+void
+ExportSVG::AddToOutput(char *txt)
+{
+	if((strlen(txt) + strlen(output) + strlen(indent)) < 110)strcat(output, txt);
+	else {
+		fprintf(oFile, "%s%s\n", indent, output);
+		if(!bOutputPending) Indent(true);
+		bOutputPending = true;
+		strcpy(output, txt);
+		}
+}
+char *
+ExportSVG::ColName(DWORD col)
+{
+	static char txt1[20], txt2[20];
+	static int sw;
+
+	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++;
+	if(sw & 0x01) {
+		sprintf(txt1, "rgb(%d,%d,%d)", col & 0xff, (col>>8) & 0xff, (col>>16) &0xff);
+		return txt1;
+		}
+	else {
+		sprintf(txt2, "rgb(%d,%d,%d)", col & 0xff, (col>>8) & 0xff, (col>>16) &0xff);
+		return txt2;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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;
+	FILE *oFile;
+	DWORD CurrCol;
+
+	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;
+	if(FileName)name = strdup(FileName);
+	else name = 0L;
+	oFile = 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)
+{
+	char FontName[30];
+
+	if(!set->iSize && set->fSize > 0.0f) set->iSize = un2iy(set->fSize);
+	if(!set->iSize) return false;
+	if(set->iSize == TxtSet.iSize && set->Style == TxtSet.Style && 
+		set->Font == TxtSet.Font){
+		anyOutput::SetTextSpec(set);
+		return true;
+		}
+	anyOutput::SetTextSpec(set);
+	switch(TxtSet.Font) {
+	case FONT_TIMES:	sprintf(FontName, "(Times");		break;
+	case FONT_COURIER:	sprintf(FontName, "(Courier");		break;
+	default:			sprintf(FontName, "(Helvetica");	break;		//Helvetica
+		}
+	if(TxtSet.Style & TXS_BOLD) strcat(FontName, "-Bold");
+	if(TxtSet.Style & TXS_ITALIC) strcat(FontName, "-Italic");
+	strcat(FontName, ")");
+	fprintf(oFile, "\n%s findfont %d scalefont setfont ", FontName, TxtSet.iSize/10);
+	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) {
+		oFile = fopen(name, "w");
+		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");
+	fprintf(oFile,"%%%%CreationDate: %s", ctime(&ti));
+	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;
+
+	FlushSolLines();
+	oGetTextExtent(txt, cb, &w, &h);
+	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;
+	fprintf(oFile,"\n");
+	if(fabs(TxtSet.RotBL) >.01 || fabs(TxtSet.RotCHAR) >.01) {
+		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);
+		if(CurrCol != TxtSet.ColTxt) fprintf(oFile, "%s ", col2eps(CurrCol = TxtSet.ColTxt));
+		fprintf(oFile, "(%s) show grestore ", txt);
+		}
+	else {
+		fx = ix2eps(ix);			fy = iy2eps(iy);
+		if(CurrCol != TxtSet.ColTxt) fprintf(oFile, "%s ", col2eps(CurrCol = TxtSet.ColTxt));
+		fprintf(oFile,"%.1f %.1f moveto (%s) show ", fx, fy, txt);
+		}
+	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];
+	char tmptxt[10];
+	float r, g, b;
+
+	r = (float)(color & 0xff)/255.0f;
+	g = (float)((color>>8)&0xff)/255.0f;
+	b = (float)((color>>16)&0xff)/255.0f;
+	WriteFloatToBuff(tmptxt, r);	strcpy(txt, tmptxt+1);
+	WriteFloatToBuff(tmptxt, g);	strcat(txt, tmptxt);
+	WriteFloatToBuff(tmptxt, b);	strcat(txt, tmptxt);
+	strcat(txt, " setrgbcolor");
+	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
new file mode 100755
index 0000000..50c52e2
--- /dev/null
+++ b/Fileio.cpp
@@ -0,0 +1,3353 @@
+//FileIO.cpp, Copyright (c) 2001, 2002, 2003, 2004, 2005 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[];
+
+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, typULONG, typFRECT, typNZLFPOINT, typLFPOINT, typPOINT3D,
+	typAXDEF, typPTRAXDEF, typLINEDEF, typFILLDEF, typGOBJ,	typOBJLST,
+	typFPLST, typFPLST3D, typIPLST, typTEXT, typTXTDEF, typPTRTXTDEF, 
+	typLAST = 0x100};
+
+static char *ptr =0L;
+static long ptr_step = 2048;
+static int cbOut, sizeOut = 0, iFile = 0;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// output graph to file, elementary functions
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int OpenOutputFile(char *file)
+{
+	time_t ti;
+
+	if(ptr) free(ptr);
+	ptr = 0L;	cbOut = 0;
+	if(file && BackupFile(file)) {
+		if(-1 ==(iFile = open(file, O_RDWR | O_BINARY | O_CREAT | O_TRUNC,
+			S_IWRITE | S_IREAD))) return -1;
+		ptr = (char *)malloc(sizeOut = 2048);
+		if(!ptr) goto openerr;
+		ti = time(0L);
+		cbOut = sprintf(ptr,";RLP 1.0\n;File \"%s\" created by RLPlot version "
+			""SZ_VERSION"\n;Date/Time: %s",
+			file, ctime(&ti));
+		}
+	return iFile;
+openerr:
+	ptr = 0L;	cbOut = sizeOut = 0;
+	close(iFile);
+	iFile = 0;
+	return -1;
+}
+
+void CloseOutputFile()
+{
+	if(iFile > 0){
+		if(cbOut) write(iFile, ptr, cbOut);
+		close(iFile);
+		}
+	if(ptr) free(ptr);
+	cbOut = sizeOut = 0;
+	ptr = 0L;
+}
+
+void AddStringToOutput(char *str, int length)
+{
+	int i;
+	char *tmp_ptr;
+
+	if((cbOut+length)< sizeOut) {			//fit in buffer
+		memcpy(ptr+cbOut, str, length);
+		cbOut += length;
+		}
+	else if(iFile > 0){						//buffer full: write to file
+		i = sizeOut-cbOut;
+		memcpy(ptr+cbOut, str, i);
+		write(iFile, ptr, 2048);
+		memcpy(ptr, str+i, length-i);
+		cbOut = length-i;
+		}
+	else {									//buffer full: resize buffer
+		if(tmp_ptr = (char*)realloc(ptr, sizeOut+ptr_step)){
+			ptr = tmp_ptr;		sizeOut += ptr_step;	ptr_step += (ptr_step >> 1);
+			AddStringToOutput(str, length);
+			}
+		}
+}
+
+void WriteTypObjLst(GraphObj **ptr, int count)
+{
+	int i, j, c, n;
+	char tmp[512];
+	
+	if(!ptr) return;
+	for(i = c = 0; i <= count/16; i++) {
+		if(i) c =  sprintf(tmp, "   ");
+		for(j = 0; (n=i*16+j) <count && j < 16; j++) {
+			c += sprintf(tmp+c, "%s%ld", j ? " " : "", Notary->RegisterGO(ptr[n]));
+			}
+		if(n >= count) c += sprintf(tmp+c, "}");
+		c += sprintf(tmp+c, "\n");
+		AddStringToOutput(tmp, c);
+		}
+}
+
+void WriteTypIpLst(POINT *ptr, long count)
+{
+	long i, j, c, n;
+	char tmp[256];
+
+	if(!ptr) return;
+	for(i = 0; i <= count/8; i++) {
+		for(j = c = 0; (n =i*8+j) <count && j < 8; j++) {
+			c += sprintf(tmp+c, " %ld", ptr[n].x);
+			c += sprintf(tmp+c, " %ld", ptr[n].y);
+			}
+		if(n >= count) c += sprintf(tmp+c, "}");
+		c += sprintf(tmp+c, "\n");
+		AddStringToOutput(tmp, c);
+		}
+}
+
+void WriteTypFpLst(lfPOINT *ptr, long count)
+{
+	long i, j, c, n;
+	char tmp[256];
+
+	if(!ptr) return;
+	for(i = 0; i <= count/8; i++) {
+		for(j = c = 0; (n =i*8+j) <count && j < 8; j++) {
+			c += WriteFloatToBuff(tmp+c, ptr[n].fx);
+			c += WriteFloatToBuff(tmp+c, ptr[n].fy);
+			}
+		if(n >= count) c += sprintf(tmp+c, "}");
+		c += sprintf(tmp+c, "\n");
+		AddStringToOutput(tmp, c);
+		}
+}
+
+void WriteTypFpLst3D(fPOINT3D *ptr, long count)
+{
+	long i, j, c, n;
+	char tmp[256];
+
+	if(!ptr) return;
+	for(i = 0; i <= count/5; i++) {
+		for(j = c = 0; (n =i*5+j) <count && j < 5; j++) {
+			c += WriteFloatToBuff(tmp+c, ptr[n].fx);
+			c += WriteFloatToBuff(tmp+c, ptr[n].fy);
+			c += WriteFloatToBuff(tmp+c, ptr[n].fz);
+			}
+		if(n >= count && j) c += sprintf(tmp+c, "}\n");
+		else if(n < count) c += sprintf(tmp+c, "\n");
+		AddStringToOutput(tmp, c);
+		}
+}
+
+bool ExecOutput(long id, char *Class, descIO *Desc)
+{
+	static char buff[800];
+	int i, cb, last;
+	fRECT *fr;
+	AxisDEF *ax;
+	LineDEF *ld;
+	FillDEF *fd;
+	TextDEF *tx;
+
+	cb = sprintf(buff, "\n[%ld=%s]\n", id, Class);
+	cObsW++;
+	for(i = 0; Desc[i].label; i++) {
+		last = cb;
+		cb += sprintf(buff+cb,"%s=", Desc[i].label);
+		switch(Desc[i].type & 0xff){
+		case typNZINT:
+			if(!(*(int*)Desc[i].ptr)) {
+				cb = last;
+				break;
+				}
+			//if not zero value continue as if int
+		case typINT:
+			cb += sprintf(buff+cb," %d\n", *(int*)Desc[i].ptr);
+			break;
+		case typNZLFLOAT:
+			if(*((double*)Desc[i].ptr) < 0.0001) {
+				cb = last;
+				break;
+				}
+			//if not zero or negative continue as if float
+		case typLFLOAT:
+			cb += WriteFloatToBuff(buff+cb, *(double*)Desc[i].ptr);
+			cb += sprintf(buff+cb, "\n");
+			break;
+		case typDWORD:
+			cb += sprintf(buff+cb," 0x%08x\n", *(DWORD *)Desc[i].ptr);
+			break;
+		case typULONG:
+			cb += sprintf(buff+cb," %ld\n", *(unsigned long*)Desc[i].ptr);
+			break;
+		case typFRECT:
+			fr = (fRECT*)Desc[i].ptr;
+			cb += WriteFloatToBuff(buff+cb, fr->Xmin);
+			cb += WriteFloatToBuff(buff+cb, fr->Ymax);
+			cb += WriteFloatToBuff(buff+cb, fr->Xmax);
+			cb += WriteFloatToBuff(buff+cb, fr->Ymin);
+			cb += sprintf(buff+cb, "\n");
+			break;
+		case typNZLFPOINT:
+			if(((lfPOINT *)Desc[i].ptr)->fx == ((lfPOINT *)Desc[i].ptr)->fy &&
+				((lfPOINT *)Desc[i].ptr)->fx == 0.0f){
+				cb = last;
+				break;
+				}
+			//if not zero continue as if fPOINT
+		case typLFPOINT:
+			cb += WriteFloatToBuff(buff+cb, ((lfPOINT *)Desc[i].ptr)->fx);
+			cb += WriteFloatToBuff(buff+cb, ((lfPOINT *)Desc[i].ptr)->fy);
+			cb += sprintf(buff+cb, "\n");
+			break;
+		case typPOINT3D:
+			cb += WriteFloatToBuff(buff+cb, ((fPOINT3D *)Desc[i].ptr)->fx);
+			cb += WriteFloatToBuff(buff+cb, ((fPOINT3D *)Desc[i].ptr)->fy);
+			cb += WriteFloatToBuff(buff+cb, ((fPOINT3D *)Desc[i].ptr)->fz);
+			cb += sprintf(buff+cb, "\n");
+			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
+			cb += sprintf(buff+cb, "0x%08x",  ax->flags);
+			cb += WriteFloatToBuff(buff+cb, ax->min);
+			cb += WriteFloatToBuff(buff+cb, ax->max);
+			cb += WriteFloatToBuff(buff+cb, ax->loc[0].fx);
+			cb += WriteFloatToBuff(buff+cb, ax->loc[0].fy);
+			cb += WriteFloatToBuff(buff+cb, ax->loc[0].fz);
+			cb += WriteFloatToBuff(buff+cb, ax->loc[1].fx);
+			cb += WriteFloatToBuff(buff+cb, ax->loc[1].fy);
+			cb += WriteFloatToBuff(buff+cb, ax->loc[1].fz);
+			cb += WriteFloatToBuff(buff+cb, ax->Start);
+			cb += WriteFloatToBuff(buff+cb, ax->Step);
+			cb += WriteFloatToBuff(buff+cb, ax->Center.fx);
+			cb += WriteFloatToBuff(buff+cb, ax->Center.fy);
+			cb += WriteFloatToBuff(buff+cb, ax->Radius);
+			cb += sprintf(buff+cb," %d", ax->nBreaks);
+			if(ax->nBreaks) {
+				cb += sprintf(buff+cb, " {");
+				AddStringToOutput(buff, cb);
+				cb = 0;
+				WriteTypFpLst(ax->breaks, ax->nBreaks);
+				}
+			cb += sprintf(buff+cb, "\n");
+			break;
+		case typLINEDEF:
+			ld = (LineDEF *)Desc[i].ptr;
+			cb += WriteFloatToBuff(buff+cb, ld->width);
+			cb += WriteFloatToBuff(buff+cb, ld->patlength);
+			cb += sprintf(buff+cb, ld->color ? " 0x%08x" : " 0x0", ld->color);
+			cb += sprintf(buff+cb, ld->pattern ? " 0x%08x\n" : " 0x0\n", ld->pattern);
+			break;
+		case typFILLDEF:
+			fd = (FillDEF *)Desc[i].ptr;
+			//we set the 'hatch' member to zero: reconstruct after read
+			cb += sprintf(buff+cb," %d 0x%08x", fd->type, fd->color);
+			cb += WriteFloatToBuff(buff+cb, fd->scale);
+			cb += sprintf(buff+cb," 0x0 0x%08x\n", fd->color2);
+			break;
+		case typGOBJ:
+			if(*(GraphObj **)(Desc[i].ptr)) cb += 
+				sprintf(buff+cb," %ld\n", Notary->RegisterGO(*(GraphObj **)(Desc[i].ptr)));
+			else cb = last;
+			break;
+		case typOBJLST:
+			if(!*(GraphObj ***)(Desc[i].ptr)){
+				cb = last;
+				break;
+				}
+			cb += sprintf(buff+cb, "(%ld){", Desc[i].count ? *Desc[i].count : 0L);
+			AddStringToOutput(buff, cb);
+			cb = 0;
+			WriteTypObjLst(*(GraphObj ***)Desc[i].ptr, Desc[i].count ? *Desc[i].count : 0L);
+			break;
+		case typIPLST:
+			if(!*(POINT**)(Desc[i].ptr)){
+				cb = last;
+				break;
+				}
+			cb += sprintf(buff+cb, "(%ld){", Desc[i].count ? *Desc[i].count : 0L);
+			AddStringToOutput(buff, cb);
+			cb = 0;
+			WriteTypIpLst(*(POINT**)Desc[i].ptr, Desc[i].count ? *Desc[i].count : 0L);
+			break;
+		case typFPLST:
+			if(!*(lfPOINT**)(Desc[i].ptr)){
+				cb = last;
+				break;
+				}
+			cb += sprintf(buff+cb, "(%ld){", Desc[i].count ? *Desc[i].count : 0L);
+			AddStringToOutput(buff, cb);
+			cb = 0;
+			WriteTypFpLst(*(lfPOINT**)Desc[i].ptr, Desc[i].count ? *Desc[i].count : 0L);
+			break;
+		case typFPLST3D:
+			if(!*(fPOINT3D**)(Desc[i].ptr)){
+				cb = last;
+				break;
+				}
+			cb += sprintf(buff+cb, "(%ld){", Desc[i].count ? *Desc[i].count : 0L);
+			AddStringToOutput(buff, cb);
+			cb = 0;
+			WriteTypFpLst3D(*(fPOINT3D**)Desc[i].ptr, Desc[i].count ? *Desc[i].count : 0L);
+			break;
+		case typTEXT:
+			if(!*(char**)(Desc[i].ptr)) cb = last;
+			else cb += sprintf(buff+cb, "\"%s\"\n", *(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) {
+				cb = last;
+				break;
+				}
+			cb += sprintf(buff+cb, " 0x%08x 0x%08x %g %g %g %d %d %d %d", 
+				tx->ColTxt, tx->ColBg, tx->fSize, tx->RotBL, tx->RotCHAR,
+				tx->Align, tx->Mode, tx->Style, tx->Font);
+			if(tx->text) cb += sprintf(buff+cb, " \"%s\"\n", tx->text);
+			else cb += sprintf(buff+cb, "\n");
+			break;
+			}
+		if(Desc[i].type & typLAST) break;
+		}
+	AddStringToOutput(buff, cb);
+	return true;
+}
+
+void ReadTypIpLst(POINT *ptr, long count, unsigned char *first)
+{
+	long f[20];
+	int j, k;
+	long i;
+
+	if(!ptr || !first) return;
+	k = sscanf((char*)first, "%ld%ld%ld%ld%ld%ld%ld%ld%ld%ld%ld%ld%ld%ld%ld%ld%ld%ld%ld%ld", 
+		&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]);
+	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;
+	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]);
+	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;
+	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]);
+	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 = strlen(*txt);
+
+	do {
+		mlines = false;
+		Cache->ReadLine(tmp, sizeof(tmp));
+		for(i = 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 = strlen(tmp+i)) && (ntxt = (char*)realloc(*txt, cb + j + 1))) {
+			strcpy(ntxt+cb, tmp+i);
+			cb += j;	*(txt) = ntxt;
+			}
+		} while (mlines);
+}
+
+bool ExecInput(descIO *Desc)
+{
+	char c, tmp[1000], tmp2[20];
+	int i, j, k, l;
+	bool match, mlines;
+	unsigned long 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(tmp, Desc[j].label)) {
+				Cache->ReadLine(tmp, sizeof(tmp));
+				switch(Desc[j].type & 0xff){
+				case typNZINT:
+				case typINT:
+					sscanf(tmp, "%d", (int*)Desc[j].ptr);
+					break;
+				case typNZLFLOAT:
+				case typLFLOAT:
+					sscanf(tmp, "%lf", (double*)Desc[j].ptr);
+					break;
+				case typDWORD:
+					sscanf(tmp, "%x", (DWORD*)Desc[j].ptr);
+					break;
+				case typULONG:
+					sscanf(tmp, "%ld", (unsigned long*)Desc[j].ptr);
+					break;
+				case typFRECT:
+					fr = (fRECT*) Desc[j].ptr;
+					sscanf(tmp, "%lf%lf%lf%lf", &fr->Xmin, &fr->Ymax, &fr->Xmax, &fr->Ymin);
+					break;
+				case typNZLFPOINT:
+				case typLFPOINT:
+					lfp = (lfPOINT*) Desc[j].ptr;
+					sscanf(tmp, "%lf%lf", &lfp->fx, &lfp->fy);
+					break;
+				case typPOINT3D:
+					lfp3d = (fPOINT3D*) Desc[j].ptr;
+					sscanf(tmp, "%lf%lf%lf", &lfp3d->fx, &lfp3d->fy, &lfp3d->fz);
+					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;
+					sscanf(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);
+					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;
+					sscanf(tmp,"%lf%lf%x%x", &ld->width, &ld->patlength, &ld->color, &ld->pattern);
+					break;
+				case typFILLDEF:
+					fd = (FillDEF*) Desc[j].ptr;
+					sscanf(tmp, "%d%x%lf%x%x", &fd->type, &fd->color, &fd->scale, &fd->hatch, &fd->color2);
+					fd->hatch = 0L;
+					break;
+				case typGOBJ:
+					*(GraphObj**)(Desc[j].ptr) = Notary->PopGO(atol(tmp));
+					break;
+				case typOBJLST:
+					for(i = 0; i < 10 && tmp[i] != '(' && tmp[i]; i++);
+					if(!tmp[i]) break;
+					if(sscanf(tmp+i+1, "%ld", &il) && il) {
+						*Desc[j].count = il;
+						if(!*(GraphObj***)(Desc[j].ptr)){
+							*(GraphObj***)(Desc[j].ptr) = (GraphObj**)calloc(il, sizeof(GraphObj*));
+							}
+						if((gobs = *(GraphObj***)(Desc[j].ptr))){
+							while(tmp[i-1] != '{') i++; while(tmp[i-1] < 33) i++;
+							strcat(tmp, " ");
+							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;
+										}
+									}
+								sscanf(tmp2, "%ld", &jl);
+								*gobs++ = Notary->PopGO(jl);
+								if(c == '}') break;
+								}
+							}
+						}	
+					break;
+				case typIPLST:
+					for(i = 0; i < 10 && tmp[i] != '(' && tmp[i]; i++);
+					if(!tmp[i]) break;
+					if(sscanf(tmp+i+1, "%ld", &il) && il) {
+						*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;
+					if(sscanf(tmp+i+1, "%ld", &il) && il) {
+						*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;
+					if(sscanf(tmp+i+1, "%ld", &il) && il) {
+						*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 = 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]){
+						*(char**)(Desc[j].ptr) = strdup(tmp+i);
+						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
+						}
+					sscanf(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);
+					tx->iSize = 0;
+					for(i = strlen(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 = strdup(tmp+l+1);
+						}
+					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(tmp, sizeof(tmp));	//   then continue
+						}
+					j= 0;
+					}
+				}
+			}while(!match);
+		}
+}
+
+bool SaveGraphAs(GraphObj *g)
+{
+	char *name = 0L;
+	int i;
+
+	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 = 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();
+		}
+	if(Notary) delete Notary;	Notary = 0L;
+	return true;
+}
+
+char *GraphToMem(GraphObj *g, long *size)
+{
+	static char *ret;
+
+	if(Notary || !g) {
+		ErrorBox("Output pending or\nno graph.");
+		return false;
+		}
+	cObsW = 0;
+	iFile = cbOut = sizeOut = 0;	ptr = 0L;	ptr_step = 2048;
+	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 = cbOut = sizeOut = 0;	ptr = 0L;	ptr_step = 2048;
+		return ret;
+		}
+	return 0L;
+}
+
+void UpdGOfromMem(GraphObj *go, unsigned char *buff)
+{
+	int i=0;
+
+	if(!go || !buff) return;
+	iFile = 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)
+{
+	unsigned char c, tmp[80];
+	char debug[80];
+	unsigned long id, lid;
+	int i;
+	unsigned int hv;
+	GraphObj *go;
+
+	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;
+		sscanf((char*)tmp, "%ld", &id);
+		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 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 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;
+		default:
+			sprintf(debug, "Object %ld in file\n(Class = \"%s\")\nhash #%d\nis unknown.",
+				id, tmp, hv);
+			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((go = Notary->PopGO(lid))) {
+		go->Command(CMD_SET_DATAOBJ, 0L, 0L);
+		if(root->Id == GO_PAGE) {
+			if(go->Id == GO_PAGE){
+				if(!(root->parent->Command(CMD_DROP_GRAPH,(void *)go, 0L))) DeleteGO(go);
+				}
+			else if(!(root->Command(CMD_DROP_GRAPH, (void *)go, 0L))) DeleteGO(go);
+			}
+		else if(!(root->Command(CMD_DROP_GRAPH, (void *)go, 0L))){
+			DeleteGO(go);	go = 0L;
+			}
+		if(go) go->Command(CMD_FILENAME, name, 0L);
+		}
+	delete Notary;
+	Notary = 0L;
+	delete Cache;
+	Cache = 0L;
+	return true;
+
+ReadErr:
+	Cache->Close();
+	close(iFile);
+	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 typULONG:
+			*(unsigned long*)Desc[i].ptr = 0L;
+			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 typULONG:
+			SavVarAdd(Desc[i].ptr, sizeof(unsigned long));
+			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, 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 = parent ? parent->GetSize(SIZE_SYMBOL): defs.GetSize(SIZE_SYMBOL);
+		SymLine.color = parent ? parent->GetColor(COL_SYM_LINE) : defs.Color(COL_SYM_LINE);
+		SymLine.width = parent ? parent->GetSize(SIZE_SYM_LINE) : defs.GetSize(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 = defs.GetSize(SIZE_BUBBLE_LINE);
+		BubbleFillLine.width = defs.GetSize(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 = parent ? parent->GetSize(SIZE_BAR): defs.GetSize(SIZE_BAR);
+		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;
+
+	if(!(file = fopen(name, "r"))) {
+		sprintf(TmpTxt, "DataLine: open failed for file \"%s\"", name);
+		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 {
+			c = fscanf(file, "%lf%lf", &Values[i].fx, &Values[i].fy);
+			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 {
+			c = fscanf(file, "%lf", &Values[i].fy);
+			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", typLAST | typTEXT, &file1, 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.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);
+		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", typLAST | typLINEDEF, &ErrLine, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		SizeBar = defs.GetSize(SIZE_ERRBAR);
+		ErrLine.width = defs.GetSize(SIZE_ERRBAR_LINE);
+		ErrLine.color = defs.Color(COL_SYM_LINE);
+		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 = defs.GetSize(SIZE_ARROW_CAPWIDTH);
+		cl = defs.GetSize(SIZE_ARROW_CAPLENGTH);
+		LineDef.color = parent ? parent->GetColor(COL_DATA_LINE) : defs.Color(COL_DATA_LINE);
+		LineDef.width = parent ? parent->GetSize(SIZE_ARROW_LINE) : defs.GetSize(SIZE_ARROW_LINE);
+		type = ARROW_LINE;
+		dh1 = dh2 = 0L;
+		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 = defs.GetSize(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", typLAST | typLINEDEF, &LineDef, 0L}};
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		size = defs.GetSize(SIZE_WHISKER);
+		LineDef.width = defs.GetSize(SIZE_WHISKER_LINE);
+		LineDef.color = defs.Color(COL_WHISKER);
+		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 = defs.GetSize(SIZE_SYMBOL);
+		Line.color = defs.Color(COL_SYM_LINE);
+		Line.width = defs.GetSize(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 = defs.GetSize(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 = defs.GetSize(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 = defs.GetSize(SIZE_ARROW_CAPWIDTH);
+		cl = defs.GetSize(SIZE_ARROW_CAPLENGTH);
+		Line.color = defs.Color(COL_ARROW);
+		Line.width = defs.GetSize(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},
+		{"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 = defs.GetSize(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 = defs.GetSize(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;	fmt_txt = 0L;
+		return true;
+	case FILE_READ:
+		return ExecInput(Desc);
+	case FILE_WRITE:
+		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},
+		{"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 = defs.GetSize(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;
+		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
+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 = defs.GetSize(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
+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 = defs.GetSize(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 = defs.GetSize(SIZE_DRECT_TOP);
+		B_Rect.Xmin = defs.GetSize(SIZE_DRECT_LEFT);
+		B_Rect.Xmax = B_Rect.Xmin + 1.5*(d = defs.GetSize(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;
+		trc.left = trc.right = trc.top = trc.bottom = 0;
+		if(!name) name=strdup("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[] = {
+		{"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", typLAST | typOBJLST, &Labels, &nPoints}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		DefSym = SYM_CIRCLE;
+		DefSel = 0x01;
+		dirty = true;
+		if(name) {
+			sprintf(TmpTxt, "xy-plot (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		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;
+}
+
+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},
+		{"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;
+		if(name) {
+			sprintf(TmpTxt, "freq. dist. (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		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},
+		{"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) {
+			sprintf(TmpTxt, "regression (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		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[] = {
+		{"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 = defs.GetSize(SIZE_BUBBLE_LINE);
+		BubbleFillLine.color = defs.Color(COL_BUBBLE_FILLLINE);
+		BubbleFillLine.width = defs.GetSize(SIZE_BUBBLE_HATCH_LINE);
+		BubbleFill.hatch = &BubbleFillLine;
+		dirty = true;
+		if(name) {
+			sprintf(TmpTxt, "bubbles (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		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},
+		{"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) {
+			sprintf(TmpTxt, "polar root (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		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(TheLine) TheLine->RegGO(n);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+BoxPlot::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Bounds", typFRECT, &Bounds, 0L},
+		{"boDist", typLFPOINT, &BoxDist, 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},
+		{"Line", typLAST | typGOBJ, &TheLine, 0L}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		dirty = true;
+		InitVarsGO(Desc);
+		if(name) {
+			sprintf(TmpTxt, "boxes (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		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(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},
+		{"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},
+		{"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) {
+			sprintf(TmpTxt, "stack (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		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},
+		{"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) {
+			sprintf(TmpTxt, "pie chart (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		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},
+		{"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},
+		{"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) {
+			sprintf(TmpTxt, "xyz-plot (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		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},
+		{"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},
+		{"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) {
+			sprintf(TmpTxt, "ribbon (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		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);
+		((notary*)n)->AddRegGO(this);
+		}
+}
+
+bool
+Grid3D::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"Start", typPOINT3D, &start, 0L},
+		{"Step", typPOINT3D, &step, 0L},
+		{"Line", typLINEDEF, &Line, 0L},
+		{"lines", typLAST | typOBJLST, &lines, &nLines}};
+	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) {
+			sprintf(TmpTxt, "grid (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		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;
+		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), "Grid3D", Desc);
+		}
+	return false;
+}
+
+bool
+Limits::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Bounds", typLAST | typFRECT, &Bounds, 0L}};
+
+	switch(rw) {
+	case INIT_VARS:
+		if(name) {
+			sprintf(TmpTxt, "limits (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		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)
+{
+	char *tmp_cmdxy = 0L, *tmp_param = 0L;
+	descIO Desc[] = {
+		{"x1", typNZLFLOAT, &x1, 0L},
+		{"x2", typNZLFLOAT, &x2, 0L},
+		{"xstep", typNZLFLOAT, &xstep, 0L},
+		{"Line", typLINEDEF, &Line, 0L},
+		{"f_xy", typTEXT, &tmp_cmdxy, 0L},
+		{"param", typTEXT, &tmp_param, 0L},
+		{"DataLine", typLAST | typGOBJ, &dl, 0L}};
+	int i, j;
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		InitVarsGO(Desc);
+		cmdxy = param = 0L;
+		memcpy(&Line, defs.GetLine(), sizeof(LineDEF));
+		if(name) {
+			sprintf(TmpTxt, "function (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		if(tmp_cmdxy && tmp_cmdxy[0]) cmdxy = tmp_cmdxy;
+		if(tmp_param && tmp_param[0]) param = tmp_param;
+		if(dl) dl->parent = this;
+		return true;
+	case FILE_WRITE:
+		if(dl) dl->FileIO(rw);
+		if(cmdxy) {
+			for(i = j = 0; cmdxy[i]; i++) {
+				if(cmdxy[i] == '\n') j += sprintf(TmpTxt+j, "\\n");
+				else TmpTxt[j++] = cmdxy[i];
+				}
+			TmpTxt[j] = 0;
+			if(i) tmp_cmdxy = strdup(TmpTxt);
+			}
+		if(param) {
+			for(i = j = 0; param[i]; i++) {
+				if(param[i] == '\n') j += sprintf(TmpTxt+j, "\\n");
+				else TmpTxt[j++] = param[i];
+				}
+			TmpTxt[j] = 0;
+			if(i) tmp_param = strdup(TmpTxt);
+			}
+		ExecOutput(Notary->RegisterGO(this), "Function", Desc);
+		if(tmp_cmdxy) free(tmp_cmdxy);
+		if(tmp_param) free(tmp_param);
+		}
+	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)
+{
+	char *tmp_cmdxy = 0L, *tmp_parxy = 0L;
+	descIO Desc[] = {
+		{"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, &tmp_cmdxy, 0L},
+		{"p_xy", typTEXT, &tmp_parxy, 0L},
+		{"Symbols", typLAST | typOBJLST, &Symbols, &nPoints}};
+	int i, j;
+
+	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) {
+			sprintf(TmpTxt, "fit function (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		return true;
+	case FILE_READ:
+		ExecInput(Desc);
+		if(tmp_cmdxy && tmp_cmdxy[0]) cmdxy = tmp_cmdxy;
+		if(tmp_parxy && tmp_parxy[0]) parxy = tmp_parxy;
+		if(Symbols) for(i = 0; i < nPoints; i++)
+			if(Symbols[i]) Symbols[i]->parent = this;
+		return true;
+	case FILE_WRITE:
+		if(cmdxy) {
+			for(i = j = 0; cmdxy[i]; i++) {
+				if(cmdxy[i] == '\n') j += sprintf(TmpTxt+j, "\\n");
+				else TmpTxt[j++] = cmdxy[i];
+				}
+			TmpTxt[j] = 0;
+			if(i) tmp_cmdxy = strdup(TmpTxt);
+			}
+		if(parxy) {
+			for(i = j = 0; parxy[i]; i++) {
+				if(parxy[i] == '\n') j += sprintf(TmpTxt+j, "\\n");
+				else TmpTxt[j++] = parxy[i];
+				}
+			TmpTxt[j] = 0;
+			if(i) tmp_parxy = strdup(TmpTxt);
+			}
+		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
+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 = defs.GetSize(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;
+		sprintf(TmpTxt, "Error open file \"%s\"\nfor axis ticks", name);
+		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 = 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 = defs.GetSize(SIZE_AXIS_LINE);
+		sizAxTick = defs.GetSize(SIZE_AXIS_TICKS);
+		sizAxTickLabel = defs.GetSize(SIZE_TICK_LABELS);
+		colAxis = parent ? parent->GetColor(COL_AXIS) : defs.Color(COL_AXIS);
+		GridLine.color = 0x00808080L;
+		GridLine.pattern = 0xf8f8f8f8L;
+		brksymsize = defs.GetSize(SIZE_TICK_LABELS);
+		brkgap = defs.GetSize(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 = parent ? parent->GetSize(SIZE_TICK_LABELS) : defs.GetSize(SIZE_TICK_LABELS);
+		tlbdef.Align = TXA_VCENTER | TXA_HCENTER;
+		tlbdef.Style = TXS_NORMAL;
+		tlbdef.Mode = TXM_TRANSPARENT;
+		tlbdef.Font = FONT_HELVETICA;
+		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 && NumTicks) 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;
+	//DEBUG: must initialize rot_vec, rot_ang in case they are not present in file
+	descIO Desc[] = {
+		{"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},
+		{"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 = defs.GetSize(SIZE_GRECT_LEFT) + defs.GetSize(SIZE_DRECT_LEFT);
+		cub2.fx = defs.GetSize(SIZE_GRECT_LEFT) + defs.GetSize(SIZE_DRECT_RIGHT);
+		cub1.fy = defs.GetSize(SIZE_GRECT_TOP) + defs.GetSize(SIZE_DRECT_BOTTOM);
+		cub2.fy = defs.GetSize(SIZE_GRECT_TOP) + defs.GetSize(SIZE_DRECT_TOP);
+		cub1.fy += defs.GetSize(SIZE_DRECT_TOP);	cub2.fy += defs.GetSize(SIZE_DRECT_TOP);
+		cub1.fz = 0.0;
+		cub2.fz = defs.GetSize(SIZE_DRECT_BOTTOM) - defs.GetSize(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;
+		if(name) {
+			sprintf(TmpTxt, "3D-root (%s)", name);
+			free(name);		name=strdup(TmpTxt);
+			}
+		return true;
+	case FILE_READ:
+		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
+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},
+		{"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 = 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;		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;
+		//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 && parent && parent->Id != GO_PAGE)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]->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(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.dUnits, 0L},
+		{"dtHeight", typINT, &dlgtxtheight, 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);
+		return true;
+	case FILE_WRITE:
+		Notary = new notary();
+		unlink(defs.IniFile);
+		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
new file mode 100755
index 0000000..46f1ebf
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,118 @@
+# Makefile, Copyright 2002-2004 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 Makefile assumes that you installed Trolltechs QT in /usr/local/qt
+#   visit: http://www.trolltech.com for download and licence information
+#
+QTDIR = /usr/lib/qt3
+CC = g++
+#CFLAGS = -I$(QTDIR)/include -pipe -O2 -g
+CFLAGS = -I$(QTDIR)/include -pipe -O2
+LIBS = -L$(QTDIR)/lib -L/usr/X11R6/lib
+QTLIBS = -lqt-mt
+X11LIBS = -lX11 -lm
+SRCDIR = ./
+
+GENOBJ = exprlp.o rlplot.o Output.o Utils.o UtilObj.o\
+ Fileio.o Export.o PlotObs.o Axes.o mfcalc.o rlp_math.o
+
+OBJECTS = moc_QT_Spec.o QT_Spec.o Output.o Utils.o UtilObj.o\
+ TheDialog.o rlplot.o Fileio.o PropertyDlg.o spreadwi.o\
+ Export.o PlotObs.o Axes.o ODbuttons.o mfcalc.o rlp_math.o
+
+all:
+	make rlplot QTDIR=$(QTDIR) 
+	make exprlp 
+	make clean
+
+rlplot: $(OBJECTS)
+	$(CC) $(LIBS) -o rlplot $(OBJECTS) $(QTLIBS) $(X11LIBS)
+
+exprlp: $(GENOBJ)
+	$(CC) -o exprlp $(GENOBJ)
+
+clean:
+	rm -f *.o *~
+	rm moc_QT_Spec.cpp
+
+####### Compile
+
+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 $(SRCDIR)QT_Spec.h -o moc_QT_Spec.cpp
+
+mfcalc.o: $(SRCDIR)mfcalc.cpp
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+rlp_math.o: $(SRCDIR)rlp_math.cpp
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+#$(SRCDIR)mfcalc.cpp: $(SRCDIR)mfcalc.y
+#	bison -l -o $@ $<
+
+exprlp.o: $(SRCDIR)exprlp.cpp
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+QT_Spec.o: $(SRCDIR)QT_Spec.cpp $(SRCDIR)QT_Spec.h $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+Output.o: $(SRCDIR)Output.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+Utils.o: $(SRCDIR)Utils.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+UtilObj.o: $(SRCDIR)UtilObj.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+TheDialog.o: $(SRCDIR)TheDialog.cpp $(SRCDIR)TheDialog.h $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+rlplot.o: $(SRCDIR)rlplot.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+Fileio.o: $(SRCDIR)Fileio.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+PropertyDlg.o: $(SRCDIR)PropertyDlg.cpp $(SRCDIR)rlplot.h $(SRCDIR)TheDialog.h
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+spreadwi.o: $(SRCDIR)spreadwi.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+Export.o: $(SRCDIR)Export.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+PlotObs.o: $(SRCDIR)PlotObs.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+ODbuttons.o: $(SRCDIR)ODbuttons.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+Axes.o: $(SRCDIR)Axes.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $<
+
+
+
+
+
+
+
diff --git a/Makefile.win b/Makefile.win
new file mode 100755
index 0000000..f5ca7d1
--- /dev/null
+++ b/Makefile.win
@@ -0,0 +1,97 @@
+# Makefile.win, Copyright 2002-2004 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
+#
+CC = cl.exe
+CFLAGS = /nologo /ML /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /YX /FD
+LIBS = kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib\
+ shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib
+SRCDIR =
+
+GENOBJ = exprlp.obj rlplot.obj Output.obj Utils.obj UtilObj.obj\
+ FileIO.obj Export.obj PlotObs.obj Axes.obj mfcalc.obj rlp_math.obj
+
+OBJECTS = WinSpec.obj Output.obj Utils.obj UtilObj.obj\
+ TheDialog.obj rlplot.obj FileIO.obj PropertyDlg.obj spreadwi.obj\
+ Export.obj PlotObs.obj ODbuttons.obj Axes.obj mfcalc.obj rlp_math.obj
+
+RLPlot: $(OBJECTS) RLPLOT.res
+	link.exe $(LIBS) /nologo /subsystem:windows /incremental:no /pdb:"RLPlot.pdb"\
+ /machine:I386 /out:"RLPlot.exe" $(OBJECTS) RLPLOT.res
+ 
+exprlp: $(GENOBJ)
+	$(CC) -o exprlp $(GENOBJ)
+
+clean:
+	del *.obj
+	del *.pch
+	del *.idb
+	del *.res
+
+####### Compile
+RLPLOT.res: $(SRCDIR)RLPLOT.RC
+	rc.exe /d "NDEBUG" $(SRCDIR)RLPLOT.RC
+
+mfcalc.obj: $(SRCDIR)mfcalc.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)mfcalc.cpp
+
+rlp_math.obj: $(SRCDIR)rlp_math.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)rlp_math.cpp
+
+exprlp.obj: $(SRCDIR)exprlp.cpp
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)exprlp.cpp
+
+WinSpec.obj: $(SRCDIR)WinSpec.cpp $(SRCDIR)WinSpec.h $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)WinSpec.cpp
+
+Output.obj: $(SRCDIR)Output.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)Output.cpp
+
+Utils.obj: $(SRCDIR)Utils.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)Utils.cpp
+
+UtilObj.obj: $(SRCDIR)UtilObj.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)UtilObj.cpp
+
+TheDialog.obj: $(SRCDIR)TheDialog.cpp $(SRCDIR)TheDialog.h $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)TheDialog.cpp
+
+rlplot.obj: $(SRCDIR)rlplot.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)rlplot.cpp
+
+FileIO.obj: $(SRCDIR)FileIO.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)FileIO.cpp
+
+PropertyDlg.obj: $(SRCDIR)PropertyDlg.cpp $(SRCDIR)rlplot.h $(SRCDIR)TheDialog.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)PropertyDlg.cpp
+
+spreadwi.obj: $(SRCDIR)spreadwi.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)spreadwi.cpp
+
+Export.obj: $(SRCDIR)Export.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)Export.cpp
+
+PlotObs.obj: $(SRCDIR)PlotObs.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)PlotObs.cpp
+
+ODbuttons.obj: $(SRCDIR)ODbuttons.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)ODbuttons.cpp
+	
+Axes.obj: $(SRCDIR)Axes.cpp $(SRCDIR)rlplot.h
+	$(CC) $(CFLAGS) -o $@ -c $(SRCDIR)Axes.cpp
+
diff --git a/ODbuttons.cpp b/ODbuttons.cpp
new file mode 100755
index 0000000..f4d94c4
--- /dev/null
+++ b/ODbuttons.cpp
@@ -0,0 +1,1273 @@
+//ODbuttons.cpp, Copyright (c) 2001, 2002, 2003, 2004 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;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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 = {.1f, 1.0f, 0x0L, 0x0L};
+	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
+	POINT pts[10];
+	int ix, iy, np;
+
+	switch(cmd) {
+	case OD_DRAWNORMAL:
+	case OD_DRAWSELECTED:
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00d0d0d0L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		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);
+		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;
+			}
+		if(np) o->oPolyline(pts, np);
+		o->oCircle(ix-2, iy-2, ix+2, iy+2);
+#ifdef _WINDOWS
+		o->oCircle(rec->left+13, rec->bottom-13, rec->left+17, rec->bottom-17);
+		o->oCircle(rec->right-13, rec->top+13, rec->right-17, rec->top+17);
+#else
+		o->oCircle(rec->left+13, rec->bottom-14, rec->left+17, rec->bottom-16);
+		o->oCircle(rec->right-14, rec->top+13, rec->right-16, rec->top+17);
+#endif
+		o->UpdateRect(rec, false);
+		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 = {.1f, 1.0f, 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 : 0x00d0d0d0L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		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 = {.1f, 1.0f, 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 : 0x00d0d0d0L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		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 : 0x00d0d0d0L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		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 = 0x00d0d0d0L;
+			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 : 0x00d0d0d0L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		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 = {.1f, 1.0f, 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 : 0x00d0d0d0L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		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
+			OD_AxisTempl3D(cmd, par, rec, o, data, 410+AxisTempl3D);
+			break;
+		default:
+			Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00d0d0d0L;
+			Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+			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:
+			o->SetFill(&FillR);
+			o->oRectangle(rec->left+9, rec->top+30, rec->left+13, rec->bottom-3);
+			o->oRectangle(rec->right-20, 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);
+			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 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 = 7.0;			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 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;
+			}
+		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 : 0x00d0d0d0L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		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 : 0x00d0d0d0L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		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 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);
+		break;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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 : 0x00d0d0d0L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		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;
+		}
+}
+
diff --git a/Output.cpp b/Output.cpp
new file mode 100755
index 0000000..54d6576
--- /dev/null
+++ b/Output.cpp
@@ -0,0 +1,1829 @@
+//Output.cpp, Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005 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);
+	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::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;
+	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()
+{
+	CurrGO = 0L;
+	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(CurrGraph) {
+				if(CurrGraph->Command(CMD_HIDE_MARK, MrkRect, this)){
+					return true;
+					}
+				else 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 = 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 = 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 = 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;
+#ifdef _WINDOWS
+	disp.bottom = disp.top + TxtSet.iSize;
+#else
+	disp.bottom = disp.top + iround(TxtSet.iSize*1.25);
+#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 = strlen(text);
+	for(i = w = 0; i < cb; i++) w += CharWidth[text[i]];
+	*width = (w * TxtSet.iSize)>>6;
+	*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 {
+	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();						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_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(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 -= 2*iy;	UseRect.bottom += 2*iy;
+	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()					//hatch using sine waves
+{
+	int i, j, y, ix, yinc, *pts;
+	POINT Line[2];
+	double dtmp;
+
+	if(3>(yinc = iround(ybase*.8)))yinc = 3;	if(14>(ix = iround(xbase*2.5)))ix = 14;
+	if(!(pts = (int *)malloc(ix * sizeof(int))))return;
+	for(i = 0; i < ix; i++) {
+		dtmp = sin(6.283185307/((double)ix/(double)i));
+		pts[i] = dtmp > 0.0 ? iround(0.3*ybase*dtmp) : iround(0.3*ybase*dtmp);
+		}
+	for(y = UseRect.top; y <= UseRect.bottom + yinc; y += yinc){
+		Line[0].x = UseRect.left;		Line[1].x = UseRect.left+1;
+		Line[0].y = y;					Line[1].y = y+pts[1];
+		for(j = 2; Line[0].x < UseRect.right+1; 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];
+			}
+		}
+	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::CircGrad()
+{
+	int i;
+	double f;
+	LineDEF ld = {0.0, 1.0, 0x0, 0x0};
+
+	for(i = 1; i < this->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 = strdup(FileName);
+		}
+	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];
+
+
+	if(name && bmo) {
+		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;
+			}
+		*((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;
+		sprintf(prog_name, "RLPlot %s", SZ_VERSION);
+		write(oFile, &header, sizeof(header));
+		write(oFile, &res_info, sizeof(res_info));
+		write(oFile, &res_info, 20);
+		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) {
+					write(oFile, pix_data, 3072);
+					c = 0;
+					}
+				}
+			}
+		write(oFile, pix_data, c);
+		free(pix_data);
+		}
+	oFile = close(oFile);
+	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
new file mode 100755
index 0000000..30c873a
--- /dev/null
+++ b/PlotObs.cpp
@@ -0,0 +1,4427 @@
+//PlotObs.cpp, Copyright (c) 2001, 2002, 2003, 2004, 2005 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)
+{
+	Id = GO_PLOT;
+	Bounds.Xmin = Bounds.Ymin = HUGE_VAL;	Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+	sprintf(TmpTxt, "Plot %d", ++cPlots);
+	name = strdup(TmpTxt);
+	use_xaxis = use_yaxis = 0;
+	hidden = 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:
+		if(parent) return parent->GetSize(select);
+		else return defs.GetSize(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)
+{
+	double dx, dy;
+
+	if(CurrAxes && CurrAxes[idx]) {
+		dx = fabs(CurrAxes[idx]->GetSize(SIZE_XPOS+1) -
+			CurrAxes[idx]->GetSize(SIZE_XPOS));
+		dy = fabs(CurrAxes[idx]->GetSize(SIZE_YPOS+1) -
+			CurrAxes[idx]->GetSize(SIZE_YPOS));
+		if(dx > dy && idx != use_xaxis) {
+			Undo.ValInt(parent, &use_xaxis, 0L);
+			use_xaxis = idx;			return true;
+			}
+		else if(idx != use_yaxis) {
+			Undo.ValInt(parent, &use_yaxis, 0L);
+			use_yaxis = idx;			return true;
+			}
+		}
+	return false;
+}
+
+void
+Plot::ApplyAxes(anyOutput *o)
+{
+	AxisDEF x_axis, y_axis;
+	double dsp;
+	fRECT CurrRect;
+	
+	if(!o || !CurrAxes || !parent) return;
+	memcpy(&x_axis, &o->xAxis, sizeof(AxisDEF));
+	memcpy(&y_axis, &o->yAxis, sizeof(AxisDEF));
+	CurrRect.Xmin = (dsp = parent->GetSize(SIZE_GRECT_LEFT)) +
+		parent->GetSize(SIZE_DRECT_LEFT);
+	CurrRect.Xmax = dsp + parent->GetSize(SIZE_DRECT_RIGHT);
+	if(use_xaxis && CurrAxes[use_xaxis]) {
+		memcpy(&x_axis, CurrAxes[use_xaxis]->axis, sizeof(AxisDEF));
+		if(x_axis.flags & AXIS_DEFRECT) {
+			x_axis.loc[0].fx += dsp;	x_axis.loc[1].fx += dsp;
+			}
+		}
+	else use_xaxis = 0;
+	CurrRect.Ymin = (dsp = parent->GetSize(SIZE_GRECT_TOP)) +
+		parent->GetSize(SIZE_DRECT_BOTTOM);
+	CurrRect.Ymax = dsp + parent->GetSize(SIZE_DRECT_TOP);
+	if(use_yaxis && CurrAxes[use_yaxis]) {
+		memcpy(&y_axis, CurrAxes[use_yaxis]->axis, sizeof(AxisDEF));
+		if(y_axis.flags & AXIS_DEFRECT) {
+			y_axis.loc[0].fy += dsp;	y_axis.loc[1].fy += dsp;
+			}
+		}
+	else use_yaxis = 0;
+	o->SetRect(CurrRect, o->units, &x_axis, &y_axis);
+}
+
+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;
+	double value, old_val;
+	DataObj *CumData = 0L;
+	AccRange *ax = 0L, *ay = 0L;
+
+	if(!xr || !yr || !mode || !data) return 0L;
+	if(!(CumData = new DataObj()))return 0L;
+	if(!(ax = new AccRange(xr))) {
+		delete CumData;		return 0L;
+		}
+	nr = ax->CountItems();
+	if(!(yranges = split(yr, '&', &nc))){
+		delete CumData;		delete ax;		return 0L;
+		}
+	if(CumData->Init(mode == 1 || mode == 2 ? nr : nr * 2, nc+2)){
+		// set x values as first column
+		for(i = 0, ax->GetFirst(&ic, &ir); ax->GetNext(&ic, &ir); i++) {
+			if(data->GetValue(ir, ic, &value)) CumData->SetValue(i, 0, value);
+			CumData->SetValue(i, 1, base);
+			}
+		if(mode == 3 || mode == 4) for(i = 1; i <= nr; i++) {	//complete polygon data
+			if(CumData->GetValue(nr-i, 0, &value)) CumData->SetValue(i-1+nr, 0, value);
+			}
+		//process all y-ranges
+		for (j = 2; j <= (nc+1); j++) if(ay = new AccRange(yranges[j-2])){
+			for(i = 0; i < nr; i++) {
+				if(CumData->GetValue(i, j-1, &value)) CumData->SetValue(i, j, value);
+				}
+			for(i = 0, ay->GetFirst(&ic, &ir); ay->GetNext(&ic, &ir) && i < nr; i++) {
+				if(data->GetValue(ir, ic, &value) && CumData->GetValue(i, j, &old_val)){
+					switch (mode) {
+					case 1:	case 3:	value += old_val;			break;
+					case 2:	case 4: value = old_val -value;		break;
+						}
+					CumData->SetValue(i, j, value);
+					}
+				}
+			if(mode == 3 || mode == 4) for(i = 1; i <= nr; i++) {
+				//complete polygon data
+				if(CumData->GetValue(nr-i, j-1, &value)) CumData->SetValue(i-1+nr, j, value);
+				}
+			delete ay;		ay = 0L;
+			}
+		}
+
+
+
+	for(i = 0; i < nc; i++) if(yranges[i]) free(yranges[i]);
+	if(ax) delete ax;	if(ay) delete ay;
+	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):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;
+				}
+			}
+		}
+	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);
+	Undo.InvalidGO(this);
+}
+
+double
+PlotScatt::GetSize(int select)
+{
+	int i;
+	double ft1, ft2;
+
+	switch(select){
+	case SIZE_BARMINX:
+		if(BarDist.fx >= 0.0001) return BarDist.fx;
+		if((!Bars) | (nPoints < 2)) return 1.0f;
+		ft1 = fabs(Bars[1]->GetSize(SIZE_XPOS) - Bars[0]->GetSize(SIZE_XPOS));
+		for(i = 2; i < nPoints; i++) {
+			if(Bars[i] && Bars[i-1]) {
+				ft2 = fabs(Bars[i]->GetSize(SIZE_XPOS) - Bars[i-1]->GetSize(SIZE_XPOS));
+				if(ft2 < ft1 || ft1 < 0.0001f) ft1 = ft2;
+				}
+			}
+		return BarDist.fx = ft1 > 0.0001 ? ft1 : 1.0;
+	case SIZE_BARMINY:
+		if(BarDist.fy >= 0.0001) return BarDist.fy;
+		if((!Bars) | (nPoints < 2)) return 1.0f;
+		ft1 = fabs(Bars[1]->GetSize(SIZE_YPOS) - Bars[0]->GetSize(SIZE_YPOS));
+		for(i = 2; i < nPoints; i++) {
+			if(Bars[i] && Bars[i-1]) {
+				ft2 = fabs(Bars[i]->GetSize(SIZE_YPOS) - Bars[i-1]->GetSize(SIZE_YPOS));
+				if(ft2 < ft1 || ft1 < 0.0001f) ft1 = ft2;
+				}
+			}
+		return BarDist.fy = ft1 > 0.0001 ? ft1 : 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_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_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_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]);
+				}
+			else {
+				for (i = 0; i < nPoints && i < 100; i++)
+					if(Symbols[i]) ((Legend*)tmpl)->HasSym(0L, Symbols[i]);
+				}
+			if(TheLine && TheLine->Id == GO_DATAPOLYGON) TheLine->Command(cmd, tmpl, o);
+			}
+		else if(TheLine) TheLine->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_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){
+			Undo.ObjConf(this, UNDO_CONTINUE);
+			dirty = true;
+			}
+	case CMD_SET_DATAOBJ:
+		if(cmd == CMD_SET_DATAOBJ) {
+			Id = GO_PLOTSCATT;
+			data = (DataObj *)tmpl;	
+			}
+		ForEach(cmd, tmpl, o);
+		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_HIDE_MARK:
+		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_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:
+		if(Errors) for(i = 0; i < nPoints; i++) 
+			if(Errors[i]) Errors[i]->type = *((int*)tmpl);
+		return true;
+	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);
+		}
+	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, bFound;
+
+	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:
+		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;
+	case CMD_HIDE_MARK:
+		if(!o || !tmp) return false;
+		if(bFound =(tmp == (void*)TheLine)) TheLine->DoMark(o, false);
+		else for(j = 5; j >= 0 && !bFound; j--){
+			if(obs[j]) for(i = 0; i < nPoints && !bFound; i++){
+				if(bFound = (tmp == (void*)obs[j][i])) obs[j][i]->DoMark(o, false);
+				else if(obs[j][i] && obs[j][i]->Id == GO_MLABEL &&
+					obs[j][i]->Command(cmd, tmp, o)) return true;
+				}
+			}
+		return bFound;
+	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;
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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(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;
+		}
+}
+
+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_HIDE_MARK:
+		if(plots) for(i = 0; i < nPlots; i++) if(plots[i]){
+			if(tmpl == (void*)plots[i]) {
+				plots[i]->DoMark(o, false);
+				return true;
+				}
+			else if(plots[i]->Command(cmd, tmpl, o)) return true;
+			}
+		return false;
+	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 = 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;
+		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_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;
+	double min = HUGE_VAL, max = -HUGE_VAL, sum, mean, sd, tmp, d, *s_data;
+	Bar **bars = 0L;
+	char *fo;
+
+	if(!parent || !data || !ssRef || !plots) return;
+	if(curr_data) delete(curr_data);
+	if((curr_data = new DataObj()) && (ar = new AccRange(ssRef))) {
+		nv = ar->CountItems();			ar->GetFirst(&c, &r);
+		if(!(s_data = (double*)malloc(nv * sizeof(double)))) {
+			delete(ar);					return;
+			}
+		for(sum = 0.0, nv = 0; ar->GetNext(&c, &r); ) if(data->GetValue(r, c, &tmp)) {
+			sum+=tmp;	s_data[nv++] = tmp;
+			}
+		mean = sum / ((double)nv);		delete(ar);
+		for(sum = 0.0, j = 0; j < nv; j++){
+			if(s_data[j] > max) max = s_data[j];	if(s_data[j] < min) min = s_data[j];
+			sum += ((d = (s_data[j] - mean)) * d);
+			}
+		sd = sqrt(sum/((double)nv));
+		step = fabs(step);
+		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));
+		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);
+		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) {
+				bars[i] = new Bar(this, 0L, tmp, (double)f_data[i], BAR_VERTB | BAR_RELWIDTH, 0, i, 1, i);
+				}
+			}
+		free(s_data);		free(f_data);
+		if(bars && (plots[0] = new PlotScatt(this, data, ncl, bars))){
+			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]){
+			plots[0]->Command(CMD_SET_DATAOBJ, curr_data, 0L);
+			plots[0]->Command(CMD_AUTOSCALE, 0L, 0L);
+			}
+		if(type && (fo = (char*)malloc(1000))) {
+			cb = sprintf(fo, "[1=Function]\n");
+			cb += sprintf(fo+cb,"x1= %g\n", min);
+			cb += sprintf(fo+cb,"x2= %g\n", max);
+			cb += sprintf(fo+cb,"xstep= %g\n", (max-min)/100.0);
+			cb += sprintf(fo+cb,"Line= 0.4 6 0x000000ff 0x0\n");
+
+			cb += sprintf(fo+cb,"f_xy=\"A=%g; SD=%g; M=%g\\n", nv*step, sd, mean);
+			cb += sprintf(fo+cb,"ex=(x-M)/SD; ex=-0.5*ex*ex\\n");
+			cb += sprintf(fo+cb,"y=(A/(SD*2.5066283))*exp(ex)\\n\"\n");
+
+			OpenGraph(this, 0L, (unsigned char *)fo);
+			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);
+	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]);
+				}
+			else ((Legend*)tmpl)->HasFill(ld, 0L);
+			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_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_HIDE_MARK:
+		return false;
+	case CMD_SAVE_SYMBOLS:
+		return SavVarObs((GraphObj **)Symbols, nPoints, 0L);
+	case CMD_UPDATE:
+		if(Symbols) {
+			Undo.ObjConf(this, 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);
+		}
+	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_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) Undo.ObjConf(this, UNDO_CONTINUE);
+	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;
+		}
+	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 defs.GetSize(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_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_HIDE_MARK:
+		if(!tmpl || !o) return false;
+		//do all axes
+		if(Axes)for(i = nAxes-1; i>=0; i--) {
+			if(tmpl == (void*)Axes[i]){
+				Axes[i]->DoMark(CurrDisp, false);		return true;
+				}
+			else if(Axes[i]->Id == GO_AXIS) {
+				if(Axes[i]->Command(cmd, tmpl, o))		return true;
+				}
+			}
+		//do all plots
+		if(Plots)for(i = nPlots-1; i>=0; i--) {
+			if(tmpl == (void*)Plots[i]){
+				Plots[i]->DoMark(CurrDisp, false);		return true;
+				}
+			else if(Plots[i] && (Plots[i]->Id == GO_MLABEL || Plots[i]->Id == GO_LEGEND || 
+				(Plots[i]->Id >= GO_PLOT && Plots[i]->Id < GO_GRAPH))) {
+				if(Plots[i]->Command(cmd, tmpl, o))		return true;
+				}
+			}
+		return false;
+	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;
+	if (!d && parent) parent->Command(CMD_DELOBJ, this, NULL);
+}
+
+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(TheLine) TheLine->parent = this;
+		}
+}
+
+BoxPlot::BoxPlot(GraphObj *par, DataObj *dt, int mode, int c1, int c2, int c3):Plot(par, dt)
+{
+	int i, nr;
+	lfPOINT fp;
+
+	FileIO(INIT_VARS);		Id = GO_BOXPLOT;	fp.fx = fp.fy = 0.0;
+	if(data && data->GetSize(&i, &nr)) {
+		nPoints = nr;
+		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, 0, c1, i, c2, i, c1, i, c3, i);
+			else Boxes[i] = new Box(this, data, fp, fp, 0, c2, i, c1, i, c3, i, c1, i);
+			if(Boxes[i]) Boxes[i]->Command(CMD_AUTOSCALE, 0L, 0L);
+			}
+		}
+	BoxDist.fx = BoxDist.fy = GetSize(mode == 2  ? SIZE_BOXMINY : SIZE_BOXMINX);
+}
+
+BoxPlot::~BoxPlot()
+{
+	int i;
+
+	if(Whiskers) {
+		for(i = 0; i < nPoints; i++) if(Whiskers[i]) delete(Whiskers[i]);
+		free (Whiskers);
+		}
+	if(Boxes) {
+		for(i = 0; i < nPoints; i++) if(Boxes[i]) delete(Boxes[i]);
+		free (Boxes);
+		}
+	if(Symbols) {
+		for(i = 0; i < nPoints; i++) if(Symbols[i]) delete(Symbols[i]);
+		free (Symbols);
+		}
+	if(TheLine) delete(TheLine);
+	Undo.InvalidGO(this);
+}
+
+double
+BoxPlot::GetSize(int select)
+{
+	int i;
+	double ft1, ft2;
+
+	switch(select){
+	case SIZE_BOXMINX:
+		if(BoxDist.fx >= 0.0001) return BoxDist.fx;
+		if((!Boxes) | (nPoints < 2)) return 1.0;
+		ft1 = ft2 = 1.0;
+		if(Boxes[0] && Boxes[1])  ft1 = fabs(Boxes[1]->GetSize(SIZE_XPOS) - 
+			Boxes[0]->GetSize(SIZE_XPOS));
+		else return 1.0;
+		for(i = 2; i < nPoints; i++) {
+			if(Boxes[i] && Boxes[i-1]) ft2 = fabs(Boxes[i]->
+				GetSize(SIZE_XPOS) - Boxes[i-1]->GetSize(SIZE_XPOS));
+			if(ft2 < ft1 || ft1 < 0.0001) ft1 = ft2;
+			}
+		return BoxDist.fx = ft1 > 0.0001 ? ft1 : 1.0;
+	case SIZE_BOXMINY:
+		if(BoxDist.fy >= 0.0001) return BoxDist.fy;
+		if((!Boxes) | (nPoints < 2)) return 1.0f;
+		ft1 = ft2 = 1.0f;
+		if(Boxes[0] && Boxes[1]) ft1 = fabs(Boxes[1]->GetSize(SIZE_YPOS) -
+			Boxes[0]->GetSize(SIZE_YPOS));
+		else return 1.0f;
+		for(i = 2; i < nPoints; i++) {
+			if(Boxes[i] && Boxes[i-1]) ft2 = fabs(Boxes[i]->
+				GetSize(SIZE_YPOS) - Boxes[i-1]->GetSize(SIZE_YPOS));
+			if(ft2 < ft1 || ft1 < 0.0001f) ft1 = ft2;
+			}
+		return BoxDist.fy = ft1 > 0.0001 ? ft1 : 1.0f;
+	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;
+	}
+	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)
+{
+	int i;
+
+	if(!parent) return;
+	parent->Command(CMD_REG_AXISPLOT, (void*)this, o);
+	if(use_xaxis || use_yaxis) ApplyAxes(o);
+	if(TheLine) TheLine->DoPlot(o);
+	if(Whiskers) 
+		for(i = 0; i < nPoints; i++) if(Whiskers[i]) Whiskers[i]->DoPlot(o);
+	if(Boxes) 
+		for(i = 0; i < nPoints; i++) if(Boxes[i]) Boxes[i]->DoPlot(o);
+	if(Symbols)
+		for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->DoPlot(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, j;
+	bool bRedraw;
+	MouseEvent *mev;
+	GraphObj **obs[] = {(GraphObj**)Boxes, (GraphObj**)Whiskers, (GraphObj**)Symbols};
+	GraphObj ***go;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		if(hidden) return false;
+		mev = (MouseEvent *) tmpl;
+		switch(mev->Action) {
+		case MOUSE_LBUP:
+			for(j = 2; j >= 0 && !CurrGO; j--) {	//invers to plot order
+				if(obs[j]) for (i = nPoints-1; i >= 0; i--)
+					if(obs[j][i]) if(obs[j][i]->Command(cmd, tmpl, o))break;
+				}
+			if(TheLine && !CurrGO) TheLine->Command(cmd, tmpl, o);
+			break;
+			}
+		break;
+	case CMD_LEGEND:
+		if(Boxes) for (i = 0; i < nPoints; i++)
+			if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
+		break;
+	case CMD_SET_DATAOBJ:
+		Id = GO_BOXPLOT;		data = (DataObj *)tmpl;		dirty = true;
+	case CMD_AUTOSCALE:
+		if(cmd == 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) Undo.ObjConf(this, UNDO_CONTINUE);
+		for(j = 0; j < 3; j++) {
+			if(obs[j]) for (i = 0; i < nPoints; i++)
+				if(obs[j][i]) obs[j][i]->Command(cmd, tmpl, o);
+			}
+		if(TheLine) TheLine->Command(cmd, tmpl, o);
+		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_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_DELOBJ:
+		if(!parent || !o) return false;
+		for(j = 0, bRedraw = false, go = 0L; j < 3 && !bRedraw; j++) {
+			if(obs[j]) for(i = 0; i < nPoints; i++)
+				if(obs[j][i] && tmpl == (void*)obs[j][i]) {
+					o->HideMark();
+					Undo.DeleteGO(&obs[j][i], 0L, o);
+					switch(j) {
+					case 0: go = (GraphObj***)&Boxes;		break;
+					case 1: go = (GraphObj***)&Whiskers;	break;
+					case 2: go = (GraphObj***)&Symbols;		break;
+						}
+					bRedraw = true;
+					break;
+					}
+			}
+		if(!bRedraw && TheLine && tmpl == (void *) TheLine) {
+			o->HideMark();
+			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(!Boxes && !Whiskers && !Symbols && !TheLine) 
+			parent->Command(CMD_DELOBJ_CONT, this, o);
+		else if(bRedraw) parent->Command(CMD_REDRAW, NULL, o);
+		return bRedraw;
+	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_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:
+		if(Whiskers) for (i = 0; i < nPoints; i++)
+			if(Whiskers[i]) Whiskers[i]->Command(cmd, tmpl, o);
+		return true;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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;
+	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_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;
+		for(i = 0; i < nPoints; i++) {
+			if(Boxes[i] && tmpl == (void*)Boxes[i]){
+				o->HideMark();
+				Undo.DeleteGO((GraphObj**)(&Boxes[i]), 0L, o);
+				return parent->Command(CMD_REDRAW, NULL, 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:
+		Undo.ObjConf(this, 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;
+
+	if(xRange && yRange && (rX = new AccRange(xRange)) && (rY = new AccRange(yRange))) {
+		if((n=rX->CountItems()) == rY->CountItems()) {
+			if(Boxes) {
+				for(i = 0; i < nPoints; i++) if(Boxes[i]) DeleteGO(Boxes[i]);
+				free (Boxes);
+				}
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			Boxes = (Box**)calloc(nPoints = n, sizeof(Box*));
+			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)){
+					memcpy(&fp1, &fp2, sizeof(lfPOINT));
+					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(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));
+			SetSize(SIZE_BOX_LINE, DefLine.width);
+			SetColor(COL_BOX_LINE, DefLine.color);
+			Command(CMD_BOX_FILL, (void*)&DefFill, 0L);
+			}
+		else InfoBox("Error updating Plot!");
+		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;
+	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_HIDE_MARK:
+		if(!tmpl) return false;
+		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] == tmpl){
+				Polygons[i]->DoMark(o, false);
+				return true;
+				}
+		if(Lines && !CurrGO) for(i = 0; i < numPL; i++)
+			if(Lines[i] && Lines[i] == tmpl){
+				Lines[i]->DoMark(o, false);
+				return true;
+				}
+		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_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);
+		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_UPDATE) Undo.ObjConf(this, UNDO_CONTINUE);
+		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;
+			CumData = CreaCumData(ssXrange, ssYrange, cum_data_mode, StartVal);
+			if(cmd == CMD_SET_DATAOBJ) tmpl = CumData;
+			if(cmd == CMD_UPDATE && CumData) {
+				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(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(Polygons) for(i = 0; i < numPG; i++) if(Polygons[i] == tmpl) {
+			Undo.DeleteGO((GraphObj**)(&Polygons[i]), 0L, o);
+			return parent->Command(CMD_REDRAW, 0L, o);
+			}
+		if(Lines) for(i = 0; i < numPL; i++) if(Lines[i] == tmpl) {
+			Undo.DeleteGO((GraphObj**)(&Lines[i]), 0L, o);
+			return parent->Command(CMD_REDRAW, 0L, o);
+			}
+		if(xyPlots) for(i = 0; i < numXY; i++){
+			if(xyPlots[i] ==  tmpl) {
+				Undo.DeleteGO((GraphObj**)(&xyPlots[i]), 0L, o);
+				return parent->Command(CMD_REDRAW, 0L, o);
+				}
+			else if(xyPlots[i] && xyPlots[i]->Command(cmd, tmpl, o)) return true;
+			}
+		if(Boxes) for(i = 0; i < numPlots; i++){
+			if(Boxes[i] ==  tmpl) {
+				Undo.DeleteGO((GraphObj**)(&Boxes[i]), 0L, o);
+				return parent->Command(CMD_REDRAW, 0L, o);
+				}
+			else if(Boxes[i] && Boxes[i]->Command(cmd, tmpl, o)) return true;
+			}
+		}
+	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;
+	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_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))) {
+		Undo.ObjConf(this, 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);
+		}
+	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;
+		}
+}
+
+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) return;
+	o->GetSize(&rc);
+	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);
+		}
+	dirty = false;
+}
+
+bool
+Scatt3D::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	MouseEvent *mev;
+
+	switch (cmd) {
+	case CMD_HIDE_MARK:
+		if(!o || !tmpl) return false;
+		if(rib && rib->Command(cmd, tmpl, o)) return true;
+		if(Line && (void*) Line == tmpl){
+			Line->DoMark(o, false);					return true;
+			}
+		if(Columns) for(i = 0; i < nColumns; i++) {
+			if(Columns[i] && (void*)Columns[i] == tmpl) {
+				Columns[i]->DoMark(o, false);		return true;
+				}
+			}
+		if(DropLines) for(i = 0; i < nDropLines; i++) {
+			if(DropLines[i] && (void*)DropLines[i] == tmpl) {
+				DropLines[i]->DoMark(o, false);		return true;
+				}
+			}
+		return false;
+	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_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]);
+				}
+			else {
+				for (i = 0; i < nBalls && i < 100; i++)
+					if(Balls[i]) ((Legend*)tmpl)->HasSym(0L, Balls[i]);
+				}
+			}
+		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:
+		if(cmd == CMD_UPDATE) Undo.ObjConf(this, UNDO_CONTINUE);
+		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);
+		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) ssRefX = strdup(xr);		if(yr) ssRefY = strdup(yr);
+	z_value = z;	z_width = width;
+}
+
+Ribbon::Ribbon(GraphObj *par, DataObj *d, char *xr, char *yr, char *zr)
+	:Plot(par, d)
+{
+	FileIO(INIT_VARS);		Id = GO_RIBBON;		type = 2;
+	if(xr) ssRefX = strdup(xr);		if(yr) ssRefY = strdup(yr);
+	if(zr) ssRefZ = strdup(zr);
+}
+
+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;
+}
+
+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_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, rx, cx, ry, cy, rz, cz;
+	double fx, fy, fz, tmp;
+	fPOINT3D pg[5];
+	AccRange *rX, *rY, *rZ;
+
+	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))) {
+			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 = 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(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 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;
+				}
+			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;
+		}
+	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) 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;
+		}
+	if(rX) delete rX;	if(rY) delete rY;	if(rZ) delete rZ;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// draw a 3dimensional grid
+Grid3D::Grid3D(GraphObj *par, DataObj *d)
+	:Plot(par, d)
+{
+	FileIO(INIT_VARS);		Id = GO_GRID3D;
+}
+
+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;
+		}
+	nLines = 0;
+}
+
+void
+Grid3D::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(!lines) CreateObs();
+	if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[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;
+			break;
+			}
+		break;
+	case CMD_SET_LINE:
+		if(tmpl && lines) {
+			SavVarObs((GraphObj**)lines, nLines, 0L);
+			memcpy(&Line, tmpl, sizeof(LineDEF));
+			if(lines) for(i = 0; i < nLines; i++)
+				if(lines[i]) lines[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_GRID3D;
+		data = (DataObj *)tmpl;
+		if(lines) for(i = 0; i < nLines; i++)
+			if(lines[i]) lines[i]->Command(cmd, tmpl, o);
+		return true;
+	case CMD_UPDATE:
+		InfoBox("Don't know how to update Grid");
+		return true;
+	case CMD_AUTOSCALE:
+		if(!lines) 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(lines) for(i = 0; i < nLines; i++)
+				if(lines[i]) lines[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(lines) for(i = 0; i < nLines; i++) if(lines[i] == tmpl) {
+			Undo.DeleteGO((GraphObj**)(&lines[i]), 0L, o);
+			return parent->Command(CMD_REDRAW, 0L, o);
+			}
+		}
+	return false;
+}
+
+void
+Grid3D::CreateObs()
+{
+	int i, ir, ic, idx, w, h;
+	fPOINT3D *vec;
+
+	if(!parent || !data || lines) return;
+	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].fx = start.fx;			data->GetValue(0, 0, &vec[0].fy);
+	for(ic = 1, idx = 0; ic <= w; ic++) {
+		vec[0].fz = start.fz;
+		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].fx = vec[0].fx + step.fx;	vec[1].fz = vec[0].fz;
+				lines[idx++] = new Line3D(this, 0L, vec, 2);
+				}
+			if(ir < h && data->GetValue(ir, ic-1, &vec[1].fy)) {
+				vec[1].fx = vec[0].fx;	vec[1].fz = vec[0].fz + step.fz;
+				lines[idx++] = new Line3D(this, 0L, vec, 2);
+				}
+			vec[0].fz += step.fz;	vec[0].fy = vec[1].fy;
+			}
+		vec[0].fx += step.fx;
+		}
+	for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->Command(CMD_SET_LINE, &Line, 0L);
+	free(vec);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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()
+{
+}
+
+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):Plot(par, d)
+{
+	FileIO(INIT_VARS);
+	if(parent && parent->Id == GO_POLARPLOT) {
+		x1 = 0.0;			x2 = 360.0;			xstep = 0.5;
+		cmdxy = strdup("sin(pi*x/30)+1.1");
+		}
+	else {
+		x1 = 0.0;			x2 = 100.0;			xstep = 0.5;
+		cmdxy = strdup("sin(x)/x");
+		}
+	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;
+}
+
+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 && 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_HIDE_MARK:
+		if(dl && tmpl == dl) {
+			dl->DoMark(o, false);
+			return true;
+			}
+		return false;
+	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 = strdup((char*)tmpl);
+			}
+		dirty = true;
+		return true;
+	case CMD_SETFUNC:
+		if(tmpl) {
+			if(cmdxy) free(cmdxy);
+			cmdxy = strdup((char*)tmpl);
+			}
+		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;
+	if(parent->Id != GO_FITFUNC && dl) Undo.DeleteGO((GraphObj**)&dl, flags, o);
+	do_xyfunc(data, x1, x2, xstep, cmdxy, &xydata, &ndata, param);
+	if(xydata && ndata >1) {
+		if(!dl) dl = new DataLine(this, data, xydata, ndata);
+		else dl->LineData(xydata, ndata);
+		dirty = true;
+		Command(CMD_AUTOSCALE, 0L, 0L);
+		return true;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Calculate and display a user defined function
+FitFunc::FitFunc(GraphObj *par, DataObj *d):Plot(par, d)
+{
+	int width, height;
+
+	FileIO(INIT_VARS);
+	x1 = 0.0;			x2 = 100.0;			xstep = 0.5;	dl = 0L;
+	cmdxy = strdup("a+b*x^c");
+	parxy = strdup("a=1; b=1; c=0.1;");
+	data->GetSize(&width, &height);
+	if(!ssXref) {
+		sprintf(TmpTxt, "a1:a%d", height);
+		ssXref = strdup(TmpTxt);
+		}
+	if(!ssYref) {
+		sprintf(TmpTxt, "b1:b%d", height);
+		ssYref = strdup(TmpTxt);
+		}
+	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;
+	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(xstep <= 0.0) xstep = (x2-x1)/100.0;
+	if(!dl) dl = new Function(this, data);
+	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->Update(o, 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]);
+				}
+			else ((Legend*)tmpl)->HasFill(ld, 0L);
+			return true;
+			}
+		return false;
+	case CMD_HIDE_MARK:
+		if(dl) return dl->Command(cmd, tmpl, o);
+		return false;
+	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) if(!(dl = new Function(this, data))) return false;
+			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->Update(o, 0L);
+			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:
+		Undo.ObjConf(this, UNDO_CONTINUE);
+		if(Symbols) for(i = 0; i < nPoints; i++)
+			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
+		do_fitfunc(data, ssXref, ssYref, 0L, &parxy, cmdxy, conv, maxiter, &chi2);
+		dirty = true;
+		if(parent) parent->Command(CMD_MRK_DIRTY, 0L, o);
+		return true;
+	case CMD_DELOBJ:
+		if(parent && tmpl && tmpl == dl) return parent->Command(CMD_DELOBJ, this, o);
+		else if(dl) return dl->Command(cmd, tmpl, o);
+		break;
+	case CMD_MRK_DIRTY:
+		dirty = true;
+	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;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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(drag) DeleteGO(drag);	drag = 0L;
+	if(dispObs) free(dispObs);	dispObs = 0L;
+	free(RotDef);
+	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:
+		if(parent) return parent->GetSize(select);
+		else return defs.GetSize(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;
+	o->MouseCursor(MC_WAIT, true);
+	if(dirty) DoAutoscale();
+	o->LightSource(8.0, -16.0);
+	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;
+	if(Axes && nAxes >2) {
+		o->SetSpace(&cu1, &cu2, defs.cUnits, RotDef, &rc, Axes[0]->GetAxis(),
+			Axes[1]->GetAxis(), Axes[2]->GetAxis());
+		for(i = 0; i< nAxes; i++) 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);
+			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;
+	SortObj();
+	if(nObs && dispObs){
+		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);
+		}
+	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_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_HIDE_MARK:
+		if(!tmpl) return false;
+		//do all axes
+		if(Axes)for(i = nAxes-1; i>=0; i--) {
+			if(tmpl == (void*)Axes[i]){
+				Axes[i]->DoMark(o, false);
+				return true;
+				}
+			else if(Axes[i]->Id == GO_AXIS) {
+				if(Axes[i]->Command(cmd, tmpl, o)) return true;
+				}
+			}
+		//do all plots
+		if(plots)for(i = nPlots-1; i>=0; i--) {
+			if(tmpl == (void*)plots[i]){
+				plots[i]->DoMark(o, false);
+				return true;
+				}
+			else if(plots[i]->Id == GO_MLABEL || (plots[i]->Id >= GO_PLOT && plots[i]->Id < GO_GRAPH)) {
+				if(plots[i]->Command(cmd, tmpl, o)) return true;
+				}
+			}
+		return false;
+	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);
+		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:
+		return dirty = true;
+	case CMD_ADDAXIS:
+		InfoBox("Add axis to 3D graph:\n\nThis feature is not yet implemented!");
+		return false;
+	case CMD_SET_GO3D:
+		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(o) o->HideMark();
+		if(!tmpl || !parent) return false;
+		if(plots) for(i = 0; i < nPlots; i++) if(plots[i] == tmpl) {
+			Undo.DeleteGO((GraphObj**)(&plots[i]), 0L, o);
+			return parent->Command(CMD_REDRAW, 0L, o);
+			}
+		return true;
+	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(!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 = defs.GetSize(SIZE_AXIS_TICKS);
+	int i;
+	if(Axes || !parent)return;
+	TextDEF tlbdef = {parent->GetColor(COL_AXIS), 0x00ffffffL, defs.GetSize(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+defs.GetSize(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+defs.GetSize(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+defs.GetSize(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]) 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);
+				}
+			}
+		}
+}
+
+//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) ++j;
+			if(rra->Zmin < dispObs[j]->Zmin) {
+				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;
+
+	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()
+{
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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()
+{
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// use Plot3D to create a 3 dimensional bubble plot
+BubblePlot3D::BubblePlot3D(GraphObj *par, DataObj *d)
+	:Plot3D(par, d, 0x0L)
+{
+}
+
+BubblePlot3D::~BubblePlot3D()
+{
+}
diff --git a/PropertyDlg.cpp b/PropertyDlg.cpp
new file mode 100755
index 0000000..c6f160f
--- /dev/null
+++ b/PropertyDlg.cpp
@@ -0,0 +1,7777 @@
+//PropertyDlg.cpp, Copyright (c) 2001, 2002, 2003, 2004, 2005 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 "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
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Symbol::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 45, 10, "Size & Color"};
+	TabSHEET tab2 = {110, 130, 10, "Edit"};
+	TabSHEET tab3 = {68, 110, 10, "Text Prop."};
+	TabSHEET tab4 = {45, 68, 10, "Text"};
+	Symbol *PrevSym = 0L;
+	char text1[40], text2[100];
+	int syms[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, SYM_TEXT};
+	DlgInfo SymDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to SYMBOL", 145, 10, 60, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 145, 25, 60, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 145, 40, 60, 12},
+		{4, 500, 5, CHECKED | ISPARENT, GROUP, NULL, 138, 40, 55, 12},
+		{5, 6, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 130, 70},
+		{6, 7, 300, ISPARENT, SHEET, &tab2, 5, 10, 130, 70},
+		{7, 8, 200, TOUCHEXIT | ISPARENT, SHEET, &tab3, 5, 10, 130, 70},
+		{8, 401, 250, TOUCHEXIT | ISPARENT, SHEET, &tab4, 5, 10, 130, 70},
+		{100, 101, 0, 0x0L, RTEXT, (void*)"size", 5, 25, 45, 8},
+		{101, 102, 0, TOUCHEXIT, INCDECVAL1, &size, 55, 25, 32, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 89, 25, 20, 8},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"line width", 5, 37, 45, 8},
+		{104, 105, 0, TOUCHEXIT, INCDECVAL1, &SymLine.width, 55, 37, 32, 10},
+		{105, 106, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 89, 37, 20, 8},
+		{106, 107, 0, 0x0L, RTEXT, (void*)"line color", 5, 49, 45, 8},
+		{107, 108, 0, TOUCHEXIT | OWNDIALOG, COLBUTTON, (void *)SymLine.color, 55, 49, 25, 10},
+		{108, 109, 0, 0x0L, RTEXT, (void*)"fill color" , 5, 61, 45, 8},
+		{109, 0, 0, TOUCHEXIT | OWNDIALOG, COLBUTTON, (void *)SymFill.color, 55, 61, 25, 10},
+		{200, 204, 201, CHECKED | ISPARENT, GROUPBOX, (void*)" font ", 12, 28, 50, 45},
+		{201, 202, 0, TOUCHEXIT, RADIO1, (void*)"Helvetica", 15, 35, 45, 8},
+		{202, 203, 0, TOUCHEXIT, RADIO1, (void*)"Times", 15, 45, 45, 8},
+		{203, 0, 0, TOUCHEXIT, RADIO1, (void*)"Courier", 15, 55, 45, 8},
+		{204, 0, 205, CHECKED | ISPARENT, GROUPBOX, (void*)" style ", 72, 28, 57, 45},
+		{205, 206, 0, TOUCHEXIT, CHECKBOX, (void*)"bold", 75, 35, 25, 8},
+		{206, 207, 0, TOUCHEXIT, CHECKBOX, (void*)"italic", 75, 45, 25, 8},
+		{207, 0, 0, TOUCHEXIT, CHECKBOX, (void*)"underlined", 75, 55, 25, 8},
+		{250, 251, 0, TOUCHEXIT | CHECKED, RADIO1, (void*)"fixed text:", 10, 30, 45, 8},
+		{251, 252, 0, 0x0L, EDTEXT, text1, 60, 30, 45, 10},
+		{252, 253, 0, TOUCHEXIT, RADIO1, (void*)"from spreadsheet range:", 10, 45, 60, 8},
+		{253, 0, 0, 0x0L, EDTEXT, text2, 20, 55, 100, 10},
+		{300, 301, 0, 0x0L, RTEXT, (void*)"x-value", 5, 30, 45, 8},
+		{301, 302, 0, 0x0L, EDVAL1, &fPos.fx, 55, 30, 45, 10},
+		{302, 303, 0, 0x0L, RTEXT, (void*)"y-value", 5, 50, 45, 8},
+		{303, 0, 0, 0x0L, EDVAL1, &fPos.fy, 55, 50, 45, 10},
+		{401, 402, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[0], 15, 85, 10, 10},
+		{402, 403, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[1], 25, 85, 10, 10}, 
+		{403, 404, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[2], 35, 85, 10, 10}, 
+		{404, 405, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[3], 45, 85, 10, 10}, 
+		{405, 406, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[4], 55, 85, 10, 10}, 
+		{406, 407, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[5], 65, 85, 10, 10}, 
+		{407, 408, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[6], 75, 85, 10, 10}, 
+		{408, 409, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[7], 85, 85, 10, 10}, 
+		{409, 410, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[8], 95, 85, 10, 10}, 
+		{410, 411, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[9], 105, 85, 10, 10}, 
+		{411, 412, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[10], 115, 85, 10, 10}, 
+		{412, 413, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[11], 15, 95, 10, 10}, 
+		{413, 414, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[12], 25, 95, 10, 10}, 
+		{414, 415, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[13], 35, 95, 10, 10}, 
+		{415, 416, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[14], 45, 95, 10, 10}, 
+		{416, 0, 0, TOUCHEXIT, SYMRADIO, (void*)&syms[15], 55, 95, 40, 10}, 
+		{500, 0, 0, LASTOBJ | TOUCHEXIT, SYMBUTT, (void*)&PrevSym, 155, 65, 40, 40}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, tmpType, width, height;
+	DWORD tmpCol, undo_flags = 0L;
+	double tmpVal, o_size, n_size, o_lwidth, n_lwidth;
+	TextDEF textdef;
+	lfPOINT o_pos, n_pos;
+
+	if(!parent) return false;
+	if(!Command(CMD_GETTEXT, (void*)text1, 0L)) sprintf(text1, "text");
+	if(data && data->GetSize(&width, &height)) sprintf(text2, "b1:b%d", height);
+	else sprintf(text2, "(not available)");
+	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;
+		}
+	if(PrevSym && (Dlg = new DlgRoot(SymDlg))) {
+		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);
+		if(type == SYM_TEXT) Dlg->SetCheck(416, 0L, true);
+		else Dlg->SetCheck(401+type, 0L, true);
+		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->name) sprintf(TmpTxt, "Symbol of %s", parent->name);
+	else strcpy(TmpTxt, "Symbol properties");
+	if(!(hDlg = CreateDlgWnd(TmpTxt, 50, 50, 430, 260, 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 2:
+		case 1:
+			if(PrevSym->type == SYM_TEXT && Dlg->GetCheck(250)){
+				Dlg->GetText(251, text1);
+				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(PrevSym->type == SYM_TEXT && Dlg->GetCheck(252)) {
+				if(Dlg->GetCheck(252) && Dlg->GetText(253, text2))	
+					PrevSym->Command(CMD_RANGETEXT, &text2, 0L);
+				}
+			Dlg->GetValue(101, &n_size);		Dlg->GetValue(104, &n_lwidth);
+			break;
+		case 7:											//the text sheets
+		case 8:
+			Dlg->SetCheck(416, 0L, true);
+			if(PrevSym->type != SYM_TEXT) {
+				PrevSym->type = SYM_TEXT;
+				Dlg->DoPlot(NULL);
+				}
+			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(NULL);
+				}
+			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))PrevSym->Command(CMD_SETTEXT, text1, 0L);
+			else if(Dlg->GetCheck(252) && Dlg->GetText(253, text2))	
+				PrevSym->Command(CMD_RANGETEXT, text2, 0L);
+			Dlg->DoPlot(NULL);
+			res = -1;
+			break;
+		case 401:	case 402:	case 403:	case 404:	//symbol selection
+		case 405:	case 406:	case 407:	case 408:
+		case 409:	case 410:	case 411:	case 412:
+		case 413:	case 414:	case 415:
+			tmpType = res - 401;
+		case 416:										//text symbol
+			if(res == 416) tmpType = SYM_TEXT;
+			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 500:										//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))PrevSym->Command(CMD_SETTEXT, text1, 0L);
+				else if(Dlg->GetCheck(252) && Dlg->GetText(253, text2))	
+					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);
+		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))	
+				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;
+	delete PrevSym;
+	return true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Bubble properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Bubble::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 50, 10, "Shape & Color"};
+	TabSHEET tab2 = {50, 90, 10, "Scaling"};
+	TabSHEET tab3 = {90, 120, 10, "Edit"};
+	int syms[] = {SYM_CIRCLE, SYM_RECT, SYM_TRIAU, SYM_TRIAD};
+	DlgInfo BubDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to BUBBLE", 130, 10, 60, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 130, 25, 60, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 40, 60, 12},
+		{4, 0, 5, CHECKED | ISPARENT, GROUP, NULL, 138, 40, 55, 12},
+		{5, 6, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 120, 100},
+		{6, 7, 200, ISPARENT, SHEET, &tab2, 5, 10, 120, 100},
+		{7, 0, 300, ISPARENT, SHEET, &tab3, 5, 10, 120, 100},
+		{100, 109, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 18, 57, 90, 50},
+		{109, 110, 0, 0x0L, SYMRADIO, (void *)&syms[0], 30, 30, 20, 20},
+		{110, 111, 0, 0x0L, SYMRADIO, (void *)&syms[1], 50, 30, 20, 20},
+		{111, 112, 0, 0x0L, SYMRADIO, (void *)&syms[2], 70, 30, 20, 20},
+		{112, 0, 0, 0x0L, SYMRADIO, (void *)&syms[3], 90, 30, 20, 20},
+		{200, 201, 0, 0x0L, LTEXT, (void*)"Sizes are given as", 10, 30, 110, 8},
+		{201, 202, 210, ISPARENT | CHECKED, GROUP, NULL, 0, 0, 0, 0},
+		{202, 203, 0, 0x0L, LTEXT, (void*)"Proportionality (relative to circle)", 10, 64, 110, 8},
+		{203, 0, 220, ISPARENT | CHECKED, GROUP, NULL, 0, 0, 0, 0},
+		{210, 211, 0, 0x0L, RADIO1, (void *) Units[defs.cUnits].display, 40, 38, 45, 8},
+		{211, 212, 0, 0x0L, RADIO1, (void*)"scaling with X axis", 40, 46, 45, 8},
+		{212, 0, 0, 0x0L, RADIO1, (void*)"scaling with Y axis", 40, 54, 45, 8},
+		{220, 221, 0, 0x0L, RADIO1, (void*)"diameter", 40, 72, 45, 8},
+		{221, 222, 0, 0x0L, RADIO1, (void*)"circumference", 40, 80, 45, 8},
+		{222, 0, 0, 0x0L, RADIO1, (void*)"area", 40, 88, 45, 8},
+		{300, 301, 0, 0x0L, RTEXT, (void*)"x-value", 10, 40, 45, 8},
+		{301, 302, 0, 0x0L, EDVAL1, &fPos.fx, 60, 40, 35, 10},
+		{302, 303, 0, 0x0L, RTEXT, (void*)"y-value", 10, 60, 45, 8},
+		{303, 304, 0, 0x0L, EDVAL1, &fPos.fy, 60, 60, 35, 10},
+		{304, 305, 0, 0x0L, RTEXT, (void*)"size", 10, 80, 45, 8},
+		{305, 0, 0, LASTOBJ, EDVAL1, &fs, 60, 80, 35, 10}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, tmpType;
+	lfPOINT o_pos, n_pos;
+	LineDEF newLine, newFillLine;
+	FillDEF newFill;
+	DWORD undo_flags = 0L;
+	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);
+	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) sprintf(TmpTxt, "Bubble of %s", parent->name);
+	else strcpy(TmpTxt, "Bubble properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 390, 260, Dlg, 0x0L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 1:			//accept for current bubble only
+		case 2:			//accept for plot
+			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;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Bar properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+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];
+	DlgInfo BarDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to BAR", 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, NULL, 138, 40, 55, 12},
+		{5, 6, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 120, 120},
+		{6, 7, 200, ISPARENT, SHEET, &tab2, 5, 10, 120, 120},
+		{7, 0, 300, ISPARENT, SHEET, &tab3, 5, 10, 120, 120},
+		{100, 109, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 18, 30, 90, 50},
+		{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},
+		{200, 201, 0, TOUCHEXIT, RADIO2, (void*)"vertical bars", 20, 30, 45, 8},
+		{201, 205, 202, CHECKED | ISPARENT, GROUP, NULL, 0, 0, 0, 0},
+		{202, 203, 0, TOUCHEXIT, RADIO1, (void*)"bottom baseline", 30, 40, 35, 8},
+		{203, 204, 0, TOUCHEXIT, RADIO1, (void*)"top", 30, 48, 35, 8},
+		{204, 0, 0, TOUCHEXIT, RADIO1, (void*)"user y =", 30, 56, 35, 8},
+		{205, 206, 0, 0x0L, EDVAL1, &BarBase.fy, 65, 56, 35, 10},
+		{206, 207, 0, TOUCHEXIT, RADIO2, (void*)"horizontal bars", 20, 70, 45, 8},
+		{207, 211, 208, CHECKED | ISPARENT, GROUP, NULL, 0, 0, 0, 0},
+		{208, 209, 0, TOUCHEXIT, RADIO1, (void*)"left baseline", 30, 80, 35, 8},
+		{209, 210, 0, TOUCHEXIT, RADIO1, (void*)"right", 30, 88, 35, 8},
+		{210, 0, 0, TOUCHEXIT, RADIO1, (void*)"user x =", 30, 96, 35, 8},
+		{211, 212, 0, 0x0L, EDVAL1, &BarBase.fx, 65, 96, 35, 10},
+		{212, 0, 0, 0x0L, CHECKBOX, (void*)"bars centered across baseline", 20, 113, 50, 8},
+		{300, 301, 0, 0x0L, RTEXT, (void*)"x-value", 10, 50, 45, 8},
+		{301, 302, 0, 0x0L, EDVAL1, &fPos.fx, 60, 50, 40, 10},
+		{302, 303, 0, 0x0L, RTEXT, (void*)"y-value", 10, 75, 45, 8},
+		{303, 0, 0, LASTOBJ, EDVAL1, &fPos.fy, 60, 75, 40, 10}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	double n_size;
+	int res, tmpType = type;
+	bool bRet = false;
+	LineDEF newLine, newFillLine;
+	FillDEF newFill;
+	DWORD undo_flags = 0L;
+	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, defs.GetSize(SIZE_BAR));
+		}
+	else {
+		WriteNatFloatToBuff(sTxt1, size);
+		strcpy(sTxt2, " 50");
+		}
+	Dlg = new DlgRoot(BarDlg);
+	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 && parent->name) sprintf(TmpTxt, "Bar of %s", parent->name);
+	else strcpy(TmpTxt, "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, 296, 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:
+			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;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Data line properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+DataLine::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 40, 10, "Line"};
+	TabSHEET tab2 = {40, 80, 10, "Style"};
+	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, NULL, 138, 40, 55, 12},
+		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 139, 120},
+		{5, 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, LTEXT, (void*)"select style:", 10, 38, 130, 100},
+		{201, 202, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 12, 50, 25, 25},
+		{202, 203, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 37, 50, 25, 25},
+		{203, 204, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 62, 50, 25, 25},
+		{204, 205, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 87, 50, 25, 25},
+		{205, 206, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 112, 50, 25, 25},
+		{206, 207, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 37, 75, 25, 25},
+		{207, 208, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 62, 75, 25, 25},
+		{208, 209, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 87, 75, 25, 25},
+		{209, 0, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 112, 75, 25, 25},
+		{800, 0, 0, LASTOBJ, NONE, 0L, 0, 0, 0, 0}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, tmpType = type;
+	DWORD undo_flags = 0L;
+	LineDEF newLine;
+	bool bRet = false;
+
+	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)))return false;
+	Dlg->SetCheck(201 + (type & 0x7), 0L, true);
+	if(parent && parent->name) sprintf(TmpTxt, "Line of %s", parent->name);
+	else strcpy(TmpTxt, "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:
+			tmpType &= ~0x0f;
+			tmpType |= (res-201);
+			res = -1;
+			break;
+			}
+		}while (res < 0);
+	if(res == 1){						//OK pressed
+		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;
+	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;
+	int 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);
+	if(parent && parent->name) sprintf(TmpTxt, "Polygon of %s", parent->name);
+	else strcpy(TmpTxt, "Polygon properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 310, 204, Dlg, 0x0L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		}while (res < 0);
+	if(res == 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(&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[40], text2[40], text3[40], text4[40], text5[40];
+	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 res, tmpType;
+	bool bRet = false;
+	LineDEF newLine;
+	DWORD undo_flags = 0L;
+	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;
+		}
+	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);
+	if(!(Dlg = new DlgRoot(LineDlg))) 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 && parent->name) {
+		sprintf(TmpTxt, "Regression line of %s", parent->name);
+		}
+	else strcpy(TmpTxt, "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
+		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
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+SDellipse::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 40, 10, "Line"};
+	TabSHEET tab2 = {40, 80, 10, "Details"};
+	DlgInfo ellDlg[] = {
+		{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, NULL, 138, 40, 55, 12},
+		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 139, 120},
+		{5, 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, CHECKBOX, (void*)" show regression line", 20, 30, 80, 9},
+		{201, 0, 250, CHECKED, GROUPBOX, (void*)"  ellipse  ", 10, 55, 129, 70},
+		{250, 251, 0, 0x0L, LTEXT, (void*)"center (means of data)", 25, 60, 60, 8},
+		{251, 252, 0, 0x0L, RTEXT, (void*)"x =", 20, 70, 60, 8},
+		{252, 253, 0, 0x0L, LTEXT, 0L, 82, 70, 30, 8},
+		{253, 254, 0, 0x0L, RTEXT, (void*)"y =", 20, 78, 60, 8},
+		{254, 255, 0, 0x0L, LTEXT, 0L, 82, 78, 30, 8},
+		{255, 256, 0, 0x0L, LTEXT, (void*)"standard deviation (SD)", 25, 90, 60, 8},
+		{256, 257, 0, 0x0L, RTEXT, (void*)"major axis =", 20, 100, 60, 8},
+		{257, 258, 0, 0x0L, LTEXT, 0L, 82, 100, 30, 8},
+		{258, 259, 0, 0x0L, RTEXT, (void*)"minor axis =", 20, 108, 60, 8},
+		{259, 0, 0, LASTOBJ, LTEXT, 0L, 82, 108, 30, 8}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, tmpType;
+	LineDEF newLine;
+	bool bRet = false;
+	DWORD undo_flags = 0L;
+
+	if(!parent) return false;
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
+	if(!(Dlg = new DlgRoot(ellDlg))) 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);
+	strcpy(TmpTxt, "+/-");
+	WriteNatFloatToBuff(TmpTxt+3, sd1);		Dlg->SetText(259, TmpTxt);
+	WriteNatFloatToBuff(TmpTxt+3, sd2);		Dlg->SetText(257, TmpTxt);
+	tmpType = type;
+	if(parent && parent->name) sprintf(TmpTxt, "SD ellipse of %s", parent->name);
+	else strcpy(TmpTxt, "SD ellipse properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 410, 300, Dlg, 0x0L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		}while (res < 0);
+	if(res == 1){						//OK pressed
+		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;
+		undo_flags = CheckNewInt(&type, type, tmpType, parent, undo_flags);
+		if(undo_flags & UNDO_CONTINUE) bRet = true;
+		}
+	CloseDlgWnd(hDlg);		delete Dlg;
+	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, 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, COLBUTTON, (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, 40, 28, 8},
+		{301, 302, 0, 0x0L, EDVAL1, &fPos.fx, 46, 40, 35, 10},
+		{302, 303, 0, 0x0L, RTEXT, (void*)"y-value", 15, 55, 28, 8},
+		{303, 304, 0, 0x0L, EDVAL1, &fPos.fy, 46, 55, 35, 10},
+		{304, 305, 0, 0x0L, RTEXT, (void*)"error", 15, 70, 28, 8},
+		{305, 0, 0, 0x0L, EDVAL1, &ferr, 46, 70, 35, 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 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;
+	bool bRet = false;
+
+	if(!parent) return false;
+	if(!(Dlg = new DlgRoot(ErrDlg)))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 && parent->name) sprintf(TmpTxt, "Error bar of %s", parent->name);
+	else strcpy(TmpTxt, "Error 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 1:								//accept for this object
+		case 2:								//   or all objects of plot
+			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(&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);
+		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) bRet = true;
+		break;
+	case 2:				//new settings to all error bars of plot
+		parent->Command(CMD_SAVE_ERRS, 0L, 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, COLBUTTON, (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 res, tmptype = type, undo_level = *Undo.pcb;
+	bool bRet = false;
+	DWORD o_col, n_col, undo_flags = 0L;
+
+	if(!parent) return false;
+	if(!(Dlg = new DlgRoot(ArrowDlg))) 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 && parent->name) sprintf(TmpTxt, "Arrow of %s", parent->name);
+	else strcpy(TmpTxt, "Arrow properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 328, 260, Dlg, 0x0L);
+	do {
+		LoopDlgWnd();
+		res = ExecDrawOrderButt(parent, this, 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:
+			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
+		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 res, tmpType = type;
+	FillDEF newFill;
+	LineDEF newLine, newHatchLine;
+	DWORD undo_flags = 0L;
+	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, defs.GetSize(SIZE_BAR));
+		}
+	else {
+		WriteNatFloatToBuff(sTxt1, size);
+		strcpy(sTxt2, " 50");
+		}
+	Dlg = new DlgRoot(BoxDlg);
+	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 && parent->name) sprintf(TmpTxt, "Box of %s", parent->name);
+	else strcpy(TmpTxt, "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
+			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, &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
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Whisker::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 38, 10, "Whisker"};
+	TabSHEET tab2 = {65, 90, 10, "Edit"};
+	TabSHEET tab3 = {38, 65, 10, "Style"};
+	DlgInfo ErrDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to WHISKER", 100, 10, 64, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 100, 25, 64, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 100, 40, 64, 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, 500, ISPARENT, SHEET, &tab3, 5, 10, 90, 100},
+		{7, 0, 300, ISPARENT, SHEET, &tab2, 5, 10, 90, 100},
+		{100, 101, 0, 0x0L, RTEXT, (void*)"cap width", 15, 40, 28, 8},
+		{101, 102, 0, 0x0L, EDVAL1, &size, 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, &LineDef.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, COLBUTTON, (void *)LineDef.color, 46, 70, 25, 10},
+		{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, 0, 0, 0x0L, EDVAL1, &pos2.fy, 46, 82, 35, 10},
+		{500, 501, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_WhiskerTempl),32,40,18,18},
+		{501, 502, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_WhiskerTempl), 14, 40, 18, 18},
+		{502, 503, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_WhiskerTempl), 50, 40, 18, 18},
+		{503, 504, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_WhiskerTempl), 68, 40, 18, 18},
+		{504, 0, 0, LASTOBJ, LTEXT, (void*)"select style:", 14, 30, 30, 9}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, tmp_type;
+	DWORD undo_flags = 0L, n_col = LineDef.color;
+	lfPOINT n_pos;
+	double tmpVal, o_size, n_size, o_lw, n_lw;
+	bool bRet = false;
+
+	if(!parent) return false;
+	tmp_type = type;
+	Dlg = new DlgRoot(ErrDlg);
+	Dlg->SetCheck(500 + (tmp_type & 0x03), 0L, true);
+	Dlg->GetValue(101, &o_size);		Dlg->GetValue(104, &o_lw);
+	if(parent && parent->name) sprintf(TmpTxt, "Whisker of %s", parent->name);
+	else strcpy(TmpTxt, "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
+			Dlg->GetValue(101, &n_size);		Dlg->GetValue(104, &n_lw);
+			Dlg->GetColor(107, &n_col);
+			break;
+			}
+		}while (res <0);
+	switch (res) {
+	case 1:				//new setting for current whisker
+		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) break;
+		parent->Command(CMD_SAVE_ERRS, 0L, 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;
+	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 res, tmptype;
+	bool bRet = false;
+	LineDEF newLine;
+	DWORD undo_flags = 0L;
+	lfPOINT o_pos, n_pos;
+
+	if(!parent) return false;
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
+	Dlg = new DlgRoot(LineDlg);
+	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) sprintf(TmpTxt, "Dropline of %s", parent->name);
+	else strcpy(TmpTxt, "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
+			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 res, tmptype, i;
+	bool bRet = false;
+	LineDEF newLine;
+	DWORD undo_flags = 0L;
+	fPOINT3D o_pos, n_pos;
+
+	if(!parent) return false;
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+	Dlg = new DlgRoot(LineDlg);
+	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) sprintf(TmpTxt, "Dropline of %s", parent->name);
+	else strcpy(TmpTxt, "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
+			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], 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, COLBUTTON, (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 res;
+	bool bRet = false, bContinue = false;
+	fPOINT3D n_pos, o_pos;
+	DWORD new_lcolor = Line.color, undo_flags = 0L;
+	double o_size, n_size, o_lsize, n_lsize;
+
+	if(!parent) return false;
+	memcpy(&newFill, &Fill, sizeof(FillDEF));
+	Dlg = new DlgRoot(BallDlg);
+	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) sprintf(TmpTxt, "Ball of %s", parent->name);
+	else strcpy(TmpTxt, "Ball properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 335, 216, Dlg, 0x0L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue) res = -1;
+			bContinue = false;
+			break;
+		case 1:		case 2:
+			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
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Plane3D::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 27, 10, "Plane"};
+	double rb_width = 0.0, rb_z = 0.0;
+	FillDEF newFill;
+	DlgInfo PlaneDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to PLANE", 115, 10, 55, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 115, 25, 55, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 115, 40, 55, 12},
+		{4, 0, 10, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
+		{10, 0, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 105, 85},
+		{100, 101, 0, 0x0L, RTEXT, (void*)"line width", 15, 30, 36, 8},
+		{101, 102, 0, 0x0L, EDVAL1, &Line.width, 53, 30, 25, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 80, 30, 15, 8},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"line color", 15, 42, 36, 8},
+		{104, 105, 0, OWNDIALOG, COLBUTTON, (void *)Line.color, 53, 42, 25, 10},
+		{105, 106, 0, 0x0L, RTEXT, (void*)"fill color", 15, 54, 36, 8},
+		{106, 200, 0, OWNDIALOG, SHADE3D, &newFill, 53, 54, 25, 10},
+		{200, 0, 201, CHECKED, GROUP, NULL, 0, 0, 0, 0},
+		{201, 202, 0, 0x0L, RTEXT, (void*)"ribbon width", 15, 66, 36, 8},
+		{202, 203, 0, 0x0L, INCDECVAL1, &rb_width, 53, 66, 25, 10},
+		{203, 204, 0, 0x0L, LTEXT, (void*)"%", 80, 66, 5, 8},
+		{204, 205, 0, 0x0L, RTEXT, (void*)"ribbon pos.", 15, 78, 36, 8},
+		{205, 206, 0, 0x0L, EDVAL1, &rb_z, 53, 78, 25, 10},
+		{206, 0, 0, 0x0L, LTEXT, (void*)"[z-data]", 80, 78, 40, 8},
+		{800, 0, 0, LASTOBJ, 0L, 0, 0, 0, 0}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res;
+	bool bRet = false;
+	DWORD new_lcolor = Line.color, undo_flags = 0L;
+	double o_lsize, n_lsize, o_rbw, n_rbw, o_rbz, n_rbz;
+
+	if(!parent) return false;
+	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);
+	Dlg->GetValue(101, &o_lsize);		Dlg->GetValue(202, &o_rbw);
+	Dlg->GetValue(205, &o_rbz);
+	if(parent && parent->Id==GO_RIBBON && parent->type == 2)
+		Dlg->ShowItem(200, false);								//paravent plot
+	if(parent->name) sprintf(TmpTxt, "Plane of %s", parent->name);
+	else strcpy(TmpTxt, "Plane properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 355, 226, Dlg, 0x0L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 1:		case 2:
+			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, COLBUTTON, (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 res;
+	bool bRet = false;
+	DWORD col1 = Line.color, undo_flags = 0L;
+	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);
+	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) sprintf(TmpTxt, "Column of %s", parent->name);
+	else strcpy(TmpTxt, "Column properties");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 405, 296, Dlg, 0x0L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 1:
+		case 2:
+			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
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Arrow3D::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, (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, 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, ISPARENT, SHEET, &tab3, 5, 10, 90, 100},
+		{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, &Line.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, COLBUTTON, (void *)Line.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, 25, 28, 8},
+		{301, 302, 0, 0x0L, EDVAL1, &fPos2.fx, 46, 25, 35, 10},
+		{302, 303, 0, 0x0L, RTEXT, (void*)"y-value", 10, 36, 28, 8},
+		{303, 304, 0, 0x0L, EDVAL1, &fPos2.fy, 46, 36, 35, 10},
+		{304, 305, 0, 0x0L, RTEXT, (void*)"z-value", 10, 47, 28, 8},
+		{305, 306, 0, 0x0L, EDVAL1, &fPos2.fz, 46, 47, 35, 10},
+		{306, 307, 0, 0x0L, RTEXT, (void*)"origin x", 10, 60, 28, 8},
+		{307, 308, 0, 0x0L, EDVAL1, &fPos1.fx, 46, 60, 35, 10},
+		{308, 309, 0, 0x0L, RTEXT, (void*)" y", 10, 71, 28, 8},
+		{309, 310, 0, 0x0L, EDVAL1, &fPos1.fy, 46, 71, 35, 10},
+		{310, 311, 0, 0x0L, RTEXT, (void*)" z", 10, 82, 28, 8},
+		{311, 312, 0, 0x0L, EDVAL1, &fPos1.fz, 46, 82, 35, 10},
+		{312, 0, 0, LASTOBJ, CHECKBOX, (void*)"set common origin", 16, 95, 70, 8}};
+	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 res, tmptype = type, undo_level = *Undo.pcb;
+	bool bRet = false;
+	DWORD o_col, n_col, undo_flags = 0L;
+
+	if(!parent) return false;
+	if(!(Dlg = new DlgRoot(ArrowDlg))) 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) sprintf(TmpTxt, "Arrow of %s", parent->name);
+	else strcpy(TmpTxt, "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:
+			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;
+	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 res;
+	bool bRet = false;
+	LineDEF newLine;
+
+	if(!parent) return false;
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+	if(!(Dlg = new DlgRoot(LineDlg)))return false;
+	sprintf(TmpTxt, "Line of %s", parent->name);
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 410, 314, Dlg, 0x0L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+	}while (res < 0);
+	switch(res) {
+	case 1:							//OK pressed
+		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"};
+	DlgInfo LabelDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 170, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 170, 25, 45, 12},
+		{3, 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},
+		{50, 0, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
+		{100, 101, 0, 0x0L, RTEXT, (void*)"size", 10, 33, 45, 8},
+		{101, 102, 0, 0x0L, EDVAL1, &TextDef.fSize, 60, 33, 25, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 87, 33, 20, 8},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"color", 10, 45, 45, 8},
+		{104, 107, 0, OWNDIALOG, COLBUTTON, (void *)TextDef.ColTxt, 60, 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", 10, 57, 45, 8},
+		{108, 109, 0, 0x0L, EDVAL1, &TextDef.RotBL, 60, 57, 25, 10},
+		{109, 105, 0, 0x0L, LTEXT, (void *)"deg.", 87, 57, 20, 8},
+		{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, 165, 45, 15, 15},
+		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 165, 60, 15, 15},
+		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 165, 75, 15, 15},
+		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 165, 90, 15, 15}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, i, c_style, c_font;
+	bool RetVal = false, check;
+	DWORD undo_flags = 0x0;
+	lfPOINT o_pos, o_dist, n_pos, n_dist;
+	TextDEF OldTxtDef, NewTxtDef;
+	Axis *pa;
+	fmtText *fmt = 0L;
+
+	if(parent && (Dlg = new DlgRoot(LabelDlg))) {
+		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);
+		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) strcpy(TmpTxt, "(axis)");
+			else if(parent->parent->Id == GO_TICK) strcpy(TmpTxt, "(tick)");
+			}
+		else {
+			if(parent->Id == GO_AXIS) strcpy(TmpTxt, "(axis)");
+			else if(parent->Id == GO_TICK) strcpy(TmpTxt, "(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) strcpy(TmpTxt, "(axis)");
+			if(parent->parent->Id == GO_TICK) strcpy(TmpTxt, "(tick)");
+			}
+		else {
+			if(parent->Id == GO_AXIS) strcpy(TmpTxt, "(axis)");
+			if(parent->Id == GO_TICK) strcpy(TmpTxt, "(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, 450, 254, Dlg, 0x0L);
+	do{
+		LoopDlgWnd();
+		res = ExecDrawOrderButt(parent, this, Dlg->GetResult());
+		switch (res) {
+		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))) 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) {
+		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;
+			}
+		TmpTxt[0] = 0;		Dlg->GetText(106, TmpTxt);
+		if(TextDef.text && TextDef.text[0] && strcmp(TextDef.text, TmpTxt)) {
+			Undo.String(this, &TextDef.text, undo_flags);	undo_flags |= UNDO_CONTINUE;
+			if(TextDef.text) free(TextDef.text);			TextDef.text = strdup(TmpTxt);
+			}
+		if(cmpTextDEF(&OldTxtDef, &NewTxtDef)){
+			if(NewTxtDef.ColTxt != TextDef.ColTxt) bBGvalid = false;
+			if (pa) pa->Command(CMD_TLB_TXTDEF, &NewTxtDef, 0L);
+			else if(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(parent->Id == GO_MLABEL) {
+				if(parent->SetSize(SIZE_LB_XDIST, n_dist.fx) ||
+					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(undo_flags & UNDO_CONTINUE) RetVal = true;
+		}
+	CloseDlgWnd(hDlg);
+	if(fmt) delete(fmt);
+	delete Dlg;
+	return RetVal;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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;
+
+	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);
+	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:
+			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;
+	bool bRet = false;
+	LineDEF newLine;
+
+	if(!parent) return false;
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&pgLine, 0);
+	if(!(Dlg = new DlgRoot(LineDlg)))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 = ExecDrawOrderButt(parent, this, Dlg->GetResult());
+	}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;
+	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);
+	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 = ExecDrawOrderButt(parent, this, Dlg->GetResult());
+		}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;
+	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)))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 = ExecDrawOrderButt(parent, this, Dlg->GetResult());
+		}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, 0x0L, 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, EDTEXT, 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, width, height;
+	double x, y;
+	AccRange *rY = 0L;
+	LineDEF Line;
+	FillDEF Fill; 
+
+	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);
+	data->GetSize(&width, &height);
+	sprintf(TmpTxt, "a1:a%d", height);
+	if(!(Dlg = new DlgRoot(BarDlg)))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)) rY = new AccRange(TmpTxt);
+			yRange = strdup(TmpTxt);
+			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, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 131, 100},
+		{5, 6, 200, TOUCHEXIT | ISPARENT, SHEET, &tab2, 5, 10, 131, 100},
+		{6, 7, 300, ISPARENT, SHEET, &tab3, 5, 10, 131, 100},
+		{7, 10, 400, ISPARENT, SHEET, &tab4, 5, 10, 131, 100},
+		{10, 0, 0, 0x0L, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"range for X Data", 10, 30, 60, 8},
+		{101, 102, 0, 0x0L, EDTEXT, text1, 20, 40, 100, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 55, 60, 8},
+		{103, 104, 0, 0x0L, EDTEXT, 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, EDTEXT, 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, EDTEXT, 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, width, height, BarType;
+	double x, y, e;
+	lfPOINT fp1, fp2;
+	bool bRet = false, bLayout = false, bContinue = false;
+	TextDEF lbdef = {defs.Color(COL_TEXT), defs.Color(COL_BG), defs.GetSize(SIZE_TEXT), 0.0f, 0.0f, 0,
+		TXA_HLEFT | TXA_VBOTTOM, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, TmpTxt};
+	AccRange *rX, *rY, *rE, *rL;
+
+	if(Id == GO_BARCHART) return CreateBarChart();
+	data->GetSize(&width, &height);
+	sprintf(text1, "a1:a%d", height);		sprintf(text2, "b1:b%d", height);
+	sprintf(text3, "c1:c%d", height);		sprintf(text4, "b1:b%d", height);
+	rX = rY = rE = rL = 0L;
+	if(!(Dlg = new DlgRoot(XYDlg)))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 5:								// the layout tab sheet
+			bLayout = 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)) 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)) 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)) 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)) 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)) xRange = strdup(TmpTxt);
+		if(Dlg->GetText(103, TmpTxt)) yRange = strdup(TmpTxt);
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		//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)) ErrRange = strdup(TmpTxt);
+			rE->GetFirst(&m, &n);	rE->GetNext(&m, &n);
+			}
+		if(Dlg->GetCheck(400) && rL) {					//labels ?
+			Labels = (Label**)calloc(nPoints, sizeof(Label*));
+			if(Dlg->GetText(402, TmpTxt)) LbRange = strdup(TmpTxt);
+			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 {
+			if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y)){
+				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);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Regression properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+FreqDist::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 52, 10, "Style"};
+	DlgInfo FreqDlg[] = {
+		{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, 113},
+		{5, 10, 200, ISPARENT, SHEET, &tab2, 5, 10, 120, 113},
+		{10, 0, 0, 0x0L, 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, EDTEXT, TmpTxt, 10, 38, 110, 10},
+		{102, 103, 120, ISPARENT | CHECKED, GROUPBOX, (void*)" classes ", 10, 55, 110, 42},
+		{103, 0, 150, ISPARENT | CHECKED, GROUPBOX, (void*)" plot function ", 10, 102, 110, 17}, 
+		{120, 121, 0, CHECKED, RADIO1, (void*)"create", 15, 60, 30, 9},
+		{121, 122, 0, 0x0L, EDTEXT, (void*)"7", 47, 60, 15, 10},
+		{122, 123, 0, 0x0L, LTEXT, (void*)"classes and bars", 64, 60, 35, 8},
+		{123, 124, 0, 0x0L, RADIO1, (void*)"class size is", 15, 72, 45, 9},
+		{124, 125, 0, 0x0L, EDTEXT, 0L, 65, 72, 50, 10},
+		{125, 126, 0, 0x0L, RTEXT, (void*)"starting at", 15, 84, 47, 8},
+		{126, 0, 0, 0x0L, EDTEXT, 0L, 65, 84, 50, 10},
+		{150, 0, 0, ISRADIO, CHECKBOX, (void*)" normal dist.", 15, 107, 30, 8},
+		{200, 0, 210, ISPARENT | CHECKED, GROUPBOX, (void*)" bar style ", 10, 27, 110, 61}, 
+		{210, 0, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 25, 34, 90, 50},
+		{800, 0, 0, LASTOBJ, NONE, 0L, 0, 0, 0, 0}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	bool bRet = false;
+	int res, width, height;
+
+	if(!parent || !data) return false;
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&BarLine, 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)&BarFill, 0);
+	data->GetSize(&width, &height);
+	sprintf(TmpTxt, "a1:a%d", height);
+	if(!(Dlg = new DlgRoot(FreqDlg)))return false;
+	hDlg = CreateDlgWnd("Frequency Distribution", 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(Dlg->GetText(101, TmpTxt) && TmpTxt[0]) {
+				if(ssRef) free(ssRef);
+				ssRef = strdup(TmpTxt);
+				}
+			type = 0;
+			if(Dlg->GetCheck(150)) type = 1;
+			break;
+			}
+		}while (res <0);
+	if(res==1 && (plots = (GraphObj**)calloc(nPlots=2, 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(plots[0]) bRet = true;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Regression properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Regression::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 40, 10, "Data"};
+	TabSHEET tab2 = {40, 90, 10, "Transform"};
+	char text1[100], text2[100];
+	DlgInfo RegDlg[] = {
+		{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, 0, 0, 0, 0},
+		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 130, 85},
+		{5, 10, 200, ISPARENT, SHEET, &tab2, 5, 10, 130, 85},
+		{10, 0, 0, 0x0L, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"range for X Data", 10, 20, 60, 8},
+		{101, 102, 0, 0x0L, EDTEXT, text1, 20, 30, 100, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 45, 60, 8},
+		{103, 104, 0, 0x0L, EDTEXT, text2, 20, 55, 100, 10},
+		{104, 105, 0, CHECKED, CHECKBOX, (void*)" include symbols in plot", 10, 70, 100, 8},
+		{105, 0, 0, 0x0L, CHECKBOX, (void*) " draw SD ellipse", 10, 80, 100, 8}, 
+		{200, 210, 201, CHECKED | ISPARENT, GROUPBOX, (void*)"  x-values  ", 10, 30, 58, 50},
+		{201, 202, 0, CHECKED, RADIO1, (void*)"x = x", 20, 40, 30, 8},
+		{202, 203, 0, 0x0L, RADIO1, (void*)"x = log(x)", 20, 48, 30, 8},
+		{203, 204, 0, 0x0L, RADIO1, (void*)"x = 1/x", 20, 56, 30, 8},
+		{204, 0, 0, 0x0L, RADIO1, (void*)"x = sqrt(x)", 20, 64, 30, 8},
+		{210, 0, 211, CHECKED | ISPARENT, GROUPBOX, (void*)"  y-values  ", 72, 30, 58, 50},
+		{211, 212, 0, CHECKED, RADIO1, (void*)"y = y", 82, 40, 30, 8},
+		{212, 213, 0, 0x0L, RADIO1, (void*)"y = log(y)", 82, 48, 30, 8},
+		{213, 214, 0, 0x0L, RADIO1, (void*)"y = 1/y", 82, 56, 30, 8},
+		{214, 0, 0, LASTOBJ, RADIO1, (void*)"y = sqrt(y)", 82, 64, 30, 8}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int c, i, j, k, l, ic, res, width, height, n;
+	double x, y;
+	AccRange *rX, *rY;
+	bool bRet = false, bContinue = false, dValid;
+	lfPOINT *values = 0L;
+
+	rX = rY = 0L;
+	data->GetSize(&width, &height);
+	sprintf(text1, "a1:a%d", height);
+	sprintf(text2, "b1:b%d", height);
+	Dlg = new DlgRoot(RegDlg);
+	hDlg = CreateDlgWnd("Linear regression analysis step 1/2", 50, 50, 380, 225, 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)) 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)) 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)) xRange = strdup(TmpTxt);
+		if(Dlg->GetText(103, TmpTxt)) yRange = strdup(TmpTxt);
+		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 | 0x2))&&
+				(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;
+	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, 0x0L, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"range for X Data", 10, 40, 60, 8},
+		{101, 102, 0, 0x0L, EDTEXT, text1, 20, 50, 100, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 65, 60, 8},
+		{103, 104, 0, 0x0L, EDTEXT, text2, 20, 75, 100, 10},
+		{104, 105, 0, 0x0L, LTEXT, (void*)"range for sizes", 10, 90, 60, 8},
+		{105, 0, 0, 0x0L, EDTEXT, 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, COLBUTTON, (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, COLBUTTON, (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, width, height, 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};
+
+	data->GetSize(&width, &height);
+	sprintf(text1, "a1:a%d", height);
+	sprintf(text2, "b1:b%d", height);
+	sprintf(text3, "c1:c%d", height);
+	memcpy(&ShowFill, &BubbleFill, sizeof(FillDEF));
+	if(BubbleFill.hatch) memcpy(&ShowFillLine, BubbleFill.hatch, sizeof(LineDEF));
+	ShowFill.hatch = &ShowFillLine;
+	Dlg = new DlgRoot(PlotDlg);
+	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) && Dlg->GetText(103, text2) && 
+				Dlg->GetText(105, text3) && (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
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+PolarPlot::AddPlot()
+{
+	char text1[100], text2[100];
+	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, 0, 200, ISPARENT | CHECKED, GROUPBOX, (void*)" select template and data range ", 5, 10, 131, 100},
+		{200, 201, 0, CHECKED | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 10, 20, 20, 20},
+		{201, 202, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 30, 20, 20, 20},
+		{202, 203, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 50, 20, 20, 20},
+		{203, 204, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 70, 20, 20, 20},
+		{204, 210, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PolarTempl, 90, 20, 20, 20},
+		{210, 211, 0, 0x0L, LTEXT, (void*)"range for x-data (circular or angular data)", 10, 50, 50, 8},
+		{211, 212, 0, 0x0L, EDTEXT, (void*)text1, 20, 62, 100, 10},
+		{212, 213, 0, 0x0L, LTEXT, (void*)"range for y-data (radial data)", 10, 75, 50, 8},
+		{213, 0, 0, LASTOBJ, EDTEXT, (void*)text2, 20, 87, 100, 10}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, k, l, ic, n, res, width, height, cType = 200;
+	bool bRet = false;
+	double x, y;
+	AccRange *rX = 0L, *rY = 0L;
+	Symbol **Symbols = 0L;
+	DataLine *TheLine = 0L;
+	Plot **tmpPlots;
+	Function *func;
+
+	data->GetSize(&width, &height);
+	sprintf(text1, "a1:a%d", height);
+	sprintf(text2, "b1:b%d", height);
+	if(!(Dlg = new DlgRoot(PolDlg)))return false;
+	hDlg = CreateDlgWnd("Add Polar Plot", 50, 50, 388, 260, Dlg, 0x0L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		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, text1) && Dlg->GetText(213, text2) && 
+		(rX = new AccRange(text1)) && (rY = new AccRange(text2)) &&
+		(n = rX ? rX->CountItems() : 0) && 
+		(tmpPlots = (Plot**)realloc(Plots, (nPlots+2)*sizeof(Plot*)))) {
+		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, text1, text2);
+		else if(Dlg->GetCheck(203))
+			TheLine = new DataPolygon(this, data, text1, text2);
+		else if(Dlg->GetCheck(204)) {
+			if(func = new Function(this, data)){
+				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;
+	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);
+	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 | CHECKED, SHEET, &tab1, 5, 10, 131, 100},
+		{5, 10, 200, ISPARENT | TOUCHEXIT, SHEET, &tab2, 5, 10, 131, 100},
+		{10, 0, 0, 0x0L, 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, EDTEXT, (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, EDTEXT, (void*)text2, 20, 92, 100, 10}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, width, height, 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(Plots) return Config();
+	if(parent) {
+		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;
+		}
+	else return false;
+	data->GetSize(&width, &height);
+	sprintf(text1, "a1:a%d", height);
+	sprintf(text2, "b1:b%d", height);
+	tlbdef.ColTxt = defs.Color(COL_AXIS);
+	tlbdef.ColBg = 0x00ffffffL;
+	tlbdef.RotBL = tlbdef.RotCHAR = 0.0f;
+	tlbdef.fSize = defs.GetSize(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)))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:
+			bType = true;
+			res = -1;
+			break;
+		case 1:
+			if(!bType) {		//the 'Type' sheet must have been visited
+				bType = true;
+				Dlg->SetCheck(5, 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) && Dlg->GetText(213, text2) && 
+			(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)){
+					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(-defs.GetSize(SIZE_AXIS_TICKS)*6.0)); 
+				Axes[1]->SetSize(SIZE_TLB_XDIST, 
+					NiceValue(-defs.GetSize(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
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+BoxPlot::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 36, 10, "Summary"};
+	TabSHEET tab2 = {36, 56, 10, "Box"};
+	TabSHEET tab3 = {56, 89, 10, "Whisker"};
+	TabSHEET tab4 = {89, 119, 10, "Symbol"};
+	TabSHEET tab5 = {119, 140, 10, "Line"};
+	char text1[100];
+	DlgInfo PlotDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 158, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 158, 25, 45, 12},
+		{3, 800, 4, ISPARENT | CHECKED, GROUP, NULL, 138, 40, 55, 12},
+		{4, 5, 0, 0x0L, LTEXT, (void*)"spreadsheet range for common X values", 15, 88, 120, 8},
+		{5, 6, 0, 0x0L, EDTEXT, text1, 25, 100, 100, 10},
+		{6, 7, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 140, 75},
+		{7, 8, 200, ISPARENT, SHEET, &tab2, 5, 10, 140, 75},
+		{8, 9, 300, ISPARENT, SHEET, &tab3, 5, 10, 140, 75},
+		{9, 10, 400, ISPARENT, SHEET, &tab4, 5, 10, 140, 75},
+		{10, 20, 500, ISPARENT, SHEET, &tab5, 5, 10, 140, 75},
+		{20, 0, 0, 0x0L, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"items:", 15, 30, 15, 8},
+		{101, 102, 0, 0x0L, CHECKBOX, (void*)" boxes (rectangles)", 40, 30, 60, 8},
+		{102, 103, 0, 0x0L, CHECKBOX, (void*)" whiskers (error bars)", 40, 40, 60, 8},
+		{103, 104, 0, 0x0L, CHECKBOX, (void*)" symbol in the middle", 40, 50, 60, 8},
+		{104, 105, 0, 0x0L, CHECKBOX, (void*)" line connecting close - open", 40, 60, 60, 8},
+		{105, 0, 0, 0x0L, CHECKBOX, (void*)" horizontal plot", 15, 72, 60, 8},
+		{200, 201, 0, 0x0L, LTEXT, (void*)"range for high values", 15, 30, 60, 8},
+		{201, 202, 0, 0x0L, EDTEXT, 0L, 25, 40, 100, 10},
+		{202, 203, 0, 0x0L, LTEXT, (void*)"range for low values", 15, 55, 60, 8},
+		{203, 0, 0, 0x0L, EDTEXT, 0L, 25, 65, 100, 10},
+		{300, 301, 0, 0x0L, LTEXT, (void*)"range for maxima", 15, 30, 60, 8},
+		{301, 302, 0, 0x0L, EDTEXT, 0L, 25, 40, 100, 10},
+		{302, 303, 0, 0x0L, LTEXT, (void*)"range for minima", 15, 55, 60, 8},
+		{303, 0, 0, 0x0L, EDTEXT, 0L, 25, 65, 100, 10},
+		{400, 401, 0, 0x0L, LTEXT, (void*)"mark centers with symbols,", 15, 30, 60, 8},
+		{401, 402, 0, 0x0L, LTEXT, (void*)"range for centers (means):", 15, 40, 60, 8},
+		{402, 0, 0, 0x0L, EDTEXT, 0L, 25, 55, 100, 10},
+		{500, 501, 0, 0x0L, LTEXT, (void*)"range for first value (open)", 15, 30, 60, 8},
+		{501, 502, 0, 0x0L, EDTEXT, 0L, 25, 40, 100, 10},
+		{502, 503, 0, 0x0L, LTEXT, (void*)"range for second value (close)", 15, 55, 60, 8},
+		{503, 0, 0, LASTOBJ, EDTEXT, 0L, 25, 65, 100, 10}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, j, k, l, m, n, ic, res, width, height;
+	bool bHor, bRet, bContinue = false;
+	lfPOINT fp1, fp2;
+	double x, y1, y2;
+	AccRange *rX, *rB1, *rB2, *rW1, *rW2, *rS, *rL1, *rL2;
+
+	rX = rB1 = rB2 = rW1 = rW2 = rS = rL1 = rL2 = 0L;
+	data->GetSize(&width, &height);
+	sprintf(text1, "a1:a%d", height);
+	Dlg = new DlgRoot(PlotDlg);
+	hDlg = CreateDlgWnd("Box and Whisker Plot", 50, 50, 420, 260, Dlg, 0x0L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue) res = -1;
+			else if(Dlg->GetCheck(20)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 1:
+			if(rX) delete rX;
+			if(rB1) delete rB1;			if(rB2) delete rB2;
+			if(rW1) delete rW1;			if(rW2) delete rW2;
+			if(rS) delete rS;
+			if(rL1) delete rL1;			if(rL2) delete rL2;
+			rX = rB1 = rB2 = rW1 = rW2 = rS = rL1 = rL2 = 0L;
+			bHor = Dlg->GetCheck(105);
+			if(Dlg->GetText(5, text1)) rX = new AccRange(text1);
+			n = rX ? rX->CountItems() : 0;
+			if(!n) {
+				ErrorBox("Common X-range not specified\nor not valid.");
+				bContinue = true;
+				res = -1;
+				}
+			if(n && Dlg->GetCheck(101)) {		//do boxes ?
+				if(Dlg->GetText(201, TmpTxt)) rB1 = new AccRange(TmpTxt);
+				if(Dlg->GetText(203, TmpTxt)) rB2 = new AccRange(TmpTxt);
+				if(!rB1 || !rB2 || n != rB1->CountItems() || n != rB2->CountItems()) {
+					ErrorBox("Range for boxes missing\nor not valid.\n"
+						"Size must match common X-range.");
+					Dlg->SetCheck(7, 0L, true);
+					bContinue = true;
+					res = -1;
+					}
+				}
+			if(n && Dlg->GetCheck(102)) {		//do whiskers ?
+				if(Dlg->GetText(301, TmpTxt)) rW1 = new AccRange(TmpTxt);
+				if(Dlg->GetText(303, TmpTxt)) rW2 = new AccRange(TmpTxt);
+				if(!rW1 || !rW2 || n != rW1->CountItems() || n != rW2->CountItems()) {
+					ErrorBox("Range for whiskers missing\nor not valid.\n"
+						"Size must match common X-range.");
+					Dlg->SetCheck(8, 0L, true);
+					bContinue = true;
+					res = -1;
+					}
+				}
+			if(n && Dlg->GetCheck(103)) {		//do symbols ?
+				if(Dlg->GetText(402, TmpTxt)) rS = new AccRange(TmpTxt);
+				if(!rS || n != rS->CountItems()) {
+					ErrorBox("Range for symbols missing\nor not valid.\n"
+						"Size must match common X-range.");
+					bContinue = true;
+					Dlg->SetCheck(9, 0L, true);
+					res = -1;
+					}
+				}
+			if(n && Dlg->GetCheck(104)) {		//do line ?
+				if(Dlg->GetText(501, TmpTxt)) rL1 = new AccRange(TmpTxt);
+				if(Dlg->GetText(503, TmpTxt)) rL2 = new AccRange(TmpTxt);
+				if(!rL1 || !rL2 || n != rL1->CountItems() || n != rL2->CountItems()) {
+					ErrorBox("Range for line missing\nor not valid.\n"
+						"Size must match common X-range.");
+					Dlg->SetCheck(10, 0L, true);
+					bContinue = true;
+					res = -1;
+					}
+				}
+			if(n && res > 0 && !rB1 && !rB2 && !rW1 && !rW2 && !rS && !rL1 && !rL2) {
+				ErrorBox("Nothing to do !\n\nSelect at least one of either\n"
+					"Box, Whisker, Symbol or Line.");
+				bContinue = true;
+				res = -1;
+				}
+			break;
+			}
+		}while (res < 0);
+	bRet = false;
+	if(res == 1 && n && rX){			//ok: create objects for plot
+		nPoints = n;
+		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+		if(rB1 && rB2) {
+			//DEBUG: delete existing objects first
+			Boxes =(Box**)calloc(nPoints, sizeof(Box*));
+			if(Boxes) {
+				rX->GetFirst(&i, &j);	rB1->GetFirst(&k, &l);	rB2->GetFirst(&m, &n);
+				rX->GetNext(&i, &j);	rB1->GetNext(&k, &l);	rB2->GetNext(&m, &n);
+				ic = 0;
+				do {
+					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1) && 
+						data->GetValue(n, m, &y2)){
+						if(bHor) {
+							fp1.fx = y2;	fp2.fx = y1;		fp1.fy = fp2.fy = x;
+							CheckBounds(y1, x);			CheckBounds(y2, x);
+							Boxes[ic++] = new Box(this, data, fp1, fp2, 0, m, n, i, j, k, l, i, j);
+							}
+						else {
+							fp1.fy = y2;	fp2.fy = y1;		fp1.fx = fp2.fx = x;
+							CheckBounds(x, y1);			CheckBounds(x, y2);
+							Boxes[ic++] = new Box(this, data, fp1, fp2, 0, i, j, m, n, i, j, k, l);
+							}
+						}
+					}while(rX->GetNext(&i, &j) && rB1->GetNext(&k, &l) && rB2->GetNext(&m, &n));
+				if(ic) bRet = true;
+				BoxDist.fx = BoxDist.fy = GetSize(bHor ? SIZE_BOXMINY : SIZE_BOXMINX);
+				}
+			}
+		if(rW1 && rW2) {
+			Whiskers = (Whisker**)calloc(nPoints, sizeof(Whisker*));
+			if(Whiskers) {
+				rX->GetFirst(&i, &j);	rW1->GetFirst(&k, &l);	rW2->GetFirst(&m, &n);
+				rX->GetNext(&i, &j);	rW1->GetNext(&k, &l);	rW2->GetNext(&m, &n);
+				ic = 0;
+				do {
+					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1) && 
+						data->GetValue(n, m, &y2)){
+						if(bHor) {
+							fp1.fx = y2;	fp2.fx = y1;		fp1.fy = fp2.fy = x;
+							CheckBounds(y1, x);			CheckBounds(y2, x);
+							Whiskers[ic++] = new Whisker(this, data, fp1, fp2, 0, m, n, i, j, k, l, i, j);
+							}
+						else {
+							fp1.fy = y2;	fp2.fy = y1;		fp1.fx = fp2.fx = x;
+							CheckBounds(x, y1);			CheckBounds(x, y2);
+							Whiskers[ic++] = new Whisker(this, data, fp1, fp2, 0, i, j, m, n, i, j, k, l);
+							}
+						}
+					}while(rX->GetNext(&i, &j) && rW1->GetNext(&k, &l) && rW2->GetNext(&m, &n));
+				if(ic) bRet = true;
+				}
+			}
+		if(rS) {
+			Symbols = (Symbol**)calloc(nPoints, sizeof(Symbol*));
+			if(Symbols) {
+				rX->GetFirst(&i, &j);	rS->GetFirst(&k, &l);
+				rX->GetNext(&i, &j);	rS->GetNext(&k, &l);
+				ic = 0;
+				do {
+					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1)){
+						if(bHor) {
+							CheckBounds(y1, x);
+							Symbols[ic++] = new Symbol(this, data, y1, x, 
+								SYM_PLUS, k, l, i, j);
+							}
+						else {
+							CheckBounds(x, y1);
+							Symbols[ic++] = new Symbol(this, data, x, y1, 
+								SYM_PLUS, i, j, k, l);
+							}
+						}
+					}while(rX->GetNext(&i, &j) && rS->GetNext(&k, &l));
+				if(ic) bRet = true;
+				}
+			}
+		if(rL1 && rL2) {
+			Dlg->GetText(501, TmpTxt);			i = strlen(TmpTxt);
+			TmpTxt[i++] = ';';					TmpTxt[i++] = ' ';
+			Dlg->GetText(503, TmpTxt+i);
+			TheLine = new DataLine(this, data, text1, TmpTxt);
+			bRet = true;
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	if(rX) delete rX;
+	if(rB1) delete rB1;			if(rB2) delete rB2;
+	if(rW1) delete rW1;			if(rW2) delete rW2;
+	if(rS) delete rS;
+	if(rL1) delete rL1;			if(rL2) delete rL2;
+	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, 0x0L, 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, EDTEXT, text1, 20, 40, 100, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void*)"range for width (density) data", 10, 55, 60, 8},
+		{103, 104, 0, 0x0L, EDTEXT, 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, width, height, align = 0;
+	bool bRet = false, bContinue = false, bVert;
+	AccRange *rX = 0L, *rY = 0L;
+
+	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)defs.GetOutLine(), 0);
+	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)defs.GetFill(), 0);
+	data->GetSize(&width, &height);
+	sprintf(text1, "a1:a%d", height);
+	sprintf(text2, "b1:b%d", height);
+	Dlg = new DlgRoot(PlotDlg);
+	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)) 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) && (rY = new AccRange(TmpTxt))){
+				if(n != rY->CountItems()) {
+					ErrorBox("both ranges must be given\nand must have same size");
+					bContinue = true;
+					res = -1;
+					}
+				}
+			}
+		}while (res < 0);
+	if(res == 1 && n && rX && rY) {
+		type = (bVert = Dlg->GetCheck(104)) ? align | 0x10 : align;
+		if(Dlg->GetText(101, TmpTxt)) xRange=strdup(TmpTxt);
+		if(Dlg->GetText(103, TmpTxt)) yRange=strdup(TmpTxt);
+		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 
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+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) && TmpTxt[0] && 
+				(*rX = new AccRange(TmpTxt))) *nx = rX[0]->CountItems();
+			else if(nx) *nx = 0;
+			if(Dlg->GetText(154, TmpTxt) && TmpTxt[0]) {
+				if(rd[0][*currYR]) free(rd[0][*currYR]);
+				rd[0][*currYR] = strdup(TmpTxt);
+				}
+			break;
+		case 155:
+			res = -1;
+			*ny = 0;
+			if(rX) {
+				if(!(*currYR) && Dlg->GetText(101, TmpTxt) && 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) && 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] = strdup(TmpTxt);	//store y-ranges
+			*updateYR = true;
+			(*currYR)++;
+			Dlg->SetText(154, rd[0][*currYR]);
+			Dlg->Activate(154, true);
+			break;
+		case 156:
+			if(Dlg->GetText(154, TmpTxt)){
+				if(rd[0][*currYR]) free(rd[0][*currYR]);
+				rd[0][*currYR] = strdup(TmpTxt);
+				}
+			else if(*currYR == *maxYR) (*maxYR)--;
+			(*currYR)--;
+			Dlg->SetText(154, rd[0][*currYR]);
+			*updateYR = true;
+			res = -1;
+			break;
+		}
+	return res;
+}
+
+bool
+StackBar::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 55, 10, "Details"};
+	TabSHEET tab3 = {55, 90, 10, "Scheme"};
+	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, 0x0L, 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, EDTEXT, 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, EDTEXT, 0L, 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, CHECKED, RADIO1, (void*)" add each y to start value", 25, 35, 60, 8},
+		{201, 202, 0, 0x0L, RADIO1, (void*)" subtract each y from start value", 25, 50, 60, 8},
+		{202, 203, 0, 0x0L, RTEXT, (void*)"start value:", 31, 65, 38, 8},
+		{203, 204, 0, 0x0L, EDVAL1, &StartVal, 70, 65, 30, 10},
+		{204, 0, 0, 0x0L, CHECKBOX, (void*)" horizontal plot", 25, 90, 60, 8},
+		{300, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)(OD_scheme), 20, 35, 80, 60}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, sc, j, res, width, height, currYR = 0, maxYR = 0, nx = 0, ny;
+	bool updateYR = true, bContinue = false, bSub, bRet = false, bHor;
+	char **rd;
+	AccRange *rX = 0L, *rY = 0L;
+
+	data->GetSize(&width, &height);
+	sprintf(TmpTxt, "a1:a%d", height);
+	if(!(rd = (char**)calloc(1, sizeof(char*))))return false;
+	Dlg = new DlgRoot(StackBarDlg);
+	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);
+				}
+			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
+			//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) j += sprintf(TmpTxt+j, "&");
+			j += sprintf(TmpTxt+j, "%s", rd[i]);
+			}
+		ssYrange = strdup(TmpTxt);
+		if(Dlg->GetText(101, TmpTxt)) ssXrange = strdup(TmpTxt);
+		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(Id == GO_STACKBAR){
+			numPlots = maxYR;
+			if(Boxes = (BoxPlot**)calloc(numPlots, sizeof(BoxPlot*))) 
+				for(i = sc = 0; i < (maxYR); i++) {
+					if(Boxes[i]= new BoxPlot(this, CumData, Dlg->GetCheck(204)?2:1, 0, i+1, i+2)){
+					Boxes[i]->Command(CMD_UPDATE, 0L, 0L);
+					Boxes[i]->Command(CMD_AUTOSCALE, 0L, 0L);
+					Boxes[i]->Command(CMD_BOX_FILL, GetSchemeFill(&sc), 0L);
+					}
+				}
+			}
+		//do stacked polygon
+		else if(Id == GO_STACKPG){
+			numPG = maxYR;			sprintf(TmpTxt, "a1:a%d", nx*2);
+			if(Polygons=(DataPolygon**)calloc(numPG,sizeof(DataPolygon*)))for(i=sc=0;i<maxYR;i++){
+				sprintf(TmpTxt+20, "%c1:%c%d", 'c'+i, 'c'+i, nx*2);
+				if(Dlg->GetCheck(204)) Polygons[i]=new DataPolygon(this,CumData,TmpTxt+20,TmpTxt);
+				else Polygons[i] = new DataPolygon(this, CumData, TmpTxt, TmpTxt+20);
+				if(Polygons[i]) {
+					Polygons[i]->Command(CMD_AUTOSCALE, 0L, 0L);
+					Polygons[i]->Command(CMD_PG_FILL, GetSchemeFill(&sc), 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;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// create grouped bars chart
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+GroupBars::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 55, 10, "Details"};
+	TabSHEET tab3 = {55, 90, 10, "Scheme"};
+	double start = 1.0, step = 1.0, bw = 100.0, gg = 100.0;
+	DlgInfo GBDlg[] = {
+		{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, 90},
+		{5, 6, 200, ISPARENT, SHEET, &tab2, 5, 10, 120, 90},
+		{6, 10, 300, ISPARENT, SHEET, &tab3, 5, 10, 120, 90},
+		{10, 0, 0, 0x0L, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"Get values from spreadsheet:", 10, 25, 60, 8},
+		{101, 150, 0, 0x0L, LTEXT, (void*)"All ranges should have equal size!", 10, 33, 60, 8},
+		{150, 0, 153, ISPARENT | CHECKED, GROUPBOX, (void*)" ranges for y values ", 10, 50, 110, 45},
+		{153, 154, 0, 0x0L, LTEXT, 0L, 15, 55, 60, 8},
+		{154, 155, 0, 0x0L, EDTEXT, 0L, 15, 65, 100, 10},
+		{155, 156, 0, 0x0L, PUSHBUTTON, (void*)"Next >>", 85, 77, 30, 12},
+		{156, 0, 0, 0x0L, PUSHBUTTON, (void*)"<< Prev.", 50, 77, 35, 12},
+		{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*)"group step", 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, 207, 0, 0x0L, LTEXT, (void*)"%", 95, 65, 8, 8},
+		{207, 208, 0, 0x0L, RTEXT, (void*)"group gap", 10, 80, 38, 8},
+		{208, 209, 0, 0x0L, EDVAL1, (void*)&gg, 58, 80, 35, 10},
+		{209, 0, 0, 0x0L, LTEXT, (void*)"%", 95, 80, 8, 8},
+		{300, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)(OD_scheme), 20, 30, 90, 60}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	bool bRet = false, updateYR = true, bContinue = false;
+	char **rd;
+	Bar **bars = 0L;
+	AccRange *rY = 0L;
+	int i, ic, res, ix, iy, ny, sc = 0, currYR = 0, maxYR = 0;
+	double x, y, xinc;
+
+	Id = GO_STACKBAR;
+	if(!(rd = (char**)calloc(1, sizeof(char*))))return false;
+	if(!(Dlg = new DlgRoot(GBDlg)))return false;
+	hDlg = CreateDlgWnd("Grouped bar chart", 50, 50, 370, 240, Dlg, 0x0L);
+	do {
+		if(updateYR) {
+			if(currYR >0) Dlg->ShowItem(156, true);
+			else Dlg->ShowItem(156, false);
+			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
+			//SetText will also cause a redraw of the whole dialog
+			Dlg->SetText(153, TmpTxt);
+			updateYR = false;
+			}
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:
+			if(bContinue || Dlg->GetCheck(10)) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			break;
+		case 1:		
+			Dlg->GetValue(201, &start);			Dlg->GetValue(203, &step);
+			Dlg->GetValue(205, &bw);			Dlg->GetValue(208, &gg);
+		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]){	//accept settings and create plots
+		if(rd[maxYR]) maxYR++;
+		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; i < maxYR; i++) {
+			x = start + xinc * (double)i;
+			if(rd[i] && (rY = new AccRange(rd[i]))) ny = rY->CountItems();
+			else {
+				rY = 0L;	ny = 0;
+				}
+			rY->GetFirst(&ix, &iy);				rY->GetNext(&ix, &iy);
+			if(ny && rY && (bars = (Bar **)calloc(ny, sizeof(Bar*)))){
+				for(ic = 0; ic < ny; ic++) {
+					if(data->GetValue(iy, ix, &y)){
+						bars[ic] = new Bar(0L, data, x, y, BAR_VERTB | BAR_RELWIDTH,
+							-1, -1, ix, iy);
+						CheckBounds(x, y);
+						}
+					x += step;
+					if(!rY->GetNext(&ix, &iy))break;
+					}
+				xyPlots[numXY++] = new PlotScatt(this, data, ic+1, bars);
+				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]);
+				free(bars);
+				bRet = true;
+				}
+			if(rY) 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(rY) delete rY;
+	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, 0x0L, 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, EDTEXT, text1, 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, EDTEXT, 0L, 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, COLBUTTON, (void*)defcol, 105, 35, 20, 10},
+		{302, 303, 0, 0x0L, RADIO1, (void*)" increment color scheme:", 20, 55, 80, 9},
+		{303, 304, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[0], 25, 70, 10, 10},
+		{304, 305, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[1], 37, 70, 10, 10},
+		{305, 306, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[2], 49, 70, 10, 10},
+		{306, 307, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[3], 61, 70, 10, 10},
+		{307, 308, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[4], 73, 70, 10, 10},
+		{308, 309, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[5], 85, 70, 10, 10},
+		{309, 310, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[6], 97, 70, 10, 10},
+		{310, 0, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[7], 109, 70, 10, 10},
+		{500, 0, 0, LASTOBJ, NONE, 0L, 0, 0, 0, 0}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, res, width, height, currYR=0, maxYR=0, nx=0, ny;
+	char **rd;
+	bool updateYR = true, bContinue = false, bRet = false, bUseSch;
+	AccRange *rX = 0L, *rY = 0L;
+
+	data->GetSize(&width, &height);
+	sprintf(text1, "a1:a%d", height);
+	if(!(rd = (char**)calloc(1, sizeof(char*))))return false;
+	Dlg = new DlgRoot(StackBarDlg);
+	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);
+				}
+			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
+			//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);
+		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
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+MultiLines::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Data"};
+	TabSHEET tab2 = {25, 60, 10, "Scheme"};
+	static DWORD colarr[] = {0x00000000L, 0x00ff0000L, 0x0000ff00L, 0x000000ffL,
+		0x00ff00ff, 0x00ffff00L, 0x0000ffff, 0x00c0c0c0};
+	static DWORD defcol = 0x0L;
+	char x_txt[100], y_txt[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, 20, 300, ISPARENT, SHEET, &tab2, 5, 10, 140, 100},
+		{20, 0, 0, 0x0L, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, ISPARENT | CHECKED, GROUPBOX, (void*)" ranges for x- and y- values ", 12, 30, 128, 75},
+		{101, 102, 0, 0x0L, LTEXT, 0L, 25, 39, 60, 8},
+		{102, 103, 0, 0x0L, EDTEXT, (void*)x_txt, 25, 49, 100, 10},
+		{103, 104, 0, 0x0L, LTEXT, 0L, 25, 61, 60, 8},
+		{104, 105, 0, 0x0L, EDTEXT, (void*)y_txt, 25, 71, 100, 10},
+		{105, 106, 0, 0x0L, PUSHBUTTON, (void*)"Next >>", 95, 87, 30, 12},
+		{106, 107, 0, 0x0L, PUSHBUTTON, (void*)"<< Prev.", 60, 87, 35, 12},
+		{107, 0, 0, OWNDIALOG, COLBUTTON, (void*)colarr[0], 25, 87, 20, 12},
+		{300, 301, 0, TOUCHEXIT, RADIO1, (void*)" common color for lines:", 20, 35, 80, 9},
+		{301, 302, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)defcol, 105, 35, 20, 10},
+		{302, 303, 0, CHECKED | TOUCHEXIT, RADIO1, (void*)" increment color scheme:", 20, 55, 80, 9},
+		{303, 304, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[0], 25, 70, 10, 10},
+		{304, 305, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[1], 37, 70, 10, 10},
+		{305, 306, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[2], 49, 70, 10, 10},
+		{306, 307, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[3], 61, 70, 10, 10},
+		{307, 308, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[4], 73, 70, 10, 10},
+		{308, 309, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[5], 85, 70, 10, 10},
+		{309, 310, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[6], 97, 70, 10, 10},
+		{310, 0, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[7], 109, 70, 10, 10},
+		{500, 0, 0, LASTOBJ, NONE, 0L, 0, 0, 0, 0}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	char **rdx=0L, **rdy=0L;
+	DWORD *rdc = 0L, curr_col;
+	int i, width, height, res, currYR=0, maxYR=0, s1, s2;
+	bool updateYR = true, bContinue = false, bError, bRet = false;
+	AccRange *rX = 0L, *rY = 0L;
+	DataLine *dl;
+
+	data->GetSize(&width, &height);
+	sprintf(x_txt, "a1:a%d", height);
+	sprintf(y_txt, "b1:b%d", height);
+	Dlg = new DlgRoot(StackBarDlg);
+	hDlg = CreateDlgWnd("Create multi line plot", 50, 50, 420, 260, Dlg, 0x0L);
+	do {
+		if(updateYR) {
+			if(currYR >0) Dlg->ShowItem(106, true);
+			else Dlg->ShowItem(106, false);
+			sprintf(TmpTxt,"x-range # %d/%d", currYR+1, maxYR+1);
+			//SetText will also cause a redraw of the whole dialog
+			Dlg->SetText(101, TmpTxt);
+			sprintf(TmpTxt,"y-range # %d/%d", currYR+1, maxYR+1);
+			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)) {
+				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) && !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]);
+				rdx[currYR] = strdup(x_txt);			//store x-range
+				if(rdy[currYR]) free(rdy[currYR]);
+				rdy[currYR] = strdup(y_txt);			//store y-range
+				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) && Dlg->GetText(104, y_txt)){
+				if(rdx[currYR]) free(rdx[currYR]);		rdx[currYR] = strdup(x_txt);
+				if(rdy[currYR]) free(rdy[currYR]);		rdy[currYR] = strdup(y_txt);
+				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 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);
+			Dlg->GetColor(res, &colarr[res-303]);
+			res = -1;	break;
+			}
+		}while (res < 0);
+
+	if(res == 1 && rdx && rdy && maxYR) {
+		maxYR++;
+		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(dl = new DataLine(this, data, rdx[i], rdy[i])) {
+					dl->SetColor(COL_DATA_LINE, rdc[i]);
+					if(xyPlots[numXY] = new PlotScatt(this, data, 0, 0L, dl)) numXY++;
+					else delete dl;
+					}
+				}
+			}
+		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);
+		}
+	if(rdc) free(rdc);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Pie and ring chart properties
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+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];
+	DlgInfo PieDlg[] = {
+		{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, 6, 200, ISPARENT, SHEET, &tab2, 5, 10, 120, 103},
+		{6, 10, 300, ISPARENT, SHEET, &tab3, 5, 10, 120, 103},
+		{10, 0, 0, 0x0L, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"spread sheet range for values", 10, 25, 60, 8},
+		{101, 105, 0, 0x0L, EDTEXT, TmpTxt, 15, 35, 100, 10},
+		{105, 106, 500, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{106, 107, 600, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{107, 108, 0, 0x0L, EDVAL1, &frad, 58, 59, 30, 10},
+		{108, 0, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 89, 59, 15, 8}, 
+		{200, 201, 0, 0x0L, LTEXT, (void*)"position of center:", 15, 30, 60, 8},
+		{201, 202, 0, 0x0L, RTEXT, (void*)"x", 2, 42, 20, 8},
+		{202, 204, 0, 0x0L, EDVAL1, &fcx, 23, 42, 30, 10},
+		{204, 205, 0, 0x0L, RTEXT, (void*)"y", 47, 42, 20, 8},
+		{205, 206, 0, 0x0L, EDVAL1, &fcy, 68, 42, 30, 10},
+		{206, 207, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 99, 42, 15, 8}, 
+		{207, 208, 0, 0x0L, RTEXT, (void*)"start angle", 27, 58, 20, 8},
+		{208, 209, 0, 0x0L, EDVAL1, &CtDef.fx, 48, 58, 30, 10},
+		{209, 210, 0, 0x0L, LTEXT, (void*)"degree", 79, 58, 15, 8}, 
+		{210, 211, 400, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{211, 212, 410, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{212, 0, 0, 0x0L, LTEXT, (void*)"style:", 15, 80, 30, 8},
+		{300, 0, 0, NOSELECT, ODBUTTON, (void*)(OD_scheme), 20, 35, 80, 60},
+		{400, 401, 0, TOUCHEXIT | CHECKED | ISRADIO, ODBUTTON, (void*)OD_PieTempl, 40, 75, 30, 30},
+		{401, 0, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PieTempl, 70, 75, 30, 30},
+		{410, 411, 0, TOUCHEXIT | CHECKED | ISRADIO, ODBUTTON, (void*)OD_PieTempl, 40, 75, 30, 30},
+		{411, 0, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PieTempl, 70, 75, 30, 30},
+		{500, 501, 0, CHECKED, RADIO1, (void*)"fixed radius", 10, 59, 20, 8},
+		{501, 502, 0, 0x0L, RADIO1, (void*)"pick radii from spreadsheet range", 10, 71, 40, 8},
+		{502, 503, 0, 0x0L, EDTEXT, TmpTxt+100, 15, 82, 100, 10},
+		{503, 504, 0, 0x0L, LTEXT, (void*)"x  factor", 15, 94, 10, 8},
+		{504, 505, 0, 0x0L, EDVAL1, &FacRad, 42, 94, 25, 10},
+		{505, 0, 0, 0x0L, LTEXT, &txt2, 70, 94, 15, 8},
+		{600, 601, 0, 0x0L, RTEXT, (void*)"outer radius", 8, 59, 45, 8},
+		{601, 602, 0, 0x0L, RTEXT, (void*)"inner radius", 8, 74, 45, 8},
+		{602, 603, 0, 0x0L, EDVAL1, &firad, 58, 74, 30, 10},
+		{603, 0, 0, LASTOBJ, LTEXT, (void *) Units[defs.cUnits].display, 89, 74, 15, 8}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, ix, iy, rix, riy, ny, res, width, height, cf;
+	bool bRet = false, bContinue = false;
+	double sum = 0.0, dang1, dang2;
+	double fv;
+	lfPOINT fpCent;
+	AccRange *rY = 0L, *rR = 0L;
+
+	data->GetSize(&width, &height);
+	sprintf(TmpTxt, "a1:a%d", height);
+	sprintf(TmpTxt+100, "b1:b%d", height);
+	sprintf(txt2, "= [%s]", Units[defs.cUnits].display);
+	if(parent) {
+		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;
+	Dlg = new DlgRoot(PieDlg);
+	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, 260, 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) && 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) && 
+				(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)) ssRefA = strdup(TmpTxt);
+		if(rR && Dlg->GetText(502, TmpTxt)) ssRefR = strdup(TmpTxt);
+		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;
+	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(defs.GetSize(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, 0x0L, 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, EDTEXT, 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, EDTEXT, (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, defs.GetSize(SIZE_TEXT), 0.0, 0.0, 0,
+		TXA_HCENTER | TXA_VCENTER, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, 0L}; 
+
+
+	data->GetSize(&width, &height);
+	sprintf(txt1, "a1:a%d", height);
+	sprintf(txt2, "= [%s]", Units[defs.cUnits].display);
+	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)))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) && 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) && 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;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Scatt3D is a layer representing most simple 3D plots
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+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;
+	DlgInfo Dlg3D[] = {
+		{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, 100},
+		{5, 6, 200, TOUCHEXIT | ISPARENT, SHEET, &tab2, 5, 10, 131, 100},
+		{6, 10, 400, TOUCHEXIT | ISPARENT, SHEET, &tab3, 5, 10, 131, 100},
+		{10, 0, 0, 0x0L, CHECKPIN, 0L, 5, 0, 12, 8},
+		{50, 60, 0, NOSELECT, ODBUTTON, (void*)(OD_AxisDesc3D), 142, 65, 45, 45},
+		{60, 61, 0, 0x0L, ICON, (void*)&icon, 10, 114, 20, 20},
+		{61, 62, 0, 0x0L, LTEXT, (void*)"Use [arrow keys], [shift]+[arrow key],", 30, 116, 100, 6},
+		{62, 0, 0, 0x0L, LTEXT, (void*)"and [r], [R], [l] or [L] to rotate graph.", 30, 122, 100, 6},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"range for X Data", 10, 30, 60, 8},
+		{101, 102, 0, 0x0L, EDTEXT, text1, 20, 40, 100, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 55, 60, 8},
+		{103, 104, 0, 0x0L, EDTEXT, text2, 20, 65, 100, 10},
+		{104, 105, 0, 0x0L, LTEXT, (void*)"range for Z Data", 10, 80, 60, 8},
+		{105, 0, 0, 0x0L, EDTEXT, text3, 20, 90, 100, 10},
+		{200, 201, 0, 0x0L, LTEXT, (void*)"select style:", 25, 30, 60, 8},
+		{201, 202, 0, 0x0L, CHECKBOX, (void*)" balls", 30, 55, 60, 8},
+		{202, 203, 0, TOUCHEXIT, CHECKBOX, (void*)" columns", 30, 65, 60, 8},
+		{203, 204, 0, TOUCHEXIT, CHECKBOX, (void*)" line", 30, 45, 60, 8},
+		{204, 205, 0, TOUCHEXIT, CHECKBOX, (void*)" drop lines", 30, 75, 60, 8},
+		{205, 0, 0, TOUCHEXIT, CHECKBOX, (void*)" arrows", 30, 85, 60, 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 res, width, height, n1, n2, n3, ic = 0;
+	int i, j, k, l, m, n, i2, j2, k2, l2, m2;
+	double x, y, z, bar_w, bar_d, rad;
+	bool bRet = false, bContinue = false;
+	AccRange *rX, *rY, *rZ;
+	fPOINT3D pos1, pos2;
+
+	if(!data || !parent || parent->Id != GO_PLOT3D)return false;
+	data->GetSize(&width, &height);
+	sprintf(text1, "a1:a%d", height);		sprintf(text2, "b1:b%d", height);
+	sprintf(text3, "c1:c%d", height);
+	if(!(Dlg = new DlgRoot(Dlg3D)))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) {
+		Dlg->ShowItem(5, false);		Dlg->ShowItem(6, false);
+		}
+	else Dlg->ShowItem(6, (c_flags & 0x1000) == 0x1000);
+	rX = rY = rZ = 0L;
+	rad = defs.GetSize(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 3D Plot":(char*)"Create Paravent 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) && (rX = new AccRange(text1))) n1 = rX->CountItems();
+			if(Dlg->GetText(103, text2) && (rY = new AccRange(text2))) n2 = rY->CountItems();
+			if(Dlg->GetText(105, text3) && (rZ = new AccRange(text3))) n3 = rZ->CountItems();
+			if(n1 && n2 && n3){
+				if(c_flags == 0x2000) {
+					//no more but a ribbon
+					}
+				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 {
+				sprintf(TmpTxt, "Ranges for\n%s%s%s\n\nnot given or not valid.",
+					n1 == 0 ? "\n  X Data" : "", n2 == 0 ? "\n  Y Data" : "",
+					n3 == 0 ? "\n  Z Data" : "");
+				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 = (defs.GetSize(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, 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;
+	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, 0, 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, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_linedef, 15, 25, 130, 100}};
+	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;
+
+	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))) 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;
+			}
+		}while (res < 0);
+	while(*Undo.pcb > undo_level)	Undo.Pop();
+	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);
+			cmdxy = strdup(TmpTxt);
+			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);
+			Dlg->GetText(200, TmpTxt);
+			if(cmdxy && strcmp(cmdxy, TmpTxt)) {
+				Undo.String(this, &cmdxy, undo_flags);
+				free(cmdxy);	cmdxy = strdup(TmpTxt); undo_flags |= UNDO_CONTINUE;
+				}
+			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 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, 0x0L, 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, EDTEXT, (void*)ssXref, 20, 40, 100, 10},
+		{402, 403, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 55, 60, 8},
+		{403, 404, 0, 0x0L, EDTEXT, (void*)ssYref, 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;
+	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;
+
+	if(!parent || !data) return false;
+	if(!(o_cmdxy = strdup(cmdxy))) return false;
+	if(!(o_parxy = strdup(parxy))) return false;
+	iter = (double)maxiter;
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+	if(!(Dlg = new DlgRoot(FuncDlg))) 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) {
+			sprintf(TmpTxt, "Chi 2 = %g", chi2);
+			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) && parxy) {
+					free(parxy);	parxy = strdup(TmpTxt);
+					}
+				if(Dlg->GetText(200, TmpTxt) && cmdxy) {
+					free(cmdxy);	cmdxy = strdup(TmpTxt);
+					}
+				ReshapeFormula(&parxy);		ReshapeFormula(&cmdxy);
+				dirty = true;
+				break;
+				}
+		case 7:								//Start: do nonlinear regression
+			if(Dlg->GetCheck(5)) {			//  the function tab must be shown
+				if(Dlg->CurrDisp) Dlg->CurrDisp->MouseCursor(MC_WAIT, true);
+				Dlg->GetText(401, text1);		Dlg->GetText(403, text2);
+				if(Dlg->GetText(102, TmpTxt) && parxy) {
+					free(parxy);	parxy = strdup(TmpTxt);
+					}
+				if(Dlg->GetText(200, TmpTxt) && cmdxy) {
+					free(cmdxy);	cmdxy = strdup(TmpTxt);
+					}
+				Dlg->GetValue(153, &conv);	Dlg->GetValue(155, &iter);
+				ReshapeFormula(&parxy);		ReshapeFormula(&cmdxy);
+				i = do_fitfunc(data, text1, text2, 0L, &parxy, cmdxy, conv, (int)iter, &chi2);
+				Dlg->SetText(102, parxy);
+				if(i >1 || res == 7) {
+					sprintf(TmpTxt, "The Levenberg-Marquart algorithm\nexited after %d iterations.\n\nChi2 = %g", i, chi2);
+					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);
+	while(*Undo.pcb > undo_level)	Undo.Pop();
+	if(res == 1){						//OK pressed
+		//get ranges for x- and y data (again).
+		chi2 = n_chi2;
+		Dlg->GetText(401, text1);		Dlg->GetText(403, text2);
+		if(ssXref) free(ssXref);		if(ssYref) free(ssYref);
+		ssXref = strdup(text1);			ssYref = strdup(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))){
+				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);
+			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);
+			if(o_parxy && parxy && strcmp(o_parxy, parxy)) {
+				tmp_char = parxy;	parxy = o_parxy;
+				Undo.String(this, &parxy, undo_flags);
+				free(parxy);	parxy = tmp_char;	undo_flags |= UNDO_CONTINUE;
+				o_parxy = 0L;
+				}
+			if(o_cmdxy && cmdxy && strcmp(o_cmdxy, cmdxy)) {
+				tmp_char = cmdxy;	cmdxy = o_cmdxy;
+				Undo.String(this, &cmdxy, undo_flags);
+				free(cmdxy);	cmdxy = tmp_char;	undo_flags |= UNDO_CONTINUE;
+				o_cmdxy = 0L;
+				}
+			if(undo_flags & UNDO_CONTINUE) {
+				Undo.ValInt(parent, (int*)&dirty, undo_flags);
+				Command(CMD_MRK_DIRTY, 0L, 0L);
+				}
+			undo_flags = CheckNewFloat(&xstep, o_xstep, n_xstep, this, undo_flags);
+//			Dlg->GetText(200, TmpTxt);
+//			if(cmdxy && strcmp(cmdxy, TmpTxt)) {
+//				Undo.String(this, &cmdxy, undo_flags);
+//				free(cmdxy);	cmdxy = strdup(TmpTxt); undo_flags |= UNDO_CONTINUE;
+//				}
+//			if(undo_flags & UNDO_CONTINUE) Update(0L, UNDO_CONTINUE);
+//			cmdxy = strdup(TmpTxt);
+//			bRet = Update(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;
+	if(o_parxy) free(o_parxy);		if(o_cmdxy) free(o_cmdxy);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Create a three dimensional graph
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Plot3D::AddPlot(int family)
+{
+	DlgInfo PlotsDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 150, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 150, 25, 45, 12},
+		{3, 0, 560, ISPARENT | CHECKED, GROUPBOX, (void *)"  select template  ", 5, 10, 140, 70},
+		{560, 561, 0, TOUCHEXIT | CHECKED | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 12, 20, 25, 25},
+		{561, 562, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 37, 20, 25, 25},
+		{562, 563, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 62, 20, 25, 25},
+		{563, 564, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 87, 20, 25, 25},
+		{564, 0, 0, LASTOBJ | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 112, 20, 25, 25}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, cSel = 560;
+	bool bRet = false;
+	Plot *p;
+
+	if(!(Dlg = new DlgRoot(PlotsDlg)))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:
+			if(res == cSel) res = 1;
+			else {
+				cSel = res;
+				res = -1;
+				}
+			break;
+			}
+		}while (res < 0);
+	if(res == 1){						//OK pressed
+		if(Dlg->GetCheck(560)) p = new Scatt3D(this, data, 0x01);
+		else if(Dlg->GetCheck(561)) p = new Scatt3D(this, data, 0x02);
+		else if(Dlg->GetCheck(562)) p = new Scatt3D(this, data, 0x04);
+		else if(Dlg->GetCheck(563)) p = new BubblePlot3D(this, data);
+		else if(Dlg->GetCheck(564)) p = new Scatt3D(this, data, 0x2000);
+		if(p && p->PropertyDlg()) {
+			if(p->Id == GO_PLOT3D) delete p;
+			else if(!(bRet = Command(CMD_DROP_PLOT, p, (anyOutput *)NULL))) delete p;
+			}
+		else if(p) delete p;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	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 chart
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+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;
+	char text1[100];
+	double start_z = 1.0;
+	DlgInfo Bar3D_Dlg[] = {
+		{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, 0x0L, 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, EDTEXT, text1, 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, EDTEXT, 0L, 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*)"distances:", 20, 35, 80, 8},
+		{201, 202, 0, 0x0L, RTEXT, (void*)"start z =", 48, 45, 13, 8},
+		{202, 203, 0, 0x0L, EDVAL1, &start_z, 65, 45, 25, 10},
+		{203, 204, 0, 0x0L, RTEXT, (void*)"step =", 48, 57, 13, 8},
+		{204, 0, 0, 0x0L, EDVAL1, &dspm.fz, 65, 57, 25, 10},
+		{300, 301, 0, CHECKED, RADIO1, (void*)" common color for columns:", 15, 35, 80, 9},
+		{301, 302, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)defcol, 110, 35, 20, 10},
+		{302, 303, 0, 0x0L, RADIO1, (void*)" increment color scheme:", 15, 55, 80, 9},
+		{303, 304, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[0], 25, 70, 10, 10},
+		{304, 305, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[1], 37, 70, 10, 10},
+		{305, 306, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[2], 49, 70, 10, 10},
+		{306, 307, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[3], 61, 70, 10, 10},
+		{307, 308, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[4], 73, 70, 10, 10},
+		{308, 309, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[5], 85, 70, 10, 10},
+		{309, 310, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[6], 97, 70, 10, 10},
+		{310, 0, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[7], 109, 70, 10, 10},
+		{500, 0, 0, LASTOBJ, NONE, 0L, 0, 0, 0, 0}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, ic, res, width, height, currYR=0, maxYR=0, nx=0, ny, rx, cx, ry, cy, oax;
+	char **rd;
+	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(plots) {
+		//Plots alredy defined: jump to config dialog
+		return false;
+		}
+	data->GetSize(&width, &height);
+	sprintf(text1, "a1:a%d", height);
+	if(!(rd = (char**)calloc(1, sizeof(char*))))return false;
+	Dlg = new DlgRoot(Bar3D_Dlg);
+	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);
+				}
+			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
+			//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 = defs.GetSize(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;
+						}
+					delete(rY);		rY = 0L;
+					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;
+						}
+					}
+				}
+			}
+		}
+	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;
+	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;
+	char text1[100];
+	double start_z = 1.0;
+	DlgInfo Bar3D_Dlg[] = {
+		{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, 0x0L, 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, EDTEXT, text1, 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, EDTEXT, 0L, 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*)"distances:", 20, 35, 80, 8},
+		{201, 202, 0, 0x0L, RTEXT, (void*)"start z =", 48, 45, 13, 8},
+		{202, 203, 0, 0x0L, EDVAL1, &start_z, 65, 45, 25, 10},
+		{203, 204, 0, 0x0L, RTEXT, (void*)"step =", 48, 57, 13, 8},
+		{204, 0, 0, 0x0L, EDVAL1, &dspm.fz, 65, 57, 25, 10},
+		{300, 301, 0, CHECKED, RADIO1, (void*)" common color for ribbons:", 15, 35, 80, 9},
+		{301, 302, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)defcol, 110, 35, 20, 10},
+		{302, 303, 0, 0x0L, RADIO1, (void*)" increment color scheme:", 15, 55, 80, 9},
+		{303, 304, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[0], 25, 70, 10, 10},
+		{304, 305, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[1], 37, 70, 10, 10},
+		{305, 306, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[2], 49, 70, 10, 10},
+		{306, 307, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[3], 61, 70, 10, 10},
+		{307, 308, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[4], 73, 70, 10, 10},
+		{308, 309, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[5], 85, 70, 10, 10},
+		{309, 310, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[6], 97, 70, 10, 10},
+		{310, 0, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)colarr[7], 109, 70, 10, 10},
+		{500, 0, 0, LASTOBJ, NONE, 0L, 0, 0, 0, 0}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int i, res, width, height, currYR=0, maxYR=0, nx=0, ny, oax;
+	char **rd;
+	double fz;
+	bool updateYR = true, bContinue = false, bRet = false, bUseSch;
+	AccRange *rX = 0L, *rY = 0L;
+	AxisDEF *ax;
+	Ribbon *plot;
+
+	if(plots) {
+		//Plots alredy defined: jump to config dialog
+		return false;
+		}
+	data->GetSize(&width, &height);
+	sprintf(text1, "a1:a%d", height);
+	if(!(rd = (char**)calloc(1, sizeof(char*))))return false;
+	Dlg = new DlgRoot(Bar3D_Dlg);
+	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);
+				}
+			sprintf(TmpTxt,"y-values # %d/%d", currYR+1, maxYR+1);
+			//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;
+		oax = AxisTempl3D;	AxisTempl3D = 1;	CreateAxes();
+		Dlg->GetText(101, text1);
+		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(plot = new Ribbon(this, data, fz, dspm.fz, text1, 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;
+	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, 0x0L, 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, EDTEXT, text1, 20, 35, 100, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 48, 60, 8},
+		{103, 104, 0, 0x0L, EDTEXT, text2, 20, 58, 100, 10},
+		{104, 105, 0, 0x0L, LTEXT, (void*)"range for Z Data", 10, 71, 60, 8},
+		{105, 106, 0, 0x0L, EDTEXT, 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, EDTEXT, 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, width, height, count;
+	int cx, rx, cy, ry, cz, rz, cr, rr, s_type = 0;
+	bool bRet = false;
+	double fx, fy, fz, fr;
+	Sphere **Balls;
+	AccRange *rX, *rY, *rZ, *rR;
+	Scatt3D *sc_plot;
+
+	if(!data || !parent)return false;
+	data->GetSize(&width, &height);
+	sprintf(text1, "a1:a%d", height);		sprintf(text2, "b1:b%d", height);
+	sprintf(text3, "c1:c%d", height);		sprintf(text4, "d1:d%d", height);
+	if(!(Dlg = new DlgRoot(BubDlg3D)))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:	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)) rX = new AccRange(TmpTxt);
+		if(Dlg->GetText(103, TmpTxt)) rY = new AccRange(TmpTxt);
+		if(Dlg->GetText(105, TmpTxt)) rZ = new AccRange(TmpTxt);
+		if(Dlg->GetText(151, TmpTxt)) 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) {
+				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;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Grid line properties
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+GridLine::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 21, 10, "Line"};
+	int dh = ((flags & AXIS_ANGULAR) || (flags & AXIS_RADIAL))? -20 : 0;
+	DlgInfo LineDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to LINE", 150, 10, 50, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Apply to AXIS", 150, 25, 50, 12},
+		{3, 10, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 150, 40, 50, 12},
+		{10, 0, 100, ISPARENT | CHECKED, GROUPBOX, (void*)" grid line ", 5, 10, 139, 123+dh},
+		{100, 101, 0, NOSELECT, ODBUTTON, (void*)OD_linedef, 10, 18, 130, 100},
+		{101, 102, 112, HIDDEN | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{102, 103, 115, HIDDEN | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{103, 104, 120, HIDDEN | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{104, 105, 125, HIDDEN | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{105, 0, 130, HIDDEN | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{111, 0, 0, 0x0L, RTEXT, (void*)"line to:", 15, 117, 23, 8},
+		{112, 113, 0, 0x0L, CHECKBOX, (void*)"left", 41, 117, 25, 8},
+		{113, 114, 0, 0x0L, CHECKBOX, (void*)"right", 105, 117, 25, 8},
+		{114, 111, 0, 0x0L, CHECKBOX, (void*)"y-axis", 70, 117, 25, 8},
+		{115, 116, 0, 0x0L, CHECKBOX, (void*)"top", 41, 117, 25, 8},
+		{116, 117, 0, 0x0L, CHECKBOX, (void*)"bottom", 105, 117, 25, 8},
+		{117, 111, 0, 0x0L, CHECKBOX, (void*)"x-axis", 70, 117, 25, 8},
+		{120, 121, 0, 0x0L, CHECKBOX, (void*) "x-axis", 55, 117, 25, 8},
+		{121, 135, 0, 0x0L, CHECKBOX, (void*) "y-axis", 90, 117, 25, 8},
+		{125, 126, 0, 0x0L, CHECKBOX, (void*) "x-axis", 55, 117, 25, 8},
+		{126, 135, 0, 0x0L, CHECKBOX, (void*) "z-axis", 90, 117, 25, 8},
+		{130, 131, 0, 0x0L, CHECKBOX, (void*) "y-axis", 55, 117, 25, 8},
+		{131, 135, 0, 0x0L, CHECKBOX, (void*) "z-axis", 90, 117, 25, 8},
+		{135, 0, 0, LASTOBJ, LTEXT, (void*)"parallel to:", 15, 117, 55, 8}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, tmptype;
+	bool bRet = false;
+	DWORD undo_flags = 0L;
+	LineDEF newLine;
+
+	if(!parent || !parent->parent) return false;
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
+	if(!(Dlg = new DlgRoot(LineDlg)))return false;
+	if ((flags & AXIS_ANGULAR) || (flags & AXIS_RADIAL)) {
+		//no checkboxes
+		}
+	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, 300 + 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
+			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;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Tick properties
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Tick::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 25, 10, "Tick"};
+	TabSHEET tab2 = {64, 90, 10, "Edit"};
+	TabSHEET tab3 = {25, 64, 10, "Direction"};
+	double tick_rot;
+	DlgInfo TickDlg[] = {
+		{1, 2, 0, 0x0L, PUSHBUTTON, (void*)"Apply to TICK", 100, 10, 50, 12},
+		{2, 3, 0, DEFAULT, PUSHBUTTON, (void*)"Apply to AXIS", 100, 25, 50, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 100, 40, 50, 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, 300, ISPARENT, SHEET, &tab3, 5, 10, 90, 100},
+		{7, 0, 200, ISPARENT, SHEET, &tab2, 5, 10, 90, 100},
+		{100, 101, 0, 0x0L, RTEXT, (void*)"tick size", 10, 30, 35, 8},
+		{101, 102, 0, 0x0L, EDVAL1, &size, 50, 30, 25, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 77, 30, 20, 8},
+		{103, 104, 0, 0x0L, CHECKBOX, (void*)"major tick", 18, 45, 30, 8},
+		{104, 105, 0, 0x0L, LTEXT, (void*)"type:", 18, 60, 20, 8},
+		{105, 106, 0, 0x0L, RADIO1, 0L, 40, 70, 35, 8},
+		{106, 107, 0, 0x0L, RADIO1, 0L, 40, 60, 35, 8},
+		{107, 108, 0, 0x0L, RADIO1, (void*)"symetric", 40, 80, 35, 8},
+		{108, 0, 0, 0x0L, CHECKBOX, (void*)"draw grid line(s)", 18, 95, 30, 8},
+		{200, 201, 0, 0x0L, RTEXT, (void*)"value:", 10, 35, 23, 8},
+		{201, 202, 0, 0x0L, EDVAL1, &value, 40, 35, 35, 10},
+		{202, 203, 0, 0x0L, LTEXT, (void*)"label:", 15, 50, 20, 8},
+		{203, 0, 0, 0x0L, EDTEXT, (void*)0L, 15, 62, 70, 10},
+		{300, 301, 0, 0x0L, LTEXT, (void*)"direction of ticks", 15, 30, 70, 8},
+		{301, 302, 0, TOUCHEXIT, RADIO1, (void*)"perpendicular to axis", 15, 42, 70, 8},
+		{302, 303, 0, TOUCHEXIT, RADIO1, (void*)"fixed angle", 15, 52, 70, 8},
+		{303, 304, 0, TOUCHEXIT, RADIO1, (void*)"x-axis", 15, 74, 70, 8},
+		{304, 305, 0, TOUCHEXIT, RADIO1, (void*)"y-axis", 15, 84, 70, 8},
+		{305, 306, 0, TOUCHEXIT, RADIO1, (void*)"z-axis", 15, 94, 70, 8},
+		{306, 307, 0, 0x0L, EDVAL1, &tick_rot, 28, 62, 35, 10},
+		{307, 0, 0, LASTOBJ, LTEXT, (void*)"degree", 65, 62, 20, 8}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	DWORD old_flags;
+	double new_size, old_size, new_angle, old_angle, new_value, old_value;
+	int res, tmp_type = type;
+	bool bRet = false;
+	DWORD new_flags = flags, undo_flags = 0;
+	char *old_label = 0L;
+	TextDEF *td;
+
+	if(!parent || parent->Id != GO_AXIS) 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);
+	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) {
+		label->Command(CMD_GETTEXT, &TmpTxt, 0L);
+		if(TmpTxt[0]) {
+			old_label = strdup(TmpTxt);
+			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(Dlg->GetValue(201, &old_value)) new_value = old_value;
+	else new_value = old_value = value;
+	hDlg = CreateDlgWnd("Tick properties", 50, 50, 315, 260, Dlg, 0x0L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 1:		case 2:
+			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);
+			Dlg->GetValue(201, &new_value);
+			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);
+		undo_flags = CheckNewFloat(&value, old_value, new_value, parent, undo_flags);
+		if(label && label->Id == GO_LABEL) {
+			td = ((Label*)label)->GetTextDef();
+			if(!Dlg->GetText(203, TmpTxt)) TmpTxt[0] = 0;
+			if(td->text && strcmp(td->text, TmpTxt)) {
+				Undo.String(this, &td->text, undo_flags);
+				undo_flags |= UNDO_CONTINUE;
+				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;
+	if(old_label) free(old_label);
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Axis properties dialog
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Axis::ssTicks()
+{
+	DlgInfo TickDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 113, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 113, 25, 45, 12},
+		{3, 4, 0, 0x0L, LTEXT, (void*)"range for major tick VALUES:", 5, 10, 100, 9},
+		{4, 5, 0, 0x0L, EDTEXT, (void*)ssMATval, 10, 20, 90, 10},
+		{5, 6, 0, 0x0L, LTEXT, (void*)"range for major tick LABELS:", 5, 32, 100, 9},
+		{6, 7, 0, 0x0L, EDTEXT, (void*)ssMATlbl, 10, 42, 90, 10},
+		{7, 8, 0, 0x0L, LTEXT, (void*)"minor tick VALUES:", 5, 54, 80, 8},
+		{8, 0, 0, LASTOBJ, EDTEXT, (void*)ssMITval, 10, 64, 90, 10}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, n, n1, n2, n3;
+	bool bRet = false, bContinue = false;
+	AccRange *rT, *rL, *rMT;
+
+	if(!data) return false;
+	n = n1 = n2 = n3 = 0;			rT = rL = rMT = 0L;
+	if(!(Dlg = new DlgRoot(TickDlg)))return false;
+	hDlg = CreateDlgWnd("choose ticks from spreadsheet", 50, 50, 330, 190, Dlg, 0x0L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:								// focus lost
+			if(bContinue) res = -1;
+			break;
+		case -1:
+			bContinue = false;
+			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) && (rT=new AccRange(TmpTxt)) && (n1=rT->CountItems())){
+				if(Dlg->GetText(6,TmpTxt) && (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) && (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))ssMATval = strdup(TmpTxt);
+			if(Dlg->GetText(6, TmpTxt))ssMATlbl = strdup(TmpTxt);
+			if(Dlg->GetText(8, TmpTxt))ssMITval = strdup(TmpTxt);
+			UpdateTicks();
+			}
+		bRet = true;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	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, COLBUTTON, (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;
+	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;
+		names[j=0] = strdup("[unchanged]");
+		for(i = 0, j = 1; i < res; i++) {
+			if(scp[i] && scp[i]->name){
+				names[j] = strdup(scp[i]->name);
+				somePlots[j++] = scp[i];
+				}
+			}
+		}
+	else {
+		names = (char**)calloc(2, sizeof(char*));
+		names[0] = strdup("n.a.");
+		}
+	OD_axisplot(OD_ACCEPT, 0L, 0L, 0L, names, 0);
+	if(!(Dlg = new DlgRoot(AxisDlg)))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 = strdup(TmpTxt);
+		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));
+	sprintf(TmpTxt, "%sxis properties", type_txt);
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 436, 384, 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);
+			sprintf(TmpTxt,"break # %d/%d", cbrk+1, nbrk+1);
+			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);
+	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(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;
+			}
+		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) && TmpTxt[0]) {
+			if(old_Label && strcmp(old_Label,TmpTxt) && axisLabel->Id == GO_LABEL){
+				lb_def = ((Label*)axisLabel)->GetTextDef();
+				Undo.String(axisLabel, &lb_def->text, undo_flags);
+				if(lb_def->text) free(lb_def->text);
+				lb_def->text = strdup(TmpTxt);		undo_flags |= UNDO_CONTINUE;
+				}
+			else if(!axisLabel) {
+				label_def.ColTxt = colAxis;				label_def.ColBg = 0x00ffffffL;
+				label_def.fSize = defs.GetSize(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
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Graph::AddPlot(int family)
+{
+	DlgInfo GraphDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 150, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 150, 25, 45, 12},
+		{3, 0, 520, ISPARENT | CHECKED, GROUPBOX, (void *)"  select template  ", 5, 10, 135, 95},
+		{520, 521, 0, TOUCHEXIT | CHECKED | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 10, 20, 25, 25},
+		{521, 522, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 35, 20, 25, 25},
+		{522, 523, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 60, 20, 25, 25},
+		{523, 524, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 85, 20, 25, 25},
+		{524, 525, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 110, 20, 25, 25},
+		{525, 526, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 10, 45, 25, 25},
+		{526, 528, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 35, 45, 25, 25},
+		{528, 529, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 60, 45, 25, 25},
+		{529, 530, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 85, 45, 25, 25},
+		{530, 531, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 110, 45, 25, 25},
+		{531, 540, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 10, 70, 25, 25},
+		{540, 541, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 35, 70, 25, 25},
+		{541, 0, 0, LASTOBJ | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 60, 70, 25, 25}};
+	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);
+	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 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);
+		else if(Dlg->GetCheck(530)) p = new FitFunc(this, data);
+		else if(Dlg->GetCheck(531)) p = new MultiLines(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;
+	return bRet;
+}
+
+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"};
+	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, 0, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{4, 5, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 157, 122},
+		{5, 6, 200, ISPARENT, SHEET, &tab2, 5, 10, 157, 122},
+		{6, 7, 300, ISPARENT, SHEET, &tab3, 5, 10, 157, 122},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"arrangement of data: select plot", 10, 25, 60, 8},
+		{101, 102, 500, TOUCHEXIT | ISPARENT, SHEET, &tab_A, 10, 37, 147, 88},
+		{102, 103, 520, TOUCHEXIT | ISPARENT | CHECKED, SHEET, &tab_B, 10, 37, 147, 90},
+		{103, 104, 540, TOUCHEXIT | ISPARENT, SHEET, &tab_C, 10, 37, 147, 88},
+		{104, 0, 560, TOUCHEXIT | ISPARENT, SHEET, &tab_D, 10, 37, 147, 88},
+		{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},
+		{300, 301, 0, 0x0L, LTEXT, (void*)"select template:", 20, 30, 60, 8},
+		{301, 400, 310, CHECKED | ISPARENT, GROUP, 0L, 0, 0, 0, 0,},
+		{310, 311, 0, TOUCHEXIT | CHECKED | ISRADIO, ODBUTTON, (void*)(OD_AxisTempl), 20, 42, 25, 25},
+		{311, 312, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_AxisTempl), 45, 42, 25, 25},
+		{312, 313, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_AxisTempl), 70, 42, 25, 25},
+		{313, 314, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_AxisTempl), 95, 42, 25, 25},
+		{314, 315, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_AxisTempl), 120, 42, 25, 25},
+		{315, 316, 0, CHECKED | TOUCHEXIT, RADIO1, (void*)"ticks outside", 12, 85, 40, 8},
+		{316, 317, 0, TOUCHEXIT, RADIO1, (void*)"ticks inside", 12, 93, 40, 8},
+		{317, 318, 0, TOUCHEXIT, RADIO1, (void*)"ticks symmetrical", 12, 101, 40, 8},
+		{318, 319, 0, TOUCHEXIT, CHECKBOX, (void*)"horizontal grid lines", 80, 85, 40, 8},
+		{319, 0, 0, TOUCHEXIT, CHECKBOX, (void*)"vertical grid lines", 80, 93, 40, 8},
+		{400, 0, 410, HIDDEN | CHECKED | ISPARENT, GROUP, 0L, 0, 0, 0, 0,},
+		{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, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_AxisTempl3D), 70, 42, 25, 25},
+		{500, 501, 0, TOUCHEXIT | CHECKED | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 25, 60, 25, 25},
+		{501, 502, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 50, 60, 25, 25},
+		{502, 503, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 75, 60, 25, 25},
+		{503, 504, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 25, 85, 25, 25},
+		{504, 505, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 50, 85, 25, 25},
+		{505, 0, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 75, 85, 25, 25},
+		{520, 521, 0, TOUCHEXIT | CHECKED | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 20, 50, 25, 25},
+		{521, 522, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 45, 50, 25, 25},
+		{522, 523, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 70, 50, 25, 25},
+		{523, 524, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 95, 50, 25, 25},
+		{524, 525, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 120, 50, 25, 25},
+		{525, 526, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 20, 75, 25, 25},
+		{526, 527, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 45, 75, 25, 25},
+		{527, 528, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 70, 75, 25, 25},
+		{528, 529, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 95, 75, 25, 25},
+		{529, 530, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 120, 75, 25, 25},
+		{530, 531, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 20, 100, 25, 25},
+		{531, 0, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 45, 100, 25, 25},
+		{540, 541, 0, TOUCHEXIT | CHECKED | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 20, 60, 25, 25},
+		{541, 542, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 45, 60, 25, 25},
+		{542, 543, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 70, 60, 25, 25},
+		{543, 544, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 95, 60, 25, 25},
+		{544, 0, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 120, 60, 25, 25},
+		{560, 561, 0, TOUCHEXIT | CHECKED | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 20, 60, 25, 25},
+		{561, 562, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 45, 60, 25, 25},
+		{562, 563, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 70, 60, 25, 25},
+		{563, 564, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 95, 60, 25, 25},
+		{564, 0, 0, LASTOBJ | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 120, 60, 25, 25}};
+	DlgRoot *Dlg;
+	GraphObj *p;
+	void *hDlg;
+	int i, res, selPlt = 520, selAxis = 310;
+	bool bRet, bContinue;
+	fRECT rc1, rc2;
+
+	ODtickstyle = tickstyle;
+	AxisTempl = 0;
+	if(!parent) return false;
+	Dlg = new DlgRoot(GraphDlg);
+	Dlg->SetCheck(410 + AxisTempl3D, 0L, true);
+	if(parent->Id != GO_PAGE) {
+		Dlg->Activate(202, false);	Dlg->Activate(204, false);
+		}
+	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:
+			for(i = 500; i <= 505; i++) if(Dlg->GetCheck(i))selPlt = i;
+			Dlg->ShowItem(301, true);	Dlg->ShowItem(400, false);
+			res = -1;
+			break;
+		case 102:
+			for(i = 520; i <= 531; i++) if(Dlg->GetCheck(i))selPlt = i;
+			Dlg->ShowItem(301, true);	Dlg->ShowItem(400, false);
+			res = -1;
+			break;
+		case 103:
+			for(i = 540; i <= 544; i++) if(Dlg->GetCheck(i))selPlt = i;
+			Dlg->ShowItem(301, true);	Dlg->ShowItem(400, false);
+			res = -1;
+			break;
+		case 104:
+			for(i = 560; i <= 564; i++) if(Dlg->GetCheck(i))selPlt = i;
+			Dlg->ShowItem(301, false);	Dlg->ShowItem(400, true);
+			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 520:	case 521:	case 522:	case 523:
+		case 524:	case 525:	case 526:	case 527:
+		case 528:	case 529:	case 530:	case 531:
+		case 540:	case 541:	case 542:	case 543:
+		case 544:
+		case 560:	case 561:	case 562:	case 563:
+		case 564:
+			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);
+				else if(Dlg->GetCheck(505))p = new FreqDist(this, data);
+				}
+			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);
+				else if(Dlg->GetCheck(530)) p = new FitFunc(this, data);
+				else if(Dlg->GetCheck(531)) p = new MultiLines(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);
+				}
+			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);
+	if(!bRet) parent->Command(CMD_DELOBJ, this, NULL);
+	else Command(CMD_SET_DATAOBJ, (void*)data, 0L);
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	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, COLBUTTON, (void *)ColGR, 74, 42, 25, 10},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"outline color", 15, 54, 58, 8},
+		{104, 105, 0, TOUCHEXIT | OWNDIALOG, COLBUTTON, (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, COLBUTTON, (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, COLBUTTON, (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;
+	bool bRet = false, bContinue = false;
+	fRECT o_gr, n_gr, o_dr, n_dr;
+
+	if(!(Dlg = new DlgRoot(GraphDlg)))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);
+	sprintf(TmpTxt, "%s properties", name ? name : "Graph");
+	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 450, 300, Dlg, 0x0L);
+	do{
+		LoopDlgWnd();
+		res = ExecDrawOrderButt(parent, this, Dlg->GetResult());
+		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);
+			res = -1;
+			break;
+		case 6:
+			Command(CMD_LAYERS, 0L, 0L);
+			bContinue = true;
+			res = -1;
+			break;
+			}
+		}while(res <0);
+	if(res == 1 && bRet) {
+		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]->SetColor(COL_AXIS | UNDO_STORESET, ColAX);
+			}
+		}
+	else if(res == 2) {
+		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;
+}
+
+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 = defs.GetSize(SIZE_AXIS_LINE);
+	DWORD colAxis = ColAX;
+	DlgInfo NewAxisDlg[] = {
+		{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, 50, TOUCHEXIT | ISPARENT, SHEET, &tab1, 5, 10, 130, 130},
+		{5, 6, 200, ISPARENT | CHECKED, SHEET, &tab2, 5, 10, 130, 130},
+		{6, 0, 300, ISPARENT, SHEET, &tab3, 5, 10, 130, 130},
+		{50, 51, 100, ISPARENT | CHECKED, GROUPBOX, (void*)" scaling ", 10, 30, 120, 36},
+		{51, 120, 0, 0x0L, 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, &y_axis.min, 48, 51, 32, 10},
+		{102, 103, 0, 0x0L, CTEXT, (void*)"to", 81, 51, 11, 8},
+		{103, 0, 0, 0x0L, EDVAL1, &y_axis.max, 93, 51, 32, 10},
+		{120, 0, 121, ISPARENT | CHECKED, GROUPBOX, (void*)" line ", 10, 72, 120, 20},
+		{121, 122, 0, 0x0L, RTEXT, (void*)"width", 10, 77, 25, 8},
+		{122, 123, 0, 0x0L, EDVAL1, &sizAxLine, 37, 77, 25, 10},
+		{123, 124, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 63, 77, 10, 8},
+		{124, 125, 0, 0x0L, RTEXT, (void*)"color", 73, 77, 25, 8},
+		{125, 130, 0, OWNDIALOG, COLBUTTON, (void *)colAxis, 100, 77, 25, 10},
+		{130, 131, 0, ISPARENT | CHECKED, GROUPBOX, (void*)" axis label ", 10, 98, 120, 20},
+		{131, 0, 0, 0x0L, EDTEXT, (void*)0L, 15, 103, 110, 10},
+		{200, 201, 0, 0x0L, LTEXT, (void*)"select a template:", 10, 30, 70, 9},
+		{201, 202, 0, CHECKED | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_NewAxisTempl), 20, 42, 25, 25},
+		{202, 203, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_NewAxisTempl), 45, 42, 25, 25},
+		{203, 204, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_NewAxisTempl), 70, 42, 25, 25},
+		{204, 205, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_NewAxisTempl), 95, 42, 25, 25},
+		{205, 206, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_NewAxisTempl), 20, 67, 25, 25},
+		{206, 207, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_NewAxisTempl), 45, 67, 25, 25},
+		{207, 208, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_NewAxisTempl), 70, 67, 25, 25},
+		{208, 210, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_NewAxisTempl), 95, 67, 25, 25},
+		{210, 0, 220, ISPARENT | CHECKED, GROUPBOX, (void*)" placement ", 10, 97, 120, 35},
+		{220, 221, 0, 0x0L, RTEXT, (void*)"x", 10, 105, 15, 8},
+		{221, 222, 0, 0x0L, EDVAL1, &axis.loc[0].fx, 27, 105, 35, 10},
+		{222, 223, 0, 0x0L, LTEXT, (void*)"-", 65, 105, 5, 8},
+		{223, 224, 0, 0x0L, EDVAL1, &axis.loc[1].fx, 71, 105, 35, 10},
+		{224, 225, 0, 0x0L, LTEXT, (void*)Units[units].display, 109, 105, 15, 8},
+		{225, 226, 0, 0x0L, RTEXT, (void*)"y", 10, 117, 15, 8},
+		{226, 227, 0, 0x0L, EDVAL1, &axis.loc[0].fy, 27, 117, 35, 10},
+		{227, 228, 0, 0x0L, LTEXT, (void*)"-", 65, 117, 5, 8},
+		{228, 229, 0, 0x0L, EDVAL1, &axis.loc[1].fy, 71, 117, 35, 10},
+		{229, 0, 0, 0x0L, LTEXT, (void*)Units[units].display, 109, 117, 15, 8},
+		{300, 0, 0, NOSELECT, ODBUTTON, (void*)OD_axisplot, 15, 30, 110, 140},
+		{400, 0, 0, LASTOBJ, NONE, 0L, 0, 0, 0, 0}};
+	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;
+	Axis *the_new, **tmpAxes;
+	bool bAxis = false, bRet = false;
+	char **names;
+	GraphObj **somePlots;
+	Label *label;
+
+	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*));
+	names[j=0] = strdup("[none]");
+	for(i = 0, j = 1; i < nscp; i++) {
+		if(Sc_Plots[i] && Sc_Plots[i]->name){
+			names[j] = strdup(Sc_Plots[i]->name);
+			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)))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, 314, 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) {
+				WriteFloatToBuff(TmpTxt, hx1);		Dlg->SetText(221, TmpTxt+1);
+				WriteFloatToBuff(TmpTxt, hx2);		Dlg->SetText(223, TmpTxt+1);
+				WriteFloatToBuff(TmpTxt, hy1);		Dlg->SetText(226, TmpTxt+1);
+				WriteFloatToBuff(TmpTxt, hy2);		Dlg->SetText(228, TmpTxt+1);
+				if(! bAxis) {
+					WriteFloatToBuff(TmpTxt, x_axis.min);	Dlg->SetText(101, TmpTxt+1);
+					WriteFloatToBuff(TmpTxt, x_axis.max);	Dlg->SetText(103, TmpTxt+1);
+					}
+				}
+			else {
+				WriteFloatToBuff(TmpTxt, vx1);		Dlg->SetText(221, TmpTxt+1);
+				WriteFloatToBuff(TmpTxt, vx2);		Dlg->SetText(223, TmpTxt+1);
+				WriteFloatToBuff(TmpTxt, vy1);		Dlg->SetText(226, TmpTxt+1);
+				WriteFloatToBuff(TmpTxt, vy2);		Dlg->SetText(228, TmpTxt+1);
+				if(! bAxis) {
+					WriteFloatToBuff(TmpTxt, y_axis.min);	Dlg->SetText(101, TmpTxt+1);
+					WriteFloatToBuff(TmpTxt, y_axis.max);	Dlg->SetText(103, TmpTxt+1);
+					}
+				}
+			currTempl = res;
+			Dlg->DoPlot(0L);
+			res = -1;
+			break;
+			}
+		}while (res < 0);
+	if(res == 1) {
+		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(defs.GetSize(SIZE_AXIS_TICKS));
+		tlb_dx = tlb_dy = 0.0;
+		tlbdef.ColTxt = colAxis;				tlbdef.ColBg = 0x00ffffffL;
+		tlbdef.RotBL = tlbdef.RotCHAR = 0.0f;	tlbdef.fSize = defs.GetSize(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 = defs.GetSize(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)) 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;
+	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;
+	bool bRet = false, bContinue = false;
+
+	FindPaper(GRect.Xmax - GRect.Xmin, GRect.Ymax -GRect.Ymin, .0001);
+	if(!(Dlg = new DlgRoot(PageDlg)))return false;
+	sprintf(TmpTxt, "%s properties", name ? name : "Page");
+	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, 25, 10, "Line"};
+	TabSHEET tab2 = {25, 60, 10, "Shapes"};
+	TabSHEET tab3 = {60, 93, 10, "Dialogs"};
+	TabSHEET tab4 = {93, 128, 10, "Internat."};
+	double ts =  dlgtxtheight;
+	DlgInfo DefsDlg[] = {
+		{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, 0, 0, 0, 0},
+		{4, 5, 100, TOUCHEXIT | ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 139, 120},
+		{5, 6, 200, TOUCHEXIT | ISPARENT, SHEET, &tab2, 5, 10, 139, 120},
+		{6, 7, 300, ISPARENT, SHEET, &tab3, 5, 10, 139, 120},
+		{7, 0, 400, ISPARENT, SHEET, &tab4, 5, 10, 139, 120},
+		{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},
+		{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 res, tmpUnits = cUnits;
+	bool bRet = false;
+	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);
+	Dlg = new DlgRoot(DefsDlg);
+	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;
+		}
+	hDlg = CreateDlgWnd("Edit Global Preferences", 50, 50, 410, 300, Dlg, 0x0L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 4:	case 5:
+			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))	DecPoint[0] = TmpTxt[0];
+		if(Dlg->GetText(404, TmpTxt))	ColSep[0] = TmpTxt[0];
+		bRet = true;
+		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);
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
diff --git a/QT_Spec.cpp b/QT_Spec.cpp
new file mode 100755
index 0000000..9d46d8d
--- /dev/null
+++ b/QT_Spec.cpp
@@ -0,0 +1,2667 @@
+//QT_Spec.cpp, Copyright (c) 2001, 2002, 2003, 2004, 2005 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 <qmessagebox.h>
+#include <qpixmap.h>
+#include <qapplication.h>
+#include <qnamespace.h>
+#include <qfiledialog.h>
+#include <qpaintdevicemetrics.h>
+#include <qimage.h>
+#include <qcursor.h>
+#include <qcstring.h>
+#include <qclipboard.h>
+#include <qbuffer.h>
+#include <qbitmap.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include "QT_Spec.h"
+
+extern tag_Units Units[];
+extern GraphObj *CurrGO;			//Selected Graphic Objects
+extern Graph *CurrGraph;
+extern char *WWWbrowser;
+extern char *LoadFile;				//command line argument
+extern char TmpTxt[];
+extern Default defs;
+extern UndoObj Undo;
+
+QApplication *QAppl;
+QWidget *MainWidget =0L;
+POINT CurrWidgetPos = {0,0};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Menu tem identifiers: synchronize this table with rlplot.rc for
+//    compatibility with windows
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+#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_REBOOT      520
+#define CM_SHUTDOWN    521
+#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_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
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Exute a file open dialog for the different situations
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+static char UserFileName[600];
+// Get a new file name to store data in
+char *SaveDataAsName(char *oldname)
+{
+	QString fileName = QFileDialog::getSaveFileName(oldname?oldname:defs.currPath,
+		"RLPlot workbook (*.rlw)\ndata files (*.csv)\ntab separated (*.tsv)\nXML (*.xml)", QAppl->focusWidget());
+	if(!fileName.isEmpty()){
+		strcpy(UserFileName, fileName);
+		defs.FileHistory(UserFileName);
+		return UserFileName;
+		}
+	return 0L;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Get a new file name to store graph
+char *SaveGraphAsName(char *oldname)
+{
+	QString fileName = QFileDialog::getSaveFileName(oldname?oldname:defs.currPath,
+		"RLPlot files (*.rlp)", QAppl->focusWidget());
+	if(!fileName.isEmpty()){
+		strcpy(UserFileName, fileName);
+		defs.FileHistory(UserFileName);
+		return UserFileName;
+		}
+	return 0L;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Get file name to read graph
+char *OpenGraphName(char *oldname)
+{
+	QString fileName = QFileDialog::getOpenFileName(oldname?oldname:defs.currPath,
+		"RLPlot files (*.rlp)", QAppl->focusWidget());
+	if(!fileName.isEmpty()){
+		strcpy(UserFileName, fileName);
+		defs.FileHistory(UserFileName);
+		return UserFileName;
+		}
+	return 0L;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Get a file name to load data
+char *OpenDataName(char *oldname)
+{
+	QString fileName = QFileDialog::getOpenFileName(oldname?oldname:defs.currPath,
+		"RLPlot workbook (*.rlw)\ndata files (*.csv)\ntab separated file (*.tsv)\n"
+		"RLPlot files (*.rlp)\nall files (*.*)", QAppl->focusWidget());
+	if(!fileName.isEmpty()){
+		strcpy(UserFileName, fileName);
+		defs.FileHistory(UserFileName);
+		return UserFileName;
+		}
+	return 0L;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Get a file name to export graph
+void OpenExportName(GraphObj *g, char *oldname)
+{
+	int i;
+	PrintQT *out=0L;
+
+	if (!g) return;
+	QString fileName = QFileDialog::getSaveFileName(oldname?oldname:defs.currPath,
+		"Scalable Vector Graphics (*.svg)\nEncapsulated Post Script (*.eps)\n"
+		"MSWindows MetaFile (*.wmf)\nTag Image File Format(*.tif *.tiff)", 
+		QAppl->focusWidget());
+	if(!fileName.isEmpty()){
+		strcpy(UserFileName, fileName);
+		i = strlen(UserFileName);
+		g->Command(CMD_BUSY, 0L, 0L);
+		if(0==strcasecmp(".svg", UserFileName+i-4)) {
+			DoExportSvg(g, UserFileName, 0L);
+			}
+		else if(0==strcasecmp(".wmf", UserFileName+i-4)) {
+			DoExportWmf(g, UserFileName, 600.0f, 0L);
+			}
+		else if(0==strcasecmp(".eps", UserFileName+i-4)) {
+			DoExportEps(g, UserFileName, 0L);
+			}
+		else if(0==strcasecmp(".tif", UserFileName+i-4)) {
+			DoExportTif(g, UserFileName, 0L);
+			}
+		else if(0==strcasecmp(".tiff", UserFileName+i-5)) {
+			DoExportTif(g, UserFileName, 0L);
+			}
+		else ErrorBox("Unknown file extension or format");
+		g->Command(CMD_MOUSECURSOR, 0L, 0L);
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Common alert boxes
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void InfoBox(char *Msg)
+{
+	QMessageBox::information(QAppl->focusWidget(), "Info", Msg);
+}
+
+void ErrorBox(char *Msg)
+{
+	QMessageBox::critical(QAppl->focusWidget(), "ERROR", Msg);
+}
+
+bool YesNoBox(char *Msg)
+{
+	if(QMessageBox::information(QAppl->focusWidget(), "RLPlot", Msg,
+		"&Yes", "&No", 0L, 0, -1)) return false;
+	return true;
+}
+
+void Qt_Box()
+{
+	QMessageBox::aboutQt(QAppl->focusWidget(), "RLPlot uses Qt");
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Display blinking text cursor
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+anyOutput *oTxtCur = 0L;
+RECT rTxtCur, rCopyMark;
+bool bTxtCur = false;
+DWORD coTxtCur = 0x0L;
+TxtCurBlink *cTxtCur = 0L;
+POINT ptTxtCurLine[2];
+
+void HideTextCursor()
+{
+	if(oTxtCur) {
+		bTxtCur = false;
+		oTxtCur->UpdateRect(&rTxtCur, false);
+		}
+	oTxtCur = 0L;
+}
+
+void HideTextCursorObj(anyOutput *out)
+{
+	if(oTxtCur && oTxtCur == out) HideTextCursor();
+}
+
+void ShowTextCursor(anyOutput *out, RECT *disp, DWORD color)
+{
+	coTxtCur = color;
+	HideTextCursor();
+	oTxtCur = out;
+	memcpy(&rTxtCur, disp, sizeof(RECT));
+	ptTxtCurLine[0].x = rTxtCur.left;	ptTxtCurLine[0].y = rTxtCur.top;
+	ptTxtCurLine[1].x = rTxtCur.right;	ptTxtCurLine[1].y = rTxtCur.bottom;
+	rTxtCur.bottom++;		rTxtCur.right++;
+	bTxtCur = true;
+	if(cTxtCur) cTxtCur->Show();
+}
+
+void InitTextCursor(bool init)
+{
+	if(init && !cTxtCur) cTxtCur = new TxtCurBlink();
+	else if(!init && cTxtCur) {
+		delete cTxtCur;
+		cTxtCur = 0L;
+		}
+}
+
+void HideCopyMark()
+{
+	if(cTxtCur && cTxtCur->bmCopyMark && cTxtCur->oCopyMark) {
+		cTxtCur->oCopyMark->UpdateRect(&rCopyMark, false);
+		delete cTxtCur->bmCopyMark;
+		}
+	cTxtCur->bmCopyMark = 0L;	cTxtCur->oCopyMark = 0L;
+}
+
+void ShowCopyMark(anyOutput *out, RECT *mrk, int nRec)
+{
+	int i;
+
+	if(!out || !mrk || !nRec || !cTxtCur) return;
+	cTxtCur->oCopyMark = out;
+	rCopyMark.left = mrk[0].left;	rCopyMark.right = mrk[0].right;
+	rCopyMark.top = mrk[0].top;		rCopyMark.bottom = mrk[0].bottom;
+	for(i = 1; i < nRec; i++) {
+		UpdateMinMaxRect(&rCopyMark, mrk[i].left, mrk[i].top);
+		UpdateMinMaxRect(&rCopyMark, mrk[i].right, mrk[i].bottom);
+		}
+	cTxtCur->bmCopyMark = new BitMapQT(rCopyMark.right - rCopyMark.left, 
+		rCopyMark.bottom - rCopyMark.top, out->hres, out->vres);
+}
+
+LineDEF liCopyMark1 = {0.0f, 1.0f, 0x00ffffffL, 0L};
+LineDEF liCopyMark2 = {0.0f, 6.0f, 0x0L, 0xf0f0f0f0L};
+
+TxtCurBlink::TxtCurBlink():QObject(MainWidget, 0)
+{
+	isVis = false;
+	oCopyMark = 0L;		bmCopyMark = 0L;
+	count = cp_mark = 0;
+	startTimer(60);
+}
+
+void
+TxtCurBlink::Show()
+{
+	count = -4;
+	isVis = true;
+	if(bTxtCur && oTxtCur)oTxtCur->ShowLine(ptTxtCurLine, 2, coTxtCur);
+
+}
+
+void
+TxtCurBlink::showCopyMark()
+{
+	if(bmCopyMark && oCopyMark) {
+		bmCopyMark->CopyBitmap(0, 0, oCopyMark, rCopyMark.left, rCopyMark.top, 
+			rCopyMark.right - rCopyMark.left, rCopyMark.bottom - rCopyMark.top, false);
+		bmCopyMark->SetLine(&liCopyMark1);
+		line[0].x = line[1].x = line[4].x = 0;
+		line[0].y = line[3].y = line[4].y = 0;
+		line[1].y = line[2].y = rCopyMark.bottom-rCopyMark.top-1;
+		line[2].x = line[3].x = rCopyMark.right-rCopyMark.left-1;
+		bmCopyMark->oPolyline(line, 5);
+		bmCopyMark->SetLine(&liCopyMark2);
+		bmCopyMark->RLP.finc = 1.0;		bmCopyMark->RLP.fp = (cp_mark & 0x7);
+		bmCopyMark->oPolyline(line, 5);
+		oCopyMark->ShowBitmap(rCopyMark.left, rCopyMark.top, bmCopyMark);
+		cp_mark++;
+		if(isVis && oTxtCur && ptTxtCurLine[0].y != ptTxtCurLine[1].y &&
+			oTxtCur == oCopyMark && OverlapRect(&rCopyMark, &rTxtCur)){
+			oTxtCur->ShowLine(ptTxtCurLine, 2, coTxtCur);
+			}
+		}
+}
+
+void
+TxtCurBlink::timerEvent(QTimerEvent *ev)
+{
+	showCopyMark();
+	if(!oTxtCur || (ptTxtCurLine[0].x == ptTxtCurLine[1].x &&
+		ptTxtCurLine[0].y == ptTxtCurLine[1].y)) return;
+	count++;
+	if(count<0) oTxtCur->ShowLine(ptTxtCurLine, 2, coTxtCur);
+	if(count < 8) return;
+	count = 0;
+	if(bTxtCur && oTxtCur) {
+		if(isVis) {
+			oTxtCur->UpdateRect(&rTxtCur, false);
+			isVis = false;
+			}
+		else {
+			oTxtCur->ShowLine(ptTxtCurLine, 2, coTxtCur);
+			isVis = true;
+			}
+		}
+}
+
+#define MIME_KSPREAD "application/x-kspread-snippet"
+#define MIME_RLPLOT "application/x-rlplot-snippet"
+#define MIME_RLPGRAPH "application/x-rlplot-graph"
+#define MIME_RLPOBJ "application/x-rlplot-object"
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Clipboard support
+// based on KDEs KSpread source: kspread_table.cc
+// copyright (C) 1998, 1999 Torben Weis
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+KSpreadTextDrag::KSpreadTextDrag(QWidget *dragSource, const char *name )
+	:QTextDrag(dragSource, name)
+{
+	go = 0L;
+}
+
+KSpreadTextDrag::~KSpreadTextDrag()
+{
+}
+
+bool
+KSpreadTextDrag::provides(const char *mimeType)
+{
+	if(0 == strcmp(mimeType, "text/plain")) return true;
+	if(0 == strcmp(mimeType, MIME_RLPLOT)) return true;
+	if(0 == strcmp(mimeType, selectionMimeType())) return true;
+	return false;
+}
+
+QByteArray
+KSpreadTextDrag::encodedData(const char *mime) const
+{
+	static QByteArray b;
+	char *ptr = 0L;
+	long cb;
+
+	if(!go || go->Id != GO_SPREADDATA) return 0L;
+	if(0 == strcmp(mime, MIME_RLPLOT)) {
+		go->Command(CMD_COPY_XML, &ptr, 0L);
+		}
+	else if(0 == strcmp(mime, "text/plain")) {
+		go->Command(CMD_COPY_TSV, &ptr, 0L);
+		}
+	else if(0 == strcmp(mime, selectionMimeType())){
+		go->Command(CMD_COPY_XML, &ptr, 0L);
+		}
+	else return 0L;
+	if(!ptr) ptr = (char*)calloc(cb = 4, 1);
+	else cb = strlen(ptr)+1;
+	if(ptr && b.resize(cb)) {
+		memcpy(b.data(), ptr, cb);
+		free (ptr);
+		return (b);
+		}
+	return 0L;
+}
+
+bool
+KSpreadTextDrag::canDecode(QMimeSource *e)
+{
+	if(e->provides(selectionMimeType())) return true;
+	return false;
+}
+
+const char *
+KSpreadTextDrag::format(int i) const
+{
+	static const char *fmt;
+
+	switch(i) {
+	case 0:	case 1:
+		fmt = "text/plain";
+		return fmt;
+	case 2:
+	case 3:	case 4:
+		return selectionMimeType();
+	default:
+		return 0L;
+		}
+}
+
+const char *
+KSpreadTextDrag::selectionMimeType()
+{
+	return(MIME_KSPREAD);
+}
+
+RLPGraphDrag::RLPGraphDrag(QWidget *dragSource, const char *name)
+	:QTextDrag(dragSource, name)
+{
+	go1 = go2 = 0L;
+}
+
+RLPGraphDrag::~RLPGraphDrag()
+{
+}
+
+bool
+RLPGraphDrag::provides(const char *mimeType)
+{
+	if(go1 && 0 == strcmp(mimeType, MIME_RLPGRAPH)) return true;
+	if(go2 && 0 == strcmp(mimeType, MIME_RLPOBJ)) return true;
+	return false;
+}
+
+void
+RLPGraphDrag::setGraphData(GraphObj *g)
+{
+	go1 = g;
+	if(CurrGraph && CurrGraph != g) go2 = CurrGraph;
+	else go2 = 0L;
+}
+
+QByteArray
+RLPGraphDrag::encodedData(const char* mime) const
+{
+	static QByteArray b;
+	char *ptr = 0L;
+	long cb;
+
+	if(0 == strcmp(mime, "text/plain")) return 0L;
+	else if(go1 && 0 == strcmp(mime, MIME_RLPGRAPH)) ptr = GraphToMem(go1, &cb);
+	else if(go2 && 0 == strcmp(mime, MIME_RLPOBJ)) ptr = GraphToMem(go2, &cb);
+	else return 0L;
+	if(ptr && b.resize(cb+1)) {
+		memcpy(b.data(), ptr, cb+1);
+		free (ptr);
+		return (b);
+		}
+	return 0L;
+}
+
+const char*
+RLPGraphDrag::format(int i) const
+{
+	switch(i) {
+	case 0:		return("text/plain");
+	case 1:		return MIME_RLPGRAPH;
+	case 2:		return MIME_RLPOBJ;
+	default:	return 0L;
+		}
+}
+
+bool
+RLPGraphDrag::canDecode(QMimeSource *e)
+{
+	if(e->provides(MIME_RLPGRAPH)) return true;
+	if(e->provides(MIME_RLPOBJ)) return true;
+	return false;
+}
+
+const char*
+RLPGraphDrag::selectionMimeType()
+{
+	return(MIME_RLPGRAPH);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Process paste command: check for clipboard contents
+void TestClipboard(GraphObj *g)
+{
+
+	QClipboard *cb;
+	QMimeSource *mime;
+
+	if(!(cb = QAppl->clipboard()))return;
+	if(!(mime = cb->data()))return;
+	if(g->Id == GO_SPREADDATA) {
+		if(mime->provides(MIME_RLPLOT)){
+			QByteArray b = mime->encodedData(MIME_RLPLOT);
+			if(b) g->Command(CMD_PASTE_XML, (void*)b.data(), 0L);
+			return;
+			}
+		else if(mime->provides(MIME_KSPREAD)){
+			QByteArray b = mime->encodedData(MIME_KSPREAD);
+			if(b) g->Command(CMD_PASTE_XML, (void*)b.data(), 0L);
+			return;
+			}
+		else if(QByteArray b = mime->encodedData(MIME_RLPOBJ)){
+			OpenGraph(g, 0L, (unsigned char*)b.data());
+			return;
+			}
+		else if(QByteArray b = mime->encodedData(MIME_RLPGRAPH)){
+			OpenGraph(g, 0L, (unsigned char*)b.data());
+			return;
+			}
+		else if(mime->provides("text/plain")){
+			QString _text = cb->text();
+			ProcMemData(g, (unsigned char*)_text.latin1(), true);
+			return;
+			}
+		}
+	else if(g->Id == GO_PAGE){
+		if(QByteArray b = mime->encodedData(MIME_RLPOBJ)){
+			OpenGraph(g, 0L, (unsigned char*)b.data());
+			return;
+			}
+		else if(QByteArray b = mime->encodedData(MIME_RLPGRAPH)){
+			OpenGraph(g, 0L, (unsigned char*)b.data());
+			return;
+			}
+		}
+	else if(g->Id == GO_GRAPH)TestClipboard(g->parent);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Copy spreadsheet or graph to clipboard
+void CopyData(GraphObj *g)
+{
+	EmptyClip();
+	KSpreadTextDrag *kd = new KSpreadTextDrag(0L);
+	kd->setText(QString("RLPlot snippet"));
+	kd->setSpreadData(g);
+#ifdef Q_CHECK_PTR				//n.a. in Qt version 2
+	if(QAppl->clipboard()->supportsSelection())
+		QAppl->clipboard()->setSelectionMode(TRUE);
+#endif
+	QAppl->clipboard()->setData(kd);
+}
+
+void CopyGraph(GraphObj *g)
+{
+	EmptyClip();
+	RLPGraphDrag *gd = new RLPGraphDrag(0L);
+	gd->setText(QString("RLPlot graph"));
+	gd->setGraphData(g);
+	//no support for X11 clipboard possible
+	QAppl->clipboard()->setData(gd);
+}
+
+void EmptyClip()
+{
+	HideCopyMark();
+	QAppl->clipboard()->clear();
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Get display (desktop) size
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void GetDesktopSize(int *width, int *height)
+{
+	QWidget *d = QApplication::desktop();
+	*width = d->width();
+	*height = d->height();
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Common code for all QT output classes
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool com_TextOut(int x, int y, char *ctxt, TextDEF *TxtSet, QPainter *qP, anyOutput *o)
+{
+	int i, w, h, ix, iy;
+	QBrush oldBrush;
+	QPen oldPen, currPen;
+	QWMatrix xf, dxf;
+	QString txt(ctxt);
+
+	if(!ctxt || !ctxt[0] || !TxtSet || !qP || !o) return false;
+	if(TxtSet->Font==FONT_GREEK) {
+		txt.truncate(0);
+		for(i = 0; ctxt[i]; i++) {
+			if((ctxt[i] >= 'A' && ctxt[i] <= 'Z')) txt.append(QChar(ctxt[i] - 'A' + 0x391));
+			else if((ctxt[i] >= 'a' && ctxt[i] <= 'z')) txt.append(QChar(ctxt[i] - 'a' + 0x3B1));
+			else txt.append(QChar(ctxt[i]));
+			}
+		}
+	oldBrush = qP->brush();		dxf = qP->worldMatrix();
+	oldPen = currPen = qP->pen();
+	o->oGetTextExtent(ctxt, -1, &w, &h);
+	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_HCENTER) ix = x - w/2;
+	else if(TxtSet->Align & TXA_HRIGHT) ix = x - w;
+	else ix = x;
+	currPen.setColor(SwapRB(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));
+			qP->setPen(currPen);
+			qP->setBrush(QColor(SwapRB(TxtSet->ColBg)));
+			qP->drawRect(0, - iround(h*.8), w, h);
+			currPen.setColor(SwapRB(TxtSet->ColTxt));
+			qP->setPen(currPen);
+			}
+		if(TxtSet->Style & TXS_SUB) {
+			if((TxtSet->Align & TXA_VCENTER) == TXA_VCENTER) iy += o->un2iy(TxtSet->fSize*0.4);
+			else if((TxtSet->Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy += o->un2iy(TxtSet->fSize*0.2);
+			else if((TxtSet->Align & TXA_VTOP) == TXA_VTOP) iy += o->un2iy(TxtSet->fSize*.6);
+			}
+		else if(TxtSet->Style & TXS_SUPER) {
+			if((TxtSet->Align & TXA_VCENTER) == TXA_VCENTER) iy -= o->un2iy(TxtSet->fSize*0.4);
+			else if((TxtSet->Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy -= o->un2iy(TxtSet->fSize*0.6);
+			else if((TxtSet->Align & TXA_VTOP) == TXA_VTOP) iy += o->un2iy(TxtSet->fSize*.0);
+			}
+		qP->drawText(ix-x, iy-y, txt, -1);
+		}
+	else {
+		if(TxtSet->Mode == TXM_OPAQUE){
+			currPen.setColor(SwapRB(TxtSet->ColBg));
+			qP->setPen(currPen);
+			qP->setBrush(QColor(SwapRB(TxtSet->ColBg)));
+			qP->drawRect(ix, iy - iround(h*.8), w, h);
+			currPen.setColor(SwapRB(TxtSet->ColTxt));
+			qP->setPen(currPen);
+			}
+		if(TxtSet->Style & TXS_SUB) {
+			if((TxtSet->Align & TXA_VCENTER) == TXA_VCENTER) iy += o->un2iy(TxtSet->fSize*0.4);
+			else if((TxtSet->Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy += o->un2iy(TxtSet->fSize*0.2);
+			else if((TxtSet->Align & TXA_VTOP) == TXA_VTOP) iy += o->un2iy(TxtSet->fSize*.6);
+			}
+		else if(TxtSet->Style & TXS_SUPER) {
+			if((TxtSet->Align & TXA_VCENTER) == TXA_VCENTER) iy -= o->un2iy(TxtSet->fSize*0.4);
+			else if((TxtSet->Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy -= o->un2iy(TxtSet->fSize*0.6);
+			else if((TxtSet->Align & TXA_VTOP) == TXA_VTOP) iy += o->un2iy(TxtSet->fSize*.0);
+			}
+		qP->drawText(ix, iy, txt, -1);
+		}
+	qP->setPen(oldPen);
+	qP->setBrush(oldBrush);
+	qP->setWorldMatrix(dxf, FALSE);
+	return true;
+}
+
+bool com_SetTextSpec(TextDEF *set, TextDEF *TxtSet, anyOutput *o, QFont qF, QPainter *qP)
+{
+	bool IsModified, RetVal;
+
+	if(!set->iSize && set->fSize > 0.0) set->iSize = o->un2iy(set->fSize);
+	if(!set->iSize) return false;
+	if(TxtSet->iSize != set->iSize || TxtSet->Style != set->Style ||
+		TxtSet->RotBL != set->RotBL || TxtSet->RotCHAR != set->RotCHAR ||
+		TxtSet->Font != set->Font || TxtSet->Mode != set->Mode)
+		IsModified = true;
+	else IsModified = false;
+	RetVal = o->anyOutput::SetTextSpec(set);
+	if(IsModified) {
+		qF.setBold((TxtSet->Style & TXS_BOLD) ? true : false);
+		qF.setItalic((TxtSet->Style & TXS_ITALIC) ? true : false);
+		qF.setUnderline((TxtSet->Style &TXS_UNDERLINE) ? true : false);
+		if((TxtSet->Style & TXS_SUPER) || (TxtSet->Style & TXS_SUB))
+			 qF.setPointSize(o->un2iy(set->fSize*0.71));
+		else qF.setPointSize((TxtSet->iSize > 8) ? TxtSet->iSize : 8);
+		switch(TxtSet->Font){
+		case FONT_HELVETICA:
+		default:			qF.setFamily("Helvetica");			break;
+		case FONT_GREEK:
+		case FONT_TIMES:	qF.setFamily("Times");				break;
+		case FONT_COURIER:	qF.setFamily("Courier");			break;
+			}
+		qP->setFont(qF);
+		}
+	return RetVal;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Icon definitions
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//this icon has been taken from trolltech's Qt: qmessagebox.cpp
+static char *information_xpm[]={
+"32 32 5 1",
+". c None",
+"c c #000000",
+"* c #999999",
+"a c #ffffff",
+"b c #0000ff",
+"...........********.............",
+"........***aaaaaaaa***..........",
+"......**aaaaaaaaaaaaaa**........",
+".....*aaaaaaaaaaaaaaaaaa*.......",
+"....*aaaaaaaabbbbaaaaaaaac......",
+"...*aaaaaaaabbbbbbaaaaaaaac.....",
+"..*aaaaaaaaabbbbbbaaaaaaaaac....",
+".*aaaaaaaaaaabbbbaaaaaaaaaaac...",
+".*aaaaaaaaaaaaaaaaaaaaaaaaaac*..",
+"*aaaaaaaaaaaaaaaaaaaaaaaaaaaac*.",
+"*aaaaaaaaaabbbbbbbaaaaaaaaaaac*.",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+"*aaaaaaaaaaaabbbbbaaaaaaaaaaac**",
+".*aaaaaaaaaaabbbbbaaaaaaaaaac***",
+".*aaaaaaaaaaabbbbbaaaaaaaaaac***",
+"..*aaaaaaaaaabbbbbaaaaaaaaac***.",
+"...caaaaaaabbbbbbbbbaaaaaac****.",
+"....caaaaaaaaaaaaaaaaaaaac****..",
+".....caaaaaaaaaaaaaaaaaac****...",
+"......ccaaaaaaaaaaaaaacc****....",
+".......*cccaaaaaaaaccc*****.....",
+"........***cccaaaac*******......",
+"..........****caaac*****........",
+".............*caaac**...........",
+"...............caac**...........",
+"................cac**...........",
+".................cc**...........",
+"..................***...........",
+"...................**..........."};
+
+//this icon has been taken from trolltech's Qt: qmessagebox.cpp
+static char *critical_xpm[]={
+"32 32 4 1",
+". c None",
+"a c #999999",
+"* c #ff0000",
+"b c #ffffff",
+"...........********.............",
+".........************...........",
+".......****************.........",
+"......******************........",
+".....********************a......",
+"....**********************a.....",
+"...************************a....",
+"..*******b**********b*******a...",
+"..******bbb********bbb******a...",
+".******bbbbb******bbbbb******a..",
+".*******bbbbb****bbbbb*******a..",
+"*********bbbbb**bbbbb*********a.",
+"**********bbbbbbbbbb**********a.",
+"***********bbbbbbbb***********aa",
+"************bbbbbb************aa",
+"************bbbbbb************aa",
+"***********bbbbbbbb***********aa",
+"**********bbbbbbbbbb**********aa",
+"*********bbbbb**bbbbb*********aa",
+".*******bbbbb****bbbbb*******aa.",
+".******bbbbb******bbbbb******aa.",
+"..******bbb********bbb******aaa.",
+"..*******b**********b*******aa..",
+"...************************aaa..",
+"....**********************aaa...",
+"....a********************aaa....",
+".....a******************aaa.....",
+"......a****************aaa......",
+".......aa************aaaa.......",
+".........aa********aaaaa........",
+"...........aaaaaaaaaaa..........",
+".............aaaaaaa............"};
+
+static char *RLPlot_xpm[]={
+"32 32 12 1",
+"j c #000083",
+"i c #0000ff",
+"d c #008100",
+"e c #00ff00",
+"b c #00ffff",
+"g c #830000",
+"c c #838100",
+"a c #838183",
+". c #c5c2c5",
+"h c #ff0000",
+"f c #ffff00",
+"# c #ffffff",
+".##a###a##bbbbbbbbbbbbbbbbbbbbbb",
+"##a###a###bbbbbbbbbbbbbbbbbbbbbb",
+"#a###a#.#a.b.b.b.b.b.b.b.b.b.b.b",
+"a#.#a#.#a#bbbbbbbbbbbbbbbbbbbbbb",
+"###a##.cccbbbbbbbbbbbbbdddbbbbbb",
+".#a###a#c#bbbbbbbbbbbbbbdbbbbbbb",
+"#a#.#a#.c.b.b.b.b.b.b.b.d.b.b.b.",
+"a###a###c#bbbbbbbbbbbdddddddbbbb",
+"#.#a#.#ac#bbbbbbbbbbbdeedeedbbbb",
+".#a.#cccccccbbbbbbbbbdeedeedbbbb",
+"#a###cffcffc.b.ggg.b.deddded.b.b",
+"a###acffcffcbbbbgbbbbdeeeeedbbbb",
+"..#a.cffcffcbbbbgbbbbdeeeeedbbbb",
+"##a##cffcffcbgggggggbdeeeeedbbbb",
+"#a#.#cfcccfcbghhghhgbdeeeeedb.b.",
+"a###acfffffcbghhghhgbdeeeeedbbbb",
+"#.#a#cfffffcbghggghgbdeeeeedbbbb",
+".#a.#cfffffcbghhhhhgbdeeeeedbbbb",
+"#a###cfffffcbghhhhhg.deeeeed.b.b",
+"a#.#acfffffcbghhhhhgbdeeeeedbbbb",
+"###a#cfffffcbghhhhhgbdeeeeedbbbb",
+".#a#.cfffffcighhhhhgideeeeediiii",
+"#a.##cfffffcighhhhhgideeeeedjiij",
+"a###acccccccigggggggidddddddiiii",
+"#.#a###ijiiijiiijiiijiiijiiiijii",
+".#a#.#ijijjiijiiijiiijiiijiiiiii",
+"#a###ijjiiijiijiijjijiijiijiijii",
+"a##.iijiijijjiijjiiijjijjiijjiij",
+".##jijiijjjiijjjiijjjiijjjiijjji",
+"##jjijijiijjjjijjjjijjjjijjjjijj",
+".jjjjjijjjjjijjjjijjjijjjjjjjjij",
+"jjijjjjjjjjjjjjijjjjjjjjjjijjjjj"};
+
+//this icon has been taken from trolltech's Qt: qmessagebox.cpp
+static char *qtlogo_xpm[] = {
+/* width height ncolors chars_per_pixel */
+"50 50 17 1",
+/* colors */
+"  c #000000",
+". c #495808",
+"X c #2A3304",
+"o c #242B04",
+"O c #030401",
+"+ c #9EC011",
+"@ c #93B310",
+"# c #748E0C",
+"$ c #A2C511",
+"% c #8BA90E",
+"& c #99BA10",
+"* c #060701",
+"= c #181D02",
+"- c #212804",
+"; c #61770A",
+": c #0B0D01",
+"/ c None",
+/* pixels */
+"/$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$/",
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",
+"$$$$$$$$$$$$$$$$$$$$$$$+++$$$$$$$$$$$$$$$$$$$$$$$$",
+"$$$$$$$$$$$$$$$$$$$@;.o=::=o.;@$$$$$$$$$$$$$$$$$$$",
+"$$$$$$$$$$$$$$$$+#X*         **X#+$$$$$$$$$$$$$$$$",
+"$$$$$$$$$$$$$$$#oO*         O  **o#+$$$$$$$$$$$$$$",
+"$$$$$$$$$$$$$&.* OO              O*.&$$$$$$$$$$$$$",
+"$$$$$$$$$$$$@XOO            * OO    X&$$$$$$$$$$$$",
+"$$$$$$$$$$$@XO OO  O  **:::OOO OOO   X@$$$$$$$$$$$",
+"$$$$$$$$$$&XO      O-;#@++@%.oOO      X&$$$$$$$$$$",
+"$$$$$$$$$$.O  :  *-#+$$$$$$$$+#- : O O*.$$$$$$$$$$",
+"$$$$$$$$$#*OO  O*.&$$$$$$$$$$$$+.OOOO **#$$$$$$$$$",
+"$$$$$$$$+-OO O *;$$$$$$$$$$$&$$$$;*     o+$$$$$$$$",
+"$$$$$$$$#O*  O .+$$$$$$$$$$@X;$$$+.O    *#$$$$$$$$",
+"$$$$$$$$X*    -&$$$$$$$$$$@- :;$$$&-    OX$$$$$$$$",
+"$$$$$$$@*O  *O#$$$$$$$$$$@oOO**;$$$#    O*%$$$$$$$",
+"$$$$$$$;     -+$$$$$$$$$@o O OO ;+$$-O   *;$$$$$$$",
+"$$$$$$$.     ;$$$$$$$$$@-OO OO  X&$$;O    .$$$$$$$",
+"$$$$$$$o    *#$$$$$$$$@o  O O O-@$$$#O   *o$$$$$$$",
+"$$$$$$+=    *@$$$$$$$@o* OO   -@$$$$&:    =$$$$$$$",
+"$$$$$$+:    :+$$$$$$@-      *-@$$$$$$:    :+$$$$$$",
+"$$$$$$+:    :+$$$$$@o* O    *-@$$$$$$:    :+$$$$$$",
+"$$$$$$$=    :@$$$$@o*OOO      -@$$$$@:    =+$$$$$$",
+"$$$$$$$-    O%$$$@o* O O    O O-@$$$#*   OX$$$$$$$",
+"$$$$$$$. O *O;$$&o O*O* *O      -@$$;    O.$$$$$$$",
+"$$$$$$$;*   Oo+$$;O*O:OO--      Oo at +=    *;$$$$$$$",
+"$$$$$$$@*  O O#$$$;*OOOo@@-O     Oo;O*  **@$$$$$$$",
+"$$$$$$$$X* OOO-+$$$;O o@$$@-    O O     OX$$$$$$$$",
+"$$$$$$$$#*  * O.$$$$;X@$$$$@-O O        O#$$$$$$$$",
+"$$$$$$$$+oO O OO.+$$+&$$$$$$@-O         o+$$$$$$$$",
+"$$$$$$$$$#*    **.&$$$$$$$$$$@o      OO:#$$$$$$$$$",
+"$$$$$$$$$+.   O* O-#+$$$$$$$$+;O    OOO:@$$$$$$$$$",
+"$$$$$$$$$$&X  *O    -;#@++@#;=O    O    -@$$$$$$$$",
+"$$$$$$$$$$$&X O     O*O::::O      OO    Oo@$$$$$$$",
+"$$$$$$$$$$$$@XOO                  OO    O*X+$$$$$$",
+"$$$$$$$$$$$$$&.*       **  O      ::    *:#$$$$$$$",
+"$$$$$$$$$$$$$$$#o*OO       O    Oo#@-OOO=#$$$$$$$$",
+"$$$$$$$$$$$$$$$$+#X:* *     O**X#+$$@-*:#$$$$$$$$$",
+"$$$$$$$$$$$$$$$$$$$%;.o=::=o.#@$$$$$$@X#$$$$$$$$$$",
+"$$$$$$$$$$$$$$$$$$$$$$$$+++$$$$$$$$$$$+$$$$$$$$$$$",
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",
+"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$",
+"/$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$/"};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Bitmap class for display export etc.
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+BitMapQT::BitMapQT(GraphObj *g, QWidget *wi, int vr, int hr):anyOutput()
+{
+	int w, h;
+
+	hres = (double)hr;		vres = (double)vr;
+	image = 0L;
+	hgo = 0L;
+	if(wi) {
+		w = wi->width();		h = wi->height();
+		}
+	else {
+		GetDesktopSize(&w, &h);
+		}
+	Box1.Xmin = Box1.Ymin = 0.0;
+	Box1.Xmax = w;					Box1.Ymax = h;
+	DeskRect.left = DeskRect.top = 0;
+	GetDesktopSize(&w, &h);
+	DeskRect.right = w;				DeskRect.bottom = h;
+	mempic = new QPixmap(w, h);
+	mempic->fill(0x00ffffffL);
+	qPainter.begin(mempic);
+	qPen.setCapStyle(Qt::RoundCap);
+	qPainter.setPen(qPen);
+	qFont = qPainter.font();
+}
+
+BitMapQT::BitMapQT(int w, int h, double hr, double vr):anyOutput()
+{
+	hres = hr;		vres = vr;
+	image = 0L;
+	hgo = 0L;
+	Box1.Xmin = Box1.Ymin = 0.0;
+	Box1.Xmax = w;					Box1.Ymax = h;
+	DeskRect.right = w;				DeskRect.bottom = h;
+	DeskRect.left = DeskRect.top = 0;
+	mempic = new QPixmap(w, h);
+	mempic->fill(0x00ffffffL);
+	qPainter.begin(mempic);
+	qPen.setCapStyle(Qt::RoundCap);
+	qPainter.setPen(qPen);
+	qFont = qPainter.font();
+}
+
+BitMapQT::~BitMapQT()
+{
+	Undo.KillDisp(this);
+	if(qPainter.isActive()) qPainter.end();
+	HideTextCursorObj(this);
+	if(mempic) delete mempic;
+	if(hgo) delete hgo;
+	if(image) delete image;
+	mempic = 0L;	hgo = 0L;	image = 0L;
+}
+
+bool
+BitMapQT::SetLine(LineDEF *lDef)
+{
+	int iw;
+
+	if(lDef->width != LineWidth || lDef->width != LineWidth ||
+		lDef->pattern != dPattern || lDef->color != dLineCol) {
+		LineWidth = lDef->width;
+		iw = iround(un2ix(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);
+		}
+	return true;
+}
+
+bool
+BitMapQT::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;
+		}
+	qPainter.setBrush(QColor(SwapRB(fill->color)));
+	dFillCol = fill->color;
+	dFillCol2 = fill->color2;
+	return true;
+}
+
+bool
+BitMapQT::SetTextSpec(TextDEF *set)
+{
+	return com_SetTextSpec(set, &TxtSet, this, qFont, &qPainter);
+}
+
+bool
+BitMapQT::Erase(DWORD color)
+{
+	if(!mempic) return false;
+	mempic->fill(color);
+	if(image) delete image;
+	image = 0L;
+	return true;
+}
+
+bool
+BitMapQT::CopyBitmap(int x, int y, anyOutput* sr, int sx, int sy,
+	int sw, int sh, bool invert)
+{
+	BitMapQT *src = (BitMapQT*)sr;
+
+	if(!mempic) return false;
+	bitBlt(mempic, x, y, src->mempic, sx, sy, sw, sh,
+		invert ? Qt::NotCopyROP : Qt::CopyROP);
+	return true;
+
+
+}
+
+bool
+BitMapQT::oGetTextExtent(char *text, int cb, int *width, int *height)
+{
+	if(!text) return false;
+	QRect rc = qPainter.boundingRect(0, 0, 10000, 1000, Qt::AlignLeft | Qt::AlignTop,
+		text, cb > 0 ? cb : strlen(text));
+	*width = rc.rRight() - rc.rLeft();		*height = rc.rBottom() - rc.rTop();
+	return true;
+}
+
+bool
+BitMapQT::oGetPix(int x, int y, DWORD *col)
+{
+	DWORD pix;
+
+	if(!image && !(image = new QImage(mempic->convertToImage())))return false;
+	if(x >= DeskRect.left && x < DeskRect.right &&
+		y >= DeskRect.top && y < DeskRect.bottom){
+		pix = SwapRB(image->pixel(x, y));
+		*col = pix;
+		return true;
+		}
+	return false;
+}
+
+bool
+BitMapQT::oDrawIcon(int type, int x, int y)
+{
+	char** xpm_data;
+	QPixmap pm;
+
+	switch (type) {
+	case ICO_INFO:
+		xpm_data = information_xpm;
+		break;
+	case ICO_ERROR:
+		xpm_data = critical_xpm;
+		break;
+	case ICO_RLPLOT:
+		xpm_data = RLPlot_xpm;
+		break;
+	case ICO_QT:
+		xpm_data = qtlogo_xpm;
+		break;
+	default:
+		return false;
+		}
+	if (xpm_data) {
+		QImage image((const char **)xpm_data);
+		pm.convertFromImage(image);
+		bitBlt(mempic, x, y, &pm, 0, 0,	-1, -1, Qt::CopyROP);
+		return true;
+		}
+	return false;
+}
+
+bool
+BitMapQT::oCircle(int x1, int y1, int x2, int y2, char* nam)
+{
+	qPainter.drawEllipse(x1, y1, x2-x1, y2-y1);
+	if(hgo) return hgo->oCircle(x1, y1, x2, y2);
+	return true;
+}
+
+bool
+BitMapQT::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]);
+		}
+	else {
+		qPainter.moveTo(pts[0].x, pts[0].y);
+		for (i = 1; i < cp; i++) qPainter.lineTo(pts[i].x, pts[i].y);
+		}
+	return true;
+}
+
+bool
+BitMapQT::oRectangle(int x1, int y1, int x2, int y2, char *nam)
+{
+	qPainter.drawRect(x1, y1, x2-x1, y2-y1);
+	if(hgo) hgo->oRectangle(x1, y1, x2, y2, 0L);
+	return true;
+}
+
+bool
+BitMapQT::oSolidLine(POINT *p)
+{
+	qPainter.drawLine(p[0].x, p[0].y, p[1].x, p[1].y);
+	return true;
+}
+
+bool
+BitMapQT::oTextOut(int x, int y, char *txt, int cb)
+{
+	return com_TextOut(x, y, txt, &TxtSet, &qPainter, this);
+}
+
+bool
+BitMapQT::oPolygon(POINT *pts, int cp, char *nam)
+{
+	int i;
+
+	QPointArray *a;
+
+	if(!pts || cp <2) return false;
+	a = new QPointArray(cp);
+	if (a) {
+		for(i = 0; i < cp; i++) a->setPoint(i, pts[i].x, pts[i].y);
+		qPainter.drawPolygon(*a);
+		delete a;
+		}
+	if(hgo) hgo->oPolygon(pts, cp);
+}
+
+bool
+BitMapQT::oArc(int x1, int y1, int x2, int y2, int quads)
+{
+	int i, j;
+
+	if(x1 > x2) Swap(x1, x2);	if(y1 > y2) Swap(y1, y2);
+	switch(quads) {
+	case 1:	i = 270*16;		j = 90*16;	break;
+	case 2:	i = 180*16;		j = 180*16;	break;
+	case 3:	i = 90*16;		j = 270*16;	break;
+	case 4:	i = 0;			j = 360*16;	break;
+	default: return false;
+		}
+	qPainter.drawArc(x1, y1, x2-x1, y2-y1, i, j);
+	return true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// The display output class
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+OutputQT::OutputQT(GraphObj *g):BitMapQT(g, 0L)
+{
+	int w, h;
+	RLPwidget *rw;
+
+	HScroll = VScroll = 0L;
+	CreateNewWindow(BaseObj = g);
+	if(rw = (RLPwidget*)widget) {
+		rw->move(CurrWidgetPos.x+50, CurrWidgetPos.y+50);
+		rw->show();
+		rw->mempic = mempic;
+		rw->setBackgroundMode(QWidget::NoBackground);
+		}
+}
+
+OutputQT::OutputQT(DlgWidget *wi):BitMapQT(0L, wi)
+{
+	//assume fixed size (dialog) widget
+	widget = wi;
+	HScroll = VScroll = 0L;
+	BaseObj = 0L;
+	wi->OutputClass = this;
+	wi->mempic = mempic;
+	wi->setBackgroundMode(QWidget::NoBackground);
+	xAxis.flags = 0L;
+	yAxis.flags = AXIS_INVERT;	//drawing origin upper left corner
+}
+
+OutputQT::~OutputQT()
+{
+	if(BaseObj) {
+		BaseObj->Command(CMD_CAN_DELETE, 0L, 0L);
+		}
+	if(qPainter.isActive()) qPainter.end();
+	if(widget)	delete widget;	widget = 0L;
+	HideTextCursorObj(this);
+	if(mempic) delete mempic;	mempic = 0L;
+	if(hgo) delete hgo;			hgo = 0L;
+	if(image) delete image;		image = 0L;
+}
+
+bool
+OutputQT::ActualSize(RECT *rc)
+{
+	if(rc) {
+		rc->left = rc->top = 0;
+		rc->bottom = widget->height() - MenuHeight-6;
+		rc->right = widget->width();
+		return true;
+		}
+	return false;
+}
+
+void
+OutputQT::Caption(char *txt)
+{
+	QString cap(txt);
+	widget->setCaption(cap);
+}
+
+unsigned char hand_bits[] =	{	//hand cursor bitmap
+	0x80, 0x01, 0x58, 0x0e, 0x64, 0x12, 0x64, 0x52,
+	0x48, 0xb2, 0x48, 0x92, 0x16, 0x90, 0x19, 0x80,
+	0x11, 0x40, 0x02, 0x40, 0x02, 0x40, 0x04, 0x20,
+	0x08, 0x20, 0x10, 0x10, 0x20, 0x10, 0x20, 0x10};
+
+unsigned char hand_mask[] =	{	//hand cursor mask
+	0x80, 0x01, 0xd8, 0x0f, 0xfc, 0x1f, 0xfc, 0x5f,
+	0xf8, 0xbf, 0xf8, 0xff, 0xfe, 0xff, 0xff, 0xff,
+	0xff, 0x7f, 0xfe, 0x7f, 0xfe, 0x7f, 0xfc, 0x3f,
+	0xf8, 0x3f, 0xf0, 0x1f, 0xe0, 0x1f, 0xe0, 0x1f};
+
+unsigned char zoom_bits[] =	{	//zoom cursor bitmap
+	0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0x60, 0x0a,
+	0x10, 0x10, 0x08, 0x21, 0x08, 0x21, 0x04, 0x40,
+	0x64, 0x4c, 0x04, 0x40, 0x08, 0x21, 0x08, 0x21,
+	0x10, 0x10, 0x60, 0x0a, 0x80, 0x03, 0x00, 0x00};
+
+unsigned char zoom_mask[] =	{	//zoom cursor mask
+	0x00, 0x00, 0x00, 0x00, 0x80, 0x03, 0xe0, 0x0f,
+	0xf0, 0x1f, 0xf8, 0x3f, 0xf8, 0x3f, 0xf4, 0x7e,
+	0x7c, 0x7c, 0xfc, 0x7e, 0xf8, 0x3f, 0xf8, 0x3f,
+	0xf0, 0x1f, 0xe0, 0x0f, 0x80, 0x03, 0x00, 0x00};
+
+void
+OutputQT::MouseCursor(int cid, bool force)
+{
+	if(cid == cCursor && !force) return;
+	if(cid == MC_LAST) cid = cCursor;
+	QBitmap cb(16, 16, hand_bits, TRUE);	QBitmap cm(16, 16, hand_mask, TRUE);
+	QBitmap zb(16, 16, zoom_bits, TRUE);	QBitmap zm(16, 16, zoom_mask, TRUE);
+	switch(cid) {
+#ifdef Q_CHECK_PTR				//Qt version 3
+	case MC_ARROW:	widget->setCursor(QCursor(Qt::ArrowCursor));	break;
+	case MC_CROSS:	widget->setCursor(QCursor(Qt::CrossCursor));	break;
+	case MC_WAIT:	widget->setCursor(QCursor(Qt::WaitCursor));		break;
+	case MC_TEXT:	widget->setCursor(QCursor(Qt::IbeamCursor));	break;
+	case MC_NORTH:	widget->setCursor(QCursor(Qt::SizeVerCursor));	break;
+	case MC_NE:		widget->setCursor(QCursor(Qt::SizeBDiagCursor));break;
+	case MC_EAST:	widget->setCursor(QCursor(Qt::SizeHorCursor));	break;
+	case MC_SE:		widget->setCursor(QCursor(Qt::SizeFDiagCursor));break;
+	case MC_SALL:	widget->setCursor(QCursor(Qt::SizeAllCursor));	break;
+#else							//Qt version 2
+	case MC_ARROW:	widget->setCursor(QCursor(ArrowCursor));	break;
+	case MC_CROSS:	widget->setCursor(QCursor(CrossCursor));	break;
+	case MC_WAIT:	widget->setCursor(QCursor(WaitCursor));		break;
+	case MC_TEXT:	widget->setCursor(QCursor(IbeamCursor));	break;
+	case MC_NORTH:	widget->setCursor(QCursor(SizeVerCursor));	break;
+	case MC_NE:		widget->setCursor(QCursor(SizeBDiagCursor));break;
+	case MC_EAST:	widget->setCursor(QCursor(SizeHorCursor));	break;
+	case MC_SE:		widget->setCursor(QCursor(SizeFDiagCursor));break;
+	case MC_SALL:	widget->setCursor(QCursor(SizeAllCursor));	break;
+#endif
+	case MC_MOVE:
+		widget->setCursor(QCursor(cb, cm));
+		break;
+	case MC_ZOOM:
+		widget->setCursor(QCursor(zb, zm));
+		break;
+	default:	return;
+		}
+	cCursor = cid;
+}
+
+bool
+OutputQT::SetScroll(bool isVert, int iMin, int iMax, int iPSize, int iPos)
+{
+	QScrollBar *sb;
+
+	if(isVert) {
+		if(!(sb = VScroll))return false;
+		}
+	else if(!(sb = HScroll)) return false;
+	if(iPos < sb->minValue()) return false;
+	sb->setRange(iMin, iMax);
+	sb->setPageStep(iPSize);
+	if(BaseObj && BaseObj->Id == GO_GRAPH) sb->setLineStep(8);
+	else sb->setLineStep(1);
+	sb->setValue(iPos);
+	return true;
+}
+
+bool
+OutputQT::EndPage()
+{
+	if(widget)widget->repaint();
+	return true;
+}
+
+bool
+OutputQT::UpdateRect(RECT *rc, bool invert)
+{
+	int x1, x2, y1, y2;
+
+	if(!widget || !mempic)return false;
+	if(rc->right > rc->left) {
+		x1 = rc->left;		x2 = rc->right;
+		}
+	else {
+		x1 = rc->right;		x2 = rc->left;
+		}
+	if(rc->bottom > rc->top) {
+		y1 = rc->top;		y2 = rc->bottom;
+		}
+	else {
+		y1 = rc->bottom;	y2 = rc->top;
+		}
+	bitBlt(widget, x1, y1, mempic, x1, y1,
+		x2 - x1, y2 - y1, invert ? Qt::NotCopyROP : Qt::CopyROP);
+	return true;
+}
+
+void
+OutputQT::ShowBitmap(int x, int y, anyOutput* src)
+{
+	BitMapQT *sr;
+	RECT *rc;
+
+	if(!widget || !mempic || !src)return;
+	sr = (BitMapQT*) src;		rc = &sr->DeskRect;
+	bitBlt(widget, x, y, sr->mempic, 0, 0, abs(rc->right-rc->left), 
+		abs(rc->bottom-rc->top), Qt::CopyROP);
+}
+
+void
+OutputQT::ShowLine(POINT * pts, int cp, DWORD color)
+{
+	int i;
+	QPen qp;
+	QPainter paint(widget);
+
+	qp.setColor(SwapRB(color));
+	qp.setWidth(1);
+	qp.setStyle(Qt::SolidLine);
+	paint.setPen(qp);
+	paint.moveTo(pts[0].x, pts[0].y);
+	for (i = 1; i < cp; i++) paint.lineTo(pts[i].x, pts[i].y);
+	paint.flush();
+}
+
+void
+OutputQT::ShowEllipse(POINT p1, POINT p2, DWORD color)
+{
+	int i;
+	QPen qp;
+	QPainter paint(widget);
+
+	qp.setColor(SwapRB(color));
+	qp.setWidth(1);
+	qp.setStyle(Qt::SolidLine);
+	paint.setPen(qp);
+	paint.drawArc(p1.x, p1.y, p2.x-p1.x, p2.y-p1.y, 0, 5760);
+	paint.flush();
+}
+
+bool
+OutputQT::SetMenu(int type)
+{
+	if(type == MENU_SPREAD){
+		QPopupMenu *file = new QPopupMenu(widget);
+		file->insertItem("&Open", widget, SLOT(cmOpen()));
+		file->insertItem("Save &as", widget, SLOT(cmSaveDataAs()));
+		file->insertSeparator();
+		file->insertItem("&Print", widget, SLOT(cmPrint()));
+		file->insertSeparator();
+		file->insertItem("E&xit", widget, SLOT(cmExit()));
+		file->insertSeparator();
+		file->insertItem("n.a.", widget, SLOT(cmFile1()), 0, CM_FILE1);
+		file->insertItem("n.a.", widget, SLOT(cmFile2()), 0, CM_FILE2);
+		file->insertItem("n.a.", widget, SLOT(cmFile3()), 0, CM_FILE3);
+		file->insertItem("n.a.", widget, SLOT(cmFile4()), 0, CM_FILE4);
+		file->insertItem("n.a.", widget, SLOT(cmFile5()), 0, CM_FILE5);
+		file->insertItem("n.a.", widget, SLOT(cmFile6()), 0, CM_FILE6);
+
+		QPopupMenu *edit = new QPopupMenu(widget);
+		edit->insertItem("&Undo", widget, SLOT(cmUndo()), Qt::CTRL + Qt::Key_Z);
+		edit->insertSeparator();
+		edit->insertItem("&Rows/Cols", widget, SLOT(cmAddRowCol()));
+		edit->insertSeparator();
+		edit->insertItem("&Copy", widget, SLOT(cmCopy()), Qt::CTRL + Qt::Key_C);
+		edit->insertItem("C&ut", widget, SLOT(cmCut()), Qt::CTRL + Qt::Key_X);
+		edit->insertItem("&Paste", widget, SLOT(cmPaste()), Qt::CTRL + Qt::Key_V);
+		edit->insertSeparator();
+		edit->insertItem("&Fill Range", widget, SLOT(cmFillRange()));
+
+		QPopupMenu *graph = new QPopupMenu(widget);
+		graph->insertItem("Create &Graph", widget, SLOT(cmNewGraph()));
+		graph->insertItem("Create &Page", widget, SLOT(cmNewPage()));
+		graph->insertItem("&Flush Graph(s)", widget, SLOT(cmDelGraph()));
+		graph->insertSeparator();
+		graph->insertItem("&Settings", widget, SLOT(cmDefaults()));
+
+		QPopupMenu *about = new QPopupMenu(widget);
+		about->insertItem("&About ...", widget, SLOT(cmAbout()));
+
+		menu = new QMenuBar(widget);
+		menu->insertItem("&File", file);			menu->insertItem("&Edit", edit);
+		menu->insertItem("&Graph", graph);			menu->insertItem("&?", about);
+#ifdef Q_CHECK_PTR				//Qt version 3, n.a. in version 2
+		menu->setItemVisible(CM_FILE1, false);		menu->setItemVisible(CM_FILE2, false);
+		menu->setItemVisible(CM_FILE3, false);		menu->setItemVisible(CM_FILE4, false);
+		menu->setItemVisible(CM_FILE5, false);		menu->setItemVisible(CM_FILE6, false);
+#endif
+		}
+	else if(type == MENU_GRAPH) {
+		QPopupMenu *file = new QPopupMenu(widget);
+		file->insertItem("&Open", widget, SLOT(cmOpen()));
+		file->insertItem("Save &as", widget, SLOT(cmSaveGraphAs()));
+		file->insertItem("&Copy", widget, SLOT(cmCopyGraph()));
+		file->insertSeparator();
+		file->insertItem("&Print", widget, SLOT(cmPrint()));
+		file->insertItem("&Export", widget, SLOT(cmExport()));
+		file->insertSeparator();
+		file->insertItem(widget == MainWidget ? "E&xit" : "&Close", widget, SLOT(cmExit()));
+
+		QPopupMenu *edit = new QPopupMenu(widget);
+		edit->insertItem("&Undo", widget, SLOT(cmUndo()), Qt::CTRL + Qt::Key_Z);
+		edit->insertSeparator();
+		edit->insertItem("&Copy", widget, SLOT(cmCopyGraph()), Qt::CTRL + Qt::Key_C);
+		edit->insertItem("&Paste", widget, SLOT(cmPaste()), Qt::CTRL + Qt::Key_V);
+		edit->insertSeparator();
+		edit->insertItem("&UpdateValues", widget, SLOT(cmUpdate()));
+		edit->insertSeparator();
+		edit->insertItem("&Delete Object", widget, SLOT(cmDelObj()));
+
+		QPopupMenu *zoom = new QPopupMenu(widget);
+		zoom->insertTearOffHandle();
+		zoom->insertItem("zoom &in", widget, SLOT(cmZoomIn()), Qt::CTRL + Qt::Key_Plus);
+		zoom->insertItem("zoom &out", widget, SLOT(cmZoomOut()), Qt::CTRL + Qt::Key_Minus);
+		zoom->insertItem("&fit to widget", widget, SLOT(cmZoomFit()), Qt::CTRL + Qt::Key_F);
+		zoom->insertSeparator();
+		zoom->insertItem("25%", widget, SLOT(cmZoom25()));
+		zoom->insertItem("50%", widget, SLOT(cmZoom50()));
+		zoom->insertItem("100%", widget, SLOT(cmZoom100()));
+		zoom->insertItem("200%", widget, SLOT(cmZoom200()));
+		zoom->insertItem("400%", widget, SLOT(cmZoom400()));
+
+		QPopupMenu *displ = new QPopupMenu(widget);
+		displ->insertItem("&Redraw", widget, SLOT(cmRedraw()));
+		displ->insertItem("&Zoom", zoom);
+		displ->insertItem("&Layers", widget, SLOT(cmLayers()));
+
+		QPopupMenu *tools = new QPopupMenu(widget);
+		tools->insertTearOffHandle();
+		tools->insertItem("&Standard", widget, SLOT(cmtStandard()), Qt::Key_Escape, CM_T_STANDARD);
+		tools->insertSeparator();
+		tools->insertItem("&Draw", widget, SLOT(cmtDraw()), 0, CM_T_DRAW);
+		tools->insertItem("Poly&line", widget, SLOT(cmtPolyline()), 0, CM_T_POLYLINE);
+		tools->insertItem("Poly&gon", widget, SLOT(cmtPolygon()), 0, CM_T_POLYGON);
+		tools->insertItem("&Rectangle", widget, SLOT(cmtRectangle()), 0, CM_T_RECTANGLE);
+		tools->insertItem("&r&ound Rect.", widget, SLOT(cmtRoundrect()), 0, CM_T_ROUNDREC);
+		tools->insertItem("&Ellipse", widget, SLOT(cmtEllipse()), 0, CM_T_ELLIPSE);
+		tools->insertItem("&Arrow", widget, SLOT(cmtArrow()), 0, CM_T_ARROW);
+		tools->insertItem("&Text", widget, SLOT(cmtText()), 0, CM_T_TEXT);
+		tools->setCheckable(true);
+
+		QPopupMenu *plots = new QPopupMenu(widget);
+		plots->insertItem("Add &Plot", widget, SLOT(cmAddPlot()));
+		plots->insertItem("Add &Axis", widget, SLOT(cmAddAxis()));
+		plots->insertItem("Add &Legend", widget, SLOT(cmAddLegend()));
+		plots->insertSeparator();
+		plots->insertItem("&Configure", widget, SLOT(cmDefaults()));
+
+		QPopupMenu *about = new QPopupMenu(widget);
+		about->insertItem("&About ...", widget, SLOT(cmAbout()));
+
+		menu = new QMenuBar(widget);
+		menu->insertItem("&File", file);
+		menu->insertItem("&Edit", edit);
+		menu->insertItem("&Display", displ);
+		menu->insertItem("&Tools", tools);
+		menu->insertItem("&Plots", plots);
+		menu->insertItem("&?", about);
+		}
+	else if(type == MENU_PAGE) {
+		QPopupMenu *file = new QPopupMenu(widget);
+		file->insertItem("&Open", widget, SLOT(cmOpen()));
+		file->insertItem("Save &as", widget, SLOT(cmSaveGraphAs()));
+		file->insertSeparator();
+		file->insertItem("&Print", widget, SLOT(cmPrint()));
+		file->insertItem("&Export", widget, SLOT(cmExport()));
+		file->insertSeparator();
+		file->insertItem(widget == MainWidget ? "E&xit" : "&Close", widget, SLOT(cmExit()));
+
+		QPopupMenu *edit = new QPopupMenu(widget);
+		edit->insertItem("&Undo", widget, SLOT(cmUndo()), Qt::CTRL + Qt::Key_Z);
+		edit->insertSeparator();
+		edit->insertItem("&Copy", widget, SLOT(cmCopyGraph()), Qt::CTRL + Qt::Key_C);
+		edit->insertItem("&Paste", widget, SLOT(cmPaste()), Qt::CTRL + Qt::Key_V);
+		edit->insertSeparator();
+		edit->insertItem("&UpdateValues", widget, SLOT(cmUpdate()));
+		edit->insertSeparator();
+		edit->insertItem("&Delete Object", widget, SLOT(cmDelObj()));
+
+		QPopupMenu *zoom = new QPopupMenu(widget);
+		zoom->insertTearOffHandle();
+		zoom->insertItem("zoom &in", widget, SLOT(cmZoomIn()), Qt::CTRL + Qt::Key_Plus);
+		zoom->insertItem("zoom &out", widget, SLOT(cmZoomOut()), Qt::CTRL + Qt::Key_Minus);
+		zoom->insertItem("&fit to widget", widget, SLOT(cmZoomFit()), Qt::CTRL + Qt::Key_F);
+		zoom->insertSeparator();
+		zoom->insertItem("25%", widget, SLOT(cmZoom25()));
+		zoom->insertItem("50%", widget, SLOT(cmZoom50()));
+		zoom->insertItem("100%", widget, SLOT(cmZoom100()));
+		zoom->insertItem("200%", widget, SLOT(cmZoom200()));
+		zoom->insertItem("400%", widget, SLOT(cmZoom400()));
+
+		QPopupMenu *displ = new QPopupMenu(widget);
+		displ->insertItem("&Redraw", widget, SLOT(cmRedraw()));
+		displ->insertItem("&Zoom", zoom);
+		displ->insertItem("&Layers", widget, SLOT(cmLayers()));
+
+		QPopupMenu *tools = new QPopupMenu(widget);
+		tools->insertTearOffHandle();
+		tools->insertItem("&Standard", widget, SLOT(cmtStandard()), Qt::Key_Escape, CM_T_STANDARD);
+		tools->insertSeparator();
+		tools->insertItem("&Draw", widget, SLOT(cmtDraw()), 0, CM_T_DRAW);
+		tools->insertItem("Poly&line", widget, SLOT(cmtPolyline()), 0, CM_T_POLYLINE);
+		tools->insertItem("Poly&gon", widget, SLOT(cmtPolygon()), 0, CM_T_POLYGON);
+		tools->insertItem("&Rectangle", widget, SLOT(cmtRectangle()), 0, CM_T_RECTANGLE);
+		tools->insertItem("&r&ound Rect.", widget, SLOT(cmtRoundrect()), 0, CM_T_ROUNDREC);
+		tools->insertItem("&Ellipse", widget, SLOT(cmtEllipse()), 0, CM_T_ELLIPSE);
+		tools->insertItem("&Arrow", widget, SLOT(cmtArrow()), 0, CM_T_ARROW);
+		tools->insertItem("&Text", widget, SLOT(cmtText()), 0, CM_T_TEXT);
+		tools->setCheckable(true);
+
+		QPopupMenu *plots = new QPopupMenu(widget);
+		plots->insertItem("Add &Graph", widget, SLOT(cmNewGraph()));
+		plots->insertItem("Add &Plot", widget, SLOT(cmAddPlot()));
+		plots->insertItem("Add &Axis", widget, SLOT(cmAddAxis()));
+		plots->insertItem("Add &Legend", widget, SLOT(cmAddLegend()));
+		plots->insertSeparator();
+		plots->insertItem("Page &Settings", widget, SLOT(cmDefaults()));
+
+		QPopupMenu *about = new QPopupMenu(widget);
+		about->insertItem("&About ...", widget, SLOT(cmAbout()));
+
+		menu = new QMenuBar(widget);
+		menu->insertItem("&File", file);
+		menu->insertItem("&Edit", edit);
+		menu->insertItem("&Display", displ);
+		menu->insertItem("&Tools", tools);
+		menu->insertItem("&Plots", plots);
+		menu->insertItem("&?", about);
+		}
+	else return false;
+	menu->show();
+	MenuHeight = menu->height();
+	widget->resize(widget->width()+8, widget->height()+8);
+	return true;
+}
+
+void
+OutputQT::CheckMenu(int mid, bool check)
+{
+	if(mid < CM_T_STANDARD) switch(mid){					//tool mode identifier
+	case TM_STANDARD:	mid = CM_T_STANDARD;	break;
+	case TM_DRAW:		mid = CM_T_DRAW;		break;
+	case TM_POLYLINE:	mid = CM_T_POLYLINE;	break;
+	case TM_POLYGON:	mid = CM_T_POLYGON;		break;
+	case TM_RECTANGLE:	mid = CM_T_RECTANGLE;	break;
+	case TM_ROUNDREC:	mid = CM_T_ROUNDREC;	break;
+	case TM_ELLIPSE:	mid = CM_T_ELLIPSE;		break;
+	case TM_ARROW:		mid = CM_T_ARROW;		break;
+	case TM_TEXT:		mid = CM_T_TEXT;		break;
+	default:	return;
+		}
+	if(menu) menu->setItemChecked(mid, check);
+}
+
+void
+OutputQT::FileHistory()
+{
+	char **history[] = {&defs.File1, &defs.File2, &defs.File3, &defs.File4, &defs.File5, &defs.File6};
+	int i, j, k;
+
+	if(!hasHistMenu || !defs.File1 || !menu) return;
+    for(i = 0; i < 6 && *history[i]; i++) {
+		k = strlen(*history[i]);
+		for (j = 0; j < k && defs.currPath[j] == (*history[i])[j]; j++);
+		if((*history[i])[j] == '\\' || (*history[i])[j] == '/') j++;
+		menu->changeItem(CM_FILE1+i, *history[i]+j);
+#ifdef Q_CHECK_PTR				//Qt version 3, n.a. in version 2
+		menu->setItemVisible(CM_FILE1+i, true);
+#endif
+		}
+	HistMenuSize = i;
+}
+
+void
+OutputQT::CreateNewWindow(GraphObj *g)
+{
+	int w, h;
+
+	GetDesktopSize(&w, &h);
+	if(widget = new RLPwidget(0, 0, this, g)) {
+		widget->setCaption("OutputQT::CreateNewWindow");
+		widget->setGeometry(0, 0, (int)(w*.7f), (int)(h*.7f));
+		HScroll = ((RLPwidget*)widget)->HScroll;
+		VScroll = ((RLPwidget*)widget)->VScroll;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Common widget support
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+RLPwidget::RLPwidget(QWidget *par, const char *name, anyOutput *o, GraphObj *g)
+	: QWidget(par, name)
+{
+	int w, h;
+
+	GetDesktopSize(&w, &h);
+	mempic = new QPixmap(w, h);
+	parent = par;
+	OutputClass = o;
+	BaseObj = g;
+	setMinimumSize(100, 80);
+	setBackgroundMode(NoBackground);
+	HScroll = new QScrollBar(QScrollBar::Horizontal, this, 0);
+	HScroll->setRange(0, 1000);
+	HScroll->setSteps(1, 16);
+	connect(HScroll, SIGNAL(valueChanged(int)), SLOT(hScrollEvent(int)));
+	VScroll = new QScrollBar(QScrollBar::Vertical, this, 0);
+	VScroll->setRange(0, 1000);
+	VScroll->setSteps(1, 16);
+	connect(VScroll, SIGNAL(valueChanged(int)), SLOT(vScrollEvent(int)));
+	if(!MainWidget) QAppl->setMainWidget(MainWidget = this);
+	setMouseTracking(true);
+	setFocusPolicy(StrongFocus);
+}
+
+RLPwidget::~RLPwidget()
+{
+	if(BaseObj){
+		BaseObj->Command(CMD_CAN_DELETE, 0L, 0L);
+		BaseObj = 0L;
+		}
+	if(OutputClass)((OutputQT*)OutputClass)->widget = 0L;
+}
+
+//public slots: menu items, events
+void
+RLPwidget::hScrollEvent(int pos)
+{
+	if(BaseObj){
+		BaseObj->Command(CMD_SETHPOS, (void*)(&pos), OutputClass);
+		repaint();
+		}
+}
+
+void
+RLPwidget::vScrollEvent(int pos)
+{
+	if(BaseObj){
+		BaseObj->Command(CMD_SETVPOS, (void*)(&pos), OutputClass);
+		repaint();
+		}
+}
+
+
+void
+RLPwidget::cmOpen()
+{
+	if(BaseObj)BaseObj->Command(CMD_OPEN, (void *)NULL, OutputClass);
+}
+
+void
+RLPwidget::cmSaveDataAs()
+{
+	if(BaseObj)BaseObj->Command(CMD_SAVEDATAAS, (void *)NULL, OutputClass);
+}
+
+void
+RLPwidget::cmExit()
+{
+	delete(this);
+}
+
+void
+RLPwidget::cmNewGraph()
+{
+	if(BaseObj)BaseObj->Command(CMD_NEWGRAPH, (void *)NULL, OutputClass);
+}
+
+void
+RLPwidget::cmNewPage()
+{
+	if(BaseObj)BaseObj->Command(CMD_NEWPAGE, (void *)NULL, OutputClass);
+}
+
+void
+RLPwidget::cmDelGraph()
+{
+	if(BaseObj)BaseObj->Command(CMD_DELGRAPH, (void *)NULL, OutputClass);
+}
+
+void
+RLPwidget::cmAddPlot()
+{
+	if(BaseObj)BaseObj->Command(CMD_ADDPLOT, (void *)NULL, OutputClass);
+}
+
+void
+RLPwidget::cmAddLegend()
+{
+	if(BaseObj)BaseObj->Command(CMD_LEGEND, (void *)NULL, OutputClass);
+}
+
+void
+RLPwidget::cmLayers()
+{
+	if(BaseObj)BaseObj->Command(CMD_LAYERS, (void *)NULL, OutputClass);
+}
+
+void
+RLPwidget::cmAbout()
+{
+	RLPlotInfo();
+}
+
+void
+RLPwidget::cmAddRowCol()
+{
+	if(BaseObj)BaseObj->Command(CMD_ADDROWCOL, (void *)NULL, OutputClass);
+}
+
+void
+RLPwidget::cmCopy()
+{
+	if(BaseObj && BaseObj->Id == GO_SPREADDATA) {
+		BaseObj->Command(CMD_QUERY_COPY, 0L, OutputClass);
+		CopyData(BaseObj);
+		}
+}
+
+void
+RLPwidget::cmCut()
+{
+	if(BaseObj && BaseObj->Id == GO_SPREADDATA) {
+		BaseObj->Command(CMD_CUT, 0L, OutputClass);
+		CopyData(BaseObj);
+		}
+}
+
+void
+RLPwidget::cmPaste()
+{
+	if(BaseObj) {
+		OutputClass->MouseCursor(MC_WAIT, true);
+		TestClipboard(BaseObj);
+		OutputClass->MouseCursor(MC_ARROW, true);
+		}
+}
+
+void
+RLPwidget::cmCopyGraph()
+{
+	if(BaseObj) CopyGraph(BaseObj);
+}
+
+void
+RLPwidget::cmSaveGraphAs()
+{
+	SaveGraphAs(BaseObj);
+}
+
+void
+RLPwidget::cmRedraw()
+{
+	if(OutputClass && BaseObj && OutputClass->Erase(defs.Color(COL_BG))) {
+		BaseObj->Command(CMD_SETSCROLL, 0L, OutputClass);
+		repaint();
+		}
+}
+
+void
+RLPwidget::cmZoom25()
+{
+	BaseObj->Command(CMD_ZOOM, (void*)(&"25"), OutputClass);
+}
+
+void
+RLPwidget::cmZoom50()
+{
+	BaseObj->Command(CMD_ZOOM, (void*)(&"50"), OutputClass);
+}
+
+void
+RLPwidget::cmZoom100()
+{
+	BaseObj->Command(CMD_ZOOM, (void*)(&"100"), OutputClass);
+}
+
+void
+RLPwidget::cmZoom200()
+{
+	BaseObj->Command(CMD_ZOOM, (void*)(&"200"), OutputClass);
+}
+
+void
+RLPwidget::cmZoom400()
+{
+	BaseObj->Command(CMD_ZOOM, (void*)(&"400"), OutputClass);
+}
+
+void
+RLPwidget::cmZoomIn()
+{
+	BaseObj->Command(CMD_ZOOM, (void*)(&"+"), OutputClass);
+}
+
+void
+RLPwidget::cmZoomOut()
+{
+	BaseObj->Command(CMD_ZOOM, (void*)(&"-"), OutputClass);
+}
+
+void
+RLPwidget::cmZoomFit()
+{
+	BaseObj->Command(CMD_ZOOM, (void*)(&"fit"), OutputClass);
+}
+
+void
+RLPwidget::cmPrint()
+{
+	int m;
+	PrintQT out(0L, 0L);
+
+	if(!BaseObj) return;
+	if(BaseObj->Id == GO_SPREADDATA) {
+		m = iround(out.hres/60.0);
+#ifdef Q_CHECK_PTR				//Qt version 3, n.a. in version 2
+		out.printer->setMargins(m, m, m, m);
+#endif
+		BaseObj->Command(CMD_PRINT, 0L, &out);
+		}
+	else if(out.StartPage()){
+		BaseObj->DoPlot(&out);
+		out.EndPage();
+		}
+	BaseObj->DoPlot(OutputClass);
+}
+
+void
+RLPwidget::cmExport()
+{
+	OpenExportName(BaseObj, "hello.svg");
+	BaseObj->DoPlot(0L);
+}
+
+void
+RLPwidget::cmDelObj()
+{
+	if(CurrGO && CurrGO->parent && CurrGO->parent->
+		Command(CMD_DELOBJ, (void*)CurrGO, OutputClass)) {
+		CurrGO = 0L;
+		OutputClass->Erase(defs.Color(COL_BG));
+		BaseObj->DoPlot(OutputClass);
+		}
+	else if(!CurrGO) InfoBox("No object selected!");
+}
+
+void
+RLPwidget::cmUpdate()
+{
+	if(BaseObj) BaseObj->Command(CMD_UPDATE, 0L, OutputClass);
+}
+
+void
+RLPwidget::cmDefaults()
+{
+	BaseObj->Command(CMD_CONFIG, 0L, OutputClass);
+}
+
+void
+RLPwidget::cmAddAxis()
+{
+	BaseObj->Command(CMD_ADDAXIS, 0L, OutputClass);
+}
+
+void
+RLPwidget::cmUndo()
+{
+	BaseObj->Command(CMD_UNDO, 0L, OutputClass);
+}
+
+void
+RLPwidget::cmFillRange()
+{
+	BaseObj->Command(CMD_FILLRANGE, 0L, OutputClass);
+}
+
+void ToolMenu(GraphObj *b, anyOutput *o, int tm)
+{
+	if(b && o) b->Command(CMD_TOOLMODE, (void*)(& tm), o);
+}
+
+void
+RLPwidget::cmtStandard()
+{
+	ToolMenu(BaseObj, OutputClass, TM_STANDARD);
+}
+
+void
+RLPwidget::cmtDraw()
+{
+	ToolMenu(BaseObj, OutputClass, TM_DRAW);
+}
+
+void
+RLPwidget::cmtPolyline()
+{
+	ToolMenu(BaseObj, OutputClass, TM_POLYLINE);
+}
+
+void
+RLPwidget::cmtPolygon()
+{
+	ToolMenu(BaseObj, OutputClass, TM_POLYGON);
+}
+
+void
+RLPwidget::cmtRectangle()
+{
+	ToolMenu(BaseObj, OutputClass, TM_RECTANGLE);
+}
+
+void
+RLPwidget::cmtRoundrect()
+{
+	ToolMenu(BaseObj, OutputClass, TM_ROUNDREC);
+}
+
+void
+RLPwidget::cmtEllipse()
+{
+	ToolMenu(BaseObj, OutputClass, TM_ELLIPSE);
+}
+
+void
+RLPwidget::cmtArrow()
+{
+	ToolMenu(BaseObj, OutputClass, TM_ARROW);
+}
+
+void
+RLPwidget::cmtText()
+{
+	ToolMenu(BaseObj, OutputClass, TM_TEXT);
+}
+
+//protected: widget events
+void
+RLPwidget::paintEvent(QPaintEvent *range)
+{
+	QRect rc;
+
+	rc = range->rect();
+	bitBlt(this, rc.left(), rc.top(), this->mempic, rc.left(), rc.top(),
+		1+rc.right()-rc.left(), 1+rc.bottom()-rc.top());
+}
+
+void
+RLPwidget::resizeEvent(QResizeEvent *)
+{
+	HScroll->resize(width() -16, 16);
+	HScroll->move(0, height()-16);
+	VScroll->resize(16, height()-OutputClass->MenuHeight-16);
+	VScroll->move(width()-16, OutputClass->MenuHeight);
+	if(BaseObj) BaseObj->Command(CMD_SETSCROLL, 0L, OutputClass);
+}
+
+void
+RLPwidget::closeEvent(QCloseEvent *e)
+{
+	if(BaseObj){
+		BaseObj->Command(CMD_CAN_DELETE, 0L, 0L);
+		BaseObj = 0L;
+		}
+	e->accept();
+}
+
+void
+RLPwidget::mouseDoubleClickEvent(QMouseEvent *e)
+{
+	MouseEvent mev = {1, e->button() == Qt::LeftButton?MOUSE_LBDOUBLECLICK:-1,e->x(),e->y()};
+
+	if (BaseObj)BaseObj->Command(CMD_MOUSE_EVENT, (void *)&mev, OutputClass);
+}
+
+void
+RLPwidget::mousePressEvent(QMouseEvent *e)
+{
+	MouseEvent mev = {1, e->button() == Qt::LeftButton ? MOUSE_LBDOWN : -1, e->x(), e->y()};
+
+	HideTextCursor();
+	if (BaseObj)BaseObj->Command(CMD_MOUSE_EVENT, (void *)&mev, OutputClass);
+}
+
+void
+RLPwidget::mouseReleaseEvent(QMouseEvent *e)
+{
+	MouseEvent mev = {0, e->button() == Qt::LeftButton ? MOUSE_LBUP :
+		e->button() == Qt::RightButton ? MOUSE_RBUP : -1, e->x(), e->y()};
+
+	if (BaseObj) BaseObj->Command(CMD_MOUSE_EVENT, (void *)&mev, OutputClass);
+}
+
+void
+RLPwidget::mouseMoveEvent(QMouseEvent *e)
+{
+	MouseEvent mev = {0, MOUSE_MOVE, e->x(), e->y()};
+
+	if(e->state() == Qt::LeftButton) mev.StateFlags = 1;
+	if (BaseObj) BaseObj->Command(CMD_MOUSE_EVENT, (void *)&mev, OutputClass);
+}
+
+void
+RLPwidget::keyPressEvent(QKeyEvent *e)
+{
+	int c;
+
+	if(BaseObj) switch(c = e->key()) {
+		case Key_Prior:
+			BaseObj->Command(CMD_PAGEUP, 0L, OutputClass);
+			break;
+		case Key_Next:
+			BaseObj->Command(CMD_PAGEDOWN, 0L, OutputClass);
+			break;
+		case Key_Left:
+			if(e->state() & ShiftButton) BaseObj->Command(CMD_SHIFTLEFT, 0L, OutputClass);
+			else BaseObj->Command(CMD_CURRLEFT, 0L, OutputClass);
+			break;
+		case Key_Right:
+			if(e->state() & ShiftButton) BaseObj->Command(CMD_SHIFTRIGHT, 0L, OutputClass);
+			else BaseObj->Command(CMD_CURRIGHT, 0L, OutputClass);
+			break;
+		case Key_Up:
+			if(e->state() & ShiftButton) BaseObj->Command(CMD_SHIFTUP, 0L, OutputClass);
+			else BaseObj->Command(CMD_CURRUP, 0L, OutputClass);
+			break;
+		case Key_Down:
+			if(e->state() & ShiftButton) BaseObj->Command(CMD_SHIFTDOWN, 0L, OutputClass);
+			else BaseObj->Command(CMD_CURRDOWN, 0L, OutputClass);
+			break;
+		case Key_Delete:
+			BaseObj->Command(CMD_DELETE, 0L, OutputClass);
+			break;
+		case Key_Tab:
+			BaseObj->Command(CMD_TAB, 0L, OutputClass);
+			break;
+		case Key_Backtab:
+			BaseObj->Command(CMD_SHTAB, 0L, OutputClass);
+			break;
+		case Key_Home:
+			BaseObj->Command(CMD_POS_FIRST, 0L, OutputClass);
+			break;
+		case Key_End:
+			BaseObj->Command(CMD_POS_LAST, 0L, OutputClass);
+			break;
+		default:
+			c = e->ascii();
+			if(c == 3) cmCopy();
+			else if(c == 22) cmPaste();
+			else if(c == 26) cmUndo();
+			else if(c >1 && c < 256)
+				BaseObj->Command(CMD_ADDCHAR, (void *)(& c), OutputClass);
+			break;
+		}
+	e->accept();
+}
+
+void
+RLPwidget::focusInEvent(QFocusEvent *e)
+{
+	CurrWidgetPos.x = x();			CurrWidgetPos.y = y();
+	if(BaseObj) {
+		if(BaseObj->Id == GO_GRAPH) CurrGraph = (Graph*)BaseObj;
+		}
+}
+
+//private functions
+void
+RLPwidget::openHistoryFile(int idx)
+{
+	char *name = 0L;
+
+	switch (idx) {
+	case 0:			name = defs.File1;			break;
+	case 1:			name = defs.File2;			break;
+	case 2:			name = defs.File3;			break;
+	case 3:			name = defs.File4;			break;
+	case 4:			name = defs.File5;			break;
+	case 5:			name = defs.File6;			break;
+		}
+	if(name && FileExist(name)) {
+		BaseObj->Command(CMD_DROPFILE, name, OutputClass);
+		defs.FileHistory(name);
+		OutputClass->FileHistory();
+		}
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Print and output EPS to file
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class FileEPS:public QPrinter {
+public:
+	FileEPS(GraphObj *g, anyOutput *o);
+
+protected:
+	int metric(int) const;
+
+private:
+	GraphObj *go;
+	anyOutput *out;
+};
+
+FileEPS::FileEPS(GraphObj *g, anyOutput *o)
+{
+	go = g;
+	out = o;
+}
+
+int
+FileEPS::metric(int m) const
+{
+	if(go && out)switch (m) {
+	case QPaintDeviceMetrics::PdmWidth:
+		return out->un2ix(go->GetSize(SIZE_GRECT_RIGHT) - go->GetSize(SIZE_GRECT_LEFT))/10;
+	case QPaintDeviceMetrics::PdmHeight:
+		return out->un2ix(go->GetSize(SIZE_GRECT_BOTTOM) - go->GetSize(SIZE_GRECT_TOP))/10;
+	case QPaintDeviceMetrics::PdmWidthMM:
+		return iround((go->GetSize(SIZE_GRECT_RIGHT) - go->GetSize(SIZE_GRECT_LEFT)) *
+			Units[defs.cUnits].convert);
+	case QPaintDeviceMetrics::PdmHeightMM:
+		return iround((go->GetSize(SIZE_GRECT_BOTTOM) - go->GetSize(SIZE_GRECT_TOP)) *
+			Units[defs.cUnits].convert);
+		}
+	return QPrinter::metric(m);
+}
+
+PrintQT::PrintQT(GraphObj *g, char *file)
+{
+	hres = vres = 720.0;
+	units = defs.cUnits;
+	Box1.Xmin = Box1.Ymin = 0.0;
+	Box1.Xmax = Box1.Ymax = 6000;
+	DeskRect.left = DeskRect.top = 0;
+	DeskRect.right = (long)(hres*6.0);
+	DeskRect.bottom = (long)(vres*8.0);
+	dxf.setMatrix(0.1, 0.0, 0.0, 0.1, 0.0, 0.0);
+	hgo = 0L;
+	if(file) fileName = strdup(file);
+	else fileName = 0L;
+	go = g;
+	if(fileName && go) printer = new FileEPS(g, this);
+	else printer = new QPrinter;
+	bPrinting = false;
+}
+
+PrintQT::~PrintQT()
+{
+	if(printer) delete(printer);
+	if(hgo) delete(hgo);
+	if(fileName) free(fileName);
+}
+
+bool 
+PrintQT::ActualSize(RECT *rc)
+{
+	if(printer) {
+		QPaintDeviceMetrics dm(printer);	rc->top = rc->left = 0;
+		rc->bottom = dm.height() *10;		rc->right = dm.width() *10;
+		return true;
+		}
+	return false;
+}
+
+bool
+PrintQT::SetLine(LineDEF *lDef)
+{
+	int iw;
+
+	if(lDef->width != LineWidth || lDef->width != LineWidth ||
+		lDef->pattern != dPattern || lDef->color != dLineCol) {
+		LineWidth = lDef->width;
+		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);
+		}
+	return true;
+}
+
+bool
+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);
+		}
+	else {
+		if(hgo) delete hgo;
+		hgo = 0L;
+		}
+	qPainter.setBrush(QColor(SwapRB(fill->color)));
+	dFillCol = fill->color;
+	dFillCol2 = fill->color2;
+	return true;
+}
+
+bool
+PrintQT::SetTextSpec(TextDEF *set)
+{
+	return com_SetTextSpec(set, &TxtSet, this, qFont, &qPainter);
+}
+
+bool
+PrintQT::StartPage()
+{
+	if(!printer || bPrinting) return false;
+	if(fileName) {
+		VPorg.fy = -co2fiy(go->GetSize(SIZE_GRECT_TOP));
+		VPorg.fx = -co2fix(go->GetSize(SIZE_GRECT_LEFT));
+		printer->setOutputFileName(fileName);
+		printer->setFullPage(true);
+		qPainter.begin(printer);
+		qPainter.setWorldMatrix(dxf, FALSE);
+		return bPrinting = true;
+		}
+
+	if(printer->setup(0)){
+		qPainter.begin(printer);
+		qPainter.setWorldMatrix(dxf, FALSE);
+		return bPrinting = true;
+		}
+	else return false;
+}
+
+bool
+PrintQT::EndPage()
+{
+	qPainter.flush();	qPainter.end();	bPrinting = false;
+	return true;
+}
+
+bool
+PrintQT::Eject()
+{
+	if(!bPrinting) return false;
+	qPainter.flush();	qPainter.end();		qPainter.begin(printer);
+	qPainter.setWorldMatrix(dxf, FALSE);
+	return true;
+}
+
+bool
+PrintQT::oGetTextExtent(char *text, int cb, int *width, int *height)
+{
+	QRect rc = qPainter.boundingRect(0, 0, 10000, 1000, Qt::AlignLeft | Qt::AlignTop,
+		text, cb > 0 ? cb : strlen(text));
+	*width = rc.rRight() - rc.rLeft();		*height = rc.rBottom() - rc.rTop();
+	return true;
+}
+
+bool
+PrintQT::oCircle(int x1, int y1, int x2, int y2, char* nam)
+{
+	qPainter.drawEllipse(x1, y1, x2-x1, y2-y1);
+	if(hgo) return hgo->oCircle(x1, y1, x2, y2);
+	return true;
+}
+
+bool
+PrintQT::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]);
+		}
+	else {
+		qPainter.moveTo(pts[0].x, pts[0].y);
+		for (i = 1; i < cp; i++) qPainter.lineTo(pts[i].x, pts[i].y);
+		}
+	return true;
+}
+
+bool
+PrintQT::oRectangle(int x1, int y1, int x2, int y2, char *nam)
+{
+	qPainter.drawRect(x1, y1, x2-x1-1, y2-y1-1);
+	if(hgo) hgo->oRectangle(x1, y1, x2, y2, 0L);
+	return true;
+}
+
+bool
+PrintQT::oSolidLine(POINT *p)
+{
+	qPainter.drawLine(p[0].x, p[0].y, p[1].x, p[1].y);
+	return true;
+}
+
+bool
+PrintQT::oTextOut(int x, int y, char *txt, int cb)
+{
+	return com_TextOut(x, y, txt, &TxtSet, &qPainter, this);
+}
+
+bool
+PrintQT::oPolygon(POINT *pts, int cp, char *nam)
+{
+	int i;
+
+	QPointArray *a;
+
+	if(!pts || cp <2) return false;
+	a = new QPointArray(cp);
+	if (a) {
+		for(i = 0; i < cp; i++) a->setPoint(i, pts[i].x, pts[i].y);
+		qPainter.drawPolygon(*a);
+		delete a;
+		}
+	if(hgo) hgo->oPolygon(pts, cp);
+	return true;
+}
+
+bool
+PrintQT::oArc(int x1, int y1, int x2, int y2, int quads)
+{
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Find a suitable www browser
+void FindBrowser()
+{
+	//find a suitable browser
+	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");
+	//use home as startup directory
+	sprintf(TmpTxt, "%s", getenv("HOME"));
+	defs.currPath = strdup(TmpTxt);	strcat(TmpTxt, "/.RLPlot");
+	defs.IniFile = strdup(TmpTxt);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// The MAIN antry point
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+int main (int argc, char **argv)
+{
+	QApplication a(argc, argv);
+	DefsRW *drw;
+
+	if(argc > 1 && argv[1]  && argv[1][0] && FileExist(argv[1]))
+		LoadFile = strdup(argv[1]);
+	QAppl = &a;
+	InitTextCursor(true);
+	ShowBanner(true);
+	a.exec();
+	if(defs.IniFile) {
+		if(drw = new DefsRW()){
+			drw->FileIO(FILE_WRITE);		delete drw;
+			}
+		}
+	SpreadMain(false);
+	InitTextCursor(false);
+	if(WWWbrowser) free(WWWbrowser);
+	if(LoadFile) free(LoadFile);
+	return 0;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Dialog box support
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+DlgWidget::DlgWidget(QWidget *par, const char *name, tag_DlgObj *d)
+#ifdef Q_CHECK_PTR				//n.a. in Qt version 2
+: QWidget(par, name, Qt::WType_Dialog)
+#else
+: QWidget(par, name, 0x0000002)
+#endif
+{
+	parent = par;
+	dlg = d;
+	setFocusPolicy(StrongFocus);
+}
+
+DlgWidget::~DlgWidget()
+{
+	if(OutputClass){
+		((OutputQT*)OutputClass)->widget=0L;
+		delete ((OutputQT*)OutputClass);
+		}
+}
+
+void
+DlgWidget::paintEvent(QPaintEvent *range)
+{
+	QRect rc;
+
+	rc = range->rect();
+	bitBlt(this, rc.left(), rc.top(), this->mempic, rc.left(), rc.top(),
+		1+rc.right()-rc.left(), 1+rc.bottom()-rc.top());
+}
+
+void
+DlgWidget::mouseDoubleClickEvent(QMouseEvent *e)
+{
+	MouseEvent mev = {1, e->button() == Qt::LeftButton?MOUSE_LBDOUBLECLICK:-1,e->x(),e->y()};
+
+	if (dlg) dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, OutputClass);
+}
+
+void
+DlgWidget::mousePressEvent(QMouseEvent *e)
+{
+	MouseEvent mev = {1, e->button() == Qt::LeftButton ? MOUSE_LBDOWN : -1, e->x(), e->y()};
+
+	HideTextCursor();
+	if (dlg) dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, OutputClass);
+}
+
+void
+DlgWidget::mouseReleaseEvent(QMouseEvent *e)
+{
+	MouseEvent mev = {0, e->button() == Qt::LeftButton ? MOUSE_LBUP : -1, e->x(), e->y()};
+
+	if (dlg) dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, OutputClass);
+}
+
+void
+DlgWidget::mouseMoveEvent(QMouseEvent *e)
+{
+	MouseEvent mev = {1, e->state() == Qt::LeftButton ? MOUSE_MOVE : -1, e->x(), e->y()};
+
+	if (dlg) dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, OutputClass);
+}
+
+void
+DlgWidget::keyPressEvent(QKeyEvent *e)
+{
+	int c;
+
+	if(dlg) switch(c = e->key()) {
+		case Key_Left:
+			if(e->state() & ShiftButton) dlg->Command(CMD_SHIFTLEFT, 0L, OutputClass);
+			else dlg->Command(CMD_CURRLEFT, 0L, OutputClass);
+			break;
+		case Key_Right:
+			if(e->state() & ShiftButton) dlg->Command(CMD_SHIFTRIGHT, 0L, OutputClass);
+			else dlg->Command(CMD_CURRIGHT, 0L, OutputClass);
+			break;
+		case Key_Up:
+			if(e->state() & ShiftButton) dlg->Command(CMD_SHIFTUP, 0L, OutputClass);
+			else dlg->Command(CMD_CURRUP, 0L, OutputClass);
+			break;
+		case Key_Down:
+			if(e->state() & ShiftButton) dlg->Command(CMD_SHIFTDOWN, 0L, OutputClass);
+			else dlg->Command(CMD_CURRDOWN, 0L, OutputClass);
+			break;
+		case Key_Delete:
+			dlg->Command(CMD_DELETE, 0L, OutputClass);
+			break;
+		case Key_Tab:
+			dlg->Command(CMD_TAB, 0L, OutputClass);
+			break;
+		case Key_Backtab:
+			dlg->Command(CMD_SHTAB, 0L, OutputClass);
+			break;
+		case Key_Home:
+			dlg->Command(CMD_POS_FIRST, 0L, OutputClass);
+			break;
+		case Key_End:
+			dlg->Command(CMD_POS_LAST, 0L, OutputClass);
+			break;
+		default:
+			c = e->ascii();
+			if(c >1 && c < 256)
+				if(c == 26) dlg->Command(CMD_UNDO, 0L, OutputClass);
+				else dlg->Command(CMD_ADDCHAR, (void *)(& c), OutputClass);
+			break;
+		}
+	e->accept();
+}
+
+void
+DlgWidget::focusInEvent(QFocusEvent *e)
+{
+	CurrWidgetPos.x = x();		CurrWidgetPos.y = y();
+}
+
+void
+DlgWidget::focusOutEvent(QFocusEvent *e)
+{
+	HideTextCursorObj(OutputClass);
+	if(dlg) dlg->Command(CMD_ENDDIALOG, 0L, OutputClass);
+}
+
+void
+DlgWidget::closeEvent(QCloseEvent *e)
+{
+	HideTextCursorObj(OutputClass);
+	e->ignore();
+	if(dlg){
+		dlg->Command(CMD_UNLOCK, 0L, OutputClass);
+		dlg->Command(CMD_ENDDIALOG, 0L, OutputClass);
+		}
+}
+
+void
+DlgWidget::timerEvent(QTimerEvent *)
+{
+	if(dlg) dlg->Command(CMD_ENDDIALOG, dlg, OutputClass);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void *CreateDlgWnd(char *title, int x, int y, int width, int height, tag_DlgObj *d, DWORD flags)
+{
+	DlgWidget *w;
+	OutputQT *o;
+	int dw, dh;
+
+	w = new DlgWidget(QAppl->focusWidget(), 0, d);
+	w->setCaption(title);
+	if(flags & 0x2) w->setFixedSize(width, height);
+	else w->setFixedSize(width-6, height-16);
+	if(flags & 0x1) {
+		GetDesktopSize(&dw, &dh);
+		w->move((dw>>1) - ((w->width())>>1), (dh>>1) - ((w->height())>>1));
+		}
+	else w->move(CurrWidgetPos.x+x, CurrWidgetPos.y+y);
+	o = new OutputQT(w);
+	o->units = defs.cUnits;
+	o->Erase(0x00c0c0c0L);
+	if(flags & 0x04) {
+		w->startTimer(100);
+		}
+	d->DoPlot(o);
+	w->show();
+	return w;
+}
+
+void LoopDlgWnd() 	//keep message processing running
+{
+	QAppl->processOneEvent();
+}
+
+void CloseDlgWnd(void *hDlg)
+{
+	if(hDlg) {
+		delete((DlgWidget*) hDlg);
+		}
+}
+
+void ShowDlgWnd(void *hDlg)
+{
+	if(hDlg){
+		((DlgWidget*)hDlg)->show();
+		((DlgWidget*)hDlg)->setActiveWindow();
+		((DlgWidget*)hDlg)->raise();
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// OS independent interface to Qt specific classes
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+anyOutput *NewDispClass(GraphObj *g)
+{
+	return new OutputQT(g);
+}
+
+bool DelDispClass(anyOutput *w)
+{
+	if(w) delete (OutputQT*) w;
+	return true;
+}
+
+anyOutput *NewBitmapClass(int w, int h, double hr, double vr)
+{
+	return new BitMapQT(w, h, hr, vr);
+}
+
+bool DelBitmapClass(anyOutput *w)
+{
+	if (w) delete (BitMapQT*) w;
+	return true;
+}
+
diff --git a/QT_Spec.h b/QT_Spec.h
new file mode 100755
index 0000000..582f9b3
--- /dev/null
+++ b/QT_Spec.h
@@ -0,0 +1,284 @@
+//QT_Spec.h, Copyright (c) 2001, 2002, 2003, 2004 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 <qwidget.h>
+#include <qpen.h>
+#include <qpainter.h>
+#include <qprinter.h>
+#include <qmenubar.h>
+#include <qscrollbar.h>
+#include <qdragobject.h>
+#include "TheDialog.h"
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class TxtCurBlink:public QObject {
+	Q_OBJECT
+public:
+	anyOutput *oCopyMark;
+	anyOutput *bmCopyMark;
+
+	TxtCurBlink();
+	void Show();
+	void showCopyMark();
+
+protected:
+	void timerEvent(QTimerEvent *);
+
+private:
+	POINT line[5];
+	bool isVis;
+	int count, cp_mark;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// based on KDEs KSpread source: kspread_table.h
+// copyright (C) 1998, 1999 Torben Weis
+class KSpreadTextDrag:public QTextDrag {
+	Q_OBJECT
+public:
+	KSpreadTextDrag(QWidget *dragSource = 0L, const char *name = 0L);
+	virtual ~KSpreadTextDrag();
+	virtual bool provides(const char *mimeType);
+	void setSpreadData(GraphObj *g) {go = g;};
+	virtual QByteArray encodedData(const char* mime) const;
+	virtual const char* format(int i) const;
+	static bool canDecode(QMimeSource *e);
+	static const char* selectionMimeType();
+
+protected:
+	GraphObj *go;
+};
+
+class RLPGraphDrag:public QTextDrag {
+	Q_OBJECT
+public:
+	RLPGraphDrag(QWidget *dragSource = 0L, const char *name = 0L);
+	virtual ~RLPGraphDrag();
+	virtual bool provides(const char *mimeType);
+	void setGraphData(GraphObj *g);
+	virtual QByteArray encodedData(const char* mime) const;
+	virtual const char* format(int i) const;
+	static bool canDecode(QMimeSource *e);
+	static const char* selectionMimeType();
+
+protected:
+	GraphObj *go1, *go2;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class RLPwidget:public QWidget {
+	Q_OBJECT
+
+public:
+	QScrollBar *HScroll, *VScroll;
+	QPixmap *mempic;
+	RLPwidget(QWidget *par=0, const char *name=0, anyOutput *o = 0,
+		GraphObj *g = 0);
+	~RLPwidget();
+
+public slots:
+	void hScrollEvent(int pos);
+	void vScrollEvent(int pos);
+	void cmOpen();
+	void cmSaveDataAs();
+	void cmExit();
+	void cmNewGraph();
+	void cmNewPage();
+	void cmDelGraph();
+	void cmAddPlot();
+	void cmAbout();
+	void cmAddRowCol();
+	void cmCopy();
+	void cmCut();
+	void cmPaste();
+	void cmCopyGraph();
+	void cmSaveGraphAs();
+	void cmRedraw();
+	void cmZoom25();
+	void cmZoom50();
+	void cmZoom100();
+	void cmZoom200();
+	void cmZoom400();
+	void cmZoomIn();
+	void cmZoomOut();
+	void cmZoomFit();
+	void cmPrint();
+	void cmExport();
+	void cmDelObj();
+	void cmUpdate();
+	void cmDefaults();
+	void cmAddAxis();
+	void cmAddLegend();
+	void cmLayers();
+	void cmUndo();
+	void cmFillRange();
+	void cmtStandard();
+	void cmtDraw();
+	void cmtPolyline();
+	void cmtPolygon();
+	void cmtRectangle();
+	void cmtRoundrect();
+	void cmtEllipse();
+	void cmtArrow();
+	void cmtText();
+	void cmFile1() {openHistoryFile(0);};
+	void cmFile2() {openHistoryFile(1);};
+	void cmFile3() {openHistoryFile(2);};
+	void cmFile4() {openHistoryFile(3);};
+	void cmFile5() {openHistoryFile(4);};
+	void cmFile6() {openHistoryFile(5);};
+
+protected:
+	void paintEvent(QPaintEvent *);
+	void resizeEvent(QResizeEvent *);
+	void closeEvent(QCloseEvent *);
+	void mouseDoubleClickEvent(QMouseEvent *e);
+	void mousePressEvent(QMouseEvent *e);
+	void mouseReleaseEvent(QMouseEvent *e);
+	void mouseMoveEvent(QMouseEvent *e);
+	void keyPressEvent(QKeyEvent *e);
+	void focusInEvent(QFocusEvent *e);
+
+private:
+	QWidget *parent;
+	anyOutput *OutputClass;
+	GraphObj *BaseObj;
+
+	void openHistoryFile(int idx);
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class DlgWidget:public QWidget {
+	Q_OBJECT
+public:
+	QPixmap *mempic;
+	anyOutput *OutputClass;
+
+	DlgWidget(QWidget *par=0, const char *name=0, tag_DlgObj *d = 0);
+	~DlgWidget();
+
+protected:
+	void paintEvent(QPaintEvent *);
+	void mouseDoubleClickEvent(QMouseEvent *e);
+	void mousePressEvent(QMouseEvent *e);
+	void mouseReleaseEvent(QMouseEvent *e);
+	void mouseMoveEvent(QMouseEvent *e);
+	void keyPressEvent(QKeyEvent *e);
+	void focusInEvent(QFocusEvent *e);
+	void focusOutEvent(QFocusEvent *e);
+	void closeEvent(QCloseEvent *e);
+	void timerEvent(QTimerEvent *);
+
+private:
+	QWidget *parent;
+	tag_DlgObj *dlg;
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class BitMapQT:public anyOutput {
+public:
+	QWidget *widget;
+	HatchOut *hgo;
+	QPixmap *mempic;
+	QImage *image;
+	QPen qPen;
+	QPainter qPainter;
+	QFont qFont;
+
+	BitMapQT(GraphObj *g, QWidget *wi, int vr = 98, int hr = 98);
+	BitMapQT(int w, int h, double hr, double vr);
+	~BitMapQT();
+	bool SetLine(LineDEF *lDef);
+	bool SetFill(FillDEF *fill);
+	bool SetTextSpec(TextDEF *set);
+	virtual bool Erase(DWORD Color);
+	virtual bool StartPage() {return true;};
+	bool CopyBitmap(int x, int y, anyOutput* src, int sx, int sy,
+		int sw, int sh, bool invert);
+	bool oGetTextExtent(char *text, int cb, int *width, int *height);
+	bool oGetPix(int x, int y, DWORD *col);
+	bool oDrawIcon(int type, int x, int y);
+	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);
+	bool oArc(int x1, int y1, int x2, int y2, int quads);
+};
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class OutputQT:public BitMapQT {
+public:
+	QScrollBar *HScroll, *VScroll;
+
+	OutputQT(GraphObj *g);
+	OutputQT(DlgWidget *wi);
+	~OutputQT();
+	bool ActualSize(RECT *rc);
+	void Caption(char *txt);
+	void MouseCursor(int cid, bool force);
+	bool SetScroll(bool isVert, int iMin, int iMax, int iPSize, int iPos);
+	bool EndPage();
+	bool UpdateRect(RECT *rc, bool invert);
+	void ShowBitmap(int x, int y, anyOutput* src);
+	void ShowLine(POINT * pts, int cp, DWORD color);
+	void ShowEllipse(POINT p1, POINT p2, DWORD color); 
+	bool SetMenu(int type);
+	void CheckMenu(int mid, bool check);
+	void FileHistory();
+	void CreateNewWindow(GraphObj *g);
+
+private:
+	GraphObj *BaseObj;
+	QMenuBar *menu;
+};
+
+class PrintQT:public anyOutput{
+public:
+	HatchOut *hgo;
+	QPrinter *printer;
+
+	PrintQT(GraphObj *g, char *file);
+	~PrintQT();
+	bool ActualSize(RECT *rc);
+	bool SetLine(LineDEF *lDef);
+	bool SetFill(FillDEF *fill);
+	bool SetTextSpec(TextDEF *set);
+	bool StartPage();
+	bool EndPage();
+	bool Eject();
+	bool oGetTextExtent(char *text, int cb, int *width, int *height);
+	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);
+	bool oArc(int x1, int y1, int x2, int y2, int quads);
+
+private:
+	QPen qPen;
+	QFont qFont;
+	QPainter qPainter;
+	QWMatrix dxf;
+	char *fileName;
+	GraphObj *go;
+	bool bPrinting;
+};
diff --git a/README b/README
new file mode 100755
index 0000000..791b432
--- /dev/null
+++ b/README
@@ -0,0 +1,131 @@
+README for RLPlot, Copyright (c) 2002, 2003 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
+
+
+------------------------------------------------------------------------------
+1. Building RLPlot for Linux
+Before building RLPlot for Linux using the supplied 'Makefile' you probably
+need to make some adaptations. RLPlot uses Trolltech's Qt and the default
+Makefile assumes Qt beeing installed in /usr/local/qt.
+Which version of Qt should be used ? The best version is usually that coming
+with your distribution CDs. If there are several versions installed on your
+machine you may experience crashes especially if you try to copy/paste data
+from one application to another.
+Most problems during compilation of RLPlot are due to missing Qt or installation
+of Qt in a different folder. 
+On most systems where Qt has been installed from the distribution
+CDs the environment variable 'QTDIR' is defined. In this case you may succeed
+by issuing the following command within the RLPlot folder:
+
+      make -e
+
+If that does not work you have to ...
+
+1.1: Find your Qt directory.
+   This is the directory where Qt has been installed. It contains the files
+   ./bin/moc, ./include/qapplication.h ./lib/libqt.so (However, RLPlot needs
+   more than those). If you don't find this directory you must install Qt devel.
+   first from your distribution CDs or directly from www.trolltech.com.
+
+1.2: Modify Makefile. Find the line (close to the top) where QTDIR is defined
+   and fill in the path to the Qt folder.
+   Possible examples include:
+      QTDIR = /usr/lib/qt3
+      QTDIR = /usr/lib/qt-2.3.1
+	  QTDIR = /usr/local/qt-2.3.1
+	  QTDIR = /usr/local/qt-x11-free-3.0.4
+
+	Alternatively you may leave the Makefile unchanged and put a symbolic link
+	into /usr/local (if /usr/local/qt does not yet exist), for example:
+		cd /usr/local
+		ln -s /usr/lib/qt-2.3.1 qt
+	The latter method has the advantage that you can have concurrent versions
+	of Qt installed on your system and need not modify Makefile everytime you
+	download a new version of RLPlot. You may need root privileges to create
+	the link in /usr/local
+
+1.3: Create the RLPlot executable by execting 'make' in the RLplot folder.
+   You can remove some intermediary file exceting 'make clean'.
+   No further installation required. Just execute RLPlot.	   
+
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+Problems and Platform notes:
+Some recent versions of Qt use Open GL which requires additional libraries
+to be included in the Makefile.
+
+Common errors during build:
+
+undefined reference to ...
+   This error usually occurs if a library necessary for link is not specified
+   in the Makefile. You have to find a suitable library and specify it in the
+   LIBS definition (some examples see below). May be you do not need this library
+   and the build is done properly without e.g. '-lGL'.
+   Example: undefined reference to 'glXQueryServerString'
+      libGL.so not specified in LIBS. Add '-lGL' to LIBS or a fully qualified
+	  path to the folder with libGL.so, e.g. -L/usr/X11R6/lib 
+
+cannot find ....
+   This error occurs if a file is not found, possibly one of the libraries in
+   of the LIBS definition. Probably you do not need this library and you can
+   remove it from the LIBS. If this does not help you may be lucky to find it
+   elsewhere (not very promising) or some package needs to be installed.
+   Example: cannot find -lGL
+      libGL.so specified in LIBS but not found in a standard directory
+   Example: cannot find -lqt-mt
+      libqt-mt.so not found. Modify the Makefile from 'QTLIBS= -lqt-mt' to
+      'QTLIBS= -lqt'. If this does not help Qt is not properly installed.
+
+There are some LIBS definitions which might work on your system
+   LIBS = -L$(QTDIR)/lib -L/usr/X11R6/lib
+   LIBS = -lGL -L$(QTDIR)/lib -L/usr/X11R6/lib
+   LIBS = -L/usr/lib -L$(QTDIR)/lib -L/usr/X11R6/lib
+
+------------------------------------------------------------------------------
+2. Building RLPlot for Windows
+To compile RLPlot from the sources you need to install
+MS Visual Studio first. Once you have RLPlot.exe no further installation
+is required. RLPlot does not change the registry. Double click on
+the icon in the explorer window to start the program.
+
+2.1 Building RLPlot.exe using NMAKE
+   Open a DOS or command line window and move to the MSVC folder. Execute
+   VCVARS32.BAT in the bin subdirectory. Change directory to the RLPlot
+   folder. Whenever you close the DOS-box you need to execute VCVARS32.BAT
+   again. Now execute nmake to compile and build RLPlot:
+        nmake -f "Makefile.win"
+   After building RLPlot you may wish to remove temporary files:
+        nmake -f "Makefile.win" clean
+
+2.2 Creating a project in the IDE
+   Create a new project (RLPLot) using the MS Developer Studio creating a
+   Win32 Application. Add the following files to the project: Export.cpp,
+   FileIO.cpp, Output.cpp, PropertyDlg.cpp, rlplot.cpp, rlplot.h, RLPLOT.RC,
+   spreadwi.cpp, TheDialog.cpp, TheDialog.h, UtilObj.cpp, Utils.cpp,
+   Version.h, WinSpec.cpp, WinSpec.h. Now execute [!].
+
+------------------------------------------------------------------------------
+
+Success!
+
+reinhard.lackner at uibk.ac.at
+
+Reinhard Lackner                               Reinhard Lackner
+Ing. Etzelstr. 19                              Inst. f. Zoologie u. Limnologie
+A-6020 Innsbruck                               Technikerstr. 25
+AUSTRIA                                        A-6020 Innsbruck
+                                               AUSTRIA
diff --git a/RLPLOT.ICO b/RLPLOT.ICO
new file mode 100755
index 0000000..0ba1c92
Binary files /dev/null and b/RLPLOT.ICO differ
diff --git a/RLPLOT.RC b/RLPLOT.RC
new file mode 100755
index 0000000..f7ea45f
--- /dev/null
+++ b/RLPLOT.RC
@@ -0,0 +1,337 @@
+//RLPlot.RC, (C)2000, 2001, 2002, 2003, 2004, 2005 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 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_REBOOT      520
+#define CM_SHUTDOWN    521
+#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_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 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"
+#endif
+
+MENU_1 MENU DISCARDABLE
+BEGIN
+    POPUP "&File"
+    BEGIN
+        MENUITEM "&Open",                       CM_OPEN
+        MENUITEM "&Save as",                    CM_SAVEGRAPHAS
+        MENUITEM SEPARATOR
+        MENUITEM "&Print",                      CM_PRINT
+        MENUITEM "&Export",                     CM_EXPORT
+        MENUITEM SEPARATOR
+        MENUITEM "E&xit",                       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 "&Open",                       CM_OPEN
+        MENUITEM "&Save as",                    CM_SAVEDATAAS
+        MENUITEM SEPARATOR
+        MENUITEM "&Print",                      CM_PRINT
+        MENUITEM SEPARATOR
+        MENUITEM "&Reboot",                     CM_REBOOT
+        MENUITEM "Shut &down",                  CM_SHUTDOWN
+        MENUITEM SEPARATOR
+        MENUITEM "E&xit",                       CM_EXIT
+    END
+    POPUP "&Edit"
+    BEGIN
+        MENUITEM "&Undo"                        CM_UNDO
+        MENUITEM SEPARATOR
+        MENUITEM "&Rows/Cols",                  CM_ADDROWCOL
+        MENUITEM SEPARATOR
+        MENUITEM "&Copy",                       CM_COPY
+        MENUITEM "C&ut",						CM_CUT
+        MENUITEM "&Paste",                      CM_PASTE
+        MENUITEM SEPARATOR
+        MENUITEM "&Fill Range",					CM_FILLRANGE
+    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_SAVEGRAPHAS
+        MENUITEM SEPARATOR
+        MENUITEM "&Print",                      CM_PRINT
+        MENUITEM "&Export",                     CM_EXPORT
+        MENUITEM SEPARATOR
+        MENUITEM "E&xit",                       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_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/RLPlot.bmp b/RLPlot.bmp
new file mode 100755
index 0000000..ac10363
Binary files /dev/null and b/RLPlot.bmp differ
diff --git a/RLPlot.xpm b/RLPlot.xpm
new file mode 100755
index 0000000..77b8e92
--- /dev/null
+++ b/RLPlot.xpm
@@ -0,0 +1,47 @@
+/* XPM */
+static char *RLPlot[]={
+"32 32 12 1",
+"j c #000083",
+"i c #0000ff",
+"d c #008100",
+"e c #00ff00",
+"b c #00ffff",
+"g c #830000",
+"c c #838100",
+"a c #838183",
+". c #c5c2c5",
+"h c #ff0000",
+"f c #ffff00",
+"# c #ffffff",
+".##a###a##bbbbbbbbbbbbbbbbbbbbbb",
+"##a###a###bbbbbbbbbbbbbbbbbbbbbb",
+"#a###a#.#a.b.b.b.b.b.b.b.b.b.b.b",
+"a#.#a#.#a#bbbbbbbbbbbbbbbbbbbbbb",
+"###a##.cccbbbbbbbbbbbbbdddbbbbbb",
+".#a###a#c#bbbbbbbbbbbbbbdbbbbbbb",
+"#a#.#a#.c.b.b.b.b.b.b.b.d.b.b.b.",
+"a###a###c#bbbbbbbbbbbdddddddbbbb",
+"#.#a#.#ac#bbbbbbbbbbbdeedeedbbbb",
+".#a.#cccccccbbbbbbbbbdeedeedbbbb",
+"#a###cffcffc.b.ggg.b.deddded.b.b",
+"a###acffcffcbbbbgbbbbdeeeeedbbbb",
+"..#a.cffcffcbbbbgbbbbdeeeeedbbbb",
+"##a##cffcffcbgggggggbdeeeeedbbbb",
+"#a#.#cfcccfcbghhghhgbdeeeeedb.b.",
+"a###acfffffcbghhghhgbdeeeeedbbbb",
+"#.#a#cfffffcbghggghgbdeeeeedbbbb",
+".#a.#cfffffcbghhhhhgbdeeeeedbbbb",
+"#a###cfffffcbghhhhhg.deeeeed.b.b",
+"a#.#acfffffcbghhhhhgbdeeeeedbbbb",
+"###a#cfffffcbghhhhhgbdeeeeedbbbb",
+".#a#.cfffffcighhhhhgideeeeediiii",
+"#a.##cfffffcighhhhhgideeeeedjiij",
+"a###acccccccigggggggidddddddiiii",
+"#.#a###ijiiijiiijiiijiiijiiiijii",
+".#a#.#ijijjiijiiijiiijiiijiiiiii",
+"#a###ijjiiijiijiijjijiijiijiijii",
+"a##.iijiijijjiijjiiijjijjiijjiij",
+".##jijiijjjiijjjiijjjiijjjiijjji",
+"##jjijijiijjjjijjjjijjjjijjjjijj",
+".jjjjjijjjjjijjjjijjjijjjjjjjjij",
+"jjijjjjjjjjjjjjijjjjjjjjjjijjjjj"};
diff --git a/TheDialog.cpp b/TheDialog.cpp
new file mode 100755
index 0000000..7d94615
--- /dev/null
+++ b/TheDialog.cpp
@@ -0,0 +1,4479 @@
+//TheDialog.cpp, Copyright (c) 2001, 2002, 2003, 2004, 2005 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 = 11;
+static unsigned long DlgBGcolor = 0x00c0c0c0L;
+static unsigned long DlgBGhigh = 0x00d0d0d0L;
+TextDEF DlgText = {0x00000000L, 0x00ffffffL, 4.0, 0.0f, 0.0f, 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)
+{
+	int i;
+	RECT rc;
+
+	dlg = 0L;			flags = 0L;		tabstops = 0L;
+	DlgText.iSize = dlgtxtheight;		DlgText.ColBg = DlgBGcolor;
+	type = NONE;		Id = -2;		cContinue = 0;
+	bActive = false;	Result = -1;
+	CurrDisp = 0L;		oldFocus = DialogFocus;		DialogFocus = 0L;
+	oldDefault = DialogDefault;			oldTabStop = DialogTabStop;
+	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 COLBUTTON:
+					dlg[i]->dialog = new ColorButton(this, &tmpl[i],rc, (unsigned long)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 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);
+					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(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 COLBUTTON:		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 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);
+		}
+	if(tabstops) free(tabstops);
+	DialogFocus = oldFocus;			 DialogDefault = oldDefault;
+	DialogTabStop = oldTabStop;
+}
+
+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);
+			}
+		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:
+		mev = (MouseEvent *) tmpl;
+		switch(mev->Action) {
+		case MOUSE_LBDOWN:
+			DialogFocus = NULL;		CurrGO = 0L;
+			bActive = true;
+		case MOUSE_MOVE:
+			if(!(mev->StateFlags & 1))return false;
+			//track mouse and display controls accordingly
+			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:
+			CurrGO = 0L;
+			if(bActive)ForEach(CMD_LBUP, 0, o);
+			return true;
+			}
+		return true;
+	case CMD_ENDDIALOG:
+		d = (Dialog *)tmpl;
+		if(d) {
+			Result = d->Id;		// end dialog by object
+			cContinue = 0;
+			}
+		else if(cContinue >0) {
+			cContinue--;
+			return true;		// no end upon killing the focus
+			}
+		else {
+			Result = 0;			// end dialog with closebox or loose focus
+			}
+		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]) i++;
+			if(!tabstops[i]) i = 0;
+			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:
+				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:
+				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:
+		bActive = true;
+		if(DialogFocus)return DialogFocus->Command(cmd, tmpl, o);
+        else return false;
+	case CMD_ADDCHAR:
+		if(!tmpl) return false;
+		bActive = true;
+		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:
+		for(i = 0; i < cDlgs; i++)
+			if(dlg[i] && dlg[i]->dialog) dlg[i]->dialog->Command(CMD_UNLOCK, 0L, 0L);
+		break;
+		}
+	return false;
+}
+
+void
+DlgRoot::DoPlot(anyOutput *o)
+{
+	int i;
+
+	if(tabstops) for(i = 0; i < cDlgs; tabstops[i++] = 0);
+	if(o)CurrDisp = o;
+	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:
+					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]) 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 i;
+
+	i = FindIndex(id);
+	if(i && dlg[i]) return dlg[i]->dialog->GetText(id, txt);
+	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)) DoPlot(CurrDisp);
+	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;
+
+	//return each result only once !
+	ret = Result;
+	Result = -1;
+	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]->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++) {
+							dlg[next]->dialog->Command(CMD_ADDCHILD, (void*)dlg[i = FindIndex(i)]->dialog, 0L);
+							if(i) i = dlg[i]->next;
+							}
+						}
+					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]->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]->next);
+			}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]) 
+		 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)){
+			if(bLBstate && !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 = strdup(text);
+	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_ADDCHAR:
+		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)
+{
+	TextDef.Font = FONT_COURIER;
+#ifdef _WINDOWS
+//	TextDef.fSize unchanged
+#else
+	TextDef.fSize *= .8;
+#endif
+	Fill.color = 0x00ffffffL;	Line.color = 0x00000000L;	cont = 0L;
+	if(text) cont = new mLabel(0L, 0L, rec.left+3, rec.top+1, &TextDef, text);
+}
+
+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:
+		if(bChecked && CurrGO && CurrGO->parent == cont){
+			CurrGO->Command(cmd, tmpl, o);
+			DoPlot(o);			o->MrkMode = MRK_NONE;
+			return CurrGO->Command(CMD_REDRAW, 0L, o);	//text cursor !
+			}
+	case CMD_CURRUP:		case CMD_CURRDOWN:
+		if(bChecked && CurrGO && CurrGO->parent == cont){
+			cont->Command(cmd, tmpl, o);
+			DoPlot(o);			o->MrkMode = MRK_NONE;
+			return true;
+			}
+		break;
+	case CMD_SETTEXT:
+		if(cont)DeleteGO(cont);
+		if(tmpl) cont = new mLabel(0L, 0L, cr.left+3, cr.top+1, &TextDef, (char*)tmpl);
+		return true;
+		}
+	return false;
+}
+
+void
+TextBox::DoPlot(anyOutput *o)
+{
+	o->SetLine(&Line);		o->SetFill(&Fill);
+	o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
+	if(cont)cont->DoPlot(o);
+	o->UpdateRect(&cr, false);
+}
+
+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(cont) bChecked = cont->Command(CMD_SELECT, &p1, o);
+		}
+	return false;
+}
+
+bool
+TextBox::GetText(int id, char *txt)
+{
+	if(cont) return cont->Command(CMD_ALLTEXT, txt, 0L);
+	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, unsigned long color)
+	:Dialog(par, desc, rec)
+{
+	col = color;
+}
+
+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(IsInRect(&cr, x, y)) {
+		if((flags & TOUCHEXIT) && parent) 
+			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 = strdup(text);
+	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) Text = strdup((char *)tmpl);
+		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, 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 = strdup(text);
+	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)Text = strdup((char *)tmpl);
+		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, 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) txt = strdup(text);
+	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)txt = strdup((char *)tmpl);
+		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)
+{
+	if(WWWbrowser && WWWbrowser[0] && txt && (flags & HREF) && IsInRect(&cr, x, y)) {
+		sprintf(TmpTxt, "%s %s", WWWbrowser, txt);
+		if(parent)parent->Command(CMD_CONTINUE, 0L, o);
+#ifdef _WINDOWS
+		WinExec(TmpTxt, SW_SHOWMAXIMIZED); 
+#else
+		strcat(TmpTxt, " &");
+		system(TmpTxt);
+#endif
+		if(parent)parent->Command(CMD_ENDDIALOG, 0L, o);
+		return true;
+		}
+	return false;
+}
+
+void
+Text::SetColor(int id, DWORD color)
+{
+	TextDef.ColTxt = color;
+}
+
+InputText::InputText(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
+	:Dialog(par, desc, rec)
+{
+	POINT p1, p2;
+
+	p1.x = rec.left+1;					p1.y = rec.top+1;
+	p2.x = rec.right-1;					p2.y = rec.bottom-1;
+	if(type == INCDECVAL1) cr.right = p2.x = cr.right - 7*xbase;
+	Line.color = 0x00000000L;
+	Text = new EditText(0L, p1, p2, text);
+	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)){
+			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_ADDCHAR:
+		if(Text && bActive && !(flags & NOEDIT)){
+			if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, 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);
+				}
+			}
+		return false;
+	case CMD_SETTEXT:
+		if(Text) {
+			if(Text->text) free(Text->text);
+			if(tmpl)Text->text = strdup((char*)tmpl);
+			else Text->text = 0L;
+			return false;
+			}
+		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) {
+				if(Text->text) free(Text->text);
+				Text->text = strdup(TmpTxt+1);
+				DoPlot(o);
+				}
+			}
+		break;
+		}
+	return false;
+}
+
+void
+InputText::DoPlot(anyOutput *o)
+{
+	POINT pts[5];
+
+	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 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) {
+		sscanf(Text->text, "%d", val);
+		return true;
+		}
+	return false;
+}
+
+bool
+InputText::GetText(int id, char *txt)
+{
+	if(Text && Text->text && Text->text[0]) {
+		strcpy(txt, 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) {
+		DialogFocus = this;
+		if(parent) parent->Command(CMD_FOCTXT, (void*)this, 0L);
+		o->SetTextSpec(&TextDef);
+		Text->Command(CMD_MOUSE_EVENT, o, (DataObj *)mev);
+		}
+}
+
+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);
+		}
+}
+
+InputValue::InputValue(tag_DlgObj *par, DlgInfo * desc, RECT rec, double *value)
+	:InputText(par, desc, rec, 0L)
+{
+	WriteNatFloatToBuff(TmpTxt, *value);
+	if(Text) Text->text = strdup(TmpTxt+1);
+}
+
+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 = strdup(TmpTxt+1);
+	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[] = {
+		{1, 2, 0, ISRADIO, RADIO0, 0L, x1, y1, 8, 8},
+		{2, 3, 0, ISRADIO, RADIO0, 0L, x2, y1, 8, 8},
+		{3, 4, 0, ISRADIO, RADIO0, 0L, x3, y1, 8, 8},
+		{4, 5, 0, ISRADIO, RADIO0, 0L, x1, y2, 8, 8},
+		{5, 6, 0, ISRADIO, RADIO0, 0L, x2, y2, 8, 8},
+		{6, 7, 0, ISRADIO, RADIO0, 0L, x3, y2, 8, 8},
+		{7, 8, 0, ISRADIO, RADIO0, 0L, x1, y3, 8, 8},
+		{8, 9, 0, ISRADIO, RADIO0, 0L, x2, y3, 8, 8},
+		{9, 0, 0, ISRADIO | LASTOBJ, RADIO0, 0L, x3, y3, 8, 8}};
+
+	txt.ColTxt = 0x00808080L;	txt.ColBg = 0x00ffffff;
+	txt.fSize = 8.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;
+	txt.text = strdup("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:
+		if(parent) 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 == 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)Text = strdup(txt);
+	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)
+	:Group(par, desc, rec)
+{
+	if(sh->text)Text = strdup(sh->text);
+	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;
+}
+
+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;
+
+	Line.color = 0x00000000L;
+	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);
+	o->oTextOut(rctab.right - 6, rctab.top + 1, Text, 0);
+	Group::DoPlot(o);
+	o->UpdateRect(&cr, false);
+}
+
+bool
+TabSheet::Select(int x, int y, anyOutput *o)
+{
+	if(IsInRect(&rctab, x, y)) {
+		if(parent) parent->Command(CMD_RADIOBUTT, (void *)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; strings[i] = strdup(list[i]), i++);
+		}
+}
+
+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+1));
+	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 - startY + hcr.top;
+	mrk.right = hcr.right-2;	mrk.bottom = mrk.top + TextDef.iSize;
+	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, 
+			(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;
+		if(il >= ns || il < 0){
+			o->UpdateRect(&hcr, false);
+			return false;
+			}
+		cl = il;
+		mrk.left = hcr.left+2;		mrk.top = (il)*TextDef.iSize - startY + hcr.top;
+		mrk.right = hcr.right-2;	mrk.bottom = mrk.top + TextDef.iSize;
+		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, 
+			(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)
+{
+	if(strings && ns && cl >= 0 && cl < ns){
+		strcpy(txt, 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 * 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;
+			for(i = 0; i < ns; i++) {
+				bmp->oTextOut(5, i*TextDef.iSize, 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+1));
+	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 - startY + hcr.top;
+	mrk.right = hcr.right-2;	mrk.bottom = mrk.top + TextDef.iSize;
+	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, 
+			(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;
+		if(il >= ns || il < 0){
+			o->UpdateRect(&hcr, false);
+			return false;
+			}
+		cl = il;
+		mrk.left = hcr.left+2;		mrk.top = (il)*TextDef.iSize - startY + hcr.top;
+		mrk.right = hcr.right-2;	mrk.bottom = mrk.top + TextDef.iSize;
+		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, 
+			(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);
+			}
+		}
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Select color out of predefined palette
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+DWORD GetNewColor(DWORD oldColor)
+{
+	DlgInfo *ColDlg, ColDlgTmpl[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 200, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 200, 25, 45, 12},
+		{3, 7, 4, CHECKED | ISPARENT, GROUPBOX, (void*)" RGB ", 200, 55, 45, 40},
+		{4, 5, 0, 0x0L, LTEXT, (void*)0L, 210, 60, 45, 8},
+		{5, 6, 0, 0x0L, LTEXT, (void*)0L, 210, 70, 45, 8},
+		{6, 0, 0, 0x0L, LTEXT, (void*)0L, 210, 80, 45, 8},
+		{7, 0, 8, CHECKED | ISPARENT, GROUP, 0L, 0, 0, 0, 0}};
+	DWORD currcol, newcol;
+	int ilevels[] = {0x0, 0x40, 0x80, 0xc0, 0xe0, 0xff};
+	int i, ir, ig, ib, col, row, res;
+	DlgRoot *Dlg;
+	void *hDlg;
+
+	if(!(ColDlg = (DlgInfo *)malloc(230 * sizeof(DlgInfo))))return oldColor;
+	memcpy(ColDlg, ColDlgTmpl, 14 * sizeof(DlgInfo));
+	for(ir=row=col=0, i= 7; 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 = COLBUTTON;			ColDlg[i].first = 0;
+		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 *)currcol;
+		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++;
+		}
+	ColDlg[i-1].next = 0;
+	ColDlg[i-1].flags |= LASTOBJ;
+	newcol = currcol = oldColor;
+	Dlg = new DlgRoot(ColDlg);
+	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);
+	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;
+					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);
+					}
+				}
+			break;
+			}
+		}while (res < 0);
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	free(ColDlg);
+	return currcol;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Configure 3D shading
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void ConfShade(FillDEF *oldfill)
+{
+	FillDEF newFill;
+	DlgInfo ShadeDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 95, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 95, 25, 45, 12},
+		{3, 4, 100, CHECKED | ISPARENT, GROUP, NULL, 0, 0, 0, 0},
+		{4, 5, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)oldfill->color, 60, 10, 15, 10},
+		{5, 6, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)oldfill->color2, 60, 37, 15, 10},
+		{6, 7, 0, OWNDIALOG | TOUCHEXIT, COLBUTTON, (void*)oldfill->color, 60, 49, 15, 10},
+		{7, 8, 0, 0x0L, RTEXT, (void*)"HI-color:", 25, 37, 30, 9},
+		{8, 9, 0, 0x0L, RTEXT, (void*)"LO-color:", 25, 49, 30, 9},
+		{9, 0, 0, 0x0L, SHADE3D, &newFill, 95, 50, 22, 20},
+		{100, 101, 0, TOUCHEXIT, RADIO1, (void*)"fixed color:", 10, 10, 50, 9},
+		{101, 0, 0, TOUCHEXIT | LASTOBJ, RADIO1, (void*)"use light sorce:", 10, 25, 60, 9}};
+	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))) 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(8.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;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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};
+	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, COLBUTTON, 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, COLBUTTON, 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);
+	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
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool ShowLayers(GraphObj *root)
+{
+	char curr_name[50];
+	DlgInfo PageDlg[] = {
+		{1, 3, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 165, 10, 45, 12},
+		{3, 20, 100, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{20, 550, 0, 0x0L, CHECKPIN, 0L, 10, 0, 12, 8},
+		{100, 101, 0, TOUCHEXIT, TREEVIEW, (void*)root, 10, 15, 100, 70},
+		{101, 102, 0, 0x0L, EDTEXT, (void*)curr_name, 120, 25, 90, 10},
+		{102, 200, 150, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
+		{150, 151, 0, TOUCHEXIT, RADIO1, (void*)"hidden", 120, 35, 40, 9},
+		{151, 0, 0, TOUCHEXIT, RADIO1, (void*)"visible", 160, 35, 40, 9},
+		{200, 500, 0, 0x0L, LTEXT, (void*)"Layers:", 10, 5, 110, 9},
+		{500, 501, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
+		{501, 0, 0, HIDDEN | TOUCHEXIT, TRASH, 0L, 125, 72, 15, 15},
+		{550, 0, 0, HIDDEN | TOUCHEXIT, CONFIG, 0L, 140, 72, 15, 15},
+		{600, 601, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 125, 50, 15, 15},
+		{601, 602, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 145, 50, 15, 15},
+		{602, 603, 0, TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 165, 50, 15, 15},
+		{603, 0, 0, LASTOBJ | TOUCHEXIT, ODBUTTON, (void*)OD_DrawOrder, 185, 50, 15, 15}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, cl;
+	bool bContinue = false;
+	ObjTree *ot = 0L;
+	GraphObj *cgo = 0L;
+
+	if(!root) return false;
+	sprintf(curr_name, "(root)");
+	if(!(Dlg = new DlgRoot(PageDlg)))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) {
+					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){
+					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;
+	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;
+	int res, cnt = 5;
+
+	if(!show) return;
+	if(!(Dlg = new DlgRoot(BannerDlg)))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);
+	FindBrowser();
+	SpreadMain(true);
+	ShowDlgWnd(hDlg);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 0:						//loose focus: get it again
+			ShowDlgWnd(hDlg);
+			res = -1;
+			break;
+		case -2:					//Timer event
+			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-2004 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-2004 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))) {
+		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
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+DoSpShSize(DataObj *dt)
+{
+	TabSHEET tab1 = {0, 45, 10, "Dimensions"};
+	TabSHEET tab2 = {45, 105, 10, "Width and Height"};
+	char txt1[40], txt2[40];
+	double fw, cw, ch;
+	DlgInfo SSDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 115, 10, 40, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 115, 25, 40, 12},
+		{3, 0, 5, CHECKED | ISPARENT, GROUP, NULL, 138, 40, 55, 12},
+		{5, 6, 100, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 105, 80},
+		{6, 0, 200, ISPARENT, SHEET, &tab2, 5, 10, 105, 80},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"number of columns:", 15, 25, 60, 8},
+		{101, 102, 0, 0x0L, EDTEXT, txt1, 40, 37, 40, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void*)"number of rows:", 15, 52, 60, 8},
+		{103, 0, 0, 0x0L, EDTEXT, txt2, 40, 64, 40, 10},
+		{200, 201, 0, 0x0L, RTEXT, (void*)"row buttons", 10, 34, 40, 8},
+		{201, 202, 0, 0x0L, EDVAL1, &fw, 52, 34, 25, 10},
+		{202, 203, 0, 0x0L, LTEXT, (void*)"[digits]", 79, 34, 20, 8},
+		{203, 204, 0, 0x0L, RTEXT, (void*)"column width", 10, 49, 40, 8},
+		{204, 205, 0, 0x0L, EDVAL1, &cw, 52, 49, 25, 10},
+		{205, 206, 0, 0x0L, LTEXT, (void*)"[digits]", 79, 49, 20, 8},
+		{206, 207, 0, 0x0L, RTEXT, (void*)"text size", 10, 64, 40, 8},
+		{207, 208, 0, 0x0L, EDVAL1, &ch, 52, 64, 25, 10},
+		{208, 0, 0, LASTOBJ, LTEXT, (void *) Units[defs.cUnits].display, 79, 64, 20, 8}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int w1, w2, h1, h2, res, celldim[3];
+	bool bRet = false;
+	double fw1, cw1, ch1;
+
+	if(!dt || !dt->GetSize(&w1, &h1)) return false;
+	dt->Command(CMD_GET_CELLDIMS, &celldim, 0L);
+	fw1 = fw = NiceValue(((double)celldim[0])/((double)(celldim[2]-3)/2.0));
+	cw1 = cw = NiceValue(((double)celldim[1])/((double)(celldim[2]-3)/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;
+	sprintf(txt1, "%d", w1);
+	sprintf(txt2, "%d", h1);
+	Dlg = new DlgRoot(SSDlg);
+	hDlg = CreateDlgWnd("Change spread sheet settings", 50, 50, 320, 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, txt1) && Dlg->GetText(103, txt2)) {
+				w2 = atol(txt1);		h2 = atol(txt2);
+				w2 = w2 > 0 ? w2: w1;	h2 = h2 > 0 ? h2: h1;
+				}
+			else res = -1;
+			break;
+			}
+		}while(res <0);
+	if(res == 1){
+		Dlg->GetValue(207, &ch);
+		if(ch != ch1 && ch > 0.001) {
+			switch(defs.cUnits) {
+			case 1:		celldim[2] = iround(ch/0.0259183673)+2;			break;
+			case 2:		celldim[2] = iround(ch*98.0)+2;					break;
+			default:	celldim[2] = iround(ch/0.259183673)+2;			break;
+				}
+			dt->Command(CMD_TEXTSIZE, (void*)(& celldim[2]), 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;
+	return bRet;
+}
+
+bool GetCopyRange(RECT *rc, DataObj *d)
+{
+	char text1[80];
+	DlgInfo CopyDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 115, 5, 40, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 115, 20, 40, 12},
+		{3, 4, 0, 0x0L, PUSHBUTTON, (void*)"copy all", 115, 40, 40, 12},
+		{4, 5, 0, 0x0L, LTEXT, (void*)"enter range to copy from:", 10, 10, 80, 9},
+		{5, 0, 0, 0x0L, EDTEXT, text1, 10, 25, 90, 10},
+		{800, 0, 0, LASTOBJ, NONE, 0L, 0, 0, 0, 0}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int r = rc->left, c = rc->top, res, width = 1, height = 1;
+	bool bRet = false;
+	AccRange *rC;
+
+	sprintf(TmpTxt, "%s", Int2ColLabel(rc->right-1, false));
+	sprintf(text1, "%s%d:%s%d", Int2ColLabel(rc->left, false), rc->top+1, TmpTxt, rc->bottom);
+	if(d) d->GetSize(&width, &height);
+	if(!(Dlg = new DlgRoot(CopyDlg))) return false;
+	hDlg = CreateDlgWnd("Copy data", 50, 50, 322, 140, Dlg, 0x0L);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res){
+		case 3:
+			sprintf(text1, "a1:%s%d", Int2ColLabel(width-1, false), height);
+			Dlg->SetText(5, text1);
+			Dlg->DoPlot(0L);
+			res = -1;
+			break;
+			}
+		}while(res <0);
+	if(res == 1 && Dlg->GetText(5, TmpTxt) && (rC = new AccRange(TmpTxt))) {
+		if(rC->CountItems() >0) {
+			bRet = true;		rC->BoundRec(rc);
+			}
+		delete rC;
+		}
+	CloseDlgWnd(hDlg);			delete Dlg;
+	return bRet;
+}
+
+bool FillSsRange(DataObj *d, char **range)
+{
+	TabSHEET tab1 = {0, 37, 10, "fill range "};
+	char *ra = range ? *range:0L;
+	double startval = 1.0, stepval = 1.0;
+	static char *formula = 0L;
+	if(!formula && CurrText && CurrText->isFormula()) {
+		if(CurrText->GetText(TmpTxt,TMP_TXT_SIZE)) formula = strdup(TmpTxt);
+		}
+	if(!formula) formula = strdup("=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, EDTEXT, (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, 0x0L, 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;
+
+	if(!d || !range) return false;
+	if(!(Dlg = new DlgRoot(RangeDlg))) 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)) {
+				InfoBox("Range not specified\nor not valid.");
+				bContinue = true;				res = -1;
+				}
+			else ra = strdup(TmpTxt);
+			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);
+	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)) {
+				for( ; rF->GetNext(&col, &row); startval += stepval) {
+					d->SetValue(row, col, startval);
+					}
+				delete rF;							bRet = true;
+				}
+			}
+		else if(Dlg->GetCheck(8)) {
+			if(formula) free(formula);	formula = 0L;
+			if((rF = new AccRange(ra)) && rF->GetFirst(&col, &row) && 
+				Dlg->GetText(22, TmpTxt) && (formula = strdup(TmpTxt))){
+				r1 = row;		c1 = col;
+				for( ; rF->GetNext(&col, &row); startval += stepval) {
+					MoveFormula(d, formula, TmpTxt, col-c1, row-r1);
+					d->SetText(row, col, TmpTxt);
+					}
+				delete rF;							bRet = true;
+				}
+			}
+		free(ra);
+		}
+	CloseDlgWnd(hDlg);			delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Get resolution and size for exported bitmap
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool GetBitmapRes(double *dpi, double *width, double *height, char *header)
+{
+	DlgInfo ResDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 105, 10, 35, 12},
+		{2, 100, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 105, 25, 35, 12},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"Image properties:", 10, 10, 50, 9},
+		{101, 102, 0, 0x0L, RTEXT, (void*)"width", 10, 22, 35, 9},
+		{102, 103, 0, 0x0L, EDVAL1, width, 47, 22, 30, 10},
+		{103, 104, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 79, 22, 20, 8},
+		{104, 105, 0, 0x0L, RTEXT, (void*)"height", 10, 34, 35, 9},
+		{105, 106, 0, 0x0L, EDVAL1, height, 47, 34, 30, 10},
+		{106, 107, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 79, 34, 20, 8},
+		{107, 108, 0, 0x0L, RTEXT, (void*)"resolution", 10, 46, 35, 9},
+		{108, 109, 0, 0x0L, EDVAL1, dpi, 47, 46, 30, 10},
+		{109, 110, 0, LASTOBJ, LTEXT, (void *) "dpi", 79, 46, 20, 8}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	bool bRet = false;
+	int res;
+	
+	if(!(Dlg = new DlgRoot(ResDlg))) return false;
+	if(!(hDlg = CreateDlgWnd(header, 50, 50, 300, 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);
+	if(res == 1) {
+		bRet = true;
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	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 ? COLBUTTON : 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);
+			}
+		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, COLBUTTON, (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);
+			}
+		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, COLBUTTON, (void *)ODLine.color, 42, 12, 25, 10},
+	{105, 106, 0, 0x0L, RTEXT,(void*)"fill color" , 0, 24, 40, 8},
+	{106, 107, 0, TOUCHEXIT | OWNDIALOG, COLBUTTON, (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);
+			}
+		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:
+						sprintf(TmpTxt, " %s  (%.1lf x %.1lf cm)", p_formats[i].name, 
+							p_formats[i].mwidth/10.0, p_formats[i].mheight/10.0);
+						break;
+					case 2:
+						sprintf(TmpTxt, " %s  (%.2lf x %.2lf inch)", p_formats[i].name, 
+							p_formats[i].iwidth, p_formats[i].iheight);
+						break;
+					default:
+						sprintf(TmpTxt, " %s  (%.0lf x %.0lf mm)", p_formats[i].name, 
+							p_formats[i].mwidth, p_formats[i].mheight);
+						break;
+						}
+					dispsize[i] = strdup(TmpTxt);
+					}
+				else dispsize[i] = strdup(" 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)){
+				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)){
+				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);
+			}
+		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)){
+				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 Dlg->GetInt(110, &axisplot_sel);
+		if(o) *((int*)o) = axisplot_sel;
+		}
+}
diff --git a/TheDialog.h b/TheDialog.h
new file mode 100755
index 0000000..d3ea251
--- /dev/null
+++ b/TheDialog.h
@@ -0,0 +1,527 @@
+//TheDialog.h, Copyright (c) 2001, 2002, 2003, 2004, 2005 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, COLBUTTON, FILLBUTTON, SHADE3D, LINEBUTT, SYMBUTT,
+	FILLRADIO, SYMRADIO, CHECKBOX, RADIO0, RADIO1, RADIO2, LTEXT, RTEXT, CTEXT, EDTEXT, 
+	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
+
+//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) {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;
+
+	DlgRoot(DlgInfo *tmpl);
+	~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);
+	bool SetText(int id, char *txt);
+	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 cDlgs, Result, cContinue;
+	Dialog *oldFocus, *oldDefault, *oldTabStop;
+	bool bActive;
+	Dialog **tabstops;
+	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);
+	bool GetText(int id, char *txt);
+
+private:
+	mLabel *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, unsigned long 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);
+
+private:
+	char *txt;
+};
+
+class InputText:public Dialog {
+public:
+	EditText *Text;
+
+	InputText(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
+	~InputText();
+	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);
+	virtual void MBtrack(MouseEvent *mev, anyOutput *o);
+	void Activate(int id, bool active);
+
+private:
+	anyOutput *Disp;
+};
+
+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);
+	~TabSheet();
+	void DoPlot(anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+
+private:
+	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);
+	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 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);
diff --git a/UtilObj.cpp b/UtilObj.cpp
new file mode 100755
index 0000000..c604c25
--- /dev/null
+++ b/UtilObj.cpp
@@ -0,0 +1,2950 @@
+//UtilObj.cpp, (c)2000, 2001, 2002, 2003, 2004, 2005 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 <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.0f, 1.0f, 0x00d8d8d8L, 0L};
+static LineDEF ETbgna = {0.0f, 1.0f, 0x00ffffffL, 0L};
+static LineDEF ETbgmn = {0.0f, 1.0f, 0x00000000L, 0L};
+static LineDEF ETbgma = {0.0f, 1.0f, 0x00ffff00L, 0L};
+extern const LineDEF BlackLine = {0.0f, 1.0f, 0x00000000L, 0L};
+
+static FillDEF ETfbnn = {FILL_NONE, 0x00d8d8d8L, 1.0f, NULL, 0x00ffffffL};
+static FillDEF ETfbna = {FILL_NONE, 0x00ffffffL, 1.0f, NULL, 0x00ffffffL};
+static FillDEF ETfbmn = {FILL_NONE, 0x00000000L, 1.0f, NULL, 0x00ffffffL};
+static FillDEF ETfbma = {FILL_NONE, 0x00ffff00L, 1.0f, NULL, 0x00ffffffL};
+
+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, POINT where, POINT right, char *msg)
+{
+	loc.x = where.x;				loc.y = where.y;
+	crb.x = rb.x = right.x;			crb.y = rb.y = right.y;
+	if(msg && msg[0]) text = strdup(msg);
+	else text = 0L;						Value = 0.0;
+	CursorPos = length = Align = 0;		type = ET_UNKNOWN;		TextCol=0x00000000L;
+	bgLine = &ETbgnn;					bgFill = &ETfbnn;		parent = par;
+	FindType();
+	m1 = m2 = -1;						//cursor positions track marks
+}
+
+EditText::EditText(void *par, char *msg)
+{
+	loc.x = loc.y = crb.x = rb.x = crb.y = rb.y = 0;	Value = 0.0;
+	if(msg && msg[0]) text = strdup(msg);
+	else text = 0L;
+	CursorPos = length = Align = 0;		type = ET_UNKNOWN;		TextCol=0x00000000L;
+	bgLine = &ETbgnn;					bgFill = &ETfbnn;		parent = par;
+	FindType();
+	m1 = m2 = -1;						//cursor positions track marks
+}
+
+EditText::~EditText()
+{
+	if(CurrText == this)	CurrText = 0L;
+	if(text) free(text);	text = 0L;
+}
+
+bool
+EditText::AddChar(int ci, anyOutput *Out, void *data_obj)
+{
+	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(text)length = strlen(text);
+	else length = 0;
+	if(text) tmp = (char *)realloc(text, length+2);
+	else tmp = (char *)calloc(2, sizeof(char));
+	if(!tmp) return false;
+	text = tmp;
+	if(parent) ((DataObj*)parent)->Command(CMD_MRK_DIRTY, 0L, 0L);
+	byte1 = byte2 = 0;
+	//replace mark by character if mark exists
+	if(m1 > -1 && m2 > -1) Command(CMD_DELETE, (anyOutput *)0L, (DataObj *)0L);
+	byte1 = text[CursorPos];
+	i = CursorPos;
+	text[i++] = c;
+	while(byte1) {
+		byte2 = byte1;			byte1 = text[i];			text[i++] = byte2;
+		}
+	text[i] = byte1;
+	CursorPos++;
+	Redraw(Out, true);
+	MyPos.y = loc.y;
+	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);
+	return true;
+}
+
+void
+EditText::Update(int select, anyOutput *Out, POINT *MousePos)
+{
+	POINT MyPos;
+
+	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_VTOP | TXA_HLEFT;
+		case 1:							//active spread sheet cell with cursor
+			if((type & 0xff) == ET_FORMULA) Align = TXA_VTOP | TXA_HLEFT;
+			if(!text && !(text = (char *) calloc(2, sizeof(char))))return;
+			if(CursorPos > (int)strlen(text)) CursorPos = (int)strlen(text);
+			bgLine = &ETbgna; bgFill = &ETfbna; TextCol = 0x00000000L;
+			Redraw(Out, true);
+			MyPos.y = loc.y;
+			MyPos.x = Align & TXA_HRIGHT ? crb.x - 2 : loc.x + 2;
+			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);
+			break;
+		case 2:							//inactive spreadsheet cell
+			if((type & 0xff) == ET_FORMULA) Align = TXA_VTOP | TXA_HRIGHT;
+			if(crb.x > rb.x) {
+				crb.x = rb.x;	crb.y = rb.y;
+				}
+			if(CurrText == this) FindType();
+			bgLine = &ETbgnn; bgFill = &ETfbnn; TextCol = 0x00000000L;
+			Redraw(Out, true);
+			break;
+		case 10:						//value filled in by external app.
+			if(text && text[0]) {
+				type = ET_VALUE;
+				Align = TXA_VTOP | TXA_HRIGHT;
+				sscanf(text, "%lf", &Value);
+				}
+			break;
+		case 20:						//update value only
+			FindType();
+			break;
+		}
+}
+
+bool
+EditText::Redraw(anyOutput *Out, bool display)
+{
+	RECT rc;
+	POINT MyPos;
+	char *txt, tmptxt[80];
+	int i, j, w, h, o_crbx;
+	bool b_clip = false;
+	anyOutput *opc;
+	anyResult *fmres;
+
+	o_crbx = crb.x;			crb.x = rb.x;				crb.y = rb.y;
+	if (Out) {
+		if(((type & 0xff) == ET_UNKNOWN) && text && text[0]) FindType();
+		Out->TxtSet.Align = Align;
+		Out->TxtSet.ColTxt = TextCol;
+		Out->TxtSet.ColBg = bgLine->color;
+		if(text && text[0]) {
+			Out->oGetTextExtent(text, strlen(text), &w, &h);
+			if(CurrText == this) {
+				while((crb.x - loc.x) < (w+(h>>1))) crb.x += (rb.x - loc.x);
+				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;				rc.bottom = crb.y;
+		Out->oRectangle(loc.x, loc.y, crb.x-1, crb.y-1);
+		if(!text && (type & 0xff) == ET_VALUE){
+			sprintf(tmptxt, "%g", Value);
+			text = strdup(tmptxt);
+			}
+		if(text && text[0]){
+			if((type & 0xff) == ET_FORMULA && (bgLine == &ETbgnn || bgLine == &ETbgmn)) {
+				Out->TxtSet.Align = TXA_HLEFT;
+				if(type & ET_CIRCULAR) strcpy (tmptxt, "#CIRC.");
+				else if((fmres = do_formula((DataObj*)parent, text+1)) && fmres->type != ET_ERROR) {
+					b_clip = false;		//DEBUG: assume column wide enough for number
+					if(fmres->type == ET_VALUE) {
+						sprintf(tmptxt, "%g", Value = fmres->value);
+						for(i=j=0; i < 20; i++) {
+							while(i>3 && tmptxt[i] == '0' && tmptxt[j-2] == 'e') i++;
+							tmptxt[j++] = tmptxt[i];
+							}
+						tmptxt[j] = 0;
+						Out->TxtSet.Align = TXA_HRIGHT;
+						}
+					else if(fmres->type == ET_TEXT) {
+						if(fmres->text && strlen(fmres->text)<sizeof(tmptxt)) strcpy(tmptxt, fmres->text);
+						else if(fmres->text) sprintf(tmptxt,"#SIZE");
+						else tmptxt[0] = 0;
+						}
+					else strcpy(tmptxt, "#VALUE");
+					}
+				else sprintf(tmptxt, "#ERROR");
+				txt = tmptxt;
+				}
+			else txt = text;
+			MyPos.y = loc.y;
+			if(Out->TxtSet.Align & TXA_HRIGHT) {	//right justified text
+				MyPos.x = crb.x-2;
+				}
+			else {									//left justified text
+				MyPos.x = loc.x +2;
+				}
+			if(b_clip && (opc = NewBitmapClass(w+22, h+2, 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_VTOP;
+				opc->oTextOut(2, 0, txt, 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-2, rc.bottom-rc.top-2, false);
+				DelBitmapClass(opc);
+				}
+			else {
+				scroll_dist = 0;
+				Out->oTextOut(MyPos.x, loc.y, txt, 0);
+				}
+			}
+		if(display) {
+			if(!(Out->UpdateRect(&rc, false))) return false;
+			if(m1 != m2 && m1 >=0 && m2 >=0) {
+				if (m1 >m2) Swap(m1, m2);
+				rc.left = mx1;		rc.right = mx2;
+				Out->ShowMark(&rc, MRK_INVERT);
+				Out->MrkMode = MRK_NONE;
+				}
+			}
+		return true;
+	}
+	return false;
+}
+
+void
+EditText::Mark(anyOutput *Out, int mark)
+{
+	LineDEF *ol = bgLine;
+	FillDEF *of = bgFill;
+	DWORD ocol = TextCol;
+
+	m1 = m2 = -1;
+	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 = 0x00ffffffL;
+		break;
+	case 3:				//mark active
+		bgLine = &ETbgma; bgFill = &ETfbma; TextCol = 0x00ff0000L;
+		break;
+		}
+	Redraw(Out, true);
+	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;
+
+	MyPos.y = loc.y;
+	MyPos.x = Align & TXA_HRIGHT ? crb.x - 2 : loc.x + 2;
+	if(!(text)) return false;
+	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);
+		case CMD_SETFONT:
+			if (!text || !text[0]) return false;
+			type = ET_TEXT;
+			if(m1 > -1 && m2 > -1) {
+				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 = strlen(tag1));	m2 += w;	CursorPos += w;
+				CleanTags(TmpTxt, &m1, &m2, &CursorPos);
+				free(text);					text = strdup(TmpTxt);
+				Command(CMD_REDRAW, Out, 0L);
+				return true;
+				}
+			return false;
+		case CMD_SETSTYLE:
+			if (!text || !text[0]) return false;
+			type = ET_TEXT;
+			if(m1 > -1 && m2 > -1) {
+				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 = strlen(tag1));	m2 += w;	CursorPos += w;
+				CleanTags(TmpTxt, &m1, &m2, &CursorPos);
+				free(text);					text = strdup(TmpTxt);
+				Command(CMD_REDRAW, Out, 0L);
+				return true;
+				}
+			return false;
+		case CMD_BACKSP:
+			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(cmd == CMD_DELETE) Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
+			bRet = false;
+			if(m1 > -1 && m2 > -1) {			//delete marked part of text
+				if (!text || !text[0]) return false;
+				if(m1 > m2) Swap(m1, m2);
+				if(parent) ((DataObj*)parent)->Command(CMD_MRK_DIRTY, 0L, 0L);
+				if(m2 >= (short int)strlen(text)) text[m1] = 0;
+				else strcpy(text+m1, text+m2);
+				CursorPos = m1;
+				m1 = m2 = -1;
+				if(Out) Redraw(Out, (bRet = true));
+				}
+			else if(text[CursorPos]) {
+				if(parent) ((DataObj*)parent)->Command(CMD_MRK_DIRTY, 0L, 0L);
+				strcpy(text + CursorPos, text + CursorPos + 1);
+				if(Out)Redraw(Out, (bRet = true));
+				}
+			if(Out)Out->TextCursor(text, MyPos, (POINT *) NULL, &CursorPos,
+				scroll_et == this ? scroll_dist : scroll_dist=0);
+			return bRet;
+		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;
+				}
+			if(text[CursorPos]) CursorPos++;
+			else return false;
+		case CMD_SHIFTLEFT:
+			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-2 - w;
+					}
+				else {						//left justified text
+					mx1 = loc.x +2;
+					}
+				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;
+			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-2;			MyPos.y = (rb.y+loc.y)/2;
+				if(((DataObj*)data_obj)->Select(&MyPos))return true;
+				MyPos.x = loc.x+2;
+				((DataObj*)data_obj)->Select(&MyPos);
+				}
+			return false;
+		case CMD_CURRIGHT:
+			m1 = m2 = -1;
+			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+2;		MyPos.y = (rb.y+loc.y)/2;	crb.x = rb.x;
+				if(((DataObj*)data_obj)->Select(&MyPos)) return true;
+				MyPos.x = rb.x-2;
+				((DataObj*)data_obj)->Select(&MyPos);
+				}
+			return false;
+		case CMD_POS_FIRST:		case CMD_POS_LAST:
+			CursorPos = (cmd == CMD_POS_LAST && text && text[0]) ? strlen(text) : 0;
+			m1 = m2 = -1;
+			Redraw(Out, true);
+			Out->TextCursor(text, MyPos, (POINT *) NULL, &CursorPos,
+				scroll_et == this ? scroll_dist : scroll_dist=0);
+			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-2-((rb.y-loc.y-4)*(strlen(text)-CursorPos))/2;
+				else MyPos.x = loc.x+2+((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 +2;
+				if(MyPos.x > rb.x) MyPos.x = rb.x -2;
+				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;
+				return true;
+				}
+			if(mev->Action == MOUSE_LBDOUBLECLICK) {
+				rMark.top = loc.y;				rMark.bottom = rb.y;
+				if(!Out->oGetTextExtent(text, strlen(text), &w, &h)) return false;
+				m1 = 0;							m2 = strlen(text);
+				if(Align & TXA_HRIGHT) {		//right justfied text
+					rMark.right = crb.x -2;		rMark.left = crb.x - w - 2;
+					}
+				else {							//left justified text
+					rMark.left = loc.x +2;		rMark.right = rMark.left +w;
+					}
+				Out->UpdateRect(&rMark,true);
+				return true;
+				}
+			MyPos.x = Align & TXA_HRIGHT ? mev->x + 2 : mev->x - 2;
+			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 = strlen(text)-j)){
+					if(!Out->oGetTextExtent(text+j, i, &w, &h)) return false;
+					w = crb.x - w - 2;
+					}
+				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+2);
+				}
+			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;
+				rMark.bottom = rb.y;
+				if(rMark.right < crb.x && rMark.right > loc.x &&
+					rMark.left > loc.x && rMark.left < crb.x)
+					Out->UpdateRect(&rMark,true);
+				}
+			if(m1 >= 0 && m2 >= 0 && m1 != m2 && 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){
+		*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) Value = res->value;
+				else Value = 0.0;
+				*v = Value;		type &= ~ET_BUSY;
+				return res->type == ET_VALUE;
+				}
+			type &= ~ET_BUSY;
+			return false;
+			}
+		else if(parent) {
+			((DataObj*)parent)->Command(CMD_ERROR, (void*)"circular reference in formula", 0L);
+			type |= ET_CIRCULAR;
+			}
+		*v = Value;
+		return true;
+		}
+	return false;
+}
+
+bool
+EditText::GetText(char *t, int size)
+{
+	if(text) {
+		if((int)strlen(text) < size) strcpy(t, text);
+		else {
+			memcpy(t, &text, size-1);			t[size-1] = 0;
+			}
+		return true;
+		}
+	if((type & 0xff) == ET_VALUE) {
+		sprintf(TmpTxt, "%lf", Value);
+		text = strdup(TmpTxt);
+		return GetText(t, size);
+		}
+	return false;
+}
+
+bool
+EditText::GetResult(anyResult *r)
+{
+	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) {
+		r->text = 0L;	r->value = Value;		r->type = ET_VALUE;
+		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(!(type & ET_BUSY)){
+			type |= ET_BUSY;
+			if(res = do_formula((DataObj*)parent, text+1)) {
+				if(res->type == ET_VALUE) Value = res->value;
+				else Value = 0.0;
+				type &= ~ET_BUSY;
+				memcpy(r, res, sizeof(anyResult));
+				return true;
+				}
+			type &= ~ET_BUSY;
+			return false;
+			}
+		else if(parent) {
+			((DataObj*)parent)->Command(CMD_ERROR, (void*)"circular reference in formula", 0L);
+			r->text = "#CIRC.";	r->value = 0.0;			r->type = ET_TEXT;
+			return true;
+			}
+		return false;
+		}
+	return false;
+}
+
+bool
+EditText::SetValue(double v)
+{
+	if(text){
+		free(text);		text = 0L;
+		}
+	Value = v;	type = ET_VALUE;
+	return true;
+}
+
+bool
+EditText::SetText(char *t)
+{
+	if(text){
+		free(text);		text = 0L;
+		}
+	Value = 0.0;	type = ET_UNKNOWN;
+	if(t && t[0]) text = strdup(t);
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// return the ASCII reprentation of value of text if applicable. If not return
+//    the text with double quotes: comma separated file syntax.
+bool
+EditText::GetItem(char *dest, int size)
+{
+	char tmp[50];
+	int i;
+
+	if(!text || !text[0] || !dest || size < 3) return false;
+	if((type & 0xff) == ET_UNKNOWN) FindType();
+	if(CurrText == this) FindType();
+	//its a value
+	if((type & 0xff) == ET_VALUE){
+		if(text && strlen(text) < (sizeof(tmp)-1)){
+			strcpy(tmp, text);
+			if(defs.DecPoint[0] != '.') {
+				for(i = 0; tmp[i]; i++) 
+					if(tmp[i] == defs.DecPoint[0]) tmp[i]='.';
+				}
+			}
+		else {
+			sprintf(tmp, "%f", Value);			RmTrail(tmp);
+			}
+		for(i = 0; i < (size-1) && tmp[i]; i++) dest[i] = tmp[i];
+		dest[i] = 0;
+		}
+	//else its a string
+	else {
+		dest[0] = '"';
+		for(i = 1; i < (size-2) && text[i-1]; i++) dest[i] = text[i-1];
+		dest[i++] = '"';
+		dest[i] = 0;
+		}
+	return true;
+}
+
+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);
+}
+
+bool
+EditText::isFormula()
+{
+	if((type & 0xff)==ET_UNKNOWN) FindType();
+	return (type == ET_FORMULA);
+}
+
+void
+EditText::FindType()
+{
+	if(text && text[0] == '=') {
+		Align = TXA_VTOP | TXA_HRIGHT;
+		type = ET_FORMULA;
+		}
+	else if(text && (Txt2Flt(text, &Value))) {
+		Align = TXA_VTOP | TXA_HRIGHT;
+		type = ET_VALUE;
+		}
+	else if (text && text[0]) {
+		Align = TXA_VTOP | TXA_HLEFT;
+		type = ET_TEXT;
+		}
+	else if ((type && 0xff) == ET_VALUE) {
+		Align = TXA_VTOP | TXA_HRIGHT;
+		type = ET_VALUE;
+		}
+	else {
+		Align = TXA_VTOP | TXA_HRIGHT;
+		type = ET_UNKNOWN;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// output formated text - style and font changes
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+typedef struct _tag_info {
+	char *tag;
+	int and_style, or_style;
+	int font, op;
+}tag_info;
+
+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},
+	{"<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(anyOutput *o, int x, int y, char *txt)
+{
+	if(txt && txt[0]) src=strdup(txt);
+	else src=0L;	split_text=0L;	n_split=0;
+	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  && SetTextDef(&td, n)) j += strlen(tags[n].tag);
+		if(j > idx) break;
+		if(split_text[i].txt && split_text[i].txt[0]) j += 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=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;
+}
+
+void
+fmtText::cur_right(int *pos)
+{
+	int n;
+
+	if(!src || !pos || !src[*pos]) return;
+	if(src[*pos] == '<' && (n=rightTag(src, *pos)) >= 0) {
+		*pos += strlen(tags[n].tag);
+		cur_right(pos);
+		}
+	else (*pos)++;
+}
+
+void 
+fmtText::cur_left(int *pos)
+{
+	int n;
+
+	if(!src || !pos || !(*pos)) return;
+	(*pos)--;
+	while (src[*pos] == '>' && (n=leftTag(src, *pos)) >= 0) {
+		*pos -= 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(!cb) cb = 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((n=split_text[i].tag) >= 0 && SetTextDef(&td2, n)) {
+			o->SetTextSpec(&td2);
+			l += strlen(tags[n].tag);
+			}
+		if(split_text[i].txt){
+			l1 = l;		l += strlen(split_text[i].txt);
+			if (l1 >= cb) break;
+			o->oGetTextExtent(split_text[i].txt, l >= cb ? cb-l1 : 0, &w1, &h1);
+			w += w1;	if(!i) h = h1;
+			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(txt && txt[0]) src=strdup(txt);		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;
+	char *tmp;
+
+	if(!src || !(tmp = strdup(src))) return false;
+	for(i = li = 0; src[i]; i++) {
+		if(src[i] == '<' && (n=rightTag(src, i))>=0) {
+			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 = strdup(tmp);
+				i += 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 = strdup(tmp);
+				i += strlen(tags[n].tag);	split_text[1].tag = n;
+				n_split = 2;
+				}
+			li = i;							i--;
+			}
+		}
+	if(split_text && n_split && li < i && src[li]) split_text[n_split-1].txt = strdup(src+li);
+	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:
+		if(type == 6 || type == 8) 		fd.color = ld.color;
+		pts[0].x = pts[3].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 {
+			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, 4);
+		break;
+		}
+}
+
+void 
+fmtText::DrawFormatted(anyOutput *o)
+{
+	int i, n, x, y, x1, y1, w, h;
+	TextDEF td1, td2;
+	double si, csi, fx, fy;
+
+	if(!o || !split_text) 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) {
+		if((n=split_text[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:
+				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[i].txt){
+			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));
+			}
+		}
+	o->SetTextSpec(&td1);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// the basic data object
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+DataObj::DataObj()
+{
+	cRows = cCols = 0;
+	etRows = 0L;
+}
+
+DataObj::~DataObj()
+{
+	FlushData();
+}
+
+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);
+			}
+		}
+	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)
+{
+	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
+	if(txt && etRows[row][col]) return etRows[row][col]->GetText(txt, len);
+	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)
+{
+	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
+	if(etRows[row][col]) return etRows[row][col]->GetResult(r);
+	return false;
+}
+
+bool
+DataObj::GetSize(int *width, int *height)
+{
+	if(width)*width = cCols;		if(height)*height = cRows;
+	return true;
+}
+
+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;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Store Data Object as strings: less memory required than with DataObj
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+StrData::StrData(DataObj *par)
+{
+	int r, c;
+	char *tx;
+
+	pw = ph = w = h = 0;		str_data = 0L;
+	if(src = par) {
+		src->GetSize(&pw, & ph);
+		if(!(str_data = (char***)calloc(ph, sizeof(char**))))return;
+		for (r = 0; r < ph; r++) {
+			if(!(str_data[r] = (char**)malloc(pw * sizeof(char*)))) break;
+			for(c = 0; c < pw; c++) {
+				str_data[r][c] = (tx = *(src->GetTextPtr(r, c))) && tx[0] ? strdup(tx) : 0L;
+				}
+			}
+		}
+	w = pw;		h = ph;
+}
+
+StrData::~StrData()
+{
+	int r, c;
+
+	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 r, c;
+
+	if(!dest || !str_data || !w || ! h) return;
+	for (r = 0; r < h; r++) {
+		if(str_data[r]) {
+			for(c = 0; c < w; c++) dest->SetText(r, c, str_data[r][c]);
+			}
+		}
+	return;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// The notary class handles different types of supervision and indexing
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+notary::notary()
+{
+	gObs = 0L;
+	goStack = 0L;
+	NextPopGO = NextPushGO = NextRegGO = 0L;
+}
+
+notary::~notary()
+{
+	FreeStack();
+}
+
+unsigned long
+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 (unsigned long)i*0x2000L+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 (unsigned long)i*0x2000L+j+1;
+					}
+				if(!gObs[i][j]) {
+					gObs[i][j] = go;
+					NextRegGO = ((i << 13) | j);
+					return (unsigned long)i*0x2000L+j+1;
+					}
+				}
+			if(i < 0x1fffL && !gObs[i+1])
+				gObs[i+1] = (GraphObj **)calloc(0x2000L, sizeof(GraphObj *));
+			if(i < 0x1fffL && !gObs[i+1]) return 0L;
+			}
+		}
+	return 0L;
+}
+
+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 long id, GraphObj *go)
+{
+	int i, j;
+
+	NextPopGO = 0L;
+	if(!go) return true;
+	go->Id = id;
+	if(!goStack) {
+		goStack = (GraphObj ***)calloc(8192, sizeof(GraphObj **));
+		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 long 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){
+			sprintf(TmpTxt,"%d objects deleted\nby notary", k);
+			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;
+
+	if(asc && *asc){
+		i = strlen(asc)+2;
+		txt = (char *)malloc(i);
+		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;
+
+	RetVal = 0;
+	if(txt && Reset())	do {
+		RetVal += ((x2-x1+1)*(y2-y1+1));
+		} while((curridx < (int)strlen(txt)) && 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::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);
+			}
+		return true;
+		}
+	return false;
+}
+
+bool
+AccRange::Reset()
+{
+	curridx = 0;
+	return Parse(curridx);
+}
+
+bool
+AccRange::Parse(int start)
+{
+	int i, step, *v;
+
+	i = start;
+	if(!txt) return false;
+	if(txt[i] == ';') i++;
+	if(!txt[i]) return false;
+	step = x1 = y1 = x2 = y2 = 0;
+	v = &x1;
+	for ( ; i < (int)strlen(txt)+1; 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;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Default data vault
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Default::Default()
+{
+	dUnits = cUnits = 0;
+	strcpy(DecPoint, ".");		strcpy(ColSep, ",");
+	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 = .1;	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;
+	svgAttr = svgScript = currPath = IniFile = 0L;
+	File1 = File2 = File3 = File4 = File5 = File6 = 0L;
+}
+
+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);
+}
+
+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 = 4.0;		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 = 4.0;		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:				RetVal = 4.5;		break;
+	case SIZE_RRECT_RAD:			
+		return rrect_rad ? *rrect_rad : GetSize(SIZE_SYMBOL)/2.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;
+
+	if(path && (tmp_path=strdup(path))){
+		for(i=strlen(path); i > 0 && tmp_path[i] != '/' && tmp_path[i] != '\\'; i--);
+		tmp_path[i] = 0;
+		if(currPath) free(currPath);
+		if(strlen(tmp_path)) currPath = strdup(tmp_path);
+		else currPath = 0L;
+		strcpy(tmp_path, 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 = 0;		//use stdin
+	else if(-1 ==(iFile = open(name, O_BINARY))) return false;
+	Cache = (unsigned char *)malloc((unsigned)(CharCacheSize + 1));
+	if(Cache) return true;
+	return false;
+}
+
+void
+ReadCache::Close()
+{
+	close(iFile);
+	if(Cache) free(Cache);
+	Cache = 0L;
+}
+
+unsigned char
+ReadCache::Getc()
+{
+	if(Cache){
+		if(idx < max) return (last = Cache[idx++]);
+		else {
+			do {
+				max = read(iFile, Cache, CharCacheSize);
+				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) {
+				max = read(iFile, Cache, CharCacheSize);
+				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) {
+		Cache = (unsigned char*) strdup((char*)ptr);
+		max = strlen((char*)Cache);
+		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;
+	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;
+}
+
+//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;
+
+	if(o && buffers) {
+		for(i = c_buf = 0; i < ndisp; i++) {
+			if(buffers[i] && buffers[i]->disp == o)	c_buf = i;	break;
+			}
+		if(!(c_buf)) 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[i]);	buffers[i] = 0L;
+		}
+	if(o == cdisp) SetDisp(ldisp);
+}
+
+//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()
+{
+	int idx;
+
+	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;
+		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)--;
+	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];
+					}
+				((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_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;				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_VALINT:
+			*((int*)(buff[idx]->loc)) = *((int*)(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)) free(*(buff[idx]->loc));
+			CurrGO = buff[idx]->owner;
+			*(buff[idx]->loc) = buff[idx]->data;				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");
+		}
+}
+
+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;
+	if(o){
+		SetDisp(o);					 o->HideMark();
+		}
+	if(CurrGO == *go) CurrGO = 0L;
+	if((*go)->Id == GO_POLYLINE || (*go)->Id == GO_POLYGON){
+		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 = memdup(*go, *count * sizeof(GraphObj*), 0)){
+			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::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::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);
+}
+
+void
+UndoObj::String(GraphObj *go, char **s, DWORD flags)
+{
+	char *ptr;
+
+	ptr = (s && *s &&  *(*s)) ? strdup(*(s)):0L;
+	if(0 > NewItem(UNDO_STRING, flags, go, ptr, (void**)s)) if(ptr) free(ptr);
+}
+
+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) ptr->text = strdup(td->text);
+	if(0 > NewItem(UNDO_TEXTDEF, flags, go, ptr, (void**)td)) 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, DWORD flags)
+{
+	int w, h;
+	StrData *save;
+
+	if(!go || !d) return;
+	if(o) SetDisp(o);
+	if(!(d->GetSize(&w, &h)) || !(save = new StrData(d))) 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 = strdup(text);
+			ptr->cur = cur;			ptr->m1 = m1;		ptr->m2 = m2;
+			ptr->vcur = *cur;		ptr->vm1 = *m1;		ptr->vm2 = *m2;
+			ptr->row = 0;			ptr->col = 0;
+			if(ptr->DaO = (DataObj*)DaO) {
+				if(ptr->DaO->Command(CMD_CURRPOS, &cpos, 0L)){
+					ptr->row = cpos.y;	ptr->col = cpos.x;
+					}
+				}
+			if(0 > NewItem(UNDO_ET, flags, 0L, ptr, (void**)et)) {
+				if(ptr->txt)free (ptr->txt);			free(ptr);
+				}
+			}
+		SetDisp(o_save);
+		}
+}
+
+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);
+		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:
+		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);
+	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
new file mode 100755
index 0000000..33fc5b2
--- /dev/null
+++ b/Utils.cpp
@@ -0,0 +1,1946 @@
+//Utils.cpp, Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005 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 "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;
+	Step = 1.0;
+	if(logStep > 0.301) Step = 2.0; 
+	if(logStep > 0.699) Step = 5.0;
+	Step *= pow(10.0, Magn);
+	HiVal = LoVal = Step * floor(axis->min/Step);
+	axis->max *=1.05f;
+	while(HiVal < axis->max) HiVal += Step;
+	axis->min = LoVal;
+	axis->max = HiVal;
+	axis->Start = axis->min;
+	axis->Step = Step;
+}
+
+void NiceStep(AxisDEF *axis, int nTick)
+{
+	double diff, logStep, Step, Magn;
+	int i;
+
+	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;
+	Step = 1.0;
+	if(logStep > 0.301) Step = 2.0; 
+	if(logStep > 0.699) Step = 5.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;
+		}
+	if(axis->min > defs.min4log) lv = axis->min;
+	else lv = ((GraphObj*)axis->owner)->GetSize(cmd);
+	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
+//----------------------------------------------------------------------------
+// restyle formula
+void ReshapeFormula(char **text)
+{
+	int i, j, l;
+
+	if(!text || !*text || !**text) return;
+	l = 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((int)strlen(TmpTxt) < l && TmpTxt[0]) {
+		free(*text);	*text = strdup(TmpTxt);	
+		}
+}
+
+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=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)
+{
+	WriteFloatToBuff(buff, val);
+	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 = strdup(txt))){
+			Nat2Int(tmp);
+			//the return value of sscanf only roughly identifies a number
+			if(!sscanf(tmp,"%lf", val)){
+				free(tmp);
+				return false;
+				}
+			free(tmp);
+			return true;
+			}
+		}
+	return false;
+}
+
+void RmTrail(char *txt)
+{
+	int i;
+
+	i = 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 *Int2ColLabel(int nr1, bool uc)
+{
+	static char RetTxt[10];
+	int i, j, nr = nr1;
+	char base = uc ? 'A' : 'a';
+
+	sprintf(RetTxt+8, "%c\0", base + (nr %26));
+	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;
+}
+
+//convert strings to XML specifications
+//this offers a limited support for special characters
+char *str2xml(char *str)
+{
+	int i, j;
+	wchar_t wc;
+
+	if(!str) return 0L;
+	for(i = j = 0; str[i] && j < 4090; i++) {
+		switch(str[i]) {
+		case '"':
+			j += sprintf(TmpTxt+j, """);
+			break;
+		case '&':
+			j += sprintf(TmpTxt+j, "&");
+			break;
+		case '<':
+			j += sprintf(TmpTxt+j, "<");
+			break;
+		case '>':
+			j += sprintf(TmpTxt+j, ">");
+			break;
+		default:
+			if((unsigned char)str[i] <= 127) TmpTxt[j++]=str[i];
+			else {
+				if(mbtowc(&wc, str+i, 1) >0) j += sprintf(TmpTxt+j, 
+					"&#%d;", ((unsigned short)wc));
+				}
+			}
+		}
+	TmpTxt[j] = 0;
+	return(TmpTxt);
+}
+
+// split string str into array of strings using sep as separator
+// return number of lines created in nl
+char **split(char *str, char sep, int *nl)
+{
+	int i, j, k, l, ns;
+	char **ptr, *txt;
+
+	if(!str || !sep) return 0L;
+	if(!(txt = strdup(str))) return 0L;
+	k = (int)strlen(txt);
+	for(i = ns = 0; i < k; i++) if(txt[i] == sep) ns++;
+	if(!(ptr = (char**)calloc(ns+2, sizeof(char*)))){
+		free(txt);	return 0L;
+		}
+	for(i = j = l = 0; i < k; i++) {
+		if(txt[i] == sep) {
+			txt[i] = 0;		ptr[l++] = strdup(txt+j);		j = i+1;
+			}
+		}
+	ptr[l++] = strdup(txt+j);	if(nl) *nl = l;		free(txt);
+	return ptr;
+}
+
+//----------------------------------------------------------------------------
+// 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 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))) 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)
+{
+	LineDEF OldLine;
+	double minw = defs.GetSize(SIZE_DATA_LINE);
+
+	memcpy(&OldLine, Line, sizeof(LineDEF));
+	if(OldLine.width <= 0.001) memcpy(&OldLine, defs.GetLine(), sizeof(LineDEF));
+	OldLine.color = mark ? Line->color : 0x00ffffffL;
+	OldLine.width = OldLine.width < minw ? minw * 3.0 : 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;
+	default:
+		sprintf(TmpTxt, "Cannot delete Object\nwith Id %ld", go->Id);
+		ErrorBox(TmpTxt);
+		//we do not delete the object, probably we recover
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//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;
+	strcpy(Name, FileName);
+	i = 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 {
+		sprintf(ext, ".%03d", i);
+		sprintf(TmpFileName, "%s%s", Name, ext);
+		if((TheFile = fopen(TmpFileName, "r"))) fclose(TheFile);
+		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;
+
+	if(0L ==(TheFile = fopen(FileName, "r"))) return false;
+	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;
+
+	if(0L ==(TheFile = fopen(FileName, "r"))) return false;
+	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;
+
+	if(0L ==(TheFile = fopen(FileName, "r"))) return false;
+	fclose(TheFile);
+	return true;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//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
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+unsigned int HashValue(unsigned char *str)
+{
+	unsigned int i = 0, ret=0;
+
+	if(!str) return 0;
+	do {
+		if(str[i] > 32) ret = ((str[i]-32) + (ret <<2));
+		i++;
+		}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;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//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
+// void AddToPolygon(long *cp, POINT *pts, POINT *np);
+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
+// void AddToPolygon(long *cp, POINT *pts, POINT *np);
+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;
+}
diff --git a/Version.h b/Version.h
new file mode 100755
index 0000000..5289724
--- /dev/null
+++ b/Version.h
@@ -0,0 +1,20 @@
+//RLPlot.h, Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005 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 SZ_VERSION  "0.99.14b"
+
diff --git a/WinSpec.cpp b/WinSpec.cpp
new file mode 100755
index 0000000..fb917e7
--- /dev/null
+++ b/WinSpec.cpp
@@ -0,0 +1,2422 @@
+//WinSpec.cpp, Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005 R.Lackner
+//the entire code of this module is highly specific to Windows!
+//
+//    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 <stdio.h>
+#include <math.h>
+#include "rlplot.h"
+#include "WinSpec.h"
+#include "rlplot.rc"
+#include "TheDialog.h"
+
+extern int dlgtxtheight;
+
+HINSTANCE hInstance;
+HWND MainWnd = 0L;
+HACCEL accel;
+bool isWIN95 = false;
+extern tag_Units Units[];
+extern GraphObj *CurrGO;			//Selected Graphic Objects
+extern Graph *CurrGraph;
+extern char *WWWbrowser;
+extern char *LoadFile;
+extern Default defs;
+extern char TmpTxt[];
+extern UndoObj Undo;
+
+const char name[] = "RLPLOT1";
+
+static unsigned int cf_rlpgraph = RegisterClipboardFormat("rlp_graph");
+static unsigned int cf_rlpobj = RegisterClipboardFormat("rlp_obj");
+static unsigned int cf_rlpxml = RegisterClipboardFormat("rlp_xml");
+
+long FAR PASCAL WndProc(HWND, UINT, UINT, LONG);
+PrintWin *Printer = 0L;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// I/O File name dialogs
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Get a new file name to store data in
+char *SaveDataAsName(char *oldname)
+{
+	static char szFile[500], szFileTitle[256];
+	static char szFilter[] = "RLPlot workbook (*.rlw)\0*.rlw\0data files (*.csv)\0*.csv\0tab separated (*tsv)\0"
+		"*.tsv\0XML (*.xml)\0*.xml\0";
+	OPENFILENAME ofn;
+
+	szFile[0] = '\0';
+	if(oldname)strcpy(szFile, oldname);
+	memset(&ofn, 0, sizeof(OPENFILENAME));
+	ofn.lStructSize = sizeof(OPENFILENAME);
+	ofn.hwndOwner = GetFocus();
+	ofn.lpstrFilter = szFilter;
+	ofn.nFilterIndex = 1;
+	ofn.lpstrFile = szFile;
+	ofn.nMaxFile = sizeof(szFile);
+	ofn.lpstrFileTitle = szFileTitle;
+	ofn.nMaxFileTitle = sizeof(szFileTitle);
+	ofn.lpstrInitialDir = defs.currPath;
+	ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
+	ofn.lpstrTitle = "Save Data As";
+
+	if(GetSaveFileName(&ofn)){
+		defs.FileHistory(szFile);
+		return szFile;
+		}
+	else return NULL;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Get a new file name to store graph
+char *SaveGraphAsName(char *oldname)
+{
+	static char szFile[500], szFileTitle[256];
+	static char szFilter[] = "RLPlot Graph (*.RLP)\0*.rlp\0";
+	OPENFILENAME ofn;
+
+	szFile[0] = '\0';
+	if(oldname)strcpy(szFile, oldname);
+	memset(&ofn, 0, sizeof(OPENFILENAME));
+	ofn.lStructSize = sizeof(OPENFILENAME);
+	ofn.hwndOwner = GetFocus();
+	ofn.lpstrFilter = szFilter;
+	ofn.nFilterIndex = 1;
+	ofn.lpstrFile = szFile;
+	ofn.nMaxFile = sizeof(szFile);
+	ofn.lpstrFileTitle = szFileTitle;
+	ofn.nMaxFileTitle = sizeof(szFileTitle);
+	ofn.lpstrInitialDir = defs.currPath;
+	ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
+	ofn.lpstrTitle = "Save Graph As";
+
+	if(GetSaveFileName(&ofn)){
+		defs.FileHistory(szFile);
+		return szFile;
+		}
+	else return NULL;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Get file name to read graph
+char *OpenGraphName(char *oldname)
+{
+	static char szFile[500], szFileTitle[256];
+	static char szFilter[] = "RLPlot Graph (*.RLP)\0*.rlp\0";
+	OPENFILENAME ofn;
+
+	szFile[0] = '\0';
+	if(oldname)strcpy(szFile, oldname);
+	memset(&ofn, 0, sizeof(OPENFILENAME));
+	ofn.lStructSize = sizeof(OPENFILENAME);
+	ofn.hwndOwner = GetFocus();
+	ofn.lpstrFilter = szFilter;
+	ofn.nFilterIndex = 1;
+	ofn.lpstrFile = szFile;
+	ofn.nMaxFile = sizeof(szFile);
+	ofn.lpstrFileTitle = szFileTitle;
+	ofn.nMaxFileTitle = sizeof(szFileTitle);
+	ofn.lpstrInitialDir = defs.currPath;
+	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+	ofn.lpstrTitle = "Open Graph";
+
+	if(GetOpenFileName(&ofn)){
+		defs.FileHistory(szFile);
+		return szFile;
+		}
+	else return NULL;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Get a file name to load data
+char *OpenDataName(char *oldname)
+{
+	static char szFile[500], szFileTitle[256];
+	static char szFilter[] = "RLPlot workbook (*rlw)\0*.rlw\0data files (*.csv)\0*.csv\0"
+		"tab separated file (*.tsv)\0*.tsv\0"
+		"RLPlot Graph (*.rlp)\0*.rlp\0all files (*.*)\0*.*\0";
+	OPENFILENAME ofn;
+
+	szFile[0] = '\0';
+	if(oldname)strcpy(szFile, oldname);
+	memset(&ofn, 0, sizeof(OPENFILENAME));
+	ofn.lStructSize = sizeof(OPENFILENAME);
+	ofn.hwndOwner = GetFocus();
+	ofn.lpstrFilter = szFilter;
+	ofn.nFilterIndex = 1;
+	ofn.lpstrFile = szFile;
+	ofn.nMaxFile = sizeof(szFile);
+	ofn.lpstrFileTitle = szFileTitle;
+	ofn.nMaxFileTitle = sizeof(szFileTitle);
+	ofn.lpstrInitialDir = defs.currPath;
+	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
+	ofn.lpstrTitle = "Open Data File";
+
+	if(GetOpenFileName(&ofn)){
+		defs.FileHistory(szFile);
+		return szFile;
+		}
+	else return NULL;
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Get a file name to export graph
+void OpenExportName(GraphObj *g, char *oldname)
+{
+	static char szFile[500], szFileTitle[256];
+	static char szFilter[] = "Scalable Vector Graphics (*.svg)\0*.svg\0"
+		"Encapsulated Post Script (*.eps)\0*.eps\0"
+		"MSWindows MetaFile(*.wmf)\0*.wmf\0Tag Image File Format (*.tif)\0*.tif\0";
+	OPENFILENAME ofn;
+	int i;
+
+	szFile[0] = '\0';
+	if(!g) return;
+	if(oldname)strcpy(szFile, oldname);
+	memset(&ofn, 0, sizeof(OPENFILENAME));
+	ofn.lStructSize = sizeof(OPENFILENAME);
+	ofn.hwndOwner = GetFocus();
+	ofn.lpstrFilter = szFilter;
+	ofn.nFilterIndex = 1;
+	ofn.lpstrFile = szFile;
+	ofn.nMaxFile = sizeof(szFile);
+	ofn.lpstrFileTitle = szFileTitle;
+	ofn.nMaxFileTitle = sizeof(szFileTitle);
+	ofn.lpstrInitialDir = defs.currPath;
+	ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
+	ofn.lpstrTitle = "Export Graph";
+
+	if(g && GetSaveFileName(&ofn)){
+		i = strlen(szFile);
+		g->Command(CMD_BUSY, 0L, 0L);
+		if(0==stricmp(".svg", szFile+i-4)) {
+			DoExportSvg(g, szFile, 0L);
+			}
+		else if(0==stricmp(".wmf", szFile+i-4)) {
+			DoExportWmf(g, szFile, 600.0f, 0L);
+			}
+		else if(0==stricmp(".eps", szFile+i-4)) {
+			DoExportEps(g, szFile, 0L);
+			}
+		else if(0==stricmp(".tif", szFile+i-4)) {
+			DoExportTif(g, szFile, 0L);
+			}
+		else if(0==stricmp(".tiff", szFile+i-5)) {
+			DoExportTif(g, szFile, 0L);
+			}
+		else ErrorBox("Unknown file extension or format");
+		g->Command(CMD_MOUSECURSOR, 0L, 0L);
+		}
+}
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Common alert boxes
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void InfoBox(char *Msg)
+{
+	MessageBox(0, Msg, "Info", MB_OK | MB_ICONINFORMATION);
+}
+
+void ErrorBox(char *Msg)
+{
+	MessageBox(0, Msg, "ERROR", MB_OK | MB_ICONSTOP);
+}
+
+bool YesNoBox(char *Msg)
+{
+	if(IDYES == MessageBox(0, Msg, "RLPlot", MB_YESNO | MB_ICONQUESTION)) return true;
+	return false;
+}
+
+void Qt_Box()
+{
+	MessageBox(0, "No Qt installed\nunder Windows", "Error", MB_OK | MB_ICONQUESTION);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Display blinking text cursor
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+anyOutput *oTxtCur = 0L, *oCopyMark = 0L;
+RECT rTxtCur, rCopyMark;
+bool bTxtCur = false, bTxtCurIsVis = false;
+DWORD cTxtCur = 0x0L;
+HWND hwndTxtCur = 0L;
+int iTxtCurCount = 0;
+POINT ptTxtCurLine[2];
+BitMapWin *bmCopyMark = 0L;
+
+void HideTextCursor()
+{
+	if(oTxtCur) {
+		bTxtCur = false;
+		oTxtCur->UpdateRect(&rTxtCur, false);
+		}
+	oTxtCur = 0L;
+}
+
+void HideTextCursorObj(anyOutput *out)
+{
+	if(oTxtCur && oTxtCur == out) HideTextCursor();
+}
+
+void ShowTextCursor(anyOutput *out, RECT *disp, DWORD color)
+{
+	HWND wnd = GetFocus();
+
+	cTxtCur = color;
+	HideTextCursor();
+	oTxtCur = out;
+	iTxtCurCount = -2;
+	memcpy(&rTxtCur, disp, sizeof(RECT));
+	ptTxtCurLine[0].x = rTxtCur.left;	ptTxtCurLine[0].y = rTxtCur.top;
+	ptTxtCurLine[1].x = rTxtCur.right;	ptTxtCurLine[1].y = rTxtCur.bottom;
+	rTxtCur.bottom++;		rTxtCur.right++;
+	oTxtCur->ShowLine(ptTxtCurLine, 2, cTxtCur);
+	bTxtCurIsVis = bTxtCur = true;
+}
+
+void HideCopyMark()
+{
+	if(bmCopyMark && oCopyMark) {
+		oCopyMark->UpdateRect(&rCopyMark, false);
+		delete bmCopyMark;
+		}
+	bmCopyMark = 0L;	oCopyMark = 0L;
+}
+
+void ShowCopyMark(anyOutput *out, RECT *mrk, int nRec)
+{
+	int i;
+
+	HideCopyMark();
+	if(!out || !mrk || !nRec) return;
+	oCopyMark = out;
+	rCopyMark.left = mrk[0].left;	rCopyMark.right = mrk[0].right;
+	rCopyMark.top = mrk[0].top;		rCopyMark.bottom = mrk[0].bottom;
+	for(i = 1; i < nRec; i++) {
+		UpdateMinMaxRect(&rCopyMark, mrk[i].left, mrk[i].top);
+		UpdateMinMaxRect(&rCopyMark, mrk[i].right, mrk[i].bottom);
+		}
+	bmCopyMark = new BitMapWin(rCopyMark.right - rCopyMark.left, 
+		rCopyMark.bottom - rCopyMark.top, out->hres, out->vres);
+}
+
+LineDEF liCopyMark1 = {0.0f, 1.0f, 0x00ffffffL, 0L};
+LineDEF liCopyMark2 = {0.0f, 6.0f, 0x0L, 0xf0f0f0f0L};
+
+long FAR PASCAL TimerWndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
+{
+	static POINT line[5];
+	static int cp_mark = 0;
+
+	switch(message) {
+	case WM_TIMER:
+		if(bmCopyMark && oCopyMark) {
+			bmCopyMark->CopyBitmap(0, 0, oCopyMark, rCopyMark.left, rCopyMark.top, 
+				rCopyMark.right - rCopyMark.left, rCopyMark.bottom - rCopyMark.top, false);
+			bmCopyMark->SetLine(&liCopyMark1);
+			line[0].x = line[1].x = line[4].x = 0;
+			line[0].y = line[3].y = line[4].y = 0;
+			line[1].y = line[2].y = rCopyMark.bottom-rCopyMark.top-1;
+			line[2].x = line[3].x = rCopyMark.right-rCopyMark.left-1;
+			bmCopyMark->oPolyline(line, 5);
+			bmCopyMark->SetLine(&liCopyMark2);
+			bmCopyMark->RLP.finc = 1.0;		bmCopyMark->RLP.fp = (cp_mark & 0x7);
+			bmCopyMark->oPolyline(line, 5);
+			oCopyMark->ShowBitmap(rCopyMark.left, rCopyMark.top, bmCopyMark);
+			cp_mark++;
+			if(bTxtCurIsVis && oTxtCur && ptTxtCurLine[0].y != ptTxtCurLine[1].y &&
+				oTxtCur == oCopyMark && OverlapRect(&rCopyMark, &rTxtCur)){
+				oTxtCur->ShowLine(ptTxtCurLine, 2, cTxtCur);
+				}
+			}
+		if(!oTxtCur || (ptTxtCurLine[0].x == ptTxtCurLine[1].x &&
+			ptTxtCurLine[0].y == ptTxtCurLine[1].y)) return 0;
+		iTxtCurCount++;
+		if(iTxtCurCount<0) oTxtCur->ShowLine(ptTxtCurLine, 2, cTxtCur);
+		if(iTxtCurCount < 4) return 0;
+		iTxtCurCount = 0;
+		if(bTxtCur && oTxtCur) {
+			if(!bTxtCurIsVis) {
+				oTxtCur->ShowLine(ptTxtCurLine, 2, cTxtCur);
+				bTxtCurIsVis = true;
+				}
+			else {
+				oTxtCur->UpdateRect(&rTxtCur, false);
+				bTxtCurIsVis = false;
+				}
+			}
+		return 0;
+	case WM_QUERYOPEN:
+	case WM_SIZE:	
+		return 0;
+	case WM_DESTROY:
+		KillTimer(hwnd, 1);
+		break;
+		}
+	return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+void InitTextCursor(bool init)
+{
+	WNDCLASS wndclass;
+
+	if (init) {
+		wndclass.style = CS_BYTEALIGNWINDOW | CS_DBLCLKS;
+		wndclass.lpfnWndProc = TimerWndProc;
+		wndclass.cbClsExtra = 0;
+		wndclass.cbWndExtra = 0;
+		wndclass.hInstance = hInstance;
+		wndclass.hIcon = 0L;
+		wndclass.hCursor = 0L;
+		wndclass.hbrBackground = NULL;
+		wndclass.lpszMenuName = 0L;
+		wndclass.lpszClassName = "RLP_TIMER";
+		RegisterClass(&wndclass);
+		if((hwndTxtCur = CreateWindow("RLP_TIMER", 0L, WS_OVERLAPPEDWINDOW,
+			0, 0, 0, 0, NULL, NULL, hInstance, NULL))){
+			SetTimer(hwndTxtCur, 1, 150, 0L);
+			}
+	}
+	else if(hwndTxtCur) {
+		DestroyWindow(hwndTxtCur);
+		hwndTxtCur = 0L;
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Process paste command: check for clipboard contents
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void TestClipboard(GraphObj *g)
+{
+	HANDLE hmem = 0;
+	unsigned char *ptr;
+
+	if(!g) return;
+	OpenClipboard(MainWnd);
+	if(g->Id == GO_SPREADDATA) {
+		if((hmem = GetClipboardData(cf_rlpxml)) &&
+			(ptr = (unsigned char*) GlobalLock(hmem))) g->Command(CMD_PASTE_XML, ptr, 0L);
+		else if((hmem = GetClipboardData(CF_TEXT)) &&
+			(ptr = (unsigned char*) GlobalLock(hmem))) ProcMemData(g, ptr, true);
+		else if((hmem = GetClipboardData(cf_rlpobj)) &&
+			(ptr = (unsigned char*) GlobalLock(hmem))) OpenGraph(g, 0L, ptr);
+		else if((hmem = GetClipboardData(cf_rlpgraph)) &&
+			(ptr = (unsigned char*) GlobalLock(hmem))) OpenGraph(g, 0L, ptr);
+		}
+	else if(g->Id == GO_PAGE) {
+		if((hmem = GetClipboardData(cf_rlpobj)) &&
+			(ptr = (unsigned char*) GlobalLock(hmem))) OpenGraph(g, 0L, ptr);
+		else if((hmem = GetClipboardData(cf_rlpgraph)) &&
+			(ptr = (unsigned char*) GlobalLock(hmem))) OpenGraph(g, 0L, ptr);
+		}
+	else if(g->Id == GO_GRAPH) TestClipboard(g->parent);
+	if(hmem) GlobalUnlock(hmem);
+	CloseClipboard();
+}
+
+void EmptyClip()
+{
+	HideCopyMark();
+	OpenClipboard(MainWnd);
+	EmptyClipboard();
+	CloseClipboard();
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Get display (desktop) size
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void GetDesktopSize(int *width, int *height)
+{
+	RECT rc;
+
+	GetClientRect(GetDesktopWindow(), &rc);
+	*width = rc.right;
+	*height = rc.bottom;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Common code for any Windows output class
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool com_oTextOut(int x, int y, char *txt, int cb, 
+	HFONT *hFont, HDC *dc, TextDEF *td, bool win95mode, anyOutput *o)
+{
+	XFORM xf;
+	w_char *uc;
+	int i, ix, iy, align;
+
+	if(!*hFont || !txt || !txt[0]) return false;
+	SelectObject(*dc, *hFont);
+	SetTextColor(*dc, td->ColTxt);
+	SetBkColor(*dc, td->ColBg);
+	align = ((td->Align & TXA_HRIGHT) ? TA_RIGHT : (td->Align &
+		TXA_HCENTER) ? TA_CENTER : TA_LEFT) | ((td->Align & TXA_VBOTTOM) ?
+		TA_BOTTOM : TA_TOP);
+	SetTextAlign(*dc, align);
+	SetBkMode(*dc, td->Mode ? TRANSPARENT : OPAQUE);
+	ix = iy = 0;
+	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);
+		else if((td->Align & TXA_VTOP) == TXA_VTOP) iy += o->un2iy(td->fSize*.6);
+		}
+	else if(td->Style & TXS_SUPER) {
+		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.6);
+		else if((td->Align & TXA_VTOP) == TXA_VTOP) iy += o->un2iy(td->fSize*.0);
+		}
+	if(!win95mode && (fabs(td->RotBL) >.01 || fabs(td->RotCHAR) >.01)) {
+		SetGraphicsMode(*dc, GM_ADVANCED);
+		xf.eM11 = xf.eM22 = (float)cos(td->RotBL *0.01745329252);
+		xf.eM12 = (float)-sin(td->RotBL *0.01745329252);
+		xf.eM21 = -xf.eM12;
+		xf.eDx = (float)x;
+		xf.eDy = (float)y;
+		SetWorldTransform(*dc, &xf);
+		if(td->Font==FONT_GREEK && (uc=(w_char*)calloc(strlen(txt)+1, sizeof(w_char)))) {
+			for(i = 0; txt[i]; i++) {
+				if((txt[i] >= 'A' && txt[i] <= 'Z')) uc[i] = txt[i] - 'A' + 0x391;
+				else if((txt[i] >= 'a' && txt[i] <= 'z')) uc[i] = txt[i] - 'a' + 0x3B1;
+				else uc[i] = txt[i];
+				}
+			TextOutW(*dc, ix, iy+((td->Align & TXA_VCENTER) ? - td->iSize/2 : 0), uc, 
+				(cb > 0) ? cb : strlen(txt));
+			free(uc);
+			}
+		else {
+			TextOut(*dc, ix, iy+((td->Align & TXA_VCENTER) ? - td->iSize/2 : 0), txt, 
+				(cb > 0) ? cb : strlen(txt));
+			}
+//		DrawText(*dc, txt, (cb> 0)? cb : strlen(txt), 0L /*LPRECT*/, uFormat);
+		ModifyWorldTransform(*dc, &xf, MWT_IDENTITY);
+		SetGraphicsMode(*dc, GM_COMPATIBLE);
+		return true;
+		}
+	else {
+		if(!win95mode && td->Font==FONT_GREEK && (uc=(w_char*)calloc(strlen(txt)+1, sizeof(w_char)))) {
+			for(i = 0; txt[i]; i++) {
+				if((txt[i] >= 'A' && txt[i] <= 'Z')) uc[i] = txt[i] - 'A' + 0x391;
+				else if((txt[i] >= 'a' && txt[i] <= 'z')) uc[i] = txt[i] - 'a' + 0x3B1;
+				else uc[i] = txt[i];
+				}
+			TextOutW(*dc, x+ix, iy + ((td->Align & TXA_VCENTER) ? y - td->iSize/2 : y), uc, 
+				(cb > 0) ? cb : strlen(txt));
+			free(uc);
+			return true;
+			}
+		else if(TextOut(*dc, x+ix, iy + ((td->Align & TXA_VCENTER) ? y - td->iSize/2 : y), txt, 
+			(cb > 0) ? cb : strlen(txt))) return true;
+		}
+	return false;
+}
+
+bool com_SetTextSpec(TextDEF *set, anyOutput *o, HFONT *hFont,	TextDEF *TxtSet, 
+	HDC *dc, bool win95mode)
+{	
+	bool IsModified, RetVal;
+	LOGFONT FontRec;
+	HFONT newFont;
+
+	if(!set->iSize && set->fSize > 0.001f) 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 ||
+		TxtSet->Font != set->Font || TxtSet->fSize != set->fSize) IsModified = true;
+	else IsModified = false;
+	RetVal = o->anyOutput::SetTextSpec(set);
+	if (IsModified && RetVal) {
+		// create font
+		if((TxtSet->Style & TXS_SUPER) || (TxtSet->Style & TXS_SUB))
+			FontRec.lfHeight = o->un2iy(set->fSize*0.71);
+		else FontRec.lfHeight = TxtSet->iSize;
+		if(FontRec.lfHeight <8) FontRec.lfHeight = 8;
+		FontRec.lfWidth = 0;
+		if(win95mode) {				//Win 95, 98
+			FontRec.lfEscapement = iround(TxtSet->RotBL*10);		//text angle
+			FontRec.lfOrientation = iround(TxtSet->RotBL*10);		//base line angle
+			}
+		else {						//Win NT, 2000, XP
+			FontRec.lfEscapement = 0;		//text angle
+			FontRec.lfOrientation = 0;		//base line angle
+			}
+		FontRec.lfWeight = (TxtSet->Style & TXS_BOLD) ? FW_BOLD : FW_NORMAL;
+		FontRec.lfItalic = (TxtSet->Style & TXS_ITALIC) ? TRUE : FALSE;
+		FontRec.lfUnderline = (TxtSet->Style & TXS_UNDERLINE) ? TRUE : FALSE;
+		FontRec.lfStrikeOut = 0;
+		FontRec.lfOutPrecision = OUT_DEFAULT_PRECIS;
+		FontRec.lfClipPrecision = CLIP_DEFAULT_PRECIS;
+		FontRec.lfQuality = PROOF_QUALITY;
+		switch(TxtSet->Font){
+		case FONT_HELVETICA:
+		default:
+			FontRec.lfCharSet = ANSI_CHARSET;
+			FontRec.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS;
+			strcpy(FontRec.lfFaceName, "Arial");
+			break;
+		case FONT_GREEK:		case FONT_TIMES:
+			FontRec.lfCharSet = ANSI_CHARSET;
+			FontRec.lfPitchAndFamily = VARIABLE_PITCH | FF_ROMAN;
+			strcpy(FontRec.lfFaceName, "Times New Roman");
+			break;
+		case FONT_COURIER:
+			FontRec.lfCharSet = ANSI_CHARSET;
+			FontRec.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
+			strcpy(FontRec.lfFaceName, "Courier New");
+			break;
+			}
+		newFont = CreateFontIndirect(&FontRec);
+		SelectObject(*dc, newFont);
+		if(*hFont)DeleteObject(*hFont);
+		*hFont = newFont;
+		if(!(*hFont)) return false;
+		}
+	return RetVal;
+}
+
+bool com_Arc(HDC dc, int x1, int y1, int x2, int y2, int quads)
+{
+	int bx, by, ex, ey;
+
+	if(x1 > x2) Swap(x1, x2);	if(y1 > y2) Swap(y1, y2);
+	switch (quads) {
+	case 1:
+		bx = (x1+x2)>>1;	by = y2;
+		ex = x2;			ey = (y1 +y2)>>1;
+		break;
+	case 2:
+		bx = x1;	ex = x2;		by = ey = (y1 +y2)>>1;
+		break;
+	case 3:
+		bx = (x1+x2)>>1;	by = y1;
+		ex = x2;			ey = (y1 +y2)>>1;
+		break;
+	case 4:
+		bx = ex = x2;		by = ey = (y1 +y2)>>1;
+		break;
+	default: return false;
+		}
+	if(Arc(dc, x1, y1, x2, y2, bx, by, ex, ey)) return true;
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Output to windows bitmap
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+BitMapWin::BitMapWin(GraphObj *g, HWND hw):anyOutput()
+{
+	HDC dc;
+	HWND hwndDesk;
+
+	memDC = 0L;		hgo = 0L;		go = g;
+	dc = GetDC(hwndDesk = GetDesktopWindow());
+	hres = (double)GetDeviceCaps(dc, LOGPIXELSX);
+	vres = (double)GetDeviceCaps(dc, LOGPIXELSY);
+	if(hw) GetClientRect(hw, &DeskRect);
+	else GetClientRect(hwndDesk, &DeskRect);
+	Box1.Xmin = DeskRect.left;		Box1.Xmax = DeskRect.right;
+	Box1.Ymin = DeskRect.top;		Box1.Ymax = DeskRect.bottom;
+	scr = CreateCompatibleBitmap(dc, DeskRect.right, DeskRect.bottom);
+	memDC = CreateCompatibleDC(NULL);
+	SelectObject(memDC, scr);
+	ReleaseDC(hwndDesk, dc);
+	hPen = CreatePen(PS_SOLID, 1, dLineCol = 0x00ffffffL);
+	hBrush = CreateSolidBrush(dFillCol =dBgCol = 0x00ffffffL);
+	dPattern = 0L;
+	if(memDC) {
+		oldPen = (HPEN)SelectObject(memDC, hPen);
+		oldBrush = (HBRUSH)SelectObject(memDC, hBrush);
+		}
+	else {
+		oldBrush = 0L;
+		oldPen = 0L;
+		}
+	hFont = 0L;
+}
+
+BitMapWin::BitMapWin(int w, int h, double hr, double vr)
+{
+	HDC dc;
+	HWND hwndDesk;
+
+	memDC = 0L;		hgo = 0L;		go = 0L;
+	hres = hr;		vres = vr;
+	units = defs.cUnits;
+	DeskRect.right = w;				DeskRect.bottom = h;
+	DeskRect.left = DeskRect.top = 0;
+	VPorg.fx = VPorg.fy = 0.0;
+	dc = GetDC(hwndDesk = GetDesktopWindow());
+	scr = CreateCompatibleBitmap(dc, DeskRect.right, DeskRect.bottom);
+	memDC = CreateCompatibleDC(NULL);
+	ReleaseDC(hwndDesk, dc);
+	SelectObject(memDC, scr);
+	hPen = CreatePen(PS_SOLID, 1, dLineCol = 0x00ffffffL);
+	hBrush = CreateSolidBrush(dFillCol =dBgCol = 0x00ffffffL);
+	dPattern = 0L;
+	if(memDC) {
+		SelectObject(memDC, hPen);
+		SelectObject(memDC, hBrush);
+		}
+	hFont = 0L;
+}
+
+BitMapWin::BitMapWin(GraphObj *g):anyOutput()
+{
+	HDC dc;
+	HWND hwndDesk;
+
+	memDC = 0L;		hgo = 0L;		go = g;
+	dc = GetDC(hwndDesk = GetDesktopWindow());
+	hres = vres = 300.0;
+	units = defs.cUnits;
+	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;
+	VPorg.fy = -co2fiy(go->GetSize(SIZE_GRECT_TOP));
+	VPorg.fx = -co2fix(go->GetSize(SIZE_GRECT_LEFT));
+	scr = CreateCompatibleBitmap(dc, DeskRect.right, DeskRect.bottom);
+	memDC = CreateCompatibleDC(NULL);
+	SelectObject(memDC, scr);
+	ReleaseDC(hwndDesk, dc);
+	hPen = CreatePen(PS_SOLID, 1, dLineCol = 0x00ffffffL);
+	hBrush = CreateSolidBrush(dFillCol =dBgCol = 0x00ffffffL);
+	dPattern = 0L;
+	if(memDC) {
+		SelectObject(memDC, hPen);
+		SelectObject(memDC, hBrush);
+		}
+	hFont = 0L;
+}
+
+BitMapWin::~BitMapWin()
+{
+	if(go) {
+		go->Command(CMD_CAN_DELETE, 0L, 0L);
+		}
+	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;
+}
+
+bool
+BitMapWin::SetLine(LineDEF *lDef)
+{
+	int iw;
+	HPEN newPen;
+
+	if(!hPen || lDef->width != LineWidth || lDef->width != LineWidth || 
+		lDef->pattern != dPattern || lDef->color != dLineCol) {
+		LineWidth = lDef->width;
+		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 && hPen) return true;
+		iLine = iw;
+		dLineCol = lDef->color;
+		newPen = CreatePen(PS_SOLID, iw > 0 ? iw : 1, dLineCol);
+		SelectObject(memDC, newPen);
+		if(hPen) DeleteObject(hPen);
+		hPen = newPen;
+		}
+	return true;
+}
+
+bool
+BitMapWin::SetFill(FillDEF *fill)
+{
+	HBRUSH newBrush;
+
+	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 = NULL;
+		}
+	if(dFillCol != fill->color) {
+		newBrush = CreateSolidBrush(dFillCol = fill->color);
+		SelectObject(memDC, newBrush);
+		if(hBrush) DeleteObject(hBrush);
+		hBrush = newBrush;
+		}
+	dFillCol = fill->color;
+	dFillCol2 = fill->color2;
+	return true;
+}
+
+bool
+BitMapWin::SetTextSpec(TextDEF *set)
+{
+	return com_SetTextSpec(set, this, &hFont, &TxtSet, &memDC, isWIN95);
+}
+
+bool
+BitMapWin::Erase(DWORD Color)
+{
+	HPEN hBGpen, hOldPen;
+	HBRUSH hBGbrush, hOldBrush;
+
+	hBGpen = CreatePen(PS_SOLID, 1, Color);
+	hBGbrush = CreateSolidBrush(Color);
+	if(hBGpen && memDC) {
+		if(hBGbrush) {
+			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);
+			return true;
+			}
+		if(hBGpen) DeleteObject(hBGpen);
+		}
+	return false;
+}
+
+bool
+BitMapWin::CopyBitmap(int x, int y, anyOutput* sr, int sx, int sy,
+	int sw, int sh, bool invert)
+{
+	BitMapWin *src = (BitMapWin*)sr;
+
+	return(0 != BitBlt(memDC, x, y, sw, sh, src->memDC, sx, sy, 
+		invert ? DSTINVERT : SRCCOPY));
+}
+
+bool
+BitMapWin::oGetTextExtent(char *text, int cb, int *width, int *height)
+{
+	SIZE TextExtent;
+	double si, csi, d;
+
+	if(!text) return false;
+	if(!GetTextExtentPoint32(memDC, text, cb ? cb : strlen(text), &TextExtent))return false;
+	if(fabs(TxtSet.RotBL) >0.01) {
+		si = fabs(sin(TxtSet.RotBL * 0.01745329252));	csi = fabs(cos(TxtSet.RotBL * 0.01745329252));
+		d = si > csi ? 1.0/si : 1.0/csi;
+		d = (TextExtent.cx * ((7.0 + d)/8.0));
+		TextExtent.cx = iround(d);
+		}
+	*width = TextExtent.cx;
+	*height = TextExtent.cy;
+	return true;
+}
+
+bool
+BitMapWin::oGetPix(int x, int y, DWORD *col)
+{
+	DWORD pix;
+	
+	if(x >= DeskRect.left && x < DeskRect.right &&
+		y >= DeskRect.top && y < DeskRect.bottom) {
+		pix = GetPixel(memDC, x, y);
+		*col = pix;
+		return true;
+		}
+	else return false;
+}
+
+bool
+BitMapWin::oDrawIcon(int type, int x, int y)
+{
+	HICON icon = 0L;
+	
+	switch(type) {
+	case ICO_INFO:
+		icon = LoadIcon(0L, IDI_ASTERISK);
+		break;
+	case ICO_ERROR:
+		icon = LoadIcon(0L, IDI_HAND);
+		break;
+	case ICO_RLPLOT:
+		icon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_EVAL));
+		break;
+		}
+	if(icon){
+		DrawIcon(memDC, x, y, icon);
+		return true;
+		}
+	return false;
+}
+
+bool
+BitMapWin::oCircle(int x1, int y1, int x2, int y2, char *nam)
+{
+	BOOL RetVal;
+	
+	RetVal = Ellipse(memDC, x1, y1, x2, y2);
+	if(RetVal && hgo) return hgo->oCircle(x1, y1, x2, y2);
+	else if(RetVal) return true;
+	return false;
+}
+
+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;
+		}
+	else {
+		if(Polyline(memDC, pts, cp))return true;
+		else return false;
+		}
+}
+
+bool
+BitMapWin::oRectangle(int x1, int y1, int x2, int y2, char *name)
+{
+	BOOL RetVal;
+
+	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;
+}
+
+bool
+BitMapWin::oSolidLine(POINT *p)
+{
+	if(Polyline(memDC, p, 2)) return true;
+	return false;
+}
+
+bool
+BitMapWin::oTextOut(int x, int y, char *txt, int cb)
+{
+	return com_oTextOut(x, y, txt, cb, &hFont, &memDC, &TxtSet, isWIN95, this);
+}
+
+bool
+BitMapWin::oPolygon(POINT *pts, int cp, char *nam)
+{
+	BOOL RetVal;
+
+	RetVal = Polygon(memDC, pts, cp);
+	if(RetVal && hgo) return hgo->oPolygon(pts, cp);
+	else if (RetVal) return true;
+	return false;
+}
+
+bool
+BitMapWin::oArc(int x1, int y1, int x2, int y2, int quads)
+{
+	return  com_Arc(memDC, x1, y1, x2, y2, quads);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Output to windows window
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+OutputWin::OutputWin(GraphObj *g, HWND hw):BitMapWin(g, hw)
+{
+	hdc = 0L;
+	if(g) {
+		if(!hw) CreateNewWindow(g);
+		else hWnd = hw;
+		}
+	else {							//its a dialog window
+		hWnd = hw;
+		yAxis.flags = AXIS_INVERT;	//drawing origin upper left corner
+		}
+}
+
+OutputWin::~OutputWin()
+{
+	HideTextCursorObj(this);
+	SendMessage(hWnd, WM_CLOSE, 0, 0L);
+	//Note: HGDI objects are deleted by the BitMapWin destructor
+}
+
+bool
+OutputWin::ActualSize(RECT *rc)
+{
+	if(GetClientRect(hWnd, rc)) return true;
+	return false;
+}
+
+void
+OutputWin::Caption(char *txt)
+{
+	SetWindowText(hWnd, txt);
+}
+
+unsigned char hand_bits[] =	{	//hand cursor bitmap
+	0x01, 0x80, 0x1b, 0xf0, 0x3f, 0xf8, 0x3f, 0xfa,
+	0x1f, 0xff, 0x1f, 0xff, 0x6f, 0xff, 0xff, 0xff,
+	0xff, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x3f, 0xfc,
+	0x1f, 0xfc, 0x0f, 0xf8, 0x07, 0xf8, 0x07, 0xf8};
+
+unsigned char hand_mask[] =	{	//hand cursor mask
+	0xff, 0xff, 0xfe, 0x7f, 0xe6, 0x4f, 0xe6, 0x4f,
+	0xf2, 0x4d, 0xf2, 0x49, 0x78, 0x09, 0x98, 0x01,
+	0x88, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x07,
+	0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f};
+
+unsigned char zoom_bits[] =	{	//zoom cursor bitmap
+	0x00, 0x00, 0x00, 0x00, 0x01, 0xa0, 0x06, 0x30,
+	0x08, 0x08, 0x10, 0x84, 0x10, 0x84, 0x20, 0x02,
+	0x26, 0x32, 0x20, 0x02, 0x10, 0x84, 0x10, 0x84,
+	0x08, 0x08, 0x06, 0x30, 0x01, 0xa0, 0x00, 0x00};
+
+unsigned char zoom_mask[] =	{	//zoom cursor mask
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};
+
+void
+OutputWin::MouseCursor(int cid, bool force)
+{
+	HCURSOR hc, hoc = 0L;
+
+	if(cid == cCursor && !force) return;
+	if(cid == MC_LAST) cid = cCursor;
+	switch(cid) {
+	case MC_ARROW:
+		hoc = SetCursor(LoadCursor(NULL, IDC_ARROW));	break;
+	case MC_CROSS:	hoc = SetCursor(LoadCursor(NULL, IDC_CROSS));	break;
+	case MC_WAIT:
+		hoc = SetCursor(LoadCursor(NULL, IDC_WAIT));	break;
+	case MC_TEXT:	hoc = SetCursor(LoadCursor(NULL, IDC_IBEAM));	break;
+	case MC_NORTH:	hoc = SetCursor(LoadCursor(NULL, IDC_SIZENS));	break;
+	case MC_NE:		hoc = SetCursor(LoadCursor(NULL, IDC_SIZENESW));break;
+	case MC_EAST:	hoc = SetCursor(LoadCursor(NULL, IDC_SIZEWE));	break;
+	case MC_SE:		hoc = SetCursor(LoadCursor(NULL, IDC_SIZENWSE));break;
+	case MC_SALL:	hoc = SetCursor(LoadCursor(NULL, IDC_SIZEALL));	break;	
+	case MC_MOVE:
+		hc = CreateCursor(hInstance, 8, 8, 16, 16, hand_mask, hand_bits);
+		hoc = SetCursor(hc);
+		break;
+	case MC_ZOOM:
+		hc = CreateCursor(hInstance, 8, 8, 16, 16, zoom_mask, zoom_bits);
+		hoc = SetCursor(hc);
+		break;
+	default:	return;
+		}
+	if(hoc) DestroyCursor(hoc);
+	cCursor = cid;
+}
+
+bool
+OutputWin::SetScroll(bool isVert, int iMin, int iMax, int iPSize, int iPos)
+{
+	SCROLLINFO si;
+
+	if(iPos < iMin) return false;
+	si.cbSize = sizeof(SCROLLINFO);
+	si.fMask = SIF_ALL;
+	si.nMin = iMin;
+	si.nMax = iMax;
+	si.nPage = iPSize < iMax ? iPSize : iMax;
+	si.nPos = iPos;
+	si.nTrackPos = 0;
+	SetScrollInfo(hWnd, isVert ? SB_VERT : SB_HORZ, &si, TRUE);
+	return true;
+}
+
+bool
+OutputWin::Erase(DWORD Color)
+{
+	bool bRet;
+	RECT ClientRect;
+
+	if(bRet = BitMapWin::Erase(Color)) {
+		GetClientRect(hWnd, &ClientRect);
+		InvalidateRect(hWnd, &ClientRect, FALSE);
+		}
+	return bRet;
+}
+
+bool
+OutputWin::StartPage()
+{
+	MrkMode = MRK_NONE;
+	MrkRect = 0L;
+	hdc = memDC;
+	if(hgo && hdc) hgo->StartPage();
+	return true;
+}
+
+bool
+OutputWin::EndPage()
+{
+	RECT ClientRect;
+
+	hdc = NULL;
+	GetClientRect(hWnd, &ClientRect);
+	return UpdateRect(&ClientRect, false);
+}
+
+bool
+OutputWin::UpdateRect(RECT *rc, bool invert)
+{
+	HDC dc;
+	BOOL RetVal = FALSE;
+
+	if(dc = GetDC(hWnd)) {
+		RetVal = BitBlt(dc, rc->left, rc->top, rc->right - rc->left,
+			rc->bottom - rc->top, memDC, rc->left, rc->top, invert ? DSTINVERT : SRCCOPY);
+		ReleaseDC(hWnd, dc);
+		}
+	return (RetVal != 0);
+}
+
+bool
+OutputWin::UpdateRect(HDC dc, RECT rc)
+{
+	if(BitBlt(dc, rc.left, rc.top, rc.right - rc.left,
+			rc.bottom - rc.top, memDC, rc.left, rc.top, SRCCOPY))return true;
+	return false;
+}
+
+void
+OutputWin::ShowBitmap(int x, int y, anyOutput* src)
+{
+	int w, h;
+	HDC dc;
+	BitMapWin *sr;
+
+	if(!src) return;
+	sr = (BitMapWin *) src;
+	w = sr->DeskRect.right - sr->DeskRect.left;
+	h = sr->DeskRect.bottom - sr->DeskRect.top;
+	if(dc = GetDC(hWnd)) {
+		BitBlt(dc, x, y, w,	h, sr->memDC, 0, 0, SRCCOPY);
+		ReleaseDC(hWnd, dc);
+		}
+}
+
+void
+OutputWin::ShowLine(POINT * pts, int cp, DWORD color)
+{
+	HDC dc;
+	HPEN hP, oP;
+	
+	if((hP = CreatePen(PS_SOLID, 0, color))&& (dc = GetDC(hWnd))) {
+		oP = (HPEN)SelectObject(dc, hP);
+		Polyline(dc, pts, cp);
+		SelectObject(dc, oP);
+		DeleteObject(hP);
+		ReleaseDC(hWnd, dc);
+		}
+}
+
+void
+OutputWin::ShowEllipse(POINT p1, POINT p2, DWORD color)
+{
+	HDC dc;
+	HPEN hP, oP;
+
+	if((hP = CreatePen(PS_SOLID, 0, color)) && (dc = GetDC(hWnd))) {
+		oP = (HPEN)SelectObject(dc, hP);
+		Arc(dc, p1.x, p1.y, p2.x, p2.y, 0, 0, 0, 0);
+		SelectObject(dc, oP);
+		DeleteObject(hP);
+		ReleaseDC(hWnd, dc);
+		}
+}
+
+bool
+OutputWin::SetMenu(int type)
+{
+	HMENU hMenu = 0L;
+
+	switch(type) {
+	case MENU_NONE:
+		break;
+	case MENU_SPREAD:
+		hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MENU_2));
+		break;
+	case MENU_GRAPH:
+		hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MENU_1));
+		break;
+	case MENU_PAGE:
+		hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MENU_3));
+		}
+	::SetMenu(hWnd, hMenu);
+	return true;
+}
+
+void
+OutputWin::CheckMenu(int mid, bool check)
+{
+	HMENU hMenu = GetMenu(hWnd);
+
+	if(mid < CM_T_STANDARD) switch(mid){					//tool mode identifier
+	case TM_STANDARD:	mid = CM_T_STANDARD;	break;
+	case TM_DRAW:		mid = CM_T_DRAW;		break;
+	case TM_POLYLINE:	mid = CM_T_POLYLINE;	break;
+	case TM_POLYGON:	mid = CM_T_POLYGON;		break;
+	case TM_RECTANGLE:	mid = CM_T_RECTANGLE;	break;
+	case TM_ROUNDREC:	mid = CM_T_ROUNDREC;	break;
+	case TM_ELLIPSE:	mid = CM_T_ELLIPSE;		break;
+	case TM_ARROW:		mid = CM_T_ARROW;		break;
+	case TM_TEXT:		mid = CM_T_TEXT;		break;
+	default:	return;
+		}
+	if(hMenu) CheckMenuItem(hMenu, mid, check ? MF_CHECKED : MF_UNCHECKED);
+}
+
+void
+OutputWin::FileHistory() 
+{
+	HMENU hSubMenu;
+	char **history[] = {&defs.File1, &defs.File2, &defs.File3, &defs.File4, &defs.File5, &defs.File6};
+	int i, j, k;
+
+	if(!hasHistMenu || !defs.File1) return;
+	if(!(hSubMenu = GetSubMenu(GetMenu(hWnd), 0))) return;
+	if(!HistMenuSize) AppendMenu(hSubMenu, MF_SEPARATOR, 0L, 0L);
+    for(i = 0; i < 6 && *history[i]; i++) {
+		k = strlen(*history[i]);
+		for (j = 0; j < k && defs.currPath[j] == (*history[i])[j]; j++);
+		if((*history[i])[j] == '\\' || (*history[i])[j] == '/') j++;
+		if(i < HistMenuSize) {
+			ModifyMenu(hSubMenu, CM_FILE1+i, MF_BYCOMMAND | MF_STRING, CM_FILE1+i, *history[i]+j);
+			}
+		else {
+			AppendMenu(hSubMenu, MF_STRING, CM_FILE1+i, *history[i]+j);
+			}
+		}
+	HistMenuSize = i;
+}
+
+void
+OutputWin::CreateNewWindow(void *g)
+{
+	hWnd = CreateWindow(name, "RLPlot",
+		WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
+		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+		CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
+	SetWindowLong(hWnd, 0, (long)g);		// g is the parent graphic obj
+	SetWindowLong(hWnd, GWL_USERDATA, (long)this);
+	ShowWindow(hWnd, SW_SHOW);
+	UpdateWindow(hWnd);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Copy to Clipboard
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Create Windows Meta File for clipboard
+WinCopyWMF::WinCopyWMF(GraphObj *g)
+{
+	DeskRect.left = DeskRect.top = 0;
+	DeskRect.right = DeskRect.bottom = 0x4fffffffL;
+	hPen = CreatePen(PS_SOLID, 1, dLineCol = 0x00ffffffL);
+	hBrush = CreateSolidBrush(dFillCol =dBgCol = 0x00ffffffL);
+	dPattern = 0L;
+	go = g;
+	hgo = 0L;
+	hres = vres = 1000.0;
+}
+
+WinCopyWMF::~WinCopyWMF()
+{
+	if(hgo) delete hgo;
+	if(hFont) DeleteObject(hFont);
+	if(hBrush) DeleteObject(hBrush);
+	if(hPen) DeleteObject(hPen);
+}
+
+bool
+WinCopyWMF::SetLine(LineDEF *lDef)
+{
+	int iw;
+	HPEN newPen;
+
+	if(!hPen || lDef->width != LineWidth || lDef->width != LineWidth || 
+		lDef->pattern != dPattern || lDef->color != dLineCol) {
+		LineWidth = lDef->width;
+		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 && hPen) return true;
+		iLine = iw;
+		dLineCol = lDef->color;
+		newPen = CreatePen(PS_SOLID, iw > 0 ? iw : 1, dLineCol);
+		SelectObject(hdc, newPen);
+		if(hPen) DeleteObject(hPen);
+		hPen = newPen;
+		}
+	return true;
+}
+
+bool
+WinCopyWMF::SetFill(FillDEF *fill)
+{
+	HBRUSH newBrush;
+
+	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 = NULL;
+		}
+	if(dFillCol != fill->color) {
+		newBrush = CreateSolidBrush(dFillCol = fill->color);
+		SelectObject(hdc, newBrush);
+		if(hBrush) DeleteObject(hBrush);
+		hBrush = newBrush;
+		}
+	dFillCol = fill->color;
+	dFillCol2 = fill->color2;
+	return true;
+}
+
+bool
+WinCopyWMF::SetTextSpec(TextDEF *set)
+{
+	return com_SetTextSpec(set, this, &hFont, &TxtSet, &hdc, true);
+}
+
+bool
+WinCopyWMF::StartPage()
+{
+	int w, h;
+
+
+	if(!go) return false;
+	w = un2ix(go->GetSize(SIZE_GRECT_RIGHT) - go->GetSize(SIZE_GRECT_LEFT));
+	h = un2iy(go->GetSize(SIZE_GRECT_BOTTOM) - go->GetSize(SIZE_GRECT_TOP));
+	w++; h++;
+	if(!(hdc = CreateMetaFile(NULL))) return false;
+	if(hPen)SelectObject(hdc, hPen);
+	if(hBrush) SelectObject(hdc, hBrush);
+	VPorg.fy = -co2fiy(go->GetSize(SIZE_GRECT_TOP));
+	VPorg.fx = -co2fix(go->GetSize(SIZE_GRECT_LEFT));
+	SetMapMode(hdc, MM_HIENGLISH); 
+	SetWindowExtEx(hdc, w, h, 0L);
+	SetWindowOrgEx(hdc, 0, 0, 0L);
+	return true;
+}
+
+bool
+WinCopyWMF::EndPage()
+{
+	HMETAFILE hmf;
+	HGLOBAL hGMem;
+	LPMETAFILEPICT pMFP;
+
+	hmf = CloseMetaFile(hdc);
+	hGMem = GlobalAlloc(GHND, sizeof(METAFILEPICT));
+	pMFP = (LPMETAFILEPICT)GlobalLock(hGMem);
+	pMFP->mm = MM_ANISOTROPIC;
+	pMFP->xExt = (long)(Box1.Xmax+Box1.Xmin);
+	pMFP->yExt = (long)(Box1.Ymax+Box1.Ymin);
+	pMFP->hMF = hmf;
+	GlobalUnlock(hGMem);
+	// We do not open the Clipboard because we are responding to WM_RENDERFORMAT
+	SetClipboardData(CF_METAFILEPICT, hGMem);
+	return true;
+}
+
+bool
+WinCopyWMF::oCircle(int x1, int y1, int x2, int y2, char* nam)
+{
+	BOOL RetVal;
+	
+	RetVal = Ellipse(hdc, x1, y1, x2, y2);
+	if(RetVal && hgo) return hgo->oCircle(x1, y1, x2, y2);
+	else if(RetVal) return true;
+	return false;
+}
+
+bool
+WinCopyWMF::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;
+		}
+	else {
+		if(Polyline(hdc, pts, cp))return true;
+		else return false;
+		}
+	return false;
+}
+
+bool
+WinCopyWMF::oRectangle(int x1, int y1, int x2, int y2, char *name)
+{
+	BOOL RetVal;
+
+	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;
+}
+
+bool
+WinCopyWMF::oSolidLine(POINT *p)
+{
+	if(Polyline(hdc, p, 2)) return true;
+	return false;
+}
+
+bool
+WinCopyWMF::oTextOut(int x, int y, char *txt, int cb)
+{
+	return com_oTextOut(x, y, txt, cb, &hFont, &hdc, &TxtSet, true, this);
+}
+
+bool
+WinCopyWMF::oPolygon(POINT *pts, int cp, char *nam)
+{
+	BOOL RetVal;
+
+	RetVal = Polygon(hdc, pts, cp);
+	if(RetVal && hgo) return hgo->oPolygon(pts, cp);
+	else if (RetVal) return true;
+	return false;
+}
+
+bool
+WinCopyWMF::oArc(int x1, int y1, int x2, int y2, int quads)
+{
+	return  com_Arc(hdc, x1, y1, x2, y2, quads);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Print 
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+PrintWin::PrintWin()
+{
+	int i, j;
+	double pw, ph;
+
+	PrintDriver = PrintDevice = PrintPort = 0L;
+	hPen = 0L;
+	hBrush = 0L;
+	hFont = 0L;
+	hDC = 0L;
+	hgo = 0L;
+	units = defs.cUnits;
+	i = j = 0;
+	GetProfileString("windows", "device", "", TmpTxt, 4096);
+	while(TmpTxt[i] && TmpTxt[i] != ',') i++;
+	TmpTxt[i] = 0;
+	if (i >2) {
+		PrintDevice = strdup(TmpTxt);
+		i++;
+		j = i;
+		while(TmpTxt[i] && TmpTxt[i] != ',') i++;
+		if(i-j > 2) {
+			TmpTxt[i] = 0;
+			PrintDriver = strdup(TmpTxt+j);
+			i++;
+			j = i;
+			while(TmpTxt[i] && TmpTxt[i] != ',') i++;
+			if(i-j > 2) {
+				TmpTxt[i] = 0;
+				PrintPort = strdup(TmpTxt+j);
+				//Get default paper setup
+				if(hDC = CreateDC(PrintDriver, PrintDevice, PrintPort, 0L)) { 
+					pw = GetDeviceCaps(hDC, PHYSICALWIDTH);
+					pw /= (double)GetDeviceCaps(hDC, LOGPIXELSX);
+					ph = GetDeviceCaps(hDC, PHYSICALHEIGHT);
+					ph /= (double)GetDeviceCaps(hDC, LOGPIXELSY);
+					switch (defs.cUnits){
+					case 1:		pw *= 2.54;		ph *= 2.54;		break;
+					case 2:		break;
+					default:	pw *= 25.4;		ph *= 25.4;		break;
+						}
+					FindPaper(pw, ph, 0.01);
+					DeleteDC(hDC);
+					}
+				hDC = 0L;
+				}
+			}
+		}
+}
+
+PrintWin::~PrintWin()
+{
+	if(PrintDriver) free(PrintDriver);
+	if(PrintDevice) free(PrintDevice);
+	if(PrintPort) free(PrintPort);
+	if(hPen) DeleteObject(hPen);
+	if(hBrush) DeleteObject(hBrush);
+	if(hFont) DeleteObject(hFont);
+}
+
+bool
+PrintWin::SetLine(LineDEF *lDef)
+{
+	int iw;
+	HPEN newPen;
+
+	if(!hPen || lDef->width != LineWidth || lDef->width != LineWidth || 
+		lDef->pattern != dPattern || lDef->color != dLineCol) {
+		LineWidth = lDef->width;
+		iw = iround(un2ix(lDef->width));
+		dPattern = lDef->pattern;
+		RLP.finc = 256.0/un2fix(lDef->patlength*8.0);
+		RLP.fp = 0.0;
+		if(iLine == iw && dLineCol == lDef->color && hPen) return true;
+		iLine = iw;
+		dLineCol = lDef->color;
+		newPen = CreatePen(PS_SOLID, iw > 0 ? iw : 1, dLineCol);
+		SelectObject(hDC, newPen);
+		if(hPen) DeleteObject(hPen);
+		hPen = newPen;
+		}
+	return true;
+}
+
+bool
+PrintWin::SetFill(FillDEF *fill)
+{
+	HBRUSH newBrush;
+
+	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 = NULL;
+		}
+	newBrush = CreateSolidBrush(fill->color);
+	SelectObject(hDC, newBrush);
+	if(hBrush) DeleteObject(hBrush);
+	hBrush = newBrush;
+	dFillCol = fill->color;
+	dFillCol2 = fill->color2;
+	return true;
+}
+
+bool
+PrintWin::SetTextSpec(TextDEF *set)
+{
+	return com_SetTextSpec(set, this, &hFont, &TxtSet, &hDC, isWIN95);
+}
+
+bool
+PrintWin::StartPage()
+{
+	DOCINFO DocInfo;
+	bool bRet = false;
+
+	if(hDC = CreateDC(PrintDriver, PrintDevice, PrintPort, 0L)) { 
+		hPen = CreatePen(PS_SOLID, 1, dLineCol = 0x00ffffffL);
+		hBrush = CreateSolidBrush(dFillCol =dBgCol = 0x00ffffffL);
+		dPattern = 0L;
+		SelectObject(hDC, hPen);
+		SelectObject(hDC, hBrush);
+		memset(&DocInfo, 0, sizeof(DOCINFO));
+		DocInfo.lpszDocName = "RLPlot graph";
+		DocInfo.cbSize = sizeof(DOCINFO);
+		DeskRect.left = DeskRect.top = 0;
+		DeskRect.right = GetDeviceCaps(hDC, HORZRES);
+		DeskRect.bottom = GetDeviceCaps(hDC, VERTRES);
+		hres = (double)GetDeviceCaps(hDC, LOGPIXELSX);
+		vres = (double)GetDeviceCaps(hDC, LOGPIXELSY);
+		if(StartDoc(hDC, &DocInfo) >= 0) {
+			if(::StartPage(hDC)>0) bRet = true;
+			}
+		}
+	return bRet;
+}
+
+bool
+PrintWin::EndPage()
+{
+
+	if(hDC) {
+		::EndPage(hDC);		EndDoc(hDC);		DeleteDC(hDC);
+		}
+	hDC = 0L;
+	return true;
+}
+
+bool
+PrintWin::Eject()
+{
+	if(hDC) {
+		::EndPage(hDC);		::StartPage(hDC);	return true;
+		}
+	return false;
+}
+
+bool
+PrintWin::oCircle(int x1, int y1, int x2, int y2, char* nam)
+{
+	BOOL RetVal;
+	
+	RetVal = Ellipse(hDC, x1, y1, x2, y2);
+	if(RetVal && hgo) return hgo->oCircle(x1, y1, x2, y2);
+	else if(RetVal) return true;
+	return false;
+}
+
+bool
+PrintWin::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;
+		}
+	else {
+		if(Polyline(hDC, pts, cp))return true;
+		else return false;
+		}
+}
+
+bool
+PrintWin::oRectangle(int x1, int y1, int x2, int y2, char *name)
+{
+	BOOL RetVal;
+
+	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;
+}
+
+bool
+PrintWin::oSolidLine(POINT *p)
+{
+	if(Polyline(hDC, p, 2)) return true;
+	return false;
+}
+
+bool
+PrintWin::oTextOut(int x, int y, char *txt, int cb)
+{
+	return com_oTextOut(x, y, txt, cb, &hFont, &hDC, &TxtSet, isWIN95, this);
+}
+
+bool
+PrintWin::oPolygon(POINT *pts, int cp, char *nam)
+{
+	BOOL RetVal;
+
+	RetVal = Polygon(hDC, pts, cp);
+	if(RetVal && hgo) return hgo->oPolygon(pts, cp);
+	else if (RetVal) return true;
+	return false;
+}
+
+bool
+PrintWin::oArc(int x1, int y1, int x2, int y2, int quads)
+{
+	return  com_Arc(hDC, x1, y1, x2, y2, quads);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Find a suitable www browser
+void FindBrowser()
+{
+	char text[600];
+	long size = 599;
+	HDC dc;
+	HKEY hdl;
+	HWND hwnd;
+	int i;
+
+	//find the default browser
+	if(ERROR_SUCCESS == RegQueryValue(HKEY_CLASSES_ROOT, "http\\shell\\open\\command", 
+		text, &size) && size > 7) {
+		if(text[0] == '"') {
+			for(i = size-2; i >3; i--) {
+				if(text[i+1] == '"') {
+					text[i+1] = 0;
+					break;
+					}
+				else text[i+1] = 0;
+				}
+			WWWbrowser = strdup(text+1);
+			}
+		else {
+			for(i = size-1; i >5; i--) {
+				if(0 == stricmp(text+i-3, ".exe")) break;
+				else text[i] = 0;
+				}
+			WWWbrowser = strdup(text);
+			}
+		}
+	//find user default data directory
+	if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, "Environment", NULL,
+		KEY_READ, &hdl)) {
+		text[0] = 0;	size=599;
+		RegQueryValueEx(hdl, "HOMEDRIVE", 0, 0, (unsigned char*)text, (unsigned long*)&size);
+		size= 599;
+		RegQueryValueEx(hdl, "HOMEPATH", 0, 0, (unsigned char*)(text+strlen(text)), 
+			(unsigned long*)&size);
+		defs.currPath = strdup(text);
+		}
+	//find user application data directory
+	if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, 
+		"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", NULL,
+		KEY_READ, &hdl)) {
+		text[0] = 0;	size=599;
+		RegQueryValueEx(hdl, "AppData", 0, 0, (unsigned char*)text, (unsigned long*)&size);
+		strcat(text, "\\RLPlot");
+		defs.IniFile = strdup(text);
+		}
+	//find country specific information
+	//  its not a perfect place to do it, but a good one
+	GetProfileString("intl", "sDecimal", ".", text, 2);
+	if(text[0]) defs.DecPoint[0] = text[0];
+	GetProfileString("intl", "sList", ".", text, 2);
+	if(text[0]) defs.ColSep[0] = text[0];
+	if(GetProfileInt("intl", "iMeasure", 0)) defs.dUnits = defs.cUnits = 2;
+	//test if windows95
+	dc = GetDC(hwnd = GetDesktopWindow());
+	isWIN95 = (0 == SetGraphicsMode(dc, GM_ADVANCED));
+	SetGraphicsMode(dc, GM_COMPATIBLE);
+	ReleaseDC(hwnd, dc);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Shutdown or reboot windows
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+void HardExit(bool reboot)
+{
+	int tpsize;
+	HANDLE hProcess, hAccessToken;
+	TOKEN_PRIVILEGES *tp;
+
+	hProcess = GetCurrentProcess();
+	tpsize = sizeof(TOKEN_PRIVILEGES) + sizeof(LUID_AND_ATTRIBUTES);
+	tp = (TOKEN_PRIVILEGES *)malloc(tpsize);
+	if(tp){
+		tp->PrivilegeCount = 1;
+		tp->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+		if(LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tp->Privileges[0].Luid)) {
+			if(OpenProcessToken(hProcess, TOKEN_ADJUST_PRIVILEGES, &hAccessToken))
+				AdjustTokenPrivileges(hAccessToken, FALSE, tp, tpsize, NULL, NULL);
+			}
+		free(tp);
+		}
+	if(reboot) ExitWindowsEx(EWX_REBOOT | EWX_FORCE, NULL);
+	else ExitWindowsEx(EWX_POWEROFF | EWX_FORCE, NULL);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Windos entry point
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInstance,
+	 LPSTR lpCmdLine, int nCmdShow )
+{
+	WNDCLASS wndclass;
+	MSG	msg;
+	DefsRW *drw;
+
+	//OS dependent initialization
+	dlgtxtheight = 16;
+
+	if(lpCmdLine && lpCmdLine[0] && FileExist(lpCmdLine)) LoadFile = strdup(lpCmdLine);
+	hInstance = hInst;
+	wndclass.style = CS_BYTEALIGNWINDOW | CS_DBLCLKS;
+	wndclass.lpfnWndProc = WndProc;
+	wndclass.cbClsExtra = 0;
+	wndclass.cbWndExtra = sizeof(GraphObj*);
+	wndclass.hInstance = hInstance;
+	wndclass.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_EVAL));
+	wndclass.hCursor = 0L;
+	wndclass.hbrBackground = NULL;
+	wndclass.lpszMenuName = 0L;
+	wndclass.lpszClassName = name;
+
+	RegisterClass(&wndclass);
+	ShowBanner(true);
+	InitTextCursor(true);
+	Printer = new PrintWin();
+	accel = LoadAccelerators(hInstance, MAKEINTRESOURCE(ACCELERATORS_1));
+	while(GetMessage(&msg, NULL, 0, 0)){
+		TranslateMessage(&msg);
+		TranslateAccelerator(msg.hwnd, accel, &msg);
+		DispatchMessage(&msg);
+		}
+	if(defs.IniFile) {
+		if(drw = new DefsRW()){
+			drw->FileIO(FILE_WRITE);		delete drw;
+			}
+		}
+	if(WWWbrowser) free(WWWbrowser);
+	if(LoadFile) free(LoadFile);
+	SpreadMain(false);
+	if(Printer) delete Printer;
+	InitTextCursor(false);
+	UnregisterClass(name, hInstance);
+	return msg.wParam;
+}
+
+void CopyData(GraphObj *g, unsigned int cf)
+{
+	HGLOBAL hmem;
+	long cb;
+	unsigned char *dt = 0L;
+	unsigned char *buf;
+
+	if(!g || g->Id != GO_SPREADDATA) return;
+	switch(cf) {
+	case CF_TEXT:
+		if(!g->Command(CMD_COPY_TSV, &dt, 0L))return;
+		break;
+	case CF_SYLK:
+		if(!g->Command(CMD_COPY_SYLK, &dt, 0L))return;
+		break;
+	default:
+		if(cf == cf_rlpxml && g->Command(CMD_COPY_XML, &dt, 0L)) break;
+		else return;
+		}
+	cb = strlen((char*)dt);
+	if(hmem = GlobalAlloc(GMEM_MOVEABLE, cb+2)) {
+		if(buf = (unsigned char *)GlobalLock(hmem)) {
+			memcpy(buf, dt, cb+1);
+			GlobalUnlock(hmem);
+			SetClipboardData(cf, hmem);
+			}
+		}
+}
+
+void CopyGraph(GraphObj *g, unsigned int cf)
+{
+	HGLOBAL hmem;
+	char *dt, *buf;
+	long cb;
+
+	if(!(dt = GraphToMem(g, &cb)))return;
+	if(hmem = GlobalAlloc(GMEM_MOVEABLE, cb+1)) {
+		if(buf = (char *)GlobalLock(hmem)) {
+			memcpy(buf, dt, cb);
+			buf[cb] = 0;
+			GlobalUnlock(hmem);
+			SetClipboardData(cf, hmem);
+			}
+		}
+	free(dt);
+}
+
+void ScrollEvent(bool bVert, HWND hwnd, UINT type, GraphObj *g, OutputWin *w)
+{
+	SCROLLINFO si;
+	int LineStep, cmd, pos;
+
+	if(hwnd && g && w) {
+		cmd = bVert ? CMD_SETVPOS : CMD_SETHPOS;
+		LineStep = g->Id == GO_GRAPH ? 8 : 1;
+		si.fMask = SIF_ALL;
+		si.cbSize = sizeof(SCROLLINFO);
+		if(!(GetScrollInfo(hwnd, bVert ? SB_VERT : SB_HORZ, &si)))return;
+		switch(type){
+		case SB_LINEUP:
+			pos = si.nPos - LineStep;
+			break;
+		case SB_LINEDOWN:
+			pos = si.nPos + LineStep;
+			break;
+		case SB_PAGEUP:
+			pos = (si.nPos - (int)si.nPage) >= si.nMin ?
+				(si.nPos - si.nPage) : si.nMin;
+			break;
+		case SB_PAGEDOWN:
+			pos = ((unsigned)si.nPos + si.nPage*2) < (unsigned)si.nMax ?
+				(si.nPos + si.nPage) : (si.nMax - si.nPage+1);
+			break;
+		case SB_THUMBTRACK:
+		case SB_THUMBPOSITION:
+			pos = si.nTrackPos;
+			break;
+		default:
+			return;
+			}
+		g->Command(cmd, (void*)(& pos), w);
+		}
+}
+
+long OpenFileFromHistory(OutputWin *w, GraphObj *g, int id)
+{
+	char *name = 0L;
+
+	switch (id) {
+	case 0:			name = defs.File1;			break;
+	case 1:			name = defs.File2;			break;
+	case 2:			name = defs.File3;			break;
+	case 3:			name = defs.File4;			break;
+	case 4:			name = defs.File5;			break;
+	case 5:			name = defs.File6;			break;
+		}
+	if(name && FileExist(name)) {
+		g->Command(CMD_DROPFILE, name, w);
+		defs.FileHistory(name);
+		w->FileHistory();
+		}
+	return 0;
+}
+
+static GraphObj *copy_obj;
+
+long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
+{
+	static WinCopyWMF *CopyWMF = NULL;
+	static BitMapWin *CopyBMP = NULL;
+	static bool CtrlDown = false;
+	PAINTSTRUCT ps;
+	OutputWin *w;
+	GraphObj *g;
+	MouseEvent mev;
+	HDC dc;
+	int cc;
+
+	g = (GraphObj *) GetWindowLong(hwnd, 0);
+	w = (OutputWin *) GetWindowLong(hwnd, GWL_USERDATA);
+	if(g && w) switch(message) {
+	case WM_SETFOCUS:
+		if(g->Id == GO_GRAPH) CurrGraph = (Graph*)g;
+		else CurrGraph = 0L;
+		break;
+	case WM_LBUTTONDOWN:	case WM_LBUTTONDBLCLK:
+		HideTextCursor();
+	case WM_MOUSEMOVE:		case WM_RBUTTONUP:		case WM_LBUTTONUP:
+		mev.x = LOWORD(lParam);
+		mev.y = HIWORD(lParam);
+		mev.StateFlags = 0;
+		if(wParam & MK_LBUTTON) mev.StateFlags |= 1;
+		if(wParam & MK_MBUTTON) mev.StateFlags |= 2;
+		if(wParam & MK_RBUTTON) mev.StateFlags |= 4;
+		if(wParam & MK_SHIFT) mev.StateFlags |= 8;
+		if(wParam & MK_CONTROL) mev.StateFlags |= 16;
+		if(message == WM_LBUTTONUP) mev.Action = MOUSE_LBUP;
+		else if(message == WM_RBUTTONUP) mev.Action = MOUSE_RBUP;
+		else if(message == WM_LBUTTONDBLCLK) mev.Action = MOUSE_LBDOUBLECLICK;
+		else if(message == WM_LBUTTONDOWN) mev.Action = MOUSE_LBDOWN;
+		else if(message == WM_MOUSEMOVE)mev.Action = MOUSE_MOVE;
+		g->Command(CMD_MOUSE_EVENT, (void *)&mev, w);
+		break;
+	case WM_KEYDOWN:
+		if(g && w && (GetKeyState(VK_LCONTROL) || GetKeyState(VK_RCONTROL))){
+			cc = (wParam & 0xff);
+			if(cc == 0xbb || cc == 0x6b ) g->Command(CMD_ZOOM, &"+", w);
+			else if(cc == 0xbd || cc == 0x6d ) g->Command(CMD_ZOOM, &"-", w);
+			else break;
+			return 0;
+			}
+		break;
+	case WM_CHAR:
+		cc = (wParam & 0xff);
+		g->Command(CMD_ADDCHAR, (void *)(& cc), w);
+		return 0;
+	case WM_VSCROLL:		case WM_HSCROLL:
+		ScrollEvent(message == WM_VSCROLL, hwnd, wParam & 0xffff, g, w);
+		return 0;
+		}  
+
+	switch(message) {
+	case WM_CREATE:
+		//assume that the first window created is the main window
+		if(!MainWnd) MainWnd = hwnd;
+		break;
+	case WM_SIZE:
+		if(g && w) g->Command(CMD_SETSCROLL, 0L, w);
+		break;
+	case WM_INITMENUPOPUP:		case WM_NCMOUSEMOVE:
+		SetCursor(LoadCursor(NULL, IDC_ARROW));
+		break;
+	case WM_SETCURSOR:
+		if(w) w->MouseCursor(MC_LAST, true);
+		return TRUE;
+	case WM_SETFOCUS:
+		if(g && w) if(!g->Command(message == WM_SETFOCUS ?
+			CMD_SETFOCUS : CMD_KILLFOCUS, NULL, w))
+			SetCursor(LoadCursor(NULL, IDC_ARROW));
+		return 0;
+	case WM_DESTROYCLIPBOARD:
+		return 0;
+	case WM_RENDERALLFORMATS:
+		// we do not support leaving data on the clipboard after exit
+		OpenClipboard(hwnd);
+		EmptyClipboard();
+		CloseClipboard();
+		return 0;
+	case WM_RENDERFORMAT:
+		if(g && w) switch(wParam){
+			case CF_METAFILEPICT:
+				CopyWMF = new WinCopyWMF(copy_obj);
+				if(CopyWMF && CopyWMF->StartPage()) {
+					copy_obj->DoPlot(CopyWMF);
+					CopyWMF->EndPage();
+					}
+				delete CopyWMF;
+				CopyWMF = NULL;
+				if(copy_obj->Id == GO_GRAPH || copy_obj->Id == GO_PAGE) copy_obj->DoPlot(0L);
+				break;
+			case CF_BITMAP:
+				if((CopyBMP = new BitMapWin(copy_obj)) && CopyBMP->StartPage()) {
+					copy_obj->DoPlot(CopyBMP);
+					CopyBMP->EndPage();
+					SetClipboardData(CF_BITMAP, CopyBMP->scr);
+					CopyBMP->scr = 0L;
+					}
+				delete CopyBMP;
+				CopyBMP = NULL;
+				if(copy_obj->Id == GO_GRAPH || copy_obj->Id == GO_PAGE) copy_obj->DoPlot(0L);
+				break;
+			case CF_SYLK:		case CF_TEXT:
+				if(g->Id == GO_SPREADDATA) CopyData(g, wParam);
+				break;
+			default:
+				if(wParam == cf_rlpgraph) CopyGraph(copy_obj, wParam);
+				if(wParam == cf_rlpxml) CopyData(g, wParam);
+				break;
+			}
+		if(w->Erase(defs.Color(COL_BG))) g->DoPlot(w);
+		return 0;
+	case WM_COMMAND:
+		wParam &= 0xffff;
+		if(g && w) switch(wParam) {
+		case CM_EXIT:
+			g->Command(CMD_CAN_DELETE, 0L, 0L);
+			SetWindowLong(hwnd, 0, 0L);
+			SetWindowLong(hwnd, GWL_USERDATA, 0L);
+			w->go = 0L;
+			DestroyWindow(hwnd);
+			return 0;
+		case CM_PASTE:
+			w->MouseCursor(MC_WAIT, true);
+			if(g->Id == GO_SPREADDATA || g->Id == GO_PAGE ||
+				g->Id == GO_GRAPH)TestClipboard(g);
+			w->MouseCursor(MC_ARROW, true);
+			return 0;
+		case CM_COPY:			case CM_CUT:
+			EmptyClip();
+			OpenClipboard(hwnd);
+			if(g->Id == GO_SPREADDATA && g->Command(wParam == CM_CUT ? CMD_CUT : CMD_QUERY_COPY, 0L, w)) {
+				SetClipboardData(CF_TEXT, NULL);
+				SetClipboardData(CF_SYLK, NULL);
+				SetClipboardData(cf_rlpxml, NULL);
+				}
+			CloseClipboard();
+			return 0;
+		case CM_UPDATE:
+			g->Command(CMD_UPDATE, 0L, w);
+			return 0;
+		case CM_COPYGRAPH:
+			EmptyClip();
+			OpenClipboard(GetFocus());
+			SetClipboardData(CF_METAFILEPICT, NULL);
+			SetClipboardData(CF_BITMAP, NULL);
+			SetClipboardData(cf_rlpgraph, NULL);
+			if(g->Id == GO_PAGE && CurrGraph) CopyGraph(CurrGraph, cf_rlpobj);
+			copy_obj = g;
+			CloseClipboard();
+			return 0;
+		case CM_OPEN:
+			g->Command(CMD_OPEN, (void *)NULL, w);
+			return 0;
+		case CM_FILE1:	case CM_FILE2:	case CM_FILE3:
+		case CM_FILE4:	case CM_FILE5:	case CM_FILE6:
+			return OpenFileFromHistory(w, g, wParam - CM_FILE1);
+		case CM_FILLRANGE:
+			g->Command(CMD_FILLRANGE, (void *)NULL, w);
+			return 0;
+		case CM_ABOUT:
+			RLPlotInfo();
+			return 0;
+		case CM_ZOOM25:
+			g->Command(CMD_ZOOM, &"25", w);
+			return 0;
+		case CM_ZOOM50:
+			g->Command(CMD_ZOOM, &"50", w);
+			return 0;
+		case CM_ZOOM100:
+			g->Command(CMD_ZOOM, &"100", w);
+			return 0;
+		case CM_ZOOM200:
+			g->Command(CMD_ZOOM, &"200", w);
+			return 0;
+		case CM_ZOOM400:
+			g->Command(CMD_ZOOM, &"400", w);
+			return 0;
+		case CM_ZOOMIN:
+			g->Command(CMD_ZOOM, &"+", w);
+			return 0;
+		case CM_ZOOMOUT:
+			g->Command(CMD_ZOOM, &"-", w);
+			return 0;
+		case CM_ZOOMFIT:
+			g->Command(CMD_ZOOM, &"fit", w);
+			return 0;
+		case CM_ADDPLOT:
+			g->Command(CMD_ADDPLOT, 0L, w);
+			return 0;
+		case CM_LEGEND:
+			g->Command(CMD_LEGEND, 0L, w);
+			return 0;
+		case CM_LAYERS:
+			g->Command(CMD_LAYERS, 0L, w);
+			return 0;
+		case CM_NEWGRAPH:
+			g->Command(CMD_NEWGRAPH, 0L, w);
+			return 0;
+		case CM_NEWPAGE:
+			g->Command(CMD_NEWPAGE, 0L, w);
+			return 0;
+		case CM_DELGRAPH:
+			g->Command(CMD_DELGRAPH, 0L, w);
+			return 0;
+		case CM_SAVEDATAAS:
+			g->Command(CMD_SAVEDATAAS, 0L, w);
+			return 0;
+		case CM_REDRAW:
+			if(w->Erase(defs.Color(COL_BG))) g->DoPlot(w);
+			return 0;
+		case CM_DELOBJ:
+			if(CurrGO && CurrGO->parent && CurrGO->parent->
+				Command(CMD_DELOBJ, (void*)CurrGO, w)) {
+				CurrGO = 0L;
+				if(w->Erase(defs.Color(COL_BG))) g->DoPlot(w);
+				}
+			else if(!CurrGO) InfoBox("No object selected!");
+			return 0;
+		case CM_SAVEGRAPHAS:
+			SaveGraphAs(g);
+			return 0;
+		case CM_EXPORT:
+			OpenExportName(g, "hello.svg");
+			g->DoPlot(w);
+			return 0;
+		case CM_PRINT:
+			if(g->Id == GO_SPREADDATA){
+				g->Command(CMD_PRINT, 0L, Printer);
+				return 0;
+				}
+			if(Printer && Printer->StartPage()) {
+				SetCursor(LoadCursor(0L, IDC_WAIT));
+				g->DoPlot(Printer);
+				Printer->EndPage();
+				w->Erase(defs.Color(COL_BG));
+				g->DoPlot(w);
+				SetCursor(LoadCursor(0L, IDC_ARROW));
+				}
+			return 0;
+		case CM_ADDROWCOL:
+			g->Command(CMD_ADDROWCOL, (void *)NULL, w);
+			return 0;
+		case CM_REBOOT:
+			HardExit(true);
+			return 0;
+		case CM_SHUTDOWN:
+			HardExit(false);
+			return 0;
+		case CM_DEFAULTS:
+			g->Command(CMD_CONFIG, 0L, w);
+			return 0;
+		case CM_ADDAXIS:
+			g->Command(CMD_ADDAXIS, 0L, w);
+			return 0;
+		case CM_T_STANDARD:
+			cc = TM_STANDARD;
+			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
+			return 0;
+		case CM_T_DRAW:
+			cc = TM_DRAW;
+			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
+			return 0;
+		case CM_T_POLYLINE:
+			cc = TM_POLYLINE;
+			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
+			return 0;
+		case CM_T_POLYGON:
+			cc = TM_POLYGON;
+			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
+			return 0;
+		case CM_T_RECTANGLE:
+			cc = TM_RECTANGLE;
+			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
+			return 0;
+		case CM_T_ROUNDREC:
+			cc = TM_ROUNDREC;
+			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
+			return 0;
+		case CM_T_ELLIPSE:
+			cc = TM_ELLIPSE;
+			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
+			return 0;
+		case CM_T_ARROW:
+			cc = TM_ARROW;
+			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
+			return 0;
+		case CM_T_TEXT:
+			cc = TM_TEXT;
+			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
+			return 0;
+		case CM_DELKEY:		case CM_LEFTARRKEY:		case CM_RIGHTARRKEY:
+		case CM_UPARRKEY:   case CM_DOWNARRKEY:		case CM_POS_FIRST:
+		case CM_POS_LAST:	case CM_SHLEFT:			case CM_SHRIGHT:
+		case CM_SHUP:		case CM_SHDOWN:			case CM_TAB:
+		case CM_SHTAB:
+			switch(wParam) {
+			case CM_DELKEY:			cc = CMD_DELETE;		break;
+			case CM_LEFTARRKEY:		cc = CMD_CURRLEFT;		break;
+			case CM_RIGHTARRKEY:	cc = CMD_CURRIGHT;		break;
+			case CM_UPARRKEY:		cc = CMD_CURRUP;		break;
+			case CM_DOWNARRKEY:		cc = CMD_CURRDOWN;		break;
+			case CM_POS_FIRST:		cc = CMD_POS_FIRST;		break;
+			case CM_POS_LAST:		cc = CMD_POS_LAST;		break;
+			case CM_SHLEFT:			cc = CMD_SHIFTLEFT;		break;
+			case CM_SHRIGHT:		cc = CMD_SHIFTRIGHT;	break;
+			case CM_SHUP:			cc = CMD_SHIFTUP;		break;
+			case CM_SHDOWN:			cc = CMD_SHIFTDOWN;		break;
+			case CM_TAB:			cc = CMD_TAB;			break;
+			case CM_SHTAB:			cc = CMD_SHTAB;			break;
+				}
+			g->Command(cc, (void *)NULL, w);
+			return 0;
+		case CM_PGUP:		case CM_PGDOWN:
+			g->Command(wParam == CM_PGUP ? CMD_PAGEUP : CMD_PAGEDOWN, 0L, w);
+			return 0;
+		case CM_UNDO:
+			g->Command(CMD_UNDO, 0L, w);
+			return 0;
+		default:
+			sprintf(TmpTxt, "Command 0x%x (%d)\nreceived", wParam & 0xffff,
+				wParam & 0xffff);
+			MessageBox(hwnd, TmpTxt, "Info", MB_OK | MB_ICONINFORMATION);
+		}
+		return 0;
+	case WM_CLOSE:
+		if(g) {
+			g->Command(CMD_CAN_DELETE, 0L, 0L);
+			SetWindowLong(hwnd, 0, 0L);
+			SetWindowLong(hwnd, GWL_USERDATA, 0L);
+			w->go = 0L;
+			}
+		DestroyWindow(hwnd);
+		return 0;
+	case WM_DESTROY:
+		if(hwnd == MainWnd)PostQuitMessage(0);
+		break;
+	case WM_PAINT:
+		dc = BeginPaint(hwnd, &ps);
+		if(w && dc) w->UpdateRect(dc, ps.rcPaint);
+		EndPaint(hwnd, &ps);
+		break;
+	}
+	return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Dialog window: Windows specific
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+const char dlgname[] = "RLDLGWIN";
+
+long FAR PASCAL DlgWndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
+{
+	OutputWin *w;
+	tag_DlgObj *d;
+	PAINTSTRUCT ps;
+	HDC dc;
+	MouseEvent mev;
+	int i, cc;
+
+	d = (tag_DlgObj *) GetWindowLong(hwnd, 0);
+	w = (OutputWin *) GetWindowLong(hwnd, GWL_USERDATA);
+	switch(message) {
+	case WM_CREATE:
+		break;
+	case WM_KILLFOCUS:
+		HideTextCursorObj(w);
+		if(d) d->Command(CMD_ENDDIALOG, NULL, w);
+		return 0;
+	case WM_TIMER:
+		if(d) d->Command(CMD_ENDDIALOG, d, w);
+		return 0;
+	case WM_DESTROY:	case WM_CLOSE:
+		if(d) {
+			d->Command(CMD_UNLOCK, 0L, w);
+			d->Command(CMD_ENDDIALOG, 0L, w);
+			SetWindowLong(hwnd, 0, NULL);
+			}
+		if(w) {
+			SetWindowLong(hwnd, GWL_USERDATA, NULL);
+			delete w;
+			}
+		break;
+	case WM_CHAR:
+		if(0x09 == (cc = wParam & 0xff)) break;		//ignore Tab
+		if(d && w) d->Command(CMD_ADDCHAR, (void *)(& cc), w);
+		break;
+	case WM_LBUTTONDOWN:	case WM_LBUTTONDBLCLK:
+		HideTextCursor();
+	case WM_RBUTTONUP:		case WM_LBUTTONUP:		case WM_MOUSEMOVE:
+		mev.x = LOWORD(lParam);		mev.y = HIWORD(lParam);		mev.StateFlags = 0;
+		if(wParam & MK_LBUTTON) mev.StateFlags |= 1;
+		if(wParam & MK_MBUTTON) mev.StateFlags |= 2;
+		if(wParam & MK_RBUTTON) mev.StateFlags |= 4;
+		if(wParam & MK_SHIFT) mev.StateFlags |= 8;
+		if(wParam & MK_CONTROL) mev.StateFlags |= 16;
+		if(message == WM_MOUSEMOVE) mev.Action = MOUSE_MOVE;
+		else if(message == WM_LBUTTONDOWN) mev.Action = MOUSE_LBDOWN;
+		else if(message == WM_LBUTTONUP) mev.Action = MOUSE_LBUP;
+		else if(message == WM_RBUTTONUP) mev.Action = MOUSE_RBUP;
+		else if(message == WM_LBUTTONDBLCLK) mev.Action = MOUSE_LBDOUBLECLICK;
+		if(d && w) d->Command(CMD_MOUSE_EVENT, (void *)&mev, w);
+		break;
+	case WM_COMMAND:
+		wParam &= 0xffff;
+		i = 0;
+		switch(wParam) {
+		case CM_DELKEY:			i = CMD_DELETE;		break;
+		case CM_LEFTARRKEY:		i = CMD_CURRLEFT;	break;
+		case CM_RIGHTARRKEY:	i = CMD_CURRIGHT;	break;
+		case CM_UPARRKEY:		i = CMD_CURRUP;		break;
+		case CM_DOWNARRKEY:		i = CMD_CURRDOWN;	break;
+		case CM_TAB:			i = CMD_TAB;		break;
+		case CM_SHTAB:			i = CMD_SHTAB;		break;
+		case CM_POS_FIRST:		i = CMD_POS_FIRST;	break;
+		case CM_POS_LAST:		i = CMD_POS_LAST;	break;
+		case CM_SHLEFT:			i = CMD_SHIFTLEFT;	break;
+		case CM_SHRIGHT:		i = CMD_SHIFTRIGHT;	break;
+		case CM_UNDO:			i = CMD_UNDO;		break;
+			}
+		if(i && d && w) d->Command(i, 0L, w);
+		return 0;
+	case WM_PAINT:
+		dc = BeginPaint(hwnd, &ps);
+		if(w && dc){
+      		w->HideMark();		w->UpdateRect(dc, ps.rcPaint);
+			}
+		EndPaint(hwnd, &ps);
+		break;
+	}
+	return DefWindowProc(hwnd, message, wParam, lParam);
+}
+
+void *CreateDlgWnd(char *title, int x, int y, int width, int height, tag_DlgObj *d, DWORD flags)
+{
+	WNDCLASS wndclass;
+	OutputWin *w;
+	HWND hDlg, hFoc;
+	RECT rec, BoxRec, DeskRect;
+	DWORD ws;
+
+	wndclass.style = CS_BYTEALIGNWINDOW | CS_DBLCLKS;
+	wndclass.lpfnWndProc = DlgWndProc;
+	wndclass.cbClsExtra = 0;
+	wndclass.cbWndExtra = sizeof(tag_DlgObj *);
+	wndclass.hInstance = hInstance;
+	wndclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_EVAL));
+	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
+	wndclass.hbrBackground = NULL;
+	wndclass.lpszMenuName = NULL;
+	wndclass.lpszClassName = dlgname;
+	RegisterClass(&wndclass);
+	hFoc = GetFocus();
+	if(hFoc) {
+		GetWindowRect(hFoc, &rec);
+		x += rec.left;
+		y += rec.top;
+		}
+	ws = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS;
+	if(!(flags & 0x2)) ws |= WS_CAPTION;
+	hDlg = CreateWindow(dlgname, title,	ws,
+		x, y, width, height, GetFocus(), NULL, hInstance, NULL);
+	w = new OutputWin(0L, hDlg);
+	w->units = defs.cUnits;
+	if(hDlg && w && w->Erase(0x00c0c0c0L)) {
+		SetWindowLong(hDlg, GWL_USERDATA, (long)w);
+		SetWindowLong(hDlg, 0, (long)d);
+		ShowWindow(hDlg, SW_SHOW);
+		if(flags & 0x01) {					//center on screen
+			GetWindowRect(hDlg, &BoxRec);
+			GetClientRect(GetDesktopWindow(), &DeskRect);
+			SetWindowPos(hDlg, HWND_TOPMOST, (DeskRect.right -DeskRect.left)/2 -
+				(BoxRec.right - BoxRec.left)/2, (DeskRect.bottom -DeskRect.top)/2 -
+				(BoxRec.bottom- BoxRec.top)/2, BoxRec.right - BoxRec.left,
+				BoxRec.bottom - BoxRec.top, 0);
+			}
+		if(flags & 0x04)			SetTimer(hDlg, 1, 100, 0L);
+		UpdateWindow(hDlg);			d->DoPlot(w);
+		}
+	else {
+		if(w) delete (w);			return 0L;
+		}
+	return hDlg;
+}
+
+void LoopDlgWnd() 	//keep message processing running
+{
+	MSG	msg;
+	
+	GetMessage(&msg, NULL, 0, 0);	TranslateMessage(&msg);
+	TranslateAccelerator(msg.hwnd, accel, &msg);
+	DispatchMessage(&msg);
+}
+
+void CloseDlgWnd(void *hDlg)
+{
+	if(hDlg) SendMessage((HWND)hDlg, WM_CLOSE, 0, 0);
+}
+
+void ShowDlgWnd(void *hDlg)
+{
+	ShowWindow((HWND)hDlg, SW_SHOW);
+	SetFocus((HWND)hDlg);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// OS independent interface to Windows specific classes
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+anyOutput *NewDispClass(GraphObj *g)
+{
+	return new OutputWin(g, 0L);
+}
+
+bool DelDispClass(anyOutput *w)
+{
+	if (w) delete (OutputWin*) w;
+	return true;
+}
+
+anyOutput *NewBitmapClass(int w, int h, double hr, double vr)
+{
+	return new BitMapWin(w, h, hr, vr);
+}
+
+bool DelBitmapClass(anyOutput *w)
+{
+	if (w) delete (BitMapWin*) w;
+	return true;
+}
diff --git a/WinSpec.h b/WinSpec.h
new file mode 100755
index 0000000..f6a6423
--- /dev/null
+++ b/WinSpec.h
@@ -0,0 +1,130 @@
+//WinSpec.h, Copyright (c) 2000, 2001 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
+//
+class BitMapWin:public anyOutput{
+public:
+	HBITMAP scr;
+	GraphObj *go;
+	HDC memDC;
+	HPEN hPen, oldPen;
+	HBRUSH hBrush, oldBrush;
+	HFONT hFont;
+	HatchOut *hgo;
+
+	BitMapWin(GraphObj *g, HWND hw);
+	BitMapWin(int w, int h, double hr, double vr);
+	BitMapWin(GraphObj *g);
+	~BitMapWin();
+	bool SetLine(LineDEF *lDef);
+	bool SetFill(FillDEF *fill);
+	bool SetTextSpec(TextDEF *set);
+	virtual bool Erase(DWORD Color);
+	virtual bool StartPage() {return true;};
+	bool CopyBitmap(int x, int y, anyOutput* src, int sx, int sy,
+		int sw, int sh, bool invert);
+	bool oGetTextExtent(char *text, int cb, int *width, int *height);
+	bool oGetPix(int x, int y, DWORD *col);
+	bool oDrawIcon(int type, int x, int y);
+	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);
+	bool oArc(int x1, int y1, int x2, int y2, int quads);
+};
+
+class OutputWin:public BitMapWin{
+public:
+	HWND hWnd;
+	HDC hdc;
+
+	OutputWin(GraphObj *g, HWND hw);
+	~OutputWin();
+	bool ActualSize(RECT *rc);
+	void Caption(char *txt);
+	void MouseCursor(int cid, bool force);
+	bool SetScroll(bool isVert, int iMin, int iMax, int iPSize, int iPos);
+	bool Erase(DWORD Color);
+	bool StartPage();
+	bool EndPage();
+	bool UpdateRect(RECT *rc, bool invert);
+	bool UpdateRect(HDC hdc, RECT rc);
+	void ShowBitmap(int x, int y, anyOutput* src);
+	void ShowLine(POINT * pts, int cp, DWORD color);
+	void ShowEllipse(POINT p1, POINT p2, DWORD color); 
+	bool SetMenu(int type);
+	void CheckMenu(int mid, bool check);
+	void FileHistory();
+
+private:
+	void CreateNewWindow(void *g);
+};
+
+class WinCopyWMF:public anyOutput {
+public:
+	WinCopyWMF(GraphObj *g);
+	~WinCopyWMF();
+	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);
+	bool oArc(int x1, int y1, int x2, int y2, int quads);
+
+private:
+	GraphObj *go;
+	HDC hdc;
+	HPEN hPen;
+	HBRUSH hBrush;
+	HFONT hFont;
+	HatchOut *hgo;
+};
+
+class PrintWin:public anyOutput{
+public:
+	PrintWin();
+	~PrintWin();
+	bool SetLine(LineDEF *lDef);
+	bool SetFill(FillDEF *fill);
+	bool SetTextSpec(TextDEF *set);
+	bool StartPage();
+	bool EndPage();
+	bool Eject();
+	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);
+	bool oArc(int x1, int y1, int x2, int y2, int quads);
+
+private:
+	HPEN hPen;
+	HBRUSH hBrush;
+	HFONT hFont;
+	HatchOut *hgo;
+	char *PrintDevice, *PrintDriver, *PrintPort;
+	HDC hDC;
+};
diff --git a/exprlp.cpp b/exprlp.cpp
new file mode 100755
index 0000000..b374c7f
--- /dev/null
+++ b/exprlp.cpp
@@ -0,0 +1,532 @@
+//exprlp.cpp, Copyright (c) 2002, 2003, 2004, 2005 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
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// STUBS: we do not need dialogs
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+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;
+}
+
+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;
+}
+
+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 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 Scatt3D::PropertyDlg()
+{
+	return false;
+}
+
+bool FitFunc::PropertyDlg()
+{
+	return false;
+}
+
+bool Plot3D::PropertyDlg()
+{
+	return false;
+}
+
+bool Plot3D::AddPlot(int)
+{
+	return false;
+}
+
+bool Chart25D::PropertyDlg()
+{
+	return false;
+}
+
+bool Ribbon25D::PropertyDlg()
+{
+	return false;
+}
+
+bool BubblePlot3D::PropertyDlg()
+{
+	return false;
+}
+
+bool GridLine::PropertyDlg()
+{
+	return false;
+}
+
+bool Tick::PropertyDlg()
+{
+	return false;
+}
+
+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 Page::Configure()
+{
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// some more STUBS .....
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+char *SaveGraphAsName(char *oldname)
+{
+	return 0L;
+}
+
+char *OpenGraphName(char *oldname)
+{
+	return 0L;
+}
+
+void HideTextCursor()
+{
+	return;
+}
+
+void HideTextCursorObj(anyOutput *out)
+{
+	return;
+}
+
+void ShowTextCursor(anyOutput *out, RECT *disp, DWORD color)
+{
+	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;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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:
+	char *name1, *name2;
+	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);
+	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;
+			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-2004 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 '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;
+		}
+	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;
+}
diff --git a/gpl.txt b/gpl.txt
new file mode 100755
index 0000000..5b6e7c6
--- /dev/null
+++ b/gpl.txt
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+

+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+

+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+

+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+

+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+

+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/mfcalc.cpp b/mfcalc.cpp
new file mode 100755
index 0000000..8f7f943
--- /dev/null
+++ b/mfcalc.cpp
@@ -0,0 +1,2299 @@
+
+/*  A Bison parser, made from ../../rlplot/mfcalc.y
+    by GNU Bison version 1.28  */
+
+#define YYBISON 1  /* Identify Bison output.  */
+
+#define	NUM	257
+#define	ARR	258
+#define	STR	259
+#define	PI	260
+#define	E	261
+#define	CLVAL	262
+#define	VAR	263
+#define	FNCT	264
+#define	TXT	265
+#define	CLAUSE	266
+#define	COLR	267
+#define	COLC	268
+#define	AND	269
+#define	OR	270
+#define	EQ	271
+#define	NE	272
+#define	GT	273
+#define	GE	274
+#define	LT	275
+#define	LE	276
+#define	NEG	277
+
+
+/*
+ mfcalc.y, mfcalc.cpp, Copyright (c) 2004, 2005 R.Lackner
+ parse string and simple math: based on the bison 'mfcalc' example
+
+    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 <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include "rlplot.h"
+
+struct symrec {
+	int type, row, col;
+	unsigned int h_name;
+	char *name, *text;
+	struct {
+		double var;
+		double (*fnctptr)(...);
+		} value;
+	int arg_type;
+	struct symrec *next;
+};
+
+// syntactical information
+struct syntax_info {
+	int last_tok;			//resolve ambiguous ':'
+	double clval;			//current value for where - clause
+	int cl1, cl2;			//limits of clause formula in buffer
+	struct syntax_info *next;
+	};
+static syntax_info *syntax_level = 0L;
+
+
+typedef struct{
+	double  val;
+	int type;
+	symrec  *tptr;
+	double *a_data;
+	char *text;
+	int a_count;
+
+}YYSTYPE;
+
+static symrec *putsym (unsigned int h_name, int sym_type, int arg_type);
+static symrec *getsym (unsigned int h_name, char *sym_name = 0L);
+static int push(YYSTYPE *res, YYSTYPE *val);
+static void store_res(YYSTYPE *res);
+static char *PushString(char *text);
+static char *add_strings(char *st1, char *st2);
+static char *string_value(YYSTYPE *exp);
+static int get_range(YYSTYPE *res, char *first, char *last);
+static void exec_clause(YYSTYPE *res);
+static YYSTYPE *proc_clause(YYSTYPE *res);
+static void yyerror(char *s);
+static int yylex(void);
+
+static char res_txt[1000];
+static anyResult line_res = {ET_UNKNOWN, 0.0, res_txt};
+static double line_result;
+static DataObj *curr_data;
+
+//the current command buffer
+static char *buffer = 0L;
+static int buff_pos = 0;
+#include <stdio.h>
+
+#ifndef __cplusplus
+#ifndef __STDC__
+#define const
+#endif
+#endif
+
+
+
+#define	YYFINAL		79
+#define	YYFLAG		-32768
+#define	YYNTBASE	36
+
+#define YYTRANSLATE(x) ((unsigned)(x) <= 277 ? yytranslate[x] : 40)
+
+static const char yytranslate[] = {     0,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,    32,
+     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,    34,
+    35,    28,    27,    13,    26,     2,    29,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,    33,     2,
+    12,     2,    17,     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,    31,     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,
+     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,     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,     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,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+     2,     2,     2,     2,     2,     1,     3,     4,     5,     6,
+     7,     8,     9,    10,    11,    14,    15,    16,    18,    19,
+    20,    21,    22,    23,    24,    25,    30
+};
+
+#if YYDEBUG != 0
+static const short yyprhs[] = {     0,
+     0,     1,     4,     7,    10,    13,    16,    19,    22,    24,
+    28,    32,    36,    38,    40,    42,    44,    46,    48,    52,
+    57,    61,    65,    69,    73,    77,    81,    85,    89,    93,
+    97,   101,   105,   109,   113,   116,   120,   124,   128,   134,
+   140,   146
+};
+
+static const short yyrhs[] = {    -1,
+    36,    37,     0,    32,    33,     0,    39,    32,     0,    39,
+    33,     0,    38,    32,     0,    38,    33,     0,     1,    32,
+     0,     5,     0,    38,    27,    39,     0,    39,    27,    38,
+     0,    38,    27,    38,     0,     3,     0,    11,     0,     8,
+     0,     6,     0,     7,     0,     9,     0,     9,    12,    39,
+     0,    10,    34,    39,    35,     0,    39,    18,    39,     0,
+    39,    19,    39,     0,    39,    20,    39,     0,    39,    21,
+    39,     0,    39,    22,    39,     0,    39,    23,    39,     0,
+    39,    24,    39,     0,    39,    25,    39,     0,    39,    27,
+    39,     0,    39,    26,    39,     0,    39,    28,    39,     0,
+    39,    29,    39,     0,    39,    13,    39,     0,     9,    15,
+     9,     0,    26,    39,     0,    39,    31,    39,     0,    39,
+    14,    39,     0,    34,    39,    35,     0,    39,    17,    39,
+    16,    39,     0,    39,    17,     5,    16,     5,     0,    39,
+    17,     5,    16,    39,     0,    39,    17,    39,    16,     5,
+     0
+};
+
+#endif
+
+#if YYDEBUG != 0
+static const short yyrline[] = { 0,
+   102,   103,   106,   107,   108,   109,   110,   111,   114,   116,
+   117,   118,   121,   122,   123,   124,   125,   126,   127,   129,
+   131,   132,   133,   134,   135,   136,   137,   138,   139,   140,
+   141,   142,   144,   145,   146,   147,   148,   149,   150,   151,
+   152,   153
+};
+#endif
+
+
+#if YYDEBUG != 0 || defined (YYERROR_VERBOSE)
+
+static const char * const yytname[] = {   "$","error","$undefined.","NUM","ARR",
+"STR","PI","E","CLVAL","VAR","FNCT","TXT","'='","','","CLAUSE","COLR","COLC",
+"'?'","AND","OR","EQ","NE","GT","GE","LT","LE","'-'","'+'","'*'","'/'","NEG",
+"'^'","'\\n'","';'","'('","')'","input","line","str_exp","exp", NULL
+};
+#endif
+
+static const short yyr1[] = {     0,
+    36,    36,    37,    37,    37,    37,    37,    37,    38,    38,
+    38,    38,    39,    39,    39,    39,    39,    39,    39,    39,
+    39,    39,    39,    39,    39,    39,    39,    39,    39,    39,
+    39,    39,    39,    39,    39,    39,    39,    39,    39,    39,
+    39,    39
+};
+
+static const short yyr2[] = {     0,
+     0,     2,     2,     2,     2,     2,     2,     2,     1,     3,
+     3,     3,     1,     1,     1,     1,     1,     1,     3,     4,
+     3,     3,     3,     3,     3,     3,     3,     3,     3,     3,
+     3,     3,     3,     3,     2,     3,     3,     3,     5,     5,
+     5,     5
+};
+
+static const short yydefact[] = {     1,
+     0,     0,    13,     9,    16,    17,    15,    18,     0,    14,
+     0,     0,     0,     2,     0,     0,     8,     0,     0,     0,
+    35,     3,     0,     0,     6,     7,     0,     0,     0,     0,
+     0,     0,     0,     0,     0,     0,     0,     0,     0,     0,
+     0,     0,     4,     5,    19,    34,     0,     0,    38,    12,
+    10,    33,    37,     0,     0,    21,    22,    23,    24,    25,
+    26,    27,    28,    30,    11,    29,    31,    32,    36,    20,
+    29,     0,     0,    40,    41,    42,    39,     0,     0
+};
+
+static const short yydefgoto[] = {     1,
+    14,    15,    16
+};
+
+static const short yypact[] = {-32768,
+    33,   -31,-32768,-32768,-32768,-32768,-32768,     0,   -26,-32768,
+   115,   -19,   115,-32768,   -22,   171,-32768,   115,    23,   115,
+     4,-32768,   133,    42,-32768,-32768,   115,   115,    72,   115,
+   115,   115,   115,   115,   115,   115,   115,   115,    42,   115,
+   115,   115,-32768,-32768,   211,-32768,   152,   115,-32768,-32768,
+   230,   246,   261,    47,   192,   109,   109,    29,    29,    29,
+    29,    29,    29,   -25,-32768,   -25,     4,     4,     4,-32768,
+   -25,    81,    94,-32768,   261,-32768,   261,    46,-32768
+};
+
+static const short yypgoto[] = {-32768,
+-32768,    30,   -11
+};
+
+
+#define	YYLAST		292
+
+
+static const short yytable[] = {    21,
+    17,    23,    40,    41,    24,    42,    45,    20,    47,    25,
+    26,    18,    51,    22,    19,    52,    53,    55,    56,    57,
+    58,    59,    60,    61,    62,    63,    64,    66,    67,    68,
+    69,    46,    78,     2,    42,     3,    71,     4,     5,     6,
+     7,     8,     9,    10,     3,    79,     4,     5,     6,     7,
+     8,     9,    10,    50,    38,    48,    40,    41,    11,    42,
+    75,    77,    72,     0,    12,     0,    13,    11,    65,     0,
+     0,     0,     0,     0,     3,    13,    54,     5,     6,     7,
+     8,     9,    10,     3,     0,    74,     5,     6,     7,     8,
+     9,    10,     0,     0,     0,     0,     3,    11,    76,     5,
+     6,     7,     8,     9,    10,    13,    11,     0,     0,     0,
+     0,     0,     0,     0,    13,     0,     0,     3,     0,    11,
+     5,     6,     7,     8,     9,    10,     0,    13,    32,    33,
+    34,    35,    36,    37,    38,    48,    40,    41,     0,    42,
+    11,     0,     0,     0,     0,    27,    28,     0,    13,    29,
+    30,    31,    32,    33,    34,    35,    36,    37,    38,    48,
+    40,    41,     0,    42,    27,    28,     0,    49,    29,    30,
+    31,    32,    33,    34,    35,    36,    37,    38,    48,    40,
+    41,     0,    42,    27,    28,     0,    70,    29,    30,    31,
+    32,    33,    34,    35,    36,    37,    38,    39,    40,    41,
+     0,    42,    43,    44,    27,    28,     0,    73,    29,    30,
+    31,    32,    33,    34,    35,    36,    37,    38,    48,    40,
+    41,     0,    42,    27,    28,     0,     0,    29,    30,    31,
+    32,    33,    34,    35,    36,    37,    38,    48,    40,    41,
+     0,    42,    27,    28,     0,     0,    29,    30,    31,    32,
+    33,    34,    35,    36,    37,    38,     0,    40,    41,    28,
+    42,     0,    29,    30,    31,    32,    33,    34,    35,    36,
+    37,    38,    48,    40,    41,     0,    42,    29,    30,    31,
+    32,    33,    34,    35,    36,    37,    38,    48,    40,    41,
+     0,    42
+};
+
+static const short yycheck[] = {    11,
+    32,    13,    28,    29,    27,    31,    18,    34,    20,    32,
+    33,    12,    24,    33,    15,    27,    28,    29,    30,    31,
+    32,    33,    34,    35,    36,    37,    38,    39,    40,    41,
+    42,     9,     0,     1,    31,     3,    48,     5,     6,     7,
+     8,     9,    10,    11,     3,     0,     5,     6,     7,     8,
+     9,    10,    11,    24,    26,    27,    28,    29,    26,    31,
+    72,    73,    16,    -1,    32,    -1,    34,    26,    39,    -1,
+    -1,    -1,    -1,    -1,     3,    34,     5,     6,     7,     8,
+     9,    10,    11,     3,    -1,     5,     6,     7,     8,     9,
+    10,    11,    -1,    -1,    -1,    -1,     3,    26,     5,     6,
+     7,     8,     9,    10,    11,    34,    26,    -1,    -1,    -1,
+    -1,    -1,    -1,    -1,    34,    -1,    -1,     3,    -1,    26,
+     6,     7,     8,     9,    10,    11,    -1,    34,    20,    21,
+    22,    23,    24,    25,    26,    27,    28,    29,    -1,    31,
+    26,    -1,    -1,    -1,    -1,    13,    14,    -1,    34,    17,
+    18,    19,    20,    21,    22,    23,    24,    25,    26,    27,
+    28,    29,    -1,    31,    13,    14,    -1,    35,    17,    18,
+    19,    20,    21,    22,    23,    24,    25,    26,    27,    28,
+    29,    -1,    31,    13,    14,    -1,    35,    17,    18,    19,
+    20,    21,    22,    23,    24,    25,    26,    27,    28,    29,
+    -1,    31,    32,    33,    13,    14,    -1,    16,    17,    18,
+    19,    20,    21,    22,    23,    24,    25,    26,    27,    28,
+    29,    -1,    31,    13,    14,    -1,    -1,    17,    18,    19,
+    20,    21,    22,    23,    24,    25,    26,    27,    28,    29,
+    -1,    31,    13,    14,    -1,    -1,    17,    18,    19,    20,
+    21,    22,    23,    24,    25,    26,    -1,    28,    29,    14,
+    31,    -1,    17,    18,    19,    20,    21,    22,    23,    24,
+    25,    26,    27,    28,    29,    -1,    31,    17,    18,    19,
+    20,    21,    22,    23,    24,    25,    26,    27,    28,    29,
+    -1,    31
+};
+/* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */
+
+/* This file comes from bison-1.28.  */
+
+/* Skeleton output parser for bison,
+   Copyright (C) 1984, 1989, 1990 Free Software Foundation, Inc.
+
+   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, 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., 59 Temple Place - Suite 330,
+   Boston, MA 02111-1307, USA.  */
+
+/* As a special exception, when this file is copied by Bison into a
+   Bison output file, you may use that output file without restriction.
+   This special exception was added by the Free Software Foundation
+   in version 1.24 of Bison.  */
+
+/* This is the parser code that is written into each bison parser
+  when the %semantic_parser declaration is not specified in the grammar.
+  It was written by Richard Stallman by simplifying the hairy parser
+  used when %semantic_parser is specified.  */
+
+#ifndef YYSTACK_USE_ALLOCA
+#ifdef alloca
+#define YYSTACK_USE_ALLOCA
+#else /* alloca not defined */
+#ifdef __GNUC__
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#else /* not GNU C.  */
+#if (!defined (__STDC__) && defined (sparc)) || defined (__sparc__) || defined (__sparc) || defined (__sgi) || (defined (__sun) && defined (__i386))
+#define YYSTACK_USE_ALLOCA
+#include <alloca.h>
+#else /* not sparc */
+/* We think this test detects Watcom and Microsoft C.  */
+/* This used to test MSDOS, but that is a bad idea
+   since that symbol is in the user namespace.  */
+#if (defined (_MSDOS) || defined (_MSDOS_)) && !defined (__TURBOC__)
+#if 0 /* No need for malloc.h, which pollutes the namespace;
+	 instead, just don't use alloca.  */
+#include <malloc.h>
+#endif
+#else /* not MSDOS, or __TURBOC__ */
+#if defined(_AIX)
+/* I don't know what this was needed for, but it pollutes the namespace.
+   So I turned it off.   rms, 2 May 1997.  */
+/* #include <malloc.h>  */
+ #pragma alloca
+#define YYSTACK_USE_ALLOCA
+#else /* not MSDOS, or __TURBOC__, or _AIX */
+#if 0
+#ifdef __hpux /* haible at ilog.fr says this works for HPUX 9.05 and up,
+		 and on HPUX 10.  Eventually we can turn this on.  */
+#define YYSTACK_USE_ALLOCA
+#define alloca __builtin_alloca
+#endif /* __hpux */
+#endif
+#endif /* not _AIX */
+#endif /* not MSDOS, or __TURBOC__ */
+#endif /* not sparc */
+#endif /* not GNU C */
+#endif /* alloca not defined */
+#endif /* YYSTACK_USE_ALLOCA not defined */
+
+#ifdef YYSTACK_USE_ALLOCA
+#define YYSTACK_ALLOC alloca
+#else
+#define YYSTACK_ALLOC malloc
+#endif
+
+/* Note: there must be only one dollar sign in this file.
+   It is replaced by the list of actions, each action
+   as one case of the switch.  */
+
+#define yyerrok		(yyerrstatus = 0)
+#define yyclearin	(yychar = YYEMPTY)
+#define YYEMPTY		-2
+#define YYEOF		0
+#define YYACCEPT	goto yyacceptlab
+#define YYABORT 	goto yyabortlab
+#define YYERROR		goto yyerrlab1
+/* Like YYERROR except do call yyerror.
+   This remains here temporarily to ease the
+   transition to the new meaning of YYERROR, for GCC.
+   Once GCC version 2 has supplanted version 1, this can go.  */
+#define YYFAIL		goto yyerrlab
+#define YYRECOVERING()  (!!yyerrstatus)
+#define YYBACKUP(token, value) \
+do								\
+  if (yychar == YYEMPTY && yylen == 1)				\
+    { yychar = (token), yylval = (value);			\
+      yychar1 = YYTRANSLATE (yychar);				\
+      YYPOPSTACK;						\
+      goto yybackup;						\
+    }								\
+  else								\
+    { yyerror ("syntax error: cannot back up"); YYERROR; }	\
+while (0)
+
+#define YYTERROR	1
+#define YYERRCODE	256
+
+#ifndef YYPURE
+#define YYLEX		yylex()
+#endif
+
+#ifdef YYPURE
+#ifdef YYLSP_NEEDED
+#ifdef YYLEX_PARAM
+#define YYLEX		yylex(&yylval, &yylloc, YYLEX_PARAM)
+#else
+#define YYLEX		yylex(&yylval, &yylloc)
+#endif
+#else /* not YYLSP_NEEDED */
+#ifdef YYLEX_PARAM
+#define YYLEX		yylex(&yylval, YYLEX_PARAM)
+#else
+#define YYLEX		yylex(&yylval)
+#endif
+#endif /* not YYLSP_NEEDED */
+#endif
+
+/* If nonreentrant, generate the variables here */
+
+#ifndef YYPURE
+
+int	yychar;			/*  the lookahead symbol		*/
+YYSTYPE	yylval;			/*  the semantic value of the		*/
+				/*  lookahead symbol			*/
+
+#ifdef YYLSP_NEEDED
+YYLTYPE yylloc;			/*  location data for the lookahead	*/
+				/*  symbol				*/
+#endif
+
+int yynerrs;			/*  number of parse errors so far       */
+#endif  /* not YYPURE */
+
+#if YYDEBUG != 0
+int yydebug;			/*  nonzero means print parse trace	*/
+/* Since this is uninitialized, it does not stop multiple parsers
+   from coexisting.  */
+#endif
+
+/*  YYINITDEPTH indicates the initial size of the parser's stacks	*/
+
+#ifndef	YYINITDEPTH
+#define YYINITDEPTH 200
+#endif
+
+/*  YYMAXDEPTH is the maximum size the stacks can grow to
+    (effective only if the built-in stack extension method is used).  */
+
+#if YYMAXDEPTH == 0
+#undef YYMAXDEPTH
+#endif
+
+#ifndef YYMAXDEPTH
+#define YYMAXDEPTH 10000
+#endif
+

+/* Define __yy_memcpy.  Note that the size argument
+   should be passed with type unsigned int, because that is what the non-GCC
+   definitions require.  With GCC, __builtin_memcpy takes an arg
+   of type size_t, but it can handle unsigned int.  */
+
+#if __GNUC__ > 1		/* GNU C and GNU C++ define this.  */
+#define __yy_memcpy(TO,FROM,COUNT)	__builtin_memcpy(TO,FROM,COUNT)
+#else				/* not GNU C or C++ */
+#ifndef __cplusplus
+
+/* This is the most reliable way to avoid incompatibilities
+   in available built-in functions on various systems.  */
+static void
+__yy_memcpy (to, from, count)
+     char *to;
+     char *from;
+     unsigned int count;
+{
+  register char *f = from;
+  register char *t = to;
+  register int i = count;
+
+  while (i-- > 0)
+    *t++ = *f++;
+}
+
+#else /* __cplusplus */
+
+/* This is the most reliable way to avoid incompatibilities
+   in available built-in functions on various systems.  */
+static void
+__yy_memcpy (char *to, char *from, unsigned int count)
+{
+  register char *t = to;
+  register char *f = from;
+  register int i = count;
+
+  while (i-- > 0)
+    *t++ = *f++;
+}
+
+#endif
+#endif
+

+
+
+/* The user can define YYPARSE_PARAM as the name of an argument to be passed
+   into yyparse.  The argument should have type void *.
+   It should actually point to an object.
+   Grammar actions can access the variable by casting it
+   to the proper pointer type.  */
+
+#ifdef YYPARSE_PARAM
+#ifdef __cplusplus
+#define YYPARSE_PARAM_ARG void *YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL
+#else /* not __cplusplus */
+#define YYPARSE_PARAM_ARG YYPARSE_PARAM
+#define YYPARSE_PARAM_DECL void *YYPARSE_PARAM;
+#endif /* not __cplusplus */
+#else /* not YYPARSE_PARAM */
+#define YYPARSE_PARAM_ARG
+#define YYPARSE_PARAM_DECL
+#endif /* not YYPARSE_PARAM */
+
+/* Prevent warning if -Wstrict-prototypes.  */
+#ifdef __GNUC__
+#ifdef YYPARSE_PARAM
+int yyparse (void *);
+#else
+int yyparse (void);
+#endif
+#endif
+
+int
+yyparse(YYPARSE_PARAM_ARG)
+     YYPARSE_PARAM_DECL
+{
+  register int yystate;
+  register int yyn;
+  register short *yyssp;
+  register YYSTYPE *yyvsp;
+  int yyerrstatus;	/*  number of tokens to shift before error messages enabled */
+  int yychar1 = 0;		/*  lookahead token as an internal (translated) token number */
+
+  short	yyssa[YYINITDEPTH];	/*  the state stack			*/
+  YYSTYPE yyvsa[YYINITDEPTH];	/*  the semantic value stack		*/
+
+  short *yyss = yyssa;		/*  refer to the stacks thru separate pointers */
+  YYSTYPE *yyvs = yyvsa;	/*  to allow yyoverflow to reallocate them elsewhere */
+
+#ifdef YYLSP_NEEDED
+  YYLTYPE yylsa[YYINITDEPTH];	/*  the location stack			*/
+  YYLTYPE *yyls = yylsa;
+  YYLTYPE *yylsp;
+
+#define YYPOPSTACK   (yyvsp--, yyssp--, yylsp--)
+#else
+#define YYPOPSTACK   (yyvsp--, yyssp--)
+#endif
+
+  int yystacksize = YYINITDEPTH;
+  int yyfree_stacks = 0;
+
+#ifdef YYPURE
+  int yychar;
+  YYSTYPE yylval;
+  int yynerrs;
+#ifdef YYLSP_NEEDED
+  YYLTYPE yylloc;
+#endif
+#endif
+
+  YYSTYPE yyval;		/*  the variable used to return		*/
+				/*  semantic values from the action	*/
+				/*  routines				*/
+
+  int yylen;
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Starting parse\n");
+#endif
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY;		/* Cause a token to be read.  */
+
+  /* Initialize stack pointers.
+     Waste one element of value and location stack
+     so that they stay on the same level as the state stack.
+     The wasted elements are never initialized.  */
+
+  yyssp = yyss - 1;
+  yyvsp = yyvs;
+#ifdef YYLSP_NEEDED
+  yylsp = yyls;
+#endif
+
+/* Push a new state, which is found in  yystate  .  */
+/* In all cases, when you get here, the value and location stacks
+   have just been pushed. so pushing a state here evens the stacks.  */
+yynewstate:
+
+  *++yyssp = yystate;
+
+  if (yyssp >= yyss + yystacksize - 1)
+    {
+      /* Give user a chance to reallocate the stack */
+      /* Use copies of these so that the &'s don't force the real ones into memory. */
+      YYSTYPE *yyvs1 = yyvs;
+      short *yyss1 = yyss;
+#ifdef YYLSP_NEEDED
+      YYLTYPE *yyls1 = yyls;
+#endif
+
+      /* Get the current used size of the three stacks, in elements.  */
+      int size = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      /* Each stack pointer address is followed by the size of
+	 the data in use in that stack, in bytes.  */
+#ifdef YYLSP_NEEDED
+      /* This used to be a conditional around just the two extra args,
+	 but that might be undefined if yyoverflow is a macro.  */
+      yyoverflow("parser stack overflow",
+		 &yyss1, size * sizeof (*yyssp),
+		 &yyvs1, size * sizeof (*yyvsp),
+		 &yyls1, size * sizeof (*yylsp),
+		 &yystacksize);
+#else
+      yyoverflow("parser stack overflow",
+		 &yyss1, size * sizeof (*yyssp),
+		 &yyvs1, size * sizeof (*yyvsp),
+		 &yystacksize);
+#endif
+
+      yyss = yyss1; yyvs = yyvs1;
+#ifdef YYLSP_NEEDED
+      yyls = yyls1;
+#endif
+#else /* no yyoverflow */
+      /* Extend the stack our own way.  */
+      if (yystacksize >= YYMAXDEPTH)
+	{
+	  yyerror("parser stack overflow");
+	  if (yyfree_stacks)
+	    {
+	      free (yyss);
+	      free (yyvs);
+#ifdef YYLSP_NEEDED
+	      free (yyls);
+#endif
+	    }
+	  return 2;
+	}
+      yystacksize *= 2;
+      if (yystacksize > YYMAXDEPTH)
+	yystacksize = YYMAXDEPTH;
+#ifndef YYSTACK_USE_ALLOCA
+      yyfree_stacks = 1;
+#endif
+      yyss = (short *) YYSTACK_ALLOC (yystacksize * sizeof (*yyssp));
+      __yy_memcpy ((char *)yyss, (char *)yyss1,
+		   size * (unsigned int) sizeof (*yyssp));
+      yyvs = (YYSTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yyvsp));
+      __yy_memcpy ((char *)yyvs, (char *)yyvs1,
+		   size * (unsigned int) sizeof (*yyvsp));
+#ifdef YYLSP_NEEDED
+      yyls = (YYLTYPE *) YYSTACK_ALLOC (yystacksize * sizeof (*yylsp));
+      __yy_memcpy ((char *)yyls, (char *)yyls1,
+		   size * (unsigned int) sizeof (*yylsp));
+#endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + size - 1;
+      yyvsp = yyvs + size - 1;
+#ifdef YYLSP_NEEDED
+      yylsp = yyls + size - 1;
+#endif
+
+#if YYDEBUG != 0
+      if (yydebug)
+	fprintf(stderr, "Stack size increased to %d\n", yystacksize);
+#endif
+
+      if (yyssp >= yyss + yystacksize - 1)
+	YYABORT;
+    }
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Entering state %d\n", yystate);
+#endif
+
+  goto yybackup;
+ yybackup:
+
+/* Do appropriate processing given the current state.  */
+/* Read a lookahead token if we need one and don't already have one.  */
+/* yyresume: */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* yychar is either YYEMPTY or YYEOF
+     or a valid token in external form.  */
+
+  if (yychar == YYEMPTY)
+    {
+#if YYDEBUG != 0
+      if (yydebug)
+	fprintf(stderr, "Reading a token: ");
+#endif
+      yychar = YYLEX;
+    }
+
+  /* Convert token to internal form (in yychar1) for indexing tables with */
+
+  if (yychar <= 0)		/* This means end of input. */
+    {
+      yychar1 = 0;
+      yychar = YYEOF;		/* Don't call YYLEX any more */
+
+#if YYDEBUG != 0
+      if (yydebug)
+	fprintf(stderr, "Now at end of input.\n");
+#endif
+    }
+  else
+    {
+      yychar1 = YYTRANSLATE(yychar);
+
+#if YYDEBUG != 0
+      if (yydebug)
+	{
+	  fprintf (stderr, "Next token is %d (%s", yychar, yytname[yychar1]);
+	  /* Give the individual parser a way to print the precise meaning
+	     of a token, for further debugging info.  */
+#ifdef YYPRINT
+	  YYPRINT (stderr, yychar, yylval);
+#endif
+	  fprintf (stderr, ")\n");
+	}
+#endif
+    }
+
+  yyn += yychar1;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != yychar1)
+    goto yydefault;
+
+  yyn = yytable[yyn];
+
+  /* yyn is what to do for this token type in this state.
+     Negative => reduce, -yyn is rule number.
+     Positive => shift, yyn is new state.
+       New state is final state => don't bother to shift,
+       just return success.
+     0, or most negative number => error.  */
+
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+	goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrlab;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+  /* Shift the lookahead token.  */
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Shifting token %d (%s), ", yychar, yytname[yychar1]);
+#endif
+
+  /* Discard the token being shifted unless it is eof.  */
+  if (yychar != YYEOF)
+    yychar = YYEMPTY;
+
+  *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  /* count tokens shifted since error; after three, turn off error status.  */
+  if (yyerrstatus) yyerrstatus--;
+
+  yystate = yyn;
+  goto yynewstate;
+
+/* Do the default action for the current state.  */
+yydefault:
+
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+
+/* Do a reduction.  yyn is the number of a rule to reduce with.  */
+yyreduce:
+  yylen = yyr2[yyn];
+  if (yylen > 0)
+    yyval = yyvsp[1-yylen]; /* implement default value of the action */
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      int i;
+
+      fprintf (stderr, "Reducing via rule %d (line %d), ",
+	       yyn, yyrline[yyn]);
+
+      /* Print the symbols being reduced, and their result.  */
+      for (i = yyprhs[yyn]; yyrhs[i] > 0; i++)
+	fprintf (stderr, "%s ", yytname[yyrhs[i]]);
+      fprintf (stderr, " -> %s\n", yytname[yyr1[yyn]]);
+    }
+#endif
+
+
+  switch (yyn) {
+
+case 4:
+{store_res(&yyvsp[-1]); return 0;;
+    break;}
+case 5:
+{store_res(&yyvsp[-1]); return 0;;
+    break;}
+case 6:
+{store_res(&yyvsp[-1]); return 0;;
+    break;}
+case 7:
+{store_res(&yyvsp[-1]); return 0;;
+    break;}
+case 8:
+{yyerrok;;
+    break;}
+case 9:
+{yyval.type=STR;;
+    break;}
+case 10:
+{yyval.text=add_strings(yyvsp[-2].text, string_value(&yyvsp[0])); yyval.type=STR;;
+    break;}
+case 11:
+{yyval.text=add_strings(string_value(&yyvsp[-2]), yyvsp[0].text); yyval.type=STR;;
+    break;}
+case 12:
+{yyval.text=add_strings(yyvsp[-2].text, yyvsp[0].text); yyval.type=STR;;
+    break;}
+case 13:
+{yyval.val = yyvsp[0].val; yyval.type = NUM;;
+    break;}
+case 14:
+{yyval.val = 0.0;;
+    break;}
+case 15:
+{yyval.val = syntax_level ? syntax_level->clval : 0.0; ;
+    break;}
+case 16:
+{yyval.val = 3.14159265358979; yyval.type = NUM;;
+    break;}
+case 17:
+{yyval.val = 2.71828182845905; yyval.type = NUM;;
+    break;}
+case 18:
+{yyval.val = yyvsp[0].tptr->value.var; yyval.type=VAR;
+    break;}
+case 19:
+{yyval.val = yyvsp[-2].tptr->value.var = yyvsp[0].val;
+					if(yyvsp[-2].tptr->row >=0 && yyvsp[-2].tptr->col >= 0 && curr_data) curr_data->SetValue(yyvsp[-2].tptr->row, yyvsp[-2].tptr->col, yyvsp[0].val);;
+    break;}
+case 20:
+{yyval.val = (yyvsp[-3].tptr->arg_type == ARR) ? ((*yyvsp[-3].tptr->value.fnctptr)(proc_clause(&yyvsp[-1]))) : 
+					(*(yyvsp[-3].tptr->value.fnctptr))(yyvsp[-1].val);;
+    break;}
+case 21:
+{yyval.val = ((yyvsp[-2].val != 0) && (yyvsp[0].val != 0))? 1 : 0;;
+    break;}
+case 22:
+{yyval.val = ((yyvsp[-2].val != 0) || (yyvsp[0].val != 0))? 1 : 0;;
+    break;}
+case 23:
+{yyval.val = (yyvsp[-2].val == yyvsp[0].val) ? 1 : 0;;
+    break;}
+case 24:
+{yyval.val = (yyvsp[-2].val != yyvsp[0].val) ? 1 : 0;;
+    break;}
+case 25:
+{yyval.val = (yyvsp[-2].val > yyvsp[0].val) ? 1 : 0;;
+    break;}
+case 26:
+{yyval.val = (yyvsp[-2].val >= yyvsp[0].val) ? 1 : 0;;
+    break;}
+case 27:
+{yyval.val = (yyvsp[-2].val < yyvsp[0].val) ? 1 : 0;;
+    break;}
+case 28:
+{yyval.val = (yyvsp[-2].val <= yyvsp[0].val) ? 1 : 0;;
+    break;}
+case 29:
+{yyval.val = yyvsp[-2].val + yyvsp[0].val;;
+    break;}
+case 30:
+{yyval.val = yyvsp[-2].val - yyvsp[0].val;;
+    break;}
+case 31:
+{yyval.val = yyvsp[-2].val * yyvsp[0].val;;
+    break;}
+case 32:
+{if(yyvsp[0].val != 0.0) yyval.val = yyvsp[-2].val / yyvsp[0].val;
+					else yyval.val = (getsym(HashValue((unsigned char*)"zdiv")))->value.var; ;
+    break;}
+case 33:
+{push(&yyval, &yyvsp[0]);;
+    break;}
+case 34:
+{get_range(&yyval, yyvsp[-2].tptr->name, yyvsp[0].tptr->name);;
+    break;}
+case 35:
+{yyval.val = -yyvsp[0].val;;
+    break;}
+case 36:
+{yyval.val = pow(yyvsp[-2].val, yyvsp[0].val);;
+    break;}
+case 37:
+{yyval.val = yyvsp[-2].val; exec_clause(&yyval);;
+    break;}
+case 38:
+{memcpy(&yyval, &yyvsp[-1], sizeof(YYSTYPE));
+    break;}
+case 39:
+{memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
+    break;}
+case 40:
+{memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
+    break;}
+case 41:
+{memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
+    break;}
+case 42:
+{memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
+    break;}
+}
+   /* the action file gets copied in in place of this dollarsign */
+
+

+  yyvsp -= yylen;
+  yyssp -= yylen;
+#ifdef YYLSP_NEEDED
+  yylsp -= yylen;
+#endif
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      short *ssp1 = yyss - 1;
+      fprintf (stderr, "state stack now");
+      while (ssp1 != yyssp)
+	fprintf (stderr, " %d", *++ssp1);
+      fprintf (stderr, "\n");
+    }
+#endif
+
+  *++yyvsp = yyval;
+
+#ifdef YYLSP_NEEDED
+  yylsp++;
+  if (yylen == 0)
+    {
+      yylsp->first_line = yylloc.first_line;
+      yylsp->first_column = yylloc.first_column;
+      yylsp->last_line = (yylsp-1)->last_line;
+      yylsp->last_column = (yylsp-1)->last_column;
+      yylsp->text = 0;
+    }
+  else
+    {
+      yylsp->last_line = (yylsp+yylen-1)->last_line;
+      yylsp->last_column = (yylsp+yylen-1)->last_column;
+    }
+#endif
+
+  /* Now "shift" the result of the reduction.
+     Determine what state that goes to,
+     based on the state we popped back to
+     and the rule number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTBASE] + *yyssp;
+  if (yystate >= 0 && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTBASE];
+
+  goto yynewstate;
+
+yyerrlab:   /* here on detecting error */
+
+  if (! yyerrstatus)
+    /* If not already recovering from an error, report this error.  */
+    {
+      ++yynerrs;
+
+#ifdef YYERROR_VERBOSE
+      yyn = yypact[yystate];
+
+      if (yyn > YYFLAG && yyn < YYLAST)
+	{
+	  int size = 0;
+	  char *msg;
+	  int x, count;
+
+	  count = 0;
+	  /* Start X at -yyn if nec to avoid negative indexes in yycheck.  */
+	  for (x = (yyn < 0 ? -yyn : 0);
+	       x < (sizeof(yytname) / sizeof(char *)); x++)
+	    if (yycheck[x + yyn] == x)
+	      size += strlen(yytname[x]) + 15, count++;
+	  msg = (char *) malloc(size + 15);
+	  if (msg != 0)
+	    {
+	      strcpy(msg, "parse error");
+
+	      if (count < 5)
+		{
+		  count = 0;
+		  for (x = (yyn < 0 ? -yyn : 0);
+		       x < (sizeof(yytname) / sizeof(char *)); x++)
+		    if (yycheck[x + yyn] == x)
+		      {
+			strcat(msg, count == 0 ? ", expecting `" : " or `");
+			strcat(msg, yytname[x]);
+			strcat(msg, "'");
+			count++;
+		      }
+		}
+	      yyerror(msg);
+	      free(msg);
+	    }
+	  else
+	    yyerror ("parse error; also virtual memory exceeded");
+	}
+      else
+#endif /* YYERROR_VERBOSE */
+	yyerror("parse error");
+    }
+
+  goto yyerrlab1;
+yyerrlab1:   /* here on error raised explicitly by an action */
+
+  if (yyerrstatus == 3)
+    {
+      /* if just tried and failed to reuse lookahead token after an error, discard it.  */
+
+      /* return failure if at end of input */
+      if (yychar == YYEOF)
+	YYABORT;
+
+#if YYDEBUG != 0
+      if (yydebug)
+	fprintf(stderr, "Discarding token %d (%s).\n", yychar, yytname[yychar1]);
+#endif
+
+      yychar = YYEMPTY;
+    }
+
+  /* Else will try to reuse lookahead token
+     after shifting the error token.  */
+
+  yyerrstatus = 3;		/* Each real token shifted decrements this */
+
+  goto yyerrhandle;
+
+yyerrdefault:  /* current state does not do anything special for the error token. */
+
+#if 0
+  /* This is wrong; only states that explicitly want error tokens
+     should shift them.  */
+  yyn = yydefact[yystate];  /* If its default is to accept any token, ok.  Otherwise pop it.*/
+  if (yyn) goto yydefault;
+#endif
+
+yyerrpop:   /* pop the current state because it cannot handle the error token */
+
+  if (yyssp == yyss) YYABORT;
+  yyvsp--;
+  yystate = *--yyssp;
+#ifdef YYLSP_NEEDED
+  yylsp--;
+#endif
+
+#if YYDEBUG != 0
+  if (yydebug)
+    {
+      short *ssp1 = yyss - 1;
+      fprintf (stderr, "Error: state stack now");
+      while (ssp1 != yyssp)
+	fprintf (stderr, " %d", *++ssp1);
+      fprintf (stderr, "\n");
+    }
+#endif
+
+yyerrhandle:
+
+  yyn = yypact[yystate];
+  if (yyn == YYFLAG)
+    goto yyerrdefault;
+
+  yyn += YYTERROR;
+  if (yyn < 0 || yyn > YYLAST || yycheck[yyn] != YYTERROR)
+    goto yyerrdefault;
+
+  yyn = yytable[yyn];
+  if (yyn < 0)
+    {
+      if (yyn == YYFLAG)
+	goto yyerrpop;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+  else if (yyn == 0)
+    goto yyerrpop;
+
+  if (yyn == YYFINAL)
+    YYACCEPT;
+
+#if YYDEBUG != 0
+  if (yydebug)
+    fprintf(stderr, "Shifting error token, ");
+#endif
+
+  *++yyvsp = yylval;
+#ifdef YYLSP_NEEDED
+  *++yylsp = yylloc;
+#endif
+
+  yystate = yyn;
+  goto yynewstate;
+
+ yyacceptlab:
+  /* YYACCEPT comes here.  */
+  if (yyfree_stacks)
+    {
+      free (yyss);
+      free (yyvs);
+#ifdef YYLSP_NEEDED
+      free (yyls);
+#endif
+    }
+  return 0;
+
+ yyabortlab:
+  /* YYABORT comes here.  */
+  if (yyfree_stacks)
+    {
+      free (yyss);
+      free (yyvs);
+#ifdef YYLSP_NEEDED
+      free (yyls);
+#endif
+    }
+  return 1;
+}
+
+
+static char *last_error = 0L;
+static void yyerror(char *s)
+{  
+	//Called by yyparse on error
+	if(curr_data) curr_data->Command(CMD_ERROR, last_error = s, 0L);
+	else printf("%s\n", s);
+}
+
+static void store_res(YYSTYPE *res)
+{
+	if(res->type == STR) {
+		line_result = 0.0;
+		line_res.type = ET_TEXT;
+		line_res. value = 0.0;
+		if(res->text) strcpy(res_txt, res->text);
+		}
+	else if(res->type == ARR || (res->a_data)) {
+		line_result = 0.0;
+		line_res.type = ET_TEXT;
+		line_res. value = 0.0;
+		strcpy(res_txt, "#ARRAY");
+		}
+	else if(res->tptr && res->tptr->type == TXT) {
+		line_result = 0.0;
+		line_res.type = ET_TEXT;
+		line_res.value = 0.0;
+		if(res->tptr->text) strcpy(res_txt, res->tptr->text);
+		}
+	else {
+		line_result = res->val;
+		line_res.type = ET_VALUE;
+		line_res.value = res->val;
+		}
+}
+
+static char *add_strings(char *st1, char *st2)
+{
+	char *newstr, *ret;
+
+	if(st1 && st2) {
+		if(newstr = (char*)malloc(strlen(st1) +strlen(st2) +4)) {
+			sprintf(newstr, "%s%s", st1, st2);
+			ret = PushString(newstr);
+			free(newstr);
+			return ret;
+			}
+		else return 0L;
+		}
+	if(st1) return st1;
+	if(st2) return st2;
+	return 0L;
+}
+
+static char *string_value(YYSTYPE *exp)
+{
+	char *st1, tmp[50];
+
+	if(exp->type == STR){
+		st1 = exp->text;
+		}
+	else if(exp->tptr && exp->tptr->type == TXT) {
+		st1 = exp->tptr->text;
+		}
+	else {
+		sprintf(tmp,"%g", exp->val);
+		st1 = tmp;
+		}
+	return PushString(st1);
+}
+
+// store syntactical information
+static void push_syntax()
+{
+	syntax_info *next;
+
+	if(!(next = (syntax_info*)calloc(1, sizeof(syntax_info)))) return;
+	if(syntax_level)memcpy(next, syntax_level, sizeof(syntax_info));
+	next->next=syntax_level;
+	syntax_level = next;
+}
+
+static void pop_syntax()
+{
+	syntax_info *si;
+
+	if(si = syntax_level) {
+		syntax_level = si->next;
+		free(si);
+		}
+}
+
+// more functions
+static double sign(double v)
+{
+	if(v > 0.0) return 1.0;
+	if(v < 0.0) return -1.0;
+	return 0.0;
+}
+
+static double factorial(double v)
+{
+	return factrl((int)v);
+}
+
+static void close_arr_func(YYSTYPE *sr)
+{
+	free(sr->a_data);
+	sr->a_data = 0L;		sr->a_count = 0;
+	if(sr->type == ARR) sr->type = NUM;
+}
+
+#undef min
+static double min(YYSTYPE *sr) 
+{
+	int i;
+
+	if(!sr) return 0.0;
+	if(sr->a_data && sr->a_data){
+		for(i = 1, sr->val = sr->a_data[0]; i < sr->a_count; i++) 
+			if(sr->a_data[i] < sr->val) sr->val = sr->a_data[i];
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+#undef max
+static double max(YYSTYPE *sr) 
+{
+	int i;
+
+	if(!sr) return 0.0;
+	if(sr->a_data){
+		for(i = 1, sr->val = sr->a_data[0]; i < sr->a_count; i++) 
+			if(sr->a_data[i] > sr->val) sr->val = sr->a_data[i];
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double count(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	if(sr->a_data){
+		sr->val = (double)sr->a_count;
+		close_arr_func(sr);
+		}
+	else sr->val = 0.0;
+	return sr->val;
+}
+
+static double sum(YYSTYPE *sr) 
+{
+	int i;
+
+	if(!sr) return 0.0;
+	if(sr->a_data){
+		for(i = 1, sr->val = sr->a_data[0]; i < sr->a_count; i++) sr->val += sr->a_data[i];
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double calc_variance(double *values, int n)
+{
+	int i;
+	double ss, d, mean = d_amean(n, values);
+
+	for(i=0, ss=0.0; i < n; i++) ss += ((d=values[i]-mean)*d);
+	return (ss/(n-1));
+}
+
+static double mean(YYSTYPE *sr) 
+{
+	if(!sr) return 0.0;
+	if(sr->a_data && sr->a_count){
+		sr->val = d_amean(sr->a_count, sr->a_data );
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double gmean(YYSTYPE *sr) 
+{
+	if(!sr) return 0.0;
+	if(sr->a_data && sr->a_count){
+		sr->val = d_gmean(sr->a_count, sr->a_data );
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double hmean(YYSTYPE *sr) 
+{
+	if(!sr) return 0.0;
+	if(sr->a_data && sr->a_count){
+		sr->val = d_hmean(sr->a_count, sr->a_data );
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double quartile1(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	if(sr->a_data && sr->a_count){
+		d_quartile(sr->a_count, sr->a_data, &sr->val, 0L, 0L);
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double quartile2(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	if(sr->a_data && sr->a_count){
+		d_quartile(sr->a_count, sr->a_data, 0L, &sr->val, 0L);
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double quartile3(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	if(sr->a_data && sr->a_count){
+		d_quartile(sr->a_count, sr->a_data, 0L, 0L, &sr->val);
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double variance(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count){
+		sr->val = calc_variance(sr->a_data, sr->a_count);
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double stdev(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count){
+		sr->val = sqrt(calc_variance(sr->a_data, sr->a_count));
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double sterr(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count){
+		sr->val = sqrt(calc_variance(sr->a_data, sr->a_count))/sqrt(sr->a_count);
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double tdist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 2){
+		sr->val = t_dist(sr->a_data[0], sr->a_data[1]);
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double fdist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0;
+	if(sr->a_data && sr->a_count == 3){
+		sr->val = f_dist(sr->a_data[0], sr->a_data[1], sr->a_data[2]);
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+struct init
+{
+	unsigned int h_name;
+	double (*fnct)(double);
+	int arg_type;
+};
+
+static struct init arith_fncts[] = {
+	{HashValue((unsigned char*)"variance"), (double(*)(double))&variance, ARR},
+	{HashValue((unsigned char*)"stdev"), (double(*)(double))&stdev, ARR},
+	{HashValue((unsigned char*)"sterr"), (double(*)(double))&sterr, ARR},
+	{HashValue((unsigned char*)"min"), (double(*)(double))&min, ARR},
+	{HashValue((unsigned char*)"max"), (double(*)(double))&max, ARR},
+	{HashValue((unsigned char*)"count"), (double(*)(double))&count, ARR},
+	{HashValue((unsigned char*)"sum"), (double(*)(double))&sum, ARR},
+	{HashValue((unsigned char*)"mean"), (double(*)(double))&mean, ARR},
+	{HashValue((unsigned char*)"median"), (double(*)(double))&quartile2, ARR},
+	{HashValue((unsigned char*)"quartile1"), (double(*)(double))&quartile1, ARR},
+	{HashValue((unsigned char*)"quartile2"), (double(*)(double))&quartile2, ARR},
+	{HashValue((unsigned char*)"quartile3"), (double(*)(double))&quartile3, ARR},
+	{HashValue((unsigned char*)"gmean"), (double(*)(double))&gmean, ARR},
+	{HashValue((unsigned char*)"hmean"), (double(*)(double))&hmean, ARR},
+	{HashValue((unsigned char*)"tdist"), (double(*)(double))&tdist, ARR},
+	{HashValue((unsigned char*)"fdist"), (double(*)(double))&fdist, ARR},
+	{HashValue((unsigned char*)"sign"), sign, VAR},
+	{HashValue((unsigned char*)"gammaln"), gammln, VAR},
+	{HashValue((unsigned char*)"factorial"), factorial, VAR},
+	{HashValue((unsigned char*)"abs"), fabs, VAR},
+	{HashValue((unsigned char*)"asin"), asin, VAR},
+	{HashValue((unsigned char*)"acos"), acos, VAR},
+	{HashValue((unsigned char*)"atan"), atan, VAR},
+	{HashValue((unsigned char*)"sinh"), sinh, VAR},
+	{HashValue((unsigned char*)"cosh"), cosh, VAR},
+	{HashValue((unsigned char*)"tanh"), tanh, VAR},
+	{HashValue((unsigned char*)"sin"),  sin, VAR},
+	{HashValue((unsigned char*)"cos"),  cos, VAR},
+	{HashValue((unsigned char*)"atan"), atan, VAR},
+	{HashValue((unsigned char*)"log10"), log10, VAR},
+	{HashValue((unsigned char*)"ln"),   log, VAR},
+	{HashValue((unsigned char*)"log"),   log, VAR},
+	{HashValue((unsigned char*)"exp"),  exp, VAR},
+	{HashValue((unsigned char*)"sqrt"), sqrt, VAR},
+	{0, 0, 0}};
+
+// Store strings in a list
+static char **str_list = 0L;
+static int n_str = 0;
+
+static char *PushString(char *text)
+{
+	if(text && text[0]) {
+		if(str_list = (char**)realloc(str_list, sizeof(char*)*(n_str+1)))
+			str_list[n_str] = strdup(text);
+		return str_list[n_str++];
+		}
+	return 0L;
+}
+
+//The symbol table: a chain of `struct symrec'
+static symrec *sym_table = (symrec *) 0;
+
+// Put arithmetic functions and predifened variables in table
+static void init_table (void)
+{
+	int i;
+	symrec *ptr;
+
+	for (i = 0; arith_fncts[i].h_name; i++) {
+		ptr = putsym (arith_fncts[i].h_name, FNCT, arith_fncts[i].arg_type);
+		ptr->value.fnctptr = (double (*)(...))arith_fncts[i].fnct;
+		}
+	ptr = putsym(HashValue((unsigned char*)"zdiv"), VAR, 0);	ptr->value.var = 1.0;
+	str_list = 0L;		n_str = 0;
+	push_syntax();
+}
+
+static void clear_table()
+{
+	int i;
+	symrec *ptr, *next;
+
+	for (ptr = sym_table; ptr != (symrec *) 0;){
+		if(ptr) {
+			if(ptr->name) free(ptr->name);
+			if(ptr->text) free(ptr->text);
+			next = (symrec*)ptr->next;
+			free(ptr);
+			}
+		ptr = next;
+		}
+	sym_table = (symrec *) 0;
+	if(str_list) {
+		for(i = 0; i < n_str; i++) if(str_list[i]) free(str_list[i]);
+		free(str_list);		str_list = 0L;		n_str = 0;
+		}
+	pop_syntax();
+}
+
+static symrec *
+putsym (unsigned int h_name, int sym_type, int arg_type)
+{
+	symrec *ptr;
+
+	ptr = (symrec *) malloc (sizeof (symrec));
+	ptr->h_name = h_name;
+	ptr->type = sym_type;
+	ptr->name = ptr->text = 0L;
+	ptr->value.var = 0; /* set value to 0 even if fctn.  */
+	ptr->arg_type = arg_type;
+	ptr->col = ptr->row = -1;
+	ptr->next = (struct symrec *)sym_table;
+	sym_table = ptr;
+	return ptr;
+}
+
+static symrec *
+getsym (unsigned int h_name, char *sym_name)
+{
+	symrec *ptr;
+	int row, col;
+	AccRange *ar;
+	anyResult ares;
+
+	if(!h_name) return 0;
+	for (ptr = sym_table; ptr != (symrec *) 0; ptr = (symrec *)ptr->next)
+		if (ptr->h_name == h_name){
+			if(sym_name && !ptr->name) ptr->name = ptr->name=strdup(sym_name);
+			return ptr;
+			}
+        if((sym_name && curr_data) && (isalpha(sym_name[0]) || sym_name[0] == '$') && isdigit(sym_name[strlen(sym_name)-1])) {
+		if((ar = new AccRange(sym_name)) && ar->GetFirst(&col, &row) && 
+			(ptr = putsym(h_name, VAR, 0))) {
+			ptr->name = strdup(sym_name);
+			if(curr_data->GetResult(&ares, row, col)){
+				if(ares.type == ET_VALUE) {
+					ptr->type = VAR;	ptr->value.var = ares.value;
+					ptr->text = 0L;
+					}
+				else if(ares.type == ET_TEXT && ares.text) {
+					ptr->type = TXT;	ptr->value.var = 0.0;
+					ptr->text = strdup(ares.text);
+					}
+				else {
+					ptr->type = VAR;	ptr->value.var = 0.0;
+					ptr->text = 0L;
+					}
+				}
+			ptr->row = row;	ptr->col = col;
+			delete(ar);
+			return ptr;
+			}
+		}
+	return 0;
+}
+
+static int
+push(YYSTYPE *res, YYSTYPE *val)
+{
+	if(val->a_data && val->a_count) {
+		if(!(res->a_data)) {
+			if(!(val->a_data=(double*)realloc(val->a_data, (val->a_count+2)*sizeof(double))))return 0;
+			val->a_data[val->a_count++] = res->val;
+			res->a_data = val->a_data;		res->a_count = val->a_count;
+			val->a_data = 0L;			val->a_count = 0;
+			val->val = res->val;			return 1;
+			}
+		else {
+			if(!(res->a_data=(double*)realloc(res->a_data, (val->a_count+res->a_count)*sizeof(double))))return 0;
+			memcpy(&res->a_data[res->a_count], val->a_data, val->a_count*sizeof(double));
+			res->a_count += val->a_count;		free(val->a_data);
+			val->a_data = 0L;			val->a_count = 0;
+			return 1;
+			}
+		}
+	if(!(res->a_data )){
+		if(!(res->a_data =  (double*)malloc(2*sizeof(double))))return 0;
+		res->a_data[0] = res->val;			res->a_data[1] = val->val;
+		res->a_count = 2;
+		return 1;
+		}
+	else {
+		if(!(res->a_data = (double*)realloc(res->a_data, (res->a_count+2)*sizeof(double))))return 0; 
+		res->a_data[res->a_count] = val->val;		res->a_count++;
+		return 1;
+		}
+	return 0;
+}
+
+static int
+get_range(YYSTYPE *res, char *first, char *last)
+{
+	char r_txt[40];
+	AccRange *r;
+	int row, col;
+	double value;
+	YYSTYPE tmp;
+
+	if(!res || !first || !last || !curr_data) return 0;
+	sprintf(r_txt, "%s:%s", first, last);
+	if(!(res->a_data ) && (r = new AccRange(r_txt)) && r->GetFirst(&col, &row)) {
+		if(!(res->a_data =  (double*)malloc(r->CountItems() * sizeof(double)))) return 0;
+		res->a_count = 0;
+		for( ; r->GetNext(&col, &row); ) {
+			if(curr_data->GetValue(row, col, &value)) res->a_data[res->a_count++] = value;
+			}
+		delete r;		return 1;
+		}
+	if((r = new AccRange(r_txt)) && r->GetFirst(&col, &row)) {
+		//it is probably a bit slow to push every element
+		tmp.type = NUM;
+		for( ; r->GetNext(&col, &row); ) {
+			if(curr_data->GetValue(row, col, &value)) {
+				tmp.val = value;
+				push(res, &tmp);
+				}
+			}
+		delete r;		return 1;
+		}
+	return 0;
+}
+
+static YYSTYPE *proc_clause(YYSTYPE *res)
+{
+	int i, n, o_pos;
+	char *o_cmd;
+	double *n_data;
+
+	if(!(syntax_level) || !syntax_level->cl1 || syntax_level->cl2 <= syntax_level->cl1) return res;
+	if(!res->text) return res;
+	if(!res->a_data && (res->a_data = (double*)malloc(sizeof(double)))) {
+		res->a_data[0] = res->type == VAR && res->tptr ? res->tptr->value.var : res->val;
+		res->a_count = 1;
+		}
+	else if(!res->a_data) return res;
+	if(!(n_data = (double*)malloc(res->a_count * sizeof(double)))) return res;
+	o_pos = buff_pos;	o_cmd = buffer;
+	for(i = n = 0; i < res->a_count; i++) {
+		buffer = res->text;	buff_pos = 0;
+		if(!syntax_level) break;
+		syntax_level->clval = res->a_data[i];
+		yyparse();
+		if(line_res.type == ET_VALUE && line_res.value != 0.0) n_data[n++] = res->a_data[i];
+		}
+	free(res->a_data);	res->a_data = n_data;		res->a_count = n;
+	free(res->text);	res->text=0L;
+	syntax_level->cl1 = syntax_level->cl2 = 0;
+	buffer = o_cmd;	buff_pos = o_pos;
+	return res;
+}
+
+static void exec_clause(YYSTYPE *res)
+{
+	int i, j;
+	char *cmd;
+
+	if(!(syntax_level) || !syntax_level->cl1 || syntax_level->cl2 <= syntax_level->cl1) return;
+	if(!(cmd = (char*)malloc(syntax_level->cl2 - syntax_level->cl1 +2)))return;
+	while(buffer[syntax_level->cl1] <= ' ' && syntax_level->cl1 < syntax_level->cl2) syntax_level->cl1++;
+	for(j = 0, i = syntax_level->cl1; i< syntax_level->cl2; i++) {
+		cmd[j++] = buffer[i];
+		}
+	cmd[j++] = ';';		cmd[j++] = 0;
+	res->text = cmd;
+}
+
+struct parse_info  {
+	char *buffer;
+	int buff_pos;
+	double line_result;
+	DataObj *curr_data;
+	symrec *sym_table;
+	YYSTYPE yylval;
+	struct parse_info *next;
+	char **str_list;
+	int n_str;
+};
+static parse_info *parse_stack = 0L;
+
+static void push_parser()
+{
+	parse_info *ptr;
+
+	ptr = (parse_info *) malloc(sizeof(parse_info));
+	ptr->buffer = buffer;			ptr->buff_pos = buff_pos;
+	ptr->line_result = line_result;		ptr->curr_data = curr_data;
+	ptr->sym_table = sym_table;		sym_table = 0L;
+	memcpy(&ptr->yylval, &yylval, sizeof(YYSTYPE));
+	ptr->next = parse_stack;
+	ptr->str_list = str_list;		str_list = 0L;
+	ptr->n_str = n_str;			n_str = 0;
+	parse_stack = ptr;
+	push_syntax();
+}
+
+static void pop_parser()
+{
+	parse_info *ptr;
+
+	if(ptr = parse_stack) {
+		parse_stack = ptr->next;
+		buffer = ptr->buffer;			buff_pos = ptr->buff_pos;
+		line_result = ptr->line_result;		curr_data = ptr->curr_data;
+		sym_table = ptr->sym_table;
+		memcpy(&yylval, &ptr->yylval, sizeof(YYSTYPE));
+		str_list = ptr->str_list;		n_str = ptr->n_str;
+		free(ptr);
+		}
+	pop_syntax();
+}
+
+static int is_ttoken(int h_nam)
+{
+	switch(h_nam) {
+	case 69:		return E;
+	case 393:		return PI;
+	case 28381:
+		if(syntax_level) syntax_level->cl1 = buff_pos;
+		return CLAUSE;
+	case 20:		return CLVAL;
+		}
+	return 0;
+}
+
+static symrec *curr_sym;
+static int yylex (void)
+{
+	int i, c, tok;
+	unsigned int h_nam;
+	char tmp_txt[80];
+	symrec *s;
+
+	while((c = buffer[buff_pos++]) == ' ' || c == '\t');	//get first nonwhite char
+	if(!c) return 0;
+	//test for number
+	if(c == '.' || isdigit(c)) {
+		for(buff_pos--, i = 0; i < 79 && ((c = buffer[buff_pos]) == '.' || isdigit(c)); buff_pos++) {
+			tmp_txt[i++] = (char)c;
+			if(buffer[buff_pos+1] == 'e' && (buffer[buff_pos+2] == '-' || buffer[buff_pos+2] == '+')){
+				tmp_txt[i++] = buffer[++buff_pos];
+				tmp_txt[i++] = buffer[++buff_pos];
+				}
+			}
+		tmp_txt[i] = 0;
+		sscanf(tmp_txt, "%lf", &yylval.val);
+		yylval.type = NUM;
+		return NUM;
+		}
+	//test for name or stringtoken
+	if(isalpha(c) || c=='$') {
+ 		for(buff_pos--, i = 0; i < 79 && ((c = buffer[buff_pos]) && (isalnum(c) || c == '$')); buff_pos++) {
+			tmp_txt[i++] = (char)c; 
+			}
+		tmp_txt[i] = 0;
+		h_nam = HashValue((unsigned char*)tmp_txt);
+		if(tok = is_ttoken(h_nam)) 
+			return tok;
+		if(!(strcmp(tmp_txt, "pi"))) return PI;
+		if(!(strcmp(tmp_txt, "e"))) return E;
+		if(!(s = getsym(h_nam, tmp_txt))){
+			s = putsym(h_nam, VAR, 0);
+			s->name = strdup(tmp_txt);
+			}
+		
+		curr_sym = yylval.tptr = s;	return s->type;
+		}
+	//test for string
+	if(c == '"' || c == '\'') {
+		for(i= 0; i < 79 && ((tok = buffer[buff_pos]) && (tok != c)); buff_pos++) {
+			tmp_txt[i++] = (char)tok;
+			}
+		if(buffer[buff_pos] == c)buff_pos++;
+		tmp_txt[i] = 0;
+		yylval.text = PushString(tmp_txt);
+		return yylval.type = STR;
+		}
+	tok = 0;
+	switch(c) {
+	case '=':
+		if(buffer[buff_pos] == '=') tok = EQ;
+		break;
+	case '!':
+		if(buffer[buff_pos] == '=') tok = NE;
+		break;
+	case '>':
+		if(buffer[buff_pos] == '=') tok = GE;
+		else return GT;
+		break;
+	case '<':
+		if(buffer[buff_pos] == '=') tok = LE;
+		else if(buffer[buff_pos] == '>') tok = NE;
+		else return LT;
+		break;
+	case '&':
+		if(buffer[buff_pos] == '&') tok = AND;
+		break;
+	case '|':
+		if(buffer[buff_pos] == '|') tok = OR;
+		break;
+	case ')':
+		if(syntax_level) {
+			if(syntax_level->cl1 && syntax_level->next) {
+				syntax_level->next->cl1 = syntax_level->cl1;
+				syntax_level->next->cl2 = buff_pos-1;
+				}
+			}
+		pop_syntax();
+		break;
+	case '(':
+		push_syntax();
+	case '?':
+		if(syntax_level) syntax_level->last_tok = c;
+		break;
+	case ':':
+		if(syntax_level) {
+			if(syntax_level->last_tok == '(') return COLR;
+			else if(syntax_level->last_tok == '?') return COLC;
+			}
+		break;
+		}
+	if(tok) {
+		buff_pos++;		return tok;
+		}
+	//Any other character is a token by itself
+	return c;
+}
+
+bool do_xyfunc(DataObj *d, double x1, double x2, double step, char *expr, lfPOINT **pts, long *npts, char *param)
+{
+	double x, y;
+	symrec *s;
+	lfPOINT *new_points;
+	long npoints = 0;
+	int length;
+	unsigned int hn_x = HashValue((unsigned char *)"x");
+	unsigned int hn_y = HashValue((unsigned char *)"y");
+
+	if(x1 < x2) step = fabs(step);
+	else step = -fabs(step);
+	if(!(new_points = (lfPOINT*)calloc((iround(fabs(x2-x1)/fabs(step))+2), sizeof(lfPOINT))))
+		return false;
+	if(d) curr_data = d;
+	init_table();
+	if(param) {
+		length = strlen(param);
+		if(!(buffer = (char*)malloc(length+2))){
+			pop_parser();
+			return false;
+			}
+		strcpy(buffer, param);	buffer[length++] = ';';
+		buffer[length] = 0;	buff_pos = 0;
+		do {
+			yyparse();
+			}while(buff_pos < length);
+		free(buffer);		buffer = 0L;
+		}		
+	length = strlen(expr);
+	buffer = expr;		s = putsym(hn_x, VAR, 0);
+	for(x = x1; step > 0.0 ? x <= x2 : x >= x2; x += step) {
+		if(s = getsym(hn_x)){
+			s->value.var = x;	buff_pos = 0;
+			do {
+				yyparse();
+				}while(buff_pos < length);
+			if(s = getsym(hn_y)) y = s->value.var;
+			else y = line_result;
+			new_points[npoints].fx = (getsym(hn_x))->value.var;
+			new_points[npoints++].fy = y;
+			}
+		}
+	*pts = new_points;	*npts = npoints;
+	clear_table();
+	if(curr_data) {
+		curr_data->Command(CMD_CLEAR_ERROR, 0L, 0L);
+		curr_data->Command(CMD_REDRAW, 0L, 0L);
+		}
+	return true;
+}
+
+anyResult *do_formula(DataObj *d, char *expr)
+{
+	int length;
+	static anyResult ret;
+
+	if(d) curr_data = d;
+	ret.type = ET_ERROR;		ret.text = 0L;
+	if(!expr || !expr[0]) return &ret;
+	push_parser();		//make code reentrant
+	init_table();		length = strlen(expr);
+	if(!(buffer = (char*)malloc(length+2))){
+		pop_parser();
+		return &ret;
+		}
+	strcpy(buffer, expr);	buffer[length++] = ';';
+	buffer[length] = 0;	buff_pos = 0;
+	do {
+		yyparse();
+		}while(buff_pos < length);
+	if(curr_data && last_error) {
+		curr_data->Command(CMD_ERROR, last_error = 0L, 0L);
+		return &ret;
+		}
+	free(buffer);		buffer = 0L;
+	clear_table();
+	pop_parser();
+	return &line_res;
+}
+
+bool MoveFormula(DataObj *d, char *of, char *nf, int dx, int dy)
+{
+	int length, tok, pos, i;
+	char *res, desc1[2], desc2[2];
+
+	if(d) curr_data = d;
+	if(!curr_data || !of || !nf) return false;
+	push_parser();		//make code reentrant
+	init_table();		length = strlen(of);
+	if(!(buffer = (char*)malloc(length+2))){
+		pop_parser();
+		return false;
+		}
+	strcpy(buffer, of);	buffer[length++] = ';';
+	buffer[length] = 0;	buff_pos = pos = 0;
+	res = (char *)calloc(length*2+10, sizeof(char));
+	do {
+		tok = yylex ();
+		if(tok && tok < 256) {
+			if(res[pos-1] == ' ') pos--;
+			res[pos++] = (char)tok;
+			}
+		else switch(tok) {
+			case NUM:
+				pos += sprintf(res+pos, "%g ", yylval.val);
+				break;
+			case FNCT:
+				pos += sprintf(res+pos, "%s", curr_sym->name);
+				break;
+			case COLR:
+				pos += sprintf(res+pos, ":");
+				break;
+			case COLC:
+				pos += sprintf(res+pos, ": ");
+				break;
+			case CLVAL:
+				pos += sprintf(res+pos, "$$ ");
+				break;
+			case CLAUSE:
+				pos += sprintf(res+pos, " where ");
+				break;
+			case VAR:
+				if(curr_sym->col >= 0 && curr_sym->row >= 0) {
+					desc1[0] = desc1[1] = desc2[0] = desc2[1] = 0;
+					for(i=strlen(curr_sym->name)-1; i>0 && isdigit(curr_sym->name[i]); i--);
+					if(curr_sym->name[0] == '$') desc1[0] = '$';
+					if(curr_sym->name[i] == '$') desc2[0] = '$';
+					pos += sprintf(res+pos, "%s%s%s%d", desc1, 
+						Int2ColLabel(desc1[0] ? curr_sym->col : curr_sym->col+dx, false),
+						desc2, desc2[0]? curr_sym->row+1 : curr_sym->row+1+dy);
+					}
+				else pos += sprintf(res+pos, "%s ", curr_sym->name);
+				break;
+			case STR:
+				pos += sprintf(res+pos, "\"%s\"", yylval.text && yylval.text[0] ? yylval.text : "");
+				break;
+			case PI:
+				pos += sprintf(res+pos, "pi ");
+				break;
+			case E:
+				pos += sprintf(res+pos, "e ");
+				break;
+			case AND:
+				pos += sprintf(res+pos, " && ");
+				break;
+			case OR:
+				pos += sprintf(res+pos, " || ");
+				break;
+			case EQ:
+				pos += sprintf(res+pos, " == ");
+				break;
+			case NE:
+				pos += sprintf(res+pos, " != ");
+				break;
+			case GT:
+				pos += sprintf(res+pos, " > ");
+				break;
+			case GE:
+				pos += sprintf(res+pos, " >= ");
+				break;
+			case LT:
+				pos += sprintf(res+pos, " < ");
+				break;
+			case LE:
+				pos += sprintf(res+pos, " <= ");
+				break;
+			}
+		}while(buff_pos < length);
+	while((res[pos-1] == ';' || res[pos-1] == ' ') && pos > 0) { res[pos-1] = 0; pos--;} 
+	strcpy(nf, res);	free(res);
+	free(buffer);		buffer = 0L;
+	clear_table();
+	pop_parser();
+	return true;
+}
+
+static char *txt_formula;	//function to fit
+static double **parval;		//pointers to parameter values
+static void fcurve(double x, double z, double **a, double *y, double dyda[], int ma)
+{
+	int i, length;
+	double tmp, y1, y2;
+	symrec *symx, *s=0L;
+	unsigned int hn_x = HashValue((unsigned char *)"x");
+	unsigned int hn_y = HashValue((unsigned char *)"y");
+
+	if(!(symx = getsym(hn_x))) symx = putsym(hn_x, VAR, 0);
+	//swap parameters to requested set
+	if(a != parval) for(i = 0; i < ma; i++) {
+		tmp = *parval[i];	*parval[i]  = *a[i];	*a[i] = tmp;
+		}
+	//calc result
+	symx->value.var = x;	buffer = txt_formula;
+	buff_pos = 0;		length = strlen(txt_formula);
+	do {	yyparse();	}while(buff_pos < length);
+	if(s = getsym(hn_y)) *y = s->value.var;
+	else *y = line_result;
+	if(*y == HUGE_VAL || *y == -HUGE_VAL) {
+		for(i = 0, *y = 0.0; i < ma; dyda[i++] = 0.0);
+		return;
+		}
+	//partial derivatives for each parameter by numerical differentiation
+	for(i = 0; i < ma; i++) {
+		if(*parval[i] != 0.0) {
+			tmp = *parval[i];
+			*parval[i] = tmp*.995;
+			buff_pos = 0;
+			do {	yyparse();	}while(buff_pos < length);
+			y1 = s ? s->value.var : line_result;
+			*parval[i] = tmp*1.005;
+			buff_pos = 0;
+			do {	yyparse();	}while(buff_pos < length);
+			y2 = s ? s->value.var : line_result;
+			*parval[i] = tmp;
+			dyda[i] = (y2-y1)*100.0/tmp;
+			}
+		else dyda[i] = 0.0;
+		}
+	//swap parameters back to original
+	if(a != parval) for(i = 0; i < ma; i++) {
+		tmp = *parval[i];	*parval[i]  = *a[i];	*a[i] = tmp;
+		}
+}
+
+int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr, double conv, int maxiter, double *chi_2)
+{
+	int length, i, j, k, l, ndata, nparam, r1, r2, r3, c1, c2, c3, *lista, itst, itst1;
+	symrec *tab1, *tab2, *csr, **parsym;
+	AccRange *arx, *ary, *arz;
+	double *x, *y, *z, currx, curry, currz, alamda, chisq, ochisq;
+	double **covar, **alpha;
+	char tmp_txt[500];
+
+	if(d) curr_data = d;
+	if(chi_2) *chi_2 = 0.0;
+	txt_formula = expr;
+	if(!curr_data || !par || !expr || !rx || !ry) return 0;
+	//process ranges and create arrays
+	arx = ary = arz = 0L;	x = y = z = 0L;	parval = 0L;	parsym = 0L;
+	if(!(arx = new AccRange(rx)))return 0;
+	i = arx->CountItems()+1;
+	if(!(ary = new AccRange(ry))){
+		delete arx;	return 0;
+		}
+	if(rz && !(arz = new AccRange(rz))){
+		delete ary;	delete arx;	return 0;
+		}
+	if(!(x = (double*)malloc(i * sizeof(double)))){
+		if(arz) delete arz;
+		delete ary;	delete arx;	return 0;
+		}
+	if(!(y = (double*)malloc(i * sizeof(double)))){
+		if(arz) delete arz;
+		free(x);	delete arx;	delete ary;	return 0;
+		}
+	if(rz && !(y = (double*)malloc(i * sizeof(double)))){
+		if(arz) delete arz;
+		free(y);	free(x);	delete arx;	delete ary;	return 0;
+		}
+	arx->GetFirst(&c1, &r1);	ary->GetFirst(&c2, &r2);
+	if(rz) arz->GetFirst(&c3, &r3);
+	for(ndata = j = 0; j < i; j++) {
+		if(rz) {
+			if(arx->GetNext(&c1, &r1) && ary->GetNext(&c2, & r2) && arz->GetNext(&c3, &r3) &&
+				curr_data->GetValue(r1, c1, &currx) && curr_data->GetValue(r2, c2, &curry) &&
+				curr_data->GetValue(r3, c3, &currz)) {
+				x[ndata] = currx;	y[ndata] = curry;	z[ndata] = currz;	ndata++;
+				}
+			}
+		else {
+			if(arx->GetNext(&c1, &r1) && ary->GetNext(&c2, & r2) &&
+				curr_data->GetValue(r1, c1, &currx) && curr_data->GetValue(r2, c2, &curry)) {
+				x[ndata] = currx;	y[ndata] = curry;	ndata++;
+				}
+			}
+		}
+	//common initialization for parser tasks
+	push_parser();		//make code reentrant
+	init_table();		length = strlen(*par);
+	//process parameters
+	if(!(buffer = (char*)malloc(length+2))){
+		clear_table();	pop_parser();
+		if(arz) delete arz;
+		free(y);	free(x);	delete arx;	delete ary;
+		return 0;
+		}
+	strcpy(buffer, *par);	buffer[length++] = ';';
+	buffer[length] = 0;	buff_pos = 0;
+	tab1 = sym_table;
+	do {
+		yyparse();
+		}while(buff_pos < length);
+	tab2 = sym_table;	free(buffer);	buffer =0L;
+	for(nparam = 0, csr=tab2; csr != tab1; nparam++, csr = csr->next);
+	parsym = (symrec**)malloc((nparam+1)*sizeof(symrec*));
+	parval = (double**)malloc((nparam+1)*sizeof(double*));
+	for(i = 0, csr=tab2; csr != tab1 && i < nparam; i++, csr = csr->next){
+		parsym[i] = csr;	parval[i] = &csr->value.var;
+		}
+	//do iteratations to optimize fit
+	lista = (int*)malloc(sizeof(int)*nparam);
+	for(i = 0; i< nparam; i++) lista[i] = i;
+	covar = dmatrix(1, nparam, 1, nparam);
+	alpha = dmatrix(1, nparam, 1, nparam);
+	alamda = -1.0;		itst = 0;
+	mrqmin(x, y, z, ndata, parval, nparam, lista, nparam, covar, alpha, &chisq, fcurve, &alamda);
+	if(!Check_MRQerror()) {
+		for(itst = itst1 = 0, ochisq = chisq; itst < maxiter && chisq > conv && ochisq >= chisq && itst1 < 9; itst++) {
+			ochisq = chisq;
+			mrqmin(x, y, z, ndata, parval, nparam, lista, nparam, covar, alpha, &chisq, fcurve, &alamda);
+			if(ochisq == chisq) itst1++;
+			else itst1 = 0;
+			}
+		alamda = 0.0;
+		mrqmin(x, y, z, ndata, parval, nparam, lista, nparam, covar, alpha, &chisq, fcurve, &alamda);
+		Check_MRQerror();
+		}
+	for(i = nparam-1, j = k = l = 0; i >= 0; l = 0, i--) {
+		if(k > 20) {
+			if(tmp_txt[j-1] == ' ') j--;
+			if(tmp_txt[j-1] == ';') j--;
+			l = sprintf(tmp_txt+j, "\n");
+			j += l;		k = 0;
+			}
+		l += sprintf(tmp_txt+j, "%s%s=%g;", j && k ? " " : "", parsym[i]->name, parsym[i]->value.var);
+		j += l;			k += l;
+		}
+	free(*par);	*par = strdup(tmp_txt);
+	if(chi_2) *chi_2 = chisq;
+	//write back spreadsheet data if necessary
+	buffer = *par;	length = strlen(buffer);
+	do {
+		yyparse();
+		}while(buff_pos < length);
+	buffer = 0L;
+	free_dmatrix(alpha, 1, nparam, 1, nparam);
+	free_dmatrix(covar, 1, nparam, 1, nparam);
+	if(arz) delete arz;		if(z) free(z);
+	free(y);	free(x);	delete arx;	delete ary;
+	if(parval) free(parval);	if(parsym) free(parsym);
+	clear_table();
+	pop_parser();
+	if(curr_data){
+		curr_data->Command(CMD_CLEAR_ERROR, 0L, 0L);
+		curr_data->Command(CMD_REDRAW, 0L, 0L);
+		}
+	return itst < maxiter ? itst+1 : maxiter;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/mfcalc.y b/mfcalc.y
new file mode 100755
index 0000000..9aa1928
--- /dev/null
+++ b/mfcalc.y
@@ -0,0 +1,1247 @@
+%{
+/*
+ mfcalc.y, mfcalc.cpp, Copyright (c) 2004, 2005 R.Lackner
+ parse string and simple math: based on the bison 'mfcalc' example
+
+    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 <ctype.h>
+#include <string.h>
+#include <stdio.h>
+#include "rlplot.h"
+
+struct symrec {
+	int type, row, col;
+	unsigned int h_name;
+	char *name, *text;
+	struct {
+		double var;
+		double (*fnctptr)(...);
+		} value;
+	int arg_type;
+	struct symrec *next;
+};
+
+// syntactical information
+struct syntax_info {
+	int last_tok;			//resolve ambiguous ':'
+	double clval;			//current value for where - clause
+	int cl1, cl2;			//limits of clause formula in buffer
+	struct syntax_info *next;
+	};
+static syntax_info *syntax_level = 0L;
+
+
+typedef struct{
+	double  val;
+	int type;
+	symrec  *tptr;
+	double *a_data;
+	char *text;
+	int a_count;
+
+}YYSTYPE;
+
+static symrec *putsym (unsigned int h_name, int sym_type, int arg_type);
+static symrec *getsym (unsigned int h_name, char *sym_name = 0L);
+static int push(YYSTYPE *res, YYSTYPE *val);
+static void store_res(YYSTYPE *res);
+static char *PushString(char *text);
+static char *add_strings(char *st1, char *st2);
+static char *string_value(YYSTYPE *exp);
+static int get_range(YYSTYPE *res, char *first, char *last);
+static void exec_clause(YYSTYPE *res);
+static YYSTYPE *proc_clause(YYSTYPE *res);
+static void yyerror(char *s);
+static int yylex(void);
+
+static char res_txt[1000];
+static anyResult line_res = {ET_UNKNOWN, 0.0, res_txt};
+static double line_result;
+static DataObj *curr_data;
+
+//the current command buffer
+static char *buffer = 0L;
+static int buff_pos = 0;
+%}
+
+%token <val>  NUM ARR STR PI E CLVAL
+%token <tptr> VAR FNCT TXT
+%type  <val>  exp str_exp
+
+%right  '='
+%left	','			/* list separator */
+%left	CLAUSE			/* clause (where) operator */
+%right	COLR COLC		/* range sep., conditional sep. */
+%right	'?'			/* conditional assignment */
+%left	AND OR
+%left   EQ NE GT GE LT LE
+%left   '-' '+'
+%left   '*' '/'
+%left   NEG 		/* negation-unary minus */
+%right  '^'	 	/* exponentiation       */
+
+/* Grammar follows */
+%%
+input:    /* empty string */
+	| input line
+;
+
+line:	 '\n' ';'
+	| exp '\n'		{store_res(&yyvsp[-1]); return 0;}
+	| exp ';'		{store_res(&yyvsp[-1]); return 0;}
+	| str_exp '\n'		{store_res(&yyvsp[-1]); return 0;}
+	| str_exp ';'		{store_res(&yyvsp[-1]); return 0;}
+	| error '\n'		{yyerrok;}
+;
+
+str_exp:
+	STR			{yyval.type=STR;}
+	|str_exp '+' exp	{yyval.text=add_strings(yyvsp[-2].text, string_value(&yyvsp[0])); yyval.type=STR;}
+	|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;}
+;
+
+exp:	NUM				{$$ = $1; yyval.type = NUM;}
+        |TXT				{$$ = 0.0;}
+	|CLVAL				{$$ = syntax_level ? syntax_level->clval : 0.0; }
+	|PI				{$$ = 3.14159265358979; yyval.type = NUM;}
+	|E				{$$ = 2.71828182845905; yyval.type = NUM;}
+	|VAR				{$$ = $1->value.var; yyval.type=VAR}
+	|VAR '=' exp		{$$ = $1->value.var = $3;
+					if($1->row >=0 && $1->col >= 0 && curr_data) curr_data->SetValue($1->row, $1->col, $3);}
+	|FNCT '(' exp ')'	{$$ = ($1->arg_type == ARR) ? ((*$1->value.fnctptr)(proc_clause(&yyvsp[-1]))) : 
+					(*($1->value.fnctptr))($3);}
+	|exp AND exp		{$$ = (($1 != 0) && ($3 != 0))? 1 : 0;}
+	|exp OR exp		{$$ = (($1 != 0) || ($3 != 0))? 1 : 0;}
+	|exp EQ exp		{$$ = ($1 == $3) ? 1 : 0;}
+	|exp NE exp		{$$ = ($1 != $3) ? 1 : 0;}
+	|exp GT exp		{$$ = ($1 > $3) ? 1 : 0;}
+	|exp GE exp		{$$ = ($1 >= $3) ? 1 : 0;}
+	|exp LT exp		{$$ = ($1 < $3) ? 1 : 0;}
+	|exp LE exp		{$$ = ($1 <= $3) ? 1 : 0;}
+	|exp '+' exp		{$$ = $1 + $3;}
+	|exp '-' exp		{$$ = $1 - $3;}
+	|exp '*' exp		{$$ = $1 * $3;}
+	|exp '/' exp		{if($3 != 0.0) $$ = $1 / $3;
+					else $$ = (getsym(HashValue((unsigned char*)"zdiv")))->value.var; }
+	|exp ',' exp		{push(&yyval, &yyvsp[0]);}
+	|VAR COLR VAR		{get_range(&yyval, $1->name, $3->name);}
+	|'-' exp  %prec NEG	{$$ = -$2;}
+	|exp '^' exp		{$$ = pow($1, $3);}
+	|exp CLAUSE exp		{$$ = $1; exec_clause(&yyval);} 
+	|'(' exp ')'		{memcpy(&yyval, &yyvsp[-1], sizeof(YYSTYPE))}
+	|exp '?' exp COLC exp	{memcpy(&yyval, $1 != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE))}
+	|exp '?' STR COLC STR	{memcpy(&yyval, $1 != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE))}
+	|exp '?' STR COLC exp	{memcpy(&yyval, $1 != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE))}
+	|exp '?' exp COLC STR	{memcpy(&yyval, $1 != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE))}
+;
+%%
+
+static char *last_error = 0L;
+static void yyerror(char *s)
+{  
+	//Called by yyparse on error
+	if(curr_data) curr_data->Command(CMD_ERROR, last_error = s, 0L);
+	else printf("%s\n", s);
+}
+
+static void store_res(YYSTYPE *res)
+{
+	if(res->type == STR) {
+		line_result = 0.0;
+		line_res.type = ET_TEXT;
+		line_res. value = 0.0;
+		if(res->text) strcpy(res_txt, res->text);
+		}
+	else if(res->type == ARR || (res->a_data)) {
+		line_result = 0.0;
+		line_res.type = ET_TEXT;
+		line_res. value = 0.0;
+		strcpy(res_txt, "#ARRAY");
+		}
+	else if(res->tptr && res->tptr->type == TXT) {
+		line_result = 0.0;
+		line_res.type = ET_TEXT;
+		line_res.value = 0.0;
+		if(res->tptr->text) strcpy(res_txt, res->tptr->text);
+		}
+	else {
+		line_result = res->val;
+		line_res.type = ET_VALUE;
+		line_res.value = res->val;
+		}
+}
+
+static char *add_strings(char *st1, char *st2)
+{
+	char *newstr, *ret;
+
+	if(st1 && st2) {
+		if(newstr = (char*)malloc(strlen(st1) +strlen(st2) +4)) {
+			sprintf(newstr, "%s%s", st1, st2);
+			ret = PushString(newstr);
+			free(newstr);
+			return ret;
+			}
+		else return 0L;
+		}
+	if(st1) return st1;
+	if(st2) return st2;
+	return 0L;
+}
+
+static char *string_value(YYSTYPE *exp)
+{
+	char *st1, tmp[50];
+
+	if(exp->type == STR){
+		st1 = exp->text;
+		}
+	else if(exp->tptr && exp->tptr->type == TXT) {
+		st1 = exp->tptr->text;
+		}
+	else {
+		sprintf(tmp,"%g", exp->val);
+		st1 = tmp;
+		}
+	return PushString(st1);
+}
+
+// store syntactical information
+static void push_syntax()
+{
+	syntax_info *next;
+
+	if(!(next = (syntax_info*)calloc(1, sizeof(syntax_info)))) return;
+	if(syntax_level)memcpy(next, syntax_level, sizeof(syntax_info));
+	next->next=syntax_level;
+	syntax_level = next;
+}
+
+static void pop_syntax()
+{
+	syntax_info *si;
+
+	if(si = syntax_level) {
+		syntax_level = si->next;
+		free(si);
+		}
+}
+
+// more functions
+static double sign(double v)
+{
+	if(v > 0.0) return 1.0;
+	if(v < 0.0) return -1.0;
+	return 0.0;
+}
+
+static double factorial(double v)
+{
+	return factrl((int)v);
+}
+
+static void close_arr_func(YYSTYPE *sr)
+{
+	free(sr->a_data);
+	sr->a_data = 0L;		sr->a_count = 0;
+	if(sr->type == ARR) sr->type = NUM;
+}
+
+#undef min
+static double min(YYSTYPE *sr) 
+{
+	int i;
+
+	if(!sr) return 0.0;
+	if(sr->a_data && sr->a_data){
+		for(i = 1, sr->val = sr->a_data[0]; i < sr->a_count; i++) 
+			if(sr->a_data[i] < sr->val) sr->val = sr->a_data[i];
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+#undef max
+static double max(YYSTYPE *sr) 
+{
+	int i;
+
+	if(!sr) return 0.0;
+	if(sr->a_data){
+		for(i = 1, sr->val = sr->a_data[0]; i < sr->a_count; i++) 
+			if(sr->a_data[i] > sr->val) sr->val = sr->a_data[i];
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double count(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	if(sr->a_data){
+		sr->val = (double)sr->a_count;
+		close_arr_func(sr);
+		}
+	else sr->val = 0.0;
+	return sr->val;
+}
+
+static double sum(YYSTYPE *sr) 
+{
+	int i;
+
+	if(!sr) return 0.0;
+	if(sr->a_data){
+		for(i = 1, sr->val = sr->a_data[0]; i < sr->a_count; i++) sr->val += sr->a_data[i];
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double calc_variance(double *values, int n)
+{
+	int i;
+	double ss, d, mean = d_amean(n, values);
+
+	for(i=0, ss=0.0; i < n; i++) ss += ((d=values[i]-mean)*d);
+	return (ss/(n-1));
+}
+
+static double mean(YYSTYPE *sr) 
+{
+	if(!sr) return 0.0;
+	if(sr->a_data && sr->a_count){
+		sr->val = d_amean(sr->a_count, sr->a_data );
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double gmean(YYSTYPE *sr) 
+{
+	if(!sr) return 0.0;
+	if(sr->a_data && sr->a_count){
+		sr->val = d_gmean(sr->a_count, sr->a_data );
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double hmean(YYSTYPE *sr) 
+{
+	if(!sr) return 0.0;
+	if(sr->a_data && sr->a_count){
+		sr->val = d_hmean(sr->a_count, sr->a_data );
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double quartile1(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	if(sr->a_data && sr->a_count){
+		d_quartile(sr->a_count, sr->a_data, &sr->val, 0L, 0L);
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double quartile2(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	if(sr->a_data && sr->a_count){
+		d_quartile(sr->a_count, sr->a_data, 0L, &sr->val, 0L);
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double quartile3(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	if(sr->a_data && sr->a_count){
+		d_quartile(sr->a_count, sr->a_data, 0L, 0L, &sr->val);
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double variance(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count){
+		sr->val = calc_variance(sr->a_data, sr->a_count);
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double stdev(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count){
+		sr->val = sqrt(calc_variance(sr->a_data, sr->a_count));
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double sterr(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count){
+		sr->val = sqrt(calc_variance(sr->a_data, sr->a_count))/sqrt(sr->a_count);
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double tdist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 2){
+		sr->val = t_dist(sr->a_data[0], sr->a_data[1]);
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+static double fdist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0;
+	if(sr->a_data && sr->a_count == 3){
+		sr->val = f_dist(sr->a_data[0], sr->a_data[1], sr->a_data[2]);
+		close_arr_func(sr);
+		}
+	return sr->val;
+}
+
+struct init
+{
+	unsigned int h_name;
+	double (*fnct)(double);
+	int arg_type;
+};
+
+static struct init arith_fncts[] = {
+	{HashValue((unsigned char*)"variance"), (double(*)(double))&variance, ARR},
+	{HashValue((unsigned char*)"stdev"), (double(*)(double))&stdev, ARR},
+	{HashValue((unsigned char*)"sterr"), (double(*)(double))&sterr, ARR},
+	{HashValue((unsigned char*)"min"), (double(*)(double))&min, ARR},
+	{HashValue((unsigned char*)"max"), (double(*)(double))&max, ARR},
+	{HashValue((unsigned char*)"count"), (double(*)(double))&count, ARR},
+	{HashValue((unsigned char*)"sum"), (double(*)(double))&sum, ARR},
+	{HashValue((unsigned char*)"mean"), (double(*)(double))&mean, ARR},
+	{HashValue((unsigned char*)"median"), (double(*)(double))&quartile2, ARR},
+	{HashValue((unsigned char*)"quartile1"), (double(*)(double))&quartile1, ARR},
+	{HashValue((unsigned char*)"quartile2"), (double(*)(double))&quartile2, ARR},
+	{HashValue((unsigned char*)"quartile3"), (double(*)(double))&quartile3, ARR},
+	{HashValue((unsigned char*)"gmean"), (double(*)(double))&gmean, ARR},
+	{HashValue((unsigned char*)"hmean"), (double(*)(double))&hmean, ARR},
+	{HashValue((unsigned char*)"tdist"), (double(*)(double))&tdist, ARR},
+	{HashValue((unsigned char*)"fdist"), (double(*)(double))&fdist, ARR},
+	{HashValue((unsigned char*)"sign"), sign, VAR},
+	{HashValue((unsigned char*)"gammaln"), gammln, VAR},
+	{HashValue((unsigned char*)"factorial"), factorial, VAR},
+	{HashValue((unsigned char*)"abs"), fabs, VAR},
+	{HashValue((unsigned char*)"asin"), asin, VAR},
+	{HashValue((unsigned char*)"acos"), acos, VAR},
+	{HashValue((unsigned char*)"atan"), atan, VAR},
+	{HashValue((unsigned char*)"sinh"), sinh, VAR},
+	{HashValue((unsigned char*)"cosh"), cosh, VAR},
+	{HashValue((unsigned char*)"tanh"), tanh, VAR},
+	{HashValue((unsigned char*)"sin"),  sin, VAR},
+	{HashValue((unsigned char*)"cos"),  cos, VAR},
+	{HashValue((unsigned char*)"atan"), atan, VAR},
+	{HashValue((unsigned char*)"log10"), log10, VAR},
+	{HashValue((unsigned char*)"ln"),   log, VAR},
+	{HashValue((unsigned char*)"log"),   log, VAR},
+	{HashValue((unsigned char*)"exp"),  exp, VAR},
+	{HashValue((unsigned char*)"sqrt"), sqrt, VAR},
+	{0, 0, 0}};
+
+// Store strings in a list
+static char **str_list = 0L;
+static int n_str = 0;
+
+static char *PushString(char *text)
+{
+	if(text && text[0]) {
+		if(str_list = (char**)realloc(str_list, sizeof(char*)*(n_str+1)))
+			str_list[n_str] = strdup(text);
+		return str_list[n_str++];
+		}
+	return 0L;
+}
+
+//The symbol table: a chain of `struct symrec'
+static symrec *sym_table = (symrec *) 0;
+
+// Put arithmetic functions and predifened variables in table
+static void init_table (void)
+{
+	int i;
+	symrec *ptr;
+
+	for (i = 0; arith_fncts[i].h_name; i++) {
+		ptr = putsym (arith_fncts[i].h_name, FNCT, arith_fncts[i].arg_type);
+		ptr->value.fnctptr = (double (*)(...))arith_fncts[i].fnct;
+		}
+	ptr = putsym(HashValue((unsigned char*)"zdiv"), VAR, 0);	ptr->value.var = 1.0;
+	str_list = 0L;		n_str = 0;
+	push_syntax();
+}
+
+static void clear_table()
+{
+	int i;
+	symrec *ptr, *next;
+
+	for (ptr = sym_table; ptr != (symrec *) 0;){
+		if(ptr) {
+			if(ptr->name) free(ptr->name);
+			if(ptr->text) free(ptr->text);
+			next = (symrec*)ptr->next;
+			free(ptr);
+			}
+		ptr = next;
+		}
+	sym_table = (symrec *) 0;
+	if(str_list) {
+		for(i = 0; i < n_str; i++) if(str_list[i]) free(str_list[i]);
+		free(str_list);		str_list = 0L;		n_str = 0;
+		}
+	pop_syntax();
+}
+
+static symrec *
+putsym (unsigned int h_name, int sym_type, int arg_type)
+{
+	symrec *ptr;
+
+	ptr = (symrec *) malloc (sizeof (symrec));
+	ptr->h_name = h_name;
+	ptr->type = sym_type;
+	ptr->name = ptr->text = 0L;
+	ptr->value.var = 0; /* set value to 0 even if fctn.  */
+	ptr->arg_type = arg_type;
+	ptr->col = ptr->row = -1;
+	ptr->next = (struct symrec *)sym_table;
+	sym_table = ptr;
+	return ptr;
+}
+
+static symrec *
+getsym (unsigned int h_name, char *sym_name)
+{
+	symrec *ptr;
+	int row, col;
+	AccRange *ar;
+	anyResult ares;
+
+	if(!h_name) return 0;
+	for (ptr = sym_table; ptr != (symrec *) 0; ptr = (symrec *)ptr->next)
+		if (ptr->h_name == h_name){
+			if(sym_name && !ptr->name) ptr->name = ptr->name=strdup(sym_name);
+			return ptr;
+			}
+        if((sym_name && curr_data) && (isalpha(sym_name[0]) || sym_name[0] == '$') && isdigit(sym_name[strlen(sym_name)-1])) {
+		if((ar = new AccRange(sym_name)) && ar->GetFirst(&col, &row) && 
+			(ptr = putsym(h_name, VAR, 0))) {
+			ptr->name = strdup(sym_name);
+			if(curr_data->GetResult(&ares, row, col)){
+				if(ares.type == ET_VALUE) {
+					ptr->type = VAR;	ptr->value.var = ares.value;
+					ptr->text = 0L;
+					}
+				else if(ares.type == ET_TEXT && ares.text) {
+					ptr->type = TXT;	ptr->value.var = 0.0;
+					ptr->text = strdup(ares.text);
+					}
+				else {
+					ptr->type = VAR;	ptr->value.var = 0.0;
+					ptr->text = 0L;
+					}
+				}
+			ptr->row = row;	ptr->col = col;
+			delete(ar);
+			return ptr;
+			}
+		}
+	return 0;
+}
+
+static int
+push(YYSTYPE *res, YYSTYPE *val)
+{
+	if(val->a_data && val->a_count) {
+		if(!(res->a_data)) {
+			if(!(val->a_data=(double*)realloc(val->a_data, (val->a_count+2)*sizeof(double))))return 0;
+			val->a_data[val->a_count++] = res->val;
+			res->a_data = val->a_data;		res->a_count = val->a_count;
+			val->a_data = 0L;			val->a_count = 0;
+			val->val = res->val;			return 1;
+			}
+		else {
+			if(!(res->a_data=(double*)realloc(res->a_data, (val->a_count+res->a_count)*sizeof(double))))return 0;
+			memcpy(&res->a_data[res->a_count], val->a_data, val->a_count*sizeof(double));
+			res->a_count += val->a_count;		free(val->a_data);
+			val->a_data = 0L;			val->a_count = 0;
+			return 1;
+			}
+		}
+	if(!(res->a_data )){
+		if(!(res->a_data =  (double*)malloc(2*sizeof(double))))return 0;
+		res->a_data[0] = res->val;			res->a_data[1] = val->val;
+		res->a_count = 2;
+		return 1;
+		}
+	else {
+		if(!(res->a_data = (double*)realloc(res->a_data, (res->a_count+2)*sizeof(double))))return 0; 
+		res->a_data[res->a_count] = val->val;		res->a_count++;
+		return 1;
+		}
+	return 0;
+}
+
+static int
+get_range(YYSTYPE *res, char *first, char *last)
+{
+	char r_txt[40];
+	AccRange *r;
+	int row, col;
+	double value;
+	YYSTYPE tmp;
+
+	if(!res || !first || !last || !curr_data) return 0;
+	sprintf(r_txt, "%s:%s", first, last);
+	if(!(res->a_data ) && (r = new AccRange(r_txt)) && r->GetFirst(&col, &row)) {
+		if(!(res->a_data =  (double*)malloc(r->CountItems() * sizeof(double)))) return 0;
+		res->a_count = 0;
+		for( ; r->GetNext(&col, &row); ) {
+			if(curr_data->GetValue(row, col, &value)) res->a_data[res->a_count++] = value;
+			}
+		delete r;		return 1;
+		}
+	if((r = new AccRange(r_txt)) && r->GetFirst(&col, &row)) {
+		//it is probably a bit slow to push every element
+		tmp.type = NUM;
+		for( ; r->GetNext(&col, &row); ) {
+			if(curr_data->GetValue(row, col, &value)) {
+				tmp.val = value;
+				push(res, &tmp);
+				}
+			}
+		delete r;		return 1;
+		}
+	return 0;
+}
+
+static YYSTYPE *proc_clause(YYSTYPE *res)
+{
+	int i, n, o_pos;
+	char *o_cmd;
+	double *n_data;
+
+	if(!(syntax_level) || !syntax_level->cl1 || syntax_level->cl2 <= syntax_level->cl1) return res;
+	if(!res->text) return res;
+	if(!res->a_data && (res->a_data = (double*)malloc(sizeof(double)))) {
+		res->a_data[0] = res->type == VAR && res->tptr ? res->tptr->value.var : res->val;
+		res->a_count = 1;
+		}
+	else if(!res->a_data) return res;
+	if(!(n_data = (double*)malloc(res->a_count * sizeof(double)))) return res;
+	o_pos = buff_pos;	o_cmd = buffer;
+	for(i = n = 0; i < res->a_count; i++) {
+		buffer = res->text;	buff_pos = 0;
+		if(!syntax_level) break;
+		syntax_level->clval = res->a_data[i];
+		yyparse();
+		if(line_res.type == ET_VALUE && line_res.value != 0.0) n_data[n++] = res->a_data[i];
+		}
+	free(res->a_data);	res->a_data = n_data;		res->a_count = n;
+	free(res->text);	res->text=0L;
+	syntax_level->cl1 = syntax_level->cl2 = 0;
+	buffer = o_cmd;	buff_pos = o_pos;
+	return res;
+}
+
+static void exec_clause(YYSTYPE *res)
+{
+	int i, j;
+	char *cmd;
+
+	if(!(syntax_level) || !syntax_level->cl1 || syntax_level->cl2 <= syntax_level->cl1) return;
+	if(!(cmd = (char*)malloc(syntax_level->cl2 - syntax_level->cl1 +2)))return;
+	while(buffer[syntax_level->cl1] <= ' ' && syntax_level->cl1 < syntax_level->cl2) syntax_level->cl1++;
+	for(j = 0, i = syntax_level->cl1; i< syntax_level->cl2; i++) {
+		cmd[j++] = buffer[i];
+		}
+	cmd[j++] = ';';		cmd[j++] = 0;
+	res->text = cmd;
+}
+
+struct parse_info  {
+	char *buffer;
+	int buff_pos;
+	double line_result;
+	DataObj *curr_data;
+	symrec *sym_table;
+	YYSTYPE yylval;
+	struct parse_info *next;
+	char **str_list;
+	int n_str;
+};
+static parse_info *parse_stack = 0L;
+
+static void push_parser()
+{
+	parse_info *ptr;
+
+	ptr = (parse_info *) malloc(sizeof(parse_info));
+	ptr->buffer = buffer;			ptr->buff_pos = buff_pos;
+	ptr->line_result = line_result;		ptr->curr_data = curr_data;
+	ptr->sym_table = sym_table;		sym_table = 0L;
+	memcpy(&ptr->yylval, &yylval, sizeof(YYSTYPE));
+	ptr->next = parse_stack;
+	ptr->str_list = str_list;		str_list = 0L;
+	ptr->n_str = n_str;			n_str = 0;
+	parse_stack = ptr;
+	push_syntax();
+}
+
+static void pop_parser()
+{
+	parse_info *ptr;
+
+	if(ptr = parse_stack) {
+		parse_stack = ptr->next;
+		buffer = ptr->buffer;			buff_pos = ptr->buff_pos;
+		line_result = ptr->line_result;		curr_data = ptr->curr_data;
+		sym_table = ptr->sym_table;
+		memcpy(&yylval, &ptr->yylval, sizeof(YYSTYPE));
+		str_list = ptr->str_list;		n_str = ptr->n_str;
+		free(ptr);
+		}
+	pop_syntax();
+}
+
+static int is_ttoken(int h_nam)
+{
+	switch(h_nam) {
+	case 69:		return E;
+	case 393:		return PI;
+	case 28381:
+		if(syntax_level) syntax_level->cl1 = buff_pos;
+		return CLAUSE;
+	case 20:		return CLVAL;
+		}
+	return 0;
+}
+
+static symrec *curr_sym;
+static int yylex (void)
+{
+	int i, c, tok;
+	unsigned int h_nam;
+	char tmp_txt[80];
+	symrec *s;
+
+	while((c = buffer[buff_pos++]) == ' ' || c == '\t');	//get first nonwhite char
+	if(!c) return 0;
+	//test for number
+	if(c == '.' || isdigit(c)) {
+		for(buff_pos--, i = 0; i < 79 && ((c = buffer[buff_pos]) == '.' || isdigit(c)); buff_pos++) {
+			tmp_txt[i++] = (char)c;
+			if(buffer[buff_pos+1] == 'e' && (buffer[buff_pos+2] == '-' || buffer[buff_pos+2] == '+')){
+				tmp_txt[i++] = buffer[++buff_pos];
+				tmp_txt[i++] = buffer[++buff_pos];
+				}
+			}
+		tmp_txt[i] = 0;
+		sscanf(tmp_txt, "%lf", &yylval.val);
+		yylval.type = NUM;
+		return NUM;
+		}
+	//test for name or stringtoken
+	if(isalpha(c) || c=='$') {
+ 		for(buff_pos--, i = 0; i < 79 && ((c = buffer[buff_pos]) && (isalnum(c) || c == '$')); buff_pos++) {
+			tmp_txt[i++] = (char)c; 
+			}
+		tmp_txt[i] = 0;
+		h_nam = HashValue((unsigned char*)tmp_txt);
+		if(tok = is_ttoken(h_nam)) 
+			return tok;
+		if(!(strcmp(tmp_txt, "pi"))) return PI;
+		if(!(strcmp(tmp_txt, "e"))) return E;
+		if(!(s = getsym(h_nam, tmp_txt))){
+			s = putsym(h_nam, VAR, 0);
+			s->name = strdup(tmp_txt);
+			}
+		
+		curr_sym = yylval.tptr = s;	return s->type;
+		}
+	//test for string
+	if(c == '"' || c == '\'') {
+		for(i= 0; i < 79 && ((tok = buffer[buff_pos]) && (tok != c)); buff_pos++) {
+			tmp_txt[i++] = (char)tok;
+			}
+		if(buffer[buff_pos] == c)buff_pos++;
+		tmp_txt[i] = 0;
+		yylval.text = PushString(tmp_txt);
+		return yylval.type = STR;
+		}
+	tok = 0;
+	switch(c) {
+	case '=':
+		if(buffer[buff_pos] == '=') tok = EQ;
+		break;
+	case '!':
+		if(buffer[buff_pos] == '=') tok = NE;
+		break;
+	case '>':
+		if(buffer[buff_pos] == '=') tok = GE;
+		else return GT;
+		break;
+	case '<':
+		if(buffer[buff_pos] == '=') tok = LE;
+		else if(buffer[buff_pos] == '>') tok = NE;
+		else return LT;
+		break;
+	case '&':
+		if(buffer[buff_pos] == '&') tok = AND;
+		break;
+	case '|':
+		if(buffer[buff_pos] == '|') tok = OR;
+		break;
+	case ')':
+		if(syntax_level) {
+			if(syntax_level->cl1 && syntax_level->next) {
+				syntax_level->next->cl1 = syntax_level->cl1;
+				syntax_level->next->cl2 = buff_pos-1;
+				}
+			}
+		pop_syntax();
+		break;
+	case '(':
+		push_syntax();
+	case '?':
+		if(syntax_level) syntax_level->last_tok = c;
+		break;
+	case ':':
+		if(syntax_level) {
+			if(syntax_level->last_tok == '(') return COLR;
+			else if(syntax_level->last_tok == '?') return COLC;
+			}
+		break;
+		}
+	if(tok) {
+		buff_pos++;		return tok;
+		}
+	//Any other character is a token by itself
+	return c;
+}
+
+bool do_xyfunc(DataObj *d, double x1, double x2, double step, char *expr, lfPOINT **pts, long *npts, char *param)
+{
+	double x, y;
+	symrec *s;
+	lfPOINT *new_points;
+	long npoints = 0;
+	int length;
+	unsigned int hn_x = HashValue((unsigned char *)"x");
+	unsigned int hn_y = HashValue((unsigned char *)"y");
+
+	if(x1 < x2) step = fabs(step);
+	else step = -fabs(step);
+	if(!(new_points = (lfPOINT*)calloc((iround(fabs(x2-x1)/fabs(step))+2), sizeof(lfPOINT))))
+		return false;
+	if(d) curr_data = d;
+	init_table();
+	if(param) {
+		length = strlen(param);
+		if(!(buffer = (char*)malloc(length+2))){
+			pop_parser();
+			return false;
+			}
+		strcpy(buffer, param);	buffer[length++] = ';';
+		buffer[length] = 0;	buff_pos = 0;
+		do {
+			yyparse();
+			}while(buff_pos < length);
+		free(buffer);		buffer = 0L;
+		}		
+	length = strlen(expr);
+	buffer = expr;		s = putsym(hn_x, VAR, 0);
+	for(x = x1; step > 0.0 ? x <= x2 : x >= x2; x += step) {
+		if(s = getsym(hn_x)){
+			s->value.var = x;	buff_pos = 0;
+			do {
+				yyparse();
+				}while(buff_pos < length);
+			if(s = getsym(hn_y)) y = s->value.var;
+			else y = line_result;
+			new_points[npoints].fx = (getsym(hn_x))->value.var;
+			new_points[npoints++].fy = y;
+			}
+		}
+	*pts = new_points;	*npts = npoints;
+	clear_table();
+	if(curr_data) {
+		curr_data->Command(CMD_CLEAR_ERROR, 0L, 0L);
+		curr_data->Command(CMD_REDRAW, 0L, 0L);
+		}
+	return true;
+}
+
+anyResult *do_formula(DataObj *d, char *expr)
+{
+	int length;
+	static anyResult ret;
+
+	if(d) curr_data = d;
+	ret.type = ET_ERROR;		ret.text = 0L;
+	if(!expr || !expr[0]) return &ret;
+	push_parser();		//make code reentrant
+	init_table();		length = strlen(expr);
+	if(!(buffer = (char*)malloc(length+2))){
+		pop_parser();
+		return &ret;
+		}
+	strcpy(buffer, expr);	buffer[length++] = ';';
+	buffer[length] = 0;	buff_pos = 0;
+	do {
+		yyparse();
+		}while(buff_pos < length);
+	if(curr_data && last_error) {
+		curr_data->Command(CMD_ERROR, last_error = 0L, 0L);
+		return &ret;
+		}
+	free(buffer);		buffer = 0L;
+	clear_table();
+	pop_parser();
+	return &line_res;
+}
+
+bool MoveFormula(DataObj *d, char *of, char *nf, int dx, int dy)
+{
+	int length, tok, pos, i;
+	char *res, desc1[2], desc2[2];
+
+	if(d) curr_data = d;
+	if(!curr_data || !of || !nf) return false;
+	push_parser();		//make code reentrant
+	init_table();		length = strlen(of);
+	if(!(buffer = (char*)malloc(length+2))){
+		pop_parser();
+		return false;
+		}
+	strcpy(buffer, of);	buffer[length++] = ';';
+	buffer[length] = 0;	buff_pos = pos = 0;
+	res = (char *)calloc(length*2+10, sizeof(char));
+	do {
+		tok = yylex ();
+		if(tok && tok < 256) {
+			if(res[pos-1] == ' ') pos--;
+			res[pos++] = (char)tok;
+			}
+		else switch(tok) {
+			case NUM:
+				pos += sprintf(res+pos, "%g ", yylval.val);
+				break;
+			case FNCT:
+				pos += sprintf(res+pos, "%s", curr_sym->name);
+				break;
+			case COLR:
+				pos += sprintf(res+pos, ":");
+				break;
+			case COLC:
+				pos += sprintf(res+pos, ": ");
+				break;
+			case CLVAL:
+				pos += sprintf(res+pos, "$$ ");
+				break;
+			case CLAUSE:
+				pos += sprintf(res+pos, " where ");
+				break;
+			case VAR:
+				if(curr_sym->col >= 0 && curr_sym->row >= 0) {
+					desc1[0] = desc1[1] = desc2[0] = desc2[1] = 0;
+					for(i=strlen(curr_sym->name)-1; i>0 && isdigit(curr_sym->name[i]); i--);
+					if(curr_sym->name[0] == '$') desc1[0] = '$';
+					if(curr_sym->name[i] == '$') desc2[0] = '$';
+					pos += sprintf(res+pos, "%s%s%s%d", desc1, 
+						Int2ColLabel(desc1[0] ? curr_sym->col : curr_sym->col+dx, false),
+						desc2, desc2[0]? curr_sym->row+1 : curr_sym->row+1+dy);
+					}
+				else pos += sprintf(res+pos, "%s ", curr_sym->name);
+				break;
+			case STR:
+				pos += sprintf(res+pos, "\"%s\"", yylval.text && yylval.text[0] ? yylval.text : "");
+				break;
+			case PI:
+				pos += sprintf(res+pos, "pi ");
+				break;
+			case E:
+				pos += sprintf(res+pos, "e ");
+				break;
+			case AND:
+				pos += sprintf(res+pos, " && ");
+				break;
+			case OR:
+				pos += sprintf(res+pos, " || ");
+				break;
+			case EQ:
+				pos += sprintf(res+pos, " == ");
+				break;
+			case NE:
+				pos += sprintf(res+pos, " != ");
+				break;
+			case GT:
+				pos += sprintf(res+pos, " > ");
+				break;
+			case GE:
+				pos += sprintf(res+pos, " >= ");
+				break;
+			case LT:
+				pos += sprintf(res+pos, " < ");
+				break;
+			case LE:
+				pos += sprintf(res+pos, " <= ");
+				break;
+			}
+		}while(buff_pos < length);
+	while((res[pos-1] == ';' || res[pos-1] == ' ') && pos > 0) { res[pos-1] = 0; pos--;} 
+	strcpy(nf, res);	free(res);
+	free(buffer);		buffer = 0L;
+	clear_table();
+	pop_parser();
+	return true;
+}
+
+static char *txt_formula;	//function to fit
+static double **parval;		//pointers to parameter values
+static void fcurve(double x, double z, double **a, double *y, double dyda[], int ma)
+{
+	int i, length;
+	double tmp, y1, y2;
+	symrec *symx, *s=0L;
+	unsigned int hn_x = HashValue((unsigned char *)"x");
+	unsigned int hn_y = HashValue((unsigned char *)"y");
+
+	if(!(symx = getsym(hn_x))) symx = putsym(hn_x, VAR, 0);
+	//swap parameters to requested set
+	if(a != parval) for(i = 0; i < ma; i++) {
+		tmp = *parval[i];	*parval[i]  = *a[i];	*a[i] = tmp;
+		}
+	//calc result
+	symx->value.var = x;	buffer = txt_formula;
+	buff_pos = 0;		length = strlen(txt_formula);
+	do {	yyparse();	}while(buff_pos < length);
+	if(s = getsym(hn_y)) *y = s->value.var;
+	else *y = line_result;
+	if(*y == HUGE_VAL || *y == -HUGE_VAL) {
+		for(i = 0, *y = 0.0; i < ma; dyda[i++] = 0.0);
+		return;
+		}
+	//partial derivatives for each parameter by numerical differentiation
+	for(i = 0; i < ma; i++) {
+		if(*parval[i] != 0.0) {
+			tmp = *parval[i];
+			*parval[i] = tmp*.995;
+			buff_pos = 0;
+			do {	yyparse();	}while(buff_pos < length);
+			y1 = s ? s->value.var : line_result;
+			*parval[i] = tmp*1.005;
+			buff_pos = 0;
+			do {	yyparse();	}while(buff_pos < length);
+			y2 = s ? s->value.var : line_result;
+			*parval[i] = tmp;
+			dyda[i] = (y2-y1)*100.0/tmp;
+			}
+		else dyda[i] = 0.0;
+		}
+	//swap parameters back to original
+	if(a != parval) for(i = 0; i < ma; i++) {
+		tmp = *parval[i];	*parval[i]  = *a[i];	*a[i] = tmp;
+		}
+}
+
+int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr, double conv, int maxiter, double *chi_2)
+{
+	int length, i, j, k, l, ndata, nparam, r1, r2, r3, c1, c2, c3, *lista, itst, itst1;
+	symrec *tab1, *tab2, *csr, **parsym;
+	AccRange *arx, *ary, *arz;
+	double *x, *y, *z, currx, curry, currz, alamda, chisq, ochisq;
+	double **covar, **alpha;
+	char tmp_txt[500];
+
+	if(d) curr_data = d;
+	if(chi_2) *chi_2 = 0.0;
+	txt_formula = expr;
+	if(!curr_data || !par || !expr || !rx || !ry) return 0;
+	//process ranges and create arrays
+	arx = ary = arz = 0L;	x = y = z = 0L;	parval = 0L;	parsym = 0L;
+	if(!(arx = new AccRange(rx)))return 0;
+	i = arx->CountItems()+1;
+	if(!(ary = new AccRange(ry))){
+		delete arx;	return 0;
+		}
+	if(rz && !(arz = new AccRange(rz))){
+		delete ary;	delete arx;	return 0;
+		}
+	if(!(x = (double*)malloc(i * sizeof(double)))){
+		if(arz) delete arz;
+		delete ary;	delete arx;	return 0;
+		}
+	if(!(y = (double*)malloc(i * sizeof(double)))){
+		if(arz) delete arz;
+		free(x);	delete arx;	delete ary;	return 0;
+		}
+	if(rz && !(y = (double*)malloc(i * sizeof(double)))){
+		if(arz) delete arz;
+		free(y);	free(x);	delete arx;	delete ary;	return 0;
+		}
+	arx->GetFirst(&c1, &r1);	ary->GetFirst(&c2, &r2);
+	if(rz) arz->GetFirst(&c3, &r3);
+	for(ndata = j = 0; j < i; j++) {
+		if(rz) {
+			if(arx->GetNext(&c1, &r1) && ary->GetNext(&c2, & r2) && arz->GetNext(&c3, &r3) &&
+				curr_data->GetValue(r1, c1, &currx) && curr_data->GetValue(r2, c2, &curry) &&
+				curr_data->GetValue(r3, c3, &currz)) {
+				x[ndata] = currx;	y[ndata] = curry;	z[ndata] = currz;	ndata++;
+				}
+			}
+		else {
+			if(arx->GetNext(&c1, &r1) && ary->GetNext(&c2, & r2) &&
+				curr_data->GetValue(r1, c1, &currx) && curr_data->GetValue(r2, c2, &curry)) {
+				x[ndata] = currx;	y[ndata] = curry;	ndata++;
+				}
+			}
+		}
+	//common initialization for parser tasks
+	push_parser();		//make code reentrant
+	init_table();		length = strlen(*par);
+	//process parameters
+	if(!(buffer = (char*)malloc(length+2))){
+		clear_table();	pop_parser();
+		if(arz) delete arz;
+		free(y);	free(x);	delete arx;	delete ary;
+		return 0;
+		}
+	strcpy(buffer, *par);	buffer[length++] = ';';
+	buffer[length] = 0;	buff_pos = 0;
+	tab1 = sym_table;
+	do {
+		yyparse();
+		}while(buff_pos < length);
+	tab2 = sym_table;	free(buffer);	buffer =0L;
+	for(nparam = 0, csr=tab2; csr != tab1; nparam++, csr = csr->next);
+	parsym = (symrec**)malloc((nparam+1)*sizeof(symrec*));
+	parval = (double**)malloc((nparam+1)*sizeof(double*));
+	for(i = 0, csr=tab2; csr != tab1 && i < nparam; i++, csr = csr->next){
+		parsym[i] = csr;	parval[i] = &csr->value.var;
+		}
+	//do iteratations to optimize fit
+	lista = (int*)malloc(sizeof(int)*nparam);
+	for(i = 0; i< nparam; i++) lista[i] = i;
+	covar = dmatrix(1, nparam, 1, nparam);
+	alpha = dmatrix(1, nparam, 1, nparam);
+	alamda = -1.0;		itst = 0;
+	mrqmin(x, y, z, ndata, parval, nparam, lista, nparam, covar, alpha, &chisq, fcurve, &alamda);
+	if(!Check_MRQerror()) {
+		for(itst = itst1 = 0, ochisq = chisq; itst < maxiter && chisq > conv && ochisq >= chisq && itst1 < 9; itst++) {
+			ochisq = chisq;
+			mrqmin(x, y, z, ndata, parval, nparam, lista, nparam, covar, alpha, &chisq, fcurve, &alamda);
+			if(ochisq == chisq) itst1++;
+			else itst1 = 0;
+			}
+		alamda = 0.0;
+		mrqmin(x, y, z, ndata, parval, nparam, lista, nparam, covar, alpha, &chisq, fcurve, &alamda);
+		Check_MRQerror();
+		}
+	for(i = nparam-1, j = k = l = 0; i >= 0; l = 0, i--) {
+		if(k > 20) {
+			if(tmp_txt[j-1] == ' ') j--;
+			if(tmp_txt[j-1] == ';') j--;
+			l = sprintf(tmp_txt+j, "\n");
+			j += l;		k = 0;
+			}
+		l += sprintf(tmp_txt+j, "%s%s=%g;", j && k ? " " : "", parsym[i]->name, parsym[i]->value.var);
+		j += l;			k += l;
+		}
+	free(*par);	*par = strdup(tmp_txt);
+	if(chi_2) *chi_2 = chisq;
+	//write back spreadsheet data if necessary
+	buffer = *par;	length = strlen(buffer);
+	do {
+		yyparse();
+		}while(buff_pos < length);
+	buffer = 0L;
+	free_dmatrix(alpha, 1, nparam, 1, nparam);
+	free_dmatrix(covar, 1, nparam, 1, nparam);
+	if(arz) delete arz;		if(z) free(z);
+	free(y);	free(x);	delete arx;	delete ary;
+	if(parval) free(parval);	if(parsym) free(parsym);
+	clear_table();
+	pop_parser();
+	if(curr_data){
+		curr_data->Command(CMD_CLEAR_ERROR, 0L, 0L);
+		curr_data->Command(CMD_REDRAW, 0L, 0L);
+		}
+	return itst < maxiter ? itst+1 : maxiter;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/resource.h b/resource.h
new file mode 100755
index 0000000..2e03463
--- /dev/null
+++ b/resource.h
@@ -0,0 +1,86 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Developer Studio generated include file.
+// Used by RLPLOT.RC
+//
+#define ACCELERATORS_1                  1
+#define IDI_EVAL                        50
+#define MENU_1                          400
+#define MENU_2                          401
+#define MENU_3                          402
+#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_REBOOT                       520
+#define CM_SHUTDOWN                     521
+#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_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
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NO_MFC                     1
+#define _APS_NEXT_RESOURCE_VALUE        103
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1000
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/rlp_math.cpp b/rlp_math.cpp
new file mode 100755
index 0000000..9420d89
--- /dev/null
+++ b/rlp_math.cpp
@@ -0,0 +1,411 @@
+//rlp_math.cpp, Copyright (c) 2004, 2005 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>
+
+#define SWAP(a,b) {double temp=(a);(a)=(b);(b)=temp;}
+static char *MRQ_error = 0L;
+
+//---------------------------------------------------------------------------
+//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. 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 SortArray(int n, double *vals)
+{
+	int l, j, ir, i;
+	double rra, *ra = vals-1;
+
+	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;
+		}
+}
+//---------------------------------------------------------------------------
+// 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 Gamma Function: return the ln(G(xx)) for xx > 0
+double gammln(double xx)
+{
+	double x, tmp, ser, pi=3.14159265358979;
+	static double cof[6] = {76.18009173, -86.50532033, 24.01409822,
+		-1.231739516, 0.120858003e-2, -0.536382e-5};
+	int j;
+	
+	if(xx < 0) return 0.0;
+	if(xx < 1.0)			//reflect
+		return log((pi*(1.0-xx))/(exp(gammln(2.0-xx))*sin(pi*(1.0-xx))));
+	x = xx-1;		tmp = x + 5.5;		tmp -= (x + 0.5)*log(tmp);
+	for (j = 0, ser = 1.0; j <= 5; j++) {
+		x += 1.0;	ser += cof[j]/x;
+		}
+	return -tmp + log(2.50662827465*ser);
+}
+
+//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];
+}
+
+//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) <= (1.0e-12 * 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;
+}
+
+double t_dist(double t, double df)
+{
+	return betai(df/2.0, 0.5, (df/(df+t*t)));
+}
+
+double f_dist(double f, double df1, double df2)
+{
+	return betai(df2/2.0, df1/2.0, df2/(df2+df1*f));
+}
+
+//---------------------------------------------------------------------------
+//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;
+		}
+}
+
+//do arithmethic mean
+double d_amean(int n, double *v)
+{
+	int i;
+	double sum;
+
+	for(i = 0, sum = 0.0; i < n; i++) {
+		sum += (v[i]);
+		}
+	return (sum/n);
+}
+
+
+//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);
+}
diff --git a/rlplot.1 b/rlplot.1
new file mode 100755
index 0000000..7d85027
--- /dev/null
+++ b/rlplot.1
@@ -0,0 +1,105 @@
+.\"                                      Hey, EMACS: -*- nroff -*-
+.\" First parameter, NAME, should be all caps
+.\" Second parameter, SECTION, should be 1-8, maybe w/ subsection
+.\" other parameters are allowed: see man(7), man(1)
+.TH RLPLOT 1
+.\" Please adjust this date whenever revising the manpage.
+.\"
+.\" Some roff macros, for reference:
+.\" .nh        disable hyphenation
+.\" .hy        enable hyphenation
+.\" .ad l      left justify
+.\" .ad b      justify to both left and right margins
+.\" .nf        disable filling
+.\" .fi        enable filling
+.\" .br        insert line break
+.\" .sp <n>    insert n+1 empty lines
+.\" for manpage-specific macros, see man(7)
+.SH NAME
+rlplot \- generate publication quality graphs
+.br
+exprlp \- convert rlplot files to vector based graphic files
+.SH SYNOPSIS
+.B rlplot
+.I <file>
+.br
+.B exprlp
+.RI [options] <input> [options] [<output>] 
+.SH DESCRIPTION
+This manual page documents briefly the
+.B rlplot
+and
+.B exprlp 
+commands.
+.PP
+.B rlplot 
+is a GUI based program for displaying scientific data in standard 
+formats.  Output is generated on the X display where further 
+changes can be made to the graph using point and click methods. 
+Images can be exported as scalable vector graphics (SVG) as well as 
+EPS, WMF and TIFF formats.
+.PP
+.B exprlp
+reads RLPlot files and exports various vector based graphic file 
+formats including scaleable vector graphics (SVG), Encapsulated 
+PostScript (EPS), and Windows Metafile (WMF).
+.SH OPTIONS
+.B rlplot
+does not have any command line options but the following options are
+available for 
+.B exprlp
+.
+.TP
+.B \- 
+use stdin/stdout as input or output file; requires that file format is set by -e | -s | -w option
+.TP
+.B \-h
+help
+.TP
+.B \-d
+delete input file after read
+.TP
+.B \-e
+output Encapsulated PostScript, *.eps
+.TP
+.B \-s 
+output Scalable Vector Graphics, *.svg
+.TP
+.B \-S
+like -s, start output with "Content-Type: image/svg+xml"
+.TP
+.B \-v
+print RLPlot version
+.TP
+.B \-w
+output Windows Meta File, *.wmf
+.TP
+.B \-q
+quiet mode: suppress output to the console
+.SH EXAMPLES
+.B exprlp
+foo.rlp foo.svg ;exports Scalable Vector Graphics
+.TP
+.B exprlp
+\-q foo.rlp foo.eps ;exports Encapsulated PostScript, no messages
+.TP
+.B exprlp
+foo.rlp foo.wmf ;exports Windows Meta File
+.TP
+.B exprlp
+\-sq foo.rlp \- ;exports SVG to the console, no messages
+.TP
+.B exprlp
+exprlp \-eq \- \- ;converts inputfile from stdin to EPS on stdout
+.TP
+switch character is either '\-' or '\//'
+
+.SH AUTHOR
+.B rlplot
+and
+.B exprlp
+were written by Reinhard Lackner and are released under the GNU general
+public license.
+.sp
+This manual page was written by James Stone <jmstone at dsl.pipex.com>.
+
diff --git a/rlplot.cpp b/rlplot.cpp
new file mode 100755
index 0000000..0de7a6b
--- /dev/null
+++ b/rlplot.cpp
@@ -0,0 +1,9618 @@
+//RLPlot.cpp, Copyright 2000, 2001, 2002, 2003, 2004, 2005 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;
+dragHandle *CurrHandle = 0L;
+Axis **CurrAxes = 0L;
+UndoObj Undo;
+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)
+{
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Spread sheet buttons used for rows and columns
+ssButton::ssButton(GraphObj *par, int x, int y, int w, int h):GraphObj(par, 0L)
+{
+	bLBdown = 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 = 0x00d8d8d8L;
+	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;
+	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) {
+			strcpy((char*)tmpl, TextDef.text);
+			return true;
+			}
+		return false;
+	case CMD_SETTEXT:
+		if(TextDef.text) free(TextDef.text);
+		if(tmpl) TextDef.text = strdup((char*)tmpl);
+		else TextDef.text = 0L;
+		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;
+	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) {
+			o->SetLine(&LineDef);		o->SetFill(&FillDef);
+			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];
+	double dx, dy;
+	int npts=0, idx;
+	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);
+		npts = 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[2].x = o->co2ix(parent->GetSize(SIZE_XPOS + idx +1)+dx);
+			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);
+			npts = 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;
+		npts = 5;
+		if(parent->parent->Id == GO_ELLIPSE) o->ShowEllipse(p1, p2, color);
+		}
+	o->ShowLine(pts, npts, 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){
+		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(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);
+		}
+
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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 parent ? parent->GetSize(select) : defs.GetSize(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, atype;
+	lfPOINT fip;
+	POINT pts[5];
+	FillDEF cf;
+
+	if(size <= 0.001) return;
+	atype = (type  & 0xfff);
+	memcpy(&cf, &SymFill, sizeof(FillDEF));
+	if(atype == SYM_CIRCLEF || atype == SYM_RECTF || atype == SYM_TRIAUF ||
+		atype == SYM_TRIADF || atype == SYM_DIAMONDF) 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
+		rx = target->un2ix(size/2.0);		ry = target->un2iy(size/2.0); 
+		target->SetFill(&cf);
+		target->oCircle(ix-rx, iy-ry, ix+rx+1, iy+ry+1, name);
+		rx--;ry--;			//smaller marking rectangle
+		break;
+	case SYM_RECT:			//rectange (square)
+	case SYM_RECTF:			//filled rectangle
+		rx = target->un2ix(size/2.25676);
+		ry = target->un2iy(size/2.25676);
+		target->SetFill(&cf);
+		target->oRectangle(ix-rx, iy-ry, ix+rx+1, iy+ry+1, name);
+		break;
+	case SYM_TRIAU:			//triangles up and down, open or closed
+	case SYM_TRIAUF:
+	case SYM_TRIAD:
+	case SYM_TRIADF:
+		rx = target->un2ix(size/1.48503);
+		ry = target->un2iy(size/1.48503);
+		target->SetFill(&cf);
+		pts[0].x = pts[3].x = ix - rx;		pts[1].x = ix;		pts[2].x = ix+rx;
+		if(type == SYM_TRIAU || type == SYM_TRIAUF) {
+			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);
+		rx--; ry--;
+		break;
+	case SYM_DIAMOND:
+	case SYM_DIAMONDF:
+		rx = target->un2ix(size/1.59588f);
+		ry = target->un2iy(size/1.59588f);
+		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);
+		rx--;									ry--;
+		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);
+		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(type != SYM_VLINE) target->oPolyline(pts, 2);
+		if(type == SYM_VLINE){ rx = 2; break;}
+		if(type == SYM_HLINE){ ry = 2; break;}
+		if(type == 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);
+		pts[0].x = ix - rx;					pts[1].x = ix + rx;
+		pts[0].y = iy - ry;					pts[1].y = iy + ry;
+		target->oPolyline(pts, 2);
+		Swap(pts[0].y, pts[1].y);
+		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);
+		fmtText(target, ix, iy, SymTxt->text);
+		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+1;
+	rDims.top = iy-ry-1;				rDims.bottom = iy+ry+1;
+}
+
+bool 
+Symbol::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	char *tmptxt;
+	AccRange *ac;
+	int i, r, c;
+
+	switch (cmd) {
+	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) {
+			strcpy((char*)tmpl, SymTxt->text);
+			return true;
+			}
+		return false;
+	case CMD_SYMTEXT_UNDO:
+		if(SymTxt && SymTxt->text){
+			Undo.String(this, &SymTxt->text, UNDO_CONTINUE);
+			if(tmpl) {
+				if(SymTxt->text = (char*)realloc(SymTxt->text, strlen((char*)tmpl))+2)
+					strcpy(SymTxt->text, (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) {
+			if(SymTxt->text = (char*)realloc(SymTxt->text, strlen((char*)tmpl)+1))
+				strcpy(SymTxt->text, (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; 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_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:
+		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			((Plot*)parent)->CheckBounds(fPos.fx, fPos.fy);
+			return true;
+			}
+		break;
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Bars are graphic objects
+Bar::Bar(GraphObj *par, DataObj *d, double x, double y, int which,int xc, int xr,
+		int yc, int yr):GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	parent = par;
+	fPos.fx = x;
+	fPos.fy = y;
+	type = which;
+	if(type & BAR_RELWIDTH) size = 60.0;
+	data = d;
+	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;
+			}
+		}
+}
+
+Bar::Bar(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+Bar::~Bar()
+{
+	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);
+}
+
+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_LEGEND:
+		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		((Legend*)tmpl)->HasFill(&BarLine, &BarFill);
+		break;
+	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;
+			}
+		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):GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	Id = GO_DATALINE;
+	if(xrange)ssXref = strdup(xrange);	if(yrange)ssYref = strdup(yrange);
+	SetValues();
+}
+	
+DataLine::DataLine(GraphObj *par, DataObj *d, lfPOINT *val, long nval):GraphObj(par, d)
+{
+	FileIO(INIT_VARS);
+	Values = val;			nPnt = nval;	nPntSet = nPnt-1;
+	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(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(mo) DelBitmapClass(mo);		mo = 0L;
+	if(pts) free(pts);
+	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 & 0xf) {
+	case 0:
+		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;
+		}
+	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){
+			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 <2)
+				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_SET_DATAOBJ:
+		Id = isPolygon ? GO_DATAPOLYGON : GO_DATALINE;
+		data = (DataObj*)tmpl;
+		return true;
+	case CMD_LEGEND:
+		if(tmpl && ((GraphObj*)tmpl)->Id == GO_LEGEND) {
+			if(Id == GO_DATALINE) ((Legend*)tmpl)->HasFill(&LineDef, 0L);
+			}
+		break;
+	case CMD_SET_LINE:
+		if(tmpl) memcpy(&LineDef, tmpl, sizeof(LineDEF));
+		return true;
+	case CMD_UPDATE:
+		SetValues();
+		return true;
+	case CMD_AUTOSCALE:
+		if(nPntSet < 2 || !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 = 0L, *yref2 = 0L;
+	lfPOINT *tmpValues = 0L;
+
+	if(!ssXref || !ssYref) return;
+	if(!(yref1 = strdup(ssYref)))return;
+	for(i = 0; yref1[i]; i++) {
+		if(yref1[i] == ';') {
+			yref1[i++] = 0;
+			while(yref1[i] && yref1[i] < 33) i++;
+			yref2 = strdup(yref1+i);
+			}
+		}
+	nPnt = nPntSet = 0;
+	if(Values) free(Values);		Values = 0L;
+	min.fx = min.fy = HUGE_VAL;		max.fx = max.fy = -HUGE_VAL;
+	rX = new AccRange(ssXref);		rY1 = new AccRange(yref1);
+	if(!rX || !rY1){
+		if(yref1) free(yref1);		if(yref2) free(yref2);
+		if(rX) delete(rX);	if(rY1) delete(rY1);
+		return;
+		}
+	if(yref2 &&((nPnt = rX->CountItems()) == (rY1->CountItems()))) {
+		if(!(Values = (lfPOINT *)calloc(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 *)calloc(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 = (lfPOINT *)realloc(Values, (nPnt+2)*sizeof(lfPOINT)))
+		Values = tmpValues;
+	if(rX) delete(rX);	if(rY1) delete(rY1);	if(rY2) delete(rY2);
+	if(yref1) free(yref1);	if(yref2) free(yref2);
+}
+
+void
+DataLine::LineData(lfPOINT *val, long nval)
+{
+	if(Values)free(Values);		Values = 0L;
+	if(pts) free(pts);			pts = 0L;
+	Values = val;				dirty = true;			
+	nPnt = nval;				nPntSet = nPnt-1;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// DataPolygon is a graphic object based on DataLine
+DataPolygon::DataPolygon(GraphObj *par, DataObj *d, char *xrange, char *yrange):
+	DataLine(par, d, xrange, yrange)
+{
+	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(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){
+			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_LEGEND:
+		if(tmpl && ((GraphObj*)tmpl)->Id == GO_LEGEND) {
+			if(Id == GO_DATAPOLYGON) ((Legend*)tmpl)->HasFill(&LineDef, &pgFill);
+			}
+		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, *tmppts;
+	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) free(pts);
+	if(!(pts = (POINT *)malloc(sizeof(POINT)*200)))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
+			}
+		y = a + b*x1;
+		if(dValid) 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(dValid && 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);
+	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*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_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;
+	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);
+	dx = sd2/100.0;
+	for(i = 0, cp = 0, x = -sd2; i < 2; i++) {
+		do {
+			fv = (x*x)/ss2;
+			fv = fv < 0.99999 ? sqrt((1.0-fv)*ss1) : 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 && x > -sd2);
+		x = sd2;
+		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_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(ssRef) free(ssRef);
+	ssRef = 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.0f);
+		break;
+	default:
+		ie = target->un2iy(SizeBar/2.0f);
+		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, 3);
+}
+
+void
+ErrorBar::DoMark(anyOutput *o, bool mark)
+{
+	LineDEF OldLine;
+
+	memcpy(&OldLine, &ErrLine, sizeof(LineDEF));
+	if(mark) {
+		ErrLine.color = 0x00000000L;
+		ErrLine.width = 1.2f;
+		DoPlot(o);
+		ErrLine.width = 0.4f;
+		ErrLine.color = OldLine.color ^ 0x00ffffffL;
+		DoPlot(o);
+		}
+	else {
+		ErrLine.color = 0x00ffffffL;		//DEBUG: assume white background
+		ErrLine.width = 1.2f;
+		DoPlot(o);
+		ErrLine.width = OldLine.width;
+		ErrLine.color = OldLine.color;
+		DoPlot(o);
+		}
+	memcpy(&ErrLine, &OldLine, sizeof(LineDEF));
+	o->UpdateRect(&rDims, false);
+}
+
+bool
+ErrorBar::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	MouseEvent *mev;
+	bool bFound;
+
+	switch (cmd) {
+	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);
+			}
+	case CMD_SET_DATAOBJ:
+		Id = GO_ERRBAR;
+		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, &ferr);
+			return true;
+			}
+		return false;
+	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);
+	if(this == CurrGO) o->ShowMark(this, MRK_GODRAW);
+	else 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;
+		}
+	memcpy(&OldLine, &LineDef, sizeof(LineDEF));
+	if(mark) {
+		LineDef.color = 0x00000000L;
+		LineDef.width = OldLine.width *3.0;
+		Redraw(o);
+		LineDef.width = OldLine.width;
+		LineDef.color = OldLine.color ^ 0x00ffffffL;
+		Redraw(o);
+		if(dh1) dh1->DoPlot(o);		if(dh2) dh2->DoPlot(o);
+		}
+	else if(parent){
+		LineDef.color = 0x00ffffffL;
+		LineDef.width = OldLine.width *3.0;
+		Redraw(o);
+		LineDef.width = OldLine.width;
+		LineDef.color = OldLine.color;
+		parent->DoPlot(o);
+		}
+	memcpy(&LineDef, &OldLine, sizeof(LineDEF));
+	o->UpdateRect(&rDims, false);
+}
+
+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_FLUSH:
+		if (dh1) DeleteGO(dh1);			dh1 = 0L;
+		if (dh2) DeleteGO(dh2);			dh2 = 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::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
+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;
+		}
+	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) 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_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);
+		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(&rDims, MRK_INVERT);
+					CurrGO = this;
+					return true;
+					}
+				else {
+					p.x = mev->x;		p.y = mev->y;
+					if(IsInPolygon(&p, pts, 5)) {
+						o->ShowMark(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) {
+			((Plot*)parent)->CheckBounds(pos1.fx, pos1.fy);
+			((Plot*)parent)->CheckBounds(pos2.fx, 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;
+			}
+		}
+}
+
+Whisker::Whisker(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+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)
+{
+	LineDEF OldLine;
+	int i;
+
+	memcpy(&OldLine, &LineDef, sizeof(LineDEF));
+	if(mark) {
+		LineDef.color = 0x00000000L;
+		LineDef.width = 1.2f;
+		o->SetLine(&LineDef);
+		for(i = 0; i < 5; i+=2) o->oSolidLine(pts+i);
+		LineDef.width = 0.4f;
+		LineDef.color = OldLine.color ^ 0x00ffffffL;
+		o->SetLine(&LineDef);
+		for(i = 0; i < 5; i+=2) o->oSolidLine(pts+i);
+		}
+	else {
+		LineDef.color = 0x00ffffffL;		//DEBUG: assume white background
+		LineDef.width = 1.2f;
+		o->SetLine(&LineDef);
+		for(i = 0; i < 5; i+=2) o->oSolidLine(pts+i);
+		LineDef.width = OldLine.width;
+		LineDef.color = OldLine.color;
+		o->SetLine(&LineDef);
+		for(i = 0; i < 5; i+=2) o->oSolidLine(pts+i);
+		}
+	memcpy(&LineDef, &OldLine, sizeof(LineDEF));
+	o->UpdateRect(&rDims, false);
+}
+
+bool
+Whisker::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[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_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 defs.GetSize(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:
+		rx = o->un2ix(size/2.0);	ry = o->un2iy(size/2.0);
+		type = 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_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) {
+		//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 && data && nldata){
+			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(co->parent == parent && co->Id == GO_PLANE) {
+		//if both planes have the same parent it means they are part of one object
+		// do not clip!
+		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;
+	o->ActualSize(&rDims);
+	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_LEGEND:
+		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		((Legend*)tmpl)->HasFill(&Line, &Fill);
+		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){
+		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_LEGEND:
+		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		((Legend*)tmpl)->HasFill(&Line, &Fill);
+		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) {
+		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_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_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 = strdup(rx);	if(ry && ry[0]) y_range = strdup(ry);
+	if(rz && rz[0]) z_range = strdup(rz);
+	DoUpdate();
+	Id = GO_LINE3D;
+	bModified = false;
+}
+
+Line3D::Line3D(GraphObj *par, DataObj *d, fPOINT3D *pt, int n_pt)
+	: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*));
+		}
+	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;
+	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(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) {
+		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_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:
+		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)
+{
+	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) TextDef.text = strdup(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()
+{
+	Command(CMD_FLUSH, 0L, 0L);
+	if(fmt_txt) delete(fmt_txt);			fmt_txt = 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(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 {
+		HideTextCursor();
+		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;
+	char *tmptxt;
+
+	if(cmd != CMD_SET_DATAOBJ && !parent) return false;
+	switch (cmd) {
+	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) ? strlen(TextDef.text) : 0;
+			ShowCursor(o);
+			return true;
+			}
+		return false;
+	case CMD_CURRLEFT:
+		if(o && CursorPos >0 && TextDef.text && fmt_txt) {
+			Undo.ValInt(this, &CursorPos, 0L);
+			bModified = true;		fmt_txt->cur_left(&CursorPos);			ShowCursor(o);
+			return true;
+			}
+		return false;
+	case CMD_CURRIGHT:
+		if(o && TextDef.text && CursorPos < (int)strlen(TextDef.text) && fmt_txt) {
+			Undo.ValInt(this, &CursorPos, 0L);
+			bModified = true;		fmt_txt->cur_right(&CursorPos);			ShowCursor(o);
+			return true;
+			}
+		return false;
+	case CMD_ADDCHAR:
+		SetModified();
+		if(tmpl && 8 != *((int*)tmpl)) return AddChar(*((int*)tmpl), o);
+		//value 8 == backspace
+	case CMD_BACKSP:
+		SetModified();
+		if(CursorPos <=0 && o) {
+			if(parent->Id == GO_MLABEL) {
+				parent->Command(CMD_SETFOCUS, this, o);
+				return parent->Command(CMD_BACKSP, tmpl, o);
+				}
+			RedrawEdit(o);
+			return true;
+			}
+		Undo.ValInt(this, &CursorPos, 0L);
+		CursorPos--;							//continue as if delete
+	case CMD_DELETE:
+		SetModified();
+		if(cmd == CMD_DELETE) Undo.ValInt(this, &CursorPos, 0L);
+		if(TextDef.text && TextDef.text[CursorPos]) {
+			Undo.String(this, &TextDef.text, UNDO_CONTINUE);
+			strcpy(TextDef.text + CursorPos, TextDef.text + CursorPos + 1);
+			if(o)RedrawEdit(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 && tmpl) {
+			strcpy((char*)tmpl, TextDef.text);
+			return true;
+			}
+		return false;
+	case CMD_SETTEXT:
+		if(TextDef.text) free(TextDef.text);
+		if(tmpl) TextDef.text = strdup((char*)tmpl);
+		else TextDef.text = 0L;
+		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));
+		if(!TextDef.text){
+			TextDef.text = tmptxt;
+			tmptxt = 0L;
+			}
+		else if(((TextDEF*)tmpl)->text) TextDef.text = strdup(((TextDEF*)tmpl)->text);
+		if(tmptxt) free(tmptxt);
+		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)) {
+				if(TextDef.text) free(TextDef.text);
+				TextDef.text = strdup(TmpTxt);
+				}
+			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_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);
+				o->ShowMark(this, MRK_GODRAW);
+				return true;
+				}
+			break;
+			}
+		break;
+	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(fmt_txt) delete(fmt_txt);	fmt_txt = 0L;
+		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;
+	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);
+	if(fmt_txt && TextDef.text && TextDef.text[0]) {
+		fmt_txt->SetText(0L, TextDef.text, &ix, &iy);
+		if(!(fmt_txt->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(fmt_txt && fmt_txt->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::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];
+
+	if(!o) return false;
+	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(ci < 254 && ci > 31) c = (char)ci;
+	else return false;
+	if(!TextDef.text || CursorPos < 10 || c == ' ') {
+		Undo.String(this, &TextDef.text, 0L);
+		Undo.ValInt(this, &CursorPos, UNDO_CONTINUE);
+		}
+	if(TextDef.text) txt1 = (char*)calloc((i = strlen(TextDef.text))+2, sizeof(char));
+	else txt1 = (char*)calloc((i = 0)+2, sizeof(char));
+	if(txt1) {
+		for(j = k = 0; j< i && j < CursorPos; txt1[k++] = TextDef.text[j++]);
+		txt1[k++] = c;
+		for(; j< i; txt1[k++] = TextDef.text[j++]);
+		if(TextDef.text) free(TextDef.text);
+		TextDef.text = txt1;
+		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++) {
+		o->oGetTextExtent(TextDef.text, i, &x2, &h);
+		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 = strlen(TextDef.text);
+}
+
+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)
+{
+	if(!parent || !o) return;
+	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]){
+		if(fmt_txt) fmt_txt->SetText(o, TextDef.text, &ix, &iy);
+		else fmt_txt = new fmtText(o, ix, iy, TextDef.text);
+		}
+	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);
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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;
+	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;
+	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;
+		}
+	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;
+		}
+	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));
+	dist.fx = floor(o->un2fix(TextDef.fSize * si * 1.1));
+	dist.fy = floor(o->un2fiy(TextDef.fSize * csi * 1.1));
+	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;
+
+	switch (cmd) {
+	case CMD_MOUSE_EVENT:
+		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_HIDE_MARK:
+		if(Lines && o && tmpl) for(i = 0; i< nLines; i++) 
+			if(Lines[i] && tmpl == (void*)Lines[i]){
+				Lines[i]->DoMark(o, false);
+				return true;
+				}
+		return false;
+	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:
+		if(tmpl && Lines && nLines){
+			for(i = 0, j = 500; i < nLines; i++) {
+				if(Lines[i] && Lines[i]->Command(CMD_GETTEXT, TmpTxt, o) && TmpTxt[0]){
+					j += sprintf(TmpTxt+j, "%s\n", TmpTxt);
+					}
+				}
+			strcpy((char*)tmpl, TmpTxt+500);
+			return true;
+			}
+		break;
+	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;
+		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;
+		if(Lines) for(i = 0; i< nLines; i++) 
+			if(Lines[i])Lines[i]->Command(cmd, tmpl, o);
+		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_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
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// The following GOs use absolute coordinates
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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_LEGEND:
+		if(tmpl) {
+			leg = new LegItem(this, data, 0L, &segLine, &segFill);
+			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(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);
+	o->SetLine(&pgLine);
+	if(this == CurrGO) o->ShowMark(this, MRK_GODRAW);
+	else switch(type) {
+	case 0:				//line
+		o->oPolyline(pts, nPts);
+		break;
+	case 1:				//polygon
+		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);;
+ 		}
+	else if(parent)	parent->DoPlot(o);
+	o->UpdateRect(&upd, false);
+}
+
+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_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;
+			o->ShowMark(CurrGO=this, MRK_GODRAW);
+			break;
+			}
+		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;
+
+	if(nPoints >= 200) return;
+	if(!pHandles) if(pHandles = (dragHandle**)calloc(nPoints+1, sizeof(dragHandle*))){
+		for(i = 0; i < nPoints; i++) pHandles[i] = new dragHandle(this, DH_DATA+i);
+		}
+	else return;
+	for(i = 0; i < nPoints; i++) if(pHandles[i]) pHandles[i]->DoPlot(o);
+}
+
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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);
+}
+
+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_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)){
+				o->ShowMark(this, MRK_GODRAW);
+				return true;
+				}
+			}
+		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)
+	: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(0L);		Id = GO_LEGITEM;		moveable = true;
+}
+
+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(0L);		Id = GO_LEGITEM;		moveable = true;
+}
+
+LegItem::LegItem(int src):GraphObj(0L, 0L)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+	moveable = true;
+}
+
+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[2];
+
+	if(!parent || !o) return;
+	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));
+	hcr.top = iround(parent->GetSize(SIZE_YPOS));
+	hcr.bottom = iround(parent->GetSize(SIZE_YPOS+1));
+	SetMinMaxRect(&rDims, hcr.left, hcr.top, hcr.right, hcr.bottom);
+	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);
+		}
+	if(Desc) {
+		Desc->moveable = false;	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_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_HIDE_MARK:
+		if(!tmpl || !o) return false;
+		if(Desc && Desc == (void*)tmpl) {
+			Desc->DoMark(o, false);
+			return true;
+			}
+		return false;
+	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:
+		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)
+{
+	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 && (Sym->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;
+}
+
+void
+LegItem::DefDesc(char *txt)
+{
+	TextDEF td;
+
+	td.ColTxt = 0x00000000L;		td.ColBg = 0x00ffffffL;
+	td.fSize = defs.GetSize(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 = txt ? strdup(txt) : strdup("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.left = rDims.right = rDims.top = rDims.bottom = 0;
+	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]){
+			Items[i]->DoPlot(o);
+			if(rDims.left == rDims.right || rDims.top == rDims.bottom){
+				rDims.left = Items[i]->rDims.left;	rDims.right = Items[i]->rDims.right;
+				rDims.top = Items[i]->rDims.top;	rDims.bottom = Items[i]->rDims.bottom;
+				}
+			else {
+				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_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_HIDE_MARK:
+		if(!tmpl || !o) return false;
+		if(Items && nItems) for(i = 0; i < nItems; i++) {
+			if(Items[i]) {
+				if(tmpl == (void*)Items[i]) {
+					Items[i]->DoMark(o, false);
+					return true;
+					}	
+				else if(Items[i]->Command(cmd, tmpl, o)) return true;
+				}
+			}
+		return false;
+	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)
+{
+	int i;
+	LegItem *li;
+
+	if(Items) for(i = 0; i < nItems; i++) {
+		if(Items[i] && Items[i]->HasFill(ld, fd)) return true;
+		}
+	if(li = new LegItem(this, data, 0L, ld, fd)){
+		if(!(Command(CMD_DROP_OBJECT, li, 0L))) DeleteGO(li);
+		}
+	return false;
+}
+
+bool
+Legend::HasSym(LineDEF *ld, GraphObj *sy)
+{
+	int i, sym;
+	Symbol *ns;
+	LegItem *li;
+
+	if(!parent || !sy) return 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(li = new LegItem(this, data, ld, ns)){
+		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):GraphObj(par, d)
+{
+	Graph::FileIO(INIT_VARS);
+	Disp = o;		Id = GO_GRAPH;		cGraphs++;	bModified = true;
+	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);
+		}
+	if(Axes) {
+		for(i = 0; i< NumAxes; i++) if(Axes[i]) delete Axes[i];
+		free(Axes);
+		}
+	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)
+{
+	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_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);
+	default:		return defs.GetSize(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;
+		}
+	if(parent) return parent->GetColor(select);
+	else return defs.Color(select);
+}
+
+
+void
+Graph::DoPlot(anyOutput *target)
+{
+	int i;
+	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
+	//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){
+		sprintf(TmpTxt, "Graph %d", cGraphs);
+		name = strdup(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);
+		if(GRect.Xmin > 0.0001 || GRect.Xmin < -0.0001) {
+			GRect.Xmax -= GRect.Xmin;	GRect.Xmin = 0.0;
+			}
+		if(GRect.Ymin > 0.0001 || GRect.Ymin < -0.0001) {
+			GRect.Ymax -= GRect.Ymin;	GRect.Ymin = 0.0;
+			}
+		}
+	//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(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 = CurrDisp;
+	switch (cmd){
+	case CMD_CAN_DELETE:
+		HideTextCursor();
+		if(bModified) {
+			sprintf(TmpTxt, "%s has not been saved.\nDo you want to save it now?", name);
+			if(YesNoBox(TmpTxt)) SaveGraphAs(this);
+			}
+		bModified = false;
+		if(OwnDisp && Disp && parent){		//be careful not to reenter
+			OwnDisp = false;			DelDispClass(Disp);			Disp = CurrDisp = 0L;
+			if(Axes) for(i = 0; i< NumAxes; i++) if(Axes[i]) DeleteGO(Axes[i]);
+			if(Plots) for(i = 0; i< NumPlots; i++) if(Plots[i]) DeleteGO(Plots[i]);
+			Axes = 0L;	Plots = 0L;	NumPlots = 0;	NumAxes = 0;
+			if(parent) parent->Command(CMD_DELOBJ, this, 0L);
+			}
+		return true;
+	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:
+		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;
+				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;
+				}
+			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);
+			Command(CMD_REDRAW, 0L, CurrDisp);
+			}
+		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_HIDE_MARK:
+		if(!tmpl || !o) return false;
+		//do frame rectangles
+		if(frm_g && tmpl == (void*)frm_g) {
+			frm_g->DoMark(o, false);					return true;
+			}
+		if(frm_d && tmpl == (void*)frm_d) {
+			frm_d->DoMark(o, false);					return true;
+			}
+		//do all axes
+		if(Axes)for(i = NumAxes-1; i>=0; i--) {
+			if(tmpl == (void*)Axes[i]){
+				Axes[i]->DoMark(CurrDisp, false);		return true;
+				}
+			else if(Axes[i]->Id == GO_AXIS) {
+				if(Axes[i]->Command(cmd, tmpl, o))		return true;
+				}
+			}
+		//do all plots
+		if(Plots)for(i = NumPlots-1; i>=0; i--) {
+			if(tmpl == (void*)Plots[i]){
+				Plots[i]->DoMark(CurrDisp, false);		return true;
+				}
+			else if(Plots[i] && (Plots[i]->Id == GO_MLABEL || Plots[i]->Id == GO_LEGEND || 
+				(Plots[i]->Id >= GO_PLOT && Plots[i]->Id < GO_GRAPH))) {
+				if(Plots[i]->Command(cmd, tmpl, o))		return true;
+				}
+			}
+		return false;
+	case CMD_REDRAW:
+		if(!CurrDisp) {
+			DoPlot(CurrDisp);
+			return true;
+			}
+		if(parent && parent->Id == GO_PAGE) 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 && CurrGO == CurrLabel && CurrLabel->Id == GO_LABEL)
+			CurrDisp->ShowMark(CurrLabel, MRK_GODRAW);
+		return true;
+	case CMD_ZOOM:
+		return DoZoom((char*)tmpl);
+	case CMD_MOUSECURSOR:
+		if(Disp)Disp->MouseCursor(MC_ARROW, false);	
+		return true;
+	case CMD_AXIS:			//one of the plots has changed scaling: reset
+		if(o)o->SetRect(CurrRect, units, &x_axis, &y_axis);
+		return true;
+	case CMD_REG_AXISPLOT:	//notification: plot can hnadle 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:
+		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) 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);
+		return Configure();
+	case CMD_FILENAME:
+		if(tmpl) {
+			if(filename) free(filename);
+			filename=strdup((char*)tmpl);
+			}
+		break;
+	case CMD_SETNAME:
+		if(OwnDisp && CurrDisp && tmpl){
+			CurrDisp->Caption((char*)tmpl);
+			if(name) free(name);	name = strdup((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);
+			}
+		return true;
+	case CMD_UPDHISTORY:
+		if(data) data->Command(CMD_UPDHISTORY, 0L, 0L);
+		return true;
+	case CMD_UPDATE:
+		Command(CMD_TOOLMODE, 0L, o);
+		if(parent && parent->Id != GO_PAGE){
+			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_DELETE, 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);
+			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) 
+			return parent->Command(CMD_DELOBJ_CONT, (void*)this, o);
+		return false;
+	case CMD_TOOLMODE:
+		if(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:
+		case TM_RECTANGLE:	case TM_ELLIPSE:		case TM_ROUNDREC:
+		case TM_ARROW:
+			o->MouseCursor(MC_CROSS, false);
+			break;
+		default:	o->MouseCursor(MC_ARROW, true);	break;
+			}
+		return Command(CMD_REDRAW, 0L, CurrDisp);
+	case CMD_ADDPLOT:
+		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)
+						return Plots[i]->Command(cmd, tmpl, CurrDisp);
+					}
+				}
+			else return AddPlot(0x0);
+			}
+		return false;
+	case CMD_MRK_DIRTY:
+		return (dirty = true);
+	case CMD_CURRLEFT:	case CMD_CURRIGHT:	case CMD_ADDCHAR:
+	case CMD_BACKSP:	case CMD_POS_FIRST:	case CMD_POS_LAST:
+		defs.SetDisp(o);
+		if(CurrLabel) return CurrLabel->Command(cmd, tmpl, o);
+		if(CurrGO && 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;
+			}
+	case CMD_SHIFTLEFT:	case CMD_SHIFTRIGHT:	case CMD_SHIFTUP:	case CMD_SHIFTDOWN:
+		if(Id == GO_PAGE) {
+			if(CurrGraph && CurrGraph->parent == this) 
+				return CurrGraph->Command(cmd, tmpl, o);
+			}
+		else {
+			if(type == GT_3D && Plots) {
+				for(i = 0; i < NumPlots; i++) {
+					if(Plots[i] && Plots[i]->Id == GO_PLOT3D)
+						return Plots[i]->Command(cmd, tmpl, CurrDisp);
+					}
+				}
+			}
+		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 == CurrLabel) return CurrLabel->Command(cmd, tmpl, o);
+		if(CurrGO->parent)return CurrGO->parent->Command(CMD_DELOBJ, (void*)CurrGO, o);
+		return false;
+	case CMD_DROP_PLOT:
+		if(!tmpl || ((GraphObj*)tmpl)->Id < GO_PLOT) return false;
+		if(Id == GO_GRAPH) CurrGraph = this;	bModified =true;
+		if(!NumPlots) {
+			Plots = (GraphObj**)calloc(2, sizeof(GraphObj*));
+			if(Plots) {
+				Plots[0] = (Plot *)tmpl;
+				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:
+					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;
+				if(type == GT_STANDARD && !NumAxes) CreateAxes(AxisTempl);
+				dirty = false;
+				return true;
+				}
+			return false;
+			}
+		else {
+			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;
+			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
+			CurrDisp->StartPage();
+			DoPlot(CurrDisp);
+			CurrDisp->EndPage();
+			return true;
+			}
+		break;
+	case CMD_MOUSE_EVENT:
+		mev = (MouseEvent *)tmpl;		defs.SetDisp(o);
+		if(CurrGO && CurrGO->moveable && mev->Action == MOUSE_LBDOWN &&
+			(TrackGO = (GraphObj*)CurrGO->ObjThere(mev->x, mev->y))){
+			ToolMode |= TM_MOVE;
+			}
+		else if(mev->Action == MOUSE_LBDOWN){
+			CurrGO = 0L;
+			if((ToolMode & 0xff) == TM_STANDARD || (ToolMode & 0xff) == TM_ZOOMIN){
+				rc_mrk.left = mev->x;		rc_mrk.top = mev->y;
+				}
+			}
+		if(ToolMode != TM_STANDARD && ExecTool(mev)) return true;
+		switch(mev->Action) {
+		case MOUSE_RBUP:
+			Undo.SetDisp(o);
+			i = ToolMode;
+			ToolMode = TM_STANDARD;
+			mev->Action = MOUSE_LBUP;	//fake select
+			Command(cmd, tmpl, o);
+			ToolMode = i;
+			//the default behaviour for right button click is the same as for
+			//   double click: execute properties dialog, just continue.
+		case MOUSE_LBDOUBLECLICK:
+			if(!CurrGO){
+				mev->Action = MOUSE_LBUP;
+				Command(CMD_MOUSE_EVENT, mev, CurrDisp);
+				mev->Action = MOUSE_LBDOUBLECLICK;
+				}
+			if(CurrGO) {
+				if(CurrGO->PropertyDlg()) {
+					bModified = true;
+					return Command(CMD_REDRAW, 0L, o);
+					}
+				}
+			else if (Id == GO_PAGE) return Command(CMD_CONFIG, 0L, o);
+			else o->HideMark();
+			CurrGO = TrackGO = 0L;	CurrLabel = 0L;
+			return false;
+		case MOUSE_LBUP:
+			Undo.SetDisp(o);
+			if(Id == GO_GRAPH){
+				CurrGO = TrackGO = 0L;
+				CurrGraph = this;
+				}
+		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)for(i = NumPlots-1; i>=0; i--)
+				if(Plots[i] && Plots[i]->Command(cmd, tmpl,o)) 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_SETSCROLL:
+		if(o) {
+			o->ActualSize(&rc);
+			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]->Command(cmd, tmpl, o);
+			}
+		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[20];
+	Label *label;
+	double ts, lb_ydist, lb_xdist, tlb_dist;
+	DWORD ptick, ntick, utick;
+
+	if(Axes && NumAxes) return;
+	label_def.ColTxt = defs.Color(COL_AXIS);
+	label_def.ColBg = 0x00ffffffL;
+	label_def.fSize = defs.GetSize(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 = defs.GetSize(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 = defs.GetSize(SIZE_AXIS_TICKS);
+	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+defs.GetSize(SIZE_AXIS_TICKS))*2.0);
+	lb_xdist = NiceValue((ts+defs.GetSize(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);
+			strcpy(label_text, "x-axis");
+			label = new Label(Axes[0], data, GRect.Xmin + (DRect.Xmin+DRect.Xmax)/2.0f, 
+				GRect.Ymin+DRect.Ymax+defs.GetSize(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); 
+			strcpy(label_text, "y-axis");
+			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
+			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - defs.GetSize(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;
+		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);
+			strcpy(label_text, "x-axis");
+			label_def.Align = TXA_VTOP | TXA_HRIGHT;
+			label = new Label(Axes[0], data, GRect.Xmin + DRect.Xmax - defs.GetSize(SIZE_AXIS_TICKS)*2.0, 
+				GRect.Ymin+DRect.Ymax+defs.GetSize(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);
+			strcpy(label_text, "y-axis");
+			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HRIGHT;
+			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - defs.GetSize(SIZE_AXIS_TICKS)*6.0, 
+				GRect.Ymin + DRect.Ymin + defs.GetSize(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);
+			strcpy(label_text, "x-axis");
+			label = new Label(Axes[0], data, GRect.Xmin + (DRect.Xmin+DRect.Xmax)/2.0f, 
+				GRect.Ymin+DRect.Ymax+defs.GetSize(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); 
+			strcpy(label_text, "y-axis");
+			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
+			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - defs.GetSize(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);
+			strcpy(label_text, "x-axis");
+			label = new Label(Axes[0], data, GRect.Xmin + (DRect.Xmin+DRect.Xmax)/2.0f, 
+				GRect.Ymin+DRect.Ymax+defs.GetSize(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); 
+			strcpy(label_text, "y-axis");
+			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
+			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - defs.GetSize(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);
+			strcpy(label_text, "x-axis");
+			label = new Label(Axes[0], data, GRect.Xmin + (DRect.Xmin+DRect.Xmax)/2.0f, 
+				GRect.Ymin+DRect.Ymax+defs.GetSize(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); 
+			strcpy(label_text, "y-axis");
+			label_def.RotBL = 90.0;			label_def.Align = TXA_VBOTTOM | TXA_HCENTER;
+			label = new Label(Axes[1], data, GRect.Xmin + DRect.Xmin - defs.GetSize(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;
+		}
+}
+
+bool
+Graph::ExecTool(MouseEvent *mev)
+{
+	static POINT pl = {0, 0}, pc = {0, 0};
+	static DWORD color = 0L;
+	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(ToolMode & TM_MOVE) switch (mev->Action) {
+		case MOUSE_LBDOWN:
+			pl.x = pc.x = mev->x;				pl.y = pc.y = mev->y;
+			CurrDisp->HideMark();
+			if(TrackGO && TrackGO->Command(CMD_MOUSECURSOR, 0L, CurrDisp))
+				return true;
+			else 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
+				ToolMode = TM_STANDARD;
+				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(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);
+					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;
+				CurrDisp->ShowLine(line, 2, color);
+				AddToPolygon(&tl_nPts, tl_pts, &pc);
+				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) :
+						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;
+					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_LBUP:
+			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 = defs.GetSize(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;
+	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;
+		CurrDisp->ActualSize(&cw);
+		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;
+				}
+			CurrDisp->ActualSize(&cw);
+			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;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Pages are graphic objects containing graphs and drawn objects
+Page::Page(GraphObj *par, DataObj *d):Graph(par, d, 0L)
+{
+	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);
+		sprintf(TmpTxt, "Page %d", cPages);
+		if(!name) name = strdup(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);
+
+}
+
+bool
+Page::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	GraphObj **tmpPlots;
+	Graph *g;
+
+	switch(cmd) {
+	case CMD_MOUSE_EVENT:
+		return Graph::Command(cmd, tmpl, o);
+	case CMD_REDRAW:
+		Disp->StartPage();		DoPlot(Disp);		Disp->EndPage();
+		return true;
+	case CMD_CONFIG:
+		return Configure();
+	case CMD_DROP_GRAPH:
+		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);
+		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_NEWGRAPH:
+		if((g = new Graph(this, data, Disp)) && 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;
+	default:
+		return Graph::Command(cmd, tmpl, o);
+	}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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) {
+		if(list[i]) {
+			curr_obj = list[i];			n = 0;
+			if(i) while(curr_obj && curr_obj != list[0]) {
+				if(curr_obj != list[0]) n += sprintf(TmpTxt + n, " -");
+				if(curr_obj) curr_obj = curr_obj->parent; 
+				}
+			sprintf(TmpTxt + n, "%s%s", 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) * count;
+	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;
+}
diff --git a/rlplot.h b/rlplot.h
new file mode 100755
index 0000000..ca03fa0
--- /dev/null
+++ b/rlplot.h
@@ -0,0 +1,2495 @@
+//RLPlot.h, Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005 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);}
+inline int WriteFloatToBuff(char *buff, double val) {return sprintf(buff, " %g", val);}
+
+#ifdef _WINDOWS
+#include <windows.h>
+#define w_char unsigned short
+
+#else
+#define DWORD unsigned long
+#define w_char unsigned short
+
+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};
+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_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_DELETE, 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_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_UNLOCK, CMD_SYMTEXT_UNDO,
+	CMD_FILLRANGE, CMD_BUSY, CMD_ERROR, CMD_CLEAR_ERROR, CMD_SETPARAM, CMD_SETFUNC,
+	CMD_HIDE_MARK, CMD_LEGEND, CMD_FILENAME, CMD_LAYERS, CMD_OBJTREE, CMD_TEXTDEF,
+	CMD_HASSTACK, CMD_WRITE_GRAPHS, CMD_SETFONT, CMD_SETSTYLE};
+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_TEXT, 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_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_GRAPH = 0x200, GO_PAGE, GO_SPREADDATA = 0x300, GO_DEFRW};
+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,
+	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};
+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};
+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};
+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};
+
+//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 {
+	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;
+
+// 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_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;
+	} 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 IsInRange(int x, int y);
+	bool BoundRec(RECT *rec);
+
+private:
+	char *txt;
+	int x1, y1, x2, y2, cx, cy, curridx;
+
+	bool Reset();
+	bool Parse(int start);
+};
+
+class anyOutput{
+public:
+	int units;							//use units mm, inch ...
+	int MrkMode;						//existing mark on screen
+	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 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 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 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 oPolygon(POINT *pts, int cp, char *nam = 0L){return false;};
+	virtual bool oArc(int x1, int y1, int x2, int y2, int quads){return false;};
+};
+
+enum {ET_UNKNOWN, ET_VALUE, ET_TEXT, ET_FORMULA, ET_ERROR, ET_BUSY=0x100, 
+	ET_CIRCULAR=0x200};
+
+class EditText {
+public:
+	char *text;
+	int Align, type;
+	void *parent;				//points to a data object: defined below
+
+	EditText(void *par, POINT where, POINT right, char *msg);
+	EditText(void *par, char *msg);
+	~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 GetResult(anyResult *r);
+	bool SetValue(double v);
+	bool SetText(char *t);
+	bool GetItem(char *text, int size);
+	void SetRec(RECT *rc);
+	int GetX() {return loc.x;};
+	int GetY() {return loc.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);};
+
+private:
+	LineDEF *bgLine;
+	FillDEF *bgFill;
+	int length, CursorPos, m1, m2, mx1, mx2;
+	POINT loc, rb, crb;
+	DWORD TextCol;
+	double Value;
+
+	void FindType();
+};
+
+class fmtText {
+
+typedef struct _fmt_txt_info {
+	int tag;
+	char *txt;
+}fmt_txt_info;
+
+public:
+	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);
+	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);
+
+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);
+	void DrawFormatted(anyOutput *o);
+
+	char *src;
+	POINT pos;
+	int n_split;
+	fmt_txt_info *split_text;
+};
+
+class DataObj{
+public:
+	int cRows, cCols;
+	EditText ***etRows;
+
+	DataObj();
+	~DataObj();
+	virtual bool Init(int nRows, int nCols);
+	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);
+	char ** GetTextPtr(int row, int col);
+	virtual bool GetResult(anyResult *r, int row, int col);
+	virtual bool GetSize(int *width, int *height);
+	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();
+};
+
+class StrData {
+public:
+	StrData(DataObj *par);
+	~StrData();
+	bool GetSize(int *w, int *h);
+	void StrData::RestoreData(DataObj *dest);
+
+private:
+	int pw, ph, h, w;
+	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();
+	void Herringbone();
+	void Circles();
+	void Grass();
+	void Foam();
+	void Recs();
+	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);
+};
+
+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;
+	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:
+	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);
+	Bar(int src);
+	~Bar();
+	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();
+	bool FileIO(int rw);
+
+private:
+	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);
+	DataLine(GraphObj *par, DataObj *d, lfPOINT *val, long nval);
+	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);
+};
+
+class DataPolygon:public DataLine{
+public:
+	DataPolygon(GraphObj *par, DataObj *d, char *xrange=0L, char *yrange=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:
+	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:
+	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);
+	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;
+	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::Line3D(GraphObj *par, DataObj *d, fPOINT3D *pt, int n_pt);
+	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;
+	char *x_range, *y_range, *z_range;
+	long nPts, npts;
+	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);
+	TextDEF *GetTextDef(){return &TextDef;};
+
+private:
+	lfPOINT fPos, fDist;
+	double si, csi, curr_z;
+	DWORD flags, bgcolor;
+	TextDEF TextDef;
+	LineDEF bgLine;
+	int ix, iy, CursorPos;
+	bool is3D;
+	RECT Cursor;
+	POINT pts[5];
+	POINT *ssRef;
+	long cssRef;
+	anyOutput *defDisp;
+	bool bModified, bBGvalid;
+	fmtText *fmt_txt;
+};
+
+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;
+	DWORD flags, undo_flags;
+	TextDEF TextDef;
+	long nLines;
+	int cli;
+	bool is3D;
+	Label **Lines;
+};
+
+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:
+	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);
+	void DoPlot(anyOutput *o);
+	void DoMark(anyOutput *o, bool mark);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	virtual bool PropertyDlg();
+	bool FileIO(int rw);
+	void * ObjThere(int x, int y);
+	void Track(POINT *p, anyOutput *o);
+
+private:
+	dragHandle **pHandles;
+
+	void ShowPoints(anyOutput *o);
+};
+
+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:
+	LegItem(GraphObj *par, DataObj *d, LineDEF *ld, LineDEF *lf, FillDEF *fill);
+	LegItem(GraphObj *par, DataObj *d, LineDEF *ld, Symbol *sy);
+	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);
+	bool HasSym(LineDEF *ld, GraphObj *sy);
+
+private:
+	LineDEF DataLine, OutLine, HatchLine;
+	FillDEF Fill;
+	Symbol *Sym;
+	Label *Desc;
+	DWORD flags;
+	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);
+	bool HasSym(LineDEF *ld, GraphObj *sy);
+
+private:
+	lfPOINT pos, lb_pos;
+	RECT trc;
+	anyOutput *to;
+	fRECT B_Rect, C_Rect, D_Rect, E_Rect, F_Rect;
+	long nItems;
+	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;		//this plot uses its own axes
+	lfPOINT xBounds, yBounds, zBounds;	//like Bounds but in 3D space
+	int hidden;						//plot (layer) is not visible
+
+	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:
+	PlotScatt(GraphObj *par, DataObj *d, DWORD presel);
+	PlotScatt(GraphObj *par, DataObj *d, int cBars, Bar **bars);
+	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);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	virtual bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+private:
+	DWORD DefSel;
+	lfPOINT BarDist;
+	char *xRange, *yRange, *ErrRange, *LbRange;
+	int DefSym;
+	long nPoints;
+	Bar **Bars;
+	Symbol **Symbols;
+	DataLine *TheLine;
+	ErrorBar **Errors;
+	Arrow **Arrows;
+	DropLine **DropLines;
+	Label **Labels;
+
+	bool CreateBarChart();
+	enum {FE_NONE = 0x1000, FE_PARENT, FE_PLOT, FE_FLUSH, FE_DELOBJ, 
+		FE_REPLGO, FE_MUTATE};
+	bool ForEach(int cmd, void *tmp, anyOutput *o);
+};
+
+class BarChart:public PlotScatt{
+public:
+	BarChart(GraphObj *par, DataObj *d);
+};
+
+class FreqDist:public Plot {
+public:
+	FreqDist(GraphObj *par, DataObj *d);
+	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 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);
+	~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);
+
+private:
+	long nPoints;
+	lfPOINT BoxDist;
+	Box **Boxes;
+	Whisker **Whiskers;
+	Symbol **Symbols;
+	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):StackBar(par, d){};
+	bool PropertyDlg();
+};
+
+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:
+	int 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, char *xr, char *yr, char *zr);
+	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);
+	Grid3D(int src);
+	~Grid3D();
+	void DoPlot(anyOutput *o);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+	void CreateObs();
+
+private:
+	long nLines;
+	fPOINT3D start, step;
+	LineDEF Line;
+	Line3D **lines;
+};
+
+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);
+	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 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;
+
+	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 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;
+	double *RotDef;
+
+	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);
+	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);
+	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);
+
+private:
+	long nObs, nmaxObs;
+	DWORD crea_flags;
+	Drag3D *drag;
+	fPOINT3D cub1, cub2, rotC, 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 Graph:public GraphObj{
+public:
+	long NumPlots;
+	int ToolMode, units, nscp;
+	anyOutput *Disp, *CurrDisp;
+	fRECT GRect, DRect, Bounds;
+	bool OwnDisp, bModified;
+	fRECT CurrRect;
+	GraphObj **Plots;
+	DWORD ColBG, ColAX;
+	GraphObj **Sc_Plots;
+	Axis **Axes;
+	char *filename;
+
+	Graph(GraphObj *par, DataObj *d, anyOutput *o);
+	Graph(int src);
+	~Graph();
+	double GetSize(int select);
+	bool SetSize(int select, double value);
+	DWORD GetColor(int select);
+	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);
+
+private:
+	int NumAxes, AxisTempl, tickstyle, zoom_level;
+	RECT rcDim, rcUpd, rc_mrk;
+	DWORD ColDR, ColGR, ColGRL;
+	AxisDEF x_axis, y_axis;
+	FrmRect *frm_g, *frm_d;
+	bool dirty;
+	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);
+};
+
+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);
+
+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();
+	unsigned long RegisterGO(GraphObj *go);
+	void AddRegGO(GraphObj *go);
+	bool PushGO(unsigned long id, GraphObj *go);
+	GraphObj *PopGO(unsigned long id);
+	void FreeStack();
+
+private:
+	unsigned long 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;
+	double min4log;
+	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_OBJCONF, UNDO_OBJCONF_1,
+		UNDO_LFP, 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};
+	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;
+
+public:
+	int *pcb;
+
+	UndoObj();
+	~UndoObj();
+	void Flush();
+	void SetDisp(anyOutput *o);
+	void KillDisp(anyOutput *o);
+	void InvalidGO(GraphObj *go);
+	void Pop();
+	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 ValInt(GraphObj *parent, int *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);
+	void 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, DWORD flags);
+	void TextCell(EditText *et, anyOutput *o, char *text, int *cur, int *m1, int *m2, void* DaO, DWORD flags);
+
+private:
+	UndoInfo **buff, **buff0;
+	int stub1, ndisp;
+	anyOutput *cdisp, *ldisp;
+	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);
+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 InitTextCursor(bool init);
+void EmptyClip();
+void GetDesktopSize(int *width, int *height);
+void FindBrowser();
+void LoopDlgWnd();
+void CloseDlgWnd(void *hDlg);
+void ShowDlgWnd(void *hDlg);
+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);
+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);
+bool FillSsRange(DataObj *d, char **range);
+bool GetCopyRange(RECT *rc, DataObj *d);
+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);
+void ReshapeFormula(char **text);
+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 *Int2ColLabel(int nr, bool uc);
+char *str2xml(char *str);
+char **split(char *str, char sep, int *nl);
+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);
+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 BackupFile(char *FileName);
+bool IsRlpFile(char *FileName);
+bool IsXmlFile(char *FileName);
+bool FileExist(char *FileName);
+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);
+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);
+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
+bool do_xyfunc(DataObj *, double, double, double, char *, lfPOINT **, long *, char *);
+anyResult *do_formula(DataObj *, char *);
+bool MoveFormula(DataObj *d, char *of, char *nf, int dx, int dy);
+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();
+double gammln(double xx);
+double factrl(int n);
+double t_dist(double t, double df);
+double f_dist(double f, double df1, double df2);
+void d_quartile(int n, double *v, double *q1, double *q2, double *q3);
+double d_amean(int n, double *v);
+double d_gmean(int n, double *v);
+double d_hmean(int n, double *v);
diff --git a/rlplot.spec b/rlplot.spec
new file mode 100755
index 0000000..aaaff29
--- /dev/null
+++ b/rlplot.spec
@@ -0,0 +1,62 @@
+Name:      rlplot
+Version:   0.99.12b
+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), Windows Metafile (WMF), 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
+* 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
new file mode 100755
index 0000000..40e32b8
--- /dev/null
+++ b/spreadwi.cpp
@@ -0,0 +1,1833 @@
+//spreadwin.cpp, (c)2000, 2001, 2002, 2003, 2004, 2005 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 <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 BlackLine;
+extern EditText *CurrText;
+extern char *LoadFile;
+extern char TmpTxt[];
+extern Default defs;
+extern UndoObj Undo;
+static ReadCache *Cache = 0L;
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 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, cc;
+
+	if(ptr) {
+		for(i = nt = nl = nc = 0; ptr[i]; i++) {
+			switch(ptr[i]) {
+			case 0x09:				//tab
+				nt++;
+				break;
+			case 0x0a:				//LF
+				nl++;
+				break;
+			case ',':
+				nc++;
+				break;
+				}
+			}
+		if(dispatch && i && !nt && !nl)
+			for(i = 0; ptr[i]; i++){
+				cc = ptr[i];
+				g->Command(CMD_ADDCHAR, (void*)(& cc), 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) 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_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(DataObj *Data);
+	~SpreadWin();
+	void DoPlot(anyOutput *target);
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+
+	bool ShowGrid(int CellWidth, int CellHeight, int FirstWidth);
+	bool PrintData(anyOutput *o);
+	void WriteGraphXML(unsigned char **ptr, long *cbd);
+
+private:
+	int ch, cw, fw;				//cell height and width, row button width
+	bool is_modified;
+	char *filename;
+	TextDEF ssText;
+	ssButton **cButtons, **rButtons;
+	ssButton *aButton;
+	DataObj *d;
+	int NumGraphs;
+	Graph **g;
+};
+
+SpreadWin::SpreadWin(DataObj *Data):GraphObj(0L, Data)
+{
+	d = Data;	g = 0L;		ssOrg.x =  ssOrg.y = 0;		NumGraphs = 0;
+	ch = 19;	cw = 76;	fw = 32;	filename=0L;	aButton = 0L;
+	if(w = NewDispClass(this)){
+		w->hasHistMenu = true;
+		ssText.RotBL = ssText.RotCHAR = 0.0;
+		ssText.fSize = 0.0f;
+#ifdef _WINDOWS
+		ssText.iSize = w->un2iy(defs.GetSize(SIZE_CELLTEXT));
+#else
+		ssText.iSize = w->un2iy(defs.GetSize(SIZE_CELLTEXT)*.8f);
+#endif
+		ssText.Align = TXA_VTOP | TXA_HLEFT;		ssText.Mode = TXM_TRANSPARENT;
+		ssText.Style = TXS_NORMAL;					ssText.ColBg = 0x00d8d8d8L;
+		ssText.ColTxt = 0x00000000L;				ssText.text = 0L;
+		ssText.Font = FONT_HELVETICA;				w->SetTextSpec(&ssText);
+		w->SetMenu(MENU_SPREAD);					w->FileHistory();
+		w->Erase(0x00d8d8d8L);						w->Caption("RLPlot data");
+		cw = w->un2ix(defs.GetSize(SIZE_CELLWIDTH));
+		ch = w->un2iy(defs.GetSize(SIZE_CELLTEXT)) + 2;
+		fw = 32;
+		}
+	cButtons = rButtons = 0L;
+	Id = GO_SPREADDATA;
+	is_modified = false;
+}
+
+SpreadWin::~SpreadWin()
+{
+	int i;
+
+	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]) {
+			g[i]->Command(CMD_CAN_DELETE, 0L, 0L);
+			delete(g[i]);
+			}
+		free (g);
+		}
+	if(filename) free(filename);	filename=0L;
+}
+
+void
+SpreadWin::DoPlot(anyOutput *o)
+{
+	o->ActualSize(&currRC);
+	o->StartPage();
+	d->Command(CMD_DOPLOT, (void*)this, o);
+	o->EndPage();
+}
+
+bool
+SpreadWin::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	char *Name;
+	Graph **g1, *g2;
+	int i, j, k;
+	MouseEvent *mev;
+
+	if(d) {
+		switch(cmd) {
+		case CMD_UNDO:
+			if(w) {
+				w->MouseCursor(MC_WAIT, true);
+				Undo.Restore(true, w);
+				w->MouseCursor(MC_ARROW, true);
+				}
+			return true;
+		case CMD_CAN_DELETE:
+			HideTextCursor();
+			if(is_modified && YesNoBox("The spreadsheet has been modified!\n\nDo you want to save it now?")){
+				is_modified=false;
+				return Command(CMD_SAVEDATAAS, tmpl, o);
+				}
+			Undo.KillDisp(w);
+			is_modified=false;
+			return true;
+		case CMD_MRK_DIRTY:
+			is_modified=true;
+			return true;
+		case CMD_WRITE_GRAPHS:
+			if (g && NumGraphs) WriteGraphXML((unsigned char**)tmpl, (long*)o);
+			return true;
+		case CMD_DROP_GRAPH:
+			if(o) o->FileHistory();
+			if(!g) g = (Graph **)calloc(2, sizeof(Graph*));
+			else {
+				g1 = (Graph **)calloc(NumGraphs+2, sizeof(Graph*));
+				if(!g1) return false;
+				for(i = 0; i < NumGraphs; i++) g1[i] = g[i];
+				free(g);
+				g = g1; 
+				}
+			if(!g) return false;
+			g[NumGraphs] = (Graph *)tmpl;
+			if(g[NumGraphs]){
+				g[NumGraphs]->parent = this;
+				NumGraphs++;
+				g[NumGraphs-1]->Command(CMD_SET_DATAOBJ, (void *)d, 0L);
+				g[NumGraphs-1]->DoPlot(NULL);
+				}
+			return true;
+		case CMD_NEWGRAPH:
+			if((g2 = new Graph(this, d, 0L)) && g2->PropertyDlg() && 
+				Command(CMD_DROP_GRAPH, g2, o))return true;
+			else if(g2) DeleteGO(g2);
+			return false;
+		case CMD_NEWPAGE:
+			if((g2 = new Page(this, d)) && 
+				Command(CMD_DROP_GRAPH, g2, o))return true;
+			else if(g2) DeleteGO(g2);
+			return false;
+		case CMD_DELGRAPH:
+			if (g && NumGraphs) {
+				for(i = 0; i < NumGraphs; i++) if(g[i]){
+					g[i]->Command(CMD_CAN_DELETE, 0L, 0L);
+					DeleteGO(g[i]);
+					}
+				free (g);
+				}
+			g = 0L;
+			NumGraphs = 0;
+			Undo.Flush();
+			return true;
+		case CMD_DELOBJ:
+			if (g) {
+				for(i = 0; i <= NumGraphs; i++) {
+					if(g[i] == (Graph *)tmpl) {
+						delete (g[i]);
+						g[i] = 0L;
+						return true;
+						}
+					}
+				}
+			return false;
+		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 = strdup(Name);
+					}
+				}
+			return true;
+		case CMD_DROPFILE:
+			if(IsRlpFile((char*)tmpl)) return OpenGraph(this, (char*)tmpl, 0L);
+			else if(d->ReadData((char*)tmpl, 0L, FF_UNKNOWN)){
+				if(filename) free(filename);
+				filename = strdup((char*)tmpl);
+				return Command(CMD_SETSCROLL, 0L, w);
+				}
+			return false;
+		case CMD_OPEN:
+			if((Name = OpenDataName(filename)) && Name[0]){
+				if(o) o->FileHistory();
+				if(IsRlpFile(Name)) return OpenGraph(this, Name, 0L);
+				else if(d->ReadData(Name, 0L, FF_UNKNOWN)){
+					if(filename) free(filename);
+					filename = strdup(Name);
+					return Command(CMD_SETSCROLL, 0L, w);
+					}
+				}
+			return false;
+		case CMD_ADDROWCOL:
+			DoSpShSize(d);
+			return true;
+		case CMD_MOUSE_EVENT:
+			if(o && (mev = (MouseEvent*)tmpl) && mev->y > o->MenuHeight) {
+				if(mev->x < fw && mev->y < (o->MenuHeight + ch) && aButton) {
+					aButton->Command(cmd, tmpl, o);
+					}
+				else if(mev->x < fw && rButtons) {
+					i = (mev->y - o->MenuHeight - ch)/ch;
+					if(rButtons[i]) rButtons[i]->Command(cmd, tmpl, o);
+					}
+				else if(mev->y < (o->MenuHeight + ch) && cButtons) {
+					i = (mev->x - fw)/cw;
+					if(cButtons[i]) cButtons[i]->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_XML:		case CMD_PASTE_CSV:
+			Undo.DataObject(this, w, d, 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_REDRAW:
+			return d->Command(cmd, tmpl, o); 
+		case CMD_SETSCROLL:
+			HideTextCursor();
+			o->ActualSize(&currRC);
+			k = (currRC.bottom-currRC.top)/ch;
+			d->GetSize(&i, &j);
+			o->SetScroll(true, 0, j, k, ssOrg.y);
+			k = (currRC.right-currRC.left)/cw;
+			o->SetScroll(false, 0, i, k, ssOrg.x);
+			DoPlot(o);
+			return true;
+		case CMD_PAGEUP:
+			k = (currRC.bottom-currRC.top)/ch;
+			k = k > 3 ? k-2 : 1;
+			ssOrg.y = ssOrg.y > k ? ssOrg.y-k : 0;
+			return Command(CMD_SETSCROLL, tmpl, o);
+		case CMD_PAGEDOWN:
+			k = (currRC.bottom-currRC.top)/ch;
+			k = k > 3 ? k-2 : 1;
+			d->GetSize(&i, &j);
+			ssOrg.y = ssOrg.y < j-k*2 ? ssOrg.y+k : j > k ? j-k : 0;
+			return Command(CMD_SETSCROLL, tmpl, o);
+		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:
+			return true;
+		case CMD_KILLFOCUS:
+			return true;
+		case CMD_TEXTSIZE:
+			if(tmpl)ssText.iSize = *((int*)tmpl);
+			break;
+		case CMD_CONFIG:
+			return defs.PropertyDlg();
+		case CMD_NONE:
+			return true;
+		case CMD_PRINT:
+			return PrintData(o);
+			}
+		}
+	return false;
+}
+
+bool
+SpreadWin::ShowGrid(int CellWidth, int CellHeight, int FirstWidth)
+{
+	int i, c, nr, nc;
+	RECT rc;
+	char text[20];
+	POINT grid[2];
+	TextDEF ButtText;
+	bool redim = false;
+
+	//DEBUG: Make sure not to draw the grid much larger than the dektop
+	if(ch != CellHeight || cw != CellWidth || fw != FirstWidth) redim = true;
+	ch = CellHeight;	cw = CellWidth;		fw = FirstWidth;
+	if(redim){
+		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 = (rc.right/CellWidth)+1;
+		cButtons = (ssButton **)calloc(c, sizeof(ssButton*));
+		for(i = 0; i < (c-1); i++) cButtons[i] = 
+			new ssButton(this, i*CellWidth+FirstWidth, w->MenuHeight, 
+			CellWidth+1, CellHeight);
+		}
+	if(!rButtons) {
+ 		c = (rc.bottom/CellHeight)+1;
+		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);
+		}
+	w->SetLine((LineDEF *)&BlackLine);
+	d->GetSize(&nc, &nr);
+	grid[0].x = rc.left+FirstWidth;
+	i = (nc-ssOrg.x)*CellWidth+FirstWidth;
+	grid[1].x = i < rc.right ? i : rc.right;
+	if(rButtons) for(i = 0; rButtons[i]; i++) {
+		sprintf(text, "%d", i+1+ssOrg.y);
+		if(rButtons[i]) {
+			rButtons[i]->Command(CMD_SETTEXTDEF, &ButtText, w);
+			rButtons[i]->Command(CMD_SETTEXT, text, w);
+			rButtons[i]->DoPlot(w);
+			w->SetLine((LineDEF *)&BlackLine);
+			}
+		grid[0].y = grid[1].y = w->MenuHeight + i*CellHeight - 1;
+		if(i < (2+nr-ssOrg.y)) w->oSolidLine(grid);
+		}
+	grid[0].y = rc.top+CellHeight+w->MenuHeight;
+	i = (1+nr-ssOrg.y)*CellHeight;
+	grid[1].y = (i+w->MenuHeight)< rc.bottom ? i+w->MenuHeight : rc.bottom;
+	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]->DoPlot(w);
+			w->SetLine((LineDEF *)&BlackLine);
+			}
+		grid[0].x = grid[1].x = i*CellWidth+FirstWidth-1;
+		if(i <= (nc-ssOrg.x)) w->oSolidLine(grid);
+		}
+	w->SetTextSpec(&ssText);
+	if(aButton) aButton->DoPlot(w);
+	return true;
+}
+
+bool
+SpreadWin::PrintData(anyOutput *o)
+{
+	int i, j, k, l, pfw, pcw, pch, rpp, cpp, nc, nr, ix, iy, cpages;
+	RECT rc, margin, margin_first;
+	POINT pp_pos, ss_pos, grid[2];
+	LineDEF Line1, Line2;
+	TextDEF td, tdp;
+	bool bContinue;
+	time_t ti = time(0L);
+	double val;
+
+	Line1.patlength = Line2.patlength = 1.0;
+	Line1.color = Line2.color = 0x0;			//black 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;
+		}
+	Line2.width = Line1.width * 3.0;
+	d->GetSize(&nc, &nr);
+	if(!(o->StartPage())) return false;
+	pfw = iround(o->hres * ((double)fw)/w->hres);
+	pcw = iround(o->hres * ((double)cw)/w->hres);
+	pch = iround(o->vres * ((double)ch)/w->vres + 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;
+#ifdef _WINDOWS
+	tdp.iSize = iround(o->hres/6.0);
+#else
+	tdp.iSize = iround(o->hres/7.5);
+#endif
+	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
+	td.fSize = defs.GetSize(SIZE_CELLTEXT);
+#else
+	td.fSize = defs.GetSize(SIZE_CELLTEXT)*.8f;
+#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));
+	cpp = (rc.right - margin.left - margin.right - pfw)/pcw;
+	rpp = (rc.bottom - margin.top - margin.bottom - pch)/pch;
+	pp_pos.x = margin.left;		pp_pos.y = margin.top;
+	ss_pos.x = 0;				ss_pos.y = 0;		cpages = 1;
+	do {
+		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);
+		o->SetLine(&Line1);
+		for(i = 1; i <= k; i++) {			//vertical grid
+			grid[0].x = grid[1].x = pp_pos.x + pfw + i * pcw;
+			o->oSolidLine(grid);
+			}
+		grid[0].x = margin.left+pfw;	grid[1].x = grid[0].x + k * pcw;
+		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(&Line1);
+		for(i = 1; i <= l; i++) {			//horizontal grid
+			grid[0].y = grid[1].y = pp_pos.y + pch + i * 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);
+		for(i = 0; i <= k; i++) {			//column headers
+			grid[0].x = grid[1].x = pp_pos.x + pfw + i * pcw;
+			o->oSolidLine(grid);			ix = grid[0].x + (pcw >>1);
+			if(i < k) o->oTextOut(ix, iy, Int2ColLabel(i+ss_pos.x, true), 0);
+			}
+		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);
+			sprintf(TmpTxt, "%d", i+1+ss_pos.y);
+			if(i < l) o->oTextOut(ix, iy, TmpTxt, 0);
+			}
+		for(i = 0; i < k; i++) {			//spreadsheet data
+			for (j = 0; j < l; j++) {
+				if(d->GetText(j+ss_pos.y, i+ss_pos.x, TmpTxt, TMP_TXT_SIZE)){
+					if(d->etRows[j+ss_pos.y][i+ss_pos.x]->isFormula()){
+						td.Align = TXA_HRIGHT | TXA_VCENTER;
+						ix = margin.left+pfw+pcw + i*pcw - iround(o->hres/20.0);
+						d->etRows[j+ss_pos.y][i+ss_pos.x]->GetValue(&val);
+						sprintf(TmpTxt,"%g", val);
+						}
+					else if(d->etRows[j+ss_pos.y][i+ss_pos.x]->isValue()){
+						td.Align = TXA_HRIGHT | TXA_VCENTER;
+						ix = margin.left+pfw+pcw + i*pcw - iround(o->hres/20.0);
+						}
+					else {
+						td.Align = TXA_HLEFT | TXA_VCENTER;
+						ix = margin.left+pfw + i*pcw + iround(o->hres/20.0);
+						}
+					iy = pp_pos.y + pch + (pch>>1) + j * pch;
+					o->SetTextSpec(&td);
+					o->oTextOut(ix, iy, TmpTxt, 0);
+					}
+				}
+			}
+		//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 + (cpp % nc)*pcw + iround(o->hres/3.5);
+			iy = (l+2)*pch;
+			if((margin.left + ix + pfw + k*pcw) < (rc.right-margin.right)) {
+				margin.left += pfw + k*pcw + iround(o->hres/3.5);
+				bContinue = true;
+				}
+			else if((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);
+				sprintf(TmpTxt, "page %d", cpages++);
+				o->oTextOut(rc.right-margin.right, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
+				tdp.Align = TXA_HCENTER | TXA_VCENTER; o->SetTextSpec(&tdp);
+				sprintf(TmpTxt, "%s", ctime(&ti));	TmpTxt[24] = 0;
+				o->oTextOut((rc.right-rc.left)>>1, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
+				tdp.Align = TXA_HLEFT | TXA_VCENTER; o->SetTextSpec(&tdp);
+				sprintf(TmpTxt, "RLPlot %s", 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);
+	sprintf(TmpTxt, "page %d", cpages++);
+	o->oTextOut(rc.right-margin.right, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
+	tdp.Align = TXA_HCENTER | TXA_VCENTER; o->SetTextSpec(&tdp);
+	sprintf(TmpTxt, "%s", ctime(&ti));	TmpTxt[24] = 0;
+	o->oTextOut((rc.right-rc.left)>>1, rc.bottom-(margin.bottom>>1), TmpTxt, 0);
+	tdp.Align = TXA_HLEFT | TXA_VCENTER; o->SetTextSpec(&tdp);
+	sprintf(TmpTxt, "RLPlot %s", 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;
+	int i;
+
+	if(cbd) size = (*cbd / 10000)+ 10000;
+	for(i = 0; i < NumGraphs; i++) if(g[i]) {
+		pg = (unsigned char*)GraphToMem(g[i], &cb);
+		if(pg && cb) {
+			while ((*cbd+cb+100) > size){
+				*ptr = (unsigned char*)realloc(*ptr, size += 10000);
+				}
+			*cbd += sprintf(((char*)*ptr)+*cbd, "<Graph><![CDATA[\n");
+			memcpy(*ptr+*cbd, pg, cb);	*cbd += cb;
+			*cbd += sprintf(((char*)*ptr)+*cbd, "]]>\n</Graph>\n");
+			if(pg) free(pg);	pg = 0L;	cb = 0;
+			}
+		}
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// This data object is a spreadsheet
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+class SpreadData:public DataObj{
+public:
+	SpreadData();
+	~SpreadData();
+	bool Init(int nRows, int nCols);
+	bool mpos2dpos(POINT *mp, POINT *dp);
+	bool Select(POINT *p);
+	void MarkRange(char *range);
+	void HideMark(bool cclp);
+	bool WriteData(char *FileName);
+	bool AddCols(int nCols);
+	bool AddRows(int nRows);
+	bool ChangeSize(int nCols, int nRows, bool bUndo);
+	void DoPlot(anyOutput *o);
+	bool DelRange();
+	bool PasteRange(int cmd, char *txt);
+	bool InitCopy(int cmd, void *tmpl, anyOutput *o);
+	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);
+	bool ReadTSV(char *file, unsigned char *buffer, int type);
+	bool MemList(unsigned char **ptr, int type);
+
+private:
+	int CellHeight, CellWidth, FirstWidth, r_disp, c_disp;
+	RECT rcCopy, cp_src_rec;				//bounding rectangle for copy range
+	bool bActive, new_mark, bCopyCut;
+	POINT currpos, currpos2;
+	anyOutput *w;
+	SpreadWin *Disp;
+	char *m_range, *c_range;	//mark and copy ranges
+	char *err_msg;				//error message
+};
+
+SpreadData::SpreadData()
+{
+	Disp = 0L;	m_range = 0L;	c_range = 0L;	w = 0L;	err_msg=0;
+	CellWidth = CellHeight = FirstWidth = 0;
+	currpos.x = currpos.y = r_disp = c_disp = 0;
+	bActive = bCopyCut = false;
+	rcCopy.left = rcCopy.right = rcCopy.top = rcCopy.bottom = 0;
+	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);
+		}
+}
+
+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;
+}
+
+bool
+SpreadData::Init(int nRows, int nCols)
+{
+	int i, j;
+	POINT p1, p2;
+	RECT rc;
+
+	rcCopy.left = rcCopy.top = 0;	rcCopy.bottom = cRows = nRows;
+	rcCopy.right = cCols = nCols;	currpos.x = currpos.y = 0;
+	new_mark = bCopyCut = false;
+	if(!Disp) {
+		Disp = new SpreadWin(this);
+		w = Disp->w;
+		if(w) {
+			CellWidth = w->un2ix(defs.GetSize(SIZE_CELLWIDTH));
+			CellHeight = w->un2iy(defs.GetSize(SIZE_CELLTEXT)) + 2;
+			FirstWidth = 32;
+			w->ActualSize(&rc);
+			r_disp = (rc.bottom-rc.top)/CellHeight+1;
+			c_disp = (rc.right-rc.left)/CellWidth+1;
+			}
+		else return false;
+		Disp->ShowGrid(CellWidth, CellHeight, FirstWidth);
+		}
+	if(etRows)FlushData();
+	etRows = (EditText ***)calloc (cRows, sizeof(EditText **));
+	if(etRows) for(i = 0, p1.x = FirstWidth, p1.y = CellHeight + w->MenuHeight; i < cRows; i++) {
+		etRows[i] = (EditText **)calloc(cCols, sizeof(EditText *));
+		p2.y = p1.y + CellHeight;
+		if(etRows[i]) for(j = 0; j < cCols; j++) {
+			p2.x = p1.x + CellWidth;
+#ifdef _DEBUG
+			char text[20];
+			sprintf (text, "%.2f", i*10.0 + j);
+			etRows[i][j] = new EditText(this, p1, p2, text);
+#else
+			etRows[i][j] = new EditText(this, p1, p2, 0L);
+#endif
+			if(etRows[i][j]) etRows[i][j]->Redraw(w, false);
+			p1.x += CellWidth;
+			}
+		p1.y += CellHeight;
+		p1.x = FirstWidth;
+		}
+	if (LoadFile) {
+		strcpy(TmpTxt, LoadFile);	//we will reenter by recursion !
+		free(LoadFile);
+		LoadFile = 0L;
+		Disp->Command(CMD_DROPFILE, TmpTxt, w);
+		}
+	return true;
+}
+
+bool
+SpreadData::mpos2dpos(POINT *mp, POINT *dp)
+{
+	if(mp->x < (FirstWidth+10) && mp->x > FirstWidth && Disp->ssOrg.x >0) {
+		Disp->ssOrg.x -= 1;
+		if(CurrText) CurrText->Update(2, w, mp);		CurrText = 0L;
+		Disp->Command(CMD_SETSCROLL, 0L, w);
+		mp->x += CellWidth;
+		}
+	if(mp->y < (w->MenuHeight + CellHeight+10) && 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);
+		mp->y += CellHeight;
+		}
+	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);
+		mp->x -= CellWidth;
+		}
+	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);
+		}
+	dp->y = (mp->y - w->MenuHeight - CellHeight)/CellHeight + Disp->ssOrg.y;
+	dp->x = (mp->x - FirstWidth)/CellWidth + Disp->ssOrg.x;
+	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);
+		return true;
+		}
+	mpos2dpos(p, &currpos);
+	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);
+				}
+			return true;
+			}
+		}
+	if(CurrText) CurrText->Update(2, w, p);		CurrText = 0L;
+	return false;
+}
+
+void
+SpreadData::MarkRange(char *range)
+{
+	AccRange *nr, *oldr;
+	int r, c;
+
+	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) && c >= Disp->ssOrg.x 
+				&& c < (c_disp + Disp->ssOrg.x) && !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))
+				etRows[r][c]->Mark(w, etRows[r][c] != CurrText ? 2 : 3);
+			}
+		}
+	if (m_range) free(m_range);	m_range = 0L;
+	if(range) m_range = strdup(range);
+	if(oldr) delete(oldr);	if(nr) delete(nr);
+	new_mark = true;
+}
+
+void
+SpreadData::HideMark(bool cclp)
+{
+	if(cclp && c_range && c_range != m_range){
+		free(c_range);	c_range = 0L;
+		rcCopy.left = rcCopy.top = 0;
+		rcCopy.bottom = cRows;		rcCopy.right = cCols;
+		}
+	if(m_range){
+		free(m_range);	m_range = 0L;
+		DoPlot(w);
+		}
+	if(cclp) EmptyClip();
+	new_mark = bCopyCut = false;
+}
+
+bool
+SpreadData::WriteData(char *FileName)
+{
+	FILE *File;
+	int i, j;
+	char tmp[800];
+	unsigned char *buff = 0L;
+
+	if(!cRows || !cCols || !etRows) return false;
+	BackupFile(FileName);
+	if(!(File = fopen(FileName, "w")))return false;
+	HideMark(true);
+	rcCopy.left = rcCopy.top = 0;	rcCopy.bottom = cRows;		rcCopy.right = cCols;
+	i = strlen(FileName);
+	//test for xml extension
+	if(!strcmp(".xml", FileName+i-4) || !strcmp(".XML", FileName+i-4)) {
+		MemList(&buff, FF_XML);
+		if(buff){
+			fprintf(File, "%s", buff);
+			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){
+			fprintf(File, "%s", buff);
+			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){
+			fprintf(File, "%s", buff);
+			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]->GetItem(tmp, sizeof(tmp)))
+				fprintf(File, "%s", tmp);
+			if(j < (cCols-1)) fprintf(File, ",");
+			}
+		fprintf(File, "\n");
+		}
+	fclose(File);
+	return true;
+}
+
+bool
+SpreadData::ReadData(char *FileName, unsigned char *buffer, int type)
+{
+	int i, j, l;
+	char ItemText[20];
+	bool success;
+	POINT pt;
+
+	if(FileName) {		//read disk file
+		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)){
+			if(ReadXML(FileName, buffer, type)){
+				rcCopy.left = rcCopy.top = 0;
+				rcCopy.right = cCols;
+				rcCopy.bottom = cRows;
+				return true;
+				}
+			return false;
+			}
+		if(0 == strcmp(".tsv", FileName+strlen(FileName)-4) ||
+			0 == strcmp(".TSV", FileName+strlen(FileName)-4)){
+			if(ReadTSV(FileName, buffer, type)){
+				rcCopy.left = rcCopy.top = 0;
+				rcCopy.right = cCols;
+				rcCopy.bottom = cRows;
+				return true;
+				}
+			return false;
+			}
+		if(!(Cache = new ReadCache())) return false;
+		if(! Cache->Open(FileName)) {
+			delete Cache;
+			sprintf(TmpTxt, "Error open data file\n\"%s\"", FileName);
+			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);
+		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;
+	pt.x = pt.y = 0;		//pt is a dummy argument only
+	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]) etRows[i][j]->SetText("");
+			l = strlen(ItemText);
+			if(l>1 && ItemText[0] == '"') {
+				ItemText[l-1] = 0;
+				etRows[i][j]->SetText(ItemText+1);
+				etRows[i][j]->Update(20, 0L, &pt);
+				}
+			else if(l){
+				etRows[i][j]->SetText(ItemText);
+				etRows[i][j]->Update(10, 0L, &pt);
+				}
+			j++;
+			}
+		if(!success && !Cache->IsEOF()) {i++; j = 0;}	//eol
+		}while (ItemText[0] || !Cache->IsEOF());		//eof
+
+	Cache->Close();
+	delete Cache;
+	Cache = 0L;
+	if(FileName) {
+		rcCopy.left = rcCopy.top = 0;
+		rcCopy.right = cCols;
+		rcCopy.bottom = cRows;
+		}
+	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;
+	POINT p1, p2;
+
+	if (nCols <= cCols) return false;
+	if(etRows && etRows[0] && etRows[0][cCols-1]) {
+		p1.x = p2.x = (CellWidth + etRows[0][cCols-1]->GetX());		
+		p1.y = p2.y = etRows[0][cCols-1]->GetY();
+		p2.x += CellWidth;						p2.y += CellHeight;
+		}
+	else return false;
+	for(i = 0; i < cRows; i++) {
+		NewRow = (EditText **)realloc(etRows[i], nCols * sizeof(EditText *));
+		if(NewRow) {
+			for(j = cCols; j < nCols; j++) {
+				NewRow[j] = new EditText(this, p1, p2, NULL);
+				p1.x += CellWidth;				p2.x += CellWidth;
+				}
+			etRows[i] = NewRow;
+			}
+		else {							//memory allocation error
+			cCols = nCols;
+			//DEBUG: we should warn the user that not all cells are available
+			//   or even better we remove some columns up to i rows
+			return false;
+			}
+		p1.x = p2.x = (CellWidth + etRows[0][cCols-1]->GetX());
+		p2.x += CellWidth;						p2.y += CellHeight;
+		p1.y += CellHeight;
+		}
+	cCols = nCols;
+	return true;
+}
+
+bool
+SpreadData::AddRows(int nRows)
+{
+	int i, j, x;
+	POINT p1, p2;
+	EditText ***NewRows;
+
+	if (nRows <= cRows) return false;
+	i = cRows-1;
+	if(etRows && etRows[i] && etRows[i][0]) {
+		x = p1.x = p2.x = etRows[i][0]->GetX();	p1.y = etRows[i][0]->GetY();
+		p1.y += CellHeight;						p2.y = p1.y + CellHeight;
+		p2.x += CellWidth;
+		}
+	else 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, p1, p2, NULL);
+				p1.x += CellWidth;				p2.x += CellWidth;
+				}
+			}
+		else {							//memory allocation error
+			cRows = i-1;
+			return false;
+			}
+		p1.y += CellHeight;						p2.y += CellHeight;
+		p1.x = p2.x = x;						p2.x += CellWidth;
+		}
+	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(bUndo) Undo.DataObject(Disp, w, this, 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) {
+		sprintf(TmpTxt, "%d00", nRows);
+		w->oGetTextExtent(TmpTxt, 0, &i, &j);
+		if(i > FirstWidth) FirstWidth = i;
+		Disp->Command(CMD_SETSCROLL, 0L, w);
+		}
+	rcCopy.left = rcCopy.right = 0;
+	rcCopy.right = cCols;
+	rcCopy.bottom = cRows;
+	return RetVal;
+}
+
+void
+SpreadData::DoPlot(anyOutput *o)
+{
+	RECT rc;
+	int i, j, r, c;
+	AccRange *ar;
+
+	if(!w || !Disp) return;
+	w->Erase(0x00d8d8d8L);					w->ActualSize(&rc);
+	r_disp = (rc.bottom-rc.top)/CellHeight+1;
+	c_disp = (rc.right-rc.left)/CellWidth+1;
+	Disp->ShowGrid(CellWidth, CellHeight, FirstWidth);
+	rc.top = w->MenuHeight;					rc.bottom = rc.top + CellHeight;
+	for(i = 0; i < (cRows - Disp->ssOrg.y) && i < r_disp; i++) {
+		rc.left = FirstWidth;				rc.right = rc.left + CellWidth;
+		rc.top += CellHeight;				rc.bottom += CellHeight;
+		for(j = 0; j< (cCols - Disp->ssOrg.x) && j < cCols; 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]->Redraw(w, false);
+				}
+			rc.left += CellWidth;			rc.right += CellWidth;
+			}
+		}
+	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 
+				&& c < (c_disp + Disp->ssOrg.x) && etRows[r] && etRows[r][c])
+				etRows[r][c]->Mark(w, etRows[r][c] != CurrText ? 2 : 3);
+			}
+		delete (ar);
+		}
+	if(c_range) InitCopy(0, 0L, w);			//move animated rectangle
+	w->ActualSize(&rc);				w->UpdateRect(&rc, false);
+	if(err_msg) {
+		ErrorBox(err_msg);	err_msg = 0L;
+		}
+}
+
+bool
+SpreadData::DelRange()
+{
+	AccRange *ar;
+	int r, c;
+
+	if(m_range && (ar = new AccRange(m_range)) && ar->GetFirst(&c, &r)) {
+		Undo.DataObject(Disp, w, this, 0L);
+		for( ; ar->GetNext(&c, &r); ) {
+			if(r < cRows && c < cCols){
+				if(CurrText && r == currpos.y && c == currpos.x) {
+					CurrText->Update(2, 0L, 0L);
+					CurrText = 0L;
+					}
+				if(etRows[r][c] && etRows[r][c]->text){
+					etRows[r][c]->SetText("");
+					}
+				}
+			}
+		delete (ar);	HideTextCursor();
+		}
+	HideMark(false);
+	return true;
+}
+
+bool
+SpreadData::PasteRange(int cmd, char *txt)
+{
+	AccRange *cr;
+	int r, c;
+	RECT mrk_range;
+
+	if(new_mark && m_range && (cr = new AccRange(m_range)) && cr->GetFirst(&c, &r)) {
+		cr->BoundRec(&mrk_range);
+		for( ; cr->GetNext(&c, &r); ) 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_XML: ReadXML(0L, (unsigned char*)txt, FF_XML);	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_XML:
+			return ReadXML(0L, (unsigned char*)txt, FF_XML);
+		case CMD_PASTE_CSV:
+			return ReadData(0L, (unsigned char*)txt, FF_CSV);
+		}
+	return false;
+}
+
+bool
+SpreadData::InitCopy(int cmd, void *tmpl, anyOutput *o)
+{
+	int r, c;
+	AccRange *ar;
+	RECT rc_band;
+	bool bRet = false;
+
+	if(cmd) {
+		bCopyCut = (cmd == CMD_CUT);
+		if(rcCopy.right > cCols) rcCopy.right = cCols;
+		if(rcCopy.bottom > cRows) rcCopy.bottom = cRows;
+		new_mark = false;
+		if(m_range && m_range[0]) {
+			if(c_range) free(c_range);		c_range = strdup(m_range);
+			if(c_range && (ar = new AccRange(c_range))) {
+				ar->BoundRec(&rcCopy);
+				delete ar;			bRet = true;;
+				}
+			}
+		else if(GetCopyRange(&rcCopy, this)){
+			//The range is stored in TmpTxt
+			if(!TmpTxt[0]) return false;
+			if(m_range) free(m_range);		if(c_range) free(c_range);
+			c_range = strdup(TmpTxt);		m_range = strdup(TmpTxt);
+			DoPlot(o);		bRet = true;
+			}
+		}
+	if(bRet || !cmd) {		//calculate animated mark
+		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(etRows[r][c]){
+			rc_band.right = etRows[r][c]->GetX()+CellWidth;
+			rc_band.bottom = etRows[r][c]->GetY()+CellHeight;
+			ShowCopyMark(o, &rc_band, 1);
+			}
+		}
+	return bRet;
+}
+
+bool
+SpreadData::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	int i;
+	static int move_cr = CMD_CURRDOWN;
+	MouseEvent *mev;
+	POINT p, cp;
+	
+	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) {
+			mpos2dpos(&p, &cp);
+			if(cp.y >= cRows || cp.x >= cCols || cp.y < 0 || cp.x < 0) return false;
+			}
+		switch (mev->Action) {
+		case MOUSE_LBDOWN:
+			bActive = true;
+			if(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_LBDOUBLECLICK:		case MOUSE_MOVE:
+			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(!m_range && CurrText) {
+					CurrText->Update(2, w, &p);		 CurrText = 0L;	
+					}
+				if(p.x < FirstWidth || p.y < (w->MenuHeight+CellHeight)) {
+					i = sprintf(TmpTxt, "%s%d:", Int2ColLabel(p.x < FirstWidth ? 0 : currpos.x, false),
+						p.y < (w->MenuHeight+CellHeight) ? 0 : currpos.y+1);
+					sprintf(TmpTxt+i, "%s%d", Int2ColLabel(p.x < FirstWidth ? cCols-1 : cp.x, false), 
+						p.y < (w->MenuHeight+CellHeight) ? cRows : cp.y+1);
+					}
+				else {
+					i = sprintf(TmpTxt, "%s%d:", Int2ColLabel(currpos.x, false), currpos.y+1);
+					sprintf(TmpTxt+i, "%s%d", Int2ColLabel(cp.x, false), cp.y+1);
+					}
+				if(!CurrText)MarkRange(TmpTxt);
+				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.x < FirstWidth || p.y < (w->MenuHeight+CellHeight)) {
+					if(p.x < FirstWidth) {
+						currpos.x = 0;	cp.x = cCols-1;
+						}
+					if(p.y < (w->MenuHeight+CellHeight)) {
+						currpos.y = 0;	cp.y = cRows-1;
+						}
+					i = sprintf(TmpTxt, "%s%d:", Int2ColLabel(currpos.x, false), currpos.y+1);
+					sprintf(TmpTxt+i, "%s%d", Int2ColLabel(cp.x, false), cp.y+1);
+					MarkRange(TmpTxt);
+					currpos2.x = cp.x;		currpos2.y = cp.y + 1;
+					}
+				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:
+		if(PasteRange(cmd, (char*)tmpl)) return Disp->Command(CMD_SETSCROLL, 0L, w);
+		return false;
+	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:
+		err_msg = (char*)tmpl;
+		break;
+	case CMD_CLEAR_ERROR:
+		err_msg = 0L;
+		break;
+	case CMD_TOOLMODE:
+		HideMark(true);
+		if(CurrText) CurrText->Update(2, w, 0L);
+		if(CurrText) CurrText->Update(1, w, 0L);
+		break;
+	case CMD_UPDHISTORY:
+		if(w) w->FileHistory();
+		break;
+	case CMD_MRK_DIRTY:
+		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;		DoPlot(o);
+				}
+			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;	DoPlot(o);
+				}
+			if(currpos.x < Disp->ssOrg.x) {
+				Disp->ssOrg.x = currpos.x;		DoPlot(o);
+				}
+			else if(currpos.x > (Disp->ssOrg.x + c_disp -3)) {
+				Disp->ssOrg.x = currpos.x - (c_disp-3);
+				if(Disp->ssOrg.x < 0) Disp->ssOrg.x = 0;	DoPlot(o);
+				}
+			currpos2.x = currpos.x;		currpos2.y = currpos.y;
+			i = *((int*)tmpl);
+			if(i == 27) return Command(CMD_TOOLMODE, tmpl, o);
+			if(i !=3 && i != 22 && i != 24) HideMark(true);	//Do not hide upon ^C, ^V, ^X
+			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);;
+			}
+		break;
+	case CMD_SHIFTUP:
+		currpos2.y -= 2;
+	case CMD_SHIFTDOWN:
+		currpos2.y ++;
+		if(currpos2.y >= cRows) currpos2.y --;		if(currpos2.y < 0) currpos2.y ++;
+		//mark rectangular range
+		i = sprintf(TmpTxt, "%s%d:", Int2ColLabel(currpos.x, false), currpos.y+1);
+		sprintf(TmpTxt+i, "%s%d", Int2ColLabel(currpos2.x, false), currpos2.y+1);
+		MarkRange(TmpTxt);
+		break;
+	case CMD_SHIFTRIGHT:	case CMD_SHIFTLEFT:
+		if(!m_range && CurrText && CurrText->Command(cmd, w, this)) break;
+		if(cmd == CMD_SHIFTLEFT) currpos2.x --;
+		else currpos2.x ++;
+		if(currpos2.x >= cCols) currpos2.x --;		if(currpos2.x < 0) currpos2.x ++;
+		//mark rectangular range
+		i = sprintf(TmpTxt, "%s%d:", Int2ColLabel(currpos.x, false), currpos.y+1);
+		sprintf(TmpTxt+i, "%s%d", Int2ColLabel(currpos2.x, false), currpos2.y+1);
+		MarkRange(TmpTxt);
+		break;
+	case CMD_CURRIGHT:		case CMD_CURRDOWN:
+		move_cr = cmd;
+	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);
+			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(cmd == CMD_CURRIGHT && c_disp > 3 && (currpos.x-Disp->ssOrg.x) >= (c_disp-3)) {
+			Disp->ssOrg.x ++;		currpos.y ++;			DoPlot(o);
+			if(CurrText) 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);
+			}
+		if(err_msg) ErrorBox(err_msg);	err_msg = 0L;
+		HideMark(false);
+		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);
+			}
+		return true;
+	case CMD_DELETE:
+		if(m_range) DelRange();
+		else if(CurrText) return CurrText->Command(cmd, o, this);
+		return true;
+	case CMD_QUERY_COPY:		case CMD_CUT:
+		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];
+			}
+		break;
+	case CMD_TEXTSIZE:
+		if(tmpl){
+			CellHeight = *((int*)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_REDRAW:	case CMD_DOPLOT:
+		DoPlot(o);
+		break;
+	case CMD_FILLRANGE:
+		FillSsRange(this, &m_range);
+		DoPlot(o);
+		break;
+	}
+	return true;
+}
+
+bool
+SpreadData::ReadXML(char *file, unsigned char *buffer, int type)
+{
+	int i, row, col, tag, cpgr, spgr;
+	bool bContinue, bRet = false;
+	ReadCache *XMLcache;
+	POINT pt, mov;
+	char TmpTxt[1024], *tmp_range;
+	unsigned char *pgr = 0L;
+
+	if(file) {
+		if(!(XMLcache = new ReadCache())) return false;
+		if(! XMLcache->Open(file)) {
+			delete XMLcache;
+			sprintf(TmpTxt, "Error open file\n\"%s\"", file);
+			ErrorBox(TmpTxt);
+			return false;
+			}
+		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)) tag = 1;
+		else if(!strcmp("<pos1", TmpTxt)) tag = 2;
+		else if(!strcmp("<pos2", TmpTxt)) tag = 3;
+		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);
+			OpenGraph(Disp, 0L, pgr);
+			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;
+				}
+			else if(tag ==3) {
+				cp_src_rec.right = col;		cp_src_rec.bottom = row;
+				}
+			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 && ('<' != (TmpTxt[i] =XMLcache->Getc())); i++);
+					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, currpos.x-mov.x, currpos.y-mov.y);
+							}
+						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;
+			sprintf(TmpTxt, "Error open file\n\"%s\"", file);
+			ErrorBox(TmpTxt);
+			return false;
+			}
+		if(!Init(1, 1)) goto TSVError;
+		etRows[0][0]->SetText("");
+		}
+	else if(buffer && type == FF_TSV) {
+		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;
+				}
+			}while(TmpTxt[0] && TmpTxt[0] < 33);
+		for(i = 1; (TmpTxt[i] = c = TSVcache->Getc())>=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;
+				}
+			}
+		}while(!TSVcache->IsEOF());
+	bRet = true;
+TSVError:
+	TSVcache->Close();
+	delete TSVcache;
+	return bRet;
+}
+
+bool 
+SpreadData::MemList(unsigned char **ptr, int type)
+{
+	int i, j, k, nc, nl, cb = 0; 
+	long cbd = 0, size;
+	char tmptxt[8000];
+	double val;
+	*ptr = (unsigned char *)malloc(size = 10000);
+	unsigned char *tmpptr;
+	bool bLimit = true;
+
+	if(!(*ptr))return false;
+	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 = sprintf((char*)*ptr, "ID;\n");
+	else if(type == FF_XML) {
+		cbd = sprintf((char*)*ptr, "<?xml version=\"1.0\"?><!DOCTYPE spreadsheet-snippet>"
+		"<spreadsheet-snippet rows=\"%d\" columns=\"%d\" >\n", cRows, cCols);
+		if(rcCopy.left || rcCopy.top || rcCopy.bottom || rcCopy.right){
+			cbd += sprintf((char*)*ptr+cbd, " <pos1 row=\"%d\" "
+				"column=\"%d\"></pos1>\n", rcCopy.top, rcCopy.left);
+			cbd += sprintf((char*)*ptr+cbd, " <pos2 row=\"%d\" "
+				"column=\"%d\"></pos2>\n", rcCopy.bottom, rcCopy.right);
+			}
+		}
+	else if(type == FF_RLW) {
+		cbd = sprintf((char*)*ptr, "<?xml version=\"1.0\"?><!DOCTYPE RLPlot-workbook>\n"
+		"<RLPlot-data rows=\"%d\" columns=\"%d\" >\n", cRows, cCols);
+		}
+	for(nl =0, i = rcCopy.top; i <= rcCopy.bottom; i++, nl++) {
+		for(nc = 0, j = rcCopy.left; j <= rcCopy.right; cb = 0, j++, nc++) {
+			switch (type) {
+			case FF_TSV:
+				if(nl || nc) cb = sprintf(tmptxt,"%s", nc ? "\t" : "\n");
+				if(etRows[i] && etRows[i][j] && etRows[i][j]->text){
+					for(k = 0; k < 7990 && (etRows[i][j]->text[k]); k++) 
+						tmptxt[cb++] = etRows[i][j]->text[k];
+					tmptxt[cb] = 0;
+					}
+				break;
+			case FF_SYLK:
+				if(etRows[i] && etRows[i][j] && etRows[i][j]->text){
+					cb = sprintf(tmptxt, "C;Y%d;X%d;K", nl+1, nc+1);
+					if(etRows[i][j]->GetValue(&val)){
+						cb += sprintf(tmptxt+cb, "%lf", val);
+						while(tmptxt[cb-1] == '0') tmptxt[--cb] = 0;
+						if(tmptxt[cb-1] == '.') tmptxt[--cb] = 0;
+						cb += sprintf(tmptxt+cb, "\n");
+						}
+					else {
+						etRows[i][j]->GetItem(TmpTxt, 258);
+						cb += sprintf(tmptxt+cb, "%s\n", TmpTxt);
+						if(cb >= 267) bLimit = false;
+						}
+					}
+				break;
+			case FF_RLW:	case FF_XML:
+				if(etRows[i] && etRows[i][j] && etRows[i][j]->text){
+					cb = sprintf(tmptxt, " <cell row=\"%d\" column=\"%d\" >\n", nl+1, nc+1);
+					cb += sprintf(tmptxt+cb, "  <text>");
+					for(k = 0; k < 7880 && (etRows[i][j]->text[k]); k++) 
+						tmptxt[cb++] = etRows[i][j]->text[k];
+					cb += sprintf(tmptxt+cb, "</text>\n </cell>\n");
+					}
+				break;
+				}
+			if((cbd+cb+100) > size){
+				if(tmpptr = (unsigned char*)realloc(*ptr, size+10000)) {
+					*ptr = tmpptr;					size += 10000;
+					}
+				else return true;	//not all but something on clipboard
+				}
+			memcpy(*ptr+cbd, tmptxt, cb+1);
+			cbd += cb;
+			}
+		if(type == FF_SYLK) {
+			if(!bLimit) {
+				cbd += sprintf((char*)*ptr+cbd, "C;Y%d;X1;K\"long strings were"
+				" truncated to 256 characters due to limitations of the SYLK format!\"\n", i+2);
+				}
+			sprintf((char*)*ptr+cbd, "E\n");
+			}
+		else if(type == FF_TSV) sprintf((char*)*ptr+cbd,"\n");
+		}
+	if(type == FF_XML) sprintf((char*)*ptr+cbd,"</spreadsheet-snippet>\n");
+	else if(type == FF_RLW){
+		Disp->Command(CMD_WRITE_GRAPHS, ptr, (anyOutput*)&cbd);
+		sprintf((char*)*ptr+cbd,"</RLPlot-data>\n");
+		//note: cbd may be greater than size !
+		}
+	return true;
+}
+
+void SpreadMain(bool show)
+{
+	static SpreadData *w = 0L;
+
+	if(show) {
+		w = new SpreadData();
+		if(!w ||!(w->Init(10, 10))){
+			delete w;
+			w = 0L;
+			}
+		do_formula(w, 0L);			//init mfcalc
+		}
+	else if (w) {
+		delete(w);
+		}
+}
+
+

-- 
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