Программирование для IBM OS2

         

Скан-код


Аппаратный скан-код содержится в битах 24 - 31 параметра mp1 и имеет 8 разрядов. Приложения редко используют этот код, так как он аппаратно-зависимый. Больший интерес представляет виртуальный код клавиши и код символа.

Тем не менее, при необходимости вы можете извлечь скан-код из параметра mp1 при помощи макрокоманды CHAR4FROMMP, например, так:

nScan = CHAR4FROMMP(mp1);



Сообщение WM_BUTTON1DBLCLK


Так как окно приложения не имеет ни системного меню, ни меню верхнего уровня, для завершения его работы мы используем двойной щелчок левой клавишей мыши в окне приложения. Обработчик соответствующего сообщения WM_BUTTON1DBLCLK записывает в очередь приложения сообщение WM_QUIT , что приводит к завершению цикла обработки сообщений и прекращению работы приложения.


Так как главное окно приложения не имеет ни системного, ни обычного меню, для завершения работы приложения мы используем сообщение WM_BUTTON1DBLCLK . Это сообщение передается функции окна когда пользователь делает двойной щелчок левой клавишей мыши в окне приложения.



Сообщение WM_BUTTON1DOWN


Когда пользователь начинает перемещение окна приложения MOUSEMOVE, он передвигает курсор мыши во внутреннюю область окна и нажимает левую клавишу мыши. При этом окно приложения получает сообщение WM_BUTTON1DOWN .

Обработчик этого сообщения запоминает координаты курсора мыши, соответствующие точке начала перемещения, в глобальных переменных cxPoint и cyPoint.

Далее окно выдвигается на передний план функцией WinSetWindowPos и устанавливается признак перемещения окна (в переменную fDrag записывается значение TRUE).



Сообщение WM_BUTTON1UP


После выполнения перемещения пользователь отпускает левую клавишу мыши. При этом в функцию окна приложения передается сообщение WM_BUTTON1UP . Обработчик этого сообщения просто сбрасывает признак перемещения окна, записывая в переменную fDrag значение FALSE.



Сообщение WM_BUTTON2DOWN


Отображение загруженного ранее плавающего меню выполняется с помощью функции WinPopupMenu :

case WM_BUTTON2DOWN : { WinPopupMenu (hWnd, hWnd, hwndPopupMenu, SHORT1FROMMP (mp1), SHORT2FROMMP (mp1), IDM_FILE_NEW, PU_POSITIONONITEM | PU_HCONSTRAIN | PU_VCONSTRAIN | PU_MOUSEBUTTON1 | PU_MOUSEBUTTON2 | PU_KEYBOARD); return 0; }

В качестве идентификаторов родительского окна и окна-владельца мы указали значение hWnd - идентификатор, полученный функцией окна.

Функция WinPopupMenu отображает меню с идентификатором hwndPopupMenu, полученным при загрузке меню в обработчике сообщения WM_CREATE .

Для отображения меню используются координаты курсора мыши, которые передаются в параметре mp1 сообщения WM_BUTTON2DOWN . Эти координаты извлекаются из параметра mp1 с помощью макрокоманд SHORT1FROMMP (координата X) и SHORT2FROMMP (координата Y).

Так как в дополнительных параметрах указано значение PU_POSITIONONITEM, при отображении будет выделена строка с идентификатором IDM_FILE_NEW (строка New плавающего меню).

Для того чтобы вне зависимости от расположения курсора мыши во время щелчка в окне приложения плавающее меню полностью поместилось на экране, мы указали значения PU_HCONSTRAIN и PU_VCONSTRAIN .

Пользователь сможет выбирать строки отображаемого меню при помощи левой или правой клавиши мыши, а также при помощи клавиатуры, так как мы указали значения PU_MOUSEBUTTON1 , PU_MOUSEBUTTON2 и PU_KEYBOARD .



Сообщение WM_CHAR


Обработчик сообщения WM_CHAR получает пространство отображения и выбирает в него шрифт с фиксированной шириной символов, вызывая функцию SetCourierFont.

Далее при помощи макрокоманды CHARMSG обработчик разбирает параметры сообщения WM_CHAR и записывает их в соответствующие поля структуры cm типа CHRMSG.

Из параметров сообщения WM_CHAR формируется текстовая строка szMsg, которая затем отображается в нижней части окна приложения над заголовком таблицы. При формировании текстовой строки проверяется флаг KC_CHAR. Если сообщение соответствует символьной клавише, в строку записывается код соответствующего символа, в противном случае - код символа пробела.

После отображения отформированной текстовой строки обработчик сообщения WM_CHAR восстанавливает шрифт и возвращает пространство отображения.

Затем содержимое всего окна за исключением строки заголовка сдвигается вверх при помощи функции WinScrollWindow . Прототип этой функции приведен ниже:

