DIRECTORY Imager USING [Color, Context, MaskRectangleI, SetColor], ImagerBackdoor USING [MakeStipple], InputFocus USING [CaptureButtons, NotifyProc, ReleaseButtons], MultiCursors, Rope USING [ROPE], TIPUser USING [InstantiateNewTIPTable, TIPScreenCoords, TIPScreenCoordsRec, TIPTable], UserInputInsertActions, ViewerClasses USING [Viewer], ViewerOps USING [ComputeColumn, MoveBoundary, PaintViewer], ViewerPrivate USING [LockViewerTree, PaintScreen, ReleaseViewerTree], ViewerSpecs USING [bwScreenWidth, captionHeight, iconHeight, iconRows, iconSpacing, openBottomY, openTopY], ViewersWorld, ViewersWorldInstance; ViewerAdjustImpl: CEDAR MONITOR IMPORTS Imager, ImagerBackdoor, InputFocus, MultiCursors, TIPUser, UserInputInsertActions, ViewerOps, ViewerPrivate, ViewerSpecs, ViewersWorld, ViewersWorldInstance EXPORTS ViewerOps = BEGIN AdjustMode: TYPE = {top, bottom, column, none}; Viewer: TYPE = ViewerClasses.Viewer; mode: AdjustMode ¬ none; stickyHeight, adjustBottom: BOOL; xPos, width, minY, maxY: INTEGER; lastPos: TIPUser.TIPScreenCoordsRec ¬ [LAST[INTEGER], LAST[INTEGER], FALSE]; Adjust: PUBLIC ENTRY PROC [viewer: Viewer, sticky: BOOL ¬ TRUE] = { ViewerPrivate.LockViewerTree[]; InputFocus.CaptureButtons[BoundaryAdjustNotify, vaTIP, viewer]; minY ¬ ViewerSpecs.openBottomY; maxY ¬ ViewerSpecs.openTopY; xPos ¬ viewer.wx; width ¬ viewer.ww; stickyHeight ¬ sticky; adjustBottom ¬ FALSE; }; EndAdjust: PROC [device: REF] = { ChangeMode[none, [0, 0, FALSE], device]; InputFocus.ReleaseButtons[]; ViewerPrivate.ReleaseViewerTree[]; }; BoundaryAdjustNotify: InputFocus.NotifyProc = { ENABLE UNWIND => EndAdjust[device]; pos: TIPUser.TIPScreenCoordsRec ¬ [-100, -100, FALSE]; cursorName: ATOM ¬ NARROW[device]; FOR list: LIST OF REF ANY ¬ input, list.rest UNTIL list = NIL DO WITH list.first SELECT FROM x: ATOM => SELECT x FROM $Abort => { EndAdjust[cursorName]; IF self.openHeight = 0 THEN ViewerOps.PaintViewer[self, caption] ELSE {self.openHeight ¬ 0; ViewerOps.ComputeColumn[self.column]}; }; $Move => { SELECT mode FROM top => IF pos.mouseX NOT IN [self.wx..self.wx+self.ww) THEN ChangeMode[column, pos, device] ELSE IF pos.mouseY <= self.wy THEN ChangeMode[bottom, pos, device] ELSE Feedback[pos]; bottom => IF pos.mouseX NOT IN [self.wx..self.wx+self.ww) THEN ChangeMode[column, pos, device] ELSE IF pos.mouseY >= self.wy+self.wh THEN ChangeMode[top, pos, device] ELSE Feedback[pos]; column => Feedback[pos]; ENDCASE => { ViewerOps.PaintViewer[self, caption]; ChangeMode[top, pos, device]; }; }; $End => { viewerMinHeight: NAT ~ ViewerSpecs.captionHeight; change: AdjustMode ¬ mode; EndAdjust[cursorName]; SELECT change FROM top => { self.openHeight ¬ MAX[viewerMinHeight, pos.mouseY-self.wy]; ViewerOps.ComputeColumn[self.column]; }; bottom => { self.openHeight ¬ MAX[viewerMinHeight, self.wy+self.wh-pos.mouseY]; ViewerOps.ComputeColumn[self.column]; }; column => ViewerOps.MoveBoundary[pos.mouseX, pos.mouseY]; ENDCASE; }; ENDCASE => NULL; z: TIPUser.TIPScreenCoords => pos ¬ Clip[z]; ENDCASE => ERROR; ENDLOOP; }; RestoreCursor: PROC [device: REF] = { vWorld: ViewersWorld.Ref ¬ ViewersWorldInstance.GetWorld[]; handle: UserInputInsertActions.Handle ¬ ViewersWorld.GetInputHandle[vWorld]; UserInputInsertActions.InsertFakePosition[handle, 0, device]; }; ChangeMode: PROC [newMode: AdjustMode, pos: TIPUser.TIPScreenCoordsRec, device: REF] = { cursorName: ATOM ¬ NARROW[device]; Feedback[[LAST[INTEGER], LAST[INTEGER], FALSE], TRUE]; SELECT newMode FROM top => {MultiCursors.SetACursor[pointUp, cursorName]; MultiCursors.AddACursorCorner[upperSide, cursorName]}; bottom => {MultiCursors.SetACursor[pointDown, cursorName]; MultiCursors.AddACursorCorner[lowerSide, cursorName]}; column => {MultiCursors.SetACursor[activate, cursorName]}; ENDCASE => RestoreCursor[device]; mode ¬ newMode; IF mode#none THEN Feedback[pos]; }; adjustStipple: Imager.Color ~ ImagerBackdoor.MakeStipple[stipple: 0A5A5H, xor: TRUE]; Feedback: PROC [pos: TIPUser.TIPScreenCoordsRec, remove: BOOL ¬ FALSE] = { action: PROC [context: Imager.Context] ~ { Show: PROC [x, y: INTEGER] = { IF mode=column THEN { Imager.MaskRectangleI[context, x-1, y, 2, ViewerSpecs.openTopY-y]; Imager.MaskRectangleI[context, 0, y-1, ViewerSpecs.bwScreenWidth, 2]; } ELSE Imager.MaskRectangleI[context, xPos, y-1, width, 2]; }; IF lastPos = pos THEN RETURN; -- no change Imager.SetColor[context, adjustStipple]; IF lastPos.mouseX#LAST[INTEGER] THEN Show[lastPos.mouseX, lastPos.mouseY]; IF remove THEN lastPos ¬ [LAST[INTEGER], LAST[INTEGER], FALSE] ELSE {Show[pos.mouseX, pos.mouseY]; lastPos ¬ pos}; }; IF pos.color # lastPos.color THEN {Feedback[[-100, -100, lastPos.color], TRUE]}; ViewerPrivate.PaintScreen[IF pos.color THEN color ELSE main, action, FALSE]; }; standardLeftWidth: INTEGER ~ 600; -- should be in ViewerSpecs Clip: PROC [position: TIPUser.TIPScreenCoords] RETURNS [TIPUser.TIPScreenCoordsRec] = { OPEN ViewerSpecs; fudge: INTEGER = 15; screenW: INTEGER ~ ViewerSpecs.bwScreenWidth; x: INTEGER ¬ position.mouseX; y: INTEGER ¬ position.mouseY; IF NOT position.color THEN { IF ABS[x-(screenW/2)] < fudge THEN x ¬ (screenW/2) ELSE IF ABS[x-standardLeftWidth] < fudge THEN x ¬ standardLeftWidth; IF mode=column THEN { IF y<=openBottomY THEN adjustBottom ¬ TRUE; -- must move below column to adjust bottom IF adjustBottom THEN { rowHeight: INTEGER = iconHeight+iconSpacing; IF y>rowHeight THEN { rows: INTEGER = MIN[(y+iconHeight/2), maxY]/rowHeight; y ¬ MIN[rows, iconRows]*rowHeight; }; } ELSE y ¬ openBottomY; -- pin y if not adjusting bottom } ELSE y ¬ MIN[maxY, MAX[minY, y]]; }; RETURN [[x, y, position.color]] }; vaTIP: TIPUser.TIPTable ¬ TIPUser.InstantiateNewTIPTable["ViewerAdjust.tip"]; END. Œ ViewerAdjustImpl.mesa Copyright Σ 1985, 1986, 1987, 1991 by Xerox Corporation. All rights reserved. McGregor on January 6, 1983 2:21 pm Maxwell, May 20, 1983 9:58 am Russ Atkinson, November 18, 1983 1:31 pm Doug Wyatt, April 15, 1985 5:51:09 pm PST Michael Plass, February 24, 1987 11:05:49 pm PST Bier, March 5, 1991 6:57 pm PST Willie-s, October 9, 1991 5:35 pm PDT Christian Jacobi, March 3, 1992 2:39 pm PST PROC [self: Viewer, input: LIST OF REF ANY, device: REF _ NIL, user: REF _ NIL, display: REF _ NIL]; IF self.position=0 THEN ViewerOps.PaintViewer[self, caption] ELSE BEGIN self.position _ 0; IF stickyHeight THEN self.openHeight _ self.position; ViewerOps.ComputeColumn[self.column]; END; self.position _ MAX[viewerMinHeight, mouseY-self.wy]; IF stickyHeight THEN self.openHeight _ self.position; self.position _ MAX[viewerMinHeight, self.wy+self.wh-mouseY]; IF stickyHeight THEN self.openHeight _ self.position; Should really pass in user, display, and data as well, but probably not needed. Addition by Randy Pausch: "snap" to the default setting for the two columns, and to the center If above first row, then snap to nearest row boundary ΚŸ–(cedarcode) style•NewlineDelimiter ™codešœ™Kšœ ΟeœC™NKšœ#™#Kšœ™K™(K™)K™0K™K™%K™+—K˜šΟk ˜ Kšœžœ,˜8Kšœžœ˜#Kšœ žœ.˜>Kšœ ˜ Kšœžœžœ˜KšœžœI˜VK˜Kšœžœ ˜Kšœ žœ,˜;Kšœžœ2˜EKšœ žœZ˜kK˜ Kšœ˜—K˜KšΠblœžœž˜Kšžœ˜€Kšžœ ˜šœž˜K˜Kšœ žœ˜/Kšœžœ˜$K˜K˜Kšœžœ˜!Kšœžœ˜!Kš œ'žœžœžœžœžœ˜L—K˜š Οnœžœžœžœžœžœ˜CKšœ˜K˜?Kšœ˜Kšœ˜K˜K˜Kšœ˜Kšœžœ˜Kšœ˜K˜—š  œžœ žœ˜!Kšœžœ ˜(K˜Kšœ"˜"Kšœ˜—K˜š œ˜/Kšžœžœžœžœžœ žœžœžœžœ žœžœ™dKšžœžœ˜#Kšœ/žœ˜6Kšœ žœžœ ˜"šžœžœžœžœžœžœžœž˜@šžœ žœž˜šœžœžœž˜šœ ˜ Kšœ<™<šœ ™ Kšœ™Kšœ5™5Kšœ%™%Kšœ™—Kšœ˜šžœ˜Kšžœ%˜)Kšžœ=˜A—Kšœ˜—šœ ˜ šžœž˜šœžœ žœžœž˜;Kšœ˜šžœžœžœ˜#Kšœ˜—Kšžœ˜—šœ žœ žœžœž˜>Kšœ˜šžœžœžœ˜+Kšœ˜—Kšžœ˜—Kšœ˜šžœ˜ K˜%Kšœ˜Kšœ˜——Kšœ˜—šœ ˜ Kšœžœ˜1K˜Kšœ˜šžœž˜šœ˜Kšœ5™5Kšœ5™5Kšœžœ&˜;K˜%Kšœ˜—šœ ˜ Kšœ=™=Kšœ5™5Kšœžœ.˜CKšœ%˜%Kšœ˜—K˜9Kšžœ˜—Kšœ˜—Kšžœžœ˜—Kšœ,˜,Kšžœžœ˜—Kšžœ˜—Kšœ˜K˜—š  œžœ žœ˜%Kšœ;˜;KšœL˜Lšœ=˜=K™O—Kšœ˜K˜—š  œžœ@žœ˜XKšœ žœžœ ˜"Kš œ žœžœžœžœžœžœ˜6šžœ ž˜Kšœl˜lKšœq˜qKšœ:˜:Kšžœ˜!—K˜Kšžœ žœ˜ Kšœ˜K˜—KšœBΟfœžœ˜UK˜š œžœ+žœžœ˜Jšœžœ˜*š œžœžœ˜šžœ žœ˜K˜BK˜EKšœ˜—Kšžœ5˜9Kšœ˜—KšžœžœžœΟc ˜*K˜(Kšžœžœžœžœ&˜Jšžœ˜ Kš žœ žœžœžœžœžœ˜4Kšžœ/˜3—K˜—Kšžœžœ(žœ˜PKš œžœ žœžœžœ˜LKšœ˜K˜—Kšœžœ’˜=K˜š œžœ%žœ!˜WKšžœ ˜Kšœžœ˜Kšœ žœ˜-Kšœžœžœ˜;šžœžœžœ˜Kšœ^™^Kšžœžœžœ˜2Kšžœžœžœžœ˜Dšžœ žœ˜Kšžœžœžœ’*˜Všžœžœ˜Kšœ žœ˜,Kšœ5™5šžœ žœ˜Kšœžœžœ#˜6Kšœžœ˜"K˜—K˜—Kšžœ’ ˜6K˜—Kšžœžœžœ ˜!Kšœ˜—Kšžœ˜Kšœ˜K˜—K˜MK˜Kšžœ˜K˜—…—H"s