DIRECTORY Atom, AtomButtons, AtomButtonsTypes, Buttons, FileNames, Imager, IO, Labels, Menus, PopUpButtons, Rope, TiogaButtons, VFonts, ViewerClasses, ViewerOps, ViewerTools; AtomButtonsImpl: CEDAR PROGRAM IMPORTS Atom, Buttons, FileNames, IO, Labels, PopUpButtons, Rope, TiogaButtons, VFonts, ViewerOps, ViewerTools EXPORTS AtomButtons = BEGIN Action: TYPE = AtomButtons.Action; ButtonLineEntry: TYPE = AtomButtons.ButtonLineEntry; UnQueuedButtonLineEntry: TYPE = AtomButtons.UnQueuedButtonLineEntry; ButtonList: TYPE = AtomButtons.ButtonList; ButtonType: TYPE = AtomButtons.ButtonType; DisplayStyle: TYPE = AtomButtons.DisplayStyle; ConfirmProc: TYPE = AtomButtonsTypes.ConfirmProc; Order: TYPE = AtomButtons.Order; HandleButtonProc: TYPE = AtomButtons.HandleButtonProc; InitButtonProc: TYPE = AtomButtonsTypes.InitButtonProc; PopUpChoice: TYPE = AtomButtons.PopUpChoice; ScalarButton: TYPE = AtomButtons.ScalarButton; ScalarButtonClient: TYPE = REF ScalarButtonClientObj; ScalarButtonClientObj: TYPE = AtomButtons.ScalarButtonClientObj; ScalarButtonHandle: TYPE = REF ScalarButtonHandleObj; ScalarButtonHandleObj: TYPE = AtomButtons.ScalarButtonHandleObj; StateType: TYPE = AtomButtons.StateType; StyleChoice: TYPE = AtomButtons.StyleChoice; TwoState: TYPE = REF TwoStateObj; TwoStateObj: TYPE = AtomButtons.TwoStateObj; UpdateProc: TYPE = AtomButtons.UpdateProc; Viewer: TYPE = ViewerClasses.Viewer; EnumTypeRef: TYPE = REF EnumTypeRec; EnumTypeRec: TYPE = AtomButtons.EnumTypeRec; NotYetImplemented: PUBLIC SIGNAL = CODE; GGButtonData: TYPE = REF GGButtonDataObj; GGButtonDataObj: TYPE = RECORD [ clientData: REF ANY, actions: LIST OF Action, handleProc: HandleButtonProc, confirmProc: ConfirmProc ]; BuildButtonLine: PUBLIC PROC [container: Viewer, x, y: NAT, clientData: REF ANY, handleProc: HandleButtonProc, entries: LIST OF ButtonLineEntry, horizontalSpace: INTEGER _ 2, lineHeight: INTEGER _ 15] RETURNS [nextX: INTEGER] = { thisButton, prevButton, firstButton: Buttons.Button; wxRelative: INTEGER _ -1; firstButton _ prevButton _ BuildButton[container, x, y, clientData, handleProc, entries.first, horizontalSpace, lineHeight]; FOR entryList: LIST OF ButtonLineEntry _ entries.rest, entryList.rest UNTIL entryList = NIL DO WITH entryList.first SELECT FROM a: ButtonLineEntry.button => wxRelative _ a.wxRelative; b: ButtonLineEntry.popUpButton => wxRelative _ b.wxRelative; c: ButtonLineEntry.label => wxRelative _ c.wxRelative; d: ButtonLineEntry.text => wxRelative _ d.wxRelative; ENDCASE => ERROR; nextX _ IF wxRelative < 0 THEN prevButton.wx + prevButton.ww + horizontalSpace ELSE wxRelative; thisButton _ BuildButton[container, nextX, y, clientData, handleProc, entryList.first, horizontalSpace, lineHeight]; prevButton _ thisButton; ENDLOOP; nextX _ prevButton.wx + prevButton.ww; }; BuildButton: PROC [container: Viewer, x, y: NAT, clientData: REF ANY, handleProc: HandleButtonProc, entry: ButtonLineEntry, horizontalSpace: INTEGER, lineHeight: INTEGER] RETURNS [button: Buttons.Button] = { WITH entry SELECT FROM a: ButtonLineEntry.button => button _ BuildOldButton[container, x, y, clientData, handleProc, a.name, a.actions, a.wxRelative, a.border, a.font, a.confirmProc, a.initProc, a.ww, lineHeight]; b: ButtonLineEntry.popUpButton => button _ BuildPopUpButton[container, x, y, clientData, handleProc, b.name, b.choices, b.wxRelative, b.border, b.font, b.confirmProc, b.initProc, b.ww, lineHeight]; c: ButtonLineEntry.label => button _ BuildLabel[container, x, y, clientData, handleProc, c.name, c.initProc, c.ww, c.wxRelative, c.border, c.font, lineHeight]; d: ButtonLineEntry.text => button _ BuildTextViewer[container, x, y, clientData, handleProc, d.name, d.initProc, d.ww, d.wxRelative, d.border, d.font, lineHeight]; ENDCASE => ERROR; }; BuildLabel: PROC [container: Viewer, x, y: NAT, clientData: REF ANY, handleProc: HandleButtonProc, name: Rope.ROPE, initProc: InitButtonProc, ww: NAT, wxRelative: INTEGER, border: BOOL, font: Imager.Font, lineHeight: INTEGER] RETURNS [button: Buttons.Button] = { button _ Labels.Create[ info: [ parent: container, wx: x, wy: y, ww: ww, wh: lineHeight, name: name, border: border]]; IF initProc # NIL THEN initProc[name, clientData, button]; }; BuildOldButton: PROC [container: Viewer, x, y: NAT, clientData: REF ANY, handleProc: HandleButtonProc, name: Rope.ROPE, actions: LIST OF Action, wxRelative: INTEGER, border: BOOL, font: Imager.Font, confirmProc: ConfirmProc, initProc: InitButtonProc, ww: NAT, lineHeight: INTEGER] RETURNS [button: Buttons.Button] = { buttonData: GGButtonData; buttonData _ NEW[GGButtonDataObj _ [clientData, actions, handleProc, confirmProc]]; button _ Buttons.Create[ info: [name: name, wx: x, wy: y, wh: lineHeight, parent: container, border: border], proc: HandleButton, clientData: buttonData, documentation: handleConfirm, fork: FALSE, guarded: confirmProc # NIL, font: font ]; IF initProc # NIL THEN initProc[name, clientData, button]; }; HandlePopUpButton: PROC [viewer: Viewer, instanceData, classData, key: REF ANY] = { action: LIST OF REF ANY _ NARROW[key]; popUpData: PopUpButtonData _ NARROW[instanceData]; popUpData.handleProc[classData, action]; }; PopUpButtonData: TYPE = REF PopUpButtonDataObj; PopUpButtonDataObj: TYPE = RECORD [ handleProc: HandleButtonProc ]; BuildPopUpButton: PROC [container: Viewer, x, y: NAT, clientData: REF ANY, handleProc: HandleButtonProc, name: Rope.ROPE, choices: LIST OF PopUpChoice, wxRelative: INTEGER, border: BOOL, font: Imager.Font, confirmProc: ConfirmProc, initProc: InitButtonProc, ww: NAT, lineHeight: INTEGER] RETURNS [button: Buttons.Button] = { popUpClassSpec: PopUpButtons.ClassSpec; popUpClass: PopUpButtons.Class; choiceList, ptr: LIST OF PopUpButtons.Choice; image: PopUpButtons.Image; instanceData: PopUpButtonData; [choiceList, ptr] _ StartChoiceList[]; FOR list: LIST OF PopUpChoice _ choices, list.rest UNTIL list = NIL DO IF list.first.actionImage = NIL THEN image _ PopUpButtons.ImageForRope[Atom.GetPName[NARROW[list.first.action.first]], NIL, list.first.font] ELSE image _ PopUpButtons.ImageForRope[list.first.actionImage, NIL, list.first.font]; [choiceList, ptr] _ AddChoice[[key: list.first.action, doc: list.first.doc, image: image], choiceList, ptr]; ENDLOOP; popUpClassSpec _ [ classData: clientData, proc: HandlePopUpButton, choices: choiceList, fork: FALSE ]; popUpClass _ PopUpButtons.MakeClass[popUpClassSpec]; instanceData _ NEW[PopUpButtonDataObj _ [handleProc]]; button _ PopUpButtons.Instantiate[ class: popUpClass, viewerInfo: [name: name, wx: x, wy: y, ww: ww, wh: lineHeight, parent: container, border: border], instanceData: instanceData, image: PopUpButtons.ImageForRope[name, NIL, font] ]; IF initProc # NIL THEN initProc[name, clientData, button]; }; BuildTextViewer: PROC [container: Viewer, x, y: NAT, clientData: REF ANY, handleProc: HandleButtonProc, name: Rope.ROPE, initProc: InitButtonProc, ww: NAT, wxRelative: INTEGER, border: BOOL, font: Imager.Font, lineHeight: INTEGER] RETURNS [button: Buttons.Button] = { button _ ViewerTools.MakeNewTextViewer[[ parent: container, wx: x, wy: y, ww: ww, wh: lineHeight, data: name, scrollable: FALSE, border: border]]; IF initProc # NIL THEN initProc[name, clientData, button]; }; StartChoiceList: PUBLIC PROC [] RETURNS [entityList, ptr: LIST OF PopUpButtons.Choice] = { ptr _ entityList _ NIL; }; AddChoice: PUBLIC PROC [entity: PopUpButtons.Choice, entityList, ptr: LIST OF PopUpButtons.Choice] RETURNS [newList, newPtr: LIST OF PopUpButtons.Choice] = { IF ptr = NIL THEN { IF NOT entityList = NIL THEN ERROR; newPtr _ newList _ CONS[entity, NIL]; RETURN; } ELSE { newList _ entityList; ptr.rest _ CONS[entity, NIL]; newPtr _ ptr.rest; }; }; HandleButton: Menus.ClickProc = { buttonData: GGButtonData _ NARROW[clientData]; IF mouseButton = red THEN buttonData.handleProc[buttonData.clientData, buttonData.actions.first] ELSE IF mouseButton = yellow THEN { IF buttonData.actions.rest # NIL AND buttonData.actions.rest.first # NIL THEN buttonData.handleProc[buttonData.clientData, buttonData.actions.rest.first] ELSE buttonData.handleProc[buttonData.clientData, buttonData.actions.first]; } ELSE { IF buttonData.actions.rest # NIL AND buttonData.actions.rest.rest # NIL AND buttonData.actions.rest.rest.first # NIL THEN buttonData.handleProc[buttonData.clientData, buttonData.actions.rest.rest.first] ELSE buttonData.handleProc[buttonData.clientData, buttonData.actions.first]; }; }; handleConfirm: REF Menus.ClickProc _ NEW[Menus.ClickProc _ HandleConfirm]; HandleConfirm: Menus.ClickProc = { buttonData: GGButtonData _ NARROW[clientData]; buttonData.confirmProc[buttonData.clientData]; }; GGUnQueuedButtonData: TYPE = REF GGUnQueuedButtonDataObj; GGUnQueuedButtonDataObj: TYPE = RECORD [ clientData: REF ANY, clickProc: Menus.ClickProc, confirmProc: ConfirmProc ]; BuildUnQueuedButtonLine: PUBLIC PROC [container: Viewer, x, y: NAT, clientData: REF ANY, entries: LIST OF UnQueuedButtonLineEntry, horizontalSpace: INTEGER _ 2, lineHeight: INTEGER _ 15] RETURNS [nextX: INTEGER] = { thisButton, prevButton: Buttons.Button; entry: UnQueuedButtonLineEntry; entry _ entries.first; prevButton _ BuildUnQueuedButton[container, x, y, clientData, entry.type, entry.clickProc, entry.name, entry.border, entry.ww, entry.updateProc, entry.confirmProc, entry.font, horizontalSpace, lineHeight]; nextX _ prevButton.wx + prevButton.ww; FOR entryList: LIST OF UnQueuedButtonLineEntry _ entries.rest, entryList.rest UNTIL entryList = NIL DO entry _ entryList.first; thisButton _ BuildUnQueuedButton[container, nextX + horizontalSpace, y, clientData, entry.type, entry.clickProc, entry.name, entry.border, entry.ww, entry.updateProc, entry.confirmProc, entry.font, horizontalSpace, lineHeight]; prevButton _ thisButton; nextX _ prevButton.wx + prevButton.ww; ENDLOOP; }; BuildUnQueuedButton: PROC [container: Viewer, x, y: NAT, clientData: REF ANY, type: ButtonType, clickProc: Menus.ClickProc, name: Rope.ROPE, border: BOOL, ww: NAT, updateProc: UpdateProc, confirmProc: ConfirmProc, font: Imager.Font, horizontalSpace: INTEGER, lineHeight: INTEGER] RETURNS [button: Buttons.Button] = { SELECT type FROM button => { buttonData: GGUnQueuedButtonData; buttonData _ NEW[GGUnQueuedButtonDataObj _ [clientData, clickProc, confirmProc]]; button _ Buttons.Create[ info: [name: name, wx: x, wy: y, wh: lineHeight, parent: container, border: border], proc: HandleUnQueuedButton, clientData: buttonData, documentation: handleUnQueuedConfirm, fork: TRUE, guarded: confirmProc # NIL, font: font ]; }; label => { button _ Labels.Create[ info: [ parent: container, name: name, wx: x, wy: y, ww: ww, wh: lineHeight, border: border]]; }; text => { button _ ViewerTools.MakeNewTextViewer[[ parent: container, wx: x, wy: y, ww: ww, wh: lineHeight, data: name, scrollable: FALSE, border: border]]; }; popUpButton => SIGNAL NotYetImplemented; -- not yet implemented ENDCASE => ERROR; IF updateProc # NIL THEN updateProc[name, clientData, button]; }; HandleUnQueuedButton: Menus.ClickProc = { buttonData: GGUnQueuedButtonData _ NARROW[clientData]; buttonData.clickProc[NIL, buttonData.clientData, mouseButton, shift, control]; }; handleUnQueuedConfirm: REF Menus.ClickProc _ NEW[Menus.ClickProc _ HandleUnQueuedConfirm]; HandleUnQueuedConfirm: Menus.ClickProc = { buttonData: GGUnQueuedButtonData _ NARROW[clientData]; buttonData.confirmProc[buttonData.clientData]; }; BuildTwoStateButton: PUBLIC PROC [viewer: ViewerClasses.Viewer, x, y: NAT, clientData: REF ANY, handleProc: HandleButtonProc, name: Rope.ROPE, border: BOOL _ TRUE, init: StateType _ off, action: LIST OF REF ANY, lineHeight: INTEGER _ 15] RETURNS [stateInfo: TwoState, nextX: INTEGER] = { tempButton: Buttons.Button; stateInfo _ NEW[TwoStateObj]; tempButton _ Buttons.Create[ info: [name: name, wx: x, wy: y, wh: lineHeight, parent: viewer, border: border], proc: TwoSwitchProc, clientData: stateInfo -- this will be passed to our button proc ]; stateInfo.button _ tempButton; stateInfo.action _ action; stateInfo.clientData _ clientData; stateInfo.handleProc _ handleProc; SetButtonState[stateInfo, init]; nextX _ tempButton.wx + tempButton.ww + 4; }; SetButtonState: PUBLIC PROC [twoState: TwoState, state: StateType] = { twoState.state _ state; SELECT state FROM on => Buttons.SetDisplayStyle[button: twoState.button, style: $WhiteOnBlack]; off => Buttons.SetDisplayStyle[button: twoState.button, style: $BlackOnWhite]; ENDCASE => ERROR; }; GetButtonState: PUBLIC PROC [twoState: TwoState] RETURNS [state: StateType] = { state _ twoState.state; }; NextState: PROC [state: StateType] RETURNS [StateType] = { IF state = LAST[StateType] THEN RETURN[FIRST[StateType]] ELSE RETURN[SUCC[state]]; }; TwoSwitchProc: Buttons.ButtonProc = { handle: TwoState _ NARROW[clientData]; handle.handleProc[handle.clientData, handle.action]; }; SwitchState: PUBLIC PROC [handle: TwoState] = { nextState: StateType; nextState _ NextState[handle.state]; SetButtonState[handle, nextState]; }; CreateScalarButtonViewer: PUBLIC PROC [container: Viewer, x, y: NAT, lineHeight: INTEGER _ 15] RETURNS [AtomButtons.ScalarButtonHandle] = { sbHandle: AtomButtons.ScalarButtonHandle _ NEW[AtomButtons.ScalarButtonHandleObj]; sbHandle.viewer _ MakeViewer[container, x, y, lineHeight]; sbHandle.headerButton _ NewHeaderButton[sbHandle.viewer, NIL]; RETURN[sbHandle]; }; BuildScalarButtons: PUBLIC PROC [handle: ScalarButtonHandle, clientData: REF ANY, handleProc: HandleButtonProc, header: Rope.ROPE, scalarButtonList: LIST OF ScalarButton] = { buttons: ScalarButtonClient; prevButton: TiogaButtons.TiogaButton; oldScalarButtons: ScalarButtonClient _ handle.scalarButtons; FOR thisButton: ScalarButtonClient _ oldScalarButtons, thisButton.next UNTIL thisButton=NIL DO TiogaButtons.DeleteButton[thisButton.button]; ENDLOOP; prevButton _ handle.headerButton; FOR list: LIST OF ScalarButton _ scalarButtonList, list.rest UNTIL list = NIL DO prevButton _ AppendScalarButton[prevButton, clientData, handleProc, list.first.name, list.first.value, list.first.init = on, list.first.action]; IF list = scalarButtonList THEN buttons _ NARROW[prevButton.clientData]; ENDLOOP; handle.scalarButtons _ buttons; handle.handleProc _ handleProc; }; StartScalarButtonList: PROC [] RETURNS [entityList, ptr: LIST OF ScalarButton] = { ptr _ entityList _ NIL; }; AddScalarButton: PROC [entity: ScalarButton, entityList, ptr: LIST OF ScalarButton] RETURNS [newList, newPtr: LIST OF ScalarButton] = { IF ptr = NIL THEN { IF NOT entityList = NIL THEN ERROR; newPtr _ newList _ CONS[entity, NIL]; RETURN; } ELSE { newList _ entityList; ptr.rest _ CONS[entity, NIL]; newPtr _ ptr.rest; }; }; AddValueSorted: PUBLIC PROC [clientData: REF ANY, scalarButtonHandle: ScalarButtonHandle, value: ScalarButton, order: Order _ incr] RETURNS [oldFoundButton: ScalarButtonClient _ NIL] = { epsilon: REAL = 0.001; list, finger: LIST OF AtomButtons.ScalarButton; inserted: BOOL _ FALSE; oldScalarButtons: ScalarButtonClient _ scalarButtonHandle.scalarButtons; [list, finger] _ StartScalarButtonList[]; FOR thisButton: ScalarButtonClient _ oldScalarButtons, thisButton.next UNTIL thisButton=NIL DO IF ABS[thisButton.value-value.value] < epsilon THEN { thisButton.on _ value.init = on; IF thisButton.on THEN TiogaButtons.ChangeButtonLooks[thisButton.button, "b", ""] ELSE TiogaButtons.ChangeButtonLooks[thisButton.button, "", "b"]; RETURN[thisButton]; }; IF NOT inserted AND order=decr AND thisButton.valuevalue.value THEN { -- insert new value behind thisButton [list, finger] _ AddScalarButton[value, list, finger]; inserted _ TRUE; }; [list, finger] _ AddScalarButton[[thisButton.name, thisButton.value, thisButton.action, IF thisButton.on THEN on ELSE off], list, finger]; ENDLOOP; IF NOT inserted THEN { -- tack new slope onto proper end of the list [list, finger] _ AddScalarButton[value, list, finger]; }; FOR thisButton: ScalarButtonClient _ oldScalarButtons, thisButton.next UNTIL thisButton=NIL DO TiogaButtons.DeleteButton[thisButton.button]; ENDLOOP; scalarButtonHandle.scalarButtons _ RebuildScalarButtons[viewer: scalarButtonHandle.viewer, headerButton: scalarButtonHandle.headerButton, clientData: clientData, handleProc: scalarButtonHandle.handleProc, scalarButtonList: list]; FOR thisButton: ScalarButtonClient _ scalarButtonHandle.scalarButtons, thisButton.next UNTIL thisButton=NIL DO IF thisButton.on THEN TiogaButtons.ChangeButtonLooks[thisButton.button, "b", ""] ELSE TiogaButtons.ChangeButtonLooks[thisButton.button, "", "b"]; ENDLOOP; }; RebuildScalarButtons: PUBLIC PROC [viewer: Viewer, headerButton: TiogaButtons.TiogaButton, clientData: REF ANY, handleProc: HandleButtonProc, scalarButtonList: LIST OF ScalarButton] RETURNS [buttons: ScalarButtonClient _ NIL] = { prevButton: TiogaButtons.TiogaButton _ headerButton; FOR list: LIST OF ScalarButton _ scalarButtonList, list.rest UNTIL list = NIL DO prevButton _ AppendScalarButton[prevButton, clientData, handleProc, list.first.name, list.first.value, list.first.init = on, list.first.action]; IF list = scalarButtonList THEN buttons _ NARROW[prevButton.clientData]; ENDLOOP; }; AppendScalarButton: PROC [prevButton: TiogaButtons.TiogaButton, clientData: REF ANY, handleProc: HandleButtonProc, name: Rope.ROPE _ NIL, value: REAL, on: BOOL, action: LIST OF REF ANY] RETURNS [button: TiogaButtons.TiogaButton] = { buttonData, prevButtonData: ScalarButtonClient; IF name = NIL THEN { space: CHAR = ' ; name _ IO.PutFR["%1.2f", [real[value]]]; name _ FileNames.Tail[name, space]; -- strip off leading spaces UNTIL Rope.Fetch[base: name, index: Rope.Length[name]-1]# '0 DO name _ Rope.Substr[base: name, start: 0, len: Rope.Length[name]-1]; ENDLOOP; }; buttonData _ NEW[ScalarButtonClientObj _ [NIL, name, value, action, on, NIL, clientData, handleProc]]; button _ TiogaButtons.AppendToButton[ button: prevButton, rope: Rope.Concat[name, " "], looks: "", proc: ToggleScalar, clientData: buttonData, fork: FALSE]; prevButtonData _ NARROW[prevButton.clientData]; IF prevButtonData # NIL THEN prevButtonData.next _ buttonData; buttonData.button _ button; }; ToggleScalar: TiogaButtons.TiogaButtonProc ~ { buttonData: ScalarButtonClient _ NARROW[clientData]; buttonData.handleProc[buttonData.clientData, buttonData.action]; }; MakeViewer: PROC [container: Viewer, x, y: NAT, lineHeight: INTEGER] RETURNS [viewer: Viewer] = { viewer _ TiogaButtons.CreateViewer[ info: [ wx: x, wy: y, ww: container.ww, wh: lineHeight, parent: container, border: FALSE] ]; container.class.set[self: container, data: viewer, op: $XBound]; }; NewHeaderButton: PROC [v: Viewer, name: Rope.ROPE] RETURNS [button: TiogaButtons.TiogaButton] = { button _ TiogaButtons.CreateButton[ viewer: v, rope: name, format: "", looks: "", proc: NIL, clientData: NIL]; }; BuildEnumTypeSelection: PUBLIC PROC [viewer: ViewerClasses.Viewer, x, y: NAT, maxWidth: NAT, clientData: REF ANY, handleProc: HandleButtonProc, title: Rope.ROPE, default: Rope.ROPE, borderOnButtons: BOOL, style: StyleChoice, allInOneRow: BOOL, buttonNames: ButtonList, atom: ATOM, horizontalSpace: INTEGER _ 2, lineHeight: INTEGER _ 15] RETURNS [EnumTypeRef] = { foundDefault: BOOL _ FALSE; stateInfo: EnumTypeRef _ NEW[EnumTypeRec]; DefaultFound: PROC[currentName, defaultName: Rope.ROPE] RETURNS [BOOL] = { IF Rope.Equal[currentName, defaultName, FALSE] THEN RETURN [foundDefault _ TRUE] ELSE RETURN [FALSE]; }; BuildMenuSelection: PROC = { border: INTEGER _ 10; startButtons: NAT; startX: NAT _ 0; -- keeps a running tally of where the next x position is tempButton: Buttons.Button; eachButton: ButtonList; stateInfo.type _ menuStyle; stateInfo.handleProc _ handleProc; IF title # NIL THEN { titleLabel: Labels.Label_ Labels.Create[[ name: title, parent: viewer, wx: x, wy: y, wh: lineHeight, border: FALSE ]]; startButtons _ titleLabel.wx + titleLabel.ww + horizontalSpace; } ELSE startButtons _ x; startX _ startButtons; FOR eachButton _ buttonNames, eachButton.rest UNTIL eachButton = NIL DO IF ~allInOneRow THEN { IF startX + VFonts.StringWidth[eachButton.first, VFonts.defaultFont] + border >= maxWidth THEN { startX _ startButtons; stateInfo.nexty _ stateInfo.nexty + lineHeight; }; }; tempButton _ Buttons.Create[info: [ name: eachButton.first, wx: startX, wy: stateInfo.nexty, wh: lineHeight, -- specify rather than defaulting so line is uniform parent: viewer, border: borderOnButtons ], proc: MenuSelectionProc, clientData: stateInfo ]; ViewerOps.AddProp[tempButton, $ChoiceButtons, stateInfo]; IF DefaultFound[eachButton.first, default] THEN { stateInfo.buttonOn _ tempButton; Buttons.SetDisplayStyle[button: tempButton, style: on]; }; startX _ tempButton.wx + tempButton.ww; ENDLOOP; stateInfo.nextx _ startX; }; BuildFlipThru: PROC = { eachButton: ButtonList; titleButton: Buttons.Button; nextx: INTEGER _ x; IF title # NIL THEN { -- create a title button stateInfo.type _ flipThruWithTitle; stateInfo.handleProc _ handleProc; titleButton _ Buttons.Create[ info: [name: title, parent: viewer, wx: nextx, wy: y, wh: lineHeight, border: TRUE ], proc: FlipThruButtonProc, clientData: stateInfo ]; nextx _ titleButton.wx + titleButton.ww + 3; } ELSE stateInfo.type _ flipThruNoTitle; FOR eachButton _ buttonNames, eachButton.rest UNTIL (eachButton = NIL) OR DefaultFound[eachButton.first, default] DO -- do nothing ENDLOOP; IF title # NIL THEN stateInfo.flipLabel _ Labels.Create[[ name: eachButton.first, wx: nextx, wy: y, ww: MaxNameWidth[buttonNames], -- first find the longest name so we can set up the wh: lineHeight, parent: viewer, border: FALSE ]] ELSE stateInfo.flipLabel _ Buttons.Create[ info: [name: eachButton.first, wx: nextx, wy: y, ww: MaxNameWidth[buttonNames], wh: lineHeight, parent: viewer, border: borderOnButtons ], proc: FlipThruButtonProc, clientData: stateInfo ]; stateInfo.nextx _ stateInfo.flipLabel.wx + stateInfo.flipLabel.ww; }; stateInfo.nextx _ x; stateInfo.nexty _ y; stateInfo.atom _ atom; stateInfo.namesOfButtons _ buttonNames; stateInfo.clientData _ clientData; IF default = NIL THEN -- set it to be the first name of the list of button names default _ buttonNames.first; SELECT style FROM menuSelection => BuildMenuSelection; flipThru => BuildFlipThru; ENDCASE => ERROR; IF ~foundDefault THEN SIGNAL DefaultDoesntExist; stateInfo.nexty _ stateInfo.nexty + lineHeight; RETURN[stateInfo]; }; -- end of BuildEnumTypeSelection FlipThruButtonProc: Buttons.ButtonProc = { info: EnumTypeRef _ NARROW[clientData]; IF mouseButton=red THEN info.handleProc[info.clientData, LIST[info.atom, $FlipForward--, info--]] ELSE info.handleProc[info.clientData, LIST[info.atom, $FlipBackward--, info--]]; }; TimeToFlipThru: PUBLIC PROC [event: LIST OF REF ANY] = { info: EnumTypeRef _ NARROW[event.rest.first]; IF event.first = $FlipForward THEN ViewerTools.SetContents[viewer: info.flipLabel, contents: GetNextName[info.namesOfButtons, info.flipLabel.name]] ELSE IF event.first = $FlipBackward THEN ViewerTools.SetContents[viewer: info.flipLabel, contents: GetPrevName[info.namesOfButtons, info.flipLabel.name]]; }; UpdateChoiceButtons: PUBLIC PROC [viewer: ViewerClasses.Viewer, enumTypeInfo: EnumTypeRef, newName: Rope.ROPE] = { newButton: Buttons.Button _ NIL; GetDesiredButton: ViewerOps.EnumProc = { IF (Rope.Equal[v.name, newName, FALSE]) AND (ViewerOps.FetchProp[v, $ChoiceButtons] = enumTypeInfo) THEN { newButton _ v; RETURN [FALSE]; }; }; IF enumTypeInfo = NIL THEN SIGNAL ButtonsCannotBeUpdated; SELECT enumTypeInfo.type FROM menuStyle => {ViewerOps.EnumerateChildren[viewer, GetDesiredButton]; IF newButton # NIL THEN { SwitchButtons[enumTypeInfo.buttonOn, newButton]; enumTypeInfo.buttonOn _ newButton; } ELSE SIGNAL ChoiceDoesntExist; }; flipThruWithTitle => Labels.Set[label: enumTypeInfo.flipLabel, value: newName]; flipThruNoTitle => Buttons.ReLabel[button: enumTypeInfo.flipLabel, newName: newName]; ENDCASE => ERROR; }; DefaultDoesntExist: PUBLIC SIGNAL = CODE; ChoiceDoesntExist: PUBLIC SIGNAL = CODE; ButtonsCannotBeUpdated: PUBLIC SIGNAL = CODE; MaxNameWidth: PROC[listOfNames: ButtonList] RETURNS [CARDINAL] = { eachButton: ButtonList _ listOfNames; maxWidth: CARDINAL; IF eachButton # NIL THEN { maxWidth _ VFonts.StringWidth[string: eachButton.first, font: VFonts.defaultFont]; eachButton _ eachButton.rest } ELSE RETURN [0]; WHILE (eachButton # NIL) DO IF VFonts.StringWidth[string: eachButton.first, font: VFonts.defaultFont] > maxWidth THEN maxWidth _ VFonts.StringWidth[string: eachButton.first, font: VFonts.defaultFont]; eachButton _ eachButton.rest; ENDLOOP; maxWidth _ maxWidth + VFonts.CharWidth['M]; -- add on a little extra just to be sure RETURN[maxWidth]; }; MenuSelectionProc: Buttons.ButtonProc = { info: EnumTypeRef _ NARROW[clientData]; viewer: ViewerClasses.Viewer _ NARROW[parent]; SwitchButtons[info.buttonOn, viewer]; -- viewer is the button which was just selected info.buttonOn _ viewer; }; GetNextName: PROC[listOfNames: ButtonList, currentName: Rope.ROPE] RETURNS [Rope.ROPE] = { eachButton: ButtonList _ listOfNames; WHILE (eachButton # NIL) AND (eachButton.first # currentName) DO eachButton _ eachButton.rest; ENDLOOP; IF eachButton = NIL THEN ERROR; -- This shouldn't happen; we should always be able to IF eachButton.rest = NIL THEN RETURN [listOfNames.first] ELSE RETURN [(eachButton.rest).first]; }; GetPrevName: PROC[listOfNames: ButtonList, currentName: Rope.ROPE] RETURNS [Rope.ROPE] = { prev: Rope.ROPE _ listOfNames.first; eachName: ButtonList _ listOfNames.rest; WHILE (eachName # NIL) AND ~Rope.Equal[eachName.first, currentName, FALSE] DO prev _ eachName.first; eachName _ eachName.rest; ENDLOOP; RETURN[prev]; }; on: ATOM = $WhiteOnBlack; off: ATOM = $BlackOnWhite; SwitchButtons: PROC[oldButton, newButton: Buttons.Button] = { Buttons.SetDisplayStyle[button: oldButton, style: off]; Buttons.SetDisplayStyle[button: newButton, style: on]; }; END. AtomButtonsImpl.mesa Last edited by Bier on April 30, 1987 7:38:35 pm PDT. Contents: General Purpose routines for use by Gargoyle. Pier, December 12, 1986 2:36:03 pm PST Button Lines [parent: REF ANY, clientData: REF ANY _ NIL, mouseButton: Menus.MouseButton _ red, shift: BOOL _ FALSE, control: BOOL _ FALSE] buttonData.handleProc might put the action on the slack queue. UnQueued Buttons Lines An unqueued button calls the clickProc indicated by the client. It forks a process. Like BuildButton except that action: LIST OF REF ANY is replaced with clickProc: Menus.ClickProc. This only affects buttons (not labels or text viewers). Two State Buttons Scalar Buttons A line of Tioga Buttons, but built for the new User Interface Architecture (see comment below). Makes a TiogaButtons viewer and fills it with buttons. Each button names the action atoms to give to the slack process when the button is pressed. The rest is somewhat convoluted. The client must figure out which button has been pressed, update his data structures and call an appropriate procedure to change the appearance of the button, if desired. Hence, the client must have a data structure which points to the appropriate button. This procedure builds such a data structure. clear out the existing buttons, which TiogaOps doesn't seem able to do !! add the new buttons Adds a new scalar button to the list, sort in increasing order or decreasing order as requested. If it is already there, it is not added. Turns on the new button. search the current list for duplicate OR for proper insertion point, build new list along the way Now clear out the existing buttons, which TiogaOps doesn't seem able to do !! this routine builds a new set of Tioga buttons into the given viewer the client must have cleaned out old buttons before calling herein. creates a new button and appends it to the viewer in which prevButton exists. DOES NOT APPEND TO prevButton, as one might hope !! IF Rope.Equal[Rope.Substr[name, Rope.Length[name]-5, 5], ".0000"] THEN name _ Rope.Substr[name, 0, Rope.Length[name]-5]; strip off trailing zeroes prevButtonData will be NIL after the Header button. Enumerated Types Buttons (like Choice Buttons, but built for the new User Interface Architecture (queues atoms onto the slack process queue and waits for them to return before acting. Thus, the buttons can be "pushed" from the TIP Table as well as with the mouse. default the width so that it will be computed for us now set up the initial button width properly (unfortunately when re-labeling a button, the width remains the same) signal that the default didn't exist if necessary Display the next name in succession Notify client that change has taken place if the client provided a proc for doing this IF info.proc # NIL THEN info.proc[viewer.name, info.clientdata]; find the current button name in list of button names we are at the end of our list, cycle around to the beginning merely changes the physical appearance of the buttons Switch off the previously selected button switch "on" the newly selected button ÊÔ˜Icode™K™5™8K™&—šÏk ˜ JšœAœa˜¤—K˜šœœ˜KšœœJ˜nKšœ˜—Kš˜˜Kšœœ˜"Kšœœ˜4Kšœœ'˜DKšœ œ˜*Kšœ œ˜*Kšœœ˜.Kšœ œ ˜1Kšœœ˜ Kšœœ ˜6Kšœœ#˜7Kšœ œ˜,Kšœœ˜.Kšœœœ˜5Kšœœ%˜@Kšœœœ˜5Kšœœ%˜@Kšœ œ˜(Kšœ œ˜,Kšœ œœ ˜!Kšœ œ˜,Kšœ œ˜*Kšœœ˜$Kšœ œœ ˜$Kšœ œ˜,KšÏnœœœœ˜(—K˜K™ K™Kšœœœ˜)šœœœ˜ Kšœ œœ˜Kšœ œœ˜Kšœ˜Kšœ˜K˜—šžœœœœœœ)œœ#œœœ œ˜åKšœ4˜4Kšœ œ˜K˜Kšœ|˜|š œ œœ0œ œ˜^šœœ˜ Kšœ7˜7Kšœ<˜˜>Kšœ#˜#—Kšœ˜Kšœ'œ˜1K˜—Kšœ œœ$˜:K˜K˜—šžœœœœœ+œ œœ œ!œœ˜‹šœ(˜(Kšœ˜Kšœ%˜%Kšœ ˜ Kšœ œ˜$—Kšœ œœ$˜:K˜K˜—š žœœœœœœ˜ZKšœœ˜K˜—šž œœœ0œœœœœ˜šœœœ˜Kš œœœœœ˜#Kšœœ œ˜%Kšœ˜K˜—šœ˜Kšœ˜Kšœ œ œ˜Kšœ˜K˜—K˜—šž œ˜!Kšœ œœœœœ/œœ œœ™~Kšœœ ˜.KšœœG˜`šœœœ˜#Kšœœœ!˜HKšœL˜PKšœH˜LK˜—šœ˜Kš œœœ œœ&˜tKšœQ˜UKšœH˜LK˜—KšÏb>™>K˜K˜—šŸ œœœ"˜JK˜—šž œ˜"Kšœœ ˜.Kšœ.˜.K˜K˜—K˜K™K™Kšœœœ˜9šœœœ˜(Kšœ œœ˜Kšœ˜Kšœ˜K˜K˜—šžœœœœœœ œœ+œœœ œ˜×Kšœ'˜'Kšœ˜K˜Kšœ˜KšœÍ˜ÍKšœ&˜&š œ œœ8œ œ˜fKšœ˜Kšœã˜ãKšœ˜Kšœ&˜&—Kšœ˜Kšœ˜K˜—K˜šžœœœœœ;œ œœXœœœ˜¼K™TKš œ%œœœœf™šKšœ˜˜ K˜!Kšœ œA˜Qšœ˜šœ0˜0Kšœ#˜#—KšœŸœ˜Kšœ˜KšœŸœ˜%Kšœœ˜ Kšœœ˜Kšœ ˜ Kšœ˜—K˜—˜ šœ˜šœ˜Kšœ˜Kšœ ˜ Kšœ%˜%Kšœ˜——K˜—˜ šœ(˜(Kšœ˜Kšœ%˜%Kšœ ˜ Kšœ œ˜$—K˜—KšœœÏc˜?Kšœœ˜Kšœœœ&˜>K˜K˜—šžœ˜)Kšœ#œ ˜6Kšœœ6˜NK˜K˜—KšŸœœœ*˜Zšžœ˜*Kšœ#œ ˜6Kšœ.˜.K˜K˜—K˜K™K˜š žœœœ&œœœ+œ œœ!œœœœœœœ˜ŸK˜Kšœ œ˜K˜šœ0˜0Kšœ ˜ K˜Kšœ )˜?K˜—K˜Kšœ˜Kšœ"˜"Kšœ"˜"Kšœ ˜ K˜*K˜K˜—šžœœœ+˜FKšœ˜šœœ˜KšœM˜MKšœN˜NKšœœ˜—Kšœ˜K˜—šžœœœ4˜OKšœ˜Kšœ˜K˜K˜—šž œœœ˜:Kš œ œ œœœ ˜8Kšœœœ ˜Kšœ˜K˜—šž œ˜%Kšœœ ˜&Kšœ4˜4Kšœ˜K˜—šž œœœ˜/Kšœ˜Kšœ$˜$Kšœ"˜"K˜K˜—K˜K™K™K™_K™åK™š žœœœœœœ%˜‹Kšœ+œ$˜RKšœ;˜;Kšœ9œ˜>Kšœ ˜K˜K˜—šžœœœ*œœ-œœœ˜®Kšœ˜Kšœ%˜%šœ<˜Kšœ3™3—Kšœ˜K˜K˜—šŸ œ"˜.Kšœ!œ ˜4Kšœ@˜@K˜—K˜š ž œœœœœ˜ašœ#˜#šœ˜Kšœ/˜/Kšœ˜Kšœœ˜—Kšœ˜—Kšœ@˜@K˜K˜—šžœœœœ'˜aKšœ#˜#Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœ ˜ Kšœœ˜ Kšœ œ˜K˜K˜—K˜K™K™K™ïK˜šžœœœ&œ œ œœ,œœœ#œ!œœœœ˜êKšœœœ˜Kšœœ˜*š ž œœ œœœ˜Jš œ&œœœœ˜QKšœœœ˜—Kšœ˜—šžœœ˜Kšœœ˜Kšœœ˜Kšœœ 8˜IK˜K˜K˜K˜"šœ œœ˜˜*K˜ K˜K˜Kšœ˜ K˜—Kšœ?˜?K˜—Kšœ˜K˜˜šœ+œœ˜Gšœœ˜KšœD˜Fšœœ˜K˜K˜/K˜—K˜—˜#K˜K˜ K˜Kšœ4™4Kšœ 4˜DK˜K˜K˜KšœÐbvœ˜K˜K˜—K˜9šœ)œ˜1K˜ K˜7K˜—K˜'Kšœ˜—K˜Kšœ˜——šž œœ˜K˜K˜Kšœœ˜šœ œœ ˜.K˜#K˜"˜˜K˜K˜ K˜K˜Kšœ˜ K˜—KšœŸœ˜K˜K˜—K˜,K˜—Kšœ"˜&šœ+œœ˜IKšœ(œ  ˜8Kšœ˜—Kšœ™šœ œœ&˜9K˜K˜ K˜Kšœ 3˜RKšœT™TK˜K˜Kšœ˜ K˜—šœ&˜*˜K˜ K˜K˜K˜K˜K˜K˜—KšœŸœ˜K˜K˜—K˜BKšœ˜—K˜K˜K˜K˜'K˜"šœ œœ :˜PK˜—K˜šœ˜KšœŸœ˜$Kšœ Ÿ œ˜Kšœœ˜—K˜Kšœ1™1Kšœœœ˜0K˜K˜0Kšœ ˜Kšœ  ˜#K˜—šžœ˜*Kšœœ ˜'šœ˜Kšœœ%˜J—Kšœ œ&˜PKšœ˜K˜—šžœœœ œœœœ˜8Kšœœ˜-šœ˜"Kšœ#™#˜9K˜6——šœœ˜(˜9K˜7——K˜K˜—šžœœœIœ˜rKšœœ˜ šœ(˜(šœœœ˜,Kšœ8œ˜>K˜Kšœœ˜K˜—Kšœ˜—Kšœœœœ˜9šœ˜˜Ešœ œœ˜K˜0K˜"K˜—Kšœœ˜K˜—K˜PK˜VKšœœ˜—Kšœ˜K˜—K˜Kšœœœœ˜)Kšœœœœ˜(Kšœœœœ˜-K˜šž œœœœ˜BK˜%Kšœ œ˜šœœœ˜K˜RK˜—Kšœœ˜šœœ˜šœS˜YK˜S—K˜Kšœ˜—Kšœ, (˜TKšœ ˜Kšœ˜K˜—šžœ˜)Kšœœ ˜'Kšœœ ˜.Kšœ& /˜UK˜KšœV™VKšœ œœ)™@Kšœ˜K˜—šž œœ-œ˜KKšœœ˜K˜%šœœœ"œ˜AK˜Kšœ˜—š œœœœ 5˜UKšœ4™4—šœœœ˜Kšœ<™