Программирование для IBM OS/2© Александр Фролов, Григорий ФроловТом 25, М.: Диалог-МИФИ, 1993, 286 стр. 6.8. Приложение POINTERПриложение POINTER отображет в своем окне изображения всех стандартных курсоров мыши и стандартных пиктограмм. Оно демонстрирует способ изменения формы курсора мыши, способ изменения пиктограммы приложения, отображаемой в левом верхнем углу последнего, а также способ управления курсором мыши при помощи клавиатуры. Внешний вид главного окна приложения показан на рис. 6.2.
Рис. 6.2. Главное окно приложения POINTER Исходные тексты приложения представлены в листинге 6.5. Листинг 6.5. Файл pointer\pointer.c // ================================================= // Определения // ================================================= #define INCL_WIN #define INCL_GPI #define INCL_WINDIALOGS #include <os2.h> #include <stdio.h> #include <stdlib.h> #include "pointer.h" // Прототип функции окна приложения MRESULT EXPENTRY WndProc(HWND, ULONG, MPARAM, MPARAM); // ================================================= // Глобальные переменные // ================================================= HAB hab; HWND hWndFrame; HWND hWndClient; CHAR szAppTitle[] = "Mouse Poiter"; // Размеры пиктограммы курсора мыши LONG cxPointer, cyPointer; // Размеры окна Client Window LONG cxClient, cyClient; // ================================================= // Главная функция приложения 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; // Имя класса главного окна CHAR szWndClass[] = "MOUSEPOINTER"; 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); } // Устанавливаем новую пиктограмму // для главного окна WinSendMsg (hWndFrame, WM_SETICON , (MPARAM)WinQuerySysPointer (HWND_DESKTOP, SPTR_ICONINFORMATION, FALSE), NULL); // Запускаем цикл обработки сообщений 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; RECTL rec; POINTL ptl; LONG x, y; HPOINTER hptr; LONG xMouse, yMouse; switch (msg) { case WM_CREATE : { // Определяем и сохраняем размеры пиктограмм cxPointer = WinQuerySysValue (HWND_DESKTOP, SV_CXPOINTER); cyPointer = WinQuerySysValue (HWND_DESKTOP, SV_CYPOINTER); // Расстояние между пиктограммами cxPointer += 5; return FALSE; } case WM_PAINT : { // Получаем пространство отображения hps = WinBeginPaint (hWnd, NULLHANDLE, &rec); // Закрашиваем область, требующую обновления WinFillRect (hps, &rec, CLR_WHITE); // Рисуем стандартные изображения курсора мыши x = 0; y = cyClient - cyPointer; DrawMousePtr(hps, x, y, SPTR_ARROW); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_TEXT); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_WAIT); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_MOVE); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_SIZENWSE); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_SIZENESW); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_SIZEWE); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_SIZENS); // Рисуем стандартные пиктограммы x = 0; y = cyClient - 3*cyPointer; DrawMousePtr(hps, x, y, SPTR_APPICON); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_ICONINFORMATION); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_ICONQUESTION); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_ICONERROR); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_ICONWARNING); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_ILLEGAL); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_FILE); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_MULTFILE); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_FOLDER); x += cxPointer; DrawMousePtr(hps, x, y, SPTR_PROGRAM); // Освобождаем пространство отображения WinEndPaint (hps); return 0; } case WM_MOUSEMOVE : { // Получаем идентификатор стандартной // пиктограммы SPTR_ICONINFORMATION hptr = WinQuerySysPointer (HWND_DESKTOP, SPTR_ICONINFORMATION, FALSE); // Устанавливаем курсор мыши так, чтобы он // имел форму пиктограммы SPTR_ICONINFORMATION WinSetPointer (HWND_DESKTOP, hptr); return (MRESULT)TRUE; } case WM_ERASEBACKGROUND : return(MRFROMLONG(1L)); // При изменении размеров окна приложения // перерисовываем его содержимое case WM_SIZE : { // Сохраняем размеры окна Client Window cxClient = SHORT1FROMMP (mp2); cyClient = SHORT2FROMMP (mp2); // Обновляем окно Client Window WinInvalidateRect (hWnd, NULL, TRUE); return 0; } // Выполняем эмуляцию мыши с помощью клавиатуры case WM_CHAR : { // Фильтруем сообщения, соответствующие отжатию // клавиш, а также сообщения от обычных клавиш if((CHARMSG(&msg) ->fs & KC_KEYUP) | (!(CHARMSG(&msg) ->fs & KC_VIRTUALKEY))) return 0; // Определяем текущую позицию курсора мыши // в экранных координатах WinQueryPointerPos (HWND_DESKTOP, &ptl); // Преобразуем экранные координаты в оконные WinMapWindowPoints (HWND_DESKTOP, hWnd, &ptl, 1); // Сохраняем текущие оконные координаты курсора xMouse = ptl.x; yMouse = ptl.y; // Изменяем координаты в соответствии с // виртуальным кодом нажатой клавиши switch(CHARMSG(&msg) -> vkey) { case VK_LEFT: { xMouse -= cxPointer; break; } case VK_RIGHT: { xMouse += cxPointer; break; } case VK_DOWN: { yMouse -= cyPointer; break; } case VK_UP: { yMouse += cyPointer; break; } case VK_HOME: { xMouse = cxPointer; yMouse = cyClient - cyPointer; break; } case VK_END: { xMouse = cxClient - cxPointer; yMouse = cyPointer; break; } default: break; } // Не допускаем выхода курсора мыши за пределы // окна приложения ptl.x = max(min(xMouse, cxClient - 1), 0); ptl.y = max(min(yMouse, cyClient - 1), 0); // Преобразуем оконные координаты в экранные WinMapWindowPoints (hWnd, HWND_DESKTOP, &ptl, 1); // Устанавливаем новую позицию курсора мыши WinSetPointer Pos (HWND_DESKTOP, (SHORT) ptl.x, (SHORT) ptl.y); return 0; } default: return(WinDefWindowProc (hWnd, msg, mp1, mp2)); } } // ================================================= // Функция рисования курсора мыши // ================================================= void DrawMousePtr(HPS hps, LONG x, LONG y, LONG lPtrId) { HPOINTER hptr; // Получаем идентификатор стандартного курсора мыши hptr = WinQuerySysPointer (HWND_DESKTOP, lPtrId, FALSE); // Рисуем изображение курсора мыши WinDrawPointer(hps, x, y, hptr, DP_NORMAL); } Глобальные переменныеВ области глобальных переменных определены переменные cxPointer и cyPointer, в которых хранятся размеры пиктограммы курсора мыши, полученные на этапе инициализации приложения. Размеры окна приложения записываются в переменные cxClient и cyClient обработчиком сообщения WM_SIZE при создании окна и при изменении пользователем его размеров. Функция mainФункция main имеет одну особенность - после создания главного окна приложения и перед запуском цикла обработки сообщений главному окну посылается сообщение WM_SETICON : WinSendMsg (hWndFrame, WM_SETICON , (MPARAM)WinQuerySysPointer (HWND_DESKTOP, SPTR_ICONINFORMATION, FALSE), NULL); Через первый параметр вместе с этим сообщением передается идентификатор встроенной пиктограммы SPTR_ICONINFORMATION , полученный от функции WinQuerySysPointer . Второй параметр сообщения WM_SETICON равен нулю. После получения этого сообщения пиктограмма приложения, которая отображается в окне системного меню и которая была загружена из ресурсов приложения, заменяется на новую. Функция окна WndProcРассмотрим обработчики сообщений, расположенные в функции главного окна приложения WndProc. Сообщение WM_CREATEОбработчик сообщения WM_CREATE определяет размеры курсора мыши. Эти размеры являются системными значениями, для получения которых необходимо использовать функцию WinQuerySysValue , передав ей через второй параметр константы SV_CXPOINTER (ширина курсора мыши) и SV_CYPOINTER (высота курсора мыши): cxPointer = WinQuerySysValue (HWND_DESKTOP, SV_CXPOINTER); cyPointer = WinQuerySysValue (HWND_DESKTOP, SV_CYPOINTER); Эти размеры используются приложением для размещения изображений пиктограмм в своем окне. Чтобы обеспечить интервал между пиктограммами, значение переменной cxPointer в нашем приложении увеличивается на 5 пикселов. Сообщение WM_PAINTОбработчик этого сообщения получает пространство отображения и закрашивает его белым цветом. Затем он последовательно рисует изображения всех страндартных курсоров мыши и стандартных пиктограмм при помощи функции DrawMousePtr, определенной в нашем приложении: x = 0; y = cyClient - cyPointer; DrawMousePtr(hps, x, y, SPTR_ARROW); Через первый параметр этой функции необходимо передать идентификатор пространства отображения, черз второй и третий, соответственно, координаты левого нижнего угла пиктограммы в оконной системе координат, и, наконец, через последний параметр - идентификатор курсора мыши или пиктограммы. Перед возвращением управления обработчик сообщения WM_PAINT освобождает пространство отображения. Сообщение WM_MOUSEMOVEОбработчик сообщения WM_MOUSEMOVE с помощью функции WinQuerySysPointer получает идентификатор встроенной пиктограммы SPTR_ICONINFORMATION и использует его для изменения курсора мыши. Последняя операция выполняется с помощью фукнции WinSetPointer . Таким образом, для курсора мыши вы можете использовать не только встроенные изображения курсоров, но и встроенные изображения пиктограмм. Обратите внимание, что обработка сообщения WM_MOUSEMOVE завершается возвращением значения TRUE. Это необходимо для того, чтобы отменить вызов стандартного обработчика данного сообщения, который устанавливает стандартный курсор мыши. Если после обработки сообщения WM_MOUSEMOVE передать управление функции WinDefWindowProc , форму курсора изменить не удастся, так как данная функция будет всегда восстанавливать стандартный курсор. Сообщение WM_SIZEОбработчик сообщения WM_SIZE получает и сохраняет размеры окна Client Window , а затем обновляет это окно, вызывая для этого функцию WinInvalidateRect . В результате главное окно приложения получит сообщение WM_PAINT . Сообщение WM_CHARПриложение выполняет обработку этого сообщения для дублирования функций мыши при помощи клавиатуры. Так как сообщение 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); Затем полученные таким образом оконные координаты преобразуются в экранные и используются для установки новой позиции курсора мыши. Функция DrawMousePtrФункция DrawMousePtr, определенная в нашем приложении, получает с помощью функции WinQuerySysPointer идентификатор встроенного курсора мыши или встроенной пиктограммы, а затем рисует соответствующее изображение при помощи функции WinDrawPointer. Подробнее функции рисования мы рассмотрим позже в одной из следующих книг "Библиотеки системного программиста" в главе, посвященной графическому интерфейсу Presentation Manager. Файл pointer.hФайл pointer.h (листинг 6.6) содержит определение константы ID_APP_FRAMEWND, а также прототип функции DrawMousePtr. Листинг 6.6. Файл pointer\pointer.h #define ID_APP_FRAMEWND 1 void DrawMousePtr(HPS hps, LONG x, LONG y, LONG lPtrId); Файл pointer.rcВ файле описания ресуросв приложения pointer.rc (листинг 6.7) определена пиктограмма с идентификатором ID_APP_FRAMEWND. Листинг 6.7. Файл pointer\pointer.rc #include <os2.h> #include "pointer.h" ICON ID_APP_FRAMEWND POINTER.ICO Файл pointer.defФайл определения модуля приложения pointer.def представлен в листинге 6.8. Листинг 6.8. Файл pointer\pointer.def NAME POINTER WINDOWAPI DESCRIPTION 'Pointer Application (C) Frolov A., 1996' HEAPSIZE 4096 STACKSIZE 32768 EXPORTS WndProc |