DIRECTORY Atom, Convert, FileNames, GGBasicTypes, GGBoundBox, GGBoxCluster, GGCluster, GGError, GGInterfaceTypes, GGModelTypes, GGParseOut, GGParseIn, GGTransform, Imager, ImagerFont, ImagerTransformation, IO, NodeStyle, NodeStyleFont, Real, Rope; GGClusterImpl: CEDAR PROGRAM IMPORTS Atom, Convert, FileNames, GGBoundBox, GGBoxCluster, GGError, GGParseIn, GGParseOut, GGTransform, Imager, ImagerFont, IO, NodeStyleFont, Real, Rope EXPORTS GGCluster = BEGIN Point: TYPE = GGBasicTypes.Point; ClusterObj: TYPE = GGModelTypes.ClusterObj; ClusterClass: TYPE = GGModelTypes.ClusterClass; ClusterClassObj: TYPE = GGModelTypes.ClusterClassObj; ClusterParts: TYPE = GGModelTypes.ClusterParts; Cluster: TYPE = GGModelTypes.Cluster; SelectMode: TYPE = GGModelTypes.SelectMode; ExtendMode: TYPE = GGModelTypes.ExtendMode; BoundBox: TYPE = GGModelTypes.BoundBox; SelectedObjectData: TYPE = GGModelTypes.SelectedObjectData; CameraData: TYPE = GGInterfaceTypes.CameraData; DisplayStyle: TYPE = GGCluster.DisplayStyle; ClusterClassDef: TYPE = REF ClusterClassDefObj; ClusterClassDefObj: TYPE = RECORD[type: ATOM, class: ClusterClass]; TextData: TYPE = REF TextDataObj; TextDataObj: TYPE = RECORD [ rope: Rope.ROPE, worldPt: Point, fontStyle: DisplayStyle, -- the desired font style trueFontStyle: DisplayStyle, -- the actual font style fontString: Rope.ROPE _ NIL, -- the desired font string trueFontString: Rope.ROPE _ NIL, -- the actual font string fontFamily: ATOM, fontFace: NodeStyle.FontFace, fontSize: REAL, font: ImagerFont.Font, validFontInfo: BOOL _ FALSE, -- TRUE if desires match actual color: Imager.Color, colorName: Rope.ROPE, feedbackBox: BoundBox ]; TextHitData: TYPE = REF TextHitDataObj; TextHitDataObj: TYPE = RECORD [ bestSeg: NAT _ 0 -- segment number from ClosestSegementProc ]; IPData: TYPE = REF IPDataObj; IPDataObj: TYPE = RECORD [ file: Rope.ROPE, worldPt: Point, feedbackBox: BoundBox, transform: ImagerTransformation.Transformation ]; IPHitData: TYPE = REF IPHitDataObj; IPHitDataObj: TYPE = RECORD [ bestSeg: NAT _ 0 -- segment number from ClosestSegementProc ]; NotFound: PUBLIC SIGNAL = CODE; FontStringError: PUBLIC SIGNAL = CODE; FetchClusterClass: PUBLIC PROC [type: ATOM] RETURNS [class: ClusterClass] = { FOR l: LIST OF ClusterClassDef _ clusterClasses, l.rest UNTIL l=NIL DO IF l.first.type=type THEN RETURN[l.first.class]; ENDLOOP; SIGNAL NotFound; RETURN[NIL]; }; BuildTextClusterClass: PROC [] RETURNS [class: ClusterClass] = { class _ NEW[ClusterClassObj _ [ type: $Text, boundBox: TextBoundBox, copy: TextCopy, draw: TextDraw, drawTransform: TextDrawTransform, drawSelectionFeedback: TextDrawSelectionFeedback, transform: TextTransform, emptyParts: TextEmptyParts, newParts: TextNewParts, addParts: TextAddParts, removeParts: TextRemoveParts, closestPoint: TextClosestPoint, closestPointAndTangent: NIL, closestSegment: TextClosestSegment, fileout: TextFileout, filein: TextFilein ]]; }; MakeTextCluster: PUBLIC PROC [text: Rope.ROPE, fontStyle: DisplayStyle, fontString: Rope.ROPE, colorName: Rope.ROPE, worldPt: Point] RETURNS [clus: Cluster] = { feedbackBox: BoundBox _ GGBoundBox.CreateBoundBox[0,0,0,0]; textData: TextData _ NEW[TextDataObj _ [text, worldPt, , , , , , , , , FALSE, Imager.black, colorName, feedbackBox]]; clus _ NEW[ClusterObj _ [ class: FetchClusterClass[$Text], data: textData, children: NIL, parent: NIL, selectedInFull: [FALSE, FALSE, FALSE, FALSE], boundBox: GGBoundBox.CreateBoundBox[0,0,0,0], -- gets filled in later onOverlay: FALSE, hitData: NEW[TextHitDataObj _ [] ] ]]; SetTextFont[clus, fontStyle, fontString]; }; GetText: PUBLIC PROC [clus: Cluster] RETURNS [text: Rope.ROPE] = { RETURN[IF clus.class.type#$Text THEN NIL ELSE NARROW[clus.data, TextData].rope]; }; AppendText: PUBLIC PROC [clus: Cluster, text: Rope.ROPE] = { textData: TextData; IF clus.class.type#$Text THEN RETURN; textData _ NARROW[clus.data]; textData.rope _ Rope.Concat[textData.rope, text]; -- update text clus.class.boundBox[clus]; -- update bound box }; BackspaceText: PUBLIC PROC [clus: Cluster] = { textData: TextData; IF clus.class.type#$Text THEN RETURN; textData _ NARROW[clus.data]; textData.rope _ Rope.Substr[base: textData.rope, len: Rope.Length[textData.rope]-1]; -- update text clus.class.boundBox[clus]; -- update bound box }; DigitBreak: IO.BreakProc = { RETURN[IF char IN ['0..'9] THEN break ELSE other]; }; FontParamsFromFontString: PUBLIC PROC [fontStyle: DisplayStyle, fontString: Rope.ROPE] RETURNS [fontPrefix: ATOM, fontFamily: ATOM, fontFace: NodeStyle.FontFace, fontSize: REAL] = { GetNumber: PROC [s: IO.STREAM] RETURNS [x: REAL _ 0.0] = { c: CHAR; UNTIL (c _ IO.GetChar[s ! IO.EndOfStream => {c _ 'A; CONTINUE;}; ]) NOT IN ['0..'9] DO x _ x*10+(c-'0); ENDLOOP; }; familyName, face: Rope.ROPE; bold, italic: BOOL _ FALSE; fontStream: IO.STREAM _ IO.RIS[fontString]; [familyName, ----] _ IO.GetTokenRope[stream: fontStream, breakProc: DigitBreak ! IO.EndOfStream => SIGNAL FontStringError]; fontSize _ GetNumber[fontStream ! IO.EndOfStream => SIGNAL FontStringError]; [face, ----] _ IO.GetTokenRope[stream: fontStream, breakProc: IO.TokenProc ! IO.EndOfStream => {face _ ""; CONTINUE; };]; fontPrefix _ Atom.MakeAtom[IF fontStyle IN [print..wysiwyg] THEN "xerox/pressfonts/" ELSE "xerox/tiogafonts/"]; fontFamily _ Atom.MakeAtom[familyName]; SELECT Rope.Length[face] FROM 0 => NULL; 1 => bold _ Rope.Fetch[base: face, index: 0]='B; 2 => { bold _ Rope.Fetch[base: face, index: 0]='B; italic _ Rope.Fetch[base: face, index: 1]='I; }; ENDCASE => NULL; fontFace _ SELECT TRUE FROM bold AND italic => BoldItalic, bold => Bold, italic => Italic, ENDCASE => Regular; }; UpdateFontInfo: PRIVATE PROC [clus: Cluster, displayStyle: DisplayStyle] = { textData: TextData; font: Imager.Font; fontPrefix, fontFamily: ATOM; fontFace: NodeStyle.FontFace; fontSize: REAL _ 0.0; IF clus.class.type#$Text THEN ERROR; -- shouldn't happen textData _ NARROW[clus.data]; IF textData.fontString=NIL THEN ERROR; -- shouldn't happen IF displayStyle= textData.trueFontStyle AND Rope.Equal[textData.fontString, textData.trueFontString, FALSE] THEN { textData.fontStyle _ displayStyle; clus.class.boundBox[clus]; -- update bound box textData.validFontInfo _ TRUE; -- last thing to set RETURN; }; [fontPrefix, fontFamily, fontFace, fontSize] _ FontParamsFromFontString[displayStyle, textData.fontString ! FontStringError => GOTO Abort; ]; font _ NodeStyleFont.FontFromStyleParams[prefix: fontPrefix, family: fontFamily, face: fontFace, size: fontSize, alphabets: CapsAndLower]; IF Rope.Find[s1: font.name, s2: Atom.GetPName[fontFamily], case: FALSE]=-1 THEN { font _ NodeStyleFont.FontFromStyleParams[prefix: Atom.MakeAtom["xerox/pressfonts/"], family: fontFamily, face: fontFace, size: fontSize, alphabets: CapsAndLower]; IF Rope.Find[s1: font.name, s2: Atom.GetPName[fontFamily], case: FALSE]=-1 THEN GOTO Abort; }; textData.fontStyle _ textData.trueFontStyle _ displayStyle; textData.trueFontString _ textData.fontString; textData.fontFamily _ fontFamily; textData.fontFace _ fontFace; textData.fontSize _ fontSize; textData.font _ font; clus.class.boundBox[clus]; -- update bound box textData.validFontInfo _ TRUE; -- last thing to set EXITS Abort => GGError.AppendHerald["Gargoyle Font not Found", TRUE]; }; SetTextFont: PUBLIC PROC [clus: Cluster, fontStyle: DisplayStyle, fontString: Rope.ROPE] = { textData: TextData; IF clus.class.type#$Text OR fontString=NIL THEN RETURN; textData _ NARROW[clus.data]; textData.validFontInfo _ FALSE; textData.fontStyle _ fontStyle; textData.fontString _ fontString; UpdateFontInfo[clus, fontStyle]; }; GetTextFont: PUBLIC PROC [clus: Cluster] RETURNS [font: ImagerFont.Font, fontString: Rope.ROPE] = { textData: TextData; IF clus.class.type#$Text THEN RETURN[NIL, NIL]; textData _ NARROW[clus.data]; RETURN[textData.font, textData.trueFontString]; }; SetTextColor: PUBLIC PROC [clus: Cluster, color: Imager.Color, colorName: Rope.ROPE] = { textData: TextData; IF clus.class.type#$Text THEN RETURN; textData _ NARROW[clus.data]; textData.color _ color; textData.colorName _ colorName; }; GetTextColor: PUBLIC PROC [clus: Cluster] RETURNS [color: Imager.Color, colorName: Rope.ROPE] = { textData: TextData; IF clus.class.type#$Text THEN RETURN[NIL, NIL]; textData _ NARROW[clus.data]; RETURN[textData.color, textData.colorName]; }; TextBoundBox: PROC [cluster: Cluster] = { textData: TextData _ NARROW[cluster.data]; extents: ImagerFont.Extents; halfCP: REAL = GGModelTypes.halfJointSize + 1; extents _ ImagerFont.RopeBoundingBox[textData.font, textData.rope]; GGBoundBox.UpdateBoundBox[cluster.boundBox, textData.worldPt[1] - extents.leftExtent - halfCP, textData.worldPt[2] - extents.descent - halfCP, textData.worldPt[1] + extents.rightExtent + halfCP, textData.worldPt[2] + extents.ascent + halfCP]; GGBoundBox.UpdateBoundBox[textData.feedbackBox, textData.worldPt[1] - extents.leftExtent, textData.worldPt[2] - extents.descent, textData.worldPt[1] + extents.rightExtent, textData.worldPt[2] + extents.ascent]; }; TextCopy: PROC [cluster: Cluster] RETURNS [copy: Cluster] = { textData: TextData _ NARROW[cluster.data]; copy _ MakeTextCluster[textData.rope, textData.fontStyle, textData.fontString, textData.colorName, textData.worldPt]; copy.parent _ cluster.parent; copy.children _ cluster.children; }; TextDraw: PROC [cluster: Cluster, dc: Imager.Context, camera: CameraData] = { textData: TextData _ NARROW[cluster.data]; IF NOT textData.validFontInfo THEN UpdateFontInfo[cluster, camera.displayStyle]; Imager.SetFont[dc, textData.font]; Imager.SetXY[dc, [textData.worldPt[1], textData.worldPt[2]]]; Imager.ShowRope[dc, textData.rope]; }; TextDrawTransform: PROC [cluster: Cluster, parts: ClusterParts, dc: Imager.Context, camera: CameraData, transform: ImagerTransformation.Transformation] = { textData: TextData _ NARROW[cluster.data]; tempPoint: Point _ GGTransform.Transform[transform, textData.worldPt]; IF NOT textData.validFontInfo THEN UpdateFontInfo[cluster, camera.displayStyle]; Imager.SetFont[dc, textData.font]; Imager.SetXY[dc, [ tempPoint[1], tempPoint[2] ] ]; Imager.ShowRope[dc, textData.rope]; }; TextDrawSelectionFeedback: PROC [cluster: Cluster, parts: ClusterParts, dc: Imager.Context, camera: CameraData, quick: BOOL _ FALSE] = { textData: TextData _ NARROW[cluster.data]; IF NOT textData.validFontInfo THEN UpdateFontInfo[cluster, camera.displayStyle]; GGBoundBox.DrawBoundBox[dc, textData.feedbackBox]; }; TextTransform: PROC [cluster: Cluster, parts: ClusterParts, transform: ImagerTransformation.Transformation] = { textData: TextData _ NARROW[cluster.data]; textData.worldPt _ GGTransform.Transform[transform, textData.worldPt]; TextBoundBox[cluster]; }; TextEmptyParts: PROC [cluster: Cluster, parts: ClusterParts] RETURNS [BOOL] = { RETURN[TRUE] }; TextNewParts: PROC [cluster: Cluster, mode: SelectMode] RETURNS [parts: ClusterParts] = { RETURN[NIL] }; TextAddParts: PROC [cluster: Cluster, parts: ClusterParts, mode: ExtendMode] RETURNS [newParts: ClusterParts] = { RETURN[NIL] }; TextRemoveParts: PROC [cluster: Cluster, parts: ClusterParts, mode: ExtendMode] RETURNS [newParts: ClusterParts] = { RETURN[NIL] }; TextClosestPoint: PROC [cluster: Cluster, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = { textData: TextData _ NARROW[cluster.data]; [bestDist, ----, bestPoint, success] _ GGBoundBox.NearestPoint[textData.feedbackBox, testPoint]; }; TextClosestSegment: PROC [cluster: Cluster, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = { textData: TextData _ NARROW[cluster.data]; textHitData: TextHitData _ NARROW[cluster.hitData]; [bestDist, textHitData.bestSeg, bestPoint, success] _ GGBoundBox.NearestSegment[textData.feedbackBox, testPoint, tolerance]; }; TextFileout: PROC [cluster: Cluster, f: IO.STREAM] = { textData: TextData _ NARROW[cluster.data]; f.PutF["\"%g\" %g %g", [rope[textData.rope]], [rope[textData.fontString]], [real[textData.fontSize]] ]; f.PutF[" %g ", [rope[textData.colorName]] ]; GGParseOut.WritePoint[f, textData.worldPt]; }; TextFilein: PROC [f: IO.STREAM, version: REAL] RETURNS [clus: Cluster] = { UnpackComplexFontName: PROC [fontName: Rope.ROPE, fontSize: REAL] RETURNS [new: Rope.ROPE] = { face: Rope.ROPE; new _ FileNames.GetShortName[fontName]; -- throw away "xerox/pressfonts/" face _ FileNames.Tail[new, '-]; -- get face component (MIR, BRR, ...) new _ Rope.Substr[base: new, start: 0, len: Rope.SkipTo[s: new, pos: 0, skip: "-"]]; -- throw away "-XXX" new _ Rope.Cat[new, Convert.RopeFromInt[Real.FixI[fontSize]], SELECT TRUE FROM Rope.Equal[face, "MRR", FALSE] => "", Rope.Equal[face, "BRR", FALSE] => "B", Rope.Equal[face, "MIR", FALSE] => "I", Rope.Equal[face, "BIR", FALSE] => "BI", ENDCASE => ERROR ]; }; rope, fontName, colorName: Rope.ROPE; fontSize: REAL; point: Point; IF version > 8601.22 THEN { rope _ f.GetRopeLiteral[]; } ELSE { GGParseIn.ReadBlankAndRope[f, "("]; rope _ GGParseIn.ReadBlankAndWord[f]; GGParseIn.ReadBlankAndRope[f, ")"]; }; IF version <= 8601.06 THEN { -- no font name at all fontName _ "Helvetica10"; fontSize _ 10.0; colorName _ "black"; } ELSE IF version <= 8601.27 THEN { -- a simple name like "Helvetica" or "Gacha" fontName _ GGParseIn.ReadBlankAndWord[f]; fontName _ Rope.Concat[fontName, "10"]; fontSize _ 10.0; colorName _ "black"; } ELSE IF version <= 8605.12 THEN { -- a complex name like "xerox/pressfonts/Helvetica-BIR" fontName _ GGParseIn.ReadBlankAndWord[f]; fontSize _ GGParseIn.ReadBlankAndReal[f]; colorName _ GGParseIn.ReadBlankAndWord[f]; fontName _ UnpackComplexFontName[fontName, fontSize]; } ELSE { -- a mixed mode name like Helvetica7BI or TimesRoman12 fontName _ GGParseIn.ReadBlankAndWord[f]; fontSize _ GGParseIn.ReadBlankAndReal[f]; colorName _ GGParseIn.ReadBlankAndWord[f]; }; GGParseIn.ReadBlank[f]; point _ GGParseIn.ReadPoint[f]; clus _ MakeTextCluster[rope, print, fontName, colorName, point]; }; BuildIPClusterClass: PROC [] RETURNS [class: ClusterClass] = { class _ NEW[ClusterClassObj _ [ type: $IP, boundBox: IPBoundBox, draw: IPDraw, drawTransform: IPDrawTransform, drawSelectionFeedback: IPDrawSelectionFeedback, transform: IPTransform, newParts: IPEndSelectProc, addParts: IPEndExtendProc, closestPoint: IPClosestPoint, closestPointAndTangent: NIL, closestSegment: IPClosestSegment, fileout: IPFileout, filein: IPFilein ]]; }; MakeIPCluster: PUBLIC PROC [fileName: Rope.ROPE, worldPt: Point] RETURNS [clus: Cluster] = { ipData: IPData; feedbackBox: BoundBox; feedbackBox _ GGBoundBox.CreateBoundBox[0,0,0,0]; -- gets real values later in this proc. ipData _ NEW[IPDataObj _ [fileName, worldPt, feedbackBox]]; clus _ NEW[ClusterObj _ [ class: FetchClusterClass[$IP], data: ipData, children: NIL, parent: NIL, selectedInFull: [FALSE, FALSE, FALSE, FALSE], boundBox: GGBoundBox.CreateBoundBox[0,0,0,0], -- gets set next line hitData: NEW[IPHitDataObj _ [] ] ]]; clus.class.boundBox[clus]; }; IPBoundBox: PROC [cluster: Cluster] = { ipData: IPData _ NARROW[cluster.data]; rect: ImagerTransformation.Rectangle; halfCP: REAL = GGModelTypes.halfJointSize + 1; rect.x _ 0.0; rect.y _ 0.0; rect.w _ 300.0; rect.h _ 300.0; GGBoundBox.UpdateBoundBox[cluster.boundBox, ipData.worldPt[1] + rect.x - halfCP, ipData.worldPt[2] + rect.y - halfCP, ipData.worldPt[1] + rect.w + halfCP, ipData.worldPt[2] + rect.h + halfCP]; GGBoundBox.UpdateBoundBox[ipData.feedbackBox, ipData.worldPt[1] + rect.x, ipData.worldPt[2] + rect.y, ipData.worldPt[1] + rect.w, ipData.worldPt[2] + rect.h]; }; IPDraw: PROC [cluster: Cluster, dc: Imager.Context, camera: CameraData] = { ipData: IPData _ NARROW[cluster.data]; }; IPDrawTransform: PROC [cluster: Cluster, parts: ClusterParts, dc: Imager.Context, camera: CameraData, transform: ImagerTransformation.Transformation] = { ipData: IPData _ NARROW[cluster.data]; tempPoint: Point _ GGTransform.Transform[transform, ipData.worldPt]; }; IPDrawSelectionFeedback: PROC [cluster: Cluster, parts: ClusterParts, dc: Imager.Context, camera: CameraData, quick: BOOL _ FALSE] = { ipData: IPData _ NARROW[cluster.data]; GGBoundBox.DrawBoundBox[dc, ipData.feedbackBox]; }; IPTransform: PROC [cluster: Cluster, parts: ClusterParts, transform: ImagerTransformation.Transformation] = { ipData: IPData _ NARROW[cluster.data]; ipData.worldPt _ GGTransform.Transform[transform, ipData.worldPt]; IPBoundBox[cluster]; }; IPEndSelectProc: GGModelTypes.ClusterNewPartsProc = { }; IPEndExtendProc: GGModelTypes.ClusterAddPartsProc = { }; IPClosestPoint: PROC [cluster: Cluster, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = { ipData: IPData _ NARROW[cluster.data]; [bestDist, ----, bestPoint, success] _ GGBoundBox.NearestPoint[ipData.feedbackBox, testPoint]; }; IPClosestSegment: PROC [cluster: Cluster, testPoint: Point, tolerance: REAL] RETURNS [bestDist: REAL, bestPoint: Point, success: BOOL] = { ipData: IPData _ NARROW[cluster.data]; ipHitData: IPHitData _ NARROW[cluster.hitData]; [bestDist, ipHitData.bestSeg, bestPoint, success] _ GGBoundBox.NearestSegment[ipData.feedbackBox, testPoint, tolerance]; }; IPFileout: PROC [cluster: Cluster, f: IO.STREAM] = { ipData: IPData _ NARROW[cluster.data]; f.PutF["\"%g\"", [rope[ipData.file]]]; GGParseOut.WritePoint[f, ipData.worldPt]; }; IPFilein: PROC [f: IO.STREAM, version: REAL] RETURNS [clus: Cluster] = { fileName: Rope.ROPE; point: Point; fileName _ f.GetRopeLiteral[]; GGParseIn.ReadBlank[f]; point _ GGParseIn.ReadPoint[f]; clus _ MakeIPCluster[fileName, point]; }; clusterClasses: LIST OF ClusterClassDef; Init: PRIVATE PROC [] = { textDef: ClusterClassDef _ NEW[ClusterClassDefObj _ [type: $Text, class: BuildTextClusterClass[]]]; ipDef: ClusterClassDef _ NEW[ClusterClassDefObj _ [type: $IP, class: BuildIPClusterClass[]]]; boxDef: ClusterClassDef _ NEW[ClusterClassDefObj _ [type: $Box, class: GGBoxCluster.BuildBoxClusterClass[]]]; clusterClasses _ LIST[boxDef, ipDef, textDef]; }; Init[]; END. ²GGClusterImpl.mesa Copyright c 1986 by Xerox Corporation. All rights reserved. Last edited by Pier on May 22, 1986 7:57:20 pm PDT Last edited by Bier on January 3, 1986 3:56:21 pm PST Contents: Implements various cluster classes in Gargoyle. lots of font info, duplicated in various forms Text cluster class procs SetTextFont now does an UpdateFontInfo of its own UpdateFontInfo[clus, fontStyle]; clus.class.boundBox[clus]; A fontstring is a font designation with family, size, and face, like Helvetica23BI or TimesRoman6. can't user IO.GetInt because a string like Helvetica9BI causes it to fail! Did a font substitution which we don't want. Retry with press fonts SetTextFont: PUBLIC PROC [clus: Cluster, fontStyle: DisplayStyle, fontString: Rope.ROPE] = { A fontstring is a font designation with family, size, and face, like Helvetica23BI or TimesRoman6 textData: TextData; IF clus.class.type#$Text OR fontString=NIL THEN RETURN; textData _ NARROW[clus.data]; This ONLY sets parameters in the textData. It does NOT make a new font. The new font will be made the next time the text cluster is displayed for any reason. NOTE: this could get us into bound box problems since the bound box will be obsolete for a short while. textData.validFontInfo _ FALSE; textData.fontStyle _ fontStyle; textData.fontString _ fontString; }; A fontstring is a font designation with family, size, and face, like Helvetica23BI or TimesRoman6 NOTE: The following call makes the style caching stuff superfluous but makes the refreshing work, so we do it until we either rip it out or live with it GGModelTypes.ClusterBoundBoxProc ASSERT: extent elements are all positive, so subtract left and descent, add right and ascent GGModelTypes.ClusterCopyProc IS THIS RIGHT: ?? GGModelTypes.ClusterDrawProc IF selected THEN GGBoundBox.DrawBoundBox[dc, textData.feedbackBox]; IF selected THEN DrawTextJoints[cluster, dc]; DrawTextJoints: PROC [cluster: Cluster, dc: Imager.Context] = { halfCP: REAL = GGModelTypes.halfJointSize + 1; bBox: BoundBox _ cluster.boundBox; GGShapes.DrawSelectedJoint[dc, [bBox.loX + halfCP, bBox.loY + halfCP]]; GGShapes.DrawSelectedJoint[dc, [bBox.loX + halfCP, bBox.hiY - halfCP]]; GGShapes.DrawSelectedJoint[dc, [bBox.hiX - halfCP, bBox.loY + halfCP]]; GGShapes.DrawSelectedJoint[dc, [bBox.hiX - halfCP, bBox.hiY - halfCP]]; }; GGModelTypes.ClusterDrawTransformProc Translates the origin. Rotation is not currently possible. GGModelTypes.ClusterDrawSelectionFeedbackProc DrawTextJoints[cluster, dc]; GGModelTypes.ClusterTransformProc Translates the origin. Rotation is not currently possible. GGModelTypes.ClusterClosestPointProc Used for hit testing. See comments in GGCluster.mesa [bestDist, ----, bestPoint] _ GGBoundBox.NearestPoint[textData.feedbackBox, testPoint]; success _ TRUE; GGModelTypes.ClusterClosestSegmentProc Used for hit testing. See comments in GGCluster.mesa GGModelTypes.ClusterFileoutProc Write a description of yourself onto stream f. GGModelTypes.ClusterFileinProc Read a description of yourself from stream f. Takes a complex name like "xerox/pressfonts/Helvetica-MIR" and a font size and returns a simple name like Helvetica10I Strings now surronded by quotes. Formerly surrounded by parens. We keep evoloving font names Interpress cluster class procs listBoxes: NIL, select: IPSelect, deselect: IPDeselect, GGModelTypes.ClusterBoundBoxProc GGModelTypes.ClusterDrawProc IF selected THEN GGBoundBox.DrawBoundBox[dc, ipData.feedbackBox]; GGModelTypes.ClusterDrawTransformProc IPSelect: PROC [cluster: Cluster] = { GGModelTypes.ClusterSelectProc }; IPDeselect: PROC [cluster: Cluster] = { GGModelTypes.ClusterDeselectProc }; GGModelTypes.ClusterDrawSelectionFeedbackProc GGModelTypes.ClusterTransformProc Store the transformation in the cluster. (For now, just allow translation) GGModelTypes.ClusterNewPartsProc GGModelTypes.ClusterAddPartsProc GGModelTypes.ClusterClosestPointProc Used for hit testing. See comments in GGCluster.mesa [bestDist, ----, bestPoint] _ GGBoundBox.NearestPoint[ipData.feedbackBox, testPoint]; success _ TRUE; GGModelTypes.ClusterClosestSegmentProc Used for hit testing. See comments in GGCluster.mesa GGModelTypes.ClusterFileoutProc Write a description of yourself onto stream f. GGModelTypes.ClusterFileinProc Read a description of yourself from stream f. defaultFont: ImagerFont.Font; greekFont: ImagerFont.Font; defaultFont _ ImagerFont.Scale[ImagerFont.Find["xerox/pressfonts/Helvetica-MRR"], 10.0]; defaultFont _ ImagerFont.Find["xerox/tiogafonts/Helvetica10"]; greekFont _ ImagerFont.Scale[ImagerFont.Find["xerox/tiogafonts/Hippo-MRR"], 10.0]; greekFont _ ImagerFont.Find["xerox/tiogafonts/Hippo10"]; Pier, January 28, 1986 4:17:08 pm PST changes to: TextBoundBox changed sign in equation to - leftExtent Κ“˜codešœ™Kšœ Οmœ1™[s1: ROPE, s2: ROPE, pos1: INT _ 0, case: BOOL _ TRUE]šžœ?žœžœ˜QK™CKšœ’˜’šžœ?žœž˜OKšžœ˜ —K˜—Kšœ;˜;Kšœ.˜.Kšœ!˜!Kšœ˜Kšœ˜Kšœ˜Kšœ ˜.Kšœžœ ˜3šž˜Kšœ9žœ˜?—K˜K˜—š‘ œžœžœ;žœ™\Kšœa™aKšœ™Kš žœžœ žœžœžœ™7šœ žœ ™Kš’™Kš’g™g—Kšœžœ™Kšœ™Kšœ!™!K™K™—š‘ œžœžœ;žœ˜\Kšœa™aKšœ˜Kš žœžœ žœžœžœ˜7Kšœ žœ ˜Kšœžœ˜Kšœ˜Kšœ!˜!Kš’˜™˜Kš’œ˜ K˜K˜—š ‘ œžœžœžœ*žœ˜cKšœ˜Kš žœžœžœžœžœ˜/Kšœ žœ ˜Kšžœ)˜/K˜K˜—š‘ œžœžœ6žœ˜XKšœ˜Kšžœžœžœ˜%Kšœ žœ ˜Kšœ˜Kšœ˜K˜K˜—š ‘ œžœžœžœ'žœ˜aKšœ˜Kš žœžœžœžœžœ˜/Kšœ žœ ˜Kšžœ%˜+K˜K˜—š‘ œžœ˜)K™ Kšœžœ˜*Kšœ˜Kšœžœ"˜.KšœC˜CK™\šœ+˜+Kšœ2˜2Kšœ/˜/Kšœ3˜3Kšœ/˜/—šœ/˜/Kšœ)˜)Kšœ&˜&Kšœ*˜*Kšœ&˜&—K˜K˜—š‘œžœžœ˜=Kšœ™Kšœžœ˜*Kšœu˜uKšžœžœžœ™Kšœ˜K˜!K˜K˜—š‘œžœ?˜MK™Kšœžœ˜*Kšžœžœžœ.˜PKšœ"˜"Kšœ=˜=Kšœ#˜#Kšžœ žœ3™CKšžœ žœ™-K˜K˜—š‘œžœ+™?Kšœžœ"™.Kšœ"™"KšœG™GKšœG™GKšœG™GKšœG™GK™K™—š‘œžœ„˜›K™%Kšœ;™;Kšœžœ˜*KšœF˜FKšžœžœžœ.˜PKšœ"˜"Kšœ2˜2Kšœ#˜#K˜K˜—š‘œžœXžœžœ˜ˆK™-Kšœžœ˜*Kšžœžœžœ.˜PKšœ2˜2Kšœ™K˜K˜—š‘ œžœ\˜oK™!Kšœ;™;Kšœžœ˜*KšœF˜FKšœ˜K˜K˜—š‘œžœ)žœžœ˜OKšžœžœ˜ K˜—K˜š‘ œžœ&žœ˜YKšžœžœ˜ K˜K˜—š‘ œžœ;žœ˜qKšžœžœ˜ K˜K˜—š‘œžœ;žœ˜tKšžœžœ˜ K˜K˜—š ‘œžœ1žœžœ žœžœ˜ŠK™$Kšœ5™5Kšœžœ˜*KšœW™WKšœ žœ™Kšœ  œQ˜`K˜K˜—š ‘œžœ1žœžœ žœžœ˜ŒKšœ&™&Kšœ5™5Kšœžœ˜*Kšœžœ˜3Kšœ|˜|K˜K˜—š‘ œžœžœžœ˜6K™K™.Kšœžœ˜*Kšœg˜gK˜,K˜+K˜K˜—š ‘ œžœžœžœ žœžœ˜JK™K™-š ‘œžœžœ žœžœ žœ˜^Kšœ Dœ,™vKšœ žœ˜Kšœ( !˜IKšœ  %˜EK–)[s: ROPE, pos: INT _ 0, skip: ROPE]šœU ˜išœ>žœžœž˜NKšœžœ˜%Kšœžœ ˜&Kšœžœ ˜&Kšœžœ ˜'Kšžœž˜K˜—K˜—Kšœ žœ˜%Kšœ žœ˜K˜ K™?šžœžœ˜Kšœ˜K˜—šžœ˜Kšœ#˜#K˜%K˜#K˜—K™šžœžœ ˜3Kšœ˜K˜K˜K˜—šžœžœžœ ,˜NK˜)Kšœ'˜'K˜K˜K˜—šžœžœžœ 7˜YK˜)K˜)K˜*K˜5K˜—šžœ 6˜=K˜)K˜)K˜*Kšœ˜—K˜K˜K˜Kšœ@˜@K˜—K˜K™š‘œžœžœ˜>šœžœ˜K˜ Kšœ˜Kšœ žœ™Kšœ ˜ K˜K™K™K˜/Kšœ˜K˜K˜Kšœ˜Kšœžœ˜Kšœ!˜!K˜K˜K˜—K˜K˜—š ‘ œžœžœžœžœ˜\Kšœ˜Kšœ˜Kšœ2 '˜YKšœ žœ/˜;šœžœ˜K˜Kšœ ˜ Kšœ žœ˜Kšœžœ˜ Kš œžœžœžœžœ˜-Kšœ. ˜CKšœ žœ˜ Kšœ˜—Kšœ˜K˜K˜—š‘ œžœ˜'K™ Kšœžœ˜&K˜%Kšœžœ"˜.K˜K˜šœ+˜+Kšœ$˜$Kšœ$˜$Kšœ$˜$Kšœ%˜%—šœ-˜-Kšœ˜Kšœ˜Kšœ˜Kšœ˜—K˜K˜—š‘œžœ?˜KK™Kšœžœ˜&Kšžœ žœ1™AK˜K˜—š‘œžœ„˜™K™%Kšœžœ˜&KšœD˜DK˜K˜—š‘œžœ™%K™K™K™—š‘ œžœ™'K™ K™K˜—š‘œžœXžœžœ˜†K™-Kšœžœ˜&Kšœ0˜0K˜K˜—š‘ œžœ\˜mK™!J™KKšœžœ˜&KšœB˜BKšœ˜K˜K˜—š‘œ’œ˜5Kšœ ™ K˜K˜—š‘œ’œ˜5Kšœ ™ K˜K˜—š ‘œžœ1žœžœ žœžœ˜ˆK™$Kšœ5™5Kšœžœ˜&KšœU™UKšœ žœ™Kšœ  œO˜^K˜K˜—š ‘œžœ1žœžœ žœžœ˜‹Kšœ&™&Kšœ5™5Kšœžœ˜&Kšœžœ˜/Kšœx˜xK˜K˜—š‘ œžœžœžœ˜4K™K™.Kšœžœ˜&Kšœ&˜&Kšœ)˜)K˜K˜—š ‘œžœžœžœ žœžœ˜HK™K™-Kšœžœ˜K˜ Kšœ˜K˜K˜Kšœ&˜&K˜K˜Kšœ™Kšœ™—K˜Kšœžœžœ˜(K˜š‘œžœžœ˜KšœžœE˜cKšœžœA˜]KšœžœP˜mKšœžœ˜.KšœX™XKšœ>™>KšœR™RKšœ8™8K˜K˜K˜—K˜K˜K˜Kšžœ˜™%Kšœ Οr œ)™A—K™—…—F@n