Программирование для IBM OS/2© Александр Фролов, Григорий ФроловТом 25, М.: Диалог-МИФИ, 1993, 286 стр. 6.5. Приложение MOUSEMOVС помощью сообщения WM_MOUSEMOVE мы попробуем решить задачу перемещения окна приложения, не имеющего заголовка. Приложение MOUSEMOV создает окно без заголовка, системного меню и кнопок минимизации/максимизации, отображая в его центре текстовую строку (рис. 6.1).
Рис. 6.1. Окно приложения MOUSEMOV не имеет заголовка, но его можно перемещать при помощи мыши Если в окне нажать левую клавишу мыши, то обрабатывая сообщение WM_MOUSEMOVE , приложение отслеживает текущие координаты мыши, передвигая соответствующим образом главное окно приложения Frame Window . Когда курсор мыши оказывается над окном приложения MOUSEMOV, его форма изменяется - курсор принимает вид открытой ладони. Если нажать левую клавишу мыши, курсор будет изображаться в виде закрытой ладони. Внешне это выглядит так, как будто в процессе перемещения окна вы берете его рукой, переносите и затем отпускаете руку в нужном месте. Рамка окна позволяет изменять размеры окна, причем текст отображается всегда в центре окна незвисимо от этих размеров. Исходные тексты приложения MOUSEMOV приведены в листинге 6.1. Листинг 6.1. Файл mousemov\mousemov.c
// =================================================
// Определения
// =================================================
#define INCL_WIN
#define INCL_GPI
#define INCL_WINDIALOGS
#include <os2.h>
#include <stdio.h>
#include "mousemov.h"
// Прототип функции окна приложения
MRESULT EXPENTRY WndProc(HWND, ULONG, MPARAM, MPARAM);
// =================================================
// Глобальные переменные
// =================================================
HAB hab;
HWND hWndFrame;
HWND hWndClient;
CHAR szAppTitle[] = "Mouse Mover";
// Координаты курсора мыши в момент нажатия
// левой клавиши мыши
SHORT cxPoint;
SHORT cyPoint;
// Координаты курсора мыши в момент отпускания
// левой клавиши мыши
SHORT cxNewPoint;
SHORT cyNewPoint;
// Признак начала процесса перемещения окна
BOOL fDrag = FALSE;
// Текст, который будет отображаться в окне
CHAR pszText[] =
"Перемещайте окно при помощи левой клавиши мыши";
// Идентификаторы указателей мыши
HPOINTER hptr, hptr1;
// =================================================
// Главная функция приложения main
// =================================================
int main ()
{
HMQ hmq;
QMSG qmsg;
BOOL fRc;
// Флаги для создания окна Frame Window
// Мы не создаем окна заголовка, системного меню,
// а также окна кнопок минимизации и максимизации
ULONG flFrameFlags = FCF_SIZEBORDER |
FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON;
// Имя класса главного окна
CHAR szWndClass[] = "MOUSEMOV";
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, szWndClass,
(PFNWP)WndProc, 0, 0);
if(fRc == FALSE)
{
WinMessageBox (HWND_DESKTOP, HWND_DESKTOP,
"Ошибка при регистрации класса главного окна",
"Ошибка", 0, MB_ICONHAND | MB_OK);
WinDestroyMsgQueue (hmq);
WinTerminate (hab);
return(-1);
}
// Создаем главное окно приложения
hWndFrame = WinCreateStdWindow (HWND_DESKTOP,
WS_VISIBLE ,
&flFrameFlags, szWndClass, szAppTitle,
0, 0, ID_APP_FRAMEWND, &hWndClient);
if(hWndFrame == NULLHANDLE)
{
WinMessageBox (HWND_DESKTOP, HWND_DESKTOP,
"Ошибка при создании главного окна",
"Ошибка", 0, MB_ICONHAND | MB_OK);
WinDestroyMsgQueue (hmq);
WinTerminate (hab);
return(-1);
}
while(WinGetMsg (hab, &qmsg, 0, 0, 0))
WinDispatchMsg (hab, &qmsg);
WinDestroyWindow(hWndFrame);
WinDestroyMsgQueue (hmq);
WinTerminate (hab);
return(0);
}
// =================================================
// Функция главного окна приложения
// =================================================
MRESULT EXPENTRY
WndProc(HWND hWnd, ULONG msg, MPARAM mp1, MPARAM mp2)
{
HPS hps;
POINTL ptl;
RECTL rec;
SWP swp;
switch (msg)
{
// При создании окна загружаем указатели мыши
case WM_CREATE :
{
hptr = WinLoadPointer(HWND_DESKTOP,
NULLHANDLE, ID_APP_POINTER);
hptr1 = WinLoadPointer(HWND_DESKTOP,
NULLHANDLE, ID_APP_POINTER1);
return FALSE;
}
// При уничтожении окна удаляем указатели мыши
case WM_DESTROY :
{
WinDestroyPointer (hptr);
WinDestroyPointer (hptr1);
return 0;
}
// Когда пользователь нажимает левую клавишу
// мыши, запоминаем координаты курсора и
// выдвигаем окно приложения на передний план
case WM_BUTTON1DOWN :
{
cxPoint = MOUSEMSG(&msg) -> x;
cyPoint = MOUSEMSG(&msg) -> y;
// Изменяем расположение окна по оси Z
WinSetWindowPos (hWndFrame, HWND_TOP ,
0, 0, 0, 0, SWP _ZORDER );
// Устанавливаем признак перемещения
// главного окна приложения
fDrag = TRUE;
return 0;
}
// При отпускании левой клавиши мыши сбрасываем
// признак перемещения окна
case WM_BUTTON1UP :
{
// Сбрасываем признак перемещения
// главного окна приложения
fDrag = FALSE;
return 0;
}
// Это сообщение приходит при перемещении курсора
// мыши
case WM_MOUSEMOVE :
{
// Если включен признак перемещения, определяем
// новые координаты курсора и передвигаем окно
if(fDrag)
{
// Выбираем указатель в виде закрытой руки
WinSetPointer (HWND_DESKTOP, hptr1);
cxNewPoint = MOUSEMSG(&msg) -> x;
cyNewPoint = MOUSEMSG(&msg) -> y;
// Определяем текущие координаты окна
WinQueryWindow Pos(hWndFrame, &swp);
// Передвигаем окно
WinSetWindowPos (hWndFrame, HWND_TOP ,
swp.x + (cxNewPoint - cxPoint),
swp.y + (cyNewPoint - cyPoint),
0, 0, SWP _MOVE );
}
else
// Выбираем указатель в виде открытой руки
WinSetPointer (HWND_DESKTOP, hptr);
return (MRESULT)TRUE;
}
// В ответ на сообщение WM_PAINT выводим
// в центре окна строку текста
case WM_PAINT :
{
// Получаем пространство отображения
hps = WinBeginPaint (hWnd, NULLHANDLE, &rec);
// Определяем размеры главного окна
WinQueryWindow Rect(hWnd, &rec);
// Выводим текст в центре окна
WinDrawText (hps, -1, pszText, &rec, 0L, 0L,
DT_WORDBREAK | DT_CENTER | DT_VCENTER |
DT_TEXTATTRS | DT_ERASERECT);
// Освобождаем пространство отображения
WinEndPaint (hps);
return 0;
}
case WM_ERASEBACKGROUND :
return(MRFROMLONG(1L));
// При изменении размеров окна сохраняем новые
// размеры и перерисовываем окно
case WM_SIZE :
{
// При изменении размеров окна выполняем его
// перерисовку
WinInvalidateRect (hWnd, NULL, TRUE);
return 0;
}
// Если пользователь сделал двойной щелчок левой
// клавише мыши, завершаем работу приложения
case WM_BUTTON1DBLCLK :
{
WinPostMsg (hWnd, WM_QUIT , 0L, 0L);
return 0;
}
default:
return(WinDefWindowProc (hWnd, msg, mp1, mp2));
}
}
Глобальные переменныеВ переменные cxPoint и cyPoint записываются координаты курсора мыши в момент, когда пользователь начинает перемещение окна приложения, нажав левую кнопку мыши. Эти координаты будут затем сравниваться с координатами курсора мыши после завершения процесса перемещения, которые хранятся в переменных cxNewPoint и cyNewPoint. Переменная fDrag используется в качестве признака перемещения окна и проверяется при обработке сообщения WM_MOUSEMOVE . Вначале в нее записывается значение FALSE. Когда пользователь начинает перемещать окно, это значение изменяется на TRUE. После завершения процесса перемещения в переменную снова записывается значение FALSE. В области глобальных переменных мы также определили переменные hptr и hptr1 типа HPOINTER , в которых хранятся идентификаторы курсоров мыши, загруженных из ресурсов прложения. В первой из этих переменных хранится идентификатор курсора в виде открытой ладони, во второй - в виде закрытой ладони. Функция mainВ функции main нет ничего особенного за исключением того, что при создании главного окна приложения используется сокращенный набор флагов:
ULONG flFrameFlags = FCF_SIZEBORDER |
FCF_SHELLPOSITION | FCF_TASKLIST | FCF_ICON;
В результате окно Frame Window не создает окна заголовка, системное меню и кнопки минимизации/максимизации. Без дополнительной обработки сообщений мыши пользователь не сможет перемещать такое окно. Размер окна можно изменять, так как указан флаг FCF_SIZEBORDER. Функция окна WndProcФункция окна обрабатывает сообщения WM_CREATE , WM_DESTROY , WM_BUTTON1DOWN , WM_BUTTON1UP , WM_MOUSEMOVE , WM_PAINT , WM_ERASEBACKGROUND , WM_SIZE и WM_BUTTON1DBLCLK . Сообщение WM_CREATEПри создании окна обработчик сообщения WM_CREATE загружает из ресурсов приложения изображения курсоров мыши, пользуясь для этого функцией WinLoadPointer: hptr = WinLoadPointer(HWND_DESKTOP, NULLHANDLE, ID_APP_POINTER); hptr1 = WinLoadPointer(HWND_DESKTOP, NULLHANDLE, ID_APP_POINTER1); В качестве первого параметра функции передается идентификатор окна Desktop Window . Через второй параметр необходимо передать идентификатор модуля, содержащего соответствующий ресурс, или значение NULLHANDLE, если ресурс располагается в загрузочном файле приложения. Последний параметр предназначен для передачи идентификатора ресурса, с которым последний определен в файле описания ресурсов приложения. Сообщение WM_DESTROYВ ответ на сообщение WM_DESTROY , которое передается в функцию окна при завершении работы приложения, выполняется уничтожение двух загруженных курсоров мыши при помощи функции WinDestroyPointer : WinDestroyPointer (hptr); WinDestroyPointer (hptr1); В качестве единственного параметра этой функции передается идентификатор уничтожаемого курсора мыши. Сообщение WM_SIZEОбработчик сообщения WM_SIZE выполняет перерисовку окна приложения, когда пользователь изменяет его размеры, вызывая функцию WinInvalidateRect . В результате функция окна приложения получит сообщение WM_PAINT . Это необходимо для того чтобы строка символов всегда отображалась в центре окна. Сообщение WM_PAINTОбработчик сообщения WM_PAINT получает пространство отображения и затем определяет размеры окна, вызывая для этого функцию WinQueryWindow Rect. Далее с помощью функции WinDrawText в центре окна выводится текстовая строка, после чего пространство отображения освобождается. Сообщение WM_BUTTON1DOWNКогда пользователь начинает перемещение окна приложения MOUSEMOVE, он передвигает курсор мыши во внутреннюю область окна и нажимает левую клавишу мыши. При этом окно приложения получает сообщение WM_BUTTON1DOWN . Обработчик этого сообщения запоминает координаты курсора мыши, соответствующие точке начала перемещения, в глобальных переменных cxPoint и cyPoint. Далее окно выдвигается на передний план функцией WinSetWindowPos и устанавливается признак перемещения окна (в переменную fDrag записывается значение TRUE). Сообщение WM_BUTTON1UPПосле выполнения перемещения пользователь отпускает левую клавишу мыши. При этом в функцию окна приложения передается сообщение WM_BUTTON1UP . Обработчик этого сообщения просто сбрасывает признак перемещения окна, записывая в переменную fDrag значение FALSE. Сообщение WM_MOUSEMOVEОкно приложения получает сообщение 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_BUTTON1DBLCLKТак как окно приложения не имеет ни системного меню, ни меню верхнего уровня, для завершения его работы мы используем двойной щелчок левой клавишей мыши в окне приложения. Обработчик соответствующего сообщения WM_BUTTON1DBLCLK записывает в очередь приложения сообщение WM_QUIT , что приводит к завершению цикла обработки сообщений и прекращению работы приложения. Файл mousemov.hВ файле mousemov.h определены символичесике константы для ресурсов приложения (листинг 6.2). Листинг 6.2. Файл mousemov\mousemov.h #define ID_APP_FRAMEWND 1 #define ID_APP_POINTER 2 #define ID_APP_POINTER1 3 Файл mousemov.rcВ файле описания ресурсов приложения mousemov.rc (листинг 6.3) помимо пиктограммы определены два указателя мыши, для чего использован оператор POINTER . Листинг 6.3. Файл mousemov\mousemov.rc #include <os2.h> #include "mousemov.h" ICON ID_APP_FRAMEWND MOUSEMOV.ICO POINTER ID_APP_POINTER MOUSEMOV.PTR POINTER ID_APP_POINTER1 MOUSE1.PTR Файл определения модуля mousemov.defФайл определения модуля приложения представлен в листинге 6.4. Листинг 6.4. Файл mousemov\mousemov.def NAME MOUSEMOV WINDOWAPI DESCRIPTION 'MouseMov Application (C) Frolov A., 1996' HEAPSIZE 4096 STACKSIZE 32768 EXPORTS WndProc |

