<> <> <> <> <> <> <> DIRECTORY AtomButtons, AtomButtonsTypes, BiScrollers, Buttons, Commander, Containers, Feedback, FeedbackOps, Geom2D, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGControlPanelTypes, GGCoreOps, GGDragTypes, GGEmbedTypes, GGInterfaceTypes, GGModelTypes, GGMouseEvent, GGMultiGravity, GGOutline, GGRefresh, GGRefreshTypes, GGScene, GGSessionLog, GGShapes, GGSlice, GGState, GGStateExtras, GGStateTypes, GGUserInput, GGUserProfile, GGWindow, GList, GraphicsButton, Icons, Imager, ImagerTransformation, InputFocus, IO, Labels, Match, MatchGrep, MatchViewer, MJSContainers, Rope, SlackProcess, TIPUser, ViewerClasses, ViewerOps; MatchViewerImpl: CEDAR PROGRAM IMPORTS AtomButtons, BiScrollers, Buttons, Commander, Containers, Feedback, FeedbackOps, Geom2D, GGAlign, GGBoundBox, GGCaret, GGCoreOps, GGMouseEvent, GGMultiGravity, GGOutline, GGRefresh, GGScene, GGSessionLog, GGShapes, GGSlice, GGState, GGStateExtras, GGUserInput, GGUserProfile, GGWindow, GList, GraphicsButton, Icons, Imager, ImagerTransformation, InputFocus, IO, Labels, Match, MatchGrep, MJSContainers, Rope, SlackProcess, TIPUser, ViewerOps EXPORTS MatchViewer, GGInterfaceTypes = BEGIN ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes EmbedDataObj: PUBLIC TYPE = GGEmbedTypes.EmbedDataObj; -- exported to GGInterfaceTypes RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; StateDataObj: PUBLIC TYPE = GGStateTypes.StateDataObj; -- exported to GGInterfaceTypes DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj; CameraObj: TYPE = GGModelTypes.CameraObj; Caret: TYPE = REF CaretObj; CaretObj: TYPE = GGInterfaceTypes.CaretObj; ChoiceDataObj: TYPE = MatchViewer.ChoiceDataObj; Controls: TYPE = GGInterfaceTypes.Controls; FiltersObj: TYPE = GGInterfaceTypes.FiltersObj; GGData: TYPE = GGInterfaceTypes.GGData; GGDataObj: TYPE = GGInterfaceTypes.GGDataObj; MatchData: TYPE = MatchViewer.MatchData; MatchDataObj: TYPE = MatchViewer.MatchDataObj; Point: TYPE = GGBasicTypes.Point; Sequence: TYPE = GGModelTypes.Sequence; Scene: TYPE = GGModelTypes.Scene; SearchState: TYPE = Match.SearchState; Slice: TYPE = GGModelTypes.Slice; SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor; SliceDescriptorGenerator: TYPE = GGModelTypes.SliceDescriptorGenerator; Traj: TYPE = GGModelTypes.Traj; Viewer: TYPE = ViewerClasses.Viewer; minMatchTol: REAL _ .02; initialTolerance: REAL = 0.2; ToleranceData: TYPE = REF ToleranceDataObj; ToleranceDataObj: TYPE = RECORD [ current: REAL _ initialTolerance ]; 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 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 actionAreaClass: BiScrollers.BiScrollerClass _ NIL; -- filled in by MatchInit <<>> <> globalTwoState: AtomButtons.TwoState; matchData: MatchData; -- global one of a kind matchData matchToolIcon: Icons.IconFlavor; -- MatchTool icon matchContainerClass: ViewerClasses.ViewerClass; fromViewer, toViewer: Viewer; fromLine, toLine: Viewer; fromData, toData, grepData: GGData; <> ErrorFeedback: PUBLIC PROC [message: Rope.ROPE] = { ggData: GGData _ GetToData[]; Feedback.PutF[ggData.router, oneLiner, $Error, message]; Feedback.Blink[ggData.router, $Error]; }; CreateTool: PROC [] = { [] _ ViewerOps.CreateViewer [flavor: $MatchContainer, info: [name: "MatchTool", menu: NIL, iconic: TRUE, column: right, scrollable: FALSE ], paint: TRUE]; -- MatchContainerInit will be called by Viewers as an effect of CreateViewer }; MatchContainerInit: ViewerClasses.InitProc = { fromData _ BuildMatchGGData[self]; -- From viewer GG Data toData _ BuildMatchGGData[self]; -- To viewer GG Data grepData _ BuildMatchGGData[self]; -- this data is used to search scenes during grep CreateFeedbackLine[toData, fromData, grepData]; fromViewer _ BuildActionArea[self, fromData]; -- positioned by AdjustProc toViewer _ BuildActionArea[self, toData]; -- positioned by AdjustProc fromLine _ BuildActionAreaLine[fromData, 0]; -- positioned by AdjustProc toLine _ BuildActionAreaLine[toData, 0]; -- positioned by AdjustProc BuildControlPanel[self, actionAreaStartY + actionAreaHeight + entryHeight + upperMargin]; fromData.embed.beingBorn _ FALSE; -- start input handling toData.embed.beingBorn _ FALSE; grepData.embed.beingBorn _ FALSE; }; NormalPaint: GGRefresh.PaintProc = { <> <> ViewerOps.PaintViewer[viewer: ggData.controls.actionArea, hint: client, whatChanged: ggData, clearClient: FALSE]; }; BuildMatchGGData: PROC [outer: Viewer] RETURNS [ggData: GGData] = { <> <> <<>> <> <> <<>> <> <> scene: Scene _ GGScene.CreateScene[]; ggData _ NEW[GGDataObj]; -- removed implicit copying of initial hostData values ggData.controls _ NEW[ControlsObj]; ggData.controlState _ NEW[StateDataObj]; ggData.controlState.doubleBuffer _ TRUE; ggData.controlState.clientToViewer _ ImagerTransformation.Scale[1.0]; ggData.controlState.viewerToClient _ ImagerTransformation.Scale[1.0]; ggData.drag _ NEW[DragDataObj]; ggData.embed _ NEW[EmbedDataObj]; ggData.embed.scrollDue _ ImagerTransformation.Scale[1.0]; GGRefresh.RegisterPaintProc[ggData, NormalPaint, NIL]; GGStateExtras.RegisterViewportProc[ggData, GGStateExtras.DefaultViewport, NIL]; ggData.embed.beingBorn _ TRUE; ggData.controls.bufferButton _ matchData.hostData.controls.bufferButton; outer.data _ ggData; ggData.controls.topper.newVersion _ ggData.controls.panel.newVersion _ FALSE; ggData.controls.topper _ ggData.controls.panel _ outer; <> ggData.originalWDir _ matchData.hostData.originalWDir; GGStateExtras.SetWorkingDirectory[ggData, matchData.hostData.currentWDir]; <> ggData.scene _ scene; ggData.caret _ NEW[CaretObj]; ggData.drag.savedCaret _ GGCaret.Create[]; ggData.anchor _ GGCaret.Create[]; ggData.camera _ NEW[CameraObj]; <> ggData.lastEvents _ GGCoreOps.NewEventListt[]; GGMouseEvent.InitializeFSM[ggData]; ggData.history _ [list: LIST[NIL], maxSize: 1, currentIndex: 0]; -- no history tool. Only SHIFT-ESC <> ggData.drag.selectState _ none; ggData.drag.extendMode _ none; GGState.SetSelectionCycler[ggData, GGMultiGravity.EmptyCycler[[0.0, 0.0]] ]; <> ggData.hitTest _ NEW[FiltersObj _ matchData.hostData.hitTest^]; -- copy record containing AtomButtons, then overwrite bags ggData.hitTest.triggerBag _ GGAlign.CreateTriggerBag[]; ggData.hitTest.oldTriggerBag _ GGAlign.CreateTriggerBag[]; ggData.hitTest.sceneBag _ GGAlign.CreateTriggerBag[]; ggData.hitTest.oldSceneBag _ GGAlign.CreateTriggerBag[]; ggData.hitTest.alignBag _ GGAlign.CreateAlignBag[]; ggData.hitTest.oldAlignBag _ GGAlign.CreateAlignBag[]; ggData.multiGravityPool _ GGMultiGravity.NewMultiGravityPool[]; <> ggData.refresh _ NEW[RefreshDataObj]; GGAlign.CreateLineTable[ggData]; -- part of RefreshData ggData.refresh.startBoundBox _ GGBoundBox.NullBoundBox[]; ggData.refresh.beforeBox _ GGBoundBox.NullBoundBox[]; ggData.refresh.paintBox _ GGBoundBox.NullBoundBox[]; ggData.refresh.totalBox _ GGBoundBox.NullBoundBox[]; ggData.refresh.sandwich _ GGRefresh.CreateSandwich[]; ggData.refresh.oldTransform _ ImagerTransformation.Scale[1.0]; <> ggData.behavior.activeDoc _ NIL; -- next time this field is needed it will be recomputed ggData.rootSlice _ GGSlice.MakeCircleSlice[[200.0, 200.0], [300.0, 200.0]].slice; ggData.measure _ matchData.hostData.measure; -- share record containing Viewers <> ggData.debug _ NEW[GGInterfaceTypes.DebugDataObj _ matchData.hostData.debug^]; -- share record containing IO.STREAMS ggData.defaults _ NEW[GGInterfaceTypes.DefaultDataObj _ [ strokeColor: Imager.black, fillColor: GGOutline.fillColor, font: GGUserProfile.GetDefaultDefaultFont[], dropShadowColor: Imager.black ]]; ggData.slackHandle _ matchData.hostData.slackHandle; ggData.height _ 0; }; CreateFeedbackLine: PROC [data1, data2, grepData: GGData] = { <> BuildFeedbackLine[data1]; grepData.router _ data2.router _ data1.router; data2.height _ data2.height + entryHeight; -- data1 was incremented by BuildFeedbackLine grepData.height _ grepData.height + entryHeight; -- just for consistency. Unused. }; fullColumn: CARDINAL = 600; -- the width of the standard large left column. BuildFeedbackLine: PROC [ggData: GGData] = { nextX: NAT _ AtomButtons.BuildButtonLine[ggData.controls.panel, 0, ggData.height, ggData, GGUserInput.EventNotify, LIST[ [label["Feedback . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Line", FeedbackLineInGGData, fullColumn]] ]]; ggData.height _ ggData.height + entryHeight; }; FeedbackLineInGGData: AtomButtons.UpdateProc = { ggData: GGData _ NARROW[clientData]; router: Feedback.MsgRouter _ Feedback.CreateRouter[]; FeedbackOps.SetMultiLabel[router, button, TRUE, LIST[$Error, $Complaint]]; -- blink FeedbackOps.SetMultiLabel[router, button, FALSE, LIST[$DuringMouse, $Feedback, $Warning, $Confirm, $Show, $Statistics]]; FeedbackOps.SetMultiTypescript[router, $Gargoyle, LIST[$Error, $Warning, $Show, $Typescript, $Complaint, $Statistics]]; ggData.router _ router; ggData.controls.feedbackLine _ button; }; BuildActionArea: PROC [outer: Viewer, ggData: GGData] RETURNS [Viewer] = { <> bs: BiScrollers.BiScroller _ BiScrollers.GetStyle[].CreateBiScroller[ class: actionAreaClass, info: [ parent: ggData.controls.topper, wx: 0, wy: actionAreaStartY, ww: 700, --ggData.outer.ww, wh: actionAreaHeight, --ggData.outer.wh, <> data: ggData, border: TRUE, scrollable: FALSE], paint: FALSE ]; ggData.controls.actionArea _ bs.QuaViewer[inner: TRUE]; ggData.controls.biScroller _ bs; GGWindow.SetCursorLooks[ggData.hitTest.gravityType, ggData]; -- assumes gravity is turned ON ggData.height _ ggData.height + actionAreaHeight; RETURN[bs.QuaViewer[inner: FALSE]]; }; BuildActionAreaLine: PROC [ggData: GGData, startX: CARD] RETURNS [Viewer] = { height1: CARDINAL = buttonAlign; buttonHolder: Viewer _ GetButtonHolder[parent: ggData.controls.topper, 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.controls.biScroller]; tV _ BiScrollers.CreateReset[ [parent: buttonHolder, wx: tV.wx+tV.ww+entryHSpace, wy: height1, border: FALSE], ggData.controls.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]; }; 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]]; }; BuildControlPanel: PROC [parent: Viewer, startHeight: CARD] = { MakeFromToEntry: PROC [string: Rope.ROPE, parent: Viewer, height: CARD, fromAction: LIST OF REF ANY, toAction: LIST OF REF ANY, fromOn: BOOL _ FALSE, toOn: BOOL _ FALSE] 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, fromOn, FillGlobalTwoState]]]]; fromState _ globalTwoState; [] _ AtomButtons.BuildButtonLine[container: parent, x: column2 + toBoxOffset, y: height, clientData: parent.data, handleProc: GGUserInput.EventNotify, entries: LIST [ [twoState[" ", toAction, toOn, 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["Search", LIST[LIST[$MatchSetFrom, $ClearAll], LIST[$MatchSetFrom, $ClearAll], LIST[$MatchSetFrom, $SetAll]]]]]]; [] _ AtomButtons.BuildButtonLine[container: parent, x: column2-15, y: height, clientData: parent.data, handleProc: GGUserInput.EventNotify, entries: LIST[ [button["Replace", LIST[LIST[$MatchSetTo, $ClearAll], LIST[$MatchSetTo, $ClearAll], LIST[$MatchSetTo, $SetAll]]]]]]; height _ height + entryHeight + lowerMargin; [matchData.from.shape, matchData.to.shape, height] _ MakeFromToEntry["Shape", parent, height, LIST[$MatchToggle, $FromShape], LIST[$MatchToggle, $ToShape], TRUE, TRUE]; [matchData.from.class, matchData.to.class, height] _ MakeFromToEntry["Object Class", parent, height, LIST[$MatchToggle, $FromClass], LIST[$MatchToggle, $ToClass], TRUE, TRUE]; [matchData.from.type, matchData.to.type, height] _ MakeFromToEntry["Curve Type", parent, height, LIST[$MatchToggle, $FromType], LIST[$MatchToggle, $ToType], TRUE, TRUE]; [matchData.from.fillColor, matchData.to.fillColor, height] _ MakeFromToEntry["Area color", parent, height, LIST[$MatchToggle, $FromFillColor], LIST[$MatchToggle, $ToFillColor], FALSE, TRUE]; [matchData.from.color, matchData.to.color, height] _ MakeFromToEntry["Line color", parent, height, LIST[$MatchToggle, $FromColor], LIST[$MatchToggle, $ToColor], FALSE, TRUE]; [matchData.from.width, matchData.to.width, height] _ MakeFromToEntry["Line width", parent, height, LIST[$MatchToggle, $FromWidth], LIST[$MatchToggle, $ToWidth], FALSE, TRUE]; [matchData.from.dash, matchData.to.dash, height] _ MakeFromToEntry["Line dashes", parent, height, LIST[$MatchToggle, $FromDash], LIST[$MatchToggle, $ToDash], FALSE, TRUE]; [matchData.from.joints, matchData.to.joints, height] _ MakeFromToEntry["Line joints", parent, height, LIST[$MatchToggle, $FromJoints], LIST[$MatchToggle, $ToJoints], FALSE, TRUE]; [matchData.from.ends, matchData.to.ends, height] _ MakeFromToEntry["Line ends", parent, height, LIST[$MatchToggle, $FromEnds], LIST[$MatchToggle, $ToEnds], FALSE, TRUE]; [matchData.from.string, matchData.to.string, height] _ MakeFromToEntry["Text string", parent, height, LIST[$MatchToggle, $FromString], LIST[$MatchToggle, $ToString], FALSE, TRUE]; [matchData.from.font, matchData.to.font, height] _ MakeFromToEntry["Text font", parent, height, LIST[$MatchToggle, $FromFont], LIST[$MatchToggle, $ToFont], FALSE, TRUE]; [matchData.from.fontTform, matchData.to.fontTform, height] _ MakeFromToEntry["Text font & transform", parent, height, LIST[$MatchToggle, $FromFontTform], LIST[$MatchToggle, $ToFontTform], FALSE, TRUE]; BuildRightControlPanel[parent, startHeight]; }; BuildRightControlPanel: PROC [parent: Viewer, startHeight: CARD] = { <> nextX: CARD; height: CARD _ startHeight - buttonAlign; <> [] _ AtomButtons.BuildButtonLine [parent, column3, height, parent.data, GGUserInput.UnQueuedEventNotify, LIST[ [button["Find", 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.levelButton _ AtomButtons.BuildEnumTypeSelection[viewer: parent, x: column3, y: height, maxWidth: 0, clientData: parent.data, handleProc: GGUserInput.EventNotify, title: "Granularity:", default: "Cluster Level", borderOnButtons: TRUE, style: flipThru, allInOneRow: TRUE, buttonNames: LIST["Anywhere", "Trajectory Level", "Cluster Level"], atom: $MatchLevelChange]; matchData.matchLevel _ clusterLevel; 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[ [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; 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.searchOpButton _ AtomButtons.BuildEnumTypeSelection[viewer: parent, x: column3, y: height, maxWidth: 0, clientData: parent.data, handleProc: GGUserInput.EventNotify, title: "SearchOp:", default: "Conjunction", borderOnButtons: TRUE, style: flipThru, allInOneRow: TRUE, buttonNames: LIST["Disjunction", "Conjunction"], atom: $MatchSearchOpChange]; matchData.searchOp _ conjunction; 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]]]]]]; }; 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;}; 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]; }; GGExtremaProc: PROC [clientData: REF ANY, direction: Geom2D.Vec] RETURNS [min, max: Geom2D.Vec] = { <> 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] = { <> ggData: GGData ~ NARROW[BiScrollers.ClientDataOfViewer[self]]; sanityCheck: BOOL[TRUE..TRUE] ~ (ggData.controls.actionArea=self); clientToViewer: Imager.Transformation ~ GGState.GetBiScrollersTransform[ggData]; paintAction: ATOM _ ggData.refresh.paintAction; IF whatChanged=NIL THEN { <> GGRefresh.SetScreen[ggData, self.cw, self.ch, context]; paintAction _ $ViewersPaintEntireScene; <> IF ggData.refresh.clientToViewer#NIL AND ImagerTransformation.Equal[clientToViewer, ggData.refresh.clientToViewer] THEN paintAction _ $ViewersPaintAllPlanes; } ELSE IF paintAction=$PaintEntireViewer THEN { <> GGRefresh.SetScreen[ggData, self.cw, self.ch, context]; paintAction _ $PaintEntireScene; }; GGRefresh.ActionAreaPaint[screen: context, whatHasChanged: paintAction, ggData: ggData, handleViewerAbort: (whatChanged=NIL)]; ggData.refresh.clientToViewer _ clientToViewer; -- for next time around }; <> 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 _ FALSE] = { 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, "Cluster Level"] => matchData.matchLevel _ clusterLevel; 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; }; 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]; }; 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], 1.0]; }; ToleranceButtonUpdate: AtomButtonsTypes.UpdateGraphicsButtonProc = { matchData.toleranceButton _ stateInfo; }; 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.controls.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.controls.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.controls.biScroller].clientToViewer; tbBox _ ImagerTransformation.TransformRectangle[cToV, GGBoundBox.RectangleFromBoundBox[bBox] ]; vBox _ BiScrollers.ViewportBox[ggData.controls.biScroller]; tvBox _ ImagerTransformation.TransformRectangle[cToV, vBox]; BiScrollers.BoxScale[bs: ggData.controls.biScroller, from: tbBox, to: tvBox]; <> }; }; Fetch: GGUserInput.UserInputProc = { theirScene: Scene _ NIL; ourScene: Scene _ ggData.scene; newSlices: LIST OF Slice; <> theirData: GGData _ GetGGInputFocus[]; IF theirData = NIL THEN RETURN ELSE theirScene _ theirData.scene; newSlices _ GGScene.CopySelectedParts[fromScene: theirScene, toScene: ourScene]; GGAlign.UpdateBagsForNewSlices[newSlices, ggData]; ggData.refresh.startBoundBox _ GGScene.BoundBoxOfSelected[ggData.scene, normal]; GGWindow.RestoreScreenAndInvariants[paintAction: $FinishedDragging, ggData: ggData, remake: sceneBag, edited: TRUE, okToSkipCapture: FALSE]; }; SearchEvent: GGUserInput.UserInputProc = { found: BOOL _ FALSE; toSearch: GGData _ GetGGInputFocus[]; IF toSearch#NIL THEN { Feedback.Append[GetToData[].router, oneLiner, $Feedback, "Finding ..."]; found _ Match.Search[toSearch, event, TRUE]; }; IF found THEN Feedback.Append[GetToData[].router, oneLiner, $Feedback, "Match found."] ELSE ErrorFeedback["No match found."]; }; YesEvent: GGUserInput.UserInputProc = { success: BOOL _ Match.ReplaceOperation[refresh: TRUE]; IF success THEN SearchEvent[NIL, event]; }; StartOps: GGUserInput.UserInputProc = { searchState: Match.SearchState _ Match.GetSearchState[]; searchState.macroOps _ NIL; searchState.macroOn _ TRUE; [] _ SlackProcess.RegisterLogger[ggData.slackHandle, SaveOps]; [] _ SlackProcess.EnableSessionLogging[ggData.slackHandle]; Feedback.Append[GetToData[].router, oneLiner, $Feedback, "Operation recording started ..."]; }; SaveOps: SlackProcess.LoggingProc = { <> <> event: LIST OF REF _ NARROW[inputAction]; toData: GGData _ GetToData[]; searchState: SearchState _ Match.GetSearchState[]; IF toData = clientData AND event.first # $MatchEndOps THEN { -- ie, event occurred in the To viewer, and it wasn't the $MatchEndOps atom searchState.macroOps _ CONS[event, searchState.macroOps]; }; }; EndOps: GGUserInput.UserInputProc = { searchState: Match.SearchState _ Match.GetSearchState[]; IF NOT searchState.macroOn THEN { ErrorFeedback["Must do StartOps before EndOps!"]; RETURN; }; searchState.macroOn _ FALSE; searchState.macroOps _ NARROW[GList.DReverse[searchState.macroOps]]; --order ops temporally [] _ SlackProcess.DisableSessionLogging[ggData.slackHandle]; [] _ SlackProcess.RegisterLogger[ggData.slackHandle, GGSessionLog.EnterAction]; -- re-install old logging function Feedback.Append[GetToData[].router, oneLiner, $Feedback, "Operation recording stopped."]; }; <<>> <> GetMatchData: PUBLIC PROC RETURNS [MatchData] = { RETURN [matchData]; }; GetGGInputFocus: PUBLIC PROC RETURNS [theirData: GGData _ NIL] = { inputFocus: InputFocus.Focus _ InputFocus.GetInputFocus[]; IF inputFocus = NIL OR inputFocus.owner = NIL OR inputFocus.owner.class.flavor # $ActionArea THEN ErrorFeedback["Place input focus in a Gargoyle viewer."] ELSE theirData _ NARROW[BiScrollers.ClientDataOf[NARROW[inputFocus.owner.data, BiScrollers.BiScroller]], GGData]; }; GetFromData: PUBLIC PROC RETURNS [GGData] = { RETURN[fromData]; }; GetToData: PUBLIC PROC RETURNS [GGData] = { RETURN[toData]; }; GetGrepData: PUBLIC PROC RETURNS [GGData] = { RETURN[grepData]; }; 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 _ FALSE] = { contextSensitive _ AtomButtons.GetBinaryState[matchData.contextSensitive]; }; <> 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 CreateTool[]; -- MatchContainerInit will be called by Viewers }; MatchInit: PROC = { actionAreaClass _ 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 ]]; matchData _ NewMatchDataObj[]; -- global match data matchToolIcon _ Icons.NewIconFromFile["Gargoyle.icons", 4]; -- shares Icon file with Gargoyle matchContainerClass _ NEW[ViewerClasses.ViewerClassRec _ [ init: MatchContainerInit, adjust: MatchContainerAdjust, topDownCoordSys: TRUE, icon: matchToolIcon ]]; ViewerOps.RegisterViewerClass[$MatchContainer, matchContainerClass]; -- plug in to Viewers RegisterMatchActions[]; Commander.Register["MatchTool", MatchCommand]; }; <<>> <> NewMatchDataObj: PUBLIC PROC RETURNS [matchData: MatchData] = { matchData _ NEW[MatchDataObj]; matchData.from _ NEW[ChoiceDataObj]; matchData.to _ NEW[ChoiceDataObj]; }; <> <> <> <> <> <> <> <<}>> <> <> <> <<};>> <<}>> <> <<};>> <<>> <> <<};>> <<>> FindAGargoyleViewer: PROC RETURNS [ggData: GGData _ NIL] = { IsGargoyleViewer: PROC [v: Viewer] RETURNS [gD: GGData _ NIL] = { ref: REF _ NIL; IF v=NIL OR v.data=NIL THEN RETURN; IF BiScrollers.IsBiScroller[v.data] THEN { biScroller: BiScrollers.BiScroller _ NARROW[v.data]; ref _ BiScrollers.ClientDataOf[biScroller]; } ELSE IF MJSContainers.IsMJSContainer[v] THEN { ref _ MJSContainers.GetClientData[v]; }; IF ref#NIL THEN WITH ref SELECT FROM gd: GGData => gD _ NARROW[gd]; ENDCASE; }; SomeGargoyleViewer: ViewerOps.EnumProc = { IF (ggData _ IsGargoyleViewer[v])#NIL THEN RETURN[FALSE] ELSE RETURN[TRUE]; }; ViewerOps.EnumerateViewers[SomeGargoyleViewer]; }; RegisterAction: PROC [atom: ATOM, eventProc: GGUserInput.UserInputProc] = { GGUserInput.RegisterAction[atom: atom, eventProc: eventProc, argType: none, causeMouseEventsToComplete: TRUE, ensureUnique: FALSE]; }; RegisterMatchActions: PROC = { RegisterAction[atom: $MatchFetch, eventProc: Fetch]; -- do the fetch RegisterAction[atom: $MatchSetFrom, eventProc: SetFromColumn]; -- turn on all buttons RegisterAction[atom: $MatchSetTo, eventProc: SetToColumn]; -- turn on all buttons RegisterAction[atom: $MatchToggle, eventProc: ToggleFromOrTo]; -- toggle RegisterAction[atom: $MatchSearch, eventProc: SearchEvent]; -- do search RegisterAction[atom: $MatchYes, eventProc: YesEvent]; -- do yes RegisterAction[atom: $MatchChangeAll, eventProc: Match.ChangeAll]; -- do ChangeAll RegisterAction[atom: $MatchActionChange, eventProc: MatchActionChange]; -- flip thru RegisterAction[atom: $MatchLevelChange, eventProc: MatchLevelChange]; -- flip thru RegisterAction[atom: $MatchSearchOpChange, eventProc: MatchSearchOpChange]; -- flip thru RegisterAction[atom: $MatchToleranceChange, eventProc: MatchToleranceChange]; -- adjust RegisterAction[atom: $MatchExact, eventProc: MatchExact]; -- toggle RegisterAction[atom: $MatchStartOps, eventProc: StartOps]; -- start a macro RegisterAction[atom: $MatchEndOps, eventProc: EndOps]; -- end a macro 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 RegisterAction[atom: $MatchNextFile, eventProc: MatchGrep.NextFile]; -- Edit the last gargoyle file found with grep }; MatchInit[]; END.