ZOOMIN.C

/****************************************************************************/ 
/* */
/* Microsoft Confidential */
/* */
/* Copyright (c) Microsoft Corp. 1987-1996 */
/* All Rights Reserved */
/* */
/****************************************************************************/
/****************************** Module Header *******************************
* Module Name: zoomin.c
*
* Microsoft ZoomIn utility. This tool magnifies a portion of the screen,
* allowing you to see things at a pixel level.
*
* History:
* 01/01/88 Created.
* 01/01/92 Ported to NT.
* 03/06/92 Cleanup.
*
****************************************************************************/

#include "zoomin.h"


CHAR szAppName[] = "ZoomIn"; // Aplication name.
HINSTANCE ghInst; // Instance handle.
HWND ghwndApp; // Main window handle.
HANDLE ghaccelTable; // Main accelerator table.
INT gnZoom = 4; // Zoom (magnification) factor.
HPALETTE ghpalPhysical; // Handle to the physical palette.
INT gcxScreenMax; // Width of the screen (less 1).
INT gcyScreenMax; // Height of the screen (less 1).
INT gcxZoomed; // Client width in zoomed pixels.
INT gcyZoomed; // Client height in zoomed pixels.
BOOL gfRefEnable = FALSE; // TRUE if refresh is enabled.
INT gnRefInterval = 20; // Refresh interval in 10ths of seconds.
BOOL gfTracking = FALSE; // TRUE if tracking is in progress.
POINT gptZoom = {100, 100}; // The center of the zoomed area.



/************************************************************************
* WinMain
*
* Main entry point for the application.
*
* Arguments:
*
* History:
*
************************************************************************/

