/*
 * $Id: wgraph.c,v 1.144.2.8 2013/06/08 11:54:25 markisch Exp $
 */

/* GNUPLOT - win/wgraph.c */
/*[
 * Copyright 1992, 1993, 1998, 2004   Maurice Castro, Russell Lang
 *
 * Permission to use, copy, and distribute this software and its
 * documentation for any purpose with or without fee is hereby granted,
 * provided that the above copyright notice appear in all copies and
 * that both that copyright notice and this permission notice appear
 * in supporting documentation.
 *
 * Permission to modify the software is granted, but not the right to
 * distribute the complete modified source code.  Modifications are to
 * be distributed as patches to the released version.  Permission to
 * distribute binaries produced by compiling modified sources is granted,
 * provided you
 *   1. distribute the corresponding source modifications from the
 *    released version in the form of a patch file along with the binaries,
 *   2. add special version identification to distinguish your version
 *    in addition to the base release version number,
 *   3. provide your name and address as the primary contact for the
 *    support of your modified version, and
 *   4. retain our contact information in regard to use of the base
 *    software.
 * Permission to distribute the released version of the source code along
 * with corresponding source modifications in the form of a patch file is
 * granted with same provisions 2 through 4 for binary distributions.
 *
 * This software is provided "as is" without express or implied warranty
 * to the extent permitted by applicable law.
]*/

/*
 * AUTHORS
 *
 *   Maurice Castro
 *   Russell Lang
 */

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

#define STRICT
#ifdef USE_MOUSE
/* shige: for mouse wheel, BM: GetConsoleWindow */
#define _WIN32_WINNT 0x0500
#endif
/* BM: for AlphaBlend/TransparentBlt */
#define WINVER 0x0501
/* BM: for Toolbars */
#define _WIN32_IE 0x0501
#include <windows.h>
#include <windowsx.h>
#include <commdlg.h>
#include <commctrl.h>
#ifndef __MSC__
# include <mem.h>
#endif
#include <stdio.h>
#include <string.h>
#include "winmain.h"
#include "wresourc.h"
#include "wcommon.h"
#include "term_api.h"         /* for enum JUSTIFY */
#ifdef USE_MOUSE
# include "gpexecute.h"
# include "mouse.h"
# include "command.h"
#endif
#include "color.h"
#include "getcolor.h"
#ifdef HAVE_GDIPLUS
#include "wgdiplus.h"
#endif
#include "fit.h"

#ifdef USE_MOUSE
/* Petr Mikulik, February 2001
 * Declarations similar to src/os2/gclient.c -- see section
 * "PM: Now variables for mouse" there in.
 */

/* Status of the ruler */
static struct Ruler {
    TBOOLEAN on;		/* ruler active ? */
    int x, y;			/* ruler position */
} ruler = {FALSE,0,0,};

/* Status of the line from ruler to cursor */
static struct RulerLineTo {
    TBOOLEAN on;		/* ruler line active ? */
    int x, y;			/* ruler line end position (previous cursor position) */
} ruler_lineto = {FALSE,0,0,};

/* Status of zoom box */
static struct Zoombox {
    TBOOLEAN on;		/* set to TRUE during zooming */
    POINT from, to;		/* corners of the zoom box */
    LPCSTR text1, text2;	/* texts in the corners (i.e. positions) */
} zoombox = { FALSE, {0,0}, {0,0}, NULL, NULL };

/* Pointer definitions */
HCURSOR hptrDefault, hptrCrossHair, hptrScaling, hptrRotating, hptrZooming, hptrCurrent;

/* Mouse support routines */
static void	Wnd_exec_event(LPGW lpgw, LPARAM lparam, char type, int par1);
static void	Wnd_refresh_zoombox(LPGW lpgw, LPARAM lParam);
static void	Wnd_refresh_ruler_lineto(LPGW lpgw, LPARAM lParam);
static void	GetMousePosViewport(LPGW lpgw, int *mx, int *my);
static void	Draw_XOR_Text(LPGW lpgw, const char *text, size_t length, int x, int y);
static void	UpdateStatusLine(LPGW lpgw, const char text[]);
static void	UpdateToolbar(LPGW lpgw);
static void	DrawRuler(LPGW lpgw);
static void	DrawRulerLineTo(LPGW lpgw);
static void	DrawZoomBox(LPGW lpgw);
static void	LoadCursors(LPGW lpgw);
static void	DestroyCursors(LPGW lpgw);
#endif /* USE_MOUSE */

/* ================================== */

#define WGDEFCOLOR 15
COLORREF wginitcolor[WGDEFCOLOR] =  {
	RGB(255,0,0),	/* red */
	RGB(0,255,0),	/* green */
	RGB(0,0,255),	/* blue */
	RGB(255,0,255), /* magenta */
	RGB(0,0,128),	/* dark blue */
	RGB(128,0,0),	/* dark red */
	RGB(0,128,128),	/* dark cyan */
	RGB(0,0,0),		/* black */
	RGB(128,128,128), /* grey */
	RGB(0,128,64),	/* very dark cyan */
	RGB(128,128,0), /* dark yellow */
	RGB(128,0,128),	/* dark magenta */
	RGB(192,192,192), /* light grey */
	RGB(0,255,255),	/* cyan */
	RGB(255,255,0),	/* yellow */
};
#define WGDEFSTYLE 5
int wginitstyle[WGDEFSTYLE] = {PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT};

/* Maximum number of GWOPBLK arrays to be remembered. */
/* HBB 20010218: moved here from wgnuplib.h: other parts of the program don't
 * need to know about it */
#define GWOPMAX 4096

#define MINMAX(a,val,b) (((val) <= (a)) ? (a) : ((val) <= (b) ? (val) : (b)))

/* bitmaps for filled boxes (ULIG) */
/* zeros represent the foreground color and ones represent the background color */
static unsigned char pattern_bitmaps[][16] = {
  {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
   0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* no fill */
  {0xFE, 0xFE, 0x7D, 0x7D, 0xBB, 0xBB, 0xD7, 0xD7,
   0xEF, 0xEF, 0xD7, 0xD7, 0xBB, 0xBB, 0x7D, 0x7D}, /* cross-hatch (1) */
  {0x77, 0x77, 0xAA, 0xAA, 0xDD, 0xDD, 0xAA, 0xAA,
   0x77, 0x77, 0xAA, 0xAA, 0xDD, 0xDD, 0xAA, 0xAA}, /* double cross-hatch (2) */
  {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
   0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* solid fill (3) */
  {0x7F, 0x7F, 0xBF, 0xBF, 0xDF, 0xDF, 0xEF, 0xEF,
   0xF7, 0xF7, 0xFB, 0xFB, 0xFD, 0xFD, 0xFE, 0xFE}, /* diagonals (4) */
  {0xFE, 0xFE, 0xFD, 0xFD, 0xFB, 0xFB, 0xF7, 0xF7,
   0xEF, 0xEF, 0xDF, 0xDF, 0xBF, 0xBF, 0x7F, 0x7F}, /* diagonals (5) */
  {0x77, 0x77, 0x77, 0x77, 0xBB, 0xBB, 0xBB, 0xBB,
   0xDD, 0xDD, 0xDD, 0xDD, 0xEE, 0xEE, 0xEE, 0xEE}, /* steep diagonals (6) */
  {0xEE, 0xEE, 0xEE, 0xEE, 0xDD, 0xDD, 0xDD, 0xDD,
   0xBB, 0xBB, 0xBB, 0xBB, 0x77, 0x77, 0x77, 0x77}  /* steep diagonals (7) */
#if (0)
 ,{0xFC, 0xFC, 0xF3, 0xF3, 0xCF, 0xCF, 0x3F, 0x3F,
   0xFC, 0xFC, 0xF3, 0xF3, 0xCF, 0xCF, 0x3F, 0x3F}, /* shallow diagonals (old 5) */
  {0x3F, 0x3F, 0xCF, 0xCF, 0xF3, 0xF3, 0xFC, 0xFC,
   0x3F, 0x3F, 0xCF, 0xCF, 0xF3, 0xF3, 0xFC, 0xFC}  /* shallow diagonals (old 6) */
#endif
};
#define pattern_num (sizeof(pattern_bitmaps)/(sizeof(*pattern_bitmaps)))
static HBRUSH pattern_brush[pattern_num];
static BITMAP pattern_bitdata[pattern_num];
static HBITMAP pattern_bitmap[pattern_num];

static TBOOLEAN brushes_initialized = FALSE;


/* Internal state of enhanced text processing.
   Do not access outside draw_enhanced_text, GraphEnhancedOpen or
   GraphEnhancedFlush.
*/
static struct {
	LPGW lpgw;           /* graph window */
	HDC  hdc;            /* device context */
	LPRECT rect;         /* rect to update */
	BOOL opened_string;  /* started processing of substring? */
	BOOL show;           /* print this substring? */
	int overprint;       /* overprint flag */
	BOOL widthflag;      /* FALSE for zero width boxes */
	BOOL sizeonly;       /* only measure length of substring? */
	double base;         /* current baseline */
	int xsave, ysave;    /* save text position for overprinted text */
	int x, y;            /* current text position */
	char fontname[MAXFONTNAME]; /* current font name */
	double fontsize;     /* current font size */
	int totalwidth;      /* total width of printed text */
	double res_scale;    /* scaling due to different resolution (printers) */
} enhstate;


/* ================================== */

/* prototypes for module-local functions */

LRESULT CALLBACK WndGraphProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
BOOL CALLBACK LineStyleDlgProc(HWND hdlg, UINT wmsg, WPARAM wparam, LPARAM lparam);

static void	DestroyBlocks(LPGW lpgw);
static BOOL	AddBlock(LPGW lpgw);
static void	StorePen(LPGW lpgw, int i, COLORREF ref, int colorstyle, int monostyle);
static void	MakePens(LPGW lpgw, HDC hdc);
static void	DestroyPens(LPGW lpgw);
static void	Wnd_GetTextSize(HDC hdc, LPCSTR str, size_t len, int *cx, int *cy);
static void	GetPlotRect(LPGW lpgw, LPRECT rect);
static void	MakeFonts(LPGW lpgw, LPRECT lprect, HDC hdc);
static void	DestroyFonts(LPGW lpgw);
static void	SetFont(LPGW lpgw, HDC hdc);
static void	SelFont(LPGW lpgw);
static LPWSTR	UnicodeText(const char *str, enum set_encoding_id encoding);
static void	dot(HDC hdc, int xdash, int ydash);
static unsigned int WDPROC GraphGetTextLength(LPGW lpgw, HDC hdc, LPCSTR text);
static int	draw_enhanced_text(LPGW lpgw, HDC hdc, LPRECT rect, int x, int y, char * str);
static void	draw_text_justify(HDC hdc, int justify);
static void	draw_put_text(LPGW lpgw, HDC hdc, int x, int y, char * str);
static void	drawgraph(LPGW lpgw, HDC hdc, LPRECT rect);
static void	CopyClip(LPGW lpgw);
static void	SaveAsEMF(LPGW lpgw);
static void	CopyPrint(LPGW lpgw);
static void	WriteGraphIni(LPGW lpgw);
static char *	GraphDefaultFont(void);
static void	ReadGraphIni(LPGW lpgw);
static COLORREF	GetColor(HWND hwnd, COLORREF ref);
static void	UpdateColorSample(HWND hdlg);
static BOOL	LineStyle(LPGW lpgw);
static void	GraphChangeFont(LPGW lpgw, LPCSTR font, int fontsize, HDC hdc, RECT rect);


/* ================================== */

/* Helper functions for GraphOp(): */

/* destroy memory blocks holding graph operations */
static void
DestroyBlocks(LPGW lpgw)
{
    struct GWOPBLK *this, *next;
    struct GWOP *gwop;
    unsigned int i;

	this = lpgw->gwopblk_head;
	while (this != NULL) {
		next = this->next;
		if (!this->gwop) {
			this->gwop = (struct GWOP *)GlobalLock(this->hblk);
		}
		if (this->gwop) {
			/* free all text strings within this block */
			gwop = this->gwop;
			for (i=0; i<GWOPMAX; i++) {
				if (gwop->htext)
					LocalFree(gwop->htext);
				gwop++;
			}
		}
		GlobalUnlock(this->hblk);
		GlobalFree(this->hblk);
		LocalFreePtr(this);
		this = next;
	}
	lpgw->gwopblk_head = NULL;
	lpgw->gwopblk_tail = NULL;
	lpgw->nGWOP = 0;
}


/* add a new memory block for graph operations */
/* returns TRUE if block allocated */
static BOOL
AddBlock(LPGW lpgw)
{
	HGLOBAL hblk;
	struct GWOPBLK *next, *this;

	/* create new block */
	next = (struct GWOPBLK *)LocalAllocPtr(LHND, sizeof(struct GWOPBLK) );
	if (next == NULL)
		return FALSE;
	hblk = GlobalAlloc(GHND, GWOPMAX*sizeof(struct GWOP));
	if (hblk == NULL)
		return FALSE;
	next->hblk = hblk;
	next->gwop = (struct GWOP *)NULL;
	next->next = (struct GWOPBLK *)NULL;
	next->used = 0;

	/* attach it to list */
	this = lpgw->gwopblk_tail;
	if (this == NULL) {
		lpgw->gwopblk_head = next;
	} else {
		this->next = next;
		this->gwop = (struct GWOP *)NULL;
		GlobalUnlock(this->hblk);
	}
	lpgw->gwopblk_tail = next;
	next->gwop = (struct GWOP *)GlobalLock(next->hblk);
	if (next->gwop == (struct GWOP *)NULL)
		return FALSE;

	return TRUE;
}


void WDPROC
GraphOp(LPGW lpgw, UINT op, UINT x, UINT y, LPCSTR str)
{
	if (str)
		GraphOpSize(lpgw, op, x, y, str, _fstrlen(str)+1);
	else
		GraphOpSize(lpgw, op, x, y, NULL, 0);
}


void WDPROC
GraphOpSize(LPGW lpgw, UINT op, UINT x, UINT y, LPCSTR str, DWORD size)
{
	struct GWOPBLK *this;
	struct GWOP *gwop;
	char *npstr;

	this = lpgw->gwopblk_tail;
	if ( (this==NULL) || (this->used >= GWOPMAX) ) {
		/* not enough space so get new block */
		if (!AddBlock(lpgw))
			return;
		this = lpgw->gwopblk_tail;
	}
	gwop = &this->gwop[this->used];
	gwop->op = op;
	gwop->x = x;
	gwop->y = y;
	gwop->htext = 0;
	if (str) {
		gwop->htext = LocalAlloc(LHND, size);
		npstr = LocalLock(gwop->htext);
		if (gwop->htext && (npstr != (char *)NULL))
			memcpy(npstr, str, size);
		LocalUnlock(gwop->htext);
	}
	this->used++;
	lpgw->nGWOP++;
	return;
}

/* ================================== */

/* Initialize the LPGW struct:
 * set default values and read options from ini file */
void WDPROC
GraphInitStruct(LPGW lpgw)
{
	if (!lpgw->initialized) {
		lpgw->initialized = TRUE;
		if (lpgw != listgraphs) {
			char titlestr[100];

			/* copy important fields from window #0 */
			LPGW graph0 = listgraphs;
			lpgw->IniFile = graph0->IniFile;
			lpgw->hInstance = graph0->hInstance;
			lpgw->hPrevInstance = graph0->hPrevInstance;
			lpgw->lptw = graph0->lptw;

			/* window title */
			sprintf(titlestr, "%s %i", WINGRAPHTITLE, lpgw->Id);
			lpgw->Title = strdup(titlestr);
		} else {
			lpgw->Title = strdup(WINGRAPHTITLE);
		}
		lpgw->sampling = 1;
		lpgw->fontscale = 1.;
		lpgw->linewidth = 1.;
		lpgw->color = TRUE;
		lpgw->dashed = FALSE;
		lpgw->IniSection = "WGNUPLOT";
		lpgw->fontsize = WINFONTSIZE;
		lpgw->maxkeyboxes = 0;
		lpgw->keyboxes = 0;
		lpgw->maxhideplots = MAXPLOTSHIDE;
		lpgw->hideplot = (BOOL *) calloc(MAXPLOTSHIDE, sizeof(BOOL));
		ReadGraphIni(lpgw);
	}
}


/* Prepare Graph window for being displayed by windows, update
 * the window's menus and show it */
void WDPROC
GraphInit(LPGW lpgw)
{
	HMENU sysmenu;
	WNDCLASS wndclass;
	char buf[MAX_PATH];

	if (!lpgw->hPrevInstance) {
		wndclass.style = CS_HREDRAW | CS_VREDRAW;
		wndclass.lpfnWndProc = WndGraphProc;
		wndclass.cbClsExtra = 0;
		wndclass.cbWndExtra = 2 * sizeof(void *);
		wndclass.hInstance = lpgw->hInstance;
		wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
		wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
		wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
		wndclass.lpszMenuName = NULL;
		wndclass.lpszClassName = szGraphClass;
		RegisterClass(&wndclass);
	}

	lpgw->hWndGraph = CreateWindow(szGraphClass, lpgw->Title,
		WS_OVERLAPPEDWINDOW,
		lpgw->Origin.x, lpgw->Origin.y,
		lpgw->Size.x, lpgw->Size.y,
		NULL, NULL, lpgw->hInstance, lpgw);

	if (lpgw->hWndGraph)
		SetClassLong(lpgw->hWndGraph, GCL_HICON,
			(LONG) LoadIcon(lpgw->hInstance, "GRPICON"));

	lpgw->hStatusbar = CreateWindowEx(0, STATUSCLASSNAME, (LPSTR)NULL,
				  WS_CHILD | SBARS_SIZEGRIP,
				  0, 0, 0, 0,
				  lpgw->hWndGraph, (HMENU)ID_GRAPHSTATUS,
				  lpgw->hInstance, lpgw);
	if (lpgw->hStatusbar) {
	    RECT rect;
	    /* auto-adjust size */
	    SendMessage(lpgw->hStatusbar, WM_SIZE, (WPARAM)0, (LPARAM)0);
	    ShowWindow(lpgw->hStatusbar, TRUE);

	    /* make room */
	    GetClientRect(lpgw->hStatusbar, &rect);
	    lpgw->StatusHeight = rect.bottom - rect.top;
	}

	/* create a toolbar */
	lpgw->hToolbar = CreateWindowEx(0, TOOLBARCLASSNAME, NULL,
		WS_CHILD | TBSTYLE_LIST | TBSTYLE_TOOLTIPS, // TBSTYLE_WRAPABLE
		0, 0, 0, 0,
		lpgw->hWndGraph, (HMENU)ID_TOOLBAR, lpgw->hInstance, lpgw);

	if (lpgw->hToolbar != NULL) {
		RECT rect;
		int i;
		TBBUTTON button;
		BOOL ret;
		char buttontext[10];
		unsigned num = 0;

		SendMessage(lpgw->hToolbar, TB_SETBITMAPSIZE, (WPARAM)0, (LPARAM)((16<<16) + 16));
		/* load standard toolbar icons: standard, history & view */
		SendMessage(lpgw->hToolbar, TB_LOADIMAGES, (WPARAM)IDB_STD_SMALL_COLOR, (LPARAM)HINST_COMMCTRL);
		SendMessage(lpgw->hToolbar, TB_LOADIMAGES, (WPARAM)IDB_HIST_SMALL_COLOR, (LPARAM)HINST_COMMCTRL);
		SendMessage(lpgw->hToolbar, TB_LOADIMAGES, (WPARAM)IDB_VIEW_SMALL_COLOR, (LPARAM)HINST_COMMCTRL);

		/* create buttons */
		SendMessage(lpgw->hToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);

		ZeroMemory(&button, sizeof(button));
		button.fsState = TBSTATE_ENABLED;
		button.fsStyle = BTNS_AUTOSIZE | BTNS_SHOWTEXT | BTNS_NOPREFIX;
		button.iString = 0;

		/* copy */
		button.iBitmap = STD_COPY;
		button.idCommand = M_COPY_CLIP;
		ret = SendMessage(lpgw->hToolbar, TB_INSERTBUTTON, (WPARAM)num++, (LPARAM)&button);

		/* print */
		button.iBitmap = STD_PRINT;
		button.idCommand = M_PRINT;
		ret = SendMessage(lpgw->hToolbar, TB_INSERTBUTTON, (WPARAM)num++, (LPARAM)&button);

		/* save as EMF */
		button.iBitmap = STD_FILESAVE;
		button.idCommand = M_SAVE_AS_EMF;
		ret = SendMessage(lpgw->hToolbar, TB_INSERTBUTTON, (WPARAM)num++, (LPARAM)&button);

		/* options */
		button.iBitmap = STD_PROPERTIES;
		button.idCommand = 0; /* unused */
		button.iString = (INT_PTR) "Options";
		button.fsStyle = BTNS_AUTOSIZE | BTNS_SHOWTEXT | BTNS_NOPREFIX | BTNS_WHOLEDROPDOWN;
		ret = SendMessage(lpgw->hToolbar, TB_INSERTBUTTON, (WPARAM)num++, (LPARAM)&button);

		/* TODO: Add the following buttons:
			replot/refresh, toggle grid(?), previous/next zoom, autoscale, help
		*/

		button.fsStyle = BTNS_AUTOSIZE | BTNS_NOPREFIX | BTNS_SEP;
		ret = SendMessage(lpgw->hToolbar, TB_INSERTBUTTON, (WPARAM)num++, (LPARAM)&button);

		/* hide grid */
		button.iBitmap = STD_CUT;
		button.idCommand = M_HIDEGRID;
		button.fsStyle = BTNS_AUTOSIZE | BTNS_SHOWTEXT | BTNS_NOPREFIX | BTNS_CHECK;
		button.iString = (INT_PTR) "Grid";
		ret = SendMessage(lpgw->hToolbar, TB_INSERTBUTTON, (WPARAM)num++, (LPARAM)&button);

		/* hide graphs */
		for (i = 0; i < MAXPLOTSHIDE; i++) {
			button.iBitmap = STD_CUT;
			button.idCommand = M_HIDEPLOT + i;
			sprintf(buttontext, "%i", i + 1);
			button.iString = (UINT_PTR) buttontext;
			button.dwData = i;
			ret = SendMessage(lpgw->hToolbar, TB_INSERTBUTTON, (WPARAM)num++, (LPARAM)&button);
		}

		/* auto-resize and show */
		SendMessage(lpgw->hToolbar, TB_AUTOSIZE, (WPARAM)0, (LPARAM)0);
		ShowWindow(lpgw->hToolbar, TRUE);

		/* make room */
		GetClientRect(lpgw->hToolbar, &rect);
		lpgw->ToolbarHeight = rect.bottom - rect.top + 1;
	}

	lpgw->hPopMenu = CreatePopupMenu();
	/* actions */
	AppendMenu(lpgw->hPopMenu, MF_STRING, M_COPY_CLIP, "&Copy to Clipboard (Ctrl+C)");
	AppendMenu(lpgw->hPopMenu, MF_STRING, M_SAVE_AS_EMF, "&Save as EMF... (Ctrl+S)");
	AppendMenu(lpgw->hPopMenu, MF_STRING, M_PRINT, "&Print...");
	/* settings */
	AppendMenu(lpgw->hPopMenu, MF_SEPARATOR, 0, NULL);
	AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->graphtotop ? MF_CHECKED : MF_UNCHECKED),
		M_GRAPH_TO_TOP, "Bring to &Top");
	AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->color ? MF_CHECKED : MF_UNCHECKED),
		M_COLOR, "C&olor");
	AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->doublebuffer ? MF_CHECKED : MF_UNCHECKED),
		M_DOUBLEBUFFER, "&Double buffer");
	AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->oversample ? MF_CHECKED : MF_UNCHECKED),
		M_OVERSAMPLE, "O&versampling");
