Пока ваша программа не делает ничего интересного. В данной главе мы возьмем программу Step, которая пока представляет собой просто оболочку программы, и преобразуем ее в полезное интерактивное графическое приложение. Сначала мы выведем в основном окне текст. Затем преобразуем Step в полное графическое приложение, позволяющее вам отображать в основном окне линии различной толщины.
    В качестве первого шага в направлении создания
отображающей
программы нарисуем в окне некоторые текстовые символы. Текст будет
отображаться в ответ на щелчок левой кнопкой "мыши", который
вы перехватывали на шаге 1. Но вместо вывода окна сообщения на
этот раз реакцией будет вывод текста, показывающего координаты
той точки в окне, где вы щелкнули кнопкой "мыши".
    В графической операционной среде типа Windows
текст рисуется
как графика, а не выводится в виде символов на стандартное устройство
вывода. В некотором смысле это эквивалентно использованию
позиционирования курсора в программах DOS, где вы задаете расположение
каждого текстового элемента, а не полагаетесь на прокрутку экрана.
Конечно, в Windows вы можете также управлять размером, стилем,
гарнитурой и цветом текста.
    Чтобы вывести текст в основном окне программы Step,
вам нужна некоторая отображаемая информация. В Windows имеются специальные
средства, управляющие ее графикой, которые называются контекстом дисплея.
Контекст дисплея можно рассматривать как элемент, представляющий
поверхность окна, где выводится изображение.
Контекст экрана необходим Windows для отображения в окне любого
текста или графики.
    Примечание: Подробно о контексте дисплея
рассказывается в Главе 17 "Интерфейс с графическими устройствами".
    Контекст дисплея имеет три основных функции отображения:
    Поскольку, чтобы рисовать в окне при буксировке
"мыши", вам
необходим контекст дисплея, создайте в объекте основного окна новое поле
с именем DragDC, которое будет содержать описатель контекста дисплея.
DragDC имеет тип HDC, который эквивалентен типу Word.
    Чтобы использовать контекст дисплея, ваша программа
должна:
    Чтобы отобразить что-то в окне, вы должны сначала
получить
контекст дисплея. Это можно сделать, вызвав в одном из методов
типа непосредственно перед отображением на экране функцию Windows
GetDC:
    Теперь вы можете использовать DragDC в качестве
параметра в
вызовах графических функций Windows, требующих указания контекста
дисплея. Приведем несколько примеров:
    После отображения текста или графики вы должны
освободить контекст дисплея (как только закончите отображение).
    Windows выделяет по пять контекстов дисплея на
приложение, которые можно совместно использовать через GetDC. Пока в
Windows зарезервировано достаточно памяти, вы можете с помощью GetDC
получить другие контексты.
    Примечание: GDI и вопросы использования памяти
освещаются в Главе 17 "Интерфейс с графическим устройством".
    Если вы работали с графикой раньше, то вам уже
знакомо понятие системы координат. В Windows координаты потребуются
вам для вывода текста.
    При отображении вас касаются только координаты
в контексте дисплея. Windows обеспечивает, чтобы контекст дисплея
попадал в область клиента окна.
    Примечание: Область клиента - это часть окна внутри
рамки.
    На этом шаге Step отобразит текст, показывающий
координаты той точки в окне, где вы щелкнули кнопкой "мыши". Например,
'(20,30)' - это точка, отстоящая на 20 элементов изображения вправо и
на 30 элементов изображения вниз от верхнего левого угла поверхности
отображения. Вы можете отображать прямо в той точке, где
щелкнули "мышью". Это показано на Рис. 2.1.
    Щелчок левой кнопкой "мыши" генерирует
сообщение wm_LButtonDown, который вы перехватываете с помощью метода
реакции на сообщение WMLButtonDown.
    Параметр Msg метода реакции на сообщение несет
информацию о
породившем сообщение событии (такую как координаты точки, где
пользователь щелкнул кнопкой "мыши"). Msg - это запись TMessage,
поля которой содержат параметр lParam типа Longint и параметр
wParam типа Word. Идентификаторы lParam и wParam соответствуют
полям в структуре сообщения Windows TMsg.
    TMessage определяют также вариантные поля,
содержащие подполя lParam и wParam. Например, Msg.lParamLo содержит
младшее слово
lParam, а Msg.lParamHi - старшее слово. Чаще всего используются
поля wParam, lParamLo и lParamHi.
    В случае WMLButtonDown Msg.lParamLo содержит
