TEXTFX.C

/*****************************************************************************\ 
* This is a part of the Microsoft Source Code Samples.
* Copyright 1992 - 1998 Microsoft Corporation.
* All rights reserved.
* This source code is only intended as a supplement to
* Microsoft Development Tools and/or WinHelp documentation.
* See these sources for detailed information regarding the
* Microsoft samples programs.
\*****************************************************************************/
/*****************************************************************************
* *
* PROGRAM: TextFX.c *
* *
* PURPOSE: TextFX template for Windows applications *
* *
* FUNCTIONS: *
* *
* GetLastErrorBox() - Report GetLastError() values as text *
* WinMain() - calls initialization function, processes message loop *
* InitApplication() - initializes window data and registers window *
* InitInstance() - saves instance handle and creates main window *
* WndProc() - processes messages *
* CenterWindow() - used to center the "About" box over application window *
* About() - processes messages for "About" dialog box *
* *
* COMMENTS: *
*****************************************************************************/

#include <windows.h> // required for all Windows applications
#include "resource.h" // specific to this program
#include <time.h>
#include "textfx.h" // specific to this program
#include "guide.h"
#include "fx.h"

#define QUICKTESTPOINTS 2 // Number of points to use for Quick Test

HINSTANCE hInst; // current instance

char szAppName[] = "TextFX"; // The name of this application
char szTitle[] = "TextFX Test Application"; // The title bar text

// Random number stuff for generating random points for "Quick Test" option
#define randfloat() ((float)rand() / (float)RAND_MAX)
#define random(i) ((short)(randfloat() * (float)(i)))

/**********************************************************************
* *
* FUNCTION: GetLastErrorBox(HWND, LPSTR) *
* *
* PURPOSE: Gets the error status and, if an error is indicated, *
* converts the error number into text and displays it *
* in a MessageBox. *
* *
*********************************************************************/
DWORD GetLastErrorBox(HWND hWnd, LPSTR lpTitle)
{
LPVOID lpv;
DWORD dwRv;

if (GetLastError() == 0) return 0;

dwRv = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM,
NULL,
GetLastError(),
MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
(LPVOID)&lpv,
0,
NULL);

MessageBox(hWnd, lpv, lpTitle, MB_OK);

if(dwRv)
LocalFree(lpv);

SetLastError(0);
return dwRv;
}

/*****************************************************************************
* *
* FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, int) *
* *
* PURPOSE: calls initialization function, processes message loop *
* *
* COMMENTS: *
* *
* Windows recognizes this function by name as the initial entry point *
* for the program. This function calls the application initialization *
* routine, if no other instance of the program is running, and always *
* calls the instance initialization routine. It then executes a message *
* retrieval and dispatch loop that is the top-level control structure *
* for the remainder of execution. The loop is terminated when a WM_QUIT *
* message is received, at which time this function exits the application *
* instance by returning the value passed by PostQuitMessage(). *
* *
* If this function must abort before entering the message loop, it *
* returns the conventional value NULL. *
* *
****************************************************************************/
int APIENTRY WinMain(
HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg;
HANDLE hAccelTable;
LONG lTime;

// Just for fun, lets seed the random number generator
time(&lTime);
srand(lTime);


if (!hPrevInstance) { // Other instances of app running?
if (!InitApplication(hInstance)) { // Initialize shared things
return (FALSE); // Exits if unable to initialize
}
}

/* Perform initializations that apply to a specific instance */

if (!InitInstance(hInstance, nCmdShow)) {
return (FALSE);
}

hAccelTable = LoadAccelerators (hInstance, "TEXTFXACCEL");

/* Acquire and dispatch messages until a WM_QUIT message is received. */

while (GetMessage(&msg, // message structure
NULL, // handle of window receiving the message
0, // lowest message to examine
0)) // highest message to examine
{
if (!TranslateAccelerator (msg.hwnd, hAccelTable, &msg)) {
TranslateMessage(&msg);// Translates virtual key codes
DispatchMessage(&msg); // Dispatches message to window
}
}

return (msg.wParam); // Returns the value from PostQuitMessage

UNREFERENCED_PARAMETER(lpCmdLine); // This will prevent 'unused formal parameter' warnings
}


