3992 lines
114 KiB
C
Executable File
3992 lines
114 KiB
C
Executable File
/*
|
|
* Easygl Version 2.0.1
|
|
* Written by Vaughn Betz at the University of Toronto, Department of *
|
|
* Electrical and Computer Engineering, with additions by Paul Leventis *
|
|
* and William Chow of Altera, and Guy Lemieux of the University of *
|
|
* Brish Columbia. *
|
|
* All rights reserved by U of T, etc. *
|
|
* *
|
|
* You may freely use this graphics interface for non-commercial purposes *
|
|
* as long as you leave the author info above in it.
|
|
* *
|
|
* Revision History: *
|
|
* *
|
|
* V2.0.1 Sept. 2012 (Vaughn Betz)
|
|
* - Fixed a bug in Win32 where postscript output would make the graphics
|
|
* crash when you redrew.
|
|
* - Made a cleaner makefile to simplify platform selection.
|
|
* - Commented and reorganized some of the code. Started cleaning up some of the
|
|
* win32 code. Win32 looks inefficient; it is saving and updating graphics contexts
|
|
* all the time even though we know when the context is valid vs. not-valid.
|
|
* TODO: make win32 work more like X11 (minimize gc updates).
|
|
*
|
|
* V2.0: Nov. 21, 2011 (Vaughn Betz)
|
|
* - Updated example code, and some cleanup and bug fixes to win32 code.
|
|
* - Removed some win32 code that had no X11 equivalent or wasn't well
|
|
* documented.
|
|
* - Used const char * where appropriate to get rid of g++ warnings.
|
|
* - Made interface to things like xor drawing more consistent, and added to
|
|
* example program.
|
|
* - Made a simpler (easygl.cpp) interface to the graphics library for
|
|
* use by undergraduate students.
|
|
*
|
|
* V1.06 : July 23, 2003 : (Guy Lemieux)
|
|
* - added some typecasts to cleanly compile with g++ and MS c++ tools
|
|
* - if WIN32 not defined, it defines X11 automatically
|
|
* - fixed X11 compilation; WIN32 broke some things
|
|
*
|
|
* V1.05 : July 26, 2001 : (William) *
|
|
* - changed keyboard detect function to accept an int (virtual key) *
|
|
* *
|
|
* V1.04 : June 29, 2001 : (William) *
|
|
* - added drawcurve(), fillcurve() using Bezier curves *
|
|
* (support WIN32 screen / ps) *
|
|
* - added pt on object capability : using a memory buffer to draw an *
|
|
* graphics objects, then query if a point fall on the object (bear the *
|
|
* object's colour) : object_start(), object_end(), pt_on_object() *
|
|
* - added drawellipticarc(), fillellipticarc() *
|
|
* - added findfontsize() to help find a pointsize of a given height *
|
|
* - extended t_report to keep xleft, xright, ytop, ybot *
|
|
* - added update_window() to set the window bb *
|
|
* *
|
|
* V1.03 : June 18, 2001 : (William) *
|
|
* - added change_button_text() *
|
|
* *
|
|
* V1.02 : June 13, 2001 : (William) *
|
|
* - extension to mouse click function : can tell if ctrl/shift keys are *
|
|
* pressed *
|
|
* *
|
|
* V1.01 : June 1, 2001 : (William) *
|
|
* - add tooltip support *
|
|
* *
|
|
* V1.0 : May 14, 2001 : (William) *
|
|
* - fixed a problem with line styles, initial release on the internet *
|
|
* *
|
|
* March 27, 2001 : (William) *
|
|
* - added setcolor_by_colorref to make more colors available (in Win32) *
|
|
* *
|
|
* February 16, 2001 : (William) *
|
|
* - added quick zoom using right mouse clicks *
|
|
* *
|
|
* February 11, 2001 : (William) *
|
|
* - can define cleanup(), passed in when calling init_graphics(), and *
|
|
* called when shutting down *
|
|
* *
|
|
* February 1, 2001 : (William) *
|
|
* - fix xor mode redraw problem *
|
|
* *
|
|
* September 19, 2000 : (William) *
|
|
* - can define mouse_move callback function *
|
|
* - can add separators in between buttons *
|
|
* *
|
|
* September 8, 2000 : (William) *
|
|
* - added result_structure(), *
|
|
* - can define background color in init_graphics *
|
|
* *
|
|
* August 10, 2000 : (William Chow, choww@eecg.utoronto.ca) *
|
|
* - Finished all Win32 support functions *
|
|
* - use XOR mode for window zooming box *
|
|
* - added double buffering feature *
|
|
* *
|
|
* January 12, 1999: (Paul) *
|
|
* - Fixed a bunch of stuff with the Win32 support (memory leaks, etc) *
|
|
* - Made the clipping function using the update rectangle for Win32 *
|
|
* *
|
|
* January 9, 1999: (Paul Leventis, leventi@eecg.utoronto.ca) *
|
|
* - Added Win32 support. Should work under Windows98/95/NT 4.0/NT 5.0. *
|
|
* - Added a check to deselect_all to determine whether the screen needs to *
|
|
* be updated or not. Should elminate flicker from mouse clicks *
|
|
* - Added invalidate_screen() call to graphics.c that in turn calls *
|
|
* update_screen, so this function was made non-static and added to the *
|
|
* header file. This is due to differences in the structure of Win32 *
|
|
* windowing apps. *
|
|
* - Win32 needs clipping (though done automatically, could be faster) *
|
|
* *
|
|
* Sept. 19, 1997: Incorporated Zoom Fit code of Haneef Mohammed at *
|
|
* Cypress. Makes it easy to zoom to a full view of the graphics. *
|
|
* *
|
|
* Sept. 11, 1997: Added the create_and destroy_button interface to *
|
|
* make it easy to add and destroy buttons from user code. Removed the *
|
|
* bnum parameter to the button functions, since it wasn't really needed. *
|
|
* *
|
|
* June 28, 1997: Added filled arc drawing primitive. Minor modifications *
|
|
* to PostScript driver to make the PostScript output slightly smaller. *
|
|
* *
|
|
* April 15, 1997: Added code to init_graphics so it waits for a window *
|
|
* to be exposed before returning. This ensures that users of non- *
|
|
* interactive graphics can never draw to a window before it is available. *
|
|
* *
|
|
* Feb. 24, 1997: Added code so the package will allocate a private *
|
|
* colormap if the default colormap doesn't have enough free colours. *
|
|
* *
|
|
* June 28, 1996: Converted all internal functions in graphics.c to have *
|
|
* internal (static) linkage to avoid any conflicts with user routines in *
|
|
* the rest of the program. *
|
|
* *
|
|
* June 12, 1996: Added setfontsize and setlinewidth attributes. Added *
|
|
* pre-clipping of objects for speed (and compactness of PS output) when *
|
|
* graphics are zoomed in. Rewrote PostScript engine to shrink the output *
|
|
* and make it easier to read. Made drawscreen a callback function passed *
|
|
* in rather than a global. Graphics attribute calls are more efficient -- *
|
|
* they check if they have to change anything before doing it. *
|
|
* *
|
|
* October 27, 1995: Added the message area, a callback function for *
|
|
* interacting with user button clicks, and implemented a workaround for a *
|
|
* Sun X Server bug that misdisplays extremely highly zoomed graphics. *
|
|
* *
|
|
* Jan. 13, 1995: Modified to incorporate PostScript Support. */
|
|
|
|
#ifndef NO_GRAPHICS // Strip everything out and just put in stubs if NO_GRAPHICS defined
|
|
|
|
/**********************************
|
|
* Common Preprocessor Directives *
|
|
**********************************/
|
|
|
|
#define TRUE 1
|
|
#define FALSE 0
|
|
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <string>
|
|
#include <iostream>
|
|
#include "graphics.h"
|
|
using namespace std;
|
|
|
|
|
|
#if defined(X11) || defined(WIN32)
|
|
|
|
/* Macros for translation from world to PostScript coordinates */
|
|
#define XPOST(worldx) (((worldx)-xleft)*ps_xmult + ps_left)
|
|
#define YPOST(worldy) (((worldy)-ybot)*ps_ymult + ps_bot)
|
|
|
|
/* Macros to convert from X Windows Internal Coordinates to my *
|
|
* World Coordinates. (This macro is used only rarely, so *
|
|
* the divides don't hurt speed). */
|
|
#define XTOWORLD(x) (((float) x)*xdiv + xleft)
|
|
#define YTOWORLD(y) (((float) y)*ydiv + ytop)
|
|
|
|
#ifndef max
|
|
#define max(a,b) (((a) > (b))? (a) : (b))
|
|
#endif
|
|
#ifndef min
|
|
#define min(a,b) ((a) > (b)? (b) : (a))
|
|
#endif
|
|
|
|
#define MWIDTH 104 /* width of menu window */
|
|
#define T_AREA_HEIGHT 24 /* Height of text window */
|
|
#define MAX_FONT_SIZE 24 /* Largest point size of text. */
|
|
// Some computers only have up to 24 point
|
|
#define PI 3.141592654
|
|
|
|
#define BUTTON_TEXT_LEN 100
|
|
#define BUFSIZE 1000
|
|
#endif
|
|
|
|
/*********************************************
|
|
* X-Windows Specific Preprocessor Directives *
|
|
*********************************************/
|
|
#ifdef X11
|
|
|
|
#include <X11/Xlib.h>
|
|
#include <X11/Xutil.h>
|
|
#include <X11/Xos.h>
|
|
#include <X11/Xatom.h>
|
|
|
|
/* Uncomment the line below if your X11 header files don't define XPointer */
|
|
/* typedef char *XPointer; */
|
|
|
|
// Really large pixel values cna make some X11 implementations draw crazy things
|
|
// (internal overflow in the X11 library). Use these constants to clip.
|
|
#define MAXPIXEL 15000
|
|
#define MINPIXEL -15000
|
|
|
|
#endif /* X11 Preprocessor Directives */
|
|
|
|
|
|
/*************************************************************
|
|
* Microsoft Windows (WIN32) Specific Preprocessor Directives *
|
|
*************************************************************/
|
|
#ifdef WIN32
|
|
#pragma warning(disable : 4996) // Turn off annoying warnings about strcmp.
|
|
|
|
#include <windows.h>
|
|
|
|
// Lines below are for displaying errors in a message box on windows.
|
|
#define SELECT_ERROR() { char msg[BUFSIZE]; sprintf (msg, "Error %i: Couldn't select graphics object on line %d of graphics.c\n", GetLastError(), __LINE__); MessageBox(NULL, msg, NULL, MB_OK); exit(-1); }
|
|
#define DELETE_ERROR() { char msg[BUFSIZE]; sprintf (msg, "Error %i: Couldn't delete graphics object on line %d of graphics.c\n", GetLastError(), __LINE__); MessageBox(NULL, msg, NULL, MB_OK); exit(-1); }
|
|
#define CREATE_ERROR() { char msg[BUFSIZE]; sprintf (msg, "Error %i: Couldn't create graphics object on line %d of graphics.c\n", GetLastError(), __LINE__); MessageBox(NULL, msg, NULL, MB_OK); exit(-1); }
|
|
#define DRAW_ERROR() { char msg[BUFSIZE]; sprintf (msg, "Error %i: Couldn't draw graphics object on line %d of graphics.c\n", GetLastError(), __LINE__); MessageBox(NULL, msg, NULL, MB_OK); exit(-1); }
|
|
|
|
/* Avoid funny clipping problems under windows that I suspect are caused by round-off
|
|
* in the Win32 libraries.
|
|
*/
|
|
#define MAXPIXEL 3000
|
|
#define MINPIXEL -3000
|
|
|
|
#define DEGTORAD(x) ((x)/180.*PI)
|
|
#define FONTMAG 1.3
|
|
#endif /* Win32 preprocessor Directives */
|
|
|
|
|
|
/*************************************************************
|
|
* Common Type Definitions *
|
|
*************************************************************/
|
|
|
|
/* Used to define where the output of drawscreen (graphics primitives in the user-controlled
|
|
* area) currently goes to: the screen or a postscript file.
|
|
*/
|
|
typedef enum {
|
|
SCREEN = 0,
|
|
POSTSCRIPT = 1
|
|
} t_display_type;
|
|
|
|
|
|
/* Indicates if this button displays text, a polygon or is just a separator.
|
|
*/
|
|
typedef enum {
|
|
BUTTON_TEXT = 0,
|
|
BUTTON_POLY,
|
|
BUTTON_SEPARATOR
|
|
} t_button_type;
|
|
|
|
|
|
/* Structure used to define the buttons on the right hand side of the main window (menu region).
|
|
* width, height: button size, in pixels.
|
|
* xleft, ytop: coordinates, in pixels, of the top-left corner of the button relative to its
|
|
* containing (menu) window.
|
|
* fcn: a callback function that is called when the button is pressed. This function takes one
|
|
* argument, a function pointer to the routine that can draw the graphics area (user routine).
|
|
* win, hwnd: X11 and Win32 data pointer to the window, respectively.
|
|
* button_type: indicates if this button displays text, a polygon or is just a separator.
|
|
* text: the text to display if this is a text button.
|
|
* poly: the polygon (up to 3 points right now) to display if this is a polygon button
|
|
* is_pressed: has the button been pressed, and is currently executing its callback?
|
|
* is_enabled: can you press this button right now? Visually will look "pushed in" when
|
|
* not enabled, and won't respond to clicks.
|
|
*/
|
|
typedef struct {
|
|
int width;
|
|
int height;
|
|
int xleft;
|
|
int ytop;
|
|
void (*fcn) (void (*drawscreen) (void));
|
|
#ifdef X11
|
|
Window win;
|
|
#else
|
|
HWND hwnd;
|
|
#endif
|
|
t_button_type type;
|
|
char text[BUTTON_TEXT_LEN];
|
|
int poly[3][2];
|
|
bool ispressed;
|
|
bool enabled;
|
|
} t_button;
|
|
|
|
|
|
/* Structure used to store overall graphics state variables.
|
|
* TODO: Gradually move more file scope variables in here.
|
|
* initialized: true if the graphics window & state have been
|
|
* created and initialized, false otherwise.
|
|
* disp_type: Selects SCREEN or POSTSCRIPT
|
|
* background_cindex: index of the window (or page for PS) background colour
|
|
*/
|
|
typedef struct {
|
|
bool initialized;
|
|
int disp_type;
|
|
int background_cindex;
|
|
} t_gl_state;
|
|
|
|
|
|
/*********************************************************************
|
|
* File scope variables. TODO: group in structs *
|
|
*********************************************************************/
|
|
|
|
// Need to initialize graphics_loaded to false, since checking it is
|
|
// how we avoid multiple construction or destruction of the graphics
|
|
// window.
|
|
static t_gl_state gl_state = {false, SCREEN, 0};
|
|
|
|
static const int menu_font_size = 12; /* Font for menus and dialog boxes. */
|
|
|
|
static t_button *button = NULL; /* [0..num_buttons-1] */
|
|
static int num_buttons = 0; /* Number of menu buttons */
|
|
|
|
static int display_width, display_height; /* screen size */
|
|
static int top_width, top_height; /* window size */
|
|
static float xleft, xright, ytop, ybot; /* world coordinates */
|
|
static float saved_xleft, saved_xright, saved_ytop, saved_ybot;
|
|
|
|
static float ps_left, ps_right, ps_top, ps_bot; /* Figure boundaries for *
|
|
* PostScript output, in PostScript coordinates. */
|
|
static float ps_xmult, ps_ymult; /* Transformation for PostScript. */
|
|
static float xmult, ymult; /* Transformation factors */
|
|
static float xdiv, ydiv;
|
|
|
|
static int currentcolor;
|
|
static int currentlinestyle;
|
|
static int currentlinewidth;
|
|
static int currentfontsize;
|
|
static e_draw_mode current_draw_mode;
|
|
|
|
/* For PostScript output */
|
|
static FILE *ps;
|
|
|
|
static int ProceedPressed;
|
|
|
|
static char statusMessage[BUFSIZE] = ""; /* User message to display */
|
|
|
|
static bool font_is_loaded[MAX_FONT_SIZE + 1];
|
|
static bool get_keypress_input, get_mouse_move_input;
|
|
static const char *ps_cnames[NUM_COLOR] = {"white", "black", "grey55", "grey75",
|
|
"blue", "green", "yellow", "cyan", "red", "darkgreen", "magenta",
|
|
"bisque", "lightblue", "thistle", "plum", "khaki", "coral",
|
|
"turquoise", "mediumpurple", "darkslateblue", "darkkhaki"};
|
|
|
|
|
|
/*********************************************
|
|
* Common Subroutine Declarations *
|
|
*********************************************/
|
|
|
|
static void *my_malloc(int ibytes);
|
|
static void *my_realloc(void *memblk, int ibytes);
|
|
static int xcoord (float worldx);
|
|
static int ycoord (float worldy);
|
|
static void force_setcolor(int cindex);
|
|
static void force_setlinestyle(int linestyle);
|
|
static void force_setlinewidth(int linewidth);
|
|
static void force_setfontsize (int pointsize);
|
|
static void load_font(int pointsize);
|
|
|
|
static void reset_common_state ();
|
|
static void build_default_menu (void);
|
|
|
|
/* Function declarations for button responses */
|
|
static void translate_up (void (*drawscreen) (void));
|
|
static void translate_left (void (*drawscreen) (void));
|
|
static void translate_right (void (*drawscreen) (void));
|
|
static void translate_down (void (*drawscreen) (void));
|
|
static void zoom_in (void (*drawscreen) (void));
|
|
static void zoom_out (void (*drawscreen) (void));
|
|
static void zoom_fit (void (*drawscreen) (void));
|
|
static void adjustwin (void (*drawscreen) (void));
|
|
static void postscript (void (*drawscreen) (void));
|
|
static void proceed (void (*drawscreen) (void));
|
|
static void quit (void (*drawscreen) (void));
|
|
static void map_button (int bnum);
|
|
static void unmap_button (int bnum);
|
|
|
|
#ifdef X11
|
|
|
|
/*************************************************
|
|
* X-Windows Specific File-scope Variables *
|
|
**************************************************/
|
|
static Display *display;
|
|
static int screen_num;
|
|
static GC gc, gcxor, gc_menus, current_gc;
|
|
static XFontStruct *font_info[MAX_FONT_SIZE+1]; /* Data for each size */
|
|
static Window toplevel, menu, textarea; /* various windows */
|
|
static Colormap private_cmap; /* "None" unless a private cmap was allocated. */
|
|
|
|
/* Color indices passed back from X Windows. */
|
|
static int colors[NUM_COLOR];
|
|
|
|
|
|
/****************************************************
|
|
* X-Windows Specific Subroutine Declarations *
|
|
*****************************************************/
|
|
|
|
static Bool test_if_exposed (Display *disp, XEvent *event_ptr,
|
|
XPointer dummy);
|
|
static void build_textarea (void);
|
|
static void drawbut (int bnum);
|
|
static int which_button (Window win);
|
|
|
|
static void turn_on_off (int pressed);
|
|
static void drawmenu(void);
|
|
|
|
#endif /* X11 Declarations */
|
|
|
|
|
|
|
|
#ifdef WIN32
|
|
|
|
/*****************************************************
|
|
* Microsoft Windows (Win32) File Scope Variables *
|
|
*****************************************************/
|
|
static const int win32_line_styles[2] = { PS_SOLID, PS_DASH };
|
|
|
|
static const COLORREF win32_colors[NUM_COLOR] = { RGB(255, 255, 255),
|
|
RGB(0, 0, 0), RGB(128, 128, 128), RGB(192, 192, 192), RGB(0, 0, 255),
|
|
RGB(0, 255, 0), RGB(255, 255, 0), RGB(0, 255, 255), RGB(255, 0, 0), RGB(0, 128, 0),
|
|
RGB(255, 0, 255), RGB(255, 228, 196), RGB(173, 216, 230), RGB(216, 191, 216), RGB(221, 160, 221),
|
|
RGB(240, 230, 140), RGB(255, 127, 80), RGB(64, 224, 208), RGB(147, 112, 219), RGB(72, 61, 139),
|
|
RGB(189, 183, 107)};
|
|
|
|
static TCHAR szAppName[256],
|
|
szGraphicsName[] = TEXT("VPR Graphics"),
|
|
szStatusName[] = TEXT("VPR Status"),
|
|
szButtonsName[] = TEXT("VPR Buttons");
|
|
static HPEN hGraphicsPen;
|
|
static HBRUSH hGraphicsBrush, hGrayBrush;
|
|
static HDC hGraphicsDC, hForegroundDC, hBackgroundDC,
|
|
hCurrentDC, /* WC : double-buffer */
|
|
hObjtestDC, hAllObjtestDC; /* object test */
|
|
|
|
/* WC */
|
|
static HFONT hGraphicsFont;
|
|
static LOGFONT *font_info[MAX_FONT_SIZE+1]; /* Data for each size */
|
|
|
|
/* Handles to the top level window and 3 subwindows. */
|
|
static HWND hMainWnd, hGraphicsWnd, hButtonsWnd, hStatusWnd;
|
|
|
|
static int cxClient, cyClient;
|
|
|
|
/* These are used for the "Window" graphics button. They keep track of whether we're entering
|
|
* the window rectangle to zoom to, etc.
|
|
*/
|
|
static int windowAdjustFlag = 0, adjustButton = -1;
|
|
static RECT adjustRect, updateRect;
|
|
|
|
static boolean InEventLoop = FALSE;
|
|
|
|
//static HBITMAP buttonImages[4];
|
|
|
|
|
|
/*******************************************************************
|
|
* Win32-specific subroutine declarations *
|
|
*******************************************************************/
|
|
|
|
/* Callback functions for the top-level window and 3 sub-windows.
|
|
* Windows uses an odd mix of events and callbacks, so it needs these.
|
|
*/
|
|
static LRESULT CALLBACK GraphicsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
|
|
static LRESULT CALLBACK StatusWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
|
|
static LRESULT CALLBACK ButtonsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
|
|
static LRESULT CALLBACK MainWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
|
|
|
|
|
|
// For Win32, need to save pointers to these callback functions at file
|
|
// scope, since windows has a bizarre event loop structure where you poll
|
|
// for events, but then dispatch the event and get called via a callback
|
|
// from windows (GraphicsWND, below). I can't figure out why windows
|
|
// does things this way, but it is what makes saving these function pointers
|
|
// necessary. VB.
|
|
|
|
static void (*mouseclick_ptr)(float x, float y);
|
|
static void (*mousemove_ptr)(float x, float y);
|
|
static void (*keypress_ptr)(char entered_char);
|
|
static void (*drawscreen_ptr)(void);
|
|
|
|
static void invalidate_screen();
|
|
|
|
static void reset_win32_state ();
|
|
static void win32_drain_message_queue ();
|
|
|
|
void drawtoscreen();
|
|
void displaybuffer();
|
|
|
|
|
|
#endif /* Win32 Declarations */
|
|
|
|
/*********************************************************
|
|
* Common Subroutine Definitions *
|
|
*********************************************************/
|
|
|
|
|
|
/* safer malloc */
|
|
static void *my_malloc(int ibytes) {
|
|
void *mem;
|
|
|
|
mem = (void*)malloc(ibytes);
|
|
if (mem == NULL) {
|
|
printf("memory allocation failed!");
|
|
exit(-1);
|
|
}
|
|
|
|
return mem;
|
|
}
|
|
|
|
/* safer realloc */
|
|
static void *my_realloc(void *memblk, int ibytes) {
|
|
void *mem;
|
|
|
|
mem = (void*)realloc(memblk, ibytes);
|
|
if (mem == NULL) {
|
|
printf("memory allocation failed!");
|
|
exit(-1);
|
|
}
|
|
|
|
return mem;
|
|
}
|
|
|
|
|
|
/* Translates from my internal coordinates to real-world coordinates *
|
|
* in the x direction. Add 0.5 at end for extra half-pixel accuracy. */
|
|
static int xcoord (float worldx)
|
|
{
|
|
int winx;
|
|
|
|
winx = (int) ((worldx-xleft)*xmult + 0.5);
|
|
|
|
/* Avoids overflow in the Window routines. This will allow horizontal *
|
|
* and vertical lines to be drawn correctly regardless of zooming, but *
|
|
* will cause diagonal lines that go way off screen to change their *
|
|
* slope as you zoom in. The only way I can think of to completely fix *
|
|
* this problem is to do all the clipping in advance in floating point, *
|
|
* then convert to integers and call Windows. This is a lot of extra *
|
|
* coding, and means that coordinates will be clipped twice, even though *
|
|
* this "Super Zoom" problem won't occur unless users zoom way in on *
|
|
* the graphics. */
|
|
|
|
winx = max (winx, MINPIXEL);
|
|
winx = min (winx, MAXPIXEL);
|
|
|
|
return (winx);
|
|
}
|
|
|
|
|
|
/* Translates from my internal coordinates to real-world coordinates *
|
|
* in the y direction. Add 0.5 at end for extra half-pixel accuracy. */
|
|
static int ycoord (float worldy)
|
|
{
|
|
int winy;
|
|
|
|
winy = (int) ((worldy-ytop)*ymult + 0.5);
|
|
|
|
/* Avoid overflow in the X/Win32 Window routines. */
|
|
winy = max (winy, MINPIXEL);
|
|
winy = min (winy, MAXPIXEL);
|
|
|
|
return (winy);
|
|
}
|
|
|
|
|
|
#ifdef WIN32
|
|
static void invalidate_screen(void)
|
|
{
|
|
/* Tells the graphics engine to redraw the graphics display since information has changed */
|
|
|
|
if(!InvalidateRect(hGraphicsWnd, NULL, FALSE))
|
|
DRAW_ERROR();
|
|
if(!UpdateWindow(hGraphicsWnd))
|
|
DRAW_ERROR();
|
|
}
|
|
#endif
|
|
|
|
/* Sets the current graphics context colour to cindex, regardless of whether we think it is
|
|
* needed or not.
|
|
*/
|
|
static void force_setcolor (int cindex)
|
|
{
|
|
currentcolor = cindex;
|
|
|
|
if (gl_state.disp_type == SCREEN) {
|
|
#ifdef X11
|
|
XSetForeground (display, current_gc, colors[cindex]);
|
|
#else /* Win32 */
|
|
int win_linestyle;
|
|
LOGBRUSH lb;
|
|
lb.lbStyle = BS_SOLID;
|
|
lb.lbColor = win32_colors[cindex];
|
|
lb.lbHatch = (LONG)NULL;
|
|
win_linestyle = win32_line_styles[currentlinestyle];
|
|
|
|
if(!DeleteObject(hGraphicsPen))
|
|
DELETE_ERROR();
|
|
|
|
int linewidth = max (currentlinewidth, 1); // Win32 won't draw 0 width dashed lines.
|
|
hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win_linestyle |
|
|
PS_ENDCAP_FLAT, linewidth, &lb, (LONG)NULL, NULL);
|
|
if(!hGraphicsPen)
|
|
CREATE_ERROR();
|
|
|
|
if(!DeleteObject(hGraphicsBrush))
|
|
DELETE_ERROR();
|
|
hGraphicsBrush = CreateSolidBrush(win32_colors[currentcolor]);
|
|
if(!hGraphicsBrush)
|
|
CREATE_ERROR();
|
|
#endif
|
|
}
|
|
else {
|
|
fprintf (ps,"%s\n", ps_cnames[cindex]);
|
|
}
|
|
}
|
|
|
|
|
|
/* Sets the current graphics context colour to cindex if it differs from the old colour */
|
|
void setcolor (int cindex)
|
|
{
|
|
if (currentcolor != cindex)
|
|
force_setcolor (cindex);
|
|
|
|
}
|
|
|
|
|
|
/* Sets the current graphics context color to the index that corresponds to the
|
|
* string name passed in. Slower, but maybe more convenient for simple
|
|
* client code.
|
|
*/
|
|
void setcolor (string cname) {
|
|
int icolor = -1;
|
|
for (int i = 0; i < NUM_COLOR; i++) {
|
|
if (cname == ps_cnames[i]) {
|
|
icolor = i;
|
|
break;
|
|
}
|
|
}
|
|
if (icolor == -1) {
|
|
cout << "Error: unknown color " << cname << endl;
|
|
}
|
|
else {
|
|
setcolor (icolor);
|
|
}
|
|
}
|
|
|
|
int getcolor() {
|
|
return currentcolor;
|
|
}
|
|
|
|
/* Sets the current linestyle to linestyle in the graphics context.
|
|
* Note SOLID is 0 and DASHED is 1 for linestyle.
|
|
*/
|
|
static void force_setlinestyle (int linestyle)
|
|
{
|
|
currentlinestyle = linestyle;
|
|
|
|
if (gl_state.disp_type == SCREEN) {
|
|
|
|
#ifdef X11
|
|
static int x_vals[2] = {LineSolid, LineOnOffDash};
|
|
XSetLineAttributes (display, current_gc, currentlinewidth, x_vals[linestyle],
|
|
CapButt, JoinMiter);
|
|
#else // Win32
|
|
LOGBRUSH lb;
|
|
lb.lbStyle = BS_SOLID;
|
|
lb.lbColor = win32_colors[currentcolor];
|
|
lb.lbHatch = (LONG)NULL;
|
|
int win_linestyle = win32_line_styles[linestyle];
|
|
|
|
if(!DeleteObject(hGraphicsPen))
|
|
DELETE_ERROR();
|
|
int linewidth = max (currentlinewidth, 1); // Win32 won't draw 0 width dashed lines.
|
|
hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win_linestyle |
|
|
PS_ENDCAP_FLAT, linewidth, &lb, (LONG)NULL, NULL);
|
|
if(!hGraphicsPen)
|
|
CREATE_ERROR();
|
|
#endif
|
|
}
|
|
|
|
else {
|
|
if (linestyle == SOLID)
|
|
fprintf (ps,"linesolid\n");
|
|
else if (linestyle == DASHED)
|
|
fprintf (ps, "linedashed\n");
|
|
else {
|
|
printf ("Error: invalid linestyle: %d\n", linestyle);
|
|
exit (1);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Change the linestyle in the graphics context only if it differs from the current
|
|
* linestyle.
|
|
*/
|
|
void setlinestyle (int linestyle)
|
|
{
|
|
if (linestyle != currentlinestyle)
|
|
force_setlinestyle (linestyle);
|
|
}
|
|
|
|
|
|
/* Sets current linewidth in the graphics context.
|
|
* linewidth should be greater than or equal to 0 to make any sense.
|
|
*/
|
|
static void force_setlinewidth (int linewidth)
|
|
{
|
|
currentlinewidth = linewidth;
|
|
|
|
if (gl_state.disp_type == SCREEN) {
|
|
|
|
#ifdef X11
|
|
static int x_vals[2] = {LineSolid, LineOnOffDash};
|
|
XSetLineAttributes (display, current_gc, linewidth, x_vals[currentlinestyle],
|
|
CapButt, JoinMiter);
|
|
#else /* Win32 */
|
|
LOGBRUSH lb;
|
|
lb.lbStyle = BS_SOLID;
|
|
lb.lbColor = win32_colors[currentcolor];
|
|
lb.lbHatch = (LONG)NULL;
|
|
int win_linestyle = win32_line_styles[currentlinestyle];
|
|
|
|
if(!DeleteObject(hGraphicsPen))
|
|
DELETE_ERROR();
|
|
if (linewidth == 0)
|
|
linewidth = 1; // Win32 won't draw dashed 0 width lines.
|
|
hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win_linestyle |
|
|
PS_ENDCAP_FLAT, linewidth, &lb, (LONG)NULL, NULL);
|
|
if(!hGraphicsPen)
|
|
CREATE_ERROR();
|
|
#endif
|
|
}
|
|
|
|
else {
|
|
fprintf(ps,"%d setlinewidth\n", linewidth);
|
|
}
|
|
}
|
|
|
|
|
|
/* Sets the linewidth in the grahpics context, if it differs from the current value.
|
|
*/
|
|
void setlinewidth (int linewidth)
|
|
{
|
|
if (linewidth != currentlinewidth)
|
|
force_setlinewidth (linewidth);
|
|
}
|
|
|
|
|
|
/* Force the selected fontsize to be applied to the graphics context,
|
|
* whether or not it appears to match the current fontsize. This is necessary
|
|
* when switching between postscript and window out, for example.
|
|
* Valid point sizes are between 1 and MAX_FONT_SIZE.
|
|
*/
|
|
static void force_setfontsize (int pointsize)
|
|
{
|
|
|
|
if (pointsize < 1)
|
|
pointsize = 1;
|
|
#ifdef WIN32
|
|
pointsize = (int)((float)pointsize * FONTMAG);
|
|
#endif
|
|
if (pointsize > MAX_FONT_SIZE)
|
|
pointsize = MAX_FONT_SIZE;
|
|
|
|
currentfontsize = pointsize;
|
|
|
|
if (gl_state.disp_type == SCREEN) {
|
|
load_font (pointsize);
|
|
#ifdef X11
|
|
XSetFont(display, current_gc, font_info[pointsize]->fid);
|
|
#else /* Win32 */
|
|
if(!DeleteObject(hGraphicsFont))
|
|
DELETE_ERROR();
|
|
hGraphicsFont = CreateFontIndirect(font_info[pointsize]);
|
|
if(!hGraphicsFont)
|
|
CREATE_ERROR();
|
|
if(!SelectObject(hGraphicsDC, hGraphicsFont) )
|
|
SELECT_ERROR();
|
|
|
|
#endif
|
|
}
|
|
|
|
else {
|
|
/* PostScript: set up font and centering function */
|
|
fprintf(ps,"%d setfontsize\n",pointsize);
|
|
}
|
|
}
|
|
|
|
|
|
/* For efficiency, this routine doesn't do anything if no change is
|
|
* implied. If you want to force the graphics context or PS file
|
|
* to have font info set, call force_setfontsize (this is necessary
|
|
* in initialization and X11 / Postscript switches).
|
|
*/
|
|
void setfontsize (int pointsize)
|
|
{
|
|
|
|
if (pointsize != currentfontsize)
|
|
force_setfontsize (pointsize);
|
|
}
|
|
|
|
|
|
/* Puts a triangle in the poly array for button[bnum]. Haven't made this work for
|
|
* win32 yet and instead put "U", "D" excetra on the arrow buttons.
|
|
* VB To-do: make work for win32 someday.
|
|
*/
|
|
#ifdef X11
|
|
static void setpoly (int bnum, int xc, int yc, int r, float theta)
|
|
{
|
|
int i;
|
|
|
|
button[bnum].type = BUTTON_POLY;
|
|
for (i=0;i<3;i++) {
|
|
button[bnum].poly[i][0] = (int) (xc + r*cos(theta) + 0.5);
|
|
button[bnum].poly[i][1] = (int) (yc + r*sin(theta) + 0.5);
|
|
theta += (float)(2*PI/3);
|
|
}
|
|
}
|
|
#endif // X11
|
|
|
|
|
|
/* Maps a button onto the screen and set it up for input, etc. */
|
|
static void map_button (int bnum)
|
|
{
|
|
button[bnum].ispressed = 0;
|
|
|
|
if (button[bnum].type != BUTTON_SEPARATOR) {
|
|
#ifdef X11
|
|
button[bnum].win = XCreateSimpleWindow(display,menu,
|
|
button[bnum].xleft, button[bnum].ytop, button[bnum].width,
|
|
button[bnum].height, 0, colors[WHITE], colors[LIGHTGREY]);
|
|
XMapWindow (display, button[bnum].win);
|
|
XSelectInput (display, button[bnum].win, ButtonPressMask);
|
|
#else
|
|
button[bnum].hwnd = CreateWindow( TEXT("button"), TEXT(button[bnum].text),
|
|
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, button[bnum].xleft, button[bnum].ytop,
|
|
button[bnum].width, button[bnum].height, hButtonsWnd, (HMENU)(200+bnum),
|
|
(HINSTANCE) GetWindowLong(hMainWnd, GWL_HINSTANCE), NULL);
|
|
if(!InvalidateRect(hButtonsWnd, NULL, TRUE))
|
|
DRAW_ERROR();
|
|
if(!UpdateWindow(hButtonsWnd))
|
|
DRAW_ERROR();
|
|
#endif
|
|
}
|
|
else { // Separator, not a button.
|
|
#ifdef X11
|
|
button[bnum].win = -1;
|
|
#else // WIN32
|
|
button[bnum].hwnd = NULL;
|
|
if(!InvalidateRect(hButtonsWnd, NULL, TRUE))
|
|
DRAW_ERROR();
|
|
if(!UpdateWindow(hButtonsWnd))
|
|
DRAW_ERROR();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
static void unmap_button (int bnum)
|
|
{
|
|
/* Unmaps (removes) a button from the screen. */
|
|
if (button[bnum].type != BUTTON_SEPARATOR) {
|
|
#ifdef X11
|
|
XUnmapWindow (display, button[bnum].win);
|
|
#else
|
|
if(!DestroyWindow(button[bnum].hwnd))
|
|
DRAW_ERROR();
|
|
if(!InvalidateRect(hButtonsWnd, NULL, TRUE))
|
|
DRAW_ERROR();
|
|
if(!UpdateWindow(hButtonsWnd))
|
|
DRAW_ERROR();
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
/* Creates a new button below the button containing prev_button_text. *
|
|
* The text and button function are set according to button_text and *
|
|
* button_func, respectively. */
|
|
void create_button (const char *prev_button_text , const char *button_text,
|
|
void (*button_func) (void (*drawscreen) (void)))
|
|
{
|
|
int i, bnum, space, bheight;
|
|
t_button_type button_type = BUTTON_TEXT;
|
|
|
|
space = 8;
|
|
|
|
/* Only allow new buttons that are text or separator (not poly) types.
|
|
* They can also only go after buttons that are text buttons.
|
|
*/
|
|
|
|
bnum = -1;
|
|
|
|
for (i=0; i < num_buttons;i++) {
|
|
if (button[i].type == BUTTON_TEXT &&
|
|
strcmp (button[i].text, prev_button_text) == 0) {
|
|
bnum = i + 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bnum == -1) {
|
|
printf ("Error in create_button: button with text %s not found.\n",
|
|
prev_button_text);
|
|
exit (1);
|
|
}
|
|
|
|
button = (t_button *) my_realloc (button, (num_buttons+1) * sizeof (t_button));
|
|
/* NB: Requirement that you specify the button that this button goes under *
|
|
* guarantees that button[num_buttons-2] exists and is a text button. */
|
|
|
|
/* Special string to make a separator. */
|
|
if (!strncmp(button_text, "---", 3)) {
|
|
bheight = 2;
|
|
button_type = BUTTON_SEPARATOR;
|
|
}
|
|
else
|
|
bheight = 26;
|
|
|
|
for (i=num_buttons;i>bnum;i--) {
|
|
button[i].xleft = button[i-1].xleft;
|
|
button[i].ytop = button[i-1].ytop + bheight + space;
|
|
button[i].height = button[i-1].height;
|
|
button[i].width = button[i-1].width;
|
|
button[i].type = button[i-1].type;
|
|
strcpy (button[i].text, button[i-1].text);
|
|
button[i].fcn = button[i-1].fcn;
|
|
button[i].ispressed = button[i-1].ispressed;
|
|
button[i].enabled = button[i-1].enabled;
|
|
unmap_button (i-1);
|
|
}
|
|
|
|
i = bnum;
|
|
button[i].xleft = 6;
|
|
button[i].ytop = button[i-1].ytop + button[i-1].height + space;
|
|
button[i].height = bheight;
|
|
button[i].width = 90;
|
|
button[i].type = button_type;
|
|
strncpy (button[i].text, button_text, BUTTON_TEXT_LEN);
|
|
button[i].fcn = button_func;
|
|
button[i].ispressed = false;
|
|
button[i].enabled = true;
|
|
|
|
num_buttons++;
|
|
|
|
for (i = 0; i<num_buttons;i++)
|
|
map_button (i);
|
|
}
|
|
|
|
|
|
/* Destroys the button with text button_text. */
|
|
void
|
|
destroy_button (const char *button_text)
|
|
{
|
|
int i, bnum, space, bheight;
|
|
|
|
bnum = -1;
|
|
space = 8;
|
|
for (i = 0; i < num_buttons; i++) {
|
|
if (button[i].type == BUTTON_TEXT &&
|
|
strcmp (button[i].text, button_text) == 0) {
|
|
bnum = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bnum == -1) {
|
|
printf ("Error in destroy_button: button with text %s not found.\n",
|
|
button_text);
|
|
exit (1);
|
|
}
|
|
|
|
bheight = button[bnum].height;
|
|
|
|
for (i=bnum+1;i<num_buttons;i++) {
|
|
button[i-1].xleft = button[i].xleft;
|
|
button[i-1].ytop = button[i].ytop - bheight - space;
|
|
button[i-1].height = button[i].height;
|
|
button[i-1].width = button[i].width;
|
|
button[i-1].type = button[i].type;
|
|
strcpy (button[i-1].text, button[i].text);
|
|
button[i-1].fcn = button[i].fcn;
|
|
button[i-1].ispressed = button[i].ispressed;
|
|
button[i-1].enabled = button[i].enabled;
|
|
unmap_button (i);
|
|
}
|
|
unmap_button(bnum);
|
|
|
|
button = (t_button *) my_realloc (button, (num_buttons-1) * sizeof (t_button));
|
|
|
|
num_buttons--;
|
|
|
|
for (i=bnum; i<num_buttons;i++)
|
|
map_button (i);
|
|
}
|
|
|
|
|
|
/* Open the toplevel window, get the colors, 2 graphics *
|
|
* contexts, load a font, and set up the toplevel window *
|
|
* Calls build_default_menu to set up the default menu. */
|
|
void
|
|
init_graphics (const char *window_name, int cindex)
|
|
{
|
|
if (gl_state.initialized) // Singleton graphics.
|
|
return;
|
|
|
|
reset_common_state ();
|
|
#ifdef WIN32
|
|
reset_win32_state ();
|
|
#endif
|
|
|
|
gl_state.disp_type = SCREEN;
|
|
gl_state.background_cindex = cindex;
|
|
|
|
|
|
#ifdef X11
|
|
char *display_name = NULL;
|
|
int x, y; /* window position */
|
|
unsigned int border_width = 2; /* ignored by OpenWindows */
|
|
XTextProperty windowName;
|
|
|
|
/* X Windows' names for my colours. */
|
|
const char *cnames[NUM_COLOR] = {"white", "black", "grey55", "grey75", "blue",
|
|
"green", "yellow", "cyan", "red", "RGBi:0.0/0.5/0.0", "magenta",
|
|
"bisque", "lightblue", "thistle", "plum", "khaki", "coral",
|
|
"turquoise", "mediumpurple", "darkslateblue", "darkkhaki" };
|
|
|
|
XColor exact_def;
|
|
Colormap cmap;
|
|
unsigned long valuemask = 0; /* ignore XGCvalues and use defaults */
|
|
XGCValues values;
|
|
XEvent event;
|
|
|
|
/* connect to X server */
|
|
if ( (display=XOpenDisplay(display_name)) == NULL )
|
|
{
|
|
fprintf( stderr, "Cannot connect to X server %s\n",
|
|
XDisplayName(display_name));
|
|
exit( -1 );
|
|
}
|
|
|
|
/* get screen size from display structure macro */
|
|
screen_num = DefaultScreen(display);
|
|
display_width = DisplayWidth(display, screen_num);
|
|
display_height = DisplayHeight(display, screen_num);
|
|
|
|
x = 0;
|
|
y = 0;
|
|
|
|
top_width = 2 * display_width / 3;
|
|
top_height = 4 * display_height / 5;
|
|
|
|
cmap = DefaultColormap(display, screen_num);
|
|
private_cmap = None;
|
|
|
|
for (int i=0;i<NUM_COLOR;i++) {
|
|
if (!XParseColor(display,cmap,cnames[i],&exact_def)) {
|
|
fprintf(stderr, "Color name %s not in database", cnames[i]);
|
|
exit(-1);
|
|
}
|
|
if (!XAllocColor(display, cmap, &exact_def)) {
|
|
fprintf(stderr, "Couldn't allocate color %s.\n",cnames[i]);
|
|
|
|
if (private_cmap == None) {
|
|
fprintf(stderr, "Will try to allocate a private colourmap.\n");
|
|
fprintf(stderr, "Colours will only display correctly when your "
|
|
"cursor is in the graphics window.\n"
|
|
"Exit other colour applications and rerun this "
|
|
"program if you don't like that.\n\n");
|
|
|
|
private_cmap = XCopyColormapAndFree (display, cmap);
|
|
cmap = private_cmap;
|
|
if (!XAllocColor (display, cmap, &exact_def)) {
|
|
fprintf (stderr, "Couldn't allocate color %s as private.\n",
|
|
cnames[i]);
|
|
exit (1);
|
|
}
|
|
}
|
|
|
|
else {
|
|
fprintf (stderr, "Couldn't allocate color %s as private.\n",
|
|
cnames[i]);
|
|
exit (1);
|
|
}
|
|
}
|
|
colors[i] = exact_def.pixel;
|
|
} // End setting up colours
|
|
|
|
toplevel = XCreateSimpleWindow(display,RootWindow(display,screen_num),
|
|
x, y, top_width, top_height, border_width, colors[BLACK],
|
|
colors[cindex]);
|
|
|
|
if (private_cmap != None)
|
|
XSetWindowColormap (display, toplevel, private_cmap);
|
|
|
|
XSelectInput (display, toplevel, ExposureMask | StructureNotifyMask |
|
|
ButtonPressMask | PointerMotionMask | KeyPressMask);
|
|
|
|
/* Create default Graphics Contexts. valuemask = 0 -> use defaults. */
|
|
current_gc = gc = XCreateGC(display, toplevel, valuemask, &values);
|
|
gc_menus = XCreateGC(display, toplevel, valuemask, &values);
|
|
|
|
/* Create XOR graphics context for Rubber Banding */
|
|
values.function = GXxor;
|
|
values.foreground = colors[cindex];
|
|
gcxor = XCreateGC(display, toplevel, (GCFunction | GCForeground),
|
|
&values);
|
|
|
|
/* specify font for menus. */
|
|
load_font(menu_font_size);
|
|
XSetFont(display, gc_menus, font_info[menu_font_size]->fid);
|
|
|
|
/* Set drawing defaults for user-drawable area. Use whatever the *
|
|
* initial values of the current stuff was set to. */
|
|
force_setfontsize(currentfontsize);
|
|
force_setcolor (currentcolor);
|
|
force_setlinestyle (currentlinestyle);
|
|
force_setlinewidth (currentlinewidth);
|
|
|
|
// Need a non-const name to pass to XStringListTo...
|
|
// (even though X11 won't change it).
|
|
char *window_name_copy = (char *) my_malloc (BUFSIZE * sizeof (char));
|
|
strncpy (window_name_copy, window_name, BUFSIZE);
|
|
XStringListToTextProperty(&window_name_copy, 1, &windowName);
|
|
free (window_name_copy);
|
|
window_name_copy = NULL;
|
|
|
|
XSetWMName (display, toplevel, &windowName);
|
|
/* XSetWMIconName (display, toplevel, &windowName); */
|
|
|
|
/* XStringListToTextProperty copies the window_name string into *
|
|
* windowName.value. Free this memory now. */
|
|
|
|
free (windowName.value);
|
|
|
|
XMapWindow (display, toplevel);
|
|
build_textarea ();
|
|
build_default_menu ();
|
|
|
|
/* The following is completely unnecessary if the user is using the *
|
|
* interactive (event_loop) graphics. It waits for the first Expose *
|
|
* event before returning so that I can tell the window manager has got *
|
|
* the top-level window up and running. Thus the user can start drawing *
|
|
* into this window immediately, and there's no danger of the window not *
|
|
* being ready and output being lost. */
|
|
XPeekIfEvent (display, &event, test_if_exposed, NULL);
|
|
|
|
#else /* WIN32 */
|
|
WNDCLASS wndclass;
|
|
HINSTANCE hInstance = GetModuleHandle(NULL);
|
|
int x, y;
|
|
LOGBRUSH lb;
|
|
lb.lbStyle = BS_SOLID;
|
|
lb.lbColor = win32_colors[currentcolor];
|
|
lb.lbHatch = (LONG)NULL;
|
|
x = 0;
|
|
y = 0;
|
|
|
|
/* get screen size from display structure macro */
|
|
display_width = GetSystemMetrics( SM_CXSCREEN );
|
|
if (!(display_width))
|
|
CREATE_ERROR();
|
|
display_height = GetSystemMetrics( SM_CYSCREEN );
|
|
if (!(display_height))
|
|
CREATE_ERROR();
|
|
top_width = 2*display_width/3;
|
|
top_height = 4*display_height/5;
|
|
|
|
/* Grab the Application name */
|
|
wsprintf(szAppName, TEXT(window_name));
|
|
|
|
//hGraphicsPen = CreatePen(win32_line_styles[SOLID], 1, win32_colors[BLACK]);
|
|
hGraphicsPen = ExtCreatePen(PS_GEOMETRIC | win32_line_styles[currentlinestyle] |
|
|
PS_ENDCAP_FLAT, 1, &lb, (LONG)NULL, NULL);
|
|
if(!hGraphicsPen)
|
|
CREATE_ERROR();
|
|
hGraphicsBrush = CreateSolidBrush(win32_colors[DARKGREY]);
|
|
if(!hGraphicsBrush)
|
|
CREATE_ERROR();
|
|
hGrayBrush = CreateSolidBrush(win32_colors[LIGHTGREY]);
|
|
if(!hGrayBrush)
|
|
CREATE_ERROR();
|
|
|
|
load_font (currentfontsize);
|
|
hGraphicsFont = CreateFontIndirect(font_info[currentfontsize]);
|
|
if (!hGraphicsFont)
|
|
CREATE_ERROR();
|
|
|
|
/* Register the Main Window class */
|
|
wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
|
|
wndclass.lpfnWndProc = MainWND;
|
|
wndclass.cbClsExtra = 0;
|
|
wndclass.cbWndExtra = 0;
|
|
wndclass.hInstance = hInstance;
|
|
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
|
|
wndclass.hCursor = LoadCursor( NULL, IDC_ARROW);
|
|
wndclass.hbrBackground = (HBRUSH) CreateSolidBrush(win32_colors[cindex]);
|
|
wndclass.lpszMenuName = NULL;
|
|
wndclass.lpszClassName = szAppName;
|
|
|
|
if (!RegisterClass(&wndclass)) {
|
|
printf ("Error code: %d\n", GetLastError());
|
|
MessageBox(NULL, TEXT("Initialization of Windows graphics (init_graphics) failed."),
|
|
szAppName, MB_ICONERROR);
|
|
exit(-1);
|
|
}
|
|
|
|
/* Register the Graphics Window class */
|
|
wndclass.lpfnWndProc = GraphicsWND;
|
|
wndclass.hIcon = NULL;
|
|
wndclass.lpszClassName = szGraphicsName;
|
|
|
|
if(!RegisterClass(&wndclass))
|
|
DRAW_ERROR();
|
|
|
|
/* Register the Status Window class */
|
|
wndclass.lpfnWndProc = StatusWND;
|
|
wndclass.hIcon = NULL;
|
|
wndclass.lpszClassName = szStatusName;
|
|
wndclass.hbrBackground = hGrayBrush;
|
|
|
|
if(!RegisterClass(&wndclass))
|
|
DRAW_ERROR();
|
|
|
|
/* Register the Buttons Window class */
|
|
wndclass.lpfnWndProc = ButtonsWND;
|
|
wndclass.hIcon = NULL;
|
|
wndclass.lpszClassName = szButtonsName;
|
|
wndclass.hbrBackground = hGrayBrush;
|
|
|
|
if (!RegisterClass(&wndclass))
|
|
DRAW_ERROR();
|
|
|
|
hMainWnd = CreateWindow(szAppName, TEXT(window_name),
|
|
WS_OVERLAPPEDWINDOW, x, y, top_width, top_height,
|
|
NULL, NULL, hInstance, NULL);
|
|
|
|
if(!hMainWnd)
|
|
DRAW_ERROR();
|
|
|
|
/* Set drawing defaults for user-drawable area. Use whatever the *
|
|
* initial values of the current stuff was set to. */
|
|
|
|
if (ShowWindow(hMainWnd, SW_SHOWNORMAL))
|
|
DRAW_ERROR();
|
|
build_default_menu();
|
|
if (!UpdateWindow(hMainWnd))
|
|
DRAW_ERROR();
|
|
win32_drain_message_queue ();
|
|
#endif
|
|
gl_state.initialized = true;
|
|
}
|
|
|
|
|
|
static void reset_common_state () {
|
|
currentcolor = BLACK;
|
|
currentlinestyle = SOLID;
|
|
currentlinewidth = 0;
|
|
currentfontsize = 12;
|
|
current_draw_mode = DRAW_NORMAL;
|
|
|
|
for (int i=0;i<=MAX_FONT_SIZE;i++)
|
|
font_is_loaded[i] = false; /* No fonts loaded yet. */
|
|
|
|
ProceedPressed = false;
|
|
get_keypress_input = false;
|
|
get_mouse_move_input = false;
|
|
}
|
|
|
|
|
|
static void
|
|
update_transform (void)
|
|
{
|
|
/* Set up the factors for transforming from the user world to X Windows *
|
|
* coordinates. */
|
|
|
|
float mult, y1, y2, x1, x2;
|
|
|
|
/* X Window coordinates go from (0,0) to (width-1,height-1) */
|
|
xmult = (top_width - 1 - MWIDTH) / (xright - xleft);
|
|
ymult = (top_height - 1 - T_AREA_HEIGHT)/ (ybot - ytop);
|
|
|
|
/* Need to use same scaling factor to preserve aspect ratio */
|
|
if (fabs(xmult) <= fabs(ymult)) {
|
|
mult = (float)(fabs(ymult/xmult));
|
|
y1 = ytop - (ybot-ytop)*(mult-1)/2;
|
|
y2 = ybot + (ybot-ytop)*(mult-1)/2;
|
|
ytop = y1;
|
|
ybot = y2;
|
|
}
|
|
else {
|
|
mult = (float)(fabs(xmult/ymult));
|
|
x1 = xleft - (xright-xleft)*(mult-1)/2;
|
|
x2 = xright + (xright-xleft)*(mult-1)/2;
|
|
xleft = x1;
|
|
xright = x2;
|
|
}
|
|
xmult = (top_width - 1 - MWIDTH) / (xright - xleft);
|
|
ymult = (top_height - 1 - T_AREA_HEIGHT)/ (ybot - ytop);
|
|
|
|
xdiv = 1/xmult;
|
|
ydiv = 1/ymult;
|
|
}
|
|
|
|
|
|
static void
|
|
update_ps_transform (void)
|
|
{
|
|
|
|
/* Postscript coordinates start at (0,0) for the lower left hand corner *
|
|
* of the page and increase upwards and to the right. For 8.5 x 11 *
|
|
* sheet, coordinates go from (0,0) to (612,792). Spacing is 1/72 inch.*
|
|
* I'm leaving a minimum of half an inch (36 units) of border around *
|
|
* each edge. */
|
|
|
|
float ps_width, ps_height;
|
|
|
|
ps_width = 540.; /* 72 * 7.5 */
|
|
ps_height = 720.; /* 72 * 10 */
|
|
|
|
ps_xmult = ps_width / (xright - xleft);
|
|
ps_ymult = ps_height / (ytop - ybot);
|
|
/* Need to use same scaling factor to preserve aspect ratio. *
|
|
* I show exactly as much on paper as the screen window shows, *
|
|
* or the user specifies. */
|
|
if (fabs(ps_xmult) <= fabs(ps_ymult)) {
|
|
ps_left = 36.;
|
|
ps_right = (float)(36. + ps_width);
|
|
ps_bot = (float)(396. - fabs(ps_xmult * (ytop - ybot))/2);
|
|
ps_top = (float)(396. + fabs(ps_xmult * (ytop - ybot))/2);
|
|
/* Maintain aspect ratio but watch signs */
|
|
ps_ymult = (ps_xmult*ps_ymult < 0) ? -ps_xmult : ps_xmult;
|
|
}
|
|
else {
|
|
ps_bot = 36.;
|
|
ps_top = (float)(36. + ps_height);
|
|
ps_left = (float)(306. - fabs(ps_ymult * (xright - xleft))/2);
|
|
ps_right = (float)(306. + fabs(ps_ymult * (xright - xleft))/2);
|
|
/* Maintain aspect ratio but watch signs */
|
|
ps_xmult = (ps_xmult*ps_ymult < 0) ? -ps_ymult : ps_ymult;
|
|
}
|
|
}
|
|
|
|
|
|
/* The program's main event loop. Must be passed a user routine
|
|
* drawscreen which redraws the screen. It handles all window resizing
|
|
* zooming etc. itself. If the user clicks a mousebutton in the graphics
|
|
* (toplevel) area, the act_on_mousebutton routine passed in is called.
|
|
*/
|
|
void
|
|
event_loop (void (*act_on_mousebutton)(float x, float y),
|
|
void (*act_on_mousemove)(float x, float y),
|
|
void (*act_on_keypress)(char key_pressed),
|
|
void (*drawscreen) (void))
|
|
{
|
|
#ifdef X11
|
|
XEvent report;
|
|
int bnum;
|
|
float x, y;
|
|
|
|
#define OFF 1
|
|
#define ON 0
|
|
|
|
turn_on_off (ON);
|
|
while (1) {
|
|
XNextEvent (display, &report);
|
|
switch (report.type) {
|
|
case Expose:
|
|
#ifdef VERBOSE
|
|
printf("Got an expose event.\n");
|
|
printf("Count is: %d.\n",report.xexpose.count);
|
|
printf("Window ID is: %d.\n",report.xexpose.window);
|
|
#endif
|
|
if (report.xexpose.count != 0)
|
|
break;
|
|
if (report.xexpose.window == menu)
|
|
drawmenu();
|
|
else if (report.xexpose.window == toplevel)
|
|
drawscreen();
|
|
else if (report.xexpose.window == textarea)
|
|
draw_message();
|
|
break;
|
|
case ConfigureNotify:
|
|
top_width = report.xconfigure.width;
|
|
top_height = report.xconfigure.height;
|
|
update_transform();
|
|
drawmenu();
|
|
draw_message();
|
|
#ifdef VERBOSE
|
|
printf("Got a ConfigureNotify.\n");
|
|
printf("New width: %d New height: %d.\n",top_width,top_height);
|
|
#endif
|
|
break;
|
|
case ButtonPress:
|
|
#ifdef VERBOSE
|
|
printf("Got a buttonpress.\n");
|
|
printf("Window ID is: %d.\n",report.xbutton.window);
|
|
#endif
|
|
if (report.xbutton.window == toplevel) {
|
|
x = XTOWORLD(report.xbutton.x);
|
|
y = YTOWORLD(report.xbutton.y);
|
|
act_on_mousebutton (x, y);
|
|
}
|
|
else { /* A menu button was pressed. */
|
|
bnum = which_button(report.xbutton.window);
|
|
#ifdef VERBOSE
|
|
printf("Button number is %d\n",bnum);
|
|
#endif
|
|
if (button[bnum].enabled) {
|
|
button[bnum].ispressed = 1;
|
|
drawbut(bnum);
|
|
XFlush(display); /* Flash the button */
|
|
button[bnum].fcn (drawscreen);
|
|
button[bnum].ispressed = 0;
|
|
drawbut(bnum);
|
|
if (button[bnum].fcn == proceed) {
|
|
turn_on_off(OFF);
|
|
flushinput ();
|
|
return; /* Rather clumsy way of returning *
|
|
* control to the simulator */
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
case MotionNotify:
|
|
#ifdef VERBOSE
|
|
printf("Got a MotionNotify Event.\n");
|
|
printf("x: %d y: %d\n",report.xmotion.x,report.xmotion.y);
|
|
#endif
|
|
if (get_mouse_move_input &&
|
|
report.xmotion.x <= top_width-MWIDTH &&
|
|
report.xmotion.y <= top_height-T_AREA_HEIGHT)
|
|
act_on_mousemove(XTOWORLD(report.xmotion.x), YTOWORLD(report.xmotion.y));
|
|
break;
|
|
case KeyPress:
|
|
#ifdef VERBOSE
|
|
printf("Got a KeyPress Event.\n");
|
|
#endif
|
|
if (get_keypress_input)
|
|
{
|
|
char keyb_buffer[20];
|
|
XComposeStatus composestatus;
|
|
KeySym keysym;
|
|
int length, max_bytes;
|
|
|
|
max_bytes = 1;
|
|
|
|
length = XLookupString( &report.xkey, keyb_buffer, max_bytes, &keysym,
|
|
&composestatus );
|
|
|
|
keyb_buffer[length] = '\0'; /* terminating NULL */
|
|
act_on_keypress(keyb_buffer[0]);
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
#else /* Win32 */
|
|
MSG msg;
|
|
|
|
mouseclick_ptr = act_on_mousebutton;
|
|
mousemove_ptr = act_on_mousemove;
|
|
keypress_ptr = act_on_keypress;
|
|
drawscreen_ptr = drawscreen;
|
|
ProceedPressed = FALSE;
|
|
InEventLoop = TRUE;
|
|
|
|
invalidate_screen();
|
|
|
|
while(!ProceedPressed && GetMessage(&msg, NULL, 0, 0)) {
|
|
//TranslateMessage(&msg);
|
|
if (msg.message == WM_CHAR) { // only the top window can get keyboard events
|
|
msg.hwnd = hMainWnd;
|
|
}
|
|
DispatchMessage(&msg);
|
|
}
|
|
InEventLoop = FALSE;
|
|
#endif
|
|
}
|
|
|
|
void
|
|
clearscreen (void)
|
|
{
|
|
int savecolor;
|
|
if (gl_state.disp_type == SCREEN) {
|
|
#ifdef X11
|
|
XClearWindow (display, toplevel);
|
|
#else /* Win32 */
|
|
savecolor = currentcolor;
|
|
setcolor(gl_state.background_cindex);
|
|
fillrect (xleft, ytop, xright, ybot);
|
|
setcolor(savecolor);
|
|
#endif
|
|
}
|
|
else { // Postscript
|
|
/* erases current page. Don't use erasepage, since this will erase *
|
|
* everything, (even stuff outside the clipping path) causing *
|
|
* problems if this picture is incorporated into a larger document. */
|
|
savecolor = currentcolor;
|
|
setcolor (gl_state.background_cindex);
|
|
fprintf(ps,"clippath fill\n\n");
|
|
setcolor (savecolor);
|
|
}
|
|
}
|
|
|
|
/* Return 1 if I can quarantee no part of this rectangle will *
|
|
* lie within the user drawing area. Otherwise return 0. *
|
|
* Note: this routine is only used to help speed (and to shrink ps *
|
|
* files) -- it will be highly effective when the graphics are zoomed *
|
|
* in and lots are off-screen. I don't have to pre-clip for *
|
|
* correctness. */
|
|
static int
|
|
rect_off_screen (float x1, float y1, float x2, float y2)
|
|
{
|
|
|
|
float xmin, xmax, ymin, ymax;
|
|
|
|
xmin = min (xleft, xright);
|
|
if (x1 < xmin && x2 < xmin)
|
|
return (1);
|
|
|
|
xmax = max (xleft, xright);
|
|
if (x1 > xmax && x2 > xmax)
|
|
return (1);
|
|
|
|
ymin = min (ytop, ybot);
|
|
if (y1 < ymin && y2 < ymin)
|
|
return (1);
|
|
|
|
ymax = max (ytop, ybot);
|
|
if (y1 > ymax && y2 > ymax)
|
|
return (1);
|
|
|
|
return (0);
|
|
}
|
|
|
|
void
|
|
drawline (float x1, float y1, float x2, float y2)
|
|
{
|
|
/* Draw a line from (x1,y1) to (x2,y2) in the user-drawable area. *
|
|
* Coordinates are in world (user) space. */
|
|
|
|
#ifdef WIN32
|
|
HPEN hOldPen;
|
|
#endif
|
|
|
|
if (rect_off_screen(x1,y1,x2,y2))
|
|
return;
|
|
|
|
if (gl_state.disp_type == SCREEN) {
|
|
#ifdef X11
|
|
/* Xlib.h prototype has x2 and y1 mixed up. */
|
|
XDrawLine(display, toplevel, current_gc, xcoord(x1), ycoord(y1), xcoord(x2), ycoord(y2));
|
|
#else /* Win32 */
|
|
hOldPen = (HPEN)SelectObject(hGraphicsDC, hGraphicsPen);
|
|
if(!(hOldPen))
|
|
SELECT_ERROR();
|
|
if (!BeginPath(hGraphicsDC))
|
|
DRAW_ERROR();
|
|
if(!MoveToEx (hGraphicsDC, xcoord(x1), ycoord(y1), NULL))
|
|
DRAW_ERROR();
|
|
if(!LineTo (hGraphicsDC, xcoord(x2), ycoord(y2)))
|
|
DRAW_ERROR();
|
|
if (!EndPath(hGraphicsDC))
|
|
DRAW_ERROR();
|
|
if (!StrokePath(hGraphicsDC))
|
|
DRAW_ERROR();
|
|
if(!SelectObject(hGraphicsDC, hOldPen))
|
|
SELECT_ERROR();
|
|
#endif
|
|
}
|
|
else {
|
|
fprintf(ps,"%.2f %.2f %.2f %.2f drawline\n",XPOST(x1),YPOST(y1),
|
|
XPOST(x2),YPOST(y2));
|
|
}
|
|
}
|
|
|
|
/* (x1,y1) and (x2,y2) are diagonally opposed corners, in world coords. */
|
|
void
|
|
drawrect (float x1, float y1, float x2, float y2)
|
|
{
|
|
int xw1, yw1, xw2, yw2;
|
|
#ifdef WIN32
|
|
HPEN hOldPen;
|
|
HBRUSH hOldBrush;
|
|
#else
|
|
unsigned int width, height;
|
|
int xl, yt;
|
|
#endif
|
|
|
|
if (rect_off_screen(x1,y1,x2,y2))
|
|
return;
|
|
|
|
if (gl_state.disp_type == SCREEN) {
|
|
/* translate to X Windows calling convention. */
|
|
xw1 = xcoord(x1);
|
|
xw2 = xcoord(x2);
|
|
yw1 = ycoord(y1);
|
|
yw2 = ycoord(y2);
|
|
#ifdef X11
|
|
xl = min(xw1,xw2);
|
|
yt = min(yw1,yw2);
|
|
width = abs (xw1-xw2);
|
|
height = abs (yw1-yw2);
|
|
XDrawRectangle(display, toplevel, current_gc, xl, yt, width, height);
|
|
#else /* Win32 */
|
|
if(xw1 > xw2) {
|
|
int temp = xw1;
|
|
xw1 = xw2;
|
|
xw2 = temp;
|
|
}
|
|
if(yw1 > yw2) {
|
|
int temp = yw1;
|
|
yw1 = yw2;
|
|
yw2 = temp;
|
|
}
|
|
|
|
hOldPen = (HPEN)SelectObject(hGraphicsDC, hGraphicsPen);
|
|
if(!(hOldPen))
|
|
SELECT_ERROR();
|
|
hOldBrush = (HBRUSH)SelectObject(hGraphicsDC, GetStockObject(NULL_BRUSH));
|
|
if(!(hOldBrush))
|
|
SELECT_ERROR();
|
|
if(!Rectangle(hGraphicsDC, xw1, yw1, xw2, yw2))
|
|
DRAW_ERROR();
|
|
if(!SelectObject(hGraphicsDC, hOldPen))
|
|
SELECT_ERROR();
|
|
if(!SelectObject(hGraphicsDC, hOldBrush))
|
|
SELECT_ERROR();
|
|
#endif
|
|
|
|
}
|
|
else {
|
|
fprintf(ps,"%.2f %.2f %.2f %.2f drawrect\n",XPOST(x1),YPOST(y1),
|
|
XPOST(x2),YPOST(y2));
|
|
}
|
|
}
|
|
|
|
|
|
/* (x1,y1) and (x2,y2) are diagonally opposed corners in world coords. */
|
|
void
|
|
fillrect (float x1, float y1, float x2, float y2)
|
|
{
|
|
int xw1, yw1, xw2, yw2;
|
|
#ifdef WIN32
|
|
HPEN hOldPen;
|
|
HBRUSH hOldBrush;
|
|
#else
|
|
unsigned int width, height;
|
|
int xl, yt;
|
|
#endif
|
|
|
|
if (rect_off_screen(x1,y1,x2,y2))
|
|
return;
|
|
|
|
if (gl_state.disp_type == SCREEN) {
|
|
/* translate to X Windows calling convention. */
|
|
xw1 = xcoord(x1);
|
|
xw2 = xcoord(x2);
|
|
yw1 = ycoord(y1);
|
|
yw2 = ycoord(y2);
|
|
#ifdef X11
|
|
xl = min(xw1,xw2);
|
|
yt = min(yw1,yw2);
|
|
width = abs (xw1-xw2);
|
|
height = abs (yw1-yw2);
|
|
XFillRectangle(display, toplevel, current_gc, xl, yt, width, height);
|
|
#else /* Win32 */
|
|
if(xw1 > xw2) {
|
|
int temp = xw1;
|
|
xw1 = xw2;
|
|
xw2 = temp;
|
|
}
|
|
if(yw1 > yw2) {
|
|
int temp = yw1;
|
|
yw1 = yw2;
|
|
yw2 = temp;
|
|
}
|
|
|
|
hOldPen = (HPEN)SelectObject(hGraphicsDC, hGraphicsPen);
|
|
if(!(hOldPen))
|
|
SELECT_ERROR();
|
|
hOldBrush = (HBRUSH)SelectObject(hGraphicsDC, hGraphicsBrush);
|
|
if(!(hOldBrush))
|
|
SELECT_ERROR();
|
|
if(!Rectangle(hGraphicsDC, xw1, yw1, xw2, yw2))
|
|
DRAW_ERROR();
|
|
if(!SelectObject(hGraphicsDC, hOldPen))
|
|
SELECT_ERROR();
|
|
if(!SelectObject(hGraphicsDC, hOldBrush))
|
|
SELECT_ERROR();
|
|
#endif
|
|
}
|
|
else {
|
|
fprintf(ps,"%.2f %.2f %.2f %.2f fillrect\n",XPOST(x1),YPOST(y1),
|
|
XPOST(x2),YPOST(y2));
|
|
}
|
|
}
|
|
|
|
|
|
/* Normalizes an angle to be between 0 and 360 degrees. */
|
|
static float
|
|
angnorm (float ang)
|
|
{
|
|
int scale;
|
|
|
|
if (ang < 0) {
|
|
scale = (int) (ang / 360. - 1);
|
|
}
|
|
else {
|
|
scale = (int) (ang / 360.);
|
|
}
|
|
ang = ang - scale * 360;
|
|
return (ang);
|
|
}
|
|
|
|
void
|
|
drawellipticarc (float xc, float yc, float radx, float rady, float startang, float angextent)
|
|
{
|
|
int xl, yt;
|
|
unsigned int width, height;
|
|
#ifdef WIN32
|
|
HPEN hOldPen;
|
|
int p1, p2, p3, p4;
|
|
#endif
|
|
|
|
/* Conservative (but fast) clip test -- check containing rectangle of *
|
|
* an ellipse. */
|
|
|
|
if (rect_off_screen (xc-radx,yc-rady,xc+radx,yc+rady))
|
|
return;
|
|
|
|
/* X Windows has trouble with very large angles. (Over 360). *
|
|
* Do following to prevent its inaccurate (overflow?) problems. */
|
|
if (fabs(angextent) > 360.)
|
|
angextent = 360.;
|
|
|
|
startang = angnorm (startang);
|
|
|
|
if (gl_state.disp_type == SCREEN) {
|
|
xl = (int) (xcoord(xc) - fabs(xmult*radx));
|
|
yt = (int) (ycoord(yc) - fabs(ymult*rady));
|
|
width = (unsigned int) (2*fabs(xmult*radx));
|
|
height = (unsigned int) (2*fabs(ymult*rady));
|
|
#ifdef X11
|
|
XDrawArc (display, toplevel, current_gc, xl, yt, width, height,
|
|
(int) (startang*64), (int) (angextent*64));
|
|
#else // Win32
|
|
/* set arc direction */
|
|
if (angextent > 0) {
|
|
p1 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang)));
|
|
p2 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang)));
|
|
p3 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang+angextent-.001)));
|
|
p4 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang+angextent-.001)));
|
|
}
|
|
else {
|
|
p1 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang+angextent+.001)));
|
|
p2 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang+angextent+.001)));
|
|
p3 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang)));
|
|
p4 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang)));
|
|
}
|
|
|
|
hOldPen = (HPEN)SelectObject(hGraphicsDC, hGraphicsPen);
|
|
if(!(hOldPen))
|
|
SELECT_ERROR();
|
|
if(!Arc(hGraphicsDC, xl, yt, xl+width, yt+height, p1, p2, p3, p4))
|
|
DRAW_ERROR();
|
|
if(!SelectObject(hGraphicsDC, hOldPen))
|
|
SELECT_ERROR();
|
|
#endif
|
|
}
|
|
else {
|
|
fprintf(ps, "gsave\n");
|
|
fprintf(ps, "%.2f %.2f translate\n", XPOST(xc), YPOST(yc));
|
|
fprintf(ps, "%.2f 1 scale\n", fabs(radx*ps_xmult)/fabs(rady*ps_ymult));
|
|
fprintf(ps, "0 0 %.2f %.2f %.2f %s\n", /*XPOST(xc)*/
|
|
/*YPOST(yc)*/ fabs(rady*ps_xmult), startang, startang+angextent,
|
|
(angextent < 0) ? "drawarcn" : "drawarc") ;
|
|
fprintf(ps, "grestore\n");
|
|
}
|
|
}
|
|
|
|
/* Startang is relative to the Window's positive x direction. Angles in degrees.
|
|
*/
|
|
void
|
|
drawarc (float xc, float yc, float rad, float startang,
|
|
float angextent)
|
|
{
|
|
drawellipticarc(xc, yc, rad, rad, startang, angextent);
|
|
}
|
|
|
|
|
|
/* Fills a elliptic arc. Startang is relative to the Window's positive x
|
|
* direction. Angles in degrees.
|
|
*/
|
|
void
|
|
fillellipticarc (float xc, float yc, float radx, float rady, float startang,
|
|
float angextent)
|
|
{
|
|
int xl, yt;
|
|
unsigned int width, height;
|
|
#ifdef WIN32
|
|
HPEN hOldPen;
|
|
HBRUSH hOldBrush;
|
|
int p1, p2, p3, p4;
|
|
#endif
|
|
|
|
/* Conservative (but fast) clip test -- check containing rectangle of *
|
|
* a circle. */
|
|
|
|
if (rect_off_screen (xc-radx,yc-rady,xc+radx,yc+rady))
|
|
return;
|
|
|
|
/* X Windows has trouble with very large angles. (Over 360). *
|
|
* Do following to prevent its inaccurate (overflow?) problems. */
|
|
|
|
if (fabs(angextent) > 360.)
|
|
angextent = 360.;
|
|
|
|
startang = angnorm (startang);
|
|
|
|
if (gl_state.disp_type == SCREEN) {
|
|
xl = (int) (xcoord(xc) - fabs(xmult*radx));
|
|
yt = (int) (ycoord(yc) - fabs(ymult*rady));
|
|
width = (unsigned int) (2*fabs(xmult*radx));
|
|
height = (unsigned int) (2*fabs(ymult*rady));
|
|
#ifdef X11
|
|
XFillArc (display, toplevel, current_gc, xl, yt, width, height,
|
|
(int) (startang*64), (int) (angextent*64));
|
|
#else // Win32
|
|
/* set pie direction */
|
|
if (angextent > 0) {
|
|
p1 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang)));
|
|
p2 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang)));
|
|
p3 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang+angextent-.001)));
|
|
p4 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang+angextent-.001)));
|
|
}
|
|
else {
|
|
p1 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang+angextent+.001)));
|
|
p2 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang+angextent+.001)));
|
|
p3 = (int)(xcoord(xc) + fabs(xmult*radx)*cos(DEGTORAD(startang)));
|
|
p4 = (int)(ycoord(yc) - fabs(ymult*rady)*sin(DEGTORAD(startang)));
|
|
}
|
|
|
|
hOldPen = (HPEN)SelectObject(hGraphicsDC, GetStockObject(NULL_PEN));
|
|
if(!(hOldPen))
|
|
SELECT_ERROR();
|
|
hOldBrush = (HBRUSH)SelectObject(hGraphicsDC, hGraphicsBrush);
|
|
if(!(hOldBrush))
|
|
SELECT_ERROR();
|
|
// Win32 API says a zero return value indicates an error, but it seems to always
|
|
// return zero. Don't check for an error on Pie.
|
|
Pie(hGraphicsDC, xl, yt, xl+width, yt+height, p1, p2, p3, p4);
|
|
|
|
// if(!Pie(hGraphicsDC, xl, yt, xl+width, yt+height, p1, p2, p3, p4));
|
|
// DRAW_ERROR();
|
|
if(!SelectObject(hGraphicsDC, hOldPen))
|
|
SELECT_ERROR();
|
|
if(!SelectObject(hGraphicsDC, hOldBrush))
|
|
SELECT_ERROR();
|
|
#endif
|
|
}
|
|
else {
|
|
fprintf(ps, "gsave\n");
|
|
fprintf(ps, "%.2f %.2f translate\n", XPOST(xc), YPOST(yc));
|
|
fprintf(ps, "%.2f 1 scale\n", fabs(radx*ps_xmult)/fabs(rady*ps_ymult));
|
|
fprintf(ps, "%.2f %.2f %.2f 0 0 %s\n", /*XPOST(xc)*/
|
|
/*YPOST(yc)*/ fabs(rady*ps_xmult), startang, startang+angextent,
|
|
(angextent < 0) ? "fillarcn" : "fillarc") ;
|
|
fprintf(ps, "grestore\n");
|
|
}
|
|
}
|
|
|
|
void
|
|
fillarc (float xc, float yc, float rad, float startang, float angextent) {
|
|
fillellipticarc(xc, yc, rad, rad, startang, angextent);
|
|
}
|
|
|
|
void
|
|
fillpoly (t_point *points, int npoints)
|
|
{
|
|
#ifdef X11
|
|
XPoint transpoints[MAXPTS];
|
|
#else
|
|
POINT transpoints[MAXPTS];
|
|
HPEN hOldPen;
|
|
HBRUSH hOldBrush;
|
|
#endif
|
|
int i;
|
|
float xmin, ymin, xmax, ymax;
|
|
|
|
if (npoints > MAXPTS) {
|
|
printf("Error in fillpoly: Only %d points allowed per polygon.\n",
|
|
MAXPTS);
|
|
printf("%d points were requested. Polygon is not drawn.\n",npoints);
|
|
return;
|
|
}
|
|
|
|
/* Conservative (but fast) clip test -- check containing rectangle of *
|
|
* polygon. */
|
|
|
|
xmin = xmax = points[0].x;
|
|
ymin = ymax = points[0].y;
|
|
|
|
for (i=1;i<npoints;i++) {
|
|
xmin = min (xmin,points[i].x);
|
|
xmax = max (xmax,points[i].x);
|
|
ymin = min (ymin,points[i].y);
|
|
ymax = max (ymax,points[i].y);
|
|
}
|
|
|
|
if (rect_off_screen(xmin,ymin,xmax,ymax))
|
|
return;
|
|
|
|
if (gl_state.disp_type == SCREEN) {
|
|
for (i=0;i<npoints;i++) {
|
|
transpoints[i].x = (short) xcoord (points[i].x);
|
|
transpoints[i].y = (short) ycoord (points[i].y);
|
|
}
|
|
#ifdef X11
|
|
XFillPolygon(display, toplevel, current_gc, transpoints, npoints, Complex,
|
|
CoordModeOrigin);
|
|
#else
|
|
hOldPen = (HPEN)SelectObject(hGraphicsDC, GetStockObject(NULL_PEN));
|
|
if(!(hOldPen))
|
|
SELECT_ERROR();
|
|
hOldBrush = (HBRUSH)SelectObject(hGraphicsDC, hGraphicsBrush);
|
|
if(!(hOldBrush))
|
|
SELECT_ERROR();
|
|
if(!Polygon (hGraphicsDC, transpoints, npoints))
|
|
DRAW_ERROR();
|
|
if(!SelectObject(hGraphicsDC, hOldPen))
|
|
SELECT_ERROR();
|
|
if(!SelectObject(hGraphicsDC, hOldBrush))
|
|
SELECT_ERROR();
|
|
#endif
|
|
}
|
|
else {
|
|
fprintf(ps,"\n");
|
|
|
|
for (i=npoints-1;i>=0;i--)
|
|
fprintf (ps, "%.2f %.2f\n", XPOST(points[i].x), YPOST(points[i].y));
|
|
|
|
fprintf (ps, "%d fillpoly\n", npoints);
|
|
}
|
|
}
|
|
|
|
|
|
/* Draws text centered on xc,yc if it fits in boundx */
|
|
void
|
|
drawtext (float xc, float yc, const char *text, float boundx)
|
|
{
|
|
int len, width, xw_off, yw_off, font_ascent, font_descent;
|
|
|
|
#ifdef X11
|
|
len = strlen(text);
|
|
width = XTextWidth(font_info[currentfontsize], text, len);
|
|
font_ascent = font_info[currentfontsize]->ascent;
|
|
font_descent = font_info[currentfontsize]->descent;
|
|
#else /* WC : WIN32 */
|
|
HFONT hOldFont;
|
|
SIZE textsize;
|
|
TEXTMETRIC textmetric;
|
|
|
|
hOldFont = (HFONT)SelectObject(hGraphicsDC, hGraphicsFont);
|
|
if(!(hOldFont))
|
|
SELECT_ERROR();
|
|
if(SetTextColor(hGraphicsDC, win32_colors[currentcolor]) == CLR_INVALID)
|
|
DRAW_ERROR();
|
|
|
|
len = strlen(text);
|
|
if (!GetTextExtentPoint32(hGraphicsDC, text, len, &textsize))
|
|
DRAW_ERROR();
|
|
width = textsize.cx;
|
|
if (!GetTextMetrics(hGraphicsDC, &textmetric))
|
|
DRAW_ERROR();
|
|
font_ascent = textmetric.tmAscent;
|
|
font_descent = textmetric.tmDescent;
|
|
#endif
|
|
if (width > fabs(boundx*xmult)) {
|
|
#ifdef WIN32
|
|
if(!SelectObject(hGraphicsDC, hOldFont))
|
|
SELECT_ERROR();
|
|
#endif
|
|
return; /* Don't draw if it won't fit */
|
|
}
|
|
|
|
xw_off = (int)(width/(2.*xmult)); /* NB: sign doesn't matter. */
|
|
|
|
/* NB: 2 * descent makes this slightly conservative but simplifies code. */
|
|
yw_off = (int)((font_ascent + 2 * font_descent)/(2.*ymult));
|
|
|
|
/* Note: text can be clipped when a little bit of it would be visible *
|
|
* right now. Perhaps X doesn't return extremely accurate width and *
|
|
* ascent values, etc? Could remove this completely by multiplying *
|
|
* xw_off and yw_off by, 1.2 or 1.5. */
|
|
if (rect_off_screen(xc-xw_off, yc-yw_off, xc+xw_off, yc+yw_off)) {
|
|
#ifdef WIN32
|
|
if(!SelectObject(hGraphicsDC, hOldFont))
|
|
SELECT_ERROR();
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
if (gl_state.disp_type == SCREEN) {
|
|
#ifdef X11
|
|
XDrawString(display, toplevel, current_gc, xcoord(xc)-width/2, ycoord(yc) +
|
|
(font_info[currentfontsize]->ascent - font_info[currentfontsize]->descent)/2,
|
|
text, len);
|
|
#else /* Win32 */
|
|
SetBkMode(hGraphicsDC, TRANSPARENT);
|
|
if(!TextOut (hGraphicsDC, xcoord(xc)-width/2, ycoord(yc) - (font_ascent + font_descent)/2,
|
|
text, len))
|
|
DRAW_ERROR();
|
|
if(!SelectObject(hGraphicsDC, hOldFont))
|
|
SELECT_ERROR();
|
|
#endif
|
|
}
|
|
else {
|
|
fprintf(ps,"(%s) %.2f %.2f censhow\n",text,XPOST(xc),YPOST(yc));
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
flushinput (void)
|
|
{
|
|
if (gl_state.disp_type != SCREEN)
|
|
return;
|
|
#ifdef X11
|
|
XFlush(display);
|
|
#endif
|
|
}
|
|
|
|
|
|
void
|
|
init_world (float x1, float y1, float x2, float y2)
|
|
{
|
|
/* Sets the coordinate system the user wants to draw into. */
|
|
|
|
xleft = x1;
|
|
xright = x2;
|
|
ytop = y1;
|
|
ybot = y2;
|
|
|
|
saved_xleft = xleft; /* Save initial world coordinates to allow full */
|
|
saved_xright = xright; /* view button to zoom all the way out. */
|
|
saved_ytop = ytop;
|
|
saved_ybot = ybot;
|
|
|
|
if (gl_state.disp_type == SCREEN) {
|
|
update_transform();
|
|
}
|
|
else {
|
|
update_ps_transform();
|
|
}
|
|
}
|
|
|
|
|
|
/* Draw the current message in the text area at the screen bottom. */
|
|
void
|
|
draw_message (void)
|
|
{
|
|
int savefontsize, savecolor;
|
|
float ylow;
|
|
#ifdef X11
|
|
int len, width;
|
|
#endif
|
|
|
|
if (gl_state.disp_type == SCREEN) {
|
|
#ifdef X11
|
|
XClearWindow (display, textarea);
|
|
len = strlen (statusMessage);
|
|
width = XTextWidth(font_info[menu_font_size], statusMessage, len);
|
|
XSetForeground(display, gc_menus,colors[WHITE]);
|
|
XDrawRectangle(display, textarea, gc_menus, 0, 0, top_width - MWIDTH, T_AREA_HEIGHT);
|
|
XSetForeground(display, gc_menus,colors[BLACK]);
|
|
XDrawLine(display, textarea, gc_menus, 0, T_AREA_HEIGHT-1, top_width-MWIDTH, T_AREA_HEIGHT-1);
|
|
XDrawLine(display, textarea, gc_menus, top_width-MWIDTH-1, 0, top_width-MWIDTH-1, T_AREA_HEIGHT-1);
|
|
|
|
XDrawString(display, textarea, gc_menus,
|
|
(top_width - MWIDTH - width)/2,
|
|
T_AREA_HEIGHT/2 + (font_info[menu_font_size]->ascent -
|
|
font_info[menu_font_size]->descent)/2, statusMessage, len);
|
|
#else
|
|
if(!InvalidateRect(hStatusWnd, NULL, TRUE))
|
|
DRAW_ERROR();
|
|
if(!UpdateWindow(hStatusWnd))
|
|
DRAW_ERROR();
|
|
#endif
|
|
}
|
|
|
|
else {
|
|
/* Draw the message in the bottom margin. Printer's generally can't *
|
|
* print on the bottom 1/4" (area with y < 18 in PostScript coords.) */
|
|
|
|
savecolor = currentcolor;
|
|
setcolor (BLACK);
|
|
savefontsize = currentfontsize;
|
|
setfontsize (menu_font_size - 2); /* Smaller OK on paper */
|
|
ylow = ps_bot - 8;
|
|
fprintf(ps,"(%s) %.2f %.2f censhow\n",statusMessage,(ps_left+ps_right)/2.,
|
|
ylow);
|
|
setcolor (savecolor);
|
|
setfontsize (savefontsize);
|
|
}
|
|
}
|
|
|
|
|
|
/* Changes the message to be displayed on screen. */
|
|
void
|
|
update_message (const char *msg)
|
|
{
|
|
strncpy (statusMessage, msg, BUFSIZE);
|
|
draw_message ();
|
|
#ifdef X11
|
|
// Make this appear immediately. Win32 does that automaticaly.
|
|
XFlush (display);
|
|
#endif // X11
|
|
}
|
|
|
|
|
|
/* Zooms in by a factor of 1.666. */
|
|
static void
|
|
zoom_in (void (*drawscreen) (void))
|
|
{
|
|
float xdiff, ydiff;
|
|
|
|
xdiff = xright - xleft;
|
|
ydiff = ybot - ytop;
|
|
xleft += xdiff/5;
|
|
xright -= xdiff/5;
|
|
ytop += ydiff/5;
|
|
ybot -= ydiff/5;
|
|
|
|
update_transform ();
|
|
drawscreen();
|
|
}
|
|
|
|
|
|
/* Zooms out by a factor of 1.666. */
|
|
static void
|
|
zoom_out (void (*drawscreen) (void))
|
|
{
|
|
float xdiff, ydiff;
|
|
|
|
xdiff = xright - xleft;
|
|
ydiff = ybot - ytop;
|
|
xleft -= xdiff/3;
|
|
xright += xdiff/3;
|
|
ytop -= ydiff/3;
|
|
ybot += ydiff/3;
|
|
|
|
update_transform ();
|
|
drawscreen();
|
|
}
|
|
|
|
|
|
/* Sets the view back to the initial view set by init_world (i.e. a full *
|
|
* view) of all the graphics. */
|
|
static void
|
|
zoom_fit (void (*drawscreen) (void))
|
|
{
|
|
xleft = saved_xleft;
|
|
xright = saved_xright;
|
|
ytop = saved_ytop;
|
|
ybot = saved_ybot;
|
|
|
|
update_transform ();
|
|
drawscreen();
|
|
}
|
|
|
|
|
|
/* Moves view 1/2 screen up. */
|
|
static void
|
|
translate_up (void (*drawscreen) (void))
|
|
{
|
|
float ystep;
|
|
|
|
ystep = (ybot - ytop)/2;
|
|
ytop -= ystep;
|
|
ybot -= ystep;
|
|
update_transform();
|
|
drawscreen();
|
|
}
|
|
|
|
|
|
/* Moves view 1/2 screen down. */
|
|
static void
|
|
translate_down (void (*drawscreen) (void))
|
|
{
|
|
float ystep;
|
|
|
|
ystep = (ybot - ytop)/2;
|
|
ytop += ystep;
|
|
ybot += ystep;
|
|
update_transform();
|
|
drawscreen();
|
|
}
|
|
|
|
|
|
/* Moves view 1/2 screen left. */
|
|
static void
|
|
translate_left (void (*drawscreen) (void))
|
|
{
|
|
|
|
float xstep;
|
|
|
|
xstep = (xright - xleft)/2;
|
|
xleft -= xstep;
|
|
xright -= xstep;
|
|
update_transform();
|
|
drawscreen();
|
|
}
|
|
|
|
|
|
/* Moves view 1/2 screen right. */
|
|
static void
|
|
translate_right (void (*drawscreen) (void))
|
|
{
|
|
float xstep;
|
|
|
|
xstep = (xright - xleft)/2;
|
|
xleft += xstep;
|
|
xright += xstep;
|
|
update_transform();
|
|
drawscreen();
|
|
}
|
|
|
|
|
|
static void
|
|
update_win (int x[2], int y[2], void (*drawscreen)(void))
|
|
{
|
|
float x1, x2, y1, y2;
|
|
|
|
x[0] = min(x[0],top_width-MWIDTH); /* Can't go under menu */
|
|
x[1] = min(x[1],top_width-MWIDTH);
|
|
y[0] = min(y[0],top_height-T_AREA_HEIGHT); /* Can't go under text area */
|
|
y[1] = min(y[1],top_height-T_AREA_HEIGHT);
|
|
|
|
if ((x[0] == x[1]) || (y[0] == y[1])) {
|
|
printf("Illegal (zero area) window. Window unchanged.\n");
|
|
return;
|
|
}
|
|
x1 = XTOWORLD(min(x[0],x[1]));
|
|
x2 = XTOWORLD(max(x[0],x[1]));
|
|
y1 = YTOWORLD(min(y[0],y[1]));
|
|
y2 = YTOWORLD(max(y[0],y[1]));
|
|
xleft = x1;
|
|
xright = x2;
|
|
ytop = y1;
|
|
ybot = y2;
|
|
update_transform();
|
|
drawscreen();
|
|
}
|
|
|
|
|
|
/* The window button was pressed. Let the user click on the two *
|
|
* diagonally opposed corners, and zoom in on this area. */
|
|
static void
|
|
adjustwin (void (*drawscreen) (void))
|
|
{
|
|
#ifdef X11
|
|
|
|
XEvent report;
|
|
int corner, xold, yold, x[2], y[2];
|
|
|
|
corner = 0;
|
|
xold = -1;
|
|
yold = -1; /* Don't need to init yold, but stops compiler warning. */
|
|
|
|
while (corner<2) {
|
|
XNextEvent (display, &report);
|
|
switch (report.type) {
|
|
case Expose:
|
|
#ifdef VERBOSE
|
|
printf("Got an expose event.\n");
|
|
printf("Count is: %d.\n",report.xexpose.count);
|
|
printf("Window ID is: %d.\n",report.xexpose.window);
|
|
#endif
|
|
if (report.xexpose.count != 0)
|
|
break;
|
|
if (report.xexpose.window == menu)
|
|
drawmenu();
|
|
else if (report.xexpose.window == toplevel) {
|
|
drawscreen();
|
|
xold = -1; /* No rubber band on screen */
|
|
}
|
|
else if (report.xexpose.window == textarea)
|
|
draw_message();
|
|
break;
|
|
case ConfigureNotify:
|
|
top_width = report.xconfigure.width;
|
|
top_height = report.xconfigure.height;
|
|
update_transform();
|
|
drawmenu();
|
|
draw_message();
|
|
#ifdef VERBOSE
|
|
printf("Got a ConfigureNotify.\n");
|
|
printf("New width: %d New height: %d.\n",top_width,top_height);
|
|
#endif
|
|
break;
|
|
case ButtonPress:
|
|
#ifdef VERBOSE
|
|
printf("Got a buttonpress.\n");
|
|
printf("Window ID is: %d.\n",report.xbutton.window);
|
|
printf("Location (%d, %d).\n", report.xbutton.x,
|
|
report.xbutton.y);
|
|
#endif
|
|
if (report.xbutton.window != toplevel) break;
|
|
x[corner] = report.xbutton.x;
|
|
y[corner] = report.xbutton.y;
|
|
if (corner == 0) {
|
|
/* XSelectInput (display, toplevel, ExposureMask |
|
|
StructureNotifyMask | ButtonPressMask | PointerMotionMask); */
|
|
}
|
|
else {
|
|
update_win(x,y,drawscreen);
|
|
}
|
|
corner++;
|
|
break;
|
|
case MotionNotify:
|
|
if (corner) {
|
|
#ifdef VERBOSE
|
|
printf("Got a MotionNotify Event.\n");
|
|
printf("x: %d y: %d\n",report.xmotion.x,report.xmotion.y);
|
|
#endif
|
|
if (xold >= 0) { /* xold set -ve before we draw first box */
|
|
// Erase prior box.
|
|
set_draw_mode(DRAW_XOR);
|
|
XDrawRectangle(display,toplevel,gcxor,min(x[0],xold),
|
|
min(y[0],yold),abs(x[0]-xold),abs(y[0]-yold));
|
|
set_draw_mode(DRAW_NORMAL);
|
|
}
|
|
/* Don't allow user to window under menu region */
|
|
xold = min(report.xmotion.x,top_width-1-MWIDTH);
|
|
yold = report.xmotion.y;
|
|
set_draw_mode(DRAW_XOR);
|
|
|
|
// Use forcing versions, as we want these modes to apply
|
|
// to the xor drawing context, and the regular versions
|
|
// won't update the drawing context if there is no change in line
|
|
// width etc. (but they might only be on the normal context)
|
|
force_setlinewidth(1);
|
|
force_setlinestyle(DASHED);
|
|
force_setcolor(gl_state.background_cindex);
|
|
|
|
// Draw rubber band box.
|
|
XDrawRectangle(display,toplevel,gcxor,min(x[0],xold),
|
|
min(y[0],yold),abs(x[0]-xold),abs(y[0]-yold));
|
|
set_draw_mode(DRAW_NORMAL);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
/* XSelectInput (display, toplevel, ExposureMask | StructureNotifyMask
|
|
| ButtonPressMask); */
|
|
#else /* Win32 */
|
|
/* Implemented as WM_LB... events */
|
|
|
|
/* Begin window adjust */
|
|
if (!windowAdjustFlag) {
|
|
windowAdjustFlag = 1;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
static void
|
|
postscript (void (*drawscreen) (void))
|
|
{
|
|
/* Takes a snapshot of the screen and stores it in pic?.ps. The *
|
|
* first picture goes in pic1.ps, the second in pic2.ps, etc. */
|
|
|
|
static int piccount = 1;
|
|
int success;
|
|
char fname[BUFSIZE];
|
|
|
|
sprintf(fname,"pic%d.ps",piccount);
|
|
printf("Writing postscript output to file %s\n", fname);
|
|
success = init_postscript (fname);
|
|
|
|
if (success) {
|
|
|
|
drawscreen();
|
|
close_postscript ();
|
|
piccount++;
|
|
}
|
|
else {
|
|
printf ("Error initializing for postscript output.\n");
|
|
#ifdef WIN32
|
|
MessageBox(hMainWnd, "Error initializing postscript output.", NULL, MB_OK);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
proceed (void (*drawscreen) (void))
|
|
{
|
|
ProceedPressed = TRUE;
|
|
}
|
|
|
|
|
|
static void
|
|
quit (void (*drawscreen) (void))
|
|
{
|
|
close_graphics();
|
|
exit(0);
|
|
}
|
|
|
|
|
|
/* Release all my drawing structures (through the X server) and *
|
|
* close down the connection. */
|
|
void
|
|
close_graphics (void)
|
|
{
|
|
if (!gl_state.initialized)
|
|
return;
|
|
#ifdef X11
|
|
int i;
|
|
for (i=0;i<=MAX_FONT_SIZE;i++) {
|
|
if (font_is_loaded[i])
|
|
XFreeFont(display,font_info[i]);
|
|
}
|
|
|
|
XFreeGC(display,gc);
|
|
XFreeGC(display,gcxor);
|
|
XFreeGC(display,gc_menus);
|
|
|
|
if (private_cmap != None)
|
|
XFreeColormap (display, private_cmap);
|
|
|
|
XCloseDisplay(display);
|
|
#else /* Win32 */
|
|
int i;
|
|
// Free the font data structure for each loaded font.
|
|
for (i = 0; i <= MAX_FONT_SIZE; i++) {
|
|
if (font_is_loaded[i]) {
|
|
free (font_info[i]);
|
|
}
|
|
}
|
|
|
|
// Destroy the window
|
|
if(!DestroyWindow(hMainWnd))
|
|
DRAW_ERROR();
|
|
|
|
// free the window class (type information held by MS Windows)
|
|
// for each of the four window types we created. Otherwise a
|
|
// later call to init_graphics to open the graphics window up again
|
|
// will fail.
|
|
if (!UnregisterClass (szAppName, GetModuleHandle(NULL)) )
|
|
DRAW_ERROR();
|
|
if (!UnregisterClass (szGraphicsName, GetModuleHandle(NULL)) )
|
|
DRAW_ERROR();
|
|
if (!UnregisterClass (szStatusName, GetModuleHandle(NULL)) )
|
|
DRAW_ERROR();
|
|
if (!UnregisterClass (szButtonsName, GetModuleHandle(NULL)) )
|
|
DRAW_ERROR();
|
|
#endif
|
|
|
|
free(button);
|
|
button = NULL;
|
|
|
|
for (i = 0; i <= MAX_FONT_SIZE; i++) {
|
|
font_is_loaded[i] = false;
|
|
font_info[i] = NULL;
|
|
}
|
|
gl_state.initialized = false;
|
|
}
|
|
|
|
|
|
/* Opens a file for PostScript output. The header information, *
|
|
* clipping path, etc. are all dumped out. If the file could *
|
|
* not be opened, the routine returns 0; otherwise it returns 1. */
|
|
int init_postscript (const char *fname)
|
|
{
|
|
|
|
ps = fopen (fname,"w");
|
|
if (ps == NULL) {
|
|
printf("Error: could not open %s for PostScript output.\n",fname);
|
|
printf("Drawing to screen instead.\n");
|
|
return (0);
|
|
}
|
|
gl_state.disp_type = POSTSCRIPT; /* Graphics go to postscript file now. */
|
|
|
|
/* Header for minimal conformance with the Adobe structuring convention */
|
|
fprintf(ps,"%%!PS-Adobe-1.0\n");
|
|
fprintf(ps,"%%%%DocumentFonts: Helvetica\n");
|
|
fprintf(ps,"%%%%Pages: 1\n");
|
|
/* Set up postscript transformation macros and page boundaries */
|
|
update_ps_transform();
|
|
/* Bottom margin is at ps_bot - 15. to leave room for the on-screen message. */
|
|
fprintf(ps,"%%%%HiResBoundingBox: %.2f %.2f %.2f %.2f\n",
|
|
ps_left, ps_bot - 15., ps_right, ps_top);
|
|
fprintf(ps,"%%%%EndComments\n");
|
|
|
|
fprintf(ps,"/censhow %%draw a centered string\n");
|
|
fprintf(ps," { moveto %% move to proper spot\n");
|
|
fprintf(ps," dup stringwidth pop %% get x length of string\n");
|
|
fprintf(ps," -2 div %% Proper left start\n");
|
|
fprintf(ps," yoff rmoveto %% Move left that much and down half font height\n");
|
|
fprintf(ps," show newpath } def %% show the string\n\n");
|
|
|
|
fprintf(ps,"/setfontsize %% set font to desired size and compute "
|
|
"centering yoff\n");
|
|
fprintf(ps," { /Helvetica findfont\n");
|
|
fprintf(ps," 8 scalefont\n");
|
|
fprintf(ps," setfont %% Font size set ...\n\n");
|
|
fprintf(ps," 0 0 moveto %% Get vertical centering offset\n");
|
|
fprintf(ps," (Xg) true charpath\n");
|
|
fprintf(ps," flattenpath pathbbox\n");
|
|
fprintf(ps," /ascent exch def pop -1 mul /descent exch def pop\n");
|
|
fprintf(ps," newpath\n");
|
|
fprintf(ps," descent ascent sub 2 div /yoff exch def } def\n\n");
|
|
|
|
fprintf(ps,"%% Next two lines for debugging only.\n");
|
|
fprintf(ps,"/str 20 string def\n");
|
|
fprintf(ps,"/pnum {str cvs print ( ) print} def\n");
|
|
|
|
fprintf(ps,"/drawline %% draw a line from (x2,y2) to (x1,y1)\n");
|
|
fprintf(ps," { moveto lineto stroke } def\n\n");
|
|
|
|
fprintf(ps,"/rect %% outline a rectangle \n");
|
|
fprintf(ps," { /y2 exch def /x2 exch def /y1 exch def /x1 exch def\n");
|
|
fprintf(ps," x1 y1 moveto\n");
|
|
fprintf(ps," x2 y1 lineto\n");
|
|
fprintf(ps," x2 y2 lineto\n");
|
|
fprintf(ps," x1 y2 lineto\n");
|
|
fprintf(ps," closepath } def\n\n");
|
|
|
|
fprintf(ps,"/drawrect %% draw outline of a rectanagle\n");
|
|
fprintf(ps," { rect stroke } def\n\n");
|
|
|
|
fprintf(ps,"/fillrect %% fill in a rectanagle\n");
|
|
fprintf(ps," { rect fill } def\n\n");
|
|
|
|
fprintf (ps,"/drawarc { arc stroke } def %% draw an arc\n");
|
|
fprintf (ps,"/drawarcn { arcn stroke } def "
|
|
" %% draw an arc in the opposite direction\n\n");
|
|
|
|
fprintf (ps,"%%Fill a counterclockwise or clockwise arc sector, "
|
|
"respectively.\n");
|
|
fprintf (ps,"/fillarc { moveto currentpoint 5 2 roll arc closepath fill } "
|
|
"def\n");
|
|
fprintf (ps,"/fillarcn { moveto currentpoint 5 2 roll arcn closepath fill } "
|
|
"def\n\n");
|
|
|
|
fprintf (ps,"/fillpoly { 3 1 roll moveto %% move to first point\n"
|
|
" 2 exch 1 exch {pop lineto} for %% line to all other points\n"
|
|
" closepath fill } def\n\n");
|
|
|
|
|
|
fprintf(ps,"%%Color Definitions:\n");
|
|
fprintf(ps,"/white { 1 setgray } def\n");
|
|
fprintf(ps,"/black { 0 setgray } def\n");
|
|
fprintf(ps,"/grey55 { .55 setgray } def\n");
|
|
fprintf(ps,"/grey75 { .75 setgray } def\n");
|
|
fprintf(ps,"/blue { 0 0 1 setrgbcolor } def\n");
|
|
fprintf(ps,"/green { 0 1 0 setrgbcolor } def\n");
|
|
fprintf(ps,"/yellow { 1 1 0 setrgbcolor } def\n");
|
|
fprintf(ps,"/cyan { 0 1 1 setrgbcolor } def\n");
|
|
fprintf(ps,"/red { 1 0 0 setrgbcolor } def\n");
|
|
fprintf(ps,"/darkgreen { 0 0.5 0 setrgbcolor } def\n");
|
|
fprintf(ps,"/magenta { 1 0 1 setrgbcolor } def\n");
|
|
fprintf(ps,"/bisque { 1 0.89 0.77 setrgbcolor } def\n");
|
|
fprintf(ps,"/lightblue { 0.68 0.85 0.9 setrgbcolor } def\n");
|
|
fprintf(ps,"/thistle { 0.85 0.75 0.85 setrgbcolor } def\n");
|
|
fprintf(ps,"/plum { 0.87 0.63 0.87 setrgbcolor } def\n");
|
|
fprintf(ps,"/khaki { 0.94 0.9 0.55 setrgbcolor } def\n");
|
|
fprintf(ps,"/coral { 1 0.5 0.31 setrgbcolor } def\n");
|
|
fprintf(ps,"/turquoise { 0.25 0.88 0.82 setrgbcolor } def\n");
|
|
fprintf(ps,"/mediumpurple { 0.58 0.44 0.86 setrgbcolor } def\n");
|
|
fprintf(ps,"/darkslateblue { 0.28 0.24 0.55 setrgbcolor } def\n");
|
|
fprintf(ps,"/darkkhaki { 0.74 0.72 0.42 setrgbcolor } def\n");
|
|
|
|
fprintf(ps,"\n%%Solid and dashed line definitions:\n");
|
|
fprintf(ps,"/linesolid {[] 0 setdash} def\n");
|
|
fprintf(ps,"/linedashed {[3 3] 0 setdash} def\n");
|
|
|
|
fprintf(ps,"\n%%%%EndProlog\n");
|
|
fprintf(ps,"%%%%Page: 1 1\n\n");
|
|
|
|
/* Set up PostScript graphics state to match current one. */
|
|
force_setcolor (currentcolor);
|
|
force_setlinestyle (currentlinestyle);
|
|
force_setlinewidth (currentlinewidth);
|
|
force_setfontsize (currentfontsize);
|
|
|
|
/* Draw this in the bottom margin -- must do before the clippath is set */
|
|
draw_message ();
|
|
|
|
/* Set clipping on page. */
|
|
fprintf(ps,"%.2f %.2f %.2f %.2f rect ",ps_left, ps_bot,ps_right,ps_top);
|
|
fprintf(ps,"clip newpath\n\n");
|
|
|
|
return (1);
|
|
}
|
|
|
|
/* Properly ends postscript output and redirects output to screen. */
|
|
void close_postscript (void)
|
|
{
|
|
|
|
fprintf(ps,"showpage\n");
|
|
fprintf(ps,"\n%%%%Trailer\n");
|
|
fclose (ps);
|
|
gl_state.disp_type = SCREEN;
|
|
update_transform(); /* Ensure screen world reflects any changes *
|
|
* made while printing. */
|
|
|
|
/* Need to make sure that we really set up the graphics context.
|
|
* The current font set indicates the last font used in a postscript call,
|
|
* etc., *NOT* the font set in the X11 or Win32 graphics context. Force the
|
|
* current font, colour etc. to be applied to the graphics context, so
|
|
* subsequent drawing commands work properly.
|
|
*/
|
|
|
|
force_setcolor (currentcolor);
|
|
force_setlinestyle (currentlinestyle);
|
|
force_setlinewidth (currentlinewidth);
|
|
force_setfontsize (currentfontsize);
|
|
}
|
|
|
|
|
|
/* Sets up the default menu buttons on the right hand side of the window. */
|
|
static void
|
|
build_default_menu (void)
|
|
{
|
|
int i, xcen, x1, y1, bwid, bheight, space;
|
|
const int NUM_ARROW_BUTTONS = 4, NUM_STANDARD_BUTTONS = 12, SEPARATOR_BUTTON_INDEX = 8;
|
|
|
|
|
|
#ifdef X11
|
|
unsigned long valuemask;
|
|
XSetWindowAttributes menu_attributes;
|
|
|
|
menu = XCreateSimpleWindow(display,toplevel,
|
|
top_width-MWIDTH, 0, MWIDTH, display_height, 0,
|
|
colors[BLACK], colors[LIGHTGREY]);
|
|
menu_attributes.event_mask = ExposureMask;
|
|
/* Ignore button presses on the menu background. */
|
|
menu_attributes.do_not_propagate_mask = ButtonPressMask;
|
|
/* Keep menu on top right */
|
|
menu_attributes.win_gravity = NorthEastGravity;
|
|
valuemask = CWWinGravity | CWEventMask | CWDontPropagate;
|
|
XChangeWindowAttributes(display, menu, valuemask, &menu_attributes);
|
|
XMapWindow (display, menu);
|
|
#endif
|
|
|
|
button = (t_button *) my_malloc (NUM_STANDARD_BUTTONS * sizeof (t_button));
|
|
|
|
/* Now do the arrow buttons */
|
|
bwid = 28;
|
|
space = 3;
|
|
y1 = 10;
|
|
xcen = 51;
|
|
x1 = xcen - bwid/2;
|
|
button[0].xleft = x1;
|
|
button[0].ytop = y1;
|
|
#ifdef X11
|
|
setpoly (0, bwid/2, bwid/2, bwid/3, -PI/2.); /* Up */
|
|
#else
|
|
button[0].type = BUTTON_TEXT;
|
|
strcpy(button[0].text, "U");
|
|
#endif
|
|
button[0].fcn = translate_up;
|
|
|
|
y1 += bwid + space;
|
|
x1 = xcen - 3*bwid/2 - space;
|
|
button[1].xleft = x1;
|
|
button[1].ytop = y1;
|
|
#ifdef X11
|
|
setpoly (1, bwid/2, bwid/2, bwid/3, PI); /* Left */
|
|
#else
|
|
button[1].type = BUTTON_TEXT;
|
|
strcpy(button[1].text, "L");
|
|
#endif
|
|
button[1].fcn = translate_left;
|
|
|
|
x1 = xcen + bwid/2 + space;
|
|
button[2].xleft = x1;
|
|
button[2].ytop = y1;
|
|
#ifdef X11
|
|
setpoly (2, bwid/2, bwid/2, bwid/3, 0); /* Right */
|
|
#else
|
|
button[2].type = BUTTON_TEXT;
|
|
strcpy(button[2].text, "R");
|
|
#endif
|
|
button[2].fcn = translate_right;
|
|
|
|
y1 += bwid + space;
|
|
x1 = xcen - bwid/2;
|
|
button[3].xleft = x1;
|
|
button[3].ytop = y1;
|
|
#ifdef X11
|
|
setpoly (3, bwid/2, bwid/2, bwid/3, +PI/2.); /* Down */
|
|
#else
|
|
button[3].type = BUTTON_TEXT;
|
|
strcpy(button[3].text, "D");
|
|
#endif
|
|
button[3].fcn = translate_down;
|
|
|
|
for (i = 0; i < NUM_ARROW_BUTTONS; i++) {
|
|
button[i].width = bwid;
|
|
button[i].height = bwid;
|
|
button[i].enabled = 1;
|
|
}
|
|
|
|
/* Rectangular buttons */
|
|
|
|
y1 += bwid + space + 6;
|
|
space = 8;
|
|
bwid = 90;
|
|
bheight = 26;
|
|
x1 = xcen - bwid/2;
|
|
for (i = NUM_ARROW_BUTTONS; i < NUM_STANDARD_BUTTONS; i++) {
|
|
button[i].xleft = x1;
|
|
button[i].ytop = y1;
|
|
button[i].type = BUTTON_TEXT;
|
|
button[i].width = bwid;
|
|
button[i].enabled = 1;
|
|
if (i != SEPARATOR_BUTTON_INDEX) {
|
|
button[i].height = bheight;
|
|
y1 += bheight + space;
|
|
}
|
|
else {
|
|
button[i].height = 2;
|
|
button[i].type = BUTTON_SEPARATOR;
|
|
y1 += 2 + space;
|
|
}
|
|
}
|
|
|
|
strcpy (button[4].text,"Zoom In");
|
|
strcpy (button[5].text,"Zoom Out");
|
|
strcpy (button[6].text,"Zoom Fit");
|
|
strcpy (button[7].text,"Window");
|
|
strcpy (button[8].text,"---1");
|
|
strcpy (button[9].text,"PostScript");
|
|
strcpy (button[10].text,"Proceed");
|
|
strcpy (button[11].text,"Exit");
|
|
|
|
button[4].fcn = zoom_in;
|
|
button[5].fcn = zoom_out;
|
|
button[6].fcn = zoom_fit;
|
|
button[7].fcn = adjustwin; // see 'adjustButton' below in WIN32 section
|
|
button[8].fcn = NULL;
|
|
button[9].fcn = postscript;
|
|
button[10].fcn = proceed;
|
|
button[11].fcn = quit;
|
|
|
|
for (i = 0; i < NUM_STANDARD_BUTTONS; i++)
|
|
map_button (i);
|
|
num_buttons = NUM_STANDARD_BUTTONS;
|
|
|
|
#ifdef WIN32
|
|
adjustButton = 7;
|
|
if(!InvalidateRect(hButtonsWnd, NULL, TRUE))
|
|
DRAW_ERROR();
|
|
if(!UpdateWindow(hButtonsWnd))
|
|
DRAW_ERROR();
|
|
#endif
|
|
}
|
|
|
|
/* Makes sure the font of the specified size is loaded. Point_size *
|
|
* MUST be between 1 and MAX_FONT_SIZE. */
|
|
static void
|
|
load_font(int pointsize)
|
|
{
|
|
|
|
if (pointsize > MAX_FONT_SIZE || pointsize < 1) {
|
|
printf ("Error: font size %d is out of valid range, 1 to %d.\n",
|
|
pointsize, MAX_FONT_SIZE);
|
|
return;
|
|
}
|
|
|
|
if (font_is_loaded[pointsize]) // Nothing to do.
|
|
return;
|
|
|
|
#ifdef X11
|
|
#define NUM_FONT_TYPES 3
|
|
char fontname[NUM_FONT_TYPES][BUFSIZE];
|
|
int ifont;
|
|
bool success = false;
|
|
|
|
/* Use proper point-size medium-weight upright helvetica font */
|
|
// Exists on most X11 systems.
|
|
// Backup font: lucidasans, in the new naming style.
|
|
sprintf(fontname[0],"-*-helvetica-medium-r-*--*-%d0-*-*-*-*-*-*",
|
|
pointsize);
|
|
sprintf(fontname[1], "lucidasans-%d", pointsize);
|
|
sprintf(fontname[2],"-schumacher-clean-medium-r-*--*-%d0-*-*-*-*-*-*",
|
|
pointsize);
|
|
|
|
|
|
|
|
|
|
for (ifont = 0; ifont < NUM_FONT_TYPES; ifont++) {
|
|
#ifdef VERBOSE
|
|
printf ("Loading font: point size: %d, fontname: %s\n",pointsize,
|
|
fontname[ifont]);
|
|
#endif
|
|
/* Load font and get font information structure. */
|
|
if ((font_info[pointsize] = XLoadQueryFont(display,fontname[ifont])) == NULL) {
|
|
#ifdef VERBOSE
|
|
fprintf( stderr, "Cannot open font %s\n", fontname[ifont]);
|
|
#endif
|
|
}
|
|
else {
|
|
success = true;
|
|
break;
|
|
}
|
|
}
|
|
if (!success) {
|
|
printf ("Error in load_font: cannot load any font of pointsize %d.\n",
|
|
pointsize);
|
|
printf ("Use xlsfonts to list available fonts, and modify load_font\n");
|
|
printf ("in graphics.cpp.\n");
|
|
exit (1);
|
|
}
|
|
#else /* WIN32 */
|
|
LOGFONT *lf = font_info[pointsize] = (LOGFONT*)my_malloc(sizeof(LOGFONT));
|
|
ZeroMemory(lf, sizeof(LOGFONT));
|
|
lf->lfHeight = pointsize;
|
|
lf->lfWeight = FW_NORMAL;
|
|
lf->lfCharSet = ANSI_CHARSET;
|
|
lf->lfOutPrecision = OUT_DEFAULT_PRECIS;
|
|
lf->lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
|
lf->lfQuality = PROOF_QUALITY;
|
|
lf->lfPitchAndFamily = VARIABLE_PITCH | FF_SWISS;
|
|
strcpy(lf->lfFaceName, "Arial");
|
|
#endif
|
|
|
|
font_is_loaded[pointsize] = true;
|
|
}
|
|
|
|
|
|
/* Return information useful for debugging.
|
|
* Used to return the top-level window object too, but that made graphics.h
|
|
* export all windows and X11 headers to the client program, so VB deleted
|
|
* that object (mainwnd) from this structure.
|
|
*/
|
|
void report_structure(t_report *report) {
|
|
report->xmult = xmult;
|
|
report->ymult = ymult;
|
|
report->xleft = xleft;
|
|
report->xright = xright;
|
|
report->ytop = ytop;
|
|
report->ybot = ybot;
|
|
report->ps_xmult = ps_xmult;
|
|
report->ps_ymult = ps_ymult;
|
|
report->top_width = top_width;
|
|
report->top_height = top_height;
|
|
}
|
|
|
|
|
|
void set_mouse_move_input (bool enable) {
|
|
get_mouse_move_input = enable;
|
|
}
|
|
|
|
|
|
void set_keypress_input (bool enable) {
|
|
get_keypress_input = enable;
|
|
}
|
|
|
|
|
|
void enable_or_disable_button (int ibutton, bool enabled) {
|
|
|
|
if (button[ibutton].type != BUTTON_SEPARATOR) {
|
|
button[ibutton].enabled = enabled;
|
|
#ifdef WIN32
|
|
EnableWindow(button[ibutton].hwnd, button[ibutton].enabled);
|
|
#else // X11
|
|
drawbut(ibutton);
|
|
XFlush(display);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
void set_draw_mode (enum e_draw_mode draw_mode) {
|
|
/* Set normal (overwrite) or xor (useful for rubber-banding)
|
|
* drawing mode.
|
|
*/
|
|
|
|
if (draw_mode == DRAW_NORMAL) {
|
|
#ifdef X11
|
|
current_gc = gc;
|
|
#else
|
|
if (!SetROP2(hGraphicsDC, R2_COPYPEN))
|
|
SELECT_ERROR();
|
|
#endif
|
|
}
|
|
else { // DRAW_XOR
|
|
#ifdef X11
|
|
current_gc = gcxor;
|
|
#else
|
|
if (!SetROP2(hGraphicsDC, R2_XORPEN))
|
|
SELECT_ERROR();
|
|
#endif
|
|
}
|
|
current_draw_mode = draw_mode;
|
|
}
|
|
|
|
|
|
void change_button_text(const char *button_name, const char *new_button_text) {
|
|
/* Change the text on a button with button_name to new_button_text.
|
|
* Not a strictly necessary function, since you could intead just
|
|
* destroy button_name and make a new buton.
|
|
*/
|
|
int i, bnum;
|
|
|
|
bnum = -1;
|
|
|
|
for (i=4;i<num_buttons;i++) {
|
|
if (button[i].type == BUTTON_TEXT &&
|
|
strcmp (button[i].text, button_name) == 0) {
|
|
bnum = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bnum != -1) {
|
|
strncpy (button[i].text, new_button_text, BUTTON_TEXT_LEN);
|
|
#ifdef X11
|
|
drawbut (i);
|
|
#else // Win32
|
|
SetWindowText(button[bnum].hwnd, new_button_text);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
/**********************************
|
|
* X-Windows Specific Definitions *
|
|
*********************************/
|
|
#ifdef X11
|
|
|
|
/* Creates a small window at the top of the graphics area for text messages */
|
|
static void build_textarea (void)
|
|
{
|
|
XSetWindowAttributes menu_attributes;
|
|
unsigned long valuemask;
|
|
|
|
textarea = XCreateSimpleWindow(display,toplevel,
|
|
0, top_height-T_AREA_HEIGHT, display_width-MWIDTH, T_AREA_HEIGHT, 0,
|
|
colors[BLACK], colors[LIGHTGREY]);
|
|
menu_attributes.event_mask = ExposureMask;
|
|
/* ButtonPresses in this area are ignored. */
|
|
menu_attributes.do_not_propagate_mask = ButtonPressMask;
|
|
/* Keep text area on bottom left */
|
|
menu_attributes.win_gravity = SouthWestGravity;
|
|
valuemask = CWWinGravity | CWEventMask | CWDontPropagate;
|
|
XChangeWindowAttributes(display, textarea, valuemask, &menu_attributes);
|
|
XMapWindow (display, textarea);
|
|
}
|
|
|
|
|
|
|
|
static Bool test_if_exposed (Display *disp, XEvent *event_ptr, XPointer dummy)
|
|
/* Returns True if the event passed in is an exposure event. Note that
|
|
* the bool type returned by this function is defined in Xlib.h.
|
|
*/
|
|
{
|
|
|
|
|
|
if (event_ptr->type == Expose) {
|
|
return (True);
|
|
}
|
|
|
|
return (False);
|
|
}
|
|
|
|
|
|
static void menutext(Window win, int xc, int yc, const char *text)
|
|
{
|
|
|
|
/* draws text center at xc, yc -- used only by menu drawing stuff */
|
|
|
|
int len, width;
|
|
|
|
len = strlen(text);
|
|
width = XTextWidth(font_info[menu_font_size], text, len);
|
|
XDrawString(display, win, gc_menus, xc-width/2, yc +
|
|
(font_info[menu_font_size]->ascent - font_info[menu_font_size]->descent)/2,
|
|
text, len);
|
|
}
|
|
|
|
|
|
static void drawbut (int bnum)
|
|
{
|
|
|
|
/* Draws button bnum in either its pressed or unpressed state. */
|
|
|
|
int width, height, thick, i, ispressed;
|
|
XPoint mypoly[6];
|
|
|
|
width = button[bnum].width;
|
|
height = button[bnum].height;
|
|
|
|
if (button[bnum].type == BUTTON_SEPARATOR) {
|
|
int x,y;
|
|
|
|
x = button[bnum].xleft;
|
|
y = button[bnum].ytop;
|
|
XSetForeground(display, gc_menus,colors[WHITE]);
|
|
XDrawLine(display, menu, gc_menus, x, y+1, x+width, y+1);
|
|
XSetForeground(display, gc_menus,colors[BLACK]);
|
|
XDrawLine(display, menu, gc_menus, x, y, x+width, y);
|
|
return;
|
|
}
|
|
|
|
ispressed = button[bnum].ispressed;
|
|
thick = 2;
|
|
/* Draw top and left edges of 3D box. */
|
|
if (ispressed) {
|
|
XSetForeground(display, gc_menus,colors[BLACK]);
|
|
}
|
|
else {
|
|
XSetForeground(display, gc_menus,colors[WHITE]);
|
|
}
|
|
|
|
/* Note: X Windows doesn't appear to draw the bottom pixel of *
|
|
* a polygon with XFillPolygon, so I make this 1 pixel thicker *
|
|
* to compensate. */
|
|
mypoly[0].x = 0;
|
|
mypoly[0].y = height;
|
|
mypoly[1].x = 0;
|
|
mypoly[1].y = 0;
|
|
mypoly[2].x = width;
|
|
mypoly[2].y = 0;
|
|
mypoly[3].x = width-thick;
|
|
mypoly[3].y = thick;
|
|
mypoly[4].x = thick;
|
|
mypoly[4].y = thick;
|
|
mypoly[5].x = thick;
|
|
mypoly[5].y = height-thick;
|
|
XFillPolygon(display,button[bnum].win,gc_menus,mypoly,6,Convex,
|
|
CoordModeOrigin);
|
|
|
|
/* Draw bottom and right edges of 3D box. */
|
|
if (ispressed) {
|
|
XSetForeground(display, gc_menus,colors[WHITE]);
|
|
}
|
|
else {
|
|
XSetForeground(display, gc_menus,colors[BLACK]);
|
|
}
|
|
mypoly[0].x = 0;
|
|
mypoly[0].y = height;
|
|
mypoly[1].x = width;
|
|
mypoly[1].y = height;
|
|
mypoly[2].x = width;
|
|
mypoly[2].y = 0;
|
|
mypoly[3].x = width-thick;
|
|
mypoly[3].y = thick;
|
|
mypoly[4].x = width-thick;
|
|
mypoly[4].y = height-thick;
|
|
mypoly[5].x = thick;
|
|
mypoly[5].y = height-thick;
|
|
XFillPolygon(display,button[bnum].win,gc_menus,mypoly,6,Convex,
|
|
CoordModeOrigin);
|
|
|
|
/* Draw background */
|
|
if (ispressed) {
|
|
XSetForeground(display, gc_menus,colors[DARKGREY]);
|
|
}
|
|
else {
|
|
XSetForeground(display, gc_menus,colors[LIGHTGREY]);
|
|
}
|
|
|
|
/* Give x,y of top corner and width and height */
|
|
XFillRectangle (display,button[bnum].win,gc_menus,thick,thick,
|
|
width-2*thick, height-2*thick);
|
|
|
|
/* Draw polygon, if there is one */
|
|
if (button[bnum].type == BUTTON_POLY) {
|
|
for (i=0;i<3;i++) {
|
|
mypoly[i].x = button[bnum].poly[i][0];
|
|
mypoly[i].y = button[bnum].poly[i][1];
|
|
}
|
|
XSetForeground(display, gc_menus,colors[BLACK]);
|
|
XFillPolygon(display,button[bnum].win,gc_menus,mypoly,3,Convex,
|
|
CoordModeOrigin);
|
|
}
|
|
|
|
/* Draw text, if there is any */
|
|
if (button[bnum].type == BUTTON_TEXT) {
|
|
if (button[bnum].enabled)
|
|
XSetForeground(display, gc_menus,colors[BLACK]);
|
|
else
|
|
XSetForeground(display, gc_menus,colors[DARKGREY]);
|
|
menutext(button[bnum].win,button[bnum].width/2,
|
|
button[bnum].height/2,button[bnum].text);
|
|
}
|
|
}
|
|
|
|
|
|
static int which_button (Window win)
|
|
{
|
|
int i;
|
|
|
|
for (i=0;i<num_buttons;i++) {
|
|
if (button[i].win == win)
|
|
return(i);
|
|
}
|
|
printf("Error: Unknown button ID in which_button.\n");
|
|
return(0);
|
|
}
|
|
|
|
|
|
static void turn_on_off (int pressed) {
|
|
/* Shows when the menu is active or inactive by colouring the
|
|
* buttons.
|
|
*/
|
|
int i;
|
|
|
|
for (i=0;i<num_buttons;i++) {
|
|
button[i].ispressed = pressed;
|
|
drawbut(i);
|
|
}
|
|
}
|
|
|
|
|
|
static void drawmenu(void)
|
|
{
|
|
int i;
|
|
|
|
XClearWindow (display, menu);
|
|
XSetForeground(display, gc_menus,colors[WHITE]);
|
|
XDrawRectangle(display, menu, gc_menus, 0, 0, MWIDTH, top_height);
|
|
XSetForeground(display, gc_menus,colors[BLACK]);
|
|
XDrawLine(display, menu, gc_menus, 0, top_height-1, MWIDTH, top_height-1);
|
|
XDrawLine(display, menu, gc_menus, MWIDTH-1, top_height, MWIDTH-1, 0);
|
|
|
|
for (i=0;i<num_buttons;i++) {
|
|
drawbut(i);
|
|
}
|
|
}
|
|
|
|
#endif /* X-Windows Specific Definitions */
|
|
|
|
|
|
|
|
/*************************************************
|
|
* Microsoft Windows (WIN32) Specific Definitions *
|
|
*************************************************/
|
|
#ifdef WIN32
|
|
|
|
static LRESULT CALLBACK
|
|
MainWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
MINMAXINFO FAR *lpMinMaxInfo;
|
|
|
|
switch(message)
|
|
{
|
|
|
|
case WM_CREATE:
|
|
hStatusWnd = CreateWindow(szStatusName, NULL, WS_CHILDWINDOW | WS_VISIBLE,
|
|
0, 0, 0, 0, hwnd, (HMENU) 102, (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
|
|
hButtonsWnd = CreateWindow(szButtonsName, NULL, WS_CHILDWINDOW | WS_VISIBLE,
|
|
0, 0, 0, 0, hwnd, (HMENU) 103, (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
|
|
hGraphicsWnd = CreateWindow(szGraphicsName, NULL, WS_CHILDWINDOW | WS_VISIBLE,
|
|
0, 0, 0, 0, hwnd, (HMENU) 101, (HINSTANCE) GetWindowLong(hwnd, GWL_HINSTANCE), NULL);
|
|
return 0;
|
|
|
|
case WM_SIZE:
|
|
/* Window has been resized. Save the new client dimensions */
|
|
top_width = LOWORD (lParam);
|
|
top_height = HIWORD (lParam);
|
|
|
|
/* Resize the children windows */
|
|
if(!MoveWindow(hGraphicsWnd, 1, 1, top_width - MWIDTH - 1, top_height - T_AREA_HEIGHT - 1, TRUE))
|
|
DRAW_ERROR();
|
|
// if (drawscreen_ptr)
|
|
// zoom_fit(drawscreen_ptr);
|
|
if(!MoveWindow(hStatusWnd, 0, top_height - T_AREA_HEIGHT, top_width - MWIDTH, T_AREA_HEIGHT, TRUE))
|
|
DRAW_ERROR();
|
|
if(!MoveWindow(hButtonsWnd, top_width - MWIDTH, 0, MWIDTH, top_height, TRUE))
|
|
DRAW_ERROR();
|
|
|
|
return 0;
|
|
|
|
// WC : added to solve window resizing problem
|
|
case WM_GETMINMAXINFO:
|
|
// set the MINMAXINFO structure pointer
|
|
lpMinMaxInfo = (MINMAXINFO FAR *) lParam;
|
|
lpMinMaxInfo->ptMinTrackSize.x = display_width / 2;
|
|
lpMinMaxInfo->ptMinTrackSize.y = display_height / 2;
|
|
|
|
return 0;
|
|
|
|
|
|
case WM_DESTROY:
|
|
if(!DeleteObject(hGrayBrush))
|
|
DELETE_ERROR();
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
|
|
case WM_KEYDOWN:
|
|
if (get_keypress_input)
|
|
keypress_ptr((char) wParam);
|
|
return 0;
|
|
}
|
|
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
|
|
static LRESULT CALLBACK
|
|
GraphicsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static TEXTMETRIC tm;
|
|
|
|
PAINTSTRUCT ps;
|
|
static RECT oldAdjustRect;
|
|
static HPEN hDotPen = 0;
|
|
static HBITMAP hbmBuffer = 0, hbmObjtest, hbmAllObjtest;
|
|
static int X, Y, i;
|
|
|
|
switch(message)
|
|
{
|
|
case WM_CREATE:
|
|
|
|
/* Get the text metrics once upon creation (system font cannot change) */
|
|
hCurrentDC = hGraphicsDC = hForegroundDC = GetDC (hwnd);
|
|
if(!hGraphicsDC)
|
|
DRAW_ERROR();
|
|
|
|
hBackgroundDC = CreateCompatibleDC(hForegroundDC);
|
|
if (!hBackgroundDC)
|
|
CREATE_ERROR();
|
|
if (!SetMapMode(hBackgroundDC, MM_TEXT))
|
|
CREATE_ERROR();
|
|
hbmBuffer = CreateCompatibleBitmap(hForegroundDC, display_width, display_height);
|
|
if (!(hbmBuffer))
|
|
CREATE_ERROR();
|
|
if (!SelectObject(hBackgroundDC, hbmBuffer))
|
|
SELECT_ERROR();
|
|
|
|
// monochrome bitmap
|
|
hObjtestDC = CreateCompatibleDC(hForegroundDC);
|
|
if (!hObjtestDC)
|
|
CREATE_ERROR();
|
|
if (!SetMapMode(hObjtestDC, MM_TEXT))
|
|
CREATE_ERROR();
|
|
hbmObjtest = CreateCompatibleBitmap(hObjtestDC, display_width, display_height);
|
|
if (!(hbmObjtest))
|
|
CREATE_ERROR();
|
|
if (!SelectObject(hObjtestDC, hbmObjtest))
|
|
SELECT_ERROR();
|
|
|
|
// monochrome bitmap
|
|
hAllObjtestDC = CreateCompatibleDC(hForegroundDC);
|
|
if (!hObjtestDC)
|
|
CREATE_ERROR();
|
|
if (!SetMapMode(hAllObjtestDC, MM_TEXT))
|
|
CREATE_ERROR();
|
|
hbmAllObjtest = CreateCompatibleBitmap(hAllObjtestDC, display_width, display_height);
|
|
if (!(hbmAllObjtest))
|
|
CREATE_ERROR();
|
|
if (!SelectObject(hAllObjtestDC, hbmAllObjtest))
|
|
SELECT_ERROR();
|
|
|
|
//if(!GetTextMetrics (hGraphicsDC, &tm))
|
|
// DRAW_ERROR();
|
|
if(!SetBkMode(hGraphicsDC, TRANSPARENT))
|
|
DRAW_ERROR();
|
|
|
|
/* Setup the pens, etc */
|
|
currentlinestyle = SOLID;
|
|
currentcolor = BLACK;
|
|
currentlinewidth = 1;
|
|
|
|
/*
|
|
if(!ReleaseDC (hwnd, hGraphicsDC))
|
|
DRAW_ERROR();
|
|
|
|
hGraphicsDC = 0;
|
|
*/ currentfontsize = 12;
|
|
return 0;
|
|
|
|
case WM_PAINT:
|
|
// was in xor mode, but got a general redraw.
|
|
// switch to normal drawing so we repaint properly.
|
|
if (current_draw_mode == DRAW_XOR) {
|
|
set_draw_mode(DRAW_NORMAL);
|
|
invalidate_screen();
|
|
return 0;
|
|
}
|
|
hCurrentDC = hGraphicsDC;
|
|
drawtoscreen();
|
|
/*hGraphicsDC =*/ BeginPaint(hwnd, &ps);
|
|
if(!hGraphicsDC)
|
|
DRAW_ERROR();
|
|
|
|
if (InEventLoop) {
|
|
if(!GetUpdateRect(hwnd, &updateRect, FALSE)) {
|
|
updateRect.left = 0;
|
|
updateRect.right = top_width;
|
|
updateRect.top = 0;
|
|
updateRect.bottom = top_height;
|
|
}
|
|
|
|
if(windowAdjustFlag > 1) {
|
|
hDotPen = CreatePen(PS_DASH, 1, win32_colors[gl_state.background_cindex]);
|
|
if(!hDotPen)
|
|
CREATE_ERROR();
|
|
if (!SetROP2(hGraphicsDC, R2_XORPEN))
|
|
SELECT_ERROR();
|
|
if(!SelectObject(hGraphicsDC, GetStockObject(NULL_BRUSH)))
|
|
SELECT_ERROR();
|
|
if(!SelectObject(hGraphicsDC, hDotPen))
|
|
SELECT_ERROR();
|
|
if(!Rectangle(hGraphicsDC, oldAdjustRect.left, oldAdjustRect.top,
|
|
oldAdjustRect.right, oldAdjustRect.bottom))
|
|
DRAW_ERROR();
|
|
if(!Rectangle(hGraphicsDC, adjustRect.left, adjustRect.top, adjustRect.right,
|
|
adjustRect.bottom))
|
|
DRAW_ERROR();
|
|
oldAdjustRect = adjustRect;
|
|
if (!SetROP2(hGraphicsDC, R2_COPYPEN))
|
|
SELECT_ERROR();
|
|
if(!SelectObject(hGraphicsDC, GetStockObject(NULL_PEN)))
|
|
SELECT_ERROR();
|
|
if(!DeleteObject(hDotPen))
|
|
DELETE_ERROR();
|
|
}
|
|
else
|
|
drawscreen_ptr();
|
|
}
|
|
EndPaint(hwnd, &ps);
|
|
hGraphicsDC = hCurrentDC;
|
|
|
|
/* Crash hard if called at wrong time */
|
|
/* hGraphicsDC = NULL;*/
|
|
return 0;
|
|
|
|
case WM_SIZE:
|
|
/* Window has been resized. Save the new client dimensions */
|
|
cxClient = LOWORD (lParam);
|
|
cyClient = HIWORD (lParam);
|
|
update_transform();
|
|
|
|
return 0;
|
|
|
|
case WM_DESTROY:
|
|
if(!DeleteObject(hGraphicsPen))
|
|
DELETE_ERROR();
|
|
if(!DeleteObject(hGraphicsBrush))
|
|
DELETE_ERROR();
|
|
if(!DeleteObject(hGraphicsFont))
|
|
DELETE_ERROR();
|
|
if (!DeleteObject(hbmBuffer))
|
|
DELETE_ERROR();
|
|
if (!DeleteObject(hbmObjtest))
|
|
DELETE_ERROR();
|
|
if (!DeleteObject(hbmAllObjtest))
|
|
DELETE_ERROR();
|
|
if(!DeleteDC(hBackgroundDC))
|
|
DELETE_ERROR();
|
|
if(!DeleteDC(hObjtestDC))
|
|
DELETE_ERROR();
|
|
if(!DeleteDC(hAllObjtestDC))
|
|
DELETE_ERROR();
|
|
if(!ReleaseDC(hwnd, hForegroundDC))
|
|
DELETE_ERROR();
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
if (!windowAdjustFlag) {
|
|
mouseclick_ptr(XTOWORLD(LOWORD(lParam)), YTOWORLD(HIWORD(lParam)));
|
|
}
|
|
else {
|
|
// Special handling for the "Window" command, which takes multiple clicks.
|
|
// First you push the button, then you click for one corner, then you click for the other
|
|
// corner.
|
|
if(windowAdjustFlag == 1) {
|
|
windowAdjustFlag ++;
|
|
X = adjustRect.left = adjustRect.right = LOWORD(lParam);
|
|
Y = adjustRect.top = adjustRect.bottom = HIWORD(lParam);
|
|
oldAdjustRect = adjustRect;
|
|
}
|
|
else {
|
|
int i;
|
|
int adjustx[2], adjusty[2];
|
|
|
|
windowAdjustFlag = 0;
|
|
button[adjustButton].ispressed = 0;
|
|
SendMessage(button[adjustButton].hwnd, BM_SETSTATE, 0, 0);
|
|
|
|
for (i=0; i<num_buttons; i++) {
|
|
if (button[i].type != BUTTON_SEPARATOR && button[i].enabled) {
|
|
if(!EnableWindow (button[i].hwnd, TRUE))
|
|
DRAW_ERROR();
|
|
}
|
|
}
|
|
adjustx[0] = adjustRect.left;
|
|
adjustx[1] = adjustRect.right;
|
|
adjusty[0] = adjustRect.top;
|
|
adjusty[1] = adjustRect.bottom;
|
|
|
|
update_win(adjustx, adjusty, invalidate_screen);
|
|
}
|
|
}
|
|
return 0;
|
|
|
|
// right click : a quick way to zoom in
|
|
case WM_RBUTTONDOWN:
|
|
if (!windowAdjustFlag) {
|
|
// first disable some buttons
|
|
//adjustButton = LOWORD(wParam) - 200;
|
|
button[adjustButton].ispressed = 1;
|
|
for (i=0; i<num_buttons; i++) {
|
|
EnableWindow(button[i].hwnd, FALSE);
|
|
SendMessage(button[i].hwnd, BM_SETSTATE, button[i].ispressed, 0);
|
|
}
|
|
|
|
windowAdjustFlag = 2;
|
|
X = adjustRect.left = adjustRect.right = LOWORD(lParam);
|
|
Y = adjustRect.top = adjustRect.bottom = HIWORD(lParam);
|
|
oldAdjustRect = adjustRect;
|
|
}
|
|
else {
|
|
int i;
|
|
int adjustx[2], adjusty[2];
|
|
|
|
windowAdjustFlag = 0;
|
|
button[adjustButton].ispressed = 0;
|
|
SendMessage(button[adjustButton].hwnd, BM_SETSTATE, 0, 0);
|
|
|
|
for (i=0; i<num_buttons; i++) {
|
|
if (button[i].type != BUTTON_SEPARATOR && button[i].enabled) {
|
|
if(!EnableWindow (button[i].hwnd, TRUE))
|
|
DRAW_ERROR();
|
|
}
|
|
}
|
|
adjustx[0] = adjustRect.left;
|
|
adjustx[1] = adjustRect.right;
|
|
adjusty[0] = adjustRect.top;
|
|
adjusty[1] = adjustRect.bottom;
|
|
|
|
update_win(adjustx, adjusty, invalidate_screen);
|
|
}
|
|
return 0;
|
|
|
|
case WM_MOUSEMOVE:
|
|
if(windowAdjustFlag == 1) {
|
|
return 0;
|
|
}
|
|
else if (windowAdjustFlag >= 2) {
|
|
if ( X > LOWORD(lParam)) {
|
|
adjustRect.left = LOWORD(lParam);
|
|
adjustRect.right = X;
|
|
}
|
|
else {
|
|
adjustRect.left = X;
|
|
adjustRect.right = LOWORD(lParam);
|
|
}
|
|
if ( Y > HIWORD(lParam)) {
|
|
adjustRect.top = HIWORD(lParam);
|
|
adjustRect.bottom = Y;
|
|
}
|
|
else {
|
|
adjustRect.top = Y;
|
|
adjustRect.bottom = HIWORD(lParam);
|
|
}
|
|
if(!InvalidateRect(hGraphicsWnd, &oldAdjustRect, FALSE))
|
|
DRAW_ERROR();
|
|
if(!InvalidateRect(hGraphicsWnd, &adjustRect, FALSE))
|
|
DRAW_ERROR();
|
|
if(!UpdateWindow(hGraphicsWnd))
|
|
DRAW_ERROR();
|
|
|
|
return 0;
|
|
}
|
|
else if (get_mouse_move_input)
|
|
mousemove_ptr(XTOWORLD(LOWORD(lParam)), YTOWORLD(HIWORD(lParam)));
|
|
|
|
return 0;
|
|
}
|
|
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
|
|
static LRESULT CALLBACK
|
|
StatusWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HDC hdc;
|
|
PAINTSTRUCT ps;
|
|
RECT rect;
|
|
|
|
switch(message)
|
|
{
|
|
case WM_CREATE:
|
|
hdc = GetDC(hwnd);
|
|
if(!hdc)
|
|
DRAW_ERROR();
|
|
if(!SetBkMode(hdc, TRANSPARENT))
|
|
DRAW_ERROR();
|
|
if(!ReleaseDC(hwnd, hdc))
|
|
DRAW_ERROR();
|
|
return 0;
|
|
|
|
case WM_PAINT:
|
|
hdc = BeginPaint(hwnd, &ps);
|
|
if(!hdc)
|
|
DRAW_ERROR();
|
|
|
|
if(!GetClientRect(hwnd, &rect))
|
|
DRAW_ERROR();
|
|
|
|
if(!SelectObject(hdc, GetStockObject(NULL_BRUSH)))
|
|
SELECT_ERROR();
|
|
if(!SelectObject(hdc, GetStockObject(WHITE_PEN)))
|
|
SELECT_ERROR();
|
|
if(!Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom))
|
|
DRAW_ERROR();
|
|
if(!SelectObject(hdc, GetStockObject(BLACK_PEN)))
|
|
SELECT_ERROR();
|
|
if(!MoveToEx(hdc, rect.left, rect.bottom-1, NULL))
|
|
DRAW_ERROR();
|
|
if(!LineTo(hdc, rect.right-1, rect.bottom-1))
|
|
DRAW_ERROR();
|
|
if(!LineTo(hdc, rect.right-1, rect.top))
|
|
DRAW_ERROR();
|
|
|
|
if(!DrawText(hdc, TEXT(statusMessage), -1, &rect, DT_CENTER | DT_VCENTER | DT_SINGLELINE))
|
|
DRAW_ERROR();
|
|
|
|
if(!EndPaint(hwnd, &ps))
|
|
DRAW_ERROR();
|
|
return 0;
|
|
|
|
case WM_SIZE:
|
|
return 0;
|
|
|
|
case WM_DESTROY:
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
}
|
|
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
|
|
static LRESULT CALLBACK
|
|
ButtonsWND(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
HDC hdc;
|
|
PAINTSTRUCT ps;
|
|
RECT rect;
|
|
static HBRUSH hBrush;
|
|
int i;
|
|
|
|
switch(message)
|
|
{
|
|
case WM_COMMAND:
|
|
if (!windowAdjustFlag) {
|
|
button[LOWORD(wParam) - 200].fcn(invalidate_screen);
|
|
if (windowAdjustFlag) {
|
|
adjustButton = LOWORD(wParam) - 200;
|
|
button[adjustButton].ispressed = 1;
|
|
for (i=0; i<num_buttons; i++) {
|
|
EnableWindow(button[i].hwnd, FALSE);
|
|
SendMessage(button[i].hwnd, BM_SETSTATE, button[i].ispressed, 0);
|
|
}
|
|
}
|
|
}
|
|
SetFocus(hMainWnd);
|
|
return 0;
|
|
|
|
case WM_CREATE:
|
|
hdc = GetDC(hwnd);
|
|
if(!hdc)
|
|
DRAW_ERROR();
|
|
hBrush = CreateSolidBrush(win32_colors[LIGHTGREY]);
|
|
if(!hBrush)
|
|
CREATE_ERROR();
|
|
if(!SelectObject(hdc, hBrush))
|
|
SELECT_ERROR();
|
|
if(!SetBkMode(hdc, TRANSPARENT))
|
|
DRAW_ERROR();
|
|
if(!ReleaseDC(hwnd, hdc))
|
|
DRAW_ERROR();
|
|
|
|
return 0;
|
|
|
|
case WM_PAINT:
|
|
hdc = BeginPaint(hwnd, &ps);
|
|
if(!hdc)
|
|
DRAW_ERROR();
|
|
|
|
if(!GetClientRect(hwnd, &rect))
|
|
DRAW_ERROR();
|
|
|
|
if(!SelectObject(hdc, GetStockObject(NULL_BRUSH)))
|
|
SELECT_ERROR();
|
|
if(!SelectObject(hdc, GetStockObject(WHITE_PEN)))
|
|
SELECT_ERROR();
|
|
if(!Rectangle(hdc, rect.left, rect.top, rect.right, rect.bottom))
|
|
DRAW_ERROR();
|
|
if(!SelectObject(hdc, GetStockObject(BLACK_PEN)))
|
|
SELECT_ERROR();
|
|
if(!MoveToEx(hdc, rect.left, rect.bottom-1, NULL))
|
|
DRAW_ERROR();
|
|
if(!LineTo(hdc, rect.right-1, rect.bottom-1))
|
|
DRAW_ERROR();
|
|
if(!LineTo(hdc, rect.right-1, rect.top))
|
|
DRAW_ERROR();
|
|
|
|
for (i=0; i < num_buttons; i++) {
|
|
if(button[i].type == BUTTON_SEPARATOR) {
|
|
int x, y, w;
|
|
|
|
x = button[i].xleft;
|
|
y = button[i].ytop;
|
|
w = button[i].width;
|
|
|
|
if(!MoveToEx (hdc, x, y, NULL))
|
|
DRAW_ERROR();
|
|
if(!LineTo (hdc, x + w, y))
|
|
DRAW_ERROR();
|
|
if(!SelectObject(hdc, GetStockObject(WHITE_PEN)))
|
|
SELECT_ERROR();
|
|
if(!MoveToEx (hdc, x, y+1, NULL))
|
|
DRAW_ERROR();
|
|
if(!LineTo (hdc, x + w, y+1))
|
|
DRAW_ERROR();
|
|
if(!SelectObject(hdc, GetStockObject(BLACK_PEN)))
|
|
SELECT_ERROR();
|
|
}
|
|
}
|
|
if(!EndPaint(hwnd, &ps))
|
|
DRAW_ERROR();
|
|
return 0;
|
|
|
|
case WM_DESTROY:
|
|
for (i=0; i<num_buttons; i++) {
|
|
}
|
|
if(!DeleteObject(hBrush))
|
|
DELETE_ERROR();
|
|
PostQuitMessage(0);
|
|
return 0;
|
|
}
|
|
|
|
return DefWindowProc(hwnd, message, wParam, lParam);
|
|
}
|
|
|
|
|
|
void reset_win32_state () {
|
|
// Not sure exactly what needs to be reset to NULL etc.
|
|
// Resetting everthing to be safe.
|
|
hGraphicsPen = 0;
|
|
hGraphicsBrush = 0;
|
|
hGrayBrush = 0;
|
|
hGraphicsDC = 0;
|
|
hForegroundDC = 0;
|
|
hBackgroundDC = 0;
|
|
hCurrentDC = 0, /* WC : double-buffer */
|
|
hObjtestDC = 0;
|
|
hAllObjtestDC = 0; /* object test */
|
|
|
|
hGraphicsFont = 0;
|
|
|
|
/* These are used for the "Window" graphics button. They keep track of whether we're entering
|
|
* the window rectangle to zoom to, etc.
|
|
*/
|
|
windowAdjustFlag = 0;
|
|
adjustButton = -1;
|
|
InEventLoop = FALSE;
|
|
}
|
|
|
|
|
|
void win32_drain_message_queue () {
|
|
// Drain the message queue, so we don't have a quit message lying around
|
|
// that will stop us from re-opening a window later if desired.
|
|
MSG msg;
|
|
while (PeekMessage(&msg, hMainWnd, 0, 0, PM_REMOVE)) {
|
|
if (msg.message == WM_QUIT) {
|
|
printf ("Got the quit message.\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
void drawtobuffer(void) {
|
|
hGraphicsDC = hBackgroundDC;
|
|
}
|
|
|
|
|
|
void drawtoscreen(void) {
|
|
hGraphicsDC = hForegroundDC;
|
|
}
|
|
|
|
|
|
void displaybuffer(void) {
|
|
if (!BitBlt(hForegroundDC, xcoord(xleft), ycoord(ytop),
|
|
xcoord(xright)-xcoord(xleft), ycoord(ybot)-ycoord(ytop), hBackgroundDC,//hAllObjtestDC,
|
|
0, 0, SRCCOPY))
|
|
DRAW_ERROR();
|
|
}
|
|
|
|
|
|
static void _drawcurve(t_point *points, int npoints, int fill) {
|
|
/* Draw a beizer curve.
|
|
* Must have 3I+1 points, since each Beizer curve needs 3 points and we also
|
|
* need an initial starting point
|
|
*/
|
|
HPEN hOldPen;
|
|
HBRUSH hOldBrush;
|
|
float xmin, ymin, xmax, ymax;
|
|
int i;
|
|
|
|
if ((npoints - 1) % 3 != 0 || npoints > MAXPTS)
|
|
DRAW_ERROR();
|
|
|
|
/* Conservative (but fast) clip test -- check containing rectangle of *
|
|
* polygon. */
|
|
|
|
xmin = xmax = points[0].x;
|
|
ymin = ymax = points[0].y;
|
|
|
|
for (i=1;i<npoints;i++) {
|
|
xmin = min (xmin,points[i].x);
|
|
xmax = max (xmax,points[i].x);
|
|
ymin = min (ymin,points[i].y);
|
|
ymax = max (ymax,points[i].y);
|
|
}
|
|
|
|
if (rect_off_screen(xmin,ymin,xmax,ymax))
|
|
return;
|
|
|
|
if (gl_state.disp_type == SCREEN) {
|
|
#ifdef X11
|
|
/* implement X11 version here */
|
|
#else /* Win32 */
|
|
// create POINT array
|
|
POINT pts[MAXPTS];
|
|
int i;
|
|
|
|
for (i = 0; i < npoints; i++) {
|
|
pts[i].x = xcoord(points[i].x);
|
|
pts[i].y = ycoord(points[i].y);
|
|
}
|
|
|
|
if (!fill) {
|
|
hOldPen = (HPEN)SelectObject(hGraphicsDC, hGraphicsPen);
|
|
if(!(hOldPen))
|
|
SELECT_ERROR();
|
|
}
|
|
else {
|
|
hOldPen = (HPEN)SelectObject(hGraphicsDC, GetStockObject(NULL_PEN));
|
|
if(!(hOldPen))
|
|
SELECT_ERROR();
|
|
hOldBrush = (HBRUSH)SelectObject(hGraphicsDC, hGraphicsBrush);
|
|
if(!(hOldBrush))
|
|
SELECT_ERROR();
|
|
}
|
|
|
|
if (!BeginPath(hGraphicsDC))
|
|
DRAW_ERROR();
|
|
if(!PolyBezier(hGraphicsDC, pts, npoints))
|
|
DRAW_ERROR();
|
|
if (!EndPath(hGraphicsDC))
|
|
DRAW_ERROR();
|
|
|
|
if (!fill) {
|
|
if (!StrokePath(hGraphicsDC))
|
|
DRAW_ERROR();
|
|
}
|
|
else {
|
|
if (!FillPath(hGraphicsDC))
|
|
DRAW_ERROR();
|
|
}
|
|
|
|
if(!SelectObject(hGraphicsDC, hOldPen))
|
|
SELECT_ERROR();
|
|
|
|
if (fill) {
|
|
if(!SelectObject(hGraphicsDC, hOldBrush))
|
|
SELECT_ERROR();
|
|
}
|
|
#endif
|
|
}
|
|
else {
|
|
int i;
|
|
|
|
fprintf(ps, "newpath\n");
|
|
fprintf(ps, "%.2f %.2f moveto\n", XPOST(points[0].x), YPOST(points[0].y));
|
|
for (i = 1; i < npoints; i+= 3)
|
|
fprintf(ps,"%.2f %.2f %.2f %.2f %.2f %.2f curveto\n", XPOST(points[i].x), YPOST(points[i].y),
|
|
XPOST(points[i+1].x), YPOST(points[i+1].y), XPOST(points[i+2].x), YPOST(points[i+2].y));
|
|
if (!fill)
|
|
fprintf(ps, "stroke\n");
|
|
else
|
|
fprintf(ps, "fill\n");
|
|
}
|
|
}
|
|
|
|
|
|
void drawcurve(t_point *points,
|
|
int npoints) {
|
|
_drawcurve(points, npoints, 0);
|
|
}
|
|
|
|
|
|
void fillcurve(t_point *points,
|
|
int npoints) {
|
|
_drawcurve(points, npoints, 1);
|
|
}
|
|
|
|
|
|
void object_start(int all) {
|
|
if (all)
|
|
hGraphicsDC = hAllObjtestDC;
|
|
else
|
|
hGraphicsDC = hObjtestDC;
|
|
setcolor(WHITE);
|
|
fillrect (xleft, ytop, xright, ybot);
|
|
setcolor(BLACK);
|
|
}
|
|
|
|
|
|
void object_end() {
|
|
hGraphicsDC = hCurrentDC;
|
|
|
|
}
|
|
|
|
|
|
int pt_on_object(int all, float x, float y) {
|
|
COLORREF c;
|
|
|
|
if (all)
|
|
c = GetPixel(hAllObjtestDC, xcoord(x), ycoord(y));
|
|
else
|
|
c = GetPixel(hObjtestDC, xcoord(x), ycoord(y));
|
|
|
|
// printf("c = %x\n", c);
|
|
|
|
return c == win32_colors[BLACK];
|
|
}
|
|
|
|
static int check_fontsize(int pointsize,
|
|
float ymax) {
|
|
// return 0 if matches, 1 if font too big, -1 if font too small
|
|
// a font matches if it's height is 90-100% of ymax tall
|
|
float height;
|
|
TEXTMETRIC textmetric;
|
|
HFONT hOldFont;
|
|
int ret;
|
|
|
|
setfontsize(pointsize);
|
|
|
|
hOldFont = (HFONT)SelectObject(hGraphicsDC, hGraphicsFont);
|
|
if(!(hOldFont))
|
|
SELECT_ERROR();
|
|
|
|
if (!GetTextMetrics(hGraphicsDC, &textmetric))
|
|
DRAW_ERROR();
|
|
height = (textmetric.tmAscent + 2 * textmetric.tmDescent) / ymult;
|
|
|
|
if (height >= ymax * 0.9) {
|
|
if (height <= ymax)
|
|
ret = 0;
|
|
else
|
|
ret = 1;
|
|
}
|
|
else
|
|
ret = -1;
|
|
|
|
if(!SelectObject(hGraphicsDC, hOldFont))
|
|
SELECT_ERROR();
|
|
|
|
return ret;
|
|
}
|
|
|
|
int findfontsize(float ymax) {
|
|
// find the correct point size which will fit in the specified ymax as the max
|
|
// height of the font, using a binary search
|
|
int bot = 1;
|
|
int top = MAX_FONT_SIZE;
|
|
int mid, check;
|
|
|
|
while (bot <= top) {
|
|
mid = (bot+top)/2;
|
|
|
|
check = check_fontsize(mid, ymax);
|
|
if (!(check))
|
|
return mid;
|
|
else if (check > 0) // too big
|
|
top = mid - 1;
|
|
else // too small
|
|
bot = mid + 1;
|
|
}
|
|
if (bot > MAX_FONT_SIZE)
|
|
return MAX_FONT_SIZE;
|
|
|
|
return -1; // can't fit
|
|
}
|
|
|
|
#endif /******** Win32 Specific Definitions ********************/
|
|
|
|
|
|
#else /***** NO_GRAPHICS *******/
|
|
/* No graphics at all. Stub everything out so calling program doesn't have to change
|
|
* but of course graphics won't do anything.
|
|
*/
|
|
|
|
#include "graphics.h"
|
|
|
|
void event_loop (void (*act_on_mousebutton) (float x, float y),
|
|
void (*act_on_mousemove) (float x, float y),
|
|
void (*act_on_keypress) (char key_pressed),
|
|
void (*drawscreen) (void)) { }
|
|
|
|
void init_graphics (const char *window_name, int cindex) { }
|
|
void close_graphics (void) { }
|
|
void update_message (const char *msg) { }
|
|
void draw_message (void) { }
|
|
void init_world (float xl, float yt, float xr, float yb) { }
|
|
void flushinput (void) { }
|
|
void setcolor (int cindex) { }
|
|
int getcolor (void) { return 0; }
|
|
void setlinestyle (int linestyle) { }
|
|
void setlinewidth (int linewidth) { }
|
|
void setfontsize (int pointsize) { }
|
|
void drawline (float x1, float y1, float x2, float y2) { }
|
|
void drawrect (float x1, float y1, float x2, float y2) { }
|
|
void fillrect (float x1, float y1, float x2, float y2) { }
|
|
void fillpoly (t_point *points, int npoints) { }
|
|
void drawarc (float xcen, float ycen, float rad, float startang,
|
|
float angextent) { }
|
|
void drawellipticarc (float xc, float yc, float radx, float rady, float startang, float angextent) { }
|
|
|
|
void fillarc (float xcen, float ycen, float rad, float startang,
|
|
float angextent) { }
|
|
void fillellipticarc (float xc, float yc, float radx, float rady, float startang, float angextent) { }
|
|
|
|
void drawtext (float xc, float yc, const char *text, float boundx) { }
|
|
void clearscreen (void) { }
|
|
|
|
void create_button (const char *prev_button_text , const char *button_text,
|
|
void (*button_func) (void (*drawscreen) (void))) { }
|
|
|
|
void destroy_button (const char *button_text) { }
|
|
|
|
int init_postscript (const char *fname) {
|
|
return (1);
|
|
}
|
|
|
|
void close_postscript (void) { }
|
|
|
|
void report_structure(t_report*) { }
|
|
|
|
void set_mouse_move_input (bool) { }
|
|
|
|
void set_keypress_input (bool) { }
|
|
|
|
void set_draw_mode (enum e_draw_mode draw_mode) { }
|
|
|
|
void enable_or_disable_button(int ibutton, bool enabled) { }
|
|
|
|
void change_button_text(const char *button_text, const char *new_button_text) { }
|
|
|
|
#ifdef WIN32
|
|
void drawtobuffer(void) { }
|
|
|
|
void drawtoscreen(void) { }
|
|
|
|
void displaybuffer(void) { }
|
|
|
|
void drawcurve(t_point *points, int npoints) { }
|
|
|
|
void fillcurve(t_point *points, int npoints) { }
|
|
|
|
void object_start(int all) { }
|
|
|
|
void object_end() { }
|
|
|
|
int pt_on_object(float x, float y) { }
|
|
|
|
int findfontsize(float ymax) { }
|
|
|
|
|
|
#endif // WIN32 (subset of commands)
|
|
|
|
#endif // NO_GRAPHICS
|