Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Programming_Windows_95_Part_I.pdf
Скачиваний:
96
Добавлен:
05.06.2014
Размер:
4.61 Mб
Скачать

178

после того, как состояние переключателя было изменено. Если вам действительно нужна информация о текущем положении клавиши, вы можете использовать функцию GetAsyncKeyState.

Использование сообщений клавиатуры

Идея программы, получающей информацию о нажатии любой клавиши несомненно привлекательна; однако, большинство программ для Windows игнорируют все, кроме нескольких сообщений о нажатии и отпускании клавиш. Сообщения WM_SYSKEYUP и WM_SYSKEYDOWN адресованы системным функциям Windows, и вам не нужно их отслеживать. Если вы обрабатываете сообщения WM_KEYDOWN, то сообщения WM_KEYUP вам обычно также можно игнорировать.

Программы для Windows обычно используют сообщения WM_KEYDOWN для нажатия и отпускания клавиш, которые не генерируют символьные сообщения. Хотя вы можете подумать, что есть возможность использовать сообщения о нажатии клавиш в сочетании с информацией о состоянии клавиш сдвига для преобразования сообщений о нажатии клавиш в символьные сообщения, не делайте так. У вас будут проблемы из-за отличий международных клавиатур. Например, если вы получаете сообщение WM_KEYDOWN с wParam равным 33H, вы знаете, что пользователь нажал клавишу <3>. Так, хорошо. Если вы используете GetKeyState и обнаруживаете, что клавиша <Shift> нажата, то можно было бы предположить, что пользователь печатает знак фунта стерлингов <£>. Вовсе необязательно. Британский пользователь печатает знак <&>. Поэтому сообщения WM_KEYDOWN более удобны для клавиш управления курсором, функциональных клавиш и специальных клавиш, таких как <Insert> и <Delete>. Однако, иногда клавиши <Insert> и <Delete>, а также функциональные клавиши используются в качестве быстрых клавиш меню. Поскольку Windows преобразует быстрые клавиши меню в сообщения команд меню, вы не должны сами обрабатывать эти сообщения. Некоторые программы, написанные не для Windows, широко используют функциональные клавиши в сочетании с клавишами <Shift>, <Ctrl> и <Alt>. Вы можете сделать что-то похожее в ваших программах для Windows, но это не рекомендуется. Если вы хотите использовать функциональные клавиши, то лучше, чтобы они дублировали команды меню. Одна из задач Windows — обеспечить такой пользовательский интерфейс, для которого не требуется заучивание или использование сложного набора команд.

Мы собираемся отказаться от всего, за исключением последнего пункта: большую часть времени вы будете обрабатывать сообщения WM_KEYDOWN только для клавиш управления курсором. Если вы используете клавиши управления курсором, то можете контролировать состояние клавиш <Shift> и <Ctrl> с помощью функции GetKeyState. Функции Windows часто используют клавишу <Shift> в сочетании с клавишами управления курсором для расширения выбора, например, в программах текстовых редакторов. Клавиша <Ctrl> часто используется для изменения значения клавиш управления курсором. (Например, <Ctrl> в сочетании с клавишей стрелки вправо могло бы означать перемещение курсора на одно слово вправо.)

Одним из лучших способов выяснить то, как использовать клавиатуру должно быть изучение использования клавиатуры в существующих популярных программах для Windows. Если вам это не подходит, можете действовать как-то иначе. Но запомните, что в этом случае вы можете помешать пользователю быстро изучить вашу программу.

Модернизация SYSMETS: добавление интерфейса клавиатуры

Когда в главе 3 мы написали три версии программы SYSMETS, мы ничего не знали о клавиатуре. Мы могли прокрутить текст только с помощью мыши на полосе прокрутки. Теперь мы знаем, как обрабатывать сообщения клавиатуры, давайте добавим интерфейс клавиатуры в программу SYSMETS. Это очевидно будет работа для клавиш управления курсором. Мы используем большинство клавиш управления курсором <Home>, <End>, <PageUp>, <PageDown>, <> и <> для вертикальной прокрутки. Клавиши <> и <> можно оставить на менее важную горизонтальную прокрутку.