/*****************************************************************************
* *
* FUNCTION: InitApplication(HINSTANCE) *
* *
* PURPOSE: Initializes window data and registers window class *
* *
* COMMENTS: *
* *
* This function is called at initialization time only if no other *
* instances of the application are running. This function performs *
* initialization tasks that can be done once for any number of running *
* instances. *
* *
* In this case, we initialize a window class by filling out a data *
* structure of type WNDCLASS and calling the Windows RegisterClass() *
* function. Since all instances of this application use the same window *
* class, we only need to do this when the first instance is initialized. *
* *
*****************************************************************************/
BOOL InitApplication(HINSTANCE hInstance)
{
WNDCLASS wc;

// Fill in window class structure with parameters that describe the
// main window.

wc.style = CS_OWNDC; // Class style(s).
wc.lpfnWndProc = (WNDPROC)WndProc; // Window Procedure
wc.cbClsExtra = 0; // No per-class extra data.
wc.cbWndExtra = 0; // No per-window extra data.
wc.hInstance = hInstance; // Owner of this class
wc.hIcon = LoadIcon (hInstance, szAppName); // Icon name from .RC
wc.hCursor = LoadCursor(NULL, IDC_ARROW);// Cursor
wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1);// Default color
wc.lpszMenuName = "TEXTFXMENU"; // Menu name from .RC
wc.lpszClassName = szAppName; // Name to register as

// Register the window class and return success/failure code.
return (RegisterClass(&wc));
}


/*****************************************************************************
* *
* FUNCTION: InitInstance(HINSTANCE, int) *
* *
* PURPOSE: Saves instance handle and creates main window *
* *
* COMMENTS: *
* *
* This function is called at initialization time for every instance of *
* this application. This function performs initialization tasks that *
* cannot be shared by multiple instances. *
* *
* In this case, we save the instance handle in a static variable and *
* create and display the main program window. *
* *
****************************************************************************/
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
HWND hWnd; // Main window handle.

// Save the instance handle in static variable, which will be used in
// many subsequence calls from this application to Windows.

hInst = hInstance; // Store instance handle in our global variable

// Create a main window for this application instance.

hWnd = CreateWindow(
szAppName, // See RegisterClass() call.
szTitle, // Text for window title bar.
WS_OVERLAPPEDWINDOW,// Window style.
CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, // Use default positioning
NULL, // Overlapped windows have no parent.
NULL, // Use the window class menu.
hInstance, // This instance owns this window.
NULL // We don't use any data in our WM_CREATE
);

// If window could not be created, return "failure"
if (!hWnd) {
return (FALSE);
}

// Make the window visible; update its client area; and return "success"
ShowWindow(hWnd, nCmdShow); // Show the window
UpdateWindow(hWnd); // Sends WM_PAINT message

return (TRUE); // We succeeded...

}


