//TheDialog.cpp, Copyright (c) 2001-2008 R.Lackner
//Operating system independent code for dialog boxes
//
//    This file is part of RLPlot.
//
//    RLPlot is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.
//
//    RLPlot is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with RLPlot; if not, write to the Free Software
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
#include "rlplot.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "TheDialog.h"

extern tag_Units Units[];
extern char TmpTxt[];
extern Default defs;
extern GraphObj *CurrGO;
extern EditText *CurrText;		//current EditText object
extern RECT rTxtCur;			//text cursor position and direction
extern UndoObj Undo;

char *WWWbrowser = 0L;
char *LoadFile = 0L;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// internal declarations 
static int xbase = 2;
static int ybase = 2;
int dlgtxtheight = 10;
static unsigned long DlgBGcolor = 0x00e0e0e0L;
static unsigned long DlgBGhigh = 0x00e8e8e8L;
TextDEF DlgText = {0x00000000L, 0x00ffffffL, 4.0, 0.0, 0.0, 0, 
	TXA_HLEFT | TXA_VTOP, TXM_TRANSPARENT, TXS_NORMAL, FONT_HELVETICA, 0L}; 

//prototypes: WinSpec.cpp
void *CreateDlgWnd(char *title, int x, int y, int width, int height, tag_DlgObj *d, DWORD flags);

