DIRECTORY Basics USING [LongDiv, LongMult], Cursors USING [CursorType, SetCursor], Imager USING [ClipRectangleI, Color, Context, MaskRectangleI, SetColor, white], ImagerBackdoor USING [MakeStipple], InputFocus USING [CaptureButtons, ReleaseButtons], Interminal USING [TurnOffColorCursor, TurnOnColorCursor], InterminalBackdoor USING [InsertAction], Menus USING [AppendMenuEntry, CreateEntry, CreateMenu, Menu, MenuEntry, MouseButton, ReplaceMenuEntry, SetGuarded], MessageWindow USING [Append, Blink], Process USING [Detach], TIPUser USING [TIPScreenCoords], ViewerClasses USING [Column, HScrollOp, NotifyProc, ScrollOp, Viewer], ViewerLocks USING [CallUnderWriteLock, Wedged], ViewerMenus USING [Adjust, Close, Color, Destroy, Grow, Left, Right, Top], ViewerOps USING [ChangeColumn, CloseViewer, EnumerateViewers, EnumProc, MouseInViewer, PaintViewer], ViewerPrivate, ViewerSpecs USING [captionHeight, scrollBarW, windowBorderSize], WindowManager USING [ScreenPos]; WindowManagerImpl: CEDAR MONITOR IMPORTS Basics, Cursors, Imager, ImagerBackdoor, InputFocus, Interminal, InterminalBackdoor, Menus, MessageWindow, Process, ViewerLocks, ViewerMenus, ViewerOps, ViewerPrivate, ViewerSpecs EXPORTS ViewerPrivate, WindowManager SHARES InputFocus, Menus, ViewerClasses ~ BEGIN OPEN Cursors, ViewerSpecs; Column: TYPE = ViewerClasses.Column; HScrollOp: TYPE = ViewerClasses.HScrollOp; NotifyProc: TYPE = ViewerClasses.NotifyProc; ScrollOp: TYPE = ViewerClasses.ScrollOp; Viewer: TYPE = ViewerClasses.Viewer; Zone: TYPE ~ {none, caption, menu, vscroll, hscroll}; CursorZone: PROC[v: Viewer, mousePos: TIPUser.TIPScreenCoords] RETURNS[Zone] ~ { x: INTEGER ~ mousePos.mouseX; y: INTEGER ~ mousePos.mouseY; IF v.caption AND v.parent=NIL AND y>=(v.wh-ViewerSpecs.captionHeight) THEN RETURN[caption]; IF v.menu#NIL AND y>=(v.cy+v.ch) THEN RETURN[menu]; IF v.scrollable AND x { SetC[bullseye]; AddEntry[feedbackViewer, feedbackZone, FALSE]; }; vscroll => IF viewer.class.scroll # NIL AND viewer.init THEN { left, right: INTEGER; [left, right] _ viewer.class.scroll[viewer, query, 0]; AddEntry[feedbackViewer, feedbackZone, FALSE, left, right]; }; hscroll => IF viewer.class.hscroll # NIL AND viewer.init THEN { left, right: INTEGER; [left, right] _ viewer.class.hscroll[viewer, query, 0]; AddEntry[feedbackViewer, feedbackZone, FALSE, left, right]; }; ENDCASE => SetC[textPointer]; -- no new feedback InputFocus.CaptureButtons[proc: ProcessWindowResults, tip: ViewerPrivate.WindowManagerTIPTable, viewer: feedbackViewer]; RETURN; }; SetC[textPointer]; -- no new feedback }; scrollLow: Imager.Color _ ImagerBackdoor.MakeStipple[8120H]; scrollVisible: Imager.Color _ ImagerBackdoor.MakeStipple[5A5AH]; scrollHigh: Imager.Color _ ImagerBackdoor.MakeStipple[2180H]; FeedbackEchoProcess: PROC [col: Column] = { DO entry: FeedbackEchoEntry _ RemEntry[col]; viewer: Viewer _ entry.viewer; inner: PROC = { IF viewer.iconic OR viewer.destroyed THEN RETURN; IF entry.remove THEN SELECT entry.zone FROM caption => { ViewerPrivate.ClearMenu[windowMenu, viewer, FALSE]; ViewerOps.PaintViewer[viewer, caption]; }; menu => ViewerPrivate.ClearMenu[viewer.menu, viewer]; vscroll => { removeScrollbar: PROC [context: Imager.Context] ~ { x: INTEGER ~ IF viewer.border THEN windowBorderSize ELSE 0; y: INTEGER ~ viewer.cy; w: INTEGER ~ viewer.cx-x; h: INTEGER ~ viewer.ch; Imager.SetColor[context, Imager.white]; Imager.MaskRectangleI[context, x, y, w, h]; }; ViewerPrivate.PaintWindow[viewer, removeScrollbar]; }; hscroll => { removeScrollbar: PROC [context: Imager.Context] ~ { x: INTEGER ~ viewer.cx; y: INTEGER ~ IF viewer.border THEN ViewerSpecs.windowBorderSize ELSE 0; w: INTEGER ~ viewer.cw; h: INTEGER ~ ViewerSpecs.scrollBarW; Imager.SetColor[context, Imager.white]; Imager.MaskRectangleI[context, x, y, w, h]; }; ViewerPrivate.PaintWindow[viewer, removeScrollbar]; }; ENDCASE ELSE SELECT entry.zone FROM caption => DrawCaptionMenu[viewer, TRUE]; menu => {}; vscroll => { drawScrollbar: PROC [context: Imager.Context] ~ { x: INTEGER ~ IF viewer.border THEN ViewerSpecs.windowBorderSize ELSE 0; y: INTEGER ~ viewer.cy; w: INTEGER ~ ViewerSpecs.scrollBarW; h: INTEGER ~ viewer.ch; d1: INTEGER ~ Basics.LongDiv[Basics.LongMult[entry.left, h]+50, 100]; d2: INTEGER ~ Basics.LongDiv[Basics.LongMult[entry.right, h]+50, 100]; Imager.SetColor[context, scrollLow]; Imager.MaskRectangleI[context, x, y, w, h-d2]; Imager.SetColor[context, scrollVisible]; Imager.MaskRectangleI[context, x, y+h-d2, w, d2-d1]; Imager.SetColor[context, scrollHigh]; Imager.MaskRectangleI[context, x, y+h-d1, w, d1]; }; IF entry.left IN [0..100] AND entry.right IN [0..100] THEN ViewerPrivate.PaintWindow[viewer, drawScrollbar]; }; hscroll => { drawScrollbar: PROC [context: Imager.Context] ~ { x: INTEGER ~ viewer.cx; y: INTEGER ~ IF viewer.border THEN ViewerSpecs.windowBorderSize ELSE 0; w: INTEGER ~ viewer.cw; h: INTEGER ~ ViewerSpecs.scrollBarW; d1: INTEGER ~ Basics.LongDiv[Basics.LongMult[entry.left, w]+50, 100]; d2: INTEGER ~ Basics.LongDiv[Basics.LongMult[entry.right, w]+50, 100]; Imager.SetColor[context, scrollLow]; Imager.MaskRectangleI[context, x, y, d1, h]; Imager.SetColor[context, scrollVisible]; Imager.MaskRectangleI[context, x+d1, y, d2-d1, h]; Imager.SetColor[context, scrollHigh]; Imager.MaskRectangleI[context, x+d2, y, w-d2, h]; }; IF entry.left IN [0..100] AND entry.right IN [0..100] THEN ViewerPrivate.PaintWindow[viewer, drawScrollbar]; }; ENDCASE; }; IF viewer # NIL THEN ViewerLocks.CallUnderWriteLock[inner, viewer ! ViewerLocks.Wedged, ABORTED => CONTINUE]; ENDLOOP; }; AddEntry: ENTRY PROC [viewer: Viewer, zone: Zone, remove: BOOL _ FALSE, left, right: INTEGER _ 0] = { IF NOT viewer.iconic AND NOT viewer.destroyed THEN { col: Column _ viewer.column; fbq: FeedbackEchoQueue _ feedBackQueues[col]; new: FeedbackEchoList _ LIST[[viewer, zone, remove, left, right]]; IF fbq = NIL THEN feedBackQueues[col] _ fbq _ NEW[FeedbackEchoQueueRep _ [NIL, NIL]]; IF fbq.tail = NIL THEN fbq.head _ new ELSE fbq.tail.rest _ new; fbq.tail _ new; BROADCAST feedbackChange; }; }; RemEntry: ENTRY PROC [col: Column] RETURNS [FeedbackEchoEntry] = { DO fbq: FeedbackEchoQueue _ feedBackQueues[col]; IF fbq # NIL THEN { head: FeedbackEchoList _ fbq.head; IF head # NIL THEN { IF (fbq.head _ head.rest) = NIL THEN fbq.tail _ NIL; head.rest _ NIL; RETURN [head.first]; }; }; WAIT feedbackChange; ENDLOOP; }; feedbackChange: CONDITION; feedBackQueues: REF FeedBackQueuesRep _ NEW[FeedBackQueuesRep _ ALL[NIL]]; FeedBackQueuesRep: TYPE = ARRAY Column OF FeedbackEchoQueue; FeedbackEchoQueue: TYPE = REF FeedbackEchoQueueRep; FeedbackEchoQueueRep: TYPE = RECORD [head, tail: FeedbackEchoList _ NIL]; FeedbackEchoList: TYPE = LIST OF FeedbackEchoEntry; FeedbackEchoEntry: TYPE = RECORD [ viewer: Viewer, zone: Zone, remove: BOOL, left, right: INTEGER]; vCursor: ARRAY Menus.MouseButton OF CursorType _ [red: scrollUp, yellow: scrollRight, blue: scrollDown]; vScrollOp: ARRAY Menus.MouseButton OF ScrollOp _ [red: up, yellow: thumb, blue: down]; hCursor: ARRAY Menus.MouseButton OF CursorType _ [red: scrollLeft, yellow: scrollUp, blue: scrollRight]; hScrollOp: ARRAY Menus.MouseButton OF HScrollOp _ [red: left, yellow: thumb, blue: right]; ProcessWindowResults: PUBLIC ViewerClasses.NotifyProc = { viewer: Viewer _ self; -- the current viewer zone: Zone _ none; mouse: TIPUser.TIPScreenCoords _ NIL; shft, ctrl: BOOL _ FALSE; FOR l: LIST OF REF ANY _ input, l.rest UNTIL l=NIL DO WITH l.first SELECT FROM coords: TIPUser.TIPScreenCoords => { client: BOOL _ FALSE; mouse _ coords; IF feedbackZone#none THEN [viewer, client] _ ViewerOps.MouseInViewer[mouse]; IF client OR viewer=NIL THEN zone _ none ELSE zone _ CursorZone[viewer, mouse]; }; atom: ATOM => { button: Menus.MouseButton _ red; action: {nil, move, down, slide, up} _ nil; SELECT atom FROM $M => action _ move; $RD => { button _ red; action _ down }; $RM => { button _ red; action _ slide }; $RU => { button _ red; action _ up }; $YD => { button _ yellow; action _ down }; $YM => { button _ yellow; action _ slide }; $YU => { button _ yellow; action _ up }; $BD => { button _ blue; action _ down }; $BM => { button _ blue; action _ slide }; $BU => { button _ blue; action _ up }; $Control => ctrl _ TRUE; $Shift => shft _ TRUE; ENDCASE; IF action=nil THEN LOOP; PostNewFeedback[viewer, zone]; IF zone=caption THEN SELECT button FROM yellow => mouse.mouseX _ growEntry.xPos+1; blue => mouse.mouseX _ closeEntry.xPos+1; ENDCASE; SELECT zone FROM -- zone specific ops caption => SELECT action FROM down, slide => ViewerPrivate.MarkMenu[windowMenu, viewer, mouse]; up => ViewerPrivate.HitMenu[windowMenu, viewer, mouse, button, shft, ctrl]; ENDCASE; menu => SELECT action FROM down, slide => ViewerPrivate.MarkMenu[viewer.menu, viewer, mouse]; up => ViewerPrivate.HitMenu[viewer.menu, viewer, mouse, button, shft, ctrl]; ENDCASE; vscroll => SELECT action FROM move => SetC[scrollUpDown]; down, slide => SetC[vCursor[button]]; up => HitVScroll[viewer, mouse.mouseY, vScrollOp[button], shft, ctrl]; ENDCASE; hscroll => SELECT action FROM move => SetC[scrollLeftRight]; down, slide => SetC[hCursor[button]]; up => HitHScroll[viewer, mouse.mouseX, hScrollOp[button], shft, ctrl]; ENDCASE; ENDCASE; }; ENDCASE; ENDLOOP; }; HitVScroll: PROC [viewer: Viewer, wy: INTEGER, op: ScrollOp, shift, control: BOOL] = { IF viewer.class.scroll#NIL THEN { h: NAT ~ viewer.ch; y: INTEGER ~ h-(wy-viewer.cy); -- from top of client area amount: NAT _ y; left, right: INTEGER; IF op=thumb THEN amount _ Basics.LongDiv[Basics.LongMult[amount, 100]+h/2, h]; AddEntry[viewer, vscroll, TRUE]; [] _ viewer.class.scroll[viewer, op, amount, shift, control]; [left, right] _ viewer.class.scroll[viewer, query, 0]; AddEntry[viewer, vscroll, FALSE, left, right]; }; SetC[scrollUpDown]; -- put the cursor back }; HitHScroll: PROC [viewer: Viewer, wx: INTEGER, op: HScrollOp, shift, control: BOOL] = { IF viewer.class.hscroll#NIL THEN { w: NAT ~ viewer.cw; x: INTEGER ~ wx-viewer.cx; -- from left of client area amount: NAT _ x; left, right: INTEGER; IF op=thumb THEN amount _ Basics.LongDiv[Basics.LongMult[amount, 100]+w/2, w]; AddEntry[viewer, hscroll, TRUE]; [] _ viewer.class.hscroll[viewer, op, amount, shift, control]; [left, right] _ viewer.class.hscroll[viewer, query, 0]; AddEntry[viewer, hscroll, FALSE, left, right]; }; SetC[scrollLeftRight]; -- put the cursor back }; DrawCaptionMenu: PUBLIC PROC [v: Viewer, guard: BOOL] = { drawCaption: PROC [context: Imager.Context] ~ { wbs: INTEGER ~ IF v.border THEN windowBorderSize ELSE 0; x: INTEGER ~ wbs; y: INTEGER ~ v.wh-captionHeight; w: INTEGER ~ v.ww-2*wbs; h: INTEGER ~ captionHeight; Imager.ClipRectangleI[context, x, y, w, h]; Imager.SetColor[context, Imager.white]; Imager.MaskRectangleI[context, x, y, w, h]; IF guard THEN Menus.SetGuarded[destroyEntry, v.guardDestroy OR (v.newVersion AND v.link=NIL AND NOT v.saveInProgress)]; ViewerPrivate.DrawMenu[windowMenu, context, x, y+h]; }; IF v.visible AND NOT v.iconic THEN ViewerPrivate.PaintWindow[v, drawCaption]; }; waitCount: PUBLIC INTEGER _ 0; SetC: PROC [cursor: CursorType] = INLINE { IF waitCount=0 THEN Cursors.SetCursor[cursor] }; WaitCursor: PUBLIC ENTRY PROC [cursor: Cursors.CursorType _ hourGlass] = { ENABLE UNWIND => NULL; IF ViewerPrivate.inputEnabled THEN SetCursor[cursor]; waitCount _ waitCount + 1; }; UnWaitCursor: PUBLIC ENTRY PROC = { ENABLE UNWIND => NULL; waitCount _ MAX[0, waitCount - 1]; IF waitCount=0 THEN RestoreCursor[]; }; RestoreCursor: PUBLIC PROC = TRUSTED { IF ViewerPrivate.inputEnabled THEN InterminalBackdoor.InsertAction[[contents: deltaMouse[[0,0]]]] }; colorDisplayOn: PUBLIC BOOL _ FALSE; -- color display status StartColorViewers: PUBLIC PROC [ screenPos: WindowManager.ScreenPos, bitsPerPixel: CARDINAL] = { IF colorDisplayOn THEN StopColorViewers[]; colorDisplayOn _ ViewerPrivate.EnableColor[bitsPerPixel]; IF colorDisplayOn THEN { Interminal.TurnOnColorCursor[IF screenPos=left THEN left ELSE right]; Menus.ReplaceMenuEntry[windowMenu, tNopEntry, tColorEntry]; } ELSE { MessageWindow.Append["Sorry, you don't have a color display.", TRUE]; MessageWindow.Blink[]; }; }; StopColorViewers: PUBLIC PROC = { DoColorViewer: ViewerOps.EnumProc = { IF v.column=color THEN { ViewerOps.CloseViewer[v]; ViewerOps.ChangeColumn[v, left]; }; }; IF NOT colorDisplayOn THEN RETURN; Interminal.TurnOffColorCursor[]; Menus.ReplaceMenuEntry[windowMenu, tColorEntry, tNopEntry]; ViewerOps.EnumerateViewers[DoColorViewer]; ViewerPrivate.DisableColor[]; colorDisplayOn _ FALSE; }; destroyEntry: Menus.MenuEntry _ Menus.CreateEntry[name: "Destroy", proc: ViewerMenus.Destroy, fork: FALSE, documentation: "Edits will be discarded..."]; adjustEntry: Menus.MenuEntry _ Menus.CreateEntry[name: "Adjust", proc: ViewerMenus.Adjust, fork: FALSE]; topEntry: Menus.MenuEntry _ Menus.CreateEntry[name: "Top", proc: ViewerMenus.Top, fork: FALSE]; leftEntry: Menus.MenuEntry _ Menus.CreateEntry[name: "<--", proc: ViewerMenus.Left, fork: FALSE]; rightEntry: Menus.MenuEntry _ Menus.CreateEntry[name: "-->", proc: ViewerMenus.Right, fork: FALSE]; tNopEntry: Menus.MenuEntry _ Menus.CreateEntry["", NIL]; tColorEntry: Menus.MenuEntry _ Menus.CreateEntry[name: "Color", proc: ViewerMenus.Color, fork: FALSE]; growEntry: Menus.MenuEntry _ Menus.CreateEntry[name: "Grow", proc: ViewerMenus.Grow, fork: FALSE]; closeEntry: Menus.MenuEntry _ Menus.CreateEntry[name: "Close", proc: ViewerMenus.Close, fork: FALSE]; windowMenu: PUBLIC Menus.Menu _ Menus.CreateMenu[]; Menus.AppendMenuEntry[windowMenu, destroyEntry]; Menus.AppendMenuEntry[windowMenu, adjustEntry]; Menus.AppendMenuEntry[windowMenu, topEntry]; Menus.AppendMenuEntry[windowMenu, leftEntry]; Menus.AppendMenuEntry[windowMenu, rightEntry]; Menus.AppendMenuEntry[windowMenu, tNopEntry]; Menus.AppendMenuEntry[windowMenu, growEntry]; Menus.AppendMenuEntry[windowMenu, closeEntry]; TRUSTED { FOR c: Column IN Column DO Process.Detach[FORK FeedbackEchoProcess[c]]; ENDLOOP; }; END. €WindowManagerImpl.mesa Copyright c 1985 by Xerox Corporation. All rights reserved. Doug Wyatt, July 22, 1985 2:18:21 pm PDT Russ Atkinson (RRA) July 1, 1985 6:47:27 pm PDT Michael Plass, November 26, 1985 10:33:25 am PST Note: mousePos is relative to lower left corner of v. This procedure is called under the notifier (and therefore needs no monitor protection) to process an input event into feedback actions. Priori to Cedar6.0 the feedback was "immediate", which tended to garbage up viewers in the process of painting. As part of the lock rationalization, feedback is required to acquire viewer locks as well. However, viewer locks must not force the input notifier to wedge, so we give the feedback chore to some other processes. Add a feedback entry This procedure is the base of a process that services caption, menu, and scrollbar feedback. There is a separate process for each column to allow reasonable feedback during long operations (like Save) in the other column(s). If this is not enough, a separate process could be spawned for each viewer that needs feedback. Preliminary results indicate that this is enough. We are careful to serialize all of the feedback for a given viewer properly. There is still a minor amount of feedback that gets done by other modules that are called by this one. MenusImpl.MarkMenu & MenusImpl.HitMenu (both exported to ViewerPrivate) can invert menu entries. At some time the feedback processes in this module could take over that chore. [self: Viewer, input: LIST OF REF ANY] RETURNS[BOOL] not monitored since notifier is synchronous Fork a feedback process for each column (even the "static" column) Κΐ˜codešœ™Kšœ Οmœ1™Kšœ žœ˜K˜6Kšœ'žœ˜;Kšœ˜—š œ žœžœžœ žœ˜?Kšœ žœ˜K˜7Kšœ'žœ˜;Kšœ˜—KšžœΟc˜0—Kšœy˜yKšžœ˜K˜—Kšœ‘˜%K˜K˜—Kšœ5Οfœ˜˜NKšœžœ˜ Kšœ=˜=Kšœ6˜6Kšœžœ˜.K˜—Kšœ‘˜*Kšœ˜—K˜š  œžœžœ!žœ˜Wšžœžœžœ˜"Kšœžœ ˜Kšœžœ‘˜6Kšœžœ˜Kšœ žœ˜Kšžœ žœ>˜NKšœžœ˜ Kšœ>˜>Kšœ7˜7Kšœžœ˜.K˜—Kšœ‘˜-Kšœ˜—K˜š œžœžœžœ˜9šœ žœ˜/Kš œžœžœ žœžœ˜8Kšœžœ˜Kšœžœ˜ Kšœžœ˜Kšœžœ˜Kšœ+˜+K˜'Kšœ+˜+šžœž˜ Kš œ.žœžœžœžœžœ˜i—Kšœ4˜4K˜—šžœ žœžœ ž˜"Kšœ*˜*—Kšœ˜K˜—šœ žœžœ˜K˜—Kš  œžœžœžœ žœ˜[š  œžœžœžœ-˜JKšžœžœžœ˜Kšžœžœ˜5K˜Kšœ˜K˜—š  œžœžœžœ˜#Kšžœžœžœ˜Kšœ žœ˜"Kšžœ žœ˜$Kšœ˜K˜—š  œžœžœžœ˜&šžœž˜"Kšœ>˜>—Kšœ˜K˜—K˜šœžœžœžœ‘˜