#ifdef HAVE_GDIPLUS
	AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->antialiasing ? MF_CHECKED : MF_UNCHECKED),
		M_ANTIALIASING, "&Antialiasing");
	AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->polyaa ? MF_CHECKED : MF_UNCHECKED),
		M_POLYAA, "Antialiasing of Pol&ygons");
	AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->patternaa ? MF_CHECKED : MF_UNCHECKED),
		M_PATTERNAA, "Antialiasing of Patt&erns");
#endif
	AppendMenu(lpgw->hPopMenu, MF_STRING, M_BACKGROUND, "&Background...");
	AppendMenu(lpgw->hPopMenu, MF_STRING, M_CHOOSE_FONT, "Choose &Font...");
	AppendMenu(lpgw->hPopMenu, MF_STRING, M_LINESTYLE, "&Line Styles...");
	/* save settings */
	AppendMenu(lpgw->hPopMenu, MF_SEPARATOR, 0, NULL);
	if (lpgw->IniFile != (LPSTR)NULL) {
		wsprintf(buf,"&Update %s",lpgw->IniFile);
		AppendMenu(lpgw->hPopMenu, MF_STRING, M_WRITEINI, (LPSTR)buf);
	}

	/* modify the system menu to have the new items we want */
	sysmenu = GetSystemMenu(lpgw->hWndGraph,0);
	AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
	AppendMenu(sysmenu, MF_POPUP, (UINT)lpgw->hPopMenu, "&Options");
	AppendMenu(sysmenu, MF_STRING, M_ABOUT, "&About");

#ifndef WGP_CONSOLE
	if (!IsWindowVisible(lpgw->lptw->hWndParent)) {
		AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
		AppendMenu(sysmenu, MF_STRING, M_COMMANDLINE, "C&ommand Line");
	}
#endif

	ShowWindow(lpgw->hWndGraph, SW_SHOWNORMAL);
}


/* close a graph window */
void WDPROC
GraphClose(LPGW lpgw)
{
	/* Pass it through mouse handling to check for "bind Close" */
	Wnd_exec_event(lpgw, (LPARAM)0, GE_reset, 0);
	/* close window */
	if (lpgw->hWndGraph)
		DestroyWindow(lpgw->hWndGraph);
	WinMessageLoop();
	lpgw->hWndGraph = NULL;

	lpgw->locked = TRUE;
	DestroyBlocks(lpgw);
	lpgw->locked = FALSE;
}


