(*
  .name       EProject
  .task       extended window class, standart requesters
  .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, 19:34:12, version 100 *)

MODULE EProject;   (* main *)

IMPORT
  SYS:= SYSTEM,

(* $IF M2Amiga THEN *)
  M2:= Modula2,
(* $END *)

  AVL:= UntracedAVL,
  BM:= Bookmarks,
  F:= Files,
  GUI,
  I:= Intuition,
  IO:= IOServer,
  K:= Kernel,
  L:= UntracedLists,
  OberonLib,
  Req:= ERequests,
  Rx:= ERexx,
  S:= Strings,
  Sett:= Settings,
  Str:= StrPool,
  T:= ETexts,
  T0:= Texts,
  W:= Windows;

CONST
  uniqueID* = -1;
  uniqueMin0 = 10000;

TYPE
  Bookmark= UNTRACED POINTER TO BookmarkDesc;
  BookmarkDesc= RECORD (AVL.Node)
    id: INTEGER;
    bm: BM.BookmarkID;
  END;

  Window* = UNTRACED POINTER TO WindowDesc;
  WindowDesc* = RECORD (W.WindowDesc)
    bookmarks: AVL.Root;
    uniqueMin: INTEGER;
    srFlags* : SHORTSET;
    searchStr*, replaceStr*: ARRAY 256 OF CHAR;
    rxSearchStr*, rxReplaceStr*: ARRAY 256 OF CHAR;
    rxSearchStrLen*, rxReplaceStrLen*: INTEGER;
  END;

  Text* = UNTRACED POINTER TO TextDesc;
  TextDesc* = RECORD (T.TextDesc)
(* $IF M2Amiga THEN *)
    errors- : M2.ErrList;
(* $END *)
  END;

VAR
  searchID: INTEGER;

(* -- bookmark class -- *)

PROCEDURE* BMFind(VAR root: AVL.Root; n: AVL.NodePtr): INTEGER;

BEGIN
  RETURN n(BookmarkDesc).id - searchID;
END BMFind;

PROCEDURE* BMComp(a, b: AVL.NodePtr): INTEGER;

BEGIN
  RETURN a(BookmarkDesc).id - b(BookmarkDesc).id;
END BMComp;

(* -- standart requests -- *)

PROCEDURE ReqFilename* (w: W.Window; save: BOOLEAN): BOOLEAN;

VAR
  name: ARRAY F.nameLen OF CHAR;
  path: ARRAY F.pathLen OF CHAR;
  filename: ARRAY F.pathLen + F.nameLen OF CHAR;

BEGIN
  F.GetFilename(filename, w.text(Text).path, w.text(Text).name);
  IF Req.ReqFile(w.win, filename, save) THEN
    F.GetName(name, filename);
    F.GetPath(path, filename);
    w.text(Text).RenameFile(path, name);
    RETURN TRUE;
  ELSE
    RETURN FALSE;
  END;
END ReqFilename;

(* report error opening 'name' *)
PROCEDURE ReqOpenErr* (w: W.Window; name: ARRAY OF CHAR): BOOLEAN;

(* $CopyArrays- *)

BEGIN
  RETURN Req.ReqActionArgs(w.win, Str.cannotOpenFile^, Str.retryCancel^, SYS.ADR(name), 0) # 0;
END ReqOpenErr;

(* report error inserting 'name' *)
PROCEDURE ReqInsertErr* (w: W.Window; name: ARRAY OF CHAR): BOOLEAN;

(* $CopyArrays- *)

BEGIN
  RETURN Req.ReqActionArgs(w.win, Str.cannotInsertFile^, Str.retryCancel^, SYS.ADR(name), 0) # 0;
END ReqInsertErr;

(* report error saving 'name' *)
PROCEDURE ReqSaveErr* (w: W.Window): BOOLEAN;

VAR
  name: ARRAY F.nameLen OF CHAR;

(* $CopyArrays- *)

BEGIN
  COPY(w.text(Text).name, name);
  RETURN Req.ReqActionArgs(w.win, Str.cannotSaveFile^, Str.retryCancel^, SYS.ADR(name), 0) # 0;
