<> <> <> <> DIRECTORY AtomButtons, AtomButtonsTypes, Basics, BiScrollers, BufferedRefresh, Buttons, Commander, Containers, Feedback, FunctionCache, Geom2D, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGContainer, GGFont, GGGravity, GGInterfaceTypes, GGMenu, GGModelTypes, GGMouseEvent, GGMultiGravity, GGOutline, GGRefresh, GGScene, GGSegmentTypes, GGSelect, GGSequence, GGShapes, GGSlice, GGTraj, GGUserInput, GGUtility, GGWindow, GraphicsButton, Icons, Imager, ImagerTransformation, InputFocus, IO, Labels, Match, MatchGrep, MatchViewer, Rope, SlackProcess, TIPUser, ViewerClasses, ViewerOps; MatchViewerImpl: CEDAR PROGRAM IMPORTS AtomButtons, Basics, BiScrollers, BufferedRefresh, Buttons, Commander, Containers, Feedback, FunctionCache, Geom2D, GGAlign, GGBoundBox, GGCaret, GGFont, GGGravity, GGMenu, GGMouseEvent, GGMultiGravity, GGOutline, GGRefresh, GGScene, GGSelect, GGSequence, GGSlice, GGShapes, GGTraj, GGUserInput, GGUtility, GGWindow, GraphicsButton, Icons, Imager, ImagerTransformation, InputFocus, IO, Labels, Match, MatchGrep, Rope, TIPUser, ViewerOps EXPORTS MatchViewer = BEGIN CameraObj: TYPE = GGModelTypes.CameraObj; Caret: TYPE = REF CaretObj; CaretObj: TYPE = GGInterfaceTypes.CaretObj; ChoiceDataObj: TYPE = MatchViewer.ChoiceDataObj; FeedbackData: TYPE = Feedback.FeedbackData; FiltersObj: TYPE = GGInterfaceTypes.FiltersObj; GGDataObj: TYPE = GGInterfaceTypes.GGDataObj; GGData: TYPE = GGInterfaceTypes.GGData; MatchData: TYPE = MatchViewer.MatchData; MatchDataObj: TYPE = MatchViewer.MatchDataObj; Point: TYPE = GGBasicTypes.Point; Sequence: TYPE = GGModelTypes.Sequence; Scene: TYPE = GGModelTypes.Scene; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; Traj: TYPE = GGModelTypes.Traj; Viewer: TYPE = ViewerClasses.Viewer; actionAreaHeight: CARDINAL = 200; -- height of action area biscroller viewer actionAreaMargin: CARDINAL = 3; -- distance between two action areas actionAreaStartY: CARDINAL = entryHeight; buttonAlign: CARDINAL = 2; -- vertical alignment differential for the 2 kinds of buttons used entryHeight: CARDINAL = 15; -- height of a line of items entryHSpace: CARDINAL = 2; -- space between ButtonLines on same line entryVSpace: CARDINAL = 2; -- vertical leading between lines InitViewers: PROC [] = { matchContainerClass: ViewerClasses.ViewerClass _ NEW[ViewerClasses.ViewerClassRec _ [ init: MatchContainerInit, adjust: MatchContainerAdjust, topDownCoordSys: TRUE, icon: matchToolIcon ]]; ViewerOps.RegisterViewerClass[$MatchContainer, matchContainerClass]; -- plug in to Viewers [] _ ViewerOps.CreateViewer [flavor: $MatchContainer, info: [name: "MatchTool", menu: NIL, iconic: FALSE, column: right, scrollable: FALSE ], paint: TRUE]; }; fromViewer, toViewer: Viewer _ NIL; fromData, toData, grepData: GGData _ NIL; fromLine, toLine: Viewer _ NIL; <> <<>> MatchContainerInit: ViewerClasses.InitProc = { fromData _ BuildDefaultGGData[self]; toData _ BuildDefaultGGData[self]; grepData _ BuildDefaultGGData[self]; -- this data is used to search scenes during grep CreateFeedbackLine[toData, fromData, grepData]; fromViewer _ BuildActionArea[self, fromData]; toViewer _ BuildActionArea[self, toData]; fromLine _ BuildActionAreaLine[fromData, 0]; toLine _ BuildActionAreaLine[toData, 0]; InitControlPanel[self, actionAreaStartY + actionAreaHeight + entryHeight + upperMargin]; }; GetFromData: PUBLIC PROC RETURNS [GGData] = { RETURN[fromData]; }; GetToData: PUBLIC PROC RETURNS [GGData] = { RETURN[toData]; }; GetGrepData: PUBLIC PROC RETURNS [GGData] = { RETURN[grepData]; }; BuildDefaultGGData: PROC [outer: Viewer] RETURNS [ggData: GGData] = { <> ggData _ NEW[GGDataObj _ matchData.hostData^]; ggData.biScroller _ NIL; outer.data _ ggData; ggData.outer _ outer; ggData.hitTest _ NEW[FiltersObj _ matchData.hostData.hitTest^]; ggData.hitTest.triggerBag _ GGAlign.CreateTriggerBag[]; ggData.hitTest.sceneBag _ GGAlign.CreateTriggerBag[]; ggData.hitTest.alignBag _ GGAlign.CreateAlignBag[]; ggData.refresh.lineCache _ FunctionCache.Create[maxEntries: 200]; ggData.scene _ GGScene.CreateScene[]; ggData.caret _ NEW[CaretObj]; ggData.anchor _ NEW[CaretObj]; ggData.camera _ NEW[CameraObj]; GGMouseEvent.InitializeFSM[ggData]; ggData.drag.savedCaret _ NEW[CaretObj]; ggData.drag.selectState _ none; ggData.drag.extendMode _ none; ggData.refresh.startBoundBox _ GGBoundBox.NullBoundBox[]; ggData.refresh.sandwich _ GGRefresh.CreateSandwich[]; ggData.defaults _ NEW[GGInterfaceTypes.DefaultDataObj _ [strokeColor: Imager.black, fillColor: GGOutline.fillColor, font: GGFont.DefaultDefaultFontData[] ] ]; ggData.height _ 0; ggData.outer.newVersion _ FALSE; ggData.gravityPool _ GGGravity.NewGravityPool[]; ggData.multiGravityPool _ GGMultiGravity.NewMultiGravityPool[]; <<[] _ SlackProcess.EnableAborts[handle: ggData.slackHandle];>> }; CreateFeedbackLine: PROC [data1, data2, grepData: GGData] = { <> GGMenu.BuildFeedbackLine[data1]; grepData.feedback _ data2.feedback _ data1.feedback; data2.height _ data2.height + entryHeight; -- data1 was incremented by BuildFeedbackLine }; MatchContainerAdjust: ViewerClasses.AdjustProc = { margin1: CARD _ (self.ww - actionAreaMargin) / 2; margin2: CARD _ (self.ww + actionAreaMargin) / 2; IF fromViewer # NIL THEN ViewerOps.MoveViewer[fromViewer, 0, fromViewer.wy, margin1, fromViewer.wh, FALSE]; IF toViewer # NIL THEN ViewerOps.MoveViewer[toViewer, margin2, toViewer.wy, margin1, toViewer.wh, FALSE]; IF fromLine # NIL THEN ViewerOps.MoveViewer[fromLine, fromLine.wx, fromLine.wy, margin1, fromLine.wh, FALSE]; IF toLine # NIL THEN ViewerOps.MoveViewer[toLine, margin2, toLine.wy, margin1, toLine.wh, FALSE]; }; BuildActionArea: PRIVATE PROC [outer: Viewer, ggData: GGData] RETURNS [Viewer] = { <> actionAreaClass: BiScrollers.BiScrollerClass _ BiScrollers.GetStyle[].NewBiScrollerClass[[ flavor: $ActionArea, extrema: GGExtremaProc, notify: GGUserInput.InputNotify, paint: GGActionAreaPaint, tipTable: TIPUser.InstantiateNewTIPTable["Gargoyle.TIP"], mayStretch: FALSE, -- NOT OK to scale X and Y differently offsetsMustBeIntegers: TRUE, preferIntegerCoefficients: FALSE, <> preserve: [X: 0.5, Y: 0.5] --this specifies point that stays fixed when viewer size changes ]]; bs: BiScrollers.BiScroller _ BiScrollers.GetStyle[].CreateBiScroller[ class: actionAreaClass, info: [ parent: ggData.outer, wx: 0, wy: actionAreaStartY, ww: 700, --ggData.outer.ww, wh: actionAreaHeight, --ggData.outer.wh, <> data: ggData, border: TRUE, scrollable: FALSE], paint: FALSE ]; ggData.actionArea _ bs.QuaViewer[inner: TRUE]; ggData.biScroller _ bs; GGWindow.SetCursorLooks[ggData.hitTest.gravityType, ggData]; -- assumes gravity is turned ON ggData.height _ ggData.height + actionAreaHeight; RETURN[bs.QuaViewer[inner: FALSE]]; }; GGExtremaProc: PROC [clientData: REF ANY, direction: Geom2D.Vec] RETURNS [min, max: Geom2D.Vec] --BiScrollers.ExtremaProc-- = { <> area: Geom2D.Rect; ggData: GGData _ NARROW[clientData]; bigBox: GGBoundBox.BoundBox _ GGBoundBox.BoundBoxOfBoxes[GGScene.BoundBoxesInScene[ggData.scene].list]; area _ IF bigBox#NIL THEN [x: bigBox.loX, y: bigBox.loY, w: bigBox.hiX-bigBox.loX, h: bigBox.hiY-bigBox.loY] ELSE [0.0, 0.0, 1.0, 1.0]; [min, max] _ Geom2D.ExtremaOfRect[r: area, n: direction]; }; GGActionAreaPaint: PROC [self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] RETURNS [quit: BOOL _ FALSE] = { <> PaintEntireViewer: PROC = CHECKED { BufferedRefresh.FitSandwichToScreen[ggData.refresh.sandwich, self.cw, self.ch]; GGWindow.RestoreScreenAndInvariants[paintAction: $PaintEntireScene, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; clientToViewer: Imager.Transformation _ BiScrollers.GetStyle[].GetTransforms[BiScrollers.QuaBiScroller[self]].clientToViewer; ggData: GGData; IF whatChanged = NIL THEN { --we are being called by Window Manager <> ggData _ NARROW[BiScrollers.ClientDataOfViewer[self]]; BufferedRefresh.FitSandwichToScreen[ggData.refresh.sandwich, self.cw, self.ch]; IF ggData.refresh.clientToViewer#NIL AND ImagerTransformation.Equal[clientToViewer, ggData.refresh.clientToViewer] THEN { -- Window Motion GGWindow.RestoreScreenAndInvariants[paintAction: $ViewersPaintAllPlanes, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; } ELSE { -- Scrolling GGWindow.RestoreScreenAndInvariants[paintAction: $ViewersPaintEntireScene, ggData: ggData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; } ELSE { ggData _ NARROW[whatChanged]; IF ggData.refresh.paintAction=$PaintEntireViewer THEN PaintEntireViewer[] ELSE GGRefresh.ActionAreaPaint[context, ggData.refresh.paintAction, ggData]; }; ggData.refresh.clientToViewer _ clientToViewer; -- for next time around }; BuildActionAreaLine: PROC [ggData: GGData, startX: CARD] RETURNS [Viewer] = { height1: CARDINAL = buttonAlign; buttonHolder: Viewer _ GetButtonHolder[parent: ggData.outer, wx: startX, wy: ggData.height]; nextX: INTEGER _ AtomButtons.BuildButtonLine [buttonHolder, 0, 0, ggData, GGUserInput.EventNotify, LIST[[button["Fetch", LIST[LIST[$MatchFetch]], -1, FALSE, NIL]] ]]; tV: Viewer _ BiScrollers.CreateFit[ [parent: buttonHolder, wx: nextX+entryHSpace, wy: height1, border: FALSE], ggData.biScroller]; tV _ BiScrollers.CreateReset[ [parent: buttonHolder, wx: tV.wx+tV.ww+entryHSpace, wy: height1, border: FALSE], ggData.biScroller]; tV_ Buttons.Create[info: [parent: buttonHolder, name: "CenterSel", wx: tV.wx+tV.ww+entryHSpace, wy: 0, border: FALSE], proc: CenterSel, clientData: ggData, paint: TRUE]; RETURN[buttonHolder]; }; RegisterMatchActions: PROC = { GGUserInput.RegisterAction[atom: $MatchFetch, eventProc: Fetch, argType: none, ensureUnique: FALSE]; -- Fetch button GGUserInput.RegisterAction[atom: $MatchSetFrom, eventProc: SetFromColumn, argType: none, ensureUnique: FALSE]; GGUserInput.RegisterAction[atom: $MatchSetTo, eventProc: SetToColumn, argType: none, ensureUnique: FALSE]; GGUserInput.RegisterAction[atom: $MatchToggle, eventProc: ToggleFromOrTo, argType: none, ensureUnique: FALSE]; -- Toggle From/To buttons GGUserInput.RegisterAction[atom: $MatchSearch, eventProc: Match.SearchEvent, argType: none, ensureUnique: FALSE]; -- Search button GGUserInput.RegisterAction[atom: $MatchYes, eventProc: Match.YesEvent, argType: none, ensureUnique: FALSE]; -- Yes button GGUserInput.RegisterAction[atom: $MatchChangeAll, eventProc: Match.ChangeAll, argType: none, ensureUnique: FALSE]; -- ChangeAll button GGUserInput.RegisterAction[atom: $MatchActionChange, eventProc: MatchActionChange, argType: none, ensureUnique: FALSE]; -- Flip through action types GGUserInput.RegisterAction[atom: $MatchLevelChange, eventProc: MatchLevelChange, argType: none, ensureUnique: FALSE]; -- Flip through action types GGUserInput.RegisterAction[atom: $MatchSearchOpChange, eventProc: MatchSearchOpChange, argType: none, ensureUnique: FALSE]; -- Flip through SearchOps GGUserInput.RegisterAction[atom: $MatchToleranceChange, eventProc: MatchToleranceChange, argType: none, ensureUnique: FALSE]; -- Adjust match tolerance GGUserInput.RegisterAction[atom: $MatchExact, eventProc: MatchExact, argType: none, ensureUnique: FALSE]; -- Toggle the Exact Match button GGUserInput.RegisterAction[atom: $MatchStartOps, eventProc: Match.StartOps, argType: none, ensureUnique: FALSE]; -- Invoke a procedure to intitiate recording of To events GGUserInput.RegisterAction[atom: $MatchEndOps, eventProc: Match.EndOps, argType: none, ensureUnique: FALSE]; -- Invoke a procedure to terminate recording of To events GGUserInput.RegisterAction[atom: $MatchGrep, eventProc: MatchGrep.Grep, argType: rope, ensureUnique: FALSE]; -- Grep for the pattern in the From viewer GGUserInput.RegisterAction[atom: $MatchGrepListOnly, eventProc: MatchGrep.GrepListOnly, argType: rope, ensureUnique: FALSE]; -- Grep for the pattern in the From viewer GGUserInput.RegisterAction[atom: $MatchNextFile, eventProc: MatchGrep.NextFile, argType: none, ensureUnique: FALSE]; -- Edit the last gargoyle file found with grep }; GetButtonHolder: PROC [parent: Viewer, wx, wy, ww: CARD _ 0, wh: CARD _ entryHeight] RETURNS [Viewer] = { IF ww = 0 THEN ww _ parent.ww; RETURN[Containers.Create[info: [name: "holder", parent: parent, wx: wx, wy: wy, ww: ww, wh: wh, border: FALSE, scrollable: FALSE], paint: FALSE]]; }; <<>> <> <<>> CenterSel: PUBLIC Buttons.ButtonProc = { ggData: GGData _ NARROW[clientData]; caretPos: Point _ GGCaret.GetPoint[ggData.caret]; bBox: GGModelTypes.BoundBox _ GGScene.BoundBoxOfSelected[scene: ggData.scene, selectClass: normal]; IF bBox.null THEN BiScrollers.Align[bs: ggData.biScroller, client: [variant: coord[x: caretPos.x, y: caretPos.y]], viewer: [variant: fraction[fx: 0.5, fy: 0.5]], paint: TRUE] ELSE BiScrollers.Align[bs: ggData.biScroller, client: [variant: coord[x: (bBox.loX+bBox.hiX)/2.0, y: (bBox.loY+bBox.hiY)/2.0]], viewer: [variant: fraction[fx: 0.5, fy: 0.5]], paint: TRUE]; }; FitSel: Buttons.ButtonProc = { ggData: GGData _ NARROW[clientData]; tbBox, vBox, tvBox: Geom2D.Rect; cToV: ImagerTransformation.Transformation; bBox: GGModelTypes.BoundBox _ GGScene.BoundBoxOfSelected[scene: ggData.scene, selectClass: normal]; IF NOT bBox.null THEN { cToV _ BiScrollers.GetStyle[].GetTransforms[ggData.biScroller].clientToViewer; tbBox _ ImagerTransformation.TransformRectangle[cToV, GGBoundBox.RectangleFromBoundBox[bBox] ]; vBox _ BiScrollers.ViewportBox[ggData.biScroller]; tvBox _ ImagerTransformation.TransformRectangle[cToV, vBox]; BiScrollers.BoxScale[bs: ggData.biScroller, from: tbBox, to: tvBox]; <> }; }; <> <<>> SetFromColumn: GGUserInput.UserInputProc = { SetColumn[matchData.from, event.rest.first = $SetAll]; }; SetToColumn: GGUserInput.UserInputProc = { SetColumn[matchData.to, event.rest.first = $SetAll]; }; SetColumn: PROC [choice: MatchViewer.ChoiceData, state: BOOL] = { AtomButtons.SetBinaryState[choice.class, state]; AtomButtons.SetBinaryState[choice.type, state]; AtomButtons.SetBinaryState[choice.shape, state]; AtomButtons.SetBinaryState[choice.color, state]; AtomButtons.SetBinaryState[choice.fillColor, state]; AtomButtons.SetBinaryState[choice.string, state]; AtomButtons.SetBinaryState[choice.font, state]; AtomButtons.SetBinaryState[choice.fontTform, state]; AtomButtons.SetBinaryState[choice.dash, state]; AtomButtons.SetBinaryState[choice.joints, state]; AtomButtons.SetBinaryState[choice.ends, state]; AtomButtons.SetBinaryState[choice.width, state]; }; ToggleFromOrTo: GGUserInput.UserInputProc = { FlipThreeAsUnit: PROC = { AtomButtons.SwitchBinaryState[matchData.to.class]; AtomButtons.SwitchBinaryState[matchData.to.type]; AtomButtons.SwitchBinaryState[matchData.to.shape]; }; SELECT event.rest.first FROM $FromClass => AtomButtons.SwitchBinaryState[matchData.from.class]; $ToClass => FlipThreeAsUnit[]; $FromType => AtomButtons.SwitchBinaryState[matchData.from.type]; $ToType => FlipThreeAsUnit; $FromShape => AtomButtons.SwitchBinaryState[matchData.from.shape]; $ToShape => FlipThreeAsUnit; $FromColor => AtomButtons.SwitchBinaryState[matchData.from.color]; $ToColor => AtomButtons.SwitchBinaryState[matchData.to.color]; $FromFillColor => AtomButtons.SwitchBinaryState[matchData.from.fillColor]; $ToFillColor => AtomButtons.SwitchBinaryState[matchData.to.fillColor]; $FromString => AtomButtons.SwitchBinaryState[matchData.from.string]; $ToString => AtomButtons.SwitchBinaryState[matchData.to.string]; $FromFont => AtomButtons.SwitchBinaryState[matchData.from.font]; $ToFont => AtomButtons.SwitchBinaryState[matchData.to.font]; $FromFontTform => AtomButtons.SwitchBinaryState[matchData.from.fontTform]; $ToFontTform => AtomButtons.SwitchBinaryState[matchData.to.fontTform]; $FromDash => AtomButtons.SwitchBinaryState[matchData.from.dash]; $ToDash => AtomButtons.SwitchBinaryState[matchData.to.dash]; $FromJoints => AtomButtons.SwitchBinaryState[matchData.from.joints]; $ToJoints => AtomButtons.SwitchBinaryState[matchData.to.joints]; $FromEnds => AtomButtons.SwitchBinaryState[matchData.from.ends]; $ToEnds => AtomButtons.SwitchBinaryState[matchData.to.ends]; $FromWidth => AtomButtons.SwitchBinaryState[matchData.from.width]; $ToWidth => AtomButtons.SwitchBinaryState[matchData.to.width]; $RotationInv => AtomButtons.SwitchBinaryState[matchData.rotationInv]; $ScaleInv => AtomButtons.SwitchBinaryState[matchData.scaleInv]; $Polarity => AtomButtons.SwitchBinaryState[matchData.polarity]; $Structure => AtomButtons.SwitchBinaryState[matchData.structure]; $MatchNodes => AtomButtons.SwitchBinaryState[matchData.matchNodes]; $ContextSensitive => AtomButtons.SwitchBinaryState[matchData.contextSensitive]; ENDCASE; }; MatchActionChange: GGUserInput.UserInputProc = { name: Rope.ROPE; AtomButtons.TimeToFlipThru[LIST[$FlipForward, matchData.replaceOpButton]]; name _ matchData.replaceOpButton.flipLabel.name; SELECT TRUE FROM Rope.Equal[name, "Do Replace"] => {matchData.replaceOp _ doReplace;}; Rope.Equal[name, "Do Operations"] => {matchData.replaceOp _ doOperations;}; ENDCASE => ERROR; }; MatchLevelChange: GGUserInput.UserInputProc = { name: Rope.ROPE; AtomButtons.TimeToFlipThru[LIST[$FlipForward, matchData.levelButton]]; name _ matchData.levelButton.flipLabel.name; SELECT TRUE FROM Rope.Equal[name, "Anywhere"] => matchData.matchLevel _ anywhere; Rope.Equal[name, "Trajectory Level"] => matchData.matchLevel _ trajLevel; Rope.Equal[name, "Slice Level"] => matchData.matchLevel _ sliceLevel; ENDCASE => ERROR; }; MatchSearchOpChange: GGUserInput.UserInputProc = { name: Rope.ROPE; AtomButtons.TimeToFlipThru[LIST[$FlipForward, matchData.searchOpButton]]; name _ matchData.searchOpButton.flipLabel.name; SELECT TRUE FROM Rope.Equal[name, "Disjunction"] => matchData.searchOp _ disjunction; Rope.Equal[name, "Conjunction"] => matchData.searchOp _ conjunction; ENDCASE => ERROR; }; minMatchTol: REAL _ .02; MatchToleranceChange: GGUserInput.UserInputProc = { toleranceData: ToleranceData _ NARROW[GraphicsButton.GetValue[matchData.toleranceButton].buttonData]; IF AtomButtons.GetBinaryState[matchData.exactMatch] THEN { -- Exact button is on ErrorFeedback["First turn Exact match off."]; RETURN; }; SELECT event.rest.first FROM $ValueUp => toleranceData.current _ toleranceData.current + .1; $InitialValue => toleranceData.current _ initialTolerance; $ValueDown => toleranceData.current _ toleranceData.current - .1; ENDCASE => ERROR; toleranceData.current _ MAX[MIN[toleranceData.current, 1.0], minMatchTol]; GraphicsButton.SetButtonValueAndPaint[matchData.toleranceButton, ggData, toleranceData]; }; MatchExact: GGUserInput.UserInputProc = { toleranceData: ToleranceData _ NARROW[GraphicsButton.GetValue[matchData.toleranceButton].buttonData]; AtomButtons.SwitchBinaryState[matchData.exactMatch]; GraphicsButton.SetButtonValueAndPaint[matchData.toleranceButton, ggData, toleranceData]; }; GetMatchTolerance: PUBLIC PROC RETURNS [REAL] = { toleranceData: ToleranceData _ NARROW[GraphicsButton.GetValue[matchData.toleranceButton].buttonData]; IF AtomButtons.GetBinaryState[matchData.exactMatch] THEN RETURN[minMatchTol] -- Exact button is on, so it overrides tolerance value ELSE RETURN[toleranceData.current]; }; GetContextSensitive: PUBLIC PROC [matchData: MatchData] RETURNS [contextSensitive: BOOL] = { contextSensitive _ AtomButtons.GetBinaryState[matchData.contextSensitive]; }; <> Fetch: GGUserInput.UserInputProc = { <> SortNewEntities: PROC [entityList: LIST OF Slice] RETURNS [sorted: LIST OF Slice] = { <> CompareProc: GGUtility.SliceCompareProc = { <<[ref1: Slice, ref2: Slice] RETURNS [Basics.Comparison]>> priority1, priority2: INT; priority1 _ ref1.priority; priority2 _ ref2.priority; RETURN[Basics.CompareInt[priority1, priority2]]; }; sorted _ GGUtility.SortSliceList[entityList, CompareProc]; }; theirScene: Scene _ NIL; ourScene: Scene _ ggData.scene; feedback: FeedbackData _ ggData.feedback; sliceDGen: SliceDescriptorGenerator; outSeqGen: GGSelect.OutlineSequenceGenerator; newOutline, outline: Slice; newTraj: Traj; newSlice: Slice; newSlices: LIST OF Slice; <> theirData: GGData _ GetGGInputFocus[]; IF theirData = NIL THEN RETURN ELSE theirScene _ theirData.scene; <> sliceDGen _ GGSelect.SelectedSlices[theirScene, normal]; FOR sliceD: SliceDescriptor _ GGSelect.NextSliceDescriptor[sliceDGen], GGSelect.NextSliceDescriptor[sliceDGen] UNTIL sliceD = NIL DO IF sliceD.slice.class.type#$Outline THEN { -- outlines are handled separately, below newSlice _ GGSlice.CopySlice[sliceD.slice]; -- make the new one newSlice.priority _ GGScene.GetPriority[theirScene, sliceD.slice]; <> newSlices _ CONS[newSlice, newSlices]; }; ENDLOOP; BEGIN <> outSeqGen _ GGSelect.SelectedOutlineSequences[theirScene, normal]; FOR outSeq: GGSelect.OutlineSequence _ GGSelect.NextOutlineSequences[outSeqGen], GGSelect.NextOutlineSequences[outSeqGen] UNTIL outSeq = NIL DO outline _ outSeq.outline; IF outSeq.fenceSeq # NIL THEN { IF NOT GGSequence.ContainsSomeSegment[outSeq.fenceSeq] THEN GOTO NoSegmentsSelected; <> IF GGSelect.IsSelectedInFull[outline, theirScene, normal] THEN { newOutline _ outline.class.copy[outline]; newOutline.priority _ GGScene.GetPriority[theirScene, outline]; <> newSlices _ CONS[newOutline, newSlices]; LOOP; } <> ELSE { newTraj _ GGTraj.CopyTrajFromRun[outSeq.fenceSeq]; newOutline _ GGOutline.CreateOutline[newTraj, outline.class.getFillColor[outline]]; newOutline.priority _ GGScene.GetPriority[theirScene, outline]; <> newSlices _ CONS[newOutline, newSlices]; }; }; FOR holeSeq: Sequence _ GGSequence.NextSequence[outSeq.holeSeqs], GGSequence.NextSequence[outSeq.holeSeqs] UNTIL holeSeq = NIL DO IF NOT GGSequence.ContainsSomeSegment[holeSeq] THEN GOTO NoSegmentsSelected; newTraj _ GGTraj.CopyTrajFromRun[holeSeq]; newOutline _ GGOutline.CreateOutline[newTraj, outline.class.getFillColor[outline]]; newOutline.priority _ GGScene.GetPriority[theirScene, outline]; <> newSlices _ CONS[newOutline, newSlices]; ENDLOOP; ENDLOOP; <> newSlices _ SortNewEntities[newSlices]; <> FOR sliceList: LIST OF Slice _ newSlices, sliceList.rest UNTIL sliceList = NIL DO allParts: SliceDescriptor; GGScene.AddSlice[ourScene, sliceList.first, -1]; allParts _ sliceList.first.class.newParts[sliceList.first, NIL, slice]; GGSelect.SelectSlice[allParts, ourScene, normal]; ENDLOOP; GGAlign.UpdateBagsForNewSlices[newSlices, ggData]; ggData.refresh.startBoundBox _ GGScene.BoundBoxOfSelected[ggData.scene, normal]; GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedDragging, ggData: ggData, remake: sceneBag, backgndOK: FALSE, edited: TRUE, okToClearFeedback: TRUE]; EXITS NoSegmentsSelected => { Feedback.AppendHerald[feedback, ". . . Cannot Copy Joints or CPs", begin]; Feedback.Blink[feedback]; GGSelect.DeselectAll[theirScene, normal]; <> }; END; }; <> column0: CARD = 0; -- x coordinate of Characteristics column column1: CARD = 135; -- x coordinate of From column column2: CARD = 195; -- x coordinate of To column column3: CARD = 245; -- x coordinate of Search button line upperMargin: CARD = 8; -- Vertical space between actionAreas and Characteristics line lowerMargin: CARD = 5; -- Extra space between Characteristics description line and FromTo entries fromBoxOffset: CARD = 14; -- column1 + fromBoxOffset = x location of From boxes toBoxOffset: CARD = 4; -- column2 + toBoxOffset = x location of To boxes matchData: MatchData _ NewMatchDataObj[]; NewMatchDataObj: PUBLIC PROC RETURNS [matchData: MatchData] = { matchData _ NEW[MatchDataObj]; matchData.from _ NEW[ChoiceDataObj]; matchData.to _ NEW[ChoiceDataObj]; }; InitControlPanel: PROC [parent: Viewer, startHeight: CARD] = { MakeFromToEntry: PROC [string: Rope.ROPE, parent: Viewer, height: CARD, fromAction: LIST OF REF ANY, toAction: LIST OF REF ANY] RETURNS [fromState, toState: AtomButtons.TwoState, newHeight: CARD] = { [] _ Labels.Create[[name: string, parent: parent, wx: column0, wy: height, border: FALSE]]; [] _ AtomButtons.BuildButtonLine[container: parent, x: column1 + fromBoxOffset, y: height, clientData: parent.data, handleProc: GGUserInput.EventNotify, entries: LIST [ [twoState[" ", fromAction, FALSE, FillGlobalTwoState]]]]; fromState _ globalTwoState; [] _ AtomButtons.BuildButtonLine[container: parent, x: column2 + toBoxOffset, y: height, clientData: parent.data, handleProc: GGUserInput.EventNotify, entries: LIST [ [twoState[" ", toAction, FALSE, FillGlobalTwoState]]]]; toState _ globalTwoState; newHeight _ height + entryHeight + entryVSpace; }; <> height: CARD _ startHeight; [] _ Labels.Create[[name: "CHARACTERISTICS", parent: parent, wx: column0, wy: height, border: FALSE]]; [] _ AtomButtons.BuildButtonLine[container: parent, x: column1, y: height, clientData: parent.data, handleProc: GGUserInput.EventNotify, entries: LIST[ [button["FROM", LIST[LIST[$MatchSetFrom, $ClearAll], LIST[$MatchSetFrom, $ClearAll], LIST[$MatchSetFrom, $SetAll]]]]]]; [] _ AtomButtons.BuildButtonLine[container: parent, x: column2, y: height, clientData: parent.data, handleProc: GGUserInput.EventNotify, entries: LIST[ [button["TO", LIST[LIST[$MatchSetTo, $ClearAll], LIST[$MatchSetTo, $ClearAll], LIST[$MatchSetTo, $SetAll]]]]]]; <<[] _ Labels.Create[[name: "FROM", parent: parent, wx: column1, wy: height, border: FALSE]];>> <<[] _ Labels.Create[[name: "TO", parent: parent, wx: column2, wy: height, border: FALSE]];>> height _ height + entryHeight + lowerMargin; [matchData.from.class, matchData.to.class, height] _ MakeFromToEntry["Object class", parent, height, LIST[$MatchToggle, $FromClass], LIST[$MatchToggle, $ToClass]]; [matchData.from.type, matchData.to.type, height] _ MakeFromToEntry["Object type", parent, height, LIST[$MatchToggle, $FromType], LIST[$MatchToggle, $ToType]]; [matchData.from.shape, matchData.to.shape, height] _ MakeFromToEntry["Object shape", parent, height, LIST[$MatchToggle, $FromShape], LIST[$MatchToggle, $ToShape]]; [matchData.from.color, matchData.to.color, height] _ MakeFromToEntry["Object color", parent, height, LIST[$MatchToggle, $FromColor], LIST[$MatchToggle, $ToColor]]; [matchData.from.fillColor, matchData.to.fillColor, height] _ MakeFromToEntry["Object fill color", parent, height, LIST[$MatchToggle, $FromFillColor], LIST[$MatchToggle, $ToFillColor]]; [matchData.from.string, matchData.to.string, height] _ MakeFromToEntry["Text string", parent, height, LIST[$MatchToggle, $FromString], LIST[$MatchToggle, $ToString]]; [matchData.from.font, matchData.to.font, height] _ MakeFromToEntry["Text font", parent, height, LIST[$MatchToggle, $FromFont], LIST[$MatchToggle, $ToFont]]; [matchData.from.fontTform, matchData.to.fontTform, height] _ MakeFromToEntry["Text font & transform", parent, height, LIST[$MatchToggle, $FromFontTform], LIST[$MatchToggle, $ToFontTform]]; [matchData.from.dash, matchData.to.dash, height] _ MakeFromToEntry["Line dashes", parent, height, LIST[$MatchToggle, $FromDash], LIST[$MatchToggle, $ToDash]]; [matchData.from.joints, matchData.to.joints, height] _ MakeFromToEntry["Line joints", parent, height, LIST[$MatchToggle, $FromJoints], LIST[$MatchToggle, $ToJoints]]; [matchData.from.ends, matchData.to.ends, height] _ MakeFromToEntry["Line ends", parent, height, LIST[$MatchToggle, $FromEnds], LIST[$MatchToggle, $ToEnds]]; [matchData.from.width, matchData.to.width, height] _ MakeFromToEntry["Line width", parent, height, LIST[$MatchToggle, $FromWidth], LIST[$MatchToggle, $ToWidth]]; InitRightControlPanel[parent, startHeight]; }; InitRightControlPanel: PROC [parent: Viewer, startHeight: CARD] = { <> nextX: CARD; height: CARD _ startHeight - buttonAlign; <> [] _ AtomButtons.BuildButtonLine [parent, column3, height, parent.data, GGUserInput.UnQueuedEventNotify, LIST[ [button["Search", LIST[LIST[$MatchSearch, $SearchBelow], LIST[$MatchSearch, $SearchFromTop], LIST[$MatchSearch, $SearchAbove]]]], [button["Yes", LIST[LIST[$MatchYes, $SearchBelow], LIST[$MatchYes, $SearchFromTop], LIST[$MatchYes, $SearchAbove]]]], [button["No", LIST[LIST[$MatchSearch, $SearchBelow], LIST[$MatchSearch, $SearchFromTop], LIST[$MatchSearch, $SearchAbove]]]], [button["ChangeAll", LIST[LIST[$MatchChangeAll, $SearchBelow], LIST[$MatchChangeAll, $SearchFromTop], LIST[$MatchChangeAll, $SearchAbove]]]]]]; height _ height + entryHeight + entryVSpace; matchData.replaceOpButton _ AtomButtons.BuildEnumTypeSelection[viewer: parent, x: column3, y: height, maxWidth: 0, clientData: parent.data, handleProc: GGUserInput.EventNotify, title: "Action:", default: "Do Replace", borderOnButtons: TRUE, style: flipThru, allInOneRow: TRUE, buttonNames: LIST["Do Replace", "Do Operations"], atom: $MatchActionChange]; matchData.replaceOp _ doReplace; height _ height + entryHeight + entryVSpace; matchData.levelButton _ AtomButtons.BuildEnumTypeSelection[viewer: parent, x: column3, y: height, maxWidth: 0, clientData: parent.data, handleProc: GGUserInput.EventNotify, title: "Match:", default: "Slice Level", borderOnButtons: TRUE, style: flipThru, allInOneRow: TRUE, buttonNames: LIST["Anywhere", "Trajectory Level", "Slice Level"], atom: $MatchLevelChange]; matchData.matchLevel _ trajLevel; height _ height + entryHeight + entryVSpace; matchData.searchOpButton _ AtomButtons.BuildEnumTypeSelection[viewer: parent, x: column3, y: height, maxWidth: 0, clientData: parent.data, handleProc: GGUserInput.EventNotify, title: "SearchOp:", default: "Disjunction", borderOnButtons: TRUE, style: flipThru, allInOneRow: TRUE, buttonNames: LIST["Disjunction", "Conjunction"], atom: $MatchSearchOpChange]; matchData.searchOp _ disjunction; height _ height + entryHeight + entryVSpace; nextX _ AtomButtons.BuildButtonLine[container: parent, x: column3, y: height, clientData: parent.data, handleProc: GGUserInput.EventNotify, entries: LIST[ [twoState["Exact", LIST[$MatchExact], TRUE, FillExact]], [label["Tolerance:"]]]]; nextX _ GraphicsButton.BuildGraphicsButton[container: parent, x: nextX, y: height, w: 60, h: entryHeight, clientData: parent.data, choices: LIST[[LIST[$MatchToleranceChange, $ValueDown]], [LIST[$MatchToleranceChange, $InitialValue]], [LIST[$MatchToleranceChange, $ValueUp]]], handleProc: GGUserInput.EventNotify, repaintProc: ToleranceButtonRepaint, buttonData: NEW[ToleranceDataObj], updateProc: ToleranceButtonUpdate]; height _ height + entryHeight + entryVSpace; nextX _ AtomButtons.BuildButtonLine[container: parent, x: column3, y: height, clientData: parent.data, handleProc: GGUserInput.EventNotify, entries: LIST[ [twoState["RotationInv", LIST[$MatchToggle, $RotationInv], FALSE, FillRotationInv]], [twoState["ScaleInv", LIST[$MatchToggle, $ScaleInv], FALSE, FillScaleInv]]]]; height _ height + entryHeight + entryVSpace; nextX _ AtomButtons.BuildButtonLine[container: parent, x: column3, y: height, clientData: parent.data, handleProc: GGUserInput.EventNotify, entries: LIST[ [twoState["Polarity", LIST[$MatchToggle, $Polarity], TRUE, FillPolarity]], [twoState["ContextSensitive", LIST[$MatchToggle, $ContextSensitive], FALSE, FillContextSensitive]]]]; height _ height + entryHeight + entryVSpace; nextX _ AtomButtons.BuildButtonLine[container: parent, x: column3, y: height, clientData: parent.data, handleProc: GGUserInput.EventNotify, entries: LIST[ [button["StartOps", LIST[LIST[$MatchStartOps], LIST[$MatchStartOps], LIST[$MatchStartOps]]]], [button["EndOps", LIST[LIST[$MatchEndOps], LIST[$MatchEndOps], LIST[$MatchEndOps]]]]]]; height _ height + entryHeight + entryVSpace; nextX _ AtomButtons.BuildButtonLine[container: parent, x: column3, y: height, clientData: parent.data, handleProc: GGUserInput.EventNotify, entries: LIST [ [button["Grep", LIST[LIST[$MatchGrep], LIST[$MatchGrep], LIST[$MatchGrepListOnly]]]], [button["NextFile", LIST[LIST[$MatchNextFile], LIST[$MatchNextFile], LIST[$MatchNextFile]]]]]]; }; <<>> <> <<>> globalTwoState: AtomButtons.TwoState; FillGlobalTwoState: AtomButtons.InitTwoStateProc = {globalTwoState _ twoState;}; FillExact: AtomButtons.InitTwoStateProc = {matchData.exactMatch _ twoState;}; FillRotationInv: AtomButtons.InitTwoStateProc = {matchData.rotationInv _ twoState;}; FillScaleInv: AtomButtons.InitTwoStateProc = {matchData.scaleInv _ twoState;}; FillPolarity: AtomButtons.InitTwoStateProc = {matchData.polarity _ twoState;}; FillStructure: AtomButtons.InitTwoStateProc = {matchData.structure _ twoState;}; FillMatchNodes: AtomButtons.InitTwoStateProc = {matchData.matchNodes _ twoState;}; FillContextSensitive: AtomButtons.InitTwoStateProc = {matchData.contextSensitive _ twoState;}; <> <<>> initialTolerance: REAL = 0.2; ToleranceData: TYPE = REF ToleranceDataObj; ToleranceDataObj: TYPE = RECORD [ current: REAL _ initialTolerance ]; ToleranceButtonRepaint: PROC [dc: Imager.Context, clientData: REF ANY, buttonData: REF ANY, button: Viewer] = { <> caretPoint: Point; tol: REAL _ GetMatchTolerance[]; extent: REAL _ tol * button.ww; Imager.TranslateT[dc, [0, button.wh/2.0]]; caretPoint _ [extent, 0.0]; GGShapes.DrawCaret[dc, caretPoint, [0, -1]]; }; ToleranceButtonUpdate: AtomButtonsTypes.UpdateGraphicsButtonProc = { matchData.toleranceButton _ stateInfo; }; <> <<>> ErrorFeedback: PUBLIC PROC [message: Rope.ROPE] = { ggData: GGData _ GetToData[]; Feedback.PutFHerald[ggData.feedback, oneLiner, message]; Feedback.Blink[ggData.feedback]; }; GetGGInputFocus: PUBLIC PROC RETURNS [theirData: GGData _ NIL] = { inputFocus: InputFocus.Focus _ InputFocus.GetInputFocus[]; theirViewer: Viewer; IF inputFocus = NIL OR inputFocus.owner = NIL THEN RETURN; theirViewer _ inputFocus.owner; IF theirViewer.class.flavor # $ActionArea THEN ErrorFeedback["Place input focus in a Gargoyle viewer."] ELSE theirData _ NARROW[BiScrollers.ClientDataOf[NARROW[theirViewer.data, BiScrollers.BiScroller]], GGData]; }; GetMatchData: PUBLIC PROC RETURNS [MatchData] = { RETURN [matchData]; }; FindAGargoyleViewer: PROC RETURNS [ggData: GGData _ NIL] = { IsGargoyleViewer: ViewerOps.EnumProc = { IF Rope.Equal["Gargoyle", Rope.Substr[v.name, 0, 8]] AND ISTYPE[v.data, GGData] THEN { ggData _ NARROW[v.data]; RETURN[FALSE]; -- terminate enumeration } ELSE RETURN[TRUE]; -- continue enumerating }; ViewerOps.EnumerateViewers[IsGargoyleViewer]; -- look for a viewer whose name starts with "Gargoyle" }; <<>> <> <<>> MatchCommand: Commander.CommandProc = { matchData.commander _ cmd; matchData.hostData _ FindAGargoyleViewer[]; -- so we can leach on its menu and use its SlackProcess IF matchData.hostData = NIL THEN IO.PutRope[cmd.err, "Invoke a gargoyle viewer first.\n"] ELSE InitViewers[]; }; matchToolIcon: Icons.IconFlavor _ Icons.NewIconFromFile["Gargoyle.icons", 4]; -- shares Icon file with Gargoyle RegisterMatchActions[]; Commander.Register["MatchTool", MatchCommand]; END.