Syntax10.Scn.Fnt StampElems Alloc 29 May 96 VersionElems AllocBeg Syntax10.Scn.Fnt PowerMac Windows AmigaAmiga PowerMac Syntax10.Scn.Fnt DELR = 008X; PageUp = 0ACX; PageDown = 0ADX; Home = 091X; BRK = 0B9X;Windows Syntax10.Scn.Fnt DELR = 0A1X; PageUp = 0C5X; PageDown = 0C6X; Home = 0C8X; BRK = 0ACX;Amiga VersionElems AllocEnd Syntax10i.Scn.Fnt Syntax10b.Scn.Fnt FoldElems Syntax10i.Scn.Fnt forward declarations MarkElems Alloc Syntax10i.Scn.Fnt track mouse Syntax10i.Scn.Fnt open subdirectory Syntax10i.Scn.Fnt open file MODULE Dir; (** HM IMPORT Files, Display, Input, Viewers, Texts, TextFrames, MenuViewers, Oberon, Directories, Strings, In, Out; CONST DELR = 0A1X; PageUp = 0C5X; PageDown = 0C6X; Home = 0C8X; BRK = 0ACX; delimiter = Directories.delimiter; left =2; middle = 1; right = 0; delete = 0; copy = 1; move = 2; select = 3; unselect = 4; (*opcodes for ForAllElemsDo*) pixel = LONG(10000); CR = 0DX; DEL = 7FX; CRSU = 0C1X; CRSD = 0C2X; Elem* = POINTER TO ElemDesc; ElemDesc* = RECORD (Texts.ElemDesc) name*: ARRAY 32 OF CHAR; sel*: BOOLEAN; icon, iconSel, iconCopy: Display.Pattern END ; Text* = POINTER TO TextDesc; TextDesc* = RECORD (Texts.TextDesc) path*: ARRAY 128 OF CHAR END ; UpdateMsg = RECORD (Display.FrameMsg) op: INTEGER; path, name: ARRAY 128 OF CHAR END ; Opener = POINTER TO OpenerDesc; (*list of commands to open files*) OpenerDesc = RECORD pattern, cmd: ARRAY 32 OF CHAR; next: Opener END ; scratch: Texts.Text; invalidOp: BOOLEAN; (*TRUE if text operation suppressed in Notify*) ignoreNeutralize: BOOLEAN; w: Texts.Writer; fileIcon, fileIconSel, fileIconCopy: Display.Pattern; (*x=0, y=0, w=15, h=13*) dirIcon, dirIconSel, dirIconCopy: Display.Pattern; (*x=0, y=0, w=15, h=13*) copyHull, moveHull: Display.Pattern; copyMarker, moveMarker: Oberon.Marker; mask: ARRAY 32 OF CHAR; openers: Opener; CBtext: Texts.Text; (* global variable for callback procedure MakeEntry *) oldNotifier: Directories.Notifier; PROCEDURE ^ NoNotify (t: Texts.Text; op: INTEGER; beg, end: LONGINT); PROCEDURE ^ Notify (t: Texts.Text; op: INTEGER; beg, end: LONGINT); PROCEDURE ^ NotifyDir* (op: INTEGER; path, name: ARRAY OF CHAR); PROCEDURE ^ HandleElem* (e: Texts.Elem; VAR m: Texts.ElemMsg); PROCEDURE ^ HandleFrame*(f: Display.Frame; VAR m: Display.FrameMsg); PROCEDURE (e: Elem) Update; VAR m: TextFrames.UpdateMsg; BEGIN m.id := TextFrames.replace; m.text := Texts.ElemBase(e); m.beg := Texts.ElemPos(e); m.end := m.beg + 1; Viewers.Broadcast(m) END Update; PROCEDURE (e: Elem) Restore; VAR r: Texts.Reader; t: Texts.Text; pos1, pos2: LONGINT; BEGIN t := Texts.ElemBase(e); pos1 := Texts.ElemPos(e) + 1; Texts.OpenReader(r, t, pos1); Texts.ReadElem(r); IF r.eot THEN pos2 := t.len -1 ELSE pos2 := Texts.Pos(r) - 2 END ; Texts.Delete(t, pos1, pos2); Texts.WriteString(w, e.name); Texts.Insert(t, pos1, w.buf) END Restore; PROCEDURE (e: Elem) Check; VAR s: Texts.Scanner; t: Texts.Text; old, new: ARRAY 256 OF CHAR; res: INTEGER; BEGIN t := Texts.ElemBase(e); Texts.OpenScanner(s, t, Texts.ElemPos(e) + 1); Texts.Scan(s); IF (s.class = Texts.Name) & (s.line = 0) THEN IF e.name # s.s THEN COPY(t(Text).path, old); Strings.Append(delimiter, old); COPY(old, new); Strings.Append(e.name, old); Strings.Append(s.s, new); Files.Rename(old, new, res); IF res > 1 THEN Out.String("-- failed$"); e.Restore END END ELSE Out.String("-- failed$"); e.Restore END Check; PROCEDURE Diff (VAR a, b: ARRAY OF CHAR): INTEGER; VAR i, d: INTEGER; BEGIN i := 0; LOOP IF CAP(a[i]) = CAP(b[i]) THEN IF a[i] = 0X THEN d := 0; EXIT END ELSIF CAP(a[i]) < CAP(b[i]) THEN d := -1; EXIT ELSE d := 1; EXIT END ; INC(i) END ; RETURN d END Diff; PROCEDURE NewElem (name: ARRAY OF CHAR; isDir: BOOLEAN): Elem; VAR e: Elem; BEGIN NEW(e); e.W := 15 * pixel; e.H := 13 * pixel; e.handle := HandleElem; COPY(name, e.name); e.sel := FALSE; IF isDir THEN e.icon := dirIcon; e.iconSel := dirIconSel; e.iconCopy := dirIconCopy ELSE e.icon := fileIcon; e.iconSel := fileIconSel; e.iconCopy := fileIconCopy END ; RETURN e END NewElem; PROCEDURE ThisElem (t: Texts.Text; pos: LONGINT): Elem; VAR r: Texts.Reader; ch: CHAR; BEGIN Texts.OpenReader(r, t, pos); Texts.Read(r, ch); IF ch # Texts.ElemChar THEN Texts.ReadPrevElem(r) END ; IF r.eot THEN RETURN NIL ELSE RETURN r.elem(Elem) END END ThisElem; PROCEDURE FindElem (t: Texts.Text; name: ARRAY OF CHAR; VAR e: Elem; VAR pos: LONGINT; VAR d: INTEGER); VAR r: Texts.Reader; BEGIN Texts.OpenReader(r, t, 0); Texts.ReadElem(r); LOOP IF r.eot THEN e := NIL; d := 1; pos := t.len; RETURN END ; IF r.elem IS Elem THEN e := r.elem(Elem); d := Diff(e.name, name); IF d >= 0 THEN pos := Texts.ElemPos(e); EXIT END END ; Texts.ReadElem(r) END FindElem; PROCEDURE CopyFile (e:Elem; src, dst: Text); CONST bufSize = 4096; VAR sn, dn: ARRAY 128 OF CHAR; sf, df: Files.File; sr, dr: Files.Rider; len: LONGINT; buf: ARRAY bufSize OF CHAR; BEGIN COPY(src.path, sn); Strings.Append(delimiter, sn); Strings.Append(e.name, sn); COPY(dst.path, dn); Strings.Append(delimiter, dn); Strings.Append(e.name, dn); sf := Files.Old(sn); Files.Set(sr, sf, 0); df := Files.New(dn); Files.Set(dr, df, 0); REPEAT Files.ReadBytes(sr, buf, bufSize); Files.WriteBytes(dr, buf, bufSize - sr.res) UNTIL sr.res # 0; Files.Register(df); END CopyFile; PROCEDURE MoveFile (e:Elem; src, dst: Text); VAR sn, dn: ARRAY 256 OF CHAR; res: INTEGER; BEGIN COPY(src.path, sn); Strings.Append(delimiter, sn); Strings.Append(e.name, sn); COPY(dst.path, dn); Strings.Append(delimiter, dn); Strings.Append(e.name, dn); Files.Rename(sn, dn, res); END MoveFile; PROCEDURE MakeEntry(d: Directories.Directory; name: ARRAY OF CHAR; isDir: BOOLEAN; VAR continue: BOOLEAN); VAR e: Elem; pos: LONGINT; found: INTEGER; BEGIN Texts.WriteElem(w, NewElem(name, isDir)); Texts.WriteString(w, name); Texts.WriteLn(w); FindElem(CBtext, name, e, pos, found); Texts.Insert(CBtext, pos, w.buf) END MakeEntry; PROCEDURE OpenDir (d: Directories.Directory); VAR t: Text; f: TextFrames.Frame; x, y: INTEGER; v: Viewers.Viewer; BEGIN NEW(t); Texts.Open(t, ""); COPY(d.path, t.path); t.notify := NoNotify; CBtext := t; Directories.Enumerate(d, MakeEntry); CBtext := NIL; t.notify := Notify; NEW(f); TextFrames.Open(f, t, 0); f.handle := HandleFrame; Oberon.AllocateSystemViewer(0, x, y); v := MenuViewers.New( TextFrames.NewMenu(d.path, "^Dir.Menu.Text"), f, TextFrames.menuH, x, y) END OpenDir; PROCEDURE ForAllElemsDo (op: INTEGER; src, dst: Text); VAR r: Texts.Reader; beg, end: LONGINT; f: TextFrames.Frame; e: Elem; path: ARRAY 256 OF CHAR; res: INTEGER; m: UpdateMsg; BEGIN IF src = NIL THEN RETURN END ; end := src.len; Texts.OpenReader(r, src, end); LOOP Texts.ReadPrevElem(r); IF r.eot THEN EXIT END ; IF r.elem IS Elem THEN e := r.elem(Elem); beg := Texts.Pos(r); IF e.sel THEN IF op = delete THEN COPY(src.path, path); Strings.Append(delimiter, path); Strings.Append(e.name, path); Files.Delete(path, res); IF res # 0 THEN Out.String("-- failed$") ELSE m.op := Directories.delete; COPY(src.path, m.path); COPY(e.name, m.name); (*< f.X+f.W) OR (y < f.Y) OR (y > f.Y+f.H)) DO f := f.next END ; IF f IS TextFrames.Frame THEN RETURN f(TextFrames.Frame) ELSE RETURN NIL END ThisFrame; PROCEDURE TargetText(): Text; VAR f: TextFrames.Frame; BEGIN IF Oberon.Par.frame = Oberon.Par.vwr.dsc THEN f := Oberon.Par.frame.next(TextFrames.Frame) ELSE f := ThisFrame(Oberon.Pointer.X, Oberon.Pointer.Y); END ; IF f.text IS Text THEN RETURN f.text(Text) ELSE RETURN NIL END END TargetText; PROCEDURE Init; VAR line: ARRAY 14 OF SET; s: Texts.Scanner; x: Opener; BEGIN line[1] := {}; line[2] := {1..8}; line[3] := {1, 8}; line[4] := {1, 8}; line[5] := {1, 8}; line[6] := {1, 8}; line[7] := {1, 8}; line[8] := {1, 8}; line[9] := {1, 5..8}; line[10] := {1, 5, 7}; line[11] := {1, 5, 6}; line[12] := {1..5}; line[13] := {}; fileIcon := Display.NewPattern(line, 15, 13); line[1] := {}; line[2] := {1..8}; line[3] := {1, 4, 8}; line[4] := {1, 4, 8}; line[5] := {1, 2..6, 8}; line[6] := {1, 4, 8}; line[7] := {1, 4, 8}; line[8] := {1, 8}; line[9] := {1, 5..8}; line[10] := {1, 5, 7}; line[11] := {1, 5, 6}; line[12] := {1..5}; line[13] := {}; fileIconCopy := Display.NewPattern(line, 15, 13); line[1] := {}; line[2] := {1..8}; line[3] := {1..8}; line[4] := {1..8}; line[5] := {1..8}; line[6] := {1..8}; line[7] := {1..8}; line[8] := {1..8}; line[9] := {1..8}; line[10] := {1..5, 7}; line[11] := {1..6}; line[12] := {1..5}; line[13] := {}; fileIconSel := Display.NewPattern(line, 15, 13); line[1] := {}; line[2] := {}; line[3] := {1..10}; line[4] := {1, 10}; line[5] := {1, 10}; line[6] := {1, 10}; line[7] := {1, 10}; line[8] := {1, 10}; line[9] := {1, 10}; line[10] := {1..10}; line[11] := {1, 5}; line[12] := {1..4}; line[13] := {}; dirIcon := Display.NewPattern(line, 15, 13); line[1] := {}; line[2] := {}; line[3] := {1..10}; line[4] := {1, 6, 10}; line[5] := {1, 6, 10}; line[6] := {1, 4..8, 10}; line[7] := {1, 6, 10}; line[8] := {1, 6, 10}; line[9] := {1, 10}; line[10] := {1..10}; line[11] := {1, 5}; line[12] := {1..4}; line[13] := {}; dirIconCopy := Display.NewPattern(line, 15, 13); line[1] := {}; line[2] := {}; line[3] := {1..10}; line[4] := {1..10}; line[5] := {1..10}; line[6] := {1..10}; line[7] := {1..10}; line[8] := {1..10}; line[9] := {1..10}; line[10] := {1..10}; line[11] := {1, 5}; line[12] := {1..4}; line[13] := {}; dirIconSel := Display.NewPattern(line, 15, 13); copyMarker.Draw := DrawCopy; copyMarker.Fade := DrawCopy; moveMarker.Draw := DrawMove; moveMarker.Fade := DrawMove; Texts.OpenWriter(w); ignoreNeutralize := FALSE; oldNotifier := Directories.notify; Directories.notify := NotifyDir; NEW(scratch); Texts.Open(scratch, "Dir.Menu.Text"); scratch.notify := NoNotify; Texts.OpenScanner(s, scratch, 0); openers := NIL; REPEAT Texts.Scan(s) UNTIL s.eot OR (s.line # 0); WHILE s.class = Texts.String DO NEW(x); COPY(s.s, x.pattern); Texts.Scan(s); IF s.class IN {Texts.Name, Texts.String} THEN COPY(s.s, x.cmd); Texts.Scan(s); x.next := openers; openers := x END END Init; PROCEDURE NoNotify (t: Texts.Text; op: INTEGER; beg, end: LONGINT); END NoNotify; PROCEDURE Notify (t: Texts.Text; op: INTEGER; beg, end: LONGINT); VAR b: Texts.Buffer; r: Texts.Reader; ch: CHAR; pos: LONGINT; BEGIN IF op = TextFrames.delete THEN pos := scratch.len; Texts.Recall(b); Texts.Append(scratch, b); Texts.OpenReader(r, scratch, pos); REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch = CR) OR (ch = Texts.ElemChar) & (r.elem IS Elem); IF ~r.eot THEN invalidOp := TRUE; t.notify := NoNotify; Texts.Save(scratch, pos, t.len, b); Texts.Insert(t, beg, b); t.notify := Notify; op := TextFrames.replace; end := beg END ELSIF op = TextFrames.insert THEN Texts.OpenReader(r, t, beg); pos := beg; REPEAT Texts.Read(r, ch); INC(pos) UNTIL (pos > end) OR (ch = CR) OR (ch = Texts.ElemChar) & (r.elem IS Elem); IF pos <= end THEN invalidOp := TRUE; t.notify := NoNotify; Texts.Delete(t, beg, end); t.notify := Notify; op := TextFrames.replace; end := beg END END ; ignoreNeutralize := TRUE; TextFrames.NotifyDisplay(t, op, beg, end); ignoreNeutralize := FALSE END Notify; PROCEDURE NotifyDir* (op: INTEGER; path, name: ARRAY OF CHAR); VAR m: UpdateMsg; BEGIN m.op := op; COPY(path, m.path); COPY(name, m.name); Viewers.Broadcast(m); oldNotifier(op, path, name) END NotifyDir; PROCEDURE HandleElem* (e: Texts.Elem; VAR m: Texts.ElemMsg); CONST moved = -1000; VAR e1: Elem; x, y, x0, y0, dsr, res: INTEGER; keys: SET; t: Texts.Text; path: ARRAY 128 OF CHAR; marker: Oberon.Marker; f: TextFrames.Frame; src, dst: Text; d: Directories.Directory; name: ARRAY 256 OF CHAR; o: Opener; BEGIN WITH e: Elem DO WITH m: Texts.CopyMsg DO IF m.e = NIL THEN NEW(e1); m.e := e1 ELSE e1 := m.e(Elem) END ; Texts.CopyElem(e, e1); COPY(e.name, e1.name); e1.sel := e.sel; e1.icon := e.icon; e1.iconSel := e.iconSel; e1.iconCopy := e.iconCopy | m: TextFrames.DisplayMsg DO IF ~m.prepare THEN IF e.sel THEN Display.CopyPattern(Display.white, e.iconSel, m.X0, m.Y0+1, Display.replace) ELSE Display.CopyPattern(Display.white, e.icon, m.X0, m.Y0+1, Display.replace) END END | m: TextFrames.TrackMsg DO IF middle IN m.keys THEN e.sel := ~e.sel; e.Update; src := m.frame(TextFrames.Frame).text(Text); Input.Mouse(keys, x0, y0); marker := Oberon.Arrow; copyHull := e.iconCopy; moveHull := e.icon; REPEAT Input.Mouse(keys, x, y); m.keys := m.keys + keys; IF (e.icon # dirIcon) & ((ABS(x-x0) > 10) OR (ABS(y-y0) > 10)) THEN x0 := moved; IF m.keys = {middle, left} THEN marker := copyMarker ELSE marker := moveMarker END END ; Oberon.DrawCursor(Oberon.Mouse, marker, x, y) UNTIL keys = {}; IF m.keys = {middle, right} THEN IF e.icon = dirIcon THEN t := Texts.ElemBase(e); COPY(t(Text).path, path); Strings.Append(delimiter, path); Strings.Append(e.name, path); OpenDir(Directories.This(path)) ELSE d := Directories.Current(); name := ""; IF src.path # d.path THEN Strings.Append(src.path, name); Strings.Append(delimiter, name) END ; Strings.Append(e.name, name); o := openers; WHILE (o # NIL) & ~Strings.Match(e.name, o.pattern) DO o := o.next END ; IF o = NIL THEN NEW(o); o.cmd := "Edit.Open" END ; Oberon.Par.vwr := Viewers.This(m.X, m.Y); Oberon.Par.frame := m.frame; Oberon.Par.pos := scratch.len; Oberon.Par.text := scratch; Texts.WriteString(w, name); Texts.Append(scratch, w.buf); Oberon.Call(o.cmd, Oberon.Par, FALSE, res) END ; e.sel := FALSE; e.Update ELSIF (x0 = moved) & e.sel THEN f := ThisFrame(x, y); IF (f # NIL) & (f # m.frame) & (f.text IS Text) THEN dst := f.text(Text); IF m.keys = {middle} THEN ForAllElemsDo(move, src, dst) ELSIF m.keys = {middle, left} THEN ForAllElemsDo(copy, src, dst) END END END END ELSE END END HandleElem; PROCEDURE HandleFrame* (f: Display.Frame; VAR m: Display.FrameMsg); VAR ready: BOOLEAN; e: Elem; keys: SET; x, y, d: INTEGER; t: Texts.Text; pos, pos2: LONGINT; r: Texts.Reader; ch: CHAR; path: ARRAY 256 OF CHAR; dir: Directories.Directory; PROCEDURE CheckChar(ch: CHAR): BOOLEAN; BEGIN RETURN (ch=".") OR ((ch>="0") & (ch<="9")) OR ((ch>="A") & (ch<="Z")) OR ((ch>="a") & (ch<="z")); END CheckChar; BEGIN ready := FALSE; WITH f: TextFrames.Frame DO WITH m: Oberon.InputMsg DO IF m.id = Oberon.consume THEN pos := f.carloc.pos; e := ThisElem(f.text, pos); IF (m.ch = CR) OR (m.ch = PageUp) OR (m.ch = PageDown) OR (m.ch = Home) OR (m.ch = BRK) THEN e.Check; TextFrames.RemoveCaret(f); ready := TRUE ELSIF (m.ch = CRSU) OR (m.ch = CRSD) THEN e.Check ELSIF m.ch = DEL THEN invalidOp := FALSE; TextFrames.Handle(f, m); ready := TRUE; IF invalidOp THEN e := ThisElem(f.text, pos); e.Check; TextFrames.RemoveCaret(f) END ELSE Texts.OpenReader(r, f.text, f.carloc.pos); Texts.Read(r, ch); ready := (ch = Texts.ElemChar) & (r.elem IS Elem); (* We have to check here wether the char is allowed or not. Allowed chars; letters, digits, "." *) IF ~CheckChar(m.ch) THEN ready := TRUE END; END ELSIF (m.id = Oberon.track) & f.hasCar & (m.keys * {middle, right} # {}) THEN e := ThisElem(f.text, f.carloc.pos); IF (e # NIL) & (ThisElem(f.text, TextFrames.Pos(f, x, y)) # e) THEN e.Check END END | m: Oberon.ControlMsg DO IF m.id = Oberon.defocus THEN e := ThisElem(f.text, f.carloc.pos); Input.Mouse(keys, x, y); IF (e # NIL) & ((f # ThisFrame(x, y)) OR (ThisElem(f.text, TextFrames.Pos(f, x, y)) # e)) THEN e.Check END ELSIF (m.id = Oberon.neutralize) & ~ignoreNeutralize & f.hasCar THEN e := ThisElem(f.text, f.carloc.pos); IF e # NIL THEN e.Check END END | m: UpdateMsg DO IF (Diff(f.text(Text).path, m.path) = 0) THEN t := f.text; FindElem(t, m.name, e, pos, d); t.notify := TextFrames.NotifyDisplay; CASE m.op OF | Directories.insert: IF d # 0 THEN COPY(m.path, path); Strings.Append(delimiter, path); Strings.Append(m.name, path); dir := Directories.This(path); Texts.WriteElem(w, NewElem(m.name, dir # NIL)); Texts.WriteString(w, m.name); Texts.WriteLn(w); Texts.Insert(t, pos, w.buf) END | Directories.delete: IF d = 0 THEN Texts.OpenReader(r, t, pos+1); Texts.ReadElem(r); IF r.eot THEN pos2 := t.len ELSE pos2 := Texts.Pos(r) - 1 END ; Texts.Delete(t, pos, pos2) END | Directories.change: END ; t.notify := Notify END ELSE END END ; IF ~ready THEN TextFrames.Handle(f, m) END END HandleFrame; PROCEDURE Open*; dir: Directories.Directory; (*<= " "; REPEAT mask[i] := ch; INC(i); In.Char(ch) UNTIL (ch <= " ") OR (ch > 7FX); mask[i] := 0X; ForAllElemsDo(select, TargetText(), NIL) END Select; PROCEDURE Deselect*; BEGIN ForAllElemsDo(unselect, TargetText(), NIL) END Deselect; BEGIN Init END Dir.