DIRECTORY BiScrollers, Buttons, Commander, CommandTool, Cursors, FileNames, GGAlign, GGBasicTypes, GGBoundBox, AtomButtons, GGCaret, GGContainer, GGError, GGEvent, GGGraphicsButton, GGGravity, GGInterfaceTypes, GGMeasure, GGMenus, GGModelTypes, GGMultiGravity, GGObjects, GGRefresh, GGSegmentTypes, GGSessionLog, GGUserInput, GGVector, GGViewerOps, GGWindow, Geom2D, Icons, Imager, ImagerBackdoor, ImagerFastShow, ImagerPixelMap, ImagerTransformation, IO, Rope, SlackProcess, Terminal, TiogaButtons, TIPUser, ViewerClasses, ViewerOps; GGWindowImpl: CEDAR PROGRAM IMPORTS BiScrollers, Buttons, Commander, CommandTool, Cursors, FileNames, GGAlign, GGCaret, GGEvent, GGGravity, GGBoundBox, AtomButtons, GGContainer, GGError, GGGraphicsButton, GGMeasure, GGMenus, GGMultiGravity, GGObjects, GGRefresh, GGSessionLog, GGUserInput, GGVector, GGViewerOps, Geom2D, Icons, Imager, ImagerBackdoor, ImagerTransformation, ImagerFastShow, SlackProcess, Terminal, TIPUser, Rope, ViewerOps EXPORTS GGWindow = BEGIN Caret: TYPE = REF CaretObj; CaretObj: TYPE = GGInterfaceTypes.CaretObj; CameraDataObj: TYPE = GGModelTypes.CameraDataObj; Filters: TYPE = REF FiltersObj; FiltersObj: TYPE = GGInterfaceTypes.FiltersObj; Slice: TYPE = GGModelTypes.Slice; ImagerProc: TYPE = GGInterfaceTypes.ImagerProc; Outline: TYPE = GGModelTypes.Outline; Point: TYPE = GGBasicTypes.Point; Scene: TYPE = REF SceneObj; SceneObj: TYPE = GGModelTypes.SceneObj; Segment: TYPE = GGSegmentTypes.Segment; Sequence: TYPE = GGModelTypes.Sequence; Traj: TYPE = GGModelTypes.Traj; Viewer: TYPE = ViewerClasses.Viewer; ForegroundParts: TYPE = GGWindow.ForegroundParts; GargoyleData: TYPE = REF GargoyleDataObj; GargoyleDataObj: TYPE = GGInterfaceTypes.GargoyleDataObj; 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; GGActionAreaPaint: PROC [self: Viewer, context: Imager.Context, whatChanged: REF ANY, clear: BOOL] RETURNS [quit: BOOL _ FALSE] = TRUSTED { PaintEntireViewer: PROC = CHECKED { PlaceOrigin[self]; RestoreScreenAndInvariants[paintAction: $PaintEntireScene, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; }; gargoyleData: GargoyleData; IF whatChanged = NIL THEN { --we are being called by Window Manager gargoyleData _ NARROW[BiScrollers.ClientDataOfViewer[self]]; PlaceOrigin[self]; RestoreScreenAndInvariants[paintAction: $ViewersPaintEntireScene, gargoyleData: gargoyleData, remake: none, backgndOK: TRUE, edited: FALSE, okToClearFeedback: FALSE]; } ELSE { gargoyleData _ NARROW[whatChanged]; IF gargoyleData.refresh.paintAction=$PaintEntireViewer THEN PaintEntireViewer[] ELSE GGRefresh.ActionAreaPaint[context, gargoyleData.refresh.paintAction, gargoyleData]; }; }; RestoreScreenAndInvariants: PUBLIC PROC [paintAction: ATOM, gargoyleData: GargoyleData, remake: ForegroundParts _ triggerBag, backgndOK: BOOL _ FALSE, edited: BOOL _ TRUE, okToClearFeedback: BOOL] = { IF okToClearFeedback THEN GGError.ClearHerald[gargoyleData.feedback]; IF gargoyleData.aborted[bags] THEN { GGAlign.SetBagsForAction[gargoyleData, $CaretPos]; gargoyleData.refresh.foregndBitmapOK _ FALSE; gargoyleData.refresh.backgndBitmapOK _ FALSE; gargoyleData.aborted[bags] _ FALSE; } ELSE { SELECT remake FROM none => NULL; triggerBag => { GGAlign.SetBagsForAction[gargoyleData, $CaretPos]; gargoyleData.refresh.foregndBitmapOK _ FALSE; }; objectBag => { GGGravity.FlushObjectBag[gargoyleData.hitTest.currentObjectBag]; GGAlign.BuiltInFilters[gargoyleData.hitTest.currentTriggerBag, gargoyleData.hitTest.currentObjectBag, gargoyleData]; GGAlign.AddAllMidpoints[gargoyleData.hitTest.sceneTriggerBag, gargoyleData.hitTest.currentObjectBag, gargoyleData]; gargoyleData.refresh.foregndBitmapOK _ FALSE; }; bitMap => { gargoyleData.refresh.foregndBitmapOK _ FALSE; }; sceneBag => { GGAlign.SetSceneBagForAction[gargoyleData, $CaretPos]; GGAlign.AddAllMidpoints[gargoyleData.hitTest.sceneTriggerBag, gargoyleData.hitTest.currentObjectBag, gargoyleData]; }; ENDCASE => ERROR; gargoyleData.refresh.backgndBitmapOK _ backgndOK; }; IF edited THEN { IF NOT gargoyleData.outer.newVersion THEN { gargoyleData.outer.icon _ dirtyIcon; gargoyleData.outer.newVersion _ TRUE; ViewerOps.PaintViewer[gargoyleData.outer, caption]; }; }; gargoyleData.refresh.paintAction _ paintAction; ViewerOps.PaintViewer[ viewer: gargoyleData.actionArea, hint: client, whatChanged: gargoyleData, clearClient: FALSE] }; MakeGGViewer: PROC [fileName: Rope.ROPE _ NIL] = { gargoyleData: GargoyleData _ CreateWindow[GGObjects.CreateScene[], TRUE, FALSE, FileNames.CurrentWorkingDirectory[] ]; -- create brand new GG window IF fileName#NIL AND ~Rope.Equal[fileName, ""] THEN GGEvent.Get[event: LIST[$Get, fileName], clientData: gargoyleData]; -- tell GGEvent.Get to try for this file ViewerOps.PaintViewer[gargoyleData.outer, caption]; -- just to get the icon to appear }; CreateWindow: PUBLIC PROC [scene: Scene, iconic: BOOL, paint: BOOL, workingDirectory: Rope.ROPE] RETURNS [gargoyleData: GargoyleData] = { tV: Viewer; gargoyleData _ NEW[GargoyleDataObj]; gargoyleData.currentWDir _ workingDirectory; gargoyleData.originalWDir _ originalWDir; --global gargoyleData.hitTest _ NEW[FiltersObj]; gargoyleData.hitTest.currentTriggerBag _ GGAlign.CreateTriggerBag[]; gargoyleData.hitTest.sceneTriggerBag _ GGAlign.CreateTriggerBag[]; gargoyleData.hitTest.currentObjectBag _ GGGravity.CreateObjectBag[]; gargoyleData.scene _ scene; gargoyleData.caret _ NEW[CaretObj]; gargoyleData.anchor _ NEW[CaretObj]; gargoyleData.camera _ NEW[CameraDataObj]; gargoyleData.state _ $None; gargoyleData.mouseMode _ $None; gargoyleData.drag.savedCaret _ NEW[CaretObj]; gargoyleData.drag.selectState _ none; gargoyleData.drag.extendMode _ none; gargoyleData.refresh.startBoundBox _ GGBoundBox.CopyBoundBox[GGBoundBox.emptyBoundBox]; gargoyleData.outer _ GGContainer.Create[ info: [ name: "Gargoyle", menu: NIL, data: gargoyleData, iconic: TRUE, column: left, scrollable: FALSE, icon: cleanIcon ], paint: FALSE]; BuildActionArea[gargoyleData]; gargoyleData.height _ 0; tV _ BiScrollers.CreateScale[ [parent: gargoyleData.outer, wx: 0, wy: buttonAlign, border: FALSE], gargoyleData.biScroller]; tV _ BiScrollers.CreateRotate[ [parent: gargoyleData.outer, wx: tV.wx+tV.ww+entryHSpace, wy: buttonAlign, border: FALSE], gargoyleData.biScroller]; tV _ BiScrollers.CreateFit[ [parent: gargoyleData.outer, wx: tV.wx+tV.ww+entryHSpace, wy: buttonAlign, border: FALSE], gargoyleData.biScroller]; tV _ BiScrollers.CreateReset[ [parent: gargoyleData.outer, wx: tV.wx+tV.ww+entryHSpace, wy: buttonAlign, border: FALSE], gargoyleData.biScroller]; tV _ BiScrollers.CreateEdge[ [parent: gargoyleData.outer, wx: tV.wx+tV.ww+entryHSpace, wy: buttonAlign, border: FALSE], gargoyleData.biScroller]; tV _ BiScrollers.CreatePrev[ [parent: gargoyleData.outer, wx: tV.wx+tV.ww+entryHSpace, wy: buttonAlign, border: FALSE], gargoyleData.biScroller]; tV _ Buttons.Create[info: [parent: gargoyleData.outer, name: "CenterSel", wx: tV.wx+tV.ww+entryHSpace, wy: 0, border: FALSE], proc: CenterSel, clientData: gargoyleData, paint: FALSE]; tV _ Buttons.Create[info: [parent: gargoyleData.outer, name: "FitSel", wx: tV.wx+tV.ww+entryHSpace, wy: 0, border: FALSE], proc: FitSel, clientData: gargoyleData, paint: FALSE]; gargoyleData.height _ entryHeight; gargoyleData.slackHandle _ SlackProcess.Create[queueSize: 50, logSize: 50, optimizeProc: OptimizeQueue, loggingProc: GGSessionLog.EnterAction, abortProc: GGAbortProc, abortData: gargoyleData, abortViewer: gargoyleData.actionArea]; GGMenus.BuildControlPanel[gargoyleData, NIL]; tV _ BiScrollers.QuaViewer[gargoyleData.biScroller]; ViewerOps.MoveViewer[tV, 0, gargoyleData.height, gargoyleData.outer.cw, gargoyleData.outer.ch-gargoyleData.height, FALSE]; gargoyleData.outer.newVersion _ FALSE; gargoyleData.gravityPool _ GGGravity.NewGravityPool[]; gargoyleData.multiGravityPool _ GGMultiGravity.NewMultiGravityPool[]; SetCursorLooks[gargoyleData.hitTest.gravityType, gargoyleData]; -- assumes gravity is turned ON [] _ SlackProcess.EnableAborts[handle: gargoyleData.slackHandle]; }; OptimizeQueue: SlackProcess.OptimizeProc = { atom, nextAtom: ATOM; action: LIST OF REF ANY; IF actionsOnQueue < 2 THEN RETURN [0]; skipActions _ 0; FOR i: NAT IN [0..actionsOnQueue-2] DO [----, ----, action, ----] _ SlackProcess.GetQueueEntry[qeGen, i]; atom _ NARROW[action.first]; [----, ----, action, ----] _ SlackProcess.GetQueueEntry[qeGen, i+1]; nextAtom _ NARROW[action.first]; IF atom = $During AND nextAtom = $During THEN { skipActions _ skipActions + 1; } ELSE RETURN; ENDLOOP; }; GGAbortProc: PROC [data: REF ANY] = { -- called by SlackProcess when user signals for abort gargoyleData: GargoyleData _ NARROW[data]; gargoyleData.refresh.suppressRefresh _ FALSE; -- in case you killed FastPlayback gargoyleData.aborted _ ALL[TRUE]; -- copies of aborted for all purposes }; CenterSel: Buttons.ButtonProc = { gargoyleData: GargoyleData _ NARROW[clientData]; caretPos: Point _ GGCaret.GetPoint[gargoyleData.caret]; bBox: GGBoundBox.BoundBox _ GGBoundBox.BoundBoxOfSelected[scene: gargoyleData.scene, selectClass: normal]; IF bBox.null THEN BiScrollers.Align[bs: gargoyleData.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: gargoyleData.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 = { gargoyleData: GargoyleData _ NARROW[clientData]; tbBox, vBox, tvBox: Geom2D.Rect; cToV: ImagerTransformation.Transformation; bBox: GGBoundBox.BoundBox _ GGBoundBox.BoundBoxOfSelected[scene: gargoyleData.scene, selectClass: normal]; IF NOT bBox.null THEN { cToV _ BiScrollers.GetStyle[].GetTransforms[gargoyleData.biScroller].clientToViewer; tbBox _ ImagerTransformation.TransformRectangle[cToV, GGBoundBox.RectangleFromBoundBox[bBox] ]; vBox _ BiScrollers.ViewportBox[gargoyleData.biScroller]; tvBox _ ImagerTransformation.TransformRectangle[cToV, vBox]; BiScrollers.BoxScale[bs: gargoyleData.biScroller, from: tbBox, to: tvBox]; }; }; PlaceOrigin: PROC [viewer: Viewer] = TRUSTED { clientToViewer, viewerToClient: Imager.Transformation; rect: Imager.Rectangle; pm: ImagerPixelMap.PixelMap; pmRef: REF ImagerPixelMap.PixelMapRep; bitmap: ImagerBackdoor.Bitmap; gargoyleData: GargoyleData _ NARROW[BiScrollers.ClientDataOfViewer[viewer]]; [clientToViewer, viewerToClient] _ BiScrollers.GetStyle[].GetTransforms[BiScrollers.QuaBiScroller[viewer]]; bitmap _ gargoyleData.refresh.foregroundBitmap _ ImagerBackdoor.NewBitmap[viewer.cw, viewer.ch]; pmRef _ NEW[ImagerPixelMap.PixelMapRep _ [ ref: bitmap.ref, pointer: bitmap.base, words: LONG[bitmap.wordsPerLine]*bitmap.height, lgBitsPerPixel: 0, rast: bitmap.wordsPerLine, lines: bitmap.height ]]; pm _ [ sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: bitmap.height, fSize: bitmap.width, refRep: pmRef ]; gargoyleData.refresh.foregroundContext _ ImagerFastShow.Create[pm, 72, TRUE]; Imager.ConcatT[gargoyleData.refresh.foregroundContext, clientToViewer]; gargoyleData.refresh.backgroundPixelMap.refRep _ NIL; bitmap _ gargoyleData.refresh.backgroundBitmap _ ImagerBackdoor.NewBitmap[viewer.cw, viewer.ch]; pmRef _ NEW[ImagerPixelMap.PixelMapRep _ [ ref: bitmap.ref, pointer: bitmap.base, words: LONG[bitmap.wordsPerLine]*bitmap.height, lgBitsPerPixel: 0, rast: bitmap.wordsPerLine, lines: bitmap.height ]]; pm _ [ sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: bitmap.height, fSize: bitmap.width, refRep: pmRef ]; gargoyleData.refresh.backgroundContext _ ImagerFastShow.Create[pm, 72, TRUE]; Imager.ConcatT[gargoyleData.refresh.backgroundContext, clientToViewer]; bitmap _ gargoyleData.refresh.chunkingBitmap _ ImagerBackdoor.NewBitmap[viewer.cw, viewer.ch]; pmRef _ NEW[ImagerPixelMap.PixelMapRep _ [ ref: bitmap.ref, pointer: bitmap.base, words: LONG[bitmap.wordsPerLine]*bitmap.height, lgBitsPerPixel: 0, rast: bitmap.wordsPerLine, lines: bitmap.height ]]; pm _ [ sOrigin: 0, fOrigin: 0, sMin: 0, fMin: 0, sSize: bitmap.height, fSize: bitmap.width, refRep: pmRef ]; gargoyleData.refresh.chunkingContext _ ImagerFastShow.Create[pm, 72, TRUE]; Imager.ConcatT[gargoyleData.refresh.chunkingContext, clientToViewer]; rect _ ImagerBackdoor.GetBounds[gargoyleData.refresh.backgroundContext]; Imager.SetColor[gargoyleData.refresh.backgroundContext, Imager.white]; Imager.MaskRectangle[gargoyleData.refresh.backgroundContext, rect]; }; ViewerToWorld: PUBLIC PROC [viewerPoint: Point, gargoyleData: GargoyleData] RETURNS [worldPoint: Point] = { vec: Imager.VEC; viewerToClient: Imager.Transformation _ BiScrollers.GetStyle[].GetTransforms[BiScrollers.QuaBiScroller[gargoyleData.actionArea]].viewerToClient; vec _ ImagerTransformation.Transform[viewerToClient, [x: viewerPoint.x, y: viewerPoint.y] ]; worldPoint _ [ vec.x, vec.y]; }; WorldToViewer: PUBLIC PROC [worldPoint: Point, gargoyleData: GargoyleData] RETURNS [viewerPoint: Point] = { vec: Imager.VEC; clientToViewer: Imager.Transformation _ BiScrollers.GetStyle[].GetTransforms[BiScrollers.QuaBiScroller[gargoyleData.actionArea]].clientToViewer; vec _ ImagerTransformation.Transform[clientToViewer, [x: worldPoint.x, y: worldPoint.y] ]; viewerPoint _ [ vec.x, vec.y]; }; NewGGViewers: Commander.CommandProc = { nameList, args: LIST OF Rope.ROPE _ NIL; argLength: NAT _ 0; switchChar: CHAR = '-; [list: args, length: argLength] _ CommandTool.ParseToList[cmd: cmd, starExpand: TRUE, switchChar: switchChar ! CommandTool.Failed => CONTINUE; ]; IF args = NIL OR argLength < 1 THEN { MakeGGViewer[fileName: NIL]; RETURN;}; FOR rl: LIST OF Rope.ROPE _ args, rl.rest UNTIL rl = NIL DO --open a GGViewer on each file [] _ MakeGGViewer[fileName: FileNames.ResolveRelativePath[rl.first]]; ENDLOOP; }; BuildActionArea: PRIVATE PROC [gargoyleData: GargoyleData] = { bs: BiScrollers.BiScroller _ BiScrollers.GetStyle[].CreateBiScroller[ class: actionAreaClass, info: [ parent: gargoyleData.outer, wx: 0, wy: gargoyleData.height, ww: gargoyleData.outer.ww, wh: gargoyleData.outer.wh, -- only initial values for ww and wh. They are constrained below data: gargoyleData, border: FALSE, scrollable: FALSE], paint: FALSE ]; gargoyleData.actionArea _ bs.QuaViewer[inner: TRUE]; gargoyleData.biScroller _ bs; GGContainer.ChildXBound[gargoyleData.outer, bs.QuaViewer[inner: FALSE]]; GGContainer.ChildYBound[gargoyleData.outer, bs.QuaViewer[inner: FALSE]]; gargoyleData.height _ gargoyleData.height + gargoyleData.actionArea.wh; }; GGExtremaProc: PROC [clientData: REF ANY, direction: Geom2D.Vec] RETURNS [min, max: Geom2D.Vec] --BiScrollers.ExtremaProc-- = { area: Geom2D.Rect; gargoyleData: GargoyleData _ NARROW[clientData]; bigBox: GGBoundBox.BoundBox _ GGBoundBox.BoundBoxOfBoxes[GGObjects.BoundBoxesInScene[gargoyleData.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]; }; NewCaretPos: PUBLIC PROC [gargoyleData: GargoyleData] = { caret0, caret1, caret2Pos: Point; distance, angle, slope, lineDist: REAL; caret0 _ gargoyleData.measure.caret0; caret1 _ gargoyleData.measure.caret1; caret2Pos _ GGVector.Scale[GGCaret.GetPoint[gargoyleData.caret], 1.0/gargoyleData.hitTest.scaleUnit]; distance _ GGMeasure.DistanceBetweenPoints[caret1, caret2Pos]; -- distance in scaleUnits angle _ GGMeasure.SmallestAngleOfPoints[caret0, caret1, caret2Pos]; slope _ GGMeasure.SlopeOfPoints[caret1, caret2Pos]; IF slope<0.0 THEN slope _ slope+180.0; IF slope=360.0 THEN slope _ 0.0; lineDist _ GGMeasure.DistanceFromPointToLine[caret2Pos, caret0, caret1]; -- line distance in scaleUnits gargoyleData.measure.caret2Value _ caret2Pos; GGViewerOps.SetPoint[gargoyleData.measure.caret2, caret2Pos]; gargoyleData.measure.slopeViewValue _ slope; GGViewerOps.SetReal[gargoyleData.measure.slopeView, slope]; gargoyleData.measure.angleViewValue _ angle; GGViewerOps.SetReal[gargoyleData.measure.angleView, angle]; gargoyleData.measure.radiusViewValue _ distance; GGViewerOps.SetReal[gargoyleData.measure.radiusView, distance]; gargoyleData.measure.lineDistViewValue _ lineDist; GGViewerOps.SetReal[gargoyleData.measure.lineDistView, lineDist]; }; SaveCaretPos: PUBLIC PROC [gargoyleData: GargoyleData] = { caret2Pos: Point _ GGVector.Scale[GGCaret.GetPoint[gargoyleData.caret], 1.0/gargoyleData.hitTest.scaleUnit]; gargoyleData.measure.caret2Value _ caret2Pos; GGViewerOps.SetPoint[gargoyleData.measure.caret2, caret2Pos]; gargoyleData.measure.caret0 _ gargoyleData.measure.caret1; gargoyleData.measure.caret1 _ caret2Pos; }; SetCursorLooks: PUBLIC PROC [type: GGInterfaceTypes.GravityType, gargoyleData: GargoyleData] = { newCursor: Cursors.CursorType; mouseViewer: ViewerClasses.Viewer; client: BOOL; terminal: Terminal.Virtual _ Terminal.Current[]; mousePos: Terminal.Position _ Terminal.GetMousePosition[terminal]; -- mouse origin in upper left tipScreenCoords: TIPUser.TIPScreenCoords _ NEW[TIPUser.TIPScreenCoordsRec _ [mouseX: mousePos.x, mouseY: mousePos.y, color: gargoyleData.actionArea.column=color] ]; tipScreenCoords.mouseY _ IF gargoyleData.actionArea.column=color THEN terminal.colorHeight-mousePos.y ELSE terminal.bwHeight-mousePos.y; -- move mouse origin to lower left [mouseViewer, client] _ ViewerOps.MouseInViewer[tipScreenCoords]; gargoyleData.actionArea.class.cursor _ newCursor _ SELECT type FROM innerCircle => pointsPreferredCursor, strictDistance => strictDistanceCursor, off => offCursor, ENDCASE => ERROR; IF mouseViewer=gargoyleData.actionArea AND client THEN Cursors.SetCursor[newCursor]; }; BuildCursors: PROC [] = { pointsPreferredArray: Cursors.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 strictDistanceArray: Cursors.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: Cursors.CursorArray = [177777B, 177777B, 140003B, 140003B, 140003B, 140003B, 140003B, 140003B, 140003B, 140003B, 140003B, 140003B, 140003B, 140003B, 177777B, 177777B]; -- square pointsPreferredCursor _ Cursors.NewCursor[bits: pointsPreferredArray, hotX: -8, hotY: -6]; strictDistanceCursor _ Cursors.NewCursor[bits: strictDistanceArray, hotX: -8, hotY: -5]; offCursor _ Cursors.NewCursor[bits: offArray, hotX: -8, hotY: -6]; }; SetHeuristics: PUBLIC PROC [gargoyleData: GargoyleData, on: BOOL] = { stateInfo: AtomButtons.TwoState _ gargoyleData.hitTest.heuristicsButton; state: AtomButtons.StateType _ AtomButtons.GetButtonState[stateInfo]; IF state = on AND on OR state = off AND NOT on THEN RETURN; AtomButtons.SwitchState[stateInfo]; RestoreScreenAndInvariants[paintAction: $PaintEntireViewer, gargoyleData: gargoyleData, remake: triggerBag, backgndOK: FALSE, edited: FALSE, okToClearFeedback: TRUE]; }; GetHeuristics: PUBLIC PROC [gargoyleData: GargoyleData] RETURNS [on: BOOL] = { stateInfo: AtomButtons.TwoState _ gargoyleData.hitTest.heuristicsButton; state: AtomButtons.StateType _ AtomButtons.GetButtonState[stateInfo]; on _ state = on; }; SetGravityExtent: PUBLIC PROC [gargoyleData: GargoyleData, inches: REAL] = { screenDots: REAL; graphicsState: GGGraphicsButton.GraphicsState _ gargoyleData.hitTest.gravityExtentButton; screenDots _ inches*72.0; GGError.PutF[gargoyleData.feedback, oneLiner, "Gravity extent is now %g inches.", [real[inches]] ]; GGGraphicsButton.SetButtonValueAndPaint[graphicsState, screenDots]; gargoyleData.hitTest.tolerance _ screenDots; gargoyleData.hitTest.criticalR _ screenDots; gargoyleData.hitTest.innerR _ screenDots/2.0; }; GetGravityExtent: PUBLIC PROC [gargoyleData: GargoyleData] RETURNS [inches: REAL] = { screenDots: REAL; graphicsState: GGGraphicsButton.GraphicsState _ gargoyleData.hitTest.gravityExtentButton; screenDots _ GGGraphicsButton.GetValue[graphicsState]; inches _ screenDots/72.0; }; Init: 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 ]]; cleanIcon _ Icons.NewIconFromFile["Gargoyle.icons", 1]; -- done here so file will come from working directory into which Gargoyle was brought over, e.g. ///Commands/ dirtyIcon _ Icons.NewIconFromFile["Gargoyle.icons", 0]; -- done here so file will come from working directory into which Gargoyle was brought over, e.g. ///Commands/ originalWDir _ FileNames.CurrentWorkingDirectory[]; BuildCursors[]; Commander.Register[ key: "Gargoyle", proc: NewGGViewers, doc: "Create a Gargoyle Graphics Window", clientData: NIL ]; }; actionAreaClass: BiScrollers.BiScrollerClass _ NIL; -- filled in by Init cleanIcon: Icons.IconFlavor _ unInit; -- filled in by Init dirtyIcon: Icons.IconFlavor _ unInit; -- filled in by Init originalWDir: Rope.ROPE _ NIL; -- filled in by Init offCursor: Cursors.CursorType; -- filled in by BuildCursors pointsPreferredCursor: Cursors.CursorType; -- filled in by BuildCursors strictDistanceCursor: Cursors.CursorType; -- filled in by BuildCursors Init[]; END. ����File: GGWindowImpl.mesa Author: Eric Bier on June 5, 1985 11:34:50 pm PDT Copyright c 1985, 1986 by Xerox Corporation. All rights reserved. Last edited by Bier on January 28, 1987 3:56:19 pm PST Contents: Code to create a Gargoyle window. Calls GGContainer to make the container and adds menus at the top. The graphics part of the Gargoyle window is an inner viewer of a BiScroller of class $ActionArea. The paint proc for a $ActionArea is defined herein. Pier, January 16, 1987 6:52:46 pm PST ViewerClasses.PaintProc whatChanged is a GargoyleData. self is an ActionArea. Fix the foreground Chain. Fix the background invariant. Show the viewer as edited, if appropriate. Refresh the screen. Construct the outer container. slack process must be created before call to GGMenus.BuildControlPanel PROC [qeGen: QueueEntryGenerator, actionsOnQueue: NAT] RETURNS [skipActions: NAT]; Notice that skipActions will be at most summary.count -1; The most recent action on the queue will be done if nothing else is appropriate. Always do the last During. Change scale and offsets so that client data previously in `from' covers as much as possible of `to' (from and to in viewer coords). viewer is the ActionArea. The created bitmap contexts have their transformations concatted with the analogous BiScrollers transformation so that operations on the new bitmaps will match the viewer when they are finally displayed. Construct the foreground bitmap. Construct the background bitMap or pixelMap. IF gargoyleData.refresh.showColors.state = off THEN { -- use 1 bit per pixel bitmap }; ELSE { -- use 8 bit per pixel pixelMap for color display gargoyleData.refresh.backgroundBitmap _ NIL; pm _ gargoyleData.refresh.backgroundPixelMap _ ImagerPixelMap.Create[lgBitsPerPixel: 3, bounds: [0, 0, viewer.ch, viewer.cw] ]; gargoyleData.refresh.backgroundContext _ ImagerDitheredDevice.ContextFromPixelMap[frame: pm, displayHeight: viewer.ch, pixelUnits: TRUE]; }; Construct the chunking bitMap or pixelMap. IF gargoyleData.refresh.showColors.state = off THEN { -- use 1 bit per pixel bitmap }; ELSE { -- use 8 bit per pixel pixelMap for color display gargoyleData.refresh.chunkingBitmap _ NIL; pm _ gargoyleData.refresh.chunkingPixelMap _ ImagerPixelMap.Create[lgBitsPerPixel: 3, bounds: [0, 0, viewer.ch, viewer.cw] ]; gargoyleData.refresh.chunkingContext _ ImagerDitheredDevice.ContextFromPixelMap[frame: pm, displayHeight: viewer.ch, pixelUnits: TRUE]; }; Initialize the context to white. [cmd: Handle] RETURNS [result: REF _ NIL, msg: Rope.ROPE _ NIL]; Create an ActionArea which fills the bottom of the Gargoyle Container This proc is required by BiScrollers to return the extremes of the displayed data The following is a hack and is NOT the right way to do business. Unfortunately, the cursor data lives in a viewer CLASS rec instead of a viewer INSTANCE rec so their is no easy way to keep an instance-specific cursor. change the cursor immediately if the mouse is in the action area Gargoyle Viewer State vanilla: GGBasicTransformProc, --proc which provides the vanilla transform for BiScrollers ÊŸ��˜�codešœ™Kšœ1™1KšœB™BKšœ6™6šœ‡™‡Kšœ%™%——K˜�šÏk ˜ K˜�KšœºœP˜ŒK˜�—šÏnœœ˜Kšœ“˜šKšœ˜—˜�Kšœœœ ˜Kšœ œ˜+Kšœœ˜1Kšœ œœ˜Kšœœ˜/Kšœœ˜!Kšœœ˜/Kšœ œ˜%Kšœœ˜!Kšœœœ ˜Kšœ œ˜'Kšœ œ˜'Kšœ œ˜'Kšœœ˜Kšœœ˜$Kšœœ˜1K˜�Kšœœœ˜)Kšœœ$˜9K˜�Kšœ œÏc8˜RKšœ œŸ˜8Kšœ œŸ!˜<Kšœ œŸ+˜FKšœ œŸ0˜IKšœ œŸ0˜IKšœ œŸ0˜IKšœ œ˜Kšœœ˜Kšœœ˜Kšœœ˜Kšœœ˜—K˜�šžœœ6œœ œœœœœ˜‹Kšœ™Kšœ6™6šžœœœ˜#Kšœ˜Kšœpœ œœ˜ŸK˜—Kšœ˜šœœœŸ'˜CKšœœ'˜<Kšœ˜Kšœwœ œœ˜¦Kšœ˜—šœ˜Kšœœ˜#Kšœ5œÏbœ˜TKšœS˜SKšœ˜—Kšœ˜K˜�—šžœœœœOœœ œœœ˜ÈKšœœ,˜EKšœ™šœœ˜$Kšœ2˜2Kšœ'œ˜-Kšœ'œ˜-Kšœœ˜#K˜—šœ˜šœ˜Kšœœ˜ šœ˜Kšœ2˜2Kšœ'œ˜-K˜—˜Kšœ@˜@Kšœt˜tKšœs˜sKšœ'œ˜-K˜—˜Kšœ'œ˜-K˜—šœ ˜ Kšœ6˜6Kšœs˜sK˜—Kšœœ˜—Kšœ™Kšœ1˜1K˜Kšœ*™*—šœœ˜šœœœ˜+Kšœ$˜$Kšœ œ˜%K˜�Kšœ3˜3K˜—K˜Kšœ™—Kšœ/˜/šœ˜Kšœ ˜ Kšœ ˜ Kšœ˜Kšœ œ˜—Kšœ˜K˜�—šžœœœœ˜2KšœCœœ)Ÿ˜”Kšœ œœœœ-Ÿ(˜ŸKšœ4Ÿ!˜UK˜K˜�—šžœœœœ œœœ!˜‰K˜�K˜Kšœœ˜$Kšœ,˜,Kšœ*Ÿ˜2Kšœœ ˜'KšœD˜DKšœB˜BKšœD˜DKšœ˜Kšœœ˜#Kšœœ˜$Kšœœ˜)Kšœ˜Kšœ˜Kšœœ˜-Kšœ%˜%Kšœ$˜$KšœW˜WKšœ™šœ(˜(šœ˜Kšœ˜Kšœœ˜ Kšœ˜Kšœœ˜ Kšœ ˜ Kšœœ˜Kšœ˜Kšœ˜—Kšœœ˜—Kšœ˜K˜�K˜Kšœ[œ˜|Kšœrœ˜“Kšœoœ˜Kšœqœ˜’Kšœpœ˜‘Kšœpœ˜‘K•StartOfExpansionê[info: ViewerClasses.ViewerRec _ [class: NIL, wx: 0, wy: 0, ww: 0, wh: 0, cx: 0, cy: 0, cw: 0, ch: 0, lock: [process: PROCESS#0B, count: 0B (0)], tipTable: NIL, name: NIL, file: NIL, label: NIL, menu: NIL, icon: 177777B?, column: left, caption: FALSE, scrollable: TRUE, hscrollable: FALSE, iconic: TRUE, border: TRUE, newVersion: FALSE, newFile: FALSE, visible: TRUE, offDeskTop: FALSE, destroyed: FALSE, init: FALSE, saveInProgress: FALSE, inhibitDestroy: FALSE, guardDestroy: FALSE, paintingWedged: FALSE, spare0: FALSE, spare1: FALSE, spare2: FALSE, spare3: FALSE, spare4: FALSE, spare5: FALSE, spare6: FALSE, position: 0, openHeight: 0, link: NIL, parent: NIL, sibling: NIL, child: NIL, props: NIL, data: NIL], proc: Buttons.ButtonProc, clientData: REF ANY _ NIL, fork: BOOL _ TRUE, font: ImagerFont.Font _ NIL, documentation: REF ANY _ NIL, guarded: BOOL _ FALSE, paint: BOOL _ TRUE]šœvœ5œ˜·K–ê[info: ViewerClasses.ViewerRec _ [class: NIL, wx: 0, wy: 0, ww: 0, wh: 0, cx: 0, cy: 0, cw: 0, ch: 0, lock: [process: PROCESS#0B, count: 0B (0)], tipTable: NIL, name: NIL, file: NIL, label: NIL, menu: NIL, icon: 177777B?, column: left, caption: FALSE, scrollable: TRUE, hscrollable: FALSE, iconic: TRUE, border: TRUE, newVersion: FALSE, newFile: FALSE, visible: TRUE, offDeskTop: FALSE, destroyed: FALSE, init: FALSE, saveInProgress: FALSE, inhibitDestroy: FALSE, guardDestroy: FALSE, paintingWedged: FALSE, spare0: FALSE, spare1: FALSE, spare2: FALSE, spare3: FALSE, spare4: FALSE, spare5: FALSE, spare6: FALSE, position: 0, openHeight: 0, link: NIL, parent: NIL, sibling: NIL, child: NIL, props: NIL, data: NIL], proc: Buttons.ButtonProc, clientData: REF ANY _ NIL, fork: BOOL _ TRUE, font: ImagerFont.Font _ NIL, documentation: REF ANY _ NIL, guarded: BOOL _ FALSE, paint: BOOL _ TRUE]šœsœ2œ˜±Kšœ"˜"˜�KšœF™F—Kšœæ˜æK˜�Kšœ(œ˜-Kšœ4˜4Kšœsœ˜zKšœ œ˜&Kšœ6˜6KšœE˜EKšœ@Ÿ˜_K–$[handle: SlackProcess.SlackHandle]šœA˜AKšœ˜K˜�—šž œ˜,Kšœ.œœœ™RKšœ§™§Kšœœ˜Kš œœœœœ˜Kšœœœ˜&K˜šœœœ˜&KšœB˜BKšœœ˜KšœD˜DKšœœ˜ šœœœ˜/Kšœ˜K˜—Kšœœ˜Kšœ˜—K˜K˜�—š žœœœœŸ5˜[Kšœœ˜*Kšœ'œŸ"˜PKšœœœŸ%˜GK˜K˜�—šž œ˜!Kšœœ ˜0K–f[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGSegmentTypes.SelectionClass _ normal]šœ7˜7Kšœj˜jK–œ[bs: BiScrollers.BiScroller, client: BiScrollers.Location, viewer: BiScrollers.Location, doX: BOOL _ TRUE, doY: BOOL _ TRUE, paint: BOOL _ TRUE]šœ œžœ˜´Kšœ¸œ˜ÂK˜K˜�—šžœ˜Kšœœ ˜0K–f[gargoyleData: GGInterfaceTypes.GargoyleData, selectClass: GGSegmentTypes.SelectionClass _ normal]šœ ˜ Kšœ*˜*Kšœj˜jšœœœ˜KšœT˜TK–O[m: ImagerTransformation.Transformation, r: ImagerTransformation.Rectangle]šœ_˜_Kšœ8˜8Kšœ<˜<šœJ˜JKšœdŸœŸ™„—K˜—K˜K˜�—šžœœœ˜.Kšœæ™æKšœ6˜6Kšœ˜Kšœ˜Kšœœ˜&Kšœ˜Kšœœ)˜LKšœl˜lK˜�K™ Kšœ`˜`šœœ˜*Kšœ.œ$˜VKšœB˜BKšœ˜—šœ˜Kšœ)˜)Kšœ8˜8Kšœ˜—KšœGœ˜MKšœG˜GK˜�K™,šœ-œŸ™SKšœ1œ˜5Kšœ`˜`šœœ˜*Kšœ.œ$˜VKšœB˜BKšœ˜—šœ˜Kšœ)˜)Kšœ8˜8Kšœ˜—KšœGœ˜MK™—šœŸ1™8Kšœ(œ™,Kšœ™Kšœƒœ™‰K™—KšœG˜GK˜�K™*šœ-œŸ™SKšœ^˜^šœœ˜*Kšœ.œ$˜VKšœB˜BKšœ˜—šœ˜Kšœ)˜)Kšœ8˜8Kšœ˜—KšœEœ˜KK™—šœŸ1™8Kšœ&œ™*Kšœ}™}Kšœœ™‡K™—KšœE˜E˜�K™ —KšœH˜HKšœF˜FKšœC˜CKšœ˜K˜�—šž œœœ2œ˜kKšœœ˜Kšœ‘˜‘Kšœ\˜\Kšœ˜Kšœ˜K˜�—šž œœœ1œ˜kKšœœ˜Kšœ‘˜‘KšœZ˜ZKšœ˜Kšœ˜K˜�—šÐbnœ˜'Kšœœ œœœœ™@Kš œœœœœ˜(Kšœœ˜Kšœœ˜KšœPœ1œ˜‘Kšœœœœœœ˜LšœœœœœœœŸ˜ZKšœE˜EKšœ˜—K˜K˜�—šžœœœ!˜>KšœE™EšœE˜EKšœ˜šœ˜Kšœ˜Kšœ˜Kšœ˜KšœŸA˜\Kšœ˜Kšœœ˜Kšœœ˜—Kšœ˜Kšœ˜—Kšœ.œ˜4Kšœ˜Kšœ@œ˜HKšœ@œ˜HKšœG˜GKšœ˜K˜�—š¡ œœœœœŸœ˜K™QKšœ˜Kšœœ ˜0Kšœo˜oKš œœœœTœ˜‡Kšœ9˜9K˜K˜�—K˜�šžœœœ!˜9Kšœ!˜!Kšœ"œ˜'Kšœ%˜%Kšœ%˜%KšœE œ˜eKšœ?Ÿ˜XKšœC˜CKšœ3˜3Kšœœ˜&Kšœ œ ˜ KšœIŸ˜gKšœ-˜-Kšœ=˜=Kšœ,˜,Kšœ;˜;Kšœ,˜,Kšœ;˜;Kšœ0˜0Kšœ?˜?Kšœ2˜2KšœA˜AK˜K˜�—šžœœœ!˜:Kšœl˜lKšœ-˜-Kšœ=˜=Kšœ:˜:Kšœ(˜(K˜K˜�—K˜�šžœœœE˜`K™ÙK˜Kšœ"˜"Kšœœ˜ K˜0KšœCŸ˜`Kšœ+œv˜¤Kšœœ&œ!œŸ"˜«KšœA˜Ašœ3œ˜CKšœ%˜%Kšœ'˜'Kšœ˜Kšœœ˜K–[Cursors.CursorType]š @™@—Kšœ%œœ˜TK˜K˜�—šžœœ˜KšœœŸ ˜¦KšœÙŸ˜êKšœ²Ÿ ˜»KšœZ˜ZKšœX˜XKšœB˜BK˜K˜�—Kšœ™K™�šž œœœ"œ˜EKšœH˜HKšœE˜EKšœœœ œœœœ˜;Kšœ#˜#Kšœwœ œœ˜¦K˜K˜�—š ž œœœœœ˜NKšœH˜HKšœE˜EKšœ˜K˜K™�—šžœœœ&œ˜LKšœœ˜KšœY˜YKšœ˜Kšœc˜cKšœC˜CKšœ,˜,Kšœ,˜,Kšœ-˜-K˜K˜�—š žœœœœ œ˜UKšœœ˜KšœY˜YKšœ6˜6Kšœ˜K˜—K˜�šžœœ˜šœ=˜=Kšœ˜Kšœ˜Kšœ ˜ Kšœ˜Kšœ9˜9KšœœŸ&˜9Kšœœ˜Kšœœ˜!KšœŸ;™ZKšœŸ@˜[Kšœ˜—Kšœ8Ÿm˜¥Kšœ8Ÿm˜¥Kšœ3˜3K˜šœ˜Kšœ˜Kšœ˜Kšœ)˜)Kšœ˜K˜—Kšœ˜—K˜�Kšœ/œŸ˜HKšœ&Ÿ˜:Kšœ&Ÿ˜:KšœœœŸ˜3Kšœ Ÿ˜<Kšœ,Ÿ˜HKšœ+Ÿ˜GK˜�Kšœ˜K˜�Kšœ˜—�…—����Vº��~s��