Файл wintree.rc
Файл описания ресурсов приложения (листиг 2.3) содержит определение пиктограммы.
Листинг 2.3. Файл wintree\wintree.rc
#include <os2.h> #include "wintree.h" ICON ID_APP_FRAMEWND WINTREE.ICO
Флаги
Младшие 16 бит параметра mp1 сообщения WM_CHAR содержат флаги, отражающие состояние клавиш в момент генерации сообщения.
Для извлечения флагов из параметра mp1 удобно использовать макрокоманду SHORT1FROMMP :
nFs = SHORT1FROMMP (mp1);
Список отдельных флагов вместе с маской для проверки и кратким описанием мы привели ниже:
Флаг | Маска | Описание |
KC_CHAR | 0x0001 | Поле кода символа содержит правильное значение |
KC_VIRTUALKEY | 0x0002 | Поле виртуального кода клавиши содержит правильное значение |
KC_SCANCODE | 0x0004 | Поле аппаратного скан-кода клавиши содержит правильное значение |
KC_SHIFT | 0x0008 | Была нажата клавиша <Shift> |
KC_CTRL | 0x0010 | Была нажата клавиша <Control> |
KC_ALT | 0x0020 | Была нажата клавиша <Alt> |
KC_KEYUP | 0x0040 | Сообщение было создано, когда пользователь отпустил нажатую ранее клавишу |
KC_PREVDOWN | 0x0080 | Ранее эта клавиша находилась в нажатом состоянии (устанавливается при выполнении функции автоповтора для клавиши, которую держат в нажатом состоянии достаточно долгое время) |
KC_LONEKEY | 0x0100 | Во время ввода комбинации клавиш отпущена та клавиша, которая была перед этим нажата |
KC_DEADKEY | 0x0200 | Нажата клавиша для создания диактрических символов ("мертвая" клавиша) |
KC_COMPOSITE | 0x0400 | Композитная клавиша, составленная с использованием диактрических символов |
KC_INVALIDCOMP | 0x0800 | Неправильная композитная клавиша |
KC_TOGGLE | 0x1000 | С помощью этого флага можно использвать любую клавишу как переключающую |
KC_INVALIDCHAR | 0x2000 | Неправильный код клавиши |
KC_DBCSRSRVD1 | 0x4000 | Зарезервировано для двухсимвольных кодов клавиш |
KC_DBCSRSRVD2 | 0x8000 | Аналогично предыдущему |
Фокус ввода
В любой момент только одно из главных окон, расположенных на поверхности рабочего стола операционной системы OS/2 Warp, может быть активно. Функция активного окна получает сообщения от клавиатуры, поэтому не возникает никаких проблем с разделением клавиатурного ввода между приложениями - все сообщения от клавиатуры направляются в активное окно.
Если окно активно, для него автоматически устанавливается атрибут, который называется фокусом ввода. Следовательно, сообщения от клавиатуры будут поступать в функцию окна, владеющего фокусом ввода. Если приложение создало несколько окон, оно может передавать фокус ввода от одного окна другому.
В отличие от клавиатурных сообщений, сообщения от мыши всегда поступают в функцию того окна, над которым находится курсор мыши.
При необходимости приложение может выполнить операцию захвата (capturing) мыши. В этом случае сообщения от мыши будут всегда поступать в очередь приложения, захватившего мышь, вне зависимости от расположения курсора мыши.
Функции для просмотра дерева окон
В составе программного интерфейса Presentation Manager имеются функции, позволяющие проследить "семейные отношения" между окнами. При помощи этих функций зная идентификатор дочернего окна, вы можете определить идентификатор родительского окна и наоборот, зная идентификатор родительского окна, определить идентификаторы всех его дочерних окон.
Например, функция WinQueryWindow позволяет определить идентификатор родительского окна, если идентификатор дочернего окна передается ей в качестве первого параметра, а константа QW_PARENT - в качестве второго.
С помощью функций WinBeginEnumWindows и WinGetNextWindow можно определить идентификаторы всех дочерних окон любого родительского окна.
Более подробно мы рассмотрим функции, предназначенные для просмотра дерева окон позже, когда в этом возникнет необходимость.
Функции для работы с мышью
В программном интерфейсе Presentation Manager есть несколько функций, предназначенных для работы с мышью. В этом разделе мы опишем некоторые из них, наиболее полезные на наш взгляд.
Функции окон
Функции окон, определенные в нашем приложении, похожи друг на друга и на функцию окна из предыдущего приложения. Их основная работа заключается в том, чтобы выводить на экран сообщение, когда пользователь делает щелчок левой клавишей мыши в области окна. В сообщении указывается название окна и координаты курсора мыши (в системе координат, связанной с этим окном).
Функция DosGetDateTime
Для определения текущего времени и даты удобнее всего использовать функцию DosGetDateTime :
APIRET DosGetDateTime(PDATETIME PDateTime);
Эта функция записывает в стркутуру типа DATETIME информацию о текущем времени и дате. Формат этой структуры приведен ниже:
typedef struct _DATETIME { UCHAR hours; // часы UCHAR minutes; // минуты UCHAR seconds; // секунды UCHAR hundredths; // сотые доли секунды UCHAR day; // число UCHAR month; // месяц USHORT year; // год SHORT timezone; // временной пояс UCHAR weekday; // день недели } DATETIME;
Функция DrawMousePtr
Функция DrawMousePtr, определенная в нашем приложении, получает с помощью функции WinQuerySysPointer идентификатор встроенного курсора мыши или встроенной пиктограммы, а затем рисует соответствующее изображение при помощи функции WinDrawPointer. Подробнее функции рисования мы рассмотрим позже в одной из следующих книг "Библиотеки системного программиста" в главе, посвященной графическому интерфейсу Presentation Manager.
Функция main
Функция main , получающая управление при запуске приложения, создает главное окно и организует цикл обработки сообщений.
В переменной hmq хранится идентификатор очереди сообщений, созданной функцией WinCreateMsgQueue . При выборке сообщение записвается для временного хранения в структуру qmsg.
Переменная flFrameFlags хранит набор флагов, которые используются при создании окна Frame Window :
ULONG flFrameFlags = FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON;
Мы создаем окно, которое имеет меню, заголовок, кнопки минимизации и максимизации, рамку для изменения размеров окна и пиктограмму. Начальные размеры и расположение окна выбираются оболочкой Workplace Shell.
В строке szWndClass записано имя класса, которое используется для окна Client Window .
Первое, что делает функция main после запуска приложения - это инициализация при помощи функции WinInitialize . Данная функция должна вернуть идентификатор блока Anchor-block при нормальном завершении и значение NULLHANDLE в случае возникновения ошибки.
Как обработать ошибку?
Можно, конечно, просто завершить работу приложения, однако в этом случае мы так и не узнаем, что же произошло. Гораздо лучше было бы вывести на экран сообщение об ошибке, однако в приложении Presentation Manager мы не можем воспользоваться для этого такими функциями, как puts или printf .
Нас выручит тот факт, что в программном интерфейсе Presentation Manager имеется простейшее средство отображения сообщений в диалоговых панелях - функция WinMessageBox . Вот как мы использовали эту функцию для вывода сообщения при возникновении ошибки инициализации:
if(hab == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка инициализации", "Ошибка", 0, MB_ICONHAND | MB_OK); return(-1); }
Прототип функции WinMessageBox приведен ниже:
ULONG WinMessageBox ( HWND hwndParent, // идентификатор родительского окна HWND hwndOwner, // идентификатор окна владельца PSZ pszText, // текст сообщения PSZ pszCaption, // заголовок диалоговой панели ULONG idWindow, // идентификатор окна // диалоговой панели ULONG flStyle); // стиль диалоговой // панели с сообщением
В функции main определены флаги, которые используются для создания окон верхнего уровня (переменная flFrameFlags) и для окна дочернего уровня (переменная flFrameChildFlags).
Обратите внимание, что для дочернего окна мы не используем флаги FCF_ICON , FCF_SHELLPOSITION и FCF_TASKLIST . Для установки пиктограммы, отображемой в верхнем левом углу дочернего окна, мы посылаем этому окну сообщение WM_SETICON . Размеры и расположение дочернего окна будут установлены явням образом при помощи функции WinSetWindowPos .
Каждое из трех окон нашего приложения имеет свою функцию окна и потому создается на базе своего класса окна. Имена этих классов хранятся в переменных szWndClass1, szWndClass2 и szWndClassChild.
Свою работу функция main начинает, как обычно, с вызова функции инициализации WinInitialize . Далее при помощи функции WinCreateMsgQueue создается очередь сообщений.
После этого для регистрации классов окна три раза вызывается функция WinRegisterClass . При этом определяется, что для окон, создаваемых на базе класса szWndClass1, будет использоваться функция окна WndProc1, для окон, создаваемых на базе класса szWndClass2 - функция окна WndProc2, а для окон, создаваемых на базе класса szWndClassChild - функция окна WndProcChild.
Как и раньше, после вызова каждой функции проверяется код возврата. Если при выполнении функции произошла ошибка, приложение выводит на экран соответствующее сообщение и завершает свою работу.
Если регистрация классов завершилась успешно, функция main создает два окна верхнего уровня, аналогично тому, как создавалось главное окно предыдущего приложения. Каждое из этих окон основано на базе своего класса окна и имеет собственную функцию окна, однако для обоих окон мы указали один и тот же идентификатор ресурсов. В результате эти окна будут иметь одинаковую пиктограмму системного меню.
На следующем этапе наше приложение создает окно, которое является дочерним по отношению к второму окну верхнего уровня, имеющему идентификатор hWndFrame2. Для этого используется знакомая вам функция WinCreateStdWindow :
Функция main в этом приложении не имеет никаких особенностей. Так как первоначально меню верхнего уровня загружается из ресурсов приложения, при создании главного окна мы указали флаг FCF_MENU .
Функция main не имеет никаких особенностей. Она создает главное окно приложения и очередь сообщений, а затем запускает цикл обработки сообщений.
В функции main нет ничего особенного за исключением того, что при создании главного окна приложения используется сокращенный набор флагов:
ULONG flFrameFlags = FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON;
В результате окно Frame Window не создает окна заголовка, системное меню и кнопки минимизации/максимизации. Без дополнительной обработки сообщений мыши пользователь не сможет перемещать такое окно. Размер окна можно изменять, так как указан флаг FCF_SIZEBORDER.
Функция main имеет одну особенность - после создания главного окна приложения и перед запуском цикла обработки сообщений главному окну посылается сообщение WM_SETICON :
WinSendMsg (hWndFrame, WM_SETICON , (MPARAM)WinQuerySysPointer (HWND_DESKTOP, SPTR_ICONINFORMATION, FALSE), NULL);
Через первый параметр вместе с этим сообщением передается идентификатор встроенной пиктограммы SPTR_ICONINFORMATION , полученный от функции WinQuerySysPointer . Второй параметр сообщения WM_SETICON равен нулю.
После получения этого сообщения пиктограмма приложения, которая отображается в окне системного меню и которая была загружена из ресурсов приложения, заменяется на новую.
В функции main приложения CLOCK для создания главного окна приложения используется самый маленький набор флагов:
ULONG flFrameFlags = FCF_TASKLIST | FCF_ICON;
Главное окно приложения не имеет рамки, поэтому пользователь не может изменять его размеры. У окна нет заголовка, системного меню и меню минимизации/максимизации, поэтому для перемещения окна используется та же методика, что и в описанном ранее приложении MOUSEMOVE. Эта методика основана на обработке сообщения WM_MOUSEMOVE .
Сразу после создания главного окна наше приложение устанавливает его размер. Для этого функция main получает пространство отображения, выбирает в него шрифт с фиксированной шириной символов и определяет метрики этого шрифта. Ширина главного окна приложения выбирается в 10 раз больше, чем ширина символов, а высота - равной высоте символов с учетом размера выступающей части:
WinSetWindowPos (hWndFrame, HWND_TOP , 0, 0, 10 * cxChar, cyChar + cyDesc, SWP _SIZE | SWP_MOVE | SWP_ZORDER );
Кроме изменения размеров онка, функция WinSetWindowPos изменяет расположение окна по оси Z, выдвигая его на передний план.
Далее функция main обычным образом запускает цикл обработки сообщений.
Функция main не имеет особенностей. Она создает главное окно приложений, очередь сообщений и запускает цикл обработки сообщений.
Функция main не имеет никаких особенностей. Она создает главное окно приложения, очередь сообщений и затем запускает цикл обработки сообщений.
Функция окна
Другим базовым понятием, с которым вы неизбежно столкнетесь при создании приложений для Presentation Manager, является так называемая функция окна .
Чтобы вам было понятно назначение функции окна, проведем аналогию окон, создаваемых системой Presentation Manager, с объектами в языке программирования C++. Там объект определяется как структура данных и набор методов, предназначенных для обработки этих данных. В качестве методов выступают функции. Среди этих методов существуют два особых - конструктор и деструктор. Эти методы выполняют, соответственно, создание и уничтожение объекта.
Окно, созданное с помощью средств Presentation Manager, можно также рассматривать как объект, состоящий из некоторой структуры данных и методов, реализованных с помощью функции окна. При этом функция окна полностью определяет свойства окна.
Создавая окно, вы должны в вашем приложении создать для него функцию окна. В этой функции вы определяете реакцию окна на различные внешние воздействия, такие, например, как инициализация приложения и завершение его работы, перерисовка внутренней области окна, щелчок клавишей мыши и т. д.
Как вы увидите позже, отдельные фрагменты функции окна выполняют работу конструктора и деструктора, а также других методов, определяющих поведение окна.
Самое интересное заключается в том, что после определения функции окна приложение... никогда не вызывает эту функцию!
Зачем, спросите вы, приложение создает функцию, которую оно не вызывает?
Дело в том, что функция окна вызывается системой Presentation Manager, когда над окном выполняются какие-либо действия. После вызова функция окна анализирует параметры, полученные ей от Presentation Manager, и выполняет соответствующий фрагмент, определяющий реакцию окна на данное действие.
Если вы знакомы со структурой драйверов MS-DOS , то вам будет понятна аналогия между функцией окна и функцией прерывания драйвера. Драйвер создает функцию прерывания, которая обрабатывает посылаемые ему команды, однако сам драйвер никогда напрямую не вызывает эту функцию.
Таким образом, для каждого окна приложение должно определить функцию окна и каким-то образом (каким - вы узнаете позже) указать системе Presentation Manager, что для данного окна будет использована именно эта функция. После создания окна система Presentation Manager будет при необходимости вызывать функцию окна.
Параметры этой функции полностью аналогичны параметрам функции окна.
При создании окна ему передается сообщение WM_CREATE . Обработчик этого сообщения может проверить параметры, переданные функции создания окна, или выполнить какие-либо инициализирующие действия, например, дианмическое получение блока памяти или инициализацию данных. Если такие действия не нужны, функция окна может не обрабатывать это сообщение, передавая его функции WinDefWindowProc .
Аналогично, в процессе уничтожения окна функции окна передается сообщение WM_DESTROY . Обработчик этого сообщения должен освободить ресурсы, заказанные у операционной системы обработчиком сообщения WM_CREATE .
Сравнивая окно с объектом языка программирования С++, можно сказать, что обработчик сообщения WM_CREATE выполняет функцию конструктора, а обработчик сообщения WM_DESTROY - функцию деструктора объекта.
Функция окна может получать сообщения с десятками различных кодов, поступающих от мыши, клавиатуры, таймера, различных органов управления приложения, дочерних окон, операционной системы и других приложений. Мы будем рассказывать вам об этих сообщениях по мере необходимости.
Функция окна приложения WndProc
Наше приложение рисует текстовую строку во время обработки сообщения WM_PAINT , а также во время обработки сообщения WM_BUTTON1DOWN . Кроме того, функция окна обрабатывает сообщение WM_SIZE , поступающее в функцию окна при изменении его размеров.
Функция окна WndProc
Функция главного окна приложения WndProc обрабатывает два сообщения, поступающие от меню. Это сообщения WM_INITMENU и WM_COMMAND .
Изменение системного меню и создание нового временного меню Edit выполняется обработчиком сообщения WM_CREATE . При этом используется методика, описанная нами ранее.
Обработка остальных сообщений выполняется обычным образом, поэтому мы не будем на этом останавливаться.
Функция окна обрабатывает сообщения WM_CREATE , WM_DESTROY , WM_BUTTON1DOWN , WM_BUTTON1UP , WM_MOUSEMOVE , WM_PAINT , WM_ERASEBACKGROUND , WM_SIZE и WM_BUTTON1DBLCLK .
Рассмотрим обработчики сообщений, расположенные в функции главного окна приложения WndProc.
Для экономии места мы опишем только обработчики тех сообщений, которые имеют непосредственное отношение к таймеру. Способы перемещения окна мышью и процедура выбора шрифта в пространство отображения были описаны раньше.
Рассмотрим обработку сообщений, поступающих в функцию окна приложения BUTTON.
Рассмотрим обработку сообщений функцией окна WndProc.
Функция PrintLong
Функция PrintLong аналогична только что описанной функции PrintString, и отличается от последней только тем, что используется для отображения значения типа LONG.
Функция PrintString
Функция PrintString, определенная в нашем приложении, используется для ввода в окно приложения текстовых параметров метрик шрифта.
Вначале эта функция подготавливает в буфере szBuf название поля шрифта, а переменной i - размер соответствующей строки символов. Затем в структуру ptl типа POINTL записываются начальные координаты вывода строки. Рисование строки выполняется функцией GpiCharStringAt :
ptl.x = cxCurrentPosition; ptl.y = cyCurrentPosition - cyChar * (1 - nYScrollPos); GpiCharStringAt(hps, &ptl, i, szBuf);
Заметьте, что текущая координата по оси Y зависит от положения движка вертикальной полосы просмотра.
Далее аналогичным образом выполняется печать значения поля.
Перед возвращением управления функция уменьшает текущую позицию вывода по оси Y на высоту символа, чтобы следующая строка была нарисована ниже:
cyCurrentPosition -= cyChar;
Функция ResetFont
Функция ResetFont восстанавливает шрифт после функции SetCourierFont. Для этого она с помощью функции GpiSetCharSet выбирает в пространство отображения шрифт по умолчанию и затем удаляет логический шрифт, созданный в функции SetCourierFont.
Функция SetCourierFont
Эта функция выбирает в пространство отображения шрифт с фиксированной шириной символов. Пока мы не будем рассматривать функцию SetCourierFont подробно. Скажем только, что для выбора шрифта его описание в виде набора параметров записывается в поля структуры fat типа FATTRS . Адрес этой структуры затем передается функции GpiCreateLogFont, создающей логический шрифт с идентификатором 1L.
Далее созданный шрифт выбирается в пространство отображения при помощи функции GpiSetCharSet .
Функция WinCreateWindow
Приведем прототип функции WinCreateWindow , предназначенной для создания окон (и, в частности, окон органов управления):
HWND WinCreateWindow ( HWND hwndParent, // родительское окно PSZ pszClass, // имя класса PSZ pszName, // заголовок окна ULONG flStyle, // стиль класса окна LONG x, // координата по оси X LONG y, // координата по оси Y LONG cx, // ширина LONG cy, // высота HWND hwndOwner, // окно-владелец HWND hwndInsertBehind, // окно-брат, за которым // отображается окно ULONG id, // идентификатор окна PVOID pCtlData, // управляющие данные PVOID pPresParams); // параметры отображения
В качестве параметра hwndParent вы должны указать идентификатор дочернего окна.
Параметр pszClass определяет имя класса, на базе которого создается окно. Для кнопки здесь необходимо указать имя WC_BUTTON .
Параметр pszName задает текст для окна. Если создается кнопка, то это тот самый текст, который будет написан на кнопке.
С помощью параметра flStyle можно указать стиль класса окна, определяющий поведение и внешний вид окон, создаваемых на базе этого класса. Этот параметр мы опишем немного позже.
Расположение окна и его размеры определяется параметрами x (координата по оси X), y (координата по оси Y), cx (ширина), cy (высота).
Параметр hwndOwner определяет идентификатор окна-владельца, которое будет получать извещающие сообщения от создаваемого дочернего окна. Если вы создаете кнопку, то сообщения, возникающие при ее нажатии, будут передаваться именно в это окно.
Через параметр hwndInsertBehind передается идентификатор братского (или сестринского, что одно и то же) окна, за которым будет нарисовано создаваемое окно. Вы можете также указать константы HWND_TOP или HWND_BOTTOM . В первом случае создаваемое окно окажется наверху, во втором - за всеми остальными братскими окнами.
Идентификатор, передаваемый через параметр id, играет важную роль при обработке сообщений, поступающих в окно, владеющее создаваемым дочерним окном. Анализируя этот идентификатор, функция окна может определить, от какого окна поступило сообщение. Поэтому если окно создает несколько дочерних окон, каждое из них должно иметь свой идентификатор.
При помощи параметра pCtlData (указатель) вы можете передать данные функции создаваемого дочернего окна. Этот указатель будет передан в параметре mp1 сообщения WM_CREATE , поступающего в функцию дочернего окна при его создании.
Аналогично, данные, передаваемые через параметр pPresParams, также попадают в функцию дочернего окна через параметр mp2 сообщения WM_CREATE . Для того чтобы понять, как использовать параметр pPresParams, опишем подробнее параметры сообщения WM_CREATE.
Функция WinGetCurrentTime
Функция WinGetCurrentTime возвращает текущее время, измеренное в миллисекундах от момента загрузки операционной системы IBM OS/2. Учтите, что если компьютер работает много недель, это значение может переполняться и сбрасываться в ноль.
Прототип функции WinGetCurrentTime приведен ниже:
ULONG WinGetCurrentTime(HAB hab);
Этой функцией удобно пользоваться, например, для измерения продолжительности какого-либо процесса. Для этого нужно вызвать функцию до начала процесса и после, а затем сравнить возвращенные значения. Что же касается определения абсолютного текущего времени и даты, для этого удобнее использовать функцию DosGetDateTime , которую мы опишем ниже.
Функция WinQueryMsgPos
Так же как и функция WinQueryPointerPos , функция WinQueryMsgPos возвращает экранные координаты курсора мыши. Однако в отличие от функции WinQueryPointerPos, координаты курсора определяются не в момент извлечения сообщения из очереди, а в момент записи последнего сообщения в очередь приложения.
Прототип функции WinQueryMsgPos приведен ниже:
BOOL WinQueryMsgPos ( HAB hab, // идентификатор блока Anchor-block PPOINTL pptl); // адрес структуры типа POINTL
Функция WinQueryMsgTime
Эта функция возвращает время в миллисекундах для последнего сообщения, извлеченного из очереди сообщений приложения. Возвращаемое значение времени отсчитывается от момента загрузки операционной системы.
Прототип функции WinQueryMsgTime представлен ниже:
ULONG WinQueryMsgTime(HAB hab);
Вы можете использовать эту функцию для измерения времени, прошедшего между обработкой двух сообщений.
Функция WinQueryPointerPos
С помощью функции WinQueryPointerPos вы можете узнать положение курсора на момент выборки последнего сообщения из очереди приложения. Прототип функции приведен ниже:
BOOL WinQueryPointerPos ( HWND hwndDeskTop, // идентификатор окна Desktop Window PPOINTL pptlPoint); // адрес структуры типа POINTL
Положение курсора записывается в структуру типа POINTL, адрес которой передается функции через параметр pptlPoint. В случае успешного завершения функция возвращает значение TRUE, при ошибке - FALSE.
Заметим, что при определении координат курсора мыши используется система координат, связанная с окном рабочего стола. Таким образом, с помощью этой функции вы можете определить не окнонные, а экранные координаты курсора мыши.
Функция WndProc
Эта функция выполняет всю полезную работу в нашем приложении. Рассмотрим обработчики отдельных сообщений.
Функция WndProc выполняет всю работу с таймером. Рассмотрим по отдельности обработчики различных сообщений.
Рассмотрим обработку сообщений, выполняемую функцией WndProc.
Эта функция выполняет обработку сообщений, поступающих в главное окно приложения. Наибольший интерес для нас представляют сообщения WM_CREATE, обработчик которого создает круглые регуляторы, и WM_CONTROL, посредством которого в окно приложения поступают извещения от этих регуляторов.
Глобальные переменные
В области глобальных переменных определены идентификаторы окон FrameWindow и ClientWindow (соответственно, переменные hWndFrame и hWndClient), заголовок приложения szAppTitle.
Кроме этого, определен идентификатор hwndPopupMenu. В него будет записан идентификатор плавающего меню, которое появляется после щелчка правой клавишей мыши в окне приложения.
Среди глобальных переменных, обычных для наших приложений, имеются переменные hwndEditMenu и hwndMenu. В первой из них хранится идентификатор динамически создаваемого временного меню Edit, вторая предназначена для хранения идентификатора окна меню верхнего уровня.
В глобальных переменных cxClient и cyClient хранятся размеры окна Client Window (соответственно, ширина и высота). Эти размеры определяются в момент обработки сообщения WM_SIZE и используются при сдвиге (свертке) содержимого окна.
В переменные cxChar, cyChar и cyDesc записываются размеры символов для выбранного нами шрифта с фиксированной шириной символов. Подробно о метриках шрифта вы узнаете позже в одной из следующих книг "Библиотеки системного программиста", а пока можете считать, что в переменных cxChar и cyChar хранятся, соответственно, ширина и высота символов, а в переменной cyDesc - размер выступающей части символов (например, размер хвостика у буквы 'у').
В массиве szTitle записана текстовая строка, которая используется для заголовка таблицы и отображается в нижней части главного окна приложения.
В переменные cxPoint и cyPoint записываются координаты курсора мыши в момент, когда пользователь начинает перемещение окна приложения, нажав левую кнопку мыши. Эти координаты будут затем сравниваться с координатами курсора мыши после завершения процесса перемещения, которые хранятся в переменных cxNewPoint и cyNewPoint.
Переменная fDrag используется в качестве признака перемещения окна и проверяется при обработке сообщения WM_MOUSEMOVE . Вначале в нее записывается значение FALSE. Когда пользователь начинает перемещать окно, это значение изменяется на TRUE. После завершения процесса перемещения в переменную снова записывается значение FALSE.
В области глобальных переменных мы также определили переменные hptr и hptr1 типа HPOINTER , в которых хранятся идентификаторы курсоров мыши, загруженных из ресурсов прложения. В первой из этих переменных хранится идентификатор курсора в виде открытой ладони, во второй - в виде закрытой ладони.
В области глобальных переменных определены переменные cxPointer и cyPointer, в которых хранятся размеры пиктограммы курсора мыши, полученные на этапе инициализации приложения.
Размеры окна приложения записываются в переменные cxClient и cyClient обработчиком сообщения WM_SIZE при создании окна и при изменении пользователем его размеров.
Помимо идентификаторов hab, hWndFrame и hWndClient, обычных для всех наших приложений, в области глобальных переменных хранится заголовок окна приложения (который не отображается, так как окно не имеет заголовка). Переменные cxPoint, cyPoint, cxNewPoint, cyNewPoint, hptr, hptr1 и fDrag используются для перемещения окна мышью таким же образом, что и в рассмотренном нами ранее приложении MOUSEMOVE.
Переменные cxChar, cyChar и cyDesc хранят метрики шрифта с фиксированной шириной символов, который используется для отображения времени.
В области глобальных переменных определены переменные hWndButton1 и hWndButton2, в которых хранятся идентификаторы двух созданных приложением кнопок.
В области глобальных переменных определены переменные hWndButton1, hWndButton2 и hWndButton3, предназначенные, соответственно, для хранения идентификаторов кнопки и двух переключателей.
Состояние переключателей записывается в переменные fButton2Checked и fButton3Checked.
В глобальных переменных hWndCirc1, hWndCirc2 и hWndCirc3 хранятся идентификаторы регуляторов цвета, создаваемых при обработке сообщения WM_CREATE. Текущие значения отдельных цветовых компонент записываются в переменные sColorR (красный цвет), sColorG (зеленый цвет) и sColorB (синий цвет).
Иерархия окон и родственные связи
В предыдущей главе мы привели исходные тексты простейшего приложения Presentation Manager, создающего единственное окно. При этом мы сделали оговорку, что на самом деле при этом создается много окон и среди них, в частности, имеется окно Client Window , которое можно использовать для отображения какой-либо информации.
Теперь мы рассмотрим иерархию и взаимосвязь окон Presentation Manager более детально.
Инициализация полосы просмотра
Для полосы просмотра определены понятия текущая позиция и диапазон изменения значений позиции. При передвижении движка вдоль полосы просмотра текущая позиция принимает дискретные значения внутри диапазона изменения значений позиции. Если движок находится в самом левом (для горизонтальной полосы просмотра) или самом верхнем (для вертикальной полосы просмотра) положении, текущая позиция равна минимальной. Если же движок находится в самом правом или самом нижнем положении, текущая позиция равна максимальной.
После того как вы создали полосу просмотра одним из описанных выше способов, ее необходимо проинициализировать, указав диапазон изменений значений позиции. И то, и другое можно сделать, передав окну полосы просмотра сообщение SBM_SETSCROLLBAR , например, так:
WinSendMsg(hwndYScroll, SBM_SETSCROLLBAR, (MPARAM)0, MPFROM2SHORT(0, YSIZE)); WinSendMsg(hwndXScroll, SBM_SETSCROLLBAR, (MPARAM)0, MPFROM2SHORT(0, XSIZE));
Здесь мы устанавливаем текущую позицию и диапазон изменения значений для вертикальной и горизонтальной полосы просмотра с идентификаторами окон hwndYScroll и hwndXScroll, соответственно.
Прежде чем мы подробно опишем параметры сообщения SBM_SETSCROLLBAR , сделаем одно замечание относительно идентификатора окна полосы просмотра, которое должно использоваться при передаче сообщений этому органу управления.
Если полоса просмотра создавалась функцией WinCreateWindow, то для передачи сообщений полосе вы должны использовать идентификатор окна, полученный от этой функции. Если же полоса просмотра создавалась при помощи флагов FCF_VERTSCROLL или FCF_HORZSCROLL , для получения идентификатора этой полосы вам необходимо воспользоваться функциями WinWindowFromID и WinQueryWindow, как это показано ниже:
hwndYScroll = WinWindowFromID( WinQueryWindow(hWnd, QW_PARENT), FID_VERTSCROLL), hwndXScroll = WinWindowFromID( WinQueryWindow(hWnd, QW_PARENT), FID_HORZSCROLL),
Полосы просмотра, создаваемые как дочерние для окна Frame Window, имеют идентификаторы FID_VERTSCROLL (вертикальная полоса просмотра) и FID_HORZSCROLL (горизонтальная полоса просмотра).
Для получения идентификатора окна этих полос вы должны передать указанные значения в качестве второго параметра функции WinWindowFromID . Что же касается первого параметра этой функции, то через него следует передать идентификатор родительского окна.
Если идентификаторы окон полос просмотра определяются в функции главного окна приложения, идентификатор родительского окна можно получить при помощи функции WinQueryWindow . Для этого ей через первый параметр необходимо передать идентификатор окна hWnd, передаваемый в функцию главного окна приложения, а через второй параметр - константу QW_PARENT .
Итак, опишем параметры сообщения SBM_SETSCROLLBAR , предназначенного для установки позиции и диапазона изменений значений позиции.
Параметр mp1 задает новое значение позиции движка. Это значение должно лежать в пределах от минимального, определяемого младшим словом параметра mp2, и максимального, которое задается старшим словом этого же параметра.
В приведенном выше примере мы устанавливали для вертикальной полосы просмотра диапазон изменения значений от 0 до YSIZE, причем начальное значение было равно нулю. Это соответствует расположению движка в верхней части полосы просмотра.
Инициализация приложения
Прежде чем приложение сможет использовать функции программного интерфейса Presentation Manager, оно должно зарегистрировать себя в системе Presentation Manager при помощи функции WinInitialize . Перед завершением своей работы необходимо вызвать функцию WinTerminate , как это показано ниже:
#define INCL_WIN #include <os2.h> int main () { // Идентификатор Anchor-block HAB hab; hab = WinInitialize (0); . . . // Строки исходного текста приложения . . . WinTerminate (hab); return(0); }
Функция WinInitialize имеет единственный параметр, который всегда должен иметь значение, равное нулю.
В случае успешной регистрации функция WinInitialize возвращает идентификатор задачи, который называется Anchor-block handle, а при ошибке - значение NULLHANDLE . Идентификатор Anchor-block имеет тип HAB , который, также как и сама функция, определен в include-файле os2.h.
Как заметил Петцольд в своей книге Programming the OS/2 Presentation Manager, "морской" термин Anchor-block (anchor - якорь) родился в мире больших компьютеров и в контексте операционной системы IBM OS/2 ничего не означает.
Файл os2.h имеет небольшие размеры и содержит команды условного включения других include-файлов, поставляемых в составе систем разработки приложений. Определение INCL_WIN требуется для подключения одного из таких файлов, необходимого для работы с функциями оконого интерфейса Presentation Manager.
Заметьте, что если приложение создает несколько задач, то каждая задача, которая будет вызывать функции программного интерфейса Presentation Manager, должна зарегистрировать себя с помощью функции WinInitialize . В наших первых примерах создается только одна задача, поэтому функция WinInitialize вызывается только один раз в функции main .
В паре с функцией WinInitialize вы должны использовать функцию WinTerminate , назначение которой заключается в освобожении ресурсов, полученных функцией WinInitialize при регистрации задачи в системе Presentation Manager. Забегая вперед, скажем, что перед вызовом функции WinTerminate вы должны уничтожить очередь сообщений, созданную на этапе инициализации приложения, уничтожить все созданные окна и освободить другие ресурсы, полученные у системы Presentation Manager.
При успешном завершении функция WinTerminate возвращает значение TRUE, а при ошибке - FALSE.
Использование класса WC_SCROLLBAR
Для создания полосы просмотра с помощью функции WinCreateWindow вы должны в первом параметре функции указать класс окна WC_SCROLLBAR:
#define IDC_SCROLLBAR 100 HWND hWndScroll; hWndScroll = WinCreateWindow (hWnd, WC_BUTTON , NULL, // текст указывать не нужно WS_VISIBLE | SBS_VERT, 0, 0, 0, 0, hWnd, HWND_TOP , IDC_SCROLLBAR, NULL, NULL);
Заголовок окна не используется, поэтому третий параметр функции WinCreateWindow должен быть указан как NULL.
Четвертый параметр, определяющий стиль окна, наряду с константой WS_VISIBLE должен содержать определение стиля полосы просмотра. Существует четыре стиля для полосы просмотра. Соответствующие символические константы имеют префикс имени SBS_ (например, SBS_HORZ ).
Одиннадцатый параметр функции WinCreateWindow должен задавать идентификатор полосы просмотра.
Изменение размеров и расположения окна
С помощью функции WinSetWindowPos приложение может изменить расположение или размеры созданного ранее окна. Прототип этой функции приведен ниже:
BOOL WinSetWindowPos ( HWND hwnd, // идентификатор окна HWND hwndInsertBehind,// относительный // порядок расположения LONG x, // координата по оси X LONG y, // координата по оси Y LONG cx, // ширина окна LONG cy, // высота окна ULONG fl); // индикатор изменения позиции
Через параметр hwnd функции передается идентификатор окна, размеры или расположение которого будут изменяться.
Параметр hwndInsertBehind задает новое расположение окна по оси Z (если в параметре fl, описанном ниже, указан флаг SWP _ZORDER ).
Сделаем пояснение относительно оси Z .
Ось Z направлена перпендикулярно к плоскости экрана в направлении от экрана к глазам пользователя.
Если на экране отображается несколько перекрывающих друг друга окон, можно считать, что все эти окна имеют разные координаты вдоль оси Z. Так как экран плоский, пользователь видит не объемное изображение, а плоскую проекцию этих окон на поверхность экрана. При этом некоторые окна могут полностью или частично закрывать другие.
С помощью функции WinSetWindowPos приложение может выдвигать некоторые окна на передний план или наоборот, отодвигать на задний. Для этого в качестве значения для параметра hwndInsertBehind следует указывать константы HWND_TOP и HWND_BOTTOM , соответственно.
Что касается параметров x и y, то они задают новые значения для координат дочернего окна по соответствующим координатным осям, связанным с родительским окном (если в параметре fl указан флаг SWP _MOVE ). По умолчанию начало системы координат находится в левом нижнем углу окна, ось X направлена слева направо, а ось Y - снизу вверх. В качестве единицы измерения, опять же по умолчанию, используется пиксел - минимальный элемент изображения при выбранном видеорежиме.
Параметры cx и cy задают, соответственно, новые значения для ширины и высоты окна в пикселах (если в параметре fl указан флаг SWP _SIZE ).
Параметр fl указывается как набор флагов, объединенных при помощи логической операции ИЛИ.
Он определяет, какие операции выполняет функция WinSetWindowPos . Ниже мы привели возможные значения флагов с кратким описанием.
Флаг | Описание |
SWP _SIZE | Изменение размеров окна |
SWP _MOVE | Изменение расположения окна по осям X и Y |
SWP _ZORDER | Изменение расположения окна по оси Z |
SWP _SHOW | Отображение окна |
SWP _HIDE | Скрытие окна |
SWP _NOREDRAW | Если указан этот флаг, не выполняется перерисовка изменившихся областей окна |
SWP _NOADJUST | Если указан этот флаг, перед перемещением окна или перед изменением его размеров в функцию окна не передается сообщение WM_ADJUSTWINDOWPOS |
SWP _ACTIVATE | Активизация окна (действует только для окна Frame Window ). Если не указан флаг SWP _ZORDER и параметр hwndInsertBehind не равен HWND_BOTTOM , активизированное окно "всплывает" наверх по оси Z |
SWP _DEACTIVATE | Блокирование окна (действует только для окна Frame Window ). Если не указан флаг SWP _ZORDER и параметр hwndInsertBehind не равен HWND_TOP , активизированное окно отодвигается на задний план по оси Z |
SWP _MINIMIZE | Минимизация окна. Этот флаг несовместим с флагами SWP _MAXIMIZE и SWP_RESTORE |
SWP _MAXIMIZE | Максимизация окна. Этот флаг несовместим с флагами SWP _MINIMIZE и SWP_RESTORE |
SWP _RESTORE | Восстановление размеров окна. Этот флаг несовместим с флагами SWP _MINIMIZE и SWP_MAXIMIZE |
Заметим, что если не указан флаг SWP _NOADJUST, в функцию окна перед выполнением перемещения или изменения размеров передается сообщение WM_ADJUSTWINDOWPOS . Первый параметр этого сообщения содержит указатель на структуру типа SWP, показанную ниже:
typedef struct _SWP { ULONG fl; LONG cy; LONG cx; LONG y; LONG x; HWND hwndInsertBehind; HWND hwnd; ULONG ulReserved1; // зарезервировано ULONG ulReserved2; // зарезервировано } SWP ; typedef SWP *PSWP;
Обработчик сообщения может изменить поля x, y, cx, cy или hwndInsertBehind, внеся коррективы в изменения размеров или расположения окна.
Изменение системного меню
В некоторых случаях приложения изменяют системное меню, доступное пользователю через пиктограмму, расположенную в верхнем левом углу главного окна приложения. Создавая простейшее приложение, вы можете организовать интерфейс с пользователем при помощи нескольких строк, добавленных к системному меню.
Процедура добавления строк к системному меню достаточно проста, хотя и состоит из нескольких шагов.
Прежде всего вы должны определить идентификатор окна системного меню. Это можно сделать при помощи функций WinWindowFromID и WinQueryWindow в обработчике сообщения WM_CREATE , например, так:
HWND hwndSystemMenu; hwndSystemMenu = WinWindowFromID ( WinQueryWindow (hWnd, QW_PARENT ), FID_SYSMENU );
В качестве первого параметра функции WinQueryWindow мы передали идентификатор окна Client Window , который передается в функцию окна. Второй параметр, равный константе QW_PARENT , сообщает функции WinQueryWindow о необходимости вернуть идентификатор окна, которое является родительским по отношению к окну hWnd. При этом мы получим идентификатор окна Frame Window , которое, очевидно, является родительским для окна системного меню.
Приведем прототип для функции WinQueryWindow :
HWND WinQueryWindow ( HWND hwnd, // идентификатор окна LONG lCode); // тип информации об окне
В зависимости от значения параметра lCode функция возвращает различную информацию об окне. Вы можете использовать для параметра lCode одну из следующих констант:
Константа | Информация, возвращаемая функцией |
QW_BOTTOM | Дочернее окно самого нижнего уровня |
QW_FRAMEOWNER | Возвращается идентификатор окна-владельца для окна hwnd. Это окно имеет такое же родительское окно, что и окно hwnd |
QW_NEXT | Идентификатор окна, расположенного под окном, заданным параметром hwnd |
QW_NEXTTOP | Идентификатор следующего окна в иерархии окна-владельца |
QW_OWNER | Идентификатор окна-владельца |
QW_PARENT | Идентификатор родительского окна |
QW_PREV | Аналогично, но для окна, расположенного над заданным |
QW_PREVTOP | Идентификатор предыдущего окна в иерархии окна-владельца |
QW_TOP | Дочернее окно самого верхнего уровня |
Функция WinWindowFromID возвращает идентификатор дочернего окна, заданного своим идентификатором ресурса. Она имеет такой прототип:
HWND WinWindowFromID ( HWND hwndParent, // идентификатор родительского окна ULONG id); // идентификатор ресурса дочернего окна
Так как системное меню имеет идентификатор ресурса, равный FID_SYSMENU , зная его, мы легко сможем определить идентификатор окна системного меню.
На следующем этапе нам необходимо определить идентификатор ресурса для системного меню. Для этого окну системного меню необходимо послать сообщение MM_ITEMIDFROMPOSITION , как это показано ниже:
SHORT sSysMeniID; sSysMeniID = (SHORT)WinSendMsg (hwndSystemMenu, MM_ITEMIDFROMPOSITION , MPFROMSHORT(0), NULL);
Младшее слово параметра mp1 сообщения MM_ITEMIDFROMPOSITION должно содержать порядковый номер элемента меню. В нашем случае требуется определить идентификатор для самого первого элемента, имеющего нулевой номер. Этот элемент является временным системным меню, которое появляется на экране, если сделать щелчок левой или правой клавишей мыши по пиктограмме системного меню.
Для того чтобы вставить строку в это временное меню, нам нужно знать его идентификатор. Этот идентификатор можно узнать, если послать окну системного меню сообщение MM_QUERYITEM , передав вместе с ним через первый параметр идентификатор ресурса системного меню, а через второй параметр - адрес структуры типа MENUITEM :
MENUITEM mi; HWND hwndSystemSubMenu; WinSendMsg (hwndSystemMenu, MM_QUERYITEM , MPFROMSHORT(sSysMeniID), MPFROMP(&mi)); hwndSystemSubMenu = mi.hwndSubMenu;
В поле hwndSubMenu этой структуры будет записан искомый идентификатор временного меню.
Структура MENUITEM и указатель на нее определены следующим образом:
typedef struct _MENUITEM { SHORT iPosition; // позиция элемента USHORT afStyle; // стиль USHORT afAttribute; // атрибуты USHORT id; // идентификатор ресурса HWND hwndSubMenu; // вложенное меню ULONG hItem; // идентификатор объекта отображения } MENUITEM ; typedef MENUITEM *PMENUITEM;
В поле iPosition хранится порядковый номер элемента (т. е. его позиция). Нумерация начинается с нуля. Для обозначения последней позиции (например, при вставке элемента в конец) можно использовать значение MIT_END .
Поля afStyle и afAttribute определяют, соответственно, стиль и атрибуты элемента меню. Здесь вы можете использовать константы с префиксом имени MIS_ и MIA_, описанные в начале этой главы в разделе "Подготовка шаблона меню".
В поле id при добавлении нового элемента необходимо записать его идентификатор. Этот идентификатор будет потом проверяться обработчиком сообщения WM_COMMAND . Если добавляется разделительная линия, в качестве идентификатора можно использовать значение -1.
Если данный элемент является вложенным меню, то в поле hwndSubMenu должен храниться идентификатор окна вложенного меню. Для обычной строки меню здесь располагается значение NULL.
Поле hItem содержит идентификатор объекта отображения, однако если элемент описывает строку меню, имеющую стиль MIS_TEXT , в поле находится нулевое значение. Это поле нужно в том случае, если в стоках меню отображаются графические пиктограммы.
Теперь, когда вы познакомились со структурой MENUITEM , покажем, как выполняется добавление строк в меню. Начнем с добавления разделительной линии к системному меню:
mi.afStyle = MIS_SEPARATOR; mi.afAttribute = 0; mi.hwndSubMenu = 0; mi.hItem = 0; mi.id = -1; mi.iPosition = MIT_END; WinSendMsg (hwndSystemSubMenu, MM_INSERTITEM , MPFROMP(&mi), NULL);
Так как мы добавляем разделительную линию, в поле стиля afStyle записываем константу MIS_SEPARATOR . Поля afAttribute, hwndSubMenu и hItem будут иметь нулевые значения.
Разделительная линия не посылает сообщение WM_COMMAND , поэтому ее идентификатор устанавливаем равным -1. Мы решили добавить разделительную линию в конец меню, поэтому устанавливаем позицию, равную MIT_END.
Для добавления строки посылаем окну временного системного меню сообщение MM_INSERTITEM . В качестве первого параметра вместе с этим сообщением необходимо передать адрес подготовленной струткуры MENUITEM , а в качестве второго - текстовую строку, которая будет отображаться в меню.При добавлении разделительной линии текстовая строка не нужна, поэтому второй параметр имеет значение NULL.
Для вставки обычной строки после разделительной линии мы снова готовим структуру MENUITEM и посылаем окну временного системного меню сообщение MM_INSERTITEM :
mi.afStyle = MIS_TEXT; mi.afAttribute = 0; mi.hwndSubMenu = 0; mi.hItem = 0; mi.iPosition = MIT_END; mi.id = IDM_HELP_ABOUT; WinSendMsg (hwndSystemSubMenu, MM_INSERTITEM , MPFROMP(&mi), "Product Information...");
На этот раз в поле стиля находится константа MIS_TEXT, а в поле id - константа IDM_HELP_ABOUT. Во втором параметре сообщения MM_INSERTITEM мы указываем адрес текстовой строки Product Information..., которая будет отображаться в нижней части измененного нами системного меню.
Изменение состояния переключателя
Состояние автоматического переключателя, определенного со стилями BS_AUTOCHECKBOX или BS_AUTORADIOBUTTON, изменяется, когда пользователь делает щелчок мышью в его окне. Если же переключатель имеет стиль BS_CHECKBOX или BS_RADIOBUTTON, приложение должно само изменять его состояние.
Для изменения состояния переключателя (обычного или автоматического) ему необходимо послать сообщение BM_SETCHECK . В параметре mp1 этого сообщения следует указать новое состояние (0, 1 или2). Параметр mp2 не используется и должен содержать нулевое значение.
В следующем фрагменте кода приложение проверяет текущее состояние перключателя, записанное в переменной fButton3Checked. Если переключатель выключен, он включается, а если включен - выключается. После этого содержимое переменной fButton3Checked изменяется на противоположеное:
if(fButton3Checked) // Выключение переключателя WinSendMsg (hWndButton3, BM_SETCHECK, MPFROMSHORT(0), NULL); else // Включение переключателя WinSendMsg (hWndButton3, BM_SETCHECK, MPFROMSHORT(1), NULL); fButton3Checked = ~fButton3Checked;
Изменение внешнего вида курсора мыши
В предыдущем приложении мы изменяли пиктограмму курсора мыши, когда он находился над поверхностью окна Client Window . Процедура изменения пиктограммы очень проста - в момент обработки сообщения WM_MOUSEMOVE нужно получить идентификатор нужной пиктограммы и передать его функции WinSetPointer .
Вы можете загрузить идентификатор пиктограммы из ресурсов приложения с помощью функции WinLoadPointer , передав ей в качестве последнего параметра идентификатор курсора мыши, с которым этот курсор определен в файле описания ресурсов приложения, например:
HPOINTER hptr; hptr = WinLoadPointer(HWND_DESKTOP, NULLHANDLE, ID_APP_POINTER);
Здесь подразумевается, что файл описания ресурсов приложения содержит строку вида:
POINTER ID_APP_POINTER MOUSEMOV.PTR
В файле MOUSEMOV.PTR находится изображение курсора мыши, подготовленное приложением Icon Editor . С помощью этого приложения вы можете создавать не только пиктограммы, но и курсоры мыши.
В системе Presentation Manager имеется также набор встроенных изображений курсора мыши, который также доступен для вашего приложения. Получить идентификатор одного из таких курсоров можно с помощью функции WinQuerySysPointer :
hptr = WinQuerySysPointer (HWND_DESKTOP, SPTR_ICONINFORMATION, FALSE);
Идентификатор встроенного курсора передается этой функции через второй параметр. Третий параметр определяет, надо ли копировать курсор для приложения (значение FALSE) либо достаточно просто вернуть идентификатор встроенного курсора (значение TRUE). В первом случае перед завершением приложение должно удалить копию встроенного курсора, передав соответствующий идентификатор фукнции WinDestroyPointer через ее единственный параметр.
Получив идентификатор курсора мыши, вы можете изменить форму курсора при помощи функции WinSetPointer , вызвав ее при обработке сообщения WM_MOUSEMOVE :
WinSetPointer (HWND_DESKTOP, hptr);
Ниже мы приведем список всроенных идентификаторов курсора мыши вместе с кратким описанием и соответствующим изображением:
Идентификатор | Описание | Изображение курсора мыши |
SPTR_ARROW | Стандартный курсор | |
SPTR_TEXT | Текстовый курсор, используется при редакторовании текста | |
SPTR_WAIT | Курсор в виде часов, используется для режима, в котором пользователь должен ждать завершение какой-либо длительной операции | |
SPTR_MOVE | Курсор для перемещения объекта | |
SPTR_SIZENWSE | Курсор для одновременного изменения размеров окна по вертикали и горизонтали | |
SPTR_SIZENESW | Аналогично предыдущему, но с другим направлением стрелки | |
SPTR_SIZEWE | Курсор для изменения ширины окна | |
SPTR_SIZENS | Курсор для изменения высоты окна |
С помощью функции WinQuerySysPointer вы также можете получить идентификаторы встроенных пиктограмм, которые также можно использовать для установки курсора мыши:
Идентификатор | Описание | Изображение пиктограммы |
SPTR_APPICON | Пиктограмма приложения | |
SPTR_ICONINFORMATION | Информационное сообщение | |
SPTR_ICONQUESTION | Запрос информации от пользователя | |
SPTR_ICONERROR | Сообщение об ошибке | |
SPTR_ICONWARNING | Предупреждающее сообщение | |
SPTR_ILLEGAL | Запрещенные действия | |
SPTR_FILE | Файл | |
SPTR_MULTFILE | Группа файлов | |
SPTR_FOLDER | Папка | |
SPTR_PROGRAM | Программа |
Извещение от кнопки
Когда пользователь нажмает на кнопку (или изменяет состояние переключателя), окно-владелец кнопки (идентификатор которого был указан в параметрах функции WinCreateWindow ) получает извещающее сообщение.
Код этого сообщения зависит от стиля кнопки. Если не указаны стили BS_SYSCOMMAND или BS_HELP, извещение посылается в виде сообщения WM_COMMAND . Если же указаны перечисленные выше стили, вместо сообщения WM_COMMAND посылаются, соответственно, сообщения WM_SYSCOMMAND или WM_HELP .
Что же касается переключателей, то они посылают извещение в форме сообщения WM_CONTROL , которое мы рассмотрим в разделе "Переключатели".
Через параметр mp1 сообщения WM_COMMAND передается идентификатор органа управления. Однако вспомним, что сообщение WM_COMMAND посылает также меню приложения. Для того чтобы различить источник извещающего сообщения WM_COMMAND, необходимо проанализировать параметр mp2.
Младшее слово параметра mp2 содержит искомый идентификатор источника сообщения и может принимать одно из перечисленных ниже значений:
Значение | Описание |
CMDSRC_PUSHBUTTON | Извещающее сообщение WM_COMMAND посылается кнопкой, идентификатор которой передается через параметр mp1 |
CMDSRC_MENU | Источником сообщения является меню, идентификатор которого передается через параметр mp1 |
CMDSRC_ACCELERATOR | Сообщение пришло от акселератора с командным значением mp1 |
CMDSRC_FONTDLG | Источником сообщения является диалоговая панель для выбора шрифта. Через параметр mp1 передается идентификатор этой диалоговой панели |
CMDSRC_FILEDLG | Источником сообщения является диалоговая панель для выбора файла. Через параметр mp1 передается идентификатор этой диалоговой панели |
CMDSRC_OTHER | Прочий источник сообщения |
Старшее слово параметра mp2 сообщения WM_COMMAND может содержать значение TRUE или FALSE. В первом случае извещение появилось в результате работы с органом управления при помощи мыши, во втором - при помощи клавиатуры.
Для того чтобы было удобнее разбирать параметры сообщения WM_COMMAND , в файле pmwin.h определена структура CMDMSG, указатель на нее PCMDMSG и макрокоманда COMMANDMSG:
typedef struct _COMMANDMSG { USHORT cmd; // идентификатор органа управления USHORT unused; // не используется USHORT source; // источник извещающего сообщения USHORT fMouse; // признак использования мыши } CMDMSG; typedef CMDMSG *PCMDMSG;
#define COMMANDMSG(pmsg) \ ((PCMDMSG)((PBYTE)pmsg + sizeof(MPARAM)))
Обработчик сообщения WM_COMMAND обычно имеет такой вид:
case WM_COMMAND : { switch (COMMANDMSG(&msg) -> cmd) { case BTN1_ID: { // Обработка извещения от кнопки BTN1_ID . . . break; } case BTN2_ID: { // Обработка извещения от кнопки BTN2_ID . . . break; } default: break; } return 0; }
Если сообщение WM_COMMAND может поступать как от меню, так и от кнопок (или других органов управления), необходимо дополнительно анализировать источник сообщения, например, следующим способом:
if(COMMANDMSG (&msg) -> source == CMDSRC_PUSHBUTTON) { // Сообщение от кнопки . . . } else if(COMMANDMSG(&msg) -> source == CMDSRC_MENU) { // Сообщение от меню . . . }
Для извлечения параметров кнопки вы можете также использовать макрокоманды SHORT1FROMMP и SHORT2FROMMP :
cmd = SHORT1FROMMP (mp1); source = SHORT1FROMMP (mp2); fMouse = SHORT1FROMMP (mp2);
Извещения от круглого регулятора
Когда пользователь выполняет какие-либо операции с регулятором, в родительское окно, создавшее этот орган управления, поступают извещающие сообщения с кодом WM_CONTROL . Обработчик этого сообщения должен выполнять установку значения параметра, который регулируется пользователем.
Младшее слово параметра mp1 сообщения WM_CONTROL содержит идентификатор регулятора. Если ваше приложение создает несколько регуляторов или других органов управления, посылающих извещения в виде сообщения WM_CONTROL, вы должны проверять этот параметр.
Через старшее слово параметра mp1 передается код извещения, который соответствует операции, выполняемой пользователем. Код извещения может принимать одно из следующих значений:
Код извещения | Описание |
CSN_SETFOCUS | Регулятор получил или потерял фокус ввода. В первом случае через параметр mp2 передается значение TRUE, во втором - FALSE |
CSN_CHANGED | Пользователь изменил текущую позицию регулятора. Новое значение позиции передается через параметр mp2 |
CSN_TRACKING | Ручка регулятора перемещается при помощи мыши. Промежуточные значения позиции передаются через параметр mp2 |
Помимо перечисленных выше, от круглого регулятора поступает извещение с кодом CSN_QUERYBACKGROUNDCOLOR . Это извещение можно использовать для изменения цвета фона регулятора.
Как убрать курсор мыши
С помощью функции WinShowPointer вы можете убрать курсор мыши с экрана или высветить его вновь. Прототип этой функции представлен ниже:
BOOL WinShowPointer( HWND hwndDeskTop, // идентификатор окна Desktop Window BOOL fShow); // флаг
Вызывая эту функцию, вы можете увеличивать или уменьшать внутренний индикатор отображения курсора, передавая через параметр fShow, соответственно, значения FALSE и TRUE. Если этот индикатор равен нулю, курсор мыши отображается, а если больше нуля - нет.
Функция WinShowPointer возвращает новое значение индикатора отображения курсора мыши.
Классификация типов меню
В окне Frame Window имеется несколько органов управления, которые имеют отношение к меню.
В левом верхнем углу окна практически любого приложения имеется пиктограмма системного меню (рис. 3.1), при помощи которого можно изменить размеры или расположение окна, завершить работу приложения или просмотреть список открытых окон.
Рис. 3.1. Системное меню приложения OS/2 System Editor в частично локализованной версии операционной системы IBM OS/2
Системное меню представляет собой дочернее окно главного окна приложения Frame Window и имеет идентификатор FID_SYSMENU .
В правом верхнем углу главного окна приложения имеются кнопки минимизации и максимизации окна, которые дублируют соответствующие строки системного меню. Эти кнопки являются отдельным органом управления, идентифкатор окна которого равен FID_MINMAX .
На рис. 3.2 показано главное окно приложения OS/2 System Editor, имеющее меню верхнего уровня . Это меню состоит из строк File, Edit, Options и Help.
Рис. 3.2. Меню верхнего уровня и временное меню в окне приложения OS/2 System Editor
Помимо меню верхнего уровня, на рис. 3.2 также показано в раскрытом состоянии временное меню File, содержащее строки New, Open, Save, Save as и Autosave.
Обратите внимание, что после названия некоторых строк стоит многоточие. Это означает, что при выборе таких строк вместо немедленного выполнения функции на экране появится диалоговая панель, при помощи которой надо указать дополнительные параметры или выполнить другие действия.
Помимо текстовых строк, во временных меню обычно имеются горизонтальные разделительные линии. Кроме того, временные меню могут также содержать графические изображения. О том, как создавать такие меню, мы расскажем после того, как вы познакомитесь с функциями программного интерфейса Presentation Manager, предназначенными для работы с графическими изображениями.
На рис. 3.3 показана еще одна разновидность меню - меню второго уровня , которое появляется при выборе соответствующей строки временного меню .
Это меню состоит из строк On и Off.
Рис. 3.3. Меню второго уровня в приложении OS/2 System Editor
Заметим, что меню второго уровня может содержать ссылки на меню третьего уровня и т. д. Однако не следует увлекаться созданием многоуровневых меню, так как они неудобны в работе. Если вам нужно организовать настройку большого количества параметров, это лучше сделать при помощи другого органа управления, который называется блокнотом.
Последний тип меню, который мы рассмотрим в этой главе - это плавающие меню , которые в оригинальной документации называются меню Pop-Up . Обычно такое меню появляется, когда пользователь делает щелчок правой клавишей мыши по тому или иному объекту, расположенному в окне приложения. На рис. 3.4 показано плавающее меню, которое появляется после щелчка правой клавишей мыши по поверхности рабочего стола Workplace Shell. Это меню позволяет изменить свойства рабочего стола, завершить работу операционной системы и т. д.
Рис. 3.4. Контекстное меню рабочего стола Workplace Shell, которое отображается в виде плавающего меню
Плавающее меню, точно также как и временное, может содержать ссылки на меню второго уровня и графические изображения.
Кнопки
Работая в среде Workplace Shell с приложениями Presentation Manager, вы имели дело с кнопками и переключателями. Последние бывают квадратной либо круглой формы. Переключатели создаются на базе того же самого предопределенного класса окна, что и кнопки прямоугольной формы с надписанным на них текстом или нарисованным графическим изображением.
Для создания кнопки или переключателя (а так же других органов управления) используется функция WinCreateWindow . Кнопка создается на базе предопределенного класса окна WC_BUTTON , например, следующим образом:
hWndButton = WinCreateWindow (hWnd, WC_BUTTON , "Кнопка", WS_VISIBLE | BS_PUSHBUTTON, 0, 0, 0, 0, hWnd, HWND_TOP , BTN1_ID, NULL, NULL);
Заметим, что здесь используется именно функция WinCreateWindow , а не функция WinCreateStdWindow , при помощи которой мы создаем главное окно приложения. Эта функция может быть использована для создания дочернего окна на базе зарегистрированного вами класса.
Код символа
Код символа, соответствующий нажатой символьной клавише, располагается в битах 0 - 15 параметра mp2 и может быть получен при помощи макрокоманды SHORT1FROMMP :
cChar = SHORT1FROMMP (mp2);
Обычные символьные клавиши могут нажиматься одновременно с клавишей сдвига. Код символа, полученный вместе с сообщением WM_CHAR , учитывает состояние клавиши <Shift> в момент, когда была нажата символьная клавиша.
Заметьте, что клавиша <Shift> сама по себе генерирует сообщение WM_CHAR . Поэтому когда вы нажимаете, а затем отпускаете, например, комбинацию клавиш <Shift + a>, в функцию окна приходит четыре сообщения WM_CHAR. Первое соответствует нажатию клавиши <Shift>, второе - нажатию клавиши <a>. Третье и четвертое соответствуют отпусканию указанных клавиш и приходят в той последоваетльности, в которой отпускаются клавиши.
ЛИТЕРАТУРА
1. Фролов А.В., Фролов Г.В. Библиотека системного программиста. М.: ДИАЛОГ-МИФИ, 1993 - 1996
Т.6. Защищенный режим работы процессоров Intel 80286/80386/80486, 1993
T.20. Операционная система IBM OS/2 Warp, 1995
T.23. Глобальные сети компьютеров, 1996
Т.11 - 13. Операционная система Microsoft Windows 3.1 для программиста, 1994
Т.14. Графический интерфейс GDI в Microsoft Windows, 1994
Т.15. Мультимедиа для Windows. Руководство для программиста, 1994
Т.17. Операционная система Microsoft Windows 3.1 для программиста. Дополнительные главы, 1995
2. Фролов А.В., Фролов Г.В. Персональный компьютер - шаг за шагом. М.: ДИАЛОГ-МИФИ, 1994 - 1996
Т.3. Сети компьютеров в вашем офисе.
3. Real-World programming for OS/2 2.11. Sams Publishing, Indianapolis, 1994
4. OS/2 Warp Unleashed. Sams Publishing, Indianapolis, 1995
5. Nguyen T. and Moskal R. Advanced Programmer's Guide to OS/2. Brady Books, New York, 1989
6. Petzold C. Programming the OS/2 Presentation Manager. Microsoft Press, Redmont, 1989
7. Letwin G. Inside OS/2. Microsoft Press, Redmont, 1988
8. Крэнц Дж., Майзелл Э., Уилльямз Р. Операционная система OS/2. Возможности, функции и приложения. Мир, М.:, 1991
9. Операционная система IBM OS/2/2. Техническое описание и справочник программиста. "ИВК-СОФТ", М.:, 1990
10. Дж. Л. Кэмпбелл. Операционная система OS/2. Справочное руководство для программистов. Финансы и статистика, М.:, 1991
Макрокоманда CHARMSG
Для извлечения отдельных компонент сообщения WM_CHAR удобно использовать макрокоманду CHARMSG , определенную следующим образом:
#define CHARMSG(pmsg) \ ((PCHRMSG)((PBYTE)pmsg + sizeof(MPARAM) ))
Эта макрокоманда пользуется структурой CHRMSG , показанной ниже:
typedef struct _CHARMSG { USHORT fs; // поле флагов UCHAR cRepeat; // счетчик повторений UCHAR scancode; // скан-код USHORT chr; // код символа USHORT vkey; // виртуальный код клавиши } CHRMSG; typedef CHRMSG *PCHRMSG;
Ниже мы показали пример использования этой макрокоманды для сохранения всех параметров сообщения WM_CHAR в структуре cm типа CHRMSG:
CHRMSG cm; cm.chr = CHARMSG(&msg) ->chr; cm.vkey = CHARMSG(&msg) ->vkey; cm.scancode = CHARMSG(&msg) ->scancode; cm.cRepeat = CHARMSG(&msg) ->cRepeat; cm.fs = CHARMSG(&msg) ->fs;
Начальная позиция
Начальная позиция регулятора устанавливается при помощи сообщения CSM_SETVALUE . Ниже показан фрагмент кода, в котором начальная позиция устанавливается равной 255:
WinSendMsg(hWndCirc1, CSM_SETVALUE, MPFROMLONG(255L), NULL);
Как видно из этого примера, значение начальной позиции передается через параметр mp1. Параметр mp2 не используется и должен быть равен нулю.
Nbsp;ЭЛЕМЕНТАРНАЯ ТЕОРИЯ ОКОН
В этой главе мы будем говорить об окнах, которые являются основным элементом пользовательского интерфейса. Окна создаются любым приложением Presentation Manager, как явным вызовом соответствующих функций, так и косвенными методами.
Самое главное, в чем вы должны разобраться после прочтения этой главы, это иерархия окон, способы создания дочерних окон и управления ими, а также принципы взаимодействия окон посредством передачи сообщений. Пусть вас не смущает, что пока в наших окнах ничего не нарисовано - скоро мы это исправим.
Nbsp;КЛАВИАТУРА
В этой главе мы расскажем о том, как приложения Presentation Manager работают с клавиатурой. Как и следовало ожидать, методика работы с клавиатурой в прилоениях Presentation Manager полностью отличается от той, что используется в программах MS-DOS .
Напомним, что когда пользователь нажимает клавишу, генерируется аппаратное прерывание. В операционной системе MS-DOS обработчик этого прерывания записывал скан-код нажатой клавиши в специальный клавиатурный буфер, расположенный в области данных BIOS . Когда программе MS-DOS нужно было ввести код нажатой клавиши, она (прямо или косвенно) обращалась к прерыванию INT 16h , которое возвращало расширенный ASCII-код нажатой клавиши. При необходимости программа переходила в режим ожидания и находилась в нем до тех пор, пока пользователь не нажимал какую-нибудь клавишу. Способы работы с клавиатурой в среде MS-DOS мы подробно рассмотрели во втором томе "Библиотеки системного программиста", который называется "Аппаратное обеспечение IBM PC".
В среде операционной системы IBM OS/2 одновременно может работать несколько приложений, которые при этом должны пользоваться только одной клавиатурой. Очевидно, что в такой ситуации никакое приложение не может блокировать работу всей системы, ожидая ввод с клавиатуры.
Для работы с клавиатурой в приложениях Presentation Manager используется механизм сообщений. Каждый раз когда пользователь нажимает или отпускает любую клавишу, система Presentation Manager генерирует сообщение с кодом WM_CHAR . Параметры этого сообщения несут всю необходимую информацию о нажатой (или отпущенной) клавише.
В какое окно попадают клавиатурные сообщения?
В то, которое имеет фокус ввода. Если пользователь делает одно из приложений активным, выдвигая его на передний план, функция окна этого приложения будет получать клавиатурные сообщения. С помощью функции WinSetFocus приложение может передать фокус ввода любому окну. При этом сообщения от клавиатуры будут поступать в функцию этого окна.
Заметим, что в среде операционной системы IBM OS/2 могут работать приложения двух типов - приложения Presentation Manager и приложения текстового режима, напоминающие программы MS-DOS . Последние работают с клавиатурой при помощи набора функций, имеющих префикс имени Kbd, например, KbdCharIn . По своим возможностям эти функции недалеко ушли от прерывания INT 16h. Такие функции нельзя использовать в приложениях Presentation Manager, поэтому в нашей книге мы не будем их рассматривать.
Nbsp;ОРГАНЫ УПРАВЛЕНИЯ
В приложениях Presentation Manager используются различные органы управления, например, кнопки, полосы просмотра, однострочные и многострочные редакторы текстов, различные списки и т. д. Создание таких органов управления с использованием только функций рисования представляет достаточно сложную задачу, так как их поведение нетривиально.
Примером простейшего органа управления служит обыкновенная кнопка, которую можно увидеть практически в любой диалоговой панели. Этот орган управления создается на базе предопределенного класса окна WC_BUTTON и при внимательном изучении ведет себя достаточно сложно. Когда пользователь устанавливает на кнопку курсор мыши и нажимает клавишу мыши, кнопка как бы "уходит вглубь". При отпускании клавиши мыши кнопка возвращается в исходное состояние. Если в диалоговой панели расположено несколько кнопок, при помощи клавиатуры можно выбрать и нажать нужную. Кнопка может находиться в заблокированном состоянии, когда надпись, расположенная на ней, отображается серым цветом. Заблокированную кнопку нельзя нажать.
Можно продолжить перечисление свойств хорошо знакомой вам кнопки, однако и так ясно, что реализуя эти свойства самостоятельно, программист потратил бы немало времени. А ведь есть и более сложные органы управления, например, списки или блокноты. Органам управления можно посылать сообщения, на которые они будут реагировать соответствующим образом. С другой стороны, когда пользователь работает с органом управления, последний сам посылает сообщения в родительское окно.
Как же создаются органы управления в приложениях Presentation Manager?
Все органы управления представляют собой окна, которые являются дочерними по отношению к тем окнам, на которых они изображаются. Эти окна создаются при помощи функции WinCreateWindow на базе преопределенных классов, для которых в Presentation Manager предусмотрены соответствующие функции окна. В результате приложение только создает органы управления, подобно тому как оно создает дочерние окна, а Presentation Manager обеспечивает работу этих органов управления.
Здесь используется механизм наследования. Приложение создает объект (орган управления), который наследует свойства базового объекта, реализуемые функцией окна, зарегистрированной системой Presentation Manager для того или иного предопределенного класса окна.
При необходимости программист может переопределить те или иные методы (обработчики отдельных сообщений) для объекта, используя технику, которая называется Window Subclassing . Однако для обычных органов управления в большинстве случаев этого делать не нужно.
Как правило, приложение создает несколько органов управления в своем окне или в окне диалоговой панели. Так как органы управления являются дочерними окнами, они перемещаются вместе с родительским окном.
Что же касается диалоговых панелей , то они тоже представляют собой окна, созданные на базе предопределенного класса. Функция этого окна обеспечивает взаимодействие отдельных органов управления (например, обеспечивает клавиатурный интерфейс, позволяющий при помощи клавиш табуляции выбирать нужный орган управления).
Nbsp;РАБОТА С МЫШЬЮ
В приложениях Presentation Manager мышь используется также часто, как и клавиатура, а в некоторых случаях даже чаще. И хотя многие приложения могут обходиться без мыши, существуют такие приложения, для которых мышь является необходимым устройством ввода. Например, большинство функций графических редакторов выполняется только с помощью мыши.
Когда пользователь перемещает мышь в окне приложения, функция окна получает сообщение с кодом WM_MOUSEMOVE . Если установить курсор мыши в область окна и нажимать на мыши клавиши, функция окна также будет получать различные сообщения, которые мы скоро подробно рассмотрим. Вместе с параметрами этих сообщений передаются текущие координаты курсора мыши.
Nbsp;РИСОВАНИЕ ТЕКСТА
Создавая приложения Presentation Manager, вы не сможете использовать для рисования в его окнах приемы, знакомые вам из опыта программирования для операционной системы MS-DOS . Приложения Presentation Manager не могут вызывать прерывания BIOS или такие стандратные функции вывода, как printf, putc или puts. Однако, как вы увидите дальше, в вашем распоряжении имеются куда более мощные средства.
Для рисования в среде Presentation Manager имеется набор функций графического интерфейса GPI (Graphics Programming Interface ), в котором есть все необходимые средства для отображения текста, графических изображений, составленных из графических примитивов, таких как линия или окружность, а также растровых графических изображений. Функции этого интерфейса мы подробно рассмотрим позже в одном из следующих томов "Библиотеки системного программиста". В этой главе мы ограничимся только кратким знакомством с принципами рисования в окнах приложений Presentation Manager.
Следует заметить, что интерфейс GPI позволяет программистам создавать аппаратно-независимые приложения, которые будут работать, например, с любым видеоадаптером и любым принтером, если в составе операционной системы IBM OS/2 для них есть драйверы. Аппаратная независимость сильно упрощает программирование, исключая необходимость учитывать особенности различных моделей "почти стандартных" устройств компьютера.
Одной из особенностей приложений Presentation Manager является централизованный способ рисования содержимого окна. Как правило, приложение рисует в своем главном окне только в процессе обработки сообщения с кодом WM_PAINT (хотя можно рисовать и в любое другое время).
Сообщение WM_PAINT передается системой Presentation Manager функции окна в тот момент, когда необходимо обновить содержимое окна. Таким образом, приложение Presentation Manager должно быть готово выполнить операцию обновления окна в любой момент, как только это потребуется. Для этого приложение должно хранить в памяти текущее состояние окна, пользуясь которым обработчик сообщения WM_PAINT сможет выполнить операцию полной или частичной перерисовки содержимого окна Client Window .
Nbsp;СОЗДАНИЕ МЕНЮ
Практически любое приложение Presentation Manager имеет меню , расположенное под заголовком главного окна. С помощью меню пользователь может выполнять отдельные команды или операции, либо изменять режимы работы приложения.
Процецура создания меню в приложении Presentation Manager намного проще, чем аналогичная процедура в программе MS-DOS , так как для этого в программном интерфейсе Presentation Manager предусмотрены специальные средства.
Для того чтобы создать меню, вам нужно разработать его внешний вид и перечислить строки меню в файле описания ресурсов, используя специальные операторы.
Подключить меню к главному окну несложно - достаточно при создании окна функцией WinCreateStdWindow указать флаг FCF_MENU и организовать в функции окна обработку сообщения WM_COMMAND . Это сообщение посылается в функцию окна, создавшего меню, когда пользователь выбирает одну из строк меню. Через один из параметров сообщения в функцию окна передается код выбранной строки.
Nbsp;ТАЙМЕР
Приложения Presentation Manager могут следить за временем или выполнять какие-либо периодические действия с использованием таймера. Однако в отличие от программы MS-DOS , где для использования таймера было необходимо перехватывать соответствующее аппаратное прерывание, работа с таймером в Presentation Manager основана на передаче сообщений.
В своем приложении вы можете запустить несколько таймеров, каждый из которых будет периодически посылать в функцию окна сообщение с кодом WM_TIMER . В первом параметре этого сообщения передается идентификатор созданного таймера, поэтому нетрудно определить, от какого таймера пришло сообщение.
Заметим, что таймеры, запущенные с использованием функций Presentation Manager, не подходят для прецизионного измерения времени. Дело в том, что сообщения от таймера попадает в очередь сообщений приложения и обрабатываются наряду с другими сообщениями, поэтому они могут оказаться задержанными.
Если вы собираетесь создавать на базе IBM OS/2 систему управления, работающую в реальном режиме времени, не исключено, что вам придется создавать собственный драйвер или использовать другие приемы работы с физическим таймером компьютера. Однако для диалоговых программ задержки в поступлении сообщений от таймера не играют большой роли.
Нумерация кнопок
До сих пор для того чтобы различать кнопки мыши, мы называли их в зависимости от расположения - левая, правая или средняя кнопка. Однако в документации к программному интерфейсу Program Manager принято нумеровать кнопки.
Для однокнопочной мыши все просто - единственная кнопка имеет первый номер. Левая кнопка двухкнопочной мыши имеет первый номер, а правая - второй. Кнопки трехкнопочной мыши нумеруются следующим образом: левая имеет первый номер, правая - третий, средняя - второй.
Однако с помощью приложения Mouse, пиктограмма которого находится в папке System Setup, пользователь-левша может изменить назначение кнопок. При этом левая кнопка двухкнопочной мыши будет иметь второй номер, а правая - первый. В трехкнопочной мыши номера левой и правой кнопок меняются местами, а номер средней остается прежним.
Обработчик сообщения WM_BUTTON1DOWN
Когда пользователь делает щелчок левой клавишей мыши в области окна Client Window , функции окна передается сообщение WM_BUTTON1DOWN . В ответ на это сообщение она получает пространство отображения и рисует в месте расположения курсора мыши текстовую строку с координатами курсора.
Так как пространство отображения получается не во время обработки сообщения WM_PAINT , вместо функции WinBeginPaint используется функция WinGetPS . После выполнения рисования пространство отображения освобождается функцией WinReleasePS .
Обработчик сообщения WM_ERASEBACKGROUND
Сообщение WM_ERASEBACKGROUND посылается окном Frame Window перед тем, как оно будет рисовать окно Client Window . Если обработчик этого сообщения вернет значение TRUE, содержимое окна Client Window будет стерто (точнее говоря, закрашено системным цветом, выбранным для закрашивания внутренней области окна). Если же обработчик сообщения WM_ERASEBACKGROUND вернет значение FALSE, предполагается, что этот обработчик выполнит закрашивание внутренней области окна Frame Window самостоятельно. Именно такая обработка сообщения WM_ERASEBACKGROUND выполняется по умолчанию функцией WinDefWindowProc .
Заметим, что вместе с этим сообщением в первом параметре mp1 передается идентификатор пространства отображения для окна Frame Window . Этот идентификатор не следует использовать для закраски окна Client Window , так как сообщение WM_ERASEBACKGROUND передается только в том случае, если необходимо перерисовать окно Frame Window.
Может возникнуть такая ситуация, когда содержимое окна Client Window оказалось испорченным, например, в результате размещения там другого окна, а окно Frame Window не изменилось. В этом случае сообщение WM_ERASEBACKGROUND не будет записано в очередь приложения. Поэтому для закрашивания окна Client Window мы использовали обработчик сообщения WM_PAINT .