Initial release of Maemo 5 port of gnuplot
[gnuplot] / src / win / wgraph.c
1 #ifndef lint
2 static char *RCSid() { return RCSid("$Id: wgraph.c,v 1.52.2.7 2009/03/23 23:03:25 sfeam Exp $"); }
3 #endif
4
5 /* GNUPLOT - win/wgraph.c */
6 /*[
7  * Copyright 1992, 1993, 1998, 2004   Maurice Castro, Russell Lang
8  *
9  * Permission to use, copy, and distribute this software and its
10  * documentation for any purpose with or without fee is hereby granted,
11  * provided that the above copyright notice appear in all copies and
12  * that both that copyright notice and this permission notice appear
13  * in supporting documentation.
14  *
15  * Permission to modify the software is granted, but not the right to
16  * distribute the complete modified source code.  Modifications are to
17  * be distributed as patches to the released version.  Permission to
18  * distribute binaries produced by compiling modified sources is granted,
19  * provided you
20  *   1. distribute the corresponding source modifications from the
21  *    released version in the form of a patch file along with the binaries,
22  *   2. add special version identification to distinguish your version
23  *    in addition to the base release version number,
24  *   3. provide your name and address as the primary contact for the
25  *    support of your modified version, and
26  *   4. retain our contact information in regard to use of the base
27  *    software.
28  * Permission to distribute the released version of the source code along
29  * with corresponding source modifications in the form of a patch file is
30  * granted with same provisions 2 through 4 for binary distributions.
31  *
32  * This software is provided "as is" without express or implied warranty
33  * to the extent permitted by applicable law.
34 ]*/
35
36 /*
37  * AUTHORS
38  *
39  *   Maurice Castro
40  *   Russell Lang
41  */
42
43 #define STRICT
44 #include <windows.h>
45 #include <windowsx.h>
46 #if WINVER >= 0x030a
47 #  include <commdlg.h>
48 #endif
49 #ifndef __MSC__
50 # include <mem.h>
51 #endif
52 #include <stdio.h>
53 #include <string.h>
54 #include "wgnuplib.h"
55 #include "wresourc.h"
56 #include "wcommon.h"
57 #include "term_api.h"         /* for enum JUSTIFY */
58 #ifdef USE_MOUSE
59 # include "gpexecute.h"
60 # include "mouse.h"
61 #endif
62 # include "color.h"
63 # include "getcolor.h"
64
65 #ifdef USE_MOUSE
66 /* Petr Mikulik, February 2001
67  * Declarations similar to src/os2/gclient.c -- see section
68  * "PM: Now variables for mouse" there in.
69  */
70
71 /* If wgnuplot crashes during redrawing and mouse on, then this could help: */
72 /* static char lock_mouse = 1; */
73
74 /* Status of the ruler */
75 static struct Ruler {
76     TBOOLEAN on;                /* ruler active ? */
77     int x, y;                   /* ruler position */
78 } ruler = {FALSE,0,0,};
79
80 /* Status of the line from ruler to cursor */
81 static struct RulerLineTo {
82     TBOOLEAN on;                /* ruler line active ? */
83     int x, y;                   /* ruler line end position (previous cursor position) */
84 } ruler_lineto = {FALSE,0,0,};
85
86 /* Status of zoom box */
87 static struct Zoombox {
88     TBOOLEAN on;                /* set to TRUE during zooming */
89     POINT from, to;             /* corners of the zoom box */
90     LPCSTR text1, text2;        /* texts in the corners (i.e. positions) */
91 } zoombox = { FALSE, {0,0}, {0,0}, NULL, NULL };
92
93 /* Pointer definitions */
94 HCURSOR hptrDefault, hptrCrossHair, hptrScaling, hptrRotating, hptrZooming, hptrCurrent;
95
96 /* Mouse support routines */
97 static void     Wnd_exec_event(LPGW lpgw, LPARAM lparam, char type, int par1);
98 static void     Wnd_refresh_zoombox(LPGW lpgw, LPARAM lParam);
99 static void     Wnd_refresh_ruler_lineto(LPGW lpgw, LPARAM lParam);
100 static void     GetMousePosViewport(LPGW lpgw, int *mx, int *my);
101 static void     Draw_XOR_Text(LPGW lpgw, const char *text, size_t length, int x, int y);
102 static void     DisplayStatusLine(LPGW lpgw);
103 static void     UpdateStatusLine(LPGW lpgw, const char text[]);
104 static void     DrawRuler(LPGW lpgw);
105 static void     DrawRulerLineTo(LPGW lpgw);
106 static void     DrawZoomBox(LPGW lpgw);
107 static void     LoadCursors(LPGW lpgw);
108 static void     DestroyCursors(LPGW lpgw);
109 #endif /* USE_MOUSE */
110
111 /* ================================== */
112
113 #define MAXSTR 255
114
115 #define WGDEFCOLOR 15
116 COLORREF wginitcolor[WGDEFCOLOR] =  {
117         RGB(255,0,0),   /* red */
118         RGB(0,255,0),   /* green */
119         RGB(0,0,255),   /* blue */
120         RGB(255,0,255), /* magenta */
121         RGB(0,0,128),   /* dark blue */
122         RGB(128,0,0),   /* dark red */
123         RGB(0,128,128), /* dark cyan */
124         RGB(0,0,0),     /* black */
125         RGB(128,128,128), /* grey */
126         RGB(0,128,64),  /* very dark cyan */
127         RGB(128,128,0), /* dark yellow */
128         RGB(128,0,128), /* dark magenta */
129         RGB(192,192,192), /* light grey */
130         RGB(0,255,255), /* cyan */
131         RGB(255,255,0), /* yellow */
132 };
133 #define WGDEFSTYLE 5
134 int wginitstyle[WGDEFSTYLE] = {PS_SOLID, PS_DASH, PS_DOT, PS_DASHDOT, PS_DASHDOTDOT};
135
136 /* Maximum number of GWOPBLK arrays to be remembered. */
137 /* HBB 20010218: moved here from wgnuplib.h: other parts of the program don't
138  * need to know about it */
139 #define GWOPMAX 4096
140
141
142 /* bitmaps for filled boxes (ULIG) */
143 /* zeros represent the foreground color and ones represent the background color */
144 /* FIXME HBB 20010916: *never* extern in a C source! */
145 /*  extern int filldensity; */
146 /*  extern int fillpattern; */
147
148 static unsigned char halftone_bitmaps[][16] ={
149   { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
150     0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF },   /* no fill */
151   { 0xEE, 0xEE, 0xBB, 0xBB, 0xEE, 0xEE, 0xBB, 0xBB,
152     0xEE, 0xEE, 0xBB, 0xBB, 0xEE, 0xEE, 0xBB, 0xBB },   /* 25% pattern */
153   { 0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55,
154     0xAA, 0xAA, 0x55, 0x55, 0xAA, 0xAA, 0x55, 0x55 },   /* 50% pattern */
155   { 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22,
156     0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22 },   /* 75% pattern */
157   { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
158     0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }    /* solid pattern */
159 };
160 #define halftone_num (sizeof(halftone_bitmaps) / sizeof (*halftone_bitmaps))
161 static HBRUSH halftone_brush[halftone_num];
162 static BITMAP halftone_bitdata[halftone_num];
163 static HBITMAP halftone_bitmap[halftone_num];
164
165 static unsigned char pattern_bitmaps[][16] = {
166   {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
167    0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /* no fill */
168   {0xFE, 0xFE, 0x7D, 0x7D, 0xBB, 0xBB, 0xD7, 0xD7,
169    0xEF, 0xEF, 0xD7, 0xD7, 0xBB, 0xBB, 0x7D, 0x7D}, /* cross-hatch (1) */
170   {0x77, 0x77, 0xBB, 0xBB, 0xDD, 0xDD, 0xBB, 0xBB,
171    0x77, 0x77, 0xBB, 0xBB, 0xDD, 0xDD, 0xBB, 0xBB}, /* double cross-hatch (2) */
172   {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
173    0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* solid fill (3) */
174   {0xFE, 0xFE, 0xFD, 0xFD, 0xFB, 0xFB, 0xF7, 0xF7,
175    0xEF, 0xEF, 0xDF, 0xDF, 0xBF, 0xBF, 0x7F, 0x7F}, /* diagonals (4) */
176   {0x7F, 0x7F, 0xBF, 0xBF, 0xDF, 0xDF, 0xEF, 0xEF,
177    0xF7, 0xF7, 0xFB, 0xFB, 0xFD, 0xFD, 0xFE, 0xFE}, /* diagonals (5) */
178   {0xEE, 0xEE, 0xEE, 0xEE, 0xDD, 0xDD, 0xDD, 0xDD,
179    0xBB, 0xBB, 0xBB, 0xBB, 0x77, 0x77, 0x77, 0x77}, /* steep diagonals (6) */
180   {0x77, 0x77, 0x77, 0x77, 0xBB, 0xBB, 0xBB, 0xBB,
181    0xDD, 0xDD, 0xDD, 0xDD, 0xEE, 0xEE, 0xEE, 0xEE}  /* steep diagonals (7) */
182 #if (0)
183  ,{0xFC, 0xFC, 0xF3, 0xF3, 0xCF, 0xCF, 0x3F, 0x3F,
184    0xFC, 0xFC, 0xF3, 0xF3, 0xCF, 0xCF, 0x3F, 0x3F}, /* shallow diagonals (old 5) */
185   {0x3F, 0x3F, 0xCF, 0xCF, 0xF3, 0xF3, 0xFC, 0xFC,
186    0x3F, 0x3F, 0xCF, 0xCF, 0xF3, 0xF3, 0xFC, 0xFC}  /* shallow diagonals (old 6) */
187 #endif
188 };
189 #define pattern_num (sizeof(pattern_bitmaps)/(sizeof(*pattern_bitmaps)))
190 static HBRUSH pattern_brush[pattern_num];
191 static BITMAP pattern_bitdata[pattern_num];
192 static HBITMAP pattern_bitmap[pattern_num];
193
194 static TBOOLEAN brushes_initialized = FALSE;
195
196
197 /* ================================== */
198
199 /* prototypes for module-local functions */
200
201 LRESULT CALLBACK WINEXPORT WndGraphProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
202 BOOL CALLBACK WINEXPORT LineStyleDlgProc(HWND hdlg, UINT wmsg, WPARAM wparam, LPARAM lparam);
203
204 static void     DestroyBlocks(LPGW lpgw);
205 static BOOL     AddBlock(LPGW lpgw);
206 static void     StorePen(LPGW lpgw, int i, COLORREF ref, int colorstyle, int monostyle);
207 static void     MakePens(LPGW lpgw, HDC hdc);
208 static void     DestroyPens(LPGW lpgw);
209 static void     Wnd_GetTextSize(HDC hdc, LPCSTR str, size_t len, int *cx, int *cy);
210 static void     MakeFonts(LPGW lpgw, LPRECT lprect, HDC hdc);
211 static void     DestroyFonts(LPGW lpgw);
212 static void     SetFont(LPGW lpgw, HDC hdc);
213 static void     SelFont(LPGW lpgw);
214 static void     dot(HDC hdc, int xdash, int ydash);
215 static void     drawgraph(LPGW lpgw, HDC hdc, LPRECT rect);
216 static void     CopyClip(LPGW lpgw);
217 static void     CopyPrint(LPGW lpgw);
218 static void     WriteGraphIni(LPGW lpgw);
219 static void     ReadGraphIni(LPGW lpgw);
220 static COLORREF GetColor(HWND hwnd, COLORREF ref);
221 static void     UpdateColorSample(HWND hdlg);
222 static BOOL     LineStyle(LPGW lpgw);
223
224 /* ================================== */
225
226 /* Helper functions for GraphOp(): */
227
228 /* destroy memory blocks holding graph operations */
229 static void
230 DestroyBlocks(LPGW lpgw)
231 {
232     struct GWOPBLK *this, *next;
233     struct GWOP FAR *gwop;
234     unsigned int i;
235
236         this = lpgw->gwopblk_head;
237         while (this != NULL) {
238                 next = this->next;
239                 if (!this->gwop) {
240                         this->gwop = (struct GWOP FAR *)GlobalLock(this->hblk);
241                 }
242                 if (this->gwop) {
243                         /* free all text strings within this block */
244                         gwop = this->gwop;
245                         for (i=0; i<GWOPMAX; i++) {
246                                 if (gwop->htext)
247                                         LocalFree(gwop->htext);
248                                 gwop++;
249                         }
250                 }
251                 GlobalUnlock(this->hblk);
252                 GlobalFree(this->hblk);
253                 LocalFreePtr(this);
254                 this = next;
255         }
256         lpgw->gwopblk_head = NULL;
257         lpgw->gwopblk_tail = NULL;
258         lpgw->nGWOP = 0;
259 }
260
261
262 /* add a new memory block for graph operations */
263 /* returns TRUE if block allocated */
264 static BOOL
265 AddBlock(LPGW lpgw)
266 {
267         HGLOBAL hblk;
268         struct GWOPBLK *next, *this;
269
270         /* create new block */
271         next = (struct GWOPBLK *)LocalAllocPtr(LHND, sizeof(struct GWOPBLK) );
272         if (next == NULL)
273                 return FALSE;
274         hblk = GlobalAlloc(GHND, GWOPMAX*sizeof(struct GWOP));
275         if (hblk == NULL)
276                 return FALSE;
277         next->hblk = hblk;
278         next->gwop = (struct GWOP FAR *)NULL;
279         next->next = (struct GWOPBLK *)NULL;
280         next->used = 0;
281
282         /* attach it to list */
283         this = lpgw->gwopblk_tail;
284         if (this == NULL) {
285                 lpgw->gwopblk_head = next;
286         } else {
287                 this->next = next;
288                 this->gwop = (struct GWOP FAR *)NULL;
289                 GlobalUnlock(this->hblk);
290         }
291         lpgw->gwopblk_tail = next;
292         next->gwop = (struct GWOP FAR *)GlobalLock(next->hblk);
293         if (next->gwop == (struct GWOP FAR *)NULL)
294                 return FALSE;
295
296         return TRUE;
297 }
298
299
300 void WDPROC
301 GraphOp(LPGW lpgw, WORD op, WORD x, WORD y, LPCSTR str)
302 {
303     if (str)
304         GraphOpSize(lpgw, op, x, y, str, _fstrlen(str)+1);
305     else
306         GraphOpSize(lpgw, op, x, y, NULL, 0);
307 }
308
309
310 void WDPROC
311 GraphOpSize(LPGW lpgw, WORD op, WORD x, WORD y, LPCSTR str, DWORD size)
312 {
313         struct GWOPBLK *this;
314         struct GWOP FAR *gwop;
315         char *npstr;
316
317         this = lpgw->gwopblk_tail;
318         if ( (this==NULL) || (this->used >= GWOPMAX) ) {
319                 /* not enough space so get new block */
320                 if (!AddBlock(lpgw))
321                         return;
322                 this = lpgw->gwopblk_tail;
323         }
324         gwop = &this->gwop[this->used];
325         gwop->op = op;
326         gwop->x = x;
327         gwop->y = y;
328         gwop->htext = 0;
329         if (str) {
330                 gwop->htext = LocalAlloc(LHND, size);
331                 npstr = LocalLock(gwop->htext);
332                 if (gwop->htext && (npstr != (char *)NULL))
333                         memcpy(npstr, str, size);
334                 LocalUnlock(gwop->htext);
335         }
336         this->used++;
337         lpgw->nGWOP++;
338         return;
339 }
340
341 /* ================================== */
342
343 /* Prepare Graph window for being displayed by windows, read wgnuplot.ini, update
344  * the window's menus and show it */
345 void WDPROC
346 GraphInit(LPGW lpgw)
347 {
348         HMENU sysmenu;
349         WNDCLASS wndclass;
350         char buf[MAX_PATH];
351
352         if (!lpgw->hPrevInstance) {
353                 wndclass.style = CS_HREDRAW | CS_VREDRAW;
354                 wndclass.lpfnWndProc = WndGraphProc;
355                 wndclass.cbClsExtra = 0;
356                 wndclass.cbWndExtra = 2 * sizeof(void FAR *);
357                 wndclass.hInstance = lpgw->hInstance;
358                 wndclass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
359                 wndclass.hCursor = LoadCursor(NULL, IDC_ARROW);
360                 wndclass.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
361                 wndclass.lpszMenuName = NULL;
362                 wndclass.lpszClassName = szGraphClass;
363                 RegisterClass(&wndclass);
364         }
365
366         ReadGraphIni(lpgw);
367
368         lpgw->hWndGraph = CreateWindow(szGraphClass, lpgw->Title,
369                 WS_OVERLAPPEDWINDOW,
370                 lpgw->Origin.x, lpgw->Origin.y,
371                 lpgw->Size.x, lpgw->Size.y,
372                 NULL, NULL, lpgw->hInstance, lpgw);
373
374         lpgw->hPopMenu = CreatePopupMenu();
375         AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->graphtotop ? MF_CHECKED : MF_UNCHECKED),
376                 M_GRAPH_TO_TOP, "Bring to &Top");
377         AppendMenu(lpgw->hPopMenu, MF_STRING | (lpgw->color ? MF_CHECKED : MF_UNCHECKED),
378                 M_COLOR, "C&olor");
379         AppendMenu(lpgw->hPopMenu, MF_STRING, M_COPY_CLIP, "&Copy to Clipboard");
380 #if WINVER >= 0x030a
381         AppendMenu(lpgw->hPopMenu, MF_STRING, M_BACKGROUND, "&Background...");
382         AppendMenu(lpgw->hPopMenu, MF_STRING, M_CHOOSE_FONT, "Choose &Font...");
383         AppendMenu(lpgw->hPopMenu, MF_STRING, M_LINESTYLE, "&Line Styles...");
384 #endif
385         AppendMenu(lpgw->hPopMenu, MF_STRING, M_PRINT, "&Print...");
386         if (lpgw->IniFile != (LPSTR)NULL) {
387                 wsprintf(buf,"&Update %s",lpgw->IniFile);
388                 AppendMenu(lpgw->hPopMenu, MF_STRING, M_WRITEINI, (LPSTR)buf);
389         }
390
391         /* modify the system menu to have the new items we want */
392         sysmenu = GetSystemMenu(lpgw->hWndGraph,0);
393         AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
394         AppendMenu(sysmenu, MF_POPUP, (UINT)lpgw->hPopMenu, "&Options");
395         AppendMenu(sysmenu, MF_STRING, M_ABOUT, "&About");
396
397         if (!IsWindowVisible(lpgw->lptw->hWndParent)) {
398                 AppendMenu(sysmenu, MF_SEPARATOR, 0, NULL);
399                 AppendMenu(sysmenu, MF_STRING, M_COMMANDLINE, "C&ommand Line");
400         }
401
402         ShowWindow(lpgw->hWndGraph, SW_SHOWNORMAL);
403 }
404
405 /* close a graph window */
406 void WDPROC
407 GraphClose(LPGW lpgw)
408 {
409         /* close window */
410         if (lpgw->hWndGraph)
411                 DestroyWindow(lpgw->hWndGraph);
412         TextMessage();
413         lpgw->hWndGraph = NULL;
414
415         lpgw->locked = TRUE;
416         DestroyBlocks(lpgw);
417         lpgw->locked = FALSE;
418 }
419
420
421 void WDPROC
422 GraphStart(LPGW lpgw, double pointsize)
423 {
424         lpgw->locked = TRUE;
425         DestroyBlocks(lpgw);
426         lpgw->org_pointsize = pointsize;
427         if ( !lpgw->hWndGraph || !IsWindow(lpgw->hWndGraph) )
428                 GraphInit(lpgw);
429         if (IsIconic(lpgw->hWndGraph))
430                 ShowWindow(lpgw->hWndGraph, SW_SHOWNORMAL);
431         if (lpgw->graphtotop) {
432                 /* HBB NEW 20040221: avoid grabbing the keyboard focus
433                  * unless mouse mode is on */
434 #ifdef USE_MOUSE
435                 if (mouse_setting.on) {
436                         BringWindowToTop(lpgw->hWndGraph);
437                         return;
438                 }
439 #endif /* USE_MOUSE */
440                 SetWindowPos(lpgw->hWndGraph,
441                              HWND_TOP, 0,0,0,0,
442                              SWP_NOACTIVATE | SWP_NOMOVE | SWP_NOSIZE);
443         }
444 }
445
446 void WDPROC
447 GraphEnd(LPGW lpgw)
448 {
449         RECT rect;
450
451         GetClientRect(lpgw->hWndGraph, &rect);
452         InvalidateRect(lpgw->hWndGraph, (LPRECT) &rect, 1);
453         lpgw->locked = FALSE;
454         UpdateWindow(lpgw->hWndGraph);
455 #ifdef USE_MOUSE
456         gp_exec_event(GE_plotdone, 0, 0, 0, 0, 0);      /* notify main program */
457 #endif
458 }
459
460 void WDPROC
461 GraphResume(LPGW lpgw)
462 {
463         lpgw->locked = TRUE;
464 }
465
466 void WDPROC
467 GraphPrint(LPGW lpgw)
468 {
469         if (lpgw->hWndGraph && IsWindow(lpgw->hWndGraph))
470                 SendMessage(lpgw->hWndGraph,WM_COMMAND,M_PRINT,0L);
471 }
472
473 void WDPROC
474 GraphRedraw(LPGW lpgw)
475 {
476         if (lpgw->hWndGraph && IsWindow(lpgw->hWndGraph))
477                 SendMessage(lpgw->hWndGraph,WM_COMMAND,M_REBUILDTOOLS,0L);
478 }
479 /* ================================== */
480
481 /* Helper functions for bookkeeping of pens, brushes and fonts */
482
483 /* Set up LOGPEN structures based on information coming from wgnuplot.ini, via
484  * ReadGraphIni() */
485 static void
486 StorePen(LPGW lpgw, int i, COLORREF ref, int colorstyle, int monostyle)
487 {
488         LOGPEN FAR *plp;
489
490         plp = &lpgw->colorpen[i];
491         plp->lopnColor = ref;
492         if (colorstyle < 0) {
493                 plp->lopnWidth.x = -colorstyle;
494                 plp->lopnStyle = 0;
495         } else {
496                 plp->lopnWidth.x = 1;
497                 plp->lopnStyle = colorstyle % 5;
498         }
499         plp->lopnWidth.y = 0;
500
501         plp = &lpgw->monopen[i];
502         plp->lopnColor = RGB(0,0,0);
503         if (monostyle < 0) {
504                 plp->lopnWidth.x = -monostyle;
505                 plp->lopnStyle = 0;
506         } else {
507                 plp->lopnWidth.x = 1;
508                 plp->lopnStyle = monostyle % 5;
509         }
510         plp->lopnWidth.y = 0;
511 }
512
513 /* Prepare pens and brushes (--> colors) for use by the driver. Pens are (now) created
514  * on-the-fly (--> DeleteObject(SelectObject(...)) idiom), but the brushes are still
515  * all created statically, and kept until the window is closed */
516 static void
517 MakePens(LPGW lpgw, HDC hdc)
518 {
519         int i;
520
521         if ((GetDeviceCaps(hdc,NUMCOLORS) == 2) || !lpgw->color) {
522                 lpgw->hapen = CreatePenIndirect((LOGPEN FAR *)&lpgw->monopen[1]);       /* axis */
523                 lpgw->hbrush = CreateSolidBrush(RGB(255,255,255));
524                 for (i=0; i<WGNUMPENS+2; i++)
525                         lpgw->colorbrush[i] = CreateSolidBrush(RGB(0,0,0));
526         } else {
527                 lpgw->hapen = CreatePenIndirect((LOGPEN FAR *)&lpgw->colorpen[1]);      /* axis */
528                 lpgw->hbrush = CreateSolidBrush(lpgw->background);
529                 for (i=0; i<WGNUMPENS+2; i++)
530                         lpgw->colorbrush[i] = CreateSolidBrush(lpgw->colorpen[i].lopnColor);
531         }
532
533         /* build pattern brushes for filled boxes (ULIG) */
534         if( ! brushes_initialized ) {
535                 int i;
536
537                 for(i=0; i < halftone_num; i++) {
538                         halftone_bitdata[i].bmType       = 0;
539                         halftone_bitdata[i].bmWidth      = 16;
540                         halftone_bitdata[i].bmHeight     = 8;
541                         halftone_bitdata[i].bmWidthBytes = 2;
542                         halftone_bitdata[i].bmPlanes     = 1;
543                         halftone_bitdata[i].bmBitsPixel  = 1;
544                         halftone_bitdata[i].bmBits       = halftone_bitmaps[i];
545                         halftone_bitmap[i] = CreateBitmapIndirect(&halftone_bitdata[i]);
546                         halftone_brush[i] = CreatePatternBrush(halftone_bitmap[i]);
547                 }
548
549                 for(i=0; i < pattern_num; i++) {
550                         pattern_bitdata[i].bmType       = 0;
551                         pattern_bitdata[i].bmWidth      = 16;
552                         pattern_bitdata[i].bmHeight     = 8;
553                         pattern_bitdata[i].bmWidthBytes = 2;
554                         pattern_bitdata[i].bmPlanes     = 1;
555                         pattern_bitdata[i].bmBitsPixel  = 1;
556                         pattern_bitdata[i].bmBits       = pattern_bitmaps[i];
557                         pattern_bitmap[i] = CreateBitmapIndirect(&pattern_bitdata[i]);
558                         pattern_brush[i] = CreatePatternBrush(pattern_bitmap[i]);
559                 }
560
561                 brushes_initialized = TRUE;
562         }
563 }
564
565 /* Undo effect of MakePens(). To be called just before the window is closed. */
566 static void
567 DestroyPens(LPGW lpgw)
568 {
569         int i;
570
571         DeleteObject(lpgw->hbrush);
572         DeleteObject(lpgw->hapen);
573         for (i=0; i<WGNUMPENS+2; i++)
574                 DeleteObject(lpgw->colorbrush[i]);
575
576         /* delete brushes used for boxfilling (ULIG) */
577         if( brushes_initialized ) {
578                 int i;
579
580                 for( i=0; i<halftone_num; i++ ) {
581                         DeleteObject(halftone_bitmap[i]);
582                         DeleteObject(halftone_brush[i]);
583                 }
584                 for( i=0; i<pattern_num; i++ ) {
585                         DeleteObject(pattern_bitmap[i]);
586                         DeleteObject(pattern_brush[i]);
587                 }
588                 brushes_initialized = FALSE;
589         }
590 }
591
592 /* ================================== */
593
594 /* HBB 20010218: new function. An isolated snippet from MakeFont(), now also
595  * used in Wnd_put_tmptext() to size the temporary bitmap. */
596 static void
597 Wnd_GetTextSize(HDC hdc, LPCSTR str, size_t len, int *cx, int *cy)
598 {
599 #ifdef WIN32
600         SIZE size;
601
602         GetTextExtentPoint(hdc, str, len, &size);
603         *cx = size.cx;
604         *cy = size.cy;
605 #else
606         /* Hmm... if not for compatibility to Win 3.0 and earlier, we could
607          * use GetTextExtentPoint on Win16, too :-( */
608         DWORD extent;
609
610         extent = GetTextExtent(hdc, str, len);
611         *cx = LOWORD(extent);
612         *cy = HIWORD(extent);
613 #endif
614 }
615
616 static void
617 MakeFonts(LPGW lpgw, LPRECT lprect, HDC hdc)
618 {
619         HFONT hfontold;
620         TEXTMETRIC tm;
621         int result;
622         char FAR *p;
623         int cx, cy;
624
625         lpgw->rotate = FALSE;
626         _fmemset(&(lpgw->lf), 0, sizeof(LOGFONT));
627         _fstrncpy(lpgw->lf.lfFaceName,lpgw->fontname,LF_FACESIZE);
628         lpgw->lf.lfHeight = -MulDiv(lpgw->fontsize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
629         lpgw->lf.lfCharSet = DEFAULT_CHARSET;
630         if ( (p = _fstrstr(lpgw->fontname," Italic")) != (LPSTR)NULL ) {
631                 lpgw->lf.lfFaceName[ (unsigned int)(p-lpgw->fontname) ] = '\0';
632                 lpgw->lf.lfItalic = TRUE;
633         }
634         if ( (p = _fstrstr(lpgw->fontname," Bold")) != (LPSTR)NULL ) {
635                 lpgw->lf.lfFaceName[ (unsigned int)(p-lpgw->fontname) ] = '\0';
636                 lpgw->lf.lfWeight = FW_BOLD;
637         }
638
639         if (lpgw->hfonth == 0) {
640                 lpgw->hfonth = CreateFontIndirect((LOGFONT FAR *)&(lpgw->lf));
641         }
642
643         /* we do need a 90 degree font */
644         if (lpgw->hfontv) 
645                 DeleteObject(lpgw->hfontv);
646         lpgw->lf.lfEscapement = 900;
647         lpgw->lf.lfOrientation = 900;
648         lpgw->hfontv = CreateFontIndirect((LOGFONT FAR *)&(lpgw->lf));
649
650         /* save text size */
651         hfontold = SelectObject(hdc, lpgw->hfonth);
652         Wnd_GetTextSize(hdc, "0123456789", 10, &cx, &cy);
653         lpgw->vchar = MulDiv(cy,lpgw->ymax,lprect->bottom - lprect->top);
654         lpgw->hchar = MulDiv(cx/10,lpgw->xmax,lprect->right - lprect->left);
655
656         /* CMW: Base tick size on character size */
657         lpgw->htic = lpgw->hchar / 2;
658         cy = MulDiv(cx/20, GetDeviceCaps(hdc, LOGPIXELSY), GetDeviceCaps(hdc, LOGPIXELSX));
659         lpgw->vtic = MulDiv(cy,lpgw->ymax,lprect->bottom - lprect->top);
660         /* find out if we can rotate text 90deg */
661         SelectObject(hdc, lpgw->hfontv);
662         result = GetDeviceCaps(hdc, TEXTCAPS);
663         if ((result & TC_CR_90) || (result & TC_CR_ANY))
664                 lpgw->rotate = TRUE;
665         GetTextMetrics(hdc,(TEXTMETRIC FAR *)&tm);
666         if (tm.tmPitchAndFamily & TMPF_VECTOR)
667                 lpgw->rotate = TRUE;    /* vector fonts can all be rotated */
668 #if WINVER >=0x030a
669         if (tm.tmPitchAndFamily & TMPF_TRUETYPE)
670                 lpgw->rotate = TRUE;    /* truetype fonts can all be rotated */
671 #endif
672         SelectObject(hdc, hfontold);
673         return;
674 }
675
676 static void
677 DestroyFonts(LPGW lpgw)
678 {
679         if (lpgw->hfonth) {
680                 DeleteObject(lpgw->hfonth);
681                 lpgw->hfonth = 0;
682         }
683         if (lpgw->hfontv) {
684                 DeleteObject(lpgw->hfontv);
685                 lpgw->hfontv = 0;
686         }
687         return;
688 }
689
690 static void
691 SetFont(LPGW lpgw, HDC hdc)
692 {
693     if (lpgw->rotate && lpgw->angle) {
694         if (lpgw->hfontv)
695             DeleteObject(lpgw->hfontv);
696         lpgw->lf.lfEscapement = lpgw->lf.lfOrientation  = lpgw->angle * 10;
697         lpgw->hfontv = CreateFontIndirect((LOGFONT FAR *)&(lpgw->lf));
698        if (lpgw->hfontv)
699             SelectObject(hdc, lpgw->hfontv);
700     } else {
701         if (lpgw->hfonth)
702             SelectObject(hdc, lpgw->hfonth);
703     }
704     return;
705 }
706
707 static void
708 SelFont(LPGW lpgw)
709 {
710 #if WINVER >= 0x030a
711         LOGFONT lf;
712         CHOOSEFONT cf;
713         HDC hdc;
714         char lpszStyle[LF_FACESIZE];
715         char FAR *p;
716
717         /* Set all structure fields to zero. */
718         _fmemset(&cf, 0, sizeof(CHOOSEFONT));
719         _fmemset(&lf, 0, sizeof(LOGFONT));
720         cf.lStructSize = sizeof(CHOOSEFONT);
721         cf.hwndOwner = lpgw->hWndGraph;
722         _fstrncpy(lf.lfFaceName,lpgw->fontname,LF_FACESIZE);
723         if ((p = _fstrstr(lpgw->fontname," Bold")) != (LPSTR)NULL) {
724                 _fstrncpy(lpszStyle,p+1,LF_FACESIZE);
725                 lf.lfFaceName[ (unsigned int)(p-lpgw->fontname) ] = '\0';
726         } else if ((p = _fstrstr(lpgw->fontname," Italic")) != (LPSTR)NULL) {
727                 _fstrncpy(lpszStyle,p+1,LF_FACESIZE);
728                 lf.lfFaceName[ (unsigned int)(p-lpgw->fontname) ] = '\0';
729         } else {
730                 _fstrcpy(lpszStyle,"Regular");
731         }
732         cf.lpszStyle = lpszStyle;
733         hdc = GetDC(lpgw->hWndGraph);
734         lf.lfHeight = -MulDiv(lpgw->fontsize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
735         ReleaseDC(lpgw->hWndGraph, hdc);
736         cf.lpLogFont = &lf;
737         cf.nFontType = SCREEN_FONTTYPE;
738         cf.Flags = CF_SCREENFONTS | CF_INITTOLOGFONTSTRUCT | CF_USESTYLE;
739         if (ChooseFont(&cf)) {
740                 _fstrcpy(lpgw->fontname,lf.lfFaceName);
741                 lpgw->fontsize = cf.iPointSize / 10;
742                 if (cf.nFontType & BOLD_FONTTYPE)
743                         lstrcat(lpgw->fontname," Bold");
744                 if (cf.nFontType & ITALIC_FONTTYPE)
745                         lstrcat(lpgw->fontname," Italic");
746                 /* set current font as default font */
747                 strcpy(lpgw->deffontname,lpgw->fontname);
748                 lpgw->deffontsize = lpgw->fontsize;
749                 SendMessage(lpgw->hWndGraph,WM_COMMAND,M_REBUILDTOOLS,0L);
750         }
751 #endif
752 }
753
754 #ifdef USE_MOUSE
755 /* ================================== */
756
757 static void
758 LoadCursors(LPGW lpgw)
759 {
760         /* 3 of them are standard cursor shapes: */
761         hptrDefault = LoadCursor(NULL, IDC_ARROW);
762         hptrZooming = LoadCursor(NULL, IDC_SIZEALL);
763         hptrCrossHair = LoadCursor( NULL, IDC_CROSS);
764         /* the other 2 are kept in the resource file: */
765         hptrScaling = LoadCursor( lpgw->hInstance, MAKEINTRESOURCE(IDC_SCALING));
766         hptrRotating = LoadCursor( lpgw->hInstance, MAKEINTRESOURCE(IDC_ROTATING));
767
768         hptrCurrent = hptrCrossHair;
769 }
770
771 static void
772 DestroyCursors(LPGW lpgw)
773 {
774         /* No-op. Cursors from LoadCursor() don't need destroying */
775         return;
776 }
777
778 #endif /* USE_MOUSE */
779
780 /* ================================== */
781
782
783 static void
784 dot(HDC hdc, int xdash, int ydash)
785 {
786         MoveTo(hdc, xdash, ydash);
787         LineTo(hdc, xdash, ydash+1);
788 }
789
790
791 /* This one is really the heart of this module: it executes the stored set of
792  * commands, whenever it changed or a redraw is necessary */
793 static void
794 drawgraph(LPGW lpgw, HDC hdc, LPRECT rect)
795 {
796     int xdash, ydash;                   /* the transformed coordinates */
797     int rr, rl, rt, rb;
798     struct GWOP FAR *curptr;
799     struct GWOPBLK *blkptr;
800     int htic, vtic;
801     int hshift, vshift;
802     unsigned int lastop=-1;             /* used for plotting last point on a line */
803     int pen;
804     int polymax = 200;
805     int polyi = 0;
806     POINT *ppt;
807     unsigned int ngwop=0;
808     BOOL isColor;
809     double line_width = 1.0;
810     unsigned int fillstyle = 0.0;
811     int idx;
812
813     if (lpgw->locked)
814         return;
815
816     /* HBB 20010218: the GDI status query functions don't work on Metafile
817      * handles, so can't know whether the screen is actually showing
818      * color or not, if drawgraph() is being called from CopyClip().
819      * Solve by defaulting isColor to 1 if hdc is a metafile. */
820     isColor = (((GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc,BITSPIXEL))
821                 > 2)
822                || (GetDeviceCaps(hdc, TECHNOLOGY) == DT_METAFILE));
823
824     if (lpgw->background != RGB(255,255,255) && lpgw->color && isColor) {
825         SetBkColor(hdc,lpgw->background);
826         FillRect(hdc, rect, lpgw->hbrush);
827     }
828
829     ppt = (POINT *)LocalAllocPtr(LHND, (polymax+1) * sizeof(POINT));
830
831     rr = rect->right;
832     rl = rect->left;
833     rt = rect->top;
834     rb = rect->bottom;
835
836     htic = lpgw->org_pointsize*MulDiv(lpgw->htic, rr - rl, lpgw->xmax) + 1;
837     vtic = lpgw->org_pointsize*MulDiv(lpgw->vtic, rb - rt, lpgw->ymax) + 1;
838
839     lpgw->angle = 0;
840     SetFont(lpgw, hdc);
841     SetTextAlign(hdc, TA_LEFT|TA_BASELINE);
842  
843     /* calculate text shifting for horizontal text */
844     hshift = 0;
845     vshift = MulDiv(lpgw->vchar, rb - rt, lpgw->ymax)/2;
846   
847     pen = 2;
848     SelectObject(hdc, lpgw->hapen);
849     SelectObject(hdc, lpgw->colorbrush[pen]);
850
851     /* do the drawing */
852     blkptr = lpgw->gwopblk_head;
853     curptr = NULL;
854     if (blkptr) {
855         if (!blkptr->gwop)
856             blkptr->gwop = (struct GWOP FAR *)GlobalLock(blkptr->hblk);
857         if (!blkptr->gwop)
858             return;
859         curptr = (struct GWOP FAR *)blkptr->gwop;
860     }
861
862     while(ngwop < lpgw->nGWOP) {
863         /* transform the coordinates */
864         xdash = MulDiv(curptr->x, rr-rl-1, lpgw->xmax) + rl;
865         ydash = MulDiv(curptr->y, rt-rb+1, lpgw->ymax) + rb - 1;
866         if ((lastop==W_vect) && (curptr->op!=W_vect)) {
867             if (polyi >= 2) {
868                 Polyline(hdc, ppt, polyi);
869                 MoveTo(hdc, ppt[0].x, ppt[0].y);
870             } else if (polyi == 1)
871                 LineTo(hdc, ppt[0].x, ppt[0].y);
872             polyi = 0;
873         }
874         switch (curptr->op) {
875         case 0: /* have run past last in this block */
876             break;
877         case W_move:
878             ppt[0].x = xdash;
879             ppt[0].y = ydash;
880             polyi = 1;
881             break;
882         case W_vect:
883             ppt[polyi].x = xdash;
884             ppt[polyi].y = ydash;
885             polyi++;
886             if (polyi >= polymax) {
887                 Polyline(hdc, ppt, polyi);
888                 ppt[0].x = xdash;
889                 ppt[0].y = ydash;
890                 polyi = 1;
891             }
892             break;
893         case W_line_type:
894             {
895                 LOGBRUSH lb;
896                 LOGPEN cur_penstruct;
897
898                 short cur_pen = ((curptr->x < (WORD)(-2))
899                                  ? (curptr->x % WGNUMPENS) + 2
900                                  : curptr->x + 2);
901                 /* set color only when second parameter to W_line_type equals 1 */
902                 if (curptr->y != 1)
903                     pen = cur_pen;
904                 cur_penstruct = (lpgw->color && isColor) ?
905                     lpgw->colorpen[pen] : lpgw->monopen[pen];
906                 cur_penstruct.lopnColor = ((lpgw->color && isColor) ?
907                     lpgw->colorpen[cur_pen] : lpgw->monopen[cur_pen]).lopnColor;
908
909                 if (line_width != 1)
910                     cur_penstruct.lopnWidth.x *= line_width;
911         
912                 /* use ExtCreatePen instead of CreatePen/CreatePenIndirect
913                  * to support dashed lines if line_width > 1 */
914                 lb.lbStyle = BS_SOLID;
915                 lb.lbColor = cur_penstruct.lopnColor;
916
917 #if 0 /* shige work-around for Windows clipboard bug */
918                 lpgw->hapen = ExtCreatePen(
919                         (line_width==1 ? PS_COSMETIC : PS_GEOMETRIC) | cur_penstruct.lopnStyle | PS_ENDCAP_FLAT | PS_JOIN_BEVEL, 
920                         cur_penstruct.lopnWidth.x, &lb, 0, 0);
921 #else
922                 if (line_width==1)
923                   lpgw->hapen = CreatePenIndirect((LOGPEN FAR *) &cur_penstruct);
924                 else
925                   lpgw->hapen = ExtCreatePen(
926                         PS_GEOMETRIC | cur_penstruct.lopnStyle | PS_ENDCAP_FLAT | PS_JOIN_BEVEL, 
927                         cur_penstruct.lopnWidth.x, &lb, 0, 0);
928 #endif
929                 DeleteObject(SelectObject(hdc, lpgw->hapen));
930
931                 SelectObject(hdc, lpgw->colorbrush[cur_pen]);
932                 /* PM 7.7.2002: support color text */
933                 SetTextColor(hdc, cur_penstruct.lopnColor);
934             }
935         break;
936
937         case W_put_text:
938             {
939                 char *str;
940                 str = LocalLock(curptr->htext);
941                 if (str) {
942                     /* shift correctly for rotated text */
943                     xdash += hshift;
944                     ydash += vshift;
945
946                     SetBkMode(hdc,TRANSPARENT);
947                     TextOut(hdc,xdash,ydash,str,lstrlen(str));
948                     SetBkMode(hdc,OPAQUE);
949                 }
950                 LocalUnlock(curptr->htext);
951             }
952         break;
953
954         case W_fillstyle:
955             /* HBB 20010916: new entry, needed to squeeze the many
956              * parameters of a filled box call through the bottlneck
957              * of the fixed number of parameters in GraphOp() and
958              * struct GWOP, respectively. */
959             fillstyle = curptr->x;
960             break;
961
962         case W_boxfill:   /* ULIG */
963
964             assert (polyi == 1);
965
966             /* NOTE: the x and y passed with this call are the width and
967              * height of the box, actually. The left corner was stored into
968              * ppt[0] by a preceding W_move, and the style was memorized
969              * by a W_fillstyle call. */
970             switch(fillstyle & 0x0f) {
971                 case FS_SOLID:
972                     /* style == 1 --> use halftone fill pattern
973                      * according to filldensity. Density is from
974                      * 0..100 percent: */
975                     idx = ((fillstyle >> 4) * (halftone_num - 1) / 100 );
976                     if (idx < 0)
977                         idx = 0;
978                     if (idx > halftone_num - 1)
979                         idx = halftone_num - 1;
980                     SelectObject(hdc, halftone_brush[idx]);
981                     break;
982                 case FS_PATTERN:
983                     /* style == 2 --> use fill pattern according to
984                      * fillpattern. Pattern number is enumerated */
985                     idx = fillstyle >> 4;
986                     if (idx < 0)
987                         idx = 0;
988                     if (idx > pattern_num - 1)
989                         idx = 0;
990                     SelectObject(hdc, pattern_brush[idx]);
991                     break;
992                 case FS_EMPTY:
993                 default:
994                     /* style == 0 or unknown --> fill with background color */
995                     SelectObject(hdc, halftone_brush[0]);
996             }
997             /* needs to be fixed for monochrome devices */
998             /* FIXME: probably should keep track of text color */
999             SetTextColor(hdc, lpgw->colorpen[pen].lopnColor);
1000             xdash -= rl;
1001             ydash -= rb - 1;
1002             PatBlt(hdc, ppt[0].x, ppt[0].y, xdash, ydash, PATCOPY);
1003             polyi = 0;
1004             break;
1005         case W_text_angle:
1006             if (lpgw->angle != (short int)curptr->x) {
1007                 lpgw->angle = (short int)curptr->x;
1008                 /* correctly calculate shifting of rotated text */
1009                 hshift = sin(M_PI/180. * lpgw->angle) * MulDiv(lpgw->vchar, rr-rl, lpgw->xmax) / 2;
1010                 vshift = cos(M_PI/180. * lpgw->angle) * MulDiv(lpgw->vchar, rb-rt, lpgw->ymax) / 2;
1011                 SetFont(lpgw, hdc);
1012             }
1013             break;
1014         case W_justify:
1015             switch (curptr->x)
1016                 {
1017                 case LEFT:
1018                     SetTextAlign(hdc, TA_LEFT|TA_BASELINE);
1019                     break;
1020                 case RIGHT:
1021                     SetTextAlign(hdc, TA_RIGHT|TA_BASELINE);
1022                     break;
1023                 case CENTRE:
1024                     SetTextAlign(hdc, TA_CENTER|TA_BASELINE);
1025                     break;
1026                 }
1027             break;
1028         case W_font: {
1029                 char *font;
1030
1031                 font = LocalLock(curptr->htext);
1032                 if (font) {
1033                     GraphChangeFont(lpgw, font, curptr->x, hdc, *rect);
1034                     SetFont(lpgw, hdc);
1035                 }
1036                 LocalUnlock(curptr->htext);
1037             }
1038             break;
1039         case W_pointsize:
1040             if (curptr->x != 0) {
1041                 double pointsize = curptr->x / 100.0;
1042                 htic = pointsize*MulDiv(lpgw->htic, rr-rl, lpgw->xmax) + 1;
1043                 vtic = pointsize*MulDiv(lpgw->vtic, rb-rt, lpgw->ymax) + 1;
1044             } else {
1045                 char *str;
1046                 str = LocalLock(curptr->htext);
1047                 if (str) {
1048                     double pointsize;
1049                     sscanf(str, "%lg", &pointsize);
1050                     htic = lpgw->org_pointsize
1051                         * MulDiv(lpgw->htic, rr-rl, lpgw->xmax) + 1;
1052                     vtic = lpgw->org_pointsize
1053                         * MulDiv(lpgw->vtic, rb-rt, lpgw->ymax) + 1;
1054                 }
1055                 LocalUnlock(curptr->htext);
1056             }
1057             break;
1058         case W_line_width:
1059             /* HBB 20000813: this may look strange, but it ensures
1060              * that linewidth is exactly 1 iff it's in default
1061              * state */
1062             line_width = curptr->x == 100 ? 1 : (curptr->x / 100.0);
1063             break;
1064         case W_pm3d_setcolor:
1065             {
1066                 static HBRUSH last_pm3d_brush = NULL;
1067                 HBRUSH this_brush;
1068                 COLORREF c;
1069                 LOGPEN cur_penstruct;
1070                 LOGBRUSH lb;
1071
1072                 /* distinguish gray values and RGB colors */
1073                 if (curptr->y == 0) {
1074                     rgb255_color rgb255;
1075                     rgb255maxcolors_from_gray( curptr->x / 256.0, &rgb255 );
1076                     c = RGB(rgb255.r, rgb255.g, rgb255.b);
1077                 }
1078                 else {
1079                     c = RGB(curptr->y & 0xff, (curptr->x >> 8) & 0xff, curptr->x & 0xff);
1080                 }
1081
1082                 /* FIXME: always a _solid_ brush?? */
1083                 this_brush = CreateSolidBrush(c);
1084                 SelectObject(hdc, this_brush);
1085                 if (last_pm3d_brush != NULL)
1086                     DeleteObject(last_pm3d_brush);
1087                 last_pm3d_brush = this_brush;
1088                 /* create new pen, too: */
1089                 cur_penstruct = (lpgw->color && isColor) ?
1090                     lpgw->colorpen[pen] : lpgw->monopen[pen];   
1091                 if (line_width != 1)
1092                     cur_penstruct.lopnWidth.x *= line_width;
1093                 lb.lbStyle = BS_SOLID;
1094                 lb.lbColor = c;
1095                 lpgw->hapen = ExtCreatePen(
1096                     (line_width==1 ? PS_COSMETIC : PS_GEOMETRIC) | cur_penstruct.lopnStyle | PS_ENDCAP_FLAT | PS_JOIN_BEVEL, 
1097                     cur_penstruct.lopnWidth.x, &lb, 0, 0);
1098                 DeleteObject(SelectObject(hdc, lpgw->hapen));
1099                 /* finally set text color */
1100                 SetTextColor(hdc, c);
1101             }
1102             break;
1103         case W_pm3d_filled_polygon_pt:
1104             {
1105                 /* a point of the polygon is coming */
1106                 if (polyi >= polymax) {
1107                     polymax += 200;
1108                     ppt = (POINT *)LocalReAllocPtr(ppt, LHND, (polymax+1) * sizeof(POINT));
1109                 }
1110                 ppt[polyi].x = xdash;
1111                 ppt[polyi].y = ydash;
1112                 polyi++;
1113             }
1114             break;
1115         case W_pm3d_filled_polygon_draw:
1116             {
1117                 /* end of point series --> draw polygon now */
1118                 Polygon(hdc, ppt, polyi);
1119                 polyi = 0;
1120             }
1121             break;
1122         case W_image:
1123             {
1124                 /* Due to the structure of gwop in total 5 entries are needed.
1125                    These static variables help to collect all the needed information
1126                 */
1127                 static int seq = 0;  /* sequence counter */
1128                 static POINT corners[4];
1129
1130                 if (seq < 4) {
1131                     /* The first four OPs contain the `corner` array */
1132                     corners[seq].x = xdash;
1133                     corners[seq].y = ydash;
1134                 } else {
1135                     /* The last OP contains the image and it's size */
1136                     BITMAPINFO bmi;
1137                     char *image;
1138                     unsigned int M, N;
1139                     int rc;
1140
1141                     M = curptr->x;
1142                     N = curptr->y;
1143                     image = LocalLock(curptr->htext);
1144                     if (image) {
1145
1146                         /* rc = SetStretchBltMode(hdc, HALFTONE); */
1147
1148                         memset(&bmi, 0, sizeof(bmi));
1149                         bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
1150                         bmi.bmiHeader.biWidth = M;
1151                         bmi.bmiHeader.biHeight = N;
1152                         bmi.bmiHeader.biPlanes = 1;
1153                         bmi.bmiHeader.biBitCount = 24;
1154                         bmi.bmiHeader.biCompression = BI_RGB;
1155                         bmi.bmiHeader.biClrUsed = 0;
1156
1157                         rc = StretchDIBits(hdc, 
1158                             corners[0].x, corners[0].y, 
1159                             corners[1].x - corners[0].x, corners[1].y - corners[0].y,
1160                             0, 0,
1161                             M, N,
1162                             image, &bmi,
1163                             DIB_RGB_COLORS, SRCCOPY );
1164                     }
1165                     LocalUnlock(curptr->htext);
1166                 }
1167                 seq = (seq + 1) % 5;
1168             }
1169             break;
1170         case W_dot:
1171             dot(hdc, xdash, ydash);
1172             break;
1173         case W_plus: /* do plus */
1174             MoveTo(hdc,xdash-htic,ydash);
1175             LineTo(hdc,xdash+htic+1,ydash);
1176             MoveTo(hdc,xdash,ydash-vtic);
1177             LineTo(hdc,xdash,ydash+vtic+1);
1178             break;
1179         case W_cross: /* do X */
1180             MoveTo(hdc,xdash-htic,ydash-vtic);
1181             LineTo(hdc,xdash+htic+1,ydash+vtic+1);
1182             MoveTo(hdc,xdash-htic,ydash+vtic);
1183             LineTo(hdc,xdash+htic+1,ydash-vtic-1);
1184             break;
1185         case W_star: /* do star */
1186             MoveTo(hdc,xdash-htic,ydash);
1187             LineTo(hdc,xdash+htic+1,ydash);
1188             MoveTo(hdc,xdash,ydash-vtic);
1189             LineTo(hdc,xdash,ydash+vtic+1);
1190             MoveTo(hdc,xdash-htic,ydash-vtic);
1191             LineTo(hdc,xdash+htic+1,ydash+vtic+1);
1192             MoveTo(hdc,xdash-htic,ydash+vtic);
1193             LineTo(hdc,xdash+htic+1,ydash-vtic-1);
1194             break;
1195         case W_circle: /* do open circle */
1196             Arc(hdc, xdash-htic, ydash-vtic, xdash+htic+1, ydash+vtic+1,
1197                 xdash, ydash+vtic+1, xdash, ydash+vtic+1);
1198             dot(hdc, xdash, ydash);
1199             break;
1200         case W_fcircle: /* do filled circle */
1201             Ellipse(hdc, xdash-htic, ydash-vtic,
1202                     xdash+htic+1, ydash+vtic+1);
1203             break;
1204         default:        /* potentially closed figure */
1205             {
1206                 POINT p[6];
1207                 int i;
1208                 int shape = 0;
1209                 int filled = 0;
1210                 static float pointshapes[5][10] = {
1211                     {-1, -1, +1, -1, +1, +1, -1, +1, 0, 0}, /* box */
1212                     { 0, +1, -1,  0,  0, -1, +1,  0, 0, 0}, /* diamond */
1213                     { 0, -4./3, -4./3, 2./3,
1214                       4./3,  2./3, 0, 0}, /* triangle */
1215                     { 0, 4./3, -4./3, -2./3,
1216                       4./3,  -2./3, 0, 0}, /* inverted triangle */
1217                     { 0, 1, 0.95106, 0.30902, 0.58779, -0.80902,
1218                       -0.58779, -0.80902, -0.95106, 0.30902} /* pentagon */
1219                 };
1220
1221                 switch (curptr->op) {
1222                 case W_box:
1223                     shape = 0;
1224                     break;
1225                 case W_diamond:
1226                     shape = 1;
1227                     break;
1228                 case W_itriangle:
1229                     shape = 2;
1230                     break;
1231                 case W_triangle:
1232                     shape = 3;
1233                     break;
1234                 case W_pentagon:
1235                     shape = 4;
1236                     break;
1237                 case W_fbox:
1238                     shape = 0;
1239                     filled = 1;
1240                     break;
1241                 case W_fdiamond:
1242                     shape = 1;
1243                     filled = 1;
1244                     break;
1245                 case W_fitriangle:
1246                     shape = 2;
1247                     filled = 1;
1248                     break;
1249                 case W_ftriangle:
1250                     shape = 3;
1251                     filled = 1;
1252                     break;
1253                 case W_fpentagon:
1254                     shape = 4;
1255                     filled = 1;
1256                     break;
1257                 }
1258
1259                 for (i = 0; i < 5; ++i) {
1260                     if (pointshapes[shape][i * 2 + 1] == 0
1261                         && pointshapes[shape][i * 2] == 0)
1262                         break;
1263                     p[i].x = xdash + htic*pointshapes[shape][i*2] + 0.5;
1264                     p[i].y = ydash + vtic*pointshapes[shape][i*2+1] + 0.5;
1265                 }
1266                 if ( filled )
1267                     /* Filled polygon */
1268                     Polygon(hdc, p, i);
1269                 else {
1270                     /* Outline polygon */
1271                     p[i].x = p[0].x;
1272                     p[i].y = p[0].y;
1273                     Polyline(hdc, p, i+1);
1274                     dot(hdc, xdash, ydash);
1275                 }
1276             } /* default case */
1277         } /* switch(opcode) */
1278         lastop = curptr->op;
1279         ngwop++;
1280         curptr++;
1281         if ((unsigned)(curptr - blkptr->gwop) >= GWOPMAX) {
1282             GlobalUnlock(blkptr->hblk);
1283             blkptr->gwop = (struct GWOP FAR *)NULL;
1284             if ((blkptr = blkptr->next) == NULL)
1285                 /* If exact multiple of GWOPMAX entries are queued,
1286                  * next will be NULL. Only the next GraphOp() call would
1287                  * have allocated a new block */
1288                 return;
1289             if (!blkptr->gwop)
1290                 blkptr->gwop = (struct GWOP FAR *)GlobalLock(blkptr->hblk);
1291             if (!blkptr->gwop)
1292                 return;
1293             curptr = (struct GWOP FAR *)blkptr->gwop;
1294         }
1295     }
1296     if (polyi >= 2)
1297         Polyline(hdc, ppt, polyi);
1298     LocalFreePtr(ppt);
1299 }
1300
1301 /* ================================== */
1302
1303 /* copy graph window to clipboard --- note that the Metafile is drawn at the full
1304  * virtual resolution of the Windows terminal driver (24000 x 18000 pixels), to
1305  * preserve as much accuracy as remotely possible */
1306 static void
1307 CopyClip(LPGW lpgw)
1308 {
1309         RECT rect;
1310         HDC mem;
1311         HBITMAP bitmap;
1312         HANDLE hmf;
1313         GLOBALHANDLE hGMem;
1314         LPMETAFILEPICT lpMFP;
1315         HWND hwnd;
1316         HDC hdc;
1317
1318         hwnd = lpgw->hWndGraph;
1319
1320         /* view the window */
1321         if (IsIconic(hwnd))
1322                 ShowWindow(hwnd, SW_SHOWNORMAL);
1323         BringWindowToTop(hwnd);
1324         UpdateWindow(hwnd);
1325
1326         /* get the context */
1327         hdc = GetDC(hwnd);
1328         GetClientRect(hwnd, &rect);
1329
1330         /* make a bitmap and copy it there */
1331         mem = CreateCompatibleDC(hdc);
1332         bitmap = CreateCompatibleBitmap(hdc, rect.right - rect.left,
1333                         rect.bottom - rect.top);
1334         if (bitmap) {
1335                 /* there is enough memory and the bitmaps OK */
1336                 SelectObject(mem, bitmap);
1337                 BitBlt(mem,0,0,rect.right - rect.left,
1338                         rect.bottom - rect.top, hdc, rect.left,
1339                         rect.top, SRCCOPY);
1340         } else {
1341                 MessageBeep(MB_ICONHAND);
1342                 MessageBox(hwnd, "Insufficient Memory to Copy Clipboard",
1343                         lpgw->Title, MB_ICONHAND | MB_OK);
1344         }
1345         DeleteDC(mem);
1346
1347         /* OK, bitmap done, now create a Metafile context at full theoretical resolution
1348          * of the Windows terminal (24000 x 18000 pixels), and redraw the whole
1349          * plot into that. */
1350         {
1351                 /* make copy of window's main status struct for modification */
1352                 GW gwclip = *lpgw;
1353                 int windowfontsize = MulDiv(lpgw->fontsize, GetDeviceCaps(hdc, LOGPIXELSY), 72);
1354                 int i;
1355
1356                 gwclip.fontsize = MulDiv(windowfontsize, lpgw->ymax, rect.bottom);
1357                 gwclip.hfonth = gwclip.hfontv = 0;
1358
1359                 /* HBB 981203: scale up pens as well... */
1360                 for (i = 0; i < WGNUMPENS + 2; i++) {
1361                         if(gwclip.monopen[i].lopnWidth.x > 1)
1362                                 gwclip.monopen[i].lopnWidth.x =
1363                                         MulDiv(gwclip.monopen[i].lopnWidth.x,
1364                                                gwclip.xmax, rect.right-rect.left);
1365                         if(gwclip.colorpen[i].lopnWidth.x > 1)
1366                                 gwclip.colorpen[i].lopnWidth.x =
1367                                         MulDiv(gwclip.colorpen[i].lopnWidth.x,
1368                                                gwclip.xmax, rect.right-rect.left);
1369                 }
1370
1371                 rect.right = lpgw->xmax;
1372                 rect.bottom = lpgw->ymax;
1373
1374                 MakePens(&gwclip, hdc);
1375                 MakeFonts(&gwclip, &rect, hdc);
1376
1377                 ReleaseDC(hwnd, hdc);
1378
1379                 hdc = CreateMetaFile((LPSTR)NULL);
1380
1381 /* HBB 981203: According to Petzold, Metafiles shouldn't contain SetMapMode() calls: */
1382         /*SetMapMode(hdc, MM_ANISOTROPIC);*/
1383 #ifdef WIN32
1384                 SetWindowExtEx(hdc, rect.right, rect.bottom, (LPSIZE)NULL);
1385 #else
1386                 SetWindowExt(hdc, rect.right, rect.bottom);
1387 #endif
1388                 drawgraph(&gwclip, hdc, (LPRECT) &rect);
1389                 hmf = CloseMetaFile(hdc);
1390                 DestroyFonts(&gwclip);
1391                 DestroyPens(&gwclip);
1392         }
1393
1394         /* Now we have the Metafile and Bitmap prepared, post their contents to
1395          * the Clipboard */
1396
1397         hGMem = GlobalAlloc(GMEM_MOVEABLE, (DWORD)sizeof(METAFILEPICT));
1398         lpMFP = (LPMETAFILEPICT) GlobalLock(hGMem);
1399         hdc = GetDC(hwnd);      /* get window size */
1400         GetClientRect(hwnd, &rect);
1401         /* in MM_ANISOTROPIC, xExt & yExt give suggested size in 0.01mm units */
1402         lpMFP->mm = MM_ANISOTROPIC;
1403         lpMFP->xExt = MulDiv(rect.right-rect.left, 2540, GetDeviceCaps(hdc, LOGPIXELSX));
1404         lpMFP->yExt = MulDiv(rect.bottom-rect.top, 2540, GetDeviceCaps(hdc, LOGPIXELSY));
1405         lpMFP->hMF = hmf;
1406         ReleaseDC(hwnd, hdc);
1407         GlobalUnlock(hGMem);
1408
1409         OpenClipboard(hwnd);
1410         EmptyClipboard();
1411         SetClipboardData(CF_METAFILEPICT,hGMem);
1412         SetClipboardData(CF_BITMAP, bitmap);
1413         CloseClipboard();
1414         return;
1415 }
1416
1417 /* copy graph window to printer */
1418 static void
1419 CopyPrint(LPGW lpgw)
1420 {
1421 #ifdef WIN32
1422         DOCINFO docInfo;
1423 #endif
1424
1425 #if WINVER >= 0x030a    /* If Win 3.0, this whole function does nothing at all ... */
1426         HDC printer;
1427 #ifndef WIN32
1428         DLGPROC lpfnAbortProc;
1429         DLGPROC lpfnPrintDlgProc;
1430 #endif
1431         PRINTDLG pd;
1432         HWND hwnd;
1433         RECT rect;
1434         GP_PRINT pr;
1435
1436         hwnd = lpgw->hWndGraph;
1437
1438         _fmemset(&pd, 0, sizeof(PRINTDLG));
1439         pd.lStructSize = sizeof(PRINTDLG);
1440         pd.hwndOwner = hwnd;
1441         pd.Flags = PD_PRINTSETUP | PD_RETURNDC;
1442
1443         if (!PrintDlg(&pd))
1444                 return;
1445         printer = pd.hDC;
1446         if (NULL == printer)
1447                 return; /* abort */
1448
1449         if (!PrintSize(printer, hwnd, &rect)) {
1450                 DeleteDC(printer);
1451                 return; /* abort */
1452         }
1453
1454         pr.hdcPrn = printer;
1455         SetWindowLong(hwnd, 4, (LONG)((GP_LPPRINT)&pr));
1456 #ifdef WIN32
1457         PrintRegister((GP_LPPRINT)&pr);
1458 #endif
1459
1460         EnableWindow(hwnd,FALSE);
1461         pr.bUserAbort = FALSE;
1462 #ifdef WIN32
1463         pr.hDlgPrint = CreateDialogParam(hdllInstance,"CancelDlgBox",hwnd,PrintDlgProc,(LPARAM)lpgw->Title);
1464         SetAbortProc(printer,PrintAbortProc);
1465
1466         memset(&docInfo, 0, sizeof(DOCINFO));
1467         docInfo.cbSize = sizeof(DOCINFO);
1468         docInfo.lpszDocName = lpgw->Title;
1469
1470         if (StartDoc(printer, &docInfo) > 0) {
1471 #else /* not WIN32 */
1472 #  ifdef __DLL__
1473         lpfnPrintDlgProc = (DLGPROC)GetProcAddress(hdllInstance, "PrintDlgProc");
1474         lpfnAbortProc = (DLGPROC)GetProcAddress(hdllInstance, "PrintAbortProc");
1475 #  else /* __DLL__ */
1476         lpfnPrintDlgProc = (DLGPROC)MakeProcInstance((FARPROC)PrintDlgProc, hdllInstance);
1477         lpfnAbortProc = (DLGPROC)MakeProcInstance((FARPROC)PrintAbortProc, hdllInstance);
1478 #  endif /* __DLL__ */
1479         pr.hDlgPrint = CreateDialogParam(hdllInstance,"CancelDlgBox",hwnd,lpfnPrintDlgProc,(LPARAM)lpgw->Title);
1480         Escape(printer,SETABORTPROC,0,(LPSTR)lpfnAbortProc,NULL);
1481         if (Escape(printer, STARTDOC, lstrlen(lpgw->Title),lpgw->Title, NULL) > 0) {
1482 #endif
1483                 SetMapMode(printer, MM_TEXT);
1484                 SetBkMode(printer,OPAQUE);
1485 #ifdef WIN32
1486                 StartPage(printer);
1487 #endif
1488                 DestroyFonts(lpgw);
1489                 MakeFonts(lpgw, (RECT FAR *)&rect, printer);
1490                 DestroyPens(lpgw);      /* rebuild pens */
1491                 MakePens(lpgw, printer);
1492                 drawgraph(lpgw, printer, (void *) &rect);
1493 #ifdef WIN32
1494                 if (EndPage(printer) > 0)
1495                         EndDoc(printer);
1496 # else /* WIN32 */
1497                 if (Escape(printer,NEWFRAME,0,NULL,NULL) > 0)
1498                         Escape(printer,ENDDOC,0,NULL,NULL);
1499 # endif /* WIN32 */
1500         }
1501         if (!pr.bUserAbort) {
1502                 EnableWindow(hwnd,TRUE);
1503                 DestroyWindow(pr.hDlgPrint);
1504         }
1505 #ifndef WIN32
1506 #ifndef __DLL__
1507         FreeProcInstance((FARPROC)lpfnPrintDlgProc);
1508         FreeProcInstance((FARPROC)lpfnAbortProc);
1509 # endif /* __DLL__ */
1510 #endif /* WIN32 */
1511         DeleteDC(printer);
1512         SetWindowLong(hwnd, 4, (LONG)(0L));
1513 #ifdef WIN32
1514         PrintUnregister((GP_LPPRINT)&pr);
1515 #endif /* WIN32 */
1516         /* make certain that the screen pen set is restored */
1517         SendMessage(lpgw->hWndGraph,WM_COMMAND,M_REBUILDTOOLS,0L);
1518 #endif /* WIN Version >= 3.1 */
1519         return;
1520 }
1521
1522 /* ================================== */
1523 /*  INI file stuff */
1524 static void
1525 WriteGraphIni(LPGW lpgw)
1526 {
1527         RECT rect;
1528         int i;
1529         char entry[32];
1530         LPLOGPEN pc;
1531         LPLOGPEN pm;
1532         LPSTR file = lpgw->IniFile;
1533         LPSTR section = lpgw->IniSection;
1534         char profile[80];
1535
1536         if ((file == (LPSTR)NULL) || (section == (LPSTR)NULL))
1537                 return;
1538         if (IsIconic(lpgw->hWndGraph))
1539                 ShowWindow(lpgw->hWndGraph, SW_SHOWNORMAL);
1540         GetWindowRect(lpgw->hWndGraph,&rect);
1541         wsprintf(profile, "%d %d", rect.left, rect.top);
1542         WritePrivateProfileString(section, "GraphOrigin", profile, file);
1543         wsprintf(profile, "%d %d", rect.right-rect.left, rect.bottom-rect.top);
1544         WritePrivateProfileString(section, "GraphSize", profile, file);
1545         wsprintf(profile, "%s,%d", lpgw->deffontname, lpgw->deffontsize);
1546         WritePrivateProfileString(section, "GraphFont", profile, file);
1547         wsprintf(profile, "%d", lpgw->color);
1548         WritePrivateProfileString(section, "GraphColor", profile, file);
1549         wsprintf(profile, "%d", lpgw->graphtotop);
1550         WritePrivateProfileString(section, "GraphToTop", profile, file);
1551         wsprintf(profile, "%d %d %d",GetRValue(lpgw->background),
1552                         GetGValue(lpgw->background), GetBValue(lpgw->background));
1553         WritePrivateProfileString(section, "GraphBackground", profile, file);
1554
1555         /* now save pens */
1556         for (i=0; i<WGNUMPENS+2; i++) {
1557                 if (i==0)
1558                         _fstrcpy(entry,"Border");
1559                 else if (i==1)
1560                         _fstrcpy(entry,"Axis");
1561                 else
1562                          wsprintf(entry,"Line%d",i-1);
1563                 pc = &lpgw->colorpen[i];
1564                 pm = &lpgw->monopen[i];
1565                 wsprintf(profile, "%d %d %d %d %d",GetRValue(pc->lopnColor),
1566                         GetGValue(pc->lopnColor), GetBValue(pc->lopnColor),
1567                         (pc->lopnWidth.x != 1) ? -pc->lopnWidth.x : pc->lopnStyle,
1568                         (pm->lopnWidth.x != 1) ? -pm->lopnWidth.x : pm->lopnStyle);
1569                 WritePrivateProfileString(section, entry, profile, file);
1570         }
1571         return;
1572 }
1573
1574 static void
1575 ReadGraphIni(LPGW lpgw)
1576 {
1577         LPSTR file = lpgw->IniFile;
1578         LPSTR section = lpgw->IniSection;
1579         char profile[81];
1580         char entry[32];
1581         LPSTR p;
1582         int i,r,g,b,colorstyle,monostyle;
1583         COLORREF ref;
1584         BOOL bOKINI;
1585
1586         bOKINI = (file != (LPSTR)NULL) && (section != (LPSTR)NULL);
1587         if (!bOKINI)
1588                 profile[0] = '\0';
1589
1590         if (bOKINI)
1591           GetPrivateProfileString(section, "GraphOrigin", "", profile, 80, file);
1592         if ( (p = GetInt(profile, (LPINT)&lpgw->Origin.x)) == NULL)
1593                 lpgw->Origin.x = CW_USEDEFAULT;
1594         if ( (p = GetInt(p, (LPINT)&lpgw->Origin.y)) == NULL)
1595                 lpgw->Origin.y = CW_USEDEFAULT;
1596         if (bOKINI)
1597           GetPrivateProfileString(section, "GraphSize", "", profile, 80, file);
1598         if ( (p = GetInt(profile, (LPINT)&lpgw->Size.x)) == NULL)
1599                 lpgw->Size.x = CW_USEDEFAULT;
1600         if ( (p = GetInt(p, (LPINT)&lpgw->Size.y)) == NULL)
1601                 lpgw->Size.y = CW_USEDEFAULT;
1602
1603         if (bOKINI)
1604           GetPrivateProfileString(section, "GraphFont", "", profile, 80, file);
1605         {
1606                 char FAR *size;
1607                 size = _fstrchr(profile,',');
1608                 if (size) {
1609                         *size++ = '\0';
1610                         if ( (p = GetInt(size, (LPINT)&lpgw->fontsize)) == NULL)
1611                                 lpgw->fontsize = WINFONTSIZE;
1612                 }
1613                 _fstrcpy(lpgw->fontname, profile);
1614                 if (lpgw->fontsize == 0)
1615                         lpgw->fontsize = WINFONTSIZE;
1616                 if (!(*lpgw->fontname)) {
1617                         if (LOWORD(GetVersion()) == 3)
1618                                 _fstrcpy(lpgw->fontname,WIN30FONT);
1619                         else
1620                                 _fstrcpy(lpgw->fontname,WINFONT);
1621                 }
1622                 /* set current font as default font */
1623                 _fstrcpy(lpgw->deffontname, lpgw->fontname);
1624                 lpgw->deffontsize = lpgw->fontsize;
1625         }
1626
1627         if (bOKINI)
1628           GetPrivateProfileString(section, "GraphColor", "", profile, 80, file);
1629                 if ( (p = GetInt(profile, (LPINT)&lpgw->color)) == NULL)
1630                         lpgw->color = TRUE;
1631
1632         if (bOKINI)
1633           GetPrivateProfileString(section, "GraphToTop", "", profile, 80, file);
1634                 if ( (p = GetInt(profile, (LPINT)&lpgw->graphtotop)) == NULL)
1635                         lpgw->graphtotop = TRUE;
1636
1637         lpgw->background = RGB(255,255,255);
1638         if (bOKINI)
1639           GetPrivateProfileString(section, "GraphBackground", "", profile, 80, file);
1640         if ( ((p = GetInt(profile, (LPINT)&r)) != NULL) &&
1641              ((p = GetInt(p, (LPINT)&g)) != NULL) &&
1642              ((p = GetInt(p, (LPINT)&b)) != NULL) )
1643                         lpgw->background = RGB(r,g,b);
1644
1645         StorePen(lpgw, 0,RGB(0,0,0),PS_SOLID,PS_SOLID);
1646         if (bOKINI)
1647           GetPrivateProfileString(section, "Border", "", profile, 80, file);
1648         if ( ((p = GetInt(profile, (LPINT)&r)) != NULL) &&
1649              ((p = GetInt(p, (LPINT)&g)) != NULL) &&
1650              ((p = GetInt(p, (LPINT)&b)) != NULL) &&
1651              ((p = GetInt(p, (LPINT)&colorstyle)) != NULL) &&
1652              ((p = GetInt(p, (LPINT)&monostyle)) != NULL) )
1653                         StorePen(lpgw,0,RGB(r,g,b),colorstyle,monostyle);
1654
1655         StorePen(lpgw, 1,RGB(192,192,192),PS_DOT,PS_DOT);
1656         if (bOKINI)
1657           GetPrivateProfileString(section, "Axis", "", profile, 80, file);
1658         if ( ((p = GetInt(profile, (LPINT)&r)) != NULL) &&
1659              ((p = GetInt(p, (LPINT)&g)) != NULL) &&
1660              ((p = GetInt(p, (LPINT)&b)) != NULL) &&
1661              ((p = GetInt(p, (LPINT)&colorstyle)) != NULL) &&
1662              ((p = GetInt(p, (LPINT)&monostyle)) != NULL) )
1663                         StorePen(lpgw,1,RGB(r,g,b),colorstyle,monostyle);
1664
1665         for (i=0; i<WGNUMPENS; i++)
1666         {
1667                 ref = wginitcolor[ i%WGDEFCOLOR ];
1668                 colorstyle = wginitstyle[ (i/WGDEFCOLOR) % WGDEFSTYLE ];
1669                 monostyle  = wginitstyle[ i%WGDEFSTYLE ];
1670                 StorePen(lpgw, i+2,ref,colorstyle,monostyle);
1671                 wsprintf(entry,"Line%d",i+1);
1672                 if (bOKINI)
1673                   GetPrivateProfileString(section, entry, "", profile, 80, file);
1674                 if ( ((p = GetInt(profile, (LPINT)&r)) != NULL) &&
1675                      ((p = GetInt(p, (LPINT)&g)) != NULL) &&
1676                      ((p = GetInt(p, (LPINT)&b)) != NULL) &&
1677                      ((p = GetInt(p, (LPINT)&colorstyle)) != NULL) &&
1678                      ((p = GetInt(p, (LPINT)&monostyle)) != NULL) )
1679                                 StorePen(lpgw,i+2,RGB(r,g,b),colorstyle,monostyle);
1680         }
1681 }
1682
1683
1684 /* ================================== */
1685
1686 /* the "Line Styles..." dialog and its support functions */
1687 /* FIXME HBB 20010218: this might better be delegated to a separate source file */
1688
1689 #define LS_DEFLINE 2
1690 typedef struct tagLS {
1691         int     widtype;
1692         int     wid;
1693         HWND    hwnd;
1694         int     pen;                    /* current pen number */
1695         LOGPEN  colorpen[WGNUMPENS+2];  /* logical color pens */
1696         LOGPEN  monopen[WGNUMPENS+2];   /* logical mono pens */
1697 } LS;
1698 typedef LS FAR*  LPLS;
1699
1700
1701 static COLORREF
1702 GetColor(HWND hwnd, COLORREF ref)
1703 {
1704         CHOOSECOLOR cc;
1705         COLORREF aclrCust[16];
1706         int i;
1707
1708         for (i=0; i<16; i++) {
1709                 aclrCust[i] = RGB(0,0,0);
1710         }
1711         _fmemset(&cc, 0, sizeof(CHOOSECOLOR));
1712         cc.lStructSize = sizeof(CHOOSECOLOR);
1713         cc.hwndOwner = hwnd;
1714         cc.lpCustColors = aclrCust;
1715         cc.rgbResult = ref;
1716         cc.Flags = CC_RGBINIT;
1717         if (ChooseColor(&cc))
1718                 return cc.rgbResult;
1719         return ref;
1720 }
1721
1722
1723 /* force update of owner draw button */
1724 static void
1725 UpdateColorSample(HWND hdlg)
1726 {
1727         RECT rect;
1728         POINT ptul, ptlr;
1729         GetWindowRect( GetDlgItem(hdlg, LS_COLORSAMPLE), &rect);
1730         ptul.x = rect.left;
1731         ptul.y = rect.top;
1732         ptlr.x = rect.right;
1733         ptlr.y = rect.bottom;
1734         ScreenToClient(hdlg, &ptul);
1735         ScreenToClient(hdlg, &ptlr);
1736         rect.left   = ptul.x;
1737         rect.top    = ptul.y;
1738         rect.right  = ptlr.x;
1739         rect.bottom = ptlr.y;
1740         InvalidateRect(hdlg, &rect, TRUE);
1741         UpdateWindow(hdlg);
1742 }
1743
1744 /* Window handler function for the "Line Styles" dialog */
1745 BOOL CALLBACK WINEXPORT
1746 LineStyleDlgProc(HWND hdlg, UINT wmsg, WPARAM wparam, LPARAM lparam)
1747 {
1748         char buf[16];
1749         LPLS lpls;
1750         int i;
1751         UINT pen;
1752         LPLOGPEN plpm, plpc;
1753         lpls = (LPLS)GetWindowLong(GetParent(hdlg), 4);
1754
1755         switch (wmsg) {
1756                 case WM_INITDIALOG:
1757                         pen = 2;
1758                         for (i=0; i<WGNUMPENS+2; i++) {
1759                                 if (i==0)
1760                                         _fstrcpy(buf,"Border");
1761                                 else if (i==1)
1762                                         _fstrcpy(buf,"Axis");
1763                                 else
1764                                         wsprintf(buf,"Line%d",i-1);
1765                                 SendDlgItemMessage(hdlg, LS_LINENUM, LB_ADDSTRING, 0,
1766                                         (LPARAM)((LPSTR)buf));
1767                         }
1768                         SendDlgItemMessage(hdlg, LS_LINENUM, LB_SETCURSEL, pen, 0L);
1769
1770                         SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
1771                                 (LPARAM)((LPSTR)"Solid"));
1772                         SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
1773                                 (LPARAM)((LPSTR)"Dash"));
1774                         SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
1775                                 (LPARAM)((LPSTR)"Dot"));
1776                         SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
1777                                 (LPARAM)((LPSTR)"DashDot"));
1778                         SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_ADDSTRING, 0,
1779                                 (LPARAM)((LPSTR)"DashDotDot"));
1780
1781                         plpm = &lpls->monopen[pen];
1782                         SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_SETCURSEL,
1783                                 plpm->lopnStyle, 0L);
1784                         wsprintf(buf,"%d",plpm->lopnWidth.x);
1785                         SetDlgItemText(hdlg, LS_MONOWIDTH, buf);
1786
1787                         SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
1788                                 (LPARAM)((LPSTR)"Solid"));
1789                         SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
1790                                 (LPARAM)((LPSTR)"Dash"));
1791                         SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
1792                                 (LPARAM)((LPSTR)"Dot"));
1793                         SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
1794                                 (LPARAM)((LPSTR)"DashDot"));
1795                         SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_ADDSTRING, 0,
1796                                 (LPARAM)((LPSTR)"DashDotDot"));
1797
1798                         plpc = &lpls->colorpen[pen];
1799                         SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_SETCURSEL,
1800                                 plpc->lopnStyle, 0L);
1801                         wsprintf(buf,"%d",plpc->lopnWidth.x);
1802                         SetDlgItemText(hdlg, LS_COLORWIDTH, buf);
1803
1804                         return TRUE;
1805                 case WM_COMMAND:
1806                         pen = (UINT)SendDlgItemMessage(hdlg, LS_LINENUM, LB_GETCURSEL, 0, 0L);
1807                         plpm = &lpls->monopen[pen];
1808                         plpc = &lpls->colorpen[pen];
1809                         switch (LOWORD(wparam)) {
1810                                 case LS_LINENUM:
1811                                         wsprintf(buf,"%d",plpm->lopnWidth.x);
1812                                         SetDlgItemText(hdlg, LS_MONOWIDTH, buf);
1813                                         SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_SETCURSEL,
1814                                                 plpm->lopnStyle, 0L);
1815                                         wsprintf(buf,"%d",plpc->lopnWidth.x);
1816                                         SetDlgItemText(hdlg, LS_COLORWIDTH, buf);
1817                                         SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_SETCURSEL,
1818                                                 plpc->lopnStyle, 0L);
1819                                         UpdateColorSample(hdlg);
1820                                         return FALSE;
1821                                 case LS_MONOSTYLE:
1822                                         plpm->lopnStyle =
1823                                                 (UINT)SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_GETCURSEL, 0, 0L);
1824                                         if (plpm->lopnStyle != 0) {
1825                                                 plpm->lopnWidth.x = 1;
1826                                                 wsprintf(buf,"%d",plpm->lopnWidth.x);
1827                                                 SetDlgItemText(hdlg, LS_MONOWIDTH, buf);
1828                                         }
1829                                         return FALSE;
1830                                 case LS_MONOWIDTH:
1831                                         GetDlgItemText(hdlg, LS_MONOWIDTH, buf, 15);
1832                                         GetInt(buf, (LPINT)&plpm->lopnWidth.x);
1833                                         if (plpm->lopnWidth.x != 1) {
1834                                                 plpm->lopnStyle = 0;
1835                                                 SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_SETCURSEL,
1836                                                         plpm->lopnStyle, 0L);
1837                                         }
1838                                         return FALSE;
1839                                 case LS_CHOOSECOLOR:
1840                                         plpc->lopnColor = GetColor(hdlg, plpc->lopnColor);
1841                                         UpdateColorSample(hdlg);
1842                                         return FALSE;
1843                                 case LS_COLORSTYLE:
1844                                         plpc->lopnStyle =
1845                                                 (UINT)SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_GETCURSEL, 0, 0L);
1846                                         if (plpc->lopnStyle != 0) {
1847                                                 plpc->lopnWidth.x = 1;
1848                                                 wsprintf(buf,"%d",plpc->lopnWidth.x);
1849                                                 SetDlgItemText(hdlg, LS_COLORWIDTH, buf);
1850                                         }
1851                                         return FALSE;
1852                                 case LS_COLORWIDTH:
1853                                         GetDlgItemText(hdlg, LS_COLORWIDTH, buf, 15);
1854                                         GetInt(buf, (LPINT)&plpc->lopnWidth.x);
1855                                         if (plpc->lopnWidth.x != 1) {
1856                                                 plpc->lopnStyle = 0;
1857                                                 SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_SETCURSEL,
1858                                                         plpc->lopnStyle, 0L);
1859                                         }
1860                                         return FALSE;
1861                                 case LS_DEFAULT:
1862                                         plpm = lpls->monopen;
1863                                         plpc = lpls->colorpen;
1864                                         /* border */
1865                                         plpc->lopnColor   = RGB(0,0,0);
1866                                         plpc->lopnStyle   = PS_SOLID;
1867                                         plpc->lopnWidth.x = 1;
1868                                         plpm->lopnStyle   = PS_SOLID;
1869                                         plpm->lopnWidth.x = 1;
1870                                         plpc++; plpm++;
1871                                         /* axis */
1872                                         plpc->lopnColor   = RGB(192,192,192);
1873                                         plpc->lopnStyle   = PS_DOT;
1874                                         plpc->lopnWidth.x = 1;
1875                                         plpm->lopnStyle   = PS_DOT;
1876                                         plpm->lopnWidth.x = 1;
1877                                         /* LineX */
1878                                         for (i=0; i<WGNUMPENS; i++) {
1879                                                 plpc++; plpm++;
1880                                                 plpc->lopnColor   = wginitcolor[ i%WGDEFCOLOR ];
1881                                                 plpc->lopnStyle   = wginitstyle[ (i/WGDEFCOLOR) % WGDEFSTYLE ];
1882                                                 plpc->lopnWidth.x = 1;
1883                                                 plpm->lopnStyle   = wginitstyle[ i%WGDEFSTYLE ];
1884                                                 plpm->lopnWidth.x = 1;
1885                                         }
1886                                         /* update window */
1887                                         plpm = &lpls->monopen[pen];
1888                                         plpc = &lpls->colorpen[pen];
1889                                         SendDlgItemMessage(hdlg, LS_LINENUM, LB_SETCURSEL, pen, 0L);
1890                                         wsprintf(buf,"%d",plpm->lopnWidth.x);
1891                                         SetDlgItemText(hdlg, LS_MONOWIDTH, buf);
1892                                         SendDlgItemMessage(hdlg, LS_MONOSTYLE, CB_SETCURSEL,
1893                                                 plpm->lopnStyle, 0L);
1894                                         wsprintf(buf,"%d",plpc->lopnWidth.x);
1895                                         SetDlgItemText(hdlg, LS_COLORWIDTH, buf);
1896                                         SendDlgItemMessage(hdlg, LS_COLORSTYLE, CB_SETCURSEL,
1897                                                 plpc->lopnStyle, 0L);
1898                                         UpdateColorSample(hdlg);
1899                                         return FALSE;
1900                                 case IDOK:
1901                                         EndDialog(hdlg, IDOK);
1902                                         return TRUE;
1903                                 case IDCANCEL:
1904                                         EndDialog(hdlg, IDCANCEL);
1905                                         return TRUE;
1906                         }
1907                         break;
1908                 case WM_DRAWITEM:
1909                         {
1910                         HBRUSH hBrush;
1911                         LPDRAWITEMSTRUCT lpdis = (LPDRAWITEMSTRUCT)lparam;
1912                         pen = (UINT)SendDlgItemMessage(hdlg, LS_LINENUM, LB_GETCURSEL, (WPARAM)0, (LPARAM)0);
1913                         plpc = &lpls->colorpen[pen];
1914                         hBrush = CreateSolidBrush(plpc->lopnColor);
1915                         FillRect(lpdis->hDC, &lpdis->rcItem, hBrush);
1916                         FrameRect(lpdis->hDC, &lpdis->rcItem, (HBRUSH)GetStockObject(BLACK_BRUSH));
1917                         DeleteObject(hBrush);
1918                         }
1919                         return FALSE;
1920         }
1921         return FALSE;
1922 }
1923
1924
1925
1926 /* GetWindowLong(hwnd, 4) must be available for use */
1927 static BOOL
1928 LineStyle(LPGW lpgw)
1929 {
1930         BOOL status = FALSE;
1931         LS ls;
1932 #ifndef WIN32
1933         DLGPROC lpfnLineStyleDlgProc;
1934 #endif
1935
1936         SetWindowLong(lpgw->hWndGraph, 4, (LONG)((LPLS)&ls));
1937         _fmemcpy(&ls.colorpen, &lpgw->colorpen, (WGNUMPENS + 2) * sizeof(LOGPEN));
1938         _fmemcpy(&ls.monopen, &lpgw->monopen, (WGNUMPENS + 2) * sizeof(LOGPEN));
1939
1940 #ifdef WIN32
1941         if (DialogBox (hdllInstance, "LineStyleDlgBox", lpgw->hWndGraph, LineStyleDlgProc)
1942 #else
1943 # ifdef __DLL__
1944         lpfnLineStyleDlgProc = (DLGPROC)GetProcAddress(hdllInstance, "LineStyleDlgProc");
1945 # else
1946         lpfnLineStyleDlgProc = (DLGPROC)MakeProcInstance((FARPROC)LineStyleDlgProc, hdllInstance);
1947 # endif
1948         if (DialogBox (hdllInstance, "LineStyleDlgBox", lpgw->hWndGraph, lpfnLineStyleDlgProc)
1949 #endif
1950                 == IDOK) {
1951                 _fmemcpy(&lpgw->colorpen, &ls.colorpen, (WGNUMPENS + 2) * sizeof(LOGPEN));
1952                 _fmemcpy(&lpgw->monopen, &ls.monopen, (WGNUMPENS + 2) * sizeof(LOGPEN));
1953                 status = TRUE;
1954         }
1955 #ifndef WIN32
1956 # ifndef __DLL__
1957         FreeProcInstance((FARPROC)lpfnLineStyleDlgProc);
1958 # endif
1959 #endif
1960         SetWindowLong(lpgw->hWndGraph, 4, (LONG)(0L));
1961         return status;
1962 }
1963
1964 #ifdef USE_MOUSE
1965 /* ================================== */
1966 /* HBB 20010207: new helper functions: wrapper around gp_exec_event
1967  * and DoZoombox. These may vanish again, as has the original idea I
1968  * invented them for... */
1969
1970 static void
1971 Wnd_exec_event(LPGW lpgw, LPARAM lparam, char type, int par1)
1972 {
1973     int mx, my;
1974     static unsigned long lastTimestamp = 0;
1975     unsigned long thisTimestamp = GetMessageTime();
1976     int par2 = thisTimestamp - lastTimestamp;
1977
1978     if (type == GE_keypress)
1979         par2 = 0;
1980
1981     GetMousePosViewport(lpgw, &mx, &my);
1982     gp_exec_event(type, mx, my, par1, par2, 0);
1983     lastTimestamp = thisTimestamp;
1984 }
1985
1986 static void
1987 Wnd_refresh_zoombox(LPGW lpgw, LPARAM lParam)
1988 {
1989     int mx, my;
1990
1991     GetMousePosViewport(lpgw, &mx, &my);
1992     DrawZoomBox(lpgw); /*  erase current zoom box */
1993     zoombox.to.x = mx; zoombox.to.y = my;
1994     DrawZoomBox(lpgw); /*  draw new zoom box */
1995 }
1996
1997 static void
1998 Wnd_refresh_ruler_lineto(LPGW lpgw, LPARAM lParam)
1999 {
2000     int mx, my;
2001
2002     GetMousePosViewport(lpgw, &mx, &my);
2003     DrawRulerLineTo(lpgw); /*  erase current line */
2004     ruler_lineto.x = mx; ruler_lineto.y = my;
2005     DrawRulerLineTo(lpgw); /*  draw new line box */
2006 }
2007 #endif /* USE_MOUSE */
2008
2009 /* ================================== */
2010
2011 /* The toplevel function of this module: Window handler function of the graph window */
2012 LRESULT CALLBACK WINEXPORT
2013 WndGraphProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
2014 {
2015         HDC hdc;
2016         PAINTSTRUCT ps;
2017         RECT rect;
2018         LPGW lpgw;
2019         HMENU sysmenu;
2020         int i;
2021 #ifdef USE_MOUSE
2022         static unsigned int last_modifier_mask = -99;
2023 #endif
2024
2025         lpgw = (LPGW)GetWindowLong(hwnd, 0);
2026
2027 #ifdef USE_MOUSE
2028         /*  mouse events first */
2029         if (mouse_setting.on /* AND NOT mouse_lock */ ) {
2030                 switch (message) {
2031                         case WM_MOUSEMOVE:
2032 #if 1
2033                                 SetCursor(hptrCurrent);
2034 #else
2035                                 SetCursor(hptrCrossHair);
2036 #endif
2037                                 if (zoombox.on) {
2038                                         Wnd_refresh_zoombox(lpgw, lParam);
2039                                 }
2040                                 if (ruler.on && ruler_lineto.on) {
2041                                         Wnd_refresh_ruler_lineto(lpgw, lParam);
2042                                 }
2043                                 /* track (show) mouse position -- send the event to gnuplot */
2044                                 Wnd_exec_event(lpgw, lParam,  GE_motion, wParam);
2045                                 return 0L; /* end of WM_MOUSEMOVE */
2046
2047                         case WM_LBUTTONDOWN:
2048                                 Wnd_exec_event(lpgw, lParam, GE_buttonpress, 1);
2049                                 return 0L;
2050
2051                         case WM_RBUTTONDOWN:
2052                                 /* FIXME HBB 20010207: this collides with the right-click
2053                                  * context menu !!! */
2054                                 Wnd_exec_event(lpgw, lParam, GE_buttonpress,  3);
2055                                 return 0L;
2056
2057                         case WM_MBUTTONDOWN:
2058                                 Wnd_exec_event(lpgw, lParam, GE_buttonpress, 2);
2059                                 return 0L;
2060
2061                         case WM_LBUTTONDBLCLK:
2062                                 Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 1);
2063                                 return 0L;
2064
2065                         case WM_RBUTTONDBLCLK:
2066                                 Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 3);
2067                                 return 0L;
2068
2069                         case WM_MBUTTONDBLCLK:
2070                                 Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 2);
2071                                 return 0L;
2072
2073 #if 1
2074                         case WM_LBUTTONUP:
2075 #else
2076                         case WM_LBUTTONCLICK:
2077 #endif
2078                                 Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 1);
2079                                 return 0L;
2080
2081 #if 1
2082                         case WM_RBUTTONUP:
2083 #else
2084                         case WM_RBUTTONCLICK:
2085 #endif
2086                                 Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 3);
2087                                 return 0L;
2088
2089 #if 1
2090                         case WM_MBUTTONUP:
2091 #else
2092                         case WM_MBUTTONCLICK:
2093 #endif
2094                                 Wnd_exec_event(lpgw, lParam, GE_buttonrelease, 2);
2095                                 return 0L;
2096
2097                 } /* switch over mouse events */
2098         }
2099 #endif /* USE_MOUSE */
2100
2101
2102
2103         switch(message)
2104         {
2105                 case WM_SYSCOMMAND:
2106                         switch(LOWORD(wParam))
2107                         {
2108                                 case M_GRAPH_TO_TOP:
2109                                 case M_COLOR:
2110                                 case M_CHOOSE_FONT:
2111                                 case M_COPY_CLIP:
2112                                 case M_LINESTYLE:
2113                                 case M_BACKGROUND:
2114                                 case M_PRINT:
2115                                 case M_WRITEINI:
2116                                 case M_REBUILDTOOLS:
2117                                         SendMessage(hwnd, WM_COMMAND, wParam, lParam);
2118                                         break;
2119                                 case M_ABOUT:
2120                                         if (lpgw->lptw)
2121                                                 AboutBox(hwnd,lpgw->lptw->AboutText);
2122                                         return 0;
2123                                 case M_COMMANDLINE:
2124                                         sysmenu = GetSystemMenu(lpgw->hWndGraph,0);
2125                                         i = GetMenuItemCount (sysmenu);
2126                                         DeleteMenu (sysmenu, --i, MF_BYPOSITION);
2127                                         DeleteMenu (sysmenu, --i, MF_BYPOSITION);
2128                                         ShowWindow (lpgw->lptw->hWndParent, SW_SHOW);
2129                                         break;
2130                         }
2131                         break;
2132                 case WM_CHAR:
2133                         /* All 'normal' keys (letters, digits and the likes) end up
2134                          * here... */
2135                         if (wParam == VK_SPACE) {
2136                                 /* HBB 20001023: implement the '<space> in graph returns to
2137                                  * text window' --- feature already present in OS/2 and X11 */
2138                                 /* Make sure the text window is visible: */
2139                                 ShowWindow(lpgw->lptw->hWndParent, SW_SHOW);
2140                                 /* and activate it (--> Keyboard focus goes there */
2141                                 BringWindowToTop(lpgw->lptw->hWndParent);
2142                                 return 0;
2143                         }
2144 #ifdef USE_MOUSE
2145                         Wnd_exec_event(lpgw, lParam, GE_keypress, (TCHAR)wParam);
2146 #endif
2147                         return 0L;
2148 #ifdef USE_MOUSE
2149                 /* "special" keys have to be caught from WM_KEYDOWN, as they
2150                  * don't generate WM_CHAR messages. */
2151                 /* NB: It may not be possible to catch Alt-keys, this way */
2152                 case WM_KEYUP:
2153                         {
2154                                 /* First, look for a change in modifier status */
2155                                 unsigned int modifier_mask = 0;
2156                                 modifier_mask = ((GetKeyState(VK_SHIFT) < 0) ? Mod_Shift : 0 )
2157                                         | ((GetKeyState(VK_CONTROL) < 0) ? Mod_Ctrl : 0)
2158                                         | ((GetKeyState(VK_MENU) < 0) ? Mod_Alt : 0);
2159                                 if (modifier_mask != last_modifier_mask) {
2160                                         Wnd_exec_event ( lpgw, lParam, GE_modifier, modifier_mask);
2161                                         last_modifier_mask = modifier_mask;
2162                                 }
2163                         }
2164                         /* Ignore Key-Up events other than those of modifier keys */
2165                         break;
2166                 case WM_KEYDOWN:
2167                         {
2168                                 /* First, look for a change in modifier status */
2169                                 unsigned int modifier_mask = 0;
2170                                 modifier_mask = ((GetKeyState(VK_SHIFT) < 0) ? Mod_Shift : 0 )
2171                                         | ((GetKeyState(VK_CONTROL) < 0) ? Mod_Ctrl : 0)
2172                                         | ((GetKeyState(VK_MENU) < 0) ? Mod_Alt : 0);
2173                                 if (modifier_mask != last_modifier_mask) {
2174                                         Wnd_exec_event ( lpgw, lParam, GE_modifier, modifier_mask);
2175                                         last_modifier_mask = modifier_mask;
2176                                 }
2177                         }
2178                         switch (wParam) {
2179                         case VK_BACK:
2180                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_BackSpace);
2181                                 break;
2182                         case VK_TAB:
2183                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Tab);
2184                                 break;
2185                         case VK_RETURN:
2186                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Return);
2187                                 break;
2188                         case VK_PAUSE:
2189                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Pause);
2190                                 break;
2191                         case VK_SCROLL:
2192                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Scroll_Lock);
2193                                 break;
2194 #if 0 /* HOW_IS_THIS_FOR_WINDOWS */
2195 /* HBB 20010215: not at all, AFAICS... :-( */
2196                         case VK_SYSRQ:
2197                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Sys_Req);
2198                                 break;
2199 #endif
2200                         case VK_ESCAPE:
2201                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Escape);
2202                                 break;
2203                         case VK_DELETE:
2204                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Delete);
2205                                 break;
2206                         case VK_INSERT:
2207                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_KP_Insert);
2208                                 break;
2209                         case VK_HOME:
2210                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Home);
2211                                 break;
2212                         case VK_LEFT:
2213                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Left);
2214                                 break;
2215                         case VK_UP:
2216                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Up);
2217                                 break;
2218                         case VK_RIGHT:
2219                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Right);
2220                                 break;
2221                         case VK_DOWN:
2222                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_Down);
2223                                 break;
2224                         case VK_END:
2225                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_End);
2226                                 break;
2227                         case VK_PRIOR:
2228                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_PageUp);
2229                                 break;
2230                         case VK_NEXT:
2231                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_PageDown);
2232                                 break;
2233                         case VK_F1:
2234                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F1);
2235                                 break;
2236                         case VK_F2:
2237                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F2);
2238                                 break;
2239                         case VK_F3:
2240                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F3);
2241                                 break;
2242                         case VK_F4:
2243                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F4);
2244                                 break;
2245                         case VK_F5:
2246                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F5);
2247                                 break;
2248                         case VK_F6:
2249                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F6);
2250                                 break;
2251                         case VK_F7:
2252                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F7);
2253                                 break;
2254                         case VK_F8:
2255                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F8);
2256                                 break;
2257                         case VK_F9:
2258                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F9);
2259                                 break;
2260                         case VK_F10:
2261                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F10);
2262                                 break;
2263                         case VK_F11:
2264                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F11);
2265                                 break;
2266                         case VK_F12:
2267                                 Wnd_exec_event(lpgw, lParam, GE_keypress, GP_F12);
2268                                 break;
2269                         } /* switch (wParam) */
2270
2271                         return 0L;
2272 #if 0 /* DO WE NEED THIS ??? */
2273                 case WM_MOUSEMOVE:
2274                         /* set default pointer: */
2275                         SetCursor(hptrDefault);
2276                         return 0L;
2277 #endif
2278 #endif /* USE_MOUSE */
2279                 case WM_COMMAND:
2280                         switch(LOWORD(wParam))
2281                         {
2282                                 case M_GRAPH_TO_TOP:
2283                                         lpgw->graphtotop = !lpgw->graphtotop;
2284                                         SendMessage(hwnd,WM_COMMAND,M_REBUILDTOOLS,0L);
2285                                         return(0);
2286                                 case M_COLOR:
2287                                         lpgw->color = !lpgw->color;
2288                                         SendMessage(hwnd,WM_COMMAND,M_REBUILDTOOLS,0L);
2289                                         return(0);
2290                                 case M_CHOOSE_FONT:
2291                                         SelFont(lpgw);
2292                                         return 0;
2293                                 case M_COPY_CLIP:
2294                                         CopyClip(lpgw);
2295                                         return 0;
2296                                 case M_LINESTYLE:
2297                                         if (LineStyle(lpgw))
2298                                                 SendMessage(hwnd,WM_COMMAND,M_REBUILDTOOLS,0L);
2299                                         return 0;
2300                                 case M_BACKGROUND:
2301                                         lpgw->background = GetColor(hwnd, lpgw->background);
2302                                         SendMessage(hwnd,WM_COMMAND,M_REBUILDTOOLS,0L);
2303                                         return 0;
2304                                 case M_PRINT:
2305                                         CopyPrint(lpgw);
2306                                         return 0;
2307                                 case M_WRITEINI:
2308                                         WriteGraphIni(lpgw);
2309                                         if (lpgw->lptw)
2310                                                 WriteTextIni(lpgw->lptw);
2311                                         return 0;
2312                                 case M_REBUILDTOOLS:
2313                                         lpgw->resized = TRUE;
2314                                         if (lpgw->color)
2315                                                 CheckMenuItem(lpgw->hPopMenu, M_COLOR, MF_BYCOMMAND | MF_CHECKED);
2316                                         else
2317                                                 CheckMenuItem(lpgw->hPopMenu, M_COLOR, MF_BYCOMMAND | MF_UNCHECKED);
2318                                         if (lpgw->graphtotop)
2319                                                 CheckMenuItem(lpgw->hPopMenu, M_GRAPH_TO_TOP, MF_BYCOMMAND | MF_CHECKED);
2320                                         else
2321                                                 CheckMenuItem(lpgw->hPopMenu, M_GRAPH_TO_TOP, MF_BYCOMMAND | MF_UNCHECKED);
2322                                         DestroyPens(lpgw);
2323                                         DestroyFonts(lpgw);
2324                                         hdc = GetDC(hwnd);
2325                                         MakePens(lpgw, hdc);
2326                                         GetClientRect(hwnd, &rect);
2327                                         MakeFonts(lpgw, (LPRECT)&rect, hdc);
2328                                         ReleaseDC(hwnd, hdc);
2329                                         GetClientRect(hwnd, &rect);
2330                                         InvalidateRect(hwnd, (LPRECT) &rect, 1);
2331                                         UpdateWindow(hwnd);
2332                                         return 0;
2333                         }
2334                         return 0;
2335                 case WM_RBUTTONDOWN:
2336                         /* HBB 20010218: note that this only works in mouse-off
2337                          * mode, now. You'll need to go via the System menu to
2338                          * access this popup, instead */
2339                         {
2340                         POINT pt;
2341                         pt.x = LOWORD(lParam);
2342                         pt.y = HIWORD(lParam);
2343                         ClientToScreen(hwnd,&pt);
2344                         TrackPopupMenu(lpgw->hPopMenu, TPM_LEFTALIGN,
2345                                 pt.x, pt.y, 0, hwnd, NULL);
2346                         }
2347                         return(0);
2348                 case WM_CREATE:
2349                         lpgw = ((CREATESTRUCT FAR *)lParam)->lpCreateParams;
2350                         SetWindowLong(hwnd, 0, (LONG)lpgw);
2351                         lpgw->hWndGraph = hwnd;
2352                         hdc = GetDC(hwnd);
2353                         MakePens(lpgw, hdc);
2354 #ifdef USE_MOUSE
2355                         LoadCursors(lpgw);
2356 #endif
2357                         GetClientRect(hwnd, &rect);
2358                         MakeFonts(lpgw, (LPRECT)&rect, hdc);
2359                         ReleaseDC(hwnd, hdc);
2360 #if WINVER >= 0x030a
2361                         {
2362                         WORD version = LOWORD(GetVersion());
2363
2364                         if ((LOBYTE(version)*100 + HIBYTE(version)) >= 310)
2365                                 if ( lpgw->lptw && (lpgw->lptw->DragPre!=(LPSTR)NULL) && (lpgw->lptw->DragPost!=(LPSTR)NULL) )
2366                                         DragAcceptFiles(hwnd, TRUE);
2367                         }
2368 #endif
2369                         return(0);
2370                 case WM_PAINT:
2371                         hdc = BeginPaint(hwnd, &ps);
2372                         SetMapMode(hdc, MM_TEXT);
2373                         SetBkMode(hdc,OPAQUE);
2374                         GetClientRect(hwnd, &rect);
2375 #ifdef WIN32
2376                         SetViewportExtEx(hdc, rect.right, rect.bottom, NULL);
2377 #else
2378                         SetViewportExt(hdc, rect.right, rect.bottom);
2379 #endif
2380                         drawgraph(lpgw, hdc, &rect);
2381                         EndPaint(hwnd, &ps);
2382 #ifdef USE_MOUSE
2383                         /* drawgraph() erases the plot window, so immediately after
2384                          * it has completed, all the 'real-time' stuff the gnuplot core
2385                          * doesn't know anything about has to be redrawn */
2386                         DrawRuler(lpgw);
2387                         DisplayStatusLine(lpgw);
2388                         DrawRulerLineTo(lpgw);
2389                         DrawZoomBox(lpgw);
2390 #endif
2391                         return 0;
2392                 case WM_SIZE:
2393                         /* update font sizes if graph resized */
2394                         if ((wParam == SIZE_MAXIMIZED) || (wParam == SIZE_RESTORED)) {
2395                                 RECT rect;
2396                                 SendMessage(hwnd,WM_SYSCOMMAND,M_REBUILDTOOLS,0L);
2397                                 GetWindowRect(hwnd,&rect);
2398                                 lpgw->Size.x = rect.right-rect.left;
2399                                 lpgw->Size.y = rect.bottom-rect.top;
2400                         }
2401                         break;
2402 #if WINVER >= 0x030a
2403                 case WM_DROPFILES:
2404                         {
2405                         WORD version = LOWORD(GetVersion());
2406                         if ((LOBYTE(version)*100 + HIBYTE(version)) >= 310)
2407                                 if (lpgw->lptw)
2408                                         DragFunc(lpgw->lptw, (HDROP)wParam);
2409                         }
2410                         break;
2411 #endif
2412                 case WM_DESTROY:
2413                         DestroyPens(lpgw);
2414                         DestroyFonts(lpgw);
2415 #ifdef USE_MOUSE
2416                         DestroyCursors(lpgw);
2417 #endif
2418 #if __TURBOC__ >= 0x410    /* Borland C++ 3.1 or later */
2419                         {
2420                         WORD version = LOWORD(GetVersion());
2421                         if ((LOBYTE(version)*100 + HIBYTE(version)) >= 310)
2422                                 DragAcceptFiles(hwnd, FALSE);
2423                         }
2424 #endif
2425                         if (lpgw->lptw && !IsWindowVisible(lpgw->lptw->hWndParent)) {
2426                                 PostMessage (lpgw->lptw->hWndParent, WM_CLOSE, 0, 0);
2427                         }
2428                         return 0;
2429                 case WM_CLOSE:
2430                         GraphClose(lpgw);
2431                         return 0;
2432                 }
2433         return DefWindowProc(hwnd, message, wParam, lParam);
2434 }
2435
2436
2437 void WDPROC
2438 GraphChangeFont(LPGW lpgw, LPCSTR font, int fontsize, HDC hdc, RECT rect)
2439 {
2440     int newfontsize;
2441     bool remakefonts = FALSE;
2442
2443     newfontsize = (fontsize != 0) ? fontsize : lpgw->deffontsize;
2444     if (font != NULL) {
2445         remakefonts = (strcmp(lpgw->fontname, font) != 0) || (newfontsize != lpgw->fontsize);
2446     } else {
2447         remakefonts = (strcmp(lpgw->fontname, lpgw->deffontname) != 0) || (newfontsize != lpgw->fontsize);
2448     }
2449
2450     if (remakefonts) {
2451         lpgw->fontsize = newfontsize;
2452         strcpy(lpgw->fontname, (font) ? font : lpgw->deffontname);
2453
2454         DestroyFonts(lpgw);
2455         MakeFonts(lpgw, &rect, hdc);
2456     }
2457 }
2458
2459
2460 #if 0
2461 int WDPROC
2462 GraphGetFontScaling(LPGW lpgw, LPCSTR font, int fontsize)
2463 {
2464     HDC hdc;
2465     RECT rect;
2466     HGDIOBJ hprevfont;
2467     OUTLINETEXTMETRIC otm;
2468     int shift = 35;
2469
2470     hdc = GetDC(lpgw->hWndGraph);
2471     GetClientRect(lpgw->hWndGraph, &rect);
2472     GraphChangeFont(lpgw, font, fontsize, hdc, rect);
2473     hprevfont = SelectObject(hdc, lpgw->hfonth);
2474     if (GetOutlineTextMetrics(hdc, sizeof(otm), &otm) != 0) {
2475         shift = otm.otmptSuperscriptOffset.y - otm.otmptSubscriptOffset.y;
2476         shift = MulDiv(shift, GetDeviceCaps(hdc, LOGPIXELSY), 2 * 72);
2477         printf( "shift: %i (%i %i)\n", shift*8, otm.otmptSuperscriptOffset.y, otm.otmptSubscriptOffset.y);
2478     }
2479     SelectObject(hdc, hprevfont);
2480
2481     return shift * 8;
2482 }
2483 #endif
2484
2485
2486 unsigned int WDPROC
2487 GraphGetTextLength(LPGW lpgw, LPCSTR text, LPCSTR fontname, int fontsize)
2488 {
2489     HDC hdc;
2490     RECT rect;
2491     SIZE size;
2492     HGDIOBJ hprevfont;
2493
2494     hdc = GetDC(lpgw->hWndGraph);
2495     GetClientRect(lpgw->hWndGraph, &rect);
2496
2497     GraphChangeFont(lpgw, fontname, fontsize, hdc, rect);
2498
2499     hprevfont = SelectObject(hdc, lpgw->hfonth);
2500     GetTextExtentPoint(hdc, text, strlen(text), &size);
2501     SelectObject(hdc, hprevfont);
2502     
2503     size.cx = MulDiv(size.cx + GetTextCharacterExtra(hdc), lpgw->xmax, rect.right-rect.left-1);
2504     return size.cx;
2505 }
2506
2507
2508 #ifdef USE_MOUSE
2509 /* Implemented by Petr Mikulik, February 2001 --- the best Windows solutions
2510  * come from OS/2 :-))
2511  */
2512
2513 /* ================================================================= */
2514
2515 /* Firstly: terminal calls from win.trm */
2516
2517 /* Note that these all take lpgw as their first argument. It's an OO-type
2518  * 'this' pointer, sort of: it stores all the status information of the graph
2519  * window that we need, in a single large structure. */
2520
2521 void WDPROC
2522 Graph_set_cursor (LPGW lpgw, int c, int x, int y )
2523 {
2524         switch (c) {
2525         case -4: /* switch off line between ruler and mouse cursor */
2526                 DrawRulerLineTo(lpgw);
2527                 ruler_lineto.on = FALSE;
2528                 break;
2529         case -3: /* switch on line between ruler and mouse cursor */
2530                 if (ruler.on && ruler_lineto.on)
2531                     break;
2532                 ruler_lineto.x = x;
2533                 ruler_lineto.y = y;
2534                 ruler_lineto.on = TRUE;
2535                 DrawRulerLineTo(lpgw);
2536                 break;
2537         case -2:
2538                 { /* move mouse to the given point */
2539                         RECT rc;
2540                         POINT pt;
2541
2542                         GetClientRect(lpgw->hWndGraph, &rc);
2543                         pt.x = MulDiv(x, rc.right - rc.left, lpgw->xmax);
2544                         pt.y = rc.bottom - MulDiv(y, rc.bottom - rc.top, lpgw->ymax);
2545
2546                         MapWindowPoints(lpgw->hWndGraph, HWND_DESKTOP, &pt, 1);
2547                         SetCursorPos(pt.x, pt.y);
2548                 }
2549                 break;
2550         case -1: /* start zooming; zooming cursor */
2551                 zoombox.on = TRUE;
2552                 zoombox.from.x = zoombox.to.x = x;
2553                 zoombox.from.y = zoombox.to.y = y;
2554                 break;
2555         case 0:  /* standard cross-hair cursor */
2556                 SetCursor( (hptrCurrent = mouse_setting.on ? hptrCrossHair : hptrDefault) );
2557                 break;
2558         case 1:  /* cursor during rotation */
2559                 SetCursor(hptrCurrent = hptrRotating);
2560                 break;
2561         case 2:  /* cursor during scaling */
2562                 SetCursor(hptrCurrent = hptrScaling);
2563                 break;
2564         case 3:  /* cursor during zooming */
2565                 SetCursor(hptrCurrent = hptrZooming);
2566                 break;
2567         }
2568         if (c>=0 && zoombox.on) { /* erase zoom box */
2569                 DrawZoomBox(lpgw);
2570                 zoombox.on = FALSE;
2571         }
2572         if (c>=0 && ruler_lineto.on) { /* erase ruler line */
2573                 DrawRulerLineTo(lpgw);
2574                 ruler_lineto.on = FALSE;
2575         }
2576 }
2577
2578 /* set_ruler(int x, int y) term API: x<0 switches ruler off. */
2579 void WDPROC
2580 Graph_set_ruler (LPGW lpgw, int x, int y )
2581 {
2582         DrawRuler(lpgw); /* remove previous drawing, if any */
2583         DrawRulerLineTo(lpgw);
2584         if (x < 0) {
2585                 ruler.on = FALSE;
2586                 return;
2587         }
2588         ruler.on = TRUE;
2589         ruler.x = x; ruler.y = y;
2590         DrawRuler(lpgw); /* draw ruler at new positions */
2591         DrawRulerLineTo(lpgw);
2592 }
2593
2594 /* put_tmptext(int i, char c[]) term API
2595  *      i: 0..at statusline
2596  *      1, 2: at corners of zoom box with \r separating text
2597  */
2598 void WDPROC
2599 Graph_put_tmptext (LPGW lpgw, int where, LPCSTR text )
2600 {
2601     /* Position of the annotation string (mouse movement) or zoom box
2602      * text or whatever temporary text added...
2603      */
2604     switch (where) {
2605         case 0:
2606                 UpdateStatusLine(lpgw, text);
2607                 break;
2608         case 1:
2609                 DrawZoomBox(lpgw);
2610                 if (zoombox.text1) {
2611                         free((char*)zoombox.text1);
2612                 }
2613                 zoombox.text1 = strdup(text);
2614                 DrawZoomBox(lpgw);
2615                 break;
2616         case 2:
2617                 DrawZoomBox(lpgw);
2618                 if (zoombox.text2) {
2619                         free((char*)zoombox.text2);
2620                 }
2621                 zoombox.text2 = strdup(text);
2622                 DrawZoomBox(lpgw);
2623                 break;
2624         default:
2625                 ; /* should NEVER happen */
2626     }
2627 }
2628
2629 void WDPROC
2630 Graph_set_clipboard (LPGW lpgw, LPCSTR s)
2631 {
2632         size_t length;
2633         HGLOBAL memHandle;
2634         LPSTR clipText;
2635
2636         /* check: no string --> nothing to do */
2637         if (!s || !s[0])
2638                 return;
2639
2640         /* Transport the string into a system-global storage area. In case
2641          * of (unlikely) allocation failures, fail silently */
2642         length = strlen(s);
2643         if ( (memHandle = GlobalAlloc(GHND, length + 1)) == NULL)
2644                 return;
2645         if ( (clipText = GlobalLock(memHandle)) == NULL) {
2646                 GlobalFree(memHandle);
2647                 return;
2648         }
2649         strcpy(clipText, s);
2650         GlobalUnlock(memHandle);
2651
2652         /* Now post that memory area to the Clipboard */
2653         OpenClipboard(lpgw->hWndGraph);
2654         EmptyClipboard();
2655         SetClipboardData(CF_TEXT, memHandle);
2656         CloseClipboard();
2657 }
2658
2659 /* ================================================================= */
2660
2661 /* Secondly, support routines that implement direct mouse reactions. */
2662
2663 /* This routine gets the mouse/pointer position in the current window and
2664  * recalculates it to the viewport coordinates. */
2665 static void
2666 GetMousePosViewport (LPGW lpgw, int *mx, int *my)
2667 {
2668         POINT pt;
2669         RECT rc;
2670
2671         GetClientRect(lpgw->hWndGraph,&rc);
2672
2673         /* HBB: has to be done this way. The simpler method by taking apart LPARM
2674          * only works for mouse, but not for keypress events. */
2675         GetCursorPos(&pt);
2676         ScreenToClient(lpgw->hWndGraph,&pt);
2677         *mx = pt.x;
2678         *my = pt.y;
2679
2680         /* px=px(mx); mouse=>gnuplot driver coordinates */
2681         /* FIXME: classically, this would use MulDiv() call, and no floating point */
2682         *mx = (int)((*mx - rc.left) * lpgw->xmax / (rc.right - rc.left) + 0.5);
2683         *my = (int)((rc.bottom - *my) * lpgw->ymax / (rc.bottom -rc.top) + 0.5);
2684 }
2685
2686 /* HBB 20010218: Newly separated function: Draw text string in XOR mode.
2687  * That is surprisingly difficult using the Windows API: have to draw text
2688  * into a background bitmap, first, and then blit that onto the screen.
2689  *
2690  * x and y give the bottom-left corner of the bounding box into which the
2691  * text will be drawn */
2692 static void
2693 Draw_XOR_Text(LPGW lpgw, const char *text, size_t length, int x, int y)
2694 {
2695         HDC hdc, tempDC;
2696         HBITMAP bitmap;
2697         int cx, cy;
2698
2699         if (!text || !text[0])
2700                return; /* no text to be displayed */
2701
2702         hdc = GetDC(lpgw->hWndGraph);
2703
2704         /* Prepare background image buffer of the necessary size */
2705         Wnd_GetTextSize(hdc, text, length, &cx, &cy);
2706         bitmap = CreateCompatibleBitmap(hdc, cx, cy);
2707         /* ... and a DeviceContext to access it by */
2708         tempDC = CreateCompatibleDC(hdc);
2709         DeleteObject(SelectObject(tempDC, bitmap));
2710
2711         /* Print inverted text, so the second inversion done by SRCINVERT ends
2712          * up printing the right way round... */
2713         /* FIXME HBB 20010218: find out the real ROP3 code for operation
2714          * "target = target XOR (NOT source)" and use that, instead. It's the
2715          * one with MSByte = 0x99, but without VC++ or MSDN, I can't seem to
2716          * find out what the full code is. */
2717         SetTextColor(tempDC, GetBkColor(hdc));
2718         SetBkColor(tempDC, GetTextColor(hdc));
2719         TextOut(tempDC, 0, 0, text, length);
2720
2721         /* Copy printed string to the screen window by XORing, so the
2722          * repetition of this same operation will delete it again */
2723         BitBlt(hdc, x, y - cy, cx, cy, tempDC, 0, 0, SRCINVERT);
2724
2725         /* Clean up behind ourselves */
2726         DeleteDC(tempDC);
2727         DeleteObject(bitmap);
2728         ReleaseDC(lpgw->hWndGraph, hdc);
2729 }
2730
2731
2732 /* ================================== */
2733
2734 /* Status line routines. */
2735
2736 /* Saved text currently contained in status line */
2737 static char *sl_curr_text = NULL;
2738
2739 /* Display the status line by the text (and remove it again by calling this
2740  * same routine a second time --- thanks to XOR mode) */
2741 static void
2742 DisplayStatusLine (LPGW lpgw)
2743 {
2744         RECT rc;
2745
2746         if (!sl_curr_text || !sl_curr_text[0])
2747                return; /* no text to be displayed */
2748
2749         GetClientRect(lpgw->hWndGraph, &rc);
2750         Draw_XOR_Text(lpgw, sl_curr_text, strlen(sl_curr_text), 0, rc.bottom);
2751 }
2752
2753
2754 /*
2755  * Update the status line by the text; firstly erase the previous text
2756  */
2757 static void
2758 UpdateStatusLine (LPGW lpgw, const char text[] )
2759 {
2760         DisplayStatusLine(lpgw); /* erase previous text */
2761         free(sl_curr_text);
2762         if (!text || !*text) {
2763                 sl_curr_text = 0;
2764         } else { /* display new text */
2765                 sl_curr_text = strdup(text);
2766                 DisplayStatusLine(lpgw);
2767         }
2768 }
2769
2770 /* Draw the ruler.
2771  */
2772 static void
2773 DrawRuler (LPGW lpgw)
2774 {
2775         HDC hdc;
2776         int iOldRop;
2777         RECT rc;
2778         long rx, ry;
2779
2780         if (!ruler.on || ruler.x < 0)
2781                 return;
2782
2783         hdc = GetDC(lpgw->hWndGraph);
2784         GetClientRect(lpgw->hWndGraph, &rc);
2785
2786         rx = MulDiv(ruler.x, rc.right - rc.left, lpgw->xmax);
2787         ry = rc.bottom - MulDiv(ruler.y, rc.bottom - rc.top, lpgw->ymax);
2788
2789         iOldRop = SetROP2(hdc, R2_NOT);
2790         MoveTo(hdc, rc.left, ry);
2791         LineTo(hdc, rc.right, ry);
2792         MoveTo(hdc, rx, rc.top);
2793         LineTo(hdc, rx, rc.bottom);
2794         SetROP2(hdc, iOldRop);
2795         ReleaseDC(lpgw->hWndGraph, hdc);
2796 }
2797
2798 /* Draw the ruler line to cursor position.
2799  */
2800 static void
2801 DrawRulerLineTo (LPGW lpgw)
2802 {
2803         HDC hdc;
2804         int iOldRop;
2805         RECT rc;
2806         long rx, ry, rlx, rly;
2807
2808         if (!ruler.on || !ruler_lineto.on || ruler.x < 0 || ruler_lineto.x < 0)
2809                 return;
2810
2811         hdc = GetDC(lpgw->hWndGraph);
2812         GetClientRect(lpgw->hWndGraph, &rc);
2813
2814         rx  = MulDiv(ruler.x, rc.right - rc.left, lpgw->xmax);
2815         ry  = rc.bottom - MulDiv(ruler.y, rc.bottom - rc.top, lpgw->ymax);
2816         rlx = MulDiv(ruler_lineto.x, rc.right - rc.left, lpgw->xmax);
2817         rly = rc.bottom - MulDiv(ruler_lineto.y, rc.bottom - rc.top, lpgw->ymax);
2818
2819         iOldRop = SetROP2(hdc, R2_NOT);
2820         MoveTo(hdc, rx, ry);
2821         LineTo(hdc, rlx, rly);
2822         SetROP2(hdc, iOldRop);
2823         ReleaseDC(lpgw->hWndGraph, hdc);
2824 }
2825
2826 /* Draw the zoom box.
2827  */
2828 static void
2829 DrawZoomBox (LPGW lpgw)
2830 {
2831         HDC hdc;
2832         long fx, fy, tx, ty, text_y;
2833         int OldROP2;
2834         RECT rc;
2835         HPEN OldPen;
2836
2837         if (!zoombox.on)
2838                 return;
2839
2840         hdc = GetDC(lpgw->hWndGraph);
2841         GetClientRect(lpgw->hWndGraph, &rc);
2842
2843         fx = MulDiv(zoombox.from.x, rc.right - rc.left, lpgw->xmax);
2844         fy = rc.bottom - MulDiv(zoombox.from.y, rc.bottom - rc.top, lpgw->ymax);
2845         tx = MulDiv(zoombox.to.x, rc.right - rc.left, lpgw->xmax);
2846         ty = rc.bottom - MulDiv(zoombox.to.y, rc.bottom - rc.top, lpgw->ymax);
2847         text_y = MulDiv(lpgw->vchar, rc.bottom - rc.top, lpgw->ymax);
2848
2849         OldROP2 = SetROP2(hdc, R2_NOTXORPEN);
2850         OldPen = SelectObject(hdc, CreatePenIndirect(
2851                         (lpgw->color ? lpgw->colorpen : lpgw->monopen) + 1));
2852         Rectangle(hdc, fx, fy, tx, ty);
2853         DeleteObject(SelectObject(hdc, OldPen));
2854         SetROP2(hdc, OldROP2);
2855
2856         ReleaseDC(lpgw->hWndGraph, hdc);
2857
2858         if (zoombox.text1) {
2859                 char *separator = strchr(zoombox.text1, '\r');
2860
2861                 if (separator) {
2862                         Draw_XOR_Text(lpgw, zoombox.text1, separator - zoombox.text1, fx, fy);
2863                         Draw_XOR_Text(lpgw, separator + 1, strlen(separator + 1), fx, fy + text_y);
2864                 } else {
2865                         Draw_XOR_Text(lpgw, zoombox.text1, strlen(zoombox.text1), fx, fy + lpgw->vchar / 2);
2866                 }
2867         }
2868         if (zoombox.text2) {
2869                 char *separator = strchr(zoombox.text2, '\r');
2870
2871                 if (separator) {
2872                         Draw_XOR_Text(lpgw, zoombox.text2, separator - zoombox.text2, tx, ty);
2873                         Draw_XOR_Text(lpgw, separator + 1, strlen(separator + 1), tx, ty + text_y);
2874                 } else  {
2875                         Draw_XOR_Text(lpgw, zoombox.text2, strlen(zoombox.text2), tx, ty + lpgw->vchar / 2);
2876                 }
2877         }
2878 }
2879
2880 #endif /* USE_MOUSE */
2881
2882 /* eof wgraph.c */
2883