/*
* $Id: wgraph.c,v 1.255.2.9 2017/08/29 11:45:16 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
*/
#include "syscfg.h"
#define STRICT
#include <windows.h>
#include <windowsx.h>
#include <commdlg.h>
#include <commctrl.h>
#include <stdio.h>
#include <string.h>
#include <direct.h> /* for _chdir */
#include <tchar.h>
#include <wchar.h>
#include "winmain.h"
#include "wresourc.h"
#include "wcommon.h"
#include "wgnuplib.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
#ifdef HAVE_D2D
# include "wd2d.h"
#endif
#include "plot.h"
#ifndef WM_MOUSEHWHEEL /* requires _WIN32_WINNT >= 0x0600 */
# define WM_MOUSEHWHEEL 0x020E
#endif
#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);
#endif
static void UpdateStatusLine(LPGW lpgw, const char text[]);
static void UpdateToolbar(LPGW lpgw);
#ifdef USE_MOUSE
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 */
static void DrawFocusIndicator(LPGW lpgw);
/* ================================== */
#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 */
#define PATTERN_BITMAP_LENGTH 16
static const unsigned char pattern_bitmaps[][PATTERN_BITMAP_LENGTH] = {
{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.
*/
enhstate_struct enhstate;
static struct {
HDC hdc; /* device context */
} enhstate_gdi;
/* ================================== */
/* prototypes for module-local functions */
LRESULT CALLBACK WndGraphProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
INT_PTR 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 void GraphChangeFont(LPGW lpgw, LPCTSTR font, int fontsize, HDC hdc, RECT rect);
static void dot(HDC hdc, int xdash, int ydash);
static unsigned int GraphGetTextLength(LPGW lpgw, HDC hdc, LPCSTR text);
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 draw_image(LPGW lpgw, HDC hdc, char *image, POINT corners[4], unsigned int width, unsigned int height, int color_mode);
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 void ReadGraphIni(LPGW lpgw);
static void track_tooltip(LPGW lpgw, int x, int y);
static COLORREF GetColor(HWND hwnd, COLORREF ref);
#ifdef WIN_CUSTOM_PENS
static void UpdateColorSample(HWND hdlg);
static BOOL LineStyle(LPGW lpgw);
#endif
static void GraphUpdateMenu(LPGW lpgw);
/* ================================== */
/* 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
GraphOp(LPGW lpgw, UINT op, UINT x, UINT y, LPCSTR str)
{
if (str)
GraphOpSize(lpgw, op, x, y, str, strlen(str) + 1);
else
GraphOpSize(lpgw, op, x, y, NULL, 0);
}
void
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++;
lpgw->buffervalid = FALSE;
}
/* ================================== */
/* Initialize the LPGW struct:
* set default values and read options from ini file */
void
GraphInitStruct(LPGW lpgw)
{
if (!lpgw->initialized) {
#ifndef WIN_CUSTOM_PENS
int i;
#endif
lpgw->initialized = TRUE;
if (lpgw != listgraphs) {
TCHAR 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 */
wsprintf(titlestr, TEXT("%s %i"), WINGRAPHTITLE, lpgw->Id);
lpgw->Title = _tcsdup(titlestr);
} else {
lpgw->Title = _tcsdup(WINGRAPHTITLE);
}
lpgw->fontscale = 1.;
lpgw->linewidth = 1.;
lpgw->pointscale = 1.;
lpgw->color = TRUE;
lpgw->dashed = FALSE;
lpgw->IniSection = TEXT("WGNUPLOT");
lpgw->fontsize = WINFONTSIZE;
lpgw->maxkeyboxes = 0;
lpgw->keyboxes = 0;
lpgw->buffervalid = FALSE;
lpgw->maxhideplots = MAXPLOTSHIDE;
lpgw->hideplot = (BOOL *) calloc(MAXPLOTSHIDE, sizeof(BOOL));
/* init pens */
#ifndef WIN_CUSTOM_PENS
StorePen(lpgw, 0, RGB(0, 0, 0), PS_SOLID, PS_SOLID);
StorePen(lpgw, 1, RGB(192, 192, 192), PS_DOT, PS_DOT);
for (i = 0; i < WGNUMPENS; i++) {
COLORREF ref = wginitcolor[i % WGDEFCOLOR];
int colorstyle = wginitstyle[(i / WGDEFCOLOR) % WGDEFSTYLE];
int monostyle = wginitstyle[i % WGDEFSTYLE];
StorePen(lpgw, i + 2, ref, colorstyle, monostyle);
}
#endif
ReadGraphIni(lpgw);
}
}
/* Prepare Graph window for being displayed by windows, update
* the window's menus and show it */
void
GraphInit(LPGW lpgw)
{
HMENU sysmenu;
WNDCLASS wndclass;
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 = NULL;
wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wndclass.lpszMenuName = NULL;
wndclass.lpszClassName = szGraphClass;
RegisterClass(&wndclass);
}
if (!lpgw->bDocked) {
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);
}
#ifndef WGP_CONSOLE
else {
RECT rect;
SIZE size;
/* Note: whatever we set here as initial window size will be overridden
by DockedUpdateLayout() below.
*/
GetClientRect(textwin.hWndParent, &rect);
DockedGraphSize(lpgw->lptw, &size, TRUE);
lpgw->Origin.x = rect.right - 200;
lpgw->Origin.y = textwin.ButtonHeight;
lpgw->Size.x = size.cx;
lpgw->Size.y = size.cy;
lpgw->hWndGraph = CreateWindow(szGraphClass, lpgw->Title,
WS_CHILD,
lpgw->Origin.x, lpgw->Origin.y,
lpgw->Size.x, lpgw->Size.y,
textwin.hWndParent, NULL, lpgw->hInstance, lpgw);
}
#endif
if (lpgw->hWndGraph)
SetClassLongPtr(lpgw->hWndGraph, GCLP_HICON,
(LONG_PTR) LoadIcon(lpgw->hInstance, TEXT("GRPICON")));
if (!lpgw->bDocked)
lpgw->hStatusbar = CreateWindowEx(0, STATUSCLASSNAME, NULL,
WS_CHILD | (lpgw->bDocked ? 0 : 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, SW_SHOWNOACTIVATE);
/* make room */
GetWindowRect(lpgw->hStatusbar, &rect);
lpgw->StatusHeight = rect.bottom - rect.top;
} else {
lpgw->StatusHeight = 0;
}
/* 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;
TCHAR buttontext[10];
unsigned num = 0;
UINT dpi = GetDPI();
TBADDBITMAP bitmap = {0};
if (dpi > 96)
SendMessage(lpgw->hToolbar, TB_SETBITMAPSIZE, (WPARAM)0, MAKELPARAM(24, 24));
else
SendMessage(lpgw->hToolbar, TB_SETBITMAPSIZE, (WPARAM)0, MAKELPARAM(16, 16));
/* load standard toolbar icons: standard, history & view */
SendMessage(lpgw->hToolbar, TB_BUTTONSTRUCTSIZE, (WPARAM)sizeof(TBBUTTON), 0);
bitmap.hInst = HINST_COMMCTRL;
bitmap.nID = (dpi > 96) ? IDB_STD_LARGE_COLOR : IDB_STD_SMALL_COLOR;
SendMessage(lpgw->hToolbar, TB_ADDBITMAP, 0, (WPARAM)&bitmap);
bitmap.nID = (dpi > 96) ? IDB_HIST_LARGE_COLOR : IDB_HIST_SMALL_COLOR;
SendMessage(lpgw->hToolbar, TB_ADDBITMAP, 0, (WPARAM)&bitmap);
bitmap.nID = (dpi > 96) ? IDB_VIEW_LARGE_COLOR : IDB_VIEW_SMALL_COLOR;
SendMessage(lpgw->hToolbar, TB_ADDBITMAP, 0, (WPARAM)&bitmap);
/* create buttons */
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) TEXT("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) TEXT("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;
wsprintf(buttontext, TEXT("%i"), i + 1);
button.iString = (UINT_PTR) buttontext;
button.dwData = i;
ret = SendMessage(lpgw->hToolbar, TB_INSERTBUTTON, (WPARAM)num++, (LPARAM)&button);
}
/* silence compiler warning */
(void) ret;
/* auto-resize and show */
SendMessage(lpgw->hToolbar, TB_AUTOSIZE, (WPARAM)0, (LPARAM)0);
ShowWindow(lpgw->hToolbar, SW_SHOWNOACTIVATE);
/* make room */
GetWindowRect(lpgw->hToolbar, &rect);
lpgw->ToolbarHeight = rect.bottom - rect.top;
}
lpgw->hPopMenu = CreatePopupMenu();
/* actions */
AppendMenu(lpgw->hPopMenu, MF_STRING, M_COPY_CLIP, TEXT("&Copy to Clipboard (Ctrl+C)"));
AppendMenu(lpgw->hPopMenu, MF_STRING, M_SAVE_AS_EMF, TEXT("&Save as EMF... (Ctrl+S)"));
AppendMenu(lpgw->hPopMenu, MF_STRING, M_SAVE_AS_BITMAP, TEXT("S&ave as Bitmap..."));
AppendMenu(lpgw->hPopMenu, MF_STRING, M_PRINT, TEXT("&Print..."));
/* settings */
AppendMenu(lpgw->hPopMenu, MF_SEPARATOR, 0, NULL);
AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->graphtotop ? MF_CHECKED : MF_UNCHECKED),
M_GRAPH_TO_TOP, TEXT("Bring to &Top"));
AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->color ? MF_CHECKED : MF_UNCHECKED),
M_COLOR, TEXT("C&olor"));
AppendMenu(lpgw->hPopMenu, MF_SEPARATOR, 0, NULL);
AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->gdiplus ? MF_CHECKED : MF_UNCHECKED),
M_GDI, TEXT("&GDI backend"));
#ifdef HAVE_GDIPLUS
AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->gdiplus ? MF_CHECKED : MF_UNCHECKED),
M_GDIPLUS, TEXT("GDI&+ backend"));
#endif
#ifdef HAVE_D2D
AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->d2d ? MF_CHECKED : MF_UNCHECKED),
M_D2D, TEXT("Direct&2D backend"));
#endif
AppendMenu(lpgw->hPopMenu, MF_SEPARATOR, 0, NULL);
#if defined(HAVE_GDIPLUS) || defined(HAVE_D2D)
AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->oversample ? MF_CHECKED : MF_UNCHECKED),
M_OVERSAMPLE, TEXT("O&versampling"));
AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->antialiasing ? MF_CHECKED : MF_UNCHECKED),
M_ANTIALIASING, TEXT("&Antialiasing"));
AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->polyaa ? MF_CHECKED : MF_UNCHECKED),
M_POLYAA, TEXT("Antialiasing of pol&ygons"));
AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->fastrotation ? MF_CHECKED : MF_UNCHECKED),
M_FASTROTATE, TEXT("Fast &rotation w/o antialiasing"));
AppendMenu(lpgw->hPopMenu, MF_SEPARATOR, 0, NULL);
#endif
AppendMenu(lpgw->hPopMenu, MF_STRING, M_BACKGROUND, TEXT("&Background..."));
AppendMenu(lpgw->hPopMenu, MF_STRING, M_CHOOSE_FONT, TEXT("&Font..."));
#ifdef WIN_CUSTOM_PENS
AppendMenu(lpgw->hPopMenu, MF_STRING, M_LINESTYLE, TEXT("&Line Styles..."));
#endif
/* save settings */
AppendMenu(lpgw->hPopMenu, MF_SEPARATOR, 0, NULL);
if (lpgw->IniFile != NULL) {
TCHAR buf[MAX_PATH];
wsprintf(buf, TEXT("&Update %s"), lpgw->IniFile);
AppendMenu(lpgw->hPopMenu, MF_STRING, M_WRITEINI, buf);
}
/* Update menu items */
GraphUpdateMenu(lpgw);
/* 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_PTR)lpgw->hPopMenu, TEXT("&Options"));
AppendMenu(sysmenu, MF_STRING, M_ABOUT, TEXT("&About"));
#ifndef WGP_CONSOLE
if (!IsWindowVisible(lpgw->lptw->hWndParent)) {
AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
AppendMenu(sysmenu, MF_STRING, M_COMMANDLINE, TEXT("C&ommand Line"));
}
#endif
/* determine the size of the window decoration: border, toolbar, caption, statusbar etc. */
{
RECT wrect, rect;
/* get real window size, not lpgw->Size */
GetWindowRect(lpgw->hWndGraph, &wrect);
GetClientRect(lpgw->hWndGraph, &rect);
lpgw->Decoration.x = wrect.right - wrect.left + rect.left - rect.right;
lpgw->Decoration.y = wrect.bottom - wrect.top + rect.top - rect.bottom + lpgw->ToolbarHeight + lpgw->StatusHeight;
}
/* resize to match requested canvas size */
if (!lpgw->bDocked && lpgw->Canvas.x != 0) {
lpgw->Size.x = lpgw->Canvas.x + lpgw->Decoration.x;
lpgw->Size.y = lpgw->Canvas.y + lpgw->Decoration.y;
SetWindowPos(lpgw->hWndGraph, HWND_BOTTOM,
lpgw->Origin.x, lpgw->Origin.y,
lpgw->Size.x, lpgw->Size.y,
SWP_NOACTIVATE | SWP_NOZORDER | SWP_NOMOVE);
}
// initialize font (and pens)
{
HDC hdc;
RECT rect;
hdc = GetDC(lpgw->hWndGraph);
MakePens(lpgw, hdc);
GetPlotRect(lpgw, &rect);
MakeFonts(lpgw, &rect, hdc);
ReleaseDC(lpgw->hWndGraph, hdc);
}
ShowWindow(lpgw->hWndGraph, SW_SHOWNORMAL);
#ifndef WGP_CONSOLE
/* update docked window layout */
if (lpgw->bDocked)
DockedUpdateLayout(lpgw->lptw);
#endif
}
void
GraphUpdateWindowPosSize(LPGW lpgw)
{
/* resize to match requested canvas size */
if (lpgw->Canvas.x != 0) {
lpgw->Size.x = lpgw->Canvas.x + lpgw->Decoration.x;
lpgw->Size.y = lpgw->Canvas.y + lpgw->Decoration.y;
}
SetWindowPos(lpgw->hWndGraph, HWND_BOTTOM, lpgw->Origin.x, lpgw->Origin.y, lpgw->Size.x, lpgw->Size.y, SWP_NOACTIVATE | SWP_NOZORDER);
}
/* close a graph window */
void
GraphClose(LPGW lpgw)
{
#ifdef USE_MOUSE
/* Pass it through mouse handling to check for "bind Close" */
Wnd_exec_event(lpgw, (LPARAM)0, GE_reset, 0);
#endif
/* close window */
if (lpgw->hWndGraph)
DestroyWindow(lpgw->hWndGraph);
WinMessageLoop();
lpgw->hWndGraph = NULL;
lpgw->hStatusbar = NULL;
lpgw->hToolbar = NULL;
lpgw->locked = TRUE;
DestroyBlocks(lpgw);
lpgw->locked = FALSE;
}
void
GraphStart(LPGW lpgw, double pointsize)
{
lpgw->locked = TRUE;
lpgw->buffervalid = FALSE;
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) {
#ifndef WGP_CONSOLE
if (lpgw->bDocked)
SetFocus(lpgw->hWndGraph);
else
#endif
BringWindowToTop(lpgw->hWndGraph);
return;
}
#endif /* USE_MOUSE */
SetWindowPos(lpgw->hWndGraph,
HWND_TOP, 0,0,0,0,
SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
}
}
void
GraphEnd(LPGW lpgw)
{
RECT rect;
GetClientRect(lpgw->hWndGraph, &rect);
InvalidateRect(lpgw->hWndGraph, &rect, 1);
lpgw->buffervalid = FALSE;
lpgw->locked = FALSE;
UpdateWindow(lpgw->hWndGraph);
#ifdef USE_MOUSE
gp_exec_event(GE_plotdone, 0, 0, 0, 0, lpgw->Id); /* notify main program */
#endif
}
/* shige */
void
GraphChangeTitle(LPGW lpgw)
{
if (GraphHasWindow(lpgw))
SetWindowText(lpgw->hWndGraph, lpgw->Title);
}
void
GraphResume(LPGW lpgw)
{
lpgw->locked = TRUE;
}
void
GraphPrint(LPGW lpgw)
{
if (GraphHasWindow(lpgw))
SendMessage(lpgw->hWndGraph, WM_COMMAND, M_PRINT, 0L);
}
void
GraphRedraw(LPGW lpgw)
{
lpgw->buffervalid = FALSE;
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->hapen = CreatePenIndirect(&pen); /* axis */
lpgw->hbrush = CreateSolidBrush(lpgw->background);
for (i = 0; i < WGNUMPENS + 2; i++)
lpgw->colorbrush[i] = CreateSolidBrush(lpgw->monopen[i].lopnColor);
} else {
pen = lpgw->colorpen[1];
pen.lopnWidth.x *= lpgw->linewidth;
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 = (char *) 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->hnull);
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;
/* FIXME: Do we need to call the Unicode version here? */
GetTextExtentPoint32A(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;
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);
rect->right -= rect->left;
rect->bottom -= rect->top;
rect->left = rect->top = 0;
/* 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 TBOOLEAN
TryCreateFont(LPGW lpgw, LPTSTR fontface, HDC hdc)
{
if (fontface != NULL)
_tcsncpy(lpgw->lf.lfFaceName, fontface, LF_FACESIZE);
lpgw->hfonth = CreateFontIndirect(&(lpgw->lf));
if (lpgw->hfonth != 0) {
/* Test if we actually got the requested font. Note that this also seems to work
with GDI's magic font substitutions (e.g. Helvetica->Arial, Times->Times New Roman) */
HFONT hfontold = (HFONT) SelectObject(hdc, lpgw->hfonth);
if (hfontold != NULL) {
TCHAR fontname[MAXFONTNAME];
GetTextFace(hdc, MAXFONTNAME, fontname);
SelectObject(hdc, hfontold);
if (_tcscmp(fontname, lpgw->lf.lfFaceName) == 0) {
return TRUE;
} else {
FPRINTF((stderr, "Warning: GDI would have substituted \"%s\" for \"%s\"\n", fontname, lpgw->lf.lfFaceName));
DeleteObject(lpgw->hfonth);
lpgw->hfonth = 0;
return FALSE;
}
}
return TRUE;
} else {
return FALSE;
}
return FALSE;
}
static void
MakeFonts(LPGW lpgw, LPRECT lprect, HDC hdc)
{
HFONT hfontold;
TEXTMETRIC tm;
int result;
LPTSTR p;
int cx, cy;
#ifdef HAVE_GDIPLUS
if (lpgw->gdiplus && !(lpgw->rotating && lpgw->fastrotation)) {
InitFont_gdiplus(lpgw, hdc, lprect);
return;
}
#endif
#ifdef HAVE_D2D
if (lpgw->d2d) {
InitFont_d2d(lpgw, hdc, lprect);
return;
}
#endif
lpgw->rotate = FALSE;
memset(&(lpgw->lf), 0, sizeof(LOGFONT));
_tcsncpy(lpgw->lf.lfFaceName, lpgw->fontname, LF_FACESIZE);
lpgw->lf.lfHeight = -MulDiv(lpgw->fontsize * lpgw->fontscale, GetDeviceCaps(hdc, LOGPIXELSY), 72);
lpgw->lf.lfCharSet = DEFAULT_CHARSET;
if (((p = _tcsstr(lpgw->fontname, TEXT(" Italic"))) != NULL) ||
((p = _tcsstr(lpgw->fontname, TEXT(":Italic"))) != NULL)) {
lpgw->lf.lfFaceName[(unsigned int) (p - lpgw->fontname)] = NUL;
lpgw->lf.lfItalic = TRUE;
}
if (((p = _tcsstr(lpgw->fontname, TEXT(" Bold"))) != NULL) ||
((p = _tcsstr(lpgw->fontname, TEXT(":Bold"))) != NULL)) {
lpgw->lf.lfFaceName[(unsigned int) (p - lpgw->fontname)] = NUL;
lpgw->lf.lfWeight = FW_BOLD;
}
lpgw->lf.lfOutPrecision = OUT_OUTLINE_PRECIS;
lpgw->lf.lfClipPrecision = CLIP_DEFAULT_PRECIS;
lpgw->lf.lfQuality = lpgw->antialiasing ? CLEARTYPE_QUALITY : PROOF_QUALITY;
/* Handle zero length type face name by replacing with default name */
if (lpgw->lf.lfFaceName[0] == 0)
_tcsncpy(lpgw->lf.lfFaceName, GraphDefaultFont(), LF_FACESIZE);
if (!TryCreateFont(lpgw, NULL, hdc)) {
//static const char warn_font_not_available[] =
// "Warning: font \"" TCHARFMT "\" not available, trying \"" TCHARFMT "\" instead.\n";
static const char err_giving_up[] = "Error: could not substitute another font. Giving up.\n";
if (_tcscmp(lpgw->fontname, lpgw->deffontname) != 0) {
//fprintf(stderr, warn_font_not_available, lpgw->fontname, lpgw->deffontname);
if (!TryCreateFont(lpgw, lpgw->deffontname, hdc)) {
if (_tcscmp(lpgw->deffontname, GraphDefaultFont()) != 0) {
//fprintf(stderr, warn_font_not_available, lpgw->deffontname, GraphDefaultFont());
if (!TryCreateFont(lpgw, GraphDefaultFont(), hdc)) {
fprintf(stderr, err_giving_up);
}
} else {
fprintf(stderr, err_giving_up);
}
}
} else {
if (_tcscmp(lpgw->fontname, GraphDefaultFont()) != 0) {
//fprintf(stderr, warn_font_not_available, lpgw->fontname, GraphDefaultFont());
if (!TryCreateFont(lpgw, GraphDefaultFont(), hdc)) {
fprintf(stderr, err_giving_up);
}
} else {
fprintf(stderr, "Error: font \"" TCHARFMT "\" not available, but don't know which font to substitute.\n", lpgw->fontname);
}
}
}
/* 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 = (HFONT) 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, lpgw->xmax, 10 * (lprect->right - lprect->left));
/* CMW: Base tick size on character size */
lpgw->htic = MulDiv(lpgw->hchar, 2, 5);
cy = MulDiv(cx, 2 * GetDeviceCaps(hdc, LOGPIXELSY), 50 * 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 */
/* store text metrics for later use */
lpgw->tmHeight = tm.tmHeight;
lpgw->tmAscent = tm.tmAscent;
lpgw->tmDescent = tm.tmDescent;
SelectObject(hdc, hfontold);
}
static void
DestroyFonts(LPGW lpgw)
{
if (lpgw->hfonth) {
DeleteObject(lpgw->hfonth);
lpgw->hfonth = 0;
}
if (lpgw->hfontv) {
DeleteObject(lpgw->hfontv);
lpgw->hfontv = 0;
}
}
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);
}
}
static void
SelFont(LPGW lpgw)
{
LOGFONT lf;
CHOOSEFONT cf;
HDC hdc;
TCHAR * p;
/* Set all structure fields to zero. */
memset(&cf, 0, sizeof(CHOOSEFONT));
memset(&lf, 0, sizeof(LOGFONT));
cf.lStructSize = sizeof(CHOOSEFONT);
cf.hwndOwner = lpgw->hWndGraph;
_tcsncpy(lf.lfFaceName, lpgw->fontname, LF_FACESIZE);
if ((p = _tcsstr(lpgw->fontname, TEXT(" Bold"))) != NULL) {
lf.lfWeight = FW_BOLD;
lf.lfFaceName[p - lpgw->fontname] = NUL;
} else {
lf.lfWeight = FW_NORMAL;
}
if ((p = _tcsstr(lpgw->fontname, TEXT(" Italic"))) != NULL) {
lf.lfItalic = TRUE;
lf.lfFaceName[p - lpgw->fontname] = NUL;
} else {
lf.lfItalic = FALSE;
}
lf.lfCharSet = DEFAULT_CHARSET;
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_SCALABLEONLY;
if (ChooseFont(&cf)) {
_tcscpy(lpgw->fontname, lf.lfFaceName);
lpgw->fontsize = cf.iPointSize / 10;
if (cf.nFontType & BOLD_FONTTYPE)
_tcscat(lpgw->fontname, TEXT(" Bold"));
if (cf.nFontType & ITALIC_FONTTYPE)
_tcscat(lpgw->fontname, TEXT(" Italic"));
/* set current font as default font */
_tcscpy(lpgw->deffontname, lpgw->fontname);
lpgw->deffontsize = lpgw->fontsize;
SendMessage(lpgw->hWndGraph, WM_COMMAND, M_REBUILDTOOLS, 0L);
}
}
#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 */
}
#endif /* USE_MOUSE */
/* ================================== */
static void
dot(HDC hdc, int xdash, int ydash)
{
MoveTo(hdc, xdash, ydash);
LineTo(hdc, xdash, ydash + 1);
}
unsigned
luma_from_color(unsigned red, unsigned green, unsigned blue)
{
/* convert to gray */
return (unsigned)(red * 0.30 + green * 0.59 + blue * 0.11);
}
static unsigned int
GraphGetTextLength(LPGW lpgw, HDC hdc, LPCSTR text)
{
SIZE size;
LPWSTR textw;
if (text == NULL)
return 0;
textw = UnicodeText(text, lpgw->encoding);
if (textw) {
GetTextExtentPoint32W(hdc, textw, wcslen(textw), &size);
free(textw);
} else {
GetTextExtentPoint32A(hdc, text, strlen(text), &size);
}
return size.cx;
}
/* local enhanced text helper functions */
static void
EnhancedSetFont()
{
GraphChangeFont(enhstate.lpgw, enhstate.fontname, enhstate.fontsize,
enhstate_gdi.hdc, *(enhstate.rect));
SetFont(enhstate.lpgw, enhstate_gdi.hdc);
}
static unsigned
EnhancedTextLength(char * text)
{
return GraphGetTextLength(enhstate.lpgw, enhstate_gdi.hdc, text);
}
static void
EnhancedPutText(int x, int y, char * text)
{
draw_put_text(enhstate.lpgw, enhstate_gdi.hdc, x, y, text);
}
static void
EnhancedCleanup()
{
/* restore text alignment */
draw_text_justify(enhstate_gdi.hdc, enhstate.lpgw->justify);
}
static void
draw_enhanced_init(HDC hdc)
{
enhstate.set_font = &EnhancedSetFont;
enhstate.text_length = &EnhancedTextLength;
enhstate.put_text = &EnhancedPutText;
enhstate.cleanup = &EnhancedCleanup;
enhstate_gdi.hdc = hdc;
enhstate.res_scale = GetDeviceCaps(hdc, LOGPIXELSY) / 96.;
SetTextAlign(hdc, TA_LEFT | TA_BASELINE);
}
/* enhanced text functions shared with wgdiplus.cpp */
void
GraphEnhancedOpen(char *fontname, double fontsize, double base,
TBOOLEAN widthflag, TBOOLEAN showflag, int overprint)
{
const int win_scale = 1; /* scaling of base offset */
/* 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)) {
#ifdef UNICODE
MultiByteToWideChar(CP_ACP, 0, fontname, -1, enhstate.fontname, MAXFONTNAME);
#else
strcpy(enhstate.fontname, fontname);
#endif
} else {
_tcscpy(enhstate.fontname, enhstate.lpgw->deffontname);
}
enhstate.fontsize = fontsize;
enhstate.set_font();
/* Scale fractional font height to vertical units of display */
/* TODO: Proper use of OUTLINEFONTMETRICS would yield better
results. */
enhstate.base = win_scale * base *
enhstate.lpgw->fontscale *
enhstate.res_scale;
}
}
void
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 = enhstate.text_length(enhanced_text);
width = cos(angle) * len;
height = -sin(angle) * len;
if (enhstate.widthflag && !enhstate.sizeonly && !enhstate.overprint) {
/* do not take rotation into account */
int ypos = -enhstate.base;
enhstate.totalwidth += len;
if (enhstate.totalasc > (ypos + enhstate.shift - enhstate.lpgw->tmAscent))
enhstate.totalasc = ypos + enhstate.shift - enhstate.lpgw->tmAscent;
if (enhstate.totaldesc < (ypos + enhstate.shift + enhstate.lpgw->tmDescent))
enhstate.totaldesc = ypos + enhstate.shift + enhstate.lpgw->tmDescent;
}
/* display string */
if (enhstate.show && !enhstate.sizeonly)
enhstate.put_text(x, y, enhanced_text);
/* update drawing position according to text length */
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;
}
int
draw_enhanced_text(LPGW lpgw, LPRECT rect, int x, int y, const char * str)
{
const char * original_string = str;
unsigned int pass, num_passes;
struct termentry *tsave;
TCHAR save_fontname[MAXFONTNAME];
int save_fontsize;
/* Init enhanced text state */
enhstate.lpgw = lpgw;
enhstate.rect = rect;
enhstate.opened_string = FALSE;
_tcscpy(enhstate.fontname, lpgw->fontname);
enhstate.fontsize = lpgw->fontsize;
/* Store the start position */
enhstate.x = x;
enhstate.y = y;
enhstate.totalwidth = 0;
enhstate.totalasc = 0;
enhstate.totaldesc = 0;
/* Save font information */
_tcscpy(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. */
/* Adjust baseline position: */
enhstate.shift = lpgw->tmHeight/2 - lpgw->tmDescent;
enhstate.x += sin(lpgw->angle * M_PI/180) * enhstate.shift;
enhstate.y += cos(lpgw->angle * M_PI/180) * enhstate.shift;
/* enhanced_recursion() uses the callback functions of the current
terminal. So we have to switch temporarily. */
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.
*/
#ifdef UNICODE
char save_fontname_a[MAXFONTNAME];
WideCharToMultiByte(CP_ACP, 0, save_fontname, MAXFONTNAME, save_fontname_a, MAXFONTNAME, 0, 0);
#else
char * save_fontname_a = save_fontname;
#endif
while (*(str = enhanced_recursion(str, TRUE,
save_fontname_a, 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 font */
_tcscpy(enhstate.fontname, save_fontname);
enhstate.fontsize = save_fontsize;
enhstate.set_font();
/* clean-up */
enhstate.cleanup();
return enhstate.totalwidth;
}
void
draw_get_enhanced_text_extend(PRECT extend)
{
switch (enhstate.lpgw->justify) {
case LEFT:
extend->left = 0;
extend->right = enhstate.totalwidth;
break;
case CENTRE:
extend->left = enhstate.totalwidth / 2;
extend->right = enhstate.totalwidth / 2;
break;
case RIGHT:
extend->left = enhstate.totalwidth;
extend->right = 0;
break;
}
extend->top = - enhstate.totalasc;
extend->bottom = enhstate.totaldesc;
}
static void
draw_text_justify(HDC hdc, int justify)
{
switch (justify)
{
case LEFT:
SetTextAlign(hdc, TA_LEFT | TA_TOP);
break;
case RIGHT:
SetTextAlign(hdc, TA_RIGHT | TA_TOP);
break;
case CENTRE:
SetTextAlign(hdc, TA_CENTER | TA_TOP);
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)) {
TextOutA(hdc, x, y, str, strlen(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 */
TextOutA(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 |
(lpgw->rounded ? PS_ENDCAP_ROUND | PS_JOIN_ROUND : PS_ENDCAP_SQUARE | PS_JOIN_MITER),
cur_penstruct.lopnWidth.x, &lb, 0, 0);
lpgw->hsolid = ExtCreatePen(
PS_GEOMETRIC | PS_SOLID |
(lpgw->rounded ? PS_ENDCAP_ROUND | PS_JOIN_ROUND : PS_ENDCAP_SQUARE | PS_JOIN_MITER),
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;
}
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;
}
static void
draw_grey_out_key_box(LPGW lpgw, HDC hdc, int plotno)
{
HDC memdc;
HBITMAP membmp, oldbmp;
BLENDFUNCTION ftn;
HBRUSH oldbrush;
LPRECT bb;
int width, height;
bb = lpgw->keyboxes + plotno - 1;
width = bb->right - bb->left + 1;
height = bb->top - bb->bottom + 1;
memdc = CreateCompatibleDC(hdc);
membmp = CreateCompatibleBitmap(hdc, width, height);
oldbmp = (HBITMAP) SelectObject(memdc, membmp);
oldbrush = SelectObject(memdc, (HBRUSH) GetStockObject(LTGRAY_BRUSH));
PatBlt(memdc, 0, 0, width, height, PATCOPY);
ftn.AlphaFormat = 0; /* no alpha channel in bitmap */
ftn.SourceConstantAlpha = (UCHAR)(128); /* global alpha */
ftn.BlendOp = AC_SRC_OVER;
ftn.BlendFlags = 0;
AlphaBlend(hdc, bb->left, bb->bottom, width, height,
memdc, 0, 0, width, height, ftn);
SelectObject(memdc, oldbrush);
SelectObject(memdc, oldbmp);
DeleteObject(membmp);
DeleteDC(memdc);
}
void
draw_image(LPGW lpgw, HDC hdc, char *image, POINT corners[4], unsigned int width, unsigned int height, int color_mode)
{
BITMAPINFO bmi;
HRGN hrgn;
if (image == NULL)
return;
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) {
char * dibimage;
bmi.bmiHeader.biBitCount = 24;
if (!lpgw->color) {
/* create a copy of the color image */
int pad_bytes = (4 - (3 * width) % 4) % 4; /* scan lines start on ULONG boundaries */
int image_size = (width * 3 + pad_bytes) * height;
int x, y;
dibimage = (char *) malloc(image_size);
memcpy(dibimage, image, image_size);
for (y = 0; y < height; y ++) {
for (x = 0; x < width; x ++) {
BYTE * p = (BYTE *) dibimage + y * (3 * width + pad_bytes) + x * 3;
/* convert to gray */
unsigned luma = luma_from_color(p[2], p[1], p[0]);
p[0] = p[1] = p[2] = luma;
}
}
} else {
dibimage = image;
}
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,
dibimage, &bmi, DIB_RGB_COLORS, SRCCOPY);
if (!lpgw->color) free(dibimage);
} 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);
/* convert to grayscale? */
if (!lpgw->color) {
int x, y;
for (y = 0; y < height; y ++) {
for (x = 0; x < width; x ++) {
UINT32 * p = pvBits + y * width + x;
/* convert to gray */
unsigned luma = luma_from_color(GetRValue(*p), GetGValue(*p), GetBValue(*p));
*p = (*p & 0xff000000) | RGB(luma, luma, luma);
}
}
}
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);
}
/* 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 and hypertext */
unsigned plotno = 0;
BOOL gridline = FALSE;
BOOL skipplot = FALSE;
BOOL keysample = FALSE;
BOOL interactive;
LPWSTR hypertext = NULL;
int hypertype = 0;
/* colors */
BOOL isColor; /* use colors? */
COLORREF last_color = 0; /* currently selected color */
double alpha_c = 1.; /* alpha for transparency */
/* lines */
double line_width = 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 */
/* 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 */
POINT corners[4]; /* image corners */
int color_mode = 0; /* image color mode */
#ifdef EAM_BOXED_TEXT
struct s_boxedtext {
TBOOLEAN boxing;
t_textbox_options option;
POINT margin;
POINT start;
RECT box;
int angle;
} boxedtext;
#endif
/* point symbols */
bool ps_caching = FALSE;
enum win_pointtypes last_symbol = W_invalid_pointtype;
HDC cb_memdc = NULL;
HBITMAP cb_old_bmp;
HBITMAP cb_membmp;
POINT cb_ofs;
/* 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 seq = 0; /* sequence counter for W_image and W_boxedtext */
int i;
if (lpgw->locked)
return;
/* clear hypertexts only in display sessions */
interactive = (GetObjectType(hdc) == OBJ_MEMDC) ||
((GetObjectType(hdc) == OBJ_DC) && (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASDISPLAY));
if (interactive)
clear_tooltips(lpgw);
/* 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().
* Also note that querying the technology of a metafile dc does not work.
* Query the type instead.
*/
isColor = (((GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL)) > 2)
|| (GetObjectType(hdc) == OBJ_ENHMETADC)
|| (GetObjectType(hdc) == OBJ_METADC)
|| (GetDeviceCaps(hdc, TECHNOLOGY) == DT_PLOTTER)
|| (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASPRINTER));
if (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, LOGPIXELSX) /
(double) GetDeviceCaps(hdc_screen, LOGPIXELSY);
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);
}
// only cache point symbols when drawing to a screen
ps_caching = !((GetObjectType(hdc) == OBJ_ENHMETADC)
|| (GetObjectType(hdc) == OBJ_METADC)
|| (GetDeviceCaps(hdc, TECHNOLOGY) == DT_PLOTTER)
|| (GetDeviceCaps(hdc, TECHNOLOGY) == DT_RASPRINTER));
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);
/* (re-)init GDI fonts */
GraphChangeFont(lpgw, lpgw->deffontname, lpgw->deffontsize, hdc, *rect);
lpgw->angle = 0;
SetFont(lpgw, hdc);
lpgw->justify = LEFT;
SetTextAlign(hdc, TA_LEFT | TA_TOP);
/* calculate text shift for horizontal text */
hshift = 0;
vshift = - lpgw->tmHeight / 2; /* centered */
/* 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;
}
#ifdef EAM_BOXED_TEXT
boxedtext.boxing = FALSE;
#endif
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 != NULL) {
if (!blkptr->gwop)
blkptr->gwop = (struct GWOP *)GlobalLock(blkptr->hblk);
if (!blkptr->gwop)
return;
curptr = (struct GWOP *)blkptr->gwop;
}
if (curptr == NULL)
return;
while (ngwop < lpgw->nGWOP) {
/* transform the coordinates */
xdash = MulDiv(curptr->x, rr - rl - 1, lpgw->xmax) + rl;
ydash = rb - MulDiv(curptr->y, rb - rt - 1, lpgw->ymax) + rt - 1;
/* 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:
/* grey out keysample if graph is hidden */
if ((plotno <= lpgw->maxhideplots) && lpgw->hideplot[plotno - 1])
draw_grey_out_key_box(lpgw, hdc, plotno);
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)
|| (curptr->op == W_pointsize) || (curptr->op == W_line_width)) {
/* special case hypertexts */
if ((hypertext != NULL) && (hypertype == TERM_HYPERTEXT_TOOLTIP)) {
/* point symbols */
if ((curptr->op >= W_dot) && (curptr->op <= W_dot + WIN_POINT_TYPES)) {
RECT rect;
rect.left = xdash - htic;
rect.right = xdash + htic;
rect.top = ydash - vtic;
rect.bottom = ydash + vtic;
add_tooltip(lpgw, &rect, hypertext);
hypertext = NULL;
}
}
switch (curptr->op) {
case 0: /* have run past last in this block */
break;
case W_layer: /* already handled above */
break;
case W_polyline: {
POINTL * poly = (POINTL *) LocalLock(curptr->htext);
polyi = curptr->x;
if (polyi >= polymax) {
const int step = 200;
polymax = (polyi + step) / step;
polymax *= step;
ppt = (POINT *)LocalReAllocPtr(ppt, LHND, (polymax + 1) * sizeof(POINT));
}
for (i = 0; i < polyi; i++) {
/* transform the coordinates */
ppt[i].x = MulDiv(poly[i].x, rr - rl - 1, lpgw->xmax) + rl;
ppt[i].y = rb - MulDiv(poly[i].y, rb - rt - 1, lpgw->ymax) + rt - 1;
}
LocalUnlock(poly);
Polyline(hdc, ppt, polyi);
if (keysample) {
draw_update_keybox(lpgw, plotno, ppt[0].x, ppt[0].y);
draw_update_keybox(lpgw, plotno, ppt[polyi - 1].x, ppt[polyi - 1].y);
}
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;
alpha_c = 1.;
break;
}
case W_dash_type: {
int dt = curptr->x;
if (dt >= 0) {
dt %= WGNUMPENS;
dt += 2;
cur_penstruct.lopnStyle = lpgw->monopen[dt].lopnStyle;
draw_new_pens(lpgw, hdc, cur_penstruct);
} else if (dt == DASHTYPE_SOLID) {
cur_penstruct.lopnStyle = PS_SOLID;
draw_new_pens(lpgw, hdc, cur_penstruct);
} else if (dt == DASHTYPE_AXIS) {
dt = 1;
cur_penstruct.lopnStyle =
lpgw->dashed ? lpgw->monopen[dt].lopnStyle : lpgw->colorpen[dt].lopnStyle;
draw_new_pens(lpgw, hdc, cur_penstruct);
} else if (dt == DASHTYPE_CUSTOM) {
; /* ignored */
}
break;
}
case W_text_encoding:
lpgw->encoding = (enum set_encoding_id) curptr->x;
break;
case W_put_text: {
char * str = (char *) LocalLock(curptr->htext);
if (str) {
int dxl, dxr;
int slen, vsize;
/* shift correctly for rotated text */
draw_put_text(lpgw, hdc, xdash + hshift, ydash + vshift, str);
#ifndef EAM_BOXED_TEXT
if (keysample) {
#else
if (keysample || boxedtext.boxing) {
#endif
slen = GraphGetTextLength(lpgw, hdc, str);
vsize = MulDiv(lpgw->vchar, rb - rt, 2 * lpgw->ymax);
if (lpgw->justify == LEFT) {
dxl = 0;
dxr = slen;
} else if (lpgw->justify == CENTRE) {
dxl = dxr = slen / 2;
} else {
dxl = slen;
dxr = 0;
}
}
if (keysample) {
draw_update_keybox(lpgw, plotno, xdash - dxl, ydash - vsize);
draw_update_keybox(lpgw, plotno, xdash + dxr, ydash + vsize);
}
#ifdef EAM_BOXED_TEXT
if (boxedtext.boxing) {
if (boxedtext.box.left > (xdash - boxedtext.start.x - dxl))
boxedtext.box.left = xdash - boxedtext.start.x - dxl;
if (boxedtext.box.right < (xdash - boxedtext.start.x + dxr))
boxedtext.box.right = xdash - boxedtext.start.x + dxr;
if (boxedtext.box.top > (ydash - boxedtext.start.y - vsize))
boxedtext.box.top = ydash - boxedtext.start.y - vsize;
if (boxedtext.box.bottom < (ydash - boxedtext.start.y + vsize))
boxedtext.box.bottom = ydash - boxedtext.start.y + vsize;
/* We have to remember the text angle as well. */
boxedtext.angle = lpgw->angle;
}
#endif
}
LocalUnlock(curptr->htext);
break;
}
case W_enhanced_text: {
char * str = (char *) LocalLock(curptr->htext);
if (str) {
RECT extend;
draw_enhanced_init(hdc);
draw_enhanced_text(lpgw, rect, xdash, ydash, str);
draw_get_enhanced_text_extend(&extend);
if (keysample) {
draw_update_keybox(lpgw, plotno, xdash - extend.left, ydash - extend.top);
draw_update_keybox(lpgw, plotno, xdash + extend.right, ydash + extend.bottom);
}
#ifdef EAM_BOXED_TEXT
if (boxedtext.boxing) {
if (boxedtext.box.left > (boxedtext.start.x - xdash - extend.left))
boxedtext.box.left = boxedtext.start.x - xdash - extend.left;
if (boxedtext.box.right < (boxedtext.start.x - xdash + extend.right))
boxedtext.box.right = boxedtext.start.x - xdash + extend.right;
if (boxedtext.box.top > (boxedtext.start.y - ydash - extend.top))
boxedtext.box.top = boxedtext.start.y - ydash - extend.top;
if (boxedtext.box.bottom < (boxedtext.start.y - ydash + extend.bottom))
boxedtext.box.bottom = boxedtext.start.y - ydash + extend.bottom;
/* We have to store the text angle as well. */
boxedtext.angle = lpgw->angle;
}
#endif
}
LocalUnlock(curptr->htext);
break;
}
case W_hypertext:
if (interactive) {
/* Make a copy for future reference */
char * str = LocalLock(curptr->htext);
free(hypertext);
hypertext = UnicodeText(str, lpgw->encoding);
hypertype = curptr->x;
LocalUnlock(curptr->htext);
}
break;
#ifdef EAM_BOXED_TEXT
case W_boxedtext:
if (seq == 0) {
boxedtext.option = curptr->x;
seq++;
break;
}
seq = 0;
switch (boxedtext.option) {
case TEXTBOX_INIT:
/* initialise bounding box */
boxedtext.box.left = boxedtext.box.right = 0;
boxedtext.box.bottom = boxedtext.box.top = 0;
boxedtext.start.x = xdash;
boxedtext.start.y = ydash;
/* Note: initialising the text angle here would be best IMHO,
but current core code does not set this until the actual
print-out is done. */
boxedtext.angle = lpgw->angle;
boxedtext.boxing = TRUE;
break;
case TEXTBOX_OUTLINE:
case TEXTBOX_BACKGROUNDFILL: {
/* draw rectangle */
int dx = boxedtext.margin.x;
int dy = boxedtext.margin.y;
#if 0
printf("left %i right %i top %i bottom %i angle %i\n",
boxedtext.box.left - dx, boxedtext.box.right + dx,
boxedtext.box.top - dy, boxedtext.box.bottom + dy,
boxedtext.angle);
#endif
if (((boxedtext.angle % 90) == 0) && (alpha_c == 1.)) {
RECT rect;
switch (boxedtext.angle) {
case 0:
rect.left = + boxedtext.box.left - dx;
rect.right = + boxedtext.box.right + dx;
rect.top = + boxedtext.box.top - dy;
rect.bottom = + boxedtext.box.bottom + dy;
break;
case 90:
rect.left = + boxedtext.box.top - dy;
rect.right = + boxedtext.box.bottom + dy;
rect.top = - boxedtext.box.right - dx;
rect.bottom = - boxedtext.box.left + dx;
break;
case 180:
rect.left = - boxedtext.box.right - dx;
rect.right = - boxedtext.box.left + dx;
rect.top = - boxedtext.box.bottom - dy;
rect.bottom = - boxedtext.box.top + dy;
break;
case 270:
rect.left = - boxedtext.box.bottom - dy;
rect.right = - boxedtext.box.top + dy;
rect.top = + boxedtext.box.left - dx;
rect.bottom = + boxedtext.box.right + dx;
break;
}
rect.left += boxedtext.start.x;
rect.right += boxedtext.start.x;
rect.top += boxedtext.start.y;
rect.bottom += boxedtext.start.y;
if (boxedtext.option == TEXTBOX_OUTLINE)
FrameRect(hdc, &rect, lpgw->hcolorbrush);
else
/* Fill bounding box with current color. */
FillRect(hdc, &rect, lpgw->hcolorbrush);
} else {
double theta = boxedtext.angle * M_PI/180.;
double sin_theta = sin(theta);
double cos_theta = cos(theta);
POINT rect[5];
rect[0].x = (boxedtext.box.left - dx) * cos_theta +
(boxedtext.box.top - dy) * sin_theta;
rect[0].y = -(boxedtext.box.left - dx) * sin_theta +
(boxedtext.box.top - dy) * cos_theta;
rect[1].x = (boxedtext.box.left - dx) * cos_theta +
(boxedtext.box.bottom + dy) * sin_theta;
rect[1].y = -(boxedtext.box.left - dx) * sin_theta +
(boxedtext.box.bottom + dy) * cos_theta;
rect[2].x = (boxedtext.box.right + dx) * cos_theta +
(boxedtext.box.bottom + dy) * sin_theta;
rect[2].y = -(boxedtext.box.right + dx) * sin_theta +
(boxedtext.box.bottom + dy) * cos_theta;
rect[3].x = (boxedtext.box.right + dx) * cos_theta +
(boxedtext.box.top - dy) * sin_theta;
rect[3].y = -(boxedtext.box.right + dx) * sin_theta +
(boxedtext.box.top - dy) * cos_theta;
for (i = 0; i < 4; i++) {
rect[i].x += boxedtext.start.x;
rect[i].y += boxedtext.start.y;
}
{
HBRUSH save_brush;
HPEN save_pen;
if (boxedtext.option == TEXTBOX_OUTLINE) {
save_brush = SelectObject(hdc, GetStockBrush(NULL_BRUSH));
save_pen = SelectObject(hdc, lpgw->hapen);
} else {
/* Fill bounding box with current color. */
save_brush = SelectObject(hdc, lpgw->hcolorbrush);
save_pen = SelectObject(hdc, GetStockPen(NULL_PEN));
}
Polygon(hdc, rect, 4);
SelectObject(hdc, save_brush);
SelectObject(hdc, save_pen);
}
}
boxedtext.boxing = FALSE;
break;
}
case TEXTBOX_MARGINS:
/* Adjust size of whitespace around text: default is 1/2 char height + 2 char widths. */
boxedtext.margin.x = MulDiv(curptr->x * lpgw->hchar, rr - rl, 1000 * lpgw->xmax);
boxedtext.margin.y = MulDiv(curptr->y * lpgw->hchar, rr - rl, 1000 * lpgw->xmax);
break;
default:
break;
}
break;
#endif
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.;
/* FIXME: This shouldn't be necessary... */
polyi = 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: {
if (alpha_c < 1.) {
alpha = alpha_c;
fill_color = last_color;
} else if ((int)(fillstyle >> 4) == 100) {
/* special case this common choice */
// FIXME: we should already have that!
fill_color = last_color;
} else {
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)));
fill_color = color;
}
draw_new_brush(lpgw, hdc, fill_color);
solid_brush = lpgw->hcolorbrush;
break;
}
case FS_TRANSPARENT_PATTERN:
if ((shadedblendcaps & SB_CONST_ALPHA) != 0) {
transparent = TRUE;
alpha = 1.;
} else {
/* Printers do 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_EMPTY:
/* FIXME: Instead of filling with background color, we should not fill at all in this case! */
/* fill with background color */
SelectObject(hdc, lpgw->hbrush);
fill_color = lpgw->background;
solid_brush = lpgw->hbrush;
break;
case FS_DEFAULT:
default:
/* Leave the current brush and color in place */
break;
}
break;
case W_move:
ppt[0].x = xdash;
ppt[0].y = ydash;
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 = abs(xdash - ppt[0].x);
height = abs(ppt[0].y - ydash);
p.x = GPMIN(ppt[0].x, xdash);
p.y = GPMIN(ydash, ppt[0].y);
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, width, height);
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) * lpgw->tmHeight / 2.;
vshift = - cos(M_PI/180. * lpgw->angle) * lpgw->tmHeight / 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 = (char *) LocalLock(curptr->htext);
/* GraphChangeFont already handles font==NULL and size==0,
so the checks below are a bit paranoid...
*/
#ifdef UNICODE
TCHAR tfont[MAXFONTNAME];
if (font != NULL)
MultiByteToWideChar(CP_ACP, 0, font, -1, tfont, MAXFONTNAME);
#else
LPTSTR tfont = font;
#endif
GraphChangeFont(lpgw,
font != NULL ? tfont : 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) * lpgw->tmHeight / 2.;
vshift = - cos(M_PI/180. * lpgw->angle) * lpgw->tmHeight / 2.;
break;
}
case W_pointsize:
if (curptr->x > 0) {
double pointsize = curptr->x / 100.0;
htic = MulDiv(pointsize * lpgw->pointscale * lpgw->htic, rr - rl, lpgw->xmax) + 1;
vtic = MulDiv(pointsize * lpgw->pointscale * lpgw->vtic, rb - rt, lpgw->ymax) + 1;
} else {
htic = vtic = 0;
}
/* invalidate point symbol cache */
last_symbol = W_invalid_pointtype;
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->linewidth * lw_scale;
/* invalidate point symbol cache */
last_symbol = W_invalid_pointtype;
break;
case W_setcolor: {
COLORREF color;
if (curptr->htext != NULL) { /* TC_LT */
int pen = (int)curptr->x % WGNUMPENS;
if (pen <= LT_NODRAW) {
color = lpgw->background;
} else {
if (lpgw->color)
color = lpgw->colorpen[pen + 2].lopnColor;
else
color = lpgw->monopen[pen + 2].lopnColor;
}
alpha_c = 1.;
} else { /* TC_RGB */
rgb255_color rgb255;
rgb255.r = (curptr->y & 0xff);
rgb255.g = (curptr->x >> 8);
rgb255.b = (curptr->x & 0xff);
alpha_c = 1. - ((curptr->y >> 8) & 0xff) / 255.;
if (lpgw->color || ((rgb255.r == rgb255.g) && (rgb255.r == rgb255.b))) {
/* Use colors or this is already gray scale */
color = RGB(rgb255.r, rgb255.g, rgb255.b);
} else {
/* convert to gray */
unsigned luma = luma_from_color(rgb255.r, rgb255.g, rgb255.b);
color = RGB(luma, luma, luma);
}
}
/* 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);
/* invalidate point symbol cache */
if (last_color != color)
last_symbol = W_invalid_pointtype;
/* 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) {
/* fill area without border */
SelectObject(hdc, lpgw->hnull);
Polygon(hdc, ppt, polyi);
SelectObject(hdc, lpgw->hapen); /* restore previous pen */
} else {
/* 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 6 entries are needed in total. */
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 = (char *) LocalLock(curptr->htext);
unsigned int width = curptr->x;
unsigned int height = curptr->y;
draw_image(lpgw, hdc, image, corners, width, height, color_mode);
LocalUnlock(curptr->htext);
}
seq = (seq + 1) % 6;
}
break;
default: {
int xofs, yofs;
HDC dc;
HBRUSH old_brush;
HPEN old_pen;
enum win_pointtypes symbol = (enum win_pointtypes) curptr->op;
/* This covers only point symbols. All other codes should be
handled in the switch statement. */
if ((symbol < W_dot) || (symbol > W_last_pointtype))
break;
/* draw cached point symbol */
if (ps_caching && (last_symbol == symbol) && (cb_memdc != NULL)) {
TransparentBlt(hdc, xdash - cb_ofs.x, ydash - cb_ofs.y, 2*htic+2, 2*vtic+2,
cb_memdc, 0, 0, 2*htic+2, 2*vtic+2, 0x00ffffff);
break;
} else {
if (cb_memdc != NULL) {
SelectObject(cb_memdc, cb_old_bmp);
DeleteObject(cb_membmp);
DeleteDC(cb_memdc);
cb_memdc = NULL;
}
}
/* caching of point symbols? */
if (ps_caching) {
RECT rect;
/* create memory device context for bitmap */
dc = cb_memdc = CreateCompatibleDC(hdc);
/* create standard bitmap, no alpha channel */
cb_membmp = CreateCompatibleBitmap(hdc, 2*htic+2, 2*vtic+2);
cb_old_bmp = (HBITMAP) SelectObject(dc, cb_membmp);
/* prepare memory context */
SetTextColor(dc, fill_color);
old_brush = (HBRUSH) SelectObject(dc, solid_brush);
old_pen = (HPEN) SelectObject(dc, lpgw->hapen);
rect.left = rect.bottom = 0;
rect.top = 2*vtic+2;
rect.right = 2*htic+2;
FillRect(dc, &rect, (HBRUSH) GetStockObject(WHITE_BRUSH));
cb_ofs.x = xofs = htic+1;
cb_ofs.y = yofs = vtic+1;
last_symbol = symbol;
} else {
dc = hdc;
xofs = xdash;
yofs = ydash;
}
switch (symbol) {
case W_dot:
dot(dc, xofs, yofs);
break;
case W_plus: /* do plus */
case W_star: /* do star: first plus, then cross */
SelectObject(dc, lpgw->hsolid);
MoveTo(dc, xofs - htic, yofs);
LineTo(dc, xofs + htic, yofs);
MoveTo(dc, xofs, yofs - vtic);
LineTo(dc, xofs, yofs + vtic);
SelectObject(dc, lpgw->hapen);
if (symbol == W_plus)
break;
case W_cross: /* do X */
SelectObject(dc, lpgw->hsolid);
MoveTo(dc, xofs - htic, yofs - vtic);
LineTo(dc, xofs + htic, yofs + vtic);
MoveTo(dc, xofs - htic, yofs + vtic);
LineTo(dc, xofs + htic, yofs - vtic);
SelectObject(dc, lpgw->hapen);
break;
case W_circle: /* do open circle */
SelectObject(dc, lpgw->hsolid);
Arc(dc, xofs - htic, yofs - vtic, xofs + htic + 1, yofs + vtic + 1,
xofs, yofs + vtic + 1, xofs, yofs + vtic + 1);
dot(dc, xofs, yofs);
SelectObject(dc, lpgw->hapen);
break;
case W_fcircle: /* do filled circle */
SelectObject(dc, lpgw->hsolid);
Ellipse(dc, xofs-htic, yofs-vtic,
xofs+htic+1, yofs+vtic+1);
SelectObject(dc, lpgw->hapen);
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, -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, -1, 0, 0, -1, +1, 0, 0, 0}, /* diamond */
{ 0, 1, 0.95106, 0.30902, 0.58779, -0.80902,
-0.58779, -0.80902, -0.95106, 0.30902} /* pentagon */
};
/* This should never happen since all other codes should be
handled in the switch statement. */
if ((symbol < W_box) || (symbol > W_last_pointtype))
break;
/* Calculate index, instead of an ugly long switch statement;
Depends on definition of commands in wgnuplib.h.
*/
index = symbol - 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 = xofs + htic * pointshapes[shape][i * 2] + 0.5;
p[i].y = yofs + vtic * pointshapes[shape][i * 2 + 1] + 0.5;
}
if (filled) {
/* Filled polygon */
SelectObject(dc, lpgw->hsolid);
Polygon(dc, p, i);
SelectObject(dc, lpgw->hapen);
} else {
/* Outline polygon */
p[i].x = p[0].x;
p[i].y = p[0].y;
SelectObject(dc, lpgw->hsolid);
Polyline(dc, p, i + 1);
SelectObject(dc, lpgw->hapen);
dot(dc, xofs, yofs);
}
} /* default case */
} /* switch (point symbol) */
if (ps_caching) {
/* copy memory bitmap to screen */
TransparentBlt(hdc, xdash - xofs, ydash - yofs, 2*htic+2, 2*vtic+2,
dc, 0, 0, 2*htic+2, 2*vtic+2, 0x00ffffff);
/* partial clean up */
SelectObject(dc, old_brush);
SelectObject(dc, old_pen);
}
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? */
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;
}
} /* while (ngwop < lpgw->nGWOP) */
/* cleanup */
if (ps_caching && (cb_memdc != NULL)) {
SelectObject(cb_memdc, cb_old_bmp);
DeleteObject(cb_membmp);
DeleteDC(cb_memdc);
cb_memdc = NULL;
}
if (lpgw->hcolorbrush) {
SelectObject(hdc, GetStockObject(BLACK_BRUSH));
DeleteObject(lpgw->hcolorbrush);
lpgw->hcolorbrush = NULL;
}
LocalFreePtr(ppt);
}
/* ================================== */
/* save graph windows as enhanced metafile
* The code in here is very similar to what CopyClip does...
*/
static void
SaveAsEMF(LPGW lpgw)
{
static OPENFILENAME Ofn;
static TCHAR lpstrCustomFilter[256] = { '\0' };
static TCHAR lpstrFileName[MAX_PATH] = { '\0' };
static TCHAR lpstrFileTitle[MAX_PATH] = { '\0' };
HWND hwnd = lpgw->hWndGraph;
Ofn.lStructSize = sizeof(OPENFILENAME);
Ofn.hwndOwner = hwnd;
Ofn.lpstrInitialDir = NULL;
#ifdef HAVE_GDIPLUS
Ofn.lpstrFilter = TEXT("Enhanced Metafile (*.emf)\0*.emf\0Enhanced Metafile+ (*.emf)\0*.emf\0");
#else
Ofn.lpstrFilter = TEXT("Enhanced Metafile (*.emf)\0*.emf\0");
#endif
Ofn.lpstrCustomFilter = lpstrCustomFilter;
Ofn.nMaxCustFilter = 255;
/* Direct2D cannot do EMF. Fall back to GDI+ instead. */
Ofn.nFilterIndex = (lpgw->gdiplus || lpgw->d2d ? 2 : 1);
Ofn.lpstrFile = lpstrFileName;
Ofn.nMaxFile = MAX_PATH;
Ofn.lpstrFileTitle = lpstrFileTitle;
Ofn.nMaxFileTitle = MAX_PATH;
Ofn.lpstrInitialDir = NULL;
Ofn.lpstrTitle = NULL;
Ofn.Flags = OFN_OVERWRITEPROMPT | OFN_NOREADONLYRETURN | OFN_NOCHANGEDIR;
Ofn.lpstrDefExt = TEXT("emf");
if (GetSaveFileName(&Ofn) != 0) {
RECT rect, mfrect;
HDC hdc;
HENHMETAFILE hemf;
HDC hmf;
/* get the context */
hdc = GetDC(hwnd);
GetPlotRect(lpgw, &rect);
rect.right -= rect.left;
rect.bottom -= rect.top;
rect.left = rect.top = 0;
GetPlotRectInMM(lpgw, &mfrect, hdc);
switch (Ofn.nFilterIndex) {
case 1: /* GDI Enhanced Metafile (EMF) */
hmf = CreateEnhMetaFile(hdc, Ofn.lpstrFile, &mfrect, NULL);
drawgraph(lpgw, hmf, &rect);
hemf = CloseEnhMetaFile(hmf);
DeleteEnhMetaFile(hemf);
break;
#ifdef HAVE_GDIPLUS
case 2: {/* GDI+ Enhanced Metafile (EMF+) */
#ifndef UNICODE
LPWSTR wfile = UnicodeText(Ofn.lpstrFile, S_ENC_DEFAULT);
metafile_gdiplus(lpgw, hdc, &rect, wfile);
free(wfile);
#else
metafile_gdiplus(lpgw, hdc, &rect, Ofn.lpstrFile);
#endif
break;
}
#endif
}
ReleaseDC(hwnd, hdc);
}
}
/* ================================== */
/* 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 bitmap is available */
HBITMAP oldbmp = (HBITMAP) SelectObject(mem, bitmap);
BitBlt(mem, 0, 0, rect.right - rect.left,
rect.bottom - rect.top, hdc, rect.left,
rect.top, SRCCOPY);
SelectObject(mem, oldbmp);
} else {
MessageBeep(MB_ICONHAND);
MessageBox(hwnd, TEXT("Insufficient memory to copy to 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.
*/
rect.right -= rect.left;
rect.bottom -= rect.top;
rect.left = rect.top = 0;
#ifdef HAVE_GDIPLUS
if (lpgw->gdiplus) {
hemf = clipboard_gdiplus(lpgw, hdc, &rect);
} else
#endif
#if defined(HAVE_GDIPLUS) && defined(HAVE_D2D)
/* Direct2D cannot do EMF. Fall back to GDI+. */
if (lpgw->d2d) {
hemf = clipboard_gdiplus(lpgw, hdc, &rect);
} else
#endif
{
/* make copy of window's main status struct for modification */
GW gwclip = *lpgw;
gwclip.hfonth = gwclip.hfontv = 0;
MakePens(&gwclip, hdc);
MakeFonts(&gwclip, &rect, hdc);
GetPlotRectInMM(lpgw, &mfrect, hdc);
hmf = CreateEnhMetaFile(hdc, NULL, &mfrect, NULL);
drawgraph(&gwclip, hmf, &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();
if (hemf)
SetClipboardData(CF_ENHMETAFILE, hemf);
else
fprintf(stderr, "Error: no metafile data available.\n");
if (bitmap)
SetClipboardData(CF_BITMAP, bitmap);
else
fprintf(stderr, "Error: no bitmap data available.\n");
CloseClipboard();
ReleaseDC(hwnd, hdc);
DeleteEnhMetaFile(hemf);
}
/* copy graph window to printer */
static void
CopyPrint(LPGW lpgw)
{
DOCINFO docInfo;
HDC printer;
HANDLE printerHandle;
PRINTDLGEX pd;
DEVNAMES * pDevNames;
DEVMODE * pDevMode;
LPCTSTR szDriver, szDevice, szOutput;
HWND hwnd = lpgw->hWndGraph;
RECT rect;
GP_PRINT pr;
PROPSHEETPAGE psp;
HPROPSHEETPAGE hpsp;
HDC hdc;
/* Print Property Sheet Dialog */
memset(&pr, 0, sizeof(pr));
GetPlotRect(lpgw, &rect);
hdc = GetDC(hwnd);
pr.pdef.x = MulDiv(rect.right - rect.left, 254, 10 * GetDeviceCaps(hdc, LOGPIXELSX));
pr.pdef.y = MulDiv(rect.bottom - rect.top, 254, 10 * GetDeviceCaps(hdc, LOGPIXELSY));
pr.psize.x = -1; /* will be initialised to paper size whenever the printer driver changes */
pr.psize.y = -1;
ReleaseDC(hwnd, hdc);
psp.dwSize = sizeof(PROPSHEETPAGE);
psp.dwFlags = PSP_USETITLE;
psp.hInstance = lpgw->hInstance;
psp.pszTemplate = TEXT("PrintSizeDlgBox"); //MAKEINTRESOURCE(DLG_FONT);
psp.pszIcon = NULL; // MAKEINTRESOURCE(IDI_FONT);
psp.pfnDlgProc = PrintSizeDlgProc;
psp.pszTitle = TEXT("Layout");
psp.lParam = (LPARAM) ≺
psp.pfnCallback = NULL;
hpsp = CreatePropertySheetPage(&psp);
memset(&pd, 0, sizeof(pd));
pd.lStructSize = sizeof(pd);
pd.hwndOwner = hwnd;
pd.Flags = PD_NOPAGENUMS | PD_NOSELECTION | PD_NOCURRENTPAGE | PD_USEDEVMODECOPIESANDCOLLATE;
pd.hDevNames = hDevNames;
pd.hDevMode = hDevMode;
pd.nCopies = 1;
pd.nPropertyPages = 1;
pd.lphPropertyPages = &hpsp;
pd.nStartPage = START_PAGE_GENERAL;
pd.lpCallback = PrintingCallbackCreate(&pr);
/* remove the lower part of the "general" property sheet */
pd.lpPrintTemplateName = TEXT("PrintDlgExEmpty");
pd.hInstance = graphwin->hInstance;
pd.Flags |= PD_ENABLEPRINTTEMPLATE;
if (PrintDlgEx(&pd) != S_OK) {
DWORD error = CommDlgExtendedError();
if (error != 0)
fprintf(stderr, "\nError: Opening the print dialog failed with error code %04x.\n", error);
PrintingCallbackFree(pd.lpCallback);
return;
}
PrintingCallbackFree(pd.lpCallback);
if (pd.dwResultAction != PD_RESULT_PRINT)
return;
/* See http://support.microsoft.com/kb/240082 */
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 but preserve them for the next time
GlobalFree(pd.hDevMode);
GlobalFree(pd.hDevNames);
*/
hDevNames = pd.hDevNames;
hDevMode = pd.hDevMode;
if (printer == NULL)
return; /* abort */
/* Print Size Dialog results */
if (pr.psize.x < 0) {
/* apply default values */
pr.psize.x = pr.pdef.x;
pr.psize.y = pr.pdef.y;
}
rect.left = MulDiv(pr.poff.x * 10, GetDeviceCaps(printer, LOGPIXELSX), 254);
rect.top = MulDiv(pr.poff.y * 10, GetDeviceCaps(printer, LOGPIXELSY), 254);
rect.right = rect.left + MulDiv(pr.psize.x * 10, GetDeviceCaps(printer, LOGPIXELSX), 254);
rect.bottom = rect.top + MulDiv(pr.psize.y * 10, GetDeviceCaps(printer, LOGPIXELSY), 254);
pr.hdcPrn = printer;
PrintRegister(&pr);
EnableWindow(hwnd, FALSE);
pr.bUserAbort = FALSE;
pr.szTitle = lpgw->Title;
pr.hDlgPrint = CreateDialogParam(hdllInstance, TEXT("CancelDlgBox"),
hwnd, PrintDlgProc, (LPARAM) &pr);
SetAbortProc(printer, PrintAbortProc);
SetWindowLongPtr(GetDlgItem(pr.hDlgPrint, CANCEL_PROGRESS), GWL_STYLE, WS_CHILD | WS_VISIBLE | PBS_MARQUEE);
SendMessage(GetDlgItem(pr.hDlgPrint, CANCEL_PROGRESS), PBM_SETMARQUEE, 1, 0);
#ifdef HAVE_GDIPLUS
#ifndef HAVE_D2D11
if (lpgw->gdiplus || lpgw->d2d)
#else
if (lpgw->gdiplus)
#endif
OpenPrinter((LPTSTR) szDevice, &printerHandle, NULL);
#endif
memset(&docInfo, 0, sizeof(DOCINFO));
docInfo.cbSize = sizeof(DOCINFO);
docInfo.lpszDocName = lpgw->Title;
if (StartDoc(printer, &docInfo) > 0 && StartPage(printer) > 0) {
#ifdef HAVE_GDIPLUS
#ifndef HAVE_D2D11
if (lpgw->gdiplus || lpgw->d2d) {
#else
if (lpgw->gdiplus) {
#endif
/* Print using GDI+ */
print_gdiplus(lpgw, printer, printerHandle, &rect);
} else
#endif
{
SetMapMode(printer, MM_TEXT);
SetBkMode(printer, OPAQUE);
DestroyFonts(lpgw);
MakeFonts(lpgw, &rect, printer);
DestroyPens(lpgw);
MakePens(lpgw, printer);
drawgraph(lpgw, printer, &rect);
hdc = GetDC(hwnd);
DestroyFonts(lpgw);
MakeFonts(lpgw, &rect, hdc);
ReleaseDC(hwnd, hdc);
}
if (EndPage(printer) <= 0) {
fputs("Error when finalising the print page. Aborting.\n", stderr);
AbortDoc(printer);
} else if (EndDoc(printer) <= 0) {
fputs("Error: Could not end printer document.\n", stderr);
}
} else {
fputs("Error: Unable to start printer document.\n", stderr);
}
if (!pr.bUserAbort) {
EnableWindow(hwnd, TRUE);
DestroyWindow(pr.hDlgPrint);
}
#ifdef HAVE_GDIPLUS
if (printerHandle != NULL)
ClosePrinter(printerHandle);
#endif
if (printer != NULL)
DeleteDC(printer);
PrintUnregister(&pr);
/* make certain that the screen pen set is restored */
SendMessage(lpgw->hWndGraph, WM_COMMAND, M_REBUILDTOOLS, 0L);
}
/* ================================== */
/* INI file stuff */
static void
WriteGraphIni(LPGW lpgw)
{
RECT rect;
LPTSTR file = lpgw->IniFile;
LPTSTR section = lpgw->IniSection;
TCHAR profile[80];
UINT dpi;
#ifdef WIN_CUSTOM_PENS
int i;
#endif
if ((file == NULL) || (section == NULL))
return;
/* Only save window size and position for standalone graph windows. */
if (!lpgw->bDocked) {
if (IsIconic(lpgw->hWndGraph))
ShowWindow(lpgw->hWndGraph, SW_SHOWNORMAL);
/* Rescale window size to 96dpi. */
GetWindowRect(lpgw->hWndGraph, &rect);
dpi = GetDPI();
wsprintf(profile, TEXT("%d %d"), MulDiv(rect.left, 96, dpi), MulDiv(rect.top, 96, dpi));
WritePrivateProfileString(section, TEXT("GraphOrigin"), profile, file);
if (lpgw->Canvas.x != 0) {
wsprintf(profile, TEXT("%d %d"), MulDiv(lpgw->Canvas.x, 96, dpi), MulDiv(lpgw->Canvas.y, 96, dpi));
WritePrivateProfileString(section, TEXT("GraphSize"), profile, file);
} else if (lpgw->Size.x != CW_USEDEFAULT) {
wsprintf(profile, TEXT("%d %d"), MulDiv(lpgw->Size.x - lpgw->Decoration.x, 96, dpi), MulDiv(lpgw->Size.y - lpgw->Decoration.y, 96, dpi));
WritePrivateProfileString(section, TEXT("GraphSize"), profile, file);
}
}
wsprintf(profile, TEXT("%s,%d"), lpgw->deffontname, lpgw->deffontsize);
WritePrivateProfileString(section, TEXT("GraphFont"), profile, file);
_tcscpy(WIN_inifontname, lpgw->deffontname);
WIN_inifontsize = lpgw->deffontsize;
wsprintf(profile, TEXT("%d"), lpgw->color);
WritePrivateProfileString(section, TEXT("GraphColor"), profile, file);
wsprintf(profile, TEXT("%d"), lpgw->graphtotop);
WritePrivateProfileString(section, TEXT("GraphToTop"), profile, file);
wsprintf(profile, TEXT("%d"), lpgw->oversample);
WritePrivateProfileString(section, TEXT("GraphGDI+Oversampling"), profile, file);
// FIXME: Do not default to Direct2D just yet.
#if 1
wsprintf(profile, TEXT("%d"), lpgw->gdiplus || lpgw->d2d);
WritePrivateProfileString(section, TEXT("GraphGDI+"), profile, file);
#else
wsprintf(profile, TEXT("%d"), lpgw->gdiplus);
WritePrivateProfileString(section, TEXT("GraphGDI+"), profile, file);
wsprintf(profile, TEXT("%d"), lpgw->d2d);
WritePrivateProfileString(section, TEXT("GraphD2D"), profile, file);
#endif
wsprintf(profile, TEXT("%d"), lpgw->antialiasing);
WritePrivateProfileString(section, TEXT("GraphAntialiasing"), profile, file);
wsprintf(profile, TEXT("%d"), lpgw->polyaa);
WritePrivateProfileString(section, TEXT("GraphPolygonAA"), profile, file);
wsprintf(profile, TEXT("%d"), lpgw->fastrotation);
WritePrivateProfileString(section, TEXT("GraphFastRotation"), profile, file);
wsprintf(profile, TEXT("%d %d %d"),GetRValue(lpgw->background),
GetGValue(lpgw->background), GetBValue(lpgw->background));
WritePrivateProfileString(section, TEXT("GraphBackground"), profile, file);
#ifdef WIN_CUSTOM_PENS
/* now save pens */
for (i = 0; i < WGNUMPENS + 2; i++) {
TCHAR entry[32];
LPLOGPEN pc;
LPLOGPEN pm;
if (i == 0)
_tcscpy(entry, TEXT("Border"));
else if (i == 1)
_tcscpy(entry, TEXT("Axis"));
else
wsprintf(entry, TEXT("Line%d"), i - 1);
pc = &lpgw->colorpen[i];
pm = &lpgw->monopen[i];
wsprintf(profile, TEXT("%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);
}
#endif
}
LPTSTR
GraphDefaultFont(void)
{
if (GetACP() == 932) /* Japanese Shift-JIS */
return TEXT(WINJPFONT);
else
return TEXT(WINFONT);
}
static void
ReadGraphIni(LPGW lpgw)
{
LPTSTR file = lpgw->IniFile;
LPTSTR section = lpgw->IniSection;
TCHAR profile[81];
LPTSTR p;
int r, g, b;
BOOL bOKINI;
UINT dpi;
#ifdef WIN_CUSTOM_PENS
int i;
int colorstyle, monostyle;
#endif
bOKINI = (file != NULL) && (section != NULL);
if (!bOKINI)
profile[0] = '\0';
if (bOKINI)
GetPrivateProfileString(section, TEXT("GraphOrigin"), TEXT(""), 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, TEXT("GraphSize"), TEXT(""), 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;
/* Saved size and origin are normalised to 96dpi. */
dpi = GetDPI();
if (lpgw->Origin.x != CW_USEDEFAULT)
lpgw->Origin.x = MulDiv(lpgw->Origin.x, dpi, 96);
if (lpgw->Origin.y != CW_USEDEFAULT)
lpgw->Origin.y = MulDiv(lpgw->Origin.y, dpi, 96);
if (lpgw->Size.x != CW_USEDEFAULT)
lpgw->Size.x = MulDiv(lpgw->Size.x, dpi, 96);
if (lpgw->Size.y != CW_USEDEFAULT)
lpgw->Size.y = MulDiv(lpgw->Size.y, dpi, 96);
if ((lpgw->Size.x != CW_USEDEFAULT) && (lpgw->Size.y != CW_USEDEFAULT)) {
lpgw->Canvas.x = lpgw->Size.x;
lpgw->Canvas.y = lpgw->Size.y;
}
if (bOKINI)
GetPrivateProfileString(section, TEXT("GraphFont"), TEXT(""), profile, 80, file);
{
LPTSTR size = _tcsrchr(profile, TEXT(','));
if (size) {
*size++ = '\0';
if ((p = GetInt(size, (LPINT)&lpgw->fontsize)) == NULL)
lpgw->fontsize = WINFONTSIZE;
}
_tcscpy(lpgw->fontname, profile);
if (lpgw->fontsize == 0)
lpgw->fontsize = WINFONTSIZE;
if (!(*lpgw->fontname))
_tcscpy(lpgw->fontname, GraphDefaultFont());
/* set current font as default font */
_tcscpy(lpgw->deffontname, lpgw->fontname);
lpgw->deffontsize = lpgw->fontsize;
_tcscpy(WIN_inifontname, lpgw->deffontname);
WIN_inifontsize = lpgw->deffontsize;
}
if (bOKINI)
GetPrivateProfileString(section, TEXT("GraphColor"), TEXT(""), profile, 80, file);
if ((p = GetInt(profile, (LPINT)&lpgw->color)) == NULL)
lpgw->color = TRUE;
if (bOKINI)
GetPrivateProfileString(section, TEXT("GraphToTop"), TEXT(""), profile, 80, file);
if ((p = GetInt(profile, (LPINT)&lpgw->graphtotop)) == NULL)
lpgw->graphtotop = TRUE;
if (bOKINI)
GetPrivateProfileString(section, TEXT("GraphGDI+Oversampling"), TEXT(""), profile, 80, file);
if ((p = GetInt(profile, (LPINT)&lpgw->oversample)) == NULL)
lpgw->oversample = TRUE;
#ifdef HAVE_GDIPLUS
if (bOKINI)
GetPrivateProfileString(section, TEXT("GraphGDI+"), TEXT(""), profile, 80, file);
if ((p = GetInt(profile, (LPINT)&lpgw->gdiplus)) == NULL)
lpgw->gdiplus = TRUE;
#endif
#ifdef HAVE_D2D
if (bOKINI)
GetPrivateProfileString(section, TEXT("GraphD2D"), TEXT(""), profile, 80, file);
// FIXME: Do not default to Direct2D for now
if ((p = GetInt(profile, (LPINT)&lpgw->d2d)) == NULL)
lpgw->d2d = FALSE;
if (lpgw->d2d)
lpgw->gdiplus = FALSE;
#endif
if (bOKINI)
GetPrivateProfileString(section, TEXT("GraphAntialiasing"), TEXT(""), profile, 80, file);
if ((p = GetInt(profile, (LPINT)&lpgw->antialiasing)) == NULL)
lpgw->antialiasing = TRUE;
if (bOKINI)
GetPrivateProfileString(section, TEXT("GraphPolygonAA"), TEXT(""), profile, 80, file);
if ((p = GetInt(profile, (LPINT)&lpgw->polyaa)) == NULL)
lpgw->polyaa = TRUE;
if (bOKINI)
GetPrivateProfileString(section, TEXT("GraphFastRotation"), TEXT(""), profile, 80, file);
if ((p = GetInt(profile, (LPINT)&lpgw->fastrotation)) == NULL)
lpgw->fastrotation = TRUE;
lpgw->background = RGB(255,255,255);
if (bOKINI)
GetPrivateProfileString(section, TEXT("GraphBackground"), TEXT(""), 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);
#ifdef WIN_CUSTOM_PENS
StorePen(lpgw, 0, RGB(0,0,0), PS_SOLID, PS_SOLID);
if (bOKINI)
GetPrivateProfileString(section, TEXT("Border"), TEXT(""), 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, TEXT("Axis"), TEXT(""), 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++) {
COLORREF ref;
TCHAR entry[32];
ref = wginitcolor[i % WGDEFCOLOR];
colorstyle = wginitstyle[(i / WGDEFCOLOR) % WGDEFSTYLE];
monostyle = wginitstyle[i % WGDEFSTYLE];
StorePen(lpgw, i + 2, ref, colorstyle, monostyle);
wsprintf(entry, TEXT("Line%d"), i + 1);
if (bOKINI)
GetPrivateProfileString(section, entry, TEXT(""), 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);
}
#endif
}
/* ================================== */
/* Hypertext support functions */
void
add_tooltip(LPGW lpgw, PRECT rect, LPWSTR text)
{
int idx = lpgw->numtooltips;
/* Extend buffer, if necessary */
if (lpgw->numtooltips >= lpgw->maxtooltips) {
lpgw->maxtooltips += 10;
lpgw->tooltips = (struct tooltips *) realloc(lpgw->tooltips, lpgw->maxtooltips * sizeof(struct tooltips));
}
rect->top += lpgw->ToolbarHeight;
rect->bottom += lpgw->ToolbarHeight;
lpgw->tooltips[idx].rect = *rect;
lpgw->tooltips[idx].text = text;
lpgw->numtooltips++;
if (!lpgw->hTooltip) {
TOOLINFO ti = { 0 };
/* Create new tooltip. */
HWND hwnd = CreateWindowEx(WS_EX_TOPMOST, TOOLTIPS_CLASS, NULL,
WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
lpgw->hWndGraph, NULL, lpgw->hInstance, NULL);
lpgw->hTooltip = hwnd;
/* Associate the tooltip with the rect area.*/
ti.cbSize = sizeof(TOOLINFO);
ti.uFlags = TTF_SUBCLASS;
ti.hwnd = lpgw->hWndGraph;
ti.hinst = lpgw->hInstance;
ti.uId = 0;
ti.rect = * rect;
ti.lpszText = (LPTSTR) text;
SendMessage(hwnd, TTM_ADDTOOLW, 0, (LPARAM) (LPTOOLINFO) &ti);
SendMessage(hwnd, TTM_SETDELAYTIME, TTDT_INITIAL, (LPARAM) 100);
SendMessage(hwnd, TTM_SETDELAYTIME, TTDT_RESHOW, (LPARAM) 100);
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
}
void
clear_tooltips(LPGW lpgw)
{
int i;
for (i = 0; i < lpgw->numtooltips; i++)
free(lpgw->tooltips[i].text);
lpgw->numtooltips = 0;
lpgw->maxtooltips = 0;
free(lpgw->tooltips);
lpgw->tooltips = NULL;
}
static void
track_tooltip(LPGW lpgw, int x, int y)
{
static POINT p = {0, 0};
int i;
/* only update if mouse position changed */
if ((p.x == x) && (p.y == y))
return;
p.x = x; p.y = y;
for (i = 0; i < lpgw->numtooltips; i++) {
if (PtInRect(&(lpgw->tooltips[i].rect), p)) {
TOOLINFO ti = { 0 };
int width;
ti.cbSize = sizeof(TOOLINFO);
ti.hwnd = lpgw->hWndGraph;
ti.hinst = lpgw->hInstance;
ti.rect = lpgw->tooltips[i].rect;
ti.lpszText = (LPTSTR) lpgw->tooltips[i].text;
SendMessage(lpgw->hTooltip, TTM_NEWTOOLRECT, 0, (LPARAM) (LPTOOLINFO) &ti);
SendMessage(lpgw->hTooltip, TTM_UPDATETIPTEXTW, 0, (LPARAM) (LPTOOLINFO) &ti);
/* Multi-line tooltip. */
width = (wcschr(lpgw->tooltips[i].text, L'\n') == NULL) ? -1 : 200;
SendMessage(lpgw->hTooltip, TTM_SETMAXTIPWIDTH, 0, (LPARAM) (INT) width);
}
}
}
/* ================================== */
#ifdef WIN_CUSTOM_PENS
/* 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 {
LONG_PTR 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;
#endif
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);
}
memset(&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;
}
#ifdef WIN_CUSTOM_PENS
/* 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 */
INT_PTR CALLBACK
LineStyleDlgProc(HWND hdlg, UINT wmsg, WPARAM wparam, LPARAM lparam)
{
TCHAR buf[16];
LPLS lpls;
int i;
UINT pen;
LPLOGPEN plpm, plpc;
lpls = (LPLS) GetWindowLongPtr(GetParent(hdlg), 4);
switch (wmsg) {
case WM_INITDIALOG:
pen = 2;
for (i = 0; i < WGNUMPENS + 2; i++) {
if (i == 0)
_tcscpy(buf, TEXT("Border"));
else if (i == 1)
_tcscpy(buf, TEXT("Axis"));
else
wsprintf(buf, TEXT("Line%d"), i - 1);
SendDlgItemMessage(hdlg, LS_LINENUM, LB_ADDSTRING, 0,
(LPARAM)(buf));
}
SendDlgItemMessage(hdlg, LS_LINENUM, LB_SETCURSEL, pen, 0L);
SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
(LPARAM)(TEXT("Solid")));
SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
(LPARAM)(TEXT("Dash")));
SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
(LPARAM)(TEXT("Dot")));
SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
(LPARAM)(TEXT("DashDot")));
SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
(LPARAM)(TEXT("DashDotDot")));
plpm = &lpls->monopen[pen];
SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_SETCURSEL,
plpm->lopnStyle, 0L);
wsprintf(buf, TEXT("%d"), plpm->lopnWidth.x);
SetDlgItemText(hdlg, LS_MONOWIDTH, buf);
SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
(LPARAM)(TEXT("Solid")));
SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
(LPARAM)(TEXT("Dash")));
SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
(LPARAM)(TEXT("Dot")));
SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
(LPARAM)(TEXT("DashDot")));
SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
(LPARAM)(TEXT("DashDotDot")));
plpc = &lpls->colorpen[pen];
SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_SETCURSEL,
plpc->lopnStyle, 0L);
wsprintf(buf, TEXT("%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, TEXT("%d"), plpm->lopnWidth.x);
SetDlgItemText(hdlg, LS_MONOWIDTH, buf);
SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_SETCURSEL,
plpm->lopnStyle, 0L);
wsprintf(buf, TEXT("%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, TEXT("%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, TEXT("%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, TEXT("%d"), plpm->lopnWidth.x);
SetDlgItemText(hdlg, LS_MONOWIDTH, buf);
SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_SETCURSEL,
plpm->lopnStyle, 0L);
wsprintf(buf, TEXT("%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;
}
/* GetWindowLongPtr(hwnd, 4) must be available for use */
static BOOL
LineStyle(LPGW lpgw)
{
BOOL status = FALSE;
LS ls;
SetWindowLongPtr(lpgw->hWndGraph, 4, (LONG_PTR) &ls);
memcpy(&ls.colorpen, &lpgw->colorpen, (WGNUMPENS + 2) * sizeof(LOGPEN));
memcpy(&ls.monopen, &lpgw->monopen, (WGNUMPENS + 2) * sizeof(LOGPEN));
if (DialogBox(hdllInstance, TEXT("LineStyleDlgBox"), lpgw->hWndGraph, LineStyleDlgProc) == IDOK) {
memcpy(&lpgw->colorpen, &ls.colorpen, (WGNUMPENS + 2) * sizeof(LOGPEN));
memcpy(&lpgw->monopen, &ls.monopen, (WGNUMPENS + 2) * sizeof(LOGPEN));
status = TRUE;
}
SetWindowLongPtr(lpgw->hWndGraph, 4, 0L);
return status;
}
#endif /* WIN_CUSTOM_PENS */
#ifdef USE_MOUSE
/* ================================== */
/* helper functions: wrapper around gp_exec_event and DrawZoomBox. */
static void
Wnd_exec_event(LPGW lpgw, LPARAM lparam, char type, int par1)
{
static unsigned long lastTimestamp = 0;
unsigned long thisTimestamp = GetMessageTime();
int mx, my, par2;
TBOOLEAN old = FALSE;
if (type != GE_keypress) /* no timestamp for key events */
par2 = thisTimestamp - lastTimestamp;
else
par2 = 0;
/* map events from inactive graph windows */
if (lpgw != graphwin) {
switch (type) {
case GE_keypress:
type = GE_keypress_old;
old = TRUE;
break;
case GE_buttonpress:
type = GE_buttonpress_old;
old = TRUE;
break;
case GE_buttonrelease:
type = GE_buttonrelease_old;
old = TRUE;
break;
}
}
if ((term != NULL) && (strcmp(term->name, "windows") == 0) && ((lpgw == graphwin) || old)) {
GetMousePosViewport(lpgw, &mx, &my);
gp_exec_event(type, mx, my, par1, par2, 0);
lastTimestamp = thisTimestamp;
}
/* FIXME: IMHO changing paused_for_mouse in terminal code is bad design. */
/* end pause mouse? */
if ((type == GE_buttonrelease) && (paused_for_mouse & PAUSE_CLICK) &&
(((par1 == 1) && (paused_for_mouse & PAUSE_BUTTON1)) ||
((par1 == 2) && (paused_for_mouse & PAUSE_BUTTON2)) ||
((par1 == 3) && (paused_for_mouse & PAUSE_BUTTON3)))) {
paused_for_mouse = 0;
}
if ((type == GE_keypress) && (paused_for_mouse & PAUSE_KEYSTROKE) && (par1 != NUL)) {
paused_for_mouse = 0;
}
}
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 */
/* ================================== */
static void
GraphUpdateMenu(LPGW lpgw)
{
CheckMenuItem(lpgw->hPopMenu, M_COLOR, MF_BYCOMMAND |
(lpgw->color ? MF_CHECKED : MF_UNCHECKED));
if (lpgw->gdiplus)
CheckMenuRadioItem(lpgw->hPopMenu, M_GDI, M_D2D, M_GDIPLUS, MF_BYCOMMAND);
else if (lpgw->d2d)
CheckMenuRadioItem(lpgw->hPopMenu, M_GDI, M_D2D, M_D2D, MF_BYCOMMAND);
else
CheckMenuRadioItem(lpgw->hPopMenu, M_GDI, M_D2D, M_GDI, MF_BYCOMMAND);
#if defined(HAVE_GDIPLUS) || defined(HAVE_D2D)
if (lpgw->gdiplus || lpgw->d2d) {
EnableMenuItem(lpgw->hPopMenu, M_ANTIALIASING, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(lpgw->hPopMenu, M_POLYAA, MF_BYCOMMAND | MF_ENABLED);
EnableMenuItem(lpgw->hPopMenu, M_OVERSAMPLE, MF_BYCOMMAND | MF_ENABLED);
} else {
EnableMenuItem(lpgw->hPopMenu, M_ANTIALIASING, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(lpgw->hPopMenu, M_POLYAA, MF_BYCOMMAND | MF_GRAYED);
EnableMenuItem(lpgw->hPopMenu, M_OVERSAMPLE, MF_BYCOMMAND | MF_GRAYED);
}
EnableMenuItem(lpgw->hPopMenu, M_FASTROTATE, MF_BYCOMMAND | (lpgw->gdiplus ? MF_ENABLED : MF_DISABLED));
CheckMenuItem(lpgw->hPopMenu, M_ANTIALIASING, MF_BYCOMMAND |
((lpgw->gdiplus || lpgw->d2d) && lpgw->antialiasing ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(lpgw->hPopMenu, M_OVERSAMPLE, MF_BYCOMMAND |
((lpgw->gdiplus || lpgw->d2d) && lpgw->oversample ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(lpgw->hPopMenu, M_FASTROTATE, MF_BYCOMMAND |
(lpgw->gdiplus && lpgw->fastrotation ? MF_CHECKED : MF_UNCHECKED));
CheckMenuItem(lpgw->hPopMenu, M_POLYAA, MF_BYCOMMAND |
((lpgw->gdiplus || lpgw->d2d) && lpgw->polyaa ? MF_CHECKED : MF_UNCHECKED));
#endif
CheckMenuItem(lpgw->hPopMenu, M_GRAPH_TO_TOP, MF_BYCOMMAND |
(lpgw->graphtotop ? MF_CHECKED : MF_UNCHECKED));
}
/* 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)GetWindowLongPtr(hwnd, 0);
#ifdef USE_MOUSE
/* mouse events first */
if ((lpgw == graphwin) && mouse_setting.on) {
switch (message) {
case WM_MOUSEMOVE:
SetCursor(hptrCurrent);
if (zoombox.on) {
Wnd_refresh_zoombox(lpgw, lParam);
}
if (ruler.on && ruler_lineto.on) {
Wnd_refresh_ruler_lineto(lpgw, lParam);
}
/* track hypertexts */
track_tooltip(lpgw, GET_X_LPARAM(lParam), GET_Y_LPARAM(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;
/* need to set input focus to current graph */
if (lpgw->bDocked)
SetFocus(hwnd);
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]);
lpgw->buffervalid = FALSE;
GetClientRect(hwnd, &rect);
InvalidateRect(hwnd, &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;
case WM_MOUSEWHEEL: /* shige, BM : mouse wheel support */
case WM_MOUSEHWHEEL: {
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);
last_modifier_mask = modifier_mask;
}
if (message == WM_MOUSEWHEEL) {
Wnd_exec_event(lpgw, lParam, GE_buttonpress, zDelta > 0 ? 4 : 5);
Wnd_exec_event(lpgw, lParam, GE_buttonrelease, zDelta > 0 ? 4 : 5);
} else {
Wnd_exec_event(lpgw, lParam, GE_buttonpress, zDelta > 0 ? 6 : 7);
Wnd_exec_event(lpgw, lParam, GE_buttonrelease, zDelta > 0 ? 6 : 7);
}
return 0L;
}
case WM_LBUTTONUP:
Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 1);
return 0L;
case WM_RBUTTONUP:
Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 3);
return 0L;
case WM_MBUTTONUP:
Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 2);
return 0L;
} /* switch over mouse events */
}
#endif /* USE_MOUSE */
switch (message) {
#ifndef WGP_CONSOLE
case WM_SETFOCUS:
if (lpgw->bDocked) {
char status[100];
// register input focus
lpgw->lptw->hWndFocus = hwnd;
DrawFocusIndicator(lpgw);
if (lpgw != graphwin) {
sprintf(status, "(inactive, window number %i)", lpgw->Id);
UpdateStatusLine(lpgw, status);
}
}
break;
case WM_KILLFOCUS:
if (lpgw->bDocked) {
// remove focus indicator by enforcing a redraw
GetClientRect(hwnd, &rect);
InvalidateRect(hwnd, &rect, 1);
UpdateWindow(hwnd);
}
break;
#endif
case WM_SYSCOMMAND:
switch (LOWORD(wParam)) {
case M_GRAPH_TO_TOP:
case M_COLOR:
case M_OVERSAMPLE:
case M_GDI:
case M_GDIPLUS:
case M_D2D:
case M_ANTIALIASING:
case M_POLYAA:
case M_FASTROTATE:
case M_CHOOSE_FONT:
case M_COPY_CLIP:
case M_SAVE_AS_EMF:
case M_SAVE_AS_BITMAP:
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_SHOWNORMAL);
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) {
#ifndef WGP_CONSOLE
if (lpgw->bDocked)
SetFocus(lpgw->lptw->hWndText);
else
#endif
WinRaiseConsole();
return 0L;
}
#endif /* DISABLE_SPACE_RAISES_CONSOLE */
if (wParam == 'q') {
GraphClose(lpgw);
#ifndef WGP_CONSOLE
if (lpgw->bDocked) {
DockedUpdateLayout(lpgw->lptw);
SetFocus(lpgw->lptw->hWndText);
}
#endif
return 0L;
}
#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;
case VK_END:
/* use CTRL-END as break key */
ctrlc_flag = TRUE;
PostMessage(graphwin->hWndGraph, WM_NULL, 0, 0);
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:
ctrlc_flag = TRUE;
PostMessage(graphwin->hWndGraph, WM_NULL, 0, 0);
break;
} /* switch (wParam) */
return 0L;
#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_GDI:
lpgw->gdiplus = FALSE;
lpgw->d2d = FALSE;
SendMessage(hwnd, WM_COMMAND, M_REBUILDTOOLS, 0L);
return 0;
case M_GDIPLUS:
lpgw->gdiplus = TRUE;
lpgw->d2d = FALSE;
SendMessage(hwnd, WM_COMMAND, M_REBUILDTOOLS, 0L);
return 0;
case M_D2D:
lpgw->d2d = TRUE;
lpgw->gdiplus = FALSE;
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_FASTROTATE:
lpgw->fastrotation = !lpgw->fastrotation;
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_SAVE_AS_BITMAP:
#ifdef HAVE_GDIPLUS
SaveAsBitmap(lpgw);
#endif
return 0;
#ifdef WIN_CUSTOM_PENS
case M_LINESTYLE:
if (LineStyle(lpgw))
SendMessage(hwnd, WM_COMMAND, M_REBUILDTOOLS, 0L);
return 0;
#endif
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);
lpgw->buffervalid = FALSE;
GetClientRect(hwnd, &rect);
InvalidateRect(hwnd, &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:
GraphUpdateMenu(lpgw);
lpgw->buffervalid = FALSE;
DestroyPens(lpgw);
DestroyFonts(lpgw);
hdc = GetDC(hwnd);
MakePens(lpgw, hdc);
GetPlotRect(lpgw, &rect);
MakeFonts(lpgw, &rect, hdc);
ReleaseDC(hwnd, hdc);
GetClientRect(hwnd, &rect);
InvalidateRect(hwnd, &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);
lpgw->buffervalid = FALSE;
GetClientRect(hwnd, &rect);
InvalidateRect(hwnd, &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_LBUTTONDOWN:
/* need to set input focus to current graph */
if (lpgw->bDocked)
SetFocus(hwnd);
break;
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:
_tcscpy(lpttt->szText, TEXT("Copy graph to clipboard"));
break;
case M_PRINT:
_tcscpy(lpttt->szText, TEXT("Print graph"));
break;
case M_SAVE_AS_EMF:
_tcscpy(lpttt->szText, TEXT("Save graph as EMF"));
break;
case M_HIDEGRID:
_tcscpy(lpttt->szText, TEXT("Do not draw grid lines"));
break;
}
if ((idButton >= M_HIDEPLOT) && (idButton < (M_HIDEPLOT + MAXPLOTSHIDE))) {
unsigned index = (unsigned)idButton - (M_HIDEPLOT) + 1;
wsprintf(lpttt->szText, TEXT("Hide graph #%i"), index);
}
lpttt->uFlags |= TTF_DI_SETITEM;
return TRUE;
}
}
return FALSE;
case WM_CREATE:
lpgw = (LPGW) ((CREATESTRUCT *)lParam)->lpCreateParams;
SetWindowLongPtr(hwnd, 0, (LONG_PTR)lpgw);
lpgw->hWndGraph = hwnd;
#ifdef USE_MOUSE
LoadCursors(lpgw);
#endif
if (lpgw->lptw && (lpgw->lptw->DragPre != NULL) && (lpgw->lptw->DragPost != NULL))
DragAcceptFiles(hwnd, TRUE);
return 0;
case WM_ERASEBKGND:
return 1; /* we erase the background ourselves */
case WM_PAINT: {
HDC memdc = NULL;
HBITMAP oldbmp;
LONG width, height;
LONG wwidth, wheight;
RECT memrect;
RECT wrect;
GetPlotRect(lpgw, &rect);
#if defined(HAVE_D2D) && !defined(DCRENDERER)
if (lpgw->d2d) {
drawgraph_d2d(lpgw, hwnd, &rect);
ValidateRect(hwnd, NULL);
} else {
#endif
hdc = BeginPaint(hwnd, &ps);
SetMapMode(hdc, MM_TEXT);
SetBkMode(hdc, OPAQUE);
SetViewportExtEx(hdc, rect.right, rect.bottom, NULL);
/* Was the window resized? */
GetWindowRect(hwnd, &wrect);
wwidth = wrect.right - wrect.left;
wheight = wrect.bottom - wrect.top;
if ((lpgw->Size.x != wwidth) || (lpgw->Size.y != wheight)) {
DestroyFonts(lpgw);
MakeFonts(lpgw, &rect, hdc);
lpgw->buffervalid = FALSE;
}
/* 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;
memrect.top = 0;
memrect.bottom = height;
if (!lpgw->buffervalid || (lpgw->hBitmap == NULL)) {
BOOL save_aa;
if (lpgw->hBitmap != NULL)
DeleteObject(lpgw->hBitmap);
lpgw->hBitmap = CreateCompatibleBitmap(hdc, memrect.right, memrect.bottom);
oldbmp = (HBITMAP)SelectObject(memdc, lpgw->hBitmap);
/* Update window size */
lpgw->Size.x = wwidth;
lpgw->Size.y = wheight;
/* Temporarily switch off antialiasing during rotation (GDI+) */
save_aa = lpgw->antialiasing;
if (lpgw->rotating && lpgw->fastrotation)
lpgw->antialiasing = FALSE;
/* draw into memdc, then copy to hdc */
#ifdef HAVE_GDIPLUS
if (lpgw->gdiplus && !(lpgw->rotating && lpgw->fastrotation)) {
drawgraph_gdiplus(lpgw, memdc, &memrect);
} else {
#endif
#if defined(HAVE_D2D) && defined(DCRENDERER)
if (lpgw->d2d) {
drawgraph_d2d(lpgw, memdc, &memrect);
} else {
#endif
drawgraph(lpgw, memdc, &memrect);
#if defined(HAVE_D2D) && defined(DCRENDERER)
}
#endif
#ifdef HAVE_GDIPLUS
}
#endif
/* restore antialiasing */
lpgw->antialiasing = save_aa;
/* drawing by gnuplot still in progress... */
lpgw->buffervalid = !lpgw->locked;
} else {
oldbmp = (HBITMAP) SelectObject(memdc, lpgw->hBitmap);
}
if (lpgw->buffervalid) {
BitBlt(hdc, rect.left, rect.top, width, height, memdc, 0, 0, SRCCOPY);
}
/* select the old bitmap back into the device context */
if (memdc != NULL) {
SelectObject(memdc, oldbmp);
DeleteDC(memdc);
}
EndPaint(hwnd, &ps);
#if defined(HAVE_D2D) && !defined(DCRENDERER)
}
#endif
#ifndef WGP_CONSOLE
/* indicate input focus */
if (lpgw->bDocked && (GetFocus() == hwnd))
DrawFocusIndicator(lpgw);
#endif
#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 */
GetWindowRect(lpgw->hToolbar, &rect);
lpgw->ToolbarHeight = rect.bottom - rect.top;
}
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 */
lpgw->buffervalid = FALSE;
DestroyFonts(lpgw);
GetPlotRect(lpgw, &rect);
hdc = GetDC(hwnd);
MakeFonts(lpgw, &rect, hdc);
ReleaseDC(hwnd, hdc);
GetPlotRect(lpgw, &rect);
InvalidateRect(hwnd, &rect, 1);
UpdateWindow(hwnd);
}
}
// update internal variables
if (lpgw->Size.x == CW_USEDEFAULT) {
lpgw->Size.x = LOWORD(lParam);
lpgw->Size.y = HIWORD(lParam);
}
if (lpgw->Canvas.x != 0) {
GetPlotRect(lpgw, &rect);
lpgw->Canvas.x = rect.right - rect.left;
lpgw->Canvas.y = rect.bottom - rect.top;
}
break;
#ifndef WGP_CONSOLE
case WM_DROPFILES:
if (lpgw->lptw)
DragFunc(lpgw->lptw, (HDROP)wParam);
break;
#endif
case WM_DESTROY:
lpgw->buffervalid = FALSE;
DeleteObject(lpgw->hBitmap);
lpgw->hBitmap = NULL;
clear_tooltips(lpgw);
DestroyWindow(lpgw->hTooltip);
lpgw->hTooltip = NULL;
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);
}
static void
GraphChangeFont(LPGW lpgw, LPCTSTR 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 = (_tcscmp(lpgw->fontname, font) != 0) || (newfontsize != lpgw->fontsize);
} else {
remakefonts = (_tcscmp(lpgw->fontname, lpgw->deffontname) != 0) || (newfontsize != lpgw->fontsize);
}
remakefonts |= (lpgw->hfonth == 0);
if (remakefonts) {
lpgw->fontsize = newfontsize;
_tcscpy(lpgw->fontname, font_is_not_empty ? font : lpgw->deffontname);
SelectObject(hdc, GetStockObject(SYSTEM_FONT));
DestroyFonts(lpgw);
MakeFonts(lpgw, &rect, hdc);
}
}
/* close the terminal window */
void
win_close_terminal_window(LPGW lpgw)
{
if (GraphHasWindow(lpgw)) {
SendMessage(lpgw->hWndGraph, WM_CLOSE, 0L, 0L);
#ifndef WGP_CONSOLE
if (lpgw->bDocked)
DockedUpdateLayout(lpgw->lptw);
#endif
}
}
#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
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));
/* Once done with rotation we have to redraw with aa once more. */
if (lpgw->rotating && lpgw->fastrotation && lpgw->antialiasing) {
lpgw->rotating = FALSE;
GraphRedraw(lpgw);
}
break;
case 1: /* cursor during rotation */
SetCursor(hptrCurrent = hptrRotating);
lpgw->rotating = TRUE;
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
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
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
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));
// FIXME: Do we need to handle encodings here?
TextOutA(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);
}
#endif
/* ================================== */
/*
* Update the status line by the text; erase the previous text
*/
static void
UpdateStatusLine(LPGW lpgw, const char text[])
{
LPWSTR wtext;
if (lpgw == NULL)
return;
wtext = UnicodeText(text, encoding);
if (!lpgw->bDocked) {
if (lpgw->hStatusbar)
SendMessageW(lpgw->hStatusbar, SB_SETTEXTW, (WPARAM)0, (LPARAM)wtext);
} else {
if (lpgw->lptw != NULL && lpgw->lptw->hStatusbar)
SendMessageW(lpgw->lptw->hStatusbar, SB_SETTEXTW, (WPARAM)1, (LPARAM)wtext);
}
free(wtext);
}
/*
* Update the toolbar to reflect the current the number of active plots
*/
static void
UpdateToolbar(LPGW lpgw)
{
unsigned i;
if (lpgw->hToolbar == NULL)
return;
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);
}
}
}
}
/*
* Toggle active plots
*/
void
GraphModifyPlots(LPGW lpgw, unsigned int ops, int plotno)
{
int i;
TBOOLEAN changed = FALSE;
for (i = 0; i < GPMIN(lpgw->numplots, lpgw->maxhideplots); i++) {
if (plotno >= 0 && i != plotno)
continue;
switch (ops) {
case MODPLOTS_INVERT_VISIBILITIES:
lpgw->hideplot[i] = !lpgw->hideplot[i];
changed = TRUE;
SendMessage(lpgw->hToolbar, TB_CHECKBUTTON, M_HIDEPLOT + i, (LPARAM)lpgw->hideplot[i]);
break;
case MODPLOTS_SET_VISIBLE:
if (lpgw->hideplot[i] == TRUE) {
changed = TRUE;
SendMessage(lpgw->hToolbar, TB_CHECKBUTTON, M_HIDEPLOT + i, (LPARAM)FALSE);
}
lpgw->hideplot[i] = FALSE;
break;
case MODPLOTS_SET_INVISIBLE:
if (lpgw->hideplot[i] == FALSE) {
changed = TRUE;
SendMessage(lpgw->hToolbar, TB_CHECKBUTTON, M_HIDEPLOT + i, (LPARAM)TRUE);
}
lpgw->hideplot[i] = TRUE;
break;
}
}
if (changed) {
RECT rect;
lpgw->buffervalid = FALSE;
GetClientRect(lpgw->hWndGraph, &rect);
InvalidateRect(lpgw->hWndGraph, &rect, 1);
UpdateToolbar(lpgw);
UpdateWindow(lpgw->hWndGraph);
}
}
#ifdef USE_MOUSE
/* 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);
}
}
}
static void
DrawFocusIndicator(LPGW lpgw)
{
if (lpgw->bDocked) {
HDC hdc;
RECT rect;
GetPlotRect(lpgw, &rect);
hdc = GetDC(lpgw->hWndGraph);
SelectObject(hdc, GetStockObject(DC_PEN));
SelectObject(hdc, GetStockObject(NULL_BRUSH));
SetDCPenColor(hdc, RGB(0, 0, 128));
Rectangle(hdc, rect.left + 1, rect.top + 1, rect.right - 1, rect.bottom - 1);
ReleaseDC(lpgw->hWndGraph, hdc);
}
}
#endif /* USE_MOUSE */
/* eof wgraph.c */