home *** CD-ROM | disk | FTP | other *** search
/ Amiga MA Magazine 1998 #6 / amigamamagazinepolishissue1998.iso / coders / jËzyki_programowania / oberon / system / viewers.mod (.txt) < prev    next >
Oberon Text  |  1977-12-31  |  22KB  |  429 lines

  1. Syntax10.Scn.Fnt
  2. Syntax12.Scn.Fnt
  3. ParcElems
  4. Alloc
  5. Syntax10b.Scn.Fnt
  6. Syntax10i.Scn.Fnt
  7. MODULE Viewers; (*JG 14.9.90*)
  8.     Implement Viewers on top of Display, i.e. of the screen representing the whole display area.
  9. Note: Display.FrameDesc defines the fields dsc, next and handle which are used by Viewers,but not by Display itself.
  10.     IMPORT SYSTEM(*,O*),Display,Fonts;
  11.     CONST
  12.         restore* = 0; modify* = 1; suspend* = 2; (*message ids*)
  13.         inf = MAX(INTEGER); (* "infinity" for frame and viewer sizes *)
  14.     TYPE
  15. The viewers of a track are kept in a sorted, circularly linked list. The list will always contain a filler viewer with Y=0 and H=Display.Height. The first element (as defined by where the dsc field of a track points to) is the filler viewer. The following viewer are sorted so that a viewer with a larger Y follows one with a smaller Y, i.e. they are sorted from bottom to top.
  16. Sometimes there is a test, whther the next viewer has Y=0. This is used to test, whether it is the filler frame, as, by virtue of the sorting of the list and the fact, that viewers are always kept separate by at least minH one from another, no other frame except the first in the list may have an Y position of 0. Nevertheless it is very confusing for someone reading the code to see this test, instead of the more obvious test on state=filler.
  17. On the other hand, if you look at the initialisation part of this module you see, that FillerViewer.state is never initialised!
  18.         Viewer* = POINTER TO ViewerDesc;
  19.         ViewerDesc* = RECORD (Display.FrameDesc)
  20.             state*: INTEGER    (* state is read-only. No -, because Viewer was written before Oberon-2 *)
  21.         END;
  22.        (*state > 1: displayed
  23.             state = 1: filler    (* never used?? *)
  24.             state = 0: closed
  25.             state < 0: suspended*)
  26.             The used fields are:
  27.             restore: none.
  28.             modify: Y,H and state.
  29.             suspend: state.
  30.         ViewerMsg* = RECORD (Display.FrameMsg)
  31.             id*: INTEGER;
  32.             X*, Y*, W*, H*: INTEGER;
  33.             state*: INTEGER
  34.         END;
  35.         Track = POINTER TO TrackDesc;
  36.         TrackDesc = RECORD (ViewerDesc)
  37.             under: Display.Frame (* points to tracks covered by this track. *)
  38.         END;
  39.             curW is the width of the already configured part, and is initilaized to 0.
  40.             minH is the minimum vertical distance of two viewers. This is used in Open.
  41.             DW and DH duplicate Display.Width and Display.Height.
  42.             FillerTrack is the track covering all unused display area.
  43.             FillerViewer is the viewer covering all unused track area.
  44.             buf stores the last closed viewer.
  45.         curW*, minH*, DW, DH: INTEGER;
  46.         FillerTrack: Track;
  47.         FillerViewer: Viewer; 
  48.         buf: Viewer;
  49.     PROCEDURE Open*(V: Viewer; X, Y: INTEGER);
  50. Opens a viewer if the state of the passed viewer is =closed and the X coordinate is not "infinity".
  51.         VAR T, u, v: Display.Frame; M: ViewerMsg;
  52.     BEGIN
  53.         IF (V.state = 0) & (X < inf) THEN
  54.             (*
  55.                 Truncate Y to Display.Height.
  56.             *)
  57.             IF Y > DH THEN Y := DH END;
  58.             (*
  59.                 Search the track containing X.
  60.             *)
  61.             T := FillerTrack.next;
  62.             WHILE X >= T.X + T.W DO T := T.next END;
  63.             (*
  64.                 Search the viewer in the track list, which is just below Y.
  65.                 v:=viewer containing Y.
  66.                 u:=viewer just below v.
  67.             *)
  68.             u := T.dsc; v := u.next;
  69.             WHILE Y > v.Y + v.H DO u := v; v := u.next END;
  70.             (*
  71.                 Adjust the requested Y, so that the new viewer is at least minH height.
  72.             *)
  73.             IF Y < v.Y + minH THEN Y := v.Y + minH END;
  74.             (*
  75. If the next viewer is not the filler viewer and the requested Y position is within the first minH pixel of the viewer then open the new viewer in place of the old one.
  76. Otherwise reduce the viewer containing Y to terminate at position Y, where the new viewer is opened.
  77.             *)
  78.             IF (v.next.Y # 0) & (Y > v.Y + v.H - minH) THEN
  79.                 WITH v: Viewer DO
  80.                     (*
  81.                         The new viewer is openend in place of the old one.
  82.                     *)
  83.                     V.X := T.X; V.W := T.W; V.Y := v.Y; V.H := v.H;
  84.                     (*
  85.                         The old viewer is sent a suspend message and unlinked.
  86.                         The new viewer becomes state=displayed.
  87.                     *)
  88.                     M.id := suspend; M.state := 0;
  89.                     v.handle(v, M); v.state := 0; buf := v;
  90.                     V.next := v.next; u.next := V;
  91.                     V.state := 2
  92.                 END
  93.             ELSE
  94.                 (*
  95.                     The new viewer is opened within the old one, and covers the
  96.                     area between Y and the lower bound of the old one.
  97.                 *)
  98.                 V.X := T.X; V.W := T.W; V.Y := v.Y; V.H := Y - v.Y;
  99.                 (*
  100.                     The old viewer is reduced to cover only the area between its
  101.                     top boundary and Y, and receives a modify message.
  102.                 *)
  103.                 M.id := modify; M.Y := Y; M.H := v.Y + v.H - Y;
  104.                 v.handle(v, M); v.Y := M.Y; v.H := M.H;
  105.                 (*
  106.                     The new viewer is linked into the list and gets state=displayed.
  107.                 *)
  108.                 V.next := v; u.next := V;
  109.                 V.state := 2
  110.             END
  111.         END
  112.     END Open;
  113.     PROCEDURE Change*(V: Viewer; Y: INTEGER);
  114. Reduce the viewer by moving the lower edge to the new Y value. This procedure cannot be used to increase the viewer, i.e. move the lower edge towards the bottom of the screen.
  115.         v: Display.Frame; M: ViewerMsg;
  116.     BEGIN
  117.         IF V.state > 1 THEN
  118.             (*
  119.                 truncate Y against Display.Height.
  120.             *)
  121.             IF Y > DH THEN Y := DH END;
  122.             (*
  123.                 If the next next viewer is not the filler viewer then make sure, 
  124.                 that the new Y value is at least minH below the top edge fo the
  125.                 next window.
  126.             *)
  127.             v := V.next;
  128.             IF (v.next.Y # 0) & (Y > v.Y + v.H - minH) THEN
  129.                 Y := v.Y + v.H - minH
  130.             END;
  131.             (*
  132.                 Modify only, if the new position is at least minH above the current position.
  133.             *)
  134.             IF Y >= V.Y + minH THEN
  135.                 M.id := modify; M.Y := Y; M.H := v.Y + v.H - Y;
  136.                 v.handle(v, M); v.Y := M.Y; v.H := M.H;
  137.                 V.H := Y - V.Y
  138.             END
  139.         END
  140.     END Change;
  141.     PROCEDURE RestoreTrack(S: Display.Frame);
  142.         Remove track S and restore all tracks currently covered by S.
  143.         T, t, v: Display.Frame; M: ViewerMsg;
  144.     BEGIN
  145.         WITH S: Track DO
  146.             (*
  147.                 Search a (the?) track, preceeding one with the same X position as this one.
  148.                 Is this really a search for the predecessor of S? Then why not test for t.next=S?
  149.             *)
  150.             t := S.next;
  151.             WHILE t.next.X # S.X DO t := t.next END;
  152.             (*
  153. Get the list of tracks covered by this one, and locate the last track in this list. This seems to indicate, that the list of covered tracks is no more a circular one.
  154.             *)
  155.             T := S.under;
  156.             WHILE T.next # NIL DO T := T.next END;
  157.             (*
  158. All tracks in the S.under list are inserted between the track preceeding S and the track  following S. This operation removes S from the track list. 
  159.             *)
  160.             t.next := S.under; T.next := S.next;
  161.             (*
  162. A restore message is sent to all viewers of the newly uncovered tracks. Then the state of the viewer is chagend from suspended to displayed.
  163.             *)
  164.             M.id := restore;
  165.             REPEAT
  166.                 t := t.next;
  167.                 v := t.dsc;
  168.                 REPEAT
  169.                     v := v.next; v.handle(v, M);
  170.                     WITH v: Viewer DO v.state := - v.state END
  171.                 UNTIL v = t.dsc
  172.             UNTIL t = T
  173.         END
  174.     END RestoreTrack;
  175.     PROCEDURE Close*(V: Viewer);
  176.         Closes a viewer, if it has state=displayed.
  177.         T, U: Display.Frame; M: ViewerMsg;
  178.     BEGIN
  179.         IF V.state > 1 THEN
  180.             U := V.next;
  181.             (*
  182.                 Search the track which contains this viewer.
  183.             *)
  184.             T := FillerTrack;
  185.             REPEAT T := T.next UNTIL V.X < T.X + T.W;
  186.             (*
  187. If the viewer is not the only one in this track, or  the track does not cover any others, then only the viewer is closed. Thus the track will survive the closure of the last window on it, if it doesn't cover any other tracks.
  188.             *)
  189.             IF (T(Track).under = NIL) OR (U.next # V) THEN
  190.                 (*
  191. Send a suspend message to the viewer to be closed, then set it's state to closed and assigned it to the last closed viewer buffer.
  192.                 *)
  193.                 M.id := suspend; M.state := 0;
  194.                 V.handle(V, M);
  195.                 V.state := 0; buf := V;
  196.                 (*
  197. Send a modify message to the viewer above the closed one. Then adjust its Y and H fields.
  198.                 *)
  199.                 M.id := modify; M.Y := V.Y; M.H := V.H + U.H;
  200.                 U.handle(U, M); U.Y := M.Y; U.H := M.H;
  201.                 (*
  202. Search the viewer preceeding the closed one, and unlink the closed one from the viewer list.
  203.                 *)
  204.                 WHILE U.next # V DO U := U.next END;
  205.                 U.next := V.next
  206.             ELSE (*close track*)
  207.                 (*
  208. Send a suspend message to the closed viewer and to the filler viewer of this track. Remeber the closed viewer in the buffer and use RestoreTrack to close the track and make all covered tracks visible.
  209.                 *)
  210.                 M.id := suspend; M.state := 0;
  211.                 V.handle(V, M);
  212.                 V.state := 0; buf := V;
  213.                 U.handle(U, M); U(Viewer).state := 0;
  214.                 RestoreTrack(T);
  215.             END
  216.         END
  217.     END Close;
  218.     PROCEDURE Recall*( VAR V: Viewer);
  219.         Returns the last closed viewer.
  220.     BEGIN
  221.         V := buf
  222.     END Recall;
  223.     PROCEDURE This*(X, Y: INTEGER): Viewer;
  224.         Return the viewer which contains point X,Y.
  225.         T, V: Display.Frame;
  226.     BEGIN
  227.         IF (X < inf) & (Y < DH) THEN
  228.             (*
  229.                 Search the track, which contains the X component of the point. There is no test for
  230.                 the end of the list, because all area is covered either by a "real" track, or the filler track.
  231.                 Also, the search assumes, that the list is ordered, from left to right.
  232.             *)
  233.             T := FillerTrack;
  234.             REPEAT T := T.next UNTIL X < T.X + T.W;
  235.             (*
  236.                 Search the viewer containing the Y component of the point. There is no test for
  237.                 the end of the list, because all area is covered either by a "real" viewer, or the filler viewer.
  238.                 Also, the search assumes, that the list is ordered, from low to high.
  239.             *)
  240.             V := T.dsc;
  241.             REPEAT V := V.next UNTIL Y < V.Y + V.H;
  242.             RETURN V(Viewer)
  243.         ELSE
  244.             RETURN NIL
  245.         END
  246.     END This;
  247.     PROCEDURE Next* (V: Viewer): Viewer;
  248.         Return the next viewer in the track. I don't really know, why this procedure is there,
  249.         as the next field is exported.
  250.     BEGIN
  251.         RETURN V.next(Viewer);
  252.     END Next;
  253.     PROCEDURE Locate*(X, H: INTEGER; VAR fil, bot, alt, max: Display.Frame);
  254. This procedure examines the viewers in the track containing X and returns this four viewers:
  255.     fil: filler viewer of the track.
  256.     bot: bottom viewer of this track.
  257.     alt: The first viewer (from bottom) with a height of at least H.
  258.     max: the viewer which has the greates height.
  259. If the track doesn't contain any viewers, than bot, alt and max are equal to the filler viewer. If there is exactly one viewer, than alt will always return it.
  260. If there are at least two viewers, alt will start the search with the second one. Thus it will never return the first (most bottom) viewer, even if it larger or equal to H. If none of the searched viewers is larger than H, then the largest one is returned.
  261.         T, V: Display.Frame;
  262.     BEGIN
  263.         IF X < inf THEN
  264.             (*
  265.                 Locate the track containing X.
  266.             *)
  267.             T := FillerTrack;
  268.             REPEAT T := T.next UNTIL X < T.X + T.W;
  269.             (*
  270.                 Get the filler and the bottom viewer.
  271.             *)
  272.             fil := T.dsc;
  273.             bot := fil.next;
  274.             (*
  275.                 If there is more than one viewer, then search (starting with the second viewer) for
  276.                 the first one with height>=H. If non found, return the one with the greates height.
  277.                 If there is less then two viewers, return the one found, or the filler viewer. 
  278.             *)
  279.             IF bot.next # fil THEN
  280.                 alt := bot.next;
  281.                 V := alt.next;
  282.                 WHILE (V # fil) & (alt.H < H) DO
  283.                     IF V.H > alt.H THEN alt := V END;
  284.                     V := V.next
  285.                 END
  286.             ELSE
  287.                 alt := bot
  288.             END;
  289.             (*
  290.                 Set max to the viewer with the greatest height.
  291.             *)
  292.             max := T.dsc;
  293.             V := max.next;
  294.             WHILE V # fil DO
  295.                 IF V.H > max.H THEN max := V END;
  296.                 V := V.next
  297.             END
  298.         END
  299.     END Locate;
  300.     PROCEDURE InitTrack*(W, H: INTEGER; Filler: Viewer);
  301.         Create a new track with specified width and height.
  302.         S: Display.Frame;
  303.         T: Track;
  304.     BEGIN
  305.         IF Filler.state = 0 THEN
  306.             (*
  307.                 Prepare the filler viewer for the track, by setting the correct bounding box, state and performing
  308.                 the linking for the circular list.
  309.             *)
  310.             Filler.X := curW; Filler.W := W;
  311.             Filler.Y := 0; Filler.H := H;
  312.             Filler.state := 1; (* = filler *)
  313.             Filler.next := Filler;
  314.             (* Filler.dsc is not initialized. *)
  315.             (*
  316.                 Create and initialize the new track.
  317.             *)
  318.             NEW(T);
  319.             T.X := curW; T.W := W; T.Y := 0; T.H := H;
  320.             T.dsc := Filler; T.under := NIL;
  321.             (* FillerTrack.state is not initialized. *)
  322.             (*
  323. The filler track, and its filler viewer are reduced to occupy only the remaining room at the right of this track.
  324.             *)
  325.             FillerViewer.X := curW + W; FillerViewer.W := inf - FillerViewer.X;
  326.             FillerTrack.X := FillerViewer.X; FillerTrack.W := FillerViewer.W;
  327.             (*
  328. Search for the predecessor of the filler track, and add the new track to the track list. Adjust curW.
  329.             *)
  330.             S := FillerTrack;
  331.             WHILE S.next # FillerTrack DO S := S.next END;
  332.             S.next := T;
  333.             T.next := FillerTrack;
  334.             curW := curW + W
  335.         END
  336.     END InitTrack;
  337.     PROCEDURE OpenTrack*(X, W: INTEGER; Filler: Viewer);
  338. Open a new track at position X with width W over existing tracks. X and W are "proposals". X is adjusted to the left, so that it fully covers the track within which the original X lied. W is adjusted, so that the new track fully covers the track on which X+W lied.
  339.         newT: Track; S, T, t, v: Display.Frame; M: ViewerMsg;
  340.     BEGIN
  341.         IF (X < inf) & (Filler.state = 0) THEN
  342.             (*
  343. Search for the rightmost track (S) which is completely at the left of X and for the track (T) which contains the position X+W.
  344.             *)
  345.             S := FillerTrack; T := S.next;
  346.             WHILE X >= T.X + T.W DO S := T; T := S.next END;
  347.             WHILE X + W > T.X + T.W DO T := T.next END;
  348.             (*
  349. Send a suspend message to all viewers which are (partially) covered by the new track.
  350. Note: It seems, that filler viewers may be suspended too.
  351.             *)
  352.             M.id := suspend;
  353.             t := S;
  354.             REPEAT
  355.                 t := t.next; v := t.dsc;
  356.                 REPEAT v := v.next;
  357.                     WITH v: Viewer DO
  358.                         M.state := -v.state; v.handle(v, M); v.state := M.state
  359.                     END
  360.                 UNTIL v = t.dsc
  361.             UNTIL t = T;
  362.             (*
  363.                 Prepare the list of viewers, as list containing only the filler viewer.
  364.             *)
  365.             Filler.X := S.next.X; Filler.W := T.X + T.W - S.next.X; Filler.Y := 0; Filler.H := DH;
  366.             Filler.state := 1;
  367.             Filler.next := Filler;
  368.             (*
  369. Create the new track. Link in the viewer list. Move the list of covered track under this track, and link in this track into the track list.
  370.             *)
  371.             NEW(newT);
  372.             newT.X := Filler.X; newT.W := Filler.W; newT.Y := 0; newT.H := DH;
  373.             newT.dsc := Filler; newT.under := S.next; S.next := newT;
  374.             newT.next := T.next; T.next := NIL
  375.         END
  376.     END OpenTrack;
  377.     PROCEDURE CloseTrack*(X: INTEGER);
  378.         VAR T, V: Display.Frame; M: ViewerMsg;
  379.     BEGIN
  380.         IF X < inf THEN
  381.             T := FillerTrack;
  382.             REPEAT T := T.next UNTIL X < T.X + T.W;
  383.             IF T(Track).under # NIL THEN
  384.                 M.id := suspend; M.state := 0; V := T.dsc;
  385.                 REPEAT V := V.next; V.handle(V, M); V(Viewer).state := 0 UNTIL V = T.dsc;
  386.                 RestoreTrack(T)
  387.             END
  388.         END
  389.     END CloseTrack;
  390.     PROCEDURE Broadcast*(VAR M: Display.FrameMsg);
  391.         Call the handler of each viewer in each track, and pass it the message M, and the viewer itself.
  392.         T, V: Display.Frame;
  393.     BEGIN
  394.         T := FillerTrack.next;
  395.         WHILE T # FillerTrack DO
  396.             V := T.dsc; 
  397.             REPEAT V := V.next; V.handle(V, M) UNTIL V = T.dsc;
  398.             T := T.next
  399.         END
  400.     END Broadcast;
  401. PROCEDURE TestHan(f: Display.Frame; VAR msg: Display.FrameMsg);
  402.     InputMsg = RECORD (Display.FrameMsg)
  403.         id: INTEGER;
  404.         keys: SET;
  405.         X, Y: INTEGER;
  406.     END;
  407.     InputMsgPtr=POINTER TO InputMsg;
  408.     inp:InputMsgPtr;
  409. BEGIN
  410.     inp:=SYSTEM.VAL(InputMsgPtr,SYSTEM.ADR(msg));
  411.     O.Str("TestHan:"); O.Int(inp.id); O.Int(inp.X); O.Int(inp.Y); O.Ln;
  412. END TestHan;
  413. BEGIN
  414.     buf := NIL;
  415.     DW := Display.Width; DH := Display.Height; (* copy for "easier" access. *)
  416.     curW := 0; minH := Fonts.Default.height + 4;
  417.     Create two circular linked lists, one containing the only viewer FillerViewer, and the other containing the
  418.     only track FillerTrack.
  419.     NOTE: The filler viewer has no handler !
  420.     NEW(FillerViewer);
  421.     FillerViewer.X := 0; FillerViewer.W := inf; FillerViewer.Y := 0; FillerViewer.H := DH;
  422.     FillerViewer.next := FillerViewer; (* FillerViewer.dsc and FillerViewer.state are not initialized. *)
  423.     FillerViewer.handle:=TestHan;
  424.     NEW(FillerTrack);
  425.     FillerTrack.X := 0; FillerTrack.W := inf; FillerTrack.Y := 0; FillerTrack.H := DH;
  426.     FillerTrack.dsc := FillerViewer; (* A filler viewer is always present in every track. *)
  427.     FillerTrack.next := FillerTrack; (* FillerTrack.under and FillerTrack.state are not initialized. *)
  428. END Viewers.
  429.