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. ˆMatchViewerImpl.mesa Contents: Builds the MatchTool viewer and implements the user interface functions for toggling buttons. Maintains the MatchTool user interface state. BuildControlPanel, BuildRightControlPanel, and RegisterMatchActions are interesting places to look. Copyright Ó 1987 by Xerox Corporation. All rights reserved. Kurlander, September 5, 1987 4:36:00 pm PDT Bier, May 27, 1992 11:24 am PDT Pier, April 17, 1990 1:17 pm PDT Spreitze, May 7, 1990 12:01 pm PDT Global MatchTool Data. At the moment, there is only one MatchTool in the world. Building a MatchTool Viewer (like GGWindow) PROC [ggData: GGData, request: REF _ NIL, bounds: BoundBoxObj _ infiniteRect, clientData: REF _ NIL]; GGInMMMImpl.Paint can be used in place of this routine when Gargoyle is embedded in MMM. Builds the GGData for the MatchTool From and To viewers. outer is the MatchTool Container. Also used for MatchTool grepData. REMOVED IMPLICIT SHARING OF ALL initial ggData via hostData. If you want MatchTool to copy values, they must be explicitly copied to the new ggData HERE. ggData _ NEW[GGDataObj _ matchData.hostData^]; -- copy hostData values. SEE GGWindowImpl.CreateGGData for up-to-date initialization of ggData. Code in BOLDFACE indicates different from corresponding code in GGWindowImpl.CreateGGData. Working Directory Scene and Interface Objects Input Handling Selections Hit Testing Refresh Behavior Debugging Creates a single feedback line for two action areas (represented by data1 and data2) and the grepData Create an action area, complete with handy Viewer-Control line only initial values for ww and wh. They are constrained below Makes a simple container to hold some buttons CHARACTERISTICS FROM/TO control section FIND/YES/NO/CHANGEALL, etc... Yes and ChangeAll must be unqueued so they don't cause SlackProcess monitor deadlock on doOps playback This proc is required by BiScrollers to return the extremes of the displayed data ViewerClasses.PaintProc. whatChanged is a GGData. self is an ActionArea. window scrolled or changed size or moved to different display If we are reopening the window we can take advantage of the fact that the sandwich may still be valid from when we closed the window. We detect this by comparing the new BiScrollers clientToViewer transform with the one we saved last time we came through here. If they are the same, we assume we can use the old sandwich. DKW: why is $PaintEntireViewer different from $PaintEntireScene? Why is it necessary to reconfigure the sandwich here? Handling MatchTool Button Presses (like GGEvent) Essentially GGMenusImpl.GravityExtentRepain Change scale and offsets so that client data previously in `from' covers as much as possible of `to' (from and to in viewer coords). Get Scene from InputFocus' Viewer PROC [clientData: REF, inputAction: REF]; This LoggingProc is initiated by StartOps, and terminated by EndOps. Save the event at the front of searchState.macroOps. MatchTool User Interface State (like GGState) Initialization (like GGWindow and GGEventImpl*) Utilities FindAGargoyleViewer: PROC RETURNS [ggData: GGData _ NIL] = { IsGargoyleViewer: ViewerOps.EnumProc = { IF Rope.Equal["Gargoyle", Rope.Substr[v.name, 0, 8]] THEN { IF BiScrollers.IsBiScroller[v.data] THEN { biScroller: BiScrollers.BiScroller _ NARROW[v.data]; ggData _ NARROW[BiScrollers.ClientDataOf[biScroller]]; RETURN[FALSE]; -- terminate enumeration } ELSE IF MJSContainers.IsMJSContainer[v] THEN { ggData _ NARROW[MJSContainers.GetClientData[v]]; RETURN[FALSE]; -- terminate enumeration }; } ELSE RETURN[TRUE]; -- continue enumerating }; ViewerOps.EnumerateViewers[IsGargoyleViewer]; -- look for a viewer whose name starts with "Gargoyle" }; Ê&2˜Icode™šÏnœô™üK™˜UKšœ žœŸJ˜aKšœžœŸ5˜OKšœ žœŸ1˜HKšœ/žœŸ˜M—K™šÏbP™PK˜K˜K˜%K˜KšœŸ!˜7Kšœ"Ÿ˜3Kšœ/˜/Kšœ˜Kšœ˜Kšœ#˜#—Ihead1™+š œžœžœžœ˜3K˜K˜8K˜&K˜K˜—š œžœ˜šœ5˜5šœ˜Kšœžœ˜ Kšœžœ˜ Kšœ˜Kšœ ž˜Kšœ˜—KšœžœŸL˜Z—Kšœ˜K˜—šœ˜.Kšœ  œŸ˜9Kšœ  œŸ˜5Kšœ  œŸ1˜TK˜/Kšœ.Ÿ˜IKšœ*Ÿ˜EKšœ-Ÿ˜HKšœ)Ÿ˜DK˜YKšœžœŸ˜9Kšœžœ˜Kšœžœ˜!K˜K˜—š œ˜$Kš žœžœžœ2žœžœ™eK™XKšœjžœ˜qK˜K˜—šœžœžœ˜CKšœ[™[Kšœ!™!K™K™šKšœ žœ$Ÿ™HK™KšœF™FKš œJ™ZKšœ%˜%K˜Kšœ žœ Ÿ6˜OKšœžœ˜#šœžœ˜(Kšœ#žœ˜(KšœE˜EKšœE˜E—Kšœžœ˜šœžœ˜!Kšœ9˜9Kšœ1žœ˜6KšœJžœ˜OKšœžœ˜K˜—Kš H˜HK˜š ˜KšœGžœ˜MKšœ7˜7—Kš ™Kš œ#˜6KšœJ˜JKš ™Kšœ˜Kšœžœ ˜Kšœ*˜*Kšœ!˜!Kšœžœ ˜Kš ™Kšœ.˜.Kšœ#˜#šœžœžœ˜"Kš  œ˜ KšœŸ"˜4—Kš  ™ Kšœ˜Kšœ˜KšœL˜LKš  ™ Kš Ðbk ,Ðbc:˜zKšœ7˜7Kšœ:˜:Kšœ5˜5Kšœ8˜8Kšœ3˜3Kšœ6˜6Kšœ?˜?Kš ™Kšœžœ˜%Kšœ!Ÿ˜7Kšœ9˜9Kšœ5˜5Kšœ4˜4Kšœ4˜4Kšœ5˜5Kšœ>˜>Kš ™KšœžœŸ7˜XKšœQ˜QKš -¢"˜OKš  ™ Kš ¡ >¢%˜uK˜šœžœ$˜9Kšœ˜Kšœ˜Kšœ,˜,Kšœ˜Kšœ˜—K˜Kš 4˜4Kš ˜Kšœ˜K˜—šœžœ%˜=K™eK˜K˜.Kšœ+Ÿ-˜XKšœ1Ÿ ˜QK˜K˜—Iprocšœ žœŸ/˜Kšœžœ˜,šœžœižœ˜xKšœ¶˜¶K˜—Kšœ,˜,K˜K˜—šœ˜0Kšœžœ ˜$Kšœ5˜5Kšœ*žœžœŸ˜SKšœ*žœžœC˜xKšœ2žœA˜wKšœ˜Kšœ&˜&K˜K˜—šœžœ!žœ ˜JK™>šœE˜EKšœ˜šœ˜Kšœ˜Kšœ˜Kšœ Ÿ˜KšœŸ˜(Kšœ>™>Kšœ ˜ Kšœžœ˜ Kšœ žœ˜—Kšœž˜ Kšœ˜—Kšœ1žœ˜7Kšœ ˜ Kšœ=Ÿ˜\K˜1Kšžœžœ˜#Kšœ˜K˜—šœžœžœžœ ˜MKšœ žœ˜ Kšœf˜fš œžœUžœžœžœžœžœ˜¢K˜—Kšœgžœ˜‹Kšœgžœ˜‹Kšœožœ/žœ˜©Kšžœ˜K˜K˜—š œžœžœ žœžœ ˜iK™-Kšžœžœ˜Kšžœbžœžœ žœ˜’K˜K˜—K˜šœžœžœ˜?š$œžœžœžœžœžœžœžœ žœžœžœžœ žœžœžœžœžœ7žœ˜ñKšœSžœ˜[šœ¢žœ˜¨Kšœ;˜;—K˜šœ žœ˜¦Kšœ7˜7—K˜K˜/K˜—Kšžœžœžœ™'Kšœžœ˜Kšœ^žœ˜f•StartOfExpansionë[container: ViewerClasses.Viewer, x: NAT, y: NAT, clientData: REF ANY, handleProc: AtomButtonsTypes.HandleButtonProc, entries: LIST OF AtomButtons.ButtonLineEntry, horizontalSpace: INTEGER _ 2, lineHeight: INTEGER _ 15]šœ’žœ˜—Kš œžœžœžœžœ˜y—–ë[container: ViewerClasses.Viewer, x: NAT, y: NAT, clientData: REF ANY, handleProc: AtomButtonsTypes.HandleButtonProc, entries: LIST OF AtomButtons.ButtonLineEntry, horizontalSpace: INTEGER _ 2, lineHeight: INTEGER _ 15]šœ•žœ˜šKš œžœžœžœžœ˜t—K˜,Kš œF œžœžœžœžœ˜¨Kš œF  œžœžœžœžœ˜¯Kš œD  œžœžœžœžœ˜©Kš œN  œžœ žœžœžœ˜¾Kš œF  œžœžœžœžœ˜®Kš œF  œžœžœžœžœ˜®Kš œD  œžœžœžœžœ˜«Kš œH  œžœžœžœžœ˜³Kš œD  œžœžœžœžœ˜©Kš œH  œžœžœžœžœ˜³Kš œD  œžœžœžœžœ˜©Kš œN œžœ žœžœžœ˜ÉK˜,K˜K˜—šœžœžœ˜DKšžœžœžœž œ™Kšœžœ˜ Kšœžœ˜)K™fšœjžœ˜oKš œ  œžœžœžœ žœ ˜Kš œ  œžœžœžœžœ˜uKš œ  œžœžœžœ žœ ˜}Kš œ   œžœžœ!žœ#žœ%˜—K˜,Kšœµ  œ/žœ žœžœ œ œ  œ˜öKšœ$˜$K˜,–Š[viewer: ViewerClasses.Viewer, x: NAT _ 0, y: NAT _ 0, clientData: REF ANY, handleProc: AtomButtonsTypes.HandleButtonProc, name: ROPE, border: BOOL _ TRUE, init: AtomButtonsTypes.StateType _ off, action: LIST OF REF ANY, lineHeight: INTEGER _ 15]šœ•žœ˜šKšœ   œžœžœ˜TKšœ  œžœžœ˜M—K˜,–Š[viewer: ViewerClasses.Viewer, x: NAT _ 0, y: NAT _ 0, clientData: REF ANY, handleProc: AtomButtonsTypes.HandleButtonProc, name: ROPE, border: BOOL _ TRUE, init: AtomButtonsTypes.StateType _ off, action: LIST OF REF ANY, lineHeight: INTEGER _ 15]šœ•žœ˜šKšœ  œžœžœ˜JKšœ  œžœ#žœ˜e—K˜,šœ•žœ˜šKšœ  œžœžœ˜8Kšœ  œ˜—K–Ð[container: ViewerClasses.Viewer, x: INTEGER, y: INTEGER, w: INTEGER, h: INTEGER, clientData: REF ANY, choices: AtomButtonsTypes.GBChoiceList, handleProc: AtomButtonsTypes.HandleButtonProc, repaintProc: AtomButtonsTypes.RepaintProc, buttonData: REF ANY _ NIL, updateProc: AtomButtonsTypes.UpdateGraphicsButtonProc]š œŒžœžœ'žœ*žœ{žœ7˜¤K˜,Kš œ¹ œ,žœ žœžœ  œ  œ˜áK˜ K˜,K–ø[viewer: ViewerClasses.Viewer, x: NAT, y: NAT, maxWidth: NAT, clientData: REF ANY, handleProc: AtomButtonsTypes.HandleButtonProc, title: ROPE, default: ROPE, borderOnButtons: BOOL, style: AtomButtonsTypes.StyleChoice, allInOneRow: BOOL, buttonNames: AtomButtonsTypes.ButtonList, atom: ATOM, horizontalSpace: INTEGER _ 2, lineHeight: INTEGER _ 15]š œ¸ œ-žœ žœžœ  œ  œ ˜äK˜!K˜,šœ•žœ˜šKš œ  œžœžœžœžœ˜]Kš œ  œžœžœžœžœ˜W—K˜,šœ•žœ˜›Kš œ  œžœžœžœžœ˜UKš œ  œžœžœžœžœ˜_—K˜K˜—Kšœ>˜PKš œD˜MKšœE˜TKš œB˜NKš œB˜NKš œC˜PKšœD˜RKšœJ˜^K˜šœ˜2Kšœ žœ$˜1Kšœ žœ$˜1KšžœžœžœLžœ˜kKšžœ žœžœLžœ˜iKšžœ žœžœPžœ˜mKšžœ žœžœFžœ˜aK˜K˜—š Ðbn œžœžœžœžœ˜cK™QKšœ˜Kšœžœ ˜$Kšœg˜gKš œžœžœžœTžœ˜‡Kšœ9˜9K˜K˜—šœžœ6žœžœ žœžœžœžœ˜ƒKšœJ™JKšœžœ'˜>Kšœ žœžœžœ&˜BKšœP˜PKšœ žœ˜/šžœ žœžœ˜Kšœ=™=Kšœ7˜7Kšœ'˜'K™ÃKšžœžœžœKžœ&˜K˜—šžœžœ žœ˜-Kšžœt™wKšœ7˜7Kšœ ˜ K˜—Kšœxžœ˜~Kšœ0Ÿ˜GKšœ˜—L™0š œ˜,K˜6K˜K˜—š œ˜*K˜4K˜K˜—š œžœ)žœžœ˜IK˜0K˜/K˜0K˜0K˜4K˜1K˜/K˜4K˜/K˜1K˜/K˜0K˜K˜—šœ˜-šœžœ˜K˜2K˜1K˜2K˜—šžœž˜K˜BK˜K˜@K˜K˜BK˜K˜BK˜>K˜JK˜FK˜DKšœ@˜@K˜@K˜K˜EK˜?K˜?K˜AK˜CK˜OKšžœ˜K˜K˜——šœ˜0Kšœ žœ˜Kšœžœ+˜JKšœ0˜0šžœžœž˜K˜EK˜KKšžœžœ˜—K˜K˜—šœ˜/Kšœ žœ˜Kšœžœ'˜FK˜,šžœžœž˜K˜@K˜IK˜IKšžœžœ˜—K˜K˜—šœ˜2Kšœ žœ˜Kšœžœ*˜IK˜/šžœžœž˜K˜DK˜DKšžœžœ˜—K˜K˜—šœ˜3Kšœžœ@˜ešžœ2žœŸ˜PKšœÏiœ˜-Kšžœ˜K˜—šžœž˜K˜?K˜:K˜AKšžœžœ˜—Kšœžœžœ+˜JK˜XK˜K˜—š œ˜)Kšœžœ@˜eK˜4K˜XK˜K˜—š œžœ"žœžœžœžœ˜oKšœ+™+K˜Kšœžœ˜ Kšœžœ˜Kšœ*˜*Kšœ˜Kšœ1˜1K˜K˜—šœ/˜DK˜&K˜K˜—K˜š œžœ˜(Kšœžœ ˜$K–f[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGSegmentTypes.SelectionClass _ normal]šœ1˜1Kšœc˜cK–œ[bs: BiScrollers.BiScroller, client: BiScrollers.Location, viewer: BiScrollers.Location, doX: BOOL _ TRUE, doY: BOOL _ TRUE, paint: BOOL _ TRUE]šžœ žœ¡žœ˜·Kšžœ»žœ˜ÅK˜K˜—šœ˜Kšœžœ ˜$K–f[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGSegmentTypes.SelectionClass _ normal]šœ ˜ Kšœ*˜*Kšœc˜cšžœžœ žœ˜KšœW˜WK–O[m: ImagerTransformation.Transformation, r: ImagerTransformation.Rectangle]šœ_˜_Kšœ;˜;Kšœ<˜<šœM˜MKšœ„™„—K˜—K˜K˜—šœ˜$Kšœžœ˜K˜Kšœ žœžœ˜KšÐbi!™!K˜&K˜Kšžœ žœžœž˜Kšžœ˜"Kšœ œ+˜PK˜2K˜PKšœnžœžœ˜ŒK˜K˜—š œ˜*Kšœžœžœ˜K˜%šžœ žœžœ˜KšœH˜HKšœ&žœ˜,K˜—KšžœžœJžœ"˜}K˜K˜—šœ˜'K–[scene: GGModelTypes.Scene]šœ žœ#žœ˜6Kšžœ žœ žœ ˜(K˜K˜—šœ˜'K˜8Kšœžœ˜Kšœžœ˜K˜>K˜;Kšœ\˜\K˜K˜—šœ  œ˜%Kšžœžœžœ™)K™zKš œžœžœžœžœ˜)K˜Kšœ2˜2šžœžœžœŸK˜ˆKšœžœ˜9K˜—K˜K˜—šœ˜%K˜8šžœžœžœ˜!Kšœ1˜1Kšžœ˜K˜—Kšœžœ˜Kšœžœ(Ÿ˜[K˜