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


Атрибуты контекста устройства и текст



Pdf просмотр
страница18/41
Дата27.11.2016
Размер4.32 Mb.
Просмотров7905
Скачиваний0
ТипРеферат
1   ...   14   15   16   17   18   19   20   21   ...   41
Атрибуты контекста устройства и текст
Некоторые атрибуты контекста устройства влияют на вид выводимого текста. В контексте устройства по умолчанию для текста задан черный цвет, но вы можете изменить его с помощью функции:
SetTextColor(hdc, rgbColor);
Так же, как для цвета пера и цвета штриховой кисти, Windows преобразует значение параметра rgbColor в чистый цвет. Вы можете определить цвет выводимого текста, вызывая функцию GetTextColor.
Пространство между строками текста закрашивается на основании установленных режима фона и цвета фона. Вы можете изменить режим фона, используя функцию:
SetBkMode(hdc, iMode); где параметр iMode имеет значение OPAQUE или TRANSPARENT. По умолчанию режим фона установлен равным
OPAQUE. Это означает, что Windows закрашивает пространство между строками текста цветом фона. Вы можете изменить цвет фона с помощью функции:
SetBkColor(hdc, rgbColor);
Значение параметра rgbColor преобразуется в значение чистого цвета. По умолчанию задан белый цвет фона. Если режим фона имеет значение TRANSPARENT, то Windows игнорирует цвет фона и не закрашивает пространство между строками. Windows также использует режим фона и цвет фона для закрашивания пространства между точечными и штриховыми линиями, а также пространства между штрихами штриховых кистей, как уже обсуждалось ранее.
Многие программы под Windows задают кисть WHITE_BRUSH, которую Windows использует для закрашивания фона окна. Кисть определяется в структуре класса окна. Кроме того, можно сделать цвет окна вашей программы совпадающим с системными цветами, которые пользователь может установить в программе Control Panel (панель управления). В этом случае вы задаете цвет фона в структуре WNDCLASS таким образом: wndclass.hbrBackground =(HBRUSH)(COLOR_WINDOW + 1);
Когда вы хотите вывести текст в рабочей области, вы можете установить цвет текста и цвет фона, используя текущие системные цвета:
SetTextColor(hdc, GetSysColor(COLOR_WINDOWTEXT));
SetBkColor(hdc, GetSysColor(COLOR_WINDOW));
Можно сделать так, чтобы ваша программа отслеживала изменение системных цветов: case WM_SYSCOLORCHANGE :
InvalidateRect(hwnd, NULL, TRUE); break;
Другим атрибутом контекста устройства, определяющим вид текста, является межсимвольный интервал. По умолчанию он установлен в 0, что означает отсутствие интервала между соседними символами. Вы можете задать межсимвольный интервал, используя функцию:
SetTextCharacterExtra(hdc, iExtra);
Значение параметра iExtra задается в логических единицах. Windows преобразует его до ближайшего значения в пикселях, которое может быть и 0. Если вы зададите значение параметра iExtra отрицательным (например, чтобы прижать символы ближе друг к другу), Windows использует абсолютное значение заданного числа: вы не можете сделать эту величину меньше 0. Вы можете получить текущее значение межсимвольного интервала, используя

157 функцию GetTextCharacterExtra. Windows предварительно преобразует возвращаемое значение межсимвольного интервала из пикселей в логические единицы.
Использование стандартных шрифтов
Когда вы вызываете одну из функций вывода текста TextOut, TabbedTextOut, ExtTextOut или DrawText, Windows использует шрифт, выбранный в момент вызова функции в контексте устройства. Шрифт определяет особенности изображения символов и размер. Простейший путь выводить текст различными шрифтами состоит в использовании стандартных шрифтов, поддерживаемых Windows. Однако, их список достаточно ограничен.
Вы можете получить описатель стандартного шрифта, вызывая функцию: hFont = GetStockObject(iFont); где параметр iFont — один из нескольких идентификаторов, только два из которых обычно используются. Вы можете затем выбрать шрифт в контекст устройства:
SelectObject(hdc, hFont);
Вы можете осуществить это и за один шаг:
SelectObject(hdc, GetStockObject(iFont));
Функция GetStockObject — это та же самая функция, которую мы использовали ранее для получения стандартных перьев и кистей; функцию SelectObject мы использовали при выборе перьев, кистей, битовых образов и регионов в контекст устройства.
Шрифт, выбранный в контекст устройства по умолчанию, называется системным шрифтом и определяется параметром SYSTEM_FONT функции GetStockObject. Это пропорциональный шрифт, состоящий из ANSI символов, который Windows использует для вывода текста в меню, диалоговых окнах, окнах подсказок и в строках заголовков окон. Задание параметра SYSTEM_FIXED_FONT функции GetStockObject (которое было сделано в программе WHATSIZE ранее в этой главе) дает вам описатель шрифта фиксированной ширины (fixed-pitch), совместимого с системным шрифтом, который использовался в более ранних (до 3.0) версиях Windows. Это очень удобно, когда все символы шрифта имеют одинаковую ширину.
Когда вы выбираете новый шрифт в контекст устройства, вы должны вычислить высоту символа шрифта и среднюю ширину символа, используя функцию GetTextMetrics. Если вы выбираете пропорциональный шрифт, то вы должны помнить, что средняя ширина символа — это действительно среднее значение, т. е. некоторые символы более широкие, а некоторые более узкие. Далее в этой главе мы рассмотрим, как определить общую ширину строки, состоящую из символов различной ширины.
Несмотря на то, что функция GetStockObject несомненно предлагает простейший доступ к различным шрифтам, вы не имеете возможности управлять шрифтами, которые предлагает вам Windows. Вскоре вы увидите, каким образом вы можете точно задать желаемый вид и размер символов.
Типы шрифтов
Windows поддерживает две больших категории шрифтов — " шрифты GDI " и "шрифты устройства" (device fonts).
Шрифты GDI хранятся в файлах на вашем жестком диске. Шрифты устройства соответствуют конкретному устройству вывода. Например, большинство принтеров имеет набор встроенных шрифтов устройства.
Шрифты GDI могут быть одного из трех типов — растровые шрифты, векторные шрифты и шрифты типа True
Type.
Растровый шрифт иногда называют шрифтом битовых шаблонов, т. к. в файле растрового шрифта каждый символ хранится в виде битового шаблона. Каждый растровый шрифт разработан для определенного относительного размера пикселя дисплея и размера символа. Windows может создавать символы больших размеров из растровых шрифтов GDI, просто дублируя строки или колонки пикселей. Однако, это может быть сделано только целое количество раз и до определенного предела. По этой причине растровые шрифты GDI называют "немасштабируемыми" (nonscalable) шрифтами. Они не могут быть растянуты или сжаты до произвольного размера. Основными преимуществами растровых шрифтов являются их быстрое отображение на экране и четкость
(т. к. они были разработаны вручную, чтобы текст выглядел максимально разборчиво).
В более ранних версиях (до 3.1) Windows поддерживала, кроме шрифтов GDI, еще и векторные шрифты.
Векторные шрифты определены как набор соединенных друг с другом отрезков прямых (connect-the-dots).
Векторные шрифты легко масштабируются в широких пределах, т. е. один и тот же шрифт может быть использован в графических устройствах вывода с любой разрешающей способностью, и эти шрифты могут быть увеличены или уменьшены до любого размера. Однако, эти шрифты имеют более низкую скорость отображения, плохую четкость при маленьких размерах, а при использовании больших размеров символы выглядят очень

