//WinSpec.cpp, Copyright (c) 2000-2008 R.Lackner
//the entire code of this module is highly specific to Windows!
//
//    This file is part of RLPlot.
//
//    RLPlot is free software; you can redistribute it and/or modify
//    it under the terms of the GNU General Public License as published by
//    the Free Software Foundation; either version 2 of the License, or
//    (at your option) any later version.
//
//    RLPlot is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU General Public License for more details.
//
//    You should have received a copy of the GNU General Public License
//    along with RLPlot; if not, write to the Free Software
//    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
#include <stdio.h>
#include <math.h>
#include <fcntl.h>				//file open flags
#include <sys/stat.h>			//I/O flags
#include <io.h>					//for read/write
#include "rlplot.h"
#include "WinSpec.h"
#include "rlplot.rc"
#include "TheDialog.h"
#include "menu.h"

extern int dlgtxtheight;

HINSTANCE hInstance;
HWND MainWnd = 0L;
HACCEL accel;
extern tag_Units Units[];
extern GraphObj *CurrGO;			//Selected Graphic Objects
extern Graph *CurrGraph;
extern char *WWWbrowser;
extern char *LoadFile;
extern Default defs;
extern char TmpTxt[];
extern UndoObj Undo;

const char name[] = "RLPLOT1";

static unsigned int cf_rlpobj = RegisterClipboardFormat("rlp_obj");
static unsigned int cf_rlpxml = RegisterClipboardFormat("rlp_xml");
static char *ShellCmd;

