home *** CD-ROM | disk | FTP | other *** search
/ Gold Fish 3 / goldfish_volume_3.bin / files / dev / obero / oberon / demos / captionedit.mod (.txt) next >
Encoding:
Oberon Text  |  1995-04-06  |  50.7 KB  |  1,147 lines

  1. Syntax10.Scn.Fnt
  2. Syntax10i.Scn.Fnt
  3. StampElems
  4. Alloc
  5. 30 Jan 95
  6. Syntax10b.Scn.Fnt
  7. MODULE CaptionEdit; (* Copyright: ww 
  8.     IMPORT
  9.         Oberon, MenuViewers, Viewers, TextFrames, Texts, Display, Fonts, Input, Modules, Files;
  10.     CONST
  11.         (* Model *)
  12.         remove* = 10; insert* = 11; move* = 12; tofront* = 13;
  13.             VersionName = "CaptionEdit (ww 15 Nov 94)";
  14.         (* Frames *)
  15.         ML = 2; MM = 1; MR = 0;    Space = 5;
  16.         NoFocus* = 0; PointFocus* = 1; CaretFocus* = 2;
  17.         (* Editor *)
  18.         Menu = "System.Close  System.Copy  System.Grow  CaptionEdit.Store ";
  19.     TYPE
  20.         (* Model *)
  21.         Panel* = POINTER TO PanelDesc;
  22.         Caption* = POINTER TO CaptionDesc;
  23.         Notifier* = PROCEDURE (panel: Panel; caption: Caption; op, beg, end: LONGINT);
  24.         CaptionDesc* = RECORD(Texts.TextDesc)
  25.             host*: Panel;
  26.             next*: Caption;
  27.             x*, y*: LONGINT;
  28.             inserted: BOOLEAN
  29.         END;
  30.         PanelDesc* = RECORD
  31.             first*: Caption;
  32.             notify*: Notifier
  33.         END;
  34.         (* Frames *)
  35.         CapInfo = POINTER TO CapInfoDesc;
  36.         CapInfoDesc = RECORD
  37.             next: CapInfo;
  38.             text: Caption;
  39.             x, y, w, h, baseH: INTEGER;
  40.             ok, marked: BOOLEAN
  41.         END;
  42.         Location* = RECORD
  43.             cap*: Caption;
  44.             pos*: LONGINT;
  45.             x*, y*: INTEGER
  46.         END;
  47.         Frame* = POINTER TO FrameDesc;
  48.         FrameDesc* = RECORD(Display.FrameDesc)
  49.             panel*: Panel;
  50.             x0*, y0*: LONGINT;
  51.             hasSel*: BOOLEAN;
  52.             selTime*: LONGINT;
  53.             selBeg*, selEnd*: Location;
  54.             focus*: INTEGER;
  55.             focusPos*: Location;
  56.             subFocus*: Display.Frame;
  57.             grid*: LONGINT;
  58.             info: CapInfo
  59.         END;
  60.         UpdateMsg* = RECORD(Display.FrameMsg)
  61.             panel*: Panel;
  62.             cap*: Caption;
  63.             op*, beg*, end*: LONGINT
  64.         END;
  65.         w, wattr: Texts.Writer;
  66.         framePat: Display.Pattern;
  67.     PROCEDURE ASSERT(b: BOOLEAN);
  68.     BEGIN IF ~b THEN HALT(99) END
  69.     END ASSERT;
  70. (* Model *)
  71.     PROCEDURE Insert*(host: Panel; cap: Caption; x, y: LONGINT);
  72.         VAR q: Caption;
  73.     BEGIN ASSERT(~cap.inserted);
  74.         q := host.first;
  75.         IF q # NIL THEN
  76.             WHILE q.next # NIL DO q := q.next END;
  77.             q.next := cap
  78.         ELSE host.first := cap
  79.         END;
  80.         cap.next := NIL; cap.inserted := TRUE; cap.host := host; cap.x := x; cap.y := y;
  81.         host.notify(host, cap, insert, 0, 0)
  82.     END Insert;
  83.     PROCEDURE Remove*(cap: Caption);
  84.         VAR q: Caption; host: Panel;
  85.     BEGIN
  86.         IF cap.inserted THEN host := cap.host; q := host.first;
  87.             IF q # cap THEN
  88.                 WHILE q.next # cap DO q := q.next END;
  89.                 q.next := cap.next
  90.             ELSE host.first := cap.next
  91.             END;
  92.             cap.inserted := FALSE;
  93.             host.notify(host, cap, remove, 0, 0)
  94.         END
  95.     END Remove;
  96.     PROCEDURE Move*(cap: Caption; x, y: LONGINT);
  97.         VAR host: Panel;
  98.     BEGIN cap.x := x; cap.y := y; host := cap.host; host.notify(host, cap, move, 0, 0)
  99.     END Move;
  100.     PROCEDURE BringToFront*(cap: Caption);
  101.         VAR q: Caption; host: Panel;
  102.     BEGIN
  103.         IF cap.inserted & (cap.next # NIL) THEN host := cap.host; q := host.first;
  104.             IF q # cap THEN
  105.                 WHILE q.next # cap DO q := q.next END;
  106.                 q.next := cap.next
  107.             ELSE host.first := cap.next
  108.             END;
  109.             WHILE q.next # NIL DO q := q.next END;
  110.             q.next := cap; cap.next := NIL;
  111.             host.notify(host, cap, tofront, 0, 0)
  112.         END
  113.     END BringToFront;
  114.     PROCEDURE NotifyPanel*(t: Texts.Text; op: INTEGER; beg, end: LONGINT);
  115.         VAR c: Caption; p: Panel;
  116.     BEGIN c := t(Caption); p := c.host;
  117.         IF c.inserted THEN
  118.             IF (op = Texts.delete) & (c.len = 0) THEN Remove(c)
  119.             ELSE p.notify(p, c, op, beg, end)
  120.             END
  121.         ELSIF (p # NIL) & (op = Texts.insert) THEN Insert(p, c, c.x, c.y)
  122.         END
  123.     END NotifyPanel;
  124.     PROCEDURE OpenCaption*(cap: Caption; text: Texts.Text; beg, end: LONGINT);
  125.         VAR buf: Texts.Buffer;
  126.     BEGIN Texts.Open(cap, ""); cap.notify := NotifyPanel; cap.inserted := FALSE;
  127.         IF (text # NIL) & (beg < end) THEN
  128.             NEW(buf); Texts.OpenBuf(buf); Texts.Save(text, beg, end, buf); Texts.Append(cap, buf)
  129.         END
  130.     END OpenCaption;
  131.     PROCEDURE OpenPanel*(p: Panel; notifier: Notifier);
  132.     BEGIN p.first := NIL; p.notify := notifier
  133.     END OpenPanel;
  134.     PROCEDURE LoadPanel*(VAR r: Files.Rider; p: Panel);
  135.         VAR i: LONGINT; c, prev, anchor: Caption;
  136.     BEGIN OpenPanel(p, p.notify); NEW(anchor); prev := anchor; Files.ReadLInt(r, i);
  137.         WHILE i # 0 DO NEW(c); OpenCaption(c, NIL, 0, 0); prev.next := c; prev := c; c.host := p; c.inserted := TRUE;
  138.             Files.ReadLInt(r, c.x); Files.ReadLInt(r, c.y); Texts.Load(r, c);
  139.             DEC(i)
  140.         END;
  141.         p.first := anchor.next
  142.     END LoadPanel;
  143.     PROCEDURE ThisPanel*(f: Files.File; notifier: Notifier): Panel;
  144.         VAR ch: CHAR; p: Panel; r:Files.Rider;
  145.     BEGIN NEW(p); OpenPanel(p, notifier);
  146.         IF f # NIL THEN Files.Set(r, f, 0); Files.Read(r, ch);
  147.             IF ch = 0F7X THEN Files.Read(r, ch);
  148.                 IF ch = 1X THEN LoadPanel(r, p) END
  149.             END
  150.         END;
  151.         RETURN p
  152.     END ThisPanel;
  153.     PROCEDURE StorePanel*(VAR r: Files.Rider; p: Panel);
  154.         VAR i: LONGINT; c: Caption;
  155.     BEGIN i := 0; c := p.first;
  156.         WHILE c # NIL DO INC(i); c := c.next END;
  157.         Files.WriteLInt(r, i); c := p.first;
  158.         WHILE c # NIL DO Files.WriteLInt(r, c.x); Files.WriteLInt(r, c.y); Texts.Store(r, c); c := c.next END
  159.     END StorePanel;
  160.     PROCEDURE File*(p: Panel; name: ARRAY OF CHAR): Files.File;
  161.         VAR f: Files.File; r: Files.Rider;
  162.     BEGIN f := Files.New(name);
  163.         Files.Set(r, f, 0); Files.Write(r, 0F7X); Files.Write(r, 1X); StorePanel(r, p);
  164.         RETURN f
  165.     END File;
  166. (* Frames *)
  167. (* Measurement *)
  168.     PROCEDURE MeasureChar(VAR r: Texts.Reader; ch: CHAR; VAR w, minY, maxY: INTEGER);
  169.         VAR chX, chY, chW, chH, voff: INTEGER; pat: Display.Pattern; msg: TextFrames.DisplayMsg;
  170.     BEGIN voff := r.fnt.height * r.voff DIV 64;
  171.         IF r.elem # NIL THEN msg.prepare := TRUE; msg.fnt := r.fnt; msg.col := r.col;
  172.             msg.pos := Texts.Pos(r) - 1; msg.indent := 0; msg.Y0 := r.fnt.minY;
  173.             r.elem.handle(r.elem, msg);
  174.             IF r.elem IS TextFrames.Parc THEN r.elem.W := 0; r.elem.H := 0; msg.Y0 := 0 END;
  175.             w := SHORT(r.elem.W DIV TextFrames.Unit);
  176.             minY := voff + msg.Y0; maxY := SHORT(r.elem.H DIV TextFrames.Unit) + minY
  177.         ELSE Display.GetChar(r.fnt.raster, ch, w, chX, chY, chW, chH, pat);
  178.             minY := r.fnt.minY + voff; maxY := r.fnt.maxY + voff
  179.         END
  180.     END MeasureChar;
  181.     PROCEDURE GetBoundingBox(t: Texts.Text; beg, end: LONGINT; VAR w, h, baseH: INTEGER);
  182.         VAR pos: LONGINT; minY, maxY, w1, minY1, maxY1: INTEGER; ch: CHAR; r: Texts.Reader;
  183.     BEGIN w := 0; minY := 0; maxY := 0;
  184.         Texts.OpenReader(r, t, 0); Texts.Read(r, ch); pos := 1;
  185.         WHILE ~r.eot DO MeasureChar(r, ch, w1, minY1, maxY1);
  186.             IF minY1 < minY THEN minY := minY1 END;
  187.             IF maxY1 > maxY THEN maxY := maxY1 END;
  188.             IF (pos > beg) & (pos <= end) THEN w := w + w1 END;
  189.             Texts.Read(r, ch); INC(pos)
  190.         END;
  191.         h := maxY - minY; baseH := -minY
  192.     END GetBoundingBox;
  193.     PROCEDURE Width(t: Texts.Text; beg, end: LONGINT): INTEGER;
  194.         VAR w, h, baseH: INTEGER;
  195.     BEGIN GetBoundingBox(t, beg, end, w, h, baseH); RETURN w
  196.     END Width;
  197.     PROCEDURE Offset(t: Texts.Text; dX: INTEGER): LONGINT;
  198.         VAR i: LONGINT; w, minY, maxY: INTEGER; ch: CHAR; r: Texts.Reader;
  199.     BEGIN i := 0; Texts.OpenReader(r, t, 0);
  200.         WHILE (i < t.len) & (dX > 0) DO Texts.Read(r, ch); INC(i);
  201.             MeasureChar(r, ch, w, minY, maxY); dX := dX - w
  202.         END;
  203.         IF (dX < 0) & (i # 0) THEN DEC(i) END;
  204.         RETURN i
  205.     END Offset;
  206. (* Caption_Info *)
  207.     PROCEDURE NewInfo(f: Frame; cap: Caption; w, h, baseH: INTEGER): CapInfo;
  208.         VAR info: CapInfo;
  209.     BEGIN NEW(info); info.text := cap; info.x := SHORT(cap.x - f.x0); info.y := SHORT(cap.y - f.y0);
  210.         info.w := w; info.h := h; info.baseH := baseH;
  211.         info.ok := FALSE; info.marked := FALSE;
  212.         RETURN info
  213.     END NewInfo;
  214.     PROCEDURE ThisCaption(f: Frame; x, y: INTEGER): CapInfo;
  215.         VAR c, this: CapInfo;
  216.     BEGIN x := x - f.X; y := y - (f.Y + f.H);
  217.         c := f.info; this := NIL;
  218.         WHILE c # NIL DO
  219.             IF (c.x <= x) & (x < c.x + c.w) & (c.y <= y) & (y < c.y + c.h) THEN this := c END;
  220.             c := c.next
  221.         END;
  222.         RETURN this
  223.     END ThisCaption;
  224.     PROCEDURE SetReader(f: Frame; x, y: INTEGER; VAR r: Texts.Reader; VAR cap: CapInfo);
  225.         VAR t: Caption;
  226.     BEGIN cap := ThisCaption(f, x, y);
  227.         IF cap # NIL THEN t := cap.text; Texts.OpenReader(r, t, Offset(t, x - f.X - cap.x)) END
  228.     END SetReader;
  229.     PROCEDURE InfoAbout(f: Frame; cap: Caption): CapInfo;
  230.         VAR c: CapInfo;
  231.     BEGIN c := f.info;
  232.         WHILE (c # NIL) & (c.text # cap) DO c := c.next END;
  233.         RETURN c
  234.     END InfoAbout;
  235.     PROCEDURE InsertInfo(f: Frame; cap: CapInfo);
  236.         VAR info, p: CapInfo; q, t: Caption;
  237.     BEGIN info := f.info; p := NIL; q := f.panel.first; t := cap.text;
  238.         WHILE q # t DO
  239.             IF (info # NIL) & (q = info.text) THEN p := info; info := info.next END;
  240.             q := q.next
  241.         END;
  242.         IF p # NIL THEN p.next := cap ELSE f.info := cap END;
  243.         cap.next := info
  244.     END InsertInfo;
  245.     PROCEDURE RemoveInfo(f: Frame; cap: CapInfo);
  246.         VAR p, q: CapInfo;
  247.     BEGIN p := f.info;
  248.         IF p = cap THEN f.info := cap.next
  249.         ELSE q := p.next;
  250.             WHILE q # cap DO p := q; q := q.next END;
  251.             p.next := cap.next
  252.         END
  253.     END RemoveInfo;
  254. (* Overlaps *)
  255.     PROCEDURE MarkOverlap(x, y: LONGINT; w, h: INTEGER; cap: CapInfo);
  256.         VAR r, t, cX, cY: LONGINT;
  257.     BEGIN r := x + w; t := y + h;
  258.         WHILE cap # NIL DO cX := cap.text.x; cY := cap.text.y;
  259.             IF (x < cX + cap.w) & (cX < r) & (y < cY + cap.h) & (cY < t) THEN cap.ok := FALSE END;
  260.             cap := cap.next
  261.         END
  262.     END MarkOverlap;
  263.     PROCEDURE HasOverlap(cap: CapInfo): BOOLEAN;
  264.         VAR l, r, b, t: INTEGER;
  265.     BEGIN l := cap.x; r := l + cap.w; b := cap.y; t := b + cap.h; cap := cap.next;
  266.         WHILE (cap # NIL) & ((cap.x >= r) OR (cap.x + cap.w <= l) OR (cap.y >= t) OR (cap.y + cap.h <= b)) DO
  267.             cap := cap.next
  268.         END;
  269.         RETURN cap # NIL
  270.     END HasOverlap;
  271. (* Subframe handling *)
  272.     PROCEDURE ThisSubFrame(parent: Frame; x, y: INTEGER): Display.Frame;
  273.         VAR f: Display.Frame;
  274.     BEGIN f := parent.dsc;
  275.         WHILE (f # NIL) & ((x < f.X) OR (x >= f.X + f.W) OR (y < f.Y) OR (y >= f.Y + f.H)) DO f := f.next END;
  276.         RETURN f
  277.     END ThisSubFrame;
  278.     PROCEDURE CloseSubFrames(parent: Frame; x, y, w, h: INTEGER);
  279.         VAR r, t: INTEGER; f, p: Display.Frame; msg: MenuViewers.ModifyMsg;
  280.     BEGIN r := x + w; t := y + h;
  281.         p := parent.dsc;
  282.         IF p # NIL THEN f := p.next;
  283.             WHILE f # NIL DO
  284.                 IF (x < f.X + f.W) & (f.X < r) & (y < f.Y + f.H) & (f.Y < t) THEN p.next := f.next;
  285.                     msg.id := MenuViewers.reduce; msg.dY := 0; msg.Y := f.Y; msg.H := 0; f.handle(f, msg)
  286.                 ELSE p := f
  287.                 END;
  288.                 f := f.next
  289.             END;
  290.             f := parent.dsc;
  291.             IF (x < f.X + f.W) & (f.X < r) & (y < f.Y + f.H) & (f.Y < t) THEN parent.dsc := f.next;
  292.                 msg.id := MenuViewers.reduce; msg.dY := 0; msg.Y := f.Y; msg.H := 0; f.handle(f, msg)
  293.             END
  294.         END
  295.     END CloseSubFrames;
  296.     PROCEDURE ShiftSubFrames(parent: Frame; y, h, dY: INTEGER);
  297.         VAR t: INTEGER; f: Display.Frame; msg: MenuViewers.ModifyMsg;
  298.     BEGIN
  299.         IF dY < 0 THEN CloseSubFrames(parent, parent.X, y + dY, parent.W, -dY)
  300.         ELSE CloseSubFrames(parent, parent.X, y + h, parent.W, dY)
  301.         END;
  302.         f := parent.dsc; t := y + h;
  303.         WHILE f # NIL DO
  304.             IF (y < f.Y + f.H) & (f.Y < t) THEN f.Y := f.Y + dY;
  305.                 msg.id := MenuViewers.reduce; msg.dY := 0; msg.Y := f.Y; msg.H := f.H; f.handle(f, msg)
  306.             END;
  307.             f := f.next
  308.         END
  309.     END ShiftSubFrames;
  310. (* Marker drawing *)
  311.     PROCEDURE FlipCross(clip: Display.Frame; x, y: INTEGER);
  312.     BEGIN Display.CopyPatternC(clip, Display.white, Display.cross, x - 5, y - 5, Display.invert)
  313.     END FlipCross;
  314.     PROCEDURE FlipCaret(clip: Display.Frame; x, y: INTEGER);
  315.     BEGIN Display.CopyPatternC(clip, Display.white, Display.hook, x, y - 6, Display.invert)
  316.     END FlipCaret;
  317.     PROCEDURE InvertRect(clip: Display.Frame; x, y, w, h: INTEGER);
  318.         VAR pinX, pinY: INTEGER;
  319.     BEGIN pinX := clip.X; pinY := clip.Y + clip.H + 1;
  320.         IF w < 0 THEN x := x + w; w := -w END;
  321.         IF h < 0 THEN y := y + h; h := -h END;
  322.         Display.ReplPatternC(clip, Display.white, framePat, x, y, w, 1, pinX, pinY, Display.invert);
  323.         Display.ReplPatternC(clip, Display.white, framePat, x, y + h, w, 1, pinX, pinY, Display.invert);
  324.         Display.ReplPatternC(clip, Display.white, framePat, x, y, 1, h, pinX, pinY, Display.invert);
  325.         Display.ReplPatternC(clip, Display.white, framePat, x + w, y, 1, h, pinX, pinY, Display.invert)
  326.     END InvertRect;
  327. (* Mark removing *)
  328.     PROCEDURE RemoveSelection*(f: Frame);
  329.         VAR x, y: INTEGER;
  330.     BEGIN
  331.         IF f.hasSel THEN f.hasSel := FALSE; x := f.selBeg.x; y := f.selBeg.y;
  332.             Display.ReplConstC(f, Display.white, x, y, f.selEnd.x - x, f.selEnd.y - y, Display.invert)
  333.         END
  334.     END RemoveSelection;
  335.     PROCEDURE Defocus*(f: Frame);
  336.         VAR subF: Display.Frame; msg: Oberon.ControlMsg;
  337.     BEGIN
  338.         IF f.focus = PointFocus THEN FlipCross(f, f.focusPos.x, f.focusPos.y)
  339.         ELSIF f.focus = CaretFocus THEN FlipCaret(f, f.focusPos.x, f.focusPos.y)
  340.         END;
  341.         f.focus := NoFocus;
  342.         IF f.subFocus # NIL THEN subF := f.subFocus; msg.id := Oberon.defocus; subF.handle(subF, msg) END
  343.     END Defocus;
  344.     PROCEDURE UnmarkCaption*(f: Frame; cap: Caption);
  345.         VAR info: CapInfo;
  346.     BEGIN info := InfoAbout(f, cap);
  347.         IF (info # NIL) & info.marked THEN InvertRect(f, f.X + info.x, f.Y + f.H + info.y, info.w - 1, info.h - 1);
  348.             info.marked := FALSE
  349.         END
  350.     END UnmarkCaption;
  351.     PROCEDURE UnmarkAllCaptions*(f: Frame);
  352.         VAR c: CapInfo;
  353.     BEGIN c := f.info;
  354.         WHILE c # NIL DO
  355.             IF c.marked THEN InvertRect(f, f.X + c.x, f.Y + f.H + c.y, c.w - 1, c.h - 1); c.marked := FALSE END;
  356.             c := c.next
  357.         END
  358.     END UnmarkAllCaptions;
  359. (* Subfocus *)
  360.     PROCEDURE PassSubFocus*(f: Frame; cap: Caption; new: Display.Frame);
  361.         VAR info: CapInfo; old: Display.Frame; r: Texts.Reader; ctrl: Oberon.ControlMsg; msg: TextFrames.FocusMsg;
  362.     BEGIN old := f.subFocus;
  363.         IF old # NIL THEN ctrl.id := Oberon.defocus; old.handle(old, ctrl);
  364.             InvertRect(f, old.X - 1, old.Y - 1, old.W + 2, old.H + 2); f.subFocus := NIL;
  365.             SetReader(f, old.X, old.Y, r, info); Texts.ReadElem(r);
  366.             IF r.elem # NIL THEN msg.focus := FALSE; msg.elemFrame := old; msg.frame := f;
  367.                 r.elem.handle(r.elem, msg)
  368.             END
  369.         END;
  370.         IF new # NIL THEN info := InfoAbout(f, cap);
  371.             IF info # NIL THEN Defocus(f);
  372.                 IF HasOverlap(info) THEN BringToFront(cap) END;
  373.                 IF cap = f.selBeg.cap THEN RemoveSelection(f)END;
  374.                 Texts.OpenReader(r, cap, Offset(cap, new.X - f.X - info.x)); Texts.ReadElem(r);
  375.                 msg.focus := TRUE; msg.elemFrame := new; msg.frame := f; r.elem.handle(r.elem, msg);
  376.                 InvertRect(f, new.X - 1, new.Y - 1, new.W + 2, new.H + 2); f.subFocus := new
  377.             END
  378.         END
  379.     END PassSubFocus;
  380. (* Focus *)
  381.     PROCEDURE SetCaret*(f: Frame; cap: Caption; pos: LONGINT);
  382.         VAR x, y: INTEGER; info: CapInfo;
  383.     BEGIN
  384.         IF pos < 0 THEN pos := 0 ELSIF pos > cap.len THEN pos := cap.len END;
  385.         IF ~((f.focus = CaretFocus) & (f.focusPos.cap = cap) & (f.focusPos.pos = pos)) THEN info := InfoAbout(f, cap);
  386.             IF info # NIL THEN Defocus(f); PassSubFocus(f, NIL, NIL);
  387.                 IF HasOverlap(info) THEN BringToFront(cap) END;
  388.                 x := f.X + info.x + Space + Width(cap, 0, pos); y := f.Y + f.H + info.y + info.baseH;
  389.                 IF (f.X <= x) & (x < f.X + f.W) & (f.Y <= y) & (y < f.Y + f.H) THEN Oberon.PassFocus(Viewers.This(f.X, f.Y));
  390.                     f.focus := CaretFocus; f.focusPos.cap := cap; f.focusPos.pos := pos; f.focusPos.x := x; f.focusPos.y := y;
  391.                     FlipCaret(f, x, y)
  392.                 END
  393.             END
  394.         END
  395.     END SetCaret;
  396.     PROCEDURE SetFocus*(f: Frame; x, y: INTEGER);
  397.     BEGIN
  398.         IF (f.X <= x) & (x < f.X + f.W) & (f.Y <= y) & (y < f.Y + f.H) THEN
  399.             IF ~((f.focus = PointFocus) & (f.focusPos.x = x) & (f.focusPos.y = y)) THEN PassSubFocus(f, NIL, NIL);
  400.                 Oberon.PassFocus(Viewers.This(f.X, f.Y));
  401.                 f.focus := PointFocus; f.focusPos.x := x; f.focusPos.y := y;
  402.                 FlipCross(f, x, y)
  403.             END
  404.         END
  405.     END SetFocus;
  406. (* Selection *)
  407.     PROCEDURE SetSelection*(f: Frame; cap: Caption; beg, end: LONGINT);
  408.         VAR x, y, w, h: INTEGER; info: CapInfo;
  409.     BEGIN
  410.         IF beg < 0 THEN beg := 0 END;
  411.         IF end > cap.len THEN end := cap.len END;
  412.         IF f.hasSel & (f.selBeg.cap = cap) & (f.selBeg.pos = beg) & (beg < end) THEN
  413.             IF f.selEnd.pos # end THEN info := InfoAbout(f, cap);
  414.                 x := f.X + info.x + Space + Width(cap, 0, end); w := f.selEnd.x - x;
  415.                 f.selEnd.pos := end; f.selEnd.x := x;
  416.                 IF w < 0 THEN x := x + w; w := -w END;
  417.                 Display.ReplConstC(f, Display.white, x, f.selBeg.y, w, f.selEnd.y - f.selBeg.y, Display.invert)
  418.             END;
  419.             f.selTime := Oberon.Time()
  420.         ELSE RemoveSelection(f);
  421.             info := InfoAbout(f, cap);
  422.             IF (info # NIL) & (beg < end) THEN
  423.                 IF HasOverlap(info) THEN BringToFront(cap) END;
  424.                 IF (f.subFocus # NIL) & (info = ThisCaption(f, f.subFocus.X, f.subFocus.Y)) THEN PassSubFocus(f, NIL, NIL) END;
  425.                 x := f.X + info.x + Space + Width(cap, 0, beg); w := Width(cap, beg, end);
  426.                 y := f.Y + f.H + info.y + Space; h := info.h - 2 * Space;
  427.                 f.hasSel := TRUE; f.selTime := Oberon.Time();
  428.                 f.selBeg.cap := cap; f.selBeg.pos := beg; f.selBeg.x := x; f.selBeg.y := y;
  429.                 f.selEnd.cap := cap; f.selEnd.pos := end; f.selEnd.x := x + w; f.selEnd.y := y + h;
  430.                 Display.ReplConstC(f, Display.white, x, y, w, h, Display.invert)
  431.             END
  432.         END
  433.     END SetSelection;
  434. (* marked captions *)
  435.     PROCEDURE IsMarked*(f: Frame; cap: Caption): BOOLEAN;
  436.         VAR info: CapInfo;
  437.     BEGIN info := InfoAbout(f, cap); RETURN (info # NIL) & info.marked
  438.     END IsMarked;
  439.     PROCEDURE MarkCaption*(f: Frame; cap: Caption);
  440.         VAR info: CapInfo;
  441.     BEGIN info := InfoAbout(f, cap);
  442.         IF (info # NIL) & ~info.marked THEN InvertRect(f, f.X + info.x, f.Y + f.H + info.y, info.w - 1, info.h - 1);
  443.             info.marked := TRUE
  444.         END
  445.     END MarkCaption;
  446. (* Neutralize *)
  447.     PROCEDURE NeutralizeArea(f: Frame; x, y, w, h: INTEGER);
  448.         VAR r, t, fx, fy: INTEGER; c: CapInfo; f1: Display.Frame;
  449.     BEGIN UnmarkAllCaptions(f); r := x + w; t := y + h;
  450.         IF f.subFocus # NIL THEN f1 := f.subFocus;
  451.             IF (f.X + x < f1.X + f1.W) & (f1.X < f.X + r) & (f.Y + f.H + y < f1.Y + f1.H) & (f1.Y < f.Y + f.H + t) THEN
  452.                 PassSubFocus(f, NIL, NIL)
  453.             END
  454.         END;
  455.         IF f.focus = PointFocus THEN fx := SHORT(f.focusPos.x - f.X + f.x0); fy := SHORT(f.focusPos.y - f.Y - f.H + f.y0);
  456.             IF (x <= fx + 5) & (fx - 5 < r) & (y <= fy + 5) & (fy - 5 < t) THEN Defocus(f) END
  457.         ELSIF f.focus = CaretFocus THEN c := InfoAbout(f, f.focusPos.cap);
  458.             IF (x < c.x + c.w) & (c.x < r) & (y < c.y + c.h) & (c.y < t) THEN Defocus(f) END
  459.         END;
  460.         IF f.hasSel THEN c := InfoAbout(f, f.selBeg.cap);
  461.             IF (x < c.x + c.w) & (c.x < r) & (y < c.y + c.h) & (c.y < t) THEN RemoveSelection(f) END
  462.         END
  463.     END NeutralizeArea;
  464.     PROCEDURE Neutralize*(f: Frame);
  465.     BEGIN UnmarkAllCaptions(f); PassSubFocus(f, NIL, NIL); Defocus(f); RemoveSelection(f)
  466.     END Neutralize;
  467. (* Drawing *)
  468.     PROCEDURE DrawBackground(f: Frame; x, y, w, h: INTEGER);
  469.         VAR g, r, t, x0: INTEGER; s: LONGINT;
  470.     BEGIN Display.ReplConstC(f, Display.black, x, y, w, h, Display.replace);
  471.         g := SHORT(f.grid);
  472.         IF g > 0 THEN r := x + w; t := y + h;
  473.             WHILE g < 20 DO g := g * 2 END;
  474.             s := f.x0 - f.X; x0 := SHORT(((x + s - 1) DIV g + 1) * g - s);
  475.             s := f.y0 - f.Y - f.H; y := SHORT(((y + s - 1) DIV g + 1) * g - s);
  476.             WHILE y < t DO x := x0;
  477.                 WHILE x < r DO Display.DotC(f, Display.white, x, y, Display.replace); x := x + g END;
  478.                 y := y + g
  479.             END
  480.         END
  481.     END DrawBackground;
  482.     PROCEDURE DrawCaption(f: Frame; cap: CapInfo; beg, end: LONGINT);
  483.         VAR x, y, w, h, baseH, pX, pY, voff, dx, chX, chY, chW, chH: INTEGER; ch: CHAR; t: Caption;
  484.             pat: Display.Pattern; r: Texts.Reader; msg: TextFrames.DisplayMsg;
  485.     BEGIN y := f.Y + f.H + cap.y; h := cap.h; baseH := cap.baseH; t := cap.text; pX := f.X; pY := f.Y + f.H;
  486.         x := f.X + cap.x; w := Width(t, beg, end);
  487.         IF beg # 0 THEN x := x + Space + Width(t, 0, beg) ELSE w := w + Space END;
  488.         IF end = t.len THEN w := w + Space END;
  489.         Oberon.RemoveMarks(x, y, w, h); CloseSubFrames(f, x, y, w, h);
  490.         MarkOverlap(f.x0 + x - f.X, f.y0 + y - (f.Y + f.H), w, h, cap.next);
  491.         Display.ReplConstC(f, Display.black, x, y, w, h, Display.replace);
  492.         Display.ReplPatternC(f, Display.white, framePat, x, y, w, 1, pX, pY, Display.replace);
  493.         Display.ReplPatternC(f, Display.white, framePat, x, y + h - 1, w, 1, pX, pY, Display.replace);
  494.         IF beg = 0 THEN x := x; w := w;
  495.             Display.ReplPatternC(f, Display.white, framePat, x, y, 1, h, pX, pY, Display.replace)
  496.         END;
  497.         IF end = t.len THEN w := w;
  498.             Display.ReplPatternC(f, Display.white, framePat, x + w - 1, y, 1, h, pX, pY, Display.replace);
  499.             Display.ReplConstC(f, Display.white, x + w - Space, y, Space, Space, Display.replace)
  500.         END;
  501.         IF beg = 0 THEN x := x + Space END;
  502.         Texts.OpenReader(r, t, beg);
  503.         WHILE (beg < end) & (x < f.X + f.W) DO Texts.Read(r, ch); INC(beg);
  504.             voff := r.fnt.height * r.voff DIV 64;
  505.             IF r.elem # NIL THEN
  506.                 IF ~(r.elem IS TextFrames.Parc) THEN
  507.                     msg.prepare := TRUE; msg.fnt := r.fnt; msg.col := r.col; msg.pos := beg - 1; msg.indent := 0;
  508.                     msg.Y0 := r.fnt.minY;
  509.                     chW := SHORT(r.elem.W DIV TextFrames.Unit); chH := SHORT(r.elem.H DIV TextFrames.Unit);
  510.                     r.elem.handle(r.elem, msg);
  511.                     chY := y + baseH + voff;
  512.                     IF (f.X <= x) & (f.X + f.W >= x + chW) & (f.Y <= chY + msg.Y0) & (f.Y + f.H >= chY + msg.Y0 + chH) THEN
  513.                         msg.prepare := FALSE; msg.X0 := x; msg.Y0 := chY + r.fnt.minY;
  514.                         msg.frame := f; msg.elemFrame := NIL;
  515.                         r.elem.handle(r.elem, msg);
  516.                         IF msg.elemFrame # NIL THEN msg.elemFrame.next := f.dsc; f.dsc := msg.elemFrame END;
  517.                     END;
  518.                     x := x + chW
  519.                 END;
  520.             ELSE Display.GetChar(r.fnt.raster, ch, dx, chX, chY, chW, chH, pat);
  521.                 Display.CopyPatternC(f, r.col, pat, x + chX, y + baseH + voff + chY, Display.replace);
  522.                 x := x + dx
  523.             END
  524.         END;
  525.         cap.ok := TRUE
  526.     END DrawCaption;
  527.     PROCEDURE Restore(f: Frame);
  528.         VAR c: CapInfo;
  529.     BEGIN c := f.info;
  530.         WHILE c # NIL DO
  531.             IF ~c.ok THEN NeutralizeArea(f, c.x, c.y, c.w, c.h); DrawCaption(f, c, 0, c.text.len) END;
  532.             c := c.next
  533.         END
  534.     END Restore;
  535. (* View Modification *)
  536.     PROCEDURE Reduce*(f: Frame; y, h, dy: INTEGER);
  537.         VAR boarder: INTEGER; c, p: CapInfo; df: Display.Frame;
  538.     BEGIN Oberon.RemoveMarks(f.X, f.Y, f.W, f.H); Neutralize(f);
  539.         IF h # 0 THEN
  540.             IF dy # 0 THEN Display.CopyBlock(f.X, y + dy, f.W, h, f.X, y, Display.replace); ShiftSubFrames(f, y + dy, h, -dy)
  541.             ELSE CloseSubFrames(f, f.X, f.Y, f.W, f.H - h)
  542.             END;
  543.             f.Y := y; f.H := h;
  544.             boarder := -f.H; c := f.info; p := NIL;
  545.             WHILE c # NIL DO
  546.                 IF c.y + c.h > boarder THEN
  547.                     IF ~c.ok OR (c.y < boarder) THEN DrawCaption(f, c, 0, c.text.len) END;
  548.                     p := c
  549.                 ELSIF p = NIL THEN f.info := c.next
  550.                 ELSE p.next := c.next
  551.                 END;
  552.                 c := c.next
  553.             END
  554.         ELSE CloseSubFrames(f, f.X, f.Y, f.W, f.H); f.Y := y; f.H := h; f.info := NIL
  555.         END;
  556.         df := f; df.X := f.X; df.W := f.W; df.Y := f.Y; df.H := f.H
  557.     END Reduce;
  558.     PROCEDURE Extend*(f: Frame; y, h, dy: INTEGER);
  559.         VAR l, r, b, t: LONGINT; cw, ch, baseH, dh: INTEGER; info, p, q: CapInfo; c: Caption;
  560.     BEGIN Oberon.RemoveMarks(f.X, y, f.W, h); Neutralize(f);
  561.         IF dy # 0 THEN Display.CopyBlock(f.X, y, f.W, f.H, f.X, y + dy, Display.replace); ShiftSubFrames(f, y, f.H, dy) END;
  562.         l := f.x0; r := l + f.W; b := f.y0 - h; t := f.y0 - f.H;
  563.         dh := h - f.H; f.Y := y; f.H := h;
  564.         DrawBackground(f, f.X, f.Y, f.W, dh);
  565.         c := f.panel.first; q := f.info; p := NIL;
  566.         WHILE c # NIL DO
  567.             IF (q # NIL) & (q.text = c) THEN
  568.                 IF ~q.ok OR (c.y < t) THEN DrawCaption(f, q, 0, c.len) END;
  569.                 p := q; q := q.next
  570.             ELSIF (c.x < r) & (c.y < t) THEN
  571.                 GetBoundingBox(c, 0, c.len, cw, ch, baseH); cw := cw + 2 * Space; ch := ch + 2 * Space;
  572.                 IF (l < c.x + cw) & (b < c.y + ch) THEN info := NewInfo(f, c, cw, ch, baseH + Space);
  573.                     IF p # NIL THEN p.next := info ELSE f.info := info END;
  574.                     info.next := q; p := info;
  575.                     DrawCaption(f, info, 0, c.len)
  576.                 END
  577.             END;
  578.             c := c.next
  579.         END
  580.     END Extend;
  581.     PROCEDURE Scroll*(f: Frame; dx, dy: LONGINT);
  582.         VAR y, h: INTEGER;
  583.     BEGIN
  584.         IF (dx # 0) OR (dy # 0) THEN y := f.Y; h := f.H;
  585.             Reduce(f, y + h, 0, 0);
  586.             f.x0 := f.x0 - dx; f.y0 := f.y0 - dy;
  587.             Extend(f, y, h, 0)
  588.         END
  589.     END Scroll;
  590. (* Update *)
  591.     PROCEDURE MarkMenu(f: Frame);
  592.         VAR ch: CHAR; v: Viewers.Viewer; t: Texts.Text; r: Texts.Reader;
  593.     BEGIN v := Viewers.This(f.X, f.Y);
  594.         IF (v IS MenuViewers.Viewer) & (v.dsc # NIL) & (v.dsc IS TextFrames.Frame) THEN t := v.dsc(TextFrames.Frame).text;
  595.             IF t.len > 0 THEN Texts.OpenReader(r, t, t.len - 1); Texts.Read(r, ch) ELSE ch := 0X END;
  596.             IF ch # "!" THEN Texts.Write(w, "!"); Texts.Append(t, w.buf) END
  597.         END
  598.     END MarkMenu;
  599.     PROCEDURE ClearScreen(f: Frame; x, y: LONGINT; w, h: INTEGER);
  600.     VAR x1, y1: INTEGER;
  601.     BEGIN MarkOverlap(x, y, w, h, f.info);
  602.         x1 := SHORT(f.X + x - f.x0); y1 := SHORT(f.Y + f.H + y - f.y0);
  603.         Oberon.RemoveMarks(x1, y1, w, h); CloseSubFrames(f, x1, y1, w, h);
  604.         DrawBackground(f, x1, y1, w, h)
  605.     END ClearScreen;
  606.     PROCEDURE Update*(f: Frame; op: LONGINT; cap: Caption; beg, end: LONGINT);
  607.         VAR w, h, baseH: INTEGER; info: CapInfo;
  608.     BEGIN MarkMenu(f);
  609.         info := InfoAbout(f, cap);
  610.         IF info # NIL THEN
  611.             IF op = tofront THEN NeutralizeArea(f, info.x, info.y, info.w, info.h);
  612.                 RemoveInfo(f, info); InsertInfo(f, info); DrawCaption(f, info, 0, cap.len)
  613.             ELSIF op = move THEN NeutralizeArea(f, info.x, info.y, info.w, info.h);
  614.                 ClearScreen(f, f.x0 + info.x, f.y0 + info.y, info.w, info.h);
  615.                 info.x := SHORT(cap.x - f.x0); info.y := SHORT(cap.y - f.y0);
  616.                 NeutralizeArea(f, info.x, info.y, info.w, info.h);
  617.                 IF (info.x < f.W) & (info.x + info.w > 0) & (info.y < 0) & (info.y + info.h > -f.H) THEN
  618.                     DrawCaption(f, info, 0, cap.len)
  619.                 ELSE RemoveInfo(f, info)
  620.                 END;
  621.                 Restore(f)
  622.             ELSIF op = remove THEN NeutralizeArea(f, info.x, info.y, info.w, info.h); RemoveInfo(f, info);
  623.                 ClearScreen(f, cap.x, cap.y, info.w, info.h); Restore(f)
  624.             ELSE NeutralizeArea(f, info.x, info.y, info.w, info.h);
  625.                 GetBoundingBox(cap, 0, cap.len, w, h, baseH); w := w + 2 * Space; h := h + 2 * Space;
  626.                 NeutralizeArea(f, info.x, info.y, w, h);
  627.                 IF w < info.w THEN ClearScreen(f, cap.x + w, cap.y, info.w - w, info.h) END;
  628.                 IF h < info.h THEN ClearScreen(f, cap.x, cap.y + h, info.w, info.h - h) END;
  629.                 IF ((op # Texts.delete) & (op # Texts.insert)) OR (h # info.h) THEN beg := 0 END;
  630.                 info.w := w; info.h := h; info.baseH := baseH + Space;
  631.                 IF (info.x < f.W) & (info.x + info.w > 0) & (info.y < 0) & (info.y + info.h > -f.H) THEN
  632.                     DrawCaption(f, info, beg, cap.len)
  633.                 ELSE RemoveInfo(f, info)
  634.                 END;
  635.                 Restore(f)
  636.             END
  637.         ELSIF (op # remove) & (op # Texts.delete) & (cap.x < f.x0 + f.W) & (cap.y < f.y0) THEN
  638.             GetBoundingBox(cap, 0, cap.len, w, h, baseH); w := w + 2 * Space; h := h + 2 * Space;
  639.             IF (f.x0 < cap.x + w) & (f.y0 - f.H < cap.y + h) THEN
  640.                 NeutralizeArea(f, SHORT(cap.x), SHORT(cap.y), w, h);
  641.                 info := NewInfo(f, cap, w, h, baseH + Space); InsertInfo(f, info);
  642.                 DrawCaption(f, info, 0, cap.len)
  643.             END
  644.         END
  645.     END Update;
  646. (* Commands *)
  647.     PROCEDURE Call*(f: Frame; cap: Caption; pos: LONGINT; load: BOOLEAN);
  648.         VAR i, j, res: INTEGER; par: Oberon.ParList; s: Texts.Scanner;
  649.     BEGIN Texts.OpenScanner(s, cap, pos); Texts.Scan(s);
  650.         IF s.class = Texts.Name THEN i := 0;
  651.             WHILE (i < s.len) & (s.s[i] # ".") DO INC(i) END;
  652.             j := i + 1;
  653.             WHILE (j < s.len) & (s.s[j] # ".") DO INC(j) END;
  654.             IF (j = s.len) & (s.s[i] = ".") THEN
  655.                 NEW(par); par.text := cap; par.pos := Texts.Pos(s) - 1; par.frame := f; Oberon.Call(s.s, par, load, res);
  656.                 IF res > 0 THEN Texts.WriteString(w, "Call error: "); Texts.WriteString(w, Modules.importing);
  657.                     IF res = 1 THEN Texts.WriteString(w, " not found");
  658.                     ELSIF res = 2 THEN Texts.WriteString(w, " not an obj-file");
  659.                     ELSIF res = 3 THEN Texts.WriteString(w, " imports ");
  660.                         Texts.WriteString(w, Modules.imported); Texts.WriteString(w, " with bad key")
  661.                     ELSIF res = 4 THEN Texts.WriteString(w, " corrupted obj file")
  662.                     ELSIF res = 6 THEN Texts.WriteString(w, " has too many imports")
  663.                     ELSIF res = 7 THEN Texts.WriteString(w, " not enough space")
  664.                     END;
  665.                     Texts.WriteLn(w); Texts.Append(Oberon.Log, w.buf)
  666.                 ELSIF res < 0 THEN INC(i);
  667.                     WHILE i < s.len DO Texts.Write(w, s.s[i]); INC(i) END;
  668.                     Texts.WriteString(w, " not found"); Texts.WriteLn(w); Texts.Append(Oberon.Log, w.buf)
  669.                 END
  670.             END
  671.         END
  672.     END Call;
  673.     PROCEDURE MarkArea*(f: Frame; x, y, w, h: INTEGER);
  674.         VAR l, r, b, t: INTEGER; info: CapInfo;
  675.     BEGIN
  676.         IF w < 0 THEN x := x + w; w := -w END;
  677.         IF h < 0 THEN y := y + h; h := -h END;
  678.         l := x - f.X; r := l + w; b := y - (f.Y + f.H); t := b + h; info := f.info;
  679.         WHILE info # NIL DO
  680.             IF (l <= info.x) & (r >= info.x + info.w) & (b <= info.y) & (t >= info.y + info.h) THEN MarkCaption(f, info.text) END;
  681.             info := info.next
  682.         END
  683.     END MarkArea;
  684.     PROCEDURE RememberMarked(f: Frame; VAR mem: ARRAY OF CapInfo);
  685.         VAR i: LONGINT; info: CapInfo;
  686.     BEGIN info := f.info; i := 0;
  687.         WHILE info # NIL DO
  688.             IF info.marked THEN mem[i] := info; INC(i) END;
  689.             info := info.next
  690.         END;
  691.         mem[i] := NIL
  692.     END RememberMarked;
  693.     PROCEDURE MoveMarked*(f: Frame; dX, dY: INTEGER);
  694.         VAR i: LONGINT; info: CapInfo; text: Caption; mem: ARRAY 1024 OF CapInfo;
  695.     BEGIN RememberMarked(f, mem);
  696.         i := 0; info := mem[0];
  697.         WHILE info # NIL DO text := info.text; Move(text, text.x + dX, text.y + dY); INC(i); info := mem[i] END;
  698.         i := 0; info := mem[0];
  699.         WHILE info # NIL DO MarkCaption(f, info.text); INC(i); info := mem[i] END
  700.     END MoveMarked;
  701.     PROCEDURE DeleteMarked*(f: Frame);
  702.         VAR i: LONGINT; info: CapInfo; text: Caption; mem: ARRAY 1024 OF CapInfo;
  703.     BEGIN RememberMarked(f, mem);
  704.         i := 0; info := mem[0]; PassSubFocus(f, NIL, NIL);
  705.         WHILE info # NIL DO text := info.text; Texts.Delete(text, 0, text.len); INC(i); info := mem[i] END;
  706.         IF i = 1 THEN info := mem[0]; SetFocus(f, f.X + info.x, f.Y + f.H + info.y) END
  707.     END DeleteMarked;
  708. (* Input *)
  709.     PROCEDURE CallMenuCommand(f: Frame; cap: Caption; beg: LONGINT);
  710.         VAR ch: CHAR; c: Caption; v: Viewers.Viewer; t: Texts.Text; buf: Texts.Buffer; r: Texts.Reader;
  711.     BEGIN v := Viewers.This(f.X, f.Y);
  712.         IF (v IS MenuViewers.Viewer) & (v.dsc # NIL) & (v.dsc IS TextFrames.Frame) THEN
  713.             t := v.dsc(TextFrames.Frame).text; Texts.OpenReader(r, t, 0); Texts.Read(r, ch);
  714.             IF ~r.eot THEN NEW(c);
  715.                 IF ch = 22X THEN
  716.                     REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch = 22X);
  717.                     OpenCaption(c, t, 1, Texts.Pos(r) - 1)
  718.                 ELSE
  719.                     REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch = "|");
  720.                     OpenCaption(c, t, 0, Texts.Pos(r) - 1)
  721.                 END;
  722.                 NEW(buf); Texts.OpenBuf(buf); Texts.Save(cap, beg, cap.len, buf); Texts.Append(c, buf);
  723.                 Call(f, c, 0, FALSE)
  724.             END
  725.         END
  726.     END CallMenuCommand;
  727.     PROCEDURE SetAttributes(VAR w: Texts.Writer; c: Caption; pos: LONGINT; fnt: Fonts.Font; col, voff: SHORTINT);
  728.         VAR ch: CHAR; r: Texts.Reader;
  729.     BEGIN Texts.OpenReader(r, c, pos); Texts.Read(r, ch);
  730.         IF (r.eot OR (ch <= " ")) & (pos > 0) THEN DEC(pos); Texts.OpenReader(r, c, pos); Texts.Read(r, ch) END;
  731.         IF ~r.eot THEN Texts.SetFont(w, r.fnt); Texts.SetColor(w, r.col); Texts.SetOffset(w, r.voff)
  732.         ELSE Texts.SetFont(w, fnt); Texts.SetColor(w, col); Texts.SetOffset(w, voff)
  733.         END
  734.     END SetAttributes;
  735.     PROCEDURE Visible(fnt: Fonts.Font; ch: CHAR): BOOLEAN;
  736.         VAR pat: Display.Pattern; dx, x, y, w, h: INTEGER;
  737.     BEGIN Display.GetChar(fnt.raster, ch, dx, x, y, w, h, pat); RETURN dx > 0
  738.     END Visible;
  739.     PROCEDURE Consume*(f: Frame; ch: CHAR; fnt: Fonts.Font; col, voff: SHORTINT);
  740.         VAR pos: LONGINT; c: Caption; info: CapInfo;
  741.     BEGIN
  742.         IF f.focus = CaretFocus THEN c := f.focusPos.cap; pos := f.focusPos.pos;
  743.             IF ch = 0DX THEN (* CR *)
  744.                 SetSelection(f, c, 0, pos)
  745.             ELSIF ch = 0AX THEN (* LF *)
  746.                 CallMenuCommand(f, c, 0)
  747.             ELSIF ch = 0ACX THEN (* BRK *)
  748.                 Call(f, c, 0, FALSE); Texts.Delete(c, 0, c.len)
  749.             ELSIF ch = 0C4X THEN (* <- *)
  750.                 IF pos > 0 THEN SetCaret(f, c, pos - 1) END
  751.             ELSIF ch = 0C3X THEN (* -> *)
  752.                 IF pos < c.len THEN SetCaret(f, c, pos + 1) END
  753.             ELSIF ch = 7FX THEN (* DEL *)
  754.                 IF pos # 0 THEN
  755.                     IF c.len # 1 THEN Texts.Delete(c, pos - 1, pos); SetCaret(f, c, pos - 1)
  756.                     ELSE info := InfoAbout(f, c); Texts.Delete(c, pos - 1, pos); SetFocus(f, f.X + info.x, f.Y + f.H + info.y)
  757.                     END
  758.                 END
  759.             ELSIF (ch = 9X) OR (ch >= " ") THEN SetAttributes(wattr, c, pos, fnt, col, voff);
  760.                 IF Visible(wattr.fnt, ch) THEN Texts.Write(wattr, ch); Texts.Insert(c, pos, wattr.buf);
  761.                     SetCaret(f, c, pos + 1)
  762.                 END
  763.             END
  764.         ELSIF (f.focus = PointFocus) & ((ch = 9X) OR ((ch >= " ") & (ch # 7FX))) & Visible(fnt, ch) THEN
  765.             NEW(c); OpenCaption(c, NIL, 0, 0);
  766.             Insert(f.panel, c, f.focusPos.x - f.X + f.x0, f.focusPos.y - (f.Y + f.H) + f.y0);
  767.             SetAttributes(wattr, c, 0, fnt, col, voff); Texts.Write(wattr, ch); Texts.Insert(c, 0, wattr.buf);
  768.             SetCaret(f, c, 1)
  769.         END
  770.     END Consume;
  771.     PROCEDURE ConsumeElem*(f: Frame; e: Texts.Elem);
  772.         VAR pos: LONGINT; c: Caption;
  773.     BEGIN
  774.         IF f.focus = CaretFocus THEN c := f.focusPos.cap; pos := f.focusPos.pos;
  775.             SetAttributes(wattr, c, pos, Fonts.Default, Display.white, 0);
  776.             Texts.WriteElem(wattr, e); Texts.Insert(c, pos, wattr.buf); SetCaret(f, c, pos + 1)
  777.         ELSIF f.focus = PointFocus THEN
  778.             NEW(c); OpenCaption(c, NIL, 0, 0);
  779.             Insert(f.panel, c, f.focusPos.x - f.X + f.x0, f.focusPos.y - (f.Y + f.H) + f.y0);
  780.             SetAttributes(wattr, c, 0, Fonts.Default, Display.white, 0);
  781.             Texts.WriteElem(wattr, e); Texts.Insert(c, 0, wattr.buf); SetCaret(f, c, 1)
  782.         END
  783.     END ConsumeElem;
  784.     PROCEDURE CopyOver*(f: Frame; text: Texts.Text; beg, end: LONGINT);
  785.         VAR pos: LONGINT; buf: Texts.Buffer; c: Caption;
  786.     BEGIN
  787.         IF f.focus = CaretFocus THEN c := f.focusPos.cap; pos := f.focusPos.pos;
  788.             NEW(buf); Texts.OpenBuf(buf); Texts.Save(text, beg, end, buf); Texts.Insert(c, pos, buf);
  789.             SetCaret(f, c, pos + end - beg)
  790.         ELSIF f.focus = PointFocus THEN NEW(c); OpenCaption(c, text, beg, end);
  791.             Insert(f.panel, c, f.x0 + f.focusPos.x - f.X, f.y0 + f.focusPos.y - (f.Y + f.H));
  792.             SetCaret(f, c, c.len)
  793.         END
  794.     END CopyOver;
  795. (* Mouse Tracking *)
  796.     PROCEDURE AlignToGrid(f: Frame; VAR x, y: INTEGER);
  797.         VAR g, h, s: LONGINT;
  798.     BEGIN g := f.grid; h := g DIV 2;
  799.         IF g > 0 THEN s := f.x0 - f.X; x := SHORT(((x + s + h) DIV g) * g - s);
  800.             s := f.y0 - f.Y - f.H; y := SHORT(((y + s + h) DIV g) * g - s)
  801.         END
  802.     END AlignToGrid;
  803.     PROCEDURE TrackMouse(f: Frame; VAR x, y: INTEGER; VAR keys, keySum: SET; align: BOOLEAN);
  804.         VAR keys0: SET; x0, y0: INTEGER;
  805.     BEGIN keys0 := keys; x0 := x; y0 := y;
  806.         REPEAT Input.Mouse(keys, x, y); keySum := keySum + keys;
  807.             Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y);
  808.             IF x < f.X THEN x := f.X ELSIF x >= f.X + f.W THEN x := f.X + f.W END;
  809.             IF y < f.Y THEN y := f.Y ELSIF y >= f.Y + f.H THEN y := f.Y + f.H END;
  810.             IF align THEN AlignToGrid(f, x, y) END
  811.         UNTIL (keys # keys0) OR (x # x0) OR (y # y0)
  812.     END TrackMouse;
  813.     PROCEDURE TrackFocus*(f: Frame; VAR x, y: INTEGER; VAR keySum: SET);
  814.         VAR keys: SET;
  815.     BEGIN AlignToGrid(f, x, y);
  816.         REPEAT SetFocus(f, x, y); TrackMouse(f, x, y, keys, keySum, TRUE) UNTIL keys = {}
  817.     END TrackFocus;
  818.     PROCEDURE TrackCaption*(f: Frame; cap: Caption; VAR x, y, dx, dy: INTEGER; VAR keySum: SET);
  819.         VAR keys: SET; x0, y0, x1, y1: INTEGER; info: CapInfo;
  820.     BEGIN AlignToGrid(f, x, y); x0 := x; y0 := y;
  821.         info := InfoAbout(f, cap); x1 := f.X + info.x; y1 := f.Y + f.H + info.y; AlignToGrid(f, x1, y1);
  822.         REPEAT InvertRect(f, x1, y1, info.w - 1, info.h - 1);
  823.             TrackMouse(f, x, y, keys, keySum, TRUE); InvertRect(f, x1, y1, info.w - 1, info.h - 1);
  824.             x1 := x1 - x0 + x; x0 := x; y1 := y1 - y0 + y; y0 := y
  825.         UNTIL keys = {};
  826.         dx := x1 - f.X - info.x; dy := y1 - f.Y - f.H - info.y
  827.     END TrackCaption;
  828.     PROCEDURE TrackArea*(f: Frame; VAR x, y: INTEGER; VAR keySum: SET);
  829.         VAR keys: SET; x0, y0, x1, y1: INTEGER;
  830.     BEGIN x0 := x; y0 := y; x1 := x; y1 := y;
  831.         REPEAT TrackMouse(f, x, y, keys, keySum, FALSE);
  832.             IF x # x1 THEN InvertRect(f, x1, y0, x - x1, y1 - y0); x1 := x END;
  833.             IF y # y1 THEN InvertRect(f, x0, y1, x1 - x0, y - y1); y1 := y END
  834.         UNTIL keys = {};
  835.         InvertRect(f, x, y, x0 - x, y0 - y)
  836.     END TrackArea;
  837.     PROCEDURE TrackCaret*(f: Frame; cap: Caption; VAR x, y: INTEGER; VAR keySum: SET);
  838.         VAR keys: SET;
  839.     BEGIN
  840.         REPEAT SetCaret(f, cap, Offset(cap, SHORT(x - f.X + f.x0 - cap.x))); TrackMouse(f, x, y, keys, keySum, FALSE)
  841.         UNTIL keys = {}
  842.     END TrackCaret;
  843.     PROCEDURE LocateWord(f: Frame; cap: Caption; x0: INTEGER; VAR beg: LONGINT; VAR x, w: INTEGER);
  844.         VAR pos, end: LONGINT; ch: CHAR; r: Texts.Reader;
  845.     BEGIN pos := Offset(cap, SHORT(x0 - f.X + f.x0 - cap.x - Space)) + 1; beg := 0; end := 0;
  846.         Texts.OpenReader(r, cap, 0); Texts.Read(r, ch);
  847.         WHILE ~r.eot & (Texts.Pos(r) <= pos) DO beg := Texts.Pos(r) - 1;
  848.             IF ~r.eot & (r.elem # NIL) THEN Texts.Read(r, ch)
  849.             ELSE
  850.                 WHILE ~r.eot & (ch > " ") DO Texts.Read(r, ch) END
  851.             END;
  852.             end := Texts.Pos(r) - 1;
  853.             WHILE ~r.eot & (r.elem = NIL) & (ch <= " ") DO Texts.Read(r, ch) END
  854.         END;
  855.         x := SHORT(f.X + cap.x + Space + Width(cap, 0, beg) - f.x0); w := Width(cap, beg, end)
  856.     END LocateWord;
  857.     PROCEDURE TrackWord*(f: Frame; cap: Caption; x, y: INTEGER; VAR keySum: SET; VAR beg: LONGINT);
  858.         CONST H = 2;
  859.         VAR x0, y0, x1, w0, w1: INTEGER; keys: SET;
  860.     BEGIN y0 := SHORT(cap.y - f.y0 + f.Y + f.H + Space);
  861.         LocateWord(f, cap, x, beg, x0, w0); Display.ReplConstC(f, Display.white, x0, y0, w0, H, Display.invert);
  862.         REPEAT TrackMouse(f, x, y, keys, keySum, FALSE);
  863.             LocateWord(f, cap, x, beg, x1, w1);
  864.             IF x0 # x1 THEN
  865.                 Display.ReplConstC(f, Display.white, x0, y0, w0, H, Display.invert);
  866.                 x0 := x1; w0 := w1;
  867.                 Display.ReplConstC(f, Display.white, x0, y0, w0, H, Display.invert);
  868.             END
  869.         UNTIL keys = {};
  870.         Display.ReplConstC(f, Display.white, x0, y0, w0, H, Display.invert)
  871.     END TrackWord;
  872.     PROCEDURE TrackSelection*(f: Frame; cap: Caption; VAR x, y: INTEGER; VAR keySum: SET);
  873.         VAR keys: SET; beg, end: LONGINT;
  874.     BEGIN beg := Offset(cap, SHORT(x - f.X + f.x0 - cap.x - Space));
  875.         IF f.hasSel & (f.selBeg.cap = cap) THEN
  876.             IF beg = cap.len THEN beg := f.selBeg.pos
  877.             ELSIF (f.selBeg.pos = beg) & (f.selEnd.pos = beg + 1) THEN beg := 0
  878.             END
  879.         END;
  880.         REPEAT end := Offset(cap, SHORT(x - f.X + f.x0 - cap.x - Space)) + 1;
  881.             IF end <= beg THEN end := beg + 1 END;
  882.             SetSelection(f, cap, beg, end);
  883.             TrackMouse(f, x, y, keys, keySum, FALSE)
  884.         UNTIL keys = {}
  885.     END TrackSelection;
  886.     PROCEDURE TouchElem*(f: Frame; cap: Caption; VAR x, y: INTEGER; VAR keySum: SET);
  887.         VAR pos: LONGINT; l, b, w, minY, maxY: INTEGER; ch: CHAR; info: CapInfo;
  888.             r: Texts.Reader; msg: TextFrames.TrackMsg;
  889.     BEGIN pos := Offset(cap, SHORT(x - f.X + f.x0 - cap.x - Space)); Texts.OpenReader(r, cap, pos); Texts.Read(r, ch);
  890.         IF (r.elem # NIL) & (keySum = {MM}) THEN info := InfoAbout(f, cap); MeasureChar(r, ch, w, minY, maxY);
  891.             l := f.X + info.x + Space + Width(cap, 0, pos); b := f.Y + f.H + info.y + info.baseH + minY;
  892.             IF (l <= x) & (x < l + w) & (b <= y) & (y < b + maxY - minY)
  893.                 & (f.X <= l) & (l + w < f.X + f.W) & (f.Y <= b) & (b + maxY - minY < f.Y + f.H)
  894.             THEN msg.fnt := r.fnt; msg.col := r.col; msg.pos := pos; msg.frame := f;
  895.                 msg.X := x; msg.Y := y; msg.keys := keySum;
  896.                 msg.X0 := l; msg.Y0 := b - minY + r.fnt.height * r.voff DIV 64 + r.fnt.minY;
  897.                 r.elem.handle(r.elem, msg);
  898.                 Input.Mouse(keySum, x, y)
  899.             END
  900.         END
  901.     END TouchElem;
  902.     PROCEDURE Edit*(f: Frame; x, y: INTEGER; keys: SET);
  903.         VAR k: SET; beg, end, time, grid: LONGINT; x0, y0, dx, dy: INTEGER; ch: CHAR; c: CapInfo;
  904.             subF: Display.Frame; text: Texts.Text; r: Texts.Reader; msg: Oberon.CopyOverMsg;
  905.     BEGIN
  906.         IF keys # {} THEN c := ThisCaption(f, x, y); x0 := x; y0 := y;
  907.             IF c = NIL THEN (* on background *)
  908.                 IF keys = {ML} THEN TrackFocus(f, x, y, keys);
  909.                     IF (keys = {ML, MM}) & (f.focus # NoFocus) THEN Oberon.GetSelection(text, beg, end, time);
  910.                         IF time >= 0 THEN CopyOver(f, text, beg, end) END
  911.                     END
  912.                 ELSIF keys = {MM} THEN
  913.                     REPEAT TrackMouse(f, x, y, k, keys, TRUE) UNTIL k = {};
  914.                     IF keys = {MM} THEN grid := f.grid;
  915.                         IF grid > 0 THEN
  916.                             dx := SHORT(((x - x0 + grid DIV 2) DIV grid) * grid);
  917.                             dy := SHORT(((y - y0 + grid DIV 2) DIV grid) * grid);
  918.                             MoveMarked(f, dx, dy)
  919.                         ELSE MoveMarked(f, x - x0, y - y0)
  920.                         END
  921.                     ELSIF keys = {MM, ML} THEN Scroll(f, f.x0, f.y0)
  922.                     ELSIF keys = {MM, MR} THEN Scroll(f, x - x0, y - y0)
  923.                     END
  924.                 ELSIF keys = {MR} THEN UnmarkAllCaptions(f); TrackArea(f, x, y, keys);
  925.                     IF keys = {MR} THEN MarkArea(f, x, y, x0 - x, y0 - y)
  926.                     ELSIF keys = {MR, ML} THEN MarkArea(f, x, y, x0 - x, y0 - y); DeleteMarked(f)
  927.                     END
  928.                 END
  929.             ELSIF (x >= c.x + c.w + f.X - Space) & (y < c.y + f.Y + f.H + Space) THEN (* on hot spot *)
  930.                 IF keys = {ML} THEN Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y)
  931.                 ELSIF keys = {MM} THEN AlignToGrid(f, x, y); TrackCaption(f, c.text, x, y, dx, dy, keys);
  932.                     IF (keys = {MM}) & ((x # x0) OR (y # y0)) THEN Move(c.text, c.text.x + dx, c.text.y + dy) END
  933.                 ELSIF keys = {MR} THEN
  934.                     InvertRect(f, f.X + c.x, f.Y + f.H + c.y, c.w - 1, c.h - 1);
  935.                     REPEAT TrackMouse(f, x, y, k, keys, FALSE) UNTIL k = {};
  936.                     InvertRect(f, f.X + c.x, f.Y + f.H + c.y, c.w - 1, c.h - 1);
  937.                     IF keys = {MR} THEN
  938.                         IF IsMarked(f, c.text) THEN UnmarkCaption(f, c.text) ELSE MarkCaption(f, c.text) END
  939.                     ELSIF keys = {MR, MM} THEN msg.text := c.text; msg.beg := 0; msg.end := c.text.len;
  940.                         Oberon.FocusViewer.handle(Oberon.FocusViewer, msg)
  941.                     ELSIF (keys = {MR, ML}) THEN PassSubFocus(f, NIL, NIL);
  942.                         Texts.Delete(c.text, 0, c.text.len); SetFocus(f, f.X + c.x, f.Y + f.H + c.y)
  943.                     END
  944.                 END
  945.             ELSE (* within caption *)
  946.                 IF HasOverlap(c) THEN BringToFront(c.text) END;
  947.                 subF := ThisSubFrame(f, x, y);
  948.                 IF (subF # NIL) & (keys = {ML}) THEN
  949.                     REPEAT TrackMouse(f, x, y, k, keys, FALSE) UNTIL k = {};
  950.                     IF keys = {ML} THEN PassSubFocus(f, c.text, subF) END
  951.                 ELSE TouchElem(f, c.text, x, y, keys);
  952.                     IF keys = {ML} THEN TrackCaret(f, c.text, x, y, keys);
  953.                         IF f.focus # NoFocus THEN
  954.                             IF keys = {ML, MM} THEN Oberon.GetSelection(text, beg, end, time);
  955.                                 IF time >= 0 THEN CopyOver(f, text, beg, end) END
  956.                             ELSIF keys = {ML, MR} THEN Oberon.GetSelection(text, beg, end, time);
  957.                                 IF time >= 0 THEN Texts.OpenReader(r, c.text, f.focusPos.pos); Texts.Read(r, ch);
  958.                                     Texts.ChangeLooks(text, beg, end, {0, 1, 2}, r.fnt, r.col, r.voff)
  959.                                 END
  960.                             END
  961.                         END
  962.                     ELSIF keys = {MM} THEN TrackWord(f, c.text, x, y, keys, beg);
  963.                         IF ~(MR IN keys) THEN Call(f, c.text, beg, ML IN keys)
  964.                         ELSIF keys = {MM, MR} THEN CallMenuCommand(f, c.text, beg)
  965.                         END
  966.                     ELSIF keys = {MR} THEN TrackSelection(f, c.text, x, y, keys);
  967.                         IF f.hasSel THEN beg := f.selBeg.pos;
  968.                             IF keys = {MR, ML} THEN Texts.Delete(c.text, beg, f.selEnd.pos);
  969.                                 IF c.text.len > 0 THEN SetCaret(f, c.text, beg) ELSE SetFocus(f, f.X + c.x, f.Y + f.H + c.y) END
  970.                             ELSIF keys = {MR, MM} THEN msg.text := c.text; msg.beg := beg; msg.end := f.selEnd.pos;
  971.                                 Oberon.FocusViewer.handle(Oberon.FocusViewer, msg)
  972.                             END
  973.                         END
  974.                     END
  975.                 END
  976.             END
  977.         ELSE Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y)
  978.         END
  979.     END Edit;
  980. (* Frame Data *)
  981.     PROCEDURE OpenFrame*(f: Frame; handler: Display.Handler; panel: Panel; x0, y0, grid: LONGINT);
  982.     BEGIN f.handle := handler; f.panel := panel; f.x0 := x0; f.y0 := y0; f.grid := grid;
  983.         f.focus := NoFocus; f.hasSel := FALSE; f.subFocus := NIL;
  984.         f.info := NIL;
  985.         f.selBeg.cap := NIL; f.selEnd.cap := NIL; f.focusPos.cap := NIL
  986.     END OpenFrame;
  987.     PROCEDURE Copy*(src, dest: Frame);
  988.     BEGIN OpenFrame(dest, src.handle, src.panel, src.x0, src.y0, src.grid)
  989.     END Copy;
  990. (* General *)
  991.     PROCEDURE NotifyElems* (parent: Frame; VAR msg: Display.FrameMsg);
  992.         VAR f: Display.Frame;
  993.     BEGIN
  994.         IF msg IS TextFrames.NotifyMsg THEN msg(TextFrames.NotifyMsg).frame := parent END;
  995.         f := parent.dsc;
  996.         WHILE f # NIL DO f.handle(f, msg); f := f.next END
  997.     END NotifyElems;
  998.     PROCEDURE Handler*(f: Display.Frame; VAR msg: Display.FrameMsg);
  999.         VAR self, copy: Frame; subf: Display.Frame;
  1000.     BEGIN self := f(Frame);
  1001.         IF msg IS Oberon.CopyMsg THEN NEW(copy); Copy(self, copy); msg(Oberon.CopyMsg).F := copy
  1002.         ELSIF msg IS Oberon.ControlMsg THEN NotifyElems(self, msg);
  1003.             WITH msg: Oberon.ControlMsg DO
  1004.                 IF msg.id = Oberon.neutralize THEN NotifyElems(self, msg); Neutralize(self)
  1005.                 ELSIF msg.id = Oberon.defocus THEN Defocus(self)
  1006.                 END
  1007.             END
  1008.         ELSIF msg IS Oberon.InputMsg THEN
  1009.             WITH msg: Oberon.InputMsg DO subf := self.subFocus;
  1010.                 IF msg.id = Oberon.track THEN
  1011.                     IF (subf # NIL) & (subf.X <= msg.X) & (msg.X < subf.X + subf.W) & (subf.Y <= msg.Y) & (msg.Y < subf.Y + subf.H)
  1012.                     THEN subf.handle(subf, msg)
  1013.                     ELSE Edit(self, msg.X, msg.Y, msg.keys)
  1014.                     END
  1015.                 ELSIF msg.id = Oberon.consume THEN
  1016.                     IF self.focus # NoFocus THEN Consume(self, msg.ch, msg.fnt, msg.col, msg.voff)
  1017.                     ELSIF subf # NIL THEN subf.handle(subf, msg)
  1018.                     END
  1019.                 END
  1020.             END
  1021.         ELSIF msg IS Oberon.CopyOverMsg THEN NotifyElems(self, msg);
  1022.             WITH msg: Oberon.CopyOverMsg DO CopyOver(self, msg.text, msg.beg, msg.end) END
  1023.         ELSIF msg IS Oberon.SelectionMsg THEN NotifyElems(self, msg);
  1024.             WITH msg: Oberon.SelectionMsg DO
  1025.                 IF self.hasSel & (msg.time < self.selTime) THEN
  1026.                     msg.time := self.selTime; msg.text := self.selBeg.cap;
  1027.                     msg.beg := self.selBeg.pos; msg.end := self.selEnd.pos
  1028.                 END
  1029.             END
  1030.         ELSIF msg IS TextFrames.InsertElemMsg THEN subf := self.subFocus;
  1031.             IF self.focus # NoFocus THEN ConsumeElem(self, msg(TextFrames.InsertElemMsg).e)
  1032.             ELSIF subf # NIL THEN subf.handle(subf, msg)
  1033.             END
  1034.         ELSIF msg IS MenuViewers.ModifyMsg THEN
  1035.             WITH msg: MenuViewers.ModifyMsg DO
  1036.                     IF msg.id = MenuViewers.reduce THEN Reduce(self, msg.Y, msg.H, msg.dY)
  1037.                     ELSIF msg.id = MenuViewers.extend THEN Extend(self, msg.Y, msg.H, msg.dY)
  1038.                     END
  1039.                 END
  1040.         ELSIF msg IS UpdateMsg THEN NotifyElems(self, msg);
  1041.             WITH msg: UpdateMsg DO
  1042.                 IF msg.panel = self.panel THEN Update(self, msg.op, msg.cap, msg.beg, msg.end) END
  1043.             END
  1044.         ELSE NotifyElems(self, msg)
  1045.         END
  1046.     END Handler;
  1047.     PROCEDURE NewFrame*(panel: Panel; x0, y0, grid: LONGINT): Frame;
  1048.         VAR f: Frame;
  1049.     BEGIN NEW(f); OpenFrame(f, Handler, panel, x0, y0, grid); RETURN f
  1050.     END NewFrame;
  1051.     PROCEDURE NotifyDisplay*(panel: Panel; caption: Caption; op, beg, end: LONGINT);
  1052.         VAR msg: UpdateMsg;
  1053.     BEGIN msg.panel := panel; msg.cap := caption; msg.op := op; msg.beg := beg; msg.end := end;
  1054.         Viewers.Broadcast(msg)
  1055.     END NotifyDisplay;
  1056. (* Editor *)
  1057.     PROCEDURE UnmarkMenu(f: Frame);
  1058.         VAR ch: CHAR; t: Texts.Text; v: Viewers.Viewer; r: Texts.Reader;
  1059.     BEGIN v := Viewers.This(f.X, f.Y);
  1060.         IF (v IS MenuViewers.Viewer) & (v.dsc # NIL) & (v.dsc IS TextFrames.Frame) THEN t := v.dsc(TextFrames.Frame).text;
  1061.             IF t.len > 0 THEN Texts.OpenReader(r, t, t.len - 1); Texts.Read(r, ch);
  1062.                 IF ch = "!" THEN Texts.Delete(t, t.len - 1, t.len) END
  1063.             END
  1064.         END
  1065.     END UnmarkMenu;
  1066.     PROCEDURE OpenViewer(x, y: INTEGER);
  1067.         VAR i, j, beg, end, time: LONGINT; ch: CHAR; name: ARRAY 66 OF CHAR;
  1068.             p: Panel; v: MenuViewers.Viewer; text: Texts.Text; s: Texts.Scanner;
  1069.     BEGIN Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s);
  1070.         IF (s.line # 0) OR ((s.class = Texts.Char) & (s.c = "^")) THEN Oberon.GetSelection(text, beg, end, time);
  1071.             IF time >= 0 THEN Texts.OpenScanner(s, text, beg); Texts.Scan(s) END
  1072.         END;
  1073.         IF (s.line = 0) & ((s.class = Texts.Name) OR (s.class = Texts.String)) THEN
  1074.             p := ThisPanel(Files.Old(s.s), NotifyDisplay);
  1075.             IF s.class = Texts.String THEN name[0] := 22X; i := 1 ELSE name[0] := 22X; i := 0 END;
  1076.             j := 0;
  1077.             REPEAT ch := s.s[j]; name[j + i] := ch; INC(j) UNTIL ch = 0X;
  1078.             IF s.class = Texts.String THEN name[j] := 22X; name[j + 1] := 0X END;
  1079.             v := MenuViewers.New(
  1080.                 TextFrames.NewMenu(name, Menu),
  1081.                 NewFrame(p, 0, 0, 5),
  1082.                 TextFrames.menuH, x, y
  1083.         END
  1084.     END OpenViewer;
  1085.     PROCEDURE SysOpen*;
  1086.         VAR x, y: INTEGER;
  1087.     BEGIN Oberon.AllocateSystemViewer(Oberon.Mouse.X, x, y); OpenViewer(x, y)
  1088.     END SysOpen;
  1089.     PROCEDURE Open*;
  1090.         VAR x, y: INTEGER;
  1091.     BEGIN Oberon.AllocateUserViewer(Oberon.Mouse.X, x, y); OpenViewer(x, y)
  1092.     END Open;
  1093.     PROCEDURE Store*;
  1094.         VAR beg, end, time: LONGINT; i: INTEGER; ch: CHAR; frame: Frame;
  1095.             v: Viewers.Viewer; text: Texts.Text; s: Texts.Scanner; f: Files.File;
  1096.             bak: ARRAY 68 OF CHAR;
  1097.     BEGIN frame := NIL;
  1098.         IF (Oberon.Par.vwr IS MenuViewers.Viewer)
  1099.             & (Oberon.Par.vwr.dsc = Oberon.Par.frame) & (Oberon.Par.frame.next IS Frame)
  1100.         THEN frame := Oberon.Par.frame.next(Frame);
  1101.             Texts.OpenScanner(s, Oberon.Par.frame(TextFrames.Frame).text, 0); Texts.Scan(s)
  1102.         ELSE v := Oberon.MarkedViewer();
  1103.             IF (v IS MenuViewers.Viewer) & (v.dsc.next IS Frame) THEN
  1104.                 frame := v.dsc.next(Frame);
  1105.                 Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s);
  1106.                 IF (s.line = 0) & (s.class = Texts.Char) & (s.c = "^") THEN Oberon.GetSelection(text, beg, end, time);
  1107.                     IF time >= 0 THEN Texts.OpenScanner(s, text, beg); Texts.Scan(s) END
  1108.                 ELSIF (s.line # 0) OR ((s.class = Texts.Char) & (s.c = "*")) THEN
  1109.                     Texts.OpenScanner(s, v.dsc(TextFrames.Frame).text, 0); Texts.Scan(s)
  1110.                 END
  1111.             END
  1112.         END;
  1113.         IF (frame # NIL) & (s.line = 0) & ((s.class = Texts.Name) OR (s.class = Texts.String)) THEN
  1114.             Texts.WriteString(w, "CaptionEdit.Store "); Texts.WriteString(w, s.s); Texts.Write(w, " ");
  1115.             Texts.Append(Oberon.Log, w.buf);
  1116.             f := File(frame.panel, s.s);
  1117.             i := 0; ch := s.s[0];
  1118.             WHILE ch # 0X DO bak[i] := ch; INC(i); ch := s.s[i] END;
  1119.             bak[i] := "."; INC(i); bak[i] := "B"; INC(i); bak[i] := "a"; INC(i); bak[i] := "k"; INC(i); bak[i] := 0X;
  1120.             Files.Rename(s.s, bak, i); Files.Register(f);
  1121.             Texts.WriteInt(w, Files.Length(f), 0); Texts.WriteLn(w); Texts.Append(Oberon.Log, w.buf);
  1122.             UnmarkMenu(frame)
  1123.         END
  1124.     END Store;
  1125.     PROCEDURE SetGrid*;
  1126.         VAR beg, end, time: LONGINT; h: INTEGER; frame: Frame; v: Viewers.Viewer; text: Texts.Text; s: Texts.Scanner;
  1127.     BEGIN frame := NIL;
  1128.         IF (Oberon.Par.vwr IS MenuViewers.Viewer)
  1129.             & (Oberon.Par.vwr.dsc = Oberon.Par.frame) & (Oberon.Par.frame.next IS Frame)
  1130.         THEN frame := Oberon.Par.frame.next(Frame)
  1131.         ELSE v := Oberon.MarkedViewer();
  1132.             IF (v IS MenuViewers.Viewer) & (v.dsc.next IS Frame) THEN frame := v.dsc.next(Frame) END
  1133.         END;
  1134.         IF frame # NIL THEN Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s);
  1135.             IF (s.line # 0) OR ((s.class = Texts.Char) & (s.c = "^")) THEN Oberon.GetSelection(text, beg, end, time);
  1136.                 IF time >= 0 THEN Texts.OpenScanner(s, text, beg); Texts.Scan(s) END
  1137.             END;
  1138.             IF (s.line = 0) & (s.class = Texts.Int) & (s.i >= 0) THEN frame.grid := s.i;
  1139.                 h := frame.H; Reduce(frame, frame.Y, 0, 0); Extend(frame, frame.Y, h, 0)
  1140.             END
  1141.         END
  1142.     END SetGrid;
  1143. BEGIN framePat := Display.grey1;
  1144.     Texts.OpenWriter(w); Texts.OpenWriter(wattr);
  1145.     Texts.WriteString(w, VersionName); Texts.WriteLn(w); Texts.Append(Oberon.Log, w.buf)
  1146. END CaptionEdit.
  1147.