PLplot 5.15.0
Loading...
Searching...
No Matches
wingdi.c
Go to the documentation of this file.
1// PLplot WIN32 GDI driver.
2//
3// Copyright (C) 2004 Andrew Roach
4//
5// This file is part of PLplot.
6//
7// PLplot is free software; you can redistribute it and/or modify
8// it under the terms of the GNU Library General Public License as published
9// by the Free Software Foundation; either version 2 of the License, or
10// (at your option) any later version.
11//
12// PLplot is distributed in the hope that it will be useful,
13// but WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15// GNU Library General Public License for more details.
16//
17// You should have received a copy of the GNU Library General Public License
18// along with PLplot; if not, write to the Free Software
19// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20//
21//
22#include "plDevs.h"
23
24#ifdef PLD_wingdi
25
26#include <string.h>
27#include <windows.h>
28#include <windowsx.h> // GET_X_LPARAM/GET_Y_LPARAM
29#include <commctrl.h> // For status bars
30#if !defined ( __CYGWIN__ )
31#include <tchar.h>
32#else
33#include <winnt.h>
34#define _T( a ) __TEXT( a )
35#endif
36
37#define NEED_PLDEBUG
38#define DEBUG
39#include "plplotP.h"
40#include "drivers.h"
41#include "plevent.h"
42
43#define ARRAY_SIZE( x ) ( ( sizeof x ) / ( sizeof *x ) )
44#define INITIAL_HEAP_SIZE 16384 // Initial size of heap in bytes
45
46// Driver viewer types
47enum _dev_viewer
48{
49 VIEWER_MINIMAL = 0, // Display just a plot area window
50 VIEWER_FULL, // Display the full function window
51 VIEWER_PLOT // A child only plot area
52};
53
54// Enumerated type for the device states
55enum _dev_state
56{
57 DEV_WAITING = 0, // Device is idle
58 DEV_ACTIVE, // Device is ready for next plot
59 DEV_SIZEMOVE, // Device might be sizing or moving the window
60 DEV_RESIZE, // Device is resizing the window
61 DEV_DRAWING // Device is actively drawing
62};
63
64// Enumerated type used to indicate the device type
65// for updating page metrics
66enum _dev_type
67{
68 DEV_WINDOW, // Setup page metrics for a window
69 DEV_PRINTER // Setup page metrics for a printer
70};
71
72// Data structure used to track the fonts that are created.
73// This avoids the overhead of recreating the font everytime
74// a string is rendered.
75struct _font_entry
76{
77 LOGFONT info;
78 HDC hdc; // Device context used to create font
79 HFONT font;
80 struct _font_entry *next; // Next entry in the font list
81};
82
83// Device-specific info per stream
84struct wingdi_Dev
85{
86 //
87 // Members that are common to interactive GUI devices
88 //
89 PLFLT xdpmm; // Device x pixel per mm
90 PLFLT ydpmm; // Device y pixel per mm
91 PLFLT xscale; // Virtual x pixels to device pixel scaling
92 PLFLT yscale; // Virtual y pixels to device pixel scaling
93 PLINT width; // Window Width (which can change)
94 PLINT height; // Window Height
95
96 enum _dev_viewer viewer;
97 enum _dev_type type;
98 enum _dev_state state; // Current state of the device
99 enum _dev_state prev_state; // Previous state of the device
100 // Used to restore after redraw
101 union
102 {
103 unsigned int status_bar : 1;
104 unsigned int menu_bar : 1;
105 } feature;
106
107 //
108 // WIN32 API variables
109 //
110 HDC hdc; // Plot window device context
111 HPEN pen; // Current pen used for drawing
112 COLORREF color; // Current color
113 HDC hdc_bmp; // Bitmap device context
114 HBITMAP bitmap; // Bitmap of current display
115 HWND frame; // Handle for the main window.
116 HWND plot; // Handle for the plot area
117 HWND status_bar; // Handle for the status bar
118
119 //
120 // Image rasterization variables
121 HDC save_hdc; // Save the original plot window DC
122 HBITMAP raster_bmp; // Bitmap for the raster image
123 RECT raster_rect; // Location of the image
124};
125
126// Device info
127PLDLLIMPEXP_DRIVER const char* plD_DEVICE_INFO_wingdi = "wingdi:Windows GDI:1:wingdi:11:wingdi\n";
128
129// Counter used to indicate the number of streams in use
130static int wingdi_streams = 0;
131
132// Window class for the overall frame
133static TCHAR * frame_window_class_name = _T( "PLplotFrame" );
134static WNDCLASSEX frame_window_class;
135
136// Window class for the plot area
137static TCHAR * plot_area_class_name = _T( "PLplotArea" );
138static WNDCLASSEX plot_area_class;
139
140// Popup menu used by a plot window
141static HMENU plot_popup_menu;
142
143// Private heap used to allocate memory for the driver
144static HANDLE wingdi_heap;
145
146// Font tracking list
147struct _font_entry *font_list = NULL;
148
150void plD_init_wingdi( PLStream * );
151
152//--------------------------------------------------------------------------
153// Graphics primitive implementation functions
154//--------------------------------------------------------------------------
155static void plD_line_wingdi( PLStream *, short, short, short, short );
156static void plD_polyline_wingdi( PLStream *, short *, short *, PLINT );
157static void plD_fill_polygon_wingdi( PLStream *pls );
158static void plD_clear_wingdi( PLStream *pls,
159 PLINT x1, PLINT y1, PLINT x2, PLINT y2 );
160static void plD_eop_wingdi( PLStream * );
161static void plD_bop_wingdi( PLStream * );
162static void plD_tidy_wingdi( PLStream * );
163static void plD_wait_wingdi( PLStream * );
164static void plD_state_wingdi( PLStream *, PLINT );
165static void plD_esc_wingdi( PLStream *, PLINT, void * );
166
167enum commands
168{
169 CommandPrint = 0x08A1,
170 CommandNextPage = 0x08A2,
171 CommandQuit = 0x08A3
172};
173#define PlotAreaId 0x08F0
174#define StatusBarId 0x08F1
175
177{
178#ifndef ENABLE_DYNDRIVERS
179 pdt->pl_MenuStr = "Win32/64 GDI device";
180 pdt->pl_DevName = "wingdi";
181#endif
182 pdt->pl_type = plDevType_Interactive;
183 pdt->pl_seq = 11;
184 pdt->pl_init = (plD_init_fp) plD_init_wingdi;
185 pdt->pl_line = (plD_line_fp) plD_line_wingdi;
186 pdt->pl_polyline = (plD_polyline_fp) plD_polyline_wingdi;
187 pdt->pl_eop = (plD_eop_fp) plD_eop_wingdi;
188 pdt->pl_bop = (plD_bop_fp) plD_bop_wingdi;
189 pdt->pl_tidy = (plD_tidy_fp) plD_tidy_wingdi;
190 pdt->pl_state = (plD_state_fp) plD_state_wingdi;
191 pdt->pl_esc = (plD_esc_fp) plD_esc_wingdi;
192 pdt->pl_wait = (plD_wait_fp) plD_wait_wingdi;
193}
194
195static HCURSOR
196CrossHairCursor( struct wingdi_Dev * dev )
197{
198 HCURSOR cursor;
199
200 cursor = LoadCursor( NULL, IDC_CROSS );
201 SetClassLongPtr( dev->plot, GCL_HCURSOR, (long) cursor );
202 return SetCursor( cursor );
203}
204
205static void
206NormalCursor( struct wingdi_Dev * dev )
207{
208 HCURSOR cursor;
209
210 cursor = LoadCursor( NULL, IDC_ARROW );
211 SetClassLongPtr( dev->plot, GCL_HCURSOR, (LONG_PTR) cursor );
212 SetCursor( cursor );
213}
214
215static void
216BusyCursor( struct wingdi_Dev * dev )
217{
218 HCURSOR cursor;
219
220 cursor = LoadCursor( NULL, IDC_WAIT );
221 SetClassLongPtr( dev->plot, GCL_HCURSOR, (LONG_PTR) cursor );
222 SetCursor( cursor );
223}
224
225static void
226update_status_bar( struct wingdi_Dev * dev )
227{
228 LPSTR status_text[] =
229 {
230 TEXT( "Waiting" ),
231 TEXT( "Active" ),
232 TEXT( "Size/Move" ),
233 TEXT( "Resize" ),
234 TEXT( "Drawing" )
235 };
236
237 if ( dev->status_bar == NULL )
238 return;
239
240 SendMessage( dev->status_bar,
241 SB_SETTEXT,
242 (WPARAM) 0,
243 (LPARAM) status_text[dev->state] );
244}
245
246//--------------------------------------------------------------------------
247// static void UpdatePageMetrics ( PLStream *pls, char flag )
248//
249// UpdatePageMetrics is a simple function which simply gets new values
250// for a changed DC, be it swapping from printer to screen or vice-versa.
251// The flag variable is used to tell the function if it is updating
252// from the printer (1) or screen (0).
253//--------------------------------------------------------------------------
254static void UpdatePageMetrics( PLStream *pls, enum _dev_type dev_type )
255{
256 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
257
258 dev->type = dev_type;
259 if ( dev_type == DEV_PRINTER )
260 {
261 // Get the page size from the printer
262 PLINT vsize, hsize;
263 PLFLT plot_aspect, print_aspect;
264 hsize = GetDeviceCaps( dev->hdc, HORZRES );
265 vsize = GetDeviceCaps( dev->hdc, VERTRES );
266
267 // Determine which orientation best matches the aspect
268 // ratio of the plot window
269 plot_aspect = (PLFLT) dev->width / (PLFLT) dev->height;
270 print_aspect = (PLFLT) hsize / (PLFLT) vsize;
271 if ( plot_aspect > 1.0 )
272 {
273 // Wider than tall
274 dev->width = hsize;
275 dev->height = (PLINT) ( (PLFLT) hsize / plot_aspect );
276 }
277 else
278 {
279 // Taller than wide
280 dev->width = (PLINT) ( (PLFLT) vsize * plot_aspect );
281 dev->height = vsize;
282 }
283 }
284 else
285 {
286 RECT rect;
287
288 GetClientRect( dev->plot, &rect );
289 dev->width = rect.right;
290 dev->height = rect.bottom;
291
292 pldebug( "wingdi", "Page size [%d %d] [%d %d]\n",
293 rect.left, rect.top,
294 rect.right, rect.bottom );
295 }
296
297 // We need the -1 because some of the coordinates
298 // are signed and PIXEL_X/PIXEL_Y can exceed the
299 // maximum value of a positive signed integer, which
300 // results in a negative value.
301 dev->xscale = (PLFLT) ( PIXELS_X - 1 ) / dev->width;
302 dev->yscale = (PLFLT) ( PIXELS_Y - 1 ) / dev->height;
303
304 pldebug( "wingdi", "Scale = (%f %f) (FLT)\n",
305 dev->xscale, dev->yscale );
306
307 // Need to get the DPI information from Windows
308 // HORZRES/VERTRES = Width/Height in pixels
309 // HORZSIZE/VERTSIZE = Width/Height in millimeters
310 pldebug( "wingdi", "Original xdpi = %f ydpi = %f\n",
311 pls->xdpi, pls->ydpi );
312 //dev->xdpmm = GetDeviceCaps( dev->hdc, HORZRES )
313 // / GetDeviceCaps( dev->hdc, HORZSIZE );
314 //pls->xdpi = dev->xdpmm * 25.4;
315 //dev->ydpmm = GetDeviceCaps( dev->hdc, VERTRES )
316 // / GetDeviceCaps( dev->hdc, VERTSIZE );
317 //pls->ydpi = dev->ydpmm * 25.4;
318 pls->xdpi = GetDeviceCaps( dev->hdc, LOGPIXELSX );
319 dev->xdpmm = pls->xdpi / 25.4;
320 pls->ydpi = GetDeviceCaps( dev->hdc, LOGPIXELSY );
321 dev->ydpmm = pls->ydpi / 25.4;
322
323 pldebug( "wingdi", "New xdpi = %f ydpi = %f\n",
324 pls->xdpi, pls->ydpi );
325 pldebug( "wingdi", "Windows reports xdpi = %d ydpi = %d\n",
326 GetDeviceCaps( dev->hdc, LOGPIXELSX ),
327 GetDeviceCaps( dev->hdc, LOGPIXELSY ) );
328
329 // Set the mapping from pixels to mm
330 plP_setpxl( dev->xscale * pls->xdpi / 25.4,
331 dev->yscale * pls->ydpi / 25.4 );
332
333 // Set the physical limits for this device. See the
334 // previous comment about the -1.
335 plP_setphy( 0, PIXELS_X - 1,
336 0, PIXELS_Y - 1 );
337}
338
339//--------------------------------------------------------------------------
340// static void PrintPage ( PLStream *pls )
341//
342// Function brings up a standard printer dialog and, after the user
343// has selected a printer, replots the current page to the windows
344// printer.
345//--------------------------------------------------------------------------
346static void PrintPage( PLStream *pls )
347{
348 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
349 PRINTDLGEX Printer;
350 DOCINFO docinfo;
351 DEVMODE * hDevMode;
352 struct wingdi_Dev *push; // A copy of the entire structure
353
354 // Reset the docinfo structure to 0 and set it's fields up
355 // This structure is used to supply a name to the print queue
356 ZeroMemory( &docinfo, sizeof ( docinfo ) );
357 docinfo.cbSize = sizeof ( docinfo );
358 docinfo.lpszDocName = _T( "Plplot Page" );
359
360 // Set the defaults for the printer device
361 // Allocate a moveable block of memory. Must use GlobalAlloc because
362 // HeapAlloc is not moveable.
363 hDevMode = GlobalAlloc( GHND, sizeof ( DEVMODE ) );
364 if ( hDevMode == NULL )
365 {
366 plwarn( "wingdi: Failed to allocate memory for printer defaults\n" );
367 return;
368 }
369 ZeroMemory( hDevMode, sizeof ( DEVMODE ) );
370 hDevMode->dmSpecVersion = DM_SPECVERSION;
371 hDevMode->dmSize = sizeof ( DEVMODE );
372 hDevMode->dmFields = DM_ORIENTATION;
373 hDevMode->dmOrientation = DMORIENT_LANDSCAPE;
374
375 // Reset out printer structure to zero and initialise it
376 ZeroMemory( &Printer, sizeof ( PRINTDLGEX ) );
377 Printer.lStructSize = sizeof ( PRINTDLGEX );
378 Printer.hwndOwner = dev->plot;
379 Printer.hDevMode = hDevMode;
380 // Disable page ranges, default to collated output,
381 // and return the device context (used to generate the output)
382 Printer.Flags = PD_NOPAGENUMS | PD_NOCURRENTPAGE | PD_NOSELECTION
383 | PD_COLLATE | PD_RETURNDC;
384 // Currently, page ranges is disabled. This code
385 // is left as a placeholder in case print ranges
386 // is allowed in the future. There is no mechanism
387 // implemented that facilitates the user interaction
388 // on selecting pages, so it is best to turn it off
389 // for now.
390 Printer.nPageRanges = 0;
391 Printer.nMaxPageRanges = 0;
392 Printer.lpPageRanges = NULL;
393 Printer.nMinPage = 0;
394 Printer.nMaxPage = 0;
395 // Other print parameter defaults
396 Printer.nCopies = 1;
397 //Printer.nPropertyPages = ARRAY_SIZE( hPrintPropSheetList ),
398 //Printer.lphPropertyPages = hPrintPropSheetList;
399 Printer.nStartPage = START_PAGE_GENERAL;
400
401 // Call the printer dialog function.
402 // If the user has clicked on "Print", then we will continue
403 // processing and print out the page.
404 if ( PrintDlgEx( &Printer ) == S_OK
405 && Printer.dwResultAction == PD_RESULT_PRINT )
406 {
407 // Before doing anything, we will take some backup copies
408 // of the existing values for page size and the like, because
409 // all we are going to do is a quick and dirty modification
410 // of plplot's internals to match the new page size and hope
411 // it all works out ok. After that, we will manip the values,
412 // and when all is done, restore them.
413 push = HeapAlloc( wingdi_heap, 0, sizeof ( struct wingdi_Dev ) );
414 if ( push != NULL )
415 {
416 BusyCursor( dev );
417
418 // Save all the state information of this device
419 memcpy( push, dev, sizeof ( struct wingdi_Dev ) );
420
421 // Change the device context to the printer
422 dev->hdc = Printer.hDC;
423 UpdatePageMetrics( pls, DEV_PRINTER );
424
425 // Now the stuff that actually does the printing !!
426 StartDoc( dev->hdc, &docinfo );
427 plRemakePlot( pls );
428 EndDoc( dev->hdc );
429
430 // Now to undo everything back to what it was for the screen
431 memcpy( dev, push, sizeof ( struct wingdi_Dev ) );
432 UpdatePageMetrics( pls, DEV_WINDOW );
433
434 HeapFree( wingdi_heap, 0, push );
435 NormalCursor( dev );
436
437 // Force a redraw to make sure the plot area is clean of
438 // the leftovers from the print menu
439 //RedrawWindow( dev->plot,
440 // NULL, NULL,
441 // RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW );
442 }
443 else
444 {
445 plwarn( "wingdi: Unable to save state for print" );
446 }
447 }
448
449 // Cleanup after printing
450 if ( Printer.hDC != NULL )
451 DeleteDC( Printer.hDC );
452 if ( Printer.hDevMode != NULL )
453 DeleteDC( Printer.hDevMode );
454 if ( Printer.hDevNames != NULL )
455 DeleteDC( Printer.hDevNames );
456 // Free allocated memory
457 if ( hDevMode )
458 GlobalFree( hDevMode );
459}
460
461static void
462wait_for_user_input( PLStream *pls )
463{
464 struct wingdi_Dev * dev = (struct wingdi_Dev *) pls->dev;
465 MSG msg;
466
467 pldebug( "wingdi", "Waiting for user input\n" );
468
469 // Update the state and the message in the status bar
470 dev->state = DEV_WAITING;
471 update_status_bar( dev );
472
473 // Process messages in the queue or until we are no longer waiting
474 while ( GetMessage( &msg, NULL, 0, 0 ) && dev->state != DEV_ACTIVE )
475 {
476 TranslateMessage( &msg );
477 switch ( (int) msg.message )
478 {
479 case WM_CONTEXTMENU:
480 case WM_RBUTTONDOWN:
481 TrackPopupMenu( plot_popup_menu,
482 TPM_CENTERALIGN | TPM_RIGHTBUTTON,
483 LOWORD( msg.lParam ),
484 HIWORD( msg.lParam ),
485 0,
486 dev->plot,
487 NULL );
488 break;
489
490 case WM_CHAR:
491 if ( ( (TCHAR) ( msg.wParam ) == 32 ) ||
492 ( (TCHAR) ( msg.wParam ) == 13 ) )
493 {
494 dev->state = DEV_ACTIVE;
495 update_status_bar( dev );
496 }
497 else if ( ( (TCHAR) ( msg.wParam ) == 27 ) ||
498 ( (TCHAR) ( msg.wParam ) == 'q' ) ||
499 ( (TCHAR) ( msg.wParam ) == 'Q' ) )
500 {
501 dev->state = DEV_ACTIVE;
502 update_status_bar( dev );
503 PostQuitMessage( 0 );
504 }
505 break;
506
507 case WM_LBUTTONDBLCLK:
508 pldebug( "wingdi", "WM_LBUTTONDBLCLK\n" );
509 dev->state = DEV_ACTIVE;
510 update_status_bar( dev );
511 break;
512
513 case WM_COMMAND:
514 switch ( LOWORD( msg.wParam ) )
515 {
516 case CommandPrint:
517 pldebug( "wingdi", "CommandPrint\n" );
518 PrintPage( pls );
519 break;
520 case CommandNextPage:
521 pldebug( "wingdi", "CommandNextPage\n" );
522 dev->state = DEV_ACTIVE;
523 update_status_bar( dev );
524 break;
525 case CommandQuit:
526 pldebug( "wingdi", "CommandQuit\n" );
527 dev->state = DEV_ACTIVE;
528 update_status_bar( dev );
529 PostQuitMessage( 0 );
530 break;
531 }
532 break;
533
534 default:
535 DispatchMessage( &msg );
536 break;
537 }
538 }
539
540 pldebug( "wingdi", "Done waiting\n" );
541 dev->state = DEV_ACTIVE;
542}
543
544//--------------------------------------------------------------------------
545// GetCursorCmd()
546//
547// Handle events connected to selecting points (modelled after xwin)
548//--------------------------------------------------------------------------
549
550static void
551GetCursorCmd( PLStream *pls, PLGraphicsIn *gin )
552{
553 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
554 MSG msg;
555 HCURSOR previous;
556
557 plGinInit( gin );
558
559 previous = CrossHairCursor( dev );
560
561 while ( gin->pX < 0 )
562 {
563 GetMessage( &msg, NULL, 0, 0 );
564 TranslateMessage( &msg );
565 switch ( (int) msg.message )
566 {
567 case WM_LBUTTONDOWN:
568 gin->pX = msg.pt.x;
569 gin->pY = msg.pt.y;
570 gin->dX = (PLFLT) gin->pX / ( dev->width - 1 );
571 gin->dY = 1.0 - (PLFLT) gin->pY / ( dev->height - 1 );
572
573 if ( msg.wParam & MK_LBUTTON )
574 {
575 pldebug( "wingdi", "Left button down\n" );
576 // Left button was pressed
577 gin->button = 1;
578 gin->state = 0;
579 //gin->keysym = 0x20;
580 }
581 else if ( msg.wParam & MK_MBUTTON )
582 {
583 pldebug( "wingdi", "Middle button down\n" );
584 // Middle button was pressed
585 gin->button = 3;
586 gin->state = 0;
587 }
588 else if ( msg.wParam & MK_RBUTTON )
589 {
590 pldebug( "wingdi", "Right button down\n" );
591 // Right button was pressed
592 gin->button = 2;
593 gin->state = 0;
594 }
595 break;
596
597 case WM_LBUTTONUP:
598 gin->pX = msg.pt.x;
599 gin->pY = msg.pt.y;
600 gin->dX = (PLFLT) gin->pX / ( dev->width - 1 );
601 gin->dY = 1.0 - (PLFLT) gin->pY / ( dev->height - 1 );
602
603 if ( msg.wParam & MK_LBUTTON )
604 {
605 pldebug( "wingdi", "Left button up\n" );
606 // Left button was pressed
607 gin->button = 1;
608 gin->state = 0x100;
609 }
610 else if ( msg.wParam & MK_MBUTTON )
611 {
612 pldebug( "wingdi", "Middle button up\n" );
613 // Middle button was pressed
614 gin->button = 3;
615 gin->state = 0x10000;
616 }
617 else if ( msg.wParam & MK_RBUTTON )
618 {
619 pldebug( "wingdi", "Right button up\n" );
620 // Right button was pressed
621 gin->button = 2;
622 gin->state = 0x1000;
623 }
624 break;
625
626 case WM_CHAR:
627 gin->pX = msg.pt.x;
628 gin->pY = msg.pt.y;
629 gin->dX = (PLFLT) gin->pX / ( dev->width - 1 );
630 gin->dY = 1.0 - (PLFLT) gin->pY / ( dev->height - 1 );
631
632 gin->button = 0;
633 gin->state = 0;
634 gin->keysym = msg.wParam;
635
636 break;
637 }
638 }
639
640 // Restore the previous cursor
641 SetCursor( previous );
642}
643
644//--------------------------------------------------------------------------
645// static void CopySCRtoBMP(PLStream *pls)
646// Function copies the screen contents into a bitmap which is
647// later used for fast redraws of the screen (when it gets corrupted)
648// rather than remaking the plot from the plot buffer.
649//--------------------------------------------------------------------------
650static void CopySCRtoBMP( PLStream *pls )
651{
652 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
653 RECT rect;
654 HGDIOBJ previous;
655
656 // Delete the existing bitmap before creating a new one
657 if ( dev->hdc_bmp != NULL )
658 DeleteDC( dev->hdc_bmp );
659 if ( dev->bitmap != NULL )
660 DeleteObject( dev->bitmap );
661
662 dev->hdc_bmp = CreateCompatibleDC( dev->hdc );
663 GetClientRect( dev->plot, &rect );
664 dev->bitmap = CreateCompatibleBitmap( dev->hdc,
665 rect.right, rect.bottom );
666 previous = SelectObject( dev->hdc_bmp, dev->bitmap );
667 BitBlt( dev->hdc_bmp, 0, 0, rect.right, rect.bottom, dev->hdc, 0, 0, SRCCOPY );
668 SelectObject( dev->hdc_bmp, previous );
669}
670
671//--------------------------------------------------------------------------
672// static void Erase ( PLStream *pls )
673//
674// This function erases the client area of a window
675//--------------------------------------------------------------------------
676static void
677Erase( PLStream *pls )
678{
679 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
680 COLORREF previous_color;
681 RECT rect;
682
683 pldebug( "wingdi", " Erasing window\n" );
684 //
685 // This is a new "High Speed" way of filling in the background.
686 // supposedly this executes faster than creating a brush and
687 // filling a rectangle - go figure ?
688 //
689 if ( dev->type == DEV_WINDOW )
690 {
691 // NOTE: Should GetUpdateRect be used instead?
692 GetClientRect( dev->plot, &rect );
693 previous_color = SetBkColor( dev->hdc,
694 RGB( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b ) );
695 ExtTextOut( dev->hdc, 0, 0, ETO_OPAQUE, &rect, _T( "" ), 0, 0 );
696 SetBkColor( dev->hdc, previous_color );
697 }
698 else
699 {
700 rect.left = 0;
701 rect.top = 0;
702 rect.right = GetDeviceCaps( dev->hdc, HORZRES );
703 rect.bottom = GetDeviceCaps( dev->hdc, VERTRES );
704 }
705}
706
707//--------------------------------------------------------------------------
708// static void Resize( PLStream *pls )
709//
710// This function regenerates a plot by updating the page metrics and then
711// using the plot buffer to recreate the plot. This function assumes that is
712// being called from a WM_PAINT message, thus it does not do anything to force
713// a redraw.
714//--------------------------------------------------------------------------
715static void Resize( PLStream *pls )
716{
717 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
718 RECT rect;
719 enum _dev_state current;
720
721 pldebug( "wingdi", "Resizing\n" );
722
723 // Only resize the window IF plplot is not busy
724 if ( dev->state == DEV_WAITING
725 || dev->state == DEV_ACTIVE
726 || dev->state == DEV_DRAWING )
727 {
728 GetClientRect( dev->plot, &rect );
729 pldebug( "wingdi", " Size = [%d %d] [%d %d]\n",
730 rect.left, rect.top,
731 rect.right, rect.bottom );
732
733 // Check to make sure it isn't just minimised (i.e. zero size)
734 if ( ( rect.right > 0 ) && ( rect.bottom > 0 ) )
735 {
736 UpdatePageMetrics( pls, DEV_WINDOW );
737
738 // Save the current state because remaking the plot
739 // will change it when the BOP is executed.
740 current = dev->state;
741 plRemakePlot( pls );
742 dev->state = current;
743 update_status_bar( dev );
744 }
745 }
746 else
747 {
748 pldebug( "wingdi", " No action taken, state = %d\n", dev->state );
749 }
750 pldebug( "wingdi", "Resizing done\n" );
751}
752
753//--------------------------------------------------------------------------
754// This is the window function for the frame window.
755//--------------------------------------------------------------------------
756LRESULT CALLBACK PlplotFrameProc(
757 HWND hwnd, // handle to window
758 UINT uMsg, // message identifier
759 WPARAM wParam, // first message parameter
760 LPARAM lParam ) // second message parameter
761{
762 struct wingdi_Dev *dev = NULL;
763
764 // Try to get the address to the device data for this window
765#ifdef _WIN64
766 dev = (struct wingdi_Dev *) GetWindowLongPtr( hwnd, GWLP_USERDATA );
767#else
768 dev = (struct wingdi_Dev *) GetWindowLongPtr( hwnd, GWL_USERDATA );
769#endif
770
771 switch ( uMsg )
772 {
773 case WM_CREATE:
774 // Initialize the window.
775 {
776 HWND hStatus;
777
778 // Create the status bar only when the frame is being created
779 hStatus = CreateWindowEx(
780 0,
781 STATUSCLASSNAME,
782 NULL,
783 WS_CHILD | WS_VISIBLE,
784 0, 0, 0, 0,
785 hwnd,
786 (HMENU) StatusBarId,
787 GetModuleHandle( NULL ),
788 NULL );
789 if ( hStatus != NULL )
790 {
791 int status_widths[] = { 100, 200, 300, -1 };
792
793 SendMessage( hStatus, SB_SETPARTS,
794 (WPARAM) ARRAY_SIZE( status_widths ),
795 (LPARAM) status_widths );
796 SendMessage( hStatus, SB_SETTEXT,
797 (WPARAM) 0,
798 (LPARAM) (LPSTR) TEXT( "Active" ) );
799 }
800 else
801 {
802 MessageBox( hwnd, "Could not create status bar.",
803 "Error", MB_OK | MB_ICONERROR );
804 }
805 }
806 return 0;
807
808 case WM_SIZE:
809 {
810 // Set the size and position of the window, including the
811 // child controls
812 HWND hStatus, hPlot;
813 RECT rStatus, rFrame;
814 int plot_height;
815
816 // Get the client area of the frame
817 GetClientRect( hwnd, &rFrame );
818
819 // Get the status bar size
820 hStatus = GetDlgItem( hwnd, StatusBarId );
821 SendMessage( hStatus, WM_SIZE, 0, 0 );
822 GetWindowRect( hStatus, &rStatus );
823
824 // Set the size of the plot area
825 hPlot = GetDlgItem( hwnd, PlotAreaId );
826 plot_height = rFrame.bottom - ( rStatus.bottom - rStatus.top );
827 SetWindowPos( hPlot, // Handle to the plot area window
828 NULL,
829 0, 0,
830 rFrame.right, plot_height,
831 SWP_NOZORDER | SWP_SHOWWINDOW );
832 }
833 return 0;
834
835 case WM_DESTROY:
836 // Clean up window-specific data objects.
837 return 0;
838
839 //
840 // Process other messages.
841 //
842 default:
843 return DefWindowProc( hwnd, uMsg, wParam, lParam );
844 }
845
846 return 0;
847}
848
849//--------------------------------------------------------------------------
850// This is the window function for the plot area. Whenever a message is
851// dispatched using DispatchMessage (or sent with SendMessage) this function
852// gets called with the contents of the message.
853//--------------------------------------------------------------------------
854LRESULT CALLBACK PlplotPlotAreaProc( HWND hwnd, UINT nMsg, WPARAM wParam, LPARAM lParam )
855{
856 PLStream *pls = NULL;
857 struct wingdi_Dev *dev = NULL;
858
859 // During WM_CREATE message, the PLStream pointer is not set, thus
860 // we need to handle this message without attempting to getting the user data
861 if ( nMsg == WM_CREATE )
862 {
863 // Signal that the message was handled
864 return ( 0 );
865 }
866
867 // Try to get the address to pls for this window
868#ifdef _WIN64
869 pls = (PLStream *) GetWindowLongPtr( hwnd, GWLP_USERDATA );
870#else
871 pls = (PLStream *) GetWindowLongPtr( hwnd, GWL_USERDATA );
872#endif
873
874 // If we did not get a valid pointer, pass the message to the
875 // default message handler
876 if ( pls == NULL )
877 {
878 return DefWindowProc( hwnd, nMsg, wParam, lParam );
879 }
880
881 dev = (struct wingdi_Dev *) pls->dev;
882
883 //
884 // Process the windows messages
885 //
886 // Everything except WM_CREATE is done here and it is generally hoped that
887 // pls and dev are defined already by this stage.
888 // That will be true MOST of the time. Some times WM_PAINT will be called
889 // before we get to initialise the user data area of the window with the
890 // pointer to the windows plplot stream
891 //
892
893 switch ( nMsg )
894 {
895 case WM_DESTROY:
896 pldebug( "wingdi", "WM_DESTROY\n" );
897 PostQuitMessage( 0 );
898 return ( 0 );
899 break;
900
901 case WM_PAINT:
902 // A WM_PAINT message gets sent on an expose, resize, and move
903 // events. On expose and move events, a blit of a bitmap will
904 // be performed. On a resize, the plot needs to be regenerated.
905 // Because a WM_PAINT is sent after the triggering event
906 // (e.g. a move), the triggering event is responsible for ensuring
907 // a valid bitmap exists. Thus, this message handler only needs
908 // to blit the bitmap to the window.
909 pldebug( "wingdi", "WM_PAINT state = %d\n", dev->state );
910
911 // Check to see if there is an area that needs to be redrawn.
912 // Per the MSDN document, calling GetUpdateRect with a NULL RECT
913 // value will determine if there is an update region. BeginPaint()
914 // will provide the update region in rcPaint.
915 if ( GetUpdateRect( dev->plot, NULL, FALSE ) )
916 {
917 // Yes there is an update region, start the redraw
918 PAINTSTRUCT ps;
919 HGDIOBJ previous;
920
921 BusyCursor( dev );
922 BeginPaint( dev->plot, &ps );
923
924 pldebug( "wingdi", " Need to redraw area (%d,%d) (%d,%d)\n",
925 ps.rcPaint.left, ps.rcPaint.top,
926 ps.rcPaint.right, ps.rcPaint.bottom );
927 pldebug( "wingdi", " Erase status = %d\n", ps.fErase );
928 pldebug( "wingdi", " Device state = %d\n", dev->state );
929
930 // If we have a valid bitmap and are not currently drawing
931 // a new plot, then we can blit the bitmap to handle the
932 // redraw. On a resize, this will result in the bitmap being
933 // clipped during the resize. A StretchBlt could be used to
934 // rescale the bitmap; however, not all devices support
935 // stretch blits.
936 if ( dev->bitmap != NULL && dev->state != DEV_DRAWING )
937 {
938 // A bitmap exists, thus only a blit is required
939 pldebug( "wingdi", " Blit image\n" );
940 previous = SelectObject( dev->hdc_bmp, dev->bitmap );
941 BitBlt( dev->hdc,
942 ps.rcPaint.left, ps.rcPaint.top,
943 ps.rcPaint.right, ps.rcPaint.bottom,
944 dev->hdc_bmp,
945 ps.rcPaint.left, ps.rcPaint.top,
946 SRCCOPY );
947 SelectObject( dev->hdc_bmp, previous );
948 }
949 else
950 {
951 pldebug( "wingdi",
952 " No paint action bitmap = %lx state = \n",
953 dev->bitmap, dev->state );
954 }
955
956 EndPaint( dev->plot, &ps );
957 NormalCursor( dev );
958 }
959 else
960 {
961 pldebug( "wingdi", " No update area to paint\n" );
962 }
963 pldebug( "wingdi", "WM_PAINT exit\n" );
964 // Signal that the message was processed
965 return ( 0 );
966 break;
967
968 case WM_SIZE:
969 pldebug( "wingdi", "WM_SIZE wParam = %d\n", wParam );
970 // The size might have changed. We only care about
971 // Maximized events, a drag resize, or a restore.
972 if ( wParam == SIZE_MAXIMIZED )
973 {
974 // The window was maximized, which is a resize
975 pldebug( "wingdi", " Window maximized\n" );
976 Resize( pls );
977 }
978 else if ( dev->state == DEV_SIZEMOVE )
979 {
980 // This is a drag resize. Must check before the SIZE_RESTORED
981 // because a drag resize is also a SIZE_RESTORED.
982 pldebug( "wingdi", " Window size/moved\n" );
983
984 // Change the state to indicate that the window has changed
985 // size and not just moved.
986 dev->state = DEV_RESIZE;
987 pldebug( "wingdi", " New state %d\n", dev->state );
988 update_status_bar( dev );
989 }
990 else if ( wParam == SIZE_RESTORED )
991 {
992 // This could be a restore from a maximized or minimized state.
993 // Unless code is added to detect the difference (i.e. look for
994 // an earlier SIZE_MINIMIZED), just treat it as a resize
995 pldebug( "wingdi", " Window restored\n" );
996 Resize( pls );
997 }
998 else
999 {
1000 pldebug( "wingdi", " Unknowing sizing action\n" );
1001 }
1002 pldebug( "wingdi", "WM_SIZE exit\n" );
1003 // Indicate that this message was processed
1004 return ( 0 );
1005 break;
1006
1007 case WM_ENTERSIZEMOVE:
1008 pldebug( "wingdi", "WM_ENTERSIZEMOVE\n" );
1009 pldebug( "wingdi", " Save state %d\n", dev->state );
1010 dev->prev_state = dev->state;
1011 // Indicate that we might be sizing or moving the window
1012 dev->state = DEV_SIZEMOVE;
1013 update_status_bar( dev );
1014 return ( 0 );
1015 break;
1016
1017 case WM_EXITSIZEMOVE:
1018 pldebug( "wingdi", "WM_EXITSIZEMOVE\n" );
1019 // If the window has been resized, regenerate the plot
1020 // for the new window dimensions
1021 if ( dev->state == DEV_RESIZE || dev->state == DEV_SIZEMOVE )
1022 {
1023 pldebug( "wingdi", " Restore state %d\n", dev->prev_state );
1024 // Restore the previous state before handling the resize
1025 // The resize routine needs the original state to preserve
1026 // the state when the buffer is replayed.
1027 dev->state = dev->prev_state;
1028 update_status_bar( dev );
1029 Resize( pls );
1030 }
1031 return ( 0 );
1032 break;
1033
1034 case WM_ERASEBKGND:
1035 // Determine if the window needs to be erased based
1036 // on the current state.
1037 // DEV_WAITING = No erase necessary, the next WM_PAINT
1038 // will repaint the affected area
1039 pldebug( "wingdi", "WM_ERASEBKGND state = %d\n", dev->state );
1040
1041 if ( dev->state != DEV_WAITING )
1042 {
1043 Erase( pls );
1044 // Indicate that the client area was erased
1045 return ( 1 );
1046 }
1047
1048 // Indicate no action was taken
1049 pldebug( "wingdi", " No erase action taken\n" );
1050 return ( 0 );
1051 break;
1052
1053 case WM_MOUSEMOVE:
1054 {
1055 char mesg[80];
1056 int xw, yw;
1057 double x, y;
1058
1059 xw = GET_X_LPARAM( lParam );
1060 yw = GET_Y_LPARAM( lParam );
1061 x = (double) xw * dev->xscale;
1062 y = (double) ( dev->height - yw ) * dev->yscale;
1063
1064 snprintf( mesg, sizeof ( mesg ), "%5.1lf", x );
1065 if ( dev->status_bar != NULL )
1066 SendMessage( dev->status_bar,
1067 SB_SETTEXT,
1068 (WPARAM) 1,
1069 (LPARAM) mesg );
1070
1071 snprintf( mesg, sizeof ( mesg ), "%5.1lf", y );
1072 if ( dev->status_bar != NULL )
1073 SendMessage( dev->status_bar,
1074 SB_SETTEXT,
1075 (WPARAM) 2,
1076 (LPARAM) mesg );
1077 }
1078
1079 // Indicate that we did not process this message
1080 return ( 1 );
1081 break;
1082
1083 case WM_COMMAND:
1084 pldebug( "wingdi", "WM_COMMAND\n" );
1085 return ( 0 );
1086 break;
1087 }
1088
1089 // If we don't handle a message completely we hand it to the system
1090 // provided default window function.
1091 return DefWindowProc( hwnd, nMsg, wParam, lParam );
1092}
1093
1094//--------------------------------------------------------------------------
1095// wingdi_module_initialize()
1096//
1097// Handles the initialization of window classes and module global
1098// variables.
1099//--------------------------------------------------------------------------
1100static void
1101wingdi_module_initialize( void )
1102{
1103 INITCOMMONCONTROLSEX init_controls;
1104
1105 // Return if the module has been initialized
1106 // Use a postfix increment so that wingdi_streams is incremented
1107 // every time this function is called. That ensures a valid count
1108 // for the number of streams that are created. This allows the
1109 // module cleanup to free resources when the last stream is closed.
1110 if ( wingdi_streams++ > 0 )
1111 return;
1112
1113 pldebug( "wingdi", "module init\n" );
1114
1115 // Initialize common controls
1116 init_controls.dwSize = sizeof ( INITCOMMONCONTROLSEX );
1117 init_controls.dwICC = ICC_BAR_CLASSES;
1118 if ( !InitCommonControlsEx( &init_controls ) )
1119 {
1120 plwarn( "wingdi: Failed to initialize common Window controls\n" );
1121 }
1122
1123 //
1124 // Initialize the frame window class
1125 //
1126
1127 // Initialize the entire structure to zero.
1128 memset( &frame_window_class, 0, sizeof ( WNDCLASSEX ) );
1129
1130 // Set the name of the plot window class
1131 frame_window_class.lpszClassName = frame_window_class_name;
1132
1133 // Set the size of the window class structure, to include
1134 // any extra data that might be passed
1135 frame_window_class.cbSize = sizeof ( WNDCLASSEX );
1136
1137 // All windows of this class redraw when resized.
1138 frame_window_class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS
1139 | CS_OWNDC;
1140
1141 // Set the callback function.
1142 frame_window_class.lpfnWndProc = PlplotFrameProc;
1143
1144 // This class is used with the current program instance.
1145 frame_window_class.hInstance = GetModuleHandle( NULL );
1146
1147 // Use standard application icon and arrow cursor provided by the OS
1148 frame_window_class.hIcon = LoadIcon( NULL, IDI_APPLICATION );
1149 frame_window_class.hIconSm = LoadIcon( NULL, IDI_APPLICATION );
1150 frame_window_class.hCursor = LoadCursor( NULL, IDC_ARROW );
1151
1152 // Generic solid background for the frame window
1153 frame_window_class.hbrBackground = (HBRUSH) COLOR_WINDOW;
1154
1155 // Do not allocate extra space for the callback
1156 frame_window_class.cbWndExtra = sizeof ( struct wingdi_Dev * );
1157
1158 // Now register the window class for use.
1159 RegisterClassEx( &frame_window_class );
1160
1161 //
1162 // Initialize the plot area class
1163 //
1164
1165 // Initialize the entire structure to zero.
1166 memset( &plot_area_class, 0, sizeof ( WNDCLASSEX ) );
1167
1168 // Set the name of the plot window class
1169 plot_area_class.lpszClassName = plot_area_class_name;
1170
1171 // Set the size of the window class structure, to include
1172 // any extra data that might be passed
1173 plot_area_class.cbSize = sizeof ( WNDCLASSEX );
1174
1175 // All windows of this class redraw when resized.
1176 plot_area_class.style = CS_HREDRAW | CS_VREDRAW | CS_DBLCLKS
1177 | CS_OWNDC;
1178
1179 // Set the callback function.
1180 plot_area_class.lpfnWndProc = PlplotPlotAreaProc;
1181
1182 // This class is used with the current program instance.
1183 plot_area_class.hInstance = GetModuleHandle( NULL );
1184
1185 // Use standard application icon and arrow cursor provided by the OS
1186 plot_area_class.hIcon = LoadIcon( NULL, IDI_APPLICATION );
1187 plot_area_class.hIconSm = LoadIcon( NULL, IDI_APPLICATION );
1188 plot_area_class.hCursor = LoadCursor( NULL, IDC_ARROW );
1189
1190 // Handle the erase background in the callback function
1191 plot_area_class.hbrBackground = NULL;
1192
1193 // Allocate extra space for a window instance to store the
1194 // pointer to the plot stream (PLStream)
1195 plot_area_class.cbWndExtra = sizeof ( PLStream * );
1196
1197 // Now register the window class for use.
1198 RegisterClassEx( &plot_area_class );
1199
1200 //
1201 // Create the popup menu used by the plot window
1202 //
1203 plot_popup_menu = CreatePopupMenu();
1204 AppendMenu( plot_popup_menu, MF_STRING, CommandPrint, _T( "Print" ) );
1205 AppendMenu( plot_popup_menu, MF_STRING, CommandNextPage, _T( "Next Page" ) );
1206 AppendMenu( plot_popup_menu, MF_STRING, CommandQuit, _T( "Quit" ) );
1207
1208 //
1209 // Create a private heap to use for memory allocation.
1210 // This will keep memory separate from the overall program
1211 // and does not have the overhead of GlobalAlloc(). Requires
1212 // a minimum of Windows XP or Windows Server 2003
1213 //
1214 wingdi_heap = HeapCreate( 0, INITIAL_HEAP_SIZE, 0 );
1215 if ( wingdi_heap == NULL )
1216 {
1217 //plexit("wingdi: Unable to allocate heap of size %d bytes",
1218 // INITIAL_HEAP_SIZE);
1219 plexit( "wingdi: Unable to allocate heap" );
1220 }
1221}
1222
1223static void wingdi_module_cleanup( void )
1224{
1225 struct _font_entry *ptr;
1226
1227 wingdi_streams--;
1228 if ( wingdi_streams > 0 )
1229 return;
1230
1231 DeleteMenu( plot_popup_menu, CommandPrint, 0 );
1232 DeleteMenu( plot_popup_menu, CommandNextPage, 0 );
1233 DeleteMenu( plot_popup_menu, CommandQuit, 0 );
1234 DestroyMenu( plot_popup_menu );
1235
1236 if ( !UnregisterClass( plot_area_class_name, plot_area_class.hInstance ) )
1237 {
1238 plexit( "wingdi: Failed to unregister window class" );
1239 }
1240 if ( !UnregisterClass( frame_window_class_name, frame_window_class.hInstance ) )
1241 {
1242 plexit( "wingdi: Failed to unregister window class" );
1243 }
1244
1245 while ( font_list != NULL )
1246 {
1247 ptr = font_list;
1248 DeleteObject( ptr->font );
1249 font_list = ptr->next;
1250 HeapFree( wingdi_heap, 0, ptr );
1251 }
1252
1253 if ( HeapDestroy( wingdi_heap ) == 0 )
1254 {
1255 plexit( "wingdi: Failed to destroy heap" );
1256 }
1257}
1258
1259//--------------------------------------------------------------------------
1260// plD_init_wingdi()
1261//
1262// Initialize device (terminal).
1263//--------------------------------------------------------------------------
1264
1265void
1266plD_init_wingdi( PLStream *pls )
1267{
1268 struct wingdi_Dev *dev;
1269 int status_bar = 0; // Default to no status bar
1270 int full_viewer = 0; // Default to the minimal viewer
1271 DrvOpt wingdi_options[] = {
1272 { "full", DRV_INT, &full_viewer, "Enable full function viewer (0|1)" },
1273 { "statusbar", DRV_INT, &status_bar, "Enable status bar (0|1)" },
1274 { NULL, DRV_INT, NULL, NULL }
1275 };
1276 TCHAR *program;
1277#ifdef UNICODE
1278 int programlength;
1279#endif
1280
1281 pls->debug = 1;
1282 pldebug( "wingdi", "Device Init\n" );
1283
1284 // Initialize the module
1285 wingdi_module_initialize();
1286
1287 // Allocate and initialize device-specific data
1288 if ( pls->dev != NULL )
1289 free( (void *) pls->dev );
1290
1291 pls->dev = calloc( 1, (size_t) sizeof ( struct wingdi_Dev ) );
1292 if ( pls->dev == NULL )
1293 plexit( "plD_init_wingdi_Dev: Out of memory." );
1294
1295 // Shortcut to the device specific data structure
1296 dev = (struct wingdi_Dev *) pls->dev;
1297
1298 pls->icol0 = 1; // Set a fall back pen color in case user doesn't
1299
1300 pls->termin = 1; // interactive device
1301 pls->graphx = GRAPHICS_MODE; // No text mode for this driver
1302 pls->dev_fill0 = 1; // driver can do solid area fills
1303 pls->dev_xor = 1; // driver supports xor mode
1304 pls->dev_clear = 1; // driver supports clear
1305 pls->dev_text = 1; // driver supports text
1306 pls->dev_gradient = 0; // driver not support gradient fills
1307 pls->dev_dash = 0; // driver can not do dashed lines (yet)
1308 pls->plbuf_write = 1; // driver uses the buffer for redraws
1309
1310 if ( !pls->colorset )
1311 pls->color = 1;
1312
1313 // Check for and set up driver options
1314 plParseDrvOpts( wingdi_options );
1315
1316 // Set the appropriate viewer type
1317 if ( full_viewer )
1318 dev->viewer = VIEWER_FULL;
1319 else
1320 dev->viewer = VIEWER_MINIMAL;
1321
1322 // Determine which features should be enabled
1323 if ( status_bar )
1324 dev->feature.status_bar = 1;
1325 else
1326 dev->feature.status_bar = 0;
1327
1328 // Set up the initial device parameters. This will be updated
1329 // after the plot window is initialized.
1330 if ( pls->xlength <= 0 || pls->ylength <= 0 )
1331 {
1332 // use default width, height of 800x600 if not specified by
1333 // -geometry option or plspage
1334 plspage( 0., 0., 800, 600, 0, 0 );
1335 }
1336
1337 dev->width = pls->xlength - 1; // should I use -1 or not???
1338 dev->height = pls->ylength - 1;
1339
1340#ifdef UNICODE
1341 //convert the program name to wide char
1342 programlength = strlen( pls->program ) + 1;
1343 program = malloc( programlength * sizeof ( TCHAR ) );
1344 MultiByteToWideChar( CP_UTF8, 0, pls->program, programlength, program, programlength );
1345#else
1346 program = pls->program;
1347#endif
1348
1349 if ( dev->viewer == VIEWER_FULL )
1350 {
1351 // Create our main window using the plot window class
1352 dev->frame = CreateWindowEx(
1353 WS_EX_WINDOWEDGE + WS_EX_LEFT,
1354 frame_window_class_name, // Class name
1355 program, // Caption
1356 WS_OVERLAPPEDWINDOW // Window style
1357 | WS_CLIPCHILDREN, // Exclude child area from parent
1358 pls->xoffset, // Initial x (use default)
1359 pls->yoffset, // Initial y (use default)
1360 pls->xlength, // Initial x size (use default)
1361 pls->ylength, // Initial y size (use default)
1362 NULL, // No parent window
1363 NULL, // No menu
1364 frame_window_class.hInstance, // This program instance
1365 NULL // Creation parameters
1366 );
1367
1368 // Create the plot area
1369 dev->plot = CreateWindowEx(
1370 0,
1371 plot_area_class_name, // Class name
1372 NULL, // Caption
1373 WS_CHILD | WS_VISIBLE, // Style
1374 0, 0, 0, 0, // Position information
1375 dev->frame, // Parent window
1376 (HMENU) PlotAreaId, // No menu
1377 plot_area_class.hInstance, // This program instance
1378 NULL // Creation parameters
1379 );
1380 }
1381 else
1382 {
1383 // Create the plot area
1384 dev->plot = CreateWindowEx(
1385 WS_EX_WINDOWEDGE + WS_EX_LEFT,
1386 plot_area_class_name, // Class name
1387 NULL, // Caption
1388 WS_OVERLAPPEDWINDOW // Style
1389 | WS_VISIBLE,
1390 pls->xoffset, // Initial x (use default)
1391 pls->yoffset, // Initial y (use default)
1392 pls->xlength, // Initial x size (use default)
1393 pls->ylength, // Initial y size (use default)
1394 NULL, // Parent window
1395 NULL, // No menu
1396 plot_area_class.hInstance, // This program instance
1397 NULL // Creation parameters
1398 );
1399 }
1400
1401 if ( dev->plot == NULL )
1402 {
1403 plexit( "wingdi: Failed to create plot area\n" );
1404 }
1405
1406#ifdef UNICODE
1407 free( program );
1408#endif
1409
1410 // Attach a pointer to the stream to the window's user area
1411 // this pointer will be used by the windows call back for
1412 // process this window
1413#ifdef _WIN64
1414 SetWindowLongPtr( dev->plot, GWLP_USERDATA, (LONG_PTR) pls );
1415 if ( dev->frame )
1416 SetWindowLongPtr( dev->frame, GWLP_USERDATA, (LONG_PTR) dev );
1417#else
1418 SetWindowLongPtr( dev->plot, GWL_USERDATA, (LONG) pls );
1419 if ( dev->frame )
1420 SetWindowLongPtr( dev->frame, GWL_USERDATA, (LONG) dev );
1421#endif
1422
1423 // Get the device context of the window that was created
1424 dev->hdc = GetDC( dev->plot );
1425
1426 // Set the initial color for the drawing pen
1427 plD_state_wingdi( pls, PLSTATE_COLOR0 );
1428
1429 // Display the window which we just created (using the nShow
1430 // passed by the OS, which allows for start minimized and that
1431 // sort of thing).
1432 if ( dev->viewer == VIEWER_FULL )
1433 {
1434 ShowWindow( dev->frame, SW_SHOWDEFAULT );
1435 SetForegroundWindow( dev->frame );
1436 }
1437 else
1438 {
1439 ShowWindow( dev->plot, SW_SHOWDEFAULT );
1440 SetForegroundWindow( dev->plot );
1441 }
1442
1443 if ( dev->feature.status_bar )
1444 {
1445 // Get the handle for the status bar. This will
1446 // make it easier to update the contents
1447 dev->status_bar = GetDlgItem( dev->frame, StatusBarId );
1448 }
1449
1450 // Now we have to find out, from windows, just how big our drawing area is
1451 // when we specified the page size earlier on, that includes the borders,
1452 // title bar etc... so now that windows has done all its initialisations,
1453 // we will ask how big the drawing area is, and tell plplot
1454 UpdatePageMetrics( pls, DEV_WINDOW );
1455 plspage( pls->xdpi, pls->ydpi, 0, 0, 0, 0 );
1456
1457 // Set fill rule.
1458 if ( pls->dev_eofill )
1459 SetPolyFillMode( dev->hdc, ALTERNATE );
1460 else
1461 SetPolyFillMode( dev->hdc, WINDING );
1462
1463 // Erase the plot window
1464 Erase( pls );
1465
1466 // Indicate that the plot window is active
1467 dev->state = DEV_ACTIVE;
1468 update_status_bar( dev );
1469}
1470
1471//--------------------------------------------------------------------------
1472// plD_line_wingdi()
1473//
1474// Draw a line in the current color from (x1,y1) to (x2,y2).
1475//--------------------------------------------------------------------------
1476
1477static void
1478plD_line_wingdi( PLStream *pls, short x1a, short y1a, short x2a, short y2a )
1479{
1480 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
1481 HGDIOBJ previous_obj;
1482 POINT points[2];
1483
1484 points[0].x = (LONG) ( x1a / dev->xscale );
1485 points[1].x = (LONG) ( x2a / dev->xscale );
1486 points[0].y = (LONG) ( dev->height - ( y1a / dev->yscale ) );
1487 points[1].y = (LONG) ( dev->height - ( y2a / dev->yscale ) );
1488
1489 previous_obj = SelectObject( dev->hdc, dev->pen );
1490
1491 if ( points[0].x != points[1].x || points[0].y != points[1].y )
1492 {
1493 Polyline( dev->hdc, points, 2 );
1494 }
1495 else
1496 {
1497 SetPixel( dev->hdc, points[0].x, points[0].y, dev->color );
1498 }
1499 SelectObject( dev->hdc, previous_obj );
1500}
1501
1502//--------------------------------------------------------------------------
1503// plD_polyline_wingdi()
1504//
1505// Draw a polyline in the current color.
1506//--------------------------------------------------------------------------
1507
1508static void
1509plD_polyline_wingdi( PLStream *pls, short *xa, short *ya, PLINT npts )
1510{
1511 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
1512 int i;
1513 POINT *points = NULL;
1514 HGDIOBJ previous_obj;
1515
1516 if ( npts > 0 )
1517 {
1518 points = HeapAlloc( wingdi_heap, HEAP_ZERO_MEMORY,
1519 (size_t) npts * sizeof ( POINT ) );
1520 if ( points != NULL )
1521 {
1522 for ( i = 0; i < npts; i++ )
1523 {
1524 points[i].x = (LONG) ( xa[i] / dev->xscale );
1525 points[i].y = (LONG) ( dev->height - ( ya[i] / dev->yscale ) );
1526 }
1527
1528 previous_obj = SelectObject( dev->hdc, dev->pen );
1529 Polyline( dev->hdc, points, npts );
1530 SelectObject( dev->hdc, previous_obj );
1531 HeapFree( wingdi_heap, 0, points );
1532 }
1533 else
1534 {
1535 plexit( "Could not allocate memory to \"plD_polyline_wingdi\"\n" );
1536 }
1537 }
1538}
1539
1540//--------------------------------------------------------------------------
1541// plD_fill_polygon_wingdi()
1542//
1543// Fill polygon described in points pls->dev_x[] and pls->dev_y[].
1544//--------------------------------------------------------------------------
1545static void
1546plD_fill_polygon_wingdi( PLStream *pls )
1547{
1548 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
1549 int i;
1550 POINT *points = NULL;
1551 HGDIOBJ previous_brush, previous_pen;
1552 HPEN hpen;
1553 HBRUSH fillbrush;
1554
1555 // Do nothing if there are no points
1556 if ( pls->dev_npts == 0 )
1557 return;
1558
1559 points = HeapAlloc( wingdi_heap, HEAP_ZERO_MEMORY,
1560 (size_t) pls->dev_npts * sizeof ( POINT ) );
1561
1562 if ( points == NULL )
1563 plexit( "Could not allocate memory to \"plD_fill_polygon_wingdi\"\n" );
1564
1565 for ( i = 0; i < pls->dev_npts; i++ )
1566 {
1567 points[i].x = (PLINT) ( pls->dev_x[i] / dev->xscale );
1568 points[i].y = (PLINT) ( dev->height - ( pls->dev_y[i] / dev->yscale ) );
1569 }
1570
1571 // Create a brush for the fill and a pen for the border
1572 fillbrush = CreateSolidBrush( dev->color );
1573 hpen = CreatePen( PS_SOLID, 1, dev->color );
1574 previous_brush = SelectObject( dev->hdc, fillbrush );
1575 previous_pen = SelectObject( dev->hdc, hpen );
1576
1577 // Draw the filled polygon
1578 Polygon( dev->hdc, points, pls->dev_npts );
1579
1580 // Restore the previous objects and delete
1581 SelectObject( dev->hdc, previous_brush );
1582 DeleteObject( fillbrush );
1583 SelectObject( dev->hdc, previous_pen );
1584 DeleteObject( hpen );
1585
1586 HeapFree( wingdi_heap, 0, points );
1587}
1588
1589//--------------------------------------------------------------------------
1590// plD_clear_wingdi()
1591//
1592// Clears the client area of a window.
1593//--------------------------------------------------------------------------
1594static void
1595plD_clear_wingdi( PLStream *pls, PLINT x1, PLINT y1, PLINT x2, PLINT y2 )
1596{
1597 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
1598 COLORREF previous_color;
1599 RECT rect;
1600
1601 if ( x1 >= 0 && y1 >= 0 && x2 >= 0 && y2 >= 0 )
1602 {
1603 rect.left = (LONG) ( x1 / dev->xscale );
1604 rect.top = (LONG) ( dev->height - ( y1 / dev->yscale ) );
1605 rect.right = (LONG) ( x2 / dev->xscale );
1606 rect.bottom = (LONG) ( dev->height - ( y2 / dev->yscale ) );
1607 }
1608 else
1609 {
1610 // Invalid coordinates, erase the entire area
1611 GetClientRect( dev->plot, &rect );
1612 }
1613
1614 // This is a new "High Speed" way of filling in the background.
1615 // supposedly this executes faster than creating a brush and
1616 // filling a rectangle - go figure ?
1617 previous_color = SetBkColor( dev->hdc,
1618 RGB( pls->cmap0[0].r, pls->cmap0[0].g, pls->cmap0[0].b ) );
1619 ExtTextOut( dev->hdc, 0, 0, ETO_OPAQUE, &rect, _T( "" ), 0, 0 );
1620 SetBkColor( dev->hdc, previous_color );
1621}
1622
1623struct _text_state
1624{
1625 // Font information
1626 PLFLT font_height;
1627 LONG font_weight;
1628 BYTE font_charset;
1629 BYTE font_pitch;
1630 BYTE font_family;
1631 BYTE italic;
1632
1633 // Font transformation
1634 PLFLT rotation, shear, stride;
1635
1636 // Super/subscript state
1637 PLINT level; // super/subscript level
1638 PLFLT old_sscale, sscale;
1639 PLFLT old_soffset, soffset;
1640};
1641
1642// set_font()
1643//
1644// Sets the current font of the plot window device context to one that
1645// best matches the desired attributes. The previous font is returned so
1646// that the caller can restore the original font. This routine caches the
1647// fonts that it creates to improve performance.
1648static HFONT
1649set_font( struct wingdi_Dev * dev,
1650 LONG font_height, LONG escapement,
1651 LONG weight, BYTE italic,
1652 BYTE charset,
1653 BYTE pitch, BYTE family )
1654{
1655 struct _font_entry *ptr = font_list;
1656
1657 for ( ptr = font_list; ptr != NULL; ptr = ptr->next )
1658 {
1659 if (
1660 ptr->info.lfHeight == font_height
1661 && ptr->info.lfEscapement == escapement
1662 && ptr->info.lfWeight == weight
1663 && ptr->info.lfItalic == italic
1664 && ptr->info.lfCharSet == charset
1665 && ptr->info.lfPitchAndFamily == ( pitch | family )
1666 && ptr->hdc == dev->hdc
1667 )
1668 {
1669 return SelectObject( dev->hdc, ptr->font );
1670 }
1671 }
1672
1673 // Allocate space for this font entry
1674 ptr = HeapAlloc( wingdi_heap, 0, sizeof ( struct _font_entry ) );
1675
1676 // The font was not found, thus we need to create it
1677 ptr->info.lfHeight = font_height;
1678 ptr->info.lfWidth = 0;
1679 ptr->info.lfEscapement = escapement; // Escapement angle
1680 ptr->info.lfOrientation = 0; // Orientation angle
1681 ptr->info.lfWeight = weight; // Font weight (e.g. bold)
1682 ptr->info.lfItalic = italic;
1683 ptr->info.lfUnderline = 0;
1684 ptr->info.lfStrikeOut = 0;
1685 ptr->info.lfCharSet = charset;
1686 ptr->info.lfOutPrecision = OUT_OUTLINE_PRECIS;
1687 ptr->info.lfClipPrecision = CLIP_DEFAULT_PRECIS;
1688 ptr->info.lfQuality = ANTIALIASED_QUALITY;
1689 ptr->info.lfPitchAndFamily = pitch | family;
1690 ptr->font = CreateFontIndirect( &( ptr->info ) );
1691 ptr->hdc = dev->hdc;
1692 ptr->next = NULL;
1693
1694 if ( ptr->font == NULL )
1695 {
1696 plwarn( "wingdi: Unable to create a font, using default\n" );
1697 HeapFree( wingdi_heap, 0, ptr );
1698 return NULL;
1699 }
1700 else
1701 {
1702 if ( font_list != NULL )
1703 {
1704 ptr->next = font_list;
1705 font_list = ptr;
1706 }
1707 else
1708 {
1709 font_list = ptr;
1710 }
1711 }
1712
1713 return SelectObject( dev->hdc, ptr->font );
1714}
1715
1716// Find the corresponding ANSI value for a given
1717// Hershey character
1718static const char hershey_to_ansi_lookup[] =
1719{
1720 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0
1721 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
1722 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 20
1723 0, 0, 0, '!', '"', '#', '$', '%', '&', '\'', // 30
1724 '(', ')', '*', '+', ',', '-', '.', '/', '0', '1', // 40
1725 '2', '3', '4', '5', '6', '7', '8', '9', ':', ';', // 50
1726 '<', '=', '>', '?', '@', 'A', 'B', 'C', 'D', 'E', // 60
1727 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', // 70
1728 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', // 80
1729 'Z', '[', '\\', ']', '^', '_', '`', 'a', 'b', 'c', // 90
1730 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', // 100
1731 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', // 110
1732 'x', 'y', 'z', '{', '|', '}', '~', 0, 0, 0, // 120
1733};
1734static const char hershey_to_symbol_lookup[] =
1735{
1736 0, 0xB7, 0x2B, 0x2A, 0, 0, 0, 0, 0, 0, // 0
1737 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 10
1738 0, 0, 0, 0, 0, 0, 0, 0, 0xAC, 0xAE, // 20
1739 0xAD, 0xAF
1740};
1741
1742static void
1743process_text_escape( struct wingdi_Dev *dev, EscText *args,
1744 int *i, char *buffer, int *j,
1745 struct _text_state *state )
1746{
1747 int val;
1748
1749 // Get the next character to determine what the escape action is
1750 switch ( args->string[++( *i )] )
1751 {
1752 case 'u': // Superscript or end of subscript
1753 plP_script_scale( TRUE, &state->level,
1754 &state->old_sscale, &state->sscale,
1755 &state->old_soffset, &state->soffset );
1756
1757 set_font( dev,
1758 (LONG) ( state->font_height * state->sscale ),
1759 (LONG) ( state->rotation ),
1760 state->font_weight,
1761 state->italic,
1762 state->font_charset,
1763 state->font_pitch, state->font_family );
1764 break;
1765 case 'd': // Subscript or end of superscript
1766 plP_script_scale( FALSE, &state->level,
1767 &state->old_sscale, &state->sscale,
1768 &state->old_soffset, &state->soffset );
1769
1770 set_font( dev,
1771 (LONG) ( state->font_height * state->sscale ),
1772 (LONG) ( state->rotation ),
1773 state->font_weight,
1774 state->italic,
1775 state->font_charset,
1776 state->font_pitch, state->font_family );
1777 break;
1778 case 'f': // Font switch
1779 switch ( args->string[++( *i )] )
1780 {
1781 case 'n':
1782 state->font_family = FF_SWISS;
1783 state->italic = 0;
1784 state->font_charset = ANSI_CHARSET;
1785 break;
1786 case 'r':
1787 state->font_family = FF_ROMAN;
1788 state->italic = 0;
1789 state->font_charset = ANSI_CHARSET;
1790 break;
1791 case 'i':
1792 state->font_family = FF_ROMAN;
1793 state->italic = 1;
1794 state->font_charset = ANSI_CHARSET;
1795 break;
1796 case 's':
1797 state->font_family = FF_SCRIPT;
1798 state->italic = 0;
1799 state->font_charset = ANSI_CHARSET;
1800 break;
1801 }
1802 set_font( dev,
1803 (LONG) ( state->font_height * state->sscale ),
1804 (LONG) ( state->rotation ),
1805 state->font_weight,
1806 state->italic,
1807 state->font_charset,
1808 state->font_pitch, state->font_family );
1809 break;
1810 case 'g': // Greek character font
1811 state->font_family = FF_DONTCARE;
1812 state->italic = 0;
1813 state->font_charset = GREEK_CHARSET;
1814
1815 set_font( dev,
1816 (LONG) ( state->font_height * state->sscale ),
1817 (LONG) ( state->rotation ),
1818 state->font_weight,
1819 state->italic,
1820 state->font_charset,
1821 state->font_pitch, state->font_family );
1822 break;
1823 case '(': // Hershey character specified
1824 sscanf( args->string + *i, "%d", &val );
1825
1826 if ( val > 0 && val < ARRAY_SIZE( hershey_to_ansi_lookup )
1827 && hershey_to_ansi_lookup[val] != 0 )
1828 {
1829 buffer[( *j )++] = hershey_to_ansi_lookup[val];
1830 }
1831 else
1832 {
1833 plwarn( "Unsupported hershey character\n" );
1834 // Substitute a bullet so something is displayed
1835 buffer[( *j )++] = 0x95;
1836 }
1837 *i += 4;
1838 break;
1839 case '[': // Unicode character specified
1840 plwarn( "Unicode characters are not supported\n" );
1841 *i += 4;
1842 break;
1843 default:
1844 plwarn( "Ignoring escape code %c\n" );
1845 //plwarn( "Ignoring escape code %c\n", args->string[i-1] );
1846 }
1847}
1848
1849//--------------------------------------------------------------------------
1850// plD_text_wingdi()
1851//
1852// Renders text on the string. Support non-Unicode and Unicode strings.
1853//--------------------------------------------------------------------------
1854static void
1855plD_text_wingdi( PLStream * pls, EscText * args )
1856{
1857 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
1858 struct _text_state state;
1859 PLFLT cos_rot, sin_rot;
1860 PLFLT cos_shear, sin_shear;
1861 int rx, ry;
1862 UINT halign, valign;
1863 HFONT font = NULL, prev_font = NULL;
1864 COLORREF prev_color;
1865 int prev_bkmode;
1866 int text_segments;
1867 char esc;
1868
1869 // Get the escape character used to format strings
1870 plgesc( &esc );
1871
1872 // Determine the font characteristics
1873 state.italic = 0;
1874 state.font_weight = FW_NORMAL;
1875 state.font_charset = ANSI_CHARSET;
1876 state.font_pitch = DEFAULT_PITCH;
1877 state.font_family = FF_DONTCARE;
1878 switch ( pls->cfont )
1879 {
1880 case 1:
1881 // normal = (medium, upright, sans serif)
1882 state.font_family = FF_SWISS;
1883 break;
1884 case 2:
1885 // roman = (medium, upright, serif)
1886 state.font_family = FF_ROMAN;
1887 break;
1888 case 3:
1889 // italic = (medium, italic, serif)
1890 state.font_family = FF_ROMAN;
1891 state.italic = 1;
1892 break;
1893 case 4:
1894 // script = (medium, upright, script)
1895 state.font_family = FF_SCRIPT;
1896 break;
1897 }
1898
1899 // Calculate the font size from mm to device units
1900 state.font_height = 1.6 * pls->chrht * dev->ydpmm;
1901
1902 plRotationShear( args->xform,
1903 &state.rotation, &state.shear, &state.stride );
1904 state.rotation -= pls->diorot * PI / 2.0;
1905 cos_rot = (PLFLT) cos( state.rotation );
1906 sin_rot = (PLFLT) sin( state.rotation );
1907 cos_shear = (PLFLT) cos( state.shear );
1908 sin_shear = (PLFLT) sin( state.shear );
1909 // Convert from radians to tenths of a degree, which is what
1910 // CreateFont() uses
1911 state.rotation = state.rotation * ( 180.0 / M_PI ) * 10.0;
1912
1913 // Is the page rotated?
1914 if ( pls->diorot != 0 )
1915 {
1916 // The page is rotated. We need to apply the page rotation because
1917 // args x,y are not rotated.
1918 PLFLT x, y, ox, oy;
1919 PLFLT cos_theta, sin_theta;
1920
1921 // Translate origin of the virtual coordinates to the midpoint
1922 ox = (PLFLT) args->x - PIXELS_X / 2;
1923 oy = (PLFLT) args->y - PIXELS_Y / 2;
1924
1925 // Apply the orientation rotation
1926 cos_theta = cos( -pls->diorot * PI / 2.0 );
1927 sin_theta = sin( -pls->diorot * PI / 2.0 );
1928 x = cos_theta * (PLFLT) ox - sin_theta * (PLFLT) oy;
1929 y = sin_theta * (PLFLT) ox + cos_theta * (PLFLT) oy;
1930
1931 // Untranslate origin
1932 x = x + PIXELS_X / 2;
1933 y = y + PIXELS_Y / 2;
1934
1935 // Convert to device coordinates
1936 rx = (int) ( x / dev->xscale );
1937 ry = (int) ( dev->height - ( y / dev->yscale ) );
1938 }
1939 else
1940 {
1941 rx = (int) ( args->x / dev->xscale );
1942 ry = (int) ( dev->height - ( args->y / dev->yscale ) );
1943 }
1944
1945 // Determine the location of the bounding rectangle relative to the
1946 // reference point (bottom, top, or center)
1947 // Windows draws the text within a rectangle, thus the rectangle
1948 // needs to be positioned relative to specified coordinate
1949 if ( args->base == 2 )
1950 {
1951 // If base = 2, it is aligned with the top of the text box
1952 valign = TA_TOP;
1953 }
1954 else if ( args->base == 1 )
1955 {
1956 // If base = 1, it is aligned with the baseline of the text box
1957 valign = TA_BOTTOM;
1958 }
1959 else
1960 {
1961 // If base = 0, it is aligned with the center of the text box
1962 valign = TA_TOP;
1963
1964 // Adjust the reference point by 1/2 of the character height
1965 ry -= (int) ( 0.5 * pls->chrht * dev->ydpmm );
1966 }
1967
1968 // Text justification. Left (0.0), center (0.5) and right (1.0)
1969 // justification, which are the more common options, are supported.
1970 if ( args->just < 0.499 )
1971 {
1972 // Looks left aligned
1973 halign = TA_LEFT;
1974 }
1975 else if ( args->just > 0.501 )
1976 {
1977 // Looks right aligned
1978 halign = TA_RIGHT;
1979 }
1980 else
1981 {
1982 halign = TA_CENTER;
1983 }
1984
1985 prev_font = set_font( dev,
1986 (LONG) state.font_height,
1987 (LONG) state.rotation,
1988 state.font_weight,
1989 state.italic,
1990 state.font_charset,
1991 state.font_pitch,
1992 state.font_family );
1993 prev_color = SetTextColor( dev->hdc, dev->color );
1994 prev_bkmode = SetBkMode( dev->hdc, TRANSPARENT );
1995
1996 if ( args->unicode_array_len == 0 )
1997 {
1998 // Non unicode string
1999 size_t i, j, len;
2000 char * buffer;
2001 int height = 0, width = 0;
2002
2003 // Determine the string length and allocate a buffer to
2004 // use as a working copy of the output string. The passed
2005 // string will always be larger than the formatted version
2006 len = strlen( args->string );
2007 buffer = HeapAlloc( wingdi_heap, 0, ( len + 1 ) * sizeof ( char ) );
2008 if ( buffer == NULL )
2009 {
2010 plexit( "wingdi: Unable to allocate character buffer\n" );
2011 }
2012
2013 // First we need to determine the number of text segments in the
2014 // string. Text segments are delimited by the escape characters
2015 text_segments = 1;
2016 state.level = 0;
2017 state.sscale = 1.0;
2018 for ( i = j = 0; i < len + 1; i++ )
2019 {
2020 if ( args->string[i] != esc && args->string[i] != '\0' )
2021 {
2022 // Copy characters into the buffer to build the
2023 // string segment
2024 buffer[j++] = args->string[i];
2025 }
2026 else if ( i != len && args->string[i + 1] == esc )
2027 {
2028 // We have two escape characters in a row, which means
2029 // to ignore the escape and copy the character
2030 buffer[j++] = args->string[i];
2031 i++;
2032 }
2033 else
2034 {
2035 if ( j > 0 )
2036 {
2037 SIZE segment_size;
2038
2039 // NUL terminate what we have copied so far
2040 buffer[j] = '\0';
2041
2042 // Determine the size of the text segment and add
2043 // it to the width of the overall string
2044 GetTextExtentPoint32( dev->hdc,
2045 buffer, j,
2046 &segment_size );
2047
2048 // The effect of super/subscripts on the size of the
2049 // bounding box is ignored at this time. This will result
2050 // in small positional errors.
2051
2052 width += segment_size.cx;
2053 if ( segment_size.cy > height )
2054 height = segment_size.cy;
2055 }
2056 j = 0;
2057 if ( i != len )
2058 {
2059 text_segments++;
2060 process_text_escape( dev, args,
2061 &i, buffer, &j,
2062 &state );
2063 }
2064 }
2065 }
2066
2067 // Output the text segments
2068 if ( text_segments > 1 )
2069 {
2070 UINT save_text_align;
2071
2072 // We have the total width of all the text segments. Determine
2073 // the initial reference point based on the desired alignment.
2074 // When rendering text, we use left alignment and adjust the reference
2075 // point accordingly.
2076 switch ( halign )
2077 {
2078 case TA_LEFT:
2079 break;
2080 case TA_RIGHT:
2081 rx -= width;
2082 break;
2083 case TA_CENTER:
2084 rx -= (int) ( cos_rot * 0.5 * (PLFLT) width );
2085 ry += (int) ( sin_rot * 0.5 * (PLFLT) width );
2086 break;
2087 }
2088 save_text_align = SetTextAlign( dev->hdc, TA_LEFT | valign );
2089
2090 state.level = 0;
2091 state.sscale = 1.0;
2092 for ( i = j = 0; i < len + 1; i++ )
2093 {
2094 if ( args->string[i] != esc && args->string[i] != '\0' )
2095 {
2096 // Copy characters into the buffer to build the
2097 // string segment
2098 buffer[j++] = args->string[i];
2099 }
2100 else if ( i != len && args->string[i + 1] == esc )
2101 {
2102 // We have two escape characters in a row, which means
2103 // to ignore the escape and copy the character
2104 buffer[j++] = args->string[i];
2105 i++;
2106 }
2107 else
2108 {
2109 SIZE segment_size;
2110 int sx, sy;
2111
2112 if ( j > 0 )
2113 {
2114 // NUL terminate what we have copied so far
2115 buffer[j] = '\0';
2116
2117 GetTextExtentPoint32( dev->hdc,
2118 buffer, j,
2119 &segment_size );
2120
2121 // Determine the offset due to super/subscripts
2122 sx = (int) ( ( 1.0 - cos_rot ) * (PLFLT) state.soffset );
2123 sy = (int) ( ( 1.0 - sin_rot ) * (PLFLT) state.soffset );
2124
2125 TextOut( dev->hdc,
2126 rx + sx,
2127 ry + sy,
2128 buffer, j );
2129
2130 rx += (int) ( cos_rot * (PLFLT) segment_size.cx );
2131 ry -= (int) ( sin_rot * (PLFLT) segment_size.cx );
2132 }
2133 j = 0;
2134
2135 if ( i != len )
2136 process_text_escape( dev, args,
2137 &i, buffer, &j,
2138 &state );
2139 }
2140 }
2141 }
2142 HeapFree( wingdi_heap, 0, buffer );
2143 }
2144 else
2145 {
2146 // Unicode string
2147 }
2148
2149 if ( text_segments == 1 )
2150 {
2151 // Only one text segment, thus this is a simple string
2152 // that can be written to the device in one step. We
2153 // check for this because many strings are simple and
2154 // this approach is faster
2155 UINT save_text_align;
2156
2157 save_text_align = SetTextAlign( dev->hdc, halign | valign );
2158
2159 TextOut( dev->hdc,
2160 rx, ry,
2161 args->string, strlen( args->string ) );
2162
2163 SetTextAlign( dev->hdc, save_text_align );
2164 }
2165
2166 // Restore the previous text settings
2167 if ( prev_font != NULL )
2168 SelectObject( dev->hdc, prev_font );
2169 SetTextColor( dev->hdc, prev_color );
2170 SetBkMode( dev->hdc, prev_bkmode );
2171}
2172
2173//--------------------------------------------------------------------------
2174// End of the page
2175//--------------------------------------------------------------------------
2176static void
2177plD_eop_wingdi( PLStream *pls )
2178{
2179 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
2180
2181 pldebug( "wingdi", "End of the page\n" );
2182
2183 if ( dev->type == DEV_WINDOW )
2184 // Save a bitmap of the current screen to help with redraws
2185 CopySCRtoBMP( pls );
2186 else
2187 EndPage( dev->hdc );
2188
2189 // Set the cursor to normal to indicate that the window is not busy
2190 NormalCursor( dev );
2191
2192 // Indicate that the driver is no longer drawing and is ready to continue
2193 dev->state = DEV_ACTIVE;
2194}
2195
2196//--------------------------------------------------------------------------
2197// Beginning of the new page
2198//--------------------------------------------------------------------------
2199static void
2200plD_bop_wingdi( PLStream *pls )
2201{
2202 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
2203 pldebug( "wingdi", "Start of Page\n" );
2204
2205 // Indicate that the device is actively drawing
2206 dev->state = DEV_DRAWING;
2207
2208 // Update the status bar
2209 update_status_bar( dev );
2210
2211 // Change the cursor to indicate the program is busy
2212 BusyCursor( dev );
2213
2214 if ( dev->type == DEV_WINDOW )
2215 {
2216 // Invalidate the page so that it gets erased on the WM_PAINT. It might be
2217 // better to just clear now and not invalidate.
2218 // NOTE: Is RedrawWindow needed only for the window output device type?
2219 //RedrawWindow( dev->plot, NULL, NULL, RDW_ERASE | RDW_INVALIDATE | RDW_ERASENOW );
2220 SendMessage( dev->plot,
2221 WM_ERASEBKGND,
2222 (WPARAM) 0,
2223 (LPARAM) NULL );
2224 }
2225 else
2226 {
2227 StartPage( dev->hdc );
2228 }
2229
2230 // Reset the pen color
2231 plD_state_wingdi( pls, PLSTATE_COLOR0 );
2232}
2233
2234//--------------------------------------------------------------------------
2235// Stream cleanup function
2236//--------------------------------------------------------------------------
2237static void
2238plD_tidy_wingdi( PLStream *pls )
2239{
2240 struct wingdi_Dev *dev = NULL;
2241
2242 pldebug( "wingdi", "plD_tidy_wingdi\n" );
2243
2244 if ( pls->dev != NULL )
2245 {
2246 dev = (struct wingdi_Dev *) pls->dev;
2247
2248 if ( dev->hdc != NULL )
2249 ReleaseDC( dev->plot, dev->hdc );
2250
2251 if ( dev->bitmap != NULL )
2252 DeleteObject( dev->bitmap );
2253
2254 if ( dev->plot != NULL )
2255 DestroyWindow( dev->plot );
2256 if ( dev->status_bar != NULL )
2257 DestroyWindow( dev->status_bar );
2258 if ( dev->frame != NULL )
2259 DestroyWindow( dev->frame );
2260
2261 free_mem( pls->dev );
2262
2263 wingdi_module_cleanup();
2264 }
2265}
2266
2267//--------------------------------------------------------------------------
2268// plD_wait_wingdi()
2269//
2270// Wait for user input.
2271//--------------------------------------------------------------------------
2272
2273static void
2274plD_wait_wingdi( PLStream *pls )
2275{
2276 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
2277
2278 // Wait for the user to indicate the next action
2279 wait_for_user_input( pls );
2280}
2281
2282//--------------------------------------------------------------------------
2283// plD_state_wingdi()
2284//
2285// Handle change in PLStream state (color, pen width, fill attribute, etc).
2286//--------------------------------------------------------------------------
2287
2288static void
2289plD_state_wingdi( PLStream *pls, PLINT op )
2290{
2291 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
2292
2293 switch ( op )
2294 {
2295 case PLSTATE_COLOR0:
2296 case PLSTATE_COLOR1:
2297 dev->color = RGB( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b );
2298 break;
2299
2300 case PLSTATE_CMAP0:
2301 case PLSTATE_CMAP1:
2302 dev->color = RGB( pls->curcolor.r, pls->curcolor.g, pls->curcolor.b );
2303 break;
2304 }
2305
2306 if ( dev->pen != NULL )
2307 DeleteObject( dev->pen );
2308 dev->pen = CreatePen( PS_SOLID, (int) pls->width, dev->color );
2309}
2310
2311//--------------------------------------------------------------------------
2312// plD_esc_wingdi()
2313//
2314// Handle PLplot escapes
2315//--------------------------------------------------------------------------
2316
2317#ifndef PLESC_TELLME
2318//
2319// Placeholder code from Greg Jung until it gets incorporated
2320// somewhere else
2321//
2322struct passmeup
2323{
2324 MSG *msg;
2325 HWND *hwnd;
2326 HDC *hdc;
2327 void *roomformore[6];
2328 char *flags;
2329};
2330#define PLESC_TELLME 41
2331#endif
2332
2333static void
2334plD_esc_wingdi( PLStream *pls, PLINT op, void *ptr )
2335{
2336 struct wingdi_Dev *dev = (struct wingdi_Dev *) pls->dev;
2337
2338 switch ( op )
2339 {
2340 case PLESC_GETC:
2341 GetCursorCmd( pls, (PLGraphicsIn *) ptr );
2342 break;
2343
2344 case PLESC_FILL:
2345 plD_fill_polygon_wingdi( pls );
2346 break;
2347
2348 case PLESC_CLEAR:
2349 plD_clear_wingdi( pls,
2350 pls->sppxmi, pls->sppymi,
2351 pls->sppxma, pls->sppyma );
2352 break;
2353
2354 case PLESC_HAS_TEXT:
2355 plD_text_wingdi( pls, (EscText *) ptr );
2356 break;
2357
2359 // Ignore double buffering requests. It causes problems with
2360 // strip charts and most machines are fast enough that it
2361 // does not make much of a difference.
2362 break;
2363
2364 case PLESC_XORMOD:
2365 if ( *(PLINT *) ( ptr ) == 0 )
2366 SetROP2( dev->hdc, R2_COPYPEN );
2367 else
2368 SetROP2( dev->hdc, R2_XORPEN );
2369 break;
2370
2372 //
2373// pldebug( "wingdi", "Start Rasterize\n" );
2374// // The image rasterization in PLplot uses fills. It indicates
2375// // the start and end with a PLESC_START_RASTERIZE and a
2376// // PLESC_END_RASTERIZE escape code. Thus, when these codes are
2377// // received, the wingdi driver will switch from rendering to the
2378// // display and render to a bitmap instead
2379//
2380// // Save the original plot area DC
2381// dev->save_hdc = dev->hdc;
2382//
2383// // Create a new DC and a bitmap
2384// dev->hdc = CreateCompatibleDC( dev->save_hdc );
2385// GetClientRect( dev->plot, &dev->raster_rect );
2386// dev->raster_bmp = CreateCompatibleBitmap( dev->hdc,
2387// dev->raster_rect.right,
2388// dev->raster_rect.bottom );
2389// SelectObject( dev->hdc, dev->raster_bmp );
2390 //
2391 break;
2392
2394 //
2395// pldebug( "wingdi", "Stop Rasterize\n" );
2396// // Display the bitmap
2397// BitBlt( dev->save_hdc,
2398// dev->raster_rect.left, dev->raster_rect.top,
2399// dev->raster_rect.right, dev->raster_rect.bottom,
2400// dev->hdc,
2401// dev->raster_rect.left, dev->raster_rect.top,
2402// SRCCOPY );
2403//
2404// // Cleanup
2405// if ( dev->raster_bmp != NULL )
2406// DeleteObject( dev->raster_bmp );
2407// if ( dev->hdc != NULL )
2408// DeleteDC( dev->hdc );
2409//
2410// // Restore the original DC
2411// dev->hdc = dev->save_hdc;
2412 //
2413 break;
2414
2415 case PLESC_TELLME:
2416 {
2417 struct passmeup *pup = ptr;
2418
2419 pup->hwnd = &( dev->plot );
2420 pup->hdc = &( dev->hdc );
2421 }
2422 break;
2423 }
2424}
2425
2426#else
2427int
2429{
2430 return ( 0 );
2431}
2432
2433#endif // PLD_wingdidev
void(* plD_line_fp)(struct PLStream_struct *, short, short, short, short)
Definition disptab.h:68
void(* plD_tidy_fp)(struct PLStream_struct *)
Definition disptab.h:72
void(* plD_bop_fp)(struct PLStream_struct *)
Definition disptab.h:71
void(* plD_wait_fp)(struct PLStream_struct *)
Definition disptab.h:75
void(* plD_state_fp)(struct PLStream_struct *, PLINT)
Definition disptab.h:73
void(* plD_eop_fp)(struct PLStream_struct *)
Definition disptab.h:70
@ plDevType_Interactive
Definition disptab.h:14
void(* plD_init_fp)(struct PLStream_struct *)
Definition disptab.h:67
void(* plD_esc_fp)(struct PLStream_struct *, PLINT, void *)
Definition disptab.h:74
void(* plD_polyline_fp)(struct PLStream_struct *, short *, short *, PLINT)
Definition disptab.h:69
PLDLLIMPEXP_DRIVER void plD_dispatch_init_wingdi(PLDispatchTable *pdt)
int plParseDrvOpts(DrvOpt *acc_opt)
Definition plargs.c:1461
static PLCHAR_VECTOR program
Definition plargs.c:178
void plRemakePlot(PLStream *pls)
Definition plbuf.c:1397
void plP_setpxl(PLFLT xpmm, PLFLT ypmm)
Definition plcore.c:4238
void plgesc(char *p_esc)
Definition plcore.c:3914
void plP_setphy(PLINT xmin, PLINT xmax, PLINT ymin, PLINT ymax)
Definition plcore.c:4249
static PLStream * pls[PL_NSTREAMS]
Definition plcore.h:88
void plwarn(PLCHAR_VECTOR errormsg)
Definition plctrl.c:1863
void plGinInit(PLGraphicsIn *gin)
Definition plctrl.c:2887
void plexit(PLCHAR_VECTOR errormsg)
Definition plctrl.c:1958
#define PLDLLIMPEXP_DRIVER
Definition pldll.h:81
static PLINT * buffer
Definition plfill.c:74
void plRotationShear(PLFLT *xFormMatrix, PLFLT *rotation, PLFLT *shear, PLFLT *stride)
Definition plot3d.c:2767
#define GRAPHICS_MODE
Definition plplotP.h:288
#define PI
Definition plplotP.h:290
#define PIXELS_X
Definition plplotP.h:304
#define PLSTATE_CMAP0
Definition plplotP.h:366
#define snprintf
Definition plplotP.h:235
#define PLSTATE_COLOR1
Definition plplotP.h:364
#define TRUE
Definition plplotP.h:176
#define FALSE
Definition plplotP.h:177
#define PLSTATE_CMAP1
Definition plplotP.h:367
@ DRV_INT
Definition plplotP.h:758
#define M_PI
Definition plplotP.h:119
#define PLSTATE_COLOR0
Definition plplotP.h:363
#define free_mem(a)
Definition plplotP.h:182
#define PIXELS_Y
Definition plplotP.h:305
#define PLESC_GETC
Definition plplot.h:283
#define PLESC_HAS_TEXT
Definition plplot.h:290
float PLFLT
Definition plplot.h:163
#define PLESC_START_RASTERIZE
Definition plplot.h:302
#define plspage
Definition plplot.h:831
#define PLESC_CLEAR
Definition plplot.h:288
#define PLESC_FILL
Definition plplot.h:279
#define PLESC_XORMOD
Definition plplot.h:286
int PLINT
Definition plplot.h:181
#define PLESC_END_RASTERIZE
Definition plplot.h:303
#define PLESC_DOUBLEBUFFERING
Definition plplot.h:285
void plP_script_scale(PLBOOL ifupper, PLINT *level, PLFLT *old_scale, PLFLT *scale, PLFLT *old_offset, PLFLT *offset)
Definition plsym.c:1302
static int color
Definition ps.c:78
int pldummy_wingdi()
Definition wingdi.c:2428