158 бледными, потому что их контуры — тонкие линии. Векторные шрифты сейчас иногда называют "плоттерными"
(plotter fonts), поскольку они подходят только для плоттеров.
Для обоих типов шрифтов GDI, растровых и векторных, Windows может синтезировать полужирный курсив, подчеркнутый и зачеркнутый шрифты без хранения отдельно шрифтов каждого из этих типов. Например, чтобы получить курсив, Windows просто сдвигает верхнюю часть символа вправо.
Далее рассмотрим шрифты TrueType, которым посвятим остаток главы.
Шрифты TrueType
С введением шрифтов TrueType в версии Windows 3.1 значительно повысились возможности и гибкость работы с текстами. TrueType — это технология контурных шрифтов, которая была разработана Apple Computer Inc. и
Microsoft Corporation; она поддерживается многими производителями шрифтов. Отдельные символы шрифтов
TrueType определяются контурами, состоящими из прямых линий и кривых. Таким образом, Windows может масштабировать эти шрифты, изменяя определяющие контур координаты. Шрифты TrueType могут быть использованы как для вывода на экран, так и для вывода на принтер, делая реально возможным режим отображения текста WYSIWYG (what-you-see-is-what-you-get, что видите — то и получаете).
Когда вашей программе необходимо использовать шрифт TrueType определенного размера, Windows формирует растровое представление символов этого шрифта. Это означает, что Windows масштабирует координаты точек соединения прямых и кривых каждого символа, используя дополнительные данные, которые включены в файл шрифта TrueType. Эти дополнительные данные позволяют компенсировать ошибки округления, которые могли бы привести в результате к искажению символа. (Например, в некоторых шрифтах обе ножки заглавной буквы Н должны иметь одинаковую ширину. Простое масштабирование может привести к результату, когда одна ножка будет на пиксель шире, чем другая. Учет дополнительных данных шрифта предотвращает эту ситуацию.)
Полученный таким образом контур каждого символа используется затем для создания битового образа символа.
Эти битовые образы кэшируются в памяти для последующего применения.
Windows 95 оснащена 13 шрифтами TrueType. Они имеют следующие имена в соответствии с их видом:

Courier New

Courier New Bold

Courier New Italic

Courier New Bold Italic

Times New Roman

Times New Roman Bold

Times New Roman Italic

Times New Roman Bold Italic

Arial

Arial Bold

Arial Italic

Arial Bold Italic

Symbol
Шрифт Courier New — это фиксированный шрифт (т. е. все символы имеют одинаковую ширину). Он разработан похожим на выводимые данные такого устаревшего устройства, как пишущая машинка. Группа шрифтов Times
New Roman — это производные от шрифта Times, впервые разработанного специально для Times of London, и используемого во многих печатных материалах. Они рассчитаны на то, чтобы обеспечить максимальное удобство чтения. Группа шрифтов Arial — это производные от шрифта Helvetica, являющегося рубленым (sans serif) шрифтом. Это означает, что символы не имеют засечек на концах. Шрифт Symbol содержит ряд часто используемых специальных символов.
Обычно вы задаете шрифт путем указания его типового имени и типового размера. Типовой размер выражается в единицах, называемых пунктами. Пункт — это приблизительно 1/72 дюйма, разница настолько несущественна, что в компьютерной полиграфии пункт часто определяют как 1/72 дюйма без всяких оговорок. Текст этой книги напечатан шрифтом типового размера 12 пунктов. Типовой размер иногда определяют как высоту символа от верхней границы букв, имеющих выступающую верхнюю часть, до нижней границы букв, имеющих выступающую нижнюю часть. Это достаточно убедительный способ оценки типового размера, но он не является точным для всех шрифтов. Иногда разработчики шрифтов действуют по-другому.
Система EZFONT
Введение шрифтов TrueType — и их основополагающее использование в традиционной полиграфии — обеспечило
Windows серьезную базу для многовариантного отображения текста. Однако, некоторые из функций выбора

159 шрифта Windows основаны на более старой технологии, при которой растровые шрифты на экране должны были аппроксимировать шрифты печатающего устройства.
В предыдущих изданиях этой книги подробно рассматривалась функция EnumFonts, которая предоставляет любой программе структуры LOGFONT (logical font, логический шрифт) и TEXTMETRIC для каждого шрифта GDI, установленного для использования под Windows, и для всех шрифтов устройств. Диалоговое окно ChooseFont
(которое подробнее будет рассмотрено в главе 11) делает ненужным использование этой функции и всего, что с ней связано. Также подробно обсуждалась функция CreateFontIndirect, которая позволяет программе описывать создаваемый шрифт без его немедленного именования. Это объясняет то, каким образом шрифты печатающего устройства аппроксимировались на экране монитора.
В этой главе вам будет предложено нечто совсем другое. Здесь будет показано, как можно использовать различные стандартные шрифты TrueType в ваших программах наиболее эффективно и одновременно с учетом требований традиционной полиграфии. Для этого надо задать название шрифта (одного из 13, перечисленных выше) и его размер
(который будет рассмотрен вскоре). Шрифт назван EZFONT (easy font, простой шрифт). На рис. 4.33 приведены два файла, тексты которых вам потребуются.
EZFONT.H
/*----------------------
EZFONT.H header file
----------------------*/
HFONT EzCreateFont(HDC hdc, char * szFaceName, int iDeciPtHeight, int iDeciPtWidth, int iAttributes, BOOL fLogRes);
#define EZ_ATTR_BOLD 1
#define EZ_ATTR_ITALIC 2
#define EZ_ATTR_UNDERLINE 4
#define EZ_ATTR_STRIKEOUT 8
EZFONT.C
/*---------------------------------------
EZFONT.C -- Easy Font Creation
(c) Charles Petzold, 1996
---------------------------------------*/
#include
#include
#include
#include "ezfont.h"
HFONT EzCreateFont(HDC hdc, char * szFaceName, int iDeciPtHeight, int iDeciPtWidth, int iAttributes, BOOL fLogRes)
{
FLOAT cxDpi, cyDpi;
HFONT hFont;
LOGFONT lf;
POINT pt;
TEXTMETRIC tm;
SaveDC(hdc);
SetGraphicsMode(hdc, GM_ADVANCED);
ModifyWorldTransform(hdc, NULL, MWT_IDENTITY);
SetViewportOrgEx(hdc, 0, 0, NULL);
SetWindowOrgEx (hdc, 0, 0, NULL); if(fLogRes)
{ cxDpi =(FLOAT) GetDeviceCaps(hdc, LOGPIXELSX); cyDpi =(FLOAT) GetDeviceCaps(hdc, LOGPIXELSY);
} else
{ cxDpi =(FLOAT)(25.4 * GetDeviceCaps(hdc, HORZRES) /

160
GetDeviceCaps(hdc, HORZSIZE)); cyDpi =(FLOAT)(25.4 * GetDeviceCaps(hdc, VERTRES) /
GetDeviceCaps(hdc, VERTSIZE));
} pt.x =(int)(iDeciPtWidth * cxDpi / 72); pt.y =(int)(iDeciPtHeight * cyDpi / 72);
DPtoLP(hdc, &pt, 1); lf.lfHeight = -(int)(fabs(pt.y) / 10.0 + 0.5); lf.lfWidth = 0; lf.lfEscapement = 0; lf.lfOrientation = 0; lf.lfWeight = iAttributes & EZ_ATTR_BOLD ? 700 : 0; lf.lfItalic = iAttributes & EZ_ATTR_ITALIC ? 1 : 0; lf.lfUnderline = iAttributes & EZ_ATTR_UNDERLINE ? 1 : 0; lf.lfStrikeOut = iAttributes & EZ_ATTR_STRIKEOUT ? 1 : 0; lf.lfCharSet = 0; lf.lfOutPrecision = 0; lf.lfClipPrecision = 0; lf.lfQuality = 0; lf.lfPitchAndFamily = 0; strcpy(lf.lfFaceName, szFaceName); hFont = CreateFontIndirect(&lf); if(iDeciPtWidth != 0)
{ hFont =(HFONT) SelectObject(hdc, hFont);
GetTextMetrics(hdc, &tm);
DeleteObject(SelectObject(hdc, hFont)); lf.lfWidth =(int)(tm.tmAveCharWidth * fabs(pt.x) / fabs(pt.y) + 0.5); hFont = CreateFontIndirect(&lf);
}
RestoreDC(hdc, -1); return hFont;
}
Рис. 4.33 Файлы EZFONT
Программа EZFONT.C содержит только одну функцию — EzCreateFont — которую вы можете использовать, например, таким образом: hFont = EzCreateFont(hdc, szFaceName, iDeciPtHeight, iDeciPtWidth, iAttributes, fLogRes);
Функция возвращает описатель шрифта. Шрифт может быть выбран в контекст устройства посредством вызова функции SelectObject. Затем вы можете вызывать функции GetTextMetrics или GetOutlineTextMetrics для определения действительного размера шрифта в логических координатах. Перед окончанием вашей программы необходимо удалить все созданные шрифты, вызвав функцию DeleteObject.
Параметр szFaceName — одно из 13 типовых имен шрифта TrueType из приведенного ранее списка. Если в вашей системе есть другие шрифты TrueType, вы можете также использовать их названия, но только 13 шрифтов, перечисленных ранее, обязательно присутствуют во всех системах Windows 95.

