(*
  .name       Edt/M2Edt
  .task       ascii text editor, main module
  .release    1.0
  .language   Oberon-2
  .translator Amiga Oberon 3.11
  .system     AmigaOS 2.04/2.1/3.0
  .author     Joachim Barheine
  .address    Hochgrevestraße 3, D-38640 Goslar
  .copyright  (c) 1994 by Joachim Barheine
*)

(* .info: 09/04/95, 20:49:25, version 179 *)

MODULE M2Edt;   (* main *)

IMPORT
(* $IF M2Amiga THEN *)
  RxModula2,
(* $END *)

(* $IFNOT Demo THEN *)
  NoGuruRq,
(* $END *)

  SYS:= SYSTEM,
  OberonLib,

  ASCII,  (* ANSI-keys *)
  Rx:= ERexx,
  Clip:= EClip,
  CN:= CmdNodes,
  Dos,
  Exec,
  EArgs,
  F:= Files,
  GUI,
  I:= Intuition,
  Icon:= EIcon,
  IO:= IOServer,
  K:= Kernel,
  Key:= Keymapping,
  L:= UntracedLists,
  M:= Macro,
  Mn:= Menus,
  P:= EPrint,
  Prj:= EProject,
  Req:= ERequests,
  RxARexx,
  RxConfig,
  RxEdit,
  RxFolds,
  RxPosition,
  RxRequests,
  RxSelection,
  RxTexts,
  RxVars,
  RxWindows,
  S:= Strings,
  Sett:= Settings,
  Str:= StrPool,
  T0:= Texts,
  T:= ETexts,
  Util:= Utility,
  W:= Windows,
  Workbench;

TYPE
  Window = UNTRACED POINTER TO WindowDesc;
  WindowDesc = RECORD (Prj.WindowDesc)
    string: ARRAY 512 OF CHAR;
    number, line: LONGINT;
    rexxCmd: ARRAY 512 OF CHAR;
    rexxPrg: ARRAY F.filenameLen OF CHAR;
  END;

  Text = Prj.Text;
  TextDesc = Prj.TextDesc;

VAR
  w: Window;
  msg: SHORTINT;
  except: BOOLEAN;  (* exception situation *)

(* $IF Demo THEN *)
  demo1, demo2: INTEGER;

PROCEDURE DemoMsg;

BEGIN
  Req.ReqMessage(NIL, "Dieses ist eine eingeschränkte|Demonstrationsversion des ASCII-Editors|(M2)Edt von Joachim Barheine.||Lauffähig bis zum 1.6.95!", "Weiter");
END DemoMsg;
(* $END *)

(* -- window methods -- *)

PROCEDURE (w: Window) New*;

BEGIN
  w.New^;
  w.string:= "";
  w.number:= 0;
  w.line:= 0;
  w.rexxCmd:= "";
  w.rexxPrg:= "";
END New;

PROCEDURE (w: Window) NewText* (t: IO.Text);

BEGIN
  w.NewText^(t);
  IO.SetItemVal(t, Mn.iconsItem, t.icons);
END NewText;

PROCEDURE (w: Window) Open* (t: IO.Text): BOOLEAN;

VAR
  done: BOOLEAN;

BEGIN
  done:= w.Open^(t);
  IF done THEN IO.SetItemVal(t, Mn.iconsItem, t.icons) END;
  RETURN done;
END Open;

(* -- create a new window -- *)

(* open a new window *)
PROCEDURE CreateWindow(t: IO.Text): Window;

VAR
  new: Window;

BEGIN
  NEW(new); new.New;
  IF ~new.Open(t) THEN
    new.Dispose; DISPOSE(new); new:= NIL;
    Req.ReqMessage(NIL, Str.cannotOpenWindow^, Str.cancel^);
  END;
  RETURN new;
END CreateWindow;

(* -- open a new text -- *)

PROCEDURE OpenFile(filename: ARRAY OF CHAR): Text;

VAR
  new: Text;

(* $CopyArrays- *)

BEGIN
  NEW(new); new.New;
  IF ~new.Open(filename) THEN
    new.Dispose; DISPOSE(new); new:= NIL;
  END;
  RETURN new;
END OpenFile;

PROCEDURE CreateText(): Text;

