48.2.1 Using an Accelerator-Table Resource

The most common way to add accelerator support to a Windows application is to include an accelerator-table resource with your application's executable file and load the resource at run time. The steps involved in using an accelerator-table resource are as follows:

Use your favorite text editor to define an accelerator table in a resource-definition file, then use a resource compiler to compile the resource-definition file and add the resulting resource to your application's executable file.

Include a call to the LoadAccelerators function in your application to load the accelerator-table resource and receive a handle of the accelerator table.

Add the TranslateAccelerator function to the message loop associated with the accelerator table.

Process the WM_COMMAND messages that are generated when the user uses the accelerator.

48.2.1.1 Creating the Accelerator-Table Resource

You create an accelerator-table resource by using the ACCELERATORS statement in your application's resource-definition file. You must assign a name or resource identifier to the accelerator table—no other resource should have the same name or identifier. Windows uses this identifier to load the resource at run time.

Each accelerator that you define requires a separate entry in the accelerator-table. In each entry, you define the keystroke (either an ASCII character code or virtual-key code) that generates the accelerator, the accelerator's identifier, and whether the keystroke must be used in combination with the ALT, SHIFT, and/or CTRL keys. For more information about virtual keys, see Chapter 36, “Keyboard Input.”

You specify an ASCII keystroke either by enclosing the desired ASCII character in double quotation marks or by specifying the integer value of the character in combination with the ASCII flag. The following examples show how to define ASCII accelerators:

"A", ID_ACCEL1 ; SHIFT+A

65, ID_ACCEL2, ASCII ; SHIFT+A

You specify a virtual-key code keystroke differently depending on whether the keystroke is an alphanumeric key or a non-alphanumeric key. For an alphanumeric key, you combine the key's letter or number, enclosed in double quotation marks, with the VIRTKEY flag. For a non-alphanumeric key, you combine the Windows virtual-key code of the desired key with the VIRTKEY flag. The following examples show how to define virtual-key code accelerators:

"a", ID_ACCEL3, VIRTKEY ; A (caps-lock on) or a

VK_INSERT, ID_ACCEL4, VIRTKEY ; INSERT key

If you want the user to press the ALT, SHIFT, and/or CTRL keys in combination with the accelerator keystroke, you can specify the ALT, SHIFT, and CONTROL flags in the accelerator's definition. Following are some examples:

"B", ID_ACCEL5, ALT ; ALT_SHIFT+B

"I", ID_ACCEL6, CONTROL, VIRTKEY ; CTRL+I

VK_F5, ID_ACCEL7, CONTROL, ALT, VIRTKEY ; CTRL+ALT+F5

By default, when an accelerator key that corresponds to a menu item is used, Windows highlights the menu item. You can use the NOINVERT flag to prevent highlighting for an individual accelerator. The following example shows how to use the NOINVERT flag:

VK_DELETE, ID_ACCEL8, VIRTKEY, SHIFT, NOINVERT ; SHIFT+DELETE

The following example shows an accelerator-table resource that defines accelerators for file operations. The name of the resource is “FileAccel”.

FileAccel ACCELERATORS

BEGIN

VK_F12, IDM_OPEN, CONTROL, VIRTKEY ; CTRL+F12

VK_F4, IDM_CLOSE, ALT, VIRTKEY ; ALT+F4

VK_F12, IDM_SAVE, SHIFT, VIRTKEY ; SHIFT+F12

VK_F12, IDM_SAVEAS, VIRTKEY ; F12

END

When you define accelerators that correspond to menu items in your application, you should include the accelerators in the text of the menu items. The following example shows how to include accelerators in menu-item text in a resource-definition file:

FilePopup MENU

BEGIN

POPUP "&File"

BEGIN

MENUITEM "&New..", IDM_NEW

MENUITEM "&Open\tCtrl+F12", IDM_OPEN

MENUITEM "&Close\tAlt+F4" IDM_CLOSE

MENUITEM "&Save\tShift+F12", IDM_SAVE

MENUITEM "Save &As...\tF12", IDM_SAVEAS

END

END

48.2.1.2 Loading the Accelerator-Table Resource

You load an accelerator-table resource by calling the LoadAccelerators function, specifying the instance handle of the application whose executable file contains the resource, and the name or identifier of the resource. LoadAccelerators loads the specified accelerator table into memory and returns the handle of the accelerator table.

An application can load an accelerator-table resource at any time. Typically, a single-threaded application loads its accelerator table just before entering its main message loop. An application that uses multiple threads typically loads the accelerator-table resource for a thread before entering the message loop for the thread. An application or thread might also use multiple accelerator tables, each associated with a particular window in the application. Such an application would load a window's accelerator table each time the user activates the window. For more information about threads, see Chapter 3, “Processes and Threads.”

48.2.1.3 Calling the TranslateAccelerator Function

To process accelerators, an application's (or thread's) message loop must contain a call to the TranslateAccelerator function. TranslateAccelerator compares keystrokes to an accelerator table and, if it finds a match, translates the keystrokes into a WM_COMMAND (or WM_SYSCOMMAND) message and sends the message to a window procedure. The parameters of the TranslateAccelerator function include the handle of the window that is to receive the WM_COMMAND messages, the handle of the accelerator table used to translate accelerators, and a pointer to an MSG structure containing a message from the queue. The following example shows how to call TranslateAccelerator from within a message loop:

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