161
Третий параметр определяет желаемый размер шрифта в пунктах, но его особенность состоит в том, что он задается в деципунктах (каждый деципункт равен 1/10 пункта). Следовательно, если вы хотите задать размер в пунктах, равный 12 1
/
2
, используйте значение 125.
Обычно четвертый параметр должен быть установлен в ноль или быть таким же, как третий параметр. Однако, вы можете создать более широкий или более узкий шрифт TrueType, установив другое значение этого параметра.
Иногда этот размер называют "эм-шириной" (em-width) шрифта, и он описывает ширину шрифта в пунктах. Не путайте эту величину со средней шириной символов шрифта или с чем-нибудь похожим. На раннем этапе развития типографского дела заглавная буква ‘M’ имела одинаковые ширину и высоту. Так возникла концепция "эм- квадрат", а впоследствии и такая мера как "эм-ширина". Когда эм-ширина равна эм-высоте (размеру шрифта в пунктах), то ширины символов установлены такими, какими изначально задумывались разработчиком шрифта.
Задание большей или меньшей эм-ширины позволяет вам создавать более широкие или более узкие символы.
Параметр iAttributes может быть установлен в одно из следующих значений, определенных в EZFONT.H:

EZ_ATTR_BOLD

EZ_ATTR_ITALIC

EZ_ATTR_UNDERLINE

EZ_ATTR_STRIKEOUT
Может быть вам и не потребуется использовать значения EZ_ATTR_BOLD или EZ_ATTR_ITALIC, потому что эти атрибуты являются частью полного типового имени шрифта TrueType. Если вы их используете, то Windows синтезирует соответствующие эффекты.
Наконец, вы должны установить в TRUE значение последнего параметра для того, чтобы видимый размер шрифта основывался на логическом разрешении (logical resolution), возвращаемом функцией GetDeviceCaps. В противном случае он базируется на действительном разрешении.
Внутренняя работа
Функция EzCreateFont разработана для использования в системах Windows 95 или Windows NT. Для совместимости с NT используются функции SetGraphicsMode и ModifyWorldTransform, которые никак не влияют на работу Windows 95. (Другими словами, ModifyWorldTransform в Windows NT могла бы оказать влияние на видимый размер шрифта. Поэтому эта функция устанавливается в режим по умолчанию — без преобразования — перед тем, как вычисляется размер шрифта.)
Функция EzCreateFont сначала устанавливает поля структуры LOGFONT и вызывает функцию CreateFont, которая возвращает описатель шрифта. Для выбранных шрифтов TrueType большинство полей может быть установлено в ноль. Вам необходимо установить значения следующих полей:

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

lfWidth — это желаемая ширина символов в логических единицах. В большинстве случаев его устанавливают в 0 и позволяют Windows выбирать шрифт, основываясь исключительно на высоте.

lfWeight — это поле позволяет вам задать полужирный шрифт путем установки значения, равного 700.

lfItalic — ненулевое значение поля определяет курсив.

lfUnderline — ненулевое значение поля задает подчеркивание.

lfStrikeOut — ненулевое значение поля определяет шрифт с зачеркиванием символов.

lfFaceName (массив типа BYTE) — это имя шрифта (например, Courier New, Arial или Times New Roman).
Одним из действий, выполняемых функцией EzCreateFont, является преобразование размера в пунктах в логические единицы для последующей установки этого значения в структуре LOGFONT. Сначала размер в пунктах должен быть преобразован в единицы устройства (пиксели), а затем в логические единицы. Чтобы выполнить этот первый шаг, мы используем информацию, получаемую от функции GetDeviceCaps.
Вызов функции GetDeviceCaps c параметрами HORZRES и VERTRES дает нам ширину и высоту экрана (или области печати выводимой на принтере страницы) в пикселях. Вызов функции GetDeviceCaps c параметрами
HORZSIZE и VERTSIZE дает нам физическую ширину и высоту экрана (или области печати выводимой на

162 принтере страницы) в миллиметрах. Если последний параметр имеет значение FALSE, то функция EzCreateFont использует эти значения для получения разрешающей способности устройств в точках на дюйм.
Здесь возможны два варианта. Вы можете получить разрешение устройства в точках на дюйм непосредственно, используя параметры LOGPIXELSX и LOGPIXELSY в функции GetDeviceCaps. Это называется "логическим разрешением" (logical resolution) устройства. Для печатающих устройств нормальное разрешение и логическое разрешение одинаковы (отбрасывая ошибки округления). Для дисплеев, однако, логическое разрешение лучше, чем нормальное разрешение. Например, для VGA наилучшим значением нормального разрешения является примерно 68 точек на дюйм. В то время, как логическое разрешение — 96 точек на дюйм.
Это различие, можно сказать, драматическое. Предположим, что мы работаем со шрифтом размера 12 пунктов, который имеет высоту 1/6 дюйма. Предположив, что нормальное разрешение равно 68 точкам на дюйм, полная высота символов будет около 11 пикселей. При логическом разрешении 96 точек на дюйм эта величина будет 16 пикселей. То есть разница составляет около 45%.
Чем объясняется такая разница? Если немного задуматься над этим, то на самом деле не существует истинного разрешения VGA. Стандартный VGA показывает 640 пикселей по горизонтали и 480 пикселей по вертикали, но размер экрана реального компьютера может быть различным — от маленького в компьютерах notebook до большого в VGA — проекторах. Windows не имеет способа самостоятельно определить действительный размер экрана. Значения HORZSIZE и VERTSIZE основаны на стандартных размерах настольных (desktop) дисплеев
VGA, которые могли быть установлены для каких-нибудь ранних моделей IBM (году в 1987) путем простого измерения линейкой экрана каким-то программистом Microsoft.
Если же рассмотреть этот вопрос еще глубже, то в действительности вам и не нужно, чтобы шрифты были отображены на экране в их истинном размере. Предположим, что вы используете VGA проектор на презентации перед сотнями людей, и вы используете 12-пунктовый шрифт, реальный размер которого составляет 1/6 дюйма на проекционном экране. Нет сомнений, что ваша аудитория будет в замешательстве.
Люди, постоянно работающие с текстами, часто используют текстовые процессоры и настольные издательские системы. Довольно часто вывод осуществляется на бумаге размером 8 1
/
2
х11 дюймов (или 8х10 дюймов с учетом отступов). Многие дисплеи VGA шире, чем 8 дюймов. Отображение на экране более крупных символов предпочтительнее, чем изображение в реальных размерах на экране.
Однако, если вы используете логическое разрешение шрифта, то могут возникнуть проблемы при совмещении текста и другой графики. Если вы используете функцию SetMapMode для рисования графики в дюймах или миллиметрах и одновременно логическое разрешение устройства для установки размера шрифта, то вы придете к несоответствию — не при выводе на принтере (т. к. здесь нормальное разрешение совпадает с логическим), а при выводе на экран, где существует разница в 45%. Решение этой проблемы будет продемонстрировано далее в этой главе в программе JUSTIFY1.
Структура LOGFONT, которую вы передаете функции CreateFontIndirect, требует задания высоты шрифта в логических единицах. Однажды получив это значение в пикселях, вы легко преобразуете его в логические единицы посредством вызова функции DPtoLP (device point to logical point, точка устройства в логическую точку).
Но для того чтобы преобразование DPtoLP выполнялось правильно, должен быть установлен тот же режим отображения (mapping mode), с каким вы далее будете работать при отображении текста на экране, используя созданный шрифт. Это значит, что вы должны установить режим отображения до того, как будете вызывать функцию EzCreateFont. В большинстве случаев вы используете только один режим отображения для рисования в конкретной области окна, так что выполнение этого требования не является проблемой.
Форматирование простого текста
Разобравшись с файлами EZFONT, наступило время потренироваться в форматировании текста. Процесс форматирования заключается в расположении каждой строки текста в пределах установленных полей одним из четырех способов: с выравниванием влево, с выравниванием вправо, с выравниванием по центру или с выравниванием по всей ширине (когда строка растягивается от левого края до правого края с формированием одинаковых интервалов между словами). Первые три задачи можно решить, используя функцию DrawText с параметром DT_WORDBREAK, но ее использование ограничено. Например, вы не можете определить, какую часть текста функция DrawText сможет разместить в прямоугольнике вывода. Функция DrawText удобна для некоторых простых задач, но для более сложных задач форматирования вы, вероятно, захотите применить функцию TextOut.
Одной из наиболее часто используемых функций работы с текстом является функция GetTextExtentPoint32. (Это функция, имя которой претерпело некоторые изменения со времени более ранних версий Windows.) Эта функция дает значения ширины и высоты строки символов, основываясь на текущем шрифте, выбранном в контексте устройства:
GetTextExtentPoint32(hdc, pString, iCount, &size);

