|
|
|
|
|
|
|
|
|
|
| { MACROREC - Component
permits simplified macro handling for buttons |
|
PURPOSE - Demonstrates
dynamic overriding of event handlers in order to create a more synergistic component. |
|
ASSUMES = For simplicity
this component assumes that controls are not dynamically added and deleted to the
form. } |
| unit Macrorec; |
| interface |
| uses |
|
Windows, Messages, SysUtils,
Classes, Graphics, Controls, |
|
Forms, Dialogs, StdCtrls; |
| const |
|
MacroSep = ':';{ Macro command separator } |
|
EmptyMacro = '';{ Empty macro } |
| type |
|
TEventSaver = class { Create a type to save and restore original } |
|
|
FHandle: HWnd; { OnClick event. The window
handle is saved } |
|
|
FOnEvent: TNotifyEvent;
{ in order to re-broadcast
window messages. } |
|
end; |
|
TMacroRecorder = class(TComponent) |
|
private |
|
|
FRecording: Boolean; { Is a macro currently being
recorded? } |
|
|
FEventList: TStringList;
{ List of saved
event handlers } |
|
|
FMacro: String; {
Displayable storage for short macros } |
|
|
FOwnerCreate: TNotifyEvent;
{ Override form's
OnCreate event } |
|
protected |
|
|
procedure RecordEvent(Sender: TObject);
dynamic; |
|
|
procedure DoCreate(Sender: TObject); dynamic; |
|
public |
|
|
constructorCreate(AOwner: TComponent); override; |
|
|
destructor Destroy; override; |
|
|
procedure BeginMacro; |
|
|
procedure EndMacro; |
|
|
procedure Playback; |
|
published |
|
|
property Recording: Boolean
read FRecording write FRecording; |
|
|
property Macro: String
read FMacro; |
|
end; |
| procedure Register; |
| implementation |
| constructor TMacroRecorder.Create(AOwner:
TComponent); |
| begin |
|
inherited Create(AOwner); |
|
FMacro := EmptyMacro; { Initialize the properties
} |
|
FRecording := False; |
|
FEventList := TStringList.Create; |
|
if not (csDesigning in ComponentState)
and (AOwner is TForm)
then |
|
|
begin |
|
|
|
FOwnerCreate := TForm(AOwner).OnCreate;
{ Override event
handler } |
|
|
|
TForm(AOwner).OnCreate
:= DoCreate; |
|
|
end; |
| end; |
| destructor TMacroRecorder.Destroy; |
| var |
|
EventIdx: Integer; { Event handler index } |
| begin |
|
for EventIdx:=FEventList.Count-1downto 0 do |
|
|
begin |
|
|
|
TEventSaver(FEventList.Objects[EventIdx]).Free; |
|
|
|
FEventList.Delete(EventIdx); |
|
|
end; |
|
FEventList.Free; |
|
inherited Destroy; |
| end; |
| { DoCreate - Saves away
existing event handlers for all button components } |
| { and calls the original
user OnCreate event handler } |
| { NOTE - Load contains
a key technique for dynamic overriding events } |
| procedure TMacroRecorder.DoCreate(Sender:
TObject); |
| var |
|
CompIdx: Integer; { The index of the current component
on the form } |
|
SavedEvent: TEventSaver;
{ Local pointer
to SavedEvent } |
|
Comp: TComponent; |
| begin |
|
if Owner is TForm then { Limit the example to form wide macros } |
|
|
begin |
|
|
|
for CompIdx := 0 to
Owner.ComponentCount-1
do |
|
|
|
|
begin |
|
|
|
|
|
Comp := Owner.Components[CompIdx]; |
|
|
|
|
|
{ Event overriding is
method specific, thus type specific } |
|
|
|
|
|
if (Comp is TButton)
or |
|
|
|
|
|
|
(Comp is TCheckBox)
then { Limit macros
to controls } |
|
|
|
|
|
|
begin |
|
|
|
|
|
|
|
SavedEvent := TEventSaver.Create; |
|
|
|
|
|
|
|
with SavedEvent do |
|
|
|
|
|
|
|
|
begin |
|
|
|
|
|
|
|
|
|
if Comp is
TButton then |
|
|
|
|
|
|
|
|
|
with TButton(Comp) do
{ Upcast component } |
|
|
|
|
|
|
|
|
|
begin |
|
|
|
|
|
|
|
|
|
FOnEvent := OnClick; { Save old event } |
|
|
|
|
|
|
|
|
|
FHandle := Handle; { Save windows handle } |
|
|
|
|
|
|
|
|
|
OnClick := RecordEvent
{ Override event
} |
|
|
|
|
|
|
|
|
|
end; |
|
|
|
|
|
|
|
|
|
if Comp is
TCheckBox then |
|
|
|
|
|
|
|
|
|
with TCheckBox(Comp) do |
|
|
|
|
|
|
|
|
|
begin |
|
|
|
|
|
|
|
|
|
FOnEvent := OnClick; |
|
|
|
|
|
|
|
|
|
FHandle := Handle; |
|
|
|
|
|
|
|
|
|
OnClick := RecordEvent |
|
|
|
|
|
|
|
|
|
end; |
|
|
|
|
|
|
|
|
end; { end saving event } |
|
|
|
|
|
|
|
{ Now index the event
handler by name for retrieval later } |
|
|
|
|
|
|
|
FEventList.AddObject(Comp.Name,
SavedEvent) |
|
|
|
|
|
|
end { end limiting macros to controls } |
|
|
|
|
end { end loop for each form component } |
|
|
end; { end limiting macros to forms } |
|
|
if Assigned(FOwnerCreate) then FOwnerCreate(Sender); |
| end; |
| procedure TMacroRecorder.BeginMacro; |
| begin |
|
FMacro := EmptyMacro; |
|
FRecording := True |
| end; |
| procedure TMacroRecorder.EndMacro; |
| begin |
|
FRecording := False |
| end; |
| { Plays back the events
as recorded by triggering the original event as } |
| { if the user initiated
the message. } |
| procedure TMacroRecorder.Playback; |
| var |
|
MacroIdx: Integer; { Index into macro string } |
|
EventIdx: Integer; { Recorded index of the event
to playback } |
|
MacroCopy: String; { Local copy of macro } |
|
MacroCmd: String; {
Current macro command } |
|
wParm: Word; { parameters for windows API
calls } |
|
LParm: LongInt; { to send simulated mouse clicks
} |
| begin |
|
wParm := 0; |
|
LParm := 0; |
|
MacroCopy := FMacro; |
|
repeat {
Process all macro commands } |
|
|
MacroIdx := Pos(MacroSep,
MacroCopy); |
|
|
if MacroIdx > 0 then |
|
|
|
begin |
|
|
|
|
MacroCmd := Copy(MacroCopy,
1, MacroIdx-1); |
|
|
|
|
Delete(MacroCopy, 1, MacroIdx); |
|
|
|
|
EventIdx := FEventList.Indexof(MacroCmd);
{ Just in case
we playback } |
|
|
|
|
if FRecording then
FRecording := False; {
a button that begins a } |
|
|
|
|
|
|
|
|
|
{ macro, turn off recording
} |
|
|
|
|
PostMessage(TEventSaver(FEventList.Objects[EventIdx]).FHandle, |
|
|
|
|
|
WM_LBUTTONDOWN, wParm,
LParm); { Send
a mouse click event } |
|
|
|
|
PostMessage(TEventSaver(FEventList.Objects[EventIdx]).FHandle, |
|
|
|
|
|
WM_LBUTTONUP, wParm, LParm);
{ to the control
} |
|
|
|
|
Application.ProcessMessages;
{ Allow Windows
to process the queue } |
|
|
|
end |
|
until MacroIdx < 0 {end processing } |
| end; |
| procedure TMacroRecorder.RecordEvent(Sender:
TObject); |
| var |
|
EventIdx: Integer; { Index of event to save in
macro } |
| begin |
|
EventIdx := FEventList.Indexof(TControl(Sender).Name); |
|
if EventIdx -1 then |
|
|
begin { Add event to macro, then call original
handler if appropriate } |
|
|
|
if FRecording then
FMacro := FMacro+TControl(Sender).Name+MacroSep; |
|
|
|
if Assigned(TEventSaver(FEventList.Objects[EventIdx]).FOnEvent) then |
|
|
|
|
TEventSaver(FEventList.Objects[EventIdx]).FOnEvent(Sender); |
|
|
end; |
| end; |
| procedure Register; |
| begin |
|
RegisterComponents('Informant',
[TMacroRecorder]); |
| end; |
| end. |
|
Figure
3
|