DIRECTORY Atom, BasicTime USING [GMT], Buttons USING [ButtonProc, ReLabel], Containers USING [ChildXBound], DB, DBEnvironment, DefaultNutUtilities USING [AttrButtonLength, valueFont], IO, Menus USING[ MouseButton, Menu, CreateMenu, SetGuarded, FindEntry ], Nut, NutButtons, NutOps, NutViewer USING [Error, Message, MakeButton, Initialize, Viewer, MakeBigTextBox, MakeRuler], Rope, TuplesEditor, VFonts USING[CharWidth, FontHeight, Font, EstablishFont, defaultFont, StringWidth], ViewerIO USING [CreateViewerStreams], ViewerOps USING [AddProp, FetchProp, PaintViewer, MoveViewer, CreateViewer, EstablishViewerPosition, DestroyViewer, SetNewVersion], ViewerTools USING [GetContents, SetContents, SetSelection], ViewerClasses; TuplesEditorImpl: CEDAR MONITOR IMPORTS Atom, Buttons, Containers, DB, DBEnvironment, IO, Nut, NutOps, Menus, NutViewer, Rope, VFonts, ViewerIO, ViewerOps, ViewerTools, DefaultNutUtilities EXPORTS TuplesEditor = { OPEN DB, Rope, Nut, ViewerClasses; PosPair: TYPE = RECORD[ row: CARDINAL, col: CARDINAL]; anyErrors: BOOL; -- Set if any procedure discovered user errors in saving the new entries defaultOld: PUBLIC BOOL_ TRUE; -- FALSE => attributes start in NewOrOld mode, else OldOnly updateList: AttributeValueList; ViewerList: TYPE = LIST OF ViewerClasses.Viewer; allFieldsNull: BOOLEAN; oldRelship: Relship; buttonSize: INT = 125; cycleFont: VFonts.Font _ VFonts.EstablishFont[family: "Helvetica", size: 8]; italicFont: VFonts.Font _ VFonts.EstablishFont[family: "TimesRoman", size: 10, italic: TRUE]; xFudge: INT _ 4; yFudge: INT _ 2; DisplayTuples: PUBLIC ENTRY PROC[e: Entity, attrList: AttributeList, parent: Viewer] RETURNS[ Viewer ] = { ENABLE UNWIND => NULL; row: CARDINAL _ 0; lastButton: NutButtons.NutButton _ NutViewer.Initialize[parent]; skipRelationName: BOOL _ FALSE; lastAttr: Attribute _ NIL; typeOut: IO.STREAM; lastButton _ ViewerOps.CreateViewer[ flavor: $Typescript, info: [parent: parent, ww: parent.ww-(3*xFudge), wx: lastButton.wx+xFudge, wh: 2*VFonts.FontHeight[DefaultNutUtilities.valueFont], wy: lastButton.wy+yFudge, border: TRUE] ]; Containers.ChildXBound[parent, lastButton]; typeOut _ ViewerIO.CreateViewerStreams[NIL, lastButton].out; ViewerOps.AddProp[parent, $Typescript, typeOut]; FOR attrL: AttributeList _ attrList, attrL.rest UNTIL attrL = NIL DO tupleList: LIST OF Relship = IF e = NIL THEN NIL ELSE NutOps.GetTuples[e, attrL.first]; IF tupleList = NIL THEN [lastButton] _ BuildTuplesButtons[ attrL.first , lastButton, skipRelationName] ELSE FOR tlT: LIST OF Relship _ tupleList, tlT.rest UNTIL tlT=NIL DO [lastButton] _ BuildTuplesButtons[ attrL.first, lastButton, skipRelationName, tlT.first]; IF skipRelationName = FALSE THEN skipRelationName _ TRUE; ENDLOOP; IF attrL.rest # NIL THEN IF Rope.Equal[ GetName[NutOps.GetRelation[attrL.first]], GetName[NutOps.GetRelation[attrL.rest.first]] ] THEN skipRelationName _ TRUE ELSE skipRelationName _ FALSE; ENDLOOP; ViewerOps.AddProp[ parent, $Entity, e ]; ViewerOps.AddProp[ parent, $EntityName, NutOps.SafeNameOf[e] ]; ViewerOps.AddProp[ parent, $ChangedRels, NIL ]; RETURN[parent] }; BuildTuplesButtons: PROC[ attribute: Attribute, lastButton: NutButtons.NutButton, skipRelButton: BOOL _ FALSE, relship: Relship _ NIL ] RETURNS[NutButtons.NutButton] = { r: Relation = NutOps.GetRelation[attribute]; allAttrs: LIST OF Attribute _ NutOps.AttributesOf[r]; displayAttrs: LIST OF Attribute = NutOps.RemoveAttribute[attribute, allAttrs]; relationButton: NutButtons.NutButton; visible: BOOL _ NOT skipRelButton; lastAttrViewer: NutViewer.Viewer; lastButton.wy _ lastButton.wy + yFudge; relationButton_ NutViewer.MakeButton[ q: NIL, proc: RelProc, sib: lastButton, data: r, name: GetName[r], newLine: TRUE, visible: visible]; ViewerOps.AddProp[relationButton, $relShip, relship]; ViewerOps.AddProp[relationButton, $r, r]; ViewerOps.AddProp[relationButton, $attr, attribute]; ViewerOps.AddProp[relationButton, $Changes, NIL]; lastAttrViewer _ relationButton; lastButton _ relationButton; FOR da: LIST OF Attribute _ displayAttrs, da.rest UNTIL da = NIL DO buttonInfo: NutButtons.ButtonFontInfo _ NEW[ NutButtons.ButtonFontInfoRec _ [TRUE,,,] ]; nextAttr: Attribute = da.first; attrValue: ROPE _ IF relship # NIL THEN GetFS[relship, nextAttr] ELSE ""; attrViewer: NutViewer.Viewer; name: ROPE = Rope.Cat[DB.NameOf[nextAttr], ": ", attrValue ]; entityValuedAttr: BOOL _ FALSE; IF NutOps.EntityValued[nextAttr] THEN BEGIN entityValuedAttr _ TRUE; lastButton _ NutViewer.MakeButton[q: NIL, proc: CycleNewOld, sib: lastButton, data: r, name: IF defaultOld THEN "O" ELSE "", border: TRUE, font: NEW[ NutButtons.ButtonFontInfoRec _ [FALSE, cycleFont,,]], width: VFonts.CharWidth['O]+xFudge]; END; attrViewer _ NutViewer.MakeButton[q: NIL, proc: AttrProc, sib: lastButton, data: r, name: name, font: buttonInfo]; ViewerOps.AddProp[ attrViewer, $attr, nextAttr ]; ViewerOps.AddProp[ lastAttrViewer, $Next, attrViewer ]; ViewerOps.AddProp[attrViewer, $Value, attrValue]; ViewerOps.AddProp[attrViewer, $Relation, relationButton]; IF entityValuedAttr THEN BEGIN ViewerOps.AddProp[ lastButton, $attrViewer, attrViewer ]; ViewerOps.AddProp[ attrViewer, $OldNew, lastButton ]; ViewerOps.AddProp[ attrViewer, $which, NEW[INT _ IF defaultOld THEN 2 ELSE 0] ]; END; lastAttrViewer _ attrViewer; lastButton _ attrViewer ENDLOOP; RETURN[lastButton] }; AttrProc: Buttons.ButtonProc = TRUSTED { attrViewer: ViewerClasses.Viewer = NARROW[ parent, ViewerClasses.Viewer ]; attr: Attribute = V2E[ViewerOps.FetchProp[attrViewer, $attr]]; domain: Domain = V2E[GetP[attr, aTypeIs, aTypeOf]]; value: Rope.ROPE = NARROW[ ViewerOps.FetchProp[attrViewer, $Value] ]; entity: Entity; IF shift THEN {} ELSE SELECT mouseButton FROM Menus.MouseButton[red] => --edit the value MakeAttrValueEditor[attrViewer]; Menus.MouseButton[yellow] => --display the attribute { entity _ DeclareEntity[ domain, value, Version[OldOnly] ! DB.Error => { entity _ NIL; CONTINUE } ]; IF entity # NIL THEN [] _ Nut.Display[ eName: DB.NameOf[entity], domain: DB.NameOf[domain], segment: DB.SegmentOf[entity], parent: attrViewer.parent ] ELSE NutViewer.Message[attrViewer, value, " not found!"] }; Menus.MouseButton[blue] => --display type of attribute { eType: Entity _ V2E[GetP[attr, aTypeIs, aTypeOf]]; type: Rope.ROPE _ TypeLabel[eType]; typeButton: Viewer _ NutViewer.MakeButton[ q: NIL, sib: attrViewer, proc: TypeProc, name: type, font: NEW[ NutButtons.ButtonFontInfoRec _ [FALSE, italicFont,,]] ]; MoveOver[attrViewer, typeButton.ww]; ViewerOps.AddProp[typeButton, $attrViewer, attrViewer]; ViewerOps.AddProp[attrViewer, $Type, typeButton]; ViewerOps.PaintViewer[typeButton.parent, client]; }; ENDCASE; }; MakeAttrValueEditor: PROC[attrViewer: Viewer] = BEGIN v, lastV: ViewerClasses.Viewer; value: Rope.ROPE = NARROW[ ViewerOps.FetchProp[attrViewer, $Value]]; info: ViewerClasses.ViewerRec _ [parent: attrViewer.parent, wx: attrViewer.wx, wy: attrViewer.wy+ VFonts.FontHeight[DefaultNutUtilities.valueFont], ww: MAX[ MIN[buttonSize, VFonts.StringWidth[value, DefaultNutUtilities.valueFont]]+xFudge, 15*VFonts.CharWidth['O] ], wh: 4*(VFonts.FontHeight[DefaultNutUtilities.valueFont]+yFudge), border: TRUE, scrollable: FALSE, menu: attrValueMenu ]; MoveTuples[target: attrViewer, numberOfRows: info.wh, justBelow: TRUE, abs: TRUE]; v _ ViewerOps.CreateViewer[flavor: $Container, info: info]; lastV _ NutViewer.Initialize[v]; lastV _ NutViewer.MakeButton[q: NIL, sib: lastV, proc: ValueGrow, name: "Grow"]; lastV _ NutViewer.MakeButton[q: NIL, sib: lastV, proc: ValueDone, name: "Done"]; lastV _ NutViewer.MakeButton[q: NIL, sib: lastV, proc: ValueReset, name: "Reset"]; lastV _ NutViewer.MakeRuler[lastV]; lastV _ NutViewer.MakeBigTextBox[sib: lastV, contents: NARROW[ ViewerOps.FetchProp[attrViewer, $Value], Rope.ROPE] ]; ViewerTools.SetSelection[ lastV, NIL ]; ViewerOps.AddProp[v, $TextViewer, lastV]; --when buttoning you need the text ViewerOps.AddProp[v, $Button, attrViewer]; --after editting you need the button ViewerOps.AddProp[lastV, $Value, value]; --for fast resets ViewerOps.PaintViewer[attrViewer.parent, all] END; RelProc: Buttons.ButtonProc = TRUSTED BEGIN viewer: ViewerClasses.Viewer = NARROW[ parent, ViewerClasses.Viewer ]; attribute: Attribute = V2E[ViewerOps.FetchProp[viewer, $attr]]; u: Uniqueness = V2U[GetP[attribute, aUniquenessIs, aUniquenessOf]]; IF shift THEN {} ELSE IF u = Key OR u = OptionalKey THEN {anyErrors_ TRUE; NutViewer.Message[viewer, "Only one allowed!"]} ELSE BEGIN lastButton: NutButtons.NutButton; r: Relation = NutOps.GetRelation[attribute]; MoveTuples[target: viewer, justBelow: TRUE]; [lastButton] _ BuildTuplesButtons[ attribute: attribute, lastButton: viewer, skipRelButton: TRUE ]; ViewerOps.PaintViewer[viewer.parent, client]; END; END; MoveTuples: PROC[ target: Viewer, numberOfRows: INT _ 1, parent: Viewer _ NIL, justBelow: BOOL _ FALSE, abs: BOOL _ FALSE ] = BEGIN tempViewer: Viewer _ IF parent # NIL THEN parent.child ELSE target.parent.child; shift: INT = IF abs THEN numberOfRows ELSE numberOfRows*(VFonts.FontHeight[DefaultNutUtilities.valueFont]+yFudge); UNTIL tempViewer = NIL DO IF justBelow THEN BEGIN IF (tempViewer.wy-yFudge) > target.wy THEN ViewerOps.MoveViewer[viewer: tempViewer, x: tempViewer.wx, y: tempViewer.wy + shift, h: tempViewer.wh, w: tempViewer.ww, paint: FALSE]; END ELSE IF (tempViewer.wx > target.wx AND tempViewer.wy >= target.wy) OR (tempViewer.wy > target.wy AND tempViewer.wx >= target.wx) THEN ViewerOps.MoveViewer[viewer: tempViewer, x: tempViewer.wx, y: tempViewer.wy + shift, h: tempViewer.wh, w: tempViewer.ww, paint: FALSE]; tempViewer _ tempViewer.sibling ENDLOOP; END; MoveOver: PROC[ target: Viewer, shift: INT _ 1, mvTarget: BOOL _ FALSE ] = BEGIN tempViewer: Viewer _ NARROW[ViewerOps.FetchProp[target, $Next]]; IF mvTarget THEN ViewerOps.MoveViewer[viewer: target, y: target.wy, x: target.wx + shift, h: target.wh, w: target.ww, paint: FALSE]; UNTIL tempViewer = NIL DO oldNew: Viewer _ NARROW[ViewerOps.FetchProp[tempViewer, $OldNew]]; type: Viewer _ NARROW[ViewerOps.FetchProp[tempViewer, $Type]]; ViewerOps.MoveViewer[viewer: tempViewer, y: tempViewer.wy, x: tempViewer.wx + shift, h: tempViewer.wh, w: tempViewer.ww, paint: FALSE]; IF oldNew # NIL THEN ViewerOps.MoveViewer[viewer: oldNew, y: oldNew.wy, x: oldNew.wx + shift, h: oldNew.wh, w: oldNew.ww, paint: FALSE]; IF type # NIL THEN ViewerOps.MoveViewer[viewer: type, y: type.wy, x: type.wx + shift, h: type.wh, w: type.ww, paint: FALSE]; tempViewer _ NARROW[ViewerOps.FetchProp[tempViewer, $Next], Viewer]; ENDLOOP; END; TypeProc: Buttons.ButtonProc = BEGIN typeButton: Viewer _ NARROW[parent]; MoveOver[ target: NARROW[ViewerOps.FetchProp[typeButton, $attrViewer], Viewer], shift: -typeButton.ww]; ViewerOps.DestroyViewer[typeButton]; END; TypeLabel: PROC[ type: Entity ] RETURNS[ Rope.ROPE ] = { IF DB.Eq[type, DB.RopeType] THEN RETURN["(String)"] ELSE IF DB.Eq[type, DB.IntType] THEN RETURN["(Int)"] ELSE IF DB.Eq[type, DB.BoolType] THEN RETURN["(Bool)"] ELSE IF DB.Eq[type, DB.TimeType] THEN RETURN["(Time)"] ELSE RETURN[ Rope.Cat["(", GetName[type], ")"] ] }; CycleNewOld: Buttons.ButtonProc = TRUSTED { viewer: ViewerClasses.Viewer = NARROW[ parent, ViewerClasses.Viewer ]; attrViewer: ViewerClasses.Viewer = NARROW[ ViewerOps.FetchProp[viewer, $attrViewer] ]; Labels: ARRAY[0..3) OF ROPE = ["", "N", "O"]; attr: Attribute = V2E[ ViewerOps.FetchProp[ attrViewer, $attr ] ]; which: REF INT = NARROW[ViewerOps.FetchProp[attrViewer, $which], REF INT]; IF NOT NutOps.EntityValued[attr] THEN RETURN; which^ _ (which^ + 1) MOD 3; Buttons.ReLabel[viewer, Labels[which^] ]; ViewerOps.AddProp[attrViewer, $which, which ]; }; MergeTuples: PUBLIC ENTRY PROC[viewer: Viewer, newEntity: Entity _ NIL ] RETURNS [errors: BOOL] = { ENABLE UNWIND => NULL; parent: Viewer = viewer.parent; tempViewer: Viewer _ parent.child; newRelship: Relship; anyErrors_ FALSE; WHILE tempViewer # NIL DO IF ViewerOps.FetchProp[tempViewer, $r ] # NIL THEN BEGIN rel: Relation = V2E[ViewerOps.FetchProp[tempViewer, $r ] ]; attr: Attribute = V2E[ViewerOps.FetchProp[ tempViewer, $a ] ]; oldRelship_ V2E[ViewerOps.FetchProp[tempViewer, $relShip ] ]; updateList_ NIL; allFieldsNull_ TRUE; newRelship_ NIL; AddAllFields[ relation: tempViewer]; IF updateList#NIL AND NOT allFieldsNull THEN BEGIN updateList _ CONS[ AttributeValue[ attribute: attr, lo: newEntity], updateList ]; newRelship _ CreateRelship[rel]; UpdateTuple[newRelship, updateList]; ViewerOps.AddProp[ tempViewer, $relShip, newRelship ]; END; END; tempViewer _ tempViewer.sibling ENDLOOP; RETURN[anyErrors] }; SaveTuples: PUBLIC ENTRY PROC[viewer: Viewer, newEntity: Entity _ NIL ] RETURNS [errors: BOOL] = BEGIN ENABLE UNWIND => NULL; changedRs: ViewerList _ NARROW[ ViewerOps.FetchProp[viewer, $ChangedRels] ]; newRelship: Relship; IF changedRs = NIL THEN RETURN[FALSE]; FOR changedR: Viewer _ changedRs.first, changedRs.first UNTIL changedR = NIL DO oldRelship _ V2E[ ViewerOps.FetchProp[changedR, $relShip]]; allFieldsNull _ TRUE; AddChangedFields[changedR]; IF NOT DB.Null[oldRelship] THEN IF allFieldsNull THEN DB.DestroyRelship[oldRelship] ELSE UpdateTuple[oldRelship, updateList] ELSE IF NOT allFieldsNull THEN BEGIN updateList _ CONS[ AttributeValue[ attribute: V2E[ViewerOps.FetchProp[changedR, $a]], lo: newEntity ], updateList ]; newRelship _ CreateRelship[V2E[ViewerOps.FetchProp[changedR, $r]]]; UpdateTuple[ newRelship, updateList ]; ViewerOps.AddProp[ changedR, $relShip, newRelship ]; END; changedRs _ changedRs.rest; ENDLOOP; RETURN[anyErrors]; END; UpdateTuple: PROC[ t: Relship, updateList: AttributeValueList ] = { FOR ul: AttributeValueList _ updateList, ul.rest UNTIL ul = NIL DO av: AttributeValue = ul.first; u: Uniqueness = V2U[GetP[av.attribute, aUniquenessIs]]; IF u = Key THEN SetF[ t, av.attribute, av.lo ] ENDLOOP; FOR ul: AttributeValueList _ updateList, ul.rest UNTIL ul = NIL DO av: AttributeValue = ul.first; u: Uniqueness = V2U[GetP[av.attribute, aUniquenessIs]]; IF u # Key THEN SetF[ t, av.attribute, av.lo ] ENDLOOP }; PropertyValueList: TYPE = LIST OF PropertyValue; PropertyValue: TYPE = RECORD[ from: Attribute, -- entity-valued attribute of a relation avl: AttributeValueList -- value contraints on other attributes of same relation ]; QueryTuples: PUBLIC ENTRY PROC[ viewer: Viewer ] RETURNS [el: LIST OF Entity] = { ENABLE UNWIND => NULL; queryList: PropertyValueList_ NIL; changedRs: ViewerList _ NARROW[ ViewerOps.FetchProp[viewer, $ChangedRels] ]; IF changedRs = NIL THEN RETURN[NIL]; WHILE changedRs # NIL DO allFieldsNull _ TRUE; updateList_ NIL; AddChangedFields[changedRs.first]; IF updateList#NIL AND NOT allFieldsNull THEN queryList_ CONS[ [from: V2E[ViewerOps.FetchProp[changedRs.first, $attr]], avl: updateList], queryList ]; changedRs _ changedRs.rest; ENDLOOP; RETURN[EntityQuery[queryList]]; }; EntityQuery: PROC[pvl: PropertyValueList] RETURNS[LIST OF Entity] = { f: Attribute_ pvl.first.from; r: Relation_ V2E[GetP[f, aRelationIs]]; elNew: LIST OF Entity_ NIL; nestedRS: RelshipSet_ RelationSubset[r, pvl.first.avl]; FOR t: Relship_ NextRelship[nestedRS], NextRelship[nestedRS] UNTIL t=NIL DO elNew_ CONS[V2E[GetF[t, f]], elNew] ENDLOOP; ReleaseRelshipSet[nestedRS]; RETURN[NestedEntityQuery[elNew, pvl.rest]] }; NestedEntityQuery: PROC[elOld: LIST OF Entity, pvl: PropertyValueList] RETURNS[LIST OF Entity] = { IF pvl=NIL THEN RETURN[elOld] ELSE { f: Attribute_ pvl.first.from; r: Relation_ V2E[GetP[f, aRelationIs]]; elNew, elEnd: LIST OF Entity_ NIL; -- will equal those in elOld that satisfy pvl.first FOR elOldT: LIST OF Entity_ elOld, elOldT.rest UNTIL elOldT=NIL DO nestedRS: RelshipSet_ RelationSubset[r, CONS[ [f, elOldT.first], pvl.first.avl]]; IF NextRelship[nestedRS] #NIL THEN IF elNew=NIL THEN elEnd_ elNew_ CONS[elOldT.first, NIL] ELSE elEnd_ elEnd.rest_ CONS[elOldT.first, NIL]; ReleaseRelshipSet[nestedRS]; ENDLOOP; RETURN[NestedEntityQuery[elNew, pvl.rest]]; }}; AddChangedFields: PROC[relation: Viewer] = BEGIN attrViewerList: ViewerList _ NARROW[ViewerOps.FetchProp[relation, $Changes]]; FOR attrList: ViewerList _ attrViewerList, attrList.rest UNTIL attrList = NIL DO AddFields[attrList.first]; ENDLOOP; END; AddAllFields: PROC[relation: Viewer] = BEGIN attrViewer: Viewer _ NARROW[ViewerOps.FetchProp[relation, $Next]]; WHILE attrViewer # NIL DO AddFields[attrViewer]; attrViewer _ NARROW[ViewerOps.FetchProp[attrViewer, $Next]]; ENDLOOP; END; AddFields: PROC[attrViewer: Viewer] = BEGIN parent: Viewer = attrViewer.parent; attr: Attribute = V2E[ViewerOps.FetchProp[attrViewer, $attr]]; newOld: INT = IF NutOps.EntityValued[attr] THEN NARROW[ViewerOps.FetchProp[attrViewer, $which], REF INT]^ ELSE 0; --only needs the value if its an entity contents: ROPE = NARROW[ ViewerOps.FetchProp[attrViewer, $Value] ]; type: Entity = V2E[GetP[attr, aTypeIs, aTypeOf]]; value: DB.Value; IF NOT Rope.Equal[contents, ""] THEN allFieldsNull_ FALSE; SELECT type FROM IntType => { val: INT = IF Rope.Equal[contents, ""] THEN 0 ELSE IO.GetInt[ IO.RIS[contents] ]; value _ I2V[val]}; RopeType => value _ S2V[ contents ]; BoolType => value _ B2V[ Rope.Equal[contents, "TRUE"] ]; TimeType => { time: DB.GMT; time.time _ IO.GetTime[IO.RIS[contents] ! IO.Error => TRUSTED { anyErrors_ TRUE; NutViewer.Error[parent, "Unintelligible time/date: ", contents]}]; value _ T2V[time] }; ENDCASE => IF Rope.Equal[contents, ""] THEN value_ NIL ELSE SELECT newOld FROM 0 => -- NewOrOld { value _ DeclareEntity[ d: type, name: contents, version: OldOnly ]; IF value = NIL THEN { NutViewer.Message[parent, "Automatically creating ", contents]; value _ DeclareEntity[ d: type, name: contents ]; IF value = NIL THEN { allFieldsNull_ TRUE; -- pretend this tuple didn't happen anyErrors_ TRUE; NutViewer.Error[parent, " Error: ", contents, " could not be created automatically."]; } } }; 1 => -- NewOnly { value _ CreateEntity[ d: type, name: contents ! DBEnvironment.Error => TRUSTED { codeRef: REF DBEnvironment.ErrorCode ~ NEW[DBEnvironment.ErrorCode _ code]; NutViewer.Error[parent, IO.PutFR["%g", IO.refAny[codeRef]], " Error trying to create \"", contents, "\" entity."]; allFieldsNull_ TRUE; -- pretend this tuple didn't happen anyErrors_ TRUE; CONTINUE}]}; 2 => -- OldOnly { value _ DeclareEntity[ type, contents, Version[OldOnly] ]; IF value = NIL THEN { allFieldsNull_ TRUE; -- pretend this tuple didn't happen anyErrors_ TRUE; NutViewer.Error[parent, " Error: ", contents, " does not exist."] }}; ENDCASE; updateList _ CONS[ AttributeValue[attribute: attr, lo: value], updateList ]; END; ResetTuples: PUBLIC ENTRY PROC[viewer: Viewer] = BEGIN ViewerOps.AddProp[viewer, $ChangedRels, NIL]; viewer.newVersion _ FALSE; END; attrValueMenu: Menus.Menu _ Menus.CreateMenu[1]; ValueGrow: Buttons.ButtonProc = BEGIN edViewer: ViewerClasses.Viewer _ (NARROW[parent, Viewer]).parent; textViewer: ViewerClasses.Viewer _ NARROW[ ViewerOps.FetchProp[edViewer, $TextViewer]]; shiftHeight: INT _ 2*textViewer.wh; shiftWidth: INT _ textViewer.ww/2; edViewer.wh _ edViewer.wh+shiftHeight; textViewer.wh _ textViewer.wh+shiftHeight; IF (edViewer.ww + shiftWidth+ edViewer.wx) < edViewer.parent.ww THEN BEGIN edViewer.ww _ edViewer.ww + shiftWidth; textViewer.ww _ textViewer.ww + shiftWidth; END; MoveTuples[ target: edViewer, numberOfRows: shiftHeight, abs: TRUE, justBelow: TRUE]; ViewerOps.EstablishViewerPosition[viewer: edViewer, x: edViewer.wx, y: edViewer.wy, h: edViewer.wh, w: edViewer.ww]; ViewerOps.EstablishViewerPosition[viewer: textViewer, x: textViewer.wx, y: textViewer.wy, h: textViewer.wh, w: textViewer.ww]; ViewerOps.PaintViewer[edViewer.parent, all]; END; ValueReset: Buttons.ButtonProc = BEGIN edViewer: ViewerClasses.Viewer _ (NARROW[parent, Viewer]).parent; textViewer: ViewerClasses.Viewer _ NARROW[ ViewerOps.FetchProp[edViewer, $TextViewer]]; oldValue: Rope.ROPE _ NARROW[ViewerOps.FetchProp[textViewer, $Value]]; ViewerTools.SetContents[ textViewer, oldValue ]; ViewerOps.PaintViewer[edViewer.parent, all]; END; ValueDone: Buttons.ButtonProc = BEGIN edViewer: ViewerClasses.Viewer _ (NARROW[parent, Viewer]).parent; textViewer: ViewerClasses.Viewer _ NARROW[ ViewerOps.FetchProp[edViewer, $TextViewer] ]; newValue: Rope.ROPE _ ViewerTools.GetContents[textViewer]; attrViewer: ViewerClasses.Viewer _ NARROW[ ViewerOps.FetchProp[edViewer, $Button]]; newWidth: INT = DefaultNutUtilities.AttrButtonLength[attrViewer.name, newValue, mouseButton]; MoveTuples[target: attrViewer, numberOfRows: -edViewer.wh, parent: attrViewer.parent, abs: TRUE, justBelow: TRUE]; IF newValue # ViewerOps.FetchProp[textViewer, $Value] THEN BEGIN oldWidth: INT _ attrViewer.ww; relButton: Viewer = NARROW[ ViewerOps.FetchProp[attrViewer, $Relation]]; pastChanges: ViewerList _ NARROW[ ViewerOps.FetchProp[relButton, $Changes]]; attrViewer.name _ Rope.Cat[PreColon[attrViewer.name], Rope.Cat[" ", newValue]]; ViewerOps.EstablishViewerPosition[viewer: attrViewer, x: attrViewer.wx, y: attrViewer.wy, h: attrViewer.wh, w: newWidth]; MoveOver[ target: attrViewer, shift: attrViewer.ww-oldWidth]; ViewerOps.AddProp[attrViewer, $Value, newValue]; ViewerOps.SetNewVersion[attrViewer.parent]; IF pastChanges = NIL THEN BEGIN relList: ViewerList _ NARROW[ ViewerOps.FetchProp[relButton.parent, $ChangedRels]]; relList _ CONS[relButton, relList]; ViewerOps.AddProp[relButton.parent, $ChangedRels, relList]; END; pastChanges _ CONS[attrViewer, pastChanges]; ViewerOps.AddProp[ relButton, $Changes, pastChanges ]; IF Menus.FindEntry[attrViewer.parent.menu, "Reset"] # NIL THEN --editor viewer Menus.SetGuarded[ Menus.FindEntry[attrViewer.parent.menu, "Reset"], TRUE ] ELSE --queryer viewer Menus.SetGuarded[ Menus.FindEntry[attrViewer.parent.menu, "Clear"], TRUE ]; END ELSE IF newWidth # attrViewer.ww THEN --user bluebuttoned so viewer should grow ViewerOps.EstablishViewerPosition[viewer: attrViewer, x: attrViewer.wx, y: attrViewer.wy, h: attrViewer.wh, w: newWidth]; ViewerOps.DestroyViewer[edViewer]; END; PreColon: PROC[label: ROPE] RETURNS[ROPE] = BEGIN colonPos: INT _ Rope.Find[s1: label, s2: ":"]; IF colonPos = -1 THEN --Not a label RETURN[label] ELSE RETURN[ Rope.Substr[base: label, len: colonPos+1] ]; END; }. Change Log: File: TuplesEditorImpl.mesa Created by Donahue November 16, 1982 2:38 pm Last edited by: Donahue, April 14, 1983 10:15 am Cattell, June 24, 1983 3:09 pm Butler, August 16, 1984 11:07:15 am PDT Beach, March 28, 1984 9:25:58 pm PST Main procedure in this module: puts up a subviewer in the parent viewer containing all the tuples that do or could refer to the entity e. Main loop goes through all attributes potentially referencing e's domain, and calls AddRow to add a blank tuple if the tupleList referencing e is empty for that attribute, or the tupleList if it is not empty. -- make error message window Add a row to the table, where the entity being displayed is assumed to be the value of the attribute supplied. If a relship is supplied, take the values of the other fields from it; otherwise, display empty fields. -- make a relation button -- make old/new button -- make the attribute label button and add all necessary props creates a text viewer, below the parent & shifts all --move all the tuples below target; create new buttons for the duplicate -- attributes; search through entire sib list and move all that are to the right or under the target Changes the Relship t as specified by updateList. If ALL of the values in the update list are NILs, then just destroy the whole Relship, 'cause user has left all blanks. First set all of the key values Now set all of the non-key attributes Given a table set up by DisplayTuples, finds all the entities in the given domain that satisfy the query filled into the table by the user. Inner loop similar to SaveTuples, but must create PropertyValueList instead of updating tuples. Continue only if user modified and put something in this tuple.. A handy procedure for finding all the entities of a domain that have the given property values. Finds all the entities which, for each PropertyValue in the PropertyValueList list, there exists at least one relationship whose "from" attribute equals the entity and whose other attributes satisfy the attribute value list. Finds the entities in elOld that satisfy pvl. We do this recursively as follows. For each entity in elOld, we do a RelationSubset to find the tuples that satisfy pvl.first and also reference the entity. If this list is not empty, we can keep that entity in a new list elNew; otherwise we toss it. When all the entities in elOld that also satisfy pvl.first have been added to elNew in this way, we recursively call NestedEntityQuery, returning the entities in elNew which also satisfy pvl.rest. The nestedRS contains tuples that reference e and also satisfy first element of pvl. We can keep elOldT.first in the elNew list iff this list is non-empty. NOTE THIS PROCEDURE UPDATES GLOBAL VARIABLES AS A SIDE EFFECT. THE MONITOR ON THIS MODULE IS TO PROTECT THESE GLOBAL VARIABLES. Adds to updateList the attributes in row that need to be assigned in order to create the relationship. Caller must add the first attribute (that refs the entity), if desired. If the user does not modify a field, it does not appear in the updateList unless there was an existing relationship and we are in "copy" mode. Thus updateList=NIL if the user modified no fields of a tuple. Sets allFieldsNull to FALSE if any of the fields are non-null. ValueGrow doubles the length of a textViewer and adjusts the screen accordingly ValueReset resets the contents of an editor window to be that of the original value in the attr:value button ValueDone gets the contents of the text in the associated textViewer and updates the attr:value button with the contents of the textViewer as the new value. The editor window is then destroyed. Edited on March 21, 1984 9:17:51 pm PST, by Beach added Cedar formatting via TiogaMesa. changes to: AddFields to detect errors more often when creating entities automatically. Edited on March 28, 1984 9:25:52 pm PST, by Beach cosmetic changes to the {} conventions and indentation. changes to: RelProc to preserve the newVersion status when expanding a relation row with the blue button. Edited on August 9, 1984 11:24:10 am PDT, by Butler changes to: DisplayTuples, BuildTuplesButtons, RelProc, AddFields, ValueDone, MakeAttrValueViewer, RelProc, DIRECTORY, BuildTuplesButtons, ValueDone, ValueDone, DoTuples, SaveTuples, NestedEntityQuery, AddNewFields, AddFields, ValueDone, AddNewFields, ResetTuples, SaveTuples, MakeAttrValueViewer, DIRECTORY, AttrProc, DoTuples, SaveTuples, SaveTuples, ResetTuples, MergeTuples, SaveTuples, AddChangedFields, AddAllFields, AddFields, QueryTuples, QueryTuples, ValueDone, QueryTuples, ValueDone, QueryTuples, QueryTuples, AttrButtonLength Κ‰˜Jšœ™Jšœ,™,šœ™Jšœ ™ Jšœ™J™'J™$J˜—šΟk ˜ Jšœ˜Jšœ œœ˜Jšœœ˜$Jšœ œ˜Jšœ˜J˜Jšœœ˜8Jšœ˜Jšœœ9˜DJ˜J˜ J˜Jšœ œM˜\J˜J˜ JšœœG˜SJšœ œ˜%Jšœ œt˜ƒJšœ œ*˜;J˜J˜šœœœ˜ Jš˜Jšœœœ˜FJ˜OJšœ˜J˜——Jšœœœ˜%˜Jš œ œœœœ˜6J˜Jšœ œΟcH˜YJšœ œœœž;˜ZJ˜Jšœ0˜0Jšœœ˜J˜J˜J˜J˜˜J˜6—˜JšœF˜FJ˜—J˜J˜J˜—šΟn œœœœ4˜TJšœ ˜JšœR™RJšœR™RJšœT™TJšœU™UJšœ ™ šœœœœ˜Jšœœ˜Jšœ@˜@Jšœœœ˜Jšœœ˜Jšœ œœ˜J˜Jšœ™šœ?˜?JšœD˜DJšœ8˜8Jšœ*˜*—Jšœ+˜+Jšœ<˜™>šœ%œ,˜TJšœ˜—Jšœ1˜1Jšœ7˜7Jšœ1˜1Jšœ9˜9šœ˜Jšœ9˜9Jšœ5˜5šœ˜Jš œœœœ œœ˜>—J˜—Jšœ˜Jšœ˜Jšœ˜—Jšœ ˜Jšœ˜—J˜—šœ˜&šœ%œ!˜LJ˜>J˜3Jšœ œœ,˜EJ˜J˜˜šœ ˜šœž˜+Jšœ ˜ —šœž˜4šœ;˜;Jšœœœ˜)šœ œ˜šœ+˜+Jšœ9˜9Jšœ˜——Jšœ7˜;——šœž˜7šœ4˜4Jšœ œ˜#šœ.œ˜2Jšœ˜Jšœ˜Jšœœ"œ˜C—Jšœ$˜$Jšœ7˜7Jšœ1˜1Jšœ1˜1Jšœ˜——Jšœ˜ —J˜———šŸœœ˜0Jšœ4™4š˜Jšœ˜JšœD˜DšœN˜NJšœD˜Dšœœ˜JšœQ˜QJšœ˜—Jšœ@˜@Jšœœœ˜7—Jšœ6œ˜RJšœ;˜;Jšœ ˜ Jšœ œ-˜PJšœ œ-˜PJšœ œ/˜RJšœ#˜#šœ,˜,Jšœ œ0œ˜H—Jšœ'˜'Jšœ+ž"˜MJšœ+ž$˜OJšœ)ž˜:Jšœ-˜-Jšœ˜J˜——šŸœ˜%š˜Jšœœ!˜FJšœ?˜?J˜CJ˜˜šœ œ˜"Jšœ œ1˜A—šœ˜ Jšœ!˜!Jšœ,˜,J˜JšœH™HJšœ™Jšœ&œ˜,šœ"˜"Jšœ9œ˜@—Jšœ-˜-Jšœ˜——J˜—J˜—šŸ œœ œ˜NJš œ œœœœ˜/š˜JšœP˜Pšœœœœ ˜%JšœH˜L—J˜JšœD™DJšœ™šœœ˜šœ œ˜šœ$˜*šœ:˜:JšœFœ˜M——Jš˜—šœœœ˜BJšœœ˜=Jš˜šœ:˜:JšœFœ˜M——Jšœ˜Jšœ˜—Jšœ˜J˜—J˜—šŸœœœ ˜Jš˜Jšœ@˜@šœC˜CJšœ9œ˜@—šœœ˜JšœB˜BJšœ>˜>šœ:˜:JšœEœ˜L—šœ œ˜šœ3˜3Jšœ9œ˜@——šœœ˜šœ/˜/Jšœ3œ˜:——JšœD˜DJšœ˜—Jšœ˜J˜——šœ˜š˜Jšœœ ˜$šœœ7˜OJšœ˜—J˜$Jšœ˜J˜——šŸ œœœœ˜6š œœœ œ œœ ˜5Jš œœœ œ œœ ˜4Jš œœœ œ œœ ˜6Jš œœœ œ œœ ˜6Jšœœ(˜3—J˜—šŸ œ˜)šœ!œ!˜HJšœ#œ-˜VJšœœœœ˜-JšœC˜CJš œœœœ*œœ˜JJšœœœœ˜-Jšœœ˜J˜)J˜.Jšœ˜J˜——šŸ œ œœ%œ˜HJšœ œ˜šœœœœ˜J˜Jšœ"˜"Jšœ˜Jšœ œ˜šœ˜šœ8˜8Jšœ;˜;Jšœ>˜>Jšœ=˜=Jšœ œ˜Jšœœ˜Jšœ œ˜Jšœ$˜$š œ œœœ ˜2Jšœ œ@˜QJ˜ J˜$J˜6J˜—J˜J˜—Jšœ˜—Jšœ ˜Jšœ˜—J˜—š Ÿ œœœœ%œ˜GJšœ œ˜˜Jšœœœ˜JšœL˜LJšœ˜Jš œ œœœœ˜&šœ8œ˜OJšœ;˜;J˜Jšœ˜˜J˜3J˜(—˜$šœ"˜"JšœD˜DJ˜ —JšœC˜CJšœ&˜&Jšœ4˜4J˜—Jšœ˜J˜—J˜J˜—J˜—šŸ œœ0˜AJšœZ™ZJšœO™Ošœ˜Jšœ™šœ.œœ˜BJ˜J˜7Jšœ œ˜.Jšœ˜—Jšœ%™%šœ.œœ˜BJ˜J˜7Jšœ œ˜.Jšœ˜ J˜———Jšœœœœ˜0šœœœ˜Jšœž(˜9Jšœž8˜PJ˜J˜—šŸ œœœœ˜0Jšœœœ ˜JšœQ™QJšœ\™\Jšœ=™=šœœœœ˜Jšœœ˜"JšœL˜LJš œ œœœœ˜$šœ˜J˜Jšœ œ˜Jšœ"˜"š œ œœœ˜,Jšœ@™@Jšœ œY˜h—Jšœ˜J˜—Jšœ˜J˜J˜——š Ÿ œœœœœ ˜CJšœN™NJšœM™MJšœD™DJšœI™IJšœ™šœ˜J˜'Jšœœœ œ˜J˜7šœ:œœ˜KJšœœœ˜,—J˜Jšœ$˜*Jšœ˜—J˜—šŸœœœœ ˜FJšœœœ ˜JšœQ™QJšœZ™ZJšœW™WJšœO™OJšœK™KJšœP™Pš œœœœœ˜$šœ˜J˜'Jšœœœ œž3˜Vš œ œœœœ˜BJšœ(œ%˜QJšœT™TJšœF™Fšœœ˜"š œœœœœ˜7Jšœœœ˜0——J˜Jšœ˜—Jšœ%˜+J˜J˜———šŸœœ œ˜*šœ˜Jšœœ*˜Mšœ5˜8Jšœœ˜Jšœ˜Jš˜—Jšœ˜—J˜—šŸ œœ˜&šœ˜Jšœœ'˜Bšœœ˜Jšœ˜Jšœ œ)˜™>Jšœ@™@JšœU™UJšœN™NJšœV™VJšœD™DJšœU™UJšœ(™(˜Jšœ#˜#J˜>šœœ$˜/Jšœ*œœ˜9Jšœ2˜2—Jšœ œœ,˜CJ˜1Jšœœ˜Jšœœœœ˜:šœ˜˜ Jšœœœœœœ œœ ˜QJ˜—˜ J˜—˜ J˜,—˜ Jšœœœ˜š œ œ œœ œ œ˜?Jšœ œ˜J˜B—J˜—šœ˜ Jšœœ˜+šœœ˜šœž ˜J˜Fšœ œœ˜J˜?J˜2šœ œœ˜Jšœœž#˜8Jšœ œ˜Jšœ]˜]———šœž ˜J˜1šœœ˜ Jšœ œœ!˜KJšœœ œI˜rJšœœž#˜8Jšœ œ˜Jšœ˜ ——šœž ˜J˜<šœ œœ˜Jšœœž#˜8Jšœ œ˜J˜E——Jšœ˜———Jšœ œ;˜LJšœ˜—J˜—šŸ œœ˜0š˜Jšœ(œ˜-Jšœœ˜Jšœ˜J˜—J˜Jšœ1˜1J˜—JšœC™CJšœ ™ šœ ˜ š˜Jšœ"œ˜Ašœ"˜"Jšœ.˜4—Jšœ#˜#šœ"˜"J˜—Jšœ&˜&Jšœ*˜*šœ>œ˜JJšœ'˜'Jšœ+˜+Jšœ˜—šœ˜Jšœ œ œ˜7—šœC˜CJšœ0˜0—šœG˜GJšœ6˜6—Jšœ,˜,Jšœ˜—J˜—Jšœ=™=Jšœ.™.šœ!˜!š˜Jšœ"œ˜AJšœ#œ.˜WJšœœœ*˜FJšœ0˜0Jšœ,˜,Jšœ˜J˜——JšœD™DJšœE™EJšœ7™7šœ ˜ š˜Jšœ"œ˜Ašœ#œ˜*Jšœ-˜-—Jšœœ'˜:Jšœ#œ*˜SJšœ]˜]šœ:˜:Jšœ œ œ˜7—šœ4œ˜@Jšœ˜Jšœœ.˜HJšœœ,˜LJšœO˜OšœG˜GJšœ1˜1—Jšœ=˜=Jšœ0˜0Jšœ+˜+šœœœ˜ Jšœœ7˜SJšœ œ˜#Jšœ;˜;Jšœ˜—Jšœ,˜,Jšœ6˜6šœ5œ˜OJšœEœ˜K—˜JšœEœ˜L—Jš˜—šœP˜PšœG˜GJšœ1˜1——Jšœ"˜"Jšœ˜J˜——š Ÿœœœœœ˜+š˜Jšœ œ!˜.šœœž ˜&Jšœ˜ —Jšœœ.˜9Jšœ˜—J˜—J˜J˜J˜ J˜J˜™1J™%Jšœ Οr œB™W—™1J™7Jšœ  œV™i—J™™3Jšœ  ™™——…—VΘ€c