163
Ширина и высота текста в логических единицах возвращается в поля cx и cy структуры SIZE. Начнем с примера, использующего одну строку текста. Предположим, что вы выбрали шрифт в контексте устройства и теперь хотите вывести текст: char *szText [ ] = "Hello, how are you?";
Вам нужно, чтобы текст начинался в позиции с вертикальной координатой yStart и находился между границами, установленными координатами xLeft и xRight. Ваша задача заключается в том, чтобы вычислить значение xStart горизонтальной координаты начала текста. Эта задача была бы значительно проще, если бы текст отображался с использованием шрифта фиксированной ширины, но это не является общим случаем. Сначала определим длину строки текста:
GetTextExtentPoint32(hdc, szText, strlen(szText), &size);
Если значение size.cx больше, чем (xRightxLeft), то срока слишком длинна, чтобы поместиться в указанных границах. Предположим, что строка все же помещается.
Для того, чтобы выровнять текст влево, нужно просто установить значение xStart равным xLeft и затем вывести текст:
TextOut(hdc, xStart, yStart, szText, strlen(szText));
Это просто. Теперь вы можете прибавить значение size.cy к yStart и затем выводить следующую строку текста.
Чтобы выровнять текст вправо, воспользуемся для вычисления xStart следующей формулой: xStart = xRight — size.cx;
Чтобы выровнять текст по центру между левой и правой границами используем другую формулу: xStart =(xLeft + xRight — size.cx) / 2;
Теперь перед нами самая трудная задача — выровнять текст по всей ширине между левой и правой границами.
Расстояние между ними вычисляется как (xRightxLeft). Без растягивания по ширине текст имеет ширину size.cx.
Разница между этими двумя величинами: xRight — xLeft — size.cx должна быть равномерно распределена между тремя символами пробелов в символьной строке. На первый взгляд это кажется труднейшей задачей, но на самом деле это не так уж и трудно. Чтобы сделать это, вы вызываете функцию:
SetTextJustification(hdc, xRight — xLeft — size.cx, 3)
Второй параметр — это величина пробела, который должен быть распределен поровну между тремя символами пробела в символьной строке. Третий параметр — число символов пробела в строке, в нашем примере — 3.
Теперь установим значение xStart равным xLeft и выведем текст с помощью функции TextOut:
TextOut(hdc, xStart, yStart, szText, strlen(szText));
Текст будет выровнен по всей ширине между границами xLeft и xRight.
При каждом вызове функции SetTextJustification накапливается погрешность, если величина добавляемого пропуска не делится нацело на число символов пробела. Эта ошибка будет влиять на последующие вызовы функции GetTextExtent. Каждый раз перед тем, как начинать вывод новой строки, вы должны сбросить эту погрешность с помощью вызова функции:
SetTextJustification(hdc, 0, 0);
Работа с абзацами
Если вы работаете с целым абзацем, вы должны просматривать всю строку от начала, отыскивая символы пробела.
Каждый раз, как вы встречаете символ пробела, вы вызываете функцию GettextExtentPoint32, чтобы определить, умещается ли текст по ширине между левой и правой границами. Когда текст не укладывается в отведенное для него пространство, вы возвращаетесь на один шаг назад к предыдущему найденному символу пробела. Теперь у вас есть законченная строка символов. Если вы хотите выровнять текст по ширине, то нужно вызвать функции
SetTextJustification и TextOut, избавиться от ошибки и перейти к следующей строке.
Программа JUSTIFY1, приведенная на рис. 4.34, проделывает эту процедуру с первым параграфом книги автора
Herman Melville, которая называется "Moby Dick". Программа использует встроенный шрифт Times New Roman размера 15 пунктов, но вы можете изменить его в функции MyCreateFont, описанной в начале программы, и перекомпилировать саму программу. Вы также можете изменить тип выравнивания, используя определение в начале программы. На рис. 4.35 показан вид текста, выведенного программой JUSTIFY1 на экран.

164
JUSTIFY1.MAK
#------------------------
# JUSTIFY1.MAK make file
#------------------------ justify1.exe : justify1.obj ezfont.obj
$(LINKER) $(GUIFLAGS) -OUT:justify1.exe justify1.obj ezfont.obj $(GUILIBS) justify1.obj : justify1.c
$(CC) $(CFLAGS) justify1.c ezfont.obj : ezfont.c
$(CC) $(CFLAGS) ezfont.c
JUSTIFY1.C
/*-----------------------------------------
JUSTIFY1.C -- Justified Type Program
(c) Charles Petzold, 1996
-----------------------------------------*/
#include
#include "ezfont.h"
#define LEFT 0
#define RIGHT 1
#define CENTER 2
#define JUSTIFIED 3
#define ALIGN JUSTIFIED
#define MyCreateFont EzCreateFont(hdc, "Times New Roman", 150, 0, 0, TRUE)
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
PSTR szCmdLine, int iCmdShow)
{ static char szAppName[] = "Justify1";
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 = szAppName; wndclass.lpszClassName = szAppName; wndclass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
RegisterClassEx(&wndclass); hwnd = CreateWindow(szAppName, "Justified Type",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
CW_USEDEFAULT, CW_USEDEFAULT,
NULL, NULL, hInstance, NULL);
ShowWindow(hwnd, iCmdShow);

165
UpdateWindow(hwnd); while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
} return msg.wParam;
} void DrawRuler(HDC hdc, RECT *prc)
{ static int iRuleSize [16] = { 360, 72, 144, 72, 216, 72, 144, 72,
288, 72, 144, 72, 216, 72, 144, 72 }; int i, j;
POINT ptClient;
SaveDC(hdc);
// Set Logical Twips mapping mode
SetMapMode(hdc, MM_ANISOTROPIC);
SetWindowExtEx(hdc, 1440, 1440, NULL);
SetViewportExtEx(hdc, GetDeviceCaps(hdc, LOGPIXELSX),
GetDeviceCaps(hdc, LOGPIXELSY), NULL);
// Move the origin to a half inch from upper left
SetWindowOrgEx(hdc, -720, -720, NULL);
// Find the right margin(quarter inch from right) ptClient.x = prc->right; ptClient.y = prc->bottom;
DPtoLP(hdc, &ptClient, 1); ptClient.x -= 360;
// Draw the rulers
MoveToEx(hdc, 0, -360, NULL);
LineTo (hdc, ptClient.x, -360);
MoveToEx(hdc, -360, 0, NULL);
LineTo (hdc, -360, ptClient.y); for(i = 0, j = 0; i <= ptClient.x; i += 1440 / 16, j++)
{
MoveToEx(hdc, i, -360, NULL);
LineTo (hdc, i, -360 - iRuleSize [j % 16]);
} for(i = 0, j = 0; i <= ptClient.y; i += 1440 / 16, j++)
{
MoveToEx(hdc, -360, i, NULL);
LineTo (hdc, -360 - iRuleSize [j % 16], i);
}
RestoreDC(hdc, -1);
} void Justify(HDC hdc, PSTR pText, RECT *prc, int iAlign)
{ int xStart, yStart, iBreakCount;
PSTR pBegin, pEnd;
SIZE size; yStart = prc->top;

166 do // for each text line
{ iBreakCount = 0; while(*pText == ' ') // skip over leading blanks pText++; pBegin = pText; do // until the line is known
{ pEnd = pText; while(*pText != '\0' && *pText++ != ' '); if(*pText == '\0') break;
// for each space, calculate extents iBreakCount++;
SetTextJustification(hdc, 0, 0);
GetTextExtentPoint32(hdc, pBegin, pText - pBegin - 1, &size);
} while((int) size.cx <(prc->right - prc->left)); iBreakCount--; while(*(pEnd - 1) == ' ') // eliminate trailing blanks
{ pEnd--; iBreakCount--;
} if(*pText == '\0' || iBreakCount <= 0) pEnd = pText;
SetTextJustification(hdc, 0, 0);
GetTextExtentPoint32(hdc, pBegin, pEnd - pBegin, &size); switch(iAlign) // use alignment for xStart
{ case LEFT: xStart = prc->left; break; case RIGHT: xStart = prc->right - size.cx; break; case CENTER: xStart =(prc->right + prc->left - size.cx) / 2; break; case JUSTIFIED: if(*pText != '\0' && iBreakCount > 0)
SetTextJustification(hdc, prc->right - prc->left - size.cx, iBreakCount); xStart = prc->left; break;
}
TextOut(hdc, xStart, yStart, pBegin, pEnd - pBegin); yStart += size.cy; pText = pEnd;
} while(*pText && yStart < prc->bottom);

167
}
LRESULT CALLBACK WndProc(HWND hwnd, UINT iMsg, WPARAM wParam, LPARAM lParam)
{ static char szText[] = "Call me Ishmael. Some years ago -- never mind "
"how long precisely -- having little or no money "
"in my purse, and nothing particular to interest "
"me on shore, I thought I would sail about a "
"little and see the watery part of the world. It "
"is a way I have of driving off the spleen, and "
"regulating the circulation. Whenever I find "
"myself growing grim about the mouth; whenever "
"it is a damp, drizzly November in my soul; "
"whenever I find myself involuntarily pausing "
"before coffin warehouses, and bringing up the "
"rear of every funeral I meet; and especially "
"whenever my hypos get such an upper hand of me, "
"that it requires a strong moral principle to "
"prevent me from deliberately stepping into the "
"street, and methodically knocking people's hats "
"off -- then, I account it high time to get to sea "
"as soon as I can. This is my substitute for "
"pistol and ball. With a philosophical flourish "
"Cato throws himself upon his sword; I quietly "
"take to the ship. There is nothing surprising "
"in this. If they but knew it, almost all men in "
"their degree, some time or other, cherish very "
"nearly the same feelings towards the ocean with "
"me.";
HDC hdc;
PAINTSTRUCT ps;
RECT rcClient; switch(iMsg)
{ case WM_PAINT: hdc = BeginPaint(hwnd, &ps);
GetClientRect(hwnd, &rcClient);
DrawRuler(hdc, &rcClient); rcClient.left += GetDeviceCaps(hdc, LOGPIXELSX) / 2; rcClient.top += GetDeviceCaps(hdc, LOGPIXELSY) / 2; rcClient.right -= GetDeviceCaps(hdc, LOGPIXELSX) / 4;
SelectObject(hdc, MyCreateFont);
Justify(hdc, szText, &rcClient, ALIGN);
DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));
EndPaint(hwnd, &ps); return 0; case WM_DESTROY:
PostQuitMessage(0); return 0;
} return DefWindowProc(hwnd, iMsg, wParam, lParam);
}
Рис. 4.34 Файлы JUSTIFY1

