DIRECTORY Buttons USING [Button, ButtonProc, Create], Containers USING [ChildXBound, Container, Create], Convert USING [IntFromRope, ValueToRope], Graphics USING [Context, DrawBox, SetStipple], Labels USING [Create, Label, Set], Menus USING [AppendMenuEntry, CreateEntry, CreateMenu, Menu, MenuProc], MessageWindow USING [Append, Blink], Process USING [Detach, Pause, SecondsToTicks, Ticks], Rope USING [Cat, ROPE, Length], Rules USING [Create, Rule], SafeStorage USING [NarrowFault, NWordsAllocated], ShowTime USING [GetMark, Microseconds], UserExec USING [CommandProc, RegisterCommand], VFonts USING [CharWidth, StringWidth], ViewerClasses USING [PaintProc, Viewer, ViewerClass, ViewerClassRec], ViewerOps USING [CreateViewer, PaintViewer, RegisterViewerClass, SetOpenHeight], ViewerTools USING [MakeNewTextViewer, GetContents, SetSelection]; SampleTool: CEDAR PROGRAM IMPORTS Buttons, Containers, Convert, Graphics, Labels, Menus, MessageWindow, Process, Rope, Rules, SafeStorage, ShowTime, UserExec, VFonts, ViewerOps, ViewerTools = BEGIN entryHeight: CARDINAL = 15; -- how tall to make each line of items entryVSpace: CARDINAL = 8; -- vertical leading space between lines entryHSpace: CARDINAL = 10; -- horizontal space between items in a line Handle: TYPE = REF SampleToolRec; SampleToolRec: TYPE = RECORD [ -- the data for a particular tool instance outer: Containers.Container _ NIL, -- handle for the enclosing container height: CARDINAL _ 0, -- height measured from the top of the container fact: FactorialViewer, -- the factorial viewer's state graph: GraphViewer -- the bar graph viewer's state ]; MakeSampleTool: Commander.CommandProc = { my: Handle _ NEW[SampleToolRec]; myMenu: Menus.Menu _ Menus.CreateMenu[]; Menus.AppendMenuEntry[ -- add our command to the menu menu: myMenu, entry: Menus.CreateEntry[ name: "MyMenuEntry", -- name of the command proc: MyMenuProc -- proc associated with command ] ]; my.outer _ Containers.Create[[-- construct the outer container (note 3.1) name: "Sample Tool", -- name displayed in the caption iconic: TRUE, -- so tool will be iconic (small) when first created column: left, -- initially in the left column menu: myMenu, -- displaying our menu command scrollable: FALSE ]]; -- inhibit user from scrolling contents MakeFactorial[my]; -- build each (sub)viewer in turn MakeGraph[my]; ViewerOps.SetOpenHeight[my.outer, my.height]; -- hint our desired height ViewerOps.PaintViewer[my.outer, all]; -- reflect above change }; MyMenuProc: Menus.MenuProc = { MessageWindow.Append[ message: "You just invoked the sample menu item with the ", clearFirst: TRUE]; IF control THEN MessageWindow.Append[ message: "Control-", clearFirst: FALSE]; IF shift THEN MessageWindow.Append[ message: "Shift-", clearFirst: FALSE]; MessageWindow.Append[ message: SELECT mouseButton FROM red => "Red", yellow => "Yellow", ENDCASE => "Blue", clearFirst: FALSE]; MessageWindow.Append[message: " mouse button.", clearFirst: FALSE]; MessageWindow.Blink[ ]; }; FactorialViewer: TYPE = RECORD [ input: ViewerClasses.Viewer _ NIL, -- the Text Box for user input result: Labels.Label _ NIL -- result of the computation ]; MakeFactorial: PROC [handle: Handle] = { promptButton, computeButton: Buttons.Button; initialData: Rope.ROPE = "5"; initialResult: Rope.ROPE = "120"; handle.height _ handle.height + entryVSpace; -- space down from the top of the viewer promptButton _ Buttons.Create[ info: [ name: "Type a number:", wy: handle.height, -- default the width so that it will be computed for us -- wh: entryHeight, -- specify rather than defaulting so line is uniform parent: handle.outer, border: FALSE ], proc: Prompt, clientData: handle]; -- this will be passed to our button proc handle.fact.input _ ViewerTools.MakeNewTextViewer[ [ parent: handle.outer, wx: promptButton.wx + promptButton.ww + entryHSpace, wy: handle.height+2, ww: 5*VFonts.CharWidth['0], -- five digits worth of width wh: entryHeight, data: initialData, -- initial contents scrollable: FALSE, border: FALSE]]; computeButton _ Buttons.Create[ info: [ name: "Compute Factorial", wx: handle.fact.input.wx + handle.fact.input.ww + entryHSpace, wy: handle.height, ww:, -- default the width so that it will be computed for us wh: entryHeight, -- specify rather than defaulting so line is uniform parent: handle.outer, border: TRUE], clientData: handle, -- this will be passed to our button proc proc: ComputeFactorial]; handle.fact.result _ Labels.Create[ [ name: initialResult, -- initial contents wx: computeButton.wx + computeButton.ww + entryHSpace, wy: handle.height, ww: 20*VFonts.CharWidth['0], -- 20 digits worth of width wh: entryHeight, parent: handle.outer, border: FALSE]]; handle.height _ handle.height + entryHeight + entryVSpace; -- interline spacing }; Prompt: Buttons.ButtonProc = { handle: Handle _ NARROW[clientData]; -- get our data ViewerTools.SetSelection[handle.fact.input]; -- force the selection }; ComputeFactorial: Buttons.ButtonProc = { handle: Handle _ NARROW[clientData]; -- get our data contents: Rope.ROPE _ ViewerTools.GetContents[handle.fact.input]; inputNumber: INT; resultNumber: REAL _ 1.0; IF Rope.Length[contents]=0 THEN inputNumber _ 0 ELSE inputNumber _ Convert.IntFromRope[contents ! SafeStorage.NarrowFault => {inputNumber_-1; CONTINUE}]; IF inputNumber NOT IN [0..34] THEN { MessageWindow.Append[ message: "I can't compute factorial for that input", clearFirst: TRUE ]; MessageWindow.Blink[ ] } ELSE FOR n: INT IN [2..inputNumber] DO resultNumber _ resultNumber*n; ENDLOOP; Labels.Set[handle.fact.result, Convert.ValueToRope[[real[resultNumber]]]]; }; GraphViewer: TYPE = RECORD [viewer: ViewerClasses.Viewer _ NIL]; MakeGraph: PROC [handle: Handle] = { xIncr: INTEGER; -- temporarily used for labelling graph below xTab: INTEGER = 10; label: Rope.ROPE _ "1"; -- used to place labels on the graph rule: Rules.Rule _ Rules.Create[ [ -- create a bar to separate sections 1 and 2 parent: handle.outer, wy: handle.height, ww: handle.outer.cw, wh: 2]]; Containers.ChildXBound[handle.outer, rule]; -- constrain rule to be width of parent handle.height _ handle.height + entryVSpace; -- spacing after rule [ ] _ Labels.Create[[ name: "Words Allocated Per Second", parent: handle.outer, wx: xTab, wy: handle.height, border: FALSE ]]; handle.height _ handle.height + entryHeight + 2; -- interline spacing handle.graph.viewer _ CreateBarGraph[ parent: handle.outer, x: xTab, y: handle.height, w: 550, h: entryHeight, fullScale: 5.0 ]; -- orders of magnitude handle.height _ handle.height + entryHeight + 2; -- interline spacing xIncr _ handle.graph.viewer.ww/5; -- so we can space labels at equal fifths FOR i: INTEGER IN [0..5) DO -- place the labels, 1, 10, 100, 1000, 10000 along the graph [ ] _ Labels.Create[[name: label, parent: handle.outer, wx: xTab+i*xIncr - VFonts.StringWidth[label]/2, wy: handle.height, border: FALSE ]]; label _ label.Cat["0"]; -- concatenate another zero each time ENDLOOP; handle.height _ handle.height + entryHeight + entryVSpace; -- extra space at end TRUSTED {Process.Detach[FORK MeasureProcess[handle]]}; -- start the update process }; MeasureProcess: PROC [handle: Handle] = TRUSTED { updateInterval: Process.Ticks = Process.SecondsToTicks[1]; mark, nextMark: ShowTime.Microseconds; words, nextWords, deltaWords, deltaTime: REAL; mark _ ShowTime.GetMark[ ]; words _ SafeStorage.NWordsAllocated[ ]; UNTIL handle.graph.viewer.destroyed DO nextMark _ ShowTime.GetMark[ ]; deltaTime _ (nextMark - mark) * 1.0E-6; nextWords _ SafeStorage.NWordsAllocated[ ]; deltaWords _ nextWords - words; SetBarGraphValue[handle.graph.viewer, deltaWords/deltaTime]; words _ nextWords; mark _ nextMark; Process.Pause[updateInterval]; ENDLOOP; }; GraphData: TYPE = REF GraphDataRec; GraphDataRec: TYPE = RECORD [ value: REAL _ 0, -- current value being displayed (normalized) scale: REAL ]; -- "full scale" PaintGraph: ViewerClasses.PaintProc = { myGray: CARDINAL = 122645B; -- every other bit data: GraphData _ NARROW[self.data]; Graphics.SetStipple[context, myGray]; Graphics.DrawBox[context, [0, 0, data.value, self.ch]]; }; CreateBarGraph: PROC [x, y, w, h: INTEGER, parent: ViewerClasses.Viewer, fullScale: REAL] RETURNS [barGraph: ViewerClasses.Viewer] = { instanceData: GraphData _ NEW[GraphDataRec]; instanceData.scale _ fullScale; barGraph _ ViewerOps.CreateViewer[ flavor: $BarGraph, -- the class of viewer registered in the start code below info: [ parent: parent, wx: x, wy: y, ww: w, wh:h, data: instanceData, scrollable: FALSE] ]; }; SetBarGraphValue: PROC [barGraph: ViewerClasses.Viewer, newValue: REAL] = { my: GraphData _ NARROW[barGraph.data]; Log10: --Fast-- PROC [x: REAL] RETURNS [lx: REAL] = { sqrt10: REAL = 3.162278; t: REAL; lx _ 0; WHILE x > 10 DO x _ x/10; lx _ lx+1 ENDLOOP; -- scale to [1..10] IF x > sqrt10 THEN {x _ x/sqrt10; lx _ lx+0.5}; -- scale to [1..1/sqrt10] t _ (x-1)/(x+1); lx _ lx + 0.86304*t + 0.36415*(t*t*t) -- magic cubic approximation }; my.value _ Log10[1+newValue] * barGraph.cw / my.scale; ViewerOps.PaintViewer[viewer: barGraph, hint: client, clearClient: TRUE]; }; graphClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [ paint: PaintGraph ]]; Commander.Register[key: "SampleTool", proc: MakeSampleTool, doc: "Create a sample viewers tool"]; ViewerOps.RegisterViewerClass[$BarGraph, graphClass]; MakeSampleTool[NIL]; -- and create an instance END.  SampleTool.mesa; Written by Scott McGregor on June 9, 1982 9:51 am Last edited by Mitchell on August 16, 1982 4:16 pm Last edited by McGregor on March 1, 1983 10:33 am Last edited by Wyatt on November 11, 1983 12:46 pm The Containers interface is used to create an outer envelope or "container" for the different sections below. For uniformity, we define some standard distances between entries in the tool. The private data for a particular instance of the sample tool; multiple instances can be created. this procedure is called whenever the user clicks "MyMenuEntry" in the tool menu. force the selection into the user input field the bar graph reflecting collector activity Forked as a separate process. Updates the bar graph at periodic intervals. This section creates the viewer class for a logarithmic bar graph. private data structure for instances of BarChart viewers use this routine to set the bar graph to new values truncated for values of [1..inf), 3-4 good digits algorithm from Abramowitz: Handbook of Math Functions, p. 68 graphClass is a record containing the procedures and data common to all BarGraph viewer instances (class record). Register a command that will create an instance of this tool. Register the BarGraph class of viewer with the Window Manager Κ φ– "Cedar" style˜J– "Cedar" stylešœB™BJšœ2™2Jšœ1™1Jšœ2™2J˜šΟk ˜ Jšœœ˜+Jšœ œ"˜2Jšœœ˜)Jšœ œ ˜.Jšœœ˜"Jšœœ<˜GJšœœ˜$Jšœœ(˜5Jšœœœ ˜Jšœœ˜Jšœ œ ˜1Jšœ œ˜'Jšœ œ ˜.Jšœœ˜&Jšœœ2˜EJšœ œA˜PJšœ œ0˜A—J˜Jšœ œ˜Jšœœ˜£Jšœ˜J˜Jšœ½™½Jšœ œΟc&˜BJšœ œž'˜CJšœ œž+˜HJ˜Jšœa™aJšœœœ˜!šœœœž+˜IJšœœž%˜HJšœœž0˜GJšœž˜7Jšœž˜3Jšœ˜—J˜šœ)˜)Jšœ œ˜ Jšœ(˜(šœž˜5Jšœ ˜ šœ˜Jšœž˜+Jšœž˜2Jšœ˜—Jšœ˜—šœžœΟa ˜JJšœž ˜5Jšœœž4˜DJšœž˜0Jšœž˜-Jšœ œœž'˜>—Jšœž!˜5Jšœ˜Jšœ.ž˜HJšœ)ž˜@Jšœ˜J˜—JšžQ™Qšœ˜šœ˜Jšœ;˜;Jšœ œ˜—šœ œ˜%Jšœ˜Jšœ œ˜—šœœ˜#Jšœ˜Jšœ œ˜—šœ˜šœ œ ˜ Jšœ˜Jšœ˜Jšœ ˜—Jšœ œ˜—Jšœ<œ˜CJšœ˜Jšœ˜J˜—šœœœ˜ Jšœœž˜AJšœœž˜7J˜J˜—šΟn œœ˜(Jšœ,˜,Jšœœ˜Jšœœ ˜!Jšœ-ž(˜Ušœ˜šœ˜Jšœ˜Jšœ˜Jšž:˜:Jšœž4˜EJšœ˜Jšœœ˜—Jšœ ˜ Jšœž)˜>—šœ4˜4Jšœ˜Jšœ4˜4Jšœ˜Jšœž˜9Jšœ˜Jšœž˜&Jšœ œ˜Jšœœ˜—šœ˜šœ˜Jšœ˜Jšœ>˜>Jšœ˜Jšœž7˜Jšœœ˜Jšœ œ ž$˜=šœ#ž,˜OJšœ˜Jšœ˜Jšœ˜Jšœ˜—Jšœ,ž'˜SJšœ-ž˜Bšœ˜Jšœ9˜9Jšœ%œ˜.—Jšœ1ž˜Ešœ%˜%Jšœ˜Jšœ2˜2Jšœž˜(—Jšœ1ž˜EJšœ"ž)˜Kšœœœž<˜Xšœ7˜7Jšœ/˜/Jšœœ˜$—Jšœž%˜?Jšœ˜—Jšœ;ž˜PJšœœž˜RJšœ˜—J˜š œœœ˜1JšœK™KJšœ:˜:Jšœ&˜&Jšœ)œ˜.Jšœ˜Jšœ'˜'šœ˜&Jšœ˜Jšœ'˜'Jšœ+˜+Jšœ˜Jšœ<˜Jšœœž˜—J˜šœ'˜'Jšœœ ž˜.Jšœœ ˜$Jšœ%˜%Jšœ7˜7Jšœ˜—J˜š  œœœ,œœ%˜‡Jšœœ˜,Jšœ˜šœ"˜"Jšœž9˜Lšœ˜Jšœ˜Jšœ˜Jšœ˜Jšœ œ˜—Jšœ˜—Jšœ˜J˜—Jšœ3™3š œœ,œ˜KJšœœ˜&Jš  œžœœœœœ˜5Jšœ1™1šœ<™