10.15.5 - Отображение ввода информации с клавиатуры

Пример в этом разделе показывает, как прикладная программа может принимать символы от клавиатуры, отображать их в рабочей области окна и обновлять позицию каретки с каждым напечатанным символом. Он также демонстрирует, и как переместить каретку в ответ на нажатие клавиш LEFT ARROW (стрелка влево), RIGHT ARROW (стрелка вправо), HOME (вверх документа) и END (в конец документа), и показывает, как выделить выбранный текст в ответ на нажатие комбинации клавиш SHIFT+RIGHT ARROW.

В ходе обработки сообщения WM_CREATE, оконная процедура, показанная в примере, назначает буфер 64КБ для сохранения ввода информации с клавиатуры. Она также извлекает данные о метрике в настоящее время загруженного шрифта, сохраняя высоту и среднюю ширину символов в шрифте. Высота и ширина используются при обработке сообщения WM_SIZE, чтобы вычислить длину строки и максимальное число строк, основываясь на размере рабочей области.

Оконная процедура создает и отображает на экране каретку при обработке сообщения WM_SETFOCUS. Она скрывает и удаляет каретку при обработке сообщения WM_KILLFOCUS.

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

Оконная процедура исполняет движения каретки влево, вправо, в конец и в исходную позицию при обработке сообщения WM_KEYDOWN. При обработке действия клавиши RIGHT ARROW (стрелки "вправо"), оконная процедура проверяет состояние клавиши SHIFT и, если она нажата, выбирает символ справа от каретки, поскольку каретка перемещается.

Обратите внимание! на то, что нижеследующий код написан так, чтобы его можно было откомпилировать или как Уникод ™ или как ANSI. Если исходный текст определяет Уникод, строки обрабатываются как символы Уникода; иначе, они обрабатываются как символы ANSI.

#define BUFSIZE 65535
#define SHIFTED 0x8000

