Title.java

package de.slothsoft.charts.common;

import java.util.Objects;

import de.slothsoft.charts.Area;
import de.slothsoft.charts.Chart;
import de.slothsoft.charts.ChartPart;
import de.slothsoft.charts.Font;
import de.slothsoft.charts.GraphicContext;
import de.slothsoft.charts.PaintInstructions;
import de.slothsoft.charts.RefreshListener;
import de.slothsoft.charts.TextAlignment;
import de.slothsoft.charts.internal.RefreshListeners;

/**
 * A title over an {@link Chart}. On default it's not visible, you need to use
 * {@link #setText(String)} to get it to be displayed.
 *
 * @author Stef Schulz
 * @since 0.1.0
 */

public class Title implements ChartPart {

	/**
	 * The position of the {@link Title}.
	 */

	public enum Position {
		/**
		 * The default behavior is to have the axis on y=0 if that is visible, else on the
		 * top or bottom border of the graph depending were it is in relation to the
		 * visible area.
		 */
		TOP {

			@Override
			Area snipNecessarySpace(Area existingArea, double size) {
				return existingArea.copy().startY(existingArea.getStartY() + size);
			}

			@Override
			double getPaintY(Area area, double size) {
				return area.getStartY();
			}
		},

		BOTTOM {

			@Override
			Area snipNecessarySpace(Area existingArea, double size) {
				return existingArea.copy().endY(existingArea.getEndY() - size);
			}

			@Override
			double getPaintY(Area area, double size) {
				return area.getEndY() - size;
			}
		};

		abstract Area snipNecessarySpace(Area existingArea, double size);

		abstract double getPaintY(Area area, double size);

	}
	String text;
	int color = 0xFF112211;
	Font font = Font.TITLE;
	double size = 25;
	Title.Position position = Title.Position.TOP;
	TextAlignment alignment = TextAlignment.CENTER;

	RefreshListeners refreshListeners = new RefreshListeners(this);

	@Override
	public void paintOn(GraphicContext gc, PaintInstructions instructions) {
		if (this.text != null) {
			final Area area = instructions.getArea();
			gc.setColor(this.color);
			gc.setFont(this.font);

			final double paintY = this.position.getPaintY(area, this.size);
			this.alignment.drawText(gc, area.copy().startY(paintY).endY(paintY + this.size), this.text);
		}
	}

	@Override
	public Area snipNecessarySpace(Area existingArea) {
		if (this.text == null) return existingArea;
		return this.position.snipNecessarySpace(existingArea, this.size);
	}

	@Override
	public void addRefreshListener(RefreshListener listener) {
		this.refreshListeners.addRefreshListener(listener);
	}

	@Override
	public void removeRefreshListener(RefreshListener listener) {
		this.refreshListeners.removeRefreshListener(listener);
	}

	/**
	 * Returns the text that is the title or <code>null</code> to show there is no title.
	 *
	 * @return the text
	 */

	public String getText() {
		return this.text;
	}

	/**
	 * Sets the text that is the title or <code>null</code> to show there is no title.
	 *
	 * @param newText the text
	 * @return this instance
	 */

	public Title text(String newText) {
		setText(newText);
		return this;
	}

	/**
	 * Sets the text that is the title or <code>null</code> to show there is no title.
	 *
	 * @param text the text
	 */

	public void setText(String text) {
		final String oldText = this.text;
		this.text = text;
		if (oldText != text) {
			this.refreshListeners.fireRefreshNeeded();
		}
	}

	/**
	 * Returns the color as ARGB int, e.g. red is <code>0xFFFF0000</code> and blue is
	 * <code>0xFF0000FF</code>.
	 *
	 * @return the color
	 */

	public int getColor() {
		return this.color;
	}

	/**
	 * Sets the color as ARGB int, e.g. red is <code>0xFFFF0000</code> and blue is
	 * <code>0xFF0000FF</code>.
	 *
	 * @param newColor the color
	 * @return this instance
	 */

	public Title color(int newColor) {
		setColor(newColor);
		return this;
	}

	/**
	 * Sets the color as ARGB int, e.g. red is <code>0xFFFF0000</code> and blue is
	 * <code>0xFF0000FF</code>.
	 *
	 * @param color the color
	 */

	public void setColor(int color) {
		final int oldColor = this.color;
		this.color = color;
		if (oldColor != color) {
			this.refreshListeners.fireRefreshNeeded();
		}
	}

	/**
	 * Returns the font used to paint the title.
	 *
	 * @return the font; never null
	 */

	public Font getFont() {
		return this.font;
	}

	/**
	 * Sets the font used to paint the title.
	 *
	 * @param newFont the font; cannot be null
	 * @return this instance
	 */

	public Title font(Font newFont) {
		setFont(newFont);
		return this;
	}

	/**
	 * Sets the font used to paint the title.
	 *
	 * @param font the font; cannot be null
	 */

	public void setFont(Font font) {
		final Font oldFont = this.font;
		this.font = Objects.requireNonNull(font);
		if (oldFont != font) {
			this.refreshListeners.fireRefreshNeeded();
		}
	}

	/**
	 * Returns the height of the title, if one is drawn.
	 *
	 * @return the title's size
	 */

	public double getSize() {
		return this.size;
	}

	/**
	 * Sets the height of the title, if one is drawn.
	 *
	 * @param newSize the title's size
	 * @return this instance
	 */

	public Title size(double newSize) {
		setSize(newSize);
		return this;
	}

	/**
	 * Sets the height of the title, if one is drawn.
	 *
	 * @param size the title's size
	 */

	public void setSize(double size) {
		final double oldSize = this.size;
		this.size = size;
		if (oldSize != size) {
			this.refreshListeners.fireRefreshNeeded();
		}
	}

	/**
	 * Returns the position of the title.
	 *
	 * @return the position; never null
	 */

	public Title.Position getPosition() {
		return this.position;
	}

	/**
	 * Sets the position of the title.
	 *
	 * @param newPosition the position; cannot be null
	 * @return this instance
	 */

	public Title position(Title.Position newPosition) {
		setPosition(newPosition);
		return this;
	}

	/**
	 * Sets the position of the title.
	 *
	 * @param position the position; cannot be null
	 */

	public void setPosition(Title.Position position) {
		final Title.Position oldPosition = this.position;
		this.position = Objects.requireNonNull(position);
		if (oldPosition != position) {
			this.refreshListeners.fireRefreshNeeded();
		}
	}

	/**
	 * Returns the alignment of the title.
	 *
	 * @return the alignment; never null
	 */

	public TextAlignment getAlignment() {
		return this.alignment;
	}

	/**
	 * Sets the alignment of the title.
	 *
	 * @param newAlignment the alignment; cannot be null
	 * @return this instance
	 */

	public Title alignment(TextAlignment newAlignment) {
		setAlignment(newAlignment);
		return this;
	}

	/**
	 * Sets the alignment of the title.
	 *
	 * @param alignment the alignment; cannot be null
	 */

	public void setAlignment(TextAlignment alignment) {
		final TextAlignment oldAlignment = this.alignment;
		this.alignment = Objects.requireNonNull(alignment);
		if (oldAlignment != alignment) {
			this.refreshListeners.fireRefreshNeeded();
		}
	}

	@Override
	public String toString() {
		return "Title [text=" + this.text + ", color=" + this.color + ", font=" + this.font + ", size=" + this.size
				+ ", position=" + this.position + ", alignment=" + this.alignment + "]";
	}

}