PipalMutate.mesa 
Copyright Ó 1988 by Xerox Corporation. All rights reserved.
Bertrand Serlet May 11, 1988 11:52:31 pm PDT
Louis Monier February 1, 1988 11:27:19 pm PST
Barth, January 29, 1988 6:12:15 pm PST
DIRECTORY Pipal, PipalOps, RefTab;
PipalMutate: CEDAR DEFINITIONS = BEGIN
Theory
This module defines how to mutate Pipal objects (while retaining immutability!).
Mutation Method
The mutation method defines how to make a mutant on a given object.
mutationMethod: Pipal.Method;
MutationProc: TYPE = PROC [object: Pipal.Object] RETURNS [mutant: Pipal.Object ← NIL];
HasMutationProc: PROC [object: Pipal.Object] RETURNS [BOOL];
Mutation: MutationProc;
Invokes mutation. No mutationMethod method => crash and burn.
EachChildProc: TYPE = PROC [path: PipalOps.Path, child: Pipal.Object] RETURNS [quit: BOOLFALSE];
EnumerateMutantChildren: PROC [root: Pipal.Object, each: EachChildProc] RETURNS [quit: BOOLFALSE];
Calls each editable child or grand child of root.
Successive calls to EnumerateMutantChildren with identical root, should produce same calls to each.
If a child answers the mutation message its decendants are not enumerated. If an object does not have an enumerate proc, it is ignored.
Calls each on root if root answers the mutation message (with path=NIL).
Pop Event
The pop event is called everytime one wants to replace the original object on which the mutant was called by the current object contained in the mutant.
For example, let us imagine there is an object X in some mutant E. At some point, the user "pushes" into X, and so creates a new mutant F. So far no event is generated (no "push" event). However at the "push" time, and only if E cares about future states of X, E registers a "pop watchdog" with all the relevant data. Typically this data will be: E, X, the path from E to X, F. Some editing goes on in F, and maybe the history list is flush and F does not even have a pointer to the original X. Then "pop" is performed in F with F possibly destroyed later, but that's another story. A pop event is now generated, with F and the new resulting object Y as arguments. All "pop watchdog" are waken up, and the relevant ones (the ones that were watching F, and not some other mutant) are called. Amongs them is "pop watchdog" that E registered. This watch dog "decides" whether it should perform a "pop new" or a "pop replace". If it is a "pop new", only the path pointing to X is replaced by a path pointing to Y. If it is a "pop replace" a general replace event is broadcast (basically, PipalOps.BroadcastReplace is called).
Fine point: it should be noted at that point, that nothing prevents several editors to push into the same object X and to have the same or different editors F. Also, nothing prevents the choice "pop new" versus "pop replace" to be made at push time, or differently for each E. All those mode of operations are left to the policy makers ...
popEvent: READONLY ATOM;
For that event, arg1 is an mutant, and arg2 is the resulting "new" object.
BroadcastPop: PROC [mutant: Pipal.Object];
Recover the current object and send a pop event.
Operations
NotifyReplace: PROC [mutant: Pipal.Object];
-- BS ???? --
Call PipalOps.NotifyMapping with the version of the object last notified mapped to the current version of the object. This stacks an event, distinguished by the message ref, which, if undone, reverts to the previous state by calling PipalOps.NotifyMapping with the reverse mapping.
Upon replace the mutant ReplaceNotifyProc, if this is not the originating mutant and the propagated object is reachable from mutant.object or mutant.state, stacks an event which if undone reverts the object of that mutant back to what it was before the global replace. Upon unreplace the mutant ReplaceNotifyProc unstacks until back to before the global replace with everything going onto the redo list.
Commands
This section defines the framework for editing commands.
Currently, no queeuing or aborting mechanism is available. However it might be needed in the future (e.g. interpress command).
CommandProc: TYPE = PROC [mutant: Pipal.Object, name: ATOM, arguments: LIST OF REFNIL, issuer: REFNIL, registrationData: REFNIL] RETURNS [resultType: ResultType ← none, result: Pipal.Object ← NIL, new: Pipal.Object];
mutant is the receiver of the command;
arguments are the command arguments (e.g. mouse position);
issuer is the issuer of the command, typically the viewer from where the command started;
registrationData is useful to share command procs (move left and move right);
result is relevant information for the display (e.g area selection, area of change);
ResultType: TYPE = {none, object, selectionArea, changedArea};
none => result is NIL; display should display all
object => result is a Pipal.Object; e.g. the selection for copy between different editors
selectionArea => result is a PipalObject with an area message outlining the current selection
changedArea => result is a PipalObject with an area message; display should display these areas
if result=Pipal.void, nothing is redisplayed
RegisterCommand: PROC [class: Pipal.Class, name: ATOM, command: CommandProc, registrationData: REFNIL];
class is the class that catches the message.
FetchCommand: PROC [mutant: Pipal.Object, name: ATOM] RETURNS [command: CommandProc ← NIL, registrationData: REFNIL];
If mutant.state had a registered command <name>, that command is returned.
If mutant had a registered command <name>, that command is returned.
Otherwise NIL is returned.
ApplyCommand: PROC [mutant: Pipal.Object, name: ATOM, arguments: LIST OF REFNIL, issuer: REFNIL] RETURNS [resultType: ResultType ← none, result: Pipal.Object NIL, new: Pipal.Object];
If mutant had a registered command <name>, that command is applied.
END.