Логика обработки сообщений WM_KEYDOWN

Один из простейших способов создать интерфейс клавиатуры — это использовать логику обработки сообщений WM_KEYDOWN в оконной процедуре, которая будет работать параллельно с логикой обработки сообщений

WM_VSCROLL и WM_HSCROLL:

case WM_KEYDOWN

iVscrollInc = iHscrollInc = 0; switch(wParam)

{

case VK_HOME: // аналогично WM_VSCROLL, SB_TOP iVscrollInc = — iVscrollPos;

break;

179

case VK_END: // аналогично WM_VSCROLL, SB_BOTTOM iVscrollInc = iVscrollMax — iVscrollPos; break;

case VK_UP: // аналогично WM_VSCROLL, SB_LINEUP iVscrollInc = — 1;

break;

case VK_DOWN: // аналогично WM_VSCROLL, SB_LINEDOWN iVscrollInc = 1;

break;

case VK_PRIOR: // аналогично WM_VSCROLL, SB_PAGEUP iVscrollInc = min(-1, — cyClient / cyChar); break;

case VK_NEXT: // аналогично WM_VSCROLL, SB_PAGEDOWN iVscrollInc = max(1, cyClient / cyChar); break;

case VK_LEFT: // аналогично WM_HSCROLL, SB_PAGEUP iHscrollInc = — 8;

break;

case VK_RIGHT:// аналогично WM_HSCROLL, SB_PAGEDOWN iHscrollInc = 8;

break;

default:

break;

}

if (iVscrollInc = max(- iVscrollPos, min(iVscrollInc, iVscrollMax — iVscrollPos)))

{

iVscrollPos += iVscrollInc;

ScrollWindow(hwnd, 0, — cyChar * iVscrollInc, NULL, NULL); SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE); UpdateWindow(hwnd);

}

if (iHscrollInc = max(- iHscrollPos, min(iHscrollInc, iHscrollMax — iHscrollPos)))

{

iHscrollPos += iHscrollInc;

ScrollWindow(hwnd, — cxChar * iHscrollInc, 0, NULL, NULL); SetScrollPos(hwnd, SB_HORZ, iHscrollPos, TRUE);

}

return 0;

Вам тоже не нравится эта программа? Простое дублирование всех инструкций предыдущей программы занятие глупое, поскольку, если когда-нибудь захочется изменить логику работы полос прокрутки, то придется делать параллельные изменения в логике WM_KEYDOWN. Должен быть лучший способ. И он есть.

Посылка асинхронных сообщений

Не лучше было бы просто преобразовать каждое из этих сообщений WM_KEYDOWN в эквивалентное сообщение WM_VSCROLL и WM_HSCROLL и, таким образом, быть может, обмануть оконную процедуру WndProc, чтобы ей казалось, что она получает сообщения полосы прокрутки WM_VSCROLL или WM_HSCROLL? Windows позволяет это сделать. Функция называется SendMessage, и имеет те же параметры, что и параметры, передаваемые в оконную процедуру:

SendMessage(hwnd, message, wParam, lParam);

Когда вы вызываете SendMessage, то Windows вызывает оконную процедуру с описателем окна hwnd, передавая ей эти четыре параметра. После того, как оконная процедура заканчивает обработку сообщения, Windows передает управление следующей за вызовом SendMessage инструкции. Оконная процедура, которой вы отправляете синхронное сообщение, может быть той же самой оконной процедурой, другой оконной процедурой той же программы, или даже оконной процедурой другого приложения.

Далее показано, как можно было бы использовать SendMessage для обработки WM_KEYDOWN в программе

SYSMETS:

180

case WM_KEYDOWN: switch(wParam)

{

case VK_HOME:

SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0L); break;

case VK_END:

SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0L); break;

case VK_PRIOR:

SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0L); break;

[остальные строки программы]

