Окт 16 2009

Улучшенное приложение для просмотра текстовых файлов

При изучении полос прокрутки и вывода текста было создано приложение TextViewer, предназначенное для чтения и вывода на экран текстового файла. В том варианте приложения сообщения мыши не учитывались, но при работе с полосами прокрутки Windows берет на себя первичную обработку этих сообщений. В результате этой обработки Windows направляет окну сообщения с префиксом SB_. Именно эти сообщения и обрабатывала оконная процедура приложения.
Теперь, вооруженные новыми знаниями, мы хотели бы усовершенствовать приложение TextViewer, добавив в него обработку колесика мыши, а также возможность для пользователя общаться с программой через клавиатурный интерфейс. Второе требование означает, что все операции, выполняемые мышью, должны быть продублированы на клавиатуре.
Есть два пути решения второй задачи. Обрабатывая сообщение WM_KEYDOWN, можно было бы просто продублировать полностью код, отвечающий за обработку сообщений WM_VSCROLL и WM_HSCROLL. Но это некрасиво и непрактично с точки зрения дальнейшего сопровождения программы. Второй подход заключается в том, что при появлении аппаратного сообщение клавиатуры, которое дублирует действие мыши, следует вызывать тот код, который уже был написан для обработки сообщений, начинающихся префиксом SB_. Фактически, это означает, что оконная процедура должна послать сама себе сообщение SB_.
Win32 API имеет несколько функций для отправки сообщений. В данной ситуации лучше использовать функцию SendMessage, имеющую следующий прототип:
LRESULT SendMessage(HWND hWnd, UINT Msg. WPARAM wParam. LPARAM IParam);
Параметры функции имеют тот же смысл, что и параметры, передаваемые в оконную процедуру. Когда вызывается функция SendMessage, Windows вызывает оконную процедуру с дескриптором окна hWnd, передавая ей эти четыре параметра. После того как оконная процедура заканчивает обработку сообщения, Windows передает управление следующей за вызовом SendMessage инструкции.


Окт 16 2009

Эластичные прямоугольники

Графические редакторы, как правило, поддерживают операцию выделения некоторой прямоугольной области. Пользователь нажимает левую кнопку мыши, фиксируя одну из вершин прямоугольника, и перемещает мышь к противоположной вершине. В процессе перемещения мыши построение фигуры считается еще не законченным, и она выводится пунктирным контуром, который все время изменяется. При отпускании кнопки мыши прямоугольник фиксируется, обозначаясь, например, другим цветом или другим стилем линии контура.
Приложение ElasticRect, приведенное в листинге 4.4, демонстрирует реализацию подобной операции. Кроме того, в нем показана обработка двойного щелчка мыши, по которому программа рисует квадрат фиксированного размера.
Рисование линий и фигур происходит на некотором фоне или на некотором изображении, которое расположено в клиентской области окна. Для простоты дальнейшего изложения этот фон или существующее изображение будет называться холстом.
В основе рисования эластичных прямоугольников обычно лежит следующая алгоритмическая идея. Получив сообщение WM_M0USEM0VE, программа должна стереть предшествующее изображение прямоугольника, затем получить координаты новой позиции мыши и нарисовать новое изображение прямоугольника.
Проще всего стирание текущего изображения фигуры можно организовать, если в контексте устройства установлен режим рисования R2_X0RPEN или R2_N0TX0RPEN. В этом случае повторный вывод фигуры приводит к восстановлению того состояния холста, которое было перед первым выводом этой фигуры. Чем же различаются эти два режима или эти две растровые операции: R2_X0RPEN и R2_N0TX0RPEN?
Когда фигура рисуется первый раз, то наибольший интерес представляют ситуации, в которых цвет пера и холста различаются, что соответствует второй и третьей строкам в таблицах. Естественно, мы хотели бы в результате этого рисования получить цвет пера, а не холста. В графе «Примечание» показано, что такой результат достигается при рисовании белым пером на черном холсте для растровой операции R2_X0RPEN и при рисовании черным пером на белом холсте для растровой операции R2_N0TX0RPEN.