/*****************************************************************************
* *
* FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM) *
* *
* PURPOSE: Processes messages *
* *
* MESSAGES: *
* *
* WM_COMMAND - application menu (About dialog box) *
* WM_DESTROY - destroy window *
* *
* COMMENTS: *
* *
* To process the IDM_ABOUT message, call MakeProcInstance() to get the *
* current instance address of the About() function. Then call Dialog *
* box which will create the box according to the information in your *
* TextFX.rc file and turn control over to the About() function. When *
* it returns, free the intance address. *
* *
****************************************************************************/
LRESULT CALLBACK WndProc(
HWND hWnd, // window handle
UINT message, // type of message
WPARAM uParam, // additional information
LPARAM lParam) // additional information
{
int wmId, wmEvent;
static BOOL bOn = TRUE; // Which guide line (top = TRUE) are we on?
static LPPOINT lpTop = NULL;
static LPPOINT lpBot = NULL;
static DWORD dwTop = 0;
static DWORD dwBot = 0;
static BOOL bOutlineOnly = FALSE;
static BOOL bShowGuides = FALSE;
static HPEN hpenBlue, hpenRed;

SetLastError(0); // Set error flag to "no errors"

switch (message) {
case WM_CREATE: // Select an big Arial font into our DC
{
HDC hDC = GetDC(hWnd);
LOGFONT lf;
HFONT hf;

memset(&lf, 0, sizeof(LOGFONT));
lf.lfHeight = -72;// Big fonts scale better
strcpy((LPSTR)&(lf.lfFaceName), "arial");

// Create and select the font
hf = CreateFontIndirect(&lf);
SelectObject(hDC, hf);

hpenBlue = CreatePen(PS_SOLID, 1, RGB(0,0,255));
hpenRed = CreatePen(PS_SOLID, 1, RGB(255,0,0));
}

GetLastErrorBox(hWnd, "Error in WM_CREATE");
break;

case WM_PAINT:
{
PAINTSTRUCT ps;
HDC hDC = BeginPaint(hWnd, &ps);
RECT rect;

// Clear the client area
GetClientRect(hWnd, &rect);
PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);


// If we have something to show...
if (dwTop && dwBot) {

// Show the top guide line if we have one and the user wants to
if (dwTop && bShowGuides) {
SelectObject(hDC, hpenBlue);
ShowGuide(hDC, lpTop, dwTop);
SelectObject(hDC, GetStockObject(BLACK_PEN));
}

// If we have a bottom guide line
if (dwBot) {

// ...then show it if the use wants to
if (bShowGuides) {
SelectObject(hDC, hpenRed);
ShowGuide(hDC, lpBot, dwBot);
SelectObject(hDC, GetStockObject(BLACK_PEN));
}

// Do the mapping and show the text
TextEffect(hDC, lpTop, lpBot, dwTop, dwBot, "This is a test", bOutlineOnly);
}
}

EndPaint(hWnd, &ps);
}
break;

case WM_LBUTTONDOWN: // Read a guide line from the user
{
HDC hDC = GetDC(hWnd);
RECT rect;

if (bOn) {
GetClientRect(hWnd, &rect);

// Clear the screen
PatBlt(hDC, 0, 0, rect.right, rect.bottom, WHITENESS);

// Get rid of the previous guide lines
if (lpTop)
GlobalFree(lpTop);
if (lpBot)
GlobalFree(lpBot);
dwBot = 0;

SelectObject(hDC, hpenBlue); // Top guide line is blue
GetGuideLine(hWnd, &lpTop, &dwTop);
}
else {
SelectObject(hDC, hpenRed);// Bottom guide line is red
GetGuideLine(hWnd, &lpBot, &dwBot);
}

// Black as night, black as coal, I want to see the sun blotted out from the sky...
SelectObject(hDC, GetStockObject(BLACK_PEN));
bOn = !bOn;

if (bOn) {

// Expand the line segment into points
FillOut(&lpTop, &dwTop);
FillOut(&lpBot, &dwBot);

InvalidateRect(hWnd, NULL, FALSE); // Draw the text via WM_PAINT
}
}

GetLastErrorBox(hWnd, "Error in WM_LBUTTONDOWN");
break;

case WM_COMMAND:
wmId = LOWORD(uParam);
wmEvent = HIWORD(uParam);

switch (wmId) {
case IDM_QUICKTEST: // Randomly generate guide lines
{
RECT rect;
int i;

bOn = TRUE; // Reset to top guide line

GetClientRect(hWnd, &rect);
if (dwTop)
GlobalFree(lpTop);
if (dwBot)
GlobalFree(lpBot);

// Allocate top guide line
lpTop = (LPPOINT)GlobalAlloc(GPTR, sizeof(POINT) * QUICKTESTPOINTS);
if (!lpTop) break;

// Allocate bottom guide line
lpBot = (LPPOINT)GlobalAlloc(GPTR, sizeof(POINT) * QUICKTESTPOINTS);
if (!lpBot) {
GlobalFree(lpTop);
break;
}

// Generate points
for (i=0; i<QUICKTESTPOINTS; i++) {
lpTop[i].x = random(rect.right);
lpTop[i].y = random(rect.bottom);
lpBot[i].x = random(rect.right);
lpBot[i].y = random(rect.bottom);
}

// Set number of points for top and bottom lines
dwTop = QUICKTESTPOINTS;
dwBot = QUICKTESTPOINTS;

// Expand the line segment into points
FillOut(&lpTop, &dwTop);
FillOut(&lpBot, &dwBot);

InvalidateRect(hWnd, NULL, FALSE); // Draw the text via WM_PAINT
}

GetLastErrorBox(hWnd, "Error in IDM_QUICKTEST");
break;

case IDM_CHOOSEFONT: // Let the user pick a font
{
HDC hDC = GetDC(hWnd);
HFONT hf;
LOGFONT lf;
CHOOSEFONT cf;

ZeroMemory(&cf, sizeof(cf));
ZeroMemory(&lf, sizeof(lf));

cf.lStructSize = sizeof(CHOOSEFONT);
cf.lpLogFont = &lf;
cf.Flags = CF_SCREENFONTS | CF_TTONLY;

if (ChooseFont(&cf)) {
lf.lfHeight = -72; // Bigger fonts look better
hf = CreateFontIndirect(&lf);
DeleteObject(SelectObject(hDC, hf));
}
}

// If we have text to show then re-render it
if (dwTop && dwBot)
InvalidateRect(hWnd, NULL, FALSE); // Draw the text via WM_PAINT

GetLastErrorBox(hWnd, "Error in IDM_CHOOSEFONT");
break;

case IDM_SHOWGUIDES: // User toggled guide display
bShowGuides = !bShowGuides;
CheckMenuItem(GetMenu(hWnd), IDM_SHOWGUIDES, bShowGuides ? MF_CHECKED : MF_UNCHECKED);

// If we have text to show then re-render it
if (dwTop && dwBot)
InvalidateRect(hWnd, NULL, FALSE); // Draw the text via WM_PAINT

GetLastErrorBox(hWnd, "Error in IDM_SHOWGUIDES");
break;

case IDM_OUTLINE: // User toggled display of text as outline
bOutlineOnly = !bOutlineOnly;
CheckMenuItem(GetMenu(hWnd), IDM_OUTLINE, bOutlineOnly ? MF_CHECKED : MF_UNCHECKED);

// If we have text to show then re-render it
if (dwTop && dwBot)
InvalidateRect(hWnd, NULL, FALSE); // Draw the text via WM_PAINT

GetLastErrorBox(hWnd, "Error in IDM_OUTLINE");
break;

case IDM_ABOUT:
DialogBox(hInst, // current instance
"ABOUTBOX", // dlg resource to use
hWnd, // parent handle
(DLGPROC)About); // About() instance address

break;


case IDM_EXIT:
DestroyWindow (hWnd);
break;

default:
return (DefWindowProc(hWnd, message, uParam, lParam));
}
break;

case WM_DESTROY: // message: window being destroyed
PostQuitMessage(0);
break;

default: // Passes it on if unproccessed
return (DefWindowProc(hWnd, message, uParam, lParam));
}
return (0);
}

