<reponame>romiras/Blackbox-fw-playground<filename>Obx/Mod/Db.cp
﻿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.
