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]; }; <) 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; }; GetDefaultDashPattern: PUBLIC PROC [ggData: GGData] RETURNS [dashed: BOOL, pattern: SequenceOfReal, offset: REAL, length: REAL] = { dashed ¬ ggData.defaults.dashed; pattern ¬ ggData.defaults.pattern; offset ¬ ggData.defaults.offset; length ¬ ggData.defaults.length; }; SetDefaultDashPattern: PUBLIC PROC [ggData: GGData, dashed: BOOL, pattern: SequenceOfReal, offset: REAL, length: REAL] = { ggData.defaults.dashed ¬ dashed; ggData.defaults.pattern ¬ pattern; ggData.defaults.offset ¬ offset; ggData.defaults.length ¬ length; }; GetDefaultFillColor: PUBLIC PROC [ggData: GGData] RETURNS [fillColor: Imager.Color] = { fillColor ¬ ggData.defaults.fillColor; }; SetDefaultFillColor: PUBLIC PROC [ggData: GGData, fillColor: Imager.Color] = { ggData.defaults.fillColor ¬ fillColor; }; GetDefaultStrokeColor: PUBLIC PROC [ggData: GGData] RETURNS [strokeColor: Imager.Color] = { strokeColor ¬ ggData.defaults.strokeColor; }; SetDefaultStrokeColor: PUBLIC PROC [ggData: GGData, strokeColor: Imager.Color] = { ggData.defaults.strokeColor ¬ strokeColor; }; GetDefaultStrokeJoint: PUBLIC PROC [ggData: GGData] RETURNS [strokeJoint: StrokeJoint] = { strokeJoint ¬ ggData.defaults.strokeJoint; }; SetDefaultStrokeJoint: PUBLIC PROC [ggData: GGData, strokeJoint: StrokeJoint] = { ggData.defaults.strokeJoint ¬ strokeJoint; }; GetDefaultStrokeEnd: PUBLIC PROC [ggData: GGData] RETURNS [strokeEnd: StrokeEnd] = { strokeEnd ¬ ggData.defaults.strokeEnd; }; SetDefaultStrokeEnd: PUBLIC PROC [ggData: GGData, strokeEnd: StrokeEnd] = { ggData.defaults.strokeEnd ¬ strokeEnd; }; GetDefaultDropShadows: PUBLIC PROC [ggData: GGData] RETURNS [dropShadowOn: BOOL, dropShadowOffset: Vector, dropShadowColor: Imager.Color] = { dropShadowOn ¬ ggData.defaults.dropShadowOn; dropShadowOffset ¬ ggData.defaults.dropShadowOffset; dropShadowColor ¬ ggData.defaults.dropShadowColor; }; SetDefaultDropShadows: PUBLIC PROC [ggData: GGData, dropShadowOn: BOOL, dropShadowOffset: Vector, dropShadowColor: Imager.Color] = { ggData.defaults.dropShadowOn ¬ dropShadowOn; ggData.defaults.dropShadowOffset ¬ dropShadowOffset; ggData.defaults.dropShadowColor ¬ dropShadowColor; }; GetDefaultFont: PUBLIC PROC [ggData: GGData] RETURNS [fontData: FontData] = { fontData ¬ ggData.defaults.font; }; SetDefaultFont: PUBLIC PROC [ggData: GGData, fontData: FontData] = { ggData.defaults.font ¬ fontData; }; GetGravity: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = { IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[TRUE] ELSE { val: REF ¬ EmbeddedButtons.GetValue[$Gravity, ggData.controls.controlPanel]; trueOrFalse: REF BOOL; IF val = NIL THEN RETURN[TRUE]; -- no button so use a default value trueOrFalse ¬ NARROW[val]; RETURN[trueOrFalse­]; }; }; SetGravity: PUBLIC PROC [ggData: GGData, gravityOn: BOOL] = { IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN; EmbeddedButtons.SetValue[$Gravity, IF gravityOn THEN true ELSE false, ggData.controls.controlPanel]; UpdateCursorLooks[ggData]; }; GetGravityExtent: PUBLIC PROC [ggData: GGData] RETURNS [inches: REAL] = { screenDots: REAL; IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN screenDots ¬ GGUserProfile.GetDefaultGravityExtent[] ELSE { val: REF ¬ EmbeddedButtons.GetValue[$GravityExtent, ggData.controls.controlPanel]; IF val = NIL THEN screenDots ¬ GGUserProfile.GetDefaultGravityExtent[] ELSE { screenDots ¬ NARROW[val, REF REAL]­; }; }; inches ¬ screenDots/72.0; }; SetGravityExtent: PUBLIC PROC [ggData: GGData, inches: REAL] = { screenDots: REAL; refReal: REF REAL; IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN; screenDots ¬ inches*72.0; refReal ¬ NEW[REAL ¬ screenDots]; EmbeddedButtons.SetValue[$GravityExtent, refReal, ggData.controls.controlPanel]; ggData.hitTest.t ¬ screenDots; }; GravityTypeFromAtom: PROC [atom: ATOM] RETURNS [gravityType: GravityType] = { gravityType ¬ SELECT TRUE FROM atom = $PreferPoints => pointsPreferred, atom = $PreferLines => linesPreferred, ENDCASE => pointsPreferred; }; AtomFromGravityType: PROC [gravityType: GravityType] RETURNS [atom: ATOM] = { atom ¬ SELECT gravityType FROM pointsPreferred => $PreferPoints, linesPreferred, facesPreferred => $PreferLines, ENDCASE => ERROR; }; GetGravityType: PUBLIC PROC [ggData: GGData] RETURNS [gravityType: GravityType] = { IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN[pointsPreferred] ELSE { val: REF ¬ EmbeddedButtons.GetValue[$GravityType, ggData.controls.controlPanel]; atom: ATOM; IF val = NIL THEN RETURN[pointsPreferred]; -- no button so use a default value atom ¬ NARROW[val]; RETURN[GravityTypeFromAtom[atom]]; }; }; SetGravityType: PUBLIC PROC [ggData: GGData, gravityType: GravityType] = { atom: ATOM; IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN; atom ¬ AtomFromGravityType[gravityType]; EmbeddedButtons.SetValue[$GravityType, atom, ggData.controls.controlPanel]; ggData.hitTest.gravityType ¬ gravityType; UpdateCursorLooks[ggData]; }; CycleGravityType: PUBLIC PROC [ggData: GGData, forward: BOOL] = { gravityType: GravityType; IF ggData.controls = NIL OR ggData.controls.controlPanel = NIL THEN RETURN; gravityType ¬ GetGravityType[ggData]; gravityType ¬ IF gravityType = pointsPreferred THEN linesPreferred ELSE pointsPreferred; SetGravityType[ggData, gravityType]; ggData.hitTest.gravityType ¬ gravityType; UpdateCursorLooks[ggData]; }; 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]; }; 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]; }; 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. !jGGStateImpl.mesa Contents: Routines for getting and setting values in the Gargoyle user interface state. Copyright Ó 1986, 1988, 1989, 1991, 1992 by Xerox Corporation. All rights reserved. Pier, June 23, 1993 5:09 pm PDT Bier, June 23, 1993 9:43 pm PDT Doug Wyatt, April 17, 1992 1:55 pm PDT PROC [ggData: GGData, clientData: REF _ NIL] RETURNS [rect: Imager.Rectangle]; Gets the viewport size from the BiScroller rect ¬ BiScrollers.ViewportBox[ggData.controls.biScroller]; need to repaint the topper caption because viewer is no longer edited Changes the gargoyle icon to $clear or $clean Finds the rectangle representing the bounds of the current action area. The larger of the two scaling components of clientToViewer. pp: PreservationPair ~ bs.class.common.preserve; cv _ [v.cw*pp[X], v.ch*pp[Y]]; vanilla _ ImagerTransformation.Translate[[viewport.w/2.0, viewport.h/2.0]]; vanilla _ ImagerTransformation.Translate[[viewport.w/2.0, viewport.h/2.0]]; IF bs = NIL THEN { new ¬ old.PostTranslate[[dx, dy]]; ChangeTransform[ggData, new, ignore]; } ELSE BiScrollers.Shift[ggData.controls.biScroller, dx, dy, FALSE]; PROC [clientData: REF, direction: Vec] RETURNS [min, max: Vec]; Mostly copied from GGWindowImpl.GGExtremaProc Extracted from BiScrollersButtonned.ChangeTransform. Alignment Menus Returns the values, and state booleans in proper order. Only positive slopes, please. Return the most accurate value we have. name _ IF names = NIL THEN NIL ELSE names.first; -- why did this replace the line above? Destructive merge of the two lists, removing duplicates, where the element that is removed, when there is a choice, is the one without a name. Take the first element of list off of list and add it to mergedList, updating pointers. Put the most accurate value we have into variable angle Returns the names, values, and state booleans in proper order. Put the most accurate value we have into variable radius Returns the names, values, and state booleans in proper order. Put the most accurate value we have into variable distance Strip off trailing zeroes Strip off trailing decimal point GetMidpoints: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = { IF ggData.controls.midpointButton = NIL THEN RETURN[TRUE] ELSE RETURN[AtomButtons.GetBinaryState[ggData.controls.midpointButton]]; }; SetMidpoints: PUBLIC PROC [ggData: GGData, midpointsOn: BOOL] = { IF ggData.controls.midpointButton # NIL THEN AtomButtons.SetBinaryState[ggData.controls.midpointButton, midpointsOn]; }; Gargoyle Behaviors Update the "Palette" button and make sure that the ButtonData property of the root node is correct Update control panel. Check the root node. GetPalette: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = { IF ggData.controls.paletteButton = NIL THEN RETURN[ggData.controls.palette] ELSE RETURN[AtomButtons.GetBinaryState[ggData.controls.paletteButton]]; }; Update the "Palette" button and make sure that the ButtonData property of the root node is correct Graphical Style Not very useful because DisplayStyle doesn't include WYSIWYG. Not very useful because DisplayStyle doesn't include WYSIWYG. SetDisplayStyle: PUBLIC PROC [ggData: GGData, displayStyle: DisplayStyle] = { NEED TO GOOSE THE MENU BUTTON HERE info: AtomButtons.EnumTypeRef _ ggData.controls.screenStyle; isName, wantName: Rope.ROPE; wantName _ SELECT displayStyle FROM print => "SpecifiedFonts", screen => "AlternateFonts", ENDCASE => ERROR; isName _ info.flipLabel.name; UNTIL Rope.Equal[isName, wantName, TRUE] DO AtomButtons.TimeToFlipThru[LIST[$FlipForward, info]]; isName _ info.flipLabel.name; ENDLOOP; ggData.camera.displayStyle _ displayStyle; }; CycleDisplayStyle: PUBLIC PROC [ggData: GGData, forward: BOOL] = { name: Rope.ROPE; info: AtomButtons.EnumTypeRef _ ggData.controls.screenStyle; camera: Camera _ ggData.camera; IF forward THEN AtomButtons.TimeToFlipThru[LIST[$FlipForward, info]] ELSE AtomButtons.TimeToFlipThru[LIST[$FlipBackward, info]]; name _ info.flipLabel.name; SELECT TRUE FROM Rope.Equal[name, "SpecifiedFonts", TRUE] => { camera.quality _ fast; camera.displayStyle _ print; }; Rope.Equal[name, "AlternateFonts", TRUE] => { camera.quality _ fast; camera.displayStyle _ screen; }; Rope.Equal[name, "WYSIWYG", TRUE] => { camera.quality _ quality; camera.displayStyle _ print; }; ENDCASE => ERROR; }; Gravity GetGravity: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = { IF ggData.controls.gravButton = NIL THEN RETURN[TRUE] ELSE RETURN[AtomButtons.GetBinaryState[ggData.controls.gravButton]]; }; SetGravity: PUBLIC PROC [ggData: GGData, gravityOn: BOOL] = { IF ggData.controls.gravButton = NIL THEN RETURN; AtomButtons.SetBinaryState[ggData.controls.gravButton, gravityOn]; UpdateCursorLooks[ggData]; }; GetGravityExtent: PUBLIC PROC [ggData: GGData] RETURNS [inches: REAL] = { screenDots: REAL; graphicsState: AtomButtonsTypes.GraphicsState _ ggData.controls.gravityExtentButton; IF graphicsState = NIL THEN screenDots _ GGUserProfile.GetDefaultGravityExtent[] ELSE { ged: GGInterfaceTypes.GravityExtentData; ged _ NARROW[GraphicsButton.GetValue[graphicsState].buttonData]; screenDots _ ged.extent; }; inches _ screenDots/72.0; }; SetGravityExtent: PUBLIC PROC [ggData: GGData, inches: REAL] = { screenDots: REAL; graphicsState: AtomButtonsTypes.GraphicsState _ ggData.controls.gravityExtentButton; ged: GGInterfaceTypes.GravityExtentData; IF graphicsState = NIL THEN RETURN; screenDots _ inches*72.0; ged _ NEW[GGInterfaceTypes.GravityExtentDataObj _ [extent: screenDots]]; GraphicsButton.SetButtonValueAndPaint[graphicsState, ggData, ged]; ggData.hitTest.t _ screenDots; }; CycleGravityType: PUBLIC PROC [ggData: GGData, forward: BOOL] = { info: AtomButtons.EnumTypeRef _ ggData.controls.gravityTypeMenu; IF info # NIL THEN { IF forward THEN AtomButtons.TimeToFlipThru[LIST[$FlipForward, info]] ELSE AtomButtons.TimeToFlipThru[LIST[$FlipBackward, info]]; }; ggData.hitTest.gravityType _ SELECT TRUE FROM Rope.Equal[info.flipLabel.name, "PreferPoints", TRUE] => pointsPreferred, Rope.Equal[info.flipLabel.name, "PreferLines", TRUE] => linesPreferred, ENDCASE => ERROR; UpdateCursorLooks[ggData]; }; GetGravityType: PUBLIC PROC [ggData: GGData] RETURNS [gravityType: GravityType] = { RETURN[ggData.hitTest.gravityType]; }; SetGravityType: PUBLIC PROC [ggData: GGData, gravityType: GravityType] = { NEED TO GOOSE THE MENU BUTTON HERE info: AtomButtons.EnumTypeRef _ ggData.controls.gravityTypeMenu; isName, wantName: Rope.ROPE; wantName _ SELECT gravityType FROM pointsPreferred => "PreferPoints", linesPreferred, facesPreferred => "PreferLines", ENDCASE => ERROR; IF info # NIL THEN { isName _ info.flipLabel.name; UNTIL Rope.Equal[isName, wantName, TRUE] DO AtomButtons.TimeToFlipThru[LIST[$FlipForward, info]]; isName _ info.flipLabel.name; ENDLOOP; }; ggData.hitTest.gravityType _ gravityType; UpdateCursorLooks[ggData]; }; GetHeuristics: PUBLIC PROC [ggData: GGData] RETURNS [BOOL] = { IF ggData.controls.heuristicsButton = NIL THEN RETURN[FALSE] ELSE RETURN[AtomButtons.GetBinaryState[ggData.controls.heuristicsButton]]; }; SetHeuristics: PUBLIC PROC [ggData: GGData, heuristicsOn: BOOL] = { IF ggData.controls.heuristicsButton = NIL THEN RETURN; AtomButtons.SetBinaryState[ggData.controls.heuristicsButton, heuristicsOn]; }; GGHistoryTypes.HistoryProc This proc is called by the Undo mechanism. It is called with a history event (currentEvent) which it passes on to record its undo operations, making undo an event (and thus undoable) in itself. [] _ SetScaleUnit[stateData.ggData, oldValue^, currentEvent]; -- restore old value Other Hidden State (yuk) Debugging and Experiments precomputeMidpoints: BOOL _ FALSE; PrecomputeMidpoints: PUBLIC PROC [] RETURNS [BOOL] = { RETURN[precomputeMidpoints]; }; Names, files, windows file name like /net/server/user/gargoyle/Foo.gargoyle or /GargoylePics/MyPics/Foo.gargoyle!3 Is restore possible? removed use of originalWDir removed use of originalWDir tableName _ Rope.Concat[ggData.originalWDir, "Gargoyle.tip"]; ggData.controls.actionArea.tipTable _ newTable; -- this TIP table is now the Transparent TIP table at all times Typescript: PUBLIC PROC [ggData: GGData] = { -- Bier, March 18, 1993 alreadyExists: BOOL ¬ FALSE; typescript: Viewer; [alreadyExists, typescript] ¬ FeedbackOps.CreateNamedTypescript["Gargoyle Typescript", $Gargoyle, 120]; IF alreadyExists THEN ViewerOps.OpenIcon[icon: typescript, closeOthers: FALSE, bottom: TRUE, paint: TRUE]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Typescript opened"]; }; Picture just became edited (dirty). Change the icon. ÊE„–(cedarcode) style•NewlineDelimiter ˜codešœ™KšÏnœP™XKšœ ÏeœI™TK™K™K™&K™—šÏk ˜ Jšœ¢Ÿœ«ÏbœÿŸœ]˜®K˜—š œŸœŸ˜JšŸœuŸœ¹ŸœD˜ýKšŸœJŸ˜V—˜KšœŸ œÏc˜EKšœ Ÿ œ$¡˜[KšœŸœŸœ¡˜VKšœŸœŸœ¡˜VKšœ ŸœŸœ¡˜SKšœŸœŸœ"¡˜\K˜KšœŸœ#˜-KšœŸœ$˜/Kšœ Ÿœ˜*Kšœ Ÿœ˜&KšœŸœ˜#KšœŸœ˜%Kšœ Ÿœ˜-KšœŸœ˜/Kšœ Ÿœ˜+KšœŸœ"˜5Kšœ Ÿœ˜-KšœŸœ˜3Kšœ Ÿœ˜!KšœŸœ˜'Kšœ Ÿœ ˜1KšœŸœ˜1Kšœ Ÿœ'˜5KšœŸœ/˜EKšœŸœ˜KšŸœŸœŸœ˜Kšœ Ÿœ˜(KšœŸœ˜.Kšœ ŸœŸœ¡˜FKšœŸœ'˜?KšœŸœ'˜?Kšœ Ÿœ˜+KšœŸœ˜2KšœŸœ ˜5Kšœ Ÿœ˜'Kšœ Ÿœ˜#Kšœ Ÿœ'˜6KšœŸœ'˜;KšœŸœ Ÿœ˜KšœŸœ˜#KšœŸœ˜$KšœŸœ˜0K˜—KšœŸœ ˜K˜š œŸœŸœ2ŸœŸœ˜aK˜!K˜'K˜K™—šœŸœ˜(KšŸœŸœŸœŸœ™NKšœ*™*KšŸœŸœŸœŸœ˜>K™;šŸœ˜K˜0K˜0K˜—K˜K™—š œŸ œŸœ˜9KšœE™EKšŸœŸœŸœŸœŸœŸœŸœŸœ˜dKšœ#Ÿœ¡.˜XKšœ$Ÿœ˜*šœŸœŸ˜,Kšœ)˜)Kšœ(˜(KšŸœŸœ˜—Kšœ. œ˜7KšŸœ-Ÿœ. œ˜kKšœ˜K˜—š œŸœŸœŸœ˜6K™-Kš ŸœŸœŸœŸœŸœŸœ˜EšœŸœŸ˜,Kšœ)˜)Kšœ(˜(KšŸœŸœ˜—Kšœ. œ˜7Kšœ˜K™K˜—š œŸ œŸœŸœŸœ˜IKš ŸœŸœŸœŸœŸœ˜BKšœw˜wK˜K˜—š œŸœŸœŸœ˜NK™GKšŸœŸœŸœŸœ˜HKšŸœŸœ@˜KK˜K˜—š œŸœŸœŸœŸœ˜BKšœ˜K˜:Kš ŸœŸœŸœŸœŸœŸœ˜:K˜šŸœ&Ÿœ˜.KšœŸœ˜2Kšœ Ÿœ'˜9K˜—šŸœŸœŸœŸœ˜$šŸœŸœŸ˜ K˜%KšŸœŸœ˜—K˜—K˜K˜—š œŸœŸœŸœ ˜;KšŸœ˜Kšœ˜—K˜Kšœ;˜;šœŸœŸœŸœ5˜sKšŸœŸœŸœŸœH˜wKšŸœe˜iK˜K˜—šœŸœŸœŸœ%˜bK˜,KšŸœŸœŸœŸœ$˜;KšŸœ<˜@K˜K˜—š œŸœŸœŸœŸœ˜GKšœ;™;K˜AK˜:K˜K˜—š œŸœŸœ,ŸœŸœ˜sK˜,KšœA˜AKšœ˜šŸœŸœŸœ˜Kšœ%˜%K˜—šŸœ˜Kš œ2ŸœŸœŸœŸœ˜mIdefaultš ŸœŸœrŸœŸœŸœ¡+˜âK˜—K˜K˜—š œŸœŸœ9ŸœŸœ˜†K˜,šŸœŸœŸœ˜K˜/K˜—šŸœ˜Kš œ=ŸœŸœŸœŸœ˜xKš ŸœŸœrŸœŸœŸœ¡+˜âK˜—K˜K˜—šŸœŸœŸœŸœ˜0šœŸœŸ˜K˜ K˜K˜KšŸœŸœ˜K˜——šœŸœŸœŸœ˜CKšœ0™0K™Kšœ¡ ˜Kšœ˜K˜—š œŸœŸœŸœŸœ˜0KšœŸœ ŸœŸœ˜"K˜—Kšœ Ÿœ&˜3šœŸœŸœ"˜?K˜'K˜6K˜K˜2KšœŸœ˜KšœŸœ˜K˜!K˜Kšœ ŸœŸœ˜šŸœŸœŸœ˜Kšœ¡$˜;K˜VK™KKšœ Ÿœ˜K˜—šŸœ˜K˜K˜&K˜(K˜—K˜LKšœ%Ÿœ˜,K˜K˜—Kšœ Ÿœ ˜Kšœ Ÿœ ˜K˜šœŸœŸœ+˜IK˜'K˜6K˜K˜2KšœŸœ˜KšœŸœ˜K˜K˜šŸœŸœŸœ˜Kšœ¡$˜;K˜VK™KK˜—šŸœ˜K˜K˜&K˜—K˜AKšœ%Ÿœ˜,K˜K˜—šœŸœŸœŸœ˜@K˜'K˜6K˜šŸœŸœŸœ™K™"Kšœ%™%K™—KšŸœ7Ÿœ™BK˜/Kšœ%Ÿœ˜,K˜K˜—š œŸœŸœ6ŸœŸœ˜|K˜'K˜K˜6K˜2KšœŸœ˜KšœŸœ˜K˜lKšœ%Ÿœ˜,K˜K˜—šœ'˜8KšŸœŸœŸœ™?KšœŸœ ˜$KšŸœ"˜(K˜K˜—šÐbn œŸœ"Ÿœ˜PK™-Kšœ˜K˜\Kš œŸœŸœŸœTŸœ˜‡K˜9K˜K˜—š œŸœŸœ!¡œ ŸœŸœ˜wK˜K˜6Kšœ ŸœŸœ˜KšœŸœŸœ˜(K˜KšœœB˜fKšœ%Ÿœ˜,K˜K˜—š œŸ œŸœŸœŸœ˜DK˜'K˜K˜6K˜2KšœŸœ˜KšœŸœ˜K˜!K˜Kšœ ŸœŸœ˜KšœŸœŸœ˜(K˜šŸœŸœŸœ˜K˜K˜VK˜—šŸœ˜K˜K˜&K˜—K˜K˜ºKšœ%Ÿœ˜,K˜K˜—šœŸœ+ŸœŸœ˜`K˜K˜šŸœŸ˜KšŸœ!˜"KšŸœ!˜"KšŸœŸœ˜—K˜K˜—šœŸœŸœŸœ˜DK˜%šŸœŸ˜KšŸœ˜KšŸœ˜KšŸœŸœ˜—K˜K˜—šœŸœŸœ˜>KšœLŸœ ˜]K˜K˜—šœŸœ3˜HKšœ4™4KšŸœŸœŸœ¡%˜SšŸœŸ˜šœ ˜ K˜PK˜PKšœ˜—Kšœ Ÿœ˜KšŸœŸœ˜—K˜)K˜2KšŸœ˜K˜—K™K™K™šœŸ œŸœ˜SšœŸœ Ÿœ Ÿœ ŸœŸœŸœŸœŸœŸœŸœ˜yšŸœŸœ˜Kš œŸœŸœŸœŸœ˜&KšœŸœ1˜LK˜—K˜—šœŸœ Ÿœ Ÿœ ŸœŸœŸœŸœŸœŸœŸœ˜zšŸœŸœ˜Kš œŸœŸœŸœŸœ˜&KšœŸœ˜6K˜—K˜—šœŸœ Ÿœ Ÿœ ŸœŸœŸœŸœŸœŸœŸœ˜yšŸœŸœ˜Kš œŸœŸœŸœŸœ˜&KšœŸœ˜8K˜—K˜—šœŸœ Ÿœ Ÿœ ŸœŸœŸœŸœŸœŸœŸœ˜|šŸœŸœ˜Kš œŸœŸœŸœŸœ˜&KšœŸœ ˜>K˜—K˜—Kš œŸœŸœŸœŸœŸœ˜9šŸœŸ˜"KšŸœL˜P—šŸœŸ˜#KšŸœN˜R—šŸœ Ÿ˜%KšŸœR˜V—šŸœŸ˜"KšŸœL˜P—K˜1K˜—K˜KšœŸœ˜,šœŸœŸœ.Ÿœ˜YšŸœŸ˜Kšœ ŸœŸœŸœL˜~Kšœ ŸœŸœŸœL˜~Kšœ Ÿœ ŸœŸœM˜KšœŸœ"ŸœŸœO˜‹KšŸœ˜—K˜K˜—šœŸœŸœ*˜OšœŸœ Ÿœ Ÿœ ŸœŸœŸœŸœŸœ ŸœŸœŸœ˜†K˜K˜—K˜AKšœ@˜@K˜K˜—šœŸœ'Ÿœ!˜jšœ ŸœŸ˜Kšœ%˜%Kšœ%˜%Kšœ'˜'Kšœ/˜/KšŸœŸœ˜—K˜K˜—šœŸœŸœŸœŸœ ŸœŸœŸœ˜“š œŸœ Ÿœ Ÿœ ŸœŸœŸœŸœŸœ ŸœŸœŸœ ŸœŸœŸœŸœ˜ºKš œŸœŸœŸœŸœ˜&Kšœ Ÿœ ˜šŸœŸœŸœ˜&Kšœ Ÿœ˜K˜K˜KšœŸœ˜ K˜—KšŸœ˜K˜K˜—K˜AK˜Kšœ;˜;K˜K˜—šœŸœŸœŸœ ŸœŸœŸœŸœŸœŸœ˜eK™7šœŸœ Ÿœ Ÿœ ŸœŸœŸœŸœŸœŸœŸœ˜sKš œŸœŸœŸœŸœ˜&K˜?K˜6K˜—Kšœ ŸœŸœŸœ˜Kšœ ŸœŸœŸœ˜K˜/K˜*KšŸœŸœŸœF˜oKšœ˜K˜K˜—š œŸœŸœŸœ Ÿœ ŸœŸœ˜]Kš ŸœŸœŸœŸœŸœ˜;Kšœ   œ8˜MšŸœŸœ˜&K•StartOfExpansionB[gargoyleData: REF ANY, msg: ROPE, msgType: GGError.MsgType]šœ[˜[K˜Kšœ Ÿœ˜KšŸœ˜K˜—K˜&Kš ™KšŸœ Ÿœ˜,KšŸœŸœ˜$Kš '™'KšŸœBŸœ)Ÿœ)˜žKšœ˜K˜—š œŸœŸœŸœ˜>K˜(KšŸœŸœŸœB˜iK˜K˜—š œŸ œŸœ ŸœŸœ˜LKšŸœŸœŸœ Ÿ˜5KšŸœ5˜9K˜K˜—šœŸœŸœŸœŸœŸœŸœŸœ˜gKšœ/˜/Kš ŸœŸœŸœŸœŸœ˜7Kš œSŸœ ŸœŸœŸœŸœ˜›Kšœ Ÿœ˜$K˜K˜—š"œŸœŸœ ŸœŸœŸœŸœ ŸœŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜¡KšœŸœŸœ˜Kšœ Ÿœ˜KšœŸœŸœ˜K˜6šŸœŸœ ŸœŸ˜6Kš œŸœ ŸœŸœŸœ ˜GKš œŸœ ŸœŸœŸœŸœ5™XKš œ ŸœŸœŸœŸœŸœ ˜.Kš œCŸœŸœŸœŸœ.˜‰KšŸœ ŸœŸœ˜'KšŸœŸœŸœ˜KšŸœ˜—K˜K˜—š œŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜]KšœŸœŸœ˜*Kšœ ŸœŸœŸœ˜KšœŸœŸœŸœ˜KšŸœŸœŸœŸœ˜1Kšœ-Ÿœ˜?K˜0Kšœ*Ÿœ˜AKšœ9Ÿœ˜@Kšœ]Ÿœ˜nK˜K˜—š œŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜]Kšœ ŸœŸœ˜!KšŸœŸœŸœŸœ˜1Kšœ-Ÿœ˜?Kšœ]Ÿœ˜nK˜—K˜šœŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜ŽK™ŽKšœŸœŸœŸœ˜ KšœŸœŸœ˜Kšœ˜šŸ˜K˜9KšŸœŸœŸœ˜šŸœ Ÿ˜K˜HK˜K˜ KšœŸœŸœ˜/šŸœŸœŸœ˜ K˜@K˜K˜—šŸœ˜K˜@K˜K˜—K˜K˜—KšŸœŸœ˜—KšŸœŸœŸœ0˜LKšŸœ˜—K˜K˜—š œŸœŸœŸœŸœŸœŸœ$ŸœŸœ˜Kšœ Ÿœ ˜šŸœ ŸœŸœ˜Kš Ÿœ ŸœŸœ ŸœŸœ˜9KšŸœŸœ˜ K˜—KšŸœ ŸœŸœŸœ˜-šŸœ Ÿœ˜KšŸœ1Ÿœ˜FKšŸœŸœ1Ÿœ˜NKšŸœ˜K˜—šŸœ˜KšŸœ1Ÿœ˜IKšŸœŸœ1Ÿœ˜KKšŸœ˜K˜—K˜K˜—š œŸœŸœŸœŸœ"ŸœŸœ˜ƒK™WKšœ¡˜,Kšœ Ÿœ¡˜#KšŸœŸœŸœ'Ÿœ˜GK˜Kšœ¡!˜2Kšœ¡˜)K˜K˜—šœŸœŸœŸœ ŸœŸœŸœŸœŸœŸœ˜ešœŸœ Ÿœ Ÿœ ŸœŸœŸœŸœŸœŸœŸœ˜sKš œŸœŸœŸœŸœ˜&K˜?K˜6K˜—Kšœ ŸœŸœŸœ˜Kšœ ŸœŸœŸœ˜K˜/K˜*KšŸœŸœŸœF˜oKšœ˜K˜—š œŸœŸœŸœ Ÿœ ŸœŸœ˜]Kš ŸœŸœŸœŸœŸœ˜;K˜MšŸœŸœ˜K–B[gargoyleData: REF ANY, msg: ROPE, msgType: GGError.MsgType]šœ[˜[K˜Kšœ Ÿœ˜KšŸœ˜K˜—K˜&KšŸœŸœ˜$Kš 7™7KšŸœBŸœ)Ÿœ)˜žKšœ˜K˜—š œŸœŸœŸœ˜>K˜(KšŸœŸœŸœB˜iK˜K˜—š œŸ œŸœ ŸœŸœ˜LKšŸœŸœŸœ Ÿ˜5KšŸœ5˜9K˜K˜—šœŸœŸœŸœŸœŸœŸœŸœ˜gKšœ/˜/Kš ŸœŸœŸœŸœŸœ˜7Kš œoŸœ ŸœŸœŸœŸœ!˜¾Kšœ Ÿœ˜$K˜K˜—š œŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜]KšœŸœŸœ˜*Kšœ ŸœŸœŸœ˜KšœŸœŸœŸœ˜KšŸœŸœŸœŸœ˜1Kšœ-Ÿœ˜?K˜0Kšœ*Ÿœ˜AKšœ9Ÿœ˜@Kšœ]Ÿœ˜nK˜K˜—š œŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜]Kšœ ŸœŸœ˜!KšŸœŸœŸœŸœ˜1Kšœ-Ÿœ˜?Kšœ]Ÿœ˜nK˜—K˜šœŸœŸœŸœ ŸœŸœŸœ ŸœŸœŸœŸœŸœŸœ˜€K™>š œŸœ Ÿœ Ÿœ ŸœŸœŸœŸœŸœŸœŸœ˜tKš œŸœŸœŸœŸœ˜&Kšœ&˜&K˜?K˜6K˜—K˜Kšœ ŸœŸœŸœ˜Kšœ ŸœŸœŸœ˜K˜%K˜/K˜*KšŸœ ŸœŸœH˜rK˜Kšœ˜K˜—šœŸœŸœŸœ Ÿœ ŸœŸœ˜]Kš ŸœŸœŸœŸœŸœ˜K˜(KšŸœŸœŸœB˜jK˜K˜—š œŸ œŸœ ŸœŸœ˜MKšŸœŸœŸœ Ÿ˜6KšŸœ6˜:K˜K˜—š œŸœŸœŸœ ŸœŸœŸœŸœŸœ˜xKšœ/˜/Kš Ÿœ ŸœŸœŸœŸœ˜8Kš œ~ŸœŸœŸœŸœ ˜¿Kšœ Ÿœ˜$K˜K˜—š œŸœŸœŸœŸœŸœ ŸœŸœŸœŸœŸœŸœŸœ˜vKšœŸœŸœ˜*Kšœ ŸœŸœŸœ˜Kšœ ŸœŸœŸœ˜KšœŸœŸœŸœ˜KšŸœ ŸœŸœŸœ˜2K˜@K˜;K˜GKšœ9Ÿœ˜?Kšœ^Ÿœ˜oK˜K˜—š œŸœŸœŸœŸœŸœ ŸœŸœŸœŸœŸœŸœŸœ˜vKšœ ŸœŸœ˜!KšŸœ ŸœŸœŸœ˜2K˜@Kšœ^Ÿœ˜oK˜—K˜šœŸœŸœŸœ!˜[K˜*K˜—šœŸœŸœ1˜RK˜*K˜—K˜šœŸœŸœŸœ ŸœŸœŸœ ŸœŸœŸœŸœŸœŸœ˜†K™>š œŸœ Ÿœ Ÿœ ŸœŸœŸœŸœŸœŸœŸœ˜vKš œŸœŸœŸœŸœ˜&Kšœ&˜&K˜?K˜6K˜—K˜Kšœ ŸœŸœŸœ˜Kšœ ŸœŸœŸœ˜K˜%K˜/K˜*KšŸœ"ŸœŸœL˜xK˜Kšœ˜K˜—šœŸœŸœŸœ Ÿœ ŸœŸœ˜eKš Ÿœ ŸœŸœŸœŸœ˜>K˜QšŸœŸœ˜ K–B[gargoyleData: REF ANY, msg: ROPE, msgType: GGError.MsgType]šœc˜cK˜Kšœ Ÿœ˜KšŸœ˜K˜—Kš :™:KšŸœFŸœ-Ÿœ-˜ªKšœ˜K˜—šœŸœŸœŸœ˜FK˜,KšŸœ ŸœŸœF˜pK˜K˜—š œŸ œŸœ ŸœŸœ˜SKšŸœŸœŸœ Ÿ˜8KšŸœ8˜˜_K˜—šŸœ˜Kšœ0Ÿœ˜BKšŸœŸœŸœ$Ÿœ˜GK˜—KšŸœ ŸœŸœ˜*K˜K˜—š œŸœŸœŸœŸœ™;KšŸœ!ŸœŸœŸœ™KKšŸœŸœ<™GK™K™—š œŸœŸœŸœ˜?K™bKšœŸœ˜ Kšœ ŸœŸœ˜KšŸœ!ŸœŸœ$˜OKšŸœF˜JšŸœ Ÿœ˜KšœŸœŠ˜£KšœŸœ>˜_K˜—šŸœ˜Kšœ0Ÿœ˜BKšŸœŸœŸœ$Ÿœ˜GK˜—K˜Kš˜—K˜K™K™š œŸœŸœŸœ˜CKšŸœ˜Kšœ˜K˜—š œŸœŸœ,˜DK˜Kšœ˜K˜—šœŸœŸœŸœ˜IšœŸœŸ˜Kšœ˜Kšœ˜Kšœ˜KšŸœ ˜—K˜K˜—šœŸœŸœŸœ˜IšœŸœŸ˜Kšœ˜Kšœ˜KšŸœŸœ˜—K˜K˜—šœŸœŸœŸœ˜HKšœ=™=Kš ŸœŸœŸœ ŸœŸœŸœ˜QšŸœŸ˜KšœŸœH˜PKšœŸœ˜ Kš ŸœŸœŸœŸœ ¡#˜DKšœŸœ˜KšŸœ˜#K˜—Kšœ˜K˜—šœŸœŸœ1˜MKšœ=™=Kšœ Ÿœ&˜4Kš ŸœŸœŸœ ŸœŸœŸœ˜KKšœO˜OK˜*Kšœ˜K˜—šœŸœŸœŸœ˜BKšœŸœ˜K˜KšœŸœ˜ Kš ŸœŸœŸœ ŸœŸœŸœ˜KK˜KKšœ Ÿœ˜šŸœ Ÿœ˜šœ ŸœŸ˜Kšœ#˜#Kšœ˜Kšœ˜KšŸœ˜—K˜—šŸœ˜šœ ŸœŸ˜Kšœ˜Kšœ#˜#Kšœ˜KšŸœ˜—K˜—KšœO˜OšŸœ Ÿ˜šœ˜K˜K˜Kšœ˜—šœ˜K˜K˜Kšœ˜—šœ ˜ K˜K˜Kšœ˜—KšŸœŸœ˜—K˜K˜—šœŸœŸœ1™MKš Ðbk £ £ £ £ £ £™"Kšœ<™˜K˜,K˜4K˜2K˜—šœŸœŸœ Ÿœ>˜„K˜,K˜4K˜2K˜—šœŸœŸœŸœ˜MK˜ K˜—šœŸœŸœ)˜DK˜ K˜—K˜K™K™š œŸœŸœŸœŸœ˜;KšŸœŸœŸœ ŸœŸœŸœŸœ˜PšŸœŸ˜KšœŸœD˜LKšœ ŸœŸ˜Kš ŸœŸœŸœŸœŸœ¡#˜CKšœŸœ˜KšŸœ˜K˜—K˜K˜—š œŸœŸœŸœ˜=Kš ŸœŸœŸœŸœŸœ˜KKšœ#Ÿœ ŸœŸœ&˜dKšœ˜K˜K˜—š œŸœŸœŸœŸœ™;Kš ŸœŸœŸœŸœŸœ™5KšŸœŸœ9™DK™K™—š œŸœŸœŸœ™=KšŸœŸœŸœŸœ™0KšœB™BKšœ™K™K™—š œŸœŸœŸœ Ÿœ˜IKšœ Ÿœ˜Kš ŸœŸœŸœ ŸœŸœ5˜xšŸœŸ˜KšœŸœJ˜RKšŸœŸœŸœ5˜FšŸœ˜Kšœ ŸœŸœŸœ˜$Kšœ˜—K˜—K˜K˜K˜—šœŸœŸœŸœ˜@Kšœ Ÿœ˜Kšœ ŸœŸœ˜K˜Kš ŸœŸœŸœŸœŸœ˜KK˜Kšœ ŸœŸœ˜!KšœP˜PK˜K˜K˜K˜—š œŸœŸœŸœ Ÿœ™IKšœ Ÿœ™KšœT™TK™KšŸœŸœŸœ5™PšŸœ™Kšœ(™(KšœŸœ4™@Kšœ™K™—Kšœ™K™K™—šœŸœŸœŸœ™@Kšœ Ÿœ™KšœT™TK™(K™KšŸœŸœŸœŸœ™#Kšœ™KšœŸœ?™HKšœB™BKšœ™K™K˜—K˜šœŸœŸœŸœ˜MšœŸœŸœŸ˜Kšœ(˜(Kšœ&˜&KšŸœ˜—K˜K˜—šœŸœŸœŸœ˜MšœŸœ Ÿ˜Kšœ!˜!Kšœ/˜/KšŸœŸœ˜—K˜K˜—šœŸœŸœŸœ˜SKš ŸœŸœŸœ ŸœŸœŸœ˜[šŸœŸ˜KšœŸœH˜PKšœŸœ˜ Kš ŸœŸœŸœŸœ¡#˜NKšœŸœ˜KšŸœ˜"K˜—K˜K˜—šœŸœŸœ/˜JKšœŸ˜ Kš ŸœŸœŸœŸœŸœ˜KK˜(KšœK˜KK˜)Kšœ˜K˜K˜—šœŸœŸœŸœ˜AKšœ˜Kš ŸœŸœŸœŸœŸœ˜KK˜%KšœŸœŸœŸœ˜XKšœ$˜$K˜)Kšœ˜K˜K˜—šœŸœŸœŸœ™AKšœ@™@šŸœŸœŸœ™KšŸœ ŸœŸœ™DKšŸœŸœ™;K™—šœ™šŸœŸœŸ™Kšœ0Ÿœ™IKšœ/Ÿœ™GKšŸœŸœ™——Kšœ™K™K™—šœŸœŸœŸœ™SKšŸœ™#K™K™—šœŸœŸœ/™JKš £ £ £ £ £ £ £™"Kšœ@™@KšœŸœ™šœ Ÿœ Ÿ™"Kšœ"™"Kšœ0™0KšŸœŸœ™—šŸœŸœŸœ™Kšœ™šŸœŸœŸ™+KšœŸœ™5Kšœ™KšŸœ™—K™—Kšœ)™)Kšœ™K™K™—š œŸœŸœŸœŸœ˜>KšŸœŸœŸœ ŸœŸœŸœŸœ˜QšŸœŸ˜KšœŸœA˜IKšœ ŸœŸ˜Kš ŸœŸœŸœŸœŸœ¡#˜DKšœŸœ˜KšŸœ˜K˜—K˜K˜—š œŸœŸœ Ÿœ˜CKš ŸœŸœŸœŸœŸœ˜KKšœ ŸœŸœŸœ&˜dK˜K˜—š œŸœŸœŸœŸœ™>Kš Ÿœ$ŸœŸœŸœŸœ™¡™RK˜—KšŸœ˜—K˜—K˜šœŸœ˜,šŸœŸ˜šŸœŸœŸ˜*KšœD˜DKšœR˜RKšŸœŸœ˜—KšŸœ5Ÿœ¡˜TKšŸœ˜—K˜—K˜K™K˜šœŸœŸœŸœ˜TKšŸœ˜"K˜—šœŸœŸœ.˜KK˜#K˜—K™K™K˜š œŸœŸœŸœ˜PKšŸœ˜ K˜K˜—š œŸœŸœ-˜GK˜%K˜K˜—š œŸœŸœŸœ˜PKšŸœ˜K˜K˜—š œŸœŸœ-˜GK˜$K˜K˜—š œŸœŸœŸœŸœ˜5KšŸœ˜K˜K˜—šœŸœŸœŸœ˜-K˜K˜—K˜KšœŸœŸœ¡˜=K˜šœŸœŸœŸœ#˜[K˜4K˜K˜—šœŸœŸœ3˜RK˜4K˜K˜—K™K˜KšœŸœŸœ™"š œŸœŸœŸœŸœ™6KšŸœ™K™—K˜K™š œŸ œŸœ Ÿœ˜FK™5K™&Kš ŸœŸœŸœŸœŸœ˜*KšŸœŸœŸœŸœŸœŸœŸœŸœ˜†Kš œ ŸœŸœŸœŸœ˜oK˜K˜—š œŸœŸœŸœŸœ˜WKšœ Ÿœ*˜:KšœŸœŸœ Ÿœ;˜zK˜K˜—š œŸœŸœŸœŸœ˜UKš œ%˜2K˜K˜—š œŸœŸœ˜/KšœŸœ˜%KšŸœŸœŸœŸœŸœŸœŸœŸœŸœŸœ˜…KšœŸœŸœŸœ˜(K˜BK˜*K˜HK˜K˜—š œŸœŸœŸœŸœŸœ˜JK™KšœŸ˜ Kš ŸœŸœŸœŸœŸœ˜,KšŸœŸœŸœŸœŸœŸœŸœ ˜„Kš ŸœŸœŸœŸœŸœ˜pKšŸœŸœŸœ"Ÿœm˜¶KšŸœŸœŸœŸœn˜KšŸœŸœ˜K˜K˜—šœŸœ%Ÿœ˜DKšŸœŸœŸœŸœ˜%K˜ KšŸœŸœŸœŸœŸœŸœŸœŸœ˜lK˜_K˜_K˜cK˜K˜—š œŸ œŸœ˜IK˜ K˜K˜—šœŸ œ˜0K˜:K˜K˜—š œŸ œŸœ Ÿœ¡"˜bK˜*K˜K˜—š œŸ œŸœ Ÿœ¡"˜dK˜+K˜K™—šœŸœŸœŸœ˜:Kšœ Ÿœ˜Kšœ ˜ Kšœ Ÿœ˜šŸœ Ÿ˜K˜;K˜;K˜7KšŸœ3˜:—šŸœ˜ K™KšŸœ>ŸœŸœ¡˜cKšŸœŸœ%˜>K˜—šŸœ˜Kšœ<Ÿœ1Ÿœ˜xK™Kšœ\˜\K˜—Kšœ@˜@Kšœ'Ÿœ Ÿœ¡˜WKšœ)Ÿœ˜0Kšœ ¡˜7KšœC˜CK˜K˜—š œŸ œŸœ ŸœŸœ˜OKšœ˜K˜KšœŸœ˜Kšœ=™=K˜GKšœc˜c˜3šœŸœ ˜Kšœ Ÿœ˜K˜=KšŸœ˜ —šœ˜Kšœ Ÿœ˜K˜CKšŸœ˜ ——šŸœ Ÿœ˜KšŸœ ŸœŸœŸœ˜Kšœ7˜7Kšœo™oK˜Kšœ˜—KšŸœ;˜?K˜K˜—šœŸ œ˜1šœ˜Kšœ#˜#Kšœ ˜ Kšœ˜Kšœ Ÿœ˜—K˜K˜—š œŸ œ-™DKšœŸœŸœ™Kšœ™K™gKš ŸœŸœ3Ÿœ Ÿœ Ÿœ™jKšœI™IK™—K˜šœŸ œŸœ˜;šŸœœŸœ˜,K˜LšŸœ$ŸœŸœ?˜tKšœd˜d—K˜—KšŸœŸœŸœŸœŸœŸœŸœŸœ˜dšŸœŸœ#Ÿœ˜/Kš 4™4Kšœ/˜/K˜QKš œŸœŸœŸœŸœ ˜gKšœ#Ÿœ¡*˜SKšœ$Ÿœ˜)šŸœ!ŸœŸœ¡˜?Kšœ. œ˜7—šŸœ-Ÿœ˜6šŸœ ŸœŸœ¡˜>Kšœ- œ˜6—K˜—K˜—K˜K˜—KšŸœ˜K˜K˜—…—áðHÞ