/* Check for accelerator keystrokes. */

if (!TranslateAccelerator(

hwndMain, /* handle of receiving window */

haccel, /* handle of active accel. table */

&msg)) { /* address of message data */

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

48.2.1.4 Processing WM_COMMAND Messages

The window procedure of the window whose handle is specified in the TranslateAccelerator function receives WM_COMMAND (or WM_SYSCOMMAND) messages when an accelerator is used. The low-order word of the wParam parameter contains the identifier of the accelerator. Your window procedure should examine the identifier to determine the source of the WM_COMMAND message and process the message accordingly.

Typically, if an accelerator corresponds to an item in your application's menu, the accelerator and menu item are assigned the same identifier. If you need to know whether a given WM_COMMAND message was generated by an accelerator or by a menu item, you can examine the high-order word of the wParam parameter. The high-order word is 1 if an accelerator generated the message; it is 0 if a menu item generated the message.

48.2.1.5 Example: Accelerators for Font Attributes

This section contains example code that shows how to perform the following tasks:

Create an accelerator-table resource.

Load the accelerator table at run time.

Translate accelerators in a message loop.

Process WM_COMMAND messages generated by the accelerators.

These tasks are demonstrated in the context of an application that includes a Character menu and corresponding accelerators that allow the user to select attributes of the current font.

The following portion of a resource-definition file defines the Character menu and the associated accelerator table. Notice that the menu items show the accelerators and that each accelerator has the same identifier as its associated menu item.

#include <windows.h>

#include "acc.h"

MainMenu MENU

BEGIN

POPUP "&Character"

BEGIN

MENUITEM "&Regular\tF5", IDM_REGULAR

MENUITEM "&Bold\tCtrl+B", IDM_BOLD

MENUITEM "&Italic\tCtrl+I", IDM_ITALIC

MENUITEM "&Underline\tCtrl+U", IDM_ULINE

END

END

FontAccel ACCELERATORS

BEGIN

VK_F5, IDM_REGULAR, VIRTKEY

"B", IDM_BOLD, CONTROL, VIRTKEY

"I", IDM_ITALIC, CONTROL, VIRTKEY

"U", IDM_ULINE, CONTROL, VIRTKEY

END

The following sections from the application's source file show how to implement the accelerators:

HWND hwndMain; /* handle of main window */

HANDLE hinstAcc; /* handle of application instance */

int APIENTRY WinMain(hinst, hinstPrev, lpCmdLine, nCmdShow)

HANDLE hinst;

HANDLE hinstPrev;

LPSTR lpCmdLine;

int nCmdShow;

{

MSG msg; /* application messages */

HACCEL haccel; /* handle of accelerator table */

.

. /* Perform initialization procedure. */

.

/* Create a main window for this application instance. */

hwndMain = CreateWindowEx(0L, "MainWindowClass",

"Sample Application", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL,

hinst, NULL );

/* If window could not be created, return "failure." */

if (!hwndMain)

return FALSE;

/* Make the window visible and update its client area. */

ShowWindow(hwndMain, nCmdShow);

UpdateWindow(hwndMain);

/* Load the accelerator table. */

haccel = LoadAccelerators(hinstAcc, "FontAccel");

if (haccel == NULL)

HandleAccelErr(ERR_LOADING); /* application defined */

/*

* Get and dispatch messages until a WM_QUIT message is

* received.

*/

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

/* Check for accelerator keystrokes. */

if (!TranslateAccelerator(

hwndMain, /* handle of receiving window */

haccel, /* handle of active accel. table */

&msg)) { /* address of message data */

TranslateMessage(&msg);

DispatchMessage(&msg);

}

}

return (msg.wParam);

}

LONG APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)

HWND hwndMain;

UINT uMsg;

UINT wParam;

LONG lParam;

{

BYTE fbFontAttrib; /* array of font-attribute flags */

static HMENU hmenu; /* handle of main menu */

switch (uMsg) {

case WM_CREATE:

/*

* Add a checkmark to the Regular menu item to

* indicate that it is the default.

*/

hmenu = GetMenu(hwndMain);

CheckMenuItem(hmenu, IDM_REGULAR, MF_BYCOMMAND |

MF_CHECKED);

return 0;

case WM_COMMAND:

switch (LOWORD(wParam)) {

/* Process the accelerator/menu commands. */

case IDM_REGULAR:

case IDM_BOLD:

case IDM_ITALIC:

case IDM_ULINE:

/*

* GetFontAttributes is an

* applicaiton-defined function that sets the

* menu-item checkmarks and returns the

* user-selected font attributes.

*/

fbFontAttrib = GetFontAttributes(

(BYTE) LOWORD(wParam), hmenu);

/*

* SetFontAttributes is an

* application-defined function that creates

* a font with the user-specified attributes

* and associates the font with the main

* window's device context.

*/

SetFontAttributes(fbFontAttrib);

break;

default:

break;

}

break;

.

. /* Process other messages. */

.

default:

return DefWindowProc(hwndMain, uMsg, wParam, lParam);

}

return NULL;

}