The New Version of POPPAD

The new version of POPPAD that uses these two dialog boxes (called POPPAD3) is shown in Figure 10-8.

POPPAD3.MAK

#-----------------------

# POPPAD3.MAK make file

#-----------------------

poppad3.exe : poppad.obj poppadf.obj poppadp0.obj \

filedlg.obj poppad3.def poppad.res

link poppad poppadf poppadp0 filedlg, poppad3.exe /align:16, \

NUL, /nod slibcew libw, poppad3

rc poppad.res poppad3.exe

poppad.obj : poppad.c poppad.h

cl -c -Gsw -Ow -W2 -Zp poppad.c

poppadf.obj : poppadf.c

cl -c -Gsw -Ow -W2 -Zp poppadf.c

poppadp0.obj : poppadp0.c

cl -c -Gsw -Ow -W2 -Zp poppadp0.c

filedlg.obj : filedlg.c filedlg.h

cl -c -Gsw -Ow -W2 -Zp filedlg.c

poppad.res : poppad.rc poppad.h poppad.ico filedlg.dlg filedlg.h

rc -r poppad.rc

POPPAD.C

/*---------------------------------------

POPPAD.C -- Popup Editor

(c) Charles Petzold, 1990

---------------------------------------*/

#include <windows.h>

#include "poppad.h"

#define EDITID 1

long FAR PASCAL WndProc (HWND, WORD, WORD, LONG) ;

BOOL ReadFile (HANDLE, HWND, HWND, POFSTRUCT, char *, BOOL) ;

BOOL WriteFile (HANDLE, HWND, HWND, POFSTRUCT, char *, BOOL) ;

BOOL PrintFile (HANDLE, HWND, HWND, char *) ;

LPSTR lstrrchr (LPSTR, char) ;

char szAppName [] = "PopPad" ;

char szFileSpec [] = "*.TXT" ;

char szUntitled [] = "(untitled)" ;

int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance,

LPSTR lpszCmdLine, int nCmdShow)

{

MSG msg ;

HWND hwnd ;

HANDLE hAccel ;

WNDCLASS wndclass ;

if (!hPrevInstance)

{

wndclass.style = CS_HREDRAW | CS_VREDRAW ;

wndclass.lpfnWndProc = WndProc ;

wndclass.cbClsExtra = 0 ;

wndclass.cbWndExtra = 0 ;

wndclass.hInstance = hInstance ;

wndclass.hIcon = LoadIcon (hInstance, szAppName) ;

wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;

wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;

wndclass.lpszMenuName = szAppName ;

wndclass.lpszClassName = szAppName ;

RegisterClass (&wndclass) ;

}

hwnd = CreateWindow (szAppName, NULL,

WS_OVERLAPPEDWINDOW,

GetSystemMetrics (SM_CXSCREEN) / 4,

GetSystemMetrics (SM_CYSCREEN) / 4,

GetSystemMetrics (SM_CXSCREEN) / 2,

GetSystemMetrics (SM_CYSCREEN) / 2,

NULL, NULL, hInstance, lpszCmdLine) ;

ShowWindow (hwnd, nCmdShow) ;

UpdateWindow (hwnd) ;

hAccel = LoadAccelerators (hInstance, szAppName) ;

while (GetMessage (&msg, NULL, 0, 0))

{

if (!TranslateAccelerator (hwnd, hAccel, &msg))

{

TranslateMessage (&msg) ;

DispatchMessage (&msg) ;

}

}

return msg.wParam ;

}

BOOL FAR PASCAL AboutDlgProc (HWND hDlg, WORD message, WORD wParam, LONG lParam)

{

switch (message)

{

case WM_INITDIALOG :

return TRUE ;

case WM_COMMAND :

switch (wParam)

{

case IDOK :

EndDialog (hDlg, 0) ;

return TRUE ;

}

break ;

}

return FALSE ;

}

void DoCaption (HWND hwnd, char *szFileName)

{

char szCaption [40] ;

wsprintf (szCaption, "%s - %s", (LPSTR) szAppName,

(LPSTR) (szFileName [0] ? szFileName : szUntitled)) ;

SetWindowText (hwnd, szCaption) ;

}

short AskAboutSave (HWND hwnd, char *szFileName)

