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.