LONG WinScrollWindow ( HWND hwnd, // идентификатор окна LONG lDx, // величина сдвига вправо LONG lDy, // величина сдвига вверх PRECTL prclScroll, // область сдвига PRECTL prclClip, // область ограничения HRGN hrgnUpdateRgn, // область обновления PRECTL prclUpdate, // прямоугольная область обновления ULONG flOptions); // параметры сдвига

В нашем приложении эта функция используется сделующим образом:

WinScrollWindow (hWnd, 0, cyChar + cyDesc, &rec, NULL, NULLHANDLE, NULL, SW_INVALIDATERGN );

Окно hWnd сдвигается вверх на величину высоты символов (с учетом размера выступающей части символов). Параметр SW_INVALIDATERGN указывает, что сдвинутая область должна быть обновлена, для чего функции окна будет передано сообщение WM_PAINT .

Область свертки (т. е. область, в которой будет выполняться сдвиг), определяется содержимым полей структуры rec, для заполнения которой мы использовали функцию WinSetRect :

WinSetRect (hab, &rec, 0, 2 * cyChar, cxClient, cyClient);

Прототип функции WinSetRect приведен ниже:

BOOL WinSetRect ( HAB hab, // идентификатор блока Anchor-block PRECTL prclrect, // адрес структуры RECTL LONG lLeft, // левый край LONG lBottom, // нижний край LONG lRight, // правый край LONG lTop); // верхний край

Как видно, сдвигается все окно кроме полосы, имеющей двойную высоту символов и расположенной в нижней части окна. В этой полосе отображается заголовок таблицы.


Приложение выполняет обработку этого сообщения для дублирования функций мыши при помощи клавиатуры.

Так как сообщение WM_CHAR приходит и при нажатии, и при отжатии клавиш, наш обработчик фильтрует сообщения, пропуская только те, что соответствуют нажатиям. Кроме того, мы фильтруем сообщения от обычных символьных клавиш, которые не имеют виртуального кода клавиши:

if((CHARMSG (&msg) ->fs & KC_KEYUP) | (!(CHARMSG(&msg) ->fs & KC_VIRTUALKEY))) return 0;

После фильтрации обработчик сообщения WM_CHAR определяет текущие экранные координаты курсора мыши и преобразует их в оконные координаты, вызывая для этого функции WinQueryPointerPos и WinMapWindowPoints . Текущие оконные координаты курсора мыши сохраняютя в глобальных переменных xMouse и yMouse.

Далее наш обработчик сообщения WM_CHAR анализирует полученный код виртуальной клавиши, изменяя соответствующим образом значения глобальных переменных xMouse и yMouse.

Если были нажаты клавиши перемещения курсора влево или вправо, содержимое переменной xMouse, соответственно, уменьшается или увеличивается на ширину курсора мыши (увеличенную на 5 пикселов для обеспечения зазора при отображении). Аналогично, если пользователь нажимает клавиши перемещения курсора вверх или вниз, содержимое переменной yMouse, соовтетственно, увеличивается или уменьшается на величину высоты курсора мыши. Вы, разумеется, можете выбрать для своего приложения иной шаг изменения позиции курсора мыши.

Когда пользователь нажимает клавиши <Home> или <End>, курсор мыши устанавливается, соответственно, в верхний левый или правый нижний угол окна. Все остальные коды виртуальных клавиш фильтруются.

После изменения переменных xMouse и yMouse обработчик сообщения изменяет содержимое полей x и y структуры ptl таким образом, чтобы новые координаты курсора мыши не выходили за границы окна Client Window , размеры которого были определены при обработке сообщения WM_SIZE :

ptl.x = max(min(xMouse, cxClient - 1), 0); ptl.y = max(min(yMouse, cyClient - 1), 0);

Затем полученные таким образом оконные координаты преобразуются в экранные и используются для установки новой позиции курсора мыши.




Обычно в приложениях Presentation Manager вы можете работать с полосами просмотра не только при помощи мыши, но и при помощи клавиатуры. Для обеспечения такой возможности в нашем приложении предусмотрен обработчик клавиатурного сообщения WM_CHAR.

Прежде всего этот обработчик отфильтровывает сообщения от клавиш, не имеющих кода виртуальной клавиши, и сообщения, которые приходят при отжатии клавиш:

if(!(CHARMSG(&msg) ->fs & KC_VIRTUALKEY)) return 0; if(!(CHARMSG(&msg) ->fs & KC_KEYUP)) return 0;

Затем анализируется код виртуальной клавиши и в зависимости от него окну Client Window посылаются сообщения WM_HSCROLL или WM_VSCROLL с соответствующим кодом команды, например:

switch(CHARMSG(&msg) -> vkey) { case VK_LEFT: { WinSendMsg(hWnd, WM_HSCROLL, NULL, MPFROM2SHORT(0, SB_LINELEFT)); break; } case VK_RIGHT: { WinSendMsg(hWnd, WM_HSCROLL, NULL, MPFROM2SHORT(0, SB_LINERIGHT)); break; } . . . }



Сообщение WM_COMMAND


Задача обработчика сообщения WM_COMMAND заключается в анализе кода выбранной строки меню и выполнении соответствующей операции. Так как задача нашего приложения ограничена демонстрацией способов создания меню, при выборе строк выполняется простое отображение диалоговой панели с сообщением о том, что данная функция не реализована.

Тем не менее, при выборе строки Product Information из меню Help на экране повляется информация о разработчике приложения. Если же из меню File выбрать строку Exit, с помощью функции WinPostMsg в очередь приложения будет записано сообщение WM_QUIT . Это приведет к тому, что при его выборке из очереди цикл обработки будет завершен. В результате приложение также завершит свою работу.


Так как наше приложение не имеет меню, обработчик сообщения WM_COMMAND анализирует только идентификатор кнопки, пославшей извещающее сообщение. Этот идентификатор извлекается при помощи макрокоманды COMMANDMSG.

В зависимости от того, какая кнопка была нажата, обработчик сообщения WM_COMMAND выводит на экран то или иное сообщение.

Обратите внимание на параметры функции WinMessageBox , с помощью которой выводится это сообщение:

WinMessageBox (HWND_DESKTOP, hWnd,"Нажата кнопка 1", "Сообщение", 0, MB_INFORMATION | MB_APPLMODAL | MB_MOVEABLE | MB_OK);

При вызове функции WinMessageBox мы указали флаг MB_APPLMODAL , в результате чего диалоговая панель с сообщением стала модальной в рамках приложения BUTTON. Последнее означает, что работа приложения будет продолжена только после того, как пользователь нажмет расположенную на диалоговой панели кнопку OK.

Указав флаг MB_SYSTEMMODAL , можно создать так называемую системную модальную диалоговую панель . Пока пользователь не завершит работу с этой диалоговой панелью, работа системы Presentation Manager будет приостановлена. Системные модальные диалоговые панели используются, например, для отображения критичных сообщений, когда для продолжения работы системы требуется вмешательство пользователя.



Сообщение WM_CONTROL


Рассмотрим параметры сообщения WM_CONTROL , поступающее в функцию родительского окна от переключателей.

Через младшее слово параметра mp1 этого сообщения передается идентификатор переключателя:

id = SHORT1FROMMP (mp1);

Если в окне создано несколько переключателей, следует проанализировать значение id для определения переключателя, который послал сообщение WM_CONTROL .

Старшее слово параметра mp1 содержит код извещения, который для переключателя может иметь значение BN_CLICKED или BN_DBLCLICKED . В первом случае пользователь сделал одиночный щелчок мышью по переключателю, во втором - двойной. Вот пример извлечения кода извещения:

nNotifCode = SHORT2FROMMP (mp1);

Если переключатель был создан с использованием стиля BS_USERBUTTON , в старшем слове параметра mp1 может передаваться код извещения BN_PAINT . В этом случае переключатель должен нарисовать себя в одном из трех состояний: заблокированном, выбранном или обычном. Подробнее об этом мы расскажем в одной из следующих наших книг, посвященных программированию для IBM OS/2.

И, наконец, для обычных переключателей через параметр mp2 сообщения WM_CONTROL передается идентификатор окна переключателя. Для переключателей со стилем BS_USERBUTTON через этот параметр передается указатель на структуру USERBUTTON , определенную следующим образом:

typedef struct _USERBUTTON { HWND hwnd; // идентификатор окна HPS hps; // идентификатор пространства отображения ULONG fsState; // новое состояние переключателя ULONG fsStateOld; // старое состояние переключателя } USERBUTTON; typedef USERBUTTON *PUSERBUTTON;


Это сообщение поступает в функцию окна тогда, когда пользователь делает щелчок мышью по одному из двух переключателей, расположенных в окне.

Внешний вид и состояние автоматического переключателя изменяется сразу после щелчка, поэтому все, что делает обработчик сообщения WM_CONTROL для автоматического переключателя - это определяет его текущее состояние. Для этого окну переключателя при помощи функции WinSendMsg посылается сообщение BM_QUERYCHECK. Полученное от этой функции состояние записывается в переменную fButton2Checked.

Работа с обычным переключателем требует от программиста несколько больших усилий, так как его состояние необходимо изменять из приложения.

Состояние переключателя BTN3_ID хранится в переменной fButton3Checked и сразу после инициализации приложения равно нулю. Когда пользователь делает щелчок мышью по обычному переключателю, в функцию окна приходит сообщение WM_CONTROL с идентификатором органа управления, равным BTN3_ID.

Обработчик сообщения WM_CONTROL в этом случае проверяет содержимое переменной fButton3Checked и выполняет включение, либо выключение переключателя. Для управления переключателем с помощью функции WinSendMsg ему посылается сообщение BM_SETCHECK. Если переключатель необходимо включить, параметр mp1 этого сообщения равен 1, если выключить - 0.

После изменения состояния переключателя обработчик сообщения WM_CONTROL соответствующим образом изменяет содержимое переменной fButton3Checked.




Обработчик сообщения WM_CONTROL прежде всего анализирует младшее слово параметра mp1, через который передается идентификатор органа управления (в нашем случае, идентификатор одного из трех регуляторов). Далее из старшего слова параметра mp1 выделяется код извещения, и если он равен SLN_CHANGE, сохраняется новое значение позиции регулятора, передаваемое через параметр mp2:

if(SHORT1FROMMP(mp1) == CIRCSLD1_ID) { if(SHORT2FROMMP(mp1) == CSN_CHANGED) { sColorR = (SHORT)mp2; WinInvalidateRect(hWnd, NULL, TRUE); } }

После изменения значения глобальной переменной, в которой хранится компонента цвета, вызывается функция WinInvalidateRect, что приводит к перерисовке окна приложения.



Сообщение WM_CREATE


В процессе создания главного окна приложения его функция получает сообщение WM_CREATE . Обработчик этого сообщения загружает из ресурсов приложения временное меню, описанное с идентификатором POPUP_MENU. Для этого он использует функцию WinLoadMenu :

case WM_CREATE : { hwndPopupMenu = WinLoadMenu (hWnd, NULLHANDLE, POPUP_MENU); return FALSE; }

Меню с идентификатором POPUP_MENU будет использовано в качестве плавающего. Оно будет появляться по щелчку правой клавишей мыши в окне приложения.


Задачей обработчика сообщения WM_CREATE является определение метрик шрифта с фиксированной шириной букв, удобного для отображения таблицы с параметрами сообщения WM_CHAR .

Так как перед определением метрик шрифта последний необходимо выбрать в пространство отображения, первое, что делает обработчик сообщения WM_CREATE - это получает идентификатор пространства отображения при помощи функции WinGetPS . Напомним, что обработчики любых сообщений, кроме сообщения WM_PAINT , должны получать этот идентификатор с помощью функции WinGetPS.

Далее обработчик сообщения выбирает в полученное пространство отображения шрифт с фиксированной шириной символов. Для этого он вызывает функцию SetCourierFont, определенную в нашем приложении.

После этого с помощью функции GpiQueryFontMetrics обработчик сообщения WM_CREATE записывает метрики выбранного шрифта в структуру fm типа FONTMETRICS.

Детальное описание этой структуры мы отложим до главы, посвященной шрифтам в Presentation Manager. Скажем только, что поля lAveCharWidth, lMaxBaselineExt и lMaxDescender структуры FONTMETRICS после возвращения из функции GpiQueryFontMetrics будут содержать, соответственно, ширину, высоту и размер выступающей части символов.

После определения и сохранения необходимых нам метрик шрифта обработчик выбирает в контекст отобаржения тот шрифт, который используется по умолчанию. Для этого он вызывает функцию ResetFont, определенную в нашем приложении.

Перед возвращением управления обработчик сообщения WM_CREATE освобождает пространство отображения, вызывая функцию WinReleasePS .




Обработчик сообщения WM_CREATE определяет размеры курсора мыши. Эти размеры являются системными значениями, для получения которых необходимо использовать функцию WinQuerySysValue , передав ей через второй параметр константы SV_CXPOINTER (ширина курсора мыши) и SV_CYPOINTER (высота курсора мыши):

cxPointer = WinQuerySysValue (HWND_DESKTOP, SV_CXPOINTER); cyPointer = WinQuerySysValue (HWND_DESKTOP, SV_CYPOINTER);

Эти размеры используются приложением для размещения изображений пиктограмм в своем окне. Чтобы обеспечить интервал между пиктограммами, значение переменной cxPointer в нашем приложении увеличивается на 5 пикселов.




Обработчик сообщения WM_CREATE запускает два таймера с идентификаторами, соответственно, ID_APP_TIMER1 и ID_APP_TIMER2:

WinStartTimer (hab, hWnd, ID_APP_TIMER1, 1000); WinStartTimer (hab, hWnd, ID_APP_TIMER2, 3000);

Первый из них будет посылать функции окна hWnd сообщение WM_TIMER с интервалом 1000 мс, т. е. каждую секунду. Второй будет посылать сообщение в три раза реже, так как для него установлена задержка 3000 мс.




Обработчик сообщения WM_CREATE запускает таймер с идентификатором ID_APP_TIMER:

WinStartTimer (hab, hWnd, ID_APP_TIMER, 1000);

Этот таймер посылает функции окна раз в секунду сообщение WM_TIMER , которое будет использоваться для обновления показаний часов.

Кроме этого, при обработке сообщения WM_CREATE из ресурсов приложения загружаются курсоры мыши, которые используются при перемещении главного окна приложения.




Как вы уже знаете, сообщение WM_CREATE передается функции окна при его создании. Параметр mp1 этого сообщения содержит значение параметра pCtlData, переданного в функцию WinCreateWindow .

В параметр mp2 при этом записывается указатель на следующую структуру:

typedef struct _CREATESTRUCT { PVOID pPresParams; // параметры отображения PVOID pCtlData; // управляющие данные ULONG id; // идентификатор окна HWND hwndInsertBehind; // окно-брат, за которым // отображается окно HWND hwndOwner; // окно-владелец LONG cy; // высота окна LONG cx; // ширина окна LONG y; // координата по оси Y LONG x; // координата по оси X ULONG flStyle; // стиль окна PSZ pszText; // заголовок окна PSZ pszClassName; // имя класса HWND hwndParent; // родительское окно } CREATESTRUCT; typedef CREATESTRUCT *PCREATESTRUCT;

Нетрудно заметить, что поля этой структуры повторяют аналогичные параметры функции WinCreateWindow . Анализируя поля структуры CREATESTRUCT, обработчик сообщения WM_CREATE может проверить параметры создаваемого окна.

Если эти параметры верны, обработчик может вернуть значение FALSE, что разрешает создание окна. Если же один или несколько параметров имеют неправильное знчение, функция окна может вернуть значение TRUE. Окно при этом создано не будет, а функция WinCreateWindow вернет значение NULLHANDLE.




Обработчик сообщения WM_CREATE создает две кнопки, пользуясь для этого функцией WinCreateWindow :

hWndButton1 = WinCreateWindow (hWnd, WC_BUTTON , "Кнопка 1", WS_VISIBLE | BS_PUSHBUTTON, 0, 0, 0, 0, hWnd, HWND_TOP , BTN1_ID, NULL, NULL); hWndButton2 = WinCreateWindow (hWnd, WC_BUTTON , "Кнопка 2", WS_VISIBLE | BS_PUSHBUTTON, 0, 0, 0, 0, hWnd, HWND_TOP , BTN2_ID, NULL, NULL);

Первая из этих кнопок имеет идентифкатор BTN1_ID, вторая - BTN2_ID. Идентификаторы окон для этих кнопок записываются, соответственно, в глобальные переменные hWndButton1 и hWndButton2.

Обратите внимание, что для координат и размеров кнопок указаны нулевые значения. Реальные размеры кнопок будут установлены при обработке сообщения WM_SIZE .

Обработчик сообщения WM_CREATE возвращает значение FALSE, разрешая тем самым создание главного окна приложения.




Обработчик сообщения WM_CREATE создает кнопку и два переключателя. Идентификатор окна кнопки записывается в переменную hWndButton1. Идентификатор кнопки равен BTN1_ID.

Первый из создаваемых переключателей имеет стиль BS_AUTOCHECKBOX и работает автоматически. Идентификатор окна автоматического переключателя хранится в переменной hWndButton2, а идентификатор переключателя равен BTN2_ID.

Второй переключатель создается с использованием стиля BS_CHECKBOX. Идентификатор окна для него хранится в переменной hWndButton3, а идентификатор самого переключателя равен BTN3_ID.

При создании кнопки для размеров и места расположения мы указали нулевые значения, так как размещение органов управления и определение их размеров будет выполняться при обработке сообщения WM_SIZE .



Сообщение WM_DESTROY


Перед уничтожением окна его функция получает сообщение WM_DESTROY . В ответ на это сообщение наше приложение удаляет загруженное ранее окно плавающего меню, вызывая для этого функцию WinDestroyWindow:

case WM_DESTROY : { WinDestroyWindow(hwndPopupMenu); break; }


В ответ на сообщение WM_DESTROY , которое передается в функцию окна при завершении работы приложения, выполняется уничтожение двух загруженных курсоров мыши при помощи функции WinDestroyPointer :

WinDestroyPointer (hptr); WinDestroyPointer (hptr1);

В качестве единственного параметра этой функции передается идентификатор уничтожаемого курсора мыши.




При уничтожении главного окна приложения обработчик сообщения WM_DESTROY останавливает таймер:

WinStopTimer (hab, hWnd, ID_APP_TIMER);

Дополнительно он удаляет курсоры мыши, загруженные при обработке сообщения WM_CREATE .




Перед уничтожением окна обработчик сообщения WM_DESTROY останавливает оба таймера, запущенных ранее. Для этого используется функция WinStopTimer :

WinStopTimer (hab, hWnd, ID_APP_TIMER1); WinStopTimer (hab, hWnd, ID_APP_TIMER2);



Сообщение WM_ERASEBACKGROUND


Это сообщение обрабатывается как обычно. В ответ на него функция окна возвращает значение 1L, позволяя при необхоимости окну Frame Window стереть содержимое окна Client Window .



Сообщение WM_HITTEST


С помощью сообщения WM_HITTEST система Presentation Manager может определить способ обработки сообщений мыши для заданного окна. Это сообщение передается фукнции окна только в том случае, если при регистрации класса окна был указан стиль CS_HITTEST .

Определив собственный обработчик для этого сообщения, приложение может заблокировать сообщения мыши для выбранного окна, при этом окно будет вести себя как заблокированное. Попытка сделать щелчок мышью в этом окне вызовет звуковой сигнал.

Обработчик сообщения WM_HITTEST получает через парметр mp1 координаты курсора мыши. Он может вернуть одно из перечисленных ниже значений (так называемый индикатор теста Hit Test):

Значение Описание
HT_NORMAL Сообщения мыши обрабатываются обычным образом
HT_TRANSPARENT Часть окна, расположенная под курсором мыши, считается прозрачной. Тест должен выполняться для окна, расположенного ниже под данным окном, как будто прозрачное окно не существует. В качестве прозрачного вы можете объявить, например, дочернее окно
HT_DISCARD Сообщения мыши не должны передаваться функции окна
HT_ERROR Аналогично предыдущему, однако раздается звуковой сигнал и окно отодвигается на задний план

По умолчанию обработчик активного окна возвращает значение HT_NORMAL, а обработчик заблокированного окна - значение HT_ERROR.



Сообщение WM_HSCROLL


Сообщение WM_HSCROLL поступает в функцию окна, когда пользователь работает с горизонтальной полосой просмотра или клавишами, предназначенными для свертки текста по горизонтали. Обработчик этого сообщения выполняет действия, аналогичные действиям обработчика сообщения WM_VSCROLL, однако он изменяет содержимое глобальной переменной nXScrollPos (позиция движка горизонтальной полосы просмотра).



Сообщение WM_INITMENU


Сообщение WM_INITMENU поступает в момент инициализации каждого временного меню. Наше приложение получает вместе с этим сообщением идентификатор временного меню и идентификатор окна меню, сохраняя их, соответственно, в переменных usItemId и hwndMenu.

Для временного меню File обработчик сообщения WM_INITMENU выполняет блокировку строк New и Open, вызывая функцию WinEnableMenuItem. Во временном меню Edit аналогичным образом блокируются строки Undo и Redo.



Сообщение WM_MOUSEMOVE


Сообщение WM_MOUSEMOVE передается в функцию окна при перемещении курсора мыши внутри окна. Параметры, которые передаются с этим сообщением, такие же, что и параметры сообщений от клавиш мыши, рассмотренные выше.

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


Окно приложения получает сообщение WM_MOUSEMOVE всегда, когда над ним перемещается курсор мыши. Если пользователь нажал левую клавишу мыши и начал перемещение окна, обработчиком сообщения WM_BUTTON1UP устанавливается признак fDrag. В результате обработчик сообщения WM_MOUSEMOVE начинает процедуру перемещения окна.

Прежде всего он изменяет форму курсора мыши, вызывая функцию WinSetPointer :

WinSetPointer (HWND_DESKTOP, hptr1);

В качестве первого параметра этой функции передается идентификатор окна Desktop Window , а в качестве второго - идентификатор курсора мыши, который будет отображаться внутри окна.

На следующем шаге обработчик сообщения WM_MOUSEMOVE записывает новые координаты курсора в переменные cxNewPoint и cyNewPoint, а также определяет текущие координаты окна Frame Window , вызывая для этого функцию WinQueryWindow Pos.

Затем выполняется перемещение окна при помощи функции WinSetWindowPos :

WinSetWindowPos (hWndFrame, HWND_TOP , swp.x + (cxNewPoint - cxPoint), swp.y + (cyNewPoint - cyPoint), 0, 0, SWP _MOVE );

Новые координаты окна выбираются исходя из текущих (записанных в структуре swp) и относительной величины смещения мыши по вертикали и горизонтали. Заметим, что функция WinQueryWindow Pos записывает в структуру swp координаты окна в системе координат, связанной с окном Desktop Window . Начало этой системы координат находится в левом нижнем углу экрана.

Если пользователь перемещает курсор мыши над окном приложения не нажимая левой клавиши мыши, обработчик сообщения WM_MOUSEMOVE отображает курсор мыши в виде открытой ладони:

WinSetPointer (HWND_DESKTOP, hptr);




Обработчик сообщения WM_MOUSEMOVE с помощью функции WinQuerySysPointer получает идентификатор встроенной пиктограммы SPTR_ICONINFORMATION и использует его для изменения курсора мыши. Последняя операция выполняется с помощью фукнции WinSetPointer . Таким образом, для курсора мыши вы можете использовать не только встроенные изображения курсоров, но и встроенные изображения пиктограмм.

Обратите внимание, что обработка сообщения WM_MOUSEMOVE завершается возвращением значения TRUE. Это необходимо для того, чтобы отменить вызов стандартного обработчика данного сообщения, который устанавливает стандартный курсор мыши. Если после обработки сообщения WM_MOUSEMOVE передать управление функции WinDefWindowProc , форму курсора изменить не удастся, так как данная функция будет всегда восстанавливать стандартный курсор.



Сообщение WM_PAINT


Сообщение WM_PAINT поступает в функцию окна после его создания, а также тогда, когда пользователь изменяет размеры окна (в последнем случае посылку этого сообщения инициирует обработчик сообщения WM_SIZE ).

Получив пространство отображения, обработчик сообщения WM_PAINT закрашивает область, требующую обновления, выбирает шрифт с фиксированной шириной букв и затем рисует заголовок таблицы в нижней части главного окна приложения. После этого он восстанавливает шрифт и освобождает пространство отображения.


Обработчик сообщения WM_PAINT получает пространство отображения и затем определяет размеры окна, вызывая для этого функцию WinQueryWindow Rect. Далее с помощью функции WinDrawText в центре окна выводится текстовая строка, после чего пространство отображения освобождается.




Задачей обработчика сообщения WM_PAINT является определение текущего времени (на момент прихода сообщения WM_PAINT) и его отображение в окне приложения.

Получив пространство отображения, обработчик определяет размеры главного окна приложения, а также дату и время. Последнее делается при помощи функции DosGetDateTime:

DosGetDateTime(&dt);

Далее функция окна формирует текстовую строку pszBuf, записывая в нее время (в формате Часы:Минуты:Секунды), устанавливает шрифт с фиксированной шириной символов и отображает время при помощи функции WinDrawText :

WinDrawText (hps, -1, pszBuf, &rec, 0L, 0L, DT_WORDBREAK | DT_CENTER | DT_VCENTER | DT_TEXTATTRS | DT_ERASERECT);

Параметры для этой функции подобраны таким образом, чтобы строка времени отображалась в центре окна, причем перед отображением содержимое окна стирается.

Перед завершением обработки сообщения WM_PAINT функция окна восстанавливает шрифт и освобождает полученное ранее пространство отображения.




Единственная задача обработчика сообщения WM_PAINT заключается в закраске внутренней области окна Client Window при изменении размеров главного окна приложения. Для закраски используется функция WinFillRect .



Сообщение WM_SIZE


При обработке сообщения WM_SIZE функция окна определяет размеры окна, сохраняя их в переменных cxClient и cyClient, а затем инициирует перерисовку окна. С этой целью вызывается функция WinInvalidateRect , объявляющая все окно требующим перерисовки. В результате функция окна получит сообщение WM_PAINT .


Обработчик сообщения WM_SIZE выполняет перерисовку окна приложения, когда пользователь изменяет его размеры, вызывая функцию WinInvalidateRect . В результате функция окна приложения получит сообщение WM_PAINT . Это необходимо для того чтобы строка символов всегда отображалась в центре окна.




Обработчик сообщения WM_SIZE изменяет расположение и размеры двух кнопок, используя для этого функцию WinSetWindowPos :

WinSetWindowPos (hWndButton1, HWND_TOP , 10, 10, 200, 50, SWP _SIZE | SWP_MOVE | SWP_ZORDER ); WinSetWindowPos (hWndButton2, HWND_TOP , 10, 70, 200, 50, SWP _SIZE | SWP_MOVE | SWP_ZORDER );

После этого содержимое главного окна перерисовывается.




Обработчик сообщения WM_SIZE располагает кнопку в нижней части окна. Выше нее размещаются переключатели. Изменение расположения и размеров органов управления выполняется функцией WinSetWindowPos . Перед возвращением управления обработчик сообщения WM_SIZE заказывает перерисовку окна, вызывая функцию WinInvalidateRect . Окно будет перерисовано при обработке сообщения WM_PAINT .



Сообщение WM_TIMER


Обработчик сообщения WM_TIMER проверяет идентификатор таймера.

Для первого таймера вызывается функция DosBeep, издающая звуковой сигнал с частотой 1000 Гц и продолжительностью 100 мс. Для второго таймера эта же функция издает звуковой сигнал с частотой 400 Гц и длительностью 300 мс. Так как первый таймер посылает сообщения в три раза чаще второго, на каждый сигнал низкого тона вы услышите три сигнала высокого тона.

Если в функцию окна пришло сообщение WM_TIMER с идентификатором, отличным от определенных в приложении, оно передается без изменений функции WinDefWindowProc .

Функция DosBeep входит в программный интерфейс ядра операционной системы IBM OS/2 и не имеет никакого отношения к операционной системе MS-DOS или IBM DOS. Префикс имени Dos говорит лишь о принадлжедности этой функции к указанному программному интерфейсу.

В системе Presentation Manager есть еще одна функция, которую можно использовать для выдачи звуковых сигналов. Это фукнция WinAlarm:

BOOL WinAlarm( HWND hwndDeskTop,// идентификатор окна Desktop Window ULONG flStyle); // вид звукового сигнала

Параметр flStyle определяет вид звукового сигнала. Для подачи предупреждающего звукового сигнала этот параметр должен иметь значение WA_WARNING , для сигнализации ошибки - WA_ERROR , а для сопровождения информационного сообщения - WA_NOTE .



Сообщение WM_VSCROLL


Сообщение WM_VSCROLL поступает в функцию окна, когда пользователь работает с вертикальной полосой просмотра при помощи мыши или нажимает клавиши, которые используются нашим приложением для работы с этой полосой просмотра.

Анализируя значение старшего слова параметра mp2, в котором хранится код команды, обработчик сообщения WM_VSCROLL изменяет соответствующим образом значение глобальной переменной nYScrollPos. Напомним, что в этой переменной хранится текуще положение движка вертикальной полосы просмотра.

Далее после ограничения диапазона изменения координаты движка последний устанавливается в новую позицию. Для этого окну вертикальной полосы просмотра посылается сообщение SBM_SETPOS:

WinSendMsg(hwndYScroll, SBM_SETPOS, (MPARAM)nYScrollPos, NULL);

Для того чтобы указанные изменения позиции движка отразились на содержимом окна Client Window, его необходимо перерисовать. Поэтому перед возвращением управления обработчик сообщения WM_VSCROLL вызывает функцию WinInvalidateRect, перерисовывая окно приложения.



Сообщения


В мультизадачной среде, которой является операционная система IBM OS/2 Warp, одновременно могут происходить многие события. Приложения могут работать параллельно и независимо, что в целом повышает производительность системы. Однако возможность мультизадачной обработки накладывает определенные требования на приемы работы с такими устройствами, как мышь и клавиатура.

Программа, запущенная под управлением операционной системы MS-DOS , может монопольно использовать мышь и клавиатуру, так как она работает в одиночестве. Весьма распространенный способ ввода с клавиатуры в программах MS-DOS заключается в том, что программа вызывает функцию getch (или аналогичную), останавливающую работу программы до тех пор, пока пользователь не нажмет какую-нибудь клавишу. После ввода программа продолжает свое выполнение.

Очевидно, что в мультизадачной среде такой способ ввода применять нельзя, так как одновременно могут работать несколько приложений и каждому из них может потребоваться ввести что-нибудь с клавиатуры. Вопрос - какое приложение должно выполнять ожидание в данном случае?

Аналогичная трудность возникает и при работе с мышью. Пользователь может перемещать курсор мыши внутри окон, созданных разными приложениями. Более того, пользователь может начинать ту или иную операцию, выполняемую с помощью мыши, в окне одного приложения, а заканчивать - в окне другого приложения.

Например, пусть мы запустили два приложения. Можно установить курсор мыши в окно первого приложения, нажать левую клавишу мыши, затем, не отпуская клавишу, переместить курсор мыши в окно второго приложения и там отпустить клавишу мыши. При этом в окне первого приложения произошло одно событие (пользователь нажал левую клавишу мыши), а в окне второго - другое (пользователь отпустил клавишу мыши). Очевидно, что использование традиционных для MS-DOS способов работы с мышью в среде Presentation Manager (если бы такое было возможно) может привести к большим проблемам при попытке приложений определить, что же все-таки пользователь сделал с курсором и клавишами мыши.


Для того чтобы процедура создания приложений Presentation Manager не стала безумно сложной, был предусмотрен простой механизм передачи сообщений с использованием очередей.

Под сообщением понимается некоторая структура данных, несущая исчерпывающую информацию о том или ином событии. Например, щелчок клавишей мыши приводит к созданию сообщения, в котором, в частности, записаны координаты курсора мыши на момент возникновения такого события. Каждое сообщение имеет код, который можно использовать для идентификации события.

В процессе инициализации приложение Presentation Manager создает очередь, предназначенную для хранения сообщений, имеющих отношение к данному приложению. Поэтому если пользователь нажмет клавишу мыши, когда курсор будет указывать на окно первого приложения, в очередь первого приложения будет записано сообщение об этом событии. После того как пользователь, не отпуская клавишу мыши, переместит курсор мыши в область окна второго приложения и там отпустит клавишу, в очередь второго приложения будет записано соответствующее сообщение (о том, что пользователь отпустил клавишу).

Разумеется, сообщения могут возникать не только от мыши и клавиатуры. Они могут создаваться таймером , драйверами различных периферийных устройств, а также другими приложениями и системой Presentation Manager.


Сообщения от кнопок мыши


Когда пользователь нажимает клавишу мыши, функция окна, расположенного под курсором мыши, получает одно из следующих сообщений: WM_BUTTON1DOWN , WM_BUTTON2DOWN , WM_BUTTON3DOWN (соответственно, для первой, второй и третьей кнопки). При отпускании клавиши мыши функция окна получает сообщения WM_BUTTON1UP , WM_BUTTON2UP и WM_BUTTON3UP (в зависимости от номера кнопки). Пример обработки сообщения WM_BUTTON1DOWN есть в приложении MYWINDOW, исходные тексты которого мы привели в первой главе этой книги.

Таким образом, приложение может выполнять раздельную обработку операций нажатия и отпускания кнопок мыши.

Если пользователь сделал одиночный щелчок в окне приложения (т. е. достаточно бытро нажал и отпустил клавишу мыши, не свигая курсор), в функцию окна после отпускания кнопки мыши поступит одно из следующих сообщений: WM_BUTTON1CLICK , WM_BUTTON2CLICK или WM_BUTTON3CLICK .

В ответ на двойной щелчок в функцию окна поступают сообщения WM_BUTTON1DBLCLK , WM_BUTTON2DBLCLK и WM_BUTTON3DBLCLK . Заметим, что в ответ на первый щелчок при этом в функцию окна потсупят сообщения о нажатии кнопки и об отпускани кнопки. В ответ на второй щелчок придет сообщение о двойном щелчке и затем сообщение об отпускании кнопки.

Через параметр mp1 вместе с перечисленными выше сообщениями передаются координаты курсора мыши. Вы можете извлечь их с помощью макрокоманд SHORT1FROMMP или SHORT2FROMMP (соответственно, координату X и Y). Через параметр mp2 передаются флаги и результаты теста Hit Test (о котором мы расскажем позже).

Для извлечения параметров собщений мыши удобно использовать макрокоманду MOUSEMSG , например:

case WM_BUTTON1DOWN : { cxPoint = MOUSEMSG(&msg) -> x; cyPoint = MOUSEMSG(&msg) -> y; break; }

Эта макрокоманда, а также соответствующая структура MSEMSG и указатель на нее определены следующим образом:

typedef struct _MOUSEMSG { SHORT x; // коодрината X SHORT y; // координата Y USHORT codeHitTest; // результаты тестирования USHORT fsInp; // флаги } MSEMSG; typedef MSEMSG *PMSEMSG;

#define MOUSEMSG(pmsg) \ ((PMSEMSG)((PBYTE)pmsg + sizeof(MPARAM)))

В поле fsInp могут расплагаться флаги, аналогичные флагам, передаваемым вместе с сообщением WM_CHAR . Используя эти флаги, можно определить, была ли нажата на клавиатуре какая-либо клавиша в момент, когда пользователь нажал клавишу мыши. Список возможных значений для поля fsInp был приведен при описании сообщения WM_CHAR. Теперь к этому списку добавится только одно значение:

Флаг Маска Описание
KC_NONE 0x0000 На клавиатуре не была нажата ни одна клавиша

Поле codeHitTest мы опишем позже.



Сообщения от полосы просмотра


Горизонтальные полосы просмотра, определенные для окна (одним из описанных выше способов) посылают в окно сообщение WM_HSCROLL , а все вертикальные - WM_VSCROLL .

Если полоса просмотра была создана первым способом (как орган управления), эти сообщения будет получать функция родительского окна. Если полоса просмотра была создана вторым способом (определена при создании окна), сообщения от нее будут поступать в функцию окна, имеющего полосы просмотра.

Опишем параметры сообщений WM_HSCROLL и WM_VSCROLL.

Как мы уже говорили, через параметр mp1 этих сообщений передается идентификатор полосы просмотра, через младшее слово параметра mp1 - текущая позиция движка полосы просмотра, а через старшее слово этого же параметра - код операции, выполненной пользователем над полосой просмотра. Вы можете извлечь перечисленные выше параметры следующим образом:

usidentifier = (USHORT) mp1; // идентификатор окна sslider = SHORT1FROMMP(mp2); // позиция движка uscmd = SHORT2FROMMP(mp2); // код операции

Если значение позиции движка равно нулю, то это означает, что либо пользователь так и не переместил движок, либо в случае команды SB_SLIDERPOSITION (которая будет описана ниже) пользователь отпустил клавишу мыши, когда курсор находился вне области полосы просмотра.

Ниже мы перечислим коды команд, которые могут поступать от полос просмотра.

Код комады Описание
SB_LINELEFT Пользователь сделал щелчок мышью по левой кнопке горизонтальной полосы просмотра. Значение позиции полосы просмотра уменьшается на единицу
SB_LINERIGHT Аналогично предыдущему, но по правой кнопке. Значение позиции полосы просмотра увеличивается на единицу
SB_PAGELEFT Пользователь сделал щелчок по полосе просмотра слева от движка
SB_PAGERIGHT Аналогично предыдущему, но щелчок сделан справа от движка
SB_LINEUP Пользователь сделал щелчок мышью по верхней кнопке горизонтальной полосы просмотра. Значение позиции полосы просмотра уменьшается на единицу
SB_LINEDOWN Аналогично предыдущему, но щелчок сделан по нижней кнопке полосы просмотра. Значение позиции полосы просмотра увеличивается на единицу
SB_SLIDERPOSITION Движок установлен в конечную позицию
SB_SLIDERTRACK Сообщение с этим кодом непрерывно поступает в процессе перемещения движка полосы просмотра при помощи мыши
SB_ENDSCROLL Сообщение с кодом SB_ENDSCROLL посылается в том случае, если пользователь завершил перемещение движка полосы просмотра
<
Обычно в приложениях Presentation Manager организуется управление полосами просмотра при помощи клавиатуры. Для управления движком полосы просмотра при этом используются клавиши перемещения курсора и клавиши <PgUp>, <PgDn>, <Home> и <End>. Как правило, с помощью клавиш <Home> и <End> вы можете перейти, соответственно, в начало и в конец документа.

Так как действия, выполняемые при работе с движком, одинаковы для полосы просмотра и дублирующих ее клавиш, имеет смысл предусмотреть единый обработчик сообщений от полосы просмотра. Для добавления клавиатурного интерфейса обработчик клавиатурного сообщения WM_CHAR может посылать в функцию окна сообщения полосы просмотра. Например, если обработчик сообщения WM_CHAR обнаружил, что вы нажали клавишу <PgUp>, он может послать в функцию окна сообщение WM_VSCROLL с кодом, равным SB_PAGEUP. Результат будет в точности такой же, как будто для свертки документа на одну страницу вверх вы воспользовались полосой просмотра, а не клавиатурой.

Такой подход позволяет локализовать всю логику свертки в обработчике сообщений полосы просмотра. При этом сильно упрощается процедура подключения клавиатурного интерфейса - обработчик клавиатурного сообщения WM_CHAR должен послать в функцию окна сообщение полосы просмотра, соответствующее коду нажатой клавиши. Но ему не надо дублировать действия обработчиков сообщений WM_VSCROLL и WM_HSCROLL.

Заметим, что если полоса просмотра имеет фокус ввода, она может сама обрабатывать клавиатурные сообщения. Ниже мы привели соответствие названий клавиш и кодов извещений:

Клавиша Код извещения
Перемещение курсора вверх SB_LINEUP или SB_LINELEFT (эти значения равны)
- // - влево SB_LINEUP или SB_LINELEFT
- // - вниз SB_LINEDOWN или SB_LINERIGHT
- // - вправо SB_LINEDOWN или SB_LINERIGHT
<Page Up> SB_PAGEUP или SB_PAGELEFT
<Page Down> SB_PAGEDOWN или SB_PAGERIGHT
В том случае, когда окно приложения создает две полосы просмотра, оно обычно не передает им фокус ввода.


При этом приложение должно обрабатывать клавиатурные сообщения самостоятельно, используя для стандартизации следующую таблицу соответствия клавиш и кодов извещений:

Клавиша Код извещения
Перемещение курсора вверх SB_LINEUP
- // - влево SB_LINELEFT
- // - вниз SB_LINEDOWN
- // - вправо SB_LINERIGHT
<Page Up> SB_PAGEUP
<Page Down> SB_PAGEDOWN
<Home> SB_SLIDERTRACK (установлена минимальная позиция)
<End> SB_SLIDERTRACK (установлена максимальная позиция)
<Ctrl + Home> SB_SLIDERTRACK (установлена минимальная позиция)
<Ctrl + End> SB_SLIDERTRACK (установлена максимальная позиция)
<Ctrl + Up> SB_SLIDERTRACK (установлена минимальная позиция). Используется для вертикальной полосы просмотра
<Ctrl + Down> SB_SLIDERTRACK (установлена максимальная позиция). Используется для вертикальной полосы просмотра
<F7> SB_PAGEUP
<F8> SB_PAGEDOWN
<Ctrl + Page Up> SB_PAGELEFT
<Ctrl + Page Down> SB_PAGERIGHT

Сообщения, поступающие от мыши


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



Сообщения WM_SYSCOMMAND и WM_HELP


Родительское окно может получать извещения от органов управления в форме сообщений WM_SYSCOMMAND или WM_HELP . Первое из этих сообщений приходит, например, когда пользователь нажимает комбинацию клавиш, соответствующую акселератору меню. Сообщение WM_HELP используется приложениями для работы со справочной системой, которую мы опишем в одной из следующих книг "Библиотеки системного программиста", посвященной программированию для IBM OS/2.

Что же касается параметров этих сообщений, то они полностью идентичны параметрам сообщения WM_COMMAND . Поэтому для извлечения этих параметров вы можете использовать описанную ранее макрокоманду COMMANDMSG .



Создание дочерних окон


Для того чтобы создать дочернее окно, вы можете воспользоваться все той же функцией WinCreateStdWindow , которую мы вызывали для создания главного окна приложения. Однако прежде чем вызывать эту функцию, вы должны подготовить функцию дочернего окна и зарегистрировать отдельный класс окна для дочернего окна.

Ниже регистрируется класс окна szWndClassChild, причем для дочернего окна будет использована функция окна с именем WndProcChild:

CHAR szWndClassChild[] = "WINTREECHILD"; fRc = WinRegisterClass (hab, szWndClassChild, (PFNWP)WndProcChild, 0, 0);

После того как класса дочернего окна зарегистрирован, можно создавать дочернее окно:

hWndChildFrame = WinCreateStdWindow (hWndFrame, WS_VISIBLE , &flFrameChildFlags, szWndClassChild, szChildTitle, 0, 0, ID_CHILDWND, &hWndChildClient);

Обратите внимание, что в качестве родительского используется окно с идентификатором hWndFrame. Кроме этого, дочернее окно имеет свой собственный идентификатор ID_CHILDWND.

Для того чтобы дочернее окно было видно, следует установить его начальное расположение и размеры. Это можно сделать при помощи функции WinSetWindowPos :

WinSetWindowPos (hWndChildFrame, HWND_TOP , 10, 10, 200, 200, SWP _ACTIVATE | SWP_SIZE | SWP_SHOW | SWP_MOVE );

Описанная методика создания дочерних окон была использована нами в приложении WINTREE, исходные тексты которого вы скоро увидите.



Создание главного окна приложения


После создания очереди сообщений и регистрации класса окна все готово для того, чтобы приступить к созданию главного окна приложения.

В общем случае окно создается функцией WinCreateWindow , которую мы рассмотрим немного позже. Для создания главного окна приложения удобнее всего воспользоваться другой функцией, которая называется WinCreateStdWindow .

Для того чтобы вам была понятнее описанная ниже процедура создания главного окна, вспомним, что на самом деле окно приложения, которое видит пользователь, состоит из нескольких окон. Остановимся на этом подробнее.

Главное окно приложения называется Frame Window и на его поверхности расположены все остальные окна, а именно: окно пиктограммы системного меню, заголовок окна, вертикальные и горизонтальные полосы просмотра (если они есть), кнопки минимизации и максимизации окна, окно меню приложения, а также клиентское окно Client Window , внутри которого приложение обычно что-нибудь рисует.

Когда ранее мы говорили про создание главного окна приложения, то несколько упрощали ситуацию. На самом деле необходимо создать окна Frame Window , Client Window , а также другие окна, которые служат в качестве органов управления - системное меню, полосы просмотра и т. д.

К счастью, вы можете создать все эти окна при помощи одного вызова функции WinCreateStdWindow . Задавая соответствующим образом параметры этой функции, вы можете изменять внешний вид окна приложения, создавая (или не создавая) в окне Frame Window те или иные элементы управления.

Особое значение имеет окно Client Window , которое мы будем также называть клиентским окном приложения. Именно в этом окне приложение выполняет рисование, используя его как рабочее (хотя при необходимости приложение может рисовать в любом месте окна Frame Window или даже в любом месте на поверхности рабочего стола графической оболочки Workplace Shell).

Окно Frame Window , как и окна, соответствующие стандартным органам управления (таким как меню или кнопки), создаются на базе классов окна, предопределенных в системе Presentation Manager.


Что же касается окна Client Window , то это окно создается всегда на базе класса, зарегистрированного приложением. Приложение должно определить функцию окна, которая будет обрабатывать сообщения, предназначенные окну Client Window.

Для упрощения клиентское окно Client Window иногда называют главным окном приложения, хотя вы должны понимать, что настоящим главным окном является окно Frame Window . Функция окна Frame Window находится внутри системы Presentation Manager и вам не надо о ней беспокоиться.

Ниже мы привели фрагмент кода, в котором создается главное окно приложения. Здесь предполагается, что создание очереди сообщений и регистрация класса окна уже выполнены.

#define ID_APP_FRAMEWND 1 HWND hWndFrame; HWND hWndClient; ULONG flFrameFlags = FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON;

CHAR szAppTitle[] = "My First OS/2 Application"; CHAR szWndClass[] = "MYWINDOW";

hWndFrame = WinCreateStdWindow (HWND_DESKTOP, WS_VISIBLE , &flFrameFlags, szWndClass, szAppTitle, 0, 0, ID_APP_FRAMEWND, &hWndClient);

if(hWndFrame == NULLHANDLE) { WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); }

Ниже мы привели прототип функции WinCreateStdWindow с кратким описанием параметров:

HWND WinCreateStdWindow ( HWND hwndParent, // идентификатор родительского окна ULONG flStyle, // стиль окна Frame Window PULONG pflCreateFlags, // флаги создания // окна Frame Window PSZ pszClassClient, // имя класса клиентского окна PSZ pszTitle, // заголовок окна ULONG flStyleClient, // стиль клиентского окна HMODULE Resource, // идентификатор ресурсов ULONG ulId, // идентфикатор окна Frame Window PHWND phwndClient); // идентификатор клиентского окна

В качестве первого параметра hwndParent вы должны передать этой функции идентификатор родительского окна. В следующей главе мы рассмотрим подробнее "семейные" отношения в мире окон Presentation Manager. Сейчас ограничимся таким определением: родительское окно - это то окно, которое создало данное окно и потому связано с ним некоторыми "отношениями".


Например, дочернее окно всегда отображается внутри родительского, не выходя за его границы.

В нашем случае в качестве родительского выступает окно рабочего стола, которое имеет идентификатор HWND_DESKTOP , определенный в файле os2.h.

Параметр flStyle определяет стиль окна Frame Window . В качестве стиля вы можете использовать комбинацию перечисленных ниже констант, объединенных при помощи логической операции ИЛИ.

Стиль Описание
WS_SYNCPAINT Синхронное обновление окна. Этот стиль установлен для тех окон, которые созданы на базе класса окна со стилем CS_SYNCPAINT
WS_ANIMATE Включение режима анимации при открывании и закрывании окна. Если этот режим включен, то при изменении размеров окна пользователю будет казаться, что эти размеры плавно изменяются. Заметим, что если анимация окна отключена в блокноте свойств System-Settings, то этот стиль будет проигнорирован
WS_CLIPCHILDREN Если указан этот стиль, то область, занимаемая дочерними окнами, не перерисовывается
WS_CLIPSIBLINGS Если указан этот стиль, то область, занимаемая окнами, имеющими общих родителей, не перерисовывается
WS_DISABLED Окно заблокировано
WS_MAXIMIZED Окно создается максимизированным
WS_MINIMIZED Окно создается минимизированным
WS_PARENTCLIP Если указан этот стиль, окно не рисует вне занимаемой им прямоугольной области
WS_SAVEBITS В момент отображения окна выполняется сохранение графического изображения области под окном, имеющим этот стиль
WS_VISIBLE После создания окно становится видимым. Если этот стиль не указан, то окно создается невидимым, даже если оно не закрыто другими окнами
В нашем примере мы указали стиль WS_VISIBLE , поэтому сразу после создания окна вы увидите его на экране.

Следующий параметр называется pflCreateFlags. Он представляет собой указатель на переменную типа ULONG, в которую перед вызовом функции WinCreateStdWindow необходимо записать флаги создания окна Frame Window .

Ниже мы привели список возможных значений флагов. Эти значения можно объединять при помощи логической операции ИЛИ.



Флаг Описание
FCF_TITLEBAR Если указан этот флаг, в окне Farme Window будет создано окно заголовка
FCF_SYSMENU Будет создано системное меню
FCF_MENU -//- стандартное меню
FCF_MINMAX -//- кнопки минимизации и максимизации
FCF_MINBUTTON -//- кнопка минимизации
FCF_MAXBUTTON -//- кнопка максимизации
FCF_VERTSCROLL -//- вертикальная полоса просмотра
FCF_HORZSCROLL -//- горизонтальная полоса просмотра
FCF_SIZEBORDER -//- рамка для изменения размеров окна
FCF_BORDER Вокруг окна будет нарисована тонкая рамка
FCF_DLGBORDER Вокруг окна будет нарисована рамка, которая используется в стандартных диалоговых панелях
FCF_ACCELTABLE Из ресурса приложения, идентификатор которого указан в параметре Resource функции WinCreateStdWindow , будет загружена таблица акселераторов (будет рассмотрена позже)
FCF_ICON С создаваемым окном связывается пиктограмма, которая идентифицируется параметром Resource функции WinCreateStdWindow . Эта пиктограмма автоматически загружается при создании окна и также автоматически удаляется при уничтожении окна
FCF_SHELLPOSITION Размеры и расположение создаваемого окна определяет оболочка Workplace Shell (а не приложение)
FCF_SYSMODAL Создается системное модальное окно (об этом мы расскажем позже)
FCF_NOBYTEALIGN Если установлен этот флаг, при создании окна не выполняется оптимизация выравнивания соответствующих структур данных в оперативной памяти, которая в некоторых случаях могла бы привести к ускорению процесса отображения окна
FCF_TASKLIST Если установлен этот флаг, к заголовку окна добавляется название программы. Полученная таким образом строка используется при отображении списка задач, запущенных в операционной системе IBM OS/2
FCF_NOMOVEWITHOWNER При перемещении окна, которое является владельцем окна с установленным флагом FCF_NOMOVEWITHOWNER, последнее не должно перемещаться
FCF_AUTOICON Если окно минимизировано, ему не посылается сообщение WM_PAINT , предназначенное для инициирования процедуры перерисовки содержимого окна. Этот флаг оказывает благоприятное влияние на производительность системы
FCF_STANDARD Эквивалент следующей комбинации флагов:

FCF_TITLEBAR | FCF_SYSMENU |

FCF_MINBUTTON | FCF_TASKLIST |

FCF_MAXBUTTON |

FCF_SIZEBORDER | FCF_ICON |

FCF_ACCELTABLE | FCF_MENU |

FCF_SHELLPOSITION

Если указан флаг FCF_STANDARD, следует определить все необходимые ресурсы: пиктограмму приложения, меню, таблицу акселераторов
<


Обратите внимание, что в качестве параметра pflCreateFlags вы должны передать функции WinCreateStdWindow указатель на слово, содержащее флаги, а не само слово флагов.

Продолжим изучение параметров функции WinCreateStdWindow .

Через параметр pszClassClient вы должны передать функции имя зарегистрированного ранее класса окна Client Window . Напомним, что эта регистрация выполняется с помощью функции WinRegisterClass .

Параметр pszTitle передает функции WinCreateStdWindow указатель на текстовую строку, содержащую заголовок окна. Параметр pszTitle игнорируется, если не указан флаг FCF_TITLEBAR .

Параметр flStyleClient определяет стиль клиентского окна Client Window . Он игнорируется, если параметр pszClassClient содержит указатель на пустую текстовую строку.

Теперь о параметре Resource. Этот параметр указывает идентификатор модуля, содержащий ресурсы приложения. На данном этапе мы будем указывать для этого параметра нулевое значение. При этом загрузка ресурсов будет выполняться из EXE-файла приложения.

Что такое ресурсы приложения ?

Это данные, которые могут быть физически расположены в загрузочном EXE-файле приложения или в DLL-файле библиотеки динамической загрузки. Вы можете таким образом создать и сохранить в загрузочном файле приложения пиктограммы, меню, диалоговые панели, текстовые строки и другие типы данных, в том числе произвольные данные. При запуске приложения те ресурсы, которые не нужны, могут оставаться на диске. Они не будут отнимать у приложений дефицитную оперативную память. В то же время как только они потребуются, операционная система загрузит их в память автоматически.

Более подробное описание приемов работы с ресурсами мы приведем позже. В нашем первом приложении определен только один ресурс - пиктограмма.

Следующий параметр, который вы должны указать для функции WinCreateStdWindow , это параметр ulId. Он задает идентификатор окна Frame Window . Все ресурсы, относящиеся к одному окну Frame Window, должны иметь одинаковый идентификатор.Вы можете использовать любое значение в диапазоне от 0 до 0xFFFF.

Обращаем ваше внимание, что идентификатор окна, полученный от функции WinCreateStdWindow (или WinCreateWindow), и идентификатор, передаваемой этим функциям в качестве параметра ulId - разные по смыслу вещи. Первый из них используется для посылки сообщений окну или выполнения других операций, таких, например, как блокирование. Идентификатор ulId играет важную роль при обработке сообщений, поступающих от данного окна в родительское окно и позволяет определить, от какого окна пришло сообщение. Подробнее об использовании этого идентификатора вы узнаете из главы, посвященной органам управления.

И, наконец, последний параметр phwndClient должен содержать указатель на переменную типа HWND, в которую будет записан идентификатор созданного клиентского окна Client Window . Обращаем внимание, что здесь нужно использовать именно указатель, а не имя переменной.


Создание круглого регулятора


Для создания круглого регулятора вы можете воспользоваться функцией WinCreateWindow, как это показано ниже:

hWndCirc1 = WinCreateWindow(hWnd, WC_CIRCULARSLIDER , "Красный", WS_VISIBLE | CSS_NOTEXT, 0, 0, 0, 0, hWnd, HWND_TOP, CIRCSLD1_ID, NULL, NULL);

Здесь мы привели фрагмент обработчика сообщения WM_CREATE функции окна приложения Client Window, имеющего идентификатор hWnd. Размеры и расположение регулятора будут определены позже при обработке сообщения WM_SIZE. Как вы можете заметить, регулятор создается точно также, как и любой другой описанные нами ранее орган управления.

Внешний вид круглой ручки управления определяется стилем регулятора. Ниже приведены значения стилей, которые вы можете комбинировать при помощи логической операции ИЛИ:

Стиль Описание
CSS_360 С помощью этого стиля вы можете создать ручку управления, которая будет поворачиваться на 360 градусов. При этом будут автоматически установлены стили CSS_NONUMBER и CSS_NOBUTTON
CSS_CIRCULARVALUE Вместо риски на поверхности ручки будет создана круглая метка в виде небольшого углубления круглой формы
CSS_MIDPOINT Начальная и конечная риска на шкале регулятора будет иметь увеличенную длину
CSS_NOBUTTON Если указан этот стиль, в окне органа управления не будет кнопок + и -
CSS_NONUMBER Отмена отображения цифрового значения текущей позиции
CSS_NOTEXT Отмена отображения подписи под регулятором
CSS_POINTSELECT Если указан стиль CSS_POINTSELECT, изменяется способ управления регулятором при помощи мыши. Если установить курсор мыши в новую позицию и сделать щелчок левой клавишей мыши, регулятор скачкообразно изменит свою текущую позицию таким образом, что она будет совпадать с позицией курсора
CSS_PROPORTIONALTICKS Длина рисок на шкале регулятора будет вычисляться в процентах от радиуа регулятора. Этот способ удобен при работе видеоадаптера в режимах с низким разрешением



Создание очереди сообщений


После регистрации в системе Presentation Manager приложение должно создать очередь сообщений. Для этого необходимо воспользоваться функцией WinCreateMsgQueue . Соответствующий фрагмент кода показан ниже.

#define INCL_WIN #include <os2.h> int main () { HMQ hmq; HAB hab;

hab = WinInitialize (0); hmq = WinCreateMsgQueue (hab, 0); if(hmq == NULLHANDLE) { WinTerminate (hab); return(-1); } . . . // Строки исходного текста приложения . . . WinDestroyMsgQueue (hmq); WinTerminate (hab); return(0); }

Прототип функции WinCreateMsgQueue представлен ниже:

HMQ WinCreateMsgQueue (HAB hab, LONG lQueuesize);

Через первый параметр этой функции необходимо передать идентификатор Anchor-block , полученный при регистрации от функции WinInitialize . Второй параметр определяет размер очереди сообщений, причем если указано нулевое значение, используется размер, принятый по умолчанию (10 сообщений).

В случае успеха функция возвращает идентификатор созданной очереди сообщений, который имеет тип HMQ , а при ошибке - значение NULLHANDLE.

Обратите внимание, что если нам не удалось создать очередь сообщений, то перед аварийным завершением работы приложение дожно вызвать функцию WinTerminate .

Для уничтожения очереди сообщений следует использовать функцию WinDestroyMsgQueue , передав ей в качестве параметра идентификатор уничтожаемой очереди. В случае успеха функция возвращает значение TRUE, при ошибке - FALSE.

Если приложение создает несколько задач, оно может также создать для каждой задачи свою очередь сообщений. В этом случае перед завершением работы приложения необходимо уничтожить функцией WinDestroyMsgQueue все созданные очереди сообщений.



Создание полосы просмотра


Как мы уже говорили, существует два способа создания полос просмотра в окне приложения.

Во-первых, вы можете создать полосу просмотра с помощью функции WinCreateWindow, указав предопределенный класс окна WC_SCROLLBAR . Этот способ аналогичен тому, что используется при создании кнопок или статических органов управления. Во-вторых, при создании окна на базе своего собственного класса с помощью функции WinCreateStdWindow вы можете указать, что окно должно иметь горизонтальную, вертикальную или обе полосы просмотра.



Стили кнопок


Параметр flStyle определяет стиль класса окна, влияющий на поведение и внешний вид окон, создаваемых на базе этого класса. Для кнопок и переключателей помимо стилей с префиксом WS_, описанных в начале нашей книги, можно указывать следующие стили (некоторые стили можно комбинировать при помощи логической операции ИЛИ, о чем будет сказано дополнительно):

Стиль Описание
BS_PUSHBUTTON Кнопка в виде прямоугольника с текстом. Когда пользователь нажимает эту кнопку, родительское окно получает извещающее сообщение
BS_DEFAULT Кнопка с толстой рамкой. В диалоговых панелях вместо этой кнопки пользователь может нажать клавишу <Enter>. Этот стиль можно использовать вместе со стилями BS_PUSHBUTTON и BS_USERBUTTON
BS_CHECKBOX Переключатель квадратной формы, который может находиться в одном из двух состояний: включенном (внутри квадрата отображается галочка) или выключенном (без галочки). Справа от квадрата находится подпись (текст заголовка)
BS_AUTOCHECKBOX Аналогично предыдущему, но внешний вид при переключении изменяется автоматически
BS_RADIOBUTTON Аналогично BS_CHECKBOX, но переключатель имеет круглую форму. Во включенном состоянии внутри круга отображается жирная точка
BS_AUTORADIOBUTTON Аналогично предыдущему, но внешний вид при переключении изменяется автоматически. Обычно используется в диалоговых панелях в составе группы аналогичных переключателей, при этом во время включения одного из переключателей остальные автоматически выключаются
BS_AUTOSIZE Размер кнопки, создаваемой с этим стилем, изменяется автоматически таким образом, чтобы надпись всегда помещалась на ней целиком. При создании окна с этим стилем функции WinCreateWindow в качестве параметров cx или cy можно указывать значение -1
BS_3STATE Прямоугольная кнопка с автоматическим переключением, которая может находиться в трех состояниях: внутри квадрата отображается маленький черный квадратик (включен), черный квадратик не отображается (выключен), переключатель отображается серым цветом (переключатель не активен)
BS_AUTO3STATE Аналогично предыдущему, но изображение изменяется автоматически при переключении
BS_BITMAP Вместо текста на поверхности кнопки рисуется растровое изображение. Этот стиль можно использовать только вместе со стилем BS_PUSHBUTTON
BS_HELP Когда пользователь нажимает кнопку с этим стилем, родительское окно вместо сообщения WM_COMMAND получает сообщение с кодом WM_HELP . Этот стиль можно использовать вместе со стилем BS_PUSHBUTTON
BS_ICON Вместо текста на поверхности кнопки рисуется пиктограмма. Этот стиль можно использовать только вместе со стилем BS_PUSHBUTTON
BS_MINIICON Аналогично предыдущему, но на поверхности кнопки можно размещать пиктограммы уменьшенного размера
BS_NOBORDER Кнопка не имеет рамки. Этот стиль можно использовать только вместе со стилем BS_PUSHBUTTON
BS_NOCURSORSELECT Круглая кнопка, которая не выбирается после получения фокуса ввода в результате использования клавиш перемещения курсора или табуляции (стиль используется в диалоговых панелях) . Этот стиль можно использовать только вместе со стилем BS_AUTORADIOBUTTON
BS_NOPOINTERFOCUS Круглая кнопка, которая не выбирается щелчком мыши. В результате курсор останется в том поле, в котором требуется ввести информацию (стиль используется в диалоговых панелях). Этот стиль можно комбинировать со всеми другими стилями
BS_SYSCOMMAND Вместо сообщения WM_COMMAND кнопка с этим стилем посылает родительскому окну извещающее сообщение WM_SYSCOMMAND . Этот стиль можно использовать только вместе со стилем BS_PUSHBUTTON
BS_TEXT На поверхности кнопки может отображаться текст вместе с пиктограммой (обычной или уменьшенной), или с растровым изображением. Этот стиль можно использовать вместе со стилем BS_PUSHBUTTON. Кроме того, необходимо указать стили BS_BITMAP, BS_ICON или BS_MINIICON
BS_USERBUTTON Внешний вид кнопки определяется приложением



Стили полосы просмотра


При создании полосы просмотра функцией WinCreateWindow вы можете указать следующие стили:

Стиль Описание
SBS_HORZ Создается горизонтальная полоса просмотра
SBS_VERT Создается вертикальная полоса просмотра
SBS_AUTOTRACK При перемещении движка полосы просмотра, созданной с использованием стиля SBS_AUTOTRACK, перемещается полное изображение движка. Если же этот стиль не указан, перемещается только изображение прямоугольного контура движка, а сам движок будет нарисован в новой позиции только после завершения пользователем операции перемещения
SBS_THUMBSIZE Этот стиль используется для вычисления размера движка



Структура приложения Presentation Manager


Теперь, после того как вы познакомились с некоторыми наиболее важными понятиями, мы расскажем о структуре простейшего приложения Presentation Manager и приведем соответствующие фрагменты исходного текста (в дальнейшем для сокращения мы будем называть приложение Presentation Manager просто приложением, а приложение, предназначенное для текстового режима - текстовым приложением).



Текущее значение позиции


С помощью сообщения CSM_QUERYVALUE приложение может определить текущее значение позиции. Для этого через параметр mp1 вместе с этим сообщением необходимо передать указатель на переменную типа USHORT, в которую будет записано искомое значение.



Трансляция исходных текстов приложения


Если вы будете транслировать исходные тексты приложений, приведенных в нашей книге с помощью системы разработки Borland C++ for OS/2, то сможете воспользоваться навыками, полученными при программировании в среде MS-DOS или Microsoft Windows. Все, что вам нужно сделать, это создать новый проект, добавив в него файлы с именами *.c, *.rc и *.def. Затем вы можете транслировать и собирать проект обычным образом. К сожалению, из-за недостатка места в нашей книге мы не сможем описать в деталях этот процесс, однако он не сложен и мы уверены, что вы справитесь с этим сами, точно также, как и с установкой среды разработки Borland C++ for OS/2.

Что же касается системы разработки IBM VisualAge C++, то мы подготовили mak-файл, с помощью которого вы сможете собрать проект из командного приглашения OS/2. Этот способ хотя и не слишком удобен по сравнению с использованием интегрированных систем разработки, позволит вам работать с компьютером, оснащенным небольшим объемом оперативной памяти или медленным процессором. Файл называется mywindow.mak (листинг 1.5).

Для трансляции приложения с помощью этого файла вы должны сделать каталог, содержащий исходные тексты приложения, текущим и затем запустить программу nmake, указав ей в качестве параметра имя файла mywindow.mak:

c:\os2prg\src\mywindow>nmake mywindow.mak

Листинг 1.5. Файл mywindow\mywindow.mak

PRJ = mywindow CC = icc /Ti /c /Ge /Gd- /Se /Re /ss /Gm+ LFLAGS = /NOFREE /NOE /NOD /ALIGN:16 /EXEPACK /M /BASE:0x10000 LINK = ILINK $(LFLAGS) LIBS = CPPOM30 + OS2386 HEADERS = $(PRJ).h ALL_OBJ = $(PRJ).obj

.SUFFIXES: .rc .res .obj .lst .c

.c.lst: $(CC) -Fc$*.lst -Fo$*.obj $*.c

.c.obj: $(CC) -Fo$*.obj $*.c

.rc.res: rc -r $*.rc

all: $(PRJ).exe

$(PRJ).l: $(PRJ).mak echo $(ALL_OBJ) > $(PRJ).l echo $(PRJ).exe >> $(PRJ).l echo $(PRJ).map >> $(PRJ).l echo $(LIBS) >> $(PRJ).l echo $(PRJ).def >> $(PRJ).l

$(PRJ).res: $(PRJ).rc $(PRJ).ico $(PRJ).h

$(PRJ).obj: $(PRJ).c $(HEADERS)

$(PRJ).exe: $(ALL_OBJ) $(PRJ).def $(PRJ).l $(PRJ).res $(LINK) @$(PRJ).l rc -p -x $(PRJ).res $(PRJ).exe



Удочерение окна


При необходимости вы можете назначить для дочернего окна нового родителя, вызвав функцию WinSetParent .



Управление кнопками


Приложение при необходимости может управлять состоянием кнопок, блокируя или разблокируя их, а также изменяя состояние кнопок.

Для блокировки кнопки необходимо использовать функцию WinEnableWindow, которая имеет следующий прототип:

BOOL WinEnableWindow( HWND hwnd, // идентификатор окна BOOL fNewEnabled); // новое состояние

Через параметр hwnd вы должны передать этой функции идентификатор окна кнопки. Обращаем внимание, что это идентификатор окна кнопки, который возвращается функцией WinCreateWindow при создании кнопки, а не идентификатор кнопки, передаваемый этой же функции в качестве одного из параметров.

Параметр fNewEnabled определяет новое состояние кнопки. Если он равен TRUE, кнопка разблокируется, а если FALSE - блокируется.

Заблокированную кнопку нельзя нажать ни мышью, ни при помощи клавиатуры (если такая кнопка имеет фокус ввода). Надпись на заблокированной кнопке отображается серым цветом.

Когда состояние кнопки меняется, родительское окно получает сообщение WM_ENABLE. Параметр mp1 этого сообщения указывает новое состояние кнопки и может принимать те же значения, что и описанный только что параметр fNewEnabled функции WinEnableWindow. Параметр mp2 не используется.

Сообщение WM_ENABLE - это извещение родительского окна об изменении состояния кнопки. В ответ на это извещение родительское окно может выполнить какие-либо действия, связанные с тем, что данная кнопка оказалась заблокированной или наоборот, разблокированной.



Управление курсором мыши с помощью клавиатуры


Для того чтобы можно было работать с приложением даже в том случае, если к компьютеру не подключена мышь (или подключена, но неисправна), имеет смысл предусмотреть дублирование функций мыши при помощи клавиатуры. Это можно сделать, обрабатывая соответствующим образом сообщение WM_CHAR , поступающее в фукнцию окна от клавиатуры. Параметры этого сообщения мы рассмотрели раньше, в главе, посвященной клавиатуре.

Идея управления положением курсора мыши при помощи клавиатуры заключается в том, чтобы в ответ на сообщение WM_CHAR от клавиш перемещения курсора определять, а затем изменять положение курсора мыши.

Определить текущие экранные координаты курсора мыши можно при помощи функции WinQueryPointerPos , например, так:

POINTL ptl; WinQueryPointerPos (HWND_DESKTOP, &ptl);

Эта функция записывает в структуру ptl координаты курсора мыши в системе координат, связанной с окном рабочего стола Desktop Window . Если нужно сделать так, чтобы при управлении с помощью клавиатуры курсор мыши не выходил за рамки окна приложения, экранные координаты курсора мыши следует преобразовать в оконные. Нужные преобразования можно сделать при помощи функции WinMapWindowPoints , передав ей в качестве первого параметра идентификатор окна рабочего стола, а в качестве второго - идентификатор окна приложения:

WinMapWindowPoints (HWND_DESKTOP, hWnd, &ptl, 1);

Подробнее мы рассмотрим эту функцию в одной из следующих книг "Библиотеки системного программиста" в главе, посвященной графическому интерфейсу Presentation Manager.

После преобразования координат обработчик сообщения WM_CHAR должен изменить поля x и y структуры ptl, увеличив их или уменьшив, в зависимости от того, какая клавиша была нажата. Затем нужно выполнить обратное преобразование оконных координат в экранные и установить курсор мыши в новую позицию.

Первая задача решается все той же функцией WinMapWindowPoints , однако теперь через первый параметр этой функции передается идентификатор окна приложения, а через второй - идентификатор окна рабочего стола:

WinMapWindowPoints (hWnd, HWND_DESKTOP, &ptl, 1);

Для решения второй задачи (установки курсора мыши в новую позицию) следует вызвать функцию WinSetPointer Pos , передав ей через первый параметр идентификатор окна рабочего стола, а через второй и третий - новые экранные координаты курсора мыши по оси X и Y, соответственно:

WinSetPointer Pos (HWND_DESKTOP, (SHORT) ptl.x, (SHORT) ptl.y);



Управление полосой просмотра


Если вам нужно установить только новую позицию движка, оставив прежним диапазон изменения значений, воспользуйтесь сообщением SBM_SETPOS :

WinSendMsg(hwndYScroll, SBM_SETPOS, (MPARAM)10, (MPARAM)NULL);

Новое значение позиции вы должны передать с параметром mp1 этого сообщения. Параметр mp2 не используется и должен быть равен нулю.



Установка параметров круглого регулятора


После создания круглого регулятора вы должны определить диапазон изменения значений позиции, установить начальную позицию, шаг изменения позиции и шаг размещения меток. Для выполнения всех этих операций необходимо послать окну регулятора соответствующие сообщения.



Установка позиции курсора мыши


Ваше приложение может не только определить текущую позицию курсора мыши, но и установить курсор в новую позицию. Для этого следует использовать функцию WinSetPointer Pos , прототип которой приведен ниже:

BOOL WinSetPointer Pos ( HWND hwndDeskTop, // идентификатор окна Desktop Window LONG lx, // новая экранная координата X LONG ly); // новая экранная координата Y

Заметим, что для установки курсора мыши используются экранные координаты.



Установка размеров и расположения кнопки


Начальные размеры и расположение кнопки задаются при ее создании функцией WinCreateWindow . Однако если кнопка расположена не в диалоговой панели, а на поверхности обычного окна приложения, размещение кнопок следует выполнять при обработке сообщения WM_SIZE , например, так:

case WM_SIZE : { WinSetWindowPos (hWndButton, HWND_TOP , 10, 10, 200, 50, SWP _SIZE | SWP_MOVE | SWP_ZORDER );

WinInvalidateRect (hWnd, NULL, TRUE); return 0; }

Пользователь может изменять размеры окна, при этом, если не заботиться о стирании содержимого окна, он увидит в нем несколько одинаковых кнопок. Функция WinInvalidateRect в приведенном выше фрагменте кода вызывает перерисовку окна hWnd приложения, создавшего в этом окне кнопку hWndButton.



Виртуальный код клавиши


Виртуальный код нажатой клавиши имеет 16 разрядов и передается в битах 16 - 31 параметра mp2. Для обычных символьных клавиш виртуальных код равен нулю. Функциональные клавиши и клавиши серого цвета имеют виртуальные коды, приведенные ниже:

Клавиша Виртуальный код Значение виртуального кода
Левая клавиша мыши VK_BUTTON1 0x01
Правая клавиша мыши VK_BUTTON2 0x02
Средняя клавиша мыши VK_BUTTON3 0x03
<Break> VK_BREAK 0x04
Забой VK_BACKSPACE 0x05
Табуляция <Tab> VK_TAB 0x06
Обратная табуляция VK_BACKTAB 0x07
Переход на новую строку, клавиша <Enter> основной клавиатуры VK_NEWLINE 0x08
Левая или правая клавиша сдвига <Shift> VK_SHIFT 0x09
Левая или правая клавиша <Control> VK_CTRL 0x0A
Левая клавиша <Alt> VK_ALT 0x0B
Правая клавиша <Alt> VK_ALTGRAF 0x0C
<Pause> VK_PAUSE 0x0D
<Caps Lock> VK_CAPSLOCK 0x0E
<Esc> VK_ESC 0x0F
Пробел VK_SPACE 0x10
Переход на следующую страницу <PgUp> VK_PAGEUP 0x11
Переход на предыдущую страницу <PgUp> VK_PAGEDOWN 0x12
<End> VK_END 0x13
<Home> VK_HOME 0x14
Перемещение курсора влево <Left> VK_LEFT 0x15
Перемещение курсора вверх <Up> VK_UP 0x16
Перемещение курсора вправо <Right> VK_RIGHT 0x17
Перемещение курсора вниз <Down> VK_DOWN 0x18
Печать экрана VK_PRINTSCRN 0x19
Вставка <Insert> VK_INSERT 0x1A
Удаление <Delete> VK_DELETE 0x1B
<Scroll Lock> VK_SCRLLOCK 0x1C
<Num Lock> VK_NUMLOCK 0x1D
<Enter> на дополнительной (цифровой) клавиатуре VK_ENTER 0x1E
Клавиша запроса системы VK_SYSRQ 0x1F
Функциональная клавиша <F1> V K_F1 0x20
- // - <F2> VK_F2 0x21
- // - <F3> VK_F3 0x22
- // - <F4> VK_F4 0x23
- // - <F5> VK_F5 0x24
- // - <F6> VK_F6 0x25
- // - <F7> VK_F7 0x26
- // - <F8> VK_F8 0x27
- // - <F9> VK_F9 0x28
- // - <F10> VK_F10 0x29
- // - <F11> VK_F11 0x2A
- // - <F12> VK_F12 0x2B
- // - <F13> VK_F13 0x2C
- // - <F14> VK_F14 0x2D
- // - <F15> VK_F15 0x2E
- // - <F16> VK_F16 0x2F
- // - <F17> VK_F17 0x30
- // - <F18> VK_F18 0x31
- // - <F19> VK_F19 0x32
- // - <F20> VK_F20 0x33
- // - <F21> VK_F21 0x34
- // - <F22> VK_F22 0x35
- // - <F23> VK_F23 0x36
- // - <F24> VK_F24 0x37
Завершение перемещения VK_ENDDRAG 0x38
Вызов меню VK_MENU VK_F10

Заметьте, что в приведенном выше списке есть коды клавиш, которые отсутствуют на стандартной клавиатуре компьютера, совместимого с IBM PC. В этом нет ничего удивительного, так как в механизм виртуальных кодов клавиш заложена возможность работы и на других платформах.

Виртуальный код клавиши можно получить из параметра mp2 при помощи макрокоманды SHORT2FROMMP :

nVirt = SHORT2FROMMP (mp2);



В нашей новой книге мы


В нашей новой книге мы продолжим знакомство с операционной системой IBM OS/2 Warp, которая находится на переднем крае современных технологий наряду с такими операционными системами, как Microsoft Windows NT и Microsoft Windows 95. Напомним, что это знакомство началось в 20 томе нашей серии книг "Библиотека системного программиста", в котором мы рассмотрели принципы, положенные в основу IBM OS/2 Warp, рассказали о том как устанавливать, настраивать и использовать эту операционную систему. Мы также рассмотрели ее сетевые возможности.
Теперь, когда вы уже умеете работать в среде IBM OS/2 Warp как пользователь, настало время научиться создавать приложения для этой операционной системы.
Скажем сразу, что для IBM OS/2 Warp можно создавать приложения двух разных типов. Первый тип приложений в основном предназначен для работы в текстовом режиме (текстовые приложения), второй - в графическом (приложения Presentation Manager ).
Если вы раньше программировали только для операционной системы MS-DOS , то вы уже создавали текстовые приложения. В составе MS-DOS нет удобных средств для организации текстового или графического вывода, поэтому большинство программ MS-DOS работают напрямую с видеопамятью, физически расположенной на плате видеоадаптера.
Операционная система IBM OS/2 Warp использует защищенный режим работы процессора, причем в большинстве случаев приложениям запрещается обращаться к портам периферийных устройств и видеопамяти в обход соответствующих драйверов. Это ограничение, однако, с лихвой компенсируется удобными и быстродействующими функциями, доступными приложениям в рамках программного интерфейса IBM OS/2 Warp. Поэтому в целом программирование экранного вывода для текстового режима IBM OS/2 Warp заметно проще, чем для операционной системы MS-DOS .
Тем не менее, текстовый режим не удовлетворяет современным требованиям, предъявляемым к интерфейсу пользователя. Практически все приложения, составленные для новых операционных систем, работают в графическом режиме, реализуя графический интерфейс пользователя.
Поэтому для нас в большей степени представляет интерес создание графических приложений, которые работают с использованием программного интерфейса системы Presentation Manager (эта система входит в состав IBM OS/2 Warp и реализует графический интерфейс пользователя). Такие приложения мы в дальнейшем для сокращения будем называть приложениями Presentation Manager.
Структура приложений Presentation Manager в корне отличается от привычной вам структуры программ MS-DOS , причем главным образом за счет того, что приложения Presentation Manager являются событийно-управляемыми (также, как и приложения Microsoft Windows).
Если вы программировали только для MS-DOS и никогда не создавали приложений для операционной системы Microsoft Windows, то самое трудное для вас будет перейти от обычного, линейного программирования к событийно-управляемому. Однако мы постараемся по возможности облегчить такой переход. Для этого мы рассмотрим очень подробно принципы событийно-управляемого программирования на конкретном примере исходных текстов простейшего приложения Presentation Manager. Если же вы ранее программировали для операционной системы Microsoft Windows, то создание приложений Presentation Manager не вызовет у вас затруднений.
Другая потенциальная трудность может возникнуть при изучении программного интерфейса оболочки Presentation Manager, насчитывающей сотни функций. Однако вам не нужно изучать их все сразу. Более того, большинство функций вам так и не понадобится. В любом случае при необходимости вы можете воспользоваться электронным справочником по функциям, встроенном во все системы разработки приложений.
Что вам потребуется для работы?
Прежде всего, разумеется, вы должны установить операционную систему IBM OS/2 Warp или IBM OS/2 Warp Connect версии 3.0. Подробные рекомендации по установке и настройке вы найдете в 20 томе "Библиотеки системного программиста", который называется "Операционная система IBM OS/2 Warp".
Для трансляции исходных текстов приложений, приведенных в нашей книге, вы можете воспользоваться такими системами разработки, как Borland C++ for OS/2 версии 2.0, Watcom C версии 10.0 и, конечно же, наиболее совершенным средством разработки приложений для IBM OS/2, созданным в самой IBM - системой VisualAge C++ версии 3.0.


Если вы только начинаете изучать программирование в среде IBM OS/2 Warp и привыкли к таким системам разработки, как Borland C++ или Microsoft Visual C++, имеет смысл воспользоваться Borland C++ for OS/2 версии 2.0 или Watcom C. Особенно, если в вашем компьютере установлено только 8 Мбайт оперативной памяти.
Система VisualAge C++ версии 3.0 требует наличия в компьютере по крайней мере 16 Мбайт оперативной памяти, причем для реализации всех возможностей рекомендуется установить не менее 24 Мбайт оперативной памяти. Кроме того, эта система при полной установке занимает около 170 Мбайт дискового пространства. Поэтому мы рекомендуем вам устанавливать систему VisualAge C++ только в том случае, если в вашем компьютере имеется не менее 16 Мбайт оперативной памяти, процессор Pentium и жесткий диск объемом 540 - 1000 Мбайт.
Будет неплохо, если вы оснастите свой компьютер устройством чтения компакт-дисков. Последнее просто необходимо для установки VisualAge C++, так как эта система поставляется только на компакт-дисках.
Что же касается видеоадаптера, то мы рекомендуем видеоадаптер SVGA с объемом видеопамяти не менее 1 Мбайт, что позволит вам изучать программирование для режимов с высоким цветовым разрешением, а также для режимов, использующих цветовые палитры.
Хотя в данной книге мы не планируем рассказывать вам о создании приложений мультимедиа, этот вопрос мы рассмотрим в дальнейшем. Поэтому имеет смысл оснастить свой компьютер звуковым адаптером. Лучше приобрести звуковой адаптер типа Creative Sound Blaster, для которого в составе операционной системы IBM OS/2 Warp имеется драйвер.
Исходные тексты описанных нами приложений вы можете найти на дискете, которая продается вместе с книгой.

Захват курсора мыши


Как мы уже говорили, сообщения мыши поступают в функцию того окна, в котором находится курсор мыши. Однако при необходимости приложение может захватить мышь в монопольное владение с помощью функции WinSetCapture . При этом функция окна приложения будет получать сообщения мыши вне зависимости от того, где находится курсор мыши. Если курсор будет расположен вне окна приложения, координаты курсора могут принимать отрицательные значения.

Для чего приложению может потребоваться захват мыши?

Например, для того чтобы выделить какую-либо область рабочего стола Workplace Shell или нарисовать что-либо вне области, занятой окном приложения. Типичный пример - приложение, сохраняющее выделенный фрагмент изображения рабочего стола в графическом файле.

Для захвата мыши приложение может вызвать функцию WinSetCapture следующим образом:

WinSetCapture (HWND_DESKTOP, hWnd);

В качестве первого параметра функции передается идентификатор окна Desktop Window , в качестве второго - идентификатор окна, захватывающего мышь.

Освободить мышь несложно. Для этого достаточно вызвать функцию WinSetCapture еще раз, указав в качестве второго параметра идентификатор NULLHANDLE:

WinSetCapture (HWND_DESKTOP, NULLHANDLE);



Замена кнопок


При необходимости вы можете изменить внешний вид кнопок + и -, подготовив для них новые растровые изображения. Идентификаторы этих изображений необходимо передать вместе с сообщением CSM_SETBITMAPDATA . Подробное описание этого сообщения мы приведем в одной из следующих книг после того, как познакомим вас с графическим интерфейсом системы Presentation Manager и опишем способы работы с растровыми изображениями.



Запуск цикла обработки сообщений


После вызова функции WinCreateStdWindow окно появится на экране, но оно еще не будет работать. Теперь вы должны запустить цикл обработки сообщений. Основная задача этого цикла - выборка сообщений из очереди приложения и передача их на обработку функции окна, зарегистрированной на предыдущем этапе.

Цикл обработки сообщений выглядит следующим образом:

QMSG qmsg; while(WinGetMsg (hab, &qmsg, 0, 0, 0)) WinDispatchMsg (hab, &qmsg);

Здесь функция WinGetMsg выбирает сообщения из очереди приложения, записывая их в структуру qmsg типа QMSG . Если очередь сообщений пуста, функция переходит в состояние ожидания. Три последних нулевых параметра означают, что из очереди будут выбираться все сообщения для всех окон, созданных в данной задаче.

После того как сообщение выбрано и записано в структуру qmsg, его нужно передать функции окна. Однако вы не можете просто вызвать функцию окна и в качестве одного из параметров передать указатель на данную структуру. Вспомните, мы говорили, что приложение создает функцию окна, но никогда ее не вызывает.

Вместо этого выбранное сообщение передается функции WinDispatchMsg , которая возвращает сообщение системе Presentation Manager и та уже вызывает функцию окна, определенную в вашем приложении.

На первый взгляд такая процедура может показаться бессмысленной, так как система Presentation Manager могла бы и сама выполнять такую обработку внутри себя. Однако цикл обработки сообщений, организованный в приложении, позволяет выполнять дополнительную обработку сообщений перед тем, как они возвратятся системе Presentation Manager и попадут в функцию окна. Такой контроль открывает перед приложением большие возможности в определении поведения своего главного окна. Например, приложение может контролировать весь поток сообщений, проходящих через очередь сообщений этого приложения.

Займемся функцией WinGetMsg , прототип которой приведен ниже:

BOOL WinGetMsg ( HAB hab, // идентификатор Anchor-block PQMSG pqmsgmsg, // указатель на структуру, // куда будет записано сообщение HWND hwndFilter,// фильтр окон ULONG ulFirst, // начальный код сообщения ULONG ulLast); // конечный код сообщения


Через параметр hab функции WinGetMsg передается идентфикатор Anchor-block, полученный от функции WinInitialize . Здесь для вас нет ничего нового.

Через параметр pqmsgmsg вы должны передать функции указатель на структуру типа QMSG , в которую будет записано извлеченное из очереди сообщение.

Если значение параметра hwndFilter не равно нулю, функция WinGetMsg будет выбирать сообщения, предназначенные для всех окон, созданных в рамках данной задачи. Как правило, именно так и поступают. Однако вы можете указать здесь идентификатор какого-либо дочернего окна и организовать обработку сообщений только для него.

Как мы уже говорили, для идентификации каждому сообщению присваивается определенный код. Если значения параметров ulFirst и ulLast равны нулю, функция WinGetMsg будет обрабатывать сообщения с любым кодом. Если же значение параметра ulFirst больше чем ulLast, будут обрабатываться все сообщения, кроме тех, коды которых попали в интервал от ulLast до ulFirst. Аналогично, если значение параметра ulFirst меньше, чем ulLast, будут обрабатываться только такие сообщения, коды которых попадают в интервал от ulFirst до ulLast.

Как правило, обычно значения параметров ulFirst и ulLast устанавливаются равными нулю.


Запуск и останов таймера


Для запуска таймера вы должны использовать функцию WinStartTimer , прототип которой приведен ниже:

ULONG WinStartTimer ( HAB hab, // идентификатора блока Anchor-block HWND hwnd, // идентификатор окна ULONG idTimer, // идентификатор таймера ULONG dtTimeout);// задержка в мс

После запуска таймера функция окна hwnd будет получать сообщения WM_TIMER с периодом, заданным параметром dtTimeout. Для операционной системы IBM OS/2 Warp версии 3.0 период должен быть целым числом в диапазоне от 0 до 4294976295. Предыдущие версии этой операционной системы допускали задавать период в диапазоне от 0 до 65535. Через параметр idTimer вы должны передать функции идентификатор таймера.

В случае успеха функция WinStartTimer возвращает идентификатор таймера. Если произошла ошибка, возвращается нулевое значение.

Вот пример использования функции WinStartTimer :

#define ID_APP_TIMER1 1 WinStartTimer (hab, hWnd, ID_APP_TIMER1, 1000);

Для остановки таймера вы должны воспользоваться функцией WinStopTimer :

BOOL WinStopTimer ( HAB hab, // идентификатора блока Anchor-block HWND hwnd, // идентификатор окна ULONG idTimer); // идентификатор таймера

Через параметр idTimer этой функции необходимо передать идентификатор таймера, запущенного ранее функцией WinStartTimer .



Завершение работы приложения


Ничто не продолжается бесконечно, поэтому рано или поздно вы должны завершить цикл обработки сообщений и закрыть приложение.

Как это сделать?

Для завершения работы приложения, не имеющего стандартного меню, вы должны сделать двойной щелчок левой клавишей мыши по системному меню, выбрать из этого меню строку Close или нажать комбинацию клавиш <Alt+F4>.

В результате в очередь сообщений записывается сообщение с кодом WM_QUIT , определенным в файле os2.h.

Функция WinGetMsg , выбирающая сообщения из очереди приложения, возвращает значение TRUE для всех сообщений, кроме сообщения с кодом WM_QUIT , чем мы и пользуемся в нашем цикле обработки сообщений. Как только функция WinGetMsg возвращает значение TRUE, цикл обработки сообщений завершается.

Однако пока еще рано вызывать оператор return и возвращать управление операционной системе, завершая работу приложения. Перед этим вам необходимо уничтожить главное окно приложения, очередь сообщений и вызвать функцию WinTerminate .

Финальный фрагмент фнункции main , выполняющий все перечисленные действия, показан ниже:

while(WinGetMsg (hab, &qmsg, 0, 0, 0)) WinDispatchMsg (hab, &qmsg); WinDestroyWindow(hWndFrame); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(0);

Для уничтожения главного окна приложения мы вызываем функцию WinDestroyWindow, передавая ей в качестве единственного параметра идентификатор окна Frame Window . При уничтожении окна Frame Window все остальные окна, созданные на его поверхности и являющиеся по отношению к нему дочерними, будут уничтожены автоматически. В частности, будет автоматически уничтожено клиентское окно Client Window , поэтому у вас нет необходимости уничтожать это окно явным образом.

После уничтожения окна Frame Window вы должны уничтожить очередь сообщений, вызав функцию WinDestroyMsgQueue , а затем вызвать функцию WinTerminate . После вызова функции WinTerminate вы не можете пользоваться другими функциями программного интерфейса Presentation Manager. Теперь самое время завершить работу приложения с помощью оператора return, что мы и делаем в приведенном выше примере.