{

char szBuffer [40] ;

short nReturn ;

wsprintf (szBuffer, "Save current changes: %s",

(LPSTR) (szFileName [0] ? szFileName : szUntitled)) ;

if (IDYES == (nReturn = MessageBox (hwnd, szBuffer, szAppName,

MB_YESNOCANCEL | MB_ICONQUESTION)))

if (!SendMessage (hwnd, WM_COMMAND, IDM_SAVE, 0L))

return IDCANCEL ;

return nReturn ;

}

long FAR PASCAL WndProc (HWND hwnd, WORD message, WORD wParam, LONG lParam)

{

static BOOL bNeedSave = FALSE ;

static char szRealFileName [16] ;

static FARPROC lpfnAboutDlgProc ;

static HANDLE hInst ;

static HWND hwndEdit ;

char szFileName [16] ;

LONG lSelect ;

OFSTRUCT of ;

WORD wEnable ;

switch (message)

{

case WM_CREATE :

hInst = ((LPCREATESTRUCT) lParam)->hInstance ;

lpfnAboutDlgProc = MakeProcInstance (AboutDlgProc, hInst) ;

hwndEdit = CreateWindow ("edit", NULL,

WS_CHILD | WS_VISIBLE | WS_HSCROLL | WS_VSCROLL |

WS_BORDER | ES_LEFT | ES_MULTILINE |

ES_AUTOHSCROLL | ES_AUTOVSCROLL,

0, 0, 0, 0,

hwnd, EDITID, hInst, NULL) ;

SendMessage (hwndEdit, EM_LIMITTEXT, 32000, 0L) ;

if (lstrlen (((LPCREATESTRUCT) lParam)->lpCreateParams))

{

OpenFile (((LPCREATESTRUCT) lParam)->lpCreateParams,

&of, OF_PARSE) ;

lstrcpy (szFileName,

AnsiNext (lstrrchr (of.szPathName, '\\'))) ;

if (ReadFile (hInst, hwnd, hwndEdit, &of,

szFileName, FALSE))

lstrcpy (szRealFileName, szFileName) ;

}

DoCaption (hwnd, szRealFileName) ;

return 0 ;

case WM_SETFOCUS :

SetFocus (hwndEdit) ;

return 0 ;

case WM_SIZE :

MoveWindow (hwndEdit, 0, 0, LOWORD (lParam),

HIWORD (lParam), TRUE) ;

return 0 ;

case WM_INITMENUPOPUP :

if (lParam == 1)

{

EnableMenuItem (wParam, IDM_UNDO,

SendMessage (hwndEdit, EM_CANUNDO, 0, 0L) ?

MF_ENABLED : MF_GRAYED) ;

EnableMenuItem (wParam, IDM_PASTE,

IsClipboardFormatAvailable (CF_TEXT) ?

MF_ENABLED : MF_GRAYED) ;

lSelect = SendMessage (hwndEdit, EM_GETSEL, 0, 0L) ;

if (HIWORD (lSelect) == LOWORD (lSelect))

wEnable = MF_GRAYED ;

else

wEnable = MF_ENABLED ;

EnableMenuItem (wParam, IDM_CUT, wEnable) ;

EnableMenuItem (wParam, IDM_COPY, wEnable) ;

EnableMenuItem (wParam, IDM_CLEAR, wEnable) ;

}

return 0 ;

case WM_COMMAND :

if (LOWORD (lParam) && wParam == EDITID)

{

switch (HIWORD (lParam))

{

case EN_UPDATE :

bNeedSave = TRUE ;

return 0 ;

case EN_ERRSPACE :

MessageBox (hwnd, "Edit control out of space.",

szAppName, MB_OK | MB_ICONSTOP) ;

return 0 ;

}

break ;

}

switch (wParam)

{

case IDM_NEW :

if (bNeedSave && IDCANCEL ==

AskAboutSave (hwnd, szRealFileName))

return 0 ;

SetWindowText (hwndEdit, "\0") ;

szRealFileName [0] = '\0' ;

DoCaption (hwnd, szRealFileName) ;

bNeedSave = FALSE ;

return 0 ;

case IDM_OPEN :

if (bNeedSave && IDCANCEL ==

AskAboutSave (hwnd, szRealFileName))

return 0 ;

if (ReadFile (hInst, hwnd, hwndEdit, &of,

szFileName, TRUE))

{

lstrcpy (szRealFileName, szFileName) ;

DoCaption (hwnd, szRealFileName) ;

bNeedSave = FALSE ;

}

return 0 ;

case IDM_SAVE :

if (szRealFileName [0])

{

if (WriteFile (hInst, hwnd, hwndEdit, &of,

szRealFileName, FALSE))

{

bNeedSave = FALSE ;

return 1 ;

}

return 0 ;

}

// fall through

case IDM_SAVEAS :

if (WriteFile (hInst, hwnd, hwndEdit, &of,

szFileName, TRUE))

{

lstrcpy (szRealFileName, szFileName) ;

DoCaption (hwnd, szFileName) ;

bNeedSave = FALSE ;

return 1 ;

}

return 0 ;

case IDM_PRINT :

PrintFile (hInst, hwnd, hwndEdit,

szRealFileName [0] ? szRealFileName :

szUntitled) ;

return 0 ;

case IDM_EXIT :

SendMessage (hwnd, WM_CLOSE, 0, 0L) ;

return 0 ;

case IDM_ABOUT :

DialogBox (hInst, "AboutBox", hwnd,

lpfnAboutDlgProc) ;

return 0 ;

case IDM_UNDO :

SendMessage (hwndEdit, WM_UNDO, 0, 0L) ;

return 0 ;

case IDM_CUT :

SendMessage (hwndEdit, WM_CUT, 0, 0L) ;

return 0 ;

case IDM_COPY :

SendMessage (hwndEdit, WM_COPY, 0, 0L) ;

return 0 ;

case IDM_PASTE :

SendMessage (hwndEdit, WM_PASTE, 0, 0L) ;

return 0 ;

case IDM_CLEAR :

SendMessage (hwndEdit, WM_CLEAR, 0, 0L) ;

return 0 ;

case IDM_SELALL :

SendMessage (hwndEdit, EM_SETSEL, 0,

MAKELONG (0, 32767)) ;

return 0 ;

}

break ;

case WM_CLOSE :

if (!bNeedSave || IDCANCEL !=

AskAboutSave (hwnd, szRealFileName))

DestroyWindow (hwnd) ;

return 0 ;

case WM_QUERYENDSESSION :

if (!bNeedSave || IDCANCEL !=

AskAboutSave (hwnd, szRealFileName))

return 1L ;

return 0 ;

case WM_DESTROY :

PostQuitMessage (0) ;

return 0 ;

}

return DefWindowProc (hwnd, message, wParam, lParam) ;

}