Окт 16 2009

Рисуем мышью

В любом графическом редакторе, как правило, предусмотрена возможность рисовать линии произвольной формы с помощью мыши. Пользователь устанавливает курсор на начальную точку линии, нажимает левую кнопку мыши и, не отпуская кнопку, перемещает мышь, рисуя прямые, кривые и другие замысловатые линии. Рисование линии заканчивается, когда левая кнопка отпускается.
Процесс рисования очередной линии начинается с нажатия левой кнопки мыши. Программа, обрабатывая сообщение WM_LBUTT0ND0WN, устанавливает флаг слежения за мышью bTracking в значение TRUE и задает начальную позицию пера при помощи функции MoveToEx. После этого при обработке сообщения WM_M0USEM0VE программа просто проводит линию в текущую позицию курсора мыши. Когда пользователь отпускает кнопку мыши, флаг bTracking сбрасывается в FALSE и рисование прекращается.
Обратите особое внимание на то, с каким контекстом устройства работают рисующие функции MoveToEx и LineTo. Дескриптор контекста дисплея hDC объявлен со спецификатором static. Это раз. Программа получает контекст устройства при помощи функции GetDC в блоке обработки сообщения WM_CREATE, а освобождает его с помощью ReleaseDC в блоке обработки сообщения WM_DESTROY. Это два. Все это позволяет обновлять клиентскую область непосредственно в ответ на пользовательский ввод информации с помощью мыши, не прибегая к механизмам, генерирующим сообщение WM_PAINT. Иными словами, обеспечивается минимальная задержка между вводом пользователя и реакцией приложения. Статический класс памяти для переменной hDC здесь необходим потому, что требуется сохранять значение атрибута «текущая позиция пера», используемого и модифицируемого функцией LineTo, между двумя последовательными вызовами функции WndProc.
Но что это?.. Как только пользователь пытается изменять размеры окна, ухватившись мышью за его рамку, изображение тотчас бесследно исчезает! Ах, да... Мы же забыли про обработку сообщения WM_PAINT, которое генерируется при изменении размеров окна!
Давайте устраним эту оплошность. Но что же будет рисовать приложение в блоке обработки сообщения WM_PAINT? Ведь информация о траекториях движения мыши нигде не сохранена! Значит, надо где-то сохранять массивы координат позиций мыши для каждой рисуемой линии. Сложность проблемы состоит в том, что количество линий заранее неизвестно, так же как и количество точек в каждой линии может быть произвольным. Таким образом, использование статических массивов будет крайне неудобным.
На выручку в таких ситуациях приходит замечательный класс vector из стандартной библиотеки шаблонов STL. Объект класса vector — это, фактически, динамический массив с переменным размером, обслуживаемый набором удобных для программиста методов. Аналогично классу string класс vector обеспечивает автоматическое управление выделением и освобождением памяти.
Чтобы использовать в программе объекты класса vector, необходимо в начало файла с исходным кодом добавить следующие директивы:
finclude using namespace std;
Класс vector является шаблонным классом, поэтому при объявлении его объекта необходимо указать в угловых скобках имя конкретного типа, как показано ниже:
vector vl; // vl - вектор элементов типа int
Мы, конечно, не будем рассматривать здесь все методы класса vector, а остановимся кратко только на тех, которые нужны для нашей программы.
После объявления вектора vl, приведенного выше, этот вектор пуст, то есть не содержит ни одного элемента. Добавление нового элемента в конец вектора осуществляется при помощи метода push_back:
vl.push_back(value);
Эта инструкция добавляет новый элемент типа int в конец вектора vl и присваивает ему значение переменной value.


Окт 16 2009

Обработка сообщений от колеса мыши

