(* Copyright (C) 1992, Digital Equipment Corporation *) (* All rights reserved. *) (* See the file COPYRIGHT for a full description. *) (* *) (* by Steve Glassman and Stephen Harrison *) (* Last modified on Thu Jan 14 14:16:03 PST 1993 by steveg *) (* modified on Tue Jul 21 17:17:36 PDT 1992 by harrison *) (* modified on Fri Jul 10 18:55:54 1992 by mhb *) INTERFACE MG; <* PRAGMA LL *> (* All R2.T coordinates are in Cartesian (XY) space with x increasing to the right and y increasing upward *) IMPORT Font, Matrix2D, MGV, PaintOp, Point, R2, Rect, Region, VBT, RealPath, R2Box; TYPE V = MGV.V; TYPE T = MGV.MGT; REVEAL T <: TPublic; TYPE TPublic = OBJECT (* all fields READONLY except at initialization or through methods *) id := NoID; (* The ID provided by the algorithm that is associated with this element. Output events from the algorithm are in terms of an id which is mapped by the v.lookup method to the element. Input events to the algorithm are in terms of the id associated with the element *) m := Matrix2D.Identity; (* The position of self is m * R2.Origin which is the tx ty of a matrix translation. *) highlight := 0.0; (* 0.0 -> normal, 1.0 -> highlit *) visible := 1.0; (* 0.0 -> invisible, 1.0 -> visible *) color : PaintOp.ColorScheme; (* !!! := PaintOp.bgFg*) weight := 1.0; label := ""; font := DefaultFont; alignment := Alignment.Center; appearance: Appearance; dirty := FALSE; (* internal bookkeeping field *) METHODS <* LL < v.mu *> init (v: V := NIL; parent: Group := NIL): T; (* If self.appearance is NIL then appearance gets AppearanceDefault (paints the object "as is") init also adds self to the group "parent" or, if "parent" is NIL and "v" is not NIL, to "v"'s display list. init also registers self with the lookup table for "v" if v is not NIL and self.id is not NoID. *) <* LL = v.mu *> setColor (v: V; color: PaintOp.ColorScheme (* := PaintOp.bgFg *)); setFont (v: V; font := DefaultFont); setHighlight (v: V; highlight := 0.0); setVisible (v: V; visible := 1.0); setWeight (v: V; weight := 1.0); setAlignment (v: V; alignment := Alignment.Center); setLabel (v: V; label := ""); setAppearance (v: V; appearance := NIL); (* appearance = NIL => AppearanceDefault; appearance.prevAppearance gets set unless appearance = NIL *) transform (v: V; READONLY m: Matrix2D.T); (* apply the 2D transformation to self. The default transform method transforms self.m, subtypes with other values must transform the other values themselves or either when the transform method is called or when the values are read. *) render (v: V; VAR (* IN/OUT *) clip: Rect.T); (* renders self to the screen. repaintRect should be updated to include any dirty elements painted (normally only changed when painting a group or an appearance. Do not call directly, call self.appearance.paint (which uses render) *) bounds (v: V): R2Box.T; (* returns the bounding rectangle of the element (possibly ignoring the label). Do not call directly, call self.appearance.boundingBox (which uses boundingBox) *) rgn (v: V): Region.T; (* returns the (conservative) region the element occupies in v in screen pixels (mostly for use by appearance). Do not call directly, call self.appearance.region (which calls region) *) END; TYPE ID = INTEGER; CONST NoID: INTEGER = -1; DefaultFont = Font.T{fnt := 923199216}; TYPE Alignment = {N, NW, W, SW, S, SE, E, NE, Center}; (* Label is located outside the bounding box in the direction specified by the alignment *) TYPE AppearanceDefault <: Appearance; VAR appearanceDefault: AppearanceDefault; (* paints the object "as is" (i.e. no effects) *) TYPE (* An appearance handles highlight effects (drop shadows, underlays, marquees, blinking, partial color changes) and other special effects. The intention is for an appearance object to be independent of the object it affects, so that an apperance can be applied to a group and will affect all of the elements in the group. *) Appearance <: AppearancePublic; AppearancePublic = OBJECT <* LL = v.mu *> prevAppearance: Appearance := NIL; (* the previous appearance of the object for composing appearances *) METHODS <* LL = v.mu *> paint (t: T; v: V; VAR clip: Rect.T); boundingBox (t: T; v: V): R2Box.T; (* returns the bounding rectagle of the element (possibly ignoring the label) *) region (t: T; v: V): Region.T; (* returns the (conservative) region the element occupies in v in screen pixels (mostly for internal use) *) END; (* ------------------------------------------------------------------ See the Appearances interface for some predefined appearances ------------------------------------------------------------------ *) TYPE GroupIterator = OBJECT <* LL = v.mu *> v : V; recursively: BOOLEAN; fromBack : BOOLEAN; METHODS <* LL = v.mu *> proc (t: T): (* more *) BOOLEAN END; TYPE Group = MGV.MGGroup; REVEAL Group <: GroupPublic; TYPE GroupPublic = T OBJECT <* LL = v.mu *> elems: REF ARRAY OF T; (* elements are stored in visual back to front order. elems # NIL *) cntElems: INTEGER := 0; (* cnt of non-nil elements *) METHODS init (elemsSize := 4; v : V := NIL; parentGroup: Group := NIL ): Group; (* initializes elems to an array of "elemsSize" elements (all NIL), *) <* LL = v.mu *> iterate (gi: GroupIterator; recursively := FALSE; fromBack := TRUE): BOOLEAN; (* iterate over all the elements of the group calling gi.proc until it returns FALSE. Result is false if any proc returned FALSE else TRUE *) addAfter (v: V; t: T; prev: T := NIL); (* prev = NIL -> add as first element "t" gets painted on top of (after) "prev" first element in the group is on the bottom *) addBefore (v: V; t: T; next: T := NIL); (* next = NIL -> add as last element "t" gets painted underneath (before) "next" last element of the group is on top *) top (v: V; t: T); (* make "t" the last element in the group *) bottom (v: V; t: T); (* make "t" the first element in the group *) remove (v: V; t: T); (* remove "t" from the group *) END; TYPE Label <: LabelPublic; LabelPublic = T OBJECT (* must call init method *) END; TYPE Line <: LinePublic; LinePublic = T OBJECT style := VBT.EndStyle.Round; to : R2.T; (* Pos(self) is "from" point of the line self.m * self.to is "to" point. Call "MGPublic.LineTo" or "MG.LineToLocked" to get the "to" position of the line. *) METHODS init (READONLY from, to: R2.T; v: V := NIL; group: Group := NIL): Line; (* LL = v.mu *) setStyle (v: V; style := VBT.EndStyle.Round); reshape (v: V; READONLY from, to: R2.T); END; TYPE LineEnd <: LineEndPublic; LineEndPublic = T OBJECT line: Line := NIL; controlsFrom := TRUE; METHODS (* must call init method *) END; (* Identical behaviour to line except that the transform method only affects the from or to endpoint (depending on controlsFrom). Only the "from" end of a line repaints it. This is useful for creating a group affecting only one endpoint of the line. (Uses standard init method) *) TYPE Rectangle <: RectanglePublic; RectanglePublic = T OBJECT ne: R2.T; (* Pos(self) is southwest corner of the rectangle. self.m * self.ne is northeast corner. *) METHODS init (READONLY corner1, corner2: R2.T; v : V := NIL; group : Group := NIL ): Rectangle; (* LL = v.mu *) reshape (v: V; READONLY corner1, corner2: R2.T); END; (* The rectangle is painted in the background color of the paint scheme. The weight is interpreted as the thickness of an inset border around the rectangle. The color of the border is the foreground color of the color scheme *) TYPE Ellipse <: EllipsePublic; EllipsePublic = T OBJECT ne: R2.T; (* Pos(self) is southwest corner of the ellipse. self.m * self.ne is northeast corner. *) METHODS init (READONLY corner1, corner2: R2.T; v : V := NIL; group : Group := NIL ): Ellipse; (* LL = v.mu *) reshape (v: V; READONLY corner1, corner2: R2.T); END; (* The ellipse is painted in the background color of the color scheme. The weight is interpreted as the thickness of an inset border around the ellipse. The color of the border is the foreground color of the color scheme. *) TYPE Shape <: ShapePublic; ShapePublic = T OBJECT METHODS init (READONLY origin: R2.T; READONLY path: RealPath.T; READONLY fill := TRUE; v : V := NIL; group : Group := NIL ): Shape; (* LL = v.mu *) reshape (v: V; READONLY origin: R2.T; READONLY path: RealPath.T; READONLY fill := TRUE); END; (* A filled shape uses odd winding to determine the inside/outside of the shape *) <* LL = v.mu for following procedures *> <* INLINE *> PROCEDURE PosLocked (t: T; v: V): R2.T; <* INLINE *> PROCEDURE SetPosLocked (t: T; READONLY pos: R2.T; v: V); <* INLINE *> PROCEDURE LineFromLocked (line: Line; v: V): R2.T; <* INLINE *> PROCEDURE LineToLocked (line: Line; v: V): R2.T; <* INLINE *> PROCEDURE RectangleSWLocked (rectangle: Rectangle; v: V): R2.T; <* INLINE *> PROCEDURE RectangleNELocked (rectangle: Rectangle; v: V): R2.T; <* INLINE *> PROCEDURE EllipseSWLocked (ellipse: Ellipse; v: V): R2.T; <* INLINE *> PROCEDURE EllipseNELocked (ellipse: Ellipse; v: V): R2.T; <* INLINE *> PROCEDURE ShapeOriginLocked (shape: Shape; v: V): R2.T; <* INLINE *> PROCEDURE ShapePathLocked (shape: Shape; v: V): RealPath.T; <* INLINE *> PROCEDURE BoundingBoxLocked (t: T; v: V): R2Box.T; PROCEDURE RTranslateLocked (t: T; v: V; READONLY by: R2.T); PROCEDURE TranslateToLocked ( t : T; v : V; READONLY dest : R2.T; center := FALSE); PROCEDURE ScaleLocked ( t : T; v : V; READONLY factor := R2.Ones; READONLY wrt := R2.Origin); PROCEDURE RotateLocked ( t : T; v : V; angle : REAL; READONLY origin := R2.Origin); (* Rotate by "angle" degrees counter clockwise around "origin" *) PROCEDURE TransformLocked (t: T; v: V; READONLY m: Matrix2D.T); (* Conversion procedures *) <* INLINE *> PROCEDURE ScreenPointToMGC (v: V; READONLY pt: Point.T): R2.T; <* INLINE *> PROCEDURE MGCToScreenPoint (v: V; READONLY p: R2.T): Point.T; <* INLINE *> PROCEDURE MGCToScreenRect (v: V; READONLY corner1, corner2: R2.T): Rect.T; (* MGCToScreenRect will produce Rect.Empty if the screen points of corner1 and corner2 coincide. Use: Rect.Join(Rect.FromPoint(MGCToScreenPoint(corner1)), Rect.FromPoint(MGCScreenPoint(corner2))) if you need a non-empty rectangle *) PROCEDURE ResetLookupsLocked(v: V); (* Clear the lookup table for "v". *) END MG.