POPPADF.C

/*-----------------------------------

POPPADF -- Popup Notepad File I/O

-----------------------------------*/

#include <windows.h>

// in FILEDLG.C

int DoFileOpenDlg (HANDLE, WORD, char *, char *, WORD, char *, POFSTRUCT) ;

int DoFileSaveDlg (HANDLE, WORD, char *, char *, WORD *, char *, POFSTRUCT) ;

extern char szAppName [] ; // in POPPAD.C

extern char szFileSpec [] ;

long FileLength (HANDLE hFile)

{

long lCurrentPos = _llseek (hFile, 0L, 1) ;

long lFileLength = _llseek (hFile, 0L, 2) ;

_llseek (hFile, lCurrentPos, 0) ;

return lFileLength ;

}

void OkMessageBox (HWND hwnd, char *szString, char *szFileName)

{

char szBuffer [40] ;

wsprintf (szBuffer, szString, (LPSTR) szFileName) ;

MessageBox (hwnd, szBuffer, szAppName, MB_OK | MB_ICONEXCLAMATION) ;

}

BOOL ReadFile (HANDLE hInstance, HWND hwnd, HWND hwndEdit, POFSTRUCT pof,

char *szFileName, BOOL bAskName)

{

DWORD dwLength ;

HANDLE hFile, hTextBuffer ;

LPSTR lpTextBuffer ;

if (bAskName)

{

if (!DoFileOpenDlg (hInstance, hwnd, szFileSpec, szFileSpec + 1,

0x4010, szFileName, pof))

return FALSE ;

}

if (-1 == (hFile = OpenFile (szFileName, pof, OF_READ | OF_REOPEN)))

{

OkMessageBox (hwnd, "Cannot open file %s", szFileName) ;

return FALSE ;

}

if ((dwLength = FileLength (hFile)) >= 32000)

{

_lclose (hFile) ;

OkMessageBox (hwnd, "File %s too large", szFileName) ;

return FALSE ;

}

if (NULL == (hTextBuffer = GlobalAlloc (GHND, (DWORD) dwLength + 1)))

{

_lclose (hFile) ;

OkMessageBox (hwnd, "Cannot allocate memory for %s", szFileName) ;

return FALSE ;

}

lpTextBuffer = GlobalLock (hTextBuffer) ;

_lread (hFile, lpTextBuffer, (WORD) dwLength) ;

_lclose (hFile) ;

lpTextBuffer [(WORD) dwLength] = '\0' ;

SetWindowText (hwndEdit, lpTextBuffer) ;

GlobalUnlock (hTextBuffer) ;

GlobalFree (hTextBuffer) ;

return TRUE ;

}