INT WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR lpCmdLine, INT nCmdShow)
{
MSG msg;

if (!InitInstance(hInst, nCmdShow))
return FALSE;

/* * Polling messages from event queue
*/

while (GetMessage(&msg, NULL, 0, 0)) {
if (!TranslateAccelerator(ghwndApp, ghaccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}

return msg.wParam;
}



/************************************************************************
* InitInstance
*
* Instance initialization for the app.
*
* Arguments:
*
* History:
*
************************************************************************/

BOOL InitInstance(
HINSTANCE hInst,
INT cmdShow)
{
WNDCLASS wc;
INT dx;
INT dy;
DWORD flStyle;
RECT rc;

ghInst = hInst;

/*
* Register a class for the main application window.
*/
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hIcon = LoadIcon(hInst, "zoomin");
wc.lpszMenuName = MAKEINTRESOURCE(IDMENU_ZOOMIN);
wc.lpszClassName = szAppName;
wc.hbrBackground = GetStockObject(BLACK_BRUSH);
wc.hInstance = hInst;
wc.style = CS_BYTEALIGNCLIENT | CS_VREDRAW | CS_HREDRAW;
wc.lpfnWndProc = (WNDPROC)AppWndProc;
wc.cbWndExtra = 0;
wc.cbClsExtra = 0;

if (!RegisterClass(&wc))
return FALSE;

if (!(ghaccelTable = LoadAccelerators(hInst, MAKEINTRESOURCE(IDACCEL_ZOOMIN))))
return FALSE;

if (!(ghpalPhysical = CreatePhysicalPalette()))
return FALSE;

gcxScreenMax = GetSystemMetrics(SM_CXSCREEN) - 1; gcyScreenMax = GetSystemMetrics(SM_CYSCREEN) - 1;

flStyle = WS_CAPTION | WS_OVERLAPPED | WS_SYSMENU | WS_THICKFRAME |
WS_MINIMIZEBOX | WS_VSCROLL;
dx = 44 * gnZoom;
dy = 36 * gnZoom;

SetRect(&rc, 0, 0, dx, dy);
AdjustWindowRect(&rc, flStyle, TRUE);

ghwndApp = CreateWindow(szAppName, szAppName, flStyle,
CW_USEDEFAULT, 0, rc.right - rc.left, rc.bottom - rc.top,
NULL, NULL, hInst, NULL);

if (!ghwndApp)
return FALSE;
ShowWindow(ghwndApp, cmdShow);

return TRUE;
}



/************************************************************************
* CreatePhysicalPalette
*
* Creates a palette for the app to use. The palette references the
* physical palette, so that it can properly display images grabbed
* from palette managed apps.
*
* History:
*
************************************************************************/

HPALETTE CreatePhysicalPalette(VOID)
{
PLOGPALETTE ppal;
HPALETTE hpal = NULL;
INT i;

ppal = (PLOGPALETTE)LocalAlloc(LPTR,
sizeof(LOGPALETTE) + sizeof(PALETTEENTRY) * NPAL);
if (ppal) {
ppal->palVersion = 0x300;
ppal->palNumEntries = NPAL;

for (i = 0; i < NPAL; i++) {
ppal->palPalEntry[i].peFlags = (BYTE)PC_EXPLICIT;
ppal->palPalEntry[i].peRed = (BYTE)i;
ppal->palPalEntry[i].peGreen = (BYTE)0;
ppal->palPalEntry[i].peBlue = (BYTE)0;
}

hpal = CreatePalette(ppal);
LocalFree(ppal);
}

return hpal;
}



/************************************************************************
* AppWndProc
*
* Main window proc for the zoomin utility.
*
* Arguments:
* Standard window proc args.
*
* History:
*
************************************************************************/

LONG APIENTRY AppWndProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
PAINTSTRUCT ps;
HCURSOR hcurOld;

switch (msg) {
case WM_CREATE:
SetScrollRange(hwnd, SB_VERT, MIN_ZOOM, MAX_ZOOM, FALSE);
SetScrollPos(hwnd, SB_VERT, gnZoom, FALSE);
break;

case WM_TIMER:
/*
* Update on every timer message. The cursor will be
* flashed to the hourglash for some visual feedback
* of when a snapshot is being taken.
*/
hcurOld = SetCursor(LoadCursor(NULL, IDC_WAIT));
DoTheZoomIn(NULL);
SetCursor(hcurOld);
break;

case WM_PAINT:
BeginPaint(hwnd, &ps);
DoTheZoomIn(ps.hdc);
EndPaint(hwnd, &ps);
return 0L;

case WM_SIZE:
CalcZoomedSize();
break;

case WM_LBUTTONDOWN:
((gptZoom).x = (SHORT)LOWORD(lParam), (gptZoom).y = (SHORT)HIWORD(lParam));
ClientToScreen(hwnd, &gptZoom);
DrawZoomRect();
DoTheZoomIn(NULL);

SetCapture(hwnd);
gfTracking = TRUE;

break;

case WM_MOUSEMOVE:
if (gfTracking) {
DrawZoomRect();
((gptZoom).x = (SHORT)LOWORD(lParam), (gptZoom).y = (SHORT)HIWORD(lParam));
ClientToScreen(hwnd, &gptZoom);
DrawZoomRect();
DoTheZoomIn(NULL);
}

break;

case WM_LBUTTONUP:
if (gfTracking) {
DrawZoomRect();
ReleaseCapture();
gfTracking = FALSE;
}

break;

case WM_VSCROLL:
switch (LOWORD(wParam)) {
case SB_LINEDOWN:
gnZoom++;
break;

case SB_LINEUP:
gnZoom--;
break;

case SB_PAGEUP:
gnZoom -= 2;
break;

case SB_PAGEDOWN:
gnZoom += 2;
break;

case SB_THUMBPOSITION:
case SB_THUMBTRACK:
gnZoom = HIWORD(wParam);
break;
}

gnZoom = BOUND(gnZoom, MIN_ZOOM, MAX_ZOOM);
SetScrollPos(hwnd, SB_VERT, gnZoom, TRUE);
CalcZoomedSize();
DoTheZoomIn(NULL);
break;

case WM_KEYDOWN:
switch (wParam) {
case VK_UP:
case VK_DOWN:
case VK_LEFT:
case VK_RIGHT:
MoveView((INT)wParam, GetKeyState(VK_SHIFT) & 0x8000,
GetKeyState(VK_CONTROL) & 0x8000);
break;
}

break;

case WM_COMMAND:
switch (LOWORD(wParam)) {
case MENU_EDIT_COPY:
CopyToClipboard();
break;

case MENU_EDIT_REFRESH:
DoTheZoomIn(NULL);
break;

case MENU_OPTIONS_REFRESHRATE:
DialogBox(ghInst, (LPSTR)MAKEINTRESOURCE(DID_REFRESHRATE),
hwnd, (WNDPROC)RefreshRateDlgProc);

break;

case MENU_HELP_ABOUT:
DialogBox(ghInst, (LPSTR)MAKEINTRESOURCE(DID_ABOUT),
hwnd, (WNDPROC)AboutDlgProc);

break;

default:
break;
}

break;

case WM_CLOSE:
if (ghpalPhysical)
DeleteObject(ghpalPhysical);

DestroyWindow(hwnd);

break;

case WM_DESTROY:
PostQuitMessage(0);
break;

default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}

return 0L;
}



/************************************************************************
* CalcZoomedSize
*
* Calculates some globals. This routine needs to be called any
* time that the size of the app or the zoom factor changes.
*
* History:
*
************************************************************************/

VOID CalcZoomedSize(VOID)
{
RECT rc;

GetClientRect(ghwndApp, &rc);

gcxZoomed = (rc.right / gnZoom) + 1;
gcyZoomed = (rc.bottom / gnZoom) + 1;
}



/************************************************************************
* DoTheZoomIn
*
* Does the actual paint of the zoomed image.
*
* Arguments:
* HDC hdc - If not NULL, this hdc will be used to paint with.
* If NULL, a dc for the apps window will be obtained.
*
* History:
*
************************************************************************/

VOID DoTheZoomIn(
HDC hdc)
{
BOOL fRelease;
HPALETTE hpalOld = NULL;
HDC hdcScreen;
INT x;
INT y;

if (!hdc) {
hdc = GetDC(ghwndApp);
fRelease = TRUE;
}
else {
fRelease = FALSE;
}

if (ghpalPhysical) {
hpalOld = SelectPalette(hdc, ghpalPhysical, FALSE);
RealizePalette(hdc);
}

/*
* The point must not include areas outside the screen dimensions.
*/
x = BOUND(gptZoom.x, gcxZoomed / 2, gcxScreenMax - (gcxZoomed / 2));
y = BOUND(gptZoom.y, gcyZoomed / 2, gcyScreenMax - (gcyZoomed / 2));

hdcScreen = GetDC(NULL);
SetStretchBltMode(hdc, COLORONCOLOR);
StretchBlt(hdc, 0, 0, gnZoom * gcxZoomed, gnZoom * gcyZoomed,
hdcScreen, x - gcxZoomed / 2,
y - gcyZoomed / 2, gcxZoomed, gcyZoomed, SRCCOPY);
ReleaseDC(NULL, hdcScreen);

if (hpalOld)
SelectPalette(hdc, hpalOld, FALSE);

if (fRelease)
ReleaseDC(ghwndApp, hdc);
}



/************************************************************************
* MoveView
*
* This function moves the current view around.
*
* Arguments:
* INT nDirectionCode - Direction to move. Must be VK_UP, VK_DOWN,
* VK_LEFT or VK_RIGHT.
* BOOL fFast - TRUE if the move should jump a larger increment.
* If FALSE, the move is just one pixel.
* BOOL fPeg - If TRUE, the view will be pegged to the screen
* boundary in the specified direction. This overides
* the fFast parameter.
*
* History:
*
************************************************************************/

VOID MoveView(
INT nDirectionCode,
BOOL fFast,
BOOL fPeg)
{
INT delta;

if (fFast)
delta = FASTDELTA;
else
delta = 1;

switch (nDirectionCode) {
case VK_UP:
if (fPeg)
gptZoom.y = gcyZoomed / 2;
else
gptZoom.y -= delta;

gptZoom.y = BOUND(gptZoom.y, 0, gcyScreenMax);

break;

case VK_DOWN:
if (fPeg)
gptZoom.y = gcyScreenMax - (gcyZoomed / 2);
else
gptZoom.y += delta;

gptZoom.y = BOUND(gptZoom.y, 0, gcyScreenMax);

break;

case VK_LEFT:
if (fPeg)
gptZoom.x = gcxZoomed / 2;
else
gptZoom.x -= delta;

gptZoom.x = BOUND(gptZoom.x, 0, gcxScreenMax);

break;

case VK_RIGHT:
if (fPeg)
gptZoom.x = gcxScreenMax - (gcxZoomed / 2);
else
gptZoom.x += delta;

gptZoom.x = BOUND(gptZoom.x, 0, gcxScreenMax);

break;
}

DoTheZoomIn(NULL);
}



/************************************************************************
* DrawZoomRect
*
* This function draws the tracking rectangle. The size and shape of
* the rectangle will be proportional to the size and shape of the
* app's client, and will be affected by the zoom factor as well.
*
* History:
*
************************************************************************/

VOID DrawZoomRect(VOID)
{
HDC hdc;
RECT rc;
INT x;
INT y;

x = BOUND(gptZoom.x, gcxZoomed / 2, gcxScreenMax - (gcxZoomed / 2));
y = BOUND(gptZoom.y, gcyZoomed / 2, gcyScreenMax - (gcyZoomed / 2));

rc.left = x - gcxZoomed / 2;
rc.top = y - gcyZoomed / 2;
rc.right = rc.left + gcxZoomed;
rc.bottom = rc.top + gcyZoomed;

InflateRect(&rc, 1, 1);

hdc = GetDC(NULL);

PatBlt(hdc, rc.left, rc.top, rc.right-rc.left, 1, DSTINVERT);
PatBlt(hdc, rc.left, rc.bottom, 1, -(rc.bottom-rc.top), DSTINVERT);
PatBlt(hdc, rc.right-1, rc.top, 1, rc.bottom-rc.top, DSTINVERT);
PatBlt(hdc, rc.right, rc.bottom-1, -(rc.right-rc.left), 1, DSTINVERT);

ReleaseDC(NULL, hdc);
}



/************************************************************************
* EnableRefresh
*
* This function turns on or off the auto-refresh feature.
*
* Arguments:
* BOOL fEnable - TRUE to turn the refresh feature on, FALSE to
* turn it off.
*
* History:
*
************************************************************************/

VOID EnableRefresh(
BOOL fEnable)
{
if (fEnable) {
/*
* Already enabled. Do nothing.
*/
if (gfRefEnable)
return;

if (SetTimer(ghwndApp, IDTIMER_ZOOMIN, gnRefInterval * 100, NULL))
gfRefEnable = TRUE;
}
else {
/*
* Not enabled yet. Do nothing.
*/
if (!gfRefEnable)
return;

KillTimer(ghwndApp, IDTIMER_ZOOMIN);
gfRefEnable = FALSE;
}
}



/************************************************************************
* CopyToClipboard
*
* This function copies the client area image of the app into the
* clipboard.
*
* History:
*
************************************************************************/

VOID CopyToClipboard(VOID)
{
HDC hdcSrc;
HDC hdcDst;
RECT rc;
HBITMAP hbm;

if (OpenClipboard(ghwndApp)) {
EmptyClipboard();

if (hdcSrc = GetDC(ghwndApp)) {
GetClientRect(ghwndApp, &rc);
if (hbm = CreateCompatibleBitmap(hdcSrc,
rc.right - rc.left, rc.bottom - rc.top)) {
if (hdcDst = CreateCompatibleDC(hdcSrc)) {
/*
* Calculate the dimensions of the bitmap and
* convert them to tenths of a millimeter for
* setting the size with the SetBitmapDimensionEx
* call. This allows programs like WinWord to
* retrieve the bitmap and know what size to
* display it as.
*/






SetBitmapDimensionEx(hbm, (DWORD)((INT)(((DWORD)(rc.right - rc.left) * MM10PERINCH) / (DWORD)GetDeviceCaps(hdcSrc, LOGPIXELSX))), (DWORD)((INT)(((DWORD)(rc.bottom - rc.top) * MM10PERINCH) / (DWORD)GetDeviceCaps(hdcSrc, LOGPIXELSY))), NULL);

SelectObject(hdcDst, hbm);
BitBlt(hdcDst, 0, 0,
rc.right - rc.left, rc.bottom - rc.top,
hdcSrc, rc.left, rc.top, SRCCOPY);
DeleteDC(hdcDst);
SetClipboardData(CF_BITMAP, hbm);
}
else {
DeleteObject(hbm);
}
}

ReleaseDC(ghwndApp, hdcSrc);
}

CloseClipboard();
}
else {
MessageBeep(0);
}
}



/************************************************************************
* AboutDlgProc
*
* This is the About Box dialog procedure.
*
* History:
*
************************************************************************/

BOOL APIENTRY AboutDlgProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
switch (msg) {
case WM_INITDIALOG:
return TRUE;

case WM_COMMAND:
EndDialog(hwnd, IDOK);
return TRUE;

default:
return FALSE;
}
}