x-координату
точки нажатия кнопки "мыши", а Msg.lParamHi - y-координату этой
точки. Таким образом, чтобы переписать WMLButtonDown для отображения
координат точки нажатия кнопки, нужно преобразовать
Msg.lParamLo и Msg.lParamHi в строки и, чтобы они приняли вид
'(25,21)', конкатенировать их с запятой. В примере для форматирования
строки используется функция Windows WVSPrintF.
    Примечание: Слияние параметров зависит от сообщения.
Подробности о каждом сообщении и его параметре вы можете
узнать, воспользовавшись оперативным справочником Help.
    После получения итоговой строки ее можно вывести
в точке нажатия кнопки "мыши" с помощью функции Windows TextOut. Перед
отображением нужно получить контекст дисплея, а после отображения
- освободить его.
    В приложение, отображающее текст, вы можете
также добавить
еще одну функцию - функцию отчистки окна. Заметим, что после изменения
размера окна, либо когда вы скрываете его и выводите снова, нарисованный
текст стирается. Однако, можно задать принудительную очистку окна в
ответ на команду меню или какое-либо другое действие пользователя,
например, щелчок кнопкой "мыши".
    Чтобы очистить окно в ответ на щелчок правой
кнопкой "мыши", переопределите метод WMRButtonDown и вызовите в нем
процедуру InvalidateRect, которая приводит к повторному отображению всего
окна. Так как ваша программа пока не знает, как повторно вывести
изображение, она просто очистит область клиента:
    Теперь, когда вы познакомились со схемой отображения
(получение контекста дисплея, отображение, освобождение контекста
дисплея), ее можно использовать в более полном интерактивном графическом
приложении. Следующие несколько шагов посвящены построению простой
графической программы, позволяющей пользователю рисовать в основном окне.
    На шаге 3 мы добавим следующее поведение:
    Чтобы выполнить эти шаги, изучим сначала схему
буксировки Windows, а затем реализуем простую графическую программу.
    Мы уже видели, что щелчок левой кнопкой "мыши" дает
в результате сообщение wm_LButtonDown и вызывает метод WMLButtonDown.
В шаге 1 ваша программа отвечала на щелчки левой кнопкой "мыши",
выводя окна сообщений. Вы могли также видеть, что щелчок правой
кнопкой "мыши" давал в результате сообщение wm_RButtonDown и вызывал
метод WMRButtonDown. На нажатие правой кнопки "мыши" программа отвечала
очисткой окна.
    Но это предусматривает реакцию только на щелчки
кнопкой "мыши". Многие программы Windows требуют от пользователя нажатия
кнопки "мыши" и перемещения ее указателя по экрану (буксировка).
При этом рисуются линии или прямоугольники, либо графическое
изображение помещается в точку с конкретными координатами. Для
программ графического отображения желателен перехват событий буксировки и
реакция на них путем изображения линий.
    Сделать это можно путем реакции еще на несколько
сообщений.
Когда пользователь буксирует "мышь" в новую точку окна, Windows
посылает сообщение wm_MouseMove, а когда пользователь отпускает
левую кнопку "мыши" - сообщение wm_LButtonUp. Обычно окно получает одно
сообщение wm_LButtonDown, за которым следует последовательность сообщений
wm_MouseMove (по одному на каждую промежуточную точку буксировки) и одно
сообщение wm_LButtonUp.
    Типичная графическая программа Windows реагирует
на сообщение wm_LButtonDown инициализацией процесса рисования (получая,
кроме всего прочего, контекст дисплея). На сообщение wm_LButtonUp
она реагирует завершением процесса рисования (освобождая контекст
дисплея).
    Нужно помнить о том, что после wm_LButtonDown
всегда следует сообщение wm_LButtonUp (с промежуточными сообщениями
wm_MouseMove или без них). Таким образом, каждый раз, когда вы
получаете контекст дисплея, вы можете позднее освободить его.
    Для правильного функционирования программы
Windows очень
важным является освобождение каждого получаемого вами контекста
дисплея. Однако вы можете добавить еще одно более надежное средство.
Определите в TStepWindow новое булевское поле - тип основного окна с
именем ButtonDown и обеспечьте его инициализацию в
TStepWindow.Unit значением False. Затем вы можете проверять перед
получением и освобождением контекста дисплея значение ButtonDown.
    Приведем три метода обработки буксировки "мыши":
    В API Windows имеются графические функции
MoveTo и LineTo, которые, соответственно, перемещают текущую позицию
рисования и
рисуют линию до текущей позиции. Для правильной работы функций
требуется указание описателя контекста дисплея DragDC. Нужно помнить о
том, что вы рисуете не непосредственно в окне, а в его
контексте дисплея.
    Передачу Windows соответствующих сообщений