168
Рис. 4.35 Вывод программы JUSTIFY1
Программа JUSTIFY1 выводит на экран линейки (в логических дюймах, конечно) вдоль верхнего края и вдоль левого края рабочей области. Функция DrawRuler рисует линейки. Структура прямоугольника определяет область, в которой может быть расположен текст.
Основная работа заключается в форматировании заданного в программе JUSTIFY1 текста. Выполнение программы
JUSTIFY1 начинается с поиска всех символов пробела от начала текста. При помощи функции
GetTextExtentPoint32 измеряется длина каждой строки. Когда длина строки превышает ширину области вывода, программа JUSTIFY1 возвращается к предыдущему символу пробела и заканчивает строку в этой позиции. В зависимости от значения константы ALIGN строка выравнивается влево, выравнивается вправо, выравнивается по центру или выравнивается по ширине.
Эта программа не является совершенной. В частности, выравнивание по ширине логически бессмысленно, когда в каждой строке меньше, чем два слова. Но даже если мы решим этот вопрос (что не особенно сложно), программа все еще не будет работать как следует, если одно слово будет слишком длинным, чтобы уместиться между заданными полями. Конечно, ситуация может стать более сложной, если вы работаете с программами, использующими несколько шрифтов в одной строке (как с кажущейся легкостью делают это текстовые процессоры в Windows). Но никто никогда и не требовал, чтобы это было легко. Это проще только по сравнению с самостоятельным выполнением той же работы.




Часть II
Средства ввода

5
5


Глава 5
Клавиатура
Как и большинство интерактивных программ, работающих на персональных компьютерах, приложения Windows
95 активно используют пользовательский ввод с клавиатуры. Хотя Windows поддерживает в качестве устройства ввода также и мышь, работа с клавиатурой по-прежнему превалирует. Действительно, поскольку некоторые пользователи персональных компьютеров предпочитают пользоваться не мышью, а клавиатурой, рекомендуется, чтобы создатели программ пытались реализовать все функциональные возможности программы с помощью клавиатуры. (Конечно, в некоторых случаях, таких как программы рисования или издательские системы, это просто не практикуется, и в этом случае необходима мышь.)
Клавиатура не может рассматриваться исключительно как устройство для ввода информации, изолированно от других функций программы. Например, программы часто повторяют ввод с клавиатуры путем отображения печатаемых символов в рабочей области окна. Таким образом, обработка ввода с клавиатуры и вывод текста должны рассматриваться совместно. Если вам важно адаптировать ваши программы к иностранным языкам и рынкам, вам также нужно знать о том, как Windows 95 поддерживает расширенный набор символов ASCII (коды от 128 и выше), двухбайтные наборы символов (DBCS), и поддерживаемую Windows NT 16-разрядную кодировку клавиатуры, известную как Unicode.
Клавиатура. Основные понятия
Как вы, наверное, догадались, основанная на сообщениях архитектура Windows идеальна для работы с клавиатурой. Ваша программа узнает о нажатиях клавиш посредством сообщений, которые посылаются оконной процедуре.
На самом деле все происходит не столь просто: когда пользователь нажимает и отпускает клавиши, драйвер клавиатуры передает информацию о нажатии клавиш в Windows. Windows сохраняет эту информацию (в виде сообщений) в системной очереди сообщений. Затем она передает сообщения клавиатуры, по одному за раз, в очередь сообщений программы, содержащей окно, имеющее "фокус ввода" (input focus) (о котором вскоре будет рассказано). Затем программа отправляет сообщения соответствующей оконной процедуре.
Смысл этого двухступенчатого процесса — сохранение сообщений в системной очереди сообщений, и дальнейшая их передача в очередь сообщений приложения — в синхронизации. Если пользователь печатает на клавиатуре быстрее, чем программа может обрабатывать поступающую информацию, Windows сохраняет информацию о дополнительных нажатиях клавиш в системной очереди сообщений, поскольку одно из этих дополнительных нажатий может быть переключением фокуса ввода на другую программу. Информацию о последующих нажатиях следует затем направлять в другую программу. Таким образом Windows корректно синхронизирует такие сообщения клавиатуры.
Для отражения различных событий клавиатуры, Windows посылает программам восемь различных сообщений. Это может показаться излишним, и ваша программа вполне может игнорировать многие из них. Кроме того, в большинстве случаев, в этих сообщениях от клавиатуры содержится значительно больше закодированной информации, чем нужно вашей программе. Залог успешной работы с клавиатурой — это знание того, какие сообщения важны, а какие нет.
Игнорирование клавиатуры
Хотя клавиатура является основным источником пользовательского ввода программ для Windows, вашей программе не нужно реагировать на каждое получаемое от клавиатуры сообщение. Windows сама обрабатывает многие функции клавиатуры. Например, вы можете игнорировать нажатие системных клавиш. В таких нажатиях обычно используется клавиша Alt.

