DIRECTORY JunoCursorMenu, Rope USING [ROPE], MessageWindow USING [Append], ViewerClasses USING [ViewerClass, Viewer, NotifyProc, PaintProc, ViewerClassRec, ViewerRec], ViewerOps USING [RegisterViewerClass, CreateViewer, NotifyViewer, PaintViewer], Cursors USING [CursorType, NewCursor], TIPUser USING [TIPScreenCoords, InstantiateNewTIPTable], Graphics USING [SetCP, SetPaintMode, SetColor, white, black, DrawBox, GetBounds, DrawTo], GraphicsOps USING [DrawBitmap, BitmapRef, BitmapRep, DrawTexturedBox], Terminal USING [BWCursorBitmap], InputFocus USING [SetInputFocus], WindowManager USING [RestoreCursor]; JunoCursorMenuImpl: CEDAR MONITOR LOCKS table USING table: REF CursorTable IMPORTS ViewerOps, MessageWindow, Cursors, TIPUser, Graphics, GraphicsOps, InputFocus, WindowManager EXPORTS JunoCursorMenu = BEGIN OPEN JunoCursorMenu; cursorMargin: INTEGER = 4; -- white space around each cursor extraMargin: INTEGER = 8; -- extra white space around menu cursorHalfDelta: INTEGER = 8+cursorMargin; -- half the distance between cursor centers cursorDelta: INTEGER = cursorHalfDelta+cursorHalfDelta; -- distance between cursor centers CursorEntry: TYPE = RECORD [type: Cursors.CursorType, bitmap: GraphicsOps.BitmapRef, name: ATOM, -- NIL iff type = blank help: Rope.ROPE, grayed: BOOL _ FALSE, -- client highlight (gray mask) setFocus: BOOL]; CursorTable: TYPE = MONITORED RECORD [rows, cols: Index, showCurrent: BOOL _ TRUE, current: INTEGER, -- index of most recently selected cursor (-1 if none) candidate: INTEGER, -- index of candidate cursor during mouse rolls (-1 if none) framed, reversed: INTEGER, -- selection feedback status (-1 if none). PaintProc use only. cursor: SEQUENCE len: INTEGER OF CursorEntry]; CreateCursorMenu: PUBLIC PROC [parent: Viewer, x, y: INTEGER, rows, cols: Index, showCurrent: BOOL _ TRUE] RETURNS [menu: Viewer] = BEGIN table: REF CursorTable = NEW [CursorTable[rows*cols] _ [rows: rows, cols: cols, current: -1, candidate: -1, reversed: -1, framed: -1, showCurrent: showCurrent, cursor: ]]; FOR ix: INTEGER IN [0..rows*cols) DO table.cursor[ix] _ [type: blank, bitmap: NIL, name: NIL, setFocus: FALSE] ENDLOOP; menu _ ViewerOps.CreateViewer[flavor: $JunoCursorMenu, info: [cx: x, -- position of menu wrt parent cy: y, wx: x, -- origin of menu frame wrt parent wy: y, ww: extraMargin+cursorMargin+cols*cursorDelta+extraMargin, -- size of menu wh: extraMargin+cursorMargin+rows*cursorDelta+extraMargin, scrollable: FALSE, name: NIL, parent: parent, iconic: FALSE, border: TRUE, data: table]] END; InvalidIndices: PUBLIC ERROR = CODE; InvalidCursorName: PUBLIC ERROR = CODE; WhatWhatWhat: PUBLIC ERROR = CODE; AddCursor: PUBLIC PROC [menu: Viewer, name: ATOM, help: Rope.ROPE, bits: Terminal.BWCursorBitmap, row, col: Index, hotX, hotY: INTEGER _ 0, setFocus: BOOL _ TRUE] = BEGIN ix: INTEGER; DoIt: ENTRY PROC [table: REF CursorTable] = {type: Cursors.CursorType = Cursors.NewCursor[bits, hotX, hotY]; IF name=NIL OR type=blank THEN RETURN WITH ERROR InvalidCursorName; IF row>table.rows OR col>table.cols THEN RETURN WITH ERROR InvalidIndices; ix _ (row-1)*table.cols + col-1; table.cursor[ix] _ [type: type, bitmap: NEW[GraphicsOps.BitmapRep _ [base: NEW [Terminal.BWCursorBitmap _ bits], raster: 1, width: 16, height: 16]], name: name, help: help, setFocus: setFocus]}; DoIt [NARROW [menu.data]]; ViewerOps.PaintViewer [viewer: menu, hint: client, clearClient: FALSE, whatChanged: NEW[INTEGER _ ix]] END; RemoveCursor: PUBLIC PROC [menu: Viewer, name: ATOM] = BEGIN ix: INTEGER; DoIt: ENTRY PROC [table: REF CursorTable] = {ix _ FindCursor[table, name]; -- index of cursor with given name IF ix = -1 THEN RETURN WITH ERROR InvalidCursorName; table.cursor[ix] _ [type: blank, bitmap: NIL, name: NIL, setFocus: FALSE]}; DoIt [NARROW [menu.data]]; ViewerOps.PaintViewer [viewer: menu, hint: client, clearClient: FALSE, whatChanged: NEW[INTEGER _ ix]] END; PickUpCursor: PUBLIC PROC [menu: Viewer, name: ATOM, notify: BOOL _ TRUE] = BEGIN ix: INTEGER; DoIt: ENTRY PROC [table: REF CursorTable] = {ix _ FindCursor[table, name]; -- index of cursor with given name IF ix=-1 THEN RETURN WITH ERROR InvalidCursorName; SetParentCursor[menu, table, ix, notify]; MessageWindow.Append[message: table.cursor[ix].help, clearFirst: TRUE]}; DoIt [NARROW [menu.data]]; ViewerOps.PaintViewer [viewer: menu, hint: client, clearClient: FALSE, whatChanged: $Selection] END; HighlightCursor: PUBLIC PROC [menu: Viewer, name: ATOM, grayIt: BOOL _ FALSE] = BEGIN ix: INTEGER; DoIt: ENTRY PROC [table: REF CursorTable] = {ix _ FindCursor[table, name]; -- index of cursor with given name IF ix=-1 THEN RETURN WITH ERROR InvalidCursorName; table.cursor[ix].grayed _ grayIt}; DoIt [NARROW [menu.data]]; ViewerOps.PaintViewer [viewer: menu, hint: client, clearClient: FALSE, whatChanged: NEW[INTEGER _ ix]] END; CursorCenter: PROC [table: REF CursorTable, row, col: Index] RETURNS [xc, yc: INTEGER] = INLINE BEGIN RETURN [xc: extraMargin+cursorMargin+8+cursorDelta*(col-1), yc: extraMargin+cursorMargin+8+cursorDelta*(table.rows-row)] END; PaintCursorMenu: ViewerClasses.PaintProc = TRUSTED BEGIN DoIt: ENTRY PROC [table: REF CursorTable] = TRUSTED INLINE BEGIN row, col, ix, xc, yc: INTEGER; EraseTheEntry: PROC = TRUSTED BEGIN context.SetColor[Graphics.white]; context.DrawBox [[xmin: xc-cursorHalfDelta, ymin: yc-cursorHalfDelta, xmax: xc+cursorHalfDelta, ymax: yc+cursorHalfDelta]]; END; PaintTheEntry: PROC = TRUSTED BEGIN context.SetCP[x: xc, y: yc]; context.SetColor[Graphics.black]; GraphicsOps.DrawBitmap [self: context, bitmap: table.cursor[ix].bitmap, x: 0, y: 0, w: 16, h: 16, xorigin: 8, yorigin: 8]; IF table.current = ix AND table.showCurrent THEN {Graphics.SetCP[context, xc-11, yc-10]; Graphics.DrawTo[context, xc-11, yc+11]; Graphics.DrawTo[context, xc+10, yc+11]; Graphics.DrawTo[context, xc+10, yc-10]; Graphics.DrawTo[context, xc-11, yc-10]; table.framed _ ix} ELSE IF table.framed = ix THEN {table.framed _ -1}; IF table.candidate = ix THEN {[] _ Graphics.SetPaintMode[context, invert]; Graphics.DrawBox [context, [xmin: xc-9, ymin: yc-9, xmax: xc+9, ymax: yc+9]]; [] _ Graphics.SetPaintMode[context, opaque]; table.reversed _ ix} ELSE IF table.reversed = ix THEN {table.reversed _ -1}; IF table.cursor[ix].grayed THEN {[] _ Graphics.SetPaintMode[context, transparent]; GraphicsOps.DrawTexturedBox [context, [xmin: xc-9, ymin: yc-9, xmax: xc+9, ymax: yc+9], [104210B, 000000B, 021042B, 000000B, 104210B, 000000B, 021042B, 000000B, 104210B, 000000B, 021042B, 000000B, 104210B, 000000B, 021042B, 000000B]]; [] _ Graphics.SetPaintMode[context, opaque]}; END; RepaintEntry: PROC [which: INTEGER] = TRUSTED BEGIN ix _ which; row _ ix/table.cols+1; col _ ix-(row-1)*table.cols+1; [xc, yc] _ CursorCenter[table, row, col]; EraseTheEntry[]; IF table.cursor[ix].type # blank THEN PaintTheEntry[] END; IF whatChanged = NIL THEN -- global repaint BEGIN context.SetColor[Graphics.white]; context.DrawBox[context.GetBounds[]]; table.framed _ table.reversed _ -1; context.SetColor[Graphics.black]; FOR row IN [1..table.rows] DO FOR col IN [1..table.cols] DO ix _ (row-1)*table.cols + col-1; IF table.cursor[ix].type = blank THEN LOOP; [xc, yc] _ CursorCenter[table, row, col]; PaintTheEntry[] ENDLOOP ENDLOOP END ELSE BEGIN -- table.candidate or table.current changed, or cursor add/delete IF table.current # table.framed THEN {IF table.framed # -1 THEN RepaintEntry[table.framed]; IF table.current #-1 THEN RepaintEntry[table.current]}; IF table.candidate # table.reversed THEN {IF table.reversed # -1 THEN RepaintEntry[table.reversed]; IF table.candidate # -1 THEN RepaintEntry[table.candidate]}; IF ISTYPE[whatChanged, REF INTEGER] THEN {RepaintEntry[NARROW [whatChanged, REF INTEGER]^]} END END; IF self.iconic THEN RETURN; [ ] _ context.SetPaintMode[mode: opaque]; DoIt [NARROW [self.data]] END; FindCursor: INTERNAL PROC [table: REF CursorTable, name: ATOM] RETURNS [champ: INTEGER] = INLINE BEGIN FOR ix: INTEGER IN [0..table.cols*table.rows) DO IF table.cursor[ix].name = name AND table.cursor[ix].type # blank THEN {champ _ ix; RETURN}; ENDLOOP; champ _ -1 END; MousedCursor: INTERNAL PROC [table: REF CursorTable, x, y: INTEGER] RETURNS [champ: INTEGER] = BEGIN row, col: INTEGER; col _ (x-extraMargin+cursorDelta)/cursorDelta; -- must be careful with small negs! IF col <1 OR col > table.cols THEN RETURN [-1]; row _ table.rows + 1 - (y-extraMargin+cursorDelta)/cursorDelta; -- idem IF row <1 OR row > table.rows THEN RETURN [-1]; champ _ (row-1)*table.cols+col-1; END; SetParentCursor: INTERNAL PROCEDURE [menu: Viewer, table: REF CursorTable, newix: INTEGER, notify: BOOL _ TRUE] = BEGIN table.current _ newix; menu.parent.class.cursor _ table.cursor[newix].type; WindowManager.RestoreCursor; IF notify THEN ViewerOps.NotifyViewer[menu.parent, LIST[$Cursor, table.cursor[newix].name]]; IF table.cursor[newix].setFocus THEN InputFocus.SetInputFocus[self: menu.parent] END; ProcessMenuClicks: ViewerClasses.NotifyProc = TRUSTED BEGIN DoIt: ENTRY PROC [table: REF CursorTable] = TRUSTED BEGIN WHILE input#NIL DO WITH input.first SELECT FROM coords: TIPUser.TIPScreenCoords => BEGIN champ: INTEGER = MousedCursor [table, coords.mouseX, coords.mouseY]; IF champ = -1 THEN {table.candidate _ champ} ELSE IF table.cursor[champ].type#blank AND champ#table.candidate THEN {MessageWindow.Append [message: table.cursor[champ].help, clearFirst: TRUE]; table.candidate _ champ} ELSE {} -- keep last candidate END; atom: ATOM => BEGIN IF atom#$ButtonUp THEN RETURN WITH ERROR WhatWhatWhat; IF table.candidate # -1 THEN {SetParentCursor[self, table, table.candidate]; -- cursor help message was printed when table.candidate was set table.candidate _ -1} END; ENDCASE => RETURN WITH ERROR WhatWhatWhat; input _ input.rest ENDLOOP; END; DoIt [NARROW [self.data]]; ViewerOps.PaintViewer [viewer: self, hint: client, clearClient: FALSE, whatChanged: $Selection]; END; JunoCursorMenuClass: ViewerClasses.ViewerClass _ NEW [ViewerClasses.ViewerClassRec _ [paint: PaintCursorMenu, --called whenever the viewer should repaint notify: ProcessMenuClicks, --TIP input events tipTable: TIPUser.InstantiateNewTIPTable["JunoCursorMenu.Tip"], coordSys: bottom, cursor: bullseye ] ]; ViewerOps.RegisterViewerClass[$JunoCursorMenu, JunoCursorMenuClass] END. ’ JunoCursorMenuImpl.mesa Excised from JunoTop by Stolfi, March 29, 1984 8:49:34 am PST Last Edited by: Jorge Stolfi June 2, 1984 9:08:12 am PDT This module provides a `cursor menu' sub-viewer for Juno. Clicking one entry in this menu will set the cursor of the parent viewer to that entry, and simultaneously notify the parent about the change. - - - - PRIVATE TYPES AND CONSTANTS The table.candidate field is different from -1 only while the mouse is in the menu viewer with one of its buttons depressed, and points to the closest cursor to the current mouse coordinates. When the mouse button is released, the candidate cursor (if #-1) becomes definitive, and replaces table.current. - - - - PUBLIC PROCEDURES - - - - MENU PAINTER [self: Viewer, context: Graphics.Context, whatChanged: REF ANY, clear: BOOL] The PaintProc shows the table.current and table.candidate cursors by painting them framed and video-reversed, respectively. The cursors that currently have such highlights are given by table.framed and table.reversed. The PaintProc is constantly trying to update this selection feedback so as to make table.framed=table.current and table.reversed=table.candidate. However, painting may be delayed and/or occur asynchronously with the processing of mouse clicks, so the identities willnot hold all the time. Assumes row, col, ix, xc, yc are defined, current paintMode=opaque. Assumes row, col, ix are defined, entry is erased, current paintMode=opaque. Assumes current paintMode=opaque. Computes xc, yc, row, col, and erases entry. - - - - CLICK PROCESSING Finds index of cursor with given name. Returns -1 if not found Finds cursor entry selected by mousing at x,y. Doesn't check if the entry is blank. Returns champ=-1 if x,y falls on the margin (usually when the mouse rolls out of the menu with a button still down). Sets table.current _ newix, and changes cursor of parent's class to the given menu entry. Also notifies parent that cursor changed (if notify=TRUE), and diverts the keyboard input focus to the parent (if the cursor's setFocus=TRUE).. Crock - should change cursor of parent only, not of class... Does NOT repaint the affected portions of the menu. Does NOT print the cursor's help message. [self : Viewer, input : LIST OF REF ANY] Watches for clicks in the cursor menu, changes cursor of parent's class, sets parent's input focus, and notifies parent - - - - MODULE INITIALIZATION Ê ,˜šœ™Jšœ?™?Jšœ<™œŸœŸ?œ œœœ˜ŽJšœ±™±——šœ™šœÏnœœœœ"œœœ˜Ššœ˜Jšœœœ¶˜ÓJšœœœœœ,œœ œœ˜{JšœKŸœ&Ÿ#œZŸœ_œœ2œœ˜„—Jšœœ˜—Jšœžœœ ˜%Jšœžœœ ˜(Jšœž œœ ˜#šœ  œœœœœGœœœ˜¹šœ˜Jšœœ˜ š œ œœœ œ˜,JšœFœœœ œœœœœœœœœœgœ'œ„˜Þ—Jš œœUœœœ˜ˆ—Jšœœ˜—š œ  œœœœ˜7šœ˜Jšœœ˜ š œ œœœ œ˜,JšœœŸ$œœ œœœœ?œœ œ˜Ì—Jš œœUœœœ˜ˆ—Jšœœ˜—š œ  œœœœ œœ˜Lšœ˜Jšœœ˜ š œ œœœ œ˜,Jšœ"Ÿ$œœœœœœœ˜ï—JšœœUœ˜—Jšœœ˜—š œ œœœœ œœ˜Pšœ˜Jšœœ˜ š œ œœœ œ˜,Jš œ"Ÿ$œœœœœœ7˜ž—Jš œœTœœœ˜‡—Jšœœ˜——šœ™š œÐbn œœ œ!œ œ˜bJšœœœ|œ˜—šœžœ˜3JšœL™LJšœû™ûšœ˜š œ œœœ œœ˜;šœ˜Jšœœ˜šœ¡ œœ˜JšœD™Dšœ˜Jšœ¤˜¤—Jšœœ˜—šœ¡ œœ˜J™Mšœ˜JšœÆ˜ÆJš œœœíœœœ˜ÓJš œœÊœœœ˜ Jšœœæ˜…—Jšœœ˜—šœ¡ œœ œ˜.J™Ošœ˜Jšœ~œœ˜³—Jšœœ˜—šœœœœŸ˜,šœ˜Jšœœœœœœœ.œœœLœ˜‚—Jšœ˜—šœ˜šœœŸA˜HJš)œœœœœ!œœœ"œœœ#œœ!œœœœœœœœ˜¤—Jšœœ˜——Jšœœ˜—Jš œœ œœ2œ ˜`—Jšœœ˜——šœ™šœ  œ œ œœœ œ˜dJ™?Jšœœœœœœœœœœœœ˜Á—š œ¡ œ œ œœœ œ˜aJšœ/™/Jšœ%™%Jšœu™ušœ˜Jšœ œ˜Jšœ0Ÿ$œœœœGŸœœœœ(˜—Jšœœ˜—šœ œœ œœœ œœ˜ušœê™êJšœ=™=Jšœ6™6Jšœ,™,—šœ˜Jš œjœœ(œ&œœ/˜—Jšœœ˜—šœžœ˜7Jšœ(™(Jšœy™yšœ˜š œ œœœ œ˜4šœ˜šœœœ˜šœœ œ˜šœ#˜#šœ˜Jšœœ6˜EJšœœ œœœ œœQœ œŸ˜—Jšœœ˜—šœœ˜šœ˜Jšœœœœ˜7Jšœœœ6Ÿ@œ˜«—Jšœœ˜—Jšœœœ ˜+Jšœ˜——Jšœ ˜ —Jšœœ˜JšœœUœ˜‚——Jšœœ˜——šœ™Jšœ4œ@Ÿ,œ&Ÿœ}˜ÙJšœD˜D—Jšœœ˜J˜—…—+>Ú