Adding Menu Accelerator Keys

advertisement
Adding Menu Accelerator Keys
There is one feature of Windows that is commonly used in conjunction with a menu.
This feature is the accelerator key. Accelerator keys are special keystrokes that you
define which, when pressed, automatically select a menu option even though the
menu in which that option resides is not displayed. Put differently, you can select an
item directly by pressing an accelerator key, bypassing the menu entirely. The term
accelerator key is an accurate description because pressing one is generally a faster
way to select a menu item than first activating its menu and then selecting the item.
To define accelerator keys relative to a menu, you must add an accelerator key table
to your resource file. An accelerator table has this general form:
TableNamee ACCELERATORS \[acccl-options]
{ Keyl, MenuID1 \[, type] \[options]
Key2, MenuID2 \[, type] \[options]
Key3, MenuID3 \[, type] \[options]
.
.
.
KeyN, MenuIDN \[, type] \[options] }
Here, Tublename is the name of
the accelerator table. An ACCELERATORS
statement can have the same options as those described for MENU. If needed, they
are specified by accel-options. However, most applications simply use the default
settings. Inside the accelerator table, Key is the keystroke that selects the item and
MenuID is the ID value associated with the desired item. The type specifies whether
the key is a standard key (the default) or a virtual key. The options may be one of the
following macros:
NOINVERT, ALT, SHIFT, and CONTROL. NOINVERT prevents the selected
menu item from being highlighted when its accelerator key is pressed. ALT specifies
an alt key. SHIFT specifies a shift key. CONTROL specifies a ctrl key.
The value of Key will be either a quoted character, an ASCII integer value
corresponding to a key, or a virtual key code. If a quoted character is used, then it is
assumed to be an ASCII character. If it is an integer value, then you must tell the
resource compiler explicitly that this is an ASCII character by specifying type as
ASCII. If it is a virtual key, then type must be VIRTKEY.
If the key is an uppercase quoted character then its corresponding menu item will be
selected if it is pressed while holding down the shift key. If it is a lowercase character,
then its menu item will be selected if the key is pressed by itself. If the key is
specified as a lowercase character and ALT is specified as an option, then pressing alt
and the character will select the item. (If the key is uppercase and ALT is specified,
then you must press shiff and alt to select the item.) Finally, if you want the user to
press ctrl and the character to select an item, precede the key with a ^.
As explained in lecture 4, a virtual key is a system-independent code for a variety of
keys. To use a virtual key as an accelerator, simply specify its macro for the key and
specify VIRTKEY for its type. You may also specify ALT, SHIFT, or CONTROL to
achieve the desired key combination. Here are some examples:
"A", IDM_x
; select by pressing Shift-A
"a", IDM_x "
; select by pressing a
"^a", IDM_x "
; select by pressing Ctrl-a
"a", IDM_x, ALT
; select by pressing Alt-a
VK_F2, IDM_x
; select by pressing F2
VK_F2, IDM_x, SHIFT ; select by pressing Shift-F2
Here is the MENU.RC resource file that also contains accelerator key definitions for
MyMenu. ; Sample menu resource file and accelerators.
# include \<windows.h>
# include "menu.h"
MyMenu MENU
{POPUP "&File"
{MENUITEM "&Open\\tF2", IDM_OPEN
MENUITEM "&Close\\tF3", IDM_CLOSE
MENUITEM "&Exit \\t Ctrl-X",
IDM_EXIT}
POPUP "^Options"
{
MENUITEM "& Colors\\t Ctrl-C", IDM_COLORS
POPUP"&Priority"{
MENUITEM "&Low\\tF4", IDM_LOW
MENUITEM"&High\\tF5", IDM_HIGH}
MENUITEM "&Fonts\\t Ctrl-F", IDM_FONT
MENUITEM"&Resolution\\tCtrl-R",IDM_RESOLUTION
}
MENUITEM "&Help", IDM_HELP}
//Define menu accelerators MyMenu ACCELERATORS
{ VK_F2, IDM_OPEN, VIRTKEY
VK_F3, IDM_CLOSE, VIRTKEY
"^X", IDM_EXIT
"^C", IDM_COLORS
VK_F4, IDM_LOW, VIRTKEY
VK_F5, IDM_HIGH, VIRTKEY
"^F", IDM_FONT
"^R", IDM_RESOLUTION
VK_F1, IDM_HELP, VIRTKEY }
Notice that the menu definition has been enhanced to display which accelerator key
selects which option. Each item is separated from its accelerator key using a tab. The
header file WINDOWS.H is included because it defines the virtual key macros.
Loading the Accelerator Table
Even though the accelerators are contained in the same resource file as the menu, hey
must be loaded separately using another API function called LoadAccelerators( ),
whose prototype is shown here:
HACCEL LoadAccelerators (HlNSTANCE Thislnst, LPCSTR Name);
where Thislnst is the instance handle of the application and Name is the name of the
accelerator table. The function returns a handle to the accelerator table or NULL if the
table cannot be loaded.
You must call LoadAccelerators( ) soon after the window is created. For example, this
shows how to load the MyMenu accelerator table:
HACCEL hAccel;
hAccel = LoadAccelerators(hThisInst, "MyMenu");
The value of hAccel will be used later to help process accelerator keys.
Translating Accelerator Keys
Although the LoadAccelerators() function loads the accelerator table, your program
still cannot process accelerator keys until you add another API function to the
message loop. This function is called TranslateAcceIerator( ) and its prototype is
shown here:
int TransIateAccclerator(HWND hwnd, HACCEL hAccel, LPMSG lpMess);
Here, hwnd is the handle of the window for which accelerator keys will be translated.
hAcccl is the handle to the accelerator table that will be used. This is the handle
returned by LoadAccelerators( ). Finally, lpMess is a pointer to the message. The
TrauslateAcceIerator( ) function returns true if an accelerator key was pressed and
false otherwise.
TranslateAcccIerator( ) translates an accelerator keystroke into its corresponding
WM_COMMAND message and sends that message to the window. In this message,
the value of LOWORD(wParam) will contain the ID associated with the accelerator
key. Thus, to your program, the WM_COMMAND message will appear to have been
generated by a menu selection.
Since TranslateAccelerator( ) sends a WM_COMMAND message whenever an
accelerator key is pressed, your program must not execute TranslateMessage( ) or
DispatchMessage(
)
when
such
a
translation
takes
place.
When
using
TranslateAccelcrator( ), your message loop should look like this:
while(GetMessage(&msg, NULL, 0, 0))
{if(!TranslateAccelerator(hwnd, hAccel, &msg))
{TranslateMessage(&msg); /*allow use of keyboard */
DispatchMessage(&msg); /*return control to Windows*/}}
Trying Accelerator Keys To try using accelerators, substitute the following version of
WinMain( ) into the preceding application and add the accelerator table to your
resource file.
/* Process accelerator keys. */
#include \<windows.h>
#include \<string.h>
#include \<stdio.h>
#include "menu.h"
LRESULT CALLBACK WindowFunc(HWND, UINT, WPARAM, LPARAM);
char szWinName\[ ] = "MyWin";
/* name of window class */
int WINAPI WinMain(HINSTANCE hThisInst, HINSTANCE hPrevInst, LPSTR
IpszArgs, int nWinMode)
{HWND hwnd; MSG msg; WNDCLASSEX wc1;
HACCEL hAccel; wc1.cbSize = sizeof(WNDCLASSEX);
wc1 .hInstance = hThisInst;
wc1 .lpszClassName = szWinName;
wc1. lpfnWndProc = WindowFunc; wc1. style =0;
wc1.hIcon = LoadIcon(NULL, IDI_APPLICATION) ;
wc1.hlconSm = LoadIcon (NULL, IDI_WINLOGO) ;
wc1.hCursor = LoadCursor (NULL, IDC_ARROW) ;
wc1. lpszMenuName = "MyMenu"; wc1 .cbClsExtra =0; wc1 .cbWndExtra = 0;
wc1.hbrBackground = GetStockObject (WHITE_BRUSH) ;
if ( !RegisterClassEx (&wc1) ) return 0 ;
hwnd
=
CreateWindow
WS_OVERLAPPEDWINDOW,
(szWinName,"Adding
CW_USEDEFAULT,
Accelerator
Keys",
CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,HWND_DESKTOP,NULL, hThisInst,
NULL );
/* load the keyboard accelerators */
hAccel = LoadAccelerators (hThisInst, "MyMenu") ;
/*Display the window.*/
ShowWindow (hwnd, nWinMode) ; UpdateWindow(hwnd) ;
while(GetMessage(&msg, NULL, 0, 0)){if ( ! TranslateAccelerator (hwnd,' hAccel,.
&msg) )
{TranslateMessage(&msg); DispatchMessage(&msg)}} return msg. wParam; }
In Depth: A Closer Look at WM_COMMAND As you know, each time you make a
menu selection or press an accelerator key, a WM_COMMAND message is sent and
the value in LOWORD(wParam) contains the ID of the menu item selected or the
accelerator key pressed. However, using only the value in LOWORD(wParam) it is
not possible to determine which event occurred. In most situations, it doesn't matter
whether the user actually made a menu selection or just pressed an accelerator key.
But in those situations in which it does, you can find out because Windows provides
this
information
in
the high-order word of wParam.
If the
value in
HIWORD(wParam) is 0, then the user has made a menu selection. If this value is 1,
then the user pressed an accelerator key. For example, try substituting the following
fragment into the menu program. It reports whether the Open option was selected
using the menu or by pressing an accelerator key.
case IDM_OPEN:
if(HIWORD(wParam))
MessageBox(hwnd, "Open File via Accelerator", "Open", MB_OK);
Else MessageBox(hwnd, "Open File via Menu Selection", "Open", MB_OK); break;
The value of IParam for WM_COMMAND messages generated by menu selections
or accelerator keys is unused and always contains NULL.
As you will see in the next lecture, a WM_COMMAND is also generated n when the
user interacts with various types of controls. In this case, the meanings of lParam and
wParam are somewhat different. For example, the value of lParam will contain the
handle of the control. Non-Menu Accelerator Keys Although keyboard accelerators
are most commonly used to provide a fast means of selecting menu items, they are not
limited to this role. For example, you can define an accelerator key for which there is
no corresponding menu item. You might use such a key to activate a keyboard macro
or to initiate some frequently used option.
To define a non-menu
accelerator key, simply add it to the accelerator table,
assigning it a unique ID value. As an example, let's add a non-menu accelerator key to
the menu program,| The key will be ctrl-t and each time it is pressed, the current time
and date are displayed in a message box. The standard ANSI C time and date
functions are used to obtain the current time and date.
To begin, change the key table so that it looks like this:
MyMenu ACCELERATORS {VK_F2, IDM_OPEN, VIRTKEY
VK_F3,
IDM_CLOSE, VIRTKEY
"^X", IDM_EXIT
"^C", IDM_COLORS
VK_F4, IDM_LOW, VIRTKEY
VK_F5, IDM_HIGH, VIRTKEY
"^R", IDM_RESOLUTION
"^F", IDM_FONT
VK_F1, IDM_KELP, VIRTKEY
"^T", IDM_TIME}
Next, add this line to MENU.H:
#define IDM_TIME 500
Finally, substitute this version of WindowFunc( ) into the menu program You will
also need to include the TIME.H header file.
LRESULT CALLBACK WindowFunc(HWND hwnd, UINT message, WPARAM
wParam, LPARAM lParam)
{int response; struct tm *tod; time_t t; char str \[80];
switch(message) {case WM_COMMAND:
switch(LOWORD(wParam))
{case IDM_OPEN: MessageBox(hwnd,
"Open File",
"Open", MB_OK);break;
case IDM_CLOSE: MessageBox(hwnd, "Close File", "Close", MB_OK);break;
case
IDM_EXIT:response=MessageBox(hwnd,"Quit
the
Program?","Exit",
MB_YESNO);
if(response == IDYES) PostQuitMessage(0); break;
case IDM_COLORS:
MessageBox(hwnd,
"Set Colors",
"Colors",
MB_OK);break;
case IDM_LOW: MessageBox(hwnd,
"Low",
"Priority", MB_OK);break;
case IDM_HIGH: MessageBox(hwnd,
"High",
"Priority", MB_OK);break;
case IDM_RESOLUTION: MessageBox(hwnd,"Resolution Options","Resolution",
MB_OK);break;
case IDMJFONT: MessageBox(hwnd, "Font Options", "Fonts", MB_OK); break;
case IDM_TIME: /* show time */
t = time(NULL); tod = localtime(&t);
strcpyfstr, asctime(tod)); str(strlen(str)-1] = '\\0'; /* remove /r/n */
MessageBox(hwnd, str, "Time and Date", MB_OK); break;
case IDM_HELP: MessageBox(hwnd,"No Help","Help",MB_OK); break; }break;
case WM_DESTROY: PostQuitMessage(0); break;
default: return DefWindowProc(hwnd, message, wParam, lParam);} return 0; }
When you run this program, each time you press ctrl-t, you will see a message box
similar to the following:
Download