MODULE ObxGraphs;

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


   IMPORT Services, Stores, Ports, Models, Views, Controllers, Properties, TextModels, TextViews, TextMappers;

   CONST minVersion = 0; maxVersion = 0;


         next: Value;
         val: INTEGER
      Model = POINTER TO RECORD (Models.Model)

         values: Value
      View = POINTER TO RECORD (Views.View)

         model: Model
      ModelOp = POINTER TO RECORD (Stores.Operation)

         model: Model;
         values: Value
   PROCEDURE (op: ModelOp) Do;

      VAR v: Value; msg: Models.UpdateMsg;
      v := op.model.values; op.model.values := op.values; op.values := v;   (* swap *)
      Models.Broadcast(op.model, msg)
   END Do;
   PROCEDURE (m: Model) Internalize (VAR rd: Stores.Reader);

      VAR version: INTEGER; n: INTEGER; v, last: Value;
      rd.ReadVersion(minVersion, maxVersion, version);
      IF ~rd.cancelled THEN
         last := NIL;
         rd.ReadInt(n);   (* read number of values *)
         WHILE n # 0 DO
            NEW(v); rd.ReadInt(v.val);
            IF last = NIL THEN m.values := v ELSE := v END;   (* append value *)
            last := v;
   END Internalize;
   PROCEDURE (m: Model) Externalize (VAR wr: Stores.Writer);

      VAR v: Value; n: INTEGER;
      v := m.values; n := 0; WHILE v # NIL DO INC(n); v := END;
      wr.WriteInt(n);   (* write number of values *)
      v := m.values; WHILE v # NIL DO wr.WriteInt(v.val); v := END
   END Externalize;
   PROCEDURE (m: Model) CopyFrom (source: Stores.Store);

      m.values := source(Model).values   (* values are immutable and thus can be shared *)
   END CopyFrom;
   PROCEDURE (m: Model) SetValues (v: Value), NEW;

      VAR op: ModelOp;
      NEW(op); op.model := m; op.values := v;
      Models.Do(m, "Set Values", op)
   END SetValues;
   PROCEDURE OpenData (v: View);

      VAR t: TextModels.Model; f: TextMappers.Formatter; h: Value;
      t := TextModels.dir.New();
      h := v.model.values;
      WHILE h # NIL DO
         f.WriteInt(h.val); f.WriteLn;
         h :=
      Views.OpenAux(TextViews.dir.New(t), "Values")
   END OpenData;
   PROCEDURE DropData (t: TextModels.Model; v: View);

      VAR s: TextMappers.Scanner; first, last, h: Value;
      WHILE s.type = DO
         NEW(h); h.val :=;
         IF last = NIL THEN first := h ELSE := h END;
         last := h;
   END DropData;
   PROCEDURE (v: View) Internalize (VAR rd: Stores.Reader);

      VAR version: INTEGER; st: Stores.Store;
      rd.ReadVersion(minVersion, maxVersion, version);
      IF ~rd.cancelled THEN
         v.model := st(Model)
   END Internalize;
   PROCEDURE (v: View) CopyFromModelView (source: Views.View; model: Models.Model);

      ASSERT(model IS Model, 20);
      WITH source: View DO
         v.model := model(Model)
   END CopyFromModelView;
   PROCEDURE (v: View) Externalize (VAR wr: Stores.Writer);

   END Externalize;
   PROCEDURE (v: View) ThisModel (): Models.Model;

      RETURN v.model
   END ThisModel;
   PROCEDURE (v: View) Restore (f: Views.Frame; l, t, r, b: INTEGER);

      VAR h: Value; n: INTEGER; width, height, d, x: INTEGER;
      h := v.model.values; n := 0; WHILE h # NIL DO INC(n); h := END;   (* count values *)
      IF n > 0 THEN
         v.context.GetSize(width, height);
         d := width DIV n; x := 0;
         h := v.model.values;
         WHILE h # NIL DO
            f.DrawRect(x, height - h.val *, x + d, height, Ports.fill, Ports.grey50);
            h :=; INC(x, d)
   END Restore;
   PROCEDURE (v: View) HandleModelMsg (VAR msg: Models.Message);

      WITH msg: Models.UpdateMsg DO
         Views.Update(v, Views.keepFrames)
   END HandleModelMsg;
   PROCEDURE (v: View) HandleCtrlMsg (f: Views.Frame; VAR msg: Views.CtrlMessage;

                                                VAR focus: Views.View);
      VAR x, y, w, h: INTEGER; modifiers: SET; isDown: BOOLEAN;
      WITH msg: Controllers.TrackMsg DO
         REPEAT f.Input(x, y, modifiers, isDown) UNTIL ~isDown;
         v.context.GetSize(w, h);
         IF (x >= 0) & (y >= 0) & (x < w) & (y < h) THEN OpenData(v) END
      | msg: Controllers.PollDropMsg DO
         IF Services.Extends(msg.type, "TextViews.View") THEN
            msg.dest := f (* enable drop target feedback *)
      | msg: Controllers.DropMsg DO
         IF msg.view IS TextViews.View THEN
            DropData(msg.view(TextViews.View).ThisModel(), v)   (* interpret dropped text *)
   END HandleCtrlMsg;
   PROCEDURE (v: View) HandlePropMsg (VAR msg: Properties.Message);

      CONST min = 10 *; max = 160 *; pref = 90 *;
      WITH msg: Properties.SizePref DO   (* prevent illegal sizes *)
         IF msg.w = Views.undefined THEN msg.w := pref
         ELSIF msg.w < min THEN msg.w := min
         ELSIF msg.w > max THEN msg.w := max
         IF msg.h = Views.undefined THEN msg.h := pref
         ELSIF msg.h < min THEN msg.h := min
         ELSIF msg.h > max THEN msg.h := max
      | msg: Properties.FocusPref DO
         msg.hotFocus := TRUE
   END HandlePropMsg;
   PROCEDURE Deposit*;

      VAR m: Model; v: View;
      NEW(v); v.model := m; Stores.Join(v, m);
   END Deposit;
END ObxGraphs.