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

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

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

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

1.9. Использование плавающего меню в органе управления EDIT

Как вы знаете, орган управления, созданный на базе предопределенного класса "edit", является простым редактором текста. В приложении SMARTPAD, которое будет описано немного позже, используется интересный прием, позволяющий вызвать на экран плавающее меню простым нажатием правой клавиши мыши внутри окна редактирования. Причем меню окажется как раз около курсора мыши, так что для работы с меню вам не придется передвигать мышь на большое расстояние.

Для редактора текста внутри операционной системы Windows определена функция окна, выполняющая всю работу по редактированию текста, выделению фрагментов текста, копирование выделенного фрагмента в универсальный буфер обмена Clipboard и т. д. Когда вы устанавливаете курсор мыши в окно редактирования и нажимаете правую клавишу мыши, сообщение WM_RBUTTONDOWN попадает в функцию окна редактора текста.

Однако функция родительского окна, создавшая редактор текста, получает только сообщение с кодом WM_COMMAND, да и то только при выполнении определенных операций с текстом. Поэтому сколько бы вы не нажимали правую кнопку мыши в окне редактора текста, родительское окно об этом никогда не узнает.

Как же нам быть? Ведь нам надо не только определить момент, в который пользователь нажал правую кнопку мыши, но и узнать текущие координаты курсора мыши, чтобы создать плавающее меню в нужном месте экрана (недалеко от курсора мыши).

Так как встроенная функция окна, используемая редактором текста, перехватывает сообщение WM_RBUTTONDOWN и "не выпускает" его наружу, нам надо вставить собственный обработчик сообщений перед стандартным для класса окна "edit".

Программный интерфейс Windows позволяет нам это сделать.

Определим в программе две переменные:

WNDPROC lpfnEditOldWndProc;
WNDPROC lpfnEditWndProc;

Эти переменные будут использоваться для хранения, соответственно, указателя на старую функцию окна редактора текста и указателя на новую функцию окна редактора текста.

Для получения адреса функции окна редактора текста мы воспользуемся функцией GetWindowLong :

lpfnEditOldWndProc =
  (WNDPROC)GetWindowLong(hEdit, GWL_WNDPROC);

Если в качестве второго параметра этой функции передать константу GWL_WNDPROC , функция вернет адрес функции окна, идентификатор которого задан первым параметром. Возвращенное функцией GetWindowLong значение мы сохраним в переменной lpfnEditOldWndProc, так как наша функция окна, встроенная до стандартной, после выполнения своей задачи должна вызвать стандартную функцию окна (иначе редактор текста не будет работать).

Итак, адрес старой функции окна мы узнали. Теперь надо подготовить новую функцию окна, которая, если пользователь нажмет на правую клавишу мыши, будет выводить на экран плавающее меню. Вот эта функция:

// ======================================================
// Новая функция окна для редактора текста
// ======================================================
LRESULT CALLBACK _export
EditWndProc(HWND hwnd, UINT msg, WPARAM wParam,
   LPARAM lParam)
{
  // Если в окне редактора текста пользователь нажал
  // правую клавишу мыши, выводим в позиции курсора мыши
  // плавающее меню
  if(msg == WM_RBUTTONDOWN)
  {
    HMENU hmenuPopup;
    POINT pt;

    // Преобразуем координаты курсора мыши в экранные
    pt = MAKEPOINT(lParam);
    ClientToScreen(hwnd, &pt);

    // Создаем пустое временное меню
    hmenuPopup = CreatePopupMenu();

    // Заполняем временное меню
    AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
         CM_FILENEW, "&New");
    AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
         CM_FILEOPEN, "&Open");
    AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
         CM_FILESAVE, "&Save");
    AppendMenu(hmenuPopup, MF_SEPARATOR, 0, 0);
    AppendMenu(hmenuPopup, MF_BYCOMMAND | MF_ENABLED,
         CM_FILEEXIT, "E&xit");

    // Выводим плавающее меню в позиции курсора мыши
    TrackPopupMenu(hmenuPopup,
      TPM_CENTERALIGN | TPM_LEFTBUTTON,
      pt.x, pt.y, 0, hwndMain, NULL);

    // Удаляем временное меню
    DestroyMenu(hmenuPopup);
  }
  // Вызываем старую функцию окна редактора текста
  return CallWindowProc(lpfnEditOldWndProc,
     hwnd, msg, wParam, lParam);
}

Обратите внимание, что после завершения работы новая функция окна вызывает старую функцию окна. Так как ваше приложение не может вызывать функцию окна непосредственно, мы вызываем старую функцию окна при помощи функции CallWindowProc .

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

Однако для того чтобы вместо стандартной функции окна вызывалась наша, ее необходимо подключить при помощи функции SetWindowLong :

lpfnEditWndProc =
 (WNDPROC)MakeProcInstance((FARPROC)EditWndProc,hInst);
SetWindowLong(hEdit, GWL_WNDPROC,
  (LONG)lpfnEditWndProc);

Перед вызовом функции мы создаем переходник и сохраняем его адрес в переменной lpfnEditWndProc.

Сразу после возвращения управления из функции SetWindowLong наша новая функция окна включается в работу, пропуская через себя все сообщения, предназначенные для стандартной функции окна редактора текста.

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

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