END ReqSaveErr;

PROCEDURE ReqSaveX(w: W.Window; gads: ARRAY OF CHAR): SHORTINT;

VAR
  name: ARRAY F.nameLen OF CHAR;

(* $CopyArrays- *)

BEGIN
  COPY(w.text(Text).name, name);
  RETURN Req.ReqActionArgs(w.win, Str.fileNotSaved^, gads, SYS.ADR(name), w.text(Text).changes);
END ReqSaveX;

(* ask if file should be saved *)
PROCEDURE ReqSave* (w: W.Window): SHORTINT;

BEGIN
  RETURN ReqSaveX(w, Str.saveSaveAsNoCancel^);
END ReqSave;

(* ask if file should be saved: edt is quitting *)
PROCEDURE ReqSaveForce* (w: W.Window): SHORTINT;

BEGIN
  RETURN ReqSaveX(w, Str.saveSaveAsNo^);
END ReqSaveForce;

PROCEDURE ReqSaveAllX(w: W.Window; gads: ARRAY OF CHAR): SHORTINT;

VAR
  files, chgs: LONGINT;
  t: L.NodePtr;

(* $CopyArrays- *)

BEGIN
  files:= 0; chgs:= 0; t:= IO.texts.head;
  WHILE t # NIL DO
    IF t(Text).changes # 0 THEN INC(files); INC(chgs, ABS(t(Text).changes)) END;
    t:= t.next;
  END;
  IF chgs > 0 THEN
    RETURN Req.ReqActionArgs(w.win, Str.quitAll^, gads, files, chgs);
  ELSE
    RETURN 1;  (* ok *)
  END;
END ReqSaveAllX;

(* ask if files should be saved *)
PROCEDURE ReqSaveAll* (w: W.Window): SHORTINT;

BEGIN
  RETURN ReqSaveAllX(w, Str.quitSaveCancel^);
END ReqSaveAll;

(* ask if files should be saved *)
PROCEDURE ReqSaveAllForce* (w: W.Window): SHORTINT;

BEGIN
  RETURN ReqSaveAllX(w, Str.quitSave^);
END ReqSaveAllForce;

(* -- text class -- *)

(* $IF M2Amiga THEN *)

PROCEDURE (t: Text) New*;

BEGIN
  t.New^;
  t.errors.New;
END New;

PROCEDURE (t: Text) Dispose*;

BEGIN
  t.errors.Dispose(t);
  t.Dispose^;
END Dispose;

(* $END *)

PROCEDURE (t: Text) Open* (filename: ARRAY OF CHAR): BOOLEAN;

VAR
  iname: ARRAY F.filenameLen OF CHAR;

(* $CopyArrays- *)

BEGIN
  COPY(filename, iname);
  S.Append(iname, ".info");
  t.icons:= F.Exists(iname);
  RETURN t.Open^(filename);
END Open;

(* -- window class -- *)

PROCEDURE (w: Window) New*;

BEGIN
  AVL.Init(w.bookmarks, BMComp, BMFind);
  w.uniqueMin:= uniqueMin0;
  w.srFlags:= Sett.srFlags;
  w.searchStr:= "";
  w.replaceStr:= "";
  w.rxSearchStr:= ""; w.rxSearchStrLen:= 0;
  w.rxReplaceStr:= ""; w.rxReplaceStrLen:= 0;
END New;

PROCEDURE (w: Window) Dispose* ;

BEGIN
  AVL.Dispose(w.bookmarks);
END Dispose;

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

BEGIN
  IF w.Open^(t) THEN
    AVL.Dispose(w.bookmarks);
    t(Text).BeginAccess;
    RETURN TRUE;
  ELSE
    RETURN FALSE;
  END;
END Open;

PROCEDURE (w: Window) Close* ;

BEGIN
  w.text(Text).EndAccess;
  w.Close^;
END Close;

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

VAR
  old: Text;

BEGIN
  old:= w.text(Text);
  AVL.Dispose(w.bookmarks);
  t(T.Text).NewPattern(w.searchStr);
  w.NewText^(t);
  t(Text).BeginAccess;
  old.EndAccess;
  w.uniqueMin:= uniqueMin0;
