<> <> <> <> <> <> <<>> <> DIRECTORY AtomButtons, AtomButtonsTypes, BasicTime, BiScrollers, CodeTimer, Commander, CommanderOps, CursorTypes, EBMesaLisp, Feedback, FeedbackOps, FileNames, FS, Geom2D, GGActive, GGAlign, GGBasicTypes, GGBoundBox, GGCaret, GGContainer, GGControlPanelTypes, GGCoreOps, GGDragTypes, GGEmbedTypes, GGEvent, GGHistory, GGHistoryTypes, GGInterfaceTypes, GGMeasure, GGMenu, GGModelTypes, GGMouseEvent, GGMultiGravity, GGOutline, GGRefresh, GGRefreshTypes, GGScene, GGSegmentTypes, GGSessionLog, GGSlice, GGState, GGStateExtras, GGStateTypes, GGUserInput, GGUserProfile, GGViewerOps, GGWindow, GGWindowExtras, Icons, Imager, ImagerFont, ImagerTransformation, IO, MultiCursors, Process, Rope, SlackProcess, TiogaButtons, TIPUser, UserProfile, Vectors2d, ViewerClasses, ViewerOps; GGWindowImpl: CEDAR PROGRAM IMPORTS AtomButtons, BasicTime, BiScrollers, CodeTimer, Commander, CommanderOps, EBMesaLisp, Feedback, FeedbackOps, FileNames, FS, Geom2D, GGActive, GGAlign, GGBoundBox, GGCaret, GGContainer, GGCoreOps, GGEvent, GGHistory, GGMeasure, GGMenu, GGMouseEvent, GGMultiGravity, GGOutline, GGRefresh, GGScene, GGSessionLog, GGSlice, GGState, GGStateExtras, GGUserInput, GGUserProfile, GGViewerOps, Icons, Imager, ImagerFont, ImagerTransformation, IO, MultiCursors, Process, Rope, SlackProcess, TIPUser, UserProfile, Vectors2d, ViewerOps EXPORTS GGWindow, GGWindowExtras, GGHistoryTypes, GGInterfaceTypes = BEGIN ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes EmbedDataObj: PUBLIC TYPE = GGEmbedTypes.EmbedDataObj; -- exported to GGInterfaceTypes StateDataObj: PUBLIC TYPE = GGStateTypes.StateDataObj; -- exported to GGInterfaceTypes DragDataObj: PUBLIC TYPE = GGDragTypes.DragDataObj; -- exported to GGInterfaceTypes CameraObj: TYPE = GGModelTypes.CameraObj; Caret: TYPE = REF CaretObj; CaretObj: TYPE = GGInterfaceTypes.CaretObj; DebugDataObj: TYPE = GGInterfaceTypes.DebugDataObj; Filters: TYPE = REF FiltersObj; FiltersObj: TYPE = GGInterfaceTypes.FiltersObj; ForegroundParts: TYPE = GGWindow.ForegroundParts; GGDataObj: TYPE = GGInterfaceTypes.GGDataObj; GGData: TYPE = GGInterfaceTypes.GGData; HistoryTool: TYPE = REF HistoryToolObj; HistoryToolObj: PUBLIC TYPE = GGHistory.HistoryToolObj; -- exported to GGHistoryTypes ImagerProc: TYPE = GGInterfaceTypes.ImagerProc; Point: TYPE = GGBasicTypes.Point; RefreshDataObj: PUBLIC TYPE = GGRefreshTypes.RefreshDataObj; ROPE: TYPE = Rope.ROPE; Scene: TYPE = REF SceneObj; SceneObj: TYPE = GGModelTypes.SceneObj; Segment: TYPE = GGSegmentTypes.Segment; Sequence: TYPE = GGModelTypes.Sequence; Slice: TYPE = GGModelTypes.Slice; Traj: TYPE = GGModelTypes.Traj; Transformation: TYPE = ImagerTransformation.Transformation; Viewer: TYPE = ViewerClasses.Viewer; buttonAlign: INTEGER ¬ 2; -- align popUp and standard buttons in first button line entryHeight: CARDINAL = 15; -- height of a line of items entryVSpace: CARDINAL = 2; -- vertical leading between lines entryHSpace: CARDINAL = 2; -- horizontal space between items on a line column1: CARDINAL = 200; -- horizontal space between margin and column 1; column2: CARDINAL = 250; -- horizontal space between margin and column 2. column3: CARDINAL = 500; -- horizontal space between margin and column 3; nameSize: CARDINAL = 140; smallNumberSize: CARDINAL = 45; numberSize: CARDINAL = 80; bigNumberSize: CARDINAL = 160; pointSize: CARDINAL = 160; globalEditedProcList: LIST OF EditedProcItem; <> RestoreScreenAndInvariants: PUBLIC PROC [paintAction: ATOM, ggData: GGData, remake: ForegroundParts ¬ triggerBag, edited: BOOL ¬ TRUE, selectionChanged: BOOL ¬ FALSE, okToSkipCapture: BOOL] = { <> <> CodeTimer.StartInt[$RestoreScreenAndInvariants, $Gargoyle]; IF ggData.aborted[bags] THEN { GGAlign.SetStaticBags[ggData]; GGRefresh.InvalidateForeground[ggData]; GGRefresh.InvalidateBackground[ggData]; ggData.aborted[bags] ¬ FALSE; } ELSE { SELECT remake FROM none => NULL; triggerBag => { GGAlign.SetStaticBags[ggData]; GGRefresh.InvalidateForeground[ggData]; }; triggerBagNotSceneBag => { GGAlign.SetStaticTriggerAndAlignBags[ggData]; GGRefresh.InvalidateForeground[ggData]; }; alignBag => { GGAlign.FlushAlignBag[ggData.hitTest.alignBag]; GGAlign.FillStaticAlignBag[ggData.hitTest.triggerBag, ggData.hitTest.sceneBag, ggData, NOT GGState.GetShowAlignments[ggData], ggData.hitTest.alignBag]; GGRefresh.InvalidateForeground[ggData]; }; bitMap => { GGRefresh.InvalidateForeground[ggData]; }; sceneBag => { GGAlign.FlushTriggerBag[ggData.hitTest.sceneBag]; GGAlign.FillStaticSceneBag[ggData.scene, ggData.hitTest.sceneBag]; }; ENDCASE => ERROR; }; <> IF edited OR selectionChanged THEN GGScene.SetLastEditedTime[ggData.scene, BasicTime.ExtendedNow[]]; IF edited THEN { <> FOR list: LIST OF EditedProcItem ¬ globalEditedProcList, list.rest UNTIL list = NIL DO IF ggData=list.first.ggData THEN list.first.editedProc[ggData, list.first.clientData]; ENDLOOP; }; GGRefresh.PaintInParent[ggData, paintAction]; -- calls PaintInViewer for normal GG use IF NOT okToSkipCapture THEN GGHistory.DoAdvanceCapture[ggData: ggData]; CodeTimer.StopInt[$RestoreScreenAndInvariants, $Gargoyle]; }; NewCaretPos: PUBLIC PROC [ggData: GGData] = { caret0, caret1, caret2Pos: Point; distance, angle, slope, lineDist: REAL; caret0 ¬ ggData.measure.caret0; caret1 ¬ ggData.measure.caret1; caret2Pos ¬ Vectors2d.Scale[GGCaret.GetPoint[ggData.caret], 1.0/ggData.hitTest.scaleUnit]; distance ¬ GGMeasure.DistanceBetweenPoints[caret1, caret2Pos]; -- distance in scaleUnits angle ¬ GGMeasure.SmallestAngleOfPoints[caret0, caret1, caret2Pos]; slope ¬ GGMeasure.SlopeOfPoints[caret1, caret2Pos]; lineDist ¬ GGMeasure.DistanceFromPointToLine[caret2Pos, caret0, caret1]; -- line distance in scaleUnits ggData.measure.caret2Value ¬ caret2Pos; GGViewerOps.SetPoint[ggData.controls.caret2, caret2Pos]; GGState.SetSlopeValue[ggData, slope]; GGState.SetAngleValue[ggData, angle]; GGState.SetRadiusValue[ggData, distance]; GGState.SetLineDistanceValue[ggData, lineDist]; }; SaveCaretPos: PUBLIC PROC [ggData: GGData] = { caret2Pos: Point ¬ Vectors2d.Scale[GGCaret.GetPoint[ggData.caret], 1.0/ggData.hitTest.scaleUnit]; ggData.measure.caret2Value ¬ caret2Pos; GGViewerOps.SetPoint[ggData.controls.caret2, caret2Pos]; ggData.measure.caret0 ¬ ggData.measure.caret1; ggData.measure.caret1 ¬ caret2Pos; }; <<>> <> OpenViewerOnFile: PROC [fileName: Rope.ROPE ¬ NIL, fancyPanel: BOOL ¬ FALSE] RETURNS [ggData: GGData] = { ggData ¬ CreateWindowAux[GGScene.CreateScene[], TRUE, FALSE, FileNames.CurrentWorkingDirectory[], fancyPanel]; -- create brand new GG window IF fileName#NIL AND ~Rope.Equal[fileName, ""] THEN GGEvent.Get[event: LIST[$Get, fileName], ggData: ggData]; -- tell GGEvent.Get to try for this file GGHistory.CapTool[ggData.controls.topper.name, ggData]; ViewerOps.PaintViewer[ggData.controls.topper, caption]; -- just to get the icon to appear ViewerOps.PaintViewer[ggData.controls.panel, caption]; -- just to get the icon to appear }; CreateChildViewer: PUBLIC PROC [ scene: Scene, wx, wy: INTEGER ¬ 0, ww, wh: INTEGER ¬ 0, cx, cy: INTEGER ¬ 0, cw, ch: INTEGER ¬ 0, parent: Viewer, workingDirectory: Rope.ROPE, clientData: REF ¬ NIL, paint: BOOL ¬ TRUE] RETURNS [viewer: Viewer, ggData: GGData] = { ggData ¬ CreateGGDataForViewer[parent, scene, workingDirectory]; ggData.controls.active ¬ TRUE; ggData.controls.biScroller ¬ BiScrollers.GetStyle[].CreateBiScroller[ class: actionAreaClass, info: [ parent: parent, name: "GargoyleChild", wx: wx, wy: wy, ww: ww, wh: wh, cx: cx, cy: cy, cw: cw, ch: ch, data: ggData, border: FALSE, scrollable: FALSE], paint: paint ]; ggData.controls.picture ¬ ggData.controls.topper ¬ ggData.controls.biScroller.QuaViewer[inner: FALSE]; ggData.controls.actionArea ¬ ggData.controls.biScroller.QuaViewer[inner: TRUE]; ggData.controls.actionArea.cursor ¬ last; -- "last" means that Viewers should not do cursor handling for us ggData.height ¬ 0; ggData.tipTable ¬ ggTipTable; ggData.controls.topper.newVersion ¬ FALSE; ggData.controls.panel.newVersion ¬ FALSE; ggData.controls.picture.newVersion ¬ FALSE; SetCursorLooks[ggData.hitTest.gravityType, ggData]; <<[] _ SlackProcess.EnableAborts[handle: ggData.slackHandle];>> GGEvent.OpenAutoScript[ggData, TRUE]; RegisterEditedProc[ggData, GGState.GGEdited, NIL]; RestoreScreenAndInvariants[paintAction: $None, ggData: ggData, remake: triggerBag, edited: FALSE, okToSkipCapture: FALSE]; viewer ¬ ggData.controls.picture; <> ggData.router ¬ Feedback.CreateRouter[]; FeedbackOps.SetMultiMessageWindow[ggData.router, TRUE, LIST[$Error, $Complaint]]; -- blink FeedbackOps.SetMultiMessageWindow[ggData.router, FALSE, LIST[$DuringMouse, $Feedback, $Warning, $Confirm, $Show, $Statistics]]; FeedbackOps.SetMultiTypescript[ggData.router, $Gargoyle, LIST[$Error, $Warning, $Show, $Typescript, $Complaint, $Statistics]]; ggData.embed.beingBorn ¬ FALSE; }; PaintInViewer: PUBLIC GGRefresh.PaintProc = { <> <> ViewerOps.PaintViewer[viewer: ggData.controls.actionArea, hint: client, whatChanged: ggData, clearClient: FALSE]; }; CreateGGData: PUBLIC PROC [scene: Scene, workingDirectory: Rope.ROPE] RETURNS [ggData: GGData] = { RETURN[CreateGGDataForViewer[NIL, scene, workingDirectory]]; }; CreateGGDataForViewer: PROC [outer: Viewer, scene: Scene, workingDirectory: Rope.ROPE, withNullDoc: BOOL _ TRUE] RETURNS [ggData: GGData] = { ggData ¬ NEW[GGDataObj]; ggData.controls ¬ NEW[ControlsObj]; IF withNullDoc THEN ggData.controls.controlPanel ¬ GGActive.NullDoc[ggData]; ggData.controlState ¬ NEW[StateDataObj]; ggData.controlState.doubleBuffer ¬ TRUE; ggData.controlState.useBackingMap ¬ 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, PaintInViewer, NIL]; GGStateExtras.RegisterViewportProc[ggData, GGStateExtras.DefaultViewport, NIL]; ggData.embed.beingBorn ¬ TRUE; ggData.refresh ¬ NEW[RefreshDataObj]; <> IF outer#NIL THEN { outer.newVersion ¬ FALSE; ggData.controls.topper ¬ ggData.controls.panel ¬ outer; }; <> GGStateExtras.SetWorkingDirectory[ggData, workingDirectory]; <> ggData.scene ¬ scene; ggData.caret ¬ GGCaret.Create[]; 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: GGUserProfile.GetDefaultHistorySize[], currentIndex: 0]; -- KAP July 7, 1988 <> ggData.slackHandle ¬ SlackProcess.Create[queueSize: 50, logSize: 50, optimizeProc: OptimizeQueue, loggingProc: GGSessionLog.EnterAction, abortProc: GGAbortProc, abortData: ggData, abortViewer: ggData.controls.actionArea]; [] ¬ SlackProcess.EnableAborts[handle: ggData.slackHandle]; <> ggData.drag.selectState ¬ none; ggData.drag.extendMode ¬ none; GGState.SetSelectionCycler[ggData, GGMultiGravity.EmptyCycler[[0.0, 0.0]] ]; <> ggData.hitTest ¬ NEW[FiltersObj]; 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[]; <> GGAlign.CreateLineTable[ggData]; ggData.refresh.startBoundBox ¬ NIL; -- this field is OBSOLETE and should never be referenced. KAP. June 22, 1992. 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.debug ¬ NEW[DebugDataObj]; ggData.debug.autoScriptNames ¬ GGCoreOps.NewRopeListt[]; <> ggData.defaults ¬ NEW[GGModelTypes.DefaultDataObj ¬ [ strokeColor: Imager.black, fillColor: GGOutline.fillColor, textColor: Imager.black, font: GGUserProfile.GetDefaultDefaultFont[], dropShadowColor: Imager.black ]]; }; CreateWindow: PUBLIC PROC [scene: Scene, iconic: BOOL, paint: BOOL, workingDirectory: Rope.ROPE] RETURNS [ggData: GGData] = { fancyPanel: BOOL ¬ UserProfile.Boolean[key: "Gargoyle.FancyPanel", default: TRUE]; RETURN[CreateWindowAux[scene, iconic, paint, workingDirectory, fancyPanel]]; }; CreateWindowAux: PROC [scene: Scene, iconic: BOOL, paint: BOOL, workingDirectory: Rope.ROPE, fancyPanel: BOOL ¬ FALSE] RETURNS [ggData: GGData] = { <> <> <> <<>> <> <> <> ggData ¬ CreateGGDataForViewer[outer: NIL, scene: scene, workingDirectory: workingDirectory, withNullDoc: FALSE]; ggData.controls.panel ¬ GGContainer.GGContainerCreate[ info: [ name: "GGPanel: Gargoyle", menu: NIL, data: ggData, iconic: iconic, column: left, scrollable: FALSE, icon: panelIconG ], paint: paint]; ggData.controls.biScroller ¬ BiScrollers.GetStyle[].CreateBiScroller[ class: actionAreaClass, info: [ parent: IF GGUserProfile.GetSeparateControlPanel[] THEN NIL ELSE ggData.controls.panel, name: "Gargoyle", menu: NIL, wx: 0, wy: 0, ww: 10, wh: 10, -- only dummy values for ww and wh for now data: ggData, iconic: TRUE, column: left, icon: noNameIconG, border: FALSE, scrollable: FALSE], paint: FALSE ]; ggData.controls.picture ¬ ggData.controls.topper ¬ ggData.controls.biScroller.QuaViewer[inner: FALSE]; ggData.controls.actionArea ¬ ggData.controls.biScroller.QuaViewer[inner: TRUE]; ggData.controls.actionArea.cursor ¬ last; -- "last" means that Viewers should not do cursor handling for us ggData.height ¬ 0; ggData.tipTable ¬ ggTipTable; GGMenu.BuildControlPanel[ggData, fancyPanel]; -- updates ggData.height IF NOT GGUserProfile.GetSeparateControlPanel[] THEN { -- panel and picture together GGContainer.ChildXBound[gargoyleContainer: ggData.controls.panel, child: ggData.controls.picture]; GGContainer.ChildYBound[gargoyleContainer: ggData.controls.panel, child: ggData.controls.picture]; ViewerOps.MoveViewer[ggData.controls.picture, 0, ggData.height, ggData.controls.panel.cw, ggData.controls.panel.ch-ggData.height, FALSE]; ggData.controls.topper ¬ ggData.controls.panel; ggData.controls.topper.icon ¬ noNameIconG; ggData.controls.topper.name ¬ "Gargoyle"; } ELSE { -- panel by itself thisViewer: AtomButtons.ButtonLineEntry ¬ [label[name: "Control", font: bigFont]]; noPicture: AtomButtons.ButtonLineEntry ¬ [label[name: "Panel", font: bigFont]]; ViewerOps.SetOpenHeight[ggData.controls.panel, ggData.height-entryVSpace]; [] ¬ AtomButtons.BuildButtonLine[container: ggData.controls.panel, x: 36, y: ggData.height, clientData: ggData, handleProc: GGUserInput.EventNotify, entries: LIST[thisViewer], horizontalSpace: entryHSpace, lineHeight: 144]; ggData.height ¬ ggData.height + 144; [] ¬ AtomButtons.BuildButtonLine[container: ggData.controls.panel, x: 36, y: ggData.height, clientData: ggData, handleProc: GGUserInput.EventNotify, entries: LIST[noPicture], horizontalSpace: entryHSpace, lineHeight: 72]; ggData.height ¬ ggData.height + 72; }; ggData.controls.topper.newVersion ¬ FALSE; ggData.controls.panel.newVersion ¬ FALSE; ggData.controls.picture.newVersion ¬ FALSE; SetCursorLooks[ggData.hitTest.gravityType, ggData]; <<[] _ SlackProcess.EnableAborts[handle: ggData.slackHandle];>> GGEvent.OpenAutoScript[ggData, TRUE]; RegisterEditedProc[ggData, GGState.GGEdited, NIL]; IF GGUserProfile.GetAutoOpenHistory[] THEN ggData.history.tool ¬ GGHistory.BuildTool["Gargoyle", ggData]; <> <<[] ¬ FeedbackOps.CreateNamedTypescript["Gargoyle Typescript", $Gargoyle, 120]; -- Bier, March 18, 1993>> ggData.embed.beingBorn ¬ FALSE; -- enable input handling }; ActiveInGGData: AtomButtons.InitTwoStateProc = { ggData: GGData ¬ NARROW[clientData]; ggData.controls.activeButton ¬ twoState; }; GGActionAreaPaint: PROC [self: Viewer, context: Imager.Context, whatChanged: REF, 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]; ipContext: BOOL ~ context.GetClass[]=$Interpress; -- special check. IP contexts should not be queued for painting even if whatChanged=NIL. KAP. August 14, 1992. IF whatChanged=NIL AND NOT ipContext THEN { -- window scrolled or changed size or changed displays action: ATOM ¬ $ViewersPaintEntireScene; GGRefresh.SetScreen[ggData, self.cw, self.ch, context]; -- resize sandwich, if needed <> IF ggData.refresh.clientToViewer#NIL AND ImagerTransformation.Equal[clientToViewer, ggData.refresh.clientToViewer] THEN action ¬ $ViewersPaintAllPlanes; GGUserInput.EventNotify[ggData, LIST[action]]; -- queue the paint action } ELSE { paintAction: ATOM ¬ IF ipContext THEN $PaintSceneNoBuffer ELSE ggData.refresh.paintAction; GGRefresh.ActionAreaPaint[screen: context, whatHasChanged: paintAction, ggData: ggData, handleViewerAbort: (whatChanged=NIL)]; ggData.refresh.clientToViewer ¬ clientToViewer; -- for next time around }; }; GGAbortProc: PROC [data: REF] = { -- called by SlackProcess when user signals for abort ggData: GGData ¬ NARROW[data]; Feedback.Append[ggData.router, oneLiner, $Feedback, "Queued operations Aborted"]; ggData.refresh.suppressRefresh ¬ FALSE; -- in case you killed FastPlayback ggData.refresh.suppressScreen ¬ FALSE; -- in case you killed FastPlayback ggData.aborted ¬ ALL[TRUE]; -- copies of aborted for all purposes }; <<>> RegisterEditedProc: PUBLIC PROC [ggData: GGData, editedProc: EditedProc, clientData: REF] = { <> editedProcItem: EditedProcItem ¬ NEW[EditedProcItemObj ¬ [ggData, editedProc, clientData]]; globalEditedProcList ¬ CONS[editedProcItem, globalEditedProcList]; }; ViewerToWorld: PUBLIC PROC [viewerPoint: Point, ggData: GGData] RETURNS [worldPoint: Point] = { vec: Imager.VEC; viewerToClient: Imager.Transformation ¬ GGState.GetBiScrollersTransforms[ggData].viewerToClient; vec ¬ ImagerTransformation.Transform[viewerToClient, [x: viewerPoint.x, y: viewerPoint.y]]; worldPoint ¬ [vec.x, vec.y]; }; WorldToViewer: PUBLIC PROC [worldPoint: Point, ggData: GGData] RETURNS [viewerPoint: Point] = { vec: Imager.VEC; clientToViewer: Imager.Transformation ¬ GGState.GetBiScrollersTransform[ggData]; vec ¬ ImagerTransformation.Transform[clientToViewer, [x: worldPoint.x, y: worldPoint.y]]; viewerPoint ¬ [vec.x, vec.y]; }; <> OptimizeQueue: SlackProcess.OptimizeProc = { <> <> atom, nextAtom: ATOM; action: LIST OF REF; IF actionsOnQueue < 2 THEN RETURN [0]; skipActions ¬ 0; FOR i: NAT IN [0..actionsOnQueue-2] DO action¬ NARROW[SlackProcess.GetQueueEntry[qeGen, i].inputAction]; atom ¬ NARROW[action.first]; action ¬ NARROW[SlackProcess.GetQueueEntry[qeGen, i+1].inputAction]; nextAtom ¬ NARROW[action.first]; IF (atom = $During OR atom = $OneScale) AND (nextAtom = $During OR nextAtom = $OneScale) THEN skipActions ¬ skipActions + 1 ELSE IF (atom = $OneScroll OR atom = $OneZoom) AND (nextAtom = $OneScroll OR nextAtom = $OneZoom) THEN skipActions ¬ skipActions + 1 ELSE RETURN; ENDLOOP; }; PUChoiceList: PROC [r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, r13, r14, r15, r16, r17, r18, r19: AtomButtons.PopUpChoice ¬ [] ] RETURNS [list: AtomButtons.PopUpChoices] = { OPEN AtomButtons; InnerCons: PROC[r: PopUpChoice] = {rList ¬ CONS[r, rList]; }; rList: PopUpChoices; IF r0.action#NIL THEN InnerCons[r0]; IF r1.action#NIL THEN InnerCons[r1]; IF r2.action#NIL THEN InnerCons[r2]; IF r3.action#NIL THEN InnerCons[r3]; IF r4.action#NIL THEN InnerCons[r4]; IF r5.action#NIL THEN InnerCons[r5]; IF r6.action#NIL THEN InnerCons[r6]; IF r7.action#NIL THEN InnerCons[r7]; IF r8.action#NIL THEN InnerCons[r8]; IF r9.action#NIL THEN InnerCons[r9]; IF r10.action#NIL THEN InnerCons[r10]; IF r11.action#NIL THEN InnerCons[r11]; IF r12.action#NIL THEN InnerCons[r12]; IF r13.action#NIL THEN InnerCons[r13]; IF r14.action#NIL THEN InnerCons[r14]; IF r15.action#NIL THEN InnerCons[r15]; IF r16.action#NIL THEN InnerCons[r16]; IF r17.action#NIL THEN InnerCons[r17]; IF r18.action#NIL THEN InnerCons[r18]; IF r19.action#NIL THEN InnerCons[r19]; FOR dummy: PopUpChoices ¬ rList, dummy.rest UNTIL dummy=NIL DO list ¬ CONS[dummy.first, list]; ENDLOOP; }; Choice: PROC [action: LIST OF REF, actionImage: Rope.ROPE, doc: Rope.ROPE, font: Imager.Font ¬ NIL] RETURNS [AtomButtons.PopUpChoice]= { RETURN[[action, actionImage, doc, font]]; }; List: PROC [ref1, ref2, ref3: REF ¬ NIL] RETURNS [LIST OF REF]= { RETURN[ SELECT TRUE FROM ref2=NIL => LIST[ref1], ref3=NIL => LIST[ref1, ref2], ENDCASE => LIST[ref1, ref2, ref3] ]; }; GGExtremaProc: PROC [clientData: REF, 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]; }; <> EditedProc: TYPE = GGWindow.EditedProc; EditedProcItem: TYPE = REF EditedProcItemObj; EditedProcItemObj: TYPE = RECORD [ ggData: GGData, editedProc: EditedProc, clientData: REF ]; <> SetCursorLooks: PUBLIC PROC [type: GGInterfaceTypes.GravityType, ggData: GGData, off: BOOL ¬ FALSE ] = { newCursor: ViewerClasses.CursorType ~ IF off THEN offCursor ELSE SELECT type FROM pointsPreferred => pointsPreferredCursor, linesPreferred, facesPreferred => linesPreferredCursor, ENDCASE => ERROR; ggData.controls.cursor ¬ newCursor; IF newCursor#MultiCursors.GetACursor[NIL] THEN MultiCursors.SetACursor[newCursor, NIL]; -- this code should make sure the cursor is in the Gargoyle action area before doing this. <> }; <> NewGGViewers: Commander.CommandProc = { <<[cmd: Handle] RETURNS [result: REF _ NIL, msg: Rope.ROPE _ NIL];>> nameList, args: LIST OF Rope.ROPE ¬ NIL; argLength: NAT ¬ 0; command: BOOL ¬ FALSE; commandRope: ROPE; fileOpened: BOOL ¬ FALSE; fancyPanel: BOOL ¬ UserProfile.Boolean[key: "Gargoyle.FancyPanel", default: TRUE]; [list: args, length: argLength] ¬ CommanderOps.ParseToList[cmd: cmd ! CommanderOps.Failed => CONTINUE; ]; IF args = NIL OR argLength < 1 THEN { [] ¬ OpenViewerOnFile[fileName: NIL, fancyPanel: fancyPanel]; RETURN}; IF argLength = 1 AND Rope.Equal[args.first, "-fancyPanel", FALSE] THEN {[] ¬ OpenViewerOnFile[fileName: NIL, fancyPanel: TRUE]; RETURN}; IF argLength = 1 AND Rope.Equal[args.first, "-~fancyPanel", FALSE] THEN {[] ¬ OpenViewerOnFile[fileName: NIL, fancyPanel: FALSE]; RETURN}; FOR rl: LIST OF Rope.ROPE ¬ args, rl.rest UNTIL rl = NIL DO --open a GGViewer on each file IF Rope.Equal[rl.first, "-fancyPanel", FALSE] THEN fancyPanel ¬ TRUE ELSE IF Rope.Equal[rl.first, "-~fancyPanel", FALSE] THEN fancyPanel ¬ FALSE ELSE IF Rope.Equal[rl.first, "-command", FALSE] THEN { command ¬ TRUE; commandRope ¬ NIL; } ELSE IF command THEN { command ¬ FALSE; commandRope ¬ Rope.Cat["(", rl.first, ")"]; <> } ELSE { ggData: GGData ¬ OpenViewerOnFile[fileName: FileNames.ResolveRelativePath[rl.first], fancyPanel: fancyPanel]; fileOpened ¬ TRUE; IF commandRope # NIL AND NOT Rope.Equal[commandRope, "()", FALSE] THEN { stream: IO.STREAM ¬ IO.RIS[commandRope]; val: REF ¬ EBMesaLisp.Parse[stream].val; event: LIST OF REF ¬ NARROW[val]; GGUserInput.BiScrollerInputNotify[ggData, event]; }; }; ENDLOOP; IF NOT fileOpened THEN { ggData: GGData ¬ OpenViewerOnFile[fileName: NIL, fancyPanel: fancyPanel]; IF commandRope # NIL AND NOT Rope.Equal[commandRope, "()", FALSE] THEN { stream: IO.STREAM ¬ IO.RIS[commandRope]; val: REF ¬ EBMesaLisp.Parse[stream].val; event: LIST OF REF ¬ NARROW[val]; GGUserInput.BiScrollerInputNotify[ggData, event]; }; }; }; FilenameMinusExtension: PUBLIC PROC [wholeName: ROPE] RETURNS [ROPE] ~ { fullFName: ROPE; cp: FS.ComponentPositions; [fullFName: fullFName, cp: cp] ¬ FS.ExpandName[wholeName]; RETURN[Rope.Substr[fullFName, 0, cp.base.start+cp.base.length]]; }; GGToIP: Commander.CommandProc = { EachFile: FS.NameProc = { <<[fullFName: ROPE] RETURNS [continue: BOOL]>> ipName: Rope.ROPE; Process.CheckForAbort[]; ipName ¬ FilenameMinusExtension[fullFName]; ipName ¬ Rope.Concat[ipName, ".ip"]; GGEvent.Get[ggData, LIST[$Get, fullFName]]; GGEvent.ToIP[ggData, LIST[$ToIP, ipName]]; continue ¬ TRUE; }; TryPattern: PROC [pattern: Rope.ROPE] = { ENABLE FS.Error => IF error.group # $bug THEN { IO.PutRope[out, " -- "]; IO.PutRope[out, error.explanation]; GO TO err}; pattern ¬ FileNames.ResolveRelativePath[pattern]; pattern ¬ FS.ExpandName[pattern].fullFName; IF NOT Rope.Match["*!*", pattern] THEN pattern ¬ Rope.Concat[pattern, "!h"]; FS.EnumerateForNames[pattern, EachFile]; EXITS err => {IO.PutRope[out, "\n"]; RETURN}; }; out: IO.STREAM ¬ cmd.out; argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd: cmd ! CommanderOps.Failed => {msg ¬ errorMsg; GO TO failed}]; ggData: GGData ¬ CreateWindow[GGScene.CreateScene[], TRUE, FALSE, FileNames.CurrentWorkingDirectory[]]; ViewerOps.PaintViewer[ggData.controls.panel, caption]; -- just to get the icon to appear ViewerOps.PaintViewer[ggData.controls.topper, caption]; -- just to get the icon to appear GGUserInput.EventNotify[ggData, LIST[$Typescript]]; FOR i: NAT IN [1..argv.argc) DO arg: Rope.ROPE = argv[i]; IF Rope.Length[arg] = 0 THEN LOOP; TryPattern[arg]; ENDLOOP; EXITS failed => {result ¬ $Failure}; }; GGIPToIP: Commander.CommandProc = { <> EachFile: FS.NameProc = { <<[fullFName: ROPE] RETURNS [continue: BOOL]>> newIPName: ROPE; Process.CheckForAbort[]; GGUserInput.EventNotify[ggData, LIST[$Clear]]; GGUserInput.EventNotify[ggData, LIST[$MergeIPEditable, fullFName]]; GGSessionLog.PlaybackFromFile[scriptName, ggData]; newIPName ¬ FilenameMinusExtension[fullFName]; newIPName ¬ Rope.Concat[newIPName, "-mod.ip"]; GGUserInput.EventNotify[ggData, LIST[$ToIP, newIPName]]; continue ¬ TRUE; }; TryPattern: PROC [pattern: Rope.ROPE] = { ENABLE FS.Error => IF error.group # $bug THEN { IO.PutRope[out, " -- "]; IO.PutRope[out, error.explanation]; GO TO err}; pattern ¬ FileNames.ResolveRelativePath[pattern]; pattern ¬ FS.ExpandName[pattern].fullFName; IF NOT Rope.Match["*!*", pattern] THEN pattern ¬ Rope.Concat[pattern, "!h"]; FS.EnumerateForNames[pattern, EachFile]; EXITS err => {IO.PutRope[out, "\n"]; RETURN}; }; out: IO.STREAM ¬ cmd.out; argv: CommanderOps.ArgumentVector ¬ CommanderOps.Parse[cmd: cmd ! CommanderOps.Failed => {msg ¬ errorMsg; GO TO failed}]; scriptName: Rope.ROPE ¬ argv[1]; ggData: GGData ¬ CreateWindow[GGScene.CreateScene[], TRUE, FALSE, FileNames.CurrentWorkingDirectory[]]; ViewerOps.PaintViewer[ggData.controls.panel, caption]; -- just to get the icon to appear ViewerOps.PaintViewer[ggData.controls.topper, caption]; -- just to get the icon to appear <> <> GGUserInput.EventNotify[ggData, LIST[$DisableRefresh]]; -- more kosher FOR i: NAT IN [2..argv.argc) DO arg: Rope.ROPE = argv[i]; IF Rope.Length[arg] = 0 THEN LOOP; TryPattern[arg]; ENDLOOP; GGUserInput.EventNotify[ggData, LIST[$EnableRefresh]]; GGUserInput.EventNotify[ggData, LIST[$Refresh]]; EXITS failed => {result ¬ $Failure}; }; GGBasicTransformProc: PROC [bs: BiScrollers.BiScroller] RETURNS [Transformation] = { <> height: REAL ¬ 11.0*72.0; width: REAL ¬ 8.5*72.0; actionArea: Viewer ¬ BiScrollers.QuaViewer[bs].child; ww: REAL ¬ actionArea.ww; wh: REAL ¬ actionArea.wh; transform: Transformation; transform ¬ ImagerTransformation.Translate[[(ww-width)/2.0, (wh-height)/2.0]]; RETURN[transform]; }; BSSaveProc: PRIVATE ViewerClasses.SaveProc = { <<[self: ViewerClasses.Viewer, force: BOOL _ FALSE]>> <> GGContainer.GargoyleContainerSave[self, force]; }; BSDestroyProc: PRIVATE ViewerClasses.DestroyProc = { <> <> IF ggData#NIL AND ggData.controls.topper=ggData.controls.picture AND ggData.controls.panel.destroyed THEN GGUserInput.EventNotify[ggData, LIST[$Destroy]]; -- frees much garbage>> <> }; BiScrollerInputNotify: BiScrollers.BSUserActionProc = { <> <> ggData: GGData ¬ NARROW[BiScrollers.ClientDataOf[bs]]; GGUserInput.BiScrollerInputNotify[ggData, input]; }; SetBeingBorn: PUBLIC PROC [ggData: GGData, beingBorn: BOOL ¬ FALSE] = { <> ggData.embed.beingBorn ¬ beingBorn; }; <<>> Init: PROC = { ggTipTable ¬ TIPUser.InstantiateNewTIPTable["Gargoyle.tip"]; -- used in ggData.tipTable actionAreaClass ¬ BiScrollers.GetStyle[].NewBiScrollerClass[[ flavor: $ActionArea, save: BSSaveProc, destroy: BSDestroyProc, extrema: GGExtremaProc, notify: GGUserInput.InputNotify, bsUserAction: BiScrollerInputNotify, paint: GGActionAreaPaint, tipTable: TIPUser.TransparentTIPTable[], -- the real tipTable is in ggData.parseInfo mayStretch: TRUE, -- OK to scale X and Y differently offsetsMustBeIntegers: TRUE, preferIntegerCoefficients: FALSE, vanilla: GGBasicTransformProc, --let the vanilla transform be the identity preserve: [X: 0.5, Y: 0.5] --this point stays fixed during scaling (viewer size change) ]]; popUpFont ¬ ImagerFont.Find["xerox/tiogafonts/helvetica10I", substituteQuietly]; bigFont ¬ ImagerFont.Scale[ImagerFont.Find["xerox/pressfonts/helvetica-brr", substituteQuietly], 72.0]; <> iconsFrom ¬ "/Cedar/Gargoyle/Gargoyle.icons"; -- KAP. July 20, 1992. InitIcons[]; BEGIN -- BuildCursors pointsPreferredArray: CursorTypes.CursorArray = [600B+1100B, 600B+1100B+2040B, 2040B+4020B, 4020B+10010B, 10010B+20004B, 20004B+40002B, 40002B+100001B, 40002B+100001B, 40002B+100001B, 40002B+100001B, 20004B+40002B, 10010B+20004B, 4020B+10010B, 2040B+4020B, 600B+1100B+2040B, 600B+1100B]; -- diamond linesPreferredArray: CursorTypes.CursorArray = [100001B+40002B, 40002B+20004B, 20004B+10010B, 10010B+4020B, 4020B+2040B, 2040B, 0, 0, 0, 0, 2040B, 4020B+2040B, 10010B+4020B, 20004B+10010B, 40002B+20004B, 100001B+40002B]; -- folded diamond offArray: CursorTypes.CursorArray = [177777B, 177777B, 140003B, 140003B, 140003B, 140003B, 140003B, 140003B, 140003B, 140003B, 140003B, 140003B, 140003B, 140003B, 177777B, 177777B]; -- square pointsPreferredCursor ¬ MultiCursors.NewCursor[bits: pointsPreferredArray, hotX: -8, hotY: -6]; linesPreferredCursor ¬ MultiCursors.NewCursor[bits: linesPreferredArray, hotX: -8, hotY: -5]; offCursor ¬ MultiCursors.NewCursor[bits: offArray, hotX: -8, hotY: -6]; END; Commander.Register[ key: "Gargoyle", proc: NewGGViewers, doc: "Create a graphical editor\n Gargoyle [-~fancyPanel] [-command () ] [file name pattern]\n See /R/GargoyleControlPanel.gargoyle for example commands, like\n-command ((SelectAll)(AreaColorBlack)(DeselectAll))", clientData: NIL ]; Commander.Register[ key: "GGIPToIP", proc: GGIPToIP, doc: "GGIPToIP  apply the named script to each of the named interpress files (after performing MergeIPEditable). A new file with -mod.ip at the end will be created corresponding to each original file", clientData: NIL ]; }; InitIcons: PUBLIC PROC = { holdThatTiger: BOOL ¬ GGUserProfile.GetHoldThatTiger[]; panelIconG ¬ Icons.NewIconFromFile[iconsFrom, 5]; noNameIconG ¬ Icons.NewIconFromFile[iconsFrom, IF holdThatTiger THEN 8 ELSE 1]; dirtyNoNameIconG ¬ Icons.NewIconFromFile[iconsFrom, IF holdThatTiger THEN 7 ELSE 0]; cleanIconG ¬ Icons.NewIconFromFile[iconsFrom, IF holdThatTiger THEN 10 ELSE 3]; dirtyIconG ¬ Icons.NewIconFromFile[iconsFrom, IF holdThatTiger THEN 9 ELSE 2]; }; GetIcons: PUBLIC PROC RETURNS [panelIcon, noNameIcon, dirtyNoNameIcon, cleanIcon, dirtyIcon: Icons.IconFlavor] = { RETURN[panelIconG, noNameIconG, dirtyNoNameIconG, cleanIconG, dirtyIconG]; }; popUpFont, bigFont: Imager.Font; -- initialized in Init actionAreaClass: BiScrollers.BiScrollerClass ¬ NIL; -- filled in by Init ggTipTable: TIPUser.TIPTable ¬ NIL; -- filled in by Init panelIconG: Icons.IconFlavor ¬ unInit; -- filled in by InitIcons noNameIconG: Icons.IconFlavor ¬ unInit; -- filled in by InitIcons dirtyNoNameIconG: Icons.IconFlavor ¬ unInit; -- filled in by InitIcons dirtyIconG: Icons.IconFlavor ¬ unInit; -- filled in by InitIcons cleanIconG: Icons.IconFlavor ¬ unInit; -- filled in by InitIcons iconsFrom: Rope.ROPE ¬ NIL; -- filled in by Init offCursor: ViewerClasses.CursorType ¬ crossHairsCircle; -- proper value filled in by Init pointsPreferredCursor: ViewerClasses.CursorType ¬ crossHairsCircle; -- proper value filled in by Init linesPreferredCursor: ViewerClasses.CursorType ¬ crossHairsCircle; -- proper value filled in by Init Init[]; END.