GGUserImpl.mesa
Copyright Ó 1986, 1989, 1991, 1992 by Xerox Corporation. All rights reserved.
Contents: Routines for reading the Gargoyle entries in the user profile and setting Gargoyle options. Also, procedures which handle user actions (menu buttons and mouse events).
Kurlander August 24, 1986 1:00:26 pm PDT
Goodisman, August 11, 1989 4:59:38 pm PDT
Pier, November 4, 1992 3:36 pm PST
Bier, August 20, 1993 7:12 pm PDT
Doug Wyatt, April 20, 1992 12:24 pm PDT
DIRECTORY
Atom, BasicTime, BiScrollers, CodeTimer, Convert, Feedback, GGActive, GGBasicTypes, GGControlPanelTypes, GGCoreOps, GGCoreTypes, GGEmbedTypes, GGEvent, GGFont, GGInterfaceTypes, GGModelTypes, GGScrollMonitor, GGSlice, GGState, GGTransform, GGUserInput, GGUserProfile, GGViewerOps, GGWindow, GGWorld, Imager, ImagerTransformation, InputFocus, IO, List, Menus, Process, Real, RealFns, RefTab, Rope, ScreenCoordsTypes, SlackProcess, TIPUser, UserProfile, Vector2, ViewerClasses;
GGUserImpl:
CEDAR
MONITOR
IMPORTS Atom, BasicTime, BiScrollers, CodeTimer, Convert, Feedback, GGActive, GGCoreOps, GGEvent, GGFont, GGScrollMonitor, GGSlice, GGState, GGTransform, GGUserInput, GGViewerOps, GGWindow, ImagerTransformation, InputFocus, IO, List, Process, RealFns, RefTab, Rope, SlackProcess, UserProfile
EXPORTS GGInterfaceTypes, GGUserInput, GGUserProfile = BEGIN
Camera: TYPE = GGModelTypes.Camera;
ControlsObj: PUBLIC TYPE = GGControlPanelTypes.ControlsObj; -- exported to GGInterfaceTypes
EmbedDataObj: PUBLIC TYPE = GGEmbedTypes.EmbedDataObj; -- exported to GGInterfaceTypes
Event: TYPE = GGCoreTypes.Event;
EventListt: TYPE = GGCoreTypes.EventListt;
External: TYPE = GGUserInput.External;
ExternalRec: TYPE = GGUserInput.ExternalRec;
FeatureData: TYPE = GGModelTypes.FeatureData;
FontData: TYPE = GGModelTypes.FontData;
GGData: TYPE = GGInterfaceTypes.GGData;
Point: TYPE = GGBasicTypes.Point;
RawInputHandlerProc: TYPE = GGUserInput.RawInputHandlerProc;
Scene: TYPE = GGModelTypes.Scene;
SlackHandle: TYPE = SlackProcess.SlackHandle;
Slice: TYPE = GGModelTypes.Slice;
SliceDescriptor: TYPE = GGModelTypes.SliceDescriptor;
Transformation: TYPE = ImagerTransformation.Transformation;
UserInputProc: TYPE = GGUserInput.UserInputProc;
VEC: TYPE = Imager.VEC;
XVEC: TYPE = RECORD [x, y: REAL];
Viewer: TYPE = ViewerClasses.Viewer;
Problem:
PUBLIC
SIGNAL [msg: Rope.
ROPE] = Feedback.Problem;
externalCV:
CONDITION;
ExternalRec is used by applications external to Gargoyle to return results to those applications. Clients should call EventNotify with a registered external proc name and a REF to an ExternalRec on a two-element event list. Client should then call WaitExternal in a loop checking their particular "valid" bit. Gargoyle completes the desired action, puts results in "results", and sets "valid" to TRUE. Gargoyle external procs will call BroadcastExternal when actions complete.
WaitExternal:
PUBLIC ENTRY PROC [External] = {
WAIT externalCV;
};
BroadcastExternal:
PUBLIC ENTRY PROC [External] = {
BROADCAST externalCV;
};
NotifyExternal:
PUBLIC ENTRY PROC [External] = {
NOTIFY externalCV;
};
Before the Queue (done by InputNotifier, Menu Process or Playback Process)
From Buttons and Menus
EventNotify:
PUBLIC PROC [clientData:
REF
ANY, event:
LIST
OF
REF
ANY] = {
ggData: GGData ¬ NARROW[clientData];
Used by several menu classes as the procedure to call when a menu click occurs.
ProcessAndQueueEvent[event, ggData];
};
UnQueuedEventNotify:
PUBLIC PROC [clientData:
REF
ANY, event:
LIST
OF
REF
ANY] = {
Used by several menu classes as the procedure to call when a menu click occurs.
ggData: GGData ~ NARROW[clientData];
atom: ATOM ~ NARROW[event.first];
regEvent: RegisteredEvent ~ FetchAction[atom];
IF regEvent=
NIL
THEN {
NotYetImplementedMessage[atom, ggData];
}
ELSE {
event ¬ GetAnyArguments[event, regEvent, ggData];
TRUSTED {Process.Detach[FORK regEvent.eventProc[ggData, event] ]; };
};
};
From Playback Scripts
PlayAction:
PUBLIC PROC [clientData:
REF
ANY, event:
LIST
OF
REF
ANY] = {
ggData: GGData ¬ NARROW[clientData];
IF
event.first = $Version OR
event.first = $Store OR
event.first = $Save OR
event.first = $ToIP OR
event.first = $ToIPScreen OR
event.first = $ToIPLit OR
event.first = $IPToTioga OR
event.first = $IPToTiogaBordered OR
event.first = $IPToTiogaFit OR
event.first = $IPToTiogaBorderedAndFit OR
event.first = $IPToTiogaAlt OR
event.first = $StuffToTioga OR
event.first = $StuffToFile OR
event.first = $MergeFromTioga OR
event.first = $StuffToTiogaBordered OR
event.first = $StuffToFileAlt OR
event.first = $GrabFromTioga OR
event.first = $StuffToTiogaFit OR
event.first = $GetFromTioga OR
event.first = $StuffToTiogaBorderedAndFit OR
event.first = $MergeFromGargoyle OR
event.first = $StuffToTiogaAlt
THEN RETURN;
ProcessAndQueueEvent[event, ggData];
};
Regular Input Notification
gRawInputHandler: GGUserInput.RawInputHandlerProc ¬ GGActive.ActiveInputHandler;
InputNotify:
PUBLIC ViewerClasses.NotifyProc = {
Called by the TIP table machinery when an action is received from mouse or keyboard. Self is an ActionArea.
ggData: GGData ~ NARROW[BiScrollers.ClientDataOfViewer[self]];
notify:
PROC [input:
LIST
OF
REF] ~ {
InputFocus.
SetInputFocus[ggData.controls.actionArea];
Problem: If a gargoyle scene changes from active to inactive due to a queued action, the length of the queue will determine when Gargoyle gets the input focus.
ProcessAndQueueEvent[input, ggData];
};
gRawInputHandler[self, ggData, input, notify];
};
BiScrollerInputNotify:
PUBLIC
PROC [ggData: GGData, event:
LIST
OF
REF] = {
An action has been received from BiScroller (e.g. from the ScrollBar).
BiScrollerQueue:
PROC [event:
LIST
OF
REF
ANY, ggData: GGData] = {
first: ATOM ¬ NARROW[event.first];
IF first=$Shift
THEN {
vec: REF VEC ← NARROW[event.rest.first]; -- Shift VECTOR
xVec: REF XVEC ¬ NEW[XVEC ¬ [vec.x, vec.y] ]; -- put it in a record different from type VEC
event ¬ LIST[$Shift, xVec];
};
ProcessAndQueueEvent[event, ggData];
};
IF
ISTYPE[event.first,
LIST
OF
REF]
THEN {
FOR list:
LIST
OF
REF ← event, list.rest
UNTIL list =
NIL
DO
sublist: REF ¬ list.first;
IF ISTYPE[sublist, LIST OF REF] THEN BiScrollerQueue[NARROW[sublist], ggData];
ENDLOOP;
}
ELSE BiScrollerQueue[event, ggData];
};
finishText: LIST OF REF ANY ~ LIST[$SawTextFinish];
finishMouse:
LIST
OF
REF
ANY ~
LIST[$SawMouseFinish];
ProcessAndQueueEvent:
PROC [event:
LIST
OF
REF
ANY, ggData: GGData] = {
The event will be in one of two forms:
1) LIST[REF CHAR].
2) LIST[ATOM, ... ].
IF ggData.embed.beingBorn THEN RETURN; -- don't handle events while the window is being created
WITH event.first
SELECT
FROM
refChar:
REF
CHAR => {
Need our own copy of the ref and char, because the system reuses the storage.
myRefChar: REF CHAR ~ NEW[CHAR ¬ refChar];
event ¬ LIST[$AddChar, myRefChar];
};
atom:
ATOM => {
regEvent: RegisteredEvent ~ FetchAction[atom];
IF regEvent=NIL THEN NotYetImplementedMessage[atom, ggData]
ELSE {
atomName: Rope.ROPE ~ Atom.GetPName[atom];
startAction:
BOOL ~ Rope.Equal[Rope.Substr[atomName, 0, 5], "Start",
TRUE];
DKW: Yecch! Maybe the RegisteredEvent could contain this information.
IF startAction
THEN
QueueInput[ggData, finishText]; -- need this before selection changes
IF regEvent.causeMouseEventsToComplete
THEN
QueueInput[ggData, finishMouse];
event ¬ GetAnyArguments[event, regEvent, ggData];
IF atom = $OneScroll
THEN {
IF event.rest # NIL THEN
BEGIN
x, y: INTEGER ¬ 0;
WITH event.rest.first
SELECT
FROM
change:
REF Imager.
VEC => {
GGScrollMonitor.ConcatToDue[ggData, ImagerTransformation.Translate[[change.x, change.y]] ];
GOTO EasyCase;
};
i: REF INTEGER => x ¬ i;
ENDCASE;
IF event.rest.rest #
NIL
THEN {
WITH event.rest.rest.first
SELECT
FROM
i: REF INTEGER => y ¬ i;
ENDCASE;
};
GGScrollMonitor.ConcatToDue[ggData, ImagerTransformation.Translate[[x, y]] ];
END;
}
ELSE
IF atom = $OneZoom
THEN {
originViewer: Point;
x: INTEGER ¬ 0;
fx, scalar: REAL;
viewport: Imager.Rectangle ¬ GGState.GetViewport[ggData];
originViewer ¬ [viewport.x+(viewport.w/2.0), viewport.y+(viewport.h/2.0)];
IF event.rest #
NIL
THEN {
WITH event.rest.first
SELECT
FROM
i: REF INTEGER => x ¬ i;
ENDCASE;
};
fx ¬ x; -- convert to float
scalar ¬ RealFns.Power[base: 2.0, exponent: fx/50.0]; -- fifty clicks of the track ball gives a factor of two
Feedback.PutFL[ggData.router, oneLiner, $Typescript, "[%g %g: %g] ", LIST[ [real[originViewer.x]], [real[originViewer.y]], [real[scalar]] ]];
GGScrollMonitor.ConcatToDue[ggData, GGTransform.ScaleAboutPoint[originViewer, scalar] ];
};
};
};
ENDCASE => ERROR;
QueueInput[ggData, event, NIL];
};
Timestamp: TYPE = REF TimestampObj;
TimestampObj:
TYPE =
RECORD [
startTime: CARD32
];
timeQueue: BOOL ¬ FALSE;
QueueInput:
PROC [ggData: GGData, event:
LIST
OF
REF
ANY, optimizeHint:
REF
ANY ¬
NIL] ~ {
handle: SlackHandle ~ ggData.slackHandle;
GGEvent.PrintAllInput[ggData, event]; -- KAP
IF timeQueue THEN event ¬ List.Nconc1[event, NEW[TimestampObj ¬ [NowInMilliseconds[]]] ]; -- for timing how much time is spent on the queue
SlackProcess.QueueAction[handle, Dispatch, event, ggData, optimizeHint];
};
GetAnyArguments:
PROC [event:
LIST
OF
REF
ANY, regEvent: RegisteredEvent, ggData: GGData]
RETURNS [newEvent:
LIST
OF
REF
ANY] = {
atom: ATOM ¬ NARROW[event.first];
SELECT regEvent.argType
FROM
none => newEvent ¬ event;
DKW: REF VEC values come from Biscrollers, which treats them as immutable, so the following is unnecessary. Maybe this code is left over from pre-Biscrollers days; TIPScreenCoords values straight from TIP are mutable, and would have to be copied.
none => {
tail: LIST OF REF ANY;
Copy all of the VECs since the TIP table reuses the storage.
tail ← newEvent ← CONS[event.first, NIL];
FOR list: LIST OF REF ANY ← event.rest, list.rest UNTIL list = NIL DO
IF ISTYPE[list.first, REF VEC] THEN {
mousePlace: REF VEC ← NARROW[list.first];
tail.rest ← CONS[NEW[VEC ← [mousePlace.x, mousePlace.y]], NIL];
}
ELSE tail.rest ← CONS[list.first, NIL];
tail ← tail.rest;
ENDLOOP;
};
rope => newEvent ¬ CheckForSelectedRope[atom, event];
rope2 => newEvent ¬ CheckForSelectedRope2[atom, event];
refReal => newEvent ¬ CheckForSelectedReal[atom, event];
refInt => newEvent ¬ CheckForSelectedInt[atom, event];
refCard => newEvent ¬ CheckForSelectedCard[atom, event];
refExt => newEvent ¬ CheckForActualExt[atom, event];
ENDCASE => ERROR;
};
RegisterRawInputHandler:
PUBLIC
PROC [rawInputHandler: RawInputHandlerProc] = {
gRawInputHandler ¬ rawInputHandler;
};
After the Queue (Done by SlackProcess)
NowInMilliseconds:
PROC
RETURNS [
CARD32] = {
RETURN[(BasicTime.PulsesToMicroseconds[BasicTime.GetClockPulses[]]+500)/1000];
};
PrintUserTrace:
PROC [ggData: GGInterfaceTypes.GGData, eventProc: GGUserInput.UserInputProc, event:
LIST
OF
REF
ANY] ~ {
procTV: AMTypes.TV ← AMBridge.TVForProc[eventProc];
moduleTV: AMTypes.TV ← AMTypes.GlobalParent[procTV];
procName: Rope.ROPE ← AMTypes.TVToName[procTV];
moduleName: Rope.ROPE ← AMTypes.TVToName[moduleTV];
procName: Rope.ROPE ¬ "?";
moduleName: Rope.ROPE ¬ "?";
thisArgName: Rope.ROPE;
allArgs: Rope.ROPE;
FOR list:
LIST
OF
REF
ANY ¬ event, list.rest
UNTIL list =
NIL
DO
thisArgName ¬ IO.PutFR1["%g", [refAny[list.first]] ];
IF allArgs = NIL THEN allArgs ¬ thisArgName
ELSE allArgs ¬ Rope.Cat[allArgs, ", ", thisArgName];
ENDLOOP;
Feedback.PutFL[ggData.router, oneLiner, $Typescript, "%g.%g[%g]", LIST[[rope[moduleName]], [rope[procName]], [rope[allArgs]]] ];
};
Dispatch:
PROC [clientData:
REF
ANY, inputAction:
REF] = {
ggData: GGData ~ NARROW[clientData];
atom: ATOM;
startTime, endTime: CARD32;
startTimeRef: REF;
regEvent: RegisteredEvent;
event: LIST OF REF ¬ NARROW[inputAction];
UpdateCoords:
PROC [input:
LIST
OF
REF]
RETURNS [o:
LIST
OF
REF] = {
i, l: LIST OF REF;
viewerToClient: Transformation ← GGState.GetBiScrollersTransforms[ggData].viewerToClient;
IF input.first = $AlignFracs OR (input.rest # NIL AND input.rest.first = $AlignFracs) THEN RETURN[input]; -- because BiScrollers uses REF Vector2.VEC for the shift amount
FOR i ¬ input, i.rest
WHILE i #
NIL
DO
l ¬ IF l = NIL THEN o ¬ CONS[i.first, NIL] ELSE l.rest ¬ CONS[i.first, NIL];
WITH l.first
SELECT
FROM
z: TIPUser.TIPScreenCoords => {
l.first ¬ NEW [Vector2.VEC ¬ viewerToClient.Transform[[z.mouseX, z.mouseY]]];
};
v:
REF Vector2.
VEC => {
l.first ← NEW [Vector2.VEC ← viewerToClient.Transform[v^]];
};
z:
REF
XVEC => {
l.first ← NEW [Vector2.VEC ← [z.x, z.y] ];
};
ENDCASE;
ENDLOOP;
};
event ¬ UpdateCoords[event]; -- perform the BiScrollers transform
IF timeQueue
THEN {
startTimeRef ¬ List.NthElement[event, -1];
WITH startTimeRef
SELECT
FROM
timeStamp: Timestamp => {
startTime ¬ timeStamp.startTime;
event ¬ List.DRemove[startTimeRef, event];
endTime ¬ NowInMilliseconds[];
CodeTimer.SetIntMilliseconds[$TimeOnQueue, startTime, endTime, $Gargoyle];
};
ENDCASE;
};
atom ¬ NARROW[event.first];
regEvent ¬ FetchAction[atom];
IF regEvent=NIL THEN NotYetImplementedMessage[atom, ggData]
ELSE {
IF event.first=$Again
THEN {
IF
NOT GGCoreOps.NoEvents[ggData.lastEvents]
THEN {
FOR list:
LIST
OF Event ¬ ggData.lastEvents.list, list.rest
UNTIL list =
NIL
DO
thisEvent: LIST OF REF ANY ~ list.first;
thisAtom: ATOM ~ NARROW[thisEvent.first];
thisRegEvent: RegisteredEvent ~ FetchAction[thisAtom];
IF thisRegEvent=
NIL
THEN NotYetImplementedMessage[thisAtom, ggData]
ELSE thisRegEvent.eventProc[ggData, thisEvent];
ENDLOOP;
}
ELSE {}; -- there is nothing to do again
}
ELSE {
SELECT GetEventClass[atom]
FROM
select => ggData.justSawSelect ¬ TRUE;
action => {
IF ggData.justSawSelect
THEN {
GGCoreOps.FlushEventListt[ggData.lastEvents];
ggData.justSawSelect ¬ FALSE;
};
GGCoreOps.AppendEvent[event, ggData.lastEvents];
};
suppress => {}; -- $During. Ignore it.
neutral => {
IF ggData.justSawSelect THEN {} -- this is the during or end of a select operation
ELSE GGCoreOps.AppendEvent[event, ggData.lastEvents];
};
ENDCASE => ERROR;
IF GGUserInput.GetUserTraceOn[]
THEN
PrintUserTrace[ggData, regEvent.eventProc, event];
regEvent.eventProc[ggData, event];
};
};
};
EventClass: TYPE = {select, neutral, action, suppress};
GetEventClass:
PROC [atom:
ATOM]
RETURNS [eventClass: EventClass] = {
IF ( -- oops. You can't suppress all of the Durings. You must not suppress the last one for a given operation. Yuk.
atom = $During
) THEN RETURN [suppress];
IF (
atom = $During OR
atom = $GuardUp OR
atom = $MouseUp OR
atom = $AllUp OR
atom = $SawTextFinish OR
atom = $SawMouseFinish
) THEN RETURN [neutral];
IF (
atom = $StartSelectWithBox OR
atom = $StartSelectJoint OR
atom = $StartExtSelectJoint OR
atom = $StartSelectSegment OR
atom = $StartExtSelectSegment OR
atom = $StartSelectTrajectory OR
atom = $StartExtSelectTrajectory OR
atom = $StartSelectTopLevel OR
atom = $StartExtSelectTopLevel OR
atom = $StartExtendSelection OR
atom = $StartDeselectJoint OR
atom = $StartDeselectSegment OR
atom = $StartDeselectTrajectory OR
atom = $StartDeselectTopLevel OR
atom = $CycleSelection OR
atom = $AreaSelectNew OR
atom = $AreaSelectNewAndDelete OR
atom = $SelectAll OR
atom = $AreaSelectDegenerate OR
atom = $SelectCoincident OR
atom = $SelectUnseeableSegs OR
atom = $SelectUnseeableObjs
) THEN RETURN [select];
IF (
atom = $StartCaretPos OR
atom = $StartAdd OR
atom = $StartBox OR
atom = $StartDrag OR
atom = $StartCopyAndDrag OR
atom = $StartAddAndDrag OR
atom = $StartRotate OR
atom = $StartScale OR
atom = $StartSixPoint
) THEN RETURN [action];
IF (
atom = $OneZoom OR
atom = $OneScroll
) THEN RETURN [suppress]; -- added November 4, 1992. KAP.
RETURN [action];
};
Utility Routines
NotYetImplementedMessage:
PROC [atom:
ATOM, ggData: GGData] = {
Feedback.Append[ggData.router, begin, $Warning, "User event "];
Feedback.Append[ggData.router, middle, $Warning, Atom.GetPName[atom]];
Feedback.Append[ggData.router, end, $Warning, " is not yet implemented"];
};
CheckForSelectedRope:
PROC [atom:
ATOM, event:
LIST
OF
REF
ANY]
RETURNS [newAction:
LIST
OF
REF
ANY] = {
IF event.rest =
NIL
THEN {
-- interactive call
r: Rope.ROPE ¬ GGViewerOps.GetSelectionContents[];
newAction ¬ LIST[atom, r];
}
ELSE
IF
ISTYPE[event.rest.first,
REF
TEXT]
THEN {
-- TIP table call
newAction ¬ CONS[atom, CONS[Rope.FromRefText[NARROW[event.rest.first]], event.rest.rest]];
}
ELSE newAction ¬ event; -- SessionLog call
};
CheckForSelectedRope2:
PROC [atom:
ATOM, event:
LIST
OF
REF
ANY]
RETURNS [newAction:
LIST
OF
REF
ANY] = {
Expect a rope as the second argument.
IF event.rest = NIL THEN ERROR;
IF event.rest.rest =
NIL
THEN {
-- interactive call
r: Rope.ROPE ¬ GGViewerOps.GetSelectionContents[];
newAction ¬ LIST[atom, event.rest.first, r];
}
ELSE
IF
ISTYPE[event.rest.rest.first,
REF
TEXT]
THEN {
-- TIP table call
newAction ¬ LIST[atom, event.rest.first, Rope.FromRefText[NARROW[event.rest.rest.first]], event.rest.rest.rest];
}
ELSE newAction ¬ event; -- SessionLog call
};
CheckForSelectedReal:
PROC [atom:
ATOM, event:
LIST
OF
REF
ANY]
RETURNS [newAction:
LIST
OF
REF
ANY] = {
rope: Rope.ROPE;
real: REAL;
IF event.rest =
NIL
THEN {
-- interactive call
rope ¬ GGViewerOps.GetSelectionContents[];
real ¬ Convert.RealFromRope[rope ! Convert.Error => {real ¬ Real.LargestNumber; CONTINUE}];
newAction ¬ LIST[atom, NEW[REAL ¬ real]];
}
ELSE
IF
ISTYPE[event.rest.first,
REF
TEXT]
THEN {
-- TIP table call
rope ¬ Rope.FromRefText[NARROW[event.rest.first]];
real ¬ Convert.RealFromRope[rope ! Convert.Error => {real ¬ Real.LargestNumber; CONTINUE}];
newAction ¬ LIST[atom, NEW[REAL ¬ real]];
}
ELSE newAction ¬ event; -- SessionLog call
};
CheckForSelectedCard:
PROC [atom:
ATOM, event:
LIST
OF
REF
ANY]
RETURNS [newAction:
LIST
OF
REF
ANY] = {
rope: Rope.ROPE;
card: CARD;
IF event.rest =
NIL
THEN {
-- interactive call
rope ¬ GGViewerOps.GetSelectionContents[];
card ¬ IO.GetCard[IO.RIS[rope] ! IO.EndOfStream, IO.Error => {card ¬ LAST[CARD]; CONTINUE}];
newAction ¬ LIST[atom, NEW[CARD ¬ card]];
}
ELSE
IF
ISTYPE[event.rest.first,
REF
CARD]
THEN {
-- TIP table call or SessionLog call
newAction ¬ event;
}
ELSE ERROR;
};
CheckForSelectedInt:
PROC [atom:
ATOM, event:
LIST
OF
REF
ANY]
RETURNS [newAction:
LIST
OF
REF
ANY] = {
rope: Rope.ROPE;
int: INT;
IF event.rest =
NIL
THEN {
-- interactive call
rope ¬ GGViewerOps.GetSelectionContents[];
int ¬ IO.GetInt[IO.RIS[rope] ! IO.EndOfStream, IO.Error => {int ¬ LAST[INT]; CONTINUE}];
newAction ¬ LIST[atom, NEW[INT ¬ int]];
}
ELSE {
WITH event.rest.first
SELECT
FROM
int: REF INT => newAction ¬ event; -- TIP table call or SessionLog call
card: REF CARD => newAction ¬ List.Append[LIST[atom, NEW[INT ¬ card]], event.rest.rest]; -- type coersion
ENDCASE => ERROR;
};
};
CheckForActualExt:
PROC [atom:
ATOM, event:
LIST
OF
REF
ANY]
RETURNS [newAction:
LIST
OF
REF
ANY] = {
IF event.rest=NIL OR NOT ISTYPE[event.rest.first, REF GGUserInput.ExternalRec] THEN ERROR;
newAction ¬ event;
};
RegisteredEvent: TYPE = REF RegisteredEventObj;
RegisteredEventObj:
TYPE =
RECORD [
eventProc: UserInputProc,
argType: GGUserInput.ArgumentType,
causeMouseEventsToComplete: BOOL
];
RegisterAction:
PUBLIC
PROC [atom:
ATOM, eventProc: UserInputProc, argType: GGUserInput.ArgumentType, causeMouseEventsToComplete:
BOOL ¬
TRUE, ensureUnique:
BOOL ¬
TRUE] = {
If the event has already been registered and ensureUnique is true, an error will be signalled. If the event has been registered and ensureUnique is FALSE, the action will be reregistered.
regEvent: RegisteredEvent ¬ NEW[RegisteredEventObj ¬ [eventProc, argType, causeMouseEventsToComplete]];
justInserted: BOOL ¬ RefTab.Insert[eventTable, atom, regEvent];
IF
NOT justInserted
THEN
IF ensureUnique THEN SIGNAL Problem[msg: IO.PutFR1["Event %g was already registered in Gargoyle's event table.", [rope[Atom.GetPName[atom]]]]]
ELSE [] ¬ RefTab.Replace[eventTable, atom, regEvent]; -- register again
[] ¬ RefTab.Replace[eventTable, atom, regEvent];
};
FetchAction:
PROC [atom:
ATOM]
RETURNS [RegisteredEvent] ~ {
WITH RefTab.Fetch[eventTable, atom].val
SELECT
FROM
regEvent: RegisteredEvent => RETURN[regEvent];
ENDCASE => RETURN[NIL];
};
GGUserProfile
LookAtProfile:
PUBLIC UserProfile.ProfileChangedProc = {
[reason: UserProfile.ProfileChangeReason]
gravExtent: REAL; -- in inches
defaultHistorySize: INT;
heuristics: BOOL ¬ UserProfile.Boolean[key: "Gargoyle.Heuristics", default: FALSE];
quickClickMode: BOOL ¬ UserProfile.Boolean[key: "Gargoyle.QuickClickEnable", default: FALSE];
useLatestIPVersion: BOOL ¬ UserProfile.Boolean[key: "Gargoyle.UseLatestIPVersion", default: FALSE];
autoOpenTypescript: BOOL ¬ UserProfile.Boolean[key: "Gargoyle.AutoOpenTypescript", default: TRUE];
autoOpenHistory: BOOL ¬ UserProfile.Boolean[key: "Gargoyle.AutoOpenHistory", default: TRUE];
autoScriptingOn: BOOL ¬ UserProfile.Boolean[key: "Gargoyle.AutoScriptingOn", default: FALSE];
separateControlPanel: BOOL ¬ UserProfile.Boolean[key: "Gargoyle.SeparateControlPanel", default: FALSE];
defaultIncludeIPBy: Rope.ROPE ¬ UserProfile.Token[key: "Gargoyle.DefaultIncludeIPBy", default: "Reference"];
holdThatTiger: BOOL ¬ UserProfile.Boolean[key: "Gargoyle.HoldThatTiger", default: FALSE];
newBoxesUnfilled: BOOL ¬ UserProfile.Boolean[key: "Gargoyle.NewBoxesUnfilled", default: FALSE];
defaultHistorySize ¬ Convert.IntFromRope[UserProfile.Token[key: "Gargoyle.DefaultHistorySize", default: "40"]
! Convert.Error => IF reason = syntax THEN {defaultHistorySize ¬ -1; CONTINUE;} ELSE REJECT];
gravExtent ¬ Convert.RealFromRope[UserProfile.Token[key: "Gargoyle.GravityExtent", default: "-1.0"]
! Convert.Error => IF reason = syntax THEN {gravExtent ¬ -1.0; CONTINUE;} ELSE REJECT];
IF gravExtent=-1.0 THEN gravExtent ¬ 25.0/72.0;
IF defaultHistorySize<1 THEN defaultHistorySize ¬ IF defaultHistorySize=0 THEN 1 ELSE 40;
SetDefaultHeuristics[on: heuristics];
SetDefaultGravityExtent[inches: gravExtent];
GGState.SetQuickClickMode[quickClickMode];
SetDefaultUseLatestIPVersion[useLatestIPVersion];
SetAutoOpenTypescript[autoOpenTypescript];
SetAutoOpenHistory[autoOpenHistory];
SetDefaultHistorySize[defaultHistorySize];
SetAutoScriptingOn[autoScriptingOn];
SetSeparateControlPanel[separateControlPanel];
SetHoldThatTiger[holdThatTiger];
SetNewBoxesUnfilled[newBoxesUnfilled];
SetDefaultIncludeIPByValue[Rope.Equal[defaultIncludeIPBy, "Value", FALSE]];
BEGIN
-- set the default default font
description: Rope.ROPE ¬ UserProfile.Token[key: "Gargoyle.DefaultDefaultFont", default: NIL];
descriptionStream: IO.STREAM;
defaultFont: FontData;
IF description =
NIL
THEN
description ¬ "xerox/xc1-2-2/helvetica [r1: 0.0 s: [10.0 10.0] r2: 0.0] 1.0 1.0";
formerly "xerox/pressfonts/helvetica-mrr [r1: 0.0 s: [10.0 10.0] r2: 0.0] 1.0 1.0"
descriptionStream ¬ IO.RIS[description];
defaultFont ¬ GGFont.ParseFontData[inStream: descriptionStream, literalP: TRUE, transformP: TRUE, storedSizeP: TRUE, designSizeP: TRUE];
SetDefaultDefaultFont[defaultFont];
END;
IF reason#firstTime THEN GGWindow.InitIcons[]; -- don't do this on first registering this proc
};
No matter how many Gargoyle viewers are present, there is only one MasterData.
MasterData: TYPE = REF MasterDataObj;
MasterDataObj:
TYPE =
RECORD [
defaultGravityExtent: REAL ¬ 25.0, -- in screen dots
defaultHeuristics: BOOL ¬ TRUE,
objectsBeingCopied: LIST OF REF ANY, -- for copying objects from viewer to viewer,
defaultDefaultFont: FontData,
autoOpenTypescript: BOOL ¬ TRUE,
autoOpenHistory: BOOL ¬ TRUE,
defaultHistorySize: INT ¬ 40,
autoScriptingOn: BOOL ¬ TRUE,
separateControlPanel: BOOL ¬ FALSE,
defaultIncludeIPByValue: BOOL ¬ FALSE,
holdThatTiger: BOOL ¬ FALSE,
newBoxesUnfilled: BOOL ¬ FALSE,
turboOn: BOOL ¬ FALSE,
userTraceOn: BOOL ¬ FALSE
];
SetUserTraceOn:
PUBLIC
PROC [on:
BOOL] = {
masterData.userTraceOn ¬ on;
};
GetUserTraceOn:
PUBLIC
PROC
RETURNS [on:
BOOL] = {
on ¬ masterData.userTraceOn;
};
SetDefaultHeuristics:
PUBLIC
PROC [on:
BOOL] = {
When a new viewer is created, should heuristics be turned on?
masterData.defaultHeuristics ¬ on;
};
GetDefaultHeuristics:
PUBLIC
PROC
RETURNS [on:
BOOL] = {
When a new viewer is created, should heuristics be turned on?
on ¬ masterData.defaultHeuristics;
};
SetDefaultGravityExtent:
PUBLIC
PROC [inches:
REAL] = {
When a new viewer is created, how strong should its gravity be?
masterData.defaultGravityExtent ¬ inches*72.0;
};
GetDefaultGravityExtent:
PUBLIC
PROC
RETURNS [
screenDots:
REAL] = {
When a new viewer is created, how strong should its gravity be?
screenDots ¬ masterData.defaultGravityExtent;
};
SetDefaultUseLatestIPVersion:
PUBLIC
PROC [useLatestIPVersion:
BOOL] = {
GGSlice.SetDefaultUseLatestIPVersion[useLatestIPVersion];
};
GetDefaultUseLatestIPVersion:
PUBLIC
PROC []
RETURNS [useLatestIPVersion:
BOOL] = {
RETURN[GGSlice.GetDefaultUseLatestIPVersion[]];
};
SetDefaultDefaultFont:
PUBLIC
PROC [font: FontData] = {
masterData.defaultDefaultFont ¬ font;
};
GetDefaultDefaultFont:
PUBLIC
PROC
RETURNS [font: FontData] = {
font ¬ masterData.defaultDefaultFont;
};
SetAutoOpenTypescript:
PUBLIC
PROC [autoOpenTypescript:
BOOL] = {
masterData.autoOpenTypescript ¬ autoOpenTypescript;
};
GetAutoOpenTypescript:
PUBLIC
PROC []
RETURNS [autoOpenTypescript:
BOOL] = {
autoOpenTypescript ¬ masterData.autoOpenTypescript;
};
SetAutoOpenHistory:
PUBLIC
PROC [autoOpenHistory:
BOOL] = {
masterData.autoOpenHistory ¬ autoOpenHistory;
};
GetAutoOpenHistory:
PUBLIC
PROC []
RETURNS [autoOpenHistory:
BOOL] = {
autoOpenHistory ¬ masterData.autoOpenHistory;
};
SetDefaultHistorySize:
PUBLIC PROC [defaultHistorySize:
INT] = {
masterData.defaultHistorySize ¬ defaultHistorySize;
};
GetDefaultHistorySize:
PUBLIC PROC
RETURNS [defaultHistorySize:
INT] = {
defaultHistorySize ¬ masterData.defaultHistorySize;
};
SetAutoScriptingOn:
PUBLIC
PROC [autoScriptingOn:
BOOL] = {
masterData.autoScriptingOn ¬ autoScriptingOn;
};
GetAutoScriptingOn:
PUBLIC
PROC []
RETURNS [autoScriptingOn:
BOOL] = {
autoScriptingOn ¬ masterData.autoScriptingOn;
};
SetSeparateControlPanel:
PUBLIC
PROC [separateControlPanel:
BOOL] = {
masterData.separateControlPanel ¬ separateControlPanel;
};
GetSeparateControlPanel:
PUBLIC
PROC []
RETURNS [separateControlPanel:
BOOL] = {
separateControlPanel ¬ masterData.separateControlPanel;
};
SetDefaultIncludeIPByValue:
PUBLIC
PROC [defaultIncludeIPByValue:
BOOL] = {
masterData.defaultIncludeIPByValue ¬ defaultIncludeIPByValue;
};
GetDefaultIncludeIPByValue:
PUBLIC
PROC []
RETURNS [defaultIncludeIPByValue:
BOOL] = {
defaultIncludeIPByValue ¬ masterData.defaultIncludeIPByValue;
};
SetHoldThatTiger:
PUBLIC
PROC [holdThatTiger:
BOOL] = {
masterData.holdThatTiger ¬ holdThatTiger;
};
GetHoldThatTiger:
PUBLIC
PROC []
RETURNS [holdThatTiger:
BOOL] = {
holdThatTiger ¬ masterData.holdThatTiger;
};
SetNewBoxesUnfilled:
PUBLIC
PROC [newBoxesUnfilled:
BOOL] = {
masterData.newBoxesUnfilled ¬ newBoxesUnfilled;
};
GetNewBoxesUnfilled:
PUBLIC
PROC []
RETURNS [newBoxesUnfilled:
BOOL] = {
newBoxesUnfilled ¬ masterData.newBoxesUnfilled;
};
masterData: MasterData;
eventTable: RefTab.Ref;
masterData ¬ NEW[MasterDataObj];
UserProfile.CallWhenProfileChanges[LookAtProfile];
eventTable ¬ RefTab.Create[255];
interval ← CodeTimer.CreateInterval[$UserInput];
CodeTimer.AddInt[interval, $Gargoyle];
END.