EMFVIEW MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "&Open\tCtrl+O", IDM_FILE_OPEN MENUITEM "Save &As...", IDM_FILE_SAVE_AS MENUITEM "Cu&t\tCtrl+X", IDM_EDIT_CUT MENUITEM "
Trang 1// Show the File Save dialog box
// Save the EMF to disk file
SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
MessageBox (hwnd, TEXT ("Cannot save metafile"),
szAppName, MB_ICONEXCLAMATION | MB_OK) ;
return 0 ;
case IDM_FILE_PRINT:
// Show the Print dialog box and get printer DC
printdlg.Flags = PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION ;
MessageBox (hwnd, TEXT ("Cannot obtain printer DC"),
szAppName, MB_ICONEXCLAMATION | MB_OK) ;
// Play the EMF to the printer
SetCursor (LoadCursor (NULL, IDC_WAIT)) ;
Trang 2ShowCursor (FALSE) ;
SetCursor (LoadCursor (NULL, IDC_ARROW)) ;
DeleteDC (hdcPrn) ;
if (!bSuccess)
MessageBox (hwnd, TEXT ("Could not print metafile"),
szAppName, MB_ICONEXCLAMATION | MB_OK) ;
return 0 ;
case IDM_FILE_PROPERTIES:
if (!hemf)
return 0 ;
iLength = GetEnhMetaFileDescription (hemf, 0, NULL) ;
pBuffer = malloc ((iLength + 256) * sizeof (TCHAR)) ;
GetEnhMetaFileHeader (hemf, sizeof (ENHMETAHEADER), &header) ; // Format header file information
TEXT ("Resolution = (%i, %i) pixels")
TEXT (" = (%i, %i) mms\n"),
header.szlDevice.cx, header.szlDevice.cy,
header.szlMillimeters.cx,
header.szlMillimeters.cy) ;
i += wsprintf (pBuffer + i,
TEXT ("Size = %i, Records = %i, ")
TEXT ("Handles = %i, Palette entries = %i\n"), header.nBytes, header.nRecords,
header.nHandles, header.nPalEntries) ;
// Include the metafile description, if present
if (iLength)
{
i += wsprintf (pBuffer + i, TEXT ("Description = ")) ;
GetEnhMetaFileDescription (hemf, iLength, pBuffer + i) ; pBuffer [lstrlen (pBuffer)] = `\t' ;
// Transfer metafile copy to the clipboard
hemfCopy = CopyEnhMetaFile (hemf, NULL) ;
Trang 3SetClipboardData (CF_ENHMETAFILE, hemfCopy) ;
hemf = CopyEnhMetaFile (hemfCopy, NULL) ;
InvalidateRect (hwnd, NULL, TRUE) ;
return 0 ;
case IDM_APP_ABOUT:
MessageBox (hwnd, TEXT ("Enhanced Metafile Viewer\n") TEXT ("(c) Charles Petzold, 1998"), szAppName, MB_OK) ;
Trang 5EMFVIEW MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "&Open\tCtrl+O", IDM_FILE_OPEN
MENUITEM "Save &As ", IDM_FILE_SAVE_AS
MENUITEM "Cu&t\tCtrl+X", IDM_EDIT_CUT
MENUITEM "&Copy\tCtrl+C", IDM_EDIT_COPY
MENUITEM "&Paste\tCtrl+V", IDM_EDIT_PASTE
MENUITEM "&Delete\tDel", IDM_EDIT_DELETE
EMFVIEW ACCELERATORS DISCARDABLE
BEGIN
"C", IDM_EDIT_COPY, VIRTKEY, CONTROL, NOINVERT
"O", IDM_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT
"P", IDM_FILE_PRINT, VIRTKEY, CONTROL, NOINVERT
"V", IDM_EDIT_PASTE, VIRTKEY, CONTROL, NOINVERT
VK_DELETE, IDM_EDIT_DELETE, VIRTKEY, NOINVERT
"X", IDM_EDIT_CUT, VIRTKEY, CONTROL, NOINVERT
END
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 6RESOURCE.H (excerpts)
// Microsoft Developer Studio generated include file.
// Used by EmfView.rc
#define IDM_FILE_OPEN 40001
#define IDM_FILE_SAVE_AS 40002
#define IDM_FILE_PRINT 40003
#define IDM_FILE_PROPERTIES 40004
#define IDM_APP_EXIT 40005
#define IDM_EDIT_CUT 40006
#define IDM_EDIT_COPY 40007
#define IDM_EDIT_PASTE 40008
#define IDM_EDIT_DELETE 40009
#define IDM_APP_ABOUT 40010
EMFVIEW also has complete palette logic, just in case a palette has been encoded in the metafile (The way it gets
in there is by a call to SelectPalette.) The program extracts the palette in its CreatePaletteFromMetaFile function,
which is called when it displays a metafile during WM_PAINT and also while processing the
WM_QUERYNEWPALETTE and WM_PALETTECHANGED messages
In response to a Print command from the menu, EMFVIEW displays the common printer dialog box and then obtains the dimensions of the printable area of the page The metafile is stretched to fill that whole area EMFVIEW displays a metafile in its window similarly
The Properties item from the File menu causes EMFVIEW to display a message box containing information from the metafile header
If you print the EMF2.EMF metafile image created earlier in this chapter, you may find that the lines are very thin on high-resolution printers, perhaps nearly invisible Vector images should really have wider pens (for example, 1-point wide) for printing The ruler image shown later in this chapter does that
Displaying Accurate Metafile Images
The great thing about metafile images is that they can be stretched to any size and still maintain reasonable fidelity This is because a metafile normally consists of a series of vector graphics primitives, such as lines, filled areas, and outline fonts Enlarging or compressing the image simply involves scaling all the coordinate points that define these primitives Bitmaps, on the other hand, can lose vital information when compression results in dropping entire rows and columns of pixels
Of course, metafile compression in real life is not entirely flawless either We live with graphical output devices that have a finite pixel size A metafile image consisting of lots of lines could start to look like an indecipherable blob when compressed in size Also, area-filling patterns and color dithering start to look odd at small sizes And, if the metafile contains embedded bitmaps or old-fashioned raster fonts, these too can pose familiar problems
For the most part, though, metafiles are freely scaleable This is most useful when dropping a metafile into a word processing or desktop publishing document Generally, when you select a metafile image in such an application, you'll
be presented with a bounding rectangle that you can grab with the mouse and scale to any size The image will also have the same relative size when rendered on a printer
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 7Sometimes, however, arbitrarily scaling a metafile is not such a hot idea An example: Suppose you have a bankingsystem that stores facsimiles of account-holders' signatures as a series of polylines stored in a metafile Widening orheightening this metafile would make the signature look different At the very least, you should keep the image'saspect ratio constant
In the sample programs shown previously, we've based the bounding rectangle in the PlayEnhMetaFile call on the
size of the client area Thus, as you resize the program's window, you effectively resize the image This is conceptuallysimilar to resizing a metafile image within a word-processing document
Accurately displaying a metafile image either in specific metrical sizes or with a proper aspect ratio requires using sizeinformation in the metafile header and setting the rectangle structure accordingly
The sample programs in the remainder of this chapter will use a shell program called EMF.C that includes printinglogic, a resource script named EMF.RC, and a RESOURCE.H header file Figure 18-15 shows these files alongwith EMF8.C, a program that uses these files to display a 6-inch ruler
Figure 18-15 The EMF8 program
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 8TCHAR szClass [] = TEXT ("EMF8") ;
TCHAR szTitle [] = TEXT ("EMF8: Enhanced Metafile Demo #8") ;
void DrawRuler (HDC hdc, int cx, int cy)
// Black pen with 1-point width
SelectObject (hdc, CreatePen (PS_SOLID, cx / 72 / 6, 0)) ;
// Rectangle surrounding entire pen (with adjustment)
Rectangle (hdc, iAdj, iAdj, cx + iAdj + 1, cy + iAdj + 1) ;
else iHeight = cy / 12 ; // sixteenths MoveToEx (hdc, i * cx / 96, cy, NULL) ;
LineTo (hdc, i * cx / 96, cy - iHeight) ;
}
// Create logical font
FillMemory (&lf, sizeof (lf), 0) ;
lf.lfHeight = cy / 2 ;
lstrcpy (lf.lfFaceName, TEXT ("Times New Roman")) ;
SelectObject (hdc, CreateFontIndirect (&lf)) ;
SetTextAlign (hdc, TA_BOTTOM | TA_CENTER) ;
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 9void CreateRoutine (HWND hwnd)
{
HDC hdcEMF ;
HENHMETAFILE hemf ;
int cxMms, cyMms, cxPix, cyPix, xDpi, yDpi ;
hdcEMF = CreateEnhMetaFile (NULL, TEXT ("emf8.emf"), NULL,
TEXT ("EMF8\0EMF Demo #8\0")) ;
if (hdcEMF == NULL)
return ;
cxMms = GetDeviceCaps (hdcEMF, HORZSIZE) ;
cyMms = GetDeviceCaps (hdcEMF, VERTSIZE) ;
cxPix = GetDeviceCaps (hdcEMF, HORZRES) ;
cyPix = GetDeviceCaps (hdcEMF, VERTRES) ;
xDpi = cxPix * 254 / cxMms / 10 ;
yDpi = cyPix * 254 / cyMms / 10 ;
DrawRuler (hdcEMF, 6 * xDpi, yDpi) ;
hemf = CloseEnhMetaFile (hdcEMF) ;
hemf = GetEnhMetaFile (TEXT ("emf8.emf")) ;
GetEnhMetaFileHeader (hemf, sizeof (emh), &emh) ;
cxImage = emh.rclBounds.right - emh.rclBounds.left ;
cyImage = emh.rclBounds.bottom - emh.rclBounds.top ;
rect.left = (cxArea - cxImage) / 2 ;
rect.right = (cxArea + cxImage) / 2 ;
rect.top = (cyArea - cyImage) / 2 ;
rect.bottom = (cyArea + cyImage) / 2 ;
PlayEnhMetaFile (hdc, hemf, &rect) ;
DeleteEnhMetaFile (hemf) ;
}
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 10extern void CreateRoutine (HWND) ;
extern void PaintRoutine (HWND, HDC, int, int) ;
LRESULT CALLBACK WndProc (HWND, UINT, WPARAM, LPARAM) ;
HANDLE hInst ;
extern TCHAR szClass [] ;
extern TCHAR szTitle [] ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = GetStockObject (WHITE_BRUSH) ;
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 11BOOL PrintRoutine (HWND hwnd)
{
static DOCINFO di ;
static PRINTDLG printdlg = { sizeof (PRINTDLG) } ;
static TCHAR szMessage [32] ;
BOOL bSuccess = FALSE ;
HDC hdcPrn ;
int cxPage, cyPage ;
printdlg.Flags = PD_RETURNDC | PD_NOPAGENUMS | PD_NOSELECTION ;
if (!PrintDlg (&printdlg))
return TRUE ;
if (NULL == (hdcPrn = printdlg.hDC))
return FALSE ;
cxPage = GetDeviceCaps (hdcPrn, HORZRES) ;
cyPage = GetDeviceCaps (hdcPrn, VERTRES) ;
lstrcpy (szMessage, szClass) ;
lstrcat (szMessage, TEXT (": Printing")) ;
di.cbSize = sizeof (DOCINFO) ;
Trang 12cxClient = LOWORD (lParam) ;
cyClient = HIWORD (lParam) ;
Trang 13Because modern printers often have a much higher resolution than video displays, the ability to print a metafile is animportant tool for testing our ability to render an image in a specific size The EMF8 program creates a metafile imagethat makes most sense when displayed in a specific size The image is that of a ruler 6 inches wide by 1 inch high,complete with tick marks every 16th inch and the numbers 1 through 5 in a TrueType font.
To draw a 6-inch ruler, we need to know something about device resolution The CreateRoutine function in
EMF8.C begins by creating a metafile and calling GetDeviceCaps four times using the device context handle
returned from CreateEnhMetaFile These calls obtain the width and height of the display surface in both millimeters
and pixels
This may sound a bit odd The metafile device context is usually seen as a storage medium for GDI drawing
commands It's not a real device like a video display or a printer, so how can it have a width and height?
Well, as you may recall, the first argument to CreateEnhMetaFile is known as the "reference device context." GDI
uses this to establish device characteristics for the metafile If the argument is set to NULL (as in EMF8), GDI uses
the video display as the reference device context Thus, when EMF8 calls GetDeviceCaps using the metafile device
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 14context, it actually obtains information about the video display
EMF8.C calculates a resolution in dots per inch by dividing the pixel dimension by the millimeter dimension andmultiplying by 25.4, the number of millimeters in an inch
Even though we've taken great care to draw this metafile ruler in its correct size, the work is not yet done When it
comes time to render the image, the PlayEnhMetaFile function will display it stretched to the rectangle passed as its
last argument This rectangle must be set to the size of the ruler
For this reason, the PaintRoutine function in EMF8 calls the GetEnhMetaFileHeader function to obtain the header information in the metafile The rclBounds field of the ENHMETAHEADER structure indicates the bounding
rectangle of the metafile image in pixels The program uses this information to center the ruler in the client area, asshown in Figure 18-16
Figure 18-16 The EMF8 display
Keep in mind that if you hold a ruler up to the screen, you probably won't match exactly The video display onlyapproximates actual metrics, as I discussed in Chapter 5
This technique appears to have worked, but now try printing the image Oops! If you have a 300-dpi laser printer,the ruler will be about 11/3 inches wide That's because we've used a pixel dimension based on the video display.Although you may think the little printed ruler looks kind of cute, it's not what we want Let's try again
The ENHMETAHEADER structure contains two rectangle structures that describe the size of the image The first,
which EMF8 uses, is the rclBounds field This gives the size of the image in pixels The second is the rclFrame field,
which gives the size of the image in units of 0.01 millimeters The relationship between these two fields is governed bythe reference device context originally used when creating the metafile, in this case the video display (The metafile
header also contains two fields named szlDevice and szlMillimeters, which are SIZEL structures that indicate the size of the reference device in pixels and millimeters, the same information available from GetDeviceCaps.)
The information about the millimeter dimensions of the image is put to use by EMF9, shown in Figure 18-17
Figure 18-17 The EMF9 program
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 15TCHAR szClass [] = TEXT ("EMF9") ;
TCHAR szTitle [] = TEXT ("EMF9: Enhanced Metafile Demo #9") ;
cyMms = GetDeviceCaps (hdc, VERTSIZE) ;
cxPix = GetDeviceCaps (hdc, HORZRES) ;
cyPix = GetDeviceCaps (hdc, VERTRES) ;
hemf = GetEnhMetaFile (TEXT (" \\emf8\\emf8.emf")) ;
GetEnhMetaFileHeader (hemf, sizeof (emh), &emh) ;
cxImage = emh.rclFrame.right - emh.rclFrame.left ;
cyImage = emh.rclFrame.bottom - emh.rclFrame.top ;
cxImage = cxImage * cxPix / cxMms / 100 ;
cyImage = cyImage * cyPix / cyMms / 100 ;
rect.left = (cxArea - cxImage) / 2 ;
rect.right = (cxArea + cxImage) / 2 ;
rect.top = (cyArea - cyImage) / 2 ;
rect.bottom = (cyArea + cyImage) / 2 ;
PlayEnhMetaFile (hdc, hemf, &rect) ;
DeleteEnhMetaFile (hemf) ;
}
EMF9 uses the metafile created by EMF8, so be sure to run EMF8 before running this program
The PaintRoutine function in EMF9 begins by calling GetDeviceCaps four times using the destination device context As in the CreateRoutine function in EMF8, these calls provide information about the resolution of the device After getting the metafile handle, it obtains the header structure and uses the rclFrame field to calculate the
size of the metafile image in units of 0.01 millimeters That's the first step
The function then converts this dimension to pixels by multiplying by the pixel dimension of the output device, dividing
by the millimeter dimension, and then dividing by 100 to account for the metrical dimension in 0.01 millimeters The
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 16PaintRoutine function now has the dimensions of the ruler in pixels but not specific to the video display This is a
pixel dimension appropriate for the destination device From there on, it's easy to center the image
As far as the screen goes, the EMF9 display looks the same as the EMF8 display But if you print the ruler fromEMF9, you'll see something that looks much more normal a ruler 6 inches wide by 1 inch high
Scaling and Aspect Ratios
There may be times when you want to use the ruler metafile created by EMF8 but without necessarily displaying the6-inch image Still, it might be nice to maintain the correct 6-to-1 aspect ratio of the image As I mentioned before,using a bounding box to size a metafile in a word-processing program (or whatever) may be convenient, but it couldresult in certain undesirable distortions In such applications, users should be given an option to keep the originalaspect ratio regardless of how the bounding box is sized That is, the bounding box selected by the user would not be
used directly to define the rectangle structure passed to the PlayEnhMetaFile The rectangle structure passed to that
function would be only part of the bounding box
Let's examine how to do this in the EMF10 program shown in Figure 18-18
Figure 18-18 The EMF10 program
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 17TCHAR szClass [] = TEXT ("EMF10") ;
TCHAR szTitle [] = TEXT ("EMF10: Enhanced Metafile Demo #10") ;
cyMms = GetDeviceCaps (hdc, VERTSIZE) ;
cxPix = GetDeviceCaps (hdc, HORZRES) ;
cyPix = GetDeviceCaps (hdc, VERTRES) ;
hemf = GetEnhMetaFile (TEXT (" \\emf8\\emf8.emf")) ;
GetEnhMetaFileHeader (hemf, sizeof (emh), &emh) ;
cxImage = emh.rclFrame.right - emh.rclFrame.left ;
cyImage = emh.rclFrame.bottom - emh.rclFrame.top ;
cxImage = cxImage * cxPix / cxMms / 100 ;
cyImage = cyImage * cyPix / cyMms / 100 ;
fScale = min ((float) cxArea / cxImage, (float) cyArea / cyImage) ;
cxImage = (int) (fScale * cxImage) ;
cyImage = (int) (fScale * cyImage) ;
rect.left = (cxArea - cxImage) / 2 ;
rect.right = (cxArea + cxImage) / 2 ;
rect.top = (cyArea - cyImage) / 2 ;
rect.bottom = (cyArea + cyImage) / 2 ;
PlayEnhMetaFile (hdc, hemf, &rect) ;
DeleteEnhMetaFile (hemf) ;
}
EMF10 stretches the ruler image to fit the client area (or the printable area of the printer page) but without otherwisedistorting it Usually you'll see the ruler stretching the full width of the client area but centered between the top andbottom If you make the window somewhat stout, the ruler will be as tall as the client area but centered horizontally.There are probably numerous ways of calculating the proper display rectangle, but I decided to build upon the code
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 18in EMF9 The PaintRoutine function in EMF10.C begins like EMF9.C, by calculating the pixel size of the
6-inch-wide image appropriate for the destination device context
The program then calculates a floating point value, named fScale, that is the minimum of the ratio of the width of the
client area to the width of the image, and the ratio of the height of the client area to the height of the image This factor
is then used to increase the pixel dimensions of the image before the bounding rectangle is calculated
Mapping Modes in Metafiles
We've been drawing a ruler that displays inches, and we've also been dealing with dimensions in units of millimeters.Such jobs might seem like good candidates for using the various mapping modes provided under GDI Yet I'veinsisted on using pixels and doing all the necessary calculations "manually." Why is that?
The simple answer is that the use of mapping modes in connection with metafiles can be quite confusing But let's try
it out to see
When you call SetMapMode using a metafile device context, the function is encoded in the metafile just like any other
GDI function This is demonstrated in the EMF11 program shown in Figure 18-19
Figure 18-19 The EMF11 program
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 19TCHAR szClass [] = TEXT ("EMF11") ;
TCHAR szTitle [] = TEXT ("EMF11: Enhanced Metafile Demo #11") ;
void DrawRuler (HDC hdc, int cx, int cy)
{
int i, iHeight ;
LOGFONT lf ;
TCHAR ch ;
// Black pen with 1-point width
SelectObject (hdc, CreatePen (PS_SOLID, cx / 72 / 6, 0)) ;
// Rectangle surrounding entire pen (with adjustment)
if (GetVersion () & 0x80000000) // Windows 98
else iHeight = cy / 12 ; // sixteenths MoveToEx (hdc, i * cx / 96, 0, NULL) ;
LineTo (hdc, i * cx / 96, iHeight) ;
}
// Create logical font
FillMemory (&lf, sizeof (lf), 0) ;
lf.lfHeight = cy / 2 ;
lstrcpy (lf.lfFaceName, TEXT ("Times New Roman")) ;
SelectObject (hdc, CreateFontIndirect (&lf)) ;
SetTextAlign (hdc, TA_BOTTOM | TA_CENTER) ;
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 20hdcEMF = CreateEnhMetaFile (NULL, TEXT ("emf11.emf"), NULL,
TEXT ("EMF11\0EMF Demo #11\0")) ;
SetMapMode (hdcEMF, MM_LOENGLISH) ;
cyMms = GetDeviceCaps (hdc, VERTSIZE) ;
cxPix = GetDeviceCaps (hdc, HORZRES) ;
cyPix = GetDeviceCaps (hdc, VERTRES) ;
cxImage = emh.rclFrame.right - emh.rclFrame.left ;
cyImage = emh.rclFrame.bottom - emh.rclFrame.top ;
cxImage = cxImage * cxPix / cxMms / 100 ;
cyImage = cyImage * cyPix / cyMms / 100 ;
rect.left = (cxArea - cxImage) / 2 ;
rect.top = (cyArea - cyImage) / 2 ;
rect.right = (cxArea + cxImage) / 2 ;
rect.bottom = (cyArea + cyImage) / 2 ;
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 21The CreateRoutine function in EMF11 is simpler than the one in EMF8 (our original ruler-metafile program)
because it does not need to call GetDeviceCaps to determine the resolution of the video display in dots per inch Instead, EMF11 calls SetMapMode to set the mapping mode to MM_LOENGLISH, where logical units are equal
to 0.01 inches Thus, the dimensions of the ruler are 600 units by 100 units, and these numbers are passed to
DrawRuler.
The DrawRuler function in EMF11 is the same as the one in EMF9, except for the MoveToEx and LineTo calls
that draw the tick marks of the ruler When drawing in units of pixels (the default MM_TEXT mapping mode), units
on the vertical axis increase going down the screen For the MM_LOENGLISH mapping mode (and the othermetrical mapping modes), they increase going up That required a change to this code The adjustment factors in the
Rectangle function were also changed.
The PaintRoutine function in EMF11 is basically the same as the one in EMF9, which was the version of the
program that successfully displayed the ruler in its correct dimensions on both the video display and the printer Theonly difference is that EMF11 uses the EMF11.EMF file, whereas EMF9 used the EMF8.EMF file created byEMF8
The image displayed by EMF11 is basically the same as EMF9 So, we see here how embedding a SetMapMode
call into a metafile can simplify the metafile creation and doesn't affect at all the mechanics of playing the metafile in itscorrect size
Mapping and Playing
Calculating the destination rectangle in EMF11 involves some calls to GetDeviceCaps Our second goal is to
eliminate those and use a mapping mode instead GDI treats the coordinates of the destination rectangle as logicalcoordinates Using the MM_HIMETRIC mode seems like a good candidate for these coordinates, because thatmakes logical units 0.01 millimeters, the same units used for the bounding rectangle in the enhanced metafile header
The EMF12 program shown in Figure 18-20 restores the DrawRuler logic as originally presented in EMF8 but uses
the MM_HIMETRIC mapping mode to display the metafile
Figure 18-20 The EMF12 program
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 22TCHAR szClass [] = TEXT ("EMF12") ;
TCHAR szTitle [] = TEXT ("EMF12: Enhanced Metafile Demo #12") ;
void DrawRuler (HDC hdc, int cx, int cy)
{
int iAdj, i, iHeight ;
LOGFONT lf ;
TCHAR ch ;
iAdj = GetVersion () & 0x80000000 ? 0 : 1 ;
// Black pen with 1-point width
SelectObject (hdc, CreatePen (PS_SOLID, cx / 72 / 6, 0)) ;
// Rectangle surrounding entire pen (with adjustment)
Rectangle (hdc, iAdj, iAdj, cx + iAdj + 1, cy + iAdj + 1) ;
else iHeight = cy / 12 ; // sixteenths MoveToEx (hdc, i * cx / 96, cy, NULL) ;
LineTo (hdc, i * cx / 96, cy - iHeight) ;
}
// Create logical font
FillMemory (&lf, sizeof (lf), 0) ;
lf.lfHeight = cy / 2 ;
lstrcpy (lf.lfFaceName, TEXT ("Times New Roman")) ;
SelectObject (hdc, CreateFontIndirect (&lf)) ;
SetTextAlign (hdc, TA_BOTTOM | TA_CENTER) ;
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 23/ Clean up
DeleteObject (SelectObject (hdc, GetStockObject (SYSTEM_FONT))) ; DeleteObject (SelectObject (hdc, GetStockObject (BLACK_PEN))) ; }
hdcEMF = CreateEnhMetaFile (NULL, TEXT ("emf12.emf"), NULL,
TEXT ("EMF13\0EMF Demo #12\0")) ;
cxMms = GetDeviceCaps (hdcEMF, HORZSIZE) ;
cyMms = GetDeviceCaps (hdcEMF, VERTSIZE) ;
cxPix = GetDeviceCaps (hdcEMF, HORZRES) ;
cyPix = GetDeviceCaps (hdcEMF, VERTRES) ;
hemf = GetEnhMetaFile (TEXT ("emf12.emf")) ;
GetEnhMetaFileHeader (hemf, sizeof (emh), &emh) ;
cxImage = emh.rclFrame.right - emh.rclFrame.left ;
cyImage = emh.rclFrame.bottom - emh.rclFrame.top ;
Trang 24The PaintRoutine function in EMF12 first sets the mapping mode to MM_HIMETRIC As with the other metric modes, values of y increase going up the screen However, the origin is still at the upper left corner, which means that
y-coordinates within the client area are negative To correct this oddity, the program calls SetViewportOrgEx to set
the origin to the lower left corner
The device point (cxArea, 0) is at the upper right corner of the screen Passing that point to the DPtoLP ("device
point to logical point") function gives us the size of the client area in 0.01 millimeters
The program then loads the metafile, gets the header, and finds the dimensions of the metafile in 0.01 millimeters Thedestination rectangle centered in the middle of the client area is then easy to calculate
Now we've seen how we can use a mapping mode when creating the metafile and also for displaying it Can we doboth?
It turns out that it works, as EMF13 (shown in Figure 18-21) demonstrates
Figure 18-21 The EMF13 program
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 25TCHAR szClass [] = TEXT ("EMF13") ;
TCHAR szTitle [] = TEXT ("EMF13: Enhanced Metafile Demo #13") ;
cxImage = emh.rclFrame.right - emh.rclFrame.left ;
cyImage = emh.rclFrame.bottom - emh.rclFrame.top ;
Now we can establish a couple principles When the metafile is created, GDI uses any embedded changes to themapping mode to calculate the size of the metafile image in pixels and millimeters The size of the image is stored inthe metafile header When the metafile is played, GDI establishes the physical location of the destination rectangle
based on the mapping mode in effect at the time of the PlayEnhMetaFile call Nothing in the metafile can change
that location
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 26Chapter 19
The Multiple-Document Interface
The Multiple-Document Interface (MDI) is a specification for applications that handle documents in MicrosoftWindows The specification describes a window structure and user interface that allow the user to work with multipledocuments within a single application (such as text documents in a word-processing program or spreadsheets in aspreadsheet program) Simply put, just as Windows maintains multiple application windows within a single screen, anMDI application maintains multiple document windows within a single client area The first MDI application forWindows was the first Windows version of Microsoft Excel But many others soon followed
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 27MDI Concepts
Although the MDI specification has been around since Windows 2.0, at that time MDI applications were difficult towrite and required some very intricate programming work Since Windows 3.0, however, much of that work hasalready been done for you That support, with some enhancements from Windows 95, has been carried over intoWindows 98 and Microsoft Windows NT
The Elements of MDI
The main application window of an MDI program is conventional: it has a title bar, a menu, a sizing border, a systemmenu icon, and minimize/maximize/close buttons The client area, however, is often called a "workspace" and is notdirectly used to display program output This workspace contains zero or more child windows, each of which
displays a document
These child windows look much like normal application windows and much like the main application window of anMDI program They too have a title bar, a sizing border, a system menu icon, minimize/maximize/close buttons, andpossibly scroll bars None of the document windows has a menu, however The menu on the main application
window applies to the document windows
At any one time, only one document window is active (indicated by a highlighted title bar), and it appears in front ofall the other document windows All the document child windows are clipped to the workspace area and neverappear outside the application window
At first, MDI seems a fairly straightforward job for the Windows programmer All you need to do is create a
WS_CHILD window for each document, making the program's main application window the parent of the documentwindow But with a little exploration of existing MDI applications, you'll find some complications that require difficultcode
• An MDI document window can be minimized A short title bar with an icon appears at the bottom of theworkspace Generally, an MDI application will use different icons for the main application window and eachtype of document window
• An MDI document window can be maximized In this case, the title bar of the document window (normallyused to show the filename of the document in the window) disappears and the filename appears appended tothe application name in the application window's title bar The system menu icon of the document windowbecomes the first item in the top-level menu of the application window The button to close the documentwindow becomes the last item in the top-level menu and appears to the far right
• The system keyboard accelerator to close a document window is the same as that to close the main window,except that the Ctrl key is used rather than Alt That is, Alt-F4 closes the application window, while Ctrl-F4closes the document window In addition, Ctrl-F6 switches among the child document windows within theactive MDI application Alt-Spacebar invokes the system menu of the main window, as usual Alt (minus)invokes the system menu of the active child document window
• When using the cursor keys to move among items on the menu, control normally passes from the systemmenu to the first item on the menu bar In an MDI application, control passes from the application systemmenu to the active document system menu to the first item on the menu bar
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 28• If the application is capable of supporting several types of child windows (for example, the worksheet andchart documents in Microsoft Excel), the menu should reflect the operations associated with that type ofdocument This requires that the program change the menu when a different document window becomesactive In addition, when no document window exists, the menu should be stripped down to only thoseoperations involved in opening or creating a new document
• The top-level menu bar has an item called Window By convention, this is the last item on the top-level menubar except for Help The Window submenu generally has options to arrange the document windows withinthe workspace Document windows can be "cascaded" from the upper left or "tiled" so that each documentwindow is fully visible This submenu also has a list of all the document windows Selecting one moves thatdocument window to the foreground
All of these aspects of MDI are supported in Windows 98 Some overhead is required of course (as will be shown in
a sample program), but it isn't anywhere close to the amount of code you'd have to write to support all these featuresdirectly
MDI Support
Some new terminology is necessary when approaching the Windows MDI support The main application window iscalled the "frame window." Just as in a conventional Windows program, this is a window of the
WS_OVERLAPPEDWINDOW style
An MDI application also creates a "client window" based on the predefined window class MDICLIENT The client
window is created by a call to CreateWindow using this window class and the WS_CHILD style The last argument
to CreateWindow is a pointer to a small structure of type CLIENTCREATESTRUCT This client window covers
the client area of the frame window and is responsible for much of the MDI support The color of this client window
is the system color COLOR_APPWORKSPACE
The document windows, as you've probably noticed, are called "child windows." You create these windows byinitializing a structure of type MDICREATESTRUCT and sending the client window a WM_MDICREATE messagewith a pointer to this structure
The document windows are children of the client window, which in turn is a child of the frame window The
parent-child hierarchy is shown in Figure 19-1
Figure 19-1 The parent-child hierarchy of a Windows MDI application
You need a window class (and window procedure) for the frame window and for each type of child window
supported by the application You don't need a window procedure for the client window because the window class
is preregistered
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 29The MDI support of Windows 98 includes one window class, five functions, two data structures, and twelve
messages I've already mentioned the new window class, which is MDICLIENT, and the new data structures,
CLIENTCREATESTRUCT and MDICREATESTRUCT Two of the five functions replace DefWindowProc in MDI applications: rather than call DefWindowProc for all unprocessed messages, a frame window procedure calls
DefFrameProc and a child window procedure calls DefMDIChildProc Another function specific to MDI,
TranslateMDISysAccel, is used in the same way as TranslateAccelerator, which I discussed in Chapter 10 The
MDI support also includes ArrangeIconicWindows, but one of the special MDI messages makes this function
unnecessary for MDI programs
The fifth MDI function is called CreateMDIWindow This allows the child window to be created in a separate thread
of execution This function is not required in a single-threaded program, which is what I'll be demonstrating
In the sample program coming up, I'll demonstrate nine of the twelve MDI messages (The other three are not
normally required.) These messages begin with the prefix WM_MDI A frame window sends these messages to theclient window to perform an operation on a child window or to obtain information about a child window (Forexample, a frame window sends a WM_MDICREATE message to a client window to create a child window.) TheWM_MDIACTIVATE message is an exception: while a frame window can send this message to the client window
to activate one of the child windows, the client window also sends the message to the child windows being activatedand deactivated to inform them of this change
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 30A Sample MDI Implementation
The MDIDEMO program, shown in Figure 19-2, demonstrates the basics of writing an MDI application
Figure 19-2 The MDIDEMO program
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 31LRESULT CALLBACK FrameWndProc (HWND, UINT, WPARAM, LPARAM) ;
BOOL CALLBACK CloseEnumProc (HWND, LPARAM) ;
LRESULT CALLBACK HelloWndProc (HWND, UINT, WPARAM, LPARAM) ;
LRESULT CALLBACK RectWndProc (HWND, UINT, WPARAM, LPARAM) ;
// structure for storing data unique to each Hello child window typedef struct tagHELLODATA
TCHAR szAppName[] = TEXT ("MDIDemo") ;
TCHAR szFrameClass[] = TEXT ("MdiFrame") ;
TCHAR szHelloClass[] = TEXT ("MdiHelloChild") ;
TCHAR szRectClass[] = TEXT ("MdiRectChild") ;
HINSTANCE hInst ;
HMENU hMenuInit, hMenuHello, hMenuRect ;
HMENU hMenuInitWindow, hMenuHelloWindow, hMenuRectWindow ;
int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 32wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) (COLOR_APPWORKSPACE + 1) ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ;
wndclass.hIcon = LoadIcon (NULL, IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH) GetStockObject (WHITE_BRUSH) ; wndclass.lpszMenuName = NULL ;
hMenuInit = LoadMenu (hInstance, TEXT ("MdiMenuInit")) ;
hMenuHello = LoadMenu (hInstance, TEXT ("MdiMenuHello")) ;
hMenuRect = LoadMenu (hInstance, TEXT ("MdiMenuRect")) ;
hMenuInitWindow = GetSubMenu (hMenuInit, INIT_MENU_POS) ;
hMenuHelloWindow = GetSubMenu (hMenuHello, HELLO_MENU_POS) ;
hMenuRectWindow = GetSubMenu (hMenuRect, RECT_MENU_POS) ;
// Load accelerator table
hAccel = LoadAccelerators (hInstance, szAppName) ;
// Create the frame window
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 33NULL, hMenuInit, hInstance, NULL) ;
hwndClient = GetWindow (hwndFrame, GW_CHILD) ;
if (!TranslateMDISysAccel (hwndClient, &msg) &&
!TranslateAccelerator (hwndFrame, hAccel, &msg))
LRESULT CALLBACK FrameWndProc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
hwndClient = CreateWindow (TEXT ("MDICLIENT"), NULL,
WS_CHILD | WS_CLIPCHILDREN | WS_VISIBLE,
Trang 34if (SendMessage (hwndChild, WM_QUERYENDSESSION, 0, 0))
SendMessage (hwndClient, WM_MDIDESTROY,
default: // Pass to active child
hwndChild = (HWND) SendMessage (hwndClient,
Trang 35LRESULT CALLBACK HelloWndProc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
{
static COLORREF clrTextArray[] = { RGB (0, 0, 0), RGB (255, 0, 0), RGB (0, 255, 0), RGB ( 0, 0, 255), RGB (255, 255, 255) } ;
static HWND hwndClient, hwndFrame ;
pHelloData = (PHELLODATA) HeapAlloc (GetProcessHeap (),
HEAP_ZERO_MEMORY, sizeof (HELLODATA)) ;
Trang 36DrawText (hdc, TEXT ("Hello, World!"), -1, &rect,
DT_SINGLELINE | DT_CENTER | DT_VCENTER) ;
SendMessage (hwndClient, WM_MDISETMENU,
(WPARAM) hMenuHello, (LPARAM) hMenuHelloWindow) ;
// Check or uncheck menu item
pHelloData = (PHELLODATA) GetWindowLong (hwnd, 0) ;
CheckMenuItem (hMenuHello, pHelloData->iColor,
(lParam == (LPARAM) hwnd) ? MF_CHECKED : MF_UNCHECKED) ;
// Set the Init menu if losing focus
Trang 37
break ; // i.e., call DefMDIChildProc
case WM_DESTROY:
pHelloData = (PHELLODATA) GetWindowLong (hwnd, 0) ;
HeapFree (GetProcessHeap (), 0, pHelloData) ;
LRESULT CALLBACK RectWndProc (HWND hwnd, UINT message,
WPARAM wParam, LPARAM lParam)
int xLeft, xRight, yTop, yBottom ;
short nRed, nGreen, nBlue ;
pRectData = (PRECTDATA) HeapAlloc (GetProcessHeap (),
HEAP_ZERO_MEMORY, sizeof (RECTDATA)) ;
SetWindowLong (hwnd, 0, (long) pRectData) ;
pRectData->cxClient = LOWORD (lParam) ;
pRectData->cyClient = HIWORD (lParam) ;
pRectData = (PRECTDATA) GetWindowLong (hwnd, 0) ;
xLeft = rand () % pRectData->cxClient ;
xRight = rand () % pRectData->cxClient ;
yTop = rand () % pRectData->cyClient ;
yBottom = rand () % pRectData->cyClient ;
nRed = rand () & 255 ;
nGreen = rand () & 255 ;
nBlue = rand () & 255 ;
Trang 38
Rectangle (hdc, min (xLeft, xRight), min (yTop, yBottom),
max (xLeft, xRight), max (yTop, yBottom)) ;
pRectData = (PRECTDATA) GetWindowLong (hwnd, 0) ;
HeapFree (GetProcessHeap (), 0, pRectData) ;
Trang 39MDIMENUINIT MENU DISCARDABLE
BEGIN
POPUP "&File"
BEGIN
MENUITEM "New &Hello", IDM_FILE_NEWHELLO
MENUITEM "New &Rectangle", IDM_FILE_NEWRECT
MENUITEM "New &Hello", IDM_FILE_NEWHELLO
MENUITEM "New &Rectangle", IDM_FILE_NEWRECT
MENUITEM "&Close", IDM_FILE_CLOSE
MENUITEM "&Black", IDM_COLOR_BLACK
MENUITEM "&Red", IDM_COLOR_RED
MENUITEM "&Green", IDM_COLOR_GREEN
MENUITEM "B&lue", IDM_COLOR_BLUE
MENUITEM "&White", IDM_COLOR_WHITE
END
POPUP "&Window"
BEGIN
MENUITEM "&Cascade\tShift+F5", IDM_WINDOW_CASCADE
MENUITEM "&Tile\tShift+F4", IDM_WINDOW_TILE
MENUITEM "Arrange &Icons", IDM_WINDOW_ARRANGE
MENUITEM "Close &All", IDM_WINDOW_CLOSEALL
MENUITEM "New &Hello", IDM_FILE_NEWHELLO
MENUITEM "New &Rectangle", IDM_FILE_NEWRECT
MENUITEM "&Close", IDM_FILE_CLOSE
MENUITEM SEPARATOR
MENUITEM "E&xit", IDM_APP_EXIT
END
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com
Trang 40POPUP "&Window"
BEGIN
MENUITEM "&Cascade\tShift+F5", IDM_WINDOW_CASCADE
MENUITEM "&Tile\tShift+F4", IDM_WINDOW_TILE
MENUITEM "Arrange &Icons", IDM_WINDOW_ARRANGE
MENUITEM "Close &All", IDM_WINDOW_CLOSEALL
END
END
///////////////////////////////////////////////////////////////////////////// // Accelerator
MDIDEMO ACCELERATORS DISCARDABLE
BEGIN
VK_F4, IDM_WINDOW_TILE, VIRTKEY, SHIFT, NOINVERT
VK_F5, IDM_WINDOW_CASCADE, VIRTKEY, SHIFT, NOINVERT
END
This document is created with the unregistered version of CHM2PDF Pilot
Simpo PDF Merge and Split Unregistered Version - http://www.simpopdf.com