PipalEdit.mesa 
Copyright Ó 1988 by Xerox Corporation. All rights reserved.
Bertrand Serlet February 5, 1988 0:07:35 am PST
Louis Monier February 1, 1988 11:27:19 pm PST
Barth, January 29, 1988 6:12:15 pm PST
DIRECTORY Pipal, PipalOps, RefTab;
PipalEdit: CEDAR DEFINITIONS = BEGIN
Theory
This module defines how to edit Pipal objects.
Class
editorClass: Pipal.Class;
Editor: TYPE = REF EditorRec;
EditorRec: TYPE = RECORD [
object: Pipal.Object,
state: Pipal.Object ← NIL,
undoEvents: Events ← NIL, -- first undoable event head of list
redoEvents: Events ← NIL]; -- first redoable event head of list
Events: TYPE = LIST OF Event;
Event: TYPE = REF EventRec;
EventRec: TYPE = RECORD [
message: Pipal.ROPE,
object: Pipal.Object,
state: Pipal.Object ← NIL];
Method
editMethod: Pipal.Method;
EditProc: TYPE = PROC [object: Pipal.Object] RETURNS [editor: Editor ← NIL];
HasEdit: PROC [object: Pipal.Object] RETURNS [BOOL];
Edit: EditProc;
Invoke interactive editor. No editMethod method => crash and burn.
Path: TYPE = INT;
A path from a root to an editable object is entirely specified by an integer.
path=0 designates root.
EachEditableChildProc: TYPE = PROC [path: Path, child: Pipal.Object] RETURNS [quit: BOOLFALSE];
EnumerateEditableChildren: PROC [root: Pipal.Object, each: EachEditableChildProc] RETURNS [quit: BOOLFALSE];
Calls each editable child or grand child of root.
Successive calls to EnumerateEditableChildren with identical root, should produce same calls to each.
If a child is editable its decendants are not enumerated. If an object does not have an enumerate proc, it is ignored.
Calls each on root if root is editable (with path=0).
Pop Event
The pop event is called everytime one wants to replace the original object on which the editor was called by the current object contained in the editor.
For example, let us imagine there is an object X in some editor E. At some point, the user "pushes" into X, and so creates a new editor 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 editor) 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 editor, and arg2 is the resulting "new" object.
BroadcastPop: PROC [editor: Editor];
Recover the current object and send a pop event.
Operations
CreateEditor: PROC [object: Pipal.Object, state: Pipal.Object ← NIL] RETURNS [editor: Editor];
Creates a new editor and adds a replace watchdog.
DestroyEditor: PROC [editor: Editor];
Removes editor from the replacement notifier bag.
Do: PROC [editor: Editor, message: Pipal.ROPE, newObject, newState: Pipal.Object];
Empties the redo list, stacks a new event containing the current object and state on the undo list, and modifies current object and state.
ReplaceInEditor: PROC [editor: Editor, oldChild, newChild, newState: Pipal.Object];
Replace oldChild by newChild everywhere reachable from editor.object.
ReplaceInPath: PROC [root: Pipal.Object, path: Path, oldChild, newChild: Pipal.Object] RETURNS [table: PipalOps.ReplaceTable];
Replaces all the objects on the path from root to oldChild.
PipalOps.ReplaceWithTable is called (no broadcast). A table is built during that descent.
NotifyReplace: PROC [editor: Editor];
-- 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 editor ReplaceNotifyProc, if this is not the originating editor and the propagated object is reachable from editor.object or editor.state, stacks an event which if undone reverts the object of that editor back to what it was before the global replace. Upon unreplace the editor 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 [editor: Editor, arguments: LIST OF REFNIL, issuer: REFNIL, registrationData: REFNIL] RETURNS [resultType: ResultType ← none, result: Pipal.Object ← NIL];
editor 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 ← editorClass, name: ATOM, command: CommandProc, registrationData: REFNIL];
class is the class that catches the message.
FetchCommand: PROC [editor: Editor, name: ATOM] RETURNS [command: CommandProc ← NIL, registrationData: REFNIL];
If editor.state had a registered command <name>, that command is returned.
If editor had a registered command <name>, that command is returned.
Otherwise NIL is returned.
ApplyCommand: PROC [editor: Editor, name: ATOM, arguments: LIST OF REFNIL, issuer: REFNIL] RETURNS [resultType: ResultType ← none, result: Pipal.Object NIL];
Fetches the relevant command and applies it.
Generic Commands
Reset: PROC [editor: Editor];
Undoes while possible.
Same as ApplyCommand[editor, $Reset];
Undo: PROC [editor: Editor];
Undoes the last action, if any.
Errors: cantUndo
Same as ApplyCommand[editor, $Undo];
Redo: PROC [editor: Editor];
Redoes the next action, if any.
Errors: cantRedo
Same as ApplyCommand[editor, $Redo];
Flush: PROC [editor: Editor];
Flushes undoEvents and redoEvents.
Same as ApplyCommand[editor, $Redo];
END.