Einstein
Disk Puzzle
Hasító
táblázat
DEFINITION Oberon0; PROCEDURE Loop; END Oberon0. DEFINITION Viewers0; IMPORT OS; TYPE Frame = POINTER TO FrameDesc; FrameDesc = RECORD ( OS.ObjectDesc ) x, y : INTEGER; (* left bottom in pixels relative to left bot. of Screen *) w, h : INTEGER; (* width, height in pixels *) PROCEDURE ( f : Frame ) Draw; PROCEDURE ( f : Frame ) Modify ( dy : INTEGER ); PROCEDURE ( f : Frame ) Move ( dy : INTEGER ); PROCEDURE ( f : Frame ) Copy () : Frame; PROCEDURE ( f : Frame ) HandleKey ( ch : CHAR ); PROCEDURE ( f : Frame ) HandleMouse ( x, y : INTEGER; buttons : SET ); PROCEDURE ( f : Frame ) Handle ( VAR m : OS.Message ); PROCEDURE ( f : Frame ) Neutralize; PROCEDURE ( f : Frame ) SetFocus; PROCEDURE ( f : Frame ) Defocus; END; Viewer = POINTER TO ViewerDesc; ViewerDesc = RECORD ( FrameDesc ) menu-, cont- : Frame; next- : Viewer; PROCEDURE ( v : Viewer ) Close; END; VAR focus- : Frame; (* the frame that gets the keyboard input *) PROCEDURE New ( menu, cont : Frame ) : Viewer; PROCEDURE ViewerAt ( y : INTEGER ) : Viewer; PROCEDURE Broadcast ( VAR m : OS.Message ); PROCEDURE Close; PROCEDURE Copy; END Viewers0. DEFINITION AsciiTexts; IMPORT OS; TYPE Text = POINTER TO TextDesc; TextDesc = RECORD ( OS.ObjectDesc ) len- : LONGINT; (* text length *) pos- : LONGINT; (* read/write position *) PROCEDURE ( t : Text ) Clear; PROCEDURE ( t : Text ) Insert ( at : LONGINT; t1 : Text; beg, end : LONGINT ); PROCEDURE ( t : Text ) Delete ( beg, end : LONGINT ); PROCEDURE ( t : Text ) SetPos ( pos : LONGINT ); PROCEDURE ( t : Text ) Read ( VAR ch : CHAR ); PROCEDURE ( t : Text ) Write ( ch : CHAR ); PROCEDURE ( t : Text ) Load ( VAR r : OS.Rider ); PROCEDURE ( t : Text ) Store ( VAR r : OS.Rider ); END; NotifyInsMsg = RECORD ( OS.Message ) t : Text; beg, end : LONGINT END; NotifyDelMsg = RECORD ( OS.Message ) t : Text; beg, end : LONGINT END; END AsciiTexts. DEFINITION Texts0; IMPORT OS, AsciiTexts; TYPE Attribute = POINTER TO AttrDesc; Element = POINTER TO ElemDesc; Text = POINTER TO TextDesc; TextDesc = RECORD ( AsciiTexts.TextDesc ) attr- : Attribute; (* attributes of previously read character *) PROCEDURE ( t : Text ) ChangeFont ( beg, end : LONGINT; fnt : OS.Font ); PROCEDURE ( t : Text ) ReadNextElem ( VAR e: Element ); PROCEDURE ( t : Text ) WriteElem ( e : Element ); PROCEDURE ( t : Text ) ElemPos ( e : Element ); END; AttrDesc = RECORD fnt- : OS.Font; (* font of this attribute segment *) elem- : Element (* if not NIL, the corresponding character is an element *) END; ElemDesc = RECORD ( OS.ObjectDesc ) w, h : INTEGER; (* width and height of element in pixels *) dsc : INTEGER; (* descender (part below the base line) *) PROCEDURE ( e : Element ) Draw ( x, y : INTEGER ); PROCEDURE ( e : Element ) HandleMouse ( frame : OS.Object; x, y : INTEGER ); PROCEDURE ( e : Element ) Copy () : Element; END; NotifyDelMsg = AsciiTexts.NotifyDelMsg; NotifyInsMsg = AsciiTexts.NotityInsMsg; NotityReplMsg = RECORD ( OS.Message ) t : Text; beg, end : LONGINT END; END Texts0. DEFINITION TextFrames0; IMPORT OS, Viewers0, Texts0; TYPE Position = RECORD (* position of a character ch on the screen *) x-, y- : INTEGER; (* left point on base line *) dx- : INTEGER; (* width of ch *) org- : LONGINT; (* origin of Iine containing ch *) pos- : LONGINT (* text position of ch *) END; Frame = POINTER TO FrameDesc; FrameDesc = RECORD ( Viewers0.FrameDesc ) text : Texts0.Text; (* text displayed in this frame *) org- : LONGINT; (* origin: text pos. of first char. in frame *) caret- : Position; (* caret.pos < 0: no caret visible *) selBeg-, selEnd- : Position; (* seiBeg.pos < 0: no selection visible *) PROCEDURE ( f : Frame ) Draw; PROCEDURE ( f : Frame ) Defocus; PROCEDURE ( f : Frame ) Neutralize; PROCEDURE ( f : Frame ) Modify ( dy : INTEGER ); PROCEDURE ( f : Frame ) HandleKey ( ch : CHAR ); PROCEDURE ( f : Frame ) HandleMouse ( x, y : INTEGER; buttons : SET ); PROCEDURE ( f : Frame ) Handle ( VAR m : OS.Message ); PROCEDURE ( f : Frame ) SetCaret ( pos : LONGINT ); PROCEDURE ( f : Frame ) RemoveCaret; PROCEDURE ( f : Frame ) SetSelection ( from, to : LONGINT ); PROCEDURE ( f : Frame ) RemoveSelection; PROCEDURE ( f : Frame ) Copy () : Viewers0.Frame; END; VAR cmdFrame- : Frame; (* frame containing most recent command *) cmdPos- : LONGINT; (* text position after most recent command *) PROCEDURE New ( t : Texts0.Text ) : Frame; PROCEDURE NewMenu ( name, commands : ARRAY OF CHAR ) : Frame; PROCEDURE GetSelection ( VAR f : Frame ); END TextFrames0. DEFINITION Edit0; PROCEDURE Open; PROCEDURE Store; PROCEDURE ChangeFont; END Edit0. DEFINITION Shapes0; IMPORT OS, Viewers0; TYPE Shape = POINTER TO ShapeDesc; ShapeDesc = RECORD ( OS.ObjectDesc ) selected: BOOLEAN; (* TRUE: shape is selected *) PROCEDURE ( s : Shape ) SetBox ( x, y, w, h : INTEGER ); PROCEDURE ( s : Shape ) GetBox ( VAR x, y, w, h : INTEGER ); PROCEDURE ( s : Shape ) Draw ( f : Viewers0.Frame ); PROCEDURE ( s : Shape ) Move ( dx, dy : INTEGER ); PROCEDURE ( s : Shape ) Neutralize; PROCEDURE ( s : Shape ) SetSelection ( x, y, w, h : INTEGER ); PROCEDURE ( s : Shape ) Copy () : Shape; END; Graphic = POINTER TO GraphicDesc; GraphicDesc = RECORD shapes : Shape; PROCEDURE ( g : Graphic ) Insert ( s : Shape ); PROCEDURE ( g : Graphic ) DeleteSelected; PROCEDURE ( g : Graphic ) MoveSelected ( dx, dy : INTEGER ); PROCEDURE ( g : Graphic ) Draw ( f : Viewers0.Frame ); PROCEDURE ( g : Graphic ) Neutralize; PROCEDURE ( g : Graphic ) SetSelection ( x, y, w, h : INTEGER ); PROCEDURE ( g : Graphic ) GetBox ( VAR x, y, w, h : INTEGER ); PROCEDURE ( g : Graphic ) Copy () : Graphic; PROCEDURE ( g : Graphic ) Load ( VAR r : OS.Rider ); PROCEDURE ( g : Graphic ) Store ( VAR r : OS.Rider ); END; NotifyChangeMsg = RECORD ( OS.Message ) g : Graphic END; VAR curShape : ARRAY 32 OF CHAR; (* name of current shape type*) PROCEDURE InitGraphic ( VAR g : Graphic ); END Shapes0. DEFINITION GraphicFrames0; IMPORT Viewers0, OS, Shapes0; TYPE Frame = POINTER TO FrameDesc; FrameDesc = RECORD ( Viewers0.FrameDesc ) orgX, orgY : INTEGER; graphic : Shapes0.Graphic; PROCEDURE ( f : Frame ) Draw; PROCEDURE ( f : Frame ) Neutralize; PROCEDURE ( f : Frame ) Modify ( y : INTEGER ); PROCEDURE ( f : Frame ) Copy () : Viewers0.Frame; PROCEDURE ( f : Frame ) HandleMouse ( x, y : INTEGER; buttons : SET ); PROCEDURE ( f : Frame ) Handle ( VAR m : OS.Message ); PROCEDURE ( f : Frame ) InvertBlock ( x, y, w, h : INTEGER ); END; PROCEDURE New ( graphic : ShapesO.Graphic ) : Frame; END GraphicFramesO. DEFINITION Draw0; PROCEDURE Open; PROCEDURE Store; END Draw0. DEFINITION Rectangles0; IMPORT Shapes0; TYPE Rectangle = POINTER TO RectDesc; RectDesc = RECORD ( Shapes0.ShapeDesc ) END; PROCEDURE Set; END Rectangles0. DEFINITION GraphicElems0; IMPORT Texts0; TYPE Element = POINTER TO ElemDesc; ElemDesc = RECORD ( Texts0.ElemDesc ) END; PROCEDURE Insert; PROCEDURE Update; END GraphicElems0;
MODULE Oberon0;
IMPORT OS, Viewers0, Texts0, TextFrames0;
CONST ESC = 1BX;
PROCEDURE Loop*;
VAR ch : CHAR;
x, y : INTEGER;
buttons : SET;
v : Viewers0.Viewer;
t : Texts0.Text;
BEGIN
NEW ( t );
t.Clear;
v := Viewers0.New ( TextFramesO.NewMenu ( "LOG", "Viewers0.Close" ),
TextFrames0.New ( t )); (*open the log viewer*)
LOOP (* wait for events *)
IF OS.AvailChars() > 0 THEN
OS.ReadKey ( ch );
IF ch = ESC THEN
EXIT
ELSIF Viewers0.focus # NIL THEN
Viewers0.focus.HandleKey ( ch )
END
ELSE
OS.GetMouse ( buttons, x, y );
v := Viewers0.ViewerAt ( y );
IF v # NIL THEN
v.HandleMouse ( x, y, buttons )
ELSE
OS.DrawCursor( x, y )
END
END
END
END Loop;
END Oberon0.
MODULE Viewers0;
IMPORT OS;
TYPE
Frame* = POINTER TO FrameDesc;
FrameDesc* = RECORD ( OS.ObjectDesc )
x*, y* : INTEGER; (* Ieft bottom in pixels relative to Ieft bot.
of screen *)
w*, h* : INTEGER (* width, height in pixels *)
END;
Viewer* = POINTER TO ViewerDesc;
ViewerDesc* = RECORD ( FrameDesc )
menu-, cont- : Frame; (* menu frame, contents frame *)
next- : Viewer;
END;
VAR
focus- : Frame; (* the frame that gets the keyboard input *)
viewers : Viewer; (* root for list of viewers on the screen *)
barH : INTEGER; (* default height of title bar *)
minH : INTEGER; (* minimal height of a viewer *)
PROCEDURE ( f : Frame ) Draw*;
END Draw;
PROCEDURE ( f : Frame ) Copy* () : Frame;
END Copy;
PROCEDURE ( f : Frame ) Neutralize*;
END Neutralize;
PROCEDURE ( f : Frame ) HandleKey* ( ch : CHAR );
END HandleKey;
PROCEDURE ( f : Frame ) HandleMouse* ( x, y : INTEGER; buttons : SET );
END HandleMouse;
PROCEDURE ( f : Frame ) Handle* ( VAR m : OS.Message );
END Handle;
PROCEDURE ( f : Frame ) Modify* ( dy : INTEGER );
BEGIN
INC ( f.y, dy );
DEC( f.h, dy )
END Modify;
PROCEDURE ( f : Frame ) Move* ( dy : INTEGER );
BEGIN
INC ( f.y, dy )
END Move;
PROCEDURE ( f : Frame ) Defocus*;
BEGIN
focus := NIL
END Defocus;
PROCEDURE ( f : Frame ) SetFocus*;
BEGIN
IF focus # NIL THEN
focus.Defocus
END;
focus := f
END SetFocus;
PROCEDURE ( v : Viewer ) Erase ( h : INTEGER );
BEGIN
IF h > O THEN (* clear bottom block and draw Ieft and right border *)
OS.EraseBlock ( v.x, v.y, v.w, h );
OS.FillBlock ( v.x, v.y, 1, h );
OS.FillBlock ( v.x + v.w - 1, v.y, 1, h )
END;
OS.FillBlock ( v.x, v.y, OS.screenW, 1 )
END Erase;
PROCEDURE ( v : Viewer ) FlipTitleBar;
BEGIN
OS.InvertBlock ( v.x + 1, v.y + v.h - barH, OS.screenW - 2, barH )
END FlipTitleBar;
PROCEDURE ( v : Viewer ) Neutralize*;
BEGIN
v.menu.Neutralize;
v.cont.Neutralize
END Neutralize;
PROCEDURE ( v : Viewer ) Modify* ( dy : INTEGER );
BEGIN
v.Neutralize;
v.ModifyA ( dy );
v.Erase ( - dy + 1 );
v.cont.Modify ( dy )
END Modify;
PROCEDURE ( v : Viewer ) Move* ( dy : INTEGER );
BEGIN
v.Neutralize;
v.menu.Move ( dy );
v.cont.Move ( dy );
OS.CopyBlock ( v.x, v.y + 1, v.w, v.h - 1, v.x, v.y + dy + 1 );
INC ( v.y, dy )
END Move;
PROCEDURE ( v : Viewer ) Draw*;
BEGIN
OS.FadeCursor;
v.Erase ( v.h );
v.menu.Draw;
v.cont.Draw;
v.FlipTitleBar
END Draw;
PROCEDURE ( v : Viewer ) HandleMouse* ( x, y : INTEGER; buttons: SET );
VAR b : SET;
x1, y1: INTEGER;
dy, maxUp, maxDown: INTEGER;
BEGIN
OS.DrawCursor( x, y );
IF y > v.menu.y THEN
IF OS.left IN buttons THEN (* left click in menu bar => resize viewer *)
(* ----- track mouse movements*)
v.FlipTitleBar;
REPEAT
OS.GetMouse( b, x1, y1 );
OS.DrawCursor( x1, y1 )
UNTIL b = {};
v.FlipTitleBar;
(* ----- compute how far v can be moved up or down *)
dy := y1 -y;
maxDown := v.h - minH;
IF v.next = NIL THEN
maxUp := OS.screenH - v.y - v.h
ELSE
maxUp := v.next.h - minH;
v.next.Neutralize
END;
IF dy < - maxDown THEN
dy := - maxDown
ELSIF dy > maxUp THEN
dy := maxUp
END;
(* ----- move v up or down and adjust neighbor viewers *)
OS.FadeCursor;
v.Neutralize;
IF dy < O THEN (* move down *)
v.Modify ( - dy );
v.Move ( dy )
ELSE (* move up *)
v.Move ( dy );
v.Modify ( - dy )
END;
IF v.next # NIL THEN
v.next.Modify ( dy )
ELSE
OS.EraseBlock ( v.x, v.y + v.h, v.w, OS.screenH - v.y - v.h )
END
ELSE
v.menu.HandleMouse( x, y, buttons )
END
ELSE
v.cont.HandleMouse ( x, y, buttons )
END
END HandleMouse;
PROCEDURE ( v : Viewer ) Handle* ( VAR m : OS.Message );
BEGIN
v.menu.Handle ( m );
v.cont.Handle ( m )
END Handle;
PROCEDURE ( v : Viewer ) Close*;
VAR x: Viewer;
BEGIN
OS.FadeCursor;
v.Neutralize;
IF v.next # NIL THEN
v.next.Modify ( - v.h )
ELSE
OS.EraseBlock ( v.x, v.y, v.w, v.h )
END;
IF viewers = v THEN
viewers := v.next
ELSE
x := viewers;
WHILE x.next # v DO
x := x.next
END;
x.next := v.next
END
END Close;
PROCEDURE ViewerAt* ( y : INTEGER ) : Viewer;
VAR v : Viewer;
BEGIN
v := viewers;
WHILE ( v # NIL ) & ( y > v.y + v.h ) DO
v := v.next
END;
RETURN v
END ViewerAt;
PROCEDURE New* ( menu, cont : Frame ) : Viewer;
VAR below, above, v, w: Viewer;
top: INTEGER;
BEGIN
(* ----- compute position of new viewer *)
IF ViewerAt ( OS.screenH ) = NIL THEN
top := OS.screenH
ELSE
w := viewers;
v := viewers.next;
WHILE v # NIL DO
IF v.h > w.h THEN
w := v
END;
v := v.next
END;
top := w.y + w.h DIV 2
END;
(* ----- generate new viewer and link it into viewer list *)
above := viewers;
below := NIL;
WHILE ( above # NIL ) & ( top > above.y + above.h ) DO
below := above;
above := above.next
END;
NEW(v);
v.x := 0;
v.w := OS.screenW;
v.next := above;
IF below = NIL THEN
v.y := 0;
v.h := top
ELSE
v.y := below.y + below.h;
v.h := top -v.y
END;
IF v.h < minH THEN
RETURN NIL
END;
v.menu := menu;
v.cont := cont;
menu.x := v.x + 1;
menu.y := v.y + v.h - barH;
menu.w := v.w - 2;
menu.h := barH - 1;
cont.x := v.x + 1;
cont.y := v.y + 1;
cont.w := v.w - 2;
cont.h := menu.y - v.y - 1;
IF below = NIL THEN
viewers := v
ELSE
below.next := v
END;
IF above # NIL THEN
above.Modify(v.h)
END;
v.Draw;
RETURN v
END New;
PROCEDURE Broadcast* ( VAR m : OS.Message );
VAR v : Viewer;
BEGIN
v := viewers;
WHILE v # NIL DO
v.Handle ( m );
v := v.next
END
END Broadcast;
PROCEDURE Close*;
VAR x, y : INTEGER;
buttons : SET;
v : Viewer;
BEGIN
OS.GetMouse ( buttons, x, y );
v := ViewerAt ( y );
v.Close
END Close;
PROCEDURE Copy*;
VAR v : Viewer;
x, y : INTEGER;
buttons : SET;
BEGIN
OS.GetMouse ( buttons, x, y );
v := ViewerAt ( y );
v := New ( v.menu.Copy (). v.cont.Copy ())
END Copy;
PROCEDURE Init;
VAR f : OS.Font;
BEGIN
viewers := NIL;
focus := NIL;
f := OS.DefaultFont ();
barH := f.height + 2;
minH := barH + 2;
END Init;
BEGIN (* Viewers0 *)
Init
END Viewers0.
MODULE AsciiTexts;
IMPORT OS, Viewers0;
CONST minBufLen = 32;
TYPE
Buffer = POINTER TO ARRAY OF CHAR;
Text* = POINTER TO TextDesc;
TextDesc* = RECORD ( OS.ObjectDesc )
Len- : LONGINT; (* text length *)
pos- : LONGINT; (* read/write position *)
buf : Buffer; (* text buffer *)
gap : LONGINT (* index of first byte in gap *)
END;
NotifyInsMsg* = RECORD ( OS.Message )
t* : Text;
beg*, end* : LONGINT
END;
NotifyDelMsg* = RECORD ( OS.Message )
t* : Text;
beg*, end* : LONGINT
END;
PROCEDURE ( t : Text ) MoveGap ( to : LONGINT );
VAR n, gapLen : LONGINT;
BEGIN
n := ABS ( to - t.gap );
gapLen := LEN ( t.buf^ ) - t.len;
IF to > t.gap THEN
OS.Move ( t.buf^, t.gap + gapLen, t.buf^, t.gap, n )
ELSIF to < t.gap THEN
OS.Move ( t.buf^, t.gap - n, t.buf^, t.gap + gapLen - n, n )
END;
t.gap := to
END MoveGap;
PROCEDURE ( t : Text ) Grow ( size : LONGINT );
VAR bufLen : LONGINT; old : Buffer;
BEGIN
bufLen := LEN ( t.buf^ );
IF size > bufLen THEN
t.MoveGap ( t.len );
WHILE bufLen < size DO
bufLen := 2 * bufLen
END;
old := t.buf;
NEW ( t.buf, bufLen );
OS.Move ( old^, 0, t.buf^, 0, t.len )
END
END Grow;
PROCEDURE ( t : Text ) Shrink;
VAR bufLen : LONGINT;
old : Buffer;
BEGIN
bufLen := LEN ( t.buf^ );
t.MoveGap ( t.len );
WHILE ( bufLen >= 2 * t.len ) & ( bufLen > minBufLen ) DO
bufLen := bufLen DIV 2
END;
old := t.buf;
NEW( t.buf, bufLen );
OS.Move( old^, 0, t.buf^, 0, t.len )
END Shrink;
PROCEDURE ( t : Text ) Clear*;
BEGIN
NEW( t.buf, minBufLen );
t.gap := 0;
t.pos := 0;
t.len := 0
END Clear;
PROCEDURE ( t : Text ) Insert* ( at : LONGINT; t1 : Text;
beg, end : LONGINT );
VAR len : LONGINT;
rn : NotifylnsMsg; t0: Text;
BEGIN
IF t = t1 THEN
NEW ( t0 );
t0.Clear;
t0.Insert ( 0, t1, beg, end );
t.Insert( at, t0, 0, t0.len )
ELSE
len := end - beg;
IF t.len + len > LEN ( t.buf^ ) THEN
t.Grow ( t.len + len )
END;
t.MoveGap ( at );
t1.MoveGap ( end );
OS.Move ( t1.buf^, beg, t.buf^, t.gap, len );
INC ( t.gap, len );
INC ( t.len, len );
m.t := t;
m.beg := at;
m.end := at + len;
Viewers0.Broadcast ( m )
END
END Insert;
PROCEDURE ( t : Text ) Delete* ( beg, end : LONGINT );
VAR rn : NotifyDelMsg;
BEGIN
t.MoveGap ( end );
t.gap := beg;
DEC ( t.len, end - beg );
IF ( t.len * 2 < LEN ( t.buf^ )) & ( LEN ( t.buf^ ) > minBufLen ) THEN
t.Shrink
END;
m.t := t;
m.beg := beg;
m.end := end;
Viewers0.Broadcast ( m )
END Delete;
PROCEDURE ( t : Text ) SetPos* ( pos : LONGINT );
BEGIN
t.pos := pos
END SetPos;
PROCEDURE ( t : Text ) Read* ( VAR ch : CHAR );
VAR i : LONGINT;
BEGIN
i := t.pos;
IF t.pos >= t.gap THEN
INC ( i, LEN ( t.buf^ ) - t.len )
END;
IF t.pos < t.len THEN
ch := t.buf [ i ];
INC ( t.pos )
ELSE
ch := 0X
END
END Read;
PROCEDURE ( t : Text ) Write* ( ch : CHAR );
VAR rn : NotifyInsMsg;
BEGIN
IF t.len = LEN ( t.buf^ ) THEN
t.Grow ( t.len + 1 )
END;
IF t.pos # t.gap THEN
t.MoveGap ( t.pos )
END;
t.buf [ t.gap ] := ch;
INC ( t.gap );
INC ( t.pos );
INC ( t.len );
m.t := t;
m.beg := t.gap - 1;
m.end := t.gap;
Viewers0.Broadcast ( m )
END Write;
PROCEDURE ( t : Text ) Load* ( VAR r : OS.Rider );
VAR len : LONGINT;
BEGIN
t.Clear;
r.ReadLInt ( len );
t.Grow ( len );
r.ReadChars ( t.buf^, len);
t.gap := len;
t.len := len
END Load;
PROCEDURE ( t : Text) Store* ( VAR r : OS.Rider );
BEGIN
t.MoveGap ( t.len );
r.WriteLInt ( t.len );
r.WriteChars ( t.buf^, t.len )
END Store;
END AsciiTexts.
MODULE Texts0;
IMPORT OS, AsciiTexts, Viewers0;
CONST ELEM = 1CX;
TYPE
Element* = POINTER TO ElemDesc;
Attribute* = POINTER TO AttrDesc;
Text* = POINTER TO TextDesc;
TextDesc* = RECORD ( AsciiTexts.TextDesc )
attr- : Attribute; (* attributes of previously read character *)
firstAttr : Attribute; (* to attribute first (first node is dummy) *)
attrRes t: LONGINT (* unread bytes in current attribute segment *)
END;
AttrDesc* = RECORD
len : LONGINT; (* length of attribute segment *)
fnt- : OS.Font; (* font of this attribute segment *)
elem- : Element; (* pointer to element descriptor or NIL *)
next : Attribute
END;
ElemDesc* = RECORD ( OS.ObjectDesc )
w*, h* : INTEGER; (* width and height in pixels *)
dsc* : INTEGER (* descender (part under the base fine) *)
END;
NotifyInsMsg* = AsciiTexts.NotifyInsMsg;
NotifyDelMsg* = AsciiTexts.NotityDelMsg;
NotifyReplMsg* = RECORD ( OS.Message )
t* : Text;
beg*, end* : LONGINT
END;
PROCEDURE ( e : Element ) Draw* ( x, y : INTEGER );
END Draw;
PROCEDURE ( e : Element ) HandleMouse* ( f : OS.Object; x, y : INTEGER );
END HandleMouse;
PROCEDURE ( e : Element ) Copy* () : Element;
END Copy;
PROCEDURE ( e : Element ) Load* ( VAR r : OS.Rider );
BEGIN
r.ReadInt ( e.w );
r.ReadInt ( e.h );
r.ReadInt ( e.dsc )
END Load;
PROCEDURE ( e : Element ) Store* ( VAR r : OS.Rider );
BEGIN
r.WriteInt ( e.w );
r.WriteInt ( e.h );
r.WriteInt ( e.dsc )
END Store;
PROCEDURE ( t : Text ) Split ( pos : LONGINT; VAR prev : Attribute );
VAR a, b : Attribute;
BEGIN
a := t.firstAttr;
WHILE ( a # NIL ) & ( pos >= a.len ) DO
DEC ( pos, a.len );
prev := a;
a := a.next
END;
IF ( a # NIL ) & ( pos > 0 ) THEN
NEW ( b );
b.elem := a.elem;
b.fnt := a.fnt;
b.len := a.len - pos;
a.len := pos;
b.next := a.next;
a.next := b;
prev := a
END
END Split;
PROCEDURE ( t : Text ) Merge ( a : Attribute );
VAR b : Attribute;
BEGIN
b := a.next;
IF ( b # NIL ) & ( a.fnt = b.fnt ) & ( a.len > 0 ) &
( a.elem = NIL ) & ( b.elem = NIL ) THEN
INC ( a.len, b.len );
a.next := b.next
END
END Merge;
PROCEDURE ( t : Text ) Insert* ( at : LONGINT; t1 : AsciiTexts.Text;
beg, end : LONGINT );
VAR a, b, c, d, i, j, k : Attribute;
t0 : Text;
BEGIN
IF t = t1 THEN
NEW(t0);
t0.Clear;
t0.Insert ( 0, t1, beg, end );
t.Insert ( at, t0, 0, t0.len )
ELSE
WITH t1 : Text DO
t1.Split ( beg, a );
t1.Split ( end, b );
t.Split ( at, c );
d := c.next;
i := a;
j := c;
WHILE i # b DO
i := i.next;
NEW ( k );
k^ := i^;
IF i.elem # NIL THEN
k.elem := i.elem.Copy()
END;
j.next := k;
j := k
END;
j.next := d;
t1.Merge ( b );
t1.Merge ( a );
t.Merge ( j );
t.Merge ( c );
t.Insert^ ( at, t1 , beg, end )
END
END
END Insert;
PROCEDURE ( t : Text ) Delete* ( beg, end : LONGINT );
VAR a, b : Attribute;
BEGIN
t.Split ( beg, a );
t.Split ( end, b );
a.next := b.next;
t.Merge ( a );
t.Delete^ ( beg, end )
END Delete;
PROCEDURE ( t : Text ) SetPos* ( pos : LONGINT );
VAR prev, a: Attribute;
BEGIN
t.SetPos^ ( pos );
a := t.firstAttr;
WHILE ( a # NIL ) & ( pos >= a.len ) DO
DEC ( pos, a.len );
prev := a;
a := a.next
END;
IF ( a = NIL ) OR ( pos = 0 ) THEN
t.attr := prev;
t.attrRest := 0
ELSE
t.attr := a;
t.attrRest := a.len - pos
END
END SetPos;
PROCEDURE ( t : Text ) Read* ( VAR ch: CHAR );
BEGIN
t.Read^ ( ch );
IF ( t.attrRest = 0 ) & ( t.attr.next # NIL ) THEN
t.attr := t.attr.next;
t.attrRest := t.attr.len
END;
DEC ( t.attrRest )
END Read;
PROCEDURE ( t : Text ) Write* ( ch : CHAR );
VAR a, prev : Attribute;
at : LONGINT;
BEGIN
a := t.firstAttr;
at := t.pos;
WHILE ( a # NIL ) & ( at >= a.len ) DO
DEC ( at, a.len );
prev := a;
a := a.next
END;
IF ( a = NIL ) OR ( at = 0 ) THEN (* insert at end of attribute segment *)
IF ( prev = t.firstAttr ) OR ( prev.elem # NIL ) THEN
NEW ( a );
a.elem := NIL;
a.fnt := prev.fnt;
a.len := 1;
a.next := prev.next;
prev.next := a;
t.Merge( a )
ELSE
INC ( prev.len )
END
ELSE
INC ( a.len )
END;
t.Write^ ( ch )
END Write;
PROCEDURE ( t : Text ) ReadNextElem* ( VAR e : Element );
VAR pos : LONGINT;
a : Attribute;
BEGIN
pos := t.pos + t.attrRest;
a := t.attr.next;
WHILE ( a # NIL ) & ( a.elem = NIL ) DO
pos := pos + a.len;
a := a.next
END;
IF a # NIL THEN
e := a.elem;
t.SetPos( pos + 1 )
ELSE
e := NIL;
t.SetPos ( t.len )
END
END ReadNextElem;
PROCEDURE ( t : Text ) WriteElem* ( e : Element );
VAR x, y : Attribute;
m : NotifyReplMsg;
BEGIN
t.Write ( ELEM );
t.Split ( t.pos - 1, x);
t.Split ( t.pos, y );
y.elem := e;
m.t := t;
m.beg := t.pos - 1;
m.end := t.pos;
Viewers0.Broadcast( m )
END WriteElem;
PROCEDURE ( t : Text ) ElemPos* ( e : Element ) : LONGINT;
VAR pos : LONGINT;
a: Attribute;
BEGIN
a := t.firstAttr;
pos := 0;
WHILE ( a # NIL ) & ( a.elem # e ) DO
pos := pos + a.len;
a := a.next
END;
RETURN pos
END ElemPos;
PROCEDURE ( t : Text ) ChangeFont* ( beg, end: LONGINT; fnt : OS.Font );
VAR a, b : Attribute;
m : NotifyReplMsg;
PROCEDURE Change( a : Attribute );
BEGIN
a.fnt := fnt;
IF a # b THEN
Change ( a.next )
END;
t.Merge ( a )
END Change;
BEGIN
IF end > beg THEN
t.Split ( beg, a );
t.Split ( end, b );
Change ( a.next );
t.Merge ( a );
m.t := t;
m.beg := beg;
m.end := end;
Viewers0.Broadcast ( m )
END
END ChangeFont;
PROCEDURE ( t : Text ) Clear*;
BEGIN
t.Clear^;
NEW ( t.firstAttr );
t.firstAttr.elem := NIL;
t.firstAttr.next := NIL;
t.firstAttr.fnt := OS.DefaultFont ();
t.firstAttr.len := 0;
t.SetPos ( 0 )
END Clear;
PROCEDURE ( t : Text ) Store* ( VAR r : OS.Rider );
VAR a : Attribute;
BEGIN
t.Store^ ( r );
a := t.firstAttr.next;
WHILE a # NIL DO
r.WriteString ( a.fnt.name );
r.WriteObj ( a.elem );
r.WriteLInt ( a.len );
a := a.next
END;
r.Write ( 0X ) (*empty font name terminates attribute list *)
END Store;
PROCEDURE ( t : Text) Load* ( VAR r : OS.Rider );
VAR prev, a: Attribute;
name: ARRAY 32 OF CHAR;
x : OS.Object;
BEGIN
t.Load^ ( r );
prev := t.firstAttr;
LOOP
r.ReadString ( name );
IF name = "" THEN
EXIT
END;
NEW ( a );
a.fnt := OS.FontWithName ( name );
r.ReadObj ( x );
r.ReadLInt( a.len );
IF x = NIL THEN
a.elem := NIL
ELSE
a.elem := x ( Element )
END;
prev.next := a;
prev := a
END;
prev.next := NIL
END Load;
END Texts0.
MODULE TextFrames0;
IMPORT OS, Viewers0, Texts0;
CONST
EOL = 0DX; (* end of line character *)
DEL = 7FX; (* delete character *)
scrollW = 12; (* width of scroll bat *)
TYPE
Line = POINTER TO LineDesc;
LineDesc = RECORD
len, wid, asc, dsc : INTEGER; (* length, width, ascender, descendet *)
eol : BOOLEAN; (* TRUE if line is terminated with EOL *)
next: Line
END;
Position* = RECORD (* position of a character c on the screen*)
x-, y-, dx- : INTEGER; (* (x,y) = left point on base line;
dx = width of c *)
org-, pos- : LONGINT; (* origin of line containing c; text position of c *)
L : Line (* line containing c *)
END;
Frame* = POINTER TO FrameDesc;
FrameDesc* = RECORD ( Viewers0.FrameDesc )
text* : Texts0.Text;
org- : LONGINT; (* index of first character in the frame *)
caret- : Position; (* caret; visible if caret.pos >= 0 *)
selBeg-, selEnd- : Position; (* selection; visible if selBeg.pos >= 0 *)
seiTime : LONGINT; (* time stamp of selection *)
lsp : INTEGER; (* space between lines *)
margin : INTEGER; (* space between frame border and text *)
lines : Line (* list of lines in frame (first line in dummy) *)
END;
SelectionMsg = RECORD ( OS.Message )
f : Frame
END;
VAR
cmdFrame- : Frame; (* frame containing the most recent command *)
cmdPos- : LONGINT; (*text position after the most recent command*)
PROCEDURE GetMetric ( at : Texts0.Attribute;
ch : CHAR; VAR dx, x, y, asc, dsc : INTEGER; VAR pat : OS.Pattern);
VAR w, h : INTEGER;
BEGIN
IF at.elem = NIL THEN
OS.GetCharMetric ( at.fnt, ch, dx, x, y, w, h, pat );
asc := at.fnt.maxY;
dsc := - at.fnt.minY
ELSE
dx := at.elem.w;
x := 0;
y := 0;
dsc := at.elem.dsc;
asc := at.elem.h - dsc
END
END GetMetric;
PROCEDURE MeasureLine ( t : Texts0.Text; VAR L : Line);
VAR ch : CHAR;
dx, x, y, asc, dsc : INTEGER;
pat : OS.Pattern;
BEGIN
L.len := 0;
L.wid := 0;
L.asc := 0;
L.dsc := 0;
ch := " ";
WHILE ( ch # EOL ) & ( t.pos < t.len ) DO
t.Read ( ch );
INC ( L.len );
GetMetric ( t.attr, ch, dx, x, y, asc, dsc, pat );
INC ( L.wid, dx );
IF asc > L.asc THEN
L.asc := asc
END;
IF dsc > L.dsc THEN
L.dsc := dsc
END
END;
L.eol := ch = EOL
END MeasureLine;
PROCEDURE DrawLine ( t : Texts0.Text; len, left, right, base : INTEGER );
VAR ch : CHAR; dx, X, y, w, h : INTEGER;
pat : OS.Pattern;
BEGIN
WHILE len > 0 DO
t.Read ( ch );
DEC ( len );
IF t.attr.elem = NIL THEN
OS.GetCharMetric ( t.attr.fnt, ch, dx, x, y, w, h, pat );
IF left + dx < right THEN
OS.DrawPattern ( pat, left + x, base + y )
END
ELSE
dx := t.attr.elem.w;
IF left + dx < right THEN
t.attr.elem.Draw ( left, base )
END
END;
INC ( left, dx )
END
END DrawLine;
PROCEDURE ( f : Frame ) FlipCaret;
BEGIN
OS.DrawPattern ( OS.Caret, f.caret.x, f.caret.y - 10 )
END FlipCaret;
PROCEDURE ( f : Frame) FlipSelection ( a, b : Position );
VAR x, y : INTEGER;
L : Line;
BEGIN
L := a.L;
x := a.x;
y := a.y - L.dsc;
WHILE L # b.L DO
OS.InvertBlock ( x, y, f.x + f.w - x, L.asc + L.dsc );
L := L.next;
x := f.x + scrolIW + f.margin;
y := y - f.lsp - L.asc - L.dsc
END;
OS.InvertBlock ( x, y, b.x - x, L.asc + L.dsc )
END FlipSelection;
PROCEDURE ( f : Frame ) RedrawFrom ( top : INTEGER );
VAR t : Texts0.Text;
L, L0 : Line;
y: INTEGER;
org: LONGINT;
BEGIN
(* ----- find first line to be redrawn *)
y := f.y + f.h - f.margin;
org := f.org;
L0 := f.lines;
L := L0.next;
WHILE ( L # f.lines ) & ( y - L.asc - L.dsc >= top ) DO
DEC ( y, L.asc + L.dsc + f.lsp );
org := org + L.len;
L0 := L;
L := L.next
END;
IF y > top THEN
top := y
END;
OS.FadeCursor;
OS.EraseBlock ( f.x, f.y, f.w, top - f.y );
IF f.margin > 0 THEN (* draw scroll bar *)
OS.InvertBlock ( f.x + scrollW, f.y, 1 , top - f.y )
END;
(* ----- redraw lines and rebuild line descriptors;
L0 is last valid line descriptor *)
t := f.text;
LOOP
NEW ( L );
t.SetPos ( org );
MeasureLine ( t, L );
IF ( L.len = 0 ) OR ( y - L.asc - L.dsc < f.y + f.margin ) THEN
EXIT
END;
t.SetPos ( org );
DrawLine ( t, L.len, f.x + scrollW + f.margin, f.x + f.w - f.margin,
y - L.asc );
org := org + L.len;
DEC ( y, L.asc + L.dsc + f.lsp);
L0.next := L;
L0 := L;
IF t.pos >= t.len THEN
EXIT
END
END;
L0.next := f.lines
END RedrawFrom;
PROCEDURE ( f : Frame ) GetPointPos ( x0, y0 : INTEGER; VAR p : Position );
VAR t : Texts0.Text;
ch: CHAR;
L: Line;
dx, x, y, asc, dsc: INTEGER;
pat: OS.Pattern;
BEGIN
(* ----- find line containing y0 *)
L := f.lines.next;
p.y := f.y + f.h - f.margin;
p.org := f.org;
WHILE ( L # f.lines ) & ( y0 < p.y - L.asc - L.dsc - f.lsp ) & L.eol DO
DEC ( p.y, L.asc + L.dsc + f.lsp );
p.org := p.org + L.len;
L := L.next
END;
DEC ( p.y, L.asc );
(* ----- find character containing x0 *)
p.x := f.x + scrollW + f.margin;
p.L := L;
p.pos := p.org;
t := f.text;
t.SetPos ( p.pos );
LOOP
IF p.pos >= t.len THEN
p.dx := 0;
EXIT
END;
t.Read ( ch );
GetMetric ( t.attr, ch, dx, x, y, asc, dsc, pat );
IF ( ch = EOL ) OR ( p.x + dx > x0 ) THEN
p.dx := dx;
EXIT
ELSE
INC ( p.pos );
INC ( p.x, dx )
END;
END
END GetPointPos;
PROCEDURE ( f : Frame ) GetCharPos ( pos : LONGINT; VAR p : Position );
VAR t : Texts0.Text;
ch : CHAR;
L : Line;
dx, x, y, asc, dsc : INTEGER;
pat : OS.Pattern;
i : LONGINT;
BEGIN
(* ----- find line containing pos *)
L := f.lines.next;
p.y := f.y + f.h - f.margin;
p.org := f.org;
p.pos := pos;
WHILE ( L # f.lines ) & ( pos >= p.org + L.len ) & L.eol DO
p.org := p.org + L.len;
DEC ( p.y, L.asc + L.dsc + f.lsp );
L := L.next
END;
DEC ( p.y, L.asc );
p.L := L;
(* ----- find character at pos *)
p.x := f.x + scrollW + f.margin;
t := f.text;
t.SetPos ( p.org );
FOR i := 1 TO p.pos - p.org DO
t.Read ( ch ); GetMetric ( t.attr, ch, dx, x, y, asc, dsc, pat );
INC ( p.x, dx )
END;
IF t.pos >= t.len THEN
p.dx := 0
ELSE
t.Read ( ch );
GetMetric ( t.attr, ch, p.dx, x, y, asc, dsc, pat )
END
END GetCharPos;
PROCEDURE ( f : Frame ) CallCommand;
VAR x, y, i : INTEGER;
buttons : SET;
p : Position;
t : Texts0.Text;
ch : CHAR;
cmd : ARRAY 64 OF CHAR;
BEGIN
REPEAT
OS.GetMouse ( buttons, x, y )
UNTIL buttons = {};
f.GetPointPos ( x, y, p );
t := f.text;
t.SetPos ( p.org );
t.Read ( ch );
REPEAT
WHILE ( t.pos < t.len ) & ( ch # EOL ) & (( CAP ( ch ) < "A" )
OR ( CAP ( ch ) > "Z" )) DO
t.Read(ch)
END;
i := 0;
WHILE ( CAP ( ch ) >= "A" ) & ( CAP ( ch ) <= "Z" )
OR ( ch >= "0" ) & ( ch <= "9" ) OR ( ch = "." ) DO
cmd [ i ] := ch;
INC ( i );
t.Read ( ch )
END;
cmd [ i ] := 0X;
UNTIL ( t.pos >= t.len ) OR ( ch = EOL ) OR ( t.pos > p.pos );
cmdFrame := f;
cmdPos := t.pos;
OS.Call ( cmd )
END CallCommand;
PROCEDURE ( f : Frame ) RemoveCaret*;
BEGIN
IF f.caret.pos >= 0 THEN
f.FlipCaret;
f.caret.pos := - 1
END
END RemoveCaret;
PROCEDURE ( f : Frame ) SetCaret* ( pos : LONGINT );
VAR p : Position;
BEGIN
IF pos < O THEN
pos := O
ELSIF pos > f.text.len THEN
pos := f.text.len
END;
f.SetFocus;
f.GetCharPos ( pos, p );
IF p.x < f.x + f.w - f.margin THEN
f.caret := p;
f.FlipCaret
END
END SetCaret;
PROCEDURE ( f : Frame) RemoveSelection*;
BEGIN
IF f.selBeg.pos >= O THEN
f.FlipSelection ( f.selBeg, f.selEnd);
f.selBeg.pos := - 1
END
END RemoveSelection;
PROCEDURE ( f : Frame ) SetSelection* ( from, to : LONGINT );
BEGIN
f.RemoveSelection;
f.GetCharPos ( from, f.selBeg );
f.GetCharPos ( to, f.selEnd );
f.FlipSelection ( f.selBeg, f.selEnd);
f.selTime := OS.Time ()
END SetSelection;
PROCEDURE ( f : Frame ) Defocus*;
BEGIN
f.RemoveCaret;
f.Defocus^
END Defocus;
PROCEDURE ( f : Frame ) Neutralize*;
BEGIN
f.RemoveCaret;
f.RemoveSelection
END Neutralize;
PROCEDURE ( f : Frame ) Draw*;
BEGIN
f.RedrawFrom ( f.y + f.h )
END Draw;
PROCEDURE ( f : Frame ) Modify* ( dy : INTEGER);
VAR y : INTEGER;
BEGIN
y := f.y;
f.Modify^ ( dy );
IF y > f.y THEN
f.RedrawFrom ( y )
ELSE
f.RedrawFrom ( f.y )
END
END Modify;
PROCEDURE ( f : Frame ) HandleMouse* ( x, y : INTEGER; buttons : SET );
VAR p : Position;
b : SET;
t : Texts0.Text;
ch : CHAR;
f1 : Frame;
BEGIN
f.HandleMouse^ ( x, y, buttons );
t := f.text;
IF ( x < f.x + scrollW ) & ( buttons # {} )
THEN (* handle click in scroll bar *)
REPEAT
OS.GetMouse ( b, x, y );
buttons := buttons + b
UNTIL b = {};
f.Neutralize;
IF OS.left IN buttons THEN
f.GetPointPos ( x, y, p );
f.org := p.org
ELSIF OS.right IN buttons THEN
f.org := O
ELSIF OS.middle IN buttons THEN
t.SetPos (( f.y + f.h - y ) * f.text.len DIV f.h );
REPEAT
t.Read ( ch )
UNTIL ( ch = EOL ) OR ( t.pos >= t.len );
f.org := t.pos
END;
f.RedrawFrom ( f.y + f.h )
ELSE (* handle click in text area *)
f.GetPointPos ( x, y, p );
IF OS.left IN buttons THEN
IF p.pos # f.caret.pos THEN
f.SetCaret ( p.pos )
END
ELSIF OS.middle IN buttons THEN
t.SetPos ( p.pos );
t.Read ( ch );
IF t.attr.elem = NIL THEN
f.CallCommand
ELSE
t.attr.elem.HandleMouse ( f, x, y )
END
ELSIF OS.right IN buttons THEN
f.RemoveSelection;
f.selBeg := p;
f.selEnd := p;
f.selTime := OS.Time ();
LOOP
OS.GetMouse ( b, x, y );
buttons := buttons + b;
IF b = {} THEN
EXIT
END;
OS.DrawCursor ( x, y );
f.GetPointPos ( x, y, p );
IF p.pos < f.selBeg.pos THEN
p := f.selBeg
END;
IF p.pos < t.len THEN
INC ( p.pos );
INC ( p.x, p.dx )
END;
IF p.pos # f.selEnd.pos THEN
IF p.pos > f.selEnd.pos THEN
f.FlipSelection ( f.selEnd, p )
ELSE
f.FlipSelection ( p, f.selEnd )
END;
f.selEnd := p
END
END;
(* ----- check for right-left or right-middle click *)
IF OS.left IN buttons THEN
t.Delete ( f.selBeg.pos, f.selEnd.pos )
ELSIF ( OS.middle IN buttons ) & ( Viewers0.focus # NIL )
& ( Viewers0.focus IS Frame ) THEN
f1 := Viewers0.focus ( Frame );
IF f1.caret.pos >= O THEN
f1.text.Insert ( f1.caret.pos, t, f.selBeg.pos, f.selEnd.pos )
END
END
END
END
END HandleMouse;
PROCEDURE ( f : Frame) HandleKey* ( ch : CHAR );
VAR pos : LONGINT;
BEGIN
pos := f.caret.pos;
IF pos >= O THEN
IF ch = DEL THEN
IF pos > O THEN
f.text.Delete ( pos - 1, pos );
f.SetCaret ( pos - 1 )
END
ELSE
f.text.SetPos ( pos );
f.text.Write ( ch );
f.SetCaret ( pos + 1 )
END
END
END HandleKey;
PROCEDURE ( f : Frame ) Handle* ( VAR m : OS.Message );
VAR t : Texts0.Text;
ch: CHAR;
dx, x, y, asc, dsc : INTEGER;
pat: OS.Pattern; p: Position;
BEGIN
t := f.text;
WITH m : Texts0.NotifyInsMsg DO
IF m.t = t THEN
IF m.beg < f.org THEN
f.org := f.org + ( m.end - m.beg )
ELSE
f.Neutralize;
OS.FadeCursor;
f.GetCharPos ( m.beg, p );
t.SetPos ( m.beg );
t.Read ( ch );
GetMetric ( t.attr, ch, dx, x, y, asc, dsc, pat );
IF ( m.end = m.beg + 1 ) & ( ch # EOL ) & ( p.L # f.lines )
& ( asc + dsc <= p.L.asc + p.L.dsc ) THEN
IF p.x + dx <= f.x + f.w - f.margin THEN
OS.CopyBlock ( p.x, p.y - p.L.dsc, f.x + f.w - f.margin - dx - p.x,
p.L.asc + p.L.dsc, p.x + dx, p.y - p.L.dsc );
OS.EraseBlock ( p.x, p.y - p.L.dsc, dx, p.L.asc + p.L.dsc );
IF t.attr.elem = NIL THEN
OS.DrawPattern ( pat, p.x + x, p.y + y )
ELSE
t.attr.elem.Draw ( p.x, p.y )
END
ELSE
OS.EraseBlock( p.x, p.y - p.L.dsc, f.x + f.w - p.x,
p.L.asc + p.L.dsc )
END;
INC ( p.L.len );
INC ( p.L.wid, dx )
ELSE
f.RedrawFrom ( p.y + p.L.asc )
END
END
END
| m : Texts0.NotifyDelMsg DO
IF m.t = t THEN
IF m.end <= f.org THEN
f.org := f.org - ( m.end - m.beg )
ELSE
f.Neutralize;
IF m.beg < f.org THEN
f.org := m.beg;
f.RedrawFrom ( f.y + f.h )
ELSE
f.GetCharPos ( m.beg, p );
f.RedrawFrom ( p.y + p.L.asc )
END
END
END
| m : Texts0.NotifyReplMsg DO
IF ( m.t = t ) & ( m.end > f.org ) THEN
f.Neutralize;
IF m.beg < f.org THEN
m.beg := f.org
END;
f.GetCharPos ( m.beg, p );
f.RedrawFrom ( p.y + p.L.asc )
END
| m: SelectionMsg DO
IF ( f.selBeg.pos >= 0 ) & (( m.f = NIL ) OR
( m.f.selTime < f.selTime )) THEN
m.f := f
END
ELSE
END
END Handle;
PROCEDURE New* ( t : Texts0.Text ) : Frame;
VAR f : Frame;
fnt : OS.Font;
BEGIN
NEW ( f );
f.text := t;
f.org := 0;
f.caret.pos := - 1;
f.selBeg.pos := - 1;
f.lsp := 2;
f.margin := 5;
NEW ( f.lines );
f.lines.next := f.lines;
fnt := OS.DefaultFont ();
f.lines.asc := fnt.maxY;
f.lines.dsc := - fnt.minY;
f.lines.len := 0;
RETURN f
END New;
PROCEDURE NewMenu* ( name, menu : ARRAY OF CHAR ) : Frame;
VAR t : Texts0.Text;
f : Frame;
i : INTEGER;
BEGIN
NEW ( t );
t.Clear;
i := 0;
WHILE name [ i ] # 0X DO t.Write ( name [ i ] );
INC(i)
END;
t.Write ( " " );
t.Write ( "|" );
t.Write ( " " );
i := 0;
WHILE menu [ i ] # 0X DO
t.Write ( menu [ i ] );
INC ( i )
END;
f := New ( t );
f.margin := 0;
RETURN f
END NewMenu;
PROCEDURE ( f : Frame ) Copy* (): Viewers0.Frame;
VAR f1 : Frame;
BEGIN
f1 := New ( f.text );
f1.margin := f.margin;
RETURN f1
END Copy;
PROCEDURE GetSelection* ( VAR f : Frame );
VAR m : SelectionMsg;
BEGIN
m.f := NIL;
Viewers0.Broadcast ( m );
f := m.f
END GetSelection;
END TextFrames0.
MODULE Edit0;
IMPORT OS, IO, TextFrames0, Texts0, Viewers0;
PROCEDURE Open*;
VAR s : IO.Scanner;
t : Texts0.Text;
menu, cont : TextFrames0.Frame;
v : Viewers0.Viewer;
f : OS.File;
r : OS.Rider;
BEGIN
s.SetToParameters;
s.Read;
IF s.class = IO.name THEN
menu := TextFrames0.NewMenu ( s.str,
"Viewers0.Close Viewers0.Copy Edit0.Store" );
NEW( t );
f := OS.OldFile ( s.str );
IF f = NIL THEN
t.Clear
ELSE
OS.InitRider ( r );
r.Set ( f, 0 );
t.Load ( r )
END;
cont := TextFrames0.New ( t );
v := Viewers0.New ( menu, cont )
END
END Open;
PROCEDURE Store*;
VAR v : Viewers0.Viewer;
s : IO.Scanner;
f : OS.File;
r : OS.Rider;
BEGIN
v := Viewers0.ViewerAt ( TextFrames0.cmdFrame.y );
s.Set ( v.menu ( TextFrames0.Frame ).text, 0 );
s.Read; (* read viewer name*)
IF s.class = IO.name THEN
v.Neutralize;
f := OS.NewFile ( s.str );
OS.InitRider ( r );
r.Set ( f, 0 );
v.cont ( TextFrames0.Frame ).text.Store ( r );
OS.Register ( f )
END
END Store;
PROCEOURE ChangeFont*;
VAR s : IO.Scanner;
f : TextFrames0.Frame;
BEGIN
s.SetToParameters;
s.Read;
TextFrames0.GetSelection ( f );
IF ( f # NIL ) & ( s.class = IO.name ) THEN
f.text.ChangeFont ( f.selBeg.pos, f.selEnd.pos, OS.FontWithName ( s.str ))
END
END ChangeFont;
END Edit0.
MODULE Shapes0;
IMPORT OS, Viewers0;
TYPE
Shape* = POINTER TO ShapeDesc;
ShapeDesc* = RECORD ( OS.ObjectDesc )
selected* : BOOLEAN; (* TRUE: shape is selected *)
next : Shape
END;
Graphic* = POINTER TO GraphicDesc;
GraphicDesc* = RECORD
shapes* : Shape
END;
NotifyChangeMsg* = RECORD ( OS.Message )
g* : Graphic
END;
VAR
curShape* : ARRAY 32 OF CHAR; (* name of current shape type *)
PROCEDURE ( s : Shape ) SetBox* ( x. y. w. h : INTEGER );
BEGIN
s.selected := FALSE;
END SetBox;
PROCEDURE ( s : Shape ) Draw* ( f : Viewers0.Frame );
END Draw;
PROCEDURE ( s : Shape ) Move* ( dx, dy: INTEGER );
END Move;
PROCEDURE ( s : Shape ) SetSelection* ( x, y, w, h : INTEGER );
END SetSelection;
PROCEDURE ( s : Shape ) Neutralize*;
BEGIN
s.selected := FALSE
END Neutralize;
PROCEDURE ( s : Shape ) GetBox* ( VAR x, y, w, h : INTEGER );
END GetBox;
PROCEDURE ( s : Shape ) Copy* () : Shape;
END Copy;
PROCEDURE InitGraphic* ( VAR g : Graphic );
BEGIN
g.shapes := NIL
END InitGraphic;
PROCEDURE ( g : Graphic ) Insert* ( s : Shape );
VAR msg : NotifyChangeMsg;
BEGIN
s.next := g.shapes;
g.shapes := s;
msg.g := g;
Viewers0.Broadcast ( msg )
END Insert;
PROCEDURE ( g : Graphic ) DeleteSelected*;
VAR s, s0 : Shape;
msg : NotifyChangeMsg;
BEGIN
s := g.shapes;
s0 := NIL;
WHILE s # NIL DO
IF s.selected THEN
IF s0 = NIL THEN
g.shapes := s.next
ELSE
s0.next := s.next
END
ELSE
s0 := s
END;
s := s.next
END;
msg.g := g;
Viewers0.Broadcast ( msg )
END DeleteSelected;
PROCEDURE ( g : Graphic) MoveSelected* ( dx, dy : INTEGER );
VAR s : Shape;
msg : NotifyChangeMsg;
BEGIN
s := g.shapes;
WHILE s # NIL DO
IF s.selected THEN
s.Move ( dx, dy )
END;
s := s.next
END;
msg.g := g;
Viewers0.Broadcast ( msg )
END MoveSelected;
PROCEDURE ( g : Graphic ) Draw* ( f : Viewers0.Frame );
VAR s : Shape;
BEGIN
s := g.shapes;
WHILE.s # NIL DO
s.Draw ( f );
s := s.next
END
END Draw;
PROCEDURE ( g : Graphic) Neutralize*;
VAR s : Shape;
msg : NotifyChangeMsg;
changed : BOOLEAN;
BEGIN
s := g.shapes;
changed := FALSE;
WHILE s # NIL DO
changed := changed OR s.selected;
s.Neutralize;
s := s.next
END;
IF changed THEN
msg.g := g;
Viewers0.Broadcast ( msg )
END
END Neutralize;
PROCEDURE ( g : Graphic ) SetSelection* ( x, y, w, h : INTEGER );
VAR s : Shape;
msg : NotifyChangeMsg;
BEGIN
s := g.shapes;
WHILE s # NIL DO s.SetSelection ( x, y, w, h );
s := s.next
END;
msg.g := g;
Viewers0.Broadcast ( msg )
END SetSelection;
PROCEDURE ( g : Graphic ) GetBox* ( VAR x, y, w, h : INTEGER );
VAR x0, y0, w0, h0: INTEGER; s: Shape;
BEGIN
x := 0; y := 0; w := 12; h := 12;
s := g.shapes;
IF s # NIL THEN
s.GetBox ( x, y, w, h );
s := s.next
END;
WHILE s # NIL DO
s.GetBox ( x0, y0, w0, h0 );
IF x0 < x THEN
INC ( w, x -x0 );
x := x0
END;
IF y0 < y THEN
INC ( h, y - y0 );
y := y0
END;
IF x0 + w0 > x + w THEN
w := x0 + w0 - x
END;
IF y0 + h0 > y + h THEN
h := y0 + h0 - y
END;
s := s.next
END;
END GetBox;
PROCEDURE ( g : Graphic ) Copy* () : Graphic;
VAR s, a, b : Shape;
g1 : Graphic;
BEGIN
NEW ( g1 );
g1.shapes := NIL;
s := g.shapes;
WHILE s # NIL DO
a := s.Copy0;
a.next := NIL;
IF g1.shapes = NIL THEN
g1.shapes := a
ELSE
b.next := a
END;
b := a;
s := s.next
END;
RETURN g1
END Copy;
PROCEDURE ( g : Graphic ) Load* ( VAR r : OS.Rider );
VAR s, last : Shape;
x : OS.Object;
BEGIN
last := NIL;
REPEAT
r.ReadObj ( x );
IF x = NIL THEN
s := NIL
ELSE
s := x ( Shape )
END;
IF last = NIL THEN
g.shapes := s
ELSE
last.next := s
END;
last := s
UNTIL x = NIL (* terminated by a NIL shape *)
END Load;
PROCEDURE ( g : Graphic ) Store* ( VAR r : OS.Rider );
VAR s : Shape;
BEGIN
s := g.shapes;
WHILE s # NIL DO
r.WriteObj ( s );
s := s.next
END;
r.WriteObj ( NIL )
END Store;
BEGIN
curShape := ""
END Shapes0.
MODULE GraphicFrames0;
IMPORT OS, Viewers0, Shapes0;
TYPE
Frame* = POINTER TO FrameDesc;
FrameDesc* = RECORD ( Viewers0.FrameDesc )
orgX*, orgY* : INTEGER; (* origin *)
graphic* : Shapes0.Graphic (* shapes in this frame *)
END;
PROCEDURE ( f : Frame ) InvertBlock* ( x, y, w, h : INTEGER );
BEGIN
INC ( x, f.x + f.orgX );
INC ( y, f.y + f.orgY );
IF x < f.x THEN
DEC ( w, f.x - x );
x := f.x
END;
IF x + w > f.x + f.w THEN
w := f.x + f.w - x
END;
IF y < f.y THEN
DEC ( h, f.y - y );
y := f.y
END;
IF y + h > f.y + f.h THEN
h := f.y + f.h - y
END;
IF ( w > 0 ) & ( h > 0 ) THEN
OS.InvertBlock ( x, y, w, h )
END
END InvertBlock;
PROCEDURE ( f : Frame ) Draw*;
BEGIN
OS.FadeCursor;
OS.EraseBlock ( f.x, f.y, f.w, f.h );
f.graphic.Draw ( f )
END Draw;
PROCEDURE ( f : Frame ) Modify* ( y : INTEGER );
BEGIN
f.Modify^ (y);
f.Draw
END Modify;
PROCEDURE ( f : Frame ) HandleMouse* ( x, y : INTEGER; buttons : SET );
VAR w, h, dx, dy: INTEGER;
obj : OS.Object;
s : Shapes0.Shape;
changed: BOOLEAN;
PROCEDURE Track( VAR x, y, w, h, dx, dy : INTEGER; VAR buttons : SET );
VAR b : SET;
x1, y1 : INTEGER;
BEGIN
REPEAT
OS.GetMouse ( b, x1 , y1 );
buttons := buttons + b;
OS.DrawCursor ( x1, y1 )
UNTIL b = {};
dx := x1 - x;
dy := y1 -y;
w := ABS(dx);
h := ABS(dy);
IF x1 < x THEN
x := x1
END;
IF y1 < y THEN
y := y1
END;
DEC ( x, f.x + f.orgX );
DEC ( y, f.y + f.orgY )
END Track;
BEGIN
changed := FALSE;
IF OS.left IN buttons THEN
Track ( x, y, w, h, dx, dy, buttons );
(* ----- generate new shape with type curShape *)
OS.NameToObj ( Shapes0.curShape, obj );
IF obj # NIL THEN
s := obj ( Shapes0.Shape );
s.SetBox ( x, y, w, h );
f.graphic.Insert ( s )
END
ELSIF OS.middle IN buttons THEN
Track ( x, y, w, h, dx, dy, buttons );
IF OS.left IN buttons THEN (* ----- MM+ML click: move selected figures *)
f.graphic.MoveSelected ( dx, dy )
ELSE (* ----- MM click: move origin *)
INC ( f.orgX, dx );
INC ( f.orgY, dy );
f.Draw
END
ELSIF OS.right IN buttons THEN
f.Neutralize;
Track ( x, y, w, h, dx, dy, buttons );
f.graphic.SetSelection ( x, y, w, h );
IF OS.left IN buttons THEN (* ----- MR+ML click: delete selected shapes *)
f.graphic.DeleteSelected
END
END
END HandleMouse;
PROCEDURE ( f: Frame ) Handle* ( VAR m : OS.Message);
BEGIN
WITH m : Shapes0.NotifyChangeMsg DO
IF f.graphic = m.g THEN
f.Draw
END
ELSE
END
END Handle;
PROCEDURE ( f : Frame ) Neutralize*;
BEGIN
f.graphic.Neutralize
END Neutralize;
PROCEDURE New* ( graphic : Shapes0.Graphic ) : Frame;
VAR f: Frame;
BEGIN
NEW ( f );
f.graphic := graphic;
f.orgX := 0;
f.orgY := 0;
RETURN f
END New;
PROCEDURE ( f : Frame ) Copy* (): Viewers0.Frame;
VAR f1 : Frame;
BEGIN
f1 := New ( f.graphic );
f1.orgX := f.orgX;
f1.orgY := f.orgY;
RETURN f1
END Copy;
END GraphicFrames0.
MODULE Draw0;
IMPORT OS, IO, Texts0, TextFrames0, Shapes0, GraphicFrames0, Viewers0;
PROCEDURE Open*;
VAR s : IO.Scanner;
v : Viewers0.Viewer;
menu : TextFrames0.Frame;
cont : GraphicFrames0.Frame;
file : OS.File;
r : OS.Rider;
g : Shapes0.Graphic;
BEGIN
s.SetToParameters;
s.Read;
IF s.class = IO.name THEN
menu := TextFrames0.NewMenu ( s.str,
"Viewers0.Close Viewers0.Copy Draw0.Store" );
NEW ( g );
Shapes0.InitGraphic ( g );
file := OS.OldFile ( s.str );
IF file # NIL THEN
OS.InitRider ( r );
r.Set ( file, 0 );
g.Load ( r )
END;
cont := GraphicFrames0.New ( g );
v := Viewers0.New ( menu, cont )
END
END Open;
PROCEDURE Store*;
VAR v : Viewers0.Viewer;
s : IO.Scanner;
file : OS.File;
r : OS.Rider;
BEGIN
v := Viewers0.ViewerAt ( TextFrames0.cmdFrame.y );
s.Set ( v.menu ( TextFrames0.Frame ).text, 0);
s.Read;
IF s.class = IO.name THEN
file := OS.NewFile ( s.str );
OS.InitRider ( r );
r.Set ( file, 0 );
v.cont ( GraphicFrames0.Frame ).graphic.Store ( r );
OS.Register ( file )
END
END Store;
END Draw0.
MODULE Rectangles0;
IMPORT OS, Viewers0, Shapes0, GraphicFrames0;
TYPE
Rectangle* = POINTER TO RectDesc;
RectDesc* = RECORD ( Shapes0.ShapeDesc )
x, y, w, h : INTEGER
END;
PROCEDURE ( r : Rectangle ) SetBox* ( x, y, w, h : INTEGER );
BEGIN
r.SetBox^ ( x, y, w, h );
r.x := x;
r.y := y;
r.w := w;
r.h := h
END SetBox;
PROCEDURE ( r: Rectangle ) Draw* ( f : Viewers0.Frame );
BEGIN
WITH f : GraphicFrames0.Frame DO
IF r.selected THEN
f.InvertBlock ( r.x, r.y, r.w, r.h )
ELSE
f.InvertBlock ( r.x, r.y, r.w, 1 );
f.InvertBlock ( r.x, r.y + r.h - 1, r.w, 1 );
f.InvertBlock ( r.x, r.y + 1, 1, r.h - 2 );
f.InvertBlock ( r.x + r.w - 1, r.y + 1, 1, r.h - 2 )
END
END
END Draw;
PROCEDURE ( r : Rectangle ) Move* ( dx, dy : INTEGER );
BEGIN
INC ( r.x, dx );
INC ( r.y, dy )
END Move;
PROCEDURE ( r : Rectangle ) SetSelection* ( x, y, w, h : INTEGER );
BEGIN
r.selected := ( r.x >= x ) & ( r.x + r.w <= x + w )
& ( r.y >= y ) & ( r.y + r.h <= y + h )
END SetSelection;
PROCEDURE ( r : Rectangle ) GetBox* ( VAR x, y, w, h : INTEGER );
BEGIN
x := r.x;
y := r.y;
w := r.w;
h := r.h
END GetBox;
PROCEDURE ( r : Rectangle ) Copy* () : Shapes0.Shape;
VAR r1 : Rectangle;
BEGIN
NEW ( r1 );
r1.selected := r.selected;
r1.x := r.x;
r1.y := r.y;
r1.w := r.w;
r1.h := r.h;
RETURN r1
END Copy;
PROCEDURE ( r : Rectangle ) Load* ( VAR R : OS.Rider );
BEGIN
R.ReadInt ( r.x );
R.ReadInt ( r.y );
R.ReadInt ( r.w );
R.ReadInt ( r.h )
END Load;
PROCEDURE ( r : Rectangle ) Store* ( VAR R : OS.Rider );
BEGIN
R.WriteInt ( r.x );
R.WriteInt ( r.y );
R.WriteInt ( r.w );
R.WriteInt ( r.h )
END Store;
PROCEDURE Set*;
BEGIN
Shapes0.curShape := "Rectangles0.RectDesc"
END Set;
END Rectangles0.
MODULE GraphicElems0;
IMPORT OS, Texts0, Shapes0, GraphicFrames0, TextFrames0, Viewers0;
TYPE
Element* = POINTER TO ElemDesc;
ElemDesc* = RECORD ( Texts0.ElemDesc )
orgX, orgY : INTEGER;
graphic : Shapes0.Graphic;
END;
UpdateFrame = POINTER TO UpdateFrameDesc;
UpdateFrameDesc = RECORD ( GraphicFrames0.FrameDesc)
text : Texts0.Text;
e : Element
END;
VAR f : GraphicFrames0.Frame;
(* - reused within a text frame whenever
a graphic element has to be redrawn *)
PROCEDURE ( e : Element ) Copy* () : Texts0.Element;
VAR res : Element;
BEGIN
NEW ( res );
res^ := e^;
res.graphic := e.graphic.Copy ();
RETURN res
END Copy;
PROCEDURE ( e : Element ) Draw* ( x, y : INTEGER );
BEGIN
f.x := x;
f.y := y;
f.w := e.w;
f.h := e.h;
f.orgX := e.orgX;
f.orgY := e.orgY;
f.graphic := e.graphic;
f.Draw
END Draw;
PROCEDURE ( e : Element ) HandleMouse* ( f : OS.Object; x, y: INTEGER);
VAR v : Viewers0.Viewer;
menu : TextFrames0.Frame;
cont : UpdateFrame;
buttons : SET;
BEGIN
REPEAT
OS.GetMouse ( buttons, x, y )
UNTIL buttons = {};
menu := TextFrames0.NewMenu ( "",
"Viewers0.Close Viewers0.Copy GraphicElems0.Update" );
NEW ( cont );
cont.graphic := e.graphic;
cont.orgX := e.orgX + 10;
cont.orgY := e.orgY + 10;
cont.text := f ( TextFrames0.Frame ).text;
cont.e := e;
v := Viewers0.New ( menu, cont )
END HandleMouse;
PROCEDURE ( e : Element) Load* ( VAR r : OS.Rider );
BEGIN
e.load^ ( r );
r.ReadInt ( e.orgX );
r.ReadInt ( e.orgY );
NEW ( e.graphic );
Shapes0.InitGraphic ( e.graphic );
e.graphic.load ( r )
END Load;
PROCEDURE ( e : Element ) Store* ( VAR r : OS.Rider );
BEGIN
e.Store^ (r);
r.WriteInt ( e.orgX );
r.WriteInt ( e.orgY );
e.graphic.Store ( r )
END Store;
PROCEDURE Insert*;
VAR e : Element;
f : TextFrames0.Frame;
BEGIN
IF Viewers0.focus # NIL THEN
f := Viewers0.focus ( TextFrames0.Frame );
IF ( f # NIL ) & ( f.caret.pos >= 0 ) THEN
NEW ( e );
e.w := 12;
e.h := 12;
e.dsc := 0;
NEW ( e.graphic );
Shapes0.InitGraphic ( e.graphic );
e.orgX := 0;
e.orgY := 0;
f.text.SetPos ( f.caret.pos );
f.text.WriteElem ( e )
END
END
END Insert;
PROCEDURE Update*;
VAR v : Viewers0.Viewer;
f : UpdateFrame;
e : Element;
m : Texts0.NotifyReplMsg;
x, y : INTEGER;
pos: LONGINT;
BEGIN
v := Viewers0.ViewerAt ( TextFrames0.cmdFrame.y );
f := v.cont ( UpdateFrame );
e := f.e;
pos := f.text.ElemPos ( e );
IF pos < f.text.len THEN
f.graphic.GetBox ( x, y, e.w, e.h );
e.graphic := f.graphic;
e.orgX := - x;
e.orgY := - y;
m.t := f.text;
m.beg := pos;
m.end := pos + 1;
Viewers0.Broadcast ( m )
END
END Update;
PROCEDURE Init;
VAR g : Shapes0.Graphic;
BEGIN
NEW ( g );
Shapes0.InitGraphic ( g );
f := GraphicFrames0.New ( g )
END Init;
BEGIN
Init
END GraphicElems0.