Программирование для 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 |