MODULE FormGen;
(**

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

**)

   
   IMPORT
      Strings, Meta, Dates, Files, Ports, Views, Properties, Dialog, Containers, Documents,
      Controls, StdDialog, StdCmds, FormModels, FormViews, FormControllers;
   CONST

      defaultDelta = 4 * Ports.point;
      minW = 10 * Ports.mm; minH = 4 * Ports.mm;
      maxW = 200 * Ports.mm; maxH = 50 * Ports.mm;
      
   TYPE
      ProcVal = RECORD (Meta.Value)
         p: PROCEDURE
      END;
   VAR

      new*: RECORD
         link*: Dialog.String
      END;
      list: RECORD (Meta.Value) p*: POINTER TO Dialog.List END;

      select: RECORD (Meta.Value) p*: POINTER TO Dialog.Selection END;
      combo: RECORD (Meta.Value) p*: POINTER TO Dialog.Combo END;
      date: RECORD (Meta.Value) p*: POINTER TO Dates.Date END;
      time: RECORD (Meta.Value) p*: POINTER TO Dates.Time END;
      color: RECORD (Meta.Value) p*: POINTER TO Dialog.Color END;
      currency: RECORD (Meta.Value) p*: POINTER TO Dialog.Currency END;
      tree: RECORD (Meta.Value) p*: POINTER TO Dialog.Tree END;
   PROCEDURE GetSize (v: Views.View; VAR w, h: INTEGER);


   BEGIN
      w := Views.undefined; h := Views.undefined;
      Properties.PreferredSize(v, minW, maxW, minH, maxH, minW, minH, w, h)
   END GetSize;
   PROCEDURE Gen (f: FormModels.Model; fv: FormViews.View; VAR path, name: ARRAY OF CHAR;

               wr: FormModels.Writer; var: Meta.Item; VAR x, y, max: INTEGER; VAR first: BOOLEAN);
      VAR v, str: Views.View; dummy, delta, x0, y0, m, w, h, i: INTEGER; n: Meta.Name;
         p: Controls.Prop; s: Meta.Scanner; elem: Meta.Item; ok, back: BOOLEAN; pv: ProcVal;
   BEGIN
      dummy := 0; delta := defaultDelta; FormViews.RoundToGrid(fv, dummy, delta);
      NEW(p); p.guard := ""; p.notifier := ""; p.label := ""; p.level := 0;
      p.opt[0] := FALSE; p.opt[1] := FALSE; p.opt[2] := FALSE; p.opt[3] := FALSE; p.opt[4] := FALSE;
      back := FALSE;
      FormViews.RoundToGrid(fv, x, y);
      v := NIL; w := Views.undefined; x0 := x; y0 := y;
      IF (name[0] >= "0") & (name[0] <= "9") THEN p.link := path + "[" + name + "]"
      ELSIF name # "" THEN p.link := path + "." + name
      ELSE p.link := path$
      END;
      IF var.obj = Meta.modObj THEN   (* traverse module *)
         s.ConnectTo(var); s.Scan; m := 0;
         WHILE ~ s.eos DO
            s.GetObjName(n);
            Gen(f, fv, p.link, n, wr, s.this, x, y, m, first);
            s.Scan
         END;
         IF m > max THEN max := m END;
      ELSIF var.obj = Meta.procObj THEN
         var.GetVal(pv, ok);
         IF ok THEN   (* command *)
            IF first THEN p.opt[Controls.default] := TRUE END;
            p.label := name$; v := Controls.dir.NewPushButton(p);
            p.opt[Controls.default] := FALSE; first := FALSE
         END
      ELSIF var.obj = Meta.varObj THEN
         IF (var.typ IN {Meta.byteTyp, Meta.sIntTyp, Meta.longTyp, Meta.intTyp, Meta.sRealTyp, Meta.realTyp})
            OR (var.typ = Meta.arrTyp) & (var.BaseTyp() = Meta.charTyp) THEN
         p.opt[Controls.left] := TRUE; v := Controls.dir.NewField(p)
         ELSIF var.typ = Meta.boolTyp THEN
            p.label := name$; v := Controls.dir.NewCheckBox(p)
         ELSIF var.typ = Meta.procTyp THEN
            var.GetVal(pv, ok);
            IF ok THEN   (* command *)
               IF first THEN p.opt[Controls.default] := TRUE END;
               p.label := name$; v := Controls.dir.NewPushButton(p);
               p.opt[Controls.default] := FALSE; first := FALSE
            END
         ELSIF var.typ = Meta.recTyp THEN
            IF var.Is(list) THEN v := Controls.dir.NewListBox(p)
            ELSIF var.Is(select) THEN v := Controls.dir.NewSelectionBox(p)
            ELSIF var.Is(combo) THEN v := Controls.dir.NewComboBox(p)
            ELSIF var.Is(date) THEN v := Controls.dir.NewDateField(p)
            ELSIF var.Is(time) THEN v := Controls.dir.NewTimeField(p)
            ELSIF var.Is(color) THEN v := Controls.dir.NewColorField(p)
            ELSIF var.Is(currency) THEN p.opt[Controls.left] := TRUE; v := Controls.dir.NewField(p)
            ELSIF var.Is(tree) THEN
               p.opt[Controls.haslines] := TRUE; p.opt[Controls.hasbuttons] := TRUE;
               p.opt[Controls.atroot] := TRUE; p.opt[Controls.foldericons] := TRUE;
               v := Controls.dir.NewTreeControl(p)
            ELSE   (* traverse record *)
               s.ConnectTo(var); s.Scan; m := 0;
               IF name # "" THEN INC(x, delta); INC(y, 4 * delta) END;
               WHILE ~ s.eos DO
                  s.GetObjName(n);
                  Gen(f, fv, p.link, n, wr, s.this, x, y, m, first);
                  s.Scan
               END;
               IF m > max THEN max := m END;
               IF (name # "") & (m > 0) THEN   (* insert group *)
                  p.label := name$; v := Controls.dir.NewGroup(p);
                  w := m; x := x0; h := y + delta - y0; y := y0; back := TRUE
               END
            END
         ELSIF var.typ = Meta.arrTyp THEN   (* traverse array *)
            i := 0; m := 0;
            IF name # "" THEN INC(x, delta); INC(y, 4 * delta) END;
            WHILE i < var.Len() DO
               var.Index(i, elem);
               Strings.IntToString(i, n);
               Gen(f, fv, p.link, n, wr, elem, x, y, m, first);
               INC(i)
            END;
            IF m > max THEN max := m END;
            IF (name # "") & (m > 0) THEN   (* insert group *)
               p.label := name$; v := Controls.dir.NewGroup(p);
               w := m; x := x0; h := y + delta - y0; y := y0; back := TRUE
            END
         END
      END;
      IF v # NIL THEN
         IF (name # "") & (p.label = "") THEN
            p.label := name$;
            str := Controls.dir.NewCaption(p); GetSize(str, w, h);
            wr.WriteView(str, x, y, x + w, y + h); INC(x, w + delta);
            FormViews.RoundToGrid(fv, x, y)
         END;
         IF ~back THEN GetSize(v, w, h) END;
         wr.WriteView(v, x, y, x + w, y + h); INC(x, w + delta);
         IF back THEN f.PutAbove(v, NIL) END;
         y := y + h + delta
      END;
      IF x > max THEN max := x END;
      x := x0
   END Gen;
   PROCEDURE NewDialog (name: ARRAY OF CHAR): Views.View;

      VAR var: Meta.Item;x, y, max: INTEGER; wr: FormModels.Writer; d: Documents.Document;
         f: FormModels.Model; v: FormViews.View; first: BOOLEAN; n: ARRAY 4 OF CHAR;
         c: Containers.Controller;
   BEGIN
      f := NIL; v := NIL;
      Meta.LookupPath(name, var);
      IF (var.obj = Meta.varObj) OR (var.obj = Meta.modObj) THEN
         x := defaultDelta; y := defaultDelta; max := 50 * Ports.point; first := TRUE;
         f := FormModels.dir.New();
         v := FormViews.dir.New(f); c := v.ThisController();
         IF c # NIL THEN
            c.SetOpts(Containers.layout)
         ELSE
            v.SetController(FormControllers.dir.NewController(Containers.layout))
         END;
         wr := f.NewWriter(NIL); wr.Set(NIL); n := "";
         Gen(f, v, name, n, wr, var, x, y, max, first);
         d := Documents.dir.New(v, max, y)
      ELSE Dialog.ShowParamMsg("#Form:VariableNotFound", name, "", "")
      END;
      RETURN v
   END NewDialog;
   PROCEDURE Create*;

      VAR v: Views.View; i, j: INTEGER; mod, name: Files.Name; loc: Files.Locator;
         w: FormViews.View; c: Containers.Controller;
   BEGIN
      v := NIL;
      IF new.link = "" THEN
         w := FormViews.dir.New(FormModels.dir.New()); c := w.ThisController();
         IF c # NIL THEN
            c.SetOpts({FormControllers.noFocus})
         ELSE
            w.SetController(FormControllers.dir.NewController({FormControllers.noFocus}))
         END;
         v := w
      ELSE
         v := NewDialog(new.link)
      END;
      IF v # NIL THEN
         mod := new.link$; new.link := ""; name := "";
         StdCmds.CloseDialog;
         IF mod # "" THEN
            i := 0; WHILE (mod[i] # 0X) & (mod[i] # ".") DO INC(i) END;
            IF mod[i] # 0X THEN   (* module.variable *)
               mod[i] := 0X; INC(i); j := 0;
               WHILE mod[i] # 0X DO name[j] := mod[i]; INC(i); INC(j) END;
               name[j] := 0X
            END;
            StdDialog.GetSubLoc(mod, "Rsrc", loc, mod);
            IF name = "" THEN name := mod$ END;
            loc.res := 77
         ELSE
            loc := NIL; name := ""
         END;
         Views.Open(v, loc, name, NIL);
         Views.BeginModification(Views.notUndoable, v);
         Views.EndModification(Views.notUndoable, v)
      END
   END Create;
   
   PROCEDURE Empty*;
   BEGIN
      new.link := ""; Create
   END Empty;
   
   PROCEDURE CreateGuard* (VAR par: Dialog.Par);
   BEGIN
      IF new.link = "" THEN par.disabled := TRUE END
   END CreateGuard;
END FormGen.