VAR
  new: Text;
  name: ARRAY F.nameLen OF CHAR;

BEGIN
  NEW(new); new.New;
  F.CreateName(name);
  new.Clear(name);
  RETURN new;
END CreateText;

(* -- startup -- *)

(* start up the editor *)
PROCEDURE Launch(): BOOLEAN;

VAR
  i, num: INTEGER;

  PROCEDURE OpenInitial(): BOOLEAN;

  BEGIN
    RETURN CreateWindow(CreateText()) # NIL;
  END OpenInitial;

  PROCEDURE OpenArg(i: INTEGER): BOOLEAN;

  VAR
    t: Text;

  BEGIN
    t:= OpenFile(EArgs.from[i]^);
    IF t # NIL THEN
      IF CreateWindow(t) # NIL THEN RETURN TRUE END;
      t.Dispose; DISPOSE(t);
    END;
    RETURN FALSE;
  END OpenArg;

BEGIN
  num:= 0;
  FOR i:= 0 TO EArgs.numArgs - 1 DO
    IF OpenArg(i) THEN INC(num) ELSE GUI.Flash END;
  END;
  RETURN (num > 0) OR OpenInitial();
END Launch;

(* -- Rexx -- *)

(* execute a command string (menus, command mode): return if executed in ARexx mode *)
PROCEDURE ExecRexx(w: W.Window; cmdStr: ARRAY OF CHAR; isFile: BOOLEAN): BOOLEAN;

  PROCEDURE QuickExec(): BOOLEAN;

  BEGIN
    WITH w: Window DO
      IF Util.Stricmp(cmdStr, Rx.upStr) = 0 THEN
        w.MoveDY(-1);
      ELSIF Util.Stricmp(cmdStr, Rx.downStr) = 0 THEN
        w.MoveDY(1);
      ELSIF Util.Stricmp(cmdStr, Rx.leftStr) = 0 THEN
        w.MoveDX(-1);
      ELSIF Util.Stricmp(cmdStr, Rx.rightStr) = 0 THEN
        w.MoveDX(1);
      ELSIF Util.Stricmp(cmdStr, Rx.moveR1Str) = 0 THEN
        w.SetPos(w.pos + 1);
      ELSIF Util.Stricmp(cmdStr, Rx.moveL1Str) = 0 THEN
        w.SetPos(w.pos - 1);
      ELSIF Util.Stricmp(cmdStr, Rx.deleteStr) = 0 THEN
        w.text(Text).Delete(w.pos, 1);
      ELSIF Util.Stricmp(cmdStr, Rx.delBackStr) = 0 THEN
        IF w.pos > 0 THEN
          w.SetPos(w.pos-1);
          w.text(Text).Delete(w.pos, 1);
        END;
      ELSIF Util.Stricmp(cmdStr, Rx.undoStr) = 0 THEN
        IF RxEdit.UndoQ(w) THEN END;
      ELSIF Util.Stricmp(cmdStr, Rx.redoStr) = 0 THEN
        IF RxEdit.RedoQ(w) THEN END;
      ELSE
        RETURN FALSE;
      END;
      RETURN TRUE;
    END;
  END QuickExec;

(* $CopyArrays- *)

BEGIN
  IF Sett.recording THEN M.AddCommand(cmdStr, isFile) END;
  RETURN (isFile OR ~QuickExec()) & Rx.Execute(cmdStr, isFile);
END ExecRexx;

(* -- input handling -- *)

(* $StackChk+ --> recursive *)

PROCEDURE* HandleRexx(w: W.Window): SHORTINT;  (* commands return code *)

