home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / pmos2002.zip / SRC / userinte.mod < prev    next >
Text File  |  1997-11-04  |  33KB  |  897 lines

  1. IMPLEMENTATION MODULE UserInterface;
  2.  
  3.         (****************************************************************)
  4.         (*                                                              *)
  5.         (*              Text User Interface for PMOS                    *)
  6.         (*                                                              *)
  7.         (*              Original version by M. Walsh                    *)
  8.         (*               This version by P. Moylan                      *)
  9.         (*                                                              *)
  10.         (*      Last Edited:    4 November 1997                         *)
  11.         (*      Status:         OK                                      *)
  12.         (*                                                              *)
  13.         (****************************************************************)
  14.  
  15. FROM TaskControl IMPORT
  16.     (* type *)  Lock,
  17.     (* proc *)  CreateTask, CreateLock, DestroyLock, Obtain, Release;
  18.  
  19. FROM Semaphores IMPORT
  20.     (* type *)  Semaphore,
  21.     (* proc *)  Wait, Signal, CreateSemaphore;
  22.  
  23. FROM Storage IMPORT
  24.     (* proc *)  ALLOCATE, DEALLOCATE;
  25.  
  26. FROM MiscPMOS IMPORT
  27.     (* proc *)  CopyString;
  28.  
  29. FROM Keyboard IMPORT
  30.     (* proc *)  StuffKeyboardBuffer;
  31.  
  32. FROM Windows IMPORT
  33.     (* type *)  ColumnRange, RowRange, Rectangle, DisplayPage,
  34.                 Colour, Window, FrameType, DividerType,
  35.     (* proc *)  OpenWindow, SetCursor, WriteChar, WriteString, Hide,
  36.                 ShiftWindowAbs, CloseWindow, PutOnTop, IdentifyTopWindow,
  37.                 WindowLocation, PageOf, SaveCursor, InstallCloseHandler,
  38.                 RequestPageChangeNotification, PutOnPage;
  39.  
  40. FROM Mouse IMPORT
  41.     (* type *)  Events, EventSet, Buttons, ButtonSet,
  42.     (* proc *)  MouseAvailable, InstallEventHandler, SetTextMousePage,
  43.                 ShowMouseCursor, HideMouseCursor, SetMouseCursorLimits,
  44.                 SetTextMousePosition;
  45.  
  46. FROM Queues IMPORT
  47.     (* type *)  Queue,
  48.     (* proc *)  CreateQueue, AddToQueue, TakeFromQueue;
  49.  
  50. (************************************************************************)
  51.  
  52. CONST
  53.     LeftOnly = ButtonSet {LeftButton};
  54.     RightOnly = ButtonSet {RightButton};
  55.     LeftOrRight = ButtonSet {LeftButton, RightButton};
  56.  
  57. TYPE
  58.     UIWindow = POINTER TO WindowData;
  59.  
  60.     ActiveRegionPtr = POINTER TO ActiveRegion;
  61.  
  62.     (* An ActiveRegion is a rectangle within a window which has had a   *)
  63.     (* "mouse click" action defined for it.                             *)
  64.  
  65.     ActiveRegion = RECORD
  66.                         region: Rectangle;
  67.                         LiveButtons: ButtonSet;
  68.                         ActionToBeTaken: Action;
  69.                         NextActiveRegion: ActiveRegionPtr;
  70.                    END (*RECORD*);
  71.  
  72.     (* A UIWindow is a pointer to a WindowData record.  This refers to  *)
  73.     (* a Window (as defined in module Windows), together with some      *)
  74.     (* extra information that this module keeps concerning the Window.  *)
  75.     (* The fields are:                                                  *)
  76.     (*  win                     the Window itself                       *)
  77.     (*  access                  critical section protection Lock        *)
  78.     (*  page                    the display page on which the window    *)
  79.     (*                          resides                                 *)
  80.     (*  ActiveRegionList        head of linked list of active regions   *)
  81.     (*                          associated with this window             *)
  82.     (*  OutsideHandler          an optional procedure to be called      *)
  83.     (*                          when a mouse click lands outside this   *)
  84.     (*                          this window                             *)
  85.     (*  next, previous          used to keep the set of all UIWindows   *)
  86.     (*                          as a linked list                        *)
  87.     (*  title                   title to use when the list of all       *)
  88.     (*                          windows is displayed.                   *)
  89.  
  90.     WindowData =    RECORD
  91.                         win: Window;
  92.                         access: Lock;
  93.                         page: DisplayPage;
  94.                         ActiveRegionList: ActiveRegionPtr;
  95.                         OutsideHandler: PROC;
  96.                         next, previous: UIWindow;
  97.                         title: ARRAY [0..19] OF CHAR;
  98.                     END (*RECORD*);
  99.  
  100.     (* An EventData record records the triggering event, the button     *)
  101.     (* status, and the mouse cursor position when a mouse event occurs. *)
  102.  
  103.     EventData = RECORD
  104.                     Trigger: EventSet;
  105.                     ButtonStatus: ButtonSet;
  106.                     Xpos: ColumnRange;
  107.                     Ypos: RowRange;
  108.                 END (*RECORD*);
  109.  
  110.     EventDataPtr = POINTER TO EventData;
  111.  
  112. (************************************************************************)
  113.  
  114. VAR
  115.     (* For each display page we keep all UIWindows in a linked list,    *)
  116.     (* and FirstUIW[page] is the head of this list.                     *)
  117.  
  118.     FirstUIW: ARRAY DisplayPage OF UIWindow;
  119.  
  120.     (* Lock to control access to the lists of all windows.      *)
  121.  
  122.     ListAccess: Lock;
  123.  
  124.     (* DefaultUIW is a special UIWindow which does not go into the      *)
  125.     (* master list, and which is associated with those areas of the     *)
  126.     (* screen which are not otherwise covered by a UIWindow.            *)
  127.  
  128.     DefaultUIW: UIWindow;
  129.  
  130.     (* The hardware display page currently active. *)
  131.  
  132.     CurrentDisplayPage: DisplayPage;
  133.  
  134.     (* The number of UIWindows in each hardware page. *)
  135.  
  136.     WindowCount: ARRAY DisplayPage OF CARDINAL;
  137.  
  138.     (* WindowList[p] is a special Window, used to display the titles of *)
  139.     (* all the other windows on display page p.                         *)
  140.  
  141.     WindowList: ARRAY DisplayPage OF Window;
  142.  
  143.     (* Flag to indicate that the list of all windows is on the screen.  *)
  144.  
  145.     WindowListOpen: ARRAY DisplayPage OF BOOLEAN;
  146.  
  147.     (* EventQueue is a queue of mouse event records.                    *)
  148.  
  149.     EventQueue: Queue;
  150.  
  151.     (* MoveInProgress is set iff a "move window" operation is being     *)
  152.     (* carried out.                                                     *)
  153.  
  154.     MoveInProgress: BOOLEAN;
  155.  
  156.     (* While a move is in progress, we need to carry some information   *)
  157.     (* forward from one event to the next.  These data don't need       *)
  158.     (* critical section protection, since it's impossible for more than *)
  159.     (* one move to be in progress at a time.                            *)
  160.  
  161.     MoveData:   RECORD
  162.                     window: Window;
  163.                     rowoffset: RowRange;
  164.                     coloffset: ColumnRange;
  165.                 END (*RECORD*);
  166.  
  167. (************************************************************************)
  168. (*                      THE MOUSE CLICK HANDLER                         *)
  169. (************************************************************************)
  170.  
  171. PROCEDURE MouseClickHandler (A: EventSet;  B: ButtonSet;  X, Y: CARDINAL);
  172.  
  173.     (* This procedure is called by the mouse driver whenever there is   *)
  174.     (* a mouse event, and that event is in the event set specified at   *)
  175.     (* the time this handler was activated.  Parameter A shows the      *)
  176.     (* event(s) that triggered the call; B shows the current up/down    *)
  177.     (* state of the mouse buttons; and (X,Y) is the mouse cursor        *)
  178.     (* position.                                                        *)
  179.  
  180.     VAR p: EventDataPtr;
  181.  
  182.     BEGIN
  183.         NEW (p);
  184.         WITH p^ DO
  185.             Trigger := A;
  186.             ButtonStatus := B;
  187.             Xpos := X;  Ypos := Y;
  188.         END (*WITH*);
  189.         AddToQueue (EventQueue, p);
  190.     END MouseClickHandler;
  191.  
  192. (************************************************************************)
  193. (*                      MISCELLANEOUS PROCEDURES                        *)
  194. (************************************************************************)
  195.  
  196. PROCEDURE Inside (row: RowRange;  col: ColumnRange;  R: Rectangle): BOOLEAN;
  197.  
  198.     (* Returns TRUE iff point (row,col) is in (or on the border of) R.  *)
  199.  
  200.     BEGIN
  201.         WITH R DO
  202.             RETURN (col >= left) AND (col <= right)
  203.                                 AND (row >= top) AND (row <= bottom);
  204.         END (*WITH*);
  205.     END Inside;
  206.  
  207. (************************************************************************)
  208.  
  209. PROCEDURE FindOwner (w: Window): UIWindow;
  210.  
  211.     (* Returns the UIWindow with which w is associated, or returns NIL  *)
  212.     (* if there is no such UIWindow.                                    *)
  213.  
  214.     VAR result: UIWindow;  page: DisplayPage;
  215.  
  216.     BEGIN
  217.         Obtain (ListAccess);
  218.         page := 0;
  219.         LOOP
  220.             result := FirstUIW[page];
  221.             LOOP
  222.                 IF result = NIL THEN EXIT (*LOOP*)
  223.                 ELSIF result^.win = w THEN EXIT (*LOOP*)
  224.                 ELSE result := result^.next;
  225.                 END (*IF*);
  226.             END (*LOOP*);
  227.             IF (result <> NIL) OR (page = MAX(DisplayPage)) THEN
  228.                 EXIT (*LOOP*);
  229.             END (*IF*);
  230.             INC (page);
  231.         END (*LOOP*);
  232.         Release (ListAccess);
  233.         RETURN result;
  234.     END FindOwner;
  235.  
  236. (************************************************************************)
  237. (*                      SHUTTING DOWN A UIWINDOW                        *)
  238. (************************************************************************)
  239.  
  240. PROCEDURE CloseWindowList (w: Window;  r: RowRange;  c: ColumnRange);
  241.  
  242.     (* Closes w - which happens to be the window list for some display  *)
  243.     (* page, because of the way this procedure is called.               *)
  244.  
  245.     VAR page: DisplayPage;
  246.  
  247.     BEGIN
  248.         page := PageOf(w);
  249.         HideMouseCursor;
  250.         WindowListOpen[page] := FALSE;
  251.  
  252.         (* Subtle point: we don't explicitly destroy the UIWindow to    *)
  253.         (* which w belongs, since it is automatically destroyed (by     *)
  254.         (* procedure UnregisterWindow) as a side-effect of CloseWindow. *)
  255.  
  256.         (* The complications caused by side-effects disturb me a little *)
  257.         (* on philosophical grounds.  One day I should try to invent    *)
  258.         (* a better substitute for call-back procedures.  They're       *)
  259.         (* really designed for a language like C or C++ whose           *)
  260.         (* programmers are trained to expect hidden gotchas.            *)
  261.  
  262.         CloseWindow (w);
  263.         ShowMouseCursor;
  264.  
  265.     END CloseWindowList;
  266.  
  267. (************************************************************************)
  268.  
  269. PROCEDURE DestroyUIWindow (VAR (*INOUT*) UIW: UIWindow);
  270.  
  271.     (* Destroys UIW, without closing its window. *)
  272.  
  273.     VAR ActiveRegionNext, ActiveRegionToDispose: ActiveRegionPtr;
  274.         page: DisplayPage;
  275.  
  276.     BEGIN
  277.         page := UIW^.page;
  278.  
  279.         (* If the window list is currently open, close it, since its    *)
  280.         (* entries are about to become invalid.  Hidden trap: watch out *)
  281.         (* for a recursive close of the window list.                    *)
  282.  
  283.         IF WindowListOpen[page] AND (UIW^.win <> WindowList[page]) THEN
  284.             CloseWindowList (WindowList[page], 0, 0);
  285.         END (*IF*);
  286.  
  287.         Obtain (UIW^.access);
  288.  
  289.         (* Reclaim the space used in the ActiveRegionList linked list. *)
  290.  
  291.         ActiveRegionNext := UIW^.ActiveRegionList;
  292.         WHILE ActiveRegionNext <> NIL DO
  293.             ActiveRegionToDispose := ActiveRegionNext;
  294.             ActiveRegionNext := ActiveRegionNext^.NextActiveRegion;
  295.             DISPOSE (ActiveRegionToDispose);
  296.         END (*WHILE *);
  297.         Release (UIW^.access);
  298.         DestroyLock (UIW^.access);
  299.  
  300.         (* Remove this UIWindow from the master list. *)
  301.  
  302.         Obtain (ListAccess);
  303.         DEC (WindowCount[page]);
  304.         IF WindowCount[CurrentDisplayPage] = 0 THEN
  305.             HideMouseCursor;
  306.         END(*IF*);
  307.         IF UIW^.previous = NIL THEN
  308.             FirstUIW[page] := UIW^.next;
  309.         ELSE
  310.             UIW^.previous^.next := UIW^.next;
  311.         END (*IF*);
  312.         IF UIW^.next <> NIL THEN
  313.             UIW^.next^.previous := UIW^.previous;
  314.         END (*IF*);
  315.         Release (ListAccess);
  316.         DISPOSE (UIW);
  317.  
  318.     END DestroyUIWindow;
  319.  
  320. (************************************************************************)
  321.  
  322. PROCEDURE UnregisterWindow (w: Window;  dummy: DisplayPage);
  323.  
  324.     (* Removes w from the set of Windows controlled by this module.     *)
  325.     (* The second parameter is a dummy for compatibility with           *)
  326.     (* procedure InstallCloseHandler.                                   *)
  327.  
  328.     VAR UIW: UIWindow;
  329.  
  330.     BEGIN
  331.         UIW := FindOwner (w);
  332.         IF UIW <> NIL THEN
  333.             DestroyUIWindow (UIW);
  334.         END (*IF*);
  335.     END UnregisterWindow;
  336.  
  337. (************************************************************************)
  338. (*                      MANIPULATING ACTIVE REGIONS                     *)
  339. (************************************************************************)
  340.  
  341. PROCEDURE AddActiveRegion (UIW: UIWindow; Top, Bottom: RowRange;
  342.                         Left, Right: ColumnRange;  ButtonsEnabled: ButtonSet;
  343.                         ActionProc: Action);
  344.  
  345.     (* After a call to this procedure, any mouse click on a button in   *)
  346.     (* ButtonsEnabled, and in the rectangle defined by the other        *)
  347.     (* parameters, will cause ActionProc to be called.                  *)
  348.  
  349.     VAR NewActiveRegion: ActiveRegionPtr;
  350.  
  351.     BEGIN
  352.         IF UIW = NIL THEN RETURN END(*IF*);
  353.  
  354.         NEW (NewActiveRegion);
  355.         WITH NewActiveRegion^ DO
  356.             WITH region DO
  357.                 top := Top;  bottom := Bottom;
  358.                 left := Left;  right := Right;
  359.             END (*WITH*);
  360.             LiveButtons := ButtonsEnabled;
  361.             ActionToBeTaken := ActionProc;
  362.         END (*WITH*);
  363.  
  364.         (* Insert the new active region at the head of the UIWindow's   *)
  365.         (* active region list.  Putting it at the head gives it         *)
  366.         (* priority over other (possibly overlapping) regions which     *)
  367.         (* were earlier added, and this is exactly the effect we want.  *)
  368.  
  369.         WITH UIW^ DO
  370.             Obtain (access);
  371.             NewActiveRegion^.NextActiveRegion := ActiveRegionList;
  372.             ActiveRegionList := NewActiveRegion;
  373.             Release (access);
  374.         END (*WITH*);
  375.  
  376.     END AddActiveRegion;
  377.  
  378. (************************************************************************)
  379.  
  380. PROCEDURE OutsideWindowHandler (UIW: UIWindow;  ActionProc: PROC);
  381.  
  382.     (* Like AddActiveRegion, except that in this case the region in     *)
  383.     (* question is the entire screen outside this window.  If this      *)
  384.     (* procedure has been called, mouse clicks are still sent to        *)
  385.     (* other windows, but only after ActionProc has been called.        *)
  386.     (* If several "outside window" handlers have been defined, they     *)
  387.     (* are called in last-in-first-out order, the sequence of calls     *)
  388.     (* stopping when we finally reach an action procedure that          *)
  389.     (* belongs to a window that is visible at the click location.       *)
  390.  
  391.     BEGIN
  392.         UIW^.OutsideHandler := ActionProc;
  393.     END OutsideWindowHandler;
  394.  
  395. (************************************************************************)
  396. (*              THE BUILT-IN ACTIVE REGION HANDLERS                     *)
  397. (************************************************************************)
  398.  
  399. PROCEDURE DoNothing;
  400.  
  401.     BEGIN
  402.     END DoNothing;
  403.  
  404. (************************************************************************)
  405.  
  406. PROCEDURE MoveWindow (w: Window;  yrel: RowRange;  xrel: ColumnRange);
  407.  
  408.     (* An ActiveRegion procedure to move a window on the screen.  Most  *)
  409.     (* of the work is actually done by procedure ContinueMove, given    *)
  410.     (* later.  This procedure just sets up the right conditions for     *)
  411.     (* ContinueMove to be called by the event dispatcher.               *)
  412.  
  413.     VAR R: Rectangle;
  414.  
  415.     BEGIN
  416.         R := WindowLocation(w);
  417.         WITH R DO
  418.             SetMouseCursorLimits (yrel, MAX(RowRange) - bottom + yrel + top,
  419.                                 xrel, MAX(ColumnRange) - right + xrel + left);
  420.         END (*WITH*);
  421.  
  422.         WITH MoveData DO
  423.             window := w;
  424.             rowoffset := yrel;  coloffset := xrel;
  425.         END (*WITH*);
  426.  
  427.         InstallEventHandler (EventSet {LeftUp, RightUp, Motion},
  428.                                                 MouseClickHandler);
  429.         MoveInProgress := TRUE;
  430.  
  431.     END MoveWindow;
  432.  
  433. (***********************************************************************)
  434.  
  435. PROCEDURE HideWindow (w: Window;  r: RowRange;  c: ColumnRange);
  436.  
  437.     (* An ActiveRegion procedure to hide a window. *)
  438.  
  439.     BEGIN
  440.         HideMouseCursor;
  441.         Hide (w);
  442.         ShowMouseCursor;
  443.     END HideWindow;
  444.  
  445. (***********************************************************************)
  446.  
  447. PROCEDURE ShowWindow (w: Window;  r: RowRange;  c: ColumnRange);
  448.  
  449.     (* An ActiveRegion procedure to put a window on top. *)
  450.  
  451.     BEGIN
  452.         HideMouseCursor;
  453.         PutOnTop (w);
  454.         ShowMouseCursor;
  455.     END ShowWindow;
  456.  
  457. (************************************************************************)
  458.  
  459. PROCEDURE ContinueMove (x: ColumnRange;  y: RowRange);
  460.  
  461.     (* Called by the mouse event handler when a "move window" operation *)
  462.     (* is in progress.  If the global variable MoveInProgress is FALSE, *)
  463.     (* we've just reached the end of the move.                          *)
  464.  
  465.     BEGIN
  466.         IF MoveInProgress THEN
  467.             WITH MoveData DO
  468.                 HideMouseCursor;
  469.                 ShiftWindowAbs (window, y-rowoffset, x-coloffset);
  470.                 ShowMouseCursor;
  471.             END (*WITH*);
  472.         ELSE
  473.             (* End of move.  Restore the normal event handler. *)
  474.             SetMouseCursorLimits (0, MAX(RowRange), 0, MAX(ColumnRange));
  475.             InstallEventHandler (EventSet {LeftDown, RightDown},
  476.                                                 MouseClickHandler);
  477.         END (*IF*);
  478.     END ContinueMove;
  479.  
  480. (************************************************************************)
  481.  
  482. PROCEDURE StuffEscape (w: Window;  r: RowRange;  c: ColumnRange);
  483.  
  484.     (* An ActiveRegion procedure to stuff an Esc character into the     *)
  485.     (* keyboard buffer.                                                 *)
  486.  
  487.     CONST Esc = CHR(27);
  488.  
  489.     BEGIN
  490.         StuffKeyboardBuffer (Esc);
  491.     END StuffEscape;
  492.  
  493. (************************************************************************)
  494. (*                      LOOKING AFTER THE WINDOW LIST                   *)
  495. (************************************************************************)
  496.  
  497. PROCEDURE SelectFromWindowList (dummy: Window;
  498.                                         row: RowRange;  col: ColumnRange);
  499.  
  500.     (* Called when the user clicks on a window name in the window list. *)
  501.     (* Brings that window to the top, and closes the window list.       *)
  502.  
  503.     VAR p: UIWindow;  j: RowRange;
  504.  
  505.     BEGIN
  506.         HideMouseCursor;
  507.         Obtain (ListAccess);
  508.         p := FirstUIW[CurrentDisplayPage];
  509.         FOR j := 1 TO WindowCount[CurrentDisplayPage]-row DO
  510.             p := p^.next;
  511.         END (*FOR*);
  512.         Release (ListAccess);
  513.         PutOnTop (p^.win);
  514.         CloseWindowList (WindowList[CurrentDisplayPage], 0, 0);
  515.         ShowMouseCursor;
  516.     END SelectFromWindowList;
  517.  
  518. (************************************************************************)
  519.  
  520. PROCEDURE DisplayWindowList (dummy: Window;  Y: RowRange;  X: ColumnRange);
  521.  
  522.     (* Displays the list of all windows which this module knows about,  *)
  523.     (* and sets up active regions which will allow the user to select   *)
  524.     (* a window (possibly hidden) with a mouse click.                   *)
  525.  
  526.     VAR ListUIW, UIW: UIWindow;  j, count: CARDINAL;  p: DisplayPage;
  527.  
  528.     BEGIN
  529.         p := CurrentDisplayPage;
  530.         count := WindowCount[p];
  531.         IF WindowListOpen[p] THEN DEC(count) END(*IF*);
  532.  
  533.         (* Adjust the cursor position, if necessary, to ensure that the *)
  534.         (* window list won't fall outside the screen boundaries.        *)
  535.  
  536.         IF Y > MAX(RowRange) - count - 1 THEN
  537.             Y := MAX(RowRange) - count - 1;
  538.         END (*IF*);
  539.         IF X < 2 THEN
  540.             X := 2;
  541.         ELSIF X > MAX(ColumnRange) - 19 THEN
  542.             X := MAX(ColumnRange) - 19;
  543.         END (*IF*);
  544.         HideMouseCursor;
  545.         SetTextMousePosition (X, Y);
  546.  
  547.         IF WindowListOpen[p] THEN
  548.             ShiftWindowAbs (WindowList[p], Y, X-2);
  549.         ELSE
  550.             OpenWindow (WindowList[p], black, white, Y, Y+count+1,
  551.                                 X-2, X+19, doubleframe, nodivider);
  552.             PutOnPage (WindowList[p], p);
  553.             ListUIW := AllowMouseControl (WindowList[p], "",
  554.                                 CapabilitySet {wmove, whide, wshow});
  555.  
  556.             (* Overlay the HideWindow ActiveRegion - which we specified *)
  557.             (* above only in order to get its button displayed - with a *)
  558.             (* CloseWindowList region.                                  *)
  559.  
  560.             AddActiveRegion (ListUIW,0,0,19,19,LeftOnly,CloseWindowList);
  561.  
  562.             (* Display the names of all the windows we control          *)
  563.             (* (ignoring the first, which is the WindowList itself).    *)
  564.  
  565.             SetCursor (WindowList[p], 0, 5);
  566.             WriteString (WindowList[p], "Window List");
  567.             Obtain (ListAccess);
  568.             UIW := FirstUIW[p]^.next;
  569.             FOR j := count TO 1 BY -1 DO
  570.                 SetCursor (WindowList[p], j, 1);
  571.                 WriteString (WindowList[p], UIW^.title);
  572.                 UIW := UIW^.next;
  573.             END (*FOR*);
  574.             Release (ListAccess);
  575.  
  576.             (* Create an ActiveRegion which will allow the user to      *)
  577.             (* click on the list of names.                              *)
  578.  
  579.             AddActiveRegion (ListUIW,1,count,1,20,LeftOnly,
  580.                                                 SelectFromWindowList);
  581.             WindowListOpen[p] := TRUE;
  582.         END (*IF*);
  583.         ShowMouseCursor;
  584.         MoveWindow (WindowList[p], 0, 2);
  585.  
  586.     END DisplayWindowList;
  587.  
  588. (************************************************************************)
  589. (*                      CREATING A UI WINDOW                            *)
  590. (************************************************************************)
  591.  
  592. PROCEDURE MakeDefaultUIWindow;
  593.  
  594.     (* Creates a special UIWindow which does not live on any page, and  *)
  595.     (* which does not have any Window associated with it.  The sole     *)
  596.     (* purpose of this special UIWindow is to allow us to create        *)
  597.     (* active regions on parts of the screen not occupied by any        *)
  598.     (* Window known to this module.                                     *)
  599.  
  600.     BEGIN
  601.         NEW (DefaultUIW);
  602.         WITH DefaultUIW^ DO
  603.             (*win := VAL(Window,NIL);*)
  604.             CreateLock (access);
  605.             page := 0;
  606.             ActiveRegionList := NIL;
  607.             OutsideHandler := DoNothing;
  608.             previous := NIL;  next := NIL;
  609.             title := "Default UIWindow";
  610.         END (*WITH*);
  611.     END MakeDefaultUIWindow;
  612.  
  613. (************************************************************************)
  614.  
  615. PROCEDURE MakeUIWindow (p: DisplayPage;  Label: ARRAY OF CHAR): UIWindow;
  616.  
  617.     (* Creates a new UIWindow on page p, puts it into the list of all   *)
  618.     (* UIWindows.                                                       *)
  619.  
  620.     VAR result: UIWindow;
  621.  
  622.     BEGIN
  623.         (* If the window list is currently open, close it, since its    *)
  624.         (* entries are about to become invalid.                         *)
  625.  
  626.         IF WindowListOpen[p] THEN
  627.             CloseWindowList (WindowList[p], 0, 0);
  628.         END (*IF*);
  629.  
  630.         (* Create a new UIWindow record, and fill in the details.       *)
  631.  
  632.         NEW (result);
  633.         WITH result^ DO
  634.             CreateLock (access);
  635.             page := p;
  636.             ActiveRegionList := NIL;
  637.             OutsideHandler := DoNothing;
  638.             CopyString (Label, title);
  639.         END (*WITH*);
  640.  
  641.         (* Link the new record into the master list. *)
  642.  
  643.         Obtain (ListAccess);
  644.         INC (WindowCount[p]);
  645.         result^.next := FirstUIW[p];
  646.         result^.previous := NIL;
  647.         IF FirstUIW[p] <> NIL THEN
  648.             FirstUIW[p]^.previous := result;
  649.         END (*IF*);
  650.         FirstUIW[p] := result;
  651.         Release (ListAccess);
  652.  
  653.         IF (p = CurrentDisplayPage) AND (WindowCount[p] = 1) THEN
  654.             ShowMouseCursor;
  655.         END (*IF*);
  656.  
  657.         RETURN result;
  658.  
  659.     END MakeUIWindow;
  660.  
  661. (************************************************************************)
  662.  
  663. PROCEDURE AddCapabilities (UIW: UIWindow;  CS: CapabilitySet);
  664.  
  665.     (* Activates the specified set of user-controlled capabilities.     *)
  666.     (* This includes drawing the "buttons" on the border, therefore     *)
  667.     (* UIW^.win must already be open.                                   *)
  668.  
  669.     VAR w: Window;  R: Rectangle;  maxrow, maxcol: CARDINAL;
  670.         oldrow, oldcol: CARDINAL;
  671.  
  672.     BEGIN
  673.         w := UIW^.win;
  674.         SaveCursor (w, oldrow, oldcol);
  675.         R := WindowLocation (w);
  676.         WITH R DO
  677.             maxrow := bottom - top;
  678.             maxcol := right - left;
  679.         END (*WITH*);
  680.  
  681.         IF wshow IN CS THEN
  682.             AddActiveRegion (UIW,0,maxrow,0,maxcol,LeftOnly,ShowWindow);
  683.         END (*IF*);
  684.  
  685.         IF wmove IN CS THEN
  686.             SetCursor (w, 0, 1);
  687.             WriteChar (w, '[');
  688.             WriteChar (w, CHR(29));
  689.             WriteChar (w, ']');
  690.             AddActiveRegion (UIW, 0, 0, 2, 2, LeftOrRight, MoveWindow);
  691.         END (*IF*);
  692.  
  693.         IF whide IN CS THEN
  694.             SetCursor(w, 0, maxcol-3);
  695.             WriteChar(w, '[');
  696.             WriteChar(w, CHR(15));
  697.             WriteChar(w, ']');
  698.             AddActiveRegion (UIW,0,0,maxcol-2,maxcol-2,LeftOnly, HideWindow);
  699.         END (*IF*);
  700.  
  701.         IF wescape IN CS THEN
  702.             SetCursor(w, 0, maxcol-3);
  703.             WriteChar(w, '[');
  704.             WriteChar(w, CHR(15));
  705.             WriteChar(w, ']');
  706.             AddActiveRegion (UIW,0,0,maxcol-2,maxcol-2,LeftOnly, StuffEscape);
  707.         END (*IF*);
  708.  
  709.         SetCursor (w, oldrow, oldcol);
  710.  
  711.     END AddCapabilities;
  712.  
  713. (************************************************************************)
  714. (*              ACTIVATING MOUSE CONTROL FOR A WINDOW                   *)
  715. (************************************************************************)
  716.  
  717. PROCEDURE AllowMouseControl (w: Window;  Title: ARRAY OF CHAR;
  718.                                 OptionsEnabled: CapabilitySet): UIWindow;
  719.  
  720.     (* Adds w to the set of windows which this module is allowed to     *)
  721.     (* manipulate.                                                      *)
  722.  
  723.     VAR result: UIWindow;
  724.  
  725.     BEGIN
  726.         IF MouseAvailable() THEN
  727.             result := MakeUIWindow (PageOf(w), Title);
  728.             result^.win := w;
  729.             InstallCloseHandler (w, UnregisterWindow);
  730.             AddCapabilities (result, OptionsEnabled);
  731.         ELSE
  732.             result := NIL;
  733.         END (*IF*);
  734.         RETURN result;
  735.     END AllowMouseControl;
  736.  
  737. (************************************************************************)
  738. (*                      SETTING THE DISPLAY PAGE                        *)
  739. (************************************************************************)
  740.  
  741. PROCEDURE SelectPage (page: DisplayPage);
  742.  
  743.     (* Switches the mouse focus to the given display page (but we don't *)
  744.     (* update the display, it's assumed that the caller is taking care  *)
  745.     (* of that).  Also aborts any move in progress.                     *)
  746.  
  747.     BEGIN
  748.         IF WindowCount[CurrentDisplayPage] > 0 THEN
  749.             HideMouseCursor;
  750.         END(*IF*);
  751.         SetTextMousePage (page);
  752.         MoveInProgress := FALSE;
  753.         CurrentDisplayPage := page;
  754.         IF WindowCount[page] > 0 THEN
  755.             ShowMouseCursor;
  756.         END(*IF*);
  757.     END SelectPage;
  758.  
  759. (************************************************************************)
  760. (*                      THE EVENT DISPATCHER                            *)
  761. (************************************************************************)
  762.  
  763. PROCEDURE DoWindowAction (col: ColumnRange;  row: RowRange;  BS: ButtonSet);
  764.  
  765.     (* This procedure is called when a mouse button is pressed, with    *)
  766.     (* (col,row) being the current mouse cursor position.  We check     *)
  767.     (* whether any action has been defined for this position, and if so *)
  768.     (* we call the appropriate action handler.  We also call all        *)
  769.     (* applicable "outside window" handlers.                            *)
  770.  
  771.     CONST EmptySet = ButtonSet {};
  772.  
  773.     VAR w: Window;
  774.         UIW, UIW2, nextUIW: UIWindow;
  775.         CurrentRegion: ActiveRegionPtr;
  776.         RegionFound: BOOLEAN;
  777.  
  778.     BEGIN
  779.         RegionFound := FALSE;
  780.  
  781.         IF IdentifyTopWindow (w, CurrentDisplayPage, row, col) THEN
  782.             UIW := FindOwner (w);
  783.         ELSE
  784.             UIW := DefaultUIW;
  785.         END (*IF*);
  786.  
  787.         (* Call any "click outside" handlers belonging to UIWindows     *)
  788.         (* that have been defined more recently than UIW.               *)
  789.  
  790.         UIW2 := FirstUIW[CurrentDisplayPage];
  791.         WHILE (UIW2 <> NIL) AND (UIW2 <> UIW) DO
  792.             nextUIW := UIW2^.next;
  793.             UIW2^.OutsideHandler;
  794.             UIW2 := nextUIW;
  795.         END (*WHILE*);
  796.  
  797.         (* If a window has been found at this screen position, check    *)
  798.         (* whether an ActiveRegion for that window includes the cursor  *)
  799.         (* position.  Note: a side-effect of the call to procedure      *)
  800.         (* IdentifyTopWindow is that (col,row) are now window-relative. *)
  801.  
  802.         IF UIW <> NIL THEN
  803.             CurrentRegion := UIW^.ActiveRegionList;
  804.             WHILE  (CurrentRegion <> NIL) AND (NOT RegionFound) DO
  805.                 IF Inside (row, col, CurrentRegion^.region)
  806.                         AND (BS*CurrentRegion^.LiveButtons <> EmptySet) THEN
  807.                     RegionFound := TRUE;
  808.                 ELSE
  809.                     CurrentRegion := CurrentRegion^.NextActiveRegion;
  810.                 END (*IF*);
  811.             END (*WHILE*);
  812.         END (*IF *);
  813.  
  814.         IF RegionFound THEN
  815.             CurrentRegion^.ActionToBeTaken (w, row, col);
  816.         END (*IF*);
  817.  
  818.     END DoWindowAction;
  819.  
  820. (************************************************************************)
  821.  
  822. PROCEDURE EventDispatcher;
  823.  
  824.     (* Runs as a separate task, whose function is to call the           *)
  825.     (* appropriate event handler whenever a mouse event occurs.         *)
  826.  
  827.     VAR X: ColumnRange;  Y: RowRange;  A: EventSet;  B: ButtonSet;
  828.         p: EventDataPtr;
  829.  
  830.     BEGIN
  831.         LOOP
  832.             p := TakeFromQueue (EventQueue);
  833.             WITH p^ DO
  834.                 A := Trigger;
  835.                 B := ButtonStatus;
  836.                 X := Xpos;  Y := Ypos;
  837.             END (*WITH*);
  838.             DISPOSE (p);
  839.             IF MoveInProgress THEN
  840.                 IF (LeftUp IN A) OR (RightUp IN A) THEN
  841.                     MoveInProgress := FALSE;
  842.                 END (*IF*);
  843.                 ContinueMove (X, Y);
  844.             ELSE
  845.                 DoWindowAction (X, Y, B);
  846.             END (*IF*);
  847.         END (*LOOP*);
  848.     END EventDispatcher;
  849.  
  850. (************************************************************************)
  851. (*                              TERMINATION                             *)
  852. (************************************************************************)
  853.  
  854. PROCEDURE Shutdown;
  855.  
  856.     (* Closes all WindowLists. *)
  857.  
  858.     VAR p: DisplayPage;
  859.  
  860.     BEGIN
  861.         FOR p := 0 TO MAX(DisplayPage) DO
  862.             IF WindowListOpen[p] THEN
  863.                 CloseWindowList (WindowList[p], 0, 0);
  864.             END (*IF*);
  865.         END (*FOR*);
  866.     END Shutdown;
  867.  
  868. (************************************************************************)
  869. (*                        MODULE INITIALISATION                         *)
  870. (************************************************************************)
  871.  
  872. VAR p: DisplayPage;
  873.  
  874. BEGIN
  875.     FOR p := 0 TO MAX(DisplayPage) DO
  876.         FirstUIW[p] := NIL;  WindowCount[p] := 0;
  877.         WindowListOpen[p] := FALSE;
  878.     END (*FOR*);
  879.     MakeDefaultUIWindow;
  880.     MoveInProgress := FALSE;
  881.     CurrentDisplayPage := 0;
  882.     CreateLock (ListAccess);
  883.     IF MouseAvailable() THEN
  884.         RequestPageChangeNotification (SelectPage);
  885.         CreateQueue (EventQueue);
  886.         CreateTask (EventDispatcher, 8, "Mouse Events");
  887.         InstallEventHandler (EventSet {LeftDown, RightDown}, MouseClickHandler);
  888.         SelectPage (0);
  889.     END (*IF*);
  890.     AddActiveRegion (DefaultUIW, 0, MAX(RowRange), 0, MAX(ColumnRange),
  891.                                         RightOnly, DisplayWindowList);
  892.  
  893. FINALLY
  894.     Shutdown;
  895. END UserInterface.
  896.  
  897.