long FAR PASCAL WndProc(HWND, UINT, UINT, LONG);
PrintWin *Printer = 0L;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// I/O File name dialogs
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get a new file name to store data in
char *SaveDataAsName(char *oldname)
{
	static char szFile[500], szFileTitle[256];
	static char szFilter[] = "RLPlot workbook (*.rlw)\0*.rlw\0data files (*.csv)\0*.csv\0tab separated (*.tsv)\0"
		"*.tsv\0XML (*.xml)\0*.xml\0";
	OPENFILENAME ofn;
	int i, j, cb;
	char *ext;

	szFile[0] = '\0';
	if(oldname)rlp_strcpy(szFile, 500, oldname);
	memset(&ofn, 0, sizeof(OPENFILENAME));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = GetFocus();
	ofn.lpstrFilter = szFilter;
	ofn.nFilterIndex = 1;
	ofn.lpstrFile = szFile;
	ofn.nMaxFile = sizeof(szFile);
	ofn.lpstrFileTitle = szFileTitle;
	ofn.nMaxFileTitle = sizeof(szFileTitle);
	ofn.lpstrInitialDir = defs.currPath;
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
	ofn.lpstrTitle = "Save Data As";

	if(GetSaveFileName(&ofn)){
		if(!(cb = (int)strlen(szFile)) || !szFile[0])return 0L;
		if(cb < 4 || szFile[cb-4] != '.'){
			for(i = j = 0; (j>>1) < (int)(ofn.nFilterIndex-1); i++) {
				if(szFilter[i] == '\0') j++;
				}
			ext = szFilter+i;
			for(i = 0; ext[i] && ext[i] != '*'; i++);
			rlp_strcpy(szFile+cb, 5, ext+i+1);
			}
		defs.FileHistory(szFile);
		return szFile;
		}
	else return 0L;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get a new file name to store graph
char *SaveGraphAsName(char *oldname)
{
	static char szFile[500], szFileTitle[256];
	static char szFilter[] = "RLPlot Graph (*.rlp)\0*.rlp\0";
	OPENFILENAME ofn;
	int i, j, cb;
	char *ext;

	szFile[0] = '\0';
	if(oldname)rlp_strcpy(szFile, 500, oldname);
	memset(&ofn, 0, sizeof(OPENFILENAME));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = GetFocus();
	ofn.lpstrFilter = szFilter;
	ofn.nFilterIndex = 1;
	ofn.lpstrFile = szFile;
	ofn.nMaxFile = sizeof(szFile);
	ofn.lpstrFileTitle = szFileTitle;
	ofn.nMaxFileTitle = sizeof(szFileTitle);
	ofn.lpstrInitialDir = defs.currPath;
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
	ofn.lpstrTitle = "Save Graph As";

	if(GetSaveFileName(&ofn)){
		if(!(cb = (int)strlen(szFile)) || !szFile[0])return 0L;
		if(cb < 4 || szFile[cb-4] != '.'){
			for(i = j = 0; (j>>1) < (int)(ofn.nFilterIndex-1); i++) {
				if(szFilter[i] == '\0') j++;
				}
			ext = szFilter+i;
			for(i = 0; ext[i] && ext[i] != '*'; i++);
			rlp_strcpy(szFile+cb, 5, ext+i+1);
			}
		defs.FileHistory(szFile);
		defs.FileHistory(szFile);
		return szFile;
		}
	else return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get file name to read graph
char *OpenGraphName(char *oldname)
{
	static char szFile[500], szFileTitle[256];
	static char szFilter[] = "RLPlot Graph (*.rlp)\0*.rlp\0";
	OPENFILENAME ofn;

	szFile[0] = '\0';
	if(oldname)rlp_strcpy(szFile, 500, oldname);
	memset(&ofn, 0, sizeof(OPENFILENAME));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = GetFocus();
	ofn.lpstrFilter = szFilter;
	ofn.nFilterIndex = 1;
	ofn.lpstrFile = szFile;
	ofn.nMaxFile = sizeof(szFile);
	ofn.lpstrFileTitle = szFileTitle;
	ofn.nMaxFileTitle = sizeof(szFileTitle);
	ofn.lpstrInitialDir = defs.currPath;
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	ofn.lpstrTitle = "Open Graph";

	if(GetOpenFileName(&ofn)){
		defs.FileHistory(szFile);
		return szFile;
		}
	else return NULL;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get a file name to load data
char *OpenDataName(char *oldname)
{
	static char szFile[500], szFileTitle[256];
	static char szFilter[] = "RLPlot workbook (*rlw)\0*.rlw\0data files (*.csv)\0*.csv\0"
		"tab separated file (*.tsv)\0*.tsv\0"
		"RLPlot Graph (*.rlp)\0*.rlp\0all files (*.*)\0*.*\0";
	OPENFILENAME ofn;

	szFile[0] = '\0';
	if(oldname)rlp_strcpy(szFile, 500, oldname);
	memset(&ofn, 0, sizeof(OPENFILENAME));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = GetFocus();
	ofn.lpstrFilter = szFilter;
	ofn.nFilterIndex = 1;
	ofn.lpstrFile = szFile;
	ofn.nMaxFile = sizeof(szFile);
	ofn.lpstrFileTitle = szFileTitle;
	ofn.nMaxFileTitle = sizeof(szFileTitle);
	ofn.lpstrInitialDir = defs.currPath;
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY;
	ofn.lpstrTitle = "Open Data File";

	if(GetOpenFileName(&ofn)){
		defs.FileHistory(szFile);
		return szFile;
		}
	else return NULL;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get a file name to export graph
void OpenExportName(GraphObj *g, char *oldname)
{
	static char szFile[500], szFileTitle[256];
	static char szFilter[] = "Scalable Vector Graphics (*.svg)\0*.svg\0"
		"Encapsulated Post Script (*.eps)\0*.eps\0"
		"Enhanced MetaFile(*.emf)\0*.emf\0Windows MetaFile(*.wmf)\0*.wmf\0"
		"Tag Image File Format (*.tif)\0*.tif\0";
	OPENFILENAME ofn;
	WinCopyWMF *wmf;
	int i;

	szFile[0] = '\0';
	if(!g) return;
	if(oldname)rlp_strcpy(szFile, 500, oldname);
	memset(&ofn, 0, sizeof(OPENFILENAME));
	ofn.lStructSize = sizeof(OPENFILENAME);
	ofn.hwndOwner = GetFocus();
	ofn.lpstrFilter = szFilter;
	ofn.nFilterIndex = 1;
	ofn.lpstrFile = szFile;
	ofn.nMaxFile = sizeof(szFile);
	ofn.lpstrFileTitle = szFileTitle;
	ofn.nMaxFileTitle = sizeof(szFileTitle);
	ofn.lpstrInitialDir = defs.currPath;
	ofn.Flags = OFN_PATHMUSTEXIST | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY;
	ofn.lpstrTitle = "Export Graph";

	if(g && GetSaveFileName(&ofn)){
		i = (int)strlen(szFile);
		g->Command(CMD_BUSY, 0L, 0L);
		if(0==_stricmp(".svg", szFile+i-4)) {
			DoExportSvg(g, szFile, 0L);
			}
		else if(0==_stricmp(".emf", szFile+i-4)) {
			wmf = new WinCopyWMF(g, 0L, szFile);
			if(wmf && wmf->StartPage()) {
				g->DoPlot(wmf);		wmf->EndPage(); 	delete wmf;
				}
			else if(wmf) delete wmf;	g->Command(CMD_REDRAW, 0L, 0L);
			}
		else if(0==_stricmp(".wmf", szFile+i-4)) {
			wmf = new WinCopyWMF(g, szFile, 0L);
			if(wmf && wmf->StartPage()) {
				g->DoPlot(wmf);		wmf->EndPage(); 	delete wmf;
				}
			else if(wmf) delete wmf;	g->Command(CMD_REDRAW, 0L, 0L);
			}
		else if(0==_stricmp(".eps", szFile+i-4)) {
			DoExportEps(g, szFile, 0L);
			}
		else if(0==_stricmp(".tif", szFile+i-4)) {
			DoExportTif(g, szFile, 0L);
			}
		else if(0==_stricmp(".tiff", szFile+i-5)) {
			DoExportTif(g, szFile, 0L);
			}
		else ErrorBox("Unknown file extension or format");
		g->Command(CMD_MOUSECURSOR, 0L, 0L);
		}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Common alert boxes
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void InfoBox(char *Msg)
{
	MessageBox(0, Msg, "Info", MB_OK | MB_ICONINFORMATION);
}

void ErrorBox(char *Msg)
{
	MessageBox(0, Msg, "ERROR", MB_OK | MB_ICONSTOP);
}

bool YesNoBox(char *Msg)
{
	if(IDYES == MessageBox(0, Msg, "RLPlot", MB_YESNO | MB_ICONQUESTION)) return true;
	return false;
}

int YesNoCancelBox(char *Msg)
{
	int res;

	res = MessageBox(0, Msg, "RLPlot", MB_YESNOCANCEL | MB_ICONQUESTION);
	switch(res) {
	case IDYES: return 1;
	case IDNO:	return 0;
	default:	return 2;
		}
	return 0;
}

void Qt_Box()
{
	MessageBox(0, "No Qt installed\nunder Windows", "Error", MB_OK | MB_ICONQUESTION);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Display blinking text cursor
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static anyOutput *oTxtCur = 0L, *oCopyMark = 0L;
RECT rTxtCur, rCopyMark;
static bool bTxtCur = false, bTxtCurIsVis = false, bSuspend = false;
static DWORD cTxtCur = 0x0L;
static HWND hwndTxtCur = 0L;
static int iTxtCurCount = 0;
static POINT ptTxtCurLine[2];
static BitMapWin *bmCopyMark = 0L;

void HideTextCursor()
{
	if(oTxtCur) {
		bTxtCur = false;
		oTxtCur->UpdateRect(&rTxtCur, false);
		}
	oTxtCur = 0L;
}

void HideTextCursorObj(anyOutput *out)
{
	if(oTxtCur && oTxtCur == out) HideTextCursor();
}

void ShowTextCursor(anyOutput *out, RECT *disp, DWORD color)
{
	HWND wnd = GetFocus();

	if(!out || (out->OC_type & 0xff) != OC_BITMAP) return;
	cTxtCur = color;			HideTextCursor();
	oTxtCur = out;				bSuspend = false;
	iTxtCurCount = -2;
	memcpy(&rTxtCur, disp, sizeof(RECT));
	ptTxtCurLine[0].x = rTxtCur.left;	ptTxtCurLine[0].y = rTxtCur.top;
	ptTxtCurLine[1].x = rTxtCur.right;	ptTxtCurLine[1].y = rTxtCur.bottom;
	rTxtCur.bottom++;		rTxtCur.right++;
	oTxtCur->ShowLine(ptTxtCurLine, 2, cTxtCur);
	bTxtCurIsVis = bTxtCur = true;
}

void HideCopyMark()
{
	BitMapWin *CurrCopyMark;

	if(bmCopyMark && oCopyMark) {
		CurrCopyMark = bmCopyMark;		bmCopyMark = 0L;
		oCopyMark->UpdateRect(&rCopyMark, false);
		delete CurrCopyMark;
		}
	bmCopyMark = 0L;	oCopyMark = 0L;
}

void ShowCopyMark(anyOutput *out, RECT *mrk, int nRec)
{
	int i;

	if(!out || (out->OC_type & 0xff) != OC_BITMAP) return;
	HideCopyMark();			bSuspend = false;
	if(!out || !mrk || !nRec) return;
	oCopyMark = out;
	rCopyMark.left = mrk[0].left;	rCopyMark.right = mrk[0].right;
	rCopyMark.top = mrk[0].top;		rCopyMark.bottom = mrk[0].bottom;
	for(i = 1; i < nRec; i++) {
		UpdateMinMaxRect(&rCopyMark, mrk[i].left, mrk[i].top);
		UpdateMinMaxRect(&rCopyMark, mrk[i].right, mrk[i].bottom);
		}
	bmCopyMark = new BitMapWin(rCopyMark.right - rCopyMark.left, 
		rCopyMark.bottom - rCopyMark.top, out->hres, out->vres);
}

void InvalidateOutput(anyOutput *o)
{
	if(!o || (o->OC_type & 0xff) != OC_BITMAP) return;
	if(o == oCopyMark) {
		oCopyMark = 0L;
		if(bmCopyMark) delete bmCopyMark;
		bmCopyMark = 0L;
		}
	if(o == oTxtCur) {
		oTxtCur = 0L;	bTxtCur = false;
		}
}

void SuspendAnimation(anyOutput *o, bool bSusp)
{
	if(!o || (o->OC_type & 0xff) != OC_BITMAP) return;
	if(!bSusp) bSuspend = false;
	else {
		if(o == oCopyMark) bSuspend = bSusp;
		if(o == oTxtCur) bSuspend = bSusp;
		}
}

static LineDEF liCopyMark1 = {0.0f, 1.0f, 0x00ffffffL, 0L};
static LineDEF liCopyMark2 = {0.0f, 6.0f, 0x0L, 0xf0f0f0f0L};

LRESULT FAR PASCAL TimerWndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
	static POINT line[5];
	static int cp_mark = 0;

	switch(message) {
	case WM_TIMER:
		if(bSuspend) return 0;
		if(bmCopyMark && oCopyMark && (oCopyMark->OC_type & 0xff) == OC_BITMAP) {
			bmCopyMark->CopyBitmap(0, 0, oCopyMark, rCopyMark.left, rCopyMark.top, 
				rCopyMark.right - rCopyMark.left, rCopyMark.bottom - rCopyMark.top, false);
			bmCopyMark->SetLine(&liCopyMark1);
			line[0].x = line[1].x = line[4].x = 0;
			line[0].y = line[3].y = line[4].y = 0;
			line[1].y = line[2].y = rCopyMark.bottom-rCopyMark.top-1;
			line[2].x = line[3].x = rCopyMark.right-rCopyMark.left-1;
			bmCopyMark->oPolyline(line, 5);
			bmCopyMark->SetLine(&liCopyMark2);
			bmCopyMark->RLP.finc = 1.0;		bmCopyMark->RLP.fp = (cp_mark & 0x7);
			bmCopyMark->oPolyline(line, 5);
			oCopyMark->ShowBitmap(rCopyMark.left, rCopyMark.top, bmCopyMark);
			cp_mark++;
			if(bTxtCurIsVis && oTxtCur && ptTxtCurLine[0].y != ptTxtCurLine[1].y &&
				oTxtCur == oCopyMark && OverlapRect(&rCopyMark, &rTxtCur)){
				oTxtCur->ShowLine(ptTxtCurLine, 2, cTxtCur);
				}
			}
		if(!oTxtCur || (ptTxtCurLine[0].x == ptTxtCurLine[1].x &&
			ptTxtCurLine[0].y == ptTxtCurLine[1].y)) return 0;
		iTxtCurCount++;
		if(iTxtCurCount<0) oTxtCur->ShowLine(ptTxtCurLine, 2, cTxtCur);
		if(iTxtCurCount < 4) return 0;
		iTxtCurCount = 0;
		if(bTxtCur && oTxtCur) {
			if(!bTxtCurIsVis) {
				oTxtCur->ShowLine(ptTxtCurLine, 2, cTxtCur);
				bTxtCurIsVis = true;
				}
			else {
				oTxtCur->UpdateRect(&rTxtCur, false);
				bTxtCurIsVis = false;
				}
			}
		return 0;
	case WM_QUERYOPEN:
	case WM_SIZE:	
		return 0;
	case WM_DESTROY:
		KillTimer(hwnd, 1);
		break;
		}
	return DefWindowProc(hwnd, message, wParam, lParam);
}

void InitTextCursor(bool init)
{
	WNDCLASS wndclass;

	if (init) {
		wndclass.style = CS_BYTEALIGNWINDOW | CS_DBLCLKS;
		wndclass.lpfnWndProc = TimerWndProc;
		wndclass.cbClsExtra = 0;
		wndclass.cbWndExtra = 0;
		wndclass.hInstance = hInstance;
		wndclass.hIcon = 0L;
		wndclass.hCursor = 0L;
		wndclass.hbrBackground = NULL;
		wndclass.lpszMenuName = 0L;
		wndclass.lpszClassName = "RLP_TIMER";
		RegisterClass(&wndclass);
		if((hwndTxtCur = CreateWindow("RLP_TIMER", 0L, WS_OVERLAPPEDWINDOW,
			0, 0, 0, 0, NULL, NULL, hInstance, NULL))){
			SetTimer(hwndTxtCur, 1, 150, 0L);
			}
	}
	else if(hwndTxtCur) {
		DestroyWindow(hwndTxtCur);
		hwndTxtCur = 0L;
		}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Process paste command: check for clipboard contents
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void TestClipboard(GraphObj *g)
{
	HANDLE hmem = 0;
	unsigned char *ptr;

	if(!g) return;
	OpenClipboard(MainWnd);
	if(g->Id == GO_SPREADDATA) {
		if((hmem = GetClipboardData(cf_rlpxml)) &&
			(ptr = (unsigned char*) GlobalLock(hmem))) g->Command(CMD_PASTE_XML, ptr, 0L);
		else if((hmem = GetClipboardData(CF_TEXT)) &&
			(ptr = (unsigned char*) GlobalLock(hmem))) ProcMemData(g, ptr, true);
		else if((hmem = GetClipboardData(cf_rlpobj)) &&
			(ptr = (unsigned char*) GlobalLock(hmem))) OpenGraph(g, 0L, ptr, true);
		}
	else if(g->Id == GO_PAGE || g->Id == GO_GRAPH) {
		if((hmem = GetClipboardData(cf_rlpobj)) &&
			(ptr = (unsigned char*) GlobalLock(hmem))) OpenGraph(g, 0L, ptr, true);
		}
	else TestClipboard(g->parent);
	if(hmem) GlobalUnlock(hmem);
	CloseClipboard();
}

void EmptyClip()
{
	HideCopyMark();
	OpenClipboard(MainWnd);
	EmptyClipboard();
	CloseClipboard();
}

void CopyText(char *txt, int len)
{
	HGLOBAL hmem;
	unsigned char* buf;

	if(!txt || !txt[0]) return;
	if(!len) len = (int)strlen(txt);
	OpenClipboard(MainWnd);
	EmptyClipboard();
	if(hmem = GlobalAlloc(GMEM_MOVEABLE, len+2)) {
		if(buf = (unsigned char *)GlobalLock(hmem)) {
			memcpy(buf, txt, len);	buf[len] = 0;
			GlobalUnlock(hmem);
			SetClipboardData(CF_TEXT, hmem);
			}
		}
	CloseClipboard();
}

unsigned char* PasteText()
{
	HANDLE hmem = 0;
	unsigned char *ptr, *ret=0L;
	
	OpenClipboard(MainWnd);
	if((hmem = GetClipboardData(CF_TEXT)) && (ptr = (unsigned char*) GlobalLock(hmem))){
		ret = (unsigned char*) _strdup((char*)ptr);
		}
	if(hmem) GlobalUnlock(hmem);
	CloseClipboard();
	return ret;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get display (desktop) size
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
void GetDesktopSize(int *width, int *height)
{
	RECT rc;

	GetClientRect(GetDesktopWindow(), &rc);
	*width = rc.right - rc.left;
	*height = rc.bottom - rc.top;
	if(*width < 800 || *height < 600){
		*width = 800;		*height = 600;
		}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Get long reference to pointer (win64-compatibility)
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static void **ptrs = 0L;
static size_t s_ptrs = 0;
static size_t n_ptrs = 1;

long lptrref(void* ptr)
{
	size_t i;

	if((n_ptrs +1) > s_ptrs){
		ptrs = (void **)realloc(ptrs, (s_ptrs += 1000) * sizeof(void*));
		}
	if(!ptrs) return 0L;
	for(i = 1; i < n_ptrs; i++){
		if(ptrs[i]== ptr) return (long)i;
		if(ptr && !ptrs[i]){
			ptrs[i] = ptr;
			return (long)i;
			}
		}
	//new pointer
	ptrs[n_ptrs++] = ptr;
	return (long)(n_ptrs-1);
}

void *reflptr(long ref)
{
	if(ref > 0 && ref < (long)n_ptrs && ptrs) return ptrs[ref];
	return 0L;
}

void noreflptr(long ref)
{
	if(ref > 0 && ref < (long)n_ptrs && ptrs) ptrs[ref]=0L;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Common code for any Windows output class
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
bool com_oTextOutW(int x, int y, w_char *txt, int cb, HFONT *hFont, HDC *dc, 
	TextDEF *td, anyOutput *o)
{
	XFORM xf;
	int ix, iy, w, h, dtflags, rx, ry;
	double si, csi;
	RECT dtrc;
	fRECT rc;
	TextDEF ttd;


	if(!*hFont || !txt || !txt[0]) return false;
	if(cb < 1) cb = (int)wcslen(txt);
	//test for transparency
	if(((td->ColTxt & 0xff000000L) || (td->ColBg & 0xff000000L)) && (o->OC_type & 0xff) == OC_BITMAP) {
		o->oGetTextExtentW(txt, cb, &rx, &ry);
		rx += 4;	rc.Xmin = -2.0;		rc.Ymin = 0.0;		rc.Xmax = rx;		rc.Ymax = ry;
		si = sin(td->RotBL *0.01745329252);	csi = cos(td->RotBL *0.01745329252);
		if(td->Align & TXA_HCENTER) {
			rc.Xmin -= rx/2.0-1.0;		rc.Xmax -= rx/2.0-1.0;
			}
		else if(td->Align & TXA_HRIGHT) {
			rc.Xmin -= rx-2.0;			rc.Xmax -= rx-2.0;
			}
		if(td->Align & TXA_VCENTER) {
			rc.Ymin -= ry/2.0;			rc.Ymax -= ry/2.0;
			}
		else if(td->Align & TXA_VBOTTOM) {
			rc.Ymin -= ry;				rc.Ymax -= ry;
			}
		SetMinMaxRect(&((BitMapWin*)o)->tr_rec, iround(rc.Xmin*csi + rc.Ymin*si)+x, iround(rc.Ymin*csi - rc.Xmin*si)+y,
			iround(rc.Xmax*csi + rc.Ymin*si)+x, iround(rc.Ymin*csi - rc.Xmax*si)+y);
		UpdateMinMaxRect(&((BitMapWin*)o)->tr_rec, iround(rc.Xmax*csi + rc.Ymax*si)+x, iround(rc.Ymax*csi - rc.Xmax*si)+y);
		UpdateMinMaxRect(&((BitMapWin*)o)->tr_rec, iround(rc.Xmin*csi + rc.Ymax*si)+x, iround(rc.Ymax*csi - rc.Xmin*si)+y);
		IncrementMinMaxRect(&((BitMapWin*)o)->tr_rec, (td->iSize>>1) +6);
		((BitMapWin*)o)->tr_out = GetRectBitmap(&((BitMapWin*)o)->tr_rec, o);
		((BitMapWin*)o)->tr_out->hres = ((BitMapWin*)o)->hres;
		((BitMapWin*)o)->tr_out->vres = ((BitMapWin*)o)->vres;
		memcpy(&ttd, td, sizeof(TextDEF));
		ttd.ColTxt = td->ColTxt & 0x00ffffffL;	ttd.ColBg = td->ColBg & 0x00ffffffL;
		((BitMapWin*)o)->tr_out->SetTextSpec(&ttd);
		((BitMapWin*)o)->tr_out->oTextOutW(x-((BitMapWin*)o)->tr_rec.left, y-((BitMapWin*)o)->tr_rec.top,
			txt, cb);
		((BitMapWin*)o)->DoTransparency(td->ColTxt);
		return true;
		}
	SelectObject(*dc, *hFont);					SetTextColor(*dc, (td->ColTxt)&0x00ffffffL);
	SetBkColor(*dc, (td->ColBg)&0x00ffffffL);	SetBkMode(*dc, td->Mode ? TRANSPARENT : OPAQUE);
	ix = iy = 0;								SetTextAlign(*dc, TA_LEFT | TA_TOP);
	if((o->OC_type & 0xff) == OC_HIMETRIC) {
		if(td->Style & TXS_SUB) {
			if((td->Align & TXA_VCENTER) == TXA_VCENTER) iy -= o->un2iy(td->fSize*0.4);
			else if((td->Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy -= o->un2iy(td->fSize*0.2);
			else if((td->Align & TXA_VTOP) == TXA_VTOP) iy -= o->un2iy(td->fSize*.6);
			}
		else if(td->Style & TXS_SUPER) {
			if((td->Align & TXA_VCENTER) == TXA_VCENTER) iy += o->un2iy(td->fSize*0.4);
			else if((td->Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy += o->un2iy(td->fSize*0.6);
			else if((td->Align & TXA_VTOP) == TXA_VTOP) iy -= o->un2iy(td->fSize*.0);
			}
		else {
			if((td->Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy += td->iSize;
			else if((td->Align & TXA_VCENTER) == TXA_VCENTER) iy += (td->iSize>>1);
			}
		}
	else {
		if(td->Style & TXS_SUB) {
			if((td->Align & TXA_VCENTER) == TXA_VCENTER) iy += o->un2iy(td->fSize*0.4);
			else if((td->Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy += o->un2iy(td->fSize*0.2);
			else if((td->Align & TXA_VTOP) == TXA_VTOP) iy += o->un2iy(td->fSize*.6);
			}
		else if(td->Style & TXS_SUPER) {
			if((td->Align & TXA_VCENTER) == TXA_VCENTER) iy -= o->un2iy(td->fSize*0.4);
			else if((td->Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy -= o->un2iy(td->fSize*0.6);
			else if((td->Align & TXA_VTOP) == TXA_VTOP) iy += o->un2iy(td->fSize*.0);
			}
		else {
			if((td->Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy -= td->iSize;
			else if((td->Align & TXA_VCENTER) == TXA_VCENTER) iy -= (td->iSize>>1);
			}
		}
	dtflags = DT_NOCLIP | DT_NOPREFIX;
	o->oGetTextExtentW(txt, cb, &w, &h);
	if((td->Align & TXA_HCENTER) == TXA_HCENTER) {
		dtrc.left = x+ix-(w>>1);	dtflags |= DT_CENTER;
		}
	else if((td->Align & TXA_HRIGHT) == TXA_HRIGHT) {
		dtrc.left = x+ix-w;			dtflags |= DT_RIGHT;
		}
	else {
		dtrc.left = x+ix;			dtflags |= DT_LEFT;
		}
	dtrc.top = iy + y;				dtrc.right = dtrc.left+w;		dtrc.bottom = dtrc.top+h;
	if(fabs(td->RotBL) >.01 || fabs(td->RotCHAR) >.01) {
		SetGraphicsMode(*dc, GM_ADVANCED);
		if((o->OC_type &0xff) == OC_HIMETRIC) {
			if((td->Align & TXA_VBOTTOM) == TXA_VBOTTOM) iy -= (td->iSize<<1);
			else if((td->Align & TXA_VCENTER) == TXA_VCENTER) iy -= (td->iSize);
			xf.eM11 = (float)cos(td->RotBL *0.01745329252);
			xf.eM12 = (float)sin(td->RotBL *0.01745329252);
			xf.eM22 = (float)-cos(td->RotBL *0.01745329252);
			xf.eM21 = xf.eM12;
			}
		else {
			xf.eM11 = xf.eM22 = (float)cos(td->RotBL *0.01745329252);
			xf.eM12 = (float)-sin(td->RotBL *0.01745329252);
			xf.eM21 = -xf.eM12;
			}
		xf.eDx = (float)x;		xf.eDy = (float)y;			SetWorldTransform(*dc, &xf);
		dtrc.left -= x;		dtrc.right -= x;
		dtrc.top = iy;		dtrc.bottom = dtrc.top + h;
		DrawTextW(*dc, txt, cb, &dtrc, dtflags);
		ModifyWorldTransform(*dc, &xf, MWT_IDENTITY);
		SetGraphicsMode(*dc, GM_COMPATIBLE);
		return true;
		}
	else {
		DrawTextW(*dc, txt, cb, &dtrc, DT_CENTER | DT_NOCLIP | DT_NOPREFIX);
		return true;
		}
	return false;
}

bool com_oTextOut(int x, int y, char *atxt, int cb, HFONT *hFont, HDC *dc, TextDEF *td, anyOutput *o)
{
	unsigned char *utxt = (unsigned char*)atxt;
	w_char *uc;
	int i;
	bool bRet;

	if(!*hFont || !atxt || !atxt[0]) return false;
	if(cb < 1) cb = (int)strlen(atxt);
	if(!(uc=(WCHAR *)calloc(cb+1, sizeof(WCHAR)))) return false;
	if(td->Font==FONT_GREEK) {
		for(i = 0; utxt[i]; i++) {
			if((utxt[i] >= 'A' && utxt[i] <= 'Z')) uc[i] = utxt[i] - 'A' + 0x391;
			else if((utxt[i] >= 'a' && utxt[i] <= 'z')) uc[i] = utxt[i] - 'a' + 0x3B1;
			else uc[i] = utxt[i];
			}
		}
	else for(i = 0; utxt[i]; i++) uc[i] = utxt[i];
	bRet = com_oTextOutW(x, y, uc, cb, hFont, dc, td, o);
	free(uc);		return bRet;
}

bool com_SetTextSpec(TextDEF *set, anyOutput *o, HFONT *hFont,	TextDEF *TxtSet, 
	HDC *dc)
{	
	bool IsModified, RetVal;
	LOGFONT FontRec;
	HFONT newFont;

	if(!set->iSize && set->fSize > 0.001) set->iSize = o->un2iy(set->fSize);
	if(!set->iSize) return false;
	if(!*hFont || TxtSet->iSize != set->iSize || TxtSet->Style != set->Style ||
		TxtSet->RotBL != set->RotBL || TxtSet->RotCHAR != set->RotCHAR ||
		TxtSet->Font != set->Font || TxtSet->fSize != set->fSize) IsModified = true;
	else IsModified = false;
	RetVal = o->anyOutput::SetTextSpec(set);
	if (IsModified && RetVal) {
		// create font
		if((TxtSet->Style & TXS_SUPER) || (TxtSet->Style & TXS_SUB))
			FontRec.lfHeight = o->un2iy(set->fSize*0.71);
		else FontRec.lfHeight = TxtSet->iSize;
		if(FontRec.lfHeight <2) FontRec.lfHeight = 2;
		FontRec.lfWidth = 0;
		FontRec.lfEscapement = 0;		//text angle
		FontRec.lfOrientation = 0;		//base line angle
		FontRec.lfWeight = (TxtSet->Style & TXS_BOLD) ? FW_BOLD : FW_NORMAL;
		FontRec.lfItalic = (TxtSet->Style & TXS_ITALIC) ? TRUE : FALSE;
		FontRec.lfUnderline = (TxtSet->Style & TXS_UNDERLINE) ? TRUE : FALSE;
		FontRec.lfStrikeOut = 0;
		FontRec.lfOutPrecision = OUT_DEFAULT_PRECIS;
		FontRec.lfClipPrecision = CLIP_DEFAULT_PRECIS;
		FontRec.lfQuality = PROOF_QUALITY;
		switch(TxtSet->Font){
		case FONT_HELVETICA:
		default:
			FontRec.lfCharSet = ANSI_CHARSET;
			FontRec.lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS;
			rlp_strcpy(FontRec.lfFaceName, 32, "Arial");
			break;
		case FONT_GREEK:		case FONT_TIMES:
			FontRec.lfCharSet = ANSI_CHARSET;
			FontRec.lfPitchAndFamily = VARIABLE_PITCH | FF_ROMAN;
			rlp_strcpy(FontRec.lfFaceName, 32, "Times New Roman");
			break;
		case FONT_COURIER:
			FontRec.lfCharSet = ANSI_CHARSET;
			FontRec.lfPitchAndFamily = FIXED_PITCH | FF_MODERN;
			rlp_strcpy(FontRec.lfFaceName, 32, "Courier New");
			break;
			}
		newFont = CreateFontIndirect(&FontRec);
		SelectObject(*dc, newFont);
		if(*hFont)DeleteObject(*hFont);
		*hFont = newFont;
		if(!(*hFont)) return false;
		}
	return RetVal;
}

bool com_oGetTextExtentW(w_char *text, int cb, int *width, int *height, HDC dc, TextDEF *TxtSet)
{
	SIZE TextExtent;
	double si, csi, d;

	if(!text || !TxtSet) return false;
	if(!GetTextExtentPoint32W(dc, text, cb ? cb : (int)wcslen(text), &TextExtent))return false;
	if(fabs(TxtSet->RotBL) >0.01) {
		si = fabs(sin(TxtSet->RotBL * 0.01745329252));	csi = fabs(cos(TxtSet->RotBL * 0.01745329252));
		d = si > csi ? 1.0/si : 1.0/csi;
		d = (TextExtent.cx * ((7.0 + d)/8.0));
		TextExtent.cx = iround(d);
		}
	*width = TextExtent.cx;					*height = TextExtent.cy;
	return true;
}

bool com_oGetTextExtent(char *text, int cb, int *width, int *height, HDC dc, TextDEF *TxtSet)
{
	int i;
	unsigned char *utext;
	w_char *uc;
	bool bRet;

	if(!text || !TxtSet) return false;
	if(!cb) cb = (int)strlen(text);
	if(!(uc = (w_char *) malloc((cb+1)*sizeof(w_char))))return false;
	for(i = 0, utext = (unsigned char*)text ; i <= cb; i++) uc[i] = utext[i];
	bRet = com_oGetTextExtentW(uc, cb, width, height, dc, TxtSet);
	free(uc);	
	return bRet;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Output to windows bitmap
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
BitMapWin::BitMapWin(GraphObj *g, HWND hw):anyOutput()
{
	HDC dc;
	HWND hwndDesk;

	memDC = 0L;		hgo = 0L;		go = g;
	dc = GetDC(hwndDesk = GetDesktopWindow());
	hres = (double)GetDeviceCaps(dc, LOGPIXELSX);
	vres = (double)GetDeviceCaps(dc, LOGPIXELSY);
	if(hw) GetClientRect(hw, &DeskRect);
	else GetClientRect(hwndDesk, &DeskRect);
	Box1.Xmin = DeskRect.left;		Box1.Xmax = DeskRect.right;
	Box1.Ymin = DeskRect.top;		Box1.Ymax = DeskRect.bottom;
	scr = CreateCompatibleBitmap(dc, DeskRect.right, DeskRect.bottom);
	memDC = CreateCompatibleDC(NULL);
	SelectObject(memDC, scr);		ReleaseDC(hwndDesk, dc);
	hPen = CreatePen(PS_SOLID, 1, dLineCol = 0x00ffffffL);
	hBrush = CreateSolidBrush(dFillCol =dBgCol = 0x00ffffffL);
	dPattern = 0L;				minLW = 1;
	if(memDC) {
		oldPen = (HPEN)SelectObject(memDC, hPen);
		oldBrush = (HBRUSH)SelectObject(memDC, hBrush);
		}
	else {
		oldBrush = 0L;
		oldPen = 0L;
		}
	hFont = 0L;	OC_type = OC_BITMAP;
}

BitMapWin::BitMapWin(int w, int h, double hr, double vr)
{
	HDC dc;
	HWND hwndDesk;

	memDC = 0L;		hgo = 0L;		go = 0L;
	hres = hr;		vres = vr;		minLW = 1;
	units = defs.cUnits;
	DeskRect.right = w;				DeskRect.bottom = h;
	DeskRect.left = DeskRect.top = 0;
	VPorg.fx = VPorg.fy = 0.0;
	dc = GetDC(hwndDesk = GetDesktopWindow());
	scr = CreateCompatibleBitmap(dc, DeskRect.right, DeskRect.bottom);
	memDC = CreateCompatibleDC(NULL);
	ReleaseDC(hwndDesk, dc);
	SelectObject(memDC, scr);
	hPen = CreatePen(PS_SOLID, 1, dLineCol = 0x00ffffffL);
	hBrush = CreateSolidBrush(dFillCol =dBgCol = 0x00ffffffL);
	dPattern = 0L;
	if(memDC) {
		SelectObject(memDC, hPen);
		SelectObject(memDC, hBrush);
		}
	hFont = 0L;				OC_type = OC_BITMAP;
}

BitMapWin::BitMapWin(GraphObj *g):anyOutput()
{
	HDC dc;
	HWND hwndDesk;

	memDC = 0L;		hgo = 0L;		go = g;
	dc = GetDC(hwndDesk = GetDesktopWindow());
	hres = vres = 300.0;	units = defs.cUnits;	minLW = 1;
	DeskRect.right = un2ix(go->GetSize(SIZE_GRECT_RIGHT) - go->GetSize(SIZE_GRECT_LEFT));
	DeskRect.bottom = un2iy(go->GetSize(SIZE_GRECT_BOTTOM) - go->GetSize(SIZE_GRECT_TOP));
	DeskRect.top = DeskRect.left = 0;
	VPorg.fy = -co2fiy(go->GetSize(SIZE_GRECT_TOP));
	VPorg.fx = -co2fix(go->GetSize(SIZE_GRECT_LEFT));
	scr = CreateCompatibleBitmap(dc, DeskRect.right, DeskRect.bottom);
	memDC = CreateCompatibleDC(NULL);
	SelectObject(memDC, scr);
	ReleaseDC(hwndDesk, dc);
	hPen = CreatePen(PS_SOLID, 1, dLineCol = 0x00ffffffL);
	hBrush = CreateSolidBrush(dFillCol =dBgCol = 0x00ffffffL);
	dPattern = 0L;
	if(memDC) {
		SelectObject(memDC, hPen);
		SelectObject(memDC, hBrush);
		}
	hFont = 0L;		OC_type = OC_BITMAP;
}

BitMapWin::~BitMapWin()
{
	Undo.KillDisp(this);			if(hgo) delete hgo;
	if(hFont) DeleteObject(hFont);		if(scr) DeleteObject(scr);
	SelectObject(memDC, oldPen);		SelectObject(memDC, oldBrush);
	if(memDC) DeleteDC(memDC);		if(hPen) DeleteObject(hPen);
	if(hBrush) DeleteObject(hBrush);
	hgo = 0L;	hFont = 0L;	scr = 0L;	
	memDC = 0L;	hPen = 0L;	hBrush = 0L;
}

bool
BitMapWin::SetLine(LineDEF *lDef)
{
	int iw;
	HPEN newPen;

	if(!hPen || lDef->width != LineWidth || lDef->width != LineWidth || 
		lDef->pattern != dPattern || lDef->color != dLineCol) {
		LineWidth = lDef->width;
		iw = iround(un2fix(lDef->width));
		dPattern = lDef->pattern;
		RLP.finc = 256.0/un2fix(lDef->patlength*8.0);
		RLP.fp = 0.0;
		if(iLine == iw && dLineCol == lDef->color && hPen) return true;
		iLine = iw;
		dLineCol = lDef->color;
		newPen = CreatePen(PS_SOLID, iw > 0 ? iw : 1, dLineCol);
		SelectObject(memDC, newPen);
		if(hPen) DeleteObject(hPen);
		hPen = newPen;
		}
	return true;
}

bool
BitMapWin::SetFill(FillDEF *fill)
{
	HBRUSH newBrush;

	if(!fill) return false;
	if((fill->type & 0xff) != FILL_NONE) {
		if(!hgo) hgo = new HatchOut(this);
		if(hgo) hgo->SetFill(fill);
		}
	else {
		if(hgo) delete hgo;
		hgo = 0L;
		}
	if(dFillCol != fill->color) {
		newBrush = CreateSolidBrush(dFillCol = fill->color);
		SelectObject(memDC, newBrush);
		if(hBrush) DeleteObject(hBrush);
		hBrush = newBrush;
		}
	dFillCol = fill->color;
	dFillCol2 = fill->color2;
	return true;
}

bool
BitMapWin::SetTextSpec(TextDEF *set)
{
	return com_SetTextSpec(set, this, &hFont, &TxtSet, &memDC);
}

bool
BitMapWin::Erase(DWORD Color)
{
	HPEN hBGpen, hOldPen;
	HBRUSH hBGbrush, hOldBrush;

	hBGpen = CreatePen(PS_SOLID, 1, Color);
	hBGbrush = CreateSolidBrush(Color);
	if(hBGpen && memDC) {
		if(hBGbrush) {
			hOldBrush = (HBRUSH)SelectObject(memDC, hBGbrush);
			hOldPen = (HPEN)SelectObject(memDC, hBGpen);
			Rectangle(memDC, 0, 0, DeskRect.right, DeskRect.bottom);
			SelectObject(memDC, hOldBrush);		SelectObject(memDC, hOldPen);
			DeleteObject(hBGbrush);			DeleteObject(hBGpen);
			return true;
			}
		if(hBGpen) DeleteObject(hBGpen);
		}
	return false;
}

bool
BitMapWin::CopyBitmap(int x, int y, anyOutput* sr, int sx, int sy,
	int sw, int sh, bool invert)
{
	BitMapWin *src = (BitMapWin*)sr;

	return(0 != BitBlt(memDC, x, y, sw, sh, src->memDC, sx, sy, 
		invert ? DSTINVERT : SRCCOPY));
}

bool
BitMapWin::oGetTextExtent(char *text, int cb, int *width, int *height)
{
	return com_oGetTextExtent(text, cb, width, height, memDC, &TxtSet);
}

bool
BitMapWin::oGetTextExtentW(w_char *text, int cb, int *width, int *height)
{
	return com_oGetTextExtentW(text, cb, width, height, memDC, &TxtSet);
}

bool
BitMapWin::oGetPix(int x, int y, DWORD *col)
{
	DWORD pix;
	
	if(x >= DeskRect.left && x < DeskRect.right &&
		y >= DeskRect.top && y < DeskRect.bottom) {
		pix = GetPixel(memDC, x, y);
		*col = pix;
		return true;
		}
	else return false;
}

bool
BitMapWin::oDrawIcon(int type, int x, int y)
{
	HICON icon = 0L;
	
	switch(type) {
	case ICO_INFO:
		icon = LoadIcon(0L, IDI_ASTERISK);
		break;
	case ICO_ERROR:
		icon = LoadIcon(0L, IDI_HAND);
		break;
	case ICO_RLPLOT:
		icon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_EVAL));
		break;
		}
	if(icon){
		DrawIcon(memDC, x, y, icon);
		return true;
		}
	return false;
}

bool
BitMapWin::oCircle(int x1, int y1, int x2, int y2, char *nam)
{
	BOOL RetVal;
	FillDEF tr_fill = {FILL_NONE, 0x0, 1.0, 0L, 0x0};
	LineDEF tr_line = {0.0, 1.0, 0x0, 0x0};
	bool bTrans = false;
	HPEN newPen;
	

	if(dFillCol & 0xff000000) {
		bTrans = true;
		tr_fill.color = tr_fill.color2 = tr_line.color = (dFillCol & 0x00ffffffL);
		SetMinMaxRect(&tr_rec, x1, y1, x2, y2);
		IncrementMinMaxRect(&tr_rec, 6);
		tr_out = GetRectBitmap(&tr_rec, this);
		tr_out->hres = hres;			tr_out->vres = vres;
		tr_out->SetLine(&tr_line);			tr_out->SetFill(&tr_fill);
		RetVal = tr_out->oCircle(x1-tr_rec.left, y1-tr_rec.top, x2-tr_rec.left, y2-tr_rec.top, nam);
		DoTransparency(dFillCol);
		if(!(dLineCol & 0xff000000)) Arc(memDC, x1, y1,	x2, y2, 0, 0, 0, 0);
		}
	if(dLineCol & 0xff000000) {
		if(!bTrans) {
			newPen = CreatePen(PS_SOLID, 1, dFillCol);
			SelectObject(memDC, newPen);			Ellipse(memDC, x1, y1, x2, y2);
			if(hPen) {
				SelectObject(memDC, hPen);			DeleteObject(newPen);
				}
			}
		bTrans = true;
		tr_line.color = (dLineCol & 0x00ffffffL);
		tr_line.width = LineWidth;
		tr_line.pattern = dPattern;
		SetMinMaxRect(&tr_rec, x1, y1, x2, y2);
		IncrementMinMaxRect(&tr_rec, 6 + un2ix(LineWidth*2.0));
		tr_out = GetRectBitmap(&tr_rec, this);
		tr_out->hres = hres;			tr_out->vres = vres;
		tr_out->SetLine(&tr_line);
		Arc(((BitMapWin*)tr_out)->memDC, x1-tr_rec.left, y1-tr_rec.top,
			x2-tr_rec.left, y2-tr_rec.top, 0, 0, 0, 0);
		DoTransparency(dLineCol);
		}
	if(!bTrans) {
		RetVal = Ellipse(memDC, x1, y1, x2, y2);
		if(RetVal && hgo) return hgo->oCircle(x1, y1, x2, y2);
		else if(RetVal) return true;
		}
	return true;
}

bool
BitMapWin::oPolyline(POINT * pts, int cp, char *nam)
{
	int i;
	BOOL RetVal;
	POINT *newpts;
	LineDEF tr_line = {0.0, 1.0, 0x0, 0x0};

	if(!pts || cp < 1) return false;
	if((dLineCol & 0xff000000) && (newpts = (POINT*)malloc(cp * sizeof(POINT)))) {
		tr_line.color = (dLineCol & 0x00ffffffL);
		tr_line.width = LineWidth;
		tr_line.pattern = dPattern;
		SetMinMaxRect(&tr_rec, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
		for(i = 2; i < cp; i++) {
			UpdateMinMaxRect(&tr_rec, pts[i].x, pts[i].y);
			}
		IncrementMinMaxRect(&tr_rec, 6 + un2ix(LineWidth*2.0));
		tr_out = GetRectBitmap(&tr_rec, this);
		tr_out->RLP.finc = RLP.finc;	tr_out->RLP.fp = RLP.fp;
		tr_out->hres = hres;			tr_out->vres = vres;
		for(i = 0; i < cp; i++) {
			newpts[i].x = pts[i].x - tr_rec.left;
			newpts[i].y = pts[i].y - tr_rec.top;
			}
		tr_out->SetLine(&tr_line);
		RetVal = tr_out->oPolyline(newpts, cp, 0L);
		RLP.finc = tr_out->RLP.finc;	RLP.fp = tr_out->RLP.fp;
		DoTransparency(dLineCol);		free(newpts);
		return (RetVal != 0);
		}
	else {
		if (dPattern) {
			for (i = 1; i < cp; i++) PatLine(pts[i-1], pts[i]);
			return true;
			}
		else RetVal = Polyline(memDC, pts, cp);
		}
	return false;
}

bool
BitMapWin::oRectangle(int x1, int y1, int x2, int y2, char *nam)
{
	POINT pts[5];

	pts[0].x = pts[3].x = pts[4].x = x1;	pts[0].y = pts[1].y = pts[4].y = y1;
	pts[1].x = pts[2].x = x2;		pts[2].y = pts[3].y = y2;
	return oPolygon(pts, 5, nam);
}

bool
BitMapWin::oSolidLine(POINT *p)
{
	DWORD dPat;

	if(dLineCol & 0xff000000L) {
		dPat = dPattern;		dPattern = 0L;
		oPolyline(p, 2, 0L);		dPattern = dPat;
		return true;
		}
	else {
		if(Polyline(memDC, p, 2)) return true;
		}
	return false;
}

bool
BitMapWin::oTextOut(int x, int y, char *txt, int cb)
{
	return com_oTextOut(x, y, txt, cb, &hFont, &memDC, &TxtSet, this);
}

bool
BitMapWin::oTextOutW(int x, int y, w_char *txt, int cb)
{
	return com_oTextOutW(x, y, txt, cb, &hFont, &memDC, &TxtSet, this);
}

bool
BitMapWin::oPolygon(POINT *pts, int cp, char *nam)
{
	int i;
	BOOL RetVal;
	POINT *newpts;
	FillDEF tr_fill = {FILL_NONE, 0x0, 1.0, 0L, 0x0};
	LineDEF tr_line = {0.0, 1.0, 0x0, 0x0};
	HPEN newPen;

	if(!pts || cp < 2) return false;
	if((dFillCol & 0xff000000) && (newpts = (POINT*)malloc(cp * sizeof(POINT)))) {
		tr_fill.color = tr_fill.color2 = tr_line.color = (dFillCol & 0x00ffffffL);
		SetMinMaxRect(&tr_rec, pts[0].x, pts[0].y, pts[1].x, pts[1].y);
		for(i = 2; i < cp; i++) {
			UpdateMinMaxRect(&tr_rec, pts[i].x, pts[i].y);
			}
		IncrementMinMaxRect(&tr_rec, 6);
		tr_out = GetRectBitmap(&tr_rec, this);
		tr_out->hres = hres;			tr_out->vres = vres;
		for(i = 0; i < cp; i++) {
			newpts[i].x = pts[i].x - tr_rec.left;
			newpts[i].y = pts[i].y - tr_rec.top;
			}
		tr_out->SetLine(&tr_line);		tr_out->SetFill(&tr_fill);
		RetVal = tr_out->oPolygon(newpts, cp, 0L);
		DoTransparency(dFillCol);		free(newpts);
		oPolyline(pts, cp, nam);
		}
	else if((dLineCol & 0xff000000) && (newpts = (POINT*)malloc(cp * sizeof(POINT)))) {
		newPen = CreatePen(PS_SOLID, 1, dFillCol);
		SelectObject(memDC, newPen);
		RetVal = Polygon(memDC, pts, cp);
		if(hPen) {
			SelectObject(memDC, hPen);	oPolyline(pts, cp, nam);
			DeleteObject(newPen);
			}
		}
	else {
		RetVal = Polygon(memDC, pts, cp);
		}
	if(RetVal && hgo) return hgo->oPolygon(pts, cp);
	return RetVal != 0;
}

// The following code does alpha blending for transparent colors.
//    The Windows AlphaBlend function requires msimg32.dll. To include
//    this library add MSIMG32.LIB to the Project/Settings.../Link/Library_Moduls list.
//    The code executed with USE_MSIMG32 undefined is very slow because of the GetPixel
//    and SetPixel functions.
//
#define USE_MSIMG32
void
BitMapWin::DoTransparency(DWORD color)
{
#ifdef USE_MSIMG32
	BLENDFUNCTION bf = {AC_SRC_OVER, 0, 127, 0};

	bf.SourceConstantAlpha = (unsigned char)(255 - ((color >> 24) & 0xff));
	AlphaBlend(memDC, tr_rec.left, tr_rec.top, tr_rec.right - tr_rec.left, tr_rec.bottom - tr_rec.top,
		((BitMapWin*)tr_out)->memDC, 0, 0, tr_rec.right - tr_rec.left, tr_rec.bottom - tr_rec.top, bf);
#else
	int x1, y1, x2, y2, c, c1, c2;
	DWORD col1, col2, col;
	double f, f1;
	
	if(!tr_out) return;
	f = ((color & 0xff000000L) >>24)/255.0;		f1 = 1.0 - f;
	for(y1 = tr_rec.top, y2 = 0; y1 < tr_rec.bottom; y1++, y2++) {
		for(x1 = tr_rec.left, x2 = 0; x1 < tr_rec.right; x1++, x2++) {
			col1 = GetPixel(memDC, x1, y1);
			col2 = GetPixel(((BitMapWin*)tr_out)->memDC, x2, y2);
			if(col1 != col2) {
				col = 0x0;
				c1 = (col1 & 0x000000ffL);	c = c2 = (col2 & 0x000000ffL);
				if(c1 != c2) c = (int)(c2 * f1 + c1 * f);
				col |= (c < 256 ? c : 0xff);
				c1 = ((col1 & 0x0000ff00L)>>8);	c = c2 = ((col2 & 0x0000ff00L)>>8);
				if(c1 != c2) c = (int)(c2 * f1 + c1 * f);
				col |= (c < 256 ? (c<<8) : 0x00ff00);
				c1 = ((col1 & 0x00ff0000L)>>16);	c = c2 = ((col2 & 0x00ff0000L)>>16);
				if(c1 != c2) c = (int)(c2 * f1 + c1 * f);
				col |= (c < 256 ? (c<<16) : 0xff0000);
				SetPixel(memDC, x1, y1, col);
				}
			}
		}
#endif	//USE_MSIMG32
	DelBitmapClass(tr_out);			tr_out = 0L;
	OC_type |= OC_TRANSPARENT;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Output to windows window
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
OutputWin::OutputWin(GraphObj *g, HWND hw):BitMapWin(g, hw)
{
	hdc = 0L;	minLW = 1;
	if(g) {
		if(!hw) CreateNewWindow(g);
		else hWnd = hw;
		if(!MainWnd) MainWnd = hWnd;
		}
	else {							//its a dialog window
		hWnd = hw;
		yAxis.flags = AXIS_INVERT;	//drawing origin upper left corner
		}
}

OutputWin::~OutputWin()
{
	HideTextCursorObj(this);
	SendMessage(hWnd, WM_CLOSE, 0, 0L);
	//Note: HGDI objects are deleted by the BitMapWin destructor
}

bool
OutputWin::ActualSize(RECT *rc)
{
	if(GetClientRect(hWnd, rc)&& (rc->right-rc->left) > 40 && (rc->bottom - rc->top) > 40) return true;
	return false;
}

void
OutputWin::Caption(char *txt, bool bModified)
{
	char txt1[200];
	int cb;

	cb = rlp_strcpy(txt1, 180, txt);
	if(bModified)rlp_strcpy(txt1+cb, 20, " [modified]");
	SetWindowText(hWnd, txt1);
}

const static unsigned char hand_bits[] =	{	//hand cursor bitmap
	0x01, 0x80, 0x1b, 0xf0, 0x3f, 0xf8, 0x3f, 0xfa,
	0x1f, 0xff, 0x1f, 0xff, 0x6f, 0xff, 0xff, 0xff,
	0xff, 0xfe, 0x7f, 0xfe, 0x7f, 0xfe, 0x3f, 0xfc,
	0x1f, 0xfc, 0x0f, 0xf8, 0x07, 0xf8, 0x07, 0xf8};

const static unsigned char hand_mask[] =	{	//hand cursor mask
	0xff, 0xff, 0xfe, 0x7f, 0xe6, 0x4f, 0xe6, 0x4f,
	0xf2, 0x4d, 0xf2, 0x49, 0x78, 0x09, 0x98, 0x01,
	0x88, 0x03, 0xc0, 0x03, 0xc0, 0x03, 0xe0, 0x07,
	0xf0, 0x07, 0xf8, 0x0f, 0xfc, 0x0f, 0xfc, 0x0f};

const static unsigned char zoom_bits[] =	{	//zoom cursor bitmap
	0x00, 0x00, 0x00, 0x00, 0x01, 0xa0, 0x06, 0x30,
	0x08, 0x08, 0x10, 0x84, 0x10, 0x84, 0x20, 0x02,
	0x26, 0x32, 0x20, 0x02, 0x10, 0x84, 0x10, 0x84,
	0x08, 0x08, 0x06, 0x30, 0x01, 0xa0, 0x00, 0x00};

const static unsigned char zoom_mask[] =	{	//zoom cursor mask
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff};

const static unsigned char paste_bits[] =	{	//paste cursor bitmap
	0x20, 0x00, 0x20, 0x00, 0xf8, 0x00, 0x20, 0x00,
	0x23, 0xfe, 0x07, 0xff, 0x07, 0xff, 0x07, 0xff,
	0x07, 0xff, 0x07, 0xff, 0x07, 0xff, 0x07, 0xff,
	0x07, 0xff, 0x07, 0xff, 0x03, 0xfe, 0x00, 0x00};

const static unsigned char paste_mask[] =	{	//paste cursor mask
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xfc, 0x05, 0xfd, 0x05, 0xfd, 0xf9,
	0xfc, 0x01, 0xfc, 0x01, 0xfc, 0x01, 0xfc, 0x01,
	0xfc, 0x01, 0xfc, 0x01, 0xff, 0xff, 0xff, 0xff};

const static unsigned char drawpen_bits[] =	{	//draw cursor bitmap
	0xc0, 0x00, 0xf0, 0x00, 0x7c, 0x00, 0x7f, 0x00,
	0x3f, 0x80, 0x3f, 0xc0, 0x1f, 0xe0, 0x1f, 0xf0,
	0x0f, 0xf8, 0x07, 0xfc, 0x03, 0xfe, 0x01, 0xff,
	0x00, 0xff, 0x00, 0x7e, 0x00, 0x3c, 0x00, 0x18};

const static unsigned char drawpen_mask[] =	{	//draw cursor mask
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf3, 0xff,
	0xe0, 0xff, 0xe2, 0x7f, 0xf1, 0x3f, 0xf0, 0x9f,
	0xf8, 0x4f, 0xfc, 0x27, 0xfe, 0x13, 0xff, 0x0b,
	0xff, 0x87, 0xff, 0xcf, 0xff, 0xff, 0xff, 0xff};

const static unsigned char drect_bits[] =	{	//draw rectangle bitmap
	0x20, 0x00, 0x20, 0x00, 0xf8, 0x00, 0x20, 0x00,
	0x20, 0x00, 0x00, 0x00, 0x1f, 0xff, 0x1f, 0xff,
	0x1f, 0xff, 0x1f, 0xff, 0x1f, 0xff, 0x1f, 0xff,
	0x1f, 0xff, 0x1f, 0xff, 0x1f, 0xff, 0x00, 0x00};

const static unsigned char drect_mask[] =	{	//draw rectangle mask
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf0, 0x01,
	0xf0, 0x01, 0xf0, 0x01, 0xf0, 0x01, 0xf0, 0x01,
	0xf0, 0x01, 0xf0, 0x01, 0xff, 0xff, 0xff, 0xff};

const static unsigned char drrect_bits[] =	{	//draw rounded rectangle bitmap
	0x20, 0x00, 0x20, 0x00, 0xf8, 0x00, 0x20, 0x00,
	0x20, 0x00, 0x00, 0x00, 0x07, 0xfc, 0x0f, 0xfe,
	0x1f, 0xff, 0x1f, 0xff, 0x1f, 0xff, 0x1f, 0xff,
	0x1f, 0xff, 0x0f, 0xfe, 0x07, 0xfc, 0x00, 0x00};

const static unsigned char drrect_mask[] =	{	//draw rounded rectangle mask
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xf8, 0x03,
	0xf0, 0x01, 0xf0, 0x01, 0xf0, 0x01, 0xf0, 0x01,
	0xf0, 0x01, 0xf8, 0x03, 0xff, 0xff, 0xff, 0xff};

const static unsigned char delly_bits[] =	{	//draw ellipse bitmap
	0x20, 0x00, 0x20, 0x00, 0xf8, 0x00, 0x20, 0x00,
	0x20, 0x00, 0x00, 0x00, 0x01, 0xf0, 0x07, 0xfc,
	0x0f, 0xfe, 0x1f, 0xff, 0x1f, 0xff, 0x1f, 0xff,
	0x0f, 0xfe, 0x07, 0xfc, 0x01, 0xf0, 0x00, 0x00};

const static unsigned char delly_mask[] =	{	//draw ellipse mask
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xfe, 0x0f,
	0xf8, 0x03, 0xf0, 0x01, 0xf0, 0x01, 0xf0, 0x01,
	0xf8, 0x03, 0xfe, 0x0f, 0xff, 0xff, 0xff, 0xff};


//display 16x16 cursor data: developers utility
/*
void disp_bm(unsigned char *tb)
{
	char txt[512];
	unsigned char currbyte;
	int i, j, pos;

	for(i = pos = 0; i < 32; i++) {
		currbyte = tb[i];
		for (j = 0; j < 8; j++) {
			if(currbyte & 0x80) pos += sprintf(txt+pos, "1");
			else pos += sprintf(txt+pos, "0");
			currbyte <<= 1;
			}
		if(i & 1) pos += sprintf(txt+pos, "\n");
		}
	InfoBox(txt);
}
*/

void
OutputWin::MouseCursor(int cid, bool force)
{
	HCURSOR hc, hoc = 0L;

	if(cid == cCursor && !force) return;
	if(cid == MC_LAST) cid = cCursor;
	switch(cid) {
	case MC_ARROW:
		hoc = SetCursor(LoadCursor(NULL, IDC_ARROW));	break;
	case MC_TXTFRM:
	case MC_CROSS:	hoc = SetCursor(LoadCursor(NULL, IDC_CROSS));	break;
	case MC_WAIT:
		hoc = SetCursor(LoadCursor(NULL, IDC_WAIT));	break;
	case MC_TEXT:	hoc = SetCursor(LoadCursor(NULL, IDC_IBEAM));	break;
	case MC_NORTH:	hoc = SetCursor(LoadCursor(NULL, IDC_SIZENS));	break;
	case MC_NE:		hoc = SetCursor(LoadCursor(NULL, IDC_SIZENESW));break;
	case MC_COLWIDTH:
	case MC_EAST:	hoc = SetCursor(LoadCursor(NULL, IDC_SIZEWE));	break;
	case MC_SE:		hoc = SetCursor(LoadCursor(NULL, IDC_SIZENWSE));break;
	case MC_SALL:	hoc = SetCursor(LoadCursor(NULL, IDC_SIZEALL));	break;	
	case MC_MOVE:
		hc = CreateCursor(hInstance, 7, 7, 16, 16, hand_mask, hand_bits);
		hoc = SetCursor(hc);
		break;
	case MC_ZOOM:
		hc = CreateCursor(hInstance, 7, 7, 16, 16, zoom_mask, zoom_bits);
		hoc = SetCursor(hc);
		break;
	case MC_PASTE:
		hc = CreateCursor(hInstance, 2, 2, 16, 16, paste_mask, paste_bits);
		hoc = SetCursor(hc);
		break;
	case MC_DRAWPEN:
		hc = CreateCursor(hInstance, 0, 0, 16, 16, drawpen_mask, drawpen_bits);
		hoc = SetCursor(hc);
		break;
	case MC_DRAWREC:
		hc = CreateCursor(hInstance, 2, 2, 16, 16, drect_mask, drect_bits);
		hoc = SetCursor(hc);
		break;
	case MC_DRAWRREC:
		hc = CreateCursor(hInstance, 2, 2, 16, 16, drrect_mask, drrect_bits);
		hoc = SetCursor(hc);
		break;
	case MC_DRAWELLY:
		hc = CreateCursor(hInstance, 2, 2, 16, 16, delly_mask, delly_bits);
		hoc = SetCursor(hc);
		break;
	default:	return;
		}
	if(hoc) DestroyCursor(hoc);
	cCursor = cid;
}

bool
OutputWin::SetScroll(bool isVert, int iMin, int iMax, int iPSize, int iPos)
{
	SCROLLINFO si;

	if(iPos < iMin) return false;
	si.cbSize = sizeof(SCROLLINFO);
	si.fMask = SIF_ALL;
	si.nMin = iMin;
	si.nMax = iMax;
	si.nPage = iPSize < iMax ? iPSize : iMax;
	si.nPos = iPos;
	si.nTrackPos = 0;
	SetScrollInfo(hWnd, isVert ? SB_VERT : SB_HORZ, &si, TRUE);
	return true;
}

bool
OutputWin::Erase(DWORD Color)
{
	bool bRet;
	RECT ClientRect;

	if(bRet = BitMapWin::Erase(Color)) {
		GetClientRect(hWnd, &ClientRect);
		InvalidateRect(hWnd, &ClientRect, FALSE);
		}
	return bRet;
}

bool
OutputWin::StartPage()
{
	MrkMode = MRK_NONE;
	MrkRect = 0L;
	hdc = memDC;
	if(hgo && hdc) hgo->StartPage();
	return true;
}

bool
OutputWin::EndPage()
{
	RECT ClientRect;

	hdc = NULL;
	GetClientRect(hWnd, &ClientRect);
	return UpdateRect(&ClientRect, false);
}

bool
OutputWin::UpdateRect(RECT *rc, bool invert)
{
	HDC dc;
	BOOL RetVal = FALSE;

	if(dc = GetDC(hWnd)) {
		RetVal = BitBlt(dc, rc->left, rc->top, rc->right - rc->left,
			rc->bottom - rc->top, memDC, rc->left, rc->top, invert ? DSTINVERT : SRCCOPY);
		ReleaseDC(hWnd, dc);
		}
	return (RetVal != 0);
}

bool
OutputWin::UpdateRect(HDC dc, RECT rc)
{
	if(BitBlt(dc, rc.left, rc.top, rc.right - rc.left,
			rc.bottom - rc.top, memDC, rc.left, rc.top, SRCCOPY))return true;
	return false;
}

void
OutputWin::ShowBitmap(int x, int y, anyOutput* src)
{
	int w, h;
	HDC dc;
	BitMapWin *sr;

	if(!src) return;
	sr = (BitMapWin *) src;
	w = sr->DeskRect.right - sr->DeskRect.left;
	h = sr->DeskRect.bottom - sr->DeskRect.top;
	if(dc = GetDC(hWnd)) {
		BitBlt(dc, x, y, w,	h, sr->memDC, 0, 0, SRCCOPY);
		ReleaseDC(hWnd, dc);
		}
}

void
OutputWin::ShowLine(POINT * pts, int cp, DWORD color)
{
	HDC dc;
	HPEN hP, oP;
	
	if((hP = CreatePen(PS_SOLID, 0, color))&& (dc = GetDC(hWnd))) {
		oP = (HPEN)SelectObject(dc, hP);
		Polyline(dc, pts, cp);
		SelectObject(dc, oP);
		DeleteObject(hP);
		ReleaseDC(hWnd, dc);
		}
}

void
OutputWin::ShowEllipse(POINT p1, POINT p2, DWORD color)
{
	HDC dc;
	HPEN hP, oP;

	if((hP = CreatePen(PS_SOLID, 0, color)) && (dc = GetDC(hWnd))) {
		oP = (HPEN)SelectObject(dc, hP);
		Arc(dc, p1.x, p1.y, p2.x, p2.y, 0, 0, 0, 0);
		SelectObject(dc, oP);
		DeleteObject(hP);
		ReleaseDC(hWnd, dc);
		}
}

bool
OutputWin::SetMenu(int type)
{
	HMENU hMenu = 0L, hPopup = 0L;

	switch(type) {
	case MENU_NONE:
		break;
	case MENU_SPREAD:
		hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MENU_2));
		break;
	case MENU_GRAPH:
/*
		hMenu = CreateMenu();
		hPopup = CreatePopupMenu();
		AppendMenu(hPopup, MF_STRING, CM_OPEN, "&Open");
		AppendMenu(hMenu, MF_POPUP, (UINT_PTR)hPopup, "&File");
//		AppendMenu(hMenu, MF_POPUP, (unsigned int)hPopup, "&File");
//*/		hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MENU_1));
		break;
	case MENU_PAGE:
		hMenu = LoadMenu(hInstance, MAKEINTRESOURCE(MENU_3));
		}
	::SetMenu(hWnd, hMenu);
	return true;
}

void
OutputWin::CheckMenu(int mid, bool check)
{
	HMENU hMenu = GetMenu(hWnd);

	if(mid < CM_T_STANDARD) switch(mid){					//tool mode identifier
	case TM_STANDARD:	mid = CM_T_STANDARD;	break;
	case TM_DRAW:		mid = CM_T_DRAW;		break;
	case TM_POLYLINE:	mid = CM_T_POLYLINE;	break;
	case TM_POLYGON:	mid = CM_T_POLYGON;		break;
	case TM_RECTANGLE:	mid = CM_T_RECTANGLE;	break;
	case TM_ROUNDREC:	mid = CM_T_ROUNDREC;	break;
	case TM_ELLIPSE:	mid = CM_T_ELLIPSE;		break;
	case TM_ARROW:		mid = CM_T_ARROW;		break;
	case TM_TEXT:		mid = CM_T_TEXT;		break;
	default:	return;
		}
	if(hMenu) CheckMenuItem(hMenu, mid, check ? MF_CHECKED : MF_UNCHECKED);
}

void
OutputWin::FileHistory() 
{
	HMENU hSubMenu;
	char **history[] = {&defs.File1, &defs.File2, &defs.File3, &defs.File4, &defs.File5, &defs.File6};
	int i, j, k;

	if(!hasHistMenu || !defs.File1) return;
	if(!(hSubMenu = GetSubMenu(GetMenu(hWnd), 0))) return;
	if(!HistMenuSize) AppendMenu(hSubMenu, MF_SEPARATOR, 0L, 0L);
    for(i = 0; i < 6 && *history[i]; i++) {
		k = (int)strlen(*history[i]);
		for (j = 0; j < k && defs.currPath[j] == (*history[i])[j]; j++);
		if((*history[i])[j] == '\\' || (*history[i])[j] == '/') j++;
		if(i < HistMenuSize) {
			ModifyMenu(hSubMenu, CM_FILE1+i, MF_BYCOMMAND | MF_STRING, CM_FILE1+i, *history[i]+j);
			}
		else {
			AppendMenu(hSubMenu, MF_STRING, CM_FILE1+i, *history[i]+j);
			}
		}
	HistMenuSize = i;
}

void
OutputWin::CreateNewWindow(void *g)
{
	RECT ClientRect;

	hWnd = CreateWindow(name, "RLPlot",
		WS_OVERLAPPEDWINDOW | WS_HSCROLL | WS_VSCROLL,
		CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
		CW_USEDEFAULT, NULL, NULL, hInstance, NULL);
#if _MSC_VER >= 1400
	SetWindowLongPtr(hWnd, 0, lptrref(g));		// g is the parent graphic obj
	SetWindowLongPtr(hWnd, GWL_USERDATA, lptrref(this));
#else
	SetWindowLong(hWnd, 0, lptrref(g));			// g is the parent graphic obj
	SetWindowLong(hWnd, GWL_USERDATA, lptrref(this));
#endif
	if(BitMapWin::Erase(0x00cbcbcb)) {
		GetClientRect(hWnd, &ClientRect);
		InvalidateRect(hWnd, &ClientRect, FALSE);
		}
	UpdateWindow(hWnd);
	ShowWindow(hWnd, SW_SHOW);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Copy to Clipboard
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Create Windows Meta File for clipboard
WinCopyWMF::WinCopyWMF(GraphObj *g, char *file_wmf, char *file_emf)
{
	HDC dc;
	HWND hwndDesk;

	DeskRect.left = DeskRect.top = 0;				DeskRect.right = DeskRect.bottom = 0x4fffffffL;
	hPen = CreatePen(PS_SOLID, 1, dLineCol = 0x00ff0000L);
	hBrush = CreateSolidBrush(dFillCol =dBgCol = 0x00ffffffL);
	dPattern = 0L;		go = g;			hgo = 0L;		bott_y = 0;
	dc = GetDC(hwndDesk = GetDesktopWindow());	minLW = 1;
	hres = (double)GetDeviceCaps(dc, LOGPIXELSX);	vres = (double)GetDeviceCaps(dc, LOGPIXELSY);
	ReleaseDC(hwndDesk, dc);	wmf_file = file_wmf;	emf_file = file_emf;
}

WinCopyWMF::~WinCopyWMF()
{
	if(hgo) delete hgo;
	if(hFont) DeleteObject(hFont);
	if(hBrush) DeleteObject(hBrush);
	if(hPen) DeleteObject(hPen);
}

bool
WinCopyWMF::SetLine(LineDEF *lDef)
{
	int iw;
	HPEN newPen;
	
	if(!hPen || lDef->width != LineWidth || lDef->width != LineWidth || 
		lDef->pattern != dPattern || lDef->color != dLineCol) {
		LineWidth = lDef->width;
		iw = iround(un2fix(lDef->width));
		dPattern = lDef->pattern;
		RLP.finc = 256.0/un2fix(lDef->patlength*8.0);
		RLP.fp = 0.0;
		if(iLine == iw && dLineCol == lDef->color && hPen) return true;
		iLine = iw;
		dLineCol = lDef->color;
		newPen = CreatePen(PS_SOLID, iw > 0 ? iw : 1, dLineCol);
		SelectObject(hdc, newPen);
		if(hPen) DeleteObject(hPen);
		hPen = newPen;
		}
	return true;
}

bool
WinCopyWMF::SetFill(FillDEF *fill)
{
	HBRUSH newBrush;

	if(!fill) return false;
	if((fill->type & 0xff) != FILL_NONE) {
		if(!hgo) hgo = new HatchOut(this);
		if(hgo) hgo->SetFill(fill);
		}
	else {
		if(hgo) delete hgo;
		hgo = NULL;
		}
	if(dFillCol != fill->color) {
		newBrush = CreateSolidBrush(dFillCol = fill->color);
		SelectObject(hdc, newBrush);
		if(hBrush) DeleteObject(hBrush);
		hBrush = newBrush;
		}
	dFillCol = fill->color;
	dFillCol2 = fill->color2;
	return true;
}

bool
WinCopyWMF::SetTextSpec(TextDEF *set)
{
	return com_SetTextSpec(set, this, &hFont, &TxtSet, &hdc);
}

bool
WinCopyWMF::oGetTextExtent(char *text, int cb, int *width, int *height)
{
	return com_oGetTextExtent(text, cb, width, height, hdc, &TxtSet);
}

bool
WinCopyWMF::oGetTextExtentW(w_char *text, int cb, int *width, int *height)
{
	return com_oGetTextExtentW(text, cb, width, height, hdc, &TxtSet);
}

bool
WinCopyWMF::StartPage()
{
	int w, h;
	RECT rect;
	double res;

	if(!go) return false;
	res = Units[defs.cUnits].convert;
	w = iround(hres*res*(go->GetSize(SIZE_GRECT_RIGHT) - go->GetSize(SIZE_GRECT_LEFT)));
	h = iround(vres*res*(go->GetSize(SIZE_GRECT_BOTTOM) - go->GetSize(SIZE_GRECT_TOP)));
	rect.left = rect.top = 0;
	rect.bottom = h+560;	rect.right = w+720;
	if(!(hdc = CreateEnhMetaFile(0L, emf_file, &rect, "RLPlot")))return false;
	if(SetMapMode(hdc, MM_HIMETRIC)) {
		hres = vres = 2540.0;		OC_type = OC_HIMETRIC;	
		bott_y = un2iy(go->GetSize(SIZE_GRECT_TOP) - go->GetSize(SIZE_GRECT_BOTTOM));
		VPorg.fy = bott_y;
		}
	else return false;
	if(hPen)SelectObject(hdc, hPen);	if(hBrush) SelectObject(hdc, hBrush);
	VPorg.fx = -co2fix(go->GetSize(SIZE_GRECT_LEFT));
	return true;
}

bool
WinCopyWMF::EndPage()
{
	int iFile;
	HENHMETAFILE hmf;
	unsigned int cb;
	unsigned char *buff;
	HDC dc;

	hmf = CloseEnhMetaFile(hdc);
	if(emf_file);				// nothing more to do
	else if(wmf_file) {
		dc = GetDC(MainWnd);
		cb = GetWinMetaFileBits(hmf, 0, 0L, MM_HIMETRIC, dc);
		if(cb && (buff = (unsigned char*)malloc(cb+1))){
#ifdef USE_WIN_SECURE
			if(_sopen_s(&iFile, wmf_file, O_RDWR | O_BINARY | O_CREAT | O_TRUNC, 
				0x40, S_IWRITE) || iFile < 0){
				ErrorBox("Open failed for metafile");
				free(buff);		 return false;
				}
#else
			if(-1 ==(iFile = open(wmf_file, O_RDWR | O_BINARY | O_CREAT | O_TRUNC,
				S_IWRITE | S_IREAD))){
				ErrorBox("Open failed for metafile");
				free(buff);		return false;
				}
#endif
			if(cb = GetWinMetaFileBits(hmf, cb, buff, MM_HIMETRIC, dc)){
#ifdef USE_WIN_SECURE
				_write(iFile, buff, cb);
				_close(iFile);
#else
				write(iFile, buff, cb);
				close(iFile);
#endif
				}
			free(buff);
			}
		ReleaseDC(MainWnd, dc);
		}
	else SetClipboardData(CF_ENHMETAFILE, hmf);
	DeleteEnhMetaFile(hmf);
	return true;
}

bool
WinCopyWMF::oCircle(int ix1, int iy1, int ix2, int iy2, char* nam)
{
	int x1=ix1, x2=ix2, y1, y2;
	BOOL RetVal;
	
	y1 = bott_y - iy1;				y2 = bott_y - iy2;
	if(x2 > x1) x2 = ix2 + 25;		else x1 = ix1 + 25;
	if(y1 > y2) y2 -= 25;			else y1 -=25;
	RetVal = Ellipse(hdc, x1, y1, x2, y2);
	if(RetVal && hgo) return hgo->oCircle(x1, y1, x2, y2);
	return RetVal != 0;
}

bool
WinCopyWMF::oPolyline(POINT * pts, int cp, char *nam)
{
	int i;

	if(cp < 1) return false;
	for (i = 0; i < cp; i++) pts[i].y = bott_y - pts[i].y;
	if (dPattern) {
		for (i = 1; i < cp; i++) PatLine(pts[i-1], pts[i]);
		return true;
		}
	return (0 != Polyline(hdc, pts, cp));
}

bool
WinCopyWMF::oRectangle(int ix1, int iy1, int ix2, int iy2, char *name)
{
	int x1=ix1, x2=ix2, y1, y2;
	BOOL RetVal;

	y1 = bott_y - iy1;				y2 = bott_y - iy2;
	if(x2 > x1) x2 = ix2 + 25;		else x1 = ix1 + 25;
	if(y1 > y2) y2 -= 25;			else y1 -=25;
	RetVal = Rectangle(hdc, x1, y1, x2, y2);
	if(RetVal && hgo) return hgo->oRectangle(ix1, iy1, ix2, iy2, 0L);
	return RetVal != 0;
}

bool
WinCopyWMF::oSolidLine(POINT *p)
{
	p[0].y = bott_y - p[0].y;		p[1].y = bott_y - p[1].y;
	if(Polyline(hdc, p, 2)) return true;
	return false;
}

bool
WinCopyWMF::oTextOut(int x, int y, char *txt, int cb)
{
	y = bott_y - y;
	return com_oTextOut(x, y, txt, cb, &hFont, &hdc, &TxtSet, this);
}

bool
WinCopyWMF::oTextOutW(int x, int y, w_char *txt, int cb)
{
	y = bott_y - y;
	return com_oTextOutW(x, y, txt, cb, &hFont, &hdc, &TxtSet, this);
}

bool
WinCopyWMF::oPolygon(POINT *pts, int cp, char *nam)
{
	int i;
	BOOL RetVal;

	for (i = 0; i < cp; i++) pts[i].y = bott_y - pts[i].y;
	RetVal = Polygon(hdc, pts, cp);
	if(RetVal && hgo) {
		for (i = 0; i < cp; i++) pts[i].y = - pts[i].y + bott_y ;
		return hgo->oPolygon(pts, cp);
		}
	return (RetVal != 0);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Print 
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
PrintWin::PrintWin()
{
	int i, j;
	double pw, ph;

	PrintDriver = PrintDevice = PrintPort = 0L;
	hPen = 0L;		hBrush = 0L;			hFont = 0L;		hDC = 0L;
	hgo = 0L;		units = defs.cUnits;		i = j = 0;		minLW = 1;
	GetProfileString("windows", "device", "", TmpTxt, 4096);
	while(TmpTxt[i] && TmpTxt[i] != ',') i++;
	TmpTxt[i] = 0;
	if (i >2) {
		PrintDevice = _strdup(TmpTxt);
		i++;			j = i;
		while(TmpTxt[i] && TmpTxt[i] != ',') i++;
		if(i-j > 2) {
			TmpTxt[i] = 0;
			PrintDriver = _strdup(TmpTxt+j);
			i++;
			j = i;
			while(TmpTxt[i] && TmpTxt[i] != ',') i++;
			if(i-j > 2) {
				TmpTxt[i] = 0;
				PrintPort = _strdup(TmpTxt+j);
				//Get default paper setup
				if(hDC = CreateDC(PrintDriver, PrintDevice, PrintPort, 0L)) { 
					pw = GetDeviceCaps(hDC, PHYSICALWIDTH);
					pw /= (double)GetDeviceCaps(hDC, LOGPIXELSX);
					ph = GetDeviceCaps(hDC, PHYSICALHEIGHT);
					ph /= (double)GetDeviceCaps(hDC, LOGPIXELSY);
					switch (defs.cUnits){
					case 1:		pw *= 2.54;		ph *= 2.54;		break;
					case 2:		break;
					default:	pw *= 25.4;		ph *= 25.4;		break;
						}
					FindPaper(pw, ph, 0.01);
					DeleteDC(hDC);
					}
				hDC = 0L;
				}
			}
		}
}

PrintWin::~PrintWin()
{
	if(PrintDriver) free(PrintDriver);		if(PrintDevice) free(PrintDevice);
	if(PrintPort) free(PrintPort);			if(hPen) DeleteObject(hPen);
	if(hBrush) DeleteObject(hBrush);		if(hFont) DeleteObject(hFont);
}

bool
PrintWin::SetLine(LineDEF *lDef)
{
	int iw;
	HPEN newPen;

	if(!hPen || lDef->width != LineWidth || lDef->width != LineWidth || 
		lDef->pattern != dPattern || lDef->color != dLineCol) {
		LineWidth = lDef->width;
		iw = iround(un2ix(lDef->width));
		dPattern = lDef->pattern;
		RLP.finc = 256.0/un2fix(lDef->patlength*8.0);
		RLP.fp = 0.0;
		if(iLine == iw && dLineCol == lDef->color && hPen) return true;
		iLine = iw;
		dLineCol = lDef->color;
		newPen = CreatePen(PS_SOLID, iw > 0 ? iw : 1, dLineCol);
		SelectObject(hDC, newPen);
		if(hPen) DeleteObject(hPen);
		hPen = newPen;
		}
	return true;
}

bool
PrintWin::SetFill(FillDEF *fill)
{
	HBRUSH newBrush;

	if(!fill) return false;
	if((fill->type & 0xff) != FILL_NONE) {
		if(!hgo) hgo = new HatchOut(this);
		if(hgo) hgo->SetFill(fill);
		}
	else {
		if(hgo) delete hgo;
		hgo = NULL;
		}
	newBrush = CreateSolidBrush(fill->color);
	SelectObject(hDC, newBrush);
	if(hBrush) DeleteObject(hBrush);
	hBrush = newBrush;
	dFillCol = fill->color;
	dFillCol2 = fill->color2;
	return true;
}

bool
PrintWin::SetTextSpec(TextDEF *set)
{
	return com_SetTextSpec(set, this, &hFont, &TxtSet, &hDC);
}

bool
PrintWin::oGetTextExtent(char *text, int cb, int *width, int *height)
{
	return com_oGetTextExtent(text, cb, width, height, hDC, &TxtSet);
}

bool
PrintWin::oGetTextExtentW(w_char *text, int cb, int *width, int *height)
{
	return com_oGetTextExtentW(text, cb, width, height, hDC, &TxtSet);
}

bool
PrintWin::StartPage()
{
	DOCINFO DocInfo;
	bool bRet = false;

	if(hDC = CreateDC(PrintDriver, PrintDevice, PrintPort, 0L)) { 
		hPen = CreatePen(PS_SOLID, 1, dLineCol = 0x00ffffffL);
		hBrush = CreateSolidBrush(dFillCol =dBgCol = 0x00ffffffL);
		dPattern = 0L;
		SelectObject(hDC, hPen);
		SelectObject(hDC, hBrush);
		memset(&DocInfo, 0, sizeof(DOCINFO));
		DocInfo.lpszDocName = "RLPlot graph";
		DocInfo.cbSize = sizeof(DOCINFO);
		DeskRect.left = DeskRect.top = 0;
		DeskRect.right = GetDeviceCaps(hDC, HORZRES);
		DeskRect.bottom = GetDeviceCaps(hDC, VERTRES);
		hres = (double)GetDeviceCaps(hDC, LOGPIXELSX);
		vres = (double)GetDeviceCaps(hDC, LOGPIXELSY);
		if(StartDoc(hDC, &DocInfo) >= 0) {
			if(::StartPage(hDC)>0) bRet = true;
			}
		}
	return bRet;
}

bool
PrintWin::EndPage()
{

	if(hDC) {
		::EndPage(hDC);		EndDoc(hDC);		DeleteDC(hDC);
		}
	hDC = 0L;
	return true;
}

bool
PrintWin::Eject()
{
	if(hDC) {
		::EndPage(hDC);		::StartPage(hDC);	return true;
		}
	return false;
}

bool
PrintWin::CopyBitmap(int x, int y, anyOutput* sr, int sx, int sy,
	int sw, int sh, bool invert)
{
	BitMapWin *src = (BitMapWin*)sr;

	return(0 != BitBlt(hDC, x, y, sw, sh, src->memDC, sx, sy, 
		invert ? DSTINVERT : SRCCOPY));
}

bool
PrintWin::oCircle(int x1, int y1, int x2, int y2, char* nam)
{
	BOOL RetVal;
	
	RetVal = Ellipse(hDC, x1, y1, x2, y2);
	if(RetVal && hgo) return hgo->oCircle(x1, y1, x2, y2);
	else if(RetVal) return true;
	return false;
}

bool
PrintWin::oPolyline(POINT * pts, int cp, char *nam)
{
	int i;

	if(cp < 1) return FALSE;
	if (dPattern) {
		for (i = 1; i < cp; i++) PatLine(pts[i-1], pts[i]);
		return true;
		}
	else {
		if(Polyline(hDC, pts, cp))return true;
		else return false;
		}
}

bool
PrintWin::oRectangle(int x1, int y1, int x2, int y2, char *nam)
{
	POINT pts[5];

	pts[0].x = pts[3].x = pts[4].x = x1;	pts[0].y = pts[1].y = pts[4].y = y1;
	pts[1].x = pts[2].x = x2;		pts[2].y = pts[3].y = y2;
	return oPolygon(pts, 5, nam);
}

bool
PrintWin::oSolidLine(POINT *p)
{
	if(Polyline(hDC, p, 2)) return true;
	return false;
}

bool
PrintWin::oTextOut(int x, int y, char *txt, int cb)
{
	return com_oTextOut(x, y, txt, cb, &hFont, &hDC, &TxtSet, this);
}

bool
PrintWin::oTextOutW(int x, int y, w_char *txt, int cb)
{
	return com_oTextOutW(x, y, txt, cb, &hFont, &hDC, &TxtSet, this);
}

bool
PrintWin::oPolygon(POINT *pts, int cp, char *nam)
{
	BOOL RetVal;

	RetVal = Polygon(hDC, pts, cp);
	if(RetVal && hgo) return hgo->oPolygon(pts, cp);
	else if (RetVal) return true;
	return false;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Find a suitable www browser
void FindBrowser()
{
	char text[600];
	long size = 599;
	HKEY hdl;
	int i;

	//find the default browser
	if(ERROR_SUCCESS == RegQueryValue(HKEY_CLASSES_ROOT, "http\\shell\\open\\command", 
		text, &size) && size > 7) {
		if(text[0] == '"') {
			for(i = size-2; i >3; i--) {
				if(text[i+1] == '"') {
					text[i+1] = 0;
					break;
					}
				else text[i+1] = 0;
				}
			WWWbrowser = _strdup(text+1);
			}
		else {
			for(i = size-1; i >5; i--) {
				if(0 == _stricmp(text+i-3, ".exe")) break;
				else text[i] = 0;
				}
			WWWbrowser = _strdup(text);
			}
		}
	//find user default data directory
	if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, "Environment", NULL,
		KEY_READ, &hdl)) {
		text[0] = 0;	size=599;
		RegQueryValueEx(hdl, "HOMEDRIVE", 0, 0, (unsigned char*)text, (unsigned long*)&size);
		size= 599;
		RegQueryValueEx(hdl, "HOMEPATH", 0, 0, (unsigned char*)(text+strlen(text)), 
			(unsigned long*)&size);
		defs.currPath = _strdup(text);
		}
	//find user application data directory
	if(ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, 
		"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders", NULL,
		KEY_READ, &hdl)) {
		text[0] = 0;	size=599;
		RegQueryValueEx(hdl, "AppData", 0, 0, (unsigned char*)text, (unsigned long*)&size);
#ifdef USE_WIN_SECURE
		strcat_s(text, 600, "\\RLPlot");
#else
		strcat(text, "\\RLPlot");
#endif
		defs.IniFile = _strdup(text);
		}
	//find country specific information
	//  its not a perfect place to do it, but a good one
	GetProfileString("intl", "sDecimal", ".", text, 2);
	if(text[0]) defs.DecPoint[0] = text[0];
	GetProfileString("intl", "sList", ".", text, 2);
	if(text[0]) defs.ColSep[0] = text[0];
	if(GetProfileInt("intl", "iMeasure", 0)) defs.dUnits = defs.cUnits = 2;
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Windos entry point
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
int WINAPI WinMain( HINSTANCE hInst, HINSTANCE hPrevInstance,
	 LPSTR lpCmdLine, int nCmdShow )
{
	WNDCLASS wndclass;
	MSG	msg;
	DefsRW *drw;
	HWND hwnd;
	HDC dc;

	//OS dependent initialization
	dlgtxtheight = 16;

	if(lpCmdLine && lpCmdLine[0] && FileExist(lpCmdLine)) LoadFile = _strdup(lpCmdLine);
	else if(lpCmdLine) {								//probably Unicode
#ifdef USE_WIN_SECURE
		sprintf_s(TmpTxt, TMP_TXT_SIZE, "%s", lpCmdLine);
#else
		sprintf(TmpTxt, "%s", lpCmdLine);
#endif
		rmquot(TmpTxt);
		if(TmpTxt[0]) LoadFile= _strdup(TmpTxt);
		}
	ShellCmd = GetCommandLine();
	hInstance = hInst;
	wndclass.style = CS_BYTEALIGNWINDOW | CS_DBLCLKS;
	wndclass.lpfnWndProc = WndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = sizeof(GraphObj*);
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(hInst, MAKEINTRESOURCE(IDI_EVAL));
	wndclass.hCursor = 0L;
	wndclass.hbrBackground = NULL;
	wndclass.lpszMenuName = 0L;
	wndclass.lpszClassName = name;

	RegisterClass(&wndclass);
	dc = GetDC(hwnd = GetDesktopWindow());
	if(0 == SetGraphicsMode(dc, GM_ADVANCED)){
		ErrorBox("RLPlot detected an\nold version of Windows which\nis no longer supported!\n");
		return 1;
		}
	SetGraphicsMode(dc, GM_COMPATIBLE);
	ReleaseDC(hwnd, dc);		ShowBanner(true);
	InitTextCursor(true);		Printer = new PrintWin();
	accel = LoadAccelerators(hInstance, MAKEINTRESOURCE(ACCELERATORS_1));
	while(GetMessage(&msg, NULL, 0, 0)){
		TranslateMessage(&msg);
		TranslateAccelerator(msg.hwnd, accel, &msg);
		DispatchMessage(&msg);
		}
	if(defs.IniFile) {
		if(drw = new DefsRW()){
			drw->FileIO(FILE_WRITE);		delete drw;
			}
		}
	if(WWWbrowser) free(WWWbrowser);
	if(LoadFile) free(LoadFile);
	SpreadMain(false);
	if(Printer) delete Printer;
	InitTextCursor(false);
	UnregisterClass(name, hInstance);
	return (int)msg.wParam;
}

void CopyData(GraphObj *g, unsigned int cf)
{
	HGLOBAL hmem;
	long cb;
	unsigned char *dt = 0L;
	unsigned char *buf;

	if(!g || g->Id != GO_SPREADDATA) return;
	switch(cf) {
	case CF_TEXT:
		if(!g->Command(CMD_COPY_TSV, &dt, 0L))return;
		break;
	case CF_SYLK:
		if(!g->Command(CMD_COPY_SYLK, &dt, 0L))return;
		break;
	default:
		if(cf == cf_rlpxml && g->Command(CMD_COPY_XML, &dt, 0L)) break;
		else return;
		}
	cb = (long)strlen((char*)dt);
	if(hmem = GlobalAlloc(GMEM_MOVEABLE, cb+2)) {
		if(buf = (unsigned char *)GlobalLock(hmem)) {
			memcpy(buf, dt, cb+1);
			GlobalUnlock(hmem);
			SetClipboardData(cf, hmem);
			}
		}
}

void CopyGraph(GraphObj *g, unsigned int cf, anyOutput *o)
{
	HGLOBAL hmem;
	char *dt, *buf;
	long cb;

	if(!(dt = GraphToMem(g, &cb)))return;
	if(o) ShowCopyMark(o, &g->rDims, 1);
	if(hmem = GlobalAlloc(GMEM_MOVEABLE, cb+1)) {
		if(buf = (char *)GlobalLock(hmem)) {
			memcpy(buf, dt, cb);
			buf[cb] = 0;
			GlobalUnlock(hmem);
			SetClipboardData(cf, hmem);
			}
		}
	free(dt);
}

void ScrollEvent(bool bVert, HWND hwnd, UINT type, GraphObj *g, OutputWin *w)
{
	SCROLLINFO si;
	int LineStep, cmd, pos;

	if(hwnd && g && w) {
		cmd = bVert ? CMD_SETVPOS : CMD_SETHPOS;
		si.fMask = SIF_ALL;
		si.cbSize = sizeof(SCROLLINFO);
		if(!(GetScrollInfo(hwnd, bVert ? SB_VERT : SB_HORZ, &si)))return;
		LineStep = (g->Id == GO_GRAPH || g->Id == GO_PAGE) ? 16 : 1;
		switch(type){
		case SB_LINEUP:
			pos = si.nPos - LineStep;
			break;
		case SB_LINEDOWN:
			pos = si.nPos + LineStep;
			break;
		case SB_PAGEUP:
			if(g->Id == GO_SPREADDATA) {
				g->Command(CMD_PAGEUP, 0L, w);
				return;
				}
			pos = (si.nPos - (int)si.nPage) >= si.nMin ? (si.nPos - si.nPage) : si.nMin;
			break;
		case SB_PAGEDOWN:
			if(g->Id == GO_SPREADDATA) {
				g->Command(CMD_PAGEDOWN, 0L, w);
				return;
				}
			pos = (si.nPos + (int)si.nPage*2) < si.nMax ? (si.nPos + si.nPage) : (si.nMax - si.nPage+1);
			break;
		case SB_THUMBTRACK:		case SB_THUMBPOSITION:
			pos = si.nTrackPos;
			break;
		default:
			return;
			}
		g->Command(cmd, (void*)(& pos), w);
		}
}

long OpenFileFromHistory(OutputWin *w, GraphObj *g, int id)
{
	char *name = 0L;

	switch (id) {
	case 0:			name = defs.File1;			break;
	case 1:			name = defs.File2;			break;
	case 2:			name = defs.File3;			break;
	case 3:			name = defs.File4;			break;
	case 4:			name = defs.File5;			break;
	case 5:			name = defs.File6;			break;
	default:		return 0;
		}
	if(name && name[0] && FileExist(name)) {
		g->Command(CMD_DROPFILE, name, w);
		defs.FileHistory(name);
		w->FileHistory();
		}
	else {
		ErrorBox("The selected file   \ndoes not exist!\n");
		}
	return 0;
}

static GraphObj *copy_obj;

long FAR PASCAL WndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
	static WinCopyWMF *CopyWMF = NULL;
	static BitMapWin *CopyBMP = NULL;
	static bool CtrlDown = false, bAltKey = false;
	static w_char uc_char;
	PAINTSTRUCT ps;
	OutputWin *w;
	GraphObj *g;
	MouseEvent mev;
	HDC dc;
	int cc;
	RECT rec;

	g = (GraphObj *) reflptr(GetWindowLong(hwnd, 0));
	w = (OutputWin *) reflptr(GetWindowLong(hwnd, GWL_USERDATA));
	if(g && w) switch(message) {
	case WM_SETFOCUS:
		if(g->Id == GO_GRAPH) CurrGraph = (Graph*)g;
		else CurrGraph = 0L;
		break;
	case WM_LBUTTONDOWN:	case WM_LBUTTONDBLCLK:
		HideTextCursor();
	case WM_MOUSEMOVE:		case WM_RBUTTONUP:		case WM_LBUTTONUP:
		mev.x = LOWORD(lParam);
		mev.y = HIWORD(lParam);
		mev.StateFlags = 0;
		if(wParam & MK_LBUTTON) mev.StateFlags |= 1;
		if(wParam & MK_MBUTTON) mev.StateFlags |= 2;
		if(wParam & MK_RBUTTON) mev.StateFlags |= 4;
		if(wParam & MK_SHIFT) mev.StateFlags |= 8;
		if(wParam & MK_CONTROL) mev.StateFlags |= 16;
		if(message == WM_LBUTTONUP) mev.Action = MOUSE_LBUP;
		else if(message == WM_RBUTTONUP) mev.Action = MOUSE_RBUP;
		else if(message == WM_LBUTTONDBLCLK) mev.Action = MOUSE_LBDOUBLECLICK;
		else if(message == WM_LBUTTONDOWN) mev.Action = MOUSE_LBDOWN;
		else if(message == WM_MOUSEMOVE)mev.Action = MOUSE_MOVE;
		g->Command(CMD_MOUSE_EVENT, (void *)&mev, w);
		break;
	case 0x020A:				//WM_MOUSEWHEEL
		ScrollEvent(true, hwnd, (int)wParam > 0 ? SB_LINEUP : SB_LINEDOWN, g, w);
		return 0;
	case WM_KEYDOWN:
		cc = (wParam & 0xff);
		if(g && w && (GetKeyState(VK_LCONTROL) || GetKeyState(VK_RCONTROL))){
			if(cc == 0xbb || cc == 0x6b ) g->Command(CMD_ZOOM, &"+", w);
			else if(cc == 0xbd || cc == 0x6d ) g->Command(CMD_ZOOM, &"-", w);
			else break;
			return 0;
			}
		break;
	case WM_CHAR:
		cc = (wParam & 0xff);
		if(bAltKey && uc_char) {
			if(uc_char >=255) {
				g->Command(CMD_ADDCHARW, (void *)(& uc_char), w); 
				uc_char = 0;	bAltKey = false;
				return 0;
				}
			else cc = (int)uc_char;
			uc_char = 0;	bAltKey = false;
			}
		g->Command(CMD_ADDCHAR, (void *)(& cc), w);
		return 0;
	case WM_SYSKEYDOWN:
		if(wParam == VK_MENU) {
			bAltKey = true;	uc_char = 0;
			}
		break;
	case WM_SYSKEYUP:
		if(wParam == VK_MENU) bAltKey = false;
		if(bAltKey) {
			cc = (wParam & 0xff);
			if( cc >= 0x60 && cc <= 0x69) {
				uc_char *= 10;	uc_char += (cc-0x60);
				return 0;
				}
			else {
				uc_char = 0;	bAltKey = false;
				return 0;
				}
			}
		break;
	case WM_VSCROLL:		case WM_HSCROLL:
		ScrollEvent(message == WM_VSCROLL, hwnd, wParam & 0xffff, g, w);
		return 0;
		}  

	switch(message) {
	case WM_CREATE:
		break;
	case WM_SIZE:
		if(g && w) g->Command(CMD_SETSCROLL, 0L, w);
		break;
	case WM_INITMENUPOPUP:		case WM_NCMOUSEMOVE:
		SetCursor(LoadCursor(NULL, IDC_ARROW));
		break;
	case WM_SETCURSOR:
		if(w) w->MouseCursor(MC_LAST, true);
		return TRUE;
	case WM_SETFOCUS:
		if(g && w) if(!g->Command(message == WM_SETFOCUS ?
			CMD_SETFOCUS : CMD_KILLFOCUS, NULL, w))
			SetCursor(LoadCursor(NULL, IDC_ARROW));
		return 0;
	case WM_DESTROYCLIPBOARD:
		if(g && w) g->Command(CMD_HIDEMARK, 0L, w);
		HideCopyMark();
		return 0;
	case WM_RENDERALLFORMATS:
		// we do not support leaving data on the clipboard after exit
		OpenClipboard(hwnd);
		EmptyClipboard();
		CloseClipboard();
		return 0;
	case WM_RENDERFORMAT:
		if(g && w) switch(wParam){
			case CF_ENHMETAFILE:
				CopyWMF = new WinCopyWMF(copy_obj, 0L, 0L);
				if(CopyWMF && CopyWMF->StartPage()) {
					copy_obj->DoPlot(CopyWMF);
					CopyWMF->EndPage();
					delete CopyWMF;					CopyWMF = NULL;
					}
				if(copy_obj->Id == GO_GRAPH || copy_obj->Id == GO_PAGE) copy_obj->DoPlot(0L);
				break;
			case CF_BITMAP:
				if((CopyBMP = new BitMapWin(copy_obj)) && CopyBMP->StartPage()) {
					copy_obj->DoPlot(CopyBMP);			CopyBMP->EndPage();
					SetClipboardData(CF_BITMAP, CopyBMP->scr);
					CopyBMP->scr = 0L;					CopyBMP->go = 0L;
					delete CopyBMP;						CopyBMP = NULL;
					}
				if(copy_obj->Id == GO_GRAPH || copy_obj->Id == GO_PAGE) copy_obj->DoPlot(0L);
				break;
			case CF_SYLK:		case CF_TEXT:
				if(g->Id == GO_SPREADDATA) CopyData(g, wParam);
				break;
			default:
//				if(wParam == cf_rlpgraph) CopyGraph(copy_obj, wParam, w);
				if(wParam == cf_rlpxml) CopyData(g, wParam);
				break;
			}
		if(w->Erase(defs.Color(COL_BG))) g->DoPlot(w);
		return 0;
	case WM_COMMAND:
		wParam &= 0xffff;
		if(g && w) switch(wParam) {
		case CM_EXIT:
			if(g->Command(CMD_CAN_CLOSE, 0L, 0L)) {
				SetWindowLong(hwnd, 0, 0L);
				SetWindowLong(hwnd, GWL_USERDATA, 0L);
				w->go = 0L;
				DestroyWindow(hwnd);
				}
			return 0;
		case CM_NEWINST:
			if(ShellCmd && ShellCmd[0])WinExec(ShellCmd, SW_SHOW);
			return 0;
		case CM_PASTE:
			w->MouseCursor(MC_WAIT, true);
			if(g->Id == GO_SPREADDATA) TestClipboard(g);
			else if(g->Id == GO_PAGE || g->Id == GO_GRAPH){
				if(CurrGO && CurrGO->Id == GO_TEXTFRAME && CurrGO->Command(CMD_PASTE, 0L, w));
				else TestClipboard(g);
				}
			g->Command(CMD_MOUSECURSOR, 0L, w);
			return 0;
		case CM_COPY:			case CM_CUT:		case CM_COPYGRAPH:
			EmptyClip();
			if(CurrGO && g->Id != GO_SPREADDATA) {
				if(CurrGO->Id == GO_POLYLINE || CurrGO->Id == GO_POLYGON || CurrGO->Id == GO_RECTANGLE
					|| CurrGO->Id == GO_ROUNDREC || CurrGO->Id == GO_ELLIPSE || CurrGO->Id == GO_BEZIER) {
					OpenClipboard(hwnd);
					CopyGraph(CurrGO, cf_rlpobj, w);		copy_obj = CurrGO;
					CloseClipboard();						return 0;
					}
				else if(CurrGO->Id == GO_TEXTFRAME) {
					if(CurrGO->Command(CMD_COPY, 0L, w)) return 0;
					}
				}	
			OpenClipboard(hwnd);
			if(g->Id == GO_SPREADDATA && g->Command(wParam == CM_CUT ? CMD_CUT : CMD_QUERY_COPY, 0L, w)) {
				SetClipboardData(CF_TEXT, NULL);
				SetClipboardData(CF_SYLK, NULL);
				SetClipboardData(cf_rlpxml, NULL);
				}
			else if(g->Id == GO_PAGE) {
				if(!g->hasTransp()) SetClipboardData(CF_ENHMETAFILE, NULL);
				SetClipboardData(CF_BITMAP, NULL);
				if(CurrGraph) {
					CopyGraph(CurrGraph, cf_rlpobj, w);		copy_obj = CurrGraph;
					}
				}
			else if (wParam == CM_CUT)return 0;
			else if(CurrGraph && CurrGraph->Id == GO_GRAPH){
				if(!g->hasTransp()) SetClipboardData(CF_ENHMETAFILE, NULL);
				SetClipboardData(CF_BITMAP, NULL);
				CopyGraph(CurrGraph, cf_rlpobj, w);			copy_obj = CurrGraph;
				}
			else if(g->Id == GO_GRAPH){
				if(!g->hasTransp()) SetClipboardData(CF_ENHMETAFILE, NULL);
				SetClipboardData(CF_BITMAP, NULL);
				CopyGraph(g, cf_rlpobj, w);					copy_obj = g;
				}
			CloseClipboard();
			return 0;
		case CM_UPDATE:
			g->Command(CMD_UPDATE, 0L, w);
			return 0;
		case CM_OPEN:
			g->Command(CMD_OPEN, 0L, w);
			return 0;
		case CM_FILE1:	case CM_FILE2:	case CM_FILE3:
		case CM_FILE4:	case CM_FILE5:	case CM_FILE6:
			return OpenFileFromHistory(w, g, wParam - CM_FILE1);
		case CM_FILLRANGE:
			g->Command(CMD_FILLRANGE, (void *)NULL, w);
			return 0;
		case CM_ABOUT:
			RLPlotInfo();
			return 0;
		case CM_ZOOM25:
			g->Command(CMD_ZOOM, &"25", w);
			return 0;
		case CM_ZOOM50:
			g->Command(CMD_ZOOM, &"50", w);
			return 0;
		case CM_ZOOM100:
			g->Command(CMD_ZOOM, &"100", w);
			return 0;
		case CM_ZOOM200:
			g->Command(CMD_ZOOM, &"200", w);
			return 0;
		case CM_ZOOM400:
			g->Command(CMD_ZOOM, &"400", w);
			return 0;
		case CM_ZOOMIN:
			g->Command(CMD_ZOOM, &"+", w);
			return 0;
		case CM_ZOOMOUT:
			g->Command(CMD_ZOOM, &"-", w);
			return 0;
		case CM_ZOOMFIT:
			g->Command(CMD_ZOOM, &"fit", w);
			return 0;
		case CM_ADDPLOT:
			g->Command(CMD_ADDPLOT, 0L, w);
			return 0;
		case CM_LEGEND:
			g->Command(CMD_LEGEND, 0L, w);
			return 0;
		case CM_LAYERS:
			g->Command(CMD_LAYERS, 0L, w);
			return 0;
		case CM_NEWGRAPH:
			g->Command(CMD_NEWGRAPH, 0L, w);
			return 0;
		case CM_NEWPAGE:
			g->Command(CMD_NEWPAGE, 0L, w);
			return 0;
		case CM_DELGRAPH:
			g->Command(CMD_DELGRAPH, 0L, w);
			return 0;
		case CM_SAVE:
			g->Command(CMD_SAVE, 0L, w);
			return 0;
		case CM_SAVEAS:
			g->Command(CMD_SAVEAS, 0L, w);
			return 0;
		case CM_REDRAW:
			if(w->Erase(defs.Color(COL_BG))) g->DoPlot(w);
			return 0;
		case CM_DELOBJ:
			if(CurrGO && CurrGO->parent && CurrGO->parent->
				Command(CMD_DELOBJ, (void*)CurrGO, w)) {
				CurrGO = 0L;
				if(w->Erase(defs.Color(COL_BG))) g->DoPlot(w);
				}
			else if(!CurrGO) InfoBox("No object selected!");
			return 0;
		case CM_EXPORT:
			OpenExportName(g, 0L);
			g->DoPlot(w);
			return 0;
		case CM_PRINT:
			if(g->Id == GO_SPREADDATA){
				g->Command(CMD_PRINT, 0L, Printer);
				return 0;
				}
			if(Printer && Printer->StartPage()) {
				SetCursor(LoadCursor(0L, IDC_WAIT));
				rec.left = Printer->un2ix(g->GetSize(SIZE_GRECT_LEFT));
				rec.right = Printer->un2ix(g->GetSize(SIZE_GRECT_RIGHT));
				rec.top = Printer->un2iy(g->GetSize(SIZE_GRECT_TOP));
				rec.bottom = Printer->un2iy(g->GetSize(SIZE_GRECT_BOTTOM));
				if(g->hasTransp()) {
					if((CopyBMP = new BitMapWin(rec.right-rec.left, rec.bottom-rec.top,
						Printer->hres, Printer->vres)) && CopyBMP->StartPage()) {
						CopyBMP->VPorg.fy = -Printer->co2fiy(g->GetSize(SIZE_GRECT_TOP));
						CopyBMP->VPorg.fx = -Printer->co2fix(g->GetSize(SIZE_GRECT_LEFT));
						g->DoPlot(CopyBMP);			CopyBMP->EndPage();
						Printer->CopyBitmap(rec.left, rec.top, CopyBMP, 0, 0, CopyBMP->DeskRect.right,
							CopyBMP->DeskRect.bottom, false);
						delete CopyBMP;						CopyBMP = NULL;
						Printer->EndPage();
						}
					}
				else {
					g->DoPlot(Printer);
					Printer->EndPage();
					}
				w->Erase(defs.Color(COL_BG));
				g->DoPlot(w);
				SetCursor(LoadCursor(0L, IDC_ARROW));
				}
			return 0;
		case CM_ADDROWCOL:
			g->Command(CMD_ADDROWCOL, (void *)NULL, w);
			return 0;
		case CM_DEFAULTS:
			g->Command(CMD_CONFIG, 0L, w);
			return 0;
		case CM_ADDAXIS:
			g->Command(CMD_ADDAXIS, 0L, w);
			return 0;
		case CM_T_STANDARD:
			cc = TM_STANDARD;
			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
			return 0;
		case CM_T_DRAW:
			cc = TM_DRAW;
			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
			return 0;
		case CM_T_POLYLINE:
			cc = TM_POLYLINE;
			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
			return 0;
		case CM_T_POLYGON:
			cc = TM_POLYGON;
			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
			return 0;
		case CM_T_RECTANGLE:
			cc = TM_RECTANGLE;
			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
			return 0;
		case CM_T_ROUNDREC:
			cc = TM_ROUNDREC;
			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
			return 0;
		case CM_T_ELLIPSE:
			cc = TM_ELLIPSE;
			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
			return 0;
		case CM_T_ARROW:
			cc = TM_ARROW;
			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
			return 0;
		case CM_T_TEXT:
			cc = TM_TEXT;
			g->Command(CMD_TOOLMODE, (void*)(& cc), w);
			return 0;
		case CM_DELKEY:		case CM_LEFTARRKEY:		case CM_RIGHTARRKEY:
		case CM_UPARRKEY:   case CM_DOWNARRKEY:		case CM_POS_FIRST:
		case CM_POS_LAST:	case CM_SHLEFT:			case CM_SHRIGHT:
		case CM_SHUP:		case CM_SHDOWN:			case CM_TAB:
		case CM_SHTAB:		case CM_SHPGUP:			case CM_SHPGDOWN:
			switch(wParam) {
			case CM_DELKEY:			cc = CMD_DELETE;		break;
			case CM_LEFTARRKEY:		cc = CMD_CURRLEFT;		break;
			case CM_RIGHTARRKEY:	cc = CMD_CURRIGHT;		break;
			case CM_UPARRKEY:		cc = CMD_CURRUP;		break;
			case CM_DOWNARRKEY:		cc = CMD_CURRDOWN;		break;
			case CM_POS_FIRST:		cc = CMD_POS_FIRST;		break;
			case CM_POS_LAST:		cc = CMD_POS_LAST;		break;
			case CM_SHLEFT:			cc = CMD_SHIFTLEFT;		break;
			case CM_SHRIGHT:		cc = CMD_SHIFTRIGHT;	break;
			case CM_SHUP:			cc = CMD_SHIFTUP;		break;
			case CM_SHDOWN:			cc = CMD_SHIFTDOWN;		break;
			case CM_TAB:			cc = CMD_TAB;			break;
			case CM_SHTAB:			cc = CMD_SHTAB;			break;
			case CM_SHPGUP:			cc = CMD_SHPGUP;		break;
			case CM_SHPGDOWN:		cc = CMD_SHPGDOWN;		break;
				}
			g->Command(cc, (void *)NULL, w);
			return 0;
		case CM_PGUP:		case CM_PGDOWN:
			g->Command(wParam == CM_PGUP ? CMD_PAGEUP : CMD_PAGEDOWN, 0L, w);
			return 0;
		case CM_UNDO:
			g->Command(CMD_UNDO, 0L, w);
			return 0;
		case CM_INSROW:
			g->Command(CMD_INSROW, 0L, w);
			return 0;
		case CM_INSCOL:
			g->Command(CMD_INSCOL, 0L, w);
			return 0;
		case CM_DELROW:
			g->Command(CMD_DELROW, 0L, w);
			return 0;
		case CM_DELCOL:
			g->Command(CMD_DELCOL, 0L, w);
			return 0;
		case CM_SMPLSTAT:
			if(g->data) rep_samplestats(g, g->data);
			return 0;
		case CM_REPCMEANS:
			if(g->data) rep_compmeans(g, g->data);
			return 0;
		case CM_REPANOV:
			if(g->data) rep_anova(g, g->data);
			return 0;
		case CM_REPBDANOV:
			if(g->data) rep_bdanova(g, g->data);
			return 0;
		case CM_REPTWANR:
			if(g->data) rep_twoway_anova(g, g->data);
			return 0;
		case CM_REPKRUSKAL:
			if(g->data) rep_kruskal(g, g->data);
			return 0;
		case CM_REPTWANOV:
			if(g->data) rep_twanova(g, g->data);
			return 0;
		case CM_REPFRIEDM:
			if(g->data) rep_fmanova(g, g->data);
			return 0;
		case CM_REPREGR:
			if(g->data) rep_regression(g, g->data);
			return 0;
		case CM_ROBUSTLINE:
			if(g->data) rep_robustline(g, g->data);
			return 0;
		case CM_CORRELM:
			if(g->data) rep_correl(g, g->data, 0);
			return 0;
		case CM_CORRELT:
			if(g->data) rep_correl(g, g->data, 1);
			return 0;
		case CM_REPTWOWAY:
			if(g->data) rep_twowaytable(g, g->data);
			return 0;
		default:
#ifdef USE_WIN_SECURE
			sprintf_s(TmpTxt, TMP_TXT_SIZE, "Command 0x%x (%d)\nreceived", wParam & 0xffff,
				wParam & 0xffff);
#else
			sprintf(TmpTxt, "Command 0x%x (%d)\nreceived", wParam & 0xffff,
				wParam & 0xffff);
#endif
			MessageBox(hwnd, TmpTxt, "Info", MB_OK | MB_ICONINFORMATION);
		}
		return 0;
	case WM_CLOSE:
		if(g && g->Command(CMD_CAN_CLOSE, 0L, 0L)) {
			SetWindowLong(hwnd, 0, 0L);		SetWindowLong(hwnd, GWL_USERDATA, 0L);
			w->go = 0L;
			if(g->parent) g->parent->Command(CMD_DELOBJ, g, w);
			else DestroyWindow(hwnd);
			}
		else if(!g) DestroyWindow(hwnd);
		return 0;
	case WM_DESTROY:
		if(hwnd == MainWnd)PostQuitMessage(0);
		break;
	case WM_PAINT:
		dc = BeginPaint(hwnd, &ps);
		if(w && dc) w->UpdateRect(dc, ps.rcPaint);
		EndPaint(hwnd, &ps);
		break;
	}
	return (long)DefWindowProc(hwnd, message, wParam, lParam);
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Dialog window: Windows specific
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
const char dlgname[] = "RLDLGWIN";

LRESULT FAR PASCAL DlgWndProc(HWND hwnd, UINT message, UINT wParam, LONG lParam)
{
	OutputWin *w;
	tag_DlgObj *d;
	PAINTSTRUCT ps;
	HDC dc;
	MouseEvent mev;
	int i, cc;

	d = (tag_DlgObj *) reflptr(GetWindowLong(hwnd, 0));
	w = (OutputWin *) reflptr(GetWindowLong(hwnd, GWL_USERDATA));
	switch(message) {
	case WM_CREATE:
		break;
	case WM_KILLFOCUS:
		HideTextCursorObj(w);
		if(d) d->Command(CMD_ENDDIALOG, NULL, w);
		return 0;
	case WM_TIMER:
		if(d) d->Command(CMD_ENDDIALOG, d, w);
		return 0;
	case WM_DESTROY:	case WM_CLOSE:
		if(d) {
			d->Command(CMD_UNLOCK, 0L, w);
			d->Command(CMD_ENDDIALOG, 0L, w);
			SetWindowLong(hwnd, 0, NULL);
			noreflptr(GetWindowLong(hwnd, 0));
			}
		if(w) {
			w->hWnd = 0L;
			SetWindowLong(hwnd, GWL_USERDATA, NULL);
			noreflptr(GetWindowLong(hwnd, GWL_USERDATA));
			delete w;
			}
		break;
	case WM_CHAR:
		if(0x09 == (cc = wParam & 0xff)) break;		//ignore Tab
		if(cc == 0x03) return d->Command(CMD_COPY, 0L, w);	//^c copy
		if(cc == 0x16) return d->Command(CMD_PASTE, 0L, w);	//^v paste
		if(d && w) d->Command(CMD_ADDCHAR, (void *)(& cc), w);
		break;
	case WM_LBUTTONDOWN:	case WM_LBUTTONDBLCLK:
		HideTextCursor();
	case WM_RBUTTONUP:		case WM_LBUTTONUP:		case WM_MOUSEMOVE:
		mev.x = LOWORD(lParam);		mev.y = HIWORD(lParam);		mev.StateFlags = 0;
		if(wParam & MK_LBUTTON) mev.StateFlags |= 1;
		if(wParam & MK_MBUTTON) mev.StateFlags |= 2;
		if(wParam & MK_RBUTTON) mev.StateFlags |= 4;
		if(wParam & MK_SHIFT) mev.StateFlags |= 8;
		if(wParam & MK_CONTROL) mev.StateFlags |= 16;
		if(message == WM_MOUSEMOVE) mev.Action = MOUSE_MOVE;
		else if(message == WM_LBUTTONDOWN) mev.Action = MOUSE_LBDOWN;
		else if(message == WM_LBUTTONUP) mev.Action = MOUSE_LBUP;
		else if(message == WM_RBUTTONUP) mev.Action = MOUSE_RBUP;
		else if(message == WM_LBUTTONDBLCLK) mev.Action = MOUSE_LBDOUBLECLICK;
		if(d && w) d->Command(CMD_MOUSE_EVENT, (void *)&mev, w);
		break;
	case WM_COMMAND:
		wParam &= 0xffff;
		i = 0;
		switch(wParam) {
		case CM_DELKEY:			i = CMD_DELETE;		break;
		case CM_LEFTARRKEY:		i = CMD_CURRLEFT;	break;
		case CM_RIGHTARRKEY:	i = CMD_CURRIGHT;	break;
		case CM_UPARRKEY:		i = CMD_CURRUP;		break;
		case CM_DOWNARRKEY:		i = CMD_CURRDOWN;	break;
		case CM_TAB:			i = CMD_TAB;		break;
		case CM_SHTAB:			i = CMD_SHTAB;		break;
		case CM_POS_FIRST:		i = CMD_POS_FIRST;	break;
		case CM_POS_LAST:		i = CMD_POS_LAST;	break;
		case CM_SHLEFT:			i = CMD_SHIFTLEFT;	break;
		case CM_SHRIGHT:		i = CMD_SHIFTRIGHT;	break;
		case CM_UNDO:			i = CMD_UNDO;		break;
			}
		if(i && d && w) d->Command(i, 0L, w);
		return 0;
	case WM_PAINT:
		dc = BeginPaint(hwnd, &ps);
		if(w && dc){
      		w->HideMark();		w->UpdateRect(dc, ps.rcPaint);
			}
		EndPaint(hwnd, &ps);
		break;
	}
	return DefWindowProc(hwnd, message, wParam, lParam);
}

void *CreateDlgWnd(char *title, int x, int y, int width, int height, tag_DlgObj *d, DWORD flags)
{
	WNDCLASS wndclass;
	OutputWin *w;
	HWND hDlg, hFoc;
	RECT rec, BoxRec, DeskRect;
	DWORD ws;

	wndclass.style = CS_BYTEALIGNWINDOW | CS_DBLCLKS;
	wndclass.lpfnWndProc = DlgWndProc;
	wndclass.cbClsExtra = 0;
	wndclass.cbWndExtra = sizeof(tag_DlgObj *);
	wndclass.hInstance = hInstance;
	wndclass.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_EVAL));
	wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
	wndclass.hbrBackground = NULL;
	wndclass.lpszMenuName = NULL;
	wndclass.lpszClassName = dlgname;
	RegisterClass(&wndclass);
	hFoc = GetFocus();
	if(hFoc) {
		GetWindowRect(hFoc, &rec);
		x += rec.left;
		y += rec.top;
		}
	ws = WS_POPUP | WS_VISIBLE | WS_CLIPSIBLINGS;
	if(!(flags & 0x2)) ws |= WS_CAPTION;
	hDlg = CreateWindow(dlgname, title,	ws,
		x, y, width, height, GetFocus(), NULL, hInstance, NULL);
	w = new OutputWin(0L, hDlg);
	w->units = defs.cUnits;
	if(hDlg && w && w->Erase(0x00e0e0e0L)) {
		((DlgRoot*)d)->hDialog = hDlg;
		SetWindowLong(hDlg, GWL_USERDATA, lptrref(w));
		SetWindowLong(hDlg, 0, lptrref(d));
		if(flags & 0x01) {					//center on screen
			GetWindowRect(hDlg, &BoxRec);
			GetClientRect(GetDesktopWindow(), &DeskRect);
			SetWindowPos(hDlg, HWND_TOPMOST, (DeskRect.right -DeskRect.left)/2 -
				(BoxRec.right - BoxRec.left)/2, (DeskRect.bottom -DeskRect.top)/2 -
				(BoxRec.bottom- BoxRec.top)/2, BoxRec.right - BoxRec.left,
				BoxRec.bottom - BoxRec.top, 0);
			}
		if(flags & 0x08)			SetTimer(hDlg, 1, 100, 0L);
		UpdateWindow(hDlg);			d->DoPlot(w);
		ShowWindow(hDlg, SW_SHOW);
		}
	else {
		if(w) delete (w);			return 0L;
		}
	return hDlg;
}

void LoopDlgWnd() 	//keep message processing running
{
	MSG	msg;
	
	GetMessage(&msg, NULL, 0, 0);	TranslateMessage(&msg);
	TranslateAccelerator(msg.hwnd, accel, &msg);
	DispatchMessage(&msg);
}

void CloseDlgWnd(void *hDlg)
{
	HideCopyMark();
	if(hDlg) SendMessage((HWND)hDlg, WM_CLOSE, 0, 0);
}

void ShowDlgWnd(void *hDlg)
{
	ShowWindow((HWND)hDlg, SW_SHOW);
	SetFocus((HWND)hDlg);
}

void ResizeDlgWnd(void *hDlg, int w, int h)
{
	HWND hwnd = (HWND)hDlg;
	RECT rc;

	GetWindowRect(hwnd, &rc);
	SetWindowPos(hwnd, HWND_TOPMOST, rc.left, rc.top, w, h, 0);
	ShowDlgWnd(hDlg);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// OS independent interface to Windows specific classes
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
anyOutput *NewDispClass(GraphObj *g)
{
	return new OutputWin(g, 0L);
}

bool DelDispClass(anyOutput *w)
{
	if (w) delete (OutputWin*) w;
	return true;
}

anyOutput *NewBitmapClass(int w, int h, double hr, double vr)
{
	return new BitMapWin(w, h, hr, vr);
}

bool DelBitmapClass(anyOutput *w)
{
	if (w) delete (BitMapWin*) w;
	return true;
}
