|
|
|
|
||
|
DEFINITION MODULE DMMessages; (******************************************************************* Module DMMessages ('Dialog Machine' DM_V3.0) Copyright (c) 1991-2006 by Andreas Fischlin and ETH Zurich. Purpose Set of basic mechanisms to bring a short message to the attention of the user. Remarks The user is forced to aknowledge the message. In some cases it might be even required, that the user answers a question. Typically the running client program asks the user for a clarification or displays some information, warnings or even error messages. This module is functionally a replacement for the following routines previously beeing part of the Dialog Machine: Old routine replaced by new routine ----------- ----------------------- DMAlerts.ShowAlert DMMessages.Inform DMErrorMsgs.DispError DMMessages.DoInform DMSubLaunch.Message DMMessages.Inform DMQuestions.Ask DMMessages.Ask DMSubLaunch.HaltMessage DMMessages.Abort Note: For upward compatibility only DMAlerts from above modules are still available. All other modules, i.e. DMQuestions, DMSubLaunch, DMErrorMsgs, are now removed from the Dialog Machine. DMMessages is now part of the DM kernel. This module belongs to the 'Dialog Machine'. Programming o Design Andreas Fischlin 21/04/1991 o Implementation Andreas Fischlin 29/04/1991 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: 30/05/1991 AF *******************************************************************) (***************************************) (*##### Core Message Routines #####*) (***************************************) CONST LNBREAK = 15C; (* If encountered forces a message text to continue on a new line (line break) *) undefMsgNr = -1; PROCEDURE Ask(question: ARRAY OF CHAR; butTexts: ARRAY OF CHAR; butWidth: CARDINAL; VAR answer: INTEGER); (* Activates in the middle of the screen a question window with a series of push buttons and requests from the user an answer. The pushbuttons are placed in the lower portion of the window starting with the default button as the leftmost, and ending with the cancel button as the rightmost push-button. The question text, e.g. "Save changes?", is written at the top of the window and will be wrapped onto as many lines as needed. If questions contains the character LNBREAK = 15C (ASCII Return), the current line will be terminated and subsequent characters of the question are written to a new line. Implementation restriction: Up to a maximum of 17 lines (fits still onto any Mac screen) can be displayed. String butTexts contains the labels of each button, e.g. "Yes|No|Cancel", in the sequence the buttons will be displayed from left to right. answer returns the answer given by the user, i.e. the ordinal number of the button pressed starting with 1 as the leftmost (default) button. The pushbuttons can also be pressed by means of keyboard equivalents. If the user presses the key corresponding to the first character of each button text, that button can also be activated (case insensitive). Note that in case there are several button texts starting with the same character, the first (from left to right) button matching the key pressed will be returned in the answer. The first button (default button) can also be pushed via the standard keyboard equivalents, i.e. by pressing the return or enter key. Note that in case there is more than one button present, that the standard cancel keyboard equivalents (Command^'.', ESC) are always interpreted the same as pressing the rightmost button. Hence, it is a good programing practice to assign to butTexts actual values similar to "...xyz...|Cancel". *) PROCEDURE DisplayBusyMessage( message: ARRAY OF CHAR ); (* Displays a one line small message window showing the string message. At a time there exists only one single busy message window and the user can't move nor close the busy message window. Typically the busy message window is used to inform the user about an ogoing activity. This procedure can be called repetitively, e.g. to change the displayed text while the process is busy. *) PROCEDURE DiscardBusyMessage; (* Discards the busy message window. Call it once, after the activity has been terminated. *) PROCEDURE Inform (paragraph1, paragraph2, paragraph3: ARRAY OF CHAR); PROCEDURE DoInform (msgnr: INTEGER; modIdent, locDescr, insertions: ARRAY OF CHAR); PROCEDURE Warn (paragraph1, paragraph2, paragraph3: ARRAY OF CHAR); PROCEDURE DoWarn (msgnr: INTEGER; modIdent, locDescr, insertions: ARRAY OF CHAR); PROCEDURE Abort (paragraph1, paragraph2, paragraph3: ARRAY OF CHAR); PROCEDURE DoAbort (msgnr: INTEGER; modIdent, locDescr, insertions: ARRAY OF CHAR); (* By default any of these procedures display on the main screen (Warn/DoWarn and Abort/DoAbort in the middle of the screen) a message window with some text in them. In each case the user is forced to acknowledge the message before the program may resume execution. The three pairs differ slightly in their behavior. They are designed to be called for particular purposes depending whether just a message shall be displayed to the user or whether a minor or serious error has been encounterd. This is described below under section "Basic Functioning". The two procedures of each pair differ only in the method of message text retrieval respectively construction. The procedures Inform, Warn, and Abort use a simple method, where any message text has to be fully passed at call time; the procedures DoInform, DoWarn, and DoAbort are more luxurious and offer a more sophisticated method of message text retrieval and construction as described below under section "Methods of message retrieval and construction". *) (**************************************************************** On Inform/DoInform, Warn/DoWarn, and Abort/DoAbort ================================================== Basic Functioning: ***************** Independent of the message retrieval respectively construction method each of the three procedures differ in their meanings, i.e. they are to be called under different conditions and for different purposes. The main difference is in the number of push buttons available to the user for aknowledging the message. Depending on the actual push button the user clicks, subsequent program action differs according to the following table: Procedure pair Button(s) Meaning -------------- --------- ------- o Inform/DoInform "OK" Resume program execution Typical Use: Some particular information has to be brought to the user's attention. Hence, the user must click the button or press the return key to the message before the program resumes further execution. o Warn/DoWarn 'Debug' Calls the debugger "Continue" Resume program execution (Default) 'Abort' Terminates (sub)program execution Typical Use: A non-fatal error has been encountered, still allowing for further program execution, however the user must be warned and informed on the circumstances. For instance the user is warned that the system is low on memory. o Abort/DoAbort 'Debug' Calls the debugger "Abort" Terminates (sub)program execution (Default) Typical Use: A fatal error has been encountered, and any further program execution is unsafe. E.g. a floating-point division by a small number has lead to an overflow and thus making any future computations with the resulting infinite real operand impossible. Any push button can be answered via keyboard equivalents: default button Return, Enter, 1st char of label (case insensitive) Debug 'D', 'd' Abort 'A', 'a', Command^'.', Escape Continue 'C', 'c' Methods of message retrieval and construction: ********************************************* 1st Method: Inform, Warn, and Abort ------------------------------------ The procedures Inform, Warn, and Abort adopt the simple method of text production only from the actual string parameters. Each string corresponds to a paragraph of text, which will be automatically wrapped onto as many lines as needed. Calling Inform as follows: Inform("Low on memory!","Please close some windows.","Thanks."); results in a display similar to the following message window: ------------------------------------ I I I Low on memory! I I Please close some windows. I I Thanks. I I I I ========== I I || OK || I I ========== I ------------------------------------ A second example: Calling Abort as follows: Abort("Fatal error occurred:"," ","Division by zero!"); results in a display similar to the following message window: --------------------------------------------- I ***** Halt in module "Modulexyz" I I ******* Program counter at offset xyzH I I ***** I I I I Fatal error occurred: I I I I Division by zero! I I I I ----------- ============ I I | Debug | Execution || Abort || I I ----------- stopped! ============ I --------------------------------------------- 2nd Method: DoInform, DoWarn, and DoAbort ----------------------------------------------- The more elaborate method adopted by the procedures DoInform, DoWarn, and DoAbort functions by default in the way described below. To explain these mechanisms, a typical example shall be introduced: Let us assume that the user of a program is asked to enter a number x, which ought to stay within a given subrange, say [xmin..xmax] = [-1..+1] when used by procedure 'DoIt' from module 'MyModule'. The user now enters for the number x a value, say 2. Then the procedure 'DoIt' is called, which detects that x is out of range. The user should now be informed about the situation and to achieve the desired effect procedure DoInform shall be called with the following actual arguments: DoInform(100,"MyModule",'in procedure "DoIt"',"2|-1|+1"); The following steps will then be performed by procedure DoInform: 1) First a header message text common to all messages is used and the identifiers of the module 'MyModule' and the description of the error location as "in procedure 'DoIt'" are inserted within this header. The begin of the message will then look similar to: -------------------------------------------------------- I Error in module "MyModule" in procedure "DoIt": I 2) Second the actual message text from a central, globally accessible storage (by default this is module DMLanguage) is retrieved via the number msgnr = 100. Say the message text with msgnr=100 for the out of range error is: 100 : Number Δ is out of range [ Δ .. Δ ]! Please correct. 3) Third the retrieved text, which is not yet very meaningful, is filtered, i.e. every character 'Δ' (306C), called a place-holder, is replaced by one of the substrings contained in the parameter 'insertions'. In our example the actual value of this parameter is "2|-1|+1". This means that the first occurence of 'Δ' (306C) in the original string as retrieved in the preceeding step is to be replaced with the first substring, i.e. '2', the second with '-1', and the third with '+1'. After this substitution the central section of the message looks similar to: I I I Number 2 is out of range [ -1 .. +1 ]! Please I I correct. I I I 4) Fourth the message is displayed and depending on the particular call, i.e. DoInform, DoWarn, or DoAbort, push buttons are added (DoInform just adds on 'OK'-button). The resulting message window similar to the one below will then be brought to the user's attention: -------------------------------------------------------- I Error in module "MyModule" in procedure "DoIt": I I I I Number 2 is out of range [ -1 .. +1 ]! Please I I correct. I I ========== I I || OK || I I ========== I -------------------------------------------------------- Hints on how to customize messages: ********************************** NOTE: The following information may be useful to understand some more details of the described steps or to customize the behavior. to step 1) o In the procedures Inform resp. DoInform it is possible to suppress any header. To achieve this set both parameters 'modIdent' and 'locDescr' to the empty string. Furthermore, in case 'modIdent' is the empty string, no header text such as "Error in module..." is displayed; instead just the text contained in 'locDescr' is used as the header. In the frst case ('modIdent' and 'locDescr' both not empty) the header text can be interpreted as the following string: 'Error in module "Δ" Δ:' In the second case ('modIdent' empty, but 'locDescr' not empty) the header text can be interpreted as the following string: 'Δ:' where 'modIdent' is substituted in place of the first, and 'locDescr' in place of the second placeholder. All other procedures, i.e. Warn, DoWarn, Abort, DoAbort, will always show a header. If both parameters 'modIdent' and 'locDescr' are the empty string, only automatic generation of the header will take place. In this case the identifier of the module in which execution is halted is shown together with the current value of the program counter as an offset (hexadecimal) within the module code. An automatically generated header looks similar to the following: Halt in module "Modulexyz" Program counter at offset xyzH You may override the automatic generation of the module ident by using an actual value of 'modIdent' different from the empty string. Moreover, optionally you may describe the halt location by 'locDescr'. The latter string is added after the module ident. E.g. the following call to DoAbort DoAbort(100,"MyModule (V0.2)",'in proc "DoIt"', "2|-1|+1"); results in the following message window: --------------------------------------------- I ***** Halt in module "MyModule I I ******* (V0.2)" in proc "DoIt" I I ***** Program counter at offset xyzH I I I I Number 2 is out of range [ -1 .. +1 ]! | | Please correct. I I I I ----------- ============ I I | Debug | Execution || Abort || I I ----------- stopped! ============ I --------------------------------------------- The header text can be interpreted as the following string: 'Halt in module "Δ" Δ Program counter at offset ΔH' where 'modIdent' is substituted in place of the first, 'locDescr' in place of the second, and the always automatically generated program counter offset in place of the third placeholder. to step 2) o The actual mechanism of message construction is determined by the current settings, which may be modified by the programmer by calling SetMsgRetrieveProc. By default message texts are retrieved by DMLanguage.GetMsgString. If the latter fails to find a text associated with the msgnr, a particular resource string (see DMStrings.GetRString) is searched. It is also possible to use DMMsgFile.GetMessage to retrieve the messages from a text file containing the message texts. Excerpt from a sample message file: ... ... 100 : Number Δ is out of range [ Δ .. Δ ]! Please correct. 101 : Out of memory. Requested block of size Δ too large. ... to step 3) o Note that the placeholder replacement mechanism is more important than it may first appear. In our example the information on the value actually entered by the user, as well as the information on the currently legal range may be quite crucial. It serves not only the convenience of the user, but is also needed for any subsequent correction of x. Since the user runs an interactive program he or she needs to receive some hints on currently legal ranges. Otherwise he or she might be unable to enter a correct value for x and hence may be unable to exit an endless program loop. o The substrings used in the parameter insertions are separated by the delimiter "|" (vertical bar) according to the following EBNF-Syntax: insertions = substring {"|" substring }. substring = { ' any char except "|" ' }. The substrings are inserted in sequence of their occurrence in insertions at the positions denoted by the place holders 'Δ' (=306C) as found in the original string from left to right. to step 4) o Running a program, e.g. a long simulation run, on a machine in background, may lead to undesirable program interruption, if a message producing procedure is called inadvertently. To have full control over the message production, a mechanism to globally divert all messages to, for instance a journaling file instead of the screen, is provided (see below procedure UseForMsgJournaling). This divertion is available to all exported message procedures regardless of their message text retrieval and construction method. Moreover it is possible to control this behavior individually for each class of errors, i.e. asking, informations, warnings, or abortions (see below procedure SetMsgDevice). Example: For instance in the situation in which a computer runs unobserved simulations by solving differential equations during several days: Typically simple information messages are to be recorded on the journaling file and program execution may continue always. In the second class, i.e. warnings, the messages may be treated similarly, i.e. recorded and then execution is resumed. But if a fatal error is encountered the user may wish to be able to call the debugger whithout having to repeat the lengthy computations when he/she returns to the machine. Hence, just abortions should not be diverted but still be displayed on the screen. ****************************************************************) (********************************************************** The following objects are only used to customize message production and behavior **********************************************************) (*********************************************************) (*##### Customization of message text retrieval #####*) (*********************************************************) TYPE MsgRetrieveProc = PROCEDURE ( INTEGER , VAR ARRAY OF CHAR ); PROCEDURE SetMsgRetrieveProc(rp: MsgRetrieveProc); PROCEDURE GetMsgRetrieveProc(VAR rp: MsgRetrieveProc); (* Sets or gets the current message text retrieving procedure. The following procedures retrieving a string associated with a particular number are readily available in the Dialog Machine and can be set as the current method: - DMLanguage.GetMsgString (if not found, calls DMStrings.GetRString) - DMStrings.GetRString - MsgFile.GetMessage - MsgFile.GetNumberedMessage Initial default: DMLanguage.GetMsgString *) (*************************************************) (*##### Customization of message output #####*) (*************************************************) CONST toScreen = 0; toJournalFile = 1; TYPE MsgDevice = [toScreen..toJournalFile]; MsgWriteProc = PROCEDURE ( CHAR ); MsgWriteLnProc = PROC; PROCEDURE UseForMsgJournaling(wp: MsgWriteProc; wlnp: MsgWriteLnProc); (* Use subsequently for the journaling of messages the procedures wp and wlnp. For instance journaling on a file can be accomplished by installing for wp the following sample procedure: PROCEDURE MyWrite( ch: CHAR ); BEGIN DMFiles.WriteChar(myJournalFile,ch); END MyWrite; *) PROCEDURE SetMaxMsgs (max: INTEGER); (* Set the maximum of messages which are allowed to be redirected. If a total of more than max messages, regardless of the class, should have been redirected, then the redirection mechanism will be reset to its default, i.e. messages will appear on the screen again. This avoids the filling up of a hard disk with a journaling file full of the same message in case of an endless loop. Default: max = 1000 per program level. *) PROCEDURE SetMsgDevice (forAsk,forInform,forWarn,forAbort: MsgDevice); PROCEDURE GetMsgDevice (VAR forAsk,forInform,forWarn,forAbort: MsgDevice); (* Activates respectively returns current journaling, i.e. message redirection individually for any message class. A flag value of toJournalFile starts, toScreen stops the journaling, i.e. resets the message to the default display on the screen. In the latter case the program always awaits user acknowledgement before any possible program continuation. Journaling is done by writing the message text by means of the procedures currently set by procedure UseForMsgJournaling. Note that if journaling is active for a particular message class, that no answers will be awaited by the executing program; instead the Dialog Machine assumes always that the default answer has been given. forAsk starts or stops the journaling mechanism for the procedure Ask respectively AskPredefinedQuestion, forInform does it for the procedures Inform respectively DoInform, forWarn does it for the procedures Warn resp. DoWarn, and forAbort for the procedures Abort resp. DoAbort. Hint: In case of an active journaling mechanism, make sure that the default answer can never cause the program to repeat a message endlessly, but always exits any eventually present loop. *) (**************************************************) (*##### Asking a resource based question #####*) (**************************************************) PROCEDURE AskPredefinedQuestion(fileName: ARRAY OF CHAR; alertID: INTEGER; str1,str2,str3,str4: ARRAY OF CHAR; VAR answer: INTEGER); (* Activates a predefined question window read from a Macintosh resource of type "ALRT" with ID = alertID contained in the resource fork of the file fileName. In case that the fileName passed is empty, the default search strategy to find the resource is followed. Up to four strings (str1, str2, str3, str4) may be specified in order to insert them at the place of the placeholders within the predefined text. In order to perform correctly, the resource must contain as first dialog item the default button (item 1) and as the the second button the cancel button (item 2). (NOTE: this behavior is different from the one of procedure Ask where the cancel button must be the last, i.e. rightmost not the second pushbutton). *) END DMMessages.
|
||
|
|
|