Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система Microsoft Windows 3.1 для программиста

© Александр Фролов, Григорий Фролов
Том 12, М.: Диалог-МИФИ, 1993, 255 стр.

[Назад] [Содеожание] [Дальше]

1.4. Курсор мыши

В предыдущем томе "Библиотеки системного программиста" мы рассказывали вам о том, как управлять курсором мыши и как изменять его форму. В этом разделе мы вновь вернемся к этому вопросу.

Курсор мыши представляет собой ни что иное, как упрощенный вариант битового изображения (bitmap), аналогичного пиктограмме. Вы можете выбрать один из встроенных курсоров, либо создать свой собственный. Для создания своего курсора его надо нарисовать, пользуясь приложением Resource Workshop, и записать в файл с расширением имени cur. Далее на файл с курсором следует сделать ссылку в файле описания ресурсов. После этого приложение может загрузить курсор в память и использовать его либо при регистрации класса окна, либо для изменения формы курсора в произвольный момент времени.

Создание курсора

Для создания файла, содержащего курсор, запустите приложение Resource Workshop. Из меню "File" выберите строку "New project...". В появившейся диалоговой панели включите переключатель ".CUR" и нажмите кнопку "OK".

На экране появятся окна, аналогичные используемым для редактирования пиктограмм (рис. 1.11).

Рис. 1.11. Редактирование курсора

После того как курсор будет нарисован, его следует сохранить в файле с расширением имени cur. Для этого выберите из меню "File" строку "Save file as...".

Включение курсора в файл описания ресурсов

Для включения курсора в файл описания ресурсов используется оператор CURSOR, аналогичный оператору ICON:

CursorID CURSOR [параметры загрузки] [тип памяти] имя файла

В качестве параметров загрузки можно указывать значения PRELOAD или LOADONCALL (используется по умолчанию). Ресурс с параметром загрузки LOADONCALL загружается в память при обращении к нему со стороны приложения. Ресурс типа PRELOAD загружается сразу после запуска приложения.

Тип памяти, выделяемой при загрузки ресурса, может быть FIXED или MOVEABLE. Дополнительно для ресурсов типа можно указать MOVEABLE тип DISCARDABLE. Если указан тип FIXED, ресурс будет находиться в памяти по постоянному адресу. Ресурс типа MOVEABLE может перемещаться Windows при необходимости уплотнения памяти. Если для перемещаемого ресурса указан тип DISCARDABLE, Windows может забрать у приложения память, выделенную для ресурса. Если ресурс потребуется приложению, Windows загрузит его повторно из exe-файла приложения.

В качестве имени файла необходимо указать имя файла, подготовленного с помощью приложения Resource Workshop или аналогичного и содержащего курсор, например:

AppCursor CURSOR mycursor.cur

Идентификатор курсора CursorID можно указывать как символическое имя (см. предыдущую строку) или как целое число - идентификатор ресурса:

789 CURSOR custom.cur

После сборки проекта файл курсора будет вставлен в исполняемый файл приложения Windows.

Для загрузки курсора следует использовать функцию LoadCursor:

HCURSOR WINAPI LoadCursor(HINSTANCE hInst, 
      LPCSTR lpszCursor);

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

Если в файле описания ресурсов идентификатор ресурса представлен символьной строкой, адрес этой строки необходимо указать в параметре lpszCursor:

HCURSOR hCustomCursor;
hCustomCursor = LoadCursor(hInstance, "AppCursor");

Если же в качестве идентификатора ресурса-курсора использовано целое число, следует использовать макрокоманду MAKEINTRESOURCE:

HCURSOR hCustomCursor;
hCustomCursor = LoadCursor(hInstance, MAKEINTRESOURCE(789));

Встроенные курсоры

Приложение Windows может использовать несколько встроенных курсоров. Приведем список идентификаторов встроенных курсоров.

Курсор Идентификатор Применение
IDC_ARROW Стандартный курсор в виде стрелки
IDC_IBEAM Текстовый курсор
IDC_WAIT Курсор в виде песочных часов. Используется при выполнении длительных операций
IDC_CROSS Курсор в виде перекрестия
IDC_UPARROW Курсор в виде вертикальной стрелки
IDC_SIZE Индикация изменения размера
IDC_ICON Пустая пиктограмма
IDC_SIZENWSE Индикация изменения размера
IDC_SIZENESW Индикация изменения размера
IDC_SIZEWE Индикация изменения размера
IDC_SIZENS Индикация изменения размера

Идентификаторы встроенных курсоров описаны в файле windows.h:

#define IDC_ARROW      MAKEINTRESOURCE(32512)
#define IDC_IBEAM      MAKEINTRESOURCE(32513)
#define IDC_WAIT       MAKEINTRESOURCE(32514)
#define IDC_CROSS      MAKEINTRESOURCE(32515)
#define IDC_UPARROW    MAKEINTRESOURCE(32516)
#define IDC_SIZE       MAKEINTRESOURCE(32640)
#define IDC_ICON       MAKEINTRESOURCE(32641)
#define IDC_SIZENWSE   MAKEINTRESOURCE(32642)
#define IDC_SIZENESW   MAKEINTRESOURCE(32643)
#define IDC_SIZEWE     MAKEINTRESOURCE(32644)
#define IDC_SIZENS     MAKEINTRESOURCE(32645)