void WDPROC
GraphStart(LPGW lpgw, double pointsize)
{
	lpgw->locked = TRUE;
	DestroyBlocks(lpgw);
	lpgw->org_pointsize = pointsize;
	if ( !lpgw->hWndGraph || !IsWindow(lpgw->hWndGraph) )
		GraphInit(lpgw);
	if (IsIconic(lpgw->hWndGraph))
		ShowWindow(lpgw->hWndGraph, SW_SHOWNORMAL);
	if (lpgw->graphtotop) {
		/* HBB NEW 20040221: avoid grabbing the keyboard focus
		 * unless mouse mode is on */
#ifdef USE_MOUSE
		if (mouse_setting.on) {
			BringWindowToTop(lpgw->hWndGraph);
			return;
		}
#endif /* USE_MOUSE */
		SetWindowPos(lpgw->hWndGraph,
			     HWND_TOP, 0,0,0,0,
			     SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
	}
}


void WDPROC
GraphEnd(LPGW lpgw)
{
	RECT rect;

	GetClientRect(lpgw->hWndGraph, &rect);
	InvalidateRect(lpgw->hWndGraph, (LPRECT) &rect, 1);
	lpgw->locked = FALSE;
	UpdateWindow(lpgw->hWndGraph);
#ifdef USE_MOUSE
	gp_exec_event(GE_plotdone, 0, 0, 0, 0, 0);	/* notify main program */
#endif
}


/* shige */
void WDPROC
GraphChangeTitle(LPGW lpgw)
{
	if (GraphHasWindow(lpgw))
		SetWindowText(lpgw->hWndGraph, lpgw->Title);
}


void WDPROC
GraphResume(LPGW lpgw)
{
	lpgw->locked = TRUE;
}


void WDPROC
GraphPrint(LPGW lpgw)
{
	if (GraphHasWindow(lpgw))
		SendMessage(lpgw->hWndGraph, WM_COMMAND, M_PRINT, 0L);
}


void WDPROC
GraphRedraw(LPGW lpgw)
{
	if (GraphHasWindow(lpgw))
		SendMessage(lpgw->hWndGraph, WM_COMMAND, M_REBUILDTOOLS, 0L);
}


TBOOLEAN
GraphHasWindow(LPGW lpgw)
{
	return (lpgw != NULL) && (lpgw->hWndGraph != NULL) && IsWindow(lpgw->hWndGraph);
}


/* ================================== */

/* Helper functions for bookkeeping of pens, brushes and fonts */

/* Set up LOGPEN structures based on information coming from wgnuplot.ini, via
 * ReadGraphIni() */
static void
StorePen(LPGW lpgw, int i, COLORREF ref, int colorstyle, int monostyle)
{
	LOGPEN *plp;

	plp = &lpgw->colorpen[i];
	plp->lopnColor = ref;
	if (colorstyle < 0) {
		plp->lopnWidth.x = -colorstyle;
		plp->lopnStyle = 0;
	} else {
		plp->lopnWidth.x = 1;
		plp->lopnStyle = colorstyle % 5;
	}
	plp->lopnWidth.y = 0;

	plp = &lpgw->monopen[i];
	plp->lopnColor = RGB(0,0,0);
	if (monostyle < 0) {
		plp->lopnWidth.x = -monostyle;
		plp->lopnStyle = 0;
	} else {
		plp->lopnWidth.x = 1;
		plp->lopnStyle = monostyle % 5;
	}
	plp->lopnWidth.y = 0;
}


/* Prepare pens and brushes (--> colors) for use by the driver. Pens are (now) created
 * on-the-fly (--> DeleteObject(SelectObject(...)) idiom), but the brushes are still
 * all created statically, and kept until the window is closed */
static void
MakePens(LPGW lpgw, HDC hdc)
{
	int i;
	LOGPEN pen;

	if ((GetDeviceCaps(hdc, NUMCOLORS) == 2) || !lpgw->color) {
		pen = lpgw->monopen[1];
		pen.lopnWidth.x *= lpgw->linewidth * lpgw->sampling;
		lpgw->hapen = CreatePenIndirect(&pen); 	/* axis */
		lpgw->hbrush = CreateSolidBrush(RGB(255,255,255));
		for (i = 0; i < WGNUMPENS + 2; i++)
			lpgw->colorbrush[i] = CreateSolidBrush(RGB(0,0,0));
	} else {
		pen = lpgw->colorpen[1];
		pen.lopnWidth.x *= lpgw->linewidth * lpgw->sampling;
		lpgw->hapen = CreatePenIndirect(&pen); 	/* axis */
		lpgw->hbrush = CreateSolidBrush(lpgw->background);
		for (i = 0; i < WGNUMPENS + 2; i++)
			lpgw->colorbrush[i] = CreateSolidBrush(lpgw->colorpen[i].lopnColor);
	}
	lpgw->hnull = CreatePen(PS_NULL, 0, 0); /* border for filled areas */

	/* build pattern brushes for filled boxes (ULIG) */
	if( ! brushes_initialized ) {
		int i;

		for(i=0; i < pattern_num; i++) {
			pattern_bitdata[i].bmType       = 0;
			pattern_bitdata[i].bmWidth      = 16;
			pattern_bitdata[i].bmHeight     = 8;
			pattern_bitdata[i].bmWidthBytes = 2;
			pattern_bitdata[i].bmPlanes     = 1;
			pattern_bitdata[i].bmBitsPixel  = 1;
			pattern_bitdata[i].bmBits       = pattern_bitmaps[i];
			pattern_bitmap[i] = CreateBitmapIndirect(&pattern_bitdata[i]);
			pattern_brush[i] = CreatePatternBrush(pattern_bitmap[i]);
		}

		brushes_initialized = TRUE;
	}
}


/* Undo effect of MakePens(). To be called just before the window is closed. */
static void
DestroyPens(LPGW lpgw)
{
	int i;

	DeleteObject(lpgw->hbrush);
	DeleteObject(lpgw->hapen);
	DeleteObject(lpgw->hsolid);
	for (i=0; i<WGNUMPENS+2; i++)
		DeleteObject(lpgw->colorbrush[i]);
	DeleteObject(lpgw->hnull);

	/* delete brushes used for filled areas */
	if (brushes_initialized) {
		int i;

		for (i = 0; i < pattern_num; i++) {
			DeleteObject(pattern_bitmap[i]);
			DeleteObject(pattern_brush[i]);
		}
		brushes_initialized = FALSE;
	}
}

/* ================================== */

/* HBB 20010218: new function. An isolated snippet from MakeFont(), now also
 * used in Wnd_put_tmptext() to size the temporary bitmap. */
static void
Wnd_GetTextSize(HDC hdc, LPCSTR str, size_t len, int *cx, int *cy)
{
	SIZE size;

	GetTextExtentPoint32(hdc, str, len, &size);
	*cx = size.cx;
	*cy = size.cy;
}


static void
GetPlotRect(LPGW lpgw, LPRECT rect)
{
	GetClientRect(lpgw->hWndGraph, rect);
	rect->bottom -= lpgw->StatusHeight; /* leave some room for the status line */
	rect->top += lpgw->ToolbarHeight + 1;
	if (rect->bottom < rect->top) rect->bottom = rect->top;
}


static void
GetPlotRectInMM(LPGW lpgw, LPRECT rect, HDC hdc)
{
	int iWidthMM, iHeightMM, iWidthPels, iHeightPels;

	GetPlotRect (lpgw, rect);

	/* Taken from
	http://msdn.microsoft.com/en-us/library/dd183519(VS.85).aspx
	 */
	// Determine the picture frame dimensions.
	// iWidthMM is the display width in millimeters.
	// iHeightMM is the display height in millimeters.
	// iWidthPels is the display width in pixels.
	// iHeightPels is the display height in pixels
	iWidthMM = GetDeviceCaps(hdc, HORZSIZE);
	iHeightMM = GetDeviceCaps(hdc, VERTSIZE);
	iWidthPels = GetDeviceCaps(hdc, HORZRES);
	iHeightPels = GetDeviceCaps(hdc, VERTRES);

	// Convert client coordinates to .01-mm units.
	// Use iWidthMM, iWidthPels, iHeightMM, and
	// iHeightPels to determine the number of
	// .01-millimeter units per pixel in the x-
	//  and y-directions.
	rect->left = (rect->left * iWidthMM * 100) / iWidthPels;
	rect->top = (rect->top * iHeightMM * 100) / iHeightPels;
	rect->right = (rect->right * iWidthMM * 100) / iWidthPels;
	rect->bottom = (rect->bottom * iHeightMM * 100) / iHeightPels;
}


static void
MakeFonts(LPGW lpgw, LPRECT lprect, HDC hdc)
{
	HFONT hfontold;
	TEXTMETRIC tm;
	int result;
	char *p;
	int cx, cy;

	lpgw->rotate = FALSE;
	_fmemset(&(lpgw->lf), 0, sizeof(LOGFONT));
	_fstrncpy(lpgw->lf.lfFaceName,lpgw->fontname,LF_FACESIZE);
	lpgw->lf.lfHeight = -MulDiv(lpgw->fontsize * lpgw->fontscale, GetDeviceCaps(hdc, LOGPIXELSY), 72) * lpgw->sampling;
	lpgw->lf.lfCharSet = DEFAULT_CHARSET;
	if ( (p = _fstrstr(lpgw->fontname," Italic")) != (LPSTR)NULL ) {
		lpgw->lf.lfFaceName[ (unsigned int)(p-lpgw->fontname) ] = '\0';
		lpgw->lf.lfItalic = TRUE;
	}
	if ( (p = _fstrstr(lpgw->fontname," Bold")) != (LPSTR)NULL ) {
		lpgw->lf.lfFaceName[ (unsigned int)(p-lpgw->fontname) ] = '\0';
		lpgw->lf.lfWeight = FW_BOLD;
	}
	lpgw->lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
	lpgw->lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
	/* ClearType quality is only supported on XP or later */
	lpgw->lf.lfQuality =
		IsWindowsXPorLater() && lpgw->antialiasing ? CLEARTYPE_QUALITY : PROOF_QUALITY;

	if (lpgw->hfonth == 0) {
		lpgw->hfonth = CreateFontIndirect((LOGFONT *)&(lpgw->lf));
	}

	/* we do need a 90 degree font */
	if (lpgw->hfontv)
		DeleteObject(lpgw->hfontv);
	lpgw->lf.lfEscapement = 900;
	lpgw->lf.lfOrientation = 900;
	lpgw->hfontv = CreateFontIndirect(&(lpgw->lf));

	/* save text size */
	hfontold = SelectObject(hdc, lpgw->hfonth);
	Wnd_GetTextSize(hdc, "0123456789", 10, &cx, &cy);
	lpgw->vchar = MulDiv(cy,lpgw->ymax,lprect->bottom - lprect->top);
	lpgw->hchar = MulDiv(cx/10,lpgw->xmax,lprect->right - lprect->left);

	/* CMW: Base tick size on character size */
	lpgw->htic = MulDiv(lpgw->hchar, 2, 5);
	cy = MulDiv(cx/10, 2*GetDeviceCaps(hdc, LOGPIXELSY), 5*GetDeviceCaps(hdc, LOGPIXELSX));
	lpgw->vtic = MulDiv(cy, lpgw->ymax, lprect->bottom - lprect->top);
	/* find out if we can rotate text 90deg */
	SelectObject(hdc, lpgw->hfontv);
	result = GetDeviceCaps(hdc, TEXTCAPS);
	if ((result & TC_CR_90) || (result & TC_CR_ANY))
		lpgw->rotate = TRUE;
	GetTextMetrics(hdc,(TEXTMETRIC *)&tm);
	if (tm.tmPitchAndFamily & TMPF_VECTOR)
		lpgw->rotate = TRUE;	/* vector fonts can all be rotated */
	if (tm.tmPitchAndFamily & TMPF_TRUETYPE)
		lpgw->rotate = TRUE;	/* truetype fonts can all be rotated */
	SelectObject(hdc, hfontold);
	return;
}


static void
DestroyFonts(LPGW lpgw)
{
	if (lpgw->hfonth) {
		DeleteObject(lpgw->hfonth);
		lpgw->hfonth = 0;
	}
	if (lpgw->hfontv) {
		DeleteObject(lpgw->hfontv);
		lpgw->hfontv = 0;
	}
	return;
}


static void
SetFont(LPGW lpgw, HDC hdc)
{
	SelectObject(hdc, lpgw->hfonth);
	if (lpgw->rotate && lpgw->angle) {
		if (lpgw->hfontv)
			DeleteObject(lpgw->hfontv);
		lpgw->lf.lfEscapement = lpgw->lf.lfOrientation  = lpgw->angle * 10;
		lpgw->hfontv = CreateFontIndirect(&(lpgw->lf));
		if (lpgw->hfontv)
			SelectObject(hdc, lpgw->hfontv);
	}
	return;
}


static void
SelFont(LPGW lpgw)
{
	LOGFONT lf;
	CHOOSEFONT cf;
	HDC hdc;
	char lpszStyle[LF_FACESIZE];
	char *p;

	/* Set all structure fields to zero. */
	_fmemset(&cf, 0, sizeof(CHOOSEFONT));
	_fmemset(&lf, 0, sizeof(LOGFONT));
	cf.lStructSize = sizeof(CHOOSEFONT);
	cf.hwndOwner = lpgw->hWndGraph;
	_fstrncpy(lf.lfFaceName,lpgw->fontname,LF_FACESIZE);
	if ((p = _fstrstr(lpgw->fontname," Bold")) != (LPSTR)NULL) {
		_fstrncpy(lpszStyle,p+1,LF_FACESIZE);
		lf.lfFaceName[ (unsigned int)(p-lpgw->fontname) ] = '\0';
	} else if ((p = _fstrstr(lpgw->fontname," Italic")) != (LPSTR)NULL) {
		_fstrncpy(lpszStyle,p+1,LF_FACESIZE);
		lf.lfFaceName[ (unsigned int)(p-lpgw->fontname) ] = '\0';
	} else {
		_fstrcpy(lpszStyle,"Regular");
	}
	cf.lpszStyle = lpszStyle;
	hdc = GetDC(lpgw->hWndGraph);
	lf.lfHeight = -MulDiv(lpgw->fontsize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
	ReleaseDC(lpgw->hWndGraph, hdc);
	cf.lpLogFont = &lf;
	cf.nFontType = SCREEN_FONTTYPE;
	cf.Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT | CF_USESTYLE;
	if (ChooseFont(&cf)) {
		_fstrcpy(lpgw->fontname,lf.lfFaceName);
		lpgw->fontsize = cf.iPointSize / 10;
		if (cf.nFontType & BOLD_FONTTYPE)
			lstrcat(lpgw->fontname," Bold");
		if (cf.nFontType & ITALIC_FONTTYPE)
			lstrcat(lpgw->fontname," Italic");
		/* set current font as default font */
		strcpy(lpgw->deffontname, lpgw->fontname);
		lpgw->deffontsize = lpgw->fontsize;
		SendMessage(lpgw->hWndGraph,WM_COMMAND,M_REBUILDTOOLS,0L);
	}
}


static LPWSTR
UnicodeText(const char *str, enum set_encoding_id encoding)
{
    UINT codepage = 0;
    LPWSTR textw = NULL;

    /* For a list of code page identifiers see
       http://msdn.microsoft.com/en-us/library/dd317756%28v=vs.85%29.aspx
    */
    switch (encoding) {
        case S_ENC_ISO8859_1:  codepage = 28591; break;
        case S_ENC_ISO8859_2:  codepage = 28592; break;
        case S_ENC_ISO8859_9:  codepage = 28599; break;
        case S_ENC_ISO8859_15: codepage = 28605; break;
        case S_ENC_CP437:      codepage =   437; break;
        case S_ENC_CP850:      codepage =   850; break;
        case S_ENC_CP852:      codepage =   852; break;
        case S_ENC_CP950:      codepage =   950; break;
        case S_ENC_CP1250:     codepage =  1250; break;
        case S_ENC_CP1251:     codepage =  1251; break;
        case S_ENC_CP1254:     codepage =  1254; break;
        case S_ENC_KOI8_R:     codepage = 20866; break;
        case S_ENC_KOI8_U:     codepage = 21866; break;
        case S_ENC_SJIS:       codepage =   932; break;
        case S_ENC_UTF8:       codepage = CP_UTF8; break;
		default:               codepage = 0;
    }
    if (codepage != 0) {
        int length;

        /* get length of converted string */
        length = MultiByteToWideChar(codepage, 0, str, -1, NULL, 0);
        textw = (LPWSTR) malloc(sizeof(WCHAR) * length);

        /* convert string to UTF-16 */
        length = MultiByteToWideChar(codepage, 0, str, -1, textw, length);
    }
    return textw;
}


#ifdef USE_MOUSE
/* ================================== */

static void
LoadCursors(LPGW lpgw)
{
	/* 3 of them are standard cursor shapes: */
	hptrDefault = LoadCursor(NULL, IDC_ARROW);
	hptrZooming = LoadCursor(NULL, IDC_SIZEALL);
	hptrCrossHair = LoadCursor( NULL, IDC_CROSS);
	/* the other 2 are kept in the resource file: */
	hptrScaling = LoadCursor( lpgw->hInstance, MAKEINTRESOURCE(IDC_SCALING));
	hptrRotating = LoadCursor( lpgw->hInstance, MAKEINTRESOURCE(IDC_ROTATING));

	hptrCurrent = hptrCrossHair;
}

static void
DestroyCursors(LPGW lpgw)
{
	/* No-op. Cursors from LoadCursor() don't need destroying */
	return;
}

#endif /* USE_MOUSE */

/* ================================== */


static void
dot(HDC hdc, int xdash, int ydash)
{
	MoveTo(hdc, xdash, ydash);
	LineTo(hdc, xdash, ydash+1);
}


static unsigned int WDPROC
GraphGetTextLength(LPGW lpgw, HDC hdc, LPCSTR text)
{
    SIZE size;
    LPWSTR textw;

    textw = UnicodeText(text, lpgw->encoding);
    if (textw) {
        GetTextExtentPoint32W(hdc, textw, wcslen(textw), &size);
        free(textw);
    } else
        GetTextExtentPoint32(hdc, text, strlen(text), &size);
    size.cx += GetTextCharacterExtra(hdc);
    return size.cx;
}


void WDPROC
GraphEnhancedOpen(char *fontname, double fontsize, double base,
    BOOL widthflag, BOOL showflag, int overprint)
{
	const int win_scale = 1; /* scaling of base offset */
	char *fontstring;

	/* There are two special cases:
	 * overprint = 3 means save current position
	 * overprint = 4 means restore saved position
	 */
	if (overprint == 3) {
		enhstate.xsave = enhstate.x;
		enhstate.ysave = enhstate.y;
		return;
	} else if (overprint == 4) {
		enhstate.x = enhstate.xsave;
		enhstate.y = enhstate.ysave;
		return;
	}

	if (!enhstate.opened_string) {
		/* Start new text fragment */
		enhstate.opened_string = TRUE;
		enhanced_cur_text = enhanced_text;
		/* Keep track of whether we are supposed to show this string */
		enhstate.show = showflag;
		/* 0/1/2  no overprint / 1st pass / 2nd pass */
		enhstate.overprint = overprint;
		/* widthflag FALSE means do not update text position after printing */
		enhstate.widthflag = widthflag;
		/* Select font */
		if ((fontname != NULL) && (strlen(fontname) > 0))
			fontstring = fontname;
		else
			fontstring = enhstate.lpgw->deffontname;
		strcpy(enhstate.fontname, fontstring);
		enhstate.fontsize = fontsize;
		GraphChangeFont(enhstate.lpgw, enhstate.fontname, enhstate.fontsize,
		                enhstate.hdc, *(enhstate.rect));
		SetFont(enhstate.lpgw, enhstate.hdc);

		/* Scale fractional font height to vertical units of display */
		/* FIXME:
			Font scaling is not done properly (yet) and will lead to
			non-optimal results for most font and size selections.
			OUTLINEFONTMETRICS could be used for better results here.
		*/
		enhstate.base = win_scale * base *
						enhstate.lpgw->sampling * enhstate.lpgw->fontscale *
						enhstate.res_scale;
	}
}


void WDPROC
GraphEnhancedFlush(void)
{
	int width, height;
	unsigned int x, y, len;
	double angle = M_PI/180. * enhstate.lpgw->angle;

	if (!enhstate.opened_string) return;
	*enhanced_cur_text = '\0';

	/* print the string fragment, perhaps invisibly */
	/* NB: base expresses offset from current y pos */
	x = enhstate.x - enhstate.base * sin(angle);
	y = enhstate.y - enhstate.base * cos(angle);

	/* calculate length of string first */
	len = GraphGetTextLength(enhstate.lpgw, enhstate.hdc, enhanced_text);
	width = cos(angle) * len;
	height = -sin(angle) * len;

	if (enhstate.widthflag && !enhstate.sizeonly && !enhstate.overprint)
		enhstate.totalwidth += width;

	/* display string */
	if (enhstate.show && !enhstate.sizeonly)
		draw_put_text(enhstate.lpgw, enhstate.hdc, x, y, enhanced_text);

	/* update drawing position according to len */
	if (!enhstate.widthflag) {
		width = 0;
		height = 0;
	}
	if (enhstate.sizeonly) {
		/* This is the first pass for justified printing.        */
		/* We just adjust the starting position for second pass. */
		if (enhstate.lpgw->justify == RIGHT) {
			enhstate.x -= width;
			enhstate.y -= height;
		}
		else if (enhstate.lpgw->justify == CENTRE) {
			enhstate.x -= width / 2;
			enhstate.y -= height / 2;
		}
		/* nothing to do for LEFT justified text */
	} else if (enhstate.overprint == 1) {
		/* Save current position */
		enhstate.xsave = enhstate.x + width;
		enhstate.ysave = enhstate.y + height;
		/* First pass of overprint, leave position in center of fragment */
		enhstate.x += width / 2;
		enhstate.y += height / 2;
	} else if (enhstate.overprint == 2) {
		/* Restore current position,                          */
		/* this sets the position behind the overprinted text */
		enhstate.x = enhstate.xsave;
		enhstate.y = enhstate.ysave;
	} else {
		/* Normal case is to update position to end of fragment */
		enhstate.x += width;
		enhstate.y += height;
	}
	enhstate.opened_string = FALSE;
}


static int
draw_enhanced_text(LPGW lpgw, HDC hdc, LPRECT rect, int x, int y, char * str)
{
	char * original_string = str;
	unsigned int pass, num_passes;
	struct termentry *tsave;
	char save_fontname[MAXFONTNAME];
	int save_fontsize;

	/* Init enhanced text state */
	enhstate.lpgw = lpgw;
	enhstate.hdc = hdc;
	enhstate.rect = rect;
	enhstate.opened_string = FALSE;
	strcpy(enhstate.fontname, lpgw->fontname);
	enhstate.fontsize = lpgw->fontsize;
	/* Store the start position */
	enhstate.x = x;
	enhstate.y = y;
	enhstate.totalwidth = 0;

	/* Save font information */
	strcpy(save_fontname, lpgw->fontname);
	save_fontsize = lpgw->fontsize;

	/* Set up global variables needed by enhanced_recursion() */
	enhanced_fontscale = 1.0;
	strncpy(enhanced_escape_format, "%c", sizeof(enhanced_escape_format));

	/* Text justification requires two passes. During the first pass we */
	/* don't draw anything, we just measure the space it will take.     */
	/* Without justification one pass is enough                         */
	if (enhstate.lpgw->justify == LEFT) {
		num_passes = 1;
	} else {
		num_passes = 2;
		enhstate.sizeonly = TRUE;
	}

	/* we actually print everything left to right */
	SetTextAlign(hdc, TA_LEFT|TA_BASELINE);
	/* adjust baseline accordingly */
	{
		TEXTMETRIC tm;
		if (GetTextMetrics(hdc, &tm)) {
			int shift = tm.tmHeight/2 - tm.tmDescent;
			enhstate.x += sin(lpgw->angle * M_PI/180) * shift;
			enhstate.y += cos(lpgw->angle * M_PI/180) * shift;
		}
	}

	/* enhanced_recursion() uses the callback functions
	   of the current terminal. So we have to temporarily
	   switch terminal. */
	if (WIN_term) {
		tsave = term;
		term = WIN_term;
	}

	for (pass = 1; pass <= num_passes; pass++) {
		/* Set the recursion going. We say to keep going until a
		* closing brace, but we don't really expect to find one.
		* If the return value is not the nul-terminator of the
		* string, that can only mean that we did find an unmatched
		* closing brace in the string. We increment past it (else
		* we get stuck in an infinite loop) and try again.
		*/
		while (*(str = enhanced_recursion((char *)str, TRUE,
				save_fontname, save_fontsize,
				0.0, TRUE, TRUE, 0))) {
			GraphEnhancedFlush();
			if (!*++str)
				break; /* end of string */
			/* else carry on and process the rest of the string */
		}

		/* In order to do text justification we need to do a second pass that */
		/* uses information stored during the first pass.                     */
		/* see GraphEnhancedFlush()                                           */
		if (pass == 1) {
			/* do the actual printing in the next pass */
			enhstate.sizeonly = FALSE;
			str = original_string;
		}
	}

	/* restore terminal */
	if (WIN_term) term = tsave;

	/* restore previous font */
	GraphChangeFont(lpgw, save_fontname, save_fontsize, hdc, *rect);
	SetFont(lpgw, hdc);

	/* restore text alignment */
	draw_text_justify(hdc, enhstate.lpgw->justify);

	return enhstate.totalwidth;
}


static void
draw_text_justify(HDC hdc, int justify)
{
	switch (justify)
	{
		case LEFT:
			SetTextAlign(hdc, TA_LEFT|TA_BOTTOM);
			break;
		case RIGHT:
			SetTextAlign(hdc, TA_RIGHT|TA_BOTTOM);
			break;
		case CENTRE:
			SetTextAlign(hdc, TA_CENTER|TA_BOTTOM);
			break;
	}
}


static void
draw_put_text(LPGW lpgw, HDC hdc, int x, int y, char * str)
{
	SetBkMode(hdc, TRANSPARENT);

	/* support text encoding */
	if ((lpgw->encoding == S_ENC_DEFAULT) || (lpgw->encoding == S_ENC_INVALID)) {
		TextOut(hdc, x, y, str, lstrlen(str));
	} else {
		LPWSTR textw = UnicodeText(str, lpgw->encoding);
		if (textw) {
			TextOutW(hdc, x, y, textw, wcslen(textw));
			free(textw);
		} else {
			/* print this only once */
			if (lpgw->encoding != lpgw->encoding_error) {
				fprintf(stderr, "windows terminal: encoding %s not supported\n", encoding_names[lpgw->encoding]);
				lpgw->encoding_error = lpgw->encoding;
			}
			/* fall back to standard encoding */
			TextOut(hdc, x, y, str, strlen(str));
		}
	}
	SetBkMode(hdc, OPAQUE);
}


static void
draw_new_pens(LPGW lpgw, HDC hdc, LOGPEN cur_penstruct)
{
	HPEN old_hapen = lpgw->hapen;
	HPEN old_hsolid = lpgw->hsolid;

	if (cur_penstruct.lopnWidth.x <= 1) {
		/* shige: work-around for Windows clipboard bug */
		lpgw->hapen = CreatePenIndirect((LOGPEN *) &cur_penstruct);
		lpgw->hsolid = CreatePen(PS_SOLID, 1, cur_penstruct.lopnColor);
	} else {
		LOGBRUSH lb;
		lb.lbStyle = BS_SOLID;
		lb.lbColor = cur_penstruct.lopnColor;
		lpgw->hapen = ExtCreatePen(
			PS_GEOMETRIC | cur_penstruct.lopnStyle | PS_ENDCAP_FLAT | PS_JOIN_BEVEL,
			cur_penstruct.lopnWidth.x, &lb, 0, 0);
		lpgw->hsolid = ExtCreatePen(
			PS_GEOMETRIC | PS_SOLID | PS_ENDCAP_FLAT | PS_JOIN_BEVEL,
			cur_penstruct.lopnWidth.x, &lb, 0, 0);
	}

	SelectObject(hdc, lpgw->hapen);
	DeleteObject(old_hapen);
	DeleteObject(old_hsolid);
}


static void
draw_new_brush(LPGW lpgw, HDC hdc, COLORREF color)
{
	HBRUSH new_brush = CreateSolidBrush(color);
	SelectObject(hdc, new_brush);
	if (lpgw->hcolorbrush)
		DeleteObject(lpgw->hcolorbrush);
	lpgw->hcolorbrush = new_brush;
}

static void
draw_update_keybox(LPGW lpgw, unsigned plotno, unsigned x, unsigned y)
{
	LPRECT bb;
	if (plotno == 0) return;
	if (plotno > lpgw->maxkeyboxes) {
		int i;
		lpgw->maxkeyboxes += 10;
		lpgw->keyboxes = (LPRECT) realloc(lpgw->keyboxes,
				lpgw->maxkeyboxes * sizeof(RECT));
		for (i = plotno - 1; i < lpgw->maxkeyboxes; i++) {
			lpgw->keyboxes[i].left = INT_MAX;
			lpgw->keyboxes[i].right = 0;
			lpgw->keyboxes[i].bottom = INT_MAX;
			lpgw->keyboxes[i].top = 0;
		}
	}
	bb = lpgw->keyboxes + plotno - 1;
	if (x < bb->left)   bb->left = x;
	if (x > bb->right)  bb->right = x;
	if (y < bb->bottom) bb->bottom = y;
	if (y > bb->top)    bb->top = y;
}

/* This one is really the heart of this module: it executes the stored set of
 * commands, whenever it changed or a redraw is necessary */
static void
drawgraph(LPGW lpgw, HDC hdc, LPRECT rect)
{
	/* draw ops */
	unsigned int ngwop = 0;
	struct GWOP *curptr;
	struct GWOPBLK *blkptr;

	/* layers */
	unsigned plotno = 0;
	BOOL gridline = FALSE;
	BOOL skipplot = FALSE;
	BOOL keysample = FALSE;

	/* colors */
	BOOL isColor;				/* use colors? */
	COLORREF last_color = 0;	/* currently selected color */

	/* lines */
	double line_width = lpgw->sampling * lpgw->linewidth;	/* current line width */
	double lw_scale = 1.;
	LOGPEN cur_penstruct;		/* current pen settings */

	/* polylines and polygons */
	int polymax = 200;			/* size of ppt */
	int polyi = 0;				/* number of points in ppt */
	POINT *ppt;					/* storage of polyline/polygon-points */
	unsigned int lastop=-1;		/* used for plotting last point on a line */
	POINT cpoint;				/* current GDI location */

	/* filled polygons and boxes */
	unsigned int fillstyle = 0;	/* current fill style */
	BOOL transparent = FALSE;	/* transparent fill? */
	double alpha = 0.;			/* alpha for transarency */
	int pattern = 0;			/* patter number */
	COLORREF fill_color = 0;	/* color to use for fills */
	HBRUSH solid_brush = 0;		/* current solid fill brush */
	int shadedblendcaps = SB_CONST_ALPHA;	/* displays can always do AlphaBlend */
	bool warn_no_transparent = FALSE;

	/* images */
	int seq = 0;				/* sequence counter for W_image ops */
	POINT corners[4];			/* image corners */
	int color_mode = 0;			/* image color mode */

	/* coordinates and lengths */
	int xdash, ydash;			/* the transformed coordinates */
	int rr, rl, rt, rb;			/* coordinates of drawing area */
	int htic, vtic;				/* tic sizes */
	int hshift, vshift;			/* correction of text position */

	/* indices */
	int i, k;

    if (lpgw->locked) return;

	/* The GDI status query functions don't work on metafile, printer or
	 * plotter handles, so can't know whether the screen is actually showing
	 * color or not, if drawgraph() is being called from CopyClip().
	 * Solve by defaulting isColor to TRUE in those cases.
	 * Note that info on color capabilities of printers would be available
	 * via DeviceCapabilities().
	 */
	isColor = (((GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL)) > 2)
	       || (GetDeviceCaps(hdc, TECHNOLOGY) == DT_METAFILE)
	       || (GetDeviceCaps(hdc, TECHNOLOGY) == DT_PLOTTER)
	       || (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASPRINTER));

    if (lpgw->color && isColor) {
		SetBkColor(hdc, lpgw->background);
		FillRect(hdc, rect, lpgw->hbrush);
    } else {
		FillRect(hdc, rect, (HBRUSH)GetStockObject(WHITE_BRUSH));
    }

	/* Need to scale line widths for raster printers so they are the same
	   as on screen */
	if ((GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASPRINTER)) {
		HDC hdc_screen = GetDC(NULL);
		lw_scale = (double) GetDeviceCaps(hdc, VERTRES) /
		           (double) GetDeviceCaps(hdc_screen, VERTRES);
		line_width *= lw_scale;
		ReleaseDC(NULL, hdc_screen);
		/* Does the printer support AlphaBlend with transparency (const alpha)? */
		shadedblendcaps = GetDeviceCaps(hdc, SHADEBLENDCAPS);
		warn_no_transparent = ((shadedblendcaps & SB_CONST_ALPHA) == 0);
	}
	/* Also needed for enhanced text */
	enhstate.res_scale = lw_scale;

    ppt = (POINT *)LocalAllocPtr(LHND, (polymax+1) * sizeof(POINT));

    rr = rect->right;
    rl = rect->left;
    rt = rect->top;
    rb = rect->bottom;

    htic = (lpgw->org_pointsize * MulDiv(lpgw->htic, rr - rl, lpgw->xmax) + 1);
    vtic = (lpgw->org_pointsize * MulDiv(lpgw->vtic, rb - rt, lpgw->ymax) + 1);

    lpgw->angle = 0;
    SetFont(lpgw, hdc);
    lpgw->justify = LEFT;
    SetTextAlign(hdc, TA_LEFT|TA_BOTTOM);

    /* calculate text shifting for horizontal text */
    hshift = 0;
    vshift = MulDiv(lpgw->vchar, rb - rt, lpgw->ymax)/2;

	/* init layer variables */
	lpgw->numplots = 0;
	lpgw->hasgrid = FALSE;
	for (i = 0; i < lpgw->maxkeyboxes; i++) {
		lpgw->keyboxes[i].left = INT_MAX;
		lpgw->keyboxes[i].right = 0;
		lpgw->keyboxes[i].bottom = INT_MAX;
		lpgw->keyboxes[i].top = 0;
	}

	SelectObject(hdc, lpgw->hapen); /* background brush */
	SelectObject(hdc, lpgw->colorbrush[2]); /* first user pen */

    /* do the drawing */
    blkptr = lpgw->gwopblk_head;
    curptr = NULL;
    if (blkptr) {
		if (!blkptr->gwop)
			blkptr->gwop = (struct GWOP *)GlobalLock(blkptr->hblk);
		if (!blkptr->gwop)
			return;
		curptr = (struct GWOP *)blkptr->gwop;
    }

    while (ngwop < lpgw->nGWOP) {
		/* transform the coordinates */
		xdash = MulDiv(curptr->x, rr-rl-1, lpgw->xmax) + rl;
		ydash = MulDiv(curptr->y, rt-rb+1, lpgw->ymax) + rb - 1;

		/* finish last polygon */
		if ((lastop == W_vect) && (curptr->op != W_vect)) {
			if (polyi >= 2) {
#ifdef HAVE_GDIPLUS
				if (lpgw->antialiasing)
					gdiplusPolyline(hdc, ppt, polyi, &cur_penstruct);
				else
#endif
					Polyline(hdc, ppt, polyi);
				/* move internal GDI state to last point */
				MoveTo(hdc, ppt[polyi-1].x, ppt[polyi-1].y);
				cpoint = ppt[polyi-1];
			} else if (polyi == 1) {
				/* degenerate case e.g. when using 'linecolor variable' */
#ifdef HAVE_GDIPLUS
				if (lpgw->antialiasing)
					gdiplusLine(hdc, cpoint, ppt[0], &cur_penstruct);
				else
#endif
					LineTo(hdc, ppt[0].x, ppt[0].y);
				cpoint = ppt[0];
			}
			polyi = 0;
		}

		/* handle layer commands first */
		if (curptr->op == W_layer) {
			t_termlayer layer = curptr->x;
			switch (layer) {
				case TERM_LAYER_BEFORE_PLOT:
					plotno++;
					lpgw->numplots = plotno;
					if (plotno >= lpgw->maxhideplots) {
						int idx;
						lpgw->maxhideplots += 10;
						lpgw->hideplot = (BOOL *) realloc(lpgw->hideplot, lpgw->maxhideplots * sizeof(BOOL));
						for (idx = plotno; idx < lpgw->maxhideplots; idx++)
							lpgw->hideplot[idx] = FALSE;
					}
					if (plotno <= lpgw->maxhideplots)
						skipplot = lpgw->hideplot[plotno - 1];
					break;
				case TERM_LAYER_AFTER_PLOT:
					skipplot = FALSE;
					break;
#if 0
				case TERM_LAYER_BACKTEXT:
				case TERM_LAYER_FRONTTEXT:
					break;
#endif
				case TERM_LAYER_BEGIN_GRID:
					gridline = TRUE;
					lpgw->hasgrid = TRUE;
					break;
				case TERM_LAYER_END_GRID:
					gridline = FALSE;
					break;
				case TERM_LAYER_BEGIN_KEYSAMPLE:
					keysample = TRUE;
					break;
				case TERM_LAYER_END_KEYSAMPLE:
					keysample = FALSE;
					break;
				case TERM_LAYER_RESET:
				case TERM_LAYER_RESET_PLOTNO:
					//plotno = 0;
					break;
				default:
					break;
			};
		}

		/* hide this layer? */
		if (!(skipplot || (gridline && lpgw->hidegrid)) ||
			(keysample || (curptr->op == W_line_type) || (curptr->op == W_setcolor)) ) {

		switch (curptr->op) {
		case 0:	/* have run past last in this block */
			break;

		case W_layer: /* already handled above */
			break;

		case W_move:
			ppt[0].x = xdash;
			ppt[0].y = ydash;
			polyi = 1;
			if (keysample)
				draw_update_keybox(lpgw, plotno, xdash, ydash);
			break;

		case W_vect:
			ppt[polyi].x = xdash;
			ppt[polyi].y = ydash;
			polyi++;
			if (polyi >= polymax) {
#ifdef HAVE_GDIPLUS
				if (lpgw->antialiasing)
					gdiplusPolyline(hdc, ppt, polyi, &cur_penstruct);
				else
#endif
					Polyline(hdc, ppt, polyi);
				MoveTo(hdc, xdash, ydash);
				ppt[0].x = xdash;
				ppt[0].y = ydash;
				polyi = 1;
				cpoint = ppt[0];
			}
			if (keysample)
				draw_update_keybox(lpgw, plotno, xdash, ydash);
			break;

		case W_line_type: {
			int cur_pen = (int)curptr->x % WGNUMPENS;

			/* create new pens */
			if (cur_pen > LT_NODRAW) {
				cur_pen += 2;
				cur_penstruct =  (lpgw->color && isColor) ?  lpgw->colorpen[cur_pen] : lpgw->monopen[cur_pen];
				cur_penstruct.lopnStyle =
					lpgw->dashed ? lpgw->monopen[cur_pen].lopnStyle : lpgw->colorpen[cur_pen].lopnStyle;
			} else if (cur_pen == LT_NODRAW) {
				cur_pen = WGNUMPENS;
				cur_penstruct.lopnStyle = PS_NULL;
				cur_penstruct.lopnColor = 0;
				cur_penstruct.lopnWidth.x = 1;
			} else { /* <= LT_BACKGROUND */
				cur_pen = WGNUMPENS;
				cur_penstruct.lopnStyle = PS_SOLID;
				cur_penstruct.lopnColor = lpgw->background;
				cur_penstruct.lopnWidth.x = 1;
			}
			cur_penstruct.lopnWidth.x *= line_width;
			draw_new_pens(lpgw, hdc, cur_penstruct);

			/* select new brush */
			if (cur_pen < WGNUMPENS)
				solid_brush = lpgw->colorbrush[cur_pen];
			else
				solid_brush = lpgw->hbrush;
			SelectObject(hdc, solid_brush);

			/* set text color, also used for pattern fill */
			SetTextColor(hdc, cur_penstruct.lopnColor);

			/* remember this color */
			last_color = cur_penstruct.lopnColor;
			fill_color = last_color;
			break;
		}

		case W_text_encoding:
			lpgw->encoding = curptr->x;
			break;

		case W_put_text: {
			char * str;
			str = LocalLock(curptr->htext);
			if (str) {
				/* shift correctly for rotated text */
				draw_put_text(lpgw, hdc, xdash + hshift, ydash + vshift, str);
				if (keysample) {
					int slen = GraphGetTextLength(lpgw, hdc, str);
					int vsize = MulDiv(lpgw->vchar, rb-rt, 2 * lpgw->ymax);
					if (lpgw->justify == RIGHT) slen *= -1;
					draw_update_keybox(lpgw, plotno, xdash, ydash - vsize);
					draw_update_keybox(lpgw, plotno, xdash + slen, ydash + vsize);
				}
			}
			LocalUnlock(curptr->htext);
			break;
		}

		case W_enhanced_text: {
			char * str;
			str = LocalLock(curptr->htext);
			if (str) {
				int slen = draw_enhanced_text(lpgw, hdc, rect, xdash, ydash, str);
				if (keysample) {
					int vsize = MulDiv(lpgw->vchar, rb-rt, 2 * lpgw->ymax);
					if (lpgw->justify == RIGHT) slen *= -1;
					draw_update_keybox(lpgw, plotno, xdash, ydash - vsize);
					draw_update_keybox(lpgw, plotno, xdash + slen, ydash + vsize);
				}
			}
			LocalUnlock(curptr->htext);
			break;
		}

		case W_fillstyle:
			/* HBB 20010916: new entry, needed to squeeze the many
			 * parameters of a filled box call through the bottleneck
			 * of the fixed number of parameters in GraphOp() and
			 * struct GWOP, respectively. */
			fillstyle = curptr->x;

			transparent = FALSE;
			alpha = 0.;
			switch (fillstyle & 0x0f) {
			case FS_TRANSPARENT_SOLID:
				alpha = (fillstyle >> 4) / 100.;
				if ((shadedblendcaps & SB_CONST_ALPHA) != 0) {
					transparent = TRUE;
					/* we already have a brush with that color */
				} else {
					/* Printer does not support AlphaBlend() */
					COLORREF color =
						RGB(255 - alpha * (255 - GetRValue(last_color)),
							255 - alpha * (255 - GetGValue(last_color)),
							255 - alpha * (255 - GetBValue(last_color)));
					solid_brush = lpgw->hcolorbrush;
					fill_color = color;
					draw_new_brush(lpgw, hdc, fill_color);
					fillstyle = (fillstyle & 0xfffffff0) | FS_SOLID;
					if (warn_no_transparent) {
						fprintf(stderr, "Warning: Transparency not supported on this device.\n");
						warn_no_transparent = FALSE; /* Warn only once */
					}
				}
				break;
			case FS_SOLID: {
				double density = MINMAX(0, (int)(fillstyle >> 4), 100) * 0.01;
				COLORREF color =
					RGB(255 - density * (255 - GetRValue(last_color)),
					    255 - density * (255 - GetGValue(last_color)),
					    255 - density * (255 - GetBValue(last_color)));
				draw_new_brush(lpgw, hdc, color);
				solid_brush = lpgw->hcolorbrush;
				fill_color = color;
				break;
			}
			case FS_TRANSPARENT_PATTERN:
				if ((shadedblendcaps & SB_CONST_ALPHA) != 0) {
					transparent = TRUE;
					alpha = 1.;
				} else {
					/* Printer does not support AlphaBlend() */
					fillstyle = (fillstyle & 0xfffffff0) | FS_PATTERN;
					if (warn_no_transparent) {
						fprintf(stderr, "Warning: Transparency not supported on this device.\n");
						warn_no_transparent = FALSE; /* Warn only once */
					}
				}
				/* intentionally fall through */
			case FS_PATTERN:
				/* style == 2 --> use fill pattern according to
						 * fillpattern. Pattern number is enumerated */
				pattern = GPMAX(fillstyle >> 4, 0) % pattern_num;
				SelectObject(hdc, pattern_brush[pattern]);
				break;
			case FS_DEFAULT:
				/* Leave the current brush and color in place */
				break;
			case FS_EMPTY:
			default:
				/* fill with background color */
				SelectObject(hdc, lpgw->hbrush);
				fill_color = lpgw->background;
				solid_brush = lpgw->hbrush;
				break;
			}
			break;

		case W_boxfill: {  /* ULIG */
			/* NOTE: the x and y passed with this call are the coordinates of the
			 * lower right corner of the box. The upper left corner was stored into
			 * ppt[0] by a preceding W_move, and the style was set
			 * by a W_fillstyle call. */
			POINT p;
			UINT  height, width;

			width = xdash - ppt[0].x;
			height = ppt[0].y - ydash;
			p.x = ppt[0].x;
			p.y = ydash;

#ifdef HAVE_GDIPLUS
			if (lpgw->antialiasing && lpgw->patternaa &&
			    (((fillstyle & 0x0f) == FS_PATTERN) ||
			     ((fillstyle & 0x0f) == FS_TRANSPARENT_PATTERN))) {
				ppt[1].x = ppt[0].x;
				ppt[1].y = ydash;
				ppt[2].x = xdash;
				ppt[2].y = ydash;
				ppt[3].x = xdash;
				ppt[3].y = ppt[0].y;
				ppt[4].x = ppt[0].x;
				ppt[4].y = ppt[0].y;
				gdiplusPatternFilledPolygonEx(hdc, ppt, 5, fill_color, 1., lpgw->background, transparent, pattern);
			} else
#endif
			if (transparent) {
				HDC memdc;
				HBITMAP membmp, oldbmp;
				BLENDFUNCTION ftn;
				HBRUSH old_brush;

				/* create memory device context for bitmap */
				memdc = CreateCompatibleDC(hdc);

				/* create standard bitmap, no alpha channel needed */
				membmp = CreateCompatibleBitmap(hdc, xdash, abs(ydash));
				oldbmp = (HBITMAP)SelectObject(memdc, membmp);

				/* prepare memory context */
				SetTextColor(memdc, fill_color);
				if ((fillstyle & 0x0f) == FS_TRANSPARENT_PATTERN)
					old_brush = SelectObject(memdc, pattern_brush[pattern]);
				else
					old_brush = SelectObject(memdc, solid_brush);

				/* draw into memory bitmap */
				PatBlt(memdc, 0, 0, width, height, PATCOPY);

				/* copy bitmap back */
				if ((fillstyle & 0x0f) == FS_TRANSPARENT_PATTERN) {
					TransparentBlt(hdc, p.x, p.y, width, height,
						   memdc, 0, 0, width, height, 0x00ffffff);
				} else {
					ftn.AlphaFormat = 0; /* no alpha channel in bitmap */
					ftn.SourceConstantAlpha = (UCHAR)(alpha * 0xff); /* global alpha */
					ftn.BlendOp = AC_SRC_OVER;
					ftn.BlendFlags = 0;
					AlphaBlend(hdc, p.x, p.y, width, height,
							   memdc, 0, 0, width, height, ftn);
				}

				/* clean up */
				SelectObject(memdc, old_brush);
				SelectObject(memdc, oldbmp);
				DeleteObject(membmp);
				DeleteDC(memdc);
			} else {
				/* not transparent */
				/* FIXME: this actually is transparent, but probably shouldn't be */
				PatBlt(hdc, p.x, p.y, width, height, PATCOPY);
			/*
				SelectObject(hdc, lpgw->hnull);
				Rectangle(hdc, p.x, p.y, p.x + width + 1, p.y + height + 1);
				SelectObject(hdc, lpgw->hapen);
			*/
			}
			polyi = 0;
			if (keysample)
				draw_update_keybox(lpgw, plotno, xdash + 1, ydash);
			break;
		}

		case W_text_angle:
			if (lpgw->angle != (int)curptr->x) {
				lpgw->angle = (int)curptr->x;
				SetFont(lpgw, hdc);
				/* recalculate shifting of rotated text */
				hshift = sin(M_PI/180. * lpgw->angle) * MulDiv(lpgw->vchar, rr-rl, lpgw->xmax) / 2;
				vshift = cos(M_PI/180. * lpgw->angle) * MulDiv(lpgw->vchar, rb-rt, lpgw->ymax) / 2;
			}
			break;

		case W_justify:
			draw_text_justify(hdc, curptr->x);
			lpgw->justify = curptr->x;
			break;

		case W_font: {
			int size = curptr->x;
			char * font = LocalLock(curptr->htext);
			/* GraphChangeFont already handles font==NULL and size==0,
			   so the checks below are a bit paranoid...
			*/
			GraphChangeFont(lpgw,
				font != NULL ? font : lpgw->deffontname,
				size > 0 ? size : lpgw->deffontsize,
				hdc, *rect);
			LocalUnlock(curptr->htext);
			SetFont(lpgw, hdc);
			/* recalculate shifting of rotated text */
			hshift = sin(M_PI/180. * lpgw->angle) * MulDiv(lpgw->vchar, rr-rl, lpgw->xmax) / 2;
			vshift = cos(M_PI/180. * lpgw->angle) * MulDiv(lpgw->vchar, rb-rt, lpgw->ymax) / 2;
			break;
		}

		case W_pointsize:
			if (curptr->x != 0) {
				double pointsize = curptr->x / 100.0;
				htic = pointsize * MulDiv(lpgw->htic, rr-rl, lpgw->xmax) + 1;
				vtic = pointsize * MulDiv(lpgw->vtic, rb-rt, lpgw->ymax) + 1;
			}
			break;

		case W_line_width:
			/* HBB 20000813: this may look strange, but it ensures
			 * that linewidth is exactly 1 iff it's in default
			 * state */
			line_width = curptr->x == 100 ? 1 : (curptr->x / 100.0);
			line_width *= lpgw->sampling * lpgw->linewidth * lw_scale;
			break;

		case W_setcolor: {
			COLORREF color;

			/* distinguish gray values and RGB colors */
			if (curptr->y == 0) {			/* TC_FRAC */
				rgb255_color rgb255;
				rgb255maxcolors_from_gray(curptr->x / (double)WIN_PAL_COLORS, &rgb255);
				color = RGB(rgb255.r, rgb255.g, rgb255.b);
			}
			else if (curptr->y == (TC_LT << 8)) {	/* TC_LT */
				int pen = (int)curptr->x % WGNUMPENS;
				if (pen <= LT_NODRAW)
					color = lpgw->background;
				else
					color = lpgw->colorpen[pen + 2].lopnColor;
			}
			else {					/* TC_RGB */
				color = RGB(curptr->y & 0xff, (curptr->x >> 8) & 0xff, curptr->x & 0xff);
			}

			/* solid fill brush */
			draw_new_brush(lpgw, hdc, color);
			solid_brush = lpgw->hcolorbrush;

			/* create new pen, too */
			cur_penstruct.lopnColor = color;
			draw_new_pens(lpgw, hdc, cur_penstruct);

			/* set text color, which is also used for pattern fill */
			SetTextColor(hdc, color);
			/* remember this color */
			last_color = color;
			fill_color = color;
			break;
		}

		case W_filled_polygon_pt: {
			/* a point of the polygon is coming */
			if (polyi >= polymax) {
				polymax += 200;
				ppt = (POINT *)LocalReAllocPtr(ppt, LHND, (polymax+1) * sizeof(POINT));
			}
			ppt[polyi].x = xdash;
			ppt[polyi].y = ydash;

			polyi++;
			break;
		}

		case W_filled_polygon_draw: {
			/* end of point series --> draw polygon now */
			if (!transparent) {
#ifdef HAVE_GDIPLUS
				if (lpgw->antialiasing && lpgw->polyaa && ((fillstyle & 0x0f) == FS_SOLID)) {
					/* solid, antialiased fill */
					gdiplusSolidFilledPolygonEx(hdc, ppt, polyi, fill_color, 1.0);
				} else if (lpgw->antialiasing && lpgw->patternaa && ((fillstyle & 0x0f) == FS_PATTERN)) {
					gdiplusPatternFilledPolygonEx(hdc, ppt, polyi, fill_color, 1., lpgw->background, transparent, pattern);
				} else
#endif
				{
					/* fill area without border */
					SelectObject(hdc, lpgw->hnull);
					Polygon(hdc, ppt, polyi);
					SelectObject(hdc, lpgw->hapen); /* restore previous pen */
				}
			} else {
#ifdef HAVE_GDIPLUS
				if (lpgw->antialiasing && lpgw->polyaa && (fillstyle & 0x0f) == FS_TRANSPARENT_SOLID) {
					gdiplusSolidFilledPolygonEx(hdc, ppt, polyi, fill_color, alpha);
				} else if (lpgw->antialiasing && lpgw->patternaa && ((fillstyle & 0x0f) == FS_TRANSPARENT_PATTERN)) {
					gdiplusPatternFilledPolygonEx(hdc, ppt, polyi, fill_color, 1., lpgw->background, transparent, pattern);
				} else
#endif
				{
				/* BM: To support transparent fill on Windows we draw the
				   polygon into a memory bitmap using a memory device context.

				   We then associate an alpha value to the bitmap and use
				   AlphaBlend() to copy the bitmap back.

				   Note: we could probably simplify and speed up the case of
				   pattern fill by using TransparentBlt() instead.
				*/
				HDC memdc;
				HBITMAP membmp, oldbmp;
				int minx, miny, maxx, maxy;
				UINT32 width, height;
				BITMAPINFO bmi;
				UINT32 *pvBits;
				int x, y, i;
				POINT * points;
				BLENDFUNCTION ftn;
				UINT32 uAlpha = (UCHAR)(0xff * alpha);
				/* make sure the indicator for transparency is different fill_color */
				UINT32 transparentColor = fill_color ^ 0x00ffffff;
				COLORREF bkColor = RGB((transparentColor >> 16) & 0xff, (transparentColor >> 8) & 0xff, transparentColor & 0xff);
				HBRUSH old_brush;
				HPEN old_pen;

				/* find minimum rectangle enclosing our polygon. */
				minx = maxx = ppt[0].x;
				miny = maxy = ppt[0].y;
				for (i = 1; i < polyi; i++) {
					minx = min(ppt[i].x, minx);
					miny = min(ppt[i].y, miny);
					maxx = max(ppt[i].x, maxx);
					maxy = max(ppt[i].y, maxy);
				}

				/* now shift polygon points to upper left corner */
				points = (POINT *)LocalAllocPtr(LHND, (polyi+1) * sizeof(POINT));
				for (i = 0; i < polyi; i++) {
					points[i].x = ppt[i].x - minx;
					points[i].y = ppt[i].y - miny;
				}

				/* create memory device context for bitmap */
				memdc = CreateCompatibleDC(hdc);

				/* create memory bitmap with alpha channel and minimal size */
				width  = maxx - minx;
				height = maxy - miny;
				ZeroMemory(&bmi, sizeof(BITMAPINFO));
				bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
				bmi.bmiHeader.biWidth = width;
				bmi.bmiHeader.biHeight = height;
				bmi.bmiHeader.biPlanes = 1;
				bmi.bmiHeader.biBitCount = 32;
				bmi.bmiHeader.biCompression = BI_RGB;
				bmi.bmiHeader.biSizeImage = width * height * 4;
				membmp = CreateDIBSection(memdc, &bmi, DIB_RGB_COLORS, (void **)&pvBits, NULL, 0x0);
				oldbmp = (HBITMAP)SelectObject(memdc, membmp);
				/* clear bitmap, could also do it via GDI */
				for (i = 0; i < width * height; i++)
					pvBits[i] = transparentColor;

				/* prepare the memory context */
				SetTextColor(memdc, fill_color);
				SetBkColor(memdc, bkColor);
				old_pen = SelectObject(memdc, lpgw->hnull);
				if ((fillstyle & 0x0f) == FS_TRANSPARENT_PATTERN)
					old_brush = SelectObject(memdc, pattern_brush[pattern]);
				else
					old_brush = SelectObject(memdc, solid_brush);

				/* finally, draw polygon */
				Polygon(memdc, points, polyi);

				/* add alpha channel to bitmap */
				/* Note: this is really a pre-scaled alpha channel, see MSDN.
				   To make life easy we only use global transparency, see below */
				for (y = 0; y < height; y++) {
					for (x = 0; x < width; x++) {
						UINT32 pixel = pvBits[x + y * width];
						if (pixel == transparentColor)
							pvBits[x + y * width]  = 0x00000000; /* completely transparent */
						else
							pvBits[x + y * width] |= 0xff000000; /* mark as completely opaque */
					}
				}

				/* copy to device with alpha blending */
				ftn.BlendOp = AC_SRC_OVER;
				ftn.BlendFlags = 0;
				ftn.AlphaFormat = AC_SRC_ALPHA; /* bitmap has an alpha channel */
				ftn.SourceConstantAlpha = uAlpha;
				AlphaBlend(hdc, minx, miny, width, height,
						   memdc, 0, 0, width, height, ftn);

				/* clean up */
				LocalFreePtr(points);
				SelectObject(memdc, old_pen);
				SelectObject(memdc, old_brush);
				SelectObject(memdc, oldbmp);
				DeleteObject(membmp);
				DeleteDC(memdc);
			}
			}
			polyi = 0;
			}
			break;

		case W_image:
			{
			/* Due to the structure of gwop in total 6 entries are needed.
			   These static variables help to collect all the information
			*/
			if (seq == 0) {
				/* First OP contains only the color mode */
				color_mode = curptr->x;
			} else if (seq < 5) {
				/* Next four OPs contain the `corner` array */
				corners[seq-1].x = xdash;
				corners[seq-1].y = ydash;
			} else {
				/* The last OP contains the image and it's size */
				char *image;
				unsigned int width, height;
				int rc;

				width = curptr->x;
				height = curptr->y;
				image = LocalLock(curptr->htext);
				if (image) {
					BITMAPINFO bmi;
					HRGN hrgn;

					ZeroMemory(&bmi, sizeof(BITMAPINFO));
					bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
					bmi.bmiHeader.biWidth = width;
					bmi.bmiHeader.biHeight = height;
					bmi.bmiHeader.biPlanes = 1;
					bmi.bmiHeader.biCompression = BI_RGB;

					/* create clip region */
					hrgn = CreateRectRgn(
						GPMIN(corners[2].x, corners[3].x), GPMIN(corners[2].y, corners[3].y),
						GPMAX(corners[2].x, corners[3].x) + 1, GPMAX(corners[2].y, corners[3].y) + 1);
					SelectClipRgn(hdc, hrgn);

					if (color_mode != IC_RGBA) {
						bmi.bmiHeader.biBitCount = 24;

						rc = StretchDIBits(hdc,
							GPMIN(corners[0].x, corners[1].x) , GPMIN(corners[0].y, corners[1].y),
							abs(corners[1].x - corners[0].x), abs(corners[1].y - corners[0].y),
							0, 0, width, height,
							image, &bmi, DIB_RGB_COLORS, SRCCOPY);
					} else {
						HDC memdc;
						HBITMAP membmp, oldbmp;
						UINT32 *pvBits;
						BLENDFUNCTION ftn;

						bmi.bmiHeader.biBitCount = 32;
						memdc = CreateCompatibleDC(hdc);
						membmp = CreateDIBSection(memdc, &bmi, DIB_RGB_COLORS, (void **)&pvBits, NULL, 0x0);
						oldbmp = (HBITMAP)SelectObject(memdc, membmp);

						memcpy(pvBits, image, width * height * 4);

						ftn.BlendOp = AC_SRC_OVER;
						ftn.BlendFlags = 0;
						ftn.AlphaFormat = AC_SRC_ALPHA; /* bitmap has an alpha channel */
						ftn.SourceConstantAlpha = 0xff;
						AlphaBlend(hdc,
							GPMIN(corners[0].x, corners[1].x) , GPMIN(corners[0].y, corners[1].y),
							abs(corners[1].x - corners[0].x), abs(corners[1].y - corners[0].y),
							memdc, 0, 0, width, height, ftn);

						SelectObject(memdc, oldbmp);
						DeleteObject(membmp);
						DeleteDC(memdc);
					}
					SelectClipRgn(hdc, NULL);
				}
				LocalUnlock(curptr->htext);
			}
			seq = (seq + 1) % 6;
			}
			break;

		case W_dot:
			dot(hdc, xdash, ydash);
			if (keysample) {
				draw_update_keybox(lpgw, plotno, xdash + htic, ydash + vtic);
				draw_update_keybox(lpgw, plotno, xdash - htic, ydash - vtic);
			}
			break;
		case W_plus: /* do plus */
		case W_star: /* do star: first plus, then cross */
#ifdef HAVE_GDIPLUS
			if (lpgw->antialiasing) {
				POINT a, b;
				a.x = xdash - htic;
				a.y = ydash;
				b.x = xdash + htic;
				b.y = ydash;
				gdiplusLineEx(hdc, a, b, PS_SOLID, line_width, last_color);
				a.x = xdash;
				a.y = ydash - vtic;
				b.x = xdash;
				b.y = ydash + vtic;
				gdiplusLineEx(hdc, a, b, PS_SOLID, line_width, last_color);
			} else
#endif
			{
				SelectObject(hdc, lpgw->hsolid);
				MoveTo(hdc, xdash - htic, ydash);
				LineTo(hdc, xdash + htic + 1, ydash);
				MoveTo(hdc, xdash, ydash - vtic);
				LineTo(hdc, xdash, ydash + vtic + 1);
				SelectObject(hdc, lpgw->hapen);
			}
			if (keysample) {
				draw_update_keybox(lpgw, plotno, xdash + htic, ydash + vtic);
				draw_update_keybox(lpgw, plotno, xdash - htic, ydash - vtic);
			}
			if (curptr->op == W_plus) break;
		case W_cross: /* do X */
#ifdef HAVE_GDIPLUS
			if (lpgw->antialiasing) {
				POINT a, b;
				a.x = xdash - htic;
				a.y = ydash - vtic;
				b.x = xdash + htic;
				b.y = ydash + vtic;
				gdiplusLineEx(hdc, a, b, PS_SOLID, line_width, last_color);
				a.x = xdash - htic;
				a.y = ydash + vtic;
				b.x = xdash + htic;
				b.y = ydash - vtic;
				gdiplusLineEx(hdc, a, b, PS_SOLID, line_width, last_color);
			} else
#endif
			{
				SelectObject(hdc, lpgw->hsolid);
				MoveTo(hdc, xdash - htic, ydash - vtic);
				LineTo(hdc, xdash + htic + 1, ydash + vtic + 1);
				MoveTo(hdc, xdash - htic, ydash + vtic);
				LineTo(hdc, xdash + htic + 1, ydash - vtic - 1);
				SelectObject(hdc, lpgw->hapen);
			}
			if (keysample) {
				draw_update_keybox(lpgw, plotno, xdash + htic, ydash + vtic);
				draw_update_keybox(lpgw, plotno, xdash - htic, ydash - vtic);
			}
			break;
		case W_circle: /* do open circle */
			SelectObject(hdc, lpgw->hsolid);
#ifdef HAVE_GDIPLUS
			if (lpgw->antialiasing) {
				POINT p;
				p.x = xdash;
				p.y = ydash;
				gdiplusCircleEx(hdc, &p, htic, PS_SOLID, line_width, last_color);
			} else
#endif
				Arc(hdc, xdash-htic, ydash-vtic, xdash+htic+1, ydash+vtic+1,
					xdash, ydash+vtic+1, xdash, ydash+vtic+1);
			dot(hdc, xdash, ydash);
			SelectObject(hdc, lpgw->hapen);
			if (keysample) {
				draw_update_keybox(lpgw, plotno, xdash + htic, ydash + vtic);
				draw_update_keybox(lpgw, plotno, xdash - htic, ydash - vtic);
			}
			break;
		case W_fcircle: /* do filled circle */
			SelectObject(hdc, lpgw->hsolid);
			Ellipse(hdc, xdash-htic, ydash-vtic,
				xdash+htic+1, ydash+vtic+1);
			SelectObject(hdc, lpgw->hapen);
#ifdef HAVE_GDIPLUS
			if (lpgw->antialiasing) {
				POINT p;
				p.x = xdash;
				p.y = ydash;
				gdiplusCircleEx(hdc, &p, htic, PS_SOLID, line_width, last_color);
			}
#endif
			if (keysample) {
				draw_update_keybox(lpgw, plotno, xdash + htic, ydash + vtic);
				draw_update_keybox(lpgw, plotno, xdash - htic, ydash - vtic);
			}
			break;
		default:	/* potentially closed figure */
			{
			POINT p[6];
			int i;
			int shape = 0;
			int filled = 0;
			int index = 0;
			static float pointshapes[6][10] = {
				{-1, -1, +1, -1, +1, +1, -1, +1, 0, 0}, /* box */
				{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, /* dummy, circle */
				{ 0, +1, -1,  0,  0, -1, +1,  0, 0, 0}, /* diamond */
				{ 0, -4./3, -4./3, 2./3,
				  4./3,  2./3, 0, 0}, /* triangle */
				{ 0, 4./3, -4./3, -2./3,
				  4./3,  -2./3, 0, 0}, /* inverted triangle */
				{ 0, 1, 0.95106, 0.30902, 0.58779, -0.80902,
				  -0.58779, -0.80902, -0.95106, 0.30902} /* pentagon */
			};

			/* BM: calculate index, instead of an ugly long switch statement;
			   Depends on definition of commands in wgnuplib.h.
			*/
			index = (curptr->op - W_box);
			if ((index > 0) && (index <= W_fpentagon - W_box)) {
				shape = index / 2;
				filled = (index % 2) > 0;
			}

			for (i = 0; i < 5; ++i) {
				if (pointshapes[shape][i * 2 + 1] == 0
				 && pointshapes[shape][i * 2] == 0)
					break;
				p[i].x = xdash + htic * pointshapes[shape][i * 2] + 0.5;
				p[i].y = ydash + vtic * pointshapes[shape][i * 2 + 1] + 0.5;
			}
			if (filled) {
				/* Filled polygon */
#ifdef HAVE_GDIPLUS
				if (lpgw->antialiasing) {
					/* filled polygon with border */
					gdiplusSolidFilledPolygonEx(hdc, p, i, last_color, 1.);
				} else
#endif
				{
					SelectObject(hdc, lpgw->hsolid);
					Polygon(hdc, p, i);
					SelectObject(hdc, lpgw->hapen);
				}
			} else {
				/* Outline polygon */
				p[i].x = p[0].x;
				p[i].y = p[0].y;
#ifdef HAVE_GDIPLUS
				if (lpgw->antialiasing) {
					gdiplusPolylineEx(hdc, p, i + 1, PS_SOLID, line_width, last_color);
				} else
#endif
				{
					SelectObject(hdc, lpgw->hsolid);
					Polyline(hdc, p, i + 1);
					SelectObject(hdc, lpgw->hapen);
				}
				dot(hdc, xdash, ydash);
			}
			if (keysample) {
				draw_update_keybox(lpgw, plotno, xdash + htic, ydash + vtic);
				draw_update_keybox(lpgw, plotno, xdash - htic, ydash - vtic);
			}
			} /* default case */
		} /* switch(opcode) */
		} /* hide layer? */

		lastop = curptr->op;
		ngwop++;
		curptr++;
		if ((unsigned)(curptr - blkptr->gwop) >= GWOPMAX) {
			GlobalUnlock(blkptr->hblk);
			blkptr->gwop = (struct GWOP *)NULL;
			if ((blkptr = blkptr->next) == NULL)
				/* If exact multiple of GWOPMAX entries are queued,
				 * next will be NULL. Only the next GraphOp() call would
				 * have allocated a new block */
				return;
			if (!blkptr->gwop)
				blkptr->gwop = (struct GWOP *)GlobalLock(blkptr->hblk);
			if (!blkptr->gwop)
				return;
			curptr = (struct GWOP *)blkptr->gwop;
		}
    }
    if (polyi >= 2) {
#ifdef HAVE_GDIPLUS
		if (lpgw->antialiasing)
			gdiplusPolyline(hdc, ppt, polyi, &cur_penstruct);
		else
#endif
			Polyline(hdc, ppt, polyi);
	}
    LocalFreePtr(ppt);
}

/* ================================== */

/* save graph windows as enhanced metafile
 * The code in here is very similar to what CopyClip does...
 */
static void
SaveAsEMF(LPGW lpgw)
{
    char *cwd;
    static OPENFILENAME Ofn;
    static char lpstrCustomFilter[256] = { '\0' };
    static char lpstrFileName[MAX_PATH] = { '\0' };
    static char lpstrFileTitle[MAX_PATH] = { '\0' };
    HWND hwnd = lpgw->hWndGraph;

    Ofn.lStructSize = sizeof(OPENFILENAME);
    Ofn.hwndOwner = hwnd;
    Ofn.lpstrInitialDir = (LPSTR)NULL;
    Ofn.lpstrFilter = (LPCTSTR) "Enhanced Metafile (*.EMF)\0*.EMF\0All Files (*.*)\0*.*\0";
    Ofn.lpstrCustomFilter = lpstrCustomFilter;
    Ofn.nMaxCustFilter = 255;
    Ofn.nFilterIndex = 1;   /* start with the *.emf filter */
    Ofn.lpstrFile = lpstrFileName;
    Ofn.nMaxFile = MAX_PATH;
    Ofn.lpstrFileTitle = lpstrFileTitle;
    Ofn.nMaxFileTitle = MAX_PATH;
    Ofn.lpstrInitialDir = (LPSTR)NULL;
    Ofn.lpstrTitle = (LPSTR)NULL;
    Ofn.Flags = OFN_OVERWRITEPROMPT;
    Ofn.lpstrDefExt = (LPSTR) "emf";

    /* save cwd as GetSaveFileName apparently changes it */
    cwd = _getcwd(NULL, 0);

	if (GetSaveFileName(&Ofn) != 0) {
		RECT rect, mfrect;
		HDC hdc;
		HENHMETAFILE hemf;
		HDC hmf;
		TBOOLEAN antialiasing;

		/* get the context */
		hdc = GetDC(hwnd);
		GetPlotRect(lpgw, &rect);
		GetPlotRectInMM(lpgw, &mfrect, hdc);

		/* temporarily disable antialiasing */
		antialiasing = lpgw->antialiasing;
		lpgw->antialiasing = FALSE;

		hmf = CreateEnhMetaFile(hdc, Ofn.lpstrFile, &mfrect, (LPCTSTR)NULL);
		/* Always create EMF files using GDI only! */
		drawgraph(lpgw, hmf, (LPRECT) &rect);
		hemf = CloseEnhMetaFile(hmf);

		lpgw->antialiasing = antialiasing;

		DeleteEnhMetaFile(hemf);
		ReleaseDC(hwnd, hdc);

		/* restore cwd */
		if (cwd != NULL)
			_chdir( cwd );
    }

    /* free the cwd buffer allcoated by _getcwd */
    free(cwd);
}

/* ================================== */

/* copy graph window to clipboard --- note that the Metafile is drawn at the full
 * virtual resolution of the Windows terminal driver (24000 x 18000 pixels), to
 * preserve as much accuracy as remotely possible */
static void
CopyClip(LPGW lpgw)
{
	RECT rect, mfrect;
	HDC mem, hmf;
	HBITMAP bitmap;
	HENHMETAFILE hemf;
	HWND hwnd;
	HDC hdc;

	hwnd = lpgw->hWndGraph;

	/* view the window */
	if (IsIconic(hwnd))
		ShowWindow(hwnd, SW_SHOWNORMAL);
	BringWindowToTop(hwnd);
	UpdateWindow(hwnd);

	/* get the context */
	hdc = GetDC(hwnd);
	GetPlotRect(lpgw, &rect);

	/* make a bitmap and copy it there */
	mem = CreateCompatibleDC(hdc);
	bitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left,
			rect.bottom - rect.top);
	if (bitmap) {
		/* there is enough memory and the bitmaps OK */
		SelectObject(mem, bitmap);
		BitBlt(mem,0,0,rect.right - rect.left,
			rect.bottom - rect.top, hdc, rect.left,
			rect.top, SRCCOPY);
	} else {
		MessageBeep(MB_ICONHAND);
		MessageBox(hwnd, "Insufficient Memory to Copy Clipboard",
			lpgw->Title, MB_ICONHAND | MB_OK);
	}
	DeleteDC(mem);

	/* OK, bitmap done, now create an enhanced Metafile context
	 * and redraw the whole plot into that.
	 */
	{
		/* make copy of window's main status struct for modification */
		GW gwclip = *lpgw;

		/* disable antialiasing: do not mix GDI/GDI+ */
		gwclip.antialiasing = FALSE;

		gwclip.hfonth = gwclip.hfontv = 0;
		MakePens(&gwclip, hdc);
		MakeFonts(&gwclip, &rect, hdc);

		GetPlotRectInMM(lpgw, &mfrect, hdc);

		hmf = CreateEnhMetaFile(hdc, (LPCTSTR)NULL, &mfrect, (LPCTSTR)NULL);
		/* Always create EMF files using GDI only! */
		drawgraph(&gwclip, hmf, (LPRECT) &rect);
		hemf = CloseEnhMetaFile(hmf);

		DestroyFonts(&gwclip);
		DestroyPens(&gwclip);
	}

	/* Now we have the Metafile and Bitmap prepared, post their contents to
	 * the Clipboard */
	OpenClipboard(hwnd);
	EmptyClipboard();
	SetClipboardData(CF_ENHMETAFILE,hemf);
	SetClipboardData(CF_BITMAP, bitmap);
	CloseClipboard();
	ReleaseDC(hwnd, hdc);
	DeleteEnhMetaFile(hemf);
	return;
}


/* copy graph window to printer */
static void
CopyPrint(LPGW lpgw)
{
	DOCINFO docInfo;

	HDC printer;
	PRINTDLG pd;
	static DEVNAMES * pDevNames = NULL;
	static DEVMODE * pDevMode = NULL;
	LPCTSTR szDriver, szDevice, szOutput;
	HWND hwnd = lpgw->hWndGraph;
	RECT rect;
	GP_PRINT pr;

	/* Print Setup Dialog */

	/* See http://support.microsoft.com/kb/240082 */
	_fmemset (&pd, 0, sizeof pd);
	pd.lStructSize = sizeof pd;
	pd.hwndOwner = hwnd;
	pd.Flags = PD_PRINTSETUP;
	pd.hDevNames = pDevNames;
	pd.hDevMode = pDevMode;

	if (!PrintDlg(&pd))
		return;

	pDevNames = (DEVNAMES *) GlobalLock(pd.hDevNames);
	pDevMode = (DEVMODE *) GlobalLock(pd.hDevMode);

	szDriver = (LPCTSTR)pDevNames + pDevNames->wDriverOffset;
	szDevice = (LPCTSTR)pDevNames + pDevNames->wDeviceOffset;
	szOutput = (LPCTSTR)pDevNames + pDevNames->wOutputOffset;

	printer = CreateDC(szDriver, szDevice, szOutput, pDevMode);

	GlobalUnlock(pd.hDevMode);
	GlobalUnlock(pd.hDevNames);

	/* We no longer free these structures, but preserve them for the next time
	GlobalFree(pd.hDevMode);
	GlobalFree(pd.hDevNames);
	*/

	if (printer == NULL)
		return;	/* abort */


	/* Print Size Dialog */

	if (!PrintSize(printer, hwnd, &rect)) {
		DeleteDC(printer);
		return; /* abort */
	}

	pr.hdcPrn = printer;
	SetWindowLong(hwnd, 4, (LONG)((GP_LPPRINT)&pr));
	PrintRegister((GP_LPPRINT)&pr);

	EnableWindow(hwnd, FALSE);
	pr.bUserAbort = FALSE;
	pr.hDlgPrint = CreateDialogParam(hdllInstance, "CancelDlgBox",
									hwnd, PrintDlgProc, (LPARAM)lpgw->Title);
	SetAbortProc(printer, PrintAbortProc);

	memset(&docInfo, 0, sizeof(DOCINFO));
	docInfo.cbSize = sizeof(DOCINFO);
	docInfo.lpszDocName = lpgw->Title;

	if (StartDoc(printer, &docInfo) > 0) {
		TBOOLEAN aa = lpgw->antialiasing;
		lpgw->sampling = 1;
		/* Mixing GDI/GDI+ does not seem to work properly on printer devices. */
		lpgw->antialiasing = FALSE;
		SetMapMode(printer, MM_TEXT);
		SetBkMode(printer, OPAQUE);
		StartPage(printer);
		DestroyFonts(lpgw);
		MakeFonts(lpgw, &rect, printer);
		DestroyPens(lpgw);	/* rebuild pens */
		MakePens(lpgw, printer);
		/* Always print using GDI only! */
		drawgraph(lpgw, printer, &rect);
		if (EndPage(printer) > 0)
			EndDoc(printer);
		lpgw->antialiasing = aa;
	}
	if (!pr.bUserAbort) {
		EnableWindow(hwnd, TRUE);
		DestroyWindow(pr.hDlgPrint);
	}
	DeleteDC(printer);
	SetWindowLong(hwnd, 4, (LONG)(0L));
	PrintUnregister((GP_LPPRINT)&pr);
	/* make certain that the screen pen set is restored */
	SendMessage(lpgw->hWndGraph, WM_COMMAND, M_REBUILDTOOLS, 0L);
	return;
}


/* ================================== */
/*  INI file stuff */
static void
WriteGraphIni(LPGW lpgw)
{
	RECT rect;
	int i;
	char entry[32];
	LPLOGPEN pc;
	LPLOGPEN pm;
	LPSTR file = lpgw->IniFile;
	LPSTR section = lpgw->IniSection;
	char profile[80];

	if ((file == (LPSTR)NULL) || (section == (LPSTR)NULL))
		return;
	if (IsIconic(lpgw->hWndGraph))
		ShowWindow(lpgw->hWndGraph, SW_SHOWNORMAL);
	GetWindowRect(lpgw->hWndGraph,&rect);
	wsprintf(profile, "%d %d", rect.left, rect.top);
	WritePrivateProfileString(section, "GraphOrigin", profile, file);
	wsprintf(profile, "%d %d", rect.right-rect.left, rect.bottom-rect.top);
	WritePrivateProfileString(section, "GraphSize", profile, file);
	wsprintf(profile, "%s,%d", lpgw->deffontname, lpgw->deffontsize);
	WritePrivateProfileString(section, "GraphFont", profile, file);
	strcpy(WIN_inifontname, lpgw->deffontname);
	WIN_inifontsize = lpgw->deffontsize;
	wsprintf(profile, "%d", lpgw->color);
	WritePrivateProfileString(section, "GraphColor", profile, file);
	wsprintf(profile, "%d", lpgw->graphtotop);
	WritePrivateProfileString(section, "GraphToTop", profile, file);
	wsprintf(profile, "%d", lpgw->doublebuffer);
	WritePrivateProfileString(section, "GraphDoublebuffer", profile, file);
	wsprintf(profile, "%d", lpgw->oversample);
	WritePrivateProfileString(section, "GraphOversampling", profile, file);
	wsprintf(profile, "%d", lpgw->antialiasing);
	WritePrivateProfileString(section, "GraphAntialiasing", profile, file);
	wsprintf(profile, "%d", lpgw->polyaa);
	WritePrivateProfileString(section, "GraphPolygonAA", profile, file);
	wsprintf(profile, "%d", lpgw->patternaa);
	WritePrivateProfileString(section, "GraphPatternAA", profile, file);
	wsprintf(profile, "%d %d %d",GetRValue(lpgw->background),
			GetGValue(lpgw->background), GetBValue(lpgw->background));
	WritePrivateProfileString(section, "GraphBackground", profile, file);

	/* now save pens */
	for (i=0; i<WGNUMPENS+2; i++) {
		if (i==0)
			_fstrcpy(entry,"Border");
		else if (i==1)
			_fstrcpy(entry,"Axis");
		else
			 wsprintf(entry,"Line%d",i-1);
		pc = &lpgw->colorpen[i];
		pm = &lpgw->monopen[i];
		wsprintf(profile, "%d %d %d %d %d",GetRValue(pc->lopnColor),
			GetGValue(pc->lopnColor), GetBValue(pc->lopnColor),
			(pc->lopnWidth.x != 1) ? -pc->lopnWidth.x : pc->lopnStyle,
			(pm->lopnWidth.x != 1) ? -pm->lopnWidth.x : pm->lopnStyle);
		WritePrivateProfileString(section, entry, profile, file);
	}
	return;
}


static char * GraphDefaultFont(void)
{
	if (GetACP() == 932) /* Japanese Shift-JIS */
		return WINJPFONT;
	else
		return WINFONT;
};


static void
ReadGraphIni(LPGW lpgw)
{
	LPSTR file = lpgw->IniFile;
	LPSTR section = lpgw->IniSection;
	char profile[81];
	char entry[32];
	LPSTR p;
	int i,r,g,b,colorstyle,monostyle;
	COLORREF ref;
	BOOL bOKINI;

	bOKINI = (file != (LPSTR)NULL) && (section != (LPSTR)NULL);
	if (!bOKINI)
		profile[0] = '\0';

	if (bOKINI)
		GetPrivateProfileString(section, "GraphOrigin", "", profile, 80, file);
	if ( (p = GetInt(profile, (LPINT)&lpgw->Origin.x)) == NULL)
		lpgw->Origin.x = CW_USEDEFAULT;
	if ( (p = GetInt(p, (LPINT)&lpgw->Origin.y)) == NULL)
		lpgw->Origin.y = CW_USEDEFAULT;
	if (bOKINI)
		GetPrivateProfileString(section, "GraphSize", "", profile, 80, file);
	if ( (p = GetInt(profile, (LPINT)&lpgw->Size.x)) == NULL)
		lpgw->Size.x = CW_USEDEFAULT;
	if ( (p = GetInt(p, (LPINT)&lpgw->Size.y)) == NULL)
		lpgw->Size.y = CW_USEDEFAULT;

	if (bOKINI)
	  GetPrivateProfileString(section, "GraphFont", "", profile, 80, file);
	{
		char *size;
		size = _fstrchr(profile,',');
		if (size) {
			*size++ = '\0';
			if ( (p = GetInt(size, (LPINT)&lpgw->fontsize)) == NULL)
				lpgw->fontsize = WINFONTSIZE;
		}
		_fstrcpy(lpgw->fontname, profile);
		if (lpgw->fontsize == 0)
			lpgw->fontsize = WINFONTSIZE;
		if (!(*lpgw->fontname))
			_fstrcpy(lpgw->fontname, GraphDefaultFont());
		/* set current font as default font */
		_fstrcpy(lpgw->deffontname, lpgw->fontname);
		lpgw->deffontsize = lpgw->fontsize;
		strcpy(WIN_inifontname, lpgw->deffontname);
		WIN_inifontsize = lpgw->deffontsize;
	}

	if (bOKINI)
		GetPrivateProfileString(section, "GraphColor", "", profile, 80, file);
	if ( (p = GetInt(profile, (LPINT)&lpgw->color)) == NULL)
		lpgw->color = TRUE;

	if (bOKINI)
		GetPrivateProfileString(section, "GraphToTop", "", profile, 80, file);
	if ( (p = GetInt(profile, (LPINT)&lpgw->graphtotop)) == NULL)
		lpgw->graphtotop = TRUE;

	if (bOKINI)
		GetPrivateProfileString(section, "GraphDoubleBuffer", "", profile, 80, file);
	if ( (p = GetInt(profile, (LPINT)&lpgw->doublebuffer)) == NULL)
		lpgw->doublebuffer = TRUE;

	if (bOKINI)
		GetPrivateProfileString(section, "GraphOversampling", "", profile, 80, file);
	if ( (p = GetInt(profile, (LPINT)&lpgw->oversample)) == NULL)
		lpgw->oversample = FALSE;

	if (bOKINI)
		GetPrivateProfileString(section, "GraphAntialiasing", "", profile, 80, file);
	if ( (p = GetInt(profile, (LPINT)&lpgw->antialiasing)) == NULL)
		lpgw->antialiasing = TRUE;

	if (bOKINI)
		GetPrivateProfileString(section, "GraphPolygonAA", "", profile, 80, file);
	if ((p = GetInt(profile, (LPINT)&lpgw->polyaa)) == NULL)
		lpgw->polyaa = FALSE;

	if (bOKINI)
		GetPrivateProfileString(section, "GraphPatternAA", "", profile, 80, file);
	if ((p = GetInt(profile, (LPINT)&lpgw->patternaa)) == NULL)
		lpgw->patternaa = TRUE;

	lpgw->background = RGB(255,255,255);
	if (bOKINI)
		GetPrivateProfileString(section, "GraphBackground", "", profile, 80, file);
	if ( ((p = GetInt(profile, (LPINT)&r)) != NULL) &&
	     ((p = GetInt(p, (LPINT)&g)) != NULL) &&
	     ((p = GetInt(p, (LPINT)&b)) != NULL) )
		lpgw->background = RGB(r,g,b);

	StorePen(lpgw, 0, RGB(0,0,0), PS_SOLID, PS_SOLID);
	if (bOKINI)
		GetPrivateProfileString(section, "Border", "", profile, 80, file);
	if ( ((p = GetInt(profile, (LPINT)&r)) != NULL) &&
	     ((p = GetInt(p, (LPINT)&g)) != NULL) &&
	     ((p = GetInt(p, (LPINT)&b)) != NULL) &&
	     ((p = GetInt(p, (LPINT)&colorstyle)) != NULL) &&
	     ((p = GetInt(p, (LPINT)&monostyle)) != NULL) )
		StorePen(lpgw, 0, RGB(r,g,b), colorstyle, monostyle);

	StorePen(lpgw, 1, RGB(192,192,192), PS_DOT, PS_DOT);
	if (bOKINI)
		GetPrivateProfileString(section, "Axis", "", profile, 80, file);
	if ( ((p = GetInt(profile, (LPINT)&r)) != NULL) &&
	     ((p = GetInt(p, (LPINT)&g)) != NULL) &&
	     ((p = GetInt(p, (LPINT)&b)) != NULL) &&
	     ((p = GetInt(p, (LPINT)&colorstyle)) != NULL) &&
	     ((p = GetInt(p, (LPINT)&monostyle)) != NULL) )
		StorePen(lpgw, 1, RGB(r,g,b), colorstyle, monostyle);

	for (i=0; i<WGNUMPENS; i++) {
		ref = wginitcolor[ i%WGDEFCOLOR ];
		colorstyle = wginitstyle[ (i/WGDEFCOLOR) % WGDEFSTYLE ];
		monostyle  = wginitstyle[ i%WGDEFSTYLE ];
		StorePen(lpgw, i+2, ref, colorstyle, monostyle);
		wsprintf(entry,"Line%d",i+1);
		if (bOKINI)
			GetPrivateProfileString(section, entry, "", profile, 80, file);
		if ( ((p = GetInt(profile, (LPINT)&r)) != NULL) &&
		     ((p = GetInt(p, (LPINT)&g)) != NULL) &&
		     ((p = GetInt(p, (LPINT)&b)) != NULL) &&
		     ((p = GetInt(p, (LPINT)&colorstyle)) != NULL) &&
		     ((p = GetInt(p, (LPINT)&monostyle)) != NULL) )
			StorePen(lpgw, i + 2, RGB(r,g,b), colorstyle, monostyle);
	}
}


/* ================================== */

/* the "Line Styles..." dialog and its support functions */
/* FIXME HBB 20010218: this might better be delegated to a separate source file */

#define LS_DEFLINE 2
typedef struct tagLS {
	int	widtype;
	int	wid;
	HWND	hwnd;
	int	pen;			/* current pen number */
	LOGPEN	colorpen[WGNUMPENS+2];	/* logical color pens */
	LOGPEN	monopen[WGNUMPENS+2];	/* logical mono pens */
} LS;
typedef LS *  LPLS;


static COLORREF
GetColor(HWND hwnd, COLORREF ref)
{
	CHOOSECOLOR cc;
	COLORREF aclrCust[16];
	int i;

	for (i=0; i<16; i++) {
		aclrCust[i] = RGB(0,0,0);
	}
	_fmemset(&cc, 0, sizeof(CHOOSECOLOR));
	cc.lStructSize = sizeof(CHOOSECOLOR);
	cc.hwndOwner = hwnd;
	cc.lpCustColors = aclrCust;
	cc.rgbResult = ref;
	cc.Flags = CC_RGBINIT;
	if (ChooseColor(&cc))
		return cc.rgbResult;
	return ref;
}


/* force update of owner draw button */
static void
UpdateColorSample(HWND hdlg)
{
	RECT rect;
	POINT ptul, ptlr;
	GetWindowRect( GetDlgItem(hdlg, LS_COLORSAMPLE), &rect);
	ptul.x = rect.left;
	ptul.y = rect.top;
	ptlr.x = rect.right;
	ptlr.y = rect.bottom;
	ScreenToClient(hdlg, &ptul);
	ScreenToClient(hdlg, &ptlr);
	rect.left   = ptul.x;
	rect.top    = ptul.y;
	rect.right  = ptlr.x;
	rect.bottom = ptlr.y;
	InvalidateRect(hdlg, &rect, TRUE);
	UpdateWindow(hdlg);
}

/* Window handler function for the "Line Styles" dialog */
BOOL CALLBACK
LineStyleDlgProc(HWND hdlg, UINT wmsg, WPARAM wparam, LPARAM lparam)
{
	char buf[16];
	LPLS lpls;
	int i;
	UINT pen;
	LPLOGPEN plpm, plpc;
	lpls = (LPLS)GetWindowLong(GetParent(hdlg), 4);

	switch (wmsg) {
		case WM_INITDIALOG:
			pen = 2;
			for (i=0; i<WGNUMPENS+2; i++) {
				if (i==0)
					_fstrcpy(buf,"Border");
				else if (i==1)
					_fstrcpy(buf,"Axis");
				else
			 		wsprintf(buf,"Line%d",i-1);
				SendDlgItemMessage(hdlg, LS_LINENUM, LB_ADDSTRING, 0,
					(LPARAM)((LPSTR)buf));
			}
			SendDlgItemMessage(hdlg, LS_LINENUM, LB_SETCURSEL, pen, 0L);

			SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
				(LPARAM)((LPSTR)"Solid"));
			SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
				(LPARAM)((LPSTR)"Dash"));
			SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
				(LPARAM)((LPSTR)"Dot"));
			SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
				(LPARAM)((LPSTR)"DashDot"));
			SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
				(LPARAM)((LPSTR)"DashDotDot"));

			plpm = &lpls->monopen[pen];
			SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_SETCURSEL,
				plpm->lopnStyle, 0L);
			wsprintf(buf,"%d",plpm->lopnWidth.x);
			SetDlgItemText(hdlg, LS_MONOWIDTH, buf);

			SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
				(LPARAM)((LPSTR)"Solid"));
			SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
				(LPARAM)((LPSTR)"Dash"));
			SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
				(LPARAM)((LPSTR)"Dot"));
			SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
				(LPARAM)((LPSTR)"DashDot"));
			SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
				(LPARAM)((LPSTR)"DashDotDot"));

			plpc = &lpls->colorpen[pen];
			SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_SETCURSEL,
				plpc->lopnStyle, 0L);
			wsprintf(buf,"%d",plpc->lopnWidth.x);
			SetDlgItemText(hdlg, LS_COLORWIDTH, buf);

			return TRUE;
		case WM_COMMAND:
			pen = (UINT)SendDlgItemMessage(hdlg, LS_LINENUM, LB_GETCURSEL, 0, 0L);
			plpm = &lpls->monopen[pen];
			plpc = &lpls->colorpen[pen];
			switch (LOWORD(wparam)) {
				case LS_LINENUM:
					wsprintf(buf,"%d",plpm->lopnWidth.x);
					SetDlgItemText(hdlg, LS_MONOWIDTH, buf);
					SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_SETCURSEL,
						plpm->lopnStyle, 0L);
					wsprintf(buf,"%d",plpc->lopnWidth.x);
					SetDlgItemText(hdlg, LS_COLORWIDTH, buf);
					SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_SETCURSEL,
						plpc->lopnStyle, 0L);
					UpdateColorSample(hdlg);
					return FALSE;
				case LS_MONOSTYLE:
					plpm->lopnStyle =
						(UINT)SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_GETCURSEL, 0, 0L);
					if (plpm->lopnStyle != 0) {
						plpm->lopnWidth.x = 1;
						wsprintf(buf,"%d",plpm->lopnWidth.x);
						SetDlgItemText(hdlg, LS_MONOWIDTH, buf);
					}
					return FALSE;
				case LS_MONOWIDTH:
					GetDlgItemText(hdlg, LS_MONOWIDTH, buf, 15);
					GetInt(buf, (LPINT)&plpm->lopnWidth.x);
					if (plpm->lopnWidth.x != 1) {
						plpm->lopnStyle = 0;
						SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_SETCURSEL,
							plpm->lopnStyle, 0L);
					}
					return FALSE;
				case LS_CHOOSECOLOR:
					plpc->lopnColor = GetColor(hdlg, plpc->lopnColor);
					UpdateColorSample(hdlg);
					return FALSE;
				case LS_COLORSTYLE:
					plpc->lopnStyle =
						(UINT)SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_GETCURSEL, 0, 0L);
					if (plpc->lopnStyle != 0) {
						plpc->lopnWidth.x = 1;
						wsprintf(buf,"%d",plpc->lopnWidth.x);
						SetDlgItemText(hdlg, LS_COLORWIDTH, buf);
					}
					return FALSE;
				case LS_COLORWIDTH:
					GetDlgItemText(hdlg, LS_COLORWIDTH, buf, 15);
					GetInt(buf, (LPINT)&plpc->lopnWidth.x);
					if (plpc->lopnWidth.x != 1) {
						plpc->lopnStyle = 0;
						SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_SETCURSEL,
							plpc->lopnStyle, 0L);
					}
					return FALSE;
				case LS_DEFAULT:
					plpm = lpls->monopen;
					plpc = lpls->colorpen;
					/* border */
					plpc->lopnColor   = RGB(0,0,0);
					plpc->lopnStyle   = PS_SOLID;
					plpc->lopnWidth.x = 1;
					plpm->lopnStyle   = PS_SOLID;
					plpm->lopnWidth.x = 1;
					plpc++; plpm++;
					/* axis */
					plpc->lopnColor   = RGB(192,192,192);
					plpc->lopnStyle   = PS_DOT;
					plpc->lopnWidth.x = 1;
					plpm->lopnStyle   = PS_DOT;
					plpm->lopnWidth.x = 1;
					/* LineX */
					for (i=0; i<WGNUMPENS; i++) {
						plpc++; plpm++;
						plpc->lopnColor   = wginitcolor[ i%WGDEFCOLOR ];
						plpc->lopnStyle   = wginitstyle[ (i/WGDEFCOLOR) % WGDEFSTYLE ];
						plpc->lopnWidth.x = 1;
						plpm->lopnStyle   = wginitstyle[ i%WGDEFSTYLE ];
						plpm->lopnWidth.x = 1;
					}
					/* update window */
					plpm = &lpls->monopen[pen];
					plpc = &lpls->colorpen[pen];
					SendDlgItemMessage(hdlg, LS_LINENUM, LB_SETCURSEL, pen, 0L);
					wsprintf(buf,"%d",plpm->lopnWidth.x);
					SetDlgItemText(hdlg, LS_MONOWIDTH, buf);
					SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_SETCURSEL,
						plpm->lopnStyle, 0L);
					wsprintf(buf,"%d",plpc->lopnWidth.x);
					SetDlgItemText(hdlg, LS_COLORWIDTH, buf);
					SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_SETCURSEL,
						plpc->lopnStyle, 0L);
					UpdateColorSample(hdlg);
					return FALSE;
				case IDOK:
					EndDialog(hdlg, IDOK);
					return TRUE;
				case IDCANCEL:
					EndDialog(hdlg, IDCANCEL);
					return TRUE;
			}
			break;
		case WM_DRAWITEM:
			{
			HBRUSH hBrush;
			LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lparam;
			pen = (UINT)SendDlgItemMessage(hdlg, LS_LINENUM, LB_GETCURSEL, (WPARAM)0, (LPARAM)0);
			plpc = &lpls->colorpen[pen];
			hBrush = CreateSolidBrush(plpc->lopnColor);
			FillRect(lpdis->hDC, &lpdis->rcItem, hBrush);
			FrameRect(lpdis->hDC, &lpdis->rcItem, (HBRUSH)GetStockObject(BLACK_BRUSH));
			DeleteObject(hBrush);
			}
			return FALSE;
	}
	return FALSE;
}



