-- File: WalnutMsgSetButtonsImpl.mesa
-- Contents: co-ordinates MsgSet button handling for WalnutControlWindow

-- created October, 1982 by Willie-Sue
-- Last edit by:
-- Willie-Sue on: September 19, 1984 2:49:40 pm PDT

DIRECTORY
Buttons USING [ButtonProc, SetDisplayStyle],
Rope,
VFonts USING [StringWidth],
ViewerOps USING [AddProp, DestroyViewer, FetchProp, GrowViewer, MoveViewer,
  OpenIcon, PaintViewer],
ViewerClasses USING [Viewer],
ViewerSpecs USING[openLeftWidth, openRightWidth],
WalnutDB USING [Entity, Msg, MsgSet, activeMsgSet, deletedMsgSet, MsgSetDomain,
     DeclareMsgSet, EqEntities, GetEntitiesInDomain, GetName],
WalnutMsgOps USING [BuildMsgSetDisplayer, MsgSetInViewer],
WalnutViewer USING [AnotherButton],
WalnutWindow USING [MsgSetButton, MsgSetButtonObject,
     msgSetBorders, walnut, walnutQueue, walnutRulerBefore, walnutRulerAfter,
     walnutTS];

WalnutMsgSetButtonsImpl: CEDAR PROGRAM
IMPORTS
Buttons,
VFonts, ViewerOps, ViewerSpecs,
WalnutDB, WalnutMsgOps, WalnutViewer, WalnutWindow
EXPORTS WalnutWindow =

BEGIN OPEN WalnutDB, WalnutWindow;

----------------------------
ROPE: TYPE = Rope.ROPE;
Viewer: TYPE = ViewerClasses.Viewer;

MsgSetButtonNotFound: SIGNAL = CODE;  -- something is wrong

