MODULE StdHeaders;

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


(* headers / footers support the following macros:

         &p - replaced by current page number as arabic numeral

         &r - replaced by current page number as roman numeral
         &R - replaced by current page number as capital roman numeral
         &a - replaced by current page number as alphanumeric character
         &A - replaced by current page number as capital alphanumeric character
         &d - replaced by printing date
         &t - replaced by printing time
         &&- replaced by & character
         &; - specifies split point
         &f - filename with path/title


      Stores, Ports, Models, Views, Properties, Printing, TextModels, Fonts, Dialog,
      TextViews, Dates, Windows, Controllers, Containers;

      minVersion = 0; maxVersion = 2;
      mm =; point = Ports.point;
      maxWidth = 10000 * mm;
      alternate* = 0; number* = 1; head* = 2; foot* = 3; showFoot* = 4;

      Banner* = RECORD
         left*, right*: ARRAY 128 OF CHAR;
         gap*: INTEGER
      NumberInfo* = RECORD
         new*: BOOLEAN;
         first*: INTEGER
      View = POINTER TO RECORD (Views.View)
         alternate: BOOLEAN;   (* alternate left/right *)
         number: NumberInfo;   (* new page number *)
         head, foot: Banner;
         font: Fonts.Font;
         showFoot: BOOLEAN;
      Prop* = POINTER TO RECORD (Properties.Property)

         alternate*, showFoot*: BOOLEAN;
         number*: NumberInfo;
         head*, foot*: Banner
      ChangeFontOp = POINTER TO RECORD (Stores.Operation)

         header: View;
         font: Fonts.Font
      ChangeAttrOp = POINTER TO RECORD (Stores.Operation)

         header: View;
         alternate, showFoot: BOOLEAN;
         number: NumberInfo;
         head, foot: Banner

      dialog*: RECORD
         view: View;
         alternate*, showFoot*: BOOLEAN;
         number*: NumberInfo;
         head*, foot*: Banner;
   PROCEDURE (p: Prop) IntersectWith* (q: Properties.Property; OUT equal: BOOLEAN);
      VAR valid: SET;
      PROCEDURE Equal(IN b1, b2: Banner): BOOLEAN;
         RETURN (b1.left = b2.left) & (b1.right = b2.right) & ( =
      END Equal;
      WITH q: Prop DO
         valid := p.valid * q.valid; equal := TRUE;
         IF p.alternate # q.alternate THEN EXCL(valid, alternate) END;
         IF p.showFoot # q.showFoot THEN EXCL(valid, showFoot) END;
         IF ( # OR (p.number.first # q.number.first) THEN EXCL(valid, number) END;
         IF ~Equal(p.head, q.head) THEN EXCL(valid, head) END;
         IF ~Equal(p.foot, q.foot) THEN EXCL(valid, foot) END;
         IF p.valid # valid THEN p.valid := valid; equal := FALSE END
   END IntersectWith;
   (* SetAttrOp *)
   PROCEDURE (op: ChangeFontOp) Do;

      VAR v: View; font: Fonts.Font; asc, dsc, w: INTEGER; c: Models.Context;
      v := op.header;
      font := op.font; op.font := v.font; v.font := font;
      font.GetBounds(asc, dsc, w);
      c := v.context;
      c.SetSize(maxWidth, asc + dsc + 2*point);
      Views.Update(v, Views.keepFrames)
   END Do;
   PROCEDURE DoChangeFontOp (v: View; font: Fonts.Font);

      VAR op: ChangeFontOp;
      IF v.font # font THEN
         NEW(op); op.header := v; op.font := font;
         Views.Do(v,"#System:SetProp", op)
   END DoChangeFontOp;
   PROCEDURE (op: ChangeAttrOp) Do;

      VAR v: View; alternate, showFoot: BOOLEAN; number: NumberInfo; head, foot: Banner;
      v := op.header;
      alternate := op.alternate; showFoot := op.showFoot; number := op.number; head := op.head; foot := op.foot;
      op.alternate := v.alternate; op.showFoot := v.showFoot; op.number := v.number; op.head := v.head;
      op.foot := v.foot;
      v.alternate := alternate; v.showFoot := showFoot; v.number := number; v.head := head; v.foot := foot;
      Views.Update(v, Views.keepFrames)
   END Do;
   PROCEDURE DoChangeAttrOp (v: View; alternate, showFoot: BOOLEAN; number: NumberInfo;

                                       head, foot: Banner);
      VAR op: ChangeAttrOp;
      NEW(op); op.header := v; op.alternate := alternate; op.showFoot := showFoot;
      op.number := number; op.head := head; op.foot := foot;
      Views.Do(v,"#Std:HeaderChange", op)
   END DoChangeAttrOp;
   PROCEDURE (v: View) CopyFromSimpleView (source: Views.View);

      WITH source: View DO
         v.alternate := source.alternate; :=; v.number.first := source.number.first;
         v.head := source.head;
         v.foot := source.foot;
         v.font := source.font;
         v.showFoot := source.showFoot
   END CopyFromSimpleView;
   PROCEDURE (v: View) Externalize (VAR wr: Stores.Writer);

   END Externalize;
   PROCEDURE (v: View) Internalize (VAR rd: Stores.Reader);
      VAR version: INTEGER; typeface: Fonts.Typeface; size: INTEGER; style: SET; weight: INTEGER;

      IF ~rd.cancelled THEN
         rd.ReadVersion(minVersion, maxVersion, version);
         IF ~rd.cancelled THEN
            IF version = 0 THEN
      := 5*mm;
      := 5*mm;
               v.font := Fonts.dir.This(typeface, size * point, {}, Fonts.normal);
               v.font := Fonts.dir.This(typeface, size, style, weight);
               IF version = 2 THEN rd.ReadBool(v.showFoot) ELSE v.showFoot := FALSE END
   END Internalize;
   PROCEDURE SetProp(v: View; msg: Properties.SetMsg);

      VAR p: Properties.Property;
         typeface: Fonts.Typeface; size: INTEGER; style: SET; weight: INTEGER;
         alt, sf: BOOLEAN; num: NumberInfo; h, f: Banner;
      p := msg.prop;
      WHILE p # NIL DO
         WITH p: Properties.StdProp DO
            IF Properties.typeface IN p.valid THEN typeface := p.typeface
            ELSE typeface := v.font.typeface
            IF Properties.size IN p.valid THEN size := p.size
            ELSE size := v.font.size
            IF IN p.valid THEN style :=
            ELSE style :=
            IF Properties.weight IN p.valid THEN weight := p.weight
            ELSE weight := v.font.weight
            DoChangeFontOp (v, Fonts.dir.This(typeface, size, style, weight) );
         | p: Prop DO
            IF alternate IN p.valid THEN alt := p.alternate ELSE alt := v.alternate END;
            IF showFoot IN p.valid THEN sf := p.showFoot ELSE sf := v.showFoot END;
            IF number IN p.valid THEN num := p.number ELSE num := v.number END;
            IF head IN p.valid THEN h := p.head ELSE h := v.head END;
            IF foot IN p.valid THEN f := p.foot ELSE f := v.foot END;
            DoChangeAttrOp(v, alt, sf, num, h, f)
         p :=
   END SetProp;
   PROCEDURE PollProp(v: View; VAR msg: Properties.PollMsg);
      VAR sp: Properties.StdProp; p: Prop;
      sp.known := {Properties.size, Properties.typeface,, Properties.weight};
      sp.valid := sp.known;
      sp.size := v.font.size; sp.typeface := v.font.typeface; :=; := {Fonts.italic, Fonts.underline, Fonts.strikeout};
      sp.weight := v.font.weight;
      Properties.Insert(msg.prop, sp);
      p.known := {alternate, number, head, foot, showFoot}; p.valid := p.known;
      p.head := v.head; p.foot := v.foot;
      p.alternate := v.alternate;
      p.showFoot := v.showFoot;
      p.number := v.number;
      Properties.Insert(msg.prop, p)
   END PollProp;
   PROCEDURE PageMsg(v: View; msg: TextViews.PageMsg);
      IF Printing.par # NIL THEN
         Dialog.MapString(v.head.left, Printing.par.header.left);
         Dialog.MapString(v.head.right, Printing.par.header.right);
         Dialog.MapString(v.foot.left, Printing.par.footer.left);
         Dialog.MapString(v.foot.right, Printing.par.footer.right);
         Printing.par.header.font := v.font;
         Printing.par.footer.font := v.font; := v.alternate;
         IF THEN
   := v.number.first - msg.current
         END; := 5*; := 5*
   END PageMsg;
   PROCEDURE (v: View) Restore (f: Views.Frame; l, t, r, b: INTEGER);

      VAR d, w, h: INTEGER; (*line: Line; *)asc, dsc, x0, x1, y: INTEGER;
         win: Windows.Window; title: Views.Title; dec: BOOLEAN;
         pw, ph: INTEGER;
         date: Dates.Date; time: Dates.Time; pageInfo: Printing.PageInfo; banner: Printing.Banner;
      IF Views.IsPrinterFrame(f) THEN (* am drucken *) END;
      v.font.GetBounds(asc, dsc, w);

      win := Windows.dir.First();

      WHILE (win # NIL) & (win.doc.Domain() # v.Domain()) DO win := Windows.dir.Next(win) END;
      IF win = NIL THEN title := "(" + Dialog.appName + ")"
      ELSE win.GetTitle(title)
      d :=;
      v.context.GetSize(w, h);
      win.doc.PollPage(pw, ph, l, t, r, b, dec);
      w := r - l;
      f.DrawRect(0, 0, w, h, Ports.fill, Ports.grey25);

      f.DrawRect(0, 0, w, h, 0,;
      x0 := d; x1 := w-2*d; y := asc + d;

      pageInfo.alternate := FALSE;
      pageInfo.title := title;
      banner.font := v.font;      
      IF v.showFoot THEN :=;
         Dialog.MapString(v.foot.left, banner.left); Dialog.MapString(v.foot.right, banner.right)
      ELSE :=;
         Dialog.MapString(v.head.left, banner.left); Dialog.MapString(v.head.right, banner.right)
      Printing.PrintBanner(f, pageInfo, banner, date, time, x0, x1, y)
   END Restore;
   PROCEDURE (v: View) HandlePropMsg (VAR msg: Properties.Message);

      VAR asc, dsc, w: INTEGER;
      WITH msg: Properties.SizePref DO
         msg.w := maxWidth;
         IF msg.h = Views.undefined THEN
            v.font.GetBounds(asc, dsc, w);
            msg.h := asc + dsc + 2*point
      | msg: Properties.ResizePref DO
         msg.fixed := TRUE
      | msg: TextModels.Pref DO
         msg.opts := {TextModels.hideable}
      | msg: Properties.PollMsg DO
         PollProp(v, msg)
      | msg: Properties.SetMsg DO
         SetProp(v, msg)
      | msg: TextViews.PageMsg DO
         PageMsg(v, msg)
   END HandlePropMsg;
   PROCEDURE (v: View) HandleCtrlMsg (f: Views.Frame; VAR msg: Controllers.Message;
                                                VAR focus: Views.View);
      WITH msg: Properties.EmitMsg DO Views.HandlePropMsg(v, msg.set)
      | msg: Properties.CollectMsg DO Views.HandlePropMsg(v, msg.poll)
   END HandleCtrlMsg;
   PROCEDURE New*(p: Prop; f: Fonts.Font): Views.View;
      VAR v: View;
      v.head := p.head;
      v.foot := p.foot;
      v.number := p.number;
      v.alternate := p.alternate;
      v.font := f;
      v.showFoot := FALSE;
      RETURN v;   
   END New;
   PROCEDURE Deposit*;

      VAR v: View;
      v.head.left := ""; v.head.right :="&d&;&p"; := 5*mm;
      v.foot.left := ""; v.foot.right := ""; := 5*mm;
      v.font := Fonts.dir.Default();
      v.number.first := 1; := FALSE; v.alternate := FALSE; v.showFoot := FALSE;
   END Deposit;
   (* property dialog *)
   PROCEDURE InitDialog*;
      VARp: Properties.Property;
      WHILE p # NIL DO
         WITH p: Properties.StdProp DO
         | p: Prop DO
            dialog.alternate := p.alternate; dialog.showFoot := p.showFoot;
            dialog.number := p.number;
            dialog.head := p.head; := DIV point;
            dialog.foot := p.foot; := DIV point;
         p :=
   END InitDialog;
      VAR p: Prop;
      NEW(p); p.valid := {alternate, number, head, foot, showFoot};
      p.alternate := dialog.alternate; p.showFoot := dialog.showFoot;
      p.number := dialog.number;
      p.head := dialog.head; := * point;
      p.foot := dialog.foot; := * point;
      Properties.EmitProp(NIL, p)
   END Set;
   PROCEDURE HeaderGuard* (VAR par: Dialog.Par);
      VAR v: Views.View;
      v := Containers.FocusSingleton();
      IF (v # NIL) & (v IS View) THEN
         par.disabled := FALSE;
         IF (dialog.view = NIL) OR (dialog.view # v) THEN
            dialog.view := v(View);
         par.disabled := TRUE;
         dialog.view := NIL
   END HeaderGuard;
   PROCEDURE AlternateGuard* (VAR par: Dialog.Par);
      IF ~par.disabled THEN par.disabled := ~ dialog.alternate END
   END AlternateGuard;
   PROCEDURE NewNumberGuard* (VAR par: Dialog.Par);
      IF ~par.disabled THEN par.disabled := ~ END
   END NewNumberGuard;
END StdHeaders.