(*
  .name       bookmarks
  .task       relocatable position marks
  .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: 28/02/95, 12:18:23, version 64 *)

MODULE Bookmarks;

IMPORT
  K:= Kernel,
  L:= UntracedLists;

TYPE
  Data* = L.NodePtr;
  DataDesc* = L.Node;

  BookmarkID* = INTEGER;
  Bookmark = UNTRACED POINTER TO BookmarkDesc;
  BookmarkDesc = RECORD (K.ANYDesc)
    pos:  LONGINT;
    line: LONGINT;
    data: Data;
  END;

  StoredBookmark = UNTRACED POINTER TO StoredBookmarkDesc;
  StoredBookmarkDesc = RECORD (K.ANYDesc)
    dpos: LONGINT;
    dline: LONGINT;
    id: BookmarkID;  (* which bookmark *)
  END;

  BookmarkList* = RECORD (K.DynArray) END;
  BookmarkStorage* = RECORD (K.DynArray) END;

(* -- bookmark arrays: -- *)

PROCEDURE (VAR b: BookmarkList) NotifyInsert* (pos, len, lines: LONGINT);

VAR
  i: INTEGER;
  bm: K.ANY;

BEGIN
  FOR i:= 0 TO b.len - 1 DO
    bm:= b.Get(i);
    IF bm # NIL THEN
   (* (* $RangeChk- $OvflChk- $NilChk- *) *)
      WITH bm: Bookmark DO
        IF pos <= bm.pos THEN
          INC(bm.pos, len);
          INC(bm.line, lines);
        END;
      END;
    (* (* $RangeChk= $OvflChk= $NilChk= *) *)
    END;
  END;
END NotifyInsert;

PROCEDURE (VAR b: BookmarkList) NotifyDelete* (pos, len, lines: LONGINT);

VAR
  i: INTEGER;
  bm: K.ANY;

BEGIN
  FOR i:= 0 TO b.len - 1 DO
    bm:= b.Get(i);
    IF bm # NIL THEN
(* (* $RangeChk- $OvflChk- $NilChk- *) *)
      WITH bm: Bookmark DO
        IF pos < bm.pos THEN
          IF pos + len <= bm.pos THEN
            DEC(bm.pos, len);
            DEC(bm.line, lines);
          ELSIF bm.data = NIL THEN
            b.Delete(i);
          END;
        END;
      END;
    (* (* $RangeChk= $OvflChk= $NilChk= *) *)
    END;
  END;
END NotifyDelete;

PROCEDURE (VAR b: BookmarkList) NewBookmark* (pos, line: LONGINT): BookmarkID;  (* id *)

VAR
  i: BookmarkID;
  bm: Bookmark;

BEGIN
  i:= 0;
  WHILE (i < b.len) & (b.Get(i) # NIL) DO INC(i) END;
  NEW(bm);
  bm.pos:= pos;
  bm.line:= line;
  bm.data:= NIL;
  b.Put(bm, i);
  RETURN i;
END NewBookmark;

PROCEDURE (VAR b: BookmarkList) FreeBookmark* (id: BookmarkID);

VAR
  n: K.ANY;

BEGIN
  n:= b.Get(id);
  IF n # NIL THEN b.Delete(id) END;
END FreeBookmark;

PROCEDURE (VAR b: BookmarkList) GetBookmark* (id: BookmarkID; VAR pos, line: LONGINT;
                                              VAR data: Data);

VAR
  bm: K.ANY;

BEGIN
  IF id < b.len THEN
    bm:= b.Get(id);
    IF bm # NIL THEN
      WITH bm: Bookmark DO
        pos:= bm.pos;
        line:= bm.line;
        data:= bm.data;
      END;
      RETURN;
    END;
  END;
  pos:= 0;
  line:= 0;
  data:= NIL;
END GetBookmark;

(* get position and line next to pos *)
PROCEDURE (VAR b: BookmarkList) GetBMFromPos* (pos: LONGINT; VAR p0, l0: LONGINT);

VAR
  p, l: LONGINT;
  i: INTEGER;
  data: Data;

BEGIN
  FOR i:= 0 TO b.len - 1 DO
    b.GetBookmark(i, p, l, data);
    IF ABS(pos - p) < ABS(pos - p0) THEN
      p0:= p;
      l0:= l;
    END;
  END;
END GetBMFromPos;

(* get position of a line *)
PROCEDURE (VAR b: BookmarkList) GetBMFromLine* (line: LONGINT; VAR p0, l0: LONGINT);

VAR
  p, l: LONGINT;
  i: INTEGER;
  data: Data;

BEGIN
  FOR i:= 0 TO b.len - 1 DO
    b.GetBookmark(i, p, l, data);
    IF ABS(line - l) < ABS(line - l0) THEN
      p0:= p;
      l0:= l;
    END;
  END;
END GetBMFromLine;

(* -- folder support -- *)

(* hide part: save bookmarks in the range pos to pos + len *)
PROCEDURE (VAR b: BookmarkList) Hide* (VAR storage: BookmarkStorage;
                                       pos, line, len, numLines: LONGINT;
                                       data: Data);

VAR
  i, i0: INTEGER;
  n: K.ANY;
  x: StoredBookmark;

BEGIN
  storage.New(5, 10);
  i0:= 0;
  FOR i:= 0 TO b.len - 1 DO
    n:= b.Get(i);
    IF n # NIL THEN
      WITH n: Bookmark DO
        IF (pos < n.pos) & (pos + len > n.pos) THEN
          NEW(x);
          x.id:= i;
          x.dpos:= n.pos - pos;
          x.dline:= n.line - line;
          storage.Put(x, i0);
          INC(i0);
          n.pos:= 0;
          n.line:= 0;
          n.data:= data;
        END;
      END;
    END;
  END;
  b.NotifyDelete(pos, len, numLines);
END Hide;

(* show hidden part: restore bookmarks at pos; line *)
PROCEDURE (VAR b: BookmarkList) Show* (VAR storage: BookmarkStorage;
                                       pos, line, len, numLines: LONGINT);

VAR
  i: INTEGER;
  n: Bookmark;
  x: StoredBookmark;

BEGIN
  b.NotifyInsert(pos, len, numLines);
  FOR i:= 0 TO storage.len - 1 DO
    x:= storage.Get(i)(StoredBookmark);
    n:= b.Get(x.id)(Bookmark);
    IF n # NIL THEN
      n.pos:= pos + x.dpos;
      n.line:= line + x.dline;
      n.data:= NIL;
    END;
  END;
  storage.Dispose;
END Show;

END Bookmarks.