DIRECTORY Real, Xl, XlCursor, XlPerDepth, XTk, XTkFriends, XTkScrollbar; XTkScrollbarImpl: CEDAR MONITOR IMPORTS Real, Xl, XlCursor, XlPerDepth, XTk, XTkFriends EXPORTS XTkScrollbar = BEGIN OPEN XTkScrollbar; stateChanged: PUBLIC ATOM ¬ $ScrollbarState; PerDepthRec: TYPE = RECORD [ inertCursor: Xl.Cursor ¬ Xl.nullCursor, exposeTQ: Xl.TQ ¬ NIL, --share tq, limit parallelism up, down, left, right, leftRight, upDown: Xl.Cursor ¬ Xl.nullCursor, grey1, grey2, grey3: Xl.Pixmap, gc1, gc2, gc3: Xl.GContext ]; perDHandle: XlPerDepth.Handle ¬ XlPerDepth.InstallHandle[InitDepthData]; GetDepthData: PROC [sd: Xl.ScreenDepth] RETURNS [dd: REF PerDepthRec] = { dd ¬ NARROW[XlPerDepth.GetData[perDHandle, sd]]; }; InitDepthData: XlPerDepth.InitProc = { dd: REF PerDepthRec ¬ NEW[PerDepthRec]; IF sd#NIL AND Xl.Alive[sd.screen.connection] THEN { screen: Xl.Screen ¬ sd.screen; c: Xl.Connection ¬ screen.connection; rd: Xl.Drawable ~ screen.root.drawable; stippleSpace1: REF ARRAY [0..3] OF CARD32 = NEW[ARRAY [0..3] OF CARD32 ¬ [088888888H, 044444444H, 022222222H, 0]]; stippleSpace2: REF ARRAY [0..3] OF CARD32 = NEW[ARRAY [0..3] OF CARD32 ¬ [0AAAAAAAAH, 055555555H, 0AAAAAAAAH, 055555555H]]; stippleSpace3: REF ARRAY [0..3] OF CARD32 = NEW[ARRAY [0..3] OF CARD32 ¬ [022222222H, 044444444H, 088888888H, 0]]; gc1: Xl.GContext ¬ dd.gc1 ¬ Xl.MakeGContext[c, rd]; gc2: Xl.GContext ¬ dd.gc2 ¬ Xl.MakeGContext[c, rd]; gc3: Xl.GContext ¬ dd.gc3 ¬ Xl.MakeGContext[c, rd]; dd.exposeTQ ¬ Xl.CreateTQ[]; dd.inertCursor ¬ XlCursor.SharedStandardCursor[c, tcross]; dd.up ¬ XlCursor.SharedStandardCursor[c, sbUpArrow]; dd.down ¬ XlCursor.SharedStandardCursor[c, sbDownArrow]; dd.left ¬ XlCursor.SharedStandardCursor[c, sbLeftArrow]; dd.right ¬ XlCursor.SharedStandardCursor[c, sbRightArrow]; dd.leftRight ¬ XlCursor.SharedStandardCursor[c, sbHDoubleArrow]; dd.upDown ¬ XlCursor.SharedStandardCursor[c, sbVDoubleArrow]; Xl.SetGCGrounds[gc: gc1, foreground: screen.blackPixel, background: screen.whitePixel]; Xl.SetGCGrounds[gc: gc2, foreground: screen.blackPixel, background: screen.whitePixel]; Xl.SetGCGrounds[gc: gc3, foreground: screen.blackPixel, background: screen.whitePixel]; dd.grey1 ¬ Xl.CreatePixmap[c, rd, [4, 4], sd.depth]; dd.grey2 ¬ Xl.CreatePixmap[c, rd, [4, 4], sd.depth]; dd.grey3 ¬ Xl.CreatePixmap[c, rd, [4, 4], sd.depth]; TRUSTED { Xl.PutImage[c: c, drawable: dd.grey1.drawable, gc: gc1, size: [4, 4], dest: [0, 0], base: LOOPHOLE[stippleSpace1], offx: 0, offy: 0, scanLineBytes: 4, bitsPerPixel: 1]; Xl.PutImage[c: c, drawable: dd.grey2.drawable, gc: gc2, size: [4, 4], dest: [0, 0], base: LOOPHOLE[stippleSpace2], offx: 0, offy: 0, scanLineBytes: 4, bitsPerPixel: 1]; Xl.PutImage[c: c, drawable: dd.grey3.drawable, gc: gc3, size: [4, 4], dest: [0, 0], base: LOOPHOLE[stippleSpace3], offx: 0, offy: 0, scanLineBytes: 4, bitsPerPixel: 1]; }; Xl.SetGCTile[gc: gc1, tile: dd.grey1]; Xl.SetGCTile[gc: gc2, tile: dd.grey2]; Xl.SetGCTile[gc: gc3, tile: dd.grey3]; Xl.SetGCFillStyle[gc: gc1, fillStyle: tiled]; Xl.SetGCFillStyle[gc: gc2, fillStyle: tiled]; Xl.SetGCFillStyle[gc: gc3, fillStyle: tiled]; }; RETURN [dd] }; defaultWidth: INT = 12; eventMask: Xl.SetOfEvent ~ [structureNotify: TRUE, buttonMotion: TRUE, exposure: TRUE, buttonPress: TRUE, buttonRelease: TRUE]; scrollbarClass: PUBLIC XTk.Class ¬ XTkFriends.CreateClass[[ key: $scrollbar, classNameHint: $Scrollbar, wDataNum: 1, configureLR: ScrollbarConfigureLR, initInstPart: ScrollbarInitInstPart, forgetScreenLR: ScrollbarForgetScreenLR, eventMask: eventMask, backgroundKey: $white ]]; CallRegRec: TYPE = RECORD [ scrollbar: Scrollbar, scrollProc: ScrollProc, clientData: REF ¬ NIL, tq: Xl.TQ ¬ NIL --NIL for rootTQ ]; CallData: TYPE = RECORD [ crrl: LIST OF CallRegRec, action: XTkScrollbar.Action, value: REAL ]; ScrollbarData: TYPE = REF ScrollbarRec; ScrollbarRec: TYPE = RECORD [ state: State ¬ [0, 0], --monitored direction: Direction ¬ vertical, iOwnButton: BOOL ¬ FALSE, initiatedAction: Action ¬ none, initiatedPos: Xl.Point ¬ [0, 0], leftMouseCursor: Xl.Cursor ¬ Xl.illegalCursor, middleMouseCursor: Xl.Cursor ¬ Xl.illegalCursor, rightMouseCursor: Xl.Cursor ¬ Xl.illegalCursor, inertCursor: Xl.Cursor ¬ Xl.illegalCursor, gc1: Xl.GContext ¬ NIL, gc2: Xl.GContext ¬ NIL, gc3: Xl.GContext ¬ NIL, reportedLength: INT ¬ 99999, --never zero; initiated with an impossible value. crrList: LIST OF CallRegRec ¬ NIL ]; GetInstData: PROC [scrollbar: Scrollbar] RETURNS [ScrollbarData] = INLINE { RETURN [NARROW[XTkFriends.InstPart[scrollbar, scrollbarClass]]]; }; fakeExpose: REF Xl.EventRep.local ~ NEW[Xl.EventRep.local]; ParentalSetState: PUBLIC PROC [scrollbar: Scrollbar, state: State, event: XTk.Event ¬ NIL] = { c: Xl.Connection ~ scrollbar.connection; sd: ScrollbarData ~ GetInstData[scrollbar]; EntrySetState[sd, state]; XTkFriends.CallNotifiers[scrollbar, stateChanged, sd, event]; IF Xl.Alive[c] THEN { dd: REF PerDepthRec ¬ GetDepthData[scrollbar.screenDepth]; tq: Xl.TQ ~ dd.exposeTQ; IF tq#NIL THEN Xl.Enqueue[tq: tq, proc: EventProc, data: scrollbar, event: fakeExpose]; }; }; PublicSetState: PUBLIC PROC [scrollbar: Scrollbar, action: Action, value: REAL, event: XTk.Event ¬ NIL] = { sd: ScrollbarData ~ GetInstData[scrollbar]; ForwardCallScrollProcs[sd, action, value, event]; }; GetDirection: PUBLIC PROC [scrollbar: Scrollbar] RETURNS [direction: Direction] = { sd: ScrollbarData ~ GetInstData[scrollbar]; RETURN [sd.direction] }; GetState: PUBLIC PROC [scrollbar: Scrollbar] RETURNS [state: State] = { sd: ScrollbarData ~ GetInstData[scrollbar]; state ¬ EntryGetState[sd]; }; EntrySetState: ENTRY PROC [sd: ScrollbarData, state: State] = { sd.state ¬ state }; EntryGetState: ENTRY PROC [sd: ScrollbarData] RETURNS [state: State] = { state ¬ sd.state }; ScrollbarConfigureLR: XTk.ConfigureProc = { existW: BOOL ¬ widget.actualMapping { sd.leftMouseCursor ¬ dd.up; sd.middleMouseCursor ¬ dd.upDown; sd.rightMouseCursor ¬ dd.down; SELECT TRUE FROM geometry.size.height>0 => sd.reportedLength ¬ geometry.size.height; widget.actual.size.height>0 => sd.reportedLength ¬ widget.actual.size.height; ENDCASE => sd.reportedLength ¬ 1; }; horizontal => { sd.leftMouseCursor ¬ dd.left; sd.middleMouseCursor ¬ dd.leftRight; sd.rightMouseCursor ¬ dd.right; SELECT TRUE FROM geometry.size.width>0 => sd.reportedLength ¬ geometry.size.width; widget.actual.size.width>0 => sd.reportedLength ¬ widget.actual.size.width; ENDCASE => sd.reportedLength ¬ 1; }; ENDCASE; IF Xl.IllegalCursor[widget.attributes.cursor] THEN widget.attributes.cursor ¬ sd.inertCursor ¬ dd.inertCursor ELSE sd.inertCursor ¬ widget.attributes.cursor; XTk.AddTemporaryMatch[widget, [proc: EventProc, handles: eventFilter, tq: dd.exposeTQ, data: widget], eventMask]; ForwardCallScrollProcs[sd, configure, sd.reportedLength, NIL]; --because window creation does not make configure events... }; XTkFriends.SimpleConfigureOneLevelLR[widget, geometry, mapping, reConsiderChildren]; }; CreateScrollbar: PUBLIC PROC [widgetSpec: XTk.WidgetSpec ¬ [], direction: Direction, state: State] RETURNS [scrollbar: Scrollbar] = { sd: ScrollbarData; SELECT direction FROM vertical => IF widgetSpec.geometry.size.width<=0 THEN widgetSpec.geometry.size.width ¬ defaultWidth; horizontal => IF widgetSpec.geometry.size.height<=0 THEN widgetSpec.geometry.size.height ¬ defaultWidth; ENDCASE; scrollbar ¬ XTk.CreateWidget[widgetSpec, scrollbarClass]; sd ¬ GetInstData[scrollbar]; sd.direction ¬ direction; sd.state ¬ state; }; IsScrollbar: PUBLIC PROC [widget: XTk.Widget] RETURNS [BOOL] = { RETURN [XTk.HasClass[widget, scrollbarClass]] }; SetScrollProc: PUBLIC PROC [scrollbar: Scrollbar, scrollProc: ScrollProc, clientData: REF ¬ NIL, tq: Xl.TQ ¬ NIL] = { sd: ScrollbarData ~ GetInstData[scrollbar]; crr: CallRegRec ~ [scrollProc: scrollProc, clientData: clientData, scrollbar: scrollbar]; IF sd=NIL OR scrollProc=NIL THEN ERROR; sd.crrList ¬ LIST[crr]; --only one registration ? }; ForwardCallScrollProcs: PROC [sd: ScrollbarData, action: XTkScrollbar.Action, value: REAL, event: Xl.Event ¬ NIL] = { FOR rl: LIST OF CallRegRec ¬ sd.crrList, rl.rest WHILE rl#NIL DO cd: REF CallData ~ NEW[CallData ¬ [crrl: rl, action: action, value: value]]; tq: Xl.TQ ¬ rl.first.tq; IF tq=NIL THEN {tq ¬ rl.first.scrollbar.rootTQ; IF tq=NIL THEN EXIT}; Xl.Enqueue[tq: tq, proc: ForwardedCallScrollProc, data: cd, event: event]; ENDLOOP }; ForwardedCallScrollProc: Xl.EventProcType = { cd: REF CallData ~ NARROW[clientData]; cd.crrl.first.scrollProc[scrollbar: cd.crrl.first.scrollbar, action: cd.action, value: cd.value, event: event, clientData: cd.crrl.first.clientData]; }; ScrollbarInitInstPart: XTk.InitInstancePartProc = { XTkFriends.AssignInstPart[widget, scrollbarClass, NEW[ScrollbarRec]]; }; ScrollbarForgetScreenLR: XTk.TerminateProc = { sd: ScrollbarData ~ GetInstData[widget]; sd.gc1 ¬ sd.gc2 ¬ sd.gc3 ¬ NIL; sd.leftMouseCursor ¬ sd.middleMouseCursor ¬ sd.rightMouseCursor ¬ sd.inertCursor ¬ Xl.illegalCursor; sd.iOwnButton ¬ FALSE; sd.initiatedAction ¬ none; }; eventFilter: Xl.EventFilter ~ Xl.CreateEventFilter[expose, buttonPress, buttonRelease, configureNotify]; EventProc: Xl.EventProcType = { ENABLE Xl.XError => GOTO oops; scrollbar: Scrollbar ~ NARROW[clientData]; sd: ScrollbarData ~ GetInstData[scrollbar]; SELECT event.type FROM Xl.EventCode.buttonPress => { bp: Xl.ButtonPressEvent ~ NARROW[event]; IF ~sd.iOwnButton AND Xl.SetButtonGrabOwner[bp.connection, bp.timeStamp, sd]=succeeded THEN { pm: Xl.PointerMapping ~ Xl.GetPointerMapping[bp.connection]; button: INT ~ (IF pm#NIL AND pm.leng>bp.button THEN pm[bp.button] ELSE bp.button); Forward: PROC [] = INLINE { sd.initiatedAction ¬ forward; SetCursor[scrollbar, sd.leftMouseCursor] }; Thumb: PROC [] = INLINE { sd.initiatedAction ¬ thumb; SetCursor[scrollbar, sd.middleMouseCursor] }; Backward: PROC [] = INLINE { sd.initiatedAction ¬ backward; SetCursor[scrollbar, sd.rightMouseCursor] }; sd.iOwnButton ¬ TRUE; sd.initiatedPos ¬ bp.pos; SELECT button FROM 1 => { SELECT TRUE FROM bp.state.control => Thumb[]; bp.state.shift => Backward[]; ENDCASE => Forward[]; }; 2 => Thumb[]; 3 => Backward[]; ENDCASE => {}; }; }; Xl.EventCode.buttonRelease => { IF sd.iOwnButton THEN { br: Xl.ButtonReleaseEvent ~ NARROW[event]; IF br.sameScreen AND (Inside[scrollbar, br.pos] OR Extents[scrollbar, br.pos, sd.direction]) THEN { action: XTkScrollbar.Action ¬ sd.initiatedAction; leng: REAL ~ MAX[sd.reportedLength, 1]; value: REAL; IF action=forward AND (ABS[sd.initiatedPos.x-br.pos.x]+ABS[sd.initiatedPos.y-br.pos.y])>15 THEN { distance: Xl.Point ¬ [br.pos.x-sd.initiatedPos.x, br.pos.y-sd.initiatedPos.y]; value ¬ ValueFromPos[sd, distance]; IF value>0 THEN {action ¬ backward} ELSE {value ¬ -value; action ¬ forward} } ELSE { value ¬ ValueFromPos[sd, br.pos]; }; ForwardCallScrollProcs[sd, action, value/leng, event]; }; SetCursor[scrollbar, sd.inertCursor]; sd.iOwnButton ¬ FALSE; }; }; Xl.EventCode.configureNotify => { cne: Xl.ConfigureNotifyEvent ~ NARROW[event]; value: INT ~ SELECT sd.direction FROM horizontal => cne.geometry.size.width, vertical => cne.geometry.size.height, ENDCASE => ERROR; IF value#sd.reportedLength THEN { sd.reportedLength ¬ MAX[value, 1]; ForwardCallScrollProcs[sd, configure, value, event]; } }; Xl.EventCode.expose => { expose: Xl.ExposeEvent = NARROW[event]; IF expose.count<=0 THEN ScrollbarRepaint[scrollbar, sd]; }; Xl.EventCode.local => { IF event=fakeExpose THEN ScrollbarRepaint[scrollbar, sd]; }; ENDCASE => {}; EXITS oops => {}; }; ValueFromPos: PROC [sd: ScrollbarData, pos: Xl.Point] RETURNS [p: INT] = INLINE { SELECT sd.direction FROM horizontal => {p ¬ pos.x}; vertical => {p ¬ pos.y}; ENDCASE; }; Inside: PROC [w: XTk.Widget, p: Xl.Point] RETURNS [BOOL] = { RETURN [p.x>=0 AND p.x<=w.actual.size.width AND p.y>=0 AND p.y<=w.actual.size.height] }; Extents: PROC [w: XTk.Widget, p: Xl.Point, direction: Direction] RETURNS [BOOL] = { SELECT direction FROM horizontal => RETURN[p.x<=0 OR p.x>=w.actual.size.width]; vertical => RETURN[p.y<=0 OR p.y>=w.actual.size.height]; ENDCASE => ERROR; }; SetCursor: PROC [widget: XTk.Widget, cursor: Xl.Cursor] = { attributes: Xl.Attributes; attributes.cursor ¬ cursor; Xl.ChangeWindowAttributes[widget.connection, widget.window, attributes]; Xl.Flush[widget.connection]; }; ScrollbarRepaint: PROC [scrollbar: Scrollbar, sd: ScrollbarData] = { cheat: INT ~ 3; c: Xl.Connection ~ scrollbar.connection; w: Xl.Window ~ scrollbar.window; sz: Xl.Size ~ scrollbar.actual.size; state: State ¬ sd.state; IF scrollbar.fastAccessAllowed#ok OR scrollbar.state#realized THEN RETURN; IF sz.height<=0 OR sz.width<=0 THEN RETURN; state.start ¬ MIN[MAX[state.start, 0.0], 1.0]; state.next ¬ MIN[MAX[state.start, state.next], 1.0]; SELECT sd.direction FROM horizontal => { iLength: INT ~ scrollbar.actual.size.width; iStart: INT ¬ Real.Round[iLength*state.start]; iNext: INT ¬ Real.Round[iLength*state.next]; iStart ¬ MAX[MIN[iStart, iLength-cheat], 0]; iNext ¬ MIN[iLength, MAX[iStart+cheat, iNext]]; IF iStart>0 THEN Xl.FillRectangle[c, w, sd.gc1, [0, 0], [iStart, sz.height]]; IF iNext>iStart THEN Xl.FillRectangle[c, w, sd.gc2, [iStart, 0], [iNext-iStart, sz.height]]; IF iLength>iNext THEN Xl.FillRectangle[c, w, sd.gc3, [iNext, 0], [iLength-iNext, sz.height]]; }; vertical => { iLength: INT ~ scrollbar.actual.size.height; iStart: INT ¬ Real.Round[iLength*state.start]; iNext: INT ¬ Real.Round[iLength*state.next]; iStart ¬ MAX[MIN[iStart, iLength-cheat], 0]; iNext ¬ MIN[iLength, MAX[iStart+cheat, iNext]]; IF iStart>0 THEN Xl.FillRectangle[c, w, sd.gc1, [0, 0], [sz.width, iStart]]; IF iNext>iStart THEN Xl.FillRectangle[c, w, sd.gc2, [0, iStart], [sz.width, iNext-iStart]]; IF iLength>iNext THEN Xl.FillRectangle[c, w, sd.gc3, [0, iNext], [sz.width, iLength-iNext]]; }; ENDCASE; Xl.Flush[c, TRUE]; }; END. v XTkScrollbarImpl.mesa Copyright Σ 1990, 1991, 1992 by Xerox Corporation. All rights reserved. Created by Christian Jacobi, October 22, 1990 5:38:56 pm PDT Christian Jacobi, October 8, 1992 10:04 am PDT ------------------------------------------------------ Protected: ENTRY PROC [sd: ScrollbarData, crr: CallRegRec] = { sd.crrList ¬ CONS[crr, sd.crrList] --multiple registration ? }; --dispatch actions onto right tq --Large movement with left mouse down: assume pen instead of mouse --Invert user interface for pen --Assume regular mouse --cheat to keep middle range visible... --cheat to keep middle range visible... Κl–(cedarcode) style•NewlineDelimiter ™code™Kšœ Οeœ=™HKšœ<™Kšœ žœ,™=Kšœ™—K˜+K˜ZKš žœžœžœ žœžœžœ˜'Kšœ žœ ˜2Kšœ˜K˜—šŸœžœ9žœžœ˜uK™ š žœžœžœ"žœžœž˜@Kšœžœ žœ6˜LKšœžœ˜Kšžœžœžœ"žœžœžœžœ˜EK˜JKšž˜—K˜K˜—šŸœ˜-Kšœžœ žœ ˜&K˜•K˜—K˜šŸœ˜3Kšœ2žœ˜EK˜K˜—šŸœ˜.K˜(Kšœžœ˜ Kšœd˜dKšœžœ˜Kšœ˜Kšœ˜K˜—K˜hšŸ œ˜Kšžœžœ˜Kšœžœ ˜*K˜+šžœ ž˜šœ˜Kšœžœ˜(šžœžœBžœ˜]K˜