При нажатии на колесо мыши Windows генерирует такие же сообщения, какие вырабатываются при нажатии средней кнопки трехкнопочной мыши. Прокрутка же колесика вызывает сообщение WM_MOUSEWHEEL
Если вам нужно обрабатывать это сообщение в коде программы, то в начале файла добавьте следующую директиву:
finclude
В заголовочном файле zmouse.h определены все необходимые константы.
Младшая часть параметра wParam сообщения WM_MOUSEWHEEL интерпретируется точно так же, как для других сообщений мыши, то есть показывает состояние кнопок мыши и клавиш Shift и Ctrl.
Старшая часть параметра wParam содержит значение, отображающее дистанцию, пройденную колесом. Оно рассчитывается как количество шагов колеса при прокрутке, умноженное на коэффициент WHEEL_DELTA. В заголовочном файле zmouse.h этот коэффициент определен со значением 120.


Окт 16 2009

Обработка двойного щелчка

Два последовательных щелчка воспринимаются системой как двойной щелчок, если они происходят в течение достаточно короткого промежутка времени. Пользователь может самостоятельно задать этот интервал при помощи системного приложения Мышь (Mouse), которое можно найти в группе Панель управления.
Следует особо подчеркнуть, что окно будет получать сообщения о двойном щелчке (DBLCLK) только в том случае, если стиль соответствующего класса окна содержит флаг CS_DBLCLKS. Поэтому перед регистрацией класса окна нужно присвоить полю style структуры WNDCLASS значение, включающее этот флаг. Впрочем, если используется объект класса KWnd, то следует модифицировать стиль класса окна вызовом следующих инструкций:
style = GetClassLong(hWnd. GCL_STYLE); SetClassLong(hWnd, GCL_STYLE. style | CS_DBLCLKS):
Если класс окна определен без флага CS_DBLCLKS и пользователь делает двойной щелчок левой кнопкой мыши, то оконная процедура последовательно получает сообщения WMJ.BUTT0ND0WN, WM_LBUTTONUP, WM_LBUTT0ND0WN и WM_LBUTTONUP.
Если класс окна определен с флагом CS_DBLCLKS, то после двойного щелчка оконная процедура получит сообщения WM_LBUTT0ND0WN, WM_LBUTTONUP, WM_ LBUTTON-DBLCLK и WM_LBUTTONUP. Легко заметить, что в этом случае второе сообщение WM_LBUTT0ND0WN заменяется сообщением WM_LBUTTONDBLCLK.


Окт 16 2009

Сообщения мыши

Изучая в предыдущем разделе ввод с клавиатуры, мы видели, что Windows посылает сообщения клавиатуры только тому окну, которое имеет фокус ввода. С мышью Windows ведет себя несколько иначе. Оконная процедура получает сообщения мыши, и когда мышь проходит через окно, и при щелчке внутри окна, даже если окно не активно или не имеет фокуса ввода.
Для всех этих сообщений значение параметра IParam содержит положение мыши. При этом в младшем слове находится значение координаты х, а в старшем слове — значение координаты у. Отсчет координат ведется от левого верхнего угла клиентской области окна. Эти значения можно извлечь из IParam при помощи макросов L0W0RD и HIWORD.


Окт 16 2009

Терминология, связанная с мышью

Когда пользователь перемещает мышь, Windows передвигает по экрану небольшую растровую картинку, которая выступает в качестве курсора мыши {mouse cursor).
У курсора мыши есть горячая точка {hot spot) — пиксел, соответствующий положению курсора на экране. Когда говорят о позиции курсора мыши, то имеют в виду именно позицию горячей точки. Например, горячая точка стандартного курсора в виде стрелки (IDC_ARR0W) находится на кончике стрелки. Другой курсор, в виде перекрестья (IDC_CR0SS), имеет горячую точку в центре крестообразного шаблона.
Следует помнить, что курсор, используемый по умолчанию, для конкретного окна задается при определении структуры класса окна. Например, следующим оператором:
wc.hCursor = LoadCursor(NULL. IDC_ARR0W):
Действия пользователя с мышью принято описывать следующими терминами: Щелчок — нажатие и отпускание кнопки мыши.
Двойной щелчок — быстрое двойное нажатие и отпускание кнопки мыши.
Перетаскивание — перемещение мыши при нажатой кнопке.
Кнопки трехкнопочной мыши называются левой, средней и правой кнопками. В связанных с мышью идентификаторах, которые определены в заголовочных файлах Windows, используются аббревиатуры LBUTTON, MBUTT0N и RBUTT0N. На двухкнопочной мыши имеются только левая и правая кнопки. Единственная кнопка однокнопочной мыши является левой.