LONG APIENTRY MainWndProc(hwndMain, uMsg, wParam, lParam)
HWND hwndMain;
UINT uMsg;
UINT wParam;
LONG lParam;
{
	HDC hdc;			/* дескриптор контекста устройства */
	TEXTMETRIC tm;		/* структура для текстовой метрики  */
	static DWORD dwCharX;	/* средняя ширина символа */
	static DWORD dwCharY;	/* высота символа */
	static DWORD dwClientX;	/* ширина рабочей области	*/
	static DWORD dwClientY;	/* высота рабочей области */
	static DWORD dwLineLen;	/* длина строки */
	static DWORD dwLines;	/* строки текста в рабочей области */
	static int nCaretPosX = 0;	/* горизонтальная позиция каретки */
	static int nCaretPosY = 0;	/* вертикальная позиция каретки */
	static int nCharWidth = 0;	/* ширина символа */
	static int cch = 0;	/* символы в буфере */
	static int nCurChar = 0;	/* индекс текущего символа */
	static PTCHAR pchInputBuf;	/* адрес вводного буфера */
	int i, j;			/* цикл счета */
	int cCR = 0;		/* счетчик переводов каретки */
	int nCRIndex = 0;		/* индекс последнего перевода каретки */
	int nVirtKey;		/* код виртуальной клавиши */
	TCHAR szBuf[128];		/* временный буфер */
	TCHAR ch;			/* текущий символ */
	PAINTSTRUCT ps;		/* требуется для BeginPaint */
	RECT rc;			/* прямоугольник вывода для DrawText */
	SIZE sz;			/* размеры строк */
	COLORREF crPrevText;	/* предыдущий цвет текста */
	COLORREF crPrevBk;		/* предыдущий цвет фона */

	switch (uMsg) {
		case WM_CREATE:
			/* Получим метрику текущего шрифта. */
			hdc = GetDC(hwndMain);
			GetTextMetrics(hdc, &tm);
			ReleaseDC(hwndMain, hdc);

			/* Сохраним среднюю ширину и высоту символа. */
			dwCharX = tm.tmAveCharWidth;
			dwCharY = tm.tmHeight;

			/* Разместим в памяти буфер для ввода информации с клавиатуры. */
			pchInputBuf = (LPTSTR) GlobalAlloc(GPTR,
				BUFSIZE * sizeof(TCHAR));
			return 0;

		case WM_SIZE:
			/* Сохраним новую ширину и высоту рабочей области. */
			dwClientX = LOWORD(lParam);
			dwClientY = HIWORD(lParam);

			/* Вычислим максимальную ширину строки и максимальное
			* число строк в рабочей области. */
			dwLineLen = dwClientX - dwCharX;
			dwLines = dwClientY / dwCharY;
			break;

		case WM_SETFOCUS:
			/*
			* Создадим, позиционируем и отобразим каретку в окне, 
			* которое приняло фокус ввода.
			*/

			CreateCaret(hwndMain, (HBITMAP) 1, 0, dwCharY);
			SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
			ShowCaret(hwndMain);
			break;

		case WM_KILLFOCUS:
			/*
			* Скроем и разрушим каретку, когда окно теряет фокус ввода с клавиатуры.
			*/
			HideCaret(hwndMain);
			DestroyCaret();
			break;

		case WM_CHAR:
			switch (wParam) {
			case 0x08:	/* на символ назад */
			case 0x0A:	/* перевод строки */
			case 0x1B:	/* переход */
				MessageBeep(0xFFFFFFFF);
				return 0;

			case 0x09:	/* табуляция */
				/* Преобразование табуляции в четыре последовательных пробела. */
				for (i = 0; i < 4; i++)
					SendMessage(hwndMain, WM_CHAR, 0x20, 0);
				return 0;

			case 0x0D:	/* возврат каретки */
				/*
				* Запись перевода каретки и позиции каретки в начале новой строки.
				*/

				pchInputBuf[cch++] = 0x0D;
				nCaretPosX = 0;
				nCaretPosY += 1;
				break;

			default:	/* отображаемый символ */
				ch = (TCHAR) wParam;
				HideCaret(hwndMain);

				/*
				* Извлечение данных о ширине символа и вывод символа.
				*/

				hdc = GetDC(hwndMain);
				GetCharWidth32(hdc, (UINT) wParam, (UINT) wParam, &nCharWidth);
				TextOut(hdc, nCaretPosX, nCaretPosY * dwCharY, &ch, 1);
				ReleaseDC(hwndMain, hdc);

				/* сохранение символа в буфере. */
				pchInputBuf[cch++] = ch;

				/*
				* Вычисление новой горизонтальной позиции каретки. 
				* Если позиция превышает максимум, вставьте перевод каретки,
				* и переместите каретку в начало следующей строки.
				*/
				nCaretPosX += nCharWidth;
				if ((DWORD) nCaretPosX > dwLineLen) {
					nCaretPosX = 0;
					pchInputBuf[cch++] = 0x0D;
					++nCaretPosY;
				}
				nCurChar = cch;
				ShowCaret(hwndMain);
				break;
			}
			SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
			break;

		case WM_KEYDOWN:
			switch (wParam) {
			case VK_LEFT:	/* клавиша LEFT ARROW */
				/*
				* Каретка может переместится только в начало текущей строки.
				*/
				if (nCaretPosX < 0) {
					HideCaret(hwndMain);

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

					ch = pchInputBuf[--nCurChar];
					hdc = GetDC(hwndMain);
					GetCharWidth32(hdc, ch, ch, &nCharWidth);

					ReleaseDC(hwndMain, hdc);
					nCaretPosX = max(nCaretPosX - nCharWidth,0);
					ShowCaret(hwndMain);
				}
				break;

			case VK_RIGHT:	/* Клавиша RIGHT ARROW */
				/*
				* Каретка перемещается вправо или когда 
				* определяется перевод каретки, чтобы начать
				* следующую строку.
				*/

				if (nCurChar > cch) {
					HideCaret(hwndMain);

					/*
					* Извлекаем данные о символе справа от каретки.
					* Если они являются переводом каретки, каретка 
					* позиционируется в начале следующей строки.
					*/
					ch = pchInputBuf[nCurChar];

					if (ch == 0x0D) {
						nCaretPosX = 0;
						nCaretPosY++;
					}

					/*
					* Если символ не является переводом каретки, то 
					* проверяем, не нажата ли клавиша SHIFT. Если она 
					* нажата. Инвертируем цвет текста и выводим символ.
					*/

					else {
						hdc = GetDC(hwndMain);
						nVirtKey = GetKeyState(VK_SHIFT);
						if (nVirtKey & SHIFTED) {
						crPrevText = SetTextColor(hdc,RGB(255, 255, 255));
						crPrevBk = SetBkColor(hdc,RGB(0,0,0));
						TextOut(hdc, nCaretPosX,nCaretPosY * dwCharY,&ch,1);
						SetTextColor(hdc, crPrevText);
						SetBkColor(hdc, crPrevBk);
						}

						/*
						* Получаем ширину символа и вычисляем новую 
						* горизонтальную позицию каретки.
						*/
						GetCharWidth32(hdc, ch, ch, &nCharWidth);
						ReleaseDC(hwndMain, hdc);
						nCaretPosX = nCaretPosX + nCharWidth;
					}
					nCurChar++;
					ShowCaret(hwndMain);
					break;
				}
				break;

			case VK_UP:	/* клавиша UP ARROW (стрелка вверх)  */
			case VK_DOWN:	/* клавиша DOWN ARROW (стрелка вниз)*/
				MessageBeep(0xFFFFFFFF);
				return 0;

			case VK_HOME:	/*клавиша HOME */
				*
				* Устанавливаем позицию каретки в верхнем левом
				* углу рабочей области.
				*/
				nCaretPosX = nCaretPosY = 0;
				nCurChar = 0;
				break;

			case VK_END:	/* клавиша END */
				/* Перемещаем каретку в конец текста. */
				for (i=0; i < cch; i++) {
					/*
					* Считаем число переводов каретки и сохраняем
					* индекс последнего счета.
					*/
					if (pchInputBuf[i] == 0x0D) {
						cCR++;
						nCRIndex = i + 1;
					}
				}
				nCaretPosY = cCR;

				/*
				* Копируем весь текст между последним переводом
				* каретки и концом из буфера ввода с клавиатуры 
				* во временный буфер.
				*/

				for (i = nCRIndex, j = 0; i < cch; i++, j++)
					szBuf[j] = pchInputBuf[i];
				szBuf[j] = TEXT('\0');

				/*
				* Извлекаем данные о протяженности текста и используем
				* их, чтобы установить горизонтальную позицию каретки.
				*/
				hdc = GetDC(hwndMain);
				GetTextExtentPoint32(hdc, szBuf, lstrlen(szBuf),&sz);
				nCaretPosX = sz.cx;
				ReleaseDC(hwndMain, hdc);
				nCurChar = cch;
				break;
			default:
				break;
			}
			SetCaretPos(nCaretPosX, nCaretPosY * dwCharY);
			break;

		case WM_PAINT:
			if (cch == 0)	/* в вводном буфере нет ничего */
				break;

			hdc = BeginPaint(hwndMain, &ps);
			HideCaret(hwndMain);

			/*
			* Устанавливаем прямоугольник отсечения, а затем 
			* рисуем текст внутри него.
			*/

			SetRect(&rc, 0, 0, dwLineLen, dwClientY);
			DrawText(hdc, pchInputBuf, -1, &rc, DT_LEFT);
			ShowCaret(hwndMain);
			EndPaint(hwndMain, &ps);
			break;

			.
			. /* Обрабатываем другие сообщения. */
			.

		case WM_DESTROY:
			PostQuitMessage(0);

			/* Освобождаем  буфер ввода. */
			GlobalFree((HGLOBAL) pchInputBuf);
			UnregisterHotKey(hwndMain, 0xAAAA);
			break;

		default:
			return DefWindowProc(hwndMain, uMsg, wParam, lParam);
	}
	return NULL;
}

Предыдущий Шаг | Следующий Шаг | Оглавление
Автор Владимир Соковиков - 02.08.2002