- •Содержание
- •Глава 1 README.TXT
- •Вызов, брошенный программистам
- •Основные правила
- •Краткая история Windows
- •Краткая история этой книги
- •Начнем
- •Глава 2 Hello, Windows 95
- •Отличительная особенность Windows
- •Графический интерфейс пользователя
- •Концепции и обоснование GUI
- •Содержимое интерфейса пользователя
- •Преимущество многозадачности
- •Управление памятью
- •Независимость графического интерфейса от оборудования
- •Соглашения операционной системы Windows
- •Вызовы функций
- •Объектно-ориентированное программирование
- •Архитектура, управляемая событиями
- •Оконная процедура
- •Ваша первая программа для Windows
- •Что в этой программе неправильно?
- •Файлы HELLOWIN
- •Make-файл
- •Вызовы функций Windows
- •Идентификаторы, написанные прописными буквами
- •Новые типы данных
- •Описатели
- •Венгерская нотация
- •Точка входа программы
- •Регистрация класса окна
- •Создание окна
- •Отображение окна
- •Цикл обработки сообщений
- •Оконная процедура
- •Обработка сообщений
- •Воспроизведение звукового файла
- •Сообщение WM_PAINT
- •Сообщение WM_DESTROY
- •Сложности программирования для Windows
- •Не вызывай меня, я вызову тебя
- •Синхронные и асинхронные сообщения
- •Думайте о ближнем
- •Кривая обучения
- •Глава 3 Рисование текста
- •Рисование и обновление
- •Сообщение WM_PAINT
- •Действительные и недействительные прямоугольники
- •Введение в графический интерфейс устройства (GDI)
- •Контекст устройства
- •Структура информации о рисовании
- •Получение описателя контекста устройства. Второй метод
- •Функция TextOut. Подробности
- •Системный шрифт
- •Размер символа
- •Метрические параметры текста. Подробности
- •Форматирование текста
- •Соединим все вместе
- •Не хватает места!
- •Размер рабочей области
- •Полосы прокрутки
- •Диапазон и положение полос прокрутки
- •Сообщения полос прокрутки
- •Прокрутка в программе SYSMETS
- •Структурирование вашей программы для рисования
- •Создание улучшенной прокрутки
- •Мне не нравится пользоваться мышью
- •Глава 4 Главное о графике
- •Концепция GDI
- •Структура GDI
- •Типы функций
- •Примитивы GDI
- •Другие аспекты
- •Контекст устройства
- •Получение описателя контекста устройства
- •Программа DEVCAPS1
- •Размер устройства
- •О цветах
- •Атрибуты контекста устройства
- •Сохранение контекста устройства
- •Рисование отрезков
- •Ограничивающий прямоугольник
- •Сплайны Безье
- •Использование стандартных перьев
- •Создание, выбор и удаление перьев
- •Закрашивание пустот
- •Режимы рисования
- •Рисование закрашенных областей
- •Функция Polygon и режим закрашивания многоугольника
- •Закрашивание внутренней области
- •Режим отображения
- •Координаты устройства (физические координаты) и логические координаты
- •Системы координат устройства
- •Область вывода и окно
- •Работа в режиме MM_TEXT
- •Метрические режимы отображения
- •Ваши собственные режимы отображения
- •Программа WHATSIZE
- •Прямоугольники, регионы и отсечение
- •Работа с прямоугольниками
- •Случайные прямоугольники
- •Создание и рисование регионов
- •Отсечения: прямоугольники и регионы
- •Программа CLOVER
- •Пути
- •Создание и воспроизведение путей
- •Расширенные перья
- •Bits and Blts
- •Цвета и битовые образы
- •Файл DIB
- •Упакованный формат хранения DIB
- •Отображение DIB
- •Преобразование DIB в объекты "битовые образы"
- •Битовый образ — объект GDI
- •Создание битовых образов в программе
- •Формат монохромного битового образа
- •Формат цветного битового образа
- •Контекст памяти
- •Мощная функция BitBlt
- •Перенос битов с помощью функции BitBlt
- •Функция DrawBitmap
- •Использование других ROP кодов
- •Дополнительные сведения о контексте памяти
- •Растяжение битовых образов с помощью функции StretchBlt
- •Кисти и битовые образы
- •Метафайлы
- •Простое использование метафайлов памяти
- •Сохранение метафайлов на диске
- •Расширенные метафайлы
- •Делаем это лучше
- •Базовая процедура
- •Заглянем внутрь
- •Вывод точных изображений
- •Текст и шрифты
- •Вывод простого текста
- •Атрибуты контекста устройства и текст
- •Использование стандартных шрифтов
- •Типы шрифтов
- •Шрифты TrueType
- •Система EZFONT
- •Внутренняя работа
- •Форматирование простого текста
- •Работа с абзацами
- •Глава 5 Клавиатура
- •Клавиатура. Основные понятия
- •Игнорирование клавиатуры
- •Фокус ввода
- •Аппаратные и символьные сообщения
- •Аппаратные сообщения
- •Системные и несистемные аппаратные сообщения клавиатуры
- •Переменная lParam
- •Виртуальные коды клавиш
- •Использование сообщений клавиатуры
- •Модернизация SYSMETS: добавление интерфейса клавиатуры
- •Логика обработки сообщений WM_KEYDOWN
- •Посылка асинхронных сообщений
- •Символьные сообщения
- •Сообщения WM_CHAR
- •Сообщения немых символов
- •Каретка (не курсор)
- •Функции работы с кареткой
- •Программа TYPER
- •Наборы символов Windows
- •Набор символов OEM
- •Набор символов ANSI
- •Наборы символов OEM, ANSI и шрифты
- •Международные интересы
- •Работа с набором символов
- •Связь с MS-DOS
- •Использование цифровой клавиатуры
- •Решение проблемы с использованием системы UNICODE в Windows NT
- •Глава 6 Мышь
- •Базовые знания о мыши
- •Несколько кратких определений
- •Сообщения мыши, связанные с рабочей областью окна
- •Простой пример обработки сообщений мыши
- •Обработка клавиш <Shift>
- •Сообщения мыши нерабочей области
- •Сообщение теста попадания
- •Сообщения порождают сообщения
- •Тестирование попадания в ваших программах
- •Гипотетический пример
- •Пример программы
- •Эмуляция мыши с помощью клавиатуры
- •Добавление интерфейса клавиатуры к программе CHECKER
- •Использование дочерних окон для тестирования попадания
- •Дочерние окна в программе CHECKER
- •Захват мыши
- •Рисование прямоугольника
- •Решение проблемы — захват
- •Программа BLOKOUT2
- •Глава 7 Таймер
- •Основы использования таймера
- •Система и таймер
- •Таймерные сообщения не являются асинхронными
- •Использование таймера: три способа
- •Первый способ
- •Второй способ
- •Третий способ
- •Использование таймера для часов
- •Позиционирование и изменение размеров всплывающего окна
- •Получение даты и времени
- •Обеспечение международной поддержки
- •Создание аналоговых часов
- •Стандартное время Windows
- •Анимация
- •Класс кнопок
- •Создание дочерних окон
- •Сообщения дочерних окон родительскому окну
- •Сообщения родительского окна дочерним окнам
- •Нажимаемые кнопки
- •Флажки
- •Переключатели
- •Окна группы
- •Изменение текста кнопки
- •Видимые и доступные кнопки
- •Кнопки и фокус ввода
- •Дочерние окна управления и цвета
- •Системные цвета
- •Цвета кнопок
- •Сообщение WM_CTLCOLORBTN
- •Кнопки, определяемые пользователем
- •Класс статических дочерних окон
- •Класс полос прокрутки
- •Программа COLORS1
- •Интерфейс клавиатуры, поддерживаемый автоматически
- •Введение новой оконной процедуры
- •Закрашивание фона
- •Окрашивание полос прокрутки и статического текста
- •Класс редактирования
- •Стили класса редактирования
- •Коды уведомления управляющих окон редактирования
- •Использование управляющих окон редактирования
- •Сообщения управляющему окну редактирования
- •Класс окна списка
- •Стили окна списка
- •Добавление строк в окно списка
- •Выбор и извлечение элементов списка
- •Получение сообщений от окон списка
- •Простое приложение, использующее окно списка
- •Список файлов
- •Утилита Head для Windows
- •Компиляция ресурсов
- •Значки и курсоры
- •Редактор изображений
- •Получение описателя значков
- •Использование значков в вашей программе
- •Использование альтернативных курсоров
- •Битовые образы: картинки в пикселях
- •Использование битовых образов и кистей
- •Символьные строки
- •Использование ресурсов-символьных строк
- •Меню
- •Структура меню
- •Шаблон меню
- •Ссылки на меню в вашей программе
- •Меню и сообщения
- •Образец программы
- •Этикет при организации меню
- •Сложный способ определения меню
- •Третий подход к определению меню
- •Независимые всплывающие меню
- •Использование системного меню
- •Изменение меню
- •Другие команды меню
- •Использование в меню битовых образов
- •Два способа создания битовых образов для меню
- •Контекст памяти
- •Создание битового образа, содержащего текст
- •Масштабирование битовых образов
- •Соберем все вместе
- •Добавление интерфейса клавиатуры
- •Быстрые клавиши
- •Зачем нужны быстрые клавиши?
- •Некоторые правила назначения быстрых клавиш
- •Таблица быстрых клавиш
- •Преобразование нажатий клавиш клавиатуры
- •Получение сообщений быстрых клавиш
- •Программа POPPAD, имеющая меню и быстрые клавиши
- •Разрешение пунктов меню
- •Обработка опций меню
- •Глава 11 Окна диалога
- •Модальные окна диалога
- •Создание окна диалога About
- •Шаблон окна диалога
- •Диалоговая процедура
- •Вызов окна диалога
- •Дополнительная информация о стиле окна диалога
- •Дополнительная информация об определении дочерних окон элементов управления
- •Более сложное окно диалога
- •Работа с дочерними элементами управления окна диалога
- •Кнопки OK и Cancel
- •Позиции табуляции и группы
- •Рисование в окне диалога
- •Использование с окном диалога других функций
- •Определение собственных окон управления
- •Окна сообщений
- •Информация во всплывающих окнах
- •Немодальные окна диалога
- •Различия между модальными и немодальными окнами диалога
- •Новая программа COLORS
- •Программа HEXCALC: обычное окно или окно диалога?
- •Творческое использование идентификаторов дочерних окон элементов управления
- •Диалоговые окна общего пользования
- •Модернизированная программа POPPAD
- •Изменение шрифта
- •Поиск и замена
- •Программа для Windows, содержащая всего один вызов функции
- •Основы элементов управления общего пользования
- •Инициализация библиотеки
- •Создание элементов управления общего пользования
- •Стили элементов управления общего пользования
- •Уведомляющие сообщения от элементов управления общего пользования
- •Элементы управления главного окна
- •Панели инструментов
- •Создание панели инструментов
- •Строка состояния
- •Программа GADGETS
- •Наборы страниц свойств
- •Создание набора страниц свойств
- •Процедуры диалогового окна страницы свойств
- •Программа PROPERTY
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 дюйма на проекционном экране. Нет сомнений, что ваша аудитория будет в замешательстве.
Люди, постоянно работающие с текстами, часто используют текстовые процессоры и настольные издательские системы. Довольно часто вывод осуществляется на бумаге размером 81/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 больше, чем (xRight — xLeft), то срока слишком длинна, чтобы поместиться в указанных границах. Предположим, что строка все же помещается.
Для того, чтобы выровнять текст влево, нужно просто установить значение xStart равным xLeft и затем вывести текст:
TextOut(hdc, xStart, yStart, szText, strlen(szText));
Это просто. Теперь вы можете прибавить значение size.cy к yStart и затем выводить следующую строку текста. Чтобы выровнять текст вправо, воспользуемся для вычисления xStart следующей формулой:
xStart = xRight — size.cx;
Чтобы выровнять текст по центру между левой и правой границами используем другую формулу:
xStart =(xLeft + xRight — size.cx) / 2;
Теперь перед нами самая трудная задача — выровнять текст по всей ширине между левой и правой границами. Расстояние между ними вычисляется как (xRight — xLeft). Без растягивания по ширине текст имеет ширину 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 <windows.h> |
|
|
#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
Средства ввода