//The dialog object which just has the input focus
Dialog *DialogFocus = 0L;
Dialog *DialogDefault = 0L;
Dialog *DialogTabStop = 0L;

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Base classes to dialog items
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DlgRoot::DlgRoot(DlgInfo *tmpl, DataObj *d)
{
	int i;
	RECT rc;

	dlg = 0L;			flags = 0L;		tabstops = 0L;
	DlgText.iSize = dlgtxtheight;		DlgText.ColBg = DlgBGcolor;
	DlgText.fSize = defs.GetSize(SIZE_TEXT);
	type = NONE;		Id = -2;		cContinue = 0;
	bActive = bRedraw = false;			c_go = CurrGO;
	CurrDisp = 0L;		oldFocus = DialogFocus;		DialogFocus = 0L;
	oldDefault = DialogDefault;			oldTabStop = DialogTabStop;
	data = d;			res_put = res_get = 0;		hDialog = 0L;
	if(ParentOut = Undo.cdisp) ParentOut->MouseCursor(MC_WAIT, false);
	mrk_item = 0L;		//if an item has a mark its this one
	bModal = true;		//assume its a modal dialog
	if(tmpl) {
		//count number of items first, then allocate memory
		for(cDlgs=1;!(tmpl[cDlgs-1].flags & LASTOBJ); cDlgs++);
		dlg = (DlgTmpl **)calloc(cDlgs+1, sizeof(DlgTmpl*));
		tabstops =(Dialog**)calloc(cDlgs, sizeof(Dialog*));
		if(dlg) for (i = 0; i < cDlgs; i++) {
			dlg[i] = (DlgTmpl *)malloc(sizeof(DlgTmpl));
			if(dlg[i]) {
				dlg[i]->id = tmpl[i].id;			dlg[i]->next = tmpl[i].next;
				dlg[i]->first = tmpl[i].first;			dlg[i]->flags = tmpl[i].flags;
				rc.left = tmpl[i].x * xbase;			rc.right = rc.left + tmpl[i].w * xbase;
				rc.top = tmpl[i].y * ybase;			rc.bottom = rc.top + tmpl[i].h * ybase;
				//an item appearing in the following list should have a corresponding
				//  entry in the list of ~DlgRoot()
				switch(tmpl[i].type) {
				case PUSHBUTTON:
					dlg[i]->dialog = new PushButton(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
					break;
				case TEXTBOX:
					dlg[i]->dialog = new TextBox(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
					break;
				case ARROWBUTT:
					dlg[i]->dialog = new ArrowButton(this, &tmpl[i],rc,(int*)tmpl[i].ptype);
					break;
				case COLBUTT:
					dlg[i]->dialog = new ColorButton(this, &tmpl[i],rc, (DWORD *)tmpl[i].ptype);
					break;
				case FILLBUTTON:
					dlg[i]->dialog = new FillButton(this, &tmpl[i],rc, (FillDEF *)tmpl[i].ptype);
					break;
				case SHADE3D:
					dlg[i]->dialog = new Shade3D(this, &tmpl[i],rc, (FillDEF *)tmpl[i].ptype);
					break;
				case LINEBUTT:
					dlg[i]->dialog = new LineButton(this, &tmpl[i],rc, (LineDEF *)tmpl[i].ptype);
					break;
				case SYMBUTT:
					dlg[i]->dialog = new SymButton(this, &tmpl[i],rc, (Symbol **)tmpl[i].ptype);
					break;
				case FILLRADIO:
					dlg[i]->dialog = new FillRadioButt(this, &tmpl[i],rc, *((unsigned int*)tmpl[i].ptype));
					break;
				case SYMRADIO:
					dlg[i]->dialog = new SymRadioButt(this, &tmpl[i],rc, (int*)tmpl[i].ptype);
					break;
				case CHECKBOX:
					dlg[i]->dialog = new CheckBox(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
					break;
				case CHECKPIN:
					dlg[i]->dialog = new CheckPin(this, &tmpl[i],rc);
					break;
				case TRASH:
					dlg[i]->dialog = new Trash(this, &tmpl[i],rc);
					break;
				case CONFIG:
					dlg[i]->dialog = new Config(this, &tmpl[i],rc);
					break;
				case RADIO0:	case RADIO1:	case RADIO2:
					dlg[i]->dialog = new RadioButton(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
					break;
				case LTEXT:		case RTEXT:		case CTEXT:
					dlg[i]->dialog = new Text(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
					break;
				case EDTEXT:
					dlg[i]->dialog = new InputText(this, &tmpl[i],rc,(char*)tmpl[i].ptype);
					break;
				case RANGEINPUT:
					dlg[i]->dialog = new RangeInput(this, &tmpl[i],rc,(char*)tmpl[i].ptype, data);
					break;
				case EDVAL1:
					dlg[i]->dialog = new InputValue(this, &tmpl[i],rc,(double*)tmpl[i].ptype);
					break;
				case INCDECVAL1:
					dlg[i]->dialog = new IncDecValue(this, &tmpl[i],rc,(double*)tmpl[i].ptype);
					break;
				case TXTHSP:
					dlg[i]->dialog = new TxtHSP(this, &tmpl[i],rc, (int*)tmpl[i].ptype);
					break;
				case VSCROLL:
					dlg[i]->dialog = new ScrollBar(this, &tmpl[i],rc, true);
					break;
				case HSCROLL:
					dlg[i]->dialog = new ScrollBar(this, &tmpl[i],rc, false);
					break;
				case ICON:
					dlg[i]->dialog = new Icon(this, &tmpl[i],rc, (int*)tmpl[i].ptype);
					break;
				case GROUP:
					dlg[i]->dialog = new Group(this, &tmpl[i], rc);
					break;
				case GROUPBOX:
					dlg[i]->dialog = new GroupBox(this, &tmpl[i],rc, (char*)tmpl[i].ptype);
					break;
				case SHEET:
					dlg[i]->dialog = new TabSheet(this, &tmpl[i],rc, (TabSHEET *)tmpl[i].ptype, data);
					break;
				case ODBUTTON:
					dlg[i]->dialog = new ODbutton(this, &tmpl[i],rc, tmpl[i].ptype);
					break;
				case LISTBOX1:
					dlg[i]->dialog = new Listbox(this, &tmpl[i],rc, (char**)tmpl[i].ptype);
					break;
				case TREEVIEW:
					dlg[i]->dialog = new Treeview(this, &tmpl[i],rc, (GraphObj*)tmpl[i].ptype);
					break;
				case LINEPAT:
					dlg[i]->dialog = new LinePat(this, &tmpl[i],rc, (LineDEF *)tmpl[i].ptype);
					break;
				default:
					dlg[i]->dialog = NULL;
					}
				}
			else break;
			}
		}
}

DlgRoot::~DlgRoot()
{
	int i;

	if(data) data->Command(CMD_ETRACC, 0L, 0L);
	defs.Idle(CMD_FLUSH);
	HideTextCursor();
	if(dlg){
		for (i = 0; dlg[i] && i < cDlgs; i++) {
			//we need to delete each object using a cast on its proper type
			//to call the proper destructor
			if(dlg[i]->dialog){
				switch(dlg[i]->dialog->type) {
				case PUSHBUTTON:	delete((PushButton*)dlg[i]->dialog);		break;
				case TEXTBOX:		delete((TextBox*)dlg[i]->dialog);			break;
				case ARROWBUTT:		delete((ArrowButton*)dlg[i]->dialog);		break;
				case COLBUTT:		delete((ColorButton*)dlg[i]->dialog);		break;
				case FILLBUTTON:	delete((FillButton*)dlg[i]->dialog);		break;
				case SHADE3D:		delete((Shade3D*)dlg[i]->dialog);			break;
				case LINEBUTT:		delete((LineButton*)dlg[i]->dialog);		break;
				case SYMBUTT:		delete((SymButton*)dlg[i]->dialog);			break;
				case FILLRADIO:		delete((FillRadioButt*)dlg[i]->dialog);		break;
				case SYMRADIO:		delete((SymRadioButt*)dlg[i]->dialog);		break;
				case CHECKBOX:		delete((CheckBox*)dlg[i]->dialog);			break;
				case CHECKPIN:		delete((CheckPin*)dlg[i]->dialog);			break;
				case TRASH:			delete((Trash*)dlg[i]->dialog);				break;
				case CONFIG:		delete((Config*)dlg[i]->dialog);			break;
				case RADIO0:	case RADIO1:
				case RADIO2:		delete((RadioButton*)dlg[i]->dialog);		break;
				case LTEXT:	case RTEXT:	
				case CTEXT:	delete((Text*)dlg[i]->dialog);						break;
				case EDTEXT:		delete((InputText*)dlg[i]->dialog);			break;
				case RANGEINPUT:	delete((RangeInput*)dlg[i]->dialog);			break;
				case EDVAL1:		delete((InputValue*)dlg[i]->dialog);		break;
				case INCDECVAL1:	delete((IncDecValue*)dlg[i]->dialog);		break;
				case TXTHSP:		delete((TxtHSP*)dlg[i]->dialog);			break;
				case HSCROLL:
				case VSCROLL:		delete((ScrollBar*)dlg[i]->dialog);			break;
				case ICON:			delete((Icon*)dlg[i]->dialog);				break;
				case GROUP:			delete((Group*)dlg[i]->dialog);				break;
				case GROUPBOX:		delete((GroupBox*)dlg[i]->dialog);			break;
				case SHEET:			delete((TabSheet*)dlg[i]->dialog);			break;
				case ODBUTTON:		delete((ODbutton*)dlg[i]->dialog);			break;
				case LISTBOX1:		delete((Listbox*)dlg[i]->dialog);			break;
				case TREEVIEW:		delete((Treeview*)dlg[i]->dialog);			break;
				case LINEPAT:		delete((LinePat*)dlg[i]->dialog);			break;
				default:
				//DEBUG: we should issue a message that an unknown item is
				//   deleted: this might result in a memory leak;
					InfoBox("unknown dialog object found\nin \"DlgRoot::~DlgRoot()\"");
					delete(dlg[i]->dialog);
					break;
					}
				}
			free(dlg[i]);
			}
		free(dlg);					dlg=0L;
		}
	if(tabstops) free(tabstops);	tabstops = 0L;
	DialogFocus = oldFocus;			DialogDefault = oldDefault;
	DialogTabStop = oldTabStop;		CurrGO = c_go;
	if(Undo.cdisp)Undo.cdisp->MouseCursor(MC_ARROW, false);
}

bool
DlgRoot::Command(int cmd, void *tmpl, anyOutput *o)
{
	Dialog *d;
	int i, j;

	switch (cmd) {
	case CMD_UNDO:
		if(CurrDisp) {
			Undo.Restore(false, CurrDisp);
			DoPlot(CurrDisp);
			}
		return true;
	case CMD_REDRAW:
		if(CurrDisp) {
			CurrDisp->Erase(DlgBGcolor);		DoPlot(CurrDisp);
			defs.Idle(CMD_UPDATE);
			}
		return true;
	case CMD_MOUSE_EVENT:
		if(DialogFocus && DialogFocus->type == TEXTBOX && DialogFocus->Command(cmd, tmpl, o)) return true; 
		mev = (MouseEvent *) tmpl;
		switch(mev->Action) {
		case MOUSE_LBDOWN:
			bActive = true;
		case MOUSE_MOVE:
			//track mouse and display controls accordingly
			if(!(mev->StateFlags & 1)) break;
			if(bActive)ForEach(CMD_MOUSE_EVENT, 0, o);
			break;
		case MOUSE_LBDOUBLECLICK:
			ForEach(CMD_MOUSE_EVENT, 0, o);
			bActive = false;				//skip next event (LB up);
			break;
		case MOUSE_LBUP:
			if(bActive)ForEach(CMD_LBUP, 0, o);
			break;
			}
		defs.Idle(CMD_UPDATE);
		return true;
	case CMD_ENDDIALOG:
		d = (Dialog *)tmpl;
		if(d) {
			res_q[res_put++] = d->Id;		// end dialog by object
			cContinue = 0;
			}
		else if(cContinue >0) {				// no end upon killing the focus
			cContinue--;
			return true;
			}
		else {
			res_q[res_put++] = 0;			// end dialog with closebox or loose focus
			bRedraw = true;
			}
		res_put &= 0xff;
		return true;
	case CMD_CONTINUE:
		cContinue++;
		return true;
	case CMD_TABDLG:
		if(tabstops) for (i = 0; i < cDlgs; i++) 
			if(!tabstops[i] || tabstops[i] == (Dialog*)tmpl) {
				tabstops[i] = (Dialog*)tmpl;
				return true;
				}
		return false;
	case CMD_NOTABDLG:
		if(tabstops) for (i = j = 0; i < cDlgs; i++) {
			if(tabstops[i] == (Dialog*)tmpl) tabstops[i] = 0L;
			if(tabstops[i]) tabstops[j++] = tabstops[i];
			}
		return true;
	case CMD_TAB:
		HideTextCursor();
		if(tabstops && DialogTabStop) {
			for(i = 0; tabstops[i] && tabstops[i] != DialogTabStop && i < cDlgs; i++);
			if((tabstops[i]) || (tabstops[i]->flags & HIDDEN)) i++;
			if(!tabstops[i]) i = 0;
			switch(tabstops[i]->type) {
			case PUSHBUTTON:
				d = DialogDefault;
				DialogTabStop = DialogDefault = tabstops[i];
				if(d) d->DoPlot(o);
				DialogDefault->DoPlot(o);
				break;
			case EDTEXT:			case EDVAL1:	case RANGEINPUT:
			case INCDECVAL1:
				DialogTabStop = DialogFocus = tabstops[i];
				if((InputText*)DialogFocus->bActive)
					((InputText*)DialogFocus)->Activate(DialogFocus->Id, true);
				else Command(cmd, tmpl, o);
				break;
				}
			}
		return true;
	case CMD_SHTAB:
		HideTextCursor();
		if(tabstops && DialogTabStop) {
			for(j = 0; tabstops[j]; j++);
			for(i = j-1; tabstops[i] != DialogTabStop && i; i--);
			i = i >0 ? i-1 : j-1;
			switch(tabstops[i]->type) {
			case PUSHBUTTON:
				d = DialogDefault;
				DialogTabStop = DialogDefault = tabstops[i];
				d->DoPlot(o);
				DialogDefault->DoPlot(o);
				break;
			case EDTEXT:			case EDVAL1:			case INCDECVAL1:
			case RANGEINPUT:
				DialogTabStop = DialogFocus = tabstops[i];
				((InputText*)DialogFocus)->Activate(DialogFocus->Id, true);
				break;
				}
			}
		return true;
	case CMD_CURRUP:	case CMD_CURRDOWN:
		if(DialogFocus && DialogFocus->type == TEXTBOX)
			return DialogFocus->Command(cmd, tmpl, o);
        else return CurUpDown(cmd);
	case CMD_CURRLEFT:	case CMD_CURRIGHT:	case CMD_DELETE:
	case CMD_POS_FIRST:	case CMD_POS_LAST:	case CMD_SHIFTLEFT:
	case CMD_SHIFTRIGHT:	case CMD_COPY:	case CMD_PASTE:
		Undo.SetDisp(CurrDisp);
		bActive = true;
		if(DialogFocus)return DialogFocus->Command(cmd, tmpl, CurrDisp);
        else return false;
	case CMD_ADDCHAR:
		if(!tmpl) return false;
		bActive = true;
		if(*((int*)tmpl) == 27) {						//Esc
			HideCopyMark();
			for (i = 0; dlg[i] && i < cDlgs; i++) 
				if(dlg[i]->dialog) dlg[i]->dialog->Command(cmd, tmpl, o);
			return Command(CMD_REDRAW, 0L, 0L);
			}
		if(DialogDefault && *((int*)tmpl) == 0x0d){		//return pressed
			HideTextCursor();
			return DialogDefault->Command(cmd, tmpl, o);
			}
		if(DialogFocus)return DialogFocus->Command(cmd, tmpl, o);
        else return false;
	case CMD_UNLOCK:
		CurrDisp = 0L;
		for(i = 0; i < cDlgs; i++)
			if(dlg[i] && dlg[i]->dialog) dlg[i]->dialog->Command(CMD_UNLOCK, 0L, 0L);
		break;
	case CMD_MARKOBJ:
		if(mrk_item && mrk_item != (Dialog*)tmpl){
			i = 27;
			mrk_item->Command(CMD_ADDCHAR, &i, o);
			}
		mrk_item = (Dialog*)tmpl;
		break;
		}
	return false;
}

void
DlgRoot::DoPlot(anyOutput *o)
{
	int i;

	HideCopyMark();			mrk_item = 0L;			bRedraw = false;
	HideTextCursor();
	if(tabstops) for(i = 0; i < cDlgs; tabstops[i++] = 0);
	if(o)CurrDisp = o;		DialogDefault = 0L;
	if(CurrDisp) {
		CurrDisp->SetTextSpec(&DlgText);
		ForEach(CMD_DOPLOT, 0, CurrDisp);
		}
	defs.Idle(CMD_UPDATE);
}

bool
DlgRoot::CurUpDown(int cmd)
{
	int i, ya, yb, dy;
	Dialog *above=0L, *below=0L;

	ya = -1000;		yb = 10000;
	if(DialogFocus && tabstops && DialogTabStop == DialogFocus) {
		for(i = 0; tabstops[i] && i < cDlgs; i++) {
			if(tabstops[i] != DialogTabStop) {
				switch(tabstops[i]->type) {
				case EDVAL1:	case INCDECVAL1:	case EDTEXT:	case RANGEINPUT:
					if(rTxtCur.left > tabstops[i]->cr.left && 
						rTxtCur.right < tabstops[i]->cr.right) {
						if((dy = (tabstops[i]->cr.top - rTxtCur.top))< 0) {
							if(dy > ya) {
								ya = dy;	above = tabstops[i];
								}
							}
						else {
							if(dy < yb) {
								yb = dy;	below = tabstops[i];
								}
							}
						}
					break;
					}
				}
			}
		switch(cmd) {
		case CMD_CURRUP:
			if(above) {
				above->Select(rTxtCur.left, (above->cr.top + above->cr.bottom)>>1, CurrDisp); 
				}
			break;
		case CMD_CURRDOWN:
			if(below) {
				below->Select(rTxtCur.left, (below->cr.top + below->cr.bottom)>>1, CurrDisp); 
				}
			break;
			}
		}
	return false;
}

bool
DlgRoot::GetColor(int id, DWORD *color)
{
	int i;

	i = FindIndex(id);
	if(i && color && dlg[i] && dlg[i]->dialog) return dlg[i]->dialog->GetColor(id, color);
	return false;
}

void
DlgRoot::SetColor(int id, DWORD color)
{
	int i;

	i = FindIndex(id);
	if(i && dlg[i]) {
		dlg[i]->dialog->SetColor(id, color);
		if(CurrDisp && !(dlg[i]->dialog->flags & HIDDEN)) dlg[i]->dialog->DoPlot(CurrDisp);
		}
}

bool
DlgRoot::GetValue(int id, double *val)
{
	int i;

	i = FindIndex(id);
	if(i && dlg[i]) return dlg[i]->dialog->GetValue(id, val);
	return false;
}

bool
DlgRoot::GetInt(int id, int *val)
{
	int i;

	i = FindIndex(id);
	if(i && dlg[i]) return dlg[i]->dialog->GetInt(id, val);
	return false;
}

bool
DlgRoot::SetCheck(int id, anyOutput *o, bool state)
{
	int i;

	i = FindIndex(id);
	if(i && dlg[i]) return dlg[i]->dialog->SetCheck(id, o ? o : CurrDisp, state);
	return false;
}

bool
DlgRoot::GetCheck(int Id)
{
	int i;

	i = FindIndex(Id);
	if(i && dlg[i]) return dlg[i]->dialog->GetCheck(Id);
	return false;
}

bool
DlgRoot::GetText(int id, char *txt, int size)
{
	int i;

	i = FindIndex(id);
	if(i && dlg[i]) return dlg[i]->dialog->GetText(id, txt, size);
	return false;
}

bool
DlgRoot::SetText(int id, char *txt)
{
	int i;

	i = FindIndex(id);
	if(i && dlg[i] && dlg[i]->dialog->Command(CMD_SETTEXT, txt, CurrDisp))bRedraw = true;
	else return false;
	return true;
}

bool
DlgRoot::SetValue(int id, double val)
{
	int i;
	char tmp_txt[80];

	i = FindIndex(id);
	WriteNatFloatToBuff(tmp_txt, val);
	if(i && dlg[i] && dlg[i]->dialog->Command(CMD_SETTEXT, tmp_txt+1, CurrDisp))bRedraw = true;
	else return false;
	return true;
}


bool
DlgRoot::TextStyle(int id, int style)
{
	int i;

	i = FindIndex(id);
	if(i && dlg[i]) dlg[i]->dialog->TextDef.Style = style;
	else return false;
	return true;
}

bool
DlgRoot::TextFont(int id, int font)
{
	int i;

	i = FindIndex(id);
	if(i && dlg[i]) dlg[i]->dialog->TextDef.Font = font;
	else return false;
	return true;
}

bool
DlgRoot::TextSize(int id, int size)
{
	int i;

	i = FindIndex(id);
	if(size <= 0.001f) return false;
	if(i && dlg[i]) dlg[i]->dialog->TextDef.iSize = size;
	else return false;
	return true;
}

bool
DlgRoot::ShowItem(int id, bool show)
{
	int i;

	i = FindIndex(id);
	if(i && dlg[i]) dlg[i]->dialog->flags = show ? 
		dlg[i]->dialog->flags & ~HIDDEN : dlg[i]->dialog->flags | HIDDEN;
	else return false;
	return true;
}

int
DlgRoot::GetResult()
{
	int ret;

	if(res_put != res_get) ret = res_q[res_get++];
	else ret = -1;
	res_get &= 0xff;
	if(bRedraw)DoPlot(0L);
	else defs.Idle(CMD_UPDATE);
	if(ret >= 0 && ParentOut) Undo.SetDisp(ParentOut);
	return ret;
}

int
DlgRoot::FindIndex(unsigned short id)
{
	int i;

	for (i = 0; dlg[i] && i < cDlgs; i++) if(dlg[i]->id == id) return i;
	return 0;
}

void
DlgRoot::ForEach(int cmd, int start, anyOutput *o)
{
	int i, j, next;

	if(o)CurrDisp = o;
	if(dlg && CurrDisp) {
		next = start;
		do {
			if(dlg[next] && dlg[next]->first) {
				if(dlg[next]->flags && ISPARENT) {
					if(dlg[next]->dialog) {
						dlg[next]->dialog->Command(CMD_FLUSH, 0L, 0L);
						//if j equals cDlgs we are caught in a circular reference
						for(j = 0, i = dlg[next]->first; i && j < cDlgs; j++) {
							if(i = FindIndex(i)) {
								dlg[next]->dialog->Command(CMD_ADDCHILD, (void*)dlg[i]->dialog, 0L);
								i = dlg[i]->next;
								}
							else{
								i=i;
								}
							}
						}
					else return;	//error bad structured template
					}
				else {	//a debugging aid ....
					InfoBox("Warning:\ndialog contains\ngroup which is not parent");
					}
				//resolve sub-groups recursively
				//  this will not result in any action for children 
				//  because the parent is not this!
				ForEach(cmd, FindIndex(dlg[next]->first), 0L);
				}
			//parent objects (groups) will channel command to children
			if(dlg[next] && dlg[next]->dialog && dlg[next]->dialog->parent == this && 
				!(dlg[next]->dialog->flags & HIDDEN)) switch(cmd) {
				case CMD_DOPLOT: 
					dlg[next]->dialog->DoPlot(CurrDisp);
					break;
				case CMD_LBUP:
					dlg[next]->dialog->Select(mev->x, mev->y, CurrDisp);
					break;
				case CMD_MOUSE_EVENT:
					dlg[next]->dialog->MBtrack(mev, CurrDisp);
					break;
				case CMD_SELECT:
					dlg[next]->dialog->Select(mev->x, mev->y, CurrDisp);
					break;
				}
			next = FindIndex(dlg[next] ? dlg[next]->next : 0);
			}while(next && next < cDlgs);
		}
}

void
DlgRoot::Activate(int id, bool active)
{
	int i;

	i = FindIndex(id);
	if(i && dlg[i]) dlg[i]->dialog->Activate(id, active);
}

bool
DlgRoot::ItemCmd(int id, int cmd, void *tmpl)
{
	int i;

	if((i = FindIndex(id)) && dlg[i]){ 
		bRedraw = true;
		return dlg[i]->dialog->Command(cmd, tmpl, CurrDisp);
		}
	return false;
}

Dialog::Dialog(tag_DlgObj *par, DlgInfo *desc, RECT rec)
{
	parent = par;
	Id = desc->id;
	flags = desc->flags;
	memcpy(&cr, &rec, sizeof(RECT));	memcpy(&hcr, &rec, sizeof(RECT));
	Line.width = 0.0;					Line.patlength = 1.0;
	Line.color = DlgBGcolor;			Line.pattern = 0x00000000L;
	Fill.type = FILL_NONE;				Fill.color = DlgBGcolor;
	Fill.scale = 1.0;					Fill.hatch = 0L;
	memcpy(&TextDef, &DlgText, sizeof(TextDEF));
	type = desc->type;
	bChecked = flags & CHECKED ? true : false;
	bLBdown = false;
	if(DEFAULT == (flags & DEFAULT)) DialogDefault = DialogTabStop = this;
	bActive = true;
}

bool
Dialog::Select(int x, int y, anyOutput *o)
{
	if(x > cr.left && x < cr.right && y > cr.top && y < cr.bottom) {
		if((flags & TOUCHEXIT) && parent)
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		return true;
		}
	return false;
}

bool 
Dialog::SetCheck(int id, anyOutput *o, bool state)
{
	if(state != bChecked) {
		if(parent && state && (flags & ISRADIO)) parent->Command(CMD_RADIOBUTT, (void *)this, o);
		bChecked = state;
		if(o) DoPlot(o);
		return true;
		}
	return false;
}

void
Dialog::MBtrack(MouseEvent *mev, anyOutput *o)
{
	bool bLBstate = false;

	if(mev->Action == MOUSE_LBDOUBLECLICK) {
		Select(mev->x, mev->y, o);
		return;
		}
	switch(type){
	case PUSHBUTTON:	case ARROWBUTT:		case CHECKBOX:		case RADIO1:
	case RADIO2:		case TRASH:			case CONFIG:		case CHECKPIN:
		if(mev->StateFlags &1) bLBstate = true;
		if(IsInRect(&hcr, mev->x, mev->y) && bLBstate){
			if(parent && type != CHECKBOX && type != RADIO1 && type != RADIO2) parent->Command(CMD_MARKOBJ, this, o);
			if(!bLBdown){
				bLBdown = bLBstate;			DoPlot(o);
				return;
				}
			}
		else if(bLBdown){
			bLBdown = false;
			DoPlot(o);
			}
		break;
		}
}

void
Dialog::Activate(int id, bool active)
{
	if(id == Id) bActive = active;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Collection of dialog items
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// a simple text button
PushButton::PushButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
	:Dialog(par, desc, rec)
{
	if(text && text[0]) Text = (char*)memdup(text, (int)strlen(text)+1, 0);
	else Text = 0L;
	TextDef.Align = TXA_HCENTER | TXA_VCENTER;
}

PushButton::~PushButton()
{
	if(Text) free (Text);
}

bool
PushButton::Command(int cmd, void *tmpl, anyOutput *o)
{
	switch(cmd) {
	case CMD_ENDDIALOG:
		parent->Command(CMD_ENDDIALOG, (void *)this, o);
		return true;
	case CMD_ADDCHAR:
		HideCopyMark();
		if(parent && *((int*)tmpl) == 0x0d)		//return pressed
			 parent->Command(CMD_ENDDIALOG, (void *)this, o);
		return true;
		}
	return false;
}

void
PushButton::DoPlot(anyOutput *o)
{
	POINT pts[3];

	Line.color = 0x00000000L;			Fill.color = DlgBGhigh;
	o->SetLine(&Line);					o->SetFill(&Fill);
	if(bLBdown) o->oRectangle(cr.left, cr.top, cr.right-1, cr.bottom-1);
	else {
		o->oRectangle(cr.left, cr.top, cr.right-2, cr.bottom-2);
		Line.color = DlgBGcolor;
		o->SetLine(&Line);
		pts[0].x = cr.left;					pts[0].y = pts[1].y = cr.bottom-1;
		pts[1].x = pts[2].x = cr.right-1;	pts[2].y = cr.top-1;
		o->oPolyline(pts, 3);
		Line.color = 0x00000000L;
		o->SetLine(&Line);
		pts[0].x = cr.left+5;				pts[0].y = pts[1].y = cr.bottom-1;
		pts[1].x = pts[2].x = cr.right-1;	pts[2].y = cr.top +1;
		o->oPolyline(pts, 3);
		Line.color = 0x00ffffffL;
		o->SetLine(&Line);
		pts[0].x = pts[1].x = cr.left;
		pts[0].y = cr.bottom -3;
		pts[1].y = pts[2].y = cr.top;
		pts[2].x = cr.right -2;
		o->oPolyline(pts, 3);
		}
	if(Text) {
		TextDef.Style = DialogDefault == this ? TXS_BOLD : TXS_NORMAL;
		o->SetTextSpec(&TextDef);
		o->oTextOut((cr.left + cr.right)/2-2, (cr.top + cr.bottom)/2-1, Text, 0);
		}
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
	if(parent)parent->Command(CMD_TABDLG, this, o);
}

bool
PushButton::Select(int x, int y, anyOutput *o)
{
	if(IsInRect(&cr, x, y)) {
		bLBdown = false;		DoPlot(o);
		if(parent) parent->Command(CMD_ENDDIALOG, (void *)this, o);
		return true;
		}
	else if(bLBdown) {
		bLBdown = false;		DoPlot(o);
		}
	return false;
}

TextBox::TextBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
	:Dialog(par, desc, rec)
{
	lfPOINT lfp1, lfp2;

	TextDef.Font = FONT_COURIER;	TextDef.Align = TXA_VBOTTOM | TXA_HLEFT;
#ifdef _WINDOWS
//	TextDef.fSize unchanged
#else
	TextDef.fSize *= .8;
#endif
	Fill.color = 0x00ffffffL;	Line.color = 0x00000000L;	cont = 0L;
	lfp1.fx = rec.left;				lfp1.fy = rec.top;
	lfp2.fx = rec.right;			lfp2.fy = rec.bottom;
	if(cont = new TextFrame(0L, 0L, &lfp1, &lfp2, text)) cont->Command(CMD_SETTEXTDEF, &TextDef, 0L);
}

TextBox::~TextBox()
{
	if(cont)DeleteGO(cont);
}

bool
TextBox::Command(int cmd, void *tmpl, anyOutput *o)
{
	switch(cmd) {
	case CMD_CURRLEFT:		case CMD_CURRIGHT:		case CMD_DELETE:
	case CMD_POS_FIRST:		case CMD_POS_LAST:		case CMD_SHIFTLEFT:
	case CMD_SHIFTRIGHT:	case CMD_ADDCHAR:		case CMD_MOUSE_EVENT:
	case CMD_COPY:			case CMD_PASTE:			case CMD_CURRUP:
	case CMD_CURRDOWN:
		if(bChecked && CurrGO && CurrGO == cont) return CurrGO->Command(cmd, tmpl, o);
		break;
	case CMD_SETTEXT:
		if(cont)return cont->Command(cmd, tmpl, o);
		return false;
		}
	return false;
}

void
TextBox::DoPlot(anyOutput *o)
{
	if(cont && o)cont->DoPlot(o);
}

bool
TextBox::Select(int x, int y, anyOutput *o)
{
	POINT p1;

	p1.x = x;			p1.y = y;
	if(bActive && IsInRect(&cr, x, y) && !(flags & NOEDIT)) {
		DialogDefault = DialogFocus = DialogTabStop = this;
		if(CurrGO = cont) bChecked = cont->Command(CMD_SELECT, &p1, o);
		}
	return false;
}

void
TextBox::MBtrack(MouseEvent *mev, anyOutput *o)
{
	bool bLBstate;
	int x, y;

	x = mev->x;						y = mev->y;
	if(mev->StateFlags &1) bLBstate = true;
	else bLBstate = false;
	if(bActive && IsInRect(&cr, x, y) && !(flags & NOEDIT) && 
		bLBstate && cont && parent) {
		DialogFocus = this;			bChecked = true;
		parent->Command(CMD_FOCTXT, (void*)this, 0L);
		cont->Command(CMD_MOUSE_EVENT, mev, o);
		parent->Command(CMD_MARKOBJ, this, o);
		}
}

bool
TextBox::GetText(int id, char *txt, int size)
{
	if(cont) {
		if(!(cont->Command(CMD_ALLTEXT, TmpTxt, 0L))) return false;
		rlp_strcpy(txt, size, TmpTxt);
		return true;
		}
	return false;
}

ArrowButton::ArrowButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, int *which)
	:Dialog(par, desc, rec)
{
	direct = which ? *which : 0;
}

void
ArrowButton::DoPlot(anyOutput *o)
{
	POINT pts[4];
	int ix, iy, dx, dy;

	Line.color = 0x00000000L;
	Fill.color = DlgBGhigh;
	ix = (dx =(cr.right-cr.left))>5 ? dx>>2 : 2;
	iy = (dy =(cr.bottom-cr.top))>5 ? dy>>2 : 2;
	o->SetLine(&Line);						o->SetFill(&Fill);
	if(bLBdown) o->oRectangle(cr.left, cr.top, cr.right-1, cr.bottom-1);
	else {
		o->oRectangle(cr.left, cr.top, cr.right-2, cr.bottom-2);
		Line.color = DlgBGcolor;
		o->SetLine(&Line);
		pts[0].x = cr.left;					pts[0].y = pts[1].y = cr.bottom-1;
		pts[1].x = pts[2].x = cr.right-1;	pts[2].y = cr.top-1;
		o->oPolyline(pts, 3);				Line.color = 0x00ffffffL;
		o->SetLine(&Line);					pts[0].x = pts[1].x = cr.left;
		pts[0].y = cr.bottom -3;			pts[1].y = pts[2].y = cr.top;
		pts[2].x = cr.right -2;				o->oPolyline(pts, 3);
		}
	Fill.color = Line.color = 0x00000000L;
	o->SetLine(&Line);						o->SetFill(&Fill);
	switch(direct) {
	case 1:
		pts[0].x = pts[3].x = cr.left+ix;	pts[0].y = pts[3].y = pts[1].y = cr.bottom-(iy<<1);
		pts[1].x = cr.right-(ix<<1);		pts[2].x = (cr.right + cr.left)/2 -1;
		pts[2].y = cr.top+iy;				o->oPolygon(pts, 4);
		break;
	case 2:
		pts[0].x = pts[3].x = cr.left+ix;	pts[0].y = pts[3].y = pts[1].y = cr.top+iy;
		pts[1].x = cr.right-(ix<<1);		pts[2].x = (cr.right + cr.left)/2 -1;
		pts[2].y = cr.bottom-(iy<<1);		o->oPolygon(pts, 4);
		break;
	case 3:
		pts[0].x = pts[3].x = cr.left+ix;	pts[0].y = pts[3].y = (cr.bottom + cr.top)/2-1;
		pts[1].x = pts[2].x = cr.right-(ix<<1);
		pts[1].y = cr.bottom-(iy<<1);		pts[2].y = cr.top+iy;
		o->oPolygon(pts, 4);
		break;
	case 4:
		pts[0].x = pts[3].x = cr.right-(ix<<1);
		pts[0].y = pts[3].y = (cr.bottom + cr.top)/2-1;
		pts[1].x = pts[2].x = cr.left+2;	pts[1].y = cr.bottom-(iy<<1);
		pts[2].y = cr.top+iy;				o->oPolygon(pts, 4);
		break;
		}
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
ArrowButton::Select(int x, int y, anyOutput *o)
{
	if(IsInRect(&cr, x, y)) {
		bLBdown = false;					DoPlot(o);
		if(parent) parent->Command(CMD_ENDDIALOG, (void *)this, o);
		return true;
		}
	else if(bLBdown) {
		bLBdown = false;					DoPlot(o);
		}
	return false;
}


ColorButton::ColorButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, DWORD *color)
	:Dialog(par, desc, rec)
{
	col = color ? *color : 0x0L;
}

void
ColorButton::DoPlot(anyOutput *o)
{
	POINT pts[5];

	Fill.color = (col & 0x00ffffffL);
	o->SetFill(&Fill);
	if(flags & ISRADIO) {
		Line.color = bChecked ? 0x00000000L : DlgBGcolor;
		o->SetLine(&Line);
		pts[0].x = pts[3].x = pts[4].x = cr.left;
		pts[0].y = pts[1].y = pts[4].y = cr.top;
		pts[1].x = pts[2].x = cr.right-1;
		pts[2].y = pts[3].y = cr.bottom-1;
		o->oPolyline(pts, 5);
		Line.color = 0x00000000L;
		o->SetLine(&Line);
		o->oRectangle(cr.left+3, cr.top+3, cr.right-3, cr.bottom-3);
		}
	else {
		Line.color = 0x00000000L;
		o->SetLine(&Line);
		o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
		}
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
ColorButton::Select(int x, int y, anyOutput *o)
{
	if(!parent) return false;
	if(IsInRect(&cr, x, y)) {
		bChecked = true;
		if((flags & OWNDIALOG)) {
			parent->Command(CMD_CONTINUE, 0L, o);
			col = GetNewColor(col);
			}
		if(flags & ISRADIO) parent->Command(CMD_RADIOBUTT, (void *)this, o);
		if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, o);
		DoPlot(o);
		return true;
		}
	return false;
}

FillButton::FillButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, FillDEF *fill)
	:Dialog(par, desc, rec)
{
	CurrFill = fill;
}

void
FillButton::DoPlot(anyOutput *o)
{
	Line.color = 0x00000000L;
	o->SetLine(&Line);
	o->SetFill(CurrFill);
	o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
FillButton::Select(int x, int y, anyOutput *o)
{
	if(IsInRect(&cr, x, y)) {
		if((flags & OWNDIALOG)) {
			if(parent)parent->Command(CMD_CONTINUE, 0L, o);
			GetNewFill(CurrFill);
			}
		if((flags & TOUCHEXIT) && parent) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		DoPlot(o);
		return true;
		}
	return false;
}

Shade3D::Shade3D(tag_DlgObj *par, DlgInfo * desc, RECT rec, FillDEF *fill)
	:Dialog(par, desc, rec)
{
	CurrFill = fill;
}

void
Shade3D::DoPlot(anyOutput *o)
{
	POINT pts[5];
	FillDEF fd;
	int dx;

	Line.color = 0x00000000L;
	o->SetLine(&Line);
	if(CurrFill->type & FILL_LIGHT3D) {
		fd.color = fd.color2 = (CurrFill->color & 0x00ffffff);
		fd.hatch = 0L;		fd.scale = 1.0;
		fd.type = 0L;
		dx = iround(((double)(cr.bottom - cr.top))*.26);
		pts[0].x = pts[1].x = pts[4].x = ((cr.left + cr.right)>>1);
		pts[0].y = pts[4].y = cr.bottom;
		pts[1].y = ((cr.top + cr.bottom)>>1);
		pts[2].x = pts[3].x = pts[0].x + (dx<<1);
		pts[2].y = pts[1].y - dx;		pts[3].y = pts[0].y - dx;
		o->SetFill(&fd);
		o->oPolygon(pts, 5, 0L);
		fd.color = fd.color2 = (IpolCol(CurrFill->color, CurrFill->color2, 0.4) & 0x00ffffffL);
		pts[2].x = pts[3].x = pts[0].x - (dx<<1);
		o->SetFill(&fd);
		o->oPolygon(pts, 5, 0L);
		fd.color = fd.color2 = (CurrFill->color2 & 0x00ffffffL);
		pts[0].y = pts[4].y = pts[1].y;
		pts[1].x = pts[2].x;			pts[1].y = pts[3].y = pts[2].y;
		pts[2].x = pts[0].x;			pts[2].y -= dx;
		pts[3].x = pts[0].x + (dx<<1);
		o->SetFill(&fd);
		o->oPolygon(pts, 5, 0L);
		}
	else {
		o->SetFill(CurrFill);
		o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
		}
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

void ConfShade(FillDEF *oldfill);
bool
Shade3D::Select(int x, int y, anyOutput *o)
{
	if(IsInRect(&cr, x, y)) {
		if((flags & OWNDIALOG)) {
			if(parent)parent->Command(CMD_CONTINUE, 0L, o);
			ConfShade(CurrFill);
			}
		if((flags & TOUCHEXIT) && parent) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		DoPlot(o);
		return true;
		}
	return false;
}

LineButton::LineButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, LineDEF *line)
	:Dialog(par, desc, rec)
{
	Line.color = 0x00000000L;
	CurrLine = line;
	Fill.color = defs.Color(COL_BG);
	pts[0].x = cr.left+4;
	pts[1].x = cr.right-5;
	pts[0].y = pts[1].y = (cr.top + cr.bottom)/2;
}

void
LineButton::DoPlot(anyOutput *o)
{
	o->SetLine(&Line);
	o->SetFill(&Fill);
	o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
	o->SetLine(CurrLine);
	o->oPolyline(pts, 2);
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
LineButton::Select(int x, int y, anyOutput *o)
{
	if(IsInRect(&cr, x, y)) {
		if((flags & TOUCHEXIT) && parent) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		DoPlot(o);
		return true;
		}
	return false;
}

SymButton::SymButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, Symbol **sym)
	:Dialog(par, desc, rec)
{
	symbol = sym;
	Line.color = 0x00000000L;
	Fill.color = 0x00ffffffL;
}

void
SymButton::DoPlot(anyOutput *o)
{
	Line.color = 0x00000000L;
	o->SetLine(&Line);
	o->SetFill(&Fill);
	o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
	if(*(symbol)) {		//center symbol in the rectangle
		(*symbol)->SetSize(SIZE_XPOS, (cr.right+cr.left)/2.0);
		(*symbol)->SetSize(SIZE_YPOS, (cr.bottom+cr.top)/2.0);
		(*symbol)->DoPlot(o);
		}
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
SymButton::Select(int x, int y, anyOutput *o)
{
	if(parent && IsInRect(&cr, x, y)) {
		if((flags & OWNDIALOG) && (*(symbol))) {
			parent->Command(CMD_CONTINUE, 0L, o);
			(*symbol)->PropertyDlg();
			}
		if((flags & TOUCHEXIT)) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		DoPlot(o);
		return true;
		}
	return false;
}

FillRadioButt::FillRadioButt(tag_DlgObj *par, DlgInfo * desc, RECT rec, unsigned int pattern)
	:Dialog(par, desc, rec)
{
	Line.pattern = 0x00000000L;			Fill.type = pattern;
	Fill.color = 0x00ffffffL;			Fill.hatch = &Line;
	flags |= ISRADIO;
}

void
FillRadioButt::DoPlot(anyOutput *o)
{
	POINT pts[5];

	Line.color = bChecked ? 0x00000000L : DlgBGcolor;
	o->SetLine(&Line);
	pts[0].x = pts[3].x = pts[4].x = cr.left;
	pts[0].y = pts[1].y = pts[4].y = cr.top;
	pts[1].x = pts[2].x = cr.right-1;
	pts[2].y = pts[3].y = cr.bottom-1;
	o->oPolyline(pts, 5);
	Line.color = 0x00000000L;
	o->SetLine(&Line);
	o->SetFill(&Fill);
	o->oRectangle(cr.left+3, cr.top+3, cr.right-3, cr.bottom-3);
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
FillRadioButt::Select(int x, int y, anyOutput *o)
{
	if(IsInRect(&cr, x, y)) {
		bChecked = true;
		DoPlot(o);
		if(parent) parent->Command(CMD_RADIOBUTT, (void *)this, o);
		if((flags & TOUCHEXIT) && parent) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		return true;
		}
	return false;
}

SymRadioButt::SymRadioButt(tag_DlgObj *par, DlgInfo * desc, RECT rec, int* type)
	:Dialog(par, desc, rec)
{
	double size;

	if(type && (Sym = new Symbol(0L, 0L, ((double)(cr.right+cr.left))/2.0, 
		((double)(cr.bottom+cr.top))/2.0, *type))){
		size = (0.14*(double)(cr.bottom-cr.top))/Units[defs.cUnits].convert;
		Sym->SetSize(SIZE_SYMBOL, size);			Sym->SetSize(SIZE_SYM_LINE, size/10.0);
		Sym->SetColor(COL_SYM_LINE, 0x00000000L);	Sym->SetColor(COL_SYM_FILL, 0x00ffffffL);
		}
	Fill.color = DlgBGhigh;		flags |= ISRADIO;
}

SymRadioButt::~SymRadioButt()
{
	if (Sym) delete Sym;
}

void
SymRadioButt::DoPlot(anyOutput *o)
{
	if(!Sym) return;
	Line.color = bChecked ? 0x00000000L : (Fill.color & 0x00ffffff);
	o->SetFill(&Fill);
	o->SetLine(&Line);
	o->oRectangle(cr.left+1, cr.top+1, cr.right-1, cr.bottom-1);
	Sym->DoPlot(o);
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
SymRadioButt::Select(int x, int y, anyOutput *o)
{
	if(IsInRect(&cr, x, y)) {
		bChecked = true;
		DoPlot(o);
		if(parent) parent->Command(CMD_RADIOBUTT, (void *)this, o);
		if((flags & TOUCHEXIT) && parent) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		return true;
		}
	return false;
}

CheckBox::CheckBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
	:Dialog(par, desc, rec)
{
	if(text && text[0])Text = (char*)memdup(text, (int)strlen(text)+1, 0);
	else Text = 0L;
	hcr.left = cr.left+2;		hcr.right = cr.left+14;
	hcr.top = cr.top+3;			hcr.bottom = cr.top+15;
}

CheckBox::~CheckBox()
{
	if(Text) free (Text);
}

bool
CheckBox::Command(int cmd, void *tmpl, anyOutput *o)
{
	switch(cmd) {
	case CMD_SETTEXT:
		if(Text) free(Text);
		if(tmpl && *((char*)tmpl)) Text = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1,0);
		else Text = 0L;
		return true;
		}
	return false;
}

void
CheckBox::DoPlot(anyOutput *o)
{
	POINT pts[3];

	if(flags & HIDDEN) return;
	Line.color = 0x00000000L;
	Line.width = 0.0;
	Fill.color = bLBdown ? DlgBGcolor : 0x00ffffffL;
	o->SetLine(&Line);
	o->SetFill(&Fill);
	o->oRectangle(cr.left+2, cr.top+3, cr.left+14, cr.top+15);
	if(bChecked) {
		Line.width = defs.GetSize(SIZE_SYM_LINE)*2.0;
		o->SetLine(&Line);
		pts[0].x = cr.left+4;			pts[0].y = cr.top+5;
		pts[1].x = cr.left+11;			pts[1].y = cr.top+12;
		o->oSolidLine(pts);
		pts[0].x = cr.left+11;			pts[1].x = cr.left+4;
		o->oSolidLine(pts);
		}
	if(Text) {
		o->SetTextSpec(&TextDef);
		o->oTextOut(cr.left + 20, cr.top + 1, Text, (int)strlen(Text));
		}
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
CheckBox::Select(int x, int y, anyOutput *o)
{
	if(!(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
		bChecked ^= 0x01;	//toggle selection
		bLBdown = false;
		DoPlot(o);
		if((flags & ISRADIO) && parent)
			parent->Command(CMD_RADIOBUTT, (void *)this, o);
		if((flags & TOUCHEXIT) && parent) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		return true;
		}
	return false;
}

CheckPin::CheckPin(tag_DlgObj *par, DlgInfo * desc, RECT rec)
	:Dialog(par, desc, rec)
{
	hcr.left = cr.left+2;		hcr.right = cr.right-2;
	hcr.top = cr.top+2;			hcr.bottom = cr.bottom-2;
}

CheckPin::~CheckPin()
{
}

bool
CheckPin::Command(int cmd, void *tmpl, anyOutput *o)
{
	switch(cmd) {
	case CMD_UNLOCK:
		bChecked = false;
		return true;
		}
	return false;
}

void
CheckPin::DoPlot(anyOutput *o)
{
	POINT pts[20];

	if(!o || flags & HIDDEN) return;
	Line.width = 0.0;
	Line.color = DlgBGcolor;		Fill.color = DlgBGcolor;
	o->SetLine(&Line);		o->SetFill(&Fill);
	o->oRectangle(hcr.left+2, hcr.top, hcr.right, hcr.bottom);
	Line.color = 0x00000000L;		Fill.color = bLBdown ? 0x00ffffff : 0x00e8e8e8L;
	o->SetLine(&Line);		o->SetFill(&Fill);
	if(bChecked) {
		o->oCircle(hcr.left + 5, hcr.top + 1, hcr.left + 15, hcr.top + 12);
		o->oCircle(hcr.left + 8, hcr.top + 3, hcr.left + 15, hcr.top + 10);
		}
	else {
		pts[0].x = hcr.left + 8;	pts[0].y = hcr.top + 4;
		pts[1].x = pts[0].x - 5;	pts[1].y = pts[0].y + 1;
		pts[2].x = pts[0].x;		pts[2].y = pts[1].y + 1;
		pts[3].x = pts[0].x;		pts[3].y = pts[0].y;
		o->oPolygon(pts, 4);
		pts[0].x = hcr.left + 8;	pts[0].y = hcr.top + 1;
		pts[1].x = pts[0].x;		pts[1].y = pts[0].y + 9;
		pts[2].x = pts[1].x + 3;	pts[2].y = pts[1].y - 3;
		pts[3].x = pts[2].x + 4;	pts[3].y = pts[2].y;
		pts[4].x = pts[3].x;		pts[4].y = pts[3].y - 3;
		pts[5].x = pts[4].x - 4;	pts[5].y = pts[4].y;
		pts[6].x = pts[0].x;		pts[6].y = pts[0].y;
		o->oPolygon(pts, 7);
		o->oCircle(pts[4].x-2, pts[0].y, pts[4].x+3, pts[1].y+1);
		}
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
CheckPin::Select(int x, int y, anyOutput *o)
{
	if(!(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
		bChecked ^= 0x01;	//toggle selection
		bLBdown = false;	DoPlot(o);
		if((flags & TOUCHEXIT) && parent) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		bLBdown=false;				DoPlot(o);
		return true;
		}
	return false;
}

Trash::Trash(tag_DlgObj *par, DlgInfo * desc, RECT rec)
	:Dialog(par, desc, rec)
{
	hcr.left = cr.left+2;		hcr.right = cr.right-2;
	hcr.top = cr.top+2;			hcr.bottom = cr.bottom-2;
}

Trash::~Trash()
{
}

bool
Trash::Command(int cmd, void *tmpl, anyOutput *o)
{
	return false;
}

void
Trash::DoPlot(anyOutput *o)
{
	POINT pts[2];

	if(!o || flags & HIDDEN) return;
	Line.width = 0.0;
	Line.color = DlgBGcolor;		Fill.color = DlgBGcolor;
	o->SetLine(&Line);		o->SetFill(&Fill);
	o->oRectangle(hcr.left+2, hcr.top, hcr.right, hcr.bottom);
	Line.color = 0x00000000L;		Fill.color = bLBdown ? 0x00ffffff : 0x00e8e8e8L;
	o->SetLine(&Line);		o->SetFill(&Fill);
	o->oRectangle(cr.left+8, cr.top+10, cr.right-8, cr.bottom-3);
	o->oRectangle(cr.left+6, cr.top+5, cr.right-6, cr.top+10);
	o->oRectangle(cr.left+11, cr.top+2, cr.right-11, cr.top+5);
	pts[0].y = cr.top + 12;						pts[1].y = cr.bottom - 5;
	pts[0].x = pts[1].x = cr.left + 12;			o->oSolidLine(pts);
	pts[0].x = pts[1].x = cr.right - 13;		o->oSolidLine(pts);
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
Trash::Select(int x, int y, anyOutput *o)
{
	if(!(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
		if((flags & TOUCHEXIT) && parent) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		bLBdown=false;				DoPlot(o);
		return true;
		}
	return false;
}

Config::Config(tag_DlgObj *par, DlgInfo * desc, RECT rec)
	:Dialog(par, desc, rec)
{
	hcr.left = cr.left+2;		hcr.right = cr.right-2;
	hcr.top = cr.top+2;			hcr.bottom = cr.bottom-2;
}

Config::~Config()
{
}

bool
Config::Command(int cmd, void *tmpl, anyOutput *o)
{
	return false;
}

void
Config::DoPlot(anyOutput *o)
{
	POINT pts[2];

	if(!o || flags & HIDDEN) return;
	Line.width = 0.0;
	Line.color = DlgBGcolor;		Fill.color = DlgBGcolor;
	o->SetLine(&Line);		o->SetFill(&Fill);
	o->oRectangle(hcr.left+2, hcr.top, hcr.right, hcr.bottom);
	Line.color = 0x00000000L;		Fill.color = bLBdown ? 0x00ffffff : 0x00e8e8e8L;
	o->SetLine(&Line);		o->SetFill(&Fill);
	o->oRectangle(cr.left+3, cr.top+3, cr.right-3, cr.bottom-3);
	o->oRectangle(cr.left+8, cr.top+6, cr.left+11, cr.top+9);
	o->oRectangle(cr.left+8, cr.top+10, cr.left+11, cr.top+13);
	o->oRectangle(cr.left+8, cr.top+14, cr.left+11, cr.top+17);
	o->oRectangle(cr.left+7, cr.top+18, cr.right-7, cr.bottom-5);
	pts[0].x = cr.left + 14;					pts[1].x = cr.right - 8;
	pts[0].y = pts[1].y = cr.top + 7;			o->oSolidLine(pts);
	pts[0].y = pts[1].y = cr.top + 11;			o->oSolidLine(pts);
	pts[0].y = pts[1].y = cr.top + 15;			o->oSolidLine(pts);
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
Config::Select(int x, int y, anyOutput *o)
{
	if(!(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
		if((flags & TOUCHEXIT) && parent) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		bLBdown=false;				DoPlot(o);
		return true;
		}
	return false;
}

RadioButton::RadioButton(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
	:Dialog(par, desc, rec)
{
	if(text && text[0])Text = (char*)memdup(text, (int)strlen(text)+1, 0);
	else Text = 0L;
	switch(type) {
	case RADIO0:
		hcr.left = cr.left+4;		hcr.right = cr.left+13;
		hcr.top = cr.top+4;			hcr.bottom = cr.top+13;
		break;
	case RADIO1:
		hcr.left = cr.left+2;		hcr.right = cr.left+15;
		hcr.top = cr.top+3;			hcr.bottom = cr.top+16;
		break;
	case RADIO2:
		hcr.left = cr.left;			hcr.right = cr.left+15;
		hcr.top = cr.top+1;			hcr.bottom = cr.top+16;
		break;
		}
	flags |= ISRADIO;
}

RadioButton::~RadioButton()
{
	if(Text) free (Text);
}

bool
RadioButton::Command(int cmd, void *tmpl, anyOutput *o)
{
	switch(cmd) {
	case CMD_SETTEXT:
		if(Text) free(Text);
		if(tmpl && *((char*)tmpl))Text = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0);
		else Text = 0L;
		return true;
		}
	return false;
}

void
RadioButton::DoPlot(anyOutput *o)
{
	if(flags & HIDDEN) return;
	Line.color = 0x00000000L;
	Line.width = 0.0;
	Fill.color = bLBdown ? DlgBGcolor : 0x00ffffffL;
	if(bActive) {
		o->SetLine(&Line);
		o->SetFill(&Fill);
		o->oCircle(hcr.left, hcr.top, hcr.right, hcr.bottom);
		if(bChecked) {
			Fill.color = 0x00000000L;		o->SetFill(&Fill);
			switch (type) {
			case RADIO0:
				o->oCircle(cr.left+6, cr.top+6, cr.left+11, cr.top+11);
				break;
			case RADIO1:
				o->oCircle(cr.left+5, cr.top+6, cr.left+12, cr.top+13);
				break;
			case RADIO2:
				o->oCircle(cr.left+3, cr.top+4, cr.left+12, cr.top+13);
				break;
				}	
			}
		}
	if(Text) {
		o->SetTextSpec(&TextDef);
		o->oTextOut(cr.left + 20, cr.top + 1, Text, (int)strlen(Text));
		}
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
RadioButton::Select(int x, int y, anyOutput *o)
{
	if(bActive && !(flags & HIDDEN) && IsInRect(&hcr, x, y)) {
		bChecked = true;		bLBdown = false;
		DoPlot(o);
		if(parent) parent->Command(CMD_RADIOBUTT, (void *)this, o);
		if((flags & TOUCHEXIT) && parent) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		return true;
		}
	return false;
}

void
RadioButton::SetColor(int id, DWORD color)
{
	TextDef.ColTxt = (color & 0x00ffffffL);
}

Text::Text(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
	:Dialog(par, desc, rec)
{
	if(text && text[0])txt = (char*)memdup(text, (int)strlen(text)+1, 0);
	else txt = 0L;
	switch (type) {
	case RTEXT:		TextDef.Align = TXA_HRIGHT | TXA_VTOP;	break;
	case CTEXT:		TextDef.Align = TXA_HCENTER | TXA_VTOP;	break;
	default:		TextDef.Align = TXA_HLEFT | TXA_VTOP;	break;
		}
}

Text::~Text()
{
	if(txt) free(txt);
}

bool
Text::Command(int cmd, void *tmpl, anyOutput *o)
{
	switch(cmd) {
	case CMD_SETTEXT:
		if(txt) free(txt);
		if(tmpl && *((char*)tmpl))txt = (char*)memdup(tmpl, (int)strlen((char*)tmpl)+1, 0);
		else txt = 0L;
		return true;
		}
	return false;
}

void
Text::DoPlot(anyOutput *o)
{
	if(WWWbrowser && WWWbrowser[0] && (flags & HREF)) {
		TextDef.ColTxt = 0x00ff0000L;
		TextDef.Style |= TXS_UNDERLINE;
		}
	if(txt){
		o->SetTextSpec(&TextDef);
		switch(type) {
		case RTEXT: o->oTextOut(cr.right - 2, cr.top + 1, txt, 0);				break;
		case CTEXT: o->oTextOut((cr.left + cr.right)/2, cr.top + 1, txt, 0);	break;
		default:	o->oTextOut(cr.left + 2, cr.top + 1, txt, 0);				break;
			}
		defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
		}
}

bool
Text::Select(int x, int y, anyOutput *o)
{
	int i;

	if(WWWbrowser && WWWbrowser[0] && txt && (flags & HREF) && IsInRect(&cr, x, y)) {
		o->MouseCursor(MC_WAIT, false);
		i = rlp_strcpy(TmpTxt, TMP_TXT_SIZE-50, WWWbrowser);
		TmpTxt[i++] = ' ';
		rlp_strcpy(TmpTxt+i, TMP_TXT_SIZE-i, txt);
		if(parent && (flags & TOUCHEXIT))parent->Command(CMD_ENDDIALOG, 0L, o);
		else if(parent)parent->Command(CMD_CONTINUE, 0L, o);
#ifdef _WINDOWS
		WinExec(TmpTxt, SW_SHOWMAXIMIZED); 
#else
		strcat(TmpTxt, " &");
		system(TmpTxt);
#endif
		o->MouseCursor(MC_ARROW, false);
		return true;
		}
	return false;
}

void
Text::SetColor(int id, DWORD color)
{
	TextDef.ColTxt = (color & 0x00ffffffL);
}

void
Text::MBtrack(MouseEvent *mev, anyOutput *o)
{
	if((mev->StateFlags &1) && IsInRect(&hcr, mev->x, mev->y) && parent) parent->Command(CMD_MARKOBJ, this, o);
}

InputText::InputText(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text)
	:Dialog(par, desc, rec)
{
	RECT rc;

	rc.left = rec.left+1;				rc.top = rec.top+1;
	rc.right = rec.right-1;				rc.bottom = rec.bottom-1;
	if(type == INCDECVAL1) cr.right = rc.right = cr.right - 7*xbase;
	Line.color = 0x00000000L;
	if(Text = new EditText(0L, text, -1, -1)) Text->SetRec(&rc);
	Disp = 0L;
}

InputText::~InputText()
{
	if(Text) delete (Text);		Text = 0L;
}

bool
InputText::Command(int cmd, void *tmpl, anyOutput *o)
{
	bool bRet;

	switch(cmd) {
	case CMD_CURRLEFT:	case CMD_CURRIGHT:	case CMD_DELETE:
	case CMD_POS_FIRST:	case CMD_POS_LAST:	case CMD_SHIFTLEFT:
	case CMD_SHIFTRIGHT:
		if(Text && bActive && !(flags & NOEDIT)){
			if(o) Undo.SetDisp(o);
			o->SetTextSpec(&TextDef);
			bRet = Text->Command(cmd, o, NULL);
			if(bRet && (flags & TOUCHEXIT)) parent->Command(CMD_ENDDIALOG, (void *)this, o);
			return bRet;
			}
		break;
	case CMD_SETFONT:	case CMD_SETSTYLE:
		if(Text) return Text->Command(cmd, o, tmpl);
		return false;
	case CMD_UPDATE:
		if(Text && bActive && o) Text->Command(cmd, o, 0L);
		break;
	case CMD_COPY:		case CMD_PASTE:
		if(Text && bActive && !(flags & NOEDIT)) return  Text->Command(cmd, o, NULL);
		return false;
	case CMD_ADDCHAR:
		if(Text && bActive && !(flags & NOEDIT)){
			if(o) Undo.SetDisp(o);
			o->SetTextSpec(&TextDef);
			switch(*((int*)tmpl)) {
			case 8: return Text->Command(CMD_BACKSP, o, NULL);	//Backspace
			default: return Text->AddChar(*((int*)tmpl), o, 0L);
				}
			if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, o);
			}
		return false;
	case CMD_SETTEXT:
		if(Text) return Text->SetText((char*)tmpl);
		break;
		}
	return false;
}

void
InputText::DoPlot(anyOutput *o)
{
	POINT pts[5];

	if(!o) return;
	Disp = o;
	pts[0].x = pts[0].y = 0;		//that means no caret with update(5)
	o->SetTextSpec(&TextDef);
	if(Text) Text->Update(bActive ? 5 : 2, o, &pts[0]);
	o->SetLine(&Line);
	pts[0].x = pts[1].x = pts[4].x = cr.left;
	pts[0].y = pts[3].y = pts[4].y = cr.top;
	pts[1].y = pts[2].y = cr.bottom-1;
	pts[2].x = pts[3].x = cr.right-1;
	o->oPolyline(pts, 5);
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
	if(parent && bActive){
		parent->Command(CMD_TABDLG, this, o);
		if(type == RANGEINPUT)o->MouseCapture(false);
		}
}

bool
InputText::Select(int x, int y, anyOutput *o)
{
	POINT p1;

	p1.x = x;		p1.y = y;
	if(bActive && IsInRect(&cr, x, y) && !(flags & NOEDIT)) {
		DialogFocus = DialogTabStop = this;
		if(parent) parent->Command(CMD_FOCTXT, (void*)this, 0L);
		o->SetTextSpec(&TextDef);
		if(Text) Text->Update(1, o, &p1); 
		if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, o);
		return true;
		}
	return false;
}

bool
InputText::GetValue(int id, double *val)
{
	if(Text && val) {
		Text->Update(20, NULL, 0L);			//convert string to value
		return Text->GetValue(val);
		}
	return false;
}

bool
InputText::GetInt(int id, int *val)
{
	if(type == EDTEXT) {
		if(Text) *val = Text->Cursor();
		else *val = 0;
		return true;
		}
	if(Text && Text->text) {
#ifdef USE_WIN_SECURE
		sscanf_s(Text->text, "%d", val);
#else
		sscanf(Text->text, "%d", val);
#endif
		return true;
		}
	return false;
}

bool
InputText::GetText(int id, char *txt, int size)
{
	if(Text && Text->text && Text->text[0]) {
		rlp_strcpy(txt, size, Text->text);
		return true;
		}
	return false;
}

void
InputText::MBtrack(MouseEvent *mev, anyOutput *o)
{
	bool bLBstate;
	int x, y;

	x = mev->x;						y = mev->y;
	if(mev->StateFlags &1) bLBstate = true;
	else bLBstate = false;
	if(bActive && IsInRect(&cr, x, y) && !(flags & NOEDIT) && 
		bLBstate && Text && parent) {
		DialogFocus = this;
		parent->Command(CMD_FOCTXT, (void*)this, 0L);
		o->SetTextSpec(&TextDef);
		Text->Command(CMD_MOUSE_EVENT, o, (DataObj *)mev);
		parent->Command(CMD_MARKOBJ, this, o);
		}
}

void
InputText::Activate(int id, bool activate)
{
	bActive = activate;
	if(Text && Disp) {
		Disp->SetTextSpec(&TextDef);
		if(bActive){
			Text->Update(1, Disp, 0L);
			DialogFocus = DialogTabStop = this;
			if(parent) parent->Command(CMD_FOCTXT, this, Disp);
			}
		else Text->Update(2, Disp, 0L);
		}
}

RangeInput::RangeInput(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *text, DataObj *d)
	:InputText(par, desc, rec, text)
{
	data = d;
}

RangeInput::~RangeInput()
{
	if(data) data->Command(CMD_ETRACC, 0L, 0L);
	if(Text) delete (Text);		Text = 0L;
}

bool
RangeInput::Command(int cmd, void *tmpl, anyOutput *o)
{
	switch(cmd) {
	case CMD_SET_DATAOBJ:
		data = (DataObj*)tmpl;
		return true;
		}
	return InputText::Command(cmd, tmpl, o);
}

bool
RangeInput::Select(int x, int y, anyOutput *o)
{
	bool bRes;

	bRes = InputText::Select(x, y, o);
	if(bRes && data) {
		if(DialogFocus == this){
			data->Command(CMD_ETRACC, Text, 0L);
			if(Text) Text->Update(1, o, 0L); 
			}
		else data->Command(CMD_ETRACC, 0L, 0L);
		}
	return bRes;
}

void
RangeInput::Activate(int id, bool activate)
{
	InputText::Activate(id, activate);
	if(activate && data) data->Command(CMD_ETRACC, Text, 0L);
}

InputValue::InputValue(tag_DlgObj *par, DlgInfo * desc, RECT rec, double *value)
	:InputText(par, desc, rec, 0L)
{
	if(value) {
		WriteNatFloatToBuff(TmpTxt, *value);
		if(Text) Text->text = (char*)memdup(TmpTxt+1, (int)strlen(TmpTxt+1)+1, 0);
		}
	else if(Text) Text->SetText("");
}

InputValue::~InputValue()
{
	if(Text) delete (Text);
	Text = 0L;					//in fact the destructor of InputText is also
								//  called. This prevents a double delete of Text.
}

IncDecValue::IncDecValue(tag_DlgObj *par, DlgInfo * desc, RECT rec, double *value)
	:InputText(par, desc, rec, 0L)
{
	int ab1 = 1, ab2 = 2;
	static DlgInfo ab[] = {
		{1, 2, 0, 0x0L, ARROWBUTT, (void*)&ab1, 0, 0, 0, 0},
		{2, 0, 0, LASTOBJ, ARROWBUTT, (void*)&ab2, 0, 0, 0, 0}};
	RECT br;

	WriteNatFloatToBuff(TmpTxt, *value);
	if(Text) Text->text = (char*)memdup(TmpTxt+1, (int)strlen(TmpTxt+1)+1, 0);
	br.left = cr.right+1;		br.right = br.left + 7*xbase;
	br.top = cr.top+1;			br.bottom = br.top + 5*ybase;
	butts[0] = new ArrowButton(this, &ab[0], br, &ab1);
	br.top += 5*ybase;			br.bottom += 5*ybase;
	butts[1] = new ArrowButton(this, &ab[1], br, &ab2);
	hasMinMax = hasStep = false;
	theMin = theMax = theStep = 0.0;	
}

IncDecValue::~IncDecValue()
{
	if(Text) delete (Text);
	if(butts[0]) delete (butts[0]);
	if(butts[1]) delete (butts[1]);
	Text = 0L;
}

bool
IncDecValue::Command(int cmd, void *tmpl, anyOutput *o)
{
	double tmpVal;

	switch(cmd) {
	case CMD_MINMAX:
		if(tmpl) {
			hasMinMax = true;
			theMin = ((double*)tmpl)[0];
			theMax = ((double*)tmpl)[1];
			}
		else hasMinMax = false;
		return true;
	case CMD_STEP:
		if(tmpl) {
			hasStep = true;
			theStep = *((double*)tmpl);
			}
		else hasStep = false;
		return true;
	case CMD_ENDDIALOG:
		if(GetValue(Id, &tmpVal)) {
			if(hasStep && theStep > 0.0) {
				tmpVal = floor(tmpVal/theStep)*theStep;
				if(((Dialog*)tmpl)->Id == 1) tmpVal += theStep;
				else tmpVal -= theStep;
				}
			else {
				if(((Dialog*)tmpl)->Id == 1) tmpVal *= 1.2;
				else tmpVal /= 1.2;
				tmpVal = NiceValue(tmpVal);
				}
			if(hasMinMax) {
				if(tmpVal < theMin) tmpVal = theMin;
				if(tmpVal > theMax) tmpVal = theMax;
				}
			else if(!hasStep && fabs(tmpVal) < 0.0001) switch(defs.cUnits) {
				case 1:		tmpVal = 0.01;	break;
				case 2:		tmpVal = 0.004;	break;
				default:	tmpVal = 0.1;	break;
				}
			WriteNatFloatToBuff(TmpTxt, tmpVal);
			if(Text) {
				Text->SetText(TmpTxt+1);				DoPlot(o);
				}
			}
		return true;
		}
	return InputText::Command(cmd, tmpl, o);
}

void
IncDecValue::DoPlot(anyOutput *o)
{
	InputText::DoPlot(o);
	if(butts[0]) butts[0]->DoPlot(o);
	if(butts[1]) butts[1]->DoPlot(o);
}

bool
IncDecValue::Select(int x, int y, anyOutput *o)
{
	bool bRet = false;

	if(x > cr.right) {
		if(butts[0]) bRet = butts[0]->Select(x, y, o);
		if(!bRet && butts[1]) bRet = butts[1]->Select(x, y, o);
		}
	else return InputText::Select(x, y, o);
	if(bRet && parent) {
		DialogFocus = DialogTabStop = 0L;
		if(parent) parent->Command(CMD_FOCTXT, (void*)0L, 0L);
		if(flags & TOUCHEXIT) parent->Command(CMD_ENDDIALOG, (void *)this, o);
		}
	return bRet;
}

bool
IncDecValue::GetValue(int id, double *val)
{
	if(Text && val) {
		Text->Update(20, NULL, 0L);			//convert string to value
		if(Text->GetValue(val)) {
			if(hasMinMax) {
				if(*val < theMin) *val = theMin;
				if(*val > theMax) *val = theMax;
				}
			return true;
			}
		}
	return false;
}

void
IncDecValue::MBtrack(MouseEvent *mev, anyOutput *o)
{
	if(mev->x >cr.right) {
		if(butts[0]) butts[0]->MBtrack(mev, o);
		if(butts[1]) butts[1]->MBtrack(mev, o);
		}
	else InputText::MBtrack(mev, o);
}

TxtHSP::TxtHSP(tag_DlgObj *par, DlgInfo *desc, RECT rec, int *align)
	:Dialog(par, desc, rec)
{
	int x1 = cr.left/xbase,			x2 = ((cr.left>>1) + (cr.right>>1))/xbase-4,
		x3 = cr.right/xbase-8,		y1 = cr.top/ybase,
		y2 = ((cr.top>>1) + (cr.bottom>>1))/ybase-4,	y3 = cr.bottom/ybase-8;
	int i;
	RECT br;
	DlgInfo d1[9];

	for(i = 0; i < 9; i++) {
		d1[i].id = i+1;			d1[i].next = i+2;			d1[i].first = 0;
		d1[i].flags = ISRADIO;	d1[i].type = RADIO0;		d1[i].ptype = 0L;
		d1[i].w = d1[i].h = 8;
		switch (i / 3) {
			case 0:		d1[i].y = y1;	break;
			case 1:		d1[i].y = y2;	break;
			case 2:		d1[i].y = y3;	break;
			}
		switch (i % 3) {
			case 0:		d1[i].x = x1;	break;
			case 1:		d1[i].x = x2;	break;
			case 2:		d1[i].x = x3;	break;
			}
		}
	d1[8].next = 0;			d1[8].flags |= LASTOBJ;
	txt.ColTxt = 0x00808080L;	txt.ColBg = 0x00ffffff;
	txt.fSize = defs.GetSize(SIZE_TEXT)*2.0;		
	txt.RotBL = txt.RotCHAR = 0.0;
	txt.iSize = 0;			txt.Align = TXA_HCENTER | TXA_VCENTER;
	txt.Style = TXS_NORMAL;	txt.Font = FONT_HELVETICA;
	txt.Mode = TXM_TRANSPARENT;
	if(txt.text = (char*)malloc(20*sizeof(char))) rlp_strcpy(txt.text, 20, "text");
	if((d2 = (DlgInfo*)malloc(9*sizeof(DlgInfo)))){
		memcpy(d2, &d1,9*sizeof(DlgInfo));
		if(align) switch(*align) {
		case 1:		d2[1].flags |= CHECKED;		break;
		case 2:		d2[2].flags |= CHECKED;		break;
		case 4:		d2[3].flags |= CHECKED;		break;
		case 5:		d2[4].flags |= CHECKED;		break;
		case 6:		d2[5].flags |= CHECKED;		break;
		case 8:		d2[6].flags |= CHECKED;		break;
		case 9:		d2[7].flags |= CHECKED;		break;
		case 10:	d2[8].flags |= CHECKED;		break;
		default:	d2[0].flags |= CHECKED;		break;
			}
		for(i = 0; i < 9; i++) {
			br.left = d2[i].x*xbase;
			br.right = br.left + d2[i].w*xbase;
			br.top = d2[i].y*ybase;
			br.bottom = br.top + d2[i].h*ybase;
			butts[i] = new RadioButton(this, &d2[i], br, 0L);
			}
		}
}

TxtHSP::~TxtHSP()
{
	int i;

	if(txt.text) free(txt.text);
	if(d2){
		for(i = 0; i < 9; i++) if(butts[i]) delete(butts[i]);
		free(d2);
		}
}

bool
TxtHSP::Command(int cmd, void *tmpl, anyOutput *o)
{
	int i;
	Dialog *d;

	switch(cmd) {
	case CMD_RADIOBUTT:
		d = (Dialog *)tmpl;
		for(i = 0; i < 9; i++) 
			if(butts[i] && butts[i] != d && butts[i]->type == d->type)
				butts[i]->SetCheck(0, o, false);
		break;
		}
	return false;
}

void
TxtHSP::DoPlot(anyOutput *o)
{
	int i;

	if(txt.text){
		o->SetTextSpec(&txt);
		o->oTextOut((cr.left + cr.right)>>1, (cr.top + cr.bottom)>>1, txt.text, 0);
		}
	for(i = 0; i < 9; i++) if(butts[i]) butts[i]->DoPlot(o);
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
TxtHSP::Select(int x, int y, anyOutput *o)
{
	int i;
	bool bRet = false;

	for(i = 0; i < 9; i++) if(butts[i] && butts[i]->Select(x, y, o)) bRet = true;
	return bRet;
}

bool
TxtHSP::GetInt(int id, int *val)
{
	int i;
	bool bRet = false;

	for(i = 0; i < 9; i++) {
		if(butts[i] && butts[i]->GetCheck(i+1)) {
			bRet = true;
			switch (i) {
			case 0:		*val = 0;	break;
			case 1:		*val = 1;	break;
			case 2:		*val = 2;	break;
			case 3:		*val = 4;	break;
			case 4:		*val = 5;	break;
			case 5:		*val = 6;	break;
			case 6:		*val = 8;	break;
			case 7:		*val = 9;	break;
			case 8:		*val = 10;	break;
				}
			}
		}
	return bRet;
}

void
TxtHSP::MBtrack(MouseEvent *mev, anyOutput *o)
{
	int i;

	for(i = 0; i < 9; i++) if(butts[i]) butts[i]->MBtrack(mev, o);
}

SlideRect::SlideRect(tag_DlgObj *par, DlgInfo *desc, RECT rec, bool isVert)
	:Dialog(par, desc, rec)
{
	bV = isVert;
	if(isVert) {
		buttrc.left = cr.left+1;	buttrc.top = cr.top;
		buttrc.right = cr.right;	buttrc.bottom = cr.top + (w = h = (cr.right-cr.left));
		}
	else {
		buttrc.left = cr.left;		buttrc.top = cr.top+1;
		buttrc.right = cr.left+ (w = h = (cr.bottom - cr.top));	buttrc.bottom = cr.bottom;
		}
	dx = w>>1;		dy = h>>1;
	sLine = 1;
	puSel = pdSel = false;

}

SlideRect::~SlideRect()
{
}

bool
SlideRect::Command(int cmd, void *tmpl, anyOutput *o)
{
	int i;
	double tmp;

	switch(cmd) {
	case CMD_SETSCROLL:
		//DEBUG: replace width/height calulation by variables w and h
		tmp = *((double*)tmpl);
		if(tmp < 0.0) tmp = 0.0;	if(tmp > 1.0) tmp = 1.0;
		if(bV) {
			i = (int)((double)(cr.bottom -cr.top -7*ybase)*tmp);
			buttrc.bottom -= buttrc.top;
			buttrc.top = i + cr.top;		buttrc.bottom += (i+cr.top);
			}
		else {
			i = (int)((double)(cr.right -cr.left -7*xbase)*tmp);
			buttrc.right -= buttrc.left;
			buttrc.left = i + cr.left;		buttrc.right += (i+cr.left);
			}
		if(o)DoPlot(o);
		return true;
	case CMD_LINEUP:
		i = -sLine;
	case CMD_LINEDOWN:
		if(cmd == CMD_LINEDOWN) i = sLine;
		if(bV) {
			buttrc.top += i;
			if(buttrc.top < cr.top) buttrc.top = cr.top;
			if((buttrc.top + h) > cr.bottom) buttrc.top = cr.bottom -h;
			buttrc.bottom = buttrc.top + h;
			}
		else {
			buttrc.left += i;
			if(buttrc.left < cr.left) buttrc.left = cr.left;
			if((buttrc.left + w) > cr.right) buttrc.left = cr.right -w;
			buttrc.right = buttrc.left +w;
			}
		return true;
		}
	return false;
}


void
SlideRect::DoPlot(anyOutput *o)
{
	POINT pts[3];

	Line.color = DlgBGcolor;	Fill.color = 0x00e0e0e0L;
	o->SetLine(&Line);			o->SetFill(&Fill);
	o->oRectangle(cr.left, cr.top, cr.right, cr.bottom);
	puRC.top = cr.top;		puRC.left = cr.left;
	pdRC.right = cr.right;	pdRC.bottom = cr.bottom;
	if(bV) {
		puRC.bottom = buttrc.top;	puRC.right = cr.right;
		pdRC.top = buttrc.bottom;	pdRC.left = cr.left;
		}
	else {
		puRC.bottom = cr.bottom;	puRC.right = buttrc.left;
		pdRC.top = cr.top;			pdRC.left = buttrc.right;
		}
	Fill.color = DlgBGhigh;
	o->SetFill(&Fill);
	if(bLBdown){
		Line.color = 0x00808080L;		o->SetLine(&Line);
		o->oRectangle(buttrc.left, buttrc.top, buttrc.right, buttrc.bottom);
		}
	else {
		o->oRectangle(buttrc.left, buttrc.top, buttrc.right-1, buttrc.bottom-1);
		Line.color = 0x0L;
		o->SetLine(&Line);
		pts[0].x = buttrc.left;					pts[0].y = pts[1].y = buttrc.bottom-1;
		pts[1].x = pts[2].x = buttrc.right-1;	pts[2].y = buttrc.top-1;
		o->oPolyline(pts, 3);
		Line.color = 0x00ffffffL;
		o->SetLine(&Line);
		pts[0].x = pts[1].x = buttrc.left;
		pts[0].y = buttrc.bottom -3;
		pts[1].y = pts[2].y = buttrc.top;
		pts[2].x = buttrc.right -2;
		o->oPolyline(pts, 3);
		}
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
	puSel = pdSel = false;
}

bool
SlideRect::Select(int x, int y, anyOutput *o)
{
	if(IsInRect(&buttrc, x, y)) {
		bLBdown = false;
		DoPlot(o);
		return true;
		}
	else if(bLBdown || puSel || pdSel) {
		bLBdown = false;
		DoPlot(o);
		}
	if(IsInRect(&puRC, x, y) && parent) parent->Command(CMD_PAGEUP, 0L, o);
	else if(IsInRect(&pdRC, x, y) && parent) parent->Command(CMD_PAGEDOWN, 0L, o);
	return false;
}

bool
SlideRect::GetValue(int id, double *val)
{
	double res;

	if(bV) {
		res = ((double)(buttrc.top - cr.top))/
			((double)(cr.bottom-cr.top-(cr.right-cr.left)));
		}
	else {
		res = ((double)(buttrc.left - cr.left))/
			((double)(cr.right-cr.left-(cr.bottom-cr.top)));
		}
	*val = res;
	return true;
}

void
SlideRect::MBtrack(MouseEvent *mev, anyOutput *o)
{
	bool bLBstate = false;

	if(mev->Action == MOUSE_LBDOUBLECLICK) {
		bLBdown = false;
		DoPlot(o);
		return;
		}
	if(mev->StateFlags &1) bLBstate = true;
	if(IsInRect(&buttrc, mev->x, mev->y)){
		if(mev->Action == MOUSE_LBDOWN){
			dx = mev->x-buttrc.left;	dy = mev->y - buttrc.top;
			}
		if(bLBstate && !bLBdown){
			bLBdown = bLBstate;
			DoPlot(o);
			return;
			}
		}
	if(IsInRect(&puRC, mev->x, mev->y)){
		if(pdSel) DoPlot(o);
		Line.color = Fill.color = bLBstate ? DlgBGcolor : 0x00e0e0e0L;
		o->SetLine(&Line);			o->SetFill(&Fill);
		o->oRectangle(puRC.left+3, puRC.top+3, puRC.right-3, puRC.bottom-3);
		o->UpdateRect(&puRC, false);
		puSel = true;
		}
	else if(puSel) DoPlot(o);
	if(IsInRect(&pdRC, mev->x, mev->y)){
		if(puSel) DoPlot(o);
		Line.color = Fill.color = bLBstate ? DlgBGcolor : 0x00e0e0e0L;
		o->SetLine(&Line);			o->SetFill(&Fill);
		o->oRectangle(pdRC.left+3, pdRC.top+3, pdRC.right-3, pdRC.bottom-3);
		o->UpdateRect(&pdRC, false);
		pdSel = true;
		}
	else if(pdSel) DoPlot(o);
	if(bLBdown && IsInRect(&cr, mev->x, mev->y)){
		if(bV) {
			buttrc.top = mev->y - dy;
			if(buttrc.top < cr.top) buttrc.top = cr.top;
			if((buttrc.top + h) > cr.bottom) buttrc.top = cr.bottom -h;
			buttrc.bottom = buttrc.top + h;
			}
		else {
			buttrc.left = mev->x - dx;
			if(buttrc.left < cr.left) buttrc.left = cr.left;
			if((buttrc.left + w) > cr.right) buttrc.left = cr.right -w;
			buttrc.right = buttrc.left +w;
			}
		if(parent && ((Dialog*)parent)->parent && 
			((Dialog*)parent)->parent->Command(CMD_REDRAW, 0L, o));
		else DoPlot(o);
		}
	else if(bLBdown){
		bLBdown = false;
		DoPlot(o);
		}
}

ScrollBar::ScrollBar(tag_DlgObj *par, DlgInfo *desc, RECT rec, bool isVert)
	:Dialog(par, desc, rec)
{
	int ab1 = 1, ab2 = 2, ab3 = 3, ab4 = 4;
	static DlgInfo ab[] = {
		{1, 2, 0, 0x0L, ARROWBUTT, (void*)0L, 0, 0, 0, 0},
		{2, 0, 0, 0x0L, ARROWBUTT, (void*)0L, 0, 0, 0, 0},
		{3, 0, 0, LASTOBJ, NONE, 0L, 0, 0, 0, 0}};
	RECT br, sr;

	if(isVert) {
		sr.left = cr.left;		sr.right = cr.right;
		br.left = cr.left+1;	br.right = cr.right+1;
		br.top = cr.top+1;		br.bottom = sr.top = br.top + 5*ybase + 1;
		sr.top--;	hcr.top = sr.top;
		butts[0] = new ArrowButton(this, &ab[0], br, &ab1);
		br.top = cr.bottom-5*ybase;		br.bottom = cr.bottom+1;
		butts[1] = new ArrowButton(this, &ab[1], br, &ab2);
		br.bottom -= 5*ybase;	br.top -= 5*ybase;
		hcr.bottom = sr.bottom = br.top;
		butts[2] = new ArrowButton(this, &ab[0], br, &ab1);
		}
	else {
		sr.top = cr.top;		sr.bottom = cr.bottom;
		br.left = cr.left+1;	br.right = sr.left = br.left + 5*xbase;
		sr.left--;	hcr.left = sr.left;
		br.top = cr.top+1;		br.bottom = cr.bottom+1;
		butts[0] = new ArrowButton(this, &ab[0], br, &ab3);
		br.left = cr.right - 5*xbase;	br.right = cr.right+1;
		butts[1] = new ArrowButton(this, &ab[1], br, &ab4);
		br.left -= 5*xbase;		br.right -= 5*xbase;
		hcr.right = sr.right = br.left;
		butts[2] = new ArrowButton(this, &ab[0], br, &ab3);
		}
	slrc = new SlideRect(this, &ab[2], sr, isVert);
	sLine = 1;		sPage = 8;
}

ScrollBar::~ScrollBar()
{
	int i;

	for(i = 0; i < 3; i++) if(butts[i]) delete butts[i];
	if(slrc) delete slrc;
}

bool
ScrollBar::Command(int cmd, void *tmpl, anyOutput *o)
{
	int i;

	switch(cmd) {
	case CMD_ENDDIALOG:
		if(!tmpl || !slrc) return false;
		i = ((Dialog*)tmpl)->Id;
		switch(i) {
		case 1:	return slrc->Command(CMD_LINEUP, 0L, o);
		case 2:	return slrc->Command(CMD_LINEDOWN, 0L, o);
		default:	return false;
			}
	case CMD_PAGEUP:
	case CMD_PAGEDOWN:
		if(parent)return parent->Command(cmd, tmpl, o);
		break;
	case CMD_SETSCROLL:
		if(slrc) return slrc->Command(cmd, tmpl, o);
		break;
		}
	return false;
}

void
ScrollBar::DoPlot(anyOutput *o)
{
	int i;

	for(i = 0; i < 3; i++) if(butts[i]) butts[i]->DoPlot(o);
	if(slrc){
		slrc->sLine = sLine;
		slrc->DoPlot(o);
		}
}

bool
ScrollBar::Select(int x, int y, anyOutput *o)
{
	int i;
	bool bRet = false;

	if(!IsInRect(&cr, x, y)) return false;
	for(i = 0; i < 3; i++) if(butts[i] && butts[i]->Select(x, y, o)) bRet = true;
	if(!bRet && slrc) bRet = slrc->Select(x, y, o);
	if(bRet && parent) parent->Command(CMD_REDRAW, 0L, o);
	return bRet;
}

bool
ScrollBar::GetValue(int id, double *val)
{
	if(slrc) return slrc->GetValue(id, val);
	return false;
}

void
ScrollBar::MBtrack(MouseEvent *mev, anyOutput *o)
{
	int i;

	for(i = 0; i < 3; i++) if(butts[i]) butts[i]->MBtrack(mev, o);
	if(slrc) slrc->MBtrack(mev, o);
}

Icon::Icon(tag_DlgObj *par, DlgInfo * desc, RECT rec, int *ico)
	:Dialog(par, desc, rec)
{
	icon = ico ? *ico : 0;
}

void
Icon::DoPlot(anyOutput *o)
{
	o->oDrawIcon(icon, cr.left, cr.top);
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

Group::Group(tag_DlgObj *par, DlgInfo *desc, RECT rec)
	:Dialog(par, desc, rec)
{
	numChildren = 5;
	Children = (Dialog **)calloc(numChildren, sizeof(Dialog *));
	TextFocus = 0L;
}

Group::~Group()
{
	//pointers to child objects are allocated and freed by parent:
	//  pointers are copies and we do not care further;
	if(Children) free(Children);
}

bool
Group::Command(int cmd, void *tmpl, anyOutput *o)
{
	Dialog *d, **tmp;

	int i;

	switch (cmd) {
	case CMD_FLUSH:
		if(Children) for(i = 0; i < numChildren; Children[i++] = 0L);
		break;
	case CMD_CONTINUE:		case CMD_ENDDIALOG:		case CMD_TABDLG:
	case CMD_NOTABDLG:		case CMD_MARKOBJ:
		if(parent && (flags & HIDDEN) != HIDDEN) return parent->Command(cmd, tmpl, o);
		else return false;
	case CMD_ADDCHILD:
		if(Children && numChildren && (d = (Dialog*)tmpl)) {
			d->parent = this;
			if(!TextFocus &&(d->type == EDTEXT || d->type == RANGEINPUT ||
				d->type == INCDECVAL1 || d->type == EDVAL1))
				TextFocus = ((InputText*)tmpl)->bActive ? (InputText*)tmpl: 0L ;
			for(i = 0; i < numChildren; i++) {
				if(!Children[i] || Children[i] == d) {
					Children[i] = d;
					return true;
					}
				}
			//if we come here the list for children is too short
			tmp = (Dialog **)realloc(Children, (numChildren+1) * sizeof(Dialog *));
			if(!tmp) return false;
			Children = tmp;
			Children[numChildren++] = d;
			return true;
			}
		break;
	case CMD_RADIOBUTT:
		if(Children && numChildren) {
			d = (Dialog *)tmpl;
			for(i = 0; i < numChildren; i++) {
				if(Children[i] && Children[i] != d && Children[i]->type == d->type &&
					Children[i]->bChecked) {
					Children[i]->bChecked = false;
					Children[i]->DoPlot(o);
					}
				}
			}
		break;
	case CMD_FOCTXT:
		DialogTabStop = TextFocus = (InputText *)tmpl;
		break;
		}
	return false;
}

void
Group::DoPlot(anyOutput *o)
{
	int i;

	if(flags & HIDDEN) return;
	if(Children && numChildren && bChecked) {
		for(i = 0; i < numChildren; i++) {
			if(Children[i] && !(Children[i]->flags & HIDDEN)) {
				Children[i]->DoPlot(o);
				if(Children[i] == (Dialog*)TextFocus && DialogFocus != DialogTabStop)
					TextFocus->Activate(TextFocus->Id, true);
				}
			}
		}
	else if(Children && numChildren && parent) {
		for(i = 0; i < numChildren; i++) {
			if(Children[i]) parent->Command(CMD_NOTABDLG, Children[i], o); 
			}
		}
}

bool
Group::Select(int x, int y, anyOutput *o)
{
	int i;

	if(Children && numChildren) {
		for(i = 0; i < numChildren; i++) {
			if(Children[i] && !(Children[i]->flags &HIDDEN) && Children[i]->Select(x, y, o)) 
				return (bChecked = true);
			}
		}
	return false;
}

void
Group::MBtrack(MouseEvent *mev, anyOutput *o)
{
	int i;

	if(bChecked && Children && numChildren) {
		for(i = 0; i < numChildren; i++) {
			if(Children[i]&& !(Children[i]->flags &HIDDEN)) Children[i]->MBtrack(mev, o);
			}
		}
}

GroupBox::GroupBox(tag_DlgObj *par, DlgInfo * desc, RECT rec, char *txt)
	:Group(par,desc, rec)
{
	if(txt && txt[0])Text = (char*)memdup(txt, (int)strlen(txt)+1, 0);
	else Text = 0L;
	Line.color = 0x00000000L;					Line.width = 0.0;
	Fill.color = DlgBGhigh;						TextDef.ColBg = Fill.color;
	TextDef.Align = TXA_HLEFT | TXA_VCENTER;	TextDef.Mode = TXM_OPAQUE;
}

GroupBox::~GroupBox()
{
	if(Text) free(Text);
}

void
GroupBox::DoPlot(anyOutput *o)
{
	o->SetLine(&Line);
	o->SetFill(&Fill);
	o->oRectangle(cr.left, cr.top, cr.right-1, cr.bottom-1);
	if(Text) {
		o->SetTextSpec(&TextDef);
		hcr.top = cr.top - TextDef.iSize;
		o->oTextOut(cr.left+4, cr.top, Text, 0);
		}
	Group::DoPlot(o);
	defs.UpdRect(o, hcr.left, hcr.top, hcr.right, hcr.bottom);
}

TabSheet::TabSheet(tag_DlgObj *par, DlgInfo * desc, RECT rec, TabSHEET * sh, DataObj *d)
	:Group(par, desc, rec)
{
	if(sh->text && sh->text[0])Text = (char*)memdup(sh->text, (int)strlen(sh->text)+1, 0);
	else Text = 0L;
	rctab.left = cr.left + sh->x1 * xbase;			rctab.right = cr.left + sh->x2 * xbase;
	rctab.top = cr.top;								rctab.bottom = cr.top + sh->height * ybase;
	TextDef.Align = TXA_HRIGHT | TXA_VTOP;			flags |= ISRADIO;
	data = d;
}

TabSheet::~TabSheet()
{
	if(Text) free (Text);
}

void
TabSheet::DoPlot(anyOutput *o)
{
	POINT pts[6];

	pts[0].x = pts[1].x = pts[5].x = rctab.left;
	pts[0].y = pts[4].y = pts[5].y = rctab.bottom;
	pts[1].y = (rctab.top + rctab.bottom)/2;
	pts[2].x = rctab.left + (rctab.bottom - rctab.top)/2;
	pts[2].y = pts[3].y = rctab.top;
	pts[3].x = pts[4].x = rctab.right-1;
	HideCopyMark();
	Line.color = 0x0L;				Line.width = 0.0;
	Fill.color = bChecked ? DlgBGhigh : DlgBGcolor;
	o->SetLine(&Line);				o->SetFill(&Fill);
	o->oPolygon(pts, 6);
	if(bChecked) {
		o->oRectangle(cr.left, rctab.bottom, cr.right, cr.bottom);
		Line.color = DlgBGhigh;
		o->SetLine(&Line);
		pts[4].x--;
		o->oSolidLine(pts+4);
		}
	o->SetTextSpec(&TextDef);
#ifdef _WINDOWS
	o->oTextOut(rctab.right - 6, rctab.top + 3, Text, 0);
#else
	o->oTextOut(rctab.right - 6, rctab.top + 5, Text, 0);
	if(bChecked) {
		Line.color = 0x0L;			o->SetLine(&Line);
		pts[0].y++;					o->oSolidLine(pts);
		}
#endif
	Group::DoPlot(o);
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
TabSheet::Select(int x, int y, anyOutput *o)
{
	if(IsInRect(&rctab, x, y)) {
		if(data)data->Command(CMD_ETRACC, 0L, 0L);
		if(parent) {
			parent->Command(CMD_RADIOBUTT, (void *)this, o);
			parent->Command(CMD_MARKOBJ, this, o);
			}
		bChecked = true;		DoPlot(o);
		if((flags & TOUCHEXIT) && parent) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		return true;
		}
	if(bChecked) return Group::Select(x, y, o);
	return false;
}

ODbutton::ODbutton(tag_DlgObj *par, DlgInfo * desc, RECT rec, void *proc)
	:Dialog(par, desc, rec)
{
	ODexec = (void(*)(int, void *, RECT *, anyOutput *, void *, int))proc;
	if(ODexec) (*ODexec)(OD_CREATE, parent, &cr, 0L, 0L, Id);
}

ODbutton::~ODbutton()
{
	if(ODexec) (*ODexec)(OD_DELETE, (void*)parent, &cr, 0L, 0L, Id);
}

void 
ODbutton::DoPlot(anyOutput *o)
{
	if(ODexec) (*ODexec)(bChecked ? OD_DRAWSELECTED : OD_DRAWNORMAL, (void*)parent, &cr, o, 0L, Id);
}

bool
ODbutton::Select(int x, int y, anyOutput *o)
{
	POINT p;

	if(!(flags & HIDDEN) && IsInRect(&cr, x, y)) {
		if(flags & ISRADIO) bChecked = true;
		else bChecked = bChecked ? false : true;
		if(flags & NOSELECT) bChecked = false;
		bLBdown = false;
		p.x = x;			p.y = y;
		if(ODexec) {
			(*ODexec)(OD_SELECT, (void*)parent, &cr, o, (void*)&p, Id);
			if(!(flags & NOSELECT))DoPlot(o);
			}
		if((flags & ISRADIO) && parent)
			parent->Command(CMD_RADIOBUTT, (void *)this, o);
		if((flags & TOUCHEXIT) && parent) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		return true;
		}
	return false;
}

void
ODbutton::MBtrack(MouseEvent *mev, anyOutput *o)
{
	int x = mev->x, y = mev->y;

	if(mev->Action == MOUSE_LBDOUBLECLICK) {
		Select(mev->x, mev->y, o);
		return;
		}
	if(!(flags & HIDDEN) && IsInRect(&cr, x, y) && ODexec) 
			(*ODexec)(OD_MBTRACK, (void*)parent, &cr, o, mev, Id);
}

Listbox::Listbox(tag_DlgObj *par, DlgInfo *des, RECT rec, char **list)
	:Dialog(par, des, rec)
{
	static DlgInfo sbd[] = {
		{1, 2, 0, 0x0L, VSCROLL, 0L, 0, 0, 0, 0}};
	RECT sr;
	int i;

	sbd[0].x = (sr.left = (cr.right-7*xbase))/xbase;
	sbd[0].y = (sr.top = cr.top)/ybase;		sr.right = cr.right;	sbd[0].w = 7;							
	sbd[0].h = ((sr.bottom = cr.bottom) - cr.top)/ybase;
	sb = new ScrollBar(this, &sbd[0], sr, true);
	bmp = 0L;		cl = bmh = 0;		hcr.right -= 7*xbase;
	Fill.color = 0x00ffffffL;			Line.color = 0x00000000L;
	for(i = ns = 0; list && list[i]; i++);	//count lines
	if(strings = (char **)calloc(i+1, sizeof(char*))){
		for(ns = i, i = 0; i < ns; i++){
			strings[i] = (char*)memdup(list[i], (int)strlen(list[i])+1, 0);
			}
		}
}

Listbox::~Listbox()
{
	int i;

	if(sb) delete(sb);
	if(bmp) DelBitmapClass(bmp);
	if(strings) {
		for(i = 0; i < ns; i++) if(strings[i])free(strings[i]);
		free(strings);
		}
}

bool
Listbox::Command(int cmd, void *tmpl, anyOutput *o)
{
	int i;
	double ps;
	char *txt;

	ps = ((double)(cr.bottom - cr.top))/((double)(bmh+TextDef.iSize+_SBINC));
	switch(cmd) {
	case CMD_REDRAW:
		DoPlot(o);
		return true;
	case CMD_PAGEUP:
		ps *= -1.0;
	case CMD_PAGEDOWN:
		if(sb && sb->GetValue(1, &spos)){
			spos += ps;
			sb->Command(CMD_SETSCROLL, (void*)&spos, o);
			DoPlot(o);
			}
		return true;
	case CMD_SETSCROLL:
		if(sb) return sb->Command(cmd, tmpl, o);
		break;
	case CMD_FINDTEXT:
		txt = (char*)tmpl;
		if(strings) for (i = 0; i < ns; i++) {
			if(strings[i] && 0 == strcmp(txt, strings[i])){
				cl = i;
				return true;
				}
			}
		return false;
		}
	return false;
}

void
Listbox::DoPlot(anyOutput *o)
{
	RECT mrk;

	if(!ns) return;
	if(!bmp && !CreateBitMap(o))return;
	startY = 0;
	if(sb && sb->GetValue(1, &spos)) startY = (int)(spos*(double)bmh);
	o->SetLine(&Line);			o->SetFill(&Fill);
	o->oRectangle(hcr.left, hcr.top, hcr.right+1, hcr.bottom);
	if(sb){
		sb->sLine = 1+(sb->hcr.bottom-sb->hcr.top-7*ybase)/ns;
		sb->DoPlot(o);
		}
	o->CopyBitmap(hcr.left+1, hcr.top+1, bmp, 0, startY, (hcr.right-hcr.left)-2, 
		(hcr.bottom-hcr.top)-2, false); 
	mrk.left = hcr.left+2;		mrk.top = (cl)*(TextDef.iSize+_SBINC) - startY + hcr.top;
	mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
	if(mrk.bottom > (hcr.top+1) && mrk.top < (hcr.bottom-1)){
		if(mrk.top < (hcr.top+1)) mrk.top = hcr.top+1;
		if(mrk.bottom > (hcr.bottom-1)) mrk.bottom = hcr.bottom-1;
		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, cl*(TextDef.iSize+_SBINC), 
			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true);
		}
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
Listbox::Select(int x, int y, anyOutput *o)
{
	int il;
	RECT mrk;

	if(IsInRect(&hcr, x, y)) {
		il = (y+startY-hcr.top)/(TextDef.iSize+_SBINC);
		if(il >= ns || il < 0){
			o->UpdateRect(&hcr, false);
			return false;
			}
		cl = il;
		mrk.left = hcr.left+2;		mrk.top = (il)*(TextDef.iSize+_SBINC) - startY + hcr.top;
		mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
		o->CopyBitmap(hcr.left+1, hcr.top+1, bmp, 0, startY, (hcr.right-hcr.left)-2, 
			(hcr.bottom-hcr.top)-2, false); 
		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, (cl)*(TextDef.iSize+_SBINC), 
			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true); 
		o->UpdateRect(&hcr, false);
		if((flags & TOUCHEXIT) && parent) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		return true;
		}
	if(sb)return sb->Select(x, y, o);
	return false;
}

bool
Listbox::GetInt(int id, int *val)
{
	*val = cl;
	return true;
}

bool
Listbox::GetText(int id, char *txt, int size)
{
	if(strings && ns && cl >= 0 && cl < ns){
		rlp_strcpy(txt, size, strings[cl]);
		return true;
		}
	return false;
}

void
Listbox::MBtrack(MouseEvent *mev, anyOutput *o)
{
	if(sb) sb->MBtrack(mev, o);
}

bool
Listbox::CreateBitMap(anyOutput *tmpl)
{
	int i, w, h;

	if(bmp) DelBitmapClass(bmp);
	bmp = 0L;
	if(tmpl && strings) {
		h = (TextDef.iSize+_SBINC) * ns;		w = (hcr.right - hcr.left);
		if(bmp = NewBitmapClass(w, h, tmpl->hres, tmpl->vres)){
			bmp->Erase(0x00ffffffL);
			bmp->SetTextSpec(&TextDef);
			bmh = h - (TextDef.iSize+_SBINC);
			for(i = 0; i < ns; i++) {
				bmp->oTextOut(5, i*(TextDef.iSize+_SBINC), strings[i], 0);
				}
			return true;
			}
		}
	return false;
}

Treeview::Treeview(tag_DlgObj *par, DlgInfo *des, RECT rec, GraphObj *g)
	:Dialog(par, des, rec)
{
	static DlgInfo sbd[] = {
		{1, 2, 0, 0x0L, VSCROLL, 0L, 0, 0, 0, 0}};
	RECT sr;

	sbd[0].x = (sr.left = (cr.right-7*xbase))/xbase;
	sbd[0].y = (sr.top = cr.top)/ybase;		sr.right = cr.right;	sbd[0].w = 7;							
	sbd[0].h = ((sr.bottom = cr.bottom) - cr.top)/ybase;
	sb = new ScrollBar(this, &sbd[0], sr, true);
	Fill.color = 0x00ffffffL;			Line.color = 0x00000000L;
	bmp = 0L;		startY = cl = 0;	hcr.right -= 7*xbase;
	bmh = hcr.bottom - hcr.top;			bmw = hcr.right - hcr.left;	
	ot = new ObjTree(0L, 0L, go = g);
	if(ot) ot->Command(CMD_TEXTDEF, &TextDef, 0L);
}

Treeview::~Treeview()
{
	if(sb) delete(sb);				sb = 0L;
	if(ot) delete(ot);				ot = 0L;
	if(bmp) DelBitmapClass(bmp);	bmp = 0L;
}

bool
Treeview::Command(int cmd, void *tmpl, anyOutput *o)
{
	double ps;

	ps = ((double)(cr.bottom - cr.top))/((double)(bmh+TextDef.iSize+_SBINC));
	switch(cmd) {
	case CMD_LAYERS:
		ot->Command(cmd, tmpl, o);
	case CMD_UPDATE:
		if(bmp) DelBitmapClass(bmp);
		bmh = hcr.bottom - hcr.top;			bmw = hcr.right - hcr.left;	
		bmp = ot->CreateBitmap(&bmw, &bmh, o);
		return true;
	case CMD_REDRAW:
		DoPlot(o);
		return true;
	case CMD_OBJTREE:
		if(tmpl) *((ObjTree**)tmpl) = ot;
		return true;
	case CMD_PAGEUP:
		ps *= -1.0;
	case CMD_PAGEDOWN:
		if(sb && sb->GetValue(1, &spos)){
			spos += ps;
			sb->Command(CMD_SETSCROLL, (void*)&spos, o);
			DoPlot(o);
			}
		return true;
	case CMD_SETSCROLL:
		if(sb) return sb->Command(cmd, tmpl, o);
		break;
		}
	return false;
}

void
Treeview::DoPlot(anyOutput *o)
{
	RECT mrk;

	if(!o || !ot) return;
	ot->Command(CMD_TEXTDEF, &TextDef, 0L);
	ns = ot->count_lines();
	if(bmp) ot->DoPlot(bmp);
	else if(!(bmp = ot->CreateBitmap(&bmw, &bmh, o)))return;
	startY = 0;
	if(sb && sb->GetValue(1, &spos)) startY = (int)(spos*(double)bmh);
	o->SetLine(&Line);			o->SetFill(&Fill);
	o->oRectangle(hcr.left, hcr.top, hcr.right+1, hcr.bottom);
	if(sb){
		sb->sLine = 1+(sb->hcr.bottom-sb->hcr.top-7*ybase)/ns;
		sb->DoPlot(o);
		}
	o->CopyBitmap(hcr.left+1, hcr.top+1, bmp, 0, startY, (hcr.right-hcr.left)-2, 
		(hcr.bottom-hcr.top)-2, false); 
	mrk.left = hcr.left+2;		mrk.top = (cl)*(TextDef.iSize+_SBINC) - startY + hcr.top;
	mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
	if(mrk.bottom > (hcr.top+1) && mrk.top < (hcr.bottom-1)){
		if(mrk.top < (hcr.top+1)) mrk.top = hcr.top+1;
		if(mrk.bottom > (hcr.bottom-1)) mrk.bottom = hcr.bottom-1;
		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, cl*(TextDef.iSize+_SBINC), 
			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true);
		}
	defs.UpdRect(o, cr.left, cr.top, cr.right, cr.bottom);
}

bool
Treeview::Select(int x, int y, anyOutput *o)
{
	int il;
	RECT mrk;

	if(IsInRect(&hcr, x, y)) {
		il = (y+startY-hcr.top)/(TextDef.iSize+_SBINC);
		if(il >= ns || il < 0){
			o->UpdateRect(&hcr, false);
			return false;
			}
		cl = il;
		mrk.left = hcr.left+2;		mrk.top = (il)*(TextDef.iSize+_SBINC) - startY + hcr.top;
		mrk.right = hcr.right-2;	mrk.bottom = mrk.top + (TextDef.iSize+_SBINC);
		o->CopyBitmap(hcr.left+1, hcr.top+1, bmp, 0, startY, (hcr.right-hcr.left)-2, 
			(hcr.bottom-hcr.top)-2, false); 
		o->CopyBitmap(mrk.left, mrk.top, bmp, 1, (cl)*(TextDef.iSize+_SBINC), 
			(hcr.right-hcr.left)-4, mrk.bottom-mrk.top, true); 
		o->UpdateRect(&hcr, false);
		if((flags & TOUCHEXIT) && parent) 
			parent->Command(CMD_ENDDIALOG, (void *)this, o);
		return true;
		}
	if(sb)return sb->Select(x, y, o);
	return false;
}

void
Treeview::MBtrack(MouseEvent *mev, anyOutput *o)
{
	if(sb) sb->MBtrack(mev, o);
}

bool
Treeview::GetInt(int id, int *val)
{
	*val = cl;
	return true;
}

LinePat::LinePat(tag_DlgObj *par, DlgInfo * desc, RECT rec, LineDEF *line)
	:Dialog(par, desc, rec)
{
	pPattern = &line->pattern;
	Line.color = 0x00000000L;
	bDraw = true;
	cr.right = cr.left + (cr.bottom-cr.top)*32;
}

void
LinePat::DoPlot(anyOutput *o)
{
	int i, h;
	POINT ruler[2];
	RECT gr;

	h = cr.bottom-cr.top;
	memcpy(&gr, &cr, sizeof(RECT));
	o->SetLine(&Line);
	for(i = 0; i < 32; i++) {
		if(*pPattern &(1<<i)) Fill.color = 0x00ffffffL;
		else Fill.color = 0x00808080L;
		o->SetFill(&Fill);
		o->oRectangle(cr.left+i*h, cr.top, cr.left+i*h+h, cr.top+h);
		}
	ruler[0].y = cr.top+h;
	ruler[1].y = ruler[0].y + h/2;
	for(i = 0; i <=4; i++) {
		ruler[0].x = ruler[1].x = cr.left+i*8*h+(i ? -1:0);
		o->oSolidLine(ruler);
		}
	gr.bottom += h;
	defs.UpdRect(o, gr.left, gr.top, gr.right, gr.bottom);
}

void
LinePat::MBtrack(MouseEvent *mev, anyOutput *o)
{
	int i, x, y;
	DWORD mask, oldpat;

	if(!(mev->StateFlags &1))return;	//draw with mouse left button
	x = mev->x;						y = mev->y;
	if(x > cr.left && x < cr.right && y > cr.top && y < cr.bottom){
		i = (x-cr.left)/(cr.bottom-cr.top);
		oldpat = *pPattern;
		mask = (1<<i);
		//use first hit (button down) to select color, true or false
		bDraw = mev->Action == MOUSE_LBDOWN ? 0L != (*pPattern & mask) : bDraw;
		if(bDraw) *pPattern &= ~mask;
		else *pPattern |= mask;
		if(oldpat != *pPattern) {
			DoPlot(o);
			if((flags & TOUCHEXIT) && parent) parent->Command(CMD_ENDDIALOG, (void *)this, o);
			}
		}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Common code for multiple range dialogs
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int com_StackDlg(int res, DlgRoot *Dlg, AccRange **rX, int *nx, char ***rd, int *currYR,
	AccRange **rY, bool *bContinue, int *ny, int *maxYR, bool *updateYR)
{
	char **tmprd;

	switch (res) {
		case 1:
			if(rX && nx && Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0] && 
				(*rX = new AccRange(TmpTxt))) *nx = rX[0]->CountItems();
			else if(nx) *nx = 0;
			if(Dlg->GetText(154, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0]) {
				if(rd[0][*currYR]) free(rd[0][*currYR]);
				rd[0][*currYR] = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);
				}
			break;
		case 155:
			res = -1;
			*ny = 0;
			if(rX) {
				if(!(*currYR) && Dlg->GetText(101, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0]) {
					if(*rX = new AccRange(TmpTxt)){
						*nx = rX[0]->CountItems();
						delete *rX;						*rX = 0L;
						}
					}
				if(!(*nx)) {
					ErrorBox("X-range is empty\nor not valid!\n\nEnter a valid range\n"
						"for common x-values.");
					*bContinue = true;
					break;
					}
				}
			if(Dlg->GetText(154, TmpTxt, TMP_TXT_SIZE) && TmpTxt[0]) {
				if(*rY = new AccRange(TmpTxt)){
					*ny = rY[0]->CountItems();
					delete *rY;
					*rY = 0L;
					}
				}
			if(!(*ny)) {
				ErrorBox("Y-range is empty\nor not valid!\n\nEnter a valid range\n"
					"for y-values with the same\nsize as the x-range.");
				*bContinue = true;
				break;
				}
			if((*currYR)+1 > *maxYR) {
				tmprd = (char**)realloc(*rd, sizeof(char*)*((*currYR)+2));
				if(tmprd) *rd = tmprd;
				else break;
				*maxYR = (*currYR)+1;
				rd[0][*currYR] = 0L;
				rd[0][(*currYR)+1] = 0L;
				}
			if(rd[0][*currYR]) free(rd[0][*currYR]);
			rd[0][*currYR] = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);	//store y-ranges
			*updateYR = true;
			(*currYR)++;
			Dlg->SetText(154, rd[0][*currYR]);
			Dlg->Activate(154, true);
			break;
		case 156:
			if(Dlg->GetText(154, TmpTxt, TMP_TXT_SIZE)){
				if(rd[0][*currYR]) free(rd[0][*currYR]);
				rd[0][*currYR] = (char*)memdup(TmpTxt, ((int)strlen(TmpTxt))+2, 0);;
				}
			else if(*currYR == *maxYR) (*maxYR)--;
			(*currYR)--;
			Dlg->SetText(154, rd[0][*currYR]);
			*updateYR = true;
			res = -1;
			break;
		}
	return res;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Dialog meta compiler
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static char *std_text[] = {"OK", "Cancel", "", "x", "y", "z", "-", "Next >>", "<< Prev.",
	"%", "color", "x-value", "y-value", "z-value", TmpTxt, TmpTxt+100, TmpTxt+200,
	TmpTxt+300, TmpTxt+400, "left", "right", "top", "bottom", "x-axis", "y-axis", "z-axis",
	"select style:"};

DlgInfo *CompileDialog(char* tmpl, void **ptypes)
{
	char **lines, **fields, **flags, error[80];
	int i, j, nlines, nfields, nflags, last_id, last_next;
	unsigned int hv;
	DlgInfo *Dlg;

	std_text[2] = Units[defs.cUnits].display;
	lines = split(tmpl, '\n', &nlines);
	if(!lines || nlines <1 ||(!(Dlg = (DlgInfo*)malloc(nlines*sizeof(DlgInfo))))) return 0L;
	for(i = last_id = last_next = 0; i < nlines; i++) if(lines[i] && lines[i][0]){
		if(fields = split(lines[i], ',', &nfields)) {
			if(nfields == 10) {
				Dlg[i].id = Dlg[i].next = Dlg[i].first = 0;
				if(fields[0][0]) {
					if(fields[0][0] == '.') Dlg[i].id = (last_id += 1);
					else 
#ifdef USE_WIN_SECURE
						sscanf_s(fields[0], "%d", &Dlg[i].id);		last_id = Dlg[i].id;
#else
						sscanf(fields[0], "%d", &Dlg[i].id);		last_id = Dlg[i].id;
#endif
					}
				if(fields[1][0]) {
					if(fields[1][0] == '+') Dlg[i].next = (last_id + 1);
					else if(fields[1][0] == '.') Dlg[i].next = (last_next += 1);
					else 
#ifdef USE_WIN_SECURE
						sscanf_s(fields[1], "%d", &Dlg[i].next);	last_next = Dlg[i].next;
#else
						sscanf(fields[1], "%d", &Dlg[i].next);		last_next = Dlg[i].next;
#endif
					}
#ifdef USE_WIN_SECURE
				sscanf_s(fields[2], "%d", &Dlg[i].first);
#else
				sscanf(fields[2], "%d", &Dlg[i].first);
#endif
				Dlg[i].flags = 0L;
				if(flags = split(fields[3], '|', &nflags)) {
					for(j = 0; j < nflags; j++){
						hv = HashValue((unsigned char *)str_trim(flags[j]));
						switch(hv) {
						case 196904:		Dlg[i].flags |= CHECKED;		break;
						case 4444568:		Dlg[i].flags |= TOUCHEXIT;		break;
						case 284491160:		Dlg[i].flags |= TOUCHSELEXIT;	break;
						case 235859:		Dlg[i].flags |= ISRADIO;		break;
						case 942268:		Dlg[i].flags |= ISPARENT;		break;
						case 4220131:		Dlg[i].flags |= OWNDIALOG;		break;
						case 198260:		Dlg[i].flags |= DEFAULT;		break;
						case 54530:			Dlg[i].flags |= HIDDEN;			break;
						case 1011472:		Dlg[i].flags |= NOSELECT;		break;
						case 3546:			Dlg[i].flags |= HREF;			break;
						case 62296:			Dlg[i].flags |= NOEDIT;			break;
						case 231330:		Dlg[i].flags |= LASTOBJ;		break;
						case 224595:		Dlg[i].flags |= EXRADIO;		break;
						case 60824:			Dlg[i].flags |= ODEXIT;			break;
							}
						free(flags[j]);
						}
					free(flags);
					}
				hv = HashValue((unsigned char *)str_trim(fields[4]));
				switch(hv) {
				case 17108522:		Dlg[i].type = PUSHBUTTON;	break;
				case 3252180:		Dlg[i].type = ARROWBUTT;	break;
				case 206036:		Dlg[i].type = COLBUTT;		break;
				case 13602346:		Dlg[i].type = FILLBUTTON;	break;
				case 261312:		Dlg[i].type = SHADE3D;		break;
				case 948692:		Dlg[i].type = LINEBUTT;		break;
				case 282068:		Dlg[i].type = SYMBUTT;		break;
				case 3403091:		Dlg[i].type = FILLRADIO;	break;
				case 1130835:		Dlg[i].type = SYMRADIO;		break;
				case 787668:		Dlg[i].type = CHECKBOX;		break;
				case 62812:			Dlg[i].type = RADIO0;		break;
				case 62813:			Dlg[i].type = RADIO1;		break;
				case 62814:			Dlg[i].type = RADIO2;		break;
				case 15460:			Dlg[i].type = LTEXT;		break;
				case 16996:			Dlg[i].type = RTEXT;		break;
				case 13156:			Dlg[i].type = CTEXT;		break;
				case 51300:			Dlg[i].type = EDTEXT;		break;
				case 16235656:		Dlg[i].type = RANGEINPUT;	break;
				case 51281:			Dlg[i].type = EDVAL1;		break;
				case 14534481:		Dlg[i].type = INCDECVAL1;	break;
				case 229196:		Dlg[i].type = HSCROLL;		break;
				case 286540:		Dlg[i].type = VSCROLL;		break;
				case 71804:			Dlg[i].type = TXTHSP;		break;
				case 3418:			Dlg[i].type = ICON;			break;
				case 14196:			Dlg[i].type = GROUP;		break;
				case 909332:		Dlg[i].type = GROUPBOX;		break;
				case 16408:			Dlg[i].type = SHEET;		break;
				case 970282:		Dlg[i].type = ODBUTTON;		break;
				case 957537:		Dlg[i].type = LISTBOX1;		break;
				case 1108443:		Dlg[i].type = TREEVIEW;		break;
				case 237304:		Dlg[i].type = LINEPAT;		break;
				case 269332:		Dlg[i].type = TEXTBOX;		break;
				case 787858:		Dlg[i].type = CHECKPIN;		break;
				case 17284:			Dlg[i].type = TRASH;		break;
				case 51627:			Dlg[i].type = CONFIG;		break;
				default:			Dlg[i].type = NONE;			break;
					}
#ifdef USE_WIN_SECURE
				sscanf_s(fields[5], "%d", &j);
#else
				sscanf(fields[5], "%d", &j);
#endif
				if(j < 0) Dlg[i].ptype = (void*)std_text[(-j)-1];
				else if(j > 0) Dlg[i].ptype = ptypes[j-1];
				else Dlg[i].ptype = (void*)0L;
#ifdef USE_WIN_SECURE
				sscanf_s(fields[6], "%d", &Dlg[i].x);	sscanf_s(fields[7], "%d", &Dlg[i].y);
				sscanf_s(fields[8], "%d", &Dlg[i].w);	sscanf_s(fields[9], "%d", &Dlg[i].h);
#else
				sscanf(fields[6], "%d", &Dlg[i].x);		sscanf(fields[7], "%d", &Dlg[i].y);
				sscanf(fields[8], "%d", &Dlg[i].w);		sscanf(fields[9], "%d", &Dlg[i].h);
#endif
				}
			else {
#ifdef USE_WIN_SECURE
				sprintf_s(error, 80, "Wrong number of arguments in template line %d", i);
#else
				sprintf(error,"Wrong number of arguments in template line %d\n\"%s\"\n", i, lines[i]);
#endif
				InfoBox(error);
				Dlg[i].id = Dlg[i].next = Dlg[i].first = 0;
				Dlg[i].flags = 0L;		Dlg[i].type = NONE;
				Dlg[i].ptype = 0L;		Dlg[i].x = Dlg[i].y = Dlg[i].w = Dlg[i].h = 0;
				}
			for(j = 0; j < nfields; j++)free(fields[j]);
			free(fields);
			}
		free(lines[i]);
		}
	free(lines);
	return Dlg;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Use marked ranges as default for dialog ranges
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool UseRangeMark(DataObj *d, int type, char *r0, char *r1, char *r2, char *r3,
	char *r4, char *r5, char *r6, char *r7, char *r8, char *r9, char *r10)
{
	char *dst[] = {r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10};
	char *mrk, **ra =0L;;
	int i, j, ranges=0;
	bool success = false, bErr=false;
	RECT vrc, rrc;
	AccRange *ar;
	bool bRet = true;

	for(i = 0; i < 11; i++) if(dst[i]) *dst[i] = 0;
	if(d && type) {
		d->ValueRec(&vrc);
		if(d->Command(CMD_GETMARK, &mrk, 0L)) {
			ra = split(mrk, ';', &ranges);
			ar = new AccRange(mrk);			ar->BoundRec(&vrc);			delete ar;
			}
		switch(type) {
		case 1:
			if(ranges > 1) {
				for(i = 0; i < 11; i++) if(dst[i]) {
					rlp_strcpy(dst[i], 100, i < ranges ? ra[i] : mkRangeRef(vrc.top, vrc.left, vrc.bottom, vrc.left));
					}
				success = true;
				}
			else if(vrc.top == vrc.bottom && (vrc.right - vrc.left) >2) {
				for(i = 0; i < 11; i++) if(dst[i]){
					rlp_strcpy(dst[i], 100, mkRangeRef(vrc.top+i, vrc.left, vrc.top+i, vrc.right));
					}
				success = true;
				}
			else if(vrc.right == vrc.left && (vrc.bottom - vrc.top) >2) {
				for(i = 0; i < 11; i++) if(dst[i]){
					rlp_strcpy(dst[i], 100, mkRangeRef(vrc.top, vrc.left+i, vrc.bottom, vrc.left+i));
					}
				success = true;
				}
			break;
		case 2:
			for(i = 0; i < 11; i++) if(dst[i]) *(dst[i]) = 0;
			if(ranges == 1) {
				for(i = 0, j = vrc.left; i < 11 && j <= vrc.right; i++, j++) if(dst[i]){
					rlp_strcpy(dst[i], 100, mkRangeRef(vrc.top, vrc.left+i, vrc.bottom, vrc.left+i));
					}
				}
			else if(ranges > 1) {
				ar = new AccRange(ra[0]);		j = ar->CountItems();
				ar->BoundRec(&rrc);				delete ar;
				if(rrc.left == rrc.right)for(i = 1; i < 11 && i < ranges && !bErr; i++){
					ar = new AccRange(ra[i]);	ar->BoundRec(&rrc);
					if(rrc.left != rrc.right) bErr = true;
					delete ar;
					}
				else if(rrc.top == rrc.bottom)for(i = 1; i < 11 && i < ranges && !bErr; i++){
					ar = new AccRange(ra[i]);	ar->BoundRec(&rrc);
					if(rrc.top != rrc.bottom) bErr = true;
					delete ar;
					}
				else for(i = 1; i < 11 && i < ranges && !bErr; i++){
					ar = new AccRange(ra[i]);
					if(j != ar->CountItems()) bErr = true;
					delete ar;
					}
				if(i < 12 && !bErr) for(i = 0; i < 11 && i < ranges; i++){
					if(dst[i]) rlp_strcpy(dst[i], 100, ra[i]);
					}
				}
			if(bErr) {
				InfoBox("Cannot resolve multiple ranges\nwith different sizes.\n\n"
					"Please select ranges with equal size!");
				i = 12;		bRet = false;
				}
			success = true;
			}
		}
	else {
		vrc.left = vrc.top = 0;		vrc.right = vrc.bottom = 9;
		}
	if(!success) for(i = 0; i < 11; i++) if(dst[i]){
		rlp_strcpy(dst[i], 100, mkRangeRef(vrc.top, vrc.left+i, vrc.bottom, vrc.left+i));
		}
	if(ra) {
		for(i = 0; i < ranges; i++) if(ra[i]) free(ra[i]);
		free(ra);
		}
	return bRet;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Select color out of predefined palette
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static char *NewColorTmpl =
	"1,2,,DEFAULT,PUSHBUTTON,-1,200,10,45,12\n"
	"2,3,,,PUSHBUTTON,-2,200,25,45,12\n"
	"3,7,4,CHECKED | ISPARENT,GROUPBOX,1,200,55,45,40\n"
	"4,5,,,LTEXT,0,210,60,45,9\n"
	"5,6,,,LTEXT,0,210,70,45,9\n"
	"6,,,,LTEXT,0,210,80,45,9\n"
	"7,+,,,RTEXT,2,5,130,79,9\n"
	".,.,,,INCDECVAL1,3,85,130,33,10\n"
	".,.,,,LTEXT,-10,122,130,10,9\n"
	".,,11,CHECKED | ISPARENT,GROUP,0,0,0,0,0";

DWORD GetNewColor(DWORD oldColor)
{
	double transp = (double)iround((double)((oldColor>>24) & 0xff)/2.55);
	double use_step = 10.0, use_minmax[] = {0.0, 100.0};
	void *ptypes[] = {(void*)" RGB ", (void*)"transparency", (void*)&transp};
	DlgInfo *ColDlg = CompileDialog(NewColorTmpl, ptypes);
	DWORD currcol, newcol, palette[230];
	int ilevels[] = {0x0, 0x40, 0x80, 0xc0, 0xe0, 0xff};
	int i, j, ir, ig, ib, col, row, res;
	DlgRoot *Dlg;
	void *hDlg;

	if(!(ColDlg = (DlgInfo *)realloc(ColDlg, 240 * sizeof(DlgInfo))))return oldColor;
	for(ir=row=col=0, i=10, j=0; ir<6; ir++) for(ig=0; ig<6; ig++) for(ib=0; ib<6; ib++) {
		ColDlg[i].id = i+1;				ColDlg[i].next = i+2;
		ColDlg[i].type = COLBUTT;			ColDlg[i].first = 0;
		palette[j] = currcol = (ilevels[ib]<<16)|(ilevels[ig]<<8)|(ilevels[ir]);
		ColDlg[i].flags = TOUCHEXIT | ISRADIO;
		if(currcol == (oldColor & 0x00ffffff)) ColDlg[i].flags |= CHECKED;
		ColDlg[i].ptype = (void *)&palette[j];
		ColDlg[i].x = 5 + col*10;			ColDlg[i].y = 5 + row*10;
		ColDlg[i].w = ColDlg[i].h = 10;
		col++;
		if(col >= 18) {
			col = 0;			row++;
			}
		i++, j++;
		}
	j=j;
	ColDlg[i-1].next = 0;
	ColDlg[i-1].flags |= LASTOBJ;
	newcol = currcol = oldColor;
	Dlg = new DlgRoot(ColDlg, 0L);
	Dlg->ItemCmd(8, CMD_STEP, (void*)&use_step);
	Dlg->ItemCmd(8, CMD_MINMAX, (void*)&use_minmax);
#ifdef USE_WIN_SECURE
	sprintf_s(TmpTxt, 10, "R: 0x%02x", currcol & 0xff);				Dlg->SetText(4, TmpTxt);
	sprintf_s(TmpTxt, 10, "G: 0x%02x", (currcol>>8) & 0xff);		Dlg->SetText(5, TmpTxt);
	sprintf_s(TmpTxt, 10, "B: 0x%02x", (currcol>>16) & 0xff);		Dlg->SetText(6, TmpTxt);
#else
	sprintf(TmpTxt, "R: 0x%02x", currcol & 0xff);			Dlg->SetText(4, TmpTxt);
	sprintf(TmpTxt, "G: 0x%02x", (currcol>>8) & 0xff);		Dlg->SetText(5, TmpTxt);
	sprintf(TmpTxt, "B: 0x%02x", (currcol>>16) & 0xff);		Dlg->SetText(6, TmpTxt);
#endif
	hDlg = CreateDlgWnd("Select color", 50, 50, 510, 330, Dlg, 0x4L);
	do {
		LoopDlgWnd();
		res = Dlg->GetResult();
		switch (res) {
		case 0:						//close window
		case 2:						//cancel
			currcol = oldColor;
			break;
		case 1:						//ok
			currcol = newcol;
			Dlg->GetValue(8, &transp);
			currcol &= 0x00ffffff;
			currcol |= ((((int)(transp*2.55))<<24)&0xff000000);
			break;
		default:
			if(res > 9 && res < 227) {
				currcol = newcol;
				if(Dlg->GetColor(res, &newcol) && currcol != newcol) {
					res = -1;
#ifdef USE_WIN_SECURE
					sprintf_s(TmpTxt, 10, "R: 0x%02x", newcol & 0xff);		Dlg->SetText(4, TmpTxt);
					sprintf_s(TmpTxt, 10, "G: 0x%02x", (newcol>>8) & 0xff);		Dlg->SetText(5, TmpTxt);
					sprintf_s(TmpTxt, 10, "B: 0x%02x", (newcol>>16) & 0xff);	Dlg->SetText(6, TmpTxt);
#else
					sprintf(TmpTxt, "R: 0x%02x", newcol & 0xff);			Dlg->SetText(4, TmpTxt);
					sprintf(TmpTxt, "G: 0x%02x", (newcol>>8) & 0xff);		Dlg->SetText(5, TmpTxt);
					sprintf(TmpTxt, "B: 0x%02x", (newcol>>16) & 0xff);		Dlg->SetText(6, TmpTxt);
#endif
					}
				}
			break;
			}
		}while (res < 0);
	CloseDlgWnd(hDlg);		delete Dlg;
	free(ColDlg);			return currcol;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Configure 3D shading
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static char *ConfShade_DlgTmpl = 
	"1,2,,DEFAULT,PUSHBUTTON,-1,95,10,45,12\n"
	"2,3,,,PUSHBUTTON,-2,95,25,45,12\n"
	"3,4,100,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
	"4,5,,ODEXIT,COLBUTT,1,60,10,15,10\n"
	"5,6,,ODEXIT,COLBUTT,2,60,37,15,10\n"
	"6,7,,ODEXIT,COLBUTT,1,60,49,15,10\n"
	"7,8,,,RTEXT,3,25,37,30,9\n"
	"8,9,,,RTEXT,4,25,49,30,9\n"
	"9,,,,SHADE3D,5,95,50,22,20\n"
	"100,101,,TOUCHEXIT,RADIO1,6,10,10,50,9\n"
	"101,,,TOUCHEXIT | LASTOBJ,RADIO1,7,10,25,60,9";

void ConfShade(FillDEF *oldfill)
{
	FillDEF newFill;
	void *dyndata[] = {(void*)&oldfill->color, (void*)&oldfill->color2, (void*)"HI-color:",
		(void*)"LO-color:", (void*)&newFill, (void*)"fixed color:", (void*)"use light sorce:"};
	DlgInfo *ShadeDlg = CompileDialog(ConfShade_DlgTmpl, dyndata);
	DlgRoot *Dlg;
	void *hDlg;
	int res;
	bool bRedraw;
	anyOutput *o;
	RECT rc_prev;

	if(!oldfill) return;
	memcpy(&newFill, oldfill, sizeof(FillDEF));
	newFill.type = FILL_LIGHT3D;
	if(!(Dlg = new DlgRoot(ShadeDlg, 0L))) return;
	if(oldfill->type & FILL_LIGHT3D) Dlg->SetCheck(101, 0L, true);
	else {
		Dlg->SetCheck(100, 0L, true);
		newFill.color2 = newFill.color;
		}
	hDlg = CreateDlgWnd("Shade and Fill Color", 50, 50, 300, 180, Dlg, 0x4L);
	bRedraw = true;
	o = Dlg->GetOutputClass();			o->LightSource(32.0, 16.0);
	rc_prev.left =240;					rc_prev.right = 280;
	rc_prev.top = 100;					rc_prev.bottom = 140;
	do {
		if(bRedraw) {
			Dlg->DoPlot(0L);			o->SetFill(&newFill);
			o->oSphere(260, 120, 17, 0L, 0);
			o->UpdateRect(&rc_prev, false);
			bRedraw = false;
			}
		LoopDlgWnd();
		res = Dlg->GetResult();
		switch(res) {
		case 4:		case 100:
			Dlg->GetColor(4, &newFill.color);
			newFill.color2 = newFill.color;
			Dlg->SetCheck(100, 0L, true);
			bRedraw = true;
			res = -1;
			break;
		case 5:		case 6:		case 101:
			Dlg->GetColor(5, &newFill.color2);
			Dlg->GetColor(6, &newFill.color);
			Dlg->SetCheck(101, 0L, true);
			bRedraw = true;
			res = -1;
			break;
			}
		}while (res < 0);
	if(res == 1) {
		memcpy(oldfill, &newFill, sizeof(FillDEF));
		if(Dlg->GetCheck(100)) oldfill->type &= ~FILL_LIGHT3D;
		else oldfill->type |= FILL_LIGHT3D;
		}
	CloseDlgWnd(hDlg);		delete Dlg;			 free(ShadeDlg);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Select fill pattern
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static char *FillDlgBase_Tmpl = 
	"1,2,,DEFAULT,PUSHBUTTON,-1,170,10,45,12\n"
	"2,3,,,PUSHBUTTON,-2,170,25,45,12\n"
	"3,100,500,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
	"100,+,,TOUCHEXIT,FILLBUTTON,1,170,75,45,45\n"
	".,.,,,RTEXT,2,78,95,40,8\n"
	".,.,,TOUCHEXIT,INCDECVAL1,3, 119, 95, 32, 10\n"
	".,.,,,LTEXT,-3,152,95,15,8\n"
	".,.,,,RTEXT,4,5,95,30,8\n"
	".,.,,ODEXIT,COLBUTT,8,36,95,25,10\n"
	".,.,,,RTEXT,5,78,110,40,8\n"
	".,.,,TOUCHEXIT,INCDECVAL1,6, 119, 110, 32, 10\n"
	".,.,,,LTEXT,-10,152,110,8,8\n"
	".,.,,,RTEXT,7,5,110,30,8\n"
	".,,,ODEXIT,COLBUTT,9,36,110,25,10";

void GetNewFill(FillDEF *oldfill)
{
	LineDEF PrevLine;
	FillDEF PrevFill;
	unsigned int EnumFills[] = {FILL_NONE, FILL_HLINES, FILL_VLINES, FILL_HVCROSS, FILL_DLINEU, FILL_DLINED,
		FILL_DCROSS, FILL_STIPPLE1, FILL_STIPPLE2, FILL_STIPPLE3, FILL_STIPPLE4, 
		FILL_STIPPLE5, FILL_ZIGZAG, FILL_COMBS, FILL_BRICKH, FILL_BRICKV, FILL_BRICKDU, 
		FILL_BRICKDD, FILL_TEXTURE1, FILL_TEXTURE2, FILL_WAVES1, FILL_SCALES, FILL_SHINGLES, 
		FILL_WAVES2, FILL_HERRING, FILL_CIRCLES, FILL_GRASS, FILL_FOAM, FILL_RECS, FILL_HASH,
		FILL_WATER};
	double fscale;
	void *dyndata[] = {(void *)&PrevFill, (void*)"line width", (void*)&PrevLine.width, (void*)"line color",
		(void*)"pattern size", (void*)&fscale, (void*)"BG color", (void*)&PrevLine.color, (void*) &PrevFill.color};
	DlgInfo *FillDlg;
	DlgRoot *Dlg;
	void *hDlg;
	int i, j, res;
	anyOutput *o;
	bool bRedraw, bContinue = false;
	double tmp, use_minmax[] = {10.0, 800.0};

	PrevLine.width = (oldfill && oldfill->hatch)? oldfill->hatch->width : 0.2f;
	PrevLine.patlength = 1.0f;
	PrevLine.color = (oldfill && oldfill->hatch)? oldfill->hatch->color : 0x00000000L;
	PrevLine.pattern = 0x00000000L;
	PrevFill.type = oldfill ? oldfill->type : FILL_NONE;
	PrevFill.color = oldfill ? oldfill->color : 0x00ffffffL;
	PrevFill.scale = oldfill ? oldfill->scale : 1.0f;
	PrevFill.hatch = &PrevLine;
	PrevFill.color2 = oldfill ? oldfill->color2 : 0x00ffffffL;
	fscale = PrevFill.scale *100.0f;
	if(!(FillDlg =  CompileDialog(FillDlgBase_Tmpl, dyndata))) return;
	if(!(FillDlg = (DlgInfo*)realloc(FillDlg, (NUM_FILLS + 15)*sizeof(DlgInfo)))) return;
	for(i = 14, j = 0; j < NUM_FILLS; i++, j++) {
		FillDlg[i].id = j+500;				FillDlg[i].next = j+501;
		FillDlg[i].first = 0;				FillDlg[i].flags = TOUCHEXIT;
		FillDlg[i].type = FILLRADIO;		FillDlg[i].ptype = (void*)(EnumFills+j);
		FillDlg[i].x = (j &0x07)*20+5;		FillDlg[i].y = (j>>3)*20+5;
		FillDlg[i].w = FillDlg[i].h = 20;
		}
	FillDlg[i-1].next = 0;
	FillDlg[i-1].flags |= LASTOBJ;
	Dlg = new DlgRoot(FillDlg, 0L);
	Dlg->ItemCmd(107, CMD_MINMAX, (void*)use_minmax);
	for(i = Dlg->FindIndex(500), j = 0; FillDlg[i].type == FILLRADIO; i++, j++)
		if(*((int*)FillDlg[i].ptype) == PrevFill.type)Dlg->SetCheck(500+j, 0L, true);
	hDlg = CreateDlgWnd("Fill patterns", 50, 50, 450, 290, Dlg, 0x4L);
	bRedraw = true;
	o = Dlg->GetOutputClass();
	do {
		LoopDlgWnd();
		res = Dlg->GetResult();
		if (res >= 0) switch (res) {
		case 0:								// focus lost
			if(bContinue) res = -1;
			break;
		case -1:
			bContinue = false;
			break;
		default:					// test for pattern
			i = Dlg->FindIndex(res);
			if(FillDlg[i].type == FILLRADIO) {
				bRedraw = bContinue = true;
				i = *((int*)FillDlg[i].ptype);
				if(i != PrevFill.type){		// process double click on pattern
					PrevFill.type = i;		res = -1;
					defs.Idle(CMD_UPDATE);
					}
				else res = 1;			// assume OK pressed
				break;
				}
			else break;
		case 110:					// fill color changed
		case 102:					// line width changed
		case 105:					// line color changed
		case 107:					// scaling changed
		case 100:					// update preview
			bContinue = true;
			res = -1;				// ...these buttons do not exit dialog
		case 1:						// but do exit with OK
			if((bRedraw = Dlg->GetValue(102, &tmp)))PrevLine.width = tmp;
			if(bRedraw) bRedraw = Dlg->GetColor(105, &PrevLine.color);
			if(bRedraw) bRedraw = Dlg->GetColor(110, &PrevFill.color);
			if(bRedraw && (bRedraw = Dlg->GetValue(107, &tmp))) 
				PrevFill.scale = tmp/100.0;
			break;
			}
		if (o && bRedraw) {					// send update preview to dialog
			Dlg->ForEach(CMD_DOPLOT, Dlg->FindIndex(100), o);
			bRedraw = false;
			}
		}while (res < 0);
	switch(res) {
	case 1:									//OK button pressed
		if(oldfill) {
			oldfill->type = PrevFill.type;
			oldfill->color = PrevFill.color;
			oldfill->scale = PrevFill.scale;
			if(oldfill->hatch) {
				oldfill->hatch->width = PrevLine.width;
				oldfill->hatch->patlength = PrevLine.patlength;
				oldfill->hatch->color = PrevLine.color;
				oldfill->hatch->pattern = PrevLine.pattern;
				}
			}
		break;
		}
	CloseDlgWnd(hDlg);	delete Dlg;		free(FillDlg);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// execute layers dialog
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static char* LayerDlg_Tmpl =
	"1,3,0,DEFAULT,PUSHBUTTON,-1,165,10,45,12\n"
	"3,20,100,ISPARENT | CHECKED,GROUP,0,0,0,0,0\n"
	"20,550,,,CHECKPIN,0,10,0,12,8\n"
	"100,101,,TOUCHEXIT,TREEVIEW,1,10,15,100,70\n"
	"101,102,,,EDTEXT,2, 120, 25, 90, 10\n"
	"102,200,150,ISPARENT | CHECKED | HIDDEN,GROUP,0L,0,0,0,0\n"
	"150,151,,TOUCHEXIT,RADIO1,3,120,35,40,9\n"
	"151,,,TOUCHEXIT,RADIO1,4,160,35,40,9\n"
	"200,500,,,LTEXT,5,10,5,110,9\n"
	"500,501,600,ISPARENT | CHECKED | HIDDEN,GROUP,0,0,0,0,0\n"
	"501,,,HIDDEN | TOUCHEXIT,TRASH,0,125,72,15,15\n"
	"550,,,HIDDEN | TOUCHEXIT,CONFIG,0,140,72,15,15\n"
	"600,601,,TOUCHEXIT, ODBUTTON,6,125,50,15,15\n"
	"601,602,,TOUCHEXIT, ODBUTTON,6,145,50,15,15\n"
	"602,603,,TOUCHEXIT, ODBUTTON,6,165,50,15,15\n"
	"603,,,LASTOBJ | TOUCHEXIT, ODBUTTON,6,185,50,15,15";

bool ShowLayers(GraphObj *root)
{
	char curr_name[50];
	void *dyndata[] = {(void*)root, (void*)curr_name, (void*)"hidden", (void*)"visible",
		(void*)"Layers:", (void*)OD_DrawOrder};
	DlgInfo *LayerDlg;
	DlgRoot *Dlg;
	void *hDlg;
	int res, cl;
	bool bContinue = false;
	ObjTree *ot = 0L;
	GraphObj *cgo = 0L;

	if(!root || !(LayerDlg = CompileDialog(LayerDlg_Tmpl, dyndata))) return false;
	rlp_strcpy(curr_name, 50, "(root)");
	if(!(Dlg = new DlgRoot(LayerDlg, 0L)))return false;
	Dlg->ItemCmd(100, CMD_OBJTREE, &ot);
	if(!ot) {
		delete Dlg;			return false;
		}
	Dlg->SetText(101, ot->get_name(0));		Dlg->Activate(101, false);
	Dlg->SetColor(150, 0x00000080L);		Dlg->SetColor(151, 0x00008000L);
	if(root->Id == GO_GRAPH || root->Id == GO_PAGE) Dlg->ShowItem(550, true);
	hDlg = CreateDlgWnd("Layer Control", 50, 50, 440, 210, Dlg, 0x4L);
	do{
		LoopDlgWnd();
		res = Dlg->GetResult();
		switch (res) {
		case 0:
			if(bContinue || Dlg->GetCheck(20)) res = -1;
			break;
		case -1:
			bContinue = false;
			break;
		case 550:
			bContinue = true;
		case 501:
			if (res == 501) {
				if(cgo && cgo->parent)	cgo->parent->Command(CMD_DELOBJ, cgo, 0L);
				Dlg->ItemCmd(100, CMD_LAYERS, 0L);
				}
		case 100:
			if(Dlg->GetInt(100, &cl)) Dlg->SetText(101, ot->get_name(cl));
			switch(ot->get_vis(cl)) {
			case 0:
				Dlg->SetCheck(150, 0L, true);			Dlg->ShowItem(102, true);
				break;
			case 1:
				Dlg->SetCheck(151, 0L, true);			Dlg->ShowItem(102, true);
				break;
			case 2:
				Dlg->ShowItem(102, false);
				break;
				}
			if((cgo = ot->get_obj(cl)) && cgo->parent) {
				if(cgo->parent->Id == GO_STACKBAR || cgo->parent->Id == GO_STACKPG ||
					cgo->parent->Id == GO_WATERFALL || cgo->parent->Id == GO_PLOT3D ||
					cgo->parent->Id == GO_FUNC3D) {
					Dlg->ShowItem(501, true);
					}
				else if(cgo->parent->Id == GO_GRAPH || cgo->parent->Id == GO_PAGE){ 
					Dlg->ShowItem(500, cgo->parent->Command(CMD_HASSTACK, 0L, 0L));
					Dlg->ShowItem(501, true);
					}
				else {
					Dlg->ShowItem(500, false);			Dlg->ShowItem(501, false);
					}
				if(cgo->Id == GO_GRAPH || cgo->Id == GO_PAGE || cgo->Id == GO_POLARPLOT
					|| cgo->Id == GO_GRID3D){
					Dlg->ShowItem(550, true);
					if(res == 550) if(cgo->Command(CMD_CONFIG, 0L, 0L))
						cgo->Command(CMD_REDRAW, 0L, 0L);
					}
				else Dlg->ShowItem(550, false);
				}
			Dlg->Command(CMD_REDRAW, 0L, 0L);
			res = -1;
			break;
		case 150:		case 151:
			ot->set_vis(cl, res == 151);
			Dlg->ItemCmd(100, CMD_UPDATE, 0L);			Dlg->Command(CMD_REDRAW, 0L, 0L);
			res = -1;
			break;
		case 600:	case 601:	case 602:	case 603:
			if(cgo && cgo->parent){
				ExecDrawOrderButt(cgo->parent, cgo, res);
				}
			Dlg->ItemCmd(100, CMD_UPDATE, 0L);			Dlg->Command(CMD_REDRAW, 0L, 0L);
			res = -1;
			}
		}while(res <0);
	CloseDlgWnd(hDlg);		delete Dlg;			free(LayerDlg);
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// display a welcome banner
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void ShowBanner(bool show)
{
	int icon = ICO_RLPLOT;
	static DlgInfo BannerDlg[] = {
		{1, 2, 0, 0x0L, PUSHBUTTON, 0L, 0, 0, 112, 52},
		{2, 3, 0, 0x0L, ICON, (void*)&icon, 10, 10, 20, 20},
		{3, 4, 0, 0x0L, LTEXT, (void*)"RLPlot", 40, 15, 50, 20},
		{4, 5, 0, 0x0L, LTEXT, (void*)SZ_VERSION, 13, 30, 35, 9},
		{5, 0, 0, LASTOBJ, LTEXT, (void*)"... is loading", 50, 30, 50, 9}};
	DlgRoot *Dlg;
	void *hDlg;
	bool init = true;
	int res, cnt = 5;

	if(!show) return;
	if(!(Dlg = new DlgRoot(BannerDlg, 0L)))return;
#ifdef _WINDOWS
	Dlg->TextSize(3, 36);
#else
	Dlg->TextSize(3, 24);
#endif
	Dlg->TextStyle(3, TXS_ITALIC | TXS_BOLD);
	Dlg->TextFont(3, FONT_TIMES);
	Dlg->SetColor(3, 0x00ff0000L);
	hDlg = CreateDlgWnd("RLPlot", 50, 50, 220, 100, Dlg, 0xbL);
	ShowDlgWnd(hDlg);
	do{
		LoopDlgWnd();
		res = Dlg->GetResult();
		switch (res) {
		case 0:						//loose focus: get it again
			ShowDlgWnd(hDlg);
			cnt = 5;		res = -1;
			break;
		case -2:					//Timer event
			if(init) {
				init = false;
				FindBrowser();			SpreadMain(true);
				ShowDlgWnd(hDlg);		cnt = 4;
				}
			if((cnt--) <=0) res = 1;
			break;
			}
		}while(res < 0);
	CloseDlgWnd(hDlg);
	delete Dlg;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// the RLPlot about dialog
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void RLPlotInfo()
{
	int ico_rlp = ICO_RLPLOT;
#ifdef _WINDOWS
	DlgInfo AboutDlg[] = {
		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 40, 110, 40, 12},
		{2, 3, 0, 0x0L, ICON, (void*)&ico_rlp, 10, 10, 16, 16},
		{3, 4, 0, 0x0L, LTEXT, (void*)"RLPlot", 40, 10, 60, 18},
		{4, 5, 0, 0x0L, CTEXT, (void*)"scientific plotting program", 10, 30, 100, 8},
		{5, 6, 0, 0x0L, CTEXT, (void*)"version "SZ_VERSION, 10, 38, 100, 8},
		{6, 7, 0, HREF, CTEXT, (void*)"http://rlplot.sourceforge.net/", 5, 46, 110, 8},
		{7, 8, 0, 0x0L, CTEXT, (void*)"Copyright (C) 2002-2007 R. Lackner", 5, 54, 110, 8},
		{8, 9, 0, 0x0L, CTEXT, (void*)"reinhard.lackner@uibk.ac.at", 5, 62, 110, 9},
		{9, 10, 0, 0x0L, CTEXT, (void*)"This is free software published", 5, 80, 110, 8},
		{10, 11, 0, 0x0L, CTEXT, (void*)"under the GNU general public.", 5, 88, 110, 8},
		{11, 0, 0, LASTOBJ, CTEXT, (void*)"license (GPL).", 5, 96, 110, 9}};
#else
	int ico_qt = ICO_QT;
	DlgInfo AboutDlg[] = {
		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 40, 128, 40, 12},
		{2, 3, 0, 0x0L, ICON, (void*)&ico_rlp, 10, 10, 16, 16},
		{3, 4, 0, 0x0L, LTEXT, (void*)"RLPlot", 40, 10, 60, 18},
		{4, 5, 0, 0x0L, CTEXT, (void*)"scientific plotting program", 10, 30, 100, 8},
		{5, 6, 0, 0x0L, CTEXT, (void*)"version "SZ_VERSION, 10, 38, 100, 8},
		{6, 7, 0, HREF, CTEXT, (void*)"http://rlplot.sourceforge.net/", 5, 46, 110, 8},
		{7, 8, 0, 0x0L, CTEXT, (void*)"Copyright (C) 2002-2007 R. Lackner", 5, 54, 110, 8},
		{8, 9, 0, 0x0L, CTEXT, (void*)"reinhard.lackner@uibk.ac.at", 5, 62, 110, 9},
		{9, 10, 0, 0x0L, LTEXT, (void*)"powered by Trolltech\'s Qt", 35, 72, 80, 8},
		{10, 11, 0, HREF, LTEXT, (void*)"http://www.trolltech.com", 35, 80, 80, 9},
		{11, 12, 0, TOUCHEXIT, ICON, (void*)&ico_qt, 5, 72, 25, 25},
		{12, 13, 0, 0x0L, CTEXT, (void*)"This is free software published", 5, 100, 110, 8},
		{13, 14, 0, 0x0L, CTEXT, (void*)"under the GNU general public.", 5, 108, 110, 8},
		{14, 0, 0, LASTOBJ, CTEXT, (void*)"license (GPL).", 5, 116, 110, 9}};
#endif // _WINDOWS
	DlgRoot *Dlg;
	void *hDlg;
	int res;

	if((Dlg = new DlgRoot(AboutDlg, 0L))) {
		Dlg->TextStyle(3, TXS_ITALIC | TXS_BOLD);
		Dlg->TextFont(3, FONT_TIMES);
#ifdef _WINDOWS
		Dlg->TextSize(3, 36);
#else
		Dlg->TextSize(3, 32);
#endif // _WINDOWS
		Dlg->SetColor(3, 0x00ff0000L);
		}
	else return;
#ifdef _WINDOWS
	hDlg = CreateDlgWnd("About RLPlot", 50, 50, 240, 280, Dlg, 0x4L);
#else
	hDlg = CreateDlgWnd("About RLPlot", 50, 50, 240, 310, Dlg, 0x4L);
#endif // _WINDOWS
	do{
		LoopDlgWnd();
		res = Dlg->GetResult();
		if(res == 11) Qt_Box();
		}while(res <0);
	CloseDlgWnd(hDlg);
	delete Dlg;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// change spreadsheet settings
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static char * SSDlg_Tmpl = 
	"1,2,,DEFAULT,PUSHBUTTON,-1,129,10,40,12\n"
	"2,3,,,PUSHBUTTON,-2,129,25,40,12\n"
	"3,,5,CHECKED | ISPARENT,GROUP,0,0,0,0,0\n"
	"5,6,100,ISPARENT | CHECKED,SHEET,1,5,10,115,85\n"
	"6,,200,ISPARENT,SHEET,2,5,10,115,85\n"
	"100,+,,,LTEXT,3,15,25,60,8\n"
	".,.,,,EDTEXT,-16,50,37,40,10\n"
	".,.,,,LTEXT,4,15,52,60,8\n"
	".,,,,EDTEXT,-17,50,64,40,10\n"
	"200,+,,,RTEXT,5,10,29,45,8\n"
	".,.,,,EDVAL1,6,57,29,25,10\n"
	".,.,,,LTEXT,7,84,29,20,8\n"
	".,.,,,RTEXT,8,10,41,45,8\n"
	".,.,,,EDVAL1,9,57,41,25,10\n"
	".,.,,,LTEXT,7,84,41,20,8\n"
	".,.,,,RTEXT,10,10,53,45,8\n"
	".,.,,,EDVAL1,11,57,53,25,10\n"
	".,.,,,LTEXT,-3,84,53,20,8\n"
	".,.,,,RTEXT,12,10,65,45,8\n"
	".,.,,,INCDECVAL1,13,57,65,33,10\n"
	".,.,,,LTEXT,-10,92,65,20,8\n"
	".,.,,,RTEXT,14,10,77,45,8\n"
	".,.,,,EDVAL1,15,57,77,25,10\n"
	".,,,LASTOBJ,LTEXT,16,84,77,20,8";

bool
DoSpShSize(DataObj *dt, GraphObj *parent)
{
	TabSHEET tab1 = {0, 50, 10, "Dimensions"};
	TabSHEET tab2 = {50, 115, 10, "Width and Height"};
	double fw, cw, ch, th, mh;
	void *dyndata[] = {(void*)&tab1, (void*)&tab2, (void*)"number of columns:", (void*)"number of rows:",
		(void*)"row buttons", (void*)&fw, (void*)"[digits]", (void*)"column width", (void*)&cw,
		(void*)"row height", (void*)&ch, (void*)"text size", (void*)&th, (void*)"menu height",
		(void*)&mh, (void*)"pix"};
	DlgInfo *SSDlg;
	DlgRoot *Dlg;
	void *hDlg;
	int w1, w2, h1, h2, res, celldim[3], ith;
	bool bRet = false;
	double fw1, cw1, ch1, th1, mh1;

	if(!dt || !dt->GetSize(&w1, &h1) || !parent) return false;
	if(!(SSDlg = CompileDialog(SSDlg_Tmpl, dyndata))) return false;
	dt->Command(CMD_GET_CELLDIMS, &celldim, 0L);
	fw1 = fw = NiceValue(((double)celldim[0])/((double)(celldim[2]-2)/2.0));
	cw1 = cw = NiceValue(((double)celldim[1])/((double)(celldim[2]-2)/2.0));
	mh1 = mh = (double)(defs.iMenuHeight);
	switch(defs.cUnits) {
	case 1:		ch = NiceValue(((double)(celldim[2]-2))*0.0259183673);	break;
	case 2:		ch = NiceValue(((double)(celldim[2]-2))/98.0);		break;
	default:	ch = NiceValue(((double)(celldim[2]-2))*0.259183673);	break;
		}
	ch1 = ch;						th = th1 = defs.ss_txt*100.0;
#ifdef USE_WIN_SECURE
	sprintf_s(TmpTxt+100, 40, "%d", w1);		sprintf_s(TmpTxt+200, 40, "%d", h1);
#else
	sprintf(TmpTxt+100, "%d", w1);			sprintf(TmpTxt+200, "%d", h1);
#endif
	Dlg = new DlgRoot(SSDlg, dt);
	hDlg = CreateDlgWnd("Change spread sheet settings", 50, 50, 354, 220, Dlg, 0x4L);
	Dlg->GetValue(201, &fw1);	Dlg->GetValue(204, &cw1);	Dlg->GetValue(207, &ch1);
	do{
		LoopDlgWnd();
		res = Dlg->GetResult();
		switch(res) {
		case 1:					//OK pressed
			if(Dlg->GetText(101, TmpTxt+100, 40) && Dlg->GetText(103, TmpTxt+200, 40)) {
				w2 = atol(TmpTxt+100);		h2 = atol(TmpTxt+200);
				w2 = w2 > 0 ? w2: w1;	h2 = h2 > 0 ? h2: h1;
				}
			else res = -1;
			break;
			}
		}while(res <0);
	if(res == 1){
		if(Dlg->GetValue(207, &ch) && Dlg->GetValue(210, &th) && ch > 0.001) {
			Undo.ValFloat(parent, &defs.ss_txt, 0L);
			defs.ss_txt = th = th >= 10.0 && th <= 100 ? th/100.0 : 1.0; 
			switch(defs.cUnits) {
			case 1:		
				celldim[2] = iround(ch/0.0259183673)+2;		ith = iround(th * ch/0.0259183673);
				break;
			case 2:		
				celldim[2] = iround(ch*98.0)+2;				ith = iround(th * ch*98.0);
				break;
			default:	
				celldim[2] = iround(ch/0.259183673)+2;		ith = iround(th * ch/0.259183673);
				break;
				}
			bRet = dt->Command(CMD_TEXTSIZE, (void*)(& ith), 0L);
			}
		Dlg->GetValue(201, &fw);	Dlg->GetValue(204, &cw);
		if(fw >0.001 && (fw != fw1 || ch != ch1))
			celldim[0] = iround(fw * ((double)(celldim[2]-3)/2.0));
		if(cw >0.001 && (cw != cw1 || ch != ch1))
			celldim[1] = iround(cw * ((double)(celldim[2]-3)/2.0));
		if(fw != fw1 || cw != cw1 || ch != ch1)
			dt->Command(CMD_SET_CELLDIMS, &celldim, 0L);
		if(!dt->ChangeSize(w2, h2, true))
			ErrorBox("Failed to set new dimensions\nof Spreadsheet.");
		else bRet = true;
		Dlg->GetValue(213, &mh);
		if(mh != mh1 && mh >= 0.0 && mh < 100) {
			defs.iMenuHeight = (int)mh;
			parent->Command(CMD_MENUHEIGHT, 0L, 0L);
			}
		}
	CloseDlgWnd(hDlg);		delete Dlg;			free(SSDlg);
	return bRet;
}


bool FillSsRange(DataObj *d, char **range, GraphObj *msg_go)
{
	TabSHEET tab1 = {0, 37, 10, "fill range "};
	char *ra = range ? *range:0L;
	double startval = 1.0, stepval = 1.0;
	static char *formula = (char*)malloc(500 * sizeof(char));
	if(formula && CurrText && CurrText->isFormula()) {
		if(CurrText->GetText(TmpTxt,TMP_TXT_SIZE, false)) rlp_strcpy(formula, 500, TmpTxt);
		}
	if(formula) rlp_strcpy(formula, 500, "=a1");
	DlgInfo RangeDlg[] = {
		{1, 2, 0, DEFAULT, PUSHBUTTON, (void*)"OK", 162, 5, 38, 12},
		{2, 3, 0, 0x0L, PUSHBUTTON, (void*)"Cancel", 162, 20, 38, 12},
		{3, 0, 5, CHECKED, GROUP, 0L, 0, 0, 0, 0},
		{5, 0, 6, ISPARENT | CHECKED, SHEET, &tab1, 5, 10, 150, 75},
		{6, 7, 0, 0x0L, RANGEINPUT, (void*)ra, 10, 25, 140, 10},
		{7, 8, 0, CHECKED | TOUCHEXIT, RADIO1, (void*)"with values", 10, 37, 45, 8},
		{8, 9, 0, TOUCHEXIT, RADIO1, (void*)"with formulas", 60, 37, 60, 8},
		{9, 10, 0, CHECKED, CHECKPIN, 0L, 5, 0, 12, 8},
		{10, 20, 11, CHECKED, GROUP, 0L, 0, 0, 0, 0},
		{11, 12, 0, 0x0L, RTEXT, (void*)"start value=", 10, 60, 35, 8},
		{12, 13, 0, 0x0L, EDVAL1, &startval, 45, 60, 30, 10},
		{13, 14, 0, 0x0L, RTEXT, (void*)"increment by", 77, 60, 43, 8},
		{14, 0, 0, 0x0L, EDVAL1, &stepval, 122, 60, 30, 10},
		{20, 0, 21, CHECKED | HIDDEN, GROUP, 0L, 0, 0, 0, 0},
		{21, 22, 0, 0x0L, RTEXT, (void*)"first cell formula:", 10, 52, 50, 8},
		{22, 23, 0, 0x0L, EDTEXT, (void*)formula, 60, 52, 90, 10},
		{23, 24, 0, 0x0L, LTEXT, (void*)"For a list with available funtions see:", 10, 65, 90, 8},
		{24, 0, 0, HREF | LASTOBJ | TOUCHEXIT, LTEXT, (void*)"http://rlplot.sourceforge.net/Docs/parser.html", 10, 72, 90, 8}};
	DlgRoot *Dlg;
	void *hDlg;
	int i, res, row, col, r1, c1;
	bool bRet = false, bContinue = false;
	AccRange *rF;
	RECT rc_dest;
	anyOutput *cdisp = 	Undo.cdisp;

	if(!d || !range) return false;
	if(!(Dlg = new DlgRoot(RangeDlg, d))) return false;
#ifdef _WINDOWS
	for(i = 23; i <= 24; i++) Dlg->TextSize(i, 12);
#else
	for(i = 23; i <= 24; i++) Dlg->TextSize(i, 10);
#endif
	hDlg = CreateDlgWnd("Fill Spreadsheet Range", 50, 50, 414, 206, Dlg, 0x4L);
	do{
		LoopDlgWnd();
		res = Dlg->GetResult();
		switch (res){
		case 0:
			if(bContinue) {
				bContinue = false;				res = -1;
				}
			else if(Dlg->GetCheck(9)) res = -1;
			break;
		case 1:
			ra = 0L;
			if(!Dlg->GetText(6, TmpTxt, TMP_TXT_SIZE)) {
				InfoBox("Range not specified\nor not valid.");
				bContinue = true;				res = -1;
				}
			else ra = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
			rc_dest.left = rc_dest.right = rc_dest.top = rc_dest.bottom = 0;
			break;
		case 7:
			Dlg->ShowItem(10, true);			Dlg->ShowItem(20, false);
			Dlg->Command(CMD_REDRAW, 0L, 0L);	res = -1;
			break;
		case 8:
			Dlg->ShowItem(10, false);	Dlg->ShowItem(20, true);
			Dlg->Command(CMD_REDRAW, 0L, 0L);	res = -1;
			break;
		case 24:
			bContinue=true;
			res = -1;
			break;
			}
		}while(res <0);
	Undo.SetDisp(cdisp);
	if(res == 1 && ra) {
		if(Dlg->GetCheck(7)) {
			Dlg->GetValue(12, &startval);	Dlg->GetValue(14, &stepval);
			if((rF = new AccRange(ra)) && rF->GetFirst(&col, &row)) {
				rF->BoundRec(&rc_dest);
				Undo.DataObject(msg_go, Undo.cdisp, d, &rc_dest, 0L);
				for( ; rF->GetNext(&col, &row); startval += stepval) {
					d->SetValue(row, col, startval);
					}
				delete rF;							bRet = true;
				}
			}
		else if(Dlg->GetCheck(8)) {
			if((rF = new AccRange(ra)) && rF->GetFirst(&col, &row) && 
				Dlg->GetText(22, TmpTxt, TMP_TXT_SIZE) && formula){
				rlp_strcpy(formula, 500, TmpTxt);
				r1 = row;		c1 = col;
				for( ; rF->GetNext(&col, &row); startval += stepval) {
					if(formula[0] == '=') {
						MoveFormula(d, formula, TmpTxt, TMP_TXT_SIZE, col-c1, row-r1, -1, -1);
						d->SetText(row, col, TmpTxt);
						}
					else d->SetText(row, col, formula);
					}
				delete rF;							bRet = true;
				}
			}
		free(ra);
		}
	CloseDlgWnd(hDlg);			delete Dlg;
	return bRet;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get resolution and size for exported bitmap
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static char * ResDlg_Tmpl = 
	"1,2,,DEFAULT,PUSHBUTTON,-1,125,10,35,12\n"
	"2,100,,,PUSHBUTTON,-2,125,25,35,12\n"
	"100,+,,,LTEXT,1,20,10,50,9\n"
	".,.,,,RTEXT,2,20,22,35,9\n"
	".,.,,,EDVAL1,3,57,22,30,10\n"
	".,.,,,LTEXT,-3,89,22,20,8\n"
	".,.,,,RTEXT,4,20,34,35,9\n"
	".,.,,,EDVAL1,5,57,34,30,10\n"
	".,.,,,LTEXT,-3,89,34,20,8\n"
	".,.,,,RTEXT,6,20,46,35,9\n"
	".,.,,,EDVAL1,7,57,46,30,10\n"
	".,,,LASTOBJ,LTEXT,8,89,46,20,8";

bool GetBitmapRes(double *dpi, double *width, double *height, char *header)
{
	void *dyndata[] = {(void*)"Image properties:", (void*)"width", (void*)width,
		(void*)"height", (void*)height, (void*)"resolution", (void*)dpi,  (void *) "dpi"};
	DlgInfo *ResDlg;
	DlgRoot *Dlg;
	void *hDlg;
	bool bRet = false;
	int res;
	
	if(!(ResDlg = CompileDialog(ResDlg_Tmpl, dyndata))) return false;
	if(!(Dlg = new DlgRoot(ResDlg, 0L))) return false;
	if(!(hDlg = CreateDlgWnd(header, 50, 50, 340, 160, Dlg, 0x4L)))return false;
	do {
		LoopDlgWnd();
		res = Dlg->GetResult();
		if(res==1) {
			Dlg->GetValue(102, width);		Dlg->GetValue(105, height);
			Dlg->GetValue(108, dpi);
			}
		}while (res < 0);
	bRet = (res == 1);
	CloseDlgWnd(hDlg);	delete Dlg;		free(ResDlg);
	return bRet;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Execute schemes dialog as owner drawn button
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static LineDEF Sch0_LINE = {.1, 1.0, 0x0L, 0x0L};
static LineDEF Sch3_LINE = {.1, 1.0, 0x0L, 0x0L};
static FillDEF Scheme0 = {FILL_NONE, 0x00ffffffL, 1.0, &Sch0_LINE, 0x00ffffffL};
static DWORD Scheme1[] = {0x000000ffL, 0x0000ff00L, 0x00ff0000L, 0x0000ffffL,
	0x00ff00ffL, 0x00ffff00L, 0x00000000L, 0x00ffffffL};
static DWORD Scheme2[] = {0x00ffffffL, 0x00e0e0e0L, 0x00c0c0c0L, 0x00808080L,
	0x000000L, 0x00808080L, 0x00c0c0c0L, 0x00e0e0e0L};
static FillDEF Scheme3[] = {
	{FILL_HLINES, 0x00ffffffL, 1.0, &Sch3_LINE}, {FILL_VLINES, 0x00ffffffL, 1.0, &Sch3_LINE, 0x00ffffffL},
	{FILL_HVCROSS, 0x00ffffffL, 1.0, &Sch3_LINE}, {FILL_DLINEU, 0x00ffffffL, 1.0, &Sch3_LINE, 0x00ffffffL},
	{FILL_DLINED, 0x00ffffffL, 1.0, &Sch3_LINE}, {FILL_DCROSS, 0x00ffffffL, 1.0, &Sch3_LINE, 0x00ffffffL},
	{FILL_BRICKH, 0x00ffffffL, 1.0, &Sch3_LINE}, {FILL_BRICKV, 0x00ffffffL, 1.0, &Sch3_LINE, 0x00ffffffL}};
static DlgInfo SchBase[] = {
	{0, 0, 5, CHECKED, GROUP, 0L, 0, 0, 0, 0},
	{5, 6, 0, 0x0L, RADIO1, (void*)"constant fill", 0, 0, 50, 8},
	{6, 7, 0, 0x0L, RADIO1, (void*)"colors 1", 0, 10, 50, 8},
	{7, 8, 0, 0x0L, RADIO1, (void*)"colors 2", 0, 28, 50, 8},
	{8, 10, 0, 0x0L, RADIO1, (void*)"hatches", 0, 46, 50, 8},
	{10, 20, 0, OWNDIALOG | TOUCHEXIT, FILLBUTTON, (void*)&Scheme0, 56, 0, 20, 8}};

static int CurrScheme = 1;
		
void OD_scheme(int cmd, void *par, RECT *rec, anyOutput *o,
		void *data, int id)
{
	int x = rec ? rec->left/xbase :0, y = rec ? rec->top/ybase:0;
	static DlgRoot *Dlg = 0L;
	POINT *mpos;
	MouseEvent mev;
	int i, j, k, res;
	static DlgInfo *ComSchDlg = 0L;

	switch(cmd) {
	case OD_CREATE:
		Dlg = 0L;
		//set line width: internationalization may be different
		Sch0_LINE.width = Sch3_LINE.width = defs.GetSize(SIZE_HAIRLINE);
		ComSchDlg = (DlgInfo*)calloc(40, sizeof(DlgInfo));
		if(ComSchDlg) {
			memcpy(ComSchDlg, SchBase, 6 * sizeof(DlgInfo));
			for (i = 1; i < 6; i++) {
				ComSchDlg[i].x += x;	ComSchDlg[i].y += y;
				}
			for (k = 20, j = 6; k < 50; k += 10) {
				for(i = 0; i < 8; i++) {
					ComSchDlg[j].id = i+k;				ComSchDlg[j].next = i+k+1;
					ComSchDlg[j].type = k < 40 ? COLBUTT : FILLBUTTON;		
					ComSchDlg[j].flags = OWNDIALOG | TOUCHEXIT;
					switch(k) {
					case 20:	ComSchDlg[j].ptype = (void *)&Scheme1[i];	break;
					case 30:	ComSchDlg[j].ptype = (void *)&Scheme2[i];	break;
					case 40:	ComSchDlg[j].ptype = (void *)&Scheme3[i];	break;
						}
					ComSchDlg[j].x = 5 + i*9 + x;		ComSchDlg[j].y = 18*(k-10)/10 + y;
					ComSchDlg[j].w = ComSchDlg[j].h = 8;
					j++;
					}
				ComSchDlg[j-1].next = k+10;
				}
			ComSchDlg[j-1].next = 0;	ComSchDlg[j-1].flags |= LASTOBJ;
			Dlg = new DlgRoot(ComSchDlg, 0L);
			}
		if(Dlg){
			if(CurrScheme < 4) Dlg->SetCheck(5+CurrScheme, 0L, true);
			mev.x =  mev.y = 0;			//activate RootDlg !
			mev.Action = MOUSE_LBDOWN;
			mev.StateFlags = 0L;
			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);	//fake
			}
		break;
	case OD_DELETE:
		if(Dlg) delete Dlg;
		Dlg = 0L;
		if(ComSchDlg) free(ComSchDlg);
		ComSchDlg = 0L;
		break;
	case OD_DRAWNORMAL:
	case OD_DRAWSELECTED:
		if(Dlg && o) Dlg->DoPlot(o);
		break;
	case OD_SELECT:
		mpos = (POINT*)data;
		mev.x = mpos->x;			mev.y = mpos->y;
		mev.Action = MOUSE_LBUP;
		mev.StateFlags = 0L;
		if(Dlg){
			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
			res = Dlg->GetResult();
			if(res >= 10) Dlg->SetCheck(4 + res/10, 0L, true);
			if(Dlg->GetCheck(5)) CurrScheme = 0;
			else if(Dlg->GetCheck(7)) CurrScheme = 2;
			else if(Dlg->GetCheck(8)) CurrScheme = 3;
			else CurrScheme = 1;
			for(i = 0; i < 8; i++) {
				Dlg->GetColor(i+20, &Scheme1[i]);
				Dlg->GetColor(i+30, &Scheme2[i]);
				}
			}
		break;
	case OD_MBTRACK:
		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
		break;
		}
}

FillDEF *GetSchemeFill(int *i)
{
	FillDEF curfill = {FILL_NONE, 0x00c0c0c0L, 1.0, 0L};
	static FillDEF RetFill;

	switch(CurrScheme) {
	case 0:
		memcpy(&RetFill, &Scheme0, sizeof(FillDEF));
		break;
	default:
	case 1:
		curfill.color = Scheme1[*i&0x07];
		memcpy(&RetFill, &curfill, sizeof(FillDEF));
		break;
	case 2:
		curfill.color = Scheme2[*i&0x07];
		memcpy(&RetFill, &curfill, sizeof(FillDEF));
		break;
	case 3:
		memcpy(&RetFill, &Scheme3[*i&0x07], sizeof(FillDEF));
		break;
		}
	*i += 1;
	return &RetFill;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Execute common line properties as owner drawn dialog
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static LineDEF EditLine = {0.0f, 6.0f, 0x0, 0x0}; 
static DlgInfo LinePropBase[] = {
	{100, 101, 0, 0x0L, RTEXT, (void*)"line width", 0, 0, 45, 8},
	{101, 102, 0, TOUCHEXIT, INCDECVAL1, &EditLine.width, 50, 0, 32, 10},
	{102, 103, 0, 0x0L, LTEXT, 0L, 84, 0, 20, 8},
	{103, 104, 0, 0x0L, RTEXT, (void*)"line color", 0, 12, 45, 8},
	{104, 105, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&EditLine.color, 50, 12, 25, 10},
	{105, 106, 0, 0x0L, LTEXT, (void*)"pattern:", 0, 24, 25, 8},
	{106, 107, 0, TOUCHEXIT, LINEPAT, (void *)&EditLine, 0, 34, 128, 4},
	{107, 108, 0, 0x0L, RTEXT, (void*)"pattern length", 0, 47, 45, 8},
	{108, 109, 0, TOUCHEXIT, INCDECVAL1, &EditLine.patlength, 50, 47, 32, 10},
	{109, 110, 0, 0x0L, LTEXT, 0L, 84, 47, 20, 8},
	{110, 0, 0, LASTOBJ | TOUCHEXIT, LINEBUTT, (void *)&EditLine, 0, 67, 128, 20}};

void OD_linedef(int cmd, void *par, RECT *rec, anyOutput *o,
		void *data, int id)
{
	int x = rec ? rec->left/xbase :0, y = rec ? rec->top/ybase:0;
	int i, res;
	POINT *mpos;
	MouseEvent mev;
	static DlgRoot *Dlg = 0L;
	static DlgInfo *LinePropDlg= 0L;

	switch(cmd) {
	case OD_CREATE:
		Dlg = 0L;
		LinePropDlg = (DlgInfo*)calloc(40, sizeof(DlgInfo));
		if(LinePropDlg) {
			memcpy(LinePropDlg, LinePropBase, 11 * sizeof(DlgInfo));
			LinePropDlg[2].ptype = LinePropDlg[9].ptype =
				(void *)Units[defs.cUnits].display;
			for (i = 0; i < 11; i++) {
				LinePropDlg[i].x += x;	LinePropDlg[i].y += y;
				}
			Dlg = new DlgRoot(LinePropDlg, 0L);
			}
		if(Dlg){
			Dlg->SetColor(104, EditLine.color);
			mev.x =  mev.y = 0;			//activate RootDlg !
			mev.Action = MOUSE_LBDOWN;
			mev.StateFlags = 0L;
			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);	//fake
			}
		break;
	case OD_DELETE:
		if(Dlg) delete Dlg;
		Dlg = 0L;
		if(LinePropDlg) free(LinePropDlg);
		LinePropDlg = 0L;
		break;
	case OD_DRAWNORMAL:
	case OD_DRAWSELECTED:
		if(Dlg && o) Dlg->DoPlot(o);
		break;
	case OD_SELECT:
		mpos = (POINT*)data;
		mev.x = mpos->x;			mev.y = mpos->y;
		mev.Action = MOUSE_LBUP;
		mev.StateFlags = 0L;
		if(Dlg){
			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
			res = Dlg->GetResult();
			switch (res) {
			case 101:						//line width changed
			case 108:						//pattern length changed
			case 110:						//preview button
				Dlg->GetValue(101, &EditLine.width);
				Dlg->GetValue(108, &EditLine.patlength);
			case 104:						//color button
				Dlg->GetColor(104, &EditLine.color);
			case 106:						//line pattern
				Dlg->DoPlot(0);
				break;
				}
			}
		break;
	case OD_MBTRACK:
		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
		break;
	case OD_SETLINE:
		if(data) {
			memcpy(&EditLine, data, sizeof(LineDEF));
			if(Dlg && LinePropDlg) Dlg->DoPlot(0);
			}
		break;
	case OD_GETLINE:
		if(Dlg) {
				Dlg->GetValue(101, &EditLine.width);
				Dlg->GetValue(108, &EditLine.patlength);
			}
		if(data) memcpy(data, &EditLine, sizeof(LineDEF));
		break;
		}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Execute common FILL properties as owner drawn button
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static LineDEF ODLine = {0.0f, 6.0f, 0x0, 0x0}; 
static LineDEF ODFillLine = {0.0f, 6.0f, 0x0, 0x0}; 
static FillDEF ODFill = {FILL_NONE, 0x00ffffffL, 1.0f, &ODFillLine, 0x00ffffffL}; 
static DlgInfo FillPropBase[] = {
	{100, 101, 0, 0x0L, RTEXT, (void*)"outline width", 0, 0, 40, 8},
	{101, 102, 0, 0x0L, INCDECVAL1, &ODLine.width, 42, 0, 32, 10},
	{102, 103, 0, 0x0L, LTEXT, 0L, 76, 0, 20, 8},
	{103, 104, 0, 0x0L, RTEXT, (void*)"outline color", 0, 12, 40, 8},
	{104, 105, 0, OWNDIALOG, COLBUTT, (void *)&ODLine.color, 42, 12, 25, 10},
	{105, 106, 0, 0x0L, RTEXT,(void*)"fill color" , 0, 24, 40, 8},
	{106, 107, 0, TOUCHEXIT | OWNDIALOG, COLBUTT, (void *)&ODFill.color, 42, 24, 25, 10},
	{107, 108, 0, 0x0L, RTEXT, (void*)"pattern", 0, 36, 40, 8},
	{108, 0, 0, LASTOBJ | TOUCHEXIT | OWNDIALOG, FILLBUTTON, (void*)&ODFill, 42, 36, 25, 10}};

void OD_filldef(int cmd, void *par, RECT *rec, anyOutput *o,
		void *data, int id)
{
	int x = rec ? rec->left/xbase :0, y = rec ? rec->top/ybase:0;
	int i, res;
	POINT *mpos;
	MouseEvent mev;
	static DlgRoot *Dlg = 0L;
	static DlgInfo *FillPropDlg= 0L;

	switch(cmd) {
	case OD_CREATE:
		Dlg = 0L;
		FillPropDlg = (DlgInfo*)calloc(9, sizeof(DlgInfo));
		if(FillPropDlg) {
			memcpy(FillPropDlg, FillPropBase, 9 * sizeof(DlgInfo));
			FillPropDlg[2].ptype = (void *) Units[defs.cUnits].display;
			for (i = 0; i < 9; i++) {
				FillPropDlg[i].x += x;	FillPropDlg[i].y += y;
				}
			Dlg = new DlgRoot(FillPropDlg, 0L);
			}
		if(Dlg){
			Dlg->SetColor(104, ODLine.color);
			Dlg->SetColor(106, ODFill.color);
			mev.x =  mev.y = 0;			//activate RootDlg !
			mev.Action = MOUSE_LBDOWN;
			mev.StateFlags = 0L;
			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);	//fake
			}
		break;
	case OD_DELETE:
		if(Dlg) delete Dlg;
		Dlg = 0L;
		if(FillPropDlg) free(FillPropDlg);
		FillPropDlg = 0L;
		break;
	case OD_DRAWNORMAL:
	case OD_DRAWSELECTED:
		if(Dlg && o) Dlg->DoPlot(o);
		break;
	case OD_SELECT:
		mpos = (POINT*)data;
		mev.x = mpos->x;			mev.y = mpos->y;
		mev.Action = MOUSE_LBUP;
		mev.StateFlags = 0L;
		if(Dlg){
			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
			res = Dlg->GetResult();
			switch (res) {
			case 106:					//fill color changed
				Dlg->GetColor(106, &ODFill.color);
				Dlg->DoPlot(NULL);
				break;
			case 108:					//copy color from pattern dialog
				Dlg->SetColor(106, ODFill.color);
				break;
				}
			}
		break;
	case OD_MBTRACK:
		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
		break;
	case OD_SETLINE:
		if(data) {
			memcpy(&ODLine, data, sizeof(LineDEF));
			if(Dlg && FillPropDlg) Dlg->DoPlot(0);
			}
		break;
	case OD_GETLINE:
		if(Dlg) {
			Dlg->GetValue(101, &ODLine.width);
			Dlg->GetColor(104, &ODLine.color);
			}
		if(data) memcpy(data, &ODLine, sizeof(LineDEF));
		break;
	case OD_SETFILL:
		if(data) {
			memcpy(&ODFill, data, sizeof(FillDEF));
			if(ODFill.hatch) memcpy(&ODFillLine, ((FillDEF*)data)->hatch, sizeof(LineDEF));
			ODFill.hatch = &ODFillLine;
			if(Dlg) Dlg->SetColor(106, ODFill.color);
			}
		break;
	case OD_GETFILL:
		Dlg->GetColor(106, &ODFill.color);
		if(data) memcpy(data, &ODFill, sizeof(FillDEF));
		break;
		}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Execute paper size properiteis as owner drawn button
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
typedef struct{
	char *name;
	double mwidth;		double mheight;
	double iwidth;		double iheight;
}paper;

static paper p_formats[] = {
	{"A0", 841, 1189, 33.11, 46.81},	{"A1", 594, 841, 23.39, 33.11},
	{"A2", 420, 594, 16.54, 23.39},		{"A3", 297, 420, 11.69, 16.54},
	{"A4", 210, 297, 8.27, 11.69},		{"A5", 148, 210, 5.83, 8.27},
	{"A6", 105, 148, 4.13, 5.83},		{"A7", 74, 105, 2.91, 4.13},
	{"B0", 1030, 1456, 40.55, 57.32},	{"B1", 728, 1030, 28.66, 40.55},
	{"B2", 515, 728, 20.28, 28.66},		{"B3", 364, 515, 14.33, 20.28},
	{"B4", 257, 364, 10.12, 14.33},		{"B5", 182, 257, 7.17, 10.12},
	{"B6", 128, 182, 5.04, 7.17},		{"B7", 91, 128, 3.58, 5.04},
	{"Executive", 191, 254, 7.52, 10},	{"Folio", 210, 330, 8.27, 12.99},
	{"Ledger", 432, 279, 17.01, 10.98},	{"Legal", 216, 356, 8.5, 14.02},
	{"Letter", 216, 279, 8.5, 10.98},
	{"Custom", 210, 297, 8.27, 11.69}};
static int cpformats = sizeof(p_formats)/sizeof(paper);
static double cu_width = 123.0, cu_height = 234.0;
static int pg_sel = 4;

static DlgInfo PaperDlg[] = {
	{100, 110, 0, 0x0L, NONE, (void*)0L, 0, 0, 0, 0},
	{110, 120, 0, TOUCHEXIT, LISTBOX1, (void*)0L, 0, 15, 100, 70},
	{120, 130, 0, NOEDIT, EDTEXT, (void*)"n.a.", 0, 0, 100, 10},
	{130, 0, 132, CHECKED, GROUP, 0, 0, 0, 0},
	{132, 133, 0, 0x0L, EDVAL1, (void*)&cu_width, 27, 0, 25, 10},
	{133, 134, 0, 0x0L, LTEXT, (void*)"x", 52, 0, 5, 8},
	{134, 135, 0, 0x0L, EDVAL1, (void*)&cu_height, 58, 0, 25, 10},
	{135, 0, 0, LASTOBJ, LTEXT, (void*)0L, 83, 0, 14, 8}};

void OD_paperdef(int cmd, void *par, RECT *rec, anyOutput *o,
		void *data, int id)
{
	int x = rec ? rec->left/xbase :0, y = rec ? rec->top/ybase:0;
	int i, res;
	POINT *mpos;
	MouseEvent mev;
	static DlgRoot *Dlg = 0L;
	static DlgInfo *PaperPropDlg= 0L;
	char **dispsize = 0L;
	double dtmp;

	switch(cmd) {
	case OD_CREATE:
		GetPaper(&cu_width, &cu_height);
		Dlg = 0L;
		if((PaperPropDlg = (DlgInfo*)calloc(8, sizeof(DlgInfo))) && 
			(dispsize = (char**)calloc(cpformats+1, sizeof(char*)))) {
			memcpy(PaperPropDlg, PaperDlg, 8 * sizeof(DlgInfo));
			for (i = 0; i < cpformats; i++) {
				if(i < cpformats -1) {
					switch(defs.cUnits) {
					case 1:
#ifdef USE_WIN_SECURE
						sprintf_s(TmpTxt, TMP_TXT_SIZE, " %s  (%.1lf x %.1lf cm)", p_formats[i].name, 
							p_formats[i].mwidth/10.0, p_formats[i].mheight/10.0);
#else
						sprintf(TmpTxt, " %s  (%.1lf x %.1lf cm)", p_formats[i].name, 
							p_formats[i].mwidth/10.0, p_formats[i].mheight/10.0);
#endif
						break;
					case 2:
#ifdef USE_WIN_SECURE
						sprintf_s(TmpTxt, TMP_TXT_SIZE, " %s  (%.2lf x %.2lf inch)", p_formats[i].name, 
							p_formats[i].iwidth, p_formats[i].iheight);
#else
						sprintf(TmpTxt, " %s  (%.2lf x %.2lf inch)", p_formats[i].name, 
							p_formats[i].iwidth, p_formats[i].iheight);
#endif
						break;
					default:
#ifdef USE_WIN_SECURE
						sprintf_s(TmpTxt, TMP_TXT_SIZE, " %s  (%.0lf x %.0lf mm)", p_formats[i].name, 
							p_formats[i].mwidth, p_formats[i].mheight);
#else
						sprintf(TmpTxt, " %s  (%.0lf x %.0lf mm)", p_formats[i].name, 
							p_formats[i].mwidth, p_formats[i].mheight);
#endif
						break;
						}
					dispsize[i] = (char*)memdup(TmpTxt, (int)strlen(TmpTxt)+1, 0);
					}
				else if(dispsize[i] = (char*)malloc(15*sizeof(char)))
					rlp_strcpy(dispsize[i], 15, " Custom");
				}
			PaperPropDlg[1].ptype = (void*)dispsize;
			PaperPropDlg[7].ptype = (void*)Units[defs.cUnits].display;
			if(pg_sel <(cpformats -1))PaperPropDlg[3].flags |= HIDDEN;
			for (i = 0; i < 8; i++) {
				PaperPropDlg[i].x += x;	PaperPropDlg[i].y += y;
				}
			if(Dlg = new DlgRoot(PaperPropDlg, 0L)){
				Dlg->Activate(120, false);
				if(Dlg->ItemCmd(110, CMD_FINDTEXT, (void*)dispsize[pg_sel])){
					if(pg_sel < (cpformats-1)) Dlg->SetText(120, dispsize[pg_sel]);
					else Dlg->SetText(120, dispsize[pg_sel]+1);
					dtmp = ((double)pg_sel)/((double) cpformats +10.0);
					Dlg->ItemCmd(110, CMD_SETSCROLL, (void*)&dtmp);
					}
				}
			}
		break;
	case OD_DELETE:
		if(Dlg) delete Dlg;
		Dlg = 0L;
		if(PaperPropDlg) free(PaperPropDlg);
		PaperPropDlg = 0L;
		if(dispsize) {
			for (i = 0; i < 20; i++) if(dispsize[i])free(dispsize[i]);
			free(dispsize);
			dispsize = 0L;
			}
		break;
	case OD_DRAWNORMAL:
	case OD_DRAWSELECTED:
		if(Dlg && o) Dlg->DoPlot(o);
		break;
	case OD_SELECT:
		mpos = (POINT*)data;
		mev.x = mpos->x;			mev.y = mpos->y;
		mev.Action = MOUSE_LBUP;
		mev.StateFlags = 0L;
		if(Dlg){
			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
			res = Dlg->GetResult();
			if(res == 110 && Dlg->GetText(110, TmpTxt, TMP_TXT_SIZE)){
				if(Dlg->GetInt(110, &i)) {
					if(i == cpformats-1){
						Dlg->ShowItem(130, true);
						Dlg->SetText(120, TmpTxt+1);
						}
					else {
						Dlg->ShowItem(130, false);
						Dlg->SetText(120, TmpTxt);
						}
					}
				Dlg->DoPlot(o);
				}
			}
		break;
	case OD_MBTRACK:
		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
		break;
	case OD_ACCEPT:
		if(Dlg) {
			if(Dlg->GetInt(110, &pg_sel) && pg_sel == (i=cpformats-1)){
				Dlg->GetValue(132, &cu_width);
				Dlg->GetValue(134, &cu_height);
				switch(defs.cUnits){
				case 1:
					p_formats[i].mwidth = cu_width*10.0;
					p_formats[i].mheight = cu_height*10.0;
					p_formats[i].iwidth = cu_width/2.54;
					p_formats[i].iheight = cu_height/2.54;
					break;
				case 2:
					p_formats[i].mwidth = cu_width*25.4;
					p_formats[i].mheight = cu_height*25.4;
					p_formats[i].iwidth = cu_width;
					p_formats[i].iheight = cu_height;
					break;
				default:
					p_formats[i].mwidth = cu_width;
					p_formats[i].mheight = cu_height;
					p_formats[i].iwidth = cu_width/25.4;
					p_formats[i].iheight = cu_height/25.4;
					break;
					}
				}
			}
		break;
		}
}

//Find a suitable paper size with width w and height h
void FindPaper(double w, double h, double tol)
{
	int i;
	double lw, hw, lh, hh;

	lw = w *(1.0-tol);		hw = w *(1.0+tol);
	lh = h *(1.0-tol);		hh = h *(1.0+tol);
	for(i = 0; i < cpformats; i++) {
		switch(defs.cUnits) {
		case 1:					//units are cm
			if(p_formats[i].mwidth >= lw*10.0 && p_formats[i].mwidth <= hw*10.0 &&
				p_formats[i].mheight >= lh*10.0 && p_formats[i].mheight <= hh*10.0){
					pg_sel = i;
					return;
				}
			break;
		case 2:					//units are inch
			if(p_formats[i].iwidth >= lw && p_formats[i].iwidth <= hw &&
				p_formats[i].iheight >= lh && p_formats[i].iheight <= hh){
					pg_sel = i;
					return;
				}
			break;
		default:				//units are mm
			if(p_formats[i].mwidth >= lw && p_formats[i].mwidth <= hw &&
				p_formats[i].mheight >= lh && p_formats[i].mheight <= hh){
					pg_sel = i;
					return;
				}
			break;
			}
		}
	//The paper format is non standard
	pg_sel = i = cpformats-1;
	switch(defs.cUnits){
	case 1:
		p_formats[i].mwidth = w*10.0;		p_formats[i].mheight = h*10.0;
		p_formats[i].iwidth = w/2.54;		p_formats[i].iheight = h/2.54;
		break;
	case 2:
		p_formats[i].mwidth = w*25.4;		p_formats[i].mheight = h*25.4;
		p_formats[i].iwidth = w;			p_formats[i].iheight = h;
		break;
	default:
		p_formats[i].mwidth = w;			p_formats[i].mheight = h;
		p_formats[i].iwidth = w/25.4;		p_formats[i].iheight = h/25.4;
		break;
		}
}

//Get (default) paper size
bool GetPaper(double *w, double *h)
{
	switch(defs.cUnits) {
		case 1:					//units are cm
			*w = p_formats[pg_sel].mwidth/10.0;
			*h = p_formats[pg_sel].mheight/10.0;
			break;
		case 2:					//units are inch
			*w = p_formats[pg_sel].iwidth;
			*h = p_formats[pg_sel].iheight;
			break;
		default:				//units are mm
			*w = p_formats[pg_sel].mwidth;
			*h = p_formats[pg_sel].mheight;
			break;
		}
	return true;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Select axis for plot as owner drawn button
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static DlgInfo PlotsDlg[] = {
	{100, 110, 0, 0x0L, NONE, (void*)0L, 0, 0, 0, 0},
	{110, 150, 0, TOUCHEXIT, LISTBOX1, (void*)0L, 20, 50, 100, 70},
	{150, 0, 0, LASTOBJ, LTEXT, (void*)"Apply this axis to plot:", 20, 35, 50, 9}};
static int axisplot_sel = 0;

void OD_axisplot(int cmd, void *par, RECT *rec, anyOutput *o,
		void *data, int id)
{
	static DlgInfo *PlotsPropDlg= 0L;
	static DlgRoot *Dlg = 0L;
	int i, res;
	POINT *mpos;
	MouseEvent mev;
	static char **names = 0L;

	switch(cmd) {
	case OD_CREATE:
		Dlg = 0L;
		if(PlotsPropDlg = (DlgInfo*)calloc(3, sizeof(DlgInfo))){
			memcpy(PlotsPropDlg, PlotsDlg, 3 * sizeof(DlgInfo));
			PlotsPropDlg[1].ptype = (void*)names;
			Dlg = new DlgRoot(PlotsPropDlg, 0L);
			}
		axisplot_sel = 0;
		break;
	case OD_DELETE:
		if(Dlg) delete Dlg;
		Dlg = 0L;
		if(PlotsPropDlg) free(PlotsPropDlg);
		PlotsPropDlg = 0L;
		break;
	case OD_DRAWNORMAL:
	case OD_DRAWSELECTED:
		if(Dlg && o) Dlg->DoPlot(o);
		break;
	case OD_SELECT:
		mpos = (POINT*)data;
		mev.x = mpos->x;			mev.y = mpos->y;
		mev.Action = MOUSE_LBUP;
		mev.StateFlags = 0L;
		if(Dlg){
			((Dialog*)par)->Command(CMD_CONTINUE, 0L, o);
			Dlg->Command(CMD_MOUSE_EVENT, (void *)&mev, o);
			res = Dlg->GetResult();
			if(res == 110 && Dlg->GetText(110, TmpTxt, TMP_TXT_SIZE)){
				if(Dlg->GetInt(110, &i)) {
					//get selection
					}
				Dlg->DoPlot(o);
				}
			Dlg->GetInt(110, &axisplot_sel);
			}
		break;
	case OD_MBTRACK:
		if(Dlg) Dlg->Command(CMD_MOUSE_EVENT, data, o);
		break;
	case OD_ACCEPT:
		if(data) names = (char**)data;
		else if(Dlg) Dlg->GetInt(110, &axisplot_sel);
		if(o) *((int*)o) = axisplot_sel;
		}
}
