/** * Copyright (C) 1998-1999 by University of Maryland, College Park, MD 20742, USA * All rights reserved. */ package edu.umd.cs.piccolox.swt; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.geom.*; import java.util.*; import org.eclipse.swt.graphics.FontMetrics; import org.eclipse.swt.graphics.GC; import org.eclipse.swt.graphics.Point; import org.eclipse.swt.widgets.Display; import edu.umd.cs.piccolo.PNode; import edu.umd.cs.piccolo.util.PPaintContext; /** * <b>PSWTText</b> creates a visual component to support text. Multiple lines can * be entered, and basic editing is supported. A caret is drawn, * and can be repositioned with mouse clicks. The text object is positioned * so that its upper-left corner is at the origin, though this can be changed * with the translate methods. * <P> * <b>Warning:</b> Serialized and ZSerialized objects of this class will not be * compatible with future Jazz releases. The current serialization support is * appropriate for short term storage or RMI between applications running the * same version of Jazz. A future release of Jazz will provide support for long * term persistence. */ public class PSWTText extends PNode { /** * Below this magnification render text as 'greek'. */ static protected final double DEFAULT_GREEK_THRESHOLD = 5.5; /** * Default color of text rendered as 'greek'. */ static protected final Color DEFAULT_GREEK_COLOR = Color.gray; /** * Default font name of text. */ static protected final String DEFAULT_FONT_NAME = "Helvetica"; /** * Default font style for text. */ static protected final int DEFAULT_FONT_STYLE = Font.PLAIN; /** * Default font size for text. */ static protected final int DEFAULT_FONT_SIZE = 12; /** * Default font for text. */ static protected final Font DEFAULT_FONT = new Font(DEFAULT_FONT_NAME, DEFAULT_FONT_STYLE, DEFAULT_FONT_SIZE); /** * Default color for text. */ static protected final Color DEFAULT_PEN_COLOR = Color.black; /** * Default text when new text area is created. */ static protected final String DEFAULT_TEXT = ""; /** * Default padding */ static protected final int DEFAULT_PADDING = 2; /** * Below this magnification text is rendered as greek. */ protected double greekThreshold = DEFAULT_GREEK_THRESHOLD; /** * Color for greek text. */ protected Color greekColor = DEFAULT_GREEK_COLOR; /** * Current pen color. */ protected Color penColor = DEFAULT_PEN_COLOR; /** * Current text font. */ protected Font font = DEFAULT_FONT; /** * The amount of padding on each side of the text */ protected int padding = DEFAULT_PADDING; /** * Each vector element is one line of text. */ protected ArrayList lines = new ArrayList(); /** * Translation offset X. */ protected double translateX = 0.0; /** * Translation offset Y. */ protected double translateY = 0.0; /** * Default constructor for PSWTTest. */ public PSWTText() { this("", DEFAULT_FONT); } /** * PSWTTest constructor with initial text. * @param str The initial text. */ public PSWTText(String str) { this(str, DEFAULT_FONT); } /** * PSWTTest constructor with initial text and font. * @param str The initial text. * @param font The font for this PSWTText component. */ public PSWTText(String str, Font font) { setText(str); this.font = font; recomputeBounds(); } //**************************************************************************** // // Get/Set and pairs // //*************************************************************************** /** * Returns the current pen color. */ public Color getPenColor() {return penColor;} /** * Sets the current pen color. * @param color use this color. */ public void setPenColor(Color color) { penColor = color; repaint(); } /** * Returns the current pen paint. */ public Paint getPenPaint() { return penColor; } /** * Sets the current pen paint. * @param aPaint use this paint. */ public void setPenPaint(Paint aPaint) { penColor = (Color)aPaint; } /** * Returns the current background color. */ public Color getBackgroundColor() { return (Color)getPaint(); } /** * Sets the current background color. * @param color use this color. */ public void setBackgroundColor(Color color) { super.setPaint(color); } /** * Returns the current greek threshold. Below this magnification * text is rendered as 'greek'. */ public double getGreekThreshold() {return greekThreshold;} /** * Sets the current greek threshold. Below this magnification * text is rendered as 'greek'. * @param threshold compared to renderContext magnification. */ public void setGreekThreshold(double threshold) { greekThreshold = threshold; repaint(); } /** * Returns the current font. */ public Font getFont() {return font;} /** * Return the text within this text component. * Multline text is returned as a single string * where each line is separated by a newline character. * Single line text does not have any newline characters. */ public String getText() { String line; String result = new String(); int lineNum = 0; for (Iterator i = lines.iterator() ; i.hasNext() ; ) { if (lineNum > 0) { result += '\n'; } line = (String)i.next(); result += line; lineNum++; } return result; } /** * Sets the font for the text. * <p> * <b>Warning:</b> Java has a serious bug in that it does not support very small * fonts. In particular, fonts that are less than about a pixel high just don't work. * Since in Jazz, it is common to create objects of arbitrary sizes, and then scale them, * an application can easily create a text object with a very small font by accident. * The workaround for this bug is to create a larger font for the text object, and * then scale the node down correspondingly. * @param aFont use this font. */ public void setFont(Font aFont) { font = aFont; recomputeBounds(); } /** * Sets the text of this visual component to str. Multiple lines * of text are separated by a newline character. * @param str use this string. */ public void setText(String str) { int pos = 0; int index; boolean done = false; lines = new ArrayList(); do { index = str.indexOf('\n', pos); if (index == -1) { lines.add(str); done = true; } else { lines.add(str.substring(0, index)); str = str.substring(index + 1); } } while (!done); recomputeBounds(); } /** * Set text translation offset X. * @param x the X translation. */ public void setTranslateX(double x) { setTranslation(x, translateY); } /** * Get the X offset translation. * @return the X translation. */ public double getTranslateX() { return translateX; } /** * Set text translation offset Y. * @param y the Y translation. */ public void setTranslateY(double y) { setTranslation(translateX, y); } /** * Get the Y offset translation. * @return the Y translation. */ public double getTranslateY() { return translateY; } /** * Set the text translation offset to the specified position. * @param x the X-coord of translation * @param y the Y-coord of translation */ public void setTranslation(double x, double y) { translateX = x; translateY = y; recomputeBounds(); } /** * Set the text translation offset to point p. * @param p The translation offset. */ public void setTranslation(Point2D p) { setTranslation(p.getX(), p.getY()); } /** * Get the text translation offset. * @return The translation offset. */ public Point2D getTranslation() { Point2D p = new Point2D.Double(translateX, translateY); return p; } /** * Renders the text object. * <p> * The transform, clip, and composite will be set appropriately when this object * is rendered. It is up to this object to restore the transform, clip, and composite of * the Graphics2D if this node changes any of them. However, the color, font, and stroke are * unspecified by Jazz. This object should set those things if they are used, but * they do not need to be restored. * * @param ppc Contains information about current render. */ public void paint(PPaintContext ppc) { Graphics2D g2 = ppc.getGraphics(); AffineTransform at = null; boolean translated = false; if (!lines.isEmpty()) { if ((translateX != 0.0) || (translateY != 0.0)) { at = g2.getTransform(); // save transform g2.translate(translateX, translateY); translated = true; } // If font too small and not antialiased, then greek double renderedFontSize = font.getSize() * ppc.getScale(); // BBB: HACK ALERT - July 30, 1999 // This is a workaround for a bug in Sun JDK 1.2.2 where // fonts that are rendered at very small magnifications show up big! // So, we render as greek if requested (that's normal) // OR if the font is very small (that's the workaround) if ((renderedFontSize < 0.5) || (renderedFontSize < greekThreshold)) { paintAsGreek(ppc); } else { paintAsText(ppc); } if (translated) { g2.setTransform(at); // restore transform } } } /** * Paints this object as greek. * @param ppc The graphics context to paint into. */ public void paintAsGreek(PPaintContext ppc) { Graphics2D g2 = ppc.getGraphics(); if (greekColor != null) { g2.setBackground(greekColor); ((SWTGraphics2D)g2).fillRect(0,0,getWidth(),getHeight()); } } /** * Paints this object normally (show it's text). * Note that the entire text gets rendered so that it's upper * left corner appears at the origin of this local object. * @param ppc The graphics context to paint into. */ public void paintAsText(PPaintContext ppc) { SWTGraphics2D sg2 = (SWTGraphics2D)ppc.getGraphics(); if (getPaint() != null) { sg2.setBackground((Color)getPaint()); Rectangle2D rect = new Rectangle2D.Double(0.0, 0.0, getWidth(), getHeight()); sg2.fillRect(rect.getX(),rect.getY(),rect.getWidth(),rect.getHeight()); } sg2.translate(padding,padding); double scale = Math.min(sg2.getTransform().getScaleX(),sg2.getTransform().getScaleY()); double dSize = scale*font.getSize(); double fixupScale = Math.floor(dSize)/dSize; // This moves the text size down to the next closest integer size - to help it stay in // it's alloted bounds. This is because SWT only supports integer font metrics sg2.scale(fixupScale,fixupScale); // Render each line of text // Note that the entire text gets rendered so that it's upper left corner // appears at the origin of this local object. sg2.setColor(penColor); sg2.setFont(font); int lineNum = 0; String line; double y; FontMetrics metrics = sg2.getSWTFontMetrics(); for (Iterator i = lines.iterator() ; i.hasNext() ; ) { line = (String)i.next(); // ADDED BY LEG ON 2/25/03 - BUG CAUSING PROBLEMS AT CERTAIN // SCALES WHEN LINE WAS EMPTY line = (line.equals("")) ? " " : line; y = (lineNum * metrics.getHeight()); sg2.drawString(line, (double)0, (double)y); lineNum++; } sg2.scale(1/fixupScale,1/fixupScale); sg2.translate(-padding,-padding); } /** * Notifies this object that it has changed and that it * should update its notion of its bounding box. */ protected void recomputeBounds() { Point bds; double lineWidth; double maxWidth = 0.0; double height; height = 0.0; boolean hasText = true; if ((lines.size() == 1) && (((String)lines.get(0)).equals(""))) { hasText = false; } GC gc = new GC(Display.getDefault()); SWTGraphics2D g2 = new SWTGraphics2D(gc,Display.getDefault()); g2.setFont(font); FontMetrics fm = g2.getSWTFontMetrics(); if (!lines.isEmpty() && hasText) { String line; int lineNum = 0; for (Iterator i = lines.iterator() ; i.hasNext() ; ) { line = (String)i.next(); // Find the longest line in the text bds = gc.stringExtent(line); lineWidth = bds.x; if (lineWidth > maxWidth) { maxWidth = lineWidth; } // Find the heighest line in the text if (lineNum == 0) { height += fm.getAscent()+fm.getDescent()+fm.getLeading(); } else { height += fm.getHeight(); } lineNum++; } } else { // If no text, then we want to have the bounds of a space character, // so get those bounds here bds = gc.stringExtent(" "); maxWidth = bds.x; height = bds.y; } gc.dispose(); // Finally, set the bounds of this text setBounds(translateX,translateY,maxWidth+2*DEFAULT_PADDING,height+2*DEFAULT_PADDING); } protected void internalUpdateBounds(double x, double y, double width, double height) { recomputeBounds(); } }