CS-360
Fall, 2000
Class 6
R. Eckert

MOUSE AND KEYBOARD

THE MOUSE--

A pointing device with one or more buttons.

It's important, but optional--Windows can be installed and used without one.

When the user moves the physical mouse, Windows moves a small bitmapped image
(the mouse cursor) on the display. One pixel in the cursor is the "hot spot";
it points to a precise location on the display.

The hot spot position is constantly updated by low-level logic inside
Windows. (We don't need to worry about it.)

A default cursor type for a window is specified when the window's class
structure is defined (registered):

         wndclass.hCursor = LoadCursor(NULL,IDC_ARROW);
         NULL ==> a predefined cursor

Mouse actions by the user--
  Moving ;
  Clicking (press/release);
  Double Clicking;
  Dragging (moving while holding down a button).

CLIENT AREA MOUSE MESSAGES--WndProc() receives a mouse message whenever
the mouse passes over the window or is clicked within the window. There are
21 messages in all.

WM_MOUSEMOVE Message--sent to the program whose window is under the cursor 
each time the mouse moves:
   -HIWORD(lParam) = Y position in the client area using "client area coordinates"
    (pixels) relative to the upper lefthand corner of the client area.
   -LOWORD(lParam) = X position.
   -wParam indicates if a mouse button, Shift key, or control key was pressed as
    the message was sent--Mouse Notification codes (MK_LBUTTON, MK_RBUTTON,
    MK_SHIFT, MK_CONTROL, MK_MBUTTON).

   -Use bitwise AND (&) to check for combinations:
       if (wParam==MK_SHIFT)  /* WRONG!!--won't get key combinations*/
       if (wParam & MK_SHIFT)  /* CORRECT!! */

Other mouse messages--WM_*BUTTON#
                         * = L, M, R
                         # = DOWN, UP, DBLCLK
(The DBLCLK message is sent only if the wndclass.style contains CS_DBLCLKS when the
 class is registered.)


SKETCH.CPP Example Program: Change the WINAPP2 program so that the user can
use the left mouse button to sketch--

"Sketch"--Choosing this new selection from the menu will put the program
into "sketching" mode. While the user holds down the left mouse button, a
line will be drawn in the client area that traces the movements of the
mouse cursor. When the user releases the mouse button, the sketching stops
until the mouse button is again pressed. We'll set this menu item up as a
toggle, so that the first time it's clicked, sketching is turned on, the
next time, it's turned off, etc.

The idea here is that if the program is in "Sketch" mode, each time the
mouse moves, WITH THE LEFT BUTTON DOWN, a line segment will be drawn from
the former mouse cursor position to the current mouse cursor position.

User selects the "Sketch" menu item -->
  -WM_COMMAND, LOWORD(wParam)=IDM_SKETCH.
  -Program reverses the state of a static BOOLEAN bSketch variable.
  -bSketch is used to determine whether mouse tracks should be shown or not.

User presses the left mouse button -->
  -WM_LBUTTONDOWN message with the mouse cursor coordinates in lParam.
  -Program sets a static BOOLEAN bButdn variable to TRUE.
  -Program gets the initial mouse coordinates from lParam and stores 
   them in (x0,y0).

User releases left mouse button -->
  -WM_LBUTTONUP message.
  -Program sets the bButdn variable to FALSE.

User moves mouse -->
  -WM_MOUSEMOVE message with (x,y) coords in lParam.
  -If bSketch is TRUE and if bButdn is TRUE:
     -Program gets current mouse coordinates from lParam & stores them in (x,y).
     -gets a DC with GetDC().
     -moves to (x0,y0) with MoveToEx().
     -draws a line to (x,y) with LineTo().
     -releases the DC with ReleaseDC().
     -sets the (x0,y0) to (x1,y1)--makes the old point become the current point.

Note that WM_PAINT messages are not handled ==> default processing, so any
exposure event will cause the client area to be erased (painted in the
background brush). Thus the sketch will be erased. If that is not supposed
to happen, the individual (x,y) coordinates obtained at each WM_MOUSEMOVE
message would have to be stored in some sort of an array or list. Then,
whenever a WM_PAINT message occurred, the sketch could be restored by
indexing through the list and drawing each stroke (line segment).


INPUT FOCUS--If more than one instance of a program is running, only one
will have a highlighted top caption line. That program's window is said to have 
the "input focus." This is the only window that will receive any keyboard input.

If we run two instances of SKETCH, we see that the keyboard accelerators for 
the menu items only work with the program instance that has the input focus.

Note--Input focus is significant for keyboard input, but not for mouse
input. This makes sense, since we normally use the mouse to pick an active
window (one with the input focus). If mouse messages were ignored by inactive
windows, we could never activate them.

Gaining/Losing the input focus--
When a window gains (loses) the input focus, Windows sends a WM_SETFOCUS
(WM_KILLFOCUS) message to the window. Common responses are to highlight an
edit area or change a caption.

Sometimes we want to explicitly give a window or window control the
input focus; e.g., we might want an edit control to have the input focus
when the program's main window was first displayed. We can do with:
SetFocus(hWnd);

The response of a window to receiving the focus depends on its window style--
  -caption-barred windows have their caption bars highlighted.
  -edit controls start showing a blinking caret at the point of keyboard input.
  -button controls show a highlighted outline.
  -list boxes show a highlighted selection item.
  -combo boxes display their list box or highlight it (if it's displayed).


NONCLIENT MOUSE MESSAGES--

The mouse messages we've seen up to now are only sent if the cursor is over 
the client area; In any other part of the window, WM_NC* messages are sent .
(*=MOUSEMOVE, etc.) For these:
     -wParam indicates the non-client area where the mouse action occurred -- 
      hit test codes (HT***).
     -lParam contains the mouse position, but in screen coordinates (not
      client area coordinates).

Usually our programs don't process nonclient mouse messages--but we could
use a WM_NCLBUTTONDOWN message and the coordinates returned to determine if
the cursor is over a given menu item. In fact Windows does just that, and
sends a WM_COMMAND message with the ID # for the menu item selected in its
wParam! (It's usually a lot easier to let Windows do the dirty work!)

CAPTURING THE MOUSE--

Usually we switch between applications by using the mouse. The window clicked
gets the input focus. But sometimes we might want to limit the mouse to
interacting with just one program. (useful for screen capture programs)

An application that does this has "captured" the mouse. Only it will receive
mouse messages. Do this with:  SetCapture(hWnd); After having been captured,
the mouse can be released with ReleaseCapture(void);


CHANGING THE CURSOR SHAPE (MOUSCURS Example Application)--

MOUSCURS.RC--note the menu structure; there's now a popup menu on the menu bar.
When the user clicks on "Cursor Shape" a popup menu appears. The items are:
"Arrow", "Cross", "Hand", "Mouse". Each should change the mouse cursor icon.
A click on each sends a WM_COMMAND message; LOWORD(wParam) contains the message 
ID, as usual. Popup menus are handy when you have a lot of menu items. If they 
all appeared simultaneously, they would take up too much space within the
window.

MOUSCURS.CPP

The code responding to WM_COMMAND message uses LoadCursor() to load a new mouse
cursor, but it's not visible yet. The parameters are:
 1. hInst of module whose executable file contains the cursor to be loaded.
    We can get it using GetWindowLong() for the two new cursors. (The other two 
    are already predefined, so use NULL).
 2. A pointer to a null-terminated string containing the name of the cursor 
    resource to be loaded (usually defined in the .RC file).

The cursor is made visible when Windows sends a WM_SETCURSOR message. This
is done if the mouse causes cursor movement within the window. The program
responds with a call to SetCursor(), which changes the cursor shape to the
last one loaded.

Note the two new cursor bitmaps: HAND.CUR and MOUSE.CUR--
  These were created and can be observed with the Developer Studio cursor
  editor.

Note also that the "Cursor Shape" selection is a popup menu; it does NOT
send a WM_COMMAND message. So in the .RC file, no message IDM # is needed. Note
the syntax of the popup menu in the .RC file:

Popup Menu Syntax (from MOUSCURS.RC file)--

MYMENU          MENU
BEGIN
        POPUP   "&Cursor Shape"
        BEGIN
          MENUITEM "&Arrow",    IDM_ARROW
          MENUITEM "&Cross",    IDM_CROSS
          MENUITEM "&Hand",     IDM_HAND
          MENUITEM "&Mouse",    IDM_MOUSE
        END
        MENUITEM "&Quit",       IDM_QUIT
END

You can prepare this menu script with a text editor, or, more commonly, use
Developer Studio's menu editor to do it visually (see below).

Arrow and Cross --> IDC_ARROW and IDC_CROSS, predefined stock cursor
shapes, but "Hand" and "Mouse" are defined in .cur files that are loaded
into the MOUSCURS resource data. The easiest way to create one or more
cursors and have them incorporated into your program's resources is to use
Developer Studio's cursor editor (see below).

Another feature of this program--it has a blinking caret. Any time the window 
gets the input focus, the caret is created with CreateCaret().
A Call to GetSystemMetrics(SM_CYCAPTION) gets the height of the window's caption bar
  ==> the height is the size a line of text would be on any video system.
A call to GetSystemMetrics(SM_CXBORDER) gets the width of the window's border
  ==> that will be the width of the caret.
A call to ShowCaret() makes the caret visible.
  (We could use HideCaret() to make the caret invisible temporarily.)
  The caret will be visible as long as the window has the input focus.
  The caret disappears when the window loses the focus (e.g., DestroyCaret() is 
  called when the program gets a WM_KILLFOCUS message).

Mouse press (WM_LBUTTONDOWN message) ==> The program uses SetCaretPos() to move
the caret to that point. (nXpos and nYpos are static, so the old values are
remembered). This program really doesn't do anything with the caret, but word
processor programs would use it to indicate where text is to be inserted!

PREPARING THE MOUSCURS PROGRAM RESOURCES VISUALLY WITH DEVELOPER STUDIO--

1. Set up the project as usual (Win32 Application).

2. Copy the MOUSCURS.CPP file into the project's directory and add it to the
project (as usual).

3. Get into the cursor editor--From the main menu bar choose: "Insert |
Resource", then select "Cursor" and "New".

4. Draw a hand-shaped cursor in the work area using the drawing tools as
needed.

5. Click the "Hot Spot" button, position the cursor where you want the hot
spot to be, and click again.

6. Close the cursor editor. Note that you now have a resource script with
the following structure: Script1-->Cursor-->IDC_CURSOR1.

7. Right click on IDC_CURSOR1 and select "Properties".

8. Change the ID to "HAND" (with the quote marks). The file name should
change to hand.cur.

9. Repeat steps 3 through 6 to create a mouse-shaped cursor. Note that the
resource script now has the following structure: Script1-->Cursor-->"HAND"
                                                                 -->IDC_CURSOR1.

10. Change the ID to "MOUSE" and be sure that the file name is mouse.cur.

11. Get into the menu editor: "Insert | Resource", "Menu | New".

12. Double click the menu item box to bring down its Properties box. Give it
the Caption &Cursor Shape. Note that by default, the "Pop-up" button has
been checked. This is what we want.

13. Double click the menu item box under the new "Cursor Shape" menu item
to bring down its properties box. Give it the ID IDM_ARROW and the Caption
'&Arrow' (no quotes). Note that automatically the "Pop-up" button is now 
unchecked, which is again what we want.

14. Repeat step 13 for the other three menu items: IDM_CROSS, '&Cross';
IDM_HAND, '&Hand'; and IDM_MOUSE, '&Mouse'.

15. Double click the menu item box to the right of the "Cursor Shape" menu
item to bring down its properties box. Give this menu item the ID IDM_QUIT
and the caption '&Quit'. Be sure to first click the "Pop-up" check button to
deselect the "Popup" option. (The Quit menu item is not a popup menu.)

16. Close the menu editor. Note that now the structure of your resource
script is: Script1-->Cursor-->"HAND"
                           -->"MOUSE"
                  -->Menu  -->IDR_MENU1
Right click on IDR_MENU1, then "Properties" to change the ID to "MYMENU"
(again with the quote marks).

17. From the main menu, select: "File | Save As", and change the file name
to mouscurs. Be sure that the file type is "Resource Script". You may want
to examine the resulting mouscurs.rc script's text. You can do so by opening
it in text mode.

18. From the main menu, select: "Project | Add to Project | Files", then
choose the mouscurs.rc file. If you have not done so already (Step 2), also
add the mouscurs.cpp file.

19. Build your project as usual.


THE KEYBOARD--

The keyboard provides the primary means of entering text and numbers.

Keypresses can also be used as shortcuts for mouse actions (accelerators) so
the user doesn't have to take his/her hands off the keyboard to "use" the mouse.

Character sets--

Computers store and transmit characters using coding systems. Each character
is given a number code. The entire collection is called a character set.

The most commonly used character set is the 7-bit (128) ASCII set for
letters, numbers, and common keyboard symbols.

IBM added additional graphic/special symbols-->the IBM PC character set
(See Petzold Fig. 5-7) Microsoft calls this the OEM character set.

But this doesn't have a lot of the foreign language accented characters. So the
designers of Windows went to a more international character set--the ANSI
set. (Less graphics, more accented/international symbols) (See Petzold Fig.
5-8)

Most characters are the same, but in some situations differences are
important. e.g., the following code is supposed to recognize all letters:

char c;
if ( c>='A' && c<='Z' || c>='a' && c<='z' )

This completely ignores accented characters.

Also, the standard C compiler library functions (ischar(), toupper(), tolower())
assume ASCII characters and ignore accented characters.

==> if we use these, our programs will not work right in many foreign
languages.

So Windows provides its own functions for using/converting characters:

AnsiLower()           Convert char string to lowercase
AnsiLowerBuff()       Same, doesn't have to be null terminated
AnsiNext()            Move to next char in a string
AnsiPrev()            Move to previous char in a string
AnsiToOem()           Convert a char string from ANSI to OEM char set
AnsiToOemBuff()       Same, doesn't have to be null terminated
AnsiUpper()           Convert char string to uppercase
AnsiUpperBuff()       Same,...
IsCharAlpha()         Determine if an ANSI char is an alphabetic char
IsCharAlphaNumeric()  Determine if an ANSI char is alphabetic/numeric char
IsCharLower()         Determine if an ANSI char is lowercase
IsCharUpper()         Determine if an ANSI char is uppercase
OemToAnsi()           Convert a char string from OEM to ANSI char set
OemToAnsiBuff()       Same,...
ToAscii()             Convert from virtual key/scan code data to ANSI char

Reasons we should use these and not use the Standard C functions:
  1. To deal correctly with accented characters.
  2. They are part of Windows, not part of our program. Standard C function
     code is added to our program, Windows version keeps the code in Windows ==>
     smaller programs.

To do the above example using Windows character functions:

char c;
if (IsCharAlpha(c))

Conversion between both character sets--Useful if text is being exchanged
between Windows programs and DOS programs.

Windows also has functions for copying and comparing strings. (Also are part
of Windows) They will process both ANSI and OEM strings:

lstrcat()    Concatenate one char string to the end of another.
lstrcmp()    Compare two char strings.
lstrcmpi()   Same, but not case sensitive.
lstrcpy()    Copy a char string to a memory buffer.
lstrlen()    Determine the length of a char string.

Unicode--

7/8 bit character codes are unsatisfactory for many foreign languages.
Unicode is a character coding that uses a uniform 16-bit code for each
character. It allows the representation of every character in every written
language of the world likely to be used in computer communications. No
support in Windows 3.xx; rudimentary support in Windows 95; complete support
in Windows NT.


KEYBOARD MESSAGE PROCESSING--

The Keyboard driver's SETUP copies the selected country's keyboard driver into
the \windows\system --> KB.DRV file.

Windows starts ==> KB.DRV enabled, and this responds by saving and updating
the keyboard INT 9 vector.

From then on KB.DRV intercepts INT 9 (key press/release) from the keyboard and--
  -decodes the key.
  -calls a Windows routine that stores it as a queued message in system queue.
  -Windows transfers the message to the correct application's queue.
  -So our program gets the message from the GetMessage() loop.
        This is polling--like DOS INT 21h, Service 01 or BIOS INT 16h).
        It's not asynchronous ==> our program is never interrupted.
        This gives all the benefits, but none of the headaches of INT 9!!

DETAILS OF KEYBOARD MESSAGE PROCESSING--

User presses a key ==> Windows sends a WM_KEYDOWN message to the window with the
input focus. Key released ==> WM_KEYUP message;

  -wParam contains device-independent "virtual key code" for the key
   pressed/released. (See Petzold, p. 247-249 for VK codes).
  -Most keys have VK codes = ANSI code.
  -Others have names like VK_F1, VK_TAB.
  -<ALT> key is different--it has no virtual key code.
  -The status of the <ALT> key is passed with the lParam in WM_KEYDOWN or 
   WM_KEYUP message.

A program can use VK codes to figure out what the user is doing with keys.

Key down/up messages put data into the lParam--

bit-31  1==>key being pressed; 0==>key being released.
bit-30  1==>key was down before message was sent; 0==>not down.
bit-29  1==><ALT> was held down when key was pressed; 0==>not down.
25-28   Reserved.
bit-24  1==>extended key flag (e.g., function key or numeric keypad key).
16-23   Keyboard OEM scan code (the value in AH after INT 16h returns).
0-15    Repeat count (# of times char was repeated because held down key).

This could be used to distinguish between left and right shift key (OEM scan
code for left shift = 0x2a, for right = 0x36).

But WM_KEYUP and WM_KEYDOWN message are sometimes too primitive. E.g., to
distinguish between 'A' and 'a', we would need extra logic testing for
VK_SHIFT code and 'A', since 'A' and 'a' are the same key and have the same
VK code.

An easier way--use TranslateMessage() to convert the current combination of
letter and shift keys to an equivalent ANSI code. Thus TranslateMessage()
usually is put into the message loop in WinMain().

If TranslateMessage() detects a keyboard action that translates to an ANSI
character, it generates a WM_CHAR message. The wParam will have the ANSI code.
And this is usually what you want to display!) The lParam uses the same bit
coding as above--not very useful.

Function keys and keys that don't have ANSI codes don't generate WM_CHAR
messages. To handle these we need to use WM_KEYDOWN and WM_KEYUP to detect them.

The sequence of messages sent (assuming the event loop has a TranslateMessage()):
 1. WM_KEYDOWN   key pressed
 2. WM_CHAR      ANSI character provided if there is one
 3. WM_KEYUP     key released


SYSTEM KEY MSGS--

The sequence of messages is different for the <ALT> key or when no window has
the input focus:
 1. WM_SYSKEYDOWN -- a key was depressed with <ALT> down.
 2. WM_SYSCHAR   --  ANSI character (if there was one).
 3. WM_SYSKEYUP  --  Key released.
 -<ALT> keystroke indicates some special function. Usually we use
  WM_SYSKEYDOWN to determine which VK key was depressed.
 -lParam and wParam are used the same as for ordinary WM_KEY messages


A SIMPLE KEYBOARD INTERFACE (KEYBD1 Example Program)--

The user types at the keyboard ==> blue characters appear in client area. The 
only editing feature is the backspace, which erases the last letter typed.

cBuf[] builds the text string as it's input.

WM_CHAR message received ==> the character is tested and appended to cBuf if
it's OK-- i.e., if either of the following is true:

   1. character is alphanumeric (IsCharAlphaNumeric()).
   2. character is punctuation (i.e., helper function IsAnsiPunc() returns TRUE)
      ==> it's printable but not an alphanumeric character.

and:

   3. the end of cBuf[] memory space allocated hasn't been reached.

The maximum length (LINELONG) is a constant defined in the .h file.

The null character (0) is always added to the current end of the buffer (keeps
it a null-terminated sting). nCurPos keeps track of the position in the
buffer of the current end of the string.

But the character hasn't been displayed--we need to cause a WM_PAINT message
to be sent, so use InvalidateRect(). Every time a new character is received,
the entire string will be redrawn. Also since resizing covering/uncovering -->
a WM_PAINT message, automatically the string will be redrawn in those cases.

When WM_PAINT message is received--
  Get a DC for painting (BeginPaint()).
  Change the color of the text to be displayed (SetTextColor()).
  Use TextOut() to send cBuf[] to the display.
  Get rid of DC (EndPaint()).

Since the backspace key doesn't have an ANSI code, we can't use WM_CHAR--
  -Instead process WM_KEYDOWN looking for VK_BACK.
  -If received, decrement the cursor position and overwrite the last
   character with null ==> string is one character shorter.

IsAnsiPunc()--a helper function that tests ranges of ANSI codes for
punctuation characters.


FONT: Typeface, style, size of characters in a character set--

There are three basic kinds of fonts--

-Stock fonts--built into Windows, always available (see figure below):
    (ANSI_FIXED_FONT, ANSI_VAR_FONT, DEVICE_DEFAULT_FONT, OEM_FIXED_FONT,
    SYSTEM_FONT, SYSTEM_FIXED_FONT)


-Logical or GDI fonts--defined in separate .fon (stroke or raster) or
  .fot/.ttf (TrueType) font resource files in \windows\system and stored on
  disk (see figures below).





 Stroke fonts are continuously scalable, but slow in being drawn and
 don't have the best legibility. Raster fonts are bitmaps, which means
 that only integer scaling factors can be used easily in changing their
 size. Their display is fast and they are very legible. TrueType fonts
 are really rasterized stroke fonts, which means that they are stored as
 strokes, but with hints to make them into a more legible bitmap
 (rasterization). They combine the best of both stroke and raster fonts.

 -Device fonts--native to the output device (e.g., built-in printer fonts).

Using stock fonts--

 Like stock pens, brushes, stock fonts are accessed with GetStockObject(),
 which returns a handle to a font that can be used by SelectObject():

HDC      hDC;
HFONT    hFont;
hDC = GetDC(hWnd);
hFont = GetStockObject (ANSI_VAR_FONT);
SelectObject (hDC,hFont);         /* now can use it */

In KEYBD1, the default font for the DC was used for character output--the
system font, a variable-pitch font.

Using Logical Fonts--

To use logical font data in a program--

  -Obtain a handle to the font data resource and select it into the DC.
  -Just like a stock font, except it's loaded from separate file (.fon, .fot/.ttf).
  -Use CreateFont() instead of GetStockObject() to load and get a font handle.
  -CreateFont() can make new fonts by interpolating data in a font file;
    ==> new sizes, bold/underlined, rotated/distorted characters.
  -Called logical since they come from program logic not just from a file.

hFont = CreateFont(Ht, Width, Escapement, Orientation, Weight, Italic,
                   Underline, StrikeOut, CharSet, OutputPrecision,
                   ClipPrecision, Quality, PitchAndFamily, Facename);

CreateFont() takes 14 parameters, many are often set to 0 ==> defaults 
(See the on-line help on CreateFont):

Heights and average Widths--measured in logical units. With the default units of
a DC, these are equal to pixels.

Escapement--Specifies the angle, in tenths of degrees, between the line
through the origins of the first and last characters on a line and the x-
axis of the screen surface.

Orientation--how much a character should be rotated in tenths of degree 
(900 ==> character is lying on its back).

(See figure below to see the difference between escapement and orientation).

Weight--How thickly to print character lines (FW_NORMAL=400, FW_BOLD=700,
others).

Italic--TRUE/FALSE.

Underline--TRUE/FALSE.

StrikeOut--TRUE/FALSE --> characters with a line through the center.

CharSet--ANSI_CHARSET, SYMBOL_CHARSET, OEM_CHARSET, SHIFTJIS_CHARSET.

OutputPrecision--How closely the output must match the requested font height/width/
orientation/escapement/pitch; there are several possibilities.

ClipPrecision--How to clip characters partially outside the clipping region.

Quality-- DRAFT_QUALITY, PROOF_QUALITY , DEFAULT_QUALITY (most common).

PitchAndFamily--2 values "OR'd":
  -First (low order 2 bits):-->font pitch (DEFAULT_PITCH, FIXED_PITCH,
   VARIABLE_PITCH);
  -Second (high order 4 bits): --> font family (FF_DECORATIVE, FF_DONTCARE,
   FF_MODERN, FF_ROMAN, FF_SCRIPT, FF_SWISS);
  -e.g., DEFAULT_PITCH | FF_ROMAN.

Facename--pointer to a string that specifies the typeface name (style) of
the font data; identifies the font resource data file, e.g.: Courier, MS
Serif, MS Sans Serif, Symbol, Small, Modern, Roman, Script; Courier New,
Courier New Bold, Courier New Italic, Courier New Bold, etc.

Example call to CreateFont()--

CreateFont(36,0,0,0,FW_NORMAL,0,0,0,ANSI_CHARSET,OUT_DEFAULT_PRECIS,
           CLIP_DEFAULT_PRECIS,DEFAULT_QUALITY,VARIABLE_PITCH|FF_ROMAN,
           "Roman");

TEXT METRICS--

CreateFont() may not give you exactly what you ask for; you may not know all
of the dimensions of a font after it's created. So use GetTextMetrics() to
find out font details. There's lots of information in a TEXTMETRIC structure 
(search on TEXTMETRIC). It is commonly used to determine a font size (see 
figure below), which can be used to set line spacing, caret size, sizes of 
buttons, etc.


FONT1 Example Program: Adding New Fonts to KEYBD1--

This program allows a choice of three fonts for output of text typed by the user--
  Stock ANSI variable and OEM Fixed fonts; a logical 36 point Roman font.

The logic processing character messages is the same as KEYBD1 ==> user types in
text that's displayed.  i.e., WM_CHAR and WM_KEYDOWN are handled in the same
way.

WM_CREATE when program starts --> Uses CreateFont() to create a 36-pixel-high 
logical Roman font and saves the handle in hFont.

"Font" is a popup menu (see .rc file) with 3 font choices.

User selects a font from the menu --> WM_COMMAND message; the choice is stored 
in nFontChoice, and InvalidateRect() causes a WM_PAINT message which:
 1. Gets a DC and changes the color to blue.
 2. checks the value of nFontChoice.
 3. Uses SelectObject() to select the font (two are stock, one logical).
 4. Uses TextOut() to output the cBuf[] string.
 5. Releases the DC with EndPaint().

WM_DESTROY message-->Use DeleteObect(hFont) to remove the font from memory.

We can change the logical Roman font so that it slopes downward to right by
changing the Escapement to 3000 in the call to CreateFont()--See the statement
that is commented out in FONT1.


KEYBOARD ACCELERATORS--

Provide an alternative to using the mouse to select menu items.
The simplest way of doing it is to precede the menu item's title string with 
an '&' in the resource script file.

MyMenu    MENU
BEGIN
    MENUITEM "&Paint",    IDM_PAINT
    MENUITEM "&Clear",    IDM_CLEAR
    etc.
END

Then <ALT> with P or C chooses that item.

The letter following the '&' is underlined on the menu bar, which is O.K. for 
a single menu bar.

But if there are popup menus, the user must select the <ALT>-key in the top 
menu and the <ALT>-key in the popup menu, which is cumbersome.

A better shortcut would require only one key combination to execute the
command, no matter where it was in the menu structure.

We can do this by processing WM_KEYDOWN and WM_CHAR messages and directing
program logic accordingly. But Windows gives a much simpler alternative in
the form of keyboard accelerators.

The basic idea--you define the key combinations in an "accelerator table" in 
the resource file. Each defined key combination sends a WM_COMMAND message to
the program. These WM_COMMAND messages imitate menu selections made with the
mouse.

Accelerator Table Syntax--

     TableName    ACCELERATORS
     BEGIN
         event,    ID Value,   [VIRTKEY] [NOINVERT] [ALT] [SHIFT] [CONTROL]
     END

event--the keystroke for the key combination, specified by:
    the key's letter in quotes,  e.g., "A".
    An integer for the ANSI code of the key.
    The Virtual Key code of the key, e.g., VK_NUMPAD1.

ID Value--An integer value sent with the WM_COMMAND message as LOWORD(wParam);.
    (HIWORD(wParam)=1 for an accelerator.)

Last field--zero or more flags; none==>event must be a letter in quotes;

  VIRTKEY==>event must be a virtual key code; ALT/SHIFT/CONTROL in any
  combination to indicate that these keys must accompany the event key.

  NOINVERT==>overrides default action of switching the menu item to reverse
  video (to simulate a mouse click) when the accelerator is activated.

EXAMPLE--

MyAccel    ACCELERATORS
BEGIN
    VK_F1,    IDM_ANSI,    VIRTKEY           /* F1 activates */
    VK_F2,    IDM_OEM,     VIRTKEY, CONTROL  /* <Ctrl><F2> activates */
    "A",      IDM_NOMENU,  VIRTKEY, ALT      /* <Alt><A> activates */
    "Z",      36,          SHIFT, CONTROL    /* <Shift><Ctrl><Z> activates*/
                                             /* LOWORD(wParam) = 36 */

As with other resources, an accelerator table can be prepared using a text
editor or visually using the accelerator table editor built into Developer
Studio.

Using accelerator tables--

  -Use LoadAccelerators() to load the table,

  -TranslateAccelerator()--translates WM_KEYUP/WM_KEYDOWN messages into
   WM_COMMAND messages (if there is an entry in the application's
   accelerator table). This must be placed in the WinMain()'s message loop.


hAccel = LoadAccelerators (hInstance, "MyAccel") ;  /* load accel. table */

while (GetMessage (&msg, NULL, 0, 0))   /* message loop */
{
    if (!TranslateAccelerator (hWnd, hAccel, &msg)) /* translate accel. */
    {
        TranslateMessage (&msg) ;   /* translate keyboard messages */
        DispatchMessage (&msg) ;    /* send message to WndProc() */
    }
}

If a match is found between the keyboard message and the accelerator table,
TranslateAccelerator() returns nonzero (TRUE) ==> WM_COMMAND message is sent
directly to the WndProc() (so we don't need to send it). In other words, we
we want the message processed as usual only if TranslateAccelator() returns 0.

FONT2 Program--

FONT2.RC: Defines the following Accelerator table--

MyAccel     ACCELERATORS
BEGIN
VK_F1,      IDM_ANSI,       VIRTKEY
VK_F2,      IDM_OEM,        VIRTKEY, CONTROL
VK_F2,      IDM_OEM,        VIRTKEY
VK_F3,      IDM_SWISS,      VIRTKEY, ALT
0x37,       IDM_NOMENU,     ASCII,ALT
"a",        IDM_NOMENU
END


-IDM_NOMENU: This will be sent when the user presses the "a" accelerator key or 
<ALT>-7 (0x37 is the ASCII code for a 7). In other words, it doesn't correspond 
to a menu item. (This would not be done normally).

User hits "a" or <ALT>-7==>WM_COMMAND message with LOWORD(wParam)=IDM_NOMENU.
  -The program responds by displaying the OK-message box: "Got IDM_NOMENU
   WM_COMMAND message, Accelerator only".

Note also that F2 and <Ctrl>-F2 both are accelerators for IDM_OEM, F1 for
IDM_ANSI, and <ALT>-F3 for IDM_SWISS.

Another improvement over FONT1--The current editing point is marked by a
caret and WM_SETFOCUS/WM_KILLFOCUS messages display/destroy the caret.

WM_PAINT--
  -Sets the text color.
  -Selects the appropriate font, depending on nFontChoice set in response to
   the WM_COMMAND window/accelerator table message.
  -Uses GetTextMetrics() to fill a TEXTMETRIC structure.
  -Sets the height of the caret according to the height of current font.
  -Sets the width of the caret according to the width of the window border.
  -Destroys the old caret.
  -Creates a new caret.
  -Positions caret at the end of the string-- but this is hard since font is variable:
      Use GetTextExtentPoint32(hDC,cBuf,lstrlen(cBuf),&size) to get the size of 
      the character string cBuf; the result is returned in the SIZE structure 
      pointed to by &size (see online help) and reflects the current font.
  -Uses SetCaretPos() to position it.
  -Outputs the text string with TextOut().