172
Программе не нужно отслеживать нажатие этих клавиш, поскольку Windows извещает программу об эффекте, вызванном их нажатием. (Однако, при желании программа сама может их отслеживать.) Например, когда пользователь Windows выбирает пункт меню с помощью клавиатуры, Windows посылает программе сообщение, что выбран пункт меню, независимо от того, был ли он выбран с помощью мыши, или с помощью клавиатуры. (О работе с меню рассказывается в главе 10.)
В некоторых программах для Windows используются "быстрые клавиши" (keyboard accelerators) для быстрого доступа к часто употребляемым пунктам меню. В качестве быстрых клавиш обычно используются функциональные клавиши или комбинация символьной клавиши и клавиши . Такие быстрые клавиши определяются в описании ресурсов программы. (В главе 10 показано, как Windows преобразует быстрые клавиши в сообщения команд меню. Вам не нужно самим делать это преобразование.)
Окна диалога (о которых рассказывается в главе 11) также имеют интерфейс клавиатуры, но обычно программам не нужно отслеживать клавиатурные события, когда активно окно диалога. Интерфейс клавиатуры обслуживается самой Windows, и Windows посылает сообщения вашей программе о действиях, соответствующих нажимаемым клавишам. В окнах диалога могут содержаться "окна редактирования" (edit) для ввода текста. Это обычно небольшие окна, в которых пользователь набирает строки символов. Windows управляет всей логикой окон редактирования и дает вашей программе окончательное содержимое этих окон, после того, как пользователь завершит ввод строки.
Даже внутри вашего главного окна вы можете определить дочерние окна как окна редактирования. Особым примером этого является программа POPPAD, представленная в главе 8. Эта программа практически представляет собой просто большое окно редактирования, причем вся черновая работа в ней возложена на Windows.
Фокус ввода
Клавиатура должна разделяться между всеми приложениями, работающими под Windows. Некоторые приложения могут иметь больше одного окна, и клавиатура должна разделяться между этими окнами в рамках одного и того же приложения. Когда на клавиатуре нажата клавиша, только одна оконная процедура может получить сообщение об этом. Окно, которое получает это сообщение клавиатуры, является окном, имеющем "фокус ввода" (input focus).
Концепция фокуса ввода тесно связана с концепцией "активного окна". Окно, имеющее фокус ввода — это либо активное окно, либо дочернее окно активного окна. Определить активное окно обычно достаточно просто. Если у активного окна имеется панель заголовка, то Windows выделяет ее. Если у активного окна вместо панели заголовка имеется рамка диалога (это наиболее часто встречается в окнах диалога), то Windows выделяет ее. Если активное окно минимизировано, то Windows выделяет текст заголовка в панели задач.
Наиболее часто дочерними окнами являются кнопки, переключатели, флажки, полосы прокрутки и списки, которые обычно присутствуют в окне диалога. Сами по себе дочерние окна никогда не могут быть активными.
Если фокус ввода находится в дочернем окне, то активным является родительское окно этого дочернего окна. То, что фокус ввода находится в дочерних окнах, обычно показывается посредством мигающего курсора или каретки
(caret).
Если активное окно минимизировано, то окна с фокусом ввода нет. Windows продолжает слать программе сообщения клавиатуры, но эти сообщения выглядят иначе, чем сообщения, направленные активным и еще не минимизированным окнам.
Обрабатывая сообщения WM_SETFOCUS и WM_KILLFOCUS, оконная процедура может определить, когда окно имеет фокус ввода. WM_SETFOCUS показывает, что окно получило фокус ввода, а WM_KILLFOCUS, что окно потеряло его.
Аппаратные и символьные сообщения
Сообщения, которые приложение получает от Windows о событиях, относящихся к клавиатуре, различаются на "аппаратные" (keystrokes) и "символьные" (characters). Такое положение соответствует двум представлениям о клавиатуре. Во-первых, вы можете считать клавиатуру набором клавиш. В клавиатуре имеется только одна клавиша . Нажатие на эту клавишу является аппаратным событием. Отпускание этой клавиши является аппаратным событием. Но клавиатура также является устройством ввода, генерирующем отображаемые символы.
Клавиша <А>, в зависимости от состояния клавиш , и , может стать источником нескольких символов. Обычно, этим символом является строчное ‘a’. Если нажата клавиша или установлен режим Caps Lock, то этим символом является прописное <А>. Если нажата клавиша , этим символом является +<А>. На клавиатуре, поддерживающей иностранные языки, аппаратному событию ‘А’ может предшествовать либо специальная клавиша, либо , либо , либо , либо их различные сочетания.
Эти сочетания могут стать источником вывода строчного ‘а’ или прописного ‘А’ с символом ударения.
Для сочетаний аппаратных событий, которые генерируют отображаемые символы, Windows посылает программе и оба аппаратных и символьное сообщения. Некоторые клавиши не генерируют символов. Это такие клавиши, как

173 клавиши переключения, функциональные клавиши, клавиши управления курсором и специальные клавиши, такие как Insert и Delete. Для таких клавиш Windows вырабатывает только аппаратные сообщения.
Аппаратные сообщения
Когда вы нажимаете клавишу, Windows помещает либо сообщение WM_KEYDOWN, либо сообщение
WM_SYSKEYDOWN в очередь сообщений окна, имеющего фокус ввода. Когда вы отпускаете клавишу, Windows помещает либо сообщение WM_KEYUP, либо сообщение WM_SYSKEYUP в очередь сообщений.
Клавиша нажата
Клавиша отпущена
Несистемные аппаратные сообщения WM_KEYDOWN
WM_KEYUP
Системные аппаратные сообщения WM_SYSKEYDOWN
WM_SYSKEYUP
Обычно сообщения о "нажатии" (down) и "отпускании" (up) появляются парами. Однако, если вы оставите клавишу нажатой так, чтобы включился автоповтор, то Windows посылает оконной процедуре серию сообщений
WM_KEYDOWN (или WM_SYSKEYDOWN) и одно сообщение WM_KEYUP (или WM_SYSKEYUP), когда в конце концов клавиша будет отпущена. Также как и все синхронные сообщения, аппаратные сообщения клавиатуры также становятся в очередь. Вы можете с помощью функции GetMessageTime получить время нажатия и отпускания клавиши относительно старта системы.
Системные и несистемные аппаратные сообщения клавиатуры
Префикс "SYS" в WM_SYSKEYDOWN и WM_SYSKEYUP означает "системное" (system) и относится к аппаратным сообщениям клавиатуры, которые больше важны для Windows, чем для приложений Windows.
Сообщения WM_SYSKEYDOWN и WM_SYSKEYUP обычно вырабатываются при нажатии клавиш в сочетании с клавишей . Эти сообщения вызывают опции меню программы или системного меню, или используются для системных функций, таких как смена активного окна (+ или +), или как быстрые клавиши системного меню ( в сочетании с функциональной клавишей). Программы обычно игнорируют сообщения
WM_SYSKEYDOWN и WM_SYSKEYUP и передают их DefWindowProc. Поскольку Windows отрабатывает всю логику Alt-клавиш, то вам фактически не нужно обрабатывать эти сообщения. Ваша оконная процедура в конце концов получит другие сообщения, являющиеся результатом этих аппаратных сообщений клавиатуры (например, выбор меню). Если вы хотите включить в код вашей оконной процедуры инструкции для обработки аппаратных сообщений клавиатуры (как мы это сделаем в программе KEYLOOK, представленной далее в этой главе), то после обработки этих сообщений передайте их в DefWindowProc, чтобы Windows могла по-прежнему их использовать в
обычных целях.
Но подумайте немного об этом. Почти все, что влияет на окно вашей программы, в первую очередь проходит через вашу оконную процедуру. Windows каким-то образом обрабатывает сообщения только в том случае, если они передаются в DefWindowProc. Например, если вы добавите строки: case WM_SYSKEYDOWN; case WM_SYSKEYUP; case WM_SYSCHAR; return 0; в оконную процедуру, то запретите все операции с клавишей (команды меню, +, + и т. д.), когда ваша программа имеет фокус ввода. Маловероятно, что вы захотели бы так сделать, тем не менее есть надежда, что вы начинаете понимать мощь вашей оконной процедуры.
Сообщения WM_KEYDOWN и WM_KEYUP обычно вырабатываются для клавиш, которые нажимаются и отпускаются без участия клавиши . Ваша программа может использовать или не использовать эти сообщения клавиатуры. Сама Windows их игнорирует.
Переменная
lParam

Для всех аппаратных сообщений клавиатуры, 32-разрядная переменная lParam, передаваемая в оконную процедуру, состоит из шести полей: счетчика повторений (Repeat Count), cкан-кода OEM (Original Equipment
Manufacturer Scan Code), флага расширенной клавиатуры (Extended Key Flag), кода контекста (Context Code), флага предыдущего состояния клавиши (Previous Key State) и флага состояния клавиши (Transition State). (См. рис. 5.1)

