<> <> <> <> <> <> <<>> DIRECTORY Ascii, BasicTime, CodeTimer, CubicSplines, Feedback, FeedbackTypes, FileNames, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGControlPanelTypes, GGCoreOps, GGCoreTypes, GGEvent, GGFileOps, GGFont, GGFromImager, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGModelTypes, GGMultiGravity, GGOutline, GGParent, GGParseOut, GGRefresh, GGRefreshTypes, GGScene, GGSegment, GGSegmentTypes, GGSelect, GGShapes, GGSlice, GGSliceOps, GGState, GGTraj, GGUIUtility, GGUserInput, GGUserProfile, GGUtility, GGWindow, Imager, ImagerInterpress, ImagerTransformation, Interpress, IO, IPMaster, PBasics, PFS, Random, Real, RealFns, Rope, TextNode, TiogaOps, TiogaOpsDefs, Vectors2d; GGEventImplA: CEDAR PROGRAM IMPORTS BasicTime, CodeTimer, Feedback, FileNames, GGAlign, GGBoundBox, GGCaret, GGCoreOps, GGEvent, GGFileOps, GGFont, GGFromImager, GGHistory, GGMultiGravity, GGOutline, GGParent, GGParseOut, GGRefresh, GGScene, GGSegment, GGSelect, GGShapes, GGSlice, GGSliceOps, GGState, GGTraj, GGUIUtility, GGUserInput, GGUserProfile, GGUtility, GGWindow, Imager, ImagerInterpress, ImagerTransformation, Interpress, IO, IPMaster, PBasics, PFS, Random, RealFns, Rope, TiogaOps, Vectors2d EXPORTS GGEvent, GGInterfaceTypes = BEGIN AlignBag: TYPE = GGInterfaceTypes.AlignBag; BoundBox: TYPE = GGModelTypes.BoundBox; Camera: TYPE = GGModelTypes.Camera; Caret: TYPE = GGInterfaceTypes.Caret; ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes ControlPointGenerator: TYPE = GGModelTypes.ControlPointGenerator; DisplayStyle: TYPE = GGModelTypes.DisplayStyle; MsgRouter: TYPE = FeedbackTypes.MsgRouter; FeatureData: TYPE = GGInterfaceTypes.FeatureData; FontData: TYPE = GGFont.FontData; GGData: TYPE = GGInterfaceTypes.GGData; GravityType: TYPE = GGInterfaceTypes.GravityType; HistoryEvent: TYPE = GGHistoryTypes.HistoryEvent; Joint: TYPE = GGModelTypes.Joint; JointGenerator: TYPE = GGModelTypes.JointGenerator; Orientation: TYPE = GGModelTypes.Orientation; Point: TYPE = GGBasicTypes.Point; RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; Scene: TYPE = GGModelTypes.Scene; Segment: TYPE = GGSegmentTypes.Segment; SegmentGenerator: TYPE = GGModelTypes.SegmentGenerator; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; SliceDescriptorObj: TYPE = GGModelTypes.SliceDescriptorObj; SliceGenerator: TYPE = GGModelTypes.SliceGenerator; SliceParts: TYPE = GGModelTypes.SliceParts; StrokeEnd: TYPE = Imager.StrokeEnd; StrokeJoint: TYPE = Imager.StrokeJoint; Transformation: TYPE = Imager.Transformation; Traj: TYPE = GGModelTypes.Traj; TrajData: TYPE = GGModelTypes.TrajData; TrajEnd: TYPE = GGModelTypes.TrajEnd; TrajParts: TYPE = GGModelTypes.TrajParts; TriggerBag: TYPE = GGInterfaceTypes.TriggerBag; UserInputProc: TYPE = GGEvent.UserInputProc; Vector: TYPE = GGBasicTypes.Vector; WalkProc: TYPE = GGModelTypes.WalkProc; pointsPerIn: REAL = 72.0; cmPerInch: REAL = 2.54; metersPerPixel: REAL = 0.0254/72.0; reallyBigReal: REAL = 1.0e37; DeleteHoles: UserInputProc = { IF GGSelect.NoSelections[ggData.scene, normal] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "UnmakeHoles failed: select some composite outlines for Unmake holes"] ELSE { DoDeleteHoles: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF GGSliceOps.IsCompleteParts[sliceD] THEN { DoUnmakeChild: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { <> newSlice: Slice _ IF GGSliceOps.GetType[slice]=$Traj THEN GGOutline.CreateOutline[slice, fillColor] ELSE slice; newSlice.parent _ cluster; -- reparent BEFORE reselecting <> <> newSliceList _ GGUtility.AppendSliceList[newSliceList, LIST[newSlice]]; IF GGSliceOps.GetType[newSlice]=$Box AND firstBoxChild=NIL THEN { firstBoxChild _ newSlice; GGSlice.SetBoxText[newSlice, fillText, screenStyle, NIL]; } ELSE GGSlice.SetBoxText[newSlice, [NIL, 0], screenStyle, NIL]; unmakeCount _ unmakeCount+1; }; <<>> fillText: TextNode.Location; screenStyle: BOOL _ FALSE; newSliceList: LIST OF Slice; firstBoxChild: Slice; outline: Slice _ sliceD.slice; cluster: Slice _ GGParent.GetParent[outline]; fillColor: Imager.Color _ GGSliceOps.GetFillColor[outline, NIL].color; [fillText, screenStyle] _ GGSlice.GetBoxText[outline]; -- first box child will inherit GGOutline.SaveSelectionsInOutlineAllClasses[outline]; IF GGScene.IsTopLevel[outline] THEN { -- all children become top level priority: INT _ GGScene.GetPriority[ggData.scene, outline]; GGScene.DeleteSlice[ggData.scene, outline]; -- will replace with exploded version [] _ GGParent.WalkChildren[outline, leaf, DoUnmakeChild]; GGScene.AddSlices[ggData.scene, newSliceList, priority]; reselectList _ GGUtility.AppendSliceList[reselectList, newSliceList]; } ELSE { -- child outline to be replaced by all its children in the parent cluster priority: INT _ GGParent.GetChildPriority[cluster, outline]; [] _ GGSlice.RemoveChild[cluster, outline]; -- will replace with all children [] _ GGParent.WalkChildren[outline, leaf, DoUnmakeChild]; GGSlice.AddChildrenToCluster[cluster, newSliceList, priority]; reselectList _ GGUtility.AppendSliceList[reselectList, LIST[cluster]]; }; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "UnmakeHoles: Ignoring partially selected outline for Unmake holes"]; }; unmakeCount: INT _ 0; reselectList: LIST OF Slice; -- needed because can't reselect while walking selections GGHistory.NewCapture["Unmake holes", ggData];-- capture scene BEFORE UPDATE <> GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; [] _ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoDeleteHoles, normal, $Outline]; FOR reselects: LIST OF Slice _ reselectList, reselects.rest UNTIL reselects=NIL DO GGSelect.ReselectSliceAllClasses[reselects.first, ggData.scene]; ENDLOOP; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list IF unmakeCount#0 THEN { Feedback.Append[ggData.router, oneLiner, $Feedback, "UnmakeHoles: holes unmade"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "UnmakeHoles failed: no holes were unmade"]; }; }; GetAddHolesArguments: PROC [scene: Scene, router: MsgRouter] RETURNS [distinguished: Slice, outlinesForHoles: LIST OF Slice, cluster: Slice, success: BOOL _ TRUE] = { <> DoFindHolesAndReverse: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { classType: ATOM _ GGSliceOps.GetType[sliceD.slice]; IF classType = $Cluster AND sliceD # ancestorD THEN { abortedDueToCluster _ TRUE; RETURN[TRUE]; }; IF classType = $IP OR classType = $Text THEN { RETURN[TRUE]; }; outlineList _ CONS[sliceD.slice, outlineList]; -- cheap trick to reverse order. }; outlineList: LIST OF Slice; classType: ATOM; aborted: BOOL _ FALSE; abortedDueToCluster: BOOL _ FALSE; ancestorD: SliceDescriptor _ GGScene.LastSelectedSlice[scene, first, normal]; IF GGSliceOps.GetType[ancestorD.slice] = $Cluster THEN { DoFindNonCluster: PROC [childD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF GGSliceOps.GetType[childD.slice] # $Cluster THEN { distinguished _ childD.slice; done _ TRUE; }; }; aborted _ GGParent.WalkIncludedChildren[ancestorD.slice, ancestorD.parts, all, DoFindNonCluster]; IF NOT aborted THEN ERROR; -- a cluster without any children? } ELSE distinguished _ ancestorD.slice; cluster _ GGParent.GetParent[distinguished]; classType _ GGSliceOps.GetType[distinguished]; IF classType = $IP OR classType = $Text THEN GOTO TextAndIPCannotBeHoles; aborted _ GGScene.WalkSelectedSlices[scene, first, DoFindHolesAndReverse, normal]; IF aborted THEN IF abortedDueToCluster THEN GOTO SomeHolesAreClusters ELSE GOTO TextAndIPCannotBeHoles; outlinesForHoles _ outlineList.rest; IF outlinesForHoles=NIL THEN GOTO NoHolesSelected; EXITS SomeHolesAreClusters => { success _ FALSE; Feedback.Append[router, oneLiner, $Complaint, "MakeHoles failed: select some UNCLUSTERED top level slices FIRST to become new holes"]; }; NoHolesSelected => { success _ FALSE; Feedback.Append[router, oneLiner, $Complaint, "MakeHoles failed: select some CLOSED top level slices FIRST to become new holes"]; }; TextAndIPCannotBeHoles => { success _ FALSE; Feedback.Append[router, oneLiner, $Complaint, "MakeHoles failed: text and IP slices cannot be nor receive holes"]; }; }; AddHoles: UserInputProc = { scene: Scene _ ggData.scene; router: MsgRouter _ ggData.router; IF GGSelect.NoSelections[scene, normal] THEN Feedback.Append[router, oneLiner, $Complaint, "MakeHoles failed: select some closed outlines for MakeHoles"] ELSE { hasFillText, screenStyle, success: BOOL _ FALSE; fillText: TextNode.Location; firstPriority: INT _ -1; -- on top outlinesForHoles, newHoles: LIST OF Slice; distinguished, ancestor, newOutline: Slice; -- will have holes added to it cluster: Slice; -- NIL if this AddHoles takes place at top level [distinguished, outlinesForHoles, cluster, success] _ GetAddHolesArguments[scene, router]; IF NOT success THEN RETURN; GGHistory.NewCapture["Make holes", ggData]; -- capture scene BEFORE UPDATE GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[ggData.caret, NIL]; <> GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; [fillText, screenStyle] _ GGSlice.GetBoxText[distinguished]; hasFillText _ fillText.node#NIL; ancestor _ GGParent.GetTopLevelAncestor[distinguished]; GGSelect.SaveSelectionsInSliceAllClasses[ancestor, scene]; <<>> <> SELECT GGSliceOps.GetType[distinguished] FROM $Box, $Circle => { -- create a one-child outline theChild: Slice _ GGSliceOps.Copy[distinguished].first; GGSlice.SetBoxText[theChild, [NIL,0], TRUE, NIL]; -- clear out textFill for child [] _ GGSliceOps.SetOrientation[theChild, NIL, ccw, NIL]; -- by convention, theChild is ccw newOutline _ GGOutline.CreateOutline[theChild, GGSliceOps.GetFillColor[theChild, NIL].color]; }; $Outline => { -- distinguished must have a closed first child for now. Mutate it. firstChild: Slice _ GGParent.FirstChild[distinguished, leaf]; IF GGSliceOps.GetType[firstChild]=$Traj THEN { IF NARROW[firstChild.data, TrajData].role#fence THEN { Feedback.Append[router, oneLiner, $Complaint, "MakeHoles failed: extend selection to a CLOSED object LAST to receive new holes"]; RETURN; }; [] _ GGSliceOps.SetOrientation[firstChild, NIL, ccw, NIL]; -- by convention, firstChild is ccw }; newOutline _ distinguished; }; $Traj => { -- uh oh Feedback.Append[router, oneLiner, $Complaint, "MakeHoles failed: encountered top level trajectory. Punting"]; RETURN; }; ENDCASE => ERROR; <> <> FOR list: LIST OF Slice _ outlinesForHoles, list.rest UNTIL list = NIL DO nextHole: Slice _ list.first; GGSelect.SaveSelectionsInSliceAllClasses[nextHole, scene]; SELECT GGSliceOps.GetType[nextHole] FROM $Outline => { -- walk outline children as holes. Only one level for now. <> <> IF NOT GGSelect.IsSelectedInFull[nextHole, scene, normal] THEN Feedback.Append[router, oneLiner, $Complaint, "MakeHoles: ignoring partially selected outline"] ELSE { DoNewHoles: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { IF GGSliceOps.GetType[slice]=$Traj AND NARROW[slice.data, TrajData].role=open THEN Feedback.Append[router, oneLiner, $Complaint, "MakeHoles: ignoring open hole candidate"] ELSE { [] _ GGSliceOps.SetOrientation[slice, NIL, cw, NIL]; GGSlice.SetBoxText[slice, [NIL,0], TRUE, NIL]; -- lose fillText if you become a hole newHoles _ GGUtility.AppendSliceList[newHoles, LIST[slice]]; }; }; [] _ GGParent.WalkChildren[nextHole, all, DoNewHoles]; -- all the children IF newHoles#NIL THEN GGScene.DeleteSlice[scene, nextHole]; <> }; }; $Box, $Circle => { [] _ GGSliceOps.SetOrientation[nextHole, NIL, cw, NIL]; GGSlice.SetBoxText[nextHole, [NIL,0], TRUE, NIL]; -- lose fillText if you become a hole newHoles _ GGUtility.AppendSliceList[newHoles, LIST[nextHole]]; GGScene.DeleteSlice[scene, nextHole]; -- get rid of old top level slice }; $Text, $IP, $Cluster => NULL; -- can't be holes $Traj => { Feedback.Append[router, oneLiner, $Complaint, "MakeHoles: ignoring top level trajectory as hole candidate"]; -- shouldn't be any topLevel trajs now }; ENDCASE => ERROR; ENDLOOP; IF newHoles#NIL THEN { IF cluster = NIL THEN { -- distinguished is top level priority: INT _ GGScene.GetPriority[scene, distinguished]; GGScene.DeleteSlice[scene, distinguished]; GGScene.AppendHoles[newOutline, newHoles]; GGScene.AddSlice[scene, newOutline, priority]; GGOutline.SetFillText[newOutline, fillText.node, screenStyle, NIL]; GGSelect.ReselectSliceAllClasses[newOutline, scene]; } ELSE { priority: INT _ GGParent.GetChildPriority[cluster, distinguished]; [] _ GGSlice.RemoveChild[cluster, distinguished]; GGScene.AppendHoles[newOutline, newHoles]; GGSlice.AddChildToCluster[cluster, newOutline, priority]; GGOutline.SetFillText[newOutline, fillText.node, screenStyle, NIL]; GGSelect.ReselectSliceAllClasses[ancestor, scene]; }; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.Append[router, oneLiner, $Feedback, "MakeHoles: holes added"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; }; }; EnterEditingMode: PROC [ggData: GGData, refChar: REF CHAR, withFont: FontData _ NIL] = { <> CodeTimer.StartInt[$EnterEditingMode, $Gargoyle]; IF refChar^=Ascii.BS THEN { -- try to enter editing of a single selected text slice IF GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Text]#1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Edit text failed: select a single text slice for editing"] ELSE { -- BS typed when text selected. Open that text for editing sliceDesc: SliceDescriptor _ GGScene.FirstSelectedSlice[ggData.scene, leaf, normal, $Text]; slice: Slice _ sliceDesc.slice; fontData: FontData _ GGSlice.GetFontData[slice]; origin: Point _ ImagerTransformation.Transform[fontData.transform, [0.0, 0.0] ]; -- find the origin of the text string in world coordinates GGHistory.NewCapture["Editing text", ggData]; -- just like a StartAdd, but for text GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, hotCPs: TRUE, caret: TRUE, attractor: TRUE]; GGCaret.SetAttractor[ggData.caret, origin, [0.0, -1.0], NIL]; -- move the caret to that point in anticipation of terminating CR for this string, but don't set the attractor to the sliceDesc to avoid feedback GGSelect.DeselectSlice[slice: slice, parts: sliceDesc.parts, scene: ggData.scene, selectClass: normal]; ggData.refresh.textInProgress _ slice; -- successfully enter editing mode ggData.refresh.addedObject _ GGParent.GetParent[slice]; IF ggData.refresh.addedObject = NIL THEN ggData.refresh.addedObject _ slice; Feedback.PutF[ggData.router, oneLiner, $Feedback, "Editing text"]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: IF GGSelect.IsSelectedInPart[sliceDesc.slice, ggData.scene, hot] THEN triggerBag ELSE sceneBag, edited: FALSE, okToSkipCapture: TRUE]; }; } ELSE { -- start a new text string at the caret IF GGCaret.Exists[ggData.caret] THEN { GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, caret: TRUE, attractor: TRUE]; GGCaret.NoAttractor[ggData.caret]; -- so the feedback goes away. GGHistory.NewCapture["Editing text", ggData]; -- just like a StartAdd, but for text ggData.refresh.textInProgress _ NewTextSlice[parent: NIL, text: Rope.FromChar[refChar^], ggData: ggData, selectIt: FALSE, withFont: withFont, skipCapture: TRUE]; -- start a new text slice. NewTextSlice deselects all, adds text slice, and refreshes scene Feedback.PutF[ggData.router, oneLiner, $Feedback, "Editing text"]; } ELSE Feedback.Append[ggData.router, oneLiner, $Complaint, "New text failed: caret required for new text origin"]; }; CodeTimer.StopInt[$EnterEditingMode, $Gargoyle]; }; -- end EnterEditingMode AddChar: PUBLIC UserInputProc = { <> slice, parent: Slice; refChar: REF CHAR; IF NARROW[event.first, ATOM]#$AddChar THEN ERROR; refChar _ NARROW[event.rest.first]; IF ggData.refresh.textInProgress=NIL THEN EnterEditingMode[ggData, refChar] ELSE { -- add to textInProgress CodeTimer.StartInt[$AddChar, $Gargoyle]; slice _ ggData.refresh.textInProgress; parent _ IF ggData.refresh.addedObject = slice THEN NIL ELSE ggData.refresh.addedObject; SELECT refChar^ FROM Ascii.ControlW, Ascii.BS => { <> GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE, hotCPs: TRUE, caret: TRUE, attractor: TRUE]; GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[slice], NIL]; GGSlice.BackspaceText[slice: slice, word: refChar^=Ascii.ControlW]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: IF GGSelect.IsSelectedInPart[slice, ggData.scene, hot] THEN triggerBag ELSE sceneBag, edited: TRUE, okToSkipCapture: TRUE]; }; Ascii.CR, Ascii.LF => { -- new line. Careful about embedded Text slice caretPos: Point _ GGCaret.GetPoint[ggData.caret]; -- assumes caret already positioned at origin of old line fontData: FontData _ GGSlice.GetFontData[slice]; newVec: Point _ ImagerTransformation.TransformVec[fontData.transform, [0.0, -GGSlice.GetTextLineSpacing[slice] ] ]; newPoint: Point _ [caretPos.x+newVec.x, caretPos.y+newVec.y]; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, caret: TRUE, attractor: TRUE]; GGCaret.SetAttractor[ggData.caret, newPoint, [0.0, -1.0], NIL]; GGRefresh.EnlargeStartBox[ggData, GGCaret.BoundBoxOfCaret[ggData.caret, ggData], NIL]; <> GGEvent.SawTextFinish[ggData, event]; -- terminates input, does PushCurrent for histoy, and MAY refresh screen GGHistory.NewCapture["Editing text", ggData]; -- just like a ContinueAdd, but for text ggData.refresh.textInProgress _ NewTextSlice[parent: parent, text: "", ggData: ggData, selectIt: FALSE, withFont: fontData, skipCapture: FALSE]; -- start a new EMPTY text slice, inheriting the font from the old line. NewTextSlice adds slice and refreshes scene GGSlice.SetTextLineSpacing[ggData.refresh.textInProgress, GGSlice.GetTextLineSpacing[slice], NIL]; }; ENDCASE => { -- any other char isHot: BOOL _ GGSelect.IsSelectedInPart[slice, ggData.scene, hot]; GGSlice.AppendText[slice: slice, text: Rope.FromChar[refChar^] ]; -- this can only make the slice bound box LARGER <> GGRefresh.NullStartBox[ggData]; GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[slice], NIL]; <> GGWindow.RestoreScreenAndInvariants[paintAction: IF isHot THEN $ObjectChangedBoundBoxProvided ELSE $ObjectAdded, ggData: ggData, remake: IF isHot THEN triggerBag ELSE none, edited: TRUE, okToSkipCapture: TRUE]; }; CodeTimer.StopInt[$AddChar, $Gargoyle]; }; }; -- end AddChar NewTextSlice: PRIVATE PROC [parent: Slice, text: Rope.ROPE, ggData: GGData, selectIt: BOOL _ FALSE, withFont: FontData _ NIL, skipCapture: BOOL _ FALSE] RETURNS [slice: Slice] = { caretPos: Point _ GGCaret.GetPoint[ggData.caret]; success: BOOL _ FALSE; camera: Camera _ ggData.camera; fontData: FontData _ GGFont.CopyFontData[IF withFont=NIL THEN GGState.GetDefaultFont[ggData] ELSE withFont]; slice _ GGSlice.MakeTextSlice[text, ggData.defaults.textColor, camera.displayStyle, 1.0, ggData.defaults.dropShadowOn, ggData.defaults.dropShadowOffset, ggData.defaults.dropShadowColor]; fontData.transform _ ImagerTransformation.TranslateTo[fontData.transform, caretPos]; success _ GGSlice.SetTextFontAndTransform[slice, fontData, ggData.router, NIL]; IF NOT success THEN ERROR; IF parent=NIL THEN { GGScene.AddSlice[ggData.scene, slice, -1]; ggData.refresh.addedObject _ slice; } ELSE { GGSlice.AddChildToCluster[parent, slice, -1]; ggData.refresh.addedObject _ parent; }; <> GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[slice], NIL]; <> GGSelect.DeselectAll[ggData.scene, normal]; -- speeds up text entry IF selectIt THEN GGEvent.SelectEntireSlice[slice, ggData.scene, normal, ggData]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectAdded, ggData: ggData, remake: sceneBag, edited: TRUE, okToSkipCapture: skipCapture]; }; AddText: UserInputProc = { text: Rope.ROPE _ NARROW[event.rest.first]; IF Rope.Equal[text, NIL] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "Add text failed: select a non-empty string for AddText"] ELSE { GGHistory.NewCapture["Add text", ggData]; -- capture scene BEFORE UPDATE GGRefresh.NullStartBox[ggData]; [] _ NewTextSlice[parent: NIL, text: text, ggData: ggData, selectIt: TRUE, skipCapture: FALSE]; GGHistory.PushCurrent[ggData]; }; }; SetAmplifySpace: UserInputProc = { AmplifySpaceFromSelection[ggData, event]; }; AmplifySpaceFromSelection: UserInputProc = { amplifySpace: REAL _ NARROW[event.rest.first, REF REAL]^; count: INT _ GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Text]; IF count<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "AmplifySpace failed: select at least one text object to amplify space"] ELSE IF amplifySpace>reallyBigReal THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "AmplifySpace failed: select a reasonable number for amplify space"] ELSE { DoAmplifySpace: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { someD _ sliceD; GGSlice.SetTextAmplifySpace[sliceD.slice, amplifySpace, ggData.router, currentEvent]; [newSelectList, ptr] _ GGUtility.AddSlice[sliceD.slice, newSelectList, ptr]; }; currentEvent: HistoryEvent; someD: SliceDescriptor; newSelectList, ptr: LIST OF Slice; [newSelectList, ptr] _ GGUtility.StartSliceList[]; <> GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; currentEvent _ GGHistory.NewCurrent["Amplify space", ggData]; [] _ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoAmplifySpace, normal, $Text]; 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.PutF[ggData.router, oneLiner, $Feedback, "AmplifySpace: amplify space set to %g", [real[amplifySpace]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; -- GGSlice.SetTextAmplifySpace can post error messages }; }; PrintAmplifySpace: UserInputProc = { count: INT _ GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Text]; IF count<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowAmplifySpace failed: select at least one text object to show amplify space"] ELSE { CheckAmplifySpace: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF amplifySpace = reallyBigReal THEN amplifySpace _ GGSlice.GetTextAmplifySpace[sliceD.slice]; RETURN[amplifySpace#GGSlice.GetTextAmplifySpace[sliceD.slice]]; -- abort if multiple values }; amplifySpace: REAL _ reallyBigReal; aborted: BOOL _ GGScene.WalkSelectedSlices[ggData.scene, leaf, CheckAmplifySpace, normal, $Text]; IF aborted THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowAmplifySpace failed: multiple amplification values selected"] ELSE Feedback.PutF[ggData.router, oneLiner, $Feedback, "ShowAmplifySpace: amplification is %g", [real[amplifySpace]]]; }; }; SetDropShadow: UserInputProc = { scale: REAL _ 10.0; -- convert fractions to offsets needed by SetDropShadow offsetX: REAL _ NARROW[event.rest.first, REF REAL]^; offsetY: REAL _ NARROW[event.rest.rest.first, REF REAL]^; count: INT _ GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Text]; IF count<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetDropShadow failed: select at least one text object to set drop shadow"] ELSE IF offsetX>reallyBigReal OR offsetY>reallyBigReal THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetDropShadow failed: select reasonable values for drop shadow offsets"] ELSE { DoSetDropShadow: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { someD _ sliceD; GGSlice.DropShadowOn[sliceD.slice, [offsetX, offsetY], currentEvent]; -- elements are signed and 10X, like [-5.0, -2.5] for 50% left and 25% down [newSelectList, ptr] _ GGUtility.AddSlice[sliceD.slice, newSelectList, ptr]; }; currentEvent: HistoryEvent; someD: SliceDescriptor; newSelectList, ptr: LIST OF Slice; [newSelectList, ptr] _ GGUtility.StartSliceList[]; <> GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; currentEvent _ GGHistory.NewCurrent["Set drop shadow", ggData]; -- start new history event [] _ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoSetDropShadow, normal, $Text]; 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.PutF[ggData.router, oneLiner, $Feedback, "SetDropShadow: drop shadow set to [%g, %g]", [real[offsetX/scale]], [real[offsetY/scale]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; }; DropShadowFromSelection: UserInputProc = { scale: REAL _ 10.0; -- convert fractions to offsets needed by SetDropShadow offset: REAL _ NARROW[event.rest.first, REF REAL]^; -- positive fraction IF offset>reallyBigReal THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetDropShadow failed: select reasonable values for drop shadow offsets"] ELSE SetDropShadow[ggData, LIST [event.first, NEW[REAL _ -offset*scale], NEW[REAL _ -offset*scale] ] ]; -- down and left fraction }; DropShadowTenPercent: UserInputProc = { scale: REAL _ 10.0; -- convert fractions to offsets needed by SetDropShadow offset: REAL _ 0.1; -- positive fraction SetDropShadow[ggData, LIST [event.first, NEW[REAL _ -offset*scale], NEW[REAL _ -offset*scale] ] ]; -- down and left fraction }; PrintDropShadow: UserInputProc = { count: INT _ GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Text]; IF count<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowDropShadow failed: select at least one text object to show drop shadow"] ELSE { CheckDropShadow: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF firstOffset.x=reallyBigReal THEN [firstOn, firstOffset] _ GGSlice.GetTextDropShadow[sliceD.slice]; [on, offset] _ GGSlice.GetTextDropShadow[sliceD.slice]; RETURN[firstOn#on OR firstOffset#offset]; -- abort if multiple values }; scale: REAL _ 10.0; -- convert fractions to offsets needed firstOn, on: BOOL _ FALSE; firstOffset, offset: Vector _ [reallyBigReal, reallyBigReal]; aborted: BOOL _ GGScene.WalkSelectedSlices[ggData.scene, leaf, CheckDropShadow, normal, $Text]; IF aborted THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowDropShadow failed: multiple drop shadow values selected"] ELSE { feedbackRope: Rope.ROPE _ SELECT TRUE FROM NOT on => "ShowDropShadows: no drop shadows", ENDCASE => IO.PutFR["ShowDropShadows: drop shadows are [%g, %g]", [real[offset.x/scale]], [real[offset.y/scale]] ]; Feedback.PutF[ggData.router, oneLiner, $Show, feedbackRope ]; }; }; }; DropShadowOff: UserInputProc = { count: INT _ GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Text]; IF count<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "DropShadowOff failed: select at least one text object to clear drop shadow"] ELSE { DoDropShadowOff: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { someD _ sliceD; GGSlice.DropShadowOff[sliceD.slice, currentEvent]; [newSelectList, ptr] _ GGUtility.AddSlice[sliceD.slice, newSelectList, ptr]; }; currentEvent: HistoryEvent; someD: SliceDescriptor; newSelectList, ptr: LIST OF Slice; [newSelectList, ptr] _ GGUtility.StartSliceList[]; <> GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; currentEvent _ GGHistory.NewCurrent["Drop shadow off", ggData]; -- start new history event [] _ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoDropShadowOff, normal, $Text]; FOR list: LIST OF Slice _ newSelectList, list.rest UNTIL list = NIL DO GGSelect.SelectEntireSlice[list.first, ggData.scene, normal]; ENDLOOP; <> GGHistory.PushCurrent[ggData]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "DropShadowOff: completed"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; }; SetDefaultTextLooks: UserInputProc = { <> count: INT _ GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Text]; IF count<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "MakeDefaultTextLooks failed: select at least one text object to specify defaults"] ELSE { CheckTextLooks: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { looksLike _ sliceD.slice; IF looksRope=NIL THEN looksRope _ GGSlice.GetLooksDataRope[looksLike]; RETURN[NOT Rope.Equal[looksRope, GGSlice.GetLooksDataRope[looksLike], FALSE]]; -- abort if multiple values }; looksRope: Rope.ROPE; looksLike: Slice; aborted: BOOL _ GGScene.WalkSelectedSlices[ggData.scene, leaf, CheckTextLooks, normal, $Text]; IF aborted THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "MakeDefaultTextLooks failed: multiple look values selected"] ELSE { [ggData.defaults.dropShadowOn, ggData.defaults.dropShadowOffset, ggData.defaults.dropShadowColor] _ GGSlice.GetTextDropShadow[looksLike]; ggData.defaults.textColor _ GGSlice.GetTextColors[looksLike].textColor; Feedback.PutF[ggData.router, oneLiner, $Feedback, "MakeDefaultTextLooks: default text looks set to %g", [rope[GGSlice.GetLooksDataRope[looksLike]]] ]; }; }; }; ShowDefaultTextLooks: PUBLIC UserInputProc = { scale: REAL _ 10.0; -- convert fractions to offsets scratch: IO.STREAM _ IO.ROS[]; IF ggData.defaults.textColor#NIL THEN scratch.PutF["text color is %g ", [rope[GGUtility.DescribeColor[ggData.defaults.textColor]]] ] ELSE scratch.PutF["%g", [rope["no text color "]]]; IF ggData.defaults.dropShadowOn THEN { IF ggData.defaults.dropShadowColor#NIL THEN scratch.PutF["shadow color is %g ", [rope[GGUtility.DescribeColor[ggData.defaults.dropShadowColor]]] ] ELSE scratch.PutF["%g", [rope["no shadow color "]]]; scratch.PutF[" offset: [%g, %g]", [real[ggData.defaults.dropShadowOffset.x/scale]], [real[ggData.defaults.dropShadowOffset.y/scale]] ]; }; Feedback.PutF[ggData.router, oneLiner, $Show, "Default Text Looks: %g", [rope[IO.RopeFromROS[scratch]]] ]; }; <> <> <> <> <<>> <> <> <> <<>> <> <> <<>> <> <> <<>> <> <> <> <> <> <> < pair such as "Helvetica-BI 12". The <-FontFace> may be left off, denoting regular font. Gargoyle assumes FontPrefix = Xerox/PressFonts/ and lets transformation M = Scale[]. The font is made with ImagerFont.Scale[font, 1] (in all cases I know about).>> < pair such as "Helvetica-BI 12". The <-FontFace> may be left off, denoting regular font. Gargoyle assumes FontPrefix = Xerox/XC1-2-2/ and lets transformation M = Scale[]. The font is made with ImagerFont.Scale[font, 1] (in all cases I know about).>> < pair such as "CMR 10". Gargoyle assumes FontPrefix = Xerox/TiogaFonts/ and lets transformation M = Scale[]. The font is made with ImagerFont.Scale[font, 1] if the font is CMR, with ImagerFont.Scale[font, 1.0/], if the font is Helvetica, TimesRoman, Tioga. If the font is one of the traditional TiogaFonts (e.g. Helvetica, TimesRoman, Tioga, ...) Gargoyle will attempt to find the corresponding strike font. For example, SetScreenFont TimesRoman-BI 9 will find the font in file ///fonts/xerox/tiogafonts/TimesRoman9BI.ks. If the user requests a TiogaFont not in the Tioga font set (usually an odd size), Gargoyle will fail to change the font. I don't know which camp Terminal is in. At any rate, Gargoyle will have to keep track of which fonts are in which camp.>> < triple. Gargoyle makes no assumptions. The user must provide a factored transformation. For example:>> <> <> <> < quadruple. Again, Gargoyle uses its information about font camps to successfully make a unit sized font. Otherwise, Gargoyle makes no assumptions.>> <*(1.0/) isn't quite 1.0. We can either round within epsilon for screen fonts, or we can keep around an unscaled font when we know we should be hitting the fast case.>> <> <> <<>> <> SetFontAux: PROC [ggData: GGData, fontData: FontData, andTransform: BOOL _ FALSE] = { count: INT _ GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Text]; IF count<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFont failed: select at least one text object to set font"] ELSE { DoSetFontAux: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { success _ IF andTransform THEN GGSlice.SetTextFontAndTransform[sliceD.slice, fontData, ggData.router, currentEvent] ELSE GGSlice.SetTextFont[sliceD.slice, fontData, ggData.router, currentEvent]; IF NOT success THEN RETURN [TRUE]; -- SetTextFont will have posted an error [newSelectList, ptr] _ GGUtility.AddSlice[sliceD.slice, newSelectList, ptr]; someD _ sliceD; }; aborted: BOOL _ FALSE; currentEvent: HistoryEvent; someD: SliceDescriptor; success: BOOL _ FALSE; newSelectList, ptr: LIST OF Slice; [newSelectList, ptr] _ GGUtility.StartSliceList[]; <> GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; currentEvent _ GGHistory.NewCurrent["Set font", ggData]; -- start new history event aborted _ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoSetFontAux, normal, $Text]; 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]; -- push captured history event onto history list IF aborted AND someD#NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "SetFont problem: some text slices not set"]; IF NOT aborted THEN Feedback.PutF[ggData.router, oneLiner, $Feedback, "SetFont: font %g", [rope[GGSlice.GetFontDataRope[someD.slice]]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; }; SetPressFont: UserInputProc = { < . e.g. "Cream-BI 12">> errorRope: Rope.ROPE; BEGIN inStream: IO.STREAM _ IO.RIS[NARROW[event.rest.first]]; -- "Cream-BI 12" fontData: FontData _ GGFont.CreateFontData[]; fontData.prefix _ "xerox/pressfonts/"; -- default prefix for SetPressFont fontData _ GGFont.ParseFontData[data: fontData, inStream: inStream, familyP: TRUE, faceP: TRUE, scaleP: TRUE ! GGFont.ParseError => {errorRope _ explanation; GOTO ParseError;};]; -- family, face, scale SetFontAux[ggData, fontData]; EXITS ParseError => Feedback.Append[ggData.router, oneLiner, $Complaint, Rope.Concat["SetPressFont failed: ", errorRope]]; END; }; SetPrintFont: UserInputProc = { < . e.g. "Modern-BI 12">> errorRope: Rope.ROPE; BEGIN inStream: IO.STREAM _ IO.RIS[NARROW[event.rest.first]]; -- "Modern-BI 12" fontData: FontData _ GGFont.CreateFontData[]; fontData.prefix _ "xerox/xc1-2-2/"; -- default prefix for SetPrintFont fontData _ GGFont.ParseFontData[data: fontData, inStream: inStream, familyP: TRUE, faceP: TRUE, scaleP: TRUE ! GGFont.ParseError => {errorRope _ explanation; GOTO ParseError;};]; -- family, face, scale SetFontAux[ggData, fontData]; EXITS ParseError => Feedback.Append[ggData.router, oneLiner, $Complaint, Rope.Concat["SetXCFont failed: ", errorRope]]; END; }; SetScreenFont: UserInputProc = { < . e.g. "Cream12-BI 20">> errorRope: Rope.ROPE; BEGIN inStream: IO.STREAM _ IO.RIS[NARROW[event.rest.first]]; -- "Cream-BI 12" fontData: FontData _ GGFont.CreateFontData[]; fontData.prefix _ "xerox/tiogafonts/"; -- default prefix for SetScreenFont fontData _ GGFont.ParseFontData[data: fontData, inStream: inStream, familyP: TRUE, faceP: TRUE, scaleP: TRUE ! GGFont.ParseError => {errorRope _ explanation; GOTO ParseError;};]; -- family, face, scale SetFontAux[ggData, fontData]; EXITS ParseError => Feedback.Append[ggData.router, oneLiner, $Complaint, Rope.Concat["SetScreenFont failed: ", errorRope]]; END; }; SetFontDetailed: UserInputProc = { < e.g. "xerox/myfonts/fontOne-BI [1.0 2.0 3.0 4.0 5.0 6.0]. Gargoyle assumes the designSize and the storedSize can be derived from the and ; if not, then designSize and storedSize are defaulted to 1.0>> errorRope: Rope.ROPE; BEGIN inStream: IO.STREAM _ IO.RIS[NARROW[event.rest.first]]; fontData: FontData _ GGFont.CreateFontData[]; fontData _ GGFont.ParseFontData[data: fontData, inStream: inStream, prefixP: TRUE, familyP: TRUE, faceP: TRUE, transformP: TRUE ! GGFont.ParseError => {errorRope _ explanation; GOTO ParseError;};]; -- prefix, family, face, transform SetFontAux[ggData, fontData]; EXITS ParseError => Feedback.Append[ggData.router, oneLiner, $Complaint, Rope.Concat["SetDetailedFont failed: ", errorRope]]; END; }; SetFontLiteral: UserInputProc = { < . Gargoyle accepts this as literal information and makes no attempt at understanding the semantics of the data.>> errorRope: Rope.ROPE; BEGIN inStream: IO.STREAM _ IO.RIS[NARROW[event.rest.first]]; fontData: FontData _ GGFont.CreateFontData[]; fontData _ GGFont.ParseFontData[data: fontData, inStream: inStream, literalP: TRUE, transformP: TRUE, storedSizeP: TRUE, designSizeP: TRUE ! GGFont.ParseError => {errorRope _ explanation; GOTO ParseError;};]; -- literal name, transform, storedSize, designSize SetFontAux[ggData, fontData]; EXITS ParseError => Feedback.Append[ggData.router, oneLiner, $Complaint, Rope.Concat["SetLiteralFont failed: ", errorRope]]; END; }; ShowFontValues: UserInputProc = { < >> success: BOOL _ FALSE; fontRope: Rope.ROPE; [fontRope, ----, success] _ GetSelectedFont[ggData, "ShowFont", GGSlice.GetFontDataRope]; IF NOT success THEN RETURN; Feedback.PutF[ggData.router, oneLiner, $Show, "ShowFont: %g", [rope[fontRope]]]; }; ShowFontValuesLiteral: UserInputProc = { < >> success: BOOL _ FALSE; fontRope: Rope.ROPE; [fontRope, ----, success] _ GetSelectedFont[ggData, "ShowLiteralFont", GGSlice.GetFontLiteralDataRope]; IF NOT success THEN RETURN; Feedback.PutF[ggData.router, oneLiner, $Show, "ShowLiteralFont: %g", [rope[fontRope]]]; }; CopyFont: UserInputProc = { <> scene: Scene _ ggData.scene; lastDesc: SliceDescriptor _ GGSelect.GetLastSelection[scene]; count: INT _ GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Text]; IF count<2 OR lastDesc=NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "CopyFont failed: select at least one destination and then one source text object for copy font"] ELSE { BEGIN alikeData: FontData; DoCopyFont: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF sliceD#lastDesc THEN { saveTransform: ImagerTransformation.Transformation; oldData, copyData: FontData; oldData _ GGSlice.GetFontData[sliceD.slice]; saveTransform _ oldData.transform; -- shouldn't need to copy transformation copyData _ GGFont.CopyFontData[data: alikeData, oldCopy: oldData]; -- clobbers oldData^ !! copyData.transform _ saveTransform; success _ GGSlice.SetTextFontAndTransform[sliceD.slice, copyData, ggData.router, currentEvent]; IF NOT success THEN RETURN; -- SetTextFont will have posted an error [newSelectList, ptr] _ GGUtility.AddSlice[sliceD.slice, newSelectList, ptr]; }; }; success: BOOL _ FALSE; alikeRope: Rope.ROPE; currentEvent: HistoryEvent; newSelectList, ptr: LIST OF Slice; [alikeRope, alikeData, success] _ GetSelectedFontInSlice[lastDesc, "CopyFont", GGSlice.GetFontDataRope, ggData.router]; IF NOT success THEN RETURN; currentEvent _ GGHistory.NewCurrent["Copy font", ggData]; [newSelectList, ptr] _ GGUtility.StartSliceList[]; <> GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; [] _ GGScene.WalkSelectedSlices[scene: ggData.scene, level: leaf, walkProc: DoCopyFont, selectClass: normal, classType: $Text]; 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]; -- push captured history event onto history list Feedback.PutF[ggData.router, oneLiner, $Feedback, "Copied font %g to %g text objects", [rope[alikeRope]], [integer[count-1]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; END; }; }; FontRopeProc: TYPE = PROC [slice: Slice] RETURNS [Rope.ROPE]; GetSelectedFontInSlice: PROC [sliceD: SliceDescriptor, opName: Rope.ROPE, fontRopeProc: FontRopeProc, router: MsgRouter] RETURNS [fontRope: Rope.ROPE, fontData: FontData, success: BOOL _ TRUE] = { aborted: BOOL _ FALSE; DoCheckFontValues: PROC [leafD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF fontRope=NIL THEN { fontRope _ fontRopeProc[leafD.slice]; -- first font rope fontData _ GGSlice.GetFontData[leafD.slice]; } ELSE done _ NOT Rope.Equal[fontRope, fontRopeProc[leafD.slice], FALSE]; }; IF GGParent.IsParent[sliceD.slice] THEN [] _ GGParent.WalkIncludedChildren[sliceD.slice, sliceD.parts, leaf, DoCheckFontValues, $Text] ELSE { IF GGSliceOps.GetType[sliceD.slice] = $Text THEN { fontRope _ fontRopeProc[sliceD.slice]; fontData _ GGSlice.GetFontData[sliceD.slice]; }; }; IF aborted THEN { success _ FALSE; Feedback.PutF[router, oneLiner, $Complaint, "%g failed: multiple fonts are selected", [rope[opName]] ]; } ELSE IF fontRope = NIL THEN { success _ FALSE; Feedback.PutF[router, oneLiner, $Complaint, "%g failed: no fonts are selected", [rope[opName]] ]; }; }; GetSelectedFont: PROC [ggData: GGData, opName: Rope.ROPE, fontRopeProc: FontRopeProc] RETURNS [fontRope: Rope.ROPE, fontData: FontData, success: BOOL _ TRUE] = { sliceD: SliceDescriptor _ NIL; aborted: BOOL _ FALSE; scene: Scene _ ggData.scene; DoCheckFontValues: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF fontRope=NIL THEN { fontRope _ fontRopeProc[sliceD.slice]; -- first font rope fontData _ GGSlice.GetFontData[sliceD.slice]; } ELSE done _ NOT Rope.Equal[fontRope, fontRopeProc[sliceD.slice], FALSE]; }; aborted _ GGScene.WalkSelectedSlices[scene, leaf, DoCheckFontValues, normal, $Text]; IF aborted THEN { success _ FALSE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: multiple fonts are selected", [rope[opName]] ]; } ELSE IF fontRope = NIL THEN { success _ FALSE; Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: no fonts are selected", [rope[opName]] ]; }; }; CopyAll: UserInputProc = { <> scene: Scene _ ggData.scene; count: INT _ GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Text]; lastDesc: SliceDescriptor _ GGSelect.GetLastSelection[scene]; IF count<2 OR lastDesc=NIL THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "CopyFont failed: select at least one destination and one source text object for copy font"] ELSE { BEGIN DoCopyAll: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { oldTVec: ImagerTransformation.VEC; oldData, copyData: FontData; oldData _ GGSlice.GetFontData[sliceD.slice]; oldTVec _ ImagerTransformation.Factor[oldData.transform].t; copyData _ GGFont.CopyFontData[data: alikeData, oldCopy: oldData]; -- clobbers oldData^ !! copyData.transform _ ImagerTransformation.TranslateTo[copyData.transform, oldTVec]; success _ GGSlice.SetTextFontAndTransform[sliceD.slice, copyData, ggData.router, currentEvent]; IF NOT success THEN RETURN; -- SetTextFont will have posted an error [newSelectList, ptr] _ GGUtility.AddSlice[sliceD.slice, newSelectList, ptr]; }; success: BOOL _ FALSE; alikeRope: Rope.ROPE; alikeData: FontData; currentEvent: HistoryEvent; newSelectList, ptr: LIST OF Slice; [alikeRope, alikeData, success] _ GetSelectedFontInSlice[lastDesc, "CopyFontAndTransform", GGSlice.GetFontDataRope, ggData.router]; IF NOT success THEN RETURN; currentEvent _ GGHistory.NewCurrent["Copy font", ggData]; [newSelectList, ptr] _ GGUtility.StartSliceList[]; <> GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; [] _ GGScene.WalkSelectedSlices[scene, leaf, DoCopyAll, normal, $Text]; FOR list: LIST OF Slice _ newSelectList, list.rest UNTIL list = NIL DO GGSelect.SelectEntireSlice[list.first, scene, normal]; ENDLOOP; <> GGRefresh.EnlargeStartBox[ggData, GGScene.BoundBoxOfSelected[ggData.scene, normal], NIL]; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.PutF[ggData.router, oneLiner, $Feedback, "Copied font and transformation %g to %g text objects", [rope[alikeRope]], [integer[count-1]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; END; }; }; MatchAll: UserInputProc = { <> count: INT _ GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Text]; IF count<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "MatchFont failed: select at least one text object to match font"] ELSE { CheckMatchAll: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF fontData=NIL THEN fontData _ GGSlice.GetFontData[sliceD.slice]; RETURN[NOT IsMatching[fontData, GGSlice.GetFontData[sliceD.slice]]]; -- abort if multiple values }; maxPixels: REAL = 10000.0; fontData: FontData; aborted: BOOL _ GGScene.WalkSelectedSlices[ggData.scene, leaf, CheckMatchAll, normal, $Text]; IF aborted THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "MatchFont failed: multiple fonts selected"] ELSE { CheckMatch: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { IF IsMatching[fontData, GGSlice.GetFontData[slice]] THEN GGEvent.SelectEntireSlice[slice, ggData.scene, normal, ggData]; }; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE]; GGSelect.DeselectAll[ggData.scene, normal]; [] _ GGScene.WalkSlices[scene: ggData.scene, level: leaf, walkProc: CheckMatch, classType: $Text]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "MatchFont: matched %g", [rope[fontData.literal]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; }; }; }; IsMatching: PROC [aData, bData: FontData] RETURNS [BOOL] = { maxPixels: REAL = 10000.0; RETURN[Rope.Equal[aData.literal, bData.literal, FALSE] AND RealFns.AlmostEqual[aData.storedSize, bData.storedSize, -9] AND ImagerTransformation.CloseToTranslation[aData.transform, bData.transform, maxPixels] ]; }; MatchSelectedName: UserInputProc = { <> matchRope: Rope.ROPE _ NARROW[event.rest.first]; IF Rope.Equal[matchRope, NIL] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "MatchFontName failed: select a substring for font matching"] ELSE SelectMatching[matchRope, ggData, $userFSF]; }; MatchSelectedNameLiteral: UserInputProc = { <> matchRope: Rope.ROPE _ NARROW[event.rest.first]; IF Rope.Equal[matchRope, NIL] THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "MatchFontName failed: select a substring for font literal matching"] ELSE SelectMatching[matchRope, ggData, $literal]; }; SelectMatching: PROC [matchRope: Rope.ROPE, ggData: GGData, op: ATOM] = { DoSelectMatching: PROC [slice: Slice] RETURNS [done: BOOL _ FALSE] = { nextData: FontData _ GGSlice.GetFontData[slice]; IF Rope.Find[s1: IF op=$userFSF THEN nextData.userFSF ELSE nextData.literal, s2: matchRope, pos1: 0, case: FALSE]#-1 THEN GGEvent.SelectEntireSlice[slice, ggData.scene, normal, ggData]; }; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedCPs: TRUE]; GGSelect.DeselectAll[ggData.scene, normal]; [] _ GGScene.WalkSlices[ggData.scene, leaf, DoSelectMatching, $Text]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "MatchFontName: text slices with fonts matching %g selected", [rope[matchRope]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $SelectionChanged, ggData: ggData, remake: none, edited: FALSE, okToSkipCapture: FALSE]; }; SetDefaultFontValues: UserInputProc = { <> count: INT _ GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $Text]; IF count<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "MakeDefaultFont failed: select at least one text object to specify default font"] ELSE { CheckTextFonts: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF fontData=NIL THEN fontData _ GGSlice.GetFontData[sliceD.slice]; RETURN[NOT IsMatching[fontData, GGSlice.GetFontData[sliceD.slice]]]; -- abort if multiple values }; fontData: FontData; aborted: BOOL _ GGScene.WalkSelectedSlices[ggData.scene, leaf, CheckTextFonts, normal, $Text]; IF aborted THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "MakeDefaultFont failed: multiple fonts selected"] ELSE { GGState.SetDefaultFont[ggData, GGFont.CopyFontData[fontData]]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "MakeDefaultFont: default text font set to %g", [rope[fontData.literal]] ]; }; }; }; SetDefaultFont: UserInputProc = { errorRope: Rope.ROPE; BEGIN fontRope: Rope.ROPE _ NARROW[event.rest.first]; fontStream: IO.STREAM _ IO.RIS[fontRope]; fontData: FontData _ GGFont.ParseFontData[inStream: fontStream, literalP: TRUE, transformP: TRUE, storedSizeP: TRUE, designSizeP: TRUE ! GGFont.ParseError => {errorRope _ explanation; GOTO ParseError;};]; GGState.SetDefaultFont[ggData, fontData]; ShowDefaultFontValues[ggData, event]; EXITS ParseError => Feedback.Append[ggData.router, oneLiner, $Complaint, Rope.Concat["SetDefaultFont failed: ", errorRope]]; END; }; ShowDefaultFontValues: PUBLIC UserInputProc = { scratch: IO.STREAM _ IO.ROS[]; defaultFontData: FontData _ GGState.GetDefaultFont[ggData]; GGParseOut.WriteFactoredTransformationVEC[scratch, defaultFontData.transform]; Feedback.PutF[ggData.router, oneLiner, $Show, "Default font values: %g %g %g %g", [rope[defaultFontData.literal]], [rope[IO.RopeFromROS[scratch]]], [real[defaultFontData.storedSize]], [real[defaultFontData.designSize]] ]; }; <> ReadIP: PROC [ipName: Rope.ROPE, ggData: GGData, opName: Rope.ROPE, op: ATOM] RETURNS [scene: Scene _ NIL] = { <> ShowWarnings: Interpress.LogProc = { Feedback.PutF[ggData.router, oneLiner, $Warning, "IPImager warning: %g", [rope[explanation]] ]; }; ReadMaster: PROC [context: Imager.Context] = { Imager.ScaleT[context, 2834.646]; -- pointsPerMeter=2834.646 <> Imager.SetColor[context, Imager.black]; Imager.SetAmplifySpace[context, 1.0]; Imager.SetStrokeWidth[context, 0.0]; Imager.SetStrokeEnd[context, square]; Imager.SetStrokeJoint[context, miter]; Interpress.DoPage[master: ipmaster, page: 1, context: context, log: ShowWarnings]; }; ipmaster: Interpress.Master _ NIL; fullName: Rope.ROPE _ ""; startTime, endTime: BasicTime.GMT; totalTime: INT; success: BOOL _ FALSE; camera: Camera _ ggData.camera; router: MsgRouter _ ggData.router; currentWDir: Rope.ROPE _ ggData.currentWDir; BEGIN SELECT op FROM $MergeFromFile => { [fullName, success] _ GGFileOps.GetInterpressFileName["MergeFromFile", ipName, currentWDir, router]; IF NOT success THEN RETURN; [ipmaster, success] _ GGFileOps.OpenInterpressOrComplain["MergeFromFile", router, fullName]; IF NOT success THEN RETURN; }; $MergeFromTioga => { ipProp: Rope.ROPE; selectedLoc: TiogaOpsDefs.Location; selectedNode: TiogaOpsDefs.Ref; [----, selectedLoc] _ TiogaOps.GetSelection[primary]; selectedNode _ selectedLoc.node; ipProp _ NARROW[TiogaOps.GetProp[selectedNode, $Interpress]]; IF Rope.Equal[ipProp, NIL] THEN { Feedback.Append[router, oneLiner, $Complaint, "MergeFromTioga failed: no Interpress property on selected node"]; GOTO Fail; }; ipmaster _ Interpress.FromRope[ipProp, ShowWarnings ! IPMaster.Error => { Feedback.PutF[router, oneLiner, $Complaint, "MergeFromTioga failed: %g", [rope[error.explanation]] ]; GOTO Fail; };]; }; ENDCASE => { Feedback.Append[router, oneLiner, $Complaint, "IP merge: unknown IPMerge operation"]; RETURN; }; Feedback.PutF[router, begin, $Statistics, "%g: %g . . . ", [rope[opName]], [rope[fullName]]]; startTime _ BasicTime.Now[]; scene _ GGFromImager.Capture[action: ReadMaster, camera: camera ! GGFromImager.WarningMessage => { Feedback.PutF[ggData.router, oneLiner, $Warning, "GGFromImager warning: %g", [rope[message]] ]; RESUME; }; ]; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; Feedback.PutF[ggData.router, end, $Statistics, " Done in time (%r)", [integer[totalTime]]]; EXITS Fail => NULL; END; }; IPMergeFromTioga: UserInputProc = { scene: Scene _ ReadIP[NIL, ggData, "MergeFromTioga", $MergeFromTioga]; IF scene = NIL THEN RETURN; GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SelectAll[scene, normal]; ggData.scene _ GGScene.MergeScenes[back: ggData.scene, front: scene]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; MergeIPEditable: PUBLIC UserInputProc = { ipName: Rope.ROPE _ NARROW[event.rest.first]; scene: Scene _ ReadIP[ipName, ggData, "MergeIPEditable", $MergeFromFile]; IF scene = NIL THEN RETURN; GGSelect.DeselectAll[ggData.scene, normal]; GGSelect.SelectAll[scene, normal]; ggData.scene _ GGScene.MergeScenes[back: ggData.scene, front: scene]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; }; MergeIPSlice: PUBLIC UserInputProc = { slice: Slice; localBox: BoundBox _ NIL; argStream: IO.STREAM; h, w: REAL _ 0.0; pointsPerInch: REAL _ 72.0; shortName, fullName: Rope.ROPE; startTime, endTime: BasicTime.GMT; totalTime: INT; ipMaster: Interpress.Master; success: BOOL _ FALSE; includeByValue: BOOL _ GGUserProfile.GetDefaultIncludeIPByValue[]; argStream _ IO.RIS[NARROW[event.rest.first]]; -- file name plus optional two reals shortName _ IO.GetTokenRope[argStream, IO.IDProc! IO.EndOfStream, IO.Error => CONTINUE;].token; -- file name w _ IO.GetReal[argStream ! IO.EndOfStream, IO.Error => CONTINUE;]; -- first real h _ IO.GetReal[argStream ! IO.EndOfStream, IO.Error => CONTINUE;]; -- second real [fullName, success] _ GGFileOps.GetInterpressFileName["MergeIPSlice", shortName, ggData.currentWDir, ggData.router]; IF NOT success THEN RETURN; [ipMaster, success] _ GGFileOps.OpenInterpressOrComplain["MergeIPSlice", ggData.router, fullName]; IF NOT success THEN RETURN; GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE]; GGSelect.DeselectAll[ggData.scene, normal]; Feedback.PutF[ggData.router, begin, $Statistics, "MergeIPSlice: %g . . . ", [rope[fullName]]]; startTime _ BasicTime.Now[]; IF w#0.0 AND h#0.0 THEN localBox _ GGBoundBox.CreateBoundBox[0.0, 0.0, w*pointsPerInch, h*pointsPerInch]; -- this can result in refresh problems slice _ GGSlice.MakeIPSliceFromMaster[ipMaster, 2834.646, fullName, ggData.router, NIL, localBox, localBox, includeByValue]; IF slice = NIL THEN RETURN; GGScene.AddSlice[ggData.scene, slice, -1]; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; GGEvent.SelectEntireSlice[slice, ggData.scene, normal, ggData]; <> GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[slice], NIL]; Feedback.PutF[ggData.router, end, $Statistics, " Done in time (%r)", [integer[totalTime]]]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: sceneBag, edited: TRUE, okToSkipCapture: FALSE]; }; -- end MergeIPSlice IncludeIPByReference: UserInputProc = { <> IncludeIPBy[ggData, "IncludeIPByReference", FALSE]; }; IncludeIPByValue: UserInputProc = { <> IncludeIPBy[ggData, "IncludeIPByValue", TRUE]; }; IncludeIPBy: PROC [ggData: GGData, opRope: Rope.ROPE, includeBy: BOOL] = { count: INT _ GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $IP]; IF count<1 THEN Feedback.PutF[ggData.router, oneLiner, $Complaint, "%g failed: select at least one IP object to specify include mode", [rope[opRope]] ] ELSE { DoIncludeBy: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { GGSlice.SetIncludeByValue[sliceD.slice, includeBy, currentEvent]; }; currentEvent: HistoryEvent _ GGHistory.NewCurrent[opRope, ggData]; [] _ GGScene.WalkSelectedSlices[ggData.scene, leaf, DoIncludeBy, normal, $IP]; GGHistory.PushCurrent[ggData]; Feedback.PutF[ggData.router, oneLiner, $Feedback, "%g: selected IP slices included", [rope[opRope]] ]; GGWindow.RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: none, edited: TRUE, okToSkipCapture: FALSE]; }; }; ShowIPIncludeMode: UserInputProc = { <> count: INT _ GGScene.CountSelectedSlices[ggData.scene, leaf, normal, $IP]; IF count<1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowIPIncludeMode failed: select at least one IP object to show include mode"] ELSE { CheckIncludeMode: PROC [sliceD: SliceDescriptor] RETURNS [done: BOOL _ FALSE] = { IF someD=NIL THEN includeByValue _ GGSlice.GetIncludeByValue[sliceD.slice]; -- first selected someD _ sliceD; RETURN[includeByValue#GGSlice.GetIncludeByValue[sliceD.slice]]; -- abort if multiple values }; someD: SliceDescriptor; includeByValue: BOOL _ FALSE; aborted: BOOL _ GGScene.WalkSelectedSlices[ggData.scene, leaf, CheckIncludeMode, normal, $IP]; IF aborted THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "ShowIPIncludeMode failed: multiple include modes selected"] ELSE Feedback.PutF[ggData.router, oneLiner, $Show, "ShowIPIncludeMode: include by %g", IF includeByValue THEN [rope["value"]] ELSE [rope["reference"]]]; }; }; ToIPAux: PROC [ggData: GGData, ipName: Rope.ROPE, actionAtom: ATOM, makeInterpress: PROC [dc: Imager.Context]] = { ipRef: ImagerInterpress.Ref; fullName: Rope.ROPE; success: BOOL _ FALSE; <> startTime: BasicTime.GMT; endTime: BasicTime.GMT; totalTime: INT; msgRope: Rope.ROPE; IF NOT PBasics.IsBound[LOOPHOLE[ImagerInterpress.Create]] THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "ToIP failed: please install Interpress, then retry this operation"]; RETURN; }; IF Rope.Equal[ipName, NIL] THEN IF GGState.GetFullName[ggData]=NIL THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "ToIP failed: can't default IP file name from unnamed viewer"]; RETURN; } ELSE ipName _ GGFileOps.FilenameMinusExtension[FileNames.GetShortName[GGState.GetFullName[ggData]]] ELSE ipName _ FileNames.StripVersionNumber[ipName]; -- forbid version numbers here [fullName, success] _ GGFileOps.GetInterpressFileName["ToIP", ipName, ggData.currentWDir, ggData.router]; IF NOT success THEN RETURN; ipRef _ ImagerInterpress.Create[fullName ! PFS.Error => { Feedback.PutF[ggData.router, oneLiner, $Complaint, "ToIP failed: PFS Error %g: %g while attempting to create %g", [atom[error.code]], [rope[error.explanation]], [rope[fullName]] ]; success _ FALSE; CONTINUE; }; Imager.Error => { Feedback.PutF[ggData.router, oneLiner, $Complaint, "ToIP failed: PFS Error %g: %g while attempting to create %g", [atom[error.code]], [rope[error.explanation]], [rope[fullName]] ]; success _ FALSE; CONTINUE; }; ]; IF NOT success THEN RETURN; msgRope _ IO.PutFR["%g %g . . . ", [atom[actionAtom]], [rope[fullName]]]; Feedback.PutF[ggData.router, begin, $Statistics, msgRope]; <> startTime _ BasicTime.Now[]; ImagerInterpress.DoPage[ipRef, makeInterpress, 1.0]; ImagerInterpress.Close[ipRef]; endTime _ BasicTime.Now[]; totalTime _ BasicTime.Period[startTime, endTime]; msgRope _ IO.PutFR[" Done in time (%r)", [integer[totalTime]]]; Feedback.PutF[ggData.router, end, $Statistics, msgRope]; GGEvent.SawTextFinish[ggData, NIL]; }; DoMakeAux: PROC [context: Imager.Context, q: GGInterfaceTypes.QualityMode, s: GGInterfaceTypes.DisplayStyle, ggData: GGData] = { tempQuality: GGInterfaceTypes.QualityMode _ ggData.camera.quality; tempStyle: GGInterfaceTypes.DisplayStyle _ ggData.camera.displayStyle; ggData.camera.quality _ q; ggData.camera.displayStyle _ s; Imager.ScaleT[context, metersPerPixel]; GGRefresh.InterpressEntireScene[context, ggData]; ggData.camera.quality _ tempQuality; ggData.camera.displayStyle _ tempStyle; }; ToIP: PUBLIC UserInputProc = { DoMakeInterpress: PROC [dc: Imager.Context] = { DoMakeAux[dc, quality, print, ggData]; -- KAP. }; ipName: Rope.ROPE _ NARROW[event.rest.first]; ToIPAux[ggData, ipName, NARROW[event.first], DoMakeInterpress]; }; ToIPLit: PUBLIC UserInputProc = { DoMakeInterpress: PROC [dc: Imager.Context] = { DoMakeAux[dc, showall, print, ggData]; -- KAP. }; ipName: Rope.ROPE _ "litshot.ip"; ToIPAux[ggData, ipName, NARROW[event.first], DoMakeInterpress]; }; ToIPScreen: PUBLIC UserInputProc = { DoMakeInterpress: PROC [dc: Imager.Context] = { DoMakeAux[dc, quality, screen, ggData]; -- KAP. }; ipName: Rope.ROPE _ NARROW[event.rest.first]; ToIPAux[ggData, ipName, NARROW[event.first], DoMakeInterpress]; }; ToIPSelected: PUBLIC UserInputProc = { DoMakeInterpress: PROC [dc: Imager.Context] = { tempQuality: GGInterfaceTypes.QualityMode; tempStyle: GGInterfaceTypes.DisplayStyle; tempQuality _ ggData.camera.quality; ggData.camera.quality _ quality; tempStyle _ ggData.camera.displayStyle; ggData.camera.displayStyle _ print; Imager.ScaleT[dc, metersPerPixel]; FOR slice: LIST OF Slice _ GGUtility.OrderedSelectionList[ggData, decr], slice.rest UNTIL slice=NIL DO GGSliceOps.DrawParts[slice.first, NIL, dc, ggData.camera, FALSE]; ENDLOOP; ggData.camera.quality _ tempQuality; ggData.camera.displayStyle _ tempStyle; }; ipName: Rope.ROPE _ NARROW[event.rest.first]; ToIPAux[ggData, ipName, NARROW[event.first], DoMakeInterpress]; }; <> <> <> <> <> <> <> <> <> <> <> <> <<};>> <> <> <<};>> <<>> <<>> <> TestGravity2: PUBLIC PROC [dc: Imager.Context, ggData: GGData] = { <> xRandomStream, yRandomStream: Random.RandomStream; testPoint: Point; x, y: INT; totalCount, multiHitCount, uniHitCount, diffCount: NAT _ 0; --uniPoint,-- multiPoint: Point; normal: Vector; --uniFeature,-- multiFeature: FeatureData; currentObjects: AlignBag; sceneObjects: TriggerBag; xRandomStream _ Random.Create[GGState.GetWidth[ggData]]; yRandomStream _ Random.Create[GGState.GetHeight[ggData]]; GGAlign.SetStaticBags[ggData]; ggData.aborted[gravitytest] _ FALSE; -- in case there was one left over from prior abort UNTIL totalCount > 1000 DO IF ggData.aborted[gravitytest] THEN { ggData.aborted[gravitytest] _ FALSE; EXIT; }; x _ Random.NextInt[xRandomStream]; y _ Random.NextInt[yRandomStream]; testPoint _ [x, y]; testPoint _ GGWindow.ViewerToWorld[viewerPoint: testPoint, ggData: ggData]; ggData.refresh.spotPoint _ testPoint; currentObjects _ ggData.hitTest.alignBag; sceneObjects _ ggData.hitTest.sceneBag; [multiPoint, normal, multiFeature] _ GGMultiGravity.Map[testPoint, ggData.hitTest.t, currentObjects, sceneObjects, ggData, TRUE]; IF multiFeature = NIL THEN { <> Imager.SetColor[dc, Imager.black]; GGShapes.DrawSpot[dc, ggData.refresh.spotPoint]; totalCount _ totalCount + 1; LOOP; }; multiHitCount _ multiHitCount + 1; totalCount _ totalCount + 1; ggData.refresh.hitPoint _ multiPoint; <> Imager.SetColor[dc, Imager.black]; Imager.SetStrokeEnd[dc, round]; Imager.MaskVector[dc, [ggData.refresh.spotPoint.x, ggData.refresh.spotPoint.y], [ggData.refresh.hitPoint.x, ggData.refresh.hitPoint.y]]; GGShapes.DrawFilledLoLeftSquare[dc, ggData.refresh.spotPoint, 3.0]; ENDLOOP; Feedback.PutF[ggData.router, oneLiner, $Show, "Tested %g total points. %g unihits. %g multihits. %g differences", [integer[totalCount]], [integer[uniHitCount]], [integer[multiHitCount]], [integer[diffCount]]]; }; -- end TestGravity2 Delete: UserInputProc = { aBox: BoundBox; CodeTimer.StartInt[$Delete, $Gargoyle]; IF GGSelect.NoSelections[ggData.scene, normal] THEN { Feedback.Append[ggData.router, oneLiner, $Complaint, "Delete failed: there is no selection to delete"]; RETURN; -- nothing selected }; GGHistory.NewCapture["Delete objects", ggData]; -- capture scene BEFORE UPDATE <> GGRefresh.SetStartBox[ggData: ggData, dragInProgress: FALSE, selectedParts: TRUE, selectedCPs: TRUE, hotCPs: TRUE, alignments: TRUE, attractor: TRUE]; -- really only need hotCPs of selected objects [] _ GGScene.DeleteAllSelected[ggData.scene]; GGCaret.NoAttractor[ggData.caret]; GGCaret.SitOn[caret: ggData.caret, chair: NIL]; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.PutF[ggData.router, oneLiner, $Feedback, "Delete: selected objects deleted"]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; CodeTimer.StopInt[$Delete, $Gargoyle]; }; DescribeCurve: UserInputProc = { <> SELECT event.rest.first FROM $Caret => GGEvent.DescribeCaretObject[ggData, event]; -- for now $Selected => { IF GGScene.CountSelectedSlices[ggData.scene, first, normal]#1 THEN Feedback.Append[ggData.router, oneLiner, $Complaint, "DescribeCurve failed: select a single object for description"] ELSE Feedback.Append[ggData.router, oneLiner, $Show, GGSliceOps.Describe[GGScene.FirstSelectedSlice[ggData.scene, all, normal]]]; }; ENDCASE => ERROR; }; CloseOrNewline: UserInputProc = { IF ggData.refresh.textInProgress=NIL THEN Close[ggData, event] ELSE { myRefChar: REF CHAR _ NEW[CHAR _ Ascii.CR ]; event _ LIST[$AddChar, myRefChar]; GGEvent.AddChar[ggData, event]; }; }; Close: UserInputProc = { outlineSlice: Slice; traj: Traj; seg: Segment; firstPoint, lastPoint: Point; success: BOOL _ FALSE; GGHistory.NewCapture["Close object", ggData]; -- capture scene BEFORE UPDATE BEGIN trajDescriptor: SliceDescriptor; trajData: TrajData; chairD: SliceDescriptor _ GGCaret.GetChair[ggData.caret]; IF NOT GGSliceOps.IsDescriptorOfEnd[chairD] THEN GOTO NoCaretTraj; [success, trajDescriptor, ----] _ GGSliceOps.UnpackJoint[chairD]; IF NOT success THEN GOTO NoCaretTraj; traj _ trajDescriptor.slice; trajData _ NARROW[traj.data]; IF trajData.role = fence OR trajData.role = hole THEN GOTO AlreadyClosed; outlineSlice _ GGParent.GetParent[traj]; firstPoint _ GGTraj.FetchJointPos[traj, 0]; lastPoint _ GGTraj.LastJointPos[traj]; IF firstPoint # lastPoint THEN { lastSeg: Segment _ GGTraj.FetchSegment[traj, GGTraj.HiSegment[traj]]; firstSeg: Segment _ GGTraj.FetchSegment[traj, 0]; cPoint1, cPoint2, oldCP: Point; addBezier: BOOL _ FALSE; IF lastSeg.class.type = $Bezier THEN { addBezier _ TRUE; oldCP _ lastSeg.class.controlPointGet[lastSeg, 1]; cPoint2 _ Vectors2d.Add[lastSeg.hi, Vectors2d.VectorFromPoints[oldCP, lastSeg.hi]]; } ELSE cPoint2 _ lastPoint; IF firstSeg.class.type = $Bezier THEN { addBezier _ TRUE; oldCP _ firstSeg.class.controlPointGet[firstSeg, 0]; cPoint1 _ Vectors2d.Add[firstSeg.lo, Vectors2d.VectorFromPoints[oldCP, firstSeg.lo]] } ELSE cPoint1 _ firstPoint; IF addBezier THEN seg _ GGSegment.MakeBezier[lastPoint, cPoint2, cPoint1, firstPoint, NIL] ELSE seg _ GGSegment.MakeLine[lastPoint, firstPoint, NIL]; GGSegment.CopyLooks[lastSeg, seg]; GGTraj.CloseWithSegment[traj, seg, lo]; [] _ GGSelect.ReselectTraj[traj, hi, ggData.scene, TRUE]; } ELSE { GGOutline.SaveSelectionsInOutlineAllClasses[outlineSlice]; GGSelect.DeselectEntityAllClasses[outlineSlice, ggData.scene]; GGTraj.CloseByDistorting[traj, hi]; GGSelect.ReselectSliceAllClasses[outlineSlice, ggData.scene]; }; GGEvent.SelectEntireSlice[traj, ggData.scene, normal, ggData]; GGCaret.SitOn[ggData.caret, NIL]; -- FIX THIS TO SIT CARET ON NEWLY CLOSED TRAJ GGCaret.NoAttractor[caret: ggData.caret]; <> GGRefresh.NullStartBox[ggData]; GGRefresh.EnlargeStartBox[ggData, GGSliceOps.GetBoundBox[traj], NIL]; GGHistory.PushCurrent[ggData]; -- push captured history event onto history list Feedback.PutF[ggData.router, oneLiner, $Feedback, "Close: trajectory closed."]; GGWindow.RestoreScreenAndInvariants[paintAction: $ObjectChangedBoundBoxProvided, ggData: ggData, remake: triggerBag, edited: TRUE, okToSkipCapture: FALSE]; <> EXITS NoCaretTraj => { Feedback.Append[ggData.router, oneLiner, $Complaint, "Close failed: there is no caret trajectory to close"]; }; AlreadyClosed => { Feedback.Append[ggData.router, oneLiner, $Complaint, "Close: that trajectory is already closed"]; }; END; }; -- end Close <> GravityChoiceChange: UserInputProc = { forward: BOOL _ event.rest.first = $FlipForward; GGState.CycleGravityType[ggData, forward]; }; SetGravityChoice: UserInputProc = { choiceRope: Rope.ROPE _ NARROW[event.rest.first]; gravityType: GravityType _ GGUIUtility.GravityTypeFromRope[choiceRope]; GGState.SetGravityType[ggData, gravityType]; }; GravityExtentChange: UserInputProc = { extent: REAL _ GGState.GetGravityExtent[ggData]; --RETURNS[inches]-- success: BOOL _ TRUE; SELECT event.rest.first FROM $ValueUp => { IF extent < 256.0 THEN extent _ extent*2.0 ELSE { Feedback.PutF[ggData.router, oneLiner, $Complaint, "ExtendGravity failed: can't extend gravity further than 256 inches."]; success _ FALSE; }; }; $ValueDown => extent _ extent/2.0; ENDCASE => extent _ GGUserProfile.GetDefaultGravityExtent[--RETURNS[points]--]/pointsPerIn; IF success THEN { GGState.SetGravityExtent[ggData, extent]; ShowGravityExtent[ggData, event]; }; }; SetGravityExtent: UserInputProc = { inches: REAL _ NARROW[event.rest.first, REF REAL]^; GGState.SetGravityExtent[ggData, inches]; }; ShowGravityExtent: UserInputProc = { inches: REAL _ GGState.GetGravityExtent[ggData]; Feedback.PutF[ggData.router, oneLiner, $Show, "Gravity extent is %g points = %g inches = %g centimeters", [real[inches*pointsPerIn]], [real[inches]], [real[inches*cmPerInch]] ]; }; ToggleGravity: UserInputProc = { GGState.SetGravity[ggData, NOT GGState.GetGravity[ggData]]; }; SetGravity: UserInputProc = { boolRope: Rope.ROPE _ NARROW[event.rest.first]; setGravity: BOOL _ GGCoreOps.RopeToBool[boolRope]; GGState.SetGravity[ggData, setGravity]; }; RegisterEventProcs: PROC = { OPEN GGUserInput; <> RegisterAction[$GravityChoiceChange, GravityChoiceChange, none, FALSE]; RegisterAction[$SetGravityChoice, SetGravityChoice, none, FALSE]; RegisterAction[$GravityExtentChange, GravityExtentChange, none, FALSE]; RegisterAction[$ToggleGravity, ToggleGravity, none, FALSE]; RegisterAction[$SetGravityExtent, SetGravityExtent, none, FALSE]; RegisterAction[$SetGravity, SetGravity, none, FALSE]; RegisterAction[$ShowGravExtent, ShowGravityExtent, none, FALSE]; <> RegisterAction[$AddChar, AddChar, none]; RegisterAction[$AddText, AddText, rope]; RegisterAction[$SetAmplifySpace, SetAmplifySpace, refReal]; RegisterAction[$AmplifySpaceFromSelection, AmplifySpaceFromSelection, refReal]; RegisterAction[$PrintAmplifySpace, PrintAmplifySpace, none]; RegisterAction[$SetDropShadow, SetDropShadow, none]; -- actually needs two REALS RegisterAction[$DropShadowTenPercent, DropShadowTenPercent, none]; RegisterAction[$DropShadowFromSelection, DropShadowFromSelection, refReal]; RegisterAction[$PrintDropShadow, PrintDropShadow, none]; RegisterAction[$DropShadowOff, DropShadowOff, none]; RegisterAction[$SetDefaultTextLooks, SetDefaultTextLooks, none]; RegisterAction[$ShowDefaultTextLooks, ShowDefaultTextLooks, none]; <<>> <> RegisterAction[$SetPressFont, SetPressFont, rope]; RegisterAction[$SetPrintFont, SetPrintFont, rope]; RegisterAction[$SetScreenFont, SetScreenFont, rope]; RegisterAction[$SetFontDetailed, SetFontDetailed, rope]; RegisterAction[$SetFontLiteral, SetFontLiteral, rope]; RegisterAction[$ShowFontValues, ShowFontValues, none]; RegisterAction[$ShowFontValuesLiteral, ShowFontValuesLiteral, none]; RegisterAction[$CopyFont, CopyFont, none]; RegisterAction[$CopyAll, CopyAll, none]; RegisterAction[$MatchAll, MatchAll, none]; RegisterAction[$MatchSelectedName, MatchSelectedName, rope]; RegisterAction[$MatchSelectedNameLiteral, MatchSelectedNameLiteral, rope]; RegisterAction[$SetDefaultFontValues, SetDefaultFontValues, none]; RegisterAction[$ShowDefaultFontValues, ShowDefaultFontValues, none]; RegisterAction[$SetDefaultFont, SetDefaultFont, none, FALSE]; <> RegisterAction[$IPMergeFromTioga, IPMergeFromTioga, none]; RegisterAction[$MergeIPEditable, MergeIPEditable, rope]; RegisterAction[$MergeIPSlice, MergeIPSlice, rope]; RegisterAction[$ToIP, ToIP, rope]; RegisterAction[$ToIPSelected, ToIPSelected, rope]; RegisterAction[$ToIPOnPlayback, ToIP, rope]; RegisterAction[$ToIPScreen, ToIPScreen, rope]; RegisterAction[$ToIPLit, ToIPLit, none]; <> RegisterAction[$IncludeIPByReference, IncludeIPByReference, none]; RegisterAction[$IncludeIPByValue, IncludeIPByValue, none]; RegisterAction[$ShowIPIncludeMode, ShowIPIncludeMode, none]; <<>> <> RegisterAction[$Delete, Delete, none]; <> <> <> RegisterAction[$DescribeCurve, DescribeCurve, none]; RegisterAction[$AddHoles, AddHoles, none]; RegisterAction[$DeleteHoles, DeleteHoles, none]; <<>> RegisterAction[$CloseOrNewline, CloseOrNewline, none]; RegisterAction[$Close, Close, none]; }; RegisterEventProcs[]; END.