BOOL WriteFile (HANDLE hInstance, HWND hwnd, HWND hwndEdit, POFSTRUCT pof,

char *szFileName, BOOL bAskName)

{

char szBuffer [40] ;

HANDLE hFile, hTextBuffer ;

NPSTR npTextBuffer ;

WORD wStatus, wLength ;

if (bAskName)

{

if (!DoFileSaveDlg (hInstance, hwnd, szFileSpec, szFileSpec + 1,

&wStatus, szFileName, pof))

return FALSE ;

if (wStatus == 1)

{

wsprintf (szBuffer, "Replace existing %s", (LPSTR) szFileName) ;

if (IDNO == MessageBox (hwnd, szBuffer, szAppName,

MB_YESNO | MB_ICONQUESTION))

return FALSE ;

}

}

else

OpenFile (szFileName, pof, OF_PARSE) ;

if (-1 == (hFile = OpenFile (szFileName, pof, OF_CREATE | OF_REOPEN)))

{

OkMessageBox (hwnd, "Cannot create file %s", szFileName) ;

return FALSE ;

}

wLength = GetWindowTextLength (hwndEdit) ;

hTextBuffer = (HANDLE) SendMessage (hwndEdit, EM_GETHANDLE, 0, 0L) ;

npTextBuffer = LocalLock (hTextBuffer) ;

if (wLength != _lwrite (hFile, npTextBuffer, wLength))

{

_lclose (hFile) ;

OkMessageBox (hwnd, "Cannot write file %s to disk", szFileName) ;

return FALSE ;

}

_lclose (hFile) ;

LocalUnlock (hTextBuffer) ;

return TRUE ;

}

POPPADP0.C

/*---------------------------------------------------------

POPPADP0.C -- Popup Notepad Printing -- dummy functions

---------------------------------------------------------*/

#include <windows.h>

extern char szAppName [] ; // in POPPAD.C

BOOL FAR PASCAL PrintDlgProc (HWND hDlg, WORD message,

WORD wParam, LONG lParam)

{

return FALSE ;

}

BOOL FAR PASCAL AbortProc (HDC hPrinterDC, short nCode)

{

return FALSE ;

}

BOOL PrintFile (HANDLE hInstance, HWND hwnd, HWND hwndEdit, char *szFileName)

{

MessageBox (hwnd, "Printing not yet implemented", szAppName,

MB_OK | MB_ICONEXCLAMATION) ;

return FALSE ;

}

POPPAD.RC

/*---------------------------

POPPAD.RC resource script

---------------------------*/

#include <windows.h>

#include "poppad.h"

#include "filedlg.h"

PopPad ICON "poppad.ico"

PopPad MENU

{

POPUP "&File"

{

MENUITEM "&New", IDM_NEW

MENUITEM "&Open...", IDM_OPEN

MENUITEM "&Save", IDM_SAVE

MENUITEM "Save &As...", IDM_SAVEAS

MENUITEM SEPARATOR

MENUITEM "&Print...", IDM_PRINT

MENUITEM SEPARATOR

MENUITEM "E&xit", IDM_EXIT

}

POPUP "&Edit"

{

MENUITEM "&Undo\tAlt+BkSp", IDM_UNDO

MENUITEM SEPARATOR

MENUITEM "Cu&t\tShift+Del", IDM_CUT

MENUITEM "&Copy\tCtrl+Ins", IDM_COPY

MENUITEM "&Paste\tShift+Ins", IDM_PASTE

MENUITEM "C&lear\tDel", IDM_CLEAR

MENUITEM SEPARATOR

MENUITEM "&Select All", IDM_SELALL

}

POPUP "&Help"

{

MENUITEM "&About PopPad...", IDM_ABOUT

}

}

PopPad ACCELERATORS

{

VK_DELETE, IDM_CUT, VIRTKEY, SHIFT

VK_INSERT, IDM_COPY, VIRTKEY, CONTROL

VK_INSERT, IDM_PASTE, VIRTKEY, SHIFT

VK_DELETE, IDM_CLEAR, VIRTKEY

}