selectedMsgSetButtons: PUBLIC MsgSetButton← NIL; -- List of selected MsgSets (may be empty)
lastMsgSetButton: PUBLIC MsgSetButton← NIL;   -- tail of List of MsgSets (never empty)
firstMsgSetButton: PUBLIC MsgSetButton← NIL;   -- head of List of MsgSets (never empty

SelectMsgSetProc: Buttons.ButtonProc =
-- IF red THEN deselect all & select this one; IF yellow then display;
-- IF blue THEN toggle whether selected or not
BEGIN
viewer: Viewer← NARROW[parent];
msbH: MsgSetButton← NARROW[ViewerOps.FetchProp[viewer, $msbH]];
msb, msb2: MsgSetButton;
IF mouseButton = yellow THEN {MSDisplay[msbH, shift]; RETURN};

IF mouseButton = red THEN  -- deselect any selected
{ IF (msb← selectedMsgSetButtons) # NIL THEN
{ selectedMsgSetButtons← NIL;
DO
Buttons.SetDisplayStyle[msb.selector, $BlackOnWhite];
IF (msb2← msb.selected) = NIL THEN EXIT;
msb.selected← NIL;
msb← msb2;
ENDLOOP;
}
}
ELSE -- blue button
  { FOR msb← selectedMsgSetButtons, msb.selected UNTIL msb=NIL DO
IF msb = msbH THEN {DeselectMsgSet[msbH]; RETURN};
ENDLOOP; 
  msbH.selected ← selectedMsgSetButtons;
  };
  selectedMsgSetButtons← msbH;
  Buttons.SetDisplayStyle[viewer, $WhiteOnBlack];
END;

----------------------------
ShowMsgSetButtons: PUBLIC PROC =
BEGIN
eL: LIST OF Entity← GetEntitiesInDomain[d: MsgSetDomain, alphaOrder: TRUE];
ms: Entity;
v: Viewer;

IF firstMsgSetButton # NIL THEN { UpdateMSBs[eL]; RETURN};

v← WalnutViewer.AnotherButton[q: walnutQueue, name: "Active", proc: SelectMsgSetProc,
  sib: walnutRulerBefore, border: msgSetBorders, newLine: TRUE, paint: FALSE];
firstMsgSetButton← lastMsgSetButton←
  NEW[MsgSetButtonObject← [msgSet: activeMsgSet, selector: v]];
ViewerOps.AddProp[v, $msbH, lastMsgSetButton];
AddMSButton[deletedMsgSet, "Deleted", FALSE];
FOR elT: LIST OF Entity← eL, elT.rest UNTIL elT=NIL DO
ms← elT.first;
IF ~EqEntities[ms, deletedMsgSet] AND ~EqEntities[ms, activeMsgSet] THEN
 AddMSButton[ms, GetName[ms], FALSE]; -- changes lastMsgSetButton
ENDLOOP;
END;

GetSelectedMsgSets: PUBLIC PROC RETURNS[msL: LIST OF MsgSetButton] =
BEGIN
FOR msB: MsgSetButton← selectedMsgSetButtons, msB.selected UNTIL msB=NIL DO
msL← CONS[msB, msL];
ENDLOOP;
END;

AddMsgSetButton: PUBLIC PROC[msgSet: MsgSet, msName: ROPE, select: BOOL] =
BEGIN
 AddMSButton[msgSet, msName];
IF select THEN SelectMsgSetProc[lastMsgSetButton.selector]; -- red-select new MsgSet
END;

DeleteMsgSetButton: PUBLIC PROC[msgSet: MsgSet] =
-- if msgSet is Active, just destroy viewer, but not button
BEGIN
 prevMsb: MsgSetButton← firstMsgSetButton;
FOR msb: MsgSetButton← firstMsgSetButton, msb.next UNTIL msb = NIL DO
IF EqEntities[msb.msgSet, msgSet] THEN
  { msV: Viewer← msb.msViewer;
IF msV # NIL AND ~msV.destroyed THEN ViewerOps.DestroyViewer[msV];
IF EqEntities[msgSet, activeMsgSet] THEN { msb.msViewer← NIL; RETURN};
  DeselectMsgSet[msb];
IF msb = lastMsgSetButton THEN lastMsgSetButton← prevMsb;
  prevMsb.next← msb.next;
  ViewerOps.DestroyViewer[msb.selector];
RETURN
  };
  prevMsb← msb;
ENDLOOP;
END;

FindMSViewer: PUBLIC PROC[msgSet: MsgSet] RETURNS[Viewer] =
BEGIN
FOR msb: MsgSetButton← firstMsgSetButton, msb.next UNTIL msb = NIL DO
IF EqEntities[msb.msgSet, msgSet] THEN
  {IF msb.msViewer = NIL THEN RETURN[NIL]
ELSE IF ~msb.msViewer.destroyed THEN RETURN[msb.msViewer] ELSE
  { msb.msViewer← NIL; RETURN[NIL]}};
ENDLOOP;
SIGNAL MsgSetButtonNotFound;
RETURN[NIL];
END;

DisplayMsgSet: PUBLIC PROC[msgSet: MsgSet] RETURNS[Viewer] =
{ msb: MsgSetButton;
MSDisplay[msb← FindMSBForMsgSet[msgSet], FALSE];
RETURN[msb.msViewer];
};

DestroyAllMsgSetButtons: PUBLIC PROC =
BEGIN
 selectedMsgSetButtons← NIL;
FOR msb: MsgSetButton← firstMsgSetButton, msb.next UNTIL msb=NIL DO
  ViewerOps.DestroyViewer[msb.selector, FALSE];
ENDLOOP;
 firstMsgSetButton← lastMsgSetButton← NIL;
 ViewerOps.MoveViewer[walnutRulerAfter, walnutRulerAfter.wx,
  walnutRulerBefore.wy+walnutRulerBefore.wh+14+2, walnutRulerAfter.ww,
  walnutRulerAfter.wh, FALSE];
 ViewerOps.MoveViewer[walnutTS, walnutTS.wx,
  walnutRulerAfter.wy+walnutRulerAfter.wh+1, walnutTS.ww,
  walnutTS.wh, FALSE];
 ViewerOps.PaintViewer[walnut, client];
END;

-- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

AddMSButton: PROC[msgSet: MsgSet, msName: ROPE, paint: BOOLTRUE] =
-- assumes msgSet does not already have a button; there is always a lastMsgSetButton
BEGIN
msbH: MsgSetButton;
newV: Viewer;
v: Viewer← lastMsgSetButton.selector;
x: INTEGER← v.wx + v.ww + 2;  -- x for new button
width: INT← VFonts.StringWidth[msName];
parentWidth: INTEGERIF ~walnut.iconic THEN walnut.cw
ELSE IF walnut.column = left THEN ViewerSpecs.openLeftWidth
   ELSE ViewerSpecs.openRightWidth;
newLine: BOOL← (x + width + 3) >= parentWidth;  -- will button fit on this line??
IF newLine AND (walnutTS#NIL) THEN   -- move Rule & typescript viewers
{ nH: INTEGER← v.wh + (IF msgSetBorders THEN 1 ELSE 0);
ViewerOps.MoveViewer[walnutTS, walnutTS.wx, walnutTS.wy+nH, walnutTS.ww,
  walnutTS.wh, FALSE];
ViewerOps.MoveViewer[walnutRulerAfter, walnutRulerAfter.wx,
 walnutRulerAfter.wy + nH,
 walnutRulerAfter.ww,
  walnutRulerAfter.wh, FALSE];
IF paint THEN ViewerOps.PaintViewer[walnut, client];
};
newV← WalnutViewer.AnotherButton[q: walnutQueue, name: msName,
  proc: SelectMsgSetProc,
  sib: v, border: msgSetBorders, newLine: newLine, paint: paint];
msbH← NEW[MsgSetButtonObject← [msgSet: msgSet, selector: newV]];
ViewerOps.AddProp[newV, $msbH, msbH];
lastMsgSetButton.next← msbH;
lastMsgSetButton← msbH;
END;

UpdateMSBs: PROC[eL: LIST OF Entity] =
BEGIN
 ms, ms2: Entity;
 newMS, lastNewMS: LIST OF MsgSet;
 elT: LIST OF Entity← eL;
 prevMsb: MsgSetButton;
 msb: MsgSetButton← firstMsgSetButton;

DeleteMSB: PROC[msButton: MsgSetButton] =
BEGIN
  DeselectMsgSet[msButton];
  ViewerOps.DestroyViewer[msb.selector];
END;

 msb.msgSet← DeclareMsgSet[msb.selector.name].msgSet;  -- Active
 msb← msb.next;
 msb.msgSet← DeclareMsgSet[msb.selector.name].msgSet;  -- Deleted

 prevMsb← msb;
 msb← msb.next;

DO
IF elT=NIL OR msb=NIL THEN EXIT;
  ms← elT.first;
IF EqEntities[ms, deletedMsgSet] OR EqEntities[ms, activeMsgSet] THEN
{ elT← elT.rest; LOOP};
ms2← DeclareMsgSet[msb.selector.name, OldOnly].msgSet;
IF ms2=NIL THEN  -- msgSet was destroyed
{ prevMsb.next← msb.next; DeleteMSB[msb]; msb← prevMsb.next; LOOP};
IF EqEntities[ms, ms2] THEN
{ msb.msgSet← ms; prevMsb← msb; msb← msb.next; elT← elT.rest; LOOP};
-- there was a new msgset created
IF newMS = NIL THEN newMS← lastNewMS← CONS[ms, NIL]
  ELSE lastNewMS← lastNewMS.rest← CONS[ms, NIL];
  elT← elT.rest;
ENDLOOP;

IF msb#NIL THEN   -- delete these guys (ran out of msgSets)
DO
  prevMsb.next← msb.next;
IF msb = lastMsgSetButton THEN lastMsgSetButton← prevMsb;
  DeleteMSB[msb];
IF (msb← prevMsb.next)=NIL THEN EXIT;
ENDLOOP;

IF newMS#NIL THEN   -- add these at end
FOR nms: LIST OF MsgSet← newMS, nms.rest UNTIL nms=NIL DO
  AddMSButton[nms.first, GetName[nms.first]];
ENDLOOP;

IF elT # NIL THEN   -- more to add
FOR elT← elT, elT.rest UNTIL elT=NIL DO
  AddMSButton[elT.first, GetName[elT.first]];
ENDLOOP;

END;

DeselectMsgSet: PROC[msbH: MsgSetButton] =
BEGIN
 msb: MsgSetButton← selectedMsgSetButtons;
FOR msb2: MsgSetButton← msb, msb2.selected UNTIL msb2=NIL DO
IF msb2 = msbH THEN  -- found on list so take off
  { msb.selected← msb2.selected;
  IF msbH = selectedMsgSetButtons THEN selectedMsgSetButtons← msbH.selected;
  msbH.selected← NIL;
  Buttons.SetDisplayStyle[msbH.selector, $BlackOnWhite];
  RETURN
  };
  msb← msb2;
ENDLOOP;
END;

MSDisplay: PROC[msb: MsgSetButton, shift: BOOL] =
BEGIN OPEN ViewerOps;
 msV: Viewer← msb.msViewer;
IF (msV # NIL) AND ~msV.destroyed THEN
  {IF msV.iconic THEN OpenIcon[msV, shift]
   ELSE IF shift THEN GrowViewer[msV] ELSE PaintViewer[msV, all]
  }
ELSE
  { msb.msViewer←
    WalnutMsgOps.BuildMsgSetDisplayer[msb.msgSet, msb.selector.name, shift];
  WalnutMsgOps.MsgSetInViewer[NIL, msb.msgSet, msb.msViewer]
  };
END;

FindMSBForMsgSet: PUBLIC PROC[msgSet: MsgSet] RETURNS[MsgSetButton] =
BEGIN
FOR msb: MsgSetButton← firstMsgSetButton, msb.next UNTIL msb = NIL DO
IF EqEntities[msb.msgSet, msgSet] THEN
  { IF msb.msViewer # NIL AND msb.msViewer.destroyed THEN msb.msViewer← NIL;
RETURN[msb];
  }
ENDLOOP;
RETURN[NIL];
END;

END.