Syntax10.Scn.Fnt)IStampElemsAlloc24 Sep 97u ZParcElemsAlloc   +6ևԒҝШϳ;z8FoldElemsNew8C8C8%8i8/8C8.8C808)8L8v8*8n8W88S8868R88<8!888] 8b88U8~8U88V88?88P88M8o8]8828 848C8H88m88688>88.8Y8H88J88F808A88828888@kMODULE FMFrames; (* Christian Mayrhofer,  *) IMPORT Display, Input, MenuViewers, Oberon, Strings, TextFrames, Texts, Viewers, FMDevObj, FMElems; CONST Unit = TextFrames.Unit; CR = 0DX; Elem = 1CX; lightgray = 12; black = 15; MR = 0; MM =1; ML = 2; minTab = 4*TextFrames.mm; MaxTabs = TextFrames.MaxTabs; copy* = 0; move* = 1; TYPE Text* = POINTER TO TextDesc; TextDesc* = RECORD (Texts.TextDesc) devObj*: FMDevObj.Device END; CopyMoveMsg* = RECORD (Display.FrameMsg) root*: FMDevObj.Node; devObj*: FMDevObj.Device; id*: INTEGER; (* copy, move *) END; EndMarker* = POINTER TO EndMarkerDesc; EndMarkerDesc* = RECORD (Texts.ElemDesc) desc: INTEGER END; VAR border: Display.Pattern; filler: Display.Pattern; iconW, iconH: INTEGER; lockNeutralize: BOOLEAN; PROCEDURE ^ GetEnclosingFolder* (t: Text; pos: LONGINT): FMElems.FolderElem; PROCEDURE NoNotify (t: Texts.Text; op: INTEGER; beg, end: LONGINT); BEGIN END NoNotify;  PROCEDURE EndMarkerHandle(e: Texts.Elem; VAR msg: Texts.ElemMsg); VAR clone: EndMarker; folder: FMElems.FolderElem; text: Texts.Text; BEGIN WITH e: EndMarker DO WITH msg: Texts.CopyMsg DO IF msg.e = NIL THEN NEW(clone); msg.e := clone END; Texts.CopyElem(e, msg.e); msg.e(EndMarker).desc := e.desc | msg: TextFrames.DisplayMsg DO IF msg.prepare THEN e.desc := SHORT(-msg.Y0); e.W := LONG(iconW)*Unit; e.H := LONG(iconH)*Unit ELSE Display.CopyPattern(black, border, msg.X0, msg.Y0 + e.desc, Display.paint); Display.CopyPattern(lightgray, filler, msg.X0, msg.Y0 + e.desc, Display.paint) END | msg: TextFrames.TrackMsg DO IF msg.keys = {MM} THEN text := Texts.ElemBase(e); folder := GetEnclosingFolder(text(Text), Texts.ElemPos(e) -1); IF folder # NIL THEN folder.handle(folder, msg) END END ELSE END ELSE END END EndMarkerHandle;  PROCEDURE NewEndMarker(): EndMarker; VAR e: EndMarker; BEGIN NEW(e); e.desc := 0; e.handle := EndMarkerHandle; RETURN e END NewEndMarker;  PROCEDURE Max (len1, len2: LONGINT): LONGINT; BEGIN IF len1 > len2 THEN RETURN len1 END; RETURN len2 END Max;  PROCEDURE Min (len1, len2: LONGINT): LONGINT; BEGIN IF len1 < len2 THEN RETURN len1 END; RETURN len2 END Min;  PROCEDURE SkipOpenFolder (VAR r: Texts.Reader); VAR level: INTEGER; BEGIN level := 1; REPEAT Texts.ReadElem(r); IF r.elem # NIL THEN IF (r.elem IS FMElems.FolderElem) & r.elem(FMElems.FolderElem).open THEN INC(level) ELSIF (r.elem IS EndMarker) THEN DEC(level) END END UNTIL (level = 0) OR (r.elem = NIL) END SkipOpenFolder;  PROCEDURE GetEnclosingFolder* (t: Text; pos: LONGINT): FMElems.FolderElem; VAR r: Texts.Reader; level: INTEGER; BEGIN level := 1; Texts.OpenReader(r, t, pos); REPEAT Texts.ReadPrevElem(r); IF r.elem = NIL THEN RETURN NIL END; IF (r.elem IS FMElems.FolderElem) & r.elem(FMElems.FolderElem).open THEN DEC(level) ELSIF r.elem IS EndMarker THEN INC(level) END UNTIL level = 0; RETURN r.elem(FMElems.FolderElem) END GetEnclosingFolder;  PROCEDURE AdjustParcs (t: Text): BOOLEAN; VAR r: Texts.Reader; min, nOfTabs: INTEGER; maxWidths, widths: ARRAY MaxTabs OF LONGINT; folder: FMElems.FolderElem; i: INTEGER; modified: BOOLEAN; BEGIN modified := FALSE; Texts.OpenReader(r, t, 0); REPEAT Texts.ReadElem(r) UNTIL (r.elem = NIL) OR (r.elem IS FMElems.FolderElem); IF r.elem = NIL THEN RETURN FALSE END; folder := r.elem(FMElems.FolderElem); folder.GetAttributeWidths(widths, folder.attributes); maxWidths[0] := r.elem.W + minTab; nOfTabs := SHORT(Min(folder.attributes.nOfAttributes, MaxTabs - 2)); FOR i := 1 TO nOfTabs DO maxWidths[i] := widths[i-1] + minTab END; INC(nOfTabs); REPEAT IF (r.elem IS FMElems.FolderElem) & r.elem(FMElems.FolderElem).open THEN folder := r.elem(FMElems.FolderElem); REPEAT Texts.ReadElem(r) UNTIL r.elem IS TextFrames.Parc; maxWidths[0] := Max(maxWidths[0], folder.minTabWidths[0] + r.elem(TextFrames.Parc).left); min := SHORT(Min(nOfTabs, folder.nOfTabs)); FOR i := 1 TO min - 1 DO maxWidths[i] := Max(maxWidths[i], folder.minTabWidths[i]) END; FOR i := min TO folder.nOfTabs - 1 DO maxWidths[i] := folder.minTabWidths[i] END; nOfTabs := SHORT(Max(nOfTabs, folder.nOfTabs)) END; Texts.ReadElem(r) UNTIL r.elem = NIL; Texts.OpenReader(r, t, 0); Texts.ReadElem(r); modified := r.elem(TextFrames.Parc).tab[0] # maxWidths[0]; i := 1; WHILE (i < nOfTabs) & ~modified DO modified := r.elem(TextFrames.Parc).tab[i] - r.elem(TextFrames.Parc).tab[i-1] # maxWidths[i]; INC(i) END; IF modified THEN WHILE r.elem # NIL DO IF r.elem IS TextFrames.Parc THEN r.elem(TextFrames.Parc).nofTabs:= nOfTabs; r.elem(TextFrames.Parc).tab[0] := maxWidths[0] - r.elem(TextFrames.Parc).left; FOR i := 1 TO nOfTabs - 1 DO r.elem(TextFrames.Parc).tab[i] := r.elem(TextFrames.Parc).tab[i-1] + maxWidths[i] END END; Texts.ReadElem(r) END END; RETURN modified END AdjustParcs;  PROCEDURE ComputeMinTabWidths (t: Text; folder: FMElems.FolderElem; subDirs: BOOLEAN); VAR r: Texts.Reader; minTabWidths, widths: ARRAY MaxTabs OF LONGINT; i: INTEGER; BEGIN FOR i := 0 TO MaxTabs - 1 DO minTabWidths[i] := 0 END; Texts.OpenReader(r, t, Texts.ElemPos(folder) + 1); Texts.ReadElem(r); folder.nOfTabs := SHORT(Min(MaxTabs, folder.attributes.nOfAttributes + 1)); WHILE (r.elem # NIL) & ~(r.elem IS EndMarker) DO IF r.elem IS FMElems.Elem THEN r.elem(FMElems.Elem).GetAttributeWidths(widths, folder.attributes); minTabWidths[0] := Max(minTabWidths[0], r.elem.W + minTab); FOR i := 1 TO folder.nOfTabs - 1 DO minTabWidths[i] := Max(minTabWidths[i], widths[i-1] + minTab) END; IF (r.elem IS FMElems.FolderElem) & r.elem(FMElems.FolderElem).open THEN IF subDirs THEN ComputeMinTabWidths(t, r.elem(FMElems.FolderElem), subDirs) END; SkipOpenFolder(r) END END; Texts.ReadElem(r) END; NEW(folder.minTabWidths, folder.nOfTabs); FOR i := 0 TO folder.nOfTabs -1 DO folder.minTabWidths[i] := minTabWidths[i] END END ComputeMinTabWidths;  PROCEDURE GenerateText (t: Text; folder: FMElems.FolderElem; VAR w: Texts.Writer); VAR r: Texts.Reader; elem: FMElems.Elem; copyMsg: Texts.CopyMsg; parc: TextFrames.Parc; i: LONGINT; list: FMDevObj.List; iterator: FMDevObj.Iterator; BEGIN list := t.devObj.NewSortList(folder); t.devObj.Enumerate(folder, list, folder.pattern); list.SortList; Texts.OpenReader(r, t, Texts.ElemPos(folder)); REPEAT Texts.ReadPrevElem(r) UNTIL r.elem IS TextFrames.Parc; r.elem.handle(r.elem, copyMsg); parc := copyMsg.e(TextFrames.Parc); Texts.WriteElem(w, parc); iterator := FMDevObj.NewIterator(list); WHILE iterator.NextElem(elem) DO Texts.WriteElem(w, elem); elem.WriteAttributes(w, folder.attributes); Texts.WriteLn(w); END; INC(parc.left, folder.indent); FOR i := 0 TO parc.nofTabs - 1 DO DEC(parc.tab[i], folder.indent) END; parc := r.elem(TextFrames.Parc); parc.handle(parc, copyMsg); Texts.WriteElem(w, copyMsg.e); Texts.WriteElem(w, NewEndMarker()) END GenerateText;  PROCEDURE GetSelectedElems* (t: Text): FMDevObj.Node; VAR r: Texts.Reader; root: FMDevObj.Node; PROCEDURE Search (): FMDevObj.Node; VAR root, curr, node: FMDevObj.Node; elem: Texts.Elem; BEGIN root := NIL; Texts.ReadElem(r); elem := r.elem; WHILE (elem # NIL) & ~(elem IS EndMarker) DO IF ((elem IS FMElems.Elem) & elem(FMElems.Elem).selected) OR ((elem IS FMElems.FolderElem) & elem(FMElems.FolderElem).open) THEN NEW(node); node.next := NIL; node.desc := NIL; node.elem := elem(FMElems.Elem); IF (elem IS FMElems.FolderElem) & elem(FMElems.FolderElem).open THEN node.desc := Search() END; IF (node.desc # NIL) OR node.elem.selected THEN IF root = NIL THEN curr := node; root := node ELSE curr.next := node; curr := node END END END; Texts.ReadElem(r); elem := r.elem END; RETURN root END Search;  PROCEDURE SearchRoot (root: FMDevObj.Node): FMDevObj.Node; VAR curr: FMDevObj.Node; e: FMElems.Elem; BEGIN IF root.elem.selected THEN RETURN root END; curr := root.desc; e := curr.elem; IF (curr.next = NIL) & (e IS FMElems.FolderElem) & e(FMElems.FolderElem).open THEN RETURN SearchRoot(curr) END; RETURN root END SearchRoot;  BEGIN Texts.OpenReader(r, t, 0); root := Search(); IF root # NIL THEN root := SearchRoot(root) END; RETURN root END GetSelectedElems;  PROCEDURE SearchParentRemoveElem (t: Text; path, name: ARRAY OF CHAR; srcDev: FMDevObj.Device; VAR pos: LONGINT; VAR parent: FMElems.FolderElem); VAR c, dC, sC, dD, sD: CHAR; r: Texts.Reader; i, j: INTEGER; destPath: FMDevObj.Path; str: FMElems.Name; done: BOOLEAN; BEGIN done := FALSE; parent := NIL; Texts.OpenReader(r, t, 0); REPEAT Texts.ReadElem(r) UNTIL (r.elem = NIL) OR (r.elem IS FMElems.FolderElem); IF r.elem = NIL THEN RETURN END; parent := r.elem(FMElems.FolderElem); t.devObj.GetPath(parent, destPath); dD := t.devObj.delimiter; sD := srcDev.delimiter; j := 0; dC := destPath[j]; sC := path[j]; WHILE (dC # 0X) & (sC # 0X) & (dC = sC) DO INC(j); dC := destPath[j]; sC := path[j] END; IF dC = dD THEN i := j + 1; dC := destPath[i] ELSE i := j END; IF sC = sD THEN INC(j); sC := path[j] END; IF (dC = 0X) & (sC = 0X) THEN Texts.ReadElem(r) (* parent found *) ELSIF (dC = 0X) & (j > 0) & (path[j-1] = sD) THEN (* destPath contains path *) i := 0; WHILE (path[j] # 0X) & (path[j] # sD) DO str[i] := path[j]; INC(i); INC(j) END; str[i] := 0X; Texts.ReadElem(r); LOOP IF r.elem = NIL THEN parent := NIL; EXIT END; IF r.elem IS FMElems.FolderElem THEN parent := r.elem(FMElems.FolderElem); IF t.devObj.CheckIdentity(parent, str) THEN IF path[j] = sD THEN INC(j) END; (* skip path delimiter *) IF path[j] = 0X THEN (* parent found *) Texts.ReadElem(r); EXIT END; IF ~parent.open THEN parent := NIL; EXIT END; (* parent can not be found *) i := 0; WHILE (path[j] # 0X) & (path[j] # sD) DO str[i] := path[j]; INC(i); INC(j) END; str[i] := 0X ELSIF parent.open THEN SkipOpenFolder(r) END END; Texts.ReadElem(r) END ELSIF(sC = 0X) & (i > 0) & (destPath[i-1] = dD) THEN j := i; i := 0; WHILE destPath[j] # 0X DO IF destPath[j] = dD THEN INC(i) END; INC(j) END; IF (i = 0) OR ((i = 1) & (destPath[j-1] = dD)) THEN IF t.devObj.CheckIdentity(parent, name) THEN (* first folder is elem *) parent := NIL; done := TRUE END END ELSE parent := NIL END; IF done OR ((parent # NIL) & parent.open) THEN WHILE (r.elem # NIL) & ~(r.elem IS EndMarker) DO IF r.elem IS FMElems.Elem THEN IF t.devObj.CheckIdentity(r.elem(FMElems.Elem), name) THEN pos := Texts.ElemPos(r.elem); IF (r.elem IS FMElems.FolderElem) & r.elem(FMElems.FolderElem).open THEN SkipOpenFolder(r) END; REPEAT Texts.Read(r, c) UNTIL r.eot OR (c = CR); Texts.Delete(t, pos, Texts.Pos(r)); IF parent # NIL THEN ComputeMinTabWidths(t, parent, FALSE) END; RETURN ELSIF (r.elem IS FMElems.FolderElem) & r.elem(FMElems.FolderElem).open THEN SkipOpenFolder(r) END END; Texts.ReadElem(r) END END END SearchParentRemoveElem;  PROCEDURE Insert (t: Text; path, name: ARRAY OF CHAR; srcDev: FMDevObj.Device; VAR beg: LONGINT); VAR r: Texts.Reader; w: Texts.Writer; list: FMDevObj.List; iterator: FMDevObj.Iterator; parent: FMElems.FolderElem; elem: FMElems.Elem; pos: LONGINT; done: BOOLEAN; BEGIN SearchParentRemoveElem(t, path, name, srcDev, beg, parent); IF (parent = NIL) OR ~parent.open THEN RETURN END; list := t.devObj.NewSortList(parent); elem := t.devObj.GetElem(parent, name); IF elem = NIL THEN RETURN END; list.Insert(elem); Texts.OpenReader(r, t, Texts.ElemPos(parent) +1); Texts.ReadElem(r); WHILE (r.elem # NIL) & ~(r.elem IS EndMarker) DO IF r.elem IS FMElems.Elem THEN list.Insert(r.elem(FMElems.Elem)); IF (r.elem IS FMElems.FolderElem) & r.elem(FMElems.FolderElem).open THEN SkipOpenFolder(r) END END; Texts.ReadElem(r) END; list.SortList(); iterator := FMDevObj.NewIterator(list); done := iterator.NextElem(elem); Texts.OpenReader(r, t, Texts.ElemPos(parent) + 1); Texts.ReadElem(r); WHILE ~(r.elem IS EndMarker) & done DO IF r.elem IS FMElems.Elem THEN IF r.elem # elem THEN done := FALSE; pos := Texts.ElemPos(r.elem) ELSE IF (r.elem IS FMElems.FolderElem) & r.elem(FMElems.FolderElem).open THEN SkipOpenFolder(r) END; done := iterator.NextElem(elem) END ELSIF r.elem IS TextFrames.Parc THEN pos := Texts.ElemPos(r.elem) END; Texts.ReadElem(r) END; Texts.OpenWriter(w); Texts.WriteElem(w, elem); elem.WriteAttributes(w ,parent.attributes);Texts.WriteLn(w); Texts.Insert(t, pos, w.buf); IF pos < beg THEN beg := pos END; ComputeMinTabWidths(t, parent, FALSE); END Insert;  PROCEDURE InsertElem* (t: Text; path, name: ARRAY OF CHAR; srcDev: FMDevObj.Device); VAR oldNotifier: Texts.Notifier; updateBeg: LONGINT; BEGIN updateBeg := MAX(LONGINT); oldNotifier := t.notify; t.notify := NoNotify; Insert(t, path, name, srcDev, updateBeg); IF updateBeg < MAX(LONGINT) THEN IF AdjustParcs(t) THEN updateBeg := 0 END; t.notify := oldNotifier; t.notify(t, Texts.replace, updateBeg, t.len) END; t.notify := oldNotifier END InsertElem;  PROCEDURE RemoveElem* (t: Text; path, name: ARRAY OF CHAR; srcDev: FMDevObj.Device); VAR oldNotifier: Texts.Notifier; updateBeg: LONGINT; parent: FMElems.FolderElem; BEGIN updateBeg := MAX(LONGINT); oldNotifier := t.notify; t.notify := NoNotify; SearchParentRemoveElem(t, path, name, srcDev, updateBeg, parent); IF updateBeg < MAX(LONGINT) THEN IF AdjustParcs(t) THEN updateBeg := 0 END; t.notify := oldNotifier; t.notify(t, Texts.replace, updateBeg, t.len) END; t.notify := oldNotifier END RemoveElem;  PROCEDURE SelectFiles* (t: Text; parent: FMElems.FolderElem; pattern: ARRAY OF CHAR); VAR level: INTEGER; r: Texts.Reader; elem: Texts.Elem; BEGIN Texts.OpenReader(r, t, Texts.ElemPos(parent) + 1); Texts.ReadElem(r); elem := r.elem; level := 1; WHILE (elem # NIL) & (level > 0) DO WITH elem: EndMarker DO DEC(level) | elem: FMElems.FolderElem DO IF elem.open THEN INC(level) END | elem: FMElems.Elem DO IF ~elem.selected & Strings.Match(elem.name, pattern) THEN elem.ToggleSelection() END ELSE END; Texts.ReadElem(r); elem := r.elem END END SelectFiles;  PROCEDURE SwitchFolder* (t: Text; folder: FMElems.FolderElem); VAR r: Texts.Reader; w: Texts.Writer; oldNotifier: Texts.Notifier; lineBeg: LONGINT; ch: CHAR; BEGIN folder.open := ~folder.open; oldNotifier := t.notify; t.notify := NoNotify; Texts.OpenWriter(w); lineBeg := Texts.ElemPos(folder); Texts.OpenReader(r, t, lineBeg + 1); IF folder.open THEN (* open folder *) REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch = CR); Texts.Delete(t, lineBeg + 1, Texts.Pos(r) - 1); GenerateText(t, folder, w); Texts.Insert(t, lineBeg + 1, w.buf); ComputeMinTabWidths(t, folder, FALSE); ELSE SkipOpenFolder(r); (* R.elem is EndMarker *) Texts.Delete(t, lineBeg + 1, Texts.Pos(r)); folder.WriteAttributes(w, folder.attributes); Texts.Insert(t, lineBeg + 1, w.buf); folder.minTabWidths := NIL; folder.nOfTabs := 0 END; IF AdjustParcs(t) THEN lineBeg := 0 END; t.notify := oldNotifier; t.notify(t, Texts.replace, lineBeg, t.len) END SwitchFolder;  PROCEDURE NewText* (devObj: FMDevObj.Device; folder: FMElems.FolderElem): Text; VAR w: Texts.Writer; text: Text; copyMsg: Texts.CopyMsg; widths: ARRAY MaxTabs OF LONGINT; nOfTabs: INTEGER; i: INTEGER; BEGIN NEW(text); Texts.Open(text, ""); text.notify := TextFrames.NotifyDisplay; text.devObj := devObj; TextFrames.defParc.handle(TextFrames.defParc, copyMsg); copyMsg.e(TextFrames.Parc).left := 0; copyMsg.e(TextFrames.Parc).nofTabs := 0; Texts.OpenWriter(w); Texts.WriteElem(w, copyMsg.e); Texts.WriteElem(w, folder); folder.GetAttributeWidths(widths, folder.attributes); nOfTabs := SHORT(Min(folder.attributes.nOfAttributes + 1, MaxTabs)); copyMsg.e(TextFrames.Parc).nofTabs := nOfTabs; copyMsg.e(TextFrames.Parc).tab[0] := folder.W + minTab; FOR i := 1 TO nOfTabs - 1 DO copyMsg.e(TextFrames.Parc).tab[i] := copyMsg.e(TextFrames.Parc).tab[i-1] + widths[i-1] + minTab END; Texts.Append(text, w.buf); SwitchFolder(text, folder); RETURN text END NewText;  PROCEDURE CreateSelectionText (f: TextFrames.Frame; VAR text: Texts.Text); VAR root: FMDevObj.Node; w: Texts.Writer; PROCEDURE WriteFiles(VAR w: Texts.Writer; root: FMDevObj.Node); VAR curr: FMDevObj.Node; path: FMDevObj.Path; BEGIN f.text(Text).devObj.GetPath(root.elem(FMElems.FolderElem), path); IF root.elem.selected THEN Texts.Write(w, '"'); Texts.WriteString(w, path); Texts.Write(w, '"'); Texts.Write(w, CR) END; f.text(Text).devObj.Append(path, ""); curr := root.desc; WHILE curr # NIL DO IF curr.elem IS FMElems.FolderElem THEN WriteFiles(w, curr) ELSE Texts.Write(w, '"'); Texts.WriteString(w, path); Texts.WriteString(w, curr.elem.name); Texts.Write(w, '"'); Texts.Write(w, CR) END; curr := curr.next END END WriteFiles;  BEGIN root := GetSelectedElems(f.text(Text)); IF root # NIL THEN Texts.OpenWriter(w); WriteFiles(w, root); text := TextFrames.Text(""); Texts.Append(text, w.buf) END END CreateSelectionText;  PROCEDURE RemoveSelection* (f: TextFrames.Frame); VAR r: Texts.Reader; BEGIN f.hasSel := FALSE; Texts.OpenReader(r, f.text, 0); Texts.ReadElem(r); WHILE (r.elem # NIL) DO IF (r.elem IS FMElems.Elem) & r.elem(FMElems.Elem).selected THEN r.elem(FMElems.Elem).ToggleSelection() END; Texts.ReadElem(r) END END RemoveSelection;  PROCEDURE OpenSelectedElems* (f: TextFrames.Frame); VAR root: FMDevObj.Node; PROCEDURE Open (root: FMDevObj.Node); VAR curr: FMDevObj.Node; BEGIN curr := root.desc; WHILE curr # NIL DO IF curr.elem.selected THEN f.text(Text).devObj.Open(root.elem(FMElems.FolderElem), curr.elem) END; IF (curr.elem IS FMElems.FolderElem) & curr.elem(FMElems.FolderElem).open THEN Open(curr) END; curr := curr.next END END Open;  BEGIN root := GetSelectedElems(f.text(Text)); IF root # NIL THEN Open (root); RemoveSelection(f) END END OpenSelectedElems;  PROCEDURE CopyMoveFiles* (f: TextFrames.Frame; root: FMDevObj.Node; srcDev: FMDevObj.Device; copy: BOOLEAN); VAR folder: FMElems.FolderElem; BEGIN IF ~f.hasCar THEN RETURN END; folder := GetEnclosingFolder(f.text(Text), f.carloc.pos); f.text(Text).devObj.CopyMoveFiles(folder, srcDev, root, copy); END CopyMoveFiles;  PROCEDURE DeleteSelectedFiles* (f: TextFrames.Frame); VAR root: FMDevObj.Node; BEGIN root := GetSelectedElems(f.text(Text)); IF root # NIL THEN f.text(Text).devObj.DeleteFiles(root) END END DeleteSelectedFiles;  PROCEDURE NewDir* (f: TextFrames.Frame; name: ARRAY OF CHAR); VAR folder: FMElems.FolderElem; BEGIN IF ~f.hasCar THEN RETURN END; folder := GetEnclosingFolder(f.text(Text), f.carloc.pos); IF folder # NIL THEN f.text(Text).devObj.NewDirectory(folder, name) END END NewDir;  PROCEDURE RemoveFocus* (f: TextFrames.Frame); VAR frame: FMElems.EditFrame; folder: FMElems.FolderElem; newName: FMElems.Name; BEGIN IF f.focus = NIL THEN RETURN END; frame := f.focus(FMElems.EditFrame); f.focus := NIL; IF frame.elem.name # frame.oldName THEN folder := GetEnclosingFolder(f.text(Text), Texts.ElemPos(frame.elem)); COPY(frame.elem.name, newName); COPY(frame.oldName, frame.elem.name); IF folder # NIL THEN f.text(Text).devObj.Rename(folder, frame.elem, newName); IF f.text(Text).devObj.done THEN frame.elem.Defocus(newName); RETURN END END END; frame.elem.Defocus(frame.oldName) END RemoveFocus;  PROCEDURE TrackCaret (f: TextFrames.Frame; VAR msg: Oberon.InputMsg); VAR loc: TextFrames.Location; r: Texts.Reader; focus: Display.Frame; BEGIN focus := f.focus; IF (focus # NIL) & (focus.X <= msg.X) & (msg.X < focus.X + focus.W) & (focus.Y <= msg.Y) & (msg.Y < focus.Y + focus.H) THEN focus.handle(focus, msg) ELSE Oberon.PassFocus(Viewers.This(f.X, f.Y)); TextFrames.LocateChar(f, msg.X, msg.Y, loc); Texts.OpenReader(r, f.text, loc.pos); Texts.ReadElem(r); IF (r.elem # NIL) & (Texts.ElemPos(r.elem) = loc.pos) & (r.elem IS FMElems.Elem) & r.elem(FMElems.Elem).selected & (loc.x < msg.X) THEN RemoveSelection(f); f.focus := r.elem(FMElems.Elem).Focus(loc.x, loc.y); f.focus.handle(f.focus, msg) ELSE TextFrames.TrackCaret(f, msg.X, msg.Y, msg.keys) END END END TrackCaret;  PROCEDURE TrackSelection (f: TextFrames.Frame; VAR msg: Oberon.InputMsg); VAR r: Texts.Reader; a, b, beg, end, lineBeg, lineEnd, lastSel: LONGINT; keys: SET; loc: TextFrames.Location; hasCar: BOOLEAN; carPos: LONGINT; ch: CHAR; i: INTEGER; BEGIN RemoveFocus(f); hasCar := f.hasCar; carPos := f.carloc.pos; IF hasCar THEN TextFrames.RemoveCaret(f) END; TextFrames.LocateChar(f, msg.X, msg.Y, loc); Texts.OpenReader(r, f.text, loc.org); REPEAT Texts.Read(r, ch) UNTIL r.eot OR (ch = Elem) OR (ch = CR); lineBeg := loc.org; lineEnd := Texts.Pos(r) - 1; IF (r.elem # NIL) & (Texts.ElemPos(r.elem) <= lineEnd) & (r.elem IS FMElems.Elem) THEN r.elem(FMElems.Elem).ToggleSelection(); END; lastSel := lineBeg; REPEAT a := -1; beg := -1; IF loc.pos <= lineBeg THEN IF lastSel < lineBeg THEN IF lastSel < loc.org THEN a := lastSel; b := loc.org - 1; lastSel := loc.org ELSIF lastSel > loc.org THEN beg := loc.org; end := lastSel - 1; lastSel := beg END ELSE IF lastSel > lineBeg THEN a := lineEnd + 1; b := lastSel END; beg := loc.org; end := lineBeg - 1; lastSel := beg END ELSIF loc.pos >= lineEnd THEN IF lastSel > lineEnd THEN IF lastSel > loc.pos THEN a := loc.pos + 1; b := lastSel; lastSel := loc.pos ELSIF lastSel < loc.pos THEN beg := lastSel + 1; end := loc.pos; lastSel := end END ELSE IF lastSel < lineBeg THEN a := lastSel; b := lineBeg - 1 END; beg := lineEnd + 1; end := loc.pos; lastSel := end END END; FOR i := 1 TO 2 DO IF a # -1 THEN Texts.OpenReader(r, f.text, a); Texts.ReadElem(r); WHILE (r.elem # NIL) & (Texts.ElemPos(r.elem) <= b) DO IF r.elem IS FMElems.Elem THEN r.elem(FMElems.Elem).ToggleSelection(); END; Texts.ReadElem(r) END END; a := beg; b := end END; Input.Mouse(keys, msg.X, msg.Y); Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, msg.X, msg.Y); msg.keys := msg.keys + keys; TextFrames.LocateChar(f, msg.X, msg.Y, loc) UNTIL keys = {}; f.time := Oberon.Time(); Texts.OpenReader(r, f.text, 0); f.hasSel := FALSE; REPEAT Texts.ReadElem(r); f.hasSel := (r.elem # NIL) & (r.elem IS FMElems.Elem) & r.elem(FMElems.Elem).selected UNTIL f.hasSel OR (r.elem = NIL); IF hasCar THEN TextFrames.SetCaret(f, carPos) END END TrackSelection;  PROCEDURE TrackMouse (f: TextFrames.Frame; VAR msg: Oberon.InputMsg); VAR copyMsg: CopyMoveMsg; keys: SET; loc: TextFrames.Location; r: Texts.Reader; folder: FMElems.FolderElem; BEGIN IF msg.keys = {MR} THEN TrackSelection(f, msg); IF msg.keys = {ML, MR} THEN (* delete selected files and directories *) DeleteSelectedFiles(f) ELSIF msg.keys = {MM, MR} THEN (* copy selected files and directories to caret position *) copyMsg.root := GetSelectedElems(f.text(Text)); copyMsg.devObj := f.text(Text).devObj; copyMsg.id := copy; IF copyMsg.root # NIL THEN Viewers.Broadcast(copyMsg) END END ELSIF msg.keys = {ML} THEN TrackCaret(f, msg); ELSIF msg.keys = {MM} THEN TextFrames.LocateChar(f, msg.X, msg.Y, loc); Texts.OpenReader(r, f.text, loc.pos); Texts.ReadElem(r); IF (r.elem # NIL) & (Texts.ElemPos(r.elem) = loc.pos) & (loc.x < msg.X) THEN IF (r.elem IS FMElems.Elem) & r.elem(FMElems.Elem).selected THEN REPEAT Input.Mouse(keys, msg.X, msg.Y); Oberon.DrawCursor(Oberon.Mouse, Oberon.Arrow, msg.X, msg.Y); msg.keys := msg.keys + keys UNTIL (msg.keys = {ML, MM}) OR (keys = {}); IF msg.keys = {ML, MM} THEN copyMsg.id := move; copyMsg.devObj := f.text(Text).devObj; copyMsg.root := GetSelectedElems(f.text(Text)); IF copyMsg.root # NIL THEN Viewers.Broadcast(copyMsg) END ELSIF msg.keys = {MM} THEN folder := GetEnclosingFolder(f.text(Text), Texts.ElemPos(r.elem)); IF folder # NIL THEN f.text(Text).devObj.Open(folder, r.elem(FMElems.Elem)) END; RemoveSelection(f) END ELSE TextFrames.Handle(f, msg) END END END END TrackMouse;  PROCEDURE Handle* (f: Display.Frame; VAR msg: Display.FrameMsg); VAR pos: LONGINT; focus: Display.Frame; BEGIN WITH f: TextFrames.Frame DO WITH msg: Oberon.InputMsg DO IF msg.id = Oberon.track THEN IF (msg.keys # {}) & (msg.X < f.X + f.barW) THEN lockNeutralize := TRUE; RemoveFocus(f); TextFrames.Handle(f, msg); lockNeutralize := FALSE ELSE TrackMouse(f, msg) END ELSIF f.focus # NIL THEN f.focus.handle(f.focus, msg) END | msg: CopyMoveMsg DO CopyMoveFiles(f, msg.root, msg.devObj, msg.id = copy) | msg: FMDevObj.NotifyMsg DO IF msg.id = FMDevObj.insert THEN InsertElem(f.text(Text), msg.path, msg.name, msg.devObj) ELSE RemoveElem(f.text(Text), msg.path, msg.name, msg.devObj) END | msg: FMElems.NotifyMsg DO IF (msg.elem # NIL) & (Texts.ElemBase(msg.elem) = f.text) THEN IF msg.id = FMElems.draw THEN pos := Texts.ElemPos(msg.elem); lockNeutralize := TRUE; focus := f.focus; f.focus := NIL; f.text.notify(f.text, Texts.replace, pos, pos + 1); IF (focus # NIL) & (focus(FMElems.EditFrame).elem = msg.elem) THEN msg.elem.ResizeFrame(focus(FMElems.EditFrame)); f.focus := focus; msg.elem := NIL END; lockNeutralize := FALSE ELSIF msg.id = FMElems.switchFolder THEN RemoveFocus(f); RemoveSelection(f); SwitchFolder(f.text(Text), msg.elem(FMElems.FolderElem)); msg.elem := NIL END END | msg: Oberon.ControlMsg DO Oberon.RemoveMarks(f.X, f.Y, f.W, f.H); TextFrames.RemoveCaret(f); IF (~lockNeutralize & (msg.id = Oberon.neutralize)) OR (msg.id = Oberon.defocus) THEN RemoveFocus(f); IF msg.id = Oberon.neutralize THEN RemoveSelection(f) END END | msg: Oberon.CopyMsg DO TextFrames.Handle(f, msg) | msg: Oberon.SelectionMsg DO IF f.hasSel & (f.time > msg.time) THEN CreateSelectionText(f, msg.text); msg.time := f.time; msg.beg := 0; msg.end := msg.text.len END; | msg: MenuViewers.ModifyMsg DO lockNeutralize :=TRUE; RemoveFocus(f); TextFrames.Handle(f, msg); lockNeutralize := FALSE | msg: TextFrames.UpdateMsg DO TextFrames.Handle(f, msg) ELSE END ELSE END END Handle;  PROCEDURE NewFrame* (t: Text): TextFrames.Frame; VAR frame: TextFrames.Frame; BEGIN frame := TextFrames.NewText(t, 0); frame.handle := Handle; frame.focus := NIL; RETURN frame END NewFrame;  PROCEDURE InitPatterns(); VAR pat: ARRAY 10 OF SET; BEGIN pat[0] := {}; pat[1] := {6}; pat[2] := {5,6}; pat[3] := {4,6}; pat[4] := {3,6}; pat[5] := {2,6}; pat[6] := {3, 6}; pat[7] := {4,6}; pat[8] := {5,6}; pat[9] := {6}; border := Display.NewPattern (pat, 8, 9); pat[0] := {}; pat[1] := {}; pat[2] := {}; pat[3] := {5}; pat[4] := {4,5}; pat[5] := {3..5}; pat[6] := {4,5}; pat[7] := {7}; pat[8] := {}; pat[9] := {}; filler := Display.NewPattern (pat, 8, 9); iconW := 8; iconH := 9 END InitPatterns;  BEGIN InitPatterns(); lockNeutralize := FALSE; END FMFrames.