95 в двух томах Том I



Pdf просмотр
страница19/41
Дата27.11.2016
Размер4.32 Mb.
Просмотров7754
Скачиваний0
ТипРеферат
1   ...   15   16   17   18   19   20   21   22   ...   41
, ,
,
, <

> и <

> для вертикальной прокрутки. Клавиши <

> и <

> можно оставить на менее важную горизонтальную прокрутку.
Логика обработки сообщений 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
#include
#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);

184
TextOut(hdc, x, y, sysmetrics[i].szLabel, strlen(sysmetrics[i].szLabel));
TextOut(hdc, x + 22 * cxCaps, y, sysmetrics[i].szDesc, strlen(sysmetrics[i].szDesc));
SetTextAlign(hdc, TA_RIGHT | TA_TOP);
TextOut(hdc, x + 22 * cxCaps + 40 * cxChar, y, szBuffer, wsprintf(szBuffer, "%5d",
GetSystemMetrics(sysmetrics[i].iIndex)));
SetTextAlign(hdc, TA_LEFT | TA_TOP);
}
EndPaint(hwnd, &ps); return 0; case WM_DESTROY :
PostQuitMessage(0); return 0;
} return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
Рис. 5.2 Программа SYSMETS
Символьные сообщения
Ранее рассматривалась идея преобразования аппаратных сообщений клавиатуры в символьные сообщения путем учета информации о положении клавиш сдвига и предупреждалось, что информации о положении этих клавиш недостаточно: вам необходимо также знать об особенностях реализации вашей национальной клавиатуры. По этой причине вам не следует пытаться самостоятельно преобразовывать аппаратные сообщения клавиатуры в символьные коды.
Для вас это делает Windows. Вы уже встречали такой код раньше: while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
Это типичный цикл обработки сообщений, содержащийся в WinMain. Функция GetMessage заполняет поля структуры msg данными следующего сообщения из очереди. Вызов DispatchMessage вызывает соответствующую оконную процедуру.
Между двумя этими функциями находится функция TranslateMessage, преобразующая аппаратные сообщения клавиатуры в символьные сообщения. Если этим сообщением является WM_KEYDOWN или
WM_SYSKEYDOWN и, если нажатие клавиши в сочетании с положением клавиши сдвига генерирует символ, тогда TranslateMessage помещает символьное сообщение в очередь сообщений. Это символьное сообщение будет следующим, после сообщения о нажатии клавиши, которое функция GetMessage извлечет из очереди сообщений.
Существует четыре символьных сообщения:
Символы
Немые символы
Несистемные символы: WM_CHAR
WM_DEADCHAR
Системные символы: WM_SYSCHAR
WM_SYSDEADCHAR
Сообщения WM_CHAR и WM_DEADCHAR являются следствием сообщений WM_KEYDOWN. Сообщения
WM_SYSCHAR и WM_SYSDEADCHAR являются следствием сообщений WM_SYSKEYDOWN. В большинстве случаев ваши программы для Windows могут игнорировать все сообщения, за исключением WM_CHAR. Параметр

