Korzystanie z akceleratorów klawiatury
W tej sekcji omówiono zadania skojarzone z akceleratorami klawiatury.
- Korzystanie z zasobu tabeli przyspieszeń
- przy użyciu tabeli akceleratora utworzonej w czasie wykonywania
Najczęstszym sposobem dodawania obsługi akceleratora do aplikacji jest dołączenie zasobu tabeli akceleratora do pliku wykonywalnego aplikacji, a następnie załadowanie zasobu w czasie wykonywania.
W tej sekcji omówiono następujące tematy.
- Tworzenie zasobu tabeli akceleratora
- ładowanie zasobu tabeli akceleratora
- wywoływanie funkcji akceleratora tłumaczenia
- Przetwarzanie Komunikatów WM_COMMAND
- niszczenie zasobu tabeli akceleratora
- tworzenie akceleratorów dla atrybutów czcionek
Zasób tabeli-akceleratora tworzy się przy użyciu instrukcji ACCELERATORS w pliku definicji zasobów aplikacji. Musisz przypisać nazwę lub identyfikator zasobu do tabeli akceleratora, najlepiej różniący się od innych zasobów. System używa tego identyfikatora do załadowania zasobu w czasie wykonywania.
Każdy zdefiniowany akcelerator wymaga oddzielnego wpisu w tabeli akceleratora. W każdym wpisie zdefiniujesz naciśnięcie (kod znaku ASCII lub kod klucza wirtualnego), który generuje akcelerator i identyfikator akceleratora. Należy również określić, czy naciśnięcie musi być używane w jakiejś kombinacji z ALT, SHIFT lub CTRL. Aby uzyskać więcej informacji na temat klawiszy wirtualnych, zobacz Wprowadzanie z klawiatury.
Naciśnięcie ASCII jest określane przez dołączenie znaku ASCII w podwójny cudzysłów lub użycie wartości całkowitej znaku w połączeniu z flagą ASCII. W poniższych przykładach pokazano, jak zdefiniować akceleratory ASCII.
"A", ID_ACCEL1 ; SHIFT+A
65, ID_ACCEL2, ASCII ; SHIFT+A
Kod wirtualny klawisza jest określany inaczej w zależności od tego, czy klawisz jest alfanumeryczny, czy niealfanumeryczny. W przypadku klucza alfanumerycznego litera lub cyfra klucza, ujęta w podwójny cudzysłów, jest połączona z flagą VIRTKEY. W przypadku klucza innego niż alfanumeryczny kod klucza wirtualnego dla określonego klucza jest połączony z flagą VIRTKEY. W poniższych przykładach pokazano, jak zdefiniować akceleratory kodu klucza wirtualnego.
"a", ID_ACCEL3, VIRTKEY ; A (caps-lock on) or a
VK_INSERT, ID_ACCEL4, VIRTKEY ; INSERT key
W poniższym przykładzie przedstawiono zasób tabeli skrótów definiujący akceleratory dla operacji na plikach. Nazwa zasobu to 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
Jeśli chcesz, aby użytkownik naciskał klawisze ALT, SHIFT lub CTRL w dowolnej kombinacji z klawiszem skrótu, określ flagi ALT, SHIFT i CONTROL w definicji akceleratora. Poniżej przedstawiono kilka przykładów.
"B", ID_ACCEL5, ALT ; ALT_SHIFT+B
"I", ID_ACCEL6, CONTROL, VIRTKEY ; CTRL+I
VK_F5, ID_ACCEL7, CONTROL, ALT, VIRTKEY ; CTRL+ALT+F5
Domyślnie, gdy klucz akceleratora odpowiada elementowi menu, system wyróżnia element menu. Możesz użyć flagi NOINVERT, aby zapobiec wyróżnianiu pojedynczego akceleratora. W poniższym przykładzie pokazano, jak używać flagi NOINVERT:
VK_DELETE, ID_ACCEL8, VIRTKEY, SHIFT, NOINVERT ; SHIFT+DELETE
Aby zdefiniować akceleratory odpowiadające elementom menu w aplikacji, uwzględnij akceleratory w tekście elementów menu. W poniższym przykładzie pokazano, jak uwzględnić akceleratory w tekście elementu menu w pliku definicji zasobów.
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
Aplikacja ładuje zasób tabeli skrótów, wywołując funkcję LoadAccelerators i określając uchwyt wystąpienia aplikacji, której plik wykonywalny zawiera zasób, oraz nazwę lub identyfikator zasobu. LoadAccelerators ładuje określoną tabelę akceleratora do pamięci i zwraca uchwyt do tabeli akceleratora.
Aplikacja może ładować zasób tabeli akceleratora w dowolnym momencie. Zazwyczaj jednowątkowa aplikacja ładuje tablicę przyspieszeń, zanim wejdzie w główną pętlę komunikatów. Aplikacja, która używa wielu wątków, zwykle ładuje zasób accelerator-table dla wątku przed wprowadzeniem pętli komunikatów dla wątku. Aplikacja lub wątek mogą również używać wielu tabel akceleratorów, z których każda jest skojarzona z określonym oknem w aplikacji. Taka aplikacja załaduje tabelę akceleratora dla okna za każdym razem, gdy użytkownik uaktywnił okno. Aby uzyskać więcej informacji na temat procesów i wątków, zobacz Procesy i wątki.
Aby przetworzyć akceleratory, pętla komunikatów aplikacji (lub wątku) musi zawierać wywołanie funkcji TranslateAccelerator. TranslateAccelerator porównuje naciśnięcia klawiszy z tabelą akceleratorów i, jeśli znajdzie dopasowanie, tłumaczy naciśnięcia klawiszy na komunikat WM_COMMAND (lub WM_SYSCOMMAND). Następnie funkcja wysyła komunikat do procedury okna. Parametry funkcji TranslateAccelerator obejmują uchwyt do okna, które ma odbierać komunikaty WM_COMMAND, uchwyt do tabeli przyspieszeń używanej do tłumaczenia akceleratorów oraz wskaźnik do struktury MSG zawierającej komunikat z kolejki. W poniższym przykładzie pokazano, jak wywołać TranslateAccelerator z poziomu pętli komunikatów.
MSG msg;
BOOL bRet;
while ( (bRet = GetMessage(&msg, (HWND) NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
// Check for accelerator keystrokes.
if (!TranslateAccelerator(
hwndMain, // handle to receiving window
haccel, // handle to active accelerator table
&msg)) // message data
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
Gdy akcelerator jest używany, okno określone w funkcji TranslateAccelerator odbiera komunikat WM_COMMAND lub WM_SYSCOMMAND. Słowo niższego rzędu parametru wParam zawiera identyfikator akceleratora. Procedura okna sprawdza identyfikator, aby określić źródło komunikatu WM_COMMAND i odpowiednio przetworzyć komunikat.
Zazwyczaj, jeśli akcelerator odpowiada elementowi menu w aplikacji, akcelerator i element menu mają przypisany ten sam identyfikator. Jeśli musisz wiedzieć, czy komunikat WM_COMMAND został wygenerowany przez akcelerator, czy przez element menu, możesz sprawdzić słowo o wysokiej wartości parametru wParam. Jeśli akcelerator wygenerował komunikat, wyraz o wysokiej kolejności wynosi 1; jeśli element menu wygenerował komunikat, wyraz o wysokiej kolejności wynosi 0.
System automatycznie niszczy zasoby tabeli akceleratorów ładowane przez funkcję LoadAccelerators, usuwając zasób z pamięci po zamknięciu aplikacji.
W przykładzie w tej sekcji pokazano, jak wykonywać następujące zadania:
- Utwórz zasób tabeli akceleratora.
- Załaduj tablicę przyspieszania w czasie działania.
- Tłumaczenie akceleratorów w pętli komunikatów.
- Przetwarzaj komunikaty WM_COMMAND generowane przez akceleratory.
Te zadania przedstawiono w kontekście aplikacji, która zawiera menu Znak i odpowiednie akceleratory, które umożliwiają użytkownikowi wybranie atrybutów bieżącej czcionki.
Poniższa część pliku definicji zasobów definiuje menu Znak i skojarzoną tabelę akceleratorów. Należy pamiętać, że elementy menu pokazują naciśnięcia akceleratora i że każdy akcelerator ma ten sam identyfikator co skojarzony element menu.
#include <windows.h>
#include "acc.h"
MainMenu MENU
{
POPUP "&Character"
{
MENUITEM "&Regular\tF5", IDM_REGULAR
MENUITEM "&Bold\tCtrl+B", IDM_BOLD
MENUITEM "&Italic\tCtrl+I", IDM_ITALIC
MENUITEM "&Underline\tCtrl+U", IDM_ULINE
}
}
FontAccel ACCELERATORS
{
VK_F5, IDM_REGULAR, VIRTKEY
"B", IDM_BOLD, CONTROL, VIRTKEY
"I", IDM_ITALIC, CONTROL, VIRTKEY
"U", IDM_ULINE, CONTROL, VIRTKEY
}
W poniższych sekcjach z pliku źródłowego aplikacji pokazano, jak zaimplementować akceleratory.
HWND hwndMain; // handle to main window
HANDLE hinstAcc; // handle to application instance
int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpCmdLine, int nCmdShow)
{
MSG msg; // application messages
BOOL bRet; // for return value of GetMessage
HACCEL haccel; // handle to accelerator table
// Perform the 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 a window cannot 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 ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
// Check for accelerator keystrokes.
if (!TranslateAccelerator(
hwndMain, // handle to receiving window
haccel, // handle to active accelerator table
&msg)) // message data
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
}
return msg.wParam;
}
LRESULT APIENTRY MainWndProc(HWND hwndMain, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BYTE fbFontAttrib; // array of font-attribute flags
static HMENU hmenu; // handle to main menu
switch (uMsg)
{
case WM_CREATE:
// Add a check mark 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 and menu commands.
case IDM_REGULAR:
case IDM_BOLD:
case IDM_ITALIC:
case IDM_ULINE:
// GetFontAttributes is an application-defined
// function that sets the menu-item check marks
// 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 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;
}
W tym temacie omówiono sposób używania tabel przyspieszeń utworzonych w czasie działania.
- tworzenie tabeli akceleratora Run-Time
- akceleratory przetwarzania
- niszczenie tabeli akceleratora Run-Time
- tworzenie akceleratorów edytowalnych użytkowników
Pierwszym krokiem tworzenia tabeli akceleratora w czasie wykonywania jest wypełnienie tablicy struktur ACCEL. Każda struktura w tablicy definiuje akcelerator w tabeli. Definicja akceleratora zawiera flagi, jego klucz i identyfikator. Struktura ACCEL ma następującą postać.
typedef struct tagACCEL { // accl
BYTE fVirt;
WORD key;
WORD cmd;
} ACCEL;
Aby zdefiniować naciśnięcie akceleratora, należy określić kod znaku ASCII lub kod klawisza wirtualnego w składowej struktury ACCEL. W przypadku określenia kodu klucza wirtualnego należy najpierw dołączyć flagę FVIRTKEY w elemencie członkowskim fVirt; w przeciwnym razie system interpretuje kod jako kod znaku ASCII. Możesz dołączyć flagę FCONTROL, FALT, lub FSHIFT, lub wszystkie trzy, aby połączyć klawisze CTRL, ALT lub SHIFT z naciśnięciem odpowiedniego klawisza.
Aby utworzyć tabelę akceleratora, przekaż wskaźnik do tablicy struktur ACCEL do funkcji CreateAcceleratorTable. CreateAcceleratorTable tworzy tabelę przyspieszaczy i zwraca uchwyt do tabeli.
Proces ładowania i wywoływania akceleratorów udostępnianych przez tabelę akceleratora utworzoną w czasie wykonywania jest taki sam jak przetwarzanie tych udostępnianych przez zasób tabeli akceleratora. Aby uzyskać więcej informacji, zobacz Ładowanie zasobu tabeli akceleratora za pośrednictwem przetwarzania komunikatów WM_COMMAND.
System automatycznie niszczy tabele akceleratorów utworzone w czasie wykonywania, usuwając zasoby z pamięci po zamknięciu aplikacji. Możesz zniszczyć tabelę akceleratora i wcześniej usunąć ją z pamięci, przekazując uchwyt tabeli do funkcji DestroyAcceleratorTable.
W tym przykładzie pokazano, jak utworzyć okno dialogowe, które umożliwia użytkownikowi zmianę akceleratora skojarzonego z elementem menu. Okno dialogowe składa się z listy rozwijanej zawierającej elementy menu, listy rozwijanej zawierającej nazwy klawiszy oraz pól wyboru służących do wybierania klawiszy CTRL, ALT i SHIFT. Poniższa ilustracja przedstawia okno dialogowe.
okno dialogowe
W poniższym przykładzie pokazano, jak okno dialogowe jest zdefiniowane w pliku definicji zasobów.
EdAccelBox DIALOG 5, 17, 193, 114
STYLE DS_MODALFRAME | WS_POPUP | WS_VISIBLE | WS_CAPTION
CAPTION "Edit Accelerators"
BEGIN
COMBOBOX IDD_MENUITEMS, 10, 22, 52, 53,
CBS_SIMPLE | CBS_SORT | WS_VSCROLL |
WS_TABSTOP
CONTROL "Control", IDD_CNTRL, "Button",
BS_AUTOCHECKBOX | WS_TABSTOP,
76, 35, 40, 10
CONTROL "Alt", IDD_ALT, "Button",
BS_AUTOCHECKBOX | WS_TABSTOP,
76, 48, 40, 10
CONTROL "Shift", IDD_SHIFT, "Button",
BS_AUTOCHECKBOX | WS_TABSTOP,
76, 61, 40, 10
COMBOBOX IDD_KEYSTROKES, 124, 22, 58, 58,
CBS_SIMPLE | CBS_SORT | WS_VSCROLL |
WS_TABSTOP
PUSHBUTTON "Ok", IDOK, 43, 92, 40, 14
PUSHBUTTON "Cancel", IDCANCEL, 103, 92, 40, 14
LTEXT "Select Item:", 101, 10, 12, 43, 8
LTEXT "Select Keystroke:", 102, 123, 12, 60, 8
END
Pasek menu aplikacji zawiera podmenu Znak, którego elementy mają skojarzone akceleratory.
MainMenu MENU
{
POPUP "&Character"
{
MENUITEM "&Regular\tF5", IDM_REGULAR
MENUITEM "&Bold\tCtrl+B", IDM_BOLD
MENUITEM "&Italic\tCtrl+I", IDM_ITALIC
MENUITEM "&Underline\tCtrl+U", IDM_ULINE
}
}
FontAccel ACCELERATORS
{
VK_F5, IDM_REGULAR, VIRTKEY
"B", IDM_BOLD, CONTROL, VIRTKEY
"I", IDM_ITALIC, CONTROL, VIRTKEY
"U", IDM_ULINE, CONTROL, VIRTKEY
}
Wartości elementów menu szablonu menu są stałymi zdefiniowanymi w następujący sposób w pliku nagłówkowym aplikacji.
#define IDM_REGULAR 1100
#define IDM_BOLD 1200
#define IDM_ITALIC 1300
#define IDM_ULINE 1400
W oknie dialogowym wykorzystywana jest tablica struktur VKEY zdefiniowanych przez aplikację, z których każda zawiera ciąg tekstowy reprezentujący naciśnięcia klawiszy oraz ciąg tekstowy akceleratora. Po utworzeniu okna dialogowego analizuje tablicę i dodaje każdy ciąg tekstowy naciśnięć do listy rozwijanej Select Keystroke. Gdy użytkownik kliknie przycisk OK, okno dialogowe wyszukuje wybrany ciąg tekstowy naciśnięć i pobiera odpowiedni ciąg tekstowy akceleratora. Okno dialogowe dołącza tekst akceleratora do tekstu elementu menu wybranego przez użytkownika. W poniższym przykładzie przedstawiono tablicę struktur VKEY:
// VKey Lookup Support
#define MAXKEYS 25
typedef struct _VKEYS {
char *pKeyName;
char *pKeyString;
} VKEYS;
VKEYS vkeys[MAXKEYS] = {
"BkSp", "Back Space",
"PgUp", "Page Up",
"PgDn", "Page Down",
"End", "End",
"Home", "Home",
"Lft", "Left",
"Up", "Up",
"Rgt", "Right",
"Dn", "Down",
"Ins", "Insert",
"Del", "Delete",
"Mult", "Multiply",
"Add", "Add",
"Sub", "Subtract",
"DecPt", "Decimal Point",
"Div", "Divide",
"F2", "F2",
"F3", "F3",
"F5", "F5",
"F6", "F6",
"F7", "F7",
"F8", "F8",
"F9", "F9",
"F11", "F11",
"F12", "F12"
};
Procedura inicjalizacji okna dialogowego wypełnia listy rozwijane "Wybierz element" i "Wybierz klawisz". Gdy użytkownik wybierze element menu i skojarzony akcelerator, okno dialogowe sprawdza kontrolki w oknie dialogowym, aby uzyskać wybór użytkownika, aktualizuje tekst elementu menu, a następnie tworzy nową tabelę akceleratora zawierającą nowy akcelerator zdefiniowany przez użytkownika. W poniższym przykładzie przedstawiono procedurę okna dialogowego. Należy pamiętać, że należy zainicjować procedurę okna.
// Global variables
HWND hwndMain; // handle to main window
HACCEL haccel; // handle to accelerator table
// Dialog-box procedure
BOOL CALLBACK EdAccelProc(HWND hwndDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
int nCurSel; // index of list box item
UINT idItem; // menu-item identifier
UINT uItemPos; // menu-item position
UINT i, j = 0; // loop counters
static UINT cItems; // count of items in menu
char szTemp[32]; // temporary buffer
char szAccelText[32]; // buffer for accelerator text
char szKeyStroke[16]; // buffer for keystroke text
static char szItem[32]; // buffer for menu-item text
HWND hwndCtl; // handle to control window
static HMENU hmenu; // handle to "Character" menu
PCHAR pch, pch2; // pointers for string copying
WORD wVKCode; // accelerator virtual-key code
BYTE fAccelFlags; // fVirt flags for ACCEL structure
LPACCEL lpaccelNew; // pointer to new accelerator table
HACCEL haccelOld; // handle to old accelerator table
int cAccelerators; // number of accelerators in table
static BOOL fItemSelected = FALSE; // item selection flag
static BOOL fKeySelected = FALSE; // key selection flag
HRESULT hr;
INT numTCHAR; // TCHARs in listbox text
switch (uMsg)
{
case WM_INITDIALOG:
// Get the handle to the menu-item combo box.
hwndCtl = GetDlgItem(hwndDlg, IDD_MENUITEMS);
// Get the handle to the Character submenu and
// count the number of items it has. In this example,
// the menu has position 0. You must alter this value
// if you add additional menus.
hmenu = GetSubMenu(GetMenu(hwndMain), 0);
cItems = GetMenuItemCount(hmenu);
// Get the text of each item, strip out the '&' and
// the accelerator text, and add the text to the
// menu-item combo box.
for (i = 0; i < cItems; i++)
{
if (!(GetMenuString(hmenu, i, szTemp,
sizeof(szTemp)/sizeof(TCHAR), MF_BYPOSITION)))
continue;
for (pch = szTemp, pch2 = szItem; *pch != '\0'; )
{
if (*pch != '&')
{
if (*pch == '\t')
{
*pch = '\0';
*pch2 = '\0';
}
else *pch2++ = *pch++;
}
else pch++;
}
SendMessage(hwndCtl, CB_ADDSTRING, 0,
(LONG) (LPSTR) szItem);
}
// Now fill the keystroke combo box with the list of
// keystrokes that will be allowed for accelerators.
// The list of keystrokes is in the application-defined
// structure called "vkeys".
hwndCtl = GetDlgItem(hwndDlg, IDD_KEYSTROKES);
for (i = 0; i < MAXKEYS; i++)
{
SendMessage(hwndCtl, CB_ADDSTRING, 0,
(LONG) (LPSTR) vkeys[i].pKeyString);
}
return TRUE;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDD_MENUITEMS:
// The user must select an item from the combo
// box. This flag is checked during IDOK
// processing to be sure a selection was made.
fItemSelected = TRUE;
return 0;
case IDD_KEYSTROKES:
// The user must select an item from the combo
// box. This flag is checked during IDOK
// processing to be sure a selection was made.
fKeySelected = TRUE;
return 0;
case IDOK:
// If the user has not selected a menu item
// and a keystroke, display a reminder in a
// message box.
if (!fItemSelected || !fKeySelected)
{
MessageBox(hwndDlg,
"Item or key not selected.", NULL,
MB_OK);
return 0;
}
// Determine whether the CTRL, ALT, and SHIFT
// keys are selected. Concatenate the
// appropriate strings to the accelerator-
// text buffer, and set the appropriate
// accelerator flags.
szAccelText[0] = '\0';
hwndCtl = GetDlgItem(hwndDlg, IDD_CNTRL);
if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0) == 1)
{
hr = StringCchCat(szAccelText, 32, "Ctl+");
if (FAILED(hr))
{
// TODO: write error handler
}
fAccelFlags |= FCONTROL;
}
hwndCtl = GetDlgItem(hwndDlg, IDD_ALT);
if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0) == 1)
{
hr = StringCchCat(szAccelText, 32, "Alt+");
if (FAILED(hr))
{
// TODO: write error handler
}
fAccelFlags |= FALT;
}
hwndCtl = GetDlgItem(hwndDlg, IDD_SHIFT);
if (SendMessage(hwndCtl, BM_GETCHECK, 0, 0) == 1)
{
hr = StringCchCat(szAccelText, 32, "Shft+");
if (FAILED(hr))
{
// TODO: write error handler
}
fAccelFlags |= FSHIFT;
}
// Get the selected keystroke, and look up the
// accelerator text and the virtual-key code
// for the keystroke in the vkeys structure.
hwndCtl = GetDlgItem(hwndDlg, IDD_KEYSTROKES);
nCurSel = (int) SendMessage(hwndCtl,
CB_GETCURSEL, 0, 0);
numTCHAR = SendMessage(hwndCtl, CB_GETLBTEXTLEN,
nCursel, 0);
if (numTCHAR <= 15)
{
SendMessage(hwndCtl, CB_GETLBTEXT,
nCurSel, (LONG) (LPSTR) szKeyStroke);
}
else
{
// TODO: writer error handler
}
for (i = 0; i < MAXKEYS; i++)
{
//
// lstrcmp requires that both parameters are
// null-terminated.
//
if(lstrcmp(vkeys[i].pKeyString, szKeyStroke)
== 0)
{
hr = StringCchCopy(szKeyStroke, 16, vkeys[i].pKeyName);
if (FAILED(hr))
{
// TODO: write error handler
}
break;
}
}
// Concatenate the keystroke text to the
// "Ctl+","Alt+", or "Shft+" string.
hr = StringCchCat(szAccelText, 32, szKeyStroke);
if (FAILED(hr))
{
// TODO: write error handler
}
// Determine the position in the menu of the
// selected menu item. Menu items in the
// "Character" menu have positions 0,2,3, and 4.
// Note: the lstrcmp parameters must be
// null-terminated.
if (lstrcmp(szItem, "Regular") == 0)
uItemPos = 0;
else if (lstrcmp(szItem, "Bold") == 0)
uItemPos = 2;
else if (lstrcmp(szItem, "Italic") == 0)
uItemPos = 3;
else if (lstrcmp(szItem, "Underline") == 0)
uItemPos = 4;
// Get the string that corresponds to the
// selected item.
GetMenuString(hmenu, uItemPos, szItem,
sizeof(szItem)/sizeof(TCHAR), MF_BYPOSITION);
// Append the new accelerator text to the
// menu-item text.
for (pch = szItem; *pch != '\t'; pch++);
++pch;
for (pch2 = szAccelText; *pch2 != '\0'; pch2++)
*pch++ = *pch2;
*pch = '\0';
// Modify the menu item to reflect the new
// accelerator text.
idItem = GetMenuItemID(hmenu, uItemPos);
ModifyMenu(hmenu, idItem, MF_BYCOMMAND |
MF_STRING, idItem, szItem);
// Reset the selection flags.
fItemSelected = FALSE;
fKeySelected = FALSE;
// Save the current accelerator table.
haccelOld = haccel;
// Count the number of entries in the current
// table, allocate a buffer for the table, and
// then copy the table into the buffer.
cAccelerators = CopyAcceleratorTable(
haccelOld, NULL, 0);
lpaccelNew = (LPACCEL) LocalAlloc(LPTR,
cAccelerators * sizeof(ACCEL));
if (lpaccelNew != NULL)
{
CopyAcceleratorTable(haccel, lpaccelNew,
cAccelerators);
}
// Find the accelerator that the user modified
// and change its flags and virtual-key code
// as appropriate.
for (i = 0; i < (UINT) cAccelerators; i++)
{
if (lpaccelNew[i].cmd == (WORD) idItem)
{
lpaccelNew[i].fVirt = fAccelFlags;
lpaccelNew[i].key = wVKCode;
}
}
// Create the new accelerator table, and
// destroy the old one.
DestroyAcceleratorTable(haccelOld);
haccel = CreateAcceleratorTable(lpaccelNew,
cAccelerators);
// Destroy the dialog box.
EndDialog(hwndDlg, TRUE);
return 0;
case IDCANCEL:
EndDialog(hwndDlg, TRUE);
return TRUE;
default:
break;
}
default:
break;
}
return FALSE;
}