/************************************************************************
* RefreshRateDlgProc
*
* This is the Refresh Rate dialog procedure.
*
* History:
*
************************************************************************/

BOOL APIENTRY RefreshRateDlgProc(
HWND hwnd,
UINT msg,
WPARAM wParam,
LPARAM lParam)
{
BOOL fTranslated;

switch (msg) {
case WM_INITDIALOG:
SendDlgItemMessage(hwnd, DID_REFRESHRATEINTERVAL, EM_LIMITTEXT,
3, 0L);
SetDlgItemInt(hwnd, DID_REFRESHRATEINTERVAL, gnRefInterval, FALSE);
CheckDlgButton(hwnd, DID_REFRESHRATEENABLE, gfRefEnable ? 1 : 0);
return TRUE;

case WM_COMMAND:
switch (LOWORD(wParam)) {
case IDOK:
gnRefInterval = GetDlgItemInt(hwnd, DID_REFRESHRATEINTERVAL,
&fTranslated, FALSE);

/*
* Stop any existing timers then start one with the
* new interval if requested to.
*/
EnableRefresh(FALSE);
EnableRefresh(
IsDlgButtonChecked(hwnd, DID_REFRESHRATEENABLE));

EndDialog(hwnd, IDOK);
break;

case IDCANCEL:
EndDialog(hwnd, IDCANCEL);
break;
}

break;
}

return FALSE;
}