Акселераторы
III
7. Акселераторы. На первый взгляд этот вопрос достаточно прост, но, как станет ясно, он потянет за собой множество других. Акселератор позволяет выбирать пункт меню просто сочетанием клавиш.
Это очень удобно и быстро. Таблица акселераторов является ресурсом, имя которого должно совпадать с именем того меню (ресурса), пункты которого она определяет.
Вот пример такой таблицы. Определяется один акселератор на пункт меню MENUP, имеющий идентификатор 4.
MENUP ACCELERATORS { VK_F5, 4, VIRTKEY }
А вот общий вид таблицы акселераторов.
Имя ACCELERATORS { Клавиша 1, Идентификатор пункта меню (1) [,тип] [,параметр] Клавиша 2, Идентификатор пункта меню (2) [,тип] [,параметр] Клавиша 3, Идентификатор пункта меню (3) [,тип] [,параметр] ... Клавиша N, Идентификатор пункта меню (N) [,тип] [,параметр] }
Рассмотрим представленную схему. Клавиша - это либо символ в кавычках, либо код ASCII символа, либо виртуальная клавиша. Если вначале стоит код символа, то тип задается как ASCII. Если используется виртуальная клавиша, то тип определяется как VIRTUAL. Все названия (макроимена) виртуальных клавиш можно найти в include- файлах (windows.h). Мы, как обычно, будем определять все макроимена непосредственно в программе.
Параметр может принимать одно из следующих значений: NOINVERT, ALT, CONTROL, SHIFT. Значение NOINVERT означает, что не подсвечивается выбранный при помощи акселератора пункт меню. Значения ALT, SHIFT, CONTROL означают, что, кроме клавиши, определенной в акселераторе, должна быть нажата одна из управляющих клавиш. Кроме этого, если клавиша определяется в кавычках, то нажатие при этом клавиши CONTROL определяется знаком "^": "^А".
А теперь поговорим о механизме работы акселераторов. Для того чтобы акселераторы работали, необходимо выполнить два условия:
Остановимся подробнее на втором пункте. Функция TranslateAccelerator преобразует сообщения WM_KEYDOWN и WM_SYSKEYDOWN в сообщения WM_COMMAND
и WM_SYSCOMMAND соответственно. При этом в старшем слове параметра WPARAM
помещается 1, как отличие для акселератора. В младшем слове, как Вы помните, содержится идентификатор пункта меню. Возникает вопрос: для чего необходимы два сообщения WM_COMMAND и WM_SYSCOMMAND? Здесь все закономерно: сообщение WM_SYSCOMMAND генерируется для пунктов системного меню или меню окна (см. Рисунок 2.3.4).
Функция TranslateAccelerator возвращает ненулевое значение, если было произведено преобразование сообщения акселератора, в противном случае возвращается 0. Естественно включить вызов этой функции в кольцо сообщений. Вот этот фрагмент.
MSG_LOOP: PUSH 0 PUSH 0 PUSH 0 PUSH OFFSET MSG CALL GetMessageA@16 CMP EAX, 0 JE END_LOOP PUSH OFFSET MSG PUSH [ACC] PUSH [NEWHWND] CALL TranslateAcceleratorA@12 CMP EAX ,0 JNE MSG_LOOP PUSH OFFSET MSG CALL TranslateMessage@4 PUSH OFFSET MSG CALL DispatchMessageA@4 JMP MSG_LOOP END_LOOP: