Delphi 3. Библиотека программиста

Смысловые оттенки


А где-то на другом конце города в разбитое окно убогой комнаты занесло выхлопные газы последнего автобуса, отъезжающего в Бэйпорт. Не обращая внимания на уличный шум, загадочная фигура потянулась к уголку Дневника и перевернула очередную страницу.

Дневник №16, 21 марта. Сегодня ко мне обратился новый клиент — человек по имени Барри Маунтбенк. Он занимается «раскруткой» перспектив ного политика и хочет, чтобы я написал специальный текстовый редактор, который мог бы создавать различные описания для одних и тех же событий в зависимости от того, откуда подует ветер.

Я решил, что было бы полезно написать демонстрационную программу, которая покажет, как в Delphi-приложении организовать фильтрацию и преобразование нажатых клавиш, чтобы преобразовать их к требуемому виду. Когда я предложил это клиенту, он загорелся энтузиазмом.

Методика чрезвычайно проста. Объект Application умеет обрабатывать событие OnMessage, с помощью которого можно подключиться непосредственно к цепочке сообщений для всех компонентов приложения.

Я решил создать несложную программу, которая бы демонстрировала три основных положения:

  1. Подменить одну нажатую клавишу другой несложно.
  2. Замены клавиш, выполняемые таким образом, автоматически передаются всем компонентам приложения, даже другим формам.
  3. Замену клавиш можно включать и выключать «на ходу».

На рис. 14.3 изображен созданный мной пример с двумя формами. При установке соответствующего переключателя режим фильтрации включается или выключается. При включенной фильтрации прописная буква «a» меняется на строчную, и наоборот, клавиша Backspace работает как Delete, а клавиши Delete и Shift+F5 выполняют прежнюю функцию клавиши Backspace. Исходный текст программы содержится в файлах KSMAIN.PAS и KSFORM2.PAS (см. листинги 14.7 и 14.8).



Рис. 14.3. Фильтрация нажатых клавиш

Листинг 14.7. Главная форма демонстрационной программы для замены символов

{——————————} {Замена символов (демонстрационная программа)} {KSMAIN.PAS : Главная форма } {Автор: Эйс Брейкпойнт, N.T.P. } {При содействии Дона Тейлора } { } { Приложение, демонстрирующее возможности } {избирательной фильтрации и замены символов, } { вводимых с клавиатуры. } { } { Написано для *High Performance Delphi 3 } Programming* } { Copyright (c) 1997 The Coriolis } Group, Inc. } { Дата последней редакции 22/4/97 } {————————} unit KsMain; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, KSForm2, ExtCtrls; type TForm1 = class(TForm) ExitBtn: TButton; ShowBtn: TButton; Form1Memo: TMemo; Bevel1: TBevel; KeyHandlerRBGroup: TRadioGroup; procedure FormCreate(Sender: TObject); procedure ExitBtnClick(Sender: TObject); procedure ShowBtnClick(Sender: TObject); private procedure OnAppMessage(var Msg : TMsg; var Handled : Boolean); public { Public declarations } end; const Shifted : Boolean = False; var Form1: TForm1; implementation {$R *.DFM} procedure TForm1.OnAppMessage(var Msg : TMsg; var Handled : Boolean); begin if KeyHandlerRBGroup.ItemIndex = 1 then with Msg do begin case Message of WM_KEYDOWN : begin case WParam of VK_SHIFT : Shifted := True; VK_F5 : if Shifted then WParam := VK_BACK; VK_DELETE : WParam := VK_BACK; VK_BACK : WParam := VK_DELETE; end; { case } end; WM_CHAR : begin case chr(WParam) of "a" : WParam := ord("A"); "A" : WParam := ord("a"); end; { case } end; WM_KEYUP : begin case WParam of VK_SHIFT : Shifted := False; end; { case } end; end; { case } end; { with } end; procedure TForm1.FormCreate(Sender: TObject); begin Application.OnMessage := OnAppMessage; KeyHandlerRBGroup.ItemIndex := 0; end; procedure TForm1.ExitBtnClick(Sender: TObject); begin Close; end; procedure TForm1.ShowBtnClick(Sender: TObject); begin Form2.Show; end; end.

Листинг 14.8. Вспомогательная форма демонстрационной программы

для замены символов

{—————————} {Замена символов (демонстрационная программа) } {KSFORM2.PAS : Вспомогательная форма } {Автор: Эйс Брейкпойнт, N.T.P. } {При содействии Дона Тейлора } { } {Приложение, демонстрирующее возможности } {избирательной фильтрации и замены символов, } {вводимых с клавиатуры. } { } {Написано для *High Performance Delphi 3 } {Programming* } {Copyright (c) 1997 The Coriolis Group, Inc. } { Дата последней редакции 22/4/97 } {————————} unit KsForm2; interface uses Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls; type TForm2 = class(TForm) CloseBtn: TButton; Bevel1: TBevel; Form2Memo: TMemo; procedure CloseBtnClick(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form2: TForm2; implementation {$R *.DFM} procedure TForm2.CloseBtnClick(Sender: TObject); begin Close; end; end.

Кроме того, я заглянул в исходный текст объекта TApplication и посмотрел, как в нем организована обработка сообщений по умолчанию и как написан ный мной обработчик события OnMessage участвует в этом процессе. Точнее, меня интересовало, что я должен делать с переменной Handled, передаваемой обработчику событий? В листинге 14.9 приведен исходный текст метода ProcessMessage класса TApplication, вызываемого в бесконечном цикле обработ ки сообщений приложения.

Листинг 14.9. Исходный текст метода ProcessMessage класса TApplication

function TApplication.ProcessMessage: Boolean; var Handled: Boolean; Msg: TMsg; begin Result := False; if PeekMessage(Msg, 0, 0, 0, PM_REMOVE) then begin Result := True; if Msg.Message <>WM_QUIT then begin Handled := False; if Assigned(FOnMessage) then FOnMessage (Msg, Handled); if not IsHintMsg(Msg) and not Handled and not IsMDIMsg(Msg) and not IsKeyMsg(Msg) and not IsDlgMsg(Msg) then begin TranslateMessage(Msg); DispatchMessage(Msg); end; end else FTerminate := True; end; end;

Из листинга 14.9 становится ясно, откуда взялась переменная Handled. Как нетрудно убедиться, метод ProcessMessage вызывается для обнаружения и обработки сообщений, находящихся в очереди. Обнаруженное сообщение удаляется из очереди. Если это сообщение WM_QUIT, переменной FTerminate присваивается значение True; в противном случае Handled присваивается False и вызывается обработчик OnMessage (если он был определен). Если при возвращении из него переменная Handled остается равной False и сообщение не относится к некоторым категориям (сообщения подсказок, сообщения MDI, уведомляющее сообщение от элемента или диалоговое сообщение), для обработки вызываются стандартные процедуры TranslateMessage и DispatchMessage. Очевидно, если переменной Handled в обработчике события OnMessage присвоить значение True, дальнейшая обработка сообщения прекращается. Я хочу заменить одну нажатую клавишу другой и продолжить обработку. Следовательно, значение переменной Handled не должно изменяться.

Мой обработчик OnMessage устроен достаточно просто. Если установлен переключатель Filtered, оператор case отбирает нужные сообщения и заменяет клавиши, при этом для управляющих символов используются константы виртуальных клавиш, определенные в Windows. Следует обратить внимание на один момент: клавиши, нажимаемые в сочетании с Alt, Ctrl и Shift, опознаются в два этапа. Поскольку процедура получает всего одну клавишу, она не знает, какие из управляющих клавиш были при этом нажаты. Мне пришлось отдельно обрабатывать нажатия и отпускания управляющих клавиш. Для этого я ищу константу VK_SHIFT в параметре wParam, передаваемом с сообщениями WM_KEYDOWN и WM_KEYUP, и в случае ее обнаружения — сохраняю информацию о регистре в логической переменной.

Обработчик OnMessage принадлежит приложению, а не главной форме, так что он не задается в виде свойства в режиме конструирования. Вместо этого он подключается во время выполнения в обработчике OnCreate главной формы.

Конец записи (21 марта).



Содержание раздела