-- File: NutImpl.mesa -- Contents: Implementation of Nut.mesa. -- Created by: Rick Cattell on January, 1982 -- Last edited by: -- Cattell on June 3, 1983 12:30 pm -- Willie-Sue on January 21, 1983 8:55 am -- Maxwell on July 15, 1982 12:26 pm -- Donahue on May 23, 1983 12:22 pm DIRECTORY Atom, DB, Nut, NutPrivate, Rope, DBNames USING[NameToEntity], MessageWindow USING[ Append ], Process USING [InitializeCondition, SecondsToTicks], NutOps, NutViewer, SystemNuts, ViewerClasses, ViewerOps; NutImpl: CEDAR MONITOR IMPORTS Atom, DB, DBNames, Nut, NutPrivate, NutViewer, Rope, Process, ViewerOps, SystemNuts, NutOps, MessageWindow EXPORTS Nut = BEGIN OPEN Nut, DB; DomainOf: PROC[e: Entity] RETURNS[d: Domain] = {IF e=NIL THEN RETURN[NIL] ELSE RETURN[DB.DomainOf[e]]}; -- Types and global variables implementations: PUBLIC ImplementationList _ NIL; ImplementationList: TYPE = LIST OF ImplementationRecord _ NIL; ImplementationRecord: TYPE = REF ImplementationRecordObject; ImplementationRecordObject: TYPE = RECORD[ domain: ROPE, -- name of domain segment: DB.Segment, -- the segment of the domain display: DisplayProc, -- nut displayer for this domain edit: EditProc, -- nut editor for this domain query: QueryProc, -- nut queryer for this domain create: CreateProc, -- nut creater for this domain update: UpdateProc, -- nut updater for this domain transaction: TransactionProc]; -- transaction notification for this domain defaultNutViewerProcs: ImplementationRecord_ NEW[ImplementationRecordObject _ [ display: DefaultDisplay, edit: DefaultEdit, query: DefaultQuery, create: DefaultCreate ]]; domainProcs: ImplementationRecord _ NEW[ImplementationRecordObject _ [ display: SystemNuts.DomainDisplayer, edit: SystemNuts.DomainEditor, query: SystemNuts.DomainQueryer, create: SystemNuts.DomainCreate ]]; relationProcs: ImplementationRecord _ NEW[ImplementationRecordObject _ [ display: SystemNuts.RelationDisplayer, edit: SystemNuts.RelationEditor, query: SystemNuts.RelationQueryer, create: SystemNuts.RelationCreate ]]; viewerSynchronizer: CONDITION; vSynchronizerPtr: LONG POINTER TO CONDITION; NutViewerState: TYPE = ATOM; -- {$quiescent, $beingDisplayed, $beingDestroyed}; -- Exported procedures Display: PUBLIC PROC[ e: DB.Entity, seg: DB.Segment _ NIL, parent: Viewer_ NIL, method: Method _ oneOnly] RETURNS[v: Viewer] = -- Creates a new nut viewing entity e. If a nut implementation has -- registered itself for e's type, that implementation will be called, -- else the standard NutViewer browser will be used. Replace caller's nut. BEGIN IF Null[e] THEN {MessageWindow.Append["Entity has been deleted!"]; RETURN}; { eName: ROPE = NameOf[e]; d: Domain = DomainOf[e]; dName: ROPE = NameOf[d]; segment: DB.Segment = IF DB.IsSystemEntity[e] THEN seg ELSE SegmentOf[e]; implRec: ImplementationRecord = FindImpl[dName, segment]; oldV: Viewer = FindExistingViewer[displayer, d, eName, segment, parent, method]; newV: Viewer = CreateNut[oldV, displayer, implRec, d, eName, segment, e]; IF newV=NIL THEN RETURN[NIL]; IF method # replace THEN NutPrivate.SetSpawned[parent, newV]; newV.newVersion _ TRUE; ViewerOps.PaintViewer[newV, caption]; implRec.display[e, newV, segment ! ABORTED => CONTINUE]; SetViewerState[newV, $quiescent]; newV.newVersion _ FALSE; ViewerOps.PaintViewer[newV, caption]; RETURN[newV] } END; Edit: PUBLIC PROC[ d: DB.Domain, eName: ROPE, seg: DB.Segment _ NIL, parent: Viewer_ NIL, method: Method _ oneOnly] RETURNS[v: Viewer] = BEGIN dName: ROPE = GetName[d]; e: Entity = FetchEntity[d, eName, seg]; segment: DB.Segment = IF NutOps.IsSystemDomain[d] THEN seg ELSE SegmentOf[d]; implRec: ImplementationRecord = FindImpl[dName, segment]; oldV: Viewer = FindExistingViewer[editor, d, eName, segment, parent, method]; newV: Viewer = CreateNut[oldV, editor, implRec, d, eName, segment, e]; IF newV=NIL THEN RETURN[NIL]; IF method # replace THEN NutPrivate.SetSpawned[parent, newV]; newV.newVersion _ TRUE; ViewerOps.PaintViewer[newV, caption]; implRec.edit[d, eName, newV, segment ! ABORTED => CONTINUE]; SetViewerState[newV, $quiescent]; newV.newVersion _ FALSE; ViewerOps.PaintViewer[newV, caption]; RETURN[newV] END; Query: PUBLIC PROC[d: DB.Domain, seg: DB.Segment _ NIL] RETURNS[v: Viewer] = -- Creates a new nut for querying entities in d with given initial query. BEGIN dName: ROPE = GetName[d]; segment: DB.Segment = IF NutOps.IsSystemDomain[d] THEN seg ELSE SegmentOf[d]; implRec: ImplementationRecord = FindImpl[dName, seg]; newV: Viewer = CreateNut[NIL, queryer, implRec, d, "?", segment]; IF newV=NIL THEN RETURN[NIL]; implRec.query[d, newV, segment ! ABORTED => CONTINUE]; SetViewerState[newV, $quiescent]; RETURN[newV] END; Update: PUBLIC PROC[updateType: UpdateType, tuple: Relship] = -- calls any registered proc for e's domain BEGIN FOR iL: ImplementationList_ implementations, iL.rest UNTIL iL=NIL DO -- if were smart, could just call guys that could be effected by this tuple IF iL.first.update#NIL THEN iL.first.update[updateType, tuple]; ENDLOOP; END; FindExistingViewer: PROC[ type: Nut.NutType, d: Domain, eName: ROPE, seg: DB.Segment, parent: Viewer_ NIL, method: Method _ oneOnly] RETURNS[oldV: Viewer] = BEGIN -- look for an existing viewer IF method = replace THEN oldV _ parent ELSE oldV _ NutPrivate.FindSpawned[parent]; IF oldV # NIL AND oldV.newVersion THEN oldV _ NIL; IF oldV = NIL AND method = oneOnly THEN oldV _ NutPrivate.FindViewer[type, d, eName, seg]; IF oldV # NIL AND oldV.newVersion THEN oldV _ NIL; END; CreateNut: PROC[ oldV: Viewer, type: NutType, implRec: ImplementationRecord, d: Domain, eName: ROPE, segment: Segment, e: Entity_ NIL] RETURNS [newV: Viewer] = -- Used by Display, Edit, and Create procs: sets up a new nut in newV according to the rules. -- Returns the new viewer and the nut record for it, or NIL if nut creation should be aborted. -- Sets viewer state to $beingDisplayed, caller must set to $quiescent when done displaying. BEGIN name: ROPE = Rope.Cat[Atom.GetPName[segment], "!", DB.NameOf[d], "!", eName]; newV _ implRec.create[type, d, eName, segment, IF oldV = NIL THEN left ELSE oldV.column]; IF newV=NIL THEN RETURN; ViewerOps.AddProp[ newV, $Entity, name ]; IF eName#NIL THEN ViewerOps.AddProp[ newV, $EntityHandle, DBNames.NameToEntity[name] ]; IF newV.icon = tool THEN -- Set the icon if the create proc left it as default (tool) icon newV.icon _ IF e#NIL THEN NutViewer.GetIcon[e, segment] -- use icon for specific entity ELSE NutViewer.GetIcon[d, segment]; -- use icon for domain SetViewerState[newV, $beingDisplayed]; IF newV = oldV THEN RETURN; IF oldV#NIL THEN { IF oldV.iconic AND NOT newV.iconic THEN ViewerOps.OpenIcon[oldV]; SetViewerState[oldV, $beingDestroyed]; ViewerOps.ReplaceViewer[new: newV, old: oldV] } ELSE IF ~newV.iconic THEN ViewerOps.ComputeColumn[newV.column]; END; BeingDestroyed: PROCEDURE[v: Viewer] RETURNS[BOOLEAN] = BEGIN state: NutViewerState; IF v.destroyed THEN RETURN[TRUE]; state _ NARROW[ViewerOps.FetchProp[v, $NutViewerState]]; RETURN[state = $beingDestroyed]; END; SetViewerState: ENTRY PROC[v: Viewer, newState: NutViewerState] = -- Used for synchronization of nut viewers as the change states. The states are -- 1. NIL: container created but client DisplayProc (which creates subviewers) not yet called. -- 2. $beingDisplayed: client DisplayProc in process of setting up the viewer. -- 3. $quiescent: viewer has been created and displayed and is ready to go. -- 4. $beingDestroyed: user or client has requested destruction of this viewer, not yet finished. -- If client requests destroy, waits until finished creating before doing so. -- Sets inhibitDestroy bit in ViewerRec so Viewers won't allow user to destroy it until finish create. BEGIN OPEN ViewerOps; oldState: NutViewerState = NARROW[FetchProp[v, $NutViewerState]]; AddProp[v, $NutViewerState, newState]; SELECT newState FROM $quiescent => SELECT oldState FROM $beingDisplayed => v.inhibitDestroy_ FALSE; -- normal case: finished display without destroy $beingDestroyed => BROADCAST viewerSynchronizer; -- wake up pending destroy $quiescent => NULL; -- although we don't think this can ever happen ENDCASE => ERROR; $beingDisplayed => -- we assume this MUST be a new viewer if come in with this v.inhibitDestroy _ TRUE; $beingDestroyed => -- wait for finish of display if being displayed SELECT oldState FROM NIL => RETURN; $quiescent => RETURN; $beingDestroyed => ERROR; $beingDisplayed => { WHILE NARROW[FetchProp[v, $NutViewerState], ATOM] # $quiescent DO WAIT viewerSynchronizer; ENDLOOP; v.inhibitDestroy_ FALSE; AddProp[v, $NutViewerState, $beingDestroyed]; }; ENDCASE => ERROR; ENDCASE => ERROR; END; GetViewerState: PUBLIC ENTRY PROC[v: Viewer] RETURNS [NutViewerState] = {RETURN[NARROW[ViewerOps.FetchProp[v, $NutViewerState], ATOM]]}; Register: PUBLIC PROC[ domain: ROPE, segment: DB.Segment, display: DisplayProc_ NIL, edit: EditProc_ NIL, query: QueryProc_ NIL, create: CreateProc_ NIL, update: UpdateProc_ NIL, transaction: TransactionProc_ NIL ] = -- Registers a display, create, query, and/or notify proc for given domain. These will -- supersede any previous non-NIL registrations for this domain. BEGIN implRec: ImplementationRecord_ FindImpl[domain, segment, TRUE]; IF display#NIL THEN implRec.display_ display; IF edit#NIL THEN implRec.edit_ edit; IF query#NIL THEN implRec.query_ query; IF create#NIL THEN implRec.create_ create; IF display#NIL THEN implRec.display_ display; IF update#NIL THEN implRec.update_ update; IF transaction#NIL THEN implRec.transaction_ transaction; END; DeRegister: PUBLIC PROC[segment: DB.Segment, domain: ROPE] = BEGIN prev: ImplementationList_ NIL; FOR iL: ImplementationList_ implementations, iL.rest UNTIL iL=NIL DO IF Rope.Equal[iL.first.domain, domain] AND iL.first.segment = segment THEN { IF prev = NIL THEN implementations_ iL.rest ELSE prev.rest_ iL.rest; EXIT; }; prev_ iL; ENDLOOP; END; -- Support procedures debug: PUBLIC BOOLEAN _ FALSE; FindImpl: PROC[d: ROPE, seg: Segment, create: BOOLEAN _ FALSE] RETURNS[ImplementationRecord] = -- Try to find an implementation for domain d. If not found, return the default one. BEGIN IF Rope.Equal[d, "Domain"] THEN RETURN[ domainProcs ]; IF Rope.Equal[d, "Relation"] THEN RETURN[ relationProcs ]; IF ~debug THEN FOR iL: ImplementationList _ implementations, iL.rest UNTIL iL=NIL DO IF Rope.Equal[iL.first.domain, d] AND seg = iL.first.segment THEN RETURN[iL.first] ENDLOOP; IF NOT create THEN RETURN[ defaultNutViewerProcs ] ELSE BEGIN implRec: ImplementationRecord = NEW[ImplementationRecordObject]; implRec^ _ defaultNutViewerProcs^; implRec.domain_ d; implRec.segment _ seg; implementations_ CONS[ implRec, implementations ]; RETURN[implementations.first]; END END; Initialize: PROC = TRUSTED { vSynchronizerPtr_ @viewerSynchronizer; Process.InitializeCondition[vSynchronizerPtr, Process.SecondsToTicks[1200]]; -- 20 minutes ?? }; --* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * -- one-time start code Initialize[]; END. Last edited by: Rick on April 3, 1982 5:36 pm: Added new CreateProc feature so clients can create their own viewers. Added DefaultCreateProc here. Started using ReplaceViewer. WS on April 5, 1982: Took out ReplaceViewer (can't work anyway, Scott says) WS on April 22: changed CreateProc to pass dName&eName, return vIsOld. Rick on April 23, 1982 9:03 am: CreateProc returns NIL if it doesn't want NutImpl to create new nut. WS on May 6, 1982 4:08 pm: Display returns newV Rick on May 6, 1982 6:58 pm: Check for Null entity in Display, just as defensive programming. Maxwell on June 15, 1982 9:30 am: Changed algorithm for displaying viewers. WS on June 23, 1982: added DBNotify stuff. Rick & Willie-Sue on July 8, 1982: viewerSynchronizer stuff Maxwell July 15, 1982 12:28 pm: Added "debug", NutViewer.StartTrap[]; catch ABORTED Cattell August 3, 1982 4:40 pm: don't repaint viewers or recompute columns in Display, Edit, and Query procs. Cattell August 5, 1982 6:54 pm: Cleaned up Display, Edit, and Query procs considerably, by putting much of their shared logic in new proc CreateNut. We no longer do any extra paint of the viewer. We no longer open the oldV if BOTH oldV and newV are iconic; this means the client wants to leave the nut iconic. We now create icons for Editors and Queryers as well as Displayers; we use the icon for the domain since there is not (necessarily) an entity associated with an editor or queryer. Simultaneously changed ViewerNutImpl so that the default icon for the domain will be used in conjuction with the domain itself. We now recompute the left column if newV is on the left, right if on the right, and neither if iconic. Added "init" parameter to Display procs and to Create procs so client can pass info through to both of these as well as Edit and Query procs. This last change involves changing Nut. Cattell October 12, 1982 12:27 pm: Halfway fixed problem with new Viewers (3.4) destroying nuts before they are created: set "filler" field to non-zero until viewer is fully created. Added PUBLIC GetViewerState, would like to put this in NutViewer interface when possible. WS on October 28, 1982 4:26 pm: now have inhibitDestroy bit in ViewerRec Cattell on April 13, 1983 11:45 am: should have passed segment, not seg, in call to implRec.display in Display proc. Same with Edit and Query procs. Cattell on May 25, 1983 1:41 pm: should have used seg of entity, not domain in Display.