ðmSyntax10.Scn.FntÇ;Syntax10b.Scn.FntÞ{+iàDMODULE BoardFrames; IMPORT TextFrames, MenuViewers, Boards, Oberon, Display, Input, Texts; TYPE Note* = POINTER TO NoteDesc; Frame* = POINTER TO FrameDesc; FrameDesc* = RECORD (Display.FrameDesc) SelectedNote*: Note; board*: Boards.Board; Xboard*, Yboard*: INTEGER; (* lower left corner in board coordinates *) END; NoteDesc* = RECORD (TextFrames.FrameDesc) note*: Boards.Note; flag: BOOLEAN; END; CONST right = 0; middle = 1; left = 2; (* Mouse keys *) black = 0; white = 15; (* display color *) VAR marg: INTEGER; PROCEDURE Min (i, j: INTEGER): INTEGER; BEGIN IF i >= j THEN RETURN j ELSE RETURN i END END Min; PROCEDURE Max (i, j: INTEGER): INTEGER; BEGIN IF i >= j THEN RETURN i ELSE RETURN j END END Max; PROCEDURE XYinRect(X, Y, RX, RY, RW, RH: INTEGER): BOOLEAN; BEGIN RETURN (X >= RX) & (X < RX + RW) & (Y >= RY) & (Y B.X - marg) & (B.X + B.W + marg > A.X - marg) & (A.Y + A.H + marg > B.Y - marg) & (B.Y + B.H + marg > A.Y - marg) END Overlap; (*_____________________________________________________ Draw Frames _____________________________________________________________________________*) PROCEDURE DrawNoteFrame(N: Display.Frame); VAR X, Y, W, H: INTEGER; BEGIN X := N.X - marg; Y := N.Y - marg; W := N.W + 2* marg; H := N.H + 2*marg; Oberon.RemoveMarks(X, Y, W, H); Display.ReplConst(black, X, Y, W, H, Display.replace); (* clear frame *) Display.ReplConst(white, X, Y + 1, 1, H - 1, Display.replace); (* left edge *) Display.ReplConst(white, X + W-2, Y + 1, 1, H - 1, Display.replace); (* right edge *) Display.ReplConst(white, X + W-1, Y, 1, H - 1, Display.replace); (* right edge *) Display.ReplConst(white, X + 1, Y + 1, W - 3, 1, Display.replace); (* bottom edge *) Display.ReplConst(white, X + 1, Y, W - 2, 1, Display.replace); (* bottom edge *) Display.ReplConst(white, X + 1, Y + H - 1, W - 3, 1, Display.replace); (* top edge *) (*Display.ReplConst(white, X + W - marg, Y, marg, marg, Display.replace); (* handle *)*) END DrawNoteFrame; PROCEDURE ClearNoteFrame(N: Display.Frame); VAR X, Y, W, H: INTEGER; BEGIN X := N.X - marg; Y := N.Y - marg; W := N.W + 2* marg; H := N.H + 2*marg; Oberon.RemoveMarks(X - 1, Y - 1, W + 2, H + 2); Display.ReplConst(black, X, Y, W, H, Display.replace); END ClearNoteFrame; PROCEDURE DrawNote*(N: Note); VAR oldY: INTEGER; M: MenuViewers.ModifyMsg; BEGIN DrawNoteFrame(N); oldY := N.Y; (* TextFrames.Reduce(N, N.Y + N.H); *) M.Y := N.Y + N.H; M.H := N.Y + N.H - M.Y; N.handle(N, M); (* TextFrames.Extend(N, oldY); *) M.Y := oldY; M.H := N.Y + N.H - M.Y; N.handle(N, M) END DrawNote; (*PROCEDURE DrawMarkedNotes(F: Frame; Y, H: INTEGER); VAR Q: Display.Frame; dY: INTEGER; BEGIN (* draw in background *) dY := -Display.UBottom; Display.CopyBlock(F.X, Y, F.W, H, F.X, Y - dY, Display.replace); Q := F.dsc; WHILE Q# NIL DO IF Q(Note).flag THEN Q.Y := Q.Y - dY; DrawNote(Q(Note)); Q.Y := Q.Y + dY; Q(Note).flag := FALSE END; Q := Q.next END; Display.CopyBlock(F.X, Y - dY, F.W, H, F.X, Y, Display.replace); END DrawMarkedNotes;*) PROCEDURE DrawMarkedNotes(F: Frame; Y, H: INTEGER); VAR Q: Display.Frame; dY: INTEGER; BEGIN (* draw in foreground *) Q := F.dsc; WHILE Q# NIL DO IF Q(Note).flag THEN DrawNote(Q(Note)); Q(Note).flag := FALSE END; Q := Q.next END; END DrawMarkedNotes; (*_____________________________________________________ Tracking Procedures __________________________________________________________________*) PROCEDURE TrackMouse(Marker: Oberon.Marker; VAR X, Y: INTEGER; VAR keysum: SET); VAR keys: SET; BEGIN keys := keysum; WHILE keys # {} DO Oberon.DrawCursor(Oberon.Mouse, Marker, X, Y); Input.Mouse(keys, X, Y); keysum := keysum + keys; END; END TrackMouse; PROCEDURE ReplConst(X, Y, W, H, mode: INTEGER); BEGIN IF W < 0 THEN X := X+W; W := -W; END; IF H < 0 THEN Y := Y+H; H := -H; END; IF (W # 0 ) & (H # 0) THEN Display.ReplConst(white, X, Y, W, H, mode); END; END ReplConst; PROCEDURE FlipRect(X0, Y0, X1, Y1, X2, Y2: INTEGER); BEGIN ReplConst(X0+1, Y1, X1-X0-2, 1, Display.invert); ReplConst(X1-1, Y1, 1, Y0-Y1, Display.invert); ReplConst(X1-1, Y0-1, X2-X1, 1, Display.invert); ReplConst(X2-1, Y2, 1, Y0-Y2, Display.invert); ReplConst(X0+1, Y2, X2-X0-2, 1, Display.invert); ReplConst(X0, Y2, 1, Y1-Y2, Display.invert); END FlipRect; PROCEDURE DragRect(F: Display.Frame; X0, Y0, X1, Y1: INTEGER; VAR X2, Y2: INTEGER; VAR keysum: SET ); VAR keys: SET; x, y, X1init, Y1init: INTEGER; BEGIN keys := keysum; IF ~XYinRect(X0, Y0, F.X + 4, F.Y + 4, F.W - 7, F.H - 7) THEN X2 := X0; Y2 := 0; RETURN END; X1init := X1; Y1init := Y1; WHILE keys # {} DO Input.Mouse(keys, x, y); Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y); keysum := keysum + keys; X2 := Min(Max(x, X0), F.X + F.W - 4); Y2 := Max(Min(y, Y0), F.Y + 4); IF (X2 # X1) OR (Y2 # Y1) THEN FlipRect(X0, Y0, X1, Y1, X2, Y2); X1 := X2; Y1 := Y2; END; END; FlipRect(X0, Y0, X1init, Y1init, X2, Y2); END DragRect; PROCEDURE FlipFrame(X, Y, W, H: INTEGER); BEGIN Display.ReplConst(white, X, Y, 1, H, Display.invert); (* left edge *) Display.ReplConst(white, X + W - 1, Y, 1, H, Display.invert); (* right edge *) Display.ReplConst(white, X + 1, Y, W - 2, 1, Display.invert); (* bottom edge *) Display.ReplConst(white, X + 1, Y + H - 1, W - 2, 1, Display.invert); (* top edge *) END FlipFrame; PROCEDURE TrackRect(F: Display.Frame; X, Y, W, H: INTEGER; VAR X2, Y2: INTEGER; x, y: INTEGER; VAR keysum: SET); VAR a, b: INTEGER; keys: SET; BEGIN keys := keysum; a := x - X; b := y - Y; X2 := X; Y2 := Y; FlipFrame(X, Y, W, H); WHILE keys # {} DO Input.Mouse(keys, x, y); Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y); keysum := keysum + keys; X2 := Min(F.X + F.W - 4 - W, Max(F.X + 4, x - a)); Y2 := Min(F.Y + F.H - 4 - H, Max(F.Y + 4, y - b)); IF (X # X2) OR (Y # Y2) THEN FlipFrame(X, Y, W, H); FlipFrame(X2, Y2, W, H); X := X2; Y := Y2; END; END; FlipFrame(X2, Y2, W, H); END TrackRect; (*_____________________________________________________ Notes _________________________________________________________________________________________*) PROCEDURE This*(F: Frame; X, Y: INTEGER): Display.Frame; VAR Q, DF: Display.Frame; BEGIN Q := F.dsc; DF := NIL; WHILE Q # NIL DO IF XYinRect(X, Y, Q.X - marg, Q.Y - marg, Q.W + 2*marg, Q.H + 2*marg) THEN DF := Q END; Q := Q.next END; RETURN DF END This; PROCEDURE Append(F: Frame; DF: Display.Frame); VAR Q: Display.Frame; BEGIN Q := F.dsc; IF Q = NIL THEN F.dsc := DF; ELSE WHILE Q.next # NIL DO Q := Q.next END; Q.next := DF; END; DF.next := NIL; END Append; PROCEDURE Remove(F: Frame; DF: Display.Frame); VAR Q: Display.Frame; BEGIN Q := F.dsc; IF Q = DF THEN F.dsc := DF.next ELSE WHILE Q.next # DF DO Q := Q.next END; Q.next := Q.next.next END; END Remove; PROCEDURE ToListEnd(F: Frame; DF: Display.Frame); VAR Q: Display.Frame; BEGIN IF DF.next # NIL THEN Q := F.dsc; IF Q = DF THEN F.dsc := DF.next END; WHILE Q.next # NIL DO IF Q.next = DF THEN Q.next := DF.next END; Q := Q.next END; Q.next := DF; DF.next := NIL END END ToListEnd; PROCEDURE Locate*(F: Frame; BN: Boards.Note): Display.Frame; VAR Q: Display.Frame; BEGIN Q := F.dsc; WHILE Q # NIL DO IF Q(Note).note = BN THEN RETURN Q; ELSE Q := Q.next; END; END; RETURN NIL; END Locate; PROCEDURE IsTop*(DF: Display.Frame): BOOLEAN; VAR res: BOOLEAN; Q: Display.Frame; BEGIN Q := DF.next; res := TRUE; WHILE Q # NIL DO res := res & ~Overlap(Q, DF); Q := Q.next; END; RETURN res; END IsTop; PROCEDURE Broadcast*(F: Frame; VAR M: Display.FrameMsg); VAR Q: Display.Frame; BEGIN Q := F.dsc; WHILE Q # NIL DO Q.handle(Q, M); Q := Q.next; END; END Broadcast; PROCEDURE MarkUp(N: Display.Frame); VAR Q: Display.Frame; BEGIN (* all flags FALSE *) N(Note).flag := TRUE; Q := N.next; WHILE Q # NIL DO IF ~Q(Note).flag & Overlap(Q, N) THEN Q(Note).flag := TRUE; MarkUp(Q); END; Q := Q.next; END; END MarkUp; PROCEDURE Mark(F: Frame; N: Display.Frame); VAR Q: Display.Frame; BEGIN (* all flags FALES *) Q := F.dsc; WHILE Q # NIL DO IF ~Q(Note).flag & Overlap(Q, N) THEN MarkUp(Q); END; Q := Q.next; END; END Mark; PROCEDURE OpenNote*(F: Frame; N: Note; BN: Boards.Note); VAR X, Y, W, H: INTEGER; BEGIN BoardToFrame(F, BN, X, Y, W, H); N.X := X + marg; N.Y := Y + marg; N.W := W - 2*marg; N.H := H - 2*marg; TextFrames.Open(N, BN.text, 0); N.col := black; N.left := 5; N.right := 0; N.top := 0; N.bot := 0; N.next := NIL; N.note := BN; N.flag := FALSE END OpenNote; PROCEDURE NewNote*(F: Frame; BN: Boards.Note): Note; VAR N: Note; BEGIN NEW(N); OpenNote(F, N, BN); Append(F, N); RETURN N END NewNote; (*_____________________________________________________ Oberon messages _______________________________________________________________________*) PROCEDURE ^Deselect*(F: Frame); PROCEDURE Neutralize*(F: Frame); VAR M: Oberon.ControlMsg; BEGIN Oberon.RemoveMarks(F.X, F.Y, F.W, F.H); M.id := Oberon.neutralize; Broadcast(F, M); Deselect(F); END Neutralize; PROCEDURE ^New*(B: Boards.Board; Xboard, Yboard: INTEGER): Frame; PROCEDURE CopyFrame*(F: Frame; VAR M: Oberon.CopyMsg); BEGIN M.F := New(F.board, F.Xboard, F.Yboard); END CopyFrame; PROCEDURE Deselect*(F: Frame); VAR N: Note; BEGIN IF F.SelectedNote # NIL THEN N := F.SelectedNote; F.SelectedNote := NIL; Display.ReplConst(white, N.X - marg, N.Y - marg, N.W + 2*marg, N.H + 2*marg, Display.invert); END END Deselect; PROCEDURE Select*(F: Frame; N: Note; keysum: SET); VAR X, Y: INTEGER; BEGIN Neutralize(F); Display.ReplConst(white, N.X - marg, N.Y - marg, N.W + 2*marg, N.H + 2*marg, Display.invert); TrackMouse(Oberon.Arrow, X, Y, keysum); F.SelectedNote := N; IF (left IN keysum) & ~((middle IN keysum) & (right IN keysum)) THEN Boards.Discard(F.board, N.note) END; END Select; (*_____________________________________________________ Boards messages _______________________________________________________________________*) PROCEDURE Poste*(F: Frame; BN: Boards.Note); VAR N: Note; BEGIN IF In(BN, F) THEN N := NewNote(F, BN); Neutralize(F); DrawNote(N) END END Poste; PROCEDURE ToTop*(F: Frame; BN: Boards.Note); VAR N: Display.Frame; BEGIN IF In(BN, F) THEN N := Locate(F, BN); IF ~IsTop(N) THEN Neutralize(F); DrawNote(N(Note)) END; ToListEnd(F, N) END END ToTop; PROCEDURE Discard*(F: Frame; BN: Boards.Note); VAR N: Display.Frame; BEGIN IF In(BN, F) THEN N := Locate(F, BN); Neutralize(F); Mark(F, N); ClearNoteFrame(N); Remove(F, N); DrawMarkedNotes(F, F.Y, F.H); END; END Discard; PROCEDURE Move*(F: Frame; BN: Boards.Note); VAR N: Display.Frame; X, Y, W, H: INTEGER; BEGIN N := Locate(F, BN); Neutralize(F); IF N # NIL THEN (* old location in F *) Mark(F, N); ClearNoteFrame(N); Remove(F, N) END; IF In(BN, F) THEN (* new location in F *) IF N = NIL THEN Poste(F, BN) ELSE Append(F, N); BoardToFrame(F, BN, X, Y, W, H); N.X := X + marg; N.Y := Y + marg; N.W := W - 2*marg; N.H := H - 2*marg; DrawNote(N(Note)); END END; DrawMarkedNotes(F, F.Y, F.H) END Move; (*_________________________________________________ MenuViewer Messages __________________________________________________________________*) PROCEDURE Extend(F: Frame; Ytop, Ybot: INTEGER); VAR BN: Boards.Note; R: Boards.Reader; N: Note; A, Q: Display.Frame; X, Y, W, H: INTEGER; BEGIN IF F.Y - Ybot > 0 THEN Display.ReplConst(black, F.X, Ybot, F.W, F.Y - Ybot, Display.replace) END; IF Ytop - F.Y - F.H > 0 THEN Display.ReplConst(black, F.X, F.Y + F.H, F.W, Ytop - F.Y - F.H, Display.replace) END; Boards.OpenReader(R, F.board); Boards.Read(R, BN); NEW(A); A.next := F.dsc; Q := A; WHILE BN # NIL DO BoardToFrame(F, BN, X, Y, W, H); IF (X >= F.X) & (X+W=Ybot) & (Y+H= Ytop) THEN Remove(F, Q); ClearNoteFrame(Q); Mark(F, Q) END; Q := Q.next END; DrawMarkedNotes(F, Ybot, Ytop - Ybot); F.Y := Ybot; F.H := Ytop - Ybot; END Reduce; PROCEDURE TransformDisplayDesc(F: Frame; dY: INTEGER); VAR Q: Display.Frame; BEGIN Q := F.dsc; WHILE Q # NIL DO Q.Y := Q.Y + dY; Q := Q.next END END TransformDisplayDesc; PROCEDURE Modify(F: Frame; M: MenuViewers.ModifyMsg); BEGIN Neutralize(F); IF M.id = MenuViewers.extend THEN IF M.dY > 0 THEN Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, F.Y + M.dY, Display.replace); F.Y := F.Y + M.dY; TransformDisplayDesc(F, M.dY) END; Extend(F, F.Y + F.H, M.Y); ELSIF M.id = MenuViewers.reduce THEN Reduce(F, F.Y + F.H, M.Y + M.dY); IF M.dY > 0 THEN Display.CopyBlock(F.X, M.Y + M.dY, F.W, M.H, F.X, M.Y, Display.replace); TransformDisplayDesc(F, -M.dY) END END; END Modify; PROCEDURE ShiftBoard*(F: Frame; VAR X, Y: INTEGER; VAR keysum: SET); VAR dY, Y0: INTEGER; BEGIN Y0 := Y; TrackMouse(Oberon.Arrow, X, Y, keysum); Neutralize(F); dY := Y - Y0; IF dY < 0 THEN Reduce(F, F.Y + F.H, F.Y - dY); Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, F.Y + dY, Display.replace); TransformDisplayDesc(F, dY); F.Y := F.Y + dY; Extend(F, F.Y + F.H - dY, F.Y); F.Yboard := F.Yboard - dY; ELSIF dY > 0 THEN Reduce(F, F.Y + F.H - dY, F.Y); F.Yboard := F.Yboard - dY; Display.CopyBlock(F.X, F.Y, F.W, F.H, F.X, F.Y + dY, Display.replace); TransformDisplayDesc(F, dY);F.Y := F.Y + dY; Extend(F, F.Y + F.H, F.Y - dY); END END ShiftBoard; (*_____________________________________________________ Handler ______________________________________________________________________________________*) PROCEDURE Handler* (F: Display.Frame; VAR M: Display.FrameMsg); VAR N: Display.Frame; X, Y: INTEGER; BEGIN WITH F: Frame DO IF M IS Oberon.InputMsg THEN WITH M: Oberon.InputMsg DO IF M.id = Oberon.track THEN Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, M.X, M.Y); N := This(F, M.X, M.Y); IF N = NIL THEN (* mouse in board area *) IF left IN M.keys THEN (* create empty note *) DragRect(F, M.X - 1, M.Y + 1, M.X, M.Y, X, Y, M.keys); Boards.PosteEmpty(F.board, F.Xboard + M.X - 1 - F.X, F.Yboard + Y - F.Y - F.H, X - (M.X - 1), M.Y + 1 - Y); ELSIF middle IN M.keys THEN (* scroll board area *) ShiftBoard(F, M.X, M.Y, M.keys) END; ELSIF N = F.SelectedNote THEN IF left IN M.keys THEN (* resize *) DragRect(F, N.X - marg, N.Y + N.H + marg, N.X + N.W + marg - 1, N.Y - marg + 1, X, Y, M.keys); IF ~((right IN M.keys) & (middle IN M.keys)) THEN Boards.Resize(F.board, N(Note).note, X - (N.X - marg), N.Y + N.H + marg - Y); END ELSIF middle IN M.keys THEN (* move *) TrackRect(F, N.X - marg, N.Y - marg, N.W + 2*marg, N.H + 2*marg, X, Y, M.X, M.Y, M.keys); IF ~((right IN M.keys) & (left IN M.keys)) THEN Boards.Move(F.board, N(Note).note, X - N.X + marg, Y - N.Y + marg); END END ELSE (* mouse in note frame *) IF M.keys # {} THEN IF ~IsTop(N) THEN Boards.ToTop(F.board, N(Note).note) END; IF XYinRect(M.X, M.Y, N.X, N.Y, N.W, N.H) THEN N.handle(N, M) ELSE IF right IN M.keys THEN Select(F, N(Note), M.keys) END END END END ELSIF M.id = Oberon.consume THEN Broadcast(F, M) END END ELSIF M IS Oberon.ControlMsg THEN WITH M:Oberon.ControlMsg DO IF M.id = Oberon.neutralize THEN Neutralize(F) ELSIF (M.id = Oberon.defocus) OR (M.id = Oberon.mark) THEN Broadcast(F, M) END END ELSIF M IS Oberon.CopyMsg THEN CopyFrame(F, M(Oberon.CopyMsg) ) ELSIF M IS MenuViewers.ModifyMsg THEN Modify(F, M(MenuViewers.ModifyMsg)) ELSIF M IS Boards.UpdateMsg THEN WITH M: Boards.UpdateMsg DO IF M.board = F.board THEN IF M.id = Boards.poste THEN Poste(F, M.note) ELSIF M.id = Boards.toTop THEN ToTop(F, M.note) ELSIF M.id = Boards.discard THEN Discard(F, M.note) ELSIF M.id = Boards.move THEN Move(F, M.note) END END END ELSE Broadcast(F, M) END END END Handler; PROCEDURE New*(B: Boards.Board; Xboard, Yboard: INTEGER): Frame; VAR F: Frame; BEGIN NEW(F); F.handle := Handler; F.board := B; F.Xboard := Xboard; F.Yboard := Yboard; F.SelectedNote := NIL; RETURN F END New; BEGIN marg := 5; END BoardFrames.