wm_MouseMove
обеспечивают функции SetCapture и ReleaseCapture. Например, если
вы буксируете "мышь" за пределы окна, Windows все равно будет посылать
сообщения основному, а не смежному с ним окну, в которое
она попала. Перехват "мыши" обеспечивает также поступление в ваше
окно сообщения от "мыши", так что оно будет знать о прекращении
рисования даже если "мышь" перемещается в другом окне.
    Нужно изменить определение объекта для
TStepWindow с заголовками метода для WMMouseMove и WMLButtonUp:
    До сих пор вы могли рисовать только тонкие черные линии.
Однако графические программы традиционно позволяют изменять толщину
изображаемых линий. При этом вы изменяете на самом деле не толщину линии,
а размер пера, используемого для ее вычерчивания.
Перья, также как кисти, шрифты и палитры, представляют собой
встроенные в контекст дисплея изобразительные средства. На данном
шаге вы узнаете, как установить в контексте дисплея новые изобразительные
средства, что даст программе Step возможность рисовать линии другой толщины.
    Для реализации механизма выбора пользователем
размера пера мы используем диалоговое окно (типа TInputDialog).
Это окно вида:
Шаг 2: Отображение текста в окне
Step 1 Basic App Step 2 Text Step 3 Lines Step 4 Menu Step 5 About Box Step 6 Pens Step 7 Painting Step 8 Streams Step 9 Printing Step 10 Palette Step 11 BWCC Step 12 Custom ctrls Вывод в контексте дисплея
Что такое контекст дисплея?
    Windows управляет контекстом дисплея в своем
собственном
пространстве памяти, но ваша прикладная программа может отслеживать
контекст дисплея с помощью описателей. Как и описатель окна,
описатель контекста дисплея - это число, идентифицирующее корректный
контекст дисплея Windows.
Получение контекста дисплея
DragDC := GetDC(HWindow);
Использование контекста дисплея
TextOut(DragDC, 20, 20, 'Пример текста', 11);
LineTo(DragDC, 30, 45);
Освобождение контекста дисплея
ReleaseDC(HWindow, DragDC);
    Если вы не освободите весь полученный контекст дисплея,
то скоро исчерпаете его, что приводит обычно к зависанию компьютера.
Если ваша программа не может что-либо отобразить, проверьте освобождение
контекстов дисплея.Координаты Windows
Рис. 2.1 Отображение текста в точке нажатия кнопки "мыши".Параметры сообщений
procedure TStepWindow.WMLButtonDown(var Msg: TMessage);
var S: array[0..9] of Char;
begin
WVSPrint(S, '(%d,%d)', Msg.LParam);
DragDC := GetDC(HWindow);
TextOut(DragDc, Msg.LParamLo, Msg.LParamHi, S, StrLen(S));
ReleaseDC(HWindow, DragDC);
end;
    Примечание: Windows ожидает получения строк с
завершающим нулем (конечным нулевым байтом). Подробнее эти строки
описываются в Главе 18 "Руководства по языку".Очистка окна
Procedure TStepWindow.WMRButtonDown(var Msg: TMessage);
begin
InvelidateRect(HWindow, nil, Trut);
end;
    Текущий исходный код вы можете найти в файле STEP02.PAS.
Шаг 3: Изображение линий в окне
Step 1 Basic App Step 2 Text Step 3 Lines Step 4 Menu Step 5 About Box Step 6 Pens Step 7 Painting Step 8 Streams Step 9 Printing Step 10 Palette Step 11 BWCC Step 12 Custom ctrls
Буксировка линии
Сообщения wm_MouseMove
Реакция на сообщения буксировки
procedure TStepWindow.WMLButtonDown(var Msg: TMessage);
begin
InvalidateRect(HWindow, nil, True);
if not ButtonDown then
begin
ButtonDown := True;
SetCapture(HWindow);
DragDC := GetDC(HWindow);
MoveTo(DragDC, Msg.lParamLo, Msg.lParamHi);
end;
end;
procedure TStepWindow.WMMouseMove(var Msg: TMessage);
begin
if ButtonDown then
LineTo(DragDC, Msg.lParamLo, MsglParamHi);
end;
procedure TStepWindow.WMLButtonUp(var Msg: TMessage);
begin
if ButtonDown then
begin
ButtonDown := False;
ReleaseCapture;
ReleaseDC(HWindow, DragDC);
end;
end;
Изображение точек и линий
Перехват "мыши"
procedure WMLButtonUp(var Msg: TMessage); virtual wm_First +
wm_LButtonUp;
procedure WMLMouseMove(var Msg: TMessage); virtual wm_First +
wm_LMouseMove;
    Пример полученного исходного кода вы
найдете в файле STEP03A.PAS.Изменение размера пера
Рис. 2.2 Задание новой толщины линии с помощью диалогового
окна ввода.