MODULE ObxViews13;

   project   = "BlackBox"
   organization   = ""
   contributors   = "Oberon microsystems"
   version   = "System/Rsrc/About"
   copyright   = "System/Rsrc/About"
   license   = "Docu/BB-License"
   purpose   = "Same as ObxViews12, but generate undoable operations for character insertion"
   changes   = ""
   issues   = ""


   IMPORT Fonts, Ports, Stores, Models, Views, Controllers, Properties;

   CONST d = 20 * Ports.point;


      Model = POINTER TO RECORD (Models.Model)
         i: INTEGER;         (* position of next free slot in string *)
         s: ARRAY 256 OF CHAR   (* string *)
      View = POINTER TO RECORD (Views.View)

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

         model: Model;
         char: CHAR;
         do: BOOLEAN
   (* PasteCharOp *)

   PROCEDURE (op: PasteCharOp) Do;

      VAR m: Model; msg: Models.UpdateMsg;
      m := op.model;
      IF THEN         (* do operation's transformation *)
         m.s[m.i] := op.char; INC(m.i)   (* insert character into string *)
      ELSE                  (* undo operation's transformation *)
         DEC(m.i)            (* remove character from string *)
      m.s[m.i] := 0X; :=;      (* toggle between "do" and "undo" *)
      Models.Broadcast(m, msg)   (* update all views on this model *)
   END Do;
   PROCEDURE NewPasteCharOp (m: Model; char: CHAR): PasteCharOp;

      VAR op: PasteCharOp;
      NEW(op); op.model := m; op.char := char; := TRUE;
      RETURN op
   END NewPasteCharOp;
   (* Model *)

   PROCEDURE (m: Model) Internalize (VAR rd: Stores.Reader);

      VAR version: INTEGER;
      rd.ReadVersion(0, 0, version);
      IF ~rd.cancelled THEN
         rd.ReadInt(m.i); rd.ReadString(m.s)
   END Internalize;
   PROCEDURE (m: Model) Externalize (VAR wr: Stores.Writer);

      wr.WriteInt(m.i); wr.WriteString(m.s)
   END Externalize;
   PROCEDURE (m: Model) CopyFrom (source: Stores.Store);

      WITH source: Model DO
         m.i := source.i; m.s := source.s
   END CopyFrom;
   (* View *)

   PROCEDURE (v: View) Internalize (VAR rd: Stores.Reader);

      VAR version: INTEGER; st: Stores.Store;
      rd.ReadVersion(0, 0, version);
      IF ~rd.cancelled THEN
         v.model := st(Model)
   END Internalize;
   PROCEDURE (v: View) Externalize (VAR wr: Stores.Writer);

   END Externalize;
   PROCEDURE (v: View) CopyFromModelView (source: Views.View; model: Models.Model);

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

      f.DrawString(d, d,, v.model.s, Fonts.dir.Default())
   END Restore;
   PROCEDURE (v: View) ThisModel (): Models.Model;

      RETURN v.model
   END ThisModel;
   PROCEDURE (v: View) HandleModelMsg (VAR msg: Models.Message);

      Views.Update(v, Views.keepFrames)(* restore v in any frame that displays it *)
   END HandleModelMsg;
   PROCEDURE (v: View) HandleCtrlMsg (f: Views.Frame;

                              VAR msg: Controllers.Message;
                              VAR focus: Views.View);
      VAR op: Stores.Operation;
      WITH msg: Controllers.EditMsg DO
         IF msg.op = Controllers.pasteChar THEN
            op := NewPasteCharOp(v.model, msg.char);   (* generate operation *)
            Models.Do(v.model, "Typing", op)   (* execute operation *)
      ELSE                  (* ignore other messages *)
   END HandleCtrlMsg;
   PROCEDURE (v:View) HandlePropMsg (VAR msg: Properties.Message);

      WITH msg: Properties.SizePref DO
         IF (msg.w = Views.undefined) OR (msg.h = Views.undefined) THEN
            msg.w := 10 * d; msg.h := 2 * d
      | msg: Properties.FocusPref DO
         msg.setFocus := TRUE
   END HandlePropMsg;
   PROCEDURE Deposit*;

      VAR v: View; m: Model;
      NEW(m); m.i := 0; m.s := "";
      NEW(v); v.model := m; Stores.Join(v, m);
   END Deposit;
END ObxViews13.