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 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.

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