Обратите внимание, что для встроенных пиктограмм и встроенных курсоров используются идентификаторы с одинаковым значением. Например, идентификатор курсора IDC_ARROW и идентификатор пиктограммы IDI_APPLICATION определены одинаково:

#define IDC_ARROW        MAKEINTRESOURCE(32512)
#define IDI_APPLICATION  MAKEINTRESOURCE(32512)

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

Изменение формы курсора

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

При регистрации класса окна мы задавали форму курсора следующим способом:

wc.hCursor = LoadCursor(NULL, IDC_ARROW);

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

wc.hCursor = LoadCursor(hInstance,  "AppCursor");

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

HCURSOR WINAPI SetCursor(HCURSOR hcur);

Параметр hcur функции SetCursor должен указывать идентификатор нового курсора, подготовленный при помощи функции LoadCursor. Если указать параметр как NULL, изображение курсора исчезнет с экрана.

Для того чтобы выключить изображение курсора мыши или вновь включить его используют функцию ShowCursor:

int WINAPI ShowCursor(BOOL fShow);

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

Для включения курсора в качестве параметра fShow функции следует передать значение TRUE, для выключения - FALSE.

Возвращаемое функцией ShowCursor значение равно новому содержимому счетчика.

Изображение курсора в окне приложения

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

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

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

Приложение CURSOR

Приложение CURSOR демонстрирует использование курсора, нарисованного при помощи приложения Resource Workshop, при регистрации класса окна. Это приложение рисует в главном окне приложения все встроенные курсоры и один курсор из файла описания ресурсов (рис. 1.12).

Рис. 1.12. Главное окно приложения CURSOR

Главный файл приложения CURSOR приведен в листинге 1.11.


Листинг 1.11. Файл cursor\cursor.cpp


// ----------------------------------------
// Изображение курсоров
// ----------------------------------------

#define STRICT
#include <windows.h>
#include <mem.h>
#include "cursor.hpp"

void Error(void);
BOOL InitApp(HINSTANCE);
LRESULT CALLBACK _export WndProc(HWND, UINT, WPARAM, LPARAM);

char szClassName[80];
char szWindowTitle[80];

HINSTANCE hInst;

// =====================================
// Функция WinMain
// =====================================
#pragma argsused

int PASCAL
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine,
        int       nCmdShow)
{
  MSG  msg;   // структура для работы с сообщениями
  HWND hwnd;  // идентификатор главного окна приложения
  int  cb;

  hInst = hInstance;

  // Загружаем текстовые строки 
  cb = LoadString(hInstance, IDS_CLASSNAME,
    szClassName, sizeof(szClassName));
  if(!cb)
  {
    Error(); return -1;
  }

  cb = LoadString(hInstance, IDS_WINDOWTITLE,
    szWindowTitle, sizeof(szWindowTitle));
  if(!cb)
  {
    Error(); return -1;
  }

  if(!InitApp(hInstance))
      return FALSE;

  hwnd = CreateWindow(
    szClassName,         // имя класса окна
    szWindowTitle,       // заголовок окна
    WS_OVERLAPPEDWINDOW, // стиль окна
    CW_USEDEFAULT,       // задаем размеры и расположение
    CW_USEDEFAULT,       // окна, принятые по умолчанию 
    CW_USEDEFAULT,
    CW_USEDEFAULT,
    0,                   // идентификатор родительского окна
    0,                   // идентификатор меню
    hInstance,           // идентификатор приложения
    NULL);               // указатель на дополнительные
                         // параметры
  if(!hwnd)
    return FALSE;

  ShowWindow(hwnd, nCmdShow);
  UpdateWindow(hwnd);

  while(GetMessage(&msg, 0, 0, 0))
  {
    DispatchMessage(&msg);
  }
  return msg.wParam;
}

// =====================================
// Функция InitApp
// =====================================

BOOL
InitApp(HINSTANCE hInstance)
{
  ATOM aWndClass; // атом для кода возврата
  WNDCLASS wc;    // структура для регистрации
                  // класса окна

  memset(&wc, 0, sizeof(wc));

  // Загружаем пиктограмму, которая будет использована
  // для отображения главного окна в свернутом виде
  wc.hIcon = LoadIcon(hInstance, "AppIcon");

  // Загружаем курсор для главного окна
  wc.hCursor = LoadCursor(hInst, "AppCursor");

  wc.style = 0;
  wc.lpfnWndProc = (WNDPROC) WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
  wc.lpszMenuName = (LPSTR)NULL;
  wc.lpszClassName = (LPSTR)szClassName;

  aWndClass = RegisterClass(&wc);
  return (aWndClass != 0);
}

