Package ket.display.box

Source Code of ket.display.box.BoxText

/*
* Copyright (C) 2011  Alasdair C. Hamilton
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>
*/

package ket.display.box;

import geom.Offset;
import geom.Position;

import java.awt.*;
import java.util.*;
import ket.math.*;
import ket.display.ColourScheme;



/*
* handle display of a letter, symbol or word
*/
public class BoxText extends Box {
  /**
   * The font used to draw the text.
   */
  Font font;

  /**
   * The entire text before it is split into multiple lines.
   */
  final String text;

  /**
   * The text after it has been split into multiple lines.
   */
  Vector<String> lines;

  /**
   * The height above the text's coordinate at which the top of the text
   * is drawn.
   */
  double ascent;
 
  /**
   * The distance downwards from the text's coordinate to the bottom of the text.
   */
  double decent;

  /**
   * For each line of text, record a series of line lengths (in pixels)
   * and their corresponding index in the 'text' string.
   */
  Vector<TreeMap<Integer, Integer>> wordWidthsToIndexList = null; // index against width.
  //- Position topLeft = null;
  Offset actualSize = null;


  // TODO: When is box.clone used()?  Does it require that lines also be
  // passed in as an argument?
  @Override
  public Box cloneBox() {
    return new BoxText(getArgument(), text, getSettings());
  }

  @Override
  protected void initDefaultSettings(long settings) {
    horizontalAlignment = X_CENTRE_ALIGN;
    verticalAlignment = Y_CENTRE_ALIGN;
    preserveAspectRatio = false;
    relativeFontSize = NORMAL_FONT;
    // Most box descendents default to italics, but text should be plain.
    style = PLAIN_FONT;
  }

  public BoxText(Argument argument, String text, long settings) {
    super(argument, settings);
    assert text!=null : "The text string displayed in BoxText can't be null";
    this.text = text;
    this.lines = new Vector<String>();
    this.font = null;
    this.ascent = 0.0;
  }

  /*
   * Change the font to a given size and style.
   */
  @Override
  protected void fontSetup(int parentFontSize) {
    super.fontSetup(parentFontSize); // Note: this isn't used.
    int styleFontFlag = 0;
    // Text defaults to plain while word defaults to italics.
    if (hasProperty(BOLD_FONT)) {
      styleFontFlag |= Font.BOLD;
    } else if (hasProperty(ITALIC_FONT)) {
      styleFontFlag |= Font.ITALIC;
    } else {
      styleFontFlag |= Font.PLAIN;
    }
    font = new Font( //! Font.DIALOG,
      "Times New Roman",
      styleFontFlag,
      (int) (TEXT_SCALE_FACTOR*fontSize));
    boolean invalid = false;
    for (int i=0; i<text.length(); i++) {
      int codePoint = text.codePointAt(i);
      if ( ! font.canDisplay(codePoint) ) {
        invalid = true;
      }
    }
    if (invalid) {
      font = new Font(
        Font.DIALOG,
        styleFontFlag,
        (int) (TEXT_SCALE_FACTOR*fontSize));
    }
  }

  /*
   * Use the font to determine the size of the text.
   */
  @Override
  protected void calcMinimumSize() {
    FontMetrics fontMetrics = getFontMetrics(font);
    int width = fontMetrics.stringWidth(text);
    ascent = fontMetrics.getAscent();
    decent = fontMetrics.getDescent();
    innerRectangle = new Offset(width, decent+ascent);
  }

  public boolean isWhitespace(String currentLineSoFar) {
    return currentLineSoFar.trim().isEmpty();
  }