/* GetWindowLong(hwnd, 4) must be available for use */
static BOOL
LineStyle(LPGW lpgw)
{
	BOOL status = FALSE;
	LS ls;

	SetWindowLong(lpgw->hWndGraph, 4, (LONG)((LPLS)&ls));
	_fmemcpy(&ls.colorpen, &lpgw->colorpen, (WGNUMPENS + 2) * sizeof(LOGPEN));
	_fmemcpy(&ls.monopen, &lpgw->monopen, (WGNUMPENS + 2) * sizeof(LOGPEN));

	if (DialogBox (hdllInstance, "LineStyleDlgBox", lpgw->hWndGraph, LineStyleDlgProc)
		== IDOK) {
		_fmemcpy(&lpgw->colorpen, &ls.colorpen, (WGNUMPENS + 2) * sizeof(LOGPEN));
		_fmemcpy(&lpgw->monopen, &ls.monopen, (WGNUMPENS + 2) * sizeof(LOGPEN));
		status = TRUE;
	}
	SetWindowLong(lpgw->hWndGraph, 4, (LONG)(0L));
	return status;
}

#ifdef USE_MOUSE
/* ================================== */
/* HBB 20010207: new helper functions: wrapper around gp_exec_event
 * and DoZoombox. These may vanish again, as has the original idea I
 * invented them for... */

