Программирование для IBM OS/2© Александр Фролов, Григорий ФроловТом 4, М.: Диалог-МИФИ, 1993, 286 стр. |
В этой главе мы будем говорить об окнах, которые являются основным элементом пользовательского интерфейса. Окна создаются любым приложением Presentation Manager, как явным вызовом соответствующих функций, так и косвенными методами.
Самое главное, в чем вы должны разобраться после прочтения этой главы, это иерархия окон, способы создания дочерних окон и управления ими, а также принципы взаимодействия окон посредством передачи сообщений. Пусть вас не смущает, что пока в наших окнах ничего не нарисовано - скоро мы это исправим.
В предыдущей главе мы привели исходные тексты простейшего приложения Presentation Manager, создающего единственное окно. При этом мы сделали оговорку, что на самом деле при этом создается много окон и среди них, в частности, имеется окно Client Window , которое можно использовать для отображения какой-либо информации.
Теперь мы рассмотрим иерархию и взаимосвязь окон Presentation Manager более детально.
Каждое окно, создаваемое приложением, имеет родительское окно . При этом само оно по отношению к родительскому является дочерним.
Родительское окно может иметь несколько дочерних окон, которые при этом называются окнами-братьями (или окнами-сестрами, если вам так больше нравится). Обратное неверно, т. е. у каждого дочернего окна может быть только одно родительское окно.
На рис. 2.1 показана ситуация, когда одно родительское окно имеет три дочерних окна.
Рис. 2.1. Родительское и дочерние окна
Каждое дочернее окно , в свою очередь, может выступать в роли родительского окна, создавая свои дочерние окна.
Важной особенностью дочерних окон является то, что они всегда располагаются внутри своего родительского окна. Если пользователь попытается переместить дочернее окно за пределы родительского (например, при помощи мыши), будет нарисована только часть дочернего окна.
В том случае, когда в одном родительском окне создано несколько дочерних окон, они могут перекрывать друг друга.
Если пользователь перемещает родительское окно, то дочернее окно будет перемещаться вместе с ним.
Когда пользователь изменяет размеры родительского окна, дочернее окно может отображаться не полностью. Если же пользователь минимизирует родительское окно, дочернее окно исчезает с поверхности экрана. При минимизации дочернего окна оно отображается в родительском окне в виде пиктограммы.
При уничтожении родительского окна все его дочерние окна уничтожаются автоматически, поэтому вам не нужно об этом беспокоиться.
Какое окно является "основателем рода", т. е. родительским для всех остальных окон в Presentation Manager? Окна всех приложений располагаются в окне, представляющем собой поверхность рабочего стола Workplace Shell . Это окно, которое называется Desktop Window , создается автоматически при запуске операционной системы.
Однако окно Desktop Window само по себе является дочерним по отношению к другому окну - окну Object Window . Это окно не отображается и используется системой Presentation Manager для собственных нужд.
На рис. 2.2 показано, как соотносятся между собой окна Object Window , Desktop Window и окна Frame Window , создаваемые для каждого приложения.
Рис. 2.2. Взаимосвязь основных окон Presentation Manager
Окно Desktop Window имеет идентификатор HWND_DESKTOP , который мы уже использовали в нашем первом приложении. Вы можете ссылаться на окно и другим способом, получив его идентификатор с помощью функции WinQueryObjectWindow , как это показано ниже:
hwndDesktopWnd = WinQueryDesktopWindow(hab, NULL);
На окно Object Window при необходимости можно ссылаться при помощи идентификатора HWND_OBJECT . Второй способ получения этого идентификатора основан на использовании функции WinQueryObjectWindow :
hwndObjectWnd = WinQueryObjectWindow (HWND_DESKTOP);
Каждое приложение обычно создает окно Frame Window , которое всегда располагается на поверхности окна Desktop Window . При этом окно Desktop Window является родительским (Parent Window) для окна Frame Window. Соответственно, окно Frame Window по отношению к окну Desktop Window будет дочерним (Child Window).
Когда вы создаете стандартное окно Frame Window , у
него обычно имеется несколько дочерних окон,
таких как системное меню, заголовок, окно Client Window
и т. д. Полный список этих окон вместе с их
идентификаторами приведен ниже.
Дочернее окно | Идентификатор |
Системное меню | FID_SYSMENU |
Заголовок окна | FID_TITLEBAR |
Кнопка минимизации и максимизации | FID_MINMAX |
Меню | FID_MENU |
Вертикальная полоса просмотра | FID_VERTSCROLL |
Горизонтальная полоса просмотра | FID_HORZSCROLL |
Окно Client Window | FID_CLIENT |
Напомним, что при создании окна функцией WinCreateStdWindow при помощи флагов с префиксом имени FCF_ вы указываете, какие из перечисленных выше дочерних окон нужно создать.
Если вам будет нужно определить идентификатор одного из перечисленных выше органов управления, вы можете воспользоваться функцией WinWindowFromID , передав ей в качестве первого параметра идентификатор окна Frame Window , а в качестве второго - идентификатор соответствующего дочернего окна, например:
hwndMenu = WinWindowFromID (hwndFrameWindow, FID_MENU );
В составе программного интерфейса Presentation Manager имеются функции, позволяющие проследить "семейные отношения" между окнами. При помощи этих функций зная идентификатор дочернего окна, вы можете определить идентификатор родительского окна и наоборот, зная идентификатор родительского окна, определить идентификаторы всех его дочерних окон.
Например, функция WinQueryWindow позволяет определить идентификатор родительского окна, если идентификатор дочернего окна передается ей в качестве первого параметра, а константа QW_PARENT - в качестве второго.
С помощью функций WinBeginEnumWindows и WinGetNextWindow можно определить идентификаторы всех дочерних окон любого родительского окна.
Более подробно мы рассмотрим функции, предназначенные для просмотра дерева окон позже, когда в этом возникнет необходимость.
Помимо родственных связей, между окнами существуют отношения собственности. Одно окно может владеть другими. При этом окно-владелец получает извещающие сообщения от тех окон, которыми оно владеет. Как правило, дочернее окно является также и окном-владельцем. Подробнее об извещающих сообщениях мы расскажем в разделах, посвященных органам управления. Заметим только, что вы можете сменить владельца окна при помощи функции WinSetOwner :
BOOL WinSetParent( HWND hwnd, // идентификатор окна HWND hwndNewParent, // новое родительское окно BOOL fRedraw); // флаг перерисовки
Эта функция устанавливает для окна с идентификатором hwnd новое родительское окно hwndNewParent. Если при этом значение флага fRedraw будет равно TRUE, выполняется перерисовка окна, если FALSE - перерисовка окна не выполняется.
При необходимости вы можете назначить для дочернего окна нового родителя, вызвав функцию WinSetParent .
С помощью функции WinSetWindowPos приложение может изменить расположение или размеры созданного ранее окна. Прототип этой функции приведен ниже:
BOOL WinSetWindowPos ( HWND hwnd, // идентификатор окна HWND hwndInsertBehind,// относительный // порядок расположения LONG x, // координата по оси X LONG y, // координата по оси Y LONG cx, // ширина окна LONG cy, // высота окна ULONG fl); // индикатор изменения позиции
Через параметр hwnd функции передается идентификатор окна, размеры или расположение которого будут изменяться.
Параметр hwndInsertBehind задает новое расположение окна по оси Z (если в параметре fl, описанном ниже, указан флаг SWP _ZORDER ).
Сделаем пояснение относительно оси Z .
Ось Z направлена перпендикулярно к плоскости экрана в направлении от экрана к глазам пользователя.
Если на экране отображается несколько перекрывающих друг друга окон, можно считать, что все эти окна имеют разные координаты вдоль оси Z. Так как экран плоский, пользователь видит не объемное изображение, а плоскую проекцию этих окон на поверхность экрана. При этом некоторые окна могут полностью или частично закрывать другие.
С помощью функции WinSetWindowPos приложение может выдвигать некоторые окна на передний план или наоборот, отодвигать на задний. Для этого в качестве значения для параметра hwndInsertBehind следует указывать константы HWND_TOP и HWND_BOTTOM , соответственно.
Что касается параметров x и y, то они задают новые значения для координат дочернего окна по соответствующим координатным осям, связанным с родительским окном (если в параметре fl указан флаг SWP _MOVE ). По умолчанию начало системы координат находится в левом нижнем углу окна, ось X направлена слева направо, а ось Y - снизу вверх. В качестве единицы измерения, опять же по умолчанию, используется пиксел - минимальный элемент изображения при выбранном видеорежиме.
Параметры cx и cy задают, соответственно, новые значения для ширины и высоты окна в пикселах (если в параметре fl указан флаг SWP _SIZE ).
Параметр fl указывается как набор флагов,
объединенных при помощи логической операции ИЛИ.
Он определяет, какие операции выполняет функция
WinSetWindowPos . Ниже мы привели возможные значения
флагов с кратким описанием.
Флаг | Описание |
SWP _SIZE | Изменение размеров окна |
SWP _MOVE | Изменение расположения окна по осям X и Y |
SWP _ZORDER | Изменение расположения окна по оси Z |
SWP _SHOW | Отображение окна |
SWP _HIDE | Скрытие окна |
SWP _NOREDRAW | Если указан этот флаг, не выполняется перерисовка изменившихся областей окна |
SWP _NOADJUST | Если указан этот флаг, перед перемещением окна или перед изменением его размеров в функцию окна не передается сообщение WM_ADJUSTWINDOWPOS |
SWP _ACTIVATE | Активизация окна (действует только для окна Frame Window ). Если не указан флаг SWP _ZORDER и параметр hwndInsertBehind не равен HWND_BOTTOM , активизированное окно "всплывает" наверх по оси Z |
SWP _DEACTIVATE | Блокирование окна (действует только для окна Frame Window ). Если не указан флаг SWP _ZORDER и параметр hwndInsertBehind не равен HWND_TOP , активизированное окно отодвигается на задний план по оси Z |
SWP _MINIMIZE | Минимизация окна. Этот флаг несовместим с флагами SWP _MAXIMIZE и SWP_RESTORE |
SWP _MAXIMIZE | Максимизация окна. Этот флаг несовместим с флагами SWP _MINIMIZE и SWP_RESTORE |
SWP _RESTORE | Восстановление размеров окна. Этот флаг несовместим с флагами SWP _MINIMIZE и SWP_MAXIMIZE |
Если функция WinSetWindowPos завершилась успешно, она возвращает значение TRUE, при ошибке - FALSE.
Заметим, что если не указан флаг SWP _NOADJUST, в функцию окна перед выполнением перемещения или изменения размеров передается сообщение WM_ADJUSTWINDOWPOS . Первый параметр этого сообщения содержит указатель на структуру типа SWP, показанную ниже:
typedef struct _SWP { ULONG fl; LONG cy; LONG cx; LONG y; LONG x; HWND hwndInsertBehind; HWND hwnd; ULONG ulReserved1; // зарезервировано ULONG ulReserved2; // зарезервировано } SWP ; typedef SWP *PSWP;
Обработчик сообщения может изменить поля x, y, cx, cy или hwndInsertBehind, внеся коррективы в изменения размеров или расположения окна.
Общепринятый способ взаимодействия окон в системе Presentation Manager основан на передаче сообщений . Конкретнее, функция main или обработчик сообщения функции любого окна может послать сообщение другому окну. При этом передаваемое сообщение попадет в функцию этого окна.
Существует два принципиально разных способа передачи сообщений - передача через очередь приложения и непосредственная передача.
Передача сообщений через очередь приложения выполняется функцией WinPostMsg , прототип которой приведен ниже:
BOOL WinPostMsg ( HWND hwnd, // идентификатор окна ULONG ulMsgid, // код сообщения MPARAM mpParam1, // первый параметр MPARAM mpParam2); // второй параметр
Функция WinPostMsg посылает сообщение в функцию окна с идентификатором hwnd, причем код передаваемого сообщения определяется параметром ulMsgid, а первый и второй параметры сообщения - параметрами mpParam1 и mpParam2, соответственно.
Процедура посылки сообщения при помощи функции WinPostMsg выглядит следующим образом: функция записывает сообщение в очередь приложения и сразу же возвращает управление, не дожидаясь завершения обработки переданного сообщения. Если сообщение записано в очередь, функция WinPostMsg возвращает значение TRUE. В том случае, когда сообщение невозможно записать в очередь (например, по причине ее переполнения), функция возвращает значение FALSE.
Второй способ передачи сообщений основан на использовании функции WinSendMsg , прототип которой аналогичен прототипу функции WinPostMsg :
BOOL WinSendMsg ( HWND hwnd, // идентификатор окна ULONG ulMsgid, // код сообщения MPARAM mpParam1, // первый параметр MPARAM mpParam2); // второй параметр
Вызов функции WinSendMsg приводит к тому, что Presentation Manager выполняет непосредственный вызов функции окна, идентификатор которого задан параметром hwnd. При этом функции окна передается сообщение с кодом ulMsgid и параметрами mpParam1, mpParam2.
В отличие от функции WinPostMsg , функция WinSendMsg возвращает управление только после того, как передаваемое сообщение будет обработано соответствующей функцией окна. При этом она возвращает то значение, которое было передано через оператор return обработчиком сообщения при возврате управления из функции окна.
Ниже мы привели фрагмент исходного текста приложения, в котором окну hWndChildFrame передается сообщение WM_SETICON :
WinSendMsg (hWndChildFrame, WM_SETICON , (MPARAM)WinQuerySysPointer (HWND_DESKTOP, SPTR_APPICON, FALSE), NULL);
Первый параметр этого сообщения равен значению, полученному от функции WinQuerySysPointer (подробнее эту функцию мы рассмотрим позже), второй равен NULL.
Функция WinBroadcastMsg позволяет передавать сообщения одним из двух описанных выше методов одновременно всем дочерним окнам любого родительского окна:
BOOL WinBroadcastMsg ( HWND hwnd, // идентификатор родительского окна ULONG ulMsgid, // код сообщения MPARAM mpParam1, // первый параметр MPARAM mpParam2, // второй параметр ULONG flCmd); // команда
В зависимости от значения параметра flCmd функция
WinBroadcastMsg использует различный способ передачи
сообщения дочерним окнам. Ниже мы привели список
возможных значений для этого параметра.
Значение | Описание |
BMSG_POST | Передача сообщения через очередь сообщения. Этот параметр несовместим с параметрами BMSG_SEND и BMSG_POSTQUEUE |
BMSG_SEND | Непосредственная передача сообщения. Этот параметр несовместим с параметрами BMSG_POST и BMSG_POSTQUEUE |
BMSG_POSTQUEUE | Передача сообщения всем задачам процесса, имеющим очередь сообщений. Этот параметр несовместим с параметрами BMSG_POST и BMSG_SEND |
BMSG_DESCENDANTS | Сообщение передается всем дочерним окнам родительского окна hwnd |
BMSG_FRAMEONLY | Сообщение передается только окнам, имеющим стиль CS_FRAME (только окнам Frame Window ) |
В случае успешного завершения функция WinBroadcastMsg возвращает значение TRUE, при ошибке - FALSE.
Еще одна функция, которую мы рассмотрим в этом разделе, называется WinPostQueueMsg . Она позволяет записывать сообщение в заданную очередь сообщений:
BOOL WinPostQueueMsg ( HMQ hmq, // идентификатор очереди сообщений ULONG ulMsgid, // код сообщения MPARAM mpParam1, // первый параметр MPARAM mpParam2); // второй параметр
Обратите внимание, что этой функции не передается идентификатор окна.
Для того чтобы создать дочернее окно, вы можете воспользоваться все той же функцией 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, исходные тексты которого вы скоро увидите.
Наше следующее приложение называется WINTREE. Оно создает два окна верхнего уровня и одно дочернее окно (рис. 2.1), демонстрируя древовидную структуру родительских отношений между окнами.
Рис. 2.1. Окна, создаваемые приложением WINTREE
Изучая это приложение, вы научитесь создавать дочерние окна, а также сможете изучить поведение таких окон, сравнив их с поведением окон верхнего уровня.
Исходный текст приложения WINTREE показан в листинге 2.1.
Листинг 2.1. Файл wintree\wintree.c
// =================================================== // Определения // =================================================== #define INCL_WIN #define INCL_GPI #define INCL_WINDIALOGS #include <os2.h> #include <stdio.h> #include "wintree.h" // Прототип функции окна приложения MRESULT EXPENTRY WndProc1(HWND, ULONG, MPARAM, MPARAM); MRESULT EXPENTRY WndProc2(HWND, ULONG, MPARAM, MPARAM); MRESULT EXPENTRY WndProcChild(HWND, ULONG, MPARAM, MPARAM); // ================================================== // Глобальные переменные // ================================================== // Идентификатор Anchor-block HAB hab; // Идентификатор первого и второго окна Frame Window HWND hWndFrame1; HWND hWndFrame2; // Идентификатор дочернего окна HWND hWndChildFrame; // Идентификатор первого и второго окна Client Window HWND hWndClient1; HWND hWndClient2; // Идентификатор окна Client Window дочернего окна HWND hWndChildClient; // Заголовки окон CHAR szAppTitle1[] = "Windows Tree Demo 1"; CHAR szAppTitle2[] = "Windows Tree Demo 2"; CHAR szChildTitle[] = "Child Window"; // =================================================== // Главная функция приложения main // Получает управление при запуске приложения // =================================================== int main () { HMQ hmq; QMSG qmsg; BOOL fRc; // Флаги для создания окна Frame Window ULONG flFrameFlags = FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_SIZEBORDER | FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON; // Флаги для создания дочернего окна ULONG flFrameChildFlags = FCF_SYSMENU | FCF_TITLEBAR | FCF_MINMAX | FCF_SIZEBORDER; // Имена классов для создаваемых окон CHAR szWndClass1[] = "WINTREE1"; CHAR szWndClass2[] = "WINTREE2"; CHAR szWndClassChild[] = "WINTREECHILD"; // Инициализация приложения hab = WinInitialize (0); if(hab == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка инициализации", "Ошибка", 0, MB_ICONHAND | MB_OK); return(-1); } // Создаем очередь сообщений hmq = WinCreateMsgQueue (hab, 0); if(hmq == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при создании очереди сообщений", "Ошибка", 0, MB_ICONHAND | MB_OK); WinTerminate (hab); return(-1); } // Регистрация класса главного окна приложения fRc = WinRegisterClass (hab, szWndClass1, (PFNWP)WndProc1, 0, 0); if(fRc == FALSE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при регистрации класса главного окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Регистрация класса второго окна fRc = WinRegisterClass (hab, szWndClass2, (PFNWP)WndProc2, 0, 0); if(fRc == FALSE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при регистрации класса главного окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Регистрация класса дочернего окна fRc = WinRegisterClass (hab, szWndClassChild, (PFNWP)WndProcChild, 0, 0); if(fRc == FALSE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при регистрации класса главного окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Создаем главное окно приложения hWndFrame1 = WinCreateStdWindow (HWND_DESKTOP, WS_VISIBLE , &flFrameFlags, szWndClass1, szAppTitle1, 0, 0, ID_APP_FRAMEWND, &hWndClient1); if(hWndFrame1 == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при создании главного окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Создаем второе окно hWndFrame2 = WinCreateStdWindow (HWND_DESKTOP, WS_VISIBLE , &flFrameFlags, szWndClass2, szAppTitle2, 0, 0, ID_APP_FRAMEWND, &hWndClient1); if(hWndFrame2 == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при создании второго окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyWindow(hWndFrame1); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Создаем дочернее окно hWndChildFrame = WinCreateStdWindow (hWndFrame2, WS_VISIBLE , &flFrameChildFlags, szWndClassChild, szChildTitle, 0, 0, ID_CHILDWND, &hWndChildClient); if(hWndChildFrame == NULLHANDLE) { WinMessageBox (HWND_DESKTOP, HWND_DESKTOP, "Ошибка при создании дочернего окна", "Ошибка", 0, MB_ICONHAND | MB_OK); WinDestroyWindow(hWndFrame2); WinDestroyWindow(hWndFrame1); WinDestroyMsgQueue (hmq); WinTerminate (hab); return(-1); } // Устанавливаем начальные размеры и // расположение дочернего окна WinSetWindowPos (hWndChildFrame, HWND_TOP , 10, 10, 200, 200, SWP _ACTIVATE | SWP_SIZE | SWP_SHOW | SWP_MOVE ); // Устанавливаем пиктограмму для дочернего окна WinSendMsg (hWndChildFrame, WM_SETICON , (MPARAM)WinQuerySysPointer (HWND_DESKTOP, SPTR_APPICON, FALSE), NULL); // Запускаем цикл обработки сообщений while(WinGetMsg (hab, &qmsg, 0, 0, 0)) WinDispatchMsg (hab, &qmsg); // Уничтожаем главное окно приложения WinDestroyWindow(hWndFrame1); // Удаляем очередь сообщений и вызываем // функцию WinTerminate WinDestroyMsgQueue (hmq); WinTerminate (hab); // Возвращаем управление операционной системе return(0); } // ================================================== // Функция главного окна приложения // ================================================== MRESULT EXPENTRY WndProc1(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2) { CHAR szMsg[100]; switch (msg) { case WM_ERASEBACKGROUND : return(MRFROMLONG(1L)); case WM_BUTTON1DOWN : { sprintf (szMsg, "(x, y) = (%ld, %ld)", SHORT1FROMMP (mp1), SHORT2FROMMP (mp1)); WinMessageBox (HWND_DESKTOP, hWnd, szMsg, "Координаты курсора мыши (окно 1)", 0, MB_INFORMATION | MB_OK); } default: return(WinDefWindowProc (hWnd, msg, mp1, mp2)); } } // ================================================== // Функция второго окна приложения // ================================================== MRESULT EXPENTRY WndProc2(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2) { CHAR szMsg[100]; switch (msg) { case WM_ERASEBACKGROUND : return(MRFROMLONG(1L)); case WM_BUTTON1DOWN : { sprintf (szMsg, "(x, y) = (%ld, %ld)", SHORT1FROMMP (mp1), SHORT2FROMMP (mp1)); WinMessageBox (HWND_DESKTOP, hWnd, szMsg, "Координаты курсора мыши (окно 2)", 0, MB_INFORMATION | MB_OK); } default: return(WinDefWindowProc (hWnd, msg, mp1, mp2)); } } // ================================================== // Функция дочернего окна // ================================================== MRESULT EXPENTRY WndProcChild(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2) { CHAR szMsg[100]; switch (msg) { case WM_ERASEBACKGROUND : return(MRFROMLONG(1L)); case WM_BUTTON1DOWN : { sprintf (szMsg, "(x, y) = (%ld, %ld)", SHORT1FROMMP (mp1), SHORT2FROMMP (mp1)); WinMessageBox (HWND_DESKTOP, hWnd, szMsg, "Координаты курсора мыши (дочернее окно)", 0, MB_INFORMATION | MB_OK); } default: return(WinDefWindowProc (hWnd, msg, mp1, mp2)); } }
Файл wintree.h, приведенный в листинге 2.2, содержит определение констант ID_APP_FRAMEWND и ID_CHILDWND (идентификаторы ресурсов для окон верхнего уровня и дочернего окна).
Листинг 2.2. Файл wintree\wintree.h
#define ID_APP_FRAMEWND 1 #define ID_CHILDWND 2
Файл описания ресурсов приложения (листиг 2.3) содержит определение пиктограммы.
Листинг 2.3. Файл wintree\wintree.rc
#include <os2.h> #include "wintree.h" ICON ID_APP_FRAMEWND WINTREE.ICO
В файле определения модуля (листинг 2.4) в секции EXPORTS перечислены имена всех функций обратного вызова. В роли этих функций выступают функции окон верхнего уровня WndProc1 и WndProc2, а также функция дочернего окна WndProcChild.
Листинг 2.4. Файл wintree\wintree.def
NAME WINTREE WINDOWAPI DESCRIPTION 'WinTree Application (C) Frolov A., 1996' HEAPSIZE 4096 STACKSIZE 32768 EXPORTS WndProc1 WndProc2 WndProcChild
В самом начале исходного текста приложения определены три прототипа функций окна. Две из этих функций соответствуют окнам верхнего уровня, одна - дочернему окну.
Для каждого из перечисленных выше трех окон в области глобальных переменных хранятся идентификаторы окна Frame Window (hWndFrame1, hWndFrame2 и hWndChildFrame), а также идентификаторы окон Client Window (hWndClient1, hWndClient2 и hWndChildClient).
Кроме этого, в области глобальных переменных находятся заголовки для создаваемых окон (szAppTitle1, szAppTitle2 и szChildTitle).
В функции main определены флаги, которые используются для создания окон верхнего уровня (переменная flFrameFlags) и для окна дочернего уровня (переменная flFrameChildFlags).
Обратите внимание, что для дочернего окна мы не используем флаги FCF_ICON , FCF_SHELLPOSITION и FCF_TASKLIST . Для установки пиктограммы, отображемой в верхнем левом углу дочернего окна, мы посылаем этому окну сообщение WM_SETICON . Размеры и расположение дочернего окна будут установлены явням образом при помощи функции WinSetWindowPos .
Каждое из трех окон нашего приложения имеет свою функцию окна и потому создается на базе своего класса окна. Имена этих классов хранятся в переменных szWndClass1, szWndClass2 и szWndClassChild.
Свою работу функция main начинает, как обычно, с вызова функции инициализации WinInitialize . Далее при помощи функции WinCreateMsgQueue создается очередь сообщений.
После этого для регистрации классов окна три раза вызывается функция WinRegisterClass . При этом определяется, что для окон, создаваемых на базе класса szWndClass1, будет использоваться функция окна WndProc1, для окон, создаваемых на базе класса szWndClass2 - функция окна WndProc2, а для окон, создаваемых на базе класса szWndClassChild - функция окна WndProcChild.
Как и раньше, после вызова каждой функции проверяется код возврата. Если при выполнении функции произошла ошибка, приложение выводит на экран соответствующее сообщение и завершает свою работу.
Если регистрация классов завершилась успешно, функция main создает два окна верхнего уровня, аналогично тому, как создавалось главное окно предыдущего приложения. Каждое из этих окон основано на базе своего класса окна и имеет собственную функцию окна, однако для обоих окон мы указали один и тот же идентификатор ресурсов. В результате эти окна будут иметь одинаковую пиктограмму системного меню.
На следующем этапе наше приложение создает окно, которое является дочерним по отношению к второму окну верхнего уровня, имеющему идентификатор hWndFrame2. Для этого используется знакомая вам функция WinCreateStdWindow :
hWndChildFrame = WinCreateStdWindow (hWndFrame2, WS_VISIBLE , &flFrameChildFlags, szWndClassChild, szChildTitle, 0, 0, ID_CHILDWND, &hWndChildClient);
В качестве идентификатора ресурса для дочернего окна указана константа ID_CHILDWND. В файле описания ресурсов нет пиктограммы с таким идентификатором, поэтому если не предпринимать никаких дополнительных мер, для системного меню будет использована стандартная пиктограмма, определенная в Presentation Manager.
После создания дочернего окна функция main устанавливает его размеры и расположение, вызывая для этого рассмотренную нами ранее функцию WinSetWindowPos :
WinSetWindowPos (hWndChildFrame, HWND_TOP , 10, 10, 200, 200, SWP _ACTIVATE | SWP_SIZE | SWP_SHOW | SWP_MOVE );
Далее для изменения пиктограммы системного меню при помощи функции WinSendMsg в функцию дочернего окна посылается сообщение WM_SETICON :
WinSendMsg (hWndChildFrame, WM_SETICON , (MPARAM)WinQuerySysPointer (HWND_DESKTOP, SPTR_APPICON, FALSE), NULL);
Первый параметр этого сообщения должен содержать идентификатор пиктограммы, второй не используется и указан как NULL.
Функция WinQuerySysPointer предназначена для получения идентификатора ресурса системного курсора или системной пиктограммы, заданной своим идентификатором. Прототип этой функции приведен ниже:
HPOINTER WinQuerySysPointer ( HWND hwndDeskTop, // идентификатор окна Desktop Window LONG lIdentifier, // идентификатор системного курсора // или пиктограммы BOOL fCopy); // признак копирования
В качестве значения для параметра hwndDeskTop можно
использовать константу HWND_DESKTOP. Что же касается
идентификатора lIdentifier, то для него следует
указывать одно из приведенных ниже значений.
Значение | Описание |
SPTR_ARROW | Курсор в виде стрелки |
SPTR_TEXT | Текстовый курсор в виде буквы I |
SPTR_WAIT | Курсор в виде песочных часов |
SPTR_SIZE | Курсор для изменения размера |
SPTR_MOVE | Курсор для перемещения объектов |
SPTR_SIZENWSE | Курсор для изменения размера, направленный вниз |
SPTR_SIZENESW | Курсор для изменения размера, направленный вверх |
SPTR_SIZEWE | Горизонтальный курсор для изменения размера |
SPTR_SIZENS | Вертикальный курсор для изменения размера |
SPTR_APPICON | Стандартная пиктограмма приложения |
SPTR_ICONINFORMATION | Пиктограмма для информационного сообщения |
SPTR_ICONQUESICON | Пиктограмма со знаком вопроса |
SPTR_ICONERROR | Пиктограмма для сообщения об ошибке |
SPTR_ICONWARNING | Пиктограмма для предупреждающего сообщения |
SPTR_ILLEGAL | Пиктограмма для сообщения о неправильной операции |
SPTR_FILE | Пиктограмма для обозначения одного файла |
SPTR_MULTFILE | Пиктограмма для обозначения группы файлов |
SPTR_FOLDER | Пиктограмма папки |
SPTR_PROGRAM | Пиктограмма приложения |
Признак копирования fCopy может принимать значения TRUE или FALSE. В первом случае создается копия указанного системного ресурса, которая при необходимости может быть модифицирована. Ресурс, созданный таким образом, должен быть удален после использования при помощи функции WinDestroyPointer . Эта функция имеет единественный параметр - идентификатор уничтожаемого ресурса.
Наше приложение ничего не модифицирует и использует значение FALSE. При этом функция WinQuerySysPointer просто возвращает идентификатор, который мы передаем дочернему окну через сообщение WM_SETICON .
Функции окон, определенные в нашем приложении, похожи друг на друга и на функцию окна из предыдущего приложения. Их основная работа заключается в том, чтобы выводить на экран сообщение, когда пользователь делает щелчок левой клавишей мыши в области окна. В сообщении указывается название окна и координаты курсора мыши (в системе координат, связанной с этим окном).