END NewText;

PROCEDURE (w: Window) SetMark* (id: INTEGER; pos: LONGINT): INTEGER;

VAR
  t: T.Text;
  bm, old: Bookmark;

BEGIN
  t:= w.text(T.Text);
  NEW(bm);
  bm.bm:= t.NewBookmark(pos, t.GetPosLine(pos, w.first, w.oy));
  IF id = uniqueID THEN
    bm.id:= w.uniqueMin;
    WHILE ~AVL.Add(w.bookmarks, bm) DO INC(bm.id) END;
    w.uniqueMin:= bm.id + 1;
    RETURN bm.id;
  ELSE
    bm.id:= id;
    IF ~AVL.Add(w.bookmarks, bm) THEN
      searchID:= id;
      old:= AVL.Find(w.bookmarks)(Bookmark);
      t.FreeBookmark(old.bm);
      old.bm:= bm.bm;  (* overwrite *)
      DISPOSE(bm);
    END;
    RETURN id;
  END;
END SetMark;

PROCEDURE (w: Window) GetMark* (id: INTEGER): LONGINT;

VAR
  p, l: LONGINT;
  bm: AVL.NodePtr;

BEGIN
  searchID:= id;
  bm:= AVL.Find(w.bookmarks);
  IF bm # NIL THEN
    w.text(T.Text).GetBookmark(bm(BookmarkDesc).bm, p, l);
    RETURN p;
  ELSE
    GUI.Flash;
    RETURN w.pos;
  END;
END GetMark;

(* -- project commands -- *)

PROCEDURE^ Save* (w: W.Window);

PROCEDURE SaveAs* (w: W.Window);

BEGIN
  IF ReqFilename(w, TRUE) THEN
    w.text(T.Text).new:= FALSE;
    Save(w);
  END;
END SaveAs;

PROCEDURE Save* (w: W.Window);

VAR
  t: Text;

BEGIN
  t:= w.text(Text);
  IF t.new THEN
    SaveAs(w);  (* --> Save(w) *)
  ELSE
    IO.Busy(t, Str.saving^);
    REPEAT UNTIL t.Save() OR ~ReqSaveErr(w) OR ~ReqFilename(w, TRUE);
    IO.BusyDone(t);
  END;
END Save;

(* attempt to close window, result: done *)
PROCEDURE Close* (w: W.Window; force: BOOLEAN): BOOLEAN;

BEGIN
  IF  (w.text(T.Text).useCount = 1) & (w.text(T.Text).changes # 0) THEN
    IF force THEN
      CASE ReqSaveForce(w) OF
        0:                (* no *)
      | 1: Save(w);       (* save *)
      | 2: SaveAs(w);     (* save as *)
      END;
    ELSE
      CASE ReqSave(w) OF
        0: RETURN FALSE;  (* cancel *)
      | 1: Save(w);       (* save *)
      | 2: SaveAs(w);     (* save as *)
      | 3:                (* no *)
      END;
    END;
  END;
  w.Close;
  RETURN TRUE;
END Close;

(* DoForwards() proc. *)
PROCEDURE* CloseWindow(w: L.NodePtr);

BEGIN
  IF Close(w(W.Window), TRUE) THEN END;
END CloseWindow;

(* attempt to close all windows *)
PROCEDURE CloseAll* (w: W.Window): BOOLEAN;

VAR
  windowList: L.List;

BEGIN
  windowList:= IO.windows;
  IF OberonLib.closing THEN
    CASE ReqSaveAllForce(w) OF
      0: L.DoForward(windowList, CloseWindow);  (* save *)
    | 1: IO.CloseAllWindows;     (* quit *)
    END;
  ELSE
    CASE ReqSaveAll(w) OF
      0: RETURN FALSE;                (* cancel *)
    | 1: IO.CloseAllWindows;          (* quit without saving *)
    | 2: L.DoForward(windowList, CloseWindow);       (* save *)
    END;
  END;
  RETURN TRUE;
END CloseAll;

END EProject.