static void
Wnd_exec_event(LPGW lpgw, LPARAM lparam, char type, int par1)
{
    int mx, my;
    static unsigned long lastTimestamp = 0;
    unsigned long thisTimestamp = GetMessageTime();
    int par2 = thisTimestamp - lastTimestamp;

	if (lpgw == graphwin) {
		if (type == GE_keypress)
			par2 = 0;

		GetMousePosViewport(lpgw, &mx, &my);
		gp_exec_event(type, mx, my, par1, par2, 0);
		lastTimestamp = thisTimestamp;
	}
}

static void
Wnd_refresh_zoombox(LPGW lpgw, LPARAM lParam)
{
	if (lpgw == graphwin) {
		int mx, my;

		GetMousePosViewport(lpgw, &mx, &my);
		DrawZoomBox(lpgw); /*  erase current zoom box */
		zoombox.to.x = mx; zoombox.to.y = my;
		DrawZoomBox(lpgw); /*  draw new zoom box */
	}
}

static void
Wnd_refresh_ruler_lineto(LPGW lpgw, LPARAM lParam)
{
	if (lpgw == graphwin) {
		int mx, my;

		GetMousePosViewport(lpgw, &mx, &my);
		DrawRulerLineTo(lpgw); /*  erase current line */
		ruler_lineto.x = mx; ruler_lineto.y = my;
		DrawRulerLineTo(lpgw); /*  draw new line box */
	}
}
#endif /* USE_MOUSE */