174
Extended K ey Flag
C ontext C ode
Previos K ey State
Transition State
8-B it O EM
Scan C ode
16-B it
R epeat C ount
Рис. 5.1 Шесть полей переменной lParam аппаратных сообщений клавиатуры
Счетчик повторений
Счетчик повторений равен числу нажатий клавиши, которое отражено в сообщении. В большинстве случаев он устанавливается в 1. Однако, если клавиша остается нажатой, а ваша оконная процедура недостаточно быстра, чтобы обрабатывать эти сообщения в темпе автоповтора (по умолчанию это приблизительно 10 символов в секунду), то Windows объединяет несколько сообщений WM_KEYDOWN или WM_SYSKEYDOWN в одно сообщение и соответственно увеличивает счетчик повторений. Для сообщения WM_KEYUP или WM_SYSKEYUP счетчик повторений всегда равен 1.
Поскольку значение счетчика большее 1 показывает, что имеющийся темп автоповтора превышает возможности вашей программы по обработке, вы можете игнорировать счетчик повторений при обработке сообщений клавиатуры.
Почти каждый из вас имел опыт работы с документами в программах текстовых редакторов или электронных таблиц, и видел, как строки или страницы продолжают перелистываться и после отпускания соответствующей клавиши. Это происходит из-за дополнительных сообщений в буфере клавиатуры, которые накапливаются там, если некоторое время удерживать клавишу. Игнорирование счетчика повторений в вашей программе существенно снизит вероятность проявления этого эффекта. Тем не менее, счетчик повторений может быть использован. Вероятно, вам следует протестировать в ваших программах обе эти возможности и выбрать наиболее подходящий вариант.
Скан-код OEM
Скан-код OEM является кодом клавиатуры, генерируемым аппаратурой компьютера. (Если вы хорошо знакомы с программированием на языке ассемблера, то скан-код — это код, передаваемый программе в регистре AH при вызове прерывания 16H BIOS.) Приложения Windows обычно игнорируют скан-код OEM, поскольку имеют более совершенные способы расшифровки информации от клавиатуры.
Флаг расширенной клавиатуры
Флаг расширенной клавиатуры устанавливается в 1, если сообщение клавиатуры появилось в результате работы с дополнительными клавишами расширенной клавиатуры IBM. (Расширенная клавиатура IBM имеет функциональные клавиши сверху и отдельную комбинированную область клавиш управления курсором и цифр.)
Этот флаг устанавливается в 1 для клавишей и на правой стороне клавиатуры, клавиш управления курсором (включая и ), которые не являются частью числовой клавиатуры, клавиш наклонной черты () и на числовой клавиатуре и клавиши . Программы для Windows обычно игнорируют флаг расширенной клавиатуры.
Код контекста
Код контекста устанавливается в 1, если нажата клавиша . Этот разряд всегда равен 1 для сообщений
WM_SYSKEYDOWN и WM_SYSKEYUP и 0 для сообщений WM_KEYDOWN и WM_KEYUP с двумя исключениями:

Если активное окно минимизировано, оно не имеет фокус ввода. Все нажатия клавиш вырабатывают сообщения WM_SYSKEYDOWN и WM_SYSKEYUP. Если не нажата клавиша , поле кода контекста устанавливается в 0. (Windows использует SYS сообщения клавиатуры так, чтобы активное окно, которое минимизировано, не обрабатывало эти сообщения.)

На некоторых иноязычных клавиатурах некоторые символы генерируются комбинацией клавиш ,
или с другой клавишей. В этих случаях, у переменной lParam, которая сопровождает сообщения WM_KEYDOWN и WM_KEYUP, в поле кода контекста ставится 1, но эти сообщения не являются системными сообщениями клавиатуры.

175
Флаг предыдущего состояния клавиши
Флаг предыдущего состояния клавиши равен 0, если в предыдущем состоянии клавиша была отпущена, и 1, если в предыдущем состоянии она была нажата. Он всегда устанавливается в 1 для сообщения WM_KEYUP или
WM_SYSKEYUP, но для сообщения WM_KEYDOWN или WM_SYSKEYDOWN он может устанавливаться как в
1, так и в 0. В этом случае 1 показывает наличие второго и последующих сообщений от клавиш в результате автоповтора.
Флаг состояния клавиши
Флаг состояния клавиши равен 0, если клавиша нажимается, и 1, если клавиша отпускается. Это поле устанавливается в 0 для сообщения WM_KEYDOWN или WM_SYSKEYDOWN и в 1 для сообщения WM_KEYUP или WM_SYSKEYUP.
Виртуальные коды клавиш
Хотя некоторая информация в lParam при обработке сообщений WM_KEYUP, WM_KEYDOWN, WM_SYSKEYUP и
WM_SYSKEYDOWN может оказаться полезной, гораздо более важен параметр wParam. В этом параметре содержится "виртуальный код клавиши" (virtual key code), идентифицирующий нажатую или отпущенную клавишу. Разработчики Windows попытались определить виртуальные клавиши независимым от аппаратуры способом. По этой причине, некоторые виртуальные коды клавиш не могут вырабатываться на персональных компьютерах IBM и совместимых с ними, но их можно встретить на клавиатурах других производителей.
Виртуальные коды клавиш, которые вы используете, наиболее часто имеют имена, определенные в заголовочных файлах Windows. В показанной ниже таблице представлены эти имена, числовые коды клавиш, а также клавиши персональных компьютеров IBM, соответствующие виртуальным клавишам. Хотя все клавиши вызывают аппаратные сообщения клавиатуры, в таблицу не включены клавиши символов (например, клавиши символов / или
?). Эти клавиши имеют виртуальные коды от 128 и выше, и они в международных клавиатурах часто определяются по-другому. Вы можете определить значения этих виртуальных кодов клавиш с помощью программы KEYLOOK, представленной далее в этой главе, но обычно вам не нужно обрабатывать аппаратные сообщения клавиатуры для этих клавиш.
Виртуальные коды клавиш
Деся- тичное
Шестнадца- теричное
WINDOWS.H
Идентификатор
Требу- ется
Клавиатура IBM
1 01 VK_LBUTTON
2 02
VK_RBUTTON
3 03
VK_CANCEL

Ctrl-Break
4 04
VK_MBUTTON
8 08
VK_BACK

Backspace
9 09
VK_TAB

Tab
12 0C
VK_CLEAR 5 на дополнительной цифровой клавиатуре при отключенном
Num Lock
13 0D
VK_RETURN

Enter
16 10 VK_SHIFT

Shift
17 11 VK_CONTROL

Ctrl
18 12 VK_MENU

Alt
19 13 VK_PAUSE
Pause
20 14 VK_CAPITAL

Caps Lock
27 1B VK_ESCAPE

Esc
32 20
VK_SPACE

Пробел (Spacebar)
33 21 VK_PRIOR

Page Up
34 22
VK_NEXT

Page Down
35 23
VK_END
End
36 24
VK_HOME

Home
37 25
VK_LEFT

Стрелка влево
38 26
VK_UP

Стрелка вверх
39 27
VK_RIGHT

Стрелка вправо
40 28
VK_DOWN

Стрелка вниз
41 29
VK_SELECT
42 2A
VK_PRINT
43 2B
VK_EXECUTE
44 2C
VK_SNAPSHOT
Print
Screen
45 2D
VK_INSERT

Insert

176
Деся- тичное
Шестнадца- теричное
WINDOWS.H
Идентификатор
Требу- ется
Клавиатура IBM
46 2E
VK_DELETE

Delete
47 2F
VK_HELP
48-57 30-39

От 0 до 9 на основной клавиатуре
65-90 41-5A

