MODULE ObxDb;
(**

   project   = "BlackBox"
   organization   = "www.oberon.ch"
   contributors   = "Oberon microsystems"
   version   = "System/Rsrc/About"
   copyright   = "System/Rsrc/About"
   license   = "Docu/BB-License"
   changes   = ""
   issues   = ""

**)

   IMPORT Views, TextModels, TextMappers, TextViews, TextControllers;

   CONST

      int = TextMappers.int; string = TextMappers.string; real = TextMappers.real; invalid = TextMappers.invalid;
   TYPE

      Node = POINTER TO RECORD
         next: Node;
         id: INTEGER;
         name: TextMappers.String;
         value: REAL
      END;
   VAR list: Node;

   PROCEDURE Enter (id: INTEGER; name: TextMappers.String; value: REAL);

      VAR n, h, p: Node;
   BEGIN   (* insert a new tuple into the list at its correct position *)
      NEW(n); n.id := id; n.name := name; n.value := value;
      h := list; p := NIL; WHILE (h # NIL) & (h.id <= id) DO p := h; h := h.next END;
      IF p # NIL THEN   (* insert between p and h *)
         p.next := n
      ELSE   (* insert at beginning *)
         list := n
      END;
      n.next := h
   END Enter;
   PROCEDURE EnterData*;

      VAR c: TextControllers.Controller; beg, end: INTEGER;
         s: TextMappers.Scanner; id: INTEGER;
         name: TextMappers.String; value: REAL;
   BEGIN   (* read a text selection and split it into an integer, string, and real field *)
      c := TextControllers.Focus();
      IF (c # NIL) & c.HasSelection() THEN
         c.GetSelection(beg, end);
         s.ConnectTo(c.text); s.SetPos(beg);
         s.Scan;
         WHILE (s.type = TextMappers.int) & (s.Pos() <= end) DO
            IF s.type = int THEN id := s.int; s.Scan ELSE s.type := TextMappers.invalid END;
            IF s.type = string THEN name := s.string; s.Scan ELSE s.type := invalid END;
            IF s.type = real THEN value := SHORT(s.real); s.Scan ELSE s.type := invalid END;
            Enter(id, name, value)   (* enter the new tuple into a global list *)
         END;
         c.SelectAll(FALSE)
      END
   END EnterData;
   PROCEDURE ListData*;

      VAR t: TextModels.Model; n: Node; f: TextMappers.Formatter;
   BEGIN   (* write all entered tuples into a new text *)
      t := TextModels.dir.New();
      f.ConnectTo(t); f.SetPos(0);
      n := list;
      WHILE n # NIL DO
         f.WriteInt(n.id); f.WriteTab; f.WriteString(n.name); f.WriteTab;
         (* WriteRealForm allows to format real numbers in a flexible way, here rounding to two digits after the comma *)
         f.WriteRealForm(n.value, 7, 0, -2, TextModels.digitspace);   (* "-2" indicates 2 digits after comma *)
         f.WriteLn;
         n := n.next
      END;
      Views.OpenView(TextViews.dir.New(t))
   END ListData;
   PROCEDURE Reset*;

   BEGIN   (* release data structure so that garbage collector can reclaim memory *)
      list := NIL
   END Reset;
END ObxDb.