/* ================================== */

/* The toplevel function of this module: Window handler function of the graph window */
LRESULT CALLBACK
WndGraphProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	PAINTSTRUCT ps;
	RECT rect;
	LPGW lpgw;
	HMENU sysmenu;
	int i;
#ifdef USE_MOUSE
	static unsigned int last_modifier_mask = -99;
#endif

	lpgw = (LPGW)GetWindowLong(hwnd, 0);

#ifdef USE_MOUSE
	/*  mouse events first */
	if ((lpgw == graphwin) && mouse_setting.on) {
		switch (message) {
			case WM_MOUSEMOVE:
#if 1
				SetCursor(hptrCurrent);
#else
				SetCursor(hptrCrossHair);
#endif
				if (zoombox.on) {
					Wnd_refresh_zoombox(lpgw, lParam);
				}
				if (ruler.on && ruler_lineto.on) {
					Wnd_refresh_ruler_lineto(lpgw, lParam);
				}
				/* track (show) mouse position -- send the event to gnuplot */
				Wnd_exec_event(lpgw, lParam,  GE_motion, wParam);
				return 0L; /* end of WM_MOUSEMOVE */

			case WM_LBUTTONDOWN: {
				int i;
				int x = GET_X_LPARAM(lParam);
				int y = GET_Y_LPARAM(lParam) - lpgw->ToolbarHeight;
				for (i = 0; (i < lpgw->numplots) && (i < lpgw->maxkeyboxes) && (i < lpgw->maxhideplots); i++) {
					if ((lpgw->keyboxes[i].left != INT_MAX) &&
						(x >= lpgw->keyboxes[i].left) &&
						(x <= lpgw->keyboxes[i].right) &&
						(y <= lpgw->keyboxes[i].top) &&
						(y >= lpgw->keyboxes[i].bottom)) {
						lpgw->hideplot[i] = ! lpgw->hideplot[i];
						if (i < MAXPLOTSHIDE)
							SendMessage(lpgw->hToolbar, TB_CHECKBUTTON, M_HIDEPLOT + i, (LPARAM)lpgw->hideplot[i]);
						GetClientRect(hwnd, &rect);
						InvalidateRect(hwnd, (LPRECT) &rect, 1);
						UpdateWindow(hwnd);
						return 0L;
					}
				}
				Wnd_exec_event(lpgw, lParam, GE_buttonpress, 1);
				return 0L;
			}
			case WM_RBUTTONDOWN:
				/* FIXME HBB 20010207: this collides with the right-click
				 * context menu !!! */
				Wnd_exec_event(lpgw, lParam, GE_buttonpress, 3);
				return 0L;

			case WM_MBUTTONDOWN:
				Wnd_exec_event(lpgw, lParam, GE_buttonpress, 2);
				return 0L;

#if _WIN32_WINNT >= 0x0400
			/* shige : mouse wheel support */
			case WM_MOUSEWHEEL: {
			    WORD fwKeys;
			    short int zDelta;
			    int modifier_mask;

			    fwKeys = LOWORD(wParam);
			    zDelta = HIWORD(wParam);
			    modifier_mask = ((fwKeys & MK_SHIFT)? Mod_Shift : 0)
				| ((fwKeys & MK_CONTROL)? Mod_Ctrl : 0)
				| ((fwKeys & MK_ALT)? Mod_Alt : 0);
			    if (last_modifier_mask != modifier_mask)
					Wnd_exec_event(lpgw, lParam, GE_modifier,
					       modifier_mask);
			    Wnd_exec_event(lpgw, lParam, GE_buttonpress,
					   zDelta > 0 ? 4 : 5);
			    last_modifier_mask = modifier_mask;
			    return 0L;
			}
#endif

			case WM_LBUTTONDBLCLK:
				Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 1);
				return 0L;

			case WM_RBUTTONDBLCLK:
				Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 3);
				return 0L;

			case WM_MBUTTONDBLCLK:
				Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 2);
				return 0L;