От A до Z
96 60
VK_NUMPAD0 0 на дополнительной цифровой клавиатуре при включенном Num
Lock
97 61 VK_NUMPAD1 1 на дополнительной цифровой клавиатуре при включенном Num
Lock
98 62
VK_NUMPAD2 2 на дополнительной цифровой клавиатуре при включенном Num
Lock
99 63
VK_NUMPAD3 3 на дополнительной цифровой клавиатуре при включенном Num
Lock
100 64
VK_NUMPAD4 4 на дополнительной цифровой клавиатуре при включенном Num
Lock
101 65
VK_NUMPAD5 5 на дополнительной цифровой клавиатуре при включенном Num
Lock
102 66
VK_NUMPAD6 6 на дополнительной цифровой клавиатуре при включенном Num
Lock
103 67
VK_NUMPAD7 7 на дополнительной цифровой клавиатуре при включенном Num
Lock
104 68
VK_NUMPAD8 8 на дополнительной цифровой клавиатуре при включенном Num
Lock
105 69
VK_NUMPAD9 9 на дополнительной цифровой клавиатуре при включенном Num
Lock
106 6A
VK_MULTIPLY
* на дополнительной цифровой клавиатуре
107 6B
VK_ADD
+ на дополнительной цифровой клавиатуре
108 6C
VK_SEPARATOR
109 6D
VK_SUBTRACT
- на дополнительной цифровой клавиатуре
110 6E
VK_DECIMAL на дополнительной цифровой клавиатуре
111 6F
VK_DIVIDE
/ на дополнительной цифровой клавиатуре
112 70
VK_F1

Функциональная клавиша F1 113 71 VK_F2

Функциональная клавиша F2 114 72
VK_F3

Функциональная клавиша F3 115 73
VK_F4

Функциональная клавиша F4 116 74
VK_F5

Функциональная клавиша F5 117 75
VK_F6

Функциональная

177
Деся- тичное
Шестнадца- теричное
WINDOWS.H
Идентификатор
Требу- ется
Клавиатура IBM клавиша F6 118 76
VK_F7

Функциональная клавиша F7 119 77
VK_F8

Функциональная клавиша F8 120 78
VK_F9

Функциональная клавиша F9 121 79
VK_F10

Функциональная клавиша F10 122 7A
VK_F11
Функциональная клавиша
F11
(расширенная клавиатура)
123 7B
VK_F12
Функциональная клавиша
F12
(расширенная клавиатура)
124 7C
VK_F13 125 7D
VK_F14 126 7E
VK_F15 127 7F
VK_F16 144 90
VK_NUMLOCK
Num
Lock
145 91 VK_SCROLL
Scroll
Lock
Пометка (

) в столбце "Требуется" показывает, что клавиша предопределена для любой реализации Windows.
Windows также требует, чтобы клавиатура и драйвер клавиатуры позволяли комбинировать клавиши ,
, а также и вместе, со всеми буквенными клавишами, всеми клавишами управления курсором и всеми функциональными клавишами. Виртуальные коды клавиш VK_LBUTTON, VK_MBUTTON и
VK_RBUTTON относятся к левой, центральной и правой кнопкам мыши. Однако, вам никогда не удастся получить сообщения клавиатуры с параметром wParam, в котором установлены эти значения. Мышь, как мы увидим в следующей главе, вырабатывает свои собственные сообщения.
Положения клавиш сдвига и клавиш-переключателей
Параметры
wParam и
lParam сообщений WM_KEYUP, WM_KEYDOWN, WM_SYSKEYUP и
WM_SYSKEYDOWN ничего не сообщают вашей программе о положении клавиш сдвига и клавиш- переключателей. Вы можете получить текущее состояние любой виртуальной клавиши с помощью функции
GetKeyState. Эта функция в основном используется для получения информации о состоянии клавиш сдвига
(, и ) и клавиш-переключателей (, и ). Например:
GetKeyState(VK_SHIFT); возвращает отрицательное значение (т. е., установлен старший разряд), если клавиша нажата. В возвращаемом функцией:
GetKeyState(VK_CAPITAL); значении установлен младший разряд, если переключатель включен. Вы также можете получить положение кнопок мыши с помощью виртуальных кодов клавиш VK_LBUTTON, VK_MBUTTON и
VK_RBUTTON. Однако, большая часть программ для Windows, которым надо отслеживать сочетание состояний кнопок мыши и клавиш клавиатуры, делают это другим способом — проверяя состояние клавиш клавиатуры при получении сообщения от мыши. Фактически, информация о положении клавиш сдвига и клавиш-переключателей включается в сообщения от мыши (как вы увидите в следующей главе).
Будьте осторожны с функцией GetKeyState. Она не отражает положение клавиатуры в реальном времени. Она отражает состояние клавиатуры на момент, когда последнее сообщение от клавиатуры было выбрано из очереди.
Функция GetKeyState не позволяет вам получать информацию о клавиатуре, независимо от обычных сообщений клавиатуры. Например, вы захотите, чтобы процесс обработки сообщения в оконной процедуре продолжался до тех пор, пока пользователь не нажмет функциональную клавишу : while(GetKeyState(VK_F1) >= 0); //
ОШИБКА
!!!
Не делайте так! Вашей программе необходимо получить сообщение клавиатуры из очереди сообщений до того, как
GetKeyState сможет определить состояние клавиши. Эта синхронизация, в самом деле, дает вам преимущество, потому что, если вам нужно узнать положение переключателя для конкретного сообщения клавиатуры,
GetKeyState обеспечивает возможность получения точной информации, даже если вы обработаете сообщение уже

178 после того, как состояние переключателя было изменено. Если вам действительно нужна информация о текущем положении клавиши, вы можете использовать функцию GetAsyncKeyState.
Использование сообщений клавиатуры
Идея программы, получающей информацию о нажатии любой клавиши несомненно привлекательна; однако, большинство программ для Windows игнорируют все, кроме нескольких сообщений о нажатии и отпускании клавиш. Сообщения WM_SYSKEYUP и WM_SYSKEYDOWN адресованы системным функциям Windows, и вам не нужно их отслеживать. Если вы обрабатываете сообщения WM_KEYDOWN, то сообщения WM_KEYUP вам обычно также можно игнорировать.
Программы для Windows обычно используют сообщения WM_KEYDOWN для нажатия и отпускания клавиш, которые не генерируют символьные сообщения. Хотя вы можете подумать, что есть возможность использовать сообщения о нажатии клавиш в сочетании с информацией о состоянии клавиш сдвига для преобразования сообщений о нажатии клавиш в символьные сообщения, не делайте так. У вас будут проблемы из-за отличий международных клавиатур. Например, если вы получаете сообщение WM_KEYDOWN с wParam равным 33H, вы знаете, что пользователь нажал клавишу <3>. Так, хорошо. Если вы используете GetKeyState и обнаруживаете, что клавиша нажата, то можно было бы предположить, что пользователь печатает знак фунта стерлингов <
£
>.
Вовсе необязательно. Британский пользователь печатает знак <&>. Поэтому сообщения WM_KEYDOWN более удобны для клавиш управления курсором, функциональных клавиш и специальных клавиш, таких как и
. Однако, иногда клавиши и , а также функциональные клавиши используются в качестве быстрых клавиш меню. Поскольку Windows преобразует быстрые клавиши меню в сообщения команд меню, вы не должны сами обрабатывать эти сообщения. Некоторые программы, написанные не для Windows, широко используют функциональные клавиши в сочетании с клавишами , и . Вы можете сделать что-то похожее в ваших программах для Windows, но это не рекомендуется. Если вы хотите использовать функциональные клавиши, то лучше, чтобы они дублировали команды меню. Одна из задач Windows — обеспечить такой пользовательский интерфейс, для которого не требуется заучивание или использование сложного набора команд.
Мы собираемся отказаться от всего, за исключением последнего пункта: большую часть времени вы будете обрабатывать сообщения WM_KEYDOWN только для клавиш управления курсором. Если вы используете клавиши управления курсором, то можете контролировать состояние клавиш и с помощью функции
GetKeyState. Функции Windows часто используют клавишу в сочетании с клавишами управления курсором для расширения выбора, например, в программах текстовых редакторов. Клавиша часто используется для изменения значения клавиш управления курсором. (Например, в сочетании с клавишей стрелки вправо могло бы означать перемещение курсора на одно слово вправо.)
Одним из лучших способов выяснить то, как использовать клавиатуру должно быть изучение использования клавиатуры в существующих популярных программах для Windows. Если вам это не подходит, можете действовать как-то иначе. Но запомните, что в этом случае вы можете помешать пользователю быстро изучить вашу программу.
Модернизация SYSMETS: добавление интерфейса клавиатуры
Когда в главе 3 мы написали три версии программы SYSMETS, мы ничего не знали о клавиатуре. Мы могли прокрутить текст только с помощью мыши на полосе прокрутки. Теперь мы знаем, как обрабатывать сообщения клавиатуры, давайте добавим интерфейс клавиатуры в программу SYSMETS. Это очевидно будет работа для клавиш управления курсором. Мы используем большинство клавиш управления курсором



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


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

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


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