AboutBox DIALOG 20, 20, 160, 80

STYLE WS_POPUP | WS_DLGFRAME

{

CTEXT "PopPad" -1, 0, 12, 160, 8

ICON "PopPad" -1, 8, 8, 0, 0

CTEXT "Popup Editor for Microsoft Windows" -1, 0, 36, 160, 8

CTEXT "Copyright (c) Charles Petzold, 1990" -1, 0, 48, 160, 8

DEFPUSHBUTTON "OK" IDOK, 64, 60, 32, 14, WS_GROUP

}

PrintDlgBox DIALOG 20, 20, 100, 76

STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE

CAPTION "PopPad"

{

CTEXT "Sending", -1, 0, 10, 100, 8

CTEXT "", IDD_FNAME, 0, 20, 100, 8

CTEXT "to print spooler.", -1, 0, 30, 100, 8

DEFPUSHBUTTON "Cancel", IDCANCEL, 34, 50, 32, 14, WS_GROUP

}

rcinclude filedlg.dlg

POPPAD.H

/*----------------------

POPPAD.H header file

----------------------*/

#define IDM_NEW 1

#define IDM_OPEN 2

#define IDM_SAVE 3

#define IDM_SAVEAS 4

#define IDM_PRINT 5

#define IDM_EXIT 6

#define IDM_ABOUT 7

#define IDM_UNDO 8

#define IDM_CUT 9

#define IDM_COPY 10

#define IDM_PASTE 11

#define IDM_CLEAR 12

#define IDM_SELALL 13

POPPAD.ICO

FIG 1008.epb

POPPAD3.DEF

;------------------------------------

; POPPAD3.DEF module definition file

;------------------------------------

NAME POPPAD3

DESCRIPTION 'Popup Editor Version 3 (c) Charles Petzold, 1990'

EXETYPE WINDOWS

STUB 'WINSTUB.EXE'

CODE PRELOAD MOVEABLE DISCARDABLE

DATA PRELOAD MOVEABLE MULTIPLE

HEAPSIZE 1024

STACKSIZE 8192

EXPORTS WndProc

AboutDlgProc

FileOpenDlgProc

FileSaveDlgProc

PrintDlgProc

AbortProc

As you'll recall, the POPPAD series of programs uses a multiline edit control to do all the editing. The POPPADF.C file serves as an intermediary between the main POPPAD.C program and the functions in FILEDLG.C. The ReadFile function in POPPADF.C calls DoFileOpenDlgProc and reads the file into a global memory block. ReadFile is responsible for reporting if the file is too large or if memory can't be allocated for the file. When the file is read into a global memory block, it's transferred to the edit window using SetWindowText.

WriteFile calls DoFileSaveDlgProc to obtain the name of the file. This function is responsible for asking the user if it's acceptable to replace an existing file by that name. WriteFile obtains the handle to the edit control's buffer, locks it, and writes the file to the disk directly from that buffer.

This doesn't leave the POPPAD.C module much to do in the way of file I/O. But note these facts:

In WinMain, the lpszCmdLine address is used as the last field of the CreateWindow call. This string might contain a filename that was entered as a parameter to POPPAD3 when the program was executed. During processing of the WM_CREATE message in WndProc, this filename is passed to ReadFile with the bAskName parameter set to FALSE so that ReadFile won't call DoFileOpenDlgProc.

Much of the new logic in WndProc involves keeping track of changes to the text in the edit control. Whenever this text changes, the control sends an EN_UPDATE notification message to WndProc, which then sets bNeedSave to TRUE. When the user wants to open a new file or end the program, WndProc must check the bNeedSave variable. If it is TRUE, then the program calls AskAboutSave, which displays a message box that asks whether the user wants to save the current changes. When a file is saved, bNeedSave is set to FALSE.

POPPAD3's caption displays the name of the currently loaded file. If no filename is available (for instance, when the program is first executed), then the DoCaption function in POPPAD3 causes the character string ”(untitled)“ to be displayed.

We are not yet finished with the POPPAD programs. In Chapter 15 some of these files (POPPAD.C, POPPADF.C, POPPAD.RC, POPPAD.H, and POPPAD.ICO) will be used in the final POPPAD program. At that time a new file (POPPADP.C) will be substituted for POPPADP0.C to add logic to print files.