#if 1
			case WM_LBUTTONUP:
#else
			case WM_LBUTTONCLICK:
#endif
				Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 1);
				return 0L;

#if 1
			case WM_RBUTTONUP:
#else
			case WM_RBUTTONCLICK:
#endif
				Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 3);
				return 0L;

#if 1
			case WM_MBUTTONUP:
#else
			case WM_MBUTTONCLICK:
#endif
				Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 2);
				return 0L;

		} /* switch over mouse events */
	}
#endif /* USE_MOUSE */



	switch(message)
	{
		case WM_SYSCOMMAND:
			switch(LOWORD(wParam))
			{
				case M_GRAPH_TO_TOP:
				case M_COLOR:
				case M_DOUBLEBUFFER:
				case M_OVERSAMPLE:
				case M_ANTIALIASING:
				case M_POLYAA:
				case M_PATTERNAA:
				case M_CHOOSE_FONT:
				case M_COPY_CLIP:
				case M_SAVE_AS_EMF:
				case M_LINESTYLE:
				case M_BACKGROUND:
				case M_PRINT:
				case M_WRITEINI:
				case M_REBUILDTOOLS:
					SendMessage(hwnd, WM_COMMAND, wParam, lParam);
					break;
#ifndef WGP_CONSOLE
				case M_ABOUT:
					if (lpgw->lptw)
						AboutBox(hwnd,lpgw->lptw->AboutText);
					return 0;
#endif
				case M_COMMANDLINE:
					sysmenu = GetSystemMenu(lpgw->hWndGraph,0);
					i = GetMenuItemCount (sysmenu);
					DeleteMenu (sysmenu, --i, MF_BYPOSITION);
					DeleteMenu (sysmenu, --i, MF_BYPOSITION);
					if (lpgw->lptw)
						ShowWindow (lpgw->lptw->hWndParent, SW_SHOW);
					break;
			}
			break;
		case WM_CHAR:
			/* All 'normal' keys (letters, digits and the likes) end up
			 * here... */
#ifndef DISABLE_SPACE_RAISES_CONSOLE
			if (wParam == VK_SPACE) {
				WinRaiseConsole();
				return 0L;
			}
#endif /* DISABLE_SPACE_RAISES_CONSOLE */
#ifdef USE_MOUSE
			Wnd_exec_event(lpgw, lParam, GE_keypress, (TCHAR)wParam);
#endif
			return 0L;
#ifdef USE_MOUSE
		/* "special" keys have to be caught from WM_KEYDOWN, as they
		 * don't generate WM_CHAR messages. */
		/* NB: It may not be possible to catch Alt-keys, this way */
		case WM_KEYUP:
			{
				/* First, look for a change in modifier status */
				unsigned int modifier_mask = 0;
				modifier_mask = ((GetKeyState(VK_SHIFT) < 0) ? Mod_Shift : 0 )
					| ((GetKeyState(VK_CONTROL) < 0) ? Mod_Ctrl : 0)
					| ((GetKeyState(VK_MENU) < 0) ? Mod_Alt : 0);
				if (modifier_mask != last_modifier_mask) {
					Wnd_exec_event ( lpgw, lParam, GE_modifier, modifier_mask);
					last_modifier_mask = modifier_mask;
				}
			}
			/* Ignore Key-Up events other than those of modifier keys */
			break;
		case WM_KEYDOWN:
			if ((GetKeyState(VK_CONTROL) < 0)  && (wParam != VK_CONTROL)) {
				switch(wParam) {
				case 'C':
					/* Ctrl-C: Copy to Clipboard */
					SendMessage(hwnd,WM_COMMAND,M_COPY_CLIP,0L);
					break;
				case 'S':
					/* Ctrl-S: Save As EMF */
					SendMessage(hwnd,WM_COMMAND,M_SAVE_AS_EMF,0L);
					break;
				} /* switch(wparam) */
			} else {
				/* First, look for a change in modifier status */
				unsigned int modifier_mask = 0;
				modifier_mask = ((GetKeyState(VK_SHIFT) < 0) ? Mod_Shift : 0 )
					| ((GetKeyState(VK_CONTROL) < 0) ? Mod_Ctrl : 0)
					| ((GetKeyState(VK_MENU) < 0) ? Mod_Alt : 0);
				if (modifier_mask != last_modifier_mask) {
					Wnd_exec_event(lpgw, lParam, GE_modifier, modifier_mask);
					last_modifier_mask = modifier_mask;
				}
			}
			switch (wParam) {
			case VK_BACK:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_BackSpace);
				break;
			case VK_TAB:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Tab);
				break;
			case VK_RETURN:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Return);
				break;
			case VK_PAUSE:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Pause);
				break;
			case VK_SCROLL:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Scroll_Lock);
				break;
#if 0 /* HOW_IS_THIS_FOR_WINDOWS */
/* HBB 20010215: not at all, AFAICS... :-( */
			case VK_SYSRQ:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Sys_Req);
				break;
#endif
			case VK_ESCAPE:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Escape);
				break;
			case VK_DELETE:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Delete);
				break;
			case VK_INSERT:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_KP_Insert);
				break;
			case VK_HOME:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Home);
				break;
			case VK_LEFT:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Left);
				break;
			case VK_UP:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Up);
				break;
			case VK_RIGHT:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Right);
				break;
			case VK_DOWN:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Down);
				break;
			case VK_END:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_End);
				break;
			case VK_PRIOR:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_PageUp);
				break;
			case VK_NEXT:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_PageDown);
				break;
			case VK_F1:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F1);
				break;
			case VK_F2:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F2);
				break;
			case VK_F3:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F3);
				break;
			case VK_F4:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F4);
				break;
			case VK_F5:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F5);
				break;
			case VK_F6:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F6);
				break;
			case VK_F7:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F7);
				break;
			case VK_F8:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F8);
				break;
			case VK_F9:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F9);
				break;
			case VK_F10:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F10);
				break;
			case VK_F11:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F11);
				break;
			case VK_F12:
				Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F12);
				break;
			case VK_CANCEL:
				/* FIXME: Currently, this only supports interrupting fit. */
				ctrlc_flag = TRUE;
				break;
			} /* switch (wParam) */

			return 0L;
#if 0 /* DO WE NEED THIS ??? */
		case WM_MOUSEMOVE:
			/* set default pointer: */
			SetCursor(hptrDefault);
			return 0L;
#endif
#endif /* USE_MOUSE */
		case WM_COMMAND:
			switch(LOWORD(wParam))
			{
				case M_GRAPH_TO_TOP:
					lpgw->graphtotop = !lpgw->graphtotop;
					SendMessage(hwnd,WM_COMMAND,M_REBUILDTOOLS,0L);
					return(0);
				case M_COLOR:
					lpgw->color = !lpgw->color;
					lpgw->dashed = !lpgw->color;
					SendMessage(hwnd,WM_COMMAND,M_REBUILDTOOLS,0L);
					WIN_update_options();
					return(0);
				case M_OVERSAMPLE:
					lpgw->oversample = !lpgw->oversample;
					SendMessage(hwnd, WM_COMMAND, M_REBUILDTOOLS, 0L);
					return 0;
				case M_DOUBLEBUFFER:
					lpgw->doublebuffer = !lpgw->doublebuffer;
					SendMessage(hwnd, WM_COMMAND, M_REBUILDTOOLS, 0L);
					return 0;
				case M_ANTIALIASING:
					lpgw->antialiasing = !lpgw->antialiasing;
					SendMessage(hwnd, WM_COMMAND, M_REBUILDTOOLS, 0L);
					return 0;
				case M_POLYAA:
					lpgw->polyaa = !lpgw->polyaa;
					SendMessage(hwnd, WM_COMMAND, M_REBUILDTOOLS, 0L);
					return 0;
				case M_PATTERNAA:
					lpgw->patternaa = !lpgw->patternaa;
					SendMessage(hwnd, WM_COMMAND, M_REBUILDTOOLS, 0L);
					return 0;
				case M_CHOOSE_FONT:
					SelFont(lpgw);
					WIN_update_options();
					return 0;
				case M_COPY_CLIP:
					CopyClip(lpgw);
					return 0;
				case M_SAVE_AS_EMF:
					SaveAsEMF(lpgw);
					return 0;
				case M_LINESTYLE:
					if (LineStyle(lpgw))
						SendMessage(hwnd,WM_COMMAND,M_REBUILDTOOLS,0L);
					return 0;
				case M_BACKGROUND:
					lpgw->background = GetColor(hwnd, lpgw->background);
					SendMessage(hwnd,WM_COMMAND,M_REBUILDTOOLS,0L);
					WIN_update_options();
					return 0;
				case M_PRINT:
					CopyPrint(lpgw);
					return 0;
				case M_HIDEGRID:
					lpgw->hidegrid = SendMessage(lpgw->hToolbar, TB_ISBUTTONCHECKED, LOWORD(wParam), (LPARAM)0);
					GetClientRect(hwnd, &rect);
					InvalidateRect(hwnd, (LPRECT) &rect, 1);
					UpdateWindow(hwnd);
					return 0;
				case M_WRITEINI:
					WriteGraphIni(lpgw);
#ifndef WGP_CONSOLE
					if (lpgw->lptw)
						WriteTextIni(lpgw->lptw);
#endif
					WIN_update_options();
					return 0;
				case M_REBUILDTOOLS:
					if (lpgw->color)
						CheckMenuItem(lpgw->hPopMenu, M_COLOR, MF_BYCOMMAND | MF_CHECKED);
					else
						CheckMenuItem(lpgw->hPopMenu, M_COLOR, MF_BYCOMMAND | MF_UNCHECKED);
					if (lpgw->doublebuffer)
						CheckMenuItem(lpgw->hPopMenu, M_DOUBLEBUFFER, MF_BYCOMMAND | MF_CHECKED);
					else
						CheckMenuItem(lpgw->hPopMenu, M_DOUBLEBUFFER, MF_BYCOMMAND | MF_UNCHECKED);
					if (lpgw->oversample)
						CheckMenuItem(lpgw->hPopMenu, M_OVERSAMPLE, MF_BYCOMMAND | MF_CHECKED);
					else
						CheckMenuItem(lpgw->hPopMenu, M_OVERSAMPLE, MF_BYCOMMAND | MF_UNCHECKED);
#ifdef HAVE_GDIPLUS
					if (lpgw->antialiasing)
						CheckMenuItem(lpgw->hPopMenu, M_ANTIALIASING, MF_BYCOMMAND | MF_CHECKED);
					else
						CheckMenuItem(lpgw->hPopMenu, M_ANTIALIASING, MF_BYCOMMAND | MF_UNCHECKED);
					if (lpgw->polyaa)
						CheckMenuItem(lpgw->hPopMenu, M_POLYAA, MF_BYCOMMAND | MF_CHECKED);
					else
						CheckMenuItem(lpgw->hPopMenu, M_POLYAA, MF_BYCOMMAND | MF_UNCHECKED);
					if (lpgw->patternaa)
						CheckMenuItem(lpgw->hPopMenu, M_PATTERNAA, MF_BYCOMMAND | MF_CHECKED);
					else
						CheckMenuItem(lpgw->hPopMenu, M_PATTERNAA, MF_BYCOMMAND | MF_UNCHECKED);
#endif
					if (lpgw->graphtotop)
						CheckMenuItem(lpgw->hPopMenu, M_GRAPH_TO_TOP, MF_BYCOMMAND | MF_CHECKED);
					else
						CheckMenuItem(lpgw->hPopMenu, M_GRAPH_TO_TOP, MF_BYCOMMAND | MF_UNCHECKED);
					DestroyPens(lpgw);
					DestroyFonts(lpgw);
					hdc = GetDC(hwnd);
					MakePens(lpgw, hdc);
					GetPlotRect(lpgw, &rect);
					MakeFonts(lpgw, (LPRECT)&rect, hdc);
					ReleaseDC(hwnd, hdc);
					GetClientRect(hwnd, &rect);
					InvalidateRect(hwnd, (LPRECT) &rect, 1);
					UpdateWindow(hwnd);
					return 0;
			}
			/* handle toolbar events  */
			if ((LOWORD(wParam) >= M_HIDEPLOT) && (LOWORD(wParam) < (M_HIDEPLOT + MAXPLOTSHIDE))) {
				unsigned button = LOWORD(wParam) - (M_HIDEPLOT);
				if (button < lpgw->maxhideplots)
					lpgw->hideplot[button] = SendMessage(lpgw->hToolbar, TB_ISBUTTONCHECKED, LOWORD(wParam), (LPARAM)0);
				GetClientRect(hwnd, &rect);
				InvalidateRect(hwnd, (LPRECT) &rect, 1);
				UpdateWindow(hwnd);
				return 0;
			}
			return 0;
		case WM_PARENTNOTIFY:
			/* Message from status bar (or another child window): */
#ifdef USE_MOUSE
			/* Cycle through mouse-mode on button 1 click */
			if (LOWORD(wParam) == WM_LBUTTONDOWN) {
				int y = HIWORD(lParam);
				RECT rect;
				GetClientRect(hwnd, &rect);
				if (y > rect.bottom - lpgw->StatusHeight)
					/* simulate keyboard event '1' */
					Wnd_exec_event(lpgw, lParam, GE_keypress, (TCHAR)'1');
				return 0;
			}
#endif
			/* Context menu is handled below, everything else is not */
			if (LOWORD(wParam) != WM_CONTEXTMENU)
				return 1;
			/* intentionally fall through */
		case WM_CONTEXTMENU:
		{	/* Note that this only works via mouse in `unset mouse`
			 * mode. You can access the popup via the System menu,
			 * status bar or keyboard (Shift-F10, Menu-Key) instead. */
			POINT pt;
			pt.x = GET_X_LPARAM(lParam);
			pt.y = GET_Y_LPARAM(lParam);
			if (pt.x == -1) { /* keyboard activation */
				pt.x = pt.y = 0;
				ClientToScreen(hwnd, &pt);
			}
			TrackPopupMenu(lpgw->hPopMenu, TPM_LEFTALIGN,
				pt.x, pt.y, 0, hwnd, NULL);
			return 0;
		}
		case WM_NOTIFY:
			switch (((LPNMHDR)lParam)->code) {
				case TBN_DROPDOWN: {
					RECT rc;
					TPMPARAMS tpm;
					LPNMTOOLBAR lpnmTB = (LPNMTOOLBAR)lParam;
					SendMessage(lpnmTB->hdr.hwndFrom, TB_GETRECT, (WPARAM)lpnmTB->iItem, (LPARAM)&rc);
					MapWindowPoints(lpnmTB->hdr.hwndFrom, HWND_DESKTOP, (LPPOINT)&rc, 2);
					tpm.cbSize    = sizeof(TPMPARAMS);
					tpm.rcExclude = rc;
					TrackPopupMenuEx(lpgw->hPopMenu, TPM_LEFTALIGN | TPM_LEFTBUTTON | TPM_VERTICAL,
						rc.left, rc.bottom, lpgw->hWndGraph, &tpm);
					return TBDDRET_DEFAULT;
				}
				case TTN_GETDISPINFO: {
					LPTOOLTIPTEXT lpttt = (LPTOOLTIPTEXT)lParam;
					UINT_PTR idButton = lpttt->hdr.idFrom;
					lpttt->hinst = 0;
					switch (idButton) {
						case M_COPY_CLIP:
							strcpy(lpttt->szText, "Copy graph to clipboard");
							break;
						case M_PRINT:
							strcpy(lpttt->szText, "Print graph");
							break;
						case M_SAVE_AS_EMF:
							strcpy(lpttt->szText, "Save graph as EMF");
							break;
						case M_HIDEGRID:
							strcpy(lpttt->szText, "Do not draw grid lines");
							break;
					}
					if ((idButton >= M_HIDEPLOT) && (idButton < (M_HIDEPLOT + MAXPLOTSHIDE))) {
						unsigned index = (unsigned)idButton - (M_HIDEPLOT) + 1;
						sprintf(lpttt->szText, "Hide graph #%i", index);
					}
					lpttt->uFlags |= TTF_DI_SETITEM;
					return TRUE;
				}
			}
			return FALSE;
		case WM_CREATE:
			lpgw = ((CREATESTRUCT *)lParam)->lpCreateParams;
			SetWindowLong(hwnd, 0, (LONG)lpgw);
			lpgw->hWndGraph = hwnd;
			hdc = GetDC(hwnd);
			MakePens(lpgw, hdc);
