// Copyright (c) 1996-98 The Regents of the University of California. All // Rights Reserved. Permission to use, copy, modify, and distribute this // software and its documentation for educational, research and non-profit // purposes, without fee, and without a written agreement is hereby granted, // provided that the above copyright notice and this paragraph appear in all // copies. Permission to incorporate this software into commercial products may // be obtained by contacting the University of California. David F. Redmiles // Department of Information and Computer Science (ICS) University of // California Irvine, California 92697-3425 Phone: 714-824-3823. This software // program and documentation are copyrighted by The Regents of the University // of California. The software program and documentation are supplied "as is", // without any accompanying services from The Regents. The Regents do not // warrant that the operation of the program will be uninterrupted or // error-free. The end-user understands that the program was developed for // research purposes and is advised not to rely exclusively on the program for // any reason. IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY // PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES, // INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS // DOCUMENTATION, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF THE // POSSIBILITY OF SUCH DAMAGE. THE UNIVERSITY OF CALIFORNIA SPECIFICALLY // DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE // SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS" BASIS, AND THE UNIVERSITY OF // CALIFORNIA HAS NO OBLIGATIONS TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, // ENHANCEMENTS, OR MODIFICATIONS. // File: FigText.java // Classes: FigText // Original Author: ics125b spring 1996 // $Id: FigText.java,v 1.21 1997/06/10 23:42:47 jrobbins Exp $ package uci.graphedit; import java.awt.*; import java.util.*; import java.io.*; import uci.ui.*; import uci.util.*; /** This class handles drawing and editing text Fig's in a * LayerDiagram. Needs-More-Work: should eventually allow styled text * editing, ... someday... * * FEATURE: basic_shapes_text */ public class FigText extends Fig { //////////////////////////////////////////////////////////////// // constants /** Constants to specify text justification. Needs-More-Work: some * parts of the code refer to this as "alignment", and that is * confusing. */ public final int JUSTIFY_LEFT = 0; public final int JUSTIFY_RIGHT = 1; public final int JUSTIFY_CENTER = 2; /** Minimum size of a FigText object. */ public final int MIN_TEXT_WIDTH = 30; /** Number of handles that FigText provides, currently none. */ public static final int numHandles = 0; //////////////////////////////////////////////////////////////// // instance variables /** Font info. */ private transient Font _font; private transient FontMetrics _fm; private int _lineHeight; /** Color of the actual text characters. */ protected Color _textColor = Color.black; /** Color to be drawn behind the actual text characters. Note that * this will be a smaller area than the bounding box which is * filled with FillColor. */ protected Color _textFillColor = Color.white; /** True if the area behind individual characters is to be filled * with TextColor. */ protected boolean _textFilled = false; protected String _fontName = "TimesRoman"; protected int _fontSize = 12; protected boolean _bold = false; protected boolean _italic = false; protected boolean _underline = false; protected boolean _strike = false; protected boolean _textShadow = false; protected Color _textShadowColor = Color.gray; protected int _lineSpacing = -4; protected int _topMargin = 1; protected int _botMargin = 1; protected int _leftMargin = 1; protected int _rightMargin = 1; /** True if the FigText can only grow in size, never shrink. */ protected boolean _expandOnly = false; /** Text alignment can be JUSTIFY_LEFT, JUSTIFY_RIGHT, or JUSTIFY_CENTER. */ private int _alignment; /** The current string to display. */ private String _curText; //////////////////////////////////////////////////////////////// // constructors /** Construct a new FigText with the given position, size, color, * string, and font. */ public FigText(int x, int y, int t_width, int t_height, Color t_color, String t_font, int fontSize) { super(x, y, t_width, t_height, Color.white, null, numHandles); position(x, y); _textColor = t_color; objectWidth = t_width; objectHeight = t_height; _font = new Font(t_font, Font.PLAIN, fontSize); _alignment = JUSTIFY_CENTER; _curText = ""; } /** Construct a new FigText with the given position, size, and attributes. */ public FigText(int x, int y, int t_width, int t_height, Hashtable gAttrs ) { this(x, y, t_width, t_height, Color.blue, "TimesRoman", 10); put(gAttrs); } //////////////////////////////////////////////////////////////// // accessors /** Reply a string that indicates how the text is justified: Left, * Center, or Right. */ public String alignmentByName() { if (_alignment == JUSTIFY_LEFT) return "Left"; else if (_alignment == JUSTIFY_CENTER) return "Center"; else if (_alignment == JUSTIFY_RIGHT) return "Right"; System.out.println("internal error, unknown text alignment"); return "Unknown"; } /** Set the text justification given one of these strings: Left, * Center, or Right. */ public void alignmentByName(String alignString) { if (alignString.equals("Left")) _alignment = JUSTIFY_LEFT; else if (alignString.equals("Center")) _alignment = JUSTIFY_CENTER; else if (alignString.equals("Right")) _alignment = JUSTIFY_RIGHT; _fm = null; } /** Reply the name of the current font for this FigText. */ public String fontName() { if (_font == null) return "TimesRoman"; else return _font.getName(); } // /** Set the font for this FigText. */ // public void fontName(String fn) { // // _font = new Font(fn, fontStyle(), fontSize().intValue()); // // _fm = null; // // measure(); // } // /** reply the style of the text. See java.awt.Font for possible values. */ // public int fontStyle() { // // if (_font == null) return Font.PLAIN; // // else // return _font.getStyle(); // } // /** set the style of the text. See java.awt.Font for possible values. */ // public void fontStyle(int style) { // // _font = new Font(fontName(), style, fontSize().intValue()); // // _fm = null; // // measure(); // } // /** Reply the font size of this FigText */ // public Integer fontSize() { // // if (_font == null) return new Integer(10); // // else // return new Integer(_font.getSize()); // } // /** Set the font size for this FigText */ // public void fontSize(Integer fs) { // // int size = fs.intValue(); // // if (size < 6 || size > 255) return; // keep font size reasonable // // _font = new Font(fontName(), Font.PLAIN, size); // // _fm = null; // // measure(); // } protected static Vector _PossibleFontNames = null; /** reply a String Vector of available font names. Used for the menu * in the PropertySheet when a FigText is selected. */ public static Vector possibleFontNames() { if (_PossibleFontNames == null) { _PossibleFontNames = new Vector(4); _PossibleFontNames.addElement("TimesRoman"); _PossibleFontNames.addElement("Helvetica"); _PossibleFontNames.addElement("Dialog"); _PossibleFontNames.addElement("Courier"); } return _PossibleFontNames; } // protected static Vector _possibleFontStyles= null; // /** reply a String Vector of available text styles. Used for the menu // * in the PropertySheet when a FigText is selected. */ // public static Vector possibleFontStyles() { // // if (_possibleFontStyles == null) { // // _possibleFontStyles = new Vector(4); // // _possibleFontStyles.addElement("Plain"); // // _possibleFontStyles.addElement("Bold"); // // _possibleFontStyles.addElement("Italic"); // // _possibleFontStyles.addElement("BoldItalic"); // // } // return _possibleFontStyles; // } protected static Vector _possibleAlignments = null; /** Reply a String Vector of available text alignments. Used for the menu * in the PropertySheet when a FigText is selected. */ public static Vector possibleAlignments() { if (_possibleAlignments == null) { _possibleAlignments = new Vector(3); _possibleAlignments.addElement("Left"); _possibleAlignments.addElement("Center"); _possibleAlignments.addElement("Right"); } return _possibleAlignments; } static { uci.ui.PropSheetCategory.addEnumProp(pALIGNMENT, possibleAlignments()); uci.ui.PropSheetCategory.addEnumProp(pFONT_NAME, possibleFontNames()); uci.ui.PropSheetCategory.addBigTextProp(pTEXT, 6); } /** Set the color of the text characters. */ //public void setTextColor(Color c) { _textColor = c; } /** Set the color to be drawn behind individual characters. */ //public void setTextFillColor(Color c) { _textFillColor = c; } /** Set whether any color will be drawn behind the individual characters. */ //public void setTextFilled(boolean f) { _textFilled = f; } //////////////////////////////////////////////////////////////// // accessors and modifiers public Color getTextColor() { return _textColor; } public void setTextColor(Color c) { _textColor = c; } public Color getTextFillColor() { return _textFillColor; } public void setTextFillColor(Color c) { _textFillColor = c; } public boolean getTextFilled() { return _textFilled; } public void setTextFilled(boolean b) { _textFilled = b; } public boolean getBold() { return _bold; } public void setBold(boolean b) { _bold = b; buildFont(); } public boolean getItalic() { return _italic; } public void setItalic(boolean b) { _italic = b; buildFont(); } public boolean getStrike() { return _strike; } public void setStrike(boolean b) { _strike = b; buildFont(); } public boolean getUnderline() { return _underline; } public void setUnderline(boolean b) { _underline = b; buildFont(); } public boolean getTextShadow() { return _textShadow; } public void setTextShadow(boolean b) { _textShadow = b; } public Color getTextShadowColor() { return _textShadowColor; } public void setTextShadowColor(Color c) { _textShadowColor = c; } public String getAlignment() { return alignmentByName(); } public void setAlignment(String align) { alignmentByName(align); } public int getLineSpacing() { return _lineSpacing; } public void setLineSpacing(int s) { _lineSpacing = s; measure(); } public int getTopMargin() { return _topMargin; } public void setTopMargin(int m) { _topMargin = m; measure(); } public int getBotMargin() { return _botMargin; } public void setBotMargin(int m) { _botMargin = m; measure(); } public int getLeftMargin() { return _leftMargin; } public void setLeftMargin(int m) { _leftMargin = m; measure(); } public int getRightMargin() { return _rightMargin; } public void setRightMargin(int m) { _rightMargin = m; measure(); } public boolean getExpandOnly() { return _expandOnly; } public void setExpandOnly(boolean b) { _expandOnly = b; } public String getFontName() { return _fontName; } public void setFontName(String n) { _fontName = n; buildFont(); } public int getFontSize() { return _fontSize; } public void setFontSize(int s) { _fontSize = s; buildFont(); } /** Compute an AWT font style integer from various booleans in FigText. */ public int computeStyle() { return (_bold ? Font.BOLD : 0) + (_italic ? Font.ITALIC : 0); } public void buildFont() { _font = new Font(getFontName(), computeStyle(), getFontSize()); _fm = null; measure(); } public Object get(String key) { if (key.equals(pTEXT)) return getText(); else if (key.equals(pTEXT_COLOR)) return getTextColor(); else if (key.equals(pTEXT_FILL_COLOR)) return getTextFillColor(); else if (key.equals(pTEXT_FILLED)) return getTextFilled() ? Boolean.TRUE : Boolean.FALSE; else if (key.equals(pFONT_NAME)) return getFontName(); else if (key.equals(pFONT_SIZE)) return new Integer(getFontSize()); else if (key.equals(pALIGNMENT)) return getAlignment(); else if (key.equals(pLINE_SPACING)) return new Integer(getLineSpacing()); else if (key.equals(pTOP_MARGIN)) return new Integer(getTopMargin()); else if (key.equals(pBOT_MARGIN)) return new Integer(getBotMargin()); else if (key.equals(pLEFT_MARGIN)) return new Integer(getLeftMargin()); else if (key.equals(pRIGHT_MARGIN)) return new Integer(getRightMargin()); else if (key.equals(pEXPAND_ONLY)) return getExpandOnly() ? Boolean.TRUE : Boolean.FALSE; else if (key.equals(pBOLD)) return getBold() ? Boolean.TRUE : Boolean.FALSE; else if (key.equals(pITALIC)) return getItalic() ? Boolean.TRUE : Boolean.FALSE; else if (key.equals(pSTRIKE)) return Boolean.FALSE; else if (key.equals(pUNDERLINE)) return Boolean.FALSE; else if (key.equals(pTEXT_SHADOW)) return Boolean.FALSE; else if (key.equals(pTEXT_SHADOW_COLOR)) return Color.gray; else return super.get(key); } public boolean put(String key, Object value) { if (key.equals(pTEXT)) setText((String)value); else if (key.equals(pTEXT_COLOR)) setTextColor((Color)value); else if (key.equals(pTEXT_FILL_COLOR)) setTextFillColor((Color)value); else if (key.equals(pTEXT_FILLED)) setTextFilled(((Boolean) value).booleanValue()); else if (key.equals(pFONT_NAME)) setFontName((String) value); else if (key.equals(pFONT_SIZE)) setFontSize(((Integer)value).intValue()); else if (key.equals(pALIGNMENT)) setAlignment((String) value); else if (key.equals(pLINE_SPACING)) setLineSpacing(((Integer) value).intValue()); else if (key.equals(pTOP_MARGIN)) setTopMargin(((Integer) value).intValue()); else if (key.equals(pBOT_MARGIN)) setBotMargin(((Integer) value).intValue()); else if (key.equals(pLEFT_MARGIN)) setLeftMargin(((Integer) value).intValue()); else if (key.equals(pRIGHT_MARGIN)) setRightMargin(((Integer) value).intValue()); else if (key.equals(pEXPAND_ONLY)) setExpandOnly(((Boolean) value).booleanValue()); else if (key.equals(pBOLD)) setBold(((Boolean)value).booleanValue()); else if (key.equals(pITALIC)) setItalic(((Boolean)value).booleanValue()); else if (key.equals(pSTRIKE)) setStrike(((Boolean)value).booleanValue()); else if (key.equals(pUNDERLINE)) setUnderline(((Boolean)value).booleanValue()); else if (key.equals(pTEXT_SHADOW)) setTextShadow(((Boolean)value).booleanValue()); else if (key.equals(pTEXT_SHADOW_COLOR)) setTextShadowColor((Color) value); else return super.put(key, value); changedProp(key); return true; } public static Vector _StylePropNames = null; public static Vector _ColorPropNames = null; public static Vector _GeometryPropNames = null; public static Vector _ModelPropNames = null; public Enumeration keysIn(String category) { if (_ModelPropNames == null) { _ModelPropNames = new Vector(); _ModelPropNames.addElement(pTEXT); } if (_StylePropNames == null) { _StylePropNames = new Vector(); _StylePropNames.addElement(pTEXT_COLOR); _StylePropNames.addElement(pTEXT_FILLED); _StylePropNames.addElement(pTEXT_FILL_COLOR); _StylePropNames.addElement(pALIGNMENT); _StylePropNames.addElement(pLINE_SPACING); _StylePropNames.addElement(pFONT_NAME); _StylePropNames.addElement(pFONT_SIZE); _StylePropNames.addElement(pBOLD); _StylePropNames.addElement(pITALIC); //_StylePropNames.addElement(pSTRIKE); //_StylePropNames.addElement(pUNDERLINE); _StylePropNames.addElement(pTEXT_SHADOW); _StylePropNames.addElement(pTEXT_SHADOW_COLOR); } if (_ColorPropNames == null) { _ColorPropNames = new Vector(); _ColorPropNames.addElement(pTEXT_COLOR); _ColorPropNames.addElement(pTEXT_FILLED); _ColorPropNames.addElement(pTEXT_FILL_COLOR); _ColorPropNames.addElement(pTEXT_SHADOW); _ColorPropNames.addElement(pTEXT_SHADOW_COLOR); } if (_GeometryPropNames == null) { _GeometryPropNames = new Vector(); _GeometryPropNames.addElement(pTOP_MARGIN); _GeometryPropNames.addElement(pBOT_MARGIN); _GeometryPropNames.addElement(pLEFT_MARGIN); _GeometryPropNames.addElement(pRIGHT_MARGIN); _GeometryPropNames.addElement(pEXPAND_ONLY); } Enumeration styleEnum = _StylePropNames.elements(); Enumeration colorEnum = _ColorPropNames.elements(); Enumeration geomEnum = _GeometryPropNames.elements(); Enumeration modelEnum = _ModelPropNames.elements(); if (category.equals("Model")) return new EnumerationComposite(super.keysIn(category), modelEnum); if (category.equals("Style")) return new EnumerationComposite(super.keysIn(category), styleEnum); if (category.equals("Color")) return new EnumerationComposite(super.keysIn(category), colorEnum); if (category.equals("Geometry")) return new EnumerationComposite(super.keysIn(category), geomEnum); return super.keysIn(category); } public boolean canPut(String key) { return key.equals(pTEXT) || key.equals(pTEXT_COLOR) || key.equals(pTEXT_FILLED) | key.equals(pTEXT_FILL_COLOR) || key.equals(pFONT_NAME) || key.equals(pFONT_SIZE) || key.equals(pALIGNMENT) || key.equals(pBOLD) || key.equals(pITALIC) || key.equals(pEXPAND_ONLY) || key.equals(pLINE_SPACING) || key.equals(pTOP_MARGIN) || key.equals(pBOT_MARGIN) || key.equals(pLEFT_MARGIN) || key.equals(pRIGHT_MARGIN) || //key.equals(pSTRIKE) || key.equals(pUNDERLINE) || //key.equals(pTEXT_SHADOW) || key.equals(pSHADOW_COLOR) || super.canPut(key); } /** Remove the last char from the current string line and return the * new string. Called whenever the user hits the backspace key. * Needs-More-Work: Very slow. This will eventually be replaced by * full text editing... if there are any volunteers to do that...*/ public String deleteLastCharFromString(String string) { int string_length = Math.max(string.length() - 1, 0); char[] string_char = string.toCharArray(); String new_string = new String(string_char,0,string_length); return new_string; } /** Delete the last char from the current string. Called whenever * the user hits the backspace key */ public void deleteLastChar() { _curText = deleteLastCharFromString(_curText); measure(); changedProp(pTEXT); } /** Append a character to the current String .*/ public void append(char c) { setText(_curText + c); } /** Append the given String to the current String. */ public void append(String s) { setText(_curText + s); } /** set the current String to be the given String. */ public void setText(String s) { _curText = s; measure(); changedProp(pTEXT); } /** Get the String held by this FigText. Multi-line text is * represented by newline characters embedded in the String. */ public String getText() { return _curText; } //////////////////////////////////////////////////////////////// // drawing methods /** Draw the FigText. */ public void draw(Graphics g) { int rx = position().x; int ry = position().y; int new_x = rx + _leftMargin, new_y = ry + _topMargin; int temp_y; if (filled) { g.setColor(objectFillColor); g.fillRect(rx, ry, objectWidth, objectHeight); } if (lineWidth > 0) { g.setColor(objectLineColor); g.drawRect(rx-1, ry-1, objectWidth+1, objectHeight+1); } if (_font == null) buildFont(); g.setFont(_font); _fm = g.getFontMetrics(_font); temp_y = new_y + _lineHeight + _lineSpacing; StringTokenizer lines = new StringTokenizer(_curText, "\n\r", true); while (lines.hasMoreTokens()) { String curLine = lines.nextToken(); if (curLine.equals("\r")) continue; int lineWidth = _fm.stringWidth(curLine); switch (_alignment) { case JUSTIFY_LEFT: break; case JUSTIFY_CENTER: new_x = rx + ( objectWidth - lineWidth ) / 2; break; case JUSTIFY_RIGHT: new_x = rx + objectWidth - lineWidth - _rightMargin; break; } if (curLine.equals("\n")) temp_y += _lineHeight + _lineSpacing; else { if (_textFilled) { g.setColor(_textFillColor); g.fillRect(new_x, temp_y - _lineHeight + 1, lineWidth, _lineHeight); } } } new_x = rx + _leftMargin; new_y = ry + _topMargin; temp_y = new_y + _lineHeight + _lineSpacing; lines = new StringTokenizer(_curText, "\n\r", true); while (lines.hasMoreTokens()) { String curLine = lines.nextToken(); if (curLine.equals("\r")) continue; int lineWidth = _fm.stringWidth(curLine); switch (_alignment) { case JUSTIFY_LEFT: break; case JUSTIFY_CENTER: new_x = rx + ( objectWidth - lineWidth ) / 2; break; case JUSTIFY_RIGHT: new_x = rx + objectWidth - lineWidth; break; } if (curLine.equals("\n")) temp_y += _lineHeight + _lineSpacing; else { g.setColor(_textColor); g.drawString(curLine, new_x, temp_y); } } } /** Needs-More-Work: remove this method, its logic has been moved to * SelectionBox. */ public void drawSelected(Graphics g) { } //////////////////////////////////////////////////////////////// // Editor API /** FigText's do not have handles in this version. */ public int pickHandle(int x, int y) { return -1; } /** When the Editor selects this FigText, that fact should be * recorded in a SelectionBox object. */ public Selection selectionObject() { return new SelectionBox(this); } //////////////////////////////////////////////////////////////// // event handlers /** When the user presses a key when a FigText is selected, that key * should be added to the current string, or if the key was * backspace, the last character is removed. Needs-More-Work: Should * also catch arrow keys and mouse clicks for full text * editing... someday... */ public boolean keyDown(Event evt,int key) { if (key == 127) return false; startTrans(); if (key == 8) deleteLastChar(); else append((char)key); endTrans(); return true; /* needs-more-work: not all keys are processed... */ } //////////////////////////////////////////////////////////////// // internal utility functions /** Compute the overall width and height of the FigText object based * on the font, font size, and current text. Needs-More-Work: Right * now text objects can get larger when you type more, but they * do not get smaller when you backspace. */ protected void measure() { if (_font == null) buildFont(); if (_fm == null) { _fm = Toolkit.getDefaultToolkit().getFontMetrics(_font); } int overallWidth = 0; int numLines = 1; StringTokenizer lines = new StringTokenizer(_curText, "\n\r", true); while (lines.hasMoreTokens()) { String curLine = lines.nextToken(); if (curLine.equals("\r")) continue; int lineWidth = _fm.stringWidth(curLine); if (curLine.equals("\n")) numLines++; else overallWidth = Math.max(lineWidth, overallWidth); } _lineHeight = _fm.getHeight(); int maxDescent = _fm.getMaxDescent(); int overallHeight = (_lineHeight + _lineSpacing) * numLines + _topMargin + _botMargin + maxDescent; overallWidth = Math.max(MIN_TEXT_WIDTH, overallWidth + _leftMargin + _rightMargin); switch (_alignment) { case JUSTIFY_LEFT: break; case JUSTIFY_CENTER: if (objectWidth < overallWidth) { position().x -= (overallWidth - objectWidth) / 2; } break; case JUSTIFY_RIGHT: if (objectWidth < overallWidth) { position().x -= (overallWidth - objectWidth); } break; } if (_expandOnly) { objectWidth = Math.max(objectWidth, overallWidth); objectHeight = Math.max(objectHeight, overallHeight); } else { objectWidth = overallWidth; objectHeight = overallHeight; } } } /* end class FigText */