DEFINITION MODULE DMMaster;
(*******************************************************************
Module DMMaster ('Dialog Machine' DM_V3.0)
Copyright (c) 1985-2006 by Andreas Fischlin, Alex Itten and
ETH Zurich.
Purpose Manages all user dialog for a 'Dialog Machine' program
and controls all remaining modules of the 'Dialog Machine'.
It intercepts so-called user events as issued by the user
and dispatches program events as needed to the
'Dialog Machine' program.
Remarks The 'Dialog Machine' provides means to define,construct,
manage, and use the following objects of a modern
user interface: pull-down (or pop-up) menus, windows
supporting pointing device input and window output,
modal dialog windows and alerts to display system
anomalies or call the user attention to issue
warnings.
This module provides all facilities needed to pass
the control between the application specific program
parts and the 'Dialog Machine' back and forth. Once
started (see procedure RunDialogMachine), the
control remains with the 'Dialog Machine' until a
user event (mouse is clicked or a key is pressed)
occurs that can no longer be handled automati- cally
and which requires some appli- cation specific
actions. In the latter cases the 'Dialog Machine'
calls the corresponding procedure destined to handle
the particular event.
Typically the user event handling procedures are
provided by the application program, i.e. they are
installed by the application program before any user
events can occur, in particular before procedure
RunDialogMachine is called. Such an installation
prevents the 'Dialog Machine' from responding to
user events in just the standard way and lets the
application perform its specific tasks, i.e. it
provides the mechanism to temporarily pass control
from the 'Dialog Machine' to the application
specific program sections. Hence an application
consists not of straight-line code, rather it
consists of a set of procedures to be called at
appropriate times, i.e. a user event such as a menu
command selection, or the moving respectively
resizing of a window. Such a program structure is
called inverted program control. Typically an
application written with the 'Dialog Machine'
consists of a initialization, during which all
necessary procedures handling user events are
installed in the 'Dialog Machine', and the
subsequent activation of the 'Dialog Machine'.
User events handled automatically by the "Dialog
Machine" are: selection of a menu item, activation,
dragging resizing, updating (including the content),
and closing of a window, input by means of dialog
items allowing entry of e.g. numbers, various kinds
of buttons and so-called check boxes, plus the
display of standard warning or error messages by means
of alerts.
User events which are handled by default by the
'Dialog Machine' alone but which can be changed to
produce a program event are: initialization of the
start up state of the 'Dialog Machine', mouseclick
in the front window, scrolling in the front window,
bringing a window to or removing it from the front,
pressing a key, and closing a
window.
User events which produce always a program event and
which are always passed to the application are:
execution of a procedure associated with a menu
command, and the execution of a procedure attached
to a so-called push button.
The 'Dialog Machine' consists of several
modules such as DMMaster, DMMenus, DMLanguage,
DMWindows, DMWindIO, DMSystem, DMBase,
DMErrorMsgs, DMStrings, and DMConversions plus
optional modules such as DMEntryForms, DMAlerts,
DMEditFields, DMFiles, DMClipBoard, DMPrinting,
DMSaveOutput, and DM2DGraphs etc. which can be used
depending on the actual programmer's needs.
The Dialog Machine's purpose is to separate the
application programming from the system programming
in order to provide a modern user interface with
win- dows and a pointing device like a mouse. The
particular implementation for the Macintosh gives
the application programmer indirect access to the
toolbox, quickdraw and operating system routines by
providing most of the essential features necessary
to write standard Macintosh applications, which
conform not only with the user interface of the
'Dialog Machine' but also with the "Macintosh User
Interface Guidelines" (Inside Macintosh, Promotional
ed., March 15, 1985). Hence the 'Dialog Machine'
does not only substantially simplify the usage but
also the programming of an application program.
This module belongs to the 'Dialog Machine'.
Programming
o Design
Andreas Fischlin 22/11/1985
o Implementation
Andreas Fischlin 22/11/1985
Alex Itten 29/01/1987
ETH Zurich
Systems Ecology
CHN E 35.1
Universitaetstrasse 16
8092 Zurich
SWITZERLAND
URLs:
<mailto:RAMSES@env.ethz.ch>
<http://www.sysecol.ethz.ch>
<http://www.sysecol.ethz.ch/SimSoftware/RAMSES>
Last revision of definition: 02/04/2000 AF
*******************************************************************)
FROM DMWindows IMPORT Window;
(*******************************************)
(*##### Types and Result Variable #####*)
(*******************************************)
TYPE
MouseHandlers = (WindowContent,
BringToFront, RemoveFromFront,
RedefWindow, CloseWindow);
MouseHandler = PROCEDURE (Window);
(* type of a procedure handling mouse events *)
KeyboardHandler = PROC;
(* type of a procedure handling keyboard events *)
VAR
MasterDone: BOOLEAN; (*indicates whether an installation
has been successful (installations
may fail because of exceeded level
of subprograms)*)
(********************************)
(*##### Setup Handlers #####*)
(********************************)
PROCEDURE AddSetupProc(sup: PROC; priority: INTEGER);
(*
Use this procedure to install a set-up procedure to be
executed just after the 'Dialog Machine' has been started by
calling procedure RunDialogMachine; for instance if you wish
to start your program with a particular action, e.g. the
creation of a window, or the initial setting of menu commands
etc. You may install any number of set-up procedures. They
will all be executed in reverse order of their installation.
However, set-up procedures with lower priority will be called
before such with a higher priority. First come those with
priority 0, latest those with the priority MAX(INTEGER).
By default an empty procedure is assigned, i.e. the default
set-up when starting up the 'Dialog Machine' is just the
display of the menubar (see module DMMenus) and the
background (see module DMWindows).
*)
PROCEDURE RemoveSetupProc(sup: PROC);
(* Removes set-up procedure sup *)
(******************************)
(*##### Mouse Events #####*)
(******************************)
PROCEDURE AddMouseHandler(which: MouseHandlers; mhp: MouseHandler; priority: INTEGER);
(*
Installs procedures being called automatically by the "Dialog
Machine" in case one of the following events has to be
handled:
- The mouse handler of type WindowContent is called if
the mouse has been clicked in the content region of the
currently frontmost window (scroll bars excluded).
- The mouse handler of type BringToFront is called if the mouse
has been clicked within a window which is behind the frontmost
window. This is the right moment to provide some visual
feedback, such as redrawing a scroll bar as active, since the
bringing of a window to the front implies its activation.
- The mouse handler of type RemoveFromFront is called if the
mouse has been clicked within a window which is the frontmost
window. This is the right moment to provide some visual
feedback, such as redrawing a scroll bar as inactive, since the
removing of a window from the front implies its
deactivation.
- RedefWindow is called if the window has been redefined,
e.g. by clicking within the resize symbol in the lower
right corner or the zoom box in the upper right corner of
the window title bar. This is the right moment to rescale
and redraw graphical objects related to the current window
size.
- CloseWindow is called if the window is about to be
closed, just before it is actually closed. This is the
right moment e.g. to change the state of the "Dialog
Machine" if it depends on the presence of particular
windows. (For the saving of objects associated with windows,
see also DMWindows.CloseHandler).
You may install any number of mouse handlers. They
will all be executed in reverse order of their installation.
However, mouse handlers with lower priority will be called
before such with a higher priority. First come those with
priority 0, latest those with the priority MAX(INTEGER).
By default all assigned procedures are empty, i.e. all events
listed above are just swallowed by the 'Dialog Machine' and
no particular handling of the listed mouse events takes place.
*)
PROCEDURE RemoveMouseHandler(which: MouseHandlers; mhp: MouseHandler);
(* Removes mouse handler procedure mhp of type which *)
(*******************************************)
(*##### Reading From the Keyboard #####*)
(*******************************************)
PROCEDURE AddKeyboardHandler(khp: KeyboardHandler; priority: INTEGER);
(*
AddKeyboardHandler installs procedure khp which is called in
case a key is pressed which does not invoke an action which
can be handled by the 'Dialog Machine'. Keyboard actions
always handled immediately by the 'Dialog Machine', i.e.
without calling the procedure khp, are e.g. keyboard
equivalents for a menu command selection. You may install any
number of keyboard handlers. They will all be executed in
reverse order of their installation. However, handlers with
lower priority will be called before such with a higher
priority. First come handlers with priority 0, latest those
with priority MAX(INTEGER).
Typically in the khp you call the procedure InspectKey
(see below) to inspect the key. In case the handler
decides to accept the key, KeyAccepted should be called
at the end of khp (see below). Once any keyboard handler
has called KeyAccepted, no more handlers will be called
by the 'Dialog Machine'. In case the handler shall
accept any keyboard event unconditionally, you may also
simply call Read or BusyRead instead of the statement
sequence InspectKey,...KeyAccepted.
Example 1 (Recommended policy):
PROCEDURE MyGoodAliasCharProvider;
VAR ch: CHAR; m: BITSET;
BEGIN
InspectKey(ch,m);
IF (CAP(ch)="A") AND (m={command}) THEN
KeyAccepted; MySelectAll;
END(*IF*);
END MyGoodAliasCharProvider;
…
AddKeyboardHandler( MyGoodAliasCharProvider , 0 );
…
Example 2 (not recommended policy):
PROCEDURE MyBadAliasCharProvider;
VAR ch: CHAR;
BEGIN
Read(ch);
InspectKey(ch,m);
IF (CAP(ch)="A") AND (m={command}) THEN
MySelectAll;
ELSE
(* can't pass it on, Read has accepted it definitively!! *)
END(*IF*);
END MyBadAliasCharProvider;
Above keyboard handler is not recommended, since it
does not give a chance to any other keyboard
handlers to handle the keyboard event, in case
it would detect that it is not interested in the
keyboard event.
Note: If no keyboard handler has been installed by the
'Dialog Machine' program, or none of the currently installed
handlers does accept the key, the 'Dialog Machine' just
swallows the entered keys (It actually installs a handler
which unconditionally calls KeyAccepted with priority
MAX(INTEGER)). This behavior guarantees a proper
synchronization between key board pressing and corresponding
calls to handlers (see also SetKeyboardHandlerMode).
*)
PROCEDURE RemoveKeyboardHandler(khp: KeyboardHandler);
(* Removes keyboard handler procedure khp *)
PROCEDURE InspectKey(VAR ch: CHAR; VAR modifiers: BITSET);
(*
Inspect the pressed key together with the eventually also pressed
modifiers. Modifier keys are keys which do not cause a keyboard
event when they are pressed, they require that another, not a
modifier key is pressed simultaneously. The latter is returned by
InspectKey in ch, the simultaneously pressed modifier keys are
returned in the bitset modifiers. Use the constants exported by
DMKeyChars to inspect which modifier keys have been pressed (Note
that even the pressing of the mouse button can be used as a
modifier). Typically this procedure is called within a keyboard
handler procedure installed in the 'Dialog Machine'. In case the
calling handler wants to accept the key, it should call subsequently
KeyAccepted, to avoid that further keyboard handler procedures can
process the keyboard event.
In order to write keyboard handlers which are portable accross
computer platforms, use the module DMKeyChars while inspecting key
events within your handler. E.g. to learn about a simple cursor up
event, call InspectKey and compare the returned ch with
DMKeyChars.cursorUp and make sure the modifiers = {}, i.e.
InspectKey(ch,modifiers);
IF (ch=cursorUp) AND (m={}) THEN
ExecuteCursorUpCommand;
KeyAccepted;
END(*IF*);
See also below for other sample code of keybaord handlers.
*)
PROCEDURE KeyAccepted;
(*
Typically called from within a keyboard handler. Once the
keyboard handler has analyzed the pressed key, for instance by
calling InspectKey, and decides to accept the key, this
procedure should be called. Its effect is that the "Dialog
Machine" stops calling further keyboard handlers.
*)
PROCEDURE Read(VAR ch: CHAR);
(* waits till a character is typed (see also BusyRead) *)
PROCEDURE SetKeyboardHandlerMode( readGetsThem: BOOLEAN; maxPriority: INTEGER);
PROCEDURE GetKeyboardHandlerMode(VAR readGetsThem: BOOLEAN; VAR maxPriority: INTEGER);
(*
Allows to temporarily disable the reading process, e.g. when
deactivating a window associated with a process which calls
Read, and to resume it later, e.g. when the window is brought
back again to the front.
To understand the function of the keyboard handler mode
consider the following facts: Normally the 'Dialog Machine'
handles keyboard events by calling the currently installed
keyboard handlers. In this mode Read has never a chance to
get a key, unless you call it from within a keyboard handler
or you call SetKeyboardHandlerMode(TRUE,…). Once the
keyboard handler mode is set to readGetsThem = TRUE, Read is
enabled to receive keyboard events. Read then loops forever
calling DialogMachineTask. The loop can only be terminated
if either a key is actually entered by the user or if the
'Dialog Machine' is quit. However, to temporarily disable
the keyboard polling process by procedure Read, you may call
SetKeyboardHandlerMode(FALSE,…).
A typical usage of this feature is to temporarily disable the
reading process in case the user brings his/her attention to
another process than the reading one, for instance by
clicking into another window than the one in which the
reading request, e.g. a prompt, has been written to. In the
latter case you should call SetKeyboardHandlerMode(FALSE,…) in
the window handler of the class removedFromFront (see
DMWindows WindowHandlers). To resume the reading as soon as
the window requesting the input is brought to the front
again, call SetKeyboardHandlerMode(TRUE,…) in the window
handler of the class broughtToFront (see DMWindows
WindowHandlers).
Note also that in the keyboard handler mode
readGetsThem=TRUE, any attempt to press a key while the
program is not executing a Read, results in a call to
SoundBell. This signals to the user, that all key entering
is currently ignored, unless the program actually executes a
reading request.
Finally it is also important to note that the user would be
confused, if some keys would not function the ordinary way,
e.g. a string field, a number editing field (see
DMEditFields), or keyboard equivalents for menu commands. The
latter are always recognized and a keyboard equivalent of a
menu command is of course not interpreted as a keyboard event
but as a menu command selection. Since the 'Dialog Machine'
allows to install additional keyboard equivalents to global
commands such as menu commands by means of keyboard handlers,
the user would be confused, if not some of these could remain
active, despite the fact that the mode is currently
readGetsThem=TRUE. Hence, there is a mechanism needed, which
allows to selectively call installed keyboard handlers.
Above goals are accomplished by means of the priority parameter
maxPriority. It indicates up to which priority (0 is highest,
MAX(INTEGER) the lowest) keyboard handlers are still
executed, regardless of the current keyboard handler mode.
E.g. calling SetKeyboardHandlerMode(TRUE,0) results in a mode
where any keyboard event is first handled by all handlers
with priority = 0, but by none with priority >0, before it is
passed to the procedure Read. Of course does the "Dialog
Machine" make the keyboard event only available to Read, if
none of the handlers has yet called KeyAccepted. Otherwise
the keyboard event is considered to have been handled
properly and has become meaningless for any subsequent
processing. SetKeyboardHandlerMode(TRUE,-1) results in
a suppressing of all currently installed keyboard handlers.
A good policy is to install with priority 0 only keyboard
handlers which selectively accept keys, i.e. call first
InspectKey and analyze whether they want to take the key or
not; otherwise they don't swallow the key, e.g. don't call
Read nor KeyAccepted, but leave if for further processing by
other handlers. A handler which takes all keys
unconditionally, e.g. by calling Read, should be installed
only with a lower priority, e.g. 100, so that it can be
disabled by calling SetKeyboardHandlerMode(TRUE,99) before
calling Read. Otherwise this handler would never allow a key
to arrive at the Read procedure. Note also, that the "Dialog
Machine" itself installs as a keyboard handler with priority
MAX(INTEGER) the procedure KeyAccepted.
Example:
Set up your 'Dialog Machine' program similar to the
following:
a) for keyboard events:
PROCEDURE MyGoodAliasCharProvider;
VAR ch: CHAR; m: BITSET;
BEGIN
InspectKey(ch,m);
IF (CAP(ch)="A") AND (m={command}) THEN
KeyAccepted; MySelectAll;
END(*IF*);
END MyGoodAliasCharProvider;
…
AddKeyboardHandler( MyGoodAliasCharProvider , 0 );
b) for mouse events, e.g. clicking into windows:
VAR
msdosWindow: Window; oldKBHMode, msdosActive: BOOLEAN;
oldKBHPrior: INTEGER;
PROCEDURE ActivateMSDOSWindow(u: Window);
BEGIN
DMMaster.GetKeyboardHandlerMode(oldKBHMode,oldKBHPrior);
DMMaster.SetKeyboardHandlerMode(TRUE,0);
msdosActive := TRUE;
END ActivateMSDOSWindow;
PROCEDURE DeactivateMSDOSWindow(u: Window);
BEGIN
DMMaster.SetKeyboardHandlerMode(oldKBHMode,oldKBHPrior);
msdosActive := FALSE;
END DeactivateMSDOSWindow;
PROCEDURE MSDOSForcedToForeGround;
BEGIN
IF WindowExists(msdosWindow) THEN
IF FrontWindow()<>msdosWindow THEN
PutOnTop(msdosWindow);
WHILE NOT msdosActive DO
DialogMachineTask; (* make sure activation is properly updated *)
END(*WHILE*);
END(*IF*);
SelectForOutput(msdosWindow);
ELSE
CreateWindow(msdosWindow,GrowOrShrinkOrDrag,WithVerticalScrollBar,
WithCloseBox, WithZoomBox, bottomLeft, defaultFrame,
"MS-DOS", AutoRestoreProc);
AddWindowHandler(msdosWindow,closing,DeactivateMSDOSWindow,0);
AddWindowHandler(msdosWindow,broughtToFront,ActivateMSDOSWindow,0);
AddWindowHandler(msdosWindow,removedFromFront,DeactivateMSDOSWindow,0);
END(*IF*);
END MSDOSForcedToForeGround;
…
…
Moreover you may install a menu command "MS-DOS…" associated
with the following procedure:
…
PROCEDURE MSDOSCommandInterpreter;
CONST dir = 1; copy = 2; … … exit = 32000;
TYPE Command = INTEGER;
VAR oldKBHMode,finished: BOOLEAN; oldMaxPrio: INTEGER;
msdosCmd, args: ARRAY [0..127] OF CHAR;
PROCEDURE ReadString(VAR s: ARRAY OF CHAR);
BEGIN
i:= 0;
WHILE (s[i]<>EOL) AND DialogMachineRunning() DO Read(s[i]); INC(i) END;
IF i<=HIGH(s) THEN s[i] := 0C END;
END ReadString;
PROCEDURE ParseMSDOSCmd(c: ARRAY OF CHAR; VAR args: ARRAY OF CHAR): Command;
BEGIN
Scan(c…
…
END ParseMSDOSCmd;
BEGIN
finished := FALSE;
REPEAT
MSDOSForcedToForeGround;
WriteString("prompt> "); ReadString(msdosCmd);
CASE ParseMSDOSCmd(msdosCmd,args) OF
dir : ShowDirectory;
| copy : MakeFileCopy(arg1,arg2);
| … : …
| … : …
| exit : finished := TRUE;
ELSE
WriteString("unrecognizable command."); WriteLn;
END(*CASE*);
UNTIL finished OR NOT DialogMachineIsRunning();
END MSDOSCommandInterpreter;
Such a code still allows the user to leave the MS-DOS interpreter
anytime and work with other 'Dialog Machine' objects, e.g. other
windows, menu commands etc. As soon the MS-DOS window is
activate by clicking into it and bringing it to the front,
the polling reading process by MS-DOS is resumed and it awaits
keyboard inputs from the user, before it continues processing.
P.S.: Remember that once you called
SetKeyboardHandlerMode(FALSE,…) during a reading process,
that you are fully responsible for a proper resuming of the
normal reading by calling SetKeyboardHandlerMode(TRUE,…).
Otherwise the callee of Read will not be able to continue and
will hang in the Read statement until the mode is resumed and
a key is actually entered. The only other way to terminate
this hanging is if the whole Dialog Machine is terminated
also.
*)
PROCEDURE BusyRead(VAR ch: CHAR);
(*returns immediately 0C if no character has actually been typed.
Interpretations are made to simulate on older Macintosh
models lacking the control key the following control
characters (non displayable) of the ASCII character set:
command^A .. command^Z : 01C .. 32C (CTRL ^ alpha)
command^a .. command^z : 01C .. 32C (CTRL ^ alpha)
command^[ : 33C (CTRL ^ "[")
command^] : 34C (CTRL ^ "]")
command^\ : 35C (CTRL ^ "\")
command^^ : 36C (CTRL ^ "^")
command^_ : 37C (CTRL ^ "_")
command^3 .. command^7 : 33C .. 37C (CTRL ^ number)
e.g. CTRL^3 = ESC = 33C (oktal)
IMPORTANT: Note, if the 'Dialog Machine' is in the so-called batch
mode (cf. DialogMachineIsInBatchMode) this routine functions like
Read and can't return 0C.
*)
PROCEDURE DoTillKeyReleased(p: PROC);
(* Executes p as long as key remains pressed *)
(********************************)
(*##### User Feedbacks #####*)
(********************************)
PROCEDURE ShowWaitSymbol;
PROCEDURE HideWaitSymbol;
(*
Calling procedure ShowWaitSymbol informs the user that an
action will now start which might cause him to wait for some
time. For instance call it just before reading a long file
from a floppy disk or performing some large computations.
This gives the user the assurance that his program is still
in perfect running order and that the reason for the delayed
response is only due to a slow action and not due to a silent
program crash.
On the Macintosh the shape of the omnipresent cursor is
changed to a waiting symbol (e.g. a hour-glass). DON'T FORGET
to call HideWaitSymbol after termination of the long action!
Note: Calling ShowWaitSymbol consecutively without calling
HideWaitSymbol inbetween, results in the display of a
so-called animated waiting symbol, i.e. the wait symbol will
change its shape with each call to ShowWaitSymbol. Hence,
for very lengthy actions it is recommended to call
ShowWaitSymbol regularly, e.g. in a loop, and not only once
at the begin of the action. On the Macintosh the Dialog
Machine supports up to 9 different cursor shapes. The first
is only shown once after calling for the first time
ShowWaitSymbol or after having called HideWaitSymbol
in between (resource of type 'CURS', ID=4). Any subsequent
call to ShowWaitSymbol shows another of the up to a maximum
of 8 cursors in an endless loop according to the list defined
in the resource of type 'acur' with ID=0.
*)
PROCEDURE Wait(nrTicks: LONGCARD);
(*Wait for nrTicks * 1/60 seconds *)
PROCEDURE SoundBell;
(*Beeps with the currently active sound*)
PROCEDURE PlayPredefinedMusic(fileName: ARRAY OF CHAR; musicID: INTEGER);
(*
Get a predefined piece of music from the resource fork of the
file "fileName". If the file specified by fileName couldn«t
be opened, then the default search strategy to find the
resource is followed (see Inside Macintosh: Resource
Manager). If no resource of type 'snd ' and id musicID could
be found no sound will be produced at all. When you generate
a stand alone application (With the MacMETH Linker Link using
option /A), you should copy all resources into your
application (e.g. with the ResEdit application). In this case
the resources will be found regardless which fileName you
have specified. NOTE: According to Inside Macintosh Volume
VI p. 22-18 only musicID numbers > 8191 are allowed to be
used by your application.
*)
(*************************************************)
(*##### Core Routines of Dialog Machine #####*)
(*************************************************)
PROCEDURE InitDialogMachine;
(*
Makes the current (sub)program level to a DM level so that
DMSystem.LevelIsDMLevel called on this level will from now
on return TRUE. This procedure is mainly exported for
compatibility reasons with the IBM PC version of the Dialog
Machine.
*)
PROCEDURE RunDialogMachine;
(*
Start and run the 'Dialog Machine' on the current program
level. Implicitly calls first InitDialogMachine and then
DMMenus.UseMenuBar.
*)
PROCEDURE DialogMachineIsRunning(): BOOLEAN;
(*
Returns whether the 'Dialog Machine' is currently running;
A request typically only made by the 'Dialog Machine' modules
themselves.
*)
PROCEDURE DialogMachineTask;
(*
Temporal control of events may be returned to the "Dialog
Machine" (e.g. activation of a menu etc.) by calling this
procedure typically from within a loop, e.g. a numerical
integration loop.
*)
PROCEDURE ForceDialogMachineIntoBatchMode(bm: BOOLEAN);
(*
Forces the 'Dialog Machine' to operate in a so-called batch
mode (bm is TRUE) or to operate in the so-called normal,
interactive mode (bm = FALSE). Basically this batch mode
emulates the behavior of an alternative implementation of
the 'Dialog Machine', i.e. the so-called 'Batch Dialog
Machine' which is the basis of RASS, the RAMSES Simulation
Server.
In the batch mode a call of procedure RunDialogMachine will
only result in the execution of all installed handlers and
the subsequent, automatic quitting of the 'Dialog Machine'.
No user interactions are exepected than the default ones.
E.g. a user dialog, which presents a window and displays
some information together with an OK and a Cancel button
will not await the user to press any button. Instead the
'Dialog Machine' assumes that the user has pressed the
default "OK" button and continue with whichever processing
it is programmed for.
In case the programmer has not foreseen a default answer,
the user's input is mandatory and the result of the
operation becomes ambiguous. Consequently the "Dialog
Machine" can't proceed with its processing since the post
condition of the input requesting operation is not defined.
The 'Dialog Machine' will then temporarily abandon the
batch mode and resume normal interactive mode, i.e. request
the user to provide the requested input in order to make an
unambiguous branching in the program as a consequence of
the input. If the 'Dialog Machine' is actually a "Batch
Dialog Machine" the entire 'Dialog Machine' program is
aborted if this situation of an ambiguous user input
request arises. E.g. calling DMFiles.GetExistingFile is
such an ambiguous user input request, where no default
answer can be provided by the programmer. The user has to
provide a selection of an existing file. Of course a
'Dialog Machine' in batch mode could assume the user has
cancelled the file opening dialog, however this is
typically of little interest to the processing in batch
mode and therfore batch mode is always abandoned whenever
DMFiles.GetExistingFile is called in batch mode. Instead
the programmer has an alternative, i.e. to write the batch
mode program such that such situations are avoided by using
e.g. procedure DMFiles.Lookup or to provide for all
requests of user inputs always a default answer. Once the
user has satisfied a mandatory input request the "Dialog
Machine" will resume the processing in the batch mode.
Note that error conditions, such as a program crash by a
division by zero or similar fatal exceptions, or a
programmed HALT will also abandon temporarily the batch
mode and require some user input. You can still suppress
the associated dialogs by redirecting such error output
by a call to SetMsgDevice from module DMMessages like this
SetMsgDevice(toJournalFile,toJournalFile,toJournalFile,
toJournalFile);
All output generated by the program, which might potentially
require a user dialog, will then be suppressed. This is
even valid for a programmed HALT.
The batch mode may be useful to execute some lengthy
computations in a setup procedure, such as an entire
simulation, since calling RunDialogMachine in the batch
mode will simply enter and immediately leave the "Dialog
Machine" again.
NOTE: In case the 'Dialog Machine' is implemented as the
so-called 'Batch Dialog Machine' calling this routine will
have no effect, since such a 'Dialog Machine' does always
and only operate in the batch mode. You can use routine
DMSystem.GetDMVersion to learn whether the current
implementation of the 'Dialog Machine' is a full featured
interactive or the more restricted 'Batch Dialog Machine'
variant only.
Note the batch mode is global and affects all subprogram
levels once its activated. However, its use is supported
in the following way: Termination of any subprogram
level which has activated the batch mode as the first
(after the last or initial deactivation), will resume the
normal, interactive mode as was originally the case.
IMPLEMENTATION RESTRICTIONS: The emulation of the batch
mode may not be fully possible, depending on the platform
and the particular implementation of the 'Dialog Machine'.
Consequently some user interactions may still be required
if an implementation is not complete.
RAMSES is an acronym for Reserach Aids for Modeling and
Simulation of Environmental Systems.
*)
PROCEDURE DialogMachineIsInBatchMode(): BOOLEAN;
(*
Returns wether the 'Dialog Machine' operates currently in
the so-called batch mode (returns TRUE) or operates in
the normal interactive mode (returns FALSE) (see also
comments for procedure ForceDialogMachineIntoBatchMode).
If the 'Dialog Machine' is actually implemented only as a
'Batch Dialog Machine' this procedure returns always
TRUE. The latter fact can only be learned by calling
DMSystem.GetDMVersion.
*)
PROCEDURE QuitDialogMachine;
(*
Quit running 'Dialog Machine' on current program level.
Typically this procedure is never called directly by an
application program, since the 'Dialog Machine' calls it
automatically when executing the quit command installed by
means of module DMMenus.
*)
PROCEDURE AbortDialogMachine;
(*terminate 'Dialog Machine' regardless of current program level*)
(****************************************)
(*##### Dynamic Linking-Loader #####*)
(****************************************)
(* The following features are only meaningful if a dynamic
linking loader is available. This is the case for MacMETH on
the Macintosh *)
TYPE
SubProgStatus = (normal, abnormal);
PROCEDURE CallSubProg(module: ARRAY OF CHAR; VAR status: SubProgStatus);
(*
Use this procedure to call another Modula program to be run
as a subprogram under the control of the 'Dialog Machine'.
Subprograms are stacked on each other and form a kind of
program stack. Each (sub)program acts on its particular
level, may install new menus, open windows etc. Previously
installed objects are globally controllable by means of the
'Dialog Machine', not just those objects belonging to the
topmost (sub)program level. In particular note that
procedures attached via a mouse handler to a particular
window or associated with a menu command will be executed on
the very same program level as the window has been created or
the menu command has been installed, not necessary only on
the current topmost level. Upon quitting a subprogram, any
objects created on the level to be left are removed
automatically by the 'Dialog Machine'.
*)
END DMMaster.