<> <> <> <> <> <> <> 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; <<-- make error message window>> 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; <<-- make a relation button>> 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; <<-- make old/new button>> 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; <<-- make the attribute label button and add all necessary props>> 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]; <<--move all the tuples below target; create new buttons for the duplicate>> <<-- attributes;>> 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] = <> <> <> <<"from" attribute equals the entity and whose other attributes satisfy the>> <> { 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: <> <> <> <> <> <> <<>> <> <>