Теперь вы поняли основную идею. Нашей целью было добавить интерфейс клавиатуры к полосам прокрутки, и мы это сделали. Мы продублировали логику обработки сообщений полос прокрутки обработкой сообщений о нажатии клавиш управления курсором, фактически передавая оконной процедуре синхронное сообщение полос прокрутки. Теперь вы понимаете, почему в программу SYSMETS3 включена обработка SB_TOP и SB_BOTTOM для сообщений WM_VSCROLL. Тогда это не использовалось, а сейчас используется для обработки клавиш Home и End. Последняя программа SYSMETS, показанная на рис. 5.2, включает в себя все эти изменения. Вам для компиляции этой программы понадобится также файл SYSMETS.H из главы 3 (рис. 3.4).

SYSMETS.MAK

#-----------------------

# SYSMETS.MAK make file

#-----------------------

sysmets.exe : sysmets.obj

$(LINKER) $(GUIFLAGS) -OUT:sysmets.exe sysmets.obj $(GUILIBS)

sysmets.obj : sysmets.c sysmets.h

$(CC) $(CFLAGS) sysmets.c

SYSMETS.C

/*-----------------------------------------------------

SYSMETS.C -- System Metrics Display Program(Final)

(c) Charles Petzold, 1996

-----------------------------------------------------*/

#include <windows.h> #include <string.h> #include "sysmets.h"

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)

{

static char szAppName[] = "SysMets";

HWND

hwnd;

MSG

msg;

WNDCLASSEX

wndclass;

wndclass.cbSize

= sizeof(wndclass);

wndclass.style

= CS_HREDRAW | CS_VREDRAW;

wndclass.lpfnWndProc

= WndProc;

wndclass.cbClsExtra

= 0;

wndclass.cbWndExtra

= 0;

wndclass.hInstance

= hInstance;

wndclass.hIcon

= LoadIcon(NULL, IDI_APPLICATION);

wndclass.hCursor

= LoadCursor(NULL, IDC_ARROW);

wndclass.hbrBackground

=(HBRUSH) GetStockObject(WHITE_BRUSH);

wndclass.lpszMenuName

= NULL;

181

wndclass.lpszClassName

=

szAppName;

wndclass.hIconSm

=

LoadIcon(NULL, IDI_APPLICATION);

RegisterClassEx(&wndclass);

hwnd = CreateWindow(szAppName, "System Metrics", WS_OVERLAPPEDWINDOW | WS_VSCROLL | WS_HSCROLL, CW_USEDEFAULT, CW_USEDEFAULT,

CW_USEDEFAULT, CW_USEDEFAULT,

NULL, NULL, hInstance, NULL);

ShowWindow(hwnd, iCmdShow);

UpdateWindow(hwnd);

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

{

TranslateMessage(&msg);

DispatchMessage(&msg);

}

return msg.wParam;

}

LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)

