home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Gold Fish 3
/
goldfish_volume_3.bin
/
files
/
dev
/
obero
/
oberon
/
demos
/
captionedit.mod
(
.txt
)
next >
Encoding:
Amiga
Atari
Commodore
DOS
FM Towns/JPY
Macintosh
Macintosh JP
Macintosh to JP
NeXTSTEP
RISC OS/Acorn
Shift JIS
UTF-8
Wrap
Oberon Text
|
1995-04-06
|
50.7 KB
|
1,147 lines
Syntax10.Scn.Fnt
Syntax10i.Scn.Fnt
StampElems
Alloc
30 Jan 95
Syntax10b.Scn.Fnt
MODULE CaptionEdit; (* Copyright: ww
IMPORT
Oberon, MenuViewers, Viewers, TextFrames, Texts, Display, Fonts, Input, Modules, Files;
CONST
(* Model *)
remove* = 10; insert* = 11; move* = 12; tofront* = 13;
VersionName = "CaptionEdit (ww 15 Nov 94)";
(* Frames *)
ML = 2; MM = 1; MR = 0; Space = 5;
NoFocus* = 0; PointFocus* = 1; CaretFocus* = 2;
(* Editor *)
Menu = "System.Close System.Copy System.Grow CaptionEdit.Store ";
TYPE
(* Model *)
Panel* = POINTER TO PanelDesc;
Caption* = POINTER TO CaptionDesc;
Notifier* = PROCEDURE (panel: Panel; caption: Caption; op, beg, end: LONGINT);
CaptionDesc* = RECORD(Texts.TextDesc)
host*: Panel;
next*: Caption;
x*, y*: LONGINT;
inserted: BOOLEAN
END;
PanelDesc* = RECORD
first*: Caption;
notify*: Notifier
END;
(* Frames *)
CapInfo = POINTER TO CapInfoDesc;
CapInfoDesc = RECORD
next: CapInfo;
text: Caption;
x, y, w, h, baseH: INTEGER;
ok, marked: BOOLEAN
END;
Location* = RECORD
cap*: Caption;
pos*: LONGINT;
x*, y*: INTEGER
END;
Frame* = POINTER TO FrameDesc;
FrameDesc* = RECORD(Display.FrameDesc)
panel*: Panel;
x0*, y0*: LONGINT;
hasSel*: BOOLEAN;
selTime*: LONGINT;
selBeg*, selEnd*: Location;
focus*: INTEGER;
focusPos*: Location;
subFocus*: Display.Frame;
grid*: LONGINT;
info: CapInfo
END;
UpdateMsg* = RECORD(Display.FrameMsg)
panel*: Panel;
cap*: Caption;
op*, beg*, end*: LONGINT
END;
w, wattr: Texts.Writer;
framePat: Display.Pattern;
PROCEDURE ASSERT(b: BOOLEAN);
BEGIN IF ~b THEN HALT(99) END
END ASSERT;
(* Model *)
PROCEDURE Insert*(host: Panel; cap: Caption; x, y: LONGINT);
VAR q: Caption;
BEGIN ASSERT(~cap.inserted);
q := host.first;
IF q # NIL THEN
WHILE q.next # NIL DO q := q.next END;
q.next := cap
ELSE host.first := cap
END;
cap.next := NIL; cap.inserted := TRUE; cap.host := host; cap.x := x; cap.y := y;
host.notify(host, cap, insert, 0, 0)
END Insert;
PROCEDURE Remove*(cap: Caption);
VAR q: Caption; host: Panel;
BEGIN
IF cap.inserted THEN host := cap.host; q := host.first;
IF q # cap THEN
WHILE q.next # cap DO q := q.next END;
q.next := cap.next
ELSE host.first := cap.next
END;
cap.inserted := FALSE;
host.notify(host, cap, remove, 0, 0)
END
END Remove;
PROCEDURE Move*(cap: Caption; x, y: LONGINT);
VAR host: Panel;
BEGIN cap.x := x; cap.y := y; host := cap.host; host.notify(host, cap, move, 0, 0)
END Move;
PROCEDURE BringToFront*(cap: Caption);
VAR q: Caption; host: Panel;
BEGIN
IF cap.inserted & (cap.next # NIL) THEN host := cap.host; q := host.first;
IF q # cap THEN
WHILE q.next # cap DO q := q.next END;
q.next := cap.next
ELSE host.first := cap.next
END;
WHILE q.next # NIL DO q := q.next END;
q.next := cap; cap.next := NIL;
host.notify(host, cap, tofront, 0, 0)
END
END BringToFront;
PROCEDURE NotifyPanel*(t: Texts.Text; op: INTEGER; beg, end: LONGINT);
VAR c: Caption; p: Panel;
BEGIN c := t(Caption); p := c.host;
IF c.inserted THEN
IF (op = Texts.delete) & (c.len = 0) THEN Remove(c)
ELSE p.notify(p, c, op, beg, end)
END
ELSIF (p # NIL) & (op = Texts.insert) THEN Insert(p, c, c.x, c.y)
END
END NotifyPanel;
PROCEDURE OpenCaption*(cap: Caption; text: Texts.Text; beg, end: LONGINT);
VAR buf: Texts.Buffer;
BEGIN Texts.Open(cap, ""); cap.notify := NotifyPanel; cap.inserted := FALSE;
IF (text # NIL) & (beg < end) THEN
NEW(buf); Texts.OpenBuf(buf); Texts.Save(text, beg, end, buf); Texts.Append(cap, buf)
END
END OpenCaption;
PROCEDURE OpenPanel*(p: Panel; notifier: Notifier);
BEGIN p.first := NIL; p.notify := notifier
END OpenPanel;
PROCEDURE LoadPanel*(VAR r: Files.Rider; p: Panel);
VAR i: LONGINT; c, prev, anchor: Caption;
BEGIN OpenPanel(p, p.notify); NEW(anchor); prev := anchor; Files.ReadLInt(r, i);
WHILE i # 0 DO NEW(c); OpenCaption(c, NIL, 0, 0); prev.next := c; prev := c; c.host := p; c.inserted := TRUE;
Files.ReadLInt(r, c.x); Files.ReadLInt(r, c.y); Texts.Load(r, c);
DEC(i)
END;
p.first := anchor.next
END LoadPanel;
PROCEDURE ThisPanel*(f: Files.File; notifier: Notifier): Panel;
VAR ch: CHAR; p: Panel; r:Files.Rider;
BEGIN NEW(p); OpenPanel(p, notifier);
IF f # NIL THEN Files.Set(r, f, 0); Files.Read(r, ch);
IF ch = 0F7X THEN Files.Read(r, ch);
IF ch = 1X THEN LoadPanel(r, p) END
END
END;
RETURN p
END ThisPanel;
PROCEDURE StorePanel*(VAR r: Files.Rider; p: Panel);
VAR i: LONGINT; c: Caption;
BEGIN i := 0; c := p.first;
WHILE c # NIL DO INC(i); c := c.next END;
Files.WriteLInt(r, i); c := p.first;
WHILE c # NIL DO Files.WriteLInt(r, c.x); Files.WriteLInt(r, c.y); Texts.Store(r, c); c := c.next END
END StorePanel;
PROCEDURE File*(p: Panel; name: ARRAY OF CHAR): Files.File;
VAR f: Files.File; r: Files.Rider;
BEGIN f := Files.New(name);
Files.Set(r, f, 0); Files.Write(r, 0F7X); Files.Write(r, 1X); StorePanel(r, p);
RETURN f
END File;
(* Frames *)
(* Measurement *)
PROCEDURE MeasureChar(VAR r: Texts.Reader; ch: CHAR; VAR w, minY, maxY: INTEGER);
VAR chX, chY, chW, chH, voff: INTEGER; pat: Display.Pattern; msg: TextFrames.DisplayMsg;
BEGIN voff := r.fnt.height * r.voff DIV 64;
IF r.elem # NIL THEN msg.prepare := TRUE; msg.fnt := r.fnt; msg.col := r.col;
msg.pos := Texts.Pos(r) - 1; msg.indent := 0; msg.Y0 := r.fnt.minY;
r.elem.handle(r.elem, msg);
IF r.elem IS TextFrames.Parc THEN r.elem.W := 0; r.elem.H := 0; msg.Y0 := 0 END;
w := SHORT(r.elem.W DIV TextFrames.Unit);
minY := voff + msg.Y0; maxY := SHORT(r.elem.H DIV TextFrames.Unit) + minY
ELSE Display.GetChar(r.fnt.raster, ch, w, chX, chY, chW, chH, pat);
minY := r.fnt.minY + voff; maxY := r.fnt.maxY + voff
END
END MeasureChar;
PROCEDURE GetBoundingBox(t: Texts.Text; beg, end: LONGINT; VAR w, h, baseH: INTEGER);
VAR pos: LONGINT; minY, maxY, w1, minY1, maxY1: INTEGER; ch: CHAR; r: Texts.Reader;
BEGIN w := 0; minY := 0; maxY := 0;
Texts.OpenReader(r, t, 0); Texts.Read(r, ch); pos := 1;
WHILE ~r.eot DO MeasureChar(r, ch, w1, minY1, maxY1);
IF minY1 < minY THEN minY := minY1 END;
IF maxY1 > maxY THEN maxY := maxY1 END;
IF (pos > beg) & (pos <= end) THEN w := w + w1 END;
Texts.Read(r, ch); INC(pos)
END;
h := maxY - minY; baseH := -minY
END GetBoundingBox;
PROCEDURE Width(t: Texts.Text; beg, end: LONGINT): INTEGER;
VAR w, h, baseH: INTEGER;
BEGIN GetBoundingBox(t, beg, end, w, h, baseH); RETURN w
END Width;
PROCEDURE Offset(t: Texts.Text; dX: INTEGER): LONGINT;
VAR i: LONGINT; w, minY, maxY: INTEGER; ch: CHAR; r: Texts.Reader;
BEGIN i := 0; Texts.OpenReader(r, t, 0);
WHILE (i < t.len) & (dX > 0) DO Texts.Read(r, ch); INC(i);
MeasureChar(r, ch, w, minY, maxY); dX := dX - w
END;
IF (dX < 0) & (i # 0) THEN DEC(i) END;
RETURN i
END Offset;
(* Caption_Info *)
PROCEDURE NewInfo(f: Frame; cap: Caption; w, h, baseH: INTEGER): CapInfo;
VAR info: CapInfo;
BEGIN NEW(info); info.text := cap; info.x := SHORT(cap.x - f.x0); info.y := SHORT(cap.y - f.y0);
info.w := w; info.h := h; info.baseH := baseH;
info.ok := FALSE; info.marked := FALSE;
RETURN info
END NewInfo;
PROCEDURE ThisCaption(f: Frame; x, y: INTEGER): CapInfo;
VAR c, this: CapInfo;
BEGIN x := x - f.X; y := y - (f.Y + f.H);
c := f.info; this := NIL;
WHILE c # NIL DO
IF (c.x <= x) & (x < c.x + c.w) & (c.y <= y) & (y < c.y + c.h) THEN this := c END;
c := c.next
END;
RETURN this
END ThisCaption;
PROCEDURE SetReader(f: Frame; x, y: INTEGER; VAR r: Texts.Reader; VAR cap: CapInfo);
VAR t: Caption;
BEGIN cap := ThisCaption(f, x, y);
IF cap # NIL THEN t := cap.text; Texts.OpenReader(r, t, Offset(t, x - f.X - cap.x)) END
END SetReader;
PROCEDURE InfoAbout(f: Frame; cap: Caption): CapInfo;
VAR c: CapInfo;
BEGIN c := f.info;
WHILE (c # NIL) & (c.text # cap) DO c := c.next END;
RETURN c
END InfoAbout;
PROCEDURE InsertInfo(f: Frame; cap: CapInfo);
VAR info, p: CapInfo; q, t: Caption;
BEGIN info := f.info; p := NIL; q := f.panel.first; t := cap.text;
WHILE q # t DO
IF (info # NIL) & (q = info.text) THEN p := info; info := info.next END;
q := q.next
END;
IF p # NIL THEN p.next := cap ELSE f.info := cap END;
cap.next := info
END InsertInfo;
PROCEDURE RemoveInfo(f: Frame; cap: CapInfo);
VAR p, q: CapInfo;
BEGIN p := f.info;
IF p = cap THEN f.info := cap.next
ELSE q := p.next;
WHILE q # cap DO p := q; q := q.next END;
p.next := cap.next
END
END RemoveInfo;
(* Overlaps *)
PROCEDURE MarkOverlap(x, y: LONGINT; w, h: INTEGER; cap: CapInfo);
VAR r, t, cX, cY: LONGINT;
BEGIN r := x + w; t := y + h;
WHILE cap # NIL DO cX := cap.text.x; cY := cap.text.y;
IF (x < cX + cap.w) & (cX < r) & (y < cY + cap.h) & (cY < t) THEN cap.ok := FALSE END;
cap := cap.next
END
END MarkOverlap;
PROCEDURE HasOverlap(cap: CapInfo): BOOLEAN;
VAR l, r, b, t: INTEGER;
BEGIN l := cap.x; r := l + cap.w; b := cap.y; t := b + cap.h; cap := cap.next;
WHILE (cap # NIL) & ((cap.x >= r) OR (cap.x + cap.w <= l) OR (cap.y >= t) OR (cap.y + cap.h <= b)) DO
cap := cap.next
END;
RETURN cap # NIL
END HasOverlap;
(* Subframe handling *)
PROCEDURE ThisSubFrame(parent: Frame; x, y: INTEGER): Display.Frame;
VAR f: Display.Frame;
BEGIN f := parent.dsc;
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;
RETURN f
END ThisSubFrame;
PROCEDURE CloseSubFrames(parent: Frame; x, y, w, h: INTEGER);
VAR r, t: INTEGER; f, p: Display.Frame; msg: MenuViewers.ModifyMsg;
BEGIN r := x + w; t := y + h;
p := parent.dsc;
IF p # NIL THEN f := p.next;
WHILE f # NIL DO
IF (x < f.X + f.W) & (f.X < r) & (y < f.Y + f.H) & (f.Y < t) THEN p.next := f.next;
msg.id := MenuViewers.reduce; msg.dY := 0; msg.Y := f.Y; msg.H := 0; f.handle(f, msg)
ELSE p := f
END;
f := f.next
END;
f := parent.dsc;
IF (x < f.X + f.W) & (f.X < r) & (y < f.Y + f.H) & (f.Y < t) THEN parent.dsc := f.next;
msg.id := MenuViewers.reduce; msg.dY := 0; msg.Y := f.Y; msg.H := 0; f.handle(f, msg)
END
END
END CloseSubFrames;
PROCEDURE ShiftSubFrames(parent: Frame; y, h, dY: INTEGER);
VAR t: INTEGER; f: Display.Frame; msg: MenuViewers.ModifyMsg;
BEGIN
IF dY < 0 THEN CloseSubFrames(parent, parent.X, y + dY, parent.W, -dY)
ELSE CloseSubFrames(parent, parent.X, y + h, parent.W, dY)
END;
f := parent.dsc; t := y + h;
WHILE f # NIL DO
IF (y < f.Y + f.H) & (f.Y < t) THEN f.Y := f.Y + dY;
msg.id := MenuViewers.reduce; msg.dY := 0; msg.Y := f.Y; msg.H := f.H; f.handle(f, msg)
END;
f := f.next
END
END ShiftSubFrames;
(* Marker drawing *)
PROCEDURE FlipCross(clip: Display.Frame; x, y: INTEGER);
BEGIN Display.CopyPatternC(clip, Display.white, Display.cross, x - 5, y - 5, Display.invert)
END FlipCross;
PROCEDURE FlipCaret(clip: Display.Frame; x, y: INTEGER);
BEGIN Display.CopyPatternC(clip, Display.white, Display.hook, x, y - 6, Display.invert)
END FlipCaret;
PROCEDURE InvertRect(clip: Display.Frame; x, y, w, h: INTEGER);
VAR pinX, pinY: INTEGER;
BEGIN pinX := clip.X; pinY := clip.Y + clip.H + 1;
IF w < 0 THEN x := x + w; w := -w END;
IF h < 0 THEN y := y + h; h := -h END;
Display.ReplPatternC(clip, Display.white, framePat, x, y, w, 1, pinX, pinY, Display.invert);
Display.ReplPatternC(clip, Display.white, framePat, x, y + h, w, 1, pinX, pinY, Display.invert);
Display.ReplPatternC(clip, Display.white, framePat, x, y, 1, h, pinX, pinY, Display.invert);
Display.ReplPatternC(clip, Display.white, framePat, x + w, y, 1, h, pinX, pinY, Display.invert)
END InvertRect;
(* Mark removing *)
PROCEDURE RemoveSelection*(f: Frame);
VAR x, y: INTEGER;
BEGIN
IF f.hasSel THEN f.hasSel := FALSE; x := f.selBeg.x; y := f.selBeg.y;
Display.ReplConstC(f, Display.white, x, y, f.selEnd.x - x, f.selEnd.y - y, Display.invert)
END
END RemoveSelection;
PROCEDURE Defocus*(f: Frame);
VAR subF: Display.Frame; msg: Oberon.ControlMsg;
BEGIN
IF f.focus = PointFocus THEN FlipCross(f, f.focusPos.x, f.focusPos.y)
ELSIF f.focus = CaretFocus THEN FlipCaret(f, f.focusPos.x, f.focusPos.y)
END;
f.focus := NoFocus;
IF f.subFocus # NIL THEN subF := f.subFocus; msg.id := Oberon.defocus; subF.handle(subF, msg) END
END Defocus;
PROCEDURE UnmarkCaption*(f: Frame; cap: Caption);
VAR info: CapInfo;
BEGIN info := InfoAbout(f, cap);
IF (info # NIL) & info.marked THEN InvertRect(f, f.X + info.x, f.Y + f.H + info.y, info.w - 1, info.h - 1);
info.marked := FALSE
END
END UnmarkCaption;
PROCEDURE UnmarkAllCaptions*(f: Frame);
VAR c: CapInfo;
BEGIN c := f.info;
WHILE c # NIL DO
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;
c := c.next
END
END UnmarkAllCaptions;
(* Subfocus *)
PROCEDURE PassSubFocus*(f: Frame; cap: Caption; new: Display.Frame);
VAR info: CapInfo; old: Display.Frame; r: Texts.Reader; ctrl: Oberon.ControlMsg; msg: TextFrames.FocusMsg;
BEGIN old := f.subFocus;
IF old # NIL THEN ctrl.id := Oberon.defocus; old.handle(old, ctrl);
InvertRect(f, old.X - 1, old.Y - 1, old.W + 2, old.H + 2); f.subFocus := NIL;
SetReader(f, old.X, old.Y, r, info); Texts.ReadElem(r);
IF r.elem # NIL THEN msg.focus := FALSE; msg.elemFrame := old; msg.frame := f;
r.elem.handle(r.elem, msg)
END
END;
IF new # NIL THEN info := InfoAbout(f, cap);
IF info # NIL THEN Defocus(f);
IF HasOverlap(info) THEN BringToFront(cap) END;
IF cap = f.selBeg.cap THEN RemoveSelection(f)END;
Texts.OpenReader(r, cap, Offset(cap, new.X - f.X - info.x)); Texts.ReadElem(r);
msg.focus := TRUE; msg.elemFrame := new; msg.frame := f; r.elem.handle(r.elem, msg);
InvertRect(f, new.X - 1, new.Y - 1, new.W + 2, new.H + 2); f.subFocus := new
END
END
END PassSubFocus;
(* Focus *)
PROCEDURE SetCaret*(f: Frame; cap: Caption; pos: LONGINT);
VAR x, y: INTEGER; info: CapInfo;
BEGIN
IF pos < 0 THEN pos := 0 ELSIF pos > cap.len THEN pos := cap.len END;
IF ~((f.focus = CaretFocus) & (f.focusPos.cap = cap) & (f.focusPos.pos = pos)) THEN info := InfoAbout(f, cap);
IF info # NIL THEN Defocus(f); PassSubFocus(f, NIL, NIL);
IF HasOverlap(info) THEN BringToFront(cap) END;
x := f.X + info.x + Space + Width(cap, 0, pos); y := f.Y + f.H + info.y + info.baseH;
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));
f.focus := CaretFocus; f.focusPos.cap := cap; f.focusPos.pos := pos; f.focusPos.x := x; f.focusPos.y := y;
FlipCaret(f, x, y)
END
END
END
END SetCaret;
PROCEDURE SetFocus*(f: Frame; x, y: INTEGER);
BEGIN
IF (f.X <= x) & (x < f.X + f.W) & (f.Y <= y) & (y < f.Y + f.H) THEN
IF ~((f.focus = PointFocus) & (f.focusPos.x = x) & (f.focusPos.y = y)) THEN PassSubFocus(f, NIL, NIL);
Oberon.PassFocus(Viewers.This(f.X, f.Y));
f.focus := PointFocus; f.focusPos.x := x; f.focusPos.y := y;
FlipCross(f, x, y)
END
END
END SetFocus;
(* Selection *)
PROCEDURE SetSelection*(f: Frame; cap: Caption; beg, end: LONGINT);
VAR x, y, w, h: INTEGER; info: CapInfo;
BEGIN
IF beg < 0 THEN beg := 0 END;
IF end > cap.len THEN end := cap.len END;
IF f.hasSel & (f.selBeg.cap = cap) & (f.selBeg.pos = beg) & (beg < end) THEN
IF f.selEnd.pos # end THEN info := InfoAbout(f, cap);
x := f.X + info.x + Space + Width(cap, 0, end); w := f.selEnd.x - x;
f.selEnd.pos := end; f.selEnd.x := x;
IF w < 0 THEN x := x + w; w := -w END;
Display.ReplConstC(f, Display.white, x, f.selBeg.y, w, f.selEnd.y - f.selBeg.y, Display.invert)
END;
f.selTime := Oberon.Time()
ELSE RemoveSelection(f);
info := InfoAbout(f, cap);
IF (info # NIL) & (beg < end) THEN
IF HasOverlap(info) THEN BringToFront(cap) END;
IF (f.subFocus # NIL) & (info = ThisCaption(f, f.subFocus.X, f.subFocus.Y)) THEN PassSubFocus(f, NIL, NIL) END;
x := f.X + info.x + Space + Width(cap, 0, beg); w := Width(cap, beg, end);
y := f.Y + f.H + info.y + Space; h := info.h - 2 * Space;
f.hasSel := TRUE; f.selTime := Oberon.Time();
f.selBeg.cap := cap; f.selBeg.pos := beg; f.selBeg.x := x; f.selBeg.y := y;
f.selEnd.cap := cap; f.selEnd.pos := end; f.selEnd.x := x + w; f.selEnd.y := y + h;
Display.ReplConstC(f, Display.white, x, y, w, h, Display.invert)
END
END
END SetSelection;
(* marked captions *)
PROCEDURE IsMarked*(f: Frame; cap: Caption): BOOLEAN;
VAR info: CapInfo;
BEGIN info := InfoAbout(f, cap); RETURN (info # NIL) & info.marked
END IsMarked;
PROCEDURE MarkCaption*(f: Frame; cap: Caption);
VAR info: CapInfo;
BEGIN info := InfoAbout(f, cap);
IF (info # NIL) & ~info.marked THEN InvertRect(f, f.X + info.x, f.Y + f.H + info.y, info.w - 1, info.h - 1);
info.marked := TRUE
END
END MarkCaption;
(* Neutralize *)
PROCEDURE NeutralizeArea(f: Frame; x, y, w, h: INTEGER);
VAR r, t, fx, fy: INTEGER; c: CapInfo; f1: Display.Frame;
BEGIN UnmarkAllCaptions(f); r := x + w; t := y + h;
IF f.subFocus # NIL THEN f1 := f.subFocus;
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
PassSubFocus(f, NIL, NIL)
END
END;
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);
IF (x <= fx + 5) & (fx - 5 < r) & (y <= fy + 5) & (fy - 5 < t) THEN Defocus(f) END
ELSIF f.focus = CaretFocus THEN c := InfoAbout(f, f.focusPos.cap);
IF (x < c.x + c.w) & (c.x < r) & (y < c.y + c.h) & (c.y < t) THEN Defocus(f) END
END;
IF f.hasSel THEN c := InfoAbout(f, f.selBeg.cap);
IF (x < c.x + c.w) & (c.x < r) & (y < c.y + c.h) & (c.y < t) THEN RemoveSelection(f) END
END
END NeutralizeArea;
PROCEDURE Neutralize*(f: Frame);
BEGIN UnmarkAllCaptions(f); PassSubFocus(f, NIL, NIL); Defocus(f); RemoveSelection(f)
END Neutralize;
(* Drawing *)
PROCEDURE DrawBackground(f: Frame; x, y, w, h: INTEGER);
VAR g, r, t, x0: INTEGER; s: LONGINT;
BEGIN Display.ReplConstC(f, Display.black, x, y, w, h, Display.replace);
g := SHORT(f.grid);
IF g > 0 THEN r := x + w; t := y + h;
WHILE g < 20 DO g := g * 2 END;
s := f.x0 - f.X; x0 := SHORT(((x + s - 1) DIV g + 1) * g - s);
s := f.y0 - f.Y - f.H; y := SHORT(((y + s - 1) DIV g + 1) * g - s);
WHILE y < t DO x := x0;
WHILE x < r DO Display.DotC(f, Display.white, x, y, Display.replace); x := x + g END;
y := y + g
END
END
END DrawBackground;
PROCEDURE DrawCaption(f: Frame; cap: CapInfo; beg, end: LONGINT);
VAR x, y, w, h, baseH, pX, pY, voff, dx, chX, chY, chW, chH: INTEGER; ch: CHAR; t: Caption;
pat: Display.Pattern; r: Texts.Reader; msg: TextFrames.DisplayMsg;
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;
x := f.X + cap.x; w := Width(t, beg, end);
IF beg # 0 THEN x := x + Space + Width(t, 0, beg) ELSE w := w + Space END;
IF end = t.len THEN w := w + Space END;
Oberon.RemoveMarks(x, y, w, h); CloseSubFrames(f, x, y, w, h);
MarkOverlap(f.x0 + x - f.X, f.y0 + y - (f.Y + f.H), w, h, cap.next);
Display.ReplConstC(f, Display.black, x, y, w, h, Display.replace);
Display.ReplPatternC(f, Display.white, framePat, x, y, w, 1, pX, pY, Display.replace);
Display.ReplPatternC(f, Display.white, framePat, x, y + h - 1, w, 1, pX, pY, Display.replace);
IF beg = 0 THEN x := x; w := w;
Display.ReplPatternC(f, Display.white, framePat, x, y, 1, h, pX, pY, Display.replace)
END;
IF end = t.len THEN w := w;
Display.ReplPatternC(f, Display.white, framePat, x + w - 1, y, 1, h, pX, pY, Display.replace);
Display.ReplConstC(f, Display.white, x + w - Space, y, Space, Space, Display.replace)
END;
IF beg = 0 THEN x := x + Space END;
Texts.OpenReader(r, t, beg);
WHILE (beg < end) & (x < f.X + f.W) DO Texts.Read(r, ch); INC(beg);
voff := r.fnt.height * r.voff DIV 64;
IF r.elem # NIL THEN
IF ~(r.elem IS TextFrames.Parc) THEN
msg.prepare := TRUE; msg.fnt := r.fnt; msg.col := r.col; msg.pos := beg - 1; msg.indent := 0;
msg.Y0 := r.fnt.minY;
chW := SHORT(r.elem.W DIV TextFrames.Unit); chH := SHORT(r.elem.H DIV TextFrames.Unit);
r.elem.handle(r.elem, msg);
chY := y + baseH + voff;
IF (f.X <= x) & (f.X + f.W >= x + chW) & (f.Y <= chY + msg.Y0) & (f.Y + f.H >= chY + msg.Y0 + chH) THEN
msg.prepare := FALSE; msg.X0 := x; msg.Y0 := chY + r.fnt.minY;
msg.frame := f; msg.elemFrame := NIL;
r.elem.handle(r.elem, msg);
IF msg.elemFrame # NIL THEN msg.elemFrame.next := f.dsc; f.dsc := msg.elemFrame END;
END;
x := x + chW
END;
ELSE Display.GetChar(r.fnt.raster, ch, dx, chX, chY, chW, chH, pat);
Display.CopyPatternC(f, r.col, pat, x + chX, y + baseH + voff + chY, Display.replace);
x := x + dx
END
END;
cap.ok := TRUE
END DrawCaption;
PROCEDURE Restore(f: Frame);
VAR c: CapInfo;
BEGIN c := f.info;
WHILE c # NIL DO
IF ~c.ok THEN NeutralizeArea(f, c.x, c.y, c.w, c.h); DrawCaption(f, c, 0, c.text.len) END;
c := c.next
END
END Restore;
(* View Modification *)
PROCEDURE Reduce*(f: Frame; y, h, dy: INTEGER);
VAR boarder: INTEGER; c, p: CapInfo; df: Display.Frame;
BEGIN Oberon.RemoveMarks(f.X, f.Y, f.W, f.H); Neutralize(f);
IF h # 0 THEN
IF dy # 0 THEN Display.CopyBlock(f.X, y + dy, f.W, h, f.X, y, Display.replace); ShiftSubFrames(f, y + dy, h, -dy)
ELSE CloseSubFrames(f, f.X, f.Y, f.W, f.H - h)
END;
f.Y := y; f.H := h;
boarder := -f.H; c := f.info; p := NIL;
WHILE c # NIL DO
IF c.y + c.h > boarder THEN
IF ~c.ok OR (c.y < boarder) THEN DrawCaption(f, c, 0, c.text.len) END;
p := c
ELSIF p = NIL THEN f.info := c.next
ELSE p.next := c.next
END;
c := c.next
END
ELSE CloseSubFrames(f, f.X, f.Y, f.W, f.H); f.Y := y; f.H := h; f.info := NIL
END;
df := f; df.X := f.X; df.W := f.W; df.Y := f.Y; df.H := f.H
END Reduce;
PROCEDURE Extend*(f: Frame; y, h, dy: INTEGER);
VAR l, r, b, t: LONGINT; cw, ch, baseH, dh: INTEGER; info, p, q: CapInfo; c: Caption;
BEGIN Oberon.RemoveMarks(f.X, y, f.W, h); Neutralize(f);
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;
l := f.x0; r := l + f.W; b := f.y0 - h; t := f.y0 - f.H;
dh := h - f.H; f.Y := y; f.H := h;
DrawBackground(f, f.X, f.Y, f.W, dh);
c := f.panel.first; q := f.info; p := NIL;
WHILE c # NIL DO
IF (q # NIL) & (q.text = c) THEN
IF ~q.ok OR (c.y < t) THEN DrawCaption(f, q, 0, c.len) END;
p := q; q := q.next
ELSIF (c.x < r) & (c.y < t) THEN
GetBoundingBox(c, 0, c.len, cw, ch, baseH); cw := cw + 2 * Space; ch := ch + 2 * Space;
IF (l < c.x + cw) & (b < c.y + ch) THEN info := NewInfo(f, c, cw, ch, baseH + Space);
IF p # NIL THEN p.next := info ELSE f.info := info END;
info.next := q; p := info;
DrawCaption(f, info, 0, c.len)
END
END;
c := c.next
END
END Extend;
PROCEDURE Scroll*(f: Frame; dx, dy: LONGINT);
VAR y, h: INTEGER;
BEGIN
IF (dx # 0) OR (dy # 0) THEN y := f.Y; h := f.H;
Reduce(f, y + h, 0, 0);
f.x0 := f.x0 - dx; f.y0 := f.y0 - dy;
Extend(f, y, h, 0)
END
END Scroll;
(* Update *)
PROCEDURE MarkMenu(f: Frame);
VAR ch: CHAR; v: Viewers.Viewer; t: Texts.Text; r: Texts.Reader;
BEGIN v := Viewers.This(f.X, f.Y);
IF (v IS MenuViewers.Viewer) & (v.dsc # NIL) & (v.dsc IS TextFrames.Frame) 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.Write(w, "!"); Texts.Append(t, w.buf) END
END
END MarkMenu;
PROCEDURE ClearScreen(f: Frame; x, y: LONGINT; w, h: INTEGER);
VAR x1, y1: INTEGER;
BEGIN MarkOverlap(x, y, w, h, f.info);
x1 := SHORT(f.X + x - f.x0); y1 := SHORT(f.Y + f.H + y - f.y0);
Oberon.RemoveMarks(x1, y1, w, h); CloseSubFrames(f, x1, y1, w, h);
DrawBackground(f, x1, y1, w, h)
END ClearScreen;
PROCEDURE Update*(f: Frame; op: LONGINT; cap: Caption; beg, end: LONGINT);
VAR w, h, baseH: INTEGER; info: CapInfo;
BEGIN MarkMenu(f);
info := InfoAbout(f, cap);
IF info # NIL THEN
IF op = tofront THEN NeutralizeArea(f, info.x, info.y, info.w, info.h);
RemoveInfo(f, info); InsertInfo(f, info); DrawCaption(f, info, 0, cap.len)
ELSIF op = move THEN NeutralizeArea(f, info.x, info.y, info.w, info.h);
ClearScreen(f, f.x0 + info.x, f.y0 + info.y, info.w, info.h);
info.x := SHORT(cap.x - f.x0); info.y := SHORT(cap.y - f.y0);
NeutralizeArea(f, info.x, info.y, info.w, info.h);
IF (info.x < f.W) & (info.x + info.w > 0) & (info.y < 0) & (info.y + info.h > -f.H) THEN
DrawCaption(f, info, 0, cap.len)
ELSE RemoveInfo(f, info)
END;
Restore(f)
ELSIF op = remove THEN NeutralizeArea(f, info.x, info.y, info.w, info.h); RemoveInfo(f, info);
ClearScreen(f, cap.x, cap.y, info.w, info.h); Restore(f)
ELSE NeutralizeArea(f, info.x, info.y, info.w, info.h);
GetBoundingBox(cap, 0, cap.len, w, h, baseH); w := w + 2 * Space; h := h + 2 * Space;
NeutralizeArea(f, info.x, info.y, w, h);
IF w < info.w THEN ClearScreen(f, cap.x + w, cap.y, info.w - w, info.h) END;
IF h < info.h THEN ClearScreen(f, cap.x, cap.y + h, info.w, info.h - h) END;
IF ((op # Texts.delete) & (op # Texts.insert)) OR (h # info.h) THEN beg := 0 END;
info.w := w; info.h := h; info.baseH := baseH + Space;
IF (info.x < f.W) & (info.x + info.w > 0) & (info.y < 0) & (info.y + info.h > -f.H) THEN
DrawCaption(f, info, beg, cap.len)
ELSE RemoveInfo(f, info)
END;
Restore(f)
END
ELSIF (op # remove) & (op # Texts.delete) & (cap.x < f.x0 + f.W) & (cap.y < f.y0) THEN
GetBoundingBox(cap, 0, cap.len, w, h, baseH); w := w + 2 * Space; h := h + 2 * Space;
IF (f.x0 < cap.x + w) & (f.y0 - f.H < cap.y + h) THEN
NeutralizeArea(f, SHORT(cap.x), SHORT(cap.y), w, h);
info := NewInfo(f, cap, w, h, baseH + Space); InsertInfo(f, info);
DrawCaption(f, info, 0, cap.len)
END
END
END Update;
(* Commands *)
PROCEDURE Call*(f: Frame; cap: Caption; pos: LONGINT; load: BOOLEAN);
VAR i, j, res: INTEGER; par: Oberon.ParList; s: Texts.Scanner;
BEGIN Texts.OpenScanner(s, cap, pos); Texts.Scan(s);
IF s.class = Texts.Name THEN i := 0;
WHILE (i < s.len) & (s.s[i] # ".") DO INC(i) END;
j := i + 1;
WHILE (j < s.len) & (s.s[j] # ".") DO INC(j) END;
IF (j = s.len) & (s.s[i] = ".") THEN
NEW(par); par.text := cap; par.pos := Texts.Pos(s) - 1; par.frame := f; Oberon.Call(s.s, par, load, res);
IF res > 0 THEN Texts.WriteString(w, "Call error: "); Texts.WriteString(w, Modules.importing);
IF res = 1 THEN Texts.WriteString(w, " not found");
ELSIF res = 2 THEN Texts.WriteString(w, " not an obj-file");
ELSIF res = 3 THEN Texts.WriteString(w, " imports ");
Texts.WriteString(w, Modules.imported); Texts.WriteString(w, " with bad key")
ELSIF res = 4 THEN Texts.WriteString(w, " corrupted obj file")
ELSIF res = 6 THEN Texts.WriteString(w, " has too many imports")
ELSIF res = 7 THEN Texts.WriteString(w, " not enough space")
END;
Texts.WriteLn(w); Texts.Append(Oberon.Log, w.buf)
ELSIF res < 0 THEN INC(i);
WHILE i < s.len DO Texts.Write(w, s.s[i]); INC(i) END;
Texts.WriteString(w, " not found"); Texts.WriteLn(w); Texts.Append(Oberon.Log, w.buf)
END
END
END
END Call;
PROCEDURE MarkArea*(f: Frame; x, y, w, h: INTEGER);
VAR l, r, b, t: INTEGER; info: CapInfo;
BEGIN
IF w < 0 THEN x := x + w; w := -w END;
IF h < 0 THEN y := y + h; h := -h END;
l := x - f.X; r := l + w; b := y - (f.Y + f.H); t := b + h; info := f.info;
WHILE info # NIL DO
IF (l <= info.x) & (r >= info.x + info.w) & (b <= info.y) & (t >= info.y + info.h) THEN MarkCaption(f, info.text) END;
info := info.next
END
END MarkArea;
PROCEDURE RememberMarked(f: Frame; VAR mem: ARRAY OF CapInfo);
VAR i: LONGINT; info: CapInfo;
BEGIN info := f.info; i := 0;
WHILE info # NIL DO
IF info.marked THEN mem[i] := info; INC(i) END;
info := info.next
END;
mem[i] := NIL
END RememberMarked;
PROCEDURE MoveMarked*(f: Frame; dX, dY: INTEGER);
VAR i: LONGINT; info: CapInfo; text: Caption; mem: ARRAY 1024 OF CapInfo;
BEGIN RememberMarked(f, mem);
i := 0; info := mem[0];
WHILE info # NIL DO text := info.text; Move(text, text.x + dX, text.y + dY); INC(i); info := mem[i] END;
i := 0; info := mem[0];
WHILE info # NIL DO MarkCaption(f, info.text); INC(i); info := mem[i] END
END MoveMarked;
PROCEDURE DeleteMarked*(f: Frame);
VAR i: LONGINT; info: CapInfo; text: Caption; mem: ARRAY 1024 OF CapInfo;
BEGIN RememberMarked(f, mem);
i := 0; info := mem[0]; PassSubFocus(f, NIL, NIL);
WHILE info # NIL DO text := info.text; Texts.Delete(text, 0, text.len); INC(i); info := mem[i] END;
IF i = 1 THEN info := mem[0]; SetFocus(f, f.X + info.x, f.Y + f.H + info.y) END
END DeleteMarked;
(* Input *)
PROCEDURE CallMenuCommand(f: Frame; cap: Caption; beg: LONGINT);
VAR ch: CHAR; c: Caption; v: Viewers.Viewer; t: Texts.Text; buf: Texts.Buffer; r: Texts.Reader;
BEGIN v := Viewers.This(f.X, f.Y);
IF (v IS MenuViewers.Viewer) & (v.dsc # NIL) & (v.dsc IS TextFrames.Frame) THEN
t := v.dsc(TextFrames.Frame).text; Texts.OpenReader(r, t, 0); Texts.Read(r, ch);
IF ~r.eot THEN NEW(c);
IF ch = 22X THEN
REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch = 22X);
OpenCaption(c, t, 1, Texts.Pos(r) - 1)
ELSE
REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch = "|");
OpenCaption(c, t, 0, Texts.Pos(r) - 1)
END;
NEW(buf); Texts.OpenBuf(buf); Texts.Save(cap, beg, cap.len, buf); Texts.Append(c, buf);
Call(f, c, 0, FALSE)
END
END
END CallMenuCommand;
PROCEDURE SetAttributes(VAR w: Texts.Writer; c: Caption; pos: LONGINT; fnt: Fonts.Font; col, voff: SHORTINT);
VAR ch: CHAR; r: Texts.Reader;
BEGIN Texts.OpenReader(r, c, pos); Texts.Read(r, ch);
IF (r.eot OR (ch <= " ")) & (pos > 0) THEN DEC(pos); Texts.OpenReader(r, c, pos); Texts.Read(r, ch) END;
IF ~r.eot THEN Texts.SetFont(w, r.fnt); Texts.SetColor(w, r.col); Texts.SetOffset(w, r.voff)
ELSE Texts.SetFont(w, fnt); Texts.SetColor(w, col); Texts.SetOffset(w, voff)
END
END SetAttributes;
PROCEDURE Visible(fnt: Fonts.Font; ch: CHAR): BOOLEAN;
VAR pat: Display.Pattern; dx, x, y, w, h: INTEGER;
BEGIN Display.GetChar(fnt.raster, ch, dx, x, y, w, h, pat); RETURN dx > 0
END Visible;
PROCEDURE Consume*(f: Frame; ch: CHAR; fnt: Fonts.Font; col, voff: SHORTINT);
VAR pos: LONGINT; c: Caption; info: CapInfo;
BEGIN
IF f.focus = CaretFocus THEN c := f.focusPos.cap; pos := f.focusPos.pos;
IF ch = 0DX THEN (* CR *)
SetSelection(f, c, 0, pos)
ELSIF ch = 0AX THEN (* LF *)
CallMenuCommand(f, c, 0)
ELSIF ch = 0ACX THEN (* BRK *)
Call(f, c, 0, FALSE); Texts.Delete(c, 0, c.len)
ELSIF ch = 0C4X THEN (* <- *)
IF pos > 0 THEN SetCaret(f, c, pos - 1) END
ELSIF ch = 0C3X THEN (* -> *)
IF pos < c.len THEN SetCaret(f, c, pos + 1) END
ELSIF ch = 7FX THEN (* DEL *)
IF pos # 0 THEN
IF c.len # 1 THEN Texts.Delete(c, pos - 1, pos); SetCaret(f, c, pos - 1)
ELSE info := InfoAbout(f, c); Texts.Delete(c, pos - 1, pos); SetFocus(f, f.X + info.x, f.Y + f.H + info.y)
END
END
ELSIF (ch = 9X) OR (ch >= " ") THEN SetAttributes(wattr, c, pos, fnt, col, voff);
IF Visible(wattr.fnt, ch) THEN Texts.Write(wattr, ch); Texts.Insert(c, pos, wattr.buf);
SetCaret(f, c, pos + 1)
END
END
ELSIF (f.focus = PointFocus) & ((ch = 9X) OR ((ch >= " ") & (ch # 7FX))) & Visible(fnt, ch) THEN
NEW(c); OpenCaption(c, NIL, 0, 0);
Insert(f.panel, c, f.focusPos.x - f.X + f.x0, f.focusPos.y - (f.Y + f.H) + f.y0);
SetAttributes(wattr, c, 0, fnt, col, voff); Texts.Write(wattr, ch); Texts.Insert(c, 0, wattr.buf);
SetCaret(f, c, 1)
END
END Consume;
PROCEDURE ConsumeElem*(f: Frame; e: Texts.Elem);
VAR pos: LONGINT; c: Caption;
BEGIN
IF f.focus = CaretFocus THEN c := f.focusPos.cap; pos := f.focusPos.pos;
SetAttributes(wattr, c, pos, Fonts.Default, Display.white, 0);
Texts.WriteElem(wattr, e); Texts.Insert(c, pos, wattr.buf); SetCaret(f, c, pos + 1)
ELSIF f.focus = PointFocus THEN
NEW(c); OpenCaption(c, NIL, 0, 0);
Insert(f.panel, c, f.focusPos.x - f.X + f.x0, f.focusPos.y - (f.Y + f.H) + f.y0);
SetAttributes(wattr, c, 0, Fonts.Default, Display.white, 0);
Texts.WriteElem(wattr, e); Texts.Insert(c, 0, wattr.buf); SetCaret(f, c, 1)
END
END ConsumeElem;
PROCEDURE CopyOver*(f: Frame; text: Texts.Text; beg, end: LONGINT);
VAR pos: LONGINT; buf: Texts.Buffer; c: Caption;
BEGIN
IF f.focus = CaretFocus THEN c := f.focusPos.cap; pos := f.focusPos.pos;
NEW(buf); Texts.OpenBuf(buf); Texts.Save(text, beg, end, buf); Texts.Insert(c, pos, buf);
SetCaret(f, c, pos + end - beg)
ELSIF f.focus = PointFocus THEN NEW(c); OpenCaption(c, text, beg, end);
Insert(f.panel, c, f.x0 + f.focusPos.x - f.X, f.y0 + f.focusPos.y - (f.Y + f.H));
SetCaret(f, c, c.len)
END
END CopyOver;
(* Mouse Tracking *)
PROCEDURE AlignToGrid(f: Frame; VAR x, y: INTEGER);
VAR g, h, s: LONGINT;
BEGIN g := f.grid; h := g DIV 2;
IF g > 0 THEN s := f.x0 - f.X; x := SHORT(((x + s + h) DIV g) * g - s);
s := f.y0 - f.Y - f.H; y := SHORT(((y + s + h) DIV g) * g - s)
END
END AlignToGrid;
PROCEDURE TrackMouse(f: Frame; VAR x, y: INTEGER; VAR keys, keySum: SET; align: BOOLEAN);
VAR keys0: SET; x0, y0: INTEGER;
BEGIN keys0 := keys; x0 := x; y0 := y;
REPEAT Input.Mouse(keys, x, y); keySum := keySum + keys;
Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y);
IF x < f.X THEN x := f.X ELSIF x >= f.X + f.W THEN x := f.X + f.W END;
IF y < f.Y THEN y := f.Y ELSIF y >= f.Y + f.H THEN y := f.Y + f.H END;
IF align THEN AlignToGrid(f, x, y) END
UNTIL (keys # keys0) OR (x # x0) OR (y # y0)
END TrackMouse;
PROCEDURE TrackFocus*(f: Frame; VAR x, y: INTEGER; VAR keySum: SET);
VAR keys: SET;
BEGIN AlignToGrid(f, x, y);
REPEAT SetFocus(f, x, y); TrackMouse(f, x, y, keys, keySum, TRUE) UNTIL keys = {}
END TrackFocus;
PROCEDURE TrackCaption*(f: Frame; cap: Caption; VAR x, y, dx, dy: INTEGER; VAR keySum: SET);
VAR keys: SET; x0, y0, x1, y1: INTEGER; info: CapInfo;
BEGIN AlignToGrid(f, x, y); x0 := x; y0 := y;
info := InfoAbout(f, cap); x1 := f.X + info.x; y1 := f.Y + f.H + info.y; AlignToGrid(f, x1, y1);
REPEAT InvertRect(f, x1, y1, info.w - 1, info.h - 1);
TrackMouse(f, x, y, keys, keySum, TRUE); InvertRect(f, x1, y1, info.w - 1, info.h - 1);
x1 := x1 - x0 + x; x0 := x; y1 := y1 - y0 + y; y0 := y
UNTIL keys = {};
dx := x1 - f.X - info.x; dy := y1 - f.Y - f.H - info.y
END TrackCaption;
PROCEDURE TrackArea*(f: Frame; VAR x, y: INTEGER; VAR keySum: SET);
VAR keys: SET; x0, y0, x1, y1: INTEGER;
BEGIN x0 := x; y0 := y; x1 := x; y1 := y;
REPEAT TrackMouse(f, x, y, keys, keySum, FALSE);
IF x # x1 THEN InvertRect(f, x1, y0, x - x1, y1 - y0); x1 := x END;
IF y # y1 THEN InvertRect(f, x0, y1, x1 - x0, y - y1); y1 := y END
UNTIL keys = {};
InvertRect(f, x, y, x0 - x, y0 - y)
END TrackArea;
PROCEDURE TrackCaret*(f: Frame; cap: Caption; VAR x, y: INTEGER; VAR keySum: SET);
VAR keys: SET;
BEGIN
REPEAT SetCaret(f, cap, Offset(cap, SHORT(x - f.X + f.x0 - cap.x))); TrackMouse(f, x, y, keys, keySum, FALSE)
UNTIL keys = {}
END TrackCaret;
PROCEDURE LocateWord(f: Frame; cap: Caption; x0: INTEGER; VAR beg: LONGINT; VAR x, w: INTEGER);
VAR pos, end: LONGINT; ch: CHAR; r: Texts.Reader;
BEGIN pos := Offset(cap, SHORT(x0 - f.X + f.x0 - cap.x - Space)) + 1; beg := 0; end := 0;
Texts.OpenReader(r, cap, 0); Texts.Read(r, ch);
WHILE ~r.eot & (Texts.Pos(r) <= pos) DO beg := Texts.Pos(r) - 1;
IF ~r.eot & (r.elem # NIL) THEN Texts.Read(r, ch)
ELSE
WHILE ~r.eot & (ch > " ") DO Texts.Read(r, ch) END
END;
end := Texts.Pos(r) - 1;
WHILE ~r.eot & (r.elem = NIL) & (ch <= " ") DO Texts.Read(r, ch) END
END;
x := SHORT(f.X + cap.x + Space + Width(cap, 0, beg) - f.x0); w := Width(cap, beg, end)
END LocateWord;
PROCEDURE TrackWord*(f: Frame; cap: Caption; x, y: INTEGER; VAR keySum: SET; VAR beg: LONGINT);
CONST H = 2;
VAR x0, y0, x1, w0, w1: INTEGER; keys: SET;
BEGIN y0 := SHORT(cap.y - f.y0 + f.Y + f.H + Space);
LocateWord(f, cap, x, beg, x0, w0); Display.ReplConstC(f, Display.white, x0, y0, w0, H, Display.invert);
REPEAT TrackMouse(f, x, y, keys, keySum, FALSE);
LocateWord(f, cap, x, beg, x1, w1);
IF x0 # x1 THEN
Display.ReplConstC(f, Display.white, x0, y0, w0, H, Display.invert);
x0 := x1; w0 := w1;
Display.ReplConstC(f, Display.white, x0, y0, w0, H, Display.invert);
END
UNTIL keys = {};
Display.ReplConstC(f, Display.white, x0, y0, w0, H, Display.invert)
END TrackWord;
PROCEDURE TrackSelection*(f: Frame; cap: Caption; VAR x, y: INTEGER; VAR keySum: SET);
VAR keys: SET; beg, end: LONGINT;
BEGIN beg := Offset(cap, SHORT(x - f.X + f.x0 - cap.x - Space));
IF f.hasSel & (f.selBeg.cap = cap) THEN
IF beg = cap.len THEN beg := f.selBeg.pos
ELSIF (f.selBeg.pos = beg) & (f.selEnd.pos = beg + 1) THEN beg := 0
END
END;
REPEAT end := Offset(cap, SHORT(x - f.X + f.x0 - cap.x - Space)) + 1;
IF end <= beg THEN end := beg + 1 END;
SetSelection(f, cap, beg, end);
TrackMouse(f, x, y, keys, keySum, FALSE)
UNTIL keys = {}
END TrackSelection;
PROCEDURE TouchElem*(f: Frame; cap: Caption; VAR x, y: INTEGER; VAR keySum: SET);
VAR pos: LONGINT; l, b, w, minY, maxY: INTEGER; ch: CHAR; info: CapInfo;
r: Texts.Reader; msg: TextFrames.TrackMsg;
BEGIN pos := Offset(cap, SHORT(x - f.X + f.x0 - cap.x - Space)); Texts.OpenReader(r, cap, pos); Texts.Read(r, ch);
IF (r.elem # NIL) & (keySum = {MM}) THEN info := InfoAbout(f, cap); MeasureChar(r, ch, w, minY, maxY);
l := f.X + info.x + Space + Width(cap, 0, pos); b := f.Y + f.H + info.y + info.baseH + minY;
IF (l <= x) & (x < l + w) & (b <= y) & (y < b + maxY - minY)
& (f.X <= l) & (l + w < f.X + f.W) & (f.Y <= b) & (b + maxY - minY < f.Y + f.H)
THEN msg.fnt := r.fnt; msg.col := r.col; msg.pos := pos; msg.frame := f;
msg.X := x; msg.Y := y; msg.keys := keySum;
msg.X0 := l; msg.Y0 := b - minY + r.fnt.height * r.voff DIV 64 + r.fnt.minY;
r.elem.handle(r.elem, msg);
Input.Mouse(keySum, x, y)
END
END
END TouchElem;
PROCEDURE Edit*(f: Frame; x, y: INTEGER; keys: SET);
VAR k: SET; beg, end, time, grid: LONGINT; x0, y0, dx, dy: INTEGER; ch: CHAR; c: CapInfo;
subF: Display.Frame; text: Texts.Text; r: Texts.Reader; msg: Oberon.CopyOverMsg;
BEGIN
IF keys # {} THEN c := ThisCaption(f, x, y); x0 := x; y0 := y;
IF c = NIL THEN (* on background *)
IF keys = {ML} THEN TrackFocus(f, x, y, keys);
IF (keys = {ML, MM}) & (f.focus # NoFocus) THEN Oberon.GetSelection(text, beg, end, time);
IF time >= 0 THEN CopyOver(f, text, beg, end) END
END
ELSIF keys = {MM} THEN
REPEAT TrackMouse(f, x, y, k, keys, TRUE) UNTIL k = {};
IF keys = {MM} THEN grid := f.grid;
IF grid > 0 THEN
dx := SHORT(((x - x0 + grid DIV 2) DIV grid) * grid);
dy := SHORT(((y - y0 + grid DIV 2) DIV grid) * grid);
MoveMarked(f, dx, dy)
ELSE MoveMarked(f, x - x0, y - y0)
END
ELSIF keys = {MM, ML} THEN Scroll(f, f.x0, f.y0)
ELSIF keys = {MM, MR} THEN Scroll(f, x - x0, y - y0)
END
ELSIF keys = {MR} THEN UnmarkAllCaptions(f); TrackArea(f, x, y, keys);
IF keys = {MR} THEN MarkArea(f, x, y, x0 - x, y0 - y)
ELSIF keys = {MR, ML} THEN MarkArea(f, x, y, x0 - x, y0 - y); DeleteMarked(f)
END
END
ELSIF (x >= c.x + c.w + f.X - Space) & (y < c.y + f.Y + f.H + Space) THEN (* on hot spot *)
IF keys = {ML} THEN Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y)
ELSIF keys = {MM} THEN AlignToGrid(f, x, y); TrackCaption(f, c.text, x, y, dx, dy, keys);
IF (keys = {MM}) & ((x # x0) OR (y # y0)) THEN Move(c.text, c.text.x + dx, c.text.y + dy) END
ELSIF keys = {MR} THEN
InvertRect(f, f.X + c.x, f.Y + f.H + c.y, c.w - 1, c.h - 1);
REPEAT TrackMouse(f, x, y, k, keys, FALSE) UNTIL k = {};
InvertRect(f, f.X + c.x, f.Y + f.H + c.y, c.w - 1, c.h - 1);
IF keys = {MR} THEN
IF IsMarked(f, c.text) THEN UnmarkCaption(f, c.text) ELSE MarkCaption(f, c.text) END
ELSIF keys = {MR, MM} THEN msg.text := c.text; msg.beg := 0; msg.end := c.text.len;
Oberon.FocusViewer.handle(Oberon.FocusViewer, msg)
ELSIF (keys = {MR, ML}) THEN PassSubFocus(f, NIL, NIL);
Texts.Delete(c.text, 0, c.text.len); SetFocus(f, f.X + c.x, f.Y + f.H + c.y)
END
END
ELSE (* within caption *)
IF HasOverlap(c) THEN BringToFront(c.text) END;
subF := ThisSubFrame(f, x, y);
IF (subF # NIL) & (keys = {ML}) THEN
REPEAT TrackMouse(f, x, y, k, keys, FALSE) UNTIL k = {};
IF keys = {ML} THEN PassSubFocus(f, c.text, subF) END
ELSE TouchElem(f, c.text, x, y, keys);
IF keys = {ML} THEN TrackCaret(f, c.text, x, y, keys);
IF f.focus # NoFocus THEN
IF keys = {ML, MM} THEN Oberon.GetSelection(text, beg, end, time);
IF time >= 0 THEN CopyOver(f, text, beg, end) END
ELSIF keys = {ML, MR} THEN Oberon.GetSelection(text, beg, end, time);
IF time >= 0 THEN Texts.OpenReader(r, c.text, f.focusPos.pos); Texts.Read(r, ch);
Texts.ChangeLooks(text, beg, end, {0, 1, 2}, r.fnt, r.col, r.voff)
END
END
END
ELSIF keys = {MM} THEN TrackWord(f, c.text, x, y, keys, beg);
IF ~(MR IN keys) THEN Call(f, c.text, beg, ML IN keys)
ELSIF keys = {MM, MR} THEN CallMenuCommand(f, c.text, beg)
END
ELSIF keys = {MR} THEN TrackSelection(f, c.text, x, y, keys);
IF f.hasSel THEN beg := f.selBeg.pos;
IF keys = {MR, ML} THEN Texts.Delete(c.text, beg, f.selEnd.pos);
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
ELSIF keys = {MR, MM} THEN msg.text := c.text; msg.beg := beg; msg.end := f.selEnd.pos;
Oberon.FocusViewer.handle(Oberon.FocusViewer, msg)
END
END
END
END
END
ELSE Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, x, y)
END
END Edit;
(* Frame Data *)
PROCEDURE OpenFrame*(f: Frame; handler: Display.Handler; panel: Panel; x0, y0, grid: LONGINT);
BEGIN f.handle := handler; f.panel := panel; f.x0 := x0; f.y0 := y0; f.grid := grid;
f.focus := NoFocus; f.hasSel := FALSE; f.subFocus := NIL;
f.info := NIL;
f.selBeg.cap := NIL; f.selEnd.cap := NIL; f.focusPos.cap := NIL
END OpenFrame;
PROCEDURE Copy*(src, dest: Frame);
BEGIN OpenFrame(dest, src.handle, src.panel, src.x0, src.y0, src.grid)
END Copy;
(* General *)
PROCEDURE NotifyElems* (parent: Frame; VAR msg: Display.FrameMsg);
VAR f: Display.Frame;
BEGIN
IF msg IS TextFrames.NotifyMsg THEN msg(TextFrames.NotifyMsg).frame := parent END;
f := parent.dsc;
WHILE f # NIL DO f.handle(f, msg); f := f.next END
END NotifyElems;
PROCEDURE Handler*(f: Display.Frame; VAR msg: Display.FrameMsg);
VAR self, copy: Frame; subf: Display.Frame;
BEGIN self := f(Frame);
IF msg IS Oberon.CopyMsg THEN NEW(copy); Copy(self, copy); msg(Oberon.CopyMsg).F := copy
ELSIF msg IS Oberon.ControlMsg THEN NotifyElems(self, msg);
WITH msg: Oberon.ControlMsg DO
IF msg.id = Oberon.neutralize THEN NotifyElems(self, msg); Neutralize(self)
ELSIF msg.id = Oberon.defocus THEN Defocus(self)
END
END
ELSIF msg IS Oberon.InputMsg THEN
WITH msg: Oberon.InputMsg DO subf := self.subFocus;
IF msg.id = Oberon.track THEN
IF (subf # NIL) & (subf.X <= msg.X) & (msg.X < subf.X + subf.W) & (subf.Y <= msg.Y) & (msg.Y < subf.Y + subf.H)
THEN subf.handle(subf, msg)
ELSE Edit(self, msg.X, msg.Y, msg.keys)
END
ELSIF msg.id = Oberon.consume THEN
IF self.focus # NoFocus THEN Consume(self, msg.ch, msg.fnt, msg.col, msg.voff)
ELSIF subf # NIL THEN subf.handle(subf, msg)
END
END
END
ELSIF msg IS Oberon.CopyOverMsg THEN NotifyElems(self, msg);
WITH msg: Oberon.CopyOverMsg DO CopyOver(self, msg.text, msg.beg, msg.end) END
ELSIF msg IS Oberon.SelectionMsg THEN NotifyElems(self, msg);
WITH msg: Oberon.SelectionMsg DO
IF self.hasSel & (msg.time < self.selTime) THEN
msg.time := self.selTime; msg.text := self.selBeg.cap;
msg.beg := self.selBeg.pos; msg.end := self.selEnd.pos
END
END
ELSIF msg IS TextFrames.InsertElemMsg THEN subf := self.subFocus;
IF self.focus # NoFocus THEN ConsumeElem(self, msg(TextFrames.InsertElemMsg).e)
ELSIF subf # NIL THEN subf.handle(subf, msg)
END
ELSIF msg IS MenuViewers.ModifyMsg THEN
WITH msg: MenuViewers.ModifyMsg DO
IF msg.id = MenuViewers.reduce THEN Reduce(self, msg.Y, msg.H, msg.dY)
ELSIF msg.id = MenuViewers.extend THEN Extend(self, msg.Y, msg.H, msg.dY)
END
END
ELSIF msg IS UpdateMsg THEN NotifyElems(self, msg);
WITH msg: UpdateMsg DO
IF msg.panel = self.panel THEN Update(self, msg.op, msg.cap, msg.beg, msg.end) END
END
ELSE NotifyElems(self, msg)
END
END Handler;
PROCEDURE NewFrame*(panel: Panel; x0, y0, grid: LONGINT): Frame;
VAR f: Frame;
BEGIN NEW(f); OpenFrame(f, Handler, panel, x0, y0, grid); RETURN f
END NewFrame;
PROCEDURE NotifyDisplay*(panel: Panel; caption: Caption; op, beg, end: LONGINT);
VAR msg: UpdateMsg;
BEGIN msg.panel := panel; msg.cap := caption; msg.op := op; msg.beg := beg; msg.end := end;
Viewers.Broadcast(msg)
END NotifyDisplay;
(* Editor *)
PROCEDURE UnmarkMenu(f: Frame);
VAR ch: CHAR; t: Texts.Text; v: Viewers.Viewer; r: Texts.Reader;
BEGIN v := Viewers.This(f.X, f.Y);
IF (v IS MenuViewers.Viewer) & (v.dsc # NIL) & (v.dsc IS TextFrames.Frame) THEN t := v.dsc(TextFrames.Frame).text;
IF t.len > 0 THEN Texts.OpenReader(r, t, t.len - 1); Texts.Read(r, ch);
IF ch = "!" THEN Texts.Delete(t, t.len - 1, t.len) END
END
END
END UnmarkMenu;
PROCEDURE OpenViewer(x, y: INTEGER);
VAR i, j, beg, end, time: LONGINT; ch: CHAR; name: ARRAY 66 OF CHAR;
p: Panel; v: MenuViewers.Viewer; text: Texts.Text; s: Texts.Scanner;
BEGIN Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s);
IF (s.line # 0) OR ((s.class = Texts.Char) & (s.c = "^")) THEN Oberon.GetSelection(text, beg, end, time);
IF time >= 0 THEN Texts.OpenScanner(s, text, beg); Texts.Scan(s) END
END;
IF (s.line = 0) & ((s.class = Texts.Name) OR (s.class = Texts.String)) THEN
p := ThisPanel(Files.Old(s.s), NotifyDisplay);
IF s.class = Texts.String THEN name[0] := 22X; i := 1 ELSE name[0] := 22X; i := 0 END;
j := 0;
REPEAT ch := s.s[j]; name[j + i] := ch; INC(j) UNTIL ch = 0X;
IF s.class = Texts.String THEN name[j] := 22X; name[j + 1] := 0X END;
v := MenuViewers.New(
TextFrames.NewMenu(name, Menu),
NewFrame(p, 0, 0, 5),
TextFrames.menuH, x, y
END
END OpenViewer;
PROCEDURE SysOpen*;
VAR x, y: INTEGER;
BEGIN Oberon.AllocateSystemViewer(Oberon.Mouse.X, x, y); OpenViewer(x, y)
END SysOpen;
PROCEDURE Open*;
VAR x, y: INTEGER;
BEGIN Oberon.AllocateUserViewer(Oberon.Mouse.X, x, y); OpenViewer(x, y)
END Open;
PROCEDURE Store*;
VAR beg, end, time: LONGINT; i: INTEGER; ch: CHAR; frame: Frame;
v: Viewers.Viewer; text: Texts.Text; s: Texts.Scanner; f: Files.File;
bak: ARRAY 68 OF CHAR;
BEGIN frame := NIL;
IF (Oberon.Par.vwr IS MenuViewers.Viewer)
& (Oberon.Par.vwr.dsc = Oberon.Par.frame) & (Oberon.Par.frame.next IS Frame)
THEN frame := Oberon.Par.frame.next(Frame);
Texts.OpenScanner(s, Oberon.Par.frame(TextFrames.Frame).text, 0); Texts.Scan(s)
ELSE v := Oberon.MarkedViewer();
IF (v IS MenuViewers.Viewer) & (v.dsc.next IS Frame) THEN
frame := v.dsc.next(Frame);
Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s);
IF (s.line = 0) & (s.class = Texts.Char) & (s.c = "^") THEN Oberon.GetSelection(text, beg, end, time);
IF time >= 0 THEN Texts.OpenScanner(s, text, beg); Texts.Scan(s) END
ELSIF (s.line # 0) OR ((s.class = Texts.Char) & (s.c = "*")) THEN
Texts.OpenScanner(s, v.dsc(TextFrames.Frame).text, 0); Texts.Scan(s)
END
END
END;
IF (frame # NIL) & (s.line = 0) & ((s.class = Texts.Name) OR (s.class = Texts.String)) THEN
Texts.WriteString(w, "CaptionEdit.Store "); Texts.WriteString(w, s.s); Texts.Write(w, " ");
Texts.Append(Oberon.Log, w.buf);
f := File(frame.panel, s.s);
i := 0; ch := s.s[0];
WHILE ch # 0X DO bak[i] := ch; INC(i); ch := s.s[i] END;
bak[i] := "."; INC(i); bak[i] := "B"; INC(i); bak[i] := "a"; INC(i); bak[i] := "k"; INC(i); bak[i] := 0X;
Files.Rename(s.s, bak, i); Files.Register(f);
Texts.WriteInt(w, Files.Length(f), 0); Texts.WriteLn(w); Texts.Append(Oberon.Log, w.buf);
UnmarkMenu(frame)
END
END Store;
PROCEDURE SetGrid*;
VAR beg, end, time: LONGINT; h: INTEGER; frame: Frame; v: Viewers.Viewer; text: Texts.Text; s: Texts.Scanner;
BEGIN frame := NIL;
IF (Oberon.Par.vwr IS MenuViewers.Viewer)
& (Oberon.Par.vwr.dsc = Oberon.Par.frame) & (Oberon.Par.frame.next IS Frame)
THEN frame := Oberon.Par.frame.next(Frame)
ELSE v := Oberon.MarkedViewer();
IF (v IS MenuViewers.Viewer) & (v.dsc.next IS Frame) THEN frame := v.dsc.next(Frame) END
END;
IF frame # NIL THEN Texts.OpenScanner(s, Oberon.Par.text, Oberon.Par.pos); Texts.Scan(s);
IF (s.line # 0) OR ((s.class = Texts.Char) & (s.c = "^")) THEN Oberon.GetSelection(text, beg, end, time);
IF time >= 0 THEN Texts.OpenScanner(s, text, beg); Texts.Scan(s) END
END;
IF (s.line = 0) & (s.class = Texts.Int) & (s.i >= 0) THEN frame.grid := s.i;
h := frame.H; Reduce(frame, frame.Y, 0, 0); Extend(frame, frame.Y, h, 0)
END
END
END SetGrid;
BEGIN framePat := Display.grey1;
Texts.OpenWriter(w); Texts.OpenWriter(wattr);
Texts.WriteString(w, VersionName); Texts.WriteLn(w); Texts.Append(Oberon.Log, w.buf)
END CaptionEdit.