<> <> <> <> <> 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> IF viewer=NIL THEN zone _ none; IF zone=feedbackZone AND viewer=feedbackViewer THEN RETURN; IF feedbackZone # none THEN { IF feedbackZone = caption THEN ViewerPrivate.ClearMenu[windowMenu, viewer, FALSE]; AddEntry[feedbackViewer, feedbackZone, TRUE]; InputFocus.ReleaseButtons[]; }; feedbackZone _ zone; feedbackViewer _ viewer; IF feedbackZone#none THEN { <> SELECT feedbackZone FROM caption, menu => { 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 = { <<[self: Viewer, input: LIST OF REF ANY] RETURNS[BOOL]>> <> 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.