PISyntax10.Scn.FntSyntax10i.Scn.FntAStampElemsAlloc16 Oct 98BalloonElemsAlloc Syntax10.Scn.FntISyntax10i.Scn.Fnt/a q$   ("< KeplerElemsAllocKeplerGraphsGraphDescKeplerGraphsStarDesc|}}}}~~}~~}~}Kepler1RectangleDescKeplerFramesCaptionDescPanelSyntax10.Scn.FntKepler1AttrDescElemSyntax10.Scn.Fnt x, ySyntax10.Scn.Fnt Kepler1TextureDesc  G*[  B > fL),Y       6    }      j   6  % Z2  2 +"  9    7   0   B "! " GB9  5     6  D  C  0>   L      #) =G!'> = F"selected" is set to the state of a case if its elem is selected "Case" Case = POINTER TO CaseDesc; Is used to place a textelement into a panel. x, y are the relative coords in the panel measured from topleft to the bottomleft corner of the elem. oldW, oldH represent the previous size of the elem and are used for drawing. state holds information about the elem (e.g: selected). "CaseDesc" CaseDesc = RECORD (* Wrapper Pattern *) x, y : INTEGER; oldW, oldH : LONGINT; state : SET; e : Texts.Elem; next, prev : Case; END; see Elems.Case  "Selection" Selection = POINTER TO SelectionDesc; Is a list of all selected elements in a panel. "SelectionDesc" SelectionDesc = RECORD c : Case; END; see PanelElems.Selection "Panel" Panel = POINTER TO PanelDesc; Is a container which is able to hold several TextElements. TextElems can be placed in 2 Dimensions. border is indicates the size of the border around the panel (used for 3d effect). sel is the current selection of this panel. selTime stores the last time a selection was made. elemTxt is the Text where all Elements of the panel are placed in, as TextElements expect, that they are placed in text. If set TRUE showTabs lets all elements, knowing the TabStopMsg, display their TabStop position. "PanelDesc" PanelDesc = RECORD (Elems.ElemDesc) border : INTEGER; sel : Selection; lastSel : Selection; selTime : LONGINT; elemTxt- : Text; showTabs : BOOLEAN; END; see PanelElems.Panel "SelectionMsg" SelectionMsg = RECORD (Oberon.SelectionMsg) p : Panel; sel : Selection END extends Oberon.SelectionMsg to get a complete information about a selection inside a panel. "CopyOverMsg" CopyOverMsg = RECORD (Oberon.CopyOverMsg) p : Panel; sel : Selection END; extends Oberon.CopyOverMsg to copy selection inside a panel with correct position information "NotifyMsg" NotifyMsg = RECORD (Display.FrameMsg) p : Panel; id : SET; (* elem, refresh, clear, move *) elem : Texts.Elem; x, y, w, h : INTEGER; (* area *) END; Is sent to every Panel frame if changes on elems inside a panel occured. "TruncateFrame" PROCEDURE TruncateFrame(f : Frame; VAR X, Y, W, H : INTEGER); reduces the size of f so that the border of the his panel is outside the frame. The old size of f is returned in X, Y, W, H. see also SetFrameCoords "SetFrameCoords" PROCEDURE SetFrameCoords(f : Frame; X, Y, W, H : INTEGER); sets the coordinates of f to X, Y, W, H. In most cases used after a call to TruncateFrame to reset old size. "InArea" PROCEDURE InArea(ax, ay, aw, ah, x, y, w, h : INTEGER) : BOOLEAN; returns if the coordinates x, y, w, h are totally inside the area ax, ay, aw, ah. "OverlapsArea" PROCEDURE OverlapsArea(ax, ay, aw, ah, x, y, w, h : INTEGER) : BOOLEAN; returns if the coordinates x, y, w, h are overlapping the area ax, ay, aw, ah. "InBorder" PROCEDURE InBorder(mX, mY, x, y, w, h : INTEGER) : BOOLEAN; returns if the mouse mX, mY is inside the border of the area x, y, w, h. Elems.clp defines the size of the border. "InsertElem" PROCEDURE InsertElem(p : Panel; e : Texts.Elem; x, y : INTEGER; fnt : Fonts.Font; col : SHORTINT); inserts the elem e into p at position x, y with fnt and col. x, y are relativ inside the panel. "DeleteCase" PROCEDURE DeleteCase(p : Panel; c : Case); deletes c from p "MyCase" PROCEDURE MyCase(p : Panel; e : Texts.Elem) : Case; returns the case holding e in panel p. "ThisCase" PROCEDURE ThisCase(p : Panel; x, y, x0, y0 : INTEGER) : Case; returns the case placed at screen coordinates x, y in panel p. x0, y0 are the coordinates of the panel p. "AppendToSelection" PROCEDURE AppendToSelection(p : Panel; c : Case); appends c to the selection of panel p. "RemoveFromSelection" PROCEDURE RemoveFromSelection(p : Panel; c : Case); removes c from the selection of panel p. "RemoveSelection" PROCEDURE RemoveSelection(f : Frame); removes the selection of the underlying panel of the frame f. "DeleteSelection" PROCEDURE DeleteSelection(f : Frame); deletes the selection of the underlying panel of frame f. "NextSel" PROCEDURE NextSel(s : Selection) : Selection; returns the next selection of s. "SetCaret" PROCEDURE SetCaret(f : Frame; X, Y : INTEGER); sets the caret at X, Y, where X, Y are relativ to the frame f. "RemoveCaret" PROCEDURE RemoveCaret(f : Frame); removes the caret "UpdateArea" PROCEDURE UpdateArea(p : Panel; x, y, w, h : INTEGER; id : SET); broadcasts a NotifyMsg with the parameters given. "DrawTextElem" PROCEDURE DrawTextElem(p : Panel; e : Texts.Elem; f : Display.Frame; x0, y0 : INTEGER); draws the elem e placed in panel p. f is used as clipping window and is normally the frame of the panel p. x0, y0 are the coordinates of panel p. "PrintTextElem" PROCEDURE PrintTextElem(p : Panel; e : Texts.Elem; f : Display.Frame; x0, y0 : INTEGER); prints the elem e placed in panel p. f is used as clipping frame and is normally the frame of the panel p. x0, y0 are the coordinates of panel p. "DrawContents" PROCEDURE DrawContents(p : Panel; f : Display.Frame; x0, y0 : INTEGER); draws all elems contained in panel p. f is the clipping frame and is normally the frame of the panel p. x0, y0 are the coordinates of panel p. "OpenFrame" PROCEDURE OpenFrame(p : Panel; f : Display.Frame; handle : Display.Handler; x, y, w, h : INTEGER) : Display.Frame; returns a new frame used as subframe for panel p. normally called when a DisplayMsg occurs. "DrawPanel" PROCEDURE DrawPanel(p : Panel; x, y, color: INTEGER; f : Display.Frame); draws the panel p and its contents at absolute coordinates x, y with color inside frame f. "CopyPanel" PROCEDURE CopyPanel (from, to : Panel); creates a copy of from in to. "Handle" PROCEDURE Handle(p : Texts.Elem; VAR msg : Texts.ElemMsg); is the default handler for panels. "Init" PROCEDURE Init(p : Panel); initializes the panel p with default values. "New" PROCEDURE New; is the generator for a panel, used from the load pattern in Texts.Text "Insert" PROCEDURE Insert; inserts a new panel at the caret. "FrameHandle" PROCEDURE FrameHandle(f : Display.Frame; VAR msg : Display.FrameMsg); is the default handler for the frame of a panel.rpVersionElemsAllocBeg#Syntax10.Scn.FntWindows PowerMacWindowsWindows PowerMac+Syntax10i.Scn.Fnt (* PowerMac *) pVersionElemsAllocEndSyntax10b.Scn.Fnt  8FoldElemsNew      "     S T  6%   +,>          8;$  9 H =AMarkElemsAllocP1<88)88S w\'88 \"8U8 "28W8  28~8#Syntax10.Scn.Fnt left 88#Syntax10.Scn.Fnt inside 88#Syntax10.Scn.Fnt right 88#Syntax10.Scn.Fnt bottom 88#Syntax10.Scn.Fnt inside 88#Syntax10.Scn.Fnt top 88  *8h8 &Z O88 bi!-8#Syntax10.Scn.Fnt forward msg to all childs l8 װ788 A888 =V'18E8F88  A,8Y8#Syntax10.Scn.Fnt c is first but not only *8N8 A,8e8  &88 d  88 a%8n8  ,868 k58j8 ݾ$88 XG88 `8_8 ] 8sM8   8.8 `88@8' _RB88 o(88 L8!8 r88 g3%88 a8;8 `  8y8 y  8N8 ٠(8}  y8 (?88 k388 88 s=88 R28& 8#Syntax10.Scn.Fnt++ not already in area but overlaps the area =88#Syntax10.Scn.Fnt redraw elems inside the area 8C8 a,8`8   -88 9 <8I#r8k8 bF 8685R*A8#Syntax10.Scn.Fnt not visible :8"CP8 9<8(8#Syntax10.Scn.Fnt fully visible 8x8  28*8 Z^ _8p-`8 H 68D8 u 88#Syntax10.Scn.Fnt)) returns corresponding elem to e in dest 878 =o388o8\ T888 (c287888s8 88838 K+8~~8 ^5 88 L8>8 M8<8 Q18Ae38 oX5818 88 ?o :88P888d8 '38888g&q88)8#Syntax10.Scn.Fnt11 create TextFrames.TrackMsg from Oberon.InputMsg s88#Syntax10.Scn.Fnt try to resize this panel j88#Syntax10.Scn.Fnt panel not resized 8<8#Syntax10.Scn.Fnt!! no case hit and p has selection 8"8#Syntax10.Scn.Fnt case hit 8m) 8#Syntax10.Scn.Fnt)) panel not locked and case hit in border 8"8#Syntax10.Scn.Fnt in corner or not selected Z88#Syntax10.Scn.Fnt trackable /8=88878 +% 18[* `8MODULE PanelElems; (* CE  *)  (* Windows *) IMPORT Out, Elems, Texts, Display, TextFrames, Fonts, Oberon, Viewers, Input, GU := GUtils, Files, TextPrinter, MenuViewers; CONST ML = 2; MM = 1; MR = 0; DUnit = TextFrames.Unit; (** states of a case *) selected* = 1; (* notifyMsg id's *) refresh = 2; clear = 3; (* area *) TYPE Case* = POINTER TO CaseDesc; (* Wrapper *) CaseDesc* = RECORD x*, y* : INTEGER; oldW*, oldH* : LONGINT; state* : SET; e* : Texts.Elem; next*, prev* : Case; END; Selection* = POINTER TO SelectionDesc; SelectionDesc* = RECORD c* : Case; next : Selection END; Panel* = POINTER TO PanelDesc; Text* = POINTER TO TextDesc; TextDesc* = RECORD (Texts.TextDesc) base* : Panel; END; Frame* = POINTER TO FrameDesc; FrameDesc* = RECORD (Display.FrameDesc) p* : Panel; cx*, cy* : INTEGER; hasCar* : BOOLEAN; oldY* : INTEGER; (** to recognize a shift (MenuViewers.ModifyMsg is handled in TextFrames.Handle) *) x*, y* : INTEGER; (** botleftcorner of container; X,Y are the visible coords and used for clipping *) END; PanelDesc* = RECORD (Elems.ElemDesc) contents, last : Case; border* : INTEGER; sel* : Selection; lastSel : Selection; selTime* : LONGINT; elemTxt- : Text; (* elems are placed in *) showTabs* : BOOLEAN; (** temporary *) END; SelElem = POINTER TO SelElemDesc; (* holds selected elems in selection text *) SelElemDesc = RECORD (Texts.ElemDesc) e : Texts.Elem END; SelectionMsg* = RECORD (Oberon.SelectionMsg) p* : Panel; sel* : Selection END; CopyOverMsg* = RECORD (Oberon.CopyOverMsg) p* : Panel; sel* : Selection END; NotifyMsg* = RECORD (Display.FrameMsg) p* : Panel; id* : SET; (* elem, refresh, clear, move *) (*case : Case;*) elem* : Texts.Elem; x*, y*, w*, h* : INTEGER; (* area *) END;  VAR W : Texts.Writer; elemTxtFrame : TextFrames.Frame; (* for messages to old text elems *) clp : INTEGER; (* AUXILIARY *) PROCEDURE ^UpdateArea* (p : Panel; x, y, w, h : INTEGER; id : SET); PROCEDURE ^DrawTextElem* (p : Panel; c : Case; f : Display.Frame; x0, y0 : INTEGER); PROCEDURE ^FrameHandle* (f : Display.Frame; VAR msg : Display.FrameMsg); PROCEDURE ^Init* (p : Panel); PROCEDURE ^ToTop (p : Panel; c : Case); PROCEDURE NoNotify (t : Texts.Text; op : INTEGER; beg, end : LONGINT); END NoNotify; PROCEDURE MarkMenu (f : Display.Frame); VAR r : Texts.Reader; v : Viewers.Viewer; t : Texts.Text; ch: CHAR; BEGIN v := Viewers.This(f.X, f.Y); IF (v IS MenuViewers.Viewer) & (v.dsc IS TextFrames.Frame) & (f # v.dsc) THEN t := v.dsc(TextFrames.Frame).text; IF t.len > 0 THEN Texts.OpenReader(r, t, t.len - 1); Texts.Read(r, ch) ELSE ch := 0X END ; IF ch # "!" THEN Texts.SetFont(W, Fonts.Default); Texts.SetColor(W, 15); Texts.SetOffset(W, 0); Texts.Write(W, "!"); Texts.Append(t, W.buf) END END END MarkMenu; (* reduces coords of frame that the border isn't in frame; gives back old coords *) PROCEDURE TruncateFrame*(f : Frame; VAR X, Y, W, H : INTEGER);  VAR w, h, d : INTEGER; BEGIN IF f = NIL THEN RETURN END; w := SHORT(f.p.W DIV DUnit); h := SHORT(f.p.H DIV DUnit); X := f.X; Y := f.Y; W := f.W; H := f.H; d := f.x + f.p.border - f.X; IF d > 0 THEN INC(f.X, d); DEC(f.W, d) END; d := f.y + f.p.border - f.Y; IF d > 0 THEN INC(f.Y, d); DEC(f.H, d) END; d := f.X + f.W - (f.x + w - f.p.border); IF d > 0 THEN DEC(f.W, d) END; d := f.Y + f.H - (f.y + h - f.p.border); IF d > 0 THEN DEC(f.H, d) END; END TruncateFrame; PROCEDURE SetFrameCoords*(f : Frame; X, Y, W, H : INTEGER); BEGIN IF f # NIL THEN f.X := X; f.Y := Y; f.W := W; f.H := H END END SetFrameCoords; PROCEDURE InArea* (ax, ay, aw, ah, x, y, w, h : INTEGER) : BOOLEAN; BEGIN RETURN (y >= ay) & (y+h <= ay + ah) & (x >= ax) & (x + w <= ax + aw) END InArea; PROCEDURE OverlapsArea* (ax, ay, aw, ah, x, y, w, h : INTEGER) : BOOLEAN; VAR ax2, ay2, x2, y2 : INTEGER; BEGIN ax2 := ax + aw - 1; ay2 := ay + ah - 1; x2 := x + w - 1; y2 := y + h - 1; RETURN (((x <= ax) & (x2 >= ax)) OR ( (x >= ax) & (x2 <= ax2) ) OR ((x <= ax2) & (x2 >= ax2))) & (((y <= ay) & (y2 >= ay)) OR ((y >= ay) & (y2 <= ay2)) OR ((y <= ay2) & (y2 >= ay2))) END OverlapsArea; PROCEDURE InBorder* (mX, mY, x, y, w, h : INTEGER) : BOOLEAN; BEGIN RETURN (mX < x + clp) OR (mX > x + w - clp) OR (mY < y + clp) OR (mY > y + h - clp) END InBorder; PROCEDURE InsertElem* (p : Panel; e : Texts.Elem; x, y : INTEGER; fnt : Fonts.Font; col : SHORTINT); VAR c : Case; attr : Elems.AttrMsg; notifier : Texts.Notifier; BEGIN NEW(c); c.state := {}; c.oldH := -1; c.oldW := -1; c.prev := NIL; c.next := p.contents; IF p.contents # NIL THEN p.contents.prev := c ELSE p.last := c END; p.contents := c; (* get special set *) Elems.GetSet(e, Elems.special, c.state); attr.id := Elems.get; COPY("$#cstate", attr.name); e.handle(e, attr); IF Elems.Done THEN c.state := attr.set END; c.e := e; c.x := x; c.y := y; c.oldH := -1; notifier := p.elemTxt.notify; p.elemTxt.notify := NoNotify; Texts.SetFont(W, fnt); Texts.SetColor(W, col); Texts.WriteElem(W, e); Texts.Append(p.elemTxt, W.buf); p.elemTxt.notify := notifier END InsertElem; PROCEDURE ToChilds(p : Panel; VAR msg : Texts.ElemMsg); VAR c : Case; BEGIN c := p.contents; WHILE c # NIL DO c.e.handle(c.e, msg); c := c.next END END ToChilds; PROCEDURE NotifySubFrames(f : Frame; VAR msg : Display.FrameMsg); VAR sub : Display.Frame; BEGIN sub := f.dsc; WHILE sub # NIL DO sub.handle(sub, msg); sub := sub.next END END NotifySubFrames; PROCEDURE ThisSubFrame(f : Frame; x, y : INTEGER) : Display.Frame; VAR sub : Display.Frame; done : BOOLEAN; BEGIN sub :=f.dsc; done := FALSE; WHILE (sub # NIL) & ~done DO done := (sub.X <= x) & (sub.X + sub.W >= x) & (sub.Y <= y) & (sub.Y + sub.H >= y); IF ~done THEN sub := sub.next END END; RETURN sub END ThisSubFrame; PROCEDURE RemoveSubFrames(f : Frame; x, y, w, h : INTEGER); VAR p, s : Display.Frame; msg : MenuViewers.ModifyMsg; PROCEDURE Overlaps () : BOOLEAN; BEGIN (* RETURN ((((s.X >= x) & (s.X <= x + w - 1)) OR ((s.X + s.W - 1 >= x) & (s.X + s.W <= x + w)) OR ((s.X <= x) & (s.X + s.W >= x + w))) & (((s.Y <= y) & (s.Y + s.H >= y + h - 1)) OR ((s.Y + s.H >= y) & (s.Y + s.H <= y + h)) OR ((s.Y >= y) & (s.Y < y + h)))) *) RETURN OverlapsArea(x, y, w, h, s.X, s.Y, s.W, s.H); END Overlaps; BEGIN s := f.dsc; IF s # NIL THEN p := s; s := p.next END ; WHILE s # NIL DO IF Overlaps() THEN p.next := s.next; msg.id := MenuViewers.reduce; msg.dY := 0; msg.Y := f.Y; msg.H := 0; s.handle(s, msg) ELSE p := s END ; s := p.next END ; s := f.dsc; IF (s # NIL) & Overlaps() THEN f.dsc := f.dsc.next; msg.id := MenuViewers.reduce; msg.dY := 0; msg.Y := f.Y; msg.H := 0; s.handle(s, msg) END END RemoveSubFrames; (* CASE *) PROCEDURE ToBottom (p : Panel; c : Case); VAR cc : Case; BEGIN IF (p.contents.next = NIL) OR (c.next = NIL) THEN RETURN END; IF (p.contents = c) & (p.contents.next # NIL) THEN p.contents := p.contents.next; p.contents.prev := NIL; cc := p.contents ELSE cc := p.contents; WHILE cc.next # c DO cc := cc.next END; cc.next := c.next; IF c.next # NIL THEN c.next.prev := cc END END; WHILE cc.next # NIL DO cc := cc.next END; cc.next := c; c.next := NIL; c.prev := cc; p.last := c END ToBottom; PROCEDURE ToTop (p : Panel; c : Case); VAR cc : Case; BEGIN IF Elems.back IN c.state THEN ToBottom(p, c) ELSE IF p.contents = c THEN RETURN END; cc := p.contents; WHILE cc.next # c DO cc := cc.next END; cc.next := c.next; IF c.next # NIL THEN c.next.prev := cc ELSE p.last := c.prev END; c.next := p.contents; p.contents.prev := c; p.contents := c; c.prev := NIL END END ToTop; PROCEDURE DeleteElem(p : Panel; e : Texts.Elem); VAR pos : LONGINT; notifier : Texts.Notifier; BEGIN notifier := p.elemTxt.notify; p.elemTxt.notify := NoNotify; pos := Texts.ElemPos(e); Texts.Delete(p.elemTxt, pos, pos + 1); p.elemTxt.notify := notifier END DeleteElem; PROCEDURE DeleteCase* (p : Panel; c : Case); VAR help : Case; BEGIN IF p.contents = c THEN p.contents := p.contents.next; IF p.contents # NIL THEN p.contents.prev := NIL ELSE p.last := NIL END; DeleteElem(p, c.e) ELSE help := p.contents; WHILE (help # NIL) & (help.next # c) DO help := help.next END; IF help # NIL THEN help.next := c.next; IF c.next # NIL THEN c.next.prev := help ELSE p.last := c.prev END; DeleteElem(p, c.e) END END END DeleteCase; PROCEDURE MyCase* (p : Panel; e : Texts.Elem) : Case;  VAR c : Case; BEGIN c := p.contents; WHILE (c # NIL) & (c.e # e) DO c := c.next END; RETURN c END MyCase; PROCEDURE ThisCase* (p : Panel; x, y, x0, y0 : INTEGER) : Case; VAR c : Case; rx, ry, h : INTEGER; BEGIN h := SHORT(p.H DIV DUnit); rx := x - x0; ry := y0 + h - y; c := p.contents; WHILE (c # NIL) & ~(((rx >= c.x) & (rx <= c.x + SHORT(c.e.W DIV DUnit))) & ((ry >= c.y - SHORT(c.e.H DIV DUnit)) & (ry <= c.y))) DO c := c.next END; RETURN c END ThisCase; PROCEDURE ThisElem(t : Texts.Text; pos : LONGINT) : Texts.Elem; VAR r : Texts.Reader; BEGIN Texts.OpenReader(r, t, pos); Texts.ReadElem(r); RETURN r.elem END ThisElem; (*SELECTION *) PROCEDURE InvertSelected(f : Frame; c : Case); VAR w, h : INTEGER; BEGIN w := SHORT(c.e.W DIV DUnit); h := SHORT(c.e.H DIV DUnit); GU.ReplConst(f, 15, f.x + c.x, f.y + SHORT(f.p.H DIV DUnit) - c.y, w, h, Display.invert) END InvertSelected; PROCEDURE AppendToSelection* (p : Panel; c : Case); VAR s : Selection; BEGIN NEW(s); s.c := c; s.next := NIL; IF p.sel = NIL THEN p.sel := s ELSE p.lastSel.next := s END; p.lastSel := s END AppendToSelection; PROCEDURE RemoveFromSelection* (p : Panel; c : Case); VAR s : Selection; BEGIN IF p.sel = NIL THEN RETURN END; IF p.sel.c = c THEN p.sel := p.sel.next; IF p.sel = NIL THEN p.lastSel := NIL END ELSE s := p.sel; WHILE (s.next # NIL) & (s.next.c # c) DO s := s.next END; IF s.next # NIL THEN s.next := s.next.next; IF s.next = NIL THEN p.lastSel := s END END END END RemoveFromSelection; PROCEDURE RemoveSelection* (f : Frame); VAR s : Selection; BEGIN s := f.p.sel; WHILE s # NIL DO EXCL(s.c.state, selected); Elems.UpdateElem(s.c.e); (* slow => find a better way *) s := s.next END; f.p.sel := NIL; f.p.lastSel := NIL END RemoveSelection; PROCEDURE DeleteSelection* (f : Frame); VAR s : Selection; ew, eh : INTEGER; BEGIN s := f.p.sel; WHILE s # NIL DO DeleteCase(f.p, s.c); ew := SHORT(s.c.e.W DIV DUnit); eh := SHORT(s.c.e.H DIV DUnit); UpdateArea(f.p, s.c.x, s.c.y, ew, eh, {clear, refresh}); s := s.next END; f.p.sel := NIL; f.p.lastSel := NIL END DeleteSelection; PROCEDURE SelElemHandle (s : Texts.Elem; VAR msg : Texts.ElemMsg); BEGIN s(SelElem).e.handle(s(SelElem).e, msg) END SelElemHandle; (* forwards changes to original text *) PROCEDURE SelTextNotifier(t : Texts.Text; op : INTEGER; beg, end : LONGINT); VAR r : Texts.Reader; pos : LONGINT; update : TextFrames.UpdateMsg; BEGIN WITH t : Text DO IF op = Texts.replace THEN Texts.OpenReader(r, t, 0); Texts.ReadElem(r); WHILE ~r.eot DO pos := Texts.ElemPos(r.elem(SelElem).e); Texts.ChangeLooks(t.base.elemTxt, pos, pos + 1 , {0,1,2}, r.fnt, r.col, r.voff); Texts.ReadElem(r); END; ELSIF op = Texts.delete THEN (* Clipboard.Cut was used *) update.id := TextFrames.delete; update.text := t.base.elemTxt; Viewers.Broadcast(update) ELSE END END END SelTextNotifier; PROCEDURE SelectionToText(f : Frame) : Texts.Text; VAR s : Selection; sel : SelElem; col : SHORTINT; fnt : Fonts.Font; t : Text; BEGIN NEW(t); Texts.Open(t, ""); t.notify := SelTextNotifier; t.base := f.p; s := f.p.sel; WHILE s # NIL DO NEW(sel); sel.handle := SelElemHandle; sel.e := s.c.e; Elems.GetLook(sel.e, fnt, col); Texts.SetFont(W, fnt); Texts.SetColor(W, col); Texts.WriteElem(W, sel); Texts.Append(t, W.buf); s := s.next END; RETURN t END SelectionToText; PROCEDURE NextSel* (s : Selection) : Selection; BEGIN RETURN s.next END NextSel; PROCEDURE SendSelection (f : Frame); VAR msg : CopyOverMsg; BEGIN IF f.p.sel # NIL THEN msg.text := SelectionToText(f); msg.beg := 0; msg.end := msg.text.len; msg.p := f.p; msg.sel := f.p.sel; Viewers.Broadcast(msg) END END SendSelection; PROCEDURE InvertCaret (f : Frame);  VAR x, y : INTEGER; BEGIN x := f.cx + f.X; y := f.Y + f.H - f.cy; GU.ReplConst(f, 15, x - 5, y, 11, 1, Display.invert); GU.ReplConst(f, 15, x, y - 5, 1, 11, Display.invert); END InvertCaret; PROCEDURE ToggleCaret(f : Frame); BEGIN IF f.hasCar THEN InvertCaret(f) END END ToggleCaret; PROCEDURE SetCaret* (f : Frame; X, Y : INTEGER); (** x, y are relative coords *) BEGIN Oberon.PassFocus(Viewers.This(f.X, f.Y)); f.cx := X; f.cy := Y; f.hasCar := TRUE; InvertCaret(f) END SetCaret; PROCEDURE RemoveCaret* (f : Frame); BEGIN IF f.hasCar THEN InvertCaret(f); f.hasCar := FALSE END END RemoveCaret; PROCEDURE SelToPanel (f : Frame; sel : Selection); VAR s : Selection; copy : Texts.CopyMsg; fnt : Fonts.Font; col : SHORTINT; sx, sy, px, py : INTEGER; c : Case; BEGIN (* calc absolute block coords *) sx := MAX(INTEGER); sy := MIN(INTEGER); s := sel; WHILE s # NIL DO IF s.c.x < sx THEN sx := s.c.x END; IF s.c.y > sy THEN sy := s.c.y END; s := s.next END; (* insert *) px := f.cx; py := f.cy; s := sel; WHILE s # NIL DO Elems.GetLook(s.c.e, fnt, col); copy.e := NIL; s.c.e.handle(s.c.e, copy); InsertElem(f.p, copy.e, px + (s.c.x - sx), py - (sy - s.c.y), fnt, col); Elems.UpdateElem(copy.e); c := MyCase(f.p, copy.e); c.state := s.c.state; EXCL(c.state, selected); s := s.next END; SetCaret(f, f.cx, f.cy) END SelToPanel; PROCEDURE TextSelToPanel (f : Frame; t : Texts.Text; beg, end : LONGINT); VAR r : Texts.Reader; copy : Texts.CopyMsg; fnt : Fonts.Font; col : SHORTINT; x, y : INTEGER; BEGIN x := f.cx; y := f.cy; RemoveCaret(f); Texts.OpenReader(r, t, beg); Texts.ReadElem(r); WHILE (Texts.Pos(r) <= end) & (r.elem # NIL) DO Elems.GetLook(r.elem, fnt, col); copy.e := NIL; r.elem.handle(r.elem, copy); InsertElem(f.p, copy.e, x, y, fnt, col); INC(x, SHORT(copy.e.W DIV DUnit) + 2); Elems.UpdateElem(copy.e); Texts.ReadElem(r) END; SetCaret(f, x, y) END TextSelToPanel; PROCEDURE CopyOver (f : Frame; VAR msg : Oberon.CopyOverMsg); BEGIN IF msg IS CopyOverMsg THEN SelToPanel(f, msg(CopyOverMsg).sel) ELSE TextSelToPanel (f, msg.text, msg.beg, msg.end) END END CopyOver; PROCEDURE CopyInto (f : Frame); VAR msg : SelectionMsg; BEGIN msg.p := NIL; msg.sel := NIL; msg.text := NIL; msg.time := 0; Viewers.Broadcast(msg); IF (msg.sel = NIL) & (msg.text # NIL) THEN TextSelToPanel(f, msg.text, msg.beg, msg.end) ELSIF msg.sel # NIL THEN SelToPanel(f, msg.sel) END END CopyInto; PROCEDURE FullyVisible (f : Frame; c : Case; w, h : INTEGER) : BOOLEAN; BEGIN RETURN (c.x >= f.X - f.x) & (c.x + w <= f.X + f.W - f.x) & (GU.Unit(f.p.H, TRUE) - c.y >= f.Y - f.y) & (c.y - h >= f.y + GU.Unit(f.p.H, TRUE) - (f.Y + f.H)) END FullyVisible; PROCEDURE RefreshArea (f : Frame; VAR x, y, w, h : INTEGER); VAR c : Case; done, overlaps : BOOLEAN; ew, eh, X, Y, W, H : INTEGER; msg : Elems.OverlapMsg; BEGIN done := FALSE; WHILE ~done DO done := TRUE; c := f.p.contents; WHILE (c # NIL) & done DO ew := SHORT(c.e.W DIV DUnit); eh := SHORT(c.e.H DIV DUnit); IF Elems.overlap IN c.state THEN (* has its own overlap check *) msg.x := x; msg.y := f.Y - y; msg.w := w; msg.h := h; msg.ex := c.x; msg.ey := f.Y - c.y; msg.ew := ew; msg.eh := eh; c.e.handle(c.e, msg); overlaps := msg.overlaps ELSE overlaps := OverlapsArea(x, f.Y - y, w, h, c.x, f.Y - c.y, ew, eh) END; IF ~InArea(x, f.Y - y, w, h, c.x, f.Y - c.y, ew, eh) & overlaps  THEN done := FALSE; IF (c.x < x) THEN w := w + x - c.x; x := c.x END; IF (c.x + ew > x + w) THEN w := w + c.x + ew - (x + w) END; IF (c.y > y) THEN h := h + c.y - y; y := c.y END; IF (c.y - eh < y - h) THEN h := h + (y - h - (c.y - eh)) END; END; c := c.next END END; ToggleCaret(f); Oberon.RemoveMarks(f.x + x, f.Y + f.H - y, w, h); RemoveSubFrames(f, f.x + x, f.Y + f.H - y, w, h); TruncateFrame(f, X, Y, W, H); c := f.p.last; WHILE c # NIL DO ew := SHORT(c.e.W DIV DUnit); eh := SHORT(c.e.H DIV DUnit); IF InArea(x, f.Y - y, w, h, c.x, f.Y - c.y, ew, eh) THEN DrawTextElem(f.p, c, f, f.x, f.y) END; c := c.prev END; SetFrameCoords(f, X, Y, W, H); ToggleCaret(f); END RefreshArea; PROCEDURE ClearArea (f : Frame; x, y, w, h : INTEGER); VAR fnt : Fonts.Font; color : SHORTINT; X, Y, W, H : INTEGER; BEGIN ToggleCaret(f); x := x + f.x; y := f.y + SHORT(f.p.H DIV DUnit) - y; Oberon.RemoveMarks(x, y, w, h); Elems.GetLook(f.p, fnt, color); TruncateFrame(f, X, Y, W, H); GU.ReplConst(f, color, x, y, w, h, Display.paint); SetFrameCoords(f, X, Y, W, H); ToggleCaret(f); END ClearArea; (* Panel *) PROCEDURE UpdateArea* (p : Panel; x, y, w, h : INTEGER; id : SET); VAR u : NotifyMsg; BEGIN u.id := id; u.p:= p; u.x := x; u.y := y; u.w := w; u.h := h; Viewers.Broadcast(u) END UpdateArea; (** relative coords *) PROCEDURE DrawTextElem* (p : Panel; c : Case; f : Display.Frame; x0, y0 : INTEGER); VAR msg : TextFrames.DisplayMsg; w, h, wp, hp : INTEGER; PROCEDURE TabNo() : INTEGER; VAR r : Texts.Reader; i : INTEGER; tab : Elems.TabStopMsg; BEGIN c.e.handle(c.e, tab); IF ~tab.accepted THEN RETURN 0 END; i := 1; Texts.OpenReader(r, p.elemTxt, 0); Texts.ReadElem(r); WHILE r.elem # c.e DO tab.accepted := FALSE; r.elem.handle(r.elem, tab); IF tab.accepted THEN INC(i) END; Texts.ReadElem(r) END; RETURN i END TabNo; PROCEDURE ShowTabNo; VAR i : INTEGER; s : ARRAY 4 OF CHAR; BEGIN s := ""; i := TabNo(); IF i > 0 THEN Elems.IntToString(i, s); GU.ReplConst(f, 15, x0 + c.x, y0 + hp - c.y, w, h, Display.paint); GU.String(f, s, x0 + c.x + 1, y0 + hp - c.y + 1, w - 1, Fonts.Default, 0, Display.paint, GU.left) END END ShowTabNo; BEGIN wp := GU.Unit(p.W, TRUE); hp := GU.Unit(p.H, TRUE); Elems.GetLook(c.e, msg.fnt, msg.col); msg.X0 := c.x + x0; msg.pos := Texts.ElemPos(c.e); msg.prepare := TRUE; c.e.handle(c.e, msg); IF (c.oldH > -1) & (c.oldH # c.e.H) THEN (* adjust y coord *) c.y := c.y + SHORT((c.e.H - c.oldH) DIV DUnit); (*INC(c.y, SHORT((c.e.H - c.oldH) DIV DUnit));*) (* PowerMac doesn't change c.y *) END; c.oldW := c.e.W; c.oldH := c.e.H; (* store new expansion *) w := SHORT(c.e.W DIV DUnit); h := SHORT(c.e.H DIV DUnit); IF(c.x + w < 0) OR (c.x > wp) OR (c.y < 0) OR (c.y - h > hp) THEN RETURN END; (* add: checking of other cases *) IF ~(c.e IS Elems.Elem) & ~FullyVisible(f(Frame), c, w, h) THEN (* draw dummy *) GU.ReplPattern(f, 15, Display.grey1, x0 + c.x, y0 + hp - c.y, w, h, 0, 0, Display.paint); RETURN END; IF c.e IS Elems.Elem THEN msg.frame := f ELSE msg.frame := elemTxtFrame; msg.frame.X := x0; msg.frame.Y := y0; msg.frame.W := wp; msg.frame.H := hp END; msg.prepare := FALSE; msg.elemFrame := NIL; msg.Y0 := y0 + hp - c.y; IF (TabNo() = 0) OR ~p.showTabs THEN c.e.handle(c.e, msg) END; IF p.showTabs THEN ShowTabNo() END; IF selected IN c.state THEN InvertSelected(f(Frame), c) END; IF msg.elemFrame # NIL THEN (* new sub frame *) msg.elemFrame.next := f.dsc; f.dsc := msg.elemFrame END; END DrawTextElem; PROCEDURE PrintTextElem* (p : Panel; c : Case; f : Display.Frame; x0, y0 : INTEGER); VAR msg : TextPrinter.PrintMsg; w, h, hp : INTEGER; BEGIN GU.SetDevice(GU.printer); hp := GU.Unit(p.H, TRUE); Elems.GetLook(c.e, msg.fnt, msg.col); msg.X0 := GU.Unit(c.x, FALSE) + x0; msg.prepare := TRUE; c.e.handle(c.e, msg); w := GU.Unit(c.e.W, TRUE); h := GU.Unit(c.e.H, TRUE); IF (GU.Unit(c.x, FALSE) >= GU.Unit(p.border, FALSE)) & (GU.Unit(c.x, FALSE) + w <= GU.Unit(p.W, TRUE) - GU.Unit(p.border, FALSE)) & (GU.Unit(c.y, FALSE) <= GU.Unit(p.H, TRUE) - GU.Unit(p.border, FALSE)) & (GU.Unit(c.y, FALSE) - h >= GU.Unit(p.border, FALSE)) THEN msg.prepare := FALSE; msg.Y0 := y0 + hp - GU.Unit(c.y, FALSE); c.e.handle(c.e, msg) END END PrintTextElem; PROCEDURE DrawContents* (p : Panel; f : Display.Frame; x0, y0 : INTEGER); VAR dr : PROCEDURE (p : Panel; c : Case; f : Display.Frame; x0, y0 : INTEGER); c : Case; BEGIN IF GU.device = GU.printer THEN dr := PrintTextElem ELSE dr := DrawTextElem END; c := p.last; WHILE c # NIL DO EXCL(c.state, selected); dr(p, c, f, x0, y0); c := c.prev END END DrawContents; PROCEDURE OpenFrame*(p : Panel; f : Display.Frame; handle : Display.Handler; x, y, w, h : INTEGER) : Display.Frame; VAR ef : Frame; BEGIN NEW(ef); ef.p := p; ef.X := x; ef.Y := y; ef.W := w; ef.H := h; ef.x := x; ef.y := y; (* original fixed coords used for tracking *) ef.handle := handle; Elems.AdjustSubFrame(f, ef); ef.oldY := ef.Y; RETURN ef END OpenFrame; PROCEDURE DrawPanel* (p : Panel; x, y, color: INTEGER; f : Display.Frame); VAR h, w, X, Y, W, H : INTEGER; BEGIN h := GU.Unit(p.H, TRUE); w := GU.Unit(p.W, TRUE); GU.Area(f, color, 12, x, y, w, h, GU.Unit(p.border, FALSE), TRUE, FALSE); IF f # NIL THEN TruncateFrame(f(Frame), X, Y, W, H) END; DrawContents(p, f, x, y); IF f # NIL THEN SetFrameCoords(f(Frame), X, Y, W, H) END END DrawPanel; PROCEDURE CopyPanel* (from, to : Panel); VAR newC, c : Case; notifier : Texts.Notifier; B : Texts.Buffer; PROCEDURE RefElem(e : Texts.Elem; src, dest : Texts.Text) : Texts.Elem; VAR rs, rd : Texts.Reader; BEGIN Texts.OpenReader(rs, src, 0); Texts.OpenReader(rd, dest, 0); Texts.ReadElem(rs); Texts.ReadElem(rd); WHILE rs.elem # e DO Texts.ReadElem(rs); Texts.ReadElem(rd) END; RETURN rd.elem END RefElem; BEGIN NEW(B); Texts.OpenBuf(B); Init(to); Elems.CopyElem(from, to); to.border := from.border; to.showTabs := from.showTabs; notifier := to.elemTxt.notify; to.elemTxt.notify := NoNotify; Texts.Save(from.elemTxt, 0, from.elemTxt.len, B); Texts.Append(to.elemTxt, B); to.elemTxt.notify := notifier; (* copy contents *) c := from.contents; WHILE c # NIL DO NEW(newC); newC^ := c^; newC.e := RefElem(c.e, from.elemTxt, to.elemTxt); IF to.contents = NIL THEN to.contents := newC ELSE to.last.next := newC; newC.prev := to.last END; to.last := newC; c := c.next END END CopyPanel; PROCEDURE HandleFileMsg (p : Panel; VAR msg : Texts.FileMsg); VAR c : Case; cnt, pos : LONGINT; ch : CHAR; notifier : Texts.Notifier; BEGIN IF msg.id = Texts.load THEN notifier := p.elemTxt.notify; p.elemTxt.notify := NoNotify; Files.Read(msg.r, ch); (* version -> not used yet *) Texts.Load(msg.r, p.elemTxt); Files.ReadLInt(msg.r, cnt); WHILE cnt > 0 DO NEW(c); Files.ReadInt(msg.r, c.x); Files.ReadInt(msg.r, c.y); Files.ReadSet(msg.r, c.state); EXCL(c.state, selected); Files.ReadLInt(msg.r, pos); c.e := ThisElem(p.elemTxt, pos); c.oldW := c.e.W; c.oldH := c.e.H; IF p.contents = NIL THEN p.contents := c ELSE p.last.next := c; c.prev := p.last END; p.last := c; DEC(cnt) END; (* Files.ReadBool(msg.r, eof); p.contents := NIL; last := NIL; WHILE ~eof DO NEW(c); LoadCase(msg, c, fnt, col); c.next := NIL; IF p.contents = NIL THEN p.contents := c ELSE last.next := c END; last := c; Texts.SetFont(W, fnt); Texts.SetColor(W, col); Texts.WriteElem(W, c.e); Texts.Append(p.elemTxt, W.buf); Files.ReadBool(msg.r, eof) END; *) p.elemTxt.notify := notifier; ELSIF msg.id = Texts.store THEN Files.Write(msg.r, 0X); (* version *) Texts.Store(msg.r, p.elemTxt); cnt := 0; c := p.contents; WHILE c # NIL DO INC(cnt); c := c.next END; Files.WriteLInt(msg.r, cnt); c := p.contents; WHILE c # NIL DO Files.WriteInt(msg.r, c.x); Files.WriteInt(msg.r, c.y); Files.WriteSet(msg.r, c.state); Files.WriteLInt(msg.r, Texts.ElemPos(c.e)); c := c.next END; (* Files.WriteBool(msg.r, p.contents = NIL); c := p.contents; WHILE c # NIL DO StoreCase(msg, c); Files.WriteBool(msg.r, c.next = NIL); c := c.next END*) END END HandleFileMsg; PROCEDURE HandleAttrMsg(p : Panel; VAR msg : Elems.AttrMsg); BEGIN Elems.Done := TRUE; IF msg.id = Elems.set THEN IF (msg.name = "Locked") THEN IF msg.class = Elems.Bool THEN ToChilds(p, msg) END; Elems.Handle(p, msg) ELSIF msg.name = "Border" THEN IF msg.class = Elems.Int THEN p.border := SHORT(msg.i) END ELSE Elems.Handle(p, msg) END ELSIF msg.id = Elems.get THEN IF msg.name = "Border" THEN msg.class := Elems.Int; msg.i := p.border ELSE Elems.Handle(p, msg) END ELSIF msg.id = Elems.enum THEN Elems.Handle(p, msg); msg.enum("Border", Elems.Int) ELSE Elems.Handle(p, msg) END END HandleAttrMsg; PROCEDURE Handle* (p : Texts.Elem; VAR msg : Texts.ElemMsg); VAR copy : Panel; exec : Elems.ExecMsg; cmd : ARRAY 256 OF CHAR; BEGIN WITH p : Panel DO WITH msg : TextFrames.TrackMsg DO (* send to his frame *) Elems.ToFrame(p, msg) | msg : TextFrames.DisplayMsg DO IF ~msg.prepare THEN p.sel := NIL; msg.elemFrame := OpenFrame(p, msg.frame, FrameHandle, msg.X0, msg.Y0, SHORT(p.W DIV DUnit), SHORT(p.H DIV DUnit)); DrawPanel(p, msg.X0, msg.Y0, msg.col, msg.elemFrame) END | msg : TextPrinter.PrintMsg DO IF ~msg.prepare THEN GU.SetDevice(GU.printer); DrawPanel(p, msg.X0, msg.Y0, msg.col, NIL); GU.SetDevice(GU.display) END | msg : Texts.CopyMsg DO IF msg.e = NIL THEN NEW(copy); msg.e := copy ELSE copy := msg.e(Panel) END; CopyPanel(p, copy) | msg : Elems.ExecMsg DO Elems.GetString(p, "Cmd", cmd); IF cmd[0] # 0X THEN Elems.CmdContext := p.elemTxt; Elems.CmdElem := NIL; Elems.DoCall(cmd, NIL, NIL, NIL, 0, FALSE) END; | msg : Texts.FileMsg DO Elems.Handle(p, msg); HandleFileMsg(p, msg); IF msg.id = Texts.load THEN (* execute init cmd *) exec.e := p; exec.x := -1; exec.y := -1; exec.f := NIL; exec.unload := FALSE; exec.context := p.elemTxt; p.handle(p, exec) END | msg : TextFrames.FocusMsg DO Elems.Focusing(msg) | msg : Texts.IdentifyMsg DO msg.mod := "PanelElems"; msg.proc := "New" | msg : Elems.AttrMsg DO HandleAttrMsg(p, msg) ELSE Elems.Handle(p, msg) END END END Handle; PROCEDURE Init* (p : Panel); BEGIN Elems.Init(p); p.handle := Handle; p.border := 2; p.W := 200 * DUnit; p.H := 100 * DUnit; p.contents := NIL; p.last := NIL; p.showTabs := FALSE; NEW(p.elemTxt); Texts.Open(p.elemTxt, ""); p.elemTxt.notify := TextFrames.NotifyDisplay; p.elemTxt.base := p; END Init; PROCEDURE New*;  VAR p : Panel; BEGIN NEW(p); Init(p); Texts.new := p END New; PROCEDURE Insert*; VAR p: Panel; m: TextFrames.InsertElemMsg; pos : LONGINT; t : Texts.Text; s : Texts.Scanner; BEGIN NEW(p); Init(p); Elems.GetPar(p, s); m.e := p; Viewers.Broadcast(m); t := Texts.ElemBase(p); IF t # NIL THEN pos := Texts.ElemPos(p); Texts.ChangeLooks(t, pos, pos + 1, {1}, Fonts.Default, 13, 0) END END Insert; PROCEDURE ChangePos(f : Frame; c : Case; dx, dy : INTEGER); VAR s : Selection; x, y, h, w : INTEGER; BEGIN IF c = NIL THEN (* the selection *) s := f.p.sel; WHILE s # NIL DO ChangePos(f, s.c, dx, dy); s := s.next END ELSE h := SHORT(c.e.H DIV DUnit); w := SHORT(c.e.W DIV DUnit); x := c.x; y := c.y; ToTop(f.p, c); c.x := c.x + dx; c.y := c.y + dy; UpdateArea(f.p, x, y, w, h, {clear, refresh}); IF ~OverlapsArea(x, y, w, h, c.x, c.y, w, h) THEN Elems.UpdateElem(c.e) END (* ELSE Elem would be drawn twice (!! subframes) *) END END ChangePos; PROCEDURE InvertMoving (f : Frame; c : Case; dx, dy : INTEGER); VAR s : Selection; BEGIN IF c = NIL THEN s := f.p.sel; WHILE s # NIL DO InvertMoving (f, s.c, dx, dy); s := s.next END ELSE GU.Frame(NIL, 15, f.x + c.x + dx, f.y + SHORT(f.p.H DIV DUnit) - c.y - dy, SHORT(c.e.W DIV DUnit), SHORT(c.e.H DIV DUnit), 1, Display.invert) END END InvertMoving; PROCEDURE Move (f : Frame; c : Case); VAR keys, keysum : SET; begX, begY, dx, dy, X, Y, ox, oy : INTEGER; BEGIN Input.Mouse(keys, begX, begY); keysum := {}; dx := 0; dy := 0; ox := -1; oy := -1; InvertMoving(f, c, dx, dy); REPEAT Input.Mouse(keys, X, Y); keysum := keysum + keys; Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, X, Y); IF (ox # X) OR (oy # Y) THEN InvertMoving(f, c, dx, dy); dx := X - begX; dy := begY - Y; ox := X; oy := Y; InvertMoving(f, c, dx, dy) END UNTIL (keys = {}) OR (MR IN keysum); InvertMoving(f, c, dx, dy); WHILE keys # {} DO Input.Mouse(keys, X, Y) END; IF ~(MR IN keysum) THEN ChangePos(f, c, dx, dy) END END Move; PROCEDURE TrackRubberBand (f : Frame; ix, iy : INTEGER; keys : SET); VAR x, y, w, h, ox, oy, mx, my, ph : INTEGER; keysum : SET; VAR c : Case; PROCEDURE CorrectCoords(VAR coord, exp : INTEGER); BEGIN IF exp < 0 THEN INC(coord, exp); exp := ABS(exp) END END CorrectCoords; PROCEDURE CheckSelection(); BEGIN CorrectCoords(x, w); CorrectCoords(y, h); c := f.p.contents; WHILE c # NIL DO IF InArea (x, y, w, h, f.x + c.x, f.y + ph - c.y, SHORT(c.e.W DIV DUnit), SHORT(c.e.H DIV DUnit)) THEN IF ~(selected IN c.state) THEN INCL(c.state, selected); AppendToSelection(f.p, c); InvertSelected(f, c) END ELSE IF selected IN c.state THEN EXCL(c.state, selected); RemoveFromSelection(f.p, c); InvertSelected(f, c) END END; c := c.next END END CheckSelection; BEGIN ph := SHORT(f.p.H DIV DUnit); keysum := keys; ox := -1; oy := -1; REPEAT Input.Mouse(keys, mx, my); keysum := keysum + keys; IF (ox # mx) OR (oy # my) THEN GU.Frame(f, 0, x, y, w, h, 1, Display.invert); x := ix; y := iy; w := mx - ix; h := my - iy; ox := mx; oy := my; CheckSelection(); GU.Frame(f, 0, x, y, w, h, 1, Display.invert) END UNTIL keys = {}; GU.Frame(f, 0, x, y, w, h, 1, Display.invert); IF keysum = {MR, ML} THEN DeleteSelection(f) ELSIF keysum = {MR, MM} THEN SendSelection(f) ELSIF keysum = {ML, MM, MR} THEN RemoveSelection(f) END END TrackRubberBand; PROCEDURE HandleInput (f : Frame; VAR msg : Oberon.InputMsg); VAR X, Y, w, h, ph, fx, fy, fw, fh : INTEGER; track : TextFrames.TrackMsg; keysum : SET; c : Case; done : BOOLEAN; sub : Display.Frame; BEGIN keysum := msg.keys; IF msg.id = Oberon.track THEN IF msg.keys = {ML} THEN  sub := ThisSubFrame(f, msg.X, msg.Y); IF sub # NIL THEN sub.handle(sub, msg) ELSIF ~f.p.locked THEN SetCaret(f, msg.X - f.X, f.Y + f.H - msg.Y); REPEAT Input.Mouse(msg.keys, msg.X, msg.Y); keysum := keysum + msg.keys; Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, msg.X, msg.Y); UNTIL msg.keys = {}; IF keysum = {ML, MM} THEN CopyInto(f) END END ELSIF msg.keys = {MR} THEN sub := ThisSubFrame(f, msg.X, msg.Y); c := ThisCase(f.p, msg.X, msg.Y, f.x, f.y); IF (c = NIL) & ~f.p.locked THEN RemoveSelection(f); f.p.selTime := Oberon.Time(); TrackRubberBand(f, msg.X, msg.Y, msg.keys); ELSIF ~f.p.locked & ((sub = NIL) OR InBorder(msg.X,msg.Y,sub.X,sub.Y,sub.W,sub.H)) THEN f.p.selTime := Oberon.Time(); (* in border of subframe or in elem *) IF selected IN c.state THEN EXCL(c.state, selected) ELSE INCL(c.state, selected) END; (*InvertSelected(f, c);*) Elems.UpdateElem(c.e); IF selected IN c.state THEN AppendToSelection(f.p, c) ELSE RemoveFromSelection(f.p, c) END; REPEAT Input.Mouse(msg.keys, X, Y); keysum := keysum + msg.keys; Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, X, Y) UNTIL msg.keys = {}; IF keysum = {MR, ML} THEN DeleteSelection(f) ELSIF keysum = {MR, MM} THEN SendSelection(f) END; ELSIF sub # NIL THEN sub.handle(sub, msg) END; ELSIF msg.keys = {MM} THEN  RemoveCaret(f); done := FALSE; track.X0 := f.x; track.Y0 := f.Y; track.X := msg.X; track.Y := msg.Y; track.keys := msg.keys; track.frame := f; IF ~f.p.locked THEN Elems.Track(f.p, track, done); IF done THEN Elems.UpdateElem(f.p) END END; IF ~done THEN c := ThisCase(f.p, msg.X,msg.Y, f.x, f.y); IF (c = NIL) & (f.p.sel # NIL) THEN Move(f, NIL) ELSIF c # NIL THEN w := SHORT(c.e.W DIV DUnit); h := SHORT(c.e.H DIV DUnit); ph := SHORT(f.p.H DIV DUnit); (*sub := ThisSubFrame(f, msg.X, msg.Y);*) IF ~f.p.locked & ((selected IN c.state) OR InBorder(msg.X, msg.Y, f.x + c.x, f.y - c.y + ph, w, h)) & ~Elems.InCorner(msg.X, msg.Y, f.x + c.x, f.y - c.y + ph, w, c.e) THEN Move(f, c) ELSIF  Elems.InCorner(msg.X, msg.Y, f.x + c.x, f.y - c.y + ph, w, c.e) OR ~(selected IN c.state) THEN IF (c.e IS Elems.Elem) OR FullyVisible(f, c, w, h) THEN Elems.GetLook(c.e, track.fnt, track.col); track.X0 := c.x + f.x; track.Y0 := f.y + ph - c.y; track.frame := f; TruncateFrame(f, fx, fy, fw, fh); ToTop(f.p, c); c.e.handle(c.e, track); SetFrameCoords(f, fx, fy, fw, fh); END END END; END  ELSE sub := ThisSubFrame(f, msg.X, msg.Y); IF sub # NIL THEN sub.handle(sub, msg) END END ELSIF (msg.id = Oberon.consume) & f.hasCar THEN IF msg.ch = Elems.CRSL THEN ChangePos(f, NIL, -1, 0) ELSIF msg.ch = Elems.CRSR THEN ChangePos(f, NIL, 1, 0) ELSIF msg.ch = Elems.CRSD THEN ChangePos(f, NIL, 0, 1) ELSIF msg.ch = Elems.CRSU THEN ChangePos(f, NIL, 0, -1) END ELSE NotifySubFrames(f, msg) END; END HandleInput; PROCEDURE FrameHandle* (f : Display.Frame; VAR msg : Display.FrameMsg); VAR sub : Display.Frame; r : Texts.Reader; c : Case; X, Y, W, H : INTEGER; BEGIN WITH f : Frame DO WITH msg : Oberon.InputMsg DO HandleInput(f, msg) | msg : Oberon.ControlMsg DO NotifySubFrames(f, msg); IF f.hasCar & (msg.id IN {Oberon.defocus, Oberon.neutralize}) THEN RemoveCaret(f) END; IF msg.id = Oberon.neutralize THEN RemoveSelection(f) END | msg : TextFrames.InsertElemMsg DO IF f.hasCar THEN InsertElem(f.p, msg.e, f.cx, f.cy, Fonts.Default, 15); (*Elems.UpdateElem(msg.e); doesn't work with old elems w,h => removecaret; drawtextelem *) RemoveCaret(f); TruncateFrame(f, X, Y, W, H); DrawTextElem(f.p, MyCase(f.p, msg.e), f, f.x, f.y); SetFrameCoords(f, X, Y, W, H); SetCaret(f, f.cx + SHORT(msg.e.W DIV DUnit) + 2, f.cy) ELSE NotifySubFrames(f, msg) END | msg : Oberon.CopyOverMsg DO IF f.hasCar THEN CopyOver(f, msg(Oberon.CopyOverMsg)); ELSE NotifySubFrames(f, msg) END | msg : Oberon.SelectionMsg DO NotifySubFrames(f, msg); IF (f.p.sel # NIL) & (f.p.selTime > msg.time) THEN IF msg IS SelectionMsg THEN msg(SelectionMsg).p := f.p; msg(SelectionMsg).sel := f.p.sel ELSE msg.text := SelectionToText(f); msg.beg := 0; msg.end := msg.text.len END; msg.time := f.p.selTime END | msg : MenuViewers.ModifyMsg DO IF (f.oldY # f.Y) & (msg.H # 0) THEN (* shift *) sub := f.dsc; WHILE sub # NIL DO INC(sub.Y, f.Y - f.oldY); msg.Y := sub.Y; msg.H := sub.H; sub.handle(sub, msg); sub := sub.next END; INC(f.y, f.Y - f.oldY); f.oldY := f.Y END | msg : Elems.GetContextMsg DO IF (msg.p = f.p) & ((msg.fx >= f.X) & (msg.fx <= f.X + f.W) & (msg.fy >= f.Y) & (msg.fy <= f.Y + f.H) OR (msg.fx < 0) & (msg.fy < 0)) THEN msg.context := f; msg.p := NIL ELSE NotifySubFrames(f, msg) END | msg : NotifyMsg DO IF msg.p = f.p THEN MarkMenu(f); IF clear IN msg.id THEN ClearArea(f, msg.x, msg.y, msg.w, msg.h) END; IF refresh IN msg.id THEN RefreshArea(f, msg.x, msg.y, msg.w, msg.h) END ELSE NotifySubFrames(f, msg) END; | msg : TextFrames.UpdateMsg DO IF msg.text = f.p.elemTxt THEN IF msg.id = TextFrames.delete THEN DeleteSelection(f) ELSE Texts.OpenReader(r, msg.text, msg.beg); Texts.ReadElem(r); c := MyCase(f.p, r.elem); (*UpdateArea(f.p, c.x, c.y, SHORT(c.e.W DIV DUnit), SHORT(c.e.H DIV DUnit), {clear, refresh});*) UpdateArea(f.p, c.x, c.y, SHORT(c.oldW DIV DUnit), SHORT(c.oldH DIV DUnit), {clear, refresh}); END ELSE NotifySubFrames(f, msg) END ELSE NotifySubFrames(f, msg) END END END FrameHandle; BEGIN clp := Elems.clp; Texts.OpenWriter(W); NEW(elemTxtFrame); elemTxtFrame.text := TextFrames.Text(""); END PanelElems. System.Free PanelElems ~ PanelElems.Insert