Syntax10.Scn.Fnt MODULE KeplerFrames; (* J. Templ, 18.06.92, for PowerMac *) IMPORT KeplerPorts, KeplerGraphs, TextFrames, Viewers, MenuViewers, Display, Oberon, Files, Input, Texts, Fonts, TextPrinter; CONST invFoc = 2; (* notify op-codes *) xlen = 3 * 4; eps = xlen + 4; ML = 2; MM = 1; MR = 0; cancel = {ML, MM, MR}; DEL = 7FX; BS = 08X; fg = Display.white; TYPE FocusPoint* = POINTER TO FocusPointDesc; FocusPointDesc* = RECORD next*: FocusPoint; p*: KeplerGraphs.Star; END ; Button* = POINTER TO ButtonDesc; ButtonDesc* = RECORD (KeplerGraphs.ConsDesc) cmd*, par*: ARRAY 32 OF CHAR END ; Caption* = POINTER TO CaptionDesc; CaptionDesc* = RECORD (KeplerGraphs.ConsDesc) s*: ARRAY 128 OF CHAR; fnt*: Fonts.Font; align*: SHORTINT (* 0 = left, 1 = centerX, 2 = right, 3 = centerXY *) END ; Frame* = POINTER TO FrameDesc; FrameDesc* = RECORD (KeplerPorts.DisplayPortDesc); G*: KeplerGraphs.Graph; col*, grid*: INTEGER; END ; UpdateMsg* = RECORD (Display.FrameMsg) id*: INTEGER; G*: KeplerGraphs.Graph; O*: KeplerGraphs.Object; P*: KeplerPorts.Port END ; SelMsg* = RECORD (Display.FrameMsg) time*: LONGINT; G*: KeplerGraphs.Graph END ; Notifier* = PROCEDURE (op: INTEGER; G: KeplerGraphs.Graph; O: KeplerGraphs.Object; P: KeplerPorts.Port); (*the graphics caret consists of a number of focus points and an optional focus caption *) Focus*: KeplerGraphs.Graph; first*, last*: FocusPoint; nofpts*: INTEGER; focus*: Caption; carpos*: INTEGER; upd: Frame; PROCEDURE Min(x, y: INTEGER): INTEGER; BEGIN IF x < y THEN RETURN x ELSE RETURN y END END Min; PROCEDURE Max(x, y: INTEGER): INTEGER; BEGIN IF x > y THEN RETURN x ELSE RETURN y END END Max; PROCEDURE NotifyDisplay* (op: INTEGER; G: KeplerGraphs.Graph; O: KeplerGraphs.Object; P: KeplerPorts.Port); VAR M: UpdateMsg; BEGIN M.id := op; M.G := G; M.O := O; M.P := P; Viewers.Broadcast(M) END NotifyDisplay; PROCEDURE AppendFocusPoint*(p: KeplerGraphs.Star); VAR fp: FocusPoint; BEGIN NEW(fp); fp.p := p; fp.next := NIL; IF last = NIL THEN first := fp ELSE last.next := fp END ; last := fp; INC(nofpts); NotifyDisplay(invFoc, Focus, p, NIL) END AppendFocusPoint; PROCEDURE DeleteFocusPoint*(F: Frame); VAR p: FocusPoint; BEGIN IF last # NIL THEN NotifyDisplay(invFoc, Focus, last.p, NIL); IF nofpts = 1 THEN first := NIL; last := NIL; nofpts := 0 ELSIF nofpts > 1 THEN p := first; WHILE p^.next # last DO p := p^.next END ; p.next := NIL; DEC(nofpts); last := p END END END DeleteFocusPoint; PROCEDURE IsFocusPoint*(p: KeplerGraphs.Star): BOOLEAN; VAR fp: FocusPoint; BEGIN fp := first; WHILE (fp # NIL) & (fp.p # p) DO fp := fp.next END ; RETURN fp # NIL END IsFocusPoint; PROCEDURE ThisButton*(G: KeplerGraphs.Graph; x, y: INTEGER): Button; VAR b: Button; c: KeplerGraphs.Constellation; p0, p1: KeplerGraphs.Star; BEGIN c := G.cons; b := NIL; WHILE c # NIL DO IF c IS Button THEN p0 := c.p[0]; p1 := c.p[1]; IF ((x > p0.x) = (x < p1.x)) & ((y > p0.y) = (y < p1.y)) THEN b := c(Button) END END ; c := c.next END ; RETURN b END ThisButton; PROCEDURE MarkedButton*(): Button; VAR V: Viewers.Viewer; F: Frame; BEGIN V := Oberon.MarkedViewer(); IF (V.dsc # NIL) & (V.dsc.next # NIL) & (V.dsc.next IS Frame) THEN F := V.dsc.next(Frame); RETURN ThisButton(F.G, F.Cx(Oberon.Pointer.X), F.Cy(Oberon.Pointer.Y)) ELSE RETURN NIL END END MarkedButton; PROCEDURE ThisPoint(G: KeplerGraphs.Graph; x, y: INTEGER): KeplerGraphs.Star; VAR fp: FocusPoint; p: KeplerGraphs.Star; BEGIN fp := first; WHILE (fp # NIL) & ((ABS(fp.p.x - x) > eps) OR (ABS(fp.p.y - y) > eps)) DO fp := fp.next END ; IF (fp = NIL) OR (fp.p.refcnt = 0) OR (fp.p IS KeplerGraphs.Planet) OR (G # Focus) THEN p := G.stars; WHILE (p # NIL) & ((ABS(p.x - x) > eps) OR (ABS(p.y - y) > eps)) DO p := p.next END ; ELSE p := fp.p END ; RETURN p END ThisPoint; PROCEDURE ThisCaption*(G: KeplerGraphs.Graph; x, y: INTEGER): Caption; VAR s: Caption; c: KeplerGraphs.Constellation; p: KeplerPorts.BalloonPort; BEGIN IF ThisPoint(G, x, y) # NIL THEN RETURN NIL END ; c := G.cons; s := NIL; NEW(p); WHILE c # NIL DO IF c IS Caption THEN KeplerPorts.InitBalloon(p); c.Draw(p); IF (x > p.X) & (x <= p.X + p.W) & (y > p.Y) & (y < p.Y + p.H) THEN s := c(Caption) END END ; c := c.next END ; RETURN s END ThisCaption; PROCEDURE GetPoint* (VAR p: KeplerGraphs.Star); VAR fp: FocusPoint; BEGIN fp := first; p := fp.p; first := fp.next; IF first = NIL THEN last := NIL END; NotifyDisplay(invFoc, Focus, p, NIL); DEC(nofpts) END GetPoint; PROCEDURE ConsumePoint* (VAR p: KeplerGraphs.Star); BEGIN GetPoint(p); IF (p.refcnt = 0) & ~(p IS KeplerGraphs.Planet) THEN Focus.Append(p) END ; INC(p.refcnt) END ConsumePoint; PROCEDURE SelectObjects(G: KeplerGraphs.Graph; x, y: INTEGER); VAR c: KeplerGraphs.Constellation; B: KeplerPorts.BalloonPort; i: INTEGER; BEGIN c := G.cons; NEW(B); WHILE c # NIL DO KeplerPorts.InitBalloon(B); c.Draw(B); IF (B.X <= x) & (B.X + B.W >= x) & (B.Y <= y) & (B.Y + B.H >= y) THEN FOR i := 0 TO c.nofpts-1 DO IF ~c.p[i].sel THEN G.FlipSelection(c.p[i]) END END END ; c := c.next END END SelectObjects; PROCEDURE SelectPoints(G: KeplerGraphs.Graph; x0, y0, x1, y1: INTEGER); VAR p: KeplerGraphs.Star; BEGIN p := G.stars; IF (x0 = x1) & (y0 = y1) THEN WHILE p # NIL DO IF (p.x >= x0-12) & (p.x <= x0+12) & (p.y >= y0-12) & (p.y <= y0+12) THEN G.FlipSelection(p); RETURN END ; p := p.next END ; SelectObjects(G, x0, y0) ELSE WHILE p # NIL DO IF ~p.sel THEN IF (p.x >= x0) & (p.x <= x1) & (p.y >= y0) & (p.y <= y1) THEN G.FlipSelection(p) (* direct selection *) END END ; p := p.next END END END SelectPoints; PROCEDURE AlignToGrid*(F: Frame; VAR X, Y: INTEGER); VAR dX, dY: INTEGER; BEGIN IF F.grid > 0 THEN dX := X - F.CX(0) + F.grid DIV 2; dY := Y - F.CY(0) + F.grid DIV 2; X := F.CX(0) + dX - dX MOD F.grid; Y := F.CY(0) + dY - dY MOD F.grid END END AlignToGrid; PROCEDURE GetMouse* (F: Frame; VAR x, y: INTEGER; VAR keys: SET); VAR X, Y: INTEGER; BEGIN Input.Mouse(keys, X, Y); AlignToGrid(F, X, Y); x := F.Cx(X); y := F.Cy(Y) END GetMouse; PROCEDURE DrawGrid(F: Frame); (* aligned to (x0, y0) *) CONST minGrid = 20; VAR grid, i, j: INTEGER; BEGIN IF F.grid < minGrid THEN grid := ((minGrid - 1) DIV F.grid + 1) * F.grid ELSE grid := F.grid END ; i := F.X + F.x0 DIV F.scale MOD grid; WHILE i < F.X + F.W DO j := F.Y + (F.H + F.y0 DIV F.scale) MOD grid; WHILE j < F.Y + F.H DO Display.ReplConst(Display.white, i, j, 1, 1, Display.replace); INC(j, grid) END ; INC(i, grid) END END DrawGrid; (* ------------------------------------ Button methods ------------------------------------ *) PROCEDURE (B: Button) Execute* (keys: SET); VAR res: INTEGER; par: Oberon.ParList; W: Texts.Writer; cmd: ARRAY 32 OF CHAR; BEGIN IF keys = {MM} THEN NEW(par); par.vwr := Viewers.This(Display.Width-1, Display.Height-1); par.frame := par.vwr.dsc.next; par.text := TextFrames.Text(""); par.pos := 0; Texts.OpenWriter(W); Texts.WriteString(W, B.par); Texts.Append(par.text, W.buf); COPY(B.cmd, cmd); Oberon.Call(cmd, par, FALSE, res) ELSIF keys = {MM, MR} THEN Texts.OpenWriter(W); Texts.WriteString(W, B.cmd); Texts.Write(W, " "); Texts.WriteString(W, B.par); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf) END END Execute; PROCEDURE^ (F: Frame) TrackMouse* (x, y: INTEGER; keys: SET); PROCEDURE (B: Button) HandleMouse*(F: Frame; x, y: INTEGER; keys: SET); VAR keySum: SET; x0, y0, w, h: INTEGER; BEGIN IF MM IN keys THEN keySum := keys; x0 := Min(B.p[0].x, B.p[1].x); y0 := Min(B.p[0].y, B.p[1].y); w := ABS(B.p[0].x - B.p[1].x); h := ABS(B.p[0].y - B.p[1].y); F.DrawRect(x0-4, y0-4, w+8, h+8, 5, Display.invert); REPEAT F.TrackMouse(x, y, keys); GetMouse(F, x, y, keys); keySum := keySum + keys UNTIL keys = {}; F.DrawRect(x0-4, y0-4, w+8, h+8, 5, Display.invert); B.Execute(keySum) END END HandleMouse; PROCEDURE (B: Button) Write* (VAR R: Files.Rider); BEGIN Files.WriteString(R, B.cmd); Files.WriteString(R, B.par); B.Write^(R) END Write; PROCEDURE (B: Button) Read* (VAR R: Files.Rider); BEGIN Files.ReadString(R, B.cmd); Files.ReadString(R, B.par); B.Read^(R) END Read; (* ------------------------------- Caption ------------------------------- *) PROCEDURE FlipCaret(p: KeplerPorts.Port; x, y, h: INTEGER); BEGIN p.FillRect(x, y - 4, 4, h + 8, Display.white, 5, Display.invert) END FlipCaret; PROCEDURE CarPos(VAR s: ARRAY OF CHAR; fnt: Fonts.Font; carpos: INTEGER) : INTEGER; VAR fno: SHORTINT; ch: CHAR; dx, w, i, sdx, sx, sy, sw, sh: INTEGER; p: LONGINT; BEGIN fno := TextPrinter.FontNo(fnt); w := 0; i := 0; ch := s[0]; WHILE i < carpos DO dx := SHORT(TextPrinter.DX(fno, ch) DIV 3048); INC(w, dx); INC(i); ch := s[i] END ; RETURN w END CarPos; PROCEDURE (C: Caption) Draw*(F: KeplerPorts.Port); VAR x, y, w: INTEGER; p: KeplerPorts.BalloonPort; BEGIN x := C.p[0].x; y := C.p[0].y; IF C.align # 0 THEN w := KeplerPorts.StringWidth(C.s, C.fnt); IF C.align = 1 THEN DEC(x, w DIV 2) ELSIF C.align = 2 THEN DEC(x, w) ELSIF C.align = 3 THEN DEC(x, w DIV 2); DEC(y, (C.fnt.height DIV 2 + C.fnt.minY) * 4) ELSE DEC(y, C.fnt.maxY * 4); IF C.align = 5 THEN DEC(x, w DIV 2) ELSIF C.align = 6 THEN DEC(x, w) END END END ; F.DrawString(x, y, C.s, C.fnt, Display.white, Display.paint); IF (F IS Frame) & (focus = C) THEN w := CarPos(C.s, C.fnt, carpos); NEW(p); KeplerPorts.InitBalloon(p); C.Draw(p); FlipCaret(F, p.X + w, p.Y, p.H) END END Draw; PROCEDURE (C: Caption) Write* (VAR R: Files.Rider); BEGIN (*upward compatible encoding of C.align*) IF C.align # 0 THEN Files.Write(R, C.align) END ; Files.WriteString(R, C.s); Files.WriteString(R, C.fnt.name); C.Write^(R) END Write; PROCEDURE (C: Caption) Read* (VAR R: Files.Rider); VAR fntname: ARRAY 32 OF CHAR; BEGIN (*upward compatible encoding of C.align*) Files.Read(R, C.align); IF (C.align = 0) OR (C.align > 3) THEN C.align := 0; Files.Set(R, Files.Base(R), Files.Pos(R) - 1) END ; Files.ReadString(R, C.s); Files.ReadString(R, fntname); C.fnt := Fonts.This(fntname); C.Read^(R) END Read; (* ------------------------------------ Frame methods ------------------------------------ *) PROCEDURE (F: Frame) TrackMouse* (x, y: INTEGER; keys: SET); BEGIN Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, Max(F.CX(x), 0), Min(F.CY(y), Display.Height)); END TrackMouse; PROCEDURE (F: Frame) Reduce* (newY: INTEGER); BEGIN F.H := F.H + F.Y - newY; F.Y := newY; END Reduce; PROCEDURE (F: Frame) Invert* (p: KeplerGraphs.Star); BEGIN IF (p IS KeplerGraphs.Planet) OR (p.refcnt > 0) THEN (* + *) F.DrawLine(p.x - xlen - 4, p.y, p.x + xlen + 4, p.y, Display.white, Display.invert); F.DrawLine(p.x, p.y + xlen + 4, p.x, p.y - xlen - 4, Display.white, Display.invert) ELSE (* x *) F.DrawLine(p.x - xlen, p.y - xlen, p.x + xlen, p.y + xlen, Display.white, Display.invert); F.DrawLine(p.x - xlen, p.y + xlen, p.x + xlen, p.y - xlen, Display.white, Display.invert) END END Invert; PROCEDURE Intersect(F: Frame; VAR X, Y, W, H: INTEGER): BOOLEAN; VAR t: INTEGER; BEGIN t := X+W; IF F.X > X THEN X := F.X END; IF F.X+F.W < t THEN W := F.X+F.W-X ELSE W := t-X END; IF W <= 0 THEN RETURN FALSE END; t := Y+H; IF F.Y > Y THEN Y := F.Y END; IF F.Y+F.H < t THEN H := F.Y+F.H-Y ELSE H := t-Y END; RETURN H > 0 END Intersect; PROCEDURE InvFocus(F: Frame); VAR fp: FocusPoint; BEGIN IF Focus = F.G THEN fp := first; WHILE fp # NIL DO F.Invert(fp.p); fp := fp.next END END END InvFocus; PROCEDURE (F: Frame) Extend*(newY: INTEGER); VAR dY, newH: INTEGER; BEGIN dY := F.Y - newY; Display.ReplConst(F.col, F.X, newY, F.W, F.Y - newY, Display.replace); F.H := F.H + F.Y - newY; F.Y := newY; newH := F.H; INC(F.y0, (newH - dY) * F.scale); F.H := dY; IF F.grid > 0 THEN DrawGrid(F) END; F.G.Draw(F); InvFocus(F); F.H := newH; DEC(F.y0, (newH - dY) * F.scale) END Extend; PROCEDURE (F: Frame) Restore*(X, Y, W, H: INTEGER); BEGIN IF (W > 0) & (H > 0) THEN upd.col := F.col; upd.G := F.G; upd.grid := F.grid; upd.scale := F.scale; upd.X := X; upd.Y := Y; upd.W := W; upd.H := H; IF Intersect(F, upd.X, upd.Y, upd.W, upd.H) THEN H := upd.H; upd.x0 := F.x0 + (F.X - upd.X) * F.scale; upd.y0 := F.y0 + (F.Y + F.H - upd.Y - upd.H) * F.scale; Oberon.RemoveMarks(upd.X, upd.Y, upd.W, upd.H); upd.Reduce(upd.Y + upd.H); upd.Extend(upd.Y - H) END END END Restore; PROCEDURE MoveOrigin*(F: Frame; x0, y0: INTEGER); VAR X, Y, W, H, dX, dY: INTEGER; BEGIN dX := (x0 - F.x0) DIV F.scale; dY := (y0 - F.y0) DIV F.scale; IF (dX # 0) OR (dY # 0) THEN F.x0 := x0; F.y0 := y0; Oberon.RemoveMarks(F.X, F.Y, F.W, F.H); X := F.X + dX; Y := F.Y + dY; W := F.W; H := F.H; IF Intersect(F, X, Y, W, H) THEN Display.CopyBlock(X-dX, Y-dY, W, H, X, Y, Display.replace) END ; IF dY > 0 THEN F.Restore(F.X, F.Y, F.W, dY); Y := F.Y + dY ELSIF dY < 0 THEN F.Restore(F.X, F.Y + F.H + dY, F.W, -dY); Y := F.Y END; IF dX > 0 THEN F.Restore(F.X, Y, dX, F.H - ABS(dY)) ELSIF dX < 0 THEN F.Restore(F.X + F.W + dX, Y, -dX, F.H - ABS(dY)) END END END MoveOrigin; PROCEDURE Move(F: Frame; x1, y1: INTEGER); VAR keySum, keys: SET; x0, y0, x10, y10, x2, y2: INTEGER; dragSel, dragOrg: BOOLEAN; BEGIN x0 := F.x0; y0 := F.y0; x10 := x1; y10 := y1; keySum := {MM}; dragSel := FALSE; dragOrg := FALSE; REPEAT GetMouse(F, x2, y2, keys); F.TrackMouse(x2, y2, keys); keySum := keySum + keys; IF keySum = cancel THEN IF dragSel THEN F.G.MoveSelection(x10 - x1, y10 - y1); dragSel := FALSE ELSIF dragOrg THEN MoveOrigin(F, x0, y0); dragOrg := FALSE END ELSIF keySum = {MM, ML} THEN IF (x1 # x2) OR (y1 # y2) THEN F.G.MoveSelection(x2 - x1, y2 - y1); x1 := x2; y1 := y2; dragSel := TRUE END ; ELSIF keySum = {MM, MR} THEN dragOrg := TRUE; MoveOrigin(F, F.x0 + x2 - x1, F.y0 + y2 - y1) END UNTIL keys = {}; IF keySum = {MM} THEN F.G.MoveSelection(x2 - x1, y2 - y1) END END Move; PROCEDURE DrawFrame(F: Frame; x1, y1, x2, y2: INTEGER); VAR t: INTEGER; BEGIN IF x1 > x2 THEN t := x1; x1 := x2; x2 := t END; IF y1 > y2 THEN t := y1; y1 := y2; y2 := t END; t := F.scale; F.FillRect(x1, y1, x2-x1, t, fg, 5, Display.invert); F.FillRect(x1, y2, x2-x1, t, fg, 5, Display.invert); F.FillRect(x1, y1, t, y2-y1, fg, 5, Display.invert); F.FillRect(x2, y1, t, y2-y1, fg, 5, Display.invert) END DrawFrame; PROCEDURE Select(F: Frame; x, y: INTEGER); VAR x1, y1, x2, y2: INTEGER; keySum, keys: SET; p0, p1: KeplerGraphs.Star; BEGIN keySum := {MR}; x1 := x; y1 := y; keys := {}; DrawFrame(F, x, y, x1, y1); (* for symmetry only *) LOOP F.TrackMouse(x1, y1, keys); GetMouse(F, x2, y2, keys); keySum := keySum + keys; IF keys = {} THEN EXIT END; IF x2 # x1 THEN DrawFrame(F, x1, y, x2, y1); x1 := x2 END; IF y2 # y1 THEN DrawFrame(F, x, y1, x1, y2); y1 := y2 END END; DrawFrame(F, x, y, x1, y1); IF keySum # cancel THEN SelectPoints(F.G, Min(x, x1), Min(y, y1), Max(x, x1), Max(y, y1)); IF keySum = {ML, MR} THEN F.G.DeleteSelection(2) ELSIF (keySum = {MM, MR}) & (nofpts >= 2) THEN GetPoint(p0); GetPoint(p1); Focus.CopySelection(F.G, p1.x - p0.x, p1.y - p0.y) END END END Select; PROCEDURE GetSelection*(VAR sel: KeplerGraphs.Graph); VAR M: SelMsg; BEGIN M.time := -1; M.G := NIL; Viewers.Broadcast(M); sel := M.G END GetSelection; PROCEDURE Defocus; VAR p: KeplerPorts.BalloonPort; BEGIN IF focus # NIL THEN NEW(p); KeplerPorts.InitBalloon(p); focus.Draw(p); focus := NIL; Focus.notify(KeplerGraphs.restore, Focus, NIL, p); END END Defocus; PROCEDURE DeFocus; VAR s: KeplerGraphs.Star; BEGIN WHILE nofpts > 0 DO GetPoint(s) END ; END DeFocus; PROCEDURE PassFocus(G: KeplerGraphs.Graph); BEGIN Defocus; DeFocus; Focus := G END PassFocus; PROCEDURE Modify (F: Display.Frame; id, dY, Y, H: INTEGER); BEGIN WITH F: Frame DO Oberon.RemoveMarks(F.X, F.Y, F.W, F.H); IF id = MenuViewers.extend THEN IF dY > 0 THEN Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, F.Y + dY, Display.replace); INC(F.Y, dY) END; F.Extend(Y) ELSIF id = MenuViewers.reduce THEN F.Reduce(Y + dY); IF dY > 0 THEN Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, Y, Display.replace); F.Y := Y END END END END Modify; PROCEDURE Drag(F: Frame; p: KeplerGraphs.Star); VAR keySum, keys: SET; x, y, x0, y0: INTEGER; BEGIN PassFocus(Focus); x0 := p.x; y0 := p.y; keys := {ML, MR}; keySum := {}; WHILE keys # {} DO GetMouse(F, x, y, keys); F.TrackMouse(x, y, keys); keySum := keySum + keys; IF (x # p.x) OR (y # p.y) THEN Focus.Move(p, x-p.x, y-p.y) END ; END ; IF keySum = cancel THEN Focus.Move(p, x0-p.x, y0-p.y) END END Drag; PROCEDURE Point(F: Frame; x, y: INTEGER; keys: SET); VAR keySum: SET; p: KeplerGraphs.Star; new: BOOLEAN; sel: KeplerGraphs.Graph; b: Button; BEGIN keySum := keys; p := ThisPoint(F.G, x, y); IF p = NIL THEN new := TRUE; NEW(p); p.x := x; p.y := y; p.refcnt := 0 ELSE new := FALSE END ; F.Invert(p); WHILE keys # {} DO F.TrackMouse(x, y, keys); GetMouse(F, x, y, keys); keySum := keySum + keys; IF new & (keySum # {ML, MR}) & ((x # p.x) OR (y # p.y)) THEN F.Invert(p); p.x := x; p.y := y; F.Invert(p) ELSIF (keySum = {ML, MR}) & ~(p IS KeplerGraphs.Planet) THEN (*experimental *) F.Invert(p); IF Focus # F.G THEN PassFocus(F.G) END ; IF new THEN p.x := x; p.y := y; AppendFocusPoint(p); b := MarkedButton(); IF b # NIL THEN b.Execute({MM}); Oberon.DrawCursor(Oberon.Pointer, Oberon.Star, Oberon.Pointer.X, Oberon.Pointer.Y) END END ; Drag(F, p); RETURN END END ; F.Invert(p); IF keySum = {ML, MM} THEN IF nofpts >= 1 THEN GetSelection(sel); F.G.CopySelection(sel, x - first.p.x, y - first.p.y) END ELSIF keySum # cancel THEN IF Focus # F.G THEN PassFocus(F.G) END ; IF new THEN p.x := x; p.y := y; AppendFocusPoint(p); ELSIF IsFocusPoint(p) & ~(p IS KeplerGraphs.Planet) THEN PassFocus(Focus); Focus.Move(p, x - p.x, y - p.y); AppendFocusPoint(p) ELSE AppendFocusPoint(p) END END END Point; PROCEDURE SetCaret (F: Frame; c: Caption; x: INTEGER); VAR y, i, dx, w, oldw: INTEGER; keys: SET; ch: CHAR; fno: SHORTINT; p: KeplerPorts.BalloonPort; BEGIN NEW(p); KeplerPorts.InitBalloon(p); c.Draw(p); oldw := -1; REPEAT i := 0; w := 0; ch := c.s[i]; fno := TextPrinter.FontNo(c.fnt); dx := SHORT(TextPrinter.DX(fno, ch) DIV 3048); WHILE (ch # 0X) & (p.X + w + (dx DIV 2) < x) DO INC(w, dx); INC(i); ch := c.s[i]; dx := SHORT(TextPrinter.DX(fno, ch) DIV 3048) END ; IF w # oldw THEN IF oldw # -1 THEN FlipCaret(F, p.X + oldw, p.Y, p.H) END ; FlipCaret(F, p.X + w, p.Y, p.H) END ; Input.Mouse(keys, x, y); x := F.Cx(x); y := F.Cy(y); F.TrackMouse(x, y, keys); oldw := w UNTIL keys = {}; IF Focus # F.G THEN PassFocus(F.G) END ; focus := c; carpos := i; END SetCaret; PROCEDURE (F: Frame) EditFrame* (x, y: INTEGER; keys: SET); VAR b: Button; c: Caption; BEGIN GetMouse(F, x, y, keys); IF keys = {MM} THEN b := ThisButton(F.G, x, y); IF b # NIL THEN b.HandleMouse(F, x, y, keys) ELSE Move(F, x, y) END ELSIF keys = {ML} THEN IF (focus = NIL) & (first = NIL) OR (Focus # F.G) THEN Oberon.PassFocus(Viewers.This(F.X, F.Y)); PassFocus(F.G) END; c := ThisCaption(F.G, x, y); Defocus; IF c # NIL THEN SetCaret(F, c, x) ELSE Point(F, x, y, keys) END ELSIF keys = {MR} THEN Select(F, x, y) END END EditFrame; PROCEDURE NewCaption(s: ARRAY OF CHAR; fnt: Fonts.Font; align, carp: INTEGER); VAR o: Caption; BEGIN IF nofpts > 0 THEN Defocus; NEW(o); o.nofpts := 1; o.align := SHORT(align); COPY(s, o.s); o.fnt := fnt; focus := o; carpos := carp; ConsumePoint(o.p[0]); Focus.Append(o); END END NewCaption; PROCEDURE (F: Frame) Consume* (ch: CHAR); VAR i: INTEGER; p: KeplerPorts.BalloonPort; o: Caption; s: ARRAY 2 OF CHAR; BEGIN IF focus # NIL THEN NEW(p); KeplerPorts.InitBalloon(p); focus.Draw(p); (*old size*) LOOP IF (ch = DEL) OR (ch = BS) THEN IF carpos > 0 THEN i := carpos; REPEAT focus.s[i-1] := focus.s[i]; INC(i) UNTIL focus.s[i-1] = 0X; DEC(carpos) END ELSIF (ch = 09X) OR (ch = 0DX) OR (ch = 0AX) THEN NewCaption("", focus.fnt, focus.align, 0); RETURN ELSIF ch # DEL THEN i := carpos; WHILE focus.s[i] # 0X DO INC(i) END ; IF i+1 < LEN(focus.s) THEN REPEAT focus.s[i+1] := focus.s[i]; DEC(i) UNTIL i+1 = carpos; focus.s[i+1] := ch; INC(carpos) END END ; IF (ch >= " ") & (Input.Available() > 0) THEN Input.Read(ch) ELSE EXIT END END ; focus.Draw(p); (*plus new size*) F.G.notify(KeplerGraphs.restore, F.G, NIL, p); ELSE IF ch = DEL THEN F.G.DeleteSelection(1) ELSIF ch = BS THEN DeleteFocusPoint(F) ELSIF ch = 0C1X THEN F.G.MoveSelection(0, F.scale) ELSIF ch = 0C2X THEN F.G.MoveSelection(0, -F.scale) ELSIF ch = 0C3X THEN F.G.MoveSelection(F.scale, 0) ELSIF ch = 0C4X THEN F.G.MoveSelection(-F.scale, 0) ELSIF ORD(ch) = 145 THEN F.Restore(F.X, F.Y, F.W, F.H) ELSE s[0] := ch; s[1] := 0X; NewCaption(s, Oberon.CurFnt, 0, 1) END ; WHILE Input.Available() > 0 DO Input.Read(ch) END END END Consume; PROCEDURE (F: Frame) Neutralize*; BEGIN F.G.All(0); Defocus; DeFocus END Neutralize; PROCEDURE CopyOver(T: Texts.Text; beg, end: LONGINT); VAR R: Texts.Reader; s, t: ARRAY 128 OF CHAR; fnt: Fonts.Font; ch: CHAR; i, j: INTEGER; p: KeplerPorts.BalloonPort; BEGIN Texts.OpenReader(R, T, beg); Texts.Read(R, ch); fnt := R.fnt; i := 0; WHILE (i < LEN(t)-1) & (Texts.Pos(R) <= end) & (ch # 0DX) DO s[i] := ch; INC(i); Texts.Read(R, ch) END ; s[i] := 0X; IF focus = NIL THEN NewCaption(s, fnt, 0, i) ELSE COPY(focus.s, t); i := 0; j := carpos; WHILE s[i] # 0X DO focus.s[j] := s[i]; INC(i); INC(j) END ; i := carpos-1; carpos := j; REPEAT INC(i); focus.s[j] := t[i]; INC(j) UNTIL t[i] = 0X; NEW(p); KeplerPorts.InitBalloon(p); focus.Draw(p); Focus.notify(KeplerGraphs.restore, Focus, NIL, p) END END CopyOver; PROCEDURE TextSelection(G: KeplerGraphs.Graph): Texts.Text; VAR W: Texts.Writer; T: Texts.Text; c: KeplerGraphs.Constellation; i: INTEGER; BEGIN T := TextFrames.Text(""); c := G.cons; Texts.OpenWriter(W); WHILE c # NIL DO WITH c: Caption DO IF c.State() = 2 THEN Texts.SetFont(W, c.fnt); i := 0; WHILE c.s[i] # 0X DO Texts.Write(W, c.s[i]); INC(i) END ; Texts.WriteLn(W) END ELSE END ; c := c.next END ; Texts.Append(T, W.buf); RETURN T END TextSelection; PROCEDURE Handle* (F: Display.Frame; VAR M: Display.FrameMsg); VAR F1: Frame; BEGIN WITH F: Frame DO WITH M: Oberon.InputMsg DO IF (M.id = Oberon.track) & (M.keys # {}) THEN F.EditFrame(M.X-F.X-F.x0, M.Y-F.Y-F.H-F.y0, M.keys) ELSIF M.id = Oberon.track THEN F.TrackMouse(F.Cx(M.X), F.Cy(M.Y), M.keys) ELSIF M.id = Oberon.consume THEN F.Consume(M.ch) END | M: Oberon.ControlMsg DO IF M.id = Oberon.neutralize THEN F.Neutralize ELSIF M.id = Oberon.defocus THEN Defocus; DeFocus END | M: MenuViewers.ModifyMsg DO Modify(F, M.id, M.dY, M.Y, M.H) | M: UpdateMsg DO IF M.G = F.G THEN IF M.id = KeplerGraphs.draw THEN Oberon.RemoveMarks(F.X, F.Y, F.W, F.H); InvFocus(F); M.O.Draw(F); InvFocus(F); (* IF M.O IS KeplerGraphs.Star THEN (*invert*) M.O.Draw(F) ELSE ClipFrames.InitBalloon(B); M.O.Draw(B); F.Restore(F.CX(B.X) - 1, F.CY(B.Y) - 1, B.W DIV F.scale + 3, B.H DIV F.scale + 3) END *) ELSIF M.id = KeplerGraphs.restore THEN F.Restore(F.CX(M.P.X) - 1, F.CY(M.P.Y) - 1, M.P.W DIV F.scale + 3, M.P.H DIV F.scale + 3); ELSIF (M.id = invFoc) & (Focus = F.G) THEN F.Invert(M.O(KeplerGraphs.Star)) END END | M: SelMsg DO IF F.G.seltime > M.time THEN M.G := F.G; M.time := F.G.seltime END | M: Oberon.SelectionMsg DO IF F.G.seltime > M.time THEN M.text := TextSelection(F.G); M.time := F.G.seltime; M.beg := 0; M.end := M.text.len END | M: Oberon.CopyMsg DO NEW(F1); M.F := F1; F1^ := F^ | M: Oberon.CopyOverMsg DO CopyOver(M.text, M.beg, M.end) ELSE END END END Handle; PROCEDURE Open*(F: Frame; G: KeplerGraphs.Graph; grid, scale: INTEGER; notify: KeplerGraphs.Notifier; handle: Display.Handler); BEGIN F.G := G; F.grid := grid; F.scale := scale; G.notify := notify; F.handle := handle END Open; PROCEDURE New*(G: KeplerGraphs.Graph): Frame; VAR F: Frame; BEGIN NEW(F); Open(F, G, 0, 4, NotifyDisplay, Handle); RETURN F END New; BEGIN NEW(upd); NEW(Focus) END KeplerFrames.