Окт 16 2009

Мышь

Мышь — это устройство ввода информации с одной или более кнопками. Win32 API поддерживает однокнопочную, двухкнопочную или трехкнопочную мышь, а также мышь с колесиком. Наличие мыши можно определить при помощи функции GetSystemMetrics:
fMouse = GetSystemMetrics (SM_MOUSEPRESENT);
Если мышь установлена, то значение fMouse будет равно TRUE. Количество кнопок у мыши определяется следующим вызовом:
cButtons = GetSystemMetrics (SM_CMOUSEBUTTONS);
Если мышь не установлена, то функция вернет нулевое значение.
Для двухкнопочной мыши, которая наиболее популярна среди пользователей, сложилась некоторая традиция интерпретации ее кнопок. Левая кнопка считается главной и выполняет самые распространенные действия, к которым относятся выбор предметов, перетаскивание значков и инициирование действий. Правая кнопка чаще всего используется для вызова контекстного меню. Оно появляется там, где находится курсор мыши, и набор его функций соответствует возможностям, доступным в этой области.
Трехкнопочная мышь не отличалась особой популярностью у пользователей Windows, пока Microsoft не выпустила модель IntelliMouse. Хотя эту мышь трудно считать интеллектуальной в прямом смысле этого слова, ее новый конструктивный элемент в виде небольшого колесика, расположенного между левой и правой кнопками, позволяет использовать множество дополнительных функций. При нажатии на колесико оно функционирует как средняя кнопка. Но кроме этого колесико можно прокручивать, и программы, поддерживающие эту функцию, будут реагировать прокруткой или масштабированием документа. Это намного удобнее, чем работать мышью на полосе прокрутки, так как колесико выполняет свои функции даже тогда, когда курсор мыши находится за пределами окна с документом.
Наличие мыши с колесиком можно определить при помощи вызова все той же функции GetSystemMetrics:
fMouseWheel = GetSystemMetrics(SM_MOUSEWHEELPRESENT);
Функция возвращает значение TRUE, если у мыши есть колесо.
Хотя мышь является важнейшим средством ввода для пользователя, разработчики программ для Windows часто дублируют, если это возможно, «мышиный» интерфейс соответствующим клавиатурным интерфейсом. При разработке новых программ весьма желательно придерживаться этой концепции, ориентированной на обеспечение максимума удобств для конечного пользователя.


Окт 16 2009

Примитивный текстовый редактор

Программа Турег, демонстрирует принципы и технику обработки аппаратных и символьных сообщений клавиатуры. Программу можно рассматривать как примитивный текстовый редактор. Пользователь может набирать в окне текст, переходить на следующую строку, нажимая клавишу Enter, перемещать каретку по уже набранному тексту при помощи клавиш управления курсором, вводить новый текст в том месте, на которое указывает каретка, или удалять предшествующий символ. Однако у этого редактора нет ни полос прокрутки, ни функций поиска и замены строк, ни возможности сохранять текст в файле.
Для хранения текста, введенного с клавиатуры, в программе используется объект класса string из стандартной библиотеки C++ вместо традиционной С-стро-ки. Благодаря автоматическому управлению выделением и освобождением памяти в этом классе мы можем писать более компактный программный код, не отвлекаясь на детали реализации.
Чтобы использовать в программе объекты класса string, необходимо в начале файла добавить следующие директивы:
#1 include
using namespace std;
Переменная (объект класса string) для хранения введенных символов объявляется следующим оператором:
static string text;
Спецификатор static необходим для сохранения текущего значения переменной text до следующего вызова оконной процедуры2.
Хотя предполагается, что читатель знаком с классом string, мы все же напомним назначение тех операций и методов класса, которые используются в коде.
Добавление символа symb в конец строки осуществляется с помощью операции +=:
text += symb:
Метод insert позволяет вставлять символ symb в произвольную позицию index:
text.insert(index, symb);
Для удаления п символов, начиная с позиции index, вызывается метод erase:
text.erase(index, n);
В любой момент можно узнать текущий размер строки при помощи метода size, например, в выражениях, подобных следующему:
nLines = text.sizeO / nCharPerLine;
И наконец, содержимое строки text можно преобразовать к типу обычной С-строки при помощи метода c_str. А это пригодится при вызове функции TextOut, поскольку она принимает в качестве четвертого параметра адрес С-строки.
Обратите внимание на обработку сообщений WM_KEYDOWN, WM_CHAR, WM_SETFOCUS, WM_KILLFOCUS, а также на использование редактором Турег системного фиксированного шрифта для вывода текста. Создание текстового редактора для пропорционального шрифта было бы более трудной задачей.


