<> <> <> <> <> <> <<>> DIRECTORY Angles2d, AtomButtons, AtomButtonsTypes, Basics, BiScrollers, BiScrollersTransforms, BiScrollersTransformsTypes, ColorTool, EmbeddedButtons, Feedback, FileNames, FS, Geom2D, GGAlignTypes, GGBasicTypes, GGBoundBox, GGCircleCache, GGControlPanelTypes, GGCoreOps, GGCoreTypes, GGDragTypes, GGEmbedTypes, GGFont, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGProps, GGRefresh, GGRefreshTypes, GGScene, GGSceneType, GGSessionLog, GGState, GGStateExtras, GGStateTypes, GGUIUtility, GGUserInput, GGUserProfile, GGViewerOps, GGWindow, Icons, Imager, ImagerTransformation, InputFocus, IO, Real, RealFns, Rope, TiogaMenuOps, TIPUser, Vector2, ViewerClasses, ViewerOps, ViewerTools; GGStateImpl: CEDAR PROGRAM IMPORTS Angles2d, AtomButtons, Basics, BiScrollers, BiScrollersTransforms, ColorTool, EmbeddedButtons, Feedback, FileNames, FS, Geom2D, GGBoundBox, GGCoreOps, GGHistory, GGProps, GGRefresh, GGScene, GGSessionLog, GGUIUtility, GGUserInput, GGUserProfile, GGViewerOps, GGWindow, ImagerTransformation, InputFocus, IO, Real, RealFns, Rope, TiogaMenuOps, TIPUser, ViewerOps, ViewerTools EXPORTS GGState, GGStateExtras, GGHistoryTypes, GGInterfaceTypes, GGModelTypes = BEGIN Change: PUBLIC TYPE = GGHistory.Change; -- exported to GGHistoryTypes ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes EmbedDataObj: PUBLIC TYPE = GGEmbedTypes.EmbedDataObj; -- exported to GGInterfaceTypes StateDataObj: PUBLIC TYPE = GGStateTypes.StateDataObj; -- exported to GGInterfaceTypes DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj; -- exported to GGInterfaceTypes RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; -- exported to GGInterfaceTypes Axis: TYPE = BiScrollersTransformsTypes.Axis; AgeOp: TYPE = BiScrollersTransformsTypes.AgeOp; BiScroller: TYPE = BiScrollers.BiScroller; BoundBox: TYPE = GGCoreTypes.BoundBox; Camera: TYPE = GGModelTypes.Camera; Caret: TYPE = GGInterfaceTypes.Caret; DefaultData: TYPE = GGModelTypes.DefaultData; DisplayStyle: TYPE = GGModelTypes.DisplayStyle; ExtendMode: TYPE = GGModelTypes.ExtendMode; FeatureCycler: TYPE = GGInterfaceTypes.FeatureCycler; FilterLists: TYPE = GGAlignTypes.FilterLists; FilterListsObj: TYPE = GGAlignTypes.FilterListsObj; FontData: TYPE = GGFont.FontData; GGData: TYPE = GGInterfaceTypes.GGData; GravityType: TYPE = GGInterfaceTypes.GravityType; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; Location: TYPE = BiScrollersTransformsTypes.Location; PreservationPair: TYPE = BiScrollersTransformsTypes.PreservationPair; Rect: TYPE = Geom2D.Rect; ROPE: TYPE = Rope.ROPE; RopeListt: TYPE = GGCoreTypes.RopeListt; ScalarButton: TYPE = AtomButtons.ScalarButton; SceneObj: PUBLIC TYPE = GGSceneType.SceneObj; -- export of opaque type SortedButtonClient: TYPE = AtomButtonsTypes.SortedButtonClient; SortedButtonHandle: TYPE = AtomButtonsTypes.SortedButtonHandle; SelectMode: TYPE = GGModelTypes.SelectMode; SequenceOfReal: TYPE = GGCoreTypes.SequenceOfReal; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; StrokeJoint: TYPE = Imager.StrokeJoint; StrokeEnd: TYPE = Imager.StrokeEnd; Transform: TYPE = ImagerTransformation.Transformation; Transformation: TYPE = ImagerTransformation.Transformation; Vec: TYPE = Vector2.VEC; Vector: TYPE = GGBasicTypes.Vector; Viewer: TYPE = ViewerClasses.Viewer; ViewportProc: TYPE = GGStateExtras.ViewportProc; reallyBigReal: REAL = 1.0e37; RegisterViewportProc: PUBLIC PROC [ggData: GGData, proc: ViewportProc, clientData: REF ¬ NIL] = { ggData.embed.viewportProc ¬ proc; ggData.embed.viewportData ¬ clientData; }; <<>> DefaultViewport: PUBLIC ViewportProc = { <> <> IF ggData.controls.biScroller = NIL THEN RETURN[[0,0,100,100]] <> ELSE { actionArea: Viewer ¬ ggData.controls.actionArea; rect ¬ [0.0, 0.0, actionArea.cw, actionArea.ch]; }; }; <<>> NotNewVersion: PUBLIC PROC [ggData: GGData, op: ATOM] = { <> IF ggData.controls = NIL OR ggData.controls.panel = NIL OR ggData.controls.topper = NIL THEN RETURN; ggData.controls.panel.newVersion ¬ FALSE; -- guard the destroy button. KAP. June 1, 1988 ggData.controls.topper.newVersion ¬ FALSE; ggData.controls.topper.icon ¬ SELECT op FROM $clear => GGWindow.GetIcons[].noNameIcon, $clean => GGWindow.GetIcons[].cleanIcon, ENDCASE => ERROR; ViewerOps.PaintViewer[ggData.controls.topper, caption]; IF ggData.controls.topper#ggData.controls.panel THEN ViewerOps.PaintViewer[ggData.controls.panel, caption]; }; ChangeIcon: PUBLIC PROC [ggData: GGData, op: ATOM] = { <> IF ggData.controls = NIL OR ggData.controls.topper = NIL THEN RETURN; ggData.controls.topper.icon ¬ SELECT op FROM $clear => GGWindow.GetIcons[].noNameIcon, $clean => GGWindow.GetIcons[].cleanIcon, ENDCASE => ERROR; ViewerOps.PaintViewer[ggData.controls.topper, caption]; }; <<>> ColorToolIsBound: PUBLIC PROC [ggData: GGData] RETURNS [BOOL ¬ FALSE] ~ { IF Basics.IsBound[LOOPHOLE[ColorTool.GetColor]] THEN RETURN[TRUE]; Feedback.Append[ggData.router, oneLiner, $Complaint, "ColorTool operation failed: please install ColorTool and retry"]; }; GetViewport: PUBLIC PROC [ggData: GGData] RETURNS [rect: Imager.Rectangle] = { <> IF ggData.embed.viewportProc = NIL THEN RETURN [DefaultViewport[ggData]] ELSE RETURN [ggData.embed.viewportProc[ggData, ggData.embed.viewportData]]; }; GetGGInputFocus: PUBLIC PROC RETURNS [focusData: GGData ¬ NIL] = { theirViewer: Viewer; inputFocus: InputFocus.Focus ¬ InputFocus.GetInputFocus[]; IF inputFocus = NIL OR inputFocus.owner = NIL THEN RETURN; theirViewer ¬ inputFocus.owner; IF theirViewer.class.flavor=$ActionArea THEN { biscroller: BiScroller ¬ NARROW[theirViewer.data]; focusData ¬ NARROW[BiScrollers.ClientDataOf[biscroller]]; } ELSE IF inputFocus.info # NIL THEN { WITH inputFocus.info SELECT FROM ggData: GGData => focusData ¬ ggData; ENDCASE => focusData ¬ NIL; }; }; GetAnchor: PUBLIC PROC [ggData: GGData] RETURNS [Caret] = { RETURN[ggData.anchor]; }; identity: Transformation = ImagerTransformation.Scale[1.0]; GetBiScrollersTransforms: PUBLIC PROC [ggData: GGData] RETURNS [clientToViewer, viewerToClient: Transformation] = { IF ggData.controls.biScroller = NIL THEN RETURN[ggData.controlState.clientToViewer, ggData.controlState.viewerToClient] ELSE [clientToViewer, viewerToClient] ¬ BiScrollers.GetStyle[].GetTransforms[ggData.controls.biScroller]; }; GetBiScrollersTransform: PUBLIC PROC [ggData: GGData] RETURNS [clientToViewer: Transformation] = { bs: BiScroller = ggData.controls.biScroller; IF bs = NIL THEN RETURN[ggData.controlState.clientToViewer] ELSE clientToViewer ¬ bs.style.GetTransforms[bs].clientToViewer; }; GetBiScrollersScale: PUBLIC PROC [ggData: GGData] RETURNS [s: REAL] = { <> clientToViewer: Transformation ¬ GetBiScrollersTransform[ggData]; s ¬ ImagerTransformation.SingularValues[clientToViewer].x; }; BiScrollersConcat: PUBLIC PROC [ggData: GGData, t: Transformation, paint: BOOL _ TRUE, ageOp: AgeOp _ remember] = { bs: BiScroller = ggData.controls.biScroller; old: Transform ~ GetBiScrollersTransforms[ggData].clientToViewer; new: Transform ~ old.Concat[t]; IF bs = NIL THEN { ChangeTransform[ggData, new, ignore]; } ELSE { bs.style.ChangeTransform[bs: bs, new: new, ageOp: IF ageOp = ignore THEN ignore ELSE remember, paint: FALSE]; IF paint THEN GGWindow.RestoreScreenAndInvariants[paintAction: $ViewersPaintEntireScene, ggData: ggData, remake: none, edited: FALSE, selectionChanged: FALSE, okToSkipCapture: TRUE]; -- explicit call to avoid clearing to white }; }; SetBiScrollersTransform: PUBLIC PROC [ggData: GGData, clientToViewer: Transformation, paint: BOOL _ TRUE, ageOp: AgeOp _ remember] = { bs: BiScroller = ggData.controls.biScroller; IF bs = NIL THEN { ChangeTransform[ggData, clientToViewer, ageOp]; } ELSE { bs.style.ChangeTransform[bs: bs, new: clientToViewer, ageOp: IF ageOp = ignore THEN ignore ELSE remember, paint: FALSE]; IF paint THEN GGWindow.RestoreScreenAndInvariants[paintAction: $ViewersPaintEntireScene, ggData: ggData, remake: none, edited: FALSE, selectionChanged: FALSE, okToSkipCapture: TRUE]; -- explicit call to avoid clearing to white }; }; SGN: PROC [r: REAL] RETURNS [sgn: [-1 .. 1]] = { sgn ¬ SELECT r FROM <0 => -1, =0 => 0, >0 => 1, ENDCASE => ERROR}; ConstantVector: PROC [ggData: GGData] RETURNS [cv: Vector2.VEC] = { <> <> cv ¬ [100, 100]; -- for now }; ZeroProtect: PROC [r: REAL] RETURNS [r0: REAL] = {r0 ¬ IF r = 0.0 THEN 1.0 ELSE r}; ScaleOp: TYPE = BiScrollersTransformsTypes.ScaleOp; BiScrollersScale: PUBLIC PROC [ggData: GGData, op: ScaleOp] = { bs: BiScroller = GetBiScroller[ggData]; old: Transformation = GetBiScrollersTransform[ggData]; new: Transformation; viewport: Imager.Rectangle ¬ GetViewport[ggData]; cw: REAL _ viewport.w; ch: REAL _ viewport.h; pp: BiScrollers.PreservationPair; vanilla: Transformation; mayStretch: BOOL ¬ TRUE; IF bs=NIL THEN { pp _ [X: 0.5, Y: 0.5]; -- as specified in GGWindowImpl.Init vanilla _ ImagerTransformation.Translate[[(cw-paperWidth)/2.0, (ch-paperHeight)/2.0]]; <> mayStretch _ TRUE; } ELSE { pp ¬ bs.class.common.preserve; vanilla ¬ bs.class.common.vanilla[bs]; mayStretch ¬ bs.class.common.mayStretch; }; new ¬ BiScrollersTransforms.Scale[old, op, cw, ch, pp, vanilla, mayStretch]; SetBiScrollersTransform[ggData, new, FALSE]; }; paperHeight: REAL = 11.0*72.0; paperWidth: REAL = 8.5*72.0; BiScrollersRotate: PUBLIC PROC [ggData: GGData, op: GGState.RotateOp] = { bs: BiScroller = GetBiScroller[ggData]; old: Transformation = GetBiScrollersTransform[ggData]; new: Transformation; viewport: Imager.Rectangle ¬ GetViewport[ggData]; cw: REAL _ viewport.w; ch: REAL _ viewport.h; pp: PreservationPair; vanilla: Transformation; IF bs=NIL THEN { pp _ [X: 0.5, Y: 0.5]; -- as specified in GGWindowImpl.Init vanilla _ ImagerTransformation.Translate[[(cw-paperWidth)/2.0, (ch-paperHeight)/2.0]]; <> } ELSE { pp ¬ bs.class.common.preserve; vanilla ¬ bs.class.common.vanilla[bs]; }; new ¬ BiScrollersTransforms.Rotate[old, op, cw, ch, pp, vanilla]; SetBiScrollersTransform[ggData, new, FALSE]; }; BiScrollersShift: PUBLIC PROC [ggData: GGData, dx, dy: REAL] = { bs: BiScroller = GetBiScroller[ggData]; old: Transformation = GetBiScrollersTransform[ggData]; new: Transformation; <> <> <> <<}>> <> new ¬ BiScrollersTransforms.Shift[old, dx, dy]; SetBiScrollersTransform[ggData, new, FALSE]; }; BiScrollersAlign: PUBLIC PROC [ggData: GGData, client, viewer: Location, doX, doY: BOOL _ TRUE, ageOp: AgeOp ¬ remember] = { bs: BiScroller = GetBiScroller[ggData]; new: Transformation; old: Transformation = GetBiScrollersTransform[ggData]; viewport: Imager.Rectangle ¬ GetViewport[ggData]; cw: REAL _ viewport.w; ch: REAL _ viewport.h; new ¬ BiScrollersTransforms.Align[old, client, viewer, doX, doY, cw, ch, ClientExtremaProc, ggData, ignore]; SetBiScrollersTransform[ggData, new, FALSE]; }; ClientExtremaProc: BiScrollersTransforms.ExtremaProc = { <> ggData: GGData ¬ NARROW[clientData]; RETURN GGExtremaProc[ggData, direction]; }; GGExtremaProc: PROC [ggData: GGData, direction: Vec] RETURNS [min, max: Vec] = { <> area: Geom2D.Rect; bigBox: BoundBox ¬ GGBoundBox.BoundBoxOfBoxes[GGScene.BoundBoxesInScene[ggData.scene].list]; area ¬ IF bigBox#NIL THEN [x: bigBox.loX, y: bigBox.loY, w: bigBox.hiX-bigBox.loX, h: bigBox.hiY-bigBox.loY] ELSE [0.0, 0.0, 1.0, 1.0]; [min, max] ¬ Geom2D.ExtremaOfRect[r: area, n: direction]; }; BiScrollersBoxScale: PUBLIC PROC [ggData: GGData, from, to: Rect --both in viewer coords--, uniformly: BOOL ¬ TRUE] = { new: Transformation; old: Transformation = GetBiScrollersTransform[ggData]; mayStretch: BOOL ¬ TRUE; preferIntegerCoefficients: BOOL ¬ FALSE; new ¬ BiScrollersTransforms.BoxScale[old, from, to, uniformly, mayStretch, preferIntegerCoefficients]; SetBiScrollersTransform[ggData, new, FALSE]; }; DoBSUserAction: PUBLIC PROC [ggData: GGData, event: LIST OF REF] = { bs: BiScroller = GetBiScroller[ggData]; new: Transformation; old: Transformation = GetBiScrollersTransform[ggData]; viewport: Imager.Rectangle ¬ GetViewport[ggData]; cw: REAL _ viewport.w; ch: REAL _ viewport.h; pp: BiScrollers.PreservationPair; vanilla: Transformation; mayStretch: BOOL ¬ TRUE; preferIntegerCoefficients: BOOL ¬ FALSE; IF bs=NIL THEN { pp _ [X: 0.5, Y: 0.5]; vanilla _ ImagerTransformation.Translate[[(cw-paperWidth)/2.0, (ch-paperHeight)/2.0]]; } ELSE { pp ¬ bs.class.common.preserve; vanilla ¬ bs.class.common.vanilla[bs]; }; new ¬ BiScrollersTransforms.DoBSUserAction[old, event, cw, ch, pp, vanilla, ggData.controlState.clientToViewerPrevious, mayStretch, preferIntegerCoefficients, ClientExtremaProc, ggData]; SetBiScrollersTransform[ggData, new, FALSE]; }; ViewLimitsOfImage: PROC [t: Transform, min, max: Vec, axis: Axis] RETURNS [vmin, vmax: REAL] = { min ¬ t.Transform[min]; max ¬ t.Transform[max]; SELECT axis FROM X => {vmin ¬ min.x; vmax ¬ max.x}; Y => {vmin ¬ min.y; vmax ¬ max.y}; ENDCASE => ERROR; }; Norm: PUBLIC PROC [t: Transform, axis: Axis] RETURNS [norm: Vec] = { tn: Geom2D.Trans ¬ Geom2D.ToTrans[t]; SELECT axis FROM X => norm ¬ [tn.dxdx, tn.dxdy]; Y => norm ¬ [tn.dydx, tn.dydy]; ENDCASE => ERROR; }; BiScrollersPreviousTransform: PUBLIC PROC [ggData: GGData] = { SetBiScrollersTransform[ggData, ggData.controlState.clientToViewerPrevious, FALSE, remember]; }; ChangeTransform: PROC [ggData: GGData, new: Transform, ageOp: AgeOp] = { <> IF new.a*new.e - new.b*new.d = 0 THEN RETURN; -- change is degenerate. Don't do it SELECT ageOp FROM remember => { ggData.controlState.viewerToClientPrevious ¬ ggData.controlState.viewerToClient; ggData.controlState.clientToViewerPrevious ¬ ggData.controlState.clientToViewer; }; ignore => NULL; ENDCASE => ERROR; ggData.controlState.clientToViewer ¬ new; ggData.controlState.viewerToClient ¬ new.Invert[]; }; <<>> <> <<>> GetFilterLists: PUBLIC PROC [ggData: GGData] RETURNS [filterLists: FilterLists] = { AddSlopeFilter: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL ¬ FALSE] = { IF state THEN { real: REAL ¬ NARROW[value, REF REAL]­; filterLists.onSlopes ¬ CONS[Angles2d.Normalize[real], filterLists.onSlopes]; }; }; AddRadiusFilter: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL ¬ FALSE] = { IF state THEN { real: REAL ¬ NARROW[value, REF REAL]­; filterLists.onRadii ¬ CONS[real, filterLists.onRadii]; }; }; AddAngleFilter: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL ¬ FALSE] = { IF state THEN { real: REAL ¬ NARROW[value, REF REAL]­; filterLists.onAngles ¬ CONS[real, filterLists.onAngles]; }; }; AddDistanceFilter: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL ¬ FALSE] = { IF state THEN { real: REAL ¬ NARROW[value, REF REAL]­; filterLists.onDistances ¬ CONS[real, filterLists.onDistances]; }; }; filterLists ¬ NEW[FilterListsObj ¬ [NIL, NIL, NIL, NIL]]; IF ggData.controls.slopeHandle#NIL THEN AtomButtons.ReadSortedButtons[ggData.controls.slopeHandle, AddSlopeFilter]; IF ggData.controls.radiusHandle#NIL THEN AtomButtons.ReadSortedButtons[ggData.controls.radiusHandle, AddRadiusFilter]; IF ggData.controls.distanceHandle#NIL THEN AtomButtons.ReadSortedButtons[ggData.controls.distanceHandle, AddDistanceFilter]; IF ggData.controls.angleHandle#NIL THEN AtomButtons.ReadSortedButtons[ggData.controls.angleHandle, AddAngleFilter]; filterLists.scaleUnit ¬ ggData.hitTest.scaleUnit; }; AlignmentType: TYPE = GGState.AlignmentType; SetAllAlignmentStates: PUBLIC PROC [ggData: GGData, type: AlignmentType, state: BOOL] = { SELECT type FROM slope => IF ggData.controls.slopeHandle # NIL THEN AtomButtons.SetAllScalarStates[ggData, ggData.controls.slopeHandle, state]; angle => IF ggData.controls.angleHandle # NIL THEN AtomButtons.SetAllScalarStates[ggData, ggData.controls.angleHandle, state]; radius => IF ggData.controls.radiusHandle # NIL THEN AtomButtons.SetAllScalarStates[ggData, ggData.controls.radiusHandle, state]; lineDistance => IF ggData.controls.distanceHandle # NIL THEN AtomButtons.SetAllScalarStates[ggData, ggData.controls.distanceHandle, state]; ENDCASE; }; DeleteSelectedAlignments: PUBLIC PROC [ggData: GGData, type: AlignmentType] = { DeleteSelected: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [found: BOOL, done: BOOL ¬ FALSE] = { found ¬ state; }; handle: SortedButtonHandle ¬ GetSortedButtonHandle[ggData, type]; AtomButtons.DeleteSortedButtons[ggData, handle, DeleteSelected]; }; GetSortedButtonHandle: PROC [ggData: GGData, type: AlignmentType] RETURNS [handle: SortedButtonHandle] = { handle ¬ SELECT type FROM slope => ggData.controls.slopeHandle, angle => ggData.controls.angleHandle, radius => ggData.controls.radiusHandle, lineDistance => ggData.controls.distanceHandle, ENDCASE => NIL; }; ToggleAlignment: PUBLIC PROC [ggData: GGData, alignVal: REAL, type: AlignmentType] RETURNS [menuValue: REAL ¬ 777.0, changedToOn: BOOL ¬ FALSE] = { FindAndToggleValue: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [newState: BOOL, newName: Rope.ROPE ¬ NIL, newValue: REF ANY, done: BOOL ¬ FALSE] = { real: REAL ¬ NARROW[value, REF REAL]­; epsilon: REAL = 0.001; IF ABS[real-alignVal] < epsilon THEN { newState ¬ NOT state; changedToOn ¬ newState; menuValue ¬ real; done ¬ TRUE; } ELSE newState ¬ state; newValue ¬ value; }; handle: SortedButtonHandle ¬ GetSortedButtonHandle[ggData, type]; AtomButtons.WriteSortedButtons[handle, FindAndToggleValue]; }; GetSlopeAlignments: PUBLIC PROC [ggData: GGData] RETURNS [values: LIST OF REAL, on: LIST OF BOOL] = { <> AddSlope: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL ¬ FALSE] = { real: REAL ¬ NARROW[value, REF REAL]­; [values, valuePtr] ¬ GGCoreOps.AddReal[real, values, valuePtr]; [on, boolPtr] ¬ GGCoreOps.AddBool[state, on, boolPtr]; }; valuePtr: LIST OF REAL; boolPtr: LIST OF BOOL; [values, valuePtr] ¬ GGCoreOps.StartRealList[]; [on, boolPtr] ¬ GGCoreOps.StartBoolList[]; IF ggData.controls.slopeHandle # NIL THEN AtomButtons.ReadSortedButtons[ggData.controls.slopeHandle, AddSlope]; }; GetSlopeValue: PUBLIC PROC [ggData: GGData] RETURNS [degrees: REAL, success: BOOL ¬ TRUE] = { IF ggData.controls.slopeView = NIL THEN RETURN[0.0, FALSE]; degrees ¬ GGViewerOps.GetReal[ggData.controls.slopeView, Real.LargestNumber]; IF degrees = Real.LargestNumber THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "Attempt to use illegal slope value"]; degrees ¬ 0.0; success ¬ FALSE; RETURN; }; degrees ¬ Angles2d.Normalize[degrees]; <> IF degrees<0.0 THEN degrees ¬ degrees+180.0; IF degrees=360.0 THEN degrees ¬ 0.0; <> IF RealFns.AlmostEqual[degrees, ggData.measure.slopeViewValue, -10] THEN degrees ¬ ggData.measure.slopeViewValue ELSE ggData.measure.slopeViewValue ¬ degrees; }; SetSlopeValue: PUBLIC PROC [ggData: GGData, degrees: REAL] = { ggData.measure.slopeViewValue ¬ degrees; IF ggData.controls.slopeView # NIL THEN GGViewerOps.SetReal[ggData.controls.slopeView, degrees, "%6.5f"]; }; SelectSlope: PUBLIC PROC [ggData: GGData] RETURNS [success: BOOL ¬ TRUE] = { IF ggData.controls.slopeView=NIL THEN success ¬ FALSE ELSE ViewerTools.SetSelection[ggData.controls.slopeView]; }; AddSlope: PUBLIC PROC [ggData: GGData, degrees: REAL, on: BOOL ¬ TRUE] RETURNS [alreadyThere: BOOL] = { oldFoundButton: AtomButtons.SortedButtonClient; IF ggData.controls.slopeHandle = NIL THEN RETURN[TRUE]; oldFoundButton ¬ AtomButtons.AddScalarSorted[ggData, ggData.controls.slopeHandle, [NIL, degrees, LIST[LIST[$ToggleSlope, NEW[REAL ¬ degrees]]], on], decr]; alreadyThere ¬ oldFoundButton # NIL; }; ButtonsFromValues: PROC [atom: ATOM, names: LIST OF Rope.ROPE ¬ NIL, values: LIST OF REAL, on: LIST OF BOOL ¬ NIL] RETURNS [buttonList: LIST OF ScalarButton] = { ptr: LIST OF ScalarButton; name: Rope.ROPE; active: BOOL ¬ FALSE; [buttonList, ptr] ¬ GGCoreOps.StartScalarButtonList[]; FOR values ¬ values, values.rest UNTIL values = NIL DO name ¬ IF names = NIL THEN ScalarToRope[values.first] ELSE names.first; <> active ¬ IF on = NIL THEN FALSE ELSE on.first; [buttonList, ptr] ¬ GGCoreOps.AddScalarButton[[name, values.first, LIST[LIST[atom, NEW[REAL ¬ values.first]]], active], buttonList, ptr]; IF names # NIL THEN names ¬ names.rest; IF on # NIL THEN on ¬ on.rest; ENDLOOP; }; AddSlopeList: PUBLIC PROC [ggData: GGData, degrees: LIST OF REAL, on: LIST OF BOOL ¬ NIL] = { buttonList, oldList: LIST OF ScalarButton; oldValues: LIST OF REAL; oldOn: LIST OF BOOL; IF ggData.controls.slopeHandle = NIL THEN RETURN; buttonList ¬ ButtonsFromValues[$ToggleSlope, NIL, degrees, on]; [oldValues, oldOn] ¬ GetSlopeAlignments[ggData]; oldList ¬ ButtonsFromValues[$ToggleSlope, NIL, oldValues, oldOn]; buttonList ¬ MergeScalarButtonLists[buttonList, oldList, FALSE]; AtomButtons.BuildScalarButtons[ggData.controls.slopeHandle, ggData, GGUserInput.EventNotify, NIL, buttonList]; }; NewSlopeList: PUBLIC PROC [ggData: GGData, degrees: LIST OF REAL, on: LIST OF BOOL ¬ NIL] = { buttonList: LIST OF ScalarButton; IF ggData.controls.slopeHandle = NIL THEN RETURN; buttonList ¬ ButtonsFromValues[$ToggleSlope, NIL, degrees, on]; AtomButtons.BuildScalarButtons[ggData.controls.slopeHandle, ggData, GGUserInput.EventNotify, NIL, buttonList]; }; MergeScalarButtonLists: PROC [list1, list2: LIST OF ScalarButton, ascending: BOOL ¬ TRUE] RETURNS [mergedList: LIST OF ScalarButton ¬ NIL] = { <> ptr: LIST OF ScalarButton ¬ NIL; done: BOOL ¬ FALSE; list1Is: Basics.Comparison; DO [list1Is, done] ¬ CompareValues[list1, list2, ascending]; IF done THEN RETURN; SELECT list1Is FROM less => [list1, mergedList, ptr] ¬ SpliceToList[list1, mergedList, ptr]; greater => [list2, mergedList, ptr] ¬ SpliceToList[list2, mergedList, ptr]; equal => { state: BOOL ¬ list1.first.on OR list2.first.on; IF list1.first.name # NIL THEN { [list1, mergedList, ptr] ¬ SpliceToList[list1, mergedList, ptr]; list2 ¬ list2.rest; } ELSE { [list2, mergedList, ptr] ¬ SpliceToList[list2, mergedList, ptr]; list1 ¬ list1.rest; }; ptr.first.on ¬ state; }; ENDCASE => ERROR; IF ptr.first.name = NIL THEN ptr.first.name ¬ ScalarToRope[ptr.first.value]; ENDLOOP; }; CompareValues: PROC [list1, list2: LIST OF ScalarButton, ascending: BOOL ¬ TRUE] RETURNS [list1Is: Basics.Comparison, done: BOOL ¬ FALSE] = { epsilon: REAL = 0.001; IF list1 = NIL THEN { IF list2 = NIL THEN {done ¬ TRUE; list1Is ¬ less; RETURN} ELSE {list1Is ¬ greater; RETURN} }; IF list2 = NIL THEN {list1Is ¬ less; RETURN}; IF ascending THEN { IF list1.first.value + epsilon < list2.first.value THEN list1Is ¬ less ELSE IF list1.first.value - epsilon > list2.first.value THEN list1Is ¬ greater ELSE list1Is ¬ equal; } ELSE { IF list1.first.value + epsilon < list2.first.value THEN list1Is ¬ greater ELSE IF list1.first.value - epsilon > list2.first.value THEN list1Is ¬ less ELSE list1Is ¬ equal; }; }; SpliceToList: PROC [list, mergedList, ptr: LIST OF ScalarButton] RETURNS [newList, newMergedList, newPtr: LIST OF ScalarButton] = { <> newList ¬ list.rest; -- walk over the bridge list.rest ¬ NIL; -- burn the bridge IF mergedList = NIL THEN {newMergedList ¬ list; newPtr ¬ list; RETURN}; newMergedList ¬ mergedList; ptr.rest ¬ list; -- put the new element on the end newPtr ¬ list; -- update the tail pointer }; GetAngleAlignments: PUBLIC PROC [ggData: GGData] RETURNS [values: LIST OF REAL, on: LIST OF BOOL] = { AddAngle: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL ¬ FALSE] = { real: REAL ¬ NARROW[value, REF REAL]­; [values, valuePtr] ¬ GGCoreOps.AddReal[real, values, valuePtr]; [on, boolPtr] ¬ GGCoreOps.AddBool[state, on, boolPtr]; }; valuePtr: LIST OF REAL; boolPtr: LIST OF BOOL; [values, valuePtr] ¬ GGCoreOps.StartRealList[]; [on, boolPtr] ¬ GGCoreOps.StartBoolList[]; IF ggData.controls.angleHandle # NIL THEN AtomButtons.ReadSortedButtons[ggData.controls.angleHandle, AddAngle]; }; GetAngleValue: PUBLIC PROC [ggData: GGData] RETURNS [degrees: REAL, success: BOOL ¬ TRUE] = { IF ggData.controls.angleView = NIL THEN RETURN[0.0, FALSE]; degrees ¬ GGViewerOps.GetReal[ggData.controls.angleView, Real.LargestNumber]; IF degrees>reallyBigReal THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "Attempt to use illegal angle value"]; degrees ¬ 0.0; success ¬ FALSE; RETURN; }; degrees ¬ Angles2d.Normalize[degrees]; IF degrees=360.0 THEN degrees ¬ 0.0; <> IF RealFns.AlmostEqual[degrees, ggData.measure.angleViewValue, -10] THEN degrees ¬ ggData.measure.angleViewValue ELSE ggData.measure.angleViewValue ¬ degrees; }; SetAngleValue: PUBLIC PROC [ggData: GGData, degrees: REAL] = { ggData.measure.angleViewValue ¬ degrees; IF ggData.controls.angleView # NIL THEN GGViewerOps.SetReal[ggData.controls.angleView, degrees, "%6.5f"]; }; SelectAngle: PUBLIC PROC [ggData: GGData] RETURNS [success: BOOL ¬ TRUE] = { IF ggData.controls.angleView=NIL THEN success ¬ FALSE ELSE ViewerTools.SetSelection[ggData.controls.angleView]; }; AddAngle: PUBLIC PROC [ggData: GGData, degrees: REAL, on: BOOL ¬ TRUE] RETURNS [alreadyThere: BOOL] = { oldFoundButton: AtomButtons.SortedButtonClient; IF ggData.controls.angleHandle = NIL THEN RETURN[TRUE]; oldFoundButton ¬ AtomButtons.AddScalarSorted[clientData: ggData, handle: ggData.controls.angleHandle, button: [NIL, degrees, LIST[LIST[$ToggleAngle, NEW[REAL ¬ degrees]]], on], order: decr]; alreadyThere ¬ oldFoundButton # NIL; }; AddAngleList: PUBLIC PROC [ggData: GGData, degrees: LIST OF REAL, on: LIST OF BOOL ¬ NIL] = { buttonList, oldList: LIST OF ScalarButton; oldValues: LIST OF REAL; oldOn: LIST OF BOOL; IF ggData.controls.angleHandle = NIL THEN RETURN; buttonList ¬ ButtonsFromValues[$ToggleAngle, NIL, degrees, on]; [oldValues, oldOn] ¬ GetAngleAlignments[ggData]; oldList ¬ ButtonsFromValues[$ToggleAngle, NIL, oldValues, oldOn]; buttonList ¬ MergeScalarButtonLists[buttonList, oldList, FALSE]; AtomButtons.BuildScalarButtons[ggData.controls.angleHandle, ggData, GGUserInput.EventNotify, NIL, buttonList]; }; NewAngleList: PUBLIC PROC [ggData: GGData, degrees: LIST OF REAL, on: LIST OF BOOL ¬ NIL] = { buttonList: LIST OF ScalarButton; IF ggData.controls.angleHandle = NIL THEN RETURN; buttonList ¬ ButtonsFromValues[$ToggleAngle, NIL, degrees, on]; AtomButtons.BuildScalarButtons[ggData.controls.angleHandle, ggData, GGUserInput.EventNotify, NIL, buttonList]; }; GetRadiusAlignments: PUBLIC PROC [ggData: GGData] RETURNS [names: LIST OF Rope.ROPE, values: LIST OF REAL, on: LIST OF BOOL] = { <> AddRadius: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL ¬ FALSE] = { real: REAL ¬ NARROW[value, REF REAL]­; GGCoreOps.AppendRope[name, nameListt]; [values, valuePtr] ¬ GGCoreOps.AddReal[real, values, valuePtr]; [on, boolPtr] ¬ GGCoreOps.AddBool[state, on, boolPtr]; }; nameListt: RopeListt; valuePtr: LIST OF REAL; boolPtr: LIST OF BOOL; nameListt ¬ GGCoreOps.NewRopeListt[]; [values, valuePtr] ¬ GGCoreOps.StartRealList[]; [on, boolPtr] ¬ GGCoreOps.StartBoolList[]; IF ggData.controls.radiusHandle # NIL THEN AtomButtons.ReadSortedButtons[ggData.controls.radiusHandle, AddRadius]; names ¬ nameListt.list; }; GetRadiusValue: PUBLIC PROC [ggData: GGData] RETURNS [radius: REAL, success: BOOL ¬ TRUE] = { IF ggData.controls.radiusView = NIL THEN RETURN[0.0, FALSE]; radius ¬ GGViewerOps.GetPositiveReal[ggData.controls.radiusView, Real.LargestNumber]; IF radius>reallyBigReal THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "Attempt to use illegal radius value"]; radius ¬ 0.0; success ¬ FALSE; RETURN; }; <> IF RealFns.AlmostEqual[radius, ggData.measure.radiusViewValue, -10] THEN radius ¬ ggData.measure.radiusViewValue ELSE ggData.measure.radiusViewValue ¬ radius; }; SetRadiusValue: PUBLIC PROC [ggData: GGData, radius: REAL] = { ggData.measure.radiusViewValue ¬ radius; IF ggData.controls.radiusView # NIL THEN GGViewerOps.SetReal[ggData.controls.radiusView, radius, "%6.5f"]; }; SelectRadius: PUBLIC PROC [ggData: GGData] RETURNS [success: BOOL ¬ TRUE] = { IF ggData.controls.radiusView=NIL THEN success ¬ FALSE ELSE ViewerTools.SetSelection[ggData.controls.radiusView]; }; AddRadius: PUBLIC PROC [ggData: GGData, name: Rope.ROPE, radius: REAL, on: BOOL ¬ TRUE] RETURNS [alreadyThere: BOOL] = { oldFoundButton: AtomButtons.SortedButtonClient; IF ggData.controls.radiusHandle = NIL THEN RETURN[TRUE]; oldFoundButton ¬ AtomButtons.AddScalarSorted[clientData: ggData, handle: ggData.controls.radiusHandle, button: [name, radius, LIST[LIST[$ToggleRadius, NEW[REAL ¬ radius]]], on], order: incr]; alreadyThere ¬ oldFoundButton # NIL; }; AddRadiusList: PUBLIC PROC [ggData: GGData, names: LIST OF Rope.ROPE, radii: LIST OF REAL, on: LIST OF BOOL ¬ NIL] = { buttonList, oldList: LIST OF ScalarButton; oldNames: LIST OF Rope.ROPE; oldValues: LIST OF REAL; oldOn: LIST OF BOOL; IF ggData.controls.radiusHandle = NIL THEN RETURN; buttonList ¬ ButtonsFromValues[$ToggleRadius, names, radii, on]; [oldNames, oldValues, oldOn] ¬ GetRadiusAlignments[ggData]; oldList ¬ ButtonsFromValues[$ToggleRadius, oldNames, oldValues, oldOn]; buttonList ¬ MergeScalarButtonLists[buttonList, oldList, TRUE]; AtomButtons.BuildScalarButtons[ggData.controls.radiusHandle, ggData, GGUserInput.EventNotify, NIL, buttonList]; }; NewRadiusList: PUBLIC PROC [ggData: GGData, names: LIST OF Rope.ROPE, radii: LIST OF REAL, on: LIST OF BOOL ¬ NIL] = { buttonList: LIST OF ScalarButton; IF ggData.controls.radiusHandle = NIL THEN RETURN; buttonList ¬ ButtonsFromValues[$ToggleRadius, names, radii, on]; AtomButtons.BuildScalarButtons[ggData.controls.radiusHandle, ggData, GGUserInput.EventNotify, NIL, buttonList]; }; GetRadiusCircleCache: PUBLIC PROC [ggData: GGData] RETURNS [cache: GGCircleCache.Cache] = { cache _ ggData.controls.radiusCircleCache; }; SetRadiusCircleCache: PUBLIC PROC [ggData: GGData, cache: GGCircleCache.Cache] = { ggData.controls.radiusCircleCache _ cache; }; GetLineDistanceAlignments: PUBLIC PROC [ggData: GGData] RETURNS [names: LIST OF Rope.ROPE, values: LIST OF REAL, on: LIST OF BOOL] = { <> AddDistance: PROC [state: BOOL, name: Rope.ROPE, value: REF ANY, clientData: REF ANY] RETURNS [done: BOOL ¬ FALSE] = { real: REAL ¬ NARROW[value, REF REAL]­; GGCoreOps.AppendRope[name, nameListt]; [values, valuePtr] ¬ GGCoreOps.AddReal[real, values, valuePtr]; [on, boolPtr] ¬ GGCoreOps.AddBool[state, on, boolPtr]; }; nameListt: RopeListt; valuePtr: LIST OF REAL; boolPtr: LIST OF BOOL; nameListt ¬ GGCoreOps.NewRopeListt[]; [values, valuePtr] ¬ GGCoreOps.StartRealList[]; [on, boolPtr] ¬ GGCoreOps.StartBoolList[]; IF ggData.controls.distanceHandle # NIL THEN AtomButtons.ReadSortedButtons[ggData.controls.distanceHandle, AddDistance]; names ¬ nameListt.list; }; GetLineDistanceValue: PUBLIC PROC [ggData: GGData] RETURNS [distance: REAL, success: BOOL ¬ TRUE] = { IF ggData.controls.lineDistView = NIL THEN RETURN[0.0, FALSE]; distance ¬ GGViewerOps.GetReal[ggData.controls.lineDistView, Real.LargestNumber]; IF distance>reallyBigReal THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "Attempt to use illegal line distance value"]; distance ¬ 0.0; success ¬ FALSE; RETURN; }; <> IF RealFns.AlmostEqual[distance, ggData.measure.lineDistViewValue, -10] THEN distance ¬ ggData.measure.lineDistViewValue ELSE ggData.measure.lineDistViewValue ¬ distance; }; SetLineDistanceValue: PUBLIC PROC [ggData: GGData, distance: REAL] = { ggData.measure.lineDistViewValue ¬ distance; IF ggData.controls.lineDistView # NIL THEN GGViewerOps.SetReal[ggData.controls.lineDistView, distance, "%6.5f"]; }; SelectLineDistance: PUBLIC PROC [ggData: GGData] RETURNS [success: BOOL ¬ TRUE] = { IF ggData.controls.lineDistView=NIL THEN success ¬ FALSE ELSE ViewerTools.SetSelection[ggData.controls.lineDistView]; }; AddLineDistance: PUBLIC PROC [ggData: GGData, name: Rope.ROPE, distance: REAL, on: BOOL ¬ TRUE] RETURNS [alreadyThere: BOOL] = { oldFoundButton: AtomButtons.SortedButtonClient; IF ggData.controls.distanceHandle = NIL THEN RETURN[TRUE]; oldFoundButton ¬ AtomButtons.AddScalarSorted[clientData: ggData, handle: ggData.controls.distanceHandle, button: [name, distance, LIST[LIST[$ToggleDistance, NEW[REAL ¬ distance]]], on], order: incr]; alreadyThere ¬ oldFoundButton # NIL; }; AddLineDistanceList: PUBLIC PROC [ggData: GGData, names: LIST OF Rope.ROPE, distances: LIST OF REAL, on: LIST OF BOOL ¬ NIL] = { buttonList, oldList: LIST OF ScalarButton; oldNames: LIST OF Rope.ROPE; oldValues: LIST OF REAL; oldOn: LIST OF BOOL; IF ggData.controls.distanceHandle = NIL THEN RETURN; buttonList ¬ ButtonsFromValues[$ToggleDistance, names, distances, on]; [oldNames, oldValues, oldOn] ¬ GetLineDistanceAlignments[ggData]; oldList ¬ ButtonsFromValues[$ToggleDistance, oldNames, oldValues, oldOn]; buttonList ¬ MergeScalarButtonLists[buttonList, oldList, TRUE]; AtomButtons.BuildScalarButtons[ggData.controls.distanceHandle, ggData, GGUserInput.EventNotify, NIL, buttonList]; }; NewLineDistanceList: PUBLIC PROC [ggData: GGData, names: LIST OF Rope.ROPE, distances: LIST OF REAL, on: LIST OF BOOL ¬ NIL] = { buttonList: LIST OF ScalarButton; IF ggData.controls.distanceHandle = NIL THEN RETURN; buttonList ¬ ButtonsFromValues[$ToggleDistance, names, distances, on]; AtomButtons.BuildScalarButtons[ggData.controls.distanceHandle, ggData, GGUserInput.EventNotify, NIL, buttonList]; }; ScalarToRope: PROC [scalar: REAL] RETURNS [rope: Rope.ROPE] = { space: CHAR = ' ; rope ¬ IO.PutFR1["%1.2f", [real[scalar]]]; rope ¬ FileNames.Tail[rope, space]; -- strip off leading spaces <> UNTIL Rope.Fetch[base: rope, index: Rope.Length[rope]-1]# '0 DO rope ¬ Rope.Substr[base: rope, start: 0, len: Rope.Length[rope]-1]; ENDLOOP; <> IF Rope.Fetch[base: rope, index: Rope.Length[rope]-1] = '. THEN rope ¬ Rope.Substr[base: rope, start: 0, len: Rope.Length[rope]-1]; }; <> <> <> <<};>> <<>> <> <> <<};>> GetMidpoints: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = { IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[TRUE] ELSE { val: REF ¬ EmbeddedButtons.GetValue[$Midpoints, ggData.controls.controlPanel]; trueOrFalse: REF BOOL; IF val = NIL THEN RETURN[TRUE]; -- no button so use a default value trueOrFalse ¬ NARROW[val]; RETURN[trueOrFalse­]; }; }; SetMidpoints: PUBLIC PROC [ggData: GGData, midpointsOn: BOOL] = { IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN; EmbeddedButtons.SetValue[$Midpoints, IF midpointsOn THEN true ELSE false, ggData.controls.controlPanel]; }; GetWorkingDirectory: PUBLIC PROC [ggData: GGData] RETURNS [ROPE] = { RETURN[ggData.currentWDir]; }; SetWorkingDirectory: PUBLIC PROC [ggData: GGData, directory: ROPE] = { ggData.currentWDir ¬ directory; }; GetShowAlignments: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = { IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[TRUE] ELSE { val: REF ¬ EmbeddedButtons.GetValue[$ShowAlignments, ggData.controls.controlPanel]; trueOrFalse: REF BOOL; IF val = NIL THEN RETURN[TRUE]; -- no button so use a default value trueOrFalse ¬ NARROW[val]; RETURN[trueOrFalse­]; }; }; SetShowAlignments: PUBLIC PROC [ggData: GGData, showAlignments: BOOL] = { IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN; EmbeddedButtons.SetValue[$ShowAlignments, IF showAlignments THEN true ELSE false, ggData.controls.controlPanel]; ggData.camera.hideAlignments ¬ NOT showAlignments; }; true: REF BOOL ¬ NEW[BOOL ¬ TRUE]; false: REF BOOL ¬ NEW[BOOL ¬ FALSE]; GetUseBackingMap: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = { IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[ggData.controlState.useBackingMap] ELSE { val: REF ¬ EmbeddedButtons.GetValue[$UseBackingMap, ggData.controls.controlPanel]; trueOrFalse: REF BOOL; IF val = NIL THEN RETURN[TRUE]; -- no button so use a default value trueOrFalse ¬ NARROW[val]; RETURN[trueOrFalse­]; }; }; SetUseBackingMap: PUBLIC PROC [ggData: GGData, useBackingMap: BOOL] = { IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN { ggData.controlState.useBackingMap ¬ useBackingMap; RETURN; }; EmbeddedButtons.SetValue[$UseBackingMap, IF useBackingMap THEN true ELSE false, ggData.controls.controlPanel]; IF useBackingMap THEN { GGRefresh.InvalidateBackground[ggData]; GGRefresh.InvalidateForeground[ggData]; }; }; GetDoubleBuffer: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = { IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[ggData.controlState.doubleBuffer] ELSE { val: REF ¬ EmbeddedButtons.GetValue[$DoubleBuffer, ggData.controls.controlPanel]; trueOrFalse: REF BOOL; IF val = NIL THEN RETURN[TRUE]; -- no button so use a default value trueOrFalse ¬ NARROW[val]; RETURN[trueOrFalse­]; }; }; SetDoubleBuffer: PUBLIC PROC [ggData: GGData, doubleBuffer: BOOL] = { IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN { ggData.controlState.doubleBuffer ¬ doubleBuffer; RETURN; }; EmbeddedButtons.SetValue[$DoubleBuffer, IF doubleBuffer THEN true ELSE false, ggData.controls.controlPanel]; IF doubleBuffer THEN { GGRefresh.InvalidateBackground[ggData]; GGRefresh.InvalidateForeground[ggData]; }; }; <> <<>> GetActive: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = { IF ggData.controls = NIL THEN RETURN[TRUE]; IF ggData.controls.controlPanel = NIL THEN RETURN[ggData.controls.active] ELSE { val: REF ¬ EmbeddedButtons.GetValue[$Active, ggData.controls.controlPanel]; trueOrFalse: REF BOOL; IF val = NIL THEN RETURN[TRUE] ELSE { trueOrFalse ¬ NARROW[val]; RETURN[trueOrFalse­]; }; }; }; SetActive: PUBLIC PROC [ggData: GGData, activeOn: BOOL] = { IF ggData.controls = NIL THEN RETURN; IF ggData.controls.controlPanel = NIL THEN ggData.controls.active ¬ activeOn ELSE EmbeddedButtons.SetValue[$Active, IF activeOn THEN true ELSE false, ggData.controls.controlPanel]; }; GetReadOnly: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = { IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[ggData.controlState.readOnly] ELSE { trueOrFalse: REF BOOL; val: REF ¬ EmbeddedButtons.GetValue[$Editable, ggData.controls.controlPanel]; IF val = NIL THEN RETURN[ggData.controlState.readOnly]; trueOrFalse ¬ NARROW[val]; RETURN[NOT trueOrFalse­]; }; }; SetReadOnly: PUBLIC PROC [ggData: GGData, readOnly: BOOL] = { ggData.controlState.readOnly ¬ readOnly; IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN; EmbeddedButtons.SetValue[$Editable, IF readOnly THEN false ELSE true, ggData.controls.controlPanel]; }; GetPalette: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = { IF ggData.controls = NIL THEN RETURN[FALSE]; IF ggData.controls.controlPanel = NIL THEN RETURN[ggData.controls.palette] ELSE { val: REF ¬ EmbeddedButtons.GetValue[$Palette, ggData.controls.controlPanel]; trueOrFalse: REF BOOL; IF val = NIL THEN RETURN[FALSE]; trueOrFalse ¬ NARROW[val]; RETURN[trueOrFalse­]; }; }; SetPalette: PUBLIC PROC [ggData: GGData, paletteOn: BOOL] = { <> val: REF; isUnique: BOOL ¬ TRUE; <> IF ggData.controls = NIL THEN RETURN; IF ggData.controls.controlPanel = NIL THEN ggData.controls.palette ¬ paletteOn ELSE EmbeddedButtons.SetValue[$Palette, IF paletteOn THEN true ELSE false, ggData.controls.controlPanel]; <> IF paletteOn THEN { buttonDataRope: Rope.ROPE ¬ "Poppy1 Class: PopUpButton Menu: ( ((TransferFillColor) \"TransferFillColor\" \"Transfer this button's fill color to selected objects in the input focus viewer\") ((TransferBothColors) \"TransferBothColors\" \"Transfer this button's fill and stroke colors to selected objects in the input focus viewer\") ((TransferStrokeColor) \"TransferStrokeColor\" \"Transfer this button's stroke color to selected objects in the input focus viewer\") ) Feedback: ( (MouseMoved )) MessageHandler: Palette"; GGProps.Put[ggData.rootSlice, NIL, $ButtonData, GGProps.FromRope[$ButtonData, buttonDataRope]]; } ELSE { [val, isUnique] ¬ GGProps.Get[ggData.rootSlice, NIL, $ButtonData]; IF val # NIL THEN [] ¬ GGProps.Rem[ggData.rootSlice, NIL, $ButtonData]; }; IF paletteOn THEN SetActive[ggData, TRUE]; }; <> <> <> <<};>> <<>> <> val: REF; isUnique: BOOL ¬ TRUE; IF ggData.controls.paletteButton = NIL THEN ggData.controls.palette ¬ paletteOn ELSE AtomButtons.SetBinaryState[ggData.controls.paletteButton, paletteOn]; IF paletteOn THEN { buttonDataRope: Rope.ROPE ¬ "Poppy1 Class: PopUpButton Menu: ( ((TransferFillColor) \"TransferFillColor\" \"Transfer this button's fill color to selected objects in the input focus viewer\") ((TransferBothColors) \"TransferBothColors\" \"Transfer this button's fill and stroke colors to selected objects in the input focus viewer\") ((TransferStrokeColor) \"TransferStrokeColor\" \"Transfer this button's stroke color to selected objects in the input focus viewer\") ) Feedback: (MouseMotion ) MessageHandler: Palette"; GGProps.Put[ggData.rootSlice, NIL, $ButtonData, GGProps.FromRope[$ButtonData, buttonDataRope]]; } ELSE { [val, isUnique] ¬ GGProps.Get[ggData.rootSlice, NIL, $ButtonData]; IF val # NIL THEN [] ¬ GGProps.Rem[ggData.rootSlice, NIL, $ButtonData]; }; }; >> <> <<>> GetDefaults: PUBLIC PROC [ggData: GGData] RETURNS [DefaultData] = { RETURN[ggData.defaults]; }; SetDefaults: PUBLIC PROC [ggData: GGData, defaults: DefaultData] = { ggData.defaults ¬ defaults; }; DisplayStyleFromAtom: PROC [atom: ATOM] RETURNS [style: DisplayStyle] = { style ¬ SELECT atom FROM $SpecifiedFonts => print, $AlternateFonts => screen, $WYSIWYG => print ENDCASE => print; }; AtomFromDisplayStyle: PROC [style: DisplayStyle] RETURNS [atom: ATOM] = { atom ¬ SELECT style FROM print => $SpecifiedFonts, screen => $AlternateFonts, ENDCASE => ERROR; }; GetDisplayStyle: PUBLIC PROC [ggData: GGData] RETURNS [DisplayStyle] = { <> IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[print] ELSE { val: REF ¬ EmbeddedButtons.GetValue[$ScreenStyle, ggData.controls.controlPanel]; atom: ATOM; IF val = NIL THEN RETURN[print]; -- no button so use a default value atom ¬ NARROW[val]; RETURN[DisplayStyleFromAtom[atom]]; }; }; SetDisplayStyle: PUBLIC PROC [ggData: GGData, displayStyle: DisplayStyle] = { <> wantName: ATOM ¬ AtomFromDisplayStyle[displayStyle]; IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN; EmbeddedButtons.SetValue[$ScreenStyle, wantName, ggData.controls.controlPanel]; ggData.camera.displayStyle ¬ displayStyle; }; CycleDisplayStyle: PUBLIC PROC [ggData: GGData, forward: BOOL] = { wantName, isName: ATOM; camera: Camera ¬ ggData.camera; val: REF; IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN; val ¬ EmbeddedButtons.GetValue[$ScreenStyle, ggData.controls.controlPanel]; isName ¬ NARROW[val]; IF forward THEN { wantName ¬ SELECT isName FROM $SpecifiedFonts => $AlternateFonts, $AlternateFonts => $WYSIWYG, $WYSIWYG => $SpecifiedFonts, ENDCASE => $SpecifiedFonts; } ELSE { wantName ¬ SELECT isName FROM $SpecifiedFonts => $WYSIWYG, $AlternateFonts => $SpecifiedFonts, $WYSIWYG => $AlternateFonts, ENDCASE => $SpecifiedFonts; }; EmbeddedButtons.SetValue[$ScreenStyle, wantName, ggData.controls.controlPanel]; SELECT wantName FROM $SpecifiedFonts => { camera.quality ¬ fast; camera.displayStyle ¬ print; }; $AlternateFonts => { camera.quality ¬ fast; camera.displayStyle ¬ screen; }; $WYSIWYG => { camera.quality ¬ quality; camera.displayStyle ¬ print; }; ENDCASE => ERROR; }; <> <> <> <> <> < "SpecifiedFonts",>> < "AlternateFonts",>> < ERROR;>> <> <> <> <> <> <> <<};>> <<>> <> <> <> <> <> <> <> <> < pointsPreferred,>> < linesPreferred,>> < ERROR;>> <> <<};>> <<>> <> <> <<};>> <<>> <> <> <> <> <> < "PreferPoints",>> < "PreferLines",>> < ERROR;>> <> <> <> <> <> <> <<};>> <> <> <<};>> <<>> GetHeuristics: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = { IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[FALSE] ELSE { val: REF ¬ EmbeddedButtons.GetValue[$Auto, ggData.controls.controlPanel]; trueOrFalse: REF BOOL; IF val = NIL THEN RETURN[FALSE]; -- no button so use a default value trueOrFalse ¬ NARROW[val]; RETURN[trueOrFalse­]; }; }; SetHeuristics: PUBLIC PROC [ggData: GGData, heuristicsOn: BOOL] = { IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN; EmbeddedButtons.SetValue[$Auto, IF heuristicsOn THEN true ELSE false, ggData.controls.controlPanel]; }; <> <> <> <<};>> <<>> <> <> <> <<};>> GetScaleUnit: PUBLIC PROC [ggData: GGData] RETURNS [REAL] = { RETURN[ggData.hitTest.scaleUnit]; }; SetScaleUnit: PUBLIC PROC [ggData: GGData, unit: REAL, history: HistoryEvent] = { IF unit >0.0 THEN { oldValue: REF REAL ¬ NEW[REAL ¬ GetScaleUnit[ggData]]; ggData.hitTest.scaleUnit ¬ unit; -- in screen dots IF history#NIL THEN { changeRef: REF Change.changingstate ¬ NEW[Change.changingstate ¬ [changingstate[$SetScaleUnit, ggData.scene, oldValue] ] ]; GGHistory.Note[history, UndoScale, changeRef]; }; }; }; <<>> UndoScale: PROC [historyData: REF Change, currentEvent: HistoryEvent] = { <> <> stateData: REF Change.changingstate ¬ NARROW[historyData]; SELECT stateData.op FROM $SetScaleUnit => { oldValue: REF REAL ¬ NARROW[stateData.oldValue]; <<[] _ SetScaleUnit[stateData.ggData, oldValue^, currentEvent]; -- restore old value>> }; ENDCASE; }; UpdateCursorLooks: PROC [ggData: GGData] = { SELECT GetGravity[ggData] FROM TRUE => SELECT GetGravityType[ggData] FROM pointsPreferred => GGWindow.SetCursorLooks[pointsPreferred, ggData]; linesPreferred, facesPreferred => GGWindow.SetCursorLooks[linesPreferred, ggData]; ENDCASE => ERROR; FALSE => GGWindow.SetCursorLooks[pointsPreferred, ggData, TRUE]; -- really means off ENDCASE; }; <> GetSliceToExtend: PUBLIC PROC [ggData: GGData] RETURNS [sliceD: SliceDescriptor] = { RETURN[ggData.drag.sliceToExtend]; }; SetSliceToExtend: PUBLIC PROC [ggData: GGData, sliceD: SliceDescriptor] = { ggData.drag.sliceToExtend ¬ sliceD; }; <<>> <> GetSelectMode: PUBLIC PROC [ggData: GGData] RETURNS [selectMode: SelectMode] = { RETURN[ggData.drag.selectState]; }; SetSelectMode: PUBLIC PROC [ggData: GGData, selectMode: SelectMode] = { ggData.drag.selectState ¬ selectMode; }; GetExtendMode: PUBLIC PROC [ggData: GGData] RETURNS [extendMode: ExtendMode] = { RETURN[ggData.drag.extendMode]; }; SetExtendMode: PUBLIC PROC [ggData: GGData, extendMode: ExtendMode] = { ggData.drag.extendMode ¬ extendMode; }; GetQuickClickMode: PUBLIC PROC RETURNS [on: BOOL] = { RETURN[quickClickMode]; }; SetQuickClickMode: PUBLIC PROC [on: BOOL] = { quickClickMode ¬ on; }; quickClickMode: BOOL ¬ FALSE; -- managed by GGUserProfileImpl GetSelectionCycler: PUBLIC PROC [ggData: GGData] RETURNS [featureCycler: FeatureCycler] = { featureCycler ¬ ggData.scene.selected.featureCycler; }; SetSelectionCycler: PUBLIC PROC [ggData: GGData, featureCycler: FeatureCycler] = { ggData.scene.selected.featureCycler ¬ featureCycler; }; <> <> <> <> <<};>> <> GetFullName: PUBLIC PROC [ggData: GGData] RETURNS [fullName: ROPE] = { <> <> IF ggData.controls = NIL THEN RETURN[NIL]; IF ggData.controls.panel = NIL OR ggData.controls.picture = NIL OR ggData.controls.topper = NIL THEN RETURN[ggData.controls.fileName]; fullName ¬ IF ggData.controls.topper.file#NIL THEN ggData.controls.topper.file ELSE ggData.controls.panel.file; }; StoreAdvisory: PUBLIC PROC [ggData: GGData, fullName: ROPE, versionSpecified: BOOL] = { nameNoBang: ROPE ¬ FileNames.StripVersionNumber[fullName]; SetNameFileLabel[ggData, IF versionSpecified THEN fullName ELSE nameNoBang, fullName, FileNames.GetShortName[nameNoBang]]; }; GetAdvisory: PUBLIC PROC [ggData: GGData, fullName: ROPE, versionSpecified: BOOL] = { StoreAdvisory[ggData, fullName, versionSpecified]; }; ClearAdvisory: PUBLIC PROC [ggData: GGData] = { panelPrefix: Rope.ROPE = "GGPanel: "; IF ggData.controls = NIL OR ggData.controls.panel = NIL OR ggData.controls.picture = NIL OR ggData.controls.topper = NIL THEN RETURN; SetNameFileLabel[ggData, NIL, NIL, NIL]; ggData.controls.panel.name ¬ Rope.Concat[panelPrefix, "Gargoyle"]; ggData.controls.panel.label ¬ panelPrefix; ggData.controls.topper.name ¬ ggData.controls.topper.label ¬ "Gargoyle"; }; AdviseRestore: PUBLIC PROC [ggData: GGData] RETURNS [ok: BOOL ¬ FALSE] = { <> name: ROPE; IF ggData.controls = NIL THEN RETURN[FALSE]; IF ggData.controls.panel = NIL OR ggData.controls.picture = NIL OR ggData.controls.topper = NIL THEN name ¬ ggData.controls.fileName ELSE name ¬ IF ggData.controls.topper.name#NIL THEN ggData.controls.topper.name ELSE ggData.controls.panel.name; IF ggData.controls.topper # NIL AND ggData.controls.topper.destroyed THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Restore failed: picture destroyed; try Save or Store"] ELSE IF Rope.Equal[name, NIL] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Restore failed: can't restore unnamed viewer; try Get"] ELSE ok ¬ TRUE; }; SetNameFileLabel: PROC [ggData: GGData, name, file, label: ROPE] = { IF ggData.controls = NIL THEN RETURN; ggData.controls.fileName _ file; IF ggData.controls.panel = NIL OR ggData.controls.picture = NIL OR ggData.controls.topper = NIL THEN RETURN; ggData.controls.topper.name ¬ ggData.controls.panel.name ¬ ggData.controls.picture.name ¬ name; ggData.controls.topper.file ¬ ggData.controls.panel.file ¬ ggData.controls.picture.file ¬ file; ggData.controls.topper.label ¬ ggData.controls.panel.label ¬ ggData.controls.picture.label ¬ label; }; GetBiScroller: PUBLIC PROC [ggData: GGData] RETURNS [bs: BiScroller] = { bs ¬ ggData.controls.biScroller; }; GrabInputFocus: PUBLIC PROC [ggData: GGData] = { [] ¬ InputFocus.SetInputFocus[ggData.controls.actionArea]; }; GetWidth: PUBLIC PROC [ggData: GGData] RETURNS [width: INT] = { -- drawing area, in "window" units width ¬ Real.Round[GetViewport[ggData].w]; }; GetHeight: PUBLIC PROC [ggData: GGData] RETURNS [height: INT] = { -- drawing area, in "window" units height ¬ Real.Round[GetViewport[ggData].h]; }; <<>> ShowHelp: PUBLIC PROC [ggData: GGData, category: ATOM] = { openHeight: INTEGER ¬ 140; help: Viewer; name: Rope.ROPE; SELECT category FROM $MouseActions => {name ¬ "GGHelp.tioga"; openHeight ¬ 140}; $Fonts => {name ¬ "GGFontSampler.tioga"; openHeight ¬ 210}; $Colors => {name ¬ "GGColors.tioga"; openHeight ¬ 210}; ENDCASE => {name ¬ "GargoyleDoc.tioga"; openHeight ¬ 115}; IF (help ¬ ViewerOps.FindViewer[ <> FS.ExpandName[name, GGUIUtility.GGHomeDirectory[] ].fullFName])#NIL THEN { -- viewer already exists IF help.column#right THEN ViewerOps.ChangeColumn[help, right]; } ELSE { help ¬ ViewerOps.CreateViewer[flavor: $Text, info: [iconic: TRUE, column: right, openHeight: openHeight], paint: FALSE]; <> TiogaMenuOps.Load[viewer: help, fileName: Rope.Concat[GGUIUtility.GGHomeDirectory[], name]]; }; ViewerOps.SetOpenHeight[viewer: help, clientHeight: openHeight]; ViewerOps.OpenIcon[icon: help, bottom: FALSE, paint: FALSE]; -- must do Open before Top ViewerOps.TopViewer[viewer: help, paint: FALSE]; ViewerOps.ComputeColumn[right]; -- repaint right column Feedback.Append[ggData.router, oneLiner, $Feedback, "Help opened"]; }; ReloadTipTable: PUBLIC PROC [ggData: GGData] RETURNS [success: BOOL ¬ TRUE] = { newTable: TIPUser.TIPTable; actionArea: Viewer; tableName, msg: Rope.ROPE; <> tableName ¬ Rope.Concat[GGUIUtility.GGHomeDirectory[], "Gargoyle.tip"]; Feedback.PutF[ggData.router, begin, $Feedback, "Reloading tip table %g. . . ", [rope[tableName]] ]; newTable ¬ TIPUser.InstantiateNewTIPTable[tableName ! FS.Error => { success ¬ FALSE; msg ¬ Rope.Concat["Cannot read TIP table file: ", tableName]; CONTINUE}; TIPUser.InvalidTable => { success ¬ FALSE; msg ¬ Rope.Concat["Error(s) saved on TIP.Errors for: ", tableName]; CONTINUE}]; IF success THEN { IF newTable = NIL THEN ERROR; Feedback.Append[ggData.router, end, $Feedback, "Done"]; <> ggData.tipTable ¬ newTable; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, msg]; }; PaintActionArea: PUBLIC PROC [ggData: GGData] = { ViewerOps.PaintViewer[ viewer: ggData.controls.actionArea, hint: client, whatChanged: ggData, clearClient: FALSE]; }; <> <> <> <<[alreadyExists, typescript] ¬ FeedbackOps.CreateNamedTypescript["Gargoyle Typescript", $Gargoyle, 120];>> <> <> <<};>> GGEdited: PUBLIC PROC [ggData: GGData, clientData: REF] = { IF GGUserProfile.GetAutoScriptingOn[] THEN { ggData.debug.autoScriptActionCount ¬ ggData.debug.autoScriptActionCount + 1; IF ggData.debug.autoScriptActionCount MOD 20 = 0 THEN [ggData.debug.autoScriptStream, ggData.debug.autoScriptName] ¬ GGSessionLog.FlushScript[ggData.debug.autoScriptStream, ggData.debug.autoScriptName, ggData.router]; }; IF ggData.controls = NIL OR ggData.controls.topper = NIL OR ggData.controls.panel = NIL THEN RETURN; IF NOT ggData.controls.topper.newVersion THEN { <> dirtyNoNameIconG, dirtyIconG: Icons.IconFlavor; [dirtyNoNameIcon: dirtyNoNameIconG, dirtyIcon: dirtyIconG] ¬ GGWindow.GetIcons[]; ggData.controls.topper.icon ¬ IF ggData.controls.topper.file=NIL THEN dirtyNoNameIconG ELSE dirtyIconG; ggData.controls.panel.newVersion ¬ TRUE; -- guard destroy button. KAP. June 1, 1988 ggData.controls.topper.newVersion ¬ TRUE; IF ggData.controls.topper.parent = NIL THEN -- top level viewer ViewerOps.PaintViewer[ggData.controls.topper, caption]; IF ggData.controls.topper#ggData.controls.panel THEN { IF ggData.controls.panel.parent = NIL THEN -- top level viewer ViewerOps.PaintViewer[ggData.controls.panel, caption]; }; }; }; END.