[rlplot] 03/23: Imported Upstream version 1.0

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

    Imported Upstream version 1.0
---
 Axes.cpp         |   49 +-
 Export.cpp       |   27 +-
 Fileio.cpp       |  296 ++++++++---
 ODbuttons.cpp    |  157 ++++--
 PlotObs.cpp      | 1228 +++++++++++++++++++++++++++++++++----------
 PropertyDlg.cpp  | 1260 ++++++++++++++++++++++++++++++--------------
 QT_Spec.cpp      |  585 +++++++++++++++------
 QT_Spec.h        |    5 +
 RLPLOT.ICO       |  Bin 766 -> 4286 bytes
 RLPLOT.RC        |   79 +--
 RLPlot-Icons.zip |  Bin 0 -> 38437 bytes
 RLPlot.bmp       |  Bin 630 -> 3126 bytes
 RLPlot.xpm       |  254 +++++++--
 TheDialog.cpp    |  280 +++++-----
 TheDialog.h      |   19 +-
 UtilObj.cpp      |  613 ++++++++++++++-------
 Utils.cpp        |   79 +++
 Version.h        |    3 +-
 WinSpec.cpp      |  154 ++++--
 WinSpec.h        |    1 +
 exprlp.cpp       |   47 +-
 mfcalc.cpp       | 1548 ++++++++++++++++++++++++++++++++++++++++--------------
 mfcalc.y         | 1077 +++++++++++++++++++++++++++++--------
 rlp_math.cpp     |  516 +++++++++++++++++-
 rlplot.cpp       |  406 ++++++++++----
 rlplot.h         |  244 ++++++---
 rlplot.spec      |    6 +-
 spreadwi.cpp     | 1092 ++++++++++++++++++++++++++++----------
 28 files changed, 7503 insertions(+), 2522 deletions(-)

diff --git a/Axes.cpp b/Axes.cpp
index 320ff5b..bc5a4a0 100755
--- a/Axes.cpp
+++ b/Axes.cpp
@@ -544,8 +544,14 @@ Tick::Command(int cmd, void *tmpl, anyOutput *o)
 	MouseEvent *mev;
 	TextDEF *LabelDef;
 	GraphObj **tmpPlots;
+	AxisDEF *axis;
 
 	switch(cmd){
+	case CMD_SET_AXDEF:
+		if(axis = (AxisDEF*)tmpl) {
+			flags = (flags & AXIS_MINORTICK) | axis->flags;
+			}
+		break;
 	case CMD_FLUSH:
 		if(Grid) DeleteGO(Grid);		Grid = 0L;
 		if(label) DeleteGO(label);		label = 0L;
@@ -620,11 +626,7 @@ Tick::Command(int cmd, void *tmpl, anyOutput *o)
 		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);
+				o->ShowMark(this, MRK_GODRAW);
 				return true;
 				}
 			break;
@@ -1262,11 +1264,7 @@ Axis::Command(int cmd, void *tmpl, anyOutput *o)
 				}
 			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);
+				o->ShowMark(this, MRK_GODRAW);
 				return true;
 				}
 			break;
@@ -1276,14 +1274,25 @@ Axis::Command(int cmd, void *tmpl, anyOutput *o)
 			if(Ticks[i] && Ticks[i]->Command(cmd, tmpl, o)) return true;
 		return false;
 	case CMD_SET_AXDEF:
-		if(tmpl) {
+		if(axis = (AxisDEF*)tmpl) {
 			if(axis && axis->owner == (void*)this) {
 				if(axis->breaks) free(axis->breaks);
 				free(axis);
 				}
+			if(Ticks) for(i = 0; i < NumTicks; i++)
+				if(Ticks[i]) {
+					Ticks[i]->Command(cmd, tmpl, o);
+					Ticks[i]->Command(CMD_SET_GRIDTYPE, (void*) &gl_type, 0L);
+					Ticks[i]->Command(CMD_TLB_TXTDEF, &tlbdef, 0L);
+					if(axis->flags & AXIS_GRIDLINE) Ticks[i]->Command(CMD_SET_GRIDLINE, &GridLine, 0L);
+					Ticks[i]->SetSize(SIZE_TICK_ANGLE, tick_angle);
+					Ticks[i]->SetSize(SIZE_AXIS_TICKS, sizAxTick);
+					Ticks[i]->SetSize(SIZE_LB_XDIST, tlbdist.fx);
+					Ticks[i]->SetSize(SIZE_LB_YDIST, tlbdist.fy);
+					Ticks[i]->Command(CMD_TICK_TYPE, &tick_type, 0L);
+					}
+			return true;
 			}
-		axis = (AxisDEF*)tmpl;
-		if(axis) return true;
 		return false;
 	case CMD_CAN_DELETE:
 		if(axis->owner == (void*)this) return true;
@@ -1297,18 +1306,16 @@ Axis::Command(int cmd, void *tmpl, anyOutput *o)
 	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(DeleteGOL((GraphObj***) &Ticks, NumTicks, (GraphObj *)tmpl, o)) 
+			return parent->Command(CMD_REDRAW, 0L, o);
 		if(tmpl == (void*)axisLabel) {
 			Undo.DeleteGO((GraphObj**)(&axisLabel), 0L, o);
 			return parent->Command(CMD_REDRAW, 0L, o);
 			}
+		if(Ticks && NumTicks) for(i = 0; i < NumTicks; i++){ 
+			if(Ticks[i] && Ticks[i]->Command(cmd, tmpl, o))
+				return parent->Command(CMD_REDRAW, 0L, o);
+			}
 		break;
 	case CMD_MUTATE:
 		if(!parent || !(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
diff --git a/Export.cpp b/Export.cpp
index 4470367..7b4b7f5 100755
--- a/Export.cpp
+++ b/Export.cpp
@@ -799,14 +799,26 @@ ExportSVG::oSolidLine(POINT *p)
 bool
 ExportSVG::oTextOut(int x, int y, char *txt, int cb)
 {
-	int h, ix, iy;
+	int i, h, ix, iy;
 	char tmptxt[140], *nt;
 
-	h = TxtSet.iSize;
+	if(!txt || !txt[0]) return false;
+	else h = TxtSet.iSize;
 	if(TxtSet.Align & TXA_VCENTER) iy = y + h/3;
 	else if(TxtSet.Align & TXA_VBOTTOM) iy = y;
-	else iy = y + iround(h*.8f);
+	else iy = y + iround(h * 0.8);
 	ix = x;
+	if((TxtSet.Style & TXS_SUB) || (TxtSet.Style & TXS_SUPER)) h = iround((double)TxtSet.iSize * 0.8);
+	if(TxtSet.Style & TXS_SUB) {
+		if((TxtSet.Align & TXA_VCENTER) == TXA_VCENTER) iy +=un2iy(TxtSet.fSize*0.4);
+		else if((TxtSet.Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy += un2iy(TxtSet.fSize*0.2);
+		else if((TxtSet.Align & TXA_VTOP) == TXA_VTOP) iy += un2iy(TxtSet.fSize*.6);
+		}
+	else if(TxtSet.Style & TXS_SUPER) {
+		if((TxtSet.Align & TXA_VCENTER) == TXA_VCENTER) iy -= un2iy(TxtSet.fSize*0.4);
+		else if((TxtSet.Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy -= un2iy(TxtSet.fSize*0.6);
+		else if((TxtSet.Align & TXA_VTOP) == TXA_VTOP) iy -= un2iy(TxtSet.fSize*0.2);
+		}
 	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);
@@ -825,10 +837,17 @@ ExportSVG::oTextOut(int x, int y, char *txt, int cb)
 	sprintf(tmptxt, " fill:%s; stroke:%s; ", ColName(TxtSet.ColTxt),
 		ColName(TxtSet.ColTxt));
 	AddToOutput(tmptxt);
-	sprintf(tmptxt, "font-size:%d; text-anchor:%s \">", TxtSet.iSize, 
+	sprintf(tmptxt, "font-size:%d; text-anchor:%s \">", h, 
 		(TxtSet.Align & TXA_HRIGHT) ? "end" : (TxtSet.Align & TXA_HCENTER) ? "middle":"start");
 	AddToOutput(tmptxt);
 	nt = str2xml(txt);
+	if((TxtSet.Style & TXS_SUB) || (TxtSet.Style & TXS_SUPER)) {
+		for(i = strlen(nt)+6, nt[i+1]=0; i > 5; i--) {
+			nt[i] = nt[i-6];
+			}
+		nt[0] = '&';		nt[1] = 'n';		nt[2] = 'b';
+		nt[3] = 's';		nt[4] = 'p';		nt[5] = ';';
+		}
 	if((strlen(indent)+strlen(nt)+strlen(output)) <110) strcat(output, nt);
 	else {
 		fprintf(oFile, "%s%s\n", indent, output);
diff --git a/Fileio.cpp b/Fileio.cpp
index 50c52e2..e022eb8 100755
--- a/Fileio.cpp
+++ b/Fileio.cpp
@@ -38,6 +38,7 @@ extern GraphObj *CurrGO;			//Selected Graphic Objects
 extern Default defs;
 extern int dlgtxtheight;
 extern char TmpTxt[];
+extern int cPlots;
 
 static notary *Notary = 0L;
 static ReadCache *Cache = 0L;
@@ -81,8 +82,14 @@ int OpenOutputFile(char *file)
 		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));
+			""SZ_VERSION" for %s \n;Date/Time: %s ",
+			file,  
+#ifdef _WINDOWS
+			"Windows",
+#else
+			"Qt",
+#endif
+			ctime(&ti));
 		}
 	return iFile;
 openerr:
@@ -194,6 +201,32 @@ void WriteTypFpLst3D(fPOINT3D *ptr, long count)
 		else if(n < count) c += sprintf(tmp+c, "\n");
 		AddStringToOutput(tmp, c);
 		}
+}
+
+static char * esc_str = 0L;
+static int esc_str_size = 0;
+int WriteEscString(char *txt, char *buff, int cb)
+{
+	int i, j, l, lim = 60;
+
+	if(!txt || !txt[0]) return 0;
+	l = strlen(txt);
+	if((l+10) > esc_str_size) esc_str = (char*)realloc(esc_str, esc_str_size = (l+10));
+	j = 0;	esc_str[j++] = '"';
+	for(i = 0; txt[i]; i++) {
+		switch(txt[i]) {
+		case '\\':	j += sprintf(esc_str+j, "\\\\");	break;
+		case '\n':	j += sprintf(esc_str+j, "\\n");		break;
+		default:	esc_str[j++] = txt[i];
+			}
+		if(j > esc_str_size -2) esc_str = (char*)realloc(esc_str, (esc_str_size += 20));
+		if(j > lim) {
+			j += sprintf(esc_str+j, "\"\n   \"");
+			lim += 60;
+			}
+		}
+	j += sprintf(esc_str+j, "\"\n");
+	return sprintf(buff+cb, "%s", esc_str);
 }
 
 bool ExecOutput(long id, char *Class, descIO *Desc)
@@ -222,7 +255,7 @@ bool ExecOutput(long id, char *Class, descIO *Desc)
 			cb += sprintf(buff+cb," %d\n", *(int*)Desc[i].ptr);
 			break;
 		case typNZLFLOAT:
-			if(*((double*)Desc[i].ptr) < 0.0001) {
+			if(fabs(*((double*)Desc[i].ptr)) < 1.0e-7) {
 				cb = last;
 				break;
 				}
@@ -351,7 +384,7 @@ bool ExecOutput(long id, char *Class, descIO *Desc)
 			break;
 		case typTEXT:
 			if(!*(char**)(Desc[i].ptr)) cb = last;
-			else cb += sprintf(buff+cb, "\"%s\"\n", *(char**)(Desc[i].ptr));
+			else cb += WriteEscString(*(char**)(Desc[i].ptr), buff, cb);
 			break;
 		case typTXTDEF:
 		case typPTRTXTDEF:
@@ -581,7 +614,8 @@ bool ExecInput(descIO *Desc)
 						if(!*(GraphObj***)(Desc[j].ptr)){
 							*(GraphObj***)(Desc[j].ptr) = (GraphObj**)calloc(il, sizeof(GraphObj*));
 							}
-						if((gobs = *(GraphObj***)(Desc[j].ptr))){
+						if((gobs = *(GraphObj***)(Desc[j].ptr))){
+							i += 4;
 							while(tmp[i-1] != '{') i++; while(tmp[i-1] < 33) i++;
 							strcat(tmp, " ");
 							for( ;il >0; il--) {
@@ -641,7 +675,7 @@ bool ExecInput(descIO *Desc)
 						}
 					break;
 				case typTEXT:
-					for(i = strlen(tmp); i > 0 &&(tmp[i-1] < 33 || (tmp[i-1] == 
+					for(i = strlen(tmp); i > 0 &&(tmp[i-1] < 32 || (tmp[i-1] == 
 						'"' && tmp[i-2] != '\\') ||	(tmp[i-1] == '\\' && 
 						(mlines = true))); tmp[--i] = 0);
 					for(i = 0; tmp[i] && (tmp[i] < 33 || tmp[i] == '"'); i++);
@@ -694,7 +728,8 @@ bool ExecInput(descIO *Desc)
 bool SaveGraphAs(GraphObj *g)
 {
 	char *name = 0L;
-	int i;
+	int i;
+	bool bRet = true;
 
 	if(Notary || !g) {
 		ErrorBox("Output pending or\nno graph.");
@@ -722,9 +757,10 @@ bool SaveGraphAs(GraphObj *g)
 			}
 		else ErrorBox("Open failed for\noutput file.");
 		CloseOutputFile();
-		}
+		}
+	else bRet = false;
 	if(Notary) delete Notary;	Notary = 0L;
-	return true;
+	return bRet;
 }
 
 char *GraphToMem(GraphObj *g, long *size)
@@ -821,10 +857,10 @@ bool OpenGraph(GraphObj *root, char *name, unsigned char *mem)
 		hv = HashValue(tmp);
 		switch(hv) {
 		case 3895:		go = new Axis(FILE_READ);			break;
-		case 886:		go = new Bar(FILE_READ);			break;
+		case 7496002:	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 7892802:	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;
@@ -845,6 +881,7 @@ bool OpenGraph(GraphObj *root, char *name, unsigned char *mem)
 		case 386257:	go = new ellipse(FILE_READ);		break;
 		case 95680:		go = new mLabel(FILE_READ);			break;
 		case 4819316:	go = new PlotScatt(FILE_READ);		break;
+		case 117848:	go = new xyStat(FILE_READ);			break;
 		case 15935312:	go = new BubblePlot(FILE_READ);		break;
 		case 247376:	go = new BoxPlot(FILE_READ);		break;
 		case 317384:	go = new StackBar(FILE_READ);		break;
@@ -873,7 +910,8 @@ bool OpenGraph(GraphObj *root, char *name, unsigned char *mem)
 		case 1053744:	go = new FreqDist(FILE_READ);		break;
 		case 68748:		go = new Legend(FILE_READ);			break;
 		case 66800:		go = new Grid3D(FILE_READ);			break;
-		case 967843:	go = new DefsRW(FILE_READ);			break;
+		case 967843:	go = new DefsRW(FILE_READ);			break;
+		case 66848:		go = new Func3D(FILE_READ);			break;
 		default:
 			sprintf(debug, "Object %ld in file\n(Class = \"%s\")\nhash #%d\nis unknown.",
 				id, tmp, hv);
@@ -891,6 +929,7 @@ bool OpenGraph(GraphObj *root, char *name, unsigned char *mem)
 	Cache->Close();
 	if((go = Notary->PopGO(lid))) {
 		go->Command(CMD_SET_DATAOBJ, 0L, 0L);
+		delete Notary;		Notary = 0L;
 		if(root->Id == GO_PAGE) {
 			if(go->Id == GO_PAGE){
 				if(!(root->parent->Command(CMD_DROP_GRAPH,(void *)go, 0L))) DeleteGO(go);
@@ -902,10 +941,8 @@ bool OpenGraph(GraphObj *root, char *name, unsigned char *mem)
 			}
 		if(go) go->Command(CMD_FILENAME, name, 0L);
 		}
-	delete Notary;
-	Notary = 0L;
-	delete Cache;
-	Cache = 0L;
+	if(Notary) delete Notary;		Notary = 0L;
+	delete Cache;					Cache = 0L;
 	return true;
 
 ReadErr:
@@ -1241,7 +1278,9 @@ Bar::FileIO(int rw)
 		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);
+		size = parent ? parent->GetSize(SIZE_BAR): defs.GetSize(SIZE_BAR);
+		mo = 0L;
+		mrc.left = mrc.right = mrc.top = mrc.bottom = 0;
 		return true;
 	case FILE_READ:
 		ExecInput(Desc);
@@ -1331,13 +1370,13 @@ DataLine::FileIO(int rw)
 		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;
+		min.fx = min.fy = max.fx = max.fy = 0.0;
 		return true;
 	case FILE_READ:
 		ExecInput(Desc);
 		nPnt = i;
 		nPntSet = i-1;
-		if(file2)FileValues(file2, 1, 0.0f, 1.0f);
+		if(file2)FileValues(file2, 1, 0.0, 1.0);
 		else if(file1)FileValues(file1, 2, Start, fabs(Step) > defs.min4log ? Step : 1.0);
 		if(file1) free(file1);
 		if(file2) free(file2);
@@ -1776,6 +1815,7 @@ Line3D::FileIO(int rw)
 		{"ssRefX", typTEXT, &x_range, 0L},
 		{"ssRefY", typTEXT, &y_range, 0L},
 		{"ssRefZ", typTEXT, &z_range, 0L},
+		{"ssRef", typIPLST, &ssRef, &cssRef},
 		{"values", typLAST | typFPLST3D, &values, &nPts}};
 
 	switch(rw) {
@@ -1855,7 +1895,8 @@ mLabel::FileIO(int rw)
 	descIO Desc[] = {
 		{"moveable", typNZINT, &moveable, 0L},
 		{"Pos", typNZLFPOINT, &fPos, 0L},
-		{"Dist", typNZLFPOINT, &fDist, 0L},
+		{"Dist", typNZLFPOINT, &fDist, 0L},
+		{"lspc", typLFLOAT, &lspc, 0L},
 		{"Flags", typDWORD, &flags, 0L},
 		{"TxtDef", typTXTDEF, &TextDef, 0L},
 		{"Lines", typLAST | typOBJLST, &Lines, &nLines}};
@@ -1878,7 +1919,7 @@ mLabel::FileIO(int rw)
 		TextDef.Style = TXS_NORMAL;
 		TextDef.Font = FONT_HELVETICA;
 		TextDef.text = 0L;
-		undo_flags = 0L;
+		undo_flags = 0L;	lspc = 1.0;
 		curr_z = 0.0;		is3D = false;
 		return true;
 	case FILE_READ:
@@ -2115,7 +2156,8 @@ PlotScatt::RegGO(void *n)
 bool
 PlotScatt::FileIO(int rw)
 {
-	descIO Desc[] = {
+	descIO Desc[] = {
+		{"hide", typNZINT, &hidden, 0L},
 		{"Bounds", typFRECT, &Bounds, 0L},
 		{"DefSym", typNZINT, &DefSym, 0L},
 		{"baDist", typLFPOINT, &BarDist, 0L},
@@ -2163,6 +2205,51 @@ PlotScatt::FileIO(int rw)
 	return false;
 }
 
+bool
+xyStat::FileIO(int rw)
+{
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
+		{"Bounds", typFRECT, &Bounds, 0L},
+		{"DefSym", typNZINT, &DefSym, 0L},
+		{"baDist", typLFPOINT, &BarDist, 0L},
+		{"confi", typLFLOAT, &ci, 0L},
+		{"xRange", typTEXT, &xRange, 0L},
+		{"yRange", typTEXT, &yRange, 0L},
+		{"prefix", typTEXT, &case_prefix, 0L},
+		{"x_axis", typNZINT, &use_xaxis, 0L},
+		{"y_axis", typNZINT, &use_yaxis, 0L},
+		{"Bars", typOBJLST, &Bars, &nPoints},
+		{"Symbols", typOBJLST, &Symbols, &nPoints},
+		{"PL", typGOBJ, &TheLine, 0L},
+		{"ErrBars", typOBJLST, &Errors, &nPoints},
+		{"Labels", typLAST | typOBJLST, &Labels, &nPoints}};
+	int i;
+
+	switch(rw) {
+	case INIT_VARS:
+		//most initialistion is done by PlotScatt::FileIO
+		curr_data = 0L;
+		case_prefix = 0L;
+		ci = 95.0;
+		return true;
+	case FILE_READ:
+		nPoints = 0L;
+		ExecInput(Desc);
+		ForEach(FE_PARENT, 0L, 0L);
+		return true;
+	case FILE_WRITE:
+		if(TheLine) TheLine->FileIO(rw);
+		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->FileIO(rw);
+		if(Errors) for(i = 0; i < nPoints; i++) if(Errors[i]) Errors[i]->FileIO(rw);
+		if(Labels) for(i = 0; i < nPoints; i++) if(Labels[i]) Labels[i]->FileIO(rw);
+		if(Bars) for(i = 0; i < nPoints; i++) if(Bars[i]) Bars[i]->FileIO(rw);
+		return ExecOutput(Notary->RegisterGO(this), "xyStat", Desc);
+		}
+	return false;
+}
+
 void
 FreqDist::RegGO(void *n)
 {
@@ -2179,6 +2266,7 @@ FreqDist::FileIO(int rw)
 {
 	descIO Desc[] = {
 		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
 		{"ssRef", typTEXT, &ssRef, 0L},
 		{"x_axis", typNZINT, &use_xaxis, 0L},
 		{"y_axis", typNZINT, &use_yaxis, 0L},
@@ -2234,6 +2322,7 @@ Regression::FileIO(int rw)
 {
 	descIO Desc[] = {
 		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
 		{"Bounds", typFRECT, &Bounds, 0L},
 		{"xRange", typTEXT, &xRange, 0L},
 		{"yRange", typTEXT, &yRange, 0L},
@@ -2280,6 +2369,7 @@ bool
 BubblePlot::FileIO(int rw)
 {
 	descIO Desc[] = {
+		{"hide", typNZINT, &hidden, 0L},
 		{"Bounds", typFRECT, &Bounds, 0L},
 		{"x_axis", typNZINT, &use_xaxis, 0L},
 		{"y_axis", typNZINT, &use_yaxis, 0L},
@@ -2330,6 +2420,7 @@ PolarPlot::FileIO(int rw)
 {
 	descIO Desc[] = {
 		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
 		{"Bounds", typFRECT, &Bounds, 0L},
 		{"ang_offs", typLFLOAT, &offs, 0L},
 		{"Plots", typOBJLST, &Plots, (long*)&nPlots},
@@ -2366,6 +2457,7 @@ BoxPlot::RegGO(void *n)
 		if(Boxes) for(i = 0; i < nPoints; i++) if(Boxes[i]) Boxes[i]->RegGO(n);
 		if(Whiskers) for(i = 0; i < nPoints; i++) if(Whiskers[i]) Whiskers[i]->RegGO(n);
 		if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->RegGO(n);
+		if(Labels) for(i = 0; i < nPoints; i++) if(Labels[i]) Labels[i]->RegGO(n);
 		if(TheLine) TheLine->RegGO(n);
 		((notary*)n)->AddRegGO(this);
 		}
@@ -2374,14 +2466,22 @@ BoxPlot::RegGO(void *n)
 bool
 BoxPlot::FileIO(int rw)
 {
-	descIO Desc[] = {
+	descIO Desc[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"hide", typNZINT, &hidden, 0L},
 		{"Bounds", typFRECT, &Bounds, 0L},
-		{"boDist", typLFPOINT, &BoxDist, 0L},
+		{"xRange", typTEXT, &xRange, 0L},
+		{"yRange", typTEXT, &yRange, 0L},
+		{"prefix", typTEXT, &case_prefix, 0L},
+		{"boDist", typLFPOINT, &BoxDist, 0L},
+		{"ci_box", typNZLFLOAT, &ci_box, 0L},
+		{"ci_err", typNZLFLOAT, &ci_err, 0L},
 		{"x_axis", typNZINT, &use_xaxis, 0L},
 		{"y_axis", typNZINT, &use_yaxis, 0L},
 		{"Boxes", typOBJLST, &Boxes, &nPoints},
 		{"Whiskers", typOBJLST, &Whiskers, &nPoints},
 		{"Symbols", typOBJLST, &Symbols, &nPoints},
+		{"Labels", typOBJLST, &Labels, &nPoints},
 		{"Line", typLAST | typGOBJ, &TheLine, 0L}};
 	int i;
 
@@ -2393,6 +2493,7 @@ BoxPlot::FileIO(int rw)
 			sprintf(TmpTxt, "boxes (%s)", name);
 			free(name);		name=strdup(TmpTxt);
 			}
+		curr_data = 0L;
 		return true;
 	case FILE_READ:
 		return ExecInput(Desc);
@@ -2403,6 +2504,8 @@ BoxPlot::FileIO(int rw)
 			if(Whiskers[i]) Whiskers[i]->FileIO(rw);
 		if(Symbols) for(i = 0; i < nPoints; i++) 
 			if(Symbols[i]) Symbols[i]->FileIO(rw);
+		if(Labels) for(i = 0; i < nPoints; i++) 
+			if(Labels[i]) Labels[i]->FileIO(rw);
 		if(TheLine) TheLine->FileIO(rw);
 		return ExecOutput(Notary->RegisterGO(this), "BoxPlot", Desc);
 		}
@@ -2703,6 +2806,7 @@ Grid3D::RegGO(void *n)
 
 	if(n) {
 		if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->RegGO(n);
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->RegGO(n);
 		((notary*)n)->AddRegGO(this);
 		}
 }
@@ -2715,7 +2819,9 @@ Grid3D::FileIO(int rw)
 		{"Start", typPOINT3D, &start, 0L},
 		{"Step", typPOINT3D, &step, 0L},
 		{"Line", typLINEDEF, &Line, 0L},
-		{"lines", typLAST | typOBJLST, &lines, &nLines}};
+		{"Fill", typFILLDEF, &Fill, 0L},
+		{"lines", typOBJLST, &lines, &nLines},
+		{"planes", typLAST | typOBJLST, &planes, &nPlanes}};
 	int i;
 
 	switch(rw) {
@@ -2734,9 +2840,11 @@ Grid3D::FileIO(int rw)
 		ExecInput(Desc);
 		//now set parent in all children
 		if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->parent = this;
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->parent = this;
 		return true;
 	case FILE_WRITE:
 		if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->FileIO(rw);
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->FileIO(rw);
 		return ExecOutput(Notary->RegisterGO(this), "Grid3D", Desc);
 		}
 	return false;
@@ -2775,17 +2883,15 @@ Function::RegGO(void *n)
 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},
+		{"f_xy", typTEXT, &cmdxy, 0L},
+		{"param", typTEXT, &param, 0L},
 		{"DataLine", typLAST | typGOBJ, &dl, 0L}};
-	int i, j;
-
+
 	switch(rw) {
 	case SAVE_VARS:
 		return SaveVarGO(Desc);
@@ -2800,31 +2906,11 @@ Function::FileIO(int rw)
 		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;
 }
@@ -2843,7 +2929,6 @@ FitFunc::RegGO(void *n)
 bool
 FitFunc::FileIO(int rw)
 {
-	char *tmp_cmdxy = 0L, *tmp_parxy = 0L;
 	descIO Desc[] = {
 		{"ssXref", typTEXT, &ssXref, 0L},
 		{"ssYref", typTEXT, &ssYref, 0L},
@@ -2854,10 +2939,10 @@ FitFunc::FileIO(int rw)
 		{"chi2", typNZLFLOAT, &chi2, 0L},
 		{"maxiter", typNZINT, &maxiter, 0L},
 		{"Line", typLINEDEF, &Line, 0L},
-		{"f_xy", typTEXT, &tmp_cmdxy, 0L},
-		{"p_xy", typTEXT, &tmp_parxy, 0L},
+		{"f_xy", typTEXT, &cmdxy, 0L},
+		{"p_xy", typTEXT, &parxy, 0L},
 		{"Symbols", typLAST | typOBJLST, &Symbols, &nPoints}};
-	int i, j;
+	int i;
 
 	switch(rw) {
 	case SAVE_VARS:
@@ -2875,28 +2960,10 @@ FitFunc::FileIO(int rw)
 		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);
 		}
@@ -3105,7 +3172,7 @@ Axis::FileIO(int rw)
 			}
 		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;
+		if(Ticks) for(i = 0; i < NumTicks; i++) if(Ticks[i]) Ticks[i]->parent = this;
 		return true;
 	case FILE_WRITE:
 		//do all ticks
@@ -3187,6 +3254,78 @@ Plot3D::FileIO(int rw)
 		}
 	return false;
 }
+
+void
+Func3D::RegGO(void *n)
+{
+}
+
+bool
+Func3D::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[] = {
+		{"Type", typNZINT, &type, 0L},
+		{"xBounds", typLFPOINT, &xBounds, 0L},
+		{"yBounds", typLFPOINT, &yBounds, 0L},
+		{"zBounds", typLFPOINT, &zBounds, 0L},
+		{"Corner1", typPOINT3D, &cub1, 0L},
+		{"Corner2", typPOINT3D, &cub2, 0L},
+		{"Center", typPOINT3D, &rotC, 0L},
+		{"rot_vec", typPOINT3D, &rot_vec, 0L},
+		{"rot_ang", typPOINT3D, &rot_ang, 0L},
+		{"Axes", typOBJLST, &Axes, (long*)&nAxes},
+		{"Plots", typOBJLST, &plots, (long*)&nPlots},
+		{"x1", typNZLFLOAT, &x1, 0L},
+		{"x2", typNZLFLOAT, &x2, 0L},
+		{"xstep", typNZLFLOAT, &xstep, 0L},
+		{"z1", typNZLFLOAT, &x1, 0L},
+		{"z2", typNZLFLOAT, &x2, 0L},
+		{"zstep", typNZLFLOAT, &xstep, 0L},
+		{"g_idx", typNZINT, &g_idx, 0L},
+		{"Line", typLINEDEF, &Line, 0L},
+		{"Fill", typFILLDEF, &Fill, 0L},
+		{"f_xz", typTEXT, &cmdxy, 0L},
+		{"param", typLAST | typTEXT, &param, 0L}};
+	int i;
+
+	switch(rw) {
+	case SAVE_VARS:
+		return SaveVarGO(Desc);
+	case INIT_VARS:
+		x1 = -20.0;		x2 = 20.0;	xstep = 2.0;
+		z1 = -20.0;		z2 = 20.0;	zstep = 2.0;
+		gda = 0L;		gob = 0L;
+		g_idx = 0;		param = cmdxy = 0L;
+		Line.width = defs.GetSize(SIZE_HAIRLINE);
+		Line.patlength = defs.GetSize(SIZE_PATLENGTH);
+		Line.color = Line.pattern = 0x0L;
+		Fill.color = 0x00c0c0c0;
+		Fill.color2 = 0x00ffffff;
+		Fill.hatch = 0L;
+		Fill.type = FILL_LIGHT3D;
+		if(name) {
+			sprintf(TmpTxt, "3D function (Plot %d)", cPlots);
+			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);
+		ExecOutput(Notary->RegisterGO(this), "Func3D", Desc);
+		}
+	return false;
+}
 
 void
 Graph::RegGO(void *n)
@@ -3265,7 +3404,7 @@ Graph::FileIO(int rw)
 			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);
+		if(Id == GO_GRAPH)RegGO(Notary);
 		//do all plots
 		if(Plots) for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->FileIO(rw);
 		//do all axes
@@ -3281,7 +3420,8 @@ Page::RegGO(void *n)
 	int i;
 
 	if(n) {
-		if(Plots) for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->RegGO(n);
+		if(Plots) for(i = 0; i< NumPlots; i++) 
+			if(Plots[i] && Plots[i]->Id != GO_GRAPH) Plots[i]->RegGO(n);
 		((notary*)n)->AddRegGO(this);
 		}
 }
@@ -3314,7 +3454,8 @@ Page::FileIO(int rw)
 		return true;
 	case FILE_WRITE:
 		//do all plots
-		bModified = false;
+		bModified = false;
+		if(Id == GO_PAGE)RegGO(Notary);
 		if(Plots) for(i = 0; i< NumPlots; i++) if(Plots[i]) Plots[i]->FileIO(rw);
 		return ExecOutput(Notary->RegisterGO(this), "Page", Desc);
 		}
@@ -3327,7 +3468,8 @@ DefsRW::FileIO(int rw)
 	descIO Desc[] = {
 		{"dUnits", typINT, &defs.dUnits, 0L},
 		{"cUnits", typINT, &defs.dUnits, 0L},
-		{"dtHeight", typINT, &dlgtxtheight, 0L},
+		{"dtHeight", typINT, &dlgtxtheight, 0L},
+		{"ss_txt", typLFLOAT, &defs.ss_txt, 0L},
 		{"File1", typTEXT, &defs.File1, 0L},
 		{"File2", typTEXT, &defs.File2, 0L},
 		{"File3", typTEXT, &defs.File3, 0L},
diff --git a/ODbuttons.cpp b/ODbuttons.cpp
index f4d94c4..7a22b70 100755
--- a/ODbuttons.cpp
+++ b/ODbuttons.cpp
@@ -117,14 +117,15 @@ void OD_LineStyleTempl(int cmd, void *par, RECT *rec, anyOutput *o,
 {
 	LineDEF Line = {.1f, 1.0f, 0x0L, 0x0L};
 	FillDEF Fill = {FILL_NONE, 0x00ffffffL, 1.0, 0L};
-	POINT pts[10];
-	int ix, iy, np;
+	POINT *pts;
+	int i, ix, iy, np;
 
+	if(!(pts=(POINT*)malloc(sizeof(POINT)*(rec->right-rec->left)))) return;
 	switch(cmd) {
 	case OD_DRAWNORMAL:
 	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00d0d0d0L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
 		ix = (rec->left + rec->right)/2;
 		iy = (rec->top +rec->bottom)/2;
 		o->SetLine(&Line);
@@ -189,17 +190,49 @@ void OD_LineStyleTempl(int cmd, void *par, RECT *rec, anyOutput *o,
 			pts[np].x = pts[np-1].x;		pts[np++].y = rec->top+15;
 			if(id == 209) pts[np-1].y -= 7;
 			break;
+		case 210:
+			pts[0].x = rec->left +9;	pts[0].y = iy+4;	pts[1].x = pts[0].x+1;
+			for(i = 0; i < (rec->right - rec->left - 18); i++) {
+				pts[1].y = 4 + iy + iround(pow(20.0, 1.0+((double)-i)/30.0) * -sin(((double)i)/4.0));
+				o->oSolidLine(pts);
+				pts[0].x++;		pts[1].x++;		pts[0].y = pts[1].y;	
+				}
+			o->oCircle(rec->left+7, iy+4, rec->left+12, iy +9);
+			o->oCircle(rec->left+12, iy-10, rec->left+17, iy -5);
+			o->oCircle(rec->right-19, iy+5, rec->right-24, iy +10);
+			o->oCircle(rec->right-9, iy, rec->right-14, iy+5);
+			break;
+		case 211:
+			pts[0].y = rec->top +9;	pts[0].x = ix;	pts[1].y = pts[0].y+1;
+			for(i = 0; i < (rec->bottom - rec->top - 18); i++) {
+				pts[1].x = ix + iround(pow(20.0, 1.0+((double)-i)/50.0) * -sin(((double)i)/4.0));
+				o->oSolidLine(pts);
+				pts[0].y++;		pts[1].y++;		pts[0].x = pts[1].x;	
+				}
+			o->oCircle(ix-3, rec->top + 7, ix+2, rec->top + 12);
+			o->oCircle(rec->left+11, iy-10, rec->left+16, iy -5);
+			o->oCircle(ix+3, rec->top + 27, ix+8, rec->top + 32);
+			o->oCircle(ix-5, iy+12, ix, iy + 17);
+			break;
 			}
 		if(np) o->oPolyline(pts, np);
-		o->oCircle(ix-2, iy-2, ix+2, iy+2);
+		switch(id) {
+		case 201:	case 202:	case 203:	case 204:	case 205:
+		case 206:	case 207:	case 208:	case 209:
+			o->oCircle(ix-2, iy-2, ix+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);
+			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);
+			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
+			break;
+		case 210:	case 211:
+			break;
+			}
 		o->UpdateRect(rec, false);
+		free(pts);
 		break;
 		}
 }
@@ -219,8 +252,8 @@ void OD_ErrBarTempl(int cmd, void *par, RECT *rec, anyOutput *o,
 	switch(cmd) {
 	case OD_DRAWNORMAL:
 	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00d0d0d0L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
 		ix = (rec->left + rec->right)/2;
 		iy = (rec->top +rec->bottom)/2;
 		o->SetLine(&Line);
@@ -283,8 +316,8 @@ void OD_WhiskerTempl(int cmd, void *par, RECT *rec, anyOutput *o,
 	switch(cmd) {
 	case OD_DRAWNORMAL:
 	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00d0d0d0L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
 		ix = (rec->left + rec->right)/2;
 		iy = (rec->top +rec->bottom)/2;
 		o->SetLine(&Line);
@@ -344,8 +377,8 @@ void OD_PolarTempl(int cmd, void *par, RECT *rec, anyOutput *o,
 	switch(cmd) {
 	case OD_DRAWNORMAL:
 	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00d0d0d0L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
 		ix = (rec->left + rec->right)/2;
 		iy = (rec->top +rec->bottom)/2;
 		o->SetLine(&Line);
@@ -388,7 +421,7 @@ void OD_PolarTempl(int cmd, void *par, RECT *rec, anyOutput *o,
 			o->oPolygon(pts, 5);
 			break;
 		case 204:
-			if(cmd == OD_DRAWNORMAL) FillG.color = 0x00d0d0d0L;
+			if(cmd == OD_DRAWNORMAL) FillG.color = 0x00e8e8e8L;
 			o->SetFill(&FillG);
 			o->oCircle(ix-6, rec->top+5, ix+6, iy+6);
 			memcpy(&td, &o->TxtSet, sizeof(TextDEF));
@@ -429,8 +462,8 @@ void OD_PieTempl(int cmd, void *par, RECT *rec, anyOutput *o,
 	switch(cmd) {
 	case OD_DRAWNORMAL:
 	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00d0d0d0L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
 		ix = (rec->left + rec->right)/2;
 		iy = (rec->top +rec->bottom)/2;
 		o->SetLine(&Line);
@@ -525,8 +558,8 @@ void OD_BreakTempl(int cmd, void *par, RECT *rec, anyOutput *o,
 	switch(cmd) {
 	case OD_DRAWNORMAL:
 	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00d0d0d0L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
 		ix = (rec->left + rec->right)>>1;
 		iy = (rec->top +rec->bottom)>>1;
 		o->SetLine(&Line);
@@ -617,11 +650,12 @@ void OD_PlotTempl(int cmd, void *par, RECT *rec, anyOutput *o,
 		iy = (rec->top +rec->bottom)>>1;
 		switch(id) {
 		case 560:	case 561:	case 562:	case 563:	case 564:		//3D axes
+		case 565:
 			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;
+			Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
+			Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
 			o->SetLine(&Line);
 			pts[0].x = pts[3].x = pts[4].x = rec->left;
 			pts[0].y = pts[1].y = pts[4].y = rec->top;
@@ -844,6 +878,41 @@ void OD_PlotTempl(int cmd, void *par, RECT *rec, anyOutput *o,
 			pts[0].y += 9;	pts[1].y += 10;	pts[2].y = pts[1].y;
 			o->oPolyline(pts, 3, 0L);
 			break;
+		case 532:
+			pts[0].x = rec->left +13;	pts[0].y = rec->top+8;	
+			pts[1].x = rec->left +13;	pts[1].y = iy;
+			o->oSolidLine(pts);
+			pts[0].x -= 3;		pts[1].x += 3;
+			pts[0].y = pts[1].y = rec->top+8;
+			o->oSolidLine(pts);
+			pts[0].y = pts[1].y = iy;
+			o->oSolidLine(pts);
+			pts[0].x = ix;	pts[0].y = iy-8;	
+			pts[1].x = ix;	pts[1].y = rec->bottom-13;
+			o->oSolidLine(pts);
+			pts[0].x -= 3;		pts[1].x += 3;
+			pts[0].y = pts[1].y = iy-8;
+			o->oSolidLine(pts);
+			pts[0].y = pts[1].y = rec->bottom-13;
+			o->oSolidLine(pts);
+			pts[0].x = rec->right -13;	pts[0].y = rec->top+10;	
+			pts[1].x = rec->right -13;	pts[1].y = iy-6;
+			o->oSolidLine(pts);
+			pts[0].x -= 3;		pts[1].x += 3;
+			pts[0].y = pts[1].y = rec->top+10;
+			o->oSolidLine(pts);
+			pts[0].y = pts[1].y = iy-6;
+			o->oSolidLine(pts);
+			pts[0].x = rec->left+13;	pts[1].x = ix;		pts[2].x = rec->right-13;
+			pts[0].y = (rec->top+8+iy)>>1;
+			pts[1].y = (rec->bottom-13 + iy -8)>>1;
+			pts[2].y = (rec->top+10+iy-6)>>1;
+			o->oPolyline(pts, 3, 0L);
+			o->SetFill(&FillY);
+			o->oCircle(pts[0].x-3, pts[0].y-3, pts[0].x+3, pts[0].y+3);
+			o->oCircle(pts[1].x-3, pts[1].y-3, pts[1].x+3, pts[1].y+3);
+			o->oCircle(pts[2].x-3, pts[2].y-3, pts[2].x+3, pts[2].y+3);
+			break;
 		case 540:
 			o->SetFill(&FillR);
 			o->oRectangle(rec->left+8, rec->bottom-8, rec->left+16, rec->bottom-3);
@@ -1012,6 +1081,32 @@ void OD_PlotTempl(int cmd, void *par, RECT *rec, anyOutput *o,
 			pts[4].x = pts[0].x;		pts[4].y = pts[0].y;
 			o->oPolygon(pts, 5, 0L);
 			break;
+		case 565:
+			o->SetLine(&rLine);
+			pts[0].x = ix-16;			pts[0].y = iy-2;
+			pts[1].x = ix+4;			pts[1].y = iy+6;
+			for(i = 0; i < 4; i++) {
+				o->oSolidLine(pts);
+				pts[0].x += 4;			pts[1].x += 4;
+				pts[0].y -= 4;			pts[1].y -= 4;
+				}
+			pts[0].x = ix+4;			pts[0].y = iy+6;
+			pts[1].x -= 2;				pts[1].y += 4;
+			for(i = 0; i < 5; i++) {
+				o->oSolidLine(pts);
+				pts[0].x -= 5;			pts[1].x -= 5;
+				pts[0].y -= 2;			pts[1].y -= 2;
+				}
+			memcpy(&td, &o->TxtSet, sizeof(TextDEF));
+			memcpy(&otd, &o->TxtSet, sizeof(TextDEF));
+			td.Align = TXA_HCENTER | TXA_VTOP;
+			td.Style = TXS_NORMAL;
+			td.Mode = TXM_TRANSPARENT;
+			td.ColTxt = 0x00c00000L;
+			o->SetTextSpec(&td);
+			o->oTextOut(ix, iy+4, "f(x,z)", 0);
+			o->SetTextSpec(&otd);
+			break;
 			}
 		o->UpdateRect(rec, false);
 		break;
@@ -1033,8 +1128,8 @@ void OD_AxisTempl(int cmd, void *par, RECT *rec, anyOutput *o,
 	switch(cmd) {
 	case OD_DRAWNORMAL:
 	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00d0d0d0L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
 		o->SetLine(&Line);
 		pts[0].x = pts[3].x = pts[4].x = rec->left;
 		pts[0].y = pts[1].y = pts[4].y = rec->top;
@@ -1152,8 +1247,8 @@ void OD_AxisTempl3D(int cmd, void *par, RECT *rec, anyOutput *o,
 	switch(cmd) {
 	case OD_DRAWNORMAL:
 	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00d0d0d0L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
 		o->SetLine(&Line);
 		pts[0].x = pts[3].x = pts[4].x = rec->left;
 		pts[0].y = pts[1].y = pts[4].y = rec->top;
@@ -1161,9 +1256,10 @@ void OD_AxisTempl3D(int cmd, void *par, RECT *rec, anyOutput *o,
 		pts[2].y = pts[3].y = rec->bottom-1;
 		o->oPolyline(pts, 5);
 		Line.color = 0x00000000L;
-		o->SetLine(&Line);
-		o->SetFill(&Fill);
+		o->SetLine(&Line);						o->SetFill(&Fill);
 		o->oRectangle(rec->left+3, rec->top+3, rec->right-3, rec->bottom-3);
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00c0c0c0L : 0x0L;
+		o->SetLine(&Line);						o->SetFill(&Fill);
 		switch(id) {
 		case 410:	case 411:
 			pts[0].x = rec->left+20;			pts[0].y = rec->bottom-14;
@@ -1199,6 +1295,7 @@ void OD_AxisTempl3D(int cmd, void *par, RECT *rec, anyOutput *o,
 			o->oSolidLine(pts);
 			}
 		o->UpdateRect(rec, false);
+		Line.color = 0x00000000L;		o->SetLine(&Line);
 		break;
 		}
 }
@@ -1217,8 +1314,8 @@ void OD_NewAxisTempl(int cmd, void *par, RECT *rec, anyOutput *o,
 	switch(cmd) {
 	case OD_DRAWNORMAL:
 	case OD_DRAWSELECTED:
-		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00d0d0d0L;
-		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00d0d0d0L;
+		Line.color = cmd == OD_DRAWSELECTED ? 0x00000000L : 0x00e8e8e8L;
+		Fill.color = cmd == OD_DRAWSELECTED ? 0x00ffffffL : 0x00e8e8e8L;
 		o->SetLine(&Line);
 		ix = (rec->right + rec->left)>>1;
 		iy = (rec->bottom + rec->top)>>1;
diff --git a/PlotObs.cpp b/PlotObs.cpp
index 30c873a..2af0276 100755
--- a/PlotObs.cpp
+++ b/PlotObs.cpp
@@ -279,6 +279,7 @@ PlotScatt::PlotScatt(int src):Plot(0L, 0L)
 PlotScatt::~PlotScatt()
 {
 	ForEach(FE_FLUSH, 0L, 0L);
+	if(name) free(name);		name=0L;
 	Undo.InvalidGO(this);
 }
 
@@ -286,31 +287,35 @@ double
 PlotScatt::GetSize(int select)
 {
 	int i;
-	double ft1, ft2;
+	double ft1, ft2, d;
 
 	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;
+		if((!Bars) || (nPoints < 2)) return BarDist.fx = 1.0;
+		ft1 = -HUGE_VAL;	ft2 = HUGE_VAL;		BarDist.fx= HUGE_VAL;
+		for(i = 0; i < nPoints; i++) {
+			if(Bars[i]) {
+				ft2 = Bars[i]->GetSize(SIZE_XPOS);
+				d = fabs(ft2-ft1);
+				if(d != 0.0 && d < BarDist.fx) BarDist.fx = d;
 				}
+			ft1 = ft2;
 			}
-		return BarDist.fx = ft1 > 0.0001 ? ft1 : 1.0;
+		return BarDist.fx = BarDist.fx > 0.0001 && BarDist.fx != HUGE_VAL  ? BarDist.fx : 1.0;
 	case SIZE_BARMINY:
 		if(BarDist.fy >= 0.0001) return BarDist.fy;
-		if((!Bars) | (nPoints < 2)) return 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;
+		if((!Bars) || (nPoints < 2)) return BarDist.fy = 1.0;
+		ft1 = -HUGE_VAL;	ft2 = HUGE_VAL;		BarDist.fy= HUGE_VAL;
+		for(i = 0; i < nPoints; i++) {
+			if(Bars[i]) {
+				ft2 = Bars[i]->GetSize(SIZE_YPOS);
+				d = fabs(ft2-ft1);
+				if(d != 0.0 && d < BarDist.fy) BarDist.fy = d;
 				}
+			ft1 = ft2;
 			}
-		return BarDist.fy = ft1 > 0.0001 ? ft1 : 1.0;
+		return BarDist.fy = BarDist.fy > 0.0001 && BarDist.fy != HUGE_VAL  ? BarDist.fy : 1.0;
 	default:
 		return Plot::GetSize(select);
 		}
@@ -332,6 +337,7 @@ PlotScatt::SetSize(int select, double value)
 		if(Symbols)	for(i = 0; i < nPoints; i++) 
 			if(Symbols[i]) Symbols[i]->SetSize(select, value);
 		return true;
+	case SIZE_WHISKER:		case SIZE_WHISKER_LINE:
 	case SIZE_ERRBAR:		case SIZE_ERRBAR_LINE:
 		if(Errors)	for(i = 0; i < nPoints; i++) 
 			if(Errors[i]) Errors[i]->SetSize(select, value);
@@ -340,6 +346,10 @@ PlotScatt::SetSize(int select, double value)
 		if(Bars) for(i = 0; i < nPoints; i++) 
 			if(Bars[i]) Bars[i]->SetSize(select, value);
 		return true;
+	case SIZE_LB_XDIST:		case SIZE_LB_YDIST:
+		if(Labels) for(i = 0; i < nPoints; i++)
+			if(Labels[i]) Labels[i]->SetSize(select, value);
+		return true;
 	case SIZE_ARROW_LINE:	case SIZE_ARROW_CAPWIDTH:	case SIZE_ARROW_CAPLENGTH:
 		if(Arrows) for(i = 0; i < nPoints; i++)
 			if(Arrows[i]) Arrows[i]->SetSize(select, value);
@@ -357,6 +367,7 @@ PlotScatt::SetColor(int select, DWORD col)
 	switch(select) {
 	case COL_SYM_LINE:
 	case COL_SYM_FILL:		go = (GraphObj**)Symbols;		break;
+	case COL_WHISKER:
 	case COL_ERROR_LINE:	go = (GraphObj**)Errors;		break;
 	case COL_BAR_LINE:
 	case COL_BAR_FILL:		go = (GraphObj**)Bars;			break;
@@ -438,12 +449,18 @@ PlotScatt::Command(int cmd, void *tmpl, anyOutput *o)
 		dirty = false;
 	case CMD_UPDATE:
 		if(cmd == CMD_UPDATE){
-			Undo.ObjConf(this, UNDO_CONTINUE);
+			if (Symbols) SavVarObs((GraphObj **)Symbols, nPoints, UNDO_CONTINUE);
+			if (Bars) SavVarObs((GraphObj **)Bars, nPoints, UNDO_CONTINUE);
+			if (Errors) SavVarObs((GraphObj **)Errors, nPoints, UNDO_CONTINUE);
+			if (Arrows) SavVarObs((GraphObj **)Arrows, nPoints, UNDO_CONTINUE);
+			if (DropLines) SavVarObs((GraphObj **)DropLines, nPoints, UNDO_CONTINUE);
+			if (Labels) SavVarObs((GraphObj **)Labels, nPoints, UNDO_CONTINUE);
 			dirty = true;
 			}
 	case CMD_SET_DATAOBJ:
 		if(cmd == CMD_SET_DATAOBJ) {
 			Id = GO_PLOTSCATT;
+			if(data && data == (DataObj *) tmpl) return true;
 			data = (DataObj *)tmpl;	
 			}
 		ForEach(cmd, tmpl, o);
@@ -463,14 +480,18 @@ PlotScatt::Command(int cmd, void *tmpl, anyOutput *o)
 		if(Symbols) for(i = 0; i < nPoints; i++)
 			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
 		return true;
+	case CMD_SETTEXTDEF:
+		if(Labels) for(i = 0; i < nPoints; i++)
+			if(Labels[i]) Labels[i]->Command(cmd, tmpl, o);
+		return true;
 	case CMD_DL_LINE:		case CMD_DL_TYPE:
 		if(DropLines) for(i = 0; i < nPoints; i++)
 			if(DropLines[i]) DropLines[i]->Command(cmd, tmpl, o);
 		return true;
-	case CMD_ERR_TYPE:
-		if(Errors) for(i = 0; i < nPoints; i++) 
-			if(Errors[i]) Errors[i]->type = *((int*)tmpl);
-		return true;
+	case CMD_ERR_TYPE:		case CMD_WHISKER_STYLE:
+		if(Errors) for(i = 0; i < nPoints; i++) {
+			if(Errors[i]) Errors[i]->Command(cmd, tmpl, o);
+			}
 	case CMD_BAR_TYPE:		case CMD_BAR_FILL:
 		if(Bars) for(i = 0; i < nPoints; i++) {
 			if(Bars[i]) Bars[i]->Command(cmd, tmpl, o);
@@ -495,6 +516,8 @@ PlotScatt::Command(int cmd, void *tmpl, anyOutput *o)
 		return SavVarObs((GraphObj **)Arrows, nPoints, 0L);
 	case CMD_SAVE_DROPLINES:
 		return SavVarObs((GraphObj **)DropLines, nPoints, 0L);
+	case CMD_SAVE_LABELS:
+		return SavVarObs((GraphObj **)Labels, nPoints, 0L);
 		}
 	return false;
 }
@@ -631,6 +654,206 @@ PlotScatt::ForEach(int cmd, void *tmp, anyOutput *o)
 	return false;
 }
 
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// xyStat is based on scatterplot
+xyStat::xyStat(GraphObj *par, DataObj *d):PlotScatt(par, d, 0L)
+{
+	FileIO(INIT_VARS);
+	Id = GO_XYSTAT;
+}
+
+xyStat::xyStat(int src):PlotScatt(0)
+{
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		}
+}
+
+xyStat::~xyStat()
+{
+	ForEach(FE_FLUSH, 0L, 0L);
+	if(curr_data) delete curr_data;			curr_data = 0L;
+	if(case_prefix) free(case_prefix);		case_prefix = 0L;
+	if(yRange) free(yRange);				yRange = 0L;
+	if(xRange) free(xRange);				xRange = 0L;
+	if(name) free(name);					name=0L;
+	Undo.InvalidGO(this);
+}
+
+bool
+xyStat::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch (cmd) {
+	case CMD_UPDATE:
+		if (Symbols) SavVarObs((GraphObj **)Symbols, nPoints, UNDO_CONTINUE);
+		if (Bars) SavVarObs((GraphObj **)Bars, nPoints, UNDO_CONTINUE);
+		if (Errors) SavVarObs((GraphObj **)Errors, nPoints, UNDO_CONTINUE);
+		if (Labels) SavVarObs((GraphObj **)Labels, nPoints, UNDO_CONTINUE);
+		CreateData();
+		ForEach(CMD_SET_DATAOBJ, curr_data, o);
+		ForEach(CMD_UPDATE, tmpl, o);
+		return dirty = true;
+	case CMD_SET_DATAOBJ:
+		if(cmd == CMD_SET_DATAOBJ) {
+			Id = GO_XYSTAT;
+			if(data && data == (DataObj *) tmpl) return true;
+			if(curr_data) delete curr_data;		curr_data = 0L;
+			data = (DataObj *)tmpl;
+			if(data && !curr_data) CreateData();
+			tmpl = curr_data;
+			}
+		ForEach(cmd, tmpl, o);
+		return true;
+	default:
+		return PlotScatt::Command(cmd, tmpl, o);
+		}
+	return false;
+}
+
+void
+xyStat::CreateData()
+{
+	int i, j, k, l, m, n, *ny;
+	double x, y, ss, d, lo, hi, **ay, *ax, *tay, *q1, *q2, *q3;
+	lfPOINT *xy;
+	AccRange *rX, *rY;
+
+	if(curr_data) delete curr_data;			curr_data = 0L;
+	if(!data || !xRange || !yRange || !xRange[0] || !yRange[0]) return;
+	if(!(rX = new AccRange(xRange)) || !(rY = new AccRange(yRange))) return;
+	m = rX->CountItems();	n = 0;
+	if(m < 2 || !(xy = (lfPOINT*) malloc(m * sizeof(lfPOINT)))) {
+		delete rX;	delete rY;
+		return;
+		}
+	ny = (int*) calloc(m, sizeof(int));
+	ay = (double**) calloc(m, sizeof(double*));
+	ax = (double*) calloc(m, sizeof(double));
+	tay = (double*)malloc(m * sizeof(double));
+	if(!ny || !ay || !ax || !tay) {
+		if(ny) free(ny);	if(ay) free(ay);
+		if(ax) free(ax);	if(tay) free(tay);
+		delete rX;	delete rY;
+		return;
+		}
+	rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
+	rX->GetNext(&i, &j);	rY->GetNext(&k, &l);	n=0;
+	do {
+		if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y)){
+			xy[n].fx = x;		xy[n].fy = y;
+			n++;
+			}
+		}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
+	delete rX;			delete rY;
+	if(!n) {
+		if(ny) free(ny);	if(ay) free(ay);
+		if(ax) free(ax);	if(tay) free(tay);
+		return;
+		}
+	SortFpArray(n, xy);
+	for(i = j = 0; i < (n-1); i++, j++) {
+		ax[j] = xy[i].fx;		tay[0] = xy[i].fy;
+		ny[j] = 1;
+		for(k = 1; xy[i+1].fx == xy[i].fx; k++) {
+			tay[k] = xy[i+1].fy;
+			i++;		ny[j]++;
+			}
+		ay[j] = (double*)memdup(tay, k * sizeof(double), 0);
+		}
+	if(xy[i].fx > xy[i-1].fx) {
+		ax[j] = xy[i].fx;		tay[0] = xy[i].fy;
+		ny[j] = 1;
+		ay[j++] = (double*)memdup(tay, sizeof(double), 0);
+		}
+	if(type & 0x0480) {		//medians and/or percentiles required
+		q1 = (double *)malloc(j * sizeof(double));
+		q2 = (double *)malloc(j * sizeof(double));
+		q3 = (double *)malloc(j * sizeof(double));
+		if(q1 && q2 && q3) {
+			for(i = 0; i < j; i++) {
+				if(ny[i] > 1) d_quartile(ny[i], ay[i], q1+i, q2+i, q3+i);
+				else q1[i] = q2[i] = q3[i] = *ay[i];
+				}
+			}
+		else type &= (~0x0480);
+		}
+	else q1 = q2 = q3 = 0L;
+	if((curr_data = new DataObj()) && curr_data->Init(j, 6)) {
+		for(i = 0; i < j; i++) curr_data->SetValue(i,0,ax[i]);	// set x-values
+		for(i = 0; i < j; i++) {								// set y-values
+			if(ny[i] > 1) switch(type & 0x00f0) {
+				case 0x0010:	default:
+					curr_data->SetValue(i, 1, y=d_amean(ny[i], ay[i]));
+					break;
+				case 0x0020:
+					curr_data->SetValue(i, 1, y=d_gmean(ny[i], ay[i]));
+					break;
+				case 0x0040:
+					curr_data->SetValue(i, 1, y=d_hmean(ny[i], ay[i]));
+					break;
+				case 0x0080:
+					curr_data->SetValue(i, 1, y=q2[i]);
+					break;
+				}
+			else curr_data->SetValue(i, 1, y= *ay[i]);
+			curr_data->SetValue(i, 4, y);
+			}
+		for(i = 0; i < j; i++) {								// set errors
+			switch(type & 0x1f00) {
+			case 0x0100:	case 0x0200:	case 0x1000:	//SD, SEM, conf. int.
+				if(ny[i] > 1) {
+					y = d_amean(ny[i], ay[i]);
+					for(k = 0, ss = 0.0; k < (ny[i]); k++) {
+						ss += ((d=ay[i][k]-y)*d);
+						ny[i] = ny[i];
+						}
+					ss = ss/(double)(ny[i]-1);
+					switch(type & 0x1f00) {
+					case 0x0100:
+						curr_data->SetValue(i, 2, sqrt(ss));
+						break;
+					case 0x0200:
+						curr_data->SetValue(i, 2, sqrt(ss)/sqrt(ny[i]));
+						break;
+					case 0x1000:
+						d = distinv(t_dist, ny[i]-1, 1, 1.0-(ci/100.0), 2.0);
+						curr_data->SetValue(i, 2, d * sqrt(ss)/sqrt(ny[i]));
+						break;
+						}
+					}
+				else curr_data->SetValue(i, 2, 0.0);
+				if(curr_data->GetValue(i, 1, &y) && curr_data->GetValue(i, 2, &hi))
+					curr_data->SetValue(i, 4, hi+y);
+				break;
+			case 0x0400:								//percentiles
+				curr_data->SetValue(i, 2, q1[i]);	curr_data->SetValue(i, 3, q3[i]);
+				curr_data->SetValue(i, 4, q3[i]);
+				break;
+			case 0x0800:								//min-max
+				lo = hi = *ay[i];
+				for(k = 1; k < ny[i]; k++) {
+					if(ay[i][k] < lo) lo = ay[i][k];
+					if(ay[i][k] > hi) hi = ay[i][k];
+					}
+				curr_data->SetValue(i, 2, lo);		curr_data->SetValue(i, 3, hi);
+				curr_data->SetValue(i, 4, hi);
+				break;
+				}
+			}
+		if(type & 0x6000) for(i = 0; i < j; i++) {				// number of cases
+			sprintf(TmpTxt, "%s%d", case_prefix ? case_prefix : "", ny[i]);
+			curr_data->SetText(i, 5, TmpTxt);
+			}
+		}
+	else {
+		if(curr_data) delete curr_data;
+		curr_data = 0L;
+		}
+	if(q1) free(q1);	if(q2) free(q2);	if(q3) free(q3);
+	for(i = 0; i < m; i++) if(ay[i]) free(ay[i]);
+	free(tay);	free(ay);	free(ax);	free(ny);	free(xy);
+}
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // BarChart is based on scatterplot
@@ -665,6 +888,8 @@ FreqDist::~FreqDist()
 		for(i = 0; i < nPlots; i++) if(plots[i]) DeleteGO(plots[i]);
 		free(plots);						plots=0L;
 		}
+	if(name) free(name);					name=0L;
+
 }
 
 void
@@ -854,11 +1079,9 @@ FreqDist::ProcData(int sel)
 			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);
 			}
@@ -891,6 +1114,7 @@ Regression::Regression(int src):Plot(0L, 0L)
 Regression::~Regression()
 {
 	Command(CMD_FLUSH, 0L, 0L);
+	if(name) free(name);		name=0L;
 	Undo.InvalidGO(this);
 }
 
@@ -1035,7 +1259,7 @@ Regression::Command(int cmd, void *tmpl, anyOutput *o)
 		return SavVarObs((GraphObj **)Symbols, nPoints, 0L);
 	case CMD_UPDATE:
 		if(Symbols) {
-			Undo.ObjConf(this, UNDO_CONTINUE);
+			SavVarObs((GraphObj**)Symbols, nPoints, UNDO_CONTINUE);
 			for(i = 0; i < nPoints; i++)
 				if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
 			if(rLine || sde) Recalc();
@@ -1158,6 +1382,7 @@ BubblePlot::~BubblePlot()
 		for(i = 0; i < nPoints; i++) if(Bubbles[i]) DeleteGO(Bubbles[i]);
 		free (Bubbles);
 		}
+	if(name) free(name);		name=0L;
 	Undo.InvalidGO(this);
 }
 
@@ -1218,10 +1443,14 @@ BubblePlot::Command(int cmd, void *tmpl, anyOutput *o)
 				}
 			}
 		return false;
+	case CMD_LEGEND:
+		if(((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		if(Bubbles) for (i = 0; i < nPoints; i++)
+			if(Bubbles[i]) Bubbles[i]->Command(cmd, tmpl, o);
+		return true;
 	case CMD_MRK_DIRTY:
 		dirty = true;
-	case CMD_SETSCROLL:
-	case CMD_REDRAW:
+	case CMD_SETSCROLL:		case CMD_REDRAW:
 		if(parent) return parent->Command(cmd, tmpl, o);
 		return false;
 	case CMD_USEAXIS:
@@ -1231,10 +1460,12 @@ BubblePlot::Command(int cmd, void *tmpl, anyOutput *o)
 		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:
+		if(cmd == CMD_UPDATE && Bubbles) SavVarObs((GraphObj **)Bubbles, nPoints, UNDO_CONTINUE);
+	case CMD_AUTOSCALE:
+		if(cmd == CMD_AUTOSCALE && Bubbles) {
+			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;		Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
+			}
+	case CMD_BUBBLE_ATTRIB:		case CMD_BUBBLE_TYPE:		case CMD_BUBBLE_FILL:
 	case CMD_BUBBLE_LINE:
 		if(Bubbles) for(i = 0; i < nPoints; i++)
 			if(Bubbles[i]) Bubbles[i]->Command(cmd, tmpl, o);
@@ -1292,6 +1523,7 @@ PolarPlot::~PolarPlot()
 		for(i = 0; i < nAxes; i++) if(Axes[i]) DeleteGO(Axes[i]);
 		free(Axes);			Axes = 0L;
 		}
+	if(name) free(name);	name=0L;
 	Undo.InvalidGO(this);
 }
 
@@ -1486,6 +1718,8 @@ BoxPlot::BoxPlot(int src):Plot(0L, 0L)
 			for(i = 0; i < nPoints; i++) if(Whiskers[i]) Whiskers[i]->parent = this;
 		if(Symbols) 
 			for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->parent = this;
+		if(Labels) 
+			for(i = 0; i < nPoints; i++) if(Labels[i]) Labels[i]->parent = this;
 		if(TheLine) TheLine->parent = this;
 		}
 }
@@ -1514,18 +1748,27 @@ BoxPlot::~BoxPlot()
 	int i;
 
 	if(Whiskers) {
-		for(i = 0; i < nPoints; i++) if(Whiskers[i]) delete(Whiskers[i]);
+		for(i = 0; i < nPoints; i++) if(Whiskers[i]) DeleteGO(Whiskers[i]);
 		free (Whiskers);
 		}
 	if(Boxes) {
-		for(i = 0; i < nPoints; i++) if(Boxes[i]) delete(Boxes[i]);
+		for(i = 0; i < nPoints; i++) if(Boxes[i]) DeleteGO(Boxes[i]);
 		free (Boxes);
 		}
 	if(Symbols) {
-		for(i = 0; i < nPoints; i++) if(Symbols[i]) delete(Symbols[i]);
+		for(i = 0; i < nPoints; i++) if(Symbols[i]) DeleteGO(Symbols[i]);
 		free (Symbols);
 		}
-	if(TheLine) delete(TheLine);
+	if(Labels) {
+		for(i = 0; i < nPoints; i++) if(Labels[i]) DeleteGO(Labels[i]);
+		free (Labels);
+		}
+	if(TheLine) DeleteGO(TheLine);
+	if(curr_data) delete curr_data;		curr_data = 0L;
+	if(xRange) free(xRange);			xRange = 0L;
+	if(yRange) free(yRange);			yRange = 0L;
+	if(case_prefix) free(case_prefix);	case_prefix = 0L;
+	if(name) free(name);				name=0L;
 	Undo.InvalidGO(this);
 }
 
@@ -1533,35 +1776,35 @@ double
 BoxPlot::GetSize(int select)
 {
 	int i;
-	double ft1, ft2;
+	double ft1, ft2, d;
 
 	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;
+		if((!Boxes) || (nPoints < 2)) return BoxDist.fx = 1.0;
+		ft1 = -HUGE_VAL;	ft2 = HUGE_VAL;		BoxDist.fx= HUGE_VAL;
+		for(i = 0; i < nPoints; i++) {
+			if(Boxes[i]) {
+				ft2 = Boxes[i]->GetSize(SIZE_XPOS);
+				d = fabs(ft2-ft1);
+				if(d != 0.0 && d < BoxDist.fx) BoxDist.fx = d;
+				}
+			ft1 = ft2;
+			}
+		return BoxDist.fx = BoxDist.fx > 0.0001 && BoxDist.fx != HUGE_VAL  ? BoxDist.fx : 1.0;
 	case SIZE_BOXMINY:
 		if(BoxDist.fy >= 0.0001) return BoxDist.fy;
-		if((!Boxes) | (nPoints < 2)) return 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;
+		if((!Boxes) || (nPoints < 2)) return BoxDist.fy = 1.0;
+		ft1 = -HUGE_VAL;	ft2 = HUGE_VAL;		BoxDist.fy= HUGE_VAL;
+		for(i = 0; i < nPoints; i++) {
+			if(Boxes[i]) {
+				ft2 = Boxes[i]->GetSize(SIZE_YPOS);
+				d = fabs(ft2-ft1);
+				if(d != 0.0 && d < BoxDist.fy) BoxDist.fy = d;
+				}
+			ft1 = ft2;
+			}
+		return BoxDist.fy = BoxDist.fy > 0.0001 && BoxDist.fy != HUGE_VAL  ? BoxDist.fy : 1.0;
 	default:
 		return Plot::GetSize(select);
 		}
@@ -1573,21 +1816,22 @@ BoxPlot::SetSize(int select, double value)
 	int i;
 
 	switch(select & 0xfff){
-	case SIZE_SYMBOL:
-	case SIZE_SYM_LINE:
+	case SIZE_SYMBOL:		case SIZE_SYM_LINE:
 		if(Symbols) for(i = 0; i < nPoints; i++) 
 			if(Symbols[i]) Symbols[i]->SetSize(select, value);
 		return true;
-	case SIZE_WHISKER:
-	case SIZE_WHISKER_LINE:
+	case SIZE_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:
+	case SIZE_BOX:			case SIZE_BOX_LINE:
 		if(Boxes) for(i = 0; i < nPoints; i++) 
 			if(Boxes[i]) Boxes[i]->SetSize(select, value);
 		return true;
+	case SIZE_LB_XDIST:		case SIZE_LB_YDIST:
+		if(Labels) for(i = 0; i < nPoints; i++)
+			if(Labels[i]) Labels[i]->SetSize(select, value);
+		return true;
 	}
 	return false;
 }
@@ -1598,8 +1842,7 @@ BoxPlot::SetColor(int select, DWORD col)
 	int i;
 
 	switch(select) {
-	case COL_SYM_LINE:
-	case COL_SYM_FILL:
+	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;
@@ -1619,18 +1862,10 @@ BoxPlot::SetColor(int select, DWORD col)
 void
 BoxPlot::DoPlot(anyOutput *o)
 {
-	int i;
-
-	if(!parent) return;
+	if(!parent || !o) 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);
+	ForEach(FE_PLOT, 0L, o);
 	dirty = false;
 	if(use_xaxis || use_yaxis)parent->Command(CMD_AXIS, 0L, o);
 }
@@ -1638,102 +1873,89 @@ BoxPlot::DoPlot(anyOutput *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;
+	int i;
 
 	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(!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(Boxes) for (i = 0; i < nPoints; i++)	if(Boxes[i]) Boxes[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]);
 				}
-			if(TheLine && !CurrGO) TheLine->Command(cmd, tmpl, o);
-			break;
+			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);
 			}
-		break;
-	case CMD_LEGEND:
-		if(Boxes) for (i = 0; i < nPoints; i++)
-			if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
+		else if(TheLine) TheLine->Command(cmd, tmpl, o);
 		break;
 	case CMD_SET_DATAOBJ:
 		Id = GO_BOXPLOT;		data = (DataObj *)tmpl;		dirty = true;
+		if(type && xRange && yRange && data) {		//Stat. - Plot ?
+			CreateData();
+			return ForEach(CMD_SET_DATAOBJ, curr_data, o);
+			}
+		return ForEach(cmd, tmpl, o);
 	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;
+		if(hidden) return false;
+		if(dirty) {
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;
+			Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
 			}
-	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);
+		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;
+				}
 			}
-		if(TheLine) TheLine->Command(cmd, tmpl, o);
-		if(cmd == CMD_AUTOSCALE && parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH
+		dirty = false;
+		ForEach(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_UPDATE:
+		if(Boxes) SavVarObs((GraphObj **)Boxes, nPoints, UNDO_CONTINUE);
+		if(Whiskers) SavVarObs((GraphObj **)Whiskers, nPoints, UNDO_CONTINUE);
+		if(Symbols) SavVarObs((GraphObj **)Symbols, nPoints, UNDO_CONTINUE);
+		if(Labels) SavVarObs((GraphObj **)Labels, nPoints, UNDO_CONTINUE); 
+		if(type && xRange && yRange) {		//Stat. - Plot ?
+			CreateData();
+			ForEach(CMD_SET_DATAOBJ, curr_data, o);
+			}
+		ForEach(cmd, tmpl, o);
+		return dirty = true;
 	case CMD_USEAXIS:
 		UseAxis(*((int*)tmpl));
 		return true;
 	case CMD_MRK_DIRTY:
 		dirty = true;
-	case CMD_SETSCROLL:
-	case CMD_REDRAW:
-		if(parent)return parent->Command(cmd, tmpl, o);
+	case CMD_SETSCROLL:		case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, o);
 		return false;
+	case CMD_SETTEXTDEF:
+		if(Labels) for(i = 0; i < nPoints; i++)
+			if(Labels[i]) Labels[i]->Command(cmd, tmpl, o);
+		return true;
 	case CMD_DELOBJ:
-		if(!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(ForEach(FE_DELOBJ, tmpl, o)) {
+			parent->Command(CMD_REDRAW, 0L, o);
+			return 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:
+		return false;
+	case CMD_SYMTEXT:		case CMD_SYM_RANGETEXT:
+	case CMD_SYMTEXTDEF:	case CMD_SYM_TYPE:
 		if(Symbols) for(i = 0; i < nPoints; i++)
 			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
 		return true;
@@ -1745,6 +1967,8 @@ BoxPlot::Command(int cmd, void *tmpl, anyOutput *o)
 		return SavVarObs((GraphObj **)Boxes, nPoints, UNDO_CONTINUE);
 	case CMD_SAVE_ERRS:
 		return SavVarObs((GraphObj **)Whiskers, nPoints, 0L);
+	case CMD_SAVE_LABELS:
+		return SavVarObs((GraphObj **)Labels, nPoints, 0L);
 	case CMD_BOX_TYPE:
 		BoxDist.fy = BoxDist.fx = 0.0;
 	case CMD_BOX_FILL:
@@ -1759,6 +1983,230 @@ BoxPlot::Command(int cmd, void *tmpl, anyOutput *o)
 	return false;
 }
 
+bool
+BoxPlot::ForEach(int cmd, void *tmpl, anyOutput *o)
+{
+	GraphObj ***pobs[] = {(GraphObj***)&Boxes, (GraphObj***)&Whiskers, 
+		(GraphObj***)&Symbols, (GraphObj***)&Labels};
+	GraphObj **p;
+	int i, j;
+	bool bRet;
+
+	switch(cmd) {
+	case FE_DELOBJ:
+		if(!o || !parent || !tmpl) return false;
+		for(i = 0; i < 4; i++) {
+			if(DeleteGOL(pobs[i], nPoints, (GraphObj*) tmpl, o)) return true;
+			}
+		if(TheLine && tmpl == (void *) TheLine) {
+			Undo.DeleteGO((GraphObj**)(&TheLine), 0L, o);
+			return true;
+			}
+		break;
+	case FE_PLOT:
+		if(TheLine) TheLine->DoPlot(o);
+		for(i = 0; i < 4; i++){
+			if(p= *pobs[i]) for(j = 0; j < nPoints; j++) {
+				if(p[j]) p[j]->DoPlot(o);
+				}
+			}
+		return true;
+	case CMD_MOUSE_EVENT:				//invers to plot order
+		for(i = 3; i >= 0; i--){
+			if(p= *pobs[i]) for(j = nPoints-1; j >= 0; j--) {
+				if(p[j]) {
+					bRet = p[j]->Command(cmd, tmpl, o);
+					if(bRet && cmd == CMD_MOUSE_EVENT) return true;
+					}
+				}
+			}
+		if(TheLine) return TheLine->Command(cmd, tmpl, o);
+		return false;
+	default:							//pass command to all objects
+		for(i = 0; i < 4; i++){
+			if(p= *pobs[i]) for(j = 0; j < nPoints; j++) {
+				if(p[j]) {
+					bRet = p[j]->Command(cmd, tmpl, o);
+					}
+				}
+			}
+		if(TheLine) return TheLine->Command(cmd, tmpl, o);
+		return false;
+		}
+	return false;
+}
+
+void
+BoxPlot::CreateData()
+{
+	int i, j, k, l, m, n, *ny;
+	double x, y, ss, d, lo, hi, **ay, *ax, *tay, *q1, *q2, *q3;
+	lfPOINT *xy;
+	AccRange *rX, *rY;
+
+	if(curr_data) delete curr_data;			curr_data = 0L;
+	if(!data || !xRange || !yRange || !xRange[0] || !yRange[0]) return;
+	if(!(rX = new AccRange(xRange)) || !(rY = new AccRange(yRange))) return;
+	m = rX->CountItems();	n = 0;
+	if(m < 2 || !(xy = (lfPOINT*) malloc(m * sizeof(lfPOINT)))) {
+		delete rX;	delete rY;
+		return;
+		}
+	ny = (int*) calloc(m, sizeof(int));
+	ay = (double**) calloc(m, sizeof(double*));
+	ax = (double*) calloc(m, sizeof(double));
+	tay = (double*)malloc(m * sizeof(double));
+	if(!ny || !ay || !ax || !tay) {
+		if(ny) free(ny);	if(ay) free(ay);
+		if(ax) free(ax);	if(tay) free(tay);
+		delete rX;	delete rY;
+		return;
+		}
+	rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
+	rX->GetNext(&i, &j);	rY->GetNext(&k, &l);	n=0;
+	do {
+		if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y)){
+			xy[n].fx = x;		xy[n].fy = y;
+			n++;
+			}
+		}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
+	delete rX;			delete rY;
+	if(!n) {
+		if(ny) free(ny);	if(ay) free(ay);
+		if(ax) free(ax);	if(tay) free(tay);
+		return;
+		}
+	SortFpArray(n, xy);
+	for(i = j = 0; i < (n-1); i++, j++) {
+		ax[j] = xy[i].fx;		tay[0] = xy[i].fy;
+		ny[j] = 1;
+		for(k = 1; xy[i+1].fx == xy[i].fx; k++) {
+			tay[k] = xy[i+1].fy;
+			i++;		ny[j]++;
+			}
+		ay[j] = (double*)memdup(tay, k * sizeof(double), 0);
+		}
+	if(xy[i].fx > xy[i-1].fx) {
+		ax[j] = xy[i].fx;		tay[0] = xy[i].fy;
+		ny[j] = 1;
+		ay[j++] = (double*)memdup(tay, sizeof(double), 0);
+		}
+	if((type & 0x0004) == 0x0004 || (type & 0x0030) == 0x0030 || (type & 0x0300) == 0x0300) {
+		//medians and/or percentiles required
+		q1 = (double *)malloc(j * sizeof(double));
+		q2 = (double *)malloc(j * sizeof(double));
+		q3 = (double *)malloc(j * sizeof(double));
+		if(q1 && q2 && q3) {
+			for(i = 0; i < j; i++) {
+				if(ny[i] > 1) d_quartile(ny[i], ay[i], q1+i, q2+i, q3+i);
+				else q1[i] = q2[i] = q3[i] = *ay[i];
+				}
+			}
+		else type = 0;
+		}
+	else q1 = q2 = q3 = 0L;
+	if(type && (curr_data = new DataObj()) && curr_data->Init(j, 8)) {
+		for(i = 0; i < j; i++) curr_data->SetValue(i,0,ax[i]);	// set x-values
+		for(i = 0; i < j; i++) {								// set means
+			if(ny[i] > 1) switch(type & 0x000f) {
+				case 0x0001:	default:
+					curr_data->SetValue(i, 1, y=d_amean(ny[i], ay[i]));
+					break;
+				case 0x0002:
+					curr_data->SetValue(i, 1, y=d_gmean(ny[i], ay[i]));
+					break;
+				case 0x0003:
+					curr_data->SetValue(i, 1, y=d_hmean(ny[i], ay[i]));
+					break;
+				case 0x0004:
+					curr_data->SetValue(i, 1, y=q2[i]);
+					break;
+				}
+			else curr_data->SetValue(i, 1, y= *ay[i]);
+			curr_data->SetValue(i, 6, y);						//label's y
+			}
+		if((type & 0x00f0) == 0x0010 || (type & 0x00f0) == 0x0020 || (type & 0x00f0) == 0x0050
+			|| (type & 0x0f0f) == 0x0201 || (type & 0x0f0f) == 0x0501) for(i = 0; i < j; i++) {
+			// set SD, SE, Conf. Intervall
+			if(ny[i] > 1) {
+				y = d_amean(ny[i], ay[i]);
+				for(k = 0, ss = 0.0; k < (ny[i]); k++) {
+					ss += ((d=ay[i][k]-y)*d);
+					ny[i] = ny[i];
+					}
+				ss = sqrt(ss/(double)(ny[i]-1));
+				}
+			else {
+				y = *ay[i];		ss = 0.0;
+				}
+			//Box info is in cols 2 & 3
+			if((type & 0x00f0) == 0x0010) {
+				curr_data->SetValue(i, 2, y - ss);	curr_data->SetValue(i, 3, y + ss);
+				}
+			else if((type & 0x00f0) == 0x0020) {
+				curr_data->SetValue(i, 2, y - ss/sqrt(ny[i]));	
+				curr_data->SetValue(i, 3, y + ss/sqrt(ny[i]));
+				}
+			else if((type & 0x00f0) == 0x0050) {
+				d = ny[i] > 1 ? distinv(t_dist, ny[i]-1, 1, 1.0-(ci_box/100.0), 2.0) : 0;
+				curr_data->SetValue(i, 2, y - d*ss/sqrt(ny[i]));	
+				curr_data->SetValue(i, 3, y + d*ss/sqrt(ny[i]));
+				}
+			//Whisker info is in cols 4 & 5
+			if((type & 0x0f0f) == 0x0101) {
+				curr_data->SetValue(i, 4, y - ss);	curr_data->SetValue(i, 5, y + ss);
+				}
+			else if((type & 0x0f0f) == 0x0201) {
+				curr_data->SetValue(i, 4, y - ss/sqrt(ny[i]));
+				curr_data->SetValue(i, 5, y + ss/sqrt(ny[i]));
+				}
+			else if((type & 0x0f0f) == 0x0501) {
+				d = ny[i] > 1 ? distinv(t_dist, ny[i]-1, 1, 1.0-(ci_err/100.0), 2.0) : 0;
+				curr_data->SetValue(i, 4, y - d*ss/sqrt(ny[i]));
+				curr_data->SetValue(i, 5, y + d*ss/sqrt(ny[i]));
+				}
+			}
+		if((type & 0x00f0) == 0x0040 || (type & 0x0f00) == 0x0400) for(i = 0; i < j; i++) {
+			// set min and max
+			lo = hi = *ay[i];
+ 			if(ny[i] > 1) {
+				for(k = 1; k < ny[i]; k++) {
+					if(ay[i][k] < lo) lo = ay[i][k];
+					if(ay[i][k] > hi) hi = ay[i][k];
+					}
+				}
+			if((type & 0x00f0) == 0x0040) {
+				curr_data->SetValue(i, 2, lo);	curr_data->SetValue(i, 3, hi);
+				}
+			if((type & 0x0f00) == 0x0400) {
+				curr_data->SetValue(i, 4, lo);	curr_data->SetValue(i, 5, hi);
+				}
+			}
+		if(q1 && q3 && ((type & 0x00f0) == 0x0030 || (type & 0x0f00) == 0x0300)) for(i = 0; i < j; i++) {
+			// percentiles ....
+			if((type & 0x00f0) == 0x0030) {
+				curr_data->SetValue(i, 2, q1[i]);	curr_data->SetValue(i, 3, q3[i]);
+				}
+			if((type & 0x0f00) == 0x0300) {
+				curr_data->SetValue(i, 4, q1[i]);	curr_data->SetValue(i, 5, q3[i]);
+				}
+			}
+		if(type & 0xc000) for(i = 0; i < j; i++) {
+			//labels ...
+			if((type & 0x4000) && curr_data->GetValue(i, 5, &y)) curr_data->SetValue(i, 6, y);
+			sprintf(TmpTxt, "%s%d", case_prefix ? case_prefix : "", ny[i]);
+			curr_data->SetText(i, 7, TmpTxt);
+			}
+		}
+	else {
+		if(curr_data) delete curr_data;
+		curr_data = 0L;
+		}
+	if(q1) free(q1);	if(q2) free(q2);	if(q3) free(q3);
+	for(i = 0; i < m; i++) if(ay[i]) free(ay[i]);
+	free(tay);	free(ay);	free(ax);	free(ny);	free(xy);
+}
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Density distribution plot
 DensDisp::DensDisp(GraphObj *par, DataObj *d):Plot(par, d)
@@ -1791,6 +2239,7 @@ DensDisp::~DensDisp()
 		}
 	if(yRange) free(yRange);		if(xRange) free(xRange);
 	yRange = xRange = 0L;
+	if(name) free(name);			name=0L;
 	Undo.InvalidGO(this);
 }
 
@@ -1852,8 +2301,7 @@ DensDisp::Command(int cmd, void *tmpl, anyOutput *o)
 	switch (cmd) {
 	case CMD_MRK_DIRTY:
 		dirty = true;
-	case CMD_SETSCROLL:
-	case CMD_REDRAW:
+	case CMD_SETSCROLL:		case CMD_REDRAW:
 		if(parent) return parent->Command(cmd, tmpl, o);
 		return false;
 	case CMD_USEAXIS:
@@ -1877,13 +2325,8 @@ DensDisp::Command(int cmd, void *tmpl, anyOutput *o)
 		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);
-				}
-			}
+		if(DeleteGOL((GraphObj***)&Boxes, nPoints, (GraphObj*)tmpl, o)) 
+			return parent->Command(CMD_REDRAW, 0L, o);
 		break;
 	case CMD_AUTOSCALE:
 		if(dirty){
@@ -1892,13 +2335,12 @@ DensDisp::Command(int cmd, void *tmpl, anyOutput *o)
 			}
 		else return true;
 		dirty = false;
-	case CMD_BOX_TYPE:
-	case CMD_BOX_FILL:
+	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);
+		if(Boxes) SavVarObs((GraphObj **)Boxes, nPoints, UNDO_CONTINUE);
 		DoUpdate();
 		return true;
 		}
@@ -1912,15 +2354,17 @@ DensDisp::DoUpdate()
 	int i, j, k, l, ic, n;
 	double v, w;
 	lfPOINT fp1, fp2;
+	Box **op = Boxes;
 
 	if(xRange && yRange && (rX = new AccRange(xRange)) && (rY = new AccRange(yRange))) {
 		if((n=rX->CountItems()) == rY->CountItems()) {
-			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*));
+			if(!(Boxes = (Box**)realloc(Boxes, n * sizeof(Box*)))) return;
+			if(op && op != Boxes) Undo.InvalidGO(this);
+			for(i = nPoints; i < n; i++) {
+				Boxes[i] = 0L;
+				}
+			nPoints = n;
 			rX->GetFirst(&i, &j);	rY->GetFirst(&k, &l);
 			rX->GetNext(&i, &j);	rY->GetNext(&k, &l);
 			for(ic = 0; ic < n && !data->GetValue(j, i, &v); ic++) {
@@ -1936,7 +2380,7 @@ DensDisp::DoUpdate()
 			ic = 0;
 			do {
 				if(data->GetValue(j, i, &v) && data->GetValue(l, k, &w)){
-					memcpy(&fp1, &fp2, sizeof(lfPOINT));
+					fp1.fx = fp2.fx;	fp1.fy = fp2.fy;
 					if(type & 0x10) {
 						CheckBounds(w, fp1.fy);			CheckBounds(-w, v);
 						fp2.fy = v;		
@@ -1955,16 +2399,22 @@ DensDisp::DoUpdate()
 						default:	fp2.fy = 0.0;					break;
 							}
 						}
-					if(Boxes[ic] = new Box(this, data, fp1, fp2, BAR_WIDTHDATA))
+					if(op && Boxes[ic]) {
+						Boxes[ic]->SetSize(SIZE_XPOS, fp1.fx);	Boxes[ic]->SetSize(SIZE_XPOS+1, fp2.fx);
+						Boxes[ic]->SetSize(SIZE_YPOS, fp1.fy);	Boxes[ic]->SetSize(SIZE_YPOS+1, fp2.fy);
+						Boxes[ic]->SetSize(SIZE_BOX, (type &0x03) ? w/2.0 : w);
+						}
+					else if(!op && (Boxes[ic] = new Box(this, data, fp1, fp2, BAR_WIDTHDATA)))
 						Boxes[ic]->SetSize(SIZE_BOX, (type &0x03) ? w/2.0 : w);
 					}
 				ic++;
 				}while(rX->GetNext(&i, &j) && rY->GetNext(&k, &l));
-			SetSize(SIZE_BOX_LINE, DefLine.width);
-			SetColor(COL_BOX_LINE, DefLine.color);
-			Command(CMD_BOX_FILL, (void*)&DefFill, 0L);
+			if(!op) {
+				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);
 		}
 }
@@ -2019,6 +2469,7 @@ StackBar::~StackBar()
 		}
 	if(ssXrange) free(ssXrange);	if(ssYrange) free(ssYrange);
 	if(CumData) delete CumData;		CumData = 0L;
+	if(name) free(name);			name=0L;
 	Undo.InvalidGO(this);
 }
 
@@ -2131,6 +2582,10 @@ StackBar::Command(int cmd, void *tmpl, anyOutput *o)
 			if(Boxes[i]) Boxes[i]->Command(cmd, tmpl, o);
 		if(Polygons) for (i = numPG-1; i >= 0; i--)
 			if(Polygons[i]) Polygons[i]->Command(cmd, tmpl, o);
+		if(Lines) for (i = numPL-1; i >= 0; i--)
+			if(Lines[i]) Lines[i]->Command(cmd, tmpl, o);
+		if(xyPlots) for (i = 0; i < numXY; i++)
+			if(xyPlots[i]) xyPlots[i]->Command(cmd, tmpl, o);
 		break;
 	case CMD_USEAXIS:
 		UseAxis(*((int*)tmpl));
@@ -2153,7 +2608,6 @@ StackBar::Command(int cmd, void *tmpl, anyOutput *o)
 		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;
@@ -2193,28 +2647,19 @@ StackBar::Command(int cmd, void *tmpl, anyOutput *o)
 	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);
+		if(DeleteGOL((GraphObj***)&Polygons, numPG, (GraphObj*)tmpl, 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);
+		if(DeleteGOL((GraphObj***)&Lines, numPL, (GraphObj*)tmpl, 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;
-			}
+		if(DeleteGOL((GraphObj***)&xyPlots, numXY, (GraphObj*)tmpl, o)) 
+			return parent->Command(CMD_REDRAW, 0L, o);
+		if(DeleteGOL((GraphObj***)&Boxes, numPlots, (GraphObj*)tmpl, o)) 
+			return parent->Command(CMD_REDRAW, 0L, o);
+		if(xyPlots) for(i = 0; i < numXY; i++)
+			if(xyPlots[i] && xyPlots[i]->Command(cmd, tmpl, o)) return true;
+		if(Boxes) for(i = 0; i < numPlots; i++)
+			if(Boxes[i] && Boxes[i]->Command(cmd, tmpl, o)) return true;
+		return false;
 		}
 	return false;
 }
@@ -2272,6 +2717,7 @@ PieChart::~PieChart()
 		}
 	if(ssRefA) free(ssRefA);	if(ssRefR) free(ssRefR);
 	ssRefA = ssRefR = 0L;
+	if(name) free(name);		name=0L;
 	Undo.InvalidGO(this);
 }
 
@@ -2359,7 +2805,7 @@ PieChart::DoUpdate()
 	int i, ix, iy, rix, riy;
 
 	if(ssRefA && (rY = new AccRange(ssRefA))) {
-		Undo.ObjConf(this, UNDO_CONTINUE);
+		SavVarObs((GraphObj **)Segments, nPts, UNDO_CONTINUE);
 		if(ssRefR) rR = new AccRange(ssRefR);
 		rY->GetFirst(&ix, &iy);				rY->GetNext(&ix, &iy);
 		for(i = 0, sum = 0.0; i < nPts; i++){
@@ -2431,6 +2877,7 @@ GoGroup::~GoGroup()
 		for(i = 0; i < nObs; i++) if(Objects[i]) DeleteGO(Objects[i]);
 		free(Objects);
 		}
+	if(name) free(name);		name=0L;
 	Undo.InvalidGO(this);
 }
 
@@ -2594,6 +3041,7 @@ Scatt3D::~Scatt3D()
 	if(rib) {
 		DeleteGO(rib);      rib = 0L;
 		}
+	if(name) free(name);	name=0L;
 }
 
 double
@@ -2772,8 +3220,12 @@ Scatt3D::Command(int cmd, void *tmpl, anyOutput *o)
 					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]);
+				for (i = 0; i < nBalls && i < 100; i++) {
+					if(Balls[i]) {
+						if(Balls[i]->type) Balls[i]->Command(cmd, tmpl, o);
+						else ((Legend*)tmpl)->HasSym(0L, Balls[i]);
+						}
+					}
 				}
 			}
 		else if(Line) Line->Command(cmd, tmpl, o);
@@ -2785,15 +3237,22 @@ Scatt3D::Command(int cmd, void *tmpl, anyOutput *o)
 		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(Balls) {
+			SavVarObs((GraphObj**)Balls, nBalls, UNDO_CONTINUE);
+			for(i = 0; i < nBalls; i++)	if(Balls[i]) Balls[i]->Command(cmd, tmpl, o);
+			}
+		if(Columns) {
+			SavVarObs((GraphObj**)Columns, nColumns, UNDO_CONTINUE);
+			for(i = 0; i < nColumns; i++) if(Columns[i]) Columns[i]->Command(cmd, tmpl, o);
+			}
+		if(DropLines) {
+			SavVarObs((GraphObj**)DropLines, nDropLines, UNDO_CONTINUE);
+			for(i = 0; i < nDropLines; i++) if(DropLines[i]) DropLines[i]->Command(cmd, tmpl, o);
+			}
+		if(Arrows) {
+			SavVarObs((GraphObj**)Arrows, nArrows, UNDO_CONTINUE);
+			for(i = 0; i < nArrows; i++) if(Arrows[i]) Arrows[i]->Command(cmd, tmpl, o);
+			}
 		if(Line) Line->Command(cmd, tmpl, o);
 		if(rib) rib->Command(cmd, tmpl, o);
 		return true;
@@ -2904,6 +3363,7 @@ Ribbon::~Ribbon()
 		free(planes);		planes = 0L;
 		}
 	if(values) free(values);	values = 0L;	nVal = 0;
+	if(name) free(name);		name=0L;
 }
 
 double
@@ -3184,10 +3644,13 @@ Ribbon::UpdateObs(bool bNewData)
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // draw a 3dimensional grid
-Grid3D::Grid3D(GraphObj *par, DataObj *d)
+Grid3D::Grid3D(GraphObj *par, DataObj *d, int sel, double x1, double xstep, double z1, double zstep)
 	:Plot(par, d)
 {
 	FileIO(INIT_VARS);		Id = GO_GRID3D;
+	start.fx = x1;			step.fx = xstep;
+	start.fz = z1;			step.fz = zstep;
+	type = sel;
 }
 
 Grid3D::Grid3D(int src):Plot(0L, 0L)
@@ -3207,7 +3670,38 @@ Grid3D::~Grid3D()
 		for(i = 0; i < nLines; i++) if(lines[i]) DeleteGO(lines[i]);
 		free(lines);		lines = 0L;
 		}
-	nLines = 0;
+	if(planes) {
+		for(i = 0; i < nPlanes; i++) if(planes[i]) DeleteGO(planes[i]);
+		free(planes);		planes = 0L;
+		}
+	nLines = nPlanes = 0;
+	if(name) free(name);	name=0L;
+}
+
+bool 
+Grid3D::SetSize(int select, double value)
+{
+	int i;
+
+	switch (select) {
+	case SIZE_SYM_LINE:
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->SetSize(select, value); 
+		return true;
+		}
+	return false;
+}
+
+bool
+Grid3D::SetColor(int select, DWORD col)
+{
+	int i;
+
+	switch (select) {
+	case COL_POLYLINE:
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->SetColor(select, col); 
+		return true;
+		}
+	return false;
 }
 
 void
@@ -3215,8 +3709,9 @@ Grid3D::DoPlot(anyOutput *o)
 {
 	int i;
 
-	if(!lines) CreateObs();
+	if(!lines && !planes) CreateObs();
 	if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->DoPlot(o);
+	if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->DoPlot(o);
 	dirty = false;
 }
 
@@ -3234,17 +3729,29 @@ Grid3D::Command(int cmd, void *tmpl, anyOutput *o)
 		case MOUSE_LBUP:
 			if(lines && !CurrGO) for(i = 0; i < nLines; i++)
 				if(lines[i]) if(lines[i]->Command(cmd, tmpl, o)) return true;
+			if(planes && !CurrGO) for(i = 0; i < nPlanes; i++)
+				if(planes[i]) if(planes[i]->Command(cmd, tmpl, o)) return true;
 			break;
 			}
 		break;
 	case CMD_SET_LINE:
-		if(tmpl && lines) {
-			SavVarObs((GraphObj**)lines, nLines, 0L);
+		if(tmpl) {
 			memcpy(&Line, tmpl, sizeof(LineDEF));
-			if(lines) for(i = 0; i < nLines; i++)
-				if(lines[i]) lines[i]->Command(cmd, tmpl, o);
+			if(lines) {
+				SavVarObs((GraphObj**)lines, nLines, 0L);
+				for(i = 0; i < nLines; i++)
+					if(lines[i]) lines[i]->Command(cmd, tmpl, o);
+				}
+			if(planes) {
+				SavVarObs((GraphObj**)planes, nPlanes, 0L);
+				for(i = 0; i < nPlanes; i++)
+					if(planes[i]) planes[i]->Command(cmd, tmpl, o);
+				}
 			}
 		break;
+	case CMD_LEGEND:
+		if(!hidden) ((Legend*)tmpl)->HasFill(&Line, planes ? &Fill : 0L);
+		break;
 	case CMD_MRK_DIRTY:
 		dirty = true;
 	case CMD_SET_GO3D:		case CMD_SETSCROLL:		case CMD_REDRAW:
@@ -3252,34 +3759,42 @@ Grid3D::Command(int cmd, void *tmpl, anyOutput *o)
 		break;
 	case CMD_SET_DATAOBJ:
 		Id = GO_GRID3D;
+		if(tmpl == data) return true;
 		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;
+		if(lines) for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->Command(cmd, tmpl, o);
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->Command(cmd, tmpl, o);
+		return dirty = true;
 	case CMD_AUTOSCALE:
-		if(!lines) CreateObs();
+		if(!lines && !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(lines) for(i = 0; i < nLines; i++)
 				if(lines[i]) lines[i]->Command(cmd, tmpl, o);
+			if(planes) for(i = 0; i < nPlanes; i++)
+				if(planes[i]) planes[i]->Command(cmd, tmpl, o);
 			}
+		if(zBounds.fx > zBounds.fy) zBounds.fx = zBounds.fy = 0.0;
 		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH &&
-			xBounds.fx <= xBounds.fy && yBounds.fx <= yBounds.fy && zBounds.fx <= zBounds.fy){
+			xBounds.fx <= xBounds.fy && yBounds.fx <= yBounds.fy){
 			((Plot*)parent)->CheckBounds3D(xBounds.fx, yBounds.fx, zBounds.fx);
 			((Plot*)parent)->CheckBounds3D(xBounds.fy, yBounds.fy, zBounds.fy);
 			}
 		return true;
+	case CMD_SYM_FILL:
+		if(!tmpl) return false;
+		memcpy(&Fill, tmpl, sizeof(FillDEF));
+		if(planes) for(i = 0; i < nPlanes; i++) if(planes[i]) planes[i]->Command(cmd, tmpl, o); 
+		return true;
+	case CMD_SAVE_SYMBOLS:
+		return SavVarObs((GraphObj **)planes, nPlanes, 0L);
 	case CMD_DELOBJ:
-		if(!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);
-			}
+		if(!parent) return false;
+		if(DeleteGOL((GraphObj***)&lines,nLines,(GraphObj*)tmpl,o)) return parent->Command(CMD_REDRAW, 0L, o);
+		if(DeleteGOL((GraphObj***)&planes,nPlanes,(GraphObj*)tmpl,o)) return parent->Command(CMD_REDRAW, 0L, o);
+		break;
 		}
 	return false;
 }
@@ -3290,30 +3805,67 @@ 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(!parent || !data || lines || planes) return;
+	dirty = true;
+	if(type == 0) {
+		if(!(vec = (fPOINT3D*)malloc(sizeof(fPOINT3D) * 2))) return;
+		data->GetSize(&w, &h);
+		if(0 >= (nLines = 2 * w * h - w - h)) return;
+		if(!(lines =(Line3D**)calloc(nLines, sizeof(Line3D*)))) return;
+		vec[0].fz = start.fz;			data->GetValue(0, 0, &vec[0].fy);
+		for(ic = 1, idx = 0; ic <= w; ic++) {
+			vec[0].fx = start.fx;
+			data->GetValue(0, ic-1, &vec[0].fy);
+			for(ir = 1; ir <= h; ir++){
+				if(ic < w && data->GetValue(ir-1, ic, &vec[1].fy)) {
+					vec[1].fz = vec[0].fz + step.fz;	vec[1].fx = vec[0].fx;
+					lines[idx++] = new Line3D(this, data, vec, 2, 
+						-1, -1, ic-1, ir-1, -1, -1, -1, -1, ic, ir-1, -1, -1);
+					}
+				if(ir < h && data->GetValue(ir, ic-1, &vec[1].fy)) {
+					vec[1].fz = vec[0].fz;	vec[1].fx = vec[0].fx + step.fx;
+					lines[idx++] = new Line3D(this, data, vec, 2,
+						-1, -1, ic-1, ir-1, -1, -1, -1, -1, ic-1, ir, -1, -1);
+					}
+				vec[0].fx += step.fx;	vec[0].fy = vec[1].fy;
 				}
-			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;
+			}
+		for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->Command(CMD_SET_LINE, &Line, 0L);
+		free(vec);
+		}
+	else if(type == 1) {
+		if(!(vec = (fPOINT3D*)malloc(sizeof(fPOINT3D) * 5))) return;
+		vec[0].fz = vec[4].fz = start.fz;
+		vec[3].fz = (start.fz +step.fz);
+		data->GetSize(&w, &h);
+		if(0 >= (nPlanes = w * h)) return;
+		if(!(planes =(Plane3D**)calloc(nPlanes, sizeof(Plane3D*)))) return;
+		for(ic = 1, idx = 0; ic <= w; ic++) {
+			vec[0].fx = vec[3].fx = vec[4].fx = (start.fx+step.fx);
+			vec[1].fx = vec[2].fx = start.fx;
+			vec[1].fz = vec[4].fz;	vec[2].fz = vec[3].fz;
+			data->GetValue(0, ic-1, &vec[1].fy);	data->GetValue(0, ic, &vec[2].fy);
+			for(ir = 1; ir <= h; ir++){
+				if(ic < w && ir < h && data->GetValue(ir, ic, &vec[3].fy) 
+					&& data->GetValue(ir, ic-1, &vec[4].fy)) {
+					vec[0].fz = vec[4].fz;	vec[0].fy = vec[4].fy;	vec[0].fx = vec[4].fx;
+					planes[idx++] = new Plane3D(this, 0L, vec, 5);
+					}
+				vec[1].fz = vec[4].fz;		vec[1].fy = vec[4].fy;		vec[1].fx = vec[4].fx;
+				vec[2].fz = vec[3].fz;		vec[2].fy = vec[3].fy;		vec[2].fx = vec[3].fx;
+				vec[3].fx += step.fx;		vec[4].fx += step.fx;
 				}
-			vec[0].fz += step.fz;	vec[0].fy = vec[1].fy;
+			vec[3].fz += step.fz;			vec[4].fz += step.fz;
+			}
+		nPlanes = idx;
+		for(i = 0; i < nPlanes; i++) if(planes[i]){
+			planes[i]->Command(CMD_SET_LINE, &Line, 0L);
+			planes[i]->Command(CMD_SYM_FILL, &Fill, 0L);
 			}
-		vec[0].fx += step.fx;
+		SetSize(SIZE_SYM_LINE, Line.width);		SetColor(COL_POLYLINE, Line.color);
+		free(vec);
 		}
-	for(i = 0; i < nLines; i++) if(lines[i]) lines[i]->Command(CMD_SET_LINE, &Line, 0L);
-	free(vec);
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -3328,6 +3880,7 @@ Limits::Limits(int src):Plot(0L, 0L)
 
 Limits::~Limits()
 {
+	if(name) free(name);		name=0L;
 }
 
 double
@@ -3377,6 +3930,7 @@ Function::~Function()
 	if(cmdxy) free(cmdxy);		cmdxy = 0L;
 	if(param) free(param);		param = 0L;
 	if(dl) DeleteGO(dl);		dl = 0L;
+	if(name) free(name);		name=0L;
 }
 
 bool
@@ -3393,7 +3947,7 @@ Function::SetSize(int select, double value)
 void
 Function::DoPlot(anyOutput *o)
 {
-	if(!dl && cmdxy && cmdxy[0]) Update(o, 0);
+	if((!dl || dirty) && cmdxy && cmdxy[0]) Update(o, 0);
 	dirty = false;
 	if(dl && o) {
 		dl->Command(CMD_SET_LINE, &Line, o);
@@ -3475,7 +4029,6 @@ Function::Update(anyOutput *o, DWORD flags)
 	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);
@@ -3532,6 +4085,7 @@ FitFunc::~FitFunc()
 	if(ssXref) free(ssXref);	ssXref = 0L;
 	if(ssYref) free(ssYref);	ssYref = 0L;
 	if(dl) DeleteGO(dl);		dl = 0L;
+	if(name) free(name);		name=0L;
 	Undo.InvalidGO(this);
 }
 
@@ -3572,14 +4126,7 @@ FitFunc::DoPlot(anyOutput *o)
 
 	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(dl && o) dl->DoPlot(o);
 	if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i]) Symbols[i]->DoPlot(o);
 }
 
@@ -3619,20 +4166,25 @@ FitFunc::Command(int cmd, void *tmpl, anyOutput *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);
+			if(dl) dl->Command(cmd, tmpl, o);
 			memcpy(&Bounds, &dl->Bounds, sizeof(fRECT));
 			if(Symbols) for(i = 0; i < nPoints; i++) if(Symbols[i])Symbols[i]->Command(cmd, tmpl, o);
 			dirty = false;
 			}
 		return true;
 	case CMD_UPDATE:
-		Undo.ObjConf(this, UNDO_CONTINUE);
-		if(Symbols) for(i = 0; i < nPoints; i++)
-			if(Symbols[i]) Symbols[i]->Command(cmd, tmpl, o);
+		if(Symbols) {
+			SavVarObs((GraphObj**)Symbols, nPoints, UNDO_CONTINUE);
+			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);
+		if(!dl) dl = new Function(this, data);
+		if(dl){
+			dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
+			dl->SetSize(SIZE_XSTEP, xstep);			dl->Command(CMD_SETFUNC, cmdxy, 0L);
+			dl->Command(CMD_SETPARAM, parxy, 0L);	dl->Command(CMD_SET_LINE, &Line, 0L);
+			dl->Update(o, UNDO_CONTINUE);
+			}
 		dirty = true;
 		if(parent) parent->Command(CMD_MRK_DIRTY, 0L, o);
 		return true;
@@ -3642,6 +4194,10 @@ FitFunc::Command(int cmd, void *tmpl, anyOutput *o)
 		break;
 	case CMD_MRK_DIRTY:
 		dirty = true;
+		if(dl){
+			dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
+			dl->SetSize(SIZE_XSTEP, xstep);
+			}
 	case CMD_REDRAW:
 		if(parent) return parent->Command(cmd, tmpl, o);
 		break;
@@ -3707,6 +4263,7 @@ Plot3D::~Plot3D()
 	if(drag) DeleteGO(drag);	drag = 0L;
 	if(dispObs) free(dispObs);	dispObs = 0L;
 	free(RotDef);
+	if(name) free(name);		name=0L;
 	Undo.InvalidGO(this);
 }
 
@@ -3785,35 +4342,35 @@ Plot3D::DoPlot(anyOutput *o)
 	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) {
+	if(Axes && nAxes >2) {		//if no axes then parent is another Plot3D ...
+		o->LightSource(32.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;
 		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));
+		}
+	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);
 			}
-		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);
+		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){
+	if(nObs >1  && dispObs){
+		SortObj();
 		for (i = fo = 1; i <= nObs; i++){
 			while(dispObs[fo]->Zmax < dispObs[i]->Zmin && fo < (nObs-1)) fo++;
 			for(j = fo; j <= nObs; j++) {
@@ -3870,7 +4427,7 @@ Plot3D::Command(int cmd, void *tmpl, anyOutput *o)
 	case CMD_HIDE_MARK:
 		if(!tmpl) return false;
 		//do all axes
-		if(Axes)for(i = nAxes-1; i>=0; i--) {
+		if(Axes)for(i = nAxes-1; i>=0; i--) if(Axes[i]){
 			if(tmpl == (void*)Axes[i]){
 				Axes[i]->DoMark(o, false);
 				return true;
@@ -3880,7 +4437,7 @@ Plot3D::Command(int cmd, void *tmpl, anyOutput *o)
 				}
 			}
 		//do all plots
-		if(plots)for(i = nPlots-1; i>=0; i--) {
+		if(plots)for(i = nPlots-1; i>=0; i--) if(plots[i]){
 			if(tmpl == (void*)plots[i]){
 				plots[i]->DoMark(o, false);
 				return true;
@@ -3892,8 +4449,10 @@ Plot3D::Command(int cmd, void *tmpl, anyOutput *o)
 		return false;
 	case CMD_OBJTREE:
 		if(!tmpl || !plots) return false;
-		for(i = 0; i < nPlots; i++) if(plots[i]) 
+		for(i = 0; i < nPlots; i++) if(plots[i]) {
 			((ObjTree*)tmpl)->Command(CMD_UPDATE, plots[i], 0L);
+			if(plots[i]->Id > GO_PLOT && plots[i]->Id < GO_GRAPH) plots[i]->Command(cmd, tmpl, o);
+			}
 		return true;
 	case CMD_REPL_GO:
 		if(!(tmpPlots = (GraphObj **)tmpl) || !tmpPlots[0] || !tmpPlots[1]) return false;
@@ -3902,14 +4461,15 @@ Plot3D::Command(int cmd, void *tmpl, anyOutput *o)
 			}
 		return false;
 	case CMD_MRK_DIRTY:
+		if(IsPlot3D(parent)) return parent->Command(cmd, tmpl, o);
 		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:
+		if(IsPlot3D(parent)) return parent->Command(cmd, tmpl, o);
 		return AcceptObj((GraphObj *)tmpl);
-	case CMD_SETSCROLL:
-	case CMD_REDRAW:
+	case CMD_SETSCROLL:				case CMD_REDRAW:
 		if(parent) return parent->Command(cmd, tmpl, o);
 		break;
 	case CMD_SHIFTLEFT:
@@ -3948,13 +4508,8 @@ Plot3D::Command(int cmd, void *tmpl, anyOutput *o)
 			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;
+		if(DeleteGOL((GraphObj***)&plots,nPlots,(GraphObj*)tmpl,o)) return parent->Command(CMD_REDRAW,0L,o);
+		return false;
 	case CMD_MOVE:
 		if(CurrGO && CurrGO->Id == GO_DRAGHANDLE) {
 			CalcRotation(((lfPOINT*)tmpl)[0].fx, ((lfPOINT*)tmpl)[0].fy, o, true);
@@ -3969,6 +4524,7 @@ Plot3D::Command(int cmd, void *tmpl, anyOutput *o)
 		return true;
 	case CMD_DROP_PLOT:
 		if(!parent || !tmpl || ((GraphObj*)tmpl)->Id < GO_PLOT) return false;
+		if(IsPlot3D(parent)) return parent->Command(cmd, tmpl, o);
 		if(!nPlots) {
 			plots = (GraphObj**)calloc(2, sizeof(GraphObj*));
 			if(plots) {
@@ -4158,7 +4714,14 @@ Plot3D::DoAutoscale()
 	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);
+	for(i = 0; i < nPlots; i++) {
+		if(plots[i]) {
+			if(plots[i]->Id >= GO_PLOT && plots[i]->Id < GO_GRAPH) {
+				if(!((Plot*)plots[i])->hidden) plots[i]->Command(CMD_AUTOSCALE, 0L, 0L);
+				}
+			else plots[i]->Command(CMD_AUTOSCALE, 0L, 0L);
+			}
+		}
 	if(xBounds.fx <= xBounds.fy && yBounds.fx <= yBounds.fy && zBounds.fx <= zBounds.fy){
 		if(Axes)for(i = 0; i < 3; i++) if(Axes[i]){
 			ad = Axes[i]->axis;
@@ -4188,6 +4751,10 @@ Plot3D::DoAutoscale()
 				Axes[i]->Command(CMD_AUTOSCALE, ad, 0L);
 				}
 			}
+		else if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
+			((Plot*)parent)->CheckBounds3D(xBounds.fx, yBounds.fx, zBounds.fx);
+			((Plot*)parent)->CheckBounds3D(xBounds.fy, yBounds.fy, zBounds.fy);
+			}
 		}
 }
 
@@ -4401,6 +4968,7 @@ Chart25D::Chart25D(GraphObj *par, DataObj *d, DWORD flags)
 
 Chart25D::~Chart25D()
 {
+	if(name) free(name);		name=0L;
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -4413,6 +4981,7 @@ Ribbon25D::Ribbon25D(GraphObj *par, DataObj *d, DWORD flags)
 
 Ribbon25D::~Ribbon25D()
 {
+	if(name) free(name);		name=0L;
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -4424,4 +4993,83 @@ BubblePlot3D::BubblePlot3D(GraphObj *par, DataObj *d)
 
 BubblePlot3D::~BubblePlot3D()
 {
+	if(name) free(name);		name=0L;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// 3D function plotter
+Func3D::Func3D(GraphObj *par, DataObj *d)
+	:Plot3D(par, d, 0x0L)
+{
+	FileIO(INIT_VARS);
+	cmdxy = strdup("r=sqrt(x*x+z*z)\ny=1-exp(-8/(r+1))");
+	Id = GO_FUNC3D;
+}
+
+Func3D::Func3D(int src):Plot3D(0)
+{
+	int i;
+
+	FileIO(INIT_VARS);
+	if(src == FILE_READ) {
+		FileIO(FILE_READ);
+		//now set parent in all children
+		if(Axes) for(i = 0; i < nAxes; i++)
+			if(Axes[i]) Axes[i]->parent = this;
+		if(plots) for(i = 0; i < nPlots; i++)
+			if(plots[i]) plots[i]->parent = this;
+		}
+}
+
+Func3D::~Func3D()
+{
+	if(param) free(param);		param = 0L;
+	if(cmdxy) free(cmdxy);		cmdxy = 0L;
+	if(gda) delete(gda);		gda = 0L;
+	if(name) free(name);		name=0L;
+}
+
+bool
+Func3D::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch(cmd) {
+	case CMD_SET_DATAOBJ:
+		Plot3D::Command(cmd, tmpl, o);
+		if(gob && gda) gob->Command(cmd, gda, o);
+		Id = GO_FUNC3D;
+		return true;
+	case CMD_UPDATE:
+		return Update();
+		}
+	return Plot3D::Command(cmd, tmpl, o);
+}
+
+bool
+Func3D::Update()
+{
+	if(cmdxy) {
+		dirty = true;
+		if(xstep == 0.0) xstep = 1.0;	if(zstep == 0.0) zstep = 1.0;
+		if(!gda) gda = new DataObj();
+		if(gda && do_func3D(gda, x1, x2, xstep, z1, z2, zstep, cmdxy, param)) {
+			if(gob = new Grid3D(this, gda, type, x1, xstep, z1, zstep)) {
+				gob->Command(CMD_SET_LINE, &Line, 0L);
+				gob->Command(CMD_SYM_FILL, &Fill, 0L);
+				if(!plots && (plots = (GraphObj**)calloc(2, sizeof(GraphObj*)))) {
+					nPlots = 1;				plots[0] = (Plot *)gob;
+					if(parent->Id == GO_GRAPH) CreateAxes();
+					return dirty = parent->Command(CMD_REDRAW, 0L, 0L);
+					}
+				else if(plots && nPlots && plots[0]->Id == GO_GRID3D) {
+					Undo.DeleteGO(&plots[0], UNDO_CONTINUE, 0L);
+					Undo.SetGO(this, &plots[0], gob, UNDO_CONTINUE);
+					return true;
+					}
+				else {
+					DeleteGO(gob);		gob=0L;
+					}
+				}
+			}
+		}
+	return false;
 }
diff --git a/PropertyDlg.cpp b/PropertyDlg.cpp
index c6f160f..73d8eac 100755
--- a/PropertyDlg.cpp
+++ b/PropertyDlg.cpp
@@ -109,6 +109,7 @@ Symbol::PropertyDlg()
 	DWORD tmpCol, undo_flags = 0L;
 	double tmpVal, o_size, n_size, o_lwidth, n_lwidth;
 	TextDEF textdef;
+	anyOutput *cdisp = Undo.cdisp;
 	lfPOINT o_pos, n_pos;
 
 	if(!parent) return false;
@@ -161,6 +162,7 @@ Symbol::PropertyDlg()
 		switch(res) {
 		case 2:
 		case 1:
+			Undo.SetDisp(cdisp);
 			if(PrevSym->type == SYM_TEXT && Dlg->GetCheck(250)){
 				Dlg->GetText(251, text1);
 				if(PrevSym->Command(CMD_GETTEXT, (void *)text2, 0L) && strcmp(text1, text2)) {
@@ -326,6 +328,7 @@ Bubble::PropertyDlg()
 	LineDEF newLine, newFillLine;
 	FillDEF newFill;
 	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 	double o_size, n_size;
 	bool bRet = false;
 
@@ -362,6 +365,7 @@ Bubble::PropertyDlg()
 		switch(res) {
 		case 1:			//accept for current bubble only
 		case 2:			//accept for plot
+			Undo.SetDisp(cdisp);
 			OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
 			OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
 			memcpy(&newFillLine, &BubbleFillLine, sizeof(LineDEF));
@@ -464,6 +468,7 @@ Bar::PropertyDlg()
 	LineDEF newLine, newFillLine;
 	FillDEF newFill;
 	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 	lfPOINT o_bl, n_bl, o_pos, n_pos;
 
 	if(!parent) return false;
@@ -539,6 +544,7 @@ Bar::PropertyDlg()
 			break;
 		case 1:
 		case 2:
+			Undo.SetDisp(cdisp);
 			OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
 			OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
 			memcpy(&newFillLine, &HatchLine, sizeof(LineDEF));
@@ -612,36 +618,45 @@ DataLine::PropertyDlg()
 {
 	TabSHEET tab1 = {0, 40, 10, "Line"};
 	TabSHEET tab2 = {40, 80, 10, "Style"};
+	TabSHEET tab3 = {80, 120, 10, "Edit"};
 	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},
+		{5, 6, 200, ISPARENT, SHEET, &tab2, 5, 10, 139, 120},
+		{6, 0, 300, ISPARENT | HIDDEN, SHEET, &tab3, 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}};
+		{200, 201, 0, 0x0L, LTEXT, (void*)"select style:", 10, 32, 130, 100},
+		{201, 202, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 12, 45, 25, 25},
+		{202, 203, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 37, 45, 25, 25},
+		{203, 204, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 62, 45, 25, 25},
+		{204, 205, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 87, 45, 25, 25},
+		{205, 206, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 112, 45, 25, 25},
+		{206, 207, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 37, 70, 25, 25},
+		{207, 208, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 62, 70, 25, 25},
+		{208, 209, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 87, 70, 25, 25},
+		{209, 210, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 112, 70, 25, 25},
+		{210, 211, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 37, 95, 25, 25},
+		{211, 0, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_LineStyleTempl), 62, 95, 25, 25},
+		{300, 301, 0, 0x0L, LTEXT, (void*)"range for x-values", 15, 30, 80, 9},
+		{301, 302, 0, 0x0L, EDTEXT, (void*)ssXref, 15, 40, 119, 10},
+		{302, 303, 0, 0x0L, LTEXT, (void*)"range for y-values", 15, 55, 80, 9},
+		{303, 0, 0, LASTOBJ, EDTEXT, (void*)ssYref, 15, 65, 119, 10}};
 	DlgRoot *Dlg;
 	void *hDlg;
 	int res, tmpType = type;
 	DWORD undo_flags = 0L;
 	LineDEF newLine;
 	bool bRet = false;
+	anyOutput *cdisp = Undo.cdisp;
 
 	if(!parent) return false;
 	if(parent->Id == GO_FUNCTION) return parent->PropertyDlg();
 	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
 	if(!(Dlg = new DlgRoot(LineDlg)))return false;
-	Dlg->SetCheck(201 + (type & 0x7), 0L, true);
+	Dlg->SetCheck(201 + (type & 0x0f), 0L, true);
+	if(ssXref && ssYref) Dlg->ShowItem(6, 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);
@@ -650,14 +665,31 @@ DataLine::PropertyDlg()
 		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;
+		case 206:	case 207:	case 208:	case 209:	case 210:	case 211:
+			if((tmpType & 0x0f) == (res-201)) res = 1;
+			else {
+				tmpType &= ~0x0f;	tmpType |= (res-201);
+				res = -1;
+				}
 			break;
 			}
 		}while (res < 0);
 	if(res == 1){						//OK pressed
+		Undo.SetDisp(cdisp);
+		if(ssXref && ssYref) {
+			if(Dlg->GetText(301, TmpTxt) && strcmp(ssXref, TmpTxt)) {
+				Undo.String(this, &ssXref, undo_flags);	undo_flags |= UNDO_CONTINUE;
+				if(ssXref = (char*)realloc(ssXref, strlen(TmpTxt)+1))
+					strcpy(ssXref, TmpTxt);
+				}
+			if(Dlg->GetText(303, TmpTxt) && strcmp(ssYref, TmpTxt)) {
+				Undo.String(this, &ssYref, undo_flags);	undo_flags |= UNDO_CONTINUE;
+				if(ssYref = (char*)realloc(ssYref, strlen(TmpTxt)+1))
+					strcpy(ssYref, TmpTxt);
+				}
+			if(undo_flags & UNDO_CONTINUE) Command(CMD_UPDATE, 0L, cdisp);
+			parent->Command(CMD_MRK_DIRTY, 0L, cdisp);
+			}
 		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
 		if(cmpLineDEF(&LineDef, &newLine)) {
 			Undo.Line(parent, &LineDef, undo_flags);	undo_flags |= UNDO_CONTINUE;
@@ -688,6 +720,7 @@ DataPolygon::PropertyDlg()
 	LineDEF newLine, newFillLine;
 	FillDEF newFill;
 	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 	int res;
 	bool bRet = false;
 
@@ -702,6 +735,7 @@ DataPolygon::PropertyDlg()
 		res = Dlg->GetResult();
 		}while (res < 0);
 	if(res == 1){						//OK pressed
+		Undo.SetDisp(cdisp);
 		OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
 		OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
 		memcpy(&newFillLine, &pgFillLine, sizeof(LineDEF));
@@ -778,6 +812,7 @@ RegLine::PropertyDlg()
 	bool bRet = false;
 	LineDEF newLine;
 	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 	lfPOINT o_l5, n_l5;
 	fRECT o_clip, n_clip;
 	char *tx, *ty;
@@ -865,6 +900,7 @@ RegLine::PropertyDlg()
 			}
 		}while (res < 0);
 	if(res == 1){						//OK pressed
+		Undo.SetDisp(cdisp);
 		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
 		if(cmpLineDEF(&LineDef, &newLine)) {
 			Undo.Line(parent, &LineDef, undo_flags);	undo_flags |= UNDO_CONTINUE;
@@ -926,6 +962,7 @@ SDellipse::PropertyDlg()
 	LineDEF newLine;
 	bool bRet = false;
 	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 
 	if(!parent) return false;
 	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&LineDef, 0);
@@ -945,6 +982,7 @@ SDellipse::PropertyDlg()
 		res = Dlg->GetResult();
 		}while (res < 0);
 	if(res == 1){						//OK pressed
+		Undo.SetDisp(cdisp);
 		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
 		if(cmpLineDEF(&LineDef, &newLine)) {
 			Undo.Line(parent, &LineDef, undo_flags);	undo_flags |= UNDO_CONTINUE;
@@ -1003,6 +1041,7 @@ ErrorBar::PropertyDlg()
 	double n_sb, o_sb, n_lw, o_lw, n_err, o_err;
 	lfPOINT n_pos, o_pos;
 	DWORD n_col, undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 	bool bRet = false;
 
 	if(!parent) return false;
@@ -1028,6 +1067,7 @@ ErrorBar::PropertyDlg()
 			break;
 		case 1:								//accept for this object
 		case 2:								//   or all objects of plot
+			Undo.SetDisp(cdisp);
 			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);
@@ -1113,6 +1153,7 @@ Arrow::PropertyDlg()
 	int res, tmptype = type, undo_level = *Undo.pcb;
 	bool bRet = false;
 	DWORD o_col, n_col, undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 
 	if(!parent) return false;
 	if(!(Dlg = new DlgRoot(ArrowDlg))) return false;
@@ -1134,13 +1175,18 @@ Arrow::PropertyDlg()
 	else strcpy(TmpTxt, "Arrow properties");
 	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 328, 260, Dlg, 0x0L);
 	do {
-		LoopDlgWnd();
-		res = ExecDrawOrderButt(parent, this, Dlg->GetResult());
+		LoopDlgWnd();			res = Dlg->GetResult();
+		switch (res) {
+		case 600:	case 601:	case 602:	case 603:
+			Undo.SetDisp(cdisp);
+			res = ExecDrawOrderButt(parent, this, res);
+			}
 		switch (res) {
 		case 200:	tmptype = ARROW_NOCAP;		res = -1;	break;
 		case 201:	tmptype = ARROW_LINE;		res = -1;	break;
 		case 202:	tmptype = ARROW_TRIANGLE;	res = -1;	break;
 		case 1:		case 2:
+			Undo.SetDisp(cdisp);
 			Dlg->GetValue(301, &n_pos2.fx);		Dlg->GetValue(303, &n_pos2.fy);
 			Dlg->GetValue(305, &n_pos1.fx);		Dlg->GetValue(307, &n_pos1.fy);
 			Dlg->GetValue(101, &n_cw);			Dlg->GetValue(104, &n_cl);
@@ -1180,6 +1226,7 @@ Arrow::PropertyDlg()
 			}
 		break;
 	case 3:								//Cancel
+		Undo.SetDisp(cdisp);
 		if(*Undo.pcb > undo_level) {	//restore plot order
 			while(*Undo.pcb > undo_level)	Undo.Restore(false, 0L);
 			bRet = true;
@@ -1233,6 +1280,7 @@ Box::PropertyDlg()
 	FillDEF newFill;
 	LineDEF newLine, newHatchLine;
 	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 	lfPOINT n_pos;
 	double tmpVal, o_size, n_size;
 	bool bRet = false;
@@ -1267,11 +1315,12 @@ Box::PropertyDlg()
 		switch (res) {
 		case 1:								//accept for this object
 		case 2:								//   or all objects of plot
+			Undo.SetDisp(cdisp);
 			OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
 			OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
 			if(newFill.hatch) memcpy(&newHatchLine, newFill.hatch, sizeof(LineDEF));
 			newFill.hatch = &newHatchLine;
-			if(type & BAR_WIDTHDATA) Dlg->GetValue(309, &size);
+			if(type & BAR_WIDTHDATA) Dlg->GetValue(309, &n_size);
 			else {
 				if(Dlg->GetCheck(113)) {
 					tmpType |= BAR_RELWIDTH;
@@ -1378,6 +1427,7 @@ Whisker::PropertyDlg()
 	void *hDlg;
 	int res, tmp_type;
 	DWORD undo_flags = 0L, n_col = LineDef.color;
+	anyOutput *cdisp = Undo.cdisp;
 	lfPOINT n_pos;
 	double tmpVal, o_size, n_size, o_lw, n_lw;
 	bool bRet = false;
@@ -1399,8 +1449,8 @@ Whisker::PropertyDlg()
 			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);
+			Undo.SetDisp(cdisp);			Dlg->GetValue(101, &n_size);
+			Dlg->GetValue(104, &n_lw);		Dlg->GetColor(107, &n_col);
 			break;
 			}
 		}while (res <0);
@@ -1472,6 +1522,7 @@ DropLine::PropertyDlg()
 	bool bRet = false;
 	LineDEF newLine;
 	DWORD undo_flags = 0L;
+	anyOutput * cdisp = Undo.cdisp;
 	lfPOINT o_pos, n_pos;
 
 	if(!parent) return false;
@@ -1493,7 +1544,7 @@ DropLine::PropertyDlg()
 		switch (res) {
 		case 1:							//this line
 		case 2:							//all lines of plot
-			tmptype = 0;
+			Undo.SetDisp(cdisp);		tmptype = 0;
 			if(Dlg->GetCheck(251)) tmptype |= DL_LEFT;
 			if(Dlg->GetCheck(252)) tmptype |= DL_RIGHT;
 			if(Dlg->GetCheck(253)) tmptype |= DL_YAXIS;
@@ -1565,6 +1616,7 @@ DropLine3D::PropertyDlg()
 	bool bRet = false;
 	LineDEF newLine;
 	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 	fPOINT3D o_pos, n_pos;
 
 	if(!parent) return false;
@@ -1584,6 +1636,7 @@ DropLine3D::PropertyDlg()
 		switch (res) {
 		case 1:							//this line
 		case 2:							//all lines of plot
+			Undo.SetDisp(cdisp);
 			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
 			for(i = tmptype = 0; i < 6; i++) {
 				tmptype |= Dlg->GetCheck(251+i) ? 1<<i : 0;
@@ -1640,7 +1693,7 @@ Sphere::PropertyDlg()
 		{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},
+		{102, 103, 0, 0x0L, LTEXT, (void*)type_text[type < 4 ? type : 0], 73, 35, 15, 8},
 		{103, 104, 0, 0x0L, RTEXT, (void*)"line width", 15, 47, 28, 8},
 		{104, 105, 0, 0x0L, EDVAL1, &Line.width, 46, 47, 25, 10},
 		{105, 106, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 73, 47, 15, 8},
@@ -1660,6 +1713,7 @@ Sphere::PropertyDlg()
 	bool bRet = false, bContinue = false;
 	fPOINT3D n_pos, o_pos;
 	DWORD new_lcolor = Line.color, undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 	double o_size, n_size, o_lsize, n_lsize;
 
 	if(!parent) return false;
@@ -1680,12 +1734,11 @@ Sphere::PropertyDlg()
 			bContinue = false;
 			break;
 		case 1:		case 2:
-			Dlg->GetColor(107, &new_lcolor);	Dlg->GetValue(101, &n_size);
-			Dlg->GetValue(104, &n_lsize);
+			Undo.SetDisp(cdisp);			Dlg->GetColor(107, &new_lcolor);
+			Dlg->GetValue(101, &n_size);	Dlg->GetValue(104, &n_lsize);
 			break;
 		case 109:
-			Dlg->DoPlot(0L);
-			bContinue = true;
+			Dlg->DoPlot(0L);				bContinue = true;
 			break;
 			}
 		}while (res < 0);
@@ -1755,6 +1808,7 @@ Plane3D::PropertyDlg()
 	int res;
 	bool bRet = false;
 	DWORD new_lcolor = Line.color, undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 	double o_lsize, n_lsize, o_rbw, n_rbw, o_rbz, n_rbz;
 
 	if(!parent) return false;
@@ -1766,7 +1820,7 @@ Plane3D::PropertyDlg()
 	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)
+	if(parent && ((parent->Id==GO_RIBBON && parent->type == 2) || parent->Id==GO_GRID3D))
 		Dlg->ShowItem(200, false);								//paravent plot
 	if(parent->name) sprintf(TmpTxt, "Plane of %s", parent->name);
 	else strcpy(TmpTxt, "Plane properties");
@@ -1776,7 +1830,8 @@ Plane3D::PropertyDlg()
 		res = Dlg->GetResult();
 		switch (res) {
 		case 1:		case 2:
-			Dlg->GetColor(104, &new_lcolor);	Dlg->GetValue(101, &n_lsize);		Dlg->GetValue(202, &n_rbw);
+			Undo.SetDisp(cdisp);				Dlg->GetColor(104, &new_lcolor);
+			Dlg->GetValue(101, &n_lsize);		Dlg->GetValue(202, &n_rbw);
 			Dlg->GetValue(205, &n_rbz);
 			break;
 			}
@@ -1852,6 +1907,7 @@ Brick::PropertyDlg()
 	int res;
 	bool bRet = false;
 	DWORD col1 = Line.color, undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 	double o_lw = Line.width, n_lw, o_height = height, n_height, o_width = width, n_width;
 	double o_depth = depth, n_depth;
 	fPOINT3D o_pos, n_pos;
@@ -1871,9 +1927,8 @@ Brick::PropertyDlg()
 		LoopDlgWnd();
 		res = Dlg->GetResult();
 		switch (res) {
-		case 1:
-		case 2:
-			Dlg->GetColor(104, &col1);
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);			Dlg->GetColor(104, &col1);
 			Dlg->GetValue(101, &n_lw);		Dlg->GetValue(301, &n_pos.fx);
 			Dlg->GetValue(303, &n_pos.fy);	Dlg->GetValue(305, &n_pos.fz);
 			Dlg->GetValue(307, &n_height);	Dlg->GetValue(108, &n_width);
@@ -1971,6 +2026,7 @@ Arrow3D::PropertyDlg()
 	int res, tmptype = type, undo_level = *Undo.pcb;
 	bool bRet = false;
 	DWORD o_col, n_col, undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 
 	if(!parent) return false;
 	if(!(Dlg = new DlgRoot(ArrowDlg))) return false;
@@ -1995,6 +2051,7 @@ Arrow3D::PropertyDlg()
 		case 201:	tmptype = ARROW_LINE;		res = -1;	break;
 		case 202:	tmptype = ARROW_TRIANGLE;	res = -1;	break;
 		case 1:		case 2:
+			Undo.SetDisp(cdisp);
 			Dlg->GetValue(301, &n_pos2.fx);		Dlg->GetValue(303, &n_pos2.fy);
 			Dlg->GetValue(305, &n_pos2.fz);		Dlg->GetValue(307, &n_pos1.fx);
 			Dlg->GetValue(309, &n_pos1.fy);		Dlg->GetValue(311, &n_pos1.fz);
@@ -2065,6 +2122,7 @@ Line3D::PropertyDlg()
 	int res;
 	bool bRet = false;
 	LineDEF newLine;
+	anyOutput *cdisp = Undo.cdisp;
 
 	if(!parent) return false;
 	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
@@ -2077,6 +2135,7 @@ Line3D::PropertyDlg()
 	}while (res < 0);
 	switch(res) {
 	case 1:							//OK pressed
+		Undo.SetDisp(cdisp);
 		OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
 		if(cmpLineDEF(&Line, &newLine)) {
 			if(parent->Id == GO_GRID3D) {
@@ -2105,24 +2164,32 @@ Label::PropertyDlg()
 	TabSHEET tab1 = {0, 27, 10, "Label"};
 	TabSHEET tab2 = {27, 75, 10, "Font & Style"};
 	TabSHEET tab3 = {75, 110, 10, "Position"};
+	bool isDataLabel = ((parent) && (parent->Id == GO_PLOTSCATT || parent->Id == GO_XYSTAT || parent->Id == GO_BOXPLOT));
+	int bwidth = isDataLabel ? 55 : 45;
+	double lspc = 100.0;
 	DlgInfo LabelDlg[] = {
-		{1, 2, 0, DEFAULT, PUSHBUTTON, (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},
+		{1, 2, 0, DEFAULT, PUSHBUTTON, isDataLabel ? (void*)"Apply to LABEL" : (void*)"OK", 170, 10, bwidth, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 170, isDataLabel ? 40 : 25, bwidth, 12},
+		{3, isDataLabel ? 10 : 50, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
 		{4, 5, 100, ISPARENT, SHEET, &tab1, 5, 10, 159, 100},
 		{5, 6, 200, ISPARENT | CHECKED, SHEET, &tab2, 5, 10, 159, 100},
 		{6, 0, 300, ISPARENT, SHEET, &tab3, 5, 10, 159, 100},
+		{10, 50, 0, 0x0L, PUSHBUTTON, (void*)"Apply to PLOT", 170, 25, bwidth, 12},
 		{50, 0, 600, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
-		{100, 101, 0, 0x0L, RTEXT, (void*)"size", 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},
+		{100, 101, 0, 0x0L, RTEXT, (void*)"size", 30, 33, 45, 8},
+		{101, 102, 0, 0x0L, INCDECVAL1, &TextDef.fSize, 80, 33, 33, 10},
+		{102, 103, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 115, 33, 20, 8},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"color", 30, 45, 45, 8},
+		{104, 107, 0, OWNDIALOG, COLBUTTON, (void *)TextDef.ColTxt, 80, 45, 25, 10},
 		{105, 106, 0, 0x0L, LTEXT, (void*)"text:", 10, 83, 25, 8},
 		{106, 0, 0, TOUCHEXIT, EDTEXT, (void*)TextDef.text, 10, 95, 149, 10},
-		{107, 108, 0, 0x0L, RTEXT, (void*)"rotation", 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},
+		{107, 108, 0, 0x0L, RTEXT, (void*)"rotation", 30, 57, 45, 8},
+		{108, 109, 0, 0x0L, EDVAL1, &TextDef.RotBL, 80, 57, 25, 10},
+		{109, 150, 0, 0x0L, LTEXT, (void *)"deg.", 107, 57, 20, 8},
+		{150, 105, 151, ISPARENT | CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
+		{151, 152, 0, 0x0L, RTEXT, (void *)"line spacing", 30, 69, 45, 8},
+		{152, 153, 0, 0x0L, INCDECVAL1, &lspc, 80, 69, 33, 10},
+		{153, 0, 0, 0x0L, LTEXT, (void *)"%", 115, 69, 20, 8},
 		{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},
@@ -2158,13 +2225,21 @@ Label::PropertyDlg()
 	void *hDlg;
 	int res, i, c_style, c_font;
 	bool RetVal = false, check;
+	double tmp;
 	DWORD undo_flags = 0x0;
+	anyOutput *cdisp = Undo.cdisp;
 	lfPOINT o_pos, o_dist, n_pos, n_dist;
 	TextDEF OldTxtDef, NewTxtDef;
 	Axis *pa;
 	fmtText *fmt = 0L;
 
-	if(parent && (Dlg = new DlgRoot(LabelDlg))) {
+	if(!parent) return false;
+	if(parent->Id == GO_MLABEL) {
+		lspc = 100.0 * parent->GetSize(SIZE_LSPC);
+		}
+	if (lspc < 50.0) lspc = 100.0;
+	if(Dlg = new DlgRoot(LabelDlg)) {
+		if(parent->Id == GO_MLABEL) Dlg->ShowItem(150, true);
 		Dlg->TextFont(221, FONT_HELVETICA);		Dlg->TextFont(222, FONT_TIMES);
 		Dlg->TextFont(223, FONT_COURIER);		Dlg->TextFont(224, FONT_GREEK);
 		Dlg->TextStyle(205, TXS_BOLD);			Dlg->TextStyle(206, TXS_ITALIC);
@@ -2236,15 +2311,22 @@ Label::PropertyDlg()
 	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);
+	hDlg = CreateDlgWnd("Label properties", 50, 50, isDataLabel ? 470 : 450, 254, Dlg, 0x0L);
 	do{
-		LoopDlgWnd();
-		res = ExecDrawOrderButt(parent, this, Dlg->GetResult());
+		LoopDlgWnd();			res = Dlg->GetResult();
 		switch (res) {
+		case 600:	case 601:	case 602:	case 603:
+			Undo.SetDisp(cdisp);
+			res = ExecDrawOrderButt(parent, this, res);
+			}
+		switch (res) {
+		case 10:
+			Undo.SetDisp(cdisp);
+			parent->Command(CMD_SAVE_LABELS, 0L, 0L);
+			undo_flags |= UNDO_CONTINUE;
 		case 1:
 			Dlg->GetValue(101, &NewTxtDef.fSize);	Dlg->GetColor(104, &NewTxtDef.ColTxt);
-			Dlg->GetValue(108, &NewTxtDef.RotBL);
-			Dlg->GetInt(315, &NewTxtDef.Align);
+			Dlg->GetValue(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;
@@ -2299,7 +2381,8 @@ Label::PropertyDlg()
 			break;
 			}
 		}while (res < 0);
-	if(res == 1) {
+	if(res == 1 || res == 10) {
+		Undo.SetDisp(cdisp);
 		if(parent->Id == GO_TICK && parent->parent && parent->parent->Id == GO_AXIS)
 			pa = (Axis*)parent->parent;
 		else if(parent->Id == GO_MLABEL && parent->parent->Id == GO_TICK 
@@ -2311,14 +2394,14 @@ Label::PropertyDlg()
 			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)) {
+		if(res == 1 && 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) {
+			else if(res == 10 || parent->Id == GO_MLABEL) {
 				if(parent->Command(CMD_SETTEXTDEF, &NewTxtDef, 0L))RetVal = true;
 				}
 			else {
@@ -2341,15 +2424,18 @@ Label::PropertyDlg()
 			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 if(res == 10 || parent->Id == GO_MLABEL) {
+				if(n_dist.fx != o_dist.fx && parent->SetSize(SIZE_LB_XDIST, n_dist.fx)) RetVal = true;
+				if(n_dist.fy != o_dist.fy && parent->SetSize(SIZE_LB_YDIST, n_dist.fy)) RetVal = true;
 				}
 			else {
 				Undo.SaveLFP(this, &fDist, undo_flags);		undo_flags |= UNDO_CONTINUE;
 				fDist.fx = n_dist.fx;	fDist.fy = n_dist.fy;
 				}
 			}
+		if(parent->Id == GO_MLABEL && Dlg->GetValue(152, &tmp) && tmp != lspc) {
+			parent->SetSize(SIZE_LSPC, tmp/100.0);	RetVal = true;
+			}
 		if(undo_flags & UNDO_CONTINUE) RetVal = true;
 		}
 	CloseDlgWnd(hDlg);
@@ -2407,6 +2493,7 @@ segment::PropertyDlg()
 	LineDEF newLine, newFillLine;
 	FillDEF newFill;
 	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 
 	if(!parent) return false;
 	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&segLine, 0);
@@ -2425,8 +2512,8 @@ segment::PropertyDlg()
 		LoopDlgWnd();
 		res = Dlg->GetResult();
 		switch(res) {
-		case 1:
-		case 2:
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);
 			OD_filldef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
 			OD_filldef(OD_GETFILL, 0L, 0L, 0L, (void *)&newFill, 0);
 			memcpy(&newFillLine, &segFillLine, sizeof(LineDEF));
@@ -2511,6 +2598,7 @@ polyline::PropertyDlg()
 	DlgRoot *Dlg;
 	void *hDlg;
 	int res, undo_level = *Undo.pcb;
+	anyOutput *cdisp = Undo.cdisp;
 	bool bRet = false;
 	LineDEF newLine;
 
@@ -2520,8 +2608,14 @@ polyline::PropertyDlg()
 	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());
+		LoopDlgWnd();			res = Dlg->GetResult();
+		switch (res) {
+		case 600:	case 601:	case 602:	case 603:
+			Undo.SetDisp(cdisp);
+			res = ExecDrawOrderButt(parent, this, res);	break;
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);	break;
+			}
 	}while (res < 0);
 	switch(res) {
 	case 1:							//OK pressed
@@ -2570,6 +2664,7 @@ polygon::PropertyDlg()
 	FillDEF newFill;
 	DWORD undo_flags = 0L;
 	int res, undo_level = *Undo.pcb;
+	anyOutput *cdisp = Undo.cdisp;
 	bool bRet = false;
 
 	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&pgLine, 0);
@@ -2578,8 +2673,14 @@ polygon::PropertyDlg()
 	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());
+		LoopDlgWnd();			res = Dlg->GetResult();
+		switch(res) {
+		case 600:	case 601:	case 602:	case 603:
+			Undo.SetDisp(cdisp);
+			res = ExecDrawOrderButt(parent, this, res);		break;
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);	break;
+			}
 		}while (res < 0);
 	switch (res) {
 	case 1:						//OK pressed
@@ -2662,6 +2763,7 @@ rectangle::PropertyDlg()
 	LineDEF old_Line, new_Line, old_FillLine, new_FillLine;
 	FillDEF old_Fill, new_Fill;
 	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 	bool bRet = false;
 
 	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
@@ -2679,8 +2781,14 @@ rectangle::PropertyDlg()
 		type == 2 ? (char*)"rounded rectangle" : (char*)"rectangle properties", 
 		50, 50, 330, 248, Dlg, 0x0L);
 	do{
-		LoopDlgWnd();
-		res = ExecDrawOrderButt(parent, this, Dlg->GetResult());
+		LoopDlgWnd();			res = Dlg->GetResult();
+		switch(res) {
+		case 600:	case 601:	case 602:	case 603:
+			Undo.SetDisp(cdisp);
+			res = ExecDrawOrderButt(parent, this, res);		break;
+		case 1:		case 2:
+			Undo.SetDisp(cdisp);	break;
+			}
 		}while (res < 0);
 	switch (res) {
 	case 1:						//OK pressed
@@ -2744,9 +2852,9 @@ PlotScatt::CreateBarChart()
 		{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},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{100, 101, 0, 0x0L, LTEXT, (void*)"spread sheet range for values", 10, 25, 60, 8},
-		{101, 102, 0, 0x0L, EDTEXT, TmpTxt, 10, 38, 110, 10},
+		{101, 102, 0, 0x0L, RANGEINPUT, TmpTxt, 10, 38, 110, 10},
 		{102, 0, 110, ISPARENT | CHECKED, GROUPBOX, (void*)" style ", 10, 55, 110, 61}, 
 		{110, 0, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 25, 62, 90, 50},
 		{200, 201, 0, 0x0L, RTEXT, (void*)"start value", 10, 35, 38, 8},
@@ -2765,6 +2873,7 @@ PlotScatt::CreateBarChart()
 	LineDEF Line;
 	FillDEF Fill; 
 
+	if(!parent || !data) return false;
 	memcpy(&Line, defs.GetOutLine(), sizeof(LineDEF));
 	memcpy(&Fill, defs.GetFill(), sizeof(FillDEF));
 	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
@@ -2772,6 +2881,7 @@ PlotScatt::CreateBarChart()
 	data->GetSize(&width, &height);
 	sprintf(TmpTxt, "a1:a%d", height);
 	if(!(Dlg = new DlgRoot(BarDlg)))return false;
+	Dlg->ItemCmd(101, CMD_SET_DATAOBJ, data);
 	hDlg = CreateDlgWnd("Simple bar chart", 50, 50, 370, 280, Dlg, 0x0L);
 	do {
 		LoopDlgWnd();
@@ -2848,11 +2958,11 @@ PlotScatt::PropertyDlg()
 		{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},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{100, 101, 0, 0x0L, LTEXT, (void*)"range for X Data", 10, 30, 60, 8},
-		{101, 102, 0, 0x0L, EDTEXT, text1, 20, 40, 100, 10},
+		{101, 102, 0, 0x0L, RANGEINPUT, text1, 20, 40, 100, 10},
 		{102, 103, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 55, 60, 8},
-		{103, 104, 0, 0x0L, EDTEXT, text2, 20, 65, 100, 10},
+		{103, 104, 0, 0x0L, RANGEINPUT, text2, 20, 65, 100, 10},
 		{104, 105, 0, 0x0L, ICON, (void*)&icon, 10, 85, 10, 10},
 		{105, 106, 0, 0x0L, LTEXT, (void*)"Valid ranges include e.g. \'a1:g13\'", 30, 85, 30, 6},
 		{106, 107, 0, 0x0L, LTEXT, (void*)"or \'b4:j4\' if data are available.", 30, 91, 30, 6},
@@ -2870,10 +2980,10 @@ PlotScatt::PropertyDlg()
 		{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},
+		{304, 0, 0, 0x0L, RANGEINPUT, text3, 20, 93, 100, 10},
 		{400, 401, 0, 0x0L, CHECKBOX, (void*)"add labels to data points", 15, 28, 50, 8},
 		{401, 402, 0, 0x0L, LTEXT, (void*)"spread sheet range for labels:", 15, 40, 60, 8},
-		{402, 0, 0, 0x0L, EDTEXT, text4, 20, 51, 100, 10},
+		{402, 0, 0, 0x0L, RANGEINPUT, text4, 20, 51, 100, 10},
 		{500, 501, 0, TOUCHEXIT|CHECKED|ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl),60,40,20,20},
 		{501, 502, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 80, 40, 20, 20},
 		{502, 503, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)(OD_ErrBarTempl), 100, 40, 20, 20},
@@ -2891,12 +3001,17 @@ PlotScatt::PropertyDlg()
 		TXA_HLEFT | TXA_VBOTTOM, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, TmpTxt};
 	AccRange *rX, *rY, *rE, *rL;
 
+	if(!parent || !data) return false;
 	if(Id == GO_BARCHART) return CreateBarChart();
 	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;
+	Dlg->ItemCmd(101, CMD_SET_DATAOBJ, data);
+	Dlg->ItemCmd(103, CMD_SET_DATAOBJ, data);
+	Dlg->ItemCmd(304, CMD_SET_DATAOBJ, data);
+	Dlg->ItemCmd(402, CMD_SET_DATAOBJ, data);
 #ifdef _WINDOWS
 	for(i = 104; i <= 107; i++) Dlg->TextSize(i, 12);
 #else
@@ -3072,7 +3187,161 @@ PlotScatt::PropertyDlg()
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Regression properties dialog
+// calculate means and error to create a xy-plot
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+xyStat::PropertyDlg()
+{
+	char text1[100], text2[100];
+	DlgInfo StatDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 130, 10, 45, 12},
+		{2, 10, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 25, 45, 12},
+		{10, 100, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"range for grouping variable", 10, 10, 90, 9},
+		{101, 102, 0, 0x0L, LTEXT, (void*)"(X data)", 10, 20, 60, 9},
+		{102, 103, 0, 0x0L, RANGEINPUT, text1, 10, 30, 100, 10},
+		{103, 104, 0, 0x0L, LTEXT, (void*)"range for Y data", 10, 45, 90, 9},
+		{104, 200, 0, 0x0L, RANGEINPUT, text2, 10, 55, 100, 10},
+		{200, 300, 201, ISPARENT | CHECKED, GROUPBOX, (void*) " draw means ", 10, 75, 165, 50},
+		{201, 202, 0, CHECKED, CHECKBOX, (void*)" line", 15, 80, 50, 9},
+		{202, 203, 0, CHECKED, CHECKBOX, (void*)" symbols", 15, 90, 50, 9},
+		{203, 204, 0, 0x0L, CHECKBOX, (void*)" bars", 15, 100, 50, 9},
+		{204, 205, 0, 0x0L, LTEXT, (void*)"using", 65, 90, 30, 9},
+		{205, 206, 0, CHECKED, RADIO1, (void*)" arithmetic mean", 95, 80, 50, 9},
+		{206, 207, 0, 0x0L, RADIO1, (void*)" geometric mean", 95, 90, 50, 9},
+		{207, 208, 0, 0x0L, RADIO1, (void*)" harmonic mean", 95, 100, 50, 9},
+		{208, 0, 0, 0x0L, RADIO1, (void*)" median", 95, 110, 50, 9},
+		{300, 400, 301, ISPARENT | CHECKED, GROUPBOX, (void*) " draw error bars ", 10, 130, 165, 40},
+		{301, 302, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" std. deviation (SD)", 15, 135, 70, 9},
+		{302, 303, 0, ISRADIO, CHECKBOX, (void*)" std. error (SEM)", 15, 145, 70, 9},
+		{303, 304, 0, ISRADIO, CHECKBOX, (void*)" 25, 75% percentiles", 95, 135, 70, 9},
+		{304, 305, 0, ISRADIO, CHECKBOX, (void*)" min and max", 95, 145, 70, 9},
+		{305, 306, 0, ISRADIO, CHECKBOX, (void*)" ", 15, 155, 70, 9},
+		{306, 307, 0, 0x0L, EDVAL1, &ci, 28, 154, 15, 10},
+		{307, 0, 0, 0x0L, LTEXT, (void*) "%  conf. interval", 45, 155, 70, 9},
+		{400, 0, 401, ISPARENT | CHECKED, GROUPBOX, (void*) " number of cases ", 10, 175, 165, 30},
+		{401, 402, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" on top of error", 15, 180, 70, 9},
+		{402, 403, 0, ISRADIO, CHECKBOX, (void*)" on top of mean", 95, 180, 70, 9},
+		{403, 404, 0, 0x0L, LTEXT, (void*)"prefix:", 15, 190, 24, 9},
+		{404, 0, 0, LASTOBJ, EDTEXT, (void*)"n = ", 40, 189, 30, 10}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	bool bRet = false;
+	int i, res, width, height;
+	double x, y, e, f, dx, dy;
+	lfPOINT fp1, fp2;
+	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};
+
+	if(!parent || !data) return false;
+	data->GetSize(&width, &height);
+	sprintf(text1, "a1:a%d", height);		sprintf(text2, "b1:b%d", height);
+	if(!(Dlg = new DlgRoot(StatDlg)))return false;
+	Dlg->ItemCmd(102, CMD_SET_DATAOBJ, data);
+	Dlg->ItemCmd(104, CMD_SET_DATAOBJ, data);
+	text1[0] = text2[0] = 0;
+	hDlg = CreateDlgWnd("Mean and Error Plot", 50, 50, 370, 450, Dlg, 0x0L);
+	do {
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch(res) {
+		case 0:
+			if(Dlg->GetCheck(10)) res=-1;
+			break;
+		case 1:
+			if(!(Dlg->GetText(102, text1) && Dlg->GetText(104, text2) && text1[0] && text2[0])) res = 2;
+			break;
+			}
+		}while (res <0);
+	if(res == 1) {
+		xRange = strdup(text1);		yRange = strdup(text2);
+		type = 0;
+		if(Dlg->GetCheck(201)) type |= 0x0001;		if(Dlg->GetCheck(202)) type |= 0x0002;
+		if(Dlg->GetCheck(203)) type |= 0x0004;
+		if(Dlg->GetCheck(205)) type |= 0x0010;		if(Dlg->GetCheck(206)) type |= 0x0020;
+		if(Dlg->GetCheck(207)) type |= 0x0040;		if(Dlg->GetCheck(208)) type |= 0x0080;
+		if(Dlg->GetCheck(301)) type |= 0x0100;		if(Dlg->GetCheck(302)) type |= 0x0200;
+		if(Dlg->GetCheck(303)) type |= 0x0400;		if(Dlg->GetCheck(304)) type |= 0x0800;
+		if(Dlg->GetCheck(305)) type |= 0x1000;
+		if(Dlg->GetCheck(401)) type |= 0x2000;		if(Dlg->GetCheck(402)) type |= 0x4000;
+		Dlg->GetValue(306, &ci);					TmpTxt[0] = 0;
+		if(Dlg->GetText(404, TmpTxt) && TmpTxt[0]) case_prefix = strdup(TmpTxt);
+		CreateData();
+		if(type && curr_data) {
+			data->GetSize(&width, &height);			nPoints = height;
+			sprintf(text1, "a1:a%d", height);		sprintf(text2, "b1:b%d", height);
+			Bounds.Xmin = Bounds.Ymin = HUGE_VAL;		Bounds.Xmax = Bounds.Ymax = -HUGE_VAL;
+			for(i= 0; i < height; i++) {
+				if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 1, &y))
+					CheckBounds(x, y);
+				}
+			if(type & 0x0001) {
+				if(nPoints >1) TheLine = new DataLine(this, curr_data, text1, text2); 
+				}
+			if((type & 0x0002) && (Symbols = (Symbol**)calloc(nPoints, sizeof(Symbol*)))) {
+				for(i = 0; i < height; i++) {
+					if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 1, &y)
+						&& (Symbols[i] = new Symbol(this, curr_data, x, y, DefSym, 0, i, 1, i)))
+						Symbols[i]->idx = i;
+					}
+				}
+			if((type & 0x0004) && (Bars = (Bar**)calloc(nPoints, sizeof(Bar*)))) {
+				for(i = 0; i < height; i++) {
+					if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 1, &y))
+						Bars[i] = new Bar(this, curr_data, x, y, BAR_VERTB | BAR_RELWIDTH, 0, i, 1, i);
+					}
+				}
+			if(type & 0x1f00) Errors = (ErrorBar**)calloc(nPoints, sizeof(ErrorBar*));
+			if((type & 0x1300) && Errors) {
+				for(i = 0; i < height; i++) {
+					if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 1, &y)
+						&& curr_data->GetValue(i, 2, &e))
+						Errors[i]= new ErrorBar(this, curr_data, x, y, e, 0, 0, i, 1, i, 2, i);
+					}
+				}
+			if((type & 0x0c00) && Errors) {
+				for(i = 0; i < height; i++) {
+					if(curr_data->GetValue(i, 0, &x) && curr_data->GetValue(i, 2, &e) && curr_data->GetValue(i, 3, &f)){
+						fp1.fx = fp2.fx = x;	fp1.fy = e;		fp2.fy = f;
+						Errors[i]= (ErrorBar*)new Whisker(this, curr_data, fp1, fp2, 0, 0, i, 2, i, 0, i, 3, i);
+						}
+					}
+				}
+			if((type & 0x6000) && (Labels = (Label**)calloc(nPoints, sizeof(Label*)))) {
+				dy = -0.4 * defs.GetSize(SIZE_SYMBOL);
+				if(type & 0x2000){
+					lbdef.Align = TXA_HCENTER | TXA_VBOTTOM;
+					dx = 0.0;
+					}
+				else {
+					lbdef.Align = TXA_HLEFT | TXA_VBOTTOM;
+					dx = -dy;
+					}
+				for(i = 0; i < height; i++) {
+					if(curr_data->GetValue(i, 0, &x) && curr_data->GetText(i, 5, TmpTxt, TMP_TXT_SIZE)){
+						if((type & 0x2000) && curr_data->GetValue(i, 4, &y)) 
+							Labels[i] = new Label(this, curr_data, x, y, &lbdef, 
+							LB_X_DATA | LB_Y_DATA, 0, i, 4, i, 5, i);
+						else if((type & 0x4000) && curr_data->GetValue(i, 1, &y)) 
+							Labels[i] = new Label(this, curr_data, x, y, &lbdef, 
+							LB_X_DATA | LB_Y_DATA, 0, i, 1, i, 5, i);
+						if(Labels[i]){
+							Labels[i]->SetSize(SIZE_LB_YDIST, dy);
+							Labels[i]->SetSize(SIZE_LB_XDIST, dx);
+							}
+						}
+					}
+				}
+			bRet = true;
+			}
+		}
+	CloseDlgWnd(hDlg);
+	delete Dlg;
+	return bRet;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Frequency distribution
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 bool
 FreqDist::PropertyDlg()
@@ -3085,9 +3354,9 @@ FreqDist::PropertyDlg()
 		{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},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{100, 101, 0, 0x0L, LTEXT, (void*)"spread sheet range for values", 10, 25, 60, 8},
-		{101, 102, 0, 0x0L, EDTEXT, TmpTxt, 10, 38, 110, 10},
+		{101, 102, 0, 0x0L, RANGEINPUT, 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},
@@ -3112,6 +3381,7 @@ FreqDist::PropertyDlg()
 	data->GetSize(&width, &height);
 	sprintf(TmpTxt, "a1:a%d", height);
 	if(!(Dlg = new DlgRoot(FreqDlg)))return false;
+	Dlg->ItemCmd(101, CMD_SET_DATAOBJ, data);
 	hDlg = CreateDlgWnd("Frequency Distribution", 50, 50, 370, 280, Dlg, 0x0L);
 	do {
 		LoopDlgWnd();
@@ -3161,11 +3431,11 @@ Regression::PropertyDlg()
 		{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},
+		{10, 0, 0, CHECKED, 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},
+		{101, 102, 0, 0x0L, RANGEINPUT, 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},
+		{103, 104, 0, 0x0L, RANGEINPUT, 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},
@@ -3186,11 +3456,13 @@ Regression::PropertyDlg()
 	bool bRet = false, bContinue = false, dValid;
 	lfPOINT *values = 0L;
 
+	if(!parent || !data) return false;
 	rX = rY = 0L;
 	data->GetSize(&width, &height);
 	sprintf(text1, "a1:a%d", height);
 	sprintf(text2, "b1:b%d", height);
-	Dlg = new DlgRoot(RegDlg);
+	if(!(Dlg = new DlgRoot(RegDlg)))return false;
+	Dlg->ItemCmd(101, CMD_SET_DATAOBJ, data);	Dlg->ItemCmd(103, CMD_SET_DATAOBJ, data);
 	hDlg = CreateDlgWnd("Linear regression analysis step 1/2", 50, 50, 380, 225, Dlg, 0x0L);
 	do {
 		LoopDlgWnd();
@@ -3310,13 +3582,13 @@ BubblePlot::PropertyDlg()
 		{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},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{100, 101, 0, 0x0L, LTEXT, (void*)"range for X Data", 10, 40, 60, 8},
-		{101, 102, 0, 0x0L, EDTEXT, text1, 20, 50, 100, 10},
+		{101, 102, 0, 0x0L, RANGEINPUT, text1, 20, 50, 100, 10},
 		{102, 103, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 65, 60, 8},
-		{103, 104, 0, 0x0L, EDTEXT, text2, 20, 75, 100, 10},
+		{103, 104, 0, 0x0L, RANGEINPUT, text2, 20, 75, 100, 10},
 		{104, 105, 0, 0x0L, LTEXT, (void*)"range for sizes", 10, 90, 60, 8},
-		{105, 0, 0, 0x0L, EDTEXT, text3, 20, 100, 100, 10},
+		{105, 0, 0, 0x0L, RANGEINPUT, text3, 20, 100, 100, 10},
 		{200, 201, 0, 0x0L, LTEXT, (void*)"Select one of the following shapes", 10, 30, 110, 8},
 		{201, 202, 0, CHECKED, SYMRADIO, (void *)&syms[0], 30, 40, 20, 20},
 		{202, 203, 0, 0x0L, SYMRADIO, (void *)&syms[1], 50, 40, 20, 20},
@@ -3352,6 +3624,7 @@ BubblePlot::PropertyDlg()
 	AccRange *rX, *rY, *rS;
 	LineDEF ShowFillLine ={0.2f, 1.0f, 0x0L, 0x0L};
 
+	if(!parent || !data) return false;
 	data->GetSize(&width, &height);
 	sprintf(text1, "a1:a%d", height);
 	sprintf(text2, "b1:b%d", height);
@@ -3359,7 +3632,10 @@ BubblePlot::PropertyDlg()
 	memcpy(&ShowFill, &BubbleFill, sizeof(FillDEF));
 	if(BubbleFill.hatch) memcpy(&ShowFillLine, BubbleFill.hatch, sizeof(LineDEF));
 	ShowFill.hatch = &ShowFillLine;
-	Dlg = new DlgRoot(PlotDlg);
+	if(!(Dlg = new DlgRoot(PlotDlg)))return false;
+	Dlg->ItemCmd(101, CMD_SET_DATAOBJ, data);
+	Dlg->ItemCmd(103, CMD_SET_DATAOBJ, data);
+	Dlg->ItemCmd(105, CMD_SET_DATAOBJ, data);
 	hDlg = CreateDlgWnd("Create Bubble Plot", 50, 50, 400, 300, Dlg, 0x0L);
 	rX = rY = rS = 0L;
 	do {
@@ -3463,6 +3739,7 @@ PolarPlot::AddPlot()
 	DataLine *TheLine = 0L;
 	Plot **tmpPlots;
 	Function *func;
+	anyOutput *cdisp = Undo.cdisp;
 
 	data->GetSize(&width, &height);
 	sprintf(text1, "a1:a%d", height);
@@ -3492,6 +3769,7 @@ PolarPlot::AddPlot()
 		(rX = new AccRange(text1)) && (rY = new AccRange(text2)) &&
 		(n = rX ? rX->CountItems() : 0) && 
 		(tmpPlots = (Plot**)realloc(Plots, (nPlots+2)*sizeof(Plot*)))) {
+		Undo.SetDisp(cdisp);
 		Plots = tmpPlots;
 		if(Dlg->GetCheck(200) || Dlg->GetCheck(201)) 
 			Symbols = (Symbol**) calloc(n+1, sizeof(Symbol*));
@@ -3588,9 +3866,9 @@ PolarPlot::PropertyDlg()
 		{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},
+		{4, 5, 100, ISPARENT | TOUCHEXIT, SHEET, &tab1, 5, 10, 131, 100},
+		{5, 10, 200, ISPARENT | TOUCHEXIT | CHECKED, SHEET, &tab2, 5, 10, 131, 100},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{100, 101, 0, 0x0L, LTEXT, (void*)"angular range (full circle)", 10, 25, 60, 8},
 		{101, 102, 0, 0x0L, RTEXT, (void*)"min =", 5, 37, 25, 8},
 		{102, 103, 0, 0x0L, EDVAL1, &lox, 30, 37, 30, 10},
@@ -3613,9 +3891,9 @@ PolarPlot::PropertyDlg()
 		{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},
+		{211, 212, 0, 0x0L, RANGEINPUT, (void*)text1, 20, 67, 100, 10},
 		{212, 213, 0, 0x0L, LTEXT, (void*)"range for y-data (radial data)", 10, 80, 50, 8},
-		{213, 0, 0, LASTOBJ, EDTEXT, (void*)text2, 20, 92, 100, 10}};
+		{213, 0, 0, LASTOBJ, RANGEINPUT, (void*)text2, 20, 92, 100, 10}};
 	DlgRoot *Dlg;
 	void *hDlg;
 	int res, width, height, i, j, k, l, n, ic, cType = 200;
@@ -3627,13 +3905,11 @@ PolarPlot::PropertyDlg()
 	TextDEF tlbdef;
 	AxisDEF ang_axis, rad_axis;
 
+	if(!parent || !data) return false;
 	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;
+	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;
 	data->GetSize(&width, &height);
 	sprintf(text1, "a1:a%d", height);
 	sprintf(text2, "b1:b%d", height);
@@ -3647,6 +3923,7 @@ PolarPlot::PropertyDlg()
 	tlbdef.Font = FONT_HELVETICA;
 	tlbdef.text = 0L;
 	if(!(Dlg = new DlgRoot(PolDlg)))return false;
+	Dlg->ItemCmd(211, CMD_SET_DATAOBJ, data);	Dlg->ItemCmd(213, CMD_SET_DATAOBJ, data);
 	hDlg = CreateDlgWnd("Create Polar Plot", 50, 50, 388, 260, Dlg, 0x0L);
 	do {
 		LoopDlgWnd();
@@ -3655,14 +3932,14 @@ PolarPlot::PropertyDlg()
 		case 0:
 			if(Dlg->GetCheck(10)) res = -1;
 			break;
-		case 5:
+		case 5:		case 4:
 			bType = true;
 			res = -1;
 			break;
 		case 1:
-			if(!bType) {		//the 'Type' sheet must have been visited
+			if(!bType) {		//the 'Coordinates' sheet must have been visited
 				bType = true;
-				Dlg->SetCheck(5, 0L, true);
+				Dlg->SetCheck(4, 0L, true);
 				res = -1;
 				}
 			break;
@@ -3765,228 +4042,257 @@ PolarPlot::PropertyDlg()
 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];
+	char text1[50], text2[50], text3[50], text4[50], text5[50], text6[50];
 	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}};
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 130, 10, 45, 12},
+		{2, 10, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 130, 25, 45, 12},
+		{10, 50, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
+		{50, 60, 51, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{51, 52, 0, 0x0L, LTEXT, (void*)"Data Source:", 10, 12, 40, 9},
+		{52, 53, 0, CHECKED | TOUCHEXIT, RADIO2, (void*)" user values", 60, 12, 50, 9},
+		{53, 0, 0, TOUCHEXIT, RADIO2, (void*)" statistical data", 60, 22, 60, 9},
+		{60, 61, 100, HIDDEN | ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{61, 0, 200, ISPARENT | CHECKED, GROUP, 0L, 0, 0, 0, 0},
+		{100, 102, 0, 0x0L, LTEXT, (void*)"range for grouping variable (X data)", 10, 39, 140, 9},
+		{102, 103, 0, 0x0L, RANGEINPUT, text1, 10, 49, 165, 10},
+		{103, 104, 0, 0x0L, LTEXT, (void*)"range for Y data", 10, 60, 90, 9},
+		{104, 150, 0, 0x0L, RANGEINPUT, text2, 10, 70, 165, 10},
+		{150, 160, 151, ISPARENT | CHECKED, GROUPBOX, (void*) " draw means ", 10, 87, 165, 45},
+		{151, 152, 0, 0x0L, CHECKBOX, (void*)" line", 15, 92, 50, 9},
+		{152, 153, 0, CHECKED, CHECKBOX, (void*)" symbols", 15, 101, 50, 9},
+		{153, 154, 0, 0x0L, LTEXT, (void*)"using", 65, 101, 30, 9},
+		{154, 155, 0, 0x0L, RADIO1, (void*)" arithmetic mean", 95, 90, 50, 9},
+		{155, 156, 0, 0x0L, RADIO1, (void*)" geometric mean", 95, 99, 50, 9},
+		{156, 157, 0, 0x0L, RADIO1, (void*)" harmonic mean", 95, 108, 50, 9},
+		{157, 0, 0, CHECKED, RADIO1, (void*)" median", 95, 117, 50, 9},
+		{160, 170, 161, ISPARENT | CHECKED, GROUPBOX, (void*) " draw boxes ", 10, 137, 165, 38},
+		{161, 162, 0, ISRADIO, CHECKBOX, (void*)" std. deviation (SD)", 15, 142, 70, 9},
+		{162, 163, 0, ISRADIO, CHECKBOX, (void*)" std. error (SEM)", 15, 151, 70, 9},
+		{163, 164, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" 25, 75% percentiles", 95, 142, 70, 9},
+		{164, 165, 0, ISRADIO, CHECKBOX, (void*)" min and max", 95, 151, 70, 9},
+		{165, 166, 0, ISRADIO, CHECKBOX, (void*)" ", 15, 161, 70, 9},
+		{166, 167, 0, 0x0L, EDVAL1, &ci_box, 28, 160, 15, 10},
+		{167, 0, 0, 0x0L, LTEXT, (void*) "%  conf. interval", 45, 161, 70, 9},
+		{170, 400, 171, ISPARENT | CHECKED, GROUPBOX, (void*) " draw whiskers ", 10, 180, 165, 38},
+		{171, 172, 0, ISRADIO, CHECKBOX, (void*)" std. deviation (SD)", 15, 185, 70, 9},
+		{172, 173, 0, ISRADIO, CHECKBOX, (void*)" std. error (SEM)", 15, 194, 70, 9},
+		{173, 174, 0, ISRADIO, CHECKBOX, (void*)" 25, 75% percentiles", 95, 185, 70, 9},
+		{174, 175, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" min and max", 95, 194, 70, 9},
+		{175, 176, 0, ISRADIO, CHECKBOX, (void*)" ", 15, 204, 70, 9},
+		{176, 177, 0, 0x0L, EDVAL1, &ci_err, 28, 203, 15, 10},
+		{177, 0, 0, 0x0L, LTEXT, (void*) "%  conf. interval", 45, 203, 70, 9},
+		{200, 202, 0, 0x0L, LTEXT, (void*)"range for common X values", 10, 39, 140, 9},
+		{202, 250, 0, 0x0L, RANGEINPUT, text1, 10, 49, 165, 10},
+		{250, 260, 251, ISPARENT | CHECKED, GROUPBOX, (void*) "                        ", 10, 68, 165, 30},
+		{251, 252, 0, 0x0L, CHECKBOX, (void*)" draw line", 15, 63, 50, 9},
+		{252, 253, 0, 0x0L, LTEXT, (void*)"range for line values", 15, 73, 80, 9},
+		{253, 0, 0, 0x0L, RANGEINPUT, text2, 15, 83, 155, 10},
+		{260, 270, 261, ISPARENT | CHECKED, GROUPBOX, (void*) "                               ", 10, 106, 165, 30},
+		{261, 262, 0, CHECKED, CHECKBOX, (void*)" draw symbols", 15, 101, 50, 9},
+		{262, 263, 0, 0x0L, LTEXT, (void*)"range for symbol values", 15, 111, 80, 9},
+		{263, 0, 0, 0x0L, RANGEINPUT, text2, 15, 121, 155, 10},
+		{270, 280, 271, ISPARENT | CHECKED, GROUPBOX, (void*) "                          ", 10, 144, 165, 50},
+		{271, 272, 0, CHECKED, CHECKBOX, (void*)" draw boxes", 15, 139, 50, 9},
+		{272, 273, 0, 0x0L, LTEXT, (void*)"range for HI values", 15, 149, 80, 9},
+		{273, 274, 0, 0x0L, RANGEINPUT, text3, 15, 159, 155, 10},
+		{274, 275, 0, 0x0L, LTEXT, (void*)"range for LO values", 15, 169, 80, 9},
+		{275, 0, 0, 0x0L, RANGEINPUT, text4, 15, 179, 155, 10},
+		{280, 0, 281, ISPARENT | CHECKED, GROUPBOX, (void*) "                              ", 10, 202, 165, 50},
+		{281, 282, 0, CHECKED, CHECKBOX, (void*)" draw whiskers", 15, 197, 50, 9},
+		{282, 283, 0, 0x0L, LTEXT, (void*)"range for HI values", 15, 207, 80, 9},
+		{283, 284, 0, 0x0L, RANGEINPUT, text5, 15, 217, 155, 10},
+		{284, 285, 0, 0x0L, LTEXT, (void*)"range for LO values", 15, 227, 80, 9},
+		{285, 0, 0, 0x0L, RANGEINPUT, text6, 15, 237, 155, 10},
+		{400, 0, 401, ISPARENT | CHECKED, GROUPBOX, (void*) " number of cases ", 10, 223, 165, 30},
+		{401, 402, 0, ISRADIO | CHECKED, CHECKBOX, (void*)" on top of error", 15, 228, 70, 9},
+		{402, 403, 0, ISRADIO, CHECKBOX, (void*)" on top of mean", 95, 228, 70, 9},
+		{403, 404, 0, 0x0L, LTEXT, (void*)"prefix:", 15, 238, 24, 9},
+		{404, 0, 0, LASTOBJ, EDTEXT, (void*)"n = ", 40, 237, 30, 10}};
 	DlgRoot *Dlg;
 	void *hDlg;
-	int i, j, k, l, m, n, ic, res, width, height;
-	bool bHor, bRet, bContinue = false;
+	bool bRet = false;
+	int i, j, k, k1, l, l1, n, ic, c, res, width, height;
+	double x, y1, y2, dx, dy;
 	lfPOINT fp1, fp2;
-	double x, y1, y2;
-	AccRange *rX, *rB1, *rB2, *rW1, *rW2, *rS, *rL1, *rL2;
+	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 = 0L, *rY1 = 0L, *rY2 = 0L;
+	int it_racc[] = {102, 104, 202, 253, 263, 273, 275, 283, 285};
 
-	rX = rB1 = rB2 = rW1 = rW2 = rS = rL1 = rL2 = 0L;
+	if(!parent || !data) return false;
 	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);
+	sprintf(text1, "a1:a%d", height);		sprintf(text2, "b1:b%d", height);
+	sprintf(text3, "c1:c%d", height);		sprintf(text4, "d1:d%d", height);
+	sprintf(text5, "e1:e%d", height);		sprintf(text6, "f1:f%d", height);
+	ci_box = ci_err = 95.0;
+	if(!(Dlg = new DlgRoot(PlotDlg)))return false;
+	for(i=0; i < 9; i++) Dlg->ItemCmd(it_racc[i], CMD_SET_DATAOBJ, data);
+	text1[0] = text2[0] = 0;
+	hDlg = CreateDlgWnd("Box and Whisker Plot", 50, 50, 370, 550, Dlg, 0x0L);
 	do {
 		LoopDlgWnd();
 		res = Dlg->GetResult();
-		switch (res) {
+		switch(res) {
 		case 0:
-			if(bContinue) res = -1;
-			else if(Dlg->GetCheck(20)) res = -1;
-			break;
-		case -1:
-			bContinue = false;
+			if(Dlg->GetCheck(10)) res=-1;
 			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;
+		case 52:	case 53:
+			if(res == 53) {
+				Dlg->ShowItem(60, true);	Dlg->ShowItem(61, false);
 				}
-			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;
+			else {
+				Dlg->ShowItem(60, false);	Dlg->ShowItem(61, true);
 				}
+			Dlg->Command(CMD_REDRAW, 0L, 0L);	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;
+		}while (res <0);
+	if(res == 1) {
+		type = 0;				dirty = true;
+		if(Dlg->GetCheck(52) && Dlg->GetText(202, text1) && text1[0] &&(rX = new AccRange(text1))) {
+			xRange = strdup(text1);
+			n = rX->CountItems();	nPoints = n;
+			//  data line
+			if(n > 1 && Dlg->GetCheck(251) && Dlg->GetText(253, TmpTxt)) {
+				TheLine = new DataLine(this, data, text1, TmpTxt);
+				bRet = true;
+				}
+			// symbols
+			if(n > 0 && Dlg->GetCheck(261) && Dlg->GetText(263, TmpTxt) && TmpTxt[0] 
+				&& (Symbols = (Symbol**)calloc(n, sizeof(Symbol*)))
+				&& (rY1 = new AccRange(TmpTxt))) {
+				yRange = strdup(TmpTxt);
+				rX->GetFirst(&i, &j);	rY1->GetFirst(&k, &l);
+				rX->GetNext(&i, &j);	rY1->GetNext(&k, &l);
+				ic = c = 0;
 				do {
-					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1) && 
-						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);
-							}
+					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1)) {
+						if(Symbols[ic] = new Symbol(this, data, x, y1, SYM_PLUS, i, j, k, l))
+							Symbols[ic++]->idx = c;
 						}
-					}while(rX->GetNext(&i, &j) && rB1->GetNext(&k, &l) && rB2->GetNext(&m, &n));
+					c++;
+					}while(rX->GetNext(&i, &j) && rY1->GetNext(&k, &l));
+				delete rY1;		rY1 = 0L;
 				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);
+			// boxes
+			if(n > 0 && Dlg->GetCheck(271) && Dlg->GetText(273, text3) && Dlg->GetText(275, text4)
+				&& (Boxes = (Box**)calloc(n, sizeof(Box*)))
+				&& (rY1 = new AccRange(text3)) && (rY2 = new AccRange(text4))) {
+				rX->GetFirst(&i, &j);	rY1->GetFirst(&k, &l);	rY2->GetFirst(&k1, &l1);
+				rX->GetNext(&i, &j);	rY1->GetNext(&k, &l);	rY2->GetNext(&k1, &l1);
 				ic = 0;
 				do {
-					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1) && 
-						data->GetValue(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);
-							}
+					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1) && data->GetValue(l1, k1, &y2)) {
+						fp1.fy = y1;	fp2.fy = y2;		fp1.fx = fp2.fx = x;
+						Boxes[ic] = new Box(this, data, fp1, fp2, BAR_RELWIDTH, i, j, k, l, i, j, k1, l1);
+						if(Boxes[ic]) Boxes[ic++]->SetSize(SIZE_BOX, 60.0);
 						}
-					}while(rX->GetNext(&i, &j) && rW1->GetNext(&k, &l) && rW2->GetNext(&m, &n));
+					}while(rX->GetNext(&i, &j) && rY1->GetNext(&k, &l) && rY2->GetNext(&k1, &l1));
+				delete rY1;		rY1 = 0L;		delete rY2;		rY2 = 0L;
 				if(ic) bRet = true;
 				}
-			}
-		if(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);
+			// whiskers
+			if(n > 0 && Dlg->GetCheck(281) && Dlg->GetText(283, text3) && Dlg->GetText(285, text4)
+				&& (Whiskers = (Whisker**)calloc(n, sizeof(Whisker*)))
+				&& (rY1 = new AccRange(text3)) && (rY2 = new AccRange(text4))) {
+				rX->GetFirst(&i, &j);	rY1->GetFirst(&k, &l);	rY2->GetFirst(&k1, &l1);
+				rX->GetNext(&i, &j);	rY1->GetNext(&k, &l);	rY2->GetNext(&k1, &l1);
 				ic = 0;
 				do {
-					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1)){
-						if(bHor) {
-							CheckBounds(y1, x);
-							Symbols[ic++] = new Symbol(this, data, y1, x, 
-								SYM_PLUS, k, l, i, j);
+					if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y1) && data->GetValue(l1, k1, &y2)) {
+						fp1.fy = y1;	fp2.fy = y2;		fp1.fx = fp2.fx = x;
+						Whiskers[ic++] = new Whisker(this, data, fp1, fp2, 0, i, j, k, l, i, j, k1, l1);
+						}
+					}while(rX->GetNext(&i, &j) && rY1->GetNext(&k, &l) && rY2->GetNext(&k1, &l1));
+				delete rY1;		rY1 = 0L;		delete rY2;		rY2 = 0L;
+				if(ic) bRet = true;
+				}
+			if (bRet) Command(CMD_AUTOSCALE, 0L, 0L);
+			}
+		else if(Dlg->GetText(102, text1) && text1[0] && Dlg->GetText(104, text2) && text2[0]){
+			xRange = strdup(text1);				yRange = strdup(text2);
+			if(Dlg->GetCheck(154)) type |= 0x0001;		if(Dlg->GetCheck(155)) type |= 0x0002;
+			if(Dlg->GetCheck(156)) type |= 0x0003;		if(Dlg->GetCheck(157)) type |= 0x0004;
+			if(Dlg->GetCheck(161)) type |= 0x0010;		if(Dlg->GetCheck(162)) type |= 0x0020;
+			if(Dlg->GetCheck(163)) type |= 0x0030;		if(Dlg->GetCheck(164)) type |= 0x0040;
+			if(Dlg->GetCheck(165)) type |= 0x0050;
+			if(Dlg->GetCheck(171)) type |= 0x0100;		if(Dlg->GetCheck(172)) type |= 0x0200;
+			if(Dlg->GetCheck(173)) type |= 0x0300;		if(Dlg->GetCheck(174)) type |= 0x0400;
+			if(Dlg->GetCheck(175)) type |= 0x0500;
+			if(Dlg->GetCheck(151)) type |= 0x1000;		if(Dlg->GetCheck(152)) type |= 0x2000;
+			if(Dlg->GetCheck(401)) type |= 0x4000;		if(Dlg->GetCheck(402)) type |= 0x8000;
+			Dlg->GetValue(166, &ci_box);				Dlg->GetValue(176, &ci_err);
+			if(Dlg->GetText(404, TmpTxt) && TmpTxt[0]) case_prefix = strdup(TmpTxt);
+			CreateData();
+			if(curr_data && type) {
+				curr_data->GetSize(&width, &height);
+				sprintf(text1, "a1:a%d", height);	sprintf(text2, "b1:b%d", height);
+				nPoints = height;
+				if(nPoints > 1 && (type & 0x1000)) {
+					TheLine = new DataLine(this, curr_data, text1, text2);
+					bRet = true;
+					}
+				if(nPoints > 0 && (type & 0x2000) && (Symbols = (Symbol**)calloc(nPoints, sizeof(Symbol*)))) {
+					for(i = 0; i < height; i++) {
+						if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 1, &y1)
+							&& (Symbols[i] = new Symbol(this, curr_data, x, y1, SYM_PLUS, 0, i, 1, i)))
+							Symbols[i]->idx = i;
+						}
+					bRet = true;
+					}
+				if(nPoints > 0 && (type & 0x00f0) && (Boxes = (Box**)calloc(nPoints, sizeof(Box*)))) {
+					for(i = 0; i < height; i++) {
+						if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 2, &y1)
+							&&	curr_data->GetValue(i, 3, &y2)) {
+							fp1.fy = y1;	fp2.fy = y2;		fp1.fx = fp2.fx = x;
+							Boxes[i] = new Box(this, curr_data, fp1, fp2, BAR_RELWIDTH, 0, i, 2, i, 0, i, 3, i);
+							if(Boxes[i]) Boxes[i]->SetSize(SIZE_BOX, 60.0);
 							}
-						else {
-							CheckBounds(x, y1);
-							Symbols[ic++] = new Symbol(this, data, x, y1, 
-								SYM_PLUS, i, j, k, l);
+						}
+					bRet = true;
+					}
+				if(nPoints > 0 && (type & 0x0f00) && (Whiskers = (Whisker**)calloc(nPoints, sizeof(Whisker*)))) {
+					for(i = 0; i < height; i++) {
+						if(curr_data->GetValue(i, 0, &x) &&	curr_data->GetValue(i, 4, &y1)
+							&&	curr_data->GetValue(i, 5, &y2)) {
+							fp1.fy = y1;	fp2.fy = y2;		fp1.fx = fp2.fx = x;
+							Whiskers[i] = new Whisker(this, curr_data, fp1, fp2, 0, 0, i, 4, i, 0, i, 5, i);
 							}
 						}
-					}while(rX->GetNext(&i, &j) && rS->GetNext(&k, &l));
-				if(ic) bRet = true;
+					bRet = true;
+					}
+				if(nPoints > 0 && (type & 0xc000) && (Labels = (Label**)calloc(nPoints, sizeof(Label*)))) {
+					dy = -0.4 * defs.GetSize(SIZE_SYMBOL);
+					if(type & 0x4000){
+						lbdef.Align = TXA_HCENTER | TXA_VBOTTOM;
+						dx = 0.0;
+						}
+					else {
+						lbdef.Align = TXA_HLEFT | TXA_VBOTTOM;
+						dx = -dy;
+						}
+					for(i = 0; i < height; i++) {
+						if(curr_data->GetValue(i, 0, &x) && curr_data->GetText(i, 7, TmpTxt, TMP_TXT_SIZE)){
+							if(curr_data->GetValue(i, 6, &y1))Labels[i] = new Label(this, curr_data, x, y1, &lbdef, 
+								LB_X_DATA | LB_Y_DATA, 0, i, 6, i, 7, i);
+							if(Labels[i]){
+								Labels[i]->SetSize(SIZE_LB_YDIST, dy);	Labels[i]->SetSize(SIZE_LB_XDIST, dx);
+								}
+							}
+						}
+					bRet = true;
+					}
 				}
 			}
-		if(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;
-			}
+		}
+	if(bRet) {
+		dirty = true;
+		Command(CMD_AUTOSCALE, 0L, 0L);
 		}
 	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;
 }
 
@@ -4005,11 +4311,11 @@ DensDisp::PropertyDlg()
 		{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},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{100, 101, 0, 0x0L, LTEXT, (void*)"range for direction (time, depth) data", 10, 30, 60, 8},
-		{101, 102, 0, 0x0L, EDTEXT, text1, 20, 40, 100, 10},
+		{101, 102, 0, 0x0L, RANGEINPUT, text1, 20, 40, 100, 10},
 		{102, 103, 0, 0x0L, LTEXT, (void*)"range for width (density) data", 10, 55, 60, 8},
-		{103, 104, 0, 0x0L, EDTEXT, text2, 20, 65, 100, 10},
+		{103, 104, 0, 0x0L, RANGEINPUT, text2, 20, 65, 100, 10},
 		{104, 0, 0, 0x0L, CHECKBOX, (void*)"vertical profile", 10, 90, 100, 8},
 		{200, 201, 0, NOSELECT, ODBUTTON, (void*)OD_filldef, 25, 30, 90, 45},
 		{201, 202, 0, TOUCHEXIT | CHECKED, RADIO1, (void*)"symmetric bars", 25, 80, 60, 8},
@@ -4021,12 +4327,14 @@ DensDisp::PropertyDlg()
 	bool bRet = false, bContinue = false, bVert;
 	AccRange *rX = 0L, *rY = 0L;
 
+	if(!parent || !data) return false;
 	OD_filldef(OD_SETLINE, 0L, 0L, 0L, (void *)defs.GetOutLine(), 0);
 	OD_filldef(OD_SETFILL, 0L, 0L, 0L, (void *)defs.GetFill(), 0);
 	data->GetSize(&width, &height);
 	sprintf(text1, "a1:a%d", height);
 	sprintf(text2, "b1:b%d", height);
-	Dlg = new DlgRoot(PlotDlg);
+	if(!(Dlg = new DlgRoot(PlotDlg)))return false;
+	Dlg->ItemCmd(101, CMD_SET_DATAOBJ, data);	Dlg->ItemCmd(103, CMD_SET_DATAOBJ, data);
 	hDlg = CreateDlgWnd("Density profile", 50, 50, 420, 260, Dlg, 0x0L);
 	do {
 		LoopDlgWnd();
@@ -4180,12 +4488,12 @@ StackBar::PropertyDlg()
 		{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},
+		{20, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{100, 101, 0, 0x0L, LTEXT, (void*)"range for common x values", 15, 30, 60, 8},
-		{101, 152, 0, 0x0L, EDTEXT, TmpTxt, 25, 40, 100, 10},
+		{101, 152, 0, 0x0L, RANGEINPUT, TmpTxt, 25, 40, 100, 10},
 		{152, 153, 0, ISPARENT | CHECKED, GROUPBOX, (void*)" ranges for y values ", 12, 60, 128, 45},
 		{153, 154, 0, 0x0L, LTEXT, 0L, 25, 65, 60, 8},
-		{154, 155, 0, 0x0L, EDTEXT, 0L, 25, 75, 100, 10},
+		{154, 155, 0, 0x0L, RANGEINPUT, 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},
@@ -4201,10 +4509,12 @@ StackBar::PropertyDlg()
 	char **rd;
 	AccRange *rX = 0L, *rY = 0L;
 
+	if(!parent || !data) return false;
 	data->GetSize(&width, &height);
 	sprintf(TmpTxt, "a1:a%d", height);
 	if(!(rd = (char**)calloc(1, sizeof(char*))))return false;
-	Dlg = new DlgRoot(StackBarDlg);
+	if(!(Dlg = new DlgRoot(StackBarDlg))) return false;
+	Dlg->ItemCmd(101, CMD_SET_DATAOBJ, data);	Dlg->ItemCmd(154, CMD_SET_DATAOBJ, data);
 	hDlg = CreateDlgWnd(Id == GO_STACKBAR ? (char*)"Stacked bar plot" : 
 		(char*)"Stacked polygons", 50, 50, 420, 260, Dlg, 0x0L);
 	do {
@@ -4308,12 +4618,12 @@ GroupBars::PropertyDlg()
 		{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},
+		{10, 0, 0, CHECKED, 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},
+		{154, 155, 0, 0x0L, RANGEINPUT, 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},
@@ -4336,9 +4646,11 @@ GroupBars::PropertyDlg()
 	int i, ic, res, ix, iy, ny, sc = 0, currYR = 0, maxYR = 0;
 	double x, y, xinc;
 
+	if(!parent || !data) return false;
 	Id = GO_STACKBAR;
 	if(!(rd = (char**)calloc(1, sizeof(char*))))return false;
 	if(!(Dlg = new DlgRoot(GBDlg)))return false;
+	Dlg->ItemCmd(154, CMD_SET_DATAOBJ, data);
 	hDlg = CreateDlgWnd("Grouped bar chart", 50, 50, 370, 240, Dlg, 0x0L);
 	do {
 		if(updateYR) {
@@ -4442,12 +4754,12 @@ Waterfall::PropertyDlg()
 		{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},
+		{20, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{100, 101, 0, 0x0L, LTEXT, (void*)"range for common x values", 15, 30, 60, 8},
-		{101, 152, 0, 0x0L, EDTEXT, text1, 25, 40, 100, 10},
+		{101, 152, 0, 0x0L, RANGEINPUT, 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},
+		{154, 155, 0, 0x0L, RANGEINPUT, 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},
@@ -4475,10 +4787,12 @@ Waterfall::PropertyDlg()
 	bool updateYR = true, bContinue = false, bRet = false, bUseSch;
 	AccRange *rX = 0L, *rY = 0L;
 
+	if(!parent || !data) return false;
 	data->GetSize(&width, &height);
 	sprintf(text1, "a1:a%d", height);
 	if(!(rd = (char**)calloc(1, sizeof(char*))))return false;
-	Dlg = new DlgRoot(StackBarDlg);
+	if(!(Dlg = new DlgRoot(StackBarDlg))) return false;
+	Dlg->ItemCmd(101, CMD_SET_DATAOBJ, data);	Dlg->ItemCmd(154, CMD_SET_DATAOBJ, data);
 	hDlg = CreateDlgWnd("Create waterfall plot", 50, 50, 420, 260, Dlg, 0x0L);
 	do {
 		if(updateYR) {
@@ -4567,15 +4881,16 @@ MultiLines::PropertyDlg()
 		{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},
+		{20, 0, 0, CHECKED, 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},
+		{102, 103, 0, 0x0L, RANGEINPUT, (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},
+		{104, 105, 0, 0x0L, RANGEINPUT, (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},
+		{107, 108, 0, OWNDIALOG, COLBUTTON, (void*)colarr[0], 25, 87, 20, 12},
+		{108, 0, 0, 0x0L, LTEXT, (void*)"line color", 47, 90, 30, 9},
 		{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},
@@ -4597,15 +4912,21 @@ MultiLines::PropertyDlg()
 	AccRange *rX = 0L, *rY = 0L;
 	DataLine *dl;
 
+	if(!parent || !data) return false;
 	data->GetSize(&width, &height);
 	sprintf(x_txt, "a1:a%d", height);
 	sprintf(y_txt, "b1:b%d", height);
-	Dlg = new DlgRoot(StackBarDlg);
+	if(!(Dlg = new DlgRoot(StackBarDlg))) return false;
+	Dlg->ItemCmd(102, CMD_SET_DATAOBJ, data);	Dlg->ItemCmd(104, CMD_SET_DATAOBJ, data);
 	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);
+			if(currYR >0) {
+				Dlg->ShowItem(106, true);	Dlg->ShowItem(108, false);
+				}
+			else {
+				Dlg->ShowItem(106, false);	Dlg->ShowItem(108, true);
+				}
 			sprintf(TmpTxt,"x-range # %d/%d", currYR+1, maxYR+1);
 			//SetText will also cause a redraw of the whole dialog
 			Dlg->SetText(101, TmpTxt);
@@ -4761,9 +5082,9 @@ PieChart::PropertyDlg()
 		{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},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{100, 101, 0, 0x0L, LTEXT, (void*)"spread sheet range for values", 10, 25, 60, 8},
-		{101, 105, 0, 0x0L, EDTEXT, TmpTxt, 15, 35, 100, 10},
+		{101, 105, 0, 0x0L, RANGEINPUT, 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},
@@ -4787,7 +5108,7 @@ PieChart::PropertyDlg()
 		{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},
+		{502, 503, 0, 0x0L, RANGEINPUT, 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},
@@ -4804,17 +5125,17 @@ PieChart::PropertyDlg()
 	lfPOINT fpCent;
 	AccRange *rY = 0L, *rR = 0L;
 
+	if(!parent || !data) return false;
 	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;
-		}
+	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(!(Dlg = new DlgRoot(PieDlg)))return false;
+	Dlg->ItemCmd(101, CMD_SET_DATAOBJ, data);	Dlg->ItemCmd(502, CMD_SET_DATAOBJ, data);
 	if(Id == GO_PIECHART) {
 		Dlg->ShowItem(105, true);		Dlg->ShowItem(106, false);
 		Dlg->ShowItem(210, true);		Dlg->ShowItem(211, false);
@@ -4922,9 +5243,9 @@ StarChart::PropertyDlg()
 		{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},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{100, 101, 0, 0x0L, LTEXT, (void*)"spread sheet range for values", 10, 30, 60, 8},
-		{101, 102, 0, 0x0L, EDTEXT, txt1, 15, 45, 100, 10},
+		{101, 102, 0, 0x0L, RANGEINPUT, txt1, 15, 45, 100, 10},
 		{102, 103, 0, 0x0L, RTEXT, (void*)"x  factor", 17, 57, 30, 8},
 		{103, 104, 0, 0x0L, EDVAL1, &factor, 48, 57, 30, 10},
 		{104, 105, 0, 0x0L, LTEXT, &txt2, 79, 57, 15, 8},
@@ -4934,7 +5255,7 @@ StarChart::PropertyDlg()
 		{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},
+		{201, 202, 0, 0x0L, RANGEINPUT, (void*)txt1, 15, 51, 100, 10},
 		{202, 203, 0, 0x0L, LTEXT, (void*)"spread sheet range for labels:", 15, 40, 60, 8},
 		{203, 204, 0, 0x0L, RTEXT, (void*)"distance:", 10, 70, 40, 8},
 		{204, 205, 0, 0x0L, EDVAL1, &lbdist, 52, 70, 30, 10},
@@ -4952,7 +5273,7 @@ StarChart::PropertyDlg()
 	TextDEF td = {0x00000000L, 0x00ffffffL, defs.GetSize(SIZE_TEXT), 0.0, 0.0, 0,
 		TXA_HCENTER | TXA_VCENTER, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, 0L}; 
 
-
+	if(!parent || !data) return false;
 	data->GetSize(&width, &height);
 	sprintf(txt1, "a1:a%d", height);
 	sprintf(txt2, "= [%s]", Units[defs.cUnits].display);
@@ -4962,6 +5283,7 @@ StarChart::PropertyDlg()
 		fPos.fy = parent->GetSize(SIZE_GRECT_TOP) + parent->GetSize(SIZE_DRECT_TOP) + frad;
 		}
 	if(!(Dlg = new DlgRoot(StarDlg)))return false;
+	Dlg->ItemCmd(101, CMD_SET_DATAOBJ, data);	Dlg->ItemCmd(201, CMD_SET_DATAOBJ, data);
 	hDlg = CreateDlgWnd("Create star chart", 50, 50, 370, 260, Dlg, 0x0L);
 	do {
 		LoopDlgWnd();
@@ -5054,17 +5376,17 @@ Scatt3D::PropertyDlg()
 		{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},
+		{10, 0, 0, CHECKED, 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},
+		{101, 102, 0, 0x0L, RANGEINPUT, text1, 20, 40, 100, 10},
 		{102, 103, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 55, 60, 8},
-		{103, 104, 0, 0x0L, EDTEXT, text2, 20, 65, 100, 10},
+		{103, 104, 0, 0x0L, RANGEINPUT, 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},
+		{105, 0, 0, 0x0L, RANGEINPUT, 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},
@@ -5084,12 +5406,15 @@ Scatt3D::PropertyDlg()
 	AccRange *rX, *rY, *rZ;
 	fPOINT3D pos1, pos2;
 
-	if(!data || !parent || parent->Id != GO_PLOT3D)return false;
+	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);
 	if(!(Dlg = new DlgRoot(Dlg3D)))return false;
 	Dlg->SetCheck(410 + AxisTempl3D, 0L, true);
+	Dlg->ItemCmd(101, CMD_SET_DATAOBJ, data);
+	Dlg->ItemCmd(103, CMD_SET_DATAOBJ, data);
+	Dlg->ItemCmd(105, CMD_SET_DATAOBJ, data);
 	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);
@@ -5250,6 +5575,7 @@ Function::PropertyDlg()
 	DWORD undo_flags = 0L;
 	LineDEF newLine;
 	double o_x1, n_x1, o_x2, n_x2, o_xstep, n_xstep;
+	anyOutput *cdisp = Undo.cdisp;
 
 	if(!parent) return false;
 	if(parent->Id == GO_FITFUNC) return parent->PropertyDlg();
@@ -5270,13 +5596,14 @@ Function::PropertyDlg()
 			break;
 			}
 		}while (res < 0);
-	while(*Undo.pcb > undo_level)	Undo.Pop();
+	Undo.SetDisp(cdisp);
+	while(*Undo.pcb > undo_level)	Undo.Pop(cdisp);
 	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);
+			cmdxy = strdup(TmpTxt);			ReshapeFormula(&cmdxy);
 			bRet = Update(0L, 0L);
 			}
 		else {							//edit existing function
@@ -5290,7 +5617,10 @@ Function::PropertyDlg()
 				Undo.String(this, &cmdxy, undo_flags);
 				free(cmdxy);	cmdxy = strdup(TmpTxt); undo_flags |= UNDO_CONTINUE;
 				}
-			if(undo_flags & UNDO_CONTINUE) Update(0L, UNDO_CONTINUE);
+			if(undo_flags & UNDO_CONTINUE){
+				Update(0L, UNDO_CONTINUE);
+				Command(CMD_MRK_DIRTY, 0L, 0L);
+				}
 			OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&newLine, 0);
 			if(cmpLineDEF(&Line, &newLine)) {
 				Undo.Line(parent, &Line, undo_flags);	undo_flags |= UNDO_CONTINUE;
@@ -5324,7 +5654,7 @@ FitFunc::PropertyDlg()
 		{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},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{100, 101, 0, 0x0L, LTEXT, (void*)"fit function by nonlinear regression", 10, 24, 100, 8},
 		{101, 102, 0, 0x0L, LTEXT, (void*)"parameters and initial values:", 10, 34, 100, 8},
 		{102, 150, 0, 0x0L, TEXTBOX, (void*)parxy, 22, 44, 122, 25},
@@ -5336,9 +5666,9 @@ FitFunc::PropertyDlg()
 		{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},
+		{401, 402, 0, 0x0L, RANGEINPUT, (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},
+		{403, 404, 0, 0x0L, RANGEINPUT, (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},
@@ -5358,6 +5688,7 @@ FitFunc::PropertyDlg()
 	double tmp, tmpy, o_x1, n_x1, o_x2, n_x2, o_xstep, n_xstep, n_chi2=chi2;
 	AccRange *rX, *rY;
 	char *o_cmdxy, *o_parxy, *tmp_char;
+	anyOutput *cdisp = Undo.cdisp;
 
 	if(!parent || !data) return false;
 	if(!(o_cmdxy = strdup(cmdxy))) return false;
@@ -5365,6 +5696,7 @@ FitFunc::PropertyDlg()
 	iter = (double)maxiter;
 	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
 	if(!(Dlg = new DlgRoot(FuncDlg))) return false;
+	Dlg->ItemCmd(401, CMD_SET_DATAOBJ, data);	Dlg->ItemCmd(403, CMD_SET_DATAOBJ, data);
 	if(!bNew){
 		Dlg->ShowItem(10, false);		Dlg->Activate(401, false);
 		Dlg->Activate(403, false);		Dlg->SetCheck(6, 0L, true);
@@ -5404,6 +5736,7 @@ FitFunc::PropertyDlg()
 				break;
 				}
 		case 7:								//Start: do nonlinear regression
+			Undo.SetDisp(cdisp);
 			if(Dlg->GetCheck(5)) {			//  the function tab must be shown
 				if(Dlg->CurrDisp) Dlg->CurrDisp->MouseCursor(MC_WAIT, true);
 				Dlg->GetText(401, text1);		Dlg->GetText(403, text2);
@@ -5432,7 +5765,8 @@ FitFunc::PropertyDlg()
 			break;
 			}
 		}while (res < 0);
-	while(*Undo.pcb > undo_level)	Undo.Pop();
+	Undo.SetDisp(cdisp);
+	while(*Undo.pcb > undo_level)	Undo.Pop(cdisp);
 	if(res == 1){						//OK pressed
 		//get ranges for x- and y data (again).
 		chi2 = n_chi2;
@@ -5477,6 +5811,13 @@ FitFunc::PropertyDlg()
 			Dlg->GetValue(506, &n_xstep);
 			undo_flags = CheckNewFloat(&x1, o_x1, n_x1, this, undo_flags);
 			undo_flags = CheckNewFloat(&x2, o_x2, n_x2, this, undo_flags);
+			undo_flags = CheckNewFloat(&xstep, o_xstep, n_xstep, this, undo_flags);
+			if(undo_flags){
+				dl->SetSize(SIZE_MIN_X, x1);			dl->SetSize(SIZE_MAX_X, x2);
+				dl->SetSize(SIZE_XSTEP, xstep);			dl->Command(CMD_SETFUNC, cmdxy, 0L);
+				dl->Command(CMD_SETPARAM, parxy, 0L);	dl->Command(CMD_SET_LINE, &Line, 0L);
+				dl->Update(0L, UNDO_CONTINUE);
+				}
 			if(o_parxy && parxy && strcmp(o_parxy, parxy)) {
 				tmp_char = parxy;	parxy = o_parxy;
 				Undo.String(this, &parxy, undo_flags);
@@ -5493,7 +5834,6 @@ FitFunc::PropertyDlg()
 				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);
@@ -5530,7 +5870,8 @@ Plot3D::AddPlot(int family)
 		{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}};
+		{564, 565, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 112, 20, 25, 25},
+		{565, 0, 0, LASTOBJ | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 12, 45, 25, 25}};
 	DlgRoot *Dlg;
 	void *hDlg;
 	int res, cSel = 560;
@@ -5543,7 +5884,7 @@ Plot3D::AddPlot(int family)
 		LoopDlgWnd();
 		res = Dlg->GetResult();
 		switch(res) {
-		case 560:	case 561:	case 562:	case 563:	case 564:
+		case 560:	case 561:	case 562:	case 563:	case 564:	case 565:
 			if(res == cSel) res = 1;
 			else {
 				cSel = res;
@@ -5558,9 +5899,9 @@ Plot3D::AddPlot(int family)
 		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);
+		else if(Dlg->GetCheck(565)) p = new Func3D(this, data);
 		if(p && p->PropertyDlg()) {
-			if(p->Id == GO_PLOT3D) delete p;
-			else if(!(bRet = Command(CMD_DROP_PLOT, p, (anyOutput *)NULL))) delete p;
+			if(!(bRet = Command(CMD_DROP_PLOT, p, (anyOutput *)NULL))) delete p;
 			}
 		else if(p) delete p;
 		}
@@ -5606,12 +5947,12 @@ Chart25D::PropertyDlg()
 		{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},
+		{20, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{100, 101, 0, 0x0L, LTEXT, (void*)"range for common x values", 15, 30, 60, 8},
-		{101, 152, 0, 0x0L, EDTEXT, text1, 25, 40, 100, 10},
+		{101, 152, 0, 0x0L, RANGEINPUT, 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},
+		{154, 155, 0, 0x0L, RANGEINPUT, 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},
@@ -5642,6 +5983,7 @@ Chart25D::PropertyDlg()
 	Scatt3D *plot;
 	AxisDEF *ax;
 
+	if(!parent || !data) return false;
 	if(plots) {
 		//Plots alredy defined: jump to config dialog
 		return false;
@@ -5649,7 +5991,8 @@ Chart25D::PropertyDlg()
 	data->GetSize(&width, &height);
 	sprintf(text1, "a1:a%d", height);
 	if(!(rd = (char**)calloc(1, sizeof(char*))))return false;
-	Dlg = new DlgRoot(Bar3D_Dlg);
+	if(!(Dlg = new DlgRoot(Bar3D_Dlg))) return false;
+	Dlg->ItemCmd(101, CMD_SET_DATAOBJ, data);	Dlg->ItemCmd(154, CMD_SET_DATAOBJ, data);
 	hDlg = CreateDlgWnd("Create 3D Bar Chart", 50, 50, 420, 260, Dlg, 0x0L);
 	do {
 		if(updateYR) {
@@ -5771,12 +6114,12 @@ Ribbon25D::PropertyDlg()
 		{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},
+		{20, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{100, 101, 0, 0x0L, LTEXT, (void*)"range for common x values", 15, 30, 60, 8},
-		{101, 152, 0, 0x0L, EDTEXT, text1, 25, 40, 100, 10},
+		{101, 152, 0, 0x0L, RANGEINPUT, 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},
+		{154, 155, 0, 0x0L, RANGEINPUT, 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},
@@ -5806,6 +6149,7 @@ Ribbon25D::PropertyDlg()
 	AxisDEF *ax;
 	Ribbon *plot;
 
+	if(!parent || !data) return false;
 	if(plots) {
 		//Plots alredy defined: jump to config dialog
 		return false;
@@ -5813,7 +6157,8 @@ Ribbon25D::PropertyDlg()
 	data->GetSize(&width, &height);
 	sprintf(text1, "a1:a%d", height);
 	if(!(rd = (char**)calloc(1, sizeof(char*))))return false;
-	Dlg = new DlgRoot(Bar3D_Dlg);
+	if(!(Dlg = new DlgRoot(Bar3D_Dlg)))return false;
+	Dlg->ItemCmd(101, CMD_SET_DATAOBJ, data);	Dlg->ItemCmd(154, CMD_SET_DATAOBJ, data);
 	hDlg = CreateDlgWnd("Create 3D Ribbon Chart", 50, 50, 420, 260, Dlg, 0x0L);
 	do {
 		if(updateYR) {
@@ -5909,17 +6254,17 @@ BubblePlot3D::PropertyDlg()
 		{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},
+		{10, 0, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{50, 0, 0, NOSELECT, ODBUTTON, (void*)(OD_AxisDesc3D), 142, 65, 45, 45},
 		{100, 101, 0, 0x0L, LTEXT, (void*)"range for X Data", 10, 25, 60, 8},
-		{101, 102, 0, 0x0L, EDTEXT, text1, 20, 35, 100, 10},
+		{101, 102, 0, 0x0L, RANGEINPUT, text1, 20, 35, 100, 10},
 		{102, 103, 0, 0x0L, LTEXT, (void*)"range for Y Data", 10, 48, 60, 8},
-		{103, 104, 0, 0x0L, EDTEXT, text2, 20, 58, 100, 10},
+		{103, 104, 0, 0x0L, RANGEINPUT, text2, 20, 58, 100, 10},
 		{104, 105, 0, 0x0L, LTEXT, (void*)"range for Z Data", 10, 71, 60, 8},
-		{105, 106, 0, 0x0L, EDTEXT, text3, 20, 81, 100, 10},
+		{105, 106, 0, 0x0L, RANGEINPUT, text3, 20, 81, 100, 10},
 		{106, 0, 150, ISPARENT | CHECKED, GROUPBOX, (void*)" diameter ", 8, 98, 125, 50},
 		{150, 151, 0, 0x0L, LTEXT, (void*)"range for diameters", 12, 102, 60, 8},
-		{151, 152, 0, 0x0L, EDTEXT, text4, 20, 112, 100, 10},
+		{151, 152, 0, 0x0L, RANGEINPUT, text4, 20, 112, 100, 10},
 		{152, 153, 0, 0x0L, LTEXT, (void*)"scaling:", 12, 125, 20, 8},
 		{153, 154, 0, TOUCHEXIT | CHECKED, RADIO1, (void*)Units[defs.cUnits].display, 38, 125, 20, 8},
 		{154, 155, 0, TOUCHEXIT, RADIO1, (void*)"with x-values", 70, 125, 20, 8},
@@ -5932,18 +6277,20 @@ BubblePlot3D::PropertyDlg()
 	DlgRoot *Dlg;
 	void *hDlg;
 	int i, res, width, height, count;
-	int cx, rx, cy, ry, cz, rz, cr, rr, s_type = 0;
+	int cx, rx, cy, ry, cz, rz, cr, rr, s_type = 5;
 	bool bRet = false;
 	double fx, fy, fz, fr;
 	Sphere **Balls;
 	AccRange *rX, *rY, *rZ, *rR;
 	Scatt3D *sc_plot;
+	int etracc[] = {101, 103, 105, 151};
 
 	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;
+	for(i = 0; i < 4; i++) Dlg->ItemCmd(etracc[i], CMD_SET_DATAOBJ, data);
 	rX = rY = rZ = rR = 0L;
 	Dlg->SetCheck(410 + AxisTempl3D, 0L, true);
 	hDlg = CreateDlgWnd("Bubble Plot 3D", 50, 50, 388, 340, Dlg, 0x0L);
@@ -5957,7 +6304,11 @@ BubblePlot3D::PropertyDlg()
 		case 4:		case 5:					//the tab sheets
 			res = -1;
 			break;
-		case 153:	case 154:	case 155:	case 156:
+		case 153:	
+			s_type = 5;						//absolute size, but use 5 to distinguish
+			res = -1;						//  from symbol
+			break;
+		case 154:	case 155:	case 156:
 			s_type = res - 153;
 			res = -1;
 			break;
@@ -5986,7 +6337,7 @@ BubblePlot3D::PropertyDlg()
 					}
 				}
 			sc_plot = new Scatt3D(this, data, Balls, count);
-			if(parent->Id == GO_PLOT3D) {
+			if(parent->Id == GO_PLOT3D || parent->Id == GO_FUNC3D) {
 				if(!(parent->Command(CMD_DROP_PLOT, sc_plot, 0L))) delete(sc_plot);
 				bRet = true;
 				}
@@ -6000,6 +6351,141 @@ BubblePlot3D::PropertyDlg()
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+// Create a 3D function plot
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool
+Func3D::PropertyDlg()
+{
+	TabSHEET tab1 = {0, 37, 10, "Function"};
+	TabSHEET tab2 = {37, 65, 10, "Style"};
+	FillDEF newFill;
+	DlgInfo FuncDlg[] = {
+		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 160, 10, 45, 12},
+		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 160, 25, 45, 12},
+		{3, 10, 4, ISPARENT | CHECKED, GROUP, 0L, 138, 40, 55, 12},
+		{4, 5, 100, ISPARENT, SHEET, &tab1, 5, 10, 149, 134},
+		{5, 50, 300, ISPARENT | CHECKED, SHEET, &tab2, 5, 10, 149, 134},
+		{10, 0, 0, 0x0L, CHECKPIN, 0L, 5, 0, 12, 8},
+		{50, 0, 0, NOSELECT, ODBUTTON, (void*)(OD_AxisDesc3D), 160, 85, 45, 45},
+		{100, 101, 0, 0x0L, LTEXT, (void*)"plot user defined function", 10, 30, 100, 8},
+		{101, 102, 0, 0x0L, RTEXT, (void*)"where x=", 10, 50, 28, 8}, 
+		{102, 103, 0, 0x0L, EDVAL1, &x1, 38, 50, 25, 10},
+		{103, 104, 0, 0x0L, RTEXT, (void*)"until", 61, 50, 17, 8}, 
+		{104, 105, 0, 0x0L, EDVAL1, &x2, 78, 50, 25, 10},
+		{105, 106, 0, 0x0L, RTEXT, (void*)"step", 102, 50, 17, 8}, 
+		{106, 107, 0, 0x0L, EDVAL1, &xstep, 119, 50, 25, 10},
+		{107, 108, 0, 0x0L, RTEXT, (void*)"z=", 10, 62, 28, 8}, 
+		{108, 109, 0, 0x0L, EDVAL1, &z1, 38, 62, 25, 10},
+		{109, 110, 0, 0x0L, EDVAL1, &z2, 78, 62, 25, 10},
+		{110, 150, 0, 0x0L, EDVAL1, &zstep, 119, 62, 25, 10},
+		{150, 200, 0, 0x0L, RTEXT, (void*)"y=", 10, 91, 10, 8}, 
+		{200, 0, 0, 0x0L, TEXTBOX, (void*)cmdxy, 22, 89, 122, 40},
+		{300, 301, 500, CHECKED, GROUPBOX, (void*)" grid ", 10, 40, 140, 100},
+		{301, 305, 400, HIDDEN | CHECKED, GROUPBOX, (void*)" surface ", 10, 40, 140, 100},
+		{305, 306, 0, CHECKED | TOUCHEXIT, RADIO1, (void*) " grid lines", 15, 25, 50, 10},
+		{306, 0, 0, TOUCHEXIT, RADIO1, (void*) " surface", 85, 25, 50, 10},
+		{400, 401, 0, 0x0L, RTEXT, (void*)"grid line width", 38, 50, 40, 8},
+		{401, 402, 0, 0x0L, EDVAL1, &Line.width, 80, 50, 25, 10},
+		{402, 403, 0, 0x0L, LTEXT, (void*)Units[defs.cUnits].display, 107, 50, 20, 8},
+		{403, 404, 0, 0x0L, RTEXT, (void*)"grid line color", 38, 62, 40, 8},
+		{404, 405, 0, OWNDIALOG, COLBUTTON, (void *)Line.color, 80, 62, 25, 10},
+		{405, 406, 0, 0x0L, RTEXT,(void*)"plane color" , 38, 74, 40, 8},
+		{406, 0, 0, OWNDIALOG, SHADE3D, &newFill, 80, 74, 25, 10},
+		{500, 0, 0, LASTOBJ | NOSELECT, ODBUTTON, (void*)OD_linedef, 15, 45, 130, 100}};
+	DlgRoot *Dlg;
+	void *hDlg;
+	int res, undo_level = *Undo.pcb;
+	bool bRet = false, bNew = true;
+	DWORD undo_flags = 0L;
+	LineDEF newLine;
+	double o_x1, n_x1, o_x2, n_x2, o_xstep, n_xstep;
+	double o_z1, n_z1, o_z2, n_z2, o_zstep, n_zstep;
+	anyOutput *cdisp = Undo.cdisp;
+
+	if(!parent) return false;
+//	if(parent->Id == GO_FITFUNC) return parent->PropertyDlg();
+	memcpy(&newFill, &Fill, sizeof(FillDEF));
+	OD_linedef(OD_SETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+	if(!(Dlg = new DlgRoot(FuncDlg))) return false;
+	if(!bNew) Dlg->ShowItem(10, false);
+	Dlg->GetValue(102, &o_x1);		n_x1 = o_x1;
+	Dlg->GetValue(104, &o_x2);		n_x2 = o_x2;
+	Dlg->GetValue(106, &o_xstep);	n_xstep = o_xstep;
+	Dlg->GetValue(108, &o_z1);		n_z1 = o_z1;
+	Dlg->GetValue(109, &o_z2);		n_z2 = o_z2;
+	Dlg->GetValue(110, &o_zstep);	n_zstep = o_zstep;
+	hDlg = CreateDlgWnd("3D Function Plot", 50, 50, 426, 320, Dlg, 0x0L);
+	if(bNew) Dlg->SetCheck(4, 0L, true);
+	do{
+		LoopDlgWnd();
+		res = Dlg->GetResult();
+		switch (res) {
+		case 305:		case 306:
+			if(Dlg->GetCheck(305)) {
+				Dlg->ShowItem(300, true);	Dlg->ShowItem(301, false);
+				}
+			else {
+				Dlg->ShowItem(300, false);	Dlg->ShowItem(301, true);
+				}
+			Dlg->DoPlot(0L);
+			res = -1;
+			break;
+		case 0:
+			if(Dlg->GetCheck(10)) res = -1;
+			break;
+			}
+		}while (res < 0);
+	Undo.SetDisp(cdisp);
+	while(*Undo.pcb > undo_level)	Undo.Pop(cdisp);
+	if(res == 1){						//OK pressed
+		if(bNew) {						//create function
+			if(Dlg->GetCheck(305)) {
+				type = 0;
+				OD_linedef(OD_GETLINE, 0L, 0L, 0L, (void *)&Line, 0);
+				}
+			else {
+				type = 1;
+				memcpy(&Fill, &newFill, sizeof(FillDEF));
+				Dlg->GetValue(401, &Line.width);
+				Dlg->GetColor(404, &Line.color);
+				Line.pattern = 0L;
+				Line.patlength = 1;
+				}
+			Dlg->GetValue(102, &x1);		Dlg->GetValue(104, &x2);
+			Dlg->GetValue(106, &xstep);
+			Dlg->GetValue(108, &z1);		Dlg->GetValue(109, &z2);
+			Dlg->GetValue(110, &zstep);		type = Dlg->GetCheck(305) ? 0 : 1;
+			if(Dlg->GetText(200, TmpTxt)) {
+				cmdxy = strdup(TmpTxt);		ReshapeFormula(&cmdxy);
+				bRet = Update();
+				}
+			}
+		else {							//edit existing function
+			Dlg->GetValue(102, &n_x1);		Dlg->GetValue(104, &n_x2);
+			Dlg->GetValue(106, &n_xstep);
+			undo_flags = CheckNewFloat(&x1, o_x1, n_x1, this, undo_flags);
+			undo_flags = CheckNewFloat(&x2, o_x2, n_x2, this, undo_flags);
+			undo_flags = CheckNewFloat(&xstep, o_xstep, n_xstep, this, undo_flags);
+			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;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Grid line properties
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 bool
@@ -6037,6 +6523,7 @@ GridLine::PropertyDlg()
 	int res, tmptype;
 	bool bRet = false;
 	DWORD undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 	LineDEF newLine;
 
 	if(!parent || !parent->parent) return false;
@@ -6075,23 +6562,18 @@ GridLine::PropertyDlg()
 		switch (res) {
 		case 1:							//this line
 		case 2:							//all lines of plot
+			Undo.SetDisp(cdisp);
 			if(flags &AXIS_3D) {
 				tmptype = 0;
-				if(Dlg->GetCheck(120)) tmptype |= 0x01;
-				if(Dlg->GetCheck(121)) tmptype |= 0x02;
-				if(Dlg->GetCheck(125)) tmptype |= 0x04;
-				if(Dlg->GetCheck(126)) tmptype |= 0x08;
-				if(Dlg->GetCheck(130)) tmptype |= 0x10;
-				if(Dlg->GetCheck(131)) tmptype |= 0x20;
+				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;
+				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;
@@ -6165,6 +6647,7 @@ Tick::PropertyDlg()
 	int res, tmp_type = type;
 	bool bRet = false;
 	DWORD new_flags = flags, undo_flags = 0;
+	anyOutput *cdisp = Undo.cdisp;
 	char *old_label = 0L;
 	TextDEF *td;
 
@@ -6229,7 +6712,7 @@ Tick::PropertyDlg()
 		res = Dlg->GetResult();
 		switch(res) {
 		case 1:		case 2:
-			new_flags &= ~0x7;
+			Undo.SetDisp(cdisp);				new_flags &= ~0x7;
 			if(Dlg->GetCheck(105)) new_flags |= AXIS_POSTICKS;
 			else if(Dlg->GetCheck(106)) new_flags |= AXIS_NEGTICKS;
 			else new_flags |= AXIS_SYMTICKS;
@@ -6461,6 +6944,7 @@ Axis::PropertyDlg()
 	double tmp, tmp2, old_x1, old_x2, old_y1, old_y2;
 	bool bRet = false, bChanged = false, upd_brk = true, bContinue = false;
 	DWORD new_color, undo_flags = 0L;
+	anyOutput *cdisp = Undo.cdisp;
 	lfPOINT *brks = 0L, *tmpbrks = 0L;
 	char *old_Label = 0L, *type_txt;
 	TextDEF label_def, *lb_def;
@@ -6755,6 +7239,7 @@ Axis::PropertyDlg()
 			break;
 			}
 		}while (res < 0);
+	Undo.SetDisp(cdisp);
 	switch (res) {
 	case 1:									//OK pressed
 		bModified = true;
@@ -6918,9 +7403,10 @@ Graph::AddPlot(int family)
 		{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}};
+		{531, 532, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 10, 70, 25, 25},
+		{532, 540, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 35, 70, 25, 25},
+		{540, 541, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 60, 70, 25, 25},
+		{541, 0, 0, LASTOBJ | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 85, 70, 25, 25}};
 	DlgRoot *Dlg;
 	void *hDlg;
 	int i, res, cSel = 520;
@@ -6950,7 +7436,7 @@ Graph::AddPlot(int family)
 		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:
+		case 531:	case 532:	case 540:	case 541:
 			if(res == cSel) res = 1;
 			else {
 				cSel = res;				res = -1;
@@ -6969,6 +7455,7 @@ Graph::AddPlot(int family)
 		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(532)) p = new xyStat(this, data);
 		else if(Dlg->GetCheck(540)) p = new StackBar(this, data);
 		else if(Dlg->GetCheck(541)) p = new StackPG(this, data);
 		else p = new PlotScatt(this, data, 0x01);
@@ -7001,10 +7488,10 @@ Graph::PropertyDlg()
 		{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},
+		{101, 102, 500, TOUCHEXIT | ISPARENT, SHEET, &tab_A, 10, 37, 147, 90},
 		{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},
+		{103, 104, 540, TOUCHEXIT | ISPARENT, SHEET, &tab_C, 10, 37, 147, 90},
+		{104, 0, 560, TOUCHEXIT | ISPARENT, SHEET, &tab_D, 10, 37, 147, 90},
 		{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},
@@ -7060,7 +7547,8 @@ Graph::PropertyDlg()
 		{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},
+		{531, 532, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 45, 100, 25, 25},
+		{532, 0, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 70, 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},
@@ -7070,7 +7558,8 @@ Graph::PropertyDlg()
 		{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}};
+		{564, 565, 0, TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 120, 60, 25, 25},
+		{565, 0, 0, LASTOBJ | TOUCHEXIT | ISRADIO, ODBUTTON, (void*)OD_PlotTempl, 20, 85, 25, 25}};
 	DlgRoot *Dlg;
 	GraphObj *p;
 	void *hDlg;
@@ -7105,7 +7594,7 @@ Graph::PropertyDlg()
 			res = -1;
 			break;
 		case 102:
-			for(i = 520; i <= 531; i++) if(Dlg->GetCheck(i))selPlt = i;
+			for(i = 520; i <= 532; i++) if(Dlg->GetCheck(i))selPlt = i;
 			Dlg->ShowItem(301, true);	Dlg->ShowItem(400, false);
 			res = -1;
 			break;
@@ -7115,7 +7604,7 @@ Graph::PropertyDlg()
 			res = -1;
 			break;
 		case 104:
-			for(i = 560; i <= 564; i++) if(Dlg->GetCheck(i))selPlt = i;
+			for(i = 560; i <= 565; i++) if(Dlg->GetCheck(i))selPlt = i;
 			Dlg->ShowItem(301, false);	Dlg->ShowItem(400, true);
 			res = -1;
 			break;
@@ -7143,11 +7632,11 @@ Graph::PropertyDlg()
 		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 528:	case 529:	case 530:	case 531:	case 532:
 		case 540:	case 541:	case 542:	case 543:
 		case 544:
 		case 560:	case 561:	case 562:	case 563:
-		case 564:
+		case 564:	case 565:
 			if(res != selPlt) {
 				selPlt = res;
 				res = -1;
@@ -7210,6 +7699,7 @@ Graph::PropertyDlg()
 				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(532)) p = new xyStat(this, data);
 				else p = new PlotScatt(this, data, 0x01);
 				}
 			else if(Dlg->GetCheck(103)) {
@@ -7225,6 +7715,7 @@ Graph::PropertyDlg()
 				else if(Dlg->GetCheck(562)) p = new Plot3D(this, data, 0x1004);
 				else if(Dlg->GetCheck(563)) p = new BubblePlot3D(this, data);
 				else if(Dlg->GetCheck(564)) p = new Plot3D(this, data, 0x2000);
+				else if(Dlg->GetCheck(565)) p = new Func3D(this, data);
 				}
 			if(p && p->PropertyDlg()) {
 				if(!Command(CMD_DROP_PLOT, p, 0L)) DeleteGO(p);
@@ -7306,6 +7797,7 @@ Graph::Configure()
 	void *hDlg;
 	int i, res, undo_level = *Undo.pcb;
 	DWORD undo_flags = 0, tmpcol;
+	anyOutput *cdisp = Undo.cdisp;
 	bool bRet = false, bContinue = false;
 	fRECT o_gr, n_gr, o_dr, n_dr;
 
@@ -7318,8 +7810,12 @@ Graph::Configure()
 	sprintf(TmpTxt, "%s properties", name ? name : "Graph");
 	hDlg = CreateDlgWnd(TmpTxt, 50, 50, 450, 300, Dlg, 0x0L);
 	do{
-		LoopDlgWnd();
-		res = ExecDrawOrderButt(parent, this, Dlg->GetResult());
+		LoopDlgWnd();			res = Dlg->GetResult();
+		switch (res) {
+		case 600:	case 601:	case 602:	case 603:
+			Undo.SetDisp(cdisp);
+			res = ExecDrawOrderButt(parent, this, res);
+			}
 		switch(res) {
 		case 0:
 			if(bContinue) res = -1;
@@ -7376,6 +7872,7 @@ Graph::Configure()
 			}
 		}while(res <0);
 	if(res == 1 && bRet) {
+		Undo.SetDisp(cdisp);
 		if(n_gr.Xmin != o_gr.Xmin || n_gr.Xmax != o_gr.Xmax ||
 			n_gr.Ymin != o_gr.Ymin || n_gr.Ymax != o_gr.Ymax ||
 			n_dr.Xmin != o_dr.Xmin || n_dr.Xmax != o_dr.Xmax ||
@@ -7395,11 +7892,12 @@ Graph::Configure()
 			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) 
+			if(Plots && NumPlots && Plots[0] && (Plots[0]->Id == GO_PLOT3D || Plots[0]->Id == GO_FUNC3D)) 
 				Plots[0]->SetColor(COL_AXIS | UNDO_STORESET, ColAX);
 			}
 		}
 	else if(res == 2) {
+		Undo.SetDisp(cdisp);
 		if(*Undo.pcb > undo_level) {	//restore plot order
 			while(*Undo.pcb > undo_level)	Undo.Restore(false, 0L);
 			bRet = true;
@@ -7469,6 +7967,7 @@ Graph::AddAxis()
 	double tlb_dist, tlb_dx, tlb_dy, lb_x, lb_y;
 	TextDEF label_def, tlbdef;
 	DWORD flags;
+	anyOutput *cdisp = Undo.cdisp;
 	Axis *the_new, **tmpAxes;
 	bool bAxis = false, bRet = false;
 	char **names;
@@ -7542,6 +8041,7 @@ Graph::AddAxis()
 			}
 		}while (res < 0);
 	if(res == 1) {
+		Undo.SetDisp(cdisp);
 		Dlg->GetValue(122, &sizAxLine);				Dlg->GetColor(125, &colAxis);
 		Dlg->GetValue(221, &axis.loc[0].fx);		Dlg->GetValue(223, &axis.loc[1].fx);
 		Dlg->GetValue(226, &axis.loc[0].fy);		Dlg->GetValue(228, &axis.loc[1].fy);
diff --git a/QT_Spec.cpp b/QT_Spec.cpp
index 9d46d8d..221de54 100755
--- a/QT_Spec.cpp
+++ b/QT_Spec.cpp
@@ -32,8 +32,8 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <math.h>
-#include "QT_Spec.h"
-
+#include "QT_Spec.h"
+
 extern tag_Units Units[];
 extern GraphObj *CurrGO;			//Selected Graphic Objects
 extern Graph *CurrGraph;
@@ -48,61 +48,63 @@ QWidget *MainWidget =0L;
 POINT CurrWidgetPos = {0,0};
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// Menu tem identifiers: synchronize this table with rlplot.rc for
+// Menu item 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
-
+#define CM_OPEN        500
+#define CM_SAVEDATAAS  501
+#define CM_EXIT        502
+#define CM_NEWGRAPH    503
+#define CM_NEWPAGE     504
+#define CM_DELGRAPH    505
+#define CM_ADDPLOT     506
+#define CM_ABOUT       507
+#define CM_ADDROWCOL   508
+#define CM_COPYGRAPH   509
+#define CM_SAVEGRAPHAS 510
+#define CM_REDRAW      511
+#define CM_ZOOM25      512
+#define CM_ZOOM50      513
+#define CM_ZOOM100     514
+#define CM_ZOOM200     515
+#define CM_ZOOM400     516
+#define CM_PRINT       517
+#define CM_EXPORT      518
+#define CM_DELOBJ      519
+#define CM_DEFAULTS    520
+#define CM_COPY        521
+#define CM_PASTE       522
+#define CM_UPDATE      523
+#define CM_ADDAXIS     524
+#define CM_UNDO        525
+#define CM_ZOOMIN      526
+#define CM_ZOOMOUT     527
+#define CM_ZOOMFIT     528
+#define CM_FILE1       529
+#define CM_FILE2       530
+#define CM_FILE3       531
+#define CM_FILE4       532
+#define CM_FILE5       533
+#define CM_FILE6       534
+#define CM_FILLRANGE   535
+#define CM_CUT         536
+#define CM_LEGEND      537
+#define CM_LAYERS      538
+#define CM_INSROW      539
+#define CM_INSCOL      540
+#define CM_DELROW      541
+#define CM_DELCOL      542
+
+#define CM_T_STANDARD  560
+#define CM_T_DRAW      561
+#define CM_T_POLYLINE  562
+#define CM_T_POLYGON   563
+#define CM_T_RECTANGLE 564
+#define CM_T_ROUNDREC  565
+#define CM_T_ELLIPSE   566
+#define CM_T_ARROW     567
+#define CM_T_TEXT      568
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Exute a file open dialog for the different situations
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -203,9 +205,9 @@ void OpenExportName(GraphObj *g, char *oldname)
 // Common alert boxes
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 void InfoBox(char *Msg)
-{
-	QMessageBox::information(QAppl->focusWidget(), "Info", Msg);
-}
+{
+	QMessageBox::information(QAppl->focusWidget(), "Info", Msg);
+}
 
 void ErrorBox(char *Msg)
 {
@@ -217,7 +219,21 @@ bool YesNoBox(char *Msg)
 	if(QMessageBox::information(QAppl->focusWidget(), "RLPlot", Msg,
 		"&Yes", "&No", 0L, 0, -1)) return false;
 	return true;
-}
+}
+
+int YesNoCancelBox(char *Msg)
+{
+	int res;
+
+	res = QMessageBox::information(QAppl->focusWidget(), "RLPlot", Msg,
+		"&Yes", "&No", "&Cancel", 0, -1);
+	switch(res) {
+	case 0:		return 1;
+	case 1:		return 0;
+	default:	return 2;
+		}
+	return 0;
+}
 
 void Qt_Box()
 {
@@ -291,8 +307,8 @@ void ShowCopyMark(anyOutput *out, RECT *mrk, int nRec)
 		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);
+	cTxtCur->bmCopyMark = new BitMapQT(rCopyMark.right - rCopyMark.left+1, 
+		rCopyMark.bottom - rCopyMark.top+1, out->hres, out->vres);
 }
 
 LineDEF liCopyMark1 = {0.0f, 1.0f, 0x00ffffffL, 0L};
@@ -444,7 +460,7 @@ KSpreadTextDrag::format(int i) const
 const char *
 KSpreadTextDrag::selectionMimeType()
 {
-	return(MIME_KSPREAD);
+	return(MIME_RLPLOT);
 }
 
 RLPGraphDrag::RLPGraphDrag(QWidget *dragSource, const char *name)
@@ -523,12 +539,12 @@ void TestClipboard(GraphObj *g)
 {
 
 	QClipboard *cb;
-	QMimeSource *mime;
+	QDragObject *mime;
 
-	if(!(cb = QAppl->clipboard()))return;
-	if(!(mime = cb->data()))return;
+	if(!(cb = QAppl->clipboard()))return;
+	if(!(mime = (QDragObject *)cb->data()))return;
 	if(g->Id == GO_SPREADDATA) {
-		if(mime->provides(MIME_RLPLOT)){
+		if(mime->provides(MIME_RLPLOT)){
 			QByteArray b = mime->encodedData(MIME_RLPLOT);
 			if(b) g->Command(CMD_PASTE_XML, (void*)b.data(), 0L);
 			return;
@@ -595,6 +611,47 @@ void EmptyClip()
 	HideCopyMark();
 	QAppl->clipboard()->clear();
 }
+
+void CopyText(char *txt, int len)
+{
+	QClipboard *cb;
+	char tmp_txt[90];
+
+	if(!(cb = QAppl->clipboard()) || !txt || !txt[0])return;
+	EmptyClip();
+	if(!len) len = strlen(txt);
+	memcpy(tmp_txt, txt, len);		tmp_txt[len] = 0;
+#ifdef Q_CHECK_PTR				//n.a. in Qt version 2
+	cb->setText(tmp_txt, QClipboard::Clipboard);
+	cb->setText(tmp_txt, QClipboard::Selection);
+#else
+	cb->setText(tmp_txt);
+#endif
+}
+
+unsigned char* PasteText()
+{
+	QClipboard *cb;
+	QMimeSource *mime;
+	unsigned char *tmp_txt = 0L;
+
+	if(!(cb = QAppl->clipboard())) return 0L;
+#ifdef Q_CHECK_PTR				//n.a. in Qt version 2
+	QString qstr = cb->text(QClipboard::Clipboard);
+#else
+	QString qstr = cb->text();
+#endif
+	if(qstr.length()) tmp_txt = (unsigned char*) strdup(qstr);
+	else {
+#ifdef Q_CHECK_PTR				//n.a. in Qt version 2
+		qstr = cb->text(QClipboard::Selection);
+#else
+		QString qstr = cb->text();
+#endif
+		if(qstr.length()) tmp_txt = (unsigned char*) strdup(qstr);
+		}
+	return tmp_txt;
+}
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Get display (desktop) size
@@ -641,10 +698,10 @@ bool com_TextOut(int x, int y, char *ctxt, TextDEF *TxtSet, QPainter *qP, anyOut
 	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;
+	if(TxtSet->Align & TXA_VCENTER) iy = y + iround(TxtSet->iSize *0.4);
 	else if(TxtSet->Align & TXA_VBOTTOM) iy = y;
-	else iy = y + iround(h*.8);
-	if(TxtSet->Align & TXA_HCENTER) ix = x - w/2;
+	else iy = y + TxtSet->iSize + 1;
+	if(TxtSet->Align & TXA_HCENTER) ix = x - (w >> 1);
 	else if(TxtSet->Align & TXA_HRIGHT) ix = x - w;
 	else ix = x;
 	currPen.setColor(SwapRB(TxtSet->ColTxt));
@@ -661,16 +718,8 @@ bool com_TextOut(int x, int y, char *ctxt, TextDEF *TxtSet, QPainter *qP, anyOut
 			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);
-			}
+		if(TxtSet->Style & TXS_SUB) iy += o->un2iy(TxtSet->fSize*0.4);
+		else if(TxtSet->Style & TXS_SUPER) iy -= o->un2iy(TxtSet->fSize*0.4);
 		qP->drawText(ix-x, iy-y, txt, -1);
 		}
 	else {
@@ -682,16 +731,8 @@ bool com_TextOut(int x, int y, char *ctxt, TextDEF *TxtSet, QPainter *qP, anyOut
 			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);
-			}
+		if(TxtSet->Style & TXS_SUB) iy += o->un2iy(TxtSet->fSize*0.4);
+		else if(TxtSet->Style & TXS_SUPER) iy -= o->un2iy(TxtSet->fSize*0.4);
 		qP->drawText(ix, iy, txt, -1);
 		}
 	qP->setPen(oldPen);
@@ -814,54 +855,215 @@ static char *critical_xpm[]={
 ".........aa********aaaaa........",
 "...........aaaaaaaaaaa..........",
 ".............aaaaaaa............"};
-
+
+//thanks to Markus Bongard for the following icon
 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"};
-
+/* width height ncolors chars_per_pixel */
+"32 32 169 2",
+/* colors */
+"AA c #FFFFFFFFFFFF",
+"BA c #FFFFF7F79494",
+"CA c #FFFFF7F78484",
+"DA c #FFFFF7F77373",
+"EA c #FFFFF7F75252",
+"FA c #FFFFF7F74242",
+"GA c #FFFFF7F73939",
+"HA c #FFFFEFEF8C8C",
+"IA c #FFFFEFEF4A4A",
+"JA c #FFFFEFEF2929",
+"KA c #F7F7E7E77B7B",
+"LA c #F7F7C6C6ADAD",
+"MA c #F7F7B5B59C9C",
+"NA c #F7F7ADAD9494",
+"OA c #EFEFF7F7F7F7",
+"PA c #EFEFF7F7EFEF",
+"AB c #EFEFEFEFEFEF",
+"BB c #EFEFEFEFDEDE",
+"CB c #EFEFE7E7A5A5",
+"DB c #EFEFDEDE7373",
+"EB c #EFEFDEDE3939",
+"FB c #EFEFDEDE2929",
+"GB c #EFEFD6D64242",
+"HB c #EFEFA5A58C8C",
+"IB c #EFEF94947B7B",
+"JB c #EFEF84847373",
+"KB c #EFEF84846363",
+"LB c #EFEF7B7B7373",
+"MB c #E7E7E7E7CECE",
+"NB c #E7E7E7E79494",
+"OB c #E7E7DEDE6B6B",
+"PB c #E7E7DEDE5252",
+"AC c #E7E7CECE5252",
+"BC c #E7E77B7B6363",
+"CC c #E7E773735A5A",
+"DC c #E7E76B6B5252",
+"EC c #E7E75A5A4A4A",
+"FC c #DEDEE7E7F7F7",
+"GC c #DEDEE7E7EFEF",
+"HC c #DEDEDEDEDEDE",
+"IC c #DEDEDEDEBDBD",
+"JC c #DEDECECE4A4A",
+"KC c #DEDE4A4A3939",
+"LC c #D6D6EFEFCECE",
+"MC c #D6D6DEDEEFEF",
+"NC c #D6D6DEDECECE",
+"OC c #CECEDEDEDEDE",
+"PC c #CECED6D6CECE",
+"AD c #CECECECEB5B5",
+"BD c #CECECECE5A5A",
+"CD c #C6C6E7E7C6C6",
+"DD c #C6C6DEDEEFEF",
+"ED c #C6C6D6D67373",
+"FD c #C6C6CECE4A4A",
+"GD c #BDBDDEDEB5B5",
+"HD c #BDBDCECEE7E7",
+"ID c #BDBDCECECECE",
+"JD c #BDBDC6C6D6D6",
+"KD c #BDBD39394242",
+"LD c #B5B5DEDEADAD",
+"MD c #B5B5BDBDCECE",
+"ND c #B5B5BDBD4242",
+"OD c #B5B5B5B59C9C",
+"PD c #ADADD6D6ADAD",
+"AE c #ADADCECEDEDE",
+"BE c #ADADBDBDB5B5",
+"CE c #ADADB5B54242",
+"DE c #ADAD4A4A5252",
+"EE c #A5A5BDBDDEDE",
+"FE c #A5A5BDBDCECE",
+"GE c #A5A5ADADB5B5",
+"HE c #A5A5ADAD4242",
+"IE c #A5A563637B7B",
+"JE c #9C9CD6D6A5A5",
+"KE c #9C9CBDBDD6D6",
+"LE c #9C9CBDBDBDBD",
+"ME c #9C9CADADCECE",
+"NE c #9C9CADADA5A5",
+"OE c #9C9C52525252",
+"PE c #9C9C4A4A5252",
+"AF c #9494CECE9C9C",
+"BF c #9494B5B5CECE",
+"CF c #9494ADAD5A5A",
+"DF c #9494ADAD3939",
+"EF c #8C8CCECE9494",
+"FF c #8C8CCECE8C8C",
+"GF c #8C8CB5B5B5B5",
+"HF c #8C8CADADD6D6",
+"IF c #8C8CADADCECE",
+"JF c #8C8CADADB5B5",
+"KF c #8C8CADADA5A5",
+"LF c #8C8C9C9CB5B5",
+"MF c #8C8C9C9CADAD",
+"NF c #8C8C8C8CA5A5",
+"OF c #8C8C52526363",
+"PF c #8C8C42425252",
+"AG c #8484B5B5CECE",
+"BG c #7B7BCECE9494",
+"CG c #7B7BC6C68484",
+"DG c #7B7BC6C67B7B",
+"EG c #7B7BADADC6C6",
+"FG c #7B7BADADADAD",
+"GG c #7B7B9C9CC6C6",
+"HG c #7B7B9C9CB5B5",
+"IG c #7B7B9C9CADAD",
+"JG c #7B7B84849C9C",
+"KG c #7B7B42425252",
+"LG c #737384848C8C",
+"MG c #737373738484",
+"NG c #737339395252",
+"OG c #6B6BC6C68484",
+"PG c #6B6BBDBD7B7B",
+"AH c #6B6BA5A5CECE",
+"BH c #6B6B9C9CBDBD",
+"CH c #6B6B9C9CA5A5",
+"DH c #6B6B94949C9C",
+"EH c #6B6B8C8CA5A5",
+"FH c #6B6B8C8C9C9C",
+"GH c #6B6B6B6B7B7B",
+"HH c #6B6B42425252",
+"IH c #6363BDBD6B6B",
+"JH c #6363B5B58C8C",
+"KH c #6363A5A58C8C",
+"LH c #63639C9CC6C6",
+"MH c #5A5AB5B56363",
+"NH c #5A5A9C9CBDBD",
+"OH c #5A5A8C8C9C9C",
+"PH c #5A5A7B7B8C8C",
+"AI c #5A5A6B6B8484",
+"BI c #5A5A63637B7B",
+"CI c #5252BDBD7373",
+"DI c #5252ADAD8484",
+"EI c #5252A5A57B7B",
+"FI c #4A4AB5B55A5A",
+"GI c #4A4A8C8CBDBD",
+"HI c #4A4A8C8CADAD",
+"II c #4A4A7B7B9C9C",
+"JI c #4A4A7B7B8484",
+"KI c #4A4A6B6B7B7B",
+"LI c #42429C9C7373",
+"MI c #42428C8C7B7B",
+"NI c #42425A5A7373",
+"OI c #42424A4A6B6B",
+"PI c #3939ADAD6B6B",
+"AJ c #3939ADAD5A5A",
+"BJ c #39399C9C6B6B",
+"CJ c #39398484BDBD",
+"DJ c #39397B7BADAD",
+"EJ c #39397B7B8C8C",
+"FJ c #393973737B7B",
+"GJ c #313163637B7B",
+"HJ c #31315A5A7B7B",
+"IJ c #292984846B6B",
+"JJ c #29296B6B7B7B",
+"KJ c #21217B7BB5B5",
+"LJ c #21217373A5A5",
+"MJ c #212173739C9C",
+"NJ c #21216B6B8C8C",
+"OJ c #212152527373",
+"PJ c #181884846363",
+"AK c #18186B6B7B7B",
+"BK c #18185A5A7373",
+"CK c #10106B6B9C9C",
+"DK c #10105A5A8484",
+"EK c #08087B7B6363",
+"FK c #08085A5A7B7B",
+"GK c #00006B6B9C9C",
+"HK c #000063637B7B",
+"IK c #00005A5A8C8C",
+/* pixels */
+"CJAHAHAHAHHILHLHBHLHLJNHNHNHGINHGINHGINHGINHGINHGINHGINHGINHGIKJ",
+"AHAAAAAAAGHDABBBGCBFBFHCPCOCEEHDJDEEJDEEHDJDEEHDJDEEJDEEHDEEJDNH",
+"AHAAAGAAABABABKEOCHCHCPCFEFEABAEHDFCDDJDDDMCJDHDFCHDHDGCDDJDDDLH",
+"EGAEDDABOAABOCEGHCHCPCHCBHJDJDEEJDEEJDEEJDAEJDEEAEJDEEJDEEJDEELH",
+"AHAAAAAAMCJDGCABHCHCBFOCPCJDEEJDEEJDEEAEEEEEAEJDEEFEFEFEEEEEJDGI",
+"AHAAAAPAAGGCABBBMCEGJDNCPCPCEEEEEEEEEEEEEEJDEEEEFEDIPIGFFEFEEENH",
+"EGOAKEAAABPAABKEHCHCHCHCMEBFABJDAEMCOCEEMCMCFEOCDDLEKHIDIDEEDDLH",
+"LHAEMCAAABABAEIFHCHCHCPCBHOCKEKEEEFEEEFEEEFEEEKEBFGEDIKFMEFEEEGI",
+"AHAAAAABGCJDABGCBBOCHGHCPCMDEEFEKEEEFEKEFEKEEEMEBFJFDIGEIFBFFEGI",
+"AHAAAAPAAHGCABBBMCJFMDOCPCJDMEKEKEMEKEKEKEKEMEIFGEFGEIIGJFGEMEGI",
+"AHAAAEAAABABGCNBDBOCNCNCMDEEMCEEEEMCJDKEDDDDLFCIDIEIPICIDIDIJDNH",
+"AHKEDDAAPAABOCKFPBICNCPCHGFEMEMEMEMEHFMEMEIFJFJELCCDCGBBCDLIIGHI",
+"EGAAAAABABJDMBNCPBADGENCMDMDMEHFIFHFMEHFIFMECHJEPCLDBGGDGDLIIGDJ",
+"AHAAAAAAAHEDKAOBEBBDCEODICMDIFIFMEIFIFIFIFIFCHEFLDPDOGGDPDMIFHHI",
+"AHAAOCABMBEABABAFABABAGBNELEHDKEHFDDEEIFJDEEFGCGLDPDPGPDPDAJMFHI",
+"AHDDEGABBBEABACAFABACBGBFHGEIFGGIFGGIFIFGGIFDHCGJEMHFIAFAFBJDHII",
+"AHAAAAABMBEAHAEAEADACABDODBEHGIFGGIFHGGGIFHGCHJHAFAFEFAFFFFJDHMJ",
+"AHAAAAAABEIADADAKACADAGBODNEGGHGIEIENFGGHGGGOHDGFFFFFFFFFFAJOHHI",
+"AHAAABABPCGAKADADAKADAFDODMFHIHIAIPFIIIIHIHIEJCIFFDGFFDGDGJJFJCK",
+"AHAAAHHCMBGAEAEAEAFAFAJCLGFKPEPEPEKDPEPEOIIKHKLIDGDGPGDGIHEKIKGK",
+"EGFCDDAANCJAIAIAIAIAIANDBKOJJBLALALBLAMANGFKHKFIMHPGMHFILIEKFKGK",
+"LHAAAABBNCFBJAJAJAEBGBDFBKOJBCNADCDCIBHBNGBKAKFIMHMHMHMHMHEKHKGK",
+"AHAAAAABADEBJCEBGBACACHEBKNIBCJBJBHBJBJBHHOJJJPIIHPGIHIHIHPJDKGK",
+"AHAAOCJDNCEBOBACOBOBOBHEEJNIKCLBKBBCKBCCKGHJJJMHDGIHPGDGIHEKEJCK",
+"AHAAKEABMBEBDBDBOBOBOBHENIAIKCDCDCDCDCECKGKIGJCIDGFFLGDGDGPJNJMJ",
+"CJAAOAABBBIADBHADBHAKAHEAIBIDCDCBCCCDCCCPFKIJIPGBGCFDGFFDGIJIIMJ",
+"AHAAAAABMBIABADBBAHAHAHEAIGHKCJBBCKBKBBCKGPHJIPGFFGFFFFFFFIJJILJ",
+"AHAAAAABMDFDFDBDFDNDBDHEFHJGDEDEDEDEOEDEOFPHMGBJBJAJBJBJBJMIOHDJ",
+"EGAAAAJDHGNFFHMGFHJGMGEHJGNFNFMGMGMGMGMGLGJGCHEHFHPHMGOHPHFHIGDJ",
+"LHAAOCIFIFIFLFLFMFMFJFLFIFJFHGIGMFIGNFMFMFLFIFLFHGIGFGIGIGHGJFCJ",
+"EGDDKEKEMEBFBFBFBFMEBFBFFEKEMEMEMEGEBFMEBFBFBFKEMEBFMEBFBFKEKEGI",
+"KJLHGINHGINHNHNHNHGINHNHGINHGINHNHGIGINHGINHNHGINHNHGIGINHNHGICJ"};
+
 //this icon has been taken from trolltech's Qt: qmessagebox.cpp
 static char *qtlogo_xpm[] = {
 /* width height ncolors chars_per_pixel */
@@ -944,8 +1146,7 @@ BitMapQT::BitMapQT(GraphObj *g, QWidget *wi, int vr, int hr):anyOutput()
 	int w, h;
 
 	hres = (double)hr;		vres = (double)vr;
-	image = 0L;
-	hgo = 0L;
+	image = 0L;				hgo = 0L;
 	if(wi) {
 		w = wi->width();		h = wi->height();
 		}
@@ -968,8 +1169,8 @@ BitMapQT::BitMapQT(GraphObj *g, QWidget *wi, int vr, int hr):anyOutput()
 BitMapQT::BitMapQT(int w, int h, double hr, double vr):anyOutput()
 {
 	hres = hr;		vres = vr;
-	image = 0L;
-	hgo = 0L;
+	image = 0L;		hgo = 0L;
+	w = abs(w);		h = abs(h);
 	Box1.Xmin = Box1.Ymin = 0.0;
 	Box1.Xmax = w;					Box1.Ymax = h;
 	DeskRect.right = w;				DeskRect.bottom = h;
@@ -1039,7 +1240,7 @@ BitMapQT::SetFill(FillDEF *fill)
 bool
 BitMapQT::SetTextSpec(TextDEF *set)
 {
-	return com_SetTextSpec(set, &TxtSet, this, qFont, &qPainter);
+	return com_SetTextSpec(set, &TxtSet, this, qFont, &qPainter);
 }
 
 bool
@@ -1072,7 +1273,7 @@ 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();
+	*width = rc.rRight() - rc.rLeft();		*height = TxtSet.iSize +2;
 	return true;
 }
 
@@ -1370,7 +1571,9 @@ OutputQT::UpdateRect(RECT *rc, bool invert)
 		}
 	else {
 		y1 = rc->bottom;	y2 = rc->top;
-		}
+		}
+	if(x2 > DeskRect.right) x2 = DeskRect.right;
+	if(y2 > DeskRect.bottom) y2 = DeskRect.right;
 	bitBlt(widget, x1, y1, mempic, x1, y1,
 		x2 - x1, y2 - y1, invert ? Qt::NotCopyROP : Qt::CopyROP);
 	return true;
@@ -1437,11 +1640,21 @@ OutputQT::SetMenu(int type)
 		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 *insert = new QPopupMenu(widget);
+		insert->insertItem("&Rows", widget, SLOT(cmInsRow()));
+		insert->insertItem("&Columns", widget, SLOT(cmInsCol()));
 
+		QPopupMenu *Delete = new QPopupMenu(widget);
+		Delete->insertItem("&Rows", widget, SLOT(cmDelRow()));
+		Delete->insertItem("&Columns", widget, SLOT(cmDelCol()));
+
 		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->insertItem("&Insert", insert);
+		edit->insertItem("&Delete", Delete);
 		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);
@@ -1696,11 +1909,12 @@ RLPwidget::RLPwidget(QWidget *par, const char *name, anyOutput *o, GraphObj *g)
 
 RLPwidget::~RLPwidget()
 {
+	if(OutputClass)((OutputQT*)OutputClass)->widget = 0L;
+	OutputClass = 0L;
 	if(BaseObj){
 		BaseObj->Command(CMD_CAN_DELETE, 0L, 0L);
 		BaseObj = 0L;
 		}
-	if(OutputClass)((OutputQT*)OutputClass)->widget = 0L;
 }
 
 //public slots: menu items, events
@@ -1738,7 +1952,13 @@ RLPwidget::cmSaveDataAs()
 void
 RLPwidget::cmExit()
 {
-	delete(this);
+	if(BaseObj) {
+		if(BaseObj->Command(CMD_CAN_CLOSE, 0L, 0L)){
+			BaseObj = 0L;
+			delete this;
+			}
+		}
+	else delete(this);
 }
 
 void
@@ -1793,8 +2013,8 @@ void
 RLPwidget::cmCopy()
 {
 	if(BaseObj && BaseObj->Id == GO_SPREADDATA) {
-		BaseObj->Command(CMD_QUERY_COPY, 0L, OutputClass);
-		CopyData(BaseObj);
+		if(BaseObj->Command(CMD_QUERY_COPY, 0L, OutputClass))
+			CopyData(BaseObj);
 		}
 }
 
@@ -1954,7 +2174,31 @@ void
 RLPwidget::cmFillRange()
 {
 	BaseObj->Command(CMD_FILLRANGE, 0L, OutputClass);
+}
+
+void
+RLPwidget::cmInsRow()
+{
+	BaseObj->Command(CMD_INSROW, 0L, OutputClass);
 }
+
+void
+RLPwidget::cmInsCol()
+{
+	BaseObj->Command(CMD_INSCOL, 0L, OutputClass);
+}
+
+void
+RLPwidget::cmDelRow()
+{
+	BaseObj->Command(CMD_DELROW, 0L, OutputClass);
+}
+
+void
+RLPwidget::cmDelCol()
+{
+	BaseObj->Command(CMD_DELCOL, 0L, OutputClass);
+}
 
 void ToolMenu(GraphObj *b, anyOutput *o, int tm)
 {
@@ -2040,10 +2284,14 @@ void
 RLPwidget::closeEvent(QCloseEvent *e)
 {
 	if(BaseObj){
-		BaseObj->Command(CMD_CAN_DELETE, 0L, 0L);
-		BaseObj = 0L;
+		if(BaseObj->Command(CMD_CAN_CLOSE, 0L, 0L)) {
+			if(OutputClass)((OutputQT*)OutputClass)->widget = 0L;
+			OutputClass = 0L;
+			BaseObj = 0L;
+			e->accept();
+			}
 		}
-	e->accept();
+	else e->accept();
 }
 
 void
@@ -2056,42 +2304,58 @@ RLPwidget::mouseDoubleClickEvent(QMouseEvent *e)
 
 void
 RLPwidget::mousePressEvent(QMouseEvent *e)
-{
+{
+	int i;
 	MouseEvent mev = {1, e->button() == Qt::LeftButton ? MOUSE_LBDOWN : -1, e->x(), e->y()};
 
 	HideTextCursor();
+	i = e->state();
+	if(i & Qt::ShiftButton) mev.StateFlags |= 0x08;
+	if(i & Qt::ControlButton) mev.StateFlags |= 0x10;
 	if (BaseObj)BaseObj->Command(CMD_MOUSE_EVENT, (void *)&mev, OutputClass);
 }
 
 void
 RLPwidget::mouseReleaseEvent(QMouseEvent *e)
-{
+{
+	int i;
 	MouseEvent mev = {0, e->button() == Qt::LeftButton ? MOUSE_LBUP :
 		e->button() == Qt::RightButton ? MOUSE_RBUP : -1, e->x(), e->y()};
-
+
+	i = e->state();
+	if(i & Qt::ShiftButton) mev.StateFlags |= 0x08;
+	if(i & Qt::ControlButton) mev.StateFlags |= 0x10;
 	if (BaseObj) BaseObj->Command(CMD_MOUSE_EVENT, (void *)&mev, OutputClass);
 }
 
 void
 RLPwidget::mouseMoveEvent(QMouseEvent *e)
-{
+{
+	int i;
 	MouseEvent mev = {0, MOUSE_MOVE, e->x(), e->y()};
 
-	if(e->state() == Qt::LeftButton) mev.StateFlags = 1;
+	i = e->state();
+	if(i & Qt::LeftButton) mev.StateFlags |= 0x01;
+	if(i & Qt::ShiftButton) mev.StateFlags |= 0x08;
+	if(i & Qt::ControlButton) mev.StateFlags |= 0x10;
 	if (BaseObj) BaseObj->Command(CMD_MOUSE_EVENT, (void *)&mev, OutputClass);
 }
 
 void
 RLPwidget::keyPressEvent(QKeyEvent *e)
 {
-	int c;
+	int i, c;
 
 	if(BaseObj) switch(c = e->key()) {
 		case Key_Prior:
-			BaseObj->Command(CMD_PAGEUP, 0L, OutputClass);
+			i = e->state();
+			if(i & Qt::ShiftButton) BaseObj->Command(CMD_SHPGUP, 0L, OutputClass);
+			else BaseObj->Command(CMD_PAGEUP, 0L, OutputClass);
 			break;
 		case Key_Next:
-			BaseObj->Command(CMD_PAGEDOWN, 0L, OutputClass);
+			i = e->state();
+			if(i & Qt::ShiftButton) BaseObj->Command(CMD_SHPGDOWN, 0L, OutputClass);
+			else BaseObj->Command(CMD_PAGEDOWN, 0L, OutputClass);
 			break;
 		case Key_Left:
 			if(e->state() & ShiftButton) BaseObj->Command(CMD_SHIFTLEFT, 0L, OutputClass);
@@ -2556,6 +2820,8 @@ DlgWidget::keyPressEvent(QKeyEvent *e)
 			c = e->ascii();
 			if(c >1 && c < 256)
 				if(c == 26) dlg->Command(CMD_UNDO, 0L, OutputClass);
+				else if(c == 0x03) dlg->Command(CMD_COPY, 0L, OutputClass);
+				else if(c == 0x16) dlg->Command(CMD_PASTE, 0L, OutputClass);
 				else dlg->Command(CMD_ADDCHAR, (void *)(& c), OutputClass);
 			break;
 		}
@@ -2610,7 +2876,7 @@ void *CreateDlgWnd(char *title, int x, int y, int width, int height, tag_DlgObj
 	else w->move(CurrWidgetPos.x+x, CurrWidgetPos.y+y);
 	o = new OutputQT(w);
 	o->units = defs.cUnits;
-	o->Erase(0x00c0c0c0L);
+	o->Erase(0x00e0e0e0L);
 	if(flags & 0x04) {
 		w->startTimer(100);
 		}
@@ -2626,6 +2892,7 @@ void LoopDlgWnd() 	//keep message processing running
 
 void CloseDlgWnd(void *hDlg)
 {
+	HideCopyMark();
 	if(hDlg) {
 		delete((DlgWidget*) hDlg);
 		}
diff --git a/QT_Spec.h b/QT_Spec.h
index 582f9b3..0f4b425 100755
--- a/QT_Spec.h
+++ b/QT_Spec.h
@@ -128,6 +128,10 @@ public slots:
 	void cmLayers();
 	void cmUndo();
 	void cmFillRange();
+	void cmInsRow();
+	void cmInsCol();
+	void cmDelRow();
+	void cmDelCol();
 	void cmtStandard();
 	void cmtDraw();
 	void cmtPolyline();
@@ -232,6 +236,7 @@ public:
 	OutputQT(DlgWidget *wi);
 	~OutputQT();
 	bool ActualSize(RECT *rc);
+	void Focus(){if(widget){widget->show(); widget->setActiveWindow();widget->raise();}};
 	void Caption(char *txt);
 	void MouseCursor(int cid, bool force);
 	bool SetScroll(bool isVert, int iMin, int iMax, int iPSize, int iPos);
diff --git a/RLPLOT.ICO b/RLPLOT.ICO
old mode 100755
new mode 100644
index 0ba1c92..593b642
Binary files a/RLPLOT.ICO and b/RLPLOT.ICO differ
diff --git a/RLPLOT.RC b/RLPLOT.RC
index f7ea45f..2be39ca 100755
--- a/RLPLOT.RC
+++ b/RLPLOT.RC
@@ -36,37 +36,39 @@
 #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_DEFAULTS    520
+#define CM_COPY        521
+#define CM_PASTE       522
+#define CM_UPDATE      523
+#define CM_ADDAXIS     524
+#define CM_UNDO        525
+#define CM_ZOOMIN      526
+#define CM_ZOOMOUT     527
+#define CM_ZOOMFIT     528
+#define CM_FILE1       529
+#define CM_FILE2       530
+#define CM_FILE3       531
+#define CM_FILE4       532
+#define CM_FILE5       533
+#define CM_FILE6       534
+#define CM_FILLRANGE   535
+#define CM_CUT         536
+#define CM_LEGEND      537
+#define CM_LAYERS      538
+#define CM_INSROW      539
+#define CM_INSCOL      540
+#define CM_DELROW      541
+#define CM_DELCOL      542
 
-#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_T_STANDARD  560
+#define CM_T_DRAW      561
+#define CM_T_POLYLINE  562
+#define CM_T_POLYGON   563
+#define CM_T_RECTANGLE 564
+#define CM_T_ROUNDREC  565
+#define CM_T_ELLIPSE   566
+#define CM_T_ARROW     567
+#define CM_T_TEXT      568
 
 #define CM_DELKEY      600
 #define CM_LEFTARRKEY  601
@@ -83,6 +85,8 @@
 #define CM_SHRIGHT     612
 #define CM_SHUP        613
 #define CM_SHDOWN      614
+#define CM_SHPGUP      615
+#define CM_SHPGDOWN    616
 
 #define MENU_1  400
 #define MENU_2  401
@@ -174,9 +178,6 @@ BEGIN
         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"
@@ -184,6 +185,16 @@ BEGIN
         MENUITEM "&Undo"                        CM_UNDO
         MENUITEM SEPARATOR
         MENUITEM "&Rows/Cols",                  CM_ADDROWCOL
+        POPUP "&Insert"
+        BEGIN
+        	MENUITEM "&Rows",					CM_INSROW
+        	MENUITEM "&Columns",				CM_INSCOL
+        END
+        POPUP "&Delete"
+        BEGIN
+        	MENUITEM "&Rows",					CM_DELROW
+        	MENUITEM "&Columns",				CM_DELCOL
+        END
         MENUITEM SEPARATOR
         MENUITEM "&Copy",                       CM_COPY
         MENUITEM "C&ut",						CM_CUT
@@ -322,6 +333,8 @@ BEGIN
     VK_TAB,         CM_SHTAB,               VIRTKEY, SHIFT
     VK_PRIOR,       CM_PGUP,                VIRTKEY
     VK_NEXT,        CM_PGDOWN,              VIRTKEY
+    VK_PRIOR,       CM_SHPGUP,              VIRTKEY, SHIFT
+    VK_NEXT,        CM_SHPGDOWN,            VIRTKEY, SHIFT
     VK_HOME,        CM_POS_FIRST,           VIRTKEY
     VK_END,         CM_POS_LAST,            VIRTKEY
     VK_ESCAPE,		CM_T_STANDARD,			VIRTKEY
diff --git a/RLPlot-Icons.zip b/RLPlot-Icons.zip
new file mode 100755
index 0000000..91ec8e2
Binary files /dev/null and b/RLPlot-Icons.zip differ
diff --git a/RLPlot.bmp b/RLPlot.bmp
old mode 100755
new mode 100644
index ac10363..d204d1b
Binary files a/RLPlot.bmp and b/RLPlot.bmp differ
diff --git a/RLPlot.xpm b/RLPlot.xpm
old mode 100755
new mode 100644
index 77b8e92..1ee9a00
--- a/RLPlot.xpm
+++ b/RLPlot.xpm
@@ -1,47 +1,207 @@
-/* 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"};
+/* XPM */
+static char *rlplot-icon[] = {
+/* width height ncolors chars_per_pixel */
+"32 32 169 2",
+/* colors */
+"AA c #FFFFFFFFFFFF",
+"BA c #FFFFF7F79494",
+"CA c #FFFFF7F78484",
+"DA c #FFFFF7F77373",
+"EA c #FFFFF7F75252",
+"FA c #FFFFF7F74242",
+"GA c #FFFFF7F73939",
+"HA c #FFFFEFEF8C8C",
+"IA c #FFFFEFEF4A4A",
+"JA c #FFFFEFEF2929",
+"KA c #F7F7E7E77B7B",
+"LA c #F7F7C6C6ADAD",
+"MA c #F7F7B5B59C9C",
+"NA c #F7F7ADAD9494",
+"OA c #EFEFF7F7F7F7",
+"PA c #EFEFF7F7EFEF",
+"AB c #EFEFEFEFEFEF",
+"BB c #EFEFEFEFDEDE",
+"CB c #EFEFE7E7A5A5",
+"DB c #EFEFDEDE7373",
+"EB c #EFEFDEDE3939",
+"FB c #EFEFDEDE2929",
+"GB c #EFEFD6D64242",
+"HB c #EFEFA5A58C8C",
+"IB c #EFEF94947B7B",
+"JB c #EFEF84847373",
+"KB c #EFEF84846363",
+"LB c #EFEF7B7B7373",
+"MB c #E7E7E7E7CECE",
+"NB c #E7E7E7E79494",
+"OB c #E7E7DEDE6B6B",
+"PB c #E7E7DEDE5252",
+"AC c #E7E7CECE5252",
+"BC c #E7E77B7B6363",
+"CC c #E7E773735A5A",
+"DC c #E7E76B6B5252",
+"EC c #E7E75A5A4A4A",
+"FC c #DEDEE7E7F7F7",
+"GC c #DEDEE7E7EFEF",
+"HC c #DEDEDEDEDEDE",
+"IC c #DEDEDEDEBDBD",
+"JC c #DEDECECE4A4A",
+"KC c #DEDE4A4A3939",
+"LC c #D6D6EFEFCECE",
+"MC c #D6D6DEDEEFEF",
+"NC c #D6D6DEDECECE",
+"OC c #CECEDEDEDEDE",
+"PC c #CECED6D6CECE",
+"AD c #CECECECEB5B5",
+"BD c #CECECECE5A5A",
+"CD c #C6C6E7E7C6C6",
+"DD c #C6C6DEDEEFEF",
+"ED c #C6C6D6D67373",
+"FD c #C6C6CECE4A4A",
+"GD c #BDBDDEDEB5B5",
+"HD c #BDBDCECEE7E7",
+"ID c #BDBDCECECECE",
+"JD c #BDBDC6C6D6D6",
+"KD c #BDBD39394242",
+"LD c #B5B5DEDEADAD",
+"MD c #B5B5BDBDCECE",
+"ND c #B5B5BDBD4242",
+"OD c #B5B5B5B59C9C",
+"PD c #ADADD6D6ADAD",
+"AE c #ADADCECEDEDE",
+"BE c #ADADBDBDB5B5",
+"CE c #ADADB5B54242",
+"DE c #ADAD4A4A5252",
+"EE c #A5A5BDBDDEDE",
+"FE c #A5A5BDBDCECE",
+"GE c #A5A5ADADB5B5",
+"HE c #A5A5ADAD4242",
+"IE c #A5A563637B7B",
+"JE c #9C9CD6D6A5A5",
+"KE c #9C9CBDBDD6D6",
+"LE c #9C9CBDBDBDBD",
+"ME c #9C9CADADCECE",
+"NE c #9C9CADADA5A5",
+"OE c #9C9C52525252",
+"PE c #9C9C4A4A5252",
+"AF c #9494CECE9C9C",
+"BF c #9494B5B5CECE",
+"CF c #9494ADAD5A5A",
+"DF c #9494ADAD3939",
+"EF c #8C8CCECE9494",
+"FF c #8C8CCECE8C8C",
+"GF c #8C8CB5B5B5B5",
+"HF c #8C8CADADD6D6",
+"IF c #8C8CADADCECE",
+"JF c #8C8CADADB5B5",
+"KF c #8C8CADADA5A5",
+"LF c #8C8C9C9CB5B5",
+"MF c #8C8C9C9CADAD",
+"NF c #8C8C8C8CA5A5",
+"OF c #8C8C52526363",
+"PF c #8C8C42425252",
+"AG c #8484B5B5CECE",
+"BG c #7B7BCECE9494",
+"CG c #7B7BC6C68484",
+"DG c #7B7BC6C67B7B",
+"EG c #7B7BADADC6C6",
+"FG c #7B7BADADADAD",
+"GG c #7B7B9C9CC6C6",
+"HG c #7B7B9C9CB5B5",
+"IG c #7B7B9C9CADAD",
+"JG c #7B7B84849C9C",
+"KG c #7B7B42425252",
+"LG c #737384848C8C",
+"MG c #737373738484",
+"NG c #737339395252",
+"OG c #6B6BC6C68484",
+"PG c #6B6BBDBD7B7B",
+"AH c #6B6BA5A5CECE",
+"BH c #6B6B9C9CBDBD",
+"CH c #6B6B9C9CA5A5",
+"DH c #6B6B94949C9C",
+"EH c #6B6B8C8CA5A5",
+"FH c #6B6B8C8C9C9C",
+"GH c #6B6B6B6B7B7B",
+"HH c #6B6B42425252",
+"IH c #6363BDBD6B6B",
+"JH c #6363B5B58C8C",
+"KH c #6363A5A58C8C",
+"LH c #63639C9CC6C6",
+"MH c #5A5AB5B56363",
+"NH c #5A5A9C9CBDBD",
+"OH c #5A5A8C8C9C9C",
+"PH c #5A5A7B7B8C8C",
+"AI c #5A5A6B6B8484",
+"BI c #5A5A63637B7B",
+"CI c #5252BDBD7373",
+"DI c #5252ADAD8484",
+"EI c #5252A5A57B7B",
+"FI c #4A4AB5B55A5A",
+"GI c #4A4A8C8CBDBD",
+"HI c #4A4A8C8CADAD",
+"II c #4A4A7B7B9C9C",
+"JI c #4A4A7B7B8484",
+"KI c #4A4A6B6B7B7B",
+"LI c #42429C9C7373",
+"MI c #42428C8C7B7B",
+"NI c #42425A5A7373",
+"OI c #42424A4A6B6B",
+"PI c #3939ADAD6B6B",
+"AJ c #3939ADAD5A5A",
+"BJ c #39399C9C6B6B",
+"CJ c #39398484BDBD",
+"DJ c #39397B7BADAD",
+"EJ c #39397B7B8C8C",
+"FJ c #393973737B7B",
+"GJ c #313163637B7B",
+"HJ c #31315A5A7B7B",
+"IJ c #292984846B6B",
+"JJ c #29296B6B7B7B",
+"KJ c #21217B7BB5B5",
+"LJ c #21217373A5A5",
+"MJ c #212173739C9C",
+"NJ c #21216B6B8C8C",
+"OJ c #212152527373",
+"PJ c #181884846363",
+"AK c #18186B6B7B7B",
+"BK c #18185A5A7373",
+"CK c #10106B6B9C9C",
+"DK c #10105A5A8484",
+"EK c #08087B7B6363",
+"FK c #08085A5A7B7B",
+"GK c #00006B6B9C9C",
+"HK c #000063637B7B",
+"IK c #00005A5A8C8C",
+/* pixels */
+"CJAHAHAHAHHILHLHBHLHLJNHNHNHGINHGINHGINHGINHGINHGINHGINHGINHGIKJ",
+"AHAAAAAAAGHDABBBGCBFBFHCPCOCEEHDJDEEJDEEHDJDEEHDJDEEJDEEHDEEJDNH",
+"AHAAAGAAABABABKEOCHCHCPCFEFEABAEHDFCDDJDDDMCJDHDFCHDHDGCDDJDDDLH",
+"EGAEDDABOAABOCEGHCHCPCHCBHJDJDEEJDEEJDEEJDAEJDEEAEJDEEJDEEJDEELH",
+"AHAAAAAAMCJDGCABHCHCBFOCPCJDEEJDEEJDEEAEEEEEAEJDEEFEFEFEEEEEJDGI",
+"AHAAAAPAAGGCABBBMCEGJDNCPCPCEEEEEEEEEEEEEEJDEEEEFEDIPIGFFEFEEENH",
+"EGOAKEAAABPAABKEHCHCHCHCMEBFABJDAEMCOCEEMCMCFEOCDDLEKHIDIDEEDDLH",
+"LHAEMCAAABABAEIFHCHCHCPCBHOCKEKEEEFEEEFEEEFEEEKEBFGEDIKFMEFEEEGI",
+"AHAAAAABGCJDABGCBBOCHGHCPCMDEEFEKEEEFEKEFEKEEEMEBFJFDIGEIFBFFEGI",
+"AHAAAAPAAHGCABBBMCJFMDOCPCJDMEKEKEMEKEKEKEKEMEIFGEFGEIIGJFGEMEGI",
+"AHAAAEAAABABGCNBDBOCNCNCMDEEMCEEEEMCJDKEDDDDLFCIDIEIPICIDIDIJDNH",
+"AHKEDDAAPAABOCKFPBICNCPCHGFEMEMEMEMEHFMEMEIFJFJELCCDCGBBCDLIIGHI",
+"EGAAAAABABJDMBNCPBADGENCMDMDMEHFIFHFMEHFIFMECHJEPCLDBGGDGDLIIGDJ",
+"AHAAAAAAAHEDKAOBEBBDCEODICMDIFIFMEIFIFIFIFIFCHEFLDPDOGGDPDMIFHHI",
+"AHAAOCABMBEABABAFABABAGBNELEHDKEHFDDEEIFJDEEFGCGLDPDPGPDPDAJMFHI",
+"AHDDEGABBBEABACAFABACBGBFHGEIFGGIFGGIFIFGGIFDHCGJEMHFIAFAFBJDHII",
+"AHAAAAABMBEAHAEAEADACABDODBEHGIFGGIFHGGGIFHGCHJHAFAFEFAFFFFJDHMJ",
+"AHAAAAAABEIADADAKACADAGBODNEGGHGIEIENFGGHGGGOHDGFFFFFFFFFFAJOHHI",
+"AHAAABABPCGAKADADAKADAFDODMFHIHIAIPFIIIIHIHIEJCIFFDGFFDGDGJJFJCK",
+"AHAAAHHCMBGAEAEAEAFAFAJCLGFKPEPEPEKDPEPEOIIKHKLIDGDGPGDGIHEKIKGK",
+"EGFCDDAANCJAIAIAIAIAIANDBKOJJBLALALBLAMANGFKHKFIMHPGMHFILIEKFKGK",
+"LHAAAABBNCFBJAJAJAEBGBDFBKOJBCNADCDCIBHBNGBKAKFIMHMHMHMHMHEKHKGK",
+"AHAAAAABADEBJCEBGBACACHEBKNIBCJBJBHBJBJBHHOJJJPIIHPGIHIHIHPJDKGK",
+"AHAAOCJDNCEBOBACOBOBOBHEEJNIKCLBKBBCKBCCKGHJJJMHDGIHPGDGIHEKEJCK",
+"AHAAKEABMBEBDBDBOBOBOBHENIAIKCDCDCDCDCECKGKIGJCIDGFFLGDGDGPJNJMJ",
+"CJAAOAABBBIADBHADBHAKAHEAIBIDCDCBCCCDCCCPFKIJIPGBGCFDGFFDGIJIIMJ",
+"AHAAAAABMBIABADBBAHAHAHEAIGHKCJBBCKBKBBCKGPHJIPGFFGFFFFFFFIJJILJ",
+"AHAAAAABMDFDFDBDFDNDBDHEFHJGDEDEDEDEOEDEOFPHMGBJBJAJBJBJBJMIOHDJ",
+"EGAAAAJDHGNFFHMGFHJGMGEHJGNFNFMGMGMGMGMGLGJGCHEHFHPHMGOHPHFHIGDJ",
+"LHAAOCIFIFIFLFLFMFMFJFLFIFJFHGIGMFIGNFMFMFLFIFLFHGIGFGIGIGHGJFCJ",
+"EGDDKEKEMEBFBFBFBFMEBFBFFEKEMEMEMEGEBFMEBFBFBFKEMEBFMEBFBFKEKEGI",
+"KJLHGINHGINHNHNHNHGINHNHGINHGINHNHGIGINHGINHNHGINHNHGIGINHNHGICJ"};
diff --git a/TheDialog.cpp b/TheDialog.cpp
index 7d94615..d6721ee 100755
--- a/TheDialog.cpp
+++ b/TheDialog.cpp
@@ -39,9 +39,9 @@ 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;
+int dlgtxtheight = 10;
+static unsigned long DlgBGcolor = 0x00e0e0e0L;
+static unsigned long DlgBGhigh = 0x00e8e8e8L;
 TextDEF DlgText = {0x00000000L, 0x00ffffffL, 4.0, 0.0f, 0.0f, 0, 
 	TXA_HLEFT | TXA_VTOP, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, 0L}; 
 
@@ -64,7 +64,7 @@ DlgRoot::DlgRoot(DlgInfo *tmpl)
 	dlg = 0L;			flags = 0L;		tabstops = 0L;
 	DlgText.iSize = dlgtxtheight;		DlgText.ColBg = DlgBGcolor;
 	type = NONE;		Id = -2;		cContinue = 0;
-	bActive = false;	Result = -1;
+	bActive = false;	Result = -1;	c_go = CurrGO;
 	CurrDisp = 0L;		oldFocus = DialogFocus;		DialogFocus = 0L;
 	oldDefault = DialogDefault;			oldTabStop = DialogTabStop;
 	if(tmpl) {
@@ -137,6 +137,9 @@ DlgRoot::DlgRoot(DlgInfo *tmpl)
 				case EDTEXT:
 					dlg[i]->dialog = new InputText(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
 					break;
+				case RANGEINPUT:
+					dlg[i]->dialog = new RangeInput(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
+					break;
 				case EDVAL1:
 					dlg[i]->dialog = new InputValue(this, &tmpl[i],rc,(double*)tmpl[i].ptype);
 					break;
@@ -214,6 +217,7 @@ DlgRoot::~DlgRoot()
 				case LTEXT:	case RTEXT:	
 				case CTEXT:	delete((Text*)dlg[i]->dialog);						break;
 				case EDTEXT:		delete((InputText*)dlg[i]->dialog);			break;
+				case RANGEINPUT:	delete((RangeInput*)dlg[i]->dialog);			break;
 				case EDVAL1:		delete((InputValue*)dlg[i]->dialog);		break;
 				case INCDECVAL1:	delete((IncDecValue*)dlg[i]->dialog);		break;
 				case TXTHSP:		delete((TxtHSP*)dlg[i]->dialog);			break;
@@ -240,8 +244,8 @@ DlgRoot::~DlgRoot()
 		free(dlg);
 		}
 	if(tabstops) free(tabstops);
-	DialogFocus = oldFocus;			 DialogDefault = oldDefault;
-	DialogTabStop = oldTabStop;
+	DialogFocus = oldFocus;			DialogDefault = oldDefault;
+	DialogTabStop = oldTabStop;		CurrGO = c_go;
 }
 
 bool
@@ -255,6 +259,7 @@ DlgRoot::Command(int cmd, void *tmpl, anyOutput *o)
 	case CMD_UNDO:
 		if(CurrDisp) {
 			Undo.Restore(false, CurrDisp);
+			DoPlot(CurrDisp);
 			}
 		return true;
 	case CMD_REDRAW:
@@ -267,7 +272,6 @@ DlgRoot::Command(int cmd, void *tmpl, anyOutput *o)
 		mev = (MouseEvent *) tmpl;
 		switch(mev->Action) {
 		case MOUSE_LBDOWN:
-			DialogFocus = NULL;		CurrGO = 0L;
 			bActive = true;
 		case MOUSE_MOVE:
 			if(!(mev->StateFlags & 1))return false;
@@ -279,7 +283,6 @@ DlgRoot::Command(int cmd, void *tmpl, anyOutput *o)
 			bActive = false;				//skip next event (LB up);
 			return true;
 		case MOUSE_LBUP:
-			CurrGO = 0L;
 			if(bActive)ForEach(CMD_LBUP, 0, o);
 			return true;
 			}
@@ -327,7 +330,7 @@ DlgRoot::Command(int cmd, void *tmpl, anyOutput *o)
 				d->DoPlot(o);
 				DialogDefault->DoPlot(o);
 				break;
-			case EDTEXT:			case EDVAL1:
+			case EDTEXT:			case EDVAL1:	case RANGEINPUT:
 			case INCDECVAL1:
 				DialogTabStop = DialogFocus = tabstops[i];
 				if((InputText*)DialogFocus->bActive)
@@ -351,6 +354,7 @@ DlgRoot::Command(int cmd, void *tmpl, anyOutput *o)
 				DialogDefault->DoPlot(o);
 				break;
 			case EDTEXT:			case EDVAL1:			case INCDECVAL1:
+			case RANGEINPUT:
 				DialogTabStop = DialogFocus = tabstops[i];
 				((InputText*)DialogFocus)->Activate(DialogFocus->Id, true);
 				break;
@@ -363,13 +367,20 @@ DlgRoot::Command(int cmd, void *tmpl, anyOutput *o)
         else return CurUpDown(cmd);
 	case CMD_CURRLEFT:	case CMD_CURRIGHT:	case CMD_DELETE:
 	case CMD_POS_FIRST:	case CMD_POS_LAST:	case CMD_SHIFTLEFT:
-	case CMD_SHIFTRIGHT:
+	case CMD_SHIFTRIGHT:	case CMD_COPY:	case CMD_PASTE:
+		Undo.SetDisp(CurrDisp);
 		bActive = true;
-		if(DialogFocus)return DialogFocus->Command(cmd, tmpl, o);
+		if(DialogFocus)return DialogFocus->Command(cmd, tmpl, CurrDisp);
         else return false;
 	case CMD_ADDCHAR:
 		if(!tmpl) return false;
 		bActive = true;
+		if(*((int*)tmpl) == 27) {						//Esc
+			HideCopyMark();
+			for (i = 0; dlg[i] && i < cDlgs; i++) 
+				if(dlg[i]->dialog) dlg[i]->dialog->Command(cmd, tmpl, o);
+			return Command(CMD_REDRAW, 0L, 0L);
+			}
 		if(DialogDefault && *((int*)tmpl) == 0x0d){		//return pressed
 			HideTextCursor();
 			return DialogDefault->Command(cmd, tmpl, o);
@@ -377,6 +388,7 @@ DlgRoot::Command(int cmd, void *tmpl, anyOutput *o)
 		if(DialogFocus)return DialogFocus->Command(cmd, tmpl, o);
         else return false;
 	case CMD_UNLOCK:
+		CurrDisp = 0L;
 		for(i = 0; i < cDlgs; i++)
 			if(dlg[i] && dlg[i]->dialog) dlg[i]->dialog->Command(CMD_UNLOCK, 0L, 0L);
 		break;
@@ -389,6 +401,7 @@ DlgRoot::DoPlot(anyOutput *o)
 {
 	int i;
 
+	HideCopyMark();
 	if(tabstops) for(i = 0; i < cDlgs; tabstops[i++] = 0);
 	if(o)CurrDisp = o;
 	if(CurrDisp) {
@@ -408,7 +421,7 @@ DlgRoot::CurUpDown(int cmd)
 		for(i = 0; tabstops[i] && i < cDlgs; i++) {
 			if(tabstops[i] != DialogTabStop) {
 				switch(tabstops[i]->type) {
-				case EDVAL1:	case INCDECVAL1:	case EDTEXT:
+				case EDVAL1:	case INCDECVAL1:	case EDTEXT:	case RANGEINPUT:
 					if(rTxtCur.left > tabstops[i]->cr.left && 
 						rTxtCur.right < tabstops[i]->cr.right) {
 						if((dy = (tabstops[i]->cr.top - rTxtCur.top))< 0) {
@@ -764,6 +777,7 @@ PushButton::Command(int cmd, void *tmpl, anyOutput *o)
 {
 	switch(cmd) {
 	case CMD_ADDCHAR:
+		HideCopyMark();
 		if(parent && *((int*)tmpl) == 0x0d)		//return pressed
 			 parent->Command(CMD_ENDDIALOG, (void *)this, o);
 		return true;
@@ -778,8 +792,7 @@ PushButton::DoPlot(anyOutput *o)
 
 	Line.color = 0x00000000L;
 	Fill.color = DlgBGhigh;
-	o->SetLine(&Line);
-	o->SetFill(&Fill);
+	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);
@@ -1702,13 +1715,13 @@ Text::SetColor(int id, DWORD color)
 InputText::InputText(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
 	:Dialog(par, desc, rec)
 {
-	POINT p1, p2;
+	RECT rc;
 
-	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;
+	rc.left = rec.left+1;				rc.top = rec.top+1;
+	rc.right = rec.right-1;				rc.bottom = rec.bottom-1;
+	if(type == INCDECVAL1) cr.right = rc.right = cr.right - 7*xbase;
 	Line.color = 0x00000000L;
-	Text = new EditText(0L, p1, p2, text);
+	if(Text = new EditText(0L, text, -1, -1)) Text->SetRec(&rc);
 	Disp = 0L;
 }
 
@@ -1737,6 +1750,12 @@ InputText::Command(int cmd, void *tmpl, anyOutput *o)
 	case CMD_SETFONT:	case CMD_SETSTYLE:
 		if(Text) return Text->Command(cmd, o, tmpl);
 		return false;
+	case CMD_UPDATE:
+		if(Text && bActive && o) Text->Command(cmd, o, 0L);
+		break;
+	case CMD_COPY:		case CMD_PASTE:
+		if(Text && bActive && !(flags & NOEDIT)) return  Text->Command(cmd, o, NULL);
+		return false;
 	case CMD_ADDCHAR:
 		if(Text && bActive && !(flags & NOEDIT)){
 			if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, o);
@@ -1748,12 +1767,7 @@ InputText::Command(int cmd, void *tmpl, anyOutput *o)
 			}
 		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;
-			}
+		if(Text) return Text->SetText((char*)tmpl);
 		break;
 	case CMD_ENDDIALOG:		//we should come here for IncDecValue class only
 		if(type == INCDECVAL1 && GetValue(Id, &tmpVal)) {
@@ -1782,6 +1796,7 @@ InputText::DoPlot(anyOutput *o)
 {
 	POINT pts[5];
 
+	if(!o) return;
 	Disp = o;
 	pts[0].x = pts[0].y = 0;		//that means no caret with update(5)
 	o->SetTextSpec(&TextDef);
@@ -1808,6 +1823,7 @@ InputText::Select(int x, int y, anyOutput *o)
 		o->SetTextSpec(&TextDef);
 		if(Text) Text->Update(1, o, &p1); 
 		if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, o);
+		return true;
 		}
 	return false;
 }
@@ -1880,6 +1896,49 @@ InputText::Activate(int id, bool activate)
 		}
 }
 
+RangeInput::RangeInput(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
+	:InputText(par, desc, rec, text)
+{
+	data = 0L;
+}
+
+RangeInput::~RangeInput()
+{
+	if(data) data->Command(CMD_ETRACC, 0L, 0L);
+	if(Text) delete (Text);		Text = 0L;
+}
+
+bool
+RangeInput::Command(int cmd, void *tmpl, anyOutput *o)
+{
+	switch(cmd) {
+	case CMD_SET_DATAOBJ:
+		data = (DataObj*)tmpl;
+		return true;
+		}
+	return InputText::Command(cmd, tmpl, o);
+}
+
+bool
+RangeInput::Select(int x, int y, anyOutput *o)
+{
+	bool bRes;
+
+	bRes = InputText::Select(x, y, o);
+	if(bRes && data) {
+		if(DialogFocus == this) data->Command(CMD_ETRACC, Text, 0L);
+		else data->Command(CMD_ETRACC, 0L, 0L);
+		}
+	return bRes;
+}
+
+void
+RangeInput::Activate(int id, bool activate)
+{
+	InputText::Activate(id, activate);
+	if(activate && data) data->Command(CMD_ETRACC, Text, 0L);
+}
+
 InputValue::InputValue(tag_DlgObj *par, DlgInfo * desc, RECT rec, double *value)
 	:InputText(par, desc, rec, 0L)
 {
@@ -2455,7 +2514,7 @@ Group::Command(int cmd, void *tmpl, anyOutput *o)
 	case CMD_ADDCHILD:
 		if(Children && numChildren && (d = (Dialog*)tmpl)) {
 			d->parent = this;
-			if(!TextFocus &&(d->type == EDTEXT ||
+			if(!TextFocus &&(d->type == EDTEXT || d->type == RANGEINPUT ||
 				d->type == INCDECVAL1 || d->type == EDVAL1))
 				TextFocus = ((InputText*)tmpl)->bActive ? (InputText*)tmpl: 0L ;
 			for(i = 0; i < numChildren; i++) {
@@ -2601,7 +2660,7 @@ TabSheet::DoPlot(anyOutput *o)
 	pts[2].x = rctab.left + (rctab.bottom - rctab.top)/2;
 	pts[2].y = pts[3].y = rctab.top;
 	pts[3].x = pts[4].x = rctab.right-1;
-
+	HideCopyMark();
 	Line.color = 0x00000000L;
 	Line.width = 0.0;
 	Fill.color = bChecked ? DlgBGhigh : DlgBGcolor;
@@ -2616,7 +2675,11 @@ TabSheet::DoPlot(anyOutput *o)
 		o->oSolidLine(pts+4);
 		}
 	o->SetTextSpec(&TextDef);
-	o->oTextOut(rctab.right - 6, rctab.top + 1, Text, 0);
+#ifdef _WINDOWS
+	o->oTextOut(rctab.right - 6, rctab.top + 3, Text, 0);
+#else
+	o->oTextOut(rctab.right - 6, rctab.top + 5, Text, 0);
+#endif
 	Group::DoPlot(o);
 	o->UpdateRect(&cr, false);
 }
@@ -2730,7 +2793,7 @@ Listbox::Command(int cmd, void *tmpl, anyOutput *o)
 	double ps;
 	char *txt;
 
-	ps = ((double)(cr.bottom - cr.top))/((double)(bmh+TextDef.iSize+1));
+	ps = ((double)(cr.bottom - cr.top))/((double)(bmh+TextDef.iSize+_SBINC));
 	switch(cmd) {
 	case CMD_REDRAW:
 		DoPlot(o);
@@ -2777,12 +2840,12 @@ Listbox::DoPlot(anyOutput *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;
+	mrk.left = hcr.left+2;		mrk.top = (cl)*(TextDef.iSize+_SBINC) - startY + hcr.top;
+	mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
 	if(mrk.bottom > (hcr.top+1) && mrk.top < (hcr.bottom-1)){
 		if(mrk.top < (hcr.top+1)) mrk.top = hcr.top+1;
 		if(mrk.bottom > (hcr.bottom-1)) mrk.bottom = hcr.bottom-1;
-		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, cl*TextDef.iSize, 
+		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, cl*(TextDef.iSize+_SBINC), 
 			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true);
 		}
 	o->UpdateRect(&cr, false);
@@ -2795,17 +2858,17 @@ Listbox::Select(int x, int y, anyOutput *o)
 	RECT mrk;
 
 	if(IsInRect(&hcr, x, y)) {
-		il = (y+startY-hcr.top)/TextDef.iSize;
+		il = (y+startY-hcr.top)/(TextDef.iSize+_SBINC);
 		if(il >= ns || il < 0){
 			o->UpdateRect(&hcr, false);
 			return false;
 			}
 		cl = il;
-		mrk.left = hcr.left+2;		mrk.top = (il)*TextDef.iSize - startY + hcr.top;
-		mrk.right = hcr.right-2;	mrk.bottom = mrk.top + TextDef.iSize;
+		mrk.left = hcr.left+2;		mrk.top = (il)*(TextDef.iSize+_SBINC) - startY + hcr.top;
+		mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
 		o->CopyBitmap(hcr.left+1, hcr.top+1, bmp, 0, startY, (hcr.right-hcr.left)-2, 
 			(hcr.bottom-hcr.top)-2, false); 
-		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, (cl)*TextDef.iSize, 
+		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, (cl)*(TextDef.iSize+_SBINC), 
 			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true); 
 		o->UpdateRect(&hcr, false);
 		if((flags & TOUCHEXIT) && parent) 
@@ -2847,13 +2910,13 @@ Listbox::CreateBitMap(anyOutput *tmpl)
 	if(bmp) DelBitmapClass(bmp);
 	bmp = 0L;
 	if(tmpl && strings) {
-		h = TextDef.iSize * ns;		w = (hcr.right - hcr.left);
+		h = (TextDef.iSize+_SBINC) * ns;		w = (hcr.right - hcr.left);
 		if(bmp = NewBitmapClass(w, h, tmpl->hres, tmpl->vres)){
 			bmp->Erase(0x00ffffffL);
 			bmp->SetTextSpec(&TextDef);
-			bmh = h - TextDef.iSize;
+			bmh = h - (TextDef.iSize+_SBINC);
 			for(i = 0; i < ns; i++) {
-				bmp->oTextOut(5, i*TextDef.iSize, strings[i], 0);
+				bmp->oTextOut(5, i*(TextDef.iSize+_SBINC), strings[i], 0);
 				}
 			return true;
 			}
@@ -2891,7 +2954,7 @@ Treeview::Command(int cmd, void *tmpl, anyOutput *o)
 {
 	double ps;
 
-	ps = ((double)(cr.bottom - cr.top))/((double)(bmh+TextDef.iSize+1));
+	ps = ((double)(cr.bottom - cr.top))/((double)(bmh+TextDef.iSize+_SBINC));
 	switch(cmd) {
 	case CMD_LAYERS:
 		ot->Command(cmd, tmpl, o);
@@ -2942,12 +3005,12 @@ Treeview::DoPlot(anyOutput *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;
+	mrk.left = hcr.left+2;		mrk.top = (cl)*(TextDef.iSize+_SBINC) - startY + hcr.top;
+	mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
 	if(mrk.bottom > (hcr.top+1) && mrk.top < (hcr.bottom-1)){
 		if(mrk.top < (hcr.top+1)) mrk.top = hcr.top+1;
 		if(mrk.bottom > (hcr.bottom-1)) mrk.bottom = hcr.bottom-1;
-		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, cl*TextDef.iSize, 
+		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, cl*(TextDef.iSize+_SBINC), 
 			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true);
 		}
 	o->UpdateRect(&cr, false);
@@ -2960,17 +3023,17 @@ Treeview::Select(int x, int y, anyOutput *o)
 	RECT mrk;
 
 	if(IsInRect(&hcr, x, y)) {
-		il = (y+startY-hcr.top)/TextDef.iSize;
+		il = (y+startY-hcr.top)/(TextDef.iSize+_SBINC);
 		if(il >= ns || il < 0){
 			o->UpdateRect(&hcr, false);
 			return false;
 			}
 		cl = il;
-		mrk.left = hcr.left+2;		mrk.top = (il)*TextDef.iSize - startY + hcr.top;
-		mrk.right = hcr.right-2;	mrk.bottom = mrk.top + TextDef.iSize;
+		mrk.left = hcr.left+2;		mrk.top = (il)*(TextDef.iSize+_SBINC) - startY + hcr.top;
+		mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
 		o->CopyBitmap(hcr.left+1, hcr.top+1, bmp, 0, startY, (hcr.right-hcr.left)-2, 
 			(hcr.bottom-hcr.top)-2, false); 
-		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, (cl)*TextDef.iSize, 
+		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, (cl)*(TextDef.iSize+_SBINC), 
 			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true); 
 		o->UpdateRect(&hcr, false);
 		if((flags & TOUCHEXIT) && parent) 
@@ -3404,7 +3467,8 @@ bool ShowLayers(GraphObj *root)
 				}
 			if((cgo = ot->get_obj(cl)) && cgo->parent) {
 				if(cgo->parent->Id == GO_STACKBAR || cgo->parent->Id == GO_STACKPG ||
-					cgo->parent->Id == GO_WATERFALL) {
+					cgo->parent->Id == GO_WATERFALL || cgo->parent->Id == GO_PLOT3D ||
+					cgo->parent->Id == GO_FUNC3D) {
 					Dlg->ShowItem(501, true);
 					}
 				else if(cgo->parent->Id == GO_GRAPH || cgo->parent->Id == GO_PAGE){ 
@@ -3503,7 +3567,7 @@ void RLPlotInfo()
 		{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},
+		{7, 8, 0, 0x0L, CTEXT, (void*)"Copyright (C) 2002-2005 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},
@@ -3517,7 +3581,7 @@ void RLPlotInfo()
 		{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},
+		{7, 8, 0, 0x0L, CTEXT, (void*)"Copyright (C) 2002-2005 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},
@@ -3564,7 +3628,7 @@ 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;
+	double fw, cw, ch, th;
 	DlgInfo SSDlg[] = {
 		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 115, 10, 40, 12},
 		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 115, 25, 40, 12},
@@ -3575,33 +3639,35 @@ DoSpShSize(DataObj *dt)
 		{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}};
+		{200, 201, 0, 0x0L, RTEXT, (void*)"row buttons", 10, 29, 40, 8},
+		{201, 202, 0, 0x0L, EDVAL1, &fw, 52, 29, 25, 10},
+		{202, 203, 0, 0x0L, LTEXT, (void*)"[digits]", 79, 29, 20, 8},
+		{203, 204, 0, 0x0L, RTEXT, (void*)"column width", 10, 44, 40, 8},
+		{204, 205, 0, 0x0L, EDVAL1, &cw, 52, 44, 25, 10},
+		{205, 206, 0, 0x0L, LTEXT, (void*)"[digits]", 79, 44, 20, 8},
+		{206, 207, 0, 0x0L, RTEXT, (void*)"row height", 10, 59, 40, 8},
+		{207, 208, 0, 0x0L, EDVAL1, &ch, 52, 59, 25, 10},
+		{208, 209, 0, 0x0L, LTEXT, (void *) Units[defs.cUnits].display, 79, 59, 20, 8},
+		{209, 210, 0, 0x0L, RTEXT, (void*)"text size", 10, 74, 40, 8},
+		{210, 211, 0, 0x0L, INCDECVAL1, &th, 52, 74, 33, 10},
+		{211, 0, 0, LASTOBJ, LTEXT, (void *)"%", 87, 74, 20, 8}};
 	DlgRoot *Dlg;
 	void *hDlg;
-	int w1, w2, h1, h2, res, celldim[3];
+	int w1, w2, h1, h2, res, celldim[3], ith;
 	bool bRet = false;
-	double fw1, cw1, ch1;
+	double fw1, cw1, ch1, th1;
 
 	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));
+	fw1 = fw = NiceValue(((double)celldim[0])/((double)(celldim[2]-2)/2.0));
+	cw1 = cw = NiceValue(((double)celldim[1])/((double)(celldim[2]-2)/2.0));
 	switch(defs.cUnits) {
 	case 1:		ch = NiceValue(((double)(celldim[2]-2))*0.0259183673);	break;
 	case 2:		ch = NiceValue(((double)(celldim[2]-2))/98.0);			break;
 	default:	ch = NiceValue(((double)(celldim[2]-2))*0.259183673);	break;
 		}
-	ch1 = ch;
-	sprintf(txt1, "%d", w1);
-	sprintf(txt2, "%d", h1);
+	ch1 = ch;						th = th1 = defs.ss_txt*100.0;
+	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);
@@ -3619,14 +3685,20 @@ DoSpShSize(DataObj *dt)
 			}
 		}while(res <0);
 	if(res == 1){
-		Dlg->GetValue(207, &ch);
-		if(ch != ch1 && ch > 0.001) {
+		if(Dlg->GetValue(207, &ch) && Dlg->GetValue(210, &th) && ch > 0.001) {
+			defs.ss_txt = th = th >= 10.0 && th <= 100 ? th/100.0 : 1.0; 
 			switch(defs.cUnits) {
-			case 1:		celldim[2] = iround(ch/0.0259183673)+2;			break;
-			case 2:		celldim[2] = iround(ch*98.0)+2;					break;
-			default:	celldim[2] = iround(ch/0.259183673)+2;			break;
+			case 1:		
+				celldim[2] = iround(ch/0.0259183673)+2;		ith = iround(th * ch/0.0259183673);
+				break;
+			case 2:		
+				celldim[2] = iround(ch*98.0)+2;				ith = iround(th * ch*98.0);
+				break;
+			default:	
+				celldim[2] = iround(ch/0.259183673)+2;		ith = iround(th * ch/0.259183673);
+				break;
 				}
-			dt->Command(CMD_TEXTSIZE, (void*)(& celldim[2]), 0L);
+			bRet = dt->Command(CMD_TEXTSIZE, (void*)(& ith), 0L);
 			}
 		Dlg->GetValue(201, &fw);	Dlg->GetValue(204, &cw);
 		if(fw >0.001 && (fw != fw1 || ch != ch1))
@@ -3644,50 +3716,8 @@ DoSpShSize(DataObj *dt)
 	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)
+bool FillSsRange(DataObj *d, char **range, GraphObj *msg_go)
 {
 	TabSHEET tab1 = {0, 37, 10, "fill range "};
 	char *ra = range ? *range:0L;
@@ -3702,10 +3732,10 @@ bool FillSsRange(DataObj *d, char **range)
 		{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},
+		{6, 7, 0, 0x0L, RANGEINPUT, (void*)ra, 10, 25, 140, 10},
 		{7, 8, 0, CHECKED | TOUCHEXIT, RADIO1, (void*)"with values", 10, 37, 45, 8},
 		{8, 9, 0, TOUCHEXIT, RADIO1, (void*)"with formulas", 60, 37, 60, 8},
-		{9, 10, 0, 0x0L, CHECKPIN, 0L, 5, 0, 12, 8},
+		{9, 10, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
 		{10, 20, 11, CHECKED, GROUP, 0L, 0, 0, 0, 0},
 		{11, 12, 0, 0x0L, RTEXT, (void*)"start value=", 10, 60, 35, 8},
 		{12, 13, 0, 0x0L, EDVAL1, &startval, 45, 60, 30, 10},
@@ -3721,9 +3751,12 @@ bool FillSsRange(DataObj *d, char **range)
 	int i, res, row, col, r1, c1;
 	bool bRet = false, bContinue = false;
 	AccRange *rF;
+	RECT rc_dest;
+	anyOutput *cdisp = 	Undo.cdisp;
 
 	if(!d || !range) return false;
 	if(!(Dlg = new DlgRoot(RangeDlg))) return false;
+	Dlg->ItemCmd(6, CMD_SET_DATAOBJ, d);
 #ifdef _WINDOWS
 	for(i = 23; i <= 24; i++) Dlg->TextSize(i, 12);
 #else
@@ -3747,6 +3780,7 @@ bool FillSsRange(DataObj *d, char **range)
 				bContinue = true;				res = -1;
 				}
 			else ra = strdup(TmpTxt);
+			rc_dest.left = rc_dest.right = rc_dest.top = rc_dest.bottom = 0;
 			break;
 		case 7:
 			Dlg->ShowItem(10, true);			Dlg->ShowItem(20, false);
@@ -3762,10 +3796,13 @@ bool FillSsRange(DataObj *d, char **range)
 			break;
 			}
 		}while(res <0);
+	Undo.SetDisp(cdisp);
 	if(res == 1 && ra) {
 		if(Dlg->GetCheck(7)) {
 			Dlg->GetValue(12, &startval);	Dlg->GetValue(14, &stepval);
 			if((rF = new AccRange(ra)) && rF->GetFirst(&col, &row)) {
+				rF->BoundRec(&rc_dest);
+				Undo.DataObject(msg_go, Undo.cdisp, d, &rc_dest, 0L);
 				for( ; rF->GetNext(&col, &row); startval += stepval) {
 					d->SetValue(row, col, startval);
 					}
@@ -3778,8 +3815,11 @@ bool FillSsRange(DataObj *d, char **range)
 				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);
+					if(formula[0] == '=') {
+						MoveFormula(d, formula, TmpTxt, col-c1, row-r1, -1, -1);
+						d->SetText(row, col, TmpTxt);
+						}
+					else d->SetText(row, col, formula);
 					}
 				delete rF;							bRet = true;
 				}
diff --git a/TheDialog.h b/TheDialog.h
index d3ea251..e41ffcf 100755
--- a/TheDialog.h
+++ b/TheDialog.h
@@ -39,7 +39,7 @@ typedef struct {
 //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, 
+	RANGEINPUT, EDVAL1, INCDECVAL1, HSCROLL, VSCROLL, TXTHSP, ICON, GROUP, 
 	GROUPBOX, SHEET, ODBUTTON, LISTBOX1, TREEVIEW, LINEPAT, TEXTBOX, CHECKPIN, TRASH,
 	CONFIG};
 
@@ -136,6 +136,7 @@ private:
 	int cDlgs, Result, cContinue;
 	Dialog *oldFocus, *oldDefault, *oldTabStop;
 	bool bActive;
+	GraphObj *c_go;
 	Dialog **tabstops;
 	DlgTmpl **dlg;
 	MouseEvent *mev;
@@ -320,19 +321,31 @@ public:
 
 	InputText(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
 	~InputText();
-	bool Command(int cmd, void *tmpl, anyOutput *o);
+	virtual bool Command(int cmd, void *tmpl, anyOutput *o);
 	virtual void DoPlot(anyOutput *o);
 	virtual bool Select(int x, int y, anyOutput *o);
 	bool GetValue(int id, double *val);
 	bool GetInt(int id, int *val);
 	bool GetText(int id, char *txt);
 	virtual void MBtrack(MouseEvent *mev, anyOutput *o);
-	void Activate(int id, bool active);
+	virtual void Activate(int id, bool active);
 
 private:
 	anyOutput *Disp;
 };
 
+class RangeInput:public InputText {
+public:
+	RangeInput(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text);
+	~RangeInput();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool Select(int x, int y, anyOutput *o);
+	void Activate(int id, bool active);
+
+private:
+	DataObj *data;
+};
+
 class InputValue:public InputText {
 public:
 	InputValue(tag_DlgObj *par, DlgInfo * desc, RECT rec, double *value);
diff --git a/UtilObj.cpp b/UtilObj.cpp
index c604c25..7950b37 100755
--- a/UtilObj.cpp
+++ b/UtilObj.cpp
@@ -33,16 +33,19 @@
 
 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};
+static LineDEF ETbgnn = {0.0, 1.0, 0x00e8e8e8L, 0L};
+static LineDEF ETbgna = {0.0, 1.0, 0x00ffffffL, 0L};
+static LineDEF ETbgmn = {0.0, 1.0, 0x00cbcbcbL, 0L};
+static LineDEF ETbgma = {0.0, 1.0, 0x00ffffc0L, 0L};
+static LineDEF yLine = {0.0, 1.0, 0x0000ffffL, 0L};
+extern const LineDEF BlackLine = {0.0, 1.0, 0x00000000L, 0L};
+extern const LineDEF GrayLine = {0.0, 1.0, 0x00c0c0c0L, 0L};
+
+static FillDEF ETfbnn = {FILL_NONE, 0x00e8e8e8L, 1.0, NULL, 0x00ffffffL};
+static FillDEF ETfbna = {FILL_NONE, 0x00ffffffL, 1.0, NULL, 0x00ffffffL};
+static FillDEF ETfbmn = {FILL_NONE, 0x00e8cbcbL, 1.0, NULL, 0x00ffffffL};
+static FillDEF ETfbma = {FILL_NONE, 0x00ffffc0L, 1.0, NULL, 0x00ffffffL};
+static FillDEF yFill = {FILL_NONE, 0x0000ffffL, 1.0, NULL, 0x0000ffffL};
 
 extern char TmpTxt[500];
 extern unsigned long cObsW;				//count objects written
@@ -56,33 +59,29 @@ extern UndoObj Undo;
 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)
+EditText::EditText(void *par, char *msg, int r, int c)
 {
 	loc.x = loc.y = crb.x = rb.x = crb.y = rb.y = 0;	Value = 0.0;
-	if(msg && msg[0]) text = strdup(msg);
-	else text = 0L;
+	row = r;	col = c;	disp = 0L;		text = 0L;
 	CursorPos = length = Align = 0;		type = ET_UNKNOWN;		TextCol=0x00000000L;
 	bgLine = &ETbgnn;					bgFill = &ETfbnn;		parent = par;
-	FindType();
+	if(msg && msg[0]) {
+		SetText(msg);		FindType();
+		}
+	else {
+		Align = TXA_VCENTER | TXA_HRIGHT;
+		type = ET_UNKNOWN;
+		}
 	m1 = m2 = -1;						//cursor positions track marks
+	ftext = 0L;							//store formula text result here
 }
 
 EditText::~EditText()
 {
+	HideCopyMark();
 	if(CurrText == this)	CurrText = 0L;
 	if(text) free(text);	text = 0L;
+	if(ftext) free(ftext);	ftext = 0L;
 }
 
 bool
@@ -97,30 +96,42 @@ EditText::AddChar(int ci, anyOutput *Out, void *data_obj)
 		m1 = m2 = -1;		Redraw(Out, true);		return true;
 		}
 	else return false;
+	if(parent) {
+		((DataObj*)parent)->Command(CMD_MRK_DIRTY, 0L, 0L);
+		((DataObj*)parent)->Command(CMD_SAVEPOS, 0L, 0L);
+		}
 	Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
+	bgLine = &ETbgna; bgFill = &ETfbna; TextCol = 0x00000000L;
 	if(text)length = 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);
+	if(hasMark()) {			//delete marked part of text
+			if(m1 > m2) Swap(m1, m2);
+			if(m2 >= (short int)strlen(text)) text[m1] = 0;
+			else strcpy(text+m1, text+m2);
+			CursorPos = m1;
+			m1 = m2 = -1;
+			}
 	byte1 = text[CursorPos];
 	i = CursorPos;
 	text[i++] = c;
 	while(byte1) {
 		byte2 = byte1;			byte1 = text[i];			text[i++] = byte2;
 		}
-	text[i] = byte1;
-	CursorPos++;
+	text[i] = byte1;			CursorPos++;				type = ET_UNKNOWN;
 	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);
+	if(parent && text) {
+		((DataObj*)parent)->Command(CMD_ETRACC, text[0] == '=' && ci != ')' ? this : 0L, 0L);
+		}
 	return true;
 }
 
@@ -129,39 +140,46 @@ EditText::Update(int select, anyOutput *Out, POINT *MousePos)
 {
 	POINT MyPos;
 
+	if(!parent && !disp) disp = Out;
 	if(select != 1 && select != 5) m1 = m2 = -1;		//no mark;
 	switch(select) {
 		case 0:							//just redraw with current settings
 			Redraw(Out, true);
 			break;
 		case 5:							//dialog control
-			if(!text)Align = TXA_VTOP | TXA_HLEFT;
+			if(!text)Align = TXA_VCENTER | 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((type & 0xff) == ET_FORMULA) Align = TXA_VCENTER | TXA_HLEFT;
+			if(!text && !(text = (char *) calloc(10, sizeof(char))))return;
 			if(CursorPos > (int)strlen(text)) CursorPos = (int)strlen(text);
+			if(MousePos && (type & 0xff) == ET_TEXT && (text && text[0] == '\'' && (bgLine == &ETbgnn || bgLine == &ETbgmn))) {
+				MousePos->x += 4;
+				}
 			bgLine = &ETbgna; bgFill = &ETfbna; TextCol = 0x00000000L;
-			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);
+			if(Out) {
+				Redraw(Out, true);
+				MyPos.y = loc.y;			MyPos.x = Align & TXA_HRIGHT ? crb.x - 4 : loc.x + 4;
+				if(MousePos && MousePos->x && MousePos->y) {
+					Out->TextCursor(text, MyPos, MousePos,&CursorPos, 
+					scroll_et == this ? scroll_dist : scroll_dist=0);
+					}
+				else if(select ==1) Out->TextCursor(text, MyPos, NULL, &CursorPos, 
+					scroll_et == this ? scroll_dist : (scroll_dist=0)+2);
+				}
 			break;
 		case 2:							//inactive spreadsheet cell
-			if((type & 0xff) == ET_FORMULA) Align = TXA_VTOP | TXA_HRIGHT;
+			if((type & 0xff) == ET_FORMULA) Align = TXA_VCENTER | 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);
+			if(Out) Redraw(Out, true);
 			break;
 		case 10:						//value filled in by external app.
 			if(text && text[0]) {
 				type = ET_VALUE;
-				Align = TXA_VTOP | TXA_HRIGHT;
+				Align = TXA_VCENTER | TXA_HRIGHT;
 				sscanf(text, "%lf", &Value);
 				}
 			break;
@@ -177,16 +195,18 @@ EditText::Redraw(anyOutput *Out, bool display)
 	RECT rc;
 	POINT MyPos;
 	char *txt, tmptxt[80];
-	int i, j, w, h, o_crbx;
+	int w, h, o_crbx;
 	bool b_clip = false;
 	anyOutput *opc;
 	anyResult *fmres;
 
+	if(!parent && disp) Out = disp;
+	if(loc.x <1 || rb.x < 1 || loc.y <1 || rb.y <1) return false;
 	o_crbx = crb.x;			crb.x = rb.x;				crb.y = rb.y;
 	if (Out) {
+		if (m1 >m2) Swap(m1, m2);
 		if(((type & 0xff) == ET_UNKNOWN) && text && text[0]) FindType();
-		Out->TxtSet.Align = Align;
-		Out->TxtSet.ColTxt = TextCol;
+		Out->TxtSet.Align = Align;		Out->TxtSet.ColTxt = TextCol;
 		Out->TxtSet.ColBg = bgLine->color;
 		if(text && text[0]) {
 			Out->oGetTextExtent(text, strlen(text), &w, &h);
@@ -198,28 +218,28 @@ EditText::Redraw(anyOutput *Out, bool display)
 			}
 		Out->SetFill(bgFill);		Out->SetLine(bgLine);
 		rc.left = loc.x;			rc.right = crb.x;
-		rc.top = loc.y;				rc.bottom = crb.y;
+		rc.top = loc.y+1;			rc.bottom = crb.y-2;
 		Out->oRectangle(loc.x, loc.y, crb.x-1, crb.y-1);
-		if(!text && (type & 0xff) == ET_VALUE){
+		if((!text || !text[0]) && (type & 0xff) == ET_VALUE){
 			sprintf(tmptxt, "%g", Value);
-			text = strdup(tmptxt);
+			if(text = (char*)realloc(text, strlen(tmptxt)+2)) strcpy(text, tmptxt);
+			CursorPos = 0;
 			}
+		if(ftext) free(ftext);		ftext = 0L;
 		if(text && text[0]){
 			if((type & 0xff) == ET_FORMULA && (bgLine == &ETbgnn || bgLine == &ETbgmn)) {
-				Out->TxtSet.Align = TXA_HLEFT;
+				Out->TxtSet.Align = TXA_HLEFT | TXA_VCENTER;
 				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
+					b_clip = false;
 					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;
+						fit_num_rect(Out, rb.x - loc.x, tmptxt);
+						Out->TxtSet.Align = TXA_HRIGHT | TXA_VCENTER;
 						}
 					else if(fmres->type == ET_TEXT) {
+						if(ftext) free (ftext);		ftext = 0L;
+						if(fmres->text) ftext = strdup(fmres->text);
 						if(fmres->text && strlen(fmres->text)<sizeof(tmptxt)) strcpy(tmptxt, fmres->text);
 						else if(fmres->text) sprintf(tmptxt,"#SIZE");
 						else tmptxt[0] = 0;
@@ -229,21 +249,36 @@ EditText::Redraw(anyOutput *Out, bool display)
 				else sprintf(tmptxt, "#ERROR");
 				txt = tmptxt;
 				}
+			else if((type &0xff) == ET_VALUE) {
+				Out->oGetTextExtent(text, strlen(text), &w, &h);
+				if(w >= (rb.x - loc.x-8) && (bgLine == &ETbgnn || bgLine == &ETbgmn)) {
+					sprintf(tmptxt, "%g", Value);
+					fit_num_rect(Out, rb.x - loc.x, tmptxt);
+					txt = tmptxt;		b_clip = false;
+					}
+				else txt = text;
+				}
+			else if((type & 0xff) == ET_TEXT) {
+				if(text && text[0] == '\'' && (bgLine == &ETbgnn || bgLine == &ETbgmn)) {
+					txt = text+1;
+					}
+				else txt = text;
+				}
 			else txt = text;
-			MyPos.y = loc.y;
+			MyPos.y = (loc.y+rb.y)>>1;
 			if(Out->TxtSet.Align & TXA_HRIGHT) {	//right justified text
-				MyPos.x = crb.x-2;
+				MyPos.x = crb.x-4;
 				}
 			else {									//left justified text
-				MyPos.x = loc.x +2;
+				MyPos.x = loc.x+4;
 				}
-			if(b_clip && (opc = NewBitmapClass(w+22, h+2, Out->hres, Out->vres))){
+			if(b_clip && (opc = NewBitmapClass(w+22, rb.y-loc.y, Out->hres, Out->vres))){
 				if(scroll_et != this || parent) {
 					scroll_et = this;	scroll_dist = 0;
 					}
 				opc->Erase(bgFill->color);
-				opc->SetTextSpec(&Out->TxtSet);		opc->TxtSet.Align = TXA_HLEFT | TXA_VTOP;
-				opc->oTextOut(2, 0, txt, strlen(txt));
+				opc->SetTextSpec(&Out->TxtSet);		opc->TxtSet.Align = TXA_HLEFT | TXA_VCENTER;
+				opc->oTextOut(4,(rb.y-loc.y)>>1, txt, strlen(txt));
 				if(!parent && CursorPos) {
 					Out->oGetTextExtent(txt, CursorPos, &w, &h);
 					while((scroll_dist + w)>(rc.right-rc.left-10)) scroll_dist -=10;
@@ -252,20 +287,24 @@ EditText::Redraw(anyOutput *Out, bool display)
 					}
 				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);
+					rc.right-rc.left-4, rc.bottom-rc.top-2, false);
 				DelBitmapClass(opc);
 				}
 			else {
+				if(display && hasMark() && mx1 > loc.x && mx2 < crb.x) {
+					Out->SetFill(&yFill);		Out->SetLine(&yLine);
+					Out->oRectangle(mx1, rc.top, mx2, rc.bottom);
+					Out->SetFill(bgFill);		Out->SetLine(bgLine);
+					}
 				scroll_dist = 0;
-				Out->oTextOut(MyPos.x, loc.y, txt, 0);
+				Out->oTextOut(MyPos.x, MyPos.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);
+			if(hasMark() && mx1 > loc.x && mx2 < rb.x) {
 				rc.left = mx1;		rc.right = mx2;
-				Out->ShowMark(&rc, MRK_INVERT);
+				Out->UpdateRect(&rc, true);
 				Out->MrkMode = MRK_NONE;
 				}
 			}
@@ -282,6 +321,7 @@ EditText::Mark(anyOutput *Out, int mark)
 	DWORD ocol = TextCol;
 
 	m1 = m2 = -1;
+	if(!parent) return;
 	switch (mark){
 	case 0:				//normal not active
 		bgLine = &ETbgnn; bgFill = &ETfbnn; TextCol = 0x00000000L;
@@ -290,13 +330,19 @@ EditText::Mark(anyOutput *Out, int mark)
 		bgLine = &ETbgna; bgFill = &ETfbna; TextCol = 0x00000000L;
 		break;
 	case 2:				//mark not active
-		bgLine = &ETbgmn; bgFill = &ETfbmn; TextCol = 0x00ffffffL;
+		bgLine = &ETbgmn; bgFill = &ETfbmn; TextCol = 0x00c00000L;
 		break;
 	case 3:				//mark active
 		bgLine = &ETbgma; bgFill = &ETfbma; TextCol = 0x00ff0000L;
 		break;
 		}
+	if(!mark || mark == 2) {
+		loc.y--;	rb.y++;
+		}
 	Redraw(Out, true);
+	if(!mark || mark == 2) {
+		loc.y++;	rb.y--;
+		}
 	bgLine = ol;	bgFill = of;	TextCol = ocol;
 }
 
@@ -309,10 +355,12 @@ EditText::Command(int cmd, anyOutput *Out, void *data_obj)
 	static RECT rMark;
 	bool bRet;
 	char *tag1, *tag2;
+	unsigned char *pt;
 
 	MyPos.y = loc.y;
-	MyPos.x = Align & TXA_HRIGHT ? crb.x - 2 : loc.x + 2;
+	MyPos.x = Align & TXA_HRIGHT ? crb.x - 4 : loc.x + 4;
 	if(!(text)) return false;
+	if(!parent && disp) Out = disp;		//Dialog !
 	switch(cmd) {
 		case CMD_MRK_DIRTY:
 			type = ET_UNKNOWN;
@@ -325,10 +373,11 @@ EditText::Command(int cmd, anyOutput *Out, void *data_obj)
 				((DataObj*)parent)->Command(CMD_MRK_DIRTY, Out, 0L);
 				}
 			else return Command(CMD_REDRAW, Out, data_obj);
+			return true;
 		case CMD_SETFONT:
 			if (!text || !text[0]) return false;
 			type = ET_TEXT;
-			if(m1 > -1 && m2 > -1) {
+			if(hasMark()) {
 				Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
 				switch (*((int*)data_obj)) {
 				case FONT_HELVETICA:
@@ -356,7 +405,7 @@ EditText::Command(int cmd, anyOutput *Out, void *data_obj)
 				for( ; TmpTxt[i++] = text[w]; w++);
 				m1 += (w = strlen(tag1));	m2 += w;	CursorPos += w;
 				CleanTags(TmpTxt, &m1, &m2, &CursorPos);
-				free(text);					text = strdup(TmpTxt);
+				if(text = (char*)realloc(text, strlen(TmpTxt)+2)) strcpy(text, TmpTxt);
 				Command(CMD_REDRAW, Out, 0L);
 				return true;
 				}
@@ -364,7 +413,7 @@ EditText::Command(int cmd, anyOutput *Out, void *data_obj)
 		case CMD_SETSTYLE:
 			if (!text || !text[0]) return false;
 			type = ET_TEXT;
-			if(m1 > -1 && m2 > -1) {
+			if(hasMark()) {
 				Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
 				switch (*((int*)data_obj)) {
 				case TXS_BOLD:
@@ -404,12 +453,28 @@ EditText::Command(int cmd, anyOutput *Out, void *data_obj)
 				for( ; TmpTxt[i++] = text[w]; w++);
 				m1 += (w = strlen(tag1));	m2 += w;	CursorPos += w;
 				CleanTags(TmpTxt, &m1, &m2, &CursorPos);
-				free(text);					text = strdup(TmpTxt);
+				if(text = (char*)realloc(text, strlen(TmpTxt)+2)) strcpy(text, TmpTxt);
 				Command(CMD_REDRAW, Out, 0L);
 				return true;
 				}
 			return false;
+		case CMD_ADDTXT:
+			if(data_obj && *((char*)data_obj) && text && 
+				(type == ET_TEXT || type == ET_UNKNOWN || type == ET_FORMULA)){
+				if(hasMark()) Command(CMD_DELETE, 0L, 0L);
+				else Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
+				if(m1 > -1 && m2 > -1) Command(CMD_DELETE, 0L, 0L);
+				for(i = 0; i < CursorPos && text[i]; i++) TmpTxt[i] = text[i];
+				j = i + sprintf(TmpTxt+i, "%s", (char*)data_obj);
+				if(text[i]) sprintf(TmpTxt+j, "%s", text+i);
+				if(text = (char*)realloc(text, strlen(TmpTxt)+2)) strcpy(text, TmpTxt);
+				CursorPos += strlen((char*)data_obj);
+				Out->Focus();
+				Update(1, Out, 0L);
+				}
+			return true;
 		case CMD_BACKSP:
+			if(!text) return false;
 			if(CursorPos <=0){
 				Out->TextCursor(text, MyPos, (POINT *) NULL, &CursorPos, 
 					scroll_et == this ? scroll_dist : scroll_dist=0);
@@ -418,12 +483,22 @@ EditText::Command(int cmd, anyOutput *Out, void *data_obj)
 			Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
 			CursorPos--;						//continue as if delete
 		case CMD_DELETE:
+			if(!text) return false;
+			if(parent) {
+				((DataObj*)parent)->Command(CMD_ETRACC, text[0] == '=' ? this : 0L, 0L);
+				}
 			if(cmd == CMD_DELETE) Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
+			if(parent) {
+				((DataObj*)parent)->Command(CMD_MRK_DIRTY, 0L, 0L);
+				((DataObj*)parent)->Command(CMD_SAVEPOS, 0L, 0L);
+				}
 			bRet = false;
-			if(m1 > -1 && m2 > -1) {			//delete marked part of text
+			if(!text || !text[0]) {
+				type = ET_UNKNOWN;	CursorPos = 0;
+				}
+			if(hasMark()) {			//delete marked part of text
 				if (!text || !text[0]) return false;
 				if(m1 > m2) Swap(m1, m2);
-				if(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;
@@ -431,22 +506,62 @@ EditText::Command(int cmd, anyOutput *Out, void *data_obj)
 				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(!text || !text[0]) {
+					type = ET_UNKNOWN;	CursorPos = 0;
+					}
 				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_COPY:
+			if(text && text[0]) {
+				rMark.left = loc.x+2;		rMark.right = rb.x-2;
+				rMark.top = loc.y+1;		rMark.bottom = rb.y-2;
+				if(m1 != m2 && m1 >=0 && m2 >=0) {
+					if (m1 >m2) Swap(m1, m2);
+					rMark.left = mx1;		rMark.right = mx2;
+					if(Out) Out->UpdateRect(&rMark, false);
+					CopyText(text+m1, m2-m1);
+					if(Out) {
+						Out->MrkMode = MRK_NONE;
+						ShowCopyMark(Out, &rMark, 1);
+						Out->UpdateRect(&rMark, true);
+						}
+					return false;
+					}
+				CopyText(text, strlen(text));
+				if(Out)Out->UpdateRect(&rMark, true);
+				return false;
+				}
+			return false;
+		case CMD_PASTE:
+			if(pt = PasteText()) {
+				Undo.TextCell(this, Out, text, &CursorPos, &m1, &m2, parent, 0L);
+				for(i = 0; pt[i] > 0x20 && i < 81; i++) this->AddChar(pt[i], 0L, 0L);
+				if(Out) Redraw(Out, true);				free(pt);
+				if(parent && text) {
+					((DataObj*)parent)->Command(CMD_ETRACC, text[0] == '=' ? this : 0L, 0L);
+					}
+				if(i) return true;
+				}
+			return false;
 		case CMD_SHIFTRIGHT:
 			if(CursorPos == m1 && text[m1]) m1++;
 			else if(CursorPos == m2 && text[m2]) m2++;
 			else if(text[CursorPos]){
 				m1 = CursorPos;	m2 = CursorPos+1;
 				}
+			if(parent && text) {
+				((DataObj*)parent)->Command(CMD_ETRACC, text[0] == '=' ? this : 0L, 0L);
+				}
 			if(text[CursorPos]) CursorPos++;
 			else return false;
 		case CMD_SHIFTLEFT:
+			if(parent && text) {
+				((DataObj*)parent)->Command(CMD_ETRACC, text[0] == '=' ? this : 0L, 0L);
+				}
 			if (!(CursorPos)) return false;
 		case CMD_REDRAW:
 			if(cmd == CMD_SHIFTLEFT) {
@@ -462,10 +577,10 @@ EditText::Command(int cmd, anyOutput *Out, void *data_obj)
 				w = h = 0;
 				if(Align & TXA_HRIGHT) {	//right justified text
 					Out->oGetTextExtent(text, 0, &w, &h);
-					mx1 = crb.x-2 - w;
+					mx1 = crb.x-4 - w;
 					}
 				else {						//left justified text
-					mx1 = loc.x +2;
+					mx1 = loc.x +4;
 					}
 				Out->oGetTextExtent(text, m1, &w, &h);
 				mx1 += (m1 ? w : 0);
@@ -478,6 +593,9 @@ EditText::Command(int cmd, anyOutput *Out, void *data_obj)
 			return true;
 		case CMD_CURRLEFT:
 			m1 = m2 = -1;
+			if(parent && text) {
+				((DataObj*)parent)->Command(CMD_ETRACC, text[0] == '=' ? this : 0L, 0L);
+				}
 			if(CursorPos >0) {
 				CursorPos--;
 				if(Redraw(Out, true) && Out->TextCursor(text, MyPos, (POINT *) NULL,
@@ -485,14 +603,17 @@ EditText::Command(int cmd, anyOutput *Out, void *data_obj)
 				else return false;
 				}
 			else if (data_obj) {
-				MyPos.x = loc.x-2;			MyPos.y = (rb.y+loc.y)/2;
+				MyPos.x = loc.x-4;			MyPos.y = (rb.y+loc.y)/2;
 				if(((DataObj*)data_obj)->Select(&MyPos))return true;
-				MyPos.x = loc.x+2;
+				MyPos.x = loc.x+4;
 				((DataObj*)data_obj)->Select(&MyPos);
 				}
 			return false;
 		case CMD_CURRIGHT:
 			m1 = m2 = -1;
+			if(parent && text) {
+				((DataObj*)parent)->Command(CMD_ETRACC, text[0] == '=' ? this : 0L, 0L);
+				}
 			if(text[CursorPos]){
 				CursorPos++;
 				if(Redraw(Out, true) && Out->TextCursor(text, MyPos, (POINT *) NULL,
@@ -500,13 +621,20 @@ EditText::Command(int cmd, anyOutput *Out, void *data_obj)
 				else return false;
 				}
 			else if (data_obj) {
-				MyPos.x = rb.x+2;		MyPos.y = (rb.y+loc.y)/2;	crb.x = rb.x;
+				MyPos.x = rb.x+4;		MyPos.y = (rb.y+loc.y)/2;	crb.x = rb.x;
 				if(((DataObj*)data_obj)->Select(&MyPos)) return true;
-				MyPos.x = rb.x-2;
+				MyPos.x = rb.x-4;
 				((DataObj*)data_obj)->Select(&MyPos);
 				}
 			return false;
+		case CMD_UPDATE:
+			m1 = m2 = -1;
+			Redraw(Out, true);
+			return true;
 		case CMD_POS_FIRST:		case CMD_POS_LAST:
+			if(parent && text) {
+				((DataObj*)parent)->Command(CMD_ETRACC, text[0] == '=' ? this : 0L, 0L);
+				}
 			CursorPos = (cmd == CMD_POS_LAST && text && text[0]) ? strlen(text) : 0;
 			m1 = m2 = -1;
 			Redraw(Out, true);
@@ -518,11 +646,11 @@ EditText::Command(int cmd, anyOutput *Out, void *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.x = rb.x-4-((rb.y-loc.y-4)*(strlen(text)-CursorPos))/2;
+				else MyPos.x = loc.x+4+((rb.y-loc.y-4)*CursorPos)/2;
 				MyPos.y = (cmd == CMD_CURRUP) ? loc.y-2 : rb.y+2;
-				if(MyPos.x < loc.x) MyPos.x = loc.x +2;
-				if(MyPos.x > rb.x) MyPos.x = rb.x -2;
+				if(MyPos.x < loc.x) MyPos.x = loc.x +4;
+				if(MyPos.x > rb.x) MyPos.x = rb.x -4;
 				if(((DataObj*)data_obj)->Select(&MyPos))return true;
 				MyPos.y = rb.y;
 				((DataObj*)data_obj)->Select(&MyPos);
@@ -537,19 +665,23 @@ EditText::Command(int cmd, anyOutput *Out, void *data_obj)
 				return true;
 				}
 			if(mev->Action == MOUSE_LBDOUBLECLICK) {
-				rMark.top = loc.y;				rMark.bottom = rb.y;
+				rMark.top = loc.y+1;			rMark.bottom = rb.y-2;
 				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;
+					rMark.right = crb.x -4;		rMark.left = crb.x - w - 4;
 					}
 				else {							//left justified text
-					rMark.left = loc.x +2;		rMark.right = rMark.left +w;
+					rMark.left = loc.x +4;		rMark.right = rMark.left +w;
+					}
+				mx1 = rMark.left;				mx2 = rMark.right;
+				Redraw(Out, true);
+				if(parent && text) {
+					((DataObj*)parent)->Command(CMD_ETRACC, text[0] == '=' ? this : 0L, 0L);
 					}
-				Out->UpdateRect(&rMark,true);
 				return true;
 				}
-			MyPos.x = Align & TXA_HRIGHT ? mev->x + 2 : mev->x - 2;
+			MyPos.x = Align & TXA_HRIGHT ? mev->x + 4 : mev->x - 4;
 			MyPos.y = mev->y;
 			Out->TxtSet.Align = Align;
 			j = Out->CalcCursorPos(text, Align & TXA_HRIGHT ? crb :loc, &MyPos);
@@ -557,14 +689,14 @@ EditText::Command(int cmd, anyOutput *Out, void *data_obj)
 			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;
+					w = crb.x - w - 4;
 					}
 				else w = crb.x-1;
 				}
 			else {								//left justified text
 				if(!j) w = 0;
 				else if(!Out->oGetTextExtent(text, j, &w, &h))return false;
-				w += (loc.x+2);
+				w += (loc.x+4);
 				}
 			if(m1 == m2 && m1 == -1) {
 				mx1 = (short)(rMark.left = w);
@@ -574,13 +706,12 @@ EditText::Command(int cmd, anyOutput *Out, void *data_obj)
 				m2 = j;
 				if(m2 >= 0)Out->UpdateRect(&rMark, false);
 				mx2 = (short)(rMark.right = w);
-				rMark.top = loc.y;
-				rMark.bottom = rb.y;
+				rMark.top = loc.y+1;				rMark.bottom = rb.y-2;
 				if(rMark.right < crb.x && rMark.right > loc.x &&
 					rMark.left > loc.x && rMark.left < crb.x)
 					Out->UpdateRect(&rMark,true);
 				}
-			if(m1 >= 0 && m2 >= 0 && m1 != m2 && parent)
+			if(hasMark() && parent)
 				//remove range-mark of data 
 				((DataObj*)parent)->Command(CMD_UNLOCK, 0L, 0L);
 			return true;
@@ -618,10 +749,7 @@ EditText::GetValue(double *v)
 			type &= ~ET_BUSY;
 			return false;
 			}
-		else if(parent) {
-			((DataObj*)parent)->Command(CMD_ERROR, (void*)"circular reference in formula", 0L);
-			type |= ET_CIRCULAR;
-			}
+		else type |= ET_CIRCULAR;
 		*v = Value;
 		return true;
 		}
@@ -629,25 +757,31 @@ EditText::GetValue(double *v)
 }
 
 bool
-EditText::GetText(char *t, int size)
+EditText::GetText(char *tx, int size)
 {
-	if(text) {
-		if((int)strlen(text) < size) strcpy(t, text);
+	char *t;
+
+	if(text && text[0]) {
+		if(text[0] =='\'' && text[1]) t = text + 1;
+		else t = text;
+		}
+	else t = 0L;
+	if(t) {
+		if((int)strlen(t) < size) strcpy(tx, t);
 		else {
-			memcpy(t, &text, size-1);			t[size-1] = 0;
+			memcpy(tx, text, size-1);			tx[size-1] = 0;
 			}
 		return true;
 		}
 	if((type & 0xff) == ET_VALUE) {
-		sprintf(TmpTxt, "%lf", Value);
-		text = strdup(TmpTxt);
-		return GetText(t, size);
+		if(text = (char*)realloc(text, 20)) sprintf(text, "%g", Value);
+		if(text && text[0]) return(GetText(tx, size));
 		}
 	return false;
 }
 
 bool
-EditText::GetResult(anyResult *r)
+EditText::GetResult(anyResult *r, bool use_last)
 {
 	anyResult * res;
 
@@ -671,6 +805,15 @@ EditText::GetResult(anyResult *r)
 		return true;
 		}
 	if((type & 0xff) == ET_FORMULA){
+		if(use_last) {
+			if(ftext) {
+				r->text = ftext;	r->value = 0.0;			r->type = ET_TEXT;
+				}
+			else {
+				r->text = 0L;		r->value = Value;		r->type = ET_VALUE;
+				}
+			return true;
+			}
 		if(!(type & ET_BUSY)){
 			type |= ET_BUSY;
 			if(res = do_formula((DataObj*)parent, text+1)) {
@@ -683,8 +826,8 @@ EditText::GetResult(anyResult *r)
 			type &= ~ET_BUSY;
 			return false;
 			}
-		else if(parent) {
-			((DataObj*)parent)->Command(CMD_ERROR, (void*)"circular reference in formula", 0L);
+		else {
+			type |= ET_CIRCULAR;
 			r->text = "#CIRC.";	r->value = 0.0;			r->type = ET_TEXT;
 			return true;
 			}
@@ -696,9 +839,7 @@ EditText::GetResult(anyResult *r)
 bool
 EditText::SetValue(double v)
 {
-	if(text){
-		free(text);		text = 0L;
-		}
+	if(text) text[0] = 0;
 	Value = v;	type = ET_VALUE;
 	return true;
 }
@@ -706,11 +847,10 @@ EditText::SetValue(double v)
 bool
 EditText::SetText(char *t)
 {
-	if(text){
-		free(text);		text = 0L;
-		}
 	Value = 0.0;	type = ET_UNKNOWN;
-	if(t && t[0]) text = strdup(t);
+	bgLine = &ETbgnn; bgFill = &ETfbnn; TextCol = 0x00000000L;
+	if(t && t[0] && (text = (char*)realloc(text, strlen(t)+2))) strcpy(text, t);
+	else if (text) text[0] = 0;
 	return false;
 }
 
@@ -777,23 +917,23 @@ void
 EditText::FindType()
 {
 	if(text && text[0] == '=') {
-		Align = TXA_VTOP | TXA_HRIGHT;
+		Align = TXA_VCENTER | TXA_HRIGHT;
 		type = ET_FORMULA;
 		}
 	else if(text && (Txt2Flt(text, &Value))) {
-		Align = TXA_VTOP | TXA_HRIGHT;
+		Align = TXA_VCENTER | TXA_HRIGHT;
 		type = ET_VALUE;
 		}
 	else if (text && text[0]) {
-		Align = TXA_VTOP | TXA_HLEFT;
+		Align = TXA_VCENTER | TXA_HLEFT;
 		type = ET_TEXT;
 		}
 	else if ((type && 0xff) == ET_VALUE) {
-		Align = TXA_VTOP | TXA_HRIGHT;
+		Align = TXA_VCENTER | TXA_HRIGHT;
 		type = ET_VALUE;
 		}
 	else {
-		Align = TXA_VTOP | TXA_HRIGHT;
+		Align = TXA_VCENTER | TXA_HRIGHT;
 		type = ET_UNKNOWN;
 		}
 }
@@ -827,6 +967,7 @@ static tag_info tags[] = {
 	{"<bullet3>", 0, 0, -1, 3},	{"<bullet4>", 0, 0, -1, 4},
 	{"<bullet5>", 0, 0, -1, 5},	{"<bullet6>", 0, 0, -1, 6},
 	{"<bullet7>", 0, 0, -1, 7},	{"<bullet8>", 0, 0, -1, 8},
+	{"<bullet9>", 0, 0, -1, 9},	{"<bullet10>", 0, 0, -1, 10},
 	{"<white>", 0, 0, -1, 100},	{"<black>", 0, 0, -1, 101},
 	{"<red>", 0, 0, -1, 102},	{"<blue>", 0, 0, -1, 103},
 	{"<green>", 0, 0, -1, 104},	{"<yellow>", 0, 0, -1, 105},
@@ -935,13 +1076,16 @@ fmtText::oGetTextExtent(anyOutput *o, int *width, int *height, int cb)
 			o->SetTextSpec(&td2);
 			l += strlen(tags[n].tag);
 			}
-		if(split_text[i].txt){
+		if(split_text[i].txt && split_text[i].txt[0]){
 			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;
+			w += w1;	h = h1 > h ? h1 : h;
 			if (l >= cb) break;
 			}
+		if(tags[n].font == -1 && tags[n].op > 0 && tags[n].op < 100) {
+			w += o->un2ix(td2.fSize/2.0);
+			}
 		}
 	*width = w;			*height = h;		o->SetTextSpec(&td1);
 	return true;
@@ -1065,19 +1209,24 @@ fmtText::DrawBullet(anyOutput *o, int x, int y, int type, double size, DWORD lc,
 		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;
+	case 5:		case 6:		case 7:		case 8:		case 9:		case 10:
+		if(type == 6 || type == 8 || type == 10) 		fd.color = ld.color;
+		pts[0].x = pts[3].x = pts[4].x = x - is;
+		pts[1].x = x;		pts[2].x = x+is;
 		if(type == 5 || type == 6) {
 			pts[0].y = pts[2].y = pts[3].y = y+o->un2iy(size*0.19439);
 			pts[1].y = y-o->un2iy(size*0.38878);
 			}
+		else if(type == 9 || type ==10) {
+			pts[0].y = pts[2].y = pts[4].y = y;
+			pts[1].y = y - is;		pts[3].y = y + is;		pts[3].x = x;
+			}
 		else {
 			pts[0].y = pts[2].y = pts[3].y = y-o->un2iy(size*0.19439);
 			pts[1].y = y+o->un2iy(size*0.38878);
 			}
 		o->SetLine(&ld);				o->SetFill(&fd);
-		o->oPolygon(pts, 4);
+		o->oPolygon(pts, type < 9 ? 4 : 5);
 		break;
 		}
 }
@@ -1120,7 +1269,7 @@ fmtText::DrawFormatted(anyOutput *o)
 				x1 -= iround(o->un2fix(td2.fSize)*si);
 				}
 			switch (tags[n].op) {
-			case 1:	case 2: case 3:	case 4: case 5:	case 6:	case 7:	case 8:
+			case 1:	case 2: case 3:	case 4: case 5:	case 6:	case 7:	case 8:	case 9:	case 10:
 				DrawBullet(o, x1, y1, tags[n].op, td2.fSize, td2.ColTxt, 0x00ffffff);
 				w = o->un2ix(td2.fSize/2.0);
 				x = iround(fx += (w*csi));		y = iround(fy -= (w*si));
@@ -1173,7 +1322,7 @@ DataObj::Init(int nR, int nC)
 			FlushData();	return false;
 			}
 		if(etRows[i]) for(j = 0; j < cCols; j++) {
-			etRows[i][j] = new EditText(this, 0L);
+			etRows[i][j] = new EditText(this, 0L, i, j);
 			}
 		}
 	return true;
@@ -1220,10 +1369,10 @@ DataObj::GetTextPtr(int row, int col)
 }
 
 bool
-DataObj::GetResult(anyResult *r, int row, int col)
+DataObj::GetResult(anyResult *r, int row, int col, bool use_last)
 {
 	if(row < 0 || row >= cRows || col < 0 || col >= cCols) return false;
-	if(etRows[row][col]) return etRows[row][col]->GetResult(r);
+	if(etRows[row][col]) return etRows[row][col]->GetResult(r, use_last);
 	return false;
 }
 
@@ -1252,32 +1401,49 @@ DataObj::FlushData()
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Store Data Object as strings: less memory required than with DataObj
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-StrData::StrData(DataObj *par)
+StrData::StrData(DataObj *par, RECT *rc)
 {
-	int r, c;
-	char *tx;
+	int r1, c1, r2, c2, w, h;
+	char **tx;
 
-	pw = ph = w = h = 0;		str_data = 0L;
-	if(src = par) {
-		src->GetSize(&pw, & ph);
+	pw = ph = 0;		str_data = 0L;
+	drc.left = drc.right = drc.top = drc.bottom = 0;
+	if(!(src = par)) return;
+	src->GetSize(&pw, & ph);
+	if(rc) {
+		if(0>(h = (rc->bottom - rc->top)) || 0>(w = (rc->right - rc->left))) return;
+		if(!(str_data = (char***)calloc(h+1, sizeof(char**))))return;
+		drc.left = rc->left;				drc.right = rc->right;
+		drc.top = rc->top;					drc.bottom = rc->bottom;
+		for (r1 = 0, r2 = drc.top; r1 <= h; r1++, r2++) {
+			if(!(str_data[r1] = (char**)malloc((w+1) * sizeof(char*)))) break;
+			for(c1 = 0, c2= drc.left; c1 <= w; c1++, c2++) {
+				tx = src->GetTextPtr(r2, c2);
+				str_data[r1][c1] = tx && *tx && *tx[0] ? strdup(*tx) : 0L;
+				}
+			}
+		}
+	else {
 		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;
+		for (r1 = 0; r1 < ph; r1++) {
+			if(!(str_data[r1] = (char**)malloc(pw * sizeof(char*)))) break;
+			for(c1 = 0; c1 < pw; c1++) {
+				tx = src->GetTextPtr(r1, c1);
+				str_data[r1][c1] = tx && *tx && *tx[0] ? strdup(*tx) : 0L;
 				}
 			}
+		drc.right = pw-1;		drc.bottom = ph-1;
 		}
-	w = pw;		h = ph;
 }
 
 StrData::~StrData()
 {
-	int r, c;
+	int r, c, w, h;
 
-	if(str_data) for (r = 0; r < h; r++) {
+	w = drc.right-drc.left;			h = drc.bottom-drc.top;
+	if(str_data) for (r = 0; r <= h; r++) {
 		if(str_data[r]) {
-			for(c = 0; c < w; c++) if(str_data[r][c]) free(str_data[r][c]);
+			for(c = 0; c <= w; c++) if(str_data[r][c]) free(str_data[r][c]);
 			free(str_data[r]);
 			}
 		}
@@ -1294,12 +1460,13 @@ StrData::GetSize(int *uw, int *uh)
 void
 StrData::RestoreData(DataObj *dest)
 {
-	int r, c;
+	int r1, c1, r2, c2;
 
-	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]);
+	if(!dest || !str_data) return;
+	for (r1 = 0, r2 = drc.top; r2 <= drc.bottom; r1++, r2++) {
+		if(str_data[r1]) {
+			for(c1 = 0, c2 = drc.left; c2 <= drc.right; c1++, c2++) 
+				dest->SetText(r2, c2, str_data[r1][c1]);
 			}
 		}
 	return;
@@ -1508,11 +1675,10 @@ notary::FreeStack()
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 AccRange::AccRange(char *asc)
 {
-	int i, j;
+	int i, j, l;
 
-	if(asc && *asc){
-		i = strlen(asc)+2;
-		txt = (char *)malloc(i);
+	if(asc && *asc && (l=strlen(asc)) >1){
+		txt = (char *)malloc(l+2);
 		for(i = j = 0; i< (int)strlen(asc); i++)
 			if(asc[i] > 32) txt[j++] = asc[i];
 		txt[j] = 0;
@@ -1566,6 +1732,26 @@ AccRange::GetNext(int *x, int *y)
 }
 
 bool
+AccRange::NextRow(int *y)
+{
+	if(cy <= y2) {
+		*y = cy;	cy++;	return true;
+		}
+	else if(txt[curridx] && Parse(curridx)) return NextRow(y);
+	return false;
+}
+
+bool
+AccRange::NextCol(int *x)
+{
+	if(cx <= x2) {
+		*x = cx;	cx++;	return true;
+		}
+	else if(txt[curridx] && Parse(curridx)) return NextCol(x);
+	return false;
+}
+
+bool
 AccRange::IsInRange(int x, int y)
 {
 	if(txt && Reset())	do {
@@ -1582,6 +1768,7 @@ AccRange::BoundRec(RECT *rec)
 		while((curridx < (int)strlen(txt)) && Parse(curridx)) {
 			UpdateMinMaxRect(rec, x1, y1);	UpdateMinMaxRect(rec, x2, y2);
 			}
+		Reset();
 		return true;
 		}
 	return false;
@@ -1597,15 +1784,15 @@ AccRange::Reset()
 bool
 AccRange::Parse(int start)
 {
-	int i, step, *v;
+	int i, l, step, *v;
 
 	i = start;
 	if(!txt) return false;
-	if(txt[i] == ';') i++;
+	if(txt[i] == ';' || txt[i] == ',') i++;
 	if(!txt[i]) return false;
 	step = x1 = y1 = x2 = y2 = 0;
 	v = &x1;
-	for ( ; i < (int)strlen(txt)+1; i++) {
+	for (l=strlen(txt)+1 ; i < l; i++) {
 		if(txt[i] == '$') i++;
 		switch(step) {
 		case 0:
@@ -1648,8 +1835,8 @@ AccRange::Parse(int start)
 					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--;
+				if(y1 >=0) y1--;			if(y2 >=0) y2--;
+				if(x1 >=0) x1--;			if(x2 >=0) x2--;
 				curridx = i;
 				cx = x1; cy = y1;
 				return true;
@@ -1685,8 +1872,8 @@ Default::Default()
 	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;
+	cdisp = 0L;		min4log = 0.000001;		ss_txt = 0.9;
+	axis_color = 0x0L;	
 	svgAttr = svgScript = currPath = IniFile = 0L;
 	File1 = File2 = File3 = File4 = File5 = File6 = 0L;
 }
@@ -1727,7 +1914,7 @@ Default::GetSize(int select)
 		case 2:		return Line_2.width;
 		default:	return Line_0.width;
 		}
-	case SIZE_TEXT:					RetVal = 4.0;		break;
+	case SIZE_TEXT:					RetVal = _TXH;		break;
 	case SIZE_GRECT_TOP:			RetVal = 0.0;		break;
 	case SIZE_GRECT_BOTTOM:			RetVal = 110.0;		break;
 	case SIZE_GRECT_LEFT:			RetVal = 0.0;		break;
@@ -1744,7 +1931,7 @@ Default::GetSize(int select)
 		default:	return Line_0.patlength;
 		}
 	case SIZE_AXIS_TICKS:			RetVal = 2.0;		break;
-	case SIZE_TICK_LABELS:			RetVal = 4.0;		break;
+	case SIZE_TICK_LABELS:			RetVal = _TXH;		break;
 	case SIZE_WHISKER:
 	case SIZE_ERRBAR:				RetVal = 3.0;		break;
 	case SIZE_WHISKER_LINE:
@@ -1757,16 +1944,16 @@ Default::GetSize(int select)
 		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_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*ss_txt;		break;
 	case SIZE_RRECT_RAD:			
 		return rrect_rad ? *rrect_rad : GetSize(SIZE_SYMBOL)/2.0;
 	default:	return 0.0;
@@ -2184,7 +2371,7 @@ MemCache::GetField()
 UndoObj::UndoObj()
 {
 	buff = buff0 = (UndoInfo**)calloc(UNDO_RING_SIZE, sizeof(UndoInfo*));
-	stub1 = ndisp = 0;
+	stub1 = ndisp = 0;					busy = false;
 	pcb = &stub1;	cdisp = ldisp = 0L;	buffers = 0L;
 }
 
@@ -2313,14 +2500,17 @@ UndoObj::InvalidGO(GraphObj *go)
 }
 
 void
-UndoObj::Pop()
+UndoObj::Pop(anyOutput *o)
 {
 	int idx;
 
-	if(*pcb < 1) return;
-	idx = ((*pcb-1) & UNDO_IDX_MASK);
-	if(buff[idx]) FreeInfo(&buff[idx]);
-	(*pcb)--;
+	if(o) {
+		SetDisp(o);
+		if(*pcb < 1) return;
+		idx = ((*pcb-1) & UNDO_IDX_MASK);
+		if(buff[idx]) FreeInfo(&buff[idx]);
+		(*pcb)--;
+		}
 }
 
 void
@@ -2358,6 +2548,7 @@ UndoObj::Restore(bool redraw, anyOutput*o)
 		return;
 		}
 	(*pcb)--;
+	busy = true;
 	if(buff[idx]) {
 		flags = buff[idx]->flags;
 		switch(buff[idx]->cmd) {
@@ -2370,7 +2561,9 @@ UndoObj::Restore(bool redraw, anyOutput*o)
 					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)->DaO) (((EtBuff*)buff[idx]->data)->DaO)->SetText(((EtBuff*)buff[idx]->data)->row,
+					((EtBuff*)buff[idx]->data)->col, ((EtBuff*)buff[idx]->data)->txt);
+				else ((EditText*)buff[idx]->loc)->SetText(((EtBuff*)buff[idx]->data)->txt);
 				if(((EtBuff*)buff[idx]->data)->txt) free(((EtBuff*)buff[idx]->data)->txt);
 				*(((EtBuff*)buff[idx]->data)->cur) = ((EtBuff*)buff[idx]->data)->vcur;
 				*(((EtBuff*)buff[idx]->data)->m1) = ((EtBuff*)buff[idx]->data)->vm1;
@@ -2418,9 +2611,17 @@ UndoObj::Restore(bool redraw, anyOutput*o)
 		case UNDO_VALDWORD:
 			*((DWORD*)(buff[idx]->loc)) = *((DWORD*)(buff[idx]->data));
 			free(buff[idx]->data);								break;
+		case UNDO_POINT:
+			memcpy(buff[idx]->loc, buff[idx]->data, sizeof(POINT));
+			free(buff[idx]->data);								break;
+		case UNDO_VOIDPTR:
+			*buff[idx]->loc = buff[idx]->data;					break;
 		case UNDO_VALINT:
 			*((int*)(buff[idx]->loc)) = *((int*)(buff[idx]->data));
 			free(buff[idx]->data);								break;
+		case UNDO_VALLONG:
+			*((long*)(buff[idx]->loc)) = *((long*)(buff[idx]->data));
+			free(buff[idx]->data);								break;
 		case UNDO_OBJCONF_1:			//single object restore
 			UpdGOfromMem((GraphObj *)buff[idx]->loc, (unsigned char *)buff[idx]->data);
 			free(buff[idx]->data);								break;
@@ -2437,9 +2638,10 @@ UndoObj::Restore(bool redraw, anyOutput*o)
 			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;
+			if(*(buff[idx]->loc) && buff[idx]->data) 
+				strcpy ((char*)(*buff[idx]->loc), (char*)(buff[idx]->data));
+			if(buff[idx]->data) free(buff[idx]->data);
+			CurrGO = buff[idx]->owner;							break;
 		case UNDO_ROTDEF:
 			memcpy(*(buff[idx]->loc), buff[idx]->data, 6 * sizeof(double));
 			free(buff[idx]->data);								break;
@@ -2493,6 +2695,7 @@ UndoObj::Restore(bool redraw, anyOutput*o)
 	else {
 		InfoBox("The UNDO-cache is empty");
 		}
+	busy = false;
 }
 
 void
@@ -2612,6 +2815,23 @@ UndoObj::ValDword(GraphObj *parent, DWORD *val, DWORD flags)
 	if(0 > NewItem(UNDO_VALDWORD, flags, parent, ptr, (void**)val)) free(ptr);
 }
 
+void 
+UndoObj::Point(GraphObj *parent, POINT *pt, anyOutput * o, DWORD flags)
+{
+	void *ptr;
+
+	if(o) SetDisp(o);
+	if(!(ptr = memdup(pt, sizeof(POINT), 0))) return;
+	if(0 > NewItem(UNDO_POINT, flags, parent, ptr, (void**)pt)) free(ptr);
+}
+
+void 
+UndoObj::VoidPtr(GraphObj *parent, void **pptr, void *ptr, anyOutput * o, DWORD flags)
+{
+	if(o) SetDisp(o);
+	if(0 > NewItem(UNDO_VOIDPTR, flags, parent, ptr, pptr)) free(ptr);
+}
+
 void
 UndoObj::ValInt(GraphObj *parent, int *val, DWORD flags)
 {
@@ -2622,6 +2842,15 @@ UndoObj::ValInt(GraphObj *parent, int *val, DWORD flags)
 }
 
 void
+UndoObj::ValLong(GraphObj *parent, long *val, DWORD flags)
+{
+	void *ptr;
+
+	if(!(ptr = memdup(val, sizeof(long), 0))) return;
+	if(0 > NewItem(UNDO_VALINT, flags, parent, ptr, (void**)val)) free(ptr);
+}
+
+void
 UndoObj::ObjConf(GraphObj *go, DWORD flags)
 {
 	long sz;
@@ -2768,14 +2997,13 @@ UndoObj::DataMem(GraphObj *go, void **mem, int size, long *count, DWORD flags)
 }
 
 void
-UndoObj::DataObject(GraphObj *go, anyOutput *o, DataObj *d, DWORD flags)
+UndoObj::DataObject(GraphObj *go, anyOutput *o, DataObj *d, RECT *rc, 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(!(save = new StrData(d, rc))) return;
 	if(0 > NewItem(UNDO_DATA, flags, go, save, (void**)d)) if(save) delete(save);
 }
 
@@ -2792,12 +3020,8 @@ UndoObj::TextCell(EditText *et, anyOutput *o, char *text, int *cur, int *m1, int
 			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;
-					}
-				}
+			ptr->row = et->row;		ptr->col = et->col;
+			ptr->DaO = (DataObj*)et->parent;
 			if(0 > NewItem(UNDO_ET, flags, 0L, ptr, (void**)et)) {
 				if(ptr->txt)free (ptr->txt);			free(ptr);
 				}
@@ -2874,6 +3098,7 @@ UndoObj::FreeInfo(UndoInfo** inf)
 	case UNDO_MOVE:			case UNDO_RECT:			case UNDO_STRING:
 	case UNDO_ROTDEF:		case UNDO_LINEDEF:		case UNDO_FILLDEF:
 	case UNDO_LFP3D:		case UNDO_FLOAT:		case UNDO_SAVVAR:
+	case UNDO_POINT:		case UNDO_VALLONG:
 		free((*inf)->data);
 		break;
 		}
diff --git a/Utils.cpp b/Utils.cpp
index 33fc5b2..a3bc6da 100755
--- a/Utils.cpp
+++ b/Utils.cpp
@@ -439,6 +439,43 @@ char **split(char *str, char sep, int *nl)
 	return ptr;
 }
 
+char *fit_num_rect(anyOutput *o, int max_width, char *num_str)
+{
+	int i, j, k, w, h, slen;
+	char mant[20], expo[20], fmt[10];
+	double num;
+
+	o->oGetTextExtent(num_str, slen = strlen(num_str), &w, &h);
+	if(w < (max_width-5)) return num_str;
+	//first try to remove leading zero from exponent
+	for(i = 0; i < slen; i++) if(num_str[i] == 'e') break;
+	if(num_str[i] == 'e') while (num_str[i+2] == '0') {
+		for(j = i+2; num_str[j]; j++) num_str[j] = num_str[j+1];
+		o->oGetTextExtent(num_str, slen = strlen(num_str), &w, &h);
+		if(w < (max_width-5)) return num_str;
+		}
+	//no success: reduce the number of digit by rounding
+	for(i = k = 0; i <= slen; i++){
+		if(num_str[i] == '.') k = i;
+		if((mant[i] = num_str[i]) == 'e' || mant[i] == 0) break;
+		}
+	if(num_str[i] =='e') mant[i] = 0;						k = i - k-1;
+	for(j = 0; num_str[i]; j++, i++) expo[j] = num_str[i];		expo[j] = 0;
+	sscanf(mant, "%lf", &num);	
+	if(k >0) do {
+		sprintf(fmt, "%s.%dlf%s", "%", k, expo);
+		sprintf(num_str, fmt, num);							k--;
+		o->oGetTextExtent(num_str, slen = strlen(num_str), &w, &h);
+		if(w < (max_width-5)) return num_str;
+		}while (k >= 0);
+	//cannot fit: return hash marks instead
+	for(i = w = 0; w < (max_width-5); i++) {
+		sprintf(num_str+i, "#");
+		o->oGetTextExtent(num_str, slen = strlen(num_str), &w, &h);
+		}
+	num_str[i-1] = 0;		return num_str;
+}
+
 //----------------------------------------------------------------------------
 // bounding rectangle utilities
 //----------------------------------------------------------------------------
@@ -936,6 +973,8 @@ void DeleteGO(GraphObj *go)
 	case GO_OBJTREE:		delete((ObjTree*)go);		break;
 	case GO_FREQDIST:		delete((FreqDist*)go);		break;
 	case GO_GRID3D:			delete((Grid3D*)go);		break;
+	case GO_FUNC3D:			delete((Func3D*)go);		break;
+	case GO_XYSTAT:			delete((xyStat*)go);		break;
 	default:
 		sprintf(TmpTxt, "Cannot delete Object\nwith Id %ld", go->Id);
 		ErrorBox(TmpTxt);
@@ -944,6 +983,35 @@ void DeleteGO(GraphObj *go)
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//Delete a graphic object from a list 
+bool DeleteGOL(GraphObj ***gol, long n, GraphObj *go, anyOutput *o)
+{
+	long i;
+	int c;
+	GraphObj **g, *p;
+
+	if(!gol || !(*gol) || !go || !n) return false;
+	for (i = 0, c = 0, g = *gol; i < n; i++, g++) {
+		if(*g) {
+			c++;
+			if(*g == go) {
+				p = (*g)->parent;
+				if(o) o->HideMark();
+				Undo.DeleteGO(g, 0L, o);
+				if(c == 1) {
+					for (g++, i++ ;i < n; i++, g++) {
+						if(*g) return true;
+						}
+					if(p) Undo.DropMemory(p, (void**) gol, UNDO_CONTINUE);
+					}
+				return true;
+				}
+			}
+		}
+	return false;
+}
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 //backup file before writing a new one
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 bool BackupFile(char *FileName)
@@ -1015,6 +1083,16 @@ bool FileExist(char *FileName)
 	return true;
 }
 
+
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+//check Object for certain properties
+//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+bool IsPlot3D(GraphObj *g)
+{
+	if(g && (g->Id == GO_PLOT3D || g->Id == GO_FUNC3D)) return true;
+	return false;
+}
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 //duplicate a block of memory
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -1069,6 +1147,7 @@ unsigned int HashValue(unsigned char *str)
 		if(str[i] > 32) ret = ((str[i]-32) + (ret <<2));
 		i++;
 		}while(str[i]);
+	if(i < 4) memcpy(&ret, str, i);
 	return ret;
 }
 
diff --git a/Version.h b/Version.h
index 5289724..aca17f5 100755
--- a/Version.h
+++ b/Version.h
@@ -16,5 +16,4 @@
 //    along with RLPlot; if not, write to the Free Software
 //    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 //
-#define SZ_VERSION  "0.99.14b"
-
+#define SZ_VERSION  "1.0"
diff --git a/WinSpec.cpp b/WinSpec.cpp
index fb917e7..ff11d6e 100755
--- a/WinSpec.cpp
+++ b/WinSpec.cpp
@@ -242,6 +242,19 @@ bool YesNoBox(char *Msg)
 	return false;
 }
 
+int YesNoCancelBox(char *Msg)
+{
+	int res;
+
+	res = MessageBox(0, Msg, "RLPlot", MB_YESNOCANCEL | MB_ICONQUESTION);
+	switch(res) {
+	case IDYES: return 1;
+	case IDNO:	return 0;
+	default:	return 2;
+		}
+	return 0;
+}
+
 void Qt_Box()
 {
 	MessageBox(0, "No Qt installed\nunder Windows", "Error", MB_OK | MB_ICONQUESTION);
@@ -437,6 +450,39 @@ void EmptyClip()
 	CloseClipboard();
 }
 
+void CopyText(char *txt, int len)
+{
+	HGLOBAL hmem;
+	unsigned char* buf;
+
+	if(!txt || !txt[0]) return;
+	if(!len) len = strlen(txt);
+	OpenClipboard(MainWnd);
+	EmptyClipboard();
+	if(hmem = GlobalAlloc(GMEM_MOVEABLE, len+2)) {
+		if(buf = (unsigned char *)GlobalLock(hmem)) {
+			memcpy(buf, txt, len);	buf[len] = 0;
+			GlobalUnlock(hmem);
+			SetClipboardData(CF_TEXT, hmem);
+			}
+		}
+	CloseClipboard();
+}
+
+unsigned char* PasteText()
+{
+	HANDLE hmem = 0;
+	unsigned char *ptr, *ret=0L;
+	
+	OpenClipboard(MainWnd);
+	if((hmem = GetClipboardData(CF_TEXT)) && (ptr = (unsigned char*) GlobalLock(hmem))){
+		ret = (unsigned char*) strdup((char*)ptr);
+		}
+	if(hmem) GlobalUnlock(hmem);
+	CloseClipboard();
+	return ret;
+}
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Get display (desktop) size
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -822,8 +868,7 @@ BitMapWin::oGetTextExtent(char *text, int cb, int *width, int *height)
 		d = (TextExtent.cx * ((7.0 + d)/8.0));
 		TextExtent.cx = iround(d);
 		}
-	*width = TextExtent.cx;
-	*height = TextExtent.cy;
+	*width = TextExtent.cx;					*height = TextExtent.cy;
 	return true;
 }
 
@@ -1708,31 +1753,6 @@ void FindBrowser()
 }
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-// 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,
@@ -1746,6 +1766,14 @@ WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInstance,
 	dlgtxtheight = 16;
 
 	if(lpCmdLine && lpCmdLine[0] && FileExist(lpCmdLine)) LoadFile = strdup(lpCmdLine);
+	else if(lpCmdLine) {								//probably Unicode 
+		sprintf(TmpTxt, "%s", lpCmdLine);
+		if(TmpTxt[0] == '"'){
+			strcpy(TmpTxt, TmpTxt+1);
+			if(TmpTxt[0]) TmpTxt[strlen(TmpTxt)-1] = 0;
+			}
+		if(TmpTxt[0]) LoadFile= strdup(TmpTxt);
+		}
 	hInstance = hInst;
 	wndclass.style = CS_BYTEALIGNWINDOW | CS_DBLCLKS;
 	wndclass.lpfnWndProc = WndProc;
@@ -1848,15 +1876,20 @@ void ScrollEvent(bool bVert, HWND hwnd, UINT type, GraphObj *g, OutputWin *w)
 			pos = si.nPos + LineStep;
 			break;
 		case SB_PAGEUP:
-			pos = (si.nPos - (int)si.nPage) >= si.nMin ?
-				(si.nPos - si.nPage) : si.nMin;
+			if(g->Id == GO_SPREADDATA) {
+				g->Command(CMD_PAGEUP, 0L, w);
+				return;
+				}
+			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);
+			if(g->Id == GO_SPREADDATA) {
+				g->Command(CMD_PAGEDOWN, 0L, w);
+				return;
+				}
+			pos = (si.nPos + (int)si.nPage*2) < si.nMax ? (si.nPos + si.nPage) : (si.nMax - si.nPage+1);
 			break;
-		case SB_THUMBTRACK:
-		case SB_THUMBPOSITION:
+		case SB_THUMBTRACK:		case SB_THUMBPOSITION:
 			pos = si.nTrackPos;
 			break;
 		default:
@@ -1977,20 +2010,17 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 				if(CopyWMF && CopyWMF->StartPage()) {
 					copy_obj->DoPlot(CopyWMF);
 					CopyWMF->EndPage();
+					delete CopyWMF;					CopyWMF = NULL;
 					}
-				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();
+					copy_obj->DoPlot(CopyBMP);			CopyBMP->EndPage();
 					SetClipboardData(CF_BITMAP, CopyBMP->scr);
-					CopyBMP->scr = 0L;
+					CopyBMP->scr = 0L;					CopyBMP->go = 0L;
+					delete CopyBMP;						CopyBMP = NULL;
 					}
-				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:
@@ -2007,11 +2037,12 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 		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);
+			if(g->Command(CMD_CAN_CLOSE, 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);
@@ -2134,12 +2165,6 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 		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;
@@ -2186,7 +2211,7 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 		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:
+		case CM_SHTAB:		case CM_SHPGUP:			case CM_SHPGDOWN:
 			switch(wParam) {
 			case CM_DELKEY:			cc = CMD_DELETE;		break;
 			case CM_LEFTARRKEY:		cc = CMD_CURRLEFT;		break;
@@ -2201,6 +2226,8 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 			case CM_SHDOWN:			cc = CMD_SHIFTDOWN;		break;
 			case CM_TAB:			cc = CMD_TAB;			break;
 			case CM_SHTAB:			cc = CMD_SHTAB;			break;
+			case CM_SHPGUP:			cc = CMD_SHPGUP;		break;
+			case CM_SHPGDOWN:		cc = CMD_SHPGDOWN;		break;
 				}
 			g->Command(cc, (void *)NULL, w);
 			return 0;
@@ -2210,6 +2237,18 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 		case CM_UNDO:
 			g->Command(CMD_UNDO, 0L, w);
 			return 0;
+		case CM_INSROW:
+			g->Command(CMD_INSROW, 0L, w);
+			return 0;
+		case CM_INSCOL:
+			g->Command(CMD_INSCOL, 0L, w);
+			return 0;
+		case CM_DELROW:
+			g->Command(CMD_DELROW, 0L, w);
+			return 0;
+		case CM_DELCOL:
+			g->Command(CMD_DELCOL, 0L, w);
+			return 0;
 		default:
 			sprintf(TmpTxt, "Command 0x%x (%d)\nreceived", wParam & 0xffff,
 				wParam & 0xffff);
@@ -2217,13 +2256,13 @@ long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 		}
 		return 0;
 	case WM_CLOSE:
-		if(g) {
-			g->Command(CMD_CAN_DELETE, 0L, 0L);
+		if(g && g->Command(CMD_CAN_CLOSE, 0L, 0L)) {
 			SetWindowLong(hwnd, 0, 0L);
 			SetWindowLong(hwnd, GWL_USERDATA, 0L);
 			w->go = 0L;
+			DestroyWindow(hwnd);
 			}
-		DestroyWindow(hwnd);
+		else if(!g) DestroyWindow(hwnd);
 		return 0;
 	case WM_DESTROY:
 		if(hwnd == MainWnd)PostQuitMessage(0);
@@ -2276,6 +2315,8 @@ long FAR PASCAL DlgWndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
 		break;
 	case WM_CHAR:
 		if(0x09 == (cc = wParam & 0xff)) break;		//ignore Tab
+		if(cc == 0x03) return d->Command(CMD_COPY, 0L, w);	//^c copy
+		if(cc == 0x16) return d->Command(CMD_PASTE, 0L, w);	//^v paste
 		if(d && w) d->Command(CMD_ADDCHAR, (void *)(& cc), w);
 		break;
 	case WM_LBUTTONDOWN:	case WM_LBUTTONDBLCLK:
@@ -2355,7 +2396,7 @@ void *CreateDlgWnd(char *title, int x, int y, int width, int height, tag_DlgObj
 		x, y, width, height, GetFocus(), NULL, hInstance, NULL);
 	w = new OutputWin(0L, hDlg);
 	w->units = defs.cUnits;
-	if(hDlg && w && w->Erase(0x00c0c0c0L)) {
+	if(hDlg && w && w->Erase(0x00e0e0e0L)) {
 		SetWindowLong(hDlg, GWL_USERDATA, (long)w);
 		SetWindowLong(hDlg, 0, (long)d);
 		ShowWindow(hDlg, SW_SHOW);
@@ -2387,6 +2428,7 @@ void LoopDlgWnd() 	//keep message processing running
 
 void CloseDlgWnd(void *hDlg)
 {
+	HideCopyMark();
 	if(hDlg) SendMessage((HWND)hDlg, WM_CLOSE, 0, 0);
 }
 
diff --git a/WinSpec.h b/WinSpec.h
index f6a6423..af66052 100755
--- a/WinSpec.h
+++ b/WinSpec.h
@@ -57,6 +57,7 @@ public:
 	OutputWin(GraphObj *g, HWND hw);
 	~OutputWin();
 	bool ActualSize(RECT *rc);
+	void Focus(){if(hWnd) SetFocus(hWnd);};
 	void Caption(char *txt);
 	void MouseCursor(int cid, bool force);
 	bool SetScroll(bool isVert, int iMin, int iMax, int iPSize, int iPos);
diff --git a/exprlp.cpp b/exprlp.cpp
index b374c7f..fbccddb 100755
--- a/exprlp.cpp
+++ b/exprlp.cpp
@@ -11,6 +11,7 @@ int file_fmt = FF_UNKNOWN;
 bool bQuiet = false, bSVGtype = false, bDelete = false;
 char *szFile1 = 0L, *szFile2 = 0L;
 int dlgtxtheight = 12;				//stub: not used
+char *name1, *name2;				//the filenames
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // direct messages to console
@@ -29,6 +30,28 @@ bool YesNoBox(char *Msg)
 {
 return false;
 }
+
+int YesNoCancelBox(char *Msg)
+{
+	return 0;
+}
+
+void HideCopyMark()
+{
+}
+
+void ShowCopyMark(anyOutput *out, RECT *mrk, int nRec)
+{
+}
+
+void CopyText(char *txt, int len)
+{
+}
+
+unsigned char* PasteText()
+{
+	return 0L;
+}
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // STUBS: we do not need dialogs
@@ -168,6 +191,11 @@ bool PlotScatt::PropertyDlg()
 {
 	return false;
 }
+
+bool xyStat::PropertyDlg()
+{
+	return false;
+}
 
 bool Regression::PropertyDlg()
 {
@@ -262,6 +290,11 @@ bool Chart25D::PropertyDlg()
 bool Ribbon25D::PropertyDlg()
 {
 	return false;
+}
+
+bool Func3D::PropertyDlg()
+{
+	return false;
 }
 
 bool BubblePlot3D::PropertyDlg()
@@ -314,7 +347,7 @@ bool Page::Configure()
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 char *SaveGraphAsName(char *oldname)
 {
-	return 0L;
+	return name2;
 }
 
 char *OpenGraphName(char *oldname)
@@ -356,7 +389,7 @@ bool DelBitmapClass(anyOutput *w)
 {
 	return false;
 }
-
+
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // create a root object to handle I/O
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -367,7 +400,6 @@ public:
 	bool Command(int cmd, void *tmpl, anyOutput *o);
 
 private:
-	char *name1, *name2;
 	GraphObj *go;
 };
 
@@ -409,6 +441,9 @@ ExpRoot::Command(int cmd, void *tmpl, anyOutput *o)
 				break;
 			case FF_EPS:
 				DoExportEps(go, name2, 0L);
+				break;
+			case FF_RLP:
+				SaveGraphAs(go);
 				break;
 			default:
 				ErrorBox("Unknown file extension or format of destination");
@@ -426,7 +461,7 @@ int Usage()
 {
 	printf("______________________________________________________________\n");
 	printf("\nexprlp: RLPlot export utility, version %s.\n", SZ_VERSION);
-	printf("Copyright (C) 2002-2004 R. Lackner\n");
+	printf("Copyright (C) 2002-2005 R. Lackner\n");
 	printf("This is free software published under the GNU\n");
 	printf("general public licence (GPL).\n");
 	printf("\nUsage: exprlp [options] <input> [options] [<output>]\n");
@@ -479,6 +514,9 @@ int main (int argc, char **argv)
 				case 'e':	case 'E':
 					file_fmt = FF_EPS;
 					break;
+				case 'r':	case 'R':
+					file_fmt = FF_RLP;
+					break;
 				case 'w':	case 'W':
 					file_fmt = FF_WMF;
 					break;
@@ -512,6 +550,7 @@ int main (int argc, char **argv)
 		if(0==strcmp(".svg", szFile2+i-4)) file_fmt = FF_SVG;
 		else if(0==strcmp(".wmf", szFile2+i-4)) file_fmt = FF_WMF;
 		else if(0==strcmp(".eps", szFile2+i-4)) file_fmt = FF_EPS;
+		else if(0==strcmp(".rlp", szFile2+i-4)) file_fmt = FF_RLP;
 		}
 	if(file_fmt == FF_UNKNOWN) {
 		if(szFile1)printf("\n**** Unknown file extension or format ****\n\n");
diff --git a/mfcalc.cpp b/mfcalc.cpp
old mode 100755
new mode 100644
index 8f7f943..9144a14
--- a/mfcalc.cpp
+++ b/mfcalc.cpp
@@ -5,26 +5,38 @@
 #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
+#define	STR	258
+#define	ARR	259
+#define	BLOCK	260
+#define	PI	261
+#define	E	262
+#define	CLVAL	263
+#define	PSEP	264
+#define	IF	265
+#define	ELSE	266
+#define	VAR	267
+#define	FNCT	268
+#define	AFNCT	269
+#define	SFNCT	270
+#define	FUNC2	271
+#define	TXT	272
+#define	CLAUSE	273
+#define	SER	274
+#define	COLR	275
+#define	COLC	276
+#define	AND	277
+#define	OR	278
+#define	EQ	279
+#define	NE	280
+#define	GT	281
+#define	GE	282
+#define	LT	283
+#define	LE	284
+#define	NEG	285
+#define	INC	286
+#define	DEC	287
+#define	PINC	288
+#define	PDEC	289
 
 
 /*
@@ -54,16 +66,27 @@
 #include <stdio.h>
 #include "rlplot.h"
 
-struct symrec {
+class symrec {
+public:
 	int type, row, col;
 	unsigned int h_name;
 	char *name, *text;
-	struct {
-		double var;
-		double (*fnctptr)(...);
-		} value;
-	int arg_type;
-	struct symrec *next;
+	double (*fnctptr)(...);
+	symrec *next;
+	double var;
+
+	symrec(unsigned int h_n, int typ, symrec *nxt);
+	~symrec();
+	double GetValue();
+	void GetValue(void *res);
+	double SetValue(double v);
+        void SetValue(void* dest, void* src);
+	void SetName(char *nam);
+	void InitSS();
+
+private:
+	bool isSSval;
+
 };
 
 // syntactical information
@@ -86,27 +109,32 @@ typedef struct{
 
 }YYSTYPE;
 
-static symrec *putsym (unsigned int h_name, int sym_type, int arg_type);
+static symrec *putsym (unsigned int h_name, int sym_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 double eval(YYSTYPE *sr, YYSTYPE *dst);
+static int range_array(YYSTYPE * res, char *range);
 static void exec_clause(YYSTYPE *res);
 static YYSTYPE *proc_clause(YYSTYPE *res);
 static void yyerror(char *s);
 static int yylex(void);
+static double nop() {return 0.0;};
 
 static char res_txt[1000];
 static anyResult line_res = {ET_UNKNOWN, 0.0, res_txt};
-static double line_result;
 static DataObj *curr_data;
+static char *last_error = 0L;		//error text
+static char *last_err_desc = 0L;	//short error description
 
-//the current command buffer
-static char *buffer = 0L;
+static char *buffer = 0L;		//the current command buffer
 static int buff_pos = 0;
+static bool bRecent = false;		//rearrange functions
+static int parse_level = 0;		//count reentrances into parser
+#define MAX_PARSE 20			//maximum number of reentances 
 #include <stdio.h>
 
 #ifndef __cplusplus
@@ -117,23 +145,23 @@ static int buff_pos = 0;
 
 
 
-#define	YYFINAL		79
+#define	YYFINAL		131
 #define	YYFLAG		-32768
-#define	YYNTBASE	36
+#define	YYNTBASE	48
 
-#define YYTRANSLATE(x) ((unsigned)(x) <= 277 ? yytranslate[x] : 40)
+#define YYTRANSLATE(x) ((unsigned)(x) <= 289 ? yytranslate[x] : 54)
 
 static const char yytranslate[] = {     0,
-     2,     2,     2,     2,     2,     2,     2,     2,     2,    32,
+     2,     2,     2,     2,     2,     2,     2,     2,     2,    44,
      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,    46,
+    47,    36,    35,    20,    34,     2,    37,     2,     2,     2,
+     2,     2,     2,     2,     2,     2,     2,     2,    45,     2,
+    19,     2,    25,     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,    43,     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,
@@ -150,174 +178,245 @@ static const char yytranslate[] = {     0,
      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
+     7,     8,     9,    10,    11,    12,    13,    14,    15,    16,
+    17,    18,    21,    22,    23,    24,    26,    27,    28,    29,
+    30,    31,    32,    33,    38,    39,    40,    41,    42
 };
 
 #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
+     0,     1,     4,     6,     8,    10,    13,    16,    19,    22,
+    25,    28,    30,    34,    38,    42,    44,    48,    50,    52,
+    56,    60,    62,    66,    68,    70,    72,    74,    76,    78,
+    80,    84,    88,    93,    98,   105,   114,   119,   124,   131,
+   140,   146,   154,   158,   162,   166,   170,   174,   178,   182,
+   186,   190,   194,   198,   202,   205,   208,   211,   214,   217,
+   221,   225,   231,   237,   243
 };
 
 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
+    48,    49,     0,    44,     0,    45,     0,    20,     0,    53,
+    44,     0,    53,    45,     0,    53,    20,     0,    50,    44,
+     0,    50,    45,     0,     1,    44,     0,     4,     0,    50,
+    35,    53,     0,    53,    35,    50,     0,    50,    35,    50,
+     0,    50,     0,    13,    23,    13,     0,     5,     0,    53,
+     0,    52,    20,    52,     0,    52,    21,    53,     0,    51,
+     0,     3,    22,     3,     0,     3,     0,    18,     0,     9,
+     0,     7,     0,     8,     0,    13,     0,     6,     0,    13,
+    19,    53,     0,    13,    19,    50,     0,    14,    46,    53,
+    47,     0,    15,    46,    52,    47,     0,    15,    46,    53,
+    10,    53,    47,     0,    15,    46,    53,    10,    53,    10,
+    53,    47,     0,    16,    46,    50,    47,     0,    16,    46,
+    53,    47,     0,    17,    46,    52,    10,    52,    47,     0,
+    17,    46,    52,    10,    52,    10,    51,    47,     0,    11,
+    46,    53,    47,     6,     0,    11,    46,    53,    47,     6,
+    12,     6,     0,    53,    26,    53,     0,    53,    27,    53,
+     0,    53,    28,    53,     0,    53,    29,    53,     0,    53,
+    30,    53,     0,    53,    31,    53,     0,    53,    32,    53,
+     0,    53,    33,    53,     0,    53,    35,    53,     0,    53,
+    34,    53,     0,    53,    36,    53,     0,    53,    37,    53,
+     0,    34,    53,     0,    13,    39,     0,    13,    40,     0,
+    39,    13,     0,    40,    13,     0,    53,    43,    53,     0,
+    46,    52,    47,     0,    53,    25,    53,    24,    53,     0,
+    53,    25,     4,    24,     4,     0,    53,    25,     4,    24,
+    53,     0,    53,    25,    53,    24,     4,     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
+   121,   122,   125,   125,   125,   126,   127,   128,   129,   130,
+   131,   134,   136,   137,   138,   141,   143,   146,   147,   148,
+   149,   150,   151,   155,   156,   157,   158,   159,   160,   161,
+   162,   163,   164,   165,   166,   167,   168,   169,   170,   171,
+   173,   174,   175,   176,   177,   178,   179,   180,   181,   182,
+   183,   184,   185,   186,   188,   189,   190,   191,   192,   193,
+   194,   195,   196,   197,   198
 };
 #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
+static const char * const yytname[] = {   "$","error","$undefined.","NUM","STR",
+"ARR","BLOCK","PI","E","CLVAL","PSEP","IF","ELSE","VAR","FNCT","AFNCT","SFNCT",
+"FUNC2","TXT","'='","','","CLAUSE","SER","COLR","COLC","'?'","AND","OR","EQ",
+"NE","GT","GE","LT","LE","'-'","'+'","'*'","'/'","NEG","INC","DEC","PINC","PDEC",
+"'^'","'\\n'","';'","'('","')'","input","line","str_exp","range","arr","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
+    48,    48,    49,    49,    49,    49,    49,    49,    49,    49,
+    49,    50,    50,    50,    50,    51,    51,    52,    52,    52,
+    52,    52,    52,    53,    53,    53,    53,    53,    53,    53,
+    53,    53,    53,    53,    53,    53,    53,    53,    53,    53,
+    53,    53,    53,    53,    53,    53,    53,    53,    53,    53,
+    53,    53,    53,    53,    53,    53,    53,    53,    53,    53,
+    53,    53,    53,    53,    53
 };
 
 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
+     0,     2,     1,     1,     1,     2,     2,     2,     2,     2,
+     2,     1,     3,     3,     3,     1,     3,     1,     1,     3,
+     3,     1,     3,     1,     1,     1,     1,     1,     1,     1,
+     3,     3,     4,     4,     6,     8,     4,     4,     6,     8,
+     5,     7,     3,     3,     3,     3,     3,     3,     3,     3,
+     3,     3,     3,     3,     2,     2,     2,     2,     2,     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,    24,    12,    30,    27,    28,    26,     0,    29,
+     0,     0,     0,     0,    25,     5,     0,     0,     0,     3,
+     4,     0,     2,     0,     0,    11,     0,     0,    56,    57,
+     0,     0,     0,     0,    55,    58,    59,    24,    18,    29,
+    16,    22,     0,    19,     0,     9,    10,     8,     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
+     0,     0,     6,     7,     0,    32,    31,     0,     0,    19,
+     0,     0,     0,     0,     0,     0,     0,     0,    61,    15,
+    13,     0,     0,    43,    44,    45,    46,    47,    48,    49,
+    50,    52,    14,    51,    53,    54,    60,     0,    33,    34,
+     0,    37,    38,     0,    51,    23,    17,    20,    21,     0,
+     0,    41,     0,     0,    63,    64,    65,    62,     0,     0,
+    35,     0,    39,    42,     0,     0,     0,    36,    40,     0,
+     0
 };
 
 static const short yydefgoto[] = {     1,
-    14,    15,    16
+    23,    41,    42,    43,    44
 };
 
 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
+    88,   -39,-32768,-32768,-32768,-32768,-32768,-32768,   -33,     2,
+   -26,   -24,   -18,   -17,-32768,-32768,   378,    21,    24,-32768,
+-32768,   132,-32768,   -27,   419,-32768,   378,   173,-32768,-32768,
+   378,   132,   173,   132,     3,-32768,-32768,    23,-32768,     0,
+     8,-32768,   -14,   331,   173,-32768,-32768,-32768,   214,   378,
+   378,   378,   378,   378,   378,   378,   378,   378,   173,   378,
+   378,   378,-32768,-32768,   126,     8,   331,   167,   -11,   400,
+   -32,   208,     4,   378,    67,    58,   132,   378,-32768,-32768,
+    32,    52,   291,   456,   456,    29,    29,    29,    29,    29,
+    29,    32,-32768,    32,     3,     3,     3,    72,-32768,-32768,
+   378,-32768,-32768,   132,    32,-32768,-32768,    59,   440,   255,
+   296,    69,   372,    -9,-32768,   440,-32768,   440,    76,   378,
+-32768,   337,-32768,-32768,   249,    36,   331,-32768,-32768,    84,
+-32768
 };
 
 static const short yypgoto[] = {-32768,
--32768,    30,   -11
+-32768,    34,   -37,   -30,    -1
 };
 
 
-#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
+#define	YYLAST		499
+
+
+static const short yytable[] = {    25,
+   122,    69,    45,    73,    26,    77,    78,    45,    77,    78,
+    77,    78,    27,   104,   102,    35,    46,    47,    28,    31,
+    28,    32,    76,    77,    78,    65,    67,    33,    34,    68,
+    70,    72,    79,    36,    24,   100,    37,   123,    29,    30,
+    29,    30,    45,    81,    75,    62,   108,    83,    84,    85,
+    86,    87,    88,    89,    90,    91,    92,    94,    95,    96,
+    97,    66,    58,    74,    60,    61,    71,    60,    61,   106,
+   107,    62,   105,   114,    62,   110,   109,   112,    80,    78,
+   119,   124,   129,   131,   126,     0,     0,   130,     2,     0,
+     3,     4,    93,     5,     6,     7,     8,     0,     9,   113,
+    10,    11,    12,    13,    14,    15,     0,    16,   116,   118,
+     0,     0,     0,     0,     0,     0,     0,     0,   125,     0,
+   127,    17,     0,     0,     0,     0,    18,    19,     0,     0,
+     0,    20,    21,    22,    38,     4,    39,     5,     6,     7,
+     8,     0,     9,     0,    40,    11,    12,    13,    14,    15,
+    49,    50,    51,    52,    53,    54,    55,    56,    57,    58,
+    74,    60,    61,     0,     0,    17,     0,     0,    62,     0,
+    18,    19,    98,     0,     0,     3,     4,    22,     5,     6,
+     7,     8,     0,     9,     0,    10,    11,    12,    13,    14,
+    15,    49,    50,    51,    52,    53,    54,    55,    56,    57,
+    58,    74,    60,    61,     0,     0,    17,     0,     0,    62,
+     0,    18,    19,    99,     0,     0,     3,    82,    22,     5,
+     6,     7,     8,     0,     9,     0,    10,    11,    12,    13,
+    14,    15,    49,    50,    51,    52,    53,    54,    55,    56,
+    57,    58,    59,    60,    61,     0,     0,    17,     0,     0,
+    62,     0,    18,    19,   103,     0,     0,     3,   115,    22,
+     5,     6,     7,     8,     0,     9,     0,    10,    11,    12,
+    13,    14,    15,    49,    50,    51,    52,    53,    54,    55,
+    56,    57,    58,    74,    60,    61,     0,     0,    17,     0,
+     0,    62,     0,    18,    19,   128,     0,     0,     3,   117,
+    22,     5,     6,     7,     8,     0,     9,     0,    10,    11,
+    12,    13,    14,    15,   111,    49,    50,    51,    52,    53,
+    54,    55,    56,    57,    58,    74,    60,    61,     0,    17,
+     0,     0,     0,    62,    18,    19,     0,     0,     0,     3,
+     4,    22,     5,     6,     7,     8,     0,     9,     0,    40,
+    11,    12,    13,    14,    15,    49,    50,    51,    52,    53,
+    54,    55,    56,    57,    58,    59,    60,    61,     0,     0,
+    17,     0,     0,    62,     0,    18,    19,     0,     0,     0,
+     3,   120,    22,     5,     6,     7,     8,     0,     9,     0,
+    10,    11,    12,    13,    14,    15,    49,    50,    51,    52,
+    53,    54,    55,    56,    57,    58,    74,    60,    61,   101,
+     0,    17,     0,     0,    62,     0,    18,    19,   121,     0,
+     0,     0,     0,    22,    49,    50,    51,    52,    53,    54,
+    55,    56,    57,    58,    59,    60,    61,     0,    48,     0,
+     0,     0,    62,    49,    50,    51,    52,    53,    54,    55,
+    56,    57,    58,    59,    60,    61,     0,     0,     0,     0,
+     0,    62,    63,    64,    49,    50,    51,    52,    53,    54,
+    55,    56,    57,    58,    74,    60,    61,     0,     0,     0,
+     0,     0,    62,    52,    53,    54,    55,    56,    57,    58,
+    74,    60,    61,     0,     0,     0,     0,     0,    62
 };
 
-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
+static const short yycheck[] = {     1,
+    10,    32,    35,    34,    44,    20,    21,    35,    20,    21,
+    20,    21,    46,    10,    47,    17,    44,    45,    19,    46,
+    19,    46,    23,    20,    21,    27,    28,    46,    46,    31,
+    32,    33,    47,    13,     1,    47,    13,    47,    39,    40,
+    39,    40,    35,    45,    22,    43,    77,    49,    50,    51,
+    52,    53,    54,    55,    56,    57,    58,    59,    60,    61,
+    62,    28,    34,    35,    36,    37,    33,    36,    37,     3,
+    13,    43,    74,   104,    43,    24,    78,     6,    45,    21,
+    12,     6,    47,     0,   122,    -1,    -1,     0,     1,    -1,
+     3,     4,    59,     6,     7,     8,     9,    -1,    11,   101,
+    13,    14,    15,    16,    17,    18,    -1,    20,   110,   111,
+    -1,    -1,    -1,    -1,    -1,    -1,    -1,    -1,   120,    -1,
+   122,    34,    -1,    -1,    -1,    -1,    39,    40,    -1,    -1,
+    -1,    44,    45,    46,     3,     4,     5,     6,     7,     8,
+     9,    -1,    11,    -1,    13,    14,    15,    16,    17,    18,
+    25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+    35,    36,    37,    -1,    -1,    34,    -1,    -1,    43,    -1,
+    39,    40,    47,    -1,    -1,     3,     4,    46,     6,     7,
+     8,     9,    -1,    11,    -1,    13,    14,    15,    16,    17,
+    18,    25,    26,    27,    28,    29,    30,    31,    32,    33,
+    34,    35,    36,    37,    -1,    -1,    34,    -1,    -1,    43,
+    -1,    39,    40,    47,    -1,    -1,     3,     4,    46,     6,
+     7,     8,     9,    -1,    11,    -1,    13,    14,    15,    16,
+    17,    18,    25,    26,    27,    28,    29,    30,    31,    32,
+    33,    34,    35,    36,    37,    -1,    -1,    34,    -1,    -1,
+    43,    -1,    39,    40,    47,    -1,    -1,     3,     4,    46,
+     6,     7,     8,     9,    -1,    11,    -1,    13,    14,    15,
+    16,    17,    18,    25,    26,    27,    28,    29,    30,    31,
+    32,    33,    34,    35,    36,    37,    -1,    -1,    34,    -1,
+    -1,    43,    -1,    39,    40,    47,    -1,    -1,     3,     4,
+    46,     6,     7,     8,     9,    -1,    11,    -1,    13,    14,
+    15,    16,    17,    18,    24,    25,    26,    27,    28,    29,
+    30,    31,    32,    33,    34,    35,    36,    37,    -1,    34,
+    -1,    -1,    -1,    43,    39,    40,    -1,    -1,    -1,     3,
+     4,    46,     6,     7,     8,     9,    -1,    11,    -1,    13,
+    14,    15,    16,    17,    18,    25,    26,    27,    28,    29,
+    30,    31,    32,    33,    34,    35,    36,    37,    -1,    -1,
+    34,    -1,    -1,    43,    -1,    39,    40,    -1,    -1,    -1,
+     3,    10,    46,     6,     7,     8,     9,    -1,    11,    -1,
+    13,    14,    15,    16,    17,    18,    25,    26,    27,    28,
+    29,    30,    31,    32,    33,    34,    35,    36,    37,    10,
+    -1,    34,    -1,    -1,    43,    -1,    39,    40,    47,    -1,
+    -1,    -1,    -1,    46,    25,    26,    27,    28,    29,    30,
+    31,    32,    33,    34,    35,    36,    37,    -1,    20,    -1,
+    -1,    -1,    43,    25,    26,    27,    28,    29,    30,    31,
+    32,    33,    34,    35,    36,    37,    -1,    -1,    -1,    -1,
+    -1,    43,    44,    45,    25,    26,    27,    28,    29,    30,
+    31,    32,    33,    34,    35,    36,    37,    -1,    -1,    -1,
+    -1,    -1,    43,    28,    29,    30,    31,    32,    33,    34,
+    35,    36,    37,    -1,    -1,    -1,    -1,    -1,    43
 };
 /* -*-C-*-  Note some compilers choke on comments on `#line' lines.  */
 
@@ -862,12 +961,6 @@ yyreduce:
 
   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;}
@@ -875,111 +968,176 @@ case 7:
 {store_res(&yyvsp[-1]); return 0;;
     break;}
 case 8:
-{yyerrok;;
+{store_res(&yyvsp[-1]); return 0;;
     break;}
 case 9:
-{yyval.type=STR;;
+{store_res(&yyvsp[-1]); return 0;;
     break;}
 case 10:
-{yyval.text=add_strings(yyvsp[-2].text, string_value(&yyvsp[0])); yyval.type=STR;;
+{store_res(&yyvsp[-1]); return 0;;
     break;}
 case 11:
-{yyval.text=add_strings(string_value(&yyvsp[-2]), yyvsp[0].text); yyval.type=STR;;
+{yyerrok;;
     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;;
+{yyval.text=add_strings(yyvsp[-2].text, string_value(&yyvsp[0]));;
     break;}
 case 14:
-{yyval.val = 0.0;;
+{yyval.text=add_strings(string_value(&yyvsp[-2]), yyvsp[0].text);;
     break;}
 case 15:
-{yyval.val = syntax_level ? syntax_level->clval : 0.0; ;
+{yyval.text=add_strings(yyvsp[-2].text, yyvsp[0].text);;
     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;
+{if(yyval.text =(char*)malloc(strlen(yyvsp[-2].tptr->name) +strlen(yyvsp[0].tptr->name) + 2)) sprintf(yyval.text, "%s:%s", yyvsp[-2].tptr->name, yyvsp[0].tptr->name);;
     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);;
+{if(!yyval.a_data) {yyval.a_data = (double*)malloc(sizeof(double)); yyval.a_count = 1; yyval.a_data[0] = yyval.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);;
+{push(&yyval, &yyvsp[0]);;
     break;}
 case 21:
-{yyval.val = ((yyvsp[-2].val != 0) && (yyvsp[0].val != 0))? 1 : 0;;
+{exec_clause(&yyval);;
     break;}
 case 22:
-{yyval.val = ((yyvsp[-2].val != 0) || (yyvsp[0].val != 0))? 1 : 0;;
+{range_array(&yyval, yyvsp[0].text);;
     break;}
 case 23:
-{yyval.val = (yyvsp[-2].val == yyvsp[0].val) ? 1 : 0;;
+{if(yyvsp[-2].val < yyvsp[0].val && (yyval.a_data = (double*)malloc((int)(yyvsp[0].val-yyvsp[-2].val+2)*sizeof(double))))
+					for(yyval.a_count=0; yyvsp[-2].val<=yyvsp[0].val; yyval.a_data[yyval.a_count++] = yyvsp[-2].val, yyvsp[-2].val += 1.0 );;
     break;}
 case 24:
-{yyval.val = (yyvsp[-2].val != yyvsp[0].val) ? 1 : 0;;
+{yyval.val = yyvsp[0].val; yyval.type = NUM;;
     break;}
 case 25:
-{yyval.val = (yyvsp[-2].val > yyvsp[0].val) ? 1 : 0;;
+{yyval.val = 0.0;;
     break;}
 case 26:
-{yyval.val = (yyvsp[-2].val >= yyvsp[0].val) ? 1 : 0;;
+{yyval.val = syntax_level ? syntax_level->clval : 0.0; ;
     break;}
 case 27:
-{yyval.val = (yyvsp[-2].val < yyvsp[0].val) ? 1 : 0;;
+{yyval.val = _PI; yyval.type = NUM;;
     break;}
 case 28:
-{yyval.val = (yyvsp[-2].val <= yyvsp[0].val) ? 1 : 0;;
+{yyval.val = 2.71828182845905; yyval.type = NUM;;
     break;}
 case 29:
-{yyval.val = yyvsp[-2].val + yyvsp[0].val;;
+{yyvsp[0].tptr->GetValue(&yyval);;
     break;}
 case 30:
-{yyval.val = yyvsp[-2].val - yyvsp[0].val;;
+{eval(&yyvsp[0], &yyval);;
     break;}
 case 31:
-{yyval.val = yyvsp[-2].val * yyvsp[0].val;;
+{yyvsp[-2].tptr->SetValue(&yyval, &yyvsp[0]);;
     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; ;
+{yyvsp[-2].tptr->SetValue(&yyval, &yyvsp[0]);;
     break;}
 case 33:
-{push(&yyval, &yyvsp[0]);;
+{yyval.val = ((yyvsp[-3].tptr->fnctptr)(yyvsp[-1].val));;
     break;}
 case 34:
-{get_range(&yyval, yyvsp[-2].tptr->name, yyvsp[0].tptr->name);;
+{yyval.val = ((yyvsp[-3].tptr->fnctptr)(proc_clause(&yyvsp[-1])));;
     break;}
 case 35:
-{yyval.val = -yyvsp[0].val;;
+{push(&yyvsp[-2], &yyvsp[0]); yyval.val = ((yyvsp[-5].tptr->fnctptr)(&yyvsp[-2]));;
     break;}
 case 36:
-{yyval.val = pow(yyvsp[-2].val, yyvsp[0].val);;
+{push(&yyvsp[-4], &yyvsp[-2]); push(&yyvsp[-4], &yyvsp[0]); yyval.val = ((yyvsp[-7].tptr->fnctptr)(&yyvsp[-4]));;
     break;}
 case 37:
-{yyval.val = yyvsp[-2].val; exec_clause(&yyval);;
+{yyval.val = ((yyvsp[-3].tptr->fnctptr)(&yyvsp[-1], &yyval));;
     break;}
 case 38:
-{memcpy(&yyval, &yyvsp[-1], sizeof(YYSTYPE));
+{yyvsp[-1].text = string_value(&yyvsp[-1]); yyval.val = ((yyvsp[-3].tptr->fnctptr)(&yyvsp[-1], &yyval));;
     break;}
 case 39:
-{memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
+{yyval.val = ((*yyvsp[-5].tptr->fnctptr)(proc_clause(&yyvsp[-3]), proc_clause(&yyvsp[-1]), 0L));;
     break;}
 case 40:
-{memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
+{yyval.val = ((*yyvsp[-7].tptr->fnctptr)(proc_clause(&yyvsp[-5]), proc_clause(&yyvsp[-3]), yyvsp[-1].text));;
     break;}
 case 41:
-{memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
+{yyval.val = yyvsp[-2].val != 0 ? eval(&yyvsp[0], &yyval) : 0.0;;
     break;}
 case 42:
+{yyval.val = yyvsp[-4].val != 0 ? eval(&yyvsp[-2], &yyval) : eval(&yyvsp[0], &yyval);;
+    break;}
+case 43:
+{yyval.val = ((yyvsp[-2].val != 0) && (yyvsp[0].val != 0))? 1 : 0;;
+    break;}
+case 44:
+{yyval.val = ((yyvsp[-2].val != 0) || (yyvsp[0].val != 0))? 1 : 0;;
+    break;}
+case 45:
+{yyval.val = (yyvsp[-2].val == yyvsp[0].val) ? 1 : 0;;
+    break;}
+case 46:
+{yyval.val = (yyvsp[-2].val != yyvsp[0].val) ? 1 : 0;;
+    break;}
+case 47:
+{yyval.val = (yyvsp[-2].val > yyvsp[0].val) ? 1 : 0;;
+    break;}
+case 48:
+{yyval.val = (yyvsp[-2].val >= yyvsp[0].val) ? 1 : 0;;
+    break;}
+case 49:
+{yyval.val = (yyvsp[-2].val < yyvsp[0].val) ? 1 : 0;;
+    break;}
+case 50:
+{yyval.val = (yyvsp[-2].val <= yyvsp[0].val) ? 1 : 0;;
+    break;}
+case 51:
+{yyval.val = yyvsp[-2].val + yyvsp[0].val;;
+    break;}
+case 52:
+{yyval.val = yyvsp[-2].val - yyvsp[0].val;;
+    break;}
+case 53:
+{yyval.val = yyvsp[-2].val * yyvsp[0].val;;
+    break;}
+case 54:
+{if(yyvsp[0].val != 0.0) yyval.val = yyvsp[-2].val / yyvsp[0].val;
+					else yyval.val = (getsym(HashValue((unsigned char*)"zdiv")))->GetValue(); ;
+    break;}
+case 55:
+{yyval.val = -yyvsp[0].val;;
+    break;}
+case 56:
+{yyval.val=yyvsp[-1].tptr->GetValue(); yyvsp[-1].tptr->SetValue(yyval.val+1.0); yyval.val -= 1.0; yyval.type = NUM;;
+    break;}
+case 57:
+{yyval.val=yyvsp[-1].tptr->GetValue(); yyvsp[-1].tptr->SetValue(yyval.val-1.0); yyval.val += 1.0; yyval.type = NUM;;
+    break;}
+case 58:
+{yyval.val=yyvsp[0].tptr->GetValue(); yyvsp[0].tptr->SetValue(yyval.val+1.0); yyval.type = NUM;;
+    break;}
+case 59:
+{yyval.val=yyvsp[0].tptr->GetValue(); yyvsp[0].tptr->SetValue(yyval.val-1.0); yyval.type = NUM;;
+    break;}
+case 60:
+{yyval.val = pow(yyvsp[-2].val, yyvsp[0].val);;
+    break;}
+case 61:
+{memcpy(&yyval, &yyvsp[-1], sizeof(YYSTYPE)); yyvsp[-1].a_data = 0L; yyvsp[-1].a_count = 0;;
+    break;}
+case 62:
+{memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
+    break;}
+case 63:
+{memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
+    break;}
+case 64:
+{memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
+    break;}
+case 65:
 {memcpy(&yyval, yyvsp[-4].val != 0.0 ? &yyvsp[-2] : &yyvsp[0], sizeof(YYSTYPE));
     break;}
 }
@@ -1206,36 +1364,185 @@ yyerrhandle:
 }
 
 
-static char *last_error = 0L;
+// The symrec class
+symrec::symrec(unsigned int h_n, int typ, symrec *nxt) 
+{
+	h_name = h_n;
+	type = typ;
+	next = nxt;
+	row = col = -1;
+	name = text = 0L;
+	var = 0.0;
+	isSSval = false;
+	fnctptr = (double (*)(...))nop;
+}
+
+symrec::~symrec()
+{
+	if(name) free(name);	name = 0L;
+	if(text) free(text);	text = 0L;
+}
+
+double
+symrec::GetValue()
+{
+	anyResult ares;
+
+	if(isSSval) {
+		if(row < 0 && col < 0) InitSS();
+		//GetResult( , , ,true) inhibits reentrance into parser !
+		if(curr_data->GetResult(&ares, row, col, parse_level > MAX_PARSE)){
+			return var = ares.value;
+			}
+		isSSval = false;
+		row = col = -1;
+		}
+	return var;
+}
+
+void
+symrec::GetValue(void *re)
+{
+	anyResult ares;
+	YYSTYPE *res = (YYSTYPE*)re;
+
+	if(isSSval) {
+		if(row < 0 && col < 0) InitSS();
+		res->a_data = 0L;	res->a_count = 0;
+		//GetResult( , , ,true) inhibits reentrance into parser !
+		if(curr_data->GetResult(&ares, row, col, parse_level > MAX_PARSE)){
+			if(text) free(text);		text = 0L;
+			if(ares.type == ET_VALUE) {
+				res->type = VAR;	res->val = ares.value;
+				res->text = 0L;
+				}
+			else if(ares.type == ET_TEXT && ares.text) {
+				res->type = STR;	res->val = 0.0;
+				text = strdup(ares.text);
+				res->text = text;
+				}
+			else {
+				res->type = NUM;	res->val = var;
+				res->text = 0L;
+				}
+			var = res->val;
+			return;
+			}
+		isSSval = false;
+		row = col = -1;
+		}
+	if(text && text[0]) {
+		res->text = strdup(text);
+		res->val = var;		res->type = STR;
+		}
+	else {
+		res->type = NUM;	res->val = var;
+		res->text = 0L;
+		}
+	res->a_data = 0L;	res->a_count = 0L;
+}
+
+double 
+symrec::SetValue(double v)
+{
+	if(isSSval) {
+		if(row < 0 && col < 0) InitSS();
+		if(curr_data->SetValue(row, col, v)){
+			if(curr_data) curr_data->Command(CMD_UPDATE, 0L, 0L);
+			return var = v;
+			}
+		isSSval = false;
+		row = col = -1;
+		}
+	return var = v;
+}
+
+void 
+symrec::SetValue(void* d, void* s)
+{
+	YYSTYPE *dest = (YYSTYPE*)d;
+	YYSTYPE *src = (YYSTYPE*)s;
+
+	if(isSSval && curr_data) {
+		if(row < 0 && col < 0) InitSS();
+		if(last_err_desc) curr_data->SetText(row, col, last_err_desc);
+		else if(src->type == STR) curr_data->SetText(row, col, src->text);
+		else if(src->type == ARR || (src->a_data)) curr_data->SetText(row, col, "#ARRAY");
+		else if(src->type == VAR && src->tptr->type == TXT) curr_data->SetText(row, col, src->tptr->text);
+		else curr_data->SetValue(row, col, src->val);
+		curr_data->Command(CMD_UPDATE, 0L, 0L);
+		}
+	var = src->val;
+	if(text) free(text);		text = 0L;
+	if(src->text && src->text[0]) 	text = strdup(src->text);
+	GetValue(d);
+	return;
+}
+
+void
+symrec::SetName(char *nam)
+{
+	if(name || !nam || !nam[0]) return;
+	name = strdup(nam);
+	if((name && curr_data) && (isalpha(name[0]) || name[0] == '$') && isdigit(name[strlen(name)-1])) isSSval=true;
+}
+
+void
+symrec::InitSS()
+{
+	AccRange *ar;
+
+	if(row<0 && col<0 &&(ar = new AccRange(name))) {
+		ar->GetFirst(&col, &row);
+		delete(ar);
+		}
+}
+
 static void yyerror(char *s)
 {  
-	//Called by yyparse on error
+	//called by yyparse on error
 	if(curr_data) curr_data->Command(CMD_ERROR, last_error = s, 0L);
 	else printf("%s\n", s);
 }
 
+static void yyargserr(char *s)
+{
+	//call from function on argument type/number mismatch
+	yyerror(s);
+	last_err_desc = "#ARGS";
+}
+
 static void store_res(YYSTYPE *res)
 {
-	if(res->type == STR) {
-		line_result = 0.0;
+	if(last_err_desc) {
+		line_res.type = ET_TEXT;
+		line_res.value = 0.0;
+		strcpy(res_txt, last_err_desc);
+		}
+	else if(res->type == STR) {
 		line_res.type = ET_TEXT;
-		line_res. value = 0.0;
+		line_res.value = 0.0;
 		if(res->text) strcpy(res_txt, res->text);
 		}
+	else if((res->type == ARR || (res->a_data)) && res->a_count == 1) {
+		line_res.type = ET_VALUE;
+		line_res.value = res->a_data[0];
+		}
+	else if(res->type == ARR && !(res->a_data) && !(res->a_count)) {
+		line_res.type = ET_VALUE;
+		line_res.value = res->val;
+		}
 	else if(res->type == ARR || (res->a_data)) {
-		line_result = 0.0;
 		line_res.type = ET_TEXT;
-		line_res. value = 0.0;
+		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;
 		}
@@ -1297,6 +1604,35 @@ static void pop_syntax()
 		}
 }
 
+static double eval(YYSTYPE *sr, YYSTYPE *dst) 
+{
+	anyResult *ar;
+
+	if(!sr || !sr->text) return 0.0;
+	parse_level++;
+	ar = do_formula(0L, sr->text);
+	yylval.a_data = 0L;	yylval.a_count = 0;
+	switch(ar->type) {
+	case ET_VALUE:
+		dst->type = NUM;
+		dst->val = ar->value;
+		dst->text = 0L;
+		break;
+	case ET_TEXT:
+		dst->type = STR;
+		dst->val = 0.0;
+		dst->text = PushString(ar->text);
+		break;
+	default:
+		dst->type = NUM;
+		dst->val = 0.0;
+		dst->text = 0L;
+		break;
+		}
+	parse_level--;
+	return dst->val;
+}
+
 // more functions
 static double sign(double v)
 {
@@ -1305,6 +1641,19 @@ static double sign(double v)
 	return 0.0;
 }
 
+static long idum=0;
+
+static double rand(double v)
+{
+	return ran2(&idum);
+}
+
+static double srand(double v)
+{
+	idum = (long)v;
+	return v;
+}
+
 static double factorial(double v)
 {
 	return factrl((int)v);
@@ -1312,9 +1661,14 @@ static double factorial(double v)
 
 static void close_arr_func(YYSTYPE *sr)
 {
-	free(sr->a_data);
+	if(sr->a_data) free(sr->a_data);
 	sr->a_data = 0L;		sr->a_count = 0;
-	if(sr->type == ARR) sr->type = NUM;
+}
+
+static double _strlen(YYSTYPE *sr, YYSTYPE *dst)
+{
+	if(!sr || !sr->text) return 0.0;
+	return (double)strlen(sr->text);
 }
 
 #undef min
@@ -1322,7 +1676,7 @@ static double min(YYSTYPE *sr)
 {
 	int i;
 
-	if(!sr) return 0.0;
+	if(!sr || !sr->a_count) 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];
@@ -1336,7 +1690,7 @@ static double max(YYSTYPE *sr)
 {
 	int i;
 
-	if(!sr) return 0.0;
+	if(!sr || !sr->a_count) 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];
@@ -1362,9 +1716,10 @@ static double sum(YYSTYPE *sr)
 
 	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];
+		for(i = 0, sr->val = 0.0; i < sr->a_count; i++) sr->val += sr->a_data[i];
 		close_arr_func(sr);
 		}
+	else sr->val = 0.0;
 	return sr->val;
 }
 
@@ -1470,14 +1825,159 @@ static double sterr(YYSTYPE *sr)
 	return sr->val;
 }
 
+static double beta(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 2){
+		sr->val = betaf(sr->a_data[0], sr->a_data[1]);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  beta(u, v).");
+	return sr->val;
+}
+
+static double _gammp(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 2){
+		sr->val = gammp(sr->a_data[0], sr->a_data[1]);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  gammp(a, x).");
+	return sr->val;
+}
+
+static double _gammq(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 2){
+		sr->val = gammq(sr->a_data[0], sr->a_data[1]);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  gammq(a, x).");
+	return sr->val;
+}
+
+static double _betai(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 3){
+		sr->val = betai(sr->a_data[0], sr->a_data[1], sr->a_data[2]);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  betai(x, a, b).");
+	return sr->val;
+}
+
+static double _bincof(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 2){
+		sr->val = bincof(sr->a_data[0], sr->a_data[1]);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  bincof(n, k).");
+	return sr->val;
+}
+
+static double binomdist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 3){
+		sr->val = binomdistf(sr->a_data[0], sr->a_data[1], sr->a_data[2]);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  binomdist(s, n, p).");
+	return sr->val;
+}
+
+static double normdist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 3){
+		sr->val = norm_dist(sr->a_data[0], sr->a_data[1], sr->a_data[2]);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  normdist(x, mean, SD).");
+	return sr->val;
+}
+
+static double norminv(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 3) {
+		sr->val = distinv(norm_dist,sr->a_data[1], sr->a_data[2], sr->a_data[0], 2.0);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  norminv(p, mean, SD).");
+	return sr->val;
+}
+
+static double chidist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 2){
+		sr->val = chi_dist(sr->a_data[0], sr->a_data[1], 1.0);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  chidist(x, df).");
+	return sr->val;
+}
+
+static double chiinv(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 2) {
+		sr->val = distinv(chi_dist,sr->a_data[1], 1.0, sr->a_data[0], 2.0);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  chiinv(p, df).");
+	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]);
+		sr->val = t_dist(sr->a_data[0], sr->a_data[1], 1.0);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  tdist(x, df).");
+	return sr->val;
+}
+
+static double tinv(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 2) {
+		sr->val = distinv(t_dist,sr->a_data[1], 1.0, sr->a_data[0], 2.0);
 		close_arr_func(sr);
 		}
+	else yyargserr("Wrong number of arguments\nin call to  tinv(p, df).");
+	return sr->val;
+}
+
+static double poisdist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 2){
+		sr->val = pois_dist(sr->a_data[0], sr->a_data[1], 1.0);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  poisdist(x, mean).");
 	return sr->val;
 }
 
@@ -1489,51 +1989,150 @@ static double fdist(YYSTYPE *sr)
 		sr->val = f_dist(sr->a_data[0], sr->a_data[1], sr->a_data[2]);
 		close_arr_func(sr);
 		}
+	else yyargserr("Wrong number of arguments\nin call to  fdist(x, df1, df2).");
 	return sr->val;
 }
 
+static double finv(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0;
+	if(sr->a_data && sr->a_count == 3){
+		sr->val = distinv(f_dist,sr->a_data[1], sr->a_data[2], sr->a_data[0], 2.0);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  finv(p, df1, df2).");
+	return sr->val;
+}
+
+static double pearson(YYSTYPE *sr1, YYSTYPE *sr2, char *dest)
+{
+	if(!sr1 || !sr2) return 0.0;
+	sr1->val = 0.0;
+        if(sr1->a_data && sr1->a_count > 1 && sr2->a_data && sr1->a_count == sr2->a_count){
+		sr1->val = sr2->val = d_pearson(sr1->a_data, sr2->a_data, sr1->a_count, dest, curr_data);
+		close_arr_func(sr1);		close_arr_func(sr2);
+		}
+	else yyargserr("Bad arguments in call to function\npearson(range1; range2 [;\"dest\"]).");
+	return sr1->val;
+}
+
+static double spearman(YYSTYPE *sr1, YYSTYPE *sr2, char *dest)
+{
+	if(!sr1 || !sr2) return 0.0;
+	sr1->val = 0.0;
+        if(sr1->a_data && sr1->a_count > 1 && sr2->a_data && sr1->a_count == sr2->a_count){
+		sr1->val = sr2->val = d_spearman(sr1->a_data, sr2->a_data, sr1->a_count, dest, curr_data);
+		close_arr_func(sr1);		close_arr_func(sr2);
+		}
+	else yyargserr("Bad arguments in call to function\nspearman(range1; range2 [;\"dest\"]).");
+	return sr1->val;
+}
+
+static double regression(YYSTYPE *sr1, YYSTYPE *sr2, char *dest)
+{
+	if(!sr1 || !sr2) return 0.0;
+	sr1->val = 0.0;
+	if(!(dest)) yyargserr("No destination range in call to function\nregression(range1; range2; \"dest\").");
+        if(sr1->a_data && sr1->a_count > 1 && sr2->a_data && sr1->a_count == sr2->a_count){
+		sr1->val = sr2->val = d_regression(sr1->a_data, sr2->a_data, sr1->a_count, dest, curr_data);
+		close_arr_func(sr1);		close_arr_func(sr2);
+		}
+	else yyargserr("Bad arguments in call to function\nregression(range1; range2; \"dest\").");
+	return sr1->val;
+}
+
+static double ttest(YYSTYPE *sr1, YYSTYPE *sr2, char *dest)
+{
+	if(!sr1 || !sr2) return 0.0;
+	sr1->val = 0.0;
+        if(sr1->a_data && sr1->a_count > 1 && sr2->a_data && sr2->a_count > 1){
+		sr1->val = sr2->val = d_ttest(sr1->a_data, sr2->a_data, sr1->a_count, sr2->a_count, dest, curr_data);
+		close_arr_func(sr1);		close_arr_func(sr2);
+		}
+	else yyargserr("Bad arguments in call to function\nttest(range1; range2[;\"dest\"]).");
+	return sr1->val;
+}
+
+static double ftest(YYSTYPE *sr1, YYSTYPE *sr2, char *dest)
+{
+	if(!sr1 || !sr2) return 0.0;
+	sr1->val = 0.0;
+        if(sr1->a_data && sr1->a_count > 1 && sr2->a_data && sr1->a_count > 1){
+		sr1->val = sr2->val = d_ftest(sr1->a_data, sr2->a_data, sr1->a_count, sr2->a_count, dest, curr_data);
+		close_arr_func(sr1);		close_arr_func(sr2);
+		}
+	else yyargserr("Bad arguments in call to function\nftest(range1; range2[;\"dest\"]).");
+	return sr1->val;
+}
+
 struct init
 {
+	int f_type;
 	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},
+	{FUNC2, HashValue((unsigned char*)"pearson"), (double(*)(double))&pearson},
+	{FUNC2, HashValue((unsigned char*)"spearman"), (double(*)(double))&spearman},
+	{FUNC2, HashValue((unsigned char*)"regression"), (double(*)(double))&regression},
+	{FUNC2, HashValue((unsigned char*)"ttest"), (double(*)(double))&ttest},
+	{FUNC2, HashValue((unsigned char*)"ftest"), (double(*)(double))&ftest},
+	{AFNCT, HashValue((unsigned char*)"variance"), (double(*)(double))&variance},
+	{AFNCT, HashValue((unsigned char*)"stdev"), (double(*)(double))&stdev},
+	{AFNCT, HashValue((unsigned char*)"sterr"), (double(*)(double))&sterr},
+	{AFNCT, HashValue((unsigned char*)"min"), (double(*)(double))&min},
+	{AFNCT, HashValue((unsigned char*)"max"), (double(*)(double))&max},
+	{AFNCT, HashValue((unsigned char*)"count"), (double(*)(double))&count},
+	{AFNCT, HashValue((unsigned char*)"sum"), (double(*)(double))&sum},
+	{AFNCT, HashValue((unsigned char*)"mean"), (double(*)(double))&mean},
+	{AFNCT, HashValue((unsigned char*)"median"), (double(*)(double))&quartile2},
+	{AFNCT, HashValue((unsigned char*)"quartile1"), (double(*)(double))&quartile1},
+	{AFNCT, HashValue((unsigned char*)"quartile2"), (double(*)(double))&quartile2},
+	{AFNCT, HashValue((unsigned char*)"quartile3"), (double(*)(double))&quartile3},
+	{AFNCT, HashValue((unsigned char*)"gmean"), (double(*)(double))&gmean},
+	{AFNCT, HashValue((unsigned char*)"hmean"), (double(*)(double))&hmean},
+	{AFNCT, HashValue((unsigned char*)"tdist"), (double(*)(double))&tdist},
+	{AFNCT, HashValue((unsigned char*)"tinv"), (double(*)(double))&tinv},
+	{AFNCT, HashValue((unsigned char*)"poisdist"), (double(*)(double))&poisdist},
+	{AFNCT, HashValue((unsigned char*)"fdist"), (double(*)(double))&fdist},
+	{AFNCT, HashValue((unsigned char*)"finv"), (double(*)(double))&finv},
+	{AFNCT, HashValue((unsigned char*)"gammp"), (double(*)(double))&_gammp},
+	{AFNCT, HashValue((unsigned char*)"gammq"), (double(*)(double))&_gammq},
+	{AFNCT, HashValue((unsigned char*)"beta"), (double(*)(double))&beta},
+	{AFNCT, HashValue((unsigned char*)"betai"), (double(*)(double))&_betai},
+	{AFNCT, HashValue((unsigned char*)"bincof"), (double(*)(double))&_bincof},
+	{AFNCT, HashValue((unsigned char*)"binomdist"), (double(*)(double))&binomdist},
+	{AFNCT, HashValue((unsigned char*)"normdist"), (double(*)(double))&normdist},
+	{AFNCT, HashValue((unsigned char*)"norminv"), (double(*)(double))&norminv},
+	{AFNCT, HashValue((unsigned char*)"chidist"), (double(*)(double))&chidist},
+	{AFNCT, HashValue((unsigned char*)"chiinv"), (double(*)(double))&chiinv},
+	{SFNCT, HashValue((unsigned char*)"strlen"), (double(*)(double))&_strlen},
+	{SFNCT, HashValue((unsigned char*)"eval"), (double(*)(double))&eval},
+	{FNCT, HashValue((unsigned char*)"erf"), errf},
+	{FNCT, HashValue((unsigned char*)"erfc"), errfc},
+	{FNCT, HashValue((unsigned char*)"sign"), sign},
+	{FNCT, HashValue((unsigned char*)"gammaln"), gammln},
+	{FNCT, HashValue((unsigned char*)"factorial"), factorial},
+	{FNCT, HashValue((unsigned char*)"rand"), rand},
+	{FNCT, HashValue((unsigned char*)"srand"), srand},
+	{FNCT, HashValue((unsigned char*)"floor"), floor},
+	{FNCT, HashValue((unsigned char*)"abs"), fabs},
+	{FNCT, HashValue((unsigned char*)"asin"), asin},
+	{FNCT, HashValue((unsigned char*)"acos"), acos},
+	{FNCT, HashValue((unsigned char*)"atan"), atan},
+	{FNCT, HashValue((unsigned char*)"sinh"), sinh},
+	{FNCT, HashValue((unsigned char*)"cosh"), cosh},
+	{FNCT, HashValue((unsigned char*)"tanh"), tanh},
+	{FNCT, HashValue((unsigned char*)"sin"),  sin},
+	{FNCT, HashValue((unsigned char*)"cos"),  cos},
+	{FNCT, HashValue((unsigned char*)"atan"), atan},
+	{FNCT, HashValue((unsigned char*)"log10"), log10},
+	{FNCT, HashValue((unsigned char*)"ln"),   log},
+	{FNCT, HashValue((unsigned char*)"log"),   log},
+	{FNCT, HashValue((unsigned char*)"exp"),  exp},
+	{FNCT, HashValue((unsigned char*)"sqrt"), sqrt},
 	{0, 0, 0}};
 
 // Store strings in a list
@@ -1551,19 +2150,68 @@ static char *PushString(char *text)
 }
 
 //The symbol table: a chain of `struct symrec'
-static symrec *sym_table = (symrec *) 0;
+static symrec *sym_table, *sym_tab_first;
+
+//Rearrange function table with previously used functions in front
+void ArrangeFunctions()
+{
+	symrec *ptr, *ptr1, *ptr2, *next;
+
+	for(ptr = sym_table, ptr1 = ptr2 = 0L; (ptr); ) {
+		next = ptr->next;
+		if(ptr->name) {
+			ptr->next = ptr1;
+			ptr1 = ptr;
+			}
+		else {
+			ptr->next = ptr2;
+			ptr2 = ptr;
+			}
+		ptr = next;
+		}
+	for(sym_table = 0L, ptr = ptr2; (ptr); ){
+		next = ptr->next;
+		ptr->next = sym_table;
+		sym_table = ptr;
+		ptr = next;
+		}
+	for(ptr = ptr1; (ptr); ){
+		next = ptr->next;
+		ptr->next = sym_table;
+		sym_table = ptr;
+		ptr = next;
+		}
+	sym_tab_first = sym_table;
+	bRecent = false;
+}
 
 // Put arithmetic functions and predifened variables in table
-static void init_table (void)
+void InitArithFuncs(DataObj *d)
 {
 	int i;
-	symrec *ptr;
+	symrec *ptr, *next;
 
+	if(d) curr_data = d;
+	if(sym_table) {
+		for (ptr = sym_table; ptr != (symrec *) 0;){
+			if(ptr) {
+				next = ptr->next;
+				delete (ptr);
+				}
+			ptr = next;
+			}
+		sym_table = sym_tab_first = (symrec *) 0;
+		}
 	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 (arith_fncts[i].h_name, arith_fncts[i].f_type);
+		ptr->fnctptr = (double (*)(...))arith_fncts[i].fnct;
 		}
-	ptr = putsym(HashValue((unsigned char*)"zdiv"), VAR, 0);	ptr->value.var = 1.0;
+	ptr = putsym(HashValue((unsigned char*)"zdiv"), VAR);		ptr->SetValue(1.0);
+	sym_tab_first = sym_table;
+}
+
+static void init_table (void)
+{
 	str_list = 0L;		n_str = 0;
 	push_syntax();
 }
@@ -1571,18 +2219,7 @@ static void init_table (void)
 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;
@@ -1591,58 +2228,30 @@ static void clear_table()
 }
 
 static symrec *
-putsym (unsigned int h_name, int sym_type, int arg_type)
+putsym (unsigned int h_name, int sym_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;
+	sym_table = new symrec(h_name, sym_type, sym_table);
+	return sym_table;
 }
 
 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)
+	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;
-					}
+			if(sym_name && !ptr->name) {
+				ptr->SetName(sym_name);
+				bRecent = true;
 				}
-			ptr->row = row;	ptr->col = col;
-			delete(ar);
 			return ptr;
 			}
+		//predefined variables never end on a digit
+		else if(ptr == sym_tab_first) {
+			if(sym_name && isdigit(sym_name[strlen(sym_name)-1])) return 0;
+			}
 		}
 	return 0;
 }
@@ -1650,7 +2259,7 @@ getsym (unsigned int h_name, char *sym_name)
 static int
 push(YYSTYPE *res, YYSTYPE *val)
 {
-	if(val->a_data && val->a_count) {
+	if(val->a_data) {
 		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;
@@ -1681,36 +2290,36 @@ push(YYSTYPE *res, YYSTYPE *val)
 }
 
 static int
-get_range(YYSTYPE *res, char *first, char *last)
+range_array(YYSTYPE * res, char *range)
 {
-	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);
+	anyResult ares;
+
+	if(!range || !range[0] || !(r = new AccRange(range))) return 0;
+	if(!r->GetFirst(&col, &row) || !(res->a_data =  (double*)malloc(r->CountItems() * sizeof(double)))) {
+		delete(r);
+		return 0;
+		}
+	parse_level++;
+	for(res->a_count = 0; r->GetNext(&col, &row); ) {
+		if(curr_data->GetResult(&ares, row, col, parse_level > MAX_PARSE)) {
+			switch(ares.type) {
+			case ET_VALUE: 
+				res->a_data[res->a_count++] = ares.value;
+				break;
+			case ET_TEXT:
+				if(ares.text && ares.text[0]) last_err_desc = "#ARGS";
+				break;
+			case ET_ERROR:
+				last_err_desc = "#ARGS";
+				break;
 				}
 			}
-		delete r;		return 1;
 		}
-	return 0;
+	parse_level--;
+	delete(r);
+	return 1;
 }
 
 static YYSTYPE *proc_clause(YYSTYPE *res)
@@ -1722,7 +2331,7 @@ static YYSTYPE *proc_clause(YYSTYPE *res)
 	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_data[0] = res->type == VAR && res->tptr ? res->tptr->GetValue() : res->val;
 		res->a_count = 1;
 		}
 	else if(!res->a_data) return res;
@@ -1760,13 +2369,13 @@ static void exec_clause(YYSTYPE *res)
 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;
+	char *last_err_desc;
+	int n_str, yychar, yynerrs;
 };
 static parse_info *parse_stack = 0L;
 
@@ -1774,30 +2383,42 @@ static void push_parser()
 {
 	parse_info *ptr;
 
+	if(!sym_table) InitArithFuncs(0L);
+	else if(!parse_level && bRecent) ArrangeFunctions();
 	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;
+	ptr->curr_data = curr_data;		ptr->last_err_desc = last_err_desc;
+	ptr->sym_table = sym_table;		sym_table = sym_tab_first;
 	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();
+	ptr->yychar = yychar;			ptr->yynerrs = yynerrs;
+	parse_stack = ptr;			last_err_desc = 0L;
+	parse_level++;				//reenter ?
+	push_syntax();				syntax_level->last_tok = 0;
 }
 
 static void pop_parser()
 {
 	parse_info *ptr;
+	symrec *n;
 
 	if(ptr = parse_stack) {
+		while(sym_table  && sym_table != sym_tab_first) {
+			n = sym_table->next;
+			delete(sym_table);
+			sym_table = n;
+			}
+		if(sym_table) sym_table = ptr->sym_table;
 		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;
+		buffer = ptr->buffer;		buff_pos = ptr->buff_pos;
+		curr_data = ptr->curr_data;	last_err_desc = ptr->last_err_desc;
 		memcpy(&yylval, &ptr->yylval, sizeof(YYSTYPE));
-		str_list = ptr->str_list;		n_str = ptr->n_str;
+		str_list = ptr->str_list;	n_str = ptr->n_str;
+		yychar = ptr->yychar;		yynerrs = ptr->yynerrs;
 		free(ptr);
+		parse_level--;
 		}
 	pop_syntax();
 }
@@ -1805,12 +2426,14 @@ static void pop_parser()
 static int is_ttoken(int h_nam)
 {
 	switch(h_nam) {
-	case 69:		return E;
-	case 393:		return PI;
+	case 101:		return E;
+	case 26992:		return PI;
 	case 28381:
 		if(syntax_level) syntax_level->cl1 = buff_pos;
 		return CLAUSE;
-	case 20:		return CLVAL;
+	case 9252:		return CLVAL;
+	case 26217:		return IF;
+	case 6033:		return ELSE;
 		}
 	return 0;
 }
@@ -1825,19 +2448,37 @@ static int yylex (void)
 
 	while((c = buffer[buff_pos++]) == ' ' || c == '\t');	//get first nonwhite char
 	if(!c) return 0;
+	//test for block statement
+	if(c == '{') {
+		for(i= 0; i < 79 && ((tok = buffer[buff_pos]) && (tok != '}')); buff_pos++) {
+			tmp_txt[i++] = (char)tok;
+			}
+		if(buffer[buff_pos] == '}')buff_pos++;
+		tmp_txt[i] = 0;
+		yylval.text = PushString(tmp_txt);
+		return yylval.type = BLOCK;
+		}
+	//test for '..' operator
+	if(c == '.' && buffer[buff_pos] == '.') {
+		buff_pos++;
+		return yylval.type = SER;
+		}
 	//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] == '+')){
+			if(i && 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];
 				}
+			if(i && buffer[buff_pos+1] == '.' && buffer[buff_pos+2]  == '.') {	//operator '..'
+				buff_pos++;
+				break;
+				}
 			}
 		tmp_txt[i] = 0;
 		sscanf(tmp_txt, "%lf", &yylval.val);
-		yylval.type = NUM;
-		return NUM;
+		return yylval.type = NUM;
 		}
 	//test for name or stringtoken
 	if(isalpha(c) || c=='$') {
@@ -1848,11 +2489,9 @@ static int yylex (void)
 		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);
+			s = putsym(h_nam, VAR);
+			s->SetName(tmp_txt);
 			}
 		
 		curr_sym = yylval.tptr = s;	return s->type;
@@ -1910,6 +2549,17 @@ static int yylex (void)
 			else if(syntax_level->last_tok == '?') return COLC;
 			}
 		break;
+	case ';':
+		if(syntax_level) {
+			if(syntax_level->last_tok == '(') return PSEP;
+			}
+		break;
+	case '+':
+		if(buffer[buff_pos] == '+') tok = INC;
+		break;
+	case '-':
+		if(buffer[buff_pos] == '-') tok = DEC;
+		break;
 		}
 	if(tok) {
 		buff_pos++;		return tok;
@@ -1921,10 +2571,10 @@ static int yylex (void)
 bool do_xyfunc(DataObj *d, double x1, double x2, double step, char *expr, lfPOINT **pts, long *npts, char *param)
 {
 	double x, y;
-	symrec *s;
+	symrec *sx, *sy;
 	lfPOINT *new_points;
 	long npoints = 0;
-	int length;
+	int length, res_mode = 0;
 	unsigned int hn_x = HashValue((unsigned char *)"x");
 	unsigned int hn_y = HashValue((unsigned char *)"y");
 
@@ -1933,6 +2583,7 @@ bool do_xyfunc(DataObj *d, double x1, double x2, double step, char *expr, lfPOIN
 	if(!(new_points = (lfPOINT*)calloc((iround(fabs(x2-x1)/fabs(step))+2), sizeof(lfPOINT))))
 		return false;
 	if(d) curr_data = d;
+	push_parser();
 	init_table();
 	if(param) {
 		length = strlen(param);
@@ -1948,21 +2599,34 @@ bool do_xyfunc(DataObj *d, double x1, double x2, double step, char *expr, lfPOIN
 		free(buffer);		buffer = 0L;
 		}		
 	length = strlen(expr);
-	buffer = expr;		s = putsym(hn_x, VAR, 0);
+	buffer = expr;		sx = putsym(hn_x, VAR);
 	for(x = x1; step > 0.0 ? x <= x2 : x >= x2; x += step) {
-		if(s = getsym(hn_x)){
-			s->value.var = x;	buff_pos = 0;
+		if(sx){
+			sx->SetValue(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;
+			switch (res_mode) {
+			case 1:
+				y = sy->GetValue();	break;
+			case 2:
+				y = line_res.value;	break;
+			default:
+				if(sy = getsym(hn_y)) {
+					y = sy->GetValue();	res_mode = 1;
+					}
+				else {
+					y = line_res.value;	res_mode = 2;
+					}
+				break;
+				}
+			new_points[npoints].fx = (getsym(hn_x))->GetValue();
 			new_points[npoints++].fy = y;
 			}
 		}
 	*pts = new_points;	*npts = npoints;
 	clear_table();
+	pop_parser();
 	if(curr_data) {
 		curr_data->Command(CMD_CLEAR_ERROR, 0L, 0L);
 		curr_data->Command(CMD_REDRAW, 0L, 0L);
@@ -1970,10 +2634,68 @@ bool do_xyfunc(DataObj *d, double x1, double x2, double step, char *expr, lfPOIN
 	return true;
 }
 
+bool do_func3D(DataObj *d, double x1, double x2, double xstep, double z1, double z2, double zstep, 
+	char *expr, char *param)
+{
+	int length, nr, nc, r, c, res_mode=0;
+	symrec *sx, *sz, *sy;
+	double x, y, z;
+	unsigned int hn_x = HashValue((unsigned char *)"x");
+	unsigned int hn_y = HashValue((unsigned char *)"y");
+	unsigned int hn_z = HashValue((unsigned char *)"z");
+
+	if(!d || x2 <= x1 || z2 <= z1 || xstep <= 0.0 || zstep <= 0.0) return false;
+	push_parser();
+	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;
+	sx = putsym(hn_x, VAR);		sz = putsym(hn_z, VAR);
+	nr = iround((z2-z1)/zstep)+1;	nc = iround((x2-x1)/xstep)+1;
+	d->Init(nr, nc);
+	for(r = 0, x = x1; r < nr; r++, x += xstep) {
+		for(c = 0, z = z1; c < nc; c++, z+= zstep) {
+			sx->SetValue(x);	sz->SetValue(z);	buff_pos = 0;
+			do {
+				yyparse();
+				}while(buff_pos < length);
+			switch (res_mode) {
+			case 1:
+				y = sy->GetValue();	break;
+			case 2:
+				y = line_res.value;	break;
+			default:
+				if(sy = getsym(hn_y)) {
+					y = sy->GetValue();	res_mode = 1;
+					}
+				else {
+					y = line_res.value;	res_mode = 2;
+					}
+				break;
+				}
+			d->SetValue(r, c, y);
+			}
+		} 
+	clear_table();
+	pop_parser();
+	return true;
+}
+
 anyResult *do_formula(DataObj *d, char *expr)
 {
 	int length;
-	static anyResult ret;
+	static anyResult ret, *pret = 0L;
 
 	if(d) curr_data = d;
 	ret.type = ET_ERROR;		ret.text = 0L;
@@ -1984,22 +2706,26 @@ anyResult *do_formula(DataObj *d, char *expr)
 		pop_parser();
 		return &ret;
 		}
-	strcpy(buffer, expr);	buffer[length++] = ';';
+	strcpy(buffer, expr);	if(buffer[length-1] != ';') buffer[length++] = ';';
 	buffer[length] = 0;	buff_pos = 0;
 	do {
 		yyparse();
 		}while(buff_pos < length);
+	ret.type = ET_ERROR;		ret.text = 0L;
 	if(curr_data && last_error) {
-		curr_data->Command(CMD_ERROR, last_error = 0L, 0L);
-		return &ret;
+		if(!(strcmp(last_error, "parse error"))) curr_data->Command(CMD_ERROR, 0L, 0L);
+		if(last_err_desc) pret = &line_res;
+		else pret = &ret;
 		}
+	else pret = &line_res;
+	last_error = last_err_desc = 0L;
 	free(buffer);		buffer = 0L;
 	clear_table();
 	pop_parser();
-	return &line_res;
+	return pret;
 }
 
-bool MoveFormula(DataObj *d, char *of, char *nf, int dx, int dy)
+bool MoveFormula(DataObj *d, char *of, char *nf, int dx, int dy, int r0, int c0)
 {
 	int length, tok, pos, i;
 	char *res, desc1[2], desc2[2];
@@ -2023,43 +2749,55 @@ bool MoveFormula(DataObj *d, char *of, char *nf, int dx, int dy)
 			}
 		else switch(tok) {
 			case NUM:
-				pos += sprintf(res+pos, "%g ", yylval.val);
+				pos += sprintf(res+pos, "%g", yylval.val);
 				break;
-			case FNCT:
+			case FNCT:	case FUNC2:	case AFNCT:	case SFNCT:
 				pos += sprintf(res+pos, "%s", curr_sym->name);
 				break;
-			case COLR:
+			case COLR:			case COLC:
 				pos += sprintf(res+pos, ":");
 				break;
-			case COLC:
-				pos += sprintf(res+pos, ": ");
+			case PSEP:
+				pos += sprintf(res+pos, ";");
 				break;
 			case CLVAL:
-				pos += sprintf(res+pos, "$$ ");
+				pos += sprintf(res+pos, "$$");
 				break;
 			case CLAUSE:
 				pos += sprintf(res+pos, " where ");
 				break;
 			case VAR:
+				curr_sym->InitSS();
 				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);
+						Int2ColLabel(desc1[0] || curr_sym->col < c0 ? curr_sym->col : curr_sym->col+dx >=0 ?
+						curr_sym->col+dx > c0 ? curr_sym->col+dx : c0 : 0, false),
+						desc2, desc2[0] || curr_sym->row < r0 ? curr_sym->row+1 : curr_sym->row + dy >= 0 ? 
+						curr_sym->row+dy > r0 ? curr_sym->row+1+dy : r0 : 1);
 					}
 				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 SER:
+				pos += sprintf(res+pos, "..");
+				break;
+			case INC:
+				pos += sprintf(res+pos, "++");
+				break;
+			case DEC:
+				pos += sprintf(res+pos, "--");
+				break;
 			case PI:
-				pos += sprintf(res+pos, "pi ");
+				pos += sprintf(res+pos, "pi");
 				break;
 			case E:
-				pos += sprintf(res+pos, "e ");
+				pos += sprintf(res+pos, "e");
 				break;
 			case AND:
 				pos += sprintf(res+pos, " && ");
@@ -2074,16 +2812,25 @@ bool MoveFormula(DataObj *d, char *of, char *nf, int dx, int dy)
 				pos += sprintf(res+pos, " != ");
 				break;
 			case GT:
-				pos += sprintf(res+pos, " > ");
+				pos += sprintf(res+pos, ">");
 				break;
 			case GE:
-				pos += sprintf(res+pos, " >= ");
+				pos += sprintf(res+pos, ">=");
 				break;
 			case LT:
-				pos += sprintf(res+pos, " < ");
+				pos += sprintf(res+pos, "<");
 				break;
 			case LE:
-				pos += sprintf(res+pos, " <= ");
+				pos += sprintf(res+pos, "<=");
+				break;
+			case IF: 
+				pos += sprintf(res+pos, "if");
+				break;
+			case ELSE: 
+				pos += sprintf(res+pos, "else");
+				break;
+			case BLOCK:
+				pos += sprintf(res+pos, "{%s}", yylval.text && yylval.text[0] ? yylval.text : "");
 				break;
 			}
 		}while(buff_pos < length);
@@ -2101,21 +2848,21 @@ static void fcurve(double x, double z, double **a, double *y, double dyda[], int
 {
 	int i, length;
 	double tmp, y1, y2;
-	symrec *symx, *s=0L;
+	symrec *symx, *sy=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);
+	if(!(symx = getsym(hn_x))) symx = putsym(hn_x, VAR);
 	//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;
+	symx->SetValue(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(sy = getsym(hn_y)) *y = sy->GetValue();
+	else *y = line_res.value;
 	if(*y == HUGE_VAL || *y == -HUGE_VAL) {
 		for(i = 0, *y = 0.0; i < ma; dyda[i++] = 0.0);
 		return;
@@ -2127,11 +2874,11 @@ static void fcurve(double x, double z, double **a, double *y, double dyda[], int
 			*parval[i] = tmp*.995;
 			buff_pos = 0;
 			do {	yyparse();	}while(buff_pos < length);
-			y1 = s ? s->value.var : line_result;
+			y1 = sy ? sy->GetValue() : line_res.value;
 			*parval[i] = tmp*1.005;
 			buff_pos = 0;
 			do {	yyparse();	}while(buff_pos < length);
-			y2 = s ? s->value.var : line_result;
+			y2 = sy ? sy->GetValue() : line_res.value;
 			*parval[i] = tmp;
 			dyda[i] = (y2-y1)*100.0/tmp;
 			}
@@ -2216,7 +2963,7 @@ int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr,
 	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;
+		parsym[i] = csr;	parval[i] = &csr->var;
 		}
 	//do iteratations to optimize fit
 	lista = (int*)malloc(sizeof(int)*nparam);
@@ -2243,7 +2990,7 @@ int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr,
 			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);
+		l += sprintf(tmp_txt+j, "%s%s=%g;", j && k ? " " : "", parsym[i]->name, parsym[i]->GetValue());
 		j += l;			k += l;
 		}
 	free(*par);	*par = strdup(tmp_txt);
@@ -2294,6 +3041,3 @@ int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr,
 
 
 
-
-
-
diff --git a/mfcalc.y b/mfcalc.y
index 9aa1928..1ce5db4 100755
--- a/mfcalc.y
+++ b/mfcalc.y
@@ -26,16 +26,27 @@
 #include <stdio.h>
 #include "rlplot.h"
 
-struct symrec {
+class symrec {
+public:
 	int type, row, col;
 	unsigned int h_name;
 	char *name, *text;
-	struct {
-		double var;
-		double (*fnctptr)(...);
-		} value;
-	int arg_type;
-	struct symrec *next;
+	double (*fnctptr)(...);
+	symrec *next;
+	double var;
+
+	symrec(unsigned int h_n, int typ, symrec *nxt);
+	~symrec();
+	double GetValue();
+	void GetValue(void *res);
+	double SetValue(double v);
+        void SetValue(void* dest, void* src);
+	void SetName(char *nam);
+	void InitSS();
+
+private:
+	bool isSSval;
+
 };
 
 // syntactical information
@@ -58,36 +69,42 @@ typedef struct{
 
 }YYSTYPE;
 
-static symrec *putsym (unsigned int h_name, int sym_type, int arg_type);
+static symrec *putsym (unsigned int h_name, int sym_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 double eval(YYSTYPE *sr, YYSTYPE *dst);
+static int range_array(YYSTYPE * res, char *range);
 static void exec_clause(YYSTYPE *res);
 static YYSTYPE *proc_clause(YYSTYPE *res);
 static void yyerror(char *s);
 static int yylex(void);
+static double nop() {return 0.0;};
 
 static char res_txt[1000];
 static anyResult line_res = {ET_UNKNOWN, 0.0, res_txt};
-static double line_result;
 static DataObj *curr_data;
+static char *last_error = 0L;		//error text
+static char *last_err_desc = 0L;	//short error description
 
-//the current command buffer
-static char *buffer = 0L;
+static char *buffer = 0L;		//the current command buffer
 static int buff_pos = 0;
+static bool bRecent = false;		//rearrange functions
+static int parse_level = 0;		//count reentrances into parser
+#define MAX_PARSE 20			//maximum number of reentances 
 %}
 
-%token <val>  NUM ARR STR PI E CLVAL
-%token <tptr> VAR FNCT TXT
-%type  <val>  exp str_exp
+%token <val>  NUM STR ARR BLOCK PI E CLVAL PSEP IF ELSE
+%token <tptr> VAR FNCT AFNCT SFNCT FUNC2 TXT
+%type  <val>  exp str_exp arr
 
 %right  '='
 %left	','			/* list separator */
 %left	CLAUSE			/* clause (where) operator */
+%left	SER
 %right	COLR COLC		/* range sep., conditional sep. */
 %right	'?'			/* conditional assignment */
 %left	AND OR
@@ -95,6 +112,8 @@ static int buff_pos = 0;
 %left   '-' '+'
 %left   '*' '/'
 %left   NEG 		/* negation-unary minus */
+%left	INC DEC		/* increment, decrement */
+%left	PINC PDEC	/* pre- increment, decrement */
 %right  '^'	 	/* exponentiation       */
 
 /* Grammar follows */
@@ -103,31 +122,56 @@ input:    /* empty string */
 	| input line
 ;
 
-line:	 '\n' ';'
+line:	 '\n' | ';' | ','
 	| exp '\n'		{store_res(&yyvsp[-1]); return 0;}
 	| exp ';'		{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;}
+	STR			{;}
+	|str_exp '+' exp	{yyval.text=add_strings(yyvsp[-2].text, string_value(&yyvsp[0]));}
+	|exp '+' str_exp	{yyval.text=add_strings(string_value(&yyvsp[-2]), yyvsp[0].text);}
+	|str_exp '+' str_exp	{yyval.text=add_strings(yyvsp[-2].text, yyvsp[0].text);}
+;
+
+range:
+	str_exp			{;}	
+	|VAR COLR VAR		{if(yyval.text =(char*)malloc(strlen($1->name) +strlen($3->name) + 2)) sprintf(yyval.text, "%s:%s", $1->name, $3->name);}
+;
+
+arr:	ARR
+	|exp			{if(!yyval.a_data) {yyval.a_data = (double*)malloc(sizeof(double)); yyval.a_count = 1; yyval.a_data[0] = yyval.val;}}
+	|arr ',' arr		{push(&yyval, &yyvsp[0]);}
+	|arr CLAUSE exp		{exec_clause(&yyval);} 
+	|range			{range_array(&yyval, yyvsp[0].text);}
+	|NUM SER NUM		{if($1 < $3 && (yyval.a_data = (double*)malloc((int)($3-$1+2)*sizeof(double))))
+					for(yyval.a_count=0; $1<=$3; yyval.a_data[yyval.a_count++] = $1, $1 += 1.0 );}
 ;
 
 exp:	NUM				{$$ = $1; yyval.type = NUM;}
         |TXT				{$$ = 0.0;}
 	|CLVAL				{$$ = syntax_level ? syntax_level->clval : 0.0; }
-	|PI				{$$ = 3.14159265358979; yyval.type = NUM;}
+	|PI				{$$ = _PI; 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);}
+	|VAR				{$1->GetValue(&yyval);}
+	|BLOCK			{eval(&yyvsp[0], &yyval);}
+	|VAR '=' exp		{$1->SetValue(&yyval, &yyvsp[0]);}
+	|VAR '=' str_exp	{$1->SetValue(&yyval, &yyvsp[0]);}
+	|FNCT '(' exp ')'	{$$ = (($1->fnctptr)($3));}
+	|AFNCT '(' arr ')'	{$$ = (($1->fnctptr)(proc_clause(&yyvsp[-1])));}
+	|AFNCT '(' exp PSEP exp ')' {push(&yyvsp[-2], &yyvsp[0]); $$ = (($1->fnctptr)(&yyvsp[-2]));}
+	|AFNCT '(' exp PSEP exp PSEP exp ')' {push(&yyvsp[-4], &yyvsp[-2]); push(&yyvsp[-4], &yyvsp[0]); $$ = (($1->fnctptr)(&yyvsp[-4]));}
+	|SFNCT '(' str_exp ')'	{$$ = (($1->fnctptr)(&yyvsp[-1], &yyval));}
+	|SFNCT '(' exp ')'	{yyvsp[-1].text = string_value(&yyvsp[-1]); $$ = (($1->fnctptr)(&yyvsp[-1], &yyval));}
+	|FUNC2 '(' arr PSEP arr ')'	{$$ = ((*$1->fnctptr)(proc_clause(&yyvsp[-3]), proc_clause(&yyvsp[-1]), 0L));}
+	|FUNC2 '(' arr PSEP arr PSEP range')'	
+			{$$ = ((*$1->fnctptr)(proc_clause(&yyvsp[-5]), proc_clause(&yyvsp[-3]), yyvsp[-1].text));}
+	|IF '(' exp ')' BLOCK	{$$ = $3 != 0 ? eval(&yyvsp[0], &yyval) : 0.0;}
+	|IF '(' exp ')' BLOCK ELSE BLOCK	{$$ = $3 != 0 ? eval(&yyvsp[-2], &yyval) : eval(&yyvsp[0], &yyval);}
 	|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;}
@@ -140,13 +184,14 @@ exp:	NUM				{$$ = $1; yyval.type = NUM;}
 	|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);}
+					else $$ = (getsym(HashValue((unsigned char*)"zdiv")))->GetValue(); }
 	|'-' exp  %prec NEG	{$$ = -$2;}
+	|VAR INC		{$$=$1->GetValue(); $1->SetValue($$+1.0); $$ -= 1.0; yyval.type = NUM;}
+	|VAR DEC		{$$=$1->GetValue(); $1->SetValue($$-1.0); $$ += 1.0; yyval.type = NUM;}
+	|INC VAR %prec PINC	{$$=$2->GetValue(); $2->SetValue($$+1.0); yyval.type = NUM;}
+	|DEC VAR %prec PDEC	{$$=$2->GetValue(); $2->SetValue($$-1.0); yyval.type = NUM;}
 	|exp '^' exp		{$$ = pow($1, $3);}
-	|exp CLAUSE exp		{$$ = $1; exec_clause(&yyval);} 
-	|'(' exp ')'		{memcpy(&yyval, &yyvsp[-1], sizeof(YYSTYPE))}
+	|'(' arr ')'		{memcpy(&yyval, &yyvsp[-1], sizeof(YYSTYPE)); yyvsp[-1].a_data = 0L; yyvsp[-1].a_count = 0;}
 	|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))}
@@ -154,36 +199,185 @@ exp:	NUM				{$$ = $1; yyval.type = NUM;}
 ;
 %%
 
-static char *last_error = 0L;
+// The symrec class
+symrec::symrec(unsigned int h_n, int typ, symrec *nxt) 
+{
+	h_name = h_n;
+	type = typ;
+	next = nxt;
+	row = col = -1;
+	name = text = 0L;
+	var = 0.0;
+	isSSval = false;
+	fnctptr = (double (*)(...))nop;
+}
+
+symrec::~symrec()
+{
+	if(name) free(name);	name = 0L;
+	if(text) free(text);	text = 0L;
+}
+
+double
+symrec::GetValue()
+{
+	anyResult ares;
+
+	if(isSSval) {
+		if(row < 0 && col < 0) InitSS();
+		//GetResult( , , ,true) inhibits reentrance into parser !
+		if(curr_data->GetResult(&ares, row, col, parse_level > MAX_PARSE)){
+			return var = ares.value;
+			}
+		isSSval = false;
+		row = col = -1;
+		}
+	return var;
+}
+
+void
+symrec::GetValue(void *re)
+{
+	anyResult ares;
+	YYSTYPE *res = (YYSTYPE*)re;
+
+	if(isSSval) {
+		if(row < 0 && col < 0) InitSS();
+		res->a_data = 0L;	res->a_count = 0;
+		//GetResult( , , ,true) inhibits reentrance into parser !
+		if(curr_data->GetResult(&ares, row, col, parse_level > MAX_PARSE)){
+			if(text) free(text);		text = 0L;
+			if(ares.type == ET_VALUE) {
+				res->type = VAR;	res->val = ares.value;
+				res->text = 0L;
+				}
+			else if(ares.type == ET_TEXT && ares.text) {
+				res->type = STR;	res->val = 0.0;
+				text = strdup(ares.text);
+				res->text = text;
+				}
+			else {
+				res->type = NUM;	res->val = var;
+				res->text = 0L;
+				}
+			var = res->val;
+			return;
+			}
+		isSSval = false;
+		row = col = -1;
+		}
+	if(text && text[0]) {
+		res->text = strdup(text);
+		res->val = var;		res->type = STR;
+		}
+	else {
+		res->type = NUM;	res->val = var;
+		res->text = 0L;
+		}
+	res->a_data = 0L;	res->a_count = 0L;
+}
+
+double 
+symrec::SetValue(double v)
+{
+	if(isSSval) {
+		if(row < 0 && col < 0) InitSS();
+		if(curr_data->SetValue(row, col, v)){
+			if(curr_data) curr_data->Command(CMD_UPDATE, 0L, 0L);
+			return var = v;
+			}
+		isSSval = false;
+		row = col = -1;
+		}
+	return var = v;
+}
+
+void 
+symrec::SetValue(void* d, void* s)
+{
+	YYSTYPE *dest = (YYSTYPE*)d;
+	YYSTYPE *src = (YYSTYPE*)s;
+
+	if(isSSval && curr_data) {
+		if(row < 0 && col < 0) InitSS();
+		if(last_err_desc) curr_data->SetText(row, col, last_err_desc);
+		else if(src->type == STR) curr_data->SetText(row, col, src->text);
+		else if(src->type == ARR || (src->a_data)) curr_data->SetText(row, col, "#ARRAY");
+		else if(src->type == VAR && src->tptr->type == TXT) curr_data->SetText(row, col, src->tptr->text);
+		else curr_data->SetValue(row, col, src->val);
+		curr_data->Command(CMD_UPDATE, 0L, 0L);
+		}
+	var = src->val;
+	if(text) free(text);		text = 0L;
+	if(src->text && src->text[0]) 	text = strdup(src->text);
+	GetValue(d);
+	return;
+}
+
+void
+symrec::SetName(char *nam)
+{
+	if(name || !nam || !nam[0]) return;
+	name = strdup(nam);
+	if((name && curr_data) && (isalpha(name[0]) || name[0] == '$') && isdigit(name[strlen(name)-1])) isSSval=true;
+}
+
+void
+symrec::InitSS()
+{
+	AccRange *ar;
+
+	if(row<0 && col<0 &&(ar = new AccRange(name))) {
+		ar->GetFirst(&col, &row);
+		delete(ar);
+		}
+}
+
 static void yyerror(char *s)
 {  
-	//Called by yyparse on error
+	//called by yyparse on error
 	if(curr_data) curr_data->Command(CMD_ERROR, last_error = s, 0L);
 	else printf("%s\n", s);
 }
 
+static void yyargserr(char *s)
+{
+	//call from function on argument type/number mismatch
+	yyerror(s);
+	last_err_desc = "#ARGS";
+}
+
 static void store_res(YYSTYPE *res)
 {
-	if(res->type == STR) {
-		line_result = 0.0;
+	if(last_err_desc) {
 		line_res.type = ET_TEXT;
-		line_res. value = 0.0;
+		line_res.value = 0.0;
+		strcpy(res_txt, last_err_desc);
+		}
+	else if(res->type == STR) {
+		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)) && res->a_count == 1) {
+		line_res.type = ET_VALUE;
+		line_res.value = res->a_data[0];
+		}
+	else if(res->type == ARR && !(res->a_data) && !(res->a_count)) {
+		line_res.type = ET_VALUE;
+		line_res.value = res->val;
+		}
 	else if(res->type == ARR || (res->a_data)) {
-		line_result = 0.0;
 		line_res.type = ET_TEXT;
-		line_res. value = 0.0;
+		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;
 		}
@@ -245,6 +439,35 @@ static void pop_syntax()
 		}
 }
 
+static double eval(YYSTYPE *sr, YYSTYPE *dst) 
+{
+	anyResult *ar;
+
+	if(!sr || !sr->text) return 0.0;
+	parse_level++;
+	ar = do_formula(0L, sr->text);
+	yylval.a_data = 0L;	yylval.a_count = 0;
+	switch(ar->type) {
+	case ET_VALUE:
+		dst->type = NUM;
+		dst->val = ar->value;
+		dst->text = 0L;
+		break;
+	case ET_TEXT:
+		dst->type = STR;
+		dst->val = 0.0;
+		dst->text = PushString(ar->text);
+		break;
+	default:
+		dst->type = NUM;
+		dst->val = 0.0;
+		dst->text = 0L;
+		break;
+		}
+	parse_level--;
+	return dst->val;
+}
+
 // more functions
 static double sign(double v)
 {
@@ -253,6 +476,19 @@ static double sign(double v)
 	return 0.0;
 }
 
+static long idum=0;
+
+static double rand(double v)
+{
+	return ran2(&idum);
+}
+
+static double srand(double v)
+{
+	idum = (long)v;
+	return v;
+}
+
 static double factorial(double v)
 {
 	return factrl((int)v);
@@ -260,9 +496,14 @@ static double factorial(double v)
 
 static void close_arr_func(YYSTYPE *sr)
 {
-	free(sr->a_data);
+	if(sr->a_data) free(sr->a_data);
 	sr->a_data = 0L;		sr->a_count = 0;
-	if(sr->type == ARR) sr->type = NUM;
+}
+
+static double _strlen(YYSTYPE *sr, YYSTYPE *dst)
+{
+	if(!sr || !sr->text) return 0.0;
+	return (double)strlen(sr->text);
 }
 
 #undef min
@@ -270,7 +511,7 @@ static double min(YYSTYPE *sr)
 {
 	int i;
 
-	if(!sr) return 0.0;
+	if(!sr || !sr->a_count) 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];
@@ -284,7 +525,7 @@ static double max(YYSTYPE *sr)
 {
 	int i;
 
-	if(!sr) return 0.0;
+	if(!sr || !sr->a_count) 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];
@@ -310,9 +551,10 @@ static double sum(YYSTYPE *sr)
 
 	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];
+		for(i = 0, sr->val = 0.0; i < sr->a_count; i++) sr->val += sr->a_data[i];
 		close_arr_func(sr);
 		}
+	else sr->val = 0.0;
 	return sr->val;
 }
 
@@ -418,14 +660,159 @@ static double sterr(YYSTYPE *sr)
 	return sr->val;
 }
 
+static double beta(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 2){
+		sr->val = betaf(sr->a_data[0], sr->a_data[1]);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  beta(u, v).");
+	return sr->val;
+}
+
+static double _gammp(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 2){
+		sr->val = gammp(sr->a_data[0], sr->a_data[1]);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  gammp(a, x).");
+	return sr->val;
+}
+
+static double _gammq(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 2){
+		sr->val = gammq(sr->a_data[0], sr->a_data[1]);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  gammq(a, x).");
+	return sr->val;
+}
+
+static double _betai(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 3){
+		sr->val = betai(sr->a_data[0], sr->a_data[1], sr->a_data[2]);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  betai(x, a, b).");
+	return sr->val;
+}
+
+static double _bincof(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 2){
+		sr->val = bincof(sr->a_data[0], sr->a_data[1]);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  bincof(n, k).");
+	return sr->val;
+}
+
+static double binomdist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 3){
+		sr->val = binomdistf(sr->a_data[0], sr->a_data[1], sr->a_data[2]);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  binomdist(s, n, p).");
+	return sr->val;
+}
+
+static double normdist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 3){
+		sr->val = norm_dist(sr->a_data[0], sr->a_data[1], sr->a_data[2]);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  normdist(x, mean, SD).");
+	return sr->val;
+}
+
+static double norminv(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 3) {
+		sr->val = distinv(norm_dist,sr->a_data[1], sr->a_data[2], sr->a_data[0], 2.0);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  norminv(p, mean, SD).");
+	return sr->val;
+}
+
+static double chidist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 2){
+		sr->val = chi_dist(sr->a_data[0], sr->a_data[1], 1.0);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  chidist(x, df).");
+	return sr->val;
+}
+
+static double chiinv(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 2) {
+		sr->val = distinv(chi_dist,sr->a_data[1], 1.0, sr->a_data[0], 2.0);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  chiinv(p, df).");
+	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]);
+		sr->val = t_dist(sr->a_data[0], sr->a_data[1], 1.0);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  tdist(x, df).");
+	return sr->val;
+}
+
+static double tinv(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+        if(sr->a_data && sr->a_count == 2) {
+		sr->val = distinv(t_dist,sr->a_data[1], 1.0, sr->a_data[0], 2.0);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  tinv(p, df).");
+	return sr->val;
+}
+
+static double poisdist(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0.0;
+	if(sr->a_data && sr->a_count == 2){
+		sr->val = pois_dist(sr->a_data[0], sr->a_data[1], 1.0);
 		close_arr_func(sr);
 		}
+	else yyargserr("Wrong number of arguments\nin call to  poisdist(x, mean).");
 	return sr->val;
 }
 
@@ -437,51 +824,150 @@ static double fdist(YYSTYPE *sr)
 		sr->val = f_dist(sr->a_data[0], sr->a_data[1], sr->a_data[2]);
 		close_arr_func(sr);
 		}
+	else yyargserr("Wrong number of arguments\nin call to  fdist(x, df1, df2).");
 	return sr->val;
 }
 
+static double finv(YYSTYPE *sr)
+{
+	if(!sr) return 0.0;
+	sr->val = 0;
+	if(sr->a_data && sr->a_count == 3){
+		sr->val = distinv(f_dist,sr->a_data[1], sr->a_data[2], sr->a_data[0], 2.0);
+		close_arr_func(sr);
+		}
+	else yyargserr("Wrong number of arguments\nin call to  finv(p, df1, df2).");
+	return sr->val;
+}
+
+static double pearson(YYSTYPE *sr1, YYSTYPE *sr2, char *dest)
+{
+	if(!sr1 || !sr2) return 0.0;
+	sr1->val = 0.0;
+        if(sr1->a_data && sr1->a_count > 1 && sr2->a_data && sr1->a_count == sr2->a_count){
+		sr1->val = sr2->val = d_pearson(sr1->a_data, sr2->a_data, sr1->a_count, dest, curr_data);
+		close_arr_func(sr1);		close_arr_func(sr2);
+		}
+	else yyargserr("Bad arguments in call to function\npearson(range1; range2 [;\"dest\"]).");
+	return sr1->val;
+}
+
+static double spearman(YYSTYPE *sr1, YYSTYPE *sr2, char *dest)
+{
+	if(!sr1 || !sr2) return 0.0;
+	sr1->val = 0.0;
+        if(sr1->a_data && sr1->a_count > 1 && sr2->a_data && sr1->a_count == sr2->a_count){
+		sr1->val = sr2->val = d_spearman(sr1->a_data, sr2->a_data, sr1->a_count, dest, curr_data);
+		close_arr_func(sr1);		close_arr_func(sr2);
+		}
+	else yyargserr("Bad arguments in call to function\nspearman(range1; range2 [;\"dest\"]).");
+	return sr1->val;
+}
+
+static double regression(YYSTYPE *sr1, YYSTYPE *sr2, char *dest)
+{
+	if(!sr1 || !sr2) return 0.0;
+	sr1->val = 0.0;
+	if(!(dest)) yyargserr("No destination range in call to function\nregression(range1; range2; \"dest\").");
+        if(sr1->a_data && sr1->a_count > 1 && sr2->a_data && sr1->a_count == sr2->a_count){
+		sr1->val = sr2->val = d_regression(sr1->a_data, sr2->a_data, sr1->a_count, dest, curr_data);
+		close_arr_func(sr1);		close_arr_func(sr2);
+		}
+	else yyargserr("Bad arguments in call to function\nregression(range1; range2; \"dest\").");
+	return sr1->val;
+}
+
+static double ttest(YYSTYPE *sr1, YYSTYPE *sr2, char *dest)
+{
+	if(!sr1 || !sr2) return 0.0;
+	sr1->val = 0.0;
+        if(sr1->a_data && sr1->a_count > 1 && sr2->a_data && sr2->a_count > 1){
+		sr1->val = sr2->val = d_ttest(sr1->a_data, sr2->a_data, sr1->a_count, sr2->a_count, dest, curr_data);
+		close_arr_func(sr1);		close_arr_func(sr2);
+		}
+	else yyargserr("Bad arguments in call to function\nttest(range1; range2[;\"dest\"]).");
+	return sr1->val;
+}
+
+static double ftest(YYSTYPE *sr1, YYSTYPE *sr2, char *dest)
+{
+	if(!sr1 || !sr2) return 0.0;
+	sr1->val = 0.0;
+        if(sr1->a_data && sr1->a_count > 1 && sr2->a_data && sr1->a_count > 1){
+		sr1->val = sr2->val = d_ftest(sr1->a_data, sr2->a_data, sr1->a_count, sr2->a_count, dest, curr_data);
+		close_arr_func(sr1);		close_arr_func(sr2);
+		}
+	else yyargserr("Bad arguments in call to function\nftest(range1; range2[;\"dest\"]).");
+	return sr1->val;
+}
+
 struct init
 {
+	int f_type;
 	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},
+	{FUNC2, HashValue((unsigned char*)"pearson"), (double(*)(double))&pearson},
+	{FUNC2, HashValue((unsigned char*)"spearman"), (double(*)(double))&spearman},
+	{FUNC2, HashValue((unsigned char*)"regression"), (double(*)(double))&regression},
+	{FUNC2, HashValue((unsigned char*)"ttest"), (double(*)(double))&ttest},
+	{FUNC2, HashValue((unsigned char*)"ftest"), (double(*)(double))&ftest},
+	{AFNCT, HashValue((unsigned char*)"variance"), (double(*)(double))&variance},
+	{AFNCT, HashValue((unsigned char*)"stdev"), (double(*)(double))&stdev},
+	{AFNCT, HashValue((unsigned char*)"sterr"), (double(*)(double))&sterr},
+	{AFNCT, HashValue((unsigned char*)"min"), (double(*)(double))&min},
+	{AFNCT, HashValue((unsigned char*)"max"), (double(*)(double))&max},
+	{AFNCT, HashValue((unsigned char*)"count"), (double(*)(double))&count},
+	{AFNCT, HashValue((unsigned char*)"sum"), (double(*)(double))&sum},
+	{AFNCT, HashValue((unsigned char*)"mean"), (double(*)(double))&mean},
+	{AFNCT, HashValue((unsigned char*)"median"), (double(*)(double))&quartile2},
+	{AFNCT, HashValue((unsigned char*)"quartile1"), (double(*)(double))&quartile1},
+	{AFNCT, HashValue((unsigned char*)"quartile2"), (double(*)(double))&quartile2},
+	{AFNCT, HashValue((unsigned char*)"quartile3"), (double(*)(double))&quartile3},
+	{AFNCT, HashValue((unsigned char*)"gmean"), (double(*)(double))&gmean},
+	{AFNCT, HashValue((unsigned char*)"hmean"), (double(*)(double))&hmean},
+	{AFNCT, HashValue((unsigned char*)"tdist"), (double(*)(double))&tdist},
+	{AFNCT, HashValue((unsigned char*)"tinv"), (double(*)(double))&tinv},
+	{AFNCT, HashValue((unsigned char*)"poisdist"), (double(*)(double))&poisdist},
+	{AFNCT, HashValue((unsigned char*)"fdist"), (double(*)(double))&fdist},
+	{AFNCT, HashValue((unsigned char*)"finv"), (double(*)(double))&finv},
+	{AFNCT, HashValue((unsigned char*)"gammp"), (double(*)(double))&_gammp},
+	{AFNCT, HashValue((unsigned char*)"gammq"), (double(*)(double))&_gammq},
+	{AFNCT, HashValue((unsigned char*)"beta"), (double(*)(double))&beta},
+	{AFNCT, HashValue((unsigned char*)"betai"), (double(*)(double))&_betai},
+	{AFNCT, HashValue((unsigned char*)"bincof"), (double(*)(double))&_bincof},
+	{AFNCT, HashValue((unsigned char*)"binomdist"), (double(*)(double))&binomdist},
+	{AFNCT, HashValue((unsigned char*)"normdist"), (double(*)(double))&normdist},
+	{AFNCT, HashValue((unsigned char*)"norminv"), (double(*)(double))&norminv},
+	{AFNCT, HashValue((unsigned char*)"chidist"), (double(*)(double))&chidist},
+	{AFNCT, HashValue((unsigned char*)"chiinv"), (double(*)(double))&chiinv},
+	{SFNCT, HashValue((unsigned char*)"strlen"), (double(*)(double))&_strlen},
+	{SFNCT, HashValue((unsigned char*)"eval"), (double(*)(double))&eval},
+	{FNCT, HashValue((unsigned char*)"erf"), errf},
+	{FNCT, HashValue((unsigned char*)"erfc"), errfc},
+	{FNCT, HashValue((unsigned char*)"sign"), sign},
+	{FNCT, HashValue((unsigned char*)"gammaln"), gammln},
+	{FNCT, HashValue((unsigned char*)"factorial"), factorial},
+	{FNCT, HashValue((unsigned char*)"rand"), rand},
+	{FNCT, HashValue((unsigned char*)"srand"), srand},
+	{FNCT, HashValue((unsigned char*)"floor"), floor},
+	{FNCT, HashValue((unsigned char*)"abs"), fabs},
+	{FNCT, HashValue((unsigned char*)"asin"), asin},
+	{FNCT, HashValue((unsigned char*)"acos"), acos},
+	{FNCT, HashValue((unsigned char*)"atan"), atan},
+	{FNCT, HashValue((unsigned char*)"sinh"), sinh},
+	{FNCT, HashValue((unsigned char*)"cosh"), cosh},
+	{FNCT, HashValue((unsigned char*)"tanh"), tanh},
+	{FNCT, HashValue((unsigned char*)"sin"),  sin},
+	{FNCT, HashValue((unsigned char*)"cos"),  cos},
+	{FNCT, HashValue((unsigned char*)"atan"), atan},
+	{FNCT, HashValue((unsigned char*)"log10"), log10},
+	{FNCT, HashValue((unsigned char*)"ln"),   log},
+	{FNCT, HashValue((unsigned char*)"log"),   log},
+	{FNCT, HashValue((unsigned char*)"exp"),  exp},
+	{FNCT, HashValue((unsigned char*)"sqrt"), sqrt},
 	{0, 0, 0}};
 
 // Store strings in a list
@@ -499,19 +985,68 @@ static char *PushString(char *text)
 }
 
 //The symbol table: a chain of `struct symrec'
-static symrec *sym_table = (symrec *) 0;
+static symrec *sym_table, *sym_tab_first;
+
+//Rearrange function table with previously used functions in front
+void ArrangeFunctions()
+{
+	symrec *ptr, *ptr1, *ptr2, *next;
+
+	for(ptr = sym_table, ptr1 = ptr2 = 0L; (ptr); ) {
+		next = ptr->next;
+		if(ptr->name) {
+			ptr->next = ptr1;
+			ptr1 = ptr;
+			}
+		else {
+			ptr->next = ptr2;
+			ptr2 = ptr;
+			}
+		ptr = next;
+		}
+	for(sym_table = 0L, ptr = ptr2; (ptr); ){
+		next = ptr->next;
+		ptr->next = sym_table;
+		sym_table = ptr;
+		ptr = next;
+		}
+	for(ptr = ptr1; (ptr); ){
+		next = ptr->next;
+		ptr->next = sym_table;
+		sym_table = ptr;
+		ptr = next;
+		}
+	sym_tab_first = sym_table;
+	bRecent = false;
+}
 
 // Put arithmetic functions and predifened variables in table
-static void init_table (void)
+void InitArithFuncs(DataObj *d)
 {
 	int i;
-	symrec *ptr;
+	symrec *ptr, *next;
 
+	if(d) curr_data = d;
+	if(sym_table) {
+		for (ptr = sym_table; ptr != (symrec *) 0;){
+			if(ptr) {
+				next = ptr->next;
+				delete (ptr);
+				}
+			ptr = next;
+			}
+		sym_table = sym_tab_first = (symrec *) 0;
+		}
 	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 (arith_fncts[i].h_name, arith_fncts[i].f_type);
+		ptr->fnctptr = (double (*)(...))arith_fncts[i].fnct;
 		}
-	ptr = putsym(HashValue((unsigned char*)"zdiv"), VAR, 0);	ptr->value.var = 1.0;
+	ptr = putsym(HashValue((unsigned char*)"zdiv"), VAR);		ptr->SetValue(1.0);
+	sym_tab_first = sym_table;
+}
+
+static void init_table (void)
+{
 	str_list = 0L;		n_str = 0;
 	push_syntax();
 }
@@ -519,18 +1054,7 @@ static void init_table (void)
 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;
@@ -539,58 +1063,30 @@ static void clear_table()
 }
 
 static symrec *
-putsym (unsigned int h_name, int sym_type, int arg_type)
+putsym (unsigned int h_name, int sym_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;
+	sym_table = new symrec(h_name, sym_type, sym_table);
+	return sym_table;
 }
 
 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)
+	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;
-					}
+			if(sym_name && !ptr->name) {
+				ptr->SetName(sym_name);
+				bRecent = true;
 				}
-			ptr->row = row;	ptr->col = col;
-			delete(ar);
 			return ptr;
 			}
+		//predefined variables never end on a digit
+		else if(ptr == sym_tab_first) {
+			if(sym_name && isdigit(sym_name[strlen(sym_name)-1])) return 0;
+			}
 		}
 	return 0;
 }
@@ -598,7 +1094,7 @@ getsym (unsigned int h_name, char *sym_name)
 static int
 push(YYSTYPE *res, YYSTYPE *val)
 {
-	if(val->a_data && val->a_count) {
+	if(val->a_data) {
 		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;
@@ -629,36 +1125,36 @@ push(YYSTYPE *res, YYSTYPE *val)
 }
 
 static int
-get_range(YYSTYPE *res, char *first, char *last)
+range_array(YYSTYPE * res, char *range)
 {
-	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);
+	anyResult ares;
+
+	if(!range || !range[0] || !(r = new AccRange(range))) return 0;
+	if(!r->GetFirst(&col, &row) || !(res->a_data =  (double*)malloc(r->CountItems() * sizeof(double)))) {
+		delete(r);
+		return 0;
+		}
+	parse_level++;
+	for(res->a_count = 0; r->GetNext(&col, &row); ) {
+		if(curr_data->GetResult(&ares, row, col, parse_level > MAX_PARSE)) {
+			switch(ares.type) {
+			case ET_VALUE: 
+				res->a_data[res->a_count++] = ares.value;
+				break;
+			case ET_TEXT:
+				if(ares.text && ares.text[0]) last_err_desc = "#ARGS";
+				break;
+			case ET_ERROR:
+				last_err_desc = "#ARGS";
+				break;
 				}
 			}
-		delete r;		return 1;
 		}
-	return 0;
+	parse_level--;
+	delete(r);
+	return 1;
 }
 
 static YYSTYPE *proc_clause(YYSTYPE *res)
@@ -670,7 +1166,7 @@ static YYSTYPE *proc_clause(YYSTYPE *res)
 	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_data[0] = res->type == VAR && res->tptr ? res->tptr->GetValue() : res->val;
 		res->a_count = 1;
 		}
 	else if(!res->a_data) return res;
@@ -708,13 +1204,13 @@ static void exec_clause(YYSTYPE *res)
 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;
+	char *last_err_desc;
+	int n_str, yychar, yynerrs;
 };
 static parse_info *parse_stack = 0L;
 
@@ -722,30 +1218,42 @@ static void push_parser()
 {
 	parse_info *ptr;
 
+	if(!sym_table) InitArithFuncs(0L);
+	else if(!parse_level && bRecent) ArrangeFunctions();
 	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;
+	ptr->curr_data = curr_data;		ptr->last_err_desc = last_err_desc;
+	ptr->sym_table = sym_table;		sym_table = sym_tab_first;
 	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();
+	ptr->yychar = yychar;			ptr->yynerrs = yynerrs;
+	parse_stack = ptr;			last_err_desc = 0L;
+	parse_level++;				//reenter ?
+	push_syntax();				syntax_level->last_tok = 0;
 }
 
 static void pop_parser()
 {
 	parse_info *ptr;
+	symrec *n;
 
 	if(ptr = parse_stack) {
+		while(sym_table  && sym_table != sym_tab_first) {
+			n = sym_table->next;
+			delete(sym_table);
+			sym_table = n;
+			}
+		if(sym_table) sym_table = ptr->sym_table;
 		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;
+		buffer = ptr->buffer;		buff_pos = ptr->buff_pos;
+		curr_data = ptr->curr_data;	last_err_desc = ptr->last_err_desc;
 		memcpy(&yylval, &ptr->yylval, sizeof(YYSTYPE));
-		str_list = ptr->str_list;		n_str = ptr->n_str;
+		str_list = ptr->str_list;	n_str = ptr->n_str;
+		yychar = ptr->yychar;		yynerrs = ptr->yynerrs;
 		free(ptr);
+		parse_level--;
 		}
 	pop_syntax();
 }
@@ -753,12 +1261,14 @@ static void pop_parser()
 static int is_ttoken(int h_nam)
 {
 	switch(h_nam) {
-	case 69:		return E;
-	case 393:		return PI;
+	case 101:		return E;
+	case 26992:		return PI;
 	case 28381:
 		if(syntax_level) syntax_level->cl1 = buff_pos;
 		return CLAUSE;
-	case 20:		return CLVAL;
+	case 9252:		return CLVAL;
+	case 26217:		return IF;
+	case 6033:		return ELSE;
 		}
 	return 0;
 }
@@ -773,19 +1283,37 @@ static int yylex (void)
 
 	while((c = buffer[buff_pos++]) == ' ' || c == '\t');	//get first nonwhite char
 	if(!c) return 0;
+	//test for block statement
+	if(c == '{') {
+		for(i= 0; i < 79 && ((tok = buffer[buff_pos]) && (tok != '}')); buff_pos++) {
+			tmp_txt[i++] = (char)tok;
+			}
+		if(buffer[buff_pos] == '}')buff_pos++;
+		tmp_txt[i] = 0;
+		yylval.text = PushString(tmp_txt);
+		return yylval.type = BLOCK;
+		}
+	//test for '..' operator
+	if(c == '.' && buffer[buff_pos] == '.') {
+		buff_pos++;
+		return yylval.type = SER;
+		}
 	//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] == '+')){
+			if(i && 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];
 				}
+			if(i && buffer[buff_pos+1] == '.' && buffer[buff_pos+2]  == '.') {	//operator '..'
+				buff_pos++;
+				break;
+				}
 			}
 		tmp_txt[i] = 0;
 		sscanf(tmp_txt, "%lf", &yylval.val);
-		yylval.type = NUM;
-		return NUM;
+		return yylval.type = NUM;
 		}
 	//test for name or stringtoken
 	if(isalpha(c) || c=='$') {
@@ -796,11 +1324,9 @@ static int yylex (void)
 		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);
+			s = putsym(h_nam, VAR);
+			s->SetName(tmp_txt);
 			}
 		
 		curr_sym = yylval.tptr = s;	return s->type;
@@ -858,6 +1384,17 @@ static int yylex (void)
 			else if(syntax_level->last_tok == '?') return COLC;
 			}
 		break;
+	case ';':
+		if(syntax_level) {
+			if(syntax_level->last_tok == '(') return PSEP;
+			}
+		break;
+	case '+':
+		if(buffer[buff_pos] == '+') tok = INC;
+		break;
+	case '-':
+		if(buffer[buff_pos] == '-') tok = DEC;
+		break;
 		}
 	if(tok) {
 		buff_pos++;		return tok;
@@ -869,10 +1406,10 @@ static int yylex (void)
 bool do_xyfunc(DataObj *d, double x1, double x2, double step, char *expr, lfPOINT **pts, long *npts, char *param)
 {
 	double x, y;
-	symrec *s;
+	symrec *sx, *sy;
 	lfPOINT *new_points;
 	long npoints = 0;
-	int length;
+	int length, res_mode = 0;
 	unsigned int hn_x = HashValue((unsigned char *)"x");
 	unsigned int hn_y = HashValue((unsigned char *)"y");
 
@@ -881,6 +1418,7 @@ bool do_xyfunc(DataObj *d, double x1, double x2, double step, char *expr, lfPOIN
 	if(!(new_points = (lfPOINT*)calloc((iround(fabs(x2-x1)/fabs(step))+2), sizeof(lfPOINT))))
 		return false;
 	if(d) curr_data = d;
+	push_parser();
 	init_table();
 	if(param) {
 		length = strlen(param);
@@ -896,21 +1434,34 @@ bool do_xyfunc(DataObj *d, double x1, double x2, double step, char *expr, lfPOIN
 		free(buffer);		buffer = 0L;
 		}		
 	length = strlen(expr);
-	buffer = expr;		s = putsym(hn_x, VAR, 0);
+	buffer = expr;		sx = putsym(hn_x, VAR);
 	for(x = x1; step > 0.0 ? x <= x2 : x >= x2; x += step) {
-		if(s = getsym(hn_x)){
-			s->value.var = x;	buff_pos = 0;
+		if(sx){
+			sx->SetValue(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;
+			switch (res_mode) {
+			case 1:
+				y = sy->GetValue();	break;
+			case 2:
+				y = line_res.value;	break;
+			default:
+				if(sy = getsym(hn_y)) {
+					y = sy->GetValue();	res_mode = 1;
+					}
+				else {
+					y = line_res.value;	res_mode = 2;
+					}
+				break;
+				}
+			new_points[npoints].fx = (getsym(hn_x))->GetValue();
 			new_points[npoints++].fy = y;
 			}
 		}
 	*pts = new_points;	*npts = npoints;
 	clear_table();
+	pop_parser();
 	if(curr_data) {
 		curr_data->Command(CMD_CLEAR_ERROR, 0L, 0L);
 		curr_data->Command(CMD_REDRAW, 0L, 0L);
@@ -918,10 +1469,68 @@ bool do_xyfunc(DataObj *d, double x1, double x2, double step, char *expr, lfPOIN
 	return true;
 }
 
+bool do_func3D(DataObj *d, double x1, double x2, double xstep, double z1, double z2, double zstep, 
+	char *expr, char *param)
+{
+	int length, nr, nc, r, c, res_mode=0;
+	symrec *sx, *sz, *sy;
+	double x, y, z;
+	unsigned int hn_x = HashValue((unsigned char *)"x");
+	unsigned int hn_y = HashValue((unsigned char *)"y");
+	unsigned int hn_z = HashValue((unsigned char *)"z");
+
+	if(!d || x2 <= x1 || z2 <= z1 || xstep <= 0.0 || zstep <= 0.0) return false;
+	push_parser();
+	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;
+	sx = putsym(hn_x, VAR);		sz = putsym(hn_z, VAR);
+	nr = iround((z2-z1)/zstep)+1;	nc = iround((x2-x1)/xstep)+1;
+	d->Init(nr, nc);
+	for(r = 0, x = x1; r < nr; r++, x += xstep) {
+		for(c = 0, z = z1; c < nc; c++, z+= zstep) {
+			sx->SetValue(x);	sz->SetValue(z);	buff_pos = 0;
+			do {
+				yyparse();
+				}while(buff_pos < length);
+			switch (res_mode) {
+			case 1:
+				y = sy->GetValue();	break;
+			case 2:
+				y = line_res.value;	break;
+			default:
+				if(sy = getsym(hn_y)) {
+					y = sy->GetValue();	res_mode = 1;
+					}
+				else {
+					y = line_res.value;	res_mode = 2;
+					}
+				break;
+				}
+			d->SetValue(r, c, y);
+			}
+		} 
+	clear_table();
+	pop_parser();
+	return true;
+}
+
 anyResult *do_formula(DataObj *d, char *expr)
 {
 	int length;
-	static anyResult ret;
+	static anyResult ret, *pret = 0L;
 
 	if(d) curr_data = d;
 	ret.type = ET_ERROR;		ret.text = 0L;
@@ -932,22 +1541,26 @@ anyResult *do_formula(DataObj *d, char *expr)
 		pop_parser();
 		return &ret;
 		}
-	strcpy(buffer, expr);	buffer[length++] = ';';
+	strcpy(buffer, expr);	if(buffer[length-1] != ';') buffer[length++] = ';';
 	buffer[length] = 0;	buff_pos = 0;
 	do {
 		yyparse();
 		}while(buff_pos < length);
+	ret.type = ET_ERROR;		ret.text = 0L;
 	if(curr_data && last_error) {
-		curr_data->Command(CMD_ERROR, last_error = 0L, 0L);
-		return &ret;
+		if(!(strcmp(last_error, "parse error"))) curr_data->Command(CMD_ERROR, 0L, 0L);
+		if(last_err_desc) pret = &line_res;
+		else pret = &ret;
 		}
+	else pret = &line_res;
+	last_error = last_err_desc = 0L;
 	free(buffer);		buffer = 0L;
 	clear_table();
 	pop_parser();
-	return &line_res;
+	return pret;
 }
 
-bool MoveFormula(DataObj *d, char *of, char *nf, int dx, int dy)
+bool MoveFormula(DataObj *d, char *of, char *nf, int dx, int dy, int r0, int c0)
 {
 	int length, tok, pos, i;
 	char *res, desc1[2], desc2[2];
@@ -971,43 +1584,55 @@ bool MoveFormula(DataObj *d, char *of, char *nf, int dx, int dy)
 			}
 		else switch(tok) {
 			case NUM:
-				pos += sprintf(res+pos, "%g ", yylval.val);
+				pos += sprintf(res+pos, "%g", yylval.val);
 				break;
-			case FNCT:
+			case FNCT:	case FUNC2:	case AFNCT:	case SFNCT:
 				pos += sprintf(res+pos, "%s", curr_sym->name);
 				break;
-			case COLR:
+			case COLR:			case COLC:
 				pos += sprintf(res+pos, ":");
 				break;
-			case COLC:
-				pos += sprintf(res+pos, ": ");
+			case PSEP:
+				pos += sprintf(res+pos, ";");
 				break;
 			case CLVAL:
-				pos += sprintf(res+pos, "$$ ");
+				pos += sprintf(res+pos, "$$");
 				break;
 			case CLAUSE:
 				pos += sprintf(res+pos, " where ");
 				break;
 			case VAR:
+				curr_sym->InitSS();
 				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);
+						Int2ColLabel(desc1[0] || curr_sym->col < c0 ? curr_sym->col : curr_sym->col+dx >=0 ?
+						curr_sym->col+dx > c0 ? curr_sym->col+dx : c0 : 0, false),
+						desc2, desc2[0] || curr_sym->row < r0 ? curr_sym->row+1 : curr_sym->row + dy >= 0 ? 
+						curr_sym->row+dy > r0 ? curr_sym->row+1+dy : r0 : 1);
 					}
 				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 SER:
+				pos += sprintf(res+pos, "..");
+				break;
+			case INC:
+				pos += sprintf(res+pos, "++");
+				break;
+			case DEC:
+				pos += sprintf(res+pos, "--");
+				break;
 			case PI:
-				pos += sprintf(res+pos, "pi ");
+				pos += sprintf(res+pos, "pi");
 				break;
 			case E:
-				pos += sprintf(res+pos, "e ");
+				pos += sprintf(res+pos, "e");
 				break;
 			case AND:
 				pos += sprintf(res+pos, " && ");
@@ -1022,16 +1647,25 @@ bool MoveFormula(DataObj *d, char *of, char *nf, int dx, int dy)
 				pos += sprintf(res+pos, " != ");
 				break;
 			case GT:
-				pos += sprintf(res+pos, " > ");
+				pos += sprintf(res+pos, ">");
 				break;
 			case GE:
-				pos += sprintf(res+pos, " >= ");
+				pos += sprintf(res+pos, ">=");
 				break;
 			case LT:
-				pos += sprintf(res+pos, " < ");
+				pos += sprintf(res+pos, "<");
 				break;
 			case LE:
-				pos += sprintf(res+pos, " <= ");
+				pos += sprintf(res+pos, "<=");
+				break;
+			case IF: 
+				pos += sprintf(res+pos, "if");
+				break;
+			case ELSE: 
+				pos += sprintf(res+pos, "else");
+				break;
+			case BLOCK:
+				pos += sprintf(res+pos, "{%s}", yylval.text && yylval.text[0] ? yylval.text : "");
 				break;
 			}
 		}while(buff_pos < length);
@@ -1049,21 +1683,21 @@ static void fcurve(double x, double z, double **a, double *y, double dyda[], int
 {
 	int i, length;
 	double tmp, y1, y2;
-	symrec *symx, *s=0L;
+	symrec *symx, *sy=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);
+	if(!(symx = getsym(hn_x))) symx = putsym(hn_x, VAR);
 	//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;
+	symx->SetValue(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(sy = getsym(hn_y)) *y = sy->GetValue();
+	else *y = line_res.value;
 	if(*y == HUGE_VAL || *y == -HUGE_VAL) {
 		for(i = 0, *y = 0.0; i < ma; dyda[i++] = 0.0);
 		return;
@@ -1075,11 +1709,11 @@ static void fcurve(double x, double z, double **a, double *y, double dyda[], int
 			*parval[i] = tmp*.995;
 			buff_pos = 0;
 			do {	yyparse();	}while(buff_pos < length);
-			y1 = s ? s->value.var : line_result;
+			y1 = sy ? sy->GetValue() : line_res.value;
 			*parval[i] = tmp*1.005;
 			buff_pos = 0;
 			do {	yyparse();	}while(buff_pos < length);
-			y2 = s ? s->value.var : line_result;
+			y2 = sy ? sy->GetValue() : line_res.value;
 			*parval[i] = tmp;
 			dyda[i] = (y2-y1)*100.0/tmp;
 			}
@@ -1164,7 +1798,7 @@ int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr,
 	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;
+		parsym[i] = csr;	parval[i] = &csr->var;
 		}
 	//do iteratations to optimize fit
 	lista = (int*)malloc(sizeof(int)*nparam);
@@ -1191,7 +1825,7 @@ int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr,
 			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);
+		l += sprintf(tmp_txt+j, "%s%s=%g;", j && k ? " " : "", parsym[i]->name, parsym[i]->GetValue());
 		j += l;			k += l;
 		}
 	free(*par);	*par = strdup(tmp_txt);
@@ -1242,6 +1876,3 @@ int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr,
 
 
 
-
-
-
diff --git a/rlp_math.cpp b/rlp_math.cpp
index 9420d89..7428003 100755
--- a/rlp_math.cpp
+++ b/rlp_math.cpp
@@ -21,6 +21,8 @@
 #include <stdlib.h>
 
 #define SWAP(a,b) {double temp=(a);(a)=(b);(b)=temp;}
+#define _PREC 1.0e-12
+
 static char *MRQ_error = 0L;
 
 //---------------------------------------------------------------------------
@@ -230,6 +232,7 @@ void SortArray(int n, double *vals)
 	int l, j, ir, i;
 	double rra, *ra = vals-1;
 
+	if(n < 2 || !vals) return;
 	l=(n >> 1) + 1;				ir = n;
 	for( ; ; ) {
 		if(l > 1) rra = ra[--l];
@@ -250,6 +253,99 @@ void SortArray(int n, double *vals)
 		ra[i] = rra;
 		}
 }
+
+//sorts array v1 making the corresponding rearrangement of v2
+void SortArray2(int n, double *v1, double *v2)
+{
+	int l, j, ir, i;
+	double rra, rrb, *ra = v1-1, *rb = v2-1;
+
+	if(n < 2 || !v1 || !v2) return;
+	l=(n >> 1) + 1;				ir = n;
+	for( ; ; ) {
+		if(l > 1) {
+			rra = ra[--l];		rrb = rb[l];
+			}
+		else {
+			rra = ra[ir];		rrb = rb[ir];
+			ra[ir] = ra[1];		rb[ir] = rb[1];
+			if(--ir == 1) {
+				ra[1] = rra;	rb[1] = rrb;
+				return;
+				}
+			}
+		i = l;					j = l << 1;
+		while (j <= ir) {
+			if (j < ir && ra[j] < ra[j+1]) ++j;
+			if (rra < ra[j]) {
+				ra[i] = ra[j];	rb[i] = rb[j];
+				j += (i=j);
+				}
+			else j = ir + 1;
+			}
+		ra[i] = rra;			rb[i] = rrb;
+		}
+}
+
+//Use heap sort to sort elements of an xy array
+void SortFpArray(int n, lfPOINT *vals)
+{
+	int l, j, ir, i;
+	lfPOINT rra, *ra = vals-1;
+
+	if(n < 2) return;
+	l=(n >> 1) + 1;					ir = n;
+	for( ; ; ) {
+		if(l > 1) {
+			rra.fx = ra[--l].fx; rra.fy = ra[l].fy;
+			}
+		else {
+			rra.fx = ra[ir].fx;		rra.fy = ra[ir].fy;
+			ra[ir].fx = ra[1].fx;	ra[ir].fy = ra[1].fy;	
+			if(--ir == 1) {
+				ra[1].fx = rra.fx;	ra[1].fy = rra.fy;
+				return;
+				}
+			}
+		i = l;					j = l << 1;
+		while (j <= ir) {
+			if (j < ir && ra[j].fx < ra[j+1].fx) ++j;
+			if (rra.fx < ra[j].fx) {
+				ra[i].fx = ra[j].fx;	ra[i].fy = ra[j].fy;
+				j += (i=j);
+				}
+			else j = ir + 1;
+			}
+		ra[i].fx = rra.fx;				ra[i].fy = rra.fy;
+		}
+}
+
+//---------------------------------------------------------------------------
+// Cubic Spline Interpolation
+// Ref.: W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1989), 
+//    Numerical Rcipies in C. The Art of Scientific Computing, 
+//    Cambridge University Press, ISBN 0-521-35465, pp. 96 ff.
+void spline(lfPOINT *v, int n, double *y2)
+{
+	int i, k;
+	double p, qn, sig, un, *u;
+
+	u = (double *)malloc(n * sizeof(double));
+	y2[0] = u[0] = 0.0;
+	for(i = 1; i < (n-1); i++) {
+		sig = (v[i].fx-v[i-1].fx)/(v[i+1].fx-v[i-1].fx);
+		p = sig*y2[i-1]+2.0;			y2[i]=(sig-1.0)/p;
+		u[i]=(v[i+1].fy-v[i].fy)/(v[i+1].fx-v[i].fx)-(v[i].fy-v[i-1].fy)/(v[i].fx-v[i-1].fx);
+		u[i]=(6.0*u[i]/(v[i+1].fx-v[i-1].fx)-sig*u[i-1])/p;
+		}
+	qn = un = 0.0;
+	y2[n-1] = (un - qn * u[n-2])/(qn*y2[n-2]+1.0);
+	for(k = n-2; k >= 0; k--) {
+		y2[k] = y2[k]*y2[k+1]+u[k];
+		}
+	free(u);
+}
+
 //---------------------------------------------------------------------------
 // Special Functions
 // Ref.: W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1989), 
@@ -259,14 +355,12 @@ void SortArray(int n, double *vals)
 // The Gamma Function: return the ln(G(xx)) for xx > 0
 double gammln(double xx)
 {
-	double x, tmp, ser, pi=3.14159265358979;
+	double x, tmp, ser;
 	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))));
+	if(xx < 0.0) return 0.0;
 	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;
@@ -289,6 +383,85 @@ double factrl(int n)
 	return a[n];
 }
 
+//returns the incomplete gamma function evaluated by its series representation
+void gser(double *gamser, double a, double x, double *gln)
+{
+	int n;
+	double sum, del, ap;
+
+	*gln = gammln(a);
+	if(x <= 0) {
+		*gamser = 0.0;			return;
+		}
+	else {
+		ap = a;					del = sum = 1.0/a;
+		for(n = 1; n <= 100; n++) {
+			ap += 1.0;			del *= x/ap;		sum += del;
+			if(fabs(del) <= fabs(sum) * _PREC) {
+				*gamser = sum * exp(-x + a * log(x)-(*gln));
+				return;
+				}
+			}
+		// maximum number of iterations exceeded
+		*gamser = sum * exp(-x + a * log(x)-(*gln));
+		}
+
+}
+
+//returns the incomplete gamma function evaluated by its continued fraction representation
+void gcf(double *gammcf, double a, double x, double *gln)
+{
+	int n;
+	double gold=0.0, g, fac=1.0, b1=1.0, b0=0.0, anf, ana, an, a1, a0=1.0;
+
+	*gln=gammln(a);		a1=x;
+	for(n=1; n <= 100; n++) {
+		an = (double)n;			ana = an -a;		a0 = (a1 + a0 * ana) * fac;
+		b0 = (b1 + b0 * ana) *fac;					anf = an * fac;
+		a1 = x * a0 + anf * a1;						b1 = x * b0 + anf * b1;
+		if(a1) {
+			fac = 1.0 / a1;							g = b1 * fac;
+			if(fabs((g-gold)/g) <= _PREC) {
+				*gammcf = exp(-x + a * log(x) -(*gln)) * g;
+				return;
+				}
+			gold = g;
+			}
+		}
+	// maximum number of iterations exceeded
+	*gammcf = exp(-x + a * log(x) -(*gln)) * gold;
+}
+
+//returns the incomplete gamma function P(a,x)
+double gammp(double a, double x)
+{
+	double gamser, gammcf, gln;
+
+	if(x < 0.0 || a <= 0.0) return 0.0;
+	if(x < (a+1.0)) {
+		gser(&gamser, a, x, &gln);			return gamser;
+		}
+	else {
+		gcf(&gammcf, a, x, &gln);			return 1.0-gammcf;
+		}
+	return 0.0;
+}
+
+//returns the complementary incomplete gamma function Q(a,x)
+double gammq(double a, double x)
+{
+	double gamser, gammcf, gln;
+
+	if(x < 0.0 || a <= 0.0) return 0.0;
+	if(x < (a+1.0)) {
+		gser(&gamser, a, x, &gln);			return 1.0-gamser;
+		}
+	else {
+		gcf(&gammcf, a, x, &gln);			return gammcf;
+		}
+	return 0.0;
+}
+
 //continued fraction for incomplete beta function, used by betai()
 double betacf(double a, double b, double x)
 {
@@ -305,7 +478,7 @@ double betacf(double a, double b, double x)
 		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
+		if(fabs(az-aold) <= (_PREC * fabs(az))) return az;	//success: return
 		}
 	return az;												//fail: iterations exceeded
 }
@@ -323,17 +496,147 @@ double betai(double a, double b, double x)
 	else return 1.0 - bt * betacf(b, a, 1.0 - x)/b;
 }
 
-double t_dist(double t, double df)
+//the binomial coefficient
+double bincof(double n, double k)
+{
+	if(n<0 || k<0 || k > n) return 0.0;
+	return exp(gammln(n+1.0) - gammln(k+1.0) - gammln(n-k+1.0));
+}
+
+//the cumulative binomial distribution
+double binomdistf(double k, double n, double p)
+{
+	if(k > n || n < 0.0 || p < 0.0 || p >1.0) return 0.0;
+	return betai(n-k, k+1, p);
+}
+
+//the beta function
+double betaf(double z, double w)
+{
+	return exp(gammln(z)+gammln(w)-gammln(z+w));
+}
+
+//the error function: not all compilers have a built in erf()
+double errf(double x)
+{
+	return x < 0.0 ? -gammp(0.5, x*x) : gammp(0.5, x*x);
+}
+
+//the complementary error function
+double  errfc(double x)
+{
+	double t, z, ans;
+
+	z = fabs(x);
+	t = 1.0/(1.0+0.5*z);
+	ans = t * exp(-z*z-1.26551223+t*(1.00002368+t*(0.37409196+t*(0.09678418+
+		t*(-0.18628806+t*(0.27886807+t*(-1.13520398+t*(1.48851587+
+		t*(-0.82215223+t*0.17087277)))))))));
+	return x >= 0.0 ? ans : 2.0-ans;
+}
+
+//cumulative normal distribution
+double norm_dist(double x, double m, double s)
+{
+	return 0.5 + errf((x - m)/(s * _SQRT2))/2.0;
+}
+
+//chi square distribution
+double chi_dist(double x, double df, double)
+{
+	return gammq(df/2.0, x/2);
+}
+
+//t-distribution
+double t_dist(double t, double df, double)
 {
 	return betai(df/2.0, 0.5, (df/(df+t*t)));
 }
 
+//poisson distribution
+double pois_dist(double x, double m, double)
+{
+	return gammq(x+1.0, m);
+}
+
+//f-distribution
 double f_dist(double f, double df1, double df2)
 {
 	return betai(df2/2.0, df1/2.0, df2/(df2+df1*f));
 }
 
 //---------------------------------------------------------------------------
+// Inverse of statitistical functions:
+//    Use a combination of the Newton-Raphson method and bisection
+// 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. 273 ff.
+
+// funcd supplies the function value fn and the derivative df of the function sf at x
+void funcd(double x, double *fn, double *df, double (*sf)(double, double, double), 
+		   double df1, double df2, double p)
+{
+	double y1, y2;
+
+	*fn = (sf)(x, df1, df2);
+	y1 = (sf)(x * 0.995, df1, df2);
+	y2 = (sf)(x * 1.005, df1, df2);
+	*df = (y2-y1)*100.0/x;
+	*fn = *fn - p;
+}
+
+//distinv does actual bisection and Newton-Raphson root finding
+double distinv(double (*sf)(double, double, double), double df1, double df2, double p, double x0)
+{
+	int j;
+	double df, dx, dxold, f, fh, fl;
+	double swap, temp, xh, xl, rts; 
+	double x1 = 0.0001, x2 = 10000;
+	char info[80];
+
+	funcd(x1, &fl, &df, sf, df1, df2, p);
+	funcd(x2, &fh, &df, sf, df1, df2, p);
+	for(j = 0; fl*fh >= 0 && j < 10; j++) {
+		x1 /= 2.0;		x2 *= 2.0;
+		funcd(x1, &fl, &df, sf, df1, df2, p);
+		funcd(x2, &fh, &df, sf, df1, df2, p);
+		}
+	if(fl*fh >= 0) {
+		sprintf(info, "Value for inverse distribution\nmust be between %g and %g!", x1, x2);
+		InfoBox(info);
+		return 0.0;
+		}
+	if(fl < 0.0) {
+		xl = x1;		xh = x2;
+		}
+	else {
+		xh = x1;		xl = x2;
+		swap = fl;		fl = fh;	fh = swap;
+		}
+	rts = x0;	dxold = fabs(x2-x1);	dx = dxold;
+	funcd(rts, &f, &df, sf, df1, df2, p);
+	for(j = 1; j <= 100; j++) {
+        if((((rts-xh)*df-f)*((rts-xl)*df-f) >= 0.0) || (fabs(2.0*f) > fabs(dxold * df))) {
+			dxold = dx;		dx = 0.5 * (xh-xl);		rts = xl + dx;
+			if(xl == rts) return rts;
+			}
+		else {
+			dxold = dx;		dx = f/df;		temp = rts;		rts -= dx;
+			if(temp == rts) return rts;
+			}
+		if(fabs(dx) < _PREC) return rts;
+		funcd(rts, &f, &df, sf, df1, df2, p);
+		if(f < 0.0) {
+			xl = rts;	fl = f;
+			}
+		else {
+			xh = rts;	fh = f;
+			}
+		}
+	return 0.0;
+}
+
+//---------------------------------------------------------------------------
 //some statistical basics
 //do quartiles, median of data
 void d_quartile(int n, double *v, double *q1, double *q2, double *q3)
@@ -409,3 +712,204 @@ double d_hmean(int n, double *v)
 		}
 	return (n/sum);
 }
+
+//---------------------------------------------------------------------------
+// Pearsons linear correlation
+// 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. 503 ff.
+double d_pearson(double *x, double *y, int n, char *dest, DataObj *data)
+{
+	int j, r, c;
+	double yt, xt, t, df, res[3];
+	double syy=0.0, sxy=0.0, sxx=0.0, ay=0.0, ax=0.0;
+	AccRange *rD;
+
+	for(j = 0;	j < n; j++) {				// find means
+		ax += x[j];			ay += y[j];
+		}
+	ax /= n;			ay /= n;
+	for(j = 0; j < n; j++) {				// correlation
+		xt = x[j] - ax;		yt = y[j] - ay;
+		sxx += xt*xt;		syy += yt*yt;		sxy += xt * yt;
+		}
+	res[0] = sxy/sqrt(sxx*syy);		//pearsons r
+	if(dest) {
+		res[1] = 0.5 * log((1.0+res[0]+_PREC)/(1.0-res[0]+_PREC));	//Fishers z-transform
+		df = n-2;
+		t = res[0]*sqrt(df/((1.0-res[0]+_PREC)*(1.0+res[0]+_PREC)));	//Student's t
+		res[2] = betai(0.5*df, 0.5, df/(df+t*t));					//probability
+		}
+	if((dest) && (data) && (rD = new AccRange(dest))) {
+		rD->GetFirst(&c, &r);
+		for(j = 0; j < 3 && rD->GetNext(&c, &r); j++) {
+			data->SetValue(r, c, res[j]);
+			}
+		data->Command(CMD_UPDATE, 0L, 0L);
+		delete rD;
+		}
+	return res[0];
+}
+
+//---------------------------------------------------------------------------
+// Spearman rank-order correlation
+// Ref.: W.H. Press, B.P. Flannery, S.A. Teukolsky, W.T. Vetterling (1989), 
+//    Numerical Recipies in C. The Art of Scientific Computing, 
+//    Cambridge University Press, ISBN 0-521-35465, pp. 507 ff.
+
+//Given a sorted array w, crank replaces the elements by their rank
+void crank(int n, double *w0, double *s)
+{
+	int j=1, ji, jt;
+	double t, rank, *w = w0-1;
+
+	*s = 0.0;
+	while (j < n) {
+		if(w[j+1] != w[j]) {
+			w[j] = j;		++j;
+			}
+		else {
+			for(jt = j+1; jt <= n; jt++)
+				if(w[jt] != w[j]) break;
+			rank = 0.5 * (j+jt-1);
+			for(ji = j; ji <= (jt-1); ji++) w[ji] = rank;
+			t = jt -j;
+			*s += t*t*t -t;
+			j = jt;
+			}
+		}
+	if(j == n) w[n] = n;
+}
+
+//the actual rank correlation
+double d_spearman(double *x, double *y, int n, char *dest, DataObj *data)
+{
+	int j, r, c;
+	double vard, t, sg, sf, fac, en3n, en, df, aved, tmp;
+	double res[5];
+	AccRange *rD;
+
+	SortArray2(n, x, y);		crank(n, x, &sf);
+	SortArray2(n, y, x);		crank(n, y, &sg);
+	for(j = 0, res[0] = 0.0; j < n; j++) res[0] += ((tmp = (x[j]-y[j]))*tmp);
+	en = n;						en3n = en*en*en -en;
+	aved = en3n/6.0 - (sf+sg)/12.0;
+	fac = (1.0-sf/en3n)*(1.0-sg/en3n);
+	vard = ((en-1.0)*en*en*((en+1.0)*(en+1.0))/36.0)*fac;
+	vard = ((en-1.0)*en*en*((tmp = (en+1.0))*tmp)/36.0)*fac;
+	res[1] = (res[0]-aved)/sqrt(vard);
+	res[2] = errfc(fabs(res[1])/_SQRT2);
+	res[3] = (1.0-(6.0/en3n)*(res[0]+0.5*(sf+sg)))/fac;
+	t = res[3]*sqrt((en-2.0)/((res[3]+1.0)*(1.0-res[3])));
+	df = en-2.0;
+    res[4] = betai(0.5*df, 0.5, df/(df+t*t));
+	if((dest) && (data) && (rD = new AccRange(dest))) {
+		rD->GetFirst(&c, &r);
+		for(j = 0; j < 5 && rD->GetNext(&c, &r); j++) {
+			data->SetValue(r, c, res[j]);
+			}
+		data->Command(CMD_UPDATE, 0L, 0L);
+		delete rD;
+		}
+	return res[3];
+}
+
+//linear regression
+double d_regression(double *x, double *y, int n, char *dest, DataObj *data)
+{
+	double sx, sy, dx, dy, sxy, sxx, syy, sdy;
+	double res[10];		// slope, intercept, mean x, mean y, SE of slope, 
+						//   variance(x), variance(y), variance(fit), F of regression 
+	int i, j, r, c;
+	AccRange *rD;
+
+	if(n < 2) return 0.0;
+	for(i = 0, 	sx = sy = 0.0; i < n; i++) {
+		sx += x[i];			sy += y[i];
+		}
+	res[2] = sx /n;			res[3] = sy/n;
+	sxy = sxx = syy = 0.0;
+	for(i = 0; i < n; i++) {
+		dx = x[i]-res[2];	dy = y[i]-res[3];
+		sxx += (dx*dx);		syy += (dy*dy);		sxy += (dx*dy);
+		}
+	res[0] = sxy / sxx;		res[1] = res[3] - res[0] * res[2];
+	for(i = 0, sdy = 0.0; i < n; i++) {
+		dy = y[i] - (res[1] + x[i] *res[0]);
+		sdy += (dy * dy);
+		}
+	sdy = sdy/(n-2);		res[4] = sqrt(sdy/sxx);
+	res[5] = sxx/(n-1);		res[6] = syy/(n-1);			res[7] = sdy;
+	res[8] = sxy/sdy*sxy/sxx;
+	if((dest) && (data) && (rD = new AccRange(dest))) {
+		rD->GetFirst(&c, &r);
+		for(j = 0; j < 9 && rD->GetNext(&c, &r); j++) {
+			data->SetValue(r, c, res[j]);
+			}
+		data->Command(CMD_UPDATE, 0L, 0L);
+		delete rD;
+		}
+	return n;
+}
+
+//t-test
+double d_ttest(double *x, double *y, int n1, int n2, char *dest, DataObj *data)
+{
+	int i, r, c;
+	double sx, sy, mx, my, d, df, p;
+	double res[9];			// mean1, SD1, n1, mean2, SD2, n2, p if variances equal,
+	AccRange *rD;			//    corrected df, corrected p
+
+	for(i=0, sx = 0.0; i < n1; sx += x[i], i++);				mx = sx/n1;
+	for(i=0, sy = 0.0; i < n2; sy += y[i], i++);				my = sy/n2;
+	for(i=0, sx = 0.0; i < n1; sx += ((d=(x[i]-mx))*d), i++);
+	for(i=0, sy = 0.0; i < n2; sy += ((d=(y[i]-my))*d), i++);
+    d = ((sx+sy)/(n1+n2-2)) * ((double)(n1+n2)/(double)(n1*n2));
+	d = (mx-my)/sqrt(d);										//Student's t
+
+	//Welch's correction for differences in variance
+	df = (sx/(double)n1)*(sx/(double)n1)/(double)(n1+1)+(sy/(double)n2)*(sy/(double)n2)/(double)(n2+1);
+	df = (sx/(double)n1+sy/(double)n2)*(sx/(double)n1+sy/(double)n2)/df;
+	df -= 2.0;
+	p = betai(df/2.0, 0.5, (df/(df+d*d)));
+	if((dest) && (data) && (rD = new AccRange(dest))) {
+		res[0] = mx;	res[1] = sqrt(sx/(double)(n1-1));	res[2] = n1;
+		res[3] = my;	res[4] = sqrt(sy/(double)(n2-1));	res[5] = n2;
+		res[7] = df;	df = (n1-1) + (n2-1);	res[6] = betai(df/2.0, 0.5, (df/(df+d*d)));
+		res[8] = p;
+		rD->GetFirst(&c, &r);
+		for(i = 0; i < 9 && rD->GetNext(&c, &r); i++) {
+			data->SetValue(r, c, res[i]);
+			}
+		data->Command(CMD_UPDATE, 0L, 0L);
+		delete rD;
+		}
+	return p;
+}
+
+//f-test
+double d_ftest(double *x, double *y, int n1, int n2, char *dest, DataObj *data)
+{
+	int i, r, c;
+	double sx, sy, mx, my, d, df1, df2, p;
+	double res[6];			// mean1, SD1, n1, mean2, SD2, n2
+	AccRange *rD;
+
+	for(i=0, sx = 0.0; i < n1; sx += x[i], i++);				mx = sx/n1;
+	for(i=0, sy = 0.0; i < n2; sy += y[i], i++);				my = sy/n2;
+	for(i=0, sx = 0.0; i < n1; sx += ((d=(x[i]-mx))*d), i++);	sx /= (n1-1);
+	for(i=0, sy = 0.0; i < n2; sy += ((d=(y[i]-my))*d), i++);	sy /= (n2-1);
+	d = sx/sy;		df1 = n1-1;		df2 = n2-1;
+	p= 2.0 * betai(df2/2.0, df1/2.0, df2/(df2+df1*d));
+	if((dest) && (data) && (rD = new AccRange(dest))) {
+		res[0] = mx;	res[1] = sqrt(sx);	res[2] = n1;
+		res[3] = my;	res[4] = sqrt(sy);	res[5] = n2;
+		rD->GetFirst(&c, &r);
+		for(i = 0; i < 6 && rD->GetNext(&c, &r); i++) {
+			data->SetValue(r, c, res[i]);
+			}
+		data->Command(CMD_UPDATE, 0L, 0L);
+		delete rD;
+		}
+	return p;
+}
diff --git a/rlplot.cpp b/rlplot.cpp
index 0de7a6b..0c1fdd5 100755
--- a/rlplot.cpp
+++ b/rlplot.cpp
@@ -95,13 +95,14 @@ ssButton::ssButton(GraphObj *par, int x, int y, int w, int h):GraphObj(par, 0L)
 	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.type = FILL_NONE;			Fill.color = 0x00e8e8e8L;
 	Fill.scale = 1.0;				Fill.hatch = NULL;
 	TextDef.ColTxt = 0x00000000L;	TextDef.ColBg = 0x00ffffffL;
 	TextDef.fSize = 4.0;			TextDef.RotBL = TextDef.RotCHAR = 0.0;
 	TextDef.iSize = 0;				TextDef.Align = TXA_HLEFT | TXA_VTOP;
 	TextDef.Mode = TXM_TRANSPARENT;	TextDef.Style = TXS_NORMAL;
-	TextDef.Font = FONT_HELVETICA;	TextDef.text = 0L;
+	TextDef.Font = FONT_HELVETICA;	TextDef.text = 0L;
+	bSelected = false;
 }
 
 ssButton::~ssButton()
@@ -114,9 +115,8 @@ ssButton::DoPlot(anyOutput *o)
 {
 	POINT pts[3];
 
-	Line.color = 0x00000000L;
-	o->SetLine(&Line);
-	o->SetFill(&Fill);
+	Line.color = 0x00000000L;		Fill.color = bSelected ? 0x00ffffff : 0x00e8e8e8L;
+	o->SetLine(&Line);				o->SetFill(&Fill);
 	if(bLBdown){
 		o->oRectangle(rDims.left, rDims.top, rDims.right, rDims.bottom);
 		}
@@ -163,7 +163,12 @@ ssButton::Command(int cmd, void *tmpl, anyOutput *o)
 			strcpy((char*)tmpl, TextDef.text);
 			return true;
 			}
-		return false;
+		return false;
+	case CMD_SELECT:
+		if(tmpl && *((int*)tmpl)) bSelected = true;
+		else bSelected = false;
+		if(o) DoPlot(o);
+		return true;
 	case CMD_SETTEXT:
 		if(TextDef.text) free(TextDef.text);
 		if(tmpl) TextDef.text = strdup((char*)tmpl);
@@ -1029,14 +1034,12 @@ Symbol::DoPlot(anyOutput *target)
 		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:
+	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) {
+		if(atype == SYM_TRIAU || atype == SYM_TRIAUF) {		//patch by anonymous
 			pts[0].y = pts[2].y = pts[3].y = iy+target->un2iy(size*0.38878f);
 			pts[1].y = iy-target->un2iy(size*0.77756f);
 			}
@@ -1047,8 +1050,7 @@ Symbol::DoPlot(anyOutput *target)
 		target->oPolygon(pts, 4);
 		rx--; ry--;
 		break;
-	case SYM_DIAMOND:
-	case SYM_DIAMONDF:
+	case SYM_DIAMOND:	case SYM_DIAMONDF:
 		rx = target->un2ix(size/1.59588f);
 		ry = target->un2iy(size/1.59588f);
 		target->SetFill(&cf);
@@ -1061,8 +1063,7 @@ Symbol::DoPlot(anyOutput *target)
 		break;
 	case SYM_STAR:			//star is a combination of + and x symbols
 	case SYM_PLUS:			//draw a + sign
-	case SYM_HLINE:
-	case SYM_VLINE:
+	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;
@@ -1070,10 +1071,10 @@ Symbol::DoPlot(anyOutput *target)
 		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
+		if(atype != SYM_VLINE) target->oPolyline(pts, 2);
+		if(atype == SYM_VLINE){ rx = 2; break;}
+		if(atype == SYM_HLINE){ ry = 2; break;}
+		if(atype == SYM_PLUS) break;		//continue with x symbol for star
 	case SYM_CROSS:			//draw a x symbol
 		rx = target->un2ix(size/2.5);
 		ry = target->un2iy(size/2.5);
@@ -1139,14 +1140,11 @@ Symbol::Command(int cmd, void *tmpl, anyOutput *o)
 		//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->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;
+			SymTxt->Style = TXS_NORMAL;				SymTxt->Mode = TXM_TRANSPARENT;
+			SymTxt->Font = FONT_HELVETICA;			SymTxt->text = 0L;
 			}
 		if(!SymTxt) return false;
 		if(tmpl) {
@@ -1162,8 +1160,7 @@ Symbol::Command(int cmd, void *tmpl, anyOutput *o)
 		if(!SymTxt || !tmpl) return false;
 		memcpy(tmpl, SymTxt, sizeof(TextDEF));
 		return true;
-	case CMD_SYMTEXTDEF:
-	case CMD_SETTEXTDEF:
+	case CMD_SYMTEXTDEF:		case CMD_SETTEXTDEF:
 		if(!tmpl)return false;
 		if(SymTxt) tmptxt = SymTxt->text;
 		else tmptxt = 0L;
@@ -1171,12 +1168,11 @@ Symbol::Command(int cmd, void *tmpl, anyOutput *o)
 		memcpy(SymTxt, tmpl, sizeof(TextDEF));
 		SymTxt->text = tmptxt;
 		return true;
-	case CMD_SYM_RANGETEXT:
-	case CMD_RANGETEXT:
+	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);
+			for(i = 0, tmptxt[0] = 0; i <= idx; i++) ac->GetNext(&c, &r);
 			data->GetText(r, c, tmptxt, 500);
 			delete(ac);
 			}
@@ -1363,6 +1359,10 @@ Bubble::Command(int cmd, void *tmpl, anyOutput *o)
 		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(&BubbleLine, &BubbleFill);
+		break;
 	case CMD_MOUSE_EVENT:
 		mev = (MouseEvent *) tmpl;
 		switch (mev->Action) {
@@ -1429,15 +1429,28 @@ Bubble::Command(int cmd, void *tmpl, anyOutput *o)
 	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;
-			}
+	case CMD_AUTOSCALE:
+		return DoAutoscale(o);
 		break;
 		}
 	return false;
 }
+
+bool
+Bubble::DoAutoscale(anyOutput *o) 
+{
+	double dx, dy;
+
+	switch(type & 0x0f0) {
+	case BUBBLE_XAXIS:			case BUBBLE_YAXIS:
+		dx = dy = fs/2.0;		break;
+	case BUBBLE_UNITS:
+		dx = fPos.fx/20;		dy = fPos.fy/20;		break;
+		}
+	((Plot*)parent)->CheckBounds(fPos.fx+dx, fPos.fy-dy);
+	((Plot*)parent)->CheckBounds(fPos.fx-dx, fPos.fy+dy);
+	return true;
+}
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Bars are graphic objects
@@ -1471,6 +1484,7 @@ Bar::Bar(int src):GraphObj(0L, 0L)
 
 Bar::~Bar()
 {
+	if(mo) DelBitmapClass(mo);	mo = 0L;
 	Command(CMD_FLUSH, 0L, 0L);
 }
 
@@ -1600,6 +1614,19 @@ Bar::DoPlot(anyOutput *target)
 	else target->oRectangle(pts[0].x, pts[0].y, pts[1].x, pts[1].y, name);
 	SetMinMaxRect(&rDims, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
 }
+
+void
+Bar::DoMark(anyOutput *o, bool mark)
+{
+	if(mark){
+		memcpy(&mrc, &rDims, sizeof(RECT));
+		IncrementMinMaxRect(&mrc, 3);
+		mo = GetRectBitmap(&mrc, o);
+		o->CopyBitmap(mrc.left, mrc.top, mo, 0, 0, mrc.right-mrc.left, mrc.bottom - mrc.top, true);
+		o->UpdateRect(&mrc, false);
+		}
+	else RestoreRectBitmap(&mo, &mrc, o);
+}
 
 bool 
 Bar::Command(int cmd, void *tmpl, anyOutput *o)
@@ -1622,8 +1649,7 @@ Bar::Command(int cmd, void *tmpl, anyOutput *o)
 		switch (mev->Action) {
 		case MOUSE_LBUP:
 			if(IsInRect(&rDims, mev->x, mev->y) && !CurrGO) {
-				o->ShowMark(&rDims, MRK_INVERT);
-				CurrGO = this;
+				o->ShowMark(CurrGO = this, MRK_GODRAW);
 				return true;
 				}
 			break;
@@ -1746,17 +1772,20 @@ DataLine::DoPlot(anyOutput *target)
 	lfPOINT fip;
 	POINT pn, *tmppts;
 
-	if(!Values || nPntSet < 1) return;
+	if(!Values || nPntSet < 1) return;
+	if (nPntSet >= nPnt) nPntSet = nPnt-1;
 	if(mo) DelBitmapClass(mo);		mo = 0L;
-	if(pts) free(pts);
-	if(type & 0xff) pts = (POINT *)malloc(sizeof(POINT)*(nPntSet+2)*2);
+	if(pts) free(pts);				pts = 0L;
+	if((type & 0xff) == 9 || (type & 0xff) == 10) //splines
+		pts = (POINT *)malloc(sizeof(POINT)*1000);
+	else if(type & 0xff) pts = (POINT *)malloc(sizeof(POINT)*(nPntSet+2)*2);
 	else pts = (POINT *)malloc(sizeof(POINT)*(nPntSet+2));
-	if(!pts)return;
+	if(!pts) return;
 	if(max.fx > min.fx && max.fy > min.fy) dirty = false;
-	else if(dirty)Command(CMD_AUTOSCALE, 0L, target);
+	else if(dirty) Command(CMD_AUTOSCALE, 0L, target);
 	cp = 0;
-	switch(type & 0xf) {
-	case 0:
+	switch(type & 0x0f) {
+	case 0:		default:
 		for (i = 0; i <= nPntSet; i++){
 			target->fp2fip(Values+i, &fip);
 			pn.x = iround(fip.fx);		pn.y = iround(fip.fy);
@@ -1846,6 +1875,9 @@ DataLine::DoPlot(anyOutput *target)
 			pn.y += (pn.y - iround(fip.fy))>>1;
 			AddToPolygon(&cp, pts, &pn);
 			}
+		break;
+	case 9:		case 10:
+		DrawSpline(target);
 		break;
 		}
 	if(cp < 2) return;
@@ -1890,7 +1922,7 @@ DataLine::Command(int cmd, void *tmpl, anyOutput *o)
 		mev = (MouseEvent *) tmpl;
 		switch (mev->Action) {
 		case MOUSE_LBUP:
-			if(!IsInRect(&rDims, p1.x= mev->x, p1.y= mev->y) || CurrGO || !o || nPntSet <2)
+			if(!IsInRect(&rDims, p1.x= mev->x, p1.y= mev->y) || CurrGO || !o || nPntSet <1)
 				return false; 
 			if(isPolygon && IsInPolygon(&p1, pts, cp)) bFound = true;
 			if(bFound || IsCloseToPL(p1,pts,cp)) 
@@ -1900,7 +1932,12 @@ DataLine::Command(int cmd, void *tmpl, anyOutput *o)
 	case CMD_SET_DATAOBJ:
 		Id = isPolygon ? GO_DATAPOLYGON : GO_DATALINE;
 		data = (DataObj*)tmpl;
-		return true;
+		return true;
+	case CMD_MRK_DIRTY:
+		dirty= true;
+	case CMD_REDRAW:
+		if(parent) return parent->Command(cmd, tmpl, 0L);
+		return false;
 	case CMD_LEGEND:
 		if(tmpl && ((GraphObj*)tmpl)->Id == GO_LEGEND) {
 			if(Id == GO_DATALINE) ((Legend*)tmpl)->HasFill(&LineDef, 0L);
@@ -1909,11 +1946,13 @@ DataLine::Command(int cmd, void *tmpl, anyOutput *o)
 	case CMD_SET_LINE:
 		if(tmpl) memcpy(&LineDef, tmpl, sizeof(LineDEF));
 		return true;
-	case CMD_UPDATE:
+	case CMD_UPDATE:
+		Undo.DataMem(this, (void**)&Values, nPnt * sizeof(lfPOINT), &nPnt, UNDO_CONTINUE);
+		Undo.ValLong(this, &nPntSet, UNDO_CONTINUE);
 		SetValues();
 		return true;
 	case CMD_AUTOSCALE:
-		if(nPntSet < 2 || !Values) return false;
+		if(nPntSet < 1 || !Values) return false;
 		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
 			if(dirty) {
 				min.fx = max.fx = Values[0].fx;	min.fy = max.fy = Values[0].fy;
@@ -1941,7 +1980,7 @@ DataLine::SetValues()
 	int i, j, k, l, m, n;
 	double x, y;
 	char *yref1 = 0L, *yref2 = 0L;
-	lfPOINT *tmpValues = 0L;
+	lfPOINT *tmpValues = Values;
 
 	if(!ssXref || !ssYref) return;
 	if(!(yref1 = strdup(ssYref)))return;
@@ -1953,7 +1992,6 @@ DataLine::SetValues()
 			}
 		}
 	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){
@@ -1962,7 +2000,7 @@ DataLine::SetValues()
 		return;
 		}
 	if(yref2 &&((nPnt = rX->CountItems()) == (rY1->CountItems()))) {
-		if(!(Values = (lfPOINT *)calloc(nPnt*2+2, sizeof(lfPOINT)))) return; 
+		if(!(Values = (lfPOINT *)realloc(Values, ((nPnt*2+2) * sizeof(lfPOINT))))) return; 
 		if(!(rY2 = new AccRange(yref2))) {
 			if(yref1) free(yref1);		if(yref2) free(yref2);
 			if(rX) delete(rX);	if(rY1) delete(rY1);
@@ -1983,7 +2021,7 @@ DataLine::SetValues()
 		}
 	else {
 		if((nPnt = rX->CountItems()) != (rY1->CountItems())) return;
-		if(!(Values = (lfPOINT *)calloc(nPnt+2, sizeof(lfPOINT)))) return; 
+		if(!(Values = (lfPOINT *)realloc(Values, (nPnt+2) * sizeof(lfPOINT)))) return; 
 		if(rX->GetFirst(&i, &j) && rY1->GetFirst(&k, &l) && 
 			rX->GetNext(&i, &j) && rY1->GetNext(&k, &l)) do {
 			if(data->GetValue(j, i, &x) && data->GetValue(l, k, &y)){
@@ -1992,21 +2030,87 @@ DataLine::SetValues()
 			}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;
+	Command(CMD_AUTOSCALE, 0L, 0L);
+	if(tmpValues && Values != tmpValues) Undo.InvalidGO(this);
 	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;
+{
+	lfPOINT *ov = Values;
+
+	if(!val || nval <2) return;
+	if(nval > nPnt && nPnt > 1 && Values){
+		if(!(Values = (lfPOINT *)realloc(Values, ((nval*2+2) * sizeof(lfPOINT))))) return; 
+		if(ov != Values) Undo.InvalidGO(this);
+		}
+	else if(!Undo.busy) Undo.DataMem(this, (void**)&Values, nPnt * sizeof(lfPOINT), &nPnt, UNDO_CONTINUE);
+	memcpy(Values, val, nval * sizeof(lfPOINT));
+	if(pts) free(pts);			pts = 0L;				dirty = true;			
+	free(val);					nPnt = nval;				nPntSet = nPnt-1;
 }
+
+void
+DataLine::DrawSpline(anyOutput *target)
+{
+	int i, j, k, klo, khi, ptsize = 1000;
+	double *y2, min, max, x, y, h, b, a;
+	POINT pn;
+	lfPOINT *scvals;
+	
+	if(!(y2 = (double*)malloc(sizeof(double)*(nPnt)))) return;
+	if(!(scvals = (lfPOINT*)malloc(sizeof(lfPOINT)*(nPnt)))){
+		free(y2);
+		return;
+		}
+	if((type & 0x0f) == 9 || (type & 0x0f) == 10) {
+		if((type & 0x0f) == 9) for(i = 0; i < nPnt; i++) {
+			scvals[i].fx = target->fx2fix(Values[i].fx);
+			scvals[i].fy = target->fy2fiy(Values[i].fy);
+			}
+		else for(i = 0; i < nPnt; i++) {
+			scvals[i].fy = target->fx2fix(Values[i].fx);
+			scvals[i].fx = target->fy2fiy(Values[i].fy);
+			}
+		SortFpArray(nPnt, scvals);
+		min = scvals[0].fx;			max = scvals[nPnt-1].fx;
+		for(i = j = 0; i < (nPnt-1); i++, j++) {
+			y = scvals[i].fy;			scvals[j].fx = scvals[i].fx;
+			for(k = 1; scvals[i+1].fx == scvals[i].fx; k++) {
+				y += scvals[i+1].fy;		i++;
+				}
+			scvals[j].fy = y/((double)k);
+			}
+		if(scvals[i].fx > scvals[i-1].fx) {
+			scvals[j].fx = scvals[i].fx;	scvals[j].fy = scvals[i].fy;
+			j++;
+			}
+		spline(scvals, j, y2);
+		h = scvals[1].fx - scvals[0].fx;	// klo and khi bracket the input value of x
+		for(x = min, klo = 0, i = khi = 1; x < max && i < j; x += 1.0) {
+			while(x > scvals[i].fx) {
+				klo++;		khi++;	i++;
+				h = scvals[khi].fx - scvals[klo].fx;
+				}
+			a = (scvals[khi].fx - x) / h;		b = (x - scvals[klo].fx) / h;
+			y = a * scvals[klo].fy + b * scvals[khi].fy + ((a*a*a - a) * y2[klo] + (b*b*b - b) * y2[khi]) * (h*h)/6.0;
+			if((type & 0x0f) == 9) {
+				pn.x = iround(x);		pn.y = iround(y);
+				}
+			else {
+				pn.x = iround(y);		pn.y = iround(x);
+				}
+			if(cp >= ptsize) {
+				ptsize += 1000;
+				pts = (POINT*)realloc(pts, sizeof(POINT)*ptsize); 
+				}
+			AddToPolygon(&cp, pts, &pn);
+			}
+		}
+	free(y2);	free(scvals);
+}
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // DataPolygon is a graphic object based on DataLine
@@ -2536,8 +2640,7 @@ ErrorBar::ErrorBar(int src):GraphObj(0L, 0L)
 
 ErrorBar::~ErrorBar()
 {
-	if(ssRef) free(ssRef);
-	ssRef = 0L;
+	if(ssRef) free(ssRef);		ssRef = 0L;
 }
 
 bool
@@ -2714,6 +2817,9 @@ ErrorBar::Command(int cmd, void *tmpl, anyOutput *o)
 			return true;
 			}
 		return false;
+	case CMD_ERR_TYPE:
+		if(tmpl) type = *((int*)tmpl);
+		return true;
 	case CMD_AUTOSCALE:
 		if(parent && parent->Id >= GO_PLOT && parent->Id < GO_GRAPH) {
 			switch(type) {
@@ -3115,12 +3221,12 @@ bool
 Box::SetSize(int select, double value)
 {
 	switch(select & 0xfff) {
-	case SIZE_BOX_LINE: 
-		Outline.width = value;
-		return true;
-	case SIZE_BOX:
-		size = value;
-		return true;
+	case SIZE_BOX_LINE:		Outline.width = value;		return true;
+	case SIZE_BOX:			size = value;				return true;
+	case SIZE_XPOS:			pos1.fx = value;			return true;
+	case SIZE_XPOS+1:		pos2.fx = value;			return true;
+	case SIZE_YPOS:			pos1.fy = value;			return true;
+	case SIZE_YPOS+1:		pos2.fy = value;			return true;
 		}
 	return false;
 }
@@ -3162,7 +3268,7 @@ Box::DoPlot(anyOutput *o)
 		pts[2].y = o->fy2iy(pos2.fy - dy);	pts[3].y = o->fy2iy(pos1.fy - dy);
 		}
 	else if(type & BAR_RELWIDTH) {
-		if(!parent) return;
+		if(!parent || (pos1.fy == pos2.fy && pos1.fx == pos2.fx)) return;
 		fsize = parent->GetSize(pos1.fy == pos2.fy ? SIZE_BOXMINY : SIZE_BOXMINX);
 		fsize = fsize * size /200.0;
 		dx = si * fsize;					dy = csi * fsize;
@@ -3261,8 +3367,20 @@ Box::Command(int cmd, void *tmpl, anyOutput *o)
 		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);
+			if(type & BAR_WIDTHDATA) {
+				if(pos1.fy != pos2.fy) {
+					((Plot*)parent)->CheckBounds(pos1.fx+size, pos1.fy);
+					((Plot*)parent)->CheckBounds(pos2.fx-size, pos2.fy);
+					}
+				else {
+					((Plot*)parent)->CheckBounds(pos1.fx, pos1.fy+size);
+					((Plot*)parent)->CheckBounds(pos2.fx, pos2.fy-size);
+					}
+				}
+			else {
+				((Plot*)parent)->CheckBounds(pos1.fx, pos1.fy);
+				((Plot*)parent)->CheckBounds(pos2.fx, pos2.fy);
+				}
 			return true;
 			}
 		break;
@@ -3488,8 +3606,7 @@ DropLine::DropLine(int src):GraphObj(0L, 0L)
 DropLine::~DropLine()
 {
 	if(bModified) Undo.InvalidGO(this);
-	if(ssRef) free(ssRef);
-	ssRef = 0L;
+	if(ssRef) free(ssRef);		ssRef = 0L;
 }
 
 void
@@ -3793,9 +3910,10 @@ Sphere::DoPlot(anyOutput *o)
 		rx = ry = iround(size * 0.5 * o->ddy);		break;
 	case 3:
 		rx = ry = iround(size * 0.5 * o->ddz);		break;
-	default:
+	default:
+		type = 0;
+	case 5:
 		rx = o->un2ix(size/2.0);	ry = o->un2iy(size/2.0);
-		type = 0;
 		break;
 		}
 	rDims.left = ix - rx;			rDims.right = ix + rx;
@@ -3825,6 +3943,10 @@ Sphere::Command(int cmd, void *tmpl, anyOutput *o)
 			scl = 0L;	nscl = 0;
 			}
 		return true;
+	case CMD_LEGEND:
+		if(!tmpl || ((GraphObj*)tmpl)->Id != GO_LEGEND) return false;
+		((Legend*)tmpl)->HasFill(&Line, &Fill);
+		break;
 	case CMD_SYM_FILL:
 		if(tmpl) memcpy(&Fill, tmpl, sizeof(FillDEF));
 		return true;
@@ -4112,6 +4234,7 @@ plane::DoPlot(anyOutput *o)
 		bDrawDone = true;		return;
 		}
 	if(lines) {
+		if(Line.width == 0.0) return;
 		//draw line segments for vertical plane
 		for(i = 0; i < n_lines; i++) if(lines[i]) lines[i]->DoPlot(o);
 		bDrawDone = true;		return;
@@ -4149,7 +4272,8 @@ plane::Command(int cmd, void *tmpl, anyOutput *o)
 	case CMD_REDRAW:
 		if(bDrawDone) return false;
 		bDrawDone = true;
-		if(o && data && nldata){
+		if(o && nldata){
+			if(Line.width == 0.0) Line.color = Fill.color;
 			o->SetLine(&Line);			o->SetFill(&Fill);
 			for(i = 0; i < nli; i++){
 				if(nldata[i] > 2 && (pt = (POINT*)malloc(nldata[i]*sizeof(POINT)))){
@@ -4289,11 +4413,12 @@ plane::DoClip(GraphObj *co)
 	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;
-		}
+	//if two planes have the same parent it means they are part of one object
+	// do not clip!
+	if(co->parent == parent && co->Id == GO_PLANE) return;
+	if(co->Id == GO_PLANE && parent->parent->Id == GO_GRID3D 
+		&& co->parent->parent == parent->parent) return;
+
 	cliprc.left = iround(co->GetSize(SIZE_MIN_X));
 	cliprc.right = iround(co->GetSize(SIZE_MAX_X));
 	cliprc.top = iround(co->GetSize(SIZE_MIN_Y));
@@ -5472,7 +5597,8 @@ Line3D::Line3D(GraphObj *par, DataObj *d, char *rx, char *ry, char *rz)
 	bModified = false;
 }
 
-Line3D::Line3D(GraphObj *par, DataObj *d, fPOINT3D *pt, int n_pt)
+Line3D::Line3D(GraphObj *par, DataObj *d, fPOINT3D *pt, int n_pt, int xc1, int xr1, int yc1, int yr1,
+		int zc1, int zr1, int xc2, int xr2, int yc2, int yr2, int zc2, int zr2)
 	:GraphObj(par, d)
 {
 	FileIO(INIT_VARS);
@@ -5481,6 +5607,18 @@ Line3D::Line3D(GraphObj *par, DataObj *d, fPOINT3D *pt, int n_pt)
 		nPts = n_pt;
 		ls = (line_segment **)calloc(nPts-1, sizeof(line_segment*));
 		}
+	if(xc1 >= 0 || xr1 >= 0 || yc1 >= 0 || yr1 >= 0 || zc1 >= 0 || zr1 >= 0 || 
+		xc2 >= 0 || xr2 >= 0 || yc2 >= 0 || yr2 >= 0 || zc2 >= 0 || zr2 >= 0) {
+		if(ssRef = (POINT*)malloc(sizeof(POINT)*6)) {
+			ssRef[0].x = xc1;	ssRef[0].y = xr1;
+			ssRef[1].x = yc1;	ssRef[1].y = yr1;
+			ssRef[2].x = zc1;	ssRef[2].y = zr1;
+			ssRef[3].x = xc2;	ssRef[3].y = xr2;
+			ssRef[4].x = yc2;	ssRef[4].y = yr2;
+			ssRef[5].x = zc2;	ssRef[5].y = zr2;
+			cssRef = 6;
+			}
+		}
 	Id = GO_LINE3D;
 	bModified = false;
 }
@@ -5503,12 +5641,13 @@ Line3D::~Line3D()
 		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(pts && npts) free(pts);		pts = 0L;		npts = 0;	cssRef = 0;
 	if(x_range) free(x_range);		x_range = 0L;
 	if(y_range) free(y_range);		y_range = 0L;
 	if(z_range) free(z_range);		z_range = 0L;
 	if(values) free(values);		values = 0L;
-	if(mo) DelBitmapClass(mo);		mo = 0L;
+	if(ssRef) free(ssRef);			ssRef = 0L;
+	if(mo) DelBitmapClass(mo);		mo = 0L;
 }
 
 void
@@ -5597,7 +5736,19 @@ Line3D::Command(int cmd, void *tmpl, anyOutput *o)
 			}
 		return false;
 	case CMD_UPDATE:
-		DoUpdate();
+		if(parent && parent->Id != GO_GRID3D) {
+			Undo.DataMem(this, (void**)&values, nPts * sizeof(fPOINT3D), &nPts, UNDO_CONTINUE);
+			}
+		if(ssRef && cssRef >5 && data && nPts == 2) {
+			data->GetValue(ssRef[0].y, ssRef[0].x, &values[0].fx);
+			data->GetValue(ssRef[1].y, ssRef[1].x, &values[0].fy);
+			data->GetValue(ssRef[2].y, ssRef[2].x, &values[0].fz);
+			data->GetValue(ssRef[3].y, ssRef[3].x, &values[1].fx);
+			data->GetValue(ssRef[4].y, ssRef[4].x, &values[1].fy);
+			data->GetValue(ssRef[5].y, ssRef[5].x, &values[1].fz);
+			return true;
+			}
+		else DoUpdate();
 	case CMD_AUTOSCALE:
 		if(parent && parent->Id > GO_PLOT && parent->Id < GO_GRAPH){
 			if(min.fx == max.fx || min.fy == max.fy){	//z's may be equal !
@@ -5909,9 +6060,11 @@ Label::Command(int cmd, void *tmpl, anyOutput *o)
 		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);
+			if(data->GetText(ssRef[2].y, ssRef[2].x, TmpTxt, TMP_TXT_SIZE)) {
+				Undo.String(this, &TextDef.text, UNDO_CONTINUE);
+				TextDef.text = (char*)realloc(TextDef.text, strlen(TmpTxt)+2);
+				if(TmpTxt[0]) strcpy(TextDef.text, TmpTxt);
+				else TextDef.text[0] = 0;
 				}
 			return true;
 			}
@@ -6139,8 +6292,9 @@ Label::CalcCursorPos(int x, int y, anyOutput *o)
 	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);
+	for(i = 1,  x1 = 0; TextDef.text[i-1]; i++) {
+		if(fmt_txt)fmt_txt->oGetTextExtent(o, &x2, &h,i);
+		else o->oGetTextExtent(TextDef.text, i, &x2, &h);
 		if(x1 <= ix && x2 >= ix) {
 			if((ix-x1) < (x2-ix)) CursorPos = i-1;
 			else CursorPos = i;
@@ -6215,7 +6369,7 @@ mLabel::mLabel(GraphObj *par, DataObj *d, double x, double y, TextDEF *td, char
 
 	memcpy(&TextDef, td, sizeof(TextDEF));
 	TextDef.text = 0L;		fPos.fx = x;		fPos.fy = y;
-	Lines = 0L;				flags = flg;
+	Lines = 0L;				flags = flg;		lspc = 1.0;
 	fDist.fx = 0.0;			fDist.fy = 0.0;
 	CurrGO = CurrLabel = 0L;
 	if(txt && (Lines = (Label**)calloc(2, sizeof(Label*)))) {
@@ -6241,7 +6395,7 @@ mLabel::mLabel(GraphObj *par, DataObj *d, double x, double y, TextDEF *td, char
 	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;
+	fDist.fx = 0.0;			fDist.fy = 0.0;		lspc = 1.0;
 	CurrGO = CurrLabel = 0L;
 	if(txt){
 		if((llist=split(txt,'\n',&nll)) && nll && (Lines=(Label**)calloc(nll, sizeof(Label*)))){
@@ -6287,6 +6441,8 @@ mLabel::GetSize(int select)
 		break;
 	case SIZE_MIN_Z:	case SIZE_MAX_Z:	case SIZE_ZPOS:
 		return curr_z;
+	case SIZE_LSPC:
+		return lspc;
 		}
 	return 0.0;
 }
@@ -6315,6 +6471,9 @@ mLabel::SetSize(int select, double value)
 			Lines[i]->SetSize(select, value);
 			}
 		return is3D = true;
+	case SIZE_LSPC:
+		undo_flags = CheckNewFloat(&lspc, lspc, value, this, undo_flags);
+		return true;
 		}
 	return false;
 }
@@ -6355,9 +6514,10 @@ mLabel::DoPlot(anyOutput *o)
 	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));
+	memcpy(&cPos1, &cPos, sizeof(lfPOINT));
+	if(lspc < 0.5 || lspc > 5) lspc = 1.0;
+	dist.fx = floor(o->un2fix(TextDef.fSize * si * 1.1 * lspc));
+	dist.fy = floor(o->un2fiy(TextDef.fSize * csi * 1.1 * lspc));
 	o->SetTextSpec(&TextDef);
 	rDims.left = rDims.top = rDims.right = rDims.bottom = 0;
 	for(i = 0; i < nLines; i++)	if(Lines[i]){
@@ -6470,12 +6630,11 @@ mLabel::Command(int cmd, void *tmpl, anyOutput *o)
 		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);
+		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
@@ -7495,6 +7654,7 @@ LegItem::Command(int cmd, void *tmpl, anyOutput *o)
 			}
 		break;
 	case CMD_SET_DATAOBJ:
+		if(Desc) Desc->Command(cmd, tmpl, o);
 		Id = GO_LEGITEM;
 		data = (DataObj *)tmpl;
 		return true;
@@ -7521,7 +7681,7 @@ 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->Id == GO_SYMBOL && (sy->type & 0xfff) != (Sym->type & 0xfff)) return false;
 	if(sy && Sym) {
 		if(Sym->GetSize(SIZE_SYMBOL) != sy->GetSize(SIZE_SYMBOL)) return false;
 		if(Sym->GetSize(SIZE_SYM_LINE) != sy->GetSize(SIZE_SYM_LINE)) return false;
@@ -7983,6 +8143,16 @@ Graph::Command(int cmd, void *tmpl, anyOutput *o)
 
 	if(!o) o = CurrDisp;
 	switch (cmd){
+    case CMD_CAN_CLOSE:
+		HideTextCursor();
+		if(bModified) {
+			bModified = false;
+			sprintf(TmpTxt, "%s has not been saved.\nDo you want to save it now?", name);
+			i = YesNoCancelBox(TmpTxt);
+			if(i == 2) return false;
+			else if(i == 1) if(! SaveGraphAs(this)) return false;
+			}
+		//fall through
 	case CMD_CAN_DELETE:
 		HideTextCursor();
 		if(bModified) {
@@ -8088,7 +8258,7 @@ Graph::Command(int cmd, void *tmpl, anyOutput *o)
 			if(tmpl == (void*)Axes[i]){
 				Axes[i]->DoMark(CurrDisp, false);		return true;
 				}
-			else if(Axes[i]->Id == GO_AXIS) {
+			else if(Axes[i] && Axes[i]->Id == GO_AXIS) {
 				if(Axes[i]->Command(cmd, tmpl, o))		return true;
 				}
 			}
@@ -8169,7 +8339,8 @@ Graph::Command(int cmd, void *tmpl, anyOutput *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, o); 
+					if(Plots[i] && (Plots[i]->Id == GO_PLOT3D || Plots[i]->Id == GO_FUNC3D))
+						return Plots[i]->Command(cmd, tmpl, o); 
 				}
 			else if(AddAxis()) {
 				Command(CMD_REDRAW, tmpl, o);
@@ -8214,7 +8385,8 @@ Graph::Command(int cmd, void *tmpl, anyOutput *o)
 		if(data) data->Command(CMD_UPDHISTORY, 0L, 0L);
 		return true;
 	case CMD_UPDATE:
-		Command(CMD_TOOLMODE, 0L, o);
+		Command(CMD_TOOLMODE, 0L, o);
+		Undo.SetDisp(CurrDisp);
 		if(parent && parent->Id != GO_PAGE){
 			Undo.ValInt(this, &ToolMode, 0L);		//stub, all plots have UNDO_CONTINUE
 			}
@@ -8285,9 +8457,12 @@ Graph::Command(int cmd, void *tmpl, anyOutput *o)
 		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);
-					}
+					if(Plots[i] && (Plots[i]->Id == GO_PLOT3D || Plots[i]->Id == GO_FUNC3D)) {
+						if(Plots[i]->Command(cmd, tmpl, CurrDisp)) return Command(CMD_REDRAW, 0L, o);
+						else return false;
+						}
+					}
+				return false;
 				}
 			else return AddPlot(0x0);
 			}
@@ -8315,7 +8490,7 @@ Graph::Command(int cmd, void *tmpl, anyOutput *o)
 		else {
 			if(type == GT_3D && Plots) {
 				for(i = 0; i < NumPlots; i++) {
-					if(Plots[i] && Plots[i]->Id == GO_PLOT3D)
+					if(Plots[i] && (Plots[i]->Id == GO_PLOT3D || Plots[i]->Id == GO_FUNC3D))
 						return Plots[i]->Command(cmd, tmpl, CurrDisp);
 					}
 				}
@@ -8344,16 +8519,13 @@ Graph::Command(int cmd, void *tmpl, anyOutput *o)
 			if(Plots) {
 				Plots[0] = (Plot *)tmpl;
 				switch(Plots[0]->Id) {
-				case GO_PIECHART:
-				case GO_RINGCHART:
-				case GO_STARCHART:
+				case GO_PIECHART:		case GO_RINGCHART:	case GO_STARCHART:
 					type = GT_CIRCCHART;
 					break;
 				case GO_POLARPLOT:
 					type = GT_POLARPLOT;
 					break;
-				case GO_SCATT3D:
-				case GO_PLOT3D:
+				case GO_SCATT3D:		case GO_PLOT3D:		case GO_FUNC3D:
 					type = GT_3D;
 					break;
 				default:
@@ -8455,15 +8627,16 @@ Graph::Command(int cmd, void *tmpl, anyOutput *o)
 				Command(CMD_MOUSE_EVENT, mev, CurrDisp);
 				mev->Action = MOUSE_LBDOUBLECLICK;
 				}
-			if(CurrGO) {
+			else if(CurrGO->Id < GO_PLOT) {
 				if(CurrGO->PropertyDlg()) {
 					bModified = true;
 					return Command(CMD_REDRAW, 0L, o);
-					}
+					}
 				}
+			else if(CurrGO->Command(CMD_CONFIG, 0L, o)) return Command(CMD_REDRAW, 0L, o);
 			else if (Id == GO_PAGE) return Command(CMD_CONFIG, 0L, o);
 			else o->HideMark();
-			CurrGO = TrackGO = 0L;	CurrLabel = 0L;
+			TrackGO = 0L;	CurrLabel = 0L;
 			return false;
 		case MOUSE_LBUP:
 			Undo.SetDisp(o);
@@ -8508,7 +8681,8 @@ Graph::Command(int cmd, void *tmpl, anyOutput *o)
 		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);
+				Plots[i]->Id == GO_PLOT3D || Plots[i]->Id == GO_POLARPLOT ||
+				Plots[i]->Id == GO_FUNC3D) Plots[i]->Command(cmd, tmpl, o);
 			}
 		return true;
 		}
@@ -9519,7 +9693,7 @@ ObjTree::DoPlot(anyOutput *o)
 	if(!o || !list) return;
 	o->Erase(0x00ffffffL);
 	ix = 10;	iy = 0;
-	for(i = 0; i < count; i++, iy += TextDef.iSize) {
+	for(i = 0; i < count; i++, iy += (TextDef.iSize+_SBINC)) {
 		if(list[i]) {
 			curr_obj = list[i];			n = 0;
 			if(i) while(curr_obj && curr_obj != list[0]) {
diff --git a/rlplot.h b/rlplot.h
index ca03fa0..b942817 100755
--- a/rlplot.h
+++ b/rlplot.h
@@ -24,15 +24,22 @@
 
 #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);}
+inline int WriteFloatToBuff(char *buff, double val) {return sprintf(buff, " %g", val);}
+
+#define _PI		3.1415926535897932384626433832795028841971693993751
+#define _SQRT2	1.4142135623730950488016887242096980785696718753769
 
 #ifdef _WINDOWS
 #include <windows.h>
 #define w_char unsigned short
+#define _SBINC	1				//scrollbox extra space/line
+#define _TXH	4.0				//graph text default size in mm
 
 #else
 #define DWORD unsigned long
 #define w_char unsigned short
+#define _SBINC	8				//scrollbox extra space/line
+#define _TXH	3.0				//graph text default size in mm
 
 typedef struct tagPOINT { // pt 
     long x; 
@@ -69,7 +76,7 @@ enum {SIZE_MINE, SIZE_SYMBOL, SIZE_SYM_LINE, SIZE_DATA_LINE, SIZE_TEXT,
 	SIZE_A, SIZE_B, SIZE_MX, SIZE_MY, SIZE_MIN_Z, SIZE_MAX_Z, SIZE_MIN_X, SIZE_MAX_X,
 	SIZE_MIN_Y, SIZE_MAX_Y, SIZE_TICK_ANGLE, SIZE_RRECT_RAD, SIZE_XCENT, SIZE_YCENT,
 	SIZE_ZCENT, SIZE_DRADIUS, SIZE_CURSORPOS, SIZE_CURSOR_XMIN, SIZE_CURSOR_YMIN,
-	SIZE_CURSOR_XMAX, SIZE_CURSOR_YMAX, SIZE_XSTEP};
+	SIZE_CURSOR_XMAX, SIZE_CURSOR_YMAX, SIZE_XSTEP, SIZE_LSPC};
 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,
@@ -89,12 +96,12 @@ enum {CMD_NONE, CMD_ADDCHAR, CMD_SETFOCUS, CMD_KILLFOCUS, CMD_DELETE,
 	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_SHIFT_OUT, CMD_CAN_DELETE, CMD_CAN_CLOSE, CMD_RESET_LINE, CMD_SET_TICKSTYLE,
 	CMD_GET_GRIDLINE, CMD_SET_GRIDLINE, CMD_SET_GRIDTYPE, CMD_TLB_TXTDEF,
 	CMD_DROP_LABEL, CMD_DROP_OBJECT, CMD_PAGEUP, CMD_PAGEDOWN, CMD_AUTOSCALE,
 	CMD_MRK_DIRTY, CMD_SETNAME, CMD_TOOLMODE, CMD_DROPFILE, CMD_AXIS, CMD_INIT,
 	CMD_GET_CELLDIMS, CMD_SET_CELLDIMS, CMD_TEXTSIZE, CMD_PASTE_TSV, CMD_PASTE_XML,
-	CMD_PASTE_CSV, CMD_COPY_TSV, CMD_COPY_XML, CMD_COPY_SYLK, CMD_QUERY_COPY,
+	CMD_PASTE_SSV, CMD_PASTE_CSV, CMD_COPY_TSV, CMD_COPY_XML, CMD_COPY_SYLK, CMD_QUERY_COPY,
 	CMD_MOUSECURSOR, CMD_NEWPAGE, CMD_MINRC, CMD_MAXRC,CMD_SETCHILD, CMD_SYM_FILL,
 	CMD_LINEUP, CMD_LINEDOWN, CMD_CONFIG, CMD_FINDTEXT, CMD_MOVE_TOP, CMD_MOVE_UP,
 	CMD_MOVE_DOWN, CMD_MOVE_BOTTOM, CMD_UPDATE, CMD_CURRPOS, CMD_POS_FIRST, CMD_POS_LAST,
@@ -103,10 +110,11 @@ enum {CMD_NONE, CMD_ADDCHAR, CMD_SETFOCUS, CMD_KILLFOCUS, CMD_DELETE,
 	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_SAVE_ERRS, CMD_SAVE_ARROWS, CMD_SAVE_DROPLINES, CMD_SAVE_LABELS, CMD_UNLOCK, CMD_SYMTEXT_UNDO,
 	CMD_FILLRANGE, CMD_BUSY, CMD_ERROR, CMD_CLEAR_ERROR, CMD_SETPARAM, CMD_SETFUNC,
 	CMD_HIDE_MARK, CMD_LEGEND, CMD_FILENAME, CMD_LAYERS, CMD_OBJTREE, CMD_TEXTDEF,
-	CMD_HASSTACK, CMD_WRITE_GRAPHS, CMD_SETFONT, CMD_SETSTYLE};
+	CMD_HASSTACK, CMD_WRITE_GRAPHS, CMD_SETFONT, CMD_SETSTYLE, CMD_COPY, CMD_PASTE,
+	CMD_INSROW, CMD_INSCOL, CMD_DELROW, CMD_DELCOL, CMD_ADDTXT, CMD_ETRACC, CMD_SHPGUP, CMD_SHPGDOWN};
 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};
@@ -123,7 +131,8 @@ enum {GO_UNKNOWN, GO_AXIS, GO_TICK, GO_GRIDLINE, GO_SYMBOL, GO_BUBBLE, GO_BAR,
 	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_RIBBON, GO_LIMITS, GO_FUNCTION, GO_FITFUNC, GO_FREQDIST, GO_GRID3D, GO_FUNC3D,
+	GO_XYSTAT,
 	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, 
@@ -145,7 +154,7 @@ enum {MENU_NONE, MENU_SPREAD, MENU_GRAPH, MENU_PAGE};
 enum {GT_UNKNOWN, GT_STANDARD, GT_CIRCCHART, GT_POLARPLOT, GT_3D};
 enum {ICO_NONE, ICO_INFO, ICO_ERROR, ICO_RLPLOT, ICO_QT};
 enum {FF_UNKNOWN, FF_CSV, FF_TSV, FF_XML, FF_SYLK, FF_RLP, FF_SVG, FF_EPS,
-	FF_WMF, FF_RLW};
+	FF_WMF, FF_RLW, FF_SSV};
 enum {LB_X_DATA = 0x01, LB_X_PARENT = 0x02, LB_Y_DATA = 0x10, LB_Y_PARENT = 0x20};
 enum {BUBBLE_CIRCLE = 0x000, BUBBLE_SQUARE = 0x001, BUBBLE_UPTRIA = 0x002,
 	BUBBLE_DOWNTRIA = 0x003, BUBBLE_UNITS = 0x000, BUBBLE_XAXIS = 0x010,
@@ -153,6 +162,7 @@ enum {BUBBLE_CIRCLE = 0x000, BUBBLE_SQUARE = 0x001, BUBBLE_UPTRIA = 0x002,
 	BUBBLE_AREA = 0x200};
 enum {DH_UNKNOWN, DH_12, DH_22, DH_19, DH_29, DH_39, DH_49, DH_59, DH_69, DH_79,
 	DH_89, DH_99, DH_18, DH_28, DH_38, DH_48, DH_58, DH_68, DH_78, DH_88, DH_DATA};
+enum {FE_NONE = 0x1000, FE_PARENT, FE_PLOT, FE_FLUSH, FE_DELOBJ, FE_REPLGO, FE_MUTATE};
 
 //drop line styles
 #define DL_LEFT          0x001
@@ -314,7 +324,9 @@ public:
 	~AccRange();
 	int CountItems();
 	bool GetFirst(int *x, int *y);
-	bool GetNext(int *x, int *y);
+	bool GetNext(int *x, int *y);
+	bool NextRow(int *y);
+	bool NextCol(int *x);
 	bool IsInRange(int x, int y);
 	bool BoundRec(RECT *rec);
 
@@ -385,7 +397,8 @@ public:
 	double fix2un(double fix);
 	double fiy2un(double fiy);
 	virtual bool SetLine(LineDEF *lDef){return false;};
-	bool GetLine(LineDEF *lDef);
+	bool GetLine(LineDEF *lDef);
+	virtual void Focus() {return;};
 	virtual void Caption(char *txt){return;};
 	virtual void MouseCursor(int cid, bool force){return;};
 	virtual bool SetScroll(bool, int, int, int, int){return false;};
@@ -427,12 +440,13 @@ enum {ET_UNKNOWN, ET_VALUE, ET_TEXT, ET_FORMULA, ET_ERROR, ET_BUSY=0x100,
 
 class EditText {
 public:
-	char *text;
-	int Align, type;
-	void *parent;				//points to a data object: defined below
+	char *text, *ftext;
+	int Align, type, row, col;
+	void *parent;				//points to a data object: defined below
+	anyOutput *disp;
+	double Value;
 
-	EditText(void *par, POINT where, POINT right, char *msg);
-	EditText(void *par, char *msg);
+	EditText(void *par, char *msg, int r, int c);
 	~EditText();
 	bool AddChar(int c, anyOutput *Out, void *data_obj);
 	void Update(int select, anyOutput *Out, POINT *MousePos);
@@ -441,7 +455,7 @@ public:
 	bool Command(int cmd, anyOutput *Out, void *data_obj);
 	bool GetValue(double *v);
 	bool GetText(char *t, int size);
-	bool GetResult(anyResult *r);
+	bool GetResult(anyResult *r, bool use_last = false);
 	bool SetValue(double v);
 	bool SetText(char *t);
 	bool GetItem(char *text, int size);
@@ -452,6 +466,7 @@ public:
 	bool isValue();
 	bool isFormula();
 	bool isInRect(POINT *p) {return (p->x>loc.x && p->x<crb.x && p->y>loc.y && p->y<rb.y);};
+	bool hasMark() {return (m1 != m2 && m1 >= 0 && m2 >= 0);};
 
 private:
 	LineDEF *bgLine;
@@ -459,7 +474,6 @@ private:
 	int length, CursorPos, m1, m2, mx1, mx2;
 	POINT loc, rb, crb;
 	DWORD TextCol;
-	double Value;
 
 	void FindType();
 };
@@ -508,7 +522,7 @@ public:
 	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 GetResult(anyResult *r, int row, int col, bool use_last = false);
 	virtual bool GetSize(int *width, int *height);
 	virtual bool Select(POINT *p){return false;};
 	virtual bool WriteData(char *FileName) {return false;};
@@ -522,13 +536,14 @@ public:
 
 class StrData {
 public:
-	StrData(DataObj *par);
+	StrData(DataObj *par, RECT *rc = 0L);
 	~StrData();
 	bool GetSize(int *w, int *h);
 	void StrData::RestoreData(DataObj *dest);
 
 private:
-	int pw, ph, h, w;
+	int pw, ph;
+	RECT drc;
 	DataObj *src;
 	char ***str_data;
 };
@@ -622,7 +637,7 @@ public:
 	bool Command(int cmd, void *tmpl, anyOutput *o);
 
 private:
-	bool bLBdown;
+	bool bLBdown, bSelected;
 	TextDEF TextDef;
 	LineDEF Line;
 	FillDEF Fill;
@@ -747,7 +762,9 @@ public:
 	bool PropertyDlg();
 	bool FileIO(int rw);
 
-private:
+private:
+	bool DoAutoscale(anyOutput *o);
+
 	lfPOINT fPos;
 	double fs;
 	LineDEF BubbleLine, BubbleFillLine;
@@ -767,11 +784,14 @@ public:
 	bool SetSize(int select, double value);
 	bool SetColor(int select, DWORD col);
 	void DoPlot(anyOutput *target);
+	void DoMark(anyOutput *o, bool mark);
 	bool Command(int cmd, void *tmpl, anyOutput *o);
 	bool PropertyDlg();
 	bool FileIO(int rw);
 
 private:
+	anyOutput *mo;
+	RECT mrc;
 	double size;
 	LineDEF BarLine, HatchLine;
 	FillDEF BarFill;
@@ -806,7 +826,8 @@ public:
 
 	void FileValues(char *name, int type, double start, double step);
 	void SetValues();
-	void LineData(lfPOINT *val, long nval);
+	void LineData(lfPOINT *val, long nval);
+	void DrawSpline(anyOutput *target);
 };
 
 class DataPolygon:public DataLine{
@@ -1208,7 +1229,10 @@ 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(GraphObj *par, DataObj *d, fPOINT3D *pt, int n_pt, int xc1 = -1,
+		int xr1 = -1, int yc1 = -1, int yr1 = -1, int zc1 = -1, int zr1 = -1, 
+		int xc2 = -1, int xr2 = -1, int yc2 = -1, int yr2 = -1, int zc2 = -1, 
+		int zr2 = -1);
 	Line3D(int src);
 	~Line3D();
 	void DoPlot(anyOutput *o);
@@ -1221,8 +1245,10 @@ private:
 	line_segment **ls;
 	fPOINT3D *values, min, max;
 	POINT *pts;
+	long nPts, npts;
 	char *x_range, *y_range, *z_range;
-	long nPts, npts;
+	POINT *ssRef;
+	long cssRef;
 	bool bModified;
 	anyOutput *mo;
 	RECT mrc;
@@ -1289,7 +1315,7 @@ public:
 
 private:
 	lfPOINT fPos, fDist, cPos, cPos1, dist;
-	double si, csi, curr_z;
+	double si, csi, curr_z, lspc;
 	DWORD flags, undo_flags;
 	TextDEF TextDef;
 	long nLines;
@@ -1480,6 +1506,16 @@ public:
 
 class PlotScatt:public Plot{
 public:
+	char *xRange, *yRange;
+	long nPoints;
+	int DefSym;
+	lfPOINT BarDist;
+	Bar **Bars;
+	Symbol **Symbols;
+	DataLine *TheLine;
+	ErrorBar **Errors;
+	Label **Labels;
+
 	PlotScatt(GraphObj *par, DataObj *d, DWORD presel);
 	PlotScatt(GraphObj *par, DataObj *d, int cBars, Bar **bars);
 	PlotScatt(GraphObj *par, DataObj *d, int nPts, Symbol **sym, DataLine *lin);
@@ -1489,30 +1525,39 @@ public:
 	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 Command(int cmd, void *tmpl, anyOutput *o);
 	virtual bool PropertyDlg();
 	void RegGO(void *n);
-	bool FileIO(int rw);
+	virtual bool FileIO(int rw);
+
+	bool ForEach(int cmd, void *tmp, anyOutput *o);
 
 private:
 	DWORD DefSel;
-	lfPOINT BarDist;
-	char *xRange, *yRange, *ErrRange, *LbRange;
-	int DefSym;
-	long nPoints;
-	Bar **Bars;
-	Symbol **Symbols;
-	DataLine *TheLine;
-	ErrorBar **Errors;
+	char *ErrRange, *LbRange;
 	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 xyStat:public PlotScatt{
+public:
+	xyStat(GraphObj *par, DataObj *d);
+	xyStat(int src);
+	~xyStat();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	virtual bool FileIO(int rw);
+
+	void CreateData();
+
+private:
+	double ci;
+	DataObj *curr_data;
+	char *case_prefix;
+
+};
 
 class BarChart:public PlotScatt{
 public:
@@ -1624,14 +1669,21 @@ public:
 	bool Command(int cmd, void *tmpl, anyOutput *o);
 	bool PropertyDlg();
 	void RegGO(void *n);
-	bool FileIO(int rw);
+	bool FileIO(int rw);
+
+	bool ForEach(int cmd, void *tmp, anyOutput *o);
+	void CreateData();
 
 private:
-	long nPoints;
+	char *xRange, *yRange, *case_prefix;
+	long nPoints;
+	double ci_box, ci_err;
+	DataObj *curr_data;
 	lfPOINT BoxDist;
 	Box **Boxes;
 	Whisker **Whiskers;
-	Symbol **Symbols;
+	Symbol **Symbols;
+	Label **Labels;
 	DataLine *TheLine;
 };
 
@@ -1722,7 +1774,7 @@ public:
 	void DoUpdate();
 
 private:
-	int nPts;
+	long nPts;
 	char *ssRefA, *ssRefR;
 	lfPOINT CtDef;
 	double FacRad;
@@ -1787,9 +1839,11 @@ private:
 
 class Grid3D:public Plot {
 public:
-	Grid3D(GraphObj *par, DataObj *d);
+	Grid3D(GraphObj *par, DataObj *d, int sel, double x1=0.0, double xstep=1.0, double z1=0.0, double zstep=1.0);
 	Grid3D(int src);
 	~Grid3D();
+	bool SetSize(int select, double value);
+	bool SetColor(int select, DWORD col);
 	void DoPlot(anyOutput *o);
 	bool Command(int cmd, void *tmpl, anyOutput *o);
 	void RegGO(void *n);
@@ -1798,10 +1852,12 @@ public:
 	void CreateObs();
 
 private:
-	long nLines;
+	long nLines, nPlanes;
 	fPOINT3D start, step;
 	LineDEF Line;
+	FillDEF Fill;
 	Line3D **lines;
+	Plane3D **planes;
 };
 
 class Scatt3D:public Plot{
@@ -2024,6 +2080,7 @@ public:
 	Axis **Axes;
 	GraphObj **plots;
 	double *RotDef;
+	fPOINT3D cub1, cub2, rotC;
 
 	Plot3D(GraphObj *par, DataObj *d, DWORD flags);
 	Plot3D(int src);
@@ -2032,10 +2089,10 @@ public:
 	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);
+	virtual bool Command(int cmd, void *tmpl, anyOutput *o);
+	virtual bool PropertyDlg();
+	virtual void RegGO(void *n);
+	virtual bool FileIO(int rw);
 
 	void * ObjThere(int x, int y);
 	void Track(POINT *p, anyOutput *o);
@@ -2050,7 +2107,7 @@ private:
 	long nObs, nmaxObs;
 	DWORD crea_flags;
 	Drag3D *drag;
-	fPOINT3D cub1, cub2, rotC, cu1, cu2, rc;
+	fPOINT3D cu1, cu2, rc;
 	obj_desc **dispObs;
 
 	bool AddPlot(int family);
@@ -2082,6 +2139,28 @@ public:
 	~BubblePlot3D();
 	bool PropertyDlg();
 };
+
+class Func3D:public Plot3D {
+public:
+	Func3D(GraphObj *par, DataObj *d);
+	Func3D(int src);
+	~Func3D();
+	bool Command(int cmd, void *tmpl, anyOutput *o);
+	bool PropertyDlg();
+	void RegGO(void *n);
+	bool FileIO(int rw);
+
+private:
+	bool Update();
+
+	double x1, x2, xstep, z1, z2, zstep;
+	int g_idx;
+	LineDEF Line;
+	FillDEF Fill;
+	char *param, *cmdxy;
+	DataObj *gda;
+	Grid3D  *gob;
+};
 
 class Graph:public GraphObj{
 public:
@@ -2188,7 +2267,7 @@ public:
 	char DecPoint[2], ColSep[2];
 	char *svgAttr, *svgScript, *currPath, *IniFile;
 	char *File1, *File2, *File3, *File4, *File5, *File6;
-	double min4log;
+	double min4log, ss_txt;
 	RECT clipRC;
 
 	Default();
@@ -2259,11 +2338,11 @@ public:
 #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};
+		UNDO_VALDWORD, UNDO_VALINT, UNDO_VALLONG, UNDO_OBJCONF, UNDO_OBJCONF_1,
+		UNDO_LFP, UNDO_POINT, UNDO_VOIDPTR, UNDO_MOVE, UNDO_RECT,
+		UNDO_STRING, UNDO_ROTDEF, UNDO_SETGO, UNDO_LINEDEF, UNDO_FILLDEF,
+		UNDO_AXISDEF, UNDO_LFP3D, UNDO_FLOAT, UNDO_MEM, UNDO_MUTATE, 
+		UNDO_DROPGOLIST, UNDO_TEXTDEF, UNDO_SAVVAR, UNDO_DATA, UNDO_ET};
 	typedef struct _UndoInfo {
 		int cmd;
 		DWORD flags;
@@ -2294,14 +2373,16 @@ class UndoObj {
 
 public:
 	int *pcb;
+	anyOutput *cdisp, *ldisp;
+	bool busy;
 
-	UndoObj();
+	UndoObj();
 	~UndoObj();
 	void Flush();
 	void SetDisp(anyOutput *o);
 	void KillDisp(anyOutput *o);
 	void InvalidGO(GraphObj *go);
-	void Pop();
+	void Pop(anyOutput *o);
 	void Restore(bool redraw, anyOutput *o);
 	void ListGOmoved(GraphObj **oldlist, GraphObj **newlist, long size);
 	void DeleteGO(GraphObj **go, DWORD flags, anyOutput *o);
@@ -2310,8 +2391,11 @@ public:
 	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 ValDword(GraphObj *parent, DWORD *val, DWORD flags);
+	void Point(GraphObj *parent, POINT *pt, anyOutput * o, DWORD flags);
+	void VoidPtr(GraphObj *parent, void **pptr, void *ptr, anyOutput * o, DWORD flags);
 	void ValInt(GraphObj *parent, int *val, DWORD flags);
+	void ValLong(GraphObj *parent, long *val, DWORD flags);
 	void ObjConf(GraphObj *go, DWORD flags);
 	int SaveLFP(GraphObj *go, lfPOINT *lfp, DWORD flags);
 	void MoveObj(GraphObj *go, lfPOINT *lfp, DWORD flags);
@@ -2326,13 +2410,12 @@ public:
 	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 DataObject(GraphObj *go, anyOutput *o, DataObj *d, RECT *rc, DWORD flags);
 	void TextCell(EditText *et, anyOutput *o, char *text, int *cur, int *m1, int *m2, void* DaO, DWORD flags);
 
 private:
 	UndoInfo **buff, **buff0;
 	int stub1, ndisp;
-	anyOutput *cdisp, *ldisp;
 	UndoBuff **buffers;
 
 	int NewItem(int cmd, DWORD flags, GraphObj *owner, void *data, void **loc);
@@ -2353,6 +2436,7 @@ char *OpenDataName(char *oldname);
 void InfoBox(char *Msg);
 void ErrorBox(char *Msg);
 bool YesNoBox(char *Msg);
+int YesNoCancelBox(char *Msg);
 void Qt_Box();
 void HideTextCursor();
 void HideTextCursorObj(anyOutput *out);
@@ -2361,6 +2445,8 @@ void HideCopyMark();
 void ShowCopyMark(anyOutput *out, RECT *mrk, int nRec);
 void InitTextCursor(bool init);
 void EmptyClip();
+void CopyText(char *txt, int len);
+unsigned char* PasteText();
 void GetDesktopSize(int *width, int *height);
 void FindBrowser();
 void LoopDlgWnd();
@@ -2386,8 +2472,7 @@ 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 FillSsRange(DataObj *d, char **range, GraphObj *msg_go);
 bool GetBitmapRes(double *res, double *width, double *height, char *header);
 void OD_scheme(int, void *, RECT *, anyOutput *, void *, int);
 FillDEF *GetSchemeFill(int *i);
@@ -2419,6 +2504,7 @@ double NiceValue(double fv);
 char *Int2ColLabel(int nr, bool uc);
 char *str2xml(char *str);
 char **split(char *str, char sep, int *nl);
+char *fit_num_rect(anyOutput *o, int max_width, char *num_str);
 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);
@@ -2438,10 +2524,12 @@ unsigned long isqr(unsigned long n);
 bool MatMul(double a[3][3], double b[3][3], double c[3][3]);
 char *GetNumFormat(double Magn);
 void DeleteGO(GraphObj *go);
+bool DeleteGOL(GraphObj ***gol, long n, GraphObj *go, anyOutput *o);
 bool BackupFile(char *FileName);
 bool IsRlpFile(char *FileName);
 bool IsXmlFile(char *FileName);
 bool FileExist(char *FileName);
+bool IsPlot3D(GraphObj *g);
 void *memdup(void *ptr, int cb_old, int cb_new);
 double sininv(double val);
 double trig2deg(double si, double csi);
@@ -2474,8 +2562,10 @@ void DoExportTif(GraphObj *g, char *FileName, DWORD flags);
 
 //prototypes mfcalc.cpp
 bool do_xyfunc(DataObj *, double, double, double, char *, lfPOINT **, long *, char *);
+bool do_func3D(DataObj *d, double x1, double x2, double xstep, double z1, double z2, double zstep, 
+	char *expr, char *param);
 anyResult *do_formula(DataObj *, char *);
-bool MoveFormula(DataObj *d, char *of, char *nf, int dx, int dy);
+bool MoveFormula(DataObj *d, char *of, char *nf, int dx, int dy, int r0, int c0);
 int do_fitfunc(DataObj *d, char *rx, char *ry, char *rz, char **par, char *expr, 
 	double conv, int maxiter, double *chi_2);
 
@@ -2485,11 +2575,31 @@ void free_dmatrix(double **m, int nrl, int nrh, int ncl, int);
 bool mrqmin(double *, double *, double *, int, double **, int, int *, int, double **, double **, double *,
 	void (*funcs)(double, double, double **, double *, double *, int), double *);
 bool Check_MRQerror();
+void SortFpArray(int n, lfPOINT *vals);
+void spline(lfPOINT *v, int n, double *y2);
 double gammln(double xx);
 double factrl(int n);
-double t_dist(double t, double df);
+double gammp(double a, double x);
+double gammq(double a, double x);
+double betai(double a, double b, double x);
+double bincof(double n, double k);
+double binomdistf(double k, double n, double p);
+double betaf(double z, double w);
+double errf(double x);
+double errfc(double x);
+double norm_dist(double x, double m, double s);
+double chi_dist(double x, double df, double);
+double t_dist(double t, double df, double);
+double pois_dist(double x, double m, double);
 double f_dist(double f, double df1, double df2);
+double distinv(double (*sf)(double, double, double), double df1, double df2, double p, double x0);
 void d_quartile(int n, double *v, double *q1, double *q2, double *q3);
 double d_amean(int n, double *v);
 double d_gmean(int n, double *v);
 double d_hmean(int n, double *v);
+double d_pearson(double *x, double *y, int n, char *dest, DataObj *data);
+double d_spearman(double *x, double *y, int n, char *dest, DataObj *data);
+double d_regression(double *x, double *y, int n, char *dest, DataObj *data);
+double d_ttest(double *x, double *y, int n1, int n2, char *dest, DataObj *data);
+double d_ftest(double *x, double *y, int n1, int n2, char *dest, DataObj *data);
+
diff --git a/rlplot.spec b/rlplot.spec
index aaaff29..315c497 100755
--- a/rlplot.spec
+++ b/rlplot.spec
@@ -1,5 +1,5 @@
 Name:      rlplot
-Version:   0.99.12b
+Version:   1.0
 Release:   1
 Summary:   A plotting program to create high quality graphs from data.
 License:   GPL
@@ -41,6 +41,9 @@ rm -rf "$RPM_BUILD_ROOT"
 %{_bindir}/exprlp
 
 %changelog
+* Wed Sep 07 2005 Reinhard Lackner
+- release 1.0
+
 * Tue Dec 21 2004 Reinhard Lackner
 - release canditate 2, version 0.99.12b
 
@@ -60,3 +63,4 @@ rm -rf "$RPM_BUILD_ROOT"
 
 
 
+
diff --git a/spreadwi.cpp b/spreadwi.cpp
index 40e32b8..734c7b8 100755
--- a/spreadwi.cpp
+++ b/spreadwi.cpp
@@ -32,13 +32,15 @@
 	#include <unistd.h>
 #endif
 
-extern const LineDEF BlackLine;
+extern const LineDEF GrayLine;
 extern EditText *CurrText;
 extern char *LoadFile;
 extern char TmpTxt[];
 extern Default defs;
 extern UndoObj Undo;
+
 static ReadCache *Cache = 0L;
+static TextDEF ssText;
 
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 // Get item from *.csv file
@@ -73,10 +75,10 @@ bool GetItemCSV(char *Text, int cbText)
 // 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;
+	int i, RetVal = FF_UNKNOWN, nt, nl, nc, ns;
 
 	if(ptr) {
-		for(i = nt = nl = nc = 0; ptr[i]; i++) {
+		for(i = nt = nl = nc = ns = 0; ptr[i] && nl<100; i++) {
 			switch(ptr[i]) {
 			case 0x09:				//tab
 				nt++;
@@ -86,22 +88,29 @@ int ProcMemData(GraphObj *g, unsigned char *ptr, bool dispatch)
 				break;
 			case ',':
 				nc++;
-				break;
+				break;
+			case ' ':
+				ns++;
+				break;
 				}
 			}
-		if(dispatch && i && !nt && !nl)
-			for(i = 0; ptr[i]; i++){
-				cc = ptr[i];
-				g->Command(CMD_ADDCHAR, (void*)(& cc), 0L);
-				}
+		if(dispatch && i && !nt && !nl) {
+			if(CurrText){
+				g->Command(CMD_SETFOCUS, 0L, 0L);
+				for(i = 0; ptr[i]; i++)	CurrText->AddChar(ptr[i], i? 0L : Undo.cdisp, 0L);
+				g->Command(CMD_REDRAW, 0L, 0L);
+				}
+			}
 		else if(nt) RetVal = FF_TSV;
 		else if(nl && ptr[0] == '<') RetVal = FF_XML;
 		else if(nc == nl && defs.DecPoint[0] == ',') RetVal = FF_TSV;
 		else if(nl && nc && 0 == (nc % nl)) RetVal = FF_CSV;
+		else if(nl && ns && 0 == (ns % nl)) RetVal = FF_SSV;
 		else if(nl) RetVal = FF_TSV;
 		if(dispatch) switch(RetVal) {
 		case FF_CSV:	g->Command(CMD_PASTE_CSV, ptr, 0L);	break;
 		case FF_TSV:	g->Command(CMD_PASTE_TSV, ptr, 0L);	break;
+		case FF_SSV:	g->Command(CMD_PASTE_SSV, ptr, 0L);	break;
 		case FF_XML:	g->Command(CMD_PASTE_XML, ptr, 0L); break;
 			}
 		}
@@ -117,12 +126,12 @@ public:
 	POINT ssOrg;
 	RECT currRC;
 
-	SpreadWin(DataObj *Data);
+	SpreadWin(GraphObj *par, DataObj *Data);
 	~SpreadWin();
 	void DoPlot(anyOutput *target);
 	bool Command(int cmd, void *tmpl, anyOutput *o);
 
-	bool ShowGrid(int CellWidth, int CellHeight, int FirstWidth);
+	bool ShowGrid(int CellWidth, int CellHeight, int FirstWidth, POINT *cpos);
 	bool PrintData(anyOutput *o);
 	void WriteGraphXML(unsigned char **ptr, long *cbd);
 
@@ -130,18 +139,19 @@ private:
 	int ch, cw, fw;				//cell height and width, row button width
 	bool is_modified;
 	char *filename;
-	TextDEF ssText;
 	ssButton **cButtons, **rButtons;
-	ssButton *aButton;
+	ssButton *aButton;
+	POINT cpos;
 	DataObj *d;
 	int NumGraphs;
 	Graph **g;
 };
 
-SpreadWin::SpreadWin(DataObj *Data):GraphObj(0L, Data)
+SpreadWin::SpreadWin(GraphObj *par, DataObj *Data):GraphObj(par, Data)
 {
 	d = Data;	g = 0L;		ssOrg.x =  ssOrg.y = 0;		NumGraphs = 0;
-	ch = 19;	cw = 76;	fw = 32;	filename=0L;	aButton = 0L;
+	ch = 19;	cw = 76;	fw = 32;	filename=0L;	aButton = 0L;
+	w = 0L;
 	if(w = NewDispClass(this)){
 		w->hasHistMenu = true;
 		ssText.RotBL = ssText.RotCHAR = 0.0;
@@ -149,16 +159,16 @@ SpreadWin::SpreadWin(DataObj *Data):GraphObj(0L, Data)
 #ifdef _WINDOWS
 		ssText.iSize = w->un2iy(defs.GetSize(SIZE_CELLTEXT));
 #else
-		ssText.iSize = w->un2iy(defs.GetSize(SIZE_CELLTEXT)*.8f);
+		ssText.iSize = w->un2iy(defs.GetSize(SIZE_CELLTEXT)*.7);
 #endif
-		ssText.Align = TXA_VTOP | TXA_HLEFT;		ssText.Mode = TXM_TRANSPARENT;
-		ssText.Style = TXS_NORMAL;					ssText.ColBg = 0x00d8d8d8L;
+		ssText.Align = TXA_VCENTER | TXA_HLEFT;		ssText.Mode = TXM_TRANSPARENT;
+		ssText.Style = TXS_NORMAL;					ssText.ColBg = 0x00e8e8e8L;
 		ssText.ColTxt = 0x00000000L;				ssText.text = 0L;
 		ssText.Font = FONT_HELVETICA;				w->SetTextSpec(&ssText);
 		w->SetMenu(MENU_SPREAD);					w->FileHistory();
-		w->Erase(0x00d8d8d8L);						w->Caption("RLPlot data");
+		w->Erase(0x00e8e8e8L);						w->Caption("RLPlot data");
 		cw = w->un2ix(defs.GetSize(SIZE_CELLWIDTH));
-		ch = w->un2iy(defs.GetSize(SIZE_CELLTEXT)) + 2;
+		ch = w->un2iy(defs.GetSize(SIZE_CELLTEXT)/defs.ss_txt) + 2;
 		fw = 32;
 		}
 	cButtons = rButtons = 0L;
@@ -169,28 +179,30 @@ SpreadWin::SpreadWin(DataObj *Data):GraphObj(0L, Data)
 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]);
+
+	if(parent) {
+		if(cButtons) {
+			for(i = 0; cButtons[i]; i++) if(cButtons[i]) delete(cButtons[i]);
+			free(cButtons);
 			}
-		free (g);
-		}
-	if(filename) free(filename);	filename=0L;
-}
-
-void
+		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);
@@ -205,17 +217,45 @@ SpreadWin::Command(int cmd, void *tmpl, anyOutput *o)
 	char *Name;
 	Graph **g1, *g2;
 	int i, j, k;
-	MouseEvent *mev;
+	MouseEvent *mev;
+	POINT p1;
 
 	if(d) {
-		switch(cmd) {
-		case CMD_UNDO:
-			if(w) {
-				w->MouseCursor(MC_WAIT, true);
-				Undo.Restore(true, w);
-				w->MouseCursor(MC_ARROW, true);
+		switch(cmd) {
+		case CMD_CURRPOS:
+			if(tmpl && cButtons && rButtons) {
+				int ac = 1, na =  0;
+				RECT urc;
+				if(((POINT*)tmpl)->x != cpos.x) {
+					for(cpos.x = ((POINT*)tmpl)->x, i = 0; cButtons[i]; i++) {
+						cButtons[i]->Command(CMD_SELECT, (cpos.x == (i+ssOrg.x)) ? &ac : &na, w);
+						}
+					urc.left = cButtons[0]->rDims.left;		urc.bottom = cButtons[0]->rDims.bottom;
+					urc.top = cButtons[0]->rDims.top;		urc.right = urc.left + cw * (i-1);
+					w->UpdateRect(&urc, false);
+					}
+				if(((POINT*)tmpl)->y != cpos.y) {
+					for(cpos.y = ((POINT*)tmpl)->y, i = 0; rButtons[i]; i++) {
+						rButtons[i]->Command(CMD_SELECT, (cpos.y == (i+ssOrg.y)) ? &ac : &na, w);
+						}
+					urc.left = rButtons[0]->rDims.left;		urc.right = rButtons[0]->rDims.right;
+					urc.top = rButtons[0]->rDims.top;		urc.bottom = urc.top + ch * (i-1);
+					w->UpdateRect(&urc, false);
+					}
 				}
-			return true;
+			return true;
+		case CMD_CAN_CLOSE:
+			HideTextCursor();
+			for(i = 0; i < NumGraphs; i++) {
+				if((g[i]) && !g[i]->Command(cmd, tmpl, o)) return false;
+				}
+			if(is_modified) {
+				is_modified=false;
+				i = YesNoCancelBox("The spreadsheet has been modified!\n\nDo you want to save it now?");
+				if(i == 2) return false;
+				else if(i == 1) return Command(CMD_SAVEDATAAS, tmpl, o);
+				}
+			//fall through
 		case CMD_CAN_DELETE:
 			HideTextCursor();
 			if(is_modified && YesNoBox("The spreadsheet has been modified!\n\nDo you want to save it now?")){
@@ -226,8 +266,7 @@ SpreadWin::Command(int cmd, void *tmpl, anyOutput *o)
 			is_modified=false;
 			return true;
 		case CMD_MRK_DIRTY:
-			is_modified=true;
-			return true;
+			return is_modified = true;
 		case CMD_WRITE_GRAPHS:
 			if (g && NumGraphs) WriteGraphXML((unsigned char**)tmpl, (long*)o);
 			return true;
@@ -252,13 +291,15 @@ SpreadWin::Command(int cmd, void *tmpl, anyOutput *o)
 			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);
+				Command(CMD_DROP_GRAPH, g2, o))return Command(CMD_REDRAW, 0L, o);
+			else if(g2) DeleteGO(g2);
+			Undo.SetDisp(w);
 			return false;
 		case CMD_NEWPAGE:
 			if((g2 = new Page(this, d)) && 
-				Command(CMD_DROP_GRAPH, g2, o))return true;
+				Command(CMD_DROP_GRAPH, g2, o))return Command(CMD_REDRAW, 0L, o);
 			else if(g2) DeleteGO(g2);
+			Undo.SetDisp(w);
 			return false;
 		case CMD_DELGRAPH:
 			if (g && NumGraphs) {
@@ -290,18 +331,22 @@ SpreadWin::Command(int cmd, void *tmpl, anyOutput *o)
 				if(Name && d->WriteData(Name)) {
 					if(filename) free(filename);
 					filename = strdup(Name);
-					}
-				}
+					}
+				else return false;
+				}
+			else return false;
 			return true;
-		case CMD_DROPFILE:
+		case CMD_DROPFILE:
+			if(!Command(CMD_CAN_CLOSE, 0L, o)) return false;
 			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);
+				filename = strdup((char*)tmpl);
 				return Command(CMD_SETSCROLL, 0L, w);
 				}
 			return false;
 		case CMD_OPEN:
+			if(!Command(CMD_CAN_CLOSE, 0L, o)) return false;
 			if((Name = OpenDataName(filename)) && Name[0]){
 				if(o) o->FileHistory();
 				if(IsRlpFile(Name)) return OpenGraph(this, Name, 0L);
@@ -313,7 +358,7 @@ SpreadWin::Command(int cmd, void *tmpl, anyOutput *o)
 				}
 			return false;
 		case CMD_ADDROWCOL:
-			DoSpShSize(d);
+			if(DoSpShSize(d)) DoPlot(o);
 			return true;
 		case CMD_MOUSE_EVENT:
 			if(o && (mev = (MouseEvent*)tmpl) && mev->y > o->MenuHeight) {
@@ -331,8 +376,8 @@ SpreadWin::Command(int cmd, void *tmpl, anyOutput *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_PASTE_TSV:		case CMD_PASTE_CSV:		case CMD_PASTE_SSV:
+			Undo.DataObject(this, w, d, 0L, 0L);
 		case CMD_COPY_SYLK:		case CMD_ADDCHAR:		case CMD_SHIFTUP:
 		case CMD_COPY_TSV:		case CMD_COPY_XML:		case CMD_QUERY_COPY:
 		case CMD_TAB:			case CMD_SHTAB:			case CMD_SHIFTDOWN:
@@ -340,8 +385,14 @@ SpreadWin::Command(int cmd, void *tmpl, anyOutput *o)
 		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_PASTE_XML:		case CMD_DELROW:		case CMD_INSROW:
+		case CMD_INSCOL:		case CMD_DELCOL:		case CMD_UNDO:
+		case CMD_SHPGUP:		case CMD_SHPGDOWN:
+			return d->Command(cmd, tmpl, o);
+		case CMD_REDRAW:
+			Undo.SetDisp(w);
+			d->Command(cmd, tmpl, o);
+			return true;
 		case CMD_SETSCROLL:
 			HideTextCursor();
 			o->ActualSize(&currRC);
@@ -352,32 +403,35 @@ SpreadWin::Command(int cmd, void *tmpl, anyOutput *o)
 			o->SetScroll(false, 0, i, k, ssOrg.x);
 			DoPlot(o);
 			return true;
-		case CMD_PAGEUP:
+		case CMD_PAGEUP:		case CMD_PAGEDOWN:
 			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);
+			k = k > 3 ? k-2 : 1;
+			p1.x = fw + 2;		p1.y = ch + 2;
+			if(CurrText){
+				p1.x = CurrText->GetX()+2;	p1.y = CurrText->GetY()+12;
+				}
+			d->GetSize(&i, &j);
+			if(cmd == CMD_PAGEUP) ssOrg.y = ssOrg.y > k ? ssOrg.y-k : 0;
+			else ssOrg.y = ssOrg.y < j-k*2 ? ssOrg.y+k : j > k ? j-k : 0;
+			Command(CMD_SETSCROLL, tmpl, o);
+			CurrText = 0L;		d->Select(&p1);
+			return true;
 		case CMD_SETHPOS:
 			ssOrg.x = *((int*)tmpl) >= 0 ? *((long*)tmpl) : 0L;
 			return Command(CMD_SETSCROLL, tmpl, o);
 		case CMD_SETVPOS:
 			ssOrg.y = *((int*)tmpl) >= 0 ? *((long*)tmpl) : 0L;
 			return Command(CMD_SETSCROLL, tmpl, o);
-		case CMD_SETFOCUS:
+		case CMD_SETFOCUS:
+			Undo.SetDisp(w);
 			return true;
 		case CMD_KILLFOCUS:
 			return true;
 		case CMD_TEXTSIZE:
 			if(tmpl)ssText.iSize = *((int*)tmpl);
-			break;
+			return true;
 		case CMD_CONFIG:
-			return defs.PropertyDlg();
+			return defs.PropertyDlg();
 		case CMD_NONE:
 			return true;
 		case CMD_PRINT:
@@ -388,16 +442,16 @@ SpreadWin::Command(int cmd, void *tmpl, anyOutput *o)
 }
 
 bool
-SpreadWin::ShowGrid(int CellWidth, int CellHeight, int FirstWidth)
+SpreadWin::ShowGrid(int CellWidth, int CellHeight, int FirstWidth, POINT *cp)
 {
-	int i, c, nr, nc;
+	int i, c, nr, nc, ac = 1, na = 0;
 	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
+
+	cpos.x = cp->x;			cpos.y = cp->y;
 	if(ch != CellHeight || cw != CellWidth || fw != FirstWidth) redim = true;
 	ch = CellHeight;	cw = CellWidth;		fw = FirstWidth;
 	if(redim){
@@ -430,7 +484,7 @@ SpreadWin::ShowGrid(int CellWidth, int CellHeight, int FirstWidth)
 			new ssButton(this, 0, i*CellHeight+CellHeight+w->MenuHeight, 
 			FirstWidth, CellHeight);
 		}
-	w->SetLine((LineDEF *)&BlackLine);
+	w->SetLine((LineDEF *)&GrayLine);
 	d->GetSize(&nc, &nr);
 	grid[0].x = rc.left+FirstWidth;
 	i = (nc-ssOrg.x)*CellWidth+FirstWidth;
@@ -440,8 +494,8 @@ SpreadWin::ShowGrid(int CellWidth, int CellHeight, int FirstWidth)
 		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);
+			rButtons[i]->Command(CMD_SELECT, (cpos.y == (i+ssOrg.y)) ? &ac : &na, w);
+			w->SetLine((LineDEF *)&GrayLine);
 			}
 		grid[0].y = grid[1].y = w->MenuHeight + i*CellHeight - 1;
 		if(i < (2+nr-ssOrg.y)) w->oSolidLine(grid);
@@ -453,8 +507,8 @@ SpreadWin::ShowGrid(int CellWidth, int CellHeight, int FirstWidth)
 		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);
+			cButtons[i]->Command(CMD_SELECT, (cpos.x == (i+ssOrg.x)) ? &ac : &na, w);
+			w->SetLine((LineDEF *)&GrayLine);
 			}
 		grid[0].x = grid[1].x = i*CellWidth+FirstWidth-1;
 		if(i <= (nc-ssOrg.x)) w->oSolidLine(grid);
@@ -506,7 +560,7 @@ SpreadWin::PrintData(anyOutput *o)
 #ifdef _WINDOWS
 	td.fSize = defs.GetSize(SIZE_CELLTEXT);
 #else
-	td.fSize = defs.GetSize(SIZE_CELLTEXT)*.8f;
+	td.fSize = defs.GetSize(SIZE_CELLTEXT)*.8;
 #endif
 	margin.left = iround(o->hres);	margin.right = iround(o->hres/2.0);
 	margin.top = margin.bottom = iround(o->hres);
@@ -563,6 +617,7 @@ SpreadWin::PrintData(anyOutput *o)
 						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);
+						fit_num_rect(o, pcw - iround(o->hres/30.0), TmpTxt);
 						}
 					else if(d->etRows[j+ss_pos.y][i+ss_pos.x]->isValue()){
 						td.Align = TXA_HRIGHT | TXA_VCENTER;
@@ -647,51 +702,66 @@ SpreadWin::WriteGraphXML(unsigned char **ptr, long *cbd)
 // This data object is a spreadsheet
 //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 class SpreadData:public DataObj{
+typedef struct _pos_info {
+	POINT ssOrg, currpos;
+	void *CurrText;};
+
 public:
-	SpreadData();
+	SpreadData(GraphObj *par);
 	~SpreadData();
 	bool Init(int nRows, int nCols);
-	bool mpos2dpos(POINT *mp, POINT *dp);
+	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);
+	bool AddRows(int nRows);
+	bool ChangeSize(int nCols, int nRows, bool bUndo);
+	bool DeleteCols();
+	bool InsertCols();
+	bool DeleteRows();
+	bool InsertRows();
 	void DoPlot(anyOutput *o);
 	bool DelRange();
 	bool PasteRange(int cmd, char *txt);
-	bool InitCopy(int cmd, void *tmpl, anyOutput *o);
+	bool InitCopy(int cmd, void *tmpl, anyOutput *o);
+	bool SavePos();
 	bool Command(int cmd, void *tmpl, anyOutput *o);
 	bool ReadData(char *FileName, unsigned char *buffer, int type);
 
-	bool ReadXML(char *file, unsigned char *buffer, int type);
+	bool ReadXML(char *file, unsigned char *buffer, int type, DWORD undo_flags = 0L);
 	bool ReadTSV(char *file, unsigned char *buffer, int type);
 	bool MemList(unsigned char **ptr, int type);
 
 private:
-	int CellHeight, CellWidth, FirstWidth, r_disp, c_disp;
+	int CellHeight, CellWidth, FirstWidth, r_disp, c_disp, mrk_offs;
 	RECT rcCopy, cp_src_rec;				//bounding rectangle for copy range
-	bool bActive, new_mark, bCopyCut;
+	bool bActive, new_mark, bCopyCut, bUpdate, isRowMark, isColMark;
 	POINT currpos, currpos2;
 	anyOutput *w;
-	SpreadWin *Disp;
+	SpreadWin *Disp;
+	GraphObj *g_parent;
+	EditText *et_racc;
 	char *m_range, *c_range;	//mark and copy ranges
-	char *err_msg;				//error message
+	char *err_msg, *last_err;	//error message
+	_pos_info pos_info;			//save position settings
 };
 
-SpreadData::SpreadData()
+SpreadData::SpreadData(GraphObj *par)
 {
-	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;
+	Disp = 0L;	m_range = 0L;	c_range = 0L;	w = 0L;		err_msg=last_err = 0L;
+	g_parent = par;		CellWidth = CellHeight = FirstWidth = 0;
+	et_racc = 0L;
+	currpos.x = currpos.y = r_disp = c_disp = mrk_offs = 0;
+	bActive = bCopyCut = bUpdate = isRowMark = isColMark = 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);
 		}
+	pos_info.currpos.x = currpos.x;		pos_info.currpos.y = currpos.y;
+	pos_info.CurrText = CurrText;
 }
 
 SpreadData::~SpreadData()
@@ -706,52 +776,48 @@ 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);
+		Disp = new SpreadWin(g_parent, 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;
+			CellHeight = w->un2iy(defs.GetSize(SIZE_CELLTEXT)/defs.ss_txt) + 2;
+			FirstWidth = 32;
+			w->GetSize(&rc);
+			r_disp = (rc.bottom-rc.top)/CellHeight;
 			c_disp = (rc.right-rc.left)/CellWidth+1;
 			}
 		else return false;
-		Disp->ShowGrid(CellWidth, CellHeight, FirstWidth);
+		Disp->ShowGrid(CellWidth, CellHeight, FirstWidth, &currpos);
+		pos_info.ssOrg.x = Disp->ssOrg.x;		pos_info.ssOrg.y = Disp->ssOrg.y;
+		pos_info.CurrText = CurrText;
 		}
 	if(etRows)FlushData();
 	etRows = (EditText ***)calloc (cRows, sizeof(EditText **));
-	if(etRows) for(i = 0, p1.x = FirstWidth, p1.y = CellHeight + w->MenuHeight; i < cRows; i++) {
+	if(etRows) for(i = 0; 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);
+			etRows[i][j] = new EditText(this, text, i, j);
 #else
-			etRows[i][j] = new EditText(this, p1, p2, 0L);
+			etRows[i][j] = new EditText(this, 0L, i, j);
 #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);
-		}
+		}
+	else DoPlot(w);
 	return true;
 }
 
@@ -764,7 +830,7 @@ SpreadData::mpos2dpos(POINT *mp, POINT *dp)
 		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) {
+	if(mp->y < (w->MenuHeight + CellHeight+9) && mp->y > (w->MenuHeight + CellHeight) && Disp->ssOrg.y >0) {
 		Disp->ssOrg.y -= 1;
 		if(CurrText) CurrText->Update(2, w, mp);		CurrText = 0L;
 		Disp->Command(CMD_SETSCROLL, 0L, w);
@@ -789,13 +855,15 @@ SpreadData::mpos2dpos(POINT *mp, POINT *dp)
 	if(dp->y >= cRows) dp->y = cRows-1;	
 	if(dp->x >= cCols) dp->x = cCols-1;
 	return true;
-}
-
+}
+
 bool
 SpreadData::Select(POINT *p)
 {
 	if(CurrText && CurrText->isInRect(p)){
 		CurrText->Update(1, w, p);
+		Disp->Command(CMD_CURRPOS, &currpos, w);
+		if(CurrText->isFormula() && CurrText->hasMark()) et_racc = CurrText;
 		return true;
 		}
 	mpos2dpos(p, &currpos);
@@ -808,6 +876,7 @@ SpreadData::Select(POINT *p)
 				DoPlot(w);
 				CurrText->Update(1, w, p);
 				}
+			Disp->Command(CMD_CURRPOS, &currpos, w);
 			return true;
 			}
 		}
@@ -819,7 +888,7 @@ void
 SpreadData::MarkRange(char *range)
 {
 	AccRange *nr, *oldr;
-	int r, c;
+	int r, c;
 
 	oldr = nr = 0L;
 	if(m_range && range && !strcmp(m_range, range)) return;		//no change
@@ -836,12 +905,13 @@ SpreadData::MarkRange(char *range)
 	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))
+				&& c < (c_disp + Disp->ssOrg.x) && r < cRows && c < cCols)
 				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(range && (m_range = (char*)realloc(m_range, strlen(range)+6)))
+		strcpy(m_range, range);
+	else if (m_range) m_range[0] = 0;
 	if(oldr) delete(oldr);	if(nr) delete(nr);
 	new_mark = true;
 }
@@ -859,7 +929,7 @@ SpreadData::HideMark(bool cclp)
 		DoPlot(w);
 		}
 	if(cclp) EmptyClip();
-	new_mark = bCopyCut = false;
+	new_mark = false;
 }
 
 bool
@@ -927,16 +997,16 @@ SpreadData::ReadData(char *FileName, unsigned char *buffer, int type)
 	bool success;
 	POINT pt;
 
-	if(FileName) {		//read disk file
+	if(FileName) {		//read disk file
+		Disp->Command(CMD_CAN_DELETE, 0L, 0L);
 		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)){
+			if(ReadXML(FileName, buffer, type, 0L)){
 				rcCopy.left = rcCopy.top = 0;
-				rcCopy.right = cCols;
-				rcCopy.bottom = cRows;
+				rcCopy.right = cCols;			rcCopy.bottom = cRows;
 				return true;
 				}
 			return false;
@@ -945,8 +1015,7 @@ SpreadData::ReadData(char *FileName, unsigned char *buffer, int type)
 			0 == strcmp(".TSV", FileName+strlen(FileName)-4)){
 			if(ReadTSV(FileName, buffer, type)){
 				rcCopy.left = rcCopy.top = 0;
-				rcCopy.right = cCols;
-				rcCopy.bottom = cRows;
+				rcCopy.right = cCols;			rcCopy.bottom = cRows;
 				return true;
 				}
 			return false;
@@ -965,7 +1034,7 @@ SpreadData::ReadData(char *FileName, unsigned char *buffer, int type)
 		case FF_TSV:
 			return ReadTSV(FileName, buffer, type);
 		case FF_XML:
-			return ReadXML(FileName, buffer, type);
+			return ReadXML(FileName, buffer, type, 0L);
 		case FF_CSV:
 			if(!(Cache = new MemCache(buffer))) return false;
 			break;
@@ -1021,33 +1090,16 @@ 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;
+	if (nCols <= cCols || !etRows || !etRows[0] || !etRows[0][cCols-1]) return false;
 	for(i = 0; i < cRows; i++) {
-		NewRow = (EditText **)realloc(etRows[i], nCols * sizeof(EditText *));
-		if(NewRow) {
+		if(NewRow = (EditText **)realloc(etRows[i], nCols * sizeof(EditText*))){
 			for(j = cCols; j < nCols; j++) {
-				NewRow[j] = new EditText(this, p1, p2, NULL);
-				p1.x += CellWidth;				p2.x += CellWidth;
+				NewRow[j] = new EditText(this, 0L, i, j);
 				}
 			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;
+		else return false;						//memory allocation error
 		}
 	cCols = nCols;
 	return true;
@@ -1056,35 +1108,22 @@ SpreadData::AddCols(int nCols)
 bool
 SpreadData::AddRows(int nRows)
 {
-	int i, j, x;
-	POINT p1, p2;
+	int i, j;
 	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;
+	if (nRows <= cRows || !etRows || !etRows[cRows-1] || !etRows[cRows-1][0] ) return false;
 	NewRows = (EditText ***)realloc(etRows, nRows * sizeof(EditText **));
 	if(NewRows) etRows = NewRows;
-	else return false;					//memory allocation error
+	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;
-				}
+			for(j = 0; j < cCols; j++) etRows[i][j] = new EditText(this, 0L, i, j);
 			}
 		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;
@@ -1097,7 +1136,7 @@ SpreadData::ChangeSize(int nCols, int nRows, bool bUndo)
 	bool RetVal = true;
 	
 	if(nCols == cCols && nRows == cRows) return true;
-	if(bUndo) Undo.DataObject(Disp, w, this, 0L);
+	if(bUndo) Undo.DataObject(Disp, w, this, 0L, 0L);
 	if(nRows && nRows < cRows) {
 		for (i = nRows; i < cRows; i++) {
 			if(etRows[i]) for (j = 0; j < cCols; j++) {
@@ -1126,11 +1165,305 @@ SpreadData::ChangeSize(int nCols, int nRows, bool bUndo)
 		if(i > FirstWidth) FirstWidth = i;
 		Disp->Command(CMD_SETSCROLL, 0L, w);
 		}
-	rcCopy.left = rcCopy.right = 0;
-	rcCopy.right = cCols;
-	rcCopy.bottom = cRows;
 	return RetVal;
 }
+
+bool
+SpreadData::DeleteCols()
+{
+	int i, j, r0, c0, c1, c2, cn, dc, nc;
+	RECT rc;
+	lfPOINT *cs;
+
+	AccRange * ar;
+	if(!isColMark || !m_range || !m_range[0]){
+		InfoBox("No columns selected!");
+		return false;
+		}
+	Undo.DataObject(Disp, w, this, 0L, 0L);
+	Undo.String(0L, &m_range, UNDO_CONTINUE);
+	if(!(ar = new AccRange(m_range))) return false;
+	ar->BoundRec(&rc);
+	if(!(cs = (lfPOINT*)malloc((rc.right-rc.left+2)*sizeof(lfPOINT)))) {
+		delete ar;			return false;
+		}
+	if(!(ar->GetFirst(&c0, &r0))) {
+		free(cs);			delete ar;			return false;
+		}
+	ar->NextCol(&c1);		cn = c1;			nc = 0;
+	do {
+		for(c0 = c1 = c2 = cn; ar->NextCol(&c2); ) {
+			if(c2 > c1+1 || c2 < c0) break;
+			c1 = c2;
+			}
+		dc = c1-c0+1;		cn = c2;
+		cs[nc].fx = c0;		cs[nc].fy = c1;		nc++;
+		}while(c2!=c1);
+	SortFpArray(nc, cs);	nc--;
+	do {
+		c0 = (int)cs[nc].fx;	c1 = (int)cs[nc].fy;	dc = c1-c0+1;
+		for(i = 0; i < cRows; i++) {
+			for(j = c0+dc; j <cCols; j++) {
+				if(etRows && etRows[i] && etRows[i][j] && etRows[i][j-dc]) {
+					switch(etRows[i][j]->type) {
+					default:
+						etRows[i][j-dc]->SetText(etRows[i][j]->text);
+						break;
+					case ET_VALUE:
+						etRows[i][j-dc]->SetText(etRows[i][j]->text);
+						etRows[i][j-dc]->Value = etRows[i][j]->Value; 
+						etRows[i][j-dc]->type = ET_VALUE;
+						break;
+					case ET_FORMULA:
+						MoveFormula(this, etRows[i][j]->text, TmpTxt, -dc, 0, -1, c0);
+						etRows[i][j-dc]->SetText(TmpTxt);	etRows[i][j]->type = ET_FORMULA;
+						break;
+						}
+					}
+				}
+			}
+		for(i = 0; i < cRows; i++) {
+			if(etRows[i]){
+				for(j = cCols-dc; j < cCols; j++) if(etRows[i][j]) {
+					delete(etRows[i][j]);
+					etRows[i][j] = 0L;
+					}
+				}
+			}
+		cCols -= dc;
+		MoveFormula(this, m_range, TmpTxt, -dc, 0, -1, c0+1);
+		strcpy(m_range, TmpTxt);		nc--;
+		for(i = 0; i < c0; i++) for(j = 0; j < c0; j++) {
+			if(etRows && etRows[i] && etRows[i][j] && etRows[i][j]->type == ET_FORMULA) {
+				MoveFormula(this, etRows[i][j]->text, TmpTxt, -dc, 0, -1, c0);
+				etRows[i][j]->SetText(TmpTxt);			etRows[i][j]->type = ET_FORMULA;
+				}
+			}
+		}while(nc >= 0);
+	delete ar;
+	Disp->Command(CMD_SETSCROLL, 0L, w);		Disp->Command(CMD_MRK_DIRTY, 0L, w);
+	return true;
+}
+
+bool
+SpreadData::InsertCols()
+{
+	int i, j, r0, c0, c1, c2, cn, dc, nc;
+	RECT rc;
+	lfPOINT *cs;
+
+	AccRange * ar;
+	if(!isColMark || !m_range || !m_range[0]){
+		InfoBox("No columns selected!");
+		return false;
+		}
+	Undo.DataObject(Disp, w, this, 0L, 0L);
+	Undo.String(0L, &m_range, UNDO_CONTINUE);
+	if(!(ar = new AccRange(m_range))) return false;
+	ar->BoundRec(&rc);
+	if(!(cs = (lfPOINT*)malloc((rc.right-rc.left+2)*sizeof(lfPOINT)))) {
+		delete ar;			return false;
+		}
+	if(!(ar->GetFirst(&c0, &r0))) {
+		free(cs);			delete ar;			return false;
+		}
+	ar->NextCol(&c1);		cn = c1;			nc = 0;
+	do {
+		for(c0 = c1 = c2 = cn; ar->NextCol(&c2); ) {
+			if(c2 > c1+1 || c2 < c0) break;
+			c1 = c2;
+			}
+		dc = c1-c0+1;		cn = c2;
+		cs[nc].fx = c0;		cs[nc].fy = c1;		nc++;
+		}while(c2!=c1);
+	SortFpArray(nc, cs);	nc--;
+	do {
+		c0 = (int)cs[nc].fx;	c1 = (int)cs[nc].fy;	dc = c1-c0+1;
+		if(AddCols(cCols + dc)) for(i = 0; i < cRows; i++) {
+			for(j = cCols-1; j >= c0+dc; j--) {
+				if(etRows && etRows[i] && etRows[i][j] && etRows[i][j-dc]) {
+					switch(etRows[i][j-dc]->type) {
+					default:
+						etRows[i][j]->SetText(etRows[i][j-dc]->text);
+						break;
+					case ET_VALUE:
+						etRows[i][j]->SetText(etRows[i][j-dc]->text);
+						etRows[i][j]->Value = etRows[i][j-dc]->Value; 
+						etRows[i][j]->type = ET_VALUE;
+						break;
+					case ET_FORMULA:
+						MoveFormula(this, etRows[i][j-dc]->text, TmpTxt, dc, 0, -1, c0);
+						etRows[i][j]->SetText(TmpTxt);	etRows[i][j]->type = ET_FORMULA;
+						break;
+						}
+					etRows[i][j-dc]->SetText("");
+					}
+				}
+			}
+		MoveFormula(this, m_range, TmpTxt, dc, 0, -1, c0);
+		strcpy(m_range, TmpTxt);		nc--;
+		for(i = 0; i < c0; i++) for(j = 0; j < c0; j++) {
+			if(etRows && etRows[i] && etRows[i][j] && etRows[i][j]->type == ET_FORMULA) {
+				MoveFormula(this, etRows[i][j]->text, TmpTxt, dc, 0, -1, c0);
+				etRows[i][j]->SetText(TmpTxt);			etRows[i][j]->type = ET_FORMULA;
+				}
+			}
+		}while(nc >= 0);
+	delete ar;
+	Disp->Command(CMD_SETSCROLL, 0L, w);		Disp->Command(CMD_MRK_DIRTY, 0L, w);
+	return true;
+}
+
+bool 
+SpreadData::DeleteRows()
+{
+	int i, j, r0, c0, r1, r2, rn, dr, nr;
+	AccRange * ar;
+	RECT rc;
+	lfPOINT *rs;
+
+	if(!isRowMark || !m_range || !m_range[0]){
+		InfoBox("No rows selected!");
+		return false;
+		}
+	Undo.DataObject(Disp, w, this, 0L, 0L);
+	Undo.String(0L, &m_range, UNDO_CONTINUE);
+	if(!(ar = new AccRange(m_range))) return false;
+	ar->BoundRec(&rc);
+	if(!(rs = (lfPOINT*)malloc((rc.bottom-rc.top+2)*sizeof(lfPOINT)))) {
+		delete ar;			return false;
+		}
+	if(!(ar->GetFirst(&c0, &r0))) {
+		free(rs);			delete ar;			return false;
+		}
+	ar->NextRow(&r1);		rn = r1;			nr = 0;
+	do {
+		for(r0 = r1 = r2 = rn;ar->NextRow(&r2); ) {
+			if(r2 > r1+1 || r2 < r0) break;
+			r1 = r2;
+			}
+		dr = r1-r0+1;		rn = r2;
+		rs[nr].fx = r0;		rs[nr].fy = r1;		nr++;
+		}while(r2!=r1);
+	SortFpArray(nr, rs);	nr--;
+	do {
+		r0 = (int)rs[nr].fx;	r1 = (int)rs[nr].fy;	dr = r1-r0+1;
+		for(i = r0+dr; i < cRows; i++) {
+			for(j = 0; j < cCols; j++) {
+				if(etRows && etRows[i-dr] && etRows[i-dr][j] && etRows[i] && etRows[i][j]) {
+					switch(etRows[i][j]->type) {
+					default:
+						etRows[i-dr][j]->SetText(etRows[i][j]->text);
+						break;
+					case ET_VALUE:
+						etRows[i-dr][j]->SetText(etRows[i][j]->text);
+						etRows[i-dr][j]->Value = etRows[i][j]->Value; 
+						etRows[i-dr][j]->type = ET_VALUE;
+						break;
+					case ET_FORMULA:
+						MoveFormula(this, etRows[i][j]->text, TmpTxt, 0, -dr, r0, -1);
+						etRows[i-dr][j]->SetText(TmpTxt);	etRows[i-dr][j]->type = ET_FORMULA;
+						break;
+						}
+					}
+				}
+			}
+		for(i = cRows-1; i >= cRows-dr; i--) {
+			if(etRows[i]){
+				for(j = 0; j < cCols; j++) if(etRows[i][j]) delete(etRows[i][j]);
+				free(etRows[i]);
+				}
+			}
+		cRows -= dr;
+		MoveFormula(this, m_range, TmpTxt, 0, -dr, r0+1, -1);
+		strcpy(m_range, TmpTxt);		nr--;
+		for(i = 0; i < r0; i++) for(j = 0; j < cCols; j++) {
+			if(etRows && etRows[i] && etRows[i][j] && etRows[i][j]->type == ET_FORMULA) {
+				MoveFormula(this, etRows[i][j]->text, TmpTxt, 0, -dr, r0, -1);
+				etRows[i][j]->SetText(TmpTxt);			etRows[i][j]->type = ET_FORMULA;
+				}
+			}
+		}while(nr >= 0);
+	delete ar;
+	if(w) {
+		Disp->Command(CMD_SETSCROLL, 0L, w);			Disp->Command(CMD_MRK_DIRTY, 0L, w);
+		}
+	return true;
+}
+
+bool
+SpreadData::InsertRows()
+{
+	int i, j, r0, c0, r1, r2, rn, dr, nr;
+	AccRange * ar;
+	RECT rc;
+	lfPOINT *rs;
+
+	if(!isRowMark || !m_range || !m_range[0]){
+		InfoBox("No rows selected!");
+		return false;
+		}
+	Undo.DataObject(Disp, w, this, 0L, 0L);
+	Undo.String(0L, &m_range, UNDO_CONTINUE);
+	if(!(ar = new AccRange(m_range))) return false;
+	ar->BoundRec(&rc);
+	if(!(rs = (lfPOINT*)malloc((rc.bottom-rc.top+2)*sizeof(lfPOINT)))) {
+		delete ar;			return false;
+		}
+	if(!(ar->GetFirst(&c0, &r0))) {
+		free(rs);			delete ar;			return false;
+		}
+	ar->NextRow(&r1);		rn = r1;			nr = 0;
+	do {
+		for(r0 = r1 = r2 = rn;ar->NextRow(&r2); ) {
+			if(r2 > r1+1 || r2 < r0) break;
+			r1 = r2;
+			}
+		dr = r1-r0+1;		rn = r2;
+		rs[nr].fx = r0;		rs[nr].fy = r1;		nr++;
+		}while(r2!=r1);
+	SortFpArray(nr, rs);	nr--;
+	do {
+		r0 = (int)rs[nr].fx;	r1 = (int)rs[nr].fy;	dr = r1-r0+1;
+		if(AddRows(cRows + dr)) for(i = cRows-1; i >= r0+dr; i--) {
+			for(j = 0; j < cCols; j++) {
+				if(etRows && etRows[i-dr] && etRows[i-dr][j] && etRows[i] && etRows[i][j]) {
+					switch(etRows[i-dr][j]->type) {
+					default:
+						etRows[i][j]->SetText(etRows[i-dr][j]->text);
+						break;
+					case ET_VALUE:
+						etRows[i][j]->SetText(etRows[i-dr][j]->text);
+						etRows[i][j]->Value = etRows[i-dr][j]->Value; 
+						etRows[i][j]->type = ET_VALUE;
+						break;
+					case ET_FORMULA:
+						MoveFormula(this, etRows[i-dr][j]->text, TmpTxt, 0, dr, r0, -1);
+						etRows[i][j]->SetText(TmpTxt);	etRows[i][j]->type = ET_FORMULA;
+						break;
+						}
+					etRows[i-dr][j]->SetText("");
+					}
+				}
+			}
+		MoveFormula(this, m_range, TmpTxt, 0, dr, r0, -1);
+		strcpy(m_range, TmpTxt);		nr--;
+		for(i = 0; i < r0; i++) for(j = 0; j < cCols; j++) {
+			if(etRows && etRows[i] && etRows[i][j] && etRows[i][j]->type == ET_FORMULA) {
+				MoveFormula(this, etRows[i][j]->text, TmpTxt, 0, dr, r0, -1);
+				etRows[i][j]->SetText(TmpTxt);			etRows[i][j]->type = ET_FORMULA;
+				}
+			}
+		}while(nr >= 0);
+	delete ar;
+	if(w) {
+		sprintf(TmpTxt, "%d00", cRows);
+		w->oGetTextExtent(TmpTxt, 0, &i, &j);
+		if(i > FirstWidth) FirstWidth = i;
+		Disp->Command(CMD_SETSCROLL, 0L, w);			Disp->Command(CMD_MRK_DIRTY, 0L, w);
+		}
+	return true;
+}
 
 void
 SpreadData::DoPlot(anyOutput *o)
@@ -1140,34 +1473,51 @@ SpreadData::DoPlot(anyOutput *o)
 	AccRange *ar;
 
 	if(!w || !Disp) return;
-	w->Erase(0x00d8d8d8L);					w->ActualSize(&rc);
-	r_disp = (rc.bottom-rc.top)/CellHeight+1;
+	w->Erase(0x00e8e8e8L);					w->ActualSize(&rc);
+	w->SetTextSpec(&ssText);				et_racc = 0L;
+	r_disp = (rc.bottom-rc.top)/CellHeight;
 	c_disp = (rc.right-rc.left)/CellWidth+1;
-	Disp->ShowGrid(CellWidth, CellHeight, FirstWidth);
+	Disp->ShowGrid(CellWidth, CellHeight, FirstWidth, &currpos);
 	rc.top = w->MenuHeight;					rc.bottom = rc.top + CellHeight;
-	for(i = 0; i < (cRows - Disp->ssOrg.y) && i < r_disp; i++) {
+	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++) {
+		for(j = 0; j <= (cCols - Disp->ssOrg.x) && j <= c_disp; j++) {
 			r = i + Disp->ssOrg.y;	c = j + Disp->ssOrg.x; 
 			if(r < cRows && r >= 0 && c < cCols && c >=0 && etRows[r][c]){
-				etRows[r][c]->SetRec(&rc);	etRows[r][c]->Redraw(w, false);
+				etRows[r][c]->SetRec(&rc);	etRows[r][c]->Update(2, 0L, 0L);
+				etRows[r][c]->Redraw(w, false);
 				}
 			rc.left += CellWidth;			rc.right += CellWidth;
-			}
+			}
 		}
+	if(bUpdate) {
+		for(i = 0; i <= (cRows - Disp->ssOrg.y) && i <= r_disp; i++) {
+			for(j = 0; j <= (cCols - Disp->ssOrg.x) && j <= c_disp; j++) {
+				r = i + Disp->ssOrg.y;	c = j + Disp->ssOrg.x; 
+				if(r < cRows && r >= 0 && c < cCols && c >=0 && etRows[r][c]){
+					etRows[r][c]->Redraw(w, false);
+					}
+				}
+			}
+		}
+	if(CurrText && CurrText->row >= Disp->ssOrg.y && CurrText->row < (Disp->ssOrg.y + r_disp)
+			&& CurrText->col >= Disp->ssOrg.x && CurrText->col < (Disp->ssOrg.x +c_disp))
+			CurrText->Update(1, w, 0L);
+	bUpdate = false;
 	if(m_range && (ar = new AccRange(m_range)) && ar->GetFirst(&c, &r)) {
 		for( ; ar->GetNext(&c, &r); ) {
-			if(r >= Disp->ssOrg.y && r < (r_disp + Disp->ssOrg.y) && c >= Disp->ssOrg.x 
+			if(r >= Disp->ssOrg.y && r < (r_disp + Disp->ssOrg.y) && c >= Disp->ssOrg.x 
+				&& r < cRows && c < cCols
 				&& c < (c_disp + Disp->ssOrg.x) && etRows[r] && etRows[r][c])
 				etRows[r][c]->Mark(w, etRows[r][c] != CurrText ? 2 : 3);
 			}
 		delete (ar);
-		}
+		}
 	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;
+	w->ActualSize(&rc);		rc.bottom += CellHeight;		w->UpdateRect(&rc, false);
+	if(err_msg && (!last_err || strcmp(err_msg,last_err))) {
+		ErrorBox(last_err = err_msg);
 		}
 }
 
@@ -1175,24 +1525,22 @@ 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("");
-					}
+	int r, c;
+	RECT rec;
+
+	if(m_range && (ar = new AccRange(m_range)) && ar->GetFirst(&c, &r)) {
+		ar->BoundRec(&rec);
+		Undo.DataObject(Disp, w, this, &rec, 0L);
+		while(ar->GetNext(&c, &r)) {
+			if(r >= 0 && r < cRows && c >= 0 && c < cCols){
+				if(etRows[r][c] && etRows[r][c]->text) etRows[r][c]->SetText("");
 				}
 			}
 		delete (ar);	HideTextCursor();
-		}
+		}
 	HideMark(false);
+	if(CurrText) CurrText->Update(1, w, 0L);
+	bCopyCut = false;
 	return true;
 }
 
@@ -1200,17 +1548,18 @@ bool
 SpreadData::PasteRange(int cmd, char *txt)
 {
 	AccRange *cr;
-	int r, c;
+	int i, r, c;
 	RECT mrk_range;
 
 	if(new_mark && m_range && (cr = new AccRange(m_range)) && cr->GetFirst(&c, &r)) {
 		cr->BoundRec(&mrk_range);
-		for( ; cr->GetNext(&c, &r); ) if(c >= 0 && c < cCols && r >= 0 && r < cRows){
+		for(i = 0 ; cr->GetNext(&c, &r); i++) if(c >= 0 && c < cCols && r >= 0 && r < cRows){
 			currpos.x = c;	currpos.y = r;
 			switch(cmd){
-			case CMD_PASTE_TSV: ReadTSV(0L, (unsigned char*)txt, FF_TSV);	break;
-			case CMD_PASTE_XML: ReadXML(0L, (unsigned char*)txt, FF_XML);	break;
-			case CMD_PASTE_CSV: ReadData(0L, (unsigned char*)txt, FF_CSV);	break;
+			case CMD_PASTE_TSV: ReadTSV(0L, (unsigned char*)txt, FF_TSV);		break;
+			case CMD_PASTE_SSV: ReadTSV(0L, (unsigned char*)txt, FF_SSV);		break;
+			case CMD_PASTE_XML: ReadXML(0L, (unsigned char*)txt, FF_XML, i ? UNDO_CONTINUE : 0L); break;
+			case CMD_PASTE_CSV: ReadData(0L, (unsigned char*)txt, FF_CSV);		break;
 				}
 			if((mrk_range.right - mrk_range.left) == (cp_src_rec.right - cp_src_rec.left) &&
 				(mrk_range.bottom - mrk_range.top) == (cp_src_rec.bottom - cp_src_rec.top)) break;
@@ -1222,12 +1571,14 @@ SpreadData::PasteRange(int cmd, char *txt)
 	else switch(cmd){
 		case CMD_PASTE_TSV:
 			return ReadTSV(0L, (unsigned char*)txt, FF_TSV);
+		case CMD_PASTE_SSV:
+			return ReadTSV(0L, (unsigned char*)txt, FF_SSV);
 		case CMD_PASTE_XML:
-			return ReadXML(0L, (unsigned char*)txt, FF_XML);
+			return ReadXML(0L, (unsigned char*)txt, FF_XML, 0L);
 		case CMD_PASTE_CSV:
 			return ReadData(0L, (unsigned char*)txt, FF_CSV);
 		}
-	return false;
+	return bCopyCut = false;
 }
 
 bool
@@ -1241,7 +1592,7 @@ SpreadData::InitCopy(int cmd, void *tmpl, anyOutput *o)
 	if(cmd) {
 		bCopyCut = (cmd == CMD_CUT);
 		if(rcCopy.right > cCols) rcCopy.right = cCols;
-		if(rcCopy.bottom > cRows) rcCopy.bottom = cRows;
+		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);
@@ -1249,13 +1600,16 @@ SpreadData::InitCopy(int cmd, void *tmpl, anyOutput *o)
 				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;
+			}
+		else if (CurrText) {
+			if(CurrText->hasMark()) return CurrText->Command(CMD_COPY, o, 0L);
+			else {
+				if(m_range) free(m_range);	m_range=0L;
+				sprintf(TmpTxt, "%s%d:%s%d", Int2ColLabel(currpos.x, false), currpos.y+1,
+					Int2ColLabel(currpos.x, false), currpos.y+1);
+				m_range = strdup(TmpTxt);		DoPlot(o);
+				return InitCopy(cmd, tmpl, o);
+				}
 			}
 		}
 	if(bRet || !cmd) {		//calculate animated mark
@@ -1271,14 +1625,38 @@ SpreadData::InitCopy(int cmd, void *tmpl, anyOutput *o)
 			}
 		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;
+		r = rcCopy.bottom < (Disp->ssOrg.y + r_disp)? rcCopy.bottom : Disp->ssOrg.y + r_disp;
+		if(r >= cRows) r = cRows-1;			if(c >= cCols) c = cRows -1;
 		if(etRows[r][c]){
 			rc_band.right = etRows[r][c]->GetX()+CellWidth;
-			rc_band.bottom = etRows[r][c]->GetY()+CellHeight;
+			rc_band.bottom = etRows[r][c]->GetY()+CellHeight;
+			if(rc_band.left >= rc_band.right) rc_band.right = rc_band.left + CellWidth;
+			if(rc_band.top >= rc_band.bottom) rc_band.bottom = rc_band.top + CellHeight;
 			ShowCopyMark(o, &rc_band, 1);
 			}
 		}
 	return bRet;
+}
+
+bool
+SpreadData::SavePos()
+{
+	bool bRet = false;
+
+	if(pos_info.currpos.x != currpos.x || pos_info.currpos.y != pos_info.currpos.y
+		|| CurrText != pos_info.CurrText) {
+		Undo.Point(Disp, &currpos, w, UNDO_CONTINUE);
+		Undo.VoidPtr(Disp, (void**)&CurrText, 0L, 0L, UNDO_CONTINUE);
+		}
+	if(pos_info.ssOrg.x != Disp->ssOrg.x || pos_info.ssOrg.y != Disp->ssOrg.y) {
+		Swap(pos_info.ssOrg.x, Disp->ssOrg.x );		Swap(pos_info.ssOrg.y, Disp->ssOrg.y );
+		Undo.Point(Disp, &Disp->ssOrg, w, UNDO_CONTINUE);
+		Swap(pos_info.ssOrg.x, Disp->ssOrg.x );		Swap(pos_info.ssOrg.y, Disp->ssOrg.y );
+		}
+	pos_info.currpos.x = currpos.x;		pos_info.currpos.y = currpos.y;
+	pos_info.ssOrg.x = Disp->ssOrg.x;		pos_info.ssOrg.y = Disp->ssOrg.y;
+	pos_info.CurrText = CurrText;
+	return bRet;
 }
 
 bool
@@ -1297,11 +1675,13 @@ SpreadData::Command(int cmd, void *tmpl, anyOutput *o)
 		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)){
+		case MOUSE_LBDOWN:
+			if(m_range && (mev->StateFlags & 0x08)) mrk_offs = strlen(m_range);
+			else mrk_offs = 0;
+			bActive = true;		new_mark = false;
+			if(!et_racc && CurrText && !CurrText->isInRect(&p)){
 				CurrText->Update(2, w, &p);			 CurrText = 0L;	
 				DoPlot(w);
 				}
@@ -1316,20 +1696,20 @@ SpreadData::Command(int cmd, void *tmpl, anyOutput *o)
 			if(mev->Action == MOUSE_MOVE && (mev->StateFlags & 1) 
 				&& !(CurrText && CurrText->isInRect(&p))) {
 				//mark rectangular range
-				if(!m_range && CurrText) {
+				if(!et_racc && !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),
+				if(p.x < FirstWidth || p.y < (w->MenuHeight+CellHeight)) {
+					i = sprintf(TmpTxt+mrk_offs, "%s%s%d:", mrk_offs? ";" : "", 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), 
+					sprintf(TmpTxt+mrk_offs+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);
+					i = sprintf(TmpTxt+mrk_offs, "%s%s%d:", mrk_offs? ";" : "", Int2ColLabel(currpos.x, false), currpos.y+1);
+					sprintf(TmpTxt+mrk_offs+i, "%s%d", Int2ColLabel(cp.x, false), cp.y+1);
 					}
-				if(!CurrText)MarkRange(TmpTxt);
+				if(!CurrText || et_racc)MarkRange(TmpTxt);
 				return true;
 				}
 			if(mev->Action == MOUSE_LBDOUBLECLICK) bActive = false;
@@ -1341,31 +1721,51 @@ SpreadData::Command(int cmd, void *tmpl, anyOutput *o)
 				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(bActive){
+				isRowMark = p.x < FirstWidth;
+				isColMark = p.y < (w->MenuHeight+CellHeight);
+				if(isRowMark || isColMark) {
 					if(p.x < FirstWidth) {
 						currpos.x = 0;	cp.x = cCols-1;
 						}
 					if(p.y < (w->MenuHeight+CellHeight)) {
 						currpos.y = 0;	cp.y = cRows-1;
 						}
-					i = sprintf(TmpTxt, "%s%d:", Int2ColLabel(currpos.x, false), currpos.y+1);
-					sprintf(TmpTxt+i, "%s%d", Int2ColLabel(cp.x, false), cp.y+1);
+					i = sprintf(TmpTxt+mrk_offs, "%s%s%d:", mrk_offs? ";" : "", Int2ColLabel(currpos.x, false), currpos.y+1);
+					sprintf(TmpTxt+mrk_offs+i, "%s%d", Int2ColLabel(cp.x, false), cp.y+1);
 					MarkRange(TmpTxt);
 					currpos2.x = cp.x;		currpos2.y = cp.y + 1;
+					}
+				else if((m_range) && !new_mark) HideMark(false);
+				if(et_racc) {
+					CurrText = et_racc;
+					if(m_range && m_range[0]) {
+						CurrText->Command(CMD_ADDTXT, o, (DataObj*) m_range);
+						}
+					else if(!m_range) {
+						m_range = (char*)malloc(10);
+						sprintf(m_range, "%s%d%", Int2ColLabel(currpos.x, false), currpos.y+1);
+						CurrText->Command(CMD_ADDTXT, o, (DataObj*) m_range);
+						free(m_range);	m_range = 0L;
+						}
 					}
-				if(m_range) {
+				else if(m_range) {
 					CurrText = etRows[currpos.y][currpos.x];	CurrText->Update(1, w, &p);
 					DoPlot(w);
-					currpos2.x = cp.x;		currpos2.y = cp.y;
+					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);
+	case CMD_PASTE_TSV:		case CMD_PASTE_XML:	case CMD_PASTE_CSV:	case CMD_PASTE_SSV:
+		if(PasteRange(cmd, (char*)tmpl)) 
+			return Disp->Command(CMD_SETSCROLL, 0L, w);
 		return false;
+	case CMD_ETRACC:
+		et_racc = (EditText*) tmpl;
+		if(CurrText && CurrText->parent != this) CurrText = 0L;
+		return true;
 	case CMD_CURRPOS:
 		if(tmpl) {
 			if(((POINT*)tmpl)->x < 0 && ((POINT*)tmpl)->y < 0) {
@@ -1380,41 +1780,51 @@ SpreadData::Command(int cmd, void *tmpl, anyOutput *o)
 		break;
 	case CMD_ERROR:
 		err_msg = (char*)tmpl;
-		break;
+		break;
+	case CMD_UPDATE:
+		bUpdate = true;
+		break;
+	case CMD_SAVEPOS:
+		return SavePos();
 	case CMD_CLEAR_ERROR:
-		err_msg = 0L;
+		err_msg = last_err = 0L;
 		break;
-	case CMD_TOOLMODE:
+	case CMD_TOOLMODE:						//ESC pressed
 		HideMark(true);
-		if(CurrText) CurrText->Update(2, w, 0L);
-		if(CurrText) CurrText->Update(1, w, 0L);
+		if(CurrText){
+			CurrText->Update(2, w, 0L);		CurrText->Update(1, w, 0L);
+			}
+		et_racc = 0L;
 		break;
 	case CMD_UPDHISTORY:
 		if(w) w->FileHistory();
 		break;
 	case CMD_MRK_DIRTY:
+		move_cr = CMD_CURRDOWN;
+		last_err = 0L;
 		if(Disp) return Disp->Command(cmd, tmpl, o);
 		return false;
-	case CMD_ADDCHAR:
+	case CMD_ADDCHAR:
 		if(CurrText){
 			if(currpos.y < Disp->ssOrg.y) {
-				Disp->ssOrg.y = currpos.y;		DoPlot(o);
+				Disp->ssOrg.y = currpos.y;					Disp->Command(CMD_SETSCROLL, 0L, w);
 				}
 			else if(currpos.y > (Disp->ssOrg.y + r_disp -3)) {
 				Disp->ssOrg.y = currpos.y - (r_disp-3);
-				if(Disp->ssOrg.y < 0) Disp->ssOrg.y = 0;	DoPlot(o);
+				if(Disp->ssOrg.y < 0) Disp->ssOrg.y = 0;	Disp->Command(CMD_SETSCROLL, 0L, w);
 				}
 			if(currpos.x < Disp->ssOrg.x) {
-				Disp->ssOrg.x = currpos.x;		DoPlot(o);
+				Disp->ssOrg.x = currpos.x;					Disp->Command(CMD_SETSCROLL, 0L, w);
 				}
 			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);
+				if(Disp->ssOrg.x < 0) Disp->ssOrg.x = 0;	Disp->Command(CMD_SETSCROLL, 0L, w);
 				}
 			currpos2.x = currpos.x;		currpos2.y = currpos.y;
 			i = *((int*)tmpl);
+			Disp->Command(CMD_CURRPOS, &currpos, w);
 			if(i == 27) return Command(CMD_TOOLMODE, tmpl, o);
-			if(i !=3 && i != 22 && i != 24) HideMark(true);	//Do not hide upon ^C, ^V, ^X
+			if(i !=3 && i != 22 && i != 24 && i != 26) HideMark(true);	//Do not hide upon ^C, ^V, ^X, ^Z
 			if(i == 13) return CurrText->Command(move_cr, w, this);
 			switch(i) {
 			case 8: return CurrText->Command(CMD_BACKSP, w, this);	//Backspace
@@ -1425,27 +1835,64 @@ SpreadData::Command(int cmd, void *tmpl, anyOutput *o)
 			currpos.x = currpos2.x = Disp->ssOrg.x;			currpos.y = currpos2.y = Disp->ssOrg.y;
 			if(etRows[currpos.x] && (CurrText = etRows[currpos.x][currpos.y]))
 				CurrText->Update(1, w, &p);;
-			}
+			}
+		Disp->Command(CMD_CURRPOS, &currpos, w);
 		break;
 	case CMD_SHIFTUP:
+		if(Disp->ssOrg.y && currpos2.y <= Disp->ssOrg.y) {
+			Disp->ssOrg.y --;						Disp->Command(CMD_SETSCROLL, 0L, w);
+			}
 		currpos2.y -= 2;
 	case CMD_SHIFTDOWN:
+		if(cmd == CMD_SHIFTDOWN && r_disp > 3 && (currpos2.y-Disp->ssOrg.y) >= (r_disp-3)) {
+			Disp->ssOrg.y ++;						Disp->Command(CMD_SETSCROLL, 0L, w);
+			}
 		currpos2.y ++;
 		if(currpos2.y >= cRows) currpos2.y --;		if(currpos2.y < 0) currpos2.y ++;
 		//mark rectangular range
 		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);
+		MarkRange(TmpTxt);							HideTextCursor();
 		break;
 	case CMD_SHIFTRIGHT:	case CMD_SHIFTLEFT:
 		if(!m_range && CurrText && CurrText->Command(cmd, w, this)) break;
+		if(cmd == CMD_SHIFTLEFT && c_disp > 3 && Disp->ssOrg.x && (currpos2.x-Disp->ssOrg.x) < 1) {
+			Disp->ssOrg.x --;						Disp->Command(CMD_SETSCROLL, 0L, w);
+			}
+		if(cmd == CMD_SHIFTRIGHT && c_disp > 3 && (currpos2.x-Disp->ssOrg.x) >= (c_disp-3)) {
+			Disp->ssOrg.x ++;						Disp->Command(CMD_SETSCROLL, 0L, w);
+			}
 		if(cmd == CMD_SHIFTLEFT) currpos2.x --;
 		else currpos2.x ++;
 		if(currpos2.x >= cCols) currpos2.x --;		if(currpos2.x < 0) currpos2.x ++;
 		//mark rectangular range
 		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);
+		MarkRange(TmpTxt);							HideTextCursor();
+		break;
+	case CMD_SHPGUP:
+		if(Disp->ssOrg.y >0) {
+			Disp->ssOrg.y -= (r_disp-2);
+			if(Disp->ssOrg.y < 0) Disp->ssOrg.y = 0;
+			Disp->Command(CMD_SETSCROLL, 0L, w);
+			currpos2.y -= r_disp;					if(currpos2.y < 0) currpos2.y = 0;
+			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);							HideTextCursor();
+			}
+		break;
+	case CMD_SHPGDOWN:
+		i = (Disp->ssOrg.y + 2*r_disp) < cRows ? r_disp-2 : cRows-r_disp - Disp->ssOrg.y+3;
+		if(i > 0) {
+			Disp->ssOrg.y += i;						Disp->Command(CMD_SETSCROLL, 0L, w);
+			}
+		if(currpos2.y < (cRows-1)) {
+			currpos2.y += r_disp;					if(currpos2.y >= cRows) currpos2.y = cRows-1;
+			//mark rectangular range
+			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);							HideTextCursor();
+			}
 		break;
 	case CMD_CURRIGHT:		case CMD_CURRDOWN:
 		move_cr = cmd;
@@ -1457,6 +1904,7 @@ SpreadData::Command(int cmd, void *tmpl, anyOutput *o)
 		if(cmd == CMD_CURRLEFT && Disp->ssOrg.x && CurrText && (!CurrText->Cursor()) && (currpos.x == Disp->ssOrg.x)) {
 			Disp->ssOrg.x --;		currpos.x --;	
 			CurrText->Update(2, o, 0L);
+			Disp->Command(CMD_CURRPOS, &currpos, w);
 			if(etRows && etRows[currpos.y] && etRows[currpos.y][currpos.x]) 
 				CurrText = etRows[currpos.y][currpos.x];
 			if(CurrText){
@@ -1471,8 +1919,9 @@ SpreadData::Command(int cmd, void *tmpl, anyOutput *o)
 		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;
+		Disp->Command(CMD_CURRPOS, &currpos, w);
 		HideMark(false);
+		currpos2.x = currpos.x;								currpos2.y = currpos.y;
 		if(CurrText)return CurrText->Command(cmd, w, this);
 		else {
 			currpos.x = currpos2.x = Disp->ssOrg.x;			currpos.y = currpos2.y = Disp->ssOrg.y;
@@ -1504,10 +1953,23 @@ SpreadData::Command(int cmd, void *tmpl, anyOutput *o)
 			CurrText->Update(1, w, &p);
 			CurrText->Command(cmd == CMD_TAB ? CMD_POS_FIRST : CMD_POS_LAST, o, this);
 			}
+		Disp->Command(CMD_CURRPOS, &currpos, w);
 		return true;
+	case CMD_UNDO:
+		if(w) {
+			w->MouseCursor(MC_WAIT, true);
+			Undo.Restore(true, w);
+			w->MouseCursor(MC_ARROW, true);
+			if(et_racc) {
+				CurrText = et_racc;
+				CurrText->Update(1, w, 0L);
+				}
+			}
+		return true;
 	case CMD_DELETE:
 		if(m_range) DelRange();
 		else if(CurrText) return CurrText->Command(cmd, o, this);
+		Disp->Command(CMD_CURRPOS, &currpos, w);
 		return true;
 	case CMD_QUERY_COPY:		case CMD_CUT:
 		return InitCopy(cmd, tmpl, w);
@@ -1522,39 +1984,50 @@ SpreadData::Command(int cmd, void *tmpl, anyOutput *o)
 			FirstWidth = ((int*)tmpl)[0];	CellWidth = ((int*)tmpl)[1];
 			CellHeight = ((int*)tmpl)[2];
 			}
-		break;
+		break;
 	case CMD_TEXTSIZE:
-		if(tmpl){
-			CellHeight = *((int*)tmpl);
-			return Disp->Command(cmd, tmpl, o);
-			}
+		if(tmpl) return Disp->Command(cmd, tmpl, o);
 		return false;
 	case CMD_COPY_TSV:
 		return MemList((unsigned char**)tmpl, FF_TSV);
 	case CMD_COPY_SYLK:
 		return MemList((unsigned char**)tmpl, FF_SYLK);
 	case CMD_COPY_XML:
-		return MemList((unsigned char**)tmpl, FF_XML);
-	case CMD_REDRAW:	case CMD_DOPLOT:
+		return MemList((unsigned char**)tmpl, FF_XML);
+	case CMD_DOPLOT:	case CMD_REDRAW:
+		if(CurrText) CurrText->Update(2, 0L, 0L);
+		if(etRows && etRows[currpos.y] && currpos.y < cRows && currpos.x < cCols) 
+			if(CurrText = etRows[currpos.y][currpos.x]) CurrText->Update(1,0L, 0L);
 		DoPlot(o);
 		break;
-	case CMD_FILLRANGE:
-		FillSsRange(this, &m_range);
+	case CMD_FILLRANGE:
+		Undo.SetDisp(w);
+		FillSsRange(this, &m_range, Disp);
 		DoPlot(o);
-		break;
+		Undo.SetDisp(w);
+		break;
+	case CMD_INSROW:
+		return InsertRows();
+	case CMD_INSCOL:
+		return InsertCols();
+	case CMD_DELROW:
+		return DeleteRows();
+	case CMD_DELCOL:
+		return DeleteCols();
 	}
 	return true;
 }
 
 bool
-SpreadData::ReadXML(char *file, unsigned char *buffer, int type)
+SpreadData::ReadXML(char *file, unsigned char *buffer, int type, DWORD undo_flags)
 {
-	int i, row, col, tag, cpgr, spgr;
-	bool bContinue, bRet = false;
+	int i, row, col, tag, cpgr, spgr, ufl = 0;
+	bool bContinue, bRet = false, bUndo_done = false;
 	ReadCache *XMLcache;
 	POINT pt, mov;
 	char TmpTxt[1024], *tmp_range;
-	unsigned char *pgr = 0L;
+	unsigned char *pgr = 0L;
+	RECT rc_undo;
 
 	if(file) {
 		if(!(XMLcache = new ReadCache())) return false;
@@ -1563,7 +2036,8 @@ SpreadData::ReadXML(char *file, unsigned char *buffer, int type)
 			sprintf(TmpTxt, "Error open file\n\"%s\"", file);
 			ErrorBox(TmpTxt);
 			return false;
-			}
+			}
+		bUndo_done = true;
 		if(!Init(1, 1)) goto XMLError;
 		etRows[0][0]->SetText("");
 		}
@@ -1585,9 +2059,37 @@ SpreadData::ReadXML(char *file, unsigned char *buffer, int type)
 			}while(TmpTxt[0] && TmpTxt[0] != '<');
 		for(i = 1; i < 5; TmpTxt[i++] = XMLcache->Getc());
 		TmpTxt[i] = 0;
-		if(!strcmp("<cell", TmpTxt)) tag = 1;
+		if(!strcmp("<cell", TmpTxt)){
+			if(!bUndo_done) {
+				rc_undo.left = currpos.x;
+				rc_undo.right = cp_src_rec.right - cp_src_rec.left + currpos.x;
+				rc_undo.top = currpos.y;
+				rc_undo.bottom = cp_src_rec.bottom - cp_src_rec.top + currpos.y;
+				if(ufl == 3) Undo.DataObject(Disp, w, this, &rc_undo, undo_flags);
+				bUndo_done = true;
+				}
+ 			tag = 1;
+			}
 		else if(!strcmp("<pos1", TmpTxt)) tag = 2;
-		else if(!strcmp("<pos2", TmpTxt)) tag = 3;
+		else if(!strcmp("<pos2", TmpTxt)) tag = 3;
+		else if(!strcmp("<RLPl", TmpTxt)) {
+			do {
+				TmpTxt[i++] = XMLcache->Getc();
+				}while(TmpTxt[i-1] > 31 && TmpTxt[i-1] != '>');
+			TmpTxt[i] = 0;
+			row = col = 0;
+			if(TmpTxt[17] = '=' && TmpTxt[13] == 'r') {
+				strcpy(TmpTxt, TmpTxt+19);
+				}
+			else break;
+			sscanf(TmpTxt, "%d", &row);
+			for(i = 0; TmpTxt[i] && TmpTxt[i] != '='; i++);
+			sscanf(TmpTxt+i+2, "%d", &col);
+			if(row && col) {
+				AddCols(col);		AddRows(row);
+				}
+			row = col = -1;
+			}
 		else if(!strcmp("<Grap", TmpTxt)){
 			while(XMLcache->Getc() != '[');
 			pgr = (unsigned char*)malloc(spgr = 1000);
@@ -1619,10 +2121,12 @@ SpreadData::ReadXML(char *file, unsigned char *buffer, int type)
 				}
 			if(tag ==2) {
 				mov.x = col;				mov.y = row;
-				cp_src_rec.left = col;		cp_src_rec.top = row;
+				cp_src_rec.left = col;		cp_src_rec.top = row;
+				ufl |= 1;
 				}
 			else if(tag ==3) {
-				cp_src_rec.right = col;		cp_src_rec.bottom = row;
+				cp_src_rec.right = col;		cp_src_rec.bottom = row;
+				ufl |= 2;
 				}
 			else if(row && col) do {
 				do {
@@ -1639,7 +2143,7 @@ SpreadData::ReadXML(char *file, unsigned char *buffer, int type)
 					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);
+							MoveFormula(this, TmpTxt, TmpTxt, currpos.x-mov.x, currpos.y-mov.y, -1, -1);
 							}
 						etRows[row][col]->SetText(TmpTxt);
 						etRows[row][col]->Update(20, 0L, &pt);
@@ -1676,7 +2180,7 @@ SpreadData::ReadTSV(char *file, unsigned char *buffer, int type)
 		if(!Init(1, 1)) goto TSVError;
 		etRows[0][0]->SetText("");
 		}
-	else if(buffer && type == FF_TSV) {
+	else if(buffer && (type == FF_TSV || type == FF_SSV)) {
 		if(!(TSVcache = new MemCache(buffer))) return false;
 		}
 	else return false;
@@ -1691,10 +2195,12 @@ SpreadData::ReadTSV(char *file, unsigned char *buffer, int type)
 				if(col == currpos.x) break;
 				row ++;			col = currpos.x;		break;
 			case 0x09:					//tab
-				col ++;			break;
+				col ++;			break;
+			case ' ':
+				if(type == FF_SSV) col ++;				break;
 				}
 			}while(TmpTxt[0] && TmpTxt[0] < 33);
-		for(i = 1; (TmpTxt[i] = c = TSVcache->Getc())>=32; i++)
+		for(i = 1; (TmpTxt[i] = c = TSVcache->Getc())>= (type == FF_SSV ? 33 : 32); i++)
 			if(i >= 4094) i = 4094;
 		if(TmpTxt[0] && row >= cRows)AddRows(row+1);
 		if(TmpTxt[0] && col >= cCols)AddCols(col+1);
@@ -1708,6 +2214,8 @@ SpreadData::ReadTSV(char *file, unsigned char *buffer, int type)
 				row ++;			col = currpos.x;		break;
 			case 0x09:					//tab
 				col ++;			break;
+			case ' ':
+				if(type == FF_SSV) col ++;				break;
 				}
 			}
 		}while(!TSVcache->IsEOF());
@@ -1727,7 +2235,8 @@ SpreadData::MemList(unsigned char **ptr, int type)
 	double val;
 	*ptr = (unsigned char *)malloc(size = 10000);
 	unsigned char *tmpptr;
-	bool bLimit = true;
+	bool bLimit = true;
+	AccRange *ar = 0L;
 
 	if(!(*ptr))return false;
 	if(rcCopy.left < 0) rcCopy.left = 0;
@@ -1749,19 +2258,25 @@ SpreadData::MemList(unsigned char **ptr, int type)
 		cbd = sprintf((char*)*ptr, "<?xml version=\"1.0\"?><!DOCTYPE RLPlot-workbook>\n"
 		"<RLPlot-data rows=\"%d\" columns=\"%d\" >\n", cRows, cCols);
 		}
+	if (c_range &&  c_range[0]) {
+		ar = new AccRange(c_range);
+		if(bCopyCut) Undo.DataObject(Disp, w, this, &rcCopy, 0L);
+		}
 	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];
+				if(etRows[i] && etRows[i][j] && etRows[i][j]->text){
+					if((ar && ar->IsInRange(j,i)) || !ar) {
+						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){
+				if(etRows[i] && etRows[i][j] && etRows[i][j]->text && ((ar && ar->IsInRange(j,i)) || !ar)){
 					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);
@@ -1774,15 +2289,18 @@ SpreadData::MemList(unsigned char **ptr, int type)
 						cb += sprintf(tmptxt+cb, "%s\n", TmpTxt);
 						if(cb >= 267) bLimit = false;
 						}
+					if(bCopyCut) etRows[i][j]->SetText("");
 					}
 				break;
 			case FF_RLW:	case FF_XML:
-				if(etRows[i] && etRows[i][j] && etRows[i][j]->text){
+				if(etRows[i] && etRows[i][j] && etRows[i][j]->text && ((ar && ar->IsInRange(j,i)) || !ar)
+					&& etRows[i][j]->text[0]){
 					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");
+					cb += sprintf(tmptxt+cb, "</text>\n </cell>\n");
+					if(bCopyCut) etRows[i][j]->SetText("");
 					}
 				break;
 				}
@@ -1810,6 +2328,8 @@ SpreadData::MemList(unsigned char **ptr, int type)
 		sprintf((char*)*ptr+cbd,"</RLPlot-data>\n");
 		//note: cbd may be greater than size !
 		}
+	if(ar) delete ar;
+	bCopyCut = false;
 	return true;
 }
 
@@ -1818,8 +2338,8 @@ void SpreadMain(bool show)
 	static SpreadData *w = 0L;
 
 	if(show) {
-		w = new SpreadData();
-		if(!w ||!(w->Init(10, 10))){
+		w = new SpreadData(0L);
+		if(!w ||!(w->Init(50, 10))){
 			delete w;
 			w = 0L;
 			}
@@ -1829,5 +2349,3 @@ void SpreadMain(bool show)
 		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