/*****************************************************************************
* *
* FUNCTION: CenterWindow (HWND, HWND) *
* *
* PURPOSE: Center one window over another *
* *
* COMMENTS: *
* *
* Dialog boxes take on the screen position that they were designed at, *
* which is not always appropriate. Centering the dialog over a particular *
* window usually results in a better position. *
* *
****************************************************************************/

BOOL CenterWindow (HWND hwndChild, HWND hwndParent)
{
RECT rChild, rParent;
int wChild, hChild, wParent, hParent;
int wScreen, hScreen, xNew, yNew;
HDC hdc;

// Get the Height and Width of the child window
GetWindowRect (hwndChild, &rChild);
wChild = rChild.right - rChild.left;
hChild = rChild.bottom - rChild.top;

// Get the Height and Width of the parent window
GetWindowRect (hwndParent, &rParent);
wParent = rParent.right - rParent.left;
hParent = rParent.bottom - rParent.top;

// Get the display limits
hdc = GetDC (hwndChild);
wScreen = GetDeviceCaps (hdc, HORZRES);
hScreen = GetDeviceCaps (hdc, VERTRES);
ReleaseDC (hwndChild, hdc);

// Calculate new X position, then adjust for screen
xNew = rParent.left + ((wParent - wChild) /2);
if (xNew < 0) {
xNew = 0;
} else if ((xNew+wChild) > wScreen) {
xNew = wScreen - wChild;
}

// Calculate new Y position, then adjust for screen
yNew = rParent.top + ((hParent - hChild) /2);
if (yNew < 0) {
yNew = 0;
} else if ((yNew+hChild) > hScreen) {
yNew = hScreen - hChild;
}

// Set it, and return
return SetWindowPos (hwndChild, NULL,
xNew, yNew, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
}


/*****************************************************************************
* *
* FUNCTION: About(HWND, UINT, WPARAM, LPARAM) *
* *
* PURPOSE: Processes messages for "About" dialog box *
* *
* MESSAGES: *
* *
* WM_INITDIALOG - initialize dialog box *
* WM_COMMAND - Input received *
* *
* COMMENTS: *
* *
* Display version information from the version section of the *
* application resource. *
* *
* Wait for user to click on "Ok" button, then close the dialog box. *
* *
****************************************************************************/

LRESULT CALLBACK About(
HWND hDlg, // window handle of the dialog box
UINT message, // type of message
WPARAM uParam, // message-specific information
LPARAM lParam)
{
switch (message) {
case WM_INITDIALOG: // message: initialize dialog box
// Center the dialog over the application window
CenterWindow (hDlg, GetWindow (hDlg, GW_OWNER));
return (TRUE);

case WM_COMMAND: // message: received a command
if (LOWORD(uParam) == IDOK || LOWORD(uParam) == IDCANCEL) {
EndDialog(hDlg, TRUE); // Exit the dialog
return (TRUE);
}
break;
}
return (FALSE); // Didn't process the message

UNREFERENCED_PARAMETER(lParam); // This will prevent 'unused formal parameter' warnings
}