#ifdef USE_MOUSE
			LoadCursors(lpgw);
#endif
			GetPlotRect(lpgw, &rect);
			MakeFonts(lpgw, (LPRECT)&rect, hdc);
			ReleaseDC(hwnd, hdc);
			if ( lpgw->lptw && (lpgw->lptw->DragPre!=(LPSTR)NULL) && (lpgw->lptw->DragPost!=(LPSTR)NULL) )
			    DragAcceptFiles(hwnd, TRUE);
			return(0);
		case WM_ERASEBKGND:
			return(1); /* we erase the background ourselves */
		case WM_PAINT: {
			HDC memdc;
			HBITMAP membmp, oldbmp;
			LONG width, height;
			int sampling;
			RECT memrect;

			hdc = BeginPaint(hwnd, &ps);
			SetMapMode(hdc, MM_TEXT);
			SetBkMode(hdc, OPAQUE);
			GetPlotRect(lpgw, &rect);
			SetViewportExtEx(hdc, rect.right, rect.bottom, NULL);
			/* double buffering */
			if (lpgw->doublebuffer) {
			    /* choose oversampling */
			    if (lpgw->oversample)
			        sampling = 2; /* anything bigger than that makes the computer crawl ... */
			    else
			        sampling = 1;

			    /* create memory device context for double buffering */
			    width = rect.right - rect.left;
			    height = rect.bottom - rect.top;
			    memdc = CreateCompatibleDC(hdc);
			    memrect.left = 0;
			    memrect.right = width * sampling + sampling/2;
			    memrect.top = 0;
			    memrect.bottom = height * sampling + sampling/2;
			    membmp = CreateCompatibleBitmap(hdc, memrect.right, memrect.bottom);
			    oldbmp = (HBITMAP)SelectObject(memdc, membmp);

			    /* TODO: we really should cache the results ... */
			    if (sampling > 1) {
			        lpgw->sampling = sampling;
			        DestroyFonts(lpgw);
			        MakeFonts(lpgw, &memrect, memdc);
			    }

			    /* draw into memdc, then copy to hdc */
			    drawgraph(lpgw, memdc, &memrect);
			    if (sampling == 1)
			        BitBlt(hdc, rect.left, rect.top, width, height, memdc, 0, 0, SRCCOPY);
			    else {
			        int stretch = SetStretchBltMode(hdc, HALFTONE);
			        StretchBlt(hdc, rect.left, rect.top, width, height,
			                   memdc,0, 0, memrect.right, memrect.bottom,
			                   SRCCOPY);
			        SetStretchBltMode(hdc, stretch);
			    }
			    lpgw->sampling = 1;

			    /* select the old bitmap back into the device context */
			    SelectObject(memdc, oldbmp);
			    DeleteObject(membmp);
			    DeleteDC(memdc);
			} else {
			    drawgraph(lpgw, hdc, &rect);
			}
			EndPaint(hwnd, &ps);
#ifdef USE_MOUSE
			/* drawgraph() erases the plot window, so immediately after
			 * it has completed, all the 'real-time' stuff the gnuplot core
			 * doesn't know anything about has to be redrawn */
			DrawRuler(lpgw);
			DrawRulerLineTo(lpgw);
			DrawZoomBox(lpgw);
#endif
			/* Update in case the number of graphs has changed */
			UpdateToolbar(lpgw);

			return 0;
		}
		case WM_SIZE:
			SendMessage(lpgw->hStatusbar, WM_SIZE, wParam, lParam);
			if (lpgw->hToolbar) {
				RECT rect;
				SendMessage(lpgw->hToolbar, WM_SIZE, wParam, lParam);
				/* make room */
				GetClientRect(lpgw->hToolbar, &rect);
				lpgw->ToolbarHeight = rect.bottom - rect.top + 1;
			}
			if ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED)) {
				RECT rect;
				unsigned width, height;

				GetWindowRect(hwnd, &rect);
				width = rect.right - rect.left;
				height = rect.bottom - rect.top;
				/* Ignore minimize / de-minize */
				if ((lpgw->Size.x != width) || (lpgw->Size.y != height)) {
					lpgw->Size.x = width;
					lpgw->Size.y = height;

					/* remake fonts */
					DestroyFonts(lpgw);
					GetPlotRect(lpgw, &rect);
					hdc = GetDC(hwnd);
					MakeFonts(lpgw, &rect, hdc);
					ReleaseDC(hwnd, hdc);

					GetClientRect(hwnd, &rect);
					InvalidateRect(hwnd, &rect, 1);
					UpdateWindow(hwnd);
				}
			}
			break;
#ifndef WGP_CONSOLE
		case WM_DROPFILES:
			if (lpgw->lptw)
				DragFunc(lpgw->lptw, (HDROP)wParam);
			break;
#endif
		case WM_DESTROY:
			DestroyPens(lpgw);
			DestroyFonts(lpgw);
#ifdef USE_MOUSE
			DestroyCursors(lpgw);
#endif
			DragAcceptFiles(hwnd, FALSE);
			lpgw->hWndGraph = NULL;
#ifndef WGP_CONSOLE
			WinPersistTextClose();
#endif
			return 0;
		case WM_CLOSE:
			GraphClose(lpgw);
			return 0;
		}
	return DefWindowProc(hwnd, message, wParam, lParam);
}


void
GraphChangeFont(LPGW lpgw, LPCSTR font, int fontsize, HDC hdc, RECT rect)
{
    int newfontsize;
    bool remakefonts = FALSE;
    bool font_is_not_empty = (font != NULL && *font != '\0');

    newfontsize = (fontsize != 0) ? fontsize : lpgw->deffontsize;
    if (font_is_not_empty) {
		remakefonts = (strcmp(lpgw->fontname, font) != 0) || (newfontsize != lpgw->fontsize);
    } else {
		remakefonts = (strcmp(lpgw->fontname, lpgw->deffontname) != 0) || (newfontsize != lpgw->fontsize);
    }

    if (remakefonts) {
        lpgw->fontsize = newfontsize;
        strcpy(lpgw->fontname, font_is_not_empty ? font : lpgw->deffontname);

        DestroyFonts(lpgw);
        MakeFonts(lpgw, &rect, hdc);
    }
}


/* close the terminal window */
void WDPROC
win_close_terminal_window(LPGW lpgw)
{
	if (GraphHasWindow(lpgw))
		SendMessage( lpgw->hWndGraph, WM_CLOSE, 0L, 0L);
}


#ifdef USE_MOUSE
/* Implemented by Petr Mikulik, February 2001 --- the best Windows solutions
 * come from OS/2 :-))
 */

/* ================================================================= */

/* Firstly: terminal calls from win.trm */

/* Note that these all take lpgw as their first argument. It's an OO-type
 * 'this' pointer, sort of: it stores all the status information of the graph
 * window that we need, in a single large structure. */

void WDPROC
Graph_set_cursor(LPGW lpgw, int c, int x, int y)
{
	switch (c) {
	case -4: /* switch off line between ruler and mouse cursor */
		DrawRulerLineTo(lpgw);
		ruler_lineto.on = FALSE;
		break;
	case -3: /* switch on line between ruler and mouse cursor */
		if (ruler.on && ruler_lineto.on)
		    break;
		ruler_lineto.x = x;
		ruler_lineto.y = y;
		ruler_lineto.on = TRUE;
		DrawRulerLineTo(lpgw);
		break;
	case -2:
		{ /* move mouse to the given point */
			RECT rc;
			POINT pt;

			GetPlotRect(lpgw, &rc);
			pt.x = MulDiv(x, rc.right - rc.left, lpgw->xmax);
			pt.y = rc.bottom - MulDiv(y, rc.bottom - rc.top, lpgw->ymax);

			MapWindowPoints(lpgw->hWndGraph, HWND_DESKTOP, &pt, 1);
			SetCursorPos(pt.x, pt.y);
		}
		break;
	case -1: /* start zooming; zooming cursor */
		zoombox.on = TRUE;
		zoombox.from.x = zoombox.to.x = x;
		zoombox.from.y = zoombox.to.y = y;
		break;
	case 0:  /* standard cross-hair cursor */
		SetCursor((hptrCurrent = mouse_setting.on ? hptrCrossHair : hptrDefault));
		break;
	case 1:  /* cursor during rotation */
		SetCursor(hptrCurrent = hptrRotating);
		break;
	case 2:  /* cursor during scaling */
		SetCursor(hptrCurrent = hptrScaling);
		break;
	case 3:  /* cursor during zooming */
		SetCursor(hptrCurrent = hptrZooming);
		break;
	}
	if (c>=0 && zoombox.on) { /* erase zoom box */
		DrawZoomBox(lpgw);
		zoombox.on = FALSE;
	}
	if (c>=0 && ruler_lineto.on) { /* erase ruler line */
		DrawRulerLineTo(lpgw);
		ruler_lineto.on = FALSE;
	}
}

/* set_ruler(int x, int y) term API: x<0 switches ruler off. */
void WDPROC
Graph_set_ruler (LPGW lpgw, int x, int y )
{
	DrawRuler(lpgw); /* remove previous drawing, if any */
	DrawRulerLineTo(lpgw);
	if (x < 0) {
		ruler.on = FALSE;
		return;
	}
	ruler.on = TRUE;
	ruler.x = x; ruler.y = y;
	DrawRuler(lpgw); /* draw ruler at new positions */
	DrawRulerLineTo(lpgw);
}

/* put_tmptext(int i, char c[]) term API
 * 	i: 0..at statusline
 *	1, 2: at corners of zoom box with \r separating text
 */
void WDPROC
Graph_put_tmptext (LPGW lpgw, int where, LPCSTR text )
{
    /* Position of the annotation string (mouse movement) or zoom box
     * text or whatever temporary text added...
     */
    switch (where) {
	case 0:
		UpdateStatusLine(lpgw, text);
		break;
	case 1:
		DrawZoomBox(lpgw);
		if (zoombox.text1) {
			free((char*)zoombox.text1);
		}
		zoombox.text1 = strdup(text);
		DrawZoomBox(lpgw);
		break;
	case 2:
		DrawZoomBox(lpgw);
		if (zoombox.text2) {
			free((char*)zoombox.text2);
		}
		zoombox.text2 = strdup(text);
		DrawZoomBox(lpgw);
		break;
	default:
		; /* should NEVER happen */
	}
}

void WDPROC
Graph_set_clipboard (LPGW lpgw, LPCSTR s)
{
	size_t length;
	HGLOBAL memHandle;
	LPSTR clipText;

	/* check: no string --> nothing to do */
	if (!s || !s[0])
		return;

	/* Transport the string into a system-global storage area. In case
	 * of (unlikely) allocation failures, fail silently */
	length = strlen(s);
	if ( (memHandle = GlobalAlloc(GHND, length + 1)) == NULL)
		return;
	if ( (clipText = GlobalLock(memHandle)) == NULL) {
		GlobalFree(memHandle);
		return;
	}
	strcpy(clipText, s);
	GlobalUnlock(memHandle);

	/* Now post that memory area to the Clipboard */
	OpenClipboard(lpgw->hWndGraph);
	EmptyClipboard();
	SetClipboardData(CF_TEXT, memHandle);
	CloseClipboard();
}

/* ================================================================= */

/* Secondly, support routines that implement direct mouse reactions. */

/* This routine gets the mouse/pointer position in the current window and
 * recalculates it to the viewport coordinates. */
static void
GetMousePosViewport(LPGW lpgw, int *mx, int *my)
{
	POINT pt;
	RECT rc;

	GetPlotRect(lpgw, &rc);

	/* HBB: has to be done this way. The simpler method by taking apart LPARM
	 * only works for mouse, but not for keypress events. */
	GetCursorPos(&pt);
	ScreenToClient(lpgw->hWndGraph, &pt);

	/* px=px(mx); mouse=>gnuplot driver coordinates */
	/* FIXME: classically, this would use MulDiv() call, and no floating point */
	/* BM: protect against zero window size - Vista does that when minimizing windows */
	*mx = *my = 0;
	if ((rc.right - rc.left) != 0)
		*mx = (int)((pt.x - rc.left) * lpgw->xmax / (rc.right - rc.left) + 0.5);
	if ((rc.bottom -rc.top) != 0)
		*my = (int)((rc.bottom - pt.y) * lpgw->ymax / (rc.bottom -rc.top) + 0.5);
}

/* HBB 20010218: Newly separated function: Draw text string in XOR mode.
 * That is surprisingly difficult using the Windows API: have to draw text
 * into a background bitmap, first, and then blit that onto the screen.
 *
 * x and y give the bottom-left corner of the bounding box into which the
 * text will be drawn */
static void
Draw_XOR_Text(LPGW lpgw, const char *text, size_t length, int x, int y)
{
	HDC hdc, tempDC;
	HBITMAP bitmap;
	int cx, cy;

	if (!text || !text[0])
	       return; /* no text to be displayed */

	hdc = GetDC(lpgw->hWndGraph);

	/* Prepare background image buffer of the necessary size */
	Wnd_GetTextSize(hdc, text, length, &cx, &cy);
	bitmap = CreateCompatibleBitmap(hdc, cx, cy);
	/* ... and a DeviceContext to access it by */
	tempDC = CreateCompatibleDC(hdc);
	DeleteObject(SelectObject(tempDC, bitmap));

	TextOut(tempDC, 0, 0, text, length);

	/* Copy printed string to the screen window using
	   "target = target XOR (NOT source)" ROP, see "Ternary Raster Operations"
	   http://msdn.microsoft.com/en-us/library/dd145130%28VS.85%29.aspx
	*/
	BitBlt(hdc, x, y - cy, cx, cy, tempDC, 0, 0, 0x00990066);

	/* Clean up behind ourselves */
	DeleteDC(tempDC);
	DeleteObject(bitmap);
	ReleaseDC(lpgw->hWndGraph, hdc);
}


/* ================================== */

/*
 * Update the status line by the text; erase the previous text
 */
static void
UpdateStatusLine(LPGW lpgw, const char text[])
{
    SendMessage(lpgw->hStatusbar, WM_SETTEXT, (WPARAM)0, (LPARAM)text);
}


/*
 * Update the toolbar to reflect the current the number of active plots
 */
static void
UpdateToolbar(LPGW lpgw)
{
	unsigned i;

	SendMessage(lpgw->hToolbar, TB_HIDEBUTTON, M_HIDEGRID, (LPARAM)!lpgw->hasgrid);
	if (!lpgw->hasgrid) {
		lpgw->hidegrid = FALSE;
		SendMessage(lpgw->hToolbar, TB_CHECKBUTTON, M_HIDEGRID, (LPARAM)FALSE);
	}
	for (i = 0; i < GPMAX(MAXPLOTSHIDE, lpgw->maxhideplots); i++) {
		if (i < lpgw->numplots) {
			if (i < MAXPLOTSHIDE)
				SendMessage(lpgw->hToolbar, TB_HIDEBUTTON, M_HIDEPLOT + i, (LPARAM)FALSE);
		} else {
			if (i < lpgw->maxhideplots)
				lpgw->hideplot[i] = FALSE;
			if (i < MAXPLOTSHIDE) {
				SendMessage(lpgw->hToolbar, TB_HIDEBUTTON, M_HIDEPLOT + i, (LPARAM)TRUE);
				SendMessage(lpgw->hToolbar, TB_CHECKBUTTON, M_HIDEPLOT + i, (LPARAM)FALSE);
			}
		}
	}
}


/* Draw the ruler.
 */
static void
DrawRuler (LPGW lpgw)
{
	HDC hdc;
	int iOldRop;
	RECT rc;
	long rx, ry;

	if (!ruler.on || ruler.x < 0)
		return;

	hdc = GetDC(lpgw->hWndGraph);
	GetPlotRect(lpgw, &rc);

	rx = MulDiv(ruler.x, rc.right - rc.left, lpgw->xmax);
	ry = rc.bottom - MulDiv(ruler.y, rc.bottom - rc.top, lpgw->ymax);

	iOldRop = SetROP2(hdc, R2_NOT);
	MoveTo(hdc, rc.left, ry);
	LineTo(hdc, rc.right, ry);
	MoveTo(hdc, rx, rc.top);
	LineTo(hdc, rx, rc.bottom);
	SetROP2(hdc, iOldRop);
	ReleaseDC(lpgw->hWndGraph, hdc);
}

/* Draw the ruler line to cursor position.
 */
static void
DrawRulerLineTo (LPGW lpgw)
{
	HDC hdc;
	int iOldRop;
	RECT rc;
	long rx, ry, rlx, rly;

	if (!ruler.on || !ruler_lineto.on || ruler.x < 0 || ruler_lineto.x < 0)
		return;

	hdc = GetDC(lpgw->hWndGraph);
	GetPlotRect(lpgw, &rc);

	rx  = MulDiv(ruler.x, rc.right - rc.left, lpgw->xmax);
	ry  = rc.bottom - MulDiv(ruler.y, rc.bottom - rc.top, lpgw->ymax);
	rlx = MulDiv(ruler_lineto.x, rc.right - rc.left, lpgw->xmax);
	rly = rc.bottom - MulDiv(ruler_lineto.y, rc.bottom - rc.top, lpgw->ymax);

	iOldRop = SetROP2(hdc, R2_NOT);
	MoveTo(hdc, rx, ry);
	LineTo(hdc, rlx, rly);
	SetROP2(hdc, iOldRop);
	ReleaseDC(lpgw->hWndGraph, hdc);
}

/* Draw the zoom box.
 */
static void
DrawZoomBox (LPGW lpgw)
{
	HDC hdc;
	long fx, fy, tx, ty, text_y;
	int OldROP2;
	RECT rc;
	HPEN OldPen;

	if (!zoombox.on)
		return;

	hdc = GetDC(lpgw->hWndGraph);
	GetPlotRect(lpgw, &rc);

	fx = MulDiv(zoombox.from.x, rc.right - rc.left, lpgw->xmax);
	fy = rc.bottom - MulDiv(zoombox.from.y, rc.bottom - rc.top, lpgw->ymax);
	tx = MulDiv(zoombox.to.x, rc.right - rc.left, lpgw->xmax);
	ty = rc.bottom - MulDiv(zoombox.to.y, rc.bottom - rc.top, lpgw->ymax);
	text_y = MulDiv(lpgw->vchar, rc.bottom - rc.top, lpgw->ymax);

	OldROP2 = SetROP2(hdc, R2_NOTXORPEN);
	OldPen = SelectObject(hdc, CreatePenIndirect(
			(lpgw->color ? lpgw->colorpen : lpgw->monopen) + 1));
	Rectangle(hdc, fx, fy, tx, ty);
	DeleteObject(SelectObject(hdc, OldPen));
	SetROP2(hdc, OldROP2);

	ReleaseDC(lpgw->hWndGraph, hdc);

	if (zoombox.text1) {
		char *separator = strchr(zoombox.text1, '\r');

		if (separator) {
			Draw_XOR_Text(lpgw, zoombox.text1, separator - zoombox.text1, fx, fy);
			Draw_XOR_Text(lpgw, separator + 1, strlen(separator + 1), fx, fy + text_y);
		} else {
			Draw_XOR_Text(lpgw, zoombox.text1, strlen(zoombox.text1), fx, fy + lpgw->vchar / 2);
		}
	}
	if (zoombox.text2) {
		char *separator = strchr(zoombox.text2, '\r');

		if (separator) {
			Draw_XOR_Text(lpgw, zoombox.text2, separator - zoombox.text2, tx, ty);
			Draw_XOR_Text(lpgw, separator + 1, strlen(separator + 1), tx, ty + text_y);
		} else  {
			Draw_XOR_Text(lpgw, zoombox.text2, strlen(zoombox.text2), tx, ty + lpgw->vchar / 2);
		}
	}
}

#endif /* USE_MOUSE */

/* eof wgraph.c */