// --------------------------------------------
// Функция обработки ошибки загрузки ресурса
// --------------------------------------------
void Error(void)
{
  MessageBox(NULL, "Ошибка при загрузке ресурса",
    "Error", MB_OK | MB_ICONSTOP);
}

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

wc.hIcon   = LoadIcon(hInstance, "AppIcon");
wc.hCursor = LoadCursor(hInst, "AppCursor");

Идентификаторы строк определены в файле cursor.hpp (листинг 1.12).


Листинг 1.12. Файл cursor\cursor.hpp


#define IDS_CLASSNAME    1
#define IDS_WINDOWTITLE  2

Файл ресурсов (листинг 1.13) содержит таблицу текстовых строк, ссылку на файлы пиктограммы и курсора.


Листинг 1.13. Файл cursor\cursor.rc


#include "cursor.hpp"

/* Таблица строк */
STRINGTABLE
BEGIN
IDS_CLASSNAME,   "CURSORAppClass"
IDS_WINDOWTITLE, "CURSOR Demo"
END

/* Пиктограмма */
AppIcon ICON cursor.ico

/* Курсор */
AppCursor CURSOR cursor.cur

В листинге 1.14 приведено изображение пиктограммы, указанной при регистрации класса главного окна приложения CURSOR.


Листинг 1.14. Файл cursor\cursor.ico



Когда курсор мыши находится внутри главного окна приложения CURSOR, он принимает форму, представленную в листинге 1.15.


Листинг 1.15. Файл cursor\cursor.cur



Функция главного окна приложения (листинг 1.16) рисует в окне встроенные курсоры и курсор, определенный в файле описания ресурсов.


Листинг 1.16. Файл cursor\wndproc.cpp


// =====================================
// Функция WndProc
// =====================================

#define STRICT
#include <windows.h>
#include <stdio.h>

extern HINSTANCE hInst;

LRESULT CALLBACK _export
WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  HDC hdc;             // индекс контекста устройства
  PAINTSTRUCT ps;      // структура для рисования

  // Размеры курсора
  static int xCursor, yCursor;

  // Идентификатор курсора
  HCURSOR hCursor;

  switch (msg)
  {
    case WM_CREATE:
    {
      // Определяем размеры курсора
      xCursor = GetSystemMetrics(SM_CXCURSOR);
      yCursor = GetSystemMetrics(SM_CYCURSOR);
      break;
    }

    case WM_PAINT:
    {
      hdc = BeginPaint(hwnd, &ps);

      // Загружаем и рисуем встроенные курсоры
      hCursor = LoadCursor(0, IDC_ARROW);
      DrawIcon(hdc, xCursor/2, yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_IBEAM);
      DrawIcon(hdc, xCursor/2 + xCursor, yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_WAIT);
      DrawIcon(hdc,xCursor/2 + xCursor*2,yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_CROSS);
      DrawIcon(hdc,xCursor/2 + xCursor*3,yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_UPARROW);
      DrawIcon(hdc,xCursor/2 + xCursor*4,yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_SIZE);
      DrawIcon(hdc,xCursor/2 + xCursor*5,yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_ICON);
      DrawIcon(hdc,xCursor/2 + xCursor*6,yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_SIZENWSE);
      DrawIcon(hdc,xCursor/2 + xCursor*7,yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_SIZENESW);
      DrawIcon(hdc,xCursor/2 + xCursor*8,yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_SIZEWE);
      DrawIcon(hdc,xCursor/2 + xCursor*9,yCursor/2, hCursor);

      hCursor = LoadCursor(0, IDC_SIZENS);
      DrawIcon(hdc,xCursor/2 + xCursor*10,yCursor/2,hCursor);

      // Загружаем и рисуем свой курсор,
      // указанный в файле описания ресурсов
      hCursor = LoadCursor(hInst, "AppCursor");
      DrawIcon(hdc,xCursor/2, yCursor/2 + yCursor*2,hCursor);

      EndPaint(hwnd, &ps);
      return 0;
    }

    case WM_DESTROY:
    {
      PostQuitMessage(0);
      return 0;
    }
  }
  return DefWindowProc(hwnd, msg, wParam, lParam);
}

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

hCursor = LoadCursor(0, IDC_ARROW);

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

DrawIcon(hdc, xCursor/2, yCursor/2, hCursor);

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

case WM_CREATE:
{
  xCursor = GetSystemMetrics(SM_CXCURSOR);
  yCursor = GetSystemMetrics(SM_CYCURSOR);
  break;
}

Файл определения модуля для приложения CURSOR представлен в листинге 1.17.


Листинг 1.17. Файл cursor\cursor.def


; =============================
; Файл определения модуля
; =============================
NAME        CURSOR
DESCRIPTION 'Приложение CURSOR, (C) 1994, Frolov A.V.'
EXETYPE     windows
STUB        'winstub.exe'
STACKSIZE   5120
HEAPSIZE    1024
CODE        preload moveable discardable
DATA        preload moveable multiple

[Назад] [Содеожание] [Дальше]