DIRECTORY Ascii, Atom, AtomButtonsTypes, BasicTime, CodeTimer, ColorTool, Convert, Cubic2, CubicSplines, Feedback, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGDragTypes, GGEvent, GGEventExtras, GGFileOps, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGOutline, GGParent, GGParseIn, GGProps, GGRefresh, GGRefreshTypes, GGScene, GGSceneType, GGSegment, GGSegmentTypes, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGTransform, GGUserInput, GGUserProfile, GGUtility, GGWindow, Imager, ImagerInterpress, ImagerTransformation, IO, Prop, Real, Rope, SF, SlackProcess, Vectors2d; GGEventImplC: CEDAR PROGRAM IMPORTS Atom, BasicTime, CodeTimer, ColorTool, Convert, Feedback, GGAlign, GGBoundBox, GGCaret, GGEvent, GGEventExtras, GGFileOps, GGHistory, GGMouseEvent, GGOutline, GGParent, GGParseIn, GGProps, GGRefresh, GGScene, GGSegment, GGSelect, GGSequence, GGSlice, GGSliceOps, GGState, GGTraj, GGTransform, GGUserInput, GGUserProfile, GGUtility, GGWindow, Imager, ImagerInterpress, ImagerTransformation, IO, Prop, Rope, SlackProcess, Vectors2d EXPORTS GGEvent, GGModelTypes, GGInterfaceTypes = BEGIN DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj; -- exported to GGInterfaceTypes BitVector: TYPE = GGModelTypes.BitVector; BoundBox: TYPE = GGModelTypes.BoundBox; Caret: TYPE = GGInterfaceTypes.Caret; Color: TYPE = Imager.Color; ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator; FeatureCycler: TYPE = GGInterfaceTypes.FeatureCycler; FeatureData: TYPE = GGModelTypes.FeatureData; GGData: TYPE = GGInterfaceTypes.GGData; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; Joint: TYPE = GGSegmentTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; Point: TYPE = GGBasicTypes.Point; RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; ROPE: TYPE = Rope.ROPE; Scene: TYPE = GGModelTypes.Scene; SceneObj: PUBLIC TYPE = GGSceneType.SceneObj; -- export of opaque type SceneRef: TYPE = REF SceneObj; Segment: TYPE = GGSegmentTypes.Segment; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; Sequence: TYPE = GGModelTypes.Sequence; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorWalkProc: TYPE = GGModelTypes.SliceDescriptorWalkProc; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceParts: TYPE = GGModelTypes.SliceParts; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajPartType: TYPE = GGModelTypes.TrajPartType; TrajParts: TYPE = GGModelTypes.TrajParts; UserInputProc: TYPE = GGEvent.UserInputProc; Vector: TYPE = GGBasicTypes.Vector; WalkProc: TYPE = GGModelTypes.WalkProc; reallyBigReal: REAL = 1.0e37; PaintActionArea: PUBLIC UserInputProc = { GGState.PaintActionArea[ggData]; }; UpdateCursor: UserInputProc = { Feedback.Append[ggData.router, oneLiner, $Error, "GGEventImplC.UpdateCursor was called. Please notify a Gargoyle implementor."]; }; ToggleActive: UserInputProc = { GGState.SetActive[ggData, NOT GGState.GetActive[ggData]]; }; SetActive: UserInputProc = { IF event.rest # NIL THEN WITH event.rest.first SELECT FROM r: ROPE => { b: BOOL ¬ Convert.BoolFromRope[r]; GGState.SetActive[ggData, b]; }; ENDCASE => Feedback.PutF[ggData.router, oneLiner, $Complaint, "SetActive got unexpected value %g", [refAny[event.rest.first]] ]; }; TogglePalette: UserInputProc = { GGState.SetPalette[ggData, NOT GGState.GetPalette[ggData]]; }; SaveSelections: UserInputProc = { ggData.behavior.savedSelections ¬ GGUtility.CopySliceDescriptorList[GGSelect.ListSelected[ggData.scene, normal]]; }; RestoreSelections: UserInputProc = { GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE]; GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SetSelected[ggData.scene, normal, ggData.behavior.savedSelections]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, selectionChanged: TRUE, okToSkipCapture: TRUE]; }; SelectButton: UserInputProc = { sliceD: SliceDescriptor ¬ NARROW[event.rest.first]; GGSelect.SelectSlice[sliceD, ggData.scene, normal]; }; FeedbackOn: UserInputProc = { IF ggData.router#NIL THEN Feedback.SetRouterOn[ggData.router, TRUE]; }; FeedbackOff: UserInputProc = { IF ggData.router#NIL THEN Feedback.SetRouterOn[ggData.router, FALSE]; }; BeginButton: UserInputProc = { sliceD: SliceDescriptor ¬ NARROW[event.rest.first]; ggData.behavior.savedSelections ¬ GGUtility.CopySliceDescriptorList[GGSelect.ListSelected[ggData.scene, normal]]; GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SelectSlice[sliceD, ggData.scene, normal]; IF ggData.router#NIL THEN { Feedback.SetRouterOn[ggData.router, FALSE]; }; DisableRefresh[ggData, event]; }; EndButton: UserInputProc = { GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SetSelected[ggData.scene, normal, ggData.behavior.savedSelections]; IF ggData.router#NIL THEN { Feedback.SetRouterOn[ggData.router, TRUE]; }; EnableRefresh[ggData, event]; }; ButtonFillColorFromIntensity: UserInputProc = { buttonEvent: LIST OF REF ANY ¬ LIST[event.first, event.rest.rest.first]; CodeTimer.StartInt[$ButtonFillColorFromIntensity, $Gargoyle]; BeginButton[ggData, buttonEvent]; GGEventExtras.SetFillColorFromIntensity[ggData, NARROW[event.rest.first, REF REAL]­]; EndButton[ggData, buttonEvent]; CodeTimer.StopInt[$ButtonFillColorFromIntensity, $Gargoyle]; }; ButtonStrokeColorFromIntensity: UserInputProc = { buttonEvent: LIST OF REF ANY ¬ LIST[event.first, event.rest.rest.first]; CodeTimer.StartInt[$ButtonStrokeColorFromIntensity, $Gargoyle]; BeginButton[ggData, buttonEvent]; GGEventExtras.SetStrokeColorFromIntensity[ggData, NARROW[event.rest.first, REF REAL]­]; EndButton[ggData, buttonEvent]; CodeTimer.StopInt[$ButtonStrokeColorFromIntensity, $Gargoyle]; }; SetProp: UserInputProc = { SetPropOnSlice: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { val ¬ IF valRope=NIL THEN NIL ELSE GGProps.FromRope[key, valRope]; -- must do this for every slice in case, for instance, EmbeddedButtons wants to give each button a unique name GGProps.Put[sliceD.slice, sliceD.parts, key, val]; }; rope: ROPE ¬ NARROW[event.rest.first]; s: IO.STREAM ¬ IO.RIS[rope]; keyRope, valRope: ROPE; val: REF; key: ATOM; keyRope ¬ GGParseIn.ReadWord[s]; IF keyRope = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetProp failed: Please select a key name"]; RETURN; }; GGParseIn.ReadBlank[s]; valRope ¬ IO.GetRope[s]; key ¬ Atom.MakeAtom[keyRope]; [] ¬ GGScene.WalkSelectedSlices[ggData.scene, leaf, SetPropOnSlice, normal]; GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: TRUE]; }; GetSelectedVal: PROC [key: ATOM, ggData: GGData, opName: ROPE] RETURNS [val: REF, success: BOOL ¬ TRUE, sliceD: SliceDescriptor ¬ NIL] = { aborted: BOOL ¬ FALSE; scene: Scene ¬ ggData.scene; CheckVal: PROC [thisD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { thisVal: REF; IF sliceD = NIL THEN { sliceD ¬ thisD; [val, success] ¬ GGProps.Get[thisD.slice, thisD.parts, key]; done ¬ NOT success; } ELSE { [thisVal, success] ¬ GGProps.Get[thisD.slice, thisD.parts, key]; IF NOT success OR thisVal # val THEN done ¬ TRUE }; }; aborted ¬ GGScene.WalkSelectedSlices[scene, first, CheckVal, normal]; IF aborted THEN { success ¬ FALSE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: there is more than one value for this key", [rope[opName]] ]; } ELSE IF sliceD = NIL THEN { success ¬ FALSE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no objects are selected", [rope[opName]] ]; }; }; GetProp: UserInputProc = { keyRope: ROPE ¬ NARROW[event.rest.first]; key: ATOM ¬ Atom.MakeAtom[keyRope]; val: REF; success: BOOL ¬ FALSE; sliceD: SliceDescriptor; [val, success, sliceD] ¬ GetSelectedVal[key, ggData, "GetProp"]; IF NOT success THEN RETURN; IF val#NIL THEN { valRope: ROPE; valRope ¬ GGProps.ToRope[key, val].r; IF valRope = NIL THEN Feedback.PutF[ggData.router, oneLiner, $Show, "%g ", [rope[keyRope]] ] ELSE Feedback.PutFL[ggData.router, oneLiner, $Show, "%g %g", LIST[[rope[keyRope]], [rope[valRope]]]] } ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "No value for key %g.", [rope[keyRope]]]; }; GetPropOfSelectedSlice: PROC [key: ATOM, ggData: GGData, opName: ROPE] RETURNS [val: REF, success: BOOL ¬ TRUE, error: ROPE ¬ NIL] = { aborted: BOOL ¬ FALSE; sliceD: SliceDescriptor; UniquePropOrAbort: PROC [thisD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { thisVal: REF; IF sliceD = NIL THEN { sliceD ¬ thisD; [val, success] ¬ GGProps.Get[thisD.slice, thisD.parts, key]; done ¬ NOT success; } ELSE { [thisVal, success] ¬ GGProps.Get[thisD.slice, thisD.parts, key]; IF NOT success OR thisVal # val THEN done ¬ TRUE }; }; aborted ¬ GGScene.WalkSelectedSlices[ggData.scene, first, UniquePropOrAbort, normal]; IF aborted OR sliceD = NIL THEN error ¬ ReportSelectionProblems[aborted, sliceD, opName, ggData]; }; ReportSelectionProblems: PROC [aborted: BOOL, sliceD: SliceDescriptor, opName: ROPE, ggData: GGData] RETURNS [error: ROPE] = { IF aborted THEN { error ¬ IO.PutFR1["%g failed: there is more than one value for this key", [rope[opName]] ]; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: there is more than one value for this key", [rope[opName]] ]; } ELSE IF sliceD = NIL THEN { error ¬ IO.PutFR1["%g failed: no objects are targeted", [rope[opName]] ]; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no objects are selected", [rope[opName]] ]; }; }; GetPropExternal: UserInputProc = { returnList: LIST OF ROPE; errorRope, valRope: ROPE; extRef: GGUserInput.External ¬ NARROW[event.rest.first]; key: ATOM ¬ NARROW[extRef.results]; keyRope: ROPE ¬ Atom.GetPName[key]; val: REF; success: BOOL ¬ FALSE; [val, success, errorRope] ¬ GetPropOfSelectedSlice[key, ggData, "GetProp"]; IF success THEN { IF val#NIL THEN { valRope ¬ GGProps.ToRope[key, val].r; IF valRope = NIL THEN { Feedback.PutF[ggData.router, oneLiner, $Show, "%g ", [rope[keyRope]] ]; errorRope ¬ IO.PutFR1["%g ", [rope[keyRope]] ]; } ELSE Feedback.PutFL[ggData.router, oneLiner, $Show, "%g %g", LIST[[rope[keyRope]], [rope[valRope]]]] } ELSE { Feedback.PutF[ggData.router, oneLiner, $Show, "No value for key %g.", [rope[keyRope]]]; valRope ¬ IO.PutFR1["No value for key %g.", [rope[keyRope]]]; }; }; returnList ¬ LIST[valRope, errorRope]; extRef.results ¬ returnList; extRef.valid ¬ TRUE; GGUserInput.BroadcastExternal[extRef]; }; RemoveProp: UserInputProc = { RemPropOnSlice: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { [] ¬ GGProps.Rem[sliceD.slice, sliceD.parts, key]; }; keyRope: ROPE ¬ NARROW[event.rest.first]; key: ATOM ¬ Atom.MakeAtom[keyRope]; IF keyRope = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "RemoveProp failed: Please select a key name"]; RETURN; }; [] ¬ GGScene.WalkSelectedSlices[ggData.scene, first, RemPropOnSlice, normal]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "Key %g removed from selected slices.", [rope[keyRope]] ]; }; ListProps: UserInputProc = { success: BOOL ¬ FALSE; propsList: Prop.PropList; first: BOOL ¬ TRUE; PrintKey: PROC [key: REF, val: REF] RETURNS [quit: BOOL ¬ FALSE] = { atom: ATOM ¬ NARROW[key]; keyRope: ROPE ¬ Atom.GetPName[atom]; IF first THEN { Feedback.PutF[ggData.router, begin, $Show, "%g", [rope[keyRope]] ]; first ¬ FALSE; } ELSE Feedback.PutF[ggData.router, middle, $Show, " %g", [rope[keyRope]] ]; }; [propsList, success] ¬ GetSelectedPropsList[ggData, "ListProps"]; IF NOT success THEN RETURN; IF propsList#NIL THEN { [] ¬ Prop.Map[propsList, PrintKey]; Feedback.Append[ggData.router, end, $Show, ""]; } ELSE Feedback.Append[ggData.router, oneLiner, $Show, "No properties."]; }; ListPropsExternal: UserInputProc = { extRef: GGUserInput.External ¬ NARROW[event.rest.first]; returnList: LIST OF ATOM; success: BOOL ¬ FALSE; propsList: Prop.PropList; first: BOOL ¬ TRUE; GetSelectedPropsExt: PROC [ggData: GGData, opName: ROPE] RETURNS [propsList: Prop.PropList, success: BOOL ¬ TRUE, error: ROPE] = { sliceD: SliceDescriptor ¬ NIL; aborted: BOOL ¬ FALSE; scene: Scene ¬ ggData.scene; CheckPropsList: PROC [thisD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { thisList: Prop.PropList; IF sliceD = NIL THEN { sliceD ¬ thisD; propsList ¬ sliceD.slice.props; } ELSE { thisList ¬ thisD.slice.props; IF thisList # propsList THEN done ¬ TRUE }; }; aborted ¬ GGScene.WalkSelectedSlices[scene, leaf, CheckPropsList, normal]; IF aborted THEN { success ¬ FALSE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: there is more than one properties list selected", [rope[opName]] ]; } ELSE IF sliceD = NIL THEN { success ¬ FALSE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no objects are selected", [rope[opName]] ]; }; }; PrintKey: PROC [key: REF, val: REF] RETURNS [quit: BOOL ¬ FALSE] = { atom: ATOM ¬ NARROW[key]; keyRope: ROPE ¬ Atom.GetPName[atom]; IF first THEN { Feedback.PutF[ggData.router, begin, $Show, "%g", [rope[keyRope]] ]; first ¬ FALSE; } ELSE Feedback.PutF[ggData.router, middle, $Show, " %g", [rope[keyRope]] ]; returnList ¬ CONS[atom, returnList]; }; [propsList, success] ¬ GetSelectedPropsExt[ggData, "ListProps"]; IF success THEN { IF propsList#NIL THEN { [] ¬ Prop.Map[propsList, PrintKey]; Feedback.Append[ggData.router, end, $Show, ""]; } ELSE Feedback.Append[ggData.router, oneLiner, $Show, "No properties."]; }; extRef.results ¬ returnList; extRef.valid ¬ TRUE; GGUserInput.BroadcastExternal[extRef]; }; CopyProps: UserInputProc = { CopyAndSetProps: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { IF sliceD.slice#lastSliceD.slice THEN GGProps.CopyAll[lastSliceD.slice, sliceD.slice, lastSliceD.parts, sliceD.parts]; }; lastSliceD: SliceDescriptor ¬ GGScene.LastSelectedSlice[ggData.scene, leaf, normal]; [] ¬ GGScene.WalkSelectedSlices[ggData.scene, leaf, CopyAndSetProps, normal]; }; SetRootProp: PUBLIC UserInputProc = { rope: ROPE ¬ NARROW[event.rest.first]; s: IO.STREAM ¬ IO.RIS[rope]; keyRope, valRope: ROPE; val: REF; key: ATOM; keyRope ¬ GGParseIn.ReadWord[s]; IF keyRope = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "SetRootProp failed: Please select a key name"]; RETURN; }; GGParseIn.ReadBlank[s]; valRope ¬ IO.GetRope[s]; key ¬ Atom.MakeAtom[keyRope]; val ¬ IF valRope=NIL THEN NIL ELSE GGProps.FromRope[key, valRope]; GGProps.Put[ggData.rootSlice, NIL, key, val]; GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: TRUE]; }; GetRootProp: PUBLIC UserInputProc = { keyRope: ROPE ¬ NARROW[event.rest.first]; key: ATOM ¬ Atom.MakeAtom[keyRope]; val: REF; success: BOOL ¬ FALSE; [val, success] ¬ GGProps.Get[ggData.rootSlice, NIL, key]; IF NOT success THEN RETURN; IF val#NIL THEN { valRope: ROPE; valRope ¬ GGProps.ToRope[key, val].r; IF valRope = NIL THEN Feedback.PutF[ggData.router, oneLiner, $Show, "%g ", [rope[keyRope]] ] ELSE Feedback.PutFL[ggData.router, oneLiner, $Show, "%g %g", LIST[[rope[keyRope]], [rope[valRope]]]] } ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "No value for key %g.", [rope[keyRope]]]; }; RemoveRootProp: PUBLIC UserInputProc = { keyRope: ROPE ¬ NARROW[event.rest.first]; key: ATOM ¬ Atom.MakeAtom[keyRope]; IF keyRope = NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "RemoveRootProp failed: Please select a key name"]; RETURN; }; [] ¬ GGProps.Rem[ggData.rootSlice, NIL, key]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "Key %g removed from root slice.", [rope[keyRope]] ]; }; ListRootProps: PUBLIC UserInputProc = { success: BOOL ¬ FALSE; propsList: Prop.PropList; first: BOOL ¬ TRUE; PrintKey: PROC [key: REF, val: REF] RETURNS [quit: BOOL ¬ FALSE] = { atom: ATOM ¬ NARROW[key]; keyRope: ROPE ¬ Atom.GetPName[atom]; IF first THEN { Feedback.PutF[ggData.router, begin, $Show, "%g", [rope[keyRope]] ]; first ¬ FALSE; } ELSE Feedback.PutF[ggData.router, middle, $Show, " %g", [rope[keyRope]] ]; }; propsList ¬ ggData.rootSlice.props; IF propsList#NIL THEN { [] ¬ Prop.Map[propsList, PrintKey]; Feedback.Append[ggData.router, end, $Show, ""]; } ELSE Feedback.Append[ggData.router, oneLiner, $Show, "No properties."]; }; GetSelectedPropsList: PROC [ggData: GGData, opName: ROPE] RETURNS [propsList: Prop.PropList, success: BOOL ¬ TRUE] = { sliceD: SliceDescriptor ¬ NIL; aborted: BOOL ¬ FALSE; scene: Scene ¬ ggData.scene; CheckPropsList: PROC [thisD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { thisList: Prop.PropList; IF sliceD = NIL THEN { sliceD ¬ thisD; propsList ¬ sliceD.slice.props; } ELSE { thisList ¬ thisD.slice.props; IF thisList # propsList THEN done ¬ TRUE }; }; aborted ¬ GGScene.WalkSelectedSlices[scene, leaf, CheckPropsList, normal]; IF aborted THEN { success ¬ FALSE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: there is more than one properties list selected", [rope[opName]] ]; } ELSE IF sliceD = NIL THEN { success ¬ FALSE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no objects are selected", [rope[opName]] ]; }; }; ApplyAllDefaults: UserInputProc = { newSelectList, ptr: LIST OF Slice; currentEvent: HistoryEvent; scene: Scene ¬ ggData.scene; DoApplyAllDefaults: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { GGSliceOps.SetDefaults[sliceD.slice, sliceD.parts, ggData.defaults, currentEvent]; [newSelectList, ptr] ¬ GGUtility.AddSlice[sliceD.slice, newSelectList, ptr]; }; IF GGSelect.NoSelections[ggData.scene, normal] THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "ApplyAllDefaults failed: select some objects for ApplyAllDefaults"]; RETURN; }; [newSelectList, ptr] ¬ GGUtility.StartSliceList[]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; currentEvent ¬ GGHistory.NewCurrent["Apply default looks", ggData]; -- start new history event [] ¬ GGScene.WalkSelectedSlices[scene, first, DoApplyAllDefaults, normal]; FOR list: LIST OF Slice ¬ newSelectList, list.rest UNTIL list = NIL DO GGSelect.SelectEntireSlice[list.first, ggData.scene, normal]; ENDLOOP; GGRefresh.EnlargeStartBox[ggData, GGScene.BoundBoxOfSelected[ggData.scene, normal], NIL]; GGHistory.PushCurrent[ggData]; Feedback.Append[ggData.router, oneLiner, $Feedback, "ApplyAllDefaults: all defaults applied"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; SetAllDefaults: UserInputProc = { }; ShowAllDefaults: UserInputProc = { Feedback.Append[ggData.router, oneLiner, $Typescript, "--------------------------------------------"]; GGEvent.ShowDefaultFontValues[ggData, event]; GGEvent.ShowDefaultLineColor[ggData, event]; GGEvent.ShowDefaultFillColor[ggData, event]; GGEvent.ShowDefaultStrokeValues[ggData, event]; GGEvent.ShowDefaultTextLooks[ggData, event]; Feedback.Append[ggData.router, oneLiner, $Typescript, "--------------------------------------------"]; }; StandardDefaults: UserInputProc = { ggData.defaults­ ¬ [ strokeWidth: 2.0, strokeJoint: round, strokeEnd: round, dashed: FALSE, pattern: NIL, offset: 0.0, length: 0.0, strokeColor: Imager.black, fillColor: GGOutline.fillColor, font: GGUserProfile.GetDefaultDefaultFont[], textColor: Imager.black, dropShadowOn: FALSE, dropShadowOffset: [-0.1, -0.1], dropShadowColor: Imager.black ]; ShowAllDefaults[ggData, event]; }; WeldOrBackword: UserInputProc = { IF ggData.refresh.textInProgress=NIL THEN Weld[ggData, event] ELSE { myRefChar: REF CHAR ¬ NEW[CHAR ¬ Ascii.ControlW ]; event ¬ LIST[$AddChar, myRefChar]; GGEvent.AddChar[ggData, event]; }; }; Weld: UserInputProc = { firstTraj, secondTraj, newTraj: Traj; firstOutline, secondOutline, newOutline: Slice; weldPoint: Point; firstEnd, secondEnd: TrajEnd; success: BOOL; firstPriority: INT ¬ 0; cluster: Slice; scene: Scene ¬ ggData.scene; whichClustered: OfTwoArgs ¬ none; GGHistory.NewCapture["Weld", ggData]; -- capture scene BEFORE UPDATE [firstTraj, secondTraj, cluster, whichClustered, success] ¬ GetWeldArguments[ggData]; IF NOT success THEN RETURN; IF secondTraj = NIL THEN success ¬ WeldToSelf[ggData, firstTraj] ELSE { ancestor: Slice; IF cluster # NIL THEN { ancestor ¬ GGParent.GetTopLevelAncestor[cluster]; GGSelect.SaveSelectionsInSliceAllClasses[ancestor, scene]; GGSelect.DeselectEntityAllClasses[ancestor, scene]; }; [firstEnd, secondEnd] ¬ ClosestEnds[firstTraj, secondTraj]; firstOutline ¬ GGParent.GetParent[firstTraj]; secondOutline ¬ GGParent.GetParent[secondTraj]; newTraj ¬ GGTraj.Concat[firstTraj, firstEnd, secondTraj, secondEnd]; newOutline ¬ GGOutline.CreateOutline[newTraj, GGSliceOps.GetFillColor[firstOutline, NIL].color]; IF cluster = NIL THEN { -- both are top level firstPriority ¬ GGScene.GetPriority[scene, firstOutline]; GGScene.DeleteSlice[scene, firstOutline]; GGScene.DeleteSlice[scene, secondOutline]; GGScene.AddSlice[scene, newOutline, firstPriority]; } ELSE { -- one or both are in a cluster firstPriority ¬ GGParent.GetChildPriority[cluster, firstOutline]; GGSelect.DeselectEntityAllClasses[firstOutline, scene]; GGSelect.DeselectEntityAllClasses[secondOutline, scene]; IF whichClustered = first OR whichClustered = both THEN [] ¬ GGSlice.RemoveChild[cluster, firstOutline] ELSE GGScene.DeleteSlice[scene, firstOutline]; IF whichClustered = second OR whichClustered = both THEN [] ¬ GGSlice.RemoveChild[cluster, secondOutline] ELSE GGScene.DeleteSlice[scene, secondOutline]; GGSlice.AddChildToCluster[cluster, newOutline, firstPriority]; }; IF cluster # NIL THEN GGSelect.ReselectSliceAllClasses[ancestor, ggData.scene]; GGEvent.SelectEntireSlice[newOutline, scene, normal, ggData]; weldPoint ¬ GGTraj.FetchJointPos[newTraj, GGTraj.HiJoint[IF firstEnd=hi THEN firstTraj ELSE secondTraj]]; GGCaret.SetAttractor[ggData.caret, weldPoint, [0,-1], NIL]; GGCaret.SitOn[ggData.caret, NIL]; GGRefresh.NullStartBox[ggData]; GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[firstTraj], GGSliceOps.GetBoundBox[secondTraj]]; GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[newTraj], NIL]; }; IF success THEN { GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.Append[ggData.router, oneLiner, $Feedback, "Weld: completed"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; }; OfTwoArgs: TYPE = {none, first, second, both}; GetWeldArguments: PROC [ggData: GGData] RETURNS [firstTraj, secondTraj: Traj, cluster: Slice, whichClustered: OfTwoArgs ¬ none, success: BOOL ¬ FALSE] = { firstOutlineD, secondOutlineD: SliceDescriptor; scene: Scene ¬ ggData.scene; index: NAT ¬ 0; otherCluster: Slice; FirstAndSecond: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { IF index = 0 THEN { firstOutlineD ¬ sliceD; index ¬ index + 1; } ELSE IF index = 1 THEN { secondOutlineD ¬ sliceD; done ¬ TRUE; }; }; argCount: NAT ¬ GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Outline]; IF argCount = 0 OR argCount > 2 THEN GOTO Abort; -- 0 or more than 2 selected [] ¬ GGScene.WalkSelectedSlices[scene, leaf, FirstAndSecond, normal, $Outline]; IF GGOutline.HasHoles[firstOutlineD.slice] OR (secondOutlineD#NIL AND GGOutline.HasHoles[secondOutlineD.slice]) THEN GOTO Abort; -- not simple outlines IF (firstTraj ¬ GGParent.FirstChild[firstOutlineD.slice, first])=NIL OR GGSliceOps.GetType[firstTraj]#$Traj OR NARROW[firstTraj.data, TrajData].role#open THEN GOTO Abort; IF secondOutlineD=NIL THEN { cluster ¬ GGParent.GetParent[firstOutlineD.slice]; whichClustered ¬ IF cluster = NIL THEN none ELSE first; RETURN[firstTraj, NIL, cluster, whichClustered, TRUE]; }; IF (secondTraj ¬ GGParent.FirstChild[secondOutlineD.slice, first])=NIL OR GGSliceOps.GetType[secondTraj]#$Traj OR NARROW[secondTraj.data, TrajData].role#open THEN GOTO Abort; cluster ¬ GGParent.GetParent[firstOutlineD.slice]; otherCluster ¬ GGParent.GetParent[secondOutlineD.slice]; IF cluster = NIL THEN { whichClustered ¬ IF otherCluster = NIL THEN none ELSE second; RETURN[firstTraj, secondTraj, otherCluster, whichClustered, TRUE]; }; IF otherCluster = NIL THEN { RETURN[firstTraj, secondTraj, cluster, first, TRUE]; }; IF cluster # otherCluster THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "Weld failed: can't weld two CLUSTERED trajectories"]; RETURN[NIL, NIL, NIL, none, FALSE]; }; success ¬ TRUE; whichClustered ¬ both; EXITS Abort => { Feedback.Append[ggData.router, oneLiner, $Complaint, "Weld failed: select one or two OPEN trajectories for a weld."]; RETURN[NIL, NIL, NIL, none, FALSE]; }; }; WeldToSelf: PROC [ggData: GGData, traj: Traj] RETURNS [success: BOOL ¬ FALSE] = { outline: Slice ¬ GGParent.GetParent[traj]; ancestor: Slice ¬ GGParent.GetTopLevelAncestor[traj]; GGRefresh.NullStartBox[ggData]; GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[traj], NIL]; IF GGTraj.HiSegment[traj]=0 AND GGTraj.FetchSegment[traj,0].class.type=$Line THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "Weld failed: can't weld a single straight line segment to itself"]; RETURN; }; GGSelect.SaveSelectionsInSliceAllClasses[ancestor, ggData.scene]; GGSelect.DeselectEntityAllClasses[ancestor, ggData.scene]; GGTraj.CloseByDistorting[traj, lo]; GGSelect.ReselectSliceAllClasses[ancestor, ggData.scene]; GGEvent.SelectEntireSlice[outline, ggData.scene, normal, ggData]; GGCaret.SetAttractor[ggData.caret, GGTraj.FetchJointPos[traj, 0], [0,-1], NIL]; GGCaret.SitOn[ggData.caret, NIL]; -- this is important! GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[traj], NIL]; success ¬ TRUE; }; ClosestEnds: PROC [firstTraj, secondTraj: Traj] RETURNS [firstEnd, secondEnd: TrajEnd] = { firstLo, firstHi, secondLo, secondHi: Point; d11, d12, d21, d22, d: REAL; firstLo ¬ GGTraj.FetchJointPos[firstTraj, 0]; firstHi ¬ GGTraj.FetchJointPos[firstTraj, GGTraj.HiJoint[firstTraj]]; secondLo ¬ GGTraj.FetchJointPos[secondTraj, 0]; secondHi ¬ GGTraj.FetchJointPos[secondTraj, GGTraj.HiJoint[secondTraj]]; d11 ¬ Vectors2d.DistanceSquared[firstLo, secondLo]; d12 ¬ Vectors2d.DistanceSquared[firstLo, secondHi]; d21 ¬ Vectors2d.DistanceSquared[firstHi, secondLo]; d22 ¬ Vectors2d.DistanceSquared[firstHi, secondHi]; d ¬ MIN[d11, d12, d21, d22]; SELECT TRUE FROM d11 = d => {firstEnd ¬ lo; secondEnd ¬ lo}; d12 = d => {firstEnd ¬ lo; secondEnd ¬ hi}; d21 = d => {firstEnd ¬ hi; secondEnd ¬ lo}; d22 = d => {firstEnd ¬ hi; secondEnd ¬ hi}; ENDCASE => ERROR; }; < { line: Segment ¬ GGSegment.MakeLine[firstPoint, middlePoint, List.Append[runSeg.props, NIL]]; GGSegment.CopyLooks[runSeg, line]; IF NOT GGTraj.AddSegment[traj, hi, line, lo] THEN ERROR; line ¬ GGSegment.MakeLine[middlePoint, lastPoint, List.Append[runSeg.props, NIL]]; GGSegment.CopyLooks[runSeg, line]; IF NOT GGTraj.AddSegment[traj, hi, line, lo] THEN ERROR; }; $Arc => { arc: Segment ¬ GGSegment.MakeArc[firstPoint, middlePoint, lastPoint, List.Append[runSeg.props, NIL]]; GGSegment.CopyLooks[runSeg, arc]; IF NOT GGTraj.AddSegment[traj, hi, arc, lo] THEN ERROR; }; $Conic => { conic: Segment ¬ GGSegment.MakeConic[firstPoint, middlePoint, lastPoint, 0.7, List.Append[runSeg.props, NIL]]; GGSegment.CopyLooks[runSeg, conic]; IF NOT GGTraj.AddSegment[traj, hi, conic, lo] THEN ERROR; }; $Bezier => { length: REAL ¬ Vectors2d.Distance[firstPoint, middlePoint]; dir: Point ¬ Vectors2d.Sub[middlePoint, firstPoint]; p1: Point ¬ GetPt[firstPoint, dir]; p2: Point ¬ GetPt[middlePoint, [-dir.x, -dir.y]]; bezier: Segment ¬ GGSegment.MakeBezier[firstPoint, p1, p2, middlePoint, List.Append[runSeg.props, NIL]]; GGSegment.CopyLooks[runSeg, bezier]; IF NOT GGTraj.AddSegment[traj, hi, bezier, lo] THEN ERROR; -- first Bezier dir ¬ Vectors2d.Sub[lastPoint, middlePoint]; p1 ¬ GetPt[middlePoint, dir]; p2 ¬ GetPt[lastPoint, [-dir.x, -dir.y]]; bezier ¬ GGSegment.MakeBezier[middlePoint, p1, p2, lastPoint, List.Append[runSeg.props, NIL]]; GGSegment.CopyLooks[runSeg, bezier]; IF NOT GGTraj.AddSegment[traj, hi, bezier, lo] THEN ERROR; -- last Bezier }; $CubicSpline => { cSpline: Segment; cps: CubicSplines.KnotSequence ¬ NEW[CubicSplines.KnotSequenceRec[3]]; cps[0] ¬ [firstPoint.x, firstPoint.y]; cps[1] ¬ [middlePoint.x, middlePoint.y]; cps[2] ¬ [lastPoint.x, lastPoint.y]; cSpline ¬ GGSegment.MakeCubicSpline[cps, naturalAL, List.Append[runSeg.props, NIL]]; GGSegment.CopyLooks[runSeg, cSpline]; IF NOT GGTraj.AddSegment[traj, hi, cSpline, lo] THEN ERROR; }; ENDCASE => ERROR; -- unknown segment type ENDLOOP; }; GGHistory.NewCapture["Split segments", ggData]; -- capture scene BEFORE UPDATE ggData.refresh.startBoundBox­ ¬ GGSelect.ForEachOutlineRun[ggData.scene, normal, MakeReplacement, TRUE, FALSE]­; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.Append[ggData.router, "Segements split", oneLiner]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE] }; >> AddControlPoint: UserInputProc = { seg, newSeg: Segment; segNum: INT; traj, newRun, newOutline, ancestor, oldOutline: Slice; refreshBox: BoundBox; success: BOOL; partType: TrajPartType; caretPos: Point; attractor: REF ANY; sliceD: SliceDescriptor; scene: Scene ¬ ggData.scene; caretPos ¬ GGCaret.GetPoint[ggData.caret]; attractor ¬ GGCaret.GetAttractor[ggData.caret]; IF attractor = NIL THEN GOTO NotOnSpline; IF NOT ISTYPE[attractor, SliceDescriptor] THEN GOTO NotOnSpline; sliceD ¬ NARROW[attractor]; [success, partType, traj, seg, segNum] ¬ GGSliceOps.UnpackSegment[sliceD]; IF NOT success OR partType#segment OR seg.class.type#$CubicSpline THEN GOTO NotOnSpline; ancestor ¬ GGParent.GetTopLevelAncestor[sliceD.slice]; GGHistory.NewCapture["Add control point", ggData]; -- capture scene BEFORE UPDATE GGSelect.DeselectAll[scene, normal]; GGSelect.SaveSelectionsInSliceAllClasses[ancestor, scene]; GGRefresh.NullStartBox[ggData]; GGRefresh.EnlargeStartBox[ggData, seg.class.boundBox[seg], NIL]; newSeg ¬ GGSegment.CSControlPointAdd[seg, caretPos]; newRun ¬ GGTraj.CreateTraj[newSeg.lo]; GGSliceOps.SetStrokeJoint[newRun, NIL, NARROW[traj.data, TrajData].strokeJoint, NIL]; refreshBox ¬ newSeg.class.boundBox[newSeg]; GGRefresh.EnlargeStartBox[ggData, refreshBox, NIL]; IF NOT GGTraj.AddSegment[newRun, hi, newSeg, lo] THEN ERROR; IF (segNum ¬ GGTraj.IndexOfSegment[seg, traj]) = -1 THEN ERROR; -- inconsistent newOutline ¬ SubstituteForSegment[traj, segNum, newRun].newOutline; oldOutline ¬ GGParent.GetParent[traj]; IF GGScene.IsTopLevel[oldOutline] THEN { priority: INT ¬ GGScene.GetPriority[scene, ancestor]; GGScene.DeleteSlice[scene, oldOutline]; GGScene.AddSlice[scene, newOutline, priority]; GGSelect.ReselectSliceAllClasses[newOutline, scene]; } ELSE { cluster: Slice ¬ GGParent.GetParent[oldOutline]; priority: INT ¬ GGParent.GetChildPriority[cluster, oldOutline]; [] ¬ GGSlice.RemoveChild[cluster, oldOutline]; GGSlice.AddChildToCluster[cluster, newOutline, priority]; GGSelect.ReselectSliceAllClasses[GGParent.GetTopLevelAncestor[cluster], scene]; }; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.Append[ggData.router, oneLiner, $Feedback, "Add CP: control point added"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; EXITS NotOnSpline => { Feedback.Append[ggData.router, oneLiner, $Complaint, "Add CP failed: caret must lie on a cubic spline to add a control point"]; }; }; DeleteControlPoint: UserInputProc = { scene: Scene ¬ ggData.scene; topLevelList: LIST OF Slice; DoSaveSelections: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { GGSelect.SaveSelectionsInSliceAllClasses[sliceD.slice, scene]; topLevelList ¬ CONS[sliceD.slice, topLevelList]; }; DoDeleteCP: PROC [nextD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { refreshBox: BoundBox ¬ GGOutline.DeleteControlPoints[nextD, scene]; GGRefresh.EnlargeStartBox[ggData, refreshBox, NIL]; }; IF GGSelect.NoSelections[scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Delete CP failed: no selected control points"] ELSE { GGHistory.NewCapture["Delete control point", ggData]; GGRefresh.NullStartBox[ggData]; [] ¬ GGScene.WalkSelectedSlices[scene, first, DoSaveSelections, normal]; [] ¬ GGScene.WalkSelectedSlices[scene, leaf, DoDeleteCP, normal, $Outline]; FOR list: LIST OF Slice ¬ topLevelList, list.rest UNTIL list = NIL DO GGSelect.ReselectSliceAllClasses[list.first, scene]; ENDLOOP; GGSelect.DeselectAll[scene, normal]; GGCaret.SitOn[ggData.caret, NIL]; GGCaret.NoAttractor[ggData.caret]; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.Append[ggData.router, oneLiner, $Feedback, "Delete CP: selected control points deleted"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; }; AddJoint: UserInputProc = { sliceD: SliceDescriptor; seg, newSeg1, newSeg2: Segment; segNum: INT; traj, newRun, newTraj, newOutline: Traj; newJoint: Joint; success: BOOL; partType: TrajPartType; caretPos: Point; attractor: REF ANY; scene: Scene ¬ ggData.scene; ancestor, outline: Slice; caretPos ¬ GGCaret.GetPoint[ggData.caret]; attractor ¬ GGCaret.GetAttractor[ggData.caret]; IF attractor = NIL THEN GOTO NotASegment; IF NOT ISTYPE[attractor, SliceDescriptor] THEN GOTO NotASegment; sliceD ¬ NARROW[attractor]; [success, partType, traj, seg, segNum] ¬ GGSliceOps.UnpackSegment[sliceD]; IF NOT success OR partType#segment THEN GOTO NotASegment; IF seg.class.type=$Conic THEN GOTO ConicsAreNotDone; ancestor ¬ GGParent.GetTopLevelAncestor[sliceD.slice]; GGHistory.NewCapture["Add joint", ggData]; -- capture scene BEFORE UPDATE GGSelect.DeselectAll[scene, normal]; GGSelect.SaveSelectionsInSliceAllClasses[ancestor, scene]; [newSeg1, newSeg2] ¬ seg.class.addJoint[seg, caretPos]; newRun ¬ GGTraj.CreateTraj[newSeg1.lo]; IF NOT GGTraj.AddSegment[newRun, hi, newSeg1, lo] THEN ERROR; IF NOT GGTraj.AddSegment[newRun, hi, newSeg2, lo] THEN ERROR; newJoint ¬ GGTraj.FetchJoint[newRun, 1]; newJoint.TselectedInFull.normal ¬ TRUE; IF (segNum ¬ GGTraj.IndexOfSegment[seg, traj]) = -1 THEN ERROR; -- inconsistent GGRefresh.NullStartBox[ggData]; GGRefresh.EnlargeStartBox[ggData, seg.class.boundBox[seg], NIL]; [newTraj, newOutline] ¬ SubstituteForSegment[traj, segNum, newRun]; GGSliceOps.SetStrokeJoint[newTraj, NIL, NARROW[traj.data, TrajData].strokeJoint, NIL]; GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[newOutline], NIL]; outline ¬ GGParent.GetParent[traj]; IF GGScene.IsTopLevel[outline] THEN { priority: INT ¬ GGScene.GetPriority[scene, ancestor]; GGScene.DeleteSlice[scene, ancestor]; GGScene.AddSlice[scene, newOutline, priority]; GGSelect.ReselectSliceAllClasses[newOutline, scene]; } ELSE { cluster: Slice ¬ GGParent.GetParent[outline]; priority: INT ¬ GGParent.GetChildPriority[cluster, outline]; [] ¬ GGSlice.RemoveChild[cluster, outline]; GGSlice.AddChildToCluster[cluster, newOutline, priority]; GGSelect.ReselectSliceAllClasses[GGParent.GetTopLevelAncestor[cluster], scene]; }; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.Append[ggData.router, oneLiner, $Feedback, "Add Joint: joint added"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; EXITS NotASegment => { Feedback.Append[ggData.router, oneLiner, $Complaint, "Add Joint failed: caret must lie on a segment to add a joint"]; }; ConicsAreNotDone => { Feedback.Append[ggData.router, oneLiner, $Complaint, "Add Joint failed: can't add joints to Conics"]; }; }; SubstituteForSegment: PROC [traj: Slice, segNum: NAT, newRun: Slice] RETURNS [newTraj, newOutline: Slice] = { trajData: TrajData ¬ NARROW[traj.data]; runParts: TrajParts ¬ GGSequence.CreateFromSegment[trajData, segNum]; run: SliceDescriptor ¬ GGSlice.DescriptorFromParts[traj, runParts]; oldOutline: Slice ¬ GGParent.GetParent[traj]; newTraj ¬ GGTraj.SpliceIn[run, newRun]; newOutline ¬ GGParent.GetParent[newTraj]; }; GetAnchorPoint: PROC [ggData: GGData] RETURNS [success: BOOL ¬ TRUE, anchorPoint: Point] = { IF GGCaret.Exists[ggData.anchor] THEN anchorPoint ¬ GGCaret.GetPoint[ggData.anchor] ELSE { tightBox: BoundBox ¬ GGScene.TightBoxOfSelected[ggData.scene, normal]; [anchorPoint, success] ¬ GGBoundBox.Centroid[tightBox]; }; }; TransRotScale: UserInputProc = { argStream: IO.STREAM ¬ IO.RIS[NARROW[event.rest.first]]; scalar1, scalar2: REAL ¬ 0.0; SELECT event.first FROM $Scale, $ScaleX, $ScaleY => { success: BOOL ¬ TRUE; scalar1 ¬ GGParseIn.ReadReal[argStream ! IO.Error, IO.EndOfStream, GGParseIn.SyntaxError => {success ¬ FALSE; CONTINUE}]; IF NOT success THEN scalar1 ¬ 2.0; }; ENDCASE => scalar1 ¬ GGParseIn.ReadReal[argStream ! IO.Error, IO.EndOfStream, GGParseIn.SyntaxError => GOTO NoNumber]; IF scalar1 = Real.LargestNumber OR scalar1 = 0.0 THEN GOTO NoNumber ELSE { GetSecondScalar: PROC [] RETURNS [success: BOOL ¬ TRUE] ~ { scalar2 ¬ GGParseIn.ReadReal[argStream ! IO.Error, IO.EndOfStream, GGParseIn.SyntaxError => {success ¬ FALSE; CONTINUE;}; ]; IF scalar2 = Real.LargestNumber OR scalar2 = 0.0 THEN success ¬ FALSE; }; transform: ImagerTransformation.Transformation; anchorPoint: Point; success: BOOL ¬ FALSE; [success, anchorPoint] ¬ GetAnchorPoint[ggData]; IF NOT success OR GGSelect.NoSelections[ggData.scene, normal] THEN GOTO NoSelections; SELECT event.first FROM $Scale => transform ¬ GGTransform.ScaleAboutPoint[anchorPoint, scalar1]; $ScaleXY => { IF NOT GetSecondScalar[] THEN GOTO NoNumberPair; transform ¬ GGTransform.ScaleUnevenAboutPoint[anchorPoint, scalar1, scalar2]; }; $UnScale => transform ¬ GGTransform.ScaleAboutPoint[anchorPoint, 1.0/scalar1]; $Rotate => transform ¬ GGTransform.RotateAboutPoint[anchorPoint, scalar1]; $UnRotate => transform ¬ GGTransform.RotateAboutPoint[anchorPoint, -1.0*scalar1]; $TranslateX => transform ¬ ImagerTransformation.Translate[[ggData.hitTest.scaleUnit*scalar1, 0.0]]; $TranslateXY => { IF NOT GetSecondScalar[] THEN GOTO NoNumberPair; transform ¬ ImagerTransformation.Translate[[ggData.hitTest.scaleUnit*scalar1, ggData.hitTest.scaleUnit*scalar2]]; }; $TranslateToXY => { IF NOT GetSecondScalar[] THEN GOTO NoNumberPair; transform ¬ ImagerTransformation.Translate[Vectors2d.Sub[[ggData.hitTest.scaleUnit*scalar1, ggData.hitTest.scaleUnit*scalar2], anchorPoint]]; }; $TranslateY => transform ¬ ImagerTransformation.Translate[[0.0, ggData.hitTest.scaleUnit*scalar1]]; $ScaleX => transform ¬ GGTransform.ScaleUnevenAboutPoint[anchorPoint, scalar1, 1.0]; $ScaleY => transform ¬ GGTransform.ScaleUnevenAboutPoint[anchorPoint, 1.0, scalar1]; $UnScaleX => transform ¬ GGTransform.ScaleUnevenAboutPoint[anchorPoint, 1.0/scalar1, 1.0]; $UnScaleY => transform ¬ GGTransform.ScaleUnevenAboutPoint[anchorPoint, 1.0, 1.0/scalar1]; ENDCASE => ERROR; DoTheTransforms[ggData, transform]; } EXITS NoNumber => { Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: select a non-zero real number", [rope[Atom.GetPName[NARROW[event.first]]]] ]; }; NoNumberPair => { Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: select two non-zero real numbers", [rope[Atom.GetPName[NARROW[event.first]]]] ]; }; NoSelections => { Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: please select some shapes", [rope[Atom.GetPName[NARROW[event.first]]]] ]; }; }; SixPointTransform: UserInputProc = { points: ARRAY [0..5] OF Point ¬ ALL[[0.0, 0.0]]; transform: ImagerTransformation.Transformation; aborted: BOOL ¬ FALSE; scene: Scene ¬ ggData.scene; DoGetSixPoints: PROC [hotSlice: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { IF GGTraj.HiJoint[hotSlice.slice] < 2 THEN RETURN[TRUE]; FOR j: NAT IN [0..2] DO points[j + i*3] ¬ GGTraj.FetchJointPos[hotSlice.slice, j]; ENDLOOP; i ¬ 1; }; i: NAT ¬ 0; argCount: CARD ¬ GGScene.CountSelectedSlices[scene, leaf, hot, $Traj]; IF argCount # 2 THEN GOTO Abort; aborted ¬ GGScene.WalkSelectedSlices[scene, leaf, DoGetSixPoints, hot, $Traj]; IF aborted THEN GOTO Abort; transform ¬ GGTransform.SixPoints[points]; DoTheTransforms[ggData, transform]; EXITS Abort => Feedback.Append[ggData.router, oneLiner, $Complaint, "Six Point failed: not enough arguments for a six-point transform"]; }; FourPointTransform: UserInputProc = { points: ARRAY [0..3] OF Point ¬ ALL[[0.0, 0.0]]; transform: ImagerTransformation.Transformation; i: NAT ¬ 0; scene: Scene ¬ ggData.scene; aborted: BOOL ¬ FALSE; DoGetFourPoints: PROC [hotSlice: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { IF GGTraj.HiJoint[hotSlice.slice] < 1 THEN RETURN[TRUE]; FOR j: NAT IN [0..1] DO points[j + i*2] ¬ GGTraj.FetchJointPos[hotSlice.slice, j]; ENDLOOP; i ¬ 1; }; argCount: CARD ¬ GGScene.CountSelectedSlices[scene, leaf, hot, $Traj]; IF argCount # 2 THEN GOTO Abort; aborted ¬ GGScene.WalkSelectedSlices[scene, leaf, DoGetFourPoints, hot, $Traj]; IF aborted THEN GOTO Abort; transform ¬ GGTransform.FourPoints[points]; DoTheTransforms[ggData, transform]; EXITS Abort => Feedback.Append[ggData.router, oneLiner, $Complaint, "Four Point failed: not enough arguments for a four-point transform"]; }; DoTheTransforms: PROC [ggData: GGData, transform: ImagerTransformation.Transformation] = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Transform failed: no selection to transform"] ELSE { currentEvent: HistoryEvent; scene: Scene ¬ ggData.scene; DoTransform: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL ¬ FALSE] = { GGSliceOps.Transform[sliceD.slice, sliceD.parts, transform, ggData.drag.editConstraints, currentEvent]; }; ggData.drag.transform ¬ GGTransform.Identity[]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, hotCPs: TRUE, movingParts: TRUE]; currentEvent ¬ GGHistory.NewCurrent["Transform", ggData]; -- start new history event [] ¬ GGScene.WalkSelectedSlices[scene, first, DoTransform, normal]; GGRefresh.EnlargeStartBox[ggData, GGScene.BoundBoxOfMoving[scene, ggData.drag.editConstraints, ggData.drag.bezierDrag, normal], NIL]; GGHistory.PushCurrent[ggData]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Transform completed"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; }; Refresh: UserInputProc = { startTime, endTime: BasicTime.GMT; totalTime: INT; Feedback.Append[ggData.router, begin, $Statistics, "Refreshing . . . "]; startTime ¬ BasicTime.Now[]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintSceneNoBuffer, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; endTime ¬ BasicTime.Now[]; totalTime ¬ BasicTime.Period[startTime, endTime]; Feedback.PutF[ggData.router, end, $Statistics, " Done in time (%r)", [integer[totalTime]]]; }; Revive: UserInputProc = { scene: SceneRef ¬ NARROW[ggData.scene]; GGMouseEvent.ResetMouseMachinery[ggData]; CodeTimer.ResetTable[CodeTimer.GetTable[$Gargoyle]]; SlackProcess.Restart[ggData.slackHandle]; ggData.camera.quality ¬ fast; ggData.refresh.suppressRefresh ¬ FALSE; ggData.refresh.suppressScreen ¬ FALSE; ggData.refresh.areaFollowColorTool ¬ ggData.refresh.lineFollowColorTool ¬ FALSE; scene.ptrValid ¬ scene.prioritiesValid ¬ FALSE; ggData.aborted ¬ ALL[FALSE]; GGHistory.KillAdvanceCapture[ggData]; IF event.rest # NIL AND event.rest.first = $Repaint THEN GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: TRUE]; }; DisableRefresh: UserInputProc = { -- this belongs in GGState GGRefresh.DisableRefresh[ggData]; }; EnableRefresh: UserInputProc = { -- this belongs in GGState GGRefresh.EnableRefresh[ggData]; }; ToggleBuffer: UserInputProc = { GGState.SetDoubleBuffer[ggData, NOT GGState.GetDoubleBuffer[ggData]]; }; ToggleUseBackingMap: UserInputProc = { GGState.SetUseBackingMap[ggData, NOT GGState.GetUseBackingMap[ggData]]; }; EnableScreen: UserInputProc = { -- this belongs in GGState ggData.refresh.suppressScreen ¬ FALSE; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintAllPlanes, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: TRUE]; }; DisableScreen: UserInputProc = { -- this belongs in GGState ggData.refresh.suppressScreen ¬ TRUE; }; Help: UserInputProc = { category: ATOM ¬ NARROW[event.rest.first]; GGState.ShowHelp[ggData, category]; }; IPSnapShot: UserInputProc = { DoMakeInterpress: PROC [dc: Imager.Context] = { ENABLE UNWIND => { ggData.camera.displayStyle ¬ tempStyle; ggData.camera.quality ¬ tempQuality; }; DoItInterpress: PROC = { Imager.ScaleT[dc, metersPerPixel]; ggData.camera.displayStyle ¬ print; ggData.camera.quality ¬ showall; GGRefresh.SnapShot[dc, ggData]; ggData.camera.displayStyle ¬ tempStyle; ggData.camera.quality ¬ tempQuality; }; Imager.DoSave[dc, DoItInterpress]; }; ipRef: ImagerInterpress.Ref; fullName: ROPE; success: BOOL; metersPerPixel: REAL = 0.0254/72.0; startTime: BasicTime.GMT; endTime: BasicTime.GMT; totalTime: INT; msgRope: ROPE; tempStyle: GGInterfaceTypes.DisplayStyle ¬ ggData.camera.displayStyle; tempQuality: GGInterfaceTypes.QualityMode ¬ ggData.camera.quality; [fullName, success] ¬ GGFileOps.GetInterpressFileName["IPSnapshot", "snapshot.ip", ggData.currentWDir, ggData.router]; IF NOT success THEN RETURN; ipRef ¬ ImagerInterpress.Create[fullName]; msgRope ¬ IO.PutFR1["IPSnapshot: writing to IP file: %g . . . ", [rope[fullName]]]; Feedback.Append[ggData.router, begin, $Statistics, msgRope]; startTime ¬ BasicTime.Now[]; ImagerInterpress.DoPage[ipRef, DoMakeInterpress, 1.0]; ImagerInterpress.Close[ipRef]; endTime ¬ BasicTime.Now[]; totalTime ¬ BasicTime.Period[startTime, endTime]; msgRope ¬ IO.PutFR1[" Done in time (%r)", [integer[totalTime]]]; Feedback.Append[ggData.router, end, $Statistics, msgRope]; }; ReloadTipTable: UserInputProc = { [] ¬ GGState.ReloadTipTable[ggData]; }; ToggleEditable: UserInputProc = { readOnly: BOOL ¬ GGState.GetReadOnly[ggData]; GGState.SetReadOnly[ggData, NOT readOnly]; }; SawTextFinish: PUBLIC UserInputProc = { slice: Slice ¬ ggData.refresh.textInProgress; CodeTimer.StartInt[$SawTextFinish, $Gargoyle]; IF slice#NIL AND Rope.Length[GGSlice.GetText[slice]]=0 THEN { -- backspaced to nothing isHot: BOOL ¬ GGSelect.IsSelectedInPart[slice, ggData.scene, hot]; GGScene.DeleteSlice[ggData.scene, slice]; ggData.refresh.textInProgress ¬ NIL; -- terminates typed input GGHistory.PushCurrent[ggData: ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: IF isHot THEN $NewAlignmentsSelected ELSE $None, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: FALSE]; } ELSE IF slice#NIL THEN { -- fix up alignment triggers GGHistory.PushCurrent[ggData: ggData]; IF tryIncrementalUpdate THEN GGAlign.UpdateBagsForNewSlices[LIST[slice], ggData] -- the text is not necessarily new, but I believe this will work anyway. Bier, April 20, 1987 ELSE GGWindow.RestoreScreenAndInvariants[paintAction: $NewAlignmentsSelected, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; ggData.refresh.textInProgress ¬ NIL; -- terminates typed input }; IF ggData.refresh.areaFollowColorTool THEN { GGEvent.AreaColorFromColorTool[ggData, event ! ColorTool.NoColorToolViewer => CONTINUE;]; ColorTool.RemoveProc[$GG, NIL ! ColorTool.NoColorToolViewer => CONTINUE;]; ggData.refresh.areaFollowColorTool ¬ FALSE; }; IF ggData.refresh.lineFollowColorTool THEN { GGEvent.LineColorFromColorTool[ggData, event ! ColorTool.NoColorToolViewer => CONTINUE;]; ColorTool.RemoveProc[$GG, NIL ! ColorTool.NoColorToolViewer => CONTINUE;]; ggData.refresh.lineFollowColorTool ¬ FALSE; }; CodeTimer.StopInt[$SawTextFinish, $Gargoyle]; }; PrintRope: UserInputProc = { rope: ROPE ¬ NARROW[event.rest.first]; Feedback.Append[ggData.router, oneLiner, $Statistics, rope] }; SetCaretPosition: UserInputProc = { point: Point ¬ NARROW[event.rest.first, REF Point]­; caret: Caret ¬ ggData.caret; caret.point ¬ point; }; SetCaretNormal: UserInputProc = { normal: Vector ¬ NARROW[event.rest.first, REF Vector]­; caret: Caret ¬ ggData.caret; caret.normal ¬ normal; }; NoOp: UserInputProc = {}; RegisterAction: PROC [atom: ATOM, eventProc: GGUserInput.UserInputProc, argType: GGUserInput.ArgumentType, causeMouseEventsToComplete: BOOL ¬ TRUE, ensureUnique: BOOL ¬ TRUE] = GGUserInput.RegisterAction; RegisterEventProcs: PROC = { RegisterAction[$PaintActionArea, PaintActionArea, none]; RegisterAction[$ToggleActive, ToggleActive, none, TRUE]; RegisterAction[$SetActive, SetActive, none, TRUE]; RegisterAction[$TogglePalette, TogglePalette, none, TRUE]; RegisterAction[$SaveSelections, SaveSelections, none]; RegisterAction[$RestoreSelections, RestoreSelections, none]; RegisterAction[$SelectButton, SelectButton, none, TRUE]; -- actually takes a SliceDescriptor arg RegisterAction[$FeedbackOn, FeedbackOn, none]; RegisterAction[$FeedbackOff, FeedbackOff, none]; RegisterAction[$BeginButton, BeginButton, none]; RegisterAction[$EndButton, EndButton, none]; RegisterAction[$ButtonFillColorFromIntensity, ButtonFillColorFromIntensity, refReal]; RegisterAction[$ButtonStrokeColorFromIntensity, ButtonStrokeColorFromIntensity, refReal]; RegisterAction[$SetProp, SetProp, rope]; RegisterAction[$GetProp, GetProp, rope]; RegisterAction[$GetPropExternal, GetPropExternal, refExt]; RegisterAction[$RemoveProp, RemoveProp, rope]; RegisterAction[$ListProps, ListProps, none]; RegisterAction[$ListPropsExternal, ListPropsExternal, refExt]; RegisterAction[$CopyProps, CopyProps, none]; RegisterAction[$SetRootProp, SetRootProp, rope]; RegisterAction[$GetRootProp, GetRootProp, rope]; RegisterAction[$RemoveRootProp, RemoveRootProp, rope]; RegisterAction[$ListRootProps, ListRootProps, none]; RegisterAction[$Abort, GGMouseEvent.HandleMouseless, none, FALSE]; RegisterAction[$NoOp, NoOp, none]; RegisterAction[$Again, NoOp, none, FALSE]; -- added July 24, 1987. KAP. RegisterAction[$ApplyAllDefaults, ApplyAllDefaults, none]; RegisterAction[$SetAllDefaults, SetAllDefaults, none]; RegisterAction[$ShowAllDefaults, ShowAllDefaults, none]; RegisterAction[$StandardDefaults, StandardDefaults, none]; RegisterAction[$Weld, Weld, none]; RegisterAction[$AddControlPoint, AddControlPoint, none]; RegisterAction[$DeleteControlPoint, DeleteControlPoint, none]; RegisterAction[$AddJoint, AddJoint, none]; RegisterAction[$Scale, TransRotScale, rope]; RegisterAction[$ScaleXY, TransRotScale, rope]; RegisterAction[$UnScale, TransRotScale, rope]; RegisterAction[$Rotate, TransRotScale, rope]; RegisterAction[$UnRotate, TransRotScale, rope]; RegisterAction[$TranslateX, TransRotScale, rope]; RegisterAction[$TranslateXY, TransRotScale, rope]; RegisterAction[$TranslateToXY, TransRotScale, rope]; RegisterAction[$TranslateY, TransRotScale, rope]; RegisterAction[$ScaleX, TransRotScale, rope]; RegisterAction[$SixPointTransform, SixPointTransform, none]; RegisterAction[$ScaleY, TransRotScale, rope]; RegisterAction[$UnScaleX, TransRotScale, rope]; RegisterAction[$FourPointTransform, FourPointTransform, none]; RegisterAction[$UnScaleY, TransRotScale, rope]; RegisterAction[$Refresh, Refresh, none]; RegisterAction[$Revive, Revive, none]; RegisterAction[$DisableRefresh, DisableRefresh, none]; RegisterAction[$EnableRefresh, EnableRefresh, none]; RegisterAction[$ToggleBuffer, ToggleBuffer, none]; RegisterAction[$ToggleUseBackingMap, ToggleUseBackingMap, none]; RegisterAction[$EnableScreen, EnableScreen, none]; RegisterAction[$DisableScreen, DisableScreen, none]; RegisterAction[$Help, Help, none]; RegisterAction[$ReloadTipTable, ReloadTipTable, none]; RegisterAction[$IPSnapShot, IPSnapShot, none, FALSE]; RegisterAction[$PrintRope, PrintRope, none, FALSE]; RegisterAction[$SetCaretPosition, SetCaretPosition, none]; RegisterAction[$SetCaretNormal, SetCaretNormal, none]; RegisterAction[$ToggleEditable, ToggleEditable, none]; RegisterAction[$SawTextFinish, SawTextFinish, none, FALSE]; RegisterAction[$SawMouseFinish, GGMouseEvent.HandleMouseless, none, FALSE]; RegisterAction[$WeldOrBackword, WeldOrBackword, none]; }; tryIncrementalUpdate: BOOL ¬ TRUE; RegisterEventProcs[]; END. èGGEventImplC.mesa Copyright Ó 1986, 1987, 1988, 1989, 1991, 1992 by Xerox Corporation. All rights reserved. Contents: Once an event reaches the front of the slack-process queue, it is dispatched to one of the procedures in this module. Kurlander, July 21, 1987 10:52:27 am PDT Eisenman, August 11, 1987 5:36:57 pm PDT Maureen Stone, October 5, 1987 11:15:23 am PDT Pier, June 12, 1992 5:53 pm PDT Bier, June 23, 1993 9:49 pm PDT Doug Wyatt, April 16, 1992 4:42 pm PDT Active Document Elements PROC [ggData: GGData, event: LIST OF REF ANY]; This routine should never actually be called, because InputNotify treats it specially. It exists just to make sure Gargoyle is notified of all mouse motion (even when none of the buttons are down) PROC [ggData: GGData, event: LIST OF REF ANY]; PROC [ggData: GGData, event: LIST OF REF ANY]; PROC [ggData: GGData, event: LIST OF REF ANY]; SaveSelections DeselectAll SelectButton FeedbackOff Turn off Screen Refresh until all button operations are completed RestoreSelections FeedbackOn Turn screen refresh back on and update the screen to reflect the current state PROC [ggData: GGData, event: LIST OF REF ANY]; PROC [ggData: GGData, event: LIST OF REF ANY]; Props Menu In GGEventImplC.SetProp Parse the Tioga selection, which is in the form "key value". Add the key-value pair to the selected Gargoyle objects. IF all selected slices have the same list of properties, then print out a list of the keys of those properties. IF all selected slices have the same list of properties, then return a list of the keys of those properties. Copy the propList of the last selected slice to all other selected slices Edit Menu Operations ggData.refresh.startBoundBox­ ¬ GGScene.BoundBoxOfSelected[ggData.scene, normal]­; GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGScene.BoundBoxOfSelected[ggData.scene, normal]]; GGEvent.SetDefaultStrokeValues[event, clientData]; GGEvent.SetDefaultLineColor[event, clientData]; GGEvent.SetDefaultFillColor[event, clientData]; GGEvent.SetDefaultFontValues[event, clientData]; GGEvent.ShowAllDefaults[event, clientData]; One or two top level trajectories are selected. If one, Close it by distorting one of its ends. If two, of the four possible pairings of endpoints, choose the one with the endpoints closest together. Translate the second trajectory to the first. Weld. Select the new outline. Move the caret to the weld spot Compute the new bounding box. ggData.refresh.startBoundBox­ ¬ GGSliceOps.GetBoundBox[firstTraj]­; GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGSliceOps.GetBoundBox[secondTraj]]; GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGSliceOps.GetBoundBox[newTraj]]; If either outline is at top level, weld its parts into the other cluster, if any. ggData.refresh.startBoundBox­ ¬ GGSliceOps.GetBoundBox[traj]­; Disallow special case of single segment straight line traj Modify the Traj in place to close it. move the caret to the weld spot GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGSliceOps.GetBoundBox[traj]]; Curve Modification RunProc: TYPE = PROC [run: Sequence] RETURNS [traj: Traj]; -- make new run of the appropriate type, having the same joints as that of the selected run, and with the caret position as an additional joint or control point. Adds a new control point to a segment ggData.refresh.startBoundBox­ ¬ seg.class.boundBox[seg]­; GGBoundBox.EnlargeByBox[ggData.refresh.startBoundBox, refreshBox]; Deletes all selected control points from segments GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: refreshBox]; ggData.refresh.startBoundBox­ ¬ GGBoundBox.emptyBoundBox­; [Artwork node; type 'ArtworkInterpress on' to command tool] ggData.refresh.startBoundBox­ ¬ seg.class.boundBox[seg]­; GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGSliceOps.GetBoundBox[newOutline, NIL]]; Replaces a segment with a new run. If newRun is not NIL, then only one trajectory is modified, and the new trajectory is returned. Also returned is the bbox of modified Outlines. IF NOT success THEN ERROR; -- should only happen if their are no selections Transform Menu Operations The first second and third selected objects should be one-segment trajectories indicating the six-point transform. All other selected objects are to be transformed. There are obvious problems with this scheme. Suggestions welcome. -- Bier, July 28, 1986. The first and second selected objects should be one-segment trajectories representing the four-point transform. All other selected objects are to be transformed. There are obvious problems with this scheme. Suggestions welcome. -- Bier, July 28, 1986. The boundbox before anything has moved. ggData.refresh.startBoundBox­ ¬ GGScene.BoundBoxOfMoving[ggData.scene, ggData.drag.editConstraints, ggData.drag.bezierDrag, normal]­; because ggData.drag.transform is used to compute movingParts bound box and we want the untransformed bound box to start with The boundbox after everything has moved. GGBoundBox.EnlargeByBox[bBox: ggData.refresh.startBoundBox, by: GGScene.BoundBoxOfMoving[scene, ggData.drag.editConstraints, ggData.drag.bezierDrag, normal]]; Stand alone menus PROC [ggData: GGData, event: LIST OF REF ANY]; Must not be queued, since the queue may be stuck. Copied from GGMenuImplC. ggData.refresh.suppressRefresh ¬ TRUE; ggData.refresh.suppressRefresh ¬ FALSE; Snapshots This command is executed in the middle of a dragging operation to make an interpress master of the current state of the screen. Luckily, all dragging operations use the same painting commands, namely: GGWindow.RestoreScreenAndInvariants[paintAction: $DuringMotion, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; We wish to have the same effect as these commands except written to an interpress master. We will create an interpress master named snapshot.ip and use GGRefresh.SnapShot to draw the scene into it. Miscellaneous The following has nothing to do with text. We take advantage of the fact that every mouse "Start" operation calls SawTextFinish, so we implicitly complete any FollowColorTool operation here. Active Button Props Menus Special Edit Menu RegisterAction[$SplitSegment, SplitSegment, none]; Transform Menu Refresh related operations Help Menu Miscellaneous Keyboard only Internal Atoms Ê;º–(cedarcode) style•NewlineDelimiter ˜codešœ™Kšœ ÏeœO™ZKšÏnœx™€Kšœ%Ïk™(Kšœ%Ÿ™(Kšœ.™.Kšœ™K™K™&K™—šŸ ˜ Jšœ­ŸœŸœ˜ßK˜—šž œŸœŸ˜JšŸœ‡Ÿœ%˜µKšŸœ+Ÿ˜7—˜Kšœ ŸœŸœÏc˜SK˜Kšœ Ÿœ˜)Kšœ Ÿœ˜'KšœŸœ˜%KšœŸœ˜KšœŸœ&˜AKšœŸœ"˜5Kšœ Ÿœ˜-KšœŸœ˜'KšœŸœ˜1KšœŸœ˜#KšœŸœ˜3KšœŸœ˜!KšœŸœŸœ!˜˜>K˜—˜K˜—™ Kšœ™—šžœ˜K™vš žœŸœŸœŸœŸœ˜OKš œŸœ ŸœŸœŸœŸœ! n˜±Kšœ2˜2K˜—KšœŸœŸœ˜&KšœŸœŸœŸœ˜KšœŸœ˜KšœŸœ˜ KšœŸœ˜ K˜K˜ šŸœ ŸœŸœ˜Kšœa˜aKšŸœ˜K˜—Kšœ˜K˜K˜K˜K˜LKšœ^ŸœŸœ˜{K˜K˜—šžœŸœŸœŸœŸœŸœ ŸœŸœŸœ˜ŠKšœ ŸœŸœ˜K˜–=[rgb: RGB, calibration: ImagerColor.RGBCalibration _ NIL]š žœŸœŸœŸœŸœ˜HKšœ Ÿœ˜ šŸœ ŸœŸœ˜K˜K˜˜>K˜—Kš¡™KšŸœ ŸœŸœ:˜OKšœ=˜=Kš¡™K–![caret: GGInterfaceTypes.Caret]šœ9Ÿœ Ÿœ Ÿœ˜iKšœ6Ÿœ˜;KšœŸœ˜!Kš¡™K™CKšœd™dKšœa™aK˜Kšœ ¡ œ ˜Kšœ ¡œP˜iKšœ ¡œ*Ÿœ˜HK˜—šŸœŸœ˜Kšœ ¡ œ  0˜PKšœG˜GKšœ}ŸœŸœ˜›Kšœ˜—Kšœ˜K˜—Kšœ Ÿœ˜.š žœŸœŸœZŸœŸœ˜šKšœ/˜/K˜KšœŸœ˜Kšœ˜š žœŸœŸœŸœŸœ˜OšŸœ Ÿœ˜K˜K˜K˜—šŸœŸœ Ÿœ˜K˜KšœŸœ˜ K˜—K˜—Kšœ ŸœE˜RKš ŸœŸœŸœŸœ ˜MK˜OKš Ÿœ)ŸœŸœŸœ+ŸœŸœ ˜—KšŸœž œŸœŸœ%ŸœŸœ%ŸœŸœ˜ªšŸœŸœŸœŸ˜K˜2Kš œŸœ ŸœŸœŸœ˜7KšŸœ ŸœŸœ˜6K˜—KšŸœž œŸœŸœ&ŸœŸœ&ŸœŸœ˜®K˜2K˜8Kš¡Q™QšŸœ ŸœŸœŸ˜Kš œŸœŸœŸœŸœ˜=KšŸœ6Ÿœ˜BKšœ˜—šŸœŸœŸœŸ˜KšŸœ(Ÿœ˜4K˜—šŸœŸœŸ˜ Kšœk˜kKš ŸœŸœŸœŸœŸœ˜#K˜—Kšœ Ÿœ˜K˜šŸ˜˜ Kšœu˜uKš ŸœŸœŸœŸœŸœ˜#K˜——K˜K˜—š ž œŸœŸœ ŸœŸœ˜QK˜*K˜5K–[bBox: GGCoreTypes.BoundBox]™>K™Kšœ ¡ œ ˜Kšœ ¡œ'Ÿœ˜E™Kš¡:™:—šŸœŸœ.Ÿœ˜SK–0[feedback: Feedback.FeedbackData, msg: ROPE]šœy˜yKšŸœ˜K˜Kš¡%™%—KšœA˜AKšœ:˜:Kšœ#˜#Kšœ9˜9šœA˜AKš¡™—K–)[traj: GGModelTypes.Traj, index: NAT]šœJŸœ˜OKšœŸœ ˜7Kšœ^™^Kšœ ¡œ'Ÿœ˜EKšœ Ÿœ˜Kšœ˜K˜—šž œŸœŸœ#˜ZKšœ,˜,KšœŸœ˜K˜-K˜EK˜/K˜HK˜3K˜3K˜3K˜3KšœŸœ˜šŸœŸœŸ˜K˜+K˜+K˜+K˜+KšŸœŸœ˜—K˜—K˜K™šžœ˜!šžœ˜%Kšœ ŸœŸœŸœ™:šžœŸœŸœ˜3KšœŸœ˜KšœŸœ˜K˜qKšœ˜—Kš ¡™¡K™K˜4KšœŸœ˜+KšœŸœ ˜(K˜ZšŸœXŸœŸœŸ˜nK˜K˜šŸœŸœŸœ˜K˜%Kšœ ŸœŸœ˜?K˜—šŸœŸ˜˜ KšœVŸœ˜\Kšœ"˜"KšŸœŸœ'ŸœŸœ˜8KšœLŸœ˜RKšœ"˜"KšŸœŸœ'ŸœŸœ˜8K˜—˜ Kšœ_Ÿœ˜eKšœ!˜!KšŸœŸœ&ŸœŸœ˜7K˜—˜ KšœhŸœ˜nKšœ#˜#KšŸœŸœ(ŸœŸœ˜9K˜—˜ KšœŸœ/˜;K˜4K˜#K˜1KšœbŸœ˜hKšœ$˜$Kš ŸœŸœ)ŸœŸœ ˜JK˜,K˜K˜(KšœXŸœ˜^Kšœ$˜$Kš ŸœŸœ)ŸœŸœ ˜IK˜—˜K˜Kšœ!Ÿœ"˜FK˜&K˜(K˜$KšœNŸœ˜TKšœ%˜%KšŸœŸœ*ŸœŸœ˜;K˜—KšŸœŸœ ˜)—KšŸœ˜—K˜K˜—Kšœ ž œ£œ  ˜NKšœbŸœŸœ˜pK˜"KšœŸœ˜!Kšœ ¡ œ  0˜PKšœ<˜˜>KšœŸœ˜0K˜—š ž œŸœŸœŸœŸœ˜JK˜CKšœL™LKšœ ¡œŸœ˜3K˜—KšŸœ&Ÿœe˜‘šŸœ˜Kšœ ¡ œ£œ ˜5K™:Kšœ ¡ œ ˜K˜Kšœ,£œ£œ£˜HKšœ+£¡ £œ£˜Kš ŸœŸœŸœ!ŸœŸœŸ˜EKšœ4˜4KšŸœ˜—K˜$KšœŸœ˜!Kšœ"˜"Kšœ ¡ œ  0˜PKšœb˜bKšœ}ŸœŸœ˜›K˜—K˜K˜—šžœ˜I artworkFigure–G41.62778 mm topLeading 41.62778 mm topIndent 1.411111 mm bottomLeading •Bounds:0.0 mm xmin 0.0 mm ymin 126.6472 mm xmax 38.80556 mm ymax •Artwork Interpress• InterpressõInterpress/Xerox/3.0  f j k j¡¥“ÄWB ¤ ¨  ¡£€S ¢ ¨ r j ¡ ¢ ¨¡¡¨ÄWB ¤ ¨ r j¡¥“ÄWB ¤ ¨¡¡¨¢¯“¡¡¨¢·“¢°“ÑS™Û3—P÷—v —˜£¯“¡¡¨¢·“¢°“ÄA¶—Ä1?r™ÄC{—Ä4Ïr—ÄE@—Ä1?r—˜¢¯“¡¡¨¢·“¢°“-™Ä×ßÄb#ÄVoÊÄT!*A¡’Ä#iGĨnÄO;ŽÄwá¼>@¡’˜ r jHÄŽæ ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“Á attractor– k é r jÇÄ, a ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“ÁnewSeg1– k é r jÄ+[ ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“ÁnewSeg2– k é¢¯“¡¡¨¢·“¢°“-™ÄA ­Ä¥W—-—ÄvéÄKЇ—˜¢¯“¡¡¨¢·“¢°“¤3™÷—˜ r j½ý ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“ÁnewRun– k é r j °“¢¯“¡¡¨ÄbKQÄLî­ÄbKQÄ'ýS¡¹¢¯“¡¡¨ÄbKQÄ'ýSÄMé Ä'ýS¡¹¢¯“¡¡¨ÄMé Ä'ýSÄMé ÄLî­¡¹¢¯“¡¡¨ÄMé ÄLî­ÄbKQÄLî­¡¹ k é r jèÄE[ ¢ ¨ÅXeroxÅ TiogaFontsÅ Helvetica10£¡ “ •  —¡¡¨  Š¡²“ÁnewJoint– k é k é k é k gšœ=™=Kšœ˜K˜KšœŸœ˜ K˜(K˜Kšœ Ÿœ˜Kšœ˜K˜Kšœ ŸœŸœ˜K˜K˜K˜K˜*K–![caret: GGInterfaceTypes.Caret]˜/KšŸœ ŸœŸœŸœ ˜)Kš ŸœŸœŸœŸœŸœ ˜@Kšœ Ÿœ ˜K˜JKš ŸœŸœ ŸœŸœŸœ ˜9KšŸœŸœŸœ˜4K˜6K˜Kšœ ž œ£ œ  ˜IKšœ$˜$Kšœ:˜:K˜7K˜'KšŸœŸœ,ŸœŸœ˜=KšŸœŸœ,ŸœŸœ˜=K˜(Kšœ"Ÿœ˜'KšŸœ2ŸœŸœ ˜OK™9Kšœ ¡ œ ˜Kšœ ¡œ"Ÿœ˜@Kšœ¡œ˜CKšœ#ŸœŸœ#Ÿœ˜VJšœcŸœ™iKšœ ¡œ-Ÿœ˜KK˜#šŸœŸœ˜%Kšœ Ÿœ(˜5Kšœ%˜%Kšœ.˜.Kšœ4˜4K˜—šŸœ˜K˜-Kšœ Ÿœ/˜K–#[ggData: GGInterfaceTypes.GGData]šœ ¡ œ˜&Kš œ1ŸœŸœŸœ4ŸœŸœ˜­K˜—šŸœŸœŸœ ˜5K–#[ggData: GGInterfaceTypes.GGData]šœ ¡ œ˜&KšŸœŸœ Ÿœ ^˜¯KšŸœvŸœŸœ˜˜Kšœ Ÿœ ˜>K˜—Kšœs¡ œ?™¿šŸœ$Ÿœ˜,KšœNŸœ˜YKšœŸœ"Ÿœ˜JKšœ%Ÿœ˜+K˜—šŸœ$Ÿœ˜,KšœNŸœ˜YKšœŸœ"Ÿœ˜JKšœ%Ÿœ˜+K˜—Kšœ-˜-K˜K˜—šž œ˜KšœŸœŸœ˜&K–M[feedback: Feedback.FeedbackData, msg: ROPE, msgType: Feedback.MsgType]šœ;˜;K˜K˜—šžœ˜#KšœŸœŸœ ˜4K˜K˜K˜K˜—šžœ˜!KšœŸœŸœ ˜7K˜K˜K˜—K˜Kšžœ˜K˜K˜KšžœŸœŸœgŸœŸœŸœŸœ˜ÌK˜šžœŸœ˜Kšœ8˜8K˜K™ Kšœ2Ÿœ˜8Kšœ,Ÿœ˜2Kšœ4Ÿœ˜:Kšœ6˜6Kšœ<˜˜>Kšœ,˜,Kšœ0˜0Kšœ0˜0Kšœ6˜6Kšœ4˜4K˜K™Kšœ;Ÿœ˜BKšœ"˜"Kšœ#Ÿœ ˜GK™K™ Kšœ:˜:Kšœ6˜6Kšœ8˜8Kšœ:˜:Kšœ"˜"Kšœ8˜8Kšœ>˜>Kšœ*˜*Kšœ2™2K™K™Kšœ,˜,Kšœ.˜.Kšœ.˜.Kšœ-˜-Kšœ/˜/Kšœ1˜1Kšœ2˜2Kšœ4˜4Kšœ1˜1Kšœ-˜-Kšœ<˜˜>Kšœ/˜/K˜K˜K™Kšœ(˜(Kšœ&˜&Kšœ6˜6Kšœ4˜4Kšœ2˜2K˜@Kšœ2˜2Kšœ4˜4K˜K™ Kšœ"˜"K˜K™Kšœ6˜6Kšœ.Ÿœ˜5Kšœ,Ÿœ˜3Kšœ:˜:Kšœ6˜6Kšœ6˜6K˜K™Kšœ4Ÿœ˜;KšœDŸœ˜KKšœ6˜6K™K˜—K˜KšœŸœŸœ˜"K˜K˜K˜KšŸœ˜—…—Ðä%†