%Syntax10.Scn.FntSyntax10i.Scn.FntIStampElemsAlloc16 Oct 988qBalloonElemsAllocSyntax10.Scn.FntnSyntax10i.Scn.Fnt~/QTK47p9 f 7  -)!- 0-10..*& :@ B 4$  L/ ) ,!O ~ Qe|+b"defaultH" the Height of the empty Inspector.Panel "defaultW" the Width of the empty Inspector.Panel "maxW" VAR maxW : INTEGER; the maximum width of the Inpector.Panel "descW" width of description fields in the Inspector.Panel (StaticTexts) "curElem" VAR curElem : Texts.Elem is the elem which is currently inspected "y" VAR y : INTEGER is the y-position where the next elem is to be inserted into the inspector "x" VAR x : INTEGER is the x-position where the next elem is to be inserted into the inspector "step" VAR step : INTEGER is the default y step for inserting elems into the Inspector.Panel "top" VAR top : INTEGER the y - starting point for inserting elems "left" VAR left : INTEGER the x - starting point for inserting elems "wasBool" VAR wasBool : BOOLEAN indicates if the last attribute was of type boolean, used to place two checkboxes in the same line "menuText" VAR menuText : Texts.Text is the text to be used for the PopupLists "ClearPanel" ClearPanel(); clears the Inpector.Panel so that it only contains the Inspect and Apply button. "CreateStaticText" CreateStaticText (value, align : ARRAY OF CHAR; w : INTEGER) inserts a StaticTextField with the caption value and alignment align and width w at x, y into the Inpsector.Panel. "CreateCheckBox" CreateCheckBox (name : ARRAY OF CHAR; val : BOOLEAN) inserts a CheckBox with name name where name is the attribtes name and value val at x, y into the Inspector.Panel. "CreatePopupList" CreatePopupList (name, default : ARRAY OF CHAR) inserts a PopupList with name name where name is the attribtes name and the default-value default at x, y into the Inpector.Panel. The text is taken from menuText. "CreateTextField" CreateTextField (name : ARRAY OF CHAR; msg : Elems.AttrMsg) inserts a TextField with name name where name is the attribtes name and value depending on msg at x, y into the Inpsector.Panel "CreateTextButton" CreateTextButton (name : ARRAY OF CHAR; t : Texts.Text) inserts a Button with name name where name is the attribtes name to edit t at x, y into the Inspector.Panel "EnumProps" EnumProps (name : ARRAY OF CHAR; class : INTEGER) is called for every property of the attribute name where name is the attribtes name in the inspection process. This "Enum" Enum (name : ARRAY OF CHAR; class : INTEGER) is called for every attribute in the inspection process. This procedure creates a name-value pair in the Inspector.Panel in each call. For special inspection set the enum field in the AttrMsg to your own Enum procedure. "Get" Get () tries to find a selection. curElem is set to the first found elem in the selection or NIL. "Align" Align aligns all elems contained in the current selection based on the first selected elem. Possible parameters are: l(eft), r(right), c(enter), u(p), d(own) or v(ertical center). If only one elem is selected and is placed inside a panel then the alignment is relativ to the panel. "SetSize" SetSize sets the size of all elems contained in the current selection based on the first selected elem. Possible parameters are: w(idth) and h(eight). If only one elem is selected and is placed inside a panel then the size of the panel relativ to the elem is changed. "InspectCurElem" InspectCurElem (info : ARRAY OF CHAR; VAR attr : Elems.AttrMsg) inspects curElem and sets the information of the Inspector.Panel to info. attr.enum must be set. "Inspect" Inspect inspects curElem using the standard behavior (ElemTools.Enum). "Apply" Apply applies the values of the Inspector.Panel to the selected elem. "SetAttr" SetAttr (* elemName attrName Value {attrName Value} *) looks for an elem called elemName in the same context in which SetAttr was called and sets every attribute with name attrName to the value Value. "AddAttr" AddAttr (* attrName Type *) adds the dynamic attribute with name attrName and type Type to the current selected elem. "SetTabPos" SetTabPos 'first' | 'last' | pos sets the tabstop position of the current selected elem to the given position. SetTabPos should only be used in panels, as in text the order is automatically given with the elems occurence in the text. "ShowTabs" ShowTabs 'on' | 'off' if set to 'on' all elems knowing tab stops display their current tabstop position. "EditText" EditText is called by a EditText-button in the Inspector.Panel and opens a viewer displaying the text mapped to this button. "Update" Update is used in the viewer of the text opened with EditText to apply the text to the button. "LogInput" LogInput copies the Oberon.Par.text to the System.Log. If there are problems with Par macros, it's a good idea to change the Cmd to LogInput to see which input is created. "ToCaret" ToCaret copies the Oberon.Par.text to the caret. With this command simple insert commands can be written.Syntax10b.Scn.Fnt     (  1 T8FoldElemsNew8 MarkElemsAlloc 18m8 4 8:8 x88 x-8\8 x(88 x!88 x-88 ^|(88 ; )8B8 ߰)8#Syntax10.Scn.Fnt++ creates the input fields of the inspector (e8 8#Syntax10.Scn.Fnt get selected elem 38 N'8#Syntax10.Scn.Fnt?? set elem's attributes (elem is a descriptor of the inspector) )K8  88  8 ,8 e328#Syntax10.Scn.Fnt attr.enum must be set ;8  8L8 8s%38 K/8 8 h 8#Syntax10.Scn.Fnt add dynamic attribute 8 h 8#Syntax10.Scn.Fnt delete a dynamic attribute 8 C!  8#Syntax10.Scn.Fnt set tabstop of selected elem 888 RN 88H88  88 88  B 808 r 88mJMODULE ElemTools; (* CE  *)  IMPORT Elems, PanelElems, Texts, TextFrames, Viewers, In, Out, Fonts, Types, Strings, Oberon, Display, MenuViewers; CONST DUnit = TextFrames.Unit; defaultH* = 40; defaultW* = 120; (*maxW* = 330;*) descW* = 80; TYPE EditFrame = POINTER TO EditFrameDesc; EditFrameDesc = RECORD (TextFrames.FrameDesc) e : Elems.Elem; END; VAR maxW* : INTEGER; curElem* : Texts.Elem; (* current inspected elem *) y*, x* : INTEGER; (* insertion point used for inspector *) step*, top*, left* : INTEGER; (* used for inspector *) wasBool* : BOOLEAN; (* used for inspector *) context : PanelElems.Text; W : Texts.Writer; menuText* : Texts.Text; PROCEDURE NoNotify (T: Texts.Text; op: INTEGER; beg, end: LONGINT);  END NoNotify; PROCEDURE GetSelection (VAR msg : PanelElems.SelectionMsg); BEGIN msg.time := 0; msg.text := NIL; msg.p := NIL; msg.sel := NIL; Viewers.Broadcast(msg) END GetSelection; PROCEDURE ClearPanel* (); VAR r : Texts.Reader; BEGIN Texts.OpenReader(r, context, 2); Texts.ReadElem(r); WHILE r.elem # NIL DO PanelElems.DeleteCase(context.base, PanelElems.MyCase(context.base, r.elem)); Texts.ReadElem(r) END; context.base.W := LONG(defaultW) * DUnit; context.base.H := LONG(defaultH) * DUnit END ClearPanel; (*PROCEDURE CreateSeperator (y : INTEGER); VAR e : Elems.Elem; BEGIN e := Elems.CreateElem("FrameElems.New"); e.W := LONG(maxW) * DUnit; e.H := LONG(2) * DUnit; PanelElems.InsertElem(context.base, e, 0, y + 3, Fonts.Default, Display.white); END CreateSeperator;*) PROCEDURE CreateStaticText* (value, align : ARRAY OF CHAR; w : INTEGER); VAR e : Elems.Elem; BEGIN e := Elems.CreateElem("StaticTextElems.New"); Elems.SetBoolean(e, "Border", FALSE); Elems.SetString(e, "Align", align); Elems.SetString(e, "Value", value); Elems.SetBoolean(e, "Locked", TRUE); e.W := LONG(w) * DUnit; PanelElems.InsertElem(context.base, e, x, y, Fonts.Default, Display.white); END CreateStaticText; PROCEDURE CreateCheckBox* (name : ARRAY OF CHAR; val : BOOLEAN); VAR e : Elems.Elem; BEGIN e := Elems.CreateElem("CheckBoxElems.New"); Elems.SetString(e, "Name", name); Elems.SetBoolean(e, "Value", val); Elems.SetBoolean(e, "Locked", TRUE); PanelElems.InsertElem(context.base, e, x, y - 5, Fonts.Default, Display.white); END CreateCheckBox; PROCEDURE CreatePopupList* (name, default : ARRAY OF CHAR); VAR e : Elems.Elem; BEGIN e := Elems.CreateElem("ListElems.New"); Elems.SetString(e, "Name", name); Elems.SetText(e, "ValueT", menuText); Elems.SetString(e, "Value", default); Elems.SetBoolean(e, "Popup", TRUE); Elems.SetBoolean(e, "Combo", FALSE); Elems.SetBoolean(e, "Locked", TRUE); e.W := LONG(200) * DUnit; e.H := LONG(Fonts.Default.maxY - Fonts.Default.minY + 6) * DUnit; PanelElems.InsertElem(context.base, e, x, y, Fonts.Default, Display.white); END CreatePopupList; PROCEDURE CreateTextField* (name : ARRAY OF CHAR; msg : Elems.AttrMsg); VAR e : Elems.Elem; BEGIN e := Elems.CreateElem("TextFieldElems.New"); e.W := LONG(maxW - descW - 50) * DUnit; PanelElems.InsertElem(context.base, e, x, y, Fonts.Default, Display.white); Elems.SetString(e, "Name", name); Elems.SetInteger(e, "MaxLen", 0); Elems.SetBoolean(e, "Locked", TRUE); IF msg.class = Elems.Int THEN Elems.SetInteger(e, "Value", msg.i) ELSIF msg.class = Elems.Real THEN Elems.SetReal(e, "Value",msg.r) ELSIF msg.class = Elems.LongReal THEN Elems.SetLReal(e, "Value", msg.lr) ELSIF msg.class = Elems.String THEN Elems.SetString(e, "Value", msg.s) ELSIF msg.class = Elems.Text THEN Elems.SetText(e, "Value", msg.t) END; END CreateTextField; PROCEDURE CreateTextButton* (name : ARRAY OF CHAR; t : Texts.Text); VAR e : Elems.Elem; BEGIN e := Elems.CreateElem("ButtonElems.New"); e.W := LONG(70) * DUnit; Elems.SetString(e, "Name", name); Elems.SetString(e, "Caption", "Edit Text"); Elems.SetString(e, "Cmd", "ElemTools.EditText"); Elems.SetBoolean(e, "Locked", TRUE); Elems.SetText(e, "Text", t); PanelElems.InsertElem(context.base, e, x, y, Fonts.Default, Display.white); END CreateTextButton; PROCEDURE EnumProps* (name : ARRAY OF CHAR; class : INTEGER); BEGIN Texts.WriteString(W, name); Texts.WriteLn(W) END EnumProps; PROCEDURE  Enum* (name : ARRAY OF CHAR; class : INTEGER); VAR msg : Elems.AttrMsg; v : ARRAY 256 OF CHAR; BEGIN IF wasBool & (class = Elems.Bool) THEN INC(x, 30); CreateStaticText(name, "right", descW); INC(x, 90) ELSE INC(y, step); x := left; CreateStaticText(name, "left", descW); INC(x, descW + 30); END; msg.id := Elems.get; msg.hasProps := FALSE; COPY(name, msg.name); curElem.handle(curElem, msg); IF msg.hasProps THEN (* get enumeration of his properties *) COPY(msg.s, v); msg.id := Elems.enumProps; msg.enum := EnumProps; curElem.handle(curElem, msg); menuText := TextFrames.Text(""); Texts.Append(menuText, W.buf); CreatePopupList(name, v); ELSE IF msg.class = Elems.String THEN CreateTextField(name, msg); wasBool := FALSE; ELSIF msg.class = Elems.Bool THEN CreateCheckBox(name, msg.b); wasBool := ~wasBool; ELSIF msg.class IN {Elems.Int, Elems.Real, Elems.LongReal} THEN CreateTextField(name, msg); wasBool := FALSE; ELSIF msg.class = Elems.Text THEN CreateTextButton(name, msg.t); wasBool := FALSE; END END END Enum; PROCEDURE  Get* (); VAR msg : PanelElems.SelectionMsg; r : Texts.Reader; BEGIN curElem := NIL; context := NIL; IF (Elems.CmdContext # NIL) & (Elems.CmdContext IS PanelElems.Text) THEN context := Elems.CmdContext(PanelElems.Text); ELSE context := NIL END; GetSelection(msg); IF msg.sel # NIL THEN (* container selection *) IF PanelElems.NextSel(msg.sel) # NIL THEN Out.String("-- more than one selection$") ELSE curElem := msg.sel.c.e END ELSIF msg.text # NIL THEN Texts.OpenReader(r, msg.text, msg.beg); Texts.ReadElem(r); IF (r.elem # NIL) & (Texts.Pos(r) <= msg.end) THEN curElem := r.elem END END END Get; PROCEDURE Set (elem : Elems.Elem); VAR class : INTEGER; name : ARRAY 256 OF CHAR; b : BOOLEAN; s : ARRAY 256 OF CHAR; set : SET; t : Texts.Text; S : Texts.Scanner; BEGIN Elems.GetString(elem, "Name", name); (* => name = attribute-name of curElem *) class := Elems.GetClass(curElem, name); IF class = Elems.Bool THEN Elems.GetBoolean(elem, "Value", b); Elems.SetBoolean(curElem, name, b) ELSIF class = Elems.Set THEN Elems.GetSet(elem, "Value", set); Elems.SetSet(curElem, name, set) ELSIF (class = Elems.String) OR (name = "Name") OR (name = "Cmd") OR (name = "Par") THEN Elems.GetString(elem, "Value", s); Elems.SetString(curElem, name, s) ELSIF class = Elems.Text THEN Elems.GetText(elem, "Text", t); Elems.SetText(curElem, name, t) ELSE (* text must be converted *) Elems.GetText(elem, "ValueT", t); Texts.OpenScanner(S, t, 0); Texts.Scan(S); IF class = Elems.Int THEN Elems.SetInteger(curElem, name, S.i) ELSIF class = Elems.Real THEN Elems.SetReal(curElem, name, S.x) ELSIF class = Elems.LongReal THEN Elems.SetLReal(curElem, name, S.y) ELSE HALT(98); END END END Set; PROCEDURE Align*; (** [l | c | r] [u | v | d] *) VAR setL, setC, setR, setU, setV, setD : BOOLEAN; nn : ARRAY 2 OF CHAR; msg : PanelElems.SelectionMsg; x, y, pw, ph, ew, eh : INTEGER; help : PanelElems.Selection; BEGIN setL := FALSE; setR := FALSE; setU := FALSE; setD := FALSE; In.Open; In.Name(nn); WHILE In.Done DO IF CAP(nn[0]) = "L" THEN setL := TRUE ELSIF CAP(nn[0]) = "R" THEN setR := TRUE ELSIF CAP(nn[0]) = "U" THEN setU := TRUE ELSIF CAP(nn[0]) = "D" THEN setD := TRUE ELSIF CAP(nn[0]) = "C" THEN setC := TRUE ELSIF CAP(nn[0]) = "V" THEN setV := TRUE END; In.Name(nn) END; GetSelection(msg); IF msg.p = NIL THEN Out.String("-- no panel selected$") ELSE IF setL THEN x := msg.sel.c.x ELSIF setR THEN x := msg.sel.c.x + SHORT(msg.sel.c.e.W DIV DUnit) ELSE x := msg.sel.c.x + SHORT(msg.sel.c.e.W DIV DUnit) DIV 2 END; IF setD THEN y := msg.sel.c.y ELSIF setU THEN y := msg.sel.c.y - SHORT(msg.sel.c.e.H DIV DUnit) ELSIF setV THEN y := msg.sel.c.y - SHORT(msg.sel.c.e.H DIV DUnit) DIV 2 END; IF PanelElems.NextSel(msg.sel) = NIL THEN (* only one elem selected *) ew := SHORT(msg.sel.c.e.W DIV DUnit); eh := SHORT(msg.sel.c.e.H DIV DUnit); pw := SHORT(msg.p.W DIV DUnit); ph := SHORT(msg.p.H DIV DUnit); IF setL THEN msg.sel.c.x := msg.p.border ELSIF setR THEN msg.sel.c.x := pw - msg.p.border - ew ELSIF setC THEN msg.sel.c.x := (pw - ew) DIV 2 END; IF setD THEN msg.sel.c.y := ph - msg.p.border ELSIF setU THEN msg.sel.c.y := msg.p.border + eh ELSIF setV THEN msg.sel.c.y := (ph - eh) DIV 2 + eh; END ELSE help := PanelElems.NextSel(msg.sel); WHILE help # NIL DO ew := SHORT(help.c.e.W DIV DUnit); eh := SHORT(help.c.e.H DIV DUnit); IF setL THEN help.c.x := x ELSIF setR THEN help.c.x := x - ew ELSIF setC THEN help.c.x := x - ew DIV 2 END; IF setD THEN help.c.y := y ELSIF setU THEN help.c.y := y + eh ELSIF setV THEN help.c.y := y + eh DIV 2 END; help := PanelElems.NextSel(help) END END; Elems.UpdateElem(msg.p) END END Align; PROCEDURE SetSize*; (** [w] [h] *) CONST errmsg = "-- ElemTools.SetSize w h"; VAR msg : PanelElems.SelectionMsg; w, h : LONGINT; setW, setH : BOOLEAN; r : Texts.Reader; nn : ARRAY 2 OF CHAR; notify : Texts.Notifier; help : PanelElems.Selection; BEGIN In.Open; In.Name(nn); IF In.Done THEN setW := CAP(nn[0]) = "W"; setH := CAP(nn[0]) = "H"; In.Name(nn); IF In.Done THEN setW := setW OR (CAP(nn[0]) = "W"); setH := setH OR (CAP(nn[0]) = "H"); END ELSE Out.String(errmsg) END; GetSelection(msg); IF (msg.sel = NIL) & (msg.text # NIL) THEN (* text selection *) notify := msg.text.notify; msg.text.notify := NoNotify; Texts.OpenReader(r, msg.text, msg.beg); Texts.ReadElem(r); IF r.elem = NIL THEN Out.String("-- no Element selected"); RETURN END; w := r.elem.W; h := r.elem.H; Texts.ReadElem(r); WHILE (Texts.Pos(r) <= msg.end) & (r.elem # NIL) DO IF setW THEN r.elem.W := w END; IF setH THEN r.elem.H := h END; Texts.ReadElem(r); END; msg.text.notify := notify; IF notify # NIL THEN notify(msg.text, 0, msg.beg, msg.end) END ELSIF msg.sel # NIL THEN (* container selection *) IF PanelElems.NextSel(msg.sel) = NIL THEN (* only one selected elem *) IF setW THEN msg.p.W := LONG(SHORT(msg.sel.c.e.W DIV DUnit) + 2 * msg.p.border + 4) * DUnit; msg.sel.c.x := msg.p.border + 2; END; IF setH THEN msg.p.H := LONG(SHORT(msg.sel.c.e.H DIV DUnit) + 2 * msg.p.border + 4) * DUnit; msg.sel.c.y := msg.p.border + 2 + SHORT(msg.sel.c.e.H DIV DUnit) END; Elems.UpdateElem(msg.p) ELSE help := PanelElems.NextSel(msg.sel); w := msg.sel.c.e.W; h := msg.sel.c.e.H; WHILE help # NIL DO IF setW THEN help.c.e.W := w END; IF setH THEN help.c.e.H := h END; Elems.UpdateElem(help.c.e); help := PanelElems.NextSel(help) END END END END SetSize; PROCEDURE InspectCurElem*(info : ARRAY OF CHAR; VAR attr : Elems.AttrMsg);  BEGIN ClearPanel(); IF curElem # NIL THEN wasBool := FALSE; x := 130; y := 30; CreateStaticText(info, "left", 180); context.base.W := LONG(maxW) * DUnit; IF curElem IS Elems.Elem THEN x := left; y := top; wasBool := FALSE; attr.id := Elems.enum; IF attr.enum = NIL THEN attr.enum := Enum END; curElem.handle(curElem, attr); context.base.H := LONG(y + 5) * DUnit END; IF Elems.CmdElem # NIL THEN Elems.UpdateElem(context.base) END ELSIF (context # NIL) & (Elems.CmdElem # NIL) THEN Elems.UpdateElem(context.base) END END InspectCurElem; PROCEDURE Inspect*; VAR type : Types.Type; tname : ARRAY 65 OF CHAR; attr : Elems.AttrMsg; BEGIN Get(); IF curElem # NIL THEN type := Types.TypeOf(curElem); COPY(type.module.name, tname); Strings.Append(".", tname); Strings.Append(type.name, tname); ELSE tname := ""; END; attr.enum := Enum; InspectCurElem(tname, attr) END Inspect; PROCEDURE Apply*; VAR r : Texts.Reader; BEGIN IF ~(Elems.CmdContext IS PanelElems.Text) THEN Out.String("-- command not used within a panel$"); RETURN END; Get(); IF (curElem # NIL) & (curElem IS Elems.Elem) THEN Texts.OpenReader(r, context, 4); (* after first descriptor *) Texts.ReadElem(r); WHILE r.elem # NIL DO Set(r.elem(Elems.Elem)); Texts.ReadElem(r); Texts.ReadElem(r) (* overread description statictext *) END; Elems.UpdateElem(curElem) END END Apply; PROCEDURE SetAttr*; (** elemName attrName Value {attrName Value} *) VAR s : Texts.Scanner; name, attrname : ARRAY 32 OF CHAR; e : Texts.Elem; str : ARRAY 2 OF CHAR; BEGIN Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s); IF s.class IN {Texts.Name, Texts.String} THEN COPY(s.s, name) ELSE Out.String("-- wrong input$"); RETURN END; IF Elems.CmdContext = NIL THEN RETURN END; e := Elems.NamedElem(name, Elems.CmdContext); IF e = NIL THEN Out.String("-- elem not found : "); Out.String(s.s); Out.Ln; RETURN END; REPEAT Texts.Scan(s); (* attrName *) IF s.class IN {Texts.Name, Texts.String} THEN COPY(s.s, attrname); ELSE Out.String("-- wrong input$"); RETURN END; Texts.Scan(s); (* Value *) IF s.class IN {Texts.Name, Texts.String} THEN Elems.SetString(e, attrname, s.s) ELSIF s.class = Texts.Int THEN Elems.SetInteger(e, attrname, s.i) ELSIF s.class = Texts.Real THEN Elems.SetReal(e, attrname, s.x) ELSIF s.class = Texts.LongReal THEN Elems.SetLReal(e, attrname, s.y) ELSIF s.class = Texts.Char THEN str[0] := s.c; str[1] := 0X; Elems.SetString(e, attrname, str) END; UNTIL s.eot; Elems.UpdateElem(e) END SetAttr; PROCEDURE AddAttr*; (** attrName Type *)  VAR name, type : ARRAY 64 OF CHAR; t : Texts.Text; BEGIN Get(); IF curElem = NIL THEN Out.String("-- no elem selected$"); RETURN END; In.Open; In.String(name); In.String(type); IF (name # "") & (type # "") THEN IF type = "Boolean" THEN Elems.SetBoolean(curElem, name, FALSE) ELSIF type = "String" THEN Elems.SetString(curElem, name, "") ELSIF type = "Integer" THEN Elems.SetInteger(curElem, name, 0) ELSIF type = "Real" THEN Elems.SetReal(curElem, name, 0.0) ELSIF type = "LongReal" THEN Elems.SetLReal(curElem, name, 0.0) ELSIF type = "Text" THEN t := TextFrames.Text(""); Elems.SetText(curElem, name, t) ELSIF type = "Set" THEN Elems.SetSet(curElem, name, {}) END ELSE Out.String("-- Attribute must have a name$") END END AddAttr; PROCEDURE DelAttr*; (** attrName *)  VAR msg : Elems.AttrMsg; BEGIN Get(); IF curElem = NIL THEN Out.String("-- no elem selected$"); RETURN END; In.Open; In.String(msg.name); IF In.Done THEN msg.id := Elems.delete; curElem.handle(curElem, msg); IF ~Elems.Done THEN Out.String("-- attribute <"); Out.String(msg.name); Out.String("> doesn't exist$") END ELSE Out.String("-- ElemTools.DelAttr attrName$") END END DelAttr; PROCEDURE SetTabPos*;(** "first" | "last" | number *) VAR t : Texts.Text; notifier : Texts.Notifier; pos : LONGINT; s : Texts.Scanner; i : INTEGER; tab : Elems.TabStopMsg; r : Texts.Reader; PROCEDURE UpdateTabStopElems(); VAR r : Texts.Reader; tab : Elems.TabStopMsg; BEGIN Texts.OpenReader(r, t, 0); Texts.ReadElem(r); WHILE ~r.eot DO tab.accepted := FALSE; r.elem.handle(r.elem, tab); IF tab.accepted THEN Elems.UpdateElem(r.elem) END; Texts.ReadElem(r) END END UpdateTabStopElems; BEGIN Get(); IF curElem # NIL THEN t := Texts.ElemBase(curElem); IF t IS PanelElems.Text THEN notifier := t.notify; t.notify := NoNotify; Texts.Delete(t, Texts.ElemPos(curElem), Texts.ElemPos(curElem) + 1); Texts.WriteElem(W, curElem); Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s); IF s.class IN {Texts.String, Texts.Name} THEN IF s.s = "first" THEN pos := 0 ELSIF s.s = "last" THEN pos := t.len END; ELSIF (s.class = Texts.Int) & (s.i > 0) THEN i := 0; Texts.OpenReader(r, t, 0); REPEAT Texts.ReadElem(r); IF ~r.eot THEN tab.accepted := FALSE; r.elem.handle(r.elem, tab); IF tab.accepted THEN INC(i) END; END; UNTIL (i = s.i) OR r.eot; pos := Texts.Pos(r) - 1; ELSE pos := 0 END; Texts.Insert(t, pos, W.buf); t.notify := notifier; UpdateTabStopElems(); ELSE Out.String("-- elem is not in a panel or wrong input $") END; END; END SetTabPos; PROCEDURE ShowTabs* (** "on" | "off" *); VAR s : ARRAY 4 OF CHAR; BEGIN Get(); IF (curElem # NIL) & (curElem IS PanelElems.Panel) THEN In.Open; In.Name(s); curElem(PanelElems.Panel).showTabs := In.Done & (s = "on"); Elems.UpdateElem(curElem) ELSE Out.String("-- no panel selected$") END END ShowTabs; PROCEDURE HandleEdit (f : Display.Frame; VAR msg : Display.FrameMsg);  VAR f1: EditFrame; BEGIN TextFrames.Handle(f, msg); WITH f : EditFrame DO IF msg IS Oberon.CopyMsg THEN NEW(f1); TextFrames.Open(f1, f.text, f.org); f1.handle := f.handle; f1.e := f.e; msg(Oberon.CopyMsg).F := f1 END END END HandleEdit;  PROCEDURE EditText*; VAR v : Viewers.Viewer; f : EditFrame; x, y : INTEGER; t : Texts.Text; name : ARRAY 256 OF CHAR; BEGIN Elems.GetString(Elems.CmdElem, "Name", name); Elems.GetText(Elems.CmdElem, "Text", t); Oberon.AllocateUserViewer(Oberon.Mouse.X, x, y); NEW(f); f.e := Elems.CmdElem; TextFrames.Open(f, Elems.CopyText(t), 0); f.handle := HandleEdit; v := MenuViewers.New(TextFrames.NewMenu(name, "System.Close System.Copy System.Grow Edit.Parcs ElemTools.Update "), f, TextFrames.menuH, x, y) END EditText; PROCEDURE Update*;  VAR f : EditFrame; S : Texts.Scanner; menuText : Texts.Text; BEGIN IF Oberon.Par.frame = Oberon.Par.vwr.dsc THEN f := Oberon.Par.frame.next(EditFrame); menuText := Oberon.Par.frame(TextFrames.Frame).text; Elems.SetText(f.e, "Text", Elems.CopyText(f.text)); Texts.OpenReader(S, menuText, menuText.len-1); Texts.Read(S, S.c); IF S.c = "!" THEN Texts.Delete(menuText, menuText.len-1, menuText.len) END END END Update;  PROCEDURE LogParam*; BEGIN Texts.WriteString(W, "----------------------------------"); Texts.WriteLn(W); Texts.Save(Oberon.Par.text, Oberon.Par.pos, Oberon.Par.text.len, W.buf); Texts.WriteLn(W); Texts.WriteString(W, "----------------------------------"); Texts.WriteLn(W); Texts.Append(Oberon.Log, W.buf); END LogParam; PROCEDURE ParamToCaret*; VAR msg : Oberon.CopyOverMsg; BEGIN msg.text := Oberon.Par.text; msg.beg := 0; msg.end := msg.text.len; Viewers.Broadcast(msg) END ParamToCaret; BEGIN Texts.OpenWriter(W); top := 40; left := 10; step := Fonts.Default.maxY - Fonts.Default.minY + 7; maxW := Display.Width DIV 8 * 3; END ElemTools. System.Free ElemTools ~ ElemTools.Inspect ElemTools.SetSize w ~ ElemTools.SetSize h ~ ElemTools.SetSize w h ~ ElemTools.Align l ~ ElemTools.Align r ~ ElemTools.Align u ~ ElemTools.Align b ~ ElemTools.Align c ~ ElemTools.Align v ~