{

 

static int

cxChar, cxCaps, cyChar, cxClient, cyClient, iMaxWidth,

 

iVscrollPos, iVscrollMax, iHscrollPos, iHscrollMax;

char

szBuffer[10];

HDC

hdc;

int

i, x, y, iPaintBeg, iPaintEnd, iVscrollInc, iHscrollInc;

PAINTSTRUCT

ps;

TEXTMETRIC

tm;

switch(iMsg)

{

case WM_CREATE :

hdc = GetDC(hwnd);

GetTextMetrics(hdc, &tm); cxChar = tm.tmAveCharWidth;

cxCaps =(tm.tmPitchAndFamily & 1 ? 3 : 2) * cxChar / 2; cyChar = tm.tmHeight + tm.tmExternalLeading;

ReleaseDC(hwnd, hdc);

iMaxWidth = 40 * cxChar + 22 * cxCaps; return 0;

case WM_SIZE :

cxClient = LOWORD(lParam); cyClient = HIWORD(lParam);

iVscrollMax = max(0, NUMLINES + 2 - cyClient / cyChar); iVscrollPos = min(iVscrollPos, iVscrollMax);

SetScrollRange(hwnd, SB_VERT, 0, iVscrollMax, FALSE);

SetScrollPos (hwnd, SB_VERT, iVscrollPos, TRUE);

iHscrollMax = max(0, 2 +(iMaxWidth - cxClient) / cxChar); iHscrollPos = min(iHscrollPos, iHscrollMax);

SetScrollRange(hwnd, SB_HORZ, 0, iHscrollMax, FALSE); SetScrollPos (hwnd, SB_HORZ, iHscrollPos, TRUE); return 0;

182

case WM_VSCROLL : switch(LOWORD(wParam))

{

case SB_TOP :

iVscrollInc = -iVscrollPos; break;

case SB_BOTTOM :

iVscrollInc = iVscrollMax - iVscrollPos; break;

case SB_LINEUP : iVscrollInc = -1; break;

case SB_LINEDOWN : iVscrollInc = 1; break;

case SB_PAGEUP :

iVscrollInc = min(-1, -cyClient / cyChar); break;

case SB_PAGEDOWN :

iVscrollInc = max(1, cyClient / cyChar); break;

case SB_THUMBTRACK :

iVscrollInc = HIWORD(wParam) - iVscrollPos; break;

default : iVscrollInc = 0;

}

iVscrollInc = max(-iVscrollPos,

min(iVscrollInc, iVscrollMax - iVscrollPos));

if(iVscrollInc != 0)

{

iVscrollPos += iVscrollInc;

ScrollWindow(hwnd, 0, -cyChar * iVscrollInc, NULL, NULL);

SetScrollPos(hwnd, SB_VERT, iVscrollPos, TRUE);

UpdateWindow(hwnd);

}

return 0;

case WM_HSCROLL : switch(LOWORD(wParam))

{

case SB_LINEUP : iHscrollInc = -1; break;

case SB_LINEDOWN : iHscrollInc = 1; break;

case SB_PAGEUP : iHscrollInc = -8; break;

case SB_PAGEDOWN : iHscrollInc = 8; break;

case SB_THUMBPOSITION :

183

iHscrollInc = HIWORD(wParam) - iHscrollPos; break;

default : iHscrollInc = 0;

}

iHscrollInc = max(-iHscrollPos,

min(iHscrollInc, iHscrollMax - iHscrollPos));

if(iHscrollInc != 0)

{

iHscrollPos += iHscrollInc;

ScrollWindow(hwnd, -cxChar * iHscrollInc, 0, NULL, NULL);

SetScrollPos(hwnd, SB_HORZ, iHscrollPos, TRUE);

}

return 0;

case WM_KEYDOWN : switch(wParam)

{

case VK_HOME :

SendMessage(hwnd, WM_VSCROLL, SB_TOP, 0L); break;

case VK_END :

SendMessage(hwnd, WM_VSCROLL, SB_BOTTOM, 0L); break;

case VK_PRIOR :

SendMessage(hwnd, WM_VSCROLL, SB_PAGEUP, 0L); break;

case VK_NEXT :

SendMessage(hwnd, WM_VSCROLL, SB_PAGEDOWN, 0L); break;

case VK_UP :

SendMessage(hwnd, WM_VSCROLL, SB_LINEUP, 0L); break;

case VK_DOWN :

SendMessage(hwnd, WM_VSCROLL, SB_LINEDOWN, 0L); break;

case VK_LEFT :

SendMessage(hwnd, WM_HSCROLL, SB_PAGEUP, 0L); break;

case VK_RIGHT :

SendMessage(hwnd, WM_HSCROLL, SB_PAGEDOWN, 0L); break;

}

return 0;

case WM_PAINT :

hdc = BeginPaint(hwnd, &ps);

iPaintBeg = max(0, iVscrollPos + ps.rcPaint.top / cyChar - 1); iPaintEnd = min(NUMLINES,

iVscrollPos + ps.rcPaint.bottom / cyChar);

for(i = iPaintBeg; i < iPaintEnd; i++)

{

x = cxChar *(1 - iHscrollPos);

y = cyChar *(1 - iVscrollPos + i);

Соседние файлы в предмете Операционные системы