VAR
  msg: SHORTINT;
  replied: BOOLEAN;                         (* msg. already replied? *)
  cmdID: INTEGER;                           (* command ID *)
  date: Dos.Date;

  PROCEDURE New(w: Prj.Window);

  VAR
    t: Text;

  BEGIN
    IF (w.text(Prj.Text).useCount = 1) & (w.text(Prj.Text).changes # 0) THEN
      CASE Prj.ReqSave(w) OF
        0: Rx.SetBoolResult(FALSE);  (* cancel *)
           RETURN;
      | 1: Prj.Save(w);   (* save *)
      | 2: Prj.SaveAs(w); (* save as *)
      | 3: ;              (* no *)
      END;
    END;
    t:= CreateText();
    w.NewText(t);
    Rx.SetBoolResult(TRUE);
  END New;

  PROCEDURE NewWindow(VAR w: W.Window);

  VAR
    w0: W.Window;

  BEGIN
    w0:= CreateWindow(w.text);
    IF w0 # NIL THEN w:= w0 END;
  END NewWindow;

  PROCEDURE OpenFile(w: Window);

  VAR
    new: Text;

  BEGIN
    NEW(new); new.New;
    IF ~RxTexts.Open(w, new) THEN new.Dispose; DISPOSE(new) END;
  END OpenFile;

  PROCEDURE RepeatRexx(w: Window; VAR msg: SHORTINT);

  BEGIN
    IF (w.rexxCmd # "") & CN.ExecRexx(w, w.rexxCmd, FALSE) THEN
      msg:= CN.HandleRexx(w);
    END;
  END RepeatRexx;

  PROCEDURE ReplyMsg;

  BEGIN
    IF ~replied THEN
      Rx.ReplyMsg;
      replied:= TRUE;
    END;
  END ReplyMsg;

  PROCEDURE CheckRefresh(VAR date: Dos.Date);

  VAR
    now: Dos.Date;

  BEGIN
    Dos.DateStamp(now);
    IF (now.minute > date.minute)
       OR (now.tick - date.tick > 3 * Dos.ticksPerSecond) THEN
      IO.Refresh;  (* check windows for refreshing *)
      date:= now;
    END;
  END CheckRefresh;

BEGIN
  Dos.DateStamp(date);
  msg:= IO.msgLocal;
  WHILE Rx.GetMsg() DO
    replied:= FALSE;
    CheckRefresh(date);
    IF Rx.GetCmdID(cmdID) THEN
      CASE cmdID OF
      | Rx.activateWindow:      RxWindows.ActivateWindow(w);
      | Rx.beginMacro:          RxARexx.BeginMacro(w(Prj.Window));
      | Rx.beginMark:           RxSelection.BeginMark(w);
      | Rx.bottom:              RxPosition.Bottom(w);
      | Rx.busy:                RxWindows.Busy(w);
      | Rx.busyDone:            RxWindows.BusyDone(w);
      | Rx.closeAllFolds:       RxFolds.CloseAllFolds(w);
      | Rx.closeFold:           RxFolds.CloseFold(w);
      | Rx.copy:                RxSelection.Copy(w);
      | Rx.cursorOff:   w.CursorOff;
      | Rx.cursorOn:    w.CursorOn;
      | Rx.cut:                 RxSelection.Cut(w);
      | Rx.delBack:             RxEdit.DelBack(w);
      | Rx.delete:              RxEdit.Delete(w);
      | Rx.delLines:            RxEdit.DelLines(w);
      | Rx.displayBeep  : GUI.Flash;
      | Rx.displayOff   : IO.DisplayOff(w.text);
      | Rx.displayOn    : IO.DisplayOn(w.text);
      | Rx.displayPrefs:        RxConfig.DisplayPrefs(w);
      | Rx.down:                RxPosition.Down(w);
      | Rx.editPrefs:           RxConfig.EditPrefs(w);
      | Rx.endMacro:            RxARexx.EndMacro;
      | Rx.endMark:             RxSelection.EndMark(w);
      | Rx.enterRexx:           ReplyMsg;
                                RxARexx.EnterRexx(w(Window), w(Window).rexxCmd, msg);
      | Rx.erase:               RxSelection.Erase(w);
      | Rx.execMacro:           ReplyMsg;
                                RxARexx.ExecMacro;
      | Rx.execRexx:            ReplyMsg;
                                RxARexx.ExecuteRexx(w(Window), w(Window).rexxPrg, msg);
      | Rx.find:                RxPosition.Find(w(Prj.Window));
      | Rx.first:               RxPosition.First(w);
      | Rx.firstText:           RxTexts.FirstText(w);
      | Rx.firstWindow:         RxWindows.FirstWindow(w);
      | Rx.forceAutosave:       RxTexts.ForceAutosave;
      | Rx.freeMacro:           RxARexx.FreeMacro;
      | Rx.get:                 RxVars.GetVar(w(Prj.Window));
      | Rx.getASCII:            RxEdit.GetASCII(w);
      | Rx.getBookmark:         RxPosition.GetBookmark(w(Prj.Window));
      | Rx.gotoBookmark:        RxPosition.GotoBookmark(w(Prj.Window));
      | Rx.gotoLine:            RxPosition.GotoLine(w, w(Window).line);
      | Rx.information:         RxRequests.Information(w);
      | Rx.insASCII:            RxEdit.InsASCII(w);
      | Rx.insert:              RxEdit.Insert(w);
      | Rx.insertFile:          RxTexts.InsertFile(w);
      | Rx.item:                RxConfig.DefItem;
      | Rx.last:                RxPosition.Last(w);
      | Rx.lastChange:          RxPosition.LastChange(w);
      | Rx.lastText:            RxTexts.LastText(w);
      | Rx.lastWindow:          RxWindows.LastWindow(w);
      | Rx.left:                RxPosition.Left(w);
      | Rx.linkChanges:         RxEdit.LinkChanges(w);
      | Rx.linkChangesDone:     RxEdit.LinkChangesDone(w);
      | Rx.matchBracket:        RxPosition.MatchBracket(w); (* result = success *)
      | Rx.menu:                RxConfig.DefMenu;
      | Rx.move:                RxPosition.Move(w);
      | Rx.new:                 New(w(Prj.Window));
      | Rx.newFold:             RxFolds.NewFold(w);
      | Rx.newWindow:           NewWindow(w);
      | Rx.nextText:            RxTexts.NextText(w);
      | Rx.nextWindow:          RxWindows.NextWindow(w);
      | Rx.open:                OpenFile(w(Window));
      | Rx.openAllFolds:        RxFolds.OpenAllFolds(w);
      | Rx.openFold:            RxFolds.OpenFold(w);
      | Rx.pageDown:            RxPosition.PageDown(w);
      | Rx.pageUp:              RxPosition.PageUp(w);
      | Rx.paste:               RxSelection.Paste(w);
      | Rx.prevText:            RxTexts.PrevText(w);
      | Rx.prevWindow:          RxWindows.PrevWindow(w);
      | Rx.print:               RxTexts.Print(w);
      | Rx.printClip:           RxSelection.PrintClip(w);
      | Rx.quit:                RxWindows.Quit(w, msg);
      | Rx.read:                RxEdit.Read(w);
      | Rx.redo:                RxEdit.Redo(w);
      | Rx.redraw:IO.Draw(w.text);
      | Rx.repeatRexx:          ReplyMsg;
                                RepeatRexx(w(Window), msg);
      | Rx.replace:             RxEdit.Replace(w(Prj.Window));  (* result = changes > 0 *)
      | Rx.reqAction:           RxRequests.ReqAction(w);
      | Rx.reqNumber:           RxRequests.ReqNumber(w, w(Window).number);
      | Rx.reqOptions:          RxRequests.ReqOptions(w);
      | Rx.reqChoice:           RxRequests.ReqChoice(w);
      | Rx.reqString:           RxRequests.ReqString(w, w(Window).string);
      | Rx.right:               RxPosition.Right(w);
      | Rx.resolveAllFolds:     RxFolds.ResolveAllFolds(w);
      | Rx.resolveFold:         RxFolds.ResolveFold(w);
      | Rx.save:                (* $IF Demo THEN *) DemoMsg; (* $END *) RxTexts.Save(w);
      | Rx.saveAs:              (* $IF Demo THEN *) DemoMsg; (* $END *) RxTexts.SaveAs(w);
      | Rx.saveClip:            RxSelection.SaveClip(w);
      | Rx.saveMacro:           RxARexx.SaveMacro(w(Prj.Window));
      | Rx.saveSettings:        RxConfig.SaveSettings(w(Prj.Window));
      | Rx.saveTemp:            RxTexts.SaveTemp(w);
      | Rx.screenToBack:I.ScreenToBack(IO.screen);
      | Rx.screenToFront:I.ScreenToFront(IO.screen);
      | Rx.scrollDX:            RxPosition.ScrollDX(w);
      | Rx.scrollDY:            RxPosition.ScrollDY(w);
      | Rx.selectText:          RxTexts.SelectText(w);
      | Rx.selectWindow:        RxWindows.SelectWindow(w);
      | Rx.set:                 RxVars.SetVar(w(Prj.Window));
      | Rx.setBookmark:         RxPosition.SetBookmark(w(Prj.Window));
      | Rx.setKey:              RxConfig.SetKey;
      | Rx.setWindowBox:        RxWindows.SetWindowBox(w);
      | Rx.shiftClip:            RxSelection.ShiftClip(w);
      | Rx.subItem:             RxConfig.DefSubItem;
      | Rx.title:               RxWindows.Title(w);
      | Rx.toLower:             RxEdit.ToLower(w);
      | Rx.top:                 RxPosition.Top(w);
      | Rx.toUpper:             RxEdit.ToUpper(w);
      | Rx.undo:                RxEdit.Undo(w);
      | Rx.unMark:              RxSelection.UnMark(w);
      | Rx.up:                  RxPosition.Up(w);
      | Rx.windowToBack:        RxWindows.WindowToBack(w);
      | Rx.windowToFront:       RxWindows.WindowToFront(w);
      | Rx.wordBegin:           RxPosition.WordBegin(w);
      | Rx.wordEnd:             RxPosition.WordEnd(w);
      | Rx.wordLeft:            RxPosition.WordLeft(w);
      | Rx.wordRight:           RxPosition.WordRight(w);
(* $IF M2Amiga THEN *)
      | Rx.compile:             ReplyMsg;
                                IF RxModula2.Compile(w(Prj.Window)) THEN
                                  msg:= CN.HandleRexx(w);
                                END;
      | Rx.link:                ReplyMsg;
                                IF RxModula2.Link(w(Prj.Window)) THEN
                                  msg:= CN.HandleRexx(w);
                                END;
      | Rx.make:                RxModula2.Make(w(Prj.Window));
      | Rx.execute:             RxModula2.Execute(w(Prj.Window));
      | Rx.firstError:          RxModula2.FirstError(w(Prj.Window));
      | Rx.gotoError:           RxModula2.GotoError(w(Prj.Window));
      | Rx.loadErrors:          RxModula2.LoadErrors(w(Prj.Window));
      | Rx.nextError:           RxModula2.NextError(w(Prj.Window));
      | Rx.prevError:           RxModula2.PrevError(w(Prj.Window));
(* $END *)
      END;
    END;
    ReplyMsg;
  END;
  RETURN msg;
END HandleRexx;

(* $StackChk= *)

PROCEDURE HandleApp(): SHORTINT;

VAR
  w: L.NodePtr;
  msg: SHORTINT;
  appMsg: Workbench.AppMessagePtr;
  filename: ARRAY F.filenameLen OF CHAR;
  cmd: ARRAY F.filenameLen + 30 OF CHAR;
  a: ARRAY 1 OF LONGINT;

BEGIN
  msg:= IO.msgLocal;
  appMsg:= Exec.GetMsg(IO.appPort);
  WHILE appMsg # NIL DO
    w:= IO.windows.head;
    WHILE (w # NIL) & (w(IO.Window).id # appMsg.id) DO w:= w.next END;
    IF (w # NIL) & (appMsg.numArgs > 0)
      & EArgs.WBGetFilename(filename, appMsg.argList[0].lock, appMsg.argList[0].name) THEN
      a[0]:= SYS.ADR(filename);
      K.FormatString(cmd, "'Open \"%s\"'; 'ActivateWindow'", a);
      Exec.ReplyMsg(appMsg);
      IF CN.ExecRexx(w(W.Window), cmd, FALSE) THEN
        msg:= CN.HandleRexx(w(W.Window));
      END;
    ELSE
      Exec.ReplyMsg(appMsg);
    END;
    appMsg:= Exec.GetMsg(IO.appPort);
  END;
  RETURN msg;
END HandleApp;

(* execute startup file *)
PROCEDURE Startup(): BOOLEAN;

VAR
  done: BOOLEAN;        (* success? *)
  str: ARRAY 256 OF CHAR;
  a: ARRAY 1 OF LONGINT;

(* $IF Demo THEN *)
  PROCEDURE CheckDate;

  VAR
    d: Dos.Date;

  BEGIN
    Dos.DateStamp(d);
    IF d.days < 6250 THEN
      GUI.Flash;
      Req.ReqMessage(NIL, "DEMO: Uhr verstellt/nicht vorhanden?", "Abbrechen");
      HALT(-1);
    ELSIF ABS(3 - d.days) > demo1 * demo2 THEN
      GUI.Flash;
      Req.ReqMessage(NIL, "DEMO: Der 1.6.95 ist vorüber !", "Entscheidung");
      HALT(-1);
    END;
  END CheckDate;
(* $END *)

BEGIN
  (* $IF Demo THEN *)
  DemoMsg;
  CheckDate;
  (* $END *)
  done:= ((EArgs.startup # "") & F.Exists(EArgs.startup)
            & CN.ExecRexx(NIL, EArgs.startup, TRUE)
          OR F.Exists(Str.edtStartupLocalFile^)
            & CN.ExecRexx(NIL, Str.edtStartupLocalFile^, TRUE)
          OR F.Exists(Str.edtStartupLocalFileD)
            & CN.ExecRexx(NIL, Str.edtStartupLocalFileD, TRUE)
          OR F.Exists(Str.edtStartupFile^)
            & CN.ExecRexx(NIL, Str.edtStartupFile^, TRUE)
          OR F.Exists(Str.edtStartupFileD)
            & CN.ExecRexx(NIL, Str.edtStartupFileD, TRUE))
          & (CN.HandleRexx(NIL) = IO.msgLocal) & Launch();
  IF done THEN
    RETURN TRUE;
  ELSE
    a[0]:= SYS.ADR(Str.edtStartupFile^);
    K.FormatString(str, Str.noStartupFile^, a);
    Req.ReqMessage(NIL, str, Str.cancel^);
    HALT(K.rcImportantNotFound);
  END;
END Startup;

PROCEDURE* CloseProc(w: W.Window): SHORTINT;

BEGIN
  w.Busy(Str.quitting^);
  IF Prj.Close(w(Window), FALSE) THEN
    RETURN IO.msgClosed;
  ELSE
    w.BusyDone;
    RETURN IO.msgLocal;
  END;
END CloseProc;

(* not enough memory *)
PROCEDURE* OutOfMemProc;

BEGIN
  IF Req.ReqAction(NIL, Str.notEnoughMem^, Str.retryQuit^) = 0 THEN
    K.memAlert:= TRUE;
    HALT(K.rcAborted);
  END;
END OutOfMemProc;

(* -- main -- *)

BEGIN
  except:= TRUE;
(* $IF Demo THEN *)
  demo2:= 13;
(* $END *)
  OberonLib.OutOfMemHandler:= OutOfMemProc;
  CN.ExecRexx:= ExecRexx;
  CN.HandleRexx:= HandleRexx;
(* $IF Demo THEN *)
  demo1:= 489;
(* $END *)
  W.closeProc:= CloseProc;
  IF Startup() THEN
    msg:= W.HandleInput();
    LOOP
      CASE msg OF
        IO.msgQuit    : EXIT;
      | IO.msgClosed  : w:= W.active(Window);
                        w.Dispose; DISPOSE(w);
                        IF IO.NumWindows() = 0 THEN EXIT END;
                        msg:= W.HandleInput();
      | IO.msgRexxCtrl: IF W.active # NIL THEN
                          msg:= CN.HandleRexx(W.active(Window));
                        ELSE
                          Req.ReqMessage(NIL, Str.noActiveWindow^, Str.ok^);
                          msg:= W.HandleInput();
                        END;
      | IO.msgAppCtrl:  msg:= HandleApp();
      ELSE
        msg:= W.HandleInput();
      END;
    END;
  ELSE
    GUI.Flash;   (* very serious or by intention *)
  END;
  except:= FALSE;  (* no exception *)

(* $IF Demo THEN *)
  DemoMsg;
(* $END *)

CLOSE
  IF except & (IO.NumWindows() > 0) THEN  (* soft. failure, stack ovfl., mem. *)
    WHILE ~Prj.CloseAll(IO.windows.head(Window)) DO END;  (* first window *)
  END;
END M2Edt.
