ETHZ_Logo RAMSES_Logo_Right   RAMSES   RAMSES_Logo_Left Systems Ecology  
Start    search button      Modules:   A-Z   Function   Layer        QuickRefs:   DM   AuxLib   AuxLibE   SciLib   EasyMW   MW   ISIS   RMSLib

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.

  Contact RAMSES@env.ethz.ch Last updated: 25-Jul-2011 [Top of page]