Code: Select all
{
DCAL program Hello_World is intended to be a simple introduction to
Re-entrant State Machines as they pertain to DCAL for Delphi programming.
In this example the Re-entrant State Machine is documented in order to
illustrate the fundamental structure of a DataCAD macro.
By definition a state machine is a program that stores the status of something
at a given time and can operate on input to change the status and/or cause an
action or output to take place for any given change.
Programmers originally implemented a 're-entrant' state machine in DataCAD
to make it possible to stack commands. For example, if you're in the middle of
one command such as drawing a line, you can press a keyboard interrupt such as
[/] which will interrupt the line drawing command and bring you to the zoom
command. When you exit the zoom command, the re-entrant state machine brings
you back to the previous command on the stack; drawing a line.
A hotkey, on the otherhand, breaks out of the current command, clears the
command stack, and brings you to a new command. For example, if you're drawing
a line and press the [C] key, DataCAD will 'break' out of the line command and
bring you to the copy command.
There are four main sections within DataCAD's state machine.
Section 1: AlSize
This is where DataCAD's state machine allocates memory for your local variables.
Section 2: Afirst
This is where you initialize the local state and other variables. Tasks you
define here will only be performed once.
Section 3: Aagain
This is where DataCAD returns after it has performed an action for you. The
action variable will be equal to Aagain until you set the result to XDone.
Section 4: Alast
If your macro has been interrupted by user action such as pressing a hotkey,
DataCAD will set the action to Alast to give you the opportunity to clean up any
temporary data before exiting to the new command.
}
library Hello_World;
uses
// Sharemem, { Do not use. All DataCAD parameters are limited to short strings}
Dialogs,
//DCAL for Delphi Header Files
UConstants in '..\Header Files\UConstants.pas',
UInterfaces in '..\Header Files\UInterfaces.pas',
UInterfacesRecords in '..\Header Files\UInterfacesRecords.pas',
URecords in '..\Header Files\URecords.pas',
UVariables in '..\Header Files\UVariables.pas';
{$E dmx} // dmx is the extension for DCAL Dll's
// The values stored in the records you define here will be rememberd
// between dispatcher calls because they are pushed onto the command stack
type
HelloL = record
state: asint;
case byte of
0:
(getp: getpointArg);
end;
PHelloL = ^HelloL;
// Main Function (Re-entrant State Machine)
function hello_world_main(act: action; pl, pargs: Pointer): wantType;
// The values stored in variables you define here will not be retained
// between dispatcher calls because they are not pushed onto the stack
var
retval: asint;
l: PHelloL;
begin
l := PHelloL(pl);
{ Section 3: Aagain - Return to menus and wait for input }
if act = Aagain then begin // Re-entrant section
case l.state of
1: begin
// If a key was pressed or a menu button was selected...
if l.getp.Result = res_escape then begin
case l.getp.key of
f1:
l.state := 2; { Say Hello }
s0:
l.state := 0; { Exit }
end;
end;
end;
2:
l.state := 1;
else
l.state := 0;
end;
end;
{ Section 2: Afirst - Tasks to perform only once }
if act = Afirst then begin // Initialize local state and other variables
wrterr('Hello World Macro - Version 1.0');
l.state := 1;
end
{ Section 4: Alast - If your macro has been interrupted by user action }
else if act = Alast then begin
// If you're going to get blown off the stack...
// (i.e.) the user pressed a hotkey,
// then this is your last chance to clean up temporary data.
end
{ Section 1: AlSize - Allocate memory on stack for local variables }
else if act = AlSize then begin
SetLocalSize(sizeof(l^));
end;
if act <> AlSize then begin { Action section - Invoke the Dispatcher }
case l.state of
1: begin
wrtlvl('Hello'); // Set Menu Title
lblsinit;
lblset(1, 'Say Hello', 'Select this button to say "Hello"');
lblset(20, 'Exit', '"Goodbye"');
lblson;
getpoint(l.getp, retval);
end;
2: begin
wrtmsg('Hello World!', true);
MessageDlg('Hello World!', mtConfirmation, [mbOK], 0);
callNone(retval); { You have to tell the dispatcher to do something.
In this case, tell the dispatcher to do nothing. }
end;
else
retval := XDone; // Don't go back to Aagain
end;
end;
Result := retval;
end;
// End of Main Function (Re-entrant State Machine)
function Main(dcalstate: asint; act: action; pl, pargs: Pointer)
: wantType; stdcall;
begin
case dcalstate of
XDcalStateBegin:
Result := hello_world_main(act, pl, pargs);
else
Result := XDone; { Necessary }
end;
end;
exports
Main; { This is the entry function that will be called by DataCAD.
All DCAL Dlls will have this function. }
begin
end.