Syntax10.Scn.Fnt Syntax10b.Scn.Fnt MODULE PictureFrames; (* << RC *) IMPORT Display, Oberon, Viewers, MenuViewers, Pictures, TextFrames, Input, Files, Texts, Fonts, SYSTEM; CONST black = Pictures.black; white = Pictures.white; ML = 2; MM = 1; MR = 0; CR = 0DX; TAB = 09X; CONST redraw* = 0; resize* = 1; UpdateMsg* = RECORD (Display.FrameMsg) id* : INTEGER; pict* : Pictures.Picture; x*, y*, w*, h* : INTEGER END; CopyOverMsg* = RECORD (Display.FrameMsg) pict*: Pictures.Picture; x*, y*, w*, h* : INTEGER END; Location* = POINTER TO LocDesc; LocDesc* = RECORD x*, y* : INTEGER; next* : Location END; Frame* = POINTER TO FrameDesc; FrameDesc* = RECORD (Display.FrameDesc); l*, t* : INTEGER; (* left, top of pict *) pict* : Pictures.Picture; car*, sel*, zoom* : INTEGER; time* : LONGINT; caret* : Location; selx*, sely*, selw*, selh* : INTEGER; string : RECORD len, x, y : INTEGER END END; SelectionMsg = RECORD (Display.FrameMsg) time : LONGINT; pict : Pictures.Picture; x, y, w, h : INTEGER END; Pattern = ARRAY 9 OF LONGINT; lineWidth*,grid*,color* : INTEGER; smooth : BOOLEAN; menuString* : ARRAY 100 OF CHAR; cancel : SET; resizePicture : Pictures.Picture; F : Frame; PROCEDURE max (i, j: INTEGER): INTEGER; BEGIN IF i >= j THEN RETURN i ELSE RETURN j END END max; PROCEDURE min (i, j: LONGINT): INTEGER; BEGIN IF i >= j THEN RETURN SHORT(j) ELSE RETURN SHORT(i) END END min; PROCEDURE SetToDisplay(F : Frame; VAR DX, DY : INTEGER); VAR R : INTEGER; BEGIN R := F.zoom DIV 2; DX := (DX - 1 + R) DIV F.zoom * F.zoom + 1; DY := (DY -1 + R) DIV F.zoom * F.zoom +1 END SetToDisplay; PROCEDURE Loc*(F : Frame; X,Y: INTEGER; VAR DX,DY : INTEGER); (* Yp = (Yf - F.Y) - (F.H - F.t); *) BEGIN DX := F.X + (X - F.l) * F.zoom; DY := F.Y + F.H + (Y - F.t) * F.zoom; SetToDisplay(F,DX,DY) END Loc; PROCEDURE Pos*(F : Frame; DX,DY: INTEGER; VAR X, Y : INTEGER); (* Yp = (Yf - F.Y) - (F.H - F.t); *) VAR R : INTEGER; BEGIN R := F.zoom DIV 2; X := (DX - F.X - R-1) DIV F.zoom + F.l+1; Y := (DY - F.Y - F.H - R - 1 ) DIV F.zoom + F.t + 1 END Pos; PROCEDURE SetToGrid(F : Frame; VAR X,Y : INTEGER); BEGIN X := (X + grid DIV 2) DIV grid * grid; Y := (Y + grid DIV 2) DIV grid * grid END SetToGrid; PROCEDURE SetToDGrid(F: Frame; VAR DX,DY : INTEGER); VAR x,y : INTEGER; BEGIN Pos(F,DX,DY,x,y); SetToGrid(F,x,y); Loc(F,x,y,DX,DY) END SetToDGrid; PROCEDURE DrawCaret(F : Frame; X, Y : INTEGER); CONST C = 5; VAR DX, DY, W : INTEGER; BEGIN Loc(F,X,Y,DX,DY); DX := DX - C; DY := DY - C; W := 2*C +1; IF (DX > F.X) & (DY > F.Y) & (DX + W < F.X + F.W) & (DY + W < F.Y + F.H) THEN Display.ReplConst(white,DX,DY+C,W,1,Display.invert); Display.ReplConst(white,DX+C,DY,1,W,Display.invert) END DrawCaret; PROCEDURE SetCaret*(F : Frame; X, Y : INTEGER); (** set caret in frame F *) VAR c : Location; BEGIN INC(F.car); NEW(c); c.x := X; c.y :=Y; c.next := F.caret; F.caret := c; DrawCaret(F,X,Y) END SetCaret; PROCEDURE ClipPicture(P : Pictures.Picture; VAR X,Y,W,H : INTEGER); VAR R,T : INTEGER; BEGIN R := X + W; T := Y + H; X := max(0,X); Y := max(0,Y); W := min(P.width,R) - X ; H := min(P.height,T) - Y END ClipPicture; PROCEDURE CopyOver*(F : Frame; picture : Pictures.Picture; X, Y, W, H : INTEGER); VAR DX,DY :INTEGER; BEGIN IF F.car > 0 THEN ClipPicture(F.pict,F.caret.x,F.caret.y,W,H); IF (W>0) & (H > 0) THEN Pictures.CopyBlock(picture,F.pict,X,Y,W,H,F.caret.x,F.caret.y,Display.replace); Pictures.Update(F.pict,F.caret.x,F.caret.y,W,H) END END CopyOver; PROCEDURE Defocus*(F : Frame); VAR c : Location; BEGIN c := F.caret; WHILE c # NIL DO DrawCaret(F,c.x,c.y); c := c.next END; F.caret := NIL; F.car := 0 END Defocus; PROCEDURE ClipFrame(F : Frame; VAR X, Y , W, H, DX, DY : INTEGER); VAR R, T, dX,dY : INTEGER; BEGIN Loc(F,X,Y,dX,dY); DX := max(dX,F.X); DY := max(F.Y,dY); SetToDisplay(F,DX , DY ); SetToDisplay(F,dX , dY ); Pos(F,DX,DY,X,Y); W := (min(dX + W * F.zoom, F.X+ F.W) - DX) DIV F.zoom; H := (min(dY+ H * F.zoom,F.Y + F.H) - DY) DIV F.zoom; IF DY < F.Y THEN INC(DY,8); INC(Y); DEC(H) END; IF DX + W*F.zoom > F.X + F.W THEN DEC(W) END END ClipFrame; PROCEDURE ReplConst (F : Frame; col, X, Y, W, H, mode: INTEGER); VAR D : INTEGER; BEGIN IF X < F.X THEN DEC(W,F.X - X); X := F.X END; D := X + W - F.X - F.W; IF D > 0 THEN DEC(W,D)END; IF Y < F.Y THEN DEC(H,F.Y - Y); Y := F.Y END; D := Y + H - F.Y - F.H; IF D > 0 THEN DEC(H,D) END; IF (W > 0) & (H > 0) THEN Display.ReplConst(col, X, Y, W, H, mode) END ReplConst; PROCEDURE Rectangle(F : Frame; DX,DY,W,H : INTEGER); BEGIN ReplConst(F,white,DX,DY,W,1,Display.invert); (* bottom *) ReplConst(F,white,DX+W,DY,1,H,Display.invert); (* right *) IF H > 0 THEN ReplConst(F,white,DX+1,DY+H, W,1,Display.invert)END;(* top *) IF W > 0 THEN ReplConst(F,white,DX,DY+1,1,H,Display.invert) END(* left *) END Rectangle; PROCEDURE RemoveSelection*(F : Frame); (** remove selection from frame F *) VAR DX, DY : INTEGER; BEGIN IF F.sel # 0 THEN Loc(F,F.selx,F.sely,DX,DY); Rectangle(F,DX,DY,F.selw *F.zoom,F.selh*F.zoom); F.sel := 0 END RemoveSelection; PROCEDURE SetSelection*(F : Frame; X, Y, W, H : INTEGER); (** set (change) selection in frame F *) VAR DX,DY : INTEGER; BEGIN RemoveSelection(F); IF W > 0 THEN F.selx := X; F.selw := W ELSE F.selx := X + W; F.selw := - W END; IF H > 0 THEN F.sely := Y; F.selh := H ELSE F.sely := Y + H; F.selh := - H END; Loc(F,F.selx,F.sely,DX,DY); Rectangle(F,DX,DY,F.selw*F.zoom, F.selh* F.zoom); F.time := Oberon.Time(); F.sel := 1 END SetSelection; PROCEDURE TrackRect(F : Frame; VAR mX, mY : INTEGER;VAR mkeys : SET; VAR x, y, w, h : INTEGER); VAR X, Y, dX, dY,DX,DY, W, H : INTEGER; keys,M: SET; BEGIN Pos(F,mX,mY,X, Y); dX := X - x; dY := Y - y; M := mkeys; REPEAT W := w; H := h; ClipFrame(F,x, y, W, H,DX,DY); Rectangle(F,DX, DY, W* F.zoom, H*F.zoom); X := mX; Y := mY; keys := mkeys; WHILE (mX = X) & (Y = mY) & (keys = mkeys) DO Input.Mouse(mkeys, mX, mY); Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, mX, mY) END; M := M + mkeys; Rectangle(F,DX, DY, W* F.zoom, H*F.zoom); Pos(F,mX,mY, X, Y); x := X -dX; y := Y -dY UNTIL mkeys = {}; mkeys := M END TrackRect; PROCEDURE TrackSelection*(F : Frame; DX, DY : INTEGER; VAR keys : SET) ; (** tracks selection in Frame F *) VAR M, k : SET; x0, y0,x, y, u,v,X, Y : INTEGER; t: INTEGER; BEGIN Pos(F,DX,DY,X,Y); SetToGrid(F,X,Y); x0 := DX; y0 := DY; x := x0; y := y0; k := keys; M := keys; WHILE keys # {} DO REPEAT Input.Mouse(k, u, v); Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, u, v) UNTIL (x # u) OR (y # v) OR (k # keys); RemoveSelection(F); t := min(F.Y+F.H-1,max(F.Y,v)); Pos(F,min(F.X + F.W -1,max(F.X,u)),t,x,y); SetToGrid(F,x,y); SetSelection(F,X,Y,x-X,y-Y); x := u; y := v; keys := k; M := M + keys END; keys := M END TrackSelection; PROCEDURE GraphicSelection(VAR G : Graphics.Graph; VAR time : LONGINT); VAR GF : Graphics.Frame; F : Display.Frame; BEGIN F := Oberon.MarkedViewer(); IF F IS Graphics.Frame THEN GF := F(Graphics.Frame); G := GF.graph; time := Oberon.Time() ELSE time := 0 END GraphicSelection; PROCEDURE CopyGraph(G : Graphics.Graph; F : Frame); VAR GF : Graphics.Frame; BEGIN NEW(GF); GF.X := F.X; GF.Y := F.Y; GF.W := F.W; GF.H := F.H; GF.X1 := GF.X + GF.W; GF.Y1 := GF.Y + GF.H; GF.x := GF.X - GF.Xg; GF.y := GF.Y - GF.Yg END CopyGraph; PROCEDURE GetSelection*(VAR P : Pictures.Picture; VAR time : LONGINT; VAR x, y, w, h : INTEGER); (** get most recent selection. *) VAR i : INTEGER; msg : SelectionMsg; BEGIN msg.time := -1; msg.pict := NIL; Viewers.Broadcast(msg); P := msg.pict; time := msg.time; x := msg.x; y := msg.y; w := msg.w; h := msg.h; IF time > 0 THEN ClipPicture(P,x,y,w,h) END END GetSelection; PROCEDURE Neutralize* (F: Frame); (** neutralize viewer V *) VAR cMsg : Oberon.InputMsg; BEGIN Oberon.RemoveMarks(F.X, F.Y, F.W, F.H); Defocus(F); RemoveSelection(F) END Neutralize; PROCEDURE RestoreBack(F : Frame; X, Y, W, H : INTEGER); VAR DX,DY, B, H0 , fB, fT, fL, fR : INTEGER; BEGIN Neutralize(F); (* Oberon.FadeCursor(Oberon.Mouse); *) ClipPicture(F.pict,X,Y,W,H); ClipFrame(F,X,Y,W,H,DX,DY); H0 := min((F.pict.height - Y) * F.zoom,F.Y + F.H - DY); Loc(F,0,F.pict.height,L,T); H0 := min(T,F.Y + F.H) - DY; IF (H0 <= 0) OR (W <= 0) THEN Display.ReplConst(black,F.X,F.Y, F.W, F.H, Display.replace) ELSIF (H >0) & (W > 0) THEN (* draw frame *) (* bottom *) IF Y = 0 THEN ReplConst(F,white,DX-1,DY-1,W * F.zoom+2,1,Display.replace); fB := 1 ELSE fB := 0 END; (* left *) IF X = 0 THEN ReplConst(F,white,DX-1,DY,1,H*F.zoom,Display.replace); fL := 1 ELSE fL := 0 END; (* top *) IF Y + H0 DIV F.zoom = F.pict.height THEN ReplConst(F,white,DX-1,DY + H0,W * F.zoom+2, 1, Display.replace); fT := 1 ELSE fT := 0 END; (* right *) IF X + W = F.pict.width THEN ReplConst(F,white,DX + W * F.zoom, DY, 1, H *F.zoom, Display.replace); fR := 1 ELSE fR := 0 END; (* bottom *) ReplConst(F,black,F.X,F.Y,F.W,DY-F.Y-fB,Display.replace); (* top *) ReplConst(F,black,F.X,DY + H0 + fT,F.W,F.Y + F.H - (DY + H0) - fT,Display.replace); (* left *) ReplConst(F,black,F.X,DY-1,DX - F.X-fL,H*F.zoom + 2 ,Display.replace); (* right *) ReplConst(F,black,DX + W*F.zoom+fR,DY - 1,F.X + F.W - (DX + W*F.zoom)-fR,H*F.zoom+ 2,Display.replace); IF F.zoom # 1 THEN H0 := (F.H + F.Y - DY) DIV F.zoom * F.zoom; ReplConst(F,black,F.X,DY + H0 + fT, F.W, F.Y + F.H - (DY + H0) - fT, Display.replace) END END RestoreBack; PROCEDURE DisplayBlock(F : Frame; X, Y, W, H, DX, DY : INTEGER); VAR x, y : INTEGER; P : Pictures.Picture; BEGIN y := 0; P := F.pict; Display.ReplConst(black,DX,DY,W*8,H*8,Display.replace); WHILE y < H DO x := 0; WHILE x < W DO Display.ReplConst( Pictures.Get(P,X + x,Y + y),DX + x * 8 , DY + y * 8,7, 7,Display.replace); INC(x) END; INC(y) END DisplayBlock; PROCEDURE RestorePicture(F : Frame; X, Y, W, H : INTEGER); VAR DX, DY ,Z : INTEGER; BEGIN Neutralize(F); (* Oberon.FadeCursor(Oberon.Mouse); *) ClipPicture(F.pict,X,Y,W,H); ClipFrame(F,X,Y,W,H,DX,DY); IF (H > 0) & (W > 0) THEN IF F.zoom = 1 THEN Pictures.DisplayBlock(F.pict,X, Y, W, H, DX, DY, Display.replace) ELSE SetToDisplay(F,DX,DY); DisplayBlock(F,X, Y, W, H, DX, DY) END END RestorePicture; PROCEDURE Restore*(F : Frame); BEGIN Neutralize(F); RestorePicture(F,0,0,F.pict.width,F.pict.height); RestoreBack(F,0,0,F.pict.width,F.pict.height) END Restore; PROCEDURE NotifyDisplay*(P : Pictures.Picture; X,Y,W,H : INTEGER); VAR msg : UpdateMsg; BEGIN msg.x := X; msg.y := Y; msg.w := W;msg.h := H; msg.id := redraw; msg.pict := P; Viewers.Broadcast(msg) END NotifyDisplay; PROCEDURE ResizePicture(F: Frame; P : Pictures.Picture; x, y : INTEGER); BEGIN F.sel := 0; Neutralize(F); F.pict := resizePicture; DEC(F.l,x); DEC(F.t, y); Restore(F) END ResizePicture; PROCEDURE Resize*(F : Frame; X, Y, W, H : INTEGER); VAR P : Pictures.Picture; msg : UpdateMsg; BEGIN NEW(P); Pictures.Create(P,F.selw,F.selh,F.pict.depth); P.notify := NotifyDisplay; Pictures.CopyBlock(F.pict,P,X,Y,W,H,X-F.selx,Y-F.sely,Display.replace); resizePicture := P; msg.pict := F.pict; msg.x := F.selx; msg.y := F.sely; msg.id := resize; Viewers.Broadcast(msg) END Resize; PROCEDURE Write*(F : Frame; font : Fonts.Font; col : INTEGER; ch : CHAR; VAR x, y : INTEGER; mode : INTEGER); (** write ch at position x, y; after write x,y points to new position; CR(=0DX) is processed *) VAR pat: Display.Pattern; dx,u,v,w,h: INTEGER; P : Pictures.Picture; BEGIN P := F.pict; IF (x # F.string.x) OR (y # F.string.y) THEN F.string.len := 0 END; dx := 0; IF ch = CR THEN y := y - font.height; x := x - F.string.len; F.string.len := 0 ELSIF (ch >= " ") OR (ch = TAB) THEN Display.GetChar(font.raster,ch,dx,u,v,w,h,pat); IF (x >= 0) & (x + w < P.width) & (y >= 0) & (y +h < P.height) (*& (P.depth =1 )*) THEN Pictures.CopyPattern(P,col,pat,x + u, y + v, mode) END END; x := x + dx; INC(F.string.len,dx); F.string.x := x; F.string.y := y END Write; PROCEDURE Line(F : Frame; col, x1, y1, x2, y2, mode : INTEGER; disp, first : BOOLEAN); x, y, d, dx,dy, incx, incy, DX, DY : INTEGER; P : Pictures.Picture; PROCEDURE Dot; BEGIN IF disp THEN Loc(F,x,y,DX,DY); IF (DX >= F.X) & (DY >= F.Y) & (DX < F.X + F.W) & (DY < F.Y + F.H) THEN IF F. zoom = 1 THEN Display.ReplConst(col,DX,DY,1,1,mode) ELSE ReplConst(F,col,DX,DY,F.zoom-1,F.zoom-1,mode) END END ELSE IF (x >= 0) & (y >= 0) & (x + lineWidth <= P.width) & (y + lineWidth <= P.height) THEN Pictures.ReplConst(P,col,x,y,lineWidth,lineWidth,mode) END END END Dot; BEGIN P := F.pict; x := x1; y := y1; dx := (x2-x1)*2; dy := (y2-y1)*2; IF first THEN Dot END; incx := 0; IF dx < 0 THEN incx := -1; dx := -dx ELSIF dx>0 THEN incx := 1 END; incy := 0; IF dy < 0 THEN incy := -1; dy := -dy ELSIF dy>0 THEN incy := 1 END; d := incx*(x1-x2); IF dx>dy THEN WHILE x#x2 DO INC(x, incx); INC(d, dy); IF d>0 THEN INC(y, incy); DEC(d, dx) END; Dot END ELSE WHILE y#y2 DO INC(y, incy); INC(d, dx); IF d>0 THEN INC(x, incx); DEC(d, dy) END; Dot END END Line; PROCEDURE WriteText*(F : Frame; X,Y : INTEGER; text : Texts.Text; beg, end : LONGINT); VAR R : Texts.Reader; ch : CHAR; BEGIN Texts.OpenReader(R,text,beg); Texts.Read(R,ch); WHILE beg < end DO Write(F,R.fnt,R.col, ch, X, Y, Display.paint); (* << RC *) Texts.Read(R,ch); INC(beg) END; Pictures.Update(F.pict,0,0,F.pict.width,F.pict.height); SetCaret(F,X,Y) END WriteText; PROCEDURE Modify* (F: Frame; id, dY, Y, H: INTEGER); VAR V : Viewers.Viewer; dH,X : INTEGER; BEGIN dH := H - F.H; IF F.H = 0 THEN F.Y := Y; F.H := H; Display.ReplConst(black,F.X,F.Y,F.W,F.H,Display.replace); Restore(F) ELSE F.Y := Y; F.H := H; IF F.zoom # 1 THEN IF id = MenuViewers.extend THEN ReplConst(F,black, F.X, F.Y + F.H - dY, F.W, dY, Display.replace) END; Pos(F,F.X,F.Y,X,Y); RestorePicture(F,0,Y,F.pict.width*8,F.pict.height*8); RestoreBack(F,0,Y,F.pict.width,F.H) ELSE IF id = MenuViewers.extend THEN IF (dY # 0) & (F.zoom = 1) THEN Display.CopyBlock(F.X,Y,F.W,H-dY,F.X,Y+dY, Display.replace); Display.ReplConst(black,F.X,Y,F.W,dY,Display.replace) END; Pos(F,F.X,F.Y,X,Y); RestorePicture(F,0,Y,F.pict.width,dH); RestoreBack(F,0,Y,F.pict.width,dH) ELSIF id = MenuViewers.reduce THEN IF H # 0 THEN IF dY # 0 THEN Display.CopyBlock(F.X,Y+dY,F.W,H,F.X,Y, Display.replace) END END END END END Modify; PROCEDURE Copy*(F : Frame; VAR F1 : Frame); BEGIN Neutralize(F); NEW(F1); F1^ := F^; F1.H := 0 END Copy; PROCEDURE TrackMouse(VAR keys : SET; VAR X,Y : INTEGER); BEGIN REPEAT Input.Mouse(keys, X, Y); Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, X, Y) UNTIL keys = {} END TrackMouse; PROCEDURE TrackCopy(F : Frame; P : Pictures.Picture; X, Y , W, H, X0, Y0, DX, DY, mode: INTEGER; VAR keys: SET); VAR x, y, u, v , x0, y0, deltaX, deltaY : INTEGER; k ,M: SET; B, B0 : Pictures.Picture; wth : INTEGER; PROCEDURE CopyBlock(S, D : Pictures.Picture; X, Y , W, H, DX,DY : INTEGER); PROCEDURE Clip(D : Pictures.Picture; VAR X,Y,W,H,DX,DY : INTEGER); BEGIN IF DX < 0 THEN INC(W,DX); DEC(X,DX); DX := 0 END; IF DY < 0 THEN INC(H,DY); DEC(Y,DY); DY := 0 END; IF DX + W > D.width THEN DEC(W,DX + W - D.width ) END; IF DY + H > D.height THEN DEC(H, DY + H- D.height) END END Clip; BEGIN Clip(D,X,Y,W,H,DX,DY); Clip(S,DX,DY,W,H,X,Y); IF (W > 0) & (H > 0) THEN Pictures.CopyBlock(S,D,X, Y , W, H, DX,DY,Display.replace) END END CopyBlock; BEGIN IF (W > 0) & (H > 0) THEN SetToDGrid(F,DX,DY); Pos(F,DX,DY,x0,y0); deltaX := X0 - x0; deltaY := Y0 - y0; x := DX; y := DY; k := keys; M := keys; IF ~ smooth OR (F.zoom # 1) THEN RemoveSelection(F); u := X0; v := Y0; TrackRect(F,DX,DY,keys,u, v,W, H); IF keys # cancel THEN IF mode = Display.replace THEN NEW(B0); Pictures.Create(B0,W,H,P.depth); CopyBlock(F.pict,B0,X,Y,W,H,0,0); Pictures.ReplConst(F.pict,black,X0,Y0,W,H,Display.replace); CopyBlock(B0,F.pict,0,0,W,H,u,v) ELSE CopyBlock(P,F.pict, X, Y, W, H, u,v) END END ELSE NEW(B0); Pictures.Create(B0,W,H,P.depth); CopyBlock(F.pict,B0,X,Y,W,H,0,0); NEW(B); Pictures.Create(B,W,H,P.depth); IF mode = Display.replace THEN IF F.pict = P THEN P := B0; X := 0; Y := 0 END; Pictures.ReplConst(F.pict,black,X0,Y0,W,H,Display.replace) END; CopyBlock(F.pict,B,(x0 + (x-DX) + deltaX),y0 + (y-DY ) + deltaY,W,H, 0, 0); (* save *) WHILE (keys # {}) & (M # cancel) DO REPEAT Input.Mouse(k, u, v); SetToDGrid(F,u,v) UNTIL (u # x) OR (v # y) OR (k # keys); M := M + k; CopyBlock(B,F.pict,0,0,W,H,(x0 + (x-DX) DIV F.zoom + deltaX),y0 + (y-DY ) DIV F.zoom + deltaY); (* restore *) IF M # cancel THEN CopyBlock(F.pict,B,(x0 + (u-DX) DIV F.zoom + deltaX),y0 + (v-DY ) DIV F.zoom + deltaY,W,H, 0, 0); (* save *) CopyBlock(P,F.pict,X,Y,W,H, x0 + (u-DX) DIV F.zoom + deltaX,y0 + (v-DY ) DIV F.zoom + deltaY); (* new *) RestorePicture(F,0,0,F.pict.width,F.pict.height) ELSE IF mode # Display.paint THEN CopyBlock(B0,F.pict,0,0,W,H,X0,Y0) END END; x := u; y := v; keys := k; Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y) END END; Pictures.Update(F.pict,0,0,F.pict.width,F.pict.height) END; TrackMouse(keys,X,Y); keys := M END TrackCopy; PROCEDURE Edit*(F : Frame; X, Y: INTEGER; keys: SET); VAR x, y, u, v , x0, y0, w, h, l, t : INTEGER; M,k : SET; beg,end, time, time2 : LONGINT; P : Pictures.Picture; cMsg : CopyOverMsg; text : Texts.Text; line, ln : Location; BEGIN IF keys = {ML} THEN x := X; y := Y; REPEAT Input.Mouse(k, u, v); Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, u, v) UNTIL (ABS(u-X) > 4) OR (ABS(v -Y) > 4) OR (k # {ML}); keys := k; IF (keys = {}) OR ((keys = {ML,MR}) & (F.car # 0)) THEN IF (F.car = 0) (*Viewers.This(F.X,F.Y) # Oberon.FocusViewer*) OR (keys = {}) THEN Oberon.PassFocus(Viewers.This(u,v)) END; REPEAT WHILE (keys # {}) & (keys # {ML}) DO Input.Mouse(keys, u, v); Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, u, v) END; Pos(F,u,v,X,Y); SetToGrid(F,X,Y); SetCaret(F,X,Y); WHILE keys = {ML} DO Input.Mouse(keys, u, v); Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, u, v) END UNTIL keys = {} ELSIF keys = {ML,MM} THEN GetSelection(P,time,x,y,w,h); Oberon.GetSelection(text, beg, end, time2); IF (time > time2) & (time > 0) THEN Pos(F,X,Y,u,v); SetToGrid(F,u,v); keys := {ML}; TrackCopy(F,P,x,y,w,h,u,v,X,Y,Display.paint,keys) ELSIF time2 > 0 THEN Pos(F,u,v,X,Y); SetCaret(F,X,Y); WriteText(F,X,Y,text,beg,end); TrackMouse(keys,X,Y) END ELSIF keys # {} THEN Pos(F,x,y,x0,y0); SetToGrid(F,x0,y0); M := keys; keys := {}; NEW(line); line.x := x0; line.y := y0; Line(F,Display.white,x0,y0,x0,y0,Display.invert,TRUE,TRUE); REPEAT Pos(F,u,v,X,Y); SetToGrid(F,X,Y); Line(F,Display.white,x0,y0,X,Y,Display.invert,TRUE,FALSE); x := u; y := v; REPEAT Input.Mouse(k, u, v); Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, u, v) UNTIL (keys # k) OR (x # u) OR (y # v); M := M + k; keys := k; IF keys = {ML,MR} THEN NEW(ln); ln.next:= line; line := ln; ln.x := X; ln.y := Y; x0 := X; y0 := Y ELSE Line(F,Display.white,x0,y0,X,Y,Display.invert,TRUE,FALSE) END UNTIL keys = {}; IF M # cancel THEN ln := line; WHILE ln.next # NIL DO Line(F,color,ln.next.x,ln.next.y,ln.x,ln.y,Display.replace,FALSE,TRUE); ln := ln.next END END; Pictures.Update(F.pict,0,0,F.pict.width,F.pict.height); RestoreBack(F,0,0,F.pict.width,F.pict.height) END ELSIF keys = {MM} THEN Pos(F,X,Y,u,v); IF (F.sel > 0) & (u > F.selx) & (v > F.sely) & (u <= F.selx + F.selw) & (v <= F.sely + F.selh) THEN GetSelection(P,time,x,y,w,h); TrackCopy(F,P,x,y,w,h,x,y,X,Y,Display.replace,keys) ELSE l := F.l; t := F.t; M := keys; WHILE (keys # {}) & (M # cancel)DO REPEAT Input.Mouse(k, u, v) UNTIL (u # x) OR (v # y) OR (k # keys); x := u; y := v; keys := k; M := M + keys; IF M = cancel THEN F.l := 0(* l *); F.t := F.pict.height (* t *) ELSE F.l:= l + (X - x) DIV F.zoom; F.t := t + (Y - y) DIV F.zoom END; IF smooth & (F.zoom = 1) THEN Restore(F) END; Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y) END; IF ~ smooth OR (F.zoom # 1) THEN Restore(F) END END ELSIF keys = {MR} THEN TrackSelection(F,X,Y,keys); GetSelection(P,time,x,y,w,h); IF keys = {ML,MR} THEN Pictures.ReplConst(F.pict,black,x, y, w,h, Display.replace); Pictures.Update(P,x,y,w,h) ELSIF keys = {MM,MR} THEN cMsg.x := x; cMsg.y := y;cMsg.w := w; cMsg.h := h; cMsg.pict := P; Oberon.FocusViewer.handle(Oberon.FocusViewer,cMsg) END END; TrackMouse(keys,X,Y) END Edit; PROCEDURE Handle*(F: Display.Frame; VAR msg: Display.FrameMsg); VAR F1 : Frame; DX, DY, H0, X, Y, x, y, w, h, dx : INTEGER; p : LONGINT; BEGIN WITH F : Frame DO IF msg IS Oberon.ControlMsg THEN WITH msg : Oberon.ControlMsg DO IF msg.id = Oberon.defocus THEN Defocus(F) ELSIF msg.id = Oberon.neutralize THEN Neutralize(F) ELSIF msg.id = Oberon.mark THEN Oberon.DrawCursor(Oberon.Pointer, Oberon.Star, msg.X, msg.Y) END END ELSIF msg IS Oberon.InputMsg THEN WITH msg: Oberon.InputMsg DO IF msg.id = Oberon.consume THEN IF F.car > 0 THEN X := F.caret.x; Y := F.caret.y; Write(F,msg.fnt,color,msg.ch,X,Y, Display.paint); (* << RC *) Display.GetChar(msg.fnt.raster,msg.ch,dx,x,y,w,h,p); Pictures.Update(F.pict,X+x-dx,Y+y,w,h); SetCaret(F,X,Y) END (* write *) ELSIF msg.id = Oberon.track THEN IF msg.keys # {} THEN Edit(F,msg.X, msg.Y, msg.keys) ELSE Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, msg.X, msg.Y) END END END ELSIF msg IS Oberon.CopyMsg THEN WITH msg : Oberon.CopyMsg DO Copy(F,F1); msg.F := F1 END ELSIF msg IS MenuViewers.ModifyMsg THEN Neutralize(F); WITH msg : MenuViewers.ModifyMsg DO Modify(F, msg.id, msg.dY, msg.Y, msg.H) END ELSIF msg IS Oberon.CopyOverMsg THEN WITH msg : Oberon.CopyOverMsg DO IF (F.car > 0) THEN WriteText(F,F.caret.x,F.caret.y,msg.text,msg.beg,msg.end) END END ELSIF msg IS CopyOverMsg THEN WITH msg : CopyOverMsg DO CopyOver(F,msg.pict,msg.x,msg.y,msg.w,msg.h) END ELSIF msg IS SelectionMsg THEN WITH msg : SelectionMsg DO IF (F.time > msg.time) & (F.sel > 0) THEN msg.pict := F.pict; msg.time := F.time; msg.x := F.selx; msg.y := F.sely; msg.w := F.selw; msg.h := F.selh END END ELSIF msg IS UpdateMsg THEN WITH msg : UpdateMsg DO IF msg.pict = F.pict THEN IF msg.id= redraw THEN RestorePicture(F,msg.x,msg.y,msg.w,msg.h) ELSIF msg.id = resize THEN ResizePicture(F,msg.pict,msg.x,msg.y) END END END END END Handle; PROCEDURE Picture*(name : ARRAY OF CHAR) : Pictures.Picture; VAR P : Pictures.Picture; BEGIN NEW(P); Pictures.Open(P, name); P.notify := NotifyDisplay; RETURN P END Picture; PROCEDURE Open*(F : Frame;H: Display.Handler; P : Pictures.Picture; l, t : INTEGER); BEGIN F.pict := P; F.t := t; F.l := l; F.car := 0; F.zoom := 1; F.sel := 0; F.handle := H END Open; PROCEDURE NewPicture*(P : Pictures.Picture) : Frame; VAR F : Frame; BEGIN NEW(F); Open(F,Handle,P,0,P.height); P.notify := NotifyDisplay; RETURN F END NewPicture; BEGIN menuString := "System.Close System.Copy System.Grow Paint.Zoom Paint.Resize Paint.Store"; cancel := {ML,MM,MR}; lineWidth := 1;grid := 1; color := Display.white; smooth := TRUE END PictureFrames.