home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
The C Users' Group Library 1994 August
/
wc-cdrom-cusersgrouplibrary-1994-08.iso
/
listings
/
v_10_08
/
qcdemow.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1992-07-21
|
22KB
|
631 lines
///////////////////////////////////////////////////////
// QCDEMOW.CPP: Quadcode demo program for Windows
// Written by:
// Kenneth Van Camp
// RR #1 Box 1255
// East Stroudsburg, PA 18301
// (717)223-8620
//
// Functions -
// main main pgm entry point
// init_graphics initialize graphics
// Cursor::Cursor default constructor
// Cursor::Move move cursor
// RegionDisplay::Display display region
// RegionDisplay::Interact user interaction
// win_yieldfunc yield function for build
//
///////////////////////////////////////////////////////
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#include <time.h>
#include <iostream.h>
#include "qc.h"
#include "qcdemow.h"
// Global variables:
HCURSOR ArrowCursor, // standard arrow cursor
WaitCursor; // hourglass cursor
HDC Hdc; // device context for the window
HWND BuildHdlg, // handle for the Building dialog box
MainHwnd; // handle for application's main window
HANDLE Haccel; // handle for application acclerators
int Shrink = TRUE, // shrink all qc's for plotting?
XShrinkage = 0, // amount to shrink in X direction
YShrinkage = 0, // amount to shrink in Y direction
Xmax = 0, // # pixels in viewport in X direction
Ymax = 0, // # pixels in viewport in Y direction
QCsize, // size of a single-division quadcode
Nquits, // number of quits used to represent region
RegionNumQC = 0, // number of qc's in region
BuildCancelled = FALSE; // was the region building cancelled?
time_t RegionBuildTime = 0;// time to build region
const int SCRRES = 1024; // screen resolution used (logical coords)
// class RegionDisplay: A Region class with graphical
// display and interaction capabilities.
class RegionDisplay: public Region
{
public:
RegionDisplay // constructor from outline
(PointListHeader &vertex_list):
Region (vertex_list) { } // calls base constr.
void Display (void); // display on graphics screen
void Interact (void); // graphic interact function
}; // class RegionDisplay
RegionDisplay *TheRegion = NULL;
// The following is the perimeter list for the demo region:
const int Npts = 37;
const int Ndiv = 128;
const int Reduce = 1;
Point Plist[Npts] =
{
{ 5, 68}, { 28, 68}, { 32, 88}, { 33, 94},
{ 31,110}, { 30,113}, { 35,124}, { 51,125},
{ 63,127}, { 63,119}, { 67,116}, { 59,109},
{ 63,119}, { 63,127}, { 74,126}, { 80,125},
{ 88,112}, { 99,101}, {107, 98}, {113, 96},
{124, 98}, {122, 94}, {123, 91}, {121, 85},
{118, 78}, {108, 73}, { 96, 64}, { 85, 58},
{ 81, 50}, { 82, 46}, { 90, 38}, { 83, 25},
{ 72, 21}, { 58, 8}, { 55, 8}, { 54, 40},
{ 5, 40},
};
PointListHeader Phdr =
{
Npts, Ndiv, Plist
};
// Prototypes for exported functions:
#if defined( __cplusplus )
extern "C" {
#endif // __cplusplus
long FAR PASCAL WndProc (HWND, UINT, WPARAM, LPARAM);
BOOL FAR PASCAL AboutDlgProc (HWND, UINT, WPARAM, LPARAM);
BOOL FAR PASCAL BuildDlgProc (HWND, UINT, WPARAM, LPARAM);
#if defined( __cplusplus )
}
#endif // __cplusplus
// Prototypes for other local functions:
void redraw_win (int first_time);
void update_status (int xcursor, int ycursor);
void set_draw_area (void);
int win_yieldfunc (int pct_complete);
int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,
LPSTR lpszCmdParam, int nCmdShow)
{
static char szAppName[] = "QCDemoW";
MSG msg;
WNDCLASS wndclass;
// Initialize the cursor we will use.
ArrowCursor = LoadCursor (NULL, IDC_ARROW);
WaitCursor = LoadCursor (NULL, IDC_WAIT);
if (!hPrevInstance)
{
wndclass.style = CS_HREDRAW | CS_VREDRAW;
wndclass.lpfnWndProc = WndProc;
wndclass.cbClsExtra = 0;
wndclass.cbWndExtra = 0;
wndclass.hInstance = hInstance;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION);
wndclass.hCursor = ArrowCursor;
wndclass.hbrBackground = GetStockObject (WHITE_BRUSH);
wndclass.lpszMenuName = "QCDEMOW";
wndclass.lpszClassName = szAppName;
RegisterClass (&wndclass);
}
MainHwnd = CreateWindow (szAppName, // window class name
"Quadcode Demo (Windows Version)", // window caption
WS_OVERLAPPEDWINDOW, // window style
CW_USEDEFAULT, // initial x position
CW_USEDEFAULT, // initial y position
CW_USEDEFAULT, // initial x size
CW_USEDEFAULT, // initial y size
NULL, // parent window handle
NULL, // window menu handle
hInstance, // program instance handle
NULL); // creation parameters
ShowWindow (MainHwnd, nCmdShow);
UpdateWindow (MainHwnd);
Haccel = LoadAccelerators (hInstance, "ACCELERATORS_1");
if (Haccel == NULL)
MessageBox (MainHwnd, "Can't Load Acelerators", "NOTICE",
MB_ICONEXCLAMATION | MB_OK);
while (GetMessage (&msg, NULL, 0, 0))
{
// Translate accelerator keystrokes, too.
if (! TranslateAccelerator (MainHwnd, Haccel, &msg))
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
}
return msg.wParam;
} // WinMain
long FAR PASCAL WndProc (HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
PAINTSTRUCT ps;
int ret;
WORD menu_state; // Current menu state (checked or not)
char tmpstr[80];
static int first_paint = TRUE, // Is this the first screen paint?
resized = FALSE; // Was the window just resized?
static HMENU menu; // Popup menu
static HANDLE hinstance; // Instance of this program
static FARPROC about_dlg_proc, // Point to About dialog box procedure
build_dlg_proc; // Point to Build dialog box procedure
switch (message)
{
case WM_CREATE:
// This occurs when the window is first created.
// First get the instance of the main program.
hinstance = ((LPCREATESTRUCT)lParam)->hInstance;
// Register the dialog procedures:
about_dlg_proc = MakeProcInstance ((FARPROC)AboutDlgProc,
hinstance);
build_dlg_proc = MakeProcInstance ((FARPROC)BuildDlgProc,
hinstance);
// Then get the handle for the menu
menu = GetMenu (hwnd);
return 0;
case WM_COMMAND:
// This is a menu selection or accelerator.
// Ignore any menu commands if the region is not built yet.
if (RegionNumQC == 0)
return 0;
switch ( wParam )
{
case QCMNU_EXIT: // File Menu, Exit QCDemoW selection
case QCACC_EXIT: // Alt-X accelerator
ret = MessageBox (hwnd, "Are You Sure You Want To Exit?",
"QCDemoW", MB_OKCANCEL);
if ( ret == IDOK )
PostQuitMessage (0);
return 0;
case QCMNU_ABOUT: // File Menu, About selection
DialogBox (hinstance, "ABOUT_BOX", hwnd, about_dlg_proc);
return 0;
case QCMNU_SHRINKAGE: // Options Menu, Shrinkage selection
case QCACC_SHRINKAGE: // Alt-S accelerator
// This is a boolean checked item: Reverse previous state.
if (Shrink)
{
Shrink = FALSE;
menu_state = MF_UNCHECKED;
}
else
{
Shrink = TRUE;
menu_state = MF_CHECKED;
}
menu_state |= MF_BYCOMMAND;
CheckMenuItem (menu, wParam, menu_state);
// Invalidate the entire window so a repaint is forced.
InvalidateRect (hwnd, NULL, TRUE);
return 0;
default:
// Unknown menu selection
sprintf (tmpstr, "Unknown Selection #%d", wParam);
MessageBox (hwnd, tmpstr, "WARNING",
MB_ICONEXCLAMATION | MB_OK);
return 0;
} // switch ( wParam )
break;
case WM_MOVE:
// Make sure the window is repainted after a move, so we get
// a new Hdc for the status box update.
if (RegionNumQC > 0)
InvalidateRect (hwnd, NULL, TRUE);
return 0;
case WM_SIZE:
// Flag that the window was resized so extents will be
// recalculated on the next WM_PAINT (which comes automatically
// following a resize).
if (RegionNumQC > 0)
resized = TRUE;
return 0;
case WM_PAINT:
if (! first_paint)
{
// Ignore any paint commands after the first one, until
// the region is fully built.
if (RegionNumQC == 0)
return 0;
// Change to the hourglass cursor during standard redraw.
// (Don't have to put up hourglass on first paint, since
// the modeless dialog box is displayed.)
SetCursor (WaitCursor);
}
// Get the device context to repaint the area.
Hdc = BeginPaint (hwnd, &ps);
if (first_paint || resized)
{
// The first time we paint, and each time the window is resized,
// save the viewport extent. (These are device coordinates.)
RECT rect;
GetClientRect (hwnd, &rect) ;
Xmax = rect.right;
Ymax = rect.bottom;
resized = FALSE;
}
// Setup the viewport and drawing mode.
set_draw_area ();
if (Shrink)
{
// Make sure the shrinkage is at least one pixel.
XShrinkage = SCRRES / Xmax + 1;
YShrinkage = SCRRES / Ymax + 1;
}
else
XShrinkage = YShrinkage = 0;
// On the first paint, have to build the region. Put up a
// dialog box to inform the user, and build the region.
if (first_paint)
{
first_paint = FALSE;
BuildHdlg = CreateDialog (hinstance, "BUILD_BOX", hwnd,
build_dlg_proc);
redraw_win (TRUE);
// Close the dialog box. Not an error if it can't be
// closed, since the user can close it.
PostMessage (BuildHdlg, WM_CLOSE, 0, 0);
}
else
{
// After the first paint, just redraw the entire region.
redraw_win (FALSE);
}
EndPaint (hwnd, &ps);
// Put the cursor back as the arrow pointer.
SetCursor (ArrowCursor);
return 0;
case WM_MOUSEMOVE:
// Don't interpret mouse movements unless region already built.
if (RegionNumQC > 0)
{
// When the mouse moves, we wait until there are no more mouse
// movement messages in the queue before processing it.
// That prevents the application from falling too far behind
// the user.
MSG nextmsg;
nextmsg.lParam = lParam; // In case no more in queue.
while ((ret = PeekMessage (&nextmsg, hwnd, WM_MOUSEMOVE,
WM_MOUSEMOVE, PM_REMOVE)) != FALSE)
;
// Find the current mouse position.
POINT pt = MAKEPOINT (nextmsg.lParam);
// Setup the viewport and drawing mode.
Hdc = GetDC (hwnd);
if (Hdc == NULL)
MessageBox (hwnd, "NULL DC", "ERROR",
MB_OK | MB_ICONEXCLAMATION);
set_draw_area ();
// Convert device coordinates to logical (window) coordinates.
DPtoLP (Hdc, &pt, 1); // This doesn't work!
// pt.x = (float)pt.x * SCRRES / Xmax;
// pt.y = (float)pt.y * SCRRES / Ymax;
// Update the status box.
// This should be a message to the status box window,
// but it isn't implemented as a window yet.
update_status (pt.x, pt.y);
}
return 0;
case WM_DESTROY:
PostQuitMessage (0);
return 0;
} // switch
return (DefWindowProc (hwnd, message, wParam, lParam));
} // WndProc
// AboutDlgProc: Dialog box procedure for the "About QCDemoW" selection.
extern "C"
BOOL FAR PASCAL AboutDlgProc (HWND hdlg, UINT message, WPARAM wParam,
LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
// Initialize the message with our performance message.
char tmpstr[80];
wsprintf (tmpstr, "%d Quadcodes Built in %d Seconds",
RegionNumQC, RegionBuildTime);
SetDlgItemText (hdlg, QCTXT_ABOUT, tmpstr);
return TRUE;
case WM_COMMAND:
switch (wParam)
{
case QCBUT_OKABOUT:
EndDialog (hdlg, TRUE);
return TRUE;
}
break;
}
return FALSE;
} // AboutDlgProc
// BuildDlgProc: Dialog box procedure for the "Building Region" message.
// This is a modeless dialog box.
extern "C"
BOOL FAR PASCAL BuildDlgProc (HWND hdlg, UINT message, WPARAM wParam,
LPARAM lParam)
{
switch (message)
{
case WM_INITDIALOG:
return TRUE;
case WM_COMMAND:
switch (wParam)
{
case QCBUT_CANCEL:
// The user decided to cancel the building of the region.
// This has to kill the whole application.
#ifdef NEVER
PostMessage (MainHwnd, WM_DESTROY, 0, 0);
EndDialog (hdlg, TRUE);
#endif
BuildCancelled = TRUE;
return TRUE;
}
break;
case WM_CLOSE:
DestroyWindow (hdlg);
return TRUE;
} // switch (message)
return FALSE;
} // BuildDlgProc
// redraw_win: Redraw the entire region in the graphics window.
void redraw_win (int first_time)
{
if (first_time)
{
// First draw, so build the quadcode region.
// Reduce to proper size
Phdr.ndiv /= Reduce;
int i;
for (i = 0; i < Npts; i++)
{
Phdr.pointptr[i].i /= Reduce;
Phdr.pointptr[i].j /= Reduce;
}
// Set the yield function so user can interrupt.
SetRegionYieldFunc (win_yieldfunc);
time_t tstart;
time (&tstart);
// Build the region from a perimeter list
TheRegion = new RegionDisplay (Phdr);
// Save the time it took to build the region.
time_t tend;
time (&tend);
RegionBuildTime = difftime (tend, tstart);
RegionNumQC = TheRegion->NumQC();
if (RegionNumQC == 0)
{
// Region build was aborted by user.
PostMessage (MainHwnd, WM_DESTROY, 0, 0);
return;
}
}
// Now redisplay the region.
TheRegion->Display();
} // redraw_win
// update_status: Update the status box in the upper left corner of the screen.
void update_status (int xcursor, int ycursor)
{
// These are the coordinates of the status box (logical coords):
const int StatUL = 10, // upper-left corner
StatLR = 85; // lower-right corner
// Find current mouse position
int i = ycursor / QCsize;
int j = xcursor / QCsize;
// Only check if within region limits.
if (i >= 0 && i < Ndiv && j >= 0 && j < Ndiv)
{
QuadCode qc (i, j, Nquits);
if (TheRegion->InRegion (qc))
{
// In region - draw a solid status box.
HBRUSH blk_brush = GetStockObject (BLACK_BRUSH);
SelectObject (Hdc, blk_brush);
Rectangle (Hdc, StatUL, StatUL, StatLR, StatLR);
}
else
{
// Out of region - draw empty status box.
HBRUSH wht_brush = GetStockObject (WHITE_BRUSH);
SelectObject (Hdc, wht_brush);
Rectangle (Hdc, StatUL, StatUL, StatLR, StatLR);
}
} // if i >= 0 ...
} // update_status
// set_draw_area: Setup the viewport and client drawing area for a repaint.
void set_draw_area (void)
{
// Use a viewport sized to the client rectangle, and
// the window (virtual) size is SCRRES x SCRRES. This makes
// (0,0) at the upper left of the window and (SCRRES,SCRRES)
// at the lower right of the window. This is only
// approximate however, since when drawing in isotropic
// mode (to preserver the aspect ratio) we do not have
// the entire window to draw in.
SetMapMode (Hdc, MM_ISOTROPIC);
SetViewportExt (Hdc, Xmax, Ymax);
SetWindowExt (Hdc, SCRRES, SCRRES);
SetWindowOrg (Hdc, 0, 0);
} // set_draw_area
// RegionDisplay::Display: Display the region on the graphics screen.
void RegionDisplay::Display (void)
{
if (NumQC() == 0)
return;
// Get a brush so the quadcodes are filled black:
HBRUSH blk_brush = GetStockObject (BLACK_BRUSH);
SelectObject (Hdc, blk_brush);
// Calculate the size of a single-division quadcode
QCsize = SCRRES / Ndiv;
// Do for each quadcode in the region.
QCNode *qcn;
for ( qcn = first_qcnode; qcn; qcn = qcn->next)
{
COORD i, j;
int nq;
qcn->ToIJ (i, j, nq);
COORD nqc_div = 1L << nq;
float qcfact = Ndiv / nqc_div;
int qcxlen = QCsize * qcfact;
int qcylen = QCsize * qcfact;
int x = j * qcxlen;
int y = i * qcylen;
x += XShrinkage;
y += YShrinkage;
qcxlen -= 2 * XShrinkage;
qcylen -= 2 * YShrinkage;
// Minimum quadcode size is 2 screen units.
qcxlen = max (qcxlen, 2);
qcylen = max (qcylen, 2);
// Draw the quadcode:
Rectangle (Hdc, x, y, x + qcxlen, y + qcylen);
} // for qcn
// Save the maximum number of quits in a quadcode in this region.
Nquits = MaxQuits();
} // RegionDisplay::Display
///////////////////////////////////////////////////////
// win_yieldfunc: A function that is called periodically
// during region building to yield control to the user
// or another application. Under Windows, it checks the
// message queue for a Cancel by the user. This also
// allows Windows to pass messages to other applications.
// It returns TRUE if the user aborts, or FALSE otherwise.
int win_yieldfunc (int pct_complete)
// pct_complete is the percent of region built
{
// Before checking for messages, update the status bar.
// First get the handle of the frame window.
HWND frame_hwnd = GetDlgItem (BuildHdlg, QCFRAME);
HDC frame_hdc = GetDC (frame_hwnd);
RECT rect;
GetClientRect (frame_hwnd, &rect);
// Get a brush so the frame is filled black:
HBRUSH blk_brush = GetStockObject (BLACK_BRUSH);
SelectObject (frame_hdc, blk_brush);
// Calculate the length of the rectangle, proportional to amount done.
int xmax = ((float)pct_complete * (rect.right - 1) / 100.0);
Rectangle (frame_hdc, 1, 1, xmax, rect.bottom - 1);
// Now check to see if the user pressed Cancel button.
MSG nextmsg;
#ifdef NEVER
// Don't know why this doesn't work. Should only have to check for
// commands to dialog box, but this doesn't yield control to Windows
// and it doesn't catch the mouse click.
if (PeekMessage (&nextmsg, BuildHdlg, WM_COMMAND, WM_COMMAND, PM_REMOVE))
{
if (nextmsg.wParam == QCBUT_CANCEL)
{
// User pressed Cancel button
return TRUE;
}
}
return FALSE;
#endif
if (PeekMessage (&nextmsg, NULL, 0, 0, PM_REMOVE))
{
if (! IsDialogMessage (BuildHdlg, &nextmsg))
{
if (! TranslateAccelerator (MainHwnd, Haccel, &nextmsg))
{
TranslateMessage (&nextmsg);
DispatchMessage (&nextmsg);
}
}
}
if (BuildCancelled)
return TRUE;
else
return FALSE;
} // win_yieldfunc