/* * JFLAP - Formal Languages and Automata Package * * * Susan H. Rodger * Computer Science Department * Duke University * August 27, 2009 * Copyright (c) 2002-2009 * All rights reserved. * JFLAP is open source software. Please see the LICENSE for terms. * */ package gui.sim; import java.awt.geom.GeneralPath; import java.awt.Graphics2D; import java.awt.FontMetrics; import java.awt.Color; /** * This is a rather silly factory class that returns shape objects that look * like "torn tape," for objects that need to express a potentially very large * structure in a very small amount of space. These objects are here referred to * often as "torn". I've nouned torn, like I just verbed noun. * * @author Thomas Finley */ public class Torn { /** * Returns a shape whose stroke and fill will give the impression of a "torn * tape" sort of structure. The point (x,y) will be the upper * left corner of the torn tape, the structure will be completely enclosed * in the bounds (x,y,x+width,x+height). * * @param width * the width of the torn tape * @param x * the x coordinate of the torn * @param y * the y coordinate of the torn * @param height * the height of the torn tape * @param left * true if this tape is "torn" on the left end * @param right * true if this tape is "torn" on the right end */ public static GeneralPath getTorn(float x, float y, float width, float height, boolean left, boolean right) { GeneralPath path = new GeneralPath(); path.moveTo(x, y); path.lineTo(x + width, y); if (right) { path.quadTo(x + width - height / 4.0f, y + height / 4.0f, x + width, y + height / 2.0f); path.quadTo(x + width - height / 4.0f, y + 3.0f * height / 4.0f, x + width, y + height); } else path.lineTo(x + width, y + height); path.lineTo(x, y + height); if (left) { path.quadTo(x + height / 4.0f, y + 3.0f * height / 4.0f, x, y + height / 2.0f); path.quadTo(x + height / 4.0f, y + height / 4.0f, x, y); } path.closePath(); return path; } /** * A given string is painted inside of a "Torn". The string is aligned with * whatever part of the tape is NOT torn. If both ends are torn, the string * is aligned in the center. If neither end is torn, the string is left * aligned. * * @param g * the graphics context to paint in * @param string * the string to paint inside of a torn * @param x * the x coordinate of the torn * @param y * the y coordinate of the torn * @param align * the alignment of the torn, as specified by either Torn.TOP * (i.e. (x,y) is the upper left), Torn.MIDDLE, * or Torn.BOTTOM. * @param width * the width of the torn * @param left * is the left end torn? * @param right * is the right end torn? * @param select * the character to draw as centered and selected, or -1 if no * character should be drawn as selected * @return the height of the resulting drawn "torn" */ public static float paintString(Graphics2D g, String string, float x, float y, int align, float width, boolean left, boolean right, int select) { // Convert the y coordinate into the alignment into the upper // left corner. FontMetrics metrics = g.getFontMetrics(); float toBaseline = PADDING + metrics.getAscent(); float height = toBaseline + PADDING + metrics.getDescent(); if (align == MIDDLE) y -= height * 0.5f; if (align == BOTTOM) y -= height; if (select > string.length()) select = string.length(); // Create the torn object. GeneralPath torn = getTorn(x, y, width, height, left, right); g.setColor(Color.white); g.fill(torn); // Prepare some measurements for putting the text in the right // place. float horizontalPadding = metrics.charWidth(' '); Graphics2D g2 = (Graphics2D) g.create(); g2.clip(torn); float dx = 0.0f; if (left) dx = (float) metrics.getStringBounds(string, g2).getWidth() - width + 2.0f * horizontalPadding; if (right) dx *= 0.5f; // Easy. if (select >= 0 && !left && right) { // Do that greying out and crap. String before = string.substring(0, select), after = string .substring(select); double bLength = metrics.getStringBounds(before, g2).getWidth(); double aLength = metrics.getStringBounds(after, g2).getWidth(); float aStart = bLength > width / 2.0f ? width / 2.0f : horizontalPadding + (float) bLength; float bStart = aStart - (float) bLength; g2.setColor(Color.gray); g2.drawString(before, x + bStart, y + toBaseline); g2.setColor(Color.black); g2.drawString(after, x + aStart, y + toBaseline); } else { if (select >= 0) { double l = metrics.getStringBounds(string.substring(0, select), g2).getWidth(); double c = metrics.getStringBounds( string.substring(select, select + 1), g2).getWidth(); g2.setColor(HIGHLIGHT_COLOR); g2.fillRect((int) (x + 0.5f * (width - c)), (int) y, (int) c, 100); dx = (float) (l + 0.5 * (c - width) + horizontalPadding); } g2.setColor(Color.black); // We finally get to draw the string. g2.drawString(string, x + horizontalPadding - dx, y + toBaseline); } g2.dispose(); // Finish by drawing the outline of the torn. g.setColor(Color.black); g.draw(torn); return height; } /** Alignment constants. */ public static final int TOP = 0; public static final int MIDDLE = 1; public static final int BOTTOM = 2; /** What is the padding of the torn? */ public static final float PADDING = 2.0f; /** What is the highlight color? */ private static final Color HIGHLIGHT_COLOR = new Color(255, 0, 0, 128); }