185
lParam, передаваемый в оконную процедуру как часть символьного сообщения, является таким же, как параметр
lParam аппаратного сообщения клавиатуры, из которого сгенерировано символьное сообщение. Параметр wParam
— это код символа ASCII.
Символьные сообщения доставляются в вашу оконную процедуру в промежутке между аппаратными сообщениями клавиатуры. Например, если переключатель не включен и вы нажимаете и отпускаете клавишу <А>, оконная процедура получит три следующих сообщения:
Сообщение
Клавиша или код
WM_KEYDOWN
Виртуальная клавиша А
WM_CHAR ASCII код а
WM_KEYUP
Виртуальная клавиша А
Если вы набираете прописное ‘А’, удерживая клавишу , нажимая клавишу <А>, отпуская клавишу <А>, и затем отпуская клавишу , оконная процедура получит пять сообщений:
Сообщение
Клавиша или код
WM_KEYDOWN
Виртуальная клавиша VK_SHIFT
WM_KEYDOWN
Виртуальная клавиша А
WM_CHAR ASCII код А
WM_KEYUP
Виртуальная клавиша А
WM_KEYUP
Виртуальная клавиша VK_SHIFT
Сама по себе клавиша не вырабатывает символьного сообщения.
Если вы удерживаете клавишу <А> нажатой так, что автоповтор генерирует аппаратные сообщения клавиатуры, то на каждое сообщение WM_KEYDOWN, вы получите символьное сообщение:
Сообщение
Клавиша или код
WM_KEYDOWN
Виртуальная клавиша А
WM_CHAR ASCII код А
WM_KEYDOWN
Виртуальная клавиша А
WM_CHAR ASCII код А
WM_KEYDOWN
Виртуальная клавиша А
WM_CHAR ASCII код А
WM_KEYDOWN
Виртуальная клавиша А
WM_CHAR ASCII код А
WM_KEYUP
Виртуальная клавиша А
Если у некоторых сообщений WM_KEYDOWN счетчик повторений больше 1, то у соответствующих сообщений
WM_CHAR счетчик повторений будет иметь то же значение.
Клавиша в сочетании с буквенной клавишей генерирует управляющие коды ASCII от 01H (+) до
1AH (+). Для выработки таких управляющих кодов вы можете также использовать и другие клавиши. В следующей таблице показано значение wParam в сообщении WM_CHAR для клавиш, вырабатывающих управляющие коды.
Клавиша ASCII код
Дублирующая комбинация клавиш
Backspace 08H
Ctrl-H
Tab 09H Ctrl-I
Ctrl-Enter 0AH
Ctrl-J
Enter 0DH Ctrl-M
Esc
1BH Ctrl-[
В программах для Windows клавиша иногда используется с клавишами букв в качестве быстрых клавиш, в таком случае сообщения от буквенных клавиш не преобразуются в символьные сообщения.
Сообщения WM_CHAR
Если вашей Windows-программе необходимо обрабатывать символы клавиатуры (например, в программах обработки текстов или коммуникационных программах), то она будет обрабатывать сообщения WM_CHAR.
Вероятно, вы захотите как-то по особому обрабатывать клавиши , и (и может быть клавишу ), но все остальные символы вы будете обрабатывать похожим образом: case WM_CHAR: switch(wParam)
{ case '\b':
// забой
(Backspace)

186
[другие строки программы]

break; case '\t':
// табуляция
(Tab)
[другие строки программы]

break; case '\n':
// перевод строки
(Linefeed)
[другие строки программы]

break; case '\r':
// возврат каретки
(Enter)
[другие строки программы]

break; default:
// символьный код
[другие строки программы]

break;
} return 0;
Этот фрагмент программы фактически идентичен обработке символов клавиатуры в обычных программах
MS_DOS.
Сообщения немых символов
Программы для Windows обычно могут игнорировать сообщения WM_DEADCHAR и WM_SYSDEADCHAR. На некоторых, не американских клавиатурах, некоторые клавиши определяются добавлением диакритического знака к букве. Они называются "немыми клавишами" (dead keys), поскольку эти клавиши сами по себе не определяют символов. Например, при инсталляции немецкой клавиатуры, клавиша, находящаяся там же, где в американской клавиатуре находится клавиша <+/=>, становится немой клавишей для указания высокого звука (

), при ненажатой клавише сдвига, и низкого звука (`), при нажатой клавише сдвига.
Если пользователь нажимает немую клавишу, оконная процедура получает сообщение WM_DEADCHAR с параметром wParam равным коду ASCII самого диакритического знака. Когда затем пользователь нажимает клавишу буквы (например, клавишу <А>), оконная процедура получает сообщение WM_CHAR, где параметр
wParam равен коду ASCII буквы с
диакритическим знаком. Таким образом, ваша программа не должна обрабатывать сообщение WM_DEADCHAR, поскольку сообщение WM_CHAR и так дает программе всю необходимую информацию. Windows имеет даже встроенную систему отслеживания ошибок: если за немой клавишей следует буква, у которой не может быть диакритического знака (например буква s), то оконная процедура получает два сообщения WM_CHAR подряд — первое с wParam равным коду ASCII самого диакритического знака (такое же значение wParam, как было передано с сообщением WM_DEADCHAR) и второе
wParam равным коду ASCII буквы s.
Взгляд на сообщения от клавиатуры
Если вы захотели бы узнать, как Windows посылает сообщения клавиатуры в программу, то программа KEYLOOK
(показанная на рис. 5.3) вам поможет. Эта программа выводит в рабочую область всю информацию, которую
Windows посылает оконной процедуре для восьми различных сообщений клавиатуры.
KEYLOOK.MAK
#-----------------------
# KEYLOOK.MAK make file
#----------------------- keylook.exe : keylook.obj
$(LINKER) $(GUIFLAGS) -OUT:keylook.exe keylook.obj $(GUILIBS) keylook.obj : keylook.c
$(CC) $(CFLAGS) keylook.c
KEYLOOK.C
/*-------------------------------------------------------
KEYLOOK.C -- Displays Keyboard and Character Messages
(c) Charles Petzold, 1996
-------------------------------------------------------*/

187
#include
#include
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
RECT rect; int cxChar, cyChar; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{ static char szAppName[] = "KeyLook";
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; wndclass.lpszClassName = szAppName; wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wndclass); hwnd = CreateWindow(szAppName, "Keyboard Message Looker",
WS_OVERLAPPEDWINDOW,
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;
} void ShowKey(HWND hwnd, int iType, char *szMessage,
WPARAM wParam, LPARAM lParam)
{ static char *szFormat[2] = { "%-14s %3d %c %6u %4d %3s %3s %4s %4s",
"%-14s %3d %c %6u %4d %3s %3s %4s %4s" }; char szBuffer[80];
HDC hdc;
ScrollWindow(hwnd, 0, -cyChar, &rect, &rect); hdc = GetDC(hwnd);
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
TextOut(hdc, cxChar, rect.bottom - cyChar, szBuffer, wsprintf(szBuffer, szFormat [iType],

188 szMessage, wParam,
(BYTE)(iType ? wParam : ' '),
LOWORD(lParam),
HIWORD(lParam) & 0xFF,
(PSTR)(0x01000000 & lParam ? "Yes" : "No"),
(PSTR)(0x20000000 & lParam ? "Yes" : "No"),
(PSTR)(0x40000000 & lParam ? "Down" : "Up"),
(PSTR)(0x80000000 & lParam ? "Up" : "Down")));
ReleaseDC(hwnd, hdc);
ValidateRect(hwnd, NULL);
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{ static char szTop[] =
"Message Key Char Repeat Scan Ext ALT Prev Tran"; static char szUnd[] =
"_______ ___ ____ ______ ____ ___ ___ ____ ____";
HDC hdc;
PAINTSTRUCT ps;
TEXTMETRIC tm; switch(iMsg)
{ case WM_CREATE : hdc = GetDC(hwnd);
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
GetTextMetrics(hdc, &tm); cxChar = tm.tmAveCharWidth; cyChar = tm.tmHeight;
ReleaseDC(hwnd, hdc); rect.top = 3 * cyChar / 2; return 0; case WM_SIZE : rect.right = LOWORD(lParam); rect.bottom = HIWORD(lParam);
UpdateWindow(hwnd); return 0; case WM_PAINT :
InvalidateRect(hwnd, NULL, TRUE); hdc = BeginPaint(hwnd, &ps);
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
SetBkMode(hdc, TRANSPARENT);
TextOut(hdc, cxChar, cyChar / 2, szTop,(sizeof szTop) - 1);
TextOut(hdc, cxChar, cyChar / 2, szUnd,(sizeof szUnd) - 1);
EndPaint(hwnd, &ps); return 0; case WM_KEYDOWN :
ShowKey(hwnd, 0, "WM_KEYDOWN", wParam, lParam); return 0; case WM_KEYUP :
ShowKey(hwnd, 0, "WM_KEYUP", wParam, lParam); return 0;

189 case WM_CHAR :
ShowKey(hwnd, 1, "WM_CHAR", wParam, lParam); return 0; case WM_DEADCHAR :
ShowKey(hwnd, 1, "WM_DEADCHAR", wParam, lParam); return 0; case WM_SYSKEYDOWN :
ShowKey(hwnd, 0, "WM_SYSKEYDOWN", wParam, lParam); break; // ie, call DefWindowProc case WM_SYSKEYUP :
ShowKey(hwnd, 0, "WM_SYSKEYUP", wParam, lParam); break; // ie, call DefWindowProc case WM_SYSCHAR :
ShowKey(hwnd, 1, "WM_SYSCHAR", wParam, lParam); break; // ie, call DefWindowProc case WM_SYSDEADCHAR :
ShowKey(hwnd, 1, "WM_SYSDEADCHAR", wParam, lParam); break; // ie, call DefWindowProc case WM_DESTROY :
PostQuitMessage(0); return 0;
} return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
Рис. 5.3 Программа KEYLOOK
Дисплей в программе KEYLOOK используется также, как устаревшее устройство вывода информации — телетайп.
Когда KEYLOOK получает аппаратные сообщения клавиатуры, вызывается функция ScrollWindow для прокрутки содержимого всей рабочей области окна так, чтобы это содержимое сместилось вверх на высоту одного символа.
Функция TextOut используется для вывода строки новой информации на экран, начиная с высоты одного символа от нижнего края рабочей области. Это почти также просто, как может происходить вывод информации на телетайпе. На рис. 5.4 показано, как выглядит окно программы KEYLOOK, когда вы печатаете слово "Windows". В первом столбце показаны сообщения клавиатуры, во втором — коды виртуальных клавиш для аппаратных сообщений клавиатуры, в третьем — коды символов (и сами символы) для символьных сообщений и, наконец, в шести оставшихся столбцах показано состояние шести полей параметра сообщения lParam.
Рис. 5.4 Вывод на экран программы KEYLOOK
В большей части KEYLOOK.С используются те возможности Windows, которые уже были рассмотрены в различных программах SYSMETS, но также здесь используются несколько новых функций. Отметьте однако, что форматирование выводимого текста программы KEYLOOK по столбцам было бы затруднительно при

190 использовании задаваемого по умолчанию пропорционального шрифта. Для того, чтобы получить выравненное изображение, код для вывода каждой строки пришлось бы разбить на девять секций. Чтобы избежать всех этих трудностей, гораздо легче просто использовать фиксированный шрифт. Как уже говорилось в последней главе, для этого нужны две функции, которые здесь объединены в одну инструкцию:
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
Программа KEYLOOK вызывает эти две функции везде, где она получает контекст устройства. Это происходит в трех местах: в функции ShowKey, при обработке сообщения WM_CREATE в WndProc и при обработке сообщения
WM_PAINT. Функция GetStockObject получает описатель стандартного графического объекта, каковым является фиксированный шрифт, используемый в ранних версиях Windows, предшествовавших Windows 3.0. Вызов функции SelectObject выбирает этот объект в контекст устройства. Благодаря этому вызову, весь текст будет выводиться на экран фиксированным шрифтом. Вернуться обратно к пропорциональному шрифту можно с помощью функции:
SelectObject(hdc, GetStockObject(SYSTEM_FONT));
Функция ShowKey вызывает ScrollWindow для прокрутки вверх предыдущих строк перед выводом новой строки.
Обычно это приводит к тому, что часть окна становится недействительной, и следовательно генерируется сообщение WM_PAINT. Для того, чтобы этого избежать, в функцию ShowKey включен вызов функции
ValidateRect.
Программа KEYLOOK не хранит полученные аппаратные сообщения клавиатуры, поэтому по получении сообщения
WM_PAINT она не может перерисовать окно. По этой причине KEYLOOK просто выводит заголовок таблицы в верхней части рабочей области при обработке сообщения WM_PAINT. Перед вызовом функции BeginPaint, при обработке сообщения WM_PAINT, KEYLOOK делает недействительным все окно. Это позволяет стереть все окно, вместо того, чтобы стирать недействительный прямоугольник.
(То, что программа KEYLOOK не хранит полученные аппаратные сообщения клавиатуры, и следовательно не может перерисовать окно, пока обрабатывается сообщение WM_PAINT, несомненно является недостатком. В программе TYPER, показанной далее в этой главе, эта недостаток устранен.)
Вверху рабочей области программа KEYLOOK рисует заголовок таблицы и, таким образом, идентифицирует девять столбцов. Хотя можно создать шрифт, в котором символы будут подчеркнуты, здесь применяется немного другой подход. Определены две переменные типа строка символов, которые называются szTop (в ней содержится текст) и
szUnd (в ней содержатся символы подчеркивания) и при обработке сообщения WM_PAINT они выводятся в одну и ту же позицию в верхней части окна. Обычно Windows выводит текст в режиме "opaque", означающим, что
Windows обновляет область фона символа при его выводе на экран. Использование этого режима фона может привести к тому, что вторая символьная строка (szUnd) сотрет первую (szTop). Чтобы предотвратить это, переключите контекст устройства в режим "transparent" (режим без заполнения фона символов):
SetBkMode(hdc, TRANSPARENT);
Каретка (не курсор)
Когда вы в программе набираете текст, обычно маленький символ подчеркивания или маленький четырехугольник показывает вам место, где следующий набираемый вами символ появится на экране. Вы можете считать, что это курсор (cursor), но, если вы программируете для Windows, необходимо отказаться от такого представления. В
Windows это называется "каретка" (caret). Слово же "курсор" относится к битовому образу, отражающему положение мыши на экране.
Функции работы с кареткой
Здесь перечислены пять основных функций работы с кареткой:

CreateCaret — создает связанную с окном каретку.

SetCaretPos — устанавливает положение каретки в окне.

ShowCaret — показывает каретку.

HideCaret — прячет каретку.

DestroyCaret — удаляет каретку.
Кроме этих, еще имеется функция получения положения каретки (GetCaretPos) и функции установки и получения частоты мигания каретки (GetCaretBlinkTime) и (SetCaretBlinkTime).
Каретка — это обычно горизонтальная черточка или прямоугольник, имеющие размер символа, или вертикальная черточка. Вертикальная черточка рекомендуется при использовании пропорционального шрифта, такого как задаваемый по умолчанию системный шрифт Windows. Поскольку размер символов пропорционального шрифта не фиксирован, горизонтальной черточке и прямоугольнику невозможно задать размер символа.

191
Вы не можете просто создать каретку при обработке сообщения WM_CREATE и удалить ее при обработке сообщения WM_DESTROY. Каретка — это то, что называется "общесистемным ресурсом" (systemwide resource). Это означает, что в системе имеется только одна каретка. И, как результат, программа при необходимости вывода каретки на экран своего окна "заимствует" ее у системы.
Серьезно ли это необычное ограничение? Конечно нет. Подумайте: появление каретки в окне имеет смысл только в том случае, если окно имеет фокус ввода. Каретка показывает пользователю, что он может вводить в программу текст. В каждый конкретный момент времени только одно окно имеет фокус ввода, поэтому для всей системы и нужна только одна каретка.
Обрабатывая сообщения WM_SETFOCUS и WM_KILLFOCUS, программа может определить, имеет ли она фокус ввода. Оконная процедура получает сообщение WM_SETFOCUS, когда получает фокус ввода, а сообщение
WM_KILLFOCUS, когда теряет фокус ввода. Эти сообщения приходят попарно: оконная процедура получает сообщение WM_SETFOCUS всегда до того, как получит сообщение WM_KILLFOCUS, и она всегда получает одинаковое количество сообщений WM_SETFOCUS и WM_KILLFOCUS за время существования окна.
Основное правило использования каретки выглядит просто: оконная процедура вызывает функцию CreateCaret при обработке сообщения WM_SETFOCUS и функцию DestroyCaret при обработке сообщения WM_KILLFOCUS.
Имеется несколько других правил: каретка создается скрытой. После вызова функции CreateCaret, оконная процедура должна вызвать функцию ShowCaret, чтобы сделать каретку видимой. В дополнение к этому, оконная процедура, когда она рисует в своем окне при обработке сообщения, отличного от WM_PAINT, должна скрыть каретку, вызвав функцию HideCaret. После того как оконная процедура закончит рисовать в своем окне, она вызывает функцию ShowCaret, чтобы снова вывести каретку на экран. Функция HideCaret имеет дополнительный эффект: если вы несколько раз вызываете HideCaret, не вызывая при этом ShowCaret, то чтобы каретка снова стала видимой, вам придется такое же количество раз вызвать функцию ShowCaret.
Программа TYPER
Программа TYPER, представленная на рис. 5.5, объединяет вместе многое из того, о чем мы узнали в этой главе.
Программу TYPER можете считать сильно устаревшим текстовым редактором. Вы можете набрать в окне текст, перемещая курсор (имеется ввиду каретка) по экрану с помощью клавиш управления курсором (или клавишами управления кареткой?), и стереть содержимое окна, нажав . Окно также очистится при изменении размеров окна. Здесь нет ни прокрутки, ни поиска и замены, ни возможности сохранять файлы, ни контроля правописания, но это только начало.
Для простоты в программе TYPER используется фиксированный шрифт. Создание текстового редактора для пропорционального шрифта является, как вы могли бы сообразить, гораздо более трудным делом. Программа получает контекст устройства в нескольких местах: при обработке сообщений WM_CREATE, WM_KEYDOWN,
WM_CHAR и WM_PAINT. Каждый раз при этом для выбора фиксированного шрифта вызываются функции
GetStockObject и SelectObject.
При обработке сообщения WM_SIZE программа TYPER рассчитывает ширину и высоту окна в символах и хранит эти значения в переменных cxBuffer и cyBuffer. Затем она использует функцию malloc, чтобы выделить буфер для хранения всех символов, которые могут быть напечатаны в окне. В переменных xCaret и yCaret сохраняется положение каретки в символах.
При обработке сообщения WM_SETFOCUS программа TYPER вызывает функцию CreateCaret для создания каретки, имеющей ширину и высоту символа, функцию SetCaretPos для установки положения каретки и функцию
ShowCaret, чтобы сделать каретку видимой. При обработке сообщения WM_KILLFOCUS программа вызывает функции HideCaret и DestroyCaret.
TYPER.MAK
#---------------------
# TYPER.MAK make file
#--------------------- typer.exe : typer.obj
$(LINKER) $(GUIFLAGS) -OUT:typer.exe typer.obj $(GUILIBS) typer.obj : typer.c
$(CC) $(CFLAGS) typer.c
TYPER.C
/*--------------------------------------
TYPER.C -- Typing Program

192
(c) Charles Petzold, 1996
--------------------------------------*/
#include
#include
#define BUFFER(x,y) *(pBuffer + y * cxBuffer + x)
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{ static char szAppName[] = "Typer";
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; wndclass.lpszClassName = szAppName; wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wndclass); hwnd = CreateWindow(szAppName, "Typing Program",
WS_OVERLAPPEDWINDOW,
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 char *pBuffer = NULL; static int cxChar, cyChar, cxClient, cyClient, cxBuffer, cyBuffer, xCaret, yCaret;
HDC hdc; int x, y, i;
PAINTSTRUCT ps;
TEXTMETRIC tm; switch(iMsg)
{ case WM_CREATE : hdc = GetDC(hwnd);

193
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));
GetTextMetrics(hdc, &tm); cxChar = tm.tmAveCharWidth; cyChar = tm.tmHeight;
ReleaseDC(hwnd, hdc); return 0; case WM_SIZE :
// obtain window size in pixels cxClient = LOWORD(lParam); cyClient = HIWORD(lParam);
// calculate window size in characters cxBuffer = max(1, cxClient / cxChar); cyBuffer = max(1, cyClient / cyChar);
// allocate memory for buffer and clear it if(pBuffer != NULL) free(pBuffer); if((pBuffer =(char *) malloc(cxBuffer * cyBuffer)) == NULL)
MessageBox(hwnd, "Window too large. Cannot "
"allocate enough memory.", "Typer",
MB_ICONEXCLAMATION | MB_OK); else for(y = 0; y < cyBuffer; y++) for(x = 0; x < cxBuffer; x++)
BUFFER(x,y) = ' ';
// set caret to upper left corner xCaret = 0; yCaret = 0; if(hwnd == GetFocus())
SetCaretPos(xCaret * cxChar, yCaret * cyChar); return 0; case WM_SETFOCUS :
// create and show the caret
CreateCaret(hwnd, NULL, cxChar, cyChar);
SetCaretPos(xCaret * cxChar, yCaret * cyChar);
ShowCaret(hwnd); return 0; case WM_KILLFOCUS :
// hide and destroy the caret
HideCaret(hwnd);
DestroyCaret(); return 0; case WM_KEYDOWN : switch(wParam)
{ case VK_HOME : xCaret = 0; break; case VK_END :

194 xCaret = cxBuffer - 1; break; case VK_PRIOR : yCaret = 0; break; case VK_NEXT : yCaret = cyBuffer - 1; break; case VK_LEFT : xCaret = max(xCaret - 1, 0); break; case VK_RIGHT : xCaret = min(xCaret + 1, cxBuffer - 1); break; case VK_UP : yCaret = max(yCaret - 1, 0); break; case VK_DOWN : yCaret = min(yCaret + 1, cyBuffer - 1); break; case VK_DELETE : for(x = xCaret; x < cxBuffer - 1; x++)
BUFFER(x, yCaret) = BUFFER(x + 1, yCaret);
BUFFER(cxBuffer - 1, yCaret) = ' ';
HideCaret(hwnd); hdc = GetDC(hwnd);
SelectObject(hdc,
GetStockObject(SYSTEM_FIXED_FONT));
TextOut(hdc, xCaret * cxChar, yCaret * cyChar,
& BUFFER(xCaret, yCaret), cxBuffer - xCaret);
ShowCaret(hwnd);
ReleaseDC(hwnd, hdc); break;
}
SetCaretPos(xCaret * cxChar, yCaret * cyChar); return 0; case WM_CHAR : for(i = 0; i <(int) LOWORD(lParam); i++)
{ switch(wParam)
{ case '\b' : // backspace if(xCaret > 0)
{ xCaret--;
SendMessage(hwnd, WM_KEYDOWN,
VK_DELETE, 1L);
} break;

195 case '\t' : // tab do
{
SendMessage(hwnd, WM_CHAR, ' ', 1L);
} while(xCaret % 8 != 0); break; case '\n' : // line feed if(++yCaret == cyBuffer) yCaret = 0; break; case '\r' : // carriage return xCaret = 0; if(++yCaret == cyBuffer) yCaret = 0; break; case '\x1B' : // escape for(y = 0; y < cyBuffer; y++) for(x = 0; x < cxBuffer; x++)
BUFFER(x, y) = ' '; xCaret = 0; yCaret = 0;
InvalidateRect(hwnd, NULL, FALSE); break; default : // character codes
BUFFER(xCaret, yCaret) =(char) wParam;
HideCaret(hwnd); hdc = GetDC(hwnd);
SelectObject(hdc,
GetStockObject(SYSTEM_FIXED_FONT));
TextOut(hdc, xCaret * cxChar, yCaret * cyChar,
& BUFFER(xCaret, yCaret), 1);
ShowCaret(hwnd);
ReleaseDC(hwnd, hdc); if(++xCaret == cxBuffer)
{ xCaret = 0; if(++yCaret == cyBuffer) yCaret = 0;
} break;
}
}
SetCaretPos(xCaret * cxChar, yCaret * cyChar); return 0; case WM_PAINT : hdc = BeginPaint(hwnd, &ps);
SelectObject(hdc, GetStockObject(SYSTEM_FIXED_FONT));

196 for(y = 0; y < cyBuffer; y++)
TextOut(hdc, 0, y * cyChar, & BUFFER(0,y), cxBuffer);
EndPaint(hwnd, &ps); return 0; case WM_DESTROY :
PostQuitMessage(0); return 0;
} return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
Рис. 5.5 Программа TYPER
Сообщения WM_KEYDOWN и WM_KEYUP здесь обрабатываются более полно. Обработка сообщения
WM_KEYDOWN в основном включает в себя обработку клавиш управления курсором. Клавиши


Поделитесь с Вашими друзьями:
1   ...   15   16   17   18   19   20   21   22   ...   41


База данных защищена авторским правом ©nethash.ru 2019
обратиться к администрации

войти | регистрация
    Главная страница


загрузить материал