  @Override
  public void setupOuterRectangle(Offset actualSize) {
    FontMetrics fontMetrics = getFontMetrics(font);
    boolean hasMultipleLines = innerRectangle.width>=actualSize.width;
    this.actualSize = actualSize;
    wordWidthsToIndexList = new Vector<TreeMap<Integer, Integer>>();
    lines = new Vector<String>();
    String currentLineSoFar = "";
    int lengthOfPreviousLines = 0;
    for (int i=0; i<text.length(); i++) {
      String suffix = getNextWord(i, text);
      i += (i==text.length()?0:-1) + suffix.length();
      int width = fontMetrics.stringWidth(currentLineSoFar + suffix);
      if (width > actualSize.width) { // wrap line.
        updateMap(fontMetrics, currentLineSoFar, lengthOfPreviousLines);
        lines.add(currentLineSoFar);
        lengthOfPreviousLines += currentLineSoFar.length();
        currentLineSoFar = suffix;
      } else {
        currentLineSoFar += suffix;
      }
      if (isWhitespace(currentLineSoFar)) { // Avoid indentation from whitespace.
        currentLineSoFar = "";
      }
    }
    updateMap(fontMetrics, currentLineSoFar, lengthOfPreviousLines);
    lines.add(currentLineSoFar);
    int decent = fontMetrics.getDescent();
    double textHeight = lines.size() * (ascent + decent);
    if (hasMultipleLines) {
      innerRectangle = new Offset(actualSize.width, textHeight);
    }
    double largestHeight = Math.max(actualSize.height, textHeight);
    Offset largestSize = new Offset(actualSize.width, largestHeight);
    super.setupOuterRectangle(largestSize);
 

  private void updateMap(FontMetrics fontMetrics, String currentLineSoFar, int lengthOfPreviousLines) {
    TreeMap<Integer, Integer> wordWidthsToIndex = new TreeMap<Integer, Integer>();
    for (int q=1; q<currentLineSoFar.length(); q++) {
      int fullWidth = fontMetrics.stringWidth(currentLineSoFar.substring(0, q));
      wordWidthsToIndex.put(fullWidth, lengthOfPreviousLines+q);
    }
    wordWidthsToIndexList.add(wordWidthsToIndex);
  }

  /**
   * If the i'th index of text is the start of a word, return the word,
   * and otherwise return just the character.
   */
  private String getNextWord(int i, String text) {
    String word = "";
    for ( ; i<text.length(); i++) {
      char c = text.charAt(i);
      word += c;
      //-? if (!Character.isLetter(c) && c!='|') { // cursor is '|' so don't wrap mid-way through the current word.
      if (c==' ') {
        return word;
      }
    }
    return word;
  }

  private int getFontProperty() {
    if (hasProperty(BOLD_FONT)) {
      return Font.BOLD;
    } else if (hasProperty(PLAIN_FONT)) {
      return Font.PLAIN;
    } else {
      return Font.ITALIC;
    }
  }

  /**
   * If specified, return the index corresponding to the given location.
   */
  public int getIndex(Position p) {
    if (topLeft==null || actualSize==null || wordWidthsToIndexList==null) {
      return -1;
    }
    int row = (int) (p.y - getYPosition(topLeft)) / (int) actualSize.height;
    if (row<0 || row >= wordWidthsToIndexList.size()) {
      return -1;
    }
    TreeMap<Integer, Integer> wordWidthsToIndex = wordWidthsToIndexList.get(row);
    int oldWidth = 0;
    int boxIndent = (int) getXPosition(topLeft);
    for (int width : wordWidthsToIndex.navigableKeySet()) {
      boolean bounded = 0<=p.x && p.x<boxIndent+width;
      if (bounded) {
        Integer index = wordWidthsToIndex.get(width); // Don't always 'round' the mouse click location 'up' (i.e. right).
        if (index==null) { // just in case.
          return -1;
        }
        //? return index;
        double oldSeparation = p.x - boxIndent - width;
        double separation = boxIndent + oldWidth - p.x;
        return oldSeparation<separation ? index-1 : index;
      }
      oldWidth = width;
    }
    return -1;
  }

  @Override
  public void draw(Graphics2D g2D, Position topLeft, ColourScheme colourScheme) {
    this.topLeft = topLeft; //?
    double x = getXPosition(topLeft);
    double y = getYPosition(topLeft);
    g2D.setFont(font);

    for (int i=0; i<lines.size(); i++) {
      float yShift = (float) (y + i*(ascent+decent));
      float yPos = (float) (yShift + ascent);
      g2D.drawString(lines.get(i), (float) x, yPos);
    }
  }

  public String getText() {
    return text;
  }

  public String toString() {
    return "BoxText("+lines+" | "+super.toString();
  }
}
  
TOP

Related Classes of ket.display.box.BoxText

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.