<> <> <> <> DIRECTORY Ascii, Atom, CD, CDBasics, CDCells, CDCommandOps, CDDirectory, CDErrors, CDInstances, CDProperties, CDTextExtraction, CDSequencer, CDSymbolicObjects, CDTexts, CDOps, IO, PropertyLists, Rope, TerminalIO; CDTextExtractionImpl: CEDAR PROGRAM IMPORTS Ascii, Atom, CDBasics, CDCells, CDCommandOps, CDDirectory, CDErrors, CDInstances, CDProperties, CDSequencer, CDSymbolicObjects, CDTexts, CDOps, IO, Rope, TerminalIO EXPORTS CDTextExtraction SHARES CDProperties = BEGIN lorPropKey: ATOM = $TextExtraction; PropRec: TYPE = RECORD [key: ATOM_NIL, value: REF_NIL, onOb: BOOL_FALSE]; PutLorProp: PROC [x: REF, text: Rope.ROPE] = BEGIN lor: LIST OF Rope.ROPE _ WITH CDProperties.GetProp[x, lorPropKey] SELECT FROM rl: LIST OF Rope.ROPE => rl, ENDCASE => NIL; lor _ CONS[text, lor]; CDProperties.PutProp[x, lorPropKey, lor]; END; PutPropRec: PROC [inst: CD.Instance, prop: PropRec] = <<--puts the property onto the instance or the object>> BEGIN IF prop.onOb THEN { IF inst.ob.class.inDirectory THEN CDProperties.PutProp[onto: inst.ob, prop: prop.key, val: prop.value] } ELSE CDProperties.PutProp[onto: inst, prop: prop.key, val: prop.value] END; CheckPropRec: PROC [inst: CD.Instance, prop: PropRec] RETURNS [match: BOOL] = <<--checks whether the instance or object has the same prop property>> BEGIN Similar: PROC [x, y: REF] RETURNS [BOOL_FALSE] = { <<--returns x and y are similar (considered ROPE's)>> IF x=y THEN RETURN [TRUE]; IF ISTYPE[x, Rope.ROPE] AND ISTYPE[y, Rope.ROPE] THEN { RETURN [ Rope.Equal[ NARROW[x, Rope.ROPE], NARROW[y, Rope.ROPE] ] ] }; }; match _ prop.key#NIL AND Similar[prop.value, CDProperties.GetProp[from: (IF prop.onOb THEN inst.ob ELSE inst), prop: prop.key] ] END; ScanPropRec: PROC [ob: CD.Object] RETURNS [PropRec] = <<--find out property description from text object>> BEGIN RemoveBlanks: PROC [r: Rope.ROPE] RETURNS [Rope.ROPE] = { <<-- returns rope with leading and trailing spaces removed>> RemoveTrailingBlanks: PROC [r: Rope.ROPE] RETURNS [Rope.ROPE] = INLINE { <<-- returns rope with trailing spaces removed>> n: INT _ r.Length[]; WHILE n>0 AND r.Fetch[n-1]=' DO n _ n-1 ENDLOOP; RETURN [r.Substr[start: 0, len: n]]; }; RemoveLeadingBlanks: PROC [r: Rope.ROPE] RETURNS [Rope.ROPE] = INLINE { <<-- returns rope with leading spaces removed>> RETURN [r.Substr[start: r.SkipOver[skip: " "]]] }; RETURN [RemoveLeadingBlanks[RemoveTrailingBlanks[r]]] }; Scan: PROC [text: Rope.ROPE] RETURNS [pRec: PropRec _ [NIL, NIL]] = INLINE { n: INT _ text.SkipTo[skip: ":"]; IF n RETURN [t _ tp.text] ENDCASE => NULL; }; <<>> <<--ScanPropRec>> RETURN [Scan[Text[ob]]] END; Public: PROC [key: ATOM] RETURNS [BOOL_TRUE] = <<-- returns: property key is accessible for interactive actions>> BEGIN IF key=NIL THEN RETURN [FALSE] ELSE { pType: CDProperties.PropertyProcs = CDProperties.FetchProcs[key]; RETURN [ pType=NIL OR ~pType.exclusive ]; } END; Kind: TYPE = {stuff, anyProp, topProp, ignore}; <<--anything is stuff except certain objects used by this module>> KindOf: PROC [ob: CD.Object] RETURNS [kind: Kind_stuff] = <<--determine usage of an object as property indicator...>> BEGIN IF ob.class.wireTyped THEN { IF ob.layer<=CD.selectionLayer THEN kind _ ignore } ELSE IF ob.layer=CD.commentLayer THEN WITH ob.specificRef SELECT FROM tp: CDTexts.TextPtr => { fn: Rope.ROPE ~ tp.cdFont.supposedName; IF Ascii.Upper[fn.Fetch[fn.Length[]-1]]='I THEN kind _ ignore ELSE IF fn.Length[]>=27 AND Rope.Equal["Xerox/TiogaFonts/", fn.Substr[len: 17], FALSE] THEN { IF Rope.Equal["Helvetica", fn.Substr[17, 9], FALSE] THEN kind _ anyProp ELSE IF Rope.Equal["TimesRoman", fn.Substr[17, 10], FALSE] THEN kind _ topProp } }; ENDCASE => NULL; END; Separation: TYPE = ARRAY Kind OF CD.InstanceList _ ALL[NIL]; Split: PROC [list: CD.InstanceList] RETURNS [s: Separation] = <<--Splits instancelist into different lists (reduces number of instances to be considered!)>> <<--SideEffect: removes lorProp properties>> BEGIN FOR l: CD.InstanceList _ list, l.rest WHILE l#NIL DO k: Kind _ KindOf[l.first.ob]; s[k] _ CONS[l.first, s[k]]; CDProperties.PutInstanceProp[l.first, lorPropKey, NIL]; ENDLOOP; END; UseCPtr: PROC[design: CD.Design, ob: CD.Object] RETURNS [cp: CD.CellPtr_NIL] = <<--gets an appropriate CellPtr... (or NIL)>> BEGIN IF ob=NIL THEN cp _ design^.actual.first.specific ELSE IF CDCells.IsCell[ob] THEN cp _ NARROW[ob.specificRef] ELSE IF ob.class.inDirectory THEN { new: CD.Object; tm, cm: CDDirectory.DMode; [new, tm, cm] _ CDDirectory.Expand[ob]; IF new#NIL AND new#ob AND tm#immutable THEN { IF tm=ready THEN [] _ CDDirectory.Include[design, new]; cp _ UseCPtr[design, new]; } }; END; ExtractProperties: PUBLIC PROC [design: CD.Design, cell: CD.Object_NIL, specialMode: BOOL _ FALSE, checkOnly: BOOL_FALSE] RETURNS [matches: INT_0, errors: INT_0] = BEGIN Message: PROC [text: Rope.ROPE, r, r2: CD.Rect _ CDBasics.universe] = { blow: CD.Number ~ (IF design#NIL THEN design.technology.lambda ELSE 4); [] _ CDErrors.IncludeMessage[design: design, ob: cell, rect: CDBasics.Intersection[CDBasics.Extend[r, blow], CDBasics.Extend[r2, blow]], message: text, owner: $PropertyChecker ]; errors _ errors+1; }; MatchFirst: PROC [tr: CD.Rect, l: CD.InstanceList] RETURNS [BOOL] = INLINE { RETURN [ CDBasics.Intersect[tr, CDInstances.InstRectO[l.first]] AND CDBasics.Intersect[tr, CDInstances.InstRectI[l.first]] ] }; ExtractLORText: PROC [stuff: CD.InstanceList, ti: CD.Instance] = BEGIN tp: CDTexts.TextPtr ~ NARROW[ti.ob.specificRef]; tr: CD.Rect ~ CDInstances.InstRectI[ti]; match: BOOL _ FALSE; FOR l: CD.InstanceList _ stuff, l.rest WHILE l#NIL DO IF MatchFirst[tr, l] THEN { match _ TRUE; PutLorProp[l.first, tp.text] } ENDLOOP; IF match THEN matches _ matches+1 ELSE Message["property does not match anything", tr]; END; ExtractANYText: PROC [stuff: CD.InstanceList, ti: CD.Instance] = BEGIN pRec: PropRec ~ ScanPropRec[ti.ob]; tr: CD.Rect ~ CDInstances.InstRectI[ti]; IF Public[pRec.key] THEN { match: BOOL _ FALSE; FOR l: CD.InstanceList _ stuff, l.rest WHILE l#NIL DO IF MatchFirst[tr, l] THEN { match _ TRUE; PutPropRec[l.first, pRec]; } ENDLOOP; IF match THEN matches _ matches+1 } END; CheckANYText: PROC [stuff: CD.InstanceList, ti: CD.Instance] = BEGIN pRec: PropRec ~ ScanPropRec[ti.ob]; tr: CD.Rect ~ CDInstances.InstRectI[ti]; IF Public[pRec.key] THEN { match: BOOL _ FALSE; FOR l: CD.InstanceList _ stuff, l.rest WHILE l#NIL DO IF MatchFirst[tr, l] THEN { match _ TRUE; IF ~CheckPropRec[l.first, pRec] THEN Message["property missmatch", tr, CDInstances.InstRectI[l.first]]; }; ENDLOOP; IF ~match THEN Message["property does not match anything", tr] } ELSE Message["bad or protected property", tr] END; <<--ExtractProperties>> s: Separation; cPtr: CD.CellPtr ~ UseCPtr[design, cell]; IF cPtr=NIL THEN RETURN [errors _ 1]; CDErrors.RemoveMessages[design, cell, $PropertyChecker]; s _ Split[cPtr.contents]; IF specialMode THEN { <<--special properties on instances>> FOR l: CD.InstanceList _ s[anyProp], l.rest WHILE l#NIL DO ExtractLORText[s[stuff], l.first] ENDLOOP; } ELSE { <<--normal properties on instances>> IF ~checkOnly THEN <<--extract ordinary props>> FOR l: CD.InstanceList _ s[anyProp], l.rest WHILE l#NIL DO ExtractANYText[s[stuff], l.first]; ENDLOOP; <<--check ordinary props>> FOR l: CD.InstanceList _ s[anyProp], l.rest WHILE l#NIL DO CheckANYText[s[stuff], l.first] ENDLOOP; }; <<--properties on cell itself>> IF ~checkOnly AND cell#NIL THEN { rl: LIST OF Rope.ROPE_NIL; FOR l: CD.InstanceList _ s[topProp], l.rest WHILE l#NIL DO tp: CDTexts.TextPtr ~ NARROW[l.first.ob.specificRef]; rl _ CONS[tp.text, rl] ENDLOOP; CDProperties.PutObjectProp[cell, lorPropKey, rl]; }; END; ExtractTopComm: PROC [comm: CDSequencer.Command] = BEGIN matches, errors: INT; TerminalIO.WriteRope["extract properties in top level\n"]; [matches, errors] _ ExtractProperties[comm.design]; TerminalIO.WriteF[" %g texts used in top level; %g problem(s) found\n", IO.int[matches], IO.int[errors]]; END; ExtractSelComm: PROC [comm: CDSequencer.Command] = BEGIN inst: CD.Instance _ CDCommandOps.TheInstance[comm, "extract properties selected"]; IF inst#NIL THEN { matches, errors: INT; [matches, errors] _ ExtractProperties[comm.design, inst.ob]; TerminalIO.WriteF[" %g texts used in `%g'; %g problem(s) found\n", IO.int[matches], IO.rope[CDOps.ObjectInfo[inst.ob]], IO.int[errors]]; } END; ---------------- OkToRemoveProperties: PROC [i: CD.Instance] RETURNS [BOOL_TRUE] = BEGIN IF CDSymbolicObjects.IsSymbolicOb[i.ob] THEN RETURN [FALSE]; IF i.ob.class.wireTyped THEN RETURN [i.ob.layer#CD.errorLayer]; END; RemInstProps: PROC [i: CD.Instance] = <<--removes all public properties of an instance>> BEGIN DoIt: PROC = { copy: CD.PropList _ NIL; FOR l: PropertyLists.PropList _ i.properties, l.rest WHILE l#NIL DO WITH l.first.key SELECT FROM a: ATOM => IF ~Public[a] THEN copy _ CONS[l.first, copy]; ENDCASE => copy _ CONS[l.first, copy]; ENDLOOP; i.properties _ copy; }; IF OkToRemoveProperties[i] THEN CDProperties.DoWithinLock[DoIt] END; RemovePublicProperties: PUBLIC PROC [design: CD.Design, ob: CD.Object_NIL] = <<--removes all public properties>> BEGIN cPtr: CD.CellPtr ~ UseCPtr[design, ob]; IF cPtr#NIL THEN FOR l: CD.InstanceList _ cPtr.contents, l.rest WHILE l#NIL DO RemInstProps[l.first]; ENDLOOP; END; RemTopComm: PROC [comm: CDSequencer.Command] = BEGIN TerminalIO.WriteRope["remove all properties in top level\n"]; RemovePublicProperties[comm.design]; END; RemSelInstPropsComm: PROC [comm: CDSequencer.Command] = BEGIN inst: CD.Instance _ CDCommandOps.TheInstance[comm, "remove properties of selected instance\n"]; IF inst#NIL THEN { IF OkToRemoveProperties[inst] THEN RemInstProps[inst] ELSE TerminalIO.WriteRope["don't remove the properties of this object\n"]; } END; IsAnyText: PROC [ob: CD.Object] RETURNS [yes: BOOL_FALSE] = INLINE <<-- whether ob is a text object>> BEGIN yes _ CDTexts.IsText[ob] END; SelectFromPropComm: PROC [comm: CDSequencer.Command] = BEGIN inst: CD.Instance _ CDCommandOps.TheInstance[comm, "select using property"]; IF inst#NIL THEN { design: CD.Design _ comm.design; pRec: PropRec; cnt: INT _ 0; IF IsAnyText[inst.ob] THEN pRec _ ScanPropRec[inst.ob] ELSE IF inst.ob.class.wireTyped OR CDSymbolicObjects.IsSymbolicOb[inst.ob] THEN pRec _ [$SignalName, CDProperties.GetProp[inst, $SignalName], FALSE] ELSE { TerminalIO.WriteRope[" not done: selection is neither text (Helvetica) nor rectangle\n"]; RETURN; }; IF pRec.key=NIL THEN { TerminalIO.WriteRope[" not done: property key is NIL\n"]; RETURN; }; TerminalIO.WriteF[" key: %g, value: %g\n", IO.atom[pRec.key], IO.rope[(IF pRec.value=NIL THEN "NIL" ELSE CDCommandOps.ToRope[pRec.value])] ]; FOR l: CD.InstanceList _ CDOps.InstList[design], l.rest WHILE l#NIL DO IF CheckPropRec[l.first, pRec] THEN { l.first.selected _ TRUE; CDOps.RedrawInstance[design, l.first, FALSE]; cnt _ cnt+1 }; ENDLOOP; IF cnt>0 AND CDTexts.IsText[inst.ob] THEN { inst.selected _ FALSE; CDOps.RedrawInstance[design, inst]; }; TerminalIO.WriteF[" %g instance(s) selected\n", IO.int[cnt]]; }; END; PropagateSignalNameComm: PROC [comm: CDSequencer.Command] = BEGIN inst: CD.Instance ~ CDCommandOps.TheInstance[comm, "propagate signal name"]; IF inst#NIL THEN { design: CD.Design ~ comm.design; foundL: CD.InstanceList _ LIST[inst]; no: INT _ 1; value: REF _ CDProperties.GetProp[from: inst, prop: $SignalName]; IF value=NIL THEN TerminalIO.WriteRope[" not done: no signal name\n"] ELSE { WHILE foundL#NIL DO curr: CD.Instance ~ foundL.first; currR: CD.Rect ~ CDInstances.InstRectI[curr]; foundL _ foundL.rest; FOR l: CD.InstanceList _ CDOps.InstList[design], l.rest WHILE l#NIL DO IF ~l.first.selected AND l.first.ob.class.wireTyped AND l.first.ob.layer=curr.ob.layer THEN { IF CDBasics.Intersect[currR, CDInstances.InstRectI[l.first]] THEN { foundL _ CONS[l.first, foundL]; l.first.selected _ TRUE; no _ no+1; CDProperties.PutProp[l.first, $SignalName, value]; CDOps.RedrawInstance[design, l.first, FALSE]; }; }; ENDLOOP; ENDLOOP; TerminalIO.WriteF[" %g instance(s) selected\n", IO.int[no]]; }; }; END; FindSelectedProperties: PROC [design: CD.Design] RETURNS [doList: LIST OF PropRec_NIL] = <<--and tells its result with TerminalIO>> BEGIN Occurs: PROC [list: LIST OF PropRec, key: ATOM] RETURNS [yes: BOOL _ FALSE] = INLINE { FOR pl: LIST OF PropRec _ list, pl.rest WHILE pl#NIL DO IF pl.first.key=key THEN RETURN [yes _ TRUE] ENDLOOP; }; FOR l: CD.InstanceList _ CDOps.InstList[design], l.rest WHILE l#NIL DO IF l.first.selected AND IsAnyText[l.first.ob] THEN { pr: PropRec = ScanPropRec[l.first.ob]; IF ~Public[pr.key] THEN { TerminalIO.WriteRope["unknown or restricted property\n"]; [] _ CDErrors.IncludeMessage[design: design, ob: NIL, rect: CDInstances.InstRectI[l.first], message: "bad property", owner: $PropertyChecker ]; } ELSE IF Occurs[doList, pr.key] THEN { TerminalIO.WriteF[" ** property: %g, value: %g twice or contradicting! ignored\n", IO.atom[pr.key], IO.rope[(IF pr.value=NIL THEN "NIL" ELSE CDCommandOps.ToRope[pr.value])] ]; } ELSE { TerminalIO.WriteF[" property: %g, value: %g\n", IO.atom[pr.key], IO.rope[(IF pr.value=NIL THEN "NIL" ELSE CDCommandOps.ToRope[pr.value])] ]; doList _ CONS[pr, doList]; } } ENDLOOP; END; SetPropWithSelectionComm: PROC [comm: CDSequencer.Command] = BEGIN design: CD.Design _ comm.design; doList: LIST OF PropRec; TerminalIO.WriteRope["set properties using the selection\n"]; doList _ FindSelectedProperties[design]; IF doList=NIL THEN TerminalIO.WriteRope["no texts selected"] ELSE { FOR il: CD.InstanceList _ CDOps.InstList[design], il.rest WHILE il#NIL DO IF il.first.selected AND ~IsAnyText[il.first.ob] THEN { FOR pl: LIST OF PropRec _ doList, pl.rest WHILE pl#NIL DO PutPropRec[il.first, pl.first] ENDLOOP; } ENDLOOP; TerminalIO.WriteRope[" --\n"]; }; END; SetPropToPushedInCell: PROC [comm: CDSequencer.Command] = BEGIN design: CD.Design _ comm.design; doList: LIST OF PropRec; object: REF; IF CDCells.IsPushedIn[design] THEN { TerminalIO.WriteRope["set selected properties to the pushed in cell\n"]; object _ design.actual.first.mightReplace.ob; } ELSE { TerminalIO.WriteRope["set selected properties to the design\n"]; object _ design; }; doList _ FindSelectedProperties[design]; IF doList=NIL THEN TerminalIO.WriteRope["no texts selected"] ELSE { FOR pl: LIST OF PropRec _ doList, pl.rest WHILE pl#NIL DO CDProperties.PutProp[object, pl.first.key, pl.first.value] ENDLOOP; TerminalIO.WriteRope[" --\n"]; }; END; Register: PROC [p: CDSequencer.CommandProc, key: ATOM] = { CDSequencer.ImplementCommand[key: key, proc: p]; }; [] _ CDProperties.RegisterProperty[lorPropKey, $chj0]; CDProperties.InstallProcs[ prop: lorPropKey, new: CDProperties.PropertyProcsRec[exclusive: TRUE, makeCopy: CDProperties.CopyVal] ]; Register[ExtractTopComm, $ExtractPropTop]; Register[ExtractSelComm, $ExtractPropSel]; Register[RemTopComm, $RemPropTop]; Register[RemSelInstPropsComm, $RemPropSel]; Register[SetPropWithSelectionComm, $SetPropWithSelection]; Register[SetPropToPushedInCell, $SetPropPushed]; Register[SelectFromPropComm, $SelectFromProp]; Register[PropagateSignalNameComm, $PropagateSignalName]; END.