<<>> <> <> <> <> <> <> <> <> <> DIRECTORY InputFocus, MultiCursors, Process, Rope, TIPPrivate, TIPUser, UserInputOps, UserInputGetActions, ViewerClasses, ViewerEvents, ViewerOps, ViewerPrivate, ViewersWorld, ViewersWorldInstance, ViewersWorldRefType, ViewersWorldTypes, WindowManager; InputFocusImpl: CEDAR MONITOR IMPORTS MultiCursors, Process, TIPPrivate, TIPUser, UserInputOps, UserInputGetActions, ViewerEvents, ViewerOps, ViewerPrivate, ViewersWorld, ViewersWorldInstance, WindowManager EXPORTS InputFocus, ViewersWorldRefType, ViewerPrivate SHARES ViewerEvents = BEGIN Focus: TYPE = InputFocus.Focus; FocusRec: TYPE = InputFocus.FocusRec; LORA: TYPE = LIST OF REF ANY; Viewer: TYPE = ViewerClasses.Viewer; ViewersWorldRef: TYPE = REF ViewersWorldObj; ViewersWorldObj: PUBLIC TYPE = ViewersWorldTypes.ViewersWorldObj; <> WindowManagerTIPTable: PUBLIC TIPUser.TIPTable ¬ TIPUser.InstantiateNewTIPTable["WindowMgr.tip"]; vWorld: ViewersWorldRef ¬ ViewersWorldInstance.GetWorld[]; NewWorld: ViewersWorldInstance.SetNotifyProc = { vWorld ¬ viewersWorld }; <> SetInputFocus: PUBLIC ENTRY PROC [self: Viewer ¬ NIL, info: REF ¬ NIL] = { ENABLE UNWIND => NULL; -- release lock ModifyInputFocus[kill]; vWorld.currentFocus.owner ¬ self; -- note that we don't flush any CaptureButtons! vWorld.currentFocus.info ¬ info; vWorld.currentFocus.link ¬ NIL; ModifyInputFocus[set]; IF vWorld.currentFocus.owner # NIL THEN ChangeTIPContext[vWorld.currentFocus.owner]; }; GetInputFocus: PUBLIC PROC RETURNS [Focus] = { RETURN [vWorld.currentFocus]; }; PushInputFocus: PUBLIC ENTRY PROC [self: Viewer ¬ NIL, info: REF ¬ NIL] = { ENABLE UNWIND => NULL; -- release lock ModifyInputFocus[push]; vWorld.currentFocus ¬ NEW[FocusRec ¬ [owner: self, info: info, link: vWorld.currentFocus]]; ModifyInputFocus[set]; ChangeTIPContext[vWorld.currentFocus.owner]; }; NotifyProc: TYPE = InputFocus.NotifyProc; CaptureButtons: PUBLIC ENTRY PROC [proc: NotifyProc, tip: TIPUser.TIPTable, viewer: Viewer ¬ NIL, captureKeyboard: BOOL ¬ FALSE] = { ENABLE UNWIND => NULL; -- release lock vWorld.captureProc ¬ proc; vWorld.captureTIP ¬ tip; vWorld.captureViewer ¬ viewer; }; PopInputFocus: PUBLIC ENTRY PROC = { ENABLE UNWIND => NULL; -- release lock IF vWorld.currentFocus.link = NIL THEN RETURN; ModifyInputFocus[kill]; vWorld.currentFocus ¬ vWorld.currentFocus.link; ModifyInputFocus[pop]; ChangeTIPContext[vWorld.currentFocus.owner]; }; <> ModifyInputFocus: INTERNAL PROC [action: ViewerClasses.ModifyAction] = { owner: ViewerClasses.Viewer ¬ vWorld.currentFocus.owner; -- good for the whole invocation modify: ViewerClasses.ModifyProc ¬ NIL; SELECT action FROM set => IF ViewerEvents.ProcessEvent[setInputFocus, owner, TRUE] THEN RETURN; kill => IF ViewerEvents.ProcessEvent[killInputFocus, owner, TRUE] THEN RETURN; ENDCASE; SELECT TRUE FROM owner = NIL => {}; owner.iconic => ViewerPrivate.IconModify[owner, action]; (modify ¬ owner.class.modify) # NIL => modify[owner, action]; ENDCASE; SELECT action FROM set => [] ¬ ViewerEvents.ProcessEvent[setInputFocus, owner, FALSE]; kill => [] ¬ ViewerEvents.ProcessEvent[killInputFocus, owner, FALSE]; ENDCASE; }; transparentTIP: TIPUser.TIPTable ~ TIPUser.TransparentTIPTable[]; ChangeTIPContext: INTERNAL PROC [self: Viewer, interrupt: BOOL ¬ FALSE] = { table: TIPUser.TIPTable ¬ NIL; IF self#NIL THEN SELECT TRUE FROM self.iconic => table ¬ ViewerPrivate.iconTIP; self.transparentTIP => table ¬ transparentTIP; ENDCASE => table ¬ self.tipTable; TIPPrivate.ResetTIPContext[vWorld.focusTIP, table, MasterNotifyProc, interrupt]; }; MapCoordinateResults: PROC [results: LORA, coords: TIPUser.TIPScreenCoords ¬ NIL] = { FOR lst: LORA ¬ results, lst.rest UNTIL lst = NIL DO SELECT lst.first FROM TIPPrivate.stdCoords => IF coords # NIL THEN TIPPrivate.stdCoords­ ¬ coords­ ELSE [] ¬ ViewerOps.MouseInViewer[TIPPrivate.stdCoords]; ENDCASE; ENDLOOP; }; MasterNotifyProc: PUBLIC TIPPrivate.TIPNotifyProc ~ { <> <> <> InnerMasterNotifyProc: PROC ~ { owner: Viewer ~ vWorld.currentFocus.owner; IF owner.iconic THEN ViewerPrivate.IconNotify[owner, results] ELSE { MapCoordinateResults[results]; owner.class.notify[vWorld.currentFocus.owner, results, device, user, display]; }; }; IF vWorld.inputEnabled AND vWorld.currentFocus.owner#NIL THEN <> < IF vWorld.forkNewNotifier THEN PrivateUnWedgeNotifier[]>> <<];>> InnerMasterNotifyProc[]; }; parseInfo: TIPPrivate.TIPParseInfo ¬ TIPPrivate.CreateParseInfo[NIL, "InputFocus"]; MasterButtonProc: PUBLIC TIPPrivate.TIPButtonProc ~ { <> InnerMasterButtonProc: PROC RETURNS [consume: BOOL ¬ FALSE] ~ { results: LORA ¬ NIL; viewer: ViewerClasses.Viewer ¬ NIL; client: BOOL ¬ FALSE; device, user, display: REF; TIPParse: PROC [table: TIPUser.TIPTable, mapCoords: BOOL ¬ TRUE] = { actionBody: UserInputGetActions.InputActionBody; parseInfo.inCreek ¬ state; -- OK since we only ever do one parse parseInfo.tableHead ¬ table; actionBody ¬ UserInputGetActions.GetInputActionBody[handle: parseInfo.inCreek, waitMode: forever, acceptance: clicksAndMotion]; results ¬ TIPPrivate.WideMatchEvent[parseInfo, actionBody]; device ¬ actionBody.device; user ¬ actionBody.user; display ¬ actionBody.display; UserInputOps.Close[parseInfo.localCreek]; IF mapCoords THEN MapCoordinateResults[results, screenXY]; }; IF NOT vWorld.inputEnabled THEN RETURN [FALSE]; IF vWorld.captureProc # NIL THEN { -- someone is capturing all input events TIPParse[vWorld.captureTIP, FALSE]; IF results # NIL THEN { captureProc: InputFocus.NotifyProc ¬ vWorld.captureProc; --sample just once IF captureProc # NIL THEN captureProc[vWorld.captureViewer, results, device, user, display]; }; RETURN [TRUE]; }; [viewer, client] ¬ ViewerOps.MouseInViewer[screenXY]; SELECT TRUE FROM viewer = NIL => { cursorName: ATOM; TIPParse[NIL, FALSE]; -- consume event (and find out what device produced it) cursorName ¬ IF ISTYPE[device, ATOM] THEN NARROW[device] ELSE NIL; IF WindowManager.waitCount=0 AND MultiCursors.GetACursor[cursorName] # textPointer THEN MultiCursors.SetACursor[textPointer, cursorName]; }; viewer.iconic => { TIPParse[ViewerPrivate.iconTIP, FALSE]; IF results # NIL THEN ViewerPrivate.IconNotifyMouse[viewer, results, device, user, display]; }; client => { cursor: ViewerClasses.CursorType ¬ viewer.cursor; cursorName: ATOM; TIPParse[IF viewer.transparentTIP THEN transparentTIP ELSE viewer.tipTable]; -- sets both "results" and "device" IF cursor=none THEN cursor ¬ viewer.class.cursor; IF cursor#last THEN { <> cursorName ¬ IF ISTYPE[device, ATOM] THEN NARROW[device] ELSE NIL; IF WindowManager.waitCount=0 AND cursor#MultiCursors.GetACursor[cursorName] THEN MultiCursors.SetACursor[cursor, cursorName]; }; IF results # NIL THEN viewer.class.notify[viewer, results, device, user, display]; }; ENDCASE => { TIPParse[WindowManagerTIPTable]; IF results # NIL THEN ViewerPrivate.ProcessWindowManagerInput[viewer, results, device, user, display]; }; RETURN [TRUE]; }; <> < IF vWorld.forkNewNotifier THEN PrivateUnWedgeNotifier[]>> <<];>> consume ¬ InnerMasterButtonProc[]; }; PrivateUnWedgeNotifier: PROC = { <> ViewerOps.BlinkDisplay[]; vWorld.currentFocus ¬ NEW[FocusRec]; ViewersWorld.RestartInput[vWorld]; }; WaitingCursor: PROC = { aWhile: Process.Ticks = Process.MsecToTicks[170]; ViewerPrivate.InitializeHourglass[56]; UNTIL vWorld # NIL AND vWorld.inputEnabled DO ViewerPrivate.TickHourglass[]; Process.Pause[aWhile]; ENDLOOP; WindowManager.RestoreCursor[]; }; <<<>>> <> Process.Detach[FORK WaitingCursor]; }; >> -- doesn't work in the portable world. Needs to know what the hardware is too early CreateDebugButton: PUBLIC PROC = { }; wideViewers: BOOL ¬ TRUE; ViewersWorldInstance.CallWhenSet[NewWorld]; END.