Окт 16 2009

Работа с кареткой

Каретка — это небольшой символ в виде вертикальной или горизонтальной черты. Иногда каретка отображается в виде прямоугольника, который показывает пользователю место на экране, где будет отображен очередной символ, вводимый с клавиатуры. Если вы создаете программу, реализующую функции упрощенного текстового редактора, то необходимо позаботиться и об управлении кареткой.
Для этого в Win32 API предусмотрены функции, приведенные в следующем списке:
Функция CreateCaret создает связанную с окном каретку, Функция SetCaretPos устанавливает положение каретки в окне. Функция ShowCaret показывает каретку. Функция HideCaret прячет каретку, Функция DestroyCaret удаляет каретку.
Однако вы не можете просто создать каретку при обработке сообщения WM_CREATE и удалить ее при обработке сообщения WM_DESTROY. Каретка является общесистемным ресурсом и должна разделяться между всеми работающими приложениями. Следует помнить, что появление каретки в окне имеет смысл только в том случае, когда окно имеет фокус ввода. Оконная процедура может контролировать моменты, когда окно получает или теряет фокус ввода, обрабатывая сообщения WNLSETFOCUS и WM_KILLFOCUS.
Это диктует основные правила использования каретки. Оконная процедура вызывает функцию CreateCaret при обработке сообщения WM_SETFOCUS и функцию DestroyCaret при обработке сообщения WM_KILLFOCUS.
Рассмотрим подробней использование функции CreateCaret, имеющей следующий прототип:
BOOL CreateCaret(HWND hWnd. HBITMAP hBitmap, int nWidth, int nHeight):
Первый параметр функции содержит дескриптор окна, которое становится владельцем создаваемой каретки.
Второй параметр определяет форму каретки. В нем можно указать дескриптор растра, единичное значение или значение NULL
Если параметр hBitmap равен NULL, то каретка имеет форму прямоугольного блока шириной nWidth и высотой nHeight, закрашенного черной однородной кистью.
Если параметр hBitmap равен единице, то форма каретки будет такой же, что и для значения NULL, но заполняться блок будет черно-белой кистью с шахматным рисунком, что выглядит внешне как серый цвет. При единичной ширине каретки она будет выглядеть как пунктирная линия.
Если параметру h Bitmap передается дескриптор растра, то форма каретки определяется этим растром. В этом случае параметры nWidth и nHeight игнорируются.
Параметры nWidth и nHeight задают ширину и высоту каретки в логических единицах. Их использование уже было описано выше. Если параметр nWidth имеет нулевое значение, то используется предопределенная в системе ширина рамки для окна, которая обычно равна одному пикселу. Если nHeight имеет нулевое значение, то используется предопределенная в системе высота рамки для окна, которая тоже обычно равна одному пикселу1.
В случае успешного завершения функция CreateCaret возвращает ненулевое значение. Нулевое значение служит признаком ошибки.
После создания каретки функцией CreateCaret она все еще остается скрытой. Чтобы каретка стала видимой, необходимо вызвать функцию ShowCaret. Перед уничтожением каретки с помощью функции DestroyCaret